@cdc/map 4.25.5-1 → 4.25.6-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/.idea/map.iml +12 -0
- package/.idea/modules.xml +8 -0
- package/.idea/vcs.xml +6 -0
- package/LICENSE +201 -0
- package/dist/cdcmap.js +19455 -18752
- package/examples/private/m.json +427 -0
- package/index.html +33 -32
- package/package.json +7 -13
- package/src/CdcMap.tsx +10 -2
- package/src/CdcMapComponent.tsx +23 -9
- package/src/_stories/CdcMap.Table.stories.tsx +19 -0
- package/src/_stories/CdcMap.stories.tsx +8 -0
- package/src/_stories/_mock/default-patterns.json +8 -5
- package/src/_stories/_mock/legend-bins.json +428 -0
- package/src/components/EditorPanel/components/EditorPanel.tsx +120 -76
- package/src/components/Legend/components/index.scss +56 -11
- package/src/components/UsaMap/components/UsaMap.SingleState.tsx +16 -18
- package/src/helpers/addUIDs.ts +1 -2
- package/src/helpers/formatLegendLocation.ts +3 -2
- package/src/helpers/generateRuntimeData.ts +6 -2
- package/src/helpers/generateRuntimeLegend.ts +88 -59
- package/src/helpers/getStatePicked.ts +8 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useCallback, useContext } from 'react'
|
|
2
2
|
import ConfigContext from '../context'
|
|
3
3
|
import {
|
|
4
|
+
addUIDs,
|
|
4
5
|
applyColorToLegend,
|
|
5
6
|
getGeoFillColor,
|
|
6
7
|
hashObj,
|
|
@@ -53,11 +54,15 @@ export const generateRuntimeLegend = (
|
|
|
53
54
|
const newLegendMemo = new Map() // Reset memoization
|
|
54
55
|
const newLegendSpecialClassLastMemo = new Map() // Reset bin memoization
|
|
55
56
|
const countryKeys = Object.keys(supportedCountries)
|
|
56
|
-
const {
|
|
57
|
+
const { legend, columns, general } = configObj
|
|
57
58
|
const primaryColName = columns.primary.name
|
|
58
59
|
const isBubble = general.type === 'bubble'
|
|
59
60
|
const categoricalCol = columns.categorical ? columns.categorical.name : undefined
|
|
60
61
|
|
|
62
|
+
// filter out rows without a geo column
|
|
63
|
+
addUIDs(configObj, configObj.columns.geo.name)
|
|
64
|
+
const data = configObj.data.filter(row => row.uid) // Filter out rows without UIDs
|
|
65
|
+
|
|
61
66
|
const result = {
|
|
62
67
|
fromHash: null,
|
|
63
68
|
runtimeDataHash: null,
|
|
@@ -81,69 +86,44 @@ export const generateRuntimeLegend = (
|
|
|
81
86
|
let specialClasses = 0
|
|
82
87
|
let specialClassesHash = {}
|
|
83
88
|
|
|
89
|
+
// Special classes
|
|
84
90
|
if (legend.specialClasses.length) {
|
|
85
91
|
if (typeof legend.specialClasses[0] === 'object') {
|
|
86
92
|
legend.specialClasses.forEach(specialClass => {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
)
|
|
100
|
-
specialClasses += 1
|
|
101
|
-
}
|
|
102
|
-
// Optionally, still map any rows that match this special class
|
|
103
|
-
dataSet.forEach(row => {
|
|
104
|
-
const rowVal = String(row[specialClass.key])
|
|
105
|
-
if (rowVal === val) {
|
|
106
|
-
let specialColor = result.items.findIndex(p => p.value === val)
|
|
107
|
-
newLegendMemo.set(hashObj(row), specialColor)
|
|
108
|
-
}
|
|
109
|
-
})
|
|
110
|
-
})
|
|
111
|
-
} else {
|
|
112
|
-
dataSet = dataSet.filter(row => {
|
|
113
|
-
const val = row[primaryColName]
|
|
114
|
-
|
|
115
|
-
if (legend.specialClasses.includes(val)) {
|
|
116
|
-
// apply the special color to the legend
|
|
117
|
-
if (undefined === specialClassesHash[val]) {
|
|
118
|
-
specialClassesHash[val] = true
|
|
119
|
-
|
|
120
|
-
result.items.push({
|
|
121
|
-
special: true,
|
|
122
|
-
value: val
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
result.items[result.items.length - 1].color = applyColorToLegend(
|
|
126
|
-
result.items.length - 1,
|
|
127
|
-
configObj,
|
|
128
|
-
result.items
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
specialClasses += 1
|
|
132
|
-
}
|
|
93
|
+
dataSet = dataSet.filter(row => {
|
|
94
|
+
const val = String(row[specialClass.key])
|
|
95
|
+
|
|
96
|
+
if (specialClass.value === val) {
|
|
97
|
+
if (undefined === specialClassesHash[val]) {
|
|
98
|
+
specialClassesHash[val] = true
|
|
99
|
+
|
|
100
|
+
result.items.push({
|
|
101
|
+
special: true,
|
|
102
|
+
value: val,
|
|
103
|
+
label: specialClass.label
|
|
104
|
+
})
|
|
133
105
|
|
|
134
|
-
|
|
106
|
+
result.items[result.items.length - 1].color = applyColorToLegend(
|
|
107
|
+
result.items.length - 1,
|
|
108
|
+
configObj,
|
|
109
|
+
result.items
|
|
110
|
+
)
|
|
135
111
|
|
|
136
|
-
|
|
137
|
-
|
|
112
|
+
specialClasses += 1
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
let specialColor: number
|
|
116
|
+
|
|
117
|
+
// color the configObj if val is in row
|
|
138
118
|
specialColor = result.items.findIndex(p => p.value === val)
|
|
139
|
-
}
|
|
140
119
|
|
|
141
|
-
|
|
120
|
+
newLegendMemo.set(hashObj(row), specialColor)
|
|
142
121
|
|
|
143
|
-
|
|
144
|
-
|
|
122
|
+
return false
|
|
123
|
+
}
|
|
145
124
|
|
|
146
|
-
|
|
125
|
+
return true
|
|
126
|
+
})
|
|
147
127
|
})
|
|
148
128
|
}
|
|
149
129
|
}
|
|
@@ -403,6 +383,12 @@ export const generateRuntimeLegend = (
|
|
|
403
383
|
min = 1
|
|
404
384
|
}
|
|
405
385
|
|
|
386
|
+
// For non-first ranges, add small increment to prevent overlap
|
|
387
|
+
if (index > 0 && !legend.separateZero) {
|
|
388
|
+
const decimalPlace = Number(configObj?.columns?.primary?.roundToPlace) || 1
|
|
389
|
+
min = Number(cachedBreaks[index]) + Math.pow(10, -decimalPlace)
|
|
390
|
+
}
|
|
391
|
+
|
|
406
392
|
return min
|
|
407
393
|
}
|
|
408
394
|
|
|
@@ -411,14 +397,14 @@ export const generateRuntimeLegend = (
|
|
|
411
397
|
}
|
|
412
398
|
|
|
413
399
|
const setMax = index => {
|
|
414
|
-
let max = Number(breaks[index + 1])
|
|
400
|
+
let max = Number(breaks[index + 1])
|
|
415
401
|
|
|
416
402
|
if (index === 0 && legend.separateZero) {
|
|
417
403
|
max = 0
|
|
418
404
|
}
|
|
419
405
|
|
|
420
406
|
if (index + 1 === breaks.length) {
|
|
421
|
-
max = domainNums[domainNums.length - 1]
|
|
407
|
+
max = Number(domainNums[domainNums.length - 1])
|
|
422
408
|
}
|
|
423
409
|
|
|
424
410
|
return max
|
|
@@ -443,11 +429,54 @@ export const generateRuntimeLegend = (
|
|
|
443
429
|
|
|
444
430
|
if (result.items?.[updated]?.min === undefined || result.items?.[updated]?.max === undefined) return
|
|
445
431
|
|
|
446
|
-
if
|
|
447
|
-
|
|
432
|
+
// Check if this row hasn't been assigned yet to prevent double assignment
|
|
433
|
+
if (!newLegendMemo.has(hashObj(row))) {
|
|
434
|
+
if (number >= result.items[updated].min && number <= result.items[updated].max) {
|
|
435
|
+
newLegendMemo.set(hashObj(row), updated)
|
|
436
|
+
}
|
|
448
437
|
}
|
|
449
438
|
})
|
|
450
439
|
})
|
|
440
|
+
|
|
441
|
+
// Final pass: handle any unassigned rows
|
|
442
|
+
dataSet.forEach(row => {
|
|
443
|
+
if (!newLegendMemo.has(hashObj(row))) {
|
|
444
|
+
let number = row[columns.primary.name]
|
|
445
|
+
let assigned = false
|
|
446
|
+
|
|
447
|
+
// Find the correct range for this value - check both boundaries
|
|
448
|
+
for (let itemIndex = 0; itemIndex < result.items.length; itemIndex++) {
|
|
449
|
+
const item = result.items[itemIndex]
|
|
450
|
+
|
|
451
|
+
if (item.min === undefined || item.max === undefined) continue
|
|
452
|
+
|
|
453
|
+
// Check if value falls within range (inclusive of both min and max)
|
|
454
|
+
if (number >= item.min && number <= item.max) {
|
|
455
|
+
newLegendMemo.set(hashObj(row), itemIndex)
|
|
456
|
+
assigned = true
|
|
457
|
+
break
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Fallback: if still not assigned, assign to closest range
|
|
462
|
+
if (!assigned) {
|
|
463
|
+
console.warn('Value not assigned to any range:', number, 'assigning to closest range')
|
|
464
|
+
let closestIndex = 0
|
|
465
|
+
let minDistance = Math.abs(number - ((result.items[0].min + result.items[0].max) / 2))
|
|
466
|
+
|
|
467
|
+
for (let i = 1; i < result.items.length; i++) {
|
|
468
|
+
const midpoint = (result.items[i].min + result.items[i].max) / 2
|
|
469
|
+
const distance = Math.abs(number - midpoint)
|
|
470
|
+
if (distance < minDistance) {
|
|
471
|
+
minDistance = distance
|
|
472
|
+
closestIndex = i
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
newLegendMemo.set(hashObj(row), closestIndex)
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
})
|
|
451
480
|
}
|
|
452
481
|
}
|
|
453
482
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { getFilterControllingStatePicked } from '../components/UsaMap/helpers/map'
|
|
2
|
+
import { supportedStatesFipsCodes } from '../data/supported-geos'
|
|
3
|
+
|
|
4
|
+
export const getStatePicked = (config, runtimeData) => {
|
|
5
|
+
const stateName = getFilterControllingStatePicked(config, runtimeData)
|
|
6
|
+
const fipsCode = Object.keys(supportedStatesFipsCodes).find(key => supportedStatesFipsCodes[key] === stateName)
|
|
7
|
+
return { stateName, fipsCode }
|
|
8
|
+
}
|