@cdc/map 4.23.11 → 4.24.2

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 (60) hide show
  1. package/dist/cdcmap.js +52413 -46767
  2. package/examples/default-patterns.json +581 -0
  3. package/examples/default-usa.json +159 -57
  4. package/examples/test.json +0 -9614
  5. package/examples/zika.json +1194 -0
  6. package/index.html +17 -6
  7. package/package.json +3 -3
  8. package/src/CdcMap.tsx +39 -31
  9. package/src/components/CityList.jsx +34 -23
  10. package/src/components/{EditorPanel.jsx → EditorPanel/components/EditorPanel.tsx} +31 -64
  11. package/src/components/{HexShapeSettings.jsx → EditorPanel/components/HexShapeSettings.tsx} +2 -51
  12. package/src/components/EditorPanel/components/Inputs.tsx +59 -0
  13. package/src/components/EditorPanel/components/Panel.PatternSettings.tsx +140 -0
  14. package/src/components/EditorPanel/components/Panels.tsx +7 -0
  15. package/src/components/EditorPanel/index.tsx +3 -0
  16. package/src/components/Geo.jsx +4 -2
  17. package/src/components/Legend/components/Legend.tsx +183 -0
  18. package/src/components/Legend/components/LegendItem.Hex.tsx +49 -0
  19. package/src/components/Legend/components/index.scss +235 -0
  20. package/src/components/Legend/index.tsx +3 -0
  21. package/src/components/UsaMap/components/Territory/Territory.Hexagon.tsx +129 -0
  22. package/src/components/UsaMap/components/Territory/Territory.Rectangle.tsx +66 -0
  23. package/src/components/UsaMap/components/Territory/index.tsx +9 -0
  24. package/src/components/{CountyMap.jsx → UsaMap/components/UsaMap.County.tsx} +9 -9
  25. package/src/components/{UsaRegionMap.jsx → UsaMap/components/UsaMap.Region.tsx} +1 -3
  26. package/src/components/{SingleStateMap.jsx → UsaMap/components/UsaMap.SingleState.tsx} +8 -10
  27. package/src/components/{UsaMap.jsx → UsaMap/components/UsaMap.State.tsx} +55 -127
  28. package/src/components/UsaMap/index.tsx +13 -0
  29. package/src/components/{WorldMap.jsx → WorldMap/components/WorldMap.jsx} +20 -14
  30. package/src/components/WorldMap/data/world-topo-guiana-update.json +1 -0
  31. package/src/components/WorldMap/data/world-topo-old.json +1 -0
  32. package/src/components/WorldMap/data/world-topo-recent.json +39194 -0
  33. package/src/components/WorldMap/data/world-topo.json +1 -0
  34. package/src/components/WorldMap/index.tsx +3 -0
  35. package/src/context.ts +2 -1
  36. package/src/data/initial-state.js +5 -3
  37. package/src/data/supported-geos.js +21 -1
  38. package/src/hooks/useTooltip.ts +4 -4
  39. package/src/scss/editor-panel.scss +2 -3
  40. package/src/scss/main.scss +11 -1
  41. package/src/scss/map.scss +22 -12
  42. package/src/types/MapConfig.ts +149 -0
  43. package/src/types/MapContext.ts +45 -0
  44. package/src/types/runtimeLegend.ts +1 -0
  45. package/examples/world-geocode-data.json +0 -18
  46. package/examples/world-geocode.json +0 -108
  47. package/src/components/Sidebar.tsx +0 -142
  48. package/src/data/abbreviations.js +0 -57
  49. package/src/data/feature-test.json +0 -73
  50. package/src/data/newtest.json +0 -1
  51. package/src/data/state-abbreviations.js +0 -60
  52. package/src/data/supported-cities.csv +0 -165
  53. package/src/data/test.json +0 -1
  54. package/src/data/world-topo.json +0 -1
  55. package/src/scss/sidebar.scss +0 -230
  56. /package/src/{data → components/UsaMap/data}/cb_2019_us_county_20m.json +0 -0
  57. /package/src/{data → components/UsaMap/data}/us-hex-topo.json +0 -0
  58. /package/src/{data → components/UsaMap/data}/us-regions-topo-2.json +0 -0
  59. /package/src/{data → components/UsaMap/data}/us-regions-topo.json +0 -0
  60. /package/src/{data → components/UsaMap/data}/us-topo.json +0 -0
