Skip to content

Commit 58baec7

Browse files
authored
Merge pull request #900 from cmu-delphi/sgratzl/hhs_nation
Add nation/hhs support in COVIDcast classic
2 parents 6a2e4fd + a58b6c1 commit 58baec7

25 files changed

+173
-39
lines changed

src/components/MapBox/AMapBoxWrapper.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ export default class AMapBoxWrapper {
133133
}
134134

135135
_setupReady() {
136-
this.zoom.showStateLabels(this.level === 'state');
136+
this.zoom.showStateLabels(this.level === 'state' || this.level === 'hhs' || this.level === 'nation');
137137
this.zoom.ready();
138138
this.markReady('setup');
139139
}
@@ -338,7 +338,7 @@ export default class AMapBoxWrapper {
338338
}
339339

340340
if (oldLevel !== level) {
341-
this.zoom.showStateLabels(level === 'state');
341+
this.zoom.showStateLabels(level === 'state' || level === 'hhs' || level === 'nation');
342342
}
343343

344344
const allEncodingLayers = this.getAllEncodingLayers();

src/data/fetchData.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,9 @@ export function fetchData(
142142
if (!region) {
143143
return Promise.resolve([]);
144144
}
145+
mixinValues.time_type = 'day'; // inject time_type
146+
mixinValues.geo_type = level; // inject geo_type
147+
145148
const transferFields = computeTransferFields(mixinValues, advanced, transferSignal);
146149
function fetchSeparate(defaultSignalIndex) {
147150
const extraDataFields = ['value'];
@@ -354,7 +357,7 @@ export function addMissing(rows, dataSensor) {
354357

355358
export function addNameInfos(rows) {
356359
for (const row of rows) {
357-
Object.assign(row, getInfoByName(row.geo_value));
360+
Object.assign(row, getInfoByName(row.geo_value, row.geo_type));
358361
}
359362
return rows;
360363
}

src/maps/geo.js

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import stateTopoJSON from './processed/state.topojson.json';
33
import countyTopoJSON from './processed/county.topojson.json';
44
import msaTopoJSON from './processed/msa.topojson.json';
55
import hrrTopoJSON from './processed/hrr.topojson.json';
6+
import hhsTopoJSON from './processed/hhs.topojson.json';
7+
import nationTopoJSON from './processed/nation.topojson.json';
68
import citiesRaw from './processed/cities.csv.js';
79
import { generateGeo } from './utils';
810

@@ -44,25 +46,48 @@ function citiesGeo() {
4446

4547
/**
4648
* loads all geo json sources
49+
* @param {import('.').NameInfo} nationInfo
4750
* @param {import('.').NameInfo[]} stateInfo
4851
* @param {import('.').NameInfo[]} countyInfo
4952
* @param {import('.').NameInfo[]} msaInfo
5053
* @param {import('.').NameInfo[]} hrrInfo
54+
* @param {import('.').NameInfo[]} hhsInfo
5155
* @param {string} megaLevel
5256
* @param {*} additionalProperties
5357
*/
54-
export default function load(stateInfo, countyInfo, msaInfo, hrrInfo, megaLevel, additionalProperties = {}) {
58+
export default function load(
59+
nationInfo,
60+
stateInfo,
61+
countyInfo,
62+
msaInfo,
63+
hrrInfo,
64+
hhsInfo,
65+
megaLevel,
66+
additionalProperties = {},
67+
) {
68+
const nation = generateGeo(nationTopoJSON, 'nation', [nationInfo], additionalProperties);
5569
const state = generateGeo(stateTopoJSON, 'state', stateInfo, additionalProperties);
5670
const county = generateGeo(countyTopoJSON, 'county', countyInfo, additionalProperties);
5771
const msa = generateGeo(msaTopoJSON, 'msa', msaInfo, additionalProperties);
5872
const hrr = generateGeo(hrrTopoJSON, 'hrr', hrrInfo, additionalProperties);
59-
const mega = deriveMegaGeo(state.border, megaLevel);
6073
const cities = citiesGeo();
74+
const hhs = generateGeo(hhsTopoJSON, 'hhs', hhsInfo, additionalProperties);
75+
// inject city information
76+
const byCityName = new Map(cities.features.map((d) => [d.properties.name, d.geometry]));
77+
hhs.center.features.forEach((f) => {
78+
f.geometry = byCityName.get(f.properties.name);
79+
});
80+
nation.center.features.forEach((f) => {
81+
f.geometry = byCityName.get('Washington');
82+
});
83+
const mega = deriveMegaGeo(state.border, megaLevel);
6184
return {
6285
state,
86+
nation,
6387
county,
6488
msa,
6589
hrr,
90+
hhs,
6691
mega,
6792
cities,
6893
};

src/maps/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
export * from './infos';
2-
import { stateInfo, countyInfo, msaInfo, hrrInfo, levelMegaCountyId } from './infos';
2+
import { nationInfo, stateInfo, countyInfo, msaInfo, hrrInfo, levelMegaCountyId, hhsInfo } from './infos';
33

44
export function loadSources(additionalProperties = {}) {
55
// mark to be loaded as fast as possible
66
return import(/* webpackChunkName: 'geo' */ './geo').then((r) =>
7-
r.default(stateInfo, countyInfo, msaInfo, hrrInfo, levelMegaCountyId, additionalProperties),
7+
r.default(nationInfo, stateInfo, countyInfo, msaInfo, hrrInfo, hhsInfo, levelMegaCountyId, additionalProperties),
88
);
99
}

src/maps/infos.js

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import stateRaw from './processed/state.csv.js';
33
import msaRaw from './processed/msa.csv.js';
44
import countyRaw from './processed/county.csv.js';
55
import hrrRaw from './processed/hrr.csv.js';
6+
import hhsRaw from './processed/hhs.csv.js';
67

78
export const levelMegaCountyId = 'mega-county';
89
/**
@@ -14,7 +15,7 @@ export const levelMegaCountyId = 'mega-county';
1415
* @property {number} population
1516
* @property {string?} region just for state and county
1617
* @property {string?} state just for county
17-
* @property {'state' | 'county' | 'msa' | 'hrr' | 'nation'} level
18+
* @property {'state' | 'county' | 'msa' | 'hrr' | 'nation' | 'hhs'} level
1819
*/
1920

2021
function parseCSV(csv, level, deriveDisplayName = (d) => d.name, extras = () => undefined) {
@@ -62,9 +63,9 @@ Object.entries(stateClasses).forEach(([key, value]) => {
6263
export const nationInfo = {
6364
level: 'nation',
6465
name: 'US',
65-
id: 'us',
66+
id: '0', // has to be an integer
6667
displayName: 'United States',
67-
propertyId: 'us',
68+
propertyId: 'US',
6869
population: stateInfo.reduce((acc, v) => acc + v.population, 0),
6970
};
7071

@@ -81,6 +82,16 @@ export const countyInfo = parseCSV(
8182
},
8283
);
8384
export const hrrInfo = parseCSV(hrrRaw, 'hrr', (hrr) => `${hrr.state} - ${hrr.name} (HRR)`);
85+
export const hhsInfo = parseCSV(
86+
hhsRaw,
87+
'hhs',
88+
(hhs) => `HHS Region ${hhs.id.length < 2 ? ' ' : ''}${hhs.id} ${hhs.name}`,
89+
(hhs) => {
90+
// hhs.propertyId = 'h' + hhs.propertyId; // HACK since HHS and HRR ids are the same
91+
hhs.states = hhs.states.split(',');
92+
hhs.population = hhs.states.reduce((acc, v) => acc + stateLookup.get(v.toLowerCase()).population, 0);
93+
},
94+
);
8495

8596
// generate mega counties by copying the states
8697
/**
@@ -109,10 +120,13 @@ export const infosByLevel = {
109120
msa: msaInfo.sort(sortByDisplayName),
110121
county: countyInfo.sort(sortByDisplayName),
111122
hrr: hrrInfo.sort(sortByDisplayName),
123+
hhs: hhsInfo.sort(sortByDisplayName),
112124
[levelMegaCountyId]: megaCountyInfo.sort(sortByDisplayName),
113125
};
114126

115-
export const nameInfos = stateInfo.concat(msaInfo, countyInfo, hrrInfo, megaCountyInfo).sort(sortByDisplayName);
127+
export const nameInfos = stateInfo
128+
.concat(msaInfo, countyInfo, hrrInfo, megaCountyInfo, hhsInfo)
129+
.sort(sortByDisplayName);
116130

117131
/**
118132
* helper to resolve a given id to a name info object
@@ -167,7 +181,7 @@ export function computeMegaCountyPopulation(megaCounty, data) {
167181
if (!megaCounty || !data || megaCounty.level !== levelMegaCountyId) {
168182
return null;
169183
}
170-
const state = getInfoByName(megaCounty.postal);
184+
const state = getInfoByName(megaCounty.postal, 'state');
171185
if (!state || state.population == null || Number.isNaN(state.population)) {
172186
return null;
173187
}
@@ -176,7 +190,7 @@ export function computeMegaCountyPopulation(megaCounty, data) {
176190
if (!fips.startsWith(state.id) || fips === megaCounty.id) {
177191
return population;
178192
}
179-
const county = getInfoByName(fips);
193+
const county = getInfoByName(fips, 'county');
180194
if (!county || county.population == null || Number.isNaN(county.population)) {
181195
// invalid county, so we cannot compute the rest population, keep NaN from now on
182196
return Number.NaN;
@@ -199,5 +213,17 @@ export function getCountiesOfState(state) {
199213
* @param {NameInfo} county
200214
*/
201215
export function getStateOfCounty(county) {
202-
return getInfoByName(county.state);
216+
return getInfoByName(county.state, 'state');
217+
}
218+
219+
/**
220+
* returns the states of a HHS Region
221+
* @param {NameInfo} hhs
222+
* @returns {NameInfo[]}
223+
*/
224+
export function getStatesOfHHS(hhs) {
225+
if (hhs.level !== 'hhs') {
226+
return [];
227+
}
228+
return hhs.states.map((d) => getInfoByName(d, 'state'));
203229
}

src/maps/processed/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
3+
## create a hhs shape
4+
```sh
5+
mapshaper -each 'hhs_region={"10":"3","11":"3","12":"4","13":"4","15":"9","16":"10","17":"5","18":"5","19":"7","20":"7","21":"4","22":"6","23":"1","24":"3","25":"1","26":"5","27":"5","28":"4","29":"7","30":"8","31":"7","32":"9","33":"1","34":"2","35":"6","36":"2","37":"4","38":"8","39":"5","40":"6","41":"10","42":"3","44":"1","45":"4","46":"8","47":"4","48":"6","49":"8","50":"1","51":"3","53":"10","54":"3","55":"5","56":"8","66":"9","69":"9","72":"2","78":"2","09":"1","01":"4","05":"6","08":"8","04":"9","06":"9","03":"9","02":"10"}[FID]'
6+
mapshaper -dissolve2 fields=hhs_region name=hhs target=state no-replace
7+
mapshaper -rename-fields fields=id=hhs_region target=hhs
8+
mapshaper -o format=topojson id-field=id target=hhs hhs.json
9+
```
10+
11+
## create nation shape

src/maps/processed/cities.csv.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,16 @@ Tampa;352957;12.430601855786824;-9.90637544591033
5555
Honolulu;347884;-9.720042064765895;-10.699587876299114
5656
Aurora;345803;-6.256241729058357;1.3501325160987412
5757
Anaheim;345012;-17.44631758048322;-2.835422990685346
58-
Santa Ana;334227;-17.429463225258868;-2.932081574058002
58+
Santa Ana;334227;-17.42946322
59+
5258868;-2.
60+
9 3208157405
61+
8002
62+
63+
64+
65+
66+
,
67+
5968
St. Louis;318416;4.948763957775884;0.06173242686326162
6069
Riverside;316619;-17.003408942240277;-2.816055278035148
6170
Corpus Christi;316381;-0.7764816986382742;-10.884368813608244
@@ -69,8 +78,8 @@ Toledo;282313;9.66449259343711;3.5897322100102844
6978
Greensboro;279639;13.380582796400157;-1.5500642996573628
7079
Newark;278427;16.718816263365177;3.9247991944093226
7180
Plano;274409;-0.11856273421631199;-5.719959634246842
72-
Henderson;270811;-14.642925536757707;-1.174082341335054
73-
Lincoln;268738;-0.05038192788261643;2.1452863390058377
81+
Henderson;270811;-14.642925536757;;707;-1.174082341335054
82+
Linc;;oln;268738;-0.05038192788261643;2.1452863390058377
7483
Buffalo;258959;12.869383964264998;5.332309494912177
7584
Jersey City;257342;16.78983467007687;3.9336499695491174
7685
Chula Vista;256780;-17.043846156449305;-4.16014310599635

src/maps/processed/hhs.csv.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export default `id;name;states
2+
1;Boston;CT,ME,MA,NH,RI,VT
3+
2;New York;NJ,NY,PR,VI
4+
3;Philadelphia;DE,DC,MD,PA,VA,WV
5+
4;Atlanta;AL,FL,GA,KY,MS,NC,SC,TN
6+
5;Chicago;IL,IN,MI,MN,OH,WI
7+
6;Dallas;AR,LA,NM,OK,TX
8+
7;Kansas City;IA,KS,MO,NE
9+
8;Denver;CO,MT,ND,SD,UT,WY
10+
9;San Francisco;AZ,CA,HI,NV,AS,MP,GU
11+
10;Seattle;AK,ID,OR,WA`;

src/maps/processed/hhs.topojson.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

src/maps/processed/nation.topojson.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)