@dra2020/district-analytics 15.6.1 → 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.
- package/dist/district-analytics.js +152 -143
- 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,82 +459,85 @@ 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
|
-
let f = outerThis._session.features.featureByIndex(featureID);
|
|
450
|
-
if (f == undefined) {
|
|
451
|
-
if (bLog)
|
|
452
|
-
console.log("Statistics: Skipping undefined feature in district statistics: GEOID =", geoID, "Feature ID =", featureID);
|
|
453
|
-
}
|
|
454
|
-
else {
|
|
455
|
-
// ACCUMULATE VALUES
|
|
456
|
-
// Total population of each feature
|
|
457
|
-
// NOTE - This result is used more than once
|
|
458
|
-
// 03-27-21
|
|
459
|
-
const dkCENSUS = outerThis._session.features._keys["CENSUS" /* CENSUS */];
|
|
460
|
-
featurePop = fieldForFeature(f, dkCENSUS, 0 /* TotalPop */);
|
|
461
|
-
// featurePop = outerThis._session.features.fieldForFeature(f, T.Dataset.CENSUS, T.FeatureField.TotalPop);
|
|
462
|
-
// Total district population
|
|
463
|
-
totalPop += featurePop;
|
|
464
|
-
// Ignore features when the county is unrecognized
|
|
465
|
-
const countyFIPS = U.parseGeoID(geoID)['county'];
|
|
466
|
-
if (U.keyExists(countyFIPS, outerThis._session.counties.index)) {
|
|
467
|
-
// Total population by counties w/in a district,
|
|
468
|
-
// except the dummy unassigned district 0
|
|
469
|
-
if (i > 0)
|
|
470
|
-
countySplits[outerThis.getCountyIndex(geoID)] += featurePop;
|
|
471
|
-
}
|
|
472
|
-
else {
|
|
462
|
+
// Skip water-only features
|
|
463
|
+
if (!(U.isWaterOnly(geoID))) {
|
|
464
|
+
// Map from geoID to feature index
|
|
465
|
+
let featureID = outerThis._session.features.featureID(geoID);
|
|
466
|
+
let f = outerThis._session.features.featureByIndex(featureID);
|
|
467
|
+
if (f == undefined) {
|
|
473
468
|
if (bLog)
|
|
474
|
-
console.log("Statistics:
|
|
469
|
+
console.log("Statistics: Skipping undefined feature in district statistics: GEOID =", geoID, "Feature ID =", featureID);
|
|
475
470
|
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
471
|
+
else {
|
|
472
|
+
// ACCUMULATE VALUES
|
|
473
|
+
// Total population of each feature
|
|
474
|
+
// NOTE - This result is used more than once
|
|
475
|
+
// 03-27-21
|
|
476
|
+
const dkCENSUS = outerThis._session.features._keys["CENSUS" /* CENSUS */];
|
|
477
|
+
featurePop = fieldForFeature(f, dkCENSUS, 0 /* TotalPop */);
|
|
478
|
+
// featurePop = outerThis._session.features.fieldForFeature(f, T.Dataset.CENSUS, T.FeatureField.TotalPop);
|
|
479
|
+
// Total district population
|
|
480
|
+
totalPop += featurePop;
|
|
481
|
+
// Ignore features when the county is unrecognized
|
|
482
|
+
const countyFIPS = U.parseGeoID(geoID)['county'];
|
|
483
|
+
if (U.keyExists(countyFIPS, outerThis._session.counties.index)) {
|
|
484
|
+
// Total population by counties w/in a district,
|
|
485
|
+
// except the dummy unassigned district 0
|
|
486
|
+
if (i > 0)
|
|
487
|
+
countySplits[outerThis.getCountyIndex(geoID)] += featurePop;
|
|
488
|
+
}
|
|
489
|
+
else {
|
|
490
|
+
if (bLog)
|
|
491
|
+
console.log("Statistics: County not recognized:", geoID);
|
|
492
|
+
}
|
|
493
|
+
// Democratic and Republican vote totals
|
|
494
|
+
// 10-22-2020 - Added Other votes
|
|
495
|
+
// 10-24-2020 - Added guard against inconsistent election data
|
|
496
|
+
// 03-27-2021
|
|
497
|
+
const dkELECTION = outerThis._session.features._keys["ELECTION" /* ELECTION */];
|
|
498
|
+
const featureDem = fieldForFeature(f, dkELECTION, 7 /* DemVotes */);
|
|
499
|
+
// const featureDem = outerThis._session.features.fieldForFeature(f, T.Dataset.ELECTION, T.FeatureField.DemVotes);
|
|
500
|
+
const featureRep = fieldForFeature(f, dkELECTION, 8 /* RepVotes */);
|
|
501
|
+
const featureTot = fieldForFeature(f, dkELECTION, 9 /* TotalVotes */);
|
|
502
|
+
demVotes += featureDem;
|
|
503
|
+
repVotes += featureRep;
|
|
504
|
+
totVotes += featureTot;
|
|
505
|
+
// NOTE: Unless you grab the values above before accumulating them,
|
|
506
|
+
// you can't accumulate othVotes for districts. You must calculate
|
|
507
|
+
// them by implication later.
|
|
508
|
+
if (bLog) {
|
|
509
|
+
const bBadElection = (featureDem + featureRep > featureTot) ? true : false;
|
|
510
|
+
if (bBadElection)
|
|
511
|
+
console.log("Statistics: Inconsistent election data for precinct:", geoID, featureDem, featureRep, featureTot);
|
|
512
|
+
}
|
|
513
|
+
// Voting-age demographic breakdowns (or citizen voting-age)
|
|
514
|
+
// 03-27-21
|
|
515
|
+
const dkVAP = outerThis._session.features._keys["VAP" /* VAP */];
|
|
516
|
+
totalVAP += fieldForFeature(f, dkVAP, 0 /* TotalPop */);
|
|
517
|
+
// totalVAP += outerThis._session.features.fieldForFeature(f, T.Dataset.VAP, T.FeatureField.TotalPop);
|
|
518
|
+
whitePop += fieldForFeature(f, dkVAP, 1 /* WhitePop */);
|
|
519
|
+
blackPop += fieldForFeature(f, dkVAP, 2 /* BlackPop */);
|
|
520
|
+
hispanicPop += fieldForFeature(f, dkVAP, 3 /* HispanicPop */);
|
|
521
|
+
pacificPop += fieldForFeature(f, dkVAP, 5 /* PacificPop */);
|
|
522
|
+
asianPop += fieldForFeature(f, dkVAP, 4 /* AsianPop */);
|
|
523
|
+
nativePop += fieldForFeature(f, dkVAP, 6 /* NativePop */);
|
|
495
524
|
}
|
|
496
|
-
// Voting-age demographic breakdowns (or citizen voting-age)
|
|
497
|
-
// 03-27-21
|
|
498
|
-
const dkVAP = outerThis._session.features._keys["VAP" /* VAP */];
|
|
499
|
-
totalVAP += fieldForFeature(f, dkVAP, 0 /* TotalPop */);
|
|
500
|
-
// totalVAP += outerThis._session.features.fieldForFeature(f, T.Dataset.VAP, T.FeatureField.TotalPop);
|
|
501
|
-
whitePop += fieldForFeature(f, dkVAP, 1 /* WhitePop */);
|
|
502
|
-
blackPop += fieldForFeature(f, dkVAP, 2 /* BlackPop */);
|
|
503
|
-
hispanicPop += fieldForFeature(f, dkVAP, 3 /* HispanicPop */);
|
|
504
|
-
pacificPop += fieldForFeature(f, dkVAP, 5 /* PacificPop */);
|
|
505
|
-
asianPop += fieldForFeature(f, dkVAP, 4 /* AsianPop */);
|
|
506
|
-
nativePop += fieldForFeature(f, dkVAP, 6 /* NativePop */);
|
|
507
525
|
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
// }
|
|
526
|
+
else {
|
|
527
|
+
if (bLog)
|
|
528
|
+
console.log("Statistics: Skipping water-only feature in district statistics:", geoID);
|
|
529
|
+
}
|
|
513
530
|
});
|
|
514
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.
|
|
515
535
|
// Population deviation % and equal population (boolean) by district.
|
|
516
536
|
if (i > 0) {
|
|
517
537
|
if (totalPop > 0) {
|
|
518
|
-
|
|
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;
|
|
519
541
|
bEqualPop = (Math.abs(popDevPct) <= deviationThreshold);
|
|
520
542
|
}
|
|
521
543
|
}
|
|
@@ -1000,75 +1022,47 @@ exports.fieldForFeature = fieldForFeature;
|
|
|
1000
1022
|
// f is a direct GeoJSON feature
|
|
1001
1023
|
// p is a geoID
|
|
1002
1024
|
function _getFeatures(f, datasetKey, p) {
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
/* 01-04-22 -- Replaced with the above
|
|
1011
|
-
function _getFeatures(f: any, datasetKey: string, p: string): any
|
|
1012
|
-
{
|
|
1013
|
-
// Shim to load sample data2.json from disk for command-line scaffolding
|
|
1014
|
-
if (f.properties && f.properties['datasets'])
|
|
1015
|
-
{
|
|
1016
|
-
if (!f.properties['datasets'][datasetKey])
|
|
1017
|
-
{
|
|
1018
|
-
// Feature is missing the dataset
|
|
1019
|
-
nMissingDataset += 1;
|
|
1020
|
-
// console.log(`${nMissingDataset}: Data ${datasetKey} missing for feature ${f} Returning zero.`);
|
|
1021
|
-
|
|
1022
|
-
return 0;
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
|
-
return f.properties['datasets'][datasetKey][p];
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
// NOTE - The fGetW() code from dra-client below here ...
|
|
1029
|
-
|
|
1030
|
-
// Direct property?
|
|
1031
|
-
if (f.properties && f.properties[p] !== undefined)
|
|
1032
|
-
{
|
|
1033
|
-
return f.properties[p];
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
// Joined property?
|
|
1037
|
-
let a: any[] = _fGetJoined(f);
|
|
1038
|
-
if (a)
|
|
1039
|
-
{
|
|
1040
|
-
for (let i: number = 0; i < a.length; i++)
|
|
1041
|
-
{
|
|
1042
|
-
let o: any = a[i];
|
|
1043
|
-
if (!datasetKey)
|
|
1044
|
-
{
|
|
1045
|
-
if (o[p] !== undefined)
|
|
1046
|
-
{
|
|
1047
|
-
return o[p];
|
|
1025
|
+
// Shim to load sample data2.json from disk for command-line scaffolding
|
|
1026
|
+
if (f.properties && f.properties['datasets']) {
|
|
1027
|
+
if (!f.properties['datasets'][datasetKey]) {
|
|
1028
|
+
// Feature is missing the dataset
|
|
1029
|
+
nMissingDataset += 1;
|
|
1030
|
+
// console.log(`${nMissingDataset}: Data ${datasetKey} missing for feature ${f} Returning zero.`);
|
|
1031
|
+
return 0;
|
|
1048
1032
|
}
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1033
|
+
return f.properties['datasets'][datasetKey][p];
|
|
1034
|
+
}
|
|
1035
|
+
// NOTE - The fGetW() code from dra-client below here ...
|
|
1036
|
+
// Direct property?
|
|
1037
|
+
if (f.properties && f.properties[p] !== undefined) {
|
|
1038
|
+
return f.properties[p];
|
|
1039
|
+
}
|
|
1040
|
+
// Joined property?
|
|
1041
|
+
let a = _fGetJoined(f);
|
|
1042
|
+
if (a) {
|
|
1043
|
+
for (let i = 0; i < a.length; i++) {
|
|
1044
|
+
let o = a[i];
|
|
1045
|
+
if (!datasetKey) {
|
|
1046
|
+
if (o[p] !== undefined) {
|
|
1047
|
+
return o[p];
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
else {
|
|
1051
|
+
if (o['datasets'] && o['datasets'][datasetKey]) {
|
|
1052
|
+
let v = (o['datasets'][datasetKey][p]);
|
|
1053
|
+
if ((!(v == null)) && (!(v == undefined))) {
|
|
1054
|
+
return o['datasets'][datasetKey][p];
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1059
1058
|
}
|
|
1060
|
-
}
|
|
1061
1059
|
}
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
return 0;
|
|
1069
|
-
// return undefined;
|
|
1060
|
+
// Feature is missing the property
|
|
1061
|
+
nMissingProperty += 1;
|
|
1062
|
+
// console.log(`${nMissingProperty}: ${p} value undefined for ${f.properties['GEOID10']}. Returning zero.`);
|
|
1063
|
+
return 0;
|
|
1064
|
+
// return undefined;
|
|
1070
1065
|
}
|
|
1071
|
-
*/
|
|
1072
1066
|
function _fGetJoined(f) {
|
|
1073
1067
|
return (f.properties && f.properties.joined) ? f.properties.joined : undefined;
|
|
1074
1068
|
}
|
|
@@ -1095,9 +1089,11 @@ class Counties {
|
|
|
1095
1089
|
}
|
|
1096
1090
|
exports.Counties = Counties;
|
|
1097
1091
|
// CLASSES TO ORGANIZE AND/OR ABSTRACT OTHER DATA
|
|
1092
|
+
// MMD - Extended this for # of reps
|
|
1098
1093
|
class State {
|
|
1099
1094
|
constructor(s, xx, n) {
|
|
1100
1095
|
this.totalPop = 0;
|
|
1096
|
+
this.targetSize = 0; // HACK - Will get set by doPreprocessCensus()
|
|
1101
1097
|
this.tooBigFIPS = [];
|
|
1102
1098
|
this.tooBigName = [];
|
|
1103
1099
|
this.singleCountyDistrictMax = 0;
|
|
@@ -1106,6 +1102,7 @@ class State {
|
|
|
1106
1102
|
this._session = s;
|
|
1107
1103
|
this.xx = xx;
|
|
1108
1104
|
this.nDistricts = n;
|
|
1105
|
+
this.nReps = (s.repsByDistrictt) ? s.repsByDistrictt.reduce((a, b) => a + b, 0) : this.nDistricts; // MMD
|
|
1109
1106
|
}
|
|
1110
1107
|
}
|
|
1111
1108
|
exports.State = State;
|
|
@@ -1586,6 +1583,7 @@ function isAShape(poly) {
|
|
|
1586
1583
|
//
|
|
1587
1584
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
1588
1585
|
exports.doHasEqualPopulations = void 0;
|
|
1586
|
+
// MMD - This generalizes for variable #'s of reps per district.
|
|
1589
1587
|
// NOTE - This validity check is *derived* and depends on population deviation %
|
|
1590
1588
|
// being computed (above) and normalized in test log & scorecard generation.
|
|
1591
1589
|
function doHasEqualPopulations(s, bLog = false) {
|
|
@@ -1593,7 +1591,7 @@ function doHasEqualPopulations(s, bLog = false) {
|
|
|
1593
1591
|
// Get the normalized population deviation %
|
|
1594
1592
|
let popDevTest = s.getTest(4 /* PopulationDeviation */);
|
|
1595
1593
|
const popDevPct = popDevTest['score'];
|
|
1596
|
-
const popDevNormalized = popDevTest['normalizedScore'];
|
|
1594
|
+
// const popDevNormalized: number = popDevTest['normalizedScore'] as number;
|
|
1597
1595
|
test['details']['deviation'] = popDevPct;
|
|
1598
1596
|
test['details']['thresholds'] = popDevTest['details']['scale'];
|
|
1599
1597
|
// Populate the N+1 summary "district" in district.statistics
|
|
@@ -1850,9 +1848,13 @@ function doPreprocessCensus(s, bLog = false) {
|
|
|
1850
1848
|
countyTotals[i] = totalByCounty[fipsCode];
|
|
1851
1849
|
}
|
|
1852
1850
|
s.counties.totalPopulation = countyTotals;
|
|
1851
|
+
// MMD - Rationalized calculation of target size *per rep*
|
|
1852
|
+
s.state.targetSize = s.state.totalPop / s.state.nReps;
|
|
1853
1853
|
// ANALYZE THE COUNTIES
|
|
1854
|
+
// MMD - This "these counties must be split" analysis does NOT generalize to heterogenous MMD.
|
|
1854
1855
|
// 'target_size': 733499, # calc as total / districts
|
|
1855
|
-
|
|
1856
|
+
const targetSize = s.state.targetSize;
|
|
1857
|
+
// let targetSize = Math.round(s.state.totalPop / s.state.nDistricts);
|
|
1856
1858
|
// Find counties that are bigger than the target district size.
|
|
1857
1859
|
// 'too_big' = The counties that are bigger than a district, e.g., ['Mecklenburg', 'Wake']
|
|
1858
1860
|
// 'too_big_fips' = Their FIPS codes, e.g., ['119', '183']
|
|
@@ -2130,12 +2132,15 @@ const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
|
2130
2132
|
const dra_analytics_1 = __webpack_require__(/*! @dra2020/dra-analytics */ "@dra2020/dra-analytics");
|
|
2131
2133
|
// PROFILE A PLAN
|
|
2132
2134
|
const KEEP_DECIMALS = 6;
|
|
2135
|
+
// MMD - Added # reps per district to the profile
|
|
2133
2136
|
function profilePlan(s, bLog = false) {
|
|
2134
2137
|
const state = s.state.xx;
|
|
2135
2138
|
const planName = s.title;
|
|
2136
2139
|
const nDistricts = s.state.nDistricts;
|
|
2140
|
+
const nReps = s.state.nReps; // MMD
|
|
2137
2141
|
const nCounties = s.counties.nCounties;
|
|
2138
|
-
const targetSize =
|
|
2142
|
+
const targetSize = s.state.targetSize; // MMD
|
|
2143
|
+
// const targetSize: number = Math.round(s.state.totalPop / nDistricts);
|
|
2139
2144
|
const popByDistrict = U.deepCopy(s.districts.table.totalPop.slice(1, -1));
|
|
2140
2145
|
const geoPropsByDistrict = makeArrayOfGeoProps(s, bLog);
|
|
2141
2146
|
const splits = makeNakedCxD(s);
|
|
@@ -2156,10 +2161,12 @@ function profilePlan(s, bLog = false) {
|
|
|
2156
2161
|
}
|
|
2157
2162
|
const statewideDemographics = getStatewideDemographics(s);
|
|
2158
2163
|
const demographicsByDistrict = getDemographicsByDistrict(s);
|
|
2164
|
+
// MMD - Extended the profile for # reps per district
|
|
2159
2165
|
const profile = {
|
|
2160
2166
|
state: state,
|
|
2161
2167
|
name: planName,
|
|
2162
2168
|
nDistricts: nDistricts,
|
|
2169
|
+
repsByDistrict: s.repsByDistrictt,
|
|
2163
2170
|
nCounties: nCounties,
|
|
2164
2171
|
bStateLeg: s.legislativeDistricts,
|
|
2165
2172
|
population: {
|
|
@@ -2257,10 +2264,12 @@ function computeMetrics(p, districtShapes, bLog = false) {
|
|
|
2257
2264
|
// Calculate county-district splitting metrics ...
|
|
2258
2265
|
const CxD = p.counties;
|
|
2259
2266
|
const _sS = dra_analytics_1.Splitting.makeSplittingScorecard(CxD, bLog);
|
|
2267
|
+
// MMD - Add # reps per district to call
|
|
2260
2268
|
// Calculate population deviation-related metrics ...
|
|
2261
2269
|
const totPopByDistrict = p.population.byDistrict;
|
|
2262
2270
|
const targetSize = p.population.targetSize;
|
|
2263
|
-
const
|
|
2271
|
+
const repsByDistrict = p.repsByDistrict;
|
|
2272
|
+
const _pdS = dra_analytics_1.Equal.makePopulationScorecard(totPopByDistrict, targetSize, bLegislative, repsByDistrict, bLog);
|
|
2264
2273
|
const details = {};
|
|
2265
2274
|
// Assemble the pieces into new scorecard
|
|
2266
2275
|
const scorecard = {
|