@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/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
- }
@@ -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
+ }
@@ -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
+ }