1
- // to see this example, run mapquery and go to localhost:3000/examples
1
+ /**
2
+ * Three ways you can use Mapquery data
3
+ *
4
+ * The Mapquery search page allows you to output data in several ways.
5
+ * You can save an SVG, save raw map data, or use the API call Mapquery
6
+ * generates based on your search parameters. Here's how you might use
7
+ * each of those output options together with real-world data to make a graphic.
8
+ *
9
+ * To provide a full demo of each method, each section below is independent
10
+ * from the others, so you'll notice a lot of repition in each.
11
+ *
12
+ * Static files used:
13
+ * data/cb_2014_us_county_20m.svg is topojson data exported from mapquery
14
+ * data/cb_2014_us_county_20m.json is topojson data exported from mapquery
15
+ * data/worker-gender.csv is data from the us census on worker gender by county
16
+ */
2
17
18
+ // establish some globals
3
19
var color = d3 . scale . threshold ( )
4
20
. domain ( [ 40 , 42 , 44 , 46 , 48 , 50 ] )
5
- . range ( [ '#a3d3f0' , '#8cbad8' , '#74a2bf' , '#5d8ba9' , '#467492' , '#2f5e7c' , '#154866' ] ) ;
21
+ . range ( [ '#a3d3f0' , '#8cbad8' , '#74a2bf' , '#5d8ba9' , '#467492' , '#2f5e7c' , '#154866' ] ) ,
22
+ width = 940 ,
23
+ height = 500 ;
6
24
7
- // add static svg to #map-1 div
8
- d3 . xml ( "data/cb_2014_us_county_20m.svg" , "image/svg+xml" , function ( xml ) {
9
- var mapContainer = d3 . select ( "#map-1" ) ;
10
- mapContainer . node ( ) . appendChild ( xml . documentElement ) ;
11
- var svg = d3 . select ( "svg" ) . attr ( "id" , "map-svg" ) ;
25
+ //
26
+ // Example 1: Bind data to static SVG
27
+ //
12
28
13
- // then load mappable data
14
- d3 . csv ( "data/worker-gender.csv" , function ( error , data ) {
29
+ function staticSvg ( ) {
30
+ // add static svg to #map-1 div
31
+ d3 . xml ( "data/cb_2014_us_county_20m.svg" , "image/svg+xml" , function ( xml ) {
32
+ var mapContainer = d3 . select ( "#map-1" ) ;
33
+ mapContainer . node ( ) . appendChild ( xml . documentElement ) ;
34
+ var svg = d3 . select ( "svg" ) . attr ( "id" , "map-svg" ) ;
15
35
16
- d3 . map ( data , function ( d ) {
17
- d . fips = d3 . format ( "05d" ) ( d . fips ) ;
18
- d . femalePct = ( + d . female_total / + d . pop_total ) * 100 ;
36
+ // load mappable data
37
+ d3 . csv ( "data/worker-gender.csv" , function ( error , data ) {
38
+
39
+ // format some fields in the census data
40
+ data . forEach ( function ( d ) {
41
+ d . fips = d3 . format ( "05d" ) ( d . fips ) ;
42
+ d . femalePct = ( + d . female_total / + d . pop_total ) * 100 ;
43
+ } ) ;
44
+
45
+ // create a selector by the fips field in the census data
46
+ // fips is a five-digit unique ID for each county
47
+ var dataByFips = d3 . nest ( )
48
+ . key ( function ( d ) { return d . fips } )
49
+ . map ( data ) ;
50
+
51
+ // loop through SVG's path elements
52
+ var countyPaths = svg . selectAll ( "path" )
53
+ . each ( function ( d ) {
54
+
55
+ // get the id from the path element,
56
+ // which mapquery sets with a given dataset's
57
+ // unique identifier. in this case, fips code.
58
+ // note that mapquery prepends a letter to the id,
59
+ // which you should remove to get the code
60
+ var id = d3 . select ( this ) . attr ( "id" ) . substr ( 1 ) ;
61
+
62
+ // then look for matches between the fips code from the id
63
+ // and the fips code in the data we've loaded
64
+ // bind that datum when a match is found
65
+ var datum = ( typeof dataByFips [ id ] !== "undefined" ) ? dataByFips [ id ] [ 0 ] : { } ;
66
+ d3 . select ( this ) . datum ( datum ) ;
67
+
68
+ } )
69
+ . style ( "fill" , function ( d ) {
70
+ // then use the bound datum to set the fill of each path
71
+ if ( dataByFips [ d . fips ] ) return color ( + dataByFips [ d . fips ] [ 0 ] . femalePct ) ;
72
+ } ) ;
19
73
} ) ;
74
+ } ) ;
75
+ }
76
+ staticSvg ( ) ;
20
77
21
- var dataByFips = d3 . nest ( )
22
- . key ( function ( d ) { return d . fips } )
23
- . map ( data ) ;
24
-
25
- // loop through SVG's path elements
26
- var countyPaths = svg . selectAll ( "path" )
27
- . each ( function ( d ) {
28
-
29
- // get the id from the path element,
30
- // which mapquery sets with a given dataset's
31
- // unique identifier. in this case, fips code.
32
- // note that mapquery prepends a letter to the id,
33
- // which you should remove to get the code
34
- var id = d3 . select ( this ) . attr ( "id" ) . substr ( 1 ) ;
35
-
36
- // then look for matches between the fips code from the id
37
- // and the fips code in the data we've loaded
38
- // bind that datum when a match is found
39
- var datum = ( typeof dataByFips [ id ] !== "undefined" ) ? dataByFips [ id ] [ 0 ] : { } ;
40
- d3 . select ( this ) . datum ( datum ) ;
78
+ //
79
+ // Example 2: Load data from static topojson file
80
+ //
41
81
42
- } )
43
- . style ( "fill" , function ( d ) {
44
- // then use the bound datum to set the fill of each path
45
- if ( dataByFips [ d . fips ] ) return color ( + dataByFips [ d . fips ] [ 0 ] . femalePct ) ;
46
- } ) ;
82
+ // we're using queue to load our two static data files
83
+ queue ( )
84
+ . defer ( d3 . json , "data/cb_2014_us_county_20m.json" )
85
+ . defer ( d3 . csv , "data/worker-gender.csv" )
86
+ . await ( loadStaticData ) ;
87
+
88
+ function loadStaticData ( err , mapData , workerData ) {
89
+
90
+ // append an svg
91
+ var svg = d3 . select ( "#map-2" ) . append ( "svg:svg" )
92
+ . attr ( "id" , "map-2-svg" )
93
+ . attr ( "width" , width )
94
+ . attr ( "height" , height ) ;
95
+
96
+ // format some fields in the census data
97
+ workerData . forEach ( function ( d ) {
98
+ d . fips = d3 . format ( "05d" ) ( d . fips ) ;
99
+ d . femalePct = ( + d . female_total / + d . pop_total ) * 100 ;
47
100
} ) ;
48
- } ) ;
49
101
50
- instaScale ( color , 600 ) ;
102
+ // create a selector by the fips field in the census data
103
+ // fips is a five-digit unique ID for each county
104
+ var dataByFips = d3 . nest ( )
105
+ . key ( function ( d ) { return d . fips } )
106
+ . map ( workerData ) ;
107
+
108
+ // get the name of the field in our map data
109
+ // that is denoted as a unique identifier
110
+ // in the metadata table.
111
+ // in this case, we know it's fips, but this
112
+ // is how we can get the field dynamically
113
+ var uniqueFld = ( mapData . table_metadata . fld_identifier ) ? mapData . table_metadata . fld_identifier : "name" ;
114
+
115
+ // setup the projection using the handy
116
+ // metadata mapquery provided
117
+ var projection = d3 . geo [ mapData . projection ] ( )
118
+ . scale ( mapData . scale )
119
+ . translate ( mapData . translate ) ;
120
+
121
+ var path = d3 . geo . path ( )
122
+ . projection ( projection ) ;
123
+
124
+ // explicitly select the toposon map units.
125
+ // for geojson it would be in data.map.features
126
+ var units = mapData . map . objects . features ;
127
+
128
+ // append each county path
129
+ svg . selectAll ( ".units-2" )
130
+ . data ( units )
131
+ . enter ( ) . append ( "path" )
132
+ . attr ( "class" , "units-2" )
133
+ . attr ( "id" , function ( d ) { return "u" + d . properties [ uniqueFld ] ; } )
134
+ . attr ( "d" , path )
135
+ . style ( "fill" , function ( d ) {
136
+ // get the fips code, the unique id, from this path's properties
137
+ var fips = d3 . format ( "05d" ) ( d . properties [ uniqueFld ] ) ;
138
+ // select the census data by the fips code
139
+ // when there's a match, set a color based on
140
+ // the percentage of female workers in that county
141
+ if ( dataByFips [ fips ] ) return color ( + dataByFips [ fips ] [ 0 ] . femalePct ) ;
142
+ } ) ;
143
+
144
+ }
145
+
146
+ //
147
+ // Example 3: Load data directly from the API
148
+ //
51
149
52
- function instaScale ( colorScale , w ) {
150
+ // we're using queue to load our two static data files.
151
+ // cb_2014_us_county_20m.json is topojson data exported from mapquery
152
+ // worker-gender.csv is data from the us census on worker gender by county
153
+ queue ( )
154
+ . defer ( d3 . json , "/api/feature-collection?table=cb_2014_us_county_20m&field_value=&proj=albersUsa&width=940&height=500&datatype=topojson" )
155
+ . defer ( d3 . csv , "data/worker-gender.csv" )
156
+ . await ( loadApiData ) ;
157
+
158
+ function loadApiData ( err , result , workerData ) {
159
+
160
+ // append an svg
161
+ var svg = d3 . select ( "#map-3" ) . append ( "svg:svg" )
162
+ . attr ( "id" , "map-3-svg" )
163
+ . attr ( "width" , width )
164
+ . attr ( "height" , height ) ;
165
+
166
+ // format some fields in the census data
167
+ workerData . forEach ( function ( d ) {
168
+ d . fips = d3 . format ( "05d" ) ( d . fips ) ;
169
+ d . femalePct = ( + d . female_total / + d . pop_total ) * 100 ;
170
+ } ) ;
171
+
172
+ // create a selector by the fips field in the census data
173
+ // fips is a five-digit unique ID for each county
174
+ var dataByFips = d3 . nest ( )
175
+ . key ( function ( d ) { return d . fips } )
176
+ . map ( workerData ) ;
177
+
178
+ // grap the map data from the API result
179
+ var mapData = result . data ;
180
+
181
+ // get the name of the field in our map data
182
+ // that is denoted as a unique identifier
183
+ // in the metadata table.
184
+ // in this case, we know it's fips, but this
185
+ // is how we can get the field dynamically
186
+ var uniqueFld = ( mapData . table_metadata . fld_identifier ) ? mapData . table_metadata . fld_identifier : "name" ;
187
+
188
+ // setup the projection using the handy
189
+ // metadata mapquery provided
190
+ var projection = d3 . geo [ mapData . projection ] ( )
191
+ . scale ( mapData . scale )
192
+ . translate ( mapData . translate ) ;
193
+
194
+ var path = d3 . geo . path ( )
195
+ . projection ( projection ) ;
196
+
197
+ // explicitly select the topojson map units.
198
+ // for geojson it would be in data.map.features
199
+ var units = mapData . map . objects . features ;
200
+
201
+ // append each county path
202
+ svg . selectAll ( ".units-3" )
203
+ . data ( units )
204
+ . enter ( ) . append ( "path" )
205
+ . attr ( "class" , "units-3" )
206
+ . attr ( "id" , function ( d ) { return "u" + d . properties [ uniqueFld ] ; } )
207
+ . attr ( "d" , path )
208
+ . style ( "fill" , function ( d ) {
209
+ // get the fips code, the unique id, from this path's properties
210
+ var fips = d3 . format ( "05d" ) ( d . properties [ uniqueFld ] ) ;
211
+ // select the census data by the fips code
212
+ // when there's a match, set a color based on
213
+ // the percentage of female workers in that county
214
+ if ( dataByFips [ fips ] ) return color ( + dataByFips [ fips ] [ 0 ] . femalePct ) ;
215
+ } ) ;
216
+
217
+ }
218
+
219
+ //
220
+ // Below we just call a nifty/weird little function
221
+ // that dynamically creates and appends a key
222
+ // to represent an arbitrary scale.
223
+ //
224
+ // This is not part of the demo, but feel free
225
+ // to try it out and let us know if you make improvements to it!
226
+ //
227
+
228
+ instaScale ( color , 600 , "scale-1" ) ;
229
+ instaScale ( color , 600 , "scale-2" ) ;
230
+ instaScale ( color , 600 , "scale-3" ) ;
231
+
232
+ function instaScale ( colorScale , w , div ) {
53
233
w = ( w < 300 ) ? 300 : w ;
54
234
55
- var svgScale = d3 . select ( "#scale-1" ) . append ( "svg" )
235
+ var svgScale = d3 . select ( "#" + div ) . append ( "svg" )
56
236
. attr ( "width" , w )
57
237
. attr ( "height" , 100 ) ;
58
238
@@ -62,7 +242,7 @@ function instaScale(colorScale,w) {
62
242
. domain ( scaleDomain )
63
243
. range ( [ 10 , w - 10 ] ) ;
64
244
65
- svgScale . selectAll ( ".scale -rects" )
245
+ svgScale . selectAll ( "." + div + " -rects")
66
246
. data ( colorScale . range ( ) . map ( function ( d , i ) {
67
247
return {
68
248
x0 : i ? x ( colorScale . domain ( ) [ i - 1 ] ) : x . range ( ) [ 0 ] ,
@@ -71,28 +251,28 @@ function instaScale(colorScale,w) {
71
251
} ;
72
252
} ) )
73
253
. enter ( ) . append ( "rect" )
74
- . attr ( "id" , function ( d , i ) { return "scale -"+ i ; } )
75
- . attr ( "class" , "scale -rects")
254
+ . attr ( "id" , function ( d , i ) { return div + " -"+ i ; } )
255
+ . attr ( "class" , div + " -rects")
76
256
. attr ( "width" , function ( d ) { return d . x1 - d . x0 - 5 ; } )
77
257
. attr ( "height" , 17.5 )
78
258
. attr ( "x" , function ( d ) { return d . x0 ; } )
79
259
. attr ( "y" , 25 )
80
260
. style ( "fill" , function ( d ) { return d . z ; } ) ;
81
261
82
262
svgScale . append ( "text" )
83
- . attr ( "class" , "arrow-labels" )
84
263
. attr ( "x" , 10 )
85
264
. attr ( "y" , 15 )
86
- . text ( "Share of female workers" ) ;
265
+ . text ( "Share of female workers" )
266
+ . style ( "font-family" , "'AdelleSans-Bold', Helvetica, Arial, sans-serif" ) ;
87
267
88
- svgScale . selectAll ( ".scale -labels" )
268
+ svgScale . selectAll ( "." + div + " -labels")
89
269
. data ( colorScale . range ( ) . map ( function ( d , i ) {
90
270
return {
91
271
x0 : i ? x ( colorScale . domain ( ) [ i - 1 ] ) : x . range ( ) [ 0 ] ,
92
272
} ;
93
273
} ) )
94
274
. enter ( ) . append ( "text" )
95
- . attr ( "class" , "scale -labels")
275
+ . attr ( "class" , div + " -labels")
96
276
. attr ( "x" , function ( d , i ) { if ( i == 0 ) { return d . x0 - 5 ; } else { return d . x0 - 10 ; } } )
97
277
. attr ( "y" , 60 )
98
278
. text ( function ( d , i ) {
@@ -104,5 +284,7 @@ function instaScale(colorScale,w) {
104
284
} else {
105
285
return l ;
106
286
}
107
- } ) ;
287
+ } )
288
+ . style ( "font-family" , "'AdelleSans-Regular', 'Helvetica Neue', Arial, Helvetica, sans-serif" )
289
+ . style ( "font-size" , "14px" ) ;
108
290
}
0 commit comments