@dra2020/district-analytics 2.0.2 → 2.0.5
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 +58 -25
- package/dist/cli.js.map +1 -1
- package/dist/district-analytics.js +58 -25
- package/dist/district-analytics.js.map +1 -1
- package/package.json +1 -1
- package/dist/src/constants.d.ts +0 -6
- package/dist/src/report.d.ts +0 -0
|
@@ -139,6 +139,13 @@ class AnalyticsSession {
|
|
|
139
139
|
this.features = new D.Features(this, SessionRequest['data'], this.config['datasets']);
|
|
140
140
|
this.plan = new D.Plan(this, SessionRequest['plan']);
|
|
141
141
|
this.districts = new D.Districts(this, SessionRequest['districtShapes']);
|
|
142
|
+
// TODO - Confirming that the graph includes OUT_OF_STATE neighbors
|
|
143
|
+
// if (U.keyExists(S.OUT_OF_STATE, this.graph._graph)) {
|
|
144
|
+
// console.log("Contiguity graph includes out-of-state neighbors.");
|
|
145
|
+
// }
|
|
146
|
+
// else {
|
|
147
|
+
// console.log("Contiguity graph does NOT include out-of-state neighbors.");
|
|
148
|
+
// }
|
|
142
149
|
// NOTE: I've pulled these out of the individual analytics to here. Eventually,
|
|
143
150
|
// we could want them to passed into an analytics session as data, along with
|
|
144
151
|
// everything else. For now, this keeps branching out of the main code.
|
|
@@ -248,6 +255,9 @@ const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"
|
|
|
248
255
|
const valid_1 = __webpack_require__(/*! ./valid */ "./src/valid.ts");
|
|
249
256
|
const compact_1 = __webpack_require__(/*! ./compact */ "./src/compact.ts");
|
|
250
257
|
const political_1 = __webpack_require__(/*! ./political */ "./src/political.ts");
|
|
258
|
+
// DEBUG COUNTERS
|
|
259
|
+
let nMissingDataset = 0;
|
|
260
|
+
let nMissingProperty = 0;
|
|
251
261
|
// DISTRICT STATISTICS
|
|
252
262
|
// Indexes for statistics fields in Districts
|
|
253
263
|
// NOTE - Not a const, so the number can be determined dynamically
|
|
@@ -332,6 +342,9 @@ class Districts {
|
|
|
332
342
|
// TODO - OPTIMIZE by async'ing this?
|
|
333
343
|
// TODO - Is there a way to do this programmatically off data? Does it matter?
|
|
334
344
|
recalcStatistics(bLog = false) {
|
|
345
|
+
// Initialize debug counters
|
|
346
|
+
nMissingDataset = 0;
|
|
347
|
+
nMissingProperty = 0;
|
|
335
348
|
// Compute these once per recalc cycle
|
|
336
349
|
let targetSize = this._session.state.totalPop / this._session.state.nDistricts;
|
|
337
350
|
let deviationThreshold = this._session.populationDeviationThreshold();
|
|
@@ -429,8 +442,8 @@ class Districts {
|
|
|
429
442
|
});
|
|
430
443
|
// COMPUTE DERIVED VALUES
|
|
431
444
|
// Population deviation % and equal population (boolean) by district.
|
|
432
|
-
//
|
|
433
|
-
let popDevPct =
|
|
445
|
+
// Default the value for the dummy unassigned district to 0%.
|
|
446
|
+
let popDevPct = 0 / targetSize;
|
|
434
447
|
if (i > 0) {
|
|
435
448
|
popDevPct = (totalPop - targetSize) / targetSize;
|
|
436
449
|
bEqualPop = (popDevPct <= deviationThreshold);
|
|
@@ -569,6 +582,8 @@ class Districts {
|
|
|
569
582
|
this.statistics[DistrictField.AsianPct][summaryRow] = stateAsianPop / stateVAPPop;
|
|
570
583
|
this.statistics[DistrictField.NativePct][summaryRow] = stateNativePop / stateVAPPop;
|
|
571
584
|
}
|
|
585
|
+
console.log(`${nMissingDataset} features with missing datasets.`);
|
|
586
|
+
console.log(`${nMissingProperty} features with missing properties.`);
|
|
572
587
|
}
|
|
573
588
|
// NOTE - I did not roll these into district statistics, because creating the
|
|
574
589
|
// district shapes themselves is the big district-by-district activity, these
|
|
@@ -623,36 +638,46 @@ exports.Features = Features;
|
|
|
623
638
|
// f is a direct GeoJSON feature
|
|
624
639
|
// p is a geoID
|
|
625
640
|
function _getFeatures(f, datasetKey, p) {
|
|
626
|
-
// Echo parameters for debugging
|
|
627
|
-
// console.log("f =", f, "k = ", datasetKey, "p =", p);
|
|
628
641
|
// Shim to load sample data2.json from disk for command-line scaffolding
|
|
629
642
|
if (f.properties && f.properties['datasets']) {
|
|
643
|
+
if (!f.properties['datasets'][datasetKey]) {
|
|
644
|
+
// Feature is missing the dataset
|
|
645
|
+
nMissingDataset += 1;
|
|
646
|
+
console.log(`${nMissingDataset}: Data ${datasetKey} missing for feature ${f} Returning zero.`);
|
|
647
|
+
return 0;
|
|
648
|
+
}
|
|
630
649
|
return f.properties['datasets'][datasetKey][p];
|
|
631
650
|
}
|
|
632
651
|
// NOTE - The fGetW() code from dra-client below here ...
|
|
633
652
|
// Direct property?
|
|
634
|
-
if (f.properties && f.properties[p] !== undefined)
|
|
653
|
+
if (f.properties && f.properties[p] !== undefined) {
|
|
635
654
|
return f.properties[p];
|
|
655
|
+
}
|
|
636
656
|
// Joined property?
|
|
637
657
|
let a = _fGetJoined(f);
|
|
638
658
|
if (a) {
|
|
639
659
|
for (let i = 0; i < a.length; i++) {
|
|
640
660
|
let o = a[i];
|
|
641
661
|
if (!datasetKey) {
|
|
642
|
-
if (o[p] !== undefined)
|
|
662
|
+
if (o[p] !== undefined) {
|
|
643
663
|
return o[p];
|
|
664
|
+
}
|
|
644
665
|
}
|
|
645
666
|
else {
|
|
646
667
|
if (o['datasets'] && o['datasets'][datasetKey]) {
|
|
647
668
|
let v = (o['datasets'][datasetKey][p]);
|
|
648
|
-
if ((!(v == null)) && (!(v == undefined)))
|
|
669
|
+
if ((!(v == null)) && (!(v == undefined))) {
|
|
649
670
|
return o['datasets'][datasetKey][p];
|
|
671
|
+
}
|
|
650
672
|
}
|
|
651
673
|
}
|
|
652
674
|
}
|
|
653
675
|
}
|
|
654
|
-
|
|
655
|
-
|
|
676
|
+
// Feature is missing the property
|
|
677
|
+
nMissingProperty += 1;
|
|
678
|
+
console.log(`${nMissingProperty}: ${p} value undefined for ${f.properties['GEOID10']}. Returning zero.`);
|
|
679
|
+
return 0;
|
|
680
|
+
// return undefined;
|
|
656
681
|
}
|
|
657
682
|
function _fGetJoined(f) {
|
|
658
683
|
return (f.properties && f.properties.joined) ? f.properties.joined : undefined;
|
|
@@ -697,6 +722,7 @@ class Plan {
|
|
|
697
722
|
}
|
|
698
723
|
// NOTE - DON'T remove water-only features from the plan, as they may be required
|
|
699
724
|
// for contiguity. Just skip them in aggregating district statistics.
|
|
725
|
+
//
|
|
700
726
|
// removeWaterOnlyFeatures(plan: T.PlanByGeoID): T.PlanByGeoID {
|
|
701
727
|
// let newPlan = {} as T.PlanByGeoID;
|
|
702
728
|
// for (let geoID in plan) {
|
|
@@ -1200,7 +1226,7 @@ exports.doFindCountiesSplitUnexpectedly = doFindCountiesSplitUnexpectedly;
|
|
|
1200
1226
|
function doFindSplitVTDs(s, bLog = false) {
|
|
1201
1227
|
let test = s.getTest(10 /* VTDSplits */);
|
|
1202
1228
|
let splitVTDs = [];
|
|
1203
|
-
// TODO - SPLITTING: Flesh this out, using
|
|
1229
|
+
// TODO - SPLITTING: Flesh this out, using virtual VTD's ...
|
|
1204
1230
|
test['score'] = splitVTDs.length;
|
|
1205
1231
|
test['details']['splitVTDs'] = splitVTDs;
|
|
1206
1232
|
return test;
|
|
@@ -1447,17 +1473,22 @@ const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
|
1447
1473
|
const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
1448
1474
|
function doPopulationDeviation(s, bLog = false) {
|
|
1449
1475
|
let test = s.getTest(4 /* PopulationDeviation */);
|
|
1450
|
-
|
|
1451
|
-
//
|
|
1476
|
+
let targetSize = s.state.totalPop / s.state.nDistricts;
|
|
1477
|
+
// Compute the min & max district populations
|
|
1478
|
+
// ... excluding the dummy the 'unassigned' 0 and N+1 summary "districts"
|
|
1452
1479
|
let totPopByDistrict = s.districts.statistics[D.DistrictField.TotalPop];
|
|
1453
1480
|
totPopByDistrict = totPopByDistrict.slice(1, -1);
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
let
|
|
1457
|
-
|
|
1458
|
-
//
|
|
1459
|
-
|
|
1460
|
-
|
|
1481
|
+
// Remove empty districts
|
|
1482
|
+
totPopByDistrict = totPopByDistrict.filter(x => x > 0);
|
|
1483
|
+
let min = 0;
|
|
1484
|
+
let max = 0;
|
|
1485
|
+
// If there's more than 1 non-empty district, calculate a non-zero deviation
|
|
1486
|
+
if (totPopByDistrict.length > 1) {
|
|
1487
|
+
min = U.minArray(totPopByDistrict);
|
|
1488
|
+
max = U.maxArray(totPopByDistrict);
|
|
1489
|
+
}
|
|
1490
|
+
// Calculate the raw population deviation
|
|
1491
|
+
let popDev = (max - min) / targetSize;
|
|
1461
1492
|
// Round the raw value to the desired level of precision
|
|
1462
1493
|
popDev = U.trim(popDev);
|
|
1463
1494
|
// Populate the test entry
|
|
@@ -1467,7 +1498,7 @@ function doPopulationDeviation(s, bLog = false) {
|
|
|
1467
1498
|
let totalPop = s.districts.statistics[D.DistrictField.TotalPop];
|
|
1468
1499
|
let popDevPct = s.districts.statistics[D.DistrictField.PopDevPct];
|
|
1469
1500
|
let summaryRow = s.districts.numberOfRows() - 1;
|
|
1470
|
-
totalPop[summaryRow] =
|
|
1501
|
+
totalPop[summaryRow] = targetSize;
|
|
1471
1502
|
popDevPct[summaryRow] = popDev;
|
|
1472
1503
|
return test;
|
|
1473
1504
|
}
|
|
@@ -1855,9 +1886,9 @@ function doPreprocessCensus(s, bLog = false) {
|
|
|
1855
1886
|
// Sum total population by county
|
|
1856
1887
|
totalByCounty[countyFIPS] += value;
|
|
1857
1888
|
}
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1889
|
+
else {
|
|
1890
|
+
console.log("Skipping water-only feature in Census preprocessing:", geoID);
|
|
1891
|
+
}
|
|
1861
1892
|
}
|
|
1862
1893
|
// NOTE - The above could be replaced, if I got totals on county.geojson.
|
|
1863
1894
|
// CREATE A FIPS CODE-ORDINAL MAP
|
|
@@ -2455,8 +2486,10 @@ const testDefns = {
|
|
|
2455
2486
|
// Raw numeric analytics, such as population deviation, compactness, etc. are
|
|
2456
2487
|
// normalized as part of creating a scorecard, so the code to normalize results
|
|
2457
2488
|
// is encapsulated here.
|
|
2458
|
-
// Configure scale parameters for normalizing each raw test result
|
|
2459
|
-
//
|
|
2489
|
+
// Configure scale parameters for normalizing each raw test result.
|
|
2490
|
+
// Scales consist of a minimum & a maximum *raw* value. If the values get
|
|
2491
|
+
// inverted (to make bigger better), these will switch.
|
|
2492
|
+
// This process needs to be separate from the test configuration info above,
|
|
2460
2493
|
// because some scales need access to the analytics session object.
|
|
2461
2494
|
function doConfigureScales(s) {
|
|
2462
2495
|
// Scale defn for PopulationDeviation
|