@cdc/map 4.24.7 → 4.24.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 +40720 -38422
- package/examples/county-year.csv +10 -0
- package/examples/default-geocode.json +44 -10
- package/examples/default-patterns.json +0 -2
- package/examples/default-single-state.json +279 -108
- package/examples/map-issue-3.json +646 -0
- package/examples/single-state-filter.json +153 -0
- package/index.html +9 -6
- package/package.json +3 -3
- package/src/CdcMap.tsx +322 -126
- package/src/_stories/CdcMap.stories.tsx +7 -0
- package/src/_stories/_mock/DEV-8942.json +270 -0
- package/src/components/Annotation/AnnotationDropdown.tsx +1 -0
- package/src/components/{BubbleList.jsx → BubbleList.tsx} +1 -1
- package/src/components/{CityList.jsx → CityList.tsx} +28 -2
- package/src/components/{DataTable.jsx → DataTable.tsx} +2 -2
- package/src/components/EditorPanel/components/EditorPanel.tsx +647 -127
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +0 -22
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +61 -11
- package/src/components/Legend/components/Legend.tsx +125 -36
- package/src/components/Legend/components/index.scss +42 -42
- package/src/components/Modal.tsx +25 -0
- package/src/components/UsaMap/components/SingleState/SingleState.CountyOutput.tsx +74 -0
- package/src/components/UsaMap/components/SingleState/SingleState.StateOutput.tsx +29 -0
- package/src/components/UsaMap/components/SingleState/index.tsx +9 -0
- package/src/components/UsaMap/components/UsaMap.County.tsx +84 -33
- package/src/components/UsaMap/components/UsaMap.SingleState.tsx +173 -206
- package/src/components/UsaMap/components/UsaMap.State.tsx +161 -26
- package/src/components/UsaMap/data/us-extended-geography.json +1 -0
- package/src/components/UsaMap/helpers/map.ts +111 -0
- package/src/components/WorldMap/WorldMap.tsx +17 -32
- package/src/components/ZoomControls.tsx +41 -0
- package/src/data/initial-state.js +7 -1
- package/src/data/supported-geos.js +15 -4
- package/src/helpers/generateRuntimeLegendHash.ts +2 -2
- package/src/hooks/useStateZoom.tsx +157 -0
- package/src/hooks/{useZoomPan.js → useZoomPan.ts} +6 -5
- package/src/scss/editor-panel.scss +0 -4
- package/src/scss/main.scss +23 -1
- package/src/scss/map.scss +8 -0
- package/src/types/MapConfig.ts +9 -1
- package/src/types/MapContext.ts +14 -2
- package/src/components/Modal.jsx +0 -22
- /package/src/components/{Geo.jsx → Geo.tsx} +0 -0
- /package/src/components/{NavigationMenu.jsx → NavigationMenu.tsx} +0 -0
- /package/src/components/{ZoomableGroup.jsx → ZoomableGroup.tsx} +0 -0
|
@@ -23,7 +23,7 @@ import Territory from './Territory'
|
|
|
23
23
|
import useMapLayers from '../../../hooks/useMapLayers'
|
|
24
24
|
import ConfigContext from '../../../context'
|
|
25
25
|
import { MapContext } from '../../../types/MapContext'
|
|
26
|
-
import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
|
|
26
|
+
import { checkColorContrast, getContrastColor, getColorContrast } from '@cdc/core/helpers/cove/accessibility'
|
|
27
27
|
|
|
28
28
|
const { features: unitedStates } = feature(topoJSON, topoJSON.objects.states)
|
|
29
29
|
const { features: unitedStatesHex } = feature(hexTopoJSON, hexTopoJSON.objects.states)
|
|
@@ -66,7 +66,9 @@ const UsaMap = () => {
|
|
|
66
66
|
supportedTerritories,
|
|
67
67
|
titleCase,
|
|
68
68
|
tooltipId,
|
|
69
|
-
handleDragStateChange
|
|
69
|
+
handleDragStateChange,
|
|
70
|
+
setState,
|
|
71
|
+
mapId
|
|
70
72
|
} = useContext<MapContext>(ConfigContext)
|
|
71
73
|
|
|
72
74
|
let isFilterValueSupported = false
|
|
@@ -154,15 +156,28 @@ const UsaMap = () => {
|
|
|
154
156
|
let needsPointer = false
|
|
155
157
|
|
|
156
158
|
// If we need to add a pointer cursor
|
|
157
|
-
if (
|
|
159
|
+
if (
|
|
160
|
+
(state.columns.navigate && territoryData[state.columns.navigate.name]) ||
|
|
161
|
+
state.tooltips.appearanceType === 'click'
|
|
162
|
+
) {
|
|
158
163
|
needsPointer = true
|
|
159
164
|
}
|
|
160
165
|
|
|
161
166
|
styles = {
|
|
162
167
|
color: textColor,
|
|
163
168
|
fill: legendColors[0],
|
|
164
|
-
opacity:
|
|
165
|
-
|
|
169
|
+
opacity:
|
|
170
|
+
setSharedFilterValue &&
|
|
171
|
+
isFilterValueSupported &&
|
|
172
|
+
setSharedFilterValue !== territoryData[state.columns.geo.name]
|
|
173
|
+
? 0.5
|
|
174
|
+
: 1,
|
|
175
|
+
stroke:
|
|
176
|
+
setSharedFilterValue &&
|
|
177
|
+
isFilterValueSupported &&
|
|
178
|
+
setSharedFilterValue === territoryData[state.columns.geo.name]
|
|
179
|
+
? 'rgba(0, 0, 0, 1)'
|
|
180
|
+
: geoStrokeColor,
|
|
166
181
|
cursor: needsPointer ? 'pointer' : 'default',
|
|
167
182
|
'&:hover': {
|
|
168
183
|
fill: legendColors[1]
|
|
@@ -253,8 +268,14 @@ const UsaMap = () => {
|
|
|
253
268
|
|
|
254
269
|
styles = {
|
|
255
270
|
fill: state.general.type !== 'bubble' ? legendColors[0] : '#E6E6E6',
|
|
256
|
-
opacity:
|
|
257
|
-
|
|
271
|
+
opacity:
|
|
272
|
+
setSharedFilterValue && isFilterValueSupported && setSharedFilterValue !== geoData[state.columns.geo.name]
|
|
273
|
+
? 0.5
|
|
274
|
+
: 1,
|
|
275
|
+
stroke:
|
|
276
|
+
setSharedFilterValue && isFilterValueSupported && setSharedFilterValue === geoData[state.columns.geo.name]
|
|
277
|
+
? 'rgba(0, 0, 0, 1)'
|
|
278
|
+
: geoStrokeColor,
|
|
258
279
|
cursor: 'default',
|
|
259
280
|
'&:hover': {
|
|
260
281
|
fill: state.general.type !== 'bubble' ? legendColors[1] : '#e6e6e6'
|
|
@@ -265,7 +286,10 @@ const UsaMap = () => {
|
|
|
265
286
|
}
|
|
266
287
|
|
|
267
288
|
// When to add pointer cursor
|
|
268
|
-
if (
|
|
289
|
+
if (
|
|
290
|
+
(state.columns.navigate && geoData[state.columns.navigate.name]) ||
|
|
291
|
+
state.tooltips.appearanceType === 'click'
|
|
292
|
+
) {
|
|
269
293
|
styles.cursor = 'pointer'
|
|
270
294
|
}
|
|
271
295
|
|
|
@@ -283,33 +307,81 @@ const UsaMap = () => {
|
|
|
283
307
|
switch (item.operator) {
|
|
284
308
|
case '=':
|
|
285
309
|
if (geoData[item.key] === item.value || Number(geoData[item.key]) === Number(item.value)) {
|
|
286
|
-
return
|
|
310
|
+
return (
|
|
311
|
+
<HexIcon
|
|
312
|
+
textColor={textColor}
|
|
313
|
+
item={item}
|
|
314
|
+
index={itemIndex}
|
|
315
|
+
centroid={centroid}
|
|
316
|
+
iconSize={iconSize}
|
|
317
|
+
/>
|
|
318
|
+
)
|
|
287
319
|
}
|
|
288
320
|
break
|
|
289
321
|
case '≠':
|
|
290
322
|
if (geoData[item.key] !== item.value && Number(geoData[item.key]) !== Number(item.value)) {
|
|
291
|
-
return
|
|
323
|
+
return (
|
|
324
|
+
<HexIcon
|
|
325
|
+
textColor={textColor}
|
|
326
|
+
item={item}
|
|
327
|
+
index={itemIndex}
|
|
328
|
+
centroid={centroid}
|
|
329
|
+
iconSize={iconSize}
|
|
330
|
+
/>
|
|
331
|
+
)
|
|
292
332
|
}
|
|
293
333
|
break
|
|
294
334
|
case '<':
|
|
295
335
|
if (Number(geoData[item.key]) < Number(item.value)) {
|
|
296
|
-
return
|
|
336
|
+
return (
|
|
337
|
+
<HexIcon
|
|
338
|
+
textColor={textColor}
|
|
339
|
+
item={item}
|
|
340
|
+
index={itemIndex}
|
|
341
|
+
centroid={centroid}
|
|
342
|
+
iconSize={iconSize}
|
|
343
|
+
/>
|
|
344
|
+
)
|
|
297
345
|
}
|
|
298
346
|
break
|
|
299
347
|
case '>':
|
|
300
348
|
if (Number(geoData[item.key]) > Number(item.value)) {
|
|
301
|
-
return
|
|
349
|
+
return (
|
|
350
|
+
<HexIcon
|
|
351
|
+
textColor={textColor}
|
|
352
|
+
item={item}
|
|
353
|
+
index={itemIndex}
|
|
354
|
+
centroid={centroid}
|
|
355
|
+
iconSize={iconSize}
|
|
356
|
+
/>
|
|
357
|
+
)
|
|
302
358
|
}
|
|
303
359
|
break
|
|
304
360
|
case '<=':
|
|
305
361
|
if (Number(geoData[item.key]) <= Number(item.value)) {
|
|
306
|
-
return
|
|
362
|
+
return (
|
|
363
|
+
<HexIcon
|
|
364
|
+
textColor={textColor}
|
|
365
|
+
item={item}
|
|
366
|
+
index={itemIndex}
|
|
367
|
+
centroid={centroid}
|
|
368
|
+
iconSize={iconSize}
|
|
369
|
+
/>
|
|
370
|
+
)
|
|
307
371
|
}
|
|
308
372
|
break
|
|
309
373
|
case '>=':
|
|
310
374
|
if (item.operator === '>=') {
|
|
311
375
|
if (Number(geoData[item.key]) >= Number(item.value)) {
|
|
312
|
-
return
|
|
376
|
+
return (
|
|
377
|
+
<HexIcon
|
|
378
|
+
textColor={textColor}
|
|
379
|
+
item={item}
|
|
380
|
+
index={itemIndex}
|
|
381
|
+
centroid={centroid}
|
|
382
|
+
iconSize={iconSize}
|
|
383
|
+
/>
|
|
384
|
+
)
|
|
313
385
|
}
|
|
314
386
|
}
|
|
315
387
|
break
|
|
@@ -324,7 +396,15 @@ const UsaMap = () => {
|
|
|
324
396
|
|
|
325
397
|
return (
|
|
326
398
|
<g data-name={geoName} key={key} tabIndex={-1}>
|
|
327
|
-
<g
|
|
399
|
+
<g
|
|
400
|
+
className='geo-group'
|
|
401
|
+
style={styles}
|
|
402
|
+
onClick={() => geoClickHandler(geoDisplayName, geoData)}
|
|
403
|
+
id={geoName}
|
|
404
|
+
data-tooltip-id={`tooltip__${tooltipId}`}
|
|
405
|
+
data-tooltip-html={tooltip}
|
|
406
|
+
tabIndex={-1}
|
|
407
|
+
>
|
|
328
408
|
{/* state path */}
|
|
329
409
|
<path tabIndex={-1} className='single-geo' strokeWidth={1.3} d={path} />
|
|
330
410
|
|
|
@@ -335,15 +415,45 @@ const UsaMap = () => {
|
|
|
335
415
|
const hasMatchingValues = patternData.dataValue === geoData[patternData.dataKey]
|
|
336
416
|
const patternColor = patternData.color || getContrastColor('#000', currentFill)
|
|
337
417
|
|
|
418
|
+
if (!hasMatchingValues) return
|
|
419
|
+
checkColorContrast(currentFill, patternColor)
|
|
420
|
+
|
|
338
421
|
return (
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
422
|
+
<>
|
|
423
|
+
{pattern === 'waves' && (
|
|
424
|
+
<PatternWaves
|
|
425
|
+
id={`${mapId}--${dataKey}--${geoIndex}`}
|
|
426
|
+
height={patternSizes[size] ?? 10}
|
|
427
|
+
width={patternSizes[size] ?? 10}
|
|
428
|
+
fill={patternColor}
|
|
429
|
+
/>
|
|
430
|
+
)}
|
|
431
|
+
{pattern === 'circles' && (
|
|
432
|
+
<PatternCircles
|
|
433
|
+
id={`${mapId}--${dataKey}--${geoIndex}`}
|
|
434
|
+
height={patternSizes[size] ?? 10}
|
|
435
|
+
width={patternSizes[size] ?? 10}
|
|
436
|
+
fill={patternColor}
|
|
437
|
+
/>
|
|
438
|
+
)}
|
|
439
|
+
{pattern === 'lines' && (
|
|
440
|
+
<PatternLines
|
|
441
|
+
id={`${mapId}--${dataKey}--${geoIndex}`}
|
|
442
|
+
height={patternSizes[size] ?? 6}
|
|
443
|
+
width={patternSizes[size] ?? 6}
|
|
444
|
+
stroke={patternColor}
|
|
445
|
+
strokeWidth={1}
|
|
446
|
+
orientation={['diagonalRightToLeft']}
|
|
447
|
+
/>
|
|
448
|
+
)}
|
|
449
|
+
<path
|
|
450
|
+
className={`pattern-geoKey--${dataKey}`}
|
|
451
|
+
tabIndex={-1}
|
|
452
|
+
stroke='transparent'
|
|
453
|
+
d={path}
|
|
454
|
+
fill={`url(#${mapId}--${dataKey}--${geoIndex})`}
|
|
455
|
+
/>
|
|
456
|
+
</>
|
|
347
457
|
)
|
|
348
458
|
})}
|
|
349
459
|
{(isHex || showLabel) && geoLabel(geo, legendColors[0], projection)}
|
|
@@ -386,7 +496,18 @@ const UsaMap = () => {
|
|
|
386
496
|
|
|
387
497
|
// Bubbles
|
|
388
498
|
if (state.general.type === 'bubble') {
|
|
389
|
-
geosJsx.push(
|
|
499
|
+
geosJsx.push(
|
|
500
|
+
<BubbleList
|
|
501
|
+
key='bubbles'
|
|
502
|
+
data={state.data}
|
|
503
|
+
runtimeData={data}
|
|
504
|
+
state={state}
|
|
505
|
+
projection={projection}
|
|
506
|
+
applyLegendToRow={applyLegendToRow}
|
|
507
|
+
applyTooltipsToGeo={applyTooltipsToGeo}
|
|
508
|
+
displayGeoName={displayGeoName}
|
|
509
|
+
/>
|
|
510
|
+
)
|
|
390
511
|
}
|
|
391
512
|
|
|
392
513
|
// })
|
|
@@ -435,8 +556,22 @@ const UsaMap = () => {
|
|
|
435
556
|
|
|
436
557
|
return (
|
|
437
558
|
<g tabIndex={-1}>
|
|
438
|
-
<line
|
|
439
|
-
|
|
559
|
+
<line
|
|
560
|
+
x1={centroid[0]}
|
|
561
|
+
y1={centroid[1]}
|
|
562
|
+
x2={centroid[0] + dx}
|
|
563
|
+
y2={centroid[1] + dy}
|
|
564
|
+
stroke='rgba(0,0,0,.5)'
|
|
565
|
+
strokeWidth={1}
|
|
566
|
+
/>
|
|
567
|
+
<text
|
|
568
|
+
x={4}
|
|
569
|
+
strokeWidth='0'
|
|
570
|
+
fontSize={13}
|
|
571
|
+
style={{ fill: '#202020' }}
|
|
572
|
+
alignmentBaseline='middle'
|
|
573
|
+
transform={`translate(${centroid[0] + dx}, ${centroid[1] + dy})`}
|
|
574
|
+
>
|
|
440
575
|
{abbr.substring(3)}
|
|
441
576
|
</text>
|
|
442
577
|
</g>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"Topology","arcs":[[[128628,58684],[30,57],[-17,63],[30,47],[44,-21],[35,35],[74,5],[26,-46],[170,8],[9,-28],[-140,-76],[-178,-47],[-83,3]],[[1608,280],[113,17],[5,-80],[-39,13],[-35,-44],[-44,94]],[[1408,384],[91,12],[-17,-33],[-57,-19],[-17,40]],[[122,156],[48,-77]],[[170,79],[-65,-79],[-48,74],[-57,25],[57,55],[65,2]],[[128706,59939],[0,-30]],[[128706,59909],[26,-69],[-69,-17],[-66,36],[-30,-32],[-52,78],[-105,-23],[30,85],[44,-36],[52,41],[83,-33],[87,0]],[[384328,54018],[17,44],[9,159],[39,-19],[87,105],[22,-34],[-61,-126],[17,-99],[-56,11],[0,-115],[-53,28],[-21,46]],[[128759,59886],[56,61],[48,-6],[66,-44],[-57,-7],[4,-77],[-39,32],[-57,-8],[-21,49]],[[128706,59939],[57,10],[-9,-37],[-48,-3]],[[383033,50942],[22,23],[148,25],[92,216],[26,102],[30,-17],[31,-68],[61,-17],[-44,-139],[-139,-171],[-39,-69],[-9,-175],[-52,-88],[-79,47],[-30,159],[35,79],[-53,93]],[[384201,53806],[18,70],[56,85],[22,-85],[-17,-37],[30,-63],[-9,-96],[-39,-46],[-61,172]],[[384746,55614],[44,68],[17,-25],[-35,-71],[-26,28]],[[384485,58543],[21,31],[5,-77],[-26,46]],[[384354,59393],[61,84],[0,93],[48,0],[0,-104],[-40,-21],[-43,-86],[-26,34]],[[384267,60694],[26,61],[48,-44],[-5,-80],[-35,-36],[-30,39],[-4,60]],[[384262,56279],[87,4],[13,-57],[-82,-13],[-18,66]],[[122,156],[87,108],[18,-31],[117,3],[18,-69],[-31,20],[-126,-47],[-35,-61]],[[383639,52173],[48,80],[92,55],[65,-14],[-9,-55],[-52,-20],[-35,-62],[-48,-9],[-22,36],[-39,-11]],[[113802,97593],[91,110],[48,-53],[113,-133],[-156,-185],[-5,88],[5,58],[-96,115]],[[126645,59224],[-48,-26],[-61,38],[-52,-30],[-31,21],[-74,37],[-96,-88],[-17,10],[-9,-2],[-65,-12],[-18,-38],[-65,47],[-4,27],[-92,12],[-61,-52],[-70,32],[-52,-19],[4,83],[44,49],[-48,19],[13,102],[22,24],[4,118],[31,49],[-40,132],[-47,55],[-57,119],[39,24],[96,74],[-9,115],[31,50],[48,16],[100,-7],[78,-39],[66,-9],[78,6],[87,-6],[70,-22],[105,40],[43,-17],[66,-6],[78,-24],[35,32],[109,0],[43,-21],[53,10],[87,-25],[48,-24],[39,35],[109,-41],[57,14],[104,-13],[92,-50],[43,-2],[53,-69],[100,-39],[52,44],[-9,-72],[14,-109],[56,-71],[-30,-67],[-48,29],[13,-44],[-39,4],[-35,-51],[-35,21],[-26,-32],[-44,-80],[-26,-110],[-39,-30],[-26,-75],[-74,-57],[-79,-24],[-52,19],[-31,-42],[-30,20],[-105,-67],[-43,14],[-31,-40],[-30,-3],[-27,57],[-91,58],[-57,-69],[-78,82],[-48,12],[-61,-26]],[[128105,59906],[48,-33],[6,2],[46,18],[39,-56],[-74,-57],[-11,7],[-41,23],[-13,96]],[[124954,59472],[87,19],[22,-68],[-65,-60],[-53,51],[9,58]],[[127817,59480],[214,85],[152,-51],[-78,-40],[-92,-11],[-48,-38],[-34,19],[-75,-27],[-39,63]],[[127795,59986],[31,-22],[0,-69],[-31,91]]],"transform":{"scale":[0.000823601132486181,0.000546229125192084],"translate":[-170.84530299432993,-14.373864584355845]},"objects":{"counties":{"type":"GeometryCollection","geometries":[{"arcs":[[[1]],[[2]]],"type":"MultiPolygon","properties":{"name":"Manu'a"},"id":"60020"},{"arcs":[[3,4]],"type":"Polygon","properties":{"name":"Western"},"id":"60050"},{"arcs":[[5,6]],"type":"Polygon","properties":{"name":"St. Thomas"},"id":"78030"},{"arcs":[[7]],"type":"Polygon","properties":{"name":"Saipan"},"id":"69110"},{"arcs":[[[8]],[[9,-6]]],"type":"MultiPolygon","properties":{"name":"St. John"},"id":"78020"},{"arcs":[[10]],"type":"Polygon","properties":{"name":"Guam"},"id":"66010"},{"arcs":[[11]],"type":"Polygon","properties":{"name":"Tinian"},"id":"69120"},{"arcs":[[[12]],[[13]],[[14]],[[15]],[[16]]],"type":"MultiPolygon","properties":{"name":"Northern Islands"},"id":"69085"},{"arcs":[[-4,17]],"type":"Polygon","properties":{"name":"Eastern"},"id":"60010"},{"arcs":[[18]],"type":"Polygon","properties":{"name":"Rota"},"id":"69100"},{"arcs":[[0]],"type":"Polygon","properties":{"name":"St. Croix"},"id":"78010"},{"arcs":[[0]],"type":"Polygon","properties":{"name":"District of Columbia"},"id":"11"}]},"states":{"type":"GeometryCollection","geometries":[{"arcs":[[[20]],[[21]],[[22]],[[23]],[[24]]],"type":"MultiPolygon","properties":{"name":"Puerto Rico"},"id":"72"},{"arcs":[[[1]],[[2]],[[4,17]]],"type":"MultiPolygon","properties":{"name":"American Samoa"},"id":"60"},{"arcs":[[[6,9]],[[0]],[[8]]],"type":"MultiPolygon","properties":{"name":"United States Virgin Islands"},"id":"78"},{"arcs":[[[7]],[[11]],[[12]],[[13]],[[14]],[[15]],[[16]],[[18]]],"type":"MultiPolygon","properties":{"name":"Commonwealth of the Northern Mariana Islands"},"id":"69"},{"arcs":[[10]],"type":"Polygon","properties":{"name":"Guam"},"id":"66"}]}}}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { feature } from 'topojson-client'
|
|
2
|
+
import usExtendedGeography from './../data/us-extended-geography.json'
|
|
3
|
+
|
|
4
|
+
export const getCountyTopoURL = year => {
|
|
5
|
+
return `https://www.cdc.gov/TemplatePackage/contrib/data/county-topography/cb_${year}_us_county_20m.json`
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const getTopoData = year => {
|
|
9
|
+
return new Promise((resolve, reject) => {
|
|
10
|
+
const resolveWithTopo = async response => {
|
|
11
|
+
if (response.status !== 200) {
|
|
12
|
+
response = await import('./../data/cb_2019_us_county_20m.json')
|
|
13
|
+
} else {
|
|
14
|
+
response = await response.json()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const counties = [response, usExtendedGeography].flatMap(topo => feature(topo, topo.objects.counties).features)
|
|
18
|
+
const states = [response, usExtendedGeography].flatMap(topo => feature(topo, topo.objects.states).features)
|
|
19
|
+
|
|
20
|
+
const topoData = {
|
|
21
|
+
year: year || 'default',
|
|
22
|
+
fulljson: response,
|
|
23
|
+
counties,
|
|
24
|
+
states
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
resolve(topoData)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const numericYear = parseInt(year)
|
|
31
|
+
|
|
32
|
+
if (isNaN(numericYear)) {
|
|
33
|
+
fetch(getCountyTopoURL(2019)).then(resolveWithTopo)
|
|
34
|
+
} else if (numericYear > 2022) {
|
|
35
|
+
fetch(getCountyTopoURL(2022)).then(resolveWithTopo)
|
|
36
|
+
} else if (numericYear < 2013) {
|
|
37
|
+
fetch(getCountyTopoURL(2013)).then(resolveWithTopo)
|
|
38
|
+
} else {
|
|
39
|
+
switch (numericYear) {
|
|
40
|
+
case 2022:
|
|
41
|
+
fetch(getCountyTopoURL(2022)).then(resolveWithTopo)
|
|
42
|
+
break
|
|
43
|
+
case 2021:
|
|
44
|
+
fetch(getCountyTopoURL(2021)).then(resolveWithTopo)
|
|
45
|
+
break
|
|
46
|
+
case 2020:
|
|
47
|
+
fetch(getCountyTopoURL(2020)).then(resolveWithTopo)
|
|
48
|
+
break
|
|
49
|
+
case 2018:
|
|
50
|
+
case 2017:
|
|
51
|
+
case 2016:
|
|
52
|
+
case 2015:
|
|
53
|
+
fetch(getCountyTopoURL(2015)).then(resolveWithTopo)
|
|
54
|
+
break
|
|
55
|
+
case 2014:
|
|
56
|
+
fetch(getCountyTopoURL(2014)).then(resolveWithTopo)
|
|
57
|
+
break
|
|
58
|
+
case 2013:
|
|
59
|
+
fetch(getCountyTopoURL(2013)).then(resolveWithTopo)
|
|
60
|
+
break
|
|
61
|
+
default:
|
|
62
|
+
fetch(getCountyTopoURL(2019)).then(resolveWithTopo)
|
|
63
|
+
break
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const getCurrentTopoYear = (state, runtimeFilters) => {
|
|
70
|
+
let currentYear = state.general.countyCensusYear
|
|
71
|
+
|
|
72
|
+
if (state.general.filterControlsCountyYear && runtimeFilters && runtimeFilters.length > 0) {
|
|
73
|
+
let yearFilter = runtimeFilters.filter(filter => filter.columnName === state.general.filterControlsCountyYear)
|
|
74
|
+
if (yearFilter.length > 0 && yearFilter[0].active) {
|
|
75
|
+
currentYear = yearFilter[0].active
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return currentYear || 'default'
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const isTopoReady = (topoData, state, runtimeFilters) => {
|
|
83
|
+
let currentYear = getCurrentTopoYear(state, runtimeFilters)
|
|
84
|
+
|
|
85
|
+
return topoData.year && (!currentYear || currentYear === topoData.year)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const hasMoreThanFromHash = (data: { [key: string]: any }): boolean => {
|
|
89
|
+
// Get all keys of the data object
|
|
90
|
+
const keys = Object.keys(data)
|
|
91
|
+
|
|
92
|
+
// Filter out the 'fromHash' key
|
|
93
|
+
const otherKeys = keys.filter(key => key !== 'fromHash')
|
|
94
|
+
|
|
95
|
+
// Check if there are any other keys left
|
|
96
|
+
return otherKeys.length > 0
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export const getFilterControllingStatePicked = (state, runtimeData) => {
|
|
100
|
+
if (!state.general.filterControlsStatePicked || !runtimeData) {
|
|
101
|
+
const statePicked = state?.general?.statePicked?.stateName
|
|
102
|
+
return statePicked
|
|
103
|
+
} else {
|
|
104
|
+
if (hasMoreThanFromHash(runtimeData)) {
|
|
105
|
+
let statePickedFromFilter = Object.values(runtimeData)?.map(s => s[state.general.filterControlsStatePicked])?.[0]
|
|
106
|
+
const statePicked = statePickedFromFilter || state.general.statePicked.stateName || 'Alabama'
|
|
107
|
+
return statePicked
|
|
108
|
+
}
|
|
109
|
+
return null
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -10,6 +10,7 @@ import Geo from '../Geo'
|
|
|
10
10
|
import CityList from '../CityList'
|
|
11
11
|
import BubbleList from '../BubbleList'
|
|
12
12
|
import ConfigContext from '../../context'
|
|
13
|
+
import ZoomControls from '../ZoomControls'
|
|
13
14
|
|
|
14
15
|
const { features: world } = feature(topoJSON, topoJSON.objects.countries)
|
|
15
16
|
|
|
@@ -34,7 +35,9 @@ const WorldMap = () => {
|
|
|
34
35
|
state,
|
|
35
36
|
supportedCountries,
|
|
36
37
|
titleCase,
|
|
37
|
-
tooltipId
|
|
38
|
+
tooltipId,
|
|
39
|
+
setScale,
|
|
40
|
+
setTranslate
|
|
38
41
|
} = useContext(ConfigContext)
|
|
39
42
|
|
|
40
43
|
// TODO Refactor - state should be set together here to avoid rerenders
|
|
@@ -59,35 +62,6 @@ const WorldMap = () => {
|
|
|
59
62
|
setPosition(pos => ({ ...pos, zoom: pos.zoom / 1.5 }))
|
|
60
63
|
}
|
|
61
64
|
|
|
62
|
-
const ZoomControls = ({ position, setPosition, state, setState, setRuntimeData, generateRuntimeData }) => {
|
|
63
|
-
if (!state.general.allowMapZoom) return
|
|
64
|
-
return (
|
|
65
|
-
<div className='zoom-controls' data-html2canvas-ignore>
|
|
66
|
-
<button onClick={() => handleZoomIn(position, setPosition)} aria-label='Zoom In'>
|
|
67
|
-
<svg viewBox='0 0 24 24' stroke='currentColor' strokeWidth='3'>
|
|
68
|
-
<line x1='12' y1='5' x2='12' y2='19' />
|
|
69
|
-
<line x1='5' y1='12' x2='19' y2='12' />
|
|
70
|
-
</svg>
|
|
71
|
-
</button>
|
|
72
|
-
<button onClick={() => handleZoomOut(position, setPosition)} aria-label='Zoom Out'>
|
|
73
|
-
<svg viewBox='0 0 24 24' stroke='currentColor' strokeWidth='3'>
|
|
74
|
-
<line x1='5' y1='12' x2='19' y2='12' />
|
|
75
|
-
</svg>
|
|
76
|
-
</button>
|
|
77
|
-
{state.general.type === 'bubble' && (
|
|
78
|
-
<button onClick={() => handleReset(state, setState, setRuntimeData, generateRuntimeData)} className='reset' aria-label='Reset Zoom and Map Filters'>
|
|
79
|
-
Reset Filters
|
|
80
|
-
</button>
|
|
81
|
-
)}
|
|
82
|
-
{state.general.type === 'world-geocode' && (
|
|
83
|
-
<button onClick={() => handleReset(state, setState, setRuntimeData, generateRuntimeData)} className='reset' aria-label='Reset Zoom'>
|
|
84
|
-
Reset Zoom
|
|
85
|
-
</button>
|
|
86
|
-
)}
|
|
87
|
-
</div>
|
|
88
|
-
)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
65
|
// TODO Refactor - state should be set together here to avoid rerenders
|
|
92
66
|
const handleCircleClick = (country, state, setState, setRuntimeData, generateRuntimeData) => {
|
|
93
67
|
if (!state.general.allowMapZoom) return
|
|
@@ -173,7 +147,7 @@ const WorldMap = () => {
|
|
|
173
147
|
}
|
|
174
148
|
|
|
175
149
|
// Default return state, just geo with no additional information
|
|
176
|
-
return <Geo
|
|
150
|
+
return <Geo additionaldata={JSON.stringify(additionalData)} geodata={JSON.stringify(geoData)} state={state} key={i + '-geo'} stroke={geoStrokeColor} strokeWidth={strokeWidth} style={styles} path={path} />
|
|
177
151
|
})
|
|
178
152
|
|
|
179
153
|
// Cities
|
|
@@ -217,7 +191,18 @@ const WorldMap = () => {
|
|
|
217
191
|
</svg>
|
|
218
192
|
)}
|
|
219
193
|
{(state.general.type === 'data' || (state.general.type === 'world-geocode' && hasZoom) || (state.general.type === 'bubble' && hasZoom)) && (
|
|
220
|
-
<ZoomControls
|
|
194
|
+
<ZoomControls
|
|
195
|
+
// prettier-ignore
|
|
196
|
+
generateRuntimeData={generateRuntimeData}
|
|
197
|
+
handleZoomIn={handleZoomIn}
|
|
198
|
+
handleZoomOut={handleZoomOut}
|
|
199
|
+
position={position}
|
|
200
|
+
setPosition={setPosition}
|
|
201
|
+
setRuntimeData={setRuntimeData}
|
|
202
|
+
setState={setState}
|
|
203
|
+
state={state}
|
|
204
|
+
handleReset={handleReset}
|
|
205
|
+
/>
|
|
221
206
|
)}
|
|
222
207
|
</ErrorBoundary>
|
|
223
208
|
)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React, { useContext } from 'react'
|
|
2
|
+
import { MapConfig } from '../types/MapConfig'
|
|
3
|
+
import ConfigContext from '../context'
|
|
4
|
+
|
|
5
|
+
type ZoomControlsProps = {
|
|
6
|
+
handleZoomIn: (coordinates: [Number, Number], setPosition: Function) => void
|
|
7
|
+
handleZoomOut: (coordinates: [Number, Number], setPosition: Function) => void
|
|
8
|
+
handleReset: (coordinates: [Number, Number], setPosition: Function) => void
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const ZoomControls: React.FC<ZoomControlsProps> = ({ handleZoomIn, handleZoomOut, handleReset }) => {
|
|
12
|
+
const { state, setState, setRuntimeData, setPosition, position, generateRuntimeData } = useContext<MapContext>(ConfigContext)
|
|
13
|
+
if (!state.general.allowMapZoom) return
|
|
14
|
+
return (
|
|
15
|
+
<div className='zoom-controls' data-html2canvas-ignore>
|
|
16
|
+
<button onClick={() => handleZoomIn(position, setPosition)} aria-label='Zoom In'>
|
|
17
|
+
<svg viewBox='0 0 24 24' stroke='currentColor' strokeWidth='3'>
|
|
18
|
+
<line x1='12' y1='5' x2='12' y2='19' />
|
|
19
|
+
<line x1='5' y1='12' x2='19' y2='12' />
|
|
20
|
+
</svg>
|
|
21
|
+
</button>
|
|
22
|
+
<button onClick={() => handleZoomOut(position, setPosition)} aria-label='Zoom Out'>
|
|
23
|
+
<svg viewBox='0 0 24 24' stroke='currentColor' strokeWidth='3'>
|
|
24
|
+
<line x1='5' y1='12' x2='19' y2='12' />
|
|
25
|
+
</svg>
|
|
26
|
+
</button>
|
|
27
|
+
{state.general.type === 'bubble' && (
|
|
28
|
+
<button onClick={() => handleReset(state, setState, setRuntimeData, generateRuntimeData)} className='reset' aria-label='Reset Zoom and Map Filters'>
|
|
29
|
+
Reset Filters
|
|
30
|
+
</button>
|
|
31
|
+
)}
|
|
32
|
+
{(state.general.type === 'world-geocode' || state.general.geoType === 'single-state') && (
|
|
33
|
+
<button onClick={() => handleReset(state, setState, setRuntimeData, generateRuntimeData)} className='reset' aria-label='Reset Zoom'>
|
|
34
|
+
Reset Zoom
|
|
35
|
+
</button>
|
|
36
|
+
)}
|
|
37
|
+
</div>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default ZoomControls
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
annotations: [],
|
|
3
3
|
general: {
|
|
4
|
+
noStateFoundMessage: 'Map Unavailable',
|
|
4
5
|
annotationDropdownText: 'Annotations',
|
|
5
6
|
geoBorderColor: 'darkGray',
|
|
6
7
|
headerColor: 'theme-blue',
|
|
@@ -67,7 +68,12 @@ export default {
|
|
|
67
68
|
type: 'equalnumber',
|
|
68
69
|
numberOfItems: 3,
|
|
69
70
|
position: 'side',
|
|
70
|
-
title: '
|
|
71
|
+
title: '',
|
|
72
|
+
style: 'circles',
|
|
73
|
+
subStyle: 'linear blocks',
|
|
74
|
+
tickRotation: '',
|
|
75
|
+
singleColumnLegend: false,
|
|
76
|
+
hideBorder: false
|
|
71
77
|
},
|
|
72
78
|
filters: [],
|
|
73
79
|
table: {
|
|
@@ -190,6 +190,7 @@ export const supportedStatesFipsCodes = {
|
|
|
190
190
|
'08': 'Colorado',
|
|
191
191
|
'09': 'Connecticut',
|
|
192
192
|
10: 'Delaware',
|
|
193
|
+
11: 'District of Columbia',
|
|
193
194
|
12: 'Florida',
|
|
194
195
|
13: 'Georgia',
|
|
195
196
|
15: 'Hawaii',
|
|
@@ -234,9 +235,9 @@ export const supportedStatesFipsCodes = {
|
|
|
234
235
|
56: 'Wyoming',
|
|
235
236
|
60: 'American Samoa',
|
|
236
237
|
66: 'Guam',
|
|
237
|
-
69: 'Northern Mariana Islands',
|
|
238
|
+
69: 'Commonwealth of the Northern Mariana Islands',
|
|
238
239
|
72: 'Puerto Rico',
|
|
239
|
-
78: 'Virgin Islands'
|
|
240
|
+
78: 'United States Virgin Islands'
|
|
240
241
|
}
|
|
241
242
|
|
|
242
243
|
export const supportedCountries = {
|
|
@@ -289,7 +290,12 @@ export const supportedCountries = {
|
|
|
289
290
|
COL: ['Colombia'],
|
|
290
291
|
COM: ['Comoros'],
|
|
291
292
|
COG: ['Congo', 'Congo, Republic of the', 'Republic of the Congo'],
|
|
292
|
-
COD: [
|
|
293
|
+
COD: [
|
|
294
|
+
'Democratic Republic of the Congo',
|
|
295
|
+
'Congo, Democratic Republic of the',
|
|
296
|
+
'Congo, the Democratic Republic of the',
|
|
297
|
+
'Dem. Rep. Congo'
|
|
298
|
+
],
|
|
293
299
|
COK: ['Cook Islands', 'Cook Is.', 'Cook Islands (New Zealand)'],
|
|
294
300
|
CRI: ['Costa Rica'],
|
|
295
301
|
CIV: ["Côte d'Ivoire"],
|
|
@@ -382,7 +388,12 @@ export const supportedCountries = {
|
|
|
382
388
|
MUS: ['Mauritius'],
|
|
383
389
|
MYT: ['Mayotte', 'Mayotte (France)'],
|
|
384
390
|
MEX: ['Mexico'],
|
|
385
|
-
FSM: [
|
|
391
|
+
FSM: [
|
|
392
|
+
'Micronesia',
|
|
393
|
+
'Federated States of Micronesia',
|
|
394
|
+
'Micronesia (Federated States of)',
|
|
395
|
+
'Micronesia, Federated States of'
|
|
396
|
+
],
|
|
386
397
|
MDA: ['Moldova', 'Moldova, Republic of'],
|
|
387
398
|
MCO: ['Monaco'],
|
|
388
399
|
MNG: ['Mongolia'],
|
|
@@ -15,9 +15,9 @@ export const generateRuntimeLegendHash = (state, runtimeFilters) => {
|
|
|
15
15
|
specialClasses: state.legend.specialClasses,
|
|
16
16
|
geoType: state.general.geoType,
|
|
17
17
|
data: state.data,
|
|
18
|
-
...runtimeFilters,
|
|
19
18
|
filters: {
|
|
20
19
|
...state.filters
|
|
21
|
-
}
|
|
20
|
+
},
|
|
21
|
+
...runtimeFilters
|
|
22
22
|
})
|
|
23
23
|
}
|