@cdc/map 4.25.3 → 4.25.6

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 (119) hide show
  1. package/.idea/map.iml +12 -0
  2. package/.idea/modules.xml +8 -0
  3. package/.idea/vcs.xml +6 -0
  4. package/dist/cdcmap.js +31254 -32242
  5. package/examples/hex-colors.json +3 -3
  6. package/examples/m2.json +32904 -0
  7. package/examples/private/test.json +470 -1457
  8. package/examples/private/{mmr.json → wastewatermap.json} +86 -115
  9. package/index.html +36 -63
  10. package/package.json +7 -19
  11. package/src/CdcMap.tsx +56 -1552
  12. package/src/CdcMapComponent.tsx +608 -0
  13. package/src/_stories/CdcMap.Legend.Gradient.stories.tsx +10 -0
  14. package/src/_stories/CdcMap.Legend.stories.tsx +67 -0
  15. package/src/_stories/CdcMap.Table.stories.tsx +19 -0
  16. package/src/_stories/CdcMap.stories.tsx +12 -1
  17. package/src/_stories/UsaMap.NoData.stories.tsx +4 -4
  18. package/src/_stories/_mock/default-patterns.json +8 -5
  19. package/src/_stories/_mock/legend-bins.json +428 -0
  20. package/{examples/private/default-patterns.json → src/_stories/_mock/legends/legend-tests.json} +36 -131
  21. package/src/cdcMapComponent.styles.css +9 -0
  22. package/src/components/Annotation/Annotation.Draggable.tsx +27 -26
  23. package/src/components/Annotation/AnnotationDropdown.tsx +5 -6
  24. package/src/components/BubbleList.tsx +135 -49
  25. package/src/components/CityList.tsx +89 -87
  26. package/src/components/DataTable.tsx +8 -8
  27. package/src/components/EditorPanel/components/EditorPanel.tsx +823 -885
  28. package/src/components/EditorPanel/components/Error.tsx +9 -2
  29. package/src/components/EditorPanel/components/HexShapeSettings.tsx +127 -141
  30. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +55 -86
  31. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +89 -75
  32. package/src/components/EditorPanel/components/editorPanel.styles.css +95 -0
  33. package/src/components/Geo.tsx +9 -1
  34. package/src/components/GoogleMap/components/GoogleMap.tsx +1 -1
  35. package/src/components/Legend/components/Legend.tsx +92 -87
  36. package/src/components/Legend/components/LegendGroup/Legend.Group.tsx +128 -0
  37. package/src/components/Legend/components/LegendGroup/legend.group.css +27 -0
  38. package/src/components/Legend/components/LegendItem.Hex.tsx +4 -1
  39. package/src/components/Legend/components/index.scss +74 -17
  40. package/src/components/Modal.tsx +17 -7
  41. package/src/components/NavigationMenu.tsx +11 -9
  42. package/src/components/UsaMap/components/SingleState/SingleState.CountyOutput.tsx +12 -8
  43. package/src/components/UsaMap/components/SingleState/SingleState.StateOutput.tsx +4 -4
  44. package/src/components/UsaMap/components/TerritoriesSection.tsx +33 -10
  45. package/src/components/UsaMap/components/Territory/Territory.Hexagon.tsx +12 -10
  46. package/src/components/UsaMap/components/Territory/Territory.Rectangle.tsx +12 -14
  47. package/src/components/UsaMap/components/Territory/TerritoryShape.ts +2 -1
  48. package/src/components/UsaMap/components/UsaMap.County.tsx +138 -96
  49. package/src/components/UsaMap/components/UsaMap.Region.styles.css +72 -0
  50. package/src/components/UsaMap/components/UsaMap.Region.tsx +56 -103
  51. package/src/components/UsaMap/components/UsaMap.SingleState.styles.css +10 -0
  52. package/src/components/UsaMap/components/UsaMap.SingleState.tsx +65 -74
  53. package/src/components/UsaMap/components/UsaMap.State.tsx +112 -91
  54. package/src/components/UsaMap/helpers/map.ts +1 -1
  55. package/src/components/UsaMap/helpers/shapes.ts +20 -7
  56. package/src/components/WorldMap/WorldMap.tsx +64 -118
  57. package/src/components/WorldMap/worldMap.styles.css +28 -0
  58. package/src/components/ZoomControls.tsx +15 -13
  59. package/src/components/zoomControls.styles.css +53 -0
  60. package/src/context.ts +17 -9
  61. package/src/data/initial-state.js +5 -2
  62. package/src/helpers/addUIDs.ts +150 -0
  63. package/src/helpers/applyColorToLegend.ts +39 -64
  64. package/src/helpers/applyLegendToRow.ts +51 -0
  65. package/src/helpers/colorDistributions.ts +12 -0
  66. package/src/helpers/constants.ts +44 -0
  67. package/src/helpers/displayGeoName.ts +9 -2
  68. package/src/helpers/formatLegendLocation.ts +3 -2
  69. package/src/helpers/generateColorsArray.ts +2 -1
  70. package/src/helpers/generateRuntimeData.ts +78 -0
  71. package/src/helpers/generateRuntimeFilters.ts +63 -0
  72. package/src/helpers/generateRuntimeLegend.ts +566 -0
  73. package/src/helpers/generateRuntimeLegendHash.ts +16 -15
  74. package/src/helpers/getColumnNames.ts +19 -0
  75. package/src/helpers/getMapContainerClasses.ts +23 -0
  76. package/src/helpers/getStatePicked.ts +8 -0
  77. package/src/helpers/handleMapTabbing.ts +31 -0
  78. package/src/helpers/hashObj.ts +1 -1
  79. package/src/helpers/index.ts +22 -0
  80. package/src/helpers/navigationHandler.ts +3 -3
  81. package/src/helpers/resetLegendToggles.ts +13 -0
  82. package/src/helpers/setBinNumbers.ts +5 -0
  83. package/src/helpers/sortSpecialClassesLast.ts +7 -0
  84. package/src/helpers/tests/getColumnNames.test.ts +52 -0
  85. package/src/helpers/titleCase.ts +1 -1
  86. package/src/helpers/toggleLegendActive.ts +25 -0
  87. package/src/hooks/useApplyTooltipsToGeo.tsx +51 -0
  88. package/src/hooks/useColumnsRequiredChecker.ts +51 -0
  89. package/src/hooks/useGeoClickHandler.ts +45 -0
  90. package/src/hooks/useLegendSeparators.ts +26 -0
  91. package/src/hooks/useMapLayers.tsx +34 -60
  92. package/src/hooks/useModal.ts +22 -0
  93. package/src/hooks/useResizeObserver.ts +4 -5
  94. package/src/hooks/useStateZoom.tsx +52 -75
  95. package/src/hooks/useTooltip.ts +2 -3
  96. package/src/index.jsx +3 -9
  97. package/src/scss/editor-panel.scss +3 -99
  98. package/src/scss/main.scss +1 -19
  99. package/src/scss/map.scss +15 -220
  100. package/src/store/map.actions.ts +46 -0
  101. package/src/store/map.reducer.ts +96 -0
  102. package/src/types/Annotations.ts +24 -0
  103. package/src/types/MapConfig.ts +23 -3
  104. package/src/types/MapContext.ts +36 -35
  105. package/src/types/Modal.ts +1 -0
  106. package/src/types/RuntimeData.ts +3 -0
  107. package/examples/private/DEV-9644.json +0 -184
  108. package/examples/private/DEV-9989.json +0 -229
  109. package/examples/private/ardi.json +0 -180
  110. package/examples/private/colors 2.json +0 -416
  111. package/examples/private/colors.json +0 -416
  112. package/examples/private/colors.json.zip +0 -0
  113. package/examples/private/customColors.json +0 -45348
  114. package/examples/test.json +0 -183
  115. package/src/helpers/closeModal.ts +0 -9
  116. package/src/scss/btn.scss +0 -69
  117. package/src/scss/filters.scss +0 -27
  118. package/src/scss/variables.scss +0 -1
  119. /package/src/hooks/{useActiveElement.js → useActiveElement.ts} +0 -0
