@dra2020/district-analytics 7.1.6 → 8.0.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2020 Alec Ramsay
3
+ Copyright (c) 2020 Dave's Redistricting, LLC.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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 shift = config_json_1.default.minority.shift;
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 offset = 1; // Don't process 'White'
89361
- const nDemos = 6 /* Total */; // Ditto
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 + 1); // Add a row for the 'Total' row
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
- // Accumulate the proportional values for each demographic, ignoring the first
89375
- // all Minority demographic.
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 proportion = U.deepCopy(demosByDistrict[i][j + offset]); // Skip the 'White' entries
89388
- if (exceedsMinimumThreshold(proportion)) {
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
- const bucket = (exceedsMaximumThreshold(proportion)) ? 1 : 0;
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(proportion);
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 = bucketsByDemo[6 /* Total */][4 /* PropSeats */];
89409
- const pCd = bucketsByDemo[0 /* Minority */][4 /* PropSeats */];
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
- const shift = C.minorityShift();
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 exceedsMinimumThreshold(Mf) {
89466
- const threshold = C.minorityOpportunityRange()[C.BEG];
89467
- return Mf >= threshold;
89468
- }
89469
- function exceedsMaximumThreshold(Mf) {
89470
- const threshold = C.minorityOpportunityRange()[C.END];
89471
- return Mf > threshold;
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
- this.fsmUnion = new Q.FsmQuadTree(this.env, this.options, polys);
95425
- this.waitOn(this.fsmUnion);
95426
- this.setState(FSM_UNION);
95427
- this.map = map;
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
  /******/ });
@@ -103221,6 +103422,8 @@ class Features {
103221
103422
  this._session = s;
103222
103423
  this._data = data;
103223
103424
  this._keys = keys;
103425
+ // For debugging PVI
103426
+ // console.log("Election dataset =", this._keys[Dataset.ELECTION]);
103224
103427
  }
103225
103428
  nFeatures() { return this._data.features.length; }
103226
103429
  featureByIndex(i) { return this._data.features[i]; }
@@ -103231,8 +103434,15 @@ class Features {
103231
103434
  return value;
103232
103435
  }
103233
103436
  fieldForFeature(f, dt, fk) {
103234
- let dk = this._keys[dt];
103235
- return _getFeatures(f, dk, fk);
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;
103236
103446
  }
103237
103447
  resetDataset(d, k) {
103238
103448
  this._keys[d] = k;
@@ -103795,8 +104005,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
103795
104005
  Object.defineProperty(exports, "__esModule", { value: true });
103796
104006
  const majority_minority_json_1 = __importDefault(__webpack_require__(/*! ../static/majority-minority.json */ "./static/majority-minority.json"));
103797
104007
  const vra5_preclearance_json_1 = __importDefault(__webpack_require__(/*! ../static/vra5-preclearance.json */ "./static/vra5-preclearance.json"));
103798
- // import stateContacts from '../static/state-contacts.json';
103799
- // import demographicDefs from '../static/demographic-defns.json';
103800
104008
  // TODO - 2020: Update/revise this, when the update comes out in September:
103801
104009
  // Sources for majority-minority info:
103802
104010
  // - https://en.wikipedia.org/wiki/List_of_majority-minority_United_States_congressional_districts
@@ -105051,12 +105259,16 @@ const S = __importStar(__webpack_require__(/*! ../src/settings */ "./src/setting
105051
105259
  const D = __importStar(__webpack_require__(/*! ../src/_data */ "./src/_data.ts"));
105052
105260
  // Simulate DRA unioning district shapes in the background
105053
105261
  function addToPoly(poly, polys) {
105054
- // TODO - POLY: Fix 'poly' import, so I don't have to do this workaround.
105055
- // return PC.union(poly, ...polys);
105056
- let union = PC.union;
105057
- if (union === undefined)
105058
- union = PC.default.union;
105059
- return union(poly, ...polys);
105262
+ // Terry's workaround
105263
+ let _union = undefined;
105264
+ let anyPC = PC;
105265
+ if (anyPC.union)
105266
+ _union = anyPC.union;
105267
+ if (anyPC.default.union)
105268
+ _union = anyPC.default.union;
105269
+ if (_union === undefined)
105270
+ throw 'Unable to load union function from polygon-clipping';
105271
+ return _union(poly, ...polys);
105060
105272
  }
105061
105273
  console.log("Starting command @ ", new Date());
105062
105274
  // COMMAND LINE
@@ -105500,6 +105712,17 @@ module.exports = require("stream");
105500
105712
 
105501
105713
  /***/ }),
105502
105714
 
105715
+ /***/ "util":
105716
+ /*!***********************!*\
105717
+ !*** external "util" ***!
105718
+ \***********************/
105719
+ /*! no static exports found */
105720
+ /***/ (function(module, exports) {
105721
+
105722
+ module.exports = require("util");
105723
+
105724
+ /***/ }),
105725
+
105503
105726
  /***/ "yargs":
105504
105727
  /*!************************!*\
105505
105728
  !*** external "yargs" ***!