@cdc/map 2.6.4 → 4.22.10

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 (48) hide show
  1. package/LICENSE +201 -0
  2. package/dist/cdcmap.js +22 -16
  3. package/examples/default-county.json +64 -12
  4. package/examples/default-geocode.json +746 -0
  5. package/examples/default-hex.json +3 -1
  6. package/examples/example-city-state.json +10 -1
  7. package/examples/gallery/categorical-qualitative.json +797 -0
  8. package/examples/gallery/categorical-scale-based.json +739 -0
  9. package/examples/gallery/city-state.json +479 -0
  10. package/examples/gallery/county.json +22731 -0
  11. package/examples/gallery/equal-interval.json +1027 -0
  12. package/examples/gallery/equal-number.json +1027 -0
  13. package/examples/gallery/filterable.json +909 -0
  14. package/examples/gallery/hex-filtered.json +420 -0
  15. package/examples/gallery/hex.json +413 -0
  16. package/examples/gallery/single-state.json +21402 -0
  17. package/examples/gallery/world.json +1592 -0
  18. package/examples/private/city-state.json +428 -0
  19. package/examples/private/cty-issue.json +42768 -0
  20. package/examples/private/default-usa.json +460 -0
  21. package/examples/private/legend-issue.json +1 -0
  22. package/examples/private/map-rounding-error.json +42759 -0
  23. package/examples/private/monkeypox.json +376 -0
  24. package/examples/private/valid-data-map.csv +59 -0
  25. package/examples/private/wcmsrd-14492-data.json +292 -0
  26. package/examples/private/wcmsrd-14492.json +114 -0
  27. package/package.json +3 -3
  28. package/src/CdcMap.js +1370 -1322
  29. package/src/components/BubbleList.js +9 -5
  30. package/src/components/CityList.js +63 -19
  31. package/src/components/CountyMap.js +101 -42
  32. package/src/components/DataTable.js +18 -15
  33. package/src/components/EditorPanel.js +319 -149
  34. package/src/components/Modal.js +2 -1
  35. package/src/components/NavigationMenu.js +4 -3
  36. package/src/components/Sidebar.js +14 -19
  37. package/src/components/SingleStateMap.js +177 -248
  38. package/src/components/UsaMap.js +83 -30
  39. package/src/components/UsaRegionMap.js +3 -2
  40. package/src/components/WorldMap.js +8 -2
  41. package/src/data/{dfc-map.json → county-map.json} +0 -0
  42. package/src/data/initial-state.js +5 -2
  43. package/src/data/supported-geos.js +10 -0
  44. package/src/index.html +4 -8
  45. package/src/scss/editor-panel.scss +2 -2
  46. package/src/scss/main.scss +1 -6
  47. package/src/scss/map.scss +18 -0
  48. package/src/scss/sidebar.scss +2 -1
@@ -10,6 +10,7 @@ import { AlbersUsa, Mercator } from '@visx/geo';
10
10
  import chroma from 'chroma-js';
11
11
  import CityList from './CityList';
12
12
  import BubbleList from './BubbleList';
13
+ import { supportedCities, supportedStates } from '../data/supported-geos';
13
14
 
14
15
  const { features: unitedStates } = feature(topoJSON, topoJSON.objects.states)
15
16
  const { features: unitedStatesHex } = feature(hexTopoJSON, hexTopoJSON.objects.states)
@@ -70,9 +71,31 @@ const UsaMap = (props) => {
70
71
  supportedTerritories,
71
72
  rebuildTooltips,
72
73
  titleCase,
73
- handleCircleClick
74
+ handleCircleClick,
75
+ setSharedFilterValue,
76
+ handleMapAriaLabels
74
77
  } = props;
75
78
 
79
+ let isFilterValueSupported = false;
80
+
81
+ if(setSharedFilterValue){
82
+ Object.keys(supportedStates).forEach(supportedState => {
83
+ if(supportedStates[supportedState].indexOf(setSharedFilterValue.toUpperCase()) !== -1){
84
+ isFilterValueSupported = true;
85
+ }
86
+ });
87
+ Object.keys(supportedTerritories).forEach(supportedTerritory => {
88
+ if(supportedTerritories[supportedTerritory].indexOf(setSharedFilterValue.toUpperCase()) !== -1){
89
+ isFilterValueSupported = true;
90
+ }
91
+ });
92
+ Object.keys(supportedCities).forEach(supportedCity => {
93
+ if(supportedCity === setSharedFilterValue.toUpperCase()){
94
+ isFilterValueSupported = true;
95
+ }
96
+ });
97
+ }
98
+
76
99
  // "Choose State" options