@@ -1,28 +1,19 @@
1
1
  import { useEffect, useState, useRef, useContext } from 'react'
2
- import * as d3 from 'd3-geo'
3
-
4
2
  import { geoCentroid, geoPath, geoContains } from 'd3-geo'
5
3
  import { feature } from 'topojson-client'
6
4
  import { geoAlbersUsaTerritories } from 'd3-composite-projections'
7
5
  import debounce from 'lodash.debounce'
8
-
9
6
  import Loading from '@cdc/core/components/Loading'
10
7
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
11
-
12
8
  import useMapLayers from '../../../hooks/useMapLayers'
13
9
  import ConfigContext from '../../../context'
14
- import {
15
- drawCircle,
16
- drawDiamond,
17
- drawSquare,
18
- drawTriangle,
19
- drawPin,
20
- drawStar,
21
- drawShape,
22
- createShapeProperties
23
- } from '../helpers/shapes'
24
- import { getGeoStrokeColor } from '../../../helpers/colors'
25
- import { handleMapAriaLabels } from '../../../helpers/handleMapAriaLabels'
10
+ import { drawShape, createShapeProperties } from '../helpers/shapes'
11
+ import { getGeoStrokeColor, handleMapAriaLabels, displayGeoName } from '../../../helpers'
12
+ import useGeoClickHandler from '../../../hooks/useGeoClickHandler'
13
+ import { applyLegendToRow } from '../../../helpers/applyLegendToRow'
14
+ import useApplyTooltipsToGeo from '../../../hooks/useApplyTooltipsToGeo'
15
+ import { MapConfig } from '../../../types/MapConfig'
16
+ import { DEFAULT_MAP_BACKGROUND } from '../../../helpers/constants'
26
17
 
