@cdc/map 2.6.2 → 2.6.4

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 (67) hide show
  1. package/dist/cdcmap.js +37 -29
  2. package/examples/bubble-us.json +363 -0
  3. package/examples/bubble-world.json +427 -0
  4. package/examples/default-county.json +105 -0
  5. package/examples/default-hex.json +475 -0
  6. package/examples/default-single-state.json +109 -0
  7. package/examples/default-usa-regions.json +118 -0
  8. package/examples/default-usa.json +744 -603
  9. package/examples/default-world-data.json +1450 -0
  10. package/examples/default-world.json +5 -3
  11. package/examples/example-city-state.json +510 -0
  12. package/examples/example-world-map.json +1596 -0
  13. package/examples/gender-rate-map.json +1 -0
  14. package/examples/private/atsdr.json +439 -0
  15. package/examples/private/atsdr_new.json +436 -0
  16. package/examples/private/bubble.json +285 -0
  17. package/examples/private/default-world-data.json +1444 -0
  18. package/examples/private/default.json +968 -0
  19. package/examples/private/map.csv +60 -0
  20. package/examples/private/mdx.json +210 -0
  21. package/examples/private/regions.json +52 -0
  22. package/examples/private/wcmsrd-13881-data.json +2858 -0
  23. package/examples/private/wcmsrd-13881.json +5823 -0
  24. package/examples/private/wcmsrd-test.json +268 -0
  25. package/examples/private/world.json +1580 -0
  26. package/examples/private/worldmap.json +1490 -0
  27. package/package.json +11 -7
  28. package/src/CdcMap.js +726 -202
  29. package/src/components/BubbleList.js +240 -0
  30. package/src/components/CityList.js +22 -3
  31. package/src/components/CountyMap.js +557 -0
  32. package/src/components/DataTable.js +85 -24
  33. package/src/components/EditorPanel.js +2455 -1204
  34. package/src/components/Geo.js +1 -1
  35. package/src/components/Sidebar.js +5 -5
  36. package/src/components/SingleStateMap.js +326 -0
  37. package/src/components/UsaMap.js +41 -9
  38. package/src/components/UsaRegionMap.js +319 -0
  39. package/src/components/WorldMap.js +112 -35
  40. package/src/data/abbreviations.js +57 -0
  41. package/src/data/country-coordinates.js +250 -0
  42. package/src/data/county-map-halfquality.json +58453 -0
  43. package/src/data/county-map-quarterquality.json +1 -0
  44. package/src/data/county-topo.json +1 -0
  45. package/src/data/dfc-map.json +1 -0
  46. package/src/data/initial-state.js +21 -4
  47. package/src/data/newtest.json +1 -0
  48. package/src/data/state-abbreviations.js +60 -0
  49. package/src/data/state-coordinates.js +55 -0
  50. package/src/data/supported-geos.js +3592 -163
  51. package/src/data/test.json +1 -0
  52. package/src/data/us-regions-topo-2.json +360525 -0
  53. package/src/data/us-regions-topo.json +37894 -0
  54. package/src/hooks/useActiveElement.js +19 -0
  55. package/src/hooks/useColorPalette.ts +96 -0
  56. package/src/index.html +35 -20
  57. package/src/index.js +8 -4
  58. package/src/scss/datatable.scss +2 -1
  59. package/src/scss/editor-panel.scss +76 -55
  60. package/src/scss/main.scss +10 -1
  61. package/src/scss/map.scss +257 -121
  62. package/src/scss/sidebar.scss +0 -1
  63. package/uploads/upload-example-city-state.json +392 -0
  64. package/uploads/upload-example-world-data.json +1490 -0
  65. package/LICENSE +0 -201
  66. package/src/data/color-palettes.js +0 -191
  67. package/src/images/map-folded.svg +0 -1
