@cdc/map 2.6.4 → 9.22.9
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.
- package/dist/cdcmap.js +22 -16
- package/examples/default-county.json +64 -12
- package/examples/default-hex.json +3 -1
- package/examples/example-city-state.json +10 -1
- package/examples/gallery/categorical-qualitative.json +797 -0
- package/examples/gallery/categorical-scale-based.json +739 -0
- package/examples/gallery/city-state.json +479 -0
- package/examples/gallery/county.json +22731 -0
- package/examples/gallery/equal-interval.json +1027 -0
- package/examples/gallery/equal-number.json +1027 -0
- package/examples/gallery/filterable.json +909 -0
- package/examples/gallery/hex-filtered.json +420 -0
- package/examples/gallery/hex.json +413 -0
- package/examples/gallery/single-state.json +21402 -0
- package/examples/gallery/world.json +1592 -0
- package/examples/private/city-state.json +428 -0
- package/examples/private/cty-issue.json +42768 -0
- package/examples/private/default-usa.json +460 -0
- package/examples/private/legend-issue.json +1 -0
- package/examples/private/map-rounding-error.json +42759 -0
- package/examples/private/monkeypox.json +376 -0
- package/examples/private/valid-data-map.csv +59 -0
- package/examples/private/wcmsrd-14492-data.json +292 -0
- package/examples/private/wcmsrd-14492.json +114 -0
- package/package.json +3 -3
- package/src/CdcMap.js +204 -127
- package/src/components/BubbleList.js +9 -5
- package/src/components/CityList.js +22 -4
- package/src/components/CountyMap.js +13 -4
- package/src/components/DataTable.js +9 -9
- package/src/components/EditorPanel.js +239 -121
- package/src/components/Modal.js +2 -1
- package/src/components/NavigationMenu.js +4 -3
- package/src/components/Sidebar.js +14 -19
- package/src/components/SingleStateMap.js +10 -4
- package/src/components/UsaMap.js +82 -30
- package/src/components/UsaRegionMap.js +3 -2
- package/src/components/WorldMap.js +7 -2
- package/src/data/{dfc-map.json → county-map.json} +0 -0
- package/src/data/initial-state.js +2 -1
- package/src/data/supported-geos.js +5 -0
- package/src/index.html +3 -8
- package/src/scss/editor-panel.scss +2 -2
- package/src/scss/main.scss +1 -1
- package/src/scss/map.scss +4 -0
- package/src/scss/sidebar.scss +2 -1
|
@@ -7,7 +7,7 @@ import { feature, mesh } from "topojson-client";
|
|
|
7
7
|
import { CustomProjection } from '@visx/geo';
|
|
8
8
|
import colorPalettes from '../../../core/data/colorPalettes';
|
|
9
9
|
import { geoAlbersUsaTerritories } from 'd3-composite-projections';
|
|
10
|
-
import testJSON from '../data/
|
|
10
|
+
import testJSON from '../data/county-map.json';
|
|
11
11
|
|
|
12
12
|
const abbrs = {
|
|
13
13
|
Alabama: 'AL',
|
|
@@ -99,7 +99,8 @@ const SingleStateMap = (props) => {
|
|
|
99
99
|
supportedTerritories,
|
|
100
100
|
rebuildTooltips,
|
|
101
101
|
runtimeLegend,
|
|
102
|
-
generateColorsArray
|
|
102
|
+
generateColorsArray,
|
|
103
|
+
handleMapAriaLabels
|
|
103
104
|
} = props;
|
|
104
105
|
|
|
105
106
|
|
|
@@ -290,11 +291,16 @@ const SingleStateMap = (props) => {
|
|
|
290
291
|
return geosJsx;
|
|
291
292
|
};
|
|
292
293
|
|
|
293
|
-
|
|
294
294
|
return (
|
|
295
295
|
<ErrorBoundary component="SingleStateMap">
|
|
296
296
|
{stateToShow &&
|
|
297
|
-
<svg
|
|
297
|
+
<svg
|
|
298
|
+
viewBox={`0 0 ${WIDTH} ${HEIGHT}`}
|
|
299
|
+
preserveAspectRatio="xMinYMin"
|
|
300
|
+
className="svg-container"
|
|
301
|
+
role="img"
|
|
302
|
+
aria-label={handleMapAriaLabels(state)}
|
|
303
|
+
>
|
|
298
304
|
<rect className="background center-container ocean" width={WIDTH} height={HEIGHT} fillOpacity={1} fill="white"></rect>
|
|
299
305
|
<CustomProjection
|
|
300
306
|
data={ [{states: stateToShow, counties: countiesToShow }] }
|
package/src/components/UsaMap.js
CHANGED
|
@@ -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]) <
|
|
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) <
|
|
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,
|
|
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,8 @@ const UsaMap = (props) => {
|
|
|
307
353
|
displayGeoName={displayGeoName}
|
|
308
354
|
applyLegendToRow={applyLegendToRow}
|
|
309
355
|
titleCase={titleCase}
|
|
356
|
+
setSharedFilterValue={setSharedFilterValue}
|
|
357
|
+
isFilterValueSupported={isFilterValueSupported}
|
|
310
358
|
/>)
|
|
311
359
|
|
|
312
360
|
// Bubbles
|
|
@@ -330,7 +378,11 @@ const UsaMap = (props) => {
|
|
|
330
378
|
|
|
331
379
|
return (
|
|
332
380
|
<ErrorBoundary component="UsaMap">
|
|
333
|
-
<svg
|
|
381
|
+
<svg
|
|
382
|
+
viewBox="0 0 880 500"
|
|
383
|
+
role="img"
|
|
384
|
+
aria-label={handleMapAriaLabels(state)}
|
|
385
|
+
>
|
|
334
386
|
{state.general.displayAsHex ?
|
|
335
387
|
(<Mercator data={unitedStatesHex} scale={650} translate={[1600, 775]}>
|
|
336
388
|
{({ 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
|
|
@@ -198,7 +199,11 @@ const ZoomControls = ({position, setPosition, state, setState, setRuntimeData, g
|
|
|
198
199
|
return (
|
|
199
200
|
<ErrorBoundary component="WorldMap">
|
|
200
201
|
{hasZoom ? (
|
|
201
|
-
<svg
|
|
202
|
+
<svg
|
|
203
|
+
viewBox="0 0 880 500"
|
|
204
|
+
role="img"
|
|
205
|
+
aria-label={handleMapAriaLabels(state)}
|
|
206
|
+
>
|
|
202
207
|
<rect height={500} width={880} onClick={() => handleReset(state, setState, setRuntimeData, generateRuntimeData)} fill="white"/>
|
|
203
208
|
<ZoomableGroup
|
|
204
209
|
zoom={position.zoom}
|
|
File without changes
|
|
@@ -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],
|
package/src/index.html
CHANGED
|
@@ -14,24 +14,19 @@
|
|
|
14
14
|
</head>
|
|
15
15
|
<body>
|
|
16
16
|
<!-- DEFAULT EXAMPLES -->
|
|
17
|
-
|
|
18
|
-
<div class="react-container react-container--maps" data-config="/examples/default-usa-regions.json"></div>
|
|
19
|
-
<!-- <div class="react-container react-container--maps" data-config="/examples/default-usa.json"></div>-->
|
|
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> -->
|
|
20
19
|
<!-- <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
20
|
<!-- <div class="react-container react-container--maps" data-config="/examples/bubble-us.json"></div/> -->
|
|
23
21
|
<!-- <div class="react-container react-container--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
22
|
<!-- <div class="react-container react-container--maps" data-config="/examples/default-single-state.json"></div> -->
|
|
27
23
|
|
|
28
24
|
<!-- TP4 EXAMPLES -->
|
|
29
25
|
<!-- <div class="react-container react-container--maps" data-config="/examples/example-city-state.json"></div> -->
|
|
30
26
|
<!-- <div class="react-container react-container--maps" data-config="/examples/example-world-map.json"></div> -->
|
|
31
27
|
<!-- <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
28
|
|
|
29
|
+
|
|
35
30
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
36
31
|
</body>
|
|
37
32
|
</html>
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
fill: currentColor
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
-
|
|
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;
|
package/src/scss/main.scss
CHANGED
package/src/scss/map.scss
CHANGED
|
@@ -155,6 +155,9 @@ header + .map-container.full-border {
|
|
|
155
155
|
height: 1.75em;
|
|
156
156
|
width: 1.75em;
|
|
157
157
|
}
|
|
158
|
+
&:focus {
|
|
159
|
+
background: #005eaa;
|
|
160
|
+
}
|
|
158
161
|
}
|
|
159
162
|
> button:first-child {
|
|
160
163
|
margin-right: 0.25em;
|
|
@@ -170,6 +173,7 @@ header + .map-container.full-border {
|
|
|
170
173
|
width: 2.5em;
|
|
171
174
|
}
|
|
172
175
|
}
|
|
176
|
+
|
|
173
177
|
}
|
|
174
178
|
|
|
175
179
|
@include breakpointClass(md) {
|
package/src/scss/sidebar.scss
CHANGED
|
@@ -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
|
}
|