@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 +93 -9
- package/dist/cli.js.map +1 -1
- package/dist/district-analytics.js +93 -9
- package/dist/district-analytics.js.map +1 -1
- package/dist/src/geofeature.d.ts +2 -0
- package/dist/src/score.d.ts +4 -0
- package/package.json +1 -1
|
@@ -120,6 +120,7 @@ const preprocess_1 = __webpack_require__(/*! ./preprocess */ "./src/preprocess.t
|
|
|
120
120
|
const analyze_1 = __webpack_require__(/*! ./analyze */ "./src/analyze.ts");
|
|
121
121
|
const results_1 = __webpack_require__(/*! ./results */ "./src/results.ts");
|
|
122
122
|
const results_2 = __webpack_require__(/*! ./results */ "./src/results.ts");
|
|
123
|
+
const geofeature_1 = __webpack_require__(/*! ./geofeature */ "./src/geofeature.ts");
|
|
123
124
|
const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
124
125
|
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
125
126
|
class AnalyticsSession {
|
|
@@ -192,19 +193,82 @@ class AnalyticsSession {
|
|
|
192
193
|
// NOTE - This assumes that analyzePlan() has been run!
|
|
193
194
|
getDiscontiguousDistrictFeatures(bLog = false) {
|
|
194
195
|
// Get the (possibly empty) list of discontiguous district IDs
|
|
195
|
-
|
|
196
|
-
|
|
196
|
+
const contiguousTest = this.getTest(1 /* Contiguous */);
|
|
197
|
+
const discontiguousDistrictIDs = contiguousTest['details']['discontiguousDistricts'] || [];
|
|
197
198
|
// Convert them into a (possibly empty) list of features
|
|
198
199
|
let discontiguousDistrictFeatures = { type: 'FeatureCollection', features: [] };
|
|
199
200
|
if (!(U.isArrayEmpty(discontiguousDistrictIDs))) {
|
|
200
201
|
for (let id of discontiguousDistrictIDs) {
|
|
201
202
|
let poly = this.districts.getDistrictShapeByID(id);
|
|
202
|
-
if (poly)
|
|
203
|
-
|
|
203
|
+
if (poly) {
|
|
204
|
+
// If a district has a shape & it is not contiguous, by definition,
|
|
205
|
+
// it will be a Multipolygon, i.e., it will have multiple pieces, some
|
|
206
|
+
// possibly embedded w/in other districts. Get & add all the pieces.
|
|
207
|
+
const districtParts = geofeature_1.polyParts(poly);
|
|
208
|
+
discontiguousDistrictFeatures.features.push(...districtParts.features);
|
|
209
|
+
// discontiguousDistrictFeatures.features.push(poly);
|
|
210
|
+
}
|
|
204
211
|
}
|
|
205
212
|
}
|
|
206
213
|
return discontiguousDistrictFeatures;
|
|
207
214
|
}
|
|
215
|
+
// Comments clipped from dra-client geodistrict.ts.
|
|
216
|
+
// Discontiguous polygons are:
|
|
217
|
+
// 1. All polygons in a multi-polygon; and
|
|
218
|
+
// 2. All holes in a otherwise cohesive polygon.
|
|
219
|
+
// Note that all non-cohesive features are always simple polygons.
|
|
220
|
+
/*
|
|
221
|
+
let i: number, j: number;
|
|
222
|
+
let nPoly: number = 0;
|
|
223
|
+
for (i = 0;nPoly == 0 && i < this.cacheDistricts.features.length;i++)
|
|
224
|
+
{
|
|
225
|
+
let f = this.cacheDistricts.features[i];
|
|
226
|
+
|
|
227
|
+
if (f.geometry.type === 'MultiPolygon')
|
|
228
|
+
nPoly += f.geometry.coordinates.length;
|
|
229
|
+
else if (f.geometry.type === 'Polygon' && f.geometry.coordinates.length)
|
|
230
|
+
nPoly += (f.geometry.coordinates.length - 1);
|
|
231
|
+
}
|
|
232
|
+
if (nPoly)
|
|
233
|
+
{
|
|
234
|
+
this.cacheNoncohesive = {type: 'FeatureCollection', features: []};
|
|
235
|
+
let af: any = this.cacheNoncohesive.features;
|
|
236
|
+
let oUnique: any = {};
|
|
237
|
+
|
|
238
|
+
// First add discontiguous polygons
|
|
239
|
+
for (i = 0;i < this.cacheDistricts.features.length;i++)
|
|
240
|
+
{
|
|
241
|
+
let f = this.cacheDistricts.features[i];
|
|
242
|
+
|
|
243
|
+
if (f.geometry.type === 'MultiPolygon')
|
|
244
|
+
{
|
|
245
|
+
// Push all non-contiguous polygons
|
|
246
|
+
for (j = 0;j < f.geometry.coordinates.length;j++)
|
|
247
|
+
{
|
|
248
|
+
let p: any = f.geometry.coordinates[j];
|
|
249
|
+
oUnique[Hash.qhash(p[0])] = true;
|
|
250
|
+
af.push({type: 'Feature', properties: {id: `${af.length + 1}`}, geometry: {type: 'Polygon', coordinates: p}});
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Now add unique holes
|
|
256
|
+
for (i = 0;i < this.cacheDistricts.features.length;i++)
|
|
257
|
+
{
|
|
258
|
+
let f = this.cacheDistricts.features[i];
|
|
259
|
+
|
|
260
|
+
if (f.geometry.type === 'Polygon')
|
|
261
|
+
{
|
|
262
|
+
// Push all holes from this polygon
|
|
263
|
+
for (j = 1;j < f.geometry.coordinates.length;j++)
|
|
264
|
+
{
|
|
265
|
+
let p: any = f.geometry.coordinates[j];
|
|
266
|
+
if (oUnique[Hash.qhash(p)] === undefined)
|
|
267
|
+
af.push({type: 'Feature', properties: {id: `${af.length + 1}`}, geometry: {type: 'Polygon', coordinates: [p]}});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
} */
|
|
208
272
|
// HELPERS USED INTERNALLY
|
|
209
273
|
// Get an individual test, so you can drive UI with the results.
|
|
210
274
|
getTest(testID) {
|
|
@@ -396,6 +460,9 @@ class Districts {
|
|
|
396
460
|
// 3 - MORE ...
|
|
397
461
|
// HACK - Because "this" gets ghosted inside the forEach loop below
|
|
398
462
|
let outerThis = this;
|
|
463
|
+
// Default the pop dev % for the dummy Unassigned district to 0%.
|
|
464
|
+
// Default the pop dev % for real (1–N) but empty districts to 100%.
|
|
465
|
+
let popDevPct = (i > 0) ? (targetSize / targetSize) : 0 / targetSize;
|
|
399
466
|
// Get the geoIDs assigned to the district
|
|
400
467
|
// Guard against empty districts
|
|
401
468
|
let geoIDs = this._session.plan.geoIDsForDistrictID(i);
|
|
@@ -442,11 +509,11 @@ class Districts {
|
|
|
442
509
|
});
|
|
443
510
|
// COMPUTE DERIVED VALUES
|
|
444
511
|
// Population deviation % and equal population (boolean) by district.
|
|
445
|
-
// Default the value for the dummy unassigned district to 0%.
|
|
446
|
-
let popDevPct = 0 / targetSize;
|
|
447
512
|
if (i > 0) {
|
|
448
|
-
|
|
449
|
-
|
|
513
|
+
if (totalPop > 0) {
|
|
514
|
+
popDevPct = (totalPop - targetSize) / targetSize;
|
|
515
|
+
bEqualPop = (Math.abs(popDevPct) <= deviationThreshold);
|
|
516
|
+
}
|
|
450
517
|
}
|
|
451
518
|
// Total two-party (not total total!) votes, Democratic and Republican vote
|
|
452
519
|
// shares, and Democratic first-past-the-post win (= 1) or loss (= 0).
|
|
@@ -535,7 +602,7 @@ class Districts {
|
|
|
535
602
|
}
|
|
536
603
|
}
|
|
537
604
|
else { // If a district is empty, zero these results (vs. null)
|
|
538
|
-
this.statistics[DistrictField.PopDevPct][i] =
|
|
605
|
+
this.statistics[DistrictField.PopDevPct][i] = popDevPct;
|
|
539
606
|
this.statistics[DistrictField.DemVotes][i] = 0;
|
|
540
607
|
this.statistics[DistrictField.RepVotes][i] = 0;
|
|
541
608
|
this.statistics[DistrictField.TwoPartyVote][i] = 0;
|
|
@@ -1552,6 +1619,23 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
1552
1619
|
};
|
|
1553
1620
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1554
1621
|
const Poly = __importStar(__webpack_require__(/*! @dra2020/poly */ "@dra2020/poly"));
|
|
1622
|
+
// HELPER
|
|
1623
|
+
function polyParts(poly) {
|
|
1624
|
+
let parts = { type: 'FeatureCollection', features: [] };
|
|
1625
|
+
let af = parts.features;
|
|
1626
|
+
if (poly.geometry.type === 'MultiPolygon') {
|
|
1627
|
+
// Push all non-contiguous polygons
|
|
1628
|
+
for (let j = 0; j < poly.geometry.coordinates.length; j++) {
|
|
1629
|
+
let onePoly = poly.geometry.coordinates[j];
|
|
1630
|
+
af.push({ type: 'Feature', properties: { id: `${af.length + 1}` }, geometry: { type: 'Polygon', coordinates: onePoly } });
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
else {
|
|
1634
|
+
parts.features.push(poly);
|
|
1635
|
+
}
|
|
1636
|
+
return parts;
|
|
1637
|
+
}
|
|
1638
|
+
exports.polyParts = polyParts;
|
|
1555
1639
|
// CARTESIAN SHIMS OVER 'POLY' FUNCTIONS
|
|
1556
1640
|
// TODO - POLY: Confirm Cartesian calculations
|
|
1557
1641
|
function gfArea(poly) {
|