@@ -0,0 +1,3 @@
1
+ import Legend from './components/Legend'
2
+
3
+ export default Legend
@@ -0,0 +1,129 @@
1
+ import { useContext } from 'react'
2
+ import { geoCentroid, geoPath } from 'd3-geo'
3
+ import ConfigContext from './../../../../context'
4
+ import { MapContext } from './../../../../types/MapContext'
5
+ import { AiOutlineArrowUp, AiOutlineArrowDown, AiOutlineArrowRight } from 'react-icons/ai'
6
+ import { Group } from '@visx/group'
7
+ import { Text } from '@visx/text'
8
+ import chroma from 'chroma-js'
9
+
10
+ const offsets = {
11
+ 'US-VT': [50, -8],
12
+ 'US-NH': [34, 2],
13
+ 'US-MA': [30, -1],
14
+ 'US-RI': [28, 2],
15
+ 'US-CT': [35, 10],
16
+ 'US-NJ': [42, 1],
17
+ 'US-DE': [33, 0],
18
+ 'US-MD': [47, 10]
19
+ }
20
+
21
+ const nudges = {
22
+ 'US-FL': [15, 3],
23
+ 'US-AK': [0, -8],
24
+ 'US-CA': [-10, 0],
25
+ 'US-NY': [5, 0],
26
+ 'US-MI': [13, 20],
27
+ 'US-LA': [-10, -3],
28
+ 'US-HI': [-10, 10],
29
+ 'US-ID': [0, 10],
30
+ 'US-WV': [-2, 2]
31
+ }
32
+
33
+ // todo: combine hexagonLabel & geoLabel functions
34
+ // todo: move geoLabel functions outside of components for reusability
35
+ const TerritoryHexagon = ({ label, text, stroke, strokeWidth, textColor, territory, territoryData, ...props }) => {
36
+ const { state } = useContext<MapContext>(ConfigContext)
37
+
38
+ const isHex = state.general.displayAsHex
39
+
40
+ // Labels
41
+ const hexagonLabel = (geo, bgColor = '#FFFFFF', projection) => {
42
+ let centroid = projection ? projection(geoCentroid(geo)) : [22, 17.5]
43
+
44
+ let abbr = geo?.properties?.iso ? geo.properties.iso : geo.uid
45
+
46
+ const getArrowDirection = (geoData, geo, isTerritory = false) => {
47
+ if (!isTerritory) {
48
+ centroid = projection(geoCentroid(geo))
49
+ }
50
+
51
+ return (
52
+ <>
53
+ {state.hexMap.shapeGroups.map((group, groupIndex) => {
54
+ return group.items.map((item, itemIndex) => {
55
+ if (item.operator === '=') {
56
+ if (geoData[item.key] === item.value || Number(geoData[item.key]) === Number(item.value)) {
57
+ return (
58
+ <Group style={{ transform: `translate(36%, 50%)`, fill: 'currentColor' }} key={`territory-hex--${itemIndex}`}>
59
+ {item.shape === 'Arrow Down' && <AiOutlineArrowDown size={12} stroke='none' fontWeight={100} />}
60
+ {item.shape === 'Arrow Up' && <AiOutlineArrowUp size={12} stroke='none' fontWeight={100} />}
61
+ {item.shape === 'Arrow Right' && <AiOutlineArrowRight size={12} stroke='none' fontWeight={100} />}
62
+ </Group>
63
+ )
64
+ }
65
+ }
66
+ })
67
+ })}
68
+ </>
69
+ )
70
+ }
71
+
72
+ if (undefined === abbr) return null
73
+
74
+ let textColor = '#FFF'
75
+
76
+ // Dynamic text color
77
+ if (chroma.contrast(textColor, bgColor) < 3.5) {
78
+ textColor = '#202020' // dark gray
79
+ }
80
+
81
+ // always make HI black since it is off to the side
82
+ if (abbr === 'US-HI') {
83
+ textColor = '#000'
84
+ }
85
+
86
+ let x = 0,
87
+ y = state.hexMap.type === 'shapes' ? -10 : 5
88
+
89
+ // used to nudge/move some of the labels for better readability
90
+ if (nudges[abbr] && false === isHex) {
91
+ x += nudges[abbr][0]
92
+ y += nudges[abbr][1]
93
+ }
94
+
95
+ if (undefined === offsets[abbr] || isHex) {
96
+ let y = state.hexMap.type === 'shapes' ? '30%' : '50%'
97
+ return (
98
+ <>
99
+ <Text fontSize={14} x={'50%'} y={y} style={{ fill: 'currentColor', stroke: 'initial', fontWeight: 400, opacity: 1, fillOpacity: 1 }} textAnchor='middle' verticalAnchor='middle'>
100
+ {abbr.substring(3)}
101
+ </Text>
102
+ {state.general.displayAsHex && state.hexMap.type === 'shapes' && getArrowDirection(territoryData, geo, true)}
103
+ </>
104
+ )
105
+ }
106
+
107
+ let [dx, dy] = offsets[abbr]
108
+
109
+ return (
110
+ <g>
111
+ <line x1={centroid[0]} y1={centroid[1]} x2={centroid[0] + dx} y2={centroid[1] + dy} stroke='rgba(0,0,0,.5)' strokeWidth={1} />
112
+ <text x={4} strokeWidth='0' fontSize={13} style={{ fill: '#202020' }} alignmentBaseline='middle' transform={`translate(${centroid[0] + dx}, ${centroid[1] + dy})`}>
113
+ {abbr.substring(3)}
114
+ </text>
115
+ </g>
116
+ )
117
+ }
118
+
119
+ return (
120
+ <svg viewBox='0 0 45 51' className='territory-wrapper--hex'>
121
+ <g {...props}>
122
+ <polygon stroke={stroke} strokeWidth={strokeWidth} points='22 0 44 12.702 44 38.105 22 50.807 0 38.105 0 12.702' />
123
+ {state.general.displayAsHex && hexagonLabel(territoryData ? territoryData : geo, stroke, false)}
124
+ </g>
125
+ </svg>
126
+ )
127
+ }
128
+
129
+ export default TerritoryHexagon
@@ -0,0 +1,66 @@
1
+ import { useContext } from 'react'
2
+ import { PatternLines, PatternCircles, PatternWaves } from '@visx/pattern'
3
+ import chroma from 'chroma-js'
4
+ import ConfigContext from './../../../../context'
5
+ import { type MapContext } from '../../../../types/MapContext'
6
+ // todo: move this somewhere that makes better sense for pattern sizes.
7
+ const sizes = {
8
+ small: '8',
9
+ medium: '10',
10
+ large: '12'
11
+ }
12
+
13
+ const TerritoryRectangle = ({ label, text, stroke, strokeWidth, textColor, hasPattern, territory, ...props }) => {
14
+ const { state, supportedTerritories } = useContext<MapContext>(ConfigContext)
15
+
16
+ return (
17
+ <svg viewBox='0 0 45 28'>
18
+ <g {...props} strokeLinejoin='round'>
19
+ <path
20
+ stroke={stroke}
21
+ strokeWidth={strokeWidth}
22
+ d='M40,0.5 C41.2426407,0.5 42.3676407,1.00367966 43.1819805,1.81801948 C43.9963203,2.63235931 44.5,3.75735931 44.5,5 L44.5,5 L44.5,23 C44.5,24.2426407 43.9963203,25.3676407 43.1819805,26.1819805 C42.3676407,26.9963203 41.2426407,27.5 40,27.5 L40,27.5 L5,27.5 C3.75735931,27.5 2.63235931,26.9963203 1.81801948,26.1819805 C1.00367966,25.3676407 0.5,24.2426407 0.5,23 L0.5,23 L0.5,5 C0.5,3.75735931 1.00367966,2.63235931 1.81801948,1.81801948 C2.63235931,1.00367966 3.75735931,0.5 5,0.5 L5,0.5 Z'
23
+ {...props}
24
+ />
25
+ <text textAnchor='middle' dominantBaseline='middle' x='50%' y='54%' fill={text} style={{ stroke: textColor, strokeWidth: 1 }} className='territory-text' paint-order='stroke'>
26
+ {label}
27
+ </text>
28
+
29
+ {state.map.patterns.map((patternData, patternIndex) => {
30
+ let defaultPatternColor = 'black'
31
+
32
+ const hasMatchingValues = supportedTerritories[territory].includes(patternData?.dataValue)
33
+ console.log('has match', patternData)
34
+
35
+ if (chroma.contrast(defaultPatternColor, props.style.fill) < 3.5) {
36
+ defaultPatternColor = 'white'
37
+ }
38
+
39
+ if (!hasMatchingValues) return null
40
+ if (!patternData.pattern) return null
41
+
42
+ return (
43
+ <>
44
+ {patternData?.pattern === 'waves' && <PatternWaves id={`territory-${patternData?.dataKey}--${patternIndex}`} height={sizes[patternData?.size] ?? 10} width={sizes[patternData?.size] ?? 10} fill={defaultPatternColor} complement />}
45
+ {patternData?.pattern === 'circles' && <PatternCircles id={`territory-${patternData?.dataKey}--${patternIndex}`} height={sizes[patternData?.size] ?? 10} width={sizes[patternData?.size] ?? 10} fill={defaultPatternColor} complement />}
46
+ {patternData?.pattern === 'lines' && <PatternLines id={`territory-${patternData?.dataKey}--${patternIndex}`} height={sizes[patternData?.size] ?? 6} width={sizes[patternData?.size] ?? 6} stroke={defaultPatternColor} strokeWidth={1} orientation={['diagonalRightToLeft']} />}
47
+ <path
48
+ stroke={stroke}
49
+ strokeWidth={strokeWidth}
50
+ d='M40,0.5 C41.2426407,0.5 42.3676407,1.00367966 43.1819805,1.81801948 C43.9963203,2.63235931 44.5,3.75735931 44.5,5 L44.5,5 L44.5,23 C44.5,24.2426407 43.9963203,25.3676407 43.1819805,26.1819805 C42.3676407,26.9963203 41.2426407,27.5 40,27.5 L40,27.5 L5,27.5 C3.75735931,27.5 2.63235931,26.9963203 1.81801948,26.1819805 C1.00367966,25.3676407 0.5,24.2426407 0.5,23 L0.5,23 L0.5,5 C0.5,3.75735931 1.00367966,2.63235931 1.81801948,1.81801948 C2.63235931,1.00367966 3.75735931,0.5 5,0.5 L5,0.5 Z'
51
+ fill={`url(#territory-${patternData?.dataKey}--${patternIndex})`}
52
+ color='white'
53
+ className={[`territory-pattern-${patternData.dataKey}`, `territory-pattern-${patternData.dataKey}--${patternData.dataValue}`].join(' ')}
54
+ />
55
+ <text textAnchor='middle' dominantBaseline='middle' x='50%' y='54%' fill={text} style={{ stroke: patternData ? 'black' : textColor, strokeWidth: patternData ? 6 : 0 }} className='territory-text' paint-order='stroke'>
56
+ {label}
57
+ </text>
58
+ </>
59
+ )
60
+ })}
61
+ </g>
62
+ </svg>
63
+ )
64
+ }
65
+
66
+ export default TerritoryRectangle
@@ -0,0 +1,9 @@
1
+ import TerritoryHexagon from './Territory.Hexagon'
2
+ import TerritoryRectangle from './Territory.Rectangle'
3
+
4
+ const Territory = {
5
+ Rectangle: TerritoryRectangle,
6
+ Hexagon: TerritoryHexagon
7
+ }
8
+
9
+ export default Territory
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useState, memo, useRef, useContext } from 'react'
1
+ import { useEffect, useState, useRef, useContext } from 'react'
2
2
 
