@cdc/map 4.25.3 → 4.25.5-1
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 +38945 -41511
- package/examples/hex-colors.json +3 -3
- package/examples/private/test.json +470 -1457
- package/examples/private/{mmr.json → wastewatermap.json} +86 -115
- package/index.html +13 -41
- package/package.json +4 -10
- package/src/CdcMap.tsx +51 -1555
- package/src/CdcMapComponent.tsx +594 -0
- package/src/_stories/CdcMap.Legend.Gradient.stories.tsx +10 -0
- package/src/_stories/CdcMap.Legend.stories.tsx +67 -0
- package/src/_stories/CdcMap.stories.tsx +4 -1
- package/src/_stories/UsaMap.NoData.stories.tsx +4 -4
- package/{examples/private/default-patterns.json → src/_stories/_mock/legends/legend-tests.json} +36 -131
- package/src/cdcMapComponent.styles.css +9 -0
- package/src/components/Annotation/Annotation.Draggable.tsx +27 -26
- package/src/components/Annotation/AnnotationDropdown.tsx +5 -6
- package/src/components/BubbleList.tsx +135 -49
- package/src/components/CityList.tsx +89 -87
- package/src/components/DataTable.tsx +8 -8
- package/src/components/EditorPanel/components/EditorPanel.tsx +714 -820
- package/src/components/EditorPanel/components/Error.tsx +9 -2
- package/src/components/EditorPanel/components/HexShapeSettings.tsx +127 -141
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +55 -86
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +89 -75
- package/src/components/EditorPanel/components/editorPanel.styles.css +95 -0
- package/src/components/Geo.tsx +9 -1
- package/src/components/GoogleMap/components/GoogleMap.tsx +1 -1
- package/src/components/Legend/components/Legend.tsx +92 -87
- package/src/components/Legend/components/LegendGroup/Legend.Group.tsx +128 -0
- package/src/components/Legend/components/LegendGroup/legend.group.css +27 -0
- package/src/components/Legend/components/LegendItem.Hex.tsx +4 -1
- package/src/components/Legend/components/index.scss +18 -6
- package/src/components/Modal.tsx +17 -7
- package/src/components/NavigationMenu.tsx +11 -9
- package/src/components/UsaMap/components/SingleState/SingleState.CountyOutput.tsx +12 -8
- package/src/components/UsaMap/components/SingleState/SingleState.StateOutput.tsx +4 -4
- package/src/components/UsaMap/components/TerritoriesSection.tsx +33 -10
- package/src/components/UsaMap/components/Territory/Territory.Hexagon.tsx +12 -10
- package/src/components/UsaMap/components/Territory/Territory.Rectangle.tsx +12 -14
- package/src/components/UsaMap/components/Territory/TerritoryShape.ts +2 -1
- package/src/components/UsaMap/components/UsaMap.County.tsx +138 -96
- package/src/components/UsaMap/components/UsaMap.Region.styles.css +72 -0
- package/src/components/UsaMap/components/UsaMap.Region.tsx +56 -103
- package/src/components/UsaMap/components/UsaMap.SingleState.styles.css +10 -0
- package/src/components/UsaMap/components/UsaMap.SingleState.tsx +59 -66
- package/src/components/UsaMap/components/UsaMap.State.tsx +112 -91
- package/src/components/UsaMap/helpers/map.ts +1 -1
- package/src/components/UsaMap/helpers/shapes.ts +20 -7
- package/src/components/WorldMap/WorldMap.tsx +64 -118
- package/src/components/WorldMap/worldMap.styles.css +28 -0
- package/src/components/ZoomControls.tsx +15 -13
- package/src/components/zoomControls.styles.css +53 -0
- package/src/context.ts +17 -9
- package/src/data/initial-state.js +5 -2
- package/src/helpers/addUIDs.ts +151 -0
- package/src/helpers/applyColorToLegend.ts +39 -64
- package/src/helpers/applyLegendToRow.ts +51 -0
- package/src/helpers/colorDistributions.ts +12 -0
- package/src/helpers/constants.ts +44 -0
- package/src/helpers/displayGeoName.ts +9 -2
- package/src/helpers/generateColorsArray.ts +2 -1
- package/src/helpers/generateRuntimeData.ts +74 -0
- package/src/helpers/generateRuntimeFilters.ts +63 -0
- package/src/helpers/generateRuntimeLegend.ts +537 -0
- package/src/helpers/generateRuntimeLegendHash.ts +16 -15
- package/src/helpers/getColumnNames.ts +19 -0
- package/src/helpers/getMapContainerClasses.ts +23 -0
- package/src/helpers/handleMapTabbing.ts +31 -0
- package/src/helpers/hashObj.ts +1 -1
- package/src/helpers/index.ts +22 -0
- package/src/helpers/navigationHandler.ts +3 -3
- package/src/helpers/resetLegendToggles.ts +13 -0
- package/src/helpers/setBinNumbers.ts +5 -0
- package/src/helpers/sortSpecialClassesLast.ts +7 -0
- package/src/helpers/tests/getColumnNames.test.ts +52 -0
- package/src/helpers/titleCase.ts +1 -1
- package/src/helpers/toggleLegendActive.ts +25 -0
- package/src/hooks/useApplyTooltipsToGeo.tsx +51 -0
- package/src/hooks/useColumnsRequiredChecker.ts +51 -0
- package/src/hooks/useGeoClickHandler.ts +45 -0
- package/src/hooks/useLegendSeparators.ts +26 -0
- package/src/hooks/useMapLayers.tsx +34 -60
- package/src/hooks/useModal.ts +22 -0
- package/src/hooks/useResizeObserver.ts +4 -5
- package/src/hooks/useStateZoom.tsx +52 -75
- package/src/hooks/useTooltip.ts +2 -3
- package/src/index.jsx +3 -9
- package/src/scss/editor-panel.scss +3 -99
- package/src/scss/main.scss +1 -19
- package/src/scss/map.scss +15 -220
- package/src/store/map.actions.ts +46 -0
- package/src/store/map.reducer.ts +96 -0
- package/src/types/Annotations.ts +24 -0
- package/src/types/MapConfig.ts +23 -3
- package/src/types/MapContext.ts +36 -35
- package/src/types/Modal.ts +1 -0
- package/src/types/RuntimeData.ts +3 -0
- package/LICENSE +0 -201
- package/examples/private/DEV-9644.json +0 -184
- package/examples/private/DEV-9989.json +0 -229
- package/examples/private/ardi.json +0 -180
- package/examples/private/colors 2.json +0 -416
- package/examples/private/colors.json +0 -416
- package/examples/private/colors.json.zip +0 -0
- package/examples/private/customColors.json +0 -45348
- package/examples/test.json +0 -183
- package/src/helpers/closeModal.ts +0 -9
- package/src/scss/btn.scss +0 -69
- package/src/scss/filters.scss +0 -27
- package/src/scss/variables.scss +0 -1
- /package/src/hooks/{useActiveElement.js → useActiveElement.ts} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import React, { memo, useContext, useEffect, useState } from 'react'
|
|
2
2
|
|
|
3
3
|
// 3rd party
|
|
4
4
|
import { geoCentroid } from 'd3-geo'
|
|
@@ -15,10 +15,25 @@ import { supportedTerritories } from '../../../data/supported-geos'
|
|
|
15
15
|
|
|
16
16
|
// Helpers
|
|
17
17
|
import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
|
|
18
|
-
import { getGeoFillColor, getGeoStrokeColor } from '../../../helpers
|
|
19
|
-
import
|
|
18
|
+
import { displayGeoName, getGeoFillColor, getGeoStrokeColor, handleMapAriaLabels, SVG_VIEWBOX } from '../../../helpers'
|
|
19
|
+
import useGeoClickHandler from '../../../hooks/useGeoClickHandler'
|
|
20
|
+
import useApplyTooltipsToGeo from '../../../hooks/useApplyTooltipsToGeo'
|
|
21
|
+
import './UsaMap.Region.styles.css'
|
|
22
|
+
import { applyLegendToRow } from '../../../helpers/applyLegendToRow'
|
|
23
|
+
|
|
24
|
+
type TerritoryRectProps = {
|
|
25
|
+
posX?: number
|
|
26
|
+
tName: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type RectProps = {
|
|
30
|
+
label: string
|
|
31
|
+
text: string
|
|
32
|
+
stroke: string
|
|
33
|
+
strokeWidth: number
|
|
34
|
+
}
|
|
20
35
|
|
|
21
|
-
const Rect = ({ label, text, stroke, strokeWidth, ...props }) => {
|
|
36
|
+
const Rect: React.FC<RectProps> = ({ label, text, stroke, strokeWidth, ...props }) => {
|
|
22
37
|
return (
|
|
23
38
|
<svg viewBox='0 0 45 28'>
|
|
24
39
|
<g {...props} strokeLinejoin='round'>
|
|
@@ -35,22 +50,17 @@ const Rect = ({ label, text, stroke, strokeWidth, ...props }) => {
|
|
|
35
50
|
)
|
|
36
51
|
}
|
|
37
52
|
|
|
38
|
-
const UsaRegionMap =
|
|
39
|
-
|
|
40
|
-
const {
|
|
41
|
-
applyLegendToRow,
|
|
42
|
-
applyTooltipsToGeo,
|
|
43
|
-
data,
|
|
44
|
-
displayGeoName,
|
|
45
|
-
geoClickHandler,
|
|
46
|
-
state,
|
|
47
|
-
tooltipId
|
|
48
|
-
} = useContext(ConfigContext)
|
|
49
|
-
|
|
50
|
-
// "Choose State" options
|
|
51
|
-
const [extent, setExtent] = useState(null)
|
|
53
|
+
const UsaRegionMap = () => {
|
|
54
|
+
const { data, config, tooltipId, legendMemo, legendSpecialClassLastMemo, runtimeLegend } = useContext(ConfigContext)
|
|
52
55
|
const [focusedStates, setFocusedStates] = useState(null)
|
|
53
|
-
const
|
|
56
|
+
const { geoClickHandler } = useGeoClickHandler()
|
|
57
|
+
const { applyTooltipsToGeo } = useApplyTooltipsToGeo()
|
|
58
|
+
const { general } = config
|
|
59
|
+
const { displayStateLabels, territoriesLabel, displayAsHex, type } = general
|
|
60
|
+
const tooltipInteractionType = config.tooltips.appearanceType
|
|
61
|
+
const isHex = displayAsHex
|
|
62
|
+
const [territoriesData, setTerritoriesData] = useState([])
|
|
63
|
+
const CIRCLE_RADIUS = 15
|
|
54
64
|
|
|
55
65
|
useEffect(() => {
|
|
56
66
|
const fetchData = async () => {
|
|
@@ -61,16 +71,6 @@ const UsaRegionMap = props => {
|
|
|
61
71
|
fetchData()
|
|
62
72
|
}, [])
|
|
63
73
|
|
|
64
|
-
// When returning from another map we want to reset the state
|
|
65
|
-
useEffect(() => {
|
|
66
|
-
setTranslate([455, 250])
|
|
67
|
-
setExtent(null)
|
|
68
|
-
}, [state.general.geoType])
|
|
69
|
-
|
|
70
|
-
const isHex = state.general.displayAsHex
|
|
71
|
-
|
|
72
|
-
const [territoriesData, setTerritoriesData] = useState([])
|
|
73
|
-
|
|
74
74
|
const territoriesKeys = Object.keys(supportedTerritories) // data will have already mapped abbreviated territories to their full names
|
|
75
75
|
|
|
76
76
|
useEffect(() => {
|
|
@@ -84,19 +84,18 @@ const UsaRegionMap = props => {
|
|
|
84
84
|
return <></>
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
const geoStrokeColor = getGeoStrokeColor(
|
|
88
|
-
const geoFillColor = getGeoFillColor(
|
|
87
|
+
const geoStrokeColor = getGeoStrokeColor(config)
|
|
88
|
+
const geoFillColor = getGeoFillColor(config)
|
|
89
89
|
|
|
90
90
|
const territories = territoriesData.map(territory => {
|
|
91
91
|
const Shape = Rect
|
|
92
92
|
|
|
93
93
|
const territoryData = data[territory]
|
|
94
94
|
|
|
95
|
-
let toolTip
|
|
95
|
+
let toolTip: string
|
|
96
96
|
|
|
97
97
|
let styles: React.CSSProperties = {
|
|
98
|
-
fill: geoFillColor
|
|
99
|
-
color: '#202020'
|
|
98
|
+
fill: geoFillColor
|
|
100
99
|
}
|
|
101
100
|
|
|
102
101
|
const label = supportedTerritories[territory][1]
|
|
@@ -105,7 +104,7 @@ const UsaRegionMap = props => {
|
|
|
105
104
|
|
|
106
105
|
toolTip = applyTooltipsToGeo(displayGeoName(territory), territoryData)
|
|
107
106
|
|
|
108
|
-
const legendColors = applyLegendToRow(territoryData)
|
|
107
|
+
const legendColors = applyLegendToRow(territoryData, config, runtimeLegend, legendMemo, legendSpecialClassLastMemo)
|
|
109
108
|
|
|
110
109
|
if (legendColors) {
|
|
111
110
|
const textColor = getContrastColor('#FFF', legendColors[0])
|
|
@@ -114,8 +113,8 @@ const UsaRegionMap = props => {
|
|
|
114
113
|
|
|
115
114
|
// If we need to add a pointer cursor
|
|
116
115
|
if (
|
|
117
|
-
(
|
|
118
|
-
|
|
116
|
+
(config.columns.navigate && territoryData[config.columns.navigate.name]) ||
|
|
117
|
+
tooltipInteractionType === 'click'
|
|
119
118
|
) {
|
|
120
119
|
needsPointer = true
|
|
121
120
|
}
|
|
@@ -148,46 +147,9 @@ const UsaRegionMap = props => {
|
|
|
148
147
|
}
|
|
149
148
|
})
|
|
150
149
|
|
|
151
|
-
const geoLabel = (geo, bgColor = '#FFFFFF', projection) => {
|
|
152
|
-
let centroid = projection(geoCentroid(geo))
|
|
153
|
-
let abbr = geo.properties.iso
|
|
154
|
-
|
|
155
|
-
if (undefined === abbr) return null
|
|
156
|
-
|
|
157
|
-
const textColor = getContrastColor('#FFF', bgColor)
|
|
158
|
-
|
|
159
|
-
let x = 0,
|
|
160
|
-
y = 5
|
|
161
|
-
|
|
162
|
-
return (
|
|
163
|
-
<g>
|
|
164
|
-
<line
|
|
165
|
-
x1={centroid[0]}
|
|
166
|
-
y1={centroid[1]}
|
|
167
|
-
x2={centroid[0] + x}
|
|
168
|
-
y2={centroid[1] + y}
|
|
169
|
-
stroke='rgba(0,0,0,.5)'
|
|
170
|
-
strokeWidth={1}
|
|
171
|
-
/>
|
|
172
|
-
<text
|
|
173
|
-
x={4}
|
|
174
|
-
strokeWidth='0'
|
|
175
|
-
fontSize={13}
|
|
176
|
-
style={{ fill: '#202020' }}
|
|
177
|
-
alignmentBaseline='middle'
|
|
178
|
-
transform={`translate(${centroid[0] + x}, ${centroid[1] + y})`}
|
|
179
|
-
>
|
|
180
|
-
{abbr.substring(3)}
|
|
181
|
-
</text>
|
|
182
|
-
</g>
|
|
183
|
-
)
|
|
184
|
-
}
|
|
185
|
-
|
|
186
150
|
// Constructs and displays markup for all geos on the map (except territories right now)
|
|
187
151
|
const constructGeoJsx = (geographies, projection) => {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const geosJsx = geographies.map(({ feature: geo, path = '', index }) => {
|
|
152
|
+
return geographies.map(({ feature: geo, path = '', index }) => {
|
|
191
153
|
const key = isHex ? geo.properties.iso + '-hex-group' : geo.properties.iso + '-group'
|
|
192
154
|
|
|
193
155
|
let styles = {
|
|
@@ -198,8 +160,6 @@ const UsaRegionMap = props => {
|
|
|
198
160
|
// Map the name from the geo data with the appropriate key for the processed data
|
|
199
161
|
let geoKey = geo.properties.iso
|
|
200
162
|
|
|
201
|
-
// Manually add Washington D.C. in for Hex maps
|
|
202
|
-
|
|
203
163
|
if (!geoKey) return
|
|
204
164
|
|
|
205
165
|
const geoData = data[geoKey]
|
|
@@ -207,7 +167,7 @@ const UsaRegionMap = props => {
|
|
|
207
167
|
let legendColors
|
|
208
168
|
// Once we receive data for this geographic item, setup variables.
|
|
209
169
|
if (geoData !== undefined) {
|
|
210
|
-
legendColors = applyLegendToRow(geoData)
|
|
170
|
+
legendColors = applyLegendToRow(geoData, config, runtimeLegend, legendMemo, legendSpecialClassLastMemo)
|
|
211
171
|
}
|
|
212
172
|
|
|
213
173
|
const geoDisplayName = displayGeoName(geoKey)
|
|
@@ -217,28 +177,25 @@ const UsaRegionMap = props => {
|
|
|
217
177
|
const toolTip = applyTooltipsToGeo(geoDisplayName, geoData)
|
|
218
178
|
|
|
219
179
|
styles = {
|
|
220
|
-
fill:
|
|
180
|
+
fill: type !== 'bubble' ? legendColors[0] : geoFillColor,
|
|
221
181
|
cursor: 'default',
|
|
222
182
|
'&:hover': {
|
|
223
|
-
fill:
|
|
183
|
+
fill: type !== 'bubble' ? legendColors[1] : geoFillColor
|
|
224
184
|
},
|
|
225
185
|
'&:active': {
|
|
226
|
-
fill:
|
|
186
|
+
fill: type !== 'bubble' ? legendColors[2] : geoFillColor
|
|
227
187
|
}
|
|
228
188
|
}
|
|
229
189
|
|
|
230
190
|
// When to add pointer cursor
|
|
231
|
-
if (
|
|
232
|
-
(state.columns.navigate && geoData[state.columns.navigate.name]) ||
|
|
233
|
-
state.tooltips.appearanceType === 'click'
|
|
234
|
-
) {
|
|
191
|
+
if ((config.columns.navigate && geoData[config.columns.navigate.name]) || tooltipInteractionType === 'click') {
|
|
235
192
|
styles.cursor = 'pointer'
|
|
236
193
|
}
|
|
237
194
|
|
|
238
|
-
const
|
|
195
|
+
const TerritoryRect: React.FC<TerritoryRectProps> = props => {
|
|
239
196
|
const { posX = 0, tName } = props
|
|
240
197
|
const textColor = getContrastColor('#FFF', legendColors[0])
|
|
241
|
-
const geoStrokeColor = getGeoStrokeColor(
|
|
198
|
+
const geoStrokeColor = getGeoStrokeColor(config)
|
|
242
199
|
return (
|
|
243
200
|
<>
|
|
244
201
|
<rect x={posX} width='36' height='24' rx='2' stroke={geoStrokeColor} strokeWidth='1' />
|
|
@@ -249,8 +206,6 @@ const UsaRegionMap = props => {
|
|
|
249
206
|
)
|
|
250
207
|
}
|
|
251
208
|
|
|
252
|
-
const circleRadius = 15
|
|
253
|
-
|
|
254
209
|
return (
|
|
255
210
|
<g
|
|
256
211
|
key={key}
|
|
@@ -263,29 +218,29 @@ const UsaRegionMap = props => {
|
|
|
263
218
|
>
|
|
264
219
|
<path tabIndex={-1} className='single-geo' stroke={geoStrokeColor} strokeWidth={1} d={path} />
|
|
265
220
|
<g id={`region-${index + 1}-label`}>
|
|
266
|
-
<circle fill='#fff' stroke='#999' cx={
|
|
221
|
+
<circle fill='#fff' stroke='#999' cx={CIRCLE_RADIUS} cy={CIRCLE_RADIUS} r={CIRCLE_RADIUS} />
|
|
267
222
|
<text fill='#333' x='15px' y='20px' textAnchor='middle'>
|
|
268
223
|
{index + 1}
|
|
269
224
|
</text>
|
|
270
225
|
</g>
|
|
271
226
|
{geoKey === 'region 2' && (
|
|
272
227
|
<g id='region-2-territories'>
|
|
273
|
-
<
|
|
274
|
-
<
|
|
228
|
+
<TerritoryRect tName='PR' />
|
|
229
|
+
<TerritoryRect posX={45} tName='VI' />
|
|
275
230
|
</g>
|
|
276
231
|
)}
|
|
277
232
|
|
|
278
233
|
{geoKey === 'region 9' && (
|
|
279
234
|
<g id='region-9-territories'>
|
|
280
235
|
<g className='region-9-row1'>
|
|
281
|
-
<
|
|
282
|
-
<
|
|
283
|
-
<
|
|
236
|
+
<TerritoryRect tName='AS' />
|
|
237
|
+
<TerritoryRect posX={45} tName='GU' />
|
|
238
|
+
<TerritoryRect posX={90} tName='MP' />
|
|
284
239
|
</g>
|
|
285
240
|
<g className='region-9-row2'>
|
|
286
|
-
<
|
|
287
|
-
<
|
|
288
|
-
<
|
|
241
|
+
<TerritoryRect tName='FM' />
|
|
242
|
+
<TerritoryRect posX={45} tName='PW' />
|
|
243
|
+
<TerritoryRect posX={90} tName='MH' />
|
|
289
244
|
</g>
|
|
290
245
|
</g>
|
|
291
246
|
)}
|
|
@@ -297,24 +252,22 @@ const UsaRegionMap = props => {
|
|
|
297
252
|
return (
|
|
298
253
|
<g key={key} className='geo-group' style={styles}>
|
|
299
254
|
<path tabIndex={-1} className='single-geo' stroke={geoStrokeColor} strokeWidth={1} d={path} />
|
|
300
|
-
{(isHex || showLabel) && geoLabel(geo, styles.fill, projection)}
|
|
301
255
|
</g>
|
|
302
256
|
)
|
|
303
257
|
})
|
|
304
|
-
return geosJsx
|
|
305
258
|
}
|
|
306
259
|
|
|
307
260
|
return (
|
|
308
261
|
<ErrorBoundary component='UsaRegionMap'>
|
|
309
|
-
<svg viewBox=
|
|
262
|
+
<svg viewBox={SVG_VIEWBOX} role='img' aria-label={handleMapAriaLabels(config)}>
|
|
310
263
|
<Mercator data={focusedStates} scale={620} translate={[1500, 735]}>
|
|
311
264
|
{({ features, projection }) => constructGeoJsx(features, projection)}
|
|
312
265
|
</Mercator>
|
|
313
|
-
{
|
|
266
|
+
{config.annotations.length > 0 && <Annotation.Draggable />}
|
|
314
267
|
</svg>
|
|
315
268
|
{territories.length > 0 && (
|
|
316
269
|
<section className='territories'>
|
|
317
|
-
<span className='label'>{
|
|
270
|
+
<span className='label'>{territoriesLabel}</span>
|
|
318
271
|
{territories}
|
|
319
272
|
</section>
|
|
320
273
|
)}
|
|
@@ -1,75 +1,73 @@
|
|
|
1
|
-
import { useEffect, memo, useContext
|
|
1
|
+
import { useEffect, memo, useContext } from 'react'
|
|
2
2
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
3
3
|
import { geoPath } from 'd3-geo'
|
|
4
4
|
import { CustomProjection } from '@visx/geo'
|
|
5
5
|
import Loading from '@cdc/core/components/Loading'
|
|
6
6
|
import { geoAlbersUsaTerritories } from 'd3-composite-projections'
|
|
7
7
|
import CityList from '../../CityList'
|
|
8
|
-
import ConfigContext from '../../../context'
|
|
8
|
+
import ConfigContext, { MapDispatchContext } from '../../../context'
|
|
9
9
|
import Annotation from '../../Annotation'
|
|
10
10
|
import SingleState from './SingleState'
|
|
11
|
-
import { getTopoData, getCurrentTopoYear, isTopoReady } from './../helpers/map'
|
|
12
11
|
import ZoomableGroup from '../../ZoomableGroup'
|
|
13
12
|
import ZoomControls from '../../ZoomControls'
|
|
14
13
|
import { MapContext } from '../../../types/MapContext'
|
|
15
14
|
import useStateZoom from '../../../hooks/useStateZoom'
|
|
16
15
|
import { Text } from '@visx/text'
|
|
17
|
-
import { getGeoStrokeColor } from '../../../helpers/colors'
|
|
18
|
-
import { handleMapAriaLabels } from '../../../helpers/handleMapAriaLabels'
|
|
19
|
-
import { titleCase } from '../../../helpers/titleCase'
|
|
20
16
|
|
|
21
|
-
|
|
22
|
-
const WIDTH = 880
|
|
23
|
-
const HEIGHT = 500
|
|
24
|
-
const PADDING = 25
|
|
17
|
+
import './UsaMap.SingleState.styles.css'
|
|
25
18
|
|
|
26
|
-
|
|
19
|
+
// map-level helpers
|
|
20
|
+
import { titleCase, handleMapAriaLabels, getGeoStrokeColor, MAX_ZOOM_LEVEL } from '../../../helpers'
|
|
21
|
+
|
|
22
|
+
// state-level helpers
|
|
23
|
+
import { getTopoData, getCurrentTopoYear, isTopoReady } from '../helpers/map'
|
|
24
|
+
import useGeoClickHandler from '../../../hooks/useGeoClickHandler'
|
|
25
|
+
import { SVG_WIDTH, SVG_HEIGHT, SVG_PADDING, SVG_VIEWBOX } from '../../../helpers'
|
|
26
|
+
|
|
27
|
+
const SingleStateMap = () => {
|
|
27
28
|
const {
|
|
28
|
-
|
|
29
|
-
applyTooltipsToGeo,
|
|
30
|
-
data,
|
|
31
|
-
geoClickHandler,
|
|
32
|
-
applyLegendToRow,
|
|
33
|
-
displayGeoName,
|
|
29
|
+
config,
|
|
34
30
|
setSharedFilterValue,
|
|
35
31
|
isFilterValueSupported,
|
|
36
32
|
runtimeFilters,
|
|
37
33
|
tooltipId,
|
|
38
34
|
position,
|
|
39
|
-
setPosition,
|
|
40
35
|
stateToShow,
|
|
41
36
|
topoData,
|
|
42
|
-
setTopoData,
|
|
43
37
|
scale,
|
|
44
38
|
translate,
|
|
45
|
-
|
|
39
|
+
legendMemo,
|
|
40
|
+
legendSpecialClassLastMemo
|
|
46
41
|
} = useContext<MapContext>(ConfigContext)
|
|
47
42
|
|
|
43
|
+
const dispatch = useContext(MapDispatchContext)
|
|
48
44
|
const { handleMoveEnd, handleZoomIn, handleZoomOut, handleReset, projection, statePicked } = useStateZoom(topoData)
|
|
45
|
+
const { geoClickHandler } = useGeoClickHandler()
|
|
49
46
|
|
|
50
47
|
const cityListProjection = geoAlbersUsaTerritories()
|
|
51
|
-
.translate([
|
|
48
|
+
.translate([SVG_WIDTH / 2, SVG_HEIGHT / 2])
|
|
52
49
|
.scale(1)
|
|
53
|
-
const geoStrokeColor = getGeoStrokeColor(
|
|
50
|
+
const geoStrokeColor = getGeoStrokeColor(config)
|
|
54
51
|
const path = geoPath().projection(projection)
|
|
55
52
|
|
|
56
53
|
useEffect(() => {
|
|
57
|
-
|
|
54
|
+
const stateToShow = topoData?.states?.find(s => s.properties.name === config.general.statePicked.stateName)
|
|
55
|
+
dispatch({ type: 'SET_STATE_TO_SHOW', payload: stateToShow })
|
|
58
56
|
}, [statePicked])
|
|
59
57
|
|
|
60
58
|
useEffect(() => {
|
|
61
|
-
let currentYear = getCurrentTopoYear(
|
|
59
|
+
let currentYear = getCurrentTopoYear(config, runtimeFilters)
|
|
62
60
|
|
|
63
|
-
if (currentYear !== topoData
|
|
61
|
+
if (currentYear !== topoData?.year) {
|
|
64
62
|
getTopoData(currentYear).then(response => {
|
|
65
|
-
|
|
63
|
+
dispatch({ type: 'SET_TOPO_DATA', payload: response })
|
|
66
64
|
})
|
|
67
65
|
}
|
|
68
|
-
}, [
|
|
66
|
+
}, [config.general.countyCensusYear, config.general.filterControlsCountyYear, JSON.stringify(runtimeFilters)])
|
|
69
67
|
|
|
70
|
-
if (!isTopoReady(topoData,
|
|
68
|
+
if (!isTopoReady(topoData, config, runtimeFilters)) {
|
|
71
69
|
return (
|
|
72
|
-
<div style={{ height: `${
|
|
70
|
+
<div style={{ height: `${SVG_HEIGHT}px` }}>
|
|
73
71
|
<Loading />
|
|
74
72
|
</div>
|
|
75
73
|
)
|
|
@@ -77,16 +75,16 @@ const SingleStateMap = props => {
|
|
|
77
75
|
|
|
78
76
|
const checkForNoData = () => {
|
|
79
77
|
// If no statePicked, return true
|
|
80
|
-
if (!
|
|
78
|
+
if (!config.general.statePicked.fipsCode) return true
|
|
81
79
|
}
|
|
82
80
|
|
|
83
81
|
// Constructs and displays markup for all geos on the map (except territories right now)
|
|
84
|
-
const constructGeoJsx =
|
|
82
|
+
const constructGeoJsx = geographies => {
|
|
85
83
|
const counties = geographies[0].feature.counties
|
|
86
84
|
|
|
87
85
|
let geosJsx = []
|
|
88
86
|
|
|
89
|
-
// Push
|
|
87
|
+
// Push config lines
|
|
90
88
|
geosJsx.push(
|
|
91
89
|
// prettier-ignore
|
|
92
90
|
<SingleState.StateOutput
|
|
@@ -113,12 +111,7 @@ const SingleStateMap = props => {
|
|
|
113
111
|
<CityList
|
|
114
112
|
projection={cityListProjection}
|
|
115
113
|
key='cities'
|
|
116
|
-
data={data}
|
|
117
|
-
state={state}
|
|
118
114
|
geoClickHandler={geoClickHandler}
|
|
119
|
-
applyTooltipsToGeo={applyTooltipsToGeo}
|
|
120
|
-
displayGeoName={displayGeoName}
|
|
121
|
-
applyLegendToRow={applyLegendToRow}
|
|
122
115
|
titleCase={titleCase}
|
|
123
116
|
setSharedFilterValue={setSharedFilterValue}
|
|
124
117
|
isFilterValueSupported={isFilterValueSupported}
|
|
@@ -130,28 +123,28 @@ const SingleStateMap = props => {
|
|
|
130
123
|
}
|
|
131
124
|
return (
|
|
132
125
|
<ErrorBoundary component='SingleStateMap'>
|
|
133
|
-
{statePicked &&
|
|
126
|
+
{statePicked && config.general.allowMapZoom && config.general.statePicked.fipsCode && (
|
|
134
127
|
<svg
|
|
135
|
-
viewBox={
|
|
128
|
+
viewBox={SVG_VIEWBOX}
|
|
136
129
|
preserveAspectRatio='xMinYMin'
|
|
137
130
|
className='svg-container'
|
|
138
131
|
role='img'
|
|
139
|
-
aria-label={handleMapAriaLabels(
|
|
132
|
+
aria-label={handleMapAriaLabels(config)}
|
|
140
133
|
>
|
|
141
134
|
<ZoomableGroup
|
|
142
135
|
center={position.coordinates}
|
|
143
136
|
zoom={position.zoom}
|
|
144
137
|
minZoom={1} // Adjust this value if needed
|
|
145
|
-
maxZoom={
|
|
138
|
+
maxZoom={MAX_ZOOM_LEVEL}
|
|
146
139
|
onMoveEnd={handleMoveEnd}
|
|
147
140
|
projection={projection}
|
|
148
|
-
width={
|
|
149
|
-
height={
|
|
141
|
+
width={SVG_WIDTH}
|
|
142
|
+
height={SVG_HEIGHT}
|
|
150
143
|
>
|
|
151
144
|
<rect
|
|
152
145
|
className='background center-container ocean'
|
|
153
|
-
width={
|
|
154
|
-
height={
|
|
146
|
+
width={SVG_WIDTH}
|
|
147
|
+
height={SVG_HEIGHT}
|
|
155
148
|
fillOpacity={1}
|
|
156
149
|
fill='white'
|
|
157
150
|
></rect>
|
|
@@ -159,14 +152,14 @@ const SingleStateMap = props => {
|
|
|
159
152
|
data={[
|
|
160
153
|
{
|
|
161
154
|
states: topoData?.states,
|
|
162
|
-
counties: topoData.counties.filter(c => c.id.substring(0, 2) ===
|
|
155
|
+
counties: topoData.counties.filter(c => c.id.substring(0, 2) === config.general.statePicked.fipsCode)
|
|
163
156
|
}
|
|
164
157
|
]}
|
|
165
158
|
projection={geoAlbersUsaTerritories}
|
|
166
159
|
fitExtent={[
|
|
167
160
|
[
|
|
168
|
-
[
|
|
169
|
-
[
|
|
161
|
+
[SVG_PADDING, SVG_PADDING],
|
|
162
|
+
[SVG_WIDTH - SVG_PADDING, SVG_HEIGHT - SVG_PADDING]
|
|
170
163
|
],
|
|
171
164
|
stateToShow
|
|
172
165
|
]}
|
|
@@ -176,7 +169,7 @@ const SingleStateMap = props => {
|
|
|
176
169
|
<g
|
|
177
170
|
id='mapGroup'
|
|
178
171
|
className={`countyMapGroup ${
|
|
179
|
-
|
|
172
|
+
config.general.geoType === 'single-state' ? `countyMapGroup--no-transition` : ''
|
|
180
173
|
}`}
|
|
181
174
|
transform={`translate(${translate}) scale(${scale})`}
|
|
182
175
|
data-scale=''
|
|
@@ -187,22 +180,22 @@ const SingleStateMap = props => {
|
|
|
187
180
|
)
|
|
188
181
|
}}
|
|
189
182
|
</CustomProjection>
|
|
190
|
-
{
|
|
183
|
+
{config.annotations.length > 0 && <Annotation.Draggable />}
|
|
191
184
|
</ZoomableGroup>
|
|
192
185
|
</svg>
|
|
193
186
|
)}
|
|
194
|
-
{statePicked && !
|
|
187
|
+
{statePicked && !config.general.allowMapZoom && config.general.statePicked.fipsCode && (
|
|
195
188
|
<svg
|
|
196
|
-
viewBox={
|
|
189
|
+
viewBox={SVG_VIEWBOX}
|
|
197
190
|
preserveAspectRatio='xMinYMin'
|
|
198
191
|
className='svg-container'
|
|
199
192
|
role='img'
|
|
200
|
-
aria-label={handleMapAriaLabels(
|
|
193
|
+
aria-label={handleMapAriaLabels(config)}
|
|
201
194
|
>
|
|
202
195
|
<rect
|
|
203
196
|
className='background center-container ocean'
|
|
204
|
-
width={
|
|
205
|
-
height={
|
|
197
|
+
width={SVG_WIDTH}
|
|
198
|
+
height={SVG_HEIGHT}
|
|
206
199
|
fillOpacity={1}
|
|
207
200
|
fill='white'
|
|
208
201
|
></rect>
|
|
@@ -210,14 +203,14 @@ const SingleStateMap = props => {
|
|
|
210
203
|
data={[
|
|
211
204
|
{
|
|
212
205
|
states: topoData?.states,
|
|
213
|
-
counties: topoData.counties.filter(c => c.id.substring(0, 2) ===
|
|
206
|
+
counties: topoData.counties.filter(c => c.id.substring(0, 2) === config.general.statePicked.fipsCode)
|
|
214
207
|
}
|
|
215
208
|
]}
|
|
216
209
|
projection={geoAlbersUsaTerritories}
|
|
217
210
|
fitExtent={[
|
|
218
211
|
[
|
|
219
|
-
[
|
|
220
|
-
[
|
|
212
|
+
[SVG_PADDING, SVG_PADDING],
|
|
213
|
+
[SVG_WIDTH - SVG_PADDING, SVG_HEIGHT - SVG_PADDING]
|
|
221
214
|
],
|
|
222
215
|
stateToShow
|
|
223
216
|
]}
|
|
@@ -227,7 +220,7 @@ const SingleStateMap = props => {
|
|
|
227
220
|
<g
|
|
228
221
|
id='mapGroup'
|
|
229
222
|
className={`countyMapGroup ${
|
|
230
|
-
|
|
223
|
+
config.general.geoType === 'single-state' ? `countyMapGroup--no-transition` : ''
|
|
231
224
|
}`}
|
|
232
225
|
transform={`translate(${translate}) scale(${scale})`}
|
|
233
226
|
data-scale=''
|
|
@@ -238,28 +231,28 @@ const SingleStateMap = props => {
|
|
|
238
231
|
)
|
|
239
232
|
}}
|
|
240
233
|
</CustomProjection>
|
|
241
|
-
{
|
|
234
|
+
{config.annotations.length > 0 && <Annotation.Draggable />}
|
|
242
235
|
</svg>
|
|
243
236
|
)}
|
|
244
237
|
|
|
245
238
|
{checkForNoData() && (
|
|
246
239
|
<svg
|
|
247
|
-
viewBox={
|
|
240
|
+
viewBox={SVG_VIEWBOX}
|
|
248
241
|
preserveAspectRatio='xMinYMin'
|
|
249
242
|
className='svg-container'
|
|
250
243
|
role='img'
|
|
251
|
-
aria-label={handleMapAriaLabels(
|
|
244
|
+
aria-label={handleMapAriaLabels(config)}
|
|
252
245
|
>
|
|
253
246
|
<Text
|
|
254
247
|
verticalAnchor='start'
|
|
255
248
|
textAnchor='middle'
|
|
256
|
-
x={
|
|
257
|
-
width={
|
|
258
|
-
y={
|
|
249
|
+
x={SVG_WIDTH / 2}
|
|
250
|
+
width={SVG_WIDTH}
|
|
251
|
+
y={SVG_HEIGHT / 2}
|
|
259
252
|
fontSize={18}
|
|
260
253
|
style={{ fontSize: '28px', height: '18px' }}
|
|
261
254
|
>
|
|
262
|
-
{
|
|
255
|
+
{config.general.noStateFoundMessage}
|
|
263
256
|
</Text>
|
|
264
257
|
</svg>
|
|
265
258
|
)}
|