77
100
  const [extent, setExtent] = useState(null)
78
101
  const [focusedStates, setFocusedStates] = useState(unitedStates)
@@ -125,7 +148,7 @@ const UsaMap = (props) => {
125
148
 
126
149
  if (legendColors) {
127
150
  // Use white text if the background is dark, and dark grey if it's light
128
- if (chroma.contrast(textColor, legendColors[0]) < 4.5) {
151
+ if (chroma.contrast(textColor, legendColors[0]) < 3.5) {
129
152
  textColor = '#202020';
130
153
  }
131
154
 
@@ -139,6 +162,8 @@ const UsaMap = (props) => {
139
162
  styles = {
140
163
  color: textColor,
141
164
  fill: legendColors[0],
165
+ opacity: setSharedFilterValue && isFilterValueSupported && setSharedFilterValue !== territoryData[state.columns.geo.name] ? .5 : 1,
166
+ stroke: setSharedFilterValue && isFilterValueSupported && setSharedFilterValue === territoryData[state.columns.geo.name] ? 'rgba(0, 0, 0, 1)' : geoStrokeColor,
142
167
  cursor: needsPointer ? 'pointer' : 'default',
143
168
  '&:hover': {
144
169
  fill: legendColors[1],
@@ -155,7 +180,6 @@ const UsaMap = (props) => {
155
180
  text={styles.color}
156
181
  data-tip={toolTip}
157
182
  data-for="tooltip"
158
- stroke={geoStrokeColor}
159
183
  strokeWidth={1.5}
160
184
  onClick={() => geoClickHandler(territory, territoryData)}
161
185
  />)
@@ -171,7 +195,7 @@ const UsaMap = (props) => {
171
195
  let textColor = "#FFF"
172
196
 
173
197
  // Dynamic text color
174
- if (chroma.contrast(textColor, bgColor) < 4.5 ) {
198
+ if (chroma.contrast(textColor, bgColor) < 3.5 ) {
175
199
  textColor = '#202020';
176
200
  }
177
201
 
@@ -208,6 +232,25 @@ const UsaMap = (props) => {
208
232
  const constructGeoJsx = (geographies, projection) => {
209
233
  let showLabel = state.general.displayStateLabels
210
234
 
235
+ // Order alphabetically. Important for accessibility if ever read out loud.
236
+ geographies.map ( state => {
237
+ if(!state.feature.properties.iso) return;
238
+ state.feature.properties.name = titleCase(supportedStates[state.feature.properties.iso][0])
239
+ })
240
+
241
+ geographies.sort( (a,b) => {
242
+ const first = a.feature.properties.name.toUpperCase(); // ignore upper and lowercase
243
+ const second = b.feature.properties.name.toUpperCase(); // ignore upper and lowercase
244
+ if (first < second) {
245
+ return -1;
246
+ }
247
+ if (first > second) {
248
+ return 1;
249
+ }
250
+
251
+ // names must be equal
252
+ return 0;
253
+ })
211
254
  const geosJsx = geographies.map(( {feature: geo, path = ''}) => {
212
255
  const key = isHex ? geo.properties.iso + '-hex-group' : geo.properties.iso + '-group'
213
256
 
@@ -218,6 +261,7 @@ const UsaMap = (props) => {
218
261
 
219
262
  // Map the name from the geo data with the appropriate key for the processed data
220
263
  let geoKey = geo.properties.iso;
264
+ let geoName = geo.properties.name;
221
265
 
222
266
  // Manually add Washington D.C. in for Hex maps
223
267
 
@@ -240,6 +284,8 @@ const UsaMap = (props) => {
240
284
 
241
285
  styles = {
242
286
  fill: state.general.type !== 'bubble' ? legendColors[0] : '#E6E6E6',
287
+ opacity: setSharedFilterValue && isFilterValueSupported && setSharedFilterValue !== geoData[state.columns.geo.name] ? .5 : 1,
288
+ stroke: setSharedFilterValue && isFilterValueSupported && setSharedFilterValue === geoData[state.columns.geo.name] ? 'rgba(0, 0, 0, 1)' : geoStrokeColor,
243
289
  cursor: 'default',
244
290
  '&:hover': {
245
291
  fill: state.general.type !== 'bubble' ? legendColors[1] : '#e6e6e6',
@@ -253,43 +299,43 @@ const UsaMap = (props) => {
253
299
  if ((state.columns.navigate && geoData[state.columns.navigate.name]) || state.tooltips.appearanceType === 'click') {
254
300
  styles.cursor = 'pointer'
255
301
  }
256
-
257
302
  return (
303
+ <g data-name={geoName} key={key}>
304
+ <g
305
+ data-for="tooltip"
306
+ data-tip={tooltip}
307
+ className="geo-group"
308
+ css={styles}
309
+ onClick={() => geoClickHandler(geoDisplayName, geoData)}
310
+ >
311
+ <path
312
+ tabIndex={-1}
313
+ className='single-geo'
314
+ strokeWidth={1.3}
315
+ d={path}
316
+ />
317
+ {(isHex || showLabel) && geoLabel(geo, legendColors[0], projection)}
318
+ </g>
319
+ </g>
320
+ )
321
+ }
322
+
323
+ // Default return state, just geo with no additional information
324
+ return (
325
+ <g data-name={geoName} key={key}>
258
326
  <g
259
- data-for="tooltip"
260
- data-tip={tooltip}
261
- key={key}
262
327
  className="geo-group"
263
328
  css={styles}
264
- onClick={() => geoClickHandler(geoDisplayName, geoData)}
265
329
  >
266
330
  <path
267
331
  tabIndex={-1}
268
332
  className='single-geo'
269
333
  stroke={geoStrokeColor}
270
- strokeWidth={1.3}
334
+ strokeWidth={1.3}
271
335
  d={path}
272
336
  />
273
- {(isHex || showLabel) && geoLabel(geo, legendColors[0], projection)}
337
+ {(isHex || showLabel) && geoLabel(geo, styles.fill, projection)}
274
338
  </g>
275
- )
276
- }
277
-
278
- // Default return state, just geo with no additional information
279
- return (
280
- <g
281
- key={key}
282
- className="geo-group"
283
- css={styles}
284
- >
285
- <path
286
- tabIndex={-1}
287
- className='single-geo'
288
- stroke={geoStrokeColor}
289
- strokeWidth={1.3}
290
- d={path}
291
- />
292
- {(isHex || showLabel) && geoLabel(geo, styles.fill, projection)}
293
339
  </g>
294
340
  )
295
341
  });
@@ -307,6 +353,9 @@ const UsaMap = (props) => {
307
353
  displayGeoName={displayGeoName}
308
354
  applyLegendToRow={applyLegendToRow}
309
355
  titleCase={titleCase}
356
+ setSharedFilterValue={setSharedFilterValue}
357
+ isFilterValueSupported={isFilterValueSupported}
358
+ isGeoCodeMap={state.general.type === 'us-geocode'}
310
359
  />)
311
360
 
312
361
  // Bubbles
@@ -330,7 +379,11 @@ const UsaMap = (props) => {
330
379
 
331
380
  return (
332
381
  <ErrorBoundary component="UsaMap">
333
- <svg viewBox="0 0 880 500">
382
+ <svg
383
+ viewBox="0 0 880 500"
384
+ role="img"
385
+ aria-label={handleMapAriaLabels(state)}
386
+ >
334
387
  {state.general.displayAsHex ?
335
388
  (<Mercator data={unitedStatesHex} scale={650} translate={[1600, 775]}>
336
389
  {({ features, projection }) => constructGeoJsx(features, projection)}
@@ -32,7 +32,8 @@ const UsaRegionMap = (props) => {
32
32
  supportedTerritories,
33
33
  rebuildTooltips,
34
34
  titleCase,
35
- handleCircleClick
35
+ handleCircleClick,
36
+ handleMapAriaLabels
36
37
  } = props;
37
38
 
38
39
  // "Choose State" options
@@ -299,7 +300,7 @@ const UsaRegionMap = (props) => {
299
300
 
300
301
  return (
301
302
  <ErrorBoundary component="UsaRegionMap">
302
- <svg viewBox="0 0 880 500">
303
+ <svg viewBox="0 0 880 500" role="img" aria-label={handleMapAriaLabels(state)}>
303
304
 
304
305
  <Mercator data={focusedStates} scale={620} translate={[1500, 735]}>
305
306
  {({ features, projection }) => constructGeoJsx(features, projection)}
@@ -31,7 +31,8 @@ const WorldMap = (props) => {
31
31
  setFilteredCountryCode,
32
32
  position,
33
33
  setPosition,
34
- hasZoom
34
+ hasZoom,
35
+ handleMapAriaLabels
35
36
  } = props;
36
37
 
37
38
  // TODO Refactor - state should be set together here to avoid rerenders
@@ -173,6 +174,7 @@ const ZoomControls = ({position, setPosition, state, setState, setRuntimeData, g
173
174
  applyTooltipsToGeo={applyTooltipsToGeo}
174
175
  displayGeoName={displayGeoName}
175
176
  applyLegendToRow={applyLegendToRow}
177
+ isGeoCodeMap={state.general.type === 'us-geocode'}
176
178
  />)
177
179
 
178
180
  // Bubbles
@@ -198,7 +200,11 @@ const ZoomControls = ({position, setPosition, state, setState, setRuntimeData, g
198
200
  return (
199
201
  <ErrorBoundary component="WorldMap">
200
202
  {hasZoom ? (
201
- <svg viewBox="0 0 880 500">
203
+ <svg
204
+ viewBox="0 0 880 500"
205
+ role="img"
206
+ aria-label={handleMapAriaLabels(state)}
207
+ >
202
208
  <rect height={500} width={880} onClick={() => handleReset(state, setState, setRuntimeData, generateRuntimeData)} fill="white"/>
203
209
  <ZoomableGroup
204
210
  zoom={position.zoom}
File without changes
@@ -42,7 +42,9 @@ export default {
42
42
  },
43
43
  navigate: {
44
44
  name: ''
45
- }
45
+ },
46
+ latitude: { name: "" },
47
+ longitude: { name: "" }
46
48
  },
47
49
  legend: {
48
50
  descriptions: {},
@@ -70,7 +72,8 @@ export default {
70
72
  visual: {
71
73
  minBubbleSize: 1,
72
74
  maxBubbleSize: 20,
73
- extraBubbleBorder: false
75
+ extraBubbleBorder: false,
76
+ cityStyle: 'circle'
74
77
  },
75
78
  mapPosition:
76
79
  { coordinates: [0, 30], zoom: 1 }
@@ -497,7 +497,12 @@ export const supportedTerritories = {
497
497
  };
498
498
 
499
499
  export const supportedCities = {
500
+ 'GAINESVILLE': [-82.3248, 29.6516],
500
501
  'DISTRICT OF COLUMBIA': [-77.036873, 38.907192],
502
+ 'US-DC': [-77.036873, 38.907192],
503
+ 'WASHINGTON D.C.': [-77.036873, 38.907192],
504
+ 'WASHINGTON DC.': [-77.036873, 38.907192],
505
+ 'WASHINGTON DC': [-77.036873, 38.907192],
501
506
  'LOS ANGELES COUNTY': [-118.229362, 34.058762],
502
507
  MESA: [-111.831474, 33.415184],
503
508
  PHOENIX: [-112.074036, 33.448376],
@@ -531,6 +536,7 @@ export const supportedCities = {
531
536
  'NEW YORK CITY': [-74.005974, 40.712776],
532
537
  OMAHA: [-95.934502, 41.256538],
533
538
  'LAS VEGAS': [-115.139832, 36.169941],
539
+ SALEM:[-88.945618,38.626991],
534
540
  CLEVELAND: [-81.694359, 41.499321],
535
541
  COLUMBUS: [-82.998795, 39.961178],
536
542
  'OKLAHOMA CITY': [-97.516426, 35.467560],
@@ -557,6 +563,10 @@ export const supportedCities = {
557
563
  HONOLULU: [-157.858337, 21.306944],
558
564
  'SANTA ANA': [-117.867653, 33.745472],
559
565
  RIVERSIDE: [-117.375496, 33.980602],
566
+ 'SAN BERNARDINO':[-117.302399,34.115784],
567
+ 'SANTA CLARA':[-121.955238,37.354107],
568
+ MARION:[-88.9330556,37.7305556],
569
+ PASADENA:[-95.209099,29.691063],
560
570
  'CORPUS CHRISTI': [-97.396378, 27.800583],
561
571
  LEXINGTON: [-84.503716, 38.040585],
562
572
  STOCKTON: [-121.290779, 37.957703],
package/src/index.html CHANGED
@@ -15,23 +15,19 @@
15
15
  <body>
16
16
  <!-- DEFAULT EXAMPLES -->
17
17
  <!-- <div class="react-container react-container--maps" data-config="/examples/default-county.json"></div> -->
18
- <div class="react-container react-container--maps" data-config="/examples/default-usa-regions.json"></div>
19
- <!-- <div class="react-container react-container&#45;&#45;maps" data-config="/examples/default-usa.json"></div>-->
18
+ <!-- <div class="react-container react-container--maps" data-config="/examples/default-geocode.json"></div> -->
19
+ <!-- <div class="react-container react-container--maps" data-config="/examples/default-usa-regions.json"></div> -->
20
20
  <!-- <div class="react-container react-container--maps" data-config="/examples/default-world.json"></div> -->
21
- <!-- <div class="react-container react-container--maps" data-config="/examples/private/world.json"></div> -->
22
21
  <!-- <div class="react-container react-container--maps" data-config="/examples/bubble-us.json"></div/> -->
23
22
  <!-- <div class="react-container react-container&#45;&#45;maps" data-config="/examples/bubble-world.json"></div>-->
24
- <!-- <div class="react-container react-container--maps" data-config="/examples/private/nav-only.json"></div> -->
25
- <!-- <div class="react-container react-container--maps" data-config="/examples/private/example-hex-map-with-filter.json"></div> -->
26
- <!-- <div class="react-container react-container--maps" data-config="/examples/default-single-state.json"></div> -->
23
+ <div class="react-container react-container--maps" data-config="/examples/default-single-state.json"></div>
27
24
 
28
25
  <!-- TP4 EXAMPLES -->
29
26
  <!-- <div class="react-container react-container--maps" data-config="/examples/example-city-state.json"></div> -->
30
27
  <!-- <div class="react-container react-container--maps" data-config="/examples/example-world-map.json"></div> -->
31
28
  <!-- <div class="react-container react-container--maps" data-config="/examples/default-hex.json"></div> -->
32
- <!-- <div class="react-container react-container--maps" data-config="/examples/private/atsdr.json"></div> -->
33
-
34
29
 
30
+
35
31
  <noscript>You need to enable JavaScript to run this app.</noscript>
36
32
  </body>
37
33
  </html>
@@ -30,7 +30,8 @@
30
30
  fill: currentColor
31
31
  }
32
32
  }
33
- li {
33
+ button {
34
+ background: transparent;
34
35
  padding: .3em .75em;
35
36
  display: flex;
36
37
  border: $lightGray 1px solid;
@@ -239,7 +240,6 @@
239
240
  display: block;
240
241
  text-align: left;
241
242
  cursor: pointer;
242
- color: rgba(0,0,0,.5);
243
243
  text-decoration: underline;
244
244
  span {
245
245
  text-decoration: none;
@@ -9,11 +9,6 @@
9
9
  .loading > div.la-ball-beat {
10
10
  margin-top: 20%;
11
11
  }
12
- @include breakpointClass(xs) {
13
- div#tooltip {
14
- display: none !important;
15
- }
16
- }
17
12
  @include breakpointClass(md) {
18
13
  .map-container.modal-background::before {
19
14
  border-bottom: $lightGray 1px solid;
@@ -221,4 +216,4 @@
221
216
  outline-color: rgb(0, 95, 204)
222
217
  }
223
218
 
224
- }
219
+ }
package/src/scss/map.scss CHANGED
@@ -55,6 +55,12 @@ header + .map-container.full-border {
55
55
  flex-direction: row;
56
56
  }
57
57
  }
58
+
59
+ .map-container.us-geocode {
60
+ &.side {
61
+ flex-direction: row;
62
+ }
63
+ }
58
64
  }
59
65
 
60
66
  .geography-container {
@@ -155,6 +161,9 @@ header + .map-container.full-border {
155
161
  height: 1.75em;
156
162
  width: 1.75em;
157
163
  }
164
+ &:focus {
165
+ background: #005eaa;
166
+ }
158
167
  }
159
168
  > button:first-child {
160
169
  margin-right: 0.25em;
@@ -170,6 +179,7 @@ header + .map-container.full-border {
170
179
  width: 2.5em;
171
180
  }
172
181
  }
182
+
173
183
  }
174
184
 
175
185
  @include breakpointClass(md) {
@@ -292,3 +302,11 @@ header + .map-container.full-border {
292
302
  transform: translateY(34px);
293
303
  }
294
304
  }
305
+
306
+ .county-borders {
307
+ fill: none;
308
+ stroke-width: 1px;
309
+ stroke-linejoin: round;
310
+ stroke-linecap: round;
311
+ pointer-events: none;
312
+ }
@@ -68,6 +68,7 @@ aside {
68
68
  padding: 0;
69
69
  display: flex;
70
70
  flex-wrap: wrap;
71
+ button { font-size: unset; background: transparent; }
71
72
  li {
72
73
  flex-shrink: 0;
73
74
  display: inline-block;
@@ -76,7 +77,7 @@ aside {
76
77
  vertical-align: middle;
77
78
  transition: .1s opacity;
78
79
  display: flex;
79
- align-items: center;
80
+ // align-items: center;
80
81
  &.single-legend {
81
82
  cursor: pointer;
82
83
  }