@@ -24,48 +24,50 @@ function alertMessage(message) {
24
24
25
25
class Dataspace {
26
26
constructor ( selector ) {
27
- this . svg = d3 . select ( selector ) ;
28
- this . $svg = $ ( selector ) ;
29
- this . dataset = [ ] ;
30
-
31
- // drawing properties are hardcoded for now
32
- this . width = this . $svg . width ( ) ;
33
- this . height = this . $svg . height ( ) ;
34
- this . color = d3 . scaleOrdinal ( d3 . schemeCategory10 ) ;
35
-
36
- this . xScale = d3 . scaleLinear ( )
37
- . domain ( [ 0 , 1 ] )
38
- . range ( [ margin . left , this . width - margin . right ] ) ;
39
-
40
- this . yScale = d3 . scaleLinear ( )
41
- . domain ( [ 0 , 1 ] )
42
- . range ( [ margin . top , this . height - margin . bottom ] )
27
+ this . svg = d3 . select ( selector ) ;
28
+ this . $svg = $ ( selector ) ;
29
+ this . dataset = [ ] ;
30
+ this . grid = null ;
31
+
32
+ // drawing properties are hardcoded for now
33
+ this . width = this . $svg . width ( ) ;
34
+ this . height = this . $svg . height ( ) ;
35
+ this . color = d3 . scaleOrdinal ( d3 . schemeCategory10 ) ;
36
+
37
+ this . xScale = d3 . scaleLinear ( )
38
+ . domain ( [ 0 , 1 ] )
39
+ . range ( [ margin . left , this . width - margin . right ] ) ;
40
+
41
+ this . yScale = d3 . scaleLinear ( )
42
+ . domain ( [ 0 , 1 ] )
43
+ . range ( [ margin . top , this . height - margin . bottom ] )
43
44
}
44
45
45
46
draw ( ) {
46
47
var self = this ;
47
48
self . svg . selectAll ( "circle" )
48
- . data ( self . dataset )
49
- . enter ( )
50
- . append ( "circle" )
51
- . attr ( 'cx' , function ( d ) { return self . xScale ( d . x ) ; } )
52
- . attr ( 'cy' , function ( d ) { return self . yScale ( d . y ) ; } )
53
- . attr ( 'fill' , function ( d ) { return self . color ( d . c ) ; } )
54
- . attr ( 'r' , radius ) ;
49
+ . data ( self . dataset )
50
+ . enter ( )
51
+ . append ( "circle" )
52
+ . attr ( 'cx' , function ( d ) { return self . xScale ( d . x ) ; } )
53
+ . attr ( 'cy' , function ( d ) { return self . yScale ( d . y ) ; } )
54
+ . attr ( 'fill' , function ( d ) { return self . color ( d . c ) ; } )
55
+ . attr ( "stroke" , "#FFFFFF" )
56
+ . attr ( 'r' , radius ) ;
55
57
}
56
58
57
59
// Add raw data point (e.g. where x and y are between 0 and 1)
58
60
addPoint ( point ) {
59
- this . dataset . push ( point ) ;
60
- this . draw ( ) ;
61
+ this . dataset . push ( point ) ;
62
+ this . draw ( ) ;
61
63
}
62
64
63
65
// Add coordinates data point (e.g. where x and y are in the svg)
64
66
addCoords ( coords ) {
65
67
var point = {
66
- x : this . xScale . invert ( coords [ 0 ] ) ,
67
- y : this . yScale . invert ( coords [ 1 ] ) ,
68
- c : currentClass
68
+ x : this . xScale . invert ( coords [ 0 ] ) ,
69
+ y : this . yScale . invert ( coords [ 1 ] ) ,
70
+ c : currentClass
69
71
} ;
70
72
this . addPoint ( point ) ;
71
73
}
@@ -74,14 +76,14 @@ class Dataspace {
74
76
fetch ( data ) {
75
77
this . reset ( ) ;
76
78
d3 . json ( "/generate" , {
77
- method : "POST" ,
78
- body : JSON . stringify ( data ) ,
79
- headers : {
80
- "Content-Type" : "application/json; charset=UTF-8"
81
- }
79
+ method : "POST" ,
80
+ body : JSON . stringify ( data ) ,
81
+ headers : {
82
+ "Content-Type" : "application/json; charset=UTF-8"
83
+ }
82
84
} ) . then ( json => {
83
- this . dataset = json ;
84
- this . draw ( ) ;
85
+ this . dataset = json ;
86
+ this . draw ( ) ;
85
87
} ) . catch ( error => {
86
88
console . log ( error ) ;
87
89
alertMessage ( "Server could not generate dataset!" ) ;
@@ -92,13 +94,18 @@ class Dataspace {
92
94
fit ( model ) {
93
95
$ ( "#metrics" ) . removeClass ( "visible" ) . addClass ( "invisible" ) ;
94
96
if ( this . dataset . length == 0 ) {
95
- console . log ( "cannot fit model to no data!" ) ;
96
- return
97
+ console . log ( "cannot fit model to no data!" ) ;
98
+ return
97
99
}
98
100
101
+ // The contours grid determines what to make predictions on.
102
+ // TODO: don't pass this to the server but allow the server to compute it.
103
+ var self = this ;
104
+ self . grid = self . contoursGrid ( )
99
105
var data = {
100
- model : model ,
101
- dataset : this . dataset ,
106
+ model : model ,
107
+ dataset : self . dataset ,
108
+ grid : self . grid ,
102
109
}
103
110
104
111
d3 . json ( "/fit" , {
@@ -108,18 +115,64 @@ class Dataspace {
108
115
"Content-Type" : "application/json; charset=UTF-8"
109
116
}
110
117
} ) . then ( json => {
118
+ // Reset the old contours
119
+ self . svg . selectAll ( "g" ) . remove ( ) ;
120
+
121
+ // Update the metrics
111
122
$ ( "#f1score" ) . text ( json . metrics . f1 ) ;
112
123
$ ( "#metrics" ) . removeClass ( "invisible" ) . addClass ( "visible" ) ;
124
+
125
+ // Update the grid with the predictions values.
126
+ $ . each ( json . grid , function ( i , val ) {
127
+ self . grid [ i ] = val ;
128
+ } )
129
+
130
+ // Compute the thresholds from the classes, then compute the colors
131
+ var thresholds = self . classes ( ) . map ( i => d3 . range ( i , i + 1 , 0.1 ) ) . flat ( ) . sort ( ) ;
132
+ var colorMap = { }
133
+ $ . each ( self . classes ( ) , c => {
134
+ colorMap [ c ] = d3 . scaleLinear ( ) . domain ( [ c , c + 1 ] )
135
+ . interpolate ( d3 . interpolateHcl )
136
+ . range ( [ "#FFFFFF" , self . color ( c ) ] )
137
+ } ) ;
138
+
139
+ var getColor = d => {
140
+ console . log ( d . value )
141
+ return colorMap [ Math . floor ( d . value ) ] ( d . value )
142
+ }
143
+
144
+ // Add the contours from the predictions for each class
145
+ var contours = d3 . contours ( )
146
+ . size ( [ self . grid . n , self . grid . m ] )
147
+ . thresholds ( thresholds )
148
+ . smooth ( true )
149
+ ( self . grid )
150
+ . map ( self . grid . transform )
151
+
152
+ // Draw the contours on the SVG
153
+ self . svg . insert ( "g" , ":first-child" )
154
+ . attr ( "fill" , "none" )
155
+ . attr ( "stroke" , "#FFFFFF" )
156
+ . attr ( "stroke-opacity" , 0.65 )
157
+ . selectAll ( "path" )
158
+ . data ( contours ) // Here is where the contours gets added
159
+ . join ( "path" )
160
+ . attr ( "fill" , getColor ) // Here is the color value!
161
+ . style ( "opacity" , 0.85 )
162
+ . attr ( "d" , d3 . geoPath ( ) ) ;
163
+
113
164
} ) . catch ( error => {
165
+ console . log ( error ) ;
114
166
alertMessage ( "Could not fit model, check JSON hyperparams and try again!" ) ;
115
167
} ) ;
116
168
}
117
169
118
170
// Reset the plotting area
119
171
reset ( ) {
120
- this . dataset = [ ] ;
121
- this . svg . selectAll ( "circle" ) . remove ( ) ;
122
- $ ( "#metrics" ) . removeClass ( "visible" ) . addClass ( "invisible" ) ;
172
+ this . dataset = [ ] ;
173
+ this . svg . selectAll ( "circle" ) . remove ( ) ;
174
+ this . svg . selectAll ( "g" ) . remove ( ) ;
175
+ $ ( "#metrics" ) . removeClass ( "visible" ) . addClass ( "invisible" ) ;
123
176
}
124
177
125
178
// Count the number of classes in the dataset
@@ -132,6 +185,46 @@ class Dataspace {
132
185
} , [ ] ) ;
133
186
}
134
187
188
+ // Create the contours grid to pass to the predict function.
189
+ contoursGrid ( ) {
190
+ var self = this ;
191
+ const q = 4 ;
192
+ const x0 = - q / 2 , x1 = this . width + margin . right + q ;
193
+ const y0 = - q / 2 , y1 = this . height + q ;
194
+ const n = Math . ceil ( ( x1 - x0 ) / q ) ;
195
+ const m = Math . ceil ( ( y1 - y0 ) / q ) ;
196
+ const grid = new Array ( n * m ) ;
197
+ grid . x = - q ;
198
+ grid . y = - q ;
199
+ grid . k = q ;
200
+ grid . n = n ;
201
+ grid . m = m ;
202
+
203
+ // Converts from grid coordinates (indexes) to screen coordinates (pixels).
204
+ grid . transform = ( { type, value, coordinates } ) => {
205
+ return {
206
+ type, value, coordinates : coordinates . map ( rings => {
207
+ return rings . map ( points => {
208
+ return points . map ( ( [ x , y ] ) => ( [
209
+ grid . x + grid . k * x ,
210
+ grid . y + grid . k * y
211
+ ] ) ) ;
212
+ } ) ;
213
+ } )
214
+ } ;
215
+ }
216
+
217
+ // We just have to pass the x and y values to the server to predict them using the model, then the rest of the code is the sames?
218
+ for ( let j = 0 ; j < m ; ++ j ) {
219
+ for ( let i = 0 ; i < n ; ++ i ) {
220
+ var obj = { x : this . xScale . invert ( i * q + x0 ) , y : this . yScale . invert ( j * q + y0 ) } ;
221
+ grid [ j * grid . n + i ] = obj ;
222
+ }
223
+ }
224
+
225
+ return grid ;
226
+ }
227
+
135
228
}
136
229
137
230
$ ( document ) . ready ( function ( ) {
0 commit comments