@dra2020/district-analytics 15.6.1 → 16.0.2
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
|
|
@@ -73,7 +74,18 @@ class AnalyticsSession {
|
|
|
73
74
|
this.legislativeDistricts = (SessionRequest['planType'] != 'congress');
|
|
74
75
|
else if ((this.config.cycle == 2010) && (SessionRequest['legislativeDistricts'] !== undefined))
|
|
75
76
|
this.legislativeDistricts = SessionRequest['legislativeDistricts'];
|
|
76
|
-
|
|
77
|
+
// MMD - Validate & handle optional # reps per district
|
|
78
|
+
const repsByDistrict = SessionRequest['repsByDistrict'];
|
|
79
|
+
const nDistricts = SessionRequest['nDistricts'];
|
|
80
|
+
if (repsByDistrict !== undefined) {
|
|
81
|
+
if (repsByDistrict.length != nDistricts)
|
|
82
|
+
throw new Error("Mismatched #'s of districts passed to AnalyticsSession constructor!");
|
|
83
|
+
if (repsByDistrict.includes(0))
|
|
84
|
+
throw new Error("Zero reps for a district passed to AnalyticsSession constructor!");
|
|
85
|
+
// Assume a positive integer # of reps per district
|
|
86
|
+
this.repsByDistrict = repsByDistrict;
|
|
87
|
+
}
|
|
88
|
+
this.state = new D.State(this, SessionRequest['stateXX'], nDistricts);
|
|
77
89
|
this.counties = new D.Counties(this, SessionRequest['counties']);
|
|
78
90
|
this.graph = new D.GraphClass(this, SessionRequest['graph']);
|
|
79
91
|
this.features = new D.Features(this, SessionRequest['data'], this.config['datasets']);
|
|
@@ -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 repsByDistrict = this._session.repsByDistrict;
|
|
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
|
|
@@ -512,10 +531,15 @@ class Districts {
|
|
|
512
531
|
// }
|
|
513
532
|
});
|
|
514
533
|
// COMPUTE DERIVED VALUES
|
|
534
|
+
// MMD - Generalized the per-district population deviations's for MMD's with variable #'s of reps per district.
|
|
535
|
+
// - The real districts are indexed 1–N.
|
|
536
|
+
// - But the # reps per district are indexed from zero.
|
|
515
537
|
// Population deviation % and equal population (boolean) by district.
|
|
516
538
|
if (i > 0) {
|
|
517
539
|
if (totalPop > 0) {
|
|
518
|
-
|
|
540
|
+
const n = (repsByDistrict) ? repsByDistrict[i - 1] : 1; // MMD - # of reps for the district
|
|
541
|
+
popDevPct = (totalPop - (n * targetSize)) / (n * targetSize); // MMD
|
|
542
|
+
// popDevPct = (totalPop - targetSize) / targetSize;
|
|
519
543
|
bEqualPop = (Math.abs(popDevPct) <= deviationThreshold);
|
|
520
544
|
}
|
|
521
545
|
}
|
|
@@ -1095,9 +1119,11 @@ class Counties {
|
|
|
1095
1119
|
}
|
|
1096
1120
|
exports.Counties = Counties;
|
|
1097
1121
|
// CLASSES TO ORGANIZE AND/OR ABSTRACT OTHER DATA
|
|
1122
|
+
// MMD - Extended this for # of reps
|
|
1098
1123
|
class State {
|
|
1099
1124
|
constructor(s, xx, n) {
|
|
1100
1125
|
this.totalPop = 0;
|
|
1126
|
+
this.targetSize = 0; // HACK - Will get set by doPreprocessCensus()
|
|
1101
1127
|
this.tooBigFIPS = [];
|
|
1102
1128
|
this.tooBigName = [];
|
|
1103
1129
|
this.singleCountyDistrictMax = 0;
|
|
@@ -1106,6 +1132,7 @@ class State {
|
|
|
1106
1132
|
this._session = s;
|
|
1107
1133
|
this.xx = xx;
|
|
1108
1134
|
this.nDistricts = n;
|
|
1135
|
+
this.nReps = (s.repsByDistrict) ? s.repsByDistrict.reduce((a, b) => a + b, 0) : this.nDistricts; // MMD
|
|
1109
1136
|
}
|
|
1110
1137
|
}
|
|
1111
1138
|
exports.State = State;
|
|
@@ -1586,6 +1613,7 @@ function isAShape(poly) {
|
|
|
1586
1613
|
//
|
|
1587
1614
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
1588
1615
|
exports.doHasEqualPopulations = void 0;
|
|
1616
|
+
// MMD - This generalizes for variable #'s of reps per district.
|
|
1589
1617
|
// NOTE - This validity check is *derived* and depends on population deviation %
|
|
1590
1618
|
// being computed (above) and normalized in test log & scorecard generation.
|
|
1591
1619
|
function doHasEqualPopulations(s, bLog = false) {
|
|
@@ -1593,7 +1621,7 @@ function doHasEqualPopulations(s, bLog = false) {
|
|
|
1593
1621
|
// Get the normalized population deviation %
|
|
1594
1622
|
let popDevTest = s.getTest(4 /* PopulationDeviation */);
|
|
1595
1623
|
const popDevPct = popDevTest['score'];
|
|
1596
|
-
const popDevNormalized = popDevTest['normalizedScore'];
|
|
1624
|
+
// const popDevNormalized: number = popDevTest['normalizedScore'] as number;
|
|
1597
1625
|
test['details']['deviation'] = popDevPct;
|
|
1598
1626
|
test['details']['thresholds'] = popDevTest['details']['scale'];
|
|
1599
1627
|
// Populate the N+1 summary "district" in district.statistics
|
|
@@ -1850,9 +1878,13 @@ function doPreprocessCensus(s, bLog = false) {
|
|
|
1850
1878
|
countyTotals[i] = totalByCounty[fipsCode];
|
|
1851
1879
|
}
|
|
1852
1880
|
s.counties.totalPopulation = countyTotals;
|
|
1881
|
+
// MMD - Rationalized calculation of target size *per rep*
|
|
1882
|
+
s.state.targetSize = s.state.totalPop / s.state.nReps;
|
|
1853
1883
|
// ANALYZE THE COUNTIES
|
|
1884
|
+
// MMD - This "these counties must be split" analysis does NOT generalize to heterogenous MMD.
|
|
1854
1885
|
// 'target_size': 733499, # calc as total / districts
|
|
1855
|
-
|
|
1886
|
+
const targetSize = s.state.targetSize;
|
|
1887
|
+
// let targetSize = Math.round(s.state.totalPop / s.state.nDistricts);
|
|
1856
1888
|
// Find counties that are bigger than the target district size.
|
|
1857
1889
|
// 'too_big' = The counties that are bigger than a district, e.g., ['Mecklenburg', 'Wake']
|
|
1858
1890
|
// 'too_big_fips' = Their FIPS codes, e.g., ['119', '183']
|
|
@@ -2130,12 +2162,15 @@ const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
|
2130
2162
|
const dra_analytics_1 = __webpack_require__(/*! @dra2020/dra-analytics */ "@dra2020/dra-analytics");
|
|
2131
2163
|
// PROFILE A PLAN
|
|
2132
2164
|
const KEEP_DECIMALS = 6;
|
|
2165
|
+
// MMD - Added # reps per district to the profile
|
|
2133
2166
|
function profilePlan(s, bLog = false) {
|
|
2134
2167
|
const state = s.state.xx;
|
|
2135
2168
|
const planName = s.title;
|
|
2136
2169
|
const nDistricts = s.state.nDistricts;
|
|
2170
|
+
const nReps = s.state.nReps; // MMD
|
|
2137
2171
|
const nCounties = s.counties.nCounties;
|
|
2138
|
-
const targetSize = Math.round(s.state.totalPop /
|
|
2172
|
+
const targetSize = Math.round(s.state.totalPop / nReps); // MMD
|
|
2173
|
+
// const targetSize: number = Math.round(s.state.totalPop / nDistricts);
|
|
2139
2174
|
const popByDistrict = U.deepCopy(s.districts.table.totalPop.slice(1, -1));
|
|
2140
2175
|
const geoPropsByDistrict = makeArrayOfGeoProps(s, bLog);
|
|
2141
2176
|
const splits = makeNakedCxD(s);
|
|
@@ -2156,10 +2191,12 @@ function profilePlan(s, bLog = false) {
|
|
|
2156
2191
|
}
|
|
2157
2192
|
const statewideDemographics = getStatewideDemographics(s);
|
|
2158
2193
|
const demographicsByDistrict = getDemographicsByDistrict(s);
|
|
2194
|
+
// MMD - Extended the profile for # reps per district
|
|
2159
2195
|
const profile = {
|
|
2160
2196
|
state: state,
|
|
2161
2197
|
name: planName,
|
|
2162
2198
|
nDistricts: nDistricts,
|
|
2199
|
+
repsByDistrict: s.repsByDistrict,
|
|
2163
2200
|
nCounties: nCounties,
|
|
2164
2201
|
bStateLeg: s.legislativeDistricts,
|
|
2165
2202
|
population: {
|
|
@@ -2257,10 +2294,12 @@ function computeMetrics(p, districtShapes, bLog = false) {
|
|
|
2257
2294
|
// Calculate county-district splitting metrics ...
|
|
2258
2295
|
const CxD = p.counties;
|
|
2259
2296
|
const _sS = dra_analytics_1.Splitting.makeSplittingScorecard(CxD, bLog);
|
|
2297
|
+
// MMD - Add # reps per district to call
|
|
2260
2298
|
// Calculate population deviation-related metrics ...
|
|
2261
2299
|
const totPopByDistrict = p.population.byDistrict;
|
|
2262
2300
|
const targetSize = p.population.targetSize;
|
|
2263
|
-
const
|
|
2301
|
+
const repsByDistrict = p.repsByDistrict;
|
|
2302
|
+
const _pdS = dra_analytics_1.Equal.makePopulationScorecard(totPopByDistrict, targetSize, bLegislative, repsByDistrict, bLog);
|
|
2264
2303
|
const details = {};
|
|
2265
2304
|
// Assemble the pieces into new scorecard
|
|
2266
2305
|
const scorecard = {
|