@dra2020/district-analytics 14.1.2 → 14.1.3

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.
@@ -46,6 +46,7 @@ exports.AnalyticsSession = void 0;
46
46
  const baseclient_1 = __webpack_require__(/*! @dra2020/baseclient */ "@dra2020/baseclient");
47
47
  // import * as DT from '@dra2020/dra-types';
48
48
  const Score = __importStar(__webpack_require__(/*! @dra2020/dra-score */ "@dra2020/dra-score"));
49
+ const dra_analytics_1 = __webpack_require__(/*! @dra2020/dra-analytics */ "@dra2020/dra-analytics");
49
50
  const preprocess_1 = __webpack_require__(/*! ./preprocess */ "./src/preprocess.ts");
50
51
  const analyze_1 = __webpack_require__(/*! ./analyze */ "./src/analyze.ts");
51
52
  const score_1 = __webpack_require__(/*! ./score */ "./src/score.ts");
@@ -53,6 +54,7 @@ const results_1 = __webpack_require__(/*! ./results */ "./src/results.ts");
53
54
  const results_2 = __webpack_require__(/*! ./results */ "./src/results.ts");
54
55
  const minority_1 = __webpack_require__(/*! ./minority */ "./src/minority.ts");
55
56
  const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
57
+ const M = __importStar(__webpack_require__(/*! ./minority */ "./src/minority.ts"));
56
58
  const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
57
59
  // import * as S from './settings';
58
60
  class AnalyticsSession {
@@ -108,13 +110,59 @@ class AnalyticsSession {
108
110
  (0, analyze_1.doAnalyzeDistricts)(this, bLog);
109
111
  // This does a little stuff that didn't get factored out into dra-score and then dra-analytics
110
112
  (0, analyze_1.doAnalyzePlan)(this, bLog);
111
- // The main analytics are next
112
- // Run legacy analytics
113
+ // THE MAIN ANALYTICS ARE NEXT
114
+ // Even though we don't save the profile (as I thought we would),
115
+ // we allow it to be exported, so keep generating it, and use it
116
+ // to gather the inputs to new analytics (as well as the legacy).
113
117
  this._profile = (0, score_1.profilePlan)(this, bLog);
114
- this._scorecard = (0, score_1.scorePlan)(this, this._profile, bLog, overridesJSON);
115
- // TODO: Run new analytics
116
- // TODO: Compare the new & legacy scorecards
117
- // TODO: Set a scorecard
118
+ let legacyScorecard = {};
119
+ let legacyScorecardAlt = {};
120
+ let newScorecard = {};
121
+ // LEGACY - Run legacy analytics
122
+ if (this.config.legacyanalytics) {
123
+ if (bLog)
124
+ console.log("Running legacy analytics ...");
125
+ legacyScorecard = (0, score_1.scorePlan)(this, this._profile, bLog, overridesJSON);
126
+ }
127
+ // Construct a new/alternate scorecard
128
+ if (this.config.newanalytics) {
129
+ if (bLog)
130
+ console.log("Running new analytics ...");
131
+ newScorecard = (0, score_1.computeMetrics)(this._profile, this.getGoodShapes(), bLog); // w/o ratings
132
+ newScorecard = (0, score_1.rateKeyDimensions)(newScorecard, this._profile, bLog); // w/ ratings
133
+ legacyScorecardAlt = (0, score_1.thunkScorecard)(newScorecard, bLog);
134
+ // Add minority notes
135
+ legacyScorecardAlt.minority.details['majorityMinority'] = M.getMajorityMinority(this);
136
+ legacyScorecardAlt.minority.details['vraPreclearance'] = M.getVRASection5(this);
137
+ // Compare the new & legacy scorecards
138
+ (0, score_1.compareScorecards)(legacyScorecardAlt, legacyScorecard, bLog);
139
+ }
140
+ // Use the new scorecard, after creating it
141
+ this._scorecard = (this.config.newanalytics) ? legacyScorecardAlt : legacyScorecard;
142
+ // Post-scorecard housekeeping - copied from scorePlan()
143
+ if (this.config.newanalytics) {
144
+ // Before returning, create a dummy population deviation test, for
145
+ // doHasEqualPopulations() to use later.This is preserving the old calling sequence.
146
+ let test = this.getTest(4 /* PopulationDeviation */);
147
+ // Get the raw population deviation
148
+ const popDev = this._scorecard.populationDeviation.raw;
149
+ // Populate the test entry
150
+ test['score'] = popDev;
151
+ test['details'] = { 'maxDeviation': this._scorecard.populationDeviation.notes['maxDeviation'] };
152
+ // Populate the N+1 summary "district" in district.statistics
153
+ let totalPop = this.districts.table.totalPop;
154
+ let popDevPct = this.districts.table.popDevPct;
155
+ let totalVAP = this.districts.table.totalVAP;
156
+ const summaryRow = this.districts.numberOfRows() - 1;
157
+ totalPop[summaryRow] = this._profile.population.targetSize;
158
+ popDevPct[summaryRow] = popDev;
159
+ totalVAP[summaryRow] = Math.round(totalVAP[summaryRow] / this._profile.nDistricts);
160
+ // Added w/ new scorecard
161
+ // Use 'roughly' equal population from dra-analytics
162
+ let test2 = this.getTest(3 /* EqualPopulation */);
163
+ test2['score'] = newScorecard.populationDeviation.roughlyEqual;
164
+ }
165
+ // END main analytics
118
166
  (0, results_1.doAnalyzePostProcessing)(this, bLog);
119
167
  }
120
168
  catch (e) {
@@ -124,6 +172,29 @@ class AnalyticsSession {
124
172
  }
125
173
  return true;
126
174
  }
175
+ getGoodShapes() {
176
+ const rawShapes = this.districts.getDistrictShapes();
177
+ // Filter the real shapes & throw everything else away
178
+ let goodShapes = {};
179
+ goodShapes['type'] = "FeatureCollection";
180
+ goodShapes['features'] = [];
181
+ for (let i = 0; i < rawShapes.features.length; i++) {
182
+ const shape = rawShapes.features[i];
183
+ if (isAShape(shape)) {
184
+ const d = baseclient_1.Poly.polyDescribe(shape);
185
+ let f = {
186
+ type: 'Feature',
187
+ properties: { districtID: `${i + 1}` },
188
+ geometry: {
189
+ type: (d.npoly > 1) ? 'MultiPolygon' : 'Polygon',
190
+ coordinates: shape.geometry.coordinates
191
+ }
192
+ };
193
+ goodShapes.features.push(f);
194
+ }
195
+ }
196
+ return goodShapes;
197
+ }
127
198
  // 11-03-2020 - Added for racially polarized voting analysis
128
199
  // NOTE - This assumes that analyzePlan() has been run!
129
200
  analyzeRacialPolarization(districtID, groups, bLog = false) {
@@ -196,15 +267,29 @@ class AnalyticsSession {
196
267
  // Return a pointer to the the test entry for this test
197
268
  return this.tests[testID];
198
269
  }
270
+ // LEGACY - Threshold defined in dra-analytics for new analytics
199
271
  // NOTE - Not sure why this has to be up here ...
200
272
  populationDeviationThreshold() {
201
- // NOTE - This assumes the plan has been profiled
202
- const scorer = new Score.Scorer();
203
- const threshold = scorer.populationDeviationThreshold(this.legislativeDistricts); // TODO - 2020
273
+ let threshold;
274
+ if (this.config.legacyanalytics && !this.config.newanalytics) {
275
+ // NOTE - This assumes the plan has been profiled
276
+ const scorer = new Score.Scorer();
277
+ threshold = scorer.populationDeviationThreshold(this.legislativeDistricts); // TODO - 2020
278
+ }
279
+ else {
280
+ threshold = dra_analytics_1.Rate.popdevThreshold(this.legislativeDistricts);
281
+ }
204
282
  return threshold;
205
283
  }
206
284
  }
207
285
  exports.AnalyticsSession = AnalyticsSession;
286
+ function isAShape(poly) {
287
+ if (poly == null)
288
+ return false;
289
+ if (baseclient_1.Poly.polyNull(poly))
290
+ return false;
291
+ return poly.geometry && poly.geometry.coordinates && !U.isArrayEmpty(poly.geometry.coordinates);
292
+ }
208
293
 
209
294
 
210
295
  /***/ }),
