@dra2020/district-analytics 7.1.5 → 8.0.0
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/LICENSE +1 -1
- package/dist/cli.js +295 -64
- package/dist/cli.js.map +1 -1
- package/dist/district-analytics.js +28 -13
- package/dist/district-analytics.js.map +1 -1
- package/dist/src/_data.d.ts +2 -2
- package/dist/src/geofeature.d.ts +0 -0
- package/dist/src/types.d.ts +1 -1
- package/package.json +3 -3
package/LICENSE
CHANGED
package/dist/cli.js
CHANGED
|
@@ -89056,7 +89056,7 @@ exports.scorePolsbyPopper = scorePolsbyPopper;
|
|
|
89056
89056
|
/*! exports provided: partisan, minority, compactness, splitting, popdev, default */
|
|
89057
89057
|
/***/ (function(module) {
|
|
89058
89058
|
|
|
89059
|
-
module.exports = JSON.parse("{\"partisan\":{\"bias\":{\"range\":[0,0.2],\"weight\":[50,80]},\"impact\":{\"weight\":[50,0],\"threshold\":4},\"competitiveness\":{\"range\":[0,0.75],\"distribution\":[0.25,0.75],\"simpleRange\":[0.45,0.55],\"weight\":[0,20]},\"bonus\":2},\"minority\":{\"range\":[0.37,0.5],\"distribution\":[0.25,0.75],\"shift\":0.15,\"coalition\":{\"weight\":0.5}},\"compactness\":{\"reock\":{\"range\":[0.25,0.5],\"weight\":50},\"polsby\":{\"range\":[0.1,0.5],\"weight\":50}},\"splitting\":{\"county\":{\"range\":[[1.26,1.68],[1.09,1.45]],\"weight\":50},\"district\":{\"range\":[[1.26,1.68],[1.09,1.45]],\"weight\":50}},\"popdev\":{\"range\":[[0.0075,0.002],[0.1,-1]]}}");
|
|
89059
|
+
module.exports = JSON.parse("{\"partisan\":{\"bias\":{\"range\":[0,0.2],\"weight\":[50,80]},\"impact\":{\"weight\":[50,0],\"threshold\":4},\"competitiveness\":{\"range\":[0,0.75],\"distribution\":[0.25,0.75],\"simpleRange\":[0.45,0.55],\"weight\":[0,20]},\"bonus\":2},\"minority\":{\"range\":[0.37,0.5],\"distribution\":[0.25,0.75],\"shift\":[0.15,0.5],\"coalition\":{\"weight\":0.5}},\"compactness\":{\"reock\":{\"range\":[0.25,0.5],\"weight\":50},\"polsby\":{\"range\":[0.1,0.5],\"weight\":50}},\"splitting\":{\"county\":{\"range\":[[1.26,1.68],[1.09,1.45]],\"weight\":50},\"district\":{\"range\":[[1.26,1.68],[1.09,1.45]],\"weight\":50}},\"popdev\":{\"range\":[[0.0075,0.002],[0.1,-1]]}}");
|
|
89060
89060
|
|
|
89061
89061
|
/***/ }),
|
|
89062
89062
|
|
|
@@ -89160,11 +89160,20 @@ function minorityOpportunityDistribution(overridesJSON) {
|
|
|
89160
89160
|
return dist;
|
|
89161
89161
|
}
|
|
89162
89162
|
exports.minorityOpportunityDistribution = minorityOpportunityDistribution;
|
|
89163
|
+
// For Black VAP %
|
|
89163
89164
|
function minorityShift(overridesJSON) {
|
|
89164
|
-
const
|
|
89165
|
+
const BLACK = 0;
|
|
89166
|
+
const shift = config_json_1.default.minority.shift[BLACK];
|
|
89165
89167
|
return shift;
|
|
89166
89168
|
}
|
|
89167
89169
|
exports.minorityShift = minorityShift;
|
|
89170
|
+
// Dilution for other demos
|
|
89171
|
+
function minorityShiftDilution(overridesJSON) {
|
|
89172
|
+
const DILUTION = 1;
|
|
89173
|
+
const shift = config_json_1.default.minority.shift[DILUTION];
|
|
89174
|
+
return shift;
|
|
89175
|
+
}
|
|
89176
|
+
exports.minorityShiftDilution = minorityShiftDilution;
|
|
89168
89177
|
function coalitionDistrictWeight(overridesJSON) {
|
|
89169
89178
|
const weight = config_json_1.default.minority.coalition.weight;
|
|
89170
89179
|
return weight;
|
|
@@ -89357,47 +89366,36 @@ const normalize_1 = __webpack_require__(/*! ./normalize */ "./src/normalize.ts")
|
|
|
89357
89366
|
const partisan_1 = __webpack_require__(/*! ./partisan */ "./src/partisan.ts");
|
|
89358
89367
|
function evalMinorityOpportunity(p, bLog = false) {
|
|
89359
89368
|
// Initialize arrays for results
|
|
89360
|
-
const
|
|
89361
|
-
const
|
|
89362
|
-
// const nDemos = T.DemographicField.Native + 1 - offset; // Ditto
|
|
89369
|
+
const nDemos = 5 /* Native */ + 1; // Profile includes 'White'
|
|
89370
|
+
const offset = 1; // But don't process 'White'
|
|
89363
89371
|
const demosByDistrict = p.demographicProfile.mfArrayByDistrict;
|
|
89364
89372
|
// Initialize the demographic buckets
|
|
89365
89373
|
// Get the statewide minority VAP/CVAP % (ignore 'White')
|
|
89366
89374
|
const vapPctArray = p.demographicProfile.stateMfArray.slice(1);
|
|
89367
89375
|
// Determine proportional minority districts by demographic (ignoring'White')
|
|
89368
89376
|
const districtsByDemo = calcDistrictsByDemo(vapPctArray, p.nDistricts);
|
|
89369
|
-
let bucketsByDemo = new Array(nDemos
|
|
89377
|
+
let bucketsByDemo = new Array(nDemos);
|
|
89370
89378
|
let totalProportional = 0;
|
|
89371
89379
|
for (let j = 0; j < nDemos; j++) {
|
|
89372
89380
|
const vapPct = vapPctArray[j];
|
|
89373
89381
|
const prop = districtsByDemo[j];
|
|
89374
|
-
|
|
89375
|
-
//
|
|
89382
|
+
bucketsByDemo[j] = [0, 0, 0, 0, 0, 0, vapPct, prop];
|
|
89383
|
+
// Sum the prop for each individual race/ethnicity demographic
|
|
89376
89384
|
if (j > 0)
|
|
89377
89385
|
totalProportional += prop;
|
|
89378
|
-
bucketsByDemo[j] = [0, 0, 0, vapPct, prop]; // Five conceptual columns for each demographic
|
|
89379
89386
|
}
|
|
89380
|
-
// Add a 'Total' row
|
|
89381
|
-
bucketsByDemo[nDemos] = [0, 0, 0, 0, totalProportional];
|
|
89382
89387
|
let opptyByDemo = U.initArray(nDemos, 0.0); // A state-level value
|
|
89383
89388
|
// For each district
|
|
89384
89389
|
for (let i = 0; i < p.nDistricts; i++) {
|
|
89385
89390
|
// Find the opportunities for minority representation
|
|
89386
89391
|
for (let j = 0; j < nDemos; j++) {
|
|
89387
|
-
const
|
|
89388
|
-
|
|
89392
|
+
const Mf = demosByDistrict[i][j + offset]; // Skip the 'White' entries
|
|
89393
|
+
const bucket = bucketVAPPct(Mf);
|
|
89394
|
+
if (bucket > 0) {
|
|
89389
89395
|
// Bucket opportunity districts for each demographic
|
|
89390
|
-
|
|
89391
|
-
bucketsByDemo[j][bucket] += 1;
|
|
89392
|
-
bucketsByDemo[j][2 /* Total */] += 1;
|
|
89396
|
+
bucketsByDemo[j][bucket - 1] += 1; // Zero-based array
|
|
89393
89397
|
// Accumulate seat probabilities
|
|
89394
|
-
opptyByDemo[j] += estMinorityOpportunity(
|
|
89395
|
-
// Also accumulate the 'Total' row summing the opportunities for each demographic,
|
|
89396
|
-
// ignoring the first all Minority demographic.
|
|
89397
|
-
if (j > 0) {
|
|
89398
|
-
bucketsByDemo[nDemos][bucket] += 1;
|
|
89399
|
-
bucketsByDemo[nDemos][2 /* Total */] += 1;
|
|
89400
|
-
}
|
|
89398
|
+
opptyByDemo[j] += estMinorityOpportunity(Mf, j);
|
|
89401
89399
|
}
|
|
89402
89400
|
}
|
|
89403
89401
|
}
|
|
@@ -89405,8 +89403,8 @@ function evalMinorityOpportunity(p, bLog = false) {
|
|
|
89405
89403
|
const oD = U.sumArray(opptyByDemo.slice(1)); // Sum individual demos, skipping all minorities
|
|
89406
89404
|
const cD = opptyByDemo[0 /* Minority */]; // All minorities
|
|
89407
89405
|
// The # of proportional districts for each separate demographic and all minorities
|
|
89408
|
-
const pOd =
|
|
89409
|
-
const pCd = bucketsByDemo[0 /* Minority */][
|
|
89406
|
+
const pOd = totalProportional;
|
|
89407
|
+
const pCd = bucketsByDemo[0 /* Minority */][7 /* PropSeats */];
|
|
89410
89408
|
// Score opportunity
|
|
89411
89409
|
const score = scoreMinority(oD, pOd, cD, pCd);
|
|
89412
89410
|
let mS = {
|
|
@@ -89447,13 +89445,15 @@ exports.calcDistrictsByDemo = calcDistrictsByDemo;
|
|
|
89447
89445
|
// NOTE - Sam Wang suggest 90% probability for a 37% district. That seems a little
|
|
89448
89446
|
// too abrupt and all or nothing, so I backed off to the ~70%.
|
|
89449
89447
|
//
|
|
89450
|
-
function estMinorityOpportunity(Mf) {
|
|
89448
|
+
function estMinorityOpportunity(Mf, demo) {
|
|
89451
89449
|
// NOTE - Switch to compress the probability distribution
|
|
89452
89450
|
const bCompress = false;
|
|
89453
89451
|
const dist = bCompress ? C.minorityOpportunityDistribution() : [0.0, 1.0];
|
|
89454
89452
|
const range = C.minorityOpportunityRange();
|
|
89455
89453
|
const _normalizer = new normalize_1.Normalizer(Mf);
|
|
89456
|
-
|
|
89454
|
+
let shift = C.minorityShift(); // For Black VAP % (and Minority)
|
|
89455
|
+
if (demo && (demo > 1)) // For other non-Black demos,
|
|
89456
|
+
shift *= C.minorityShiftDilution(); // dilute the Black shift (by half)
|
|
89457
89457
|
_normalizer.wipNum += shift;
|
|
89458
89458
|
_normalizer.clip(dist[C.BEG], dist[C.END]);
|
|
89459
89459
|
_normalizer.unitize(dist[C.BEG], dist[C.END]);
|
|
@@ -89462,13 +89462,21 @@ function estMinorityOpportunity(Mf) {
|
|
|
89462
89462
|
}
|
|
89463
89463
|
exports.estMinorityOpportunity = estMinorityOpportunity;
|
|
89464
89464
|
// HELPERS
|
|
89465
|
-
function
|
|
89466
|
-
|
|
89467
|
-
|
|
89468
|
-
|
|
89469
|
-
|
|
89470
|
-
|
|
89471
|
-
|
|
89465
|
+
function bucketVAPPct(Mf) {
|
|
89466
|
+
if (Mf < 0.35)
|
|
89467
|
+
return 0;
|
|
89468
|
+
else if ((Mf >= 0.35) && (Mf < 0.40))
|
|
89469
|
+
return 1;
|
|
89470
|
+
else if ((Mf >= 0.40) && (Mf < 0.45))
|
|
89471
|
+
return 2;
|
|
89472
|
+
else if ((Mf >= 0.45) && (Mf < 0.50))
|
|
89473
|
+
return 3;
|
|
89474
|
+
else if ((Mf >= 0.50) && (Mf < 0.55))
|
|
89475
|
+
return 4;
|
|
89476
|
+
else if ((Mf >= 0.55) && (Mf < 0.60))
|
|
89477
|
+
return 5;
|
|
89478
|
+
else // Mf >= 0.60
|
|
89479
|
+
return 6;
|
|
89472
89480
|
}
|
|
89473
89481
|
function calcProportionalDistricts(proportion, nDistricts) {
|
|
89474
89482
|
const roundUp = 0.0;
|
|
@@ -93801,6 +93809,7 @@ __export(__webpack_require__(/*! ./quad */ "./lib/quad.ts"));
|
|
|
93801
93809
|
__export(__webpack_require__(/*! ./polylabel */ "./lib/polylabel.ts"));
|
|
93802
93810
|
__export(__webpack_require__(/*! ./polysimplify */ "./lib/polysimplify.ts"));
|
|
93803
93811
|
__export(__webpack_require__(/*! ./polypack */ "./lib/polypack.ts"));
|
|
93812
|
+
__export(__webpack_require__(/*! ./polybin */ "./lib/polybin.ts"));
|
|
93804
93813
|
__export(__webpack_require__(/*! ./boundbox */ "./lib/boundbox.ts"));
|
|
93805
93814
|
__export(__webpack_require__(/*! ./blend */ "./lib/blend.ts"));
|
|
93806
93815
|
__export(__webpack_require__(/*! ./cartesian */ "./lib/cartesian.ts"));
|
|
@@ -93878,7 +93887,7 @@ function boundboxExtend(bbox, x, y) {
|
|
|
93878
93887
|
}
|
|
93879
93888
|
function boundboxExtendPacked(pp, bbox) {
|
|
93880
93889
|
let buffer = pp.buffer;
|
|
93881
|
-
let offset = pp.buffer[pp.offset + 1];
|
|
93890
|
+
let offset = pp.buffer[pp.offset + 1] + pp.offset;
|
|
93882
93891
|
let end = pp.offset + pp.length;
|
|
93883
93892
|
for (; offset < end; offset += 2)
|
|
93884
93893
|
boundboxExtend(bbox, buffer[offset], buffer[offset + 1]);
|
|
@@ -94517,6 +94526,151 @@ function npoints(poly) {
|
|
|
94517
94526
|
exports.npoints = npoints;
|
|
94518
94527
|
|
|
94519
94528
|
|
|
94529
|
+
/***/ }),
|
|
94530
|
+
|
|
94531
|
+
/***/ "./lib/polybin.ts":
|
|
94532
|
+
/*!************************!*\
|
|
94533
|
+
!*** ./lib/polybin.ts ***!
|
|
94534
|
+
\************************/
|
|
94535
|
+
/*! no static exports found */
|
|
94536
|
+
/***/ (function(module, exports, __webpack_require__) {
|
|
94537
|
+
|
|
94538
|
+
"use strict";
|
|
94539
|
+
|
|
94540
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
94541
|
+
const PP = __webpack_require__(/*! ./polypack */ "./lib/polypack.ts");
|
|
94542
|
+
// Only under Node. TextEncoder/Decoder implemented directly in browser.
|
|
94543
|
+
const ServerCoding = __webpack_require__(/*! util */ "util");
|
|
94544
|
+
// Packed Buffer format:
|
|
94545
|
+
//
|
|
94546
|
+
// (strings are packed as UTF8 bytes, padded to 4 byte boundary)
|
|
94547
|
+
//
|
|
94548
|
+
// { 4 byte size (byte offset to packed coordinate buffer) }
|
|
94549
|
+
// { 4 byte length: property name,
|
|
94550
|
+
// 4 byte length: json string }*
|
|
94551
|
+
// { 4 byte length: 'features' }
|
|
94552
|
+
// { 4 byte length: length of 'features' array }
|
|
94553
|
+
// { 4 byte length: property name,
|
|
94554
|
+
// 4 byte length: json string }*
|
|
94555
|
+
// { padding to 8 byte boundary }
|
|
94556
|
+
// { packed buffer coordinates in polypack format (indexed by geometry.packed above) }
|
|
94557
|
+
//
|
|
94558
|
+
const MagicInt = 17;
|
|
94559
|
+
const MagicFloat = 17.17;
|
|
94560
|
+
let encoder = ServerCoding.TextEncoder ? (new ServerCoding.TextEncoder()) : (new TextEncoder());
|
|
94561
|
+
let decoder = ServerCoding.TextDecoder ? (new ServerCoding.TextDecoder('utf-8')) : (new TextDecoder('utf-8'));
|
|
94562
|
+
function pad(n, pad) {
|
|
94563
|
+
let mod = n % pad;
|
|
94564
|
+
return mod ? pad - mod : 0;
|
|
94565
|
+
}
|
|
94566
|
+
function sizeOfString(s) {
|
|
94567
|
+
let s8 = encoder.encode(s);
|
|
94568
|
+
return 4 + s8.length + pad(s8.length, 4);
|
|
94569
|
+
}
|
|
94570
|
+
function packString(buf8, buf32, offset, s) {
|
|
94571
|
+
let s8 = encoder.encode(s);
|
|
94572
|
+
buf32[offset >> 2] = s8.length;
|
|
94573
|
+
offset += 4;
|
|
94574
|
+
let i;
|
|
94575
|
+
for (i = 0; i < s8.length; i++)
|
|
94576
|
+
buf8[offset++] = s8[i];
|
|
94577
|
+
offset += pad(offset, 4);
|
|
94578
|
+
return offset;
|
|
94579
|
+
}
|
|
94580
|
+
function unpackString(buf8, buf32, offset) {
|
|
94581
|
+
let size = buf32[offset >> 2];
|
|
94582
|
+
let s = decoder.decode(buf8.subarray(offset + 4, offset + 4 + size));
|
|
94583
|
+
return s;
|
|
94584
|
+
}
|
|
94585
|
+
function packCollection(col) {
|
|
94586
|
+
// Compute size
|
|
94587
|
+
let pp = PP.featurePack(col);
|
|
94588
|
+
let buffer = col.features.length ? col.features[0].geometry.packed.buffer : null; // to restore, below
|
|
94589
|
+
let size = 16; // int endiness, offset to coordinates, float endiness
|
|
94590
|
+
col.features.forEach((f) => { delete f.geometry.packed.buffer; }); // reconstructed when unpacking
|
|
94591
|
+
let j = JSON.stringify(col);
|
|
94592
|
+
size += sizeOfString(j);
|
|
94593
|
+
size += pad(size, 8);
|
|
94594
|
+
let fullsize = size + pp.length * 8; // add space for coordinates
|
|
94595
|
+
// Now pack it
|
|
94596
|
+
let ab = new ArrayBuffer(fullsize);
|
|
94597
|
+
let buf8 = new Uint8Array(ab);
|
|
94598
|
+
let buf32 = new Int32Array(ab);
|
|
94599
|
+
let buf64 = new Float64Array(ab);
|
|
94600
|
+
let offset = 0;
|
|
94601
|
+
buf32[0] = MagicInt;
|
|
94602
|
+
offset += 4;
|
|
94603
|
+
buf32[1] = size;
|
|
94604
|
+
offset += 4;
|
|
94605
|
+
buf64[1] = MagicFloat; // Note that buf64[0] has the two ints stored above
|
|
94606
|
+
offset += 8;
|
|
94607
|
+
offset = packString(buf8, buf32, offset, j);
|
|
94608
|
+
offset += pad(offset, 8);
|
|
94609
|
+
if (offset != size)
|
|
94610
|
+
throw 'Oops, packing error.';
|
|
94611
|
+
let foff = offset >> 3;
|
|
94612
|
+
let buf = pp.buffer;
|
|
94613
|
+
for (let i = 0; i < pp.length; i++)
|
|
94614
|
+
buf64[foff++] = buf[i];
|
|
94615
|
+
// Now restore
|
|
94616
|
+
col.features.forEach((f) => { f.geometry.packed.buffer = buffer; });
|
|
94617
|
+
PP.featureUnpack(col);
|
|
94618
|
+
return ab;
|
|
94619
|
+
}
|
|
94620
|
+
exports.packCollection = packCollection;
|
|
94621
|
+
function reverse(buf8, s, n) {
|
|
94622
|
+
let e = s + n - 1;
|
|
94623
|
+
while (s < e) {
|
|
94624
|
+
let t = buf8[s];
|
|
94625
|
+
buf8[s] = buf8[e];
|
|
94626
|
+
buf8[e] = t;
|
|
94627
|
+
s++, e--;
|
|
94628
|
+
}
|
|
94629
|
+
}
|
|
94630
|
+
function enforceEndianness(ab) {
|
|
94631
|
+
let buf8 = new Uint8Array(ab);
|
|
94632
|
+
let buf32 = new Int32Array(ab);
|
|
94633
|
+
let buf64 = new Float64Array(ab);
|
|
94634
|
+
let reverseInts = false;
|
|
94635
|
+
if (buf32[0] != MagicInt) {
|
|
94636
|
+
reverseInts = true;
|
|
94637
|
+
reverse(buf8, 0, 4);
|
|
94638
|
+
if (buf32[0] != MagicInt)
|
|
94639
|
+
throw 'unpackCollection: badly formatted buffer';
|
|
94640
|
+
reverse(buf8, 4, 4); // size of non-floats
|
|
94641
|
+
}
|
|
94642
|
+
let reverseFloats = false;
|
|
94643
|
+
if (buf64[1] != MagicFloat) {
|
|
94644
|
+
reverseFloats = true;
|
|
94645
|
+
reverse(buf8, 8, 8);
|
|
94646
|
+
if (buf64[1] != MagicFloat)
|
|
94647
|
+
throw 'unpackCollection: badly formatted buffer';
|
|
94648
|
+
}
|
|
94649
|
+
if (reverseInts)
|
|
94650
|
+
reverse(buf8, 16, 4); // JSON string length
|
|
94651
|
+
if (reverseFloats) {
|
|
94652
|
+
let s = buf32[1]; // Offset to floats
|
|
94653
|
+
let e = ab.byteLength;
|
|
94654
|
+
for (; s < e; s += 8)
|
|
94655
|
+
reverse(buf8, s, 8);
|
|
94656
|
+
}
|
|
94657
|
+
}
|
|
94658
|
+
function unpackCollection(ab) {
|
|
94659
|
+
enforceEndianness(ab);
|
|
94660
|
+
let col = {};
|
|
94661
|
+
let buf8 = new Uint8Array(ab);
|
|
94662
|
+
let buf32 = new Int32Array(ab);
|
|
94663
|
+
let size = buf32[1];
|
|
94664
|
+
let buf64 = new Float64Array(ab, size); // offset to start of packed coordinates
|
|
94665
|
+
let offset = 16;
|
|
94666
|
+
let j = unpackString(buf8, buf32, offset);
|
|
94667
|
+
col = JSON.parse(j);
|
|
94668
|
+
col.features.forEach((f) => { f.geometry.packed.buffer = buf64; });
|
|
94669
|
+
return col;
|
|
94670
|
+
}
|
|
94671
|
+
exports.unpackCollection = unpackCollection;
|
|
94672
|
+
|
|
94673
|
+
|
|
94520
94674
|
/***/ }),
|
|
94521
94675
|
|
|
94522
94676
|
/***/ "./lib/polylabel.ts":
|
|
@@ -94728,7 +94882,7 @@ function polyPackEachRing(pack, cb) {
|
|
|
94728
94882
|
let b = pack.buffer;
|
|
94729
94883
|
let iRing;
|
|
94730
94884
|
let iOffset = pack.offset;
|
|
94731
|
-
let cOffset = b[iOffset + 1];
|
|
94885
|
+
let cOffset = b[iOffset + 1] + pack.offset;
|
|
94732
94886
|
// Grab number of polygons
|
|
94733
94887
|
let nPoly = b[iOffset++];
|
|
94734
94888
|
// Move past header length
|
|
@@ -94805,7 +94959,7 @@ function polyPack(coords, prepack) {
|
|
|
94805
94959
|
af[z++] = p[j].length;
|
|
94806
94960
|
}
|
|
94807
94961
|
// set header size back in header
|
|
94808
|
-
af[prepack.offset + 1] = z;
|
|
94962
|
+
af[prepack.offset + 1] = z - prepack.offset;
|
|
94809
94963
|
// Fill in coords
|
|
94810
94964
|
for (i = 0; i < coords.length; i++) {
|
|
94811
94965
|
p = coords[i];
|
|
@@ -94831,7 +94985,7 @@ function polyUnpack(prepack) {
|
|
|
94831
94985
|
let coords = [];
|
|
94832
94986
|
let h = prepack.offset;
|
|
94833
94987
|
nPolys = af[h++];
|
|
94834
|
-
let z = af[h++]; // start of coordinates
|
|
94988
|
+
let z = af[h++] + prepack.offset; // start of coordinates
|
|
94835
94989
|
for (i = 0; i < nPolys; i++) {
|
|
94836
94990
|
p = [];
|
|
94837
94991
|
coords[i] = p;
|
|
@@ -95099,6 +95253,7 @@ class FsmQuadTree extends FSM.Fsm {
|
|
|
95099
95253
|
constructor(env, options, col) {
|
|
95100
95254
|
super(env);
|
|
95101
95255
|
this.options = Util.shallowAssignImmutable(Poly.DefaultTickOptions, options);
|
|
95256
|
+
this.work = { nUnion: 0, nDifference: 0, ms: 0 };
|
|
95102
95257
|
this.initialize(col);
|
|
95103
95258
|
}
|
|
95104
95259
|
initialize(col) {
|
|
@@ -95107,6 +95262,7 @@ class FsmQuadTree extends FSM.Fsm {
|
|
|
95107
95262
|
this.isempty = true;
|
|
95108
95263
|
if (col != null) {
|
|
95109
95264
|
let features = col.features ? col.features : col;
|
|
95265
|
+
this.work.nUnion = features.length;
|
|
95110
95266
|
this.isempty = features.length == 0;
|
|
95111
95267
|
// Compute BoundBox for each feature
|
|
95112
95268
|
let wrapped = features.map((f) => { return { box: BB.boundbox(f), p: featureCoords(f) }; });
|
|
@@ -95134,11 +95290,13 @@ class FsmQuadTree extends FSM.Fsm {
|
|
|
95134
95290
|
break;
|
|
95135
95291
|
case FSM.FSM_PENDING:
|
|
95136
95292
|
let tickCounter = { ticks: this.options.tickStep };
|
|
95293
|
+
let elapsed = new Util.Elapsed();
|
|
95137
95294
|
if (!this.isempty && this.asyncUnion === undefined) {
|
|
95138
95295
|
this.quad.tickUnion(tickCounter);
|
|
95139
95296
|
if (tickCounter.ticks != 0)
|
|
95140
95297
|
this.result;
|
|
95141
95298
|
}
|
|
95299
|
+
this.work.ms += elapsed.ms();
|
|
95142
95300
|
if (this.asyncUnion !== undefined)
|
|
95143
95301
|
this.setState(FSM.FSM_DONE);
|
|
95144
95302
|
else
|
|
@@ -95316,6 +95474,7 @@ exports.polyIntersects = polyIntersects;
|
|
|
95316
95474
|
class FsmDifference extends FSM.Fsm {
|
|
95317
95475
|
constructor(env, accum, polys) {
|
|
95318
95476
|
super(env);
|
|
95477
|
+
this.work = { nUnion: 0, nDifference: 0, ms: 0 };
|
|
95319
95478
|
this.initialize(accum, polys);
|
|
95320
95479
|
}
|
|
95321
95480
|
initialize(accum, polys) {
|
|
@@ -95323,8 +95482,10 @@ class FsmDifference extends FSM.Fsm {
|
|
|
95323
95482
|
this.polys = polys;
|
|
95324
95483
|
if (polys == null || polys.length == 0)
|
|
95325
95484
|
this.setState(FSM.FSM_DONE);
|
|
95326
|
-
else
|
|
95485
|
+
else {
|
|
95486
|
+
this.work.nDifference = polys.length;
|
|
95327
95487
|
this.setState(FSM.FSM_STARTING);
|
|
95488
|
+
}
|
|
95328
95489
|
}
|
|
95329
95490
|
cancel() {
|
|
95330
95491
|
this.accum = null;
|
|
@@ -95344,7 +95505,9 @@ class FsmDifference extends FSM.Fsm {
|
|
|
95344
95505
|
this.setState(FSM_COMPUTING);
|
|
95345
95506
|
break;
|
|
95346
95507
|
case FSM_COMPUTING:
|
|
95508
|
+
let elapsed = new Util.Elapsed();
|
|
95347
95509
|
this.accum = _difference(this.accum, this.polys);
|
|
95510
|
+
this.work.ms = elapsed.ms();
|
|
95348
95511
|
this.polys = null;
|
|
95349
95512
|
this.setState(FSM.FSM_DONE);
|
|
95350
95513
|
break;
|
|
@@ -95421,10 +95584,20 @@ class FsmIncrementalUnion extends FSM.Fsm {
|
|
|
95421
95584
|
if (this.toSub.length == 0)
|
|
95422
95585
|
this.toSub = null;
|
|
95423
95586
|
}
|
|
95424
|
-
|
|
95425
|
-
this.
|
|
95426
|
-
|
|
95427
|
-
|
|
95587
|
+
// Short-circuit when no work to be done
|
|
95588
|
+
if (polys.length == 1 && this.toSub == null && this.lastCompleteResult) {
|
|
95589
|
+
this.work = { nUnion: 0, nDifference: 0, ms: 0 };
|
|
95590
|
+
this.result = this.lastCompleteResult;
|
|
95591
|
+
this.map = this.lastCompleteMap;
|
|
95592
|
+
this.setState(FSM.FSM_DONE);
|
|
95593
|
+
}
|
|
95594
|
+
else {
|
|
95595
|
+
this.work = { nUnion: polys.length - 1, nDifference: this.toSub ? this.toSub.length : 0, ms: 0 };
|
|
95596
|
+
this.fsmUnion = new Q.FsmQuadTree(this.env, this.options, polys);
|
|
95597
|
+
this.waitOn(this.fsmUnion);
|
|
95598
|
+
this.setState(FSM_UNION);
|
|
95599
|
+
this.map = map;
|
|
95600
|
+
}
|
|
95428
95601
|
}
|
|
95429
95602
|
cancel() {
|
|
95430
95603
|
if (this.fsmUnion) {
|
|
@@ -95449,6 +95622,8 @@ class FsmIncrementalUnion extends FSM.Fsm {
|
|
|
95449
95622
|
this.setState(FSM.FSM_DONE);
|
|
95450
95623
|
break;
|
|
95451
95624
|
case FSM_UNION:
|
|
95625
|
+
if (this.fsmUnion)
|
|
95626
|
+
this.work.ms += this.fsmUnion.work.ms;
|
|
95452
95627
|
this.fsmDifference = new FsmDifference(this.env, this.fsmUnion.result, this.toSub);
|
|
95453
95628
|
this.waitOn(this.fsmDifference);
|
|
95454
95629
|
this.toSub = null;
|
|
@@ -95457,6 +95632,7 @@ class FsmIncrementalUnion extends FSM.Fsm {
|
|
|
95457
95632
|
break;
|
|
95458
95633
|
case FSM_DIFFERENCE:
|
|
95459
95634
|
this.result = this.fsmDifference.result;
|
|
95635
|
+
this.work.ms += this.fsmDifference.work.ms;
|
|
95460
95636
|
this.lastCompleteResult = this.result;
|
|
95461
95637
|
this.lastCompleteMap = this.map;
|
|
95462
95638
|
this.fsmDifference = null;
|
|
@@ -95471,10 +95647,11 @@ class FsmUnion extends FSM.Fsm {
|
|
|
95471
95647
|
super(env);
|
|
95472
95648
|
this.options = Util.shallowAssignImmutable(Poly.DefaultTickOptions, options);
|
|
95473
95649
|
this.unions = [];
|
|
95650
|
+
this.work = { nUnion: 0, nDifference: 0, ms: 0 };
|
|
95474
95651
|
}
|
|
95475
95652
|
get result() {
|
|
95476
95653
|
if (this.unions.length > 0 && this.state === FSM.FSM_DONE)
|
|
95477
|
-
return this.unions.map((i) => ({ key: i.key, poly: i.result }));
|
|
95654
|
+
return this.unions.map((i) => ({ key: i.key, poly: i.result, work: i.work }));
|
|
95478
95655
|
else
|
|
95479
95656
|
return null;
|
|
95480
95657
|
}
|
|
@@ -95485,6 +95662,15 @@ class FsmUnion extends FSM.Fsm {
|
|
|
95485
95662
|
this.unions = [];
|
|
95486
95663
|
this.setState(FSM.FSM_DONE);
|
|
95487
95664
|
}
|
|
95665
|
+
cancelOne(key) {
|
|
95666
|
+
for (let i = 0; i < this.unions.length; i++) {
|
|
95667
|
+
let u = this.unions[i];
|
|
95668
|
+
if (u.matches(key)) {
|
|
95669
|
+
u.cancel();
|
|
95670
|
+
return;
|
|
95671
|
+
}
|
|
95672
|
+
}
|
|
95673
|
+
}
|
|
95488
95674
|
recompute(key, map) {
|
|
95489
95675
|
let fsm = this.unions.find((i) => i.matches(key));
|
|
95490
95676
|
if (fsm == null) {
|
|
@@ -95493,6 +95679,8 @@ class FsmUnion extends FSM.Fsm {
|
|
|
95493
95679
|
}
|
|
95494
95680
|
else
|
|
95495
95681
|
fsm.recompute(map);
|
|
95682
|
+
this.work = { nUnion: 0, nDifference: 0, ms: 0 };
|
|
95683
|
+
this.unions.forEach((u) => { this.work.nUnion += u.work.nUnion; this.work.nDifference += u.work.nDifference; });
|
|
95496
95684
|
this.waitOn(fsm);
|
|
95497
95685
|
this.setState(FSM_COMPUTING);
|
|
95498
95686
|
}
|
|
@@ -95501,6 +95689,8 @@ class FsmUnion extends FSM.Fsm {
|
|
|
95501
95689
|
switch (this.state) {
|
|
95502
95690
|
case FSM.FSM_STARTING:
|
|
95503
95691
|
case FSM_COMPUTING:
|
|
95692
|
+
if (this.unions)
|
|
95693
|
+
this.unions.forEach((u) => { this.work.ms += u.work.ms; });
|
|
95504
95694
|
this.setState(FSM.FSM_DONE);
|
|
95505
95695
|
break;
|
|
95506
95696
|
}
|
|
@@ -98068,6 +98258,17 @@ module.exports = __webpack_require__(/*! @dra2020/fsm */ "./node_modules/@dra202
|
|
|
98068
98258
|
|
|
98069
98259
|
module.exports = __webpack_require__(/*! @dra2020/util */ "./node_modules/@dra2020/util/dist/util.js");
|
|
98070
98260
|
|
|
98261
|
+
/***/ }),
|
|
98262
|
+
|
|
98263
|
+
/***/ "util":
|
|
98264
|
+
/*!***********************!*\
|
|
98265
|
+
!*** external "util" ***!
|
|
98266
|
+
\***********************/
|
|
98267
|
+
/*! no static exports found */
|
|
98268
|
+
/***/ (function(module, exports) {
|
|
98269
|
+
|
|
98270
|
+
module.exports = __webpack_require__(/*! util */ "util");
|
|
98271
|
+
|
|
98071
98272
|
/***/ })
|
|
98072
98273
|
|
|
98073
98274
|
/******/ });
|
|
@@ -103018,7 +103219,8 @@ class Districts {
|
|
|
103018
103219
|
let featureID = outerThis._session.features.featureID(geoID);
|
|
103019
103220
|
let f = outerThis._session.features.featureByIndex(featureID);
|
|
103020
103221
|
if (f == undefined) {
|
|
103021
|
-
|
|
103222
|
+
if (bLog)
|
|
103223
|
+
console.log("Skipping undefined feature in district statistics: GEOID =", geoID, "Feature ID =", featureID);
|
|
103022
103224
|
}
|
|
103023
103225
|
else {
|
|
103024
103226
|
// ACCUMULATE VALUES
|
|
@@ -103045,8 +103247,10 @@ class Districts {
|
|
|
103045
103247
|
// 4 - MORE ...
|
|
103046
103248
|
}
|
|
103047
103249
|
}
|
|
103048
|
-
else
|
|
103049
|
-
|
|
103250
|
+
else {
|
|
103251
|
+
if (bLog)
|
|
103252
|
+
console.log("Skipping water-only feature in district statistics:", geoID);
|
|
103253
|
+
}
|
|
103050
103254
|
});
|
|
103051
103255
|
// COMPUTE DERIVED VALUES
|
|
103052
103256
|
// Population deviation % and equal population (boolean) by district.
|
|
@@ -103191,8 +103395,10 @@ class Districts {
|
|
|
103191
103395
|
this.statistics[DistrictField.AsianPct][summaryRow] = stateAsianPop / stateVAPPop;
|
|
103192
103396
|
this.statistics[DistrictField.NativePct][summaryRow] = stateNativePop / stateVAPPop;
|
|
103193
103397
|
}
|
|
103194
|
-
|
|
103195
|
-
|
|
103398
|
+
if (bLog) {
|
|
103399
|
+
console.log(`${nMissingDataset} features with missing datasets.`);
|
|
103400
|
+
console.log(`${nMissingProperty} features with missing properties.`);
|
|
103401
|
+
}
|
|
103196
103402
|
}
|
|
103197
103403
|
// NOTE - I did not roll these into district statistics, because creating the
|
|
103198
103404
|
// district shapes themselves is the big district-by-district activity, these
|
|
@@ -103216,6 +103422,8 @@ class Features {
|
|
|
103216
103422
|
this._session = s;
|
|
103217
103423
|
this._data = data;
|
|
103218
103424
|
this._keys = keys;
|
|
103425
|
+
// For debugging PVI
|
|
103426
|
+
// console.log("Election dataset =", this._keys[Dataset.ELECTION]);
|
|
103219
103427
|
}
|
|
103220
103428
|
nFeatures() { return this._data.features.length; }
|
|
103221
103429
|
featureByIndex(i) { return this._data.features[i]; }
|
|
@@ -103226,8 +103434,15 @@ class Features {
|
|
|
103226
103434
|
return value;
|
|
103227
103435
|
}
|
|
103228
103436
|
fieldForFeature(f, dt, fk) {
|
|
103229
|
-
|
|
103230
|
-
|
|
103437
|
+
const dk = this._keys[dt];
|
|
103438
|
+
let result = undefined;
|
|
103439
|
+
// 07-31-2020 - Fix to post-process PVI into the expected election composite format.
|
|
103440
|
+
if (dk === 'P16GPR') {
|
|
103441
|
+
result = (_getFeatures(f, dk, (fk === 'D' ? 'D12' : 'R12')) + _getFeatures(f, dk, (fk === 'D' ? 'D16' : 'R16'))) / 2;
|
|
103442
|
+
}
|
|
103443
|
+
else
|
|
103444
|
+
result = _getFeatures(f, dk, fk);
|
|
103445
|
+
return result;
|
|
103231
103446
|
}
|
|
103232
103447
|
resetDataset(d, k) {
|
|
103233
103448
|
this._keys[d] = k;
|
|
@@ -103345,8 +103560,8 @@ class Plan {
|
|
|
103345
103560
|
// }
|
|
103346
103561
|
// return newPlan;
|
|
103347
103562
|
// }
|
|
103348
|
-
invertPlan() {
|
|
103349
|
-
this._planByDistrictID = invertPlan(this._planByGeoID, this._session);
|
|
103563
|
+
invertPlan(bLog = false) {
|
|
103564
|
+
this._planByDistrictID = invertPlan(this._planByGeoID, this._session, bLog);
|
|
103350
103565
|
this.districtIDs = U.getNumericObjectKeys(this._planByDistrictID);
|
|
103351
103566
|
}
|
|
103352
103567
|
initializeDistrict(i) { this._planByDistrictID[i] = new Set(); }
|
|
@@ -103357,7 +103572,7 @@ class Plan {
|
|
|
103357
103572
|
}
|
|
103358
103573
|
exports.Plan = Plan;
|
|
103359
103574
|
// Invert a feature assignment structure to sets of ids by district
|
|
103360
|
-
function invertPlan(plan, s) {
|
|
103575
|
+
function invertPlan(plan, s, bLog = false) {
|
|
103361
103576
|
let invertedPlan = {};
|
|
103362
103577
|
// Add a dummy 'unassigned' district
|
|
103363
103578
|
invertedPlan[S.NOT_ASSIGNED] = new Set();
|
|
@@ -103387,7 +103602,7 @@ function invertPlan(plan, s) {
|
|
|
103387
103602
|
}
|
|
103388
103603
|
// Add the geoID to the districtID's set
|
|
103389
103604
|
invertedPlan[districtID].add(geoID);
|
|
103390
|
-
if (U.isWaterOnly(geoID))
|
|
103605
|
+
if (U.isWaterOnly(geoID) && bLog)
|
|
103391
103606
|
console.log("Water-only feature still in plan!", geoID);
|
|
103392
103607
|
}
|
|
103393
103608
|
return invertedPlan;
|
|
@@ -103871,7 +104086,7 @@ function doPreprocessData(s, bLog = false) {
|
|
|
103871
104086
|
s.bOneTimeProcessingDone = true;
|
|
103872
104087
|
}
|
|
103873
104088
|
// Invert the plan by district ID
|
|
103874
|
-
s.plan.invertPlan();
|
|
104089
|
+
s.plan.invertPlan(bLog);
|
|
103875
104090
|
// Create a map of geoIDs to feature IDs
|
|
103876
104091
|
s.features.mapGeoIDsToFeatureIDs();
|
|
103877
104092
|
}
|
|
@@ -103911,7 +104126,8 @@ function doPreprocessCensus(s, bLog = false) {
|
|
|
103911
104126
|
totalByCounty[countyFIPS] += value;
|
|
103912
104127
|
}
|
|
103913
104128
|
else {
|
|
103914
|
-
|
|
104129
|
+
if (bLog)
|
|
104130
|
+
console.log("Skipping water-only feature in Census preprocessing:", geoID);
|
|
103915
104131
|
}
|
|
103916
104132
|
}
|
|
103917
104133
|
// NOTE - The above could be replaced, if I got totals on county.geojson.
|
|
@@ -105045,12 +105261,16 @@ const S = __importStar(__webpack_require__(/*! ../src/settings */ "./src/setting
|
|
|
105045
105261
|
const D = __importStar(__webpack_require__(/*! ../src/_data */ "./src/_data.ts"));
|
|
105046
105262
|
// Simulate DRA unioning district shapes in the background
|
|
105047
105263
|
function addToPoly(poly, polys) {
|
|
105048
|
-
//
|
|
105049
|
-
|
|
105050
|
-
let
|
|
105051
|
-
if (union
|
|
105052
|
-
|
|
105053
|
-
|
|
105264
|
+
// Terry's workaround
|
|
105265
|
+
let _union = undefined;
|
|
105266
|
+
let anyPC = PC;
|
|
105267
|
+
if (anyPC.union)
|
|
105268
|
+
_union = anyPC.union;
|
|
105269
|
+
if (anyPC.default.union)
|
|
105270
|
+
_union = anyPC.default.union;
|
|
105271
|
+
if (_union === undefined)
|
|
105272
|
+
throw 'Unable to load union function from polygon-clipping';
|
|
105273
|
+
return _union(poly, ...polys);
|
|
105054
105274
|
}
|
|
105055
105275
|
console.log("Starting command @ ", new Date());
|
|
105056
105276
|
// COMMAND LINE
|
|
@@ -105156,7 +105376,7 @@ let sessionSettings = {
|
|
|
105156
105376
|
let bLog = argv.verbose;
|
|
105157
105377
|
// Invert the plan, so you can create the district shapes for the SessionRequest.
|
|
105158
105378
|
// NOTE - The plan here is complete (all features assigned).
|
|
105159
|
-
let planByDistrictID = D.invertPlan(planByGeoID);
|
|
105379
|
+
let planByDistrictID = D.invertPlan(planByGeoID, undefined, bLog);
|
|
105160
105380
|
// SIMULATE THE HOST
|
|
105161
105381
|
// Create district shapes & extract properties (area, diameter, perimeter)
|
|
105162
105382
|
// Index the shapes by geoID
|
|
@@ -105494,6 +105714,17 @@ module.exports = require("stream");
|
|
|
105494
105714
|
|
|
105495
105715
|
/***/ }),
|
|
105496
105716
|
|
|
105717
|
+
/***/ "util":
|
|
105718
|
+
/*!***********************!*\
|
|
105719
|
+
!*** external "util" ***!
|
|
105720
|
+
\***********************/
|
|
105721
|
+
/*! no static exports found */
|
|
105722
|
+
/***/ (function(module, exports) {
|
|
105723
|
+
|
|
105724
|
+
module.exports = require("util");
|
|
105725
|
+
|
|
105726
|
+
/***/ }),
|
|
105727
|
+
|
|
105497
105728
|
/***/ "yargs":
|
|
105498
105729
|
/*!************************!*\
|
|
105499
105730
|
!*** external "yargs" ***!
|