@cdc/map 2.6.2 → 2.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/cdcmap.js +27 -27
  2. package/examples/default-county.json +105 -0
  3. package/examples/default-single-state.json +109 -0
  4. package/examples/default-usa.json +744 -603
  5. package/examples/example-city-state.json +474 -0
  6. package/examples/example-world-map.json +1596 -0
  7. package/examples/gender-rate-map.json +1 -0
  8. package/package.json +50 -47
  9. package/src/CdcMap.js +422 -159
  10. package/src/components/CityList.js +3 -2
  11. package/src/components/CountyMap.js +556 -0
  12. package/src/components/DataTable.js +73 -19
  13. package/src/components/EditorPanel.js +2088 -1230
  14. package/src/components/Sidebar.js +5 -5
  15. package/src/components/SingleStateMap.js +326 -0
  16. package/src/components/UsaMap.js +20 -3
  17. package/src/data/abbreviations.js +57 -0
  18. package/src/data/color-palettes.js +10 -1
  19. package/src/data/county-map-halfquality.json +58453 -0
  20. package/src/data/county-map-quarterquality.json +1 -0
  21. package/src/data/county-topo.json +1 -0
  22. package/src/data/dfc-map.json +1 -0
  23. package/src/data/initial-state.js +2 -2
  24. package/src/data/newtest.json +1 -0
  25. package/src/data/state-abbreviations.js +60 -0
  26. package/src/data/supported-geos.js +3504 -151
  27. package/src/data/test.json +1 -0
  28. package/src/hooks/useActiveElement.js +19 -0
  29. package/src/index.html +27 -20
  30. package/src/index.js +8 -4
  31. package/src/scss/datatable.scss +2 -1
  32. package/src/scss/main.scss +10 -1
  33. package/src/scss/map.scss +153 -123
  34. package/src/scss/sidebar.scss +0 -1
  35. package/uploads/upload-example-city-state.json +392 -0
  36. package/uploads/upload-example-world-data.json +1490 -0
  37. package/LICENSE +0 -201
@@ -1,4 +1,4 @@
1
- import React, { useState } from 'react';
1
+ import React from 'react';
2
2
  import parse from 'html-react-parser';
3
3
 