@@ -1449,6 +1534,7 @@ function isAShape(poly) {
1449
1534
  return poly.geometry && poly.geometry.coordinates && !U.isArrayEmpty(poly.geometry.coordinates);
1450
1535
  }
1451
1536
  // SCORE KIWYSI COMPACTNESS
1537
+ // LEGACY
1452
1538
  function scoreKIWYSICompactness(s, bLog = false) {
1453
1539
  const rawShapes = s.districts.getDistrictShapes();
1454
1540
  // Filter the real shapes & throw everything else away
@@ -1528,17 +1614,20 @@ function doHasEqualPopulations(s, bLog = false) {
1528
1614
  let popDevTest = s.getTest(4 /* PopulationDeviation */);
1529
1615
  const popDevPct = popDevTest['score'];
1530
1616
  const popDevNormalized = popDevTest['normalizedScore'];
1531
- // 09-19-2020 - Added to catch edge case of only one non-empty district
1532
- const p = s._profile;
1533
- const totPopByDistrict = p.population.byDistrict.filter(x => x > 0);
1534
- const bTwoOrMoreDistricts = (totPopByDistrict.length > 1) ? true : false;
1535
- // Populate the test entry
1536
- if (bTwoOrMoreDistricts && (popDevNormalized > 0)) {
1537
- test['score'] = true;
1538
- }
1539
- else {
1540
- test['score'] = false;
1541
- }
1617
+ // LEGACY - Has 'roughly' equal populations is calculated in dra-analytics
1618
+ if (s.config.legacyanalytics && !s.config.newanalytics) {
1619
+ // 09-19-2020 - Added to catch edge case of only one non-empty district
1620
+ const p = s._profile;
1621
+ const totPopByDistrict = p.population.byDistrict.filter(x => x > 0);
1622
+ const bTwoOrMoreDistricts = (totPopByDistrict.length > 1) ? true : false;
1623
+ // Populate the test entry
1624
+ if (bTwoOrMoreDistricts && (popDevNormalized > 0)) {
1625
+ test['score'] = true;
1626
+ }
1627
+ else {
1628
+ test['score'] = false;
1629
+ }
1630
+ } // end
1542
1631
  test['details']['deviation'] = popDevPct;
1543
1632
  test['details']['thresholds'] = popDevTest['details']['scale'];
1544
1633
  // Populate the N+1 summary "district" in district.statistics
@@ -1573,7 +1662,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
1573
1662
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
1574
1663
  };
1575
1664
  Object.defineProperty(exports, "__esModule", ({ value: true }));
1576
- exports.uncertaintyOfMembership = exports.effectiveSplits = exports.avgSVError = exports.isAntimajoritarian = exports.ratePartisanBias = exports.inferSelectedMinority = exports.fieldForFeature = exports.geoIDForFeature = void 0;
1665
+ exports.uncertaintyOfMembership = exports.effectiveSplits = exports.avgSVError = exports.isAntimajoritarian = exports.ratePartisanBias = exports.estSeatProbability = exports.inferSelectedMinority = exports.fieldForFeature = exports.geoIDForFeature = void 0;
1577
1666
  __exportStar(__webpack_require__(/*! ./_api */ "./src/_api.ts"), exports);
1578
1667
  var _data_1 = __webpack_require__(/*! ./_data */ "./src/_data.ts");
1579
1668
  Object.defineProperty(exports, "geoIDForFeature", ({ enumerable: true, get: function () { return _data_1.geoIDForFeature; } }));
@@ -1584,6 +1673,7 @@ __exportStar(__webpack_require__(/*! ./types */ "./src/types.ts"), exports);
1584
1673
  __exportStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"), exports);
1585
1674
  // Re-export RPV types and COI splitting functions
1586
1675
  const dra_analytics_1 = __webpack_require__(/*! @dra2020/dra-analytics */ "@dra2020/dra-analytics");
1676
+ exports.estSeatProbability = dra_analytics_1.Partisan.estSeatProbability;
1587
1677
  exports.ratePartisanBias = dra_analytics_1.Rate.ratePartisanBias;
1588
1678
  exports.isAntimajoritarian = dra_analytics_1.Rate.isAntimajoritarian;
1589
1679
  exports.avgSVError = dra_analytics_1.Rate.avgSVError;
@@ -2028,6 +2118,7 @@ function doAnalyzePostProcessing(s, bLog = false) {
2028
2118
  scorecard.splitting.details['countiesWithSplits'] = simpleSplits['details']['countiesWithSplits'];
2029
2119
  // NOTE - Add split precincts in dra-client directly
2030
2120
  // Derive secondary tests
2121
+ // Note - The only secondary test is 'roughly equal population (true/false)
2031
2122
  (0, analyze_1.doDeriveSecondaryTests)(s, bLog);
2032
2123
  // Toggle the semaphore, so postprocessing isn't for both the testlog & scorecard
2033
2124
  s.bPostProcessingDone = true;
@@ -2067,11 +2158,12 @@ var __importStar = (this && this.__importStar) || function (mod) {
2067
2158
  return result;
2068
2159
  };
2069
2160
  Object.defineProperty(exports, "__esModule", ({ value: true }));
2070
- exports.scorePlan = exports.getStatewideDemographics = exports.profilePlan = void 0;
2161
+ exports.compareScorecards = exports.thunkScorecard = exports.rateKeyDimensions = exports.computeMetrics = exports.scorePlan = exports.getStatewideDemographics = exports.profilePlan = void 0;
2071
2162
  const Score = __importStar(__webpack_require__(/*! @dra2020/dra-score */ "@dra2020/dra-score"));
2072
2163
  const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
2073
2164
  const M = __importStar(__webpack_require__(/*! ./minority */ "./src/minority.ts"));
2074
2165
  const C = __importStar(__webpack_require__(/*! ./compact */ "./src/compact.ts"));
2166
+ const dra_analytics_1 = __webpack_require__(/*! @dra2020/dra-analytics */ "@dra2020/dra-analytics");
2075
2167
  // PROFILE A PLAN
2076
2168
  const KEEP_DECIMALS = 6;
2077
2169
  function profilePlan(s, bLog = false) {
@@ -2183,26 +2275,30 @@ function getStatewideDemographics(s, bLog = false) {
2183
2275
  return demographics;
2184
2276
  }
2185
2277
  exports.getStatewideDemographics = getStatewideDemographics;
2186
- // SCORE A PLAN
2278
+ // SCORE A PLAN - Legacy using dra-score
2279
+ // LEGACY
2187
2280
  function scorePlan(s, p, bLog = false, overridesJSON) {
2188
2281
  let scorer = new Score.Scorer();
2189
2282
  const scorecard = scorer.score(p, overridesJSON);
2190
- // Before returning, create a dummy population deviation test, for
2191
- // doHasEqualPopulations() to use later.This is preserving the old calling sequence.
2192
- let test = s.getTest(4 /* PopulationDeviation */);
2193
- // Get the raw population deviation
2194
- const popDev = scorecard.populationDeviation.raw;
2195
- // Populate the test entry
2196
- test['score'] = popDev;
2197
- test['details'] = { 'maxDeviation': scorecard.populationDeviation.notes['maxDeviation'] };
2198
- // Populate the N+1 summary "district" in district.statistics
2199
- let totalPop = s.districts.table.totalPop;
2200
- let popDevPct = s.districts.table.popDevPct;
2201
- let totalVAP = s.districts.table.totalVAP;
2202
- const summaryRow = s.districts.numberOfRows() - 1;
2203
- totalPop[summaryRow] = p.population.targetSize;
2204
- popDevPct[summaryRow] = popDev;
2205
- totalVAP[summaryRow] = Math.round(totalVAP[summaryRow] / p.nDistricts);
2283
+ // LEGACY POST-SCORECARD HOUSEKEEPING
2284
+ if (s.config.legacyanalytics && !s.config.newanalytics) {
2285
+ // Before returning, create a dummy population deviation test, for
2286
+ // doHasEqualPopulations() to use later.This is preserving the old calling sequence.
2287
+ let test = s.getTest(4 /* PopulationDeviation */);
2288
+ // Get the raw population deviation
2289
+ const popDev = scorecard.populationDeviation.raw;
2290
+ // Populate the test entry
2291
+ test['score'] = popDev;
2292
+ test['details'] = { 'maxDeviation': scorecard.populationDeviation.notes['maxDeviation'] };
2293
+ // Populate the N+1 summary "district" in district.statistics
2294
+ let totalPop = s.districts.table.totalPop;
2295
+ let popDevPct = s.districts.table.popDevPct;
2296
+ let totalVAP = s.districts.table.totalVAP;
2297
+ const summaryRow = s.districts.numberOfRows() - 1;
2298
+ totalPop[summaryRow] = p.population.targetSize;
2299
+ popDevPct[summaryRow] = popDev;
2300
+ totalVAP[summaryRow] = Math.round(totalVAP[summaryRow] / p.nDistricts);
2301
+ } // end
2206
2302
  // Add minority notes
2207
2303
  scorecard.minority.details['majorityMinority'] = M.getMajorityMinority(s);
2208
2304
  scorecard.minority.details['vraPreclearance'] = M.getVRASection5(s);
@@ -2227,6 +2323,432 @@ function scorePlan(s, p, bLog = false, overridesJSON) {
2227
2323
  return scorecard;
2228
2324
  }
2229
2325
  exports.scorePlan = scorePlan;
2326
+ // SCORE A PLAN - New using dra-analytics
2327
+ function computeMetrics(p, districtShapes, bLog = false) {
2328
+ if (bLog)
2329
+ console.log("Computing metrics ...");
2330
+ const bLegislative = p.bStateLeg;
2331
+ // Calculate bias & responsiveness metrics ...
2332
+ const byDistrictVf = p.partisanship.byDistrict;
2333
+ const statewideVf = p.partisanship.statewide;
2334
+ let _pS = dra_analytics_1.Partisan.makePartisanScorecard(statewideVf, byDistrictVf, bLog);
2335
+ // Calculate minority representation metrics ...
2336
+ const statewideDemos = p.demographics.statewide;
2337
+ const byDistrictDemos = p.demographics.byDistrict;
2338
+ let _mS = dra_analytics_1.Minority.makeMinorityScorecard(statewideDemos, byDistrictDemos, bLog);
2339
+ // Calculate compactness metrics ...
2340
+ let _cS = dra_analytics_1.Compactness.makeCompactnessScorecard(districtShapes, bLog);
2341
+ // Calculate county-district splitting metrics ...
2342
+ const CxD = p.counties;
2343
+ const _sS = dra_analytics_1.Splitting.makeSplittingScorecard(CxD, bLog);
2344
+ // Calculate population deviation-related metrics ...
2345
+ const totPopByDistrict = p.population.byDistrict;
2346
+ const targetSize = p.population.targetSize;
2347
+ const _pdS = dra_analytics_1.Equal.makePopulationScorecard(totPopByDistrict, targetSize, bLegislative, bLog);
2348
+ const details = {};
2349
+ // Assemble the pieces into new scorecard
2350
+ const scorecard = {
2351
+ partisan: _pS,
2352
+ minority: _mS,
2353
+ compactness: _cS,
2354
+ splitting: _sS,
2355
+ populationDeviation: _pdS,
2356
+ details: details,
2357
+ scratchpad: {} // Hack to pass legacy values between processing steps
2358
+ };
2359
+ return scorecard;
2360
+ }
2361
+ exports.computeMetrics = computeMetrics;
2362
+ function rateKeyDimensions(scorecard, p, bLog = false) {
2363
+ if (bLog)
2364
+ console.log("Rating key dimensions ...");
2365
+ const bLegislative = p.bStateLeg;
2366
+ // Rate proportionality
2367
+ const statewideVf = p.partisanship.statewide;
2368
+ const Sf = scorecard.partisan.bias.estSf;
2369
+ scorecard.partisan.bias.score = dra_analytics_1.Rate.rateProportionality(scorecard.partisan.bias.deviation, statewideVf, Sf);
2370
+ // Rate competititveness
2371
+ scorecard.partisan.responsiveness.score = dra_analytics_1.Rate.rateCompetitiveness(scorecard.partisan.responsiveness.cDf);
2372
+ // Rate minority representation
2373
+ const rawOd = scorecard.minority.opportunityDistricts;
2374
+ const pOd = scorecard.minority.proportionalOpportunities;
2375
+ const rawCd = scorecard.minority.coalitionDistricts;
2376
+ const pCd = scorecard.minority.proportionalCoalitions;
2377
+ scorecard.minority.score = dra_analytics_1.Rate.rateMinorityRepresentation(rawOd, pOd, rawCd, pCd);
2378
+ // Rate compactness
2379
+ const avgReock = scorecard.compactness.avgReock;
2380
+ const avgPolsby = scorecard.compactness.avgPolsby;
2381
+ const reockRating = dra_analytics_1.Rate.rateReock(avgReock);
2382
+ const polsbyRating = dra_analytics_1.Rate.ratePolsby(avgPolsby);
2383
+ scorecard.compactness.score = dra_analytics_1.Rate.rateCompactness(reockRating, polsbyRating);
2384
+ // Rate county- & district-splitting
2385
+ const rawCountySplitting = scorecard.splitting.county;
2386
+ const rawDistrictSplitting = scorecard.splitting.district;
2387
+ const nCounties = p.nCounties;
2388
+ const nDistricts = p.nDistricts;
2389
+ const countyRating = dra_analytics_1.Rate.rateCountySplitting(rawCountySplitting, nCounties, nDistricts, bLegislative);
2390
+ const districtRating = dra_analytics_1.Rate.rateDistrictSplitting(rawDistrictSplitting, bLegislative);
2391
+ const unadjusted = dra_analytics_1.Rate.rateSplitting(countyRating, districtRating);
2392
+ scorecard.splitting.score = dra_analytics_1.Rate.adjustSplittingRating(unadjusted, rawCountySplitting, rawDistrictSplitting);
2393
+ // Rate population deviation
2394
+ const rawDeviation = scorecard.populationDeviation.deviation;
2395
+ scorecard.populationDeviation.score = dra_analytics_1.Rate.ratePopulationDeviation(rawDeviation, bLegislative);
2396
+ // Squirrel away normalized compactness & splitting ratings for the legacy scorecard
2397
+ const keep = {
2398
+ reockScore: reockRating,
2399
+ polsbyScore: polsbyRating,
2400
+ countyScore: countyRating,
2401
+ districtScore: districtRating
2402
+ };
2403
+ scorecard.scratchpad = keep;
2404
+ return scorecard;
2405
+ }
2406
+ exports.rateKeyDimensions = rateKeyDimensions;
2407
+ function thunkScorecard(newScorecard, bLog = false) {
2408
+ if (bLog)
2409
+ console.log("Thunking new scorecard into legacy structure ...");
2410
+ const scratchpad = newScorecard.scratchpad;
2411
+ // Partisan scorecard
2412
+ const pS = U.deepCopy(newScorecard.partisan);
2413
+ // Minority scorecard
2414
+ const mS = U.deepCopy(newScorecard.minority);
2415
+ // Compactness scorecard
2416
+ const reockM = {
2417
+ raw: newScorecard.compactness.avgReock,
2418
+ normalized: scratchpad.reockScore,
2419
+ notes: {}
2420
+ };
2421
+ const polsbyM = {
2422
+ raw: newScorecard.compactness.avgPolsby,
2423
+ normalized: scratchpad.polsbyScore,
2424
+ notes: {}
2425
+ };
2426
+ let cS = {
2427
+ score: newScorecard.compactness.score,
2428
+ reock: reockM,
2429
+ polsby: polsbyM,
2430
+ details: U.deepCopy(newScorecard.compactness.details)
2431
+ };
2432
+ // Relocate byDistrict compactness #'s
2433
+ cS.details.byDistrict = U.deepCopy(newScorecard.compactness.byDistrict);
2434
+ // Add KIWYSI compactness score
2435
+ cS.details['kiwysi'] = newScorecard.compactness.avgKWIWYSI;
2436
+ // Splitting scorecard
2437
+ const countyM = {
2438
+ raw: newScorecard.splitting.county,
2439
+ normalized: scratchpad.countyScore,
2440
+ notes: {}
2441
+ };
2442
+ const districtM = {
2443
+ raw: newScorecard.splitting.district,
2444
+ normalized: scratchpad.districtScore,
2445
+ notes: {}
2446
+ };
2447
+ const sS = {
2448
+ score: newScorecard.splitting.score,
2449
+ county: countyM,
2450
+ district: districtM,
2451
+ details: U.deepCopy(newScorecard.splitting.details)
2452
+ };
2453
+ // Population (equality) scorecard
2454
+ const pdM = {
2455
+ raw: newScorecard.populationDeviation.deviation,
2456
+ normalized: newScorecard.populationDeviation.score,
2457
+ notes: newScorecard.populationDeviation.notes
2458
+ };
2459
+ const pdS = pdM;
2460
+ const scorecard = {
2461
+ partisan: pS,
2462
+ minority: mS,
2463
+ compactness: cS,
2464
+ splitting: sS,
2465
+ populationDeviation: pdS,
2466
+ details: newScorecard.details
2467
+ };
2468
+ return scorecard;
2469
+ }
2470
+ exports.thunkScorecard = thunkScorecard;
2471
+ function compareScorecards(altLegacyScorecard, legacyScorecard, bLog = false) {
2472
+ if (bLog)
2473
+ console.log("Comparing new & legacy scorecards ...");
2474
+ // A pretty loose tolerance, because we're not trimming values in the new analytics
2475
+ const DECIMAL_PLACES = 2;
2476
+ const DEFAULT_TOLERANCE = toleranceFn(DECIMAL_PLACES);
2477
+ // COMPARE PARTISAN SCORECARD
2478
+ // Compare BIAS section
2479
+ matchFloats("bestS", altLegacyScorecard.partisan.bias.bestS, legacyScorecard.partisan.bias.bestS, DEFAULT_TOLERANCE);
2480
+ matchFloats("bestSf", altLegacyScorecard.partisan.bias.bestSf, legacyScorecard.partisan.bias.bestSf, DEFAULT_TOLERANCE);
2481
+ matchFloats("estS", altLegacyScorecard.partisan.bias.estS, legacyScorecard.partisan.bias.estS, DEFAULT_TOLERANCE);
2482
+ matchFloats("estSf", altLegacyScorecard.partisan.bias.estSf, legacyScorecard.partisan.bias.estSf, DEFAULT_TOLERANCE);
2483
+ matchFloats("deviation", altLegacyScorecard.partisan.bias.deviation, legacyScorecard.partisan.bias.deviation, DEFAULT_TOLERANCE);
2484
+ matchInts("proportionality/score", altLegacyScorecard.partisan.bias.score, legacyScorecard.partisan.bias.score);
2485
+ matchFloats("tOf", altLegacyScorecard.partisan.bias.tOf, legacyScorecard.partisan.bias.tOf, DEFAULT_TOLERANCE);
2486
+ matchFloats("fptpS", altLegacyScorecard.partisan.bias.fptpS, legacyScorecard.partisan.bias.fptpS, DEFAULT_TOLERANCE);
2487
+ matchFloats("bS50", altLegacyScorecard.partisan.bias.bS50, legacyScorecard.partisan.bias.bS50, DEFAULT_TOLERANCE);
2488
+ matchFloats("deviation", altLegacyScorecard.partisan.bias.deviation, legacyScorecard.partisan.bias.deviation, DEFAULT_TOLERANCE);
2489
+ matchUndefinableFloats("decl", altLegacyScorecard.partisan.bias.decl, legacyScorecard.partisan.bias.decl, toleranceFn(1));
2490
+ let rvPointsAlt = altLegacyScorecard.partisan.bias.rvPoints;
2491
+ let rvPoints = legacyScorecard.partisan.bias.rvPoints;
2492
+ if (matchUndefinedness("rvPoints", rvPointsAlt, rvPoints)) {
2493
+ rvPointsAlt = rvPointsAlt;
2494
+ rvPoints = rvPoints;
2495
+ matchFloats("rvPoints/Sb", rvPointsAlt.Sb, rvPoints.Sb, DEFAULT_TOLERANCE);
2496
+ matchFloats("rvPoints/Ra", rvPointsAlt.Ra, rvPoints.Ra, DEFAULT_TOLERANCE);
2497
+ matchFloats("rvPoints/Rb", rvPointsAlt.Rb, rvPoints.Rb, DEFAULT_TOLERANCE);
2498
+ matchFloats("rvPoints/Va", rvPointsAlt.Va, rvPoints.Va, DEFAULT_TOLERANCE);
2499
+ matchFloats("rvPoints/Vb", rvPointsAlt.Vb, rvPoints.Vb, DEFAULT_TOLERANCE);
2500
+ }
2501
+ matchFloats("gSym", altLegacyScorecard.partisan.bias.gSym, legacyScorecard.partisan.bias.gSym, DEFAULT_TOLERANCE);
2502
+ matchUndefinableFloats("gamma", altLegacyScorecard.partisan.bias.gamma, legacyScorecard.partisan.bias.gamma, DEFAULT_TOLERANCE);
2503
+ matchFloats("eG", altLegacyScorecard.partisan.bias.eG, legacyScorecard.partisan.bias.eG, DEFAULT_TOLERANCE);
2504
+ matchUndefinableFloats("bSV", altLegacyScorecard.partisan.bias.bSV, legacyScorecard.partisan.bias.bSV, DEFAULT_TOLERANCE);
2505
+ matchFloats("prop", altLegacyScorecard.partisan.bias.prop, legacyScorecard.partisan.bias.prop, DEFAULT_TOLERANCE);
2506
+ matchFloats("mMs", altLegacyScorecard.partisan.bias.mMs, legacyScorecard.partisan.bias.mMs, DEFAULT_TOLERANCE);
2507
+ matchFloats("mMd", altLegacyScorecard.partisan.bias.mMd, legacyScorecard.partisan.bias.mMd, DEFAULT_TOLERANCE);
2508
+ matchUndefinableFloats("lO", altLegacyScorecard.partisan.bias.lO, legacyScorecard.partisan.bias.lO, DEFAULT_TOLERANCE);
2509
+ // Compare Impact section
2510
+ matchFloats("unearnedS", altLegacyScorecard.partisan.impact.unearnedS, legacyScorecard.partisan.impact.unearnedS, DEFAULT_TOLERANCE);
2511
+ // Note - We don't use the impact score, so we don't compute it in the new scorecard.
2512
+ // matchInts("impact/score", altLegacyScorecard.partisan.impact.score as number, legacyScorecard.partisan.impact.score);
2513
+ // Compare Responsiveness section
2514
+ matchUndefinableFloats("bigR", altLegacyScorecard.partisan.responsiveness.bigR, legacyScorecard.partisan.responsiveness.bigR, DEFAULT_TOLERANCE);
2515
+ matchUndefinableFloats("littleR", altLegacyScorecard.partisan.responsiveness.littleR, legacyScorecard.partisan.responsiveness.littleR, DEFAULT_TOLERANCE);
2516
+ matchUndefinableFloats("mIR", altLegacyScorecard.partisan.responsiveness.mIR, legacyScorecard.partisan.responsiveness.mIR, DEFAULT_TOLERANCE);
2517
+ matchFloats("rD", altLegacyScorecard.partisan.responsiveness.rD, legacyScorecard.partisan.responsiveness.rD, DEFAULT_TOLERANCE);
2518
+ matchFloats("rDf", altLegacyScorecard.partisan.responsiveness.rDf, legacyScorecard.partisan.responsiveness.rDf, DEFAULT_TOLERANCE);
2519
+ matchFloats("cSimple", altLegacyScorecard.partisan.responsiveness.cSimple, legacyScorecard.partisan.responsiveness.cSimple, DEFAULT_TOLERANCE);
2520
+ matchFloats("cD", altLegacyScorecard.partisan.responsiveness.cD, legacyScorecard.partisan.responsiveness.cD, DEFAULT_TOLERANCE);
2521
+ matchFloats("cDf", altLegacyScorecard.partisan.responsiveness.cDf, legacyScorecard.partisan.responsiveness.cDf, DEFAULT_TOLERANCE);
2522
+ matchInts("competitiveness/score", altLegacyScorecard.partisan.responsiveness.score, legacyScorecard.partisan.responsiveness.score);
2523
+ matchPointArrays("dSVpoints", altLegacyScorecard.partisan.dSVpoints, legacyScorecard.partisan.dSVpoints, DEFAULT_TOLERANCE);
2524
+ matchPointArrays("rSVpoints", altLegacyScorecard.partisan.rSVpoints, legacyScorecard.partisan.rSVpoints, DEFAULT_TOLERANCE);
2525
+ matchUndefinableFloats("averageDVf", altLegacyScorecard.partisan.averageDVf, legacyScorecard.partisan.averageDVf, DEFAULT_TOLERANCE);
2526
+ matchUndefinableFloats("averageRVf", altLegacyScorecard.partisan.averageRVf, legacyScorecard.partisan.averageRVf, DEFAULT_TOLERANCE);
2527
+ matchDicts("partisan/details", altLegacyScorecard.partisan.details, legacyScorecard.partisan.details);
2528
+ // COMPARE MINORITY SCORECARD
2529
+ matchObjectsAndArrays("pivotByDemographic", altLegacyScorecard.minority.pivotByDemographic, legacyScorecard.minority.pivotByDemographic);
2530
+ matchFloats("opportunityDistricts", altLegacyScorecard.minority.opportunityDistricts, legacyScorecard.minority.opportunityDistricts, DEFAULT_TOLERANCE);
2531
+ matchFloats("coalitionDistricts", altLegacyScorecard.minority.coalitionDistricts, legacyScorecard.minority.coalitionDistricts, DEFAULT_TOLERANCE);
2532
+ matchInts("minority/score", altLegacyScorecard.minority.score, legacyScorecard.minority.score);
2533
+ matchDicts("minority/details", altLegacyScorecard.minority.details, legacyScorecard.minority.details);
2534
+ // COMPARE COMPACTNESS SCORECARD
2535
+ matchFloats("reock", altLegacyScorecard.compactness.reock.raw, legacyScorecard.compactness.reock.raw, DEFAULT_TOLERANCE);
2536
+ matchInts("reock/normalized", altLegacyScorecard.compactness.reock.normalized, legacyScorecard.compactness.reock.normalized);
2537
+ matchFloats("polsby", altLegacyScorecard.compactness.polsby.raw, legacyScorecard.compactness.polsby.raw, DEFAULT_TOLERANCE);
2538
+ matchInts("polsby/normalized", altLegacyScorecard.compactness.polsby.normalized, legacyScorecard.compactness.polsby.normalized);
2539
+ matchInts("compactness/score", altLegacyScorecard.compactness.score, legacyScorecard.compactness.score);
2540
+ // Compare 'byDistrict' results separately
2541
+ const _altCompactnessByDistrict = dra_analytics_1.Utils.deepCopy(altLegacyScorecard.compactness.details['byDistrict']);
2542
+ const _CompactnessByDistrict = dra_analytics_1.Utils.deepCopy(legacyScorecard.compactness.details['byDistrict']);
2543
+ const _altCompactnessDetails = dra_analytics_1.Utils.deepCopy(altLegacyScorecard.compactness.details);
2544
+ const _CompactnessDetails = dra_analytics_1.Utils.deepCopy(legacyScorecard.compactness.details);
2545
+ delete _altCompactnessDetails['byDistrict'];
2546
+ delete _CompactnessDetails['byDistrict'];
2547
+ matchDicts("compactness/details", _altCompactnessDetails, _CompactnessDetails);
2548
+ matchCompactnessByDistrict("compactness/byDistrict", _altCompactnessByDistrict, _CompactnessByDistrict, DEFAULT_TOLERANCE);
2549
+ // COMPARE SPLITTING SCORECARD
2550
+ matchFloats("county", altLegacyScorecard.splitting.county.raw, legacyScorecard.splitting.county.raw, DEFAULT_TOLERANCE);
2551
+ matchInts("county/normalized", altLegacyScorecard.splitting.county.normalized, legacyScorecard.splitting.county.normalized);
2552
+ matchFloats("district", altLegacyScorecard.splitting.district.raw, legacyScorecard.splitting.district.raw, DEFAULT_TOLERANCE);
2553
+ matchInts("district/normalized", altLegacyScorecard.splitting.district.normalized, legacyScorecard.splitting.district.normalized);
2554
+ matchInts("splitting/score", altLegacyScorecard.splitting.score, legacyScorecard.splitting.score);
2555
+ matchDicts("splitting/details", altLegacyScorecard.splitting.details, legacyScorecard.splitting.details);
2556
+ // COMPARE POPULATION SCORECARD
2557
+ matchFloats("popdev", altLegacyScorecard.populationDeviation.raw, legacyScorecard.populationDeviation.raw, DEFAULT_TOLERANCE);
2558
+ matchInts("popdev/score", altLegacyScorecard.populationDeviation.normalized, legacyScorecard.populationDeviation.normalized);
2559
+ matchDicts("popdev/notes", altLegacyScorecard.populationDeviation.notes, legacyScorecard.populationDeviation.notes);
2560
+ matchDicts("details", altLegacyScorecard.details, legacyScorecard.details);
2561
+ }
2562
+ exports.compareScorecards = compareScorecards;
2563
+ // Matching helpers
2564
+ function matchFloats(property, received, good, tolerance) {
2565
+ if (dra_analytics_1.Utils.areRoughlyEqual(received, good, tolerance)) {
2566
+ return true;
2567
+ }
2568
+ else {
2569
+ console.log(`${property} does not match: ${good} expected. Received ${received}.`);
2570
+ return false;
2571
+ }
2572
+ }
2573
+ function toleranceFn(places) {
2574
+ return 0.5 / Math.pow(10, places);
2575
+ }
2576
+ function matchInts(property, received, good) {
2577
+ if (received == good) {
2578
+ return true;
2579
+ }
2580
+ else {
2581
+ console.log(`${property} does not match: ${good} expected. Received ${received}.`);
2582
+ return false;
2583
+ }
2584
+ }
2585
+ function matchUndefinableFloats(property, received, good, tolerance) {
2586
+ if ((received === undefined) && (good === undefined))
2587
+ return true;
2588
+ if ((received === undefined) || (good === undefined)) {
2589
+ console.log(`${property} does not match: ${good} expected. Received ${received}.`);
2590
+ return false;
2591
+ }
2592
+ return matchFloats(property, received, good, tolerance);
2593
+ }
2594
+ // https://stackoverflow.com/questions/13142968/deep-comparison-of-objects-arrays
2595
+ function matchObjectsAndArrays(property, received, good) {
2596
+ if ((received === undefined) && (good === undefined))
2597
+ return true; // Both undefined
2598
+ if ((received === undefined) || (good === undefined)) { // One undefined but not the other
2599
+ console.log(`${property} does not match: ${good} expected. Received ${received}.`);
2600
+ return false;
2601
+ }
2602
+ if ((Object.keys(received).length === 0) && (Object.keys(good).length === 0))
2603
+ return true; // Both empty
2604
+ if (JSON.stringify(received) === JSON.stringify(good))
2605
+ return true; // Contents match
2606
+ console.log(`${property} objects or arrays do not match.`); // Contents don't match
2607
+ return false;
2608
+ }
2609
+ function matchDicts(property, received, good) {
2610
+ if ((Object.keys(received).length === 0) && (Object.keys(good).length === 0))
2611
+ return true; // Both empty
2612
+ const receivedStr = JSON.stringify(received);
2613
+ const goodStr = JSON.stringify(good);
2614
+ if (receivedStr === goodStr)
2615
+ return true; // Contents match
2616
+ console.log(`${property} does not match: ${goodStr} expected. Received ${receivedStr}.`); // Contents don't match
2617
+ return false;
2618
+ }
2619
+ function matchUndefinedness(property, received, good) {
2620
+ if ((received === undefined) && (good === undefined))
2621
+ return true;
2622
+ if ((received === undefined) || (good === undefined))
2623
+ return false;
2624
+ return true;
2625
+ }
2626
+ function matchPointArrays(property, received, good, tolerance) {
2627
+ if (received.length != good.length) {
2628
+ console.log(`${property} does not match: Different number of points.`);
2629
+ return false;
2630
+ }
2631
+ for (var i = 0; i < received.length; i++) {
2632
+ const vLabel = property + '/' + i.toString() + '/' + 'v';
2633
+ const sLabel = property + '/' + i.toString() + '/' + 's';
2634
+ if (!matchFloats(vLabel, received[i].v, good[i].v, tolerance))
2635
+ return false;
2636
+ if (!matchFloats(sLabel, received[i].s, good[i].s, tolerance))
2637
+ return false;
2638
+ }
2639
+ return true;
2640
+ }
2641
+ function matchCompactnessByDistrict(property, received, good, tolerance) {
2642
+ if (received.length != good.length) {
2643
+ console.log(`${property} does not match: Different number of districts.`);
2644
+ return false;
2645
+ }
2646
+ let bMismatched = false;
2647
+ for (var i = 0; i < received.length; i++) {
2648
+ const rawReock = property + '/' + i.toString() + '/' + 'rawReock';
2649
+ if (!matchFloats(rawReock, received[i].rawReock, good[i].rawReock, tolerance))
2650
+ bMismatched = true;
2651
+ const normalizedReock = property + '/' + i.toString() + '/' + 'normalizedReock';
2652
+ if (!matchInts(normalizedReock, received[i].normalizedReock, good[i].normalizedReock))
2653
+ bMismatched = true;
2654
+ const rawPolsby = property + '/' + i.toString() + '/' + 'rawPolsby';
2655
+ if (!matchFloats(rawPolsby, received[i].rawPolsby, good[i].rawPolsby, tolerance))
2656
+ bMismatched = true;
2657
+ // 09-17-21 - By-district Polsby–Popper ratings from dra-score in production are wrong!
2658
+ // const normalizedPolsby: string = property + '/' + i.toString() + '/' + 'normalizedPolsby';
2659
+ // if (!matchInts(normalizedPolsby, received[i].normalizedPolsby, good[i].normalizedPolsby)) bMismatched = true;
2660
+ const kiwysiScore = property + '/' + i.toString() + '/' + 'kiwysiScore';
2661
+ if (!matchInts(kiwysiScore, received[i].kiwysiScore, good[i].kiwysiScore))
2662
+ bMismatched = true;
2663
+ }
2664
+ if (bMismatched = true)
2665
+ return false;
2666
+ return true;
2667
+ }
2668
+ // Not used, after all
2669
+ /* Modeled after David Sielaff's Jest extension 'toBeArrayWithValuesCloseTo'
2670
+
2671
+ function matchFloatArrays(property: string, received: Array<any>, expected: Array<any>, tolerance: number): boolean
2672
+ {
2673
+ // Note - Not sure what this check was for ...
2674
+ // if (expected.length == 0) {
2675
+ // return {
2676
+ // message: () => `expected arrays of same size`,
2677
+ // pass: received.length == 0
2678
+ // }
2679
+ // }
2680
+
2681
+ if (typeof expected[0] === "number")
2682
+ {
2683
+ if (typeof received[0] === "number") return matchArrays1d(property, received, expected, tolerance);
2684
+
2685
+ console.log(`${property} does not match: array doesn't contain numbers.`);
2686
+ return false;
2687
+ }
2688
+
2689
+ // Note - Ditto
2690
+ // if (expected[0].length == 0) {
2691
+ // return {
2692
+ // message: () => `expected arrays of same size`,
2693
+ // pass: received[0].length == 0
2694
+ // }
2695
+ // }
2696
+
2697
+ if (expected[0] instanceof Array && typeof expected[0][0] === "number")
2698
+ {
2699
+ if (received[0] instanceof Array && typeof received[0][0] === "number") return matchArrays2d(property, received, expected, tolerance);
2700
+
2701
+ console.log(`${property} does not match: arrays don't have the same dimensionality and content.`);
2702
+ return false;
2703
+ }
2704
+
2705
+ console.log(`${property}: Expected 1d or 2d arrays.`);
2706
+ return false;
2707
+ }
2708
+
2709
+ function matchArrays1d(property: string, received: number[], expected: number[], tolerance: number): boolean
2710
+ {
2711
+ if (received.length != expected.length)
2712
+ {
2713
+ console.log(`${property} does not match: arrays have different lengths.`);
2714
+ return false;
2715
+ }
2716
+
2717
+ for (var index = 0; index < received.length; index++)
2718
+ {
2719
+ const cell: string = property + '/' + index.toString();
2720
+ if (!matchFloats(cell, received[index], expected[index], tolerance)) return false;
2721
+ }
2722
+
2723
+ return true;
2724
+ }
2725
+
2726
+ function matchArrays2d(property: string, received: number[][], expected: number[][], tolerance: number): boolean
2727
+ {
2728
+ if (received.length != expected.length)
2729
+ {
2730
+ console.log(`${property} does not match: arrays have different lengths.`);
2731
+ return false;
2732
+ }
2733
+
2734
+ for (var index = 0; index < received.length; index++)
2735
+ {
2736
+ if (received[index].length != expected[index].length)
2737
+ {
2738
+ console.log(`${property} does not match: arrays have different lengths.`);
2739
+ return false;
2740
+ }
2741
+
2742
+ for (var inner = 0; inner < received[index].length; inner++)
2743
+ {
2744
+ const cell: string = property + '/' + index.toString() + '/' + inner.toString();
2745
+ if (!matchFloats(cell, received[index][inner], expected[index][inner], tolerance)) return false;
2746
+ }
2747
+ }
2748
+
2749
+ return true;
2750
+ }
2751
+ */
2230
2752
 
2231
2753
 
2232
2754
  /***/ }),
@@ -2242,24 +2764,27 @@ exports.scorePlan = scorePlan;
2242
2764
  // GLOBAL CONSTANTS
2243
2765
  //
2244
2766
  Object.defineProperty(exports, "__esModule", ({ value: true }));
2245
- exports.DISTRICT_SPLITTING_WEIGHT = exports.COUNTY_SPLITTING_WEIGHT = exports.EQUAL_TOLERANCE = exports.OUT_OF_STATE = exports.NUMBER_OF_ITEMS_TO_REPORT = exports.NOT_ASSIGNED = exports.NORMALIZED_RANGE = exports.PRECISION = void 0;
2767
+ exports.OUT_OF_STATE = exports.NUMBER_OF_ITEMS_TO_REPORT = exports.NOT_ASSIGNED = exports.PRECISION = void 0;
2246
2768
  // Keep four decimal places for fractions [0–1], i.e.,
2247
2769
  // keep two decimal places for %'s [0–100].
2248
2770
  exports.PRECISION = 4;
2771
+ // LEGACY - Not used
2249
2772
  // Normalized scores [0-100]
2250
- exports.NORMALIZED_RANGE = 100;
2773
+ // export const NORMALIZED_RANGE: number = 100;
2251
2774
  // The dummy district ID for features not assigned districts yet
2252
2775
  exports.NOT_ASSIGNED = 0;
2253
2776
  // # of items to report as problematic (e.g., features, districts, etc.)
2254
2777
  exports.NUMBER_OF_ITEMS_TO_REPORT = 10;
2255
2778
  // The virtual geoID for "neighbors" in other states
2256
2779
  exports.OUT_OF_STATE = "OUT_OF_STATE";
2780
+ // LEGACY - Not used
2257
2781
  // "Roughly equal" = average census block size / 2
2258
- const AVERAGE_BLOCK_SIZE = 30;
2259
- exports.EQUAL_TOLERANCE = AVERAGE_BLOCK_SIZE / 2;
2782
+ // const AVERAGE_BLOCK_SIZE = 30;
2783
+ // export const EQUAL_TOLERANCE: number = AVERAGE_BLOCK_SIZE / 2;
2784
+ // LEGACY - Not used
2260
2785
  // County & district splitting weights
2261
- exports.COUNTY_SPLITTING_WEIGHT = 0.8;
2262
- exports.DISTRICT_SPLITTING_WEIGHT = 1.0 - exports.COUNTY_SPLITTING_WEIGHT;
2786
+ // export const COUNTY_SPLITTING_WEIGHT = 0.8;
2787
+ // export const DISTRICT_SPLITTING_WEIGHT = 1.0 - COUNTY_SPLITTING_WEIGHT;
2263
2788
  // 2020
2264
2789
  // export const SHAPES = "2010 VTD shapes";
2265
2790
 
@@ -2270,16 +2795,13 @@ exports.DISTRICT_SPLITTING_WEIGHT = 1.0 - exports.COUNTY_SPLITTING_WEIGHT;
2270
2795
  /*!**********************!*\
2271
2796
  !*** ./src/types.ts ***!
2272
2797
  \**********************/
2273
- /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
2798
+ /***/ ((__unused_webpack_module, exports) => {
2274
2799
 
2275
2800
 
2276
2801
  //
2277
2802
  // TYPE DEFINITIONS
2278
2803
  //
2279
2804
  Object.defineProperty(exports, "__esModule", ({ value: true }));
2280
- exports.estSeatProbability = void 0;
2281
- var dra_score_1 = __webpack_require__(/*! @dra2020/dra-score */ "@dra2020/dra-score");
2282
- Object.defineProperty(exports, "estSeatProbability", ({ enumerable: true, get: function () { return dra_score_1.estSeatProbability; } }));
2283
2805
  // END
2284
2806
 
2285
2807
 
@@ -2315,21 +2837,23 @@ var __importStar = (this && this.__importStar) || function (mod) {
2315
2837
  return result;
2316
2838
  };
2317
2839
  Object.defineProperty(exports, "__esModule", ({ value: true }));
2318
- exports.depthof = exports.deepCopy = exports.shallowCopy = exports.countEnumValues = exports.objectContains = exports.arrayContains = exports.getSelectObjectKeys = exports.getNumericObjectKeys = exports.getObjectKeys = exports.isArrayEmpty = exports.isSetEmpty = exports.isObjectEmpty = exports.keyExists = exports.andArray = exports.initArray = exports.maxArray = exports.minArray = exports.avgArray = exports.sumArray = exports.trim = exports.isUninhabited = exports.isWaterOnly = exports.parseGeoID = exports.getDistrict = exports.isOutOfState = exports.isInState = void 0;
2840
+ exports.depthof = exports.deepCopy = exports.shallowCopy = exports.countEnumValues = exports.objectContains = exports.arrayContains = exports.getSelectObjectKeys = exports.getNumericObjectKeys = exports.getObjectKeys = exports.isArrayEmpty = exports.isSetEmpty = exports.isObjectEmpty = exports.keyExists = exports.andArray = exports.initArray = exports.maxArray = exports.minArray = exports.avgArray = exports.sumArray = exports.trim = exports.isUninhabited = exports.isWaterOnly = exports.parseGeoID = exports.getDistrict = void 0;
2319
2841
  const DT = __importStar(__webpack_require__(/*! @dra2020/dra-types */ "@dra2020/dra-types"));
2320
2842
  const _data_1 = __webpack_require__(/*! ./_data */ "./src/_data.ts");
2321
2843
  const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"));
2322
2844
  // PLAN HELPERS
2845
+ // LEGACY - Not used
2323
2846
  // Is a "neighbor" in state?
2324
- function isInState(geoID) {
2325
- return geoID != S.OUT_OF_STATE;
2326
- }
2327
- exports.isInState = isInState;
2847
+ // export function isInState(geoID: string): boolean
2848
+ // {
2849
+ // return geoID != S.OUT_OF_STATE;
2850
+ // }
2851
+ // LEGACY - Not used
2328
2852
  // Is a "neighbor" out of state?
2329
- function isOutOfState(geoID) {
2330
- return geoID == S.OUT_OF_STATE;
2331
- }
2332
- exports.isOutOfState = isOutOfState;
2853
+ // export function isOutOfState(geoID: string): boolean
2854
+ // {
2855
+ // return geoID == S.OUT_OF_STATE;
2856
+ // }
2333
2857
  // Get the districtID to which a geoID is assigned
2334
2858
  function getDistrict(plan, geoID) {
2335
2859
  // All geoIDs in a state *should be* assigned to a district (including the