27
18
  const getCountyTopoURL = year => {
28
19
  return `https://www.cdc.gov/TemplatePackage/contrib/data/county-topography/cb_${year}_us_county_20m.json`
@@ -35,7 +26,7 @@ const sortById = (a, b) => {
35
26
  }
36
27
 
37
28
  const getTopoData = year => {
38
- return new Promise((resolve, reject) => {
29
+ return new Promise(resolve => {
39
30
  const resolveWithTopo = async response => {
40
31
  if (response.status !== 200) {
41
32
  response = await import(/* webpackChunkName: "cb_2019_us_county_20m" */ './../data/cb_2019_us_county_20m.json')
@@ -43,7 +34,14 @@ const getTopoData = year => {
43
34
  response = await response.json()
44
35
  }
45
36
 
46
- let topoData = {}
37
+ let topoData = {
38
+ year: undefined,
39
+ counties: undefined,
40
+ states: undefined,
41
+ mapData: undefined,
42
+ countyIndecies: undefined,
43
+ projection: undefined
44
+ }
47
45
 
48
46
  topoData.year = year || 'default'
49
47
  topoData.counties = feature(response, response.objects.counties).features
@@ -109,11 +107,11 @@ const getTopoData = year => {
109
107
  })
110
108
  }
111
109
 
112
- const getCurrentTopoYear = (state, runtimeFilters) => {
113
- let currentYear = state.general.countyCensusYear
110
+ const getCurrentTopoYear = (config: MapConfig, runtimeFilters) => {
111
+ let currentYear = config.general.countyCensusYear
114
112
 
115
- if (state.general.filterControlsCountyYear && runtimeFilters && runtimeFilters.length > 0) {
116
- let yearFilter = runtimeFilters.filter(filter => filter.columnName === state.general.filterControlsCountyYear)
113
+ if (config.general.filterControlsCountyYear && runtimeFilters && runtimeFilters.length > 0) {
114
+ let yearFilter = runtimeFilters.filter(filter => filter.columnName === config.general.filterControlsCountyYear)
117
115
  if (yearFilter.length > 0 && yearFilter[0].active) {
118
116
  currentYear = yearFilter[0].active
119
117
  }
@@ -122,40 +120,37 @@ const getCurrentTopoYear = (state, runtimeFilters) => {
122
120
  return currentYear || 'default'
123
121
  }
124
122
 
125
- const isTopoReady = (topoData, state, runtimeFilters) => {
126
- let currentYear = getCurrentTopoYear(state, runtimeFilters)
123
+ const isTopoReady = (topoData, config: MapConfig, runtimeFilters) => {
124
+ let currentYear = getCurrentTopoYear(config, runtimeFilters)
127
125
 
128
126
  return topoData.year && (!currentYear || currentYear === topoData.year)
129
127
  }
130
128
 
131
- const CountyMap = props => {
132
- // prettier-ignore
129
+ const CountyMap = () => {
133
130
  const {
134
- applyLegendToRow,
135
- applyTooltipsToGeo,
136
- container,
137
- containerEl,
138
- data,
139
- displayGeoName,
140
- geoClickHandler,
141
- runtimeFilters,
142
- runtimeLegend,
143
- setState,
144
- state,
145
- tooltipId,
146
- tooltipRef,
131
+ container,
132
+ containerEl,
133
+ data,
134
+ runtimeFilters,
135
+ runtimeLegend,
136
+ setConfig,
137
+ config,
138
+ tooltipId,
139
+ tooltipRef,
140
+ legendMemo,
141
+ legendSpecialClassLastMemo
147
142
  } = useContext(ConfigContext)
148
143
 
149
144
  // CREATE STATE LINES
150
- const projection = geoAlbersUsaTerritories()
151
- const geoStrokeColor = getGeoStrokeColor(state)
152
-
145
+ const geoStrokeColor = getGeoStrokeColor(config)
146
+ const { geoClickHandler } = useGeoClickHandler()
147
+ const { applyTooltipsToGeo } = useApplyTooltipsToGeo()
153
148
  const [focus, setFocus] = useState({})
154
149
  const [topoData, setTopoData] = useState({})
155
150
 
156
151
  const pathGenerator = geoPath().projection(geoAlbersUsaTerritories())
157
152
 
158
- const { featureArray } = useMapLayers(state, '', pathGenerator, tooltipId)
153
+ const { featureArray } = useMapLayers(config, setConfig, pathGenerator, tooltipId)
159
154
 
160
155
  useEffect(() => {
161
156
  if (containerEl) {
@@ -166,7 +161,7 @@ const CountyMap = props => {
166
161
  })
167
162
 
168
163
  useEffect(() => {
169
- let currentYear = getCurrentTopoYear(state, runtimeFilters)
164
+ let currentYear = getCurrentTopoYear(config, runtimeFilters)
170
165
 
171
166
  if (currentYear !== topoData.year) {
172
167
  getTopoData(currentYear).then(response => {
@@ -177,17 +172,17 @@ const CountyMap = props => {
177
172
  setTopoData(response)
178
173
  })
179
174
  }
180
- }, [state.general.countyCensusYear, state.general.filterControlsCountyYear, JSON.stringify(runtimeFilters)])
175
+ }, [config.general.countyCensusYear, config.general.filterControlsCountyYear, JSON.stringify(runtimeFilters)])
181
176
 
182
177
  // Whenever the memo at the top is triggered and the map is called to re-render, call drawCanvas and update
183
178
  // The resize function so it includes the latest state variables
184
179
  useEffect(() => {
185
- if (isTopoReady(topoData, state, runtimeFilters)) {
180
+ if (isTopoReady(topoData, config, runtimeFilters)) {
186
181
  drawCanvas()
187
182
  }
188
183
 
189
184
  const onResize = () => {
190
- if (canvasRef.current && isTopoReady(topoData, state, runtimeFilters)) {
185
+ if (canvasRef.current && isTopoReady(topoData, config, runtimeFilters)) {
191
186
  drawCanvas()
192
187
  }
193
188
  }
@@ -203,7 +198,7 @@ const CountyMap = props => {
203
198
  const canvasRef = useRef()
204
199
 
205
200
  // If runtimeData is not defined, show loader
206
- if (!data || !isTopoReady(topoData, state, runtimeFilters)) {
201
+ if (!data || !isTopoReady(topoData, config, runtimeFilters)) {
207
202
  return (
208
203
  <div style={{ height: 300 }}>
209
204
  <Loading />
@@ -215,8 +210,8 @@ const CountyMap = props => {
215
210
  const lineWidth = 1
216
211
 
217
212
  const onReset = () => {
218
- setState({
219
- ...state,
213
+ setConfig({
214
+ ...config,
220
215
  mapPosition: { coordinates: [0, 30], zoom: 1 }
221
216
  })
222
217
  setFocus({})
@@ -240,8 +235,8 @@ const CountyMap = props => {
240
235
 
241
236
  // If the user clicked outside of all states, no behavior
242
237
  if (clickedState) {
243
- setState({
244
- ...state,
238
+ setConfig({
239
+ ...config,
245
240
  mapPosition: { coordinates: [0, 30], zoom: 3 }
246
241
  })
247
242
 
@@ -274,13 +269,13 @@ const CountyMap = props => {
274
269
  // Redraw with focus on state
275
270
  setFocus({ id: clickedState.id, index: focusIndex, center: geoCentroid(clickedState), feature: clickedState })
276
271
  }
277
- if (state.general.type === 'us-geocode') {
278
- const geoRadius = (state.visual.geoCodeCircleSize || 5) * (focus.id ? 2 : 1)
272
+ if (config.general.type === 'us-geocode') {
273
+ const geoRadius = (config.visual.geoCodeCircleSize || 5) * (focus.id ? 2 : 1)
279
274
  let clickedGeo
280
275
  for (let i = 0; i < runtimeKeys.length; i++) {
281
276
  const pixelCoords = topoData.projection([
282
- data[runtimeKeys[i]][state.columns.longitude.name],
283
- data[runtimeKeys[i]][state.columns.latitude.name]
277
+ data[runtimeKeys[i]][config.columns.longitude.name],
278
+ data[runtimeKeys[i]][config.columns.latitude.name]
284
279
  ])
285
280
  if (pixelCoords && Math.sqrt(Math.pow(pixelCoords[0] - x, 2) + Math.pow(pixelCoords[1] - y, 2)) < geoRadius) {
286
281
  clickedGeo = data[runtimeKeys[i]]
@@ -289,7 +284,7 @@ const CountyMap = props => {
289
284
  }
290
285
 
291
286
  if (clickedGeo) {
292
- geoClickHandler(displayGeoName(clickedGeo[state.columns.geo.name]), clickedGeo)
287
+ geoClickHandler(displayGeoName(clickedGeo[config.columns.geo.name]), clickedGeo)
293
288
  }
294
289
  }
295
290
  }
@@ -297,7 +292,7 @@ const CountyMap = props => {
297
292
  const canvasHover = e => {
298
293
  if (
299
294
  !tooltipRef.current ||
300
- state.tooltips.appearanceType !== 'hover' ||
295
+ config.tooltips.appearanceType !== 'hover' ||
301
296
  window.matchMedia('(any-hover: none)').matches
302
297
  )
303
298
  return
@@ -312,17 +307,32 @@ const CountyMap = props => {
312
307
  let pointCoordinates = topoData.projection.invert([x, y])
313
308
 
314
309
  const currentTooltipIndex = parseInt(tooltipRef.current.getAttribute('data-index'))
315
- const geoRadius = (state.visual.geoCodeCircleSize || 5) * (focus.id ? 2 : 1)
310
+ const geoRadius = (config.visual.geoCodeCircleSize || 5) * (focus.id ? 2 : 1)
316
311
 
317
312
  const context = canvas.getContext('2d')
318
313
  const path = geoPath(topoData.projection, context)
319
314
 
320
315
  // Handle standard county map hover
321
- if (state.general.type !== 'us-geocode') {
316
+ if (config.general.type !== 'us-geocode') {
322
317
  //If no tooltip is shown, or if the current geo associated with the tooltip shown is no longer containing the mouse, then rerender the tooltip
323
318
  if (isNaN(currentTooltipIndex) || !geoContains(topoData.mapData[currentTooltipIndex], pointCoordinates)) {
324
- if (!isNaN(currentTooltipIndex) && applyLegendToRow(data[topoData.mapData[currentTooltipIndex].id])) {
325
- context.fillStyle = applyLegendToRow(data[topoData.mapData[currentTooltipIndex].id])[0]
319
+ if (
320
+ !isNaN(currentTooltipIndex) &&
321
+ applyLegendToRow(
322
+ data[topoData.mapData[currentTooltipIndex].id],
323
+ config,
324
+ runtimeLegend,
325
+ legendMemo,
326
+ legendSpecialClassLastMemo
327
+ )
328
+ ) {
329
+ context.fillStyle = applyLegendToRow(
330
+ data[topoData.mapData[currentTooltipIndex].id],
331
+ config,
332
+ runtimeLegend,
333
+ legendMemo,
334
+ legendSpecialClassLastMemo
335
+ )[0]
326
336
  context.strokeStyle = geoStrokeColor
327
337
  context.lineWidth = lineWidth
328
338
  context.beginPath()
@@ -342,7 +352,7 @@ const CountyMap = props => {
342
352
  }
343
353
  }
344
354
 
345
- // If a state is hovered and it is not an unfocused state, search only the counties within that state for the county hovered
355
+ // If a state is hovered, and it is not an unfocused state, search only the counties within that state for the county hovered
346
356
  if (hoveredState && (!focus.id || focus.id === hoveredState) && topoData.countyIndecies[hoveredState]) {
347
357
  for (let i = topoData.countyIndecies[hoveredState][0]; i <= topoData.countyIndecies[hoveredState][1]; i++) {
348
358
  if (geoContains(topoData.mapData[i], pointCoordinates)) {
@@ -355,9 +365,17 @@ const CountyMap = props => {
355
365
 
356
366
  // If the hovered county is found, show the tooltip for that county, otherwise hide the tooltip
357
367
  if (county && data[county.id]) {
358
- if (applyLegendToRow(data[county.id])) {
368
+ if (applyLegendToRow(data[county.id], config, runtimeLegend, legendMemo, legendSpecialClassLastMemo)) {
369
+ let fillColor = applyLegendToRow(
370
+ data[county.id],
371
+ config,
372
+ runtimeLegend,
373
+ legendMemo,
374
+ legendSpecialClassLastMemo
375
+ )[0]
376
+ if (fillColor === '#000000') return
359
377
  context.globalAlpha = 1
360
- context.fillStyle = applyLegendToRow(data[county.id])[1]
378
+ context.fillStyle = fillColor
361
379
  context.strokeStyle = geoStrokeColor
362
380
  context.lineWidth = lineWidth
363
381
  context.beginPath()
@@ -386,11 +404,10 @@ const CountyMap = props => {
386
404
  // Handle geo map hover
387
405
  if (!isNaN(currentTooltipIndex)) {
388
406
  const pixelCoords = topoData.projection([
389
- data[runtimeKeys[currentTooltipIndex]][state.columns.longitude.name],
390
- data[runtimeKeys[currentTooltipIndex]][state.columns.latitude.name]
407
+ data[runtimeKeys[currentTooltipIndex]][config.columns.longitude.name],
408
+ data[runtimeKeys[currentTooltipIndex]][config.columns.latitude.name]
391
409
  ])
392
410
  if (pixelCoords && Math.sqrt(Math.pow(pixelCoords[0] - x, 2) + Math.pow(pixelCoords[1] - y, 2)) < geoRadius) {
393
- // Who knew pythagorean theorum was useful
394
411
  return // The user is still hovering over the previous geo point, don't redraw tooltip
395
412
  }
396
413
  }
@@ -402,24 +419,27 @@ const CountyMap = props => {
402
419
  let hoveredGeoIndex
403
420
  for (let i = 0; i < runtimeKeys.length; i++) {
404
421
  const pixelCoords = topoData.projection([
405
- data[runtimeKeys[i]][state.columns.longitude.name],
406
- data[runtimeKeys[i]][state.columns.latitude.name]
422
+ data[runtimeKeys[i]][config.columns.longitude.name],
423
+ data[runtimeKeys[i]][config.columns.latitude.name]
407
424
  ])
408
- let includedShapes = ['circle', 'diamond', 'star', 'triangle', 'square'].includes(state.visual.cityStyle)
425
+ let includedShapes = ['circle', 'diamond', 'star', 'triangle', 'square'].includes(config.visual.cityStyle)
409
426
  if (
410
427
  includedShapes &&
411
428
  pixelCoords &&
412
429
  Math.sqrt(Math.pow(pixelCoords[0] - x, 2) + Math.pow(pixelCoords[1] - y, 2)) < geoRadius &&
413
- applyLegendToRow(data[runtimeKeys[i]])
430
+ applyLegendToRow(data[runtimeKeys[i]], config, runtimeLegend, legendMemo, legendSpecialClassLastMemo)
414
431
  ) {
415
432
  hoveredGeo = data[runtimeKeys[i]]
416
433
  hoveredGeoIndex = i
417
434
  break
418
435
  }
419
436
 
420
- if (state.visual.cityStyle === 'pin' && pixelCoords) {
437
+ if (config.visual.cityStyle === 'pin' && pixelCoords) {
421
438
  const distance = Math.hypot(pixelCoords[0] - x, pixelCoords[1] - y)
422
- if (distance < 15 && applyLegendToRow(data[runtimeKeys[i]])) {
439
+ if (
440
+ distance < 15 &&
441
+ applyLegendToRow(data[runtimeKeys[i]], config, runtimeLegend, legendMemo, legendSpecialClassLastMemo)
442
+ ) {
423
443
  hoveredGeo = data[runtimeKeys[i]]
424
444
  hoveredGeoIndex = i
425
445
  break
@@ -438,7 +458,7 @@ const CountyMap = props => {
438
458
  tooltipRef.current.style.left = tooltipX + 5 + 'px'
439
459
  }
440
460
  tooltipRef.current.innerHTML = applyTooltipsToGeo(
441
- displayGeoName(hoveredGeo[state.columns.geo.name]),
461
+ displayGeoName(hoveredGeo[config.columns.geo.name]),
442
462
  hoveredGeo
443
463
  )
444
464
  tooltipRef.current.setAttribute('data-index', hoveredGeoIndex)
@@ -459,7 +479,7 @@ const CountyMap = props => {
459
479
 
460
480
  // Redraws canvas. Takes as parameters the fips id of a state to center on and the [lat,long] center of that state
461
481
  const drawCanvas = () => {
462
- if (canvasRef.current && runtimeLegend.length > 0) {
482
+ if (canvasRef.current && runtimeLegend.items.length > 0) {
463
483
  const canvas = canvasRef.current
464
484
  const context = canvas.getContext('2d')
465
485
  const path = geoPath(topoData.projection, context)
@@ -476,7 +496,7 @@ const CountyMap = props => {
476
496
  if (resetButton.current) resetButton.current.style.display = 'block'
477
497
  }
478
498
 
479
- // Centers the projection on the paramter passed
499
+ // Centers the projection on the parameter passed
480
500
  // Centers the projection on the parameter passed
481
501
  if (focus.feature) {
482
502
  const PADDING = 10
@@ -495,20 +515,28 @@ const CountyMap = props => {
495
515
  context.strokeStyle = geoStrokeColor
496
516
 
497
517
  // Iterates through each state/county topo and renders it
498
- topoData.mapData.forEach((geo, i) => {
518
+ topoData.mapData.forEach(geo => {
499
519
  // If invalid geo item, don't render
500
520
  if (!geo.id) return
501
521
  // If the map is focused on one state, don't render counties that are not in that state
502
522
  if (focus.id && geo.id.length > 2 && geo.id.indexOf(focus.id) !== 0) return
503
523
  // If rendering a geocode map without a focus, don't render counties
504
- if (!focus.id && state.general.type === 'us-geocode' && geo.id.length > 2) return
524
+ if (!focus.id && config.general.type === 'us-geocode' && geo.id.length > 2) return
505
525
 
506
526
  // Gets numeric data associated with the topo data for this state/county
507
527
  const geoData = data[geo.id]
508
528
 
509
529
  // Renders state/county
510
- const legendValues = geoData !== undefined ? applyLegendToRow(geoData) : false
511
- context.fillStyle = legendValues && state.general.type !== 'us-geocode' ? legendValues[0] : '#EEE'
530
+ const legendValues =
531
+ geoData !== undefined
532
+ ? applyLegendToRow(geoData, config, runtimeLegend, legendMemo, legendSpecialClassLastMemo)
533
+ : false
534
+ context.fillStyle =
535
+ legendValues && config.general.type !== 'us-geocode'
536
+ ? legendValues[0] === '#000000'
537
+ ? DEFAULT_MAP_BACKGROUND
538
+ : legendValues[0]
539
+ : DEFAULT_MAP_BACKGROUND
512
540
  context.beginPath()
513
541
  path(geo)
514
542
  context.fill()
@@ -537,10 +565,10 @@ const CountyMap = props => {
537
565
  })
538
566
  }
539
567
 
540
- if (state.general.type === 'us-geocode') {
568
+ if (config.general.type === 'us-geocode') {
541
569
  context.strokeStyle = geoStrokeColor
542
- const geoRadius = (state.visual.geoCodeCircleSize || 5) * (focus.id ? 2 : 1)
543
- const { additionalCityStyles } = state.visual || []
570
+ const geoRadius = (config.visual.geoCodeCircleSize || 5) * (focus.id ? 2 : 1)
571
+ const { additionalCityStyles } = config.visual || []
544
572
  const cityStyles = Object.values(data)
545
573
  .filter(d => additionalCityStyles.some(style => String(d[style.column]) === String(style.value)))
546
574
  .map(d => {
@@ -552,15 +580,25 @@ const CountyMap = props => {
552
580
 
553
581
  let cityPixelCoords = []
554
582
  cityStyles.forEach(city => {
555
- cityPixelCoords = topoData.projection([city[state.columns.longitude.name], city[state.columns.latitude.name]])
583
+ cityPixelCoords = topoData.projection([
584
+ city[config.columns.longitude.name],
585
+ city[config.columns.latitude.name]
586
+ ])
556
587
 
557
588
  if (cityPixelCoords) {
558
- const legendValues = applyLegendToRow(data[city?.value])
589
+ const legendValues = applyLegendToRow(
590
+ data[city?.value],
591
+ config,
592
+ runtimeLegend,
593
+ legendMemo,
594
+ legendSpecialClassLastMemo
595
+ )
559
596
  if (legendValues) {
597
+ if (legendValues?.[0] === '#000000') return
560
598
  const shapeType = city?.shape?.toLowerCase()
561
- const shapeProperties = createShapeProperties(shapeType, cityPixelCoords, legendValues, state, geoRadius)
599
+ const shapeProperties = createShapeProperties(shapeType, cityPixelCoords, legendValues, config, geoRadius)
562
600
  if (shapeProperties) {
563
- drawShape(shapeProperties, context, state, lineWidth)
601
+ drawShape(shapeProperties, context, config, lineWidth)
564
602
  }
565
603
  }
566
604
  }
@@ -570,16 +608,20 @@ const CountyMap = props => {
570
608
  const citiesList = new Set(cityStyles.map(item => item.value))
571
609
 
572
610
  const pixelCoords = topoData.projection([
573
- data[key][state.columns.longitude.name],
574
- data[key][state.columns.latitude.name]
611
+ data[key][config.columns.longitude.name],
612
+ data[key][config.columns.latitude.name]
575
613
  ])
576
614
  if (pixelCoords && !citiesList.has(key)) {
577
- const legendValues = data[key] !== undefined ? applyLegendToRow(data[key]) : false
615
+ const legendValues =
616
+ data[key] !== undefined
617
+ ? applyLegendToRow(data[key], config, runtimeLegend, legendMemo, legendSpecialClassLastMemo)
618
+ : false
578
619
  if (legendValues) {
579
- const shapeType = state.visual.cityStyle.toLowerCase()
580
- const shapeProperties = createShapeProperties(shapeType, pixelCoords, legendValues, state, geoRadius)
620
+ if (legendValues?.[0] === '#000000' || legendValues?.[0] === DEFAULT_MAP_BACKGROUND) return
621
+ const shapeType = config.visual.cityStyle.toLowerCase()
622
+ const shapeProperties = createShapeProperties(shapeType, pixelCoords, legendValues, config, geoRadius)
581
623
  if (shapeProperties) {
582
- drawShape(shapeProperties, context, state, lineWidth)
624
+ drawShape(shapeProperties, context, config, lineWidth)
583
625
  }
584
626
  }
585
627
  }
@@ -592,7 +634,7 @@ const CountyMap = props => {
592
634
  <ErrorBoundary component='CountyMap'>
593
635
  <canvas
594
636
  ref={canvasRef}
595
- aria-label={handleMapAriaLabels(state)}
637
+ aria-label={handleMapAriaLabels(config)}
596
638
  onMouseMove={canvasHover}
597
639
  onMouseOut={() => {
598
640
  tooltipRef.current.style.display = 'none'
@@ -602,7 +644,7 @@ const CountyMap = props => {
602
644
  className='county-map-canvas'
603
645
  ></canvas>
604
646
 
605
- <button className={`btn btn--reset btn-primary`} onClick={onReset} ref={resetButton} tabIndex='0'>
647
+ <button className={`btn btn--reset btn-primary p-absolute`} onClick={onReset} ref={resetButton} tabIndex={0}>
606
648
  Reset Zoom
607
649
  </button>
608
650
  </ErrorBoundary>
@@ -0,0 +1,72 @@
1
+ #region-1-label,
2
+ #region-2-label,
3
+ #region-3-label,
4
+ #region-4-label,
5
+ #region-5-label,
6
+ #region-6-label,
7
+ #region-7-label,
8
+ #region-8-label,
9
+ #region-9-label,
10
+ #region-10-label {
11
+ background: #fff;
12
+ }
13
+
14
+ #region-1-label {
15
+ transform: translate(90%, 22%);
16
+ }
17
+
18
+ #region-2-label {
19
+ transform: translate(83%, 33%);
20
+ }
21
+
22
+ #region-3-label {
23
+ transform: translate(75%, 45%);
24
+ }
25
+
26
+ #region-4-label {
27
+ transform: translate(68%, 70%);
28
+ }
29
+
30
+ #region-5-label {
31
+ transform: translate(65%, 44%);
32
+ }
33
+
34
+ #region-6-label {
35
+ transform: translate(45%, 75%);
36
+ }
37
+
38
+ #region-7-label {
39
+ transform: translate(53%, 47%);
40
+ }
41
+
42
+ #region-8-label {
43
+ transform: translate(35%, 30%);
44
+ }
45
+
46
+ #region-9-label {
47
+ transform: translate(18%, 58%);
48
+ }
49
+
50
+ #region-10-label {
51
+ transform: translate(15%, 28%);
52
+ }
53
+
54
+ #region-2-territories,
55
+ #region-9-territories {
56
+ text {
57
+ font-weight: bold;
58
+ font-size: 14px;
59
+ }
60
+ }
61
+
62
+ #region-2-territories {
63
+ transform: translate(86%, 40%);
64
+ }
65
+
66
+ #region-9-territories {
67
+ transform: translate(4%, 72%);
68
+
69
+ .region-9-row2 {
70
+ transform: translateY(34px);
71
+ }
72
+ }