@dra2020/district-analytics 2.0.6 → 2.0.9

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/cli.js CHANGED
@@ -8055,6 +8055,7 @@ const preprocess_1 = __webpack_require__(/*! ./preprocess */ "./src/preprocess.t
8055
8055
  const analyze_1 = __webpack_require__(/*! ./analyze */ "./src/analyze.ts");
8056
8056
  const results_1 = __webpack_require__(/*! ./results */ "./src/results.ts");
8057
8057
  const results_2 = __webpack_require__(/*! ./results */ "./src/results.ts");
8058
+ const geofeature_1 = __webpack_require__(/*! ./geofeature */ "./src/geofeature.ts");
8058
8059
  const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
8059
8060
  const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
8060
8061
  class AnalyticsSession {
@@ -8127,19 +8128,82 @@ class AnalyticsSession {
8127
8128
  // NOTE - This assumes that analyzePlan() has been run!
8128
8129
  getDiscontiguousDistrictFeatures(bLog = false) {
8129
8130
  // Get the (possibly empty) list of discontiguous district IDs
8130
- let contiguousTest = this.getTest(1 /* Contiguous */);
8131
- let discontiguousDistrictIDs = contiguousTest['details']['discontiguousDistricts'] || [];
8131
+ const contiguousTest = this.getTest(1 /* Contiguous */);
8132
+ const discontiguousDistrictIDs = contiguousTest['details']['discontiguousDistricts'] || [];
8132
8133
  // Convert them into a (possibly empty) list of features
8133
8134
  let discontiguousDistrictFeatures = { type: 'FeatureCollection', features: [] };
8134
8135
  if (!(U.isArrayEmpty(discontiguousDistrictIDs))) {
8135
8136
  for (let id of discontiguousDistrictIDs) {
8136
8137
  let poly = this.districts.getDistrictShapeByID(id);
8137
- if (poly)
8138
- discontiguousDistrictFeatures.features.push(poly);
8138
+ if (poly) {
8139
+ // If a district has a shape & it is not contiguous, by definition,
8140
+ // it will be a Multipolygon, i.e., it will have multiple pieces, some
8141
+ // possibly embedded w/in other districts. Get & add all the pieces.
8142
+ const districtParts = geofeature_1.polyParts(poly);
8143
+ discontiguousDistrictFeatures.features.push(...districtParts.features);
8144
+ // discontiguousDistrictFeatures.features.push(poly);
8145
+ }
8139
8146
  }
8140
8147
  }
8141
8148
  return discontiguousDistrictFeatures;
8142
8149
  }
8150
+ // Comments clipped from dra-client geodistrict.ts.
8151
+ // Discontiguous polygons are:
8152
+ // 1. All polygons in a multi-polygon; and
8153
+ // 2. All holes in a otherwise cohesive polygon.
8154
+ // Note that all non-cohesive features are always simple polygons.
8155
+ /*
8156
+ let i: number, j: number;
8157
+ let nPoly: number = 0;
8158
+ for (i = 0;nPoly == 0 && i < this.cacheDistricts.features.length;i++)
8159
+ {
8160
+ let f = this.cacheDistricts.features[i];
8161
+
8162
+ if (f.geometry.type === 'MultiPolygon')
8163
+ nPoly += f.geometry.coordinates.length;
8164
+ else if (f.geometry.type === 'Polygon' && f.geometry.coordinates.length)
8165
+ nPoly += (f.geometry.coordinates.length - 1);
8166
+ }
8167
+ if (nPoly)
8168
+ {
8169
+ this.cacheNoncohesive = {type: 'FeatureCollection', features: []};
8170
+ let af: any = this.cacheNoncohesive.features;
8171
+ let oUnique: any = {};
8172
+
8173
+ // First add discontiguous polygons
8174
+ for (i = 0;i < this.cacheDistricts.features.length;i++)
8175
+ {
8176
+ let f = this.cacheDistricts.features[i];
8177
+
8178
+ if (f.geometry.type === 'MultiPolygon')
8179
+ {
8180
+ // Push all non-contiguous polygons
8181
+ for (j = 0;j < f.geometry.coordinates.length;j++)
8182
+ {
8183
+ let p: any = f.geometry.coordinates[j];
8184
+ oUnique[Hash.qhash(p[0])] = true;
8185
+ af.push({type: 'Feature', properties: {id: `${af.length + 1}`}, geometry: {type: 'Polygon', coordinates: p}});
8186
+ }
8187
+ }
8188
+ }
8189
+
8190
+ // Now add unique holes
8191
+ for (i = 0;i < this.cacheDistricts.features.length;i++)
8192
+ {
8193
+ let f = this.cacheDistricts.features[i];
8194
+
8195
+ if (f.geometry.type === 'Polygon')
8196
+ {
8197
+ // Push all holes from this polygon
8198
+ for (j = 1;j < f.geometry.coordinates.length;j++)
8199
+ {
8200
+ let p: any = f.geometry.coordinates[j];
8201
+ if (oUnique[Hash.qhash(p)] === undefined)
8202
+ af.push({type: 'Feature', properties: {id: `${af.length + 1}`}, geometry: {type: 'Polygon', coordinates: [p]}});
8203
+ }
8204
+ }
8205
+ }
8206
+ } */
8143
8207
  // HELPERS USED INTERNALLY
8144
8208
  // Get an individual test, so you can drive UI with the results.
8145
8209
  getTest(testID) {
@@ -8331,6 +8395,9 @@ class Districts {
8331
8395
  // 3 - MORE ...
8332
8396
  // HACK - Because "this" gets ghosted inside the forEach loop below
8333
8397
  let outerThis = this;
8398
+ // Default the pop dev % for the dummy Unassigned district to 0%.
8399
+ // Default the pop dev % for real (1–N) but empty districts to 100%.
8400
+ let popDevPct = (i > 0) ? (targetSize / targetSize) : 0 / targetSize;
8334
8401
  // Get the geoIDs assigned to the district
8335
8402
  // Guard against empty districts
8336
8403
  let geoIDs = this._session.plan.geoIDsForDistrictID(i);
@@ -8377,11 +8444,11 @@ class Districts {
8377
8444
  });
8378
8445
  // COMPUTE DERIVED VALUES
8379
8446
  // Population deviation % and equal population (boolean) by district.
8380
- // Default the value for the dummy unassigned district to 0%.
8381
- let popDevPct = 0 / targetSize;
8382
8447
  if (i > 0) {
8383
- popDevPct = (totalPop - targetSize) / targetSize;
8384
- bEqualPop = (popDevPct <= deviationThreshold);
8448
+ if (totalPop > 0) {
8449
+ popDevPct = (totalPop - targetSize) / targetSize;
8450
+ bEqualPop = (Math.abs(popDevPct) <= deviationThreshold);
8451
+ }
8385
8452
  }
8386
8453
  // Total two-party (not total total!) votes, Democratic and Republican vote
8387
8454
  // shares, and Democratic first-past-the-post win (= 1) or loss (= 0).
@@ -8470,7 +8537,7 @@ class Districts {
8470
8537
  }
8471
8538
  }
8472
8539
  else { // If a district is empty, zero these results (vs. null)
8473
- this.statistics[DistrictField.PopDevPct][i] = 0.0;
8540
+ this.statistics[DistrictField.PopDevPct][i] = popDevPct;
8474
8541
  this.statistics[DistrictField.DemVotes][i] = 0;
8475
8542
  this.statistics[DistrictField.RepVotes][i] = 0;
8476
8543
  this.statistics[DistrictField.TwoPartyVote][i] = 0;
@@ -9487,6 +9554,23 @@ var __importStar = (this && this.__importStar) || function (mod) {
9487
9554
  };
9488
9555
  Object.defineProperty(exports, "__esModule", { value: true });
9489
9556
  const Poly = __importStar(__webpack_require__(/*! @dra2020/poly */ "./node_modules/@dra2020/poly/dist/poly.js"));
9557
+ // HELPER
9558
+ function polyParts(poly) {
9559
+ let parts = { type: 'FeatureCollection', features: [] };
9560
+ let af = parts.features;
9561
+ if (poly.geometry.type === 'MultiPolygon') {
9562
+ // Push all non-contiguous polygons
9563
+ for (let j = 0; j < poly.geometry.coordinates.length; j++) {
9564
+ let onePoly = poly.geometry.coordinates[j];
9565
+ af.push({ type: 'Feature', properties: { id: `${af.length + 1}` }, geometry: { type: 'Polygon', coordinates: onePoly } });
9566
+ }
9567
+ }
9568
+ else {
9569
+ parts.features.push(poly);
9570
+ }
9571
+ return parts;
9572
+ }
9573
+ exports.polyParts = polyParts;
9490
9574
  // CARTESIAN SHIMS OVER 'POLY' FUNCTIONS
9491
9575
  // TODO - POLY: Confirm Cartesian calculations
9492
9576
  function gfArea(poly) {