@dra2020/district-analytics 14.1.2 → 15.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 +696 -79
- package/dist/district-analytics.js.map +1 -1
- package/dist/node_modules/@dra2020/dra-analytics/lib/types/all.d.ts +15 -0
- package/dist/node_modules/@dra2020/dra-analytics/lib/types/compactness.d.ts +8 -1
- package/dist/node_modules/@dra2020/dra-analytics/lib/types/general.d.ts +0 -6
- package/dist/node_modules/@dra2020/dra-analytics/lib/types/minority.d.ts +3 -3
- package/dist/node_modules/@dra2020/dra-analytics/lib/types/partisan.d.ts +3 -3
- package/dist/node_modules/@dra2020/dra-analytics/lib/types/population.d.ts +1 -1
- package/dist/node_modules/@dra2020/dra-analytics/lib/types/splitting.d.ts +3 -3
- package/dist/src/_api.d.ts +6 -5
- package/dist/src/index.d.ts +3 -1
- package/dist/src/legacy-types.d.ts +163 -0
- package/dist/src/score.d.ts +8 -4
- package/dist/src/settings.d.ts +0 -4
- package/dist/src/types.d.ts +0 -6
- package/dist/src/utils.d.ts +0 -2
- package/package.json +2 -3
|
@@ -44,16 +44,23 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
44
44
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
45
45
|
exports.AnalyticsSession = void 0;
|
|
46
46
|
const baseclient_1 = __webpack_require__(/*! @dra2020/baseclient */ "@dra2020/baseclient");
|
|
47
|
+
// LEGACY - DELETE
|
|
47
48
|
// import * as DT from '@dra2020/dra-types';
|
|
48
|
-
|
|
49
|
+
// LEGACY - DELETE
|
|
50
|
+
// import * as Score from '@dra2020/dra-score';
|
|
51
|
+
const dra_analytics_1 = __webpack_require__(/*! @dra2020/dra-analytics */ "@dra2020/dra-analytics");
|
|
49
52
|
const preprocess_1 = __webpack_require__(/*! ./preprocess */ "./src/preprocess.ts");
|
|
50
53
|
const analyze_1 = __webpack_require__(/*! ./analyze */ "./src/analyze.ts");
|
|
54
|
+
// LEGACY - DELETE
|
|
55
|
+
// import { profilePlan, scorePlan, computeMetrics, rateKeyDimensions, thunkScorecard, compareScorecards } from './score'
|
|
51
56
|
const score_1 = __webpack_require__(/*! ./score */ "./src/score.ts");
|
|
52
57
|
const results_1 = __webpack_require__(/*! ./results */ "./src/results.ts");
|
|
53
58
|
const results_2 = __webpack_require__(/*! ./results */ "./src/results.ts");
|
|
54
59
|
const minority_1 = __webpack_require__(/*! ./minority */ "./src/minority.ts");
|
|
55
60
|
const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
61
|
+
const M = __importStar(__webpack_require__(/*! ./minority */ "./src/minority.ts"));
|
|
56
62
|
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
63
|
+
// LEGACY - DELETE
|
|
57
64
|
// import * as S from './settings';
|
|
58
65
|
class AnalyticsSession {
|
|
59
66
|
constructor(SessionRequest) {
|
|
@@ -108,13 +115,69 @@ class AnalyticsSession {
|
|
|
108
115
|
(0, analyze_1.doAnalyzeDistricts)(this, bLog);
|
|
109
116
|
// This does a little stuff that didn't get factored out into dra-score and then dra-analytics
|
|
110
117
|
(0, analyze_1.doAnalyzePlan)(this, bLog);
|
|
111
|
-
//
|
|
112
|
-
//
|
|
118
|
+
// THE MAIN ANALYTICS ARE NEXT
|
|
119
|
+
// Even though we don't save the profile (as I thought we would),
|
|
120
|
+
// we allow it to be exported, so keep generating it, and use it
|
|
121
|
+
// to gather the inputs to new analytics (as well as the legacy).
|
|
113
122
|
this._profile = (0, score_1.profilePlan)(this, bLog);
|
|
114
|
-
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
|
|
123
|
+
// LEGACY - DELETE
|
|
124
|
+
// let legacyScorecard = {} as Score.Scorecard;
|
|
125
|
+
let legacyScorecardAlt = {};
|
|
126
|
+
let newScorecard = {};
|
|
127
|
+
// LEGACY - DELETE: Run legacy analytics
|
|
128
|
+
/*
|
|
129
|
+
if (this.config.legacyanalytics)
|
|
130
|
+
{
|
|
131
|
+
if (bLog) console.log("Running legacy analytics ...");
|
|
132
|
+
legacyScorecard = scorePlan(this, this._profile, bLog, overridesJSON);
|
|
133
|
+
}
|
|
134
|
+
*/
|
|
135
|
+
// Construct a new/alternate scorecard
|
|
136
|
+
// LEGACY - DELETE
|
|
137
|
+
// if (this.config.newanalytics)
|
|
138
|
+
// {
|
|
139
|
+
// if (bLog) console.log("Running new analytics ...");
|
|
140
|
+
newScorecard = (0, score_1.computeMetrics)(this._profile, this.getGoodShapes(), bLog); // w/o ratings
|
|
141
|
+
newScorecard = (0, score_1.rateKeyDimensions)(newScorecard, this._profile, bLog); // w/ ratings
|
|
142
|
+
legacyScorecardAlt = (0, score_1.thunkScorecard)(newScorecard, bLog);
|
|
143
|
+
// Add minority notes
|
|
144
|
+
legacyScorecardAlt.minority.details['majorityMinority'] = M.getMajorityMinority(this);
|
|
145
|
+
legacyScorecardAlt.minority.details['vraPreclearance'] = M.getVRASection5(this);
|
|
146
|
+
// LEGACY - DELETE
|
|
147
|
+
// // Compare the new & legacy scorecards
|
|
148
|
+
// compareScorecards(legacyScorecardAlt, legacyScorecard, bLog);
|
|
149
|
+
// }
|
|
150
|
+
// LEGACY - DELETE
|
|
151
|
+
// Use the new scorecard, after creating it
|
|
152
|
+
// this._scorecard = (this.config.newanalytics) ? legacyScorecardAlt : legacyScorecard;
|
|
153
|
+
this._scorecard = legacyScorecardAlt;
|
|
154
|
+
// Post-scorecard housekeeping - copied from scorePlan()
|
|
155
|
+
// LEGACY - DELETE
|
|
156
|
+
// if (this.config.newanalytics)
|
|
157
|
+
// {
|
|
158
|
+
// Before returning, create a dummy population deviation test, for
|
|
159
|
+
// doHasEqualPopulations() to use later.This is preserving the old calling sequence.
|
|
160
|
+
let test = this.getTest(4 /* PopulationDeviation */);
|
|
161
|
+
// Get the raw population deviation
|
|
162
|
+
const popDev = this._scorecard.populationDeviation.raw;
|
|
163
|
+
// Populate the test entry
|
|
164
|
+
test['score'] = popDev;
|
|
165
|
+
test['details'] = { 'maxDeviation': this._scorecard.populationDeviation.notes['maxDeviation'] };
|
|
166
|
+
// Populate the N+1 summary "district" in district.statistics
|
|
167
|
+
let totalPop = this.districts.table.totalPop;
|
|
168
|
+
let popDevPct = this.districts.table.popDevPct;
|
|
169
|
+
let totalVAP = this.districts.table.totalVAP;
|
|
170
|
+
const summaryRow = this.districts.numberOfRows() - 1;
|
|
171
|
+
totalPop[summaryRow] = this._profile.population.targetSize;
|
|
172
|
+
popDevPct[summaryRow] = popDev;
|
|
173
|
+
totalVAP[summaryRow] = Math.round(totalVAP[summaryRow] / this._profile.nDistricts);
|
|
174
|
+
// Added w/ new scorecard
|
|
175
|
+
// Use 'roughly' equal population from dra-analytics
|
|
176
|
+
let test2 = this.getTest(3 /* EqualPopulation */);
|
|
177
|
+
test2['score'] = newScorecard.populationDeviation.roughlyEqual;
|
|
178
|
+
// LEGACY - DELETE
|
|
179
|
+
// }
|
|
180
|
+
// END main analytics
|
|
118
181
|
(0, results_1.doAnalyzePostProcessing)(this, bLog);
|
|
119
182
|
}
|
|
120
183
|
catch (e) {
|
|
@@ -124,6 +187,29 @@ class AnalyticsSession {
|
|
|
124
187
|
}
|
|
125
188
|
return true;
|
|
126
189
|
}
|
|
190
|
+
getGoodShapes() {
|
|
191
|
+
const rawShapes = this.districts.getDistrictShapes();
|
|
192
|
+
// Filter the real shapes & throw everything else away
|
|
193
|
+
let goodShapes = {};
|
|
194
|
+
goodShapes['type'] = "FeatureCollection";
|
|
195
|
+
goodShapes['features'] = [];
|
|
196
|
+
for (let i = 0; i < rawShapes.features.length; i++) {
|
|
197
|
+
const shape = rawShapes.features[i];
|
|
198
|
+
if (isAShape(shape)) {
|
|
199
|
+
const d = baseclient_1.Poly.polyDescribe(shape);
|
|
200
|
+
let f = {
|
|
201
|
+
type: 'Feature',
|
|
202
|
+
properties: { districtID: `${i + 1}` },
|
|
203
|
+
geometry: {
|
|
204
|
+
type: (d.npoly > 1) ? 'MultiPolygon' : 'Polygon',
|
|
205
|
+
coordinates: shape.geometry.coordinates
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
goodShapes.features.push(f);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return goodShapes;
|
|
212
|
+
}
|
|
127
213
|
// 11-03-2020 - Added for racially polarized voting analysis
|
|
128
214
|
// NOTE - This assumes that analyzePlan() has been run!
|
|
129
215
|
analyzeRacialPolarization(districtID, groups, bLog = false) {
|
|
@@ -196,15 +282,32 @@ class AnalyticsSession {
|
|
|
196
282
|
// Return a pointer to the the test entry for this test
|
|
197
283
|
return this.tests[testID];
|
|
198
284
|
}
|
|
285
|
+
// LEGACY - Threshold defined in dra-analytics for new analytics
|
|
199
286
|
// NOTE - Not sure why this has to be up here ...
|
|
200
287
|
populationDeviationThreshold() {
|
|
201
|
-
//
|
|
202
|
-
|
|
203
|
-
|
|
288
|
+
// LEGACY - DELETE
|
|
289
|
+
// let threshold: number;
|
|
290
|
+
// if (this.config.legacyanalytics && !this.config.newanalytics)
|
|
291
|
+
// {
|
|
292
|
+
// // NOTE - This assumes the plan has been profiled
|
|
293
|
+
// const scorer = new Score.Scorer();
|
|
294
|
+
// threshold = scorer.populationDeviationThreshold(this.legislativeDistricts); // TODO - 2020
|
|
295
|
+
// }
|
|
296
|
+
// else
|
|
297
|
+
// {
|
|
298
|
+
const threshold = dra_analytics_1.Rate.popdevThreshold(this.legislativeDistricts);
|
|
299
|
+
// }
|
|
204
300
|
return threshold;
|
|
205
301
|
}
|
|
206
302
|
}
|
|
207
303
|
exports.AnalyticsSession = AnalyticsSession;
|
|
304
|
+
function isAShape(poly) {
|
|
305
|
+
if (poly == null)
|
|
306
|
+
return false;
|
|
307
|
+
if (baseclient_1.Poly.polyNull(poly))
|
|
308
|
+
return false;
|
|
309
|
+
return poly.geometry && poly.geometry.coordinates && !U.isArrayEmpty(poly.geometry.coordinates);
|
|
310
|
+
}
|
|
208
311
|
|
|
209
312
|
|
|
210
313
|
/***/ }),
|
|
@@ -1449,6 +1552,7 @@ function isAShape(poly) {
|
|
|
1449
1552
|
return poly.geometry && poly.geometry.coordinates && !U.isArrayEmpty(poly.geometry.coordinates);
|
|
1450
1553
|
}
|
|
1451
1554
|
// SCORE KIWYSI COMPACTNESS
|
|
1555
|
+
// LEGACY
|
|
1452
1556
|
function scoreKIWYSICompactness(s, bLog = false) {
|
|
1453
1557
|
const rawShapes = s.districts.getDistrictShapes();
|
|
1454
1558
|
// Filter the real shapes & throw everything else away
|
|
@@ -1528,17 +1632,26 @@ function doHasEqualPopulations(s, bLog = false) {
|
|
|
1528
1632
|
let popDevTest = s.getTest(4 /* PopulationDeviation */);
|
|
1529
1633
|
const popDevPct = popDevTest['score'];
|
|
1530
1634
|
const popDevNormalized = popDevTest['normalizedScore'];
|
|
1531
|
-
//
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1635
|
+
// LEGACY - DELETE: Has 'roughly' equal populations is calculated in dra-analytics
|
|
1636
|
+
/*
|
|
1637
|
+
if (s.config.legacyanalytics && !s.config.newanalytics)
|
|
1638
|
+
{
|
|
1639
|
+
// 09-19-2020 - Added to catch edge case of only one non-empty district
|
|
1640
|
+
const p = s._profile as L.Profile;
|
|
1641
|
+
const totPopByDistrict = p.population.byDistrict.filter(x => x > 0);
|
|
1642
|
+
const bTwoOrMoreDistricts: boolean = (totPopByDistrict.length > 1) ? true : false;
|
|
1643
|
+
|
|
1644
|
+
// Populate the test entry
|
|
1645
|
+
if (bTwoOrMoreDistricts && (popDevNormalized > 0))
|
|
1646
|
+
{
|
|
1537
1647
|
test['score'] = true;
|
|
1538
|
-
|
|
1539
|
-
|
|
1648
|
+
}
|
|
1649
|
+
else
|
|
1650
|
+
{
|
|
1540
1651
|
test['score'] = false;
|
|
1652
|
+
}
|
|
1541
1653
|
}
|
|
1654
|
+
*/
|
|
1542
1655
|
test['details']['deviation'] = popDevPct;
|
|
1543
1656
|
test['details']['thresholds'] = popDevTest['details']['scale'];
|
|
1544
1657
|
// Populate the N+1 summary "district" in district.statistics
|
|
@@ -1573,7 +1686,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
1573
1686
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
1574
1687
|
};
|
|
1575
1688
|
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;
|
|
1689
|
+
exports.uncertaintyOfMembership = exports.effectiveSplits = exports.avgSVError = exports.isAntimajoritarian = exports.ratePartisanBias = exports.estSeatProbability = exports.inferSelectedMinority = exports.fieldForFeature = exports.geoIDForFeature = void 0;
|
|
1577
1690
|
__exportStar(__webpack_require__(/*! ./_api */ "./src/_api.ts"), exports);
|
|
1578
1691
|
var _data_1 = __webpack_require__(/*! ./_data */ "./src/_data.ts");
|
|
1579
1692
|
Object.defineProperty(exports, "geoIDForFeature", ({ enumerable: true, get: function () { return _data_1.geoIDForFeature; } }));
|
|
@@ -1584,6 +1697,7 @@ __exportStar(__webpack_require__(/*! ./types */ "./src/types.ts"), exports);
|
|
|
1584
1697
|
__exportStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"), exports);
|
|
1585
1698
|
// Re-export RPV types and COI splitting functions
|
|
1586
1699
|
const dra_analytics_1 = __webpack_require__(/*! @dra2020/dra-analytics */ "@dra2020/dra-analytics");
|
|
1700
|
+
exports.estSeatProbability = dra_analytics_1.Partisan.estSeatProbability;
|
|
1587
1701
|
exports.ratePartisanBias = dra_analytics_1.Rate.ratePartisanBias;
|
|
1588
1702
|
exports.isAntimajoritarian = dra_analytics_1.Rate.isAntimajoritarian;
|
|
1589
1703
|
exports.avgSVError = dra_analytics_1.Rate.avgSVError;
|
|
@@ -2028,6 +2142,7 @@ function doAnalyzePostProcessing(s, bLog = false) {
|
|
|
2028
2142
|
scorecard.splitting.details['countiesWithSplits'] = simpleSplits['details']['countiesWithSplits'];
|
|
2029
2143
|
// NOTE - Add split precincts in dra-client directly
|
|
2030
2144
|
// Derive secondary tests
|
|
2145
|
+
// Note - The only secondary test is 'roughly equal population (true/false)
|
|
2031
2146
|
(0, analyze_1.doDeriveSecondaryTests)(s, bLog);
|
|
2032
2147
|
// Toggle the semaphore, so postprocessing isn't for both the testlog & scorecard
|
|
2033
2148
|
s.bPostProcessingDone = true;
|
|
@@ -2067,11 +2182,9 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
2067
2182
|
return result;
|
|
2068
2183
|
};
|
|
2069
2184
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
2070
|
-
exports.
|
|
2071
|
-
const Score = __importStar(__webpack_require__(/*! @dra2020/dra-score */ "@dra2020/dra-score"));
|
|
2185
|
+
exports.thunkScorecard = exports.rateKeyDimensions = exports.computeMetrics = exports.getStatewideDemographics = exports.profilePlan = void 0;
|
|
2072
2186
|
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
2073
|
-
const
|
|
2074
|
-
const C = __importStar(__webpack_require__(/*! ./compact */ "./src/compact.ts"));
|
|
2187
|
+
const dra_analytics_1 = __webpack_require__(/*! @dra2020/dra-analytics */ "@dra2020/dra-analytics");
|
|
2075
2188
|
// PROFILE A PLAN
|
|
2076
2189
|
const KEEP_DECIMALS = 6;
|
|
2077
2190
|
function profilePlan(s, bLog = false) {
|
|
@@ -2183,50 +2296,555 @@ function getStatewideDemographics(s, bLog = false) {
|
|
|
2183
2296
|
return demographics;
|
|
2184
2297
|
}
|
|
2185
2298
|
exports.getStatewideDemographics = getStatewideDemographics;
|
|
2186
|
-
// SCORE A PLAN
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2299
|
+
// SCORE A PLAN - Legacy using dra-score
|
|
2300
|
+
// LEGACY - DELETE
|
|
2301
|
+
/*
|
|
2302
|
+
export function scorePlan(s: AnalyticsSession, p: Score.Profile, bLog: boolean = false, overridesJSON?: any): Score.Scorecard
|
|
2303
|
+
{
|
|
2304
|
+
let scorer = new Score.Scorer();
|
|
2305
|
+
const scorecard: Score.Scorecard = scorer.score(p, overridesJSON);
|
|
2306
|
+
|
|
2307
|
+
// LEGACY POST-SCORECARD HOUSEKEEPING
|
|
2308
|
+
if (s.config.legacyanalytics && !s.config.newanalytics)
|
|
2309
|
+
{
|
|
2310
|
+
// Before returning, create a dummy population deviation test, for
|
|
2191
2311
|
// doHasEqualPopulations() to use later.This is preserving the old calling sequence.
|
|
2192
|
-
let test = s.getTest(
|
|
2312
|
+
let test = s.getTest(T.Test.PopulationDeviation) as T.TestEntry;
|
|
2313
|
+
|
|
2193
2314
|
// Get the raw population deviation
|
|
2194
2315
|
const popDev = scorecard.populationDeviation.raw;
|
|
2316
|
+
|
|
2195
2317
|
// Populate the test entry
|
|
2196
2318
|
test['score'] = popDev;
|
|
2197
2319
|
test['details'] = { 'maxDeviation': scorecard.populationDeviation.notes['maxDeviation'] };
|
|
2320
|
+
|
|
2198
2321
|
// Populate the N+1 summary "district" in district.statistics
|
|
2199
2322
|
let totalPop = s.districts.table.totalPop;
|
|
2200
2323
|
let popDevPct = s.districts.table.popDevPct;
|
|
2201
2324
|
let totalVAP = s.districts.table.totalVAP;
|
|
2325
|
+
|
|
2202
2326
|
const summaryRow = s.districts.numberOfRows() - 1;
|
|
2203
2327
|
totalPop[summaryRow] = p.population.targetSize;
|
|
2204
2328
|
popDevPct[summaryRow] = popDev;
|
|
2205
2329
|
totalVAP[summaryRow] = Math.round(totalVAP[summaryRow] / p.nDistricts);
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2330
|
+
|
|
2331
|
+
} // end
|
|
2332
|
+
|
|
2333
|
+
// Add minority notes
|
|
2334
|
+
scorecard.minority.details['majorityMinority'] = M.getMajorityMinority(s);
|
|
2335
|
+
scorecard.minority.details['vraPreclearance'] = M.getVRASection5(s);
|
|
2336
|
+
|
|
2337
|
+
try
|
|
2338
|
+
{
|
|
2339
|
+
// Add KIWYSI compactness score
|
|
2340
|
+
const kiwysiScores: number[] = C.scoreKIWYSICompactness(s, bLog);
|
|
2341
|
+
// 10-21-2020 - Computing the average score here
|
|
2342
|
+
const avgKIWYSIScore: number = Math.round(U.avgArray(kiwysiScores));
|
|
2343
|
+
scorecard.compactness.details['kiwysi'] = avgKIWYSIScore;
|
|
2344
|
+
|
|
2345
|
+
// 10-21-2020 - Added KIWYSI scores to the by-district details
|
|
2346
|
+
let byDistrict: Score.CompactnessByDistrict[] = scorecard.compactness.details['byDistrict'];
|
|
2347
|
+
const nDistricts = byDistrict.length;
|
|
2348
|
+
|
|
2349
|
+
for (let districtID = 1; districtID <= nDistricts; districtID++)
|
|
2350
|
+
{
|
|
2351
|
+
byDistrict[districtID - 1].kiwysiScore = kiwysiScores[districtID - 1];
|
|
2352
|
+
|
|
2353
|
+
// 09-17-21 - Fix the normalized Polsby–Popper score!
|
|
2354
|
+
const rawPolsby = byDistrict[districtID - 1].rawPolsby;
|
|
2355
|
+
byDistrict[districtID - 1].normalizedPolsby = Rate.ratePolsby(rawPolsby);
|
|
2226
2356
|
}
|
|
2357
|
+
}
|
|
2358
|
+
catch (e)
|
|
2359
|
+
{
|
|
2360
|
+
console.log("Exception caught scoring KIWYSI compactness.");
|
|
2361
|
+
console.log((<Error>e).message);
|
|
2362
|
+
// throw e;
|
|
2363
|
+
}
|
|
2364
|
+
|
|
2365
|
+
return scorecard;
|
|
2366
|
+
}
|
|
2367
|
+
*/
|
|
2368
|
+
// SCORE A PLAN - New using dra-analytics
|
|
2369
|
+
function computeMetrics(p, districtShapes, bLog = false) {
|
|
2370
|
+
if (bLog)
|
|
2371
|
+
console.log("Computing metrics ...");
|
|
2372
|
+
const bLegislative = p.bStateLeg;
|
|
2373
|
+
// Calculate bias & responsiveness metrics ...
|
|
2374
|
+
const byDistrictVf = p.partisanship.byDistrict;
|
|
2375
|
+
const statewideVf = p.partisanship.statewide;
|
|
2376
|
+
let _pS = dra_analytics_1.Partisan.makePartisanScorecard(statewideVf, byDistrictVf, bLog);
|
|
2377
|
+
// Calculate minority representation metrics ...
|
|
2378
|
+
const statewideDemos = p.demographics.statewide;
|
|
2379
|
+
const byDistrictDemos = p.demographics.byDistrict;
|
|
2380
|
+
let _mS = dra_analytics_1.Minority.makeMinorityScorecard(statewideDemos, byDistrictDemos, bLog);
|
|
2381
|
+
// Calculate compactness metrics ...
|
|
2382
|
+
let _cS = dra_analytics_1.Compactness.makeCompactnessScorecard(districtShapes, bLog);
|
|
2383
|
+
// Calculate county-district splitting metrics ...
|
|
2384
|
+
const CxD = p.counties;
|
|
2385
|
+
const _sS = dra_analytics_1.Splitting.makeSplittingScorecard(CxD, bLog);
|
|
2386
|
+
// Calculate population deviation-related metrics ...
|
|
2387
|
+
const totPopByDistrict = p.population.byDistrict;
|
|
2388
|
+
const targetSize = p.population.targetSize;
|
|
2389
|
+
const _pdS = dra_analytics_1.Equal.makePopulationScorecard(totPopByDistrict, targetSize, bLegislative, bLog);
|
|
2390
|
+
const details = {};
|
|
2391
|
+
// Assemble the pieces into new scorecard
|
|
2392
|
+
const scorecard = {
|
|
2393
|
+
partisan: _pS,
|
|
2394
|
+
minority: _mS,
|
|
2395
|
+
compactness: _cS,
|
|
2396
|
+
splitting: _sS,
|
|
2397
|
+
populationDeviation: _pdS,
|
|
2398
|
+
details: details,
|
|
2399
|
+
scratchpad: {} // Hack to pass legacy values between processing steps
|
|
2400
|
+
};
|
|
2401
|
+
return scorecard;
|
|
2402
|
+
}
|
|
2403
|
+
exports.computeMetrics = computeMetrics;
|
|
2404
|
+
function rateKeyDimensions(scorecard, p, bLog = false) {
|
|
2405
|
+
if (bLog)
|
|
2406
|
+
console.log("Rating key dimensions ...");
|
|
2407
|
+
const bLegislative = p.bStateLeg;
|
|
2408
|
+
// Rate proportionality
|
|
2409
|
+
const statewideVf = p.partisanship.statewide;
|
|
2410
|
+
const Sf = scorecard.partisan.bias.estSf;
|
|
2411
|
+
scorecard.partisan.bias.score = dra_analytics_1.Rate.rateProportionality(scorecard.partisan.bias.deviation, statewideVf, Sf);
|
|
2412
|
+
// Rate competititveness
|
|
2413
|
+
scorecard.partisan.responsiveness.score = dra_analytics_1.Rate.rateCompetitiveness(scorecard.partisan.responsiveness.cDf);
|
|
2414
|
+
// Rate minority representation
|
|
2415
|
+
const rawOd = scorecard.minority.opportunityDistricts;
|
|
2416
|
+
const pOd = scorecard.minority.proportionalOpportunities;
|
|
2417
|
+
const rawCd = scorecard.minority.coalitionDistricts;
|
|
2418
|
+
const pCd = scorecard.minority.proportionalCoalitions;
|
|
2419
|
+
scorecard.minority.score = dra_analytics_1.Rate.rateMinorityRepresentation(rawOd, pOd, rawCd, pCd);
|
|
2420
|
+
// Rate compactness
|
|
2421
|
+
const avgReock = scorecard.compactness.avgReock;
|
|
2422
|
+
const avgPolsby = scorecard.compactness.avgPolsby;
|
|
2423
|
+
const reockRating = dra_analytics_1.Rate.rateReock(avgReock);
|
|
2424
|
+
const polsbyRating = dra_analytics_1.Rate.ratePolsby(avgPolsby);
|
|
2425
|
+
scorecard.compactness.score = dra_analytics_1.Rate.rateCompactness(reockRating, polsbyRating);
|
|
2426
|
+
// Rate county- & district-splitting
|
|
2427
|
+
const rawCountySplitting = scorecard.splitting.county;
|
|
2428
|
+
const rawDistrictSplitting = scorecard.splitting.district;
|
|
2429
|
+
const nCounties = p.nCounties;
|
|
2430
|
+
const nDistricts = p.nDistricts;
|
|
2431
|
+
const countyRating = dra_analytics_1.Rate.rateCountySplitting(rawCountySplitting, nCounties, nDistricts, bLegislative);
|
|
2432
|
+
const districtRating = dra_analytics_1.Rate.rateDistrictSplitting(rawDistrictSplitting, bLegislative);
|
|
2433
|
+
const unadjusted = dra_analytics_1.Rate.rateSplitting(countyRating, districtRating);
|
|
2434
|
+
scorecard.splitting.score = dra_analytics_1.Rate.adjustSplittingRating(unadjusted, rawCountySplitting, rawDistrictSplitting);
|
|
2435
|
+
// Rate population deviation
|
|
2436
|
+
const rawDeviation = scorecard.populationDeviation.deviation;
|
|
2437
|
+
scorecard.populationDeviation.score = dra_analytics_1.Rate.ratePopulationDeviation(rawDeviation, bLegislative);
|
|
2438
|
+
// Squirrel away normalized compactness & splitting ratings for the legacy scorecard
|
|
2439
|
+
const keep = {
|
|
2440
|
+
reockScore: reockRating,
|
|
2441
|
+
polsbyScore: polsbyRating,
|
|
2442
|
+
countyScore: countyRating,
|
|
2443
|
+
districtScore: districtRating
|
|
2444
|
+
};
|
|
2445
|
+
scorecard.scratchpad = keep;
|
|
2446
|
+
return scorecard;
|
|
2447
|
+
}
|
|
2448
|
+
exports.rateKeyDimensions = rateKeyDimensions;
|
|
2449
|
+
function thunkScorecard(newScorecard, bLog = false) {
|
|
2450
|
+
if (bLog)
|
|
2451
|
+
console.log("Thunking new scorecard into legacy structure ...");
|
|
2452
|
+
const scratchpad = newScorecard.scratchpad;
|
|
2453
|
+
// Partisan scorecard
|
|
2454
|
+
const pS = U.deepCopy(newScorecard.partisan);
|
|
2455
|
+
// Minority scorecard
|
|
2456
|
+
const mS = U.deepCopy(newScorecard.minority);
|
|
2457
|
+
// Compactness scorecard
|
|
2458
|
+
const reockM = {
|
|
2459
|
+
raw: newScorecard.compactness.avgReock,
|
|
2460
|
+
normalized: scratchpad.reockScore,
|
|
2461
|
+
notes: {}
|
|
2462
|
+
};
|
|
2463
|
+
const polsbyM = {
|
|
2464
|
+
raw: newScorecard.compactness.avgPolsby,
|
|
2465
|
+
normalized: scratchpad.polsbyScore,
|
|
2466
|
+
notes: {}
|
|
2467
|
+
};
|
|
2468
|
+
let cS = {
|
|
2469
|
+
score: newScorecard.compactness.score,
|
|
2470
|
+
reock: reockM,
|
|
2471
|
+
polsby: polsbyM,
|
|
2472
|
+
details: U.deepCopy(newScorecard.compactness.details)
|
|
2473
|
+
};
|
|
2474
|
+
// Relocate byDistrict compactness #'s
|
|
2475
|
+
cS.details.byDistrict = U.deepCopy(newScorecard.compactness.byDistrict);
|
|
2476
|
+
// Add KIWYSI compactness score
|
|
2477
|
+
cS.details['kiwysi'] = newScorecard.compactness.avgKWIWYSI;
|
|
2478
|
+
// Splitting scorecard
|
|
2479
|
+
const countyM = {
|
|
2480
|
+
raw: newScorecard.splitting.county,
|
|
2481
|
+
normalized: scratchpad.countyScore,
|
|
2482
|
+
notes: {}
|
|
2483
|
+
};
|
|
2484
|
+
const districtM = {
|
|
2485
|
+
raw: newScorecard.splitting.district,
|
|
2486
|
+
normalized: scratchpad.districtScore,
|
|
2487
|
+
notes: {}
|
|
2488
|
+
};
|
|
2489
|
+
const sS = {
|
|
2490
|
+
score: newScorecard.splitting.score,
|
|
2491
|
+
county: countyM,
|
|
2492
|
+
district: districtM,
|
|
2493
|
+
details: U.deepCopy(newScorecard.splitting.details)
|
|
2494
|
+
};
|
|
2495
|
+
// Population (equality) scorecard
|
|
2496
|
+
const pdM = {
|
|
2497
|
+
raw: newScorecard.populationDeviation.deviation,
|
|
2498
|
+
normalized: newScorecard.populationDeviation.score,
|
|
2499
|
+
notes: newScorecard.populationDeviation.notes
|
|
2500
|
+
};
|
|
2501
|
+
const pdS = pdM;
|
|
2502
|
+
const scorecard = {
|
|
2503
|
+
partisan: pS,
|
|
2504
|
+
minority: mS,
|
|
2505
|
+
compactness: cS,
|
|
2506
|
+
splitting: sS,
|
|
2507
|
+
populationDeviation: pdS,
|
|
2508
|
+
details: newScorecard.details
|
|
2509
|
+
};
|
|
2227
2510
|
return scorecard;
|
|
2228
2511
|
}
|
|
2229
|
-
exports.
|
|
2512
|
+
exports.thunkScorecard = thunkScorecard;
|
|
2513
|
+
// LEGACY - DELETE
|
|
2514
|
+
/*
|
|
2515
|
+
export function compareScorecards(altLegacyScorecard: Score.Scorecard, legacyScorecard: Score.Scorecard, bLog: boolean = false): void
|
|
2516
|
+
{
|
|
2517
|
+
if (bLog) console.log("Comparing new & legacy scorecards ...");
|
|
2518
|
+
|
|
2519
|
+
// A pretty loose tolerance, because we're not trimming values in the new analytics
|
|
2520
|
+
const DECIMAL_PLACES = 2;
|
|
2521
|
+
const DEFAULT_TOLERANCE = toleranceFn(DECIMAL_PLACES);
|
|
2522
|
+
|
|
2523
|
+
// COMPARE PARTISAN SCORECARD
|
|
2524
|
+
|
|
2525
|
+
// Compare BIAS section
|
|
2526
|
+
|
|
2527
|
+
matchFloats("bestS", altLegacyScorecard.partisan.bias.bestS, legacyScorecard.partisan.bias.bestS, DEFAULT_TOLERANCE);
|
|
2528
|
+
matchFloats("bestSf", altLegacyScorecard.partisan.bias.bestSf, legacyScorecard.partisan.bias.bestSf, DEFAULT_TOLERANCE);
|
|
2529
|
+
matchFloats("estS", altLegacyScorecard.partisan.bias.estS, legacyScorecard.partisan.bias.estS, DEFAULT_TOLERANCE);
|
|
2530
|
+
matchFloats("estSf", altLegacyScorecard.partisan.bias.estSf, legacyScorecard.partisan.bias.estSf, DEFAULT_TOLERANCE);
|
|
2531
|
+
matchFloats("deviation", altLegacyScorecard.partisan.bias.deviation, legacyScorecard.partisan.bias.deviation, DEFAULT_TOLERANCE);
|
|
2532
|
+
matchInts("proportionality/score", altLegacyScorecard.partisan.bias.score, legacyScorecard.partisan.bias.score);
|
|
2533
|
+
|
|
2534
|
+
matchFloats("tOf", altLegacyScorecard.partisan.bias.tOf as number, legacyScorecard.partisan.bias.tOf as number, DEFAULT_TOLERANCE);
|
|
2535
|
+
matchFloats("fptpS", altLegacyScorecard.partisan.bias.fptpS as number, legacyScorecard.partisan.bias.fptpS as number, DEFAULT_TOLERANCE);
|
|
2536
|
+
matchFloats("bS50", altLegacyScorecard.partisan.bias.bS50 as number, legacyScorecard.partisan.bias.bS50 as number, DEFAULT_TOLERANCE);
|
|
2537
|
+
matchFloats("deviation", altLegacyScorecard.partisan.bias.deviation, legacyScorecard.partisan.bias.deviation, DEFAULT_TOLERANCE);
|
|
2538
|
+
matchUndefinableFloats("decl", altLegacyScorecard.partisan.bias.decl, legacyScorecard.partisan.bias.decl, toleranceFn(1));
|
|
2539
|
+
|
|
2540
|
+
let rvPointsAlt = altLegacyScorecard.partisan.bias.rvPoints;
|
|
2541
|
+
let rvPoints = legacyScorecard.partisan.bias.rvPoints;
|
|
2542
|
+
if (matchUndefinedness("rvPoints", rvPointsAlt, rvPoints)) {
|
|
2543
|
+
rvPointsAlt = rvPointsAlt as L.rVpoints;
|
|
2544
|
+
rvPoints = rvPoints as L.rVpoints;
|
|
2545
|
+
matchFloats("rvPoints/Sb", rvPointsAlt.Sb, rvPoints.Sb, DEFAULT_TOLERANCE);
|
|
2546
|
+
matchFloats("rvPoints/Ra", rvPointsAlt.Ra, rvPoints.Ra, DEFAULT_TOLERANCE);
|
|
2547
|
+
matchFloats("rvPoints/Rb", rvPointsAlt.Rb, rvPoints.Rb, DEFAULT_TOLERANCE);
|
|
2548
|
+
matchFloats("rvPoints/Va", rvPointsAlt.Va, rvPoints.Va, DEFAULT_TOLERANCE);
|
|
2549
|
+
matchFloats("rvPoints/Vb", rvPointsAlt.Vb, rvPoints.Vb, DEFAULT_TOLERANCE);
|
|
2550
|
+
}
|
|
2551
|
+
|
|
2552
|
+
matchFloats("gSym", altLegacyScorecard.partisan.bias.gSym as number, legacyScorecard.partisan.bias.gSym as number, DEFAULT_TOLERANCE);
|
|
2553
|
+
matchUndefinableFloats("gamma", altLegacyScorecard.partisan.bias.gamma, legacyScorecard.partisan.bias.gamma, DEFAULT_TOLERANCE);
|
|
2554
|
+
matchFloats("eG", altLegacyScorecard.partisan.bias.eG as number, legacyScorecard.partisan.bias.eG as number, DEFAULT_TOLERANCE);
|
|
2555
|
+
matchUndefinableFloats("bSV", altLegacyScorecard.partisan.bias.bSV, legacyScorecard.partisan.bias.bSV, DEFAULT_TOLERANCE);
|
|
2556
|
+
matchFloats("prop", altLegacyScorecard.partisan.bias.prop as number, legacyScorecard.partisan.bias.prop as number, DEFAULT_TOLERANCE);
|
|
2557
|
+
matchFloats("mMs", altLegacyScorecard.partisan.bias.mMs as number, legacyScorecard.partisan.bias.mMs as number, DEFAULT_TOLERANCE);
|
|
2558
|
+
matchFloats("mMd", altLegacyScorecard.partisan.bias.mMd as number, legacyScorecard.partisan.bias.mMd as number, DEFAULT_TOLERANCE);
|
|
2559
|
+
matchUndefinableFloats("lO", altLegacyScorecard.partisan.bias.lO, legacyScorecard.partisan.bias.lO, DEFAULT_TOLERANCE);
|
|
2560
|
+
|
|
2561
|
+
// Compare Impact section
|
|
2562
|
+
|
|
2563
|
+
matchFloats("unearnedS", altLegacyScorecard.partisan.impact.unearnedS, legacyScorecard.partisan.impact.unearnedS, DEFAULT_TOLERANCE);
|
|
2564
|
+
// Note - We don't use the impact score, so we don't compute it in the new scorecard.
|
|
2565
|
+
// matchInts("impact/score", altLegacyScorecard.partisan.impact.score as number, legacyScorecard.partisan.impact.score);
|
|
2566
|
+
|
|
2567
|
+
// Compare Responsiveness section
|
|
2568
|
+
|
|
2569
|
+
matchUndefinableFloats("bigR", altLegacyScorecard.partisan.responsiveness.bigR, legacyScorecard.partisan.responsiveness.bigR, DEFAULT_TOLERANCE);
|
|
2570
|
+
matchUndefinableFloats("littleR", altLegacyScorecard.partisan.responsiveness.littleR, legacyScorecard.partisan.responsiveness.littleR, DEFAULT_TOLERANCE);
|
|
2571
|
+
matchUndefinableFloats("mIR", altLegacyScorecard.partisan.responsiveness.mIR, legacyScorecard.partisan.responsiveness.mIR, DEFAULT_TOLERANCE);
|
|
2572
|
+
matchFloats("rD", altLegacyScorecard.partisan.responsiveness.rD as number, legacyScorecard.partisan.responsiveness.rD as number, DEFAULT_TOLERANCE);
|
|
2573
|
+
matchFloats("rDf", altLegacyScorecard.partisan.responsiveness.rDf as number, legacyScorecard.partisan.responsiveness.rDf as number, DEFAULT_TOLERANCE);
|
|
2574
|
+
|
|
2575
|
+
matchFloats("cSimple", altLegacyScorecard.partisan.responsiveness.cSimple, legacyScorecard.partisan.responsiveness.cSimple, DEFAULT_TOLERANCE);
|
|
2576
|
+
matchFloats("cD", altLegacyScorecard.partisan.responsiveness.cD, legacyScorecard.partisan.responsiveness.cD, DEFAULT_TOLERANCE);
|
|
2577
|
+
matchFloats("cDf", altLegacyScorecard.partisan.responsiveness.cDf, legacyScorecard.partisan.responsiveness.cDf, DEFAULT_TOLERANCE);
|
|
2578
|
+
matchInts("competitiveness/score", altLegacyScorecard.partisan.responsiveness.score as number, legacyScorecard.partisan.responsiveness.score);
|
|
2579
|
+
|
|
2580
|
+
matchPointArrays("dSVpoints", altLegacyScorecard.partisan.dSVpoints, legacyScorecard.partisan.dSVpoints, DEFAULT_TOLERANCE);
|
|
2581
|
+
matchPointArrays("rSVpoints", altLegacyScorecard.partisan.rSVpoints, legacyScorecard.partisan.rSVpoints, DEFAULT_TOLERANCE);
|
|
2582
|
+
|
|
2583
|
+
matchUndefinableFloats("averageDVf", altLegacyScorecard.partisan.averageDVf, legacyScorecard.partisan.averageDVf, DEFAULT_TOLERANCE);
|
|
2584
|
+
matchUndefinableFloats("averageRVf", altLegacyScorecard.partisan.averageRVf, legacyScorecard.partisan.averageRVf, DEFAULT_TOLERANCE);
|
|
2585
|
+
matchDicts("partisan/details", altLegacyScorecard.partisan.details, legacyScorecard.partisan.details);
|
|
2586
|
+
|
|
2587
|
+
// COMPARE MINORITY SCORECARD
|
|
2588
|
+
|
|
2589
|
+
matchObjectsAndArrays("pivotByDemographic", altLegacyScorecard.minority.pivotByDemographic, legacyScorecard.minority.pivotByDemographic);
|
|
2590
|
+
matchFloats("opportunityDistricts", altLegacyScorecard.minority.opportunityDistricts, legacyScorecard.minority.opportunityDistricts, DEFAULT_TOLERANCE);
|
|
2591
|
+
matchFloats("coalitionDistricts", altLegacyScorecard.minority.coalitionDistricts, legacyScorecard.minority.coalitionDistricts, DEFAULT_TOLERANCE);
|
|
2592
|
+
matchInts("minority/score", altLegacyScorecard.minority.score as number, legacyScorecard.minority.score);
|
|
2593
|
+
matchDicts("minority/details", altLegacyScorecard.minority.details, legacyScorecard.minority.details);
|
|
2594
|
+
|
|
2595
|
+
// COMPARE COMPACTNESS SCORECARD
|
|
2596
|
+
|
|
2597
|
+
matchFloats("reock", altLegacyScorecard.compactness.reock.raw, legacyScorecard.compactness.reock.raw, DEFAULT_TOLERANCE);
|
|
2598
|
+
matchInts("reock/normalized", altLegacyScorecard.compactness.reock.normalized, legacyScorecard.compactness.reock.normalized);
|
|
2599
|
+
matchFloats("polsby", altLegacyScorecard.compactness.polsby.raw, legacyScorecard.compactness.polsby.raw, DEFAULT_TOLERANCE);
|
|
2600
|
+
matchInts("polsby/normalized", altLegacyScorecard.compactness.polsby.normalized, legacyScorecard.compactness.polsby.normalized);
|
|
2601
|
+
matchInts("compactness/score", altLegacyScorecard.compactness.score as number, legacyScorecard.compactness.score);
|
|
2602
|
+
// Compare 'byDistrict' results separately
|
|
2603
|
+
const _altCompactnessByDistrict = Utils.deepCopy(altLegacyScorecard.compactness.details['byDistrict']);
|
|
2604
|
+
const _CompactnessByDistrict = Utils.deepCopy(legacyScorecard.compactness.details['byDistrict']);
|
|
2605
|
+
const _altCompactnessDetails = Utils.deepCopy(altLegacyScorecard.compactness.details);
|
|
2606
|
+
const _CompactnessDetails = Utils.deepCopy(legacyScorecard.compactness.details);
|
|
2607
|
+
delete _altCompactnessDetails['byDistrict'];
|
|
2608
|
+
delete _CompactnessDetails['byDistrict'];
|
|
2609
|
+
matchDicts("compactness/details", _altCompactnessDetails, _CompactnessDetails);
|
|
2610
|
+
matchCompactnessByDistrict("compactness/byDistrict", _altCompactnessByDistrict, _CompactnessByDistrict, DEFAULT_TOLERANCE);
|
|
2611
|
+
|
|
2612
|
+
// COMPARE SPLITTING SCORECARD
|
|
2613
|
+
|
|
2614
|
+
matchFloats("county", altLegacyScorecard.splitting.county.raw, legacyScorecard.splitting.county.raw, DEFAULT_TOLERANCE);
|
|
2615
|
+
matchInts("county/normalized", altLegacyScorecard.splitting.county.normalized, legacyScorecard.splitting.county.normalized);
|
|
2616
|
+
matchFloats("district", altLegacyScorecard.splitting.district.raw, legacyScorecard.splitting.district.raw, DEFAULT_TOLERANCE);
|
|
2617
|
+
matchInts("district/normalized", altLegacyScorecard.splitting.district.normalized, legacyScorecard.splitting.district.normalized);
|
|
2618
|
+
matchInts("splitting/score", altLegacyScorecard.splitting.score as number, legacyScorecard.splitting.score);
|
|
2619
|
+
matchDicts("splitting/details", altLegacyScorecard.splitting.details, legacyScorecard.splitting.details);
|
|
2620
|
+
|
|
2621
|
+
// COMPARE POPULATION SCORECARD
|
|
2622
|
+
|
|
2623
|
+
matchFloats("popdev", altLegacyScorecard.populationDeviation.raw, legacyScorecard.populationDeviation.raw, DEFAULT_TOLERANCE);
|
|
2624
|
+
matchInts("popdev/score", altLegacyScorecard.populationDeviation.normalized, legacyScorecard.populationDeviation.normalized);
|
|
2625
|
+
matchDicts("popdev/notes", altLegacyScorecard.populationDeviation.notes, legacyScorecard.populationDeviation.notes);
|
|
2626
|
+
|
|
2627
|
+
matchDicts("details", altLegacyScorecard.details, legacyScorecard.details);
|
|
2628
|
+
}
|
|
2629
|
+
*/
|
|
2630
|
+
// LEGACY - DELETE
|
|
2631
|
+
/*
|
|
2632
|
+
// Matching helpers
|
|
2633
|
+
|
|
2634
|
+
function matchFloats(property: string, received: number, good: number, tolerance: number): boolean
|
|
2635
|
+
{
|
|
2636
|
+
if (Utils.areRoughlyEqual(received, good, tolerance)) {
|
|
2637
|
+
return true;
|
|
2638
|
+
}
|
|
2639
|
+
else {
|
|
2640
|
+
console.log(`${property} does not match: ${good} expected. Received ${received}.`);
|
|
2641
|
+
return false;
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
|
|
2645
|
+
function toleranceFn (places: number): number{
|
|
2646
|
+
return 0.5 / Math.pow(10, places)
|
|
2647
|
+
}
|
|
2648
|
+
|
|
2649
|
+
function matchInts(property: string, received: number, good: number): boolean
|
|
2650
|
+
{
|
|
2651
|
+
if (received == good) {
|
|
2652
|
+
return true;
|
|
2653
|
+
}
|
|
2654
|
+
else {
|
|
2655
|
+
console.log(`${property} does not match: ${good} expected. Received ${received}.`);
|
|
2656
|
+
return false;
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2659
|
+
|
|
2660
|
+
function matchUndefinableFloats(property: string, received: number | undefined, good: number | undefined, tolerance: number): boolean
|
|
2661
|
+
{
|
|
2662
|
+
if ((received === undefined) && (good === undefined)) return true;
|
|
2663
|
+
if ((received === undefined) || (good === undefined)) {
|
|
2664
|
+
console.log(`${property} does not match: ${good} expected. Received ${received}.`);
|
|
2665
|
+
return false;
|
|
2666
|
+
}
|
|
2667
|
+
|
|
2668
|
+
return matchFloats(property, received as number, good as number, tolerance);
|
|
2669
|
+
}
|
|
2670
|
+
|
|
2671
|
+
// https://stackoverflow.com/questions/13142968/deep-comparison-of-objects-arrays
|
|
2672
|
+
function matchObjectsAndArrays (property: string, received: object | undefined, good: object | undefined): boolean
|
|
2673
|
+
{
|
|
2674
|
+
if ((received === undefined) && (good === undefined)) return true; // Both undefined
|
|
2675
|
+
|
|
2676
|
+
if ((received === undefined) || (good === undefined)) { // One undefined but not the other
|
|
2677
|
+
console.log(`${property} does not match: ${good} expected. Received ${received}.`);
|
|
2678
|
+
return false;
|
|
2679
|
+
}
|
|
2680
|
+
|
|
2681
|
+
if ((Object.keys(received).length === 0) && (Object.keys(good).length === 0)) return true; // Both empty
|
|
2682
|
+
|
|
2683
|
+
if (JSON.stringify(received) === JSON.stringify(good)) return true; // Contents match
|
|
2684
|
+
|
|
2685
|
+
console.log(`${property} objects or arrays do not match.`); // Contents don't match
|
|
2686
|
+
return false;
|
|
2687
|
+
}
|
|
2688
|
+
|
|
2689
|
+
function matchDicts (property: string, received: L.Dict, good: L.Dict): boolean
|
|
2690
|
+
{
|
|
2691
|
+
if ((Object.keys(received).length === 0) && (Object.keys(good).length === 0)) return true; // Both empty
|
|
2692
|
+
|
|
2693
|
+
const receivedStr: string = JSON.stringify(received);
|
|
2694
|
+
const goodStr = JSON.stringify(good);
|
|
2695
|
+
if (receivedStr === goodStr) return true; // Contents match
|
|
2696
|
+
|
|
2697
|
+
console.log(`${property} does not match: ${goodStr} expected. Received ${receivedStr}.`); // Contents don't match
|
|
2698
|
+
|
|
2699
|
+
return false;
|
|
2700
|
+
}
|
|
2701
|
+
|
|
2702
|
+
function matchUndefinedness(property: string, received: any | undefined, good: any | undefined): boolean
|
|
2703
|
+
{
|
|
2704
|
+
if ((received === undefined) && (good === undefined)) return true;
|
|
2705
|
+
|
|
2706
|
+
if ((received === undefined) || (good === undefined)) return false;
|
|
2707
|
+
|
|
2708
|
+
return true;
|
|
2709
|
+
}
|
|
2710
|
+
|
|
2711
|
+
function matchPointArrays(property: string, received: L.SVpoint[], good: L.SVpoint[], tolerance: number): boolean
|
|
2712
|
+
{
|
|
2713
|
+
if (received.length != good.length)
|
|
2714
|
+
{
|
|
2715
|
+
console.log(`${property} does not match: Different number of points.`);
|
|
2716
|
+
return false;
|
|
2717
|
+
}
|
|
2718
|
+
|
|
2719
|
+
for (var i = 0; i < received.length; i++)
|
|
2720
|
+
{
|
|
2721
|
+
const vLabel: string = property + '/' + i.toString() + '/' + 'v';
|
|
2722
|
+
const sLabel: string = property + '/' + i.toString() + '/' + 's';
|
|
2723
|
+
if (!matchFloats(vLabel, received[i].v, good[i].v, tolerance)) return false;
|
|
2724
|
+
if (!matchFloats(sLabel, received[i].s, good[i].s, tolerance)) return false;
|
|
2725
|
+
}
|
|
2726
|
+
|
|
2727
|
+
return true;
|
|
2728
|
+
}
|
|
2729
|
+
|
|
2730
|
+
function matchCompactnessByDistrict(property: string, received: L.CompactnessByDistrict[], good: L.CompactnessByDistrict[], tolerance: number): boolean
|
|
2731
|
+
{
|
|
2732
|
+
if (received.length != good.length)
|
|
2733
|
+
{
|
|
2734
|
+
console.log(`${property} does not match: Different number of districts.`);
|
|
2735
|
+
return false;
|
|
2736
|
+
}
|
|
2737
|
+
|
|
2738
|
+
let bMismatched = false;
|
|
2739
|
+
for (var i = 0; i < received.length; i++)
|
|
2740
|
+
{
|
|
2741
|
+
const rawReock: string = property + '/' + i.toString() + '/' + 'rawReock';
|
|
2742
|
+
if (!matchFloats(rawReock, received[i].rawReock, good[i].rawReock, tolerance)) bMismatched = true;
|
|
2743
|
+
|
|
2744
|
+
const normalizedReock: string = property + '/' + i.toString() + '/' + 'normalizedReock';
|
|
2745
|
+
if (!matchInts(normalizedReock, received[i].normalizedReock, good[i].normalizedReock)) bMismatched = true;
|
|
2746
|
+
|
|
2747
|
+
const rawPolsby: string = property + '/' + i.toString() + '/' + 'rawPolsby';
|
|
2748
|
+
if (!matchFloats(rawPolsby, received[i].rawPolsby, good[i].rawPolsby, tolerance)) bMismatched = true;
|
|
2749
|
+
|
|
2750
|
+
// TODO
|
|
2751
|
+
// 09-17-21 - By-district Polsby–Popper ratings from dra-score in production are wrong!
|
|
2752
|
+
const normalizedPolsby: string = property + '/' + i.toString() + '/' + 'normalizedPolsby';
|
|
2753
|
+
if (!matchInts(normalizedPolsby, received[i].normalizedPolsby, good[i].normalizedPolsby)) bMismatched = true;
|
|
2754
|
+
|
|
2755
|
+
const kiwysiScore: string = property + '/' + i.toString() + '/' + 'kiwysiScore';
|
|
2756
|
+
if (!matchInts(kiwysiScore, received[i].kiwysiScore as number, good[i].kiwysiScore as number)) bMismatched = true;
|
|
2757
|
+
}
|
|
2758
|
+
if (bMismatched = true) return false;
|
|
2759
|
+
|
|
2760
|
+
return true;
|
|
2761
|
+
}
|
|
2762
|
+
*/
|
|
2763
|
+
// LEGACY - DELETE
|
|
2764
|
+
// Not used, after all
|
|
2765
|
+
/* Modeled after David Sielaff's Jest extension 'toBeArrayWithValuesCloseTo'
|
|
2766
|
+
|
|
2767
|
+
function matchFloatArrays(property: string, received: Array<any>, expected: Array<any>, tolerance: number): boolean
|
|
2768
|
+
{
|
|
2769
|
+
// Note - Not sure what this check was for ...
|
|
2770
|
+
// if (expected.length == 0) {
|
|
2771
|
+
// return {
|
|
2772
|
+
// message: () => `expected arrays of same size`,
|
|
2773
|
+
// pass: received.length == 0
|
|
2774
|
+
// }
|
|
2775
|
+
// }
|
|
2776
|
+
|
|
2777
|
+
if (typeof expected[0] === "number")
|
|
2778
|
+
{
|
|
2779
|
+
if (typeof received[0] === "number") return matchArrays1d(property, received, expected, tolerance);
|
|
2780
|
+
|
|
2781
|
+
console.log(`${property} does not match: array doesn't contain numbers.`);
|
|
2782
|
+
return false;
|
|
2783
|
+
}
|
|
2784
|
+
|
|
2785
|
+
// Note - Ditto
|
|
2786
|
+
// if (expected[0].length == 0) {
|
|
2787
|
+
// return {
|
|
2788
|
+
// message: () => `expected arrays of same size`,
|
|
2789
|
+
// pass: received[0].length == 0
|
|
2790
|
+
// }
|
|
2791
|
+
// }
|
|
2792
|
+
|
|
2793
|
+
if (expected[0] instanceof Array && typeof expected[0][0] === "number")
|
|
2794
|
+
{
|
|
2795
|
+
if (received[0] instanceof Array && typeof received[0][0] === "number") return matchArrays2d(property, received, expected, tolerance);
|
|
2796
|
+
|
|
2797
|
+
console.log(`${property} does not match: arrays don't have the same dimensionality and content.`);
|
|
2798
|
+
return false;
|
|
2799
|
+
}
|
|
2800
|
+
|
|
2801
|
+
console.log(`${property}: Expected 1d or 2d arrays.`);
|
|
2802
|
+
return false;
|
|
2803
|
+
}
|
|
2804
|
+
|
|
2805
|
+
function matchArrays1d(property: string, received: number[], expected: number[], tolerance: number): boolean
|
|
2806
|
+
{
|
|
2807
|
+
if (received.length != expected.length)
|
|
2808
|
+
{
|
|
2809
|
+
console.log(`${property} does not match: arrays have different lengths.`);
|
|
2810
|
+
return false;
|
|
2811
|
+
}
|
|
2812
|
+
|
|
2813
|
+
for (var index = 0; index < received.length; index++)
|
|
2814
|
+
{
|
|
2815
|
+
const cell: string = property + '/' + index.toString();
|
|
2816
|
+
if (!matchFloats(cell, received[index], expected[index], tolerance)) return false;
|
|
2817
|
+
}
|
|
2818
|
+
|
|
2819
|
+
return true;
|
|
2820
|
+
}
|
|
2821
|
+
|
|
2822
|
+
function matchArrays2d(property: string, received: number[][], expected: number[][], tolerance: number): boolean
|
|
2823
|
+
{
|
|
2824
|
+
if (received.length != expected.length)
|
|
2825
|
+
{
|
|
2826
|
+
console.log(`${property} does not match: arrays have different lengths.`);
|
|
2827
|
+
return false;
|
|
2828
|
+
}
|
|
2829
|
+
|
|
2830
|
+
for (var index = 0; index < received.length; index++)
|
|
2831
|
+
{
|
|
2832
|
+
if (received[index].length != expected[index].length)
|
|
2833
|
+
{
|
|
2834
|
+
console.log(`${property} does not match: arrays have different lengths.`);
|
|
2835
|
+
return false;
|
|
2836
|
+
}
|
|
2837
|
+
|
|
2838
|
+
for (var inner = 0; inner < received[index].length; inner++)
|
|
2839
|
+
{
|
|
2840
|
+
const cell: string = property + '/' + index.toString() + '/' + inner.toString();
|
|
2841
|
+
if (!matchFloats(cell, received[index][inner], expected[index][inner], tolerance)) return false;
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2844
|
+
|
|
2845
|
+
return true;
|
|
2846
|
+
}
|
|
2847
|
+
*/
|
|
2230
2848
|
|
|
2231
2849
|
|
|
2232
2850
|
/***/ }),
|
|
@@ -2242,24 +2860,27 @@ exports.scorePlan = scorePlan;
|
|
|
2242
2860
|
// GLOBAL CONSTANTS
|
|
2243
2861
|
//
|
|
2244
2862
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
2245
|
-
exports.
|
|
2863
|
+
exports.OUT_OF_STATE = exports.NUMBER_OF_ITEMS_TO_REPORT = exports.NOT_ASSIGNED = exports.PRECISION = void 0;
|
|
2246
2864
|
// Keep four decimal places for fractions [0–1], i.e.,
|
|
2247
2865
|
// keep two decimal places for %'s [0–100].
|
|
2248
2866
|
exports.PRECISION = 4;
|
|
2867
|
+
// LEGACY - Not used
|
|
2249
2868
|
// Normalized scores [0-100]
|
|
2250
|
-
|
|
2869
|
+
// export const NORMALIZED_RANGE: number = 100;
|
|
2251
2870
|
// The dummy district ID for features not assigned districts yet
|
|
2252
2871
|
exports.NOT_ASSIGNED = 0;
|
|
2253
2872
|
// # of items to report as problematic (e.g., features, districts, etc.)
|
|
2254
2873
|
exports.NUMBER_OF_ITEMS_TO_REPORT = 10;
|
|
2255
2874
|
// The virtual geoID for "neighbors" in other states
|
|
2256
2875
|
exports.OUT_OF_STATE = "OUT_OF_STATE";
|
|
2876
|
+
// LEGACY - Not used
|
|
2257
2877
|
// "Roughly equal" = average census block size / 2
|
|
2258
|
-
const AVERAGE_BLOCK_SIZE = 30;
|
|
2259
|
-
|
|
2878
|
+
// const AVERAGE_BLOCK_SIZE = 30;
|
|
2879
|
+
// export const EQUAL_TOLERANCE: number = AVERAGE_BLOCK_SIZE / 2;
|
|
2880
|
+
// LEGACY - Not used
|
|
2260
2881
|
// County & district splitting weights
|
|
2261
|
-
|
|
2262
|
-
|
|
2882
|
+
// export const COUNTY_SPLITTING_WEIGHT = 0.8;
|
|
2883
|
+
// export const DISTRICT_SPLITTING_WEIGHT = 1.0 - COUNTY_SPLITTING_WEIGHT;
|
|
2263
2884
|
// 2020
|
|
2264
2885
|
// export const SHAPES = "2010 VTD shapes";
|
|
2265
2886
|
|
|
@@ -2270,16 +2891,20 @@ exports.DISTRICT_SPLITTING_WEIGHT = 1.0 - exports.COUNTY_SPLITTING_WEIGHT;
|
|
|
2270
2891
|
/*!**********************!*\
|
|
2271
2892
|
!*** ./src/types.ts ***!
|
|
2272
2893
|
\**********************/
|
|
2273
|
-
/***/ ((__unused_webpack_module, exports
|
|
2894
|
+
/***/ ((__unused_webpack_module, exports) => {
|
|
2274
2895
|
|
|
2275
2896
|
|
|
2276
2897
|
//
|
|
2277
2898
|
// TYPE DEFINITIONS
|
|
2278
2899
|
//
|
|
2279
2900
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2901
|
+
// LEGACY - DELETE
|
|
2902
|
+
// export
|
|
2903
|
+
// {
|
|
2904
|
+
// Profile, Scorecard,
|
|
2905
|
+
// // LEGACY - DELETE
|
|
2906
|
+
// // estSeatProbability
|
|
2907
|
+
// } from '@dra2020/dra-score';
|
|
2283
2908
|
// END
|
|
2284
2909
|
|
|
2285
2910
|
|
|
@@ -2315,21 +2940,23 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
2315
2940
|
return result;
|
|
2316
2941
|
};
|
|
2317
2942
|
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 =
|
|
2943
|
+
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
2944
|
const DT = __importStar(__webpack_require__(/*! @dra2020/dra-types */ "@dra2020/dra-types"));
|
|
2320
2945
|
const _data_1 = __webpack_require__(/*! ./_data */ "./src/_data.ts");
|
|
2321
2946
|
const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"));
|
|
2322
2947
|
// PLAN HELPERS
|
|
2948
|
+
// LEGACY - Not used
|
|
2323
2949
|
// Is a "neighbor" in state?
|
|
2324
|
-
function isInState(geoID)
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2950
|
+
// export function isInState(geoID: string): boolean
|
|
2951
|
+
// {
|
|
2952
|
+
// return geoID != S.OUT_OF_STATE;
|
|
2953
|
+
// }
|
|
2954
|
+
// LEGACY - Not used
|
|
2328
2955
|
// Is a "neighbor" out of state?
|
|
2329
|
-
function isOutOfState(geoID)
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2956
|
+
// export function isOutOfState(geoID: string): boolean
|
|
2957
|
+
// {
|
|
2958
|
+
// return geoID == S.OUT_OF_STATE;
|
|
2959
|
+
// }
|
|
2333
2960
|
// Get the districtID to which a geoID is assigned
|
|
2334
2961
|
function getDistrict(plan, geoID) {
|
|
2335
2962
|
// All geoIDs in a state *should be* assigned to a district (including the
|
|
@@ -2780,16 +3407,6 @@ module.exports = require("@dra2020/dra-analytics");
|
|
|
2780
3407
|
|
|
2781
3408
|
/***/ }),
|
|
2782
3409
|
|
|
2783
|
-
/***/ "@dra2020/dra-score":
|
|
2784
|
-
/*!*************************************!*\
|
|
2785
|
-
!*** external "@dra2020/dra-score" ***!
|
|
2786
|
-
\*************************************/
|
|
2787
|
-
/***/ ((module) => {
|
|
2788
|
-
|
|
2789
|
-
module.exports = require("@dra2020/dra-score");
|
|
2790
|
-
|
|
2791
|
-
/***/ }),
|
|
2792
|
-
|
|
2793
3410
|
/***/ "@dra2020/dra-types":
|
|
2794
3411
|
/*!*************************************!*\
|
|
2795
3412
|
!*** external "@dra2020/dra-types" ***!
|