4
4
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
@@ -52,7 +52,7 @@ const Sidebar = (props) => {
52
52
 
53
53
  const entryMin = addCommas(entry.min);
54
54
 
55
- let formattedText = `${prefix + entryMin + suffix} - ${prefix + entryMax + suffix}`;
55
+ let formattedText = `${prefix + entryMin + suffix}${entryMax !== entryMin ? ` - ${prefix + entryMax + suffix}` : ''}`;
56
56
 
57
57
  // If interval, add some formatting
58
58
  if (legend.type === 'equalinterval' && idx !== runtimeLegend.length - 1) {
@@ -72,7 +72,7 @@ const Sidebar = (props) => {
72
72
  let legendLabel = formattedText;
73
73
 
74
74
  if (entry.hasOwnProperty('special')) {
75
- legendLabel = entry.value;
75
+ legendLabel = entry.label || entry.value;
76
76
  }
77
77
 
78
78
  return (
@@ -119,7 +119,7 @@ const Sidebar = (props) => {
119
119
 
120
120
  return (
121
121
  <ErrorBoundary component="Sidebar">
122
- <aside className={`${legend.position} ${legend.singleColumn ? 'single-column' : ''} ${viewport}`}>
122
+ <aside id="legend" className={`${legend.position} ${legend.singleColumn ? 'single-column cdcdataviz-sr-focusable' : 'cdcdataviz-sr-focusable'} ${viewport}`} role="region" aria-label="Legend" tabIndex="0">
123
123
  <section className="legend-section" aria-label="Map Legend">
124
124
  {runtimeLegend.disabledAmt > 0 &&
125
125
  (
@@ -164,4 +164,4 @@ const Sidebar = (props) => {
164
164
  );
165
165
  };
166
166
 
167
- export default Sidebar;
167
+ export default Sidebar;
@@ -0,0 +1,326 @@
1
+ import React, { useState, useEffect, memo, useRef } from 'react';
2
+ /** @jsx jsx */
3
+ import { jsx } from '@emotion/react'
4
+ import ErrorBoundary from '@cdc/core/components/ErrorBoundary';
5
+ import { geoCentroid, geoPath } from "d3-geo";
6
+ import { feature, mesh } from "topojson-client";
7
+ import { CustomProjection } from '@visx/geo';
8
+ import colorPalettes from '../data/color-palettes';
9
+ import { geoAlbersUsaTerritories } from 'd3-composite-projections';
10
+ import testJSON from '../data/dfc-map.json';
11
+
12
+ const abbrs = {
13
+ Alabama: 'AL',
14
+ Alaska: 'AK',
15
+ Arizona: 'AZ',
16
+ Arkansas: 'AR',
17
+ California: 'CA',
18
+ Colorado: 'CO',
19
+ Connecticut: 'CT',
20
+ Delaware: 'DE',
21
+ Florida: 'FL',
22
+ Georgia: 'GA',
23
+ Hawaii: 'HI',
24
+ Idaho: 'ID',
25
+ Illinois: 'IL',
26
+ Indiana: 'IN',
27
+ Iowa: 'IA',
28
+ Kansas: 'KS',
29
+ Kentucky: 'KY',
30
+ Louisiana: 'LA',
31
+ Maine: 'ME',
32
+ Maryland: 'MD',
33
+ Massachusetts: 'MA',
34
+ Michigan: 'MI',
35
+ Minnesota: 'MN',
36
+ Mississippi: 'MS',
37
+ Missouri: 'MO',
38
+ Montana: 'MT',
39
+ Nebraska: 'NE',
40
+ Nevada: 'NV',
41
+ 'New Hampshire': 'NH',
42
+ 'New Jersey': 'NJ',
43
+ 'New Mexico': 'NM',
44
+ 'New York': 'NY',
45
+ 'North Carolina': 'NC',
46
+ 'North Dakota': 'ND',
47
+ Ohio: 'OH',
48
+ Oklahoma: 'OK',
49
+ Oregon: 'OR',
50
+ Pennsylvania: 'PA',
51
+ 'Rhode Island': 'RI',
52
+ 'South Carolina': 'SC',
53
+ 'South Dakota': 'SD',
54
+ Tennessee: 'TN',
55
+ Texas: 'TX',
56
+ Utah: 'UT',
57
+ Vermont: 'VT',
58
+ Virginia: 'VA',
59
+ Washington: 'WA',
60
+ 'West Virginia': 'WV',
61
+ Wisconsin: 'WI',
62
+ Wyoming: 'WY',
63
+ 'District of Columbia': 'DC',
64
+ Guam: 'GU',
65
+ 'Virgin Islands': 'VI',
66
+ 'Puerto Rico': 'PR',
67
+ 'American Samoa': 'AS'
68
+ }
69
+
70
+
71
+
72
+ // SVG ITEMS
73
+ const WIDTH = 880;
74
+ const HEIGHT = 500;
75
+ const PADDING = 25;
76
+
77
+ // DATA
78
+ let { features: counties } = feature(testJSON, testJSON.objects.counties)
79
+ let { features: states } = feature(testJSON, testJSON.objects.states);
80
+
81
+ // CONSTANTS
82
+ const STATE_BORDER = '#c0cad4';
83
+ const STATE_INACTIVE_FILL = '#F4F7FA';
84
+
85
+ // CREATE STATE LINES
86
+ // const projection = geoAlbersUsaTerritories().translate([WIDTH/2,HEIGHT/2])
87
+
88
+
89
+
90
+ const SingleStateMap = (props) => {
91
+
92
+ const {
93
+ state,
94
+ applyTooltipsToGeo,
95
+ data,
96
+ geoClickHandler,
97
+ applyLegendToRow,
98
+ displayGeoName,
99
+ supportedTerritories,
100
+ rebuildTooltips,
101
+ runtimeLegend,
102
+ generateColorsArray
103
+ } = props;
104
+
105
+
106
+
107
+ const projection = geoAlbersUsaTerritories().translate([WIDTH/2, HEIGHT/2])
108
+
109
+ const geoStrokeColor = state.general.geoBorderColor === 'darkGray' ? 'rgba(0, 0, 0, 0.2)' : 'rgba(255,255,255,0.7)'
110
+
111
+ const [stateToShow, setStateToShow ] = useState( null )
112
+ const [countiesToShow, setCountiesToShow] = useState( null )
113
+ const [translate, setTranslate] = useState()
114
+ const [scale, setScale] = useState()
115
+ const [strokeWidth, setStrokeWidth ] = useState(.75)
116
+
117
+ let mapColorPalette = colorPalettes[state.color] || '#fff';
118
+ let focusedBorderColor = mapColorPalette[3];
119
+ useEffect(() => rebuildTooltips());
120
+
121
+ const path = geoPath().projection(projection)
122
+
123
+ // When choosing a state changes...
124
+ useEffect(() => {
125
+ if(state.general.hasOwnProperty('statePicked')) {
126
+ let statePicked = state.general.statePicked.stateName;
127
+ let statePickedData = states.find(s => s.properties.name === statePicked)
128
+ setStateToShow(statePickedData)
129
+
130
+ let countiesFound = counties.filter( c => c.id.substring(0,2) === state.general.statePicked.fipsCode)
131
+
132
+ setCountiesToShow(countiesFound)
133
+
134
+ const projection = geoAlbersUsaTerritories().translate([WIDTH/2,HEIGHT/2])
135
+ const newProjection = projection.fitExtent([[PADDING, PADDING], [WIDTH - PADDING, HEIGHT - PADDING]], statePickedData)
136
+ const newScale = newProjection.scale();
137
+ const newScaleWithHypot = newScale / 1070;
138
+
139
+ let [x, y] = newProjection.translate()
140
+ x = (x - WIDTH/2);
141
+ y = (y - HEIGHT/2);
142
+
143
+ setTranslate([x,y])
144
+ setScale(newScaleWithHypot)
145
+
146
+ }
147
+ }, [state.general.statePicked]);
148
+
149
+ const geoLabel = (geo, projection) => {
150
+ //projection = geoAlbersUsaTerritories().translate([WIDTH/2,HEIGHT/2])
151
+ //const newProjection = projection.fitExtent([[PADDING, PADDING], [WIDTH - PADDING, HEIGHT - PADDING]], geo)
152
+ let [x, y] = projection( geoCentroid(geo) )
153
+ let abbr = abbrs[geo.properties.name]
154
+ if (abbr === 'NJ') x += 3
155
+ if(undefined === abbr) return null
156
+ let [dx, dy] = offsets[geo.properties.name]
157
+
158
+ return (
159
+ <>
160
+ <line className="abbrLine" x1={x} y1={y} x2={x + dx} y2={y + dy} stroke="black" strokeWidth={1} />
161
+ <text className="abbrText" x={4} strokeWidth="0" fontSize={13} style={{fill: "#202020"}} alignmentBaseline="middle" transform={`translate(${x + dx}, ${y + dy})`}>
162
+ {abbr}
163
+ </text>
164
+ </>
165
+ )
166
+ }
167
+
168
+ // Constructs and displays markup for all geos on the map (except territories right now)
169
+ const constructGeoJsx = (geographies, projection) => {
170
+ const statePassed = geographies[0].feature.states;
171
+ const counties = geographies[0].feature.counties;
172
+
173
+ let geosJsx = [];
174
+
175
+
176
+ const StateOutput = () => {
177
+
178
+ let geo = testJSON.objects.states.geometries.filter( s => {
179
+ return s.id === statePassed.id
180
+ })
181
+
182
+
183
+ // const stateLine = path(mesh(testJSON, lines ))
184
+ let stateLines = path(mesh(testJSON, geo[0]))
185
+ return (
186
+ <g
187
+ key={'single-state'}
188
+ className="single-state"
189
+ style={{ fill: '#E6E6E6'}}
190
+ stroke={geoStrokeColor}
191
+ strokeWidth={ .95 / scale }
192
+ >
193
+ <path
194
+ tabIndex={-1}
195
+ className='state-path'
196
+ d={stateLines}
197
+ />
198
+ </g>
199
+ )
200
+ };
201
+
202
+ const countyOutput = ( counties.map(( county ) => {
203
+
204
+
205
+ // Map the name from the geo data with the appropriate key for the processed data
206
+ let geoKey = county.id;
207
+
208
+ if(!geoKey) return;
209
+
210
+ let countyPath = path(county);
211
+
212
+ let geoData = data[county.id];
213
+ let legendColors;
214
+
215
+ // Once we receive data for this geographic item, setup variables.
216
+ if (geoData !== undefined ) {
217
+ legendColors = applyLegendToRow(geoData);
218
+ }
219
+
220
+ const geoDisplayName = displayGeoName(geoKey);
221
+
222
+ // For some reason, these two geos are breaking the display.
223
+ if (geoDisplayName === 'Franklin City' || geoDisplayName === 'Waynesboro') return null;
224
+
225
+ const tooltip = applyTooltipsToGeo(geoDisplayName, geoData);
226
+
227
+ if (legendColors && legendColors[0] !== '#000000') {
228
+
229
+
230
+ let styles = {
231
+ fill: legendColors[0],
232
+ cursor: 'default',
233
+ '&:hover': {
234
+ fill: legendColors[1],
235
+ },
236
+ '&:active': {
237
+ fill: legendColors[2],
238
+ },
239
+ };
240
+
241
+ // When to add pointer cursor
242
+ if ((state.columns.navigate && geoData[state.columns.navigate.name]) || state.tooltips.appearanceType === 'hover') {
243
+ styles.cursor = 'pointer'
244
+ }
245
+
246
+ return (
247
+ <g
248
+ data-for="tooltip"
249
+ data-tip={tooltip}
250
+ key={`key--${county.id}`}
251
+ className={`county county--${geoDisplayName.split(" ").join("")} county--${geoData[state.columns.geo.name]}`}
252
+ css={styles}
253
+ onClick={() => geoClickHandler(geoDisplayName, geoData)}
254
+ >
255
+ <path
256
+ tabIndex={-1}
257
+ className={`county`}
258
+ stroke={geoStrokeColor}
259
+ d={countyPath}
260
+ strokeWidth={ .75 / scale }
261
+ />
262
+ </g>
263
+ )
264
+ } else {
265
+ return (
266
+ <g
267
+ data-for="tooltip"
268
+ data-tip={tooltip}
269
+ key={`key--${county.id}`}
270
+ className={`county county--${geoDisplayName.split(" ").join("")}`}
271
+ style={{ fill : '#e6e6e6'}}
272
+ >
273
+ <path
274
+ tabIndex={-1}
275
+ className={`county`}
276
+ stroke={geoStrokeColor}
277
+ d={countyPath}
278
+ strokeWidth={ .75 / scale }
279
+ />
280
+ </g>
281
+ )
282
+ }
283
+
284
+ }));
285
+
286
+
287
+ geosJsx.push( <StateOutput /> );
288
+ geosJsx.push( countyOutput );
289
+
290
+ return geosJsx;
291
+ };
292
+
293
+
294
+ return (
295
+ <ErrorBoundary component="SingleStateMap">
296
+ {stateToShow &&
297
+ <svg viewBox={`0 0 ${WIDTH} ${HEIGHT}`} preserveAspectRatio="xMinYMin" className="svg-container">
298
+ <rect className="background center-container ocean" width={WIDTH} height={HEIGHT} fillOpacity={1} fill="white"></rect>
299
+ <CustomProjection
300
+ data={ [{states: stateToShow, counties: countiesToShow }] }
301
+ // translate={translate}
302
+ // scale={scale}
303
+ projection={geoAlbersUsaTerritories}
304
+ fitExtent={[[[PADDING, PADDING], [WIDTH - PADDING, HEIGHT - PADDING]], stateToShow]}
305
+ >
306
+ { ({ features, projection }) => {
307
+ return (
308
+ <g
309
+ id="mapGroup"
310
+ className="countyMapGroup"
311
+ transform={`translate(${translate}) scale(${scale})`}
312
+ data-scale=""
313
+ key="countyMapGroup">
314
+ { constructGeoJsx(features, geoAlbersUsaTerritories) }
315
+ </g>
316
+ )
317
+ }}
318
+ </CustomProjection>
319
+ </svg>
320
+ }
321
+ {!stateToShow && 'No State Picked'}
322
+ </ErrorBoundary>
323
+ );
324
+ };
325
+
326
+ export default memo(SingleStateMap)
@@ -67,9 +67,21 @@ const UsaMap = (props) => {
67
67
  applyLegendToRow,
68
68
  displayGeoName,
69
69
  supportedTerritories,
70
- rebuildTooltips
70
+ rebuildTooltips,
71
+ titleCase
71
72
  } = props;
72
73
 
74
+ // "Choose State" options
75
+ const [extent, setExtent] = useState(null)
76
+ const [focusedStates, setFocusedStates] = useState(unitedStates)
77
+ const [translate, setTranslate] = useState([455,200])
78
+
79
+ // When returning from another map we want to reset the state
80
+ useEffect(() => {
81
+ setTranslate( [455,250] )
82
+ setExtent( null )
83
+ }, [state.general.geoType]);
84
+
73
85
  const isHex = state.general.displayAsHex
74
86
 
75
87
  const [territoriesData, setTerritoriesData] = useState([]);
@@ -295,11 +307,12 @@ const UsaMap = (props) => {
295
307
  applyTooltipsToGeo={applyTooltipsToGeo}
296
308
  displayGeoName={displayGeoName}
297
309
  applyLegendToRow={applyLegendToRow}
310
+ titleCase={titleCase}
298
311
  />)
299
312
 
300
313
  return geosJsx;
301
314
  };
302
-
315
+
303
316
  return (
304
317
  <ErrorBoundary component="UsaMap">
305
318
  <svg viewBox="0 0 880 500">
@@ -307,7 +320,11 @@ const UsaMap = (props) => {
307
320
  (<Mercator data={unitedStatesHex} scale={650} translate={[1600, 775]}>
308
321
  {({ features, projection }) => constructGeoJsx(features, projection)}
309
322
  </Mercator>) :
310
- (<AlbersUsa data={unitedStates} translate={[455, 250]}>
323
+ (<AlbersUsa
324
+ data={focusedStates}
325
+ translate={translate}
326
+ fitExtent={extent}
327
+ >
311
328
  {({ features, projection }) => constructGeoJsx(features, projection)}
312
329
  </AlbersUsa>)
313
330
  }
@@ -0,0 +1,57 @@
1
+ export const abbrs = {
2
+ Alabama: 'AL',
3
+ Alaska: 'AK',
4
+ Arizona: 'AZ',
5
+ Arkansas: 'AR',
6
+ California: 'CA',
7
+ Colorado: 'CO',
8
+ Connecticut: 'CT',
9
+ Delaware: 'DE',
10
+ Florida: 'FL',
11
+ Georgia: 'GA',
12
+ Hawaii: 'HI',
13
+ Idaho: 'ID',
14
+ Illinois: 'IL',
15
+ Indiana: 'IN',
16
+ Iowa: 'IA',
17
+ Kansas: 'KS',
18
+ Kentucky: 'KY',
19
+ Louisiana: 'LA',
20
+ Maine: 'ME',
21
+ Maryland: 'MD',
22
+ Massachusetts: 'MA',
23
+ Michigan: 'MI',
24
+ Minnesota: 'MN',
25
+ Mississippi: 'MS',
26
+ Missouri: 'MO',
27
+ Montana: 'MT',
28
+ Nebraska: 'NE',
29
+ Nevada: 'NV',
30
+ 'New Hampshire': 'NH',
31
+ 'New Jersey': 'NJ',
32
+ 'New Mexico': 'NM',
33
+ 'New York': 'NY',
34
+ 'North Carolina': 'NC',
35
+ 'North Dakota': 'ND',
36
+ Ohio: 'OH',
37
+ Oklahoma: 'OK',
38
+ Oregon: 'OR',
39
+ Pennsylvania: 'PA',
40
+ 'Rhode Island': 'RI',
41
+ 'South Carolina': 'SC',
42
+ 'South Dakota': 'SD',
43
+ Tennessee: 'TN',
44
+ Texas: 'TX',
45
+ Utah: 'UT',
46
+ Vermont: 'VT',
47
+ Virginia: 'VA',
48
+ Washington: 'WA',
49
+ 'West Virginia': 'WV',
50
+ Wisconsin: 'WI',
51
+ Wyoming: 'WY',
52
+ 'District of Columbia': 'DC',
53
+ Guam: 'GU',
54
+ 'Virgin Islands': 'VI',
55
+ 'Puerto Rico': 'PR',
56
+ 'American Samoa': 'AS',
57
+ };
@@ -187,5 +187,14 @@ export default {
187
187
  '#ff7f00',
188
188
  '#ff3',
189
189
  '#a65628',
190
- '#f781bf']
190
+ '#f781bf'],
191
+ qualitative9: [
192
+ '#497d0c',
193
+ '#84BC49',
194
+ '#88c3ea',
195
+ '#fcad90',
196
+ '#f26b4f',
197
+ '#c31b1f',
198
+ '#c31b1f'
199
+ ]
191
200
  };