@dra2020/district-analytics 15.6.0 → 16.0.1
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/district-analytics.js +182 -113
- package/dist/district-analytics.js.map +1 -1
- package/dist/src/_api.d.ts +1 -0
- package/dist/src/_data.d.ts +2 -0
- package/dist/src/legacy-types.d.ts +1 -0
- package/dist/src/types.d.ts +1 -0
- package/package.json +2 -2
|
@@ -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
|
|
@@ -440,80 +459,87 @@ class Districts {
|
|
|
440
459
|
bNotEmpty = true;
|
|
441
460
|
// ... loop over the geoIDs creating district-by-district statistics
|
|
442
461
|
geoIDs.forEach(function (geoID) {
|
|
443
|
-
//
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
462
|
+
// 01-04-22 -- Removed water-only guard
|
|
463
|
+
// // Skip water-only features
|
|
464
|
+
// if (!(U.isWaterOnly(geoID)))
|
|
465
|
+
// {
|
|
466
|
+
// Map from geoID to feature index
|
|
467
|
+
let featureID = outerThis._session.features.featureID(geoID);
|
|
468
|
+
let f = outerThis._session.features.featureByIndex(featureID);
|
|
469
|
+
if (f == undefined) {
|
|
470
|
+
if (bLog)
|
|
471
|
+
console.log("Statistics: Skipping undefined feature in district statistics: GEOID =", geoID, "Feature ID =", featureID);
|
|
472
|
+
}
|
|
473
|
+
else {
|
|
474
|
+
// ACCUMULATE VALUES
|
|
475
|
+
// Total population of each feature
|
|
476
|
+
// NOTE - This result is used more than once
|
|
477
|
+
// 03-27-21
|
|
478
|
+
const dkCENSUS = outerThis._session.features._keys["CENSUS" /* CENSUS */];
|
|
479
|
+
featurePop = fieldForFeature(f, dkCENSUS, 0 /* TotalPop */);
|
|
480
|
+
// featurePop = outerThis._session.features.fieldForFeature(f, T.Dataset.CENSUS, T.FeatureField.TotalPop);
|
|
481
|
+
// Total district population
|
|
482
|
+
totalPop += featurePop;
|
|
483
|
+
// Ignore features when the county is unrecognized
|
|
484
|
+
const countyFIPS = U.parseGeoID(geoID)['county'];
|
|
485
|
+
if (U.keyExists(countyFIPS, outerThis._session.counties.index)) {
|
|
486
|
+
// Total population by counties w/in a district,
|
|
487
|
+
// except the dummy unassigned district 0
|
|
488
|
+
if (i > 0)
|
|
489
|
+
countySplits[outerThis.getCountyIndex(geoID)] += featurePop;
|
|
451
490
|
}
|
|
452
491
|
else {
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
// NOTE - This result is used more than once
|
|
456
|
-
// 03-27-21
|
|
457
|
-
const dkCENSUS = outerThis._session.features._keys["CENSUS" /* CENSUS */];
|
|
458
|
-
featurePop = fieldForFeature(f, dkCENSUS, 0 /* TotalPop */);
|
|
459
|
-
// featurePop = outerThis._session.features.fieldForFeature(f, T.Dataset.CENSUS, T.FeatureField.TotalPop);
|
|
460
|
-
// Total district population
|
|
461
|
-
totalPop += featurePop;
|
|
462
|
-
// Ignore features when the county is unrecognized
|
|
463
|
-
const countyFIPS = U.parseGeoID(geoID)['county'];
|
|
464
|
-
if (U.keyExists(countyFIPS, outerThis._session.counties.index)) {
|
|
465
|
-
// Total population by counties w/in a district,
|
|
466
|
-
// except the dummy unassigned district 0
|
|
467
|
-
if (i > 0)
|
|
468
|
-
countySplits[outerThis.getCountyIndex(geoID)] += featurePop;
|
|
469
|
-
}
|
|
470
|
-
else {
|
|
471
|
-
if (bLog)
|
|
472
|
-
console.log("Statistics: County not recognized:", geoID);
|
|
473
|
-
}
|
|
474
|
-
// Democratic and Republican vote totals
|
|
475
|
-
// 10-22-2020 - Added Other votes
|
|
476
|
-
// 10-24-2020 - Added guard against inconsistent election data
|
|
477
|
-
// 03-27-2021
|
|
478
|
-
const dkELECTION = outerThis._session.features._keys["ELECTION" /* ELECTION */];
|
|
479
|
-
const featureDem = fieldForFeature(f, dkELECTION, 7 /* DemVotes */);
|
|
480
|
-
// const featureDem = outerThis._session.features.fieldForFeature(f, T.Dataset.ELECTION, T.FeatureField.DemVotes);
|
|
481
|
-
const featureRep = fieldForFeature(f, dkELECTION, 8 /* RepVotes */);
|
|
482
|
-
const featureTot = fieldForFeature(f, dkELECTION, 9 /* TotalVotes */);
|
|
483
|
-
demVotes += featureDem;
|
|
484
|
-
repVotes += featureRep;
|
|
485
|
-
totVotes += featureTot;
|
|
486
|
-
// NOTE: Unless you grab the values above before accumulating them,
|
|
487
|
-
// you can't accumulate othVotes for districts. You must calculate
|
|
488
|
-
// them by implication later.
|
|
489
|
-
if (bLog) {
|
|
490
|
-
const bBadElection = (featureDem + featureRep > featureTot) ? true : false;
|
|
491
|
-
if (bBadElection)
|
|
492
|
-
console.log("Statistics: Inconsistent election data for precinct:", geoID, featureDem, featureRep, featureTot);
|
|
493
|
-
}
|
|
494
|
-
// Voting-age demographic breakdowns (or citizen voting-age)
|
|
495
|
-
// 03-27-21
|
|
496
|
-
const dkVAP = outerThis._session.features._keys["VAP" /* VAP */];
|
|
497
|
-
totalVAP += fieldForFeature(f, dkVAP, 0 /* TotalPop */);
|
|
498
|
-
// totalVAP += outerThis._session.features.fieldForFeature(f, T.Dataset.VAP, T.FeatureField.TotalPop);
|
|
499
|
-
whitePop += fieldForFeature(f, dkVAP, 1 /* WhitePop */);
|
|
500
|
-
blackPop += fieldForFeature(f, dkVAP, 2 /* BlackPop */);
|
|
501
|
-
hispanicPop += fieldForFeature(f, dkVAP, 3 /* HispanicPop */);
|
|
502
|
-
pacificPop += fieldForFeature(f, dkVAP, 5 /* PacificPop */);
|
|
503
|
-
asianPop += fieldForFeature(f, dkVAP, 4 /* AsianPop */);
|
|
504
|
-
nativePop += fieldForFeature(f, dkVAP, 6 /* NativePop */);
|
|
492
|
+
if (bLog)
|
|
493
|
+
console.log("Statistics: County not recognized:", geoID);
|
|
505
494
|
}
|
|
495
|
+
// Democratic and Republican vote totals
|
|
496
|
+
// 10-22-2020 - Added Other votes
|
|
497
|
+
// 10-24-2020 - Added guard against inconsistent election data
|
|
498
|
+
// 03-27-2021
|
|
499
|
+
const dkELECTION = outerThis._session.features._keys["ELECTION" /* ELECTION */];
|
|
500
|
+
const featureDem = fieldForFeature(f, dkELECTION, 7 /* DemVotes */);
|
|
501
|
+
// const featureDem = outerThis._session.features.fieldForFeature(f, T.Dataset.ELECTION, T.FeatureField.DemVotes);
|
|
502
|
+
const featureRep = fieldForFeature(f, dkELECTION, 8 /* RepVotes */);
|
|
503
|
+
const featureTot = fieldForFeature(f, dkELECTION, 9 /* TotalVotes */);
|
|
504
|
+
demVotes += featureDem;
|
|
505
|
+
repVotes += featureRep;
|
|
506
|
+
totVotes += featureTot;
|
|
507
|
+
// NOTE: Unless you grab the values above before accumulating them,
|
|
508
|
+
// you can't accumulate othVotes for districts. You must calculate
|
|
509
|
+
// them by implication later.
|
|
510
|
+
if (bLog) {
|
|
511
|
+
const bBadElection = (featureDem + featureRep > featureTot) ? true : false;
|
|
512
|
+
if (bBadElection)
|
|
513
|
+
console.log("Statistics: Inconsistent election data for precinct:", geoID, featureDem, featureRep, featureTot);
|
|
514
|
+
}
|
|
515
|
+
// Voting-age demographic breakdowns (or citizen voting-age)
|
|
516
|
+
// 03-27-21
|
|
517
|
+
const dkVAP = outerThis._session.features._keys["VAP" /* VAP */];
|
|
518
|
+
totalVAP += fieldForFeature(f, dkVAP, 0 /* TotalPop */);
|
|
519
|
+
// totalVAP += outerThis._session.features.fieldForFeature(f, T.Dataset.VAP, T.FeatureField.TotalPop);
|
|
520
|
+
whitePop += fieldForFeature(f, dkVAP, 1 /* WhitePop */);
|
|
521
|
+
blackPop += fieldForFeature(f, dkVAP, 2 /* BlackPop */);
|
|
522
|
+
hispanicPop += fieldForFeature(f, dkVAP, 3 /* HispanicPop */);
|
|
523
|
+
pacificPop += fieldForFeature(f, dkVAP, 5 /* PacificPop */);
|
|
524
|
+
asianPop += fieldForFeature(f, dkVAP, 4 /* AsianPop */);
|
|
525
|
+
nativePop += fieldForFeature(f, dkVAP, 6 /* NativePop */);
|
|
506
526
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
527
|
+
// }
|
|
528
|
+
// else
|
|
529
|
+
// {
|
|
530
|
+
// if (bLog) console.log("Statistics: Skipping water-only feature in district statistics:", geoID);
|
|
531
|
+
// }
|
|
511
532
|
});
|
|
512
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.
|
|
513
537
|
// Population deviation % and equal population (boolean) by district.
|
|
514
538
|
if (i > 0) {
|
|
515
539
|
if (totalPop > 0) {
|
|
516
|
-
|
|
540
|
+
const n = (repsByDistrictt) ? repsByDistrictt[i - 1] : 1; // MMD - # of reps for the district
|
|
541
|
+
popDevPct = (totalPop - (n * targetSize)) / (n * targetSize); // MMD
|
|
542
|
+
// popDevPct = (totalPop - targetSize) / targetSize;
|
|
517
543
|
bEqualPop = (Math.abs(popDevPct) <= deviationThreshold);
|
|
518
544
|
}
|
|
519
545
|
}
|
|
@@ -998,47 +1024,75 @@ exports.fieldForFeature = fieldForFeature;
|
|
|
998
1024
|
// f is a direct GeoJSON feature
|
|
999
1025
|
// p is a geoID
|
|
1000
1026
|
function _getFeatures(f, datasetKey, p) {
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1027
|
+
if (!f.properties || !f.properties.datasets)
|
|
1028
|
+
return 0;
|
|
1029
|
+
if (datasetKey && !f.properties.datasets[datasetKey])
|
|
1030
|
+
return 0;
|
|
1031
|
+
let n = datasetKey ? f.properties.datasets[datasetKey][p] : f.properties[p];
|
|
1032
|
+
return !n || isNaN(n) ? 0 : n;
|
|
1033
|
+
}
|
|
1034
|
+
/* 01-04-22 -- Replaced with the above
|
|
1035
|
+
function _getFeatures(f: any, datasetKey: string, p: string): any
|
|
1036
|
+
{
|
|
1037
|
+
// Shim to load sample data2.json from disk for command-line scaffolding
|
|
1038
|
+
if (f.properties && f.properties['datasets'])
|
|
1039
|
+
{
|
|
1040
|
+
if (!f.properties['datasets'][datasetKey])
|
|
1041
|
+
{
|
|
1042
|
+
// Feature is missing the dataset
|
|
1043
|
+
nMissingDataset += 1;
|
|
1044
|
+
// console.log(`${nMissingDataset}: Data ${datasetKey} missing for feature ${f} Returning zero.`);
|
|
1045
|
+
|
|
1046
|
+
return 0;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
return f.properties['datasets'][datasetKey][p];
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
// NOTE - The fGetW() code from dra-client below here ...
|
|
1053
|
+
|
|
1054
|
+
// Direct property?
|
|
1055
|
+
if (f.properties && f.properties[p] !== undefined)
|
|
1056
|
+
{
|
|
1057
|
+
return f.properties[p];
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
// Joined property?
|
|
1061
|
+
let a: any[] = _fGetJoined(f);
|
|
1062
|
+
if (a)
|
|
1063
|
+
{
|
|
1064
|
+
for (let i: number = 0; i < a.length; i++)
|
|
1065
|
+
{
|
|
1066
|
+
let o: any = a[i];
|
|
1067
|
+
if (!datasetKey)
|
|
1068
|
+
{
|
|
1069
|
+
if (o[p] !== undefined)
|
|
1070
|
+
{
|
|
1071
|
+
return o[p];
|
|
1008
1072
|
}
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
for (let i = 0; i < a.length; i++) {
|
|
1020
|
-
let o = a[i];
|
|
1021
|
-
if (!datasetKey) {
|
|
1022
|
-
if (o[p] !== undefined) {
|
|
1023
|
-
return o[p];
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1026
|
-
else {
|
|
1027
|
-
if (o['datasets'] && o['datasets'][datasetKey]) {
|
|
1028
|
-
let v = (o['datasets'][datasetKey][p]);
|
|
1029
|
-
if ((!(v == null)) && (!(v == undefined))) {
|
|
1030
|
-
return o['datasets'][datasetKey][p];
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1073
|
+
}
|
|
1074
|
+
else
|
|
1075
|
+
{
|
|
1076
|
+
if (o['datasets'] && o['datasets'][datasetKey])
|
|
1077
|
+
{
|
|
1078
|
+
let v = (o['datasets'][datasetKey][p]);
|
|
1079
|
+
if ((!(v == null)) && (!(v == undefined)))
|
|
1080
|
+
{
|
|
1081
|
+
return o['datasets'][datasetKey][p];
|
|
1082
|
+
}
|
|
1034
1083
|
}
|
|
1084
|
+
}
|
|
1035
1085
|
}
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
// Feature is missing the property
|
|
1089
|
+
nMissingProperty += 1;
|
|
1090
|
+
// console.log(`${nMissingProperty}: ${p} value undefined for ${f.properties['GEOID10']}. Returning zero.`);
|
|
1091
|
+
|
|
1092
|
+
return 0;
|
|
1093
|
+
// return undefined;
|
|
1041
1094
|
}
|
|
1095
|
+
*/
|
|
1042
1096
|
function _fGetJoined(f) {
|
|
1043
1097
|
return (f.properties && f.properties.joined) ? f.properties.joined : undefined;
|
|
1044
1098
|
}
|
|
@@ -1065,9 +1119,11 @@ class Counties {
|
|
|
1065
1119
|
}
|
|
1066
1120
|
exports.Counties = Counties;
|
|
1067
1121
|
// CLASSES TO ORGANIZE AND/OR ABSTRACT OTHER DATA
|
|
1122
|
+
// MMD - Extended this for # of reps
|
|
1068
1123
|
class State {
|
|
1069
1124
|
constructor(s, xx, n) {
|
|
1070
1125
|
this.totalPop = 0;
|
|
1126
|
+
this.targetSize = 0; // HACK - Will get set by doPreprocessCensus()
|
|
1071
1127
|
this.tooBigFIPS = [];
|
|
1072
1128
|
this.tooBigName = [];
|
|
1073
1129
|
this.singleCountyDistrictMax = 0;
|
|
@@ -1076,6 +1132,7 @@ class State {
|
|
|
1076
1132
|
this._session = s;
|
|
1077
1133
|
this.xx = xx;
|
|
1078
1134
|
this.nDistricts = n;
|
|
1135
|
+
this.nReps = (s.repsByDistrictt) ? s.repsByDistrictt.reduce((a, b) => a + b, 0) : this.nDistricts; // MMD
|
|
1079
1136
|
}
|
|
1080
1137
|
}
|
|
1081
1138
|
exports.State = State;
|
|
@@ -1556,6 +1613,7 @@ function isAShape(poly) {
|
|
|
1556
1613
|
//
|
|
1557
1614
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
1558
1615
|
exports.doHasEqualPopulations = void 0;
|
|
1616
|
+
// MMD - This generalizes for variable #'s of reps per district.
|
|
1559
1617
|
// NOTE - This validity check is *derived* and depends on population deviation %
|
|
1560
1618
|
// being computed (above) and normalized in test log & scorecard generation.
|
|
1561
1619
|
function doHasEqualPopulations(s, bLog = false) {
|
|
@@ -1563,7 +1621,7 @@ function doHasEqualPopulations(s, bLog = false) {
|
|
|
1563
1621
|
// Get the normalized population deviation %
|
|
1564
1622
|
let popDevTest = s.getTest(4 /* PopulationDeviation */);
|
|
1565
1623
|
const popDevPct = popDevTest['score'];
|
|
1566
|
-
const popDevNormalized = popDevTest['normalizedScore'];
|
|
1624
|
+
// const popDevNormalized: number = popDevTest['normalizedScore'] as number;
|
|
1567
1625
|
test['details']['deviation'] = popDevPct;
|
|
1568
1626
|
test['details']['thresholds'] = popDevTest['details']['scale'];
|
|
1569
1627
|
// Populate the N+1 summary "district" in district.statistics
|
|
@@ -1820,9 +1878,13 @@ function doPreprocessCensus(s, bLog = false) {
|
|
|
1820
1878
|
countyTotals[i] = totalByCounty[fipsCode];
|
|
1821
1879
|
}
|
|
1822
1880
|
s.counties.totalPopulation = countyTotals;
|
|
1881
|
+
// MMD - Rationalized calculation of target size *per rep*
|
|
1882
|
+
s.state.targetSize = s.state.totalPop / s.state.nReps;
|
|
1823
1883
|
// ANALYZE THE COUNTIES
|
|
1884
|
+
// MMD - This "these counties must be split" analysis does NOT generalize to heterogenous MMD.
|
|
1824
1885
|
// 'target_size': 733499, # calc as total / districts
|
|
1825
|
-
|
|
1886
|
+
const targetSize = s.state.targetSize;
|
|
1887
|
+
// let targetSize = Math.round(s.state.totalPop / s.state.nDistricts);
|
|
1826
1888
|
// Find counties that are bigger than the target district size.
|
|
1827
1889
|
// 'too_big' = The counties that are bigger than a district, e.g., ['Mecklenburg', 'Wake']
|
|
1828
1890
|
// 'too_big_fips' = Their FIPS codes, e.g., ['119', '183']
|
|
@@ -2100,12 +2162,15 @@ const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
|
2100
2162
|
const dra_analytics_1 = __webpack_require__(/*! @dra2020/dra-analytics */ "@dra2020/dra-analytics");
|
|
2101
2163
|
// PROFILE A PLAN
|
|
2102
2164
|
const KEEP_DECIMALS = 6;
|
|
2165
|
+
// MMD - Added # reps per district to the profile
|
|
2103
2166
|
function profilePlan(s, bLog = false) {
|
|
2104
2167
|
const state = s.state.xx;
|
|
2105
2168
|
const planName = s.title;
|
|
2106
2169
|
const nDistricts = s.state.nDistricts;
|
|
2170
|
+
const nReps = s.state.nReps; // MMD
|
|
2107
2171
|
const nCounties = s.counties.nCounties;
|
|
2108
|
-
const targetSize =
|
|
2172
|
+
const targetSize = s.state.targetSize; // MMD
|
|
2173
|
+
// const targetSize: number = Math.round(s.state.totalPop / nDistricts);
|
|
2109
2174
|
const popByDistrict = U.deepCopy(s.districts.table.totalPop.slice(1, -1));
|
|
2110
2175
|
const geoPropsByDistrict = makeArrayOfGeoProps(s, bLog);
|
|
2111
2176
|
const splits = makeNakedCxD(s);
|
|
@@ -2126,10 +2191,12 @@ function profilePlan(s, bLog = false) {
|
|
|
2126
2191
|
}
|
|
2127
2192
|
const statewideDemographics = getStatewideDemographics(s);
|
|
2128
2193
|
const demographicsByDistrict = getDemographicsByDistrict(s);
|
|
2194
|
+
// MMD - Extended the profile for # reps per district
|
|
2129
2195
|
const profile = {
|
|
2130
2196
|
state: state,
|
|
2131
2197
|
name: planName,
|
|
2132
2198
|
nDistricts: nDistricts,
|
|
2199
|
+
repsByDistrict: s.repsByDistrictt,
|
|
2133
2200
|
nCounties: nCounties,
|
|
2134
2201
|
bStateLeg: s.legislativeDistricts,
|
|
2135
2202
|
population: {
|
|
@@ -2227,10 +2294,12 @@ function computeMetrics(p, districtShapes, bLog = false) {
|
|
|
2227
2294
|
// Calculate county-district splitting metrics ...
|
|
2228
2295
|
const CxD = p.counties;
|
|
2229
2296
|
const _sS = dra_analytics_1.Splitting.makeSplittingScorecard(CxD, bLog);
|
|
2297
|
+
// MMD - Add # reps per district to call
|
|
2230
2298
|
// Calculate population deviation-related metrics ...
|
|
2231
2299
|
const totPopByDistrict = p.population.byDistrict;
|
|
2232
2300
|
const targetSize = p.population.targetSize;
|
|
2233
|
-
const
|
|
2301
|
+
const repsByDistrict = p.repsByDistrict;
|
|
2302
|
+
const _pdS = dra_analytics_1.Equal.makePopulationScorecard(totPopByDistrict, targetSize, bLegislative, repsByDistrict, bLog);
|
|
2234
2303
|
const details = {};
|
|
2235
2304
|
// Assemble the pieces into new scorecard
|
|
2236
2305
|
const scorecard = {
|