@dra2020/district-analytics 15.5.0 → 16.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.
|
@@ -54,6 +54,7 @@ const minority_1 = __webpack_require__(/*! ./minority */ "./src/minority.ts");
|
|
|
54
54
|
const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
55
55
|
const M = __importStar(__webpack_require__(/*! ./minority */ "./src/minority.ts"));
|
|
56
56
|
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
57
|
+
// MMD - Extended for optional # reps per district
|
|
57
58
|
class AnalyticsSession {
|
|
58
59
|
constructor(SessionRequest) {
|
|
59
60
|
this.legislativeDistricts = false; // 2020
|
|
@@ -79,6 +80,17 @@ class AnalyticsSession {
|
|
|
79
80
|
this.features = new D.Features(this, SessionRequest['data'], this.config['datasets']);
|
|
80
81
|
this.plan = new D.Plan(this, SessionRequest['plan']);
|
|
81
82
|
this.districts = new D.Districts(this, SessionRequest['districtShapes']);
|
|
83
|
+
// MMD - Validate & handle optional # reps per district
|
|
84
|
+
const repsByDistrictt = SessionRequest['repsByDistrictt'];
|
|
85
|
+
const nDistricts = this.state.nDistricts;
|
|
86
|
+
if (repsByDistrictt !== undefined) {
|
|
87
|
+
if (repsByDistrictt.length != nDistricts)
|
|
88
|
+
throw new Error("Mismatched #'s of districts passed to AnalyticsSession constructor!");
|
|
89
|
+
if (repsByDistrictt.includes(0))
|
|
90
|
+
throw new Error("Zero reps for a district passed to AnalyticsSession constructor!");
|
|
91
|
+
// Assume a positive integer # of reps per district
|
|
92
|
+
this.repsByDistrictt = SessionRequest['repsByDistrictt'];
|
|
93
|
+
}
|
|
82
94
|
}
|
|
83
95
|
processConfig(config) {
|
|
84
96
|
// Default the Census & redistricting cycle to 2010
|
|
@@ -135,7 +147,7 @@ class AnalyticsSession {
|
|
|
135
147
|
let popDevPct = this.districts.table.popDevPct;
|
|
136
148
|
let totalVAP = this.districts.table.totalVAP;
|
|
137
149
|
const summaryRow = this.districts.numberOfRows() - 1;
|
|
138
|
-
totalPop[summaryRow] = this._profile.population.targetSize;
|
|
150
|
+
totalPop[summaryRow] = this._profile.population.targetSize; // MMD - This is generalized when the profile is created.
|
|
139
151
|
popDevPct[summaryRow] = popDev;
|
|
140
152
|
totalVAP[summaryRow] = Math.round(totalVAP[summaryRow] / this._profile.nDistricts);
|
|
141
153
|
// Added w/ new scorecard
|
|
@@ -333,8 +345,8 @@ class Districts {
|
|
|
333
345
|
return null;
|
|
334
346
|
}
|
|
335
347
|
setGeoProperties(i, p) { this._geoProperties[i] = p; }
|
|
336
|
-
// +1 for dummy unassigned 0 "district" and +1 for N+1 summary "district" for
|
|
337
|
-
//
|
|
348
|
+
// +1 for dummy unassigned 0 "district" and +1 for N+1 summary "district" for state-level values.
|
|
349
|
+
// Real districts are 1–N.
|
|
338
350
|
numberOfRows() { return this._session.state.nDistricts + 2; }
|
|
339
351
|
numberOfWorkingDistricts() { return this._session.state.nDistricts + 1; }
|
|
340
352
|
initTable() {
|
|
@@ -372,17 +384,24 @@ class Districts {
|
|
|
372
384
|
};
|
|
373
385
|
return t;
|
|
374
386
|
}
|
|
387
|
+
// MMD - Generalized district statistics for MMD's
|
|
375
388
|
// This is the workhorse computational routine!
|
|
376
389
|
recalcStatistics(bLog = false) {
|
|
377
390
|
// Initialize debug counters
|
|
378
391
|
nMissingDataset = 0;
|
|
379
392
|
nMissingProperty = 0;
|
|
393
|
+
// MMD - Generalized targetSize
|
|
380
394
|
// Compute these once per recalc cycle
|
|
381
|
-
|
|
382
|
-
|
|
395
|
+
const nDistricts = this._session.state.nDistricts;
|
|
396
|
+
const repsByDistrictt = this._session.repsByDistrictt;
|
|
397
|
+
const nReps = this._session.state.nReps;
|
|
398
|
+
const stateTotal = this._session.state.totalPop;
|
|
399
|
+
const targetSize = this._session.state.targetSize;
|
|
400
|
+
// const targetSize = Math.round(this._session.state.totalPop / this._session.state.nDistricts);
|
|
401
|
+
const deviationThreshold = this._session.populationDeviationThreshold();
|
|
383
402
|
// let planByDistrict = this._session.plan.byDistrictID();
|
|
384
|
-
|
|
385
|
-
|
|
403
|
+
const plan = this._session.plan;
|
|
404
|
+
const graph = this._session.graph;
|
|
386
405
|
// Add an extra 0th virtual county bucket for county-district splitting analysis
|
|
387
406
|
let nCountyBuckets = this._session.counties.nCounties + 1;
|
|
388
407
|
// INITIALIZE STATE VALUES THAT WILL BE ACCUMULATED
|
|
@@ -510,10 +529,15 @@ class Districts {
|
|
|
510
529
|
}
|
|
511
530
|
});
|
|
512
531
|
// COMPUTE DERIVED VALUES
|
|
532
|
+
// MMD - Generalized the per-district population deviations's for MMD's with variable #'s of reps per district.
|
|
533
|
+
// - The real districts are indexed 1–N.
|
|
534
|
+
// - But the # reps per district are indexed from zero.
|
|
513
535
|
// Population deviation % and equal population (boolean) by district.
|
|
514
536
|
if (i > 0) {
|
|
515
537
|
if (totalPop > 0) {
|
|
516
|
-
|
|
538
|
+
const n = (repsByDistrictt) ? repsByDistrictt[i - 1] : 1; // MMD - # of reps for the district
|
|
539
|
+
popDevPct = (totalPop - (n * targetSize)) / (n * targetSize); // MMD
|
|
540
|
+
// popDevPct = (totalPop - targetSize) / targetSize;
|
|
517
541
|
bEqualPop = (Math.abs(popDevPct) <= deviationThreshold);
|
|
518
542
|
}
|
|
519
543
|
}
|
|
@@ -1065,9 +1089,11 @@ class Counties {
|
|
|
1065
1089
|
}
|
|
1066
1090
|
exports.Counties = Counties;
|
|
1067
1091
|
// CLASSES TO ORGANIZE AND/OR ABSTRACT OTHER DATA
|
|
1092
|
+
// MMD - Extended this for # of reps
|
|
1068
1093
|
class State {
|
|
1069
1094
|
constructor(s, xx, n) {
|
|
1070
1095
|
this.totalPop = 0;
|
|
1096
|
+
this.targetSize = 0; // HACK - Will get set by doPreprocessCensus()
|
|
1071
1097
|
this.tooBigFIPS = [];
|
|
1072
1098
|
this.tooBigName = [];
|
|
1073
1099
|
this.singleCountyDistrictMax = 0;
|
|
@@ -1076,6 +1102,7 @@ class State {
|
|
|
1076
1102
|
this._session = s;
|
|
1077
1103
|
this.xx = xx;
|
|
1078
1104
|
this.nDistricts = n;
|
|
1105
|
+
this.nReps = (s.repsByDistrictt) ? s.repsByDistrictt.reduce((a, b) => a + b, 0) : this.nDistricts; // MMD
|
|
1079
1106
|
}
|
|
1080
1107
|
}
|
|
1081
1108
|
exports.State = State;
|
|
@@ -1556,6 +1583,7 @@ function isAShape(poly) {
|
|
|
1556
1583
|
//
|
|
1557
1584
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
1558
1585
|
exports.doHasEqualPopulations = void 0;
|
|
1586
|
+
// MMD - This generalizes for variable #'s of reps per district.
|
|
1559
1587
|
// NOTE - This validity check is *derived* and depends on population deviation %
|
|
1560
1588
|
// being computed (above) and normalized in test log & scorecard generation.
|
|
1561
1589
|
function doHasEqualPopulations(s, bLog = false) {
|
|
@@ -1563,7 +1591,7 @@ function doHasEqualPopulations(s, bLog = false) {
|
|
|
1563
1591
|
// Get the normalized population deviation %
|
|
1564
1592
|
let popDevTest = s.getTest(4 /* PopulationDeviation */);
|
|
1565
1593
|
const popDevPct = popDevTest['score'];
|
|
1566
|
-
const popDevNormalized = popDevTest['normalizedScore'];
|
|
1594
|
+
// const popDevNormalized: number = popDevTest['normalizedScore'] as number;
|
|
1567
1595
|
test['details']['deviation'] = popDevPct;
|
|
1568
1596
|
test['details']['thresholds'] = popDevTest['details']['scale'];
|
|
1569
1597
|
// Populate the N+1 summary "district" in district.statistics
|
|
@@ -1820,9 +1848,13 @@ function doPreprocessCensus(s, bLog = false) {
|
|
|
1820
1848
|
countyTotals[i] = totalByCounty[fipsCode];
|
|
1821
1849
|
}
|
|
1822
1850
|
s.counties.totalPopulation = countyTotals;
|
|
1851
|
+
// MMD - Rationalized calculation of target size *per rep*
|
|
1852
|
+
s.state.targetSize = s.state.totalPop / s.state.nReps;
|
|
1823
1853
|
// ANALYZE THE COUNTIES
|
|
1854
|
+
// MMD - This "these counties must be split" analysis does NOT generalize to heterogenous MMD.
|
|
1824
1855
|
// 'target_size': 733499, # calc as total / districts
|
|
1825
|
-
|
|
1856
|
+
const targetSize = s.state.targetSize;
|
|
1857
|
+
// let targetSize = Math.round(s.state.totalPop / s.state.nDistricts);
|
|
1826
1858
|
// Find counties that are bigger than the target district size.
|
|
1827
1859
|
// 'too_big' = The counties that are bigger than a district, e.g., ['Mecklenburg', 'Wake']
|
|
1828
1860
|
// 'too_big_fips' = Their FIPS codes, e.g., ['119', '183']
|
|
@@ -2100,12 +2132,15 @@ const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
|
2100
2132
|
const dra_analytics_1 = __webpack_require__(/*! @dra2020/dra-analytics */ "@dra2020/dra-analytics");
|
|
2101
2133
|
// PROFILE A PLAN
|
|
2102
2134
|
const KEEP_DECIMALS = 6;
|
|
2135
|
+
// MMD - Added # reps per district to the profile
|
|
2103
2136
|
function profilePlan(s, bLog = false) {
|
|
2104
2137
|
const state = s.state.xx;
|
|
2105
2138
|
const planName = s.title;
|
|
2106
2139
|
const nDistricts = s.state.nDistricts;
|
|
2140
|
+
const nReps = s.state.nReps; // MMD
|
|
2107
2141
|
const nCounties = s.counties.nCounties;
|
|
2108
|
-
const targetSize =
|
|
2142
|
+
const targetSize = s.state.targetSize; // MMD
|
|
2143
|
+
// const targetSize: number = Math.round(s.state.totalPop / nDistricts);
|
|
2109
2144
|
const popByDistrict = U.deepCopy(s.districts.table.totalPop.slice(1, -1));
|
|
2110
2145
|
const geoPropsByDistrict = makeArrayOfGeoProps(s, bLog);
|
|
2111
2146
|
const splits = makeNakedCxD(s);
|
|
@@ -2126,10 +2161,12 @@ function profilePlan(s, bLog = false) {
|
|
|
2126
2161
|
}
|
|
2127
2162
|
const statewideDemographics = getStatewideDemographics(s);
|
|
2128
2163
|
const demographicsByDistrict = getDemographicsByDistrict(s);
|
|
2164
|
+
// MMD - Extended the profile for # reps per district
|
|
2129
2165
|
const profile = {
|
|
2130
2166
|
state: state,
|
|
2131
2167
|
name: planName,
|
|
2132
2168
|
nDistricts: nDistricts,
|
|
2169
|
+
repsByDistrict: s.repsByDistrictt,
|
|
2133
2170
|
nCounties: nCounties,
|
|
2134
2171
|
bStateLeg: s.legislativeDistricts,
|
|
2135
2172
|
population: {
|
|
@@ -2227,10 +2264,12 @@ function computeMetrics(p, districtShapes, bLog = false) {
|
|
|
2227
2264
|
// Calculate county-district splitting metrics ...
|
|
2228
2265
|
const CxD = p.counties;
|
|
2229
2266
|
const _sS = dra_analytics_1.Splitting.makeSplittingScorecard(CxD, bLog);
|
|
2267
|
+
// MMD - Add # reps per district to call
|
|
2230
2268
|
// Calculate population deviation-related metrics ...
|
|
2231
2269
|
const totPopByDistrict = p.population.byDistrict;
|
|
2232
2270
|
const targetSize = p.population.targetSize;
|
|
2233
|
-
const
|
|
2271
|
+
const repsByDistrict = p.repsByDistrict;
|
|
2272
|
+
const _pdS = dra_analytics_1.Equal.makePopulationScorecard(totPopByDistrict, targetSize, bLegislative, repsByDistrict, bLog);
|
|
2234
2273
|
const details = {};
|
|
2235
2274
|
// Assemble the pieces into new scorecard
|
|
2236
2275
|
const scorecard = {
|