@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
- this.state = new D.State(this, SessionRequest['stateXX'], SessionRequest['nDistricts']);
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
- // 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 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
- 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
@@ -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
- popDevPct = (totalPop - targetSize) / targetSize;
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
- let targetSize = Math.round(s.state.totalPop / s.state.nDistricts);
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 / nDistricts);
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 _pdS = dra_analytics_1.Equal.makePopulationScorecard(totPopByDistrict, targetSize, bLegislative, bLog);
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 = {