@@ -0,0 +1,240 @@
1
+ import React, {memo, useState, useEffect} from 'react'
2
+ import { scaleLinear } from 'd3-scale';
3
+ import { countryCoordinates } from '../data/country-coordinates';
4
+ import stateCoordinates from '../data/state-coordinates';
5
+
6
+ export const BubbleList = (
7
+ {
8
+ data: dataImport,
9
+ state,
10
+ projection,
11
+ applyLegendToRow,
12
+ applyTooltipsToGeo,
13
+ handleCircleClick,
14
+ runtimeData,
15
+ displayGeoName
16
+ }) => {
17
+
18
+
19
+ const maxDataValue = Math.max(...dataImport.map(d => d[state.columns.primary.name]))
20
+ const hasBubblesWithZeroOnMap = state.visual.showBubbleZeros ? 0 : 1;
21
+ // sort runtime data. Smaller bubbles should appear on top.
22
+ const sortedRuntimeData = Object.values(runtimeData).sort((a, b) => a[state.columns.primary.name] < b[state.columns.primary.name] ? 1 : -1 )
23
+ if(!sortedRuntimeData) return;
24
+
25
+ const clickTolerance = 10;
26
+
27
+ // Set bubble sizes
28
+ var size = scaleLinear()
29
+ .domain([hasBubblesWithZeroOnMap, maxDataValue])
30
+ .range([state.visual.minBubbleSize, state.visual.maxBubbleSize])
31
+
32
+ // Start looping through the countries to create the bubbles.
33
+ if(state.general.geoType === 'world') {
34
+ const countries = sortedRuntimeData && sortedRuntimeData.map( (country, index) => {
35
+
36
+ let coordinates = countryCoordinates[country.uid]
37
+
38
+ if(!coordinates) return true;
39
+
40
+ const countryName = displayGeoName(country[state.columns.geo.name]);
41
+ const toolTip = applyTooltipsToGeo(countryName, country);
42
+ const legendColors = applyLegendToRow(country);
43
+
44
+ let primaryKey = state.columns.primary.name
45
+ if ((Math.floor(Number(size(country[primaryKey]))) === 0 || country[primaryKey] === "") && !state.visual.showBubbleZeros) return;
46
+
47
+ let transform = `translate(${projection([coordinates[1], coordinates[0]])})`
48
+
49
+ let pointerX, pointerY;
50
+
51
+ if( !projection(coordinates) ) return true;
52
+
53
+ const circle = (
54
+ <>
55
+ <circle
56
+ key={`circle-${countryName.replace(' ', '')}`}
57
+ data-tip={toolTip}
58
+ data-for="tooltip"
59
+ className="bubble"
60
+ cx={ Number(projection(coordinates[1], coordinates[0])[0]) || 0 } // || 0 handles error on loads where the data isn't ready
61
+ cy={ Number(projection(coordinates[1], coordinates[0])[1]) || 0 }
62
+ r={ Number(size(country[primaryKey])) }
63
+ fill={legendColors[0] }
64
+ stroke={legendColors[0]}
65
+ strokeWidth={1.25}
66
+ fillOpacity={.4}
67
+ onPointerDown={(e) => {
68
+ pointerX = e.clientX;
69
+ pointerY = e.clientY;
70
+ }}
71
+ onPointerUp={(e) => {
72
+ if(pointerX && pointerY &&
73
+ e.clientX > (pointerX - clickTolerance) &&
74
+ e.clientX < (pointerX + clickTolerance) &&
75
+ e.clientY > (pointerY - clickTolerance) &&
76
+ e.clientY < (pointerY + clickTolerance)
77
+ ){
78
+ handleCircleClick(country)
79
+ pointerX = undefined;
80
+ pointerY = undefined;
81
+ }
82
+ }}
83
+ transform={transform}
84
+ style={{ transition: 'all .25s ease-in-out', cursor: "pointer" }}
85
+ />
86
+
87
+ {state.visual.extraBubbleBorder &&
88
+ <circle
89
+ key={`circle-${countryName.replace(' ', '')}`}
90
+ data-tip={toolTip}
91
+ data-for="tooltip"
92
+ className="bubble"
93
+ cx={ Number(projection(coordinates[1], coordinates[0])[0]) || 0 } // || 0 handles error on loads where the data isn't ready
94
+ cy={ Number(projection(coordinates[1], coordinates[0])[1]) || 0 }
95
+ r={ Number(size(country[primaryKey])) + 1 }
96
+ fill={ "transparent" }
97
+ stroke={ "white" }
98
+ strokeWidth={.5}
99
+ onPointerDown={(e) => {
100
+ pointerX = e.clientX;
101
+ pointerY = e.clientY;
102
+ }}
103
+ onPointerUp={(e) => {
104
+ if(pointerX && pointerY &&
105
+ e.clientX > (pointerX - clickTolerance) &&
106
+ e.clientX < (pointerX + clickTolerance) &&
107
+ e.clientY > (pointerY - clickTolerance) &&
108
+ e.clientY < (pointerY + clickTolerance)
109
+ ){
110
+ handleCircleClick(country)
111
+ pointerX = undefined;
112
+ pointerY = undefined;
113
+ }
114
+ }}
115
+ transform={transform}
116
+ style={{ transition: 'all .25s ease-in-out', cursor: "pointer" }}
117
+ />
118
+ }
119
+ </>
120
+ );
121
+
122
+
123
+ return (
124
+ <g key={`group-${countryName.replace(' ', '')}`}>
125
+ {circle}
126
+ </g>
127
+ )
128
+ })
129
+ return countries;
130
+ }
131
+
132
+ if(state.general.geoType === 'us') {
133
+ const bubbles = sortedRuntimeData && sortedRuntimeData.map( (item, index) => {
134
+ let stateData = stateCoordinates[item.uid]
135
+ let primaryKey = state?.columns?.primary?.name
136
+ if ( Number(size(item[primaryKey])) === 0) return;
137
+
138
+ if (item[primaryKey] === null) item[primaryKey] = ""
139
+
140
+ // Return if hiding zeros on the map
141
+ if( (Math.floor(Number(size(item[primaryKey]))) === 0 || item[primaryKey] === "" )&& !state.visual.showBubbleZeros ) return;
142
+
143
+ if(!stateData) return true;
144
+ let longitude = Number( stateData.Longitude);
145
+ let latitude = Number( stateData.Latitude);
146
+ let coordinates = [longitude, latitude]
147
+ //console.log('projection', projection([longitude, latitude]))
148
+ let stateName = stateData.Name;
149
+ if (!coordinates) return true;
150
+
151
+ stateName = displayGeoName(stateName);
152
+ const toolTip = applyTooltipsToGeo(stateName, item);
153
+ const legendColors = applyLegendToRow(item);
154
+
155
+
156
+ let transform = `translate(${projection([coordinates[1], coordinates[0]])})`
157
+
158
+ if ( !projection(coordinates) ) return true;
159
+
160
+ let pointerX, pointerY;
161
+ const circle = (
162
+ <>
163
+ <circle
164
+ key={`circle-${stateName.replace(' ', '')}`}
165
+ data-tip={toolTip}
166
+ data-for="tooltip"
167
+ className="bubble"
168
+ cx={projection(coordinates)[0] || 0} // || 0 handles error on loads where the data isn't ready
169
+ cy={projection(coordinates)[1] || 0}
170
+ r={Number(size(item[primaryKey]))}
171
+ fill={legendColors[0]}
172
+ stroke={legendColors[0]}
173
+ strokeWidth={1.25}
174
+ fillOpacity={.4}
175
+ onPointerDown={(e) => {
176
+ pointerX = e.clientX;
177
+ pointerY = e.clientY;
178
+ }}
179
+ onPointerUp={(e) => {
180
+ if (pointerX && pointerY &&
181
+ e.clientX > (pointerX - clickTolerance) &&
182
+ e.clientX < (pointerX + clickTolerance) &&
183
+ e.clientY > (pointerY - clickTolerance) &&
184
+ e.clientY < (pointerY + clickTolerance)
185
+ ) {
186
+ handleCircleClick(state)
187
+ pointerX = undefined;
188
+ pointerY = undefined;
189
+ }
190
+ }}
191
+ transform={transform}
192
+ style={{ transition: 'all .25s ease-in-out', cursor: "pointer" }}
193
+ />
194
+ { state.visual.extraBubbleBorder &&
195
+ <circle
196
+ key={`circle-${stateName.replace(' ', '')}`}
197
+ data-tip={toolTip}
198
+ data-for="tooltip"
199
+ className="bubble"
200
+ cx={projection(coordinates)[0] || 0} // || 0 handles error on loads where the data isn't ready
201
+ cy={projection(coordinates)[1] || 0}
202
+ r={Number(size(item[primaryKey]) + 1)}
203
+ fill={"transparent"}
204
+ stroke={"white"}
205
+ strokeWidth={.5}
206
+ fillOpacity={.4}
207
+ onPointerDown={(e) => {
208
+ pointerX = e.clientX;
209
+ pointerY = e.clientY;
210
+ }}
211
+ onPointerUp={(e) => {
212
+ if (pointerX && pointerY &&
213
+ e.clientX > (pointerX - clickTolerance) &&
214
+ e.clientX < (pointerX + clickTolerance) &&
215
+ e.clientY > (pointerY - clickTolerance) &&
216
+ e.clientY < (pointerY + clickTolerance)
217
+ ) {
218
+ handleCircleClick(state)
219
+ pointerX = undefined;
220
+ pointerY = undefined;
221
+ }
222
+ }}
223
+ transform={transform}
224
+ style={{ transition: 'all .25s ease-in-out', cursor: "pointer" }}
225
+ />
226
+ }
227
+ </>
228
+ );
229
+
230
+
231
+ return (
232
+ <g key={`group-${stateName.replace(' ', '')}`}>
233
+ {circle}
234
+ </g>
235
+ )
236
+ })
237
+ return bubbles;
238
+ }
239
+ }
240
+ export default memo(BubbleList)
@@ -2,6 +2,7 @@ import React, { useState, useEffect, useContext } from 'react';
2
2
  /** @jsx jsx */