3
3
  import { geoCentroid, geoPath, geoContains } from 'd3-geo'
4
4
  import { feature } from 'topojson-client'
@@ -8,11 +8,11 @@ import debounce from 'lodash.debounce'
8
8
  import Loading from '@cdc/core/components/Loading'
9
9
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
10
10
 
11
- import useMapLayers from '../hooks/useMapLayers'
12
- import ConfigContext from '../context'
11
+ import useMapLayers from '../../../hooks/useMapLayers'
12
+ import ConfigContext from '../../../context'
13
13
 
14
- const getCountyTopoURL = (year) => {
15
- return `https://www.cdc.gov/TemplatePackage/contrib/data/county-topography/cb_${year}_us_county_20m.json`;
14
+ const getCountyTopoURL = year => {
15
+ return `https://www.cdc.gov/TemplatePackage/contrib/data/county-topography/cb_${year}_us_county_20m.json`
16
16
  }
17
17
 
18
18
  const sortById = (a, b) => {
@@ -23,11 +23,11 @@ const sortById = (a, b) => {
23
23
 
24
24
  const getTopoData = year => {
25
25
  return new Promise((resolve, reject) => {
26
- const resolveWithTopo = async (response) => {
27
- if(response.status !== 200){
28
- response = await import('../data/cb_2019_us_county_20m.json');
26
+ const resolveWithTopo = async response => {
27
+ if (response.status !== 200) {
28
+ response = await import('./../data/cb_2019_us_county_20m.json')
29
29
  } else {
30
- response = await response.json();
30
+ response = await response.json()
31
31
  }
32
32
 
33
33
  let topoData = {}
@@ -7,7 +7,7 @@ import { feature } from 'topojson-client'
7
7
  import topoJSON from '../data/us-regions-topo-2.json'
8
8
  import { Mercator } from '@visx/geo'
9
9
  import chroma from 'chroma-js'
10
- import ConfigContext from '../context'
10
+ import ConfigContext from '../../../context'
11
11
 
12
12
  const { features: unitedStates } = feature(topoJSON, topoJSON.objects.regions)
13
13
 
@@ -36,11 +36,9 @@ const UsaRegionMap = props => {
36
36
  data,
37
37
  displayGeoName,
38
38
  geoClickHandler,
39
- handleCircleClick,
40
39
  handleMapAriaLabels,
41
40
  state,
42
41
  supportedTerritories,
43
- titleCase,
44
42
  } = useContext(ConfigContext)
45
43
 
46
44
  // "Choose State" options
@@ -6,28 +6,27 @@ import { geoPath } from 'd3-geo'
6
6
  import { feature, mesh } from 'topojson-client'
7
7
  import { CustomProjection } from '@visx/geo'
8
8
  import Loading from '@cdc/core/components/Loading'
9
- import colorPalettes from '../../../core/data/colorPalettes'
9
+ import colorPalettes from '@cdc/core/data/colorPalettes'
10
10
  import { geoAlbersUsaTerritories } from 'd3-composite-projections'
11
- import CityList from './CityList'
12
- import ConfigContext from '../context'
11
+ import CityList from '../../CityList'
12
+ import ConfigContext from '../../../context'
13
13
 
14
14
  // SVG ITEMS
15
15
  const WIDTH = 880
16
16
  const HEIGHT = 500
17
17
  const PADDING = 25
18
18
 
19
-
20
- const getCountyTopoURL = (year) => {
21
- return `https://www.cdc.gov/TemplatePackage/contrib/data/county-topography/cb_${year}_us_county_20m.json`;
19
+ const getCountyTopoURL = year => {
20
+ return `https://www.cdc.gov/TemplatePackage/contrib/data/county-topography/cb_${year}_us_county_20m.json`
22
21
  }
23
22
 
24
23
  const getTopoData = year => {
25
24
  return new Promise((resolve, reject) => {
26
25
  const resolveWithTopo = async response => {
27
- if(response.status !== 200){
28
- response = await import('../data/cb_2019_us_county_20m.json');
26
+ if (response.status !== 200) {
27
+ response = await import('./../data/cb_2019_us_county_20m.json')
29
28
  } else {
30
- response = await response.json();
29
+ response = await response.json()
31
30
  }
32
31
  let topoData = {}
33
32
 
@@ -265,7 +264,6 @@ const SingleStateMap = props => {
265
264
  titleCase={titleCase}
266
265
  setSharedFilterValue={setSharedFilterValue}
267
266
  isFilterValueSupported={isFilterValueSupported}
268
- isGeoCodeMap={state.general.type === 'us-geocode'}
269
267
  />
270
268
  )
271
269
 
@@ -1,138 +1,33 @@
1
1
  import React, { useState, useEffect, memo, useContext } from 'react'
2
2
 
3
3
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
4
- import { geoCentroid, geoPath } from 'd3-geo'
5
- import { feature } from 'topojson-client'
4
+
5
+ // United States Topojson resources
6
6
  import topoJSON from '../data/us-topo.json'
7
7
  import hexTopoJSON from '../data/us-hex-topo.json'
8
+
9
+ import { geoCentroid, geoPath } from 'd3-geo'
10
+ import { feature } from 'topojson-client'
8
11
  import { AlbersUsa, Mercator } from '@visx/geo'
9
12
  import chroma from 'chroma-js'
10
- import CityList from './CityList'
11
- import BubbleList from './BubbleList'
12
- import { supportedCities, supportedStates } from '../data/supported-geos'
13
+ import CityList from '../../CityList'
14
+ import BubbleList from '../../BubbleList'
15
+ import { supportedCities, supportedStates } from '../../../data/supported-geos'
13
16
  import { geoAlbersUsa } from 'd3-composite-projections'
14
17
  import { Group } from '@visx/group'
15
18
  import { Text } from '@visx/text'
16
-
19
+ import { PatternLines, PatternCircles, PatternWaves } from '@visx/pattern'
17
20
  import { AiOutlineArrowUp, AiOutlineArrowDown, AiOutlineArrowRight } from 'react-icons/ai'
18
21
 
19
- import useMapLayers from '../hooks/useMapLayers'
20
- import ConfigContext from '../context'
22
+ import Territory from './Territory'
23
+
24
+ import useMapLayers from '../../../hooks/useMapLayers'
25
+ import ConfigContext from '../../../context'
26
+ import { MapContext } from '../../../types/MapContext'
21
27
 
22
28
  const { features: unitedStates } = feature(topoJSON, topoJSON.objects.states)
23
29
  const { features: unitedStatesHex } = feature(hexTopoJSON, hexTopoJSON.objects.states)
24
30
 
25
- // todo: combine hexagonLabel & geoLabel functions
26
- // todo: move geoLabel functions outside of components for reusability
27
- const Hexagon = ({ label, text, stroke, strokeWidth, textColor, territory, territoryData, ...props }) => {
28
- const { state } = useContext(ConfigContext)
29
-
30
- // Labels
31
- const hexagonLabel = (geo, bgColor = '#FFFFFF', projection) => {
32
- let centroid = projection ? projection(geoCentroid(geo)) : [22, 17.5]
33
-
34
- let abbr = geo?.properties?.iso ? geo.properties.iso : geo.uid
35
-
36
- const getArrowDirection = (geoData, geo, isTerritory = false) => {
37
- if (!isTerritory) {
38
- centroid = projection(geoCentroid(geo))
39
- }
40
-
41
- return (
42
- <>
43
- {state.hexMap.shapeGroups.map((group, groupIndex) => {
44
- return group.items.map((item, itemIndex) => {
45
- if (item.operator === '=') {
46
- if (geoData[item.key] === item.value) {
47
- return (
48
- <Group style={{ transform: `translate(36%, 50%)`, fill: 'currentColor' }}>
49
- {item.shape === 'Arrow Down' && <AiOutlineArrowDown size={12} stroke='none' fontWeight={100} />}
50
- {item.shape === 'Arrow Up' && <AiOutlineArrowUp size={12} stroke='none' fontWeight={100} />}
51
- {item.shape === 'Arrow Right' && <AiOutlineArrowRight size={12} stroke='none' fontWeight={100} />}
52
- </Group>
53
- )
54
- }
55
- }
56
- })
57
- })}
58
- </>
59
- )
60
- }
61
-
62
- if (undefined === abbr) return null
63
-
64
- let textColor = '#FFF'
65
-
66
- // Dynamic text color
67
- if (chroma.contrast(textColor, bgColor) < 3.5) {
68
- textColor = '#202020' // dark gray
69
- }
70
-
71
- // always make HI black since it is off to the side
72
- if (abbr === 'US-HI') {
73
- textColor = '#000'
74
- }
75
-
76
- let x = 0,
77
- y = state.hexMap.type === 'shapes' ? -10 : 5
78
-
79
- // used to nudge/move some of the labels for better readability
80
- if (nudges[abbr] && false === isHex) {
81
- x += nudges[abbr][0]
82
- y += nudges[abbr][1]
83
- }
84
-
85
- if (undefined === offsets[abbr] || isHex) {
86
- let y = state.hexMap.type === 'shapes' ? '30%' : '50%'
87
- return (
88
- <>
89
- <Text fontSize={14} x={'50%'} y={y} style={{ fill: 'currentColor', stroke: 'initial', fontWeight: 400, opacity: 1, fillOpacity: 1 }} textAnchor='middle' verticalAnchor='middle'>
90
- {abbr.substring(3)}
91
- </Text>
92
- {state.general.displayAsHex && state.hexMap.type === 'shapes' && getArrowDirection(territoryData, geo, true)}
93
- </>
94
- )
95
- }
96
-
97
- let [dx, dy] = offsets[abbr]
98
-
99
- return (
100
- <g>
101
- <line x1={centroid[0]} y1={centroid[1]} x2={centroid[0] + dx} y2={centroid[1] + dy} stroke='rgba(0,0,0,.5)' strokeWidth={1} />
102
- <text x={4} strokeWidth='0' fontSize={13} style={{ fill: '#202020' }} alignmentBaseline='middle' transform={`translate(${centroid[0] + dx}, ${centroid[1] + dy})`}>
103
- {abbr.substring(3)}
104
- </text>
105
- </g>
106
- )
107
- }
108
-
109
- return (
110
- <svg viewBox='0 0 45 51' className='territory-wrapper--hex'>
111
- <g {...props}>
112
- <polygon stroke={stroke} strokeWidth={strokeWidth} points='22 0 44 12.702 44 38.105 22 50.807 0 38.105 0 12.702' />
113
- {state.general.displayAsHex && hexagonLabel(territoryData ? territoryData : geo, stroke, false)}
114
- </g>
115
- </svg>
116
- )
117
- }
118
-
119
- const Rect = ({ label, text, stroke, strokeWidth, textColor, ...props }) => {
120
- return (
121
- <svg viewBox='0 0 45 28'>
122
- <g {...props} strokeLinejoin='round'>
123
- <path
124
- stroke={stroke}
125
- strokeWidth={strokeWidth}
126
- d='M40,0.5 C41.2426407,0.5 42.3676407,1.00367966 43.1819805,1.81801948 C43.9963203,2.63235931 44.5,3.75735931 44.5,5 L44.5,5 L44.5,23 C44.5,24.2426407 43.9963203,25.3676407 43.1819805,26.1819805 C42.3676407,26.9963203 41.2426407,27.5 40,27.5 L40,27.5 L5,27.5 C3.75735931,27.5 2.63235931,26.9963203 1.81801948,26.1819805 C1.00367966,25.3676407 0.5,24.2426407 0.5,23 L0.5,23 L0.5,5 C0.5,3.75735931 1.00367966,2.63235931 1.81801948,1.81801948 C2.63235931,1.00367966 3.75735931,0.5 5,0.5 L5,0.5 Z'
127
- />
128
- <text textAnchor='middle' dominantBaseline='middle' x='50%' y='54%' fill={text} stroke={textColor}>
129
- {label}
130
- </text>
131
- </g>
132
- </svg>
133
- )
134
- }
135
-
136
31
  const offsets = {
137
32
  'US-VT': [50, -8],
138
33
  'US-NH': [34, 2],
@@ -156,7 +51,7 @@ const nudges = {
156
51
  'US-WV': [-2, 2]
157
52
  }
158
53
 
159
- const UsaMap = props => {
54
+ const UsaMap = () => {
160
55
  // prettier-ignore
161
56
  const {
162
57
  applyLegendToRow,
@@ -170,7 +65,7 @@ const UsaMap = props => {
170
65
  state,
171
66
  supportedTerritories,
172
67
  titleCase,
173
- } = useContext(ConfigContext)
68
+ } = useContext<MapContext>(ConfigContext)
174
69
 
175
70
  let isFilterValueSupported = false
176
71
 
@@ -224,7 +119,7 @@ const UsaMap = props => {
224
119
  const geoStrokeColor = state.general.geoBorderColor === 'darkGray' ? 'rgba(0, 0, 0, 0.2)' : 'rgba(255,255,255,0.7)'
225
120
 
226
121
  const territories = territoriesData.map(territory => {
227
- const Shape = isHex ? Hexagon : Rect
122
+ const Shape = isHex ? Territory.Hexagon : Territory.Rectangle
228
123
 
229
124
  const territoryData = data[territory]
230
125
 
@@ -271,11 +166,18 @@ const UsaMap = props => {
271
166
  fill: legendColors[2]
272
167
  }
273
168
  }
274
- return <Shape key={label} label={label} css={styles} text={styles.color} strokeWidth={1.5} textColor={textColor} onClick={() => geoClickHandler(territory, territoryData)} data-tooltip-id='tooltip' data-tooltip-html={toolTip} territory={territory} territoryData={territoryData} />
169
+
170
+ return (
171
+ <>
172
+ <Shape key={label} label={label} style={styles} text={styles.color} strokeWidth={1.5} textColor={textColor} onClick={() => geoClickHandler(territory, territoryData)} data-tooltip-id='tooltip' data-tooltip-html={toolTip} territory={territory} territoryData={territoryData} />
173
+ </>
174
+ )
275
175
  }
276
176
  })
277
177
 
278
178
  let pathGenerator = geoPath().projection(geoAlbersUsa().translate(translate))
179
+
180
+ // Note: Layers are different than patterns
279
181
  const { pathArray } = useMapLayers(state, '', pathGenerator)
280
182
 
281
183
  // Constructs and displays markup for all geos on the map (except territories right now)
@@ -368,9 +270,9 @@ const UsaMap = props => {
368
270
  {state.hexMap.shapeGroups.map((group, groupIndex) => {
369
271
  return group.items.map((item, itemIndex) => {
370
272
  if (item.operator === '=') {
371
- if (geoData[item.key] === item.value) {
273
+ if (geoData[item.key] === item.value || Number(geoData[item.key]) == Number(item.value)) {
372
274
  return (
373
- <Group top={centroid[1] - 5} left={centroid[0] - iconSize} color={textColor} textAnchor='start'>
275
+ <Group top={centroid[1] - 5} left={centroid[0] - iconSize} color={textColor} textAnchor='start' key={`hex--${item.key}-${item.value}-${itemIndex}`}>
374
276
  {item.shape === 'Arrow Down' && <AiOutlineArrowDown />}
375
277
  {item.shape === 'Arrow Up' && <AiOutlineArrowUp />}
376
278
  {item.shape === 'Arrow Right' && <AiOutlineArrowRight />}
@@ -384,10 +286,37 @@ const UsaMap = props => {
384
286
  )
385
287
  }
386
288
 
289
+ const sizes = {
290
+ small: '8',
291
+ medium: '10',
292
+ large: '12'
293
+ }
294
+
387
295
  return (
388
296
  <g data-name={geoName} key={key}>
389
297
  <g className='geo-group' style={styles} onClick={() => geoClickHandler(geoDisplayName, geoData)} id={geoName} data-tooltip-id='tooltip' data-tooltip-html={tooltip}>
390
298
  <path tabIndex={-1} className='single-geo' strokeWidth={1.3} d={path} />
299
+ {state.map.patterns.map((patternData, patternIndex) => {
300
+ let { pattern, dataKey, size } = patternData
301
+ let defaultPatternColor = 'black'
302
+
303
+ const hasMatchingValues = patternData.dataValue === geoData[patternData.dataKey]
304
+
305
+ if (chroma.contrast(defaultPatternColor, legendColors[0]) < 3.5) {
306
+ defaultPatternColor = 'white'
307
+ }
308
+
309
+ return (
310
+ hasMatchingValues && (
311
+ <>
312
+ {pattern === 'waves' && <PatternWaves id={`${dataKey}--${patternIndex}`} height={sizes[size] ?? 10} width={sizes[size] ?? 10} fill={defaultPatternColor} />}
313
+ {pattern === 'circles' && <PatternCircles id={`${dataKey}--${patternIndex}`} height={sizes[size] ?? 10} width={sizes[size] ?? 10} fill={defaultPatternColor} />}
314
+ {pattern === 'lines' && <PatternLines id={`${dataKey}--${patternIndex}`} height={sizes[size] ?? 6} width={sizes[size] ?? 6} stroke={defaultPatternColor} strokeWidth={1} orientation={['diagonalRightToLeft']} />}
315
+ <path className={`pattern-geoKey--${dataKey}`} tabIndex={-1} stroke='transparent' d={path} fill={`url(#${dataKey}--${patternIndex})`} />
316
+ </>
317
+ )
318
+ )
319
+ })}
391
320
  {(isHex || showLabel) && geoLabel(geo, legendColors[0], projection)}
392
321
  {isHex && state.hexMap.type === 'shapes' && getArrowDirection(geoData, geo, legendColors[0])}
393
322
  </g>
@@ -417,7 +346,6 @@ const UsaMap = props => {
417
346
  displayGeoName={displayGeoName}
418
347
  geoClickHandler={geoClickHandler}
419
348
  isFilterValueSupported={isFilterValueSupported}
420
- isGeoCodeMap={state.general.type === 'us-geocode'}
421
349
  key='cities'
422
350
  projection={projection}
423
351
  setSharedFilterValue={setSharedFilterValue}
@@ -511,7 +439,7 @@ const UsaMap = props => {
511
439
  <span className='territories-label label'>{state.general.territoriesLabel}</span>
512
440
  </div>
513
441
  <div>
514
- <span className={window.visualViewport.width < 500 ? 'territories--mobile' : 'territories'}>{territories}</span>
442
+ <span className={window.visualViewport.width < 700 ? 'territories--mobile' : 'territories'}>{territories}</span>
515
443
  </div>
516
444
  </div>
517
445
  </>
@@ -0,0 +1,13 @@
1
+ import UsaStateMap from './components/UsaMap.State'
2
+ import UsaRegionMap from './components/UsaMap.Region.jsx'
3
+ import UsaCountyMap from './components/UsaMap.County'
4
+ import UsaSingleStateMap from './components/UsaMap.SingleState.jsx'
5
+
6
+ const UsaMap = {
7
+ State: UsaStateMap,
8
+ Region: UsaRegionMap,
9
+ County: UsaCountyMap,
10
+ SingleState: UsaSingleStateMap
11
+ }
12
+
13
+ export default UsaMap
@@ -5,12 +5,12 @@ import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
5
5
  import { geoMercator } from 'd3-geo'
6
6
  import { Mercator } from '@visx/geo'
7
7
  import { feature } from 'topojson-client'
8
- import topoJSON from '../data/world-topo.json'
9
- import ZoomableGroup from './ZoomableGroup'
10
- import Geo from './Geo'
11
- import CityList from './CityList'
12
- import BubbleList from './BubbleList'
13
- import ConfigContext from '../context'
8
+ import topoJSON from './../data/world-topo.json'
9
+ import ZoomableGroup from '../../ZoomableGroup'
10
+ import Geo from '../../Geo'
11
+ import CityList from '../../CityList'
12
+ import BubbleList from '../../BubbleList'
13
+ import ConfigContext from '../../../context'
14
14
 
15
15
  const { features: world } = feature(topoJSON, topoJSON.objects.countries)
16
16
 
@@ -101,14 +101,22 @@ const WorldMap = props => {
101
101
 
102
102
  const constructGeoJsx = geographies => {
103
103
  const geosJsx = geographies.map(({ feature: geo, path }, i) => {
104
- const geoKey = geo.properties.iso
104
+ // If the geo.properties.state value is found in the data use that, otherwise fall back to geo.properties.iso
105
+ const dataHasStateName = state.data.some(d => d[state.columns.geo.name] === geo.properties.state)
106
+ const geoKey = geo.properties.state && data[geo.properties.state] ? geo.properties.state : geo.properties.name ? geo.properties.name : geo.properties.iso
105
107
 
108
+ const additionalData = {
109
+ name: geo.properties.name
110
+ }
106
111
  if (!geoKey) return null
107
112
 
108
- const geoData = data[geoKey]
113
+ let geoData = data[geoKey]
109
114
 
110
- const geoDisplayName = displayGeoName(supportedCountries[geoKey][0])
115
+ // if ((geoKey === 'Alaska' || geoKey === 'Hawaii') && !geoData) {
116
+ // geoData = data['United States']
117
+ // }
111
118
 
119
+ const geoDisplayName = displayGeoName(supportedCountries[geoKey]?.[0])
112
120
  let legendColors
113
121
 
114
122
  // Once we receive data for this geographic item, setup variables.
@@ -146,17 +154,15 @@ const WorldMap = props => {
146
154
  styles.cursor = 'pointer'
147
155
  }
148
156
 
149
- return <Geo key={i + '-geo'} css={styles} path={path} stroke={geoStrokeColor} strokeWidth={strokeWidth} onClick={() => geoClickHandler(geoDisplayName, geoData)} data-tooltip-id='tooltip' data-tooltip-html={toolTip} />
157
+ return <Geo additionalData={additionalData} geoData={geoData} state={state} key={i + '-geo'} style={styles} path={path} stroke={geoStrokeColor} strokeWidth={strokeWidth} onClick={() => geoClickHandler(geoDisplayName, geoData)} data-tooltip-id='tooltip' data-tooltip-html={toolTip} />
150
158
  }
151
159
 
152
160
  // Default return state, just geo with no additional information
153
- return <Geo key={i + '-geo'} stroke={geoStrokeColor} strokeWidth={strokeWidth} css={styles} path={path} />
161
+ return <Geo additionalData={additionalData} geoData={geoData} state={state} key={i + '-geo'} stroke={geoStrokeColor} strokeWidth={strokeWidth} style={styles} path={path} />
154
162
  })
155
163
 
156
164
  // Cities
157
- geosJsx.push(
158
- <CityList applyLegendToRow={applyLegendToRow} applyTooltipsToGeo={applyTooltipsToGeo} data={data} displayGeoName={displayGeoName} geoClickHandler={geoClickHandler} isGeoCodeMap={state.general.type === 'world-geocode'} key='cities' projection={projection} state={state} titleCase={titleCase} />
159
- )
165
+ geosJsx.push(<CityList applyLegendToRow={applyLegendToRow} applyTooltipsToGeo={applyTooltipsToGeo} data={data} displayGeoName={displayGeoName} geoClickHandler={geoClickHandler} key='cities' projection={projection} state={state} titleCase={titleCase} />)
160
166
 
161
167
  // Bubbles
162
168
  if (state.general.type === 'bubble') {