@dra2020/dra-types 1.8.88 → 1.8.90
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 +6 -14
- package/dist/alldt.d.ts +14 -0
- package/dist/colormgr.d.ts +69 -0
- package/dist/dra-types.d.ts +1 -0
- package/dist/dra-types.js +824 -13
- package/dist/dra-types.js.map +1 -1
- package/dist/packedfields.d.ts +103 -1
- package/lib/all.ts +6 -14
- package/lib/alldt.ts +14 -0
- package/lib/colormgr.ts +563 -0
- package/lib/dra-types.ts +1 -0
- package/lib/packedfields.ts +396 -1
- package/lib/schemas.ts +24 -0
- package/package.json +1 -1
package/lib/packedfields.ts
CHANGED
|
@@ -1,18 +1,281 @@
|
|
|
1
|
+
import { Util } from "@dra2020/baseclient";
|
|
2
|
+
import { PlanType } from './dra-types';
|
|
3
|
+
|
|
4
|
+
// **** Dataset Codes Explained ****
|
|
5
|
+
// Elections:
|
|
6
|
+
// EYYGCC, where YY = year and CC = contest [PR, SE, GO, AG]
|
|
7
|
+
// C16GCO = composite (spans number of years)
|
|
8
|
+
// PYYGPR = PVI, where YY is year [16, 20]
|
|
9
|
+
// Census/ACS:
|
|
10
|
+
// DYYF = Census/ACS Total Pop, where YY = year [10, 18, 19, 20]
|
|
11
|
+
// DYYT = Census/ACS VAP/CVAP, where YY = year [10, 18, 19, 20]
|
|
12
|
+
// D20FA = Census 2020 Total Prisoner-Adjusted Pop
|
|
13
|
+
// D20TNH = Census 2020 VAP with Non-Hispanic Alone Race fields
|
|
14
|
+
|
|
15
|
+
// **** Dataset Fields Explained ****
|
|
16
|
+
// Elections:
|
|
17
|
+
// D: Democratic
|
|
18
|
+
// R: Republican
|
|
19
|
+
// Tot (Total R + D + Other)
|
|
20
|
+
// Census/ACS/VAP/CVAP:
|
|
21
|
+
// Tot: total
|
|
22
|
+
// Wh: White alone, not Hispanic
|
|
23
|
+
// His: All Hispanics
|
|
24
|
+
// Bl: Black alone, not Hispanic; BlC: Black combo, incl Hispanic
|
|
25
|
+
// Asn: Asian alone, not Hispanic; AsnC: Asian combo, incl Hispanic
|
|
26
|
+
// Nat: Native alone, not Hispanic; NatC: Native combo, incl Hispanic
|
|
27
|
+
// Pac (also PI): Pacific alone, not Hispanic; PacC: Pacific combo, incl Hispanic
|
|
28
|
+
// OthAl: Other alone, not Hispanic; Oth: Other + Two or more races, incl Hispanic
|
|
29
|
+
// Mix: Two or more races, not Hispanic
|
|
30
|
+
// AsnPI: Asian + Pacific, not Hispanic
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
export const AGG_DEMOGRAPHIC = 'demographic';
|
|
34
|
+
export const AGG_DEMOGRAPHIC18 = 'demographic18';
|
|
35
|
+
export const AGG_pres2008 = 'pres2008';
|
|
36
|
+
export const AGG_pres2016 = 'pres2016';
|
|
37
|
+
export const AGG_pvi = 'pvi';
|
|
38
|
+
|
|
39
|
+
export const DATASET_TYPE_DEMOGRAPHIC = 'demographic';
|
|
40
|
+
export const DATASET_TYPE_ELECTION = 'election';
|
|
41
|
+
export const DATASET_TYPE_PVI = 'pvi';
|
|
42
|
+
|
|
43
|
+
export const DS_PVI2020 = 'P20GPR';
|
|
44
|
+
export const PVI2020_Title = 'PVI 2016/2020';
|
|
45
|
+
export const DS_PVI2016 = 'P16GPR';
|
|
46
|
+
export const DS_PRES2020 = 'E20GPR';
|
|
47
|
+
export const DS_PRES2016 = 'E16GPR';
|
|
48
|
+
|
|
49
|
+
export interface StateMeta
|
|
50
|
+
{
|
|
51
|
+
state: string,
|
|
52
|
+
pop: number,
|
|
53
|
+
reps: number,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface StatesMetaIndex
|
|
57
|
+
{
|
|
58
|
+
[key: string]: StateMeta; // key is shortstate (2 letter) state name
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface StatesMeta
|
|
62
|
+
{
|
|
63
|
+
[key: string]: StatesMetaIndex; // key is one of the datasource strings
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export type FieldGetter = (f: string) => number;
|
|
67
|
+
export function fieldGetterNotLoaded(f: string): number { return undefined }
|
|
1
68
|
export type PackedFields = Float64Array;
|
|
69
|
+
export interface PackedFieldsIndex
|
|
70
|
+
{
|
|
71
|
+
[field: string]: number; // offset into PackedFields
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface PackedMetaIndex
|
|
75
|
+
{
|
|
76
|
+
length: number;
|
|
77
|
+
fields: { [dataset: string]: PackedFieldsIndex };
|
|
78
|
+
getDatasetField: (f: any, dataset: string, field: string) => number;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface DatasetMeta
|
|
82
|
+
{
|
|
83
|
+
type: string,
|
|
84
|
+
year: number,
|
|
85
|
+
title: string,
|
|
86
|
+
fields: {
|
|
87
|
+
[key: string]: any,
|
|
88
|
+
},
|
|
89
|
+
votingAge?: boolean,
|
|
90
|
+
office?: string,
|
|
91
|
+
subtype?: string,
|
|
92
|
+
description?: string,
|
|
93
|
+
nhAlone?: boolean,
|
|
94
|
+
privateKey?: string, // key for private data
|
|
95
|
+
members?: {[key: number]: string},
|
|
96
|
+
}
|
|
97
|
+
export type DatasetsMeta = { [dataset: string]: DatasetMeta };
|
|
98
|
+
|
|
99
|
+
export interface PrimaryDatasetKeys
|
|
100
|
+
{
|
|
101
|
+
SHAPES?: string,
|
|
102
|
+
CENSUS: string,
|
|
103
|
+
VAP: string,
|
|
104
|
+
ELECTION: string,
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// This integregates the information associated with a specific state and datasource as
|
|
108
|
+
// well as user selections around which datasets to view. Used to propagate through UI.
|
|
109
|
+
export interface DatasetContext
|
|
110
|
+
{
|
|
111
|
+
dsIndex: PackedMetaIndex;
|
|
112
|
+
primeDDS: string; // Demographic (Census)
|
|
113
|
+
primeVDS: string; // VAP/CVAP
|
|
114
|
+
primeEDS: string; // Election
|
|
115
|
+
datasetMetaDDS: DatasetMeta;
|
|
116
|
+
datasetMetaVDS: DatasetMeta;
|
|
117
|
+
datasetMetaEDS: DatasetMeta;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Dataset Lists
|
|
121
|
+
export type DSListItem = {
|
|
122
|
+
key: string,
|
|
123
|
+
title: string,
|
|
124
|
+
order: number,
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export type DSList = DSListItem[];
|
|
128
|
+
|
|
129
|
+
export type DSLists = {
|
|
130
|
+
census: DSList,
|
|
131
|
+
vap: DSList,
|
|
132
|
+
election: DSList,
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export type PlanTypePlus = PlanType | '';
|
|
136
|
+
|
|
137
|
+
export function fGetJoined(f: any): any[]
|
|
138
|
+
{
|
|
139
|
+
return (f.properties && f.properties.joined) ? f.properties.joined : undefined;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function fGet(f: any, p: string): any
|
|
143
|
+
{
|
|
144
|
+
return fGetW(f, null, p);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Note f is a direct GeoJSON feature
|
|
148
|
+
// Called when building packedFields; after that f.properties.datasets is deleted, so then it's only useful for non-dataset properties.
|
|
149
|
+
function fGetW(f: any, datasetKey: string, p: string): any
|
|
150
|
+
{
|
|
151
|
+
// pBackup helps support 2016_BG, which don't have the 'C' fields
|
|
152
|
+
const pBackup: string = (p === 'BlC') ? 'Bl' : (p === 'AsnC' || p === 'PacC') ? 'AsnPI' : (p === 'NatC') ? 'Nat' : null;
|
|
153
|
+
|
|
154
|
+
// Direct property?
|
|
155
|
+
if (f.properties && f.properties[p] !== undefined)
|
|
156
|
+
return f.properties[p];
|
|
157
|
+
else if (datasetKey && f.properties && f.properties.datasets && f.properties.datasets[datasetKey])
|
|
158
|
+
{
|
|
159
|
+
if (f.properties.datasets[datasetKey][p] != null)
|
|
160
|
+
return f.properties.datasets[datasetKey][p];
|
|
161
|
+
else if (pBackup && f.properties.datasets[datasetKey][pBackup] != null)
|
|
162
|
+
return f.properties.datasets[datasetKey][pBackup];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Joined property?
|
|
166
|
+
let a: any[] = fGetJoined(f);
|
|
167
|
+
if (a)
|
|
168
|
+
{
|
|
169
|
+
for (let i: number = 0; i < a.length; i++)
|
|
170
|
+
{
|
|
171
|
+
let o: any = a[i];
|
|
172
|
+
if (!datasetKey)
|
|
173
|
+
{
|
|
174
|
+
if (o[p] !== undefined)
|
|
175
|
+
return o[p];
|
|
176
|
+
}
|
|
177
|
+
else
|
|
178
|
+
{
|
|
179
|
+
if (o['datasets'] && o['datasets'][datasetKey] != null)
|
|
180
|
+
{
|
|
181
|
+
if (o['datasets'][datasetKey][p] != null)
|
|
182
|
+
return o['datasets'][datasetKey][p];
|
|
183
|
+
else if (pBackup && o['datasets'][datasetKey][pBackup] != null)
|
|
184
|
+
return o['datasets'][datasetKey][pBackup];
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function computeMetaIndex(meta: DatasetsMeta): PackedMetaIndex
|
|
193
|
+
{
|
|
194
|
+
if (meta == null) return null;
|
|
195
|
+
let offset = 1; // first entry is count of aggregates
|
|
196
|
+
let index: PackedMetaIndex = { length: 0, fields: {}, getDatasetField: null };
|
|
197
|
+
Object.keys(meta).forEach((datasetKey: string) => {
|
|
198
|
+
let dataset = meta[datasetKey];
|
|
199
|
+
let fieldsIndex: PackedFieldsIndex = {};
|
|
200
|
+
Object.keys(dataset.fields).forEach((field: string) => {
|
|
201
|
+
fieldsIndex[field] = offset++;
|
|
202
|
+
});
|
|
203
|
+
index.fields[datasetKey] = fieldsIndex;
|
|
204
|
+
});
|
|
205
|
+
index.length = offset;
|
|
206
|
+
index.getDatasetField = (f: any, dataset: string, field: string): number => {
|
|
207
|
+
let pf = retrievePackedFields(f);
|
|
208
|
+
return getPackedField(index, pf, dataset, field);
|
|
209
|
+
};
|
|
210
|
+
return index;
|
|
211
|
+
}
|
|
2
212
|
|
|
3
|
-
|
|
213
|
+
let nAlloc = 0;
|
|
214
|
+
function allocPackedFields(length: number): PackedFields
|
|
4
215
|
{
|
|
5
216
|
let ab = new ArrayBuffer(8 * length);
|
|
6
217
|
let af = new Float64Array(ab);
|
|
218
|
+
nAlloc++;
|
|
219
|
+
//if ((nAlloc % 10000) == 0) console.log(`allocPackedFields: ${nAlloc} allocs`);
|
|
220
|
+
return af;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export function computePackedFields(f: any, index: PackedMetaIndex): PackedFields
|
|
224
|
+
{
|
|
225
|
+
if (f.properties.packedFields) return f.properties.packedFields as PackedFields;
|
|
226
|
+
|
|
227
|
+
let af = allocPackedFields(index.length);
|
|
228
|
+
af[0] = 0; // count of number of aggregates
|
|
229
|
+
Object.keys(index.fields).forEach((dataset: string) => {
|
|
230
|
+
let fields = index.fields[dataset];
|
|
231
|
+
Object.keys(fields).forEach((field: string) => {
|
|
232
|
+
let n = fGetW(f, dataset, field);
|
|
233
|
+
if (isNaN(n))
|
|
234
|
+
n = 0;
|
|
235
|
+
af[fields[field]] = n;
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
f.properties.packedFields = af; // cache here
|
|
239
|
+
f.properties.getDatasetField = index.getDatasetField;
|
|
240
|
+
|
|
241
|
+
// Major memory savings to delete this after packing
|
|
242
|
+
delete f.properties.datasets;
|
|
7
243
|
return af;
|
|
8
244
|
}
|
|
9
245
|
|
|
246
|
+
export function hasPackedFields(f: any): boolean
|
|
247
|
+
{
|
|
248
|
+
return f.properties.packedFields !== undefined;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export function setPackedFields(f: any, pf: PackedFields, fIndex: any): void
|
|
252
|
+
{
|
|
253
|
+
if (f.properties.packedFields !== undefined) throw 'Packed fields already set';
|
|
254
|
+
f.properties.packedFields = pf;
|
|
255
|
+
f.properties.getDatasetField = fIndex.properties.getDatasetField
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export function retrievePackedFields(f: any): PackedFields
|
|
259
|
+
{
|
|
260
|
+
if (f.properties.packedFields === undefined) throw 'Feature should have pre-computed packed fields';
|
|
261
|
+
return f.properties.packedFields as PackedFields;
|
|
262
|
+
}
|
|
263
|
+
|
|
10
264
|
// The first entry in the PackedFields aggregate is the count of items aggregated.
|
|
11
265
|
// Treat a null instance as just a single entry with no aggregates.
|
|
12
266
|
let abZero = new ArrayBuffer(8);
|
|
13
267
|
let afZero = new Float64Array(abZero);
|
|
14
268
|
afZero[0] = 0;
|
|
15
269
|
|
|
270
|
+
export function zeroPackedFields(index: PackedMetaIndex): PackedFields
|
|
271
|
+
{
|
|
272
|
+
if (index == null) return afZero;
|
|
273
|
+
let af = allocPackedFields(index.length);
|
|
274
|
+
for (let i = 0; i < af.length; i++)
|
|
275
|
+
af[i] = 0;
|
|
276
|
+
return af;
|
|
277
|
+
}
|
|
278
|
+
|
|
16
279
|
export function zeroPackedCopy(pf: PackedFields): PackedFields
|
|
17
280
|
{
|
|
18
281
|
if (pf == null) return afZero;
|
|
@@ -45,3 +308,135 @@ export function aggregatePackedFields(agg: PackedFields, pf: PackedFields): Pack
|
|
|
45
308
|
agg[0]++; // count of number of aggregates
|
|
46
309
|
return agg;
|
|
47
310
|
}
|
|
311
|
+
|
|
312
|
+
export function diffPackedFields(main: any, parts: any[]): PackedFields
|
|
313
|
+
{
|
|
314
|
+
main = packedCopy(retrievePackedFields(main));
|
|
315
|
+
if (main == null || parts == null || parts.length == 0) return null;
|
|
316
|
+
parts = parts.map(retrievePackedFields);
|
|
317
|
+
parts.forEach((pf: PackedFields) => {
|
|
318
|
+
for (let i = 0; i < main.length; i++)
|
|
319
|
+
main[i] -= pf[i];
|
|
320
|
+
});
|
|
321
|
+
return main;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export function getPackedField(index: PackedMetaIndex, pf: PackedFields, dataset: string, field: string): number
|
|
325
|
+
{
|
|
326
|
+
if (index == null || pf == null) return 0;
|
|
327
|
+
let fields = index.fields[dataset];
|
|
328
|
+
return fields ? (fields[field] !== undefined ? pf[fields[field]] : 0) : 0;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export function findPackedField(index: PackedMetaIndex, pf: PackedFields, dataset: string, field: string): number
|
|
332
|
+
{
|
|
333
|
+
let fields = index.fields[dataset];
|
|
334
|
+
return fields ? (fields[field] !== undefined ? fields[field] : -1) : -1;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export function ToGetter(agg: PackedFields, dc: DatasetContext, datasetKey: string): FieldGetter
|
|
338
|
+
{
|
|
339
|
+
return (field: string) => { return getPackedField(dc.dsIndex, agg, datasetKey, field) };
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export function ToGetterPvi16(agg: PackedFields, dc: DatasetContext, datasetKey: string): FieldGetter
|
|
343
|
+
{
|
|
344
|
+
return (field: string) =>
|
|
345
|
+
{
|
|
346
|
+
if (field === 'R')
|
|
347
|
+
return Math.round((getPackedField(dc.dsIndex, agg, datasetKey, 'R12') + getPackedField(dc.dsIndex, agg, datasetKey, 'R16')) / 2);
|
|
348
|
+
if (field === 'D')
|
|
349
|
+
return Math.round((getPackedField(dc.dsIndex, agg, datasetKey, 'D12') + getPackedField(dc.dsIndex, agg, datasetKey, 'D16')) / 2);
|
|
350
|
+
if (field === 'Tot')
|
|
351
|
+
return Math.round((
|
|
352
|
+
getPackedField(dc.dsIndex, agg, datasetKey, 'R12') + getPackedField(dc.dsIndex, agg, datasetKey, 'R16') +
|
|
353
|
+
getPackedField(dc.dsIndex, agg, datasetKey, 'D12') + getPackedField(dc.dsIndex, agg, datasetKey, 'D16')) / 2);
|
|
354
|
+
return 0;
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
export function ToGetterPvi20(agg: PackedFields, dc: DatasetContext): FieldGetter
|
|
359
|
+
{
|
|
360
|
+
return (field: string) =>
|
|
361
|
+
{
|
|
362
|
+
if (field === 'R')
|
|
363
|
+
return Math.round((getPackedField(dc.dsIndex, agg, DS_PRES2016, 'R') + getPackedField(dc.dsIndex, agg, DS_PRES2020, 'R')) / 2);
|
|
364
|
+
if (field === 'D')
|
|
365
|
+
return Math.round((getPackedField(dc.dsIndex, agg, DS_PRES2016, 'D') + getPackedField(dc.dsIndex, agg, DS_PRES2020, 'D')) / 2);
|
|
366
|
+
if (field === 'Tot')
|
|
367
|
+
return Math.round((
|
|
368
|
+
getPackedField(dc.dsIndex, agg, DS_PRES2016, 'R') + getPackedField(dc.dsIndex, agg, DS_PRES2020, 'R') +
|
|
369
|
+
getPackedField(dc.dsIndex, agg, DS_PRES2016, 'D') + getPackedField(dc.dsIndex, agg, DS_PRES2020, 'D')) / 2);
|
|
370
|
+
return 0;
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
export function calcShift(agg: PackedFields, dc: DatasetContext, datasetOld: string, datasetNew: string): number
|
|
376
|
+
{
|
|
377
|
+
const getterOld = datasetOld === DS_PVI2016 ?
|
|
378
|
+
ToGetterPvi16(agg, dc, datasetOld) :
|
|
379
|
+
datasetOld === DS_PVI2020 ?
|
|
380
|
+
ToGetterPvi20(agg, dc) :
|
|
381
|
+
ToGetter(agg, dc, datasetOld);
|
|
382
|
+
const getterNew = datasetNew === DS_PVI2016 ?
|
|
383
|
+
ToGetterPvi16(agg, dc, datasetNew) :
|
|
384
|
+
datasetNew === DS_PVI2020 ?
|
|
385
|
+
ToGetterPvi20(agg, dc) :
|
|
386
|
+
ToGetter(agg, dc, datasetNew);
|
|
387
|
+
|
|
388
|
+
// Calc two-party Swing
|
|
389
|
+
const repOld = getterOld('R');
|
|
390
|
+
const demOld = getterOld('D');
|
|
391
|
+
const repNew = getterNew('R');
|
|
392
|
+
const demNew = getterNew('D');
|
|
393
|
+
|
|
394
|
+
if (repOld === undefined || demOld === undefined || repNew === undefined || demNew === undefined)
|
|
395
|
+
return null;
|
|
396
|
+
|
|
397
|
+
const totOld = demOld + repOld;
|
|
398
|
+
const totNew = demNew + repNew;
|
|
399
|
+
if (totOld <= 0 || totNew <= 0)
|
|
400
|
+
return null;
|
|
401
|
+
|
|
402
|
+
const pctDemOld: number = demOld / totOld;
|
|
403
|
+
const pctRepOld: number = repOld / totOld;
|
|
404
|
+
const pctDemNew: number = demNew / totNew;
|
|
405
|
+
const pctRepNew: number = repNew / totNew;
|
|
406
|
+
const shift: number = Math.max(Math.min((pctDemNew - pctDemOld) - (pctRepNew - pctRepOld), 1.0), -1.0);
|
|
407
|
+
return shift;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
export function calcRawPvi(getter: FieldGetter): number
|
|
411
|
+
{
|
|
412
|
+
// ((((sum(d_2016) / (sum(d_2016) + sum(r_2016))) * 100) + ((sum(d_2012) / (sum(d_2012) + sum(r_2012))) * 100)) / 2) - 51.54
|
|
413
|
+
// Fields hard coded
|
|
414
|
+
let total2012 = getter('D12') + getter('R12');
|
|
415
|
+
let total2016 = getter('D16') + getter('R16');
|
|
416
|
+
let pct2012 = total2012 != 0 ? (getter('D12') / total2012) * 100 : 0;
|
|
417
|
+
let pct2016 = total2016 != 0 ? (getter('D16') / total2016) * 100 : 0;
|
|
418
|
+
return (pct2012 + pct2016) / ((total2012 != 0 && total2016 != 0) ? 2 : 1);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
export function pviStr(getter: FieldGetter): string
|
|
422
|
+
{
|
|
423
|
+
const pviRaw: number = calcRawPvi(getter);
|
|
424
|
+
const pvi = Util.precisionRound(pviRaw != 0 ? pviRaw - 51.54 : 0, 2);
|
|
425
|
+
return pvi >= 0 ? 'D+' + pvi : 'R+' + (-pvi);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
export function calcRaw2020Pvi(getter16: FieldGetter, getter20: FieldGetter): number
|
|
429
|
+
{
|
|
430
|
+
let total2016 = getter16('D') + getter16('R');
|
|
431
|
+
let total2020 = getter20('D') + getter20('R');
|
|
432
|
+
let pct2016 = total2016 != 0 ? (getter16('D') / total2016) * 100 : 0;
|
|
433
|
+
let pct2020 = total2020 != 0 ? (getter20('D') / total2020) * 100 : 0;
|
|
434
|
+
return (pct2020 + pct2016) / ((total2020 != 0 && total2016 != 0) ? 2 : 1);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
export function pvi2020Str(getter16: FieldGetter, getter20: FieldGetter): string
|
|
438
|
+
{
|
|
439
|
+
const pviRaw: number = calcRaw2020Pvi(getter16, getter20);
|
|
440
|
+
const pvi = Util.precisionRound(pviRaw != 0 ? pviRaw - 51.54 : 0, 2);
|
|
441
|
+
return pvi >= 0 ? 'D+' + pvi : 'R+' + (-pvi);
|
|
442
|
+
}
|
package/lib/schemas.ts
CHANGED
|
@@ -220,6 +220,30 @@ export let Schemas: any = {
|
|
|
220
220
|
{ id: 'HASH' }
|
|
221
221
|
], // sparse
|
|
222
222
|
},
|
|
223
|
+
'dataset':
|
|
224
|
+
{
|
|
225
|
+
FileOptions: { map: true },
|
|
226
|
+
Schema: {
|
|
227
|
+
id: 'S',
|
|
228
|
+
name: 'S',
|
|
229
|
+
description: 'S',
|
|
230
|
+
createdBy: 'S',
|
|
231
|
+
createTime: 'S',
|
|
232
|
+
modifyTime: 'S',
|
|
233
|
+
deleted: 'BOOL',
|
|
234
|
+
published: 'S',
|
|
235
|
+
official: 'BOOL',
|
|
236
|
+
state: 'S',
|
|
237
|
+
datasource: 'S',
|
|
238
|
+
// meta: { dataset metadata structure },
|
|
239
|
+
// dotmap: 'BOOL',
|
|
240
|
+
},
|
|
241
|
+
KeySchema: { createdBy: 'HASH', id: 'RANGE' },
|
|
242
|
+
GlobalSecondaryIndexes: [
|
|
243
|
+
{ published: 'HASH' },
|
|
244
|
+
{ id: 'HASH' }
|
|
245
|
+
], // sparse
|
|
246
|
+
},
|
|
223
247
|
'groups':
|
|
224
248
|
{
|
|
225
249
|
FileOptions: { map: true },
|