@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
- // state-level values. Real districts are 1–N.
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
- let targetSize = Math.round(this._session.state.totalPop / this._session.state.nDistricts);
382
- let deviationThreshold = this._session.populationDeviationThreshold();
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
- let plan = this._session.plan;
385
- let graph = this._session.graph;
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
- popDevPct = (totalPop - targetSize) / targetSize;
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
- let targetSize = Math.round(s.state.totalPop / s.state.nDistricts);
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 = Math.round(s.state.totalPop / nDistricts);
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 _pdS = dra_analytics_1.Equal.makePopulationScorecard(totPopByDistrict, targetSize, bLegislative, bLog);
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 = {