@dra2020/dra-types 1.4.7 → 1.4.10
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/all.d.ts +4 -0
- package/dist/colordata.d.ts +35 -0
- package/dist/csv.d.ts +40 -0
- package/dist/dra-types.d.ts +0 -58
- package/dist/dra-types.js +477 -78
- package/dist/dra-types.js.map +1 -1
- package/dist/gencolor.d.ts +1 -0
- package/dist/vfeature.d.ts +20 -0
- package/lib/all.ts +4 -0
- package/lib/bucketmap.ts +5 -0
- package/lib/colordata.ts +321 -0
- package/lib/csv.ts +515 -0
- package/lib/dra-types.ts +0 -592
- package/lib/gencolor.ts +21 -0
- package/lib/vfeature.ts +105 -0
- package/package.json +1 -1
package/lib/dra-types.ts
CHANGED
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
// Public libraries
|
|
2
|
-
import * as Hash from 'object-hash';
|
|
3
|
-
import * as Util from '@dra2020/util';
|
|
4
|
-
|
|
5
|
-
// Used internally to index into District Properties Array
|
|
6
|
-
export type BlockMap = { [id: string]: number };
|
|
7
|
-
|
|
8
|
-
// Used more generically and allows string districtIDs
|
|
9
|
-
export type BlockMapping = { [id: string]: string };
|
|
10
|
-
|
|
11
1
|
// Type for single comment
|
|
12
2
|
export interface Comment
|
|
13
3
|
{
|
|
@@ -47,585 +37,3 @@ export interface UserLikes
|
|
|
47
37
|
id?: string;
|
|
48
38
|
[aid: string]: Like | string; // Really just Like but make TypeScript happy
|
|
49
39
|
}
|
|
50
|
-
|
|
51
|
-
export interface SplitBlock
|
|
52
|
-
{
|
|
53
|
-
id?: string;
|
|
54
|
-
chunkKey?: string;
|
|
55
|
-
chunk?: string;
|
|
56
|
-
state: string;
|
|
57
|
-
datasource: string;
|
|
58
|
-
geoid: string;
|
|
59
|
-
blocks: string[];
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export type DistrictToSplitBlock = { [districtID: string]: SplitBlock[] };
|
|
63
|
-
|
|
64
|
-
// Canonical hashing of splitblock data
|
|
65
|
-
function hash(o: any): string
|
|
66
|
-
{
|
|
67
|
-
return Hash(o,
|
|
68
|
-
{ respectType: false,
|
|
69
|
-
unorderedArrays: true,
|
|
70
|
-
unorderedObjects: true,
|
|
71
|
-
excludeKeys: (k: string) => (k === 'id' || k === 'chunk')
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function vgeoidToGeoid(vgeoid: string): string
|
|
76
|
-
{
|
|
77
|
-
let re = /vfeature_([^_]*)_.*/;
|
|
78
|
-
let a = re.exec(vgeoid);
|
|
79
|
-
if (a == null || a.length != 2)
|
|
80
|
-
return '';
|
|
81
|
-
else
|
|
82
|
-
return a[1];
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export function vgeoidToChunk(vgeoid: string): string
|
|
86
|
-
{
|
|
87
|
-
// vgeoid is string of form: "vfeature_[geoid]_[chunkid]_[hash]"
|
|
88
|
-
// the contents are chunked into a file of form "vfeature_chunk_[chunkid]"
|
|
89
|
-
// So extract the chunk ID and download that.
|
|
90
|
-
let re = /vfeature_([^_]*)_([^_*])_(.*)/;
|
|
91
|
-
let a = re.exec(vgeoid);
|
|
92
|
-
if (a && a.length == 4)
|
|
93
|
-
vgeoid = `vfeature_chunk_${a[2]}`;
|
|
94
|
-
else
|
|
95
|
-
vgeoid = null;
|
|
96
|
-
|
|
97
|
-
return vgeoid;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export function vgeoidToHash(vgeoid: string): string
|
|
101
|
-
{
|
|
102
|
-
// vgeoid is string of form: "vfeature_[geoid]_[chunkid]_[hash]"
|
|
103
|
-
let re = /vfeature_([^_]*)_([^_*])_(.*)/;
|
|
104
|
-
let a = re.exec(vgeoid);
|
|
105
|
-
if (a && a.length == 4)
|
|
106
|
-
vgeoid = a[3];
|
|
107
|
-
else
|
|
108
|
-
vgeoid = null;
|
|
109
|
-
|
|
110
|
-
return vgeoid;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export function isVfeature(geoid: string): boolean
|
|
114
|
-
{
|
|
115
|
-
return geoid.indexOf('vfeature') === 0;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export function splitToCacheKey(s: SplitBlock): string
|
|
119
|
-
{
|
|
120
|
-
if (s.id === undefined)
|
|
121
|
-
s.id = hash(s);
|
|
122
|
-
if (s.chunk === undefined)
|
|
123
|
-
s.chunk = "0";
|
|
124
|
-
|
|
125
|
-
return `_${s.state}_${s.datasource}_vfeature_${s.geoid}_${s.chunk}_${s.id}.geojson`;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export function splitToChunkKey(s: SplitBlock): string
|
|
129
|
-
{
|
|
130
|
-
if (s.chunk === undefined)
|
|
131
|
-
s.chunk = "0";
|
|
132
|
-
|
|
133
|
-
return `_${s.state}_${s.datasource}_vfeature_chunk_${s.chunk}.geojson`;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
export function splitToPrefix(s: SplitBlock): string
|
|
137
|
-
{
|
|
138
|
-
if (s.blocks === undefined)
|
|
139
|
-
{
|
|
140
|
-
let re = /_([^_]*)_(.*)_vfeature.*\.geojson$/;
|
|
141
|
-
let a = re.exec(s.id);
|
|
142
|
-
if (a && a.length == 3)
|
|
143
|
-
return `_${a[1]}_${a[2]}`;
|
|
144
|
-
return s.id;
|
|
145
|
-
}
|
|
146
|
-
return `_${s.state}_${s.datasource}`;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export function cacheKeysToChunkHash(keys: string[]): string
|
|
150
|
-
{
|
|
151
|
-
return hash(keys);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
let reNumeric = /^(\D*)(\d*)(\D*)$/;
|
|
155
|
-
let reDistrictNumber = /^\d+$/;
|
|
156
|
-
let reDistrictNumeric = /^\d/;
|
|
157
|
-
|
|
158
|
-
// Normalize any numeric part to have no padded leading zeros
|
|
159
|
-
export function canonicalDistrictID(districtID: string): string
|
|
160
|
-
{
|
|
161
|
-
let a = reNumeric.exec(districtID);
|
|
162
|
-
if (a && a.length == 4)
|
|
163
|
-
{
|
|
164
|
-
if (a[2].length > 0)
|
|
165
|
-
a[2] = String(Number(a[2]));
|
|
166
|
-
districtID = `${a[1]}${a[2]}${a[3]}`;
|
|
167
|
-
}
|
|
168
|
-
return districtID;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Normalize any numeric part to have four digits with padded leading zeros
|
|
172
|
-
export function canonicalSortingDistrictID(districtID: string): string
|
|
173
|
-
{
|
|
174
|
-
let a = reNumeric.exec(districtID);
|
|
175
|
-
if (a && a.length == 4)
|
|
176
|
-
{
|
|
177
|
-
let s = a[2];
|
|
178
|
-
if (s.length > 0)
|
|
179
|
-
{
|
|
180
|
-
switch (s.length)
|
|
181
|
-
{
|
|
182
|
-
case 1: s = `000${s}`; break;
|
|
183
|
-
case 2: s = `00${s}`; break;
|
|
184
|
-
case 3: s = `0${s}`; break;
|
|
185
|
-
}
|
|
186
|
-
a[2] = s;
|
|
187
|
-
}
|
|
188
|
-
districtID = `${a[1]}${a[2]}${a[3]}`;
|
|
189
|
-
}
|
|
190
|
-
return districtID;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// Return numeric part of districtID (or -1 if there is none)
|
|
194
|
-
export function canonicalNumericFromDistrictID(districtID: string): number
|
|
195
|
-
{
|
|
196
|
-
let a = reNumeric.exec(districtID);
|
|
197
|
-
if (a && a.length == 4)
|
|
198
|
-
{
|
|
199
|
-
let s = a[2];
|
|
200
|
-
if (s.length > 0)
|
|
201
|
-
return Number(s);
|
|
202
|
-
}
|
|
203
|
-
return -1;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
export function canonicalDistrictIDFromNumber(districtID: string, n: number): string
|
|
207
|
-
{
|
|
208
|
-
let a = reNumeric.exec(districtID);
|
|
209
|
-
if (a && a.length == 4)
|
|
210
|
-
{
|
|
211
|
-
a[2] = String(n);
|
|
212
|
-
districtID = `${a[1]}${a[2]}${a[3]}`;
|
|
213
|
-
}
|
|
214
|
-
else
|
|
215
|
-
districtID = String(n);
|
|
216
|
-
return districtID;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Numbers start at 1
|
|
220
|
-
export type DistrictOrder = { [districtID: string]: number };
|
|
221
|
-
|
|
222
|
-
export function canonicalDistrictIDOrdering(order: DistrictOrder): DistrictOrder
|
|
223
|
-
{
|
|
224
|
-
let keys = Object.keys(order);
|
|
225
|
-
let i: number;
|
|
226
|
-
let a: any = [];
|
|
227
|
-
let template: string = undefined;
|
|
228
|
-
|
|
229
|
-
keys = keys.map((s: string) => canonicalSortingDistrictID(s));
|
|
230
|
-
keys.sort();
|
|
231
|
-
order = {};
|
|
232
|
-
for (i = 0; i < keys.length; i++)
|
|
233
|
-
order[canonicalDistrictID(keys[i])] = i+1;
|
|
234
|
-
|
|
235
|
-
// Remove water districts
|
|
236
|
-
if (order['ZZZ']) delete order['ZZZ'];
|
|
237
|
-
if (order['ZZ']) delete order['ZZ'];
|
|
238
|
-
|
|
239
|
-
return order;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
export interface OneCSVLine
|
|
244
|
-
{
|
|
245
|
-
geoid: string;
|
|
246
|
-
districtID: string;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
let reArray = [
|
|
250
|
-
/^(\d\d[^\s,"']*)[\s]*,[\s]*([^\s'"]+)[\s]*$/,
|
|
251
|
-
/^["'](\d\d[^"']*)["'][\s]*,[\s]*["']([^"']*)["'][\s]*$/,
|
|
252
|
-
/^(\d\d[^\s,]*)[\s]*,[\s]*["']([^"']*)["'][\s]*$/,
|
|
253
|
-
/^["'](\d\d[^"']*)["'][\s]*,[\s]*([^\s]+)[\s]*$/,
|
|
254
|
-
];
|
|
255
|
-
|
|
256
|
-
export function parseCSVLine(line: string): OneCSVLine
|
|
257
|
-
{
|
|
258
|
-
if (line == null || line == '') return null;
|
|
259
|
-
for (let i: number = 0; i < reArray.length; i++)
|
|
260
|
-
{
|
|
261
|
-
let a = reArray[i].exec(line);
|
|
262
|
-
if (a && a.length === 3)
|
|
263
|
-
return { geoid: a[1], districtID: a[2] };
|
|
264
|
-
}
|
|
265
|
-
return null;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
export interface ConvertResult
|
|
269
|
-
{
|
|
270
|
-
inBlockMap: BlockMapping;
|
|
271
|
-
inStateMap: BlockMapping;
|
|
272
|
-
outValid: boolean;
|
|
273
|
-
outState: string;
|
|
274
|
-
outMap: BlockMapping;
|
|
275
|
-
outOrder: DistrictOrder;
|
|
276
|
-
outDistrictToSplit: DistrictToSplitBlock;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
export function blockmapToState(blockMap: BlockMapping): string
|
|
280
|
-
{
|
|
281
|
-
for (var id in blockMap) if (blockMap.hasOwnProperty(id))
|
|
282
|
-
return geoidToState(id);
|
|
283
|
-
return null;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// blockToVTD:
|
|
287
|
-
// Take BlockMapping (simple map of GEOID to districtID) and a per-state map of block-level GEOID to VTD
|
|
288
|
-
// and return the output mapping of VTD to districtID, as well a data structure that describes any VTD's
|
|
289
|
-
// that need to be split between districtIDs. Also returns the DistrictOrder structure that defines the
|
|
290
|
-
// districtIDs that were used by the file.
|
|
291
|
-
//
|
|
292
|
-
// The state (as specified by the first two digits of the GEOID) is also determined. If the GEOID's do
|
|
293
|
-
// not all specify the same state, the mapping is considered invalid and the outValid flag is set to false.
|
|
294
|
-
//
|
|
295
|
-
|
|
296
|
-
export function blockmapToVTDmap(blockMap: BlockMapping, stateMap: BlockMapping): ConvertResult
|
|
297
|
-
{
|
|
298
|
-
let res: ConvertResult = {
|
|
299
|
-
inBlockMap: blockMap,
|
|
300
|
-
inStateMap: stateMap,
|
|
301
|
-
outValid: true,
|
|
302
|
-
outState: null,
|
|
303
|
-
outMap: {},
|
|
304
|
-
outOrder: {},
|
|
305
|
-
outDistrictToSplit: {}
|
|
306
|
-
};
|
|
307
|
-
|
|
308
|
-
let bmGather: { [geoid: string]: { [district: string]: { [blockid: string]: boolean } } } = {};
|
|
309
|
-
let revMap: BlockMapping = {};
|
|
310
|
-
let id: string;
|
|
311
|
-
|
|
312
|
-
if (stateMap)
|
|
313
|
-
for (id in stateMap) if (stateMap.hasOwnProperty(id))
|
|
314
|
-
revMap[stateMap[id]] = null;
|
|
315
|
-
|
|
316
|
-
// First aggregate into features across all the blocks
|
|
317
|
-
for (id in blockMap) if (blockMap.hasOwnProperty(id))
|
|
318
|
-
{
|
|
319
|
-
let state = geoidToState(id);
|
|
320
|
-
if (res.outState == null)
|
|
321
|
-
res.outState = state;
|
|
322
|
-
else if (res.outState !== state)
|
|
323
|
-
{
|
|
324
|
-
res.outValid = false;
|
|
325
|
-
break;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
let districtID: string = canonicalDistrictID(blockMap[id]);
|
|
329
|
-
|
|
330
|
-
// Just ignore ZZZ (water) blocks
|
|
331
|
-
if (districtID === 'ZZZ')
|
|
332
|
-
continue;
|
|
333
|
-
|
|
334
|
-
let n: number = id.length;
|
|
335
|
-
let geoid: string;
|
|
336
|
-
|
|
337
|
-
// Simple test for block id (vs. voting district or block group) id
|
|
338
|
-
if (n >= 15)
|
|
339
|
-
{
|
|
340
|
-
if (stateMap && stateMap[id] !== undefined)
|
|
341
|
-
geoid = stateMap[id];
|
|
342
|
-
else
|
|
343
|
-
{
|
|
344
|
-
geoid = id.substr(0, 12); // heuristic for mapping blockID to blockgroupID
|
|
345
|
-
if (revMap[geoid] === undefined)
|
|
346
|
-
{
|
|
347
|
-
res.outValid = false;
|
|
348
|
-
break;
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
else
|
|
353
|
-
geoid = id;
|
|
354
|
-
|
|
355
|
-
if (res.outOrder[districtID] === undefined)
|
|
356
|
-
res.outOrder[districtID] = 0;
|
|
357
|
-
|
|
358
|
-
let districtToBlocks: { [districtID: string]: { [blockid: string]: boolean } } = bmGather[geoid];
|
|
359
|
-
if (districtToBlocks === undefined)
|
|
360
|
-
bmGather[geoid] = { [districtID]: { [id]: true } };
|
|
361
|
-
else
|
|
362
|
-
{
|
|
363
|
-
let thisDistrict: { [blockid: string]: boolean } = districtToBlocks[districtID];
|
|
364
|
-
if (thisDistrict === undefined)
|
|
365
|
-
{
|
|
366
|
-
thisDistrict = { };
|
|
367
|
-
districtToBlocks[districtID] = thisDistrict;
|
|
368
|
-
}
|
|
369
|
-
thisDistrict[id] = true;
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// Now determine actual mapping of blocks to features, looking for split features
|
|
374
|
-
for (let geoid in bmGather) if (bmGather.hasOwnProperty(geoid))
|
|
375
|
-
{
|
|
376
|
-
let districtToBlocks = bmGather[geoid];
|
|
377
|
-
if (Util.countKeys(districtToBlocks) == 1)
|
|
378
|
-
{
|
|
379
|
-
res.outMap[geoid] = Util.nthKey(districtToBlocks);
|
|
380
|
-
}
|
|
381
|
-
else
|
|
382
|
-
{
|
|
383
|
-
for (let districtID in districtToBlocks) if (districtToBlocks.hasOwnProperty(districtID))
|
|
384
|
-
{
|
|
385
|
-
let split: SplitBlock = { state: '', datasource: '', geoid: geoid, blocks: Object.keys(districtToBlocks[districtID]) };
|
|
386
|
-
let splits = res.outDistrictToSplit[districtID];
|
|
387
|
-
if (splits === undefined)
|
|
388
|
-
{
|
|
389
|
-
splits = [];
|
|
390
|
-
res.outDistrictToSplit[districtID] = splits;
|
|
391
|
-
}
|
|
392
|
-
splits.push(split);
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
res.outOrder = canonicalDistrictIDOrdering(res.outOrder);
|
|
398
|
-
|
|
399
|
-
return res;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
export const GEOIDToState: any = {
|
|
403
|
-
'01': 'AL',
|
|
404
|
-
'02': 'AK',
|
|
405
|
-
'04': 'AZ',
|
|
406
|
-
'05': 'AR',
|
|
407
|
-
'06': 'CA',
|
|
408
|
-
'08': 'CO',
|
|
409
|
-
'09': 'CT',
|
|
410
|
-
'10': 'DE',
|
|
411
|
-
'12': 'FL',
|
|
412
|
-
'13': 'GA',
|
|
413
|
-
'15': 'HI',
|
|
414
|
-
'16': 'ID',
|
|
415
|
-
'17': 'IL',
|
|
416
|
-
'18': 'IN',
|
|
417
|
-
'19': 'IA',
|
|
418
|
-
'20': 'KS',
|
|
419
|
-
'21': 'KY',
|
|
420
|
-
'22': 'LA',
|
|
421
|
-
'23': 'ME',
|
|
422
|
-
'24': 'MD',
|
|
423
|
-
'25': 'MA',
|
|
424
|
-
'26': 'MI',
|
|
425
|
-
'27': 'MN',
|
|
426
|
-
'28': 'MS',
|
|
427
|
-
'29': 'MO',
|
|
428
|
-
'30': 'MT',
|
|
429
|
-
'31': 'NE',
|
|
430
|
-
'32': 'NV',
|
|
431
|
-
'33': 'NH',
|
|
432
|
-
'34': 'NJ',
|
|
433
|
-
'35': 'NM',
|
|
434
|
-
'36': 'NY',
|
|
435
|
-
'37': 'NC',
|
|
436
|
-
'38': 'ND',
|
|
437
|
-
'39': 'OH',
|
|
438
|
-
'40': 'OK',
|
|
439
|
-
'41': 'OR',
|
|
440
|
-
'42': 'PA',
|
|
441
|
-
'44': 'RI',
|
|
442
|
-
'45': 'SC',
|
|
443
|
-
'46': 'SD',
|
|
444
|
-
'47': 'TN',
|
|
445
|
-
'48': 'TX',
|
|
446
|
-
'49': 'UT',
|
|
447
|
-
'50': 'VT',
|
|
448
|
-
'51': 'VA',
|
|
449
|
-
'53': 'WA',
|
|
450
|
-
'54': 'WV',
|
|
451
|
-
'55': 'WI',
|
|
452
|
-
'56': 'WY',
|
|
453
|
-
};
|
|
454
|
-
|
|
455
|
-
export const StateToGEOID: any = {
|
|
456
|
-
'AL': '01',
|
|
457
|
-
'AK': '02',
|
|
458
|
-
'AZ': '04',
|
|
459
|
-
'AR': '05',
|
|
460
|
-
'CA': '06',
|
|
461
|
-
'CO': '08',
|
|
462
|
-
'CT': '09',
|
|
463
|
-
'DE': '10',
|
|
464
|
-
'FL': '12',
|
|
465
|
-
'GA': '13',
|
|
466
|
-
'HI': '15',
|
|
467
|
-
'ID': '16',
|
|
468
|
-
'IL': '17',
|
|
469
|
-
'IN': '18',
|
|
470
|
-
'IA': '19',
|
|
471
|
-
'KS': '20',
|
|
472
|
-
'KY': '21',
|
|
473
|
-
'LA': '22',
|
|
474
|
-
'ME': '23',
|
|
475
|
-
'MD': '24',
|
|
476
|
-
'MA': '25',
|
|
477
|
-
'MI': '26',
|
|
478
|
-
'MN': '27',
|
|
479
|
-
'MS': '28',
|
|
480
|
-
'MO': '29',
|
|
481
|
-
'MT': '30',
|
|
482
|
-
'NE': '31',
|
|
483
|
-
'NV': '32',
|
|
484
|
-
'NH': '33',
|
|
485
|
-
'NJ': '34',
|
|
486
|
-
'NM': '35',
|
|
487
|
-
'NY': '36',
|
|
488
|
-
'NC': '37',
|
|
489
|
-
'ND': '38',
|
|
490
|
-
'OH': '39',
|
|
491
|
-
'OK': '40',
|
|
492
|
-
'OR': '41',
|
|
493
|
-
'PA': '42',
|
|
494
|
-
'RI': '44',
|
|
495
|
-
'SC': '45',
|
|
496
|
-
'SD': '46',
|
|
497
|
-
'TN': '47',
|
|
498
|
-
'TX': '48',
|
|
499
|
-
'UT': '49',
|
|
500
|
-
'VT': '50',
|
|
501
|
-
'VA': '51',
|
|
502
|
-
'WA': '53',
|
|
503
|
-
'WV': '54',
|
|
504
|
-
'WI': '55',
|
|
505
|
-
'WY': '56',
|
|
506
|
-
};
|
|
507
|
-
|
|
508
|
-
export function geoidToState(geoid: string): string
|
|
509
|
-
{
|
|
510
|
-
let re = /^(..).*$/;
|
|
511
|
-
|
|
512
|
-
let a = re.exec(geoid);
|
|
513
|
-
if (a == null || a.length != 2) return null;
|
|
514
|
-
return GEOIDToState[a[1]];
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
export type StateUrls = (
|
|
518
|
-
'alabama' |
|
|
519
|
-
'alaska' |
|
|
520
|
-
'arizona' |
|
|
521
|
-
'arkansas' |
|
|
522
|
-
'california' |
|
|
523
|
-
'colorado' |
|
|
524
|
-
'connecticut' |
|
|
525
|
-
'delaware' |
|
|
526
|
-
'florida' |
|
|
527
|
-
'georgia' |
|
|
528
|
-
'hawaii' |
|
|
529
|
-
'idaho' |
|
|
530
|
-
'illinois' |
|
|
531
|
-
'indiana' |
|
|
532
|
-
'iowa' |
|
|
533
|
-
'kansas' |
|
|
534
|
-
'kentucky' |
|
|
535
|
-
'louisiana' |
|
|
536
|
-
'maine' |
|
|
537
|
-
'maryland' |
|
|
538
|
-
'massachusetts' |
|
|
539
|
-
'michigan' |
|
|
540
|
-
'minnesota' |
|
|
541
|
-
'mississippi' |
|
|
542
|
-
'missouri' |
|
|
543
|
-
'montana' |
|
|
544
|
-
'nebraska' |
|
|
545
|
-
'nevada' |
|
|
546
|
-
'new-hampshire' |
|
|
547
|
-
'new-jersey' |
|
|
548
|
-
'new-mexico' |
|
|
549
|
-
'new-york' |
|
|
550
|
-
'north-carolina' |
|
|
551
|
-
'north-dakota' |
|
|
552
|
-
'ohio' |
|
|
553
|
-
'oklahoma' |
|
|
554
|
-
'oregon' |
|
|
555
|
-
'pennsylvania' |
|
|
556
|
-
'rhode-island' |
|
|
557
|
-
'south-carolina' |
|
|
558
|
-
'south-dakota' |
|
|
559
|
-
'tennessee' |
|
|
560
|
-
'texas' |
|
|
561
|
-
'utah' |
|
|
562
|
-
'vermont' |
|
|
563
|
-
'virginia' |
|
|
564
|
-
'washington' |
|
|
565
|
-
'west-virginia' |
|
|
566
|
-
'wisconsin' |
|
|
567
|
-
'wyoming'
|
|
568
|
-
);
|
|
569
|
-
|
|
570
|
-
export type ValidStateUrlsType =
|
|
571
|
-
{
|
|
572
|
-
readonly [stateUrl in StateUrls]: boolean;
|
|
573
|
-
};
|
|
574
|
-
|
|
575
|
-
const ValidStateUrls: ValidStateUrlsType = {
|
|
576
|
-
'alabama': true,
|
|
577
|
-
'alaska': true,
|
|
578
|
-
'arizona': true,
|
|
579
|
-
'arkansas': true,
|
|
580
|
-
'california': true,
|
|
581
|
-
'colorado': true,
|
|
582
|
-
'connecticut': true,
|
|
583
|
-
'delaware': true,
|
|
584
|
-
'florida': true,
|
|
585
|
-
'georgia': true,
|
|
586
|
-
'hawaii': true,
|
|
587
|
-
'idaho': true,
|
|
588
|
-
'illinois': true,
|
|
589
|
-
'indiana': true,
|
|
590
|
-
'iowa': true,
|
|
591
|
-
'kansas': true,
|
|
592
|
-
'kentucky': true,
|
|
593
|
-
'louisiana': true,
|
|
594
|
-
'maine': true,
|
|
595
|
-
'maryland': true,
|
|
596
|
-
'massachusetts': true,
|
|
597
|
-
'michigan': true,
|
|
598
|
-
'minnesota': true,
|
|
599
|
-
'mississippi': true,
|
|
600
|
-
'missouri': true,
|
|
601
|
-
'montana': true,
|
|
602
|
-
'nebraska': true,
|
|
603
|
-
'nevada': true,
|
|
604
|
-
'new-hampshire': true,
|
|
605
|
-
'new-jersey': true,
|
|
606
|
-
'new-mexico': true,
|
|
607
|
-
'new-york': true,
|
|
608
|
-
'north-carolina': true,
|
|
609
|
-
'north-dakota': true,
|
|
610
|
-
'ohio': true,
|
|
611
|
-
'oklahoma': true,
|
|
612
|
-
'oregon': true,
|
|
613
|
-
'pennsylvania': true,
|
|
614
|
-
'rhode-island': true,
|
|
615
|
-
'south-carolina': true,
|
|
616
|
-
'south-dakota': true,
|
|
617
|
-
'tennessee': true,
|
|
618
|
-
'texas': true,
|
|
619
|
-
'utah': true,
|
|
620
|
-
'vermont': true,
|
|
621
|
-
'virginia': true,
|
|
622
|
-
'washington': true,
|
|
623
|
-
'west-virginia': true,
|
|
624
|
-
'wisconsin': true,
|
|
625
|
-
'wyoming': true,
|
|
626
|
-
};
|
|
627
|
-
|
|
628
|
-
export function isStateUrl(s: any): s is StateUrls
|
|
629
|
-
{
|
|
630
|
-
return (typeof s === 'string' && s in ValidStateUrls);
|
|
631
|
-
}
|
package/lib/gencolor.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as ColorData from "./colordata";
|
|
2
|
+
|
|
3
|
+
let ColorTable: string[] = null;
|
|
4
|
+
const MaxColors: number = 100;
|
|
5
|
+
|
|
6
|
+
export function genColor(i: number, useFirstColor: boolean): string
|
|
7
|
+
{
|
|
8
|
+
function gen_table(): void
|
|
9
|
+
{
|
|
10
|
+
ColorTable = [];
|
|
11
|
+
for (let i: number = 0; i < MaxColors; i++)
|
|
12
|
+
{
|
|
13
|
+
// A little funky math below to skip the first (white) color
|
|
14
|
+
let j = (i % (ColorData.DefaultColorNames.length - 1)) + (useFirstColor ? 0 : 1);
|
|
15
|
+
ColorTable.push(ColorData.ColorValues[ColorData.DefaultColorNames[j]]);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (ColorTable == null) gen_table();
|
|
20
|
+
return ColorTable[i % MaxColors];
|
|
21
|
+
}
|
package/lib/vfeature.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// Public libraries
|
|
2
|
+
import * as Hash from 'object-hash';
|
|
3
|
+
|
|
4
|
+
export interface SplitBlock
|
|
5
|
+
{
|
|
6
|
+
id?: string;
|
|
7
|
+
chunkKey?: string;
|
|
8
|
+
chunk?: string;
|
|
9
|
+
state: string;
|
|
10
|
+
datasource: string;
|
|
11
|
+
geoid: string;
|
|
12
|
+
blocks: string[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type DistrictToSplitBlock = { [districtID: string]: SplitBlock[] };
|
|
16
|
+
|
|
17
|
+
// Canonical hashing of splitblock data
|
|
18
|
+
function hash(o: any): string
|
|
19
|
+
{
|
|
20
|
+
return Hash(o,
|
|
21
|
+
{ respectType: false,
|
|
22
|
+
unorderedArrays: true,
|
|
23
|
+
unorderedObjects: true,
|
|
24
|
+
excludeKeys: (k: string) => (k === 'id' || k === 'chunk')
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function vgeoidToGeoid(vgeoid: string): string
|
|
29
|
+
{
|
|
30
|
+
let re = /vfeature_([^_]*)_.*/;
|
|
31
|
+
let a = re.exec(vgeoid);
|
|
32
|
+
if (a == null || a.length != 2)
|
|
33
|
+
return '';
|
|
34
|
+
else
|
|
35
|
+
return a[1];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function vgeoidToChunk(vgeoid: string): string
|
|
39
|
+
{
|
|
40
|
+
// vgeoid is string of form: "vfeature_[geoid]_[chunkid]_[hash]"
|
|
41
|
+
// the contents are chunked into a file of form "vfeature_chunk_[chunkid]"
|
|
42
|
+
// So extract the chunk ID and download that.
|
|
43
|
+
let re = /vfeature_([^_]*)_([^_*])_(.*)/;
|
|
44
|
+
let a = re.exec(vgeoid);
|
|
45
|
+
if (a && a.length == 4)
|
|
46
|
+
vgeoid = `vfeature_chunk_${a[2]}`;
|
|
47
|
+
else
|
|
48
|
+
vgeoid = null;
|
|
49
|
+
|
|
50
|
+
return vgeoid;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function vgeoidToHash(vgeoid: string): string
|
|
54
|
+
{
|
|
55
|
+
// vgeoid is string of form: "vfeature_[geoid]_[chunkid]_[hash]"
|
|
56
|
+
let re = /vfeature_([^_]*)_([^_*])_(.*)/;
|
|
57
|
+
let a = re.exec(vgeoid);
|
|
58
|
+
if (a && a.length == 4)
|
|
59
|
+
vgeoid = a[3];
|
|
60
|
+
else
|
|
61
|
+
vgeoid = null;
|
|
62
|
+
|
|
63
|
+
return vgeoid;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function isVfeature(geoid: string): boolean
|
|
67
|
+
{
|
|
68
|
+
return geoid.indexOf('vfeature') === 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function splitToCacheKey(s: SplitBlock): string
|
|
72
|
+
{
|
|
73
|
+
if (s.id === undefined)
|
|
74
|
+
s.id = hash(s);
|
|
75
|
+
if (s.chunk === undefined)
|
|
76
|
+
s.chunk = "0";
|
|
77
|
+
|
|
78
|
+
return `_${s.state}_${s.datasource}_vfeature_${s.geoid}_${s.chunk}_${s.id}.geojson`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function splitToChunkKey(s: SplitBlock): string
|
|
82
|
+
{
|
|
83
|
+
if (s.chunk === undefined)
|
|
84
|
+
s.chunk = "0";
|
|
85
|
+
|
|
86
|
+
return `_${s.state}_${s.datasource}_vfeature_chunk_${s.chunk}.geojson`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function splitToPrefix(s: SplitBlock): string
|
|
90
|
+
{
|
|
91
|
+
if (s.blocks === undefined)
|
|
92
|
+
{
|
|
93
|
+
let re = /_([^_]*)_(.*)_vfeature.*\.geojson$/;
|
|
94
|
+
let a = re.exec(s.id);
|
|
95
|
+
if (a && a.length == 3)
|
|
96
|
+
return `_${a[1]}_${a[2]}`;
|
|
97
|
+
return s.id;
|
|
98
|
+
}
|
|
99
|
+
return `_${s.state}_${s.datasource}`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function cacheKeysToChunkHash(keys: string[]): string
|
|
103
|
+
{
|
|
104
|
+
return hash(keys);
|
|
105
|
+
}
|