3
3
  import { jsx } from '@emotion/react'
4
4
  import { supportedCities } from '../data/supported-geos';
5
+ import { scaleLinear } from 'd3-scale';
5
6
 
6
7
  const CityList = (({
7
8
  data,
@@ -10,7 +11,8 @@ const CityList = (({
10
11
  applyTooltipsToGeo,
11
12
  displayGeoName,
12
13
  applyLegendToRow,
13
- projection
14
+ projection,
15
+ titleCase
14
16
  }) => {
15
17
  const [citiesData, setCitiesData] = useState({});
16
18
 
@@ -24,10 +26,22 @@ const CityList = (({
24
26
  setCitiesData(citiesDictionary);
25
27
  }, [data]);
26
28
 
29
+ if (state.general.type === 'bubble') {
30
+ const maxDataValue = Math.max(...state.data.map(d => d[state.columns.primary.name]))
31
+ const sortedRuntimeData = Object.values(data).sort((a, b) => a[state.columns.primary.name] < b[state.columns.primary.name] ? 1 : -1)
32
+ if (!sortedRuntimeData) return;
33
+
34
+ // Set bubble sizes
35
+ var size = scaleLinear()
36
+ .domain([1, maxDataValue])
37
+ .range([state.visual.minBubbleSize, state.visual.maxBubbleSize])
38
+
39
+ }
40
+
27
41
  const cityList = Object.keys(citiesData).filter((c) => undefined !== data[c]);
28
42
 
29
43
  const cities = cityList.map((city, i) => {
30
- const cityDisplayName = displayGeoName(city);
44
+ const cityDisplayName = titleCase( displayGeoName(city) );
31
45
 
32
46
  const legendColors = applyLegendToRow(data[city]);
33
47
 
@@ -60,15 +74,20 @@ const CityList = (({
60
74
 
61
75
  const radius = state.general.geoType === 'us' ? 8 : 4;
62
76
 
77
+ const additionalProps = {
78
+ fillOpacity: state.general.type === 'bubble' ? .4 : 1
79
+ }
80
+
63
81
  const circle = (
64
82
  <circle
65
83
  data-tip={toolTip}
66
84
  data-for="tooltip"
67
85
  cx={0}
68
86
  cy={0}
69
- r={radius}
87
+ r={ state.general.type === 'bubble' ? size(geoData[state.columns.primary.name]) : radius}
70
88
  title="Click for more information"
71
89
  onClick={() => geoClickHandler(cityDisplayName, geoData)}
90
+ {...additionalProps}
72
91
  />
73
92
  );
74
93