@dra2020/district-analytics 3.0.0 → 3.2.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/cli.js +2124 -531
- package/dist/cli.js.map +1 -1
- package/dist/district-analytics.js +247 -356
- package/dist/district-analytics.js.map +1 -1
- package/dist/src/_api.d.ts +2 -1
- package/dist/src/geofeature.d.ts +2 -0
- package/dist/src/score.d.ts +1 -1
- package/package.json +3 -2
|
@@ -121,6 +121,7 @@ const analyze_1 = __webpack_require__(/*! ./analyze */ "./src/analyze.ts");
|
|
|
121
121
|
const score_1 = __webpack_require__(/*! ./score */ "./src/score.ts");
|
|
122
122
|
const results_1 = __webpack_require__(/*! ./results */ "./src/results.ts");
|
|
123
123
|
const results_2 = __webpack_require__(/*! ./results */ "./src/results.ts");
|
|
124
|
+
const geofeature_1 = __webpack_require__(/*! ./geofeature */ "./src/geofeature.ts");
|
|
124
125
|
const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
125
126
|
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
126
127
|
class AnalyticsSession {
|
|
@@ -140,45 +141,48 @@ class AnalyticsSession {
|
|
|
140
141
|
this.features = new D.Features(this, SessionRequest['data'], this.config['datasets']);
|
|
141
142
|
this.plan = new D.Plan(this, SessionRequest['plan']);
|
|
142
143
|
this.districts = new D.Districts(this, SessionRequest['districtShapes']);
|
|
143
|
-
// TODO -
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
144
|
+
// TODO - SCORE: Toggle
|
|
145
|
+
if (this.useLegacy()) {
|
|
146
|
+
console.log("Using legacy district-analytics.");
|
|
147
|
+
// NOTE: I've pulled these out of the individual analytics to here. Eventually,
|
|
148
|
+
// we could want them to passed into an analytics session as data, along with
|
|
149
|
+
// everything else. For now, this keeps branching out of the main code.
|
|
150
|
+
results_1.doConfigureScales(this);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
console.log("Using dra-score analytics.");
|
|
154
|
+
// TODO - SCORE: Temporary HACK. Query dra-score for threshold.
|
|
155
|
+
results_1.doConfigureScales(this);
|
|
156
|
+
}
|
|
154
157
|
}
|
|
155
158
|
processConfig(config) {
|
|
156
159
|
// NOTE - Session settings are required:
|
|
157
160
|
// - Analytics suites can be defaulted to all with [], but
|
|
158
161
|
// - Dataset keys must be explicitly specified with 'dataset'
|
|
159
|
-
// TODO -
|
|
160
|
-
|
|
161
|
-
// If the config passed in has no suites = [], use the default suites
|
|
162
|
-
if (U.isArrayEmpty(config['suites'])) {
|
|
163
|
-
config['suites'] = defaultSuites;
|
|
164
|
-
}
|
|
162
|
+
// TODO - SCORE: Delete
|
|
163
|
+
config['suites'] = [0 /* Legal */, 1 /* Fair */, 2 /* Best */];
|
|
165
164
|
// Default the Census & redistricting cycle to 2010
|
|
166
165
|
if (!(U.keyExists('cycle', config)))
|
|
167
166
|
config['cycle'] = 2010;
|
|
168
167
|
return config;
|
|
169
168
|
}
|
|
169
|
+
// TODO - SCORE: Toggle = Invert this logic
|
|
170
|
+
useLegacy() {
|
|
171
|
+
// TODO - SCORE: Opt-out
|
|
172
|
+
return (U.keyExists('useScore', this.config) && (this.config['useScore'] != null) && (!(this.config['useScore']))) ? true : false;
|
|
173
|
+
// TODO - SCORE: Opt-in
|
|
174
|
+
// return (U.keyExists('useScore', this.config) && (this.config['useScore'])) ? false : true;
|
|
175
|
+
}
|
|
170
176
|
// Using the the data in the analytics session, calculate all the
|
|
171
177
|
// analytics & validations, saving/updating the individual test results.
|
|
172
|
-
analyzePlan(bLog = false) {
|
|
178
|
+
analyzePlan(bLog = false, overridesJSON) {
|
|
173
179
|
try {
|
|
174
180
|
preprocess_1.doPreprocessData(this, bLog);
|
|
175
181
|
analyze_1.doAnalyzeDistricts(this, bLog);
|
|
176
182
|
analyze_1.doAnalyzePlan(this, bLog);
|
|
177
183
|
// TODO - SCORE
|
|
178
184
|
this._profile = score_1.profilePlan(this, bLog);
|
|
179
|
-
|
|
180
|
-
this._scorecard = score_1.scorePlan(this._profile, bLog);
|
|
181
|
-
// TODO - SCORE
|
|
185
|
+
this._scorecard = score_1.scorePlan(this, this._profile, bLog, overridesJSON);
|
|
182
186
|
results_1.doAnalyzePostProcessing(this, bLog);
|
|
183
187
|
}
|
|
184
188
|
catch (_a) {
|
|
@@ -199,13 +203,6 @@ class AnalyticsSession {
|
|
|
199
203
|
getPlanScorecard(bLog = false) {
|
|
200
204
|
return this._scorecard;
|
|
201
205
|
}
|
|
202
|
-
/* TODO - SCORE
|
|
203
|
-
// NOTE - This assumes that analyzePlan() has been run!
|
|
204
|
-
getPlanAnalytics(bLog: boolean = false): PlanAnalytics
|
|
205
|
-
{
|
|
206
|
-
return preparePlanAnalytics(this, bLog);
|
|
207
|
-
}
|
|
208
|
-
*/
|
|
209
206
|
// TODO - SCORE
|
|
210
207
|
// NOTE - This assumes that analyzePlan() has been run!
|
|
211
208
|
getRequirementsChecklist(bLog = false) {
|
|
@@ -214,19 +211,82 @@ class AnalyticsSession {
|
|
|
214
211
|
// NOTE - This assumes that analyzePlan() has been run!
|
|
215
212
|
getDiscontiguousDistrictFeatures(bLog = false) {
|
|
216
213
|
// Get the (possibly empty) list of discontiguous district IDs
|
|
217
|
-
|
|
218
|
-
|
|
214
|
+
const contiguousTest = this.getTest(1 /* Contiguous */);
|
|
215
|
+
const discontiguousDistrictIDs = contiguousTest['details']['discontiguousDistricts'] || [];
|
|
219
216
|
// Convert them into a (possibly empty) list of features
|
|
220
217
|
let discontiguousDistrictFeatures = { type: 'FeatureCollection', features: [] };
|
|
221
218
|
if (!(U.isArrayEmpty(discontiguousDistrictIDs))) {
|
|
222
219
|
for (let id of discontiguousDistrictIDs) {
|
|
223
220
|
let poly = this.districts.getDistrictShapeByID(id);
|
|
224
|
-
if (poly)
|
|
225
|
-
|
|
221
|
+
if (poly) {
|
|
222
|
+
// If a district has a shape & it is not contiguous, by definition,
|
|
223
|
+
// it will be a Multipolygon, i.e., it will have multiple pieces, some
|
|
224
|
+
// possibly embedded w/in other districts. Get & add all the pieces.
|
|
225
|
+
const districtParts = geofeature_1.polyParts(poly);
|
|
226
|
+
discontiguousDistrictFeatures.features.push(...districtParts.features);
|
|
227
|
+
// discontiguousDistrictFeatures.features.push(poly);
|
|
228
|
+
}
|
|
226
229
|
}
|
|
227
230
|
}
|
|
228
231
|
return discontiguousDistrictFeatures;
|
|
229
232
|
}
|
|
233
|
+
// Comments clipped from dra-client geodistrict.ts.
|
|
234
|
+
// Discontiguous polygons are:
|
|
235
|
+
// 1. All polygons in a multi-polygon; and
|
|
236
|
+
// 2. All holes in a otherwise cohesive polygon.
|
|
237
|
+
// Note that all non-cohesive features are always simple polygons.
|
|
238
|
+
/*
|
|
239
|
+
let i: number, j: number;
|
|
240
|
+
let nPoly: number = 0;
|
|
241
|
+
for (i = 0;nPoly == 0 && i < this.cacheDistricts.features.length;i++)
|
|
242
|
+
{
|
|
243
|
+
let f = this.cacheDistricts.features[i];
|
|
244
|
+
|
|
245
|
+
if (f.geometry.type === 'MultiPolygon')
|
|
246
|
+
nPoly += f.geometry.coordinates.length;
|
|
247
|
+
else if (f.geometry.type === 'Polygon' && f.geometry.coordinates.length)
|
|
248
|
+
nPoly += (f.geometry.coordinates.length - 1);
|
|
249
|
+
}
|
|
250
|
+
if (nPoly)
|
|
251
|
+
{
|
|
252
|
+
this.cacheNoncohesive = {type: 'FeatureCollection', features: []};
|
|
253
|
+
let af: any = this.cacheNoncohesive.features;
|
|
254
|
+
let oUnique: any = {};
|
|
255
|
+
|
|
256
|
+
// First add discontiguous polygons
|
|
257
|
+
for (i = 0;i < this.cacheDistricts.features.length;i++)
|
|
258
|
+
{
|
|
259
|
+
let f = this.cacheDistricts.features[i];
|
|
260
|
+
|
|
261
|
+
if (f.geometry.type === 'MultiPolygon')
|
|
262
|
+
{
|
|
263
|
+
// Push all non-contiguous polygons
|
|
264
|
+
for (j = 0;j < f.geometry.coordinates.length;j++)
|
|
265
|
+
{
|
|
266
|
+
let p: any = f.geometry.coordinates[j];
|
|
267
|
+
oUnique[Hash.qhash(p[0])] = true;
|
|
268
|
+
af.push({type: 'Feature', properties: {id: `${af.length + 1}`}, geometry: {type: 'Polygon', coordinates: p}});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Now add unique holes
|
|
274
|
+
for (i = 0;i < this.cacheDistricts.features.length;i++)
|
|
275
|
+
{
|
|
276
|
+
let f = this.cacheDistricts.features[i];
|
|
277
|
+
|
|
278
|
+
if (f.geometry.type === 'Polygon')
|
|
279
|
+
{
|
|
280
|
+
// Push all holes from this polygon
|
|
281
|
+
for (j = 1;j < f.geometry.coordinates.length;j++)
|
|
282
|
+
{
|
|
283
|
+
let p: any = f.geometry.coordinates[j];
|
|
284
|
+
if (oUnique[Hash.qhash(p)] === undefined)
|
|
285
|
+
af.push({type: 'Feature', properties: {id: `${af.length + 1}`}, geometry: {type: 'Polygon', coordinates: [p]}});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
} */
|
|
230
290
|
// HELPERS USED INTERNALLY
|
|
231
291
|
// Get an individual test, so you can drive UI with the results.
|
|
232
292
|
getTest(testID) {
|
|
@@ -242,9 +302,17 @@ class AnalyticsSession {
|
|
|
242
302
|
// Return a pointer to the the test entry for this test
|
|
243
303
|
return this.tests[testID];
|
|
244
304
|
}
|
|
245
|
-
// NOTE - Not sure why this has to be up here
|
|
305
|
+
// NOTE - Not sure why this has to be up here ...
|
|
246
306
|
populationDeviationThreshold() {
|
|
247
|
-
|
|
307
|
+
// TODO - SCORE: Toggle
|
|
308
|
+
if (this.useLegacy()) {
|
|
309
|
+
return 1 - this.testScales[4 /* PopulationDeviation */]['scale'][0];
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
// TODO - SCORE: Temporary HACK. Query dra-score for threshold.
|
|
313
|
+
// NOTE - The plan may not have been scored yet, i.e., no scorecard yet.
|
|
314
|
+
return 1 - this.testScales[4 /* PopulationDeviation */]['scale'][0];
|
|
315
|
+
}
|
|
248
316
|
}
|
|
249
317
|
}
|
|
250
318
|
exports.AnalyticsSession = AnalyticsSession;
|
|
@@ -420,7 +488,6 @@ class Districts {
|
|
|
420
488
|
let outerThis = this;
|
|
421
489
|
// Default the pop dev % for the dummy Unassigned district to 0%.
|
|
422
490
|
// Default the pop dev % for real (1–N) but empty districts to 100%.
|
|
423
|
-
// TODO - SCORE
|
|
424
491
|
let popDevPct = (i > 0) ? (targetSize / targetSize) : 0 / targetSize;
|
|
425
492
|
// Get the geoIDs assigned to the district
|
|
426
493
|
// Guard against empty districts
|
|
@@ -447,7 +514,6 @@ class Districts {
|
|
|
447
514
|
// NOTE - SPLITTING
|
|
448
515
|
// Total population by counties w/in a district,
|
|
449
516
|
// except the dummy unassigned district 0
|
|
450
|
-
// TODO - VFEATURE
|
|
451
517
|
if (i > 0)
|
|
452
518
|
countySplits[outerThis.getCountyIndex(geoID)] += featurePop;
|
|
453
519
|
// Democratic and Republican vote totals
|
|
@@ -621,10 +687,7 @@ class Districts {
|
|
|
621
687
|
compact_1.extractDistrictProperties(this._session, bLog);
|
|
622
688
|
}
|
|
623
689
|
getCountyIndex(geoID) {
|
|
624
|
-
// TODO - VFEATURE
|
|
625
690
|
let countyFIPS = U.parseGeoID(geoID)['county'];
|
|
626
|
-
// let countyGeoID = U.parseGeoID(geoID)['county'] as string;
|
|
627
|
-
// let countyFIPS = U.getFIPSFromCountyGeoID(countyGeoID);
|
|
628
691
|
let countyIndex = this._session.counties.indexFromFIPS(countyFIPS);
|
|
629
692
|
return countyIndex;
|
|
630
693
|
}
|
|
@@ -873,11 +936,45 @@ exports.doAnalyzeDistricts = doAnalyzeDistricts;
|
|
|
873
936
|
// NOTE - I could make this table-driven, but I'm thinking that the explicit
|
|
874
937
|
// calls might make chunking for aync easier.
|
|
875
938
|
function doAnalyzePlan(s, bLog = false) {
|
|
876
|
-
// TODO -
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
939
|
+
// TODO - SCORE: Toggle
|
|
940
|
+
if (s.useLegacy()) {
|
|
941
|
+
// Disable most legacy analytics
|
|
942
|
+
// Get the requested suites, and only execute those tests
|
|
943
|
+
let requestedSuites = s.config['suites'];
|
|
944
|
+
// Tests in the "Legal" suite, i.e., pass/ fail constraints
|
|
945
|
+
if (requestedSuites.includes(0 /* Legal */)) {
|
|
946
|
+
s.tests[0 /* Complete */] = valid_1.doIsComplete(s, bLog);
|
|
947
|
+
s.tests[1 /* Contiguous */] = valid_1.doIsContiguous(s, bLog);
|
|
948
|
+
s.tests[2 /* FreeOfHoles */] = valid_1.doIsFreeOfHoles(s, bLog);
|
|
949
|
+
s.tests[4 /* PopulationDeviation */] = equal_1.doPopulationDeviation(s, bLog);
|
|
950
|
+
// NOTE - I can't check whether a population deviation is legal or not, until
|
|
951
|
+
// the raw % is normalized. A zero (0) would mean "too much" / "not legal," for
|
|
952
|
+
// the given type of district (CD vs. LD). The EqualPopulation test is derived
|
|
953
|
+
// from PopulationDeviation, as part of scorecard or test log preparation.
|
|
954
|
+
// Create an empty test entry here though ...
|
|
955
|
+
s.tests[3 /* EqualPopulation */] = s.getTest(3 /* EqualPopulation */);
|
|
956
|
+
}
|
|
957
|
+
// Tests in the "Fair" suite
|
|
958
|
+
if (requestedSuites.includes(1 /* Fair */)) {
|
|
959
|
+
s.tests[11 /* SeatsBias */] = political_1.doSeatsBias(s, bLog);
|
|
960
|
+
s.tests[12 /* VotesBias */] = political_1.doVotesBias(s, bLog);
|
|
961
|
+
s.tests[13 /* Responsiveness */] = political_1.doResponsiveness(s, bLog);
|
|
962
|
+
s.tests[14 /* ResponsiveDistricts */] = political_1.doResponsiveDistricts(s, bLog);
|
|
963
|
+
s.tests[15 /* EfficiencyGap */] = political_1.doEfficiencyGap(s, bLog);
|
|
964
|
+
s.tests[16 /* MajorityMinorityDistricts */] = minority_1.doMajorityMinorityDistricts(s, bLog);
|
|
965
|
+
}
|
|
966
|
+
// Tests in the "Best" suite, i.e., criteria for better/worse
|
|
967
|
+
if (requestedSuites.includes(2 /* Best */)) {
|
|
968
|
+
s.tests[5 /* Reock */] = compact_1.doReock(s, bLog);
|
|
969
|
+
s.tests[6 /* PolsbyPopper */] = compact_1.doPolsbyPopper(s, bLog);
|
|
970
|
+
s.tests[7 /* UnexpectedCountySplits */] = cohesive_1.doFindCountiesSplitUnexpectedly(s, bLog);
|
|
971
|
+
s.tests[10 /* VTDSplits */] = cohesive_1.doFindSplitVTDs(s, bLog);
|
|
972
|
+
s.tests[8 /* CountySplitting */] = cohesive_1.doCountySplitting(s, bLog);
|
|
973
|
+
s.tests[9 /* DistrictSplitting */] = cohesive_1.doDistrictSplitting(s, bLog);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
else {
|
|
977
|
+
// TODO - SCORE: Except these. Continue to do these here vs. dra-score.
|
|
881
978
|
s.tests[0 /* Complete */] = valid_1.doIsComplete(s, bLog);
|
|
882
979
|
s.tests[1 /* Contiguous */] = valid_1.doIsContiguous(s, bLog);
|
|
883
980
|
s.tests[2 /* FreeOfHoles */] = valid_1.doIsFreeOfHoles(s, bLog);
|
|
@@ -888,24 +985,8 @@ function doAnalyzePlan(s, bLog = false) {
|
|
|
888
985
|
// from PopulationDeviation, as part of scorecard or test log preparation.
|
|
889
986
|
// Create an empty test entry here though ...
|
|
890
987
|
s.tests[3 /* EqualPopulation */] = s.getTest(3 /* EqualPopulation */);
|
|
891
|
-
}
|
|
892
|
-
// Tests in the "Fair" suite
|
|
893
|
-
if (requestedSuites.includes(1 /* Fair */)) {
|
|
894
|
-
s.tests[11 /* SeatsBias */] = political_1.doSeatsBias(s, bLog);
|
|
895
|
-
s.tests[12 /* VotesBias */] = political_1.doVotesBias(s, bLog);
|
|
896
|
-
s.tests[13 /* Responsiveness */] = political_1.doResponsiveness(s, bLog);
|
|
897
|
-
s.tests[14 /* ResponsiveDistricts */] = political_1.doResponsiveDistricts(s, bLog);
|
|
898
|
-
s.tests[15 /* EfficiencyGap */] = political_1.doEfficiencyGap(s, bLog);
|
|
899
|
-
s.tests[16 /* MajorityMinorityDistricts */] = minority_1.doMajorityMinorityDistricts(s, bLog);
|
|
900
|
-
}
|
|
901
|
-
// Tests in the "Best" suite, i.e., criteria for better/worse
|
|
902
|
-
if (requestedSuites.includes(2 /* Best */)) {
|
|
903
|
-
s.tests[5 /* Reock */] = compact_1.doReock(s, bLog);
|
|
904
|
-
s.tests[6 /* PolsbyPopper */] = compact_1.doPolsbyPopper(s, bLog);
|
|
905
988
|
s.tests[7 /* UnexpectedCountySplits */] = cohesive_1.doFindCountiesSplitUnexpectedly(s, bLog);
|
|
906
989
|
s.tests[10 /* VTDSplits */] = cohesive_1.doFindSplitVTDs(s, bLog);
|
|
907
|
-
s.tests[8 /* CountySplitting */] = cohesive_1.doCountySplitting(s, bLog);
|
|
908
|
-
s.tests[9 /* DistrictSplitting */] = cohesive_1.doDistrictSplitting(s, bLog);
|
|
909
990
|
}
|
|
910
991
|
// Enable a Test Log and Scorecard to be generated
|
|
911
992
|
s.bPlanAnalyzed = true;
|
|
@@ -1502,6 +1583,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
1502
1583
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1503
1584
|
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
1504
1585
|
const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
1586
|
+
// TODO - SCORE: Delete
|
|
1505
1587
|
function doPopulationDeviation(s, bLog = false) {
|
|
1506
1588
|
let test = s.getTest(4 /* PopulationDeviation */);
|
|
1507
1589
|
let targetSize = s.state.totalPop / s.state.nDistricts;
|
|
@@ -1540,8 +1622,8 @@ function doHasEqualPopulations(s, bLog = false) {
|
|
|
1540
1622
|
let test = s.getTest(3 /* EqualPopulation */);
|
|
1541
1623
|
// Get the normalized population deviation %
|
|
1542
1624
|
let popDevTest = s.getTest(4 /* PopulationDeviation */);
|
|
1543
|
-
|
|
1544
|
-
|
|
1625
|
+
const popDevPct = popDevTest['score'];
|
|
1626
|
+
const popDevNormalized = popDevTest['normalizedScore'];
|
|
1545
1627
|
// Populate the test entry
|
|
1546
1628
|
if (popDevNormalized > 0) {
|
|
1547
1629
|
test['score'] = true;
|
|
@@ -1583,6 +1665,23 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
1583
1665
|
};
|
|
1584
1666
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1585
1667
|
const Poly = __importStar(__webpack_require__(/*! @dra2020/poly */ "@dra2020/poly"));
|
|
1668
|
+
// HELPER
|
|
1669
|
+
function polyParts(poly) {
|
|
1670
|
+
let parts = { type: 'FeatureCollection', features: [] };
|
|
1671
|
+
let af = parts.features;
|
|
1672
|
+
if (poly.geometry.type === 'MultiPolygon') {
|
|
1673
|
+
// Push all non-contiguous polygons
|
|
1674
|
+
for (let j = 0; j < poly.geometry.coordinates.length; j++) {
|
|
1675
|
+
let onePoly = poly.geometry.coordinates[j];
|
|
1676
|
+
af.push({ type: 'Feature', properties: { id: `${af.length + 1}` }, geometry: { type: 'Polygon', coordinates: onePoly } });
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
else {
|
|
1680
|
+
parts.features.push(poly);
|
|
1681
|
+
}
|
|
1682
|
+
return parts;
|
|
1683
|
+
}
|
|
1684
|
+
exports.polyParts = polyParts;
|
|
1586
1685
|
// CARTESIAN SHIMS OVER 'POLY' FUNCTIONS
|
|
1587
1686
|
// TODO - POLY: Confirm Cartesian calculations
|
|
1588
1687
|
function gfArea(poly) {
|
|
@@ -1908,10 +2007,7 @@ function doPreprocessCensus(s, bLog = false) {
|
|
|
1908
2007
|
// Sum total population across the state
|
|
1909
2008
|
s.state.totalPop += value;
|
|
1910
2009
|
// Get the county FIPS code for the feature
|
|
1911
|
-
// TODO - VFEATURE
|
|
1912
2010
|
let countyFIPS = U.parseGeoID(geoID)['county'];
|
|
1913
|
-
// let county = U.parseGeoID(geoID)['county'] as string;
|
|
1914
|
-
// let countyFIPS = U.getFIPSFromCountyGeoID(county);
|
|
1915
2011
|
// If a subtotal for the county doesn't exist, initialize one
|
|
1916
2012
|
if (!(U.keyExists(countyFIPS, totalByCounty))) {
|
|
1917
2013
|
totalByCounty[countyFIPS] = 0;
|
|
@@ -1929,8 +2025,8 @@ function doPreprocessCensus(s, bLog = false) {
|
|
|
1929
2025
|
let fipsCodes = U.getObjectKeys(totalByCounty);
|
|
1930
2026
|
// Sort the results
|
|
1931
2027
|
fipsCodes = fipsCodes.sort();
|
|
1932
|
-
//
|
|
1933
|
-
// Add a dummy county, for county-district splitting analysis
|
|
2028
|
+
// NOTE - This was added for the legacy SPLITTING implementation.
|
|
2029
|
+
// Add a dummy county, for county-district splitting analysis.
|
|
1934
2030
|
fipsCodes.unshift('000');
|
|
1935
2031
|
// Create the ID-ordinal map
|
|
1936
2032
|
for (let i in fipsCodes) {
|
|
@@ -2020,6 +2116,7 @@ const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
|
2020
2116
|
const analyze_1 = __webpack_require__(/*! ./analyze */ "./src/analyze.ts");
|
|
2021
2117
|
const state_reqs_json_1 = __importDefault(__webpack_require__(/*! ../static/state-reqs.json */ "./static/state-reqs.json"));
|
|
2022
2118
|
// Example
|
|
2119
|
+
// TODO - DELETE?
|
|
2023
2120
|
let sampleRequirements = {
|
|
2024
2121
|
score: 2 /* Red */,
|
|
2025
2122
|
metrics: {
|
|
@@ -2043,6 +2140,7 @@ let sampleRequirements = {
|
|
|
2043
2140
|
stateReqs: "https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_20.pdf"
|
|
2044
2141
|
}
|
|
2045
2142
|
};
|
|
2143
|
+
// TODO - DELETE
|
|
2046
2144
|
let sampleCompactness = {
|
|
2047
2145
|
score: 60,
|
|
2048
2146
|
metrics: {
|
|
@@ -2055,6 +2153,7 @@ let sampleCompactness = {
|
|
|
2055
2153
|
},
|
|
2056
2154
|
resources: {}
|
|
2057
2155
|
};
|
|
2156
|
+
// TODO - DELETE
|
|
2058
2157
|
let sampleSplitting = {
|
|
2059
2158
|
score: 73,
|
|
2060
2159
|
metrics: {
|
|
@@ -2072,7 +2171,7 @@ let sampleSplitting = {
|
|
|
2072
2171
|
datasets: {},
|
|
2073
2172
|
resources: {}
|
|
2074
2173
|
};
|
|
2075
|
-
// TODO -
|
|
2174
|
+
// TODO - DELETE
|
|
2076
2175
|
let samplePartisan = {
|
|
2077
2176
|
score: 100,
|
|
2078
2177
|
metrics: {
|
|
@@ -2087,7 +2186,7 @@ let samplePartisan = {
|
|
|
2087
2186
|
planScore: "https://planscore.org/plan.html?20180219T202039.596761160Z"
|
|
2088
2187
|
}
|
|
2089
2188
|
};
|
|
2090
|
-
// TODO -
|
|
2189
|
+
// TODO - DELETE
|
|
2091
2190
|
let sampleMinority = {
|
|
2092
2191
|
score: null,
|
|
2093
2192
|
metrics: {
|
|
@@ -2114,6 +2213,7 @@ let sampleMinority = {
|
|
|
2114
2213
|
},
|
|
2115
2214
|
resources: {}
|
|
2116
2215
|
};
|
|
2216
|
+
// TODO - DELETE
|
|
2117
2217
|
exports.samplePlanAnalytics = {
|
|
2118
2218
|
requirements: sampleRequirements,
|
|
2119
2219
|
compactness: sampleCompactness,
|
|
@@ -2122,232 +2222,6 @@ exports.samplePlanAnalytics = {
|
|
|
2122
2222
|
partisan: samplePartisan,
|
|
2123
2223
|
minority: sampleMinority
|
|
2124
2224
|
};
|
|
2125
|
-
/* TODO - SCORE
|
|
2126
|
-
export function preparePlanAnalytics(s: AnalyticsSession, bLog: boolean = false): PlanAnalytics
|
|
2127
|
-
{
|
|
2128
|
-
if (!(s.bPostProcessingDone))
|
|
2129
|
-
{
|
|
2130
|
-
doAnalyzePostProcessing(s);
|
|
2131
|
-
}
|
|
2132
|
-
|
|
2133
|
-
// REQUIREMENTS CATEGORY
|
|
2134
|
-
let paRequirements: RequirementsCategory;
|
|
2135
|
-
|
|
2136
|
-
{
|
|
2137
|
-
let completeTest = s.getTest(T.Test.Complete) as T.TestEntry;
|
|
2138
|
-
let contiguousTest = s.getTest(T.Test.Contiguous) as T.TestEntry;
|
|
2139
|
-
let freeOfHolesTest = s.getTest(T.Test.FreeOfHoles) as T.TestEntry;
|
|
2140
|
-
let equalPopulationTest = s.getTest(T.Test.EqualPopulation) as T.TestEntry;
|
|
2141
|
-
|
|
2142
|
-
// Combine individual checks into an overall score
|
|
2143
|
-
|
|
2144
|
-
// TODO - DASHBOARD: Until we add three-state support top to bottom in
|
|
2145
|
-
// requirements/validations, map booleans to tri-states here.
|
|
2146
|
-
let completeMetric = U.mapBooleanToTriState(completeTest['score'] as boolean);
|
|
2147
|
-
let contiguousMetric = U.mapBooleanToTriState(contiguousTest['score'] as boolean);
|
|
2148
|
-
let freeOfHolesMetric = U.mapBooleanToTriState(freeOfHolesTest['score'] as boolean);
|
|
2149
|
-
let equalPopulationMetric = U.mapBooleanToTriState(equalPopulationTest['score'] as boolean);
|
|
2150
|
-
|
|
2151
|
-
let reqScore: T.TriState = T.TriState.Green;
|
|
2152
|
-
let checks = [completeMetric, contiguousMetric, freeOfHolesMetric, equalPopulationMetric];
|
|
2153
|
-
if (checks.includes(T.TriState.Yellow)) reqScore = T.TriState.Yellow;
|
|
2154
|
-
if (checks.includes(T.TriState.Red)) reqScore = T.TriState.Red;
|
|
2155
|
-
|
|
2156
|
-
// Get values to support details entries
|
|
2157
|
-
let unassignedFeaturesDetail = U.deepCopy(completeTest['details']['unassignedFeatures']) || [];
|
|
2158
|
-
let emptyDistrictsDetail = U.deepCopy(completeTest['details']['emptyDistricts']) || [];
|
|
2159
|
-
let discontiguousDistrictsDetail = U.deepCopy(contiguousTest['details']['discontiguousDistricts']) || [];
|
|
2160
|
-
let embeddedDistrictsDetail = U.deepCopy(freeOfHolesTest['details']['embeddedDistricts']) || [];
|
|
2161
|
-
|
|
2162
|
-
let populationDeviationDetail = U.deepCopy(equalPopulationTest['details']['deviation']);
|
|
2163
|
-
let deviationThresholdDetail = U.trim(s.populationDeviationThreshold());
|
|
2164
|
-
|
|
2165
|
-
let xx: string = s.state.xx;
|
|
2166
|
-
// TODO - JSON: Is there a better / easier way to work with the variable?
|
|
2167
|
-
let stateReqsDict: T.Dict = allStateReqs;
|
|
2168
|
-
let reqLinkToStateReqs: string = stateReqsDict[xx];
|
|
2169
|
-
|
|
2170
|
-
// Populate the category
|
|
2171
|
-
paRequirements = {
|
|
2172
|
-
score: reqScore,
|
|
2173
|
-
metrics: {
|
|
2174
|
-
complete: completeMetric,
|
|
2175
|
-
contiguous: contiguousMetric,
|
|
2176
|
-
freeOfHoles: freeOfHolesMetric,
|
|
2177
|
-
equalPopulation: equalPopulationMetric
|
|
2178
|
-
},
|
|
2179
|
-
details: {
|
|
2180
|
-
unassignedFeatures: unassignedFeaturesDetail,
|
|
2181
|
-
emptyDistricts: emptyDistrictsDetail,
|
|
2182
|
-
discontiguousDistricts: discontiguousDistrictsDetail,
|
|
2183
|
-
embeddedDistricts: embeddedDistrictsDetail,
|
|
2184
|
-
populationDeviation: populationDeviationDetail,
|
|
2185
|
-
deviationThreshold: deviationThresholdDetail
|
|
2186
|
-
},
|
|
2187
|
-
datasets: {
|
|
2188
|
-
census: U.deepCopy(s.config['descriptions']['CENSUS']),
|
|
2189
|
-
},
|
|
2190
|
-
resources: {
|
|
2191
|
-
stateReqs: reqLinkToStateReqs
|
|
2192
|
-
}
|
|
2193
|
-
}
|
|
2194
|
-
}
|
|
2195
|
-
|
|
2196
|
-
// COMPACTNESS CATEGORY
|
|
2197
|
-
let paCompactness: CompactnessCategory;
|
|
2198
|
-
{
|
|
2199
|
-
let reockWeight = 0.5;
|
|
2200
|
-
let polsbyWeight = 1.0 - reockWeight;
|
|
2201
|
-
|
|
2202
|
-
let reockTest = s.getTest(T.Test.Reock) as T.TestEntry;
|
|
2203
|
-
let polsbyTest = s.getTest(T.Test.PolsbyPopper) as T.TestEntry;
|
|
2204
|
-
|
|
2205
|
-
let normalizedReock = reockTest['normalizedScore'] as number;
|
|
2206
|
-
let normalizedPolsby = reockTest['normalizedScore'] as number;
|
|
2207
|
-
let compactnessScore = U.trim((reockWeight * normalizedReock) + (polsbyWeight * normalizedPolsby), 0);
|
|
2208
|
-
|
|
2209
|
-
let reockMetric = U.deepCopy(reockTest['score'] as number);
|
|
2210
|
-
let polsbyMetric = U.deepCopy(polsbyTest['score'] as number);
|
|
2211
|
-
|
|
2212
|
-
// Populate the category
|
|
2213
|
-
paCompactness = {
|
|
2214
|
-
score: compactnessScore,
|
|
2215
|
-
metrics: {
|
|
2216
|
-
reock: reockMetric,
|
|
2217
|
-
polsby: polsbyMetric
|
|
2218
|
-
},
|
|
2219
|
-
details: {
|
|
2220
|
-
// None at this time
|
|
2221
|
-
},
|
|
2222
|
-
datasets: {
|
|
2223
|
-
// NOTE - DATASETS
|
|
2224
|
-
shapes: U.deepCopy(s.config['descriptions']['SHAPES'])
|
|
2225
|
-
// shapes: "2010 VTD shapes"
|
|
2226
|
-
},
|
|
2227
|
-
resources: {
|
|
2228
|
-
// None at this time
|
|
2229
|
-
}
|
|
2230
|
-
}
|
|
2231
|
-
}
|
|
2232
|
-
|
|
2233
|
-
// SPLITTING CATEGORY
|
|
2234
|
-
|
|
2235
|
-
let paSplitting: SplittingCategory
|
|
2236
|
-
|
|
2237
|
-
{
|
|
2238
|
-
let unexpectedCountySplittingTest = s.getTest(T.Test.UnexpectedCountySplits) as T.TestEntry;
|
|
2239
|
-
let VTDSplitsTest = s.getTest(T.Test.VTDSplits) as T.TestEntry;
|
|
2240
|
-
let countySplittingTest = s.getTest(T.Test.CountySplitting) as T.TestEntry;
|
|
2241
|
-
let districtSplittingTest = s.getTest(T.Test.DistrictSplitting) as T.TestEntry;
|
|
2242
|
-
|
|
2243
|
-
let unexpectedAffectedMetric = U.deepCopy(unexpectedCountySplittingTest['score']);
|
|
2244
|
-
let countiesSplitUnexpectedlyDetail = U.deepCopy(unexpectedCountySplittingTest['details']['countiesSplitUnexpectedly']);
|
|
2245
|
-
|
|
2246
|
-
let nVTDSplitsMetric = U.deepCopy(VTDSplitsTest['score']);
|
|
2247
|
-
let splitVTDsDetail = U.deepCopy(VTDSplitsTest['details']['splitVTDs']);
|
|
2248
|
-
|
|
2249
|
-
let SqEnt_DCreducedMetric = U.deepCopy(countySplittingTest['score']);
|
|
2250
|
-
let SqEnt_CDreducedMetric = U.deepCopy(districtSplittingTest['score']);
|
|
2251
|
-
|
|
2252
|
-
let countySplittingNormalized = countySplittingTest['normalizedScore'] as number;
|
|
2253
|
-
let districtSplittingNormalized = districtSplittingTest['normalizedScore'] as number;
|
|
2254
|
-
let splittingScore = U.trim((S.COUNTY_SPLITTING_WEIGHT * countySplittingNormalized) +
|
|
2255
|
-
+ (S.DISTRICT_SPLITTING_WEIGHT * districtSplittingNormalized), 0);
|
|
2256
|
-
|
|
2257
|
-
paSplitting = {
|
|
2258
|
-
score: splittingScore,
|
|
2259
|
-
metrics: {
|
|
2260
|
-
sqEnt_DCreduced: SqEnt_DCreducedMetric,
|
|
2261
|
-
sqEnt_CDreduced: SqEnt_CDreducedMetric
|
|
2262
|
-
// NOTE - The un-reduced raw values
|
|
2263
|
-
// sqEnt_DC : SqEnt_DCMetric,
|
|
2264
|
-
// sqEnt_CD : SqEnt_CDMetric
|
|
2265
|
-
},
|
|
2266
|
-
details: {
|
|
2267
|
-
countiesSplitUnexpectedly: countiesSplitUnexpectedlyDetail,
|
|
2268
|
-
unexpectedAffected: unexpectedAffectedMetric,
|
|
2269
|
-
nSplitVTDs: nVTDSplitsMetric,
|
|
2270
|
-
splitVTDs: splitVTDsDetail
|
|
2271
|
-
},
|
|
2272
|
-
datasets: {
|
|
2273
|
-
// None at this time
|
|
2274
|
-
},
|
|
2275
|
-
resources: {
|
|
2276
|
-
// None at this time
|
|
2277
|
-
}
|
|
2278
|
-
}
|
|
2279
|
-
}
|
|
2280
|
-
|
|
2281
|
-
// PARTISAN CATEGORY
|
|
2282
|
-
//
|
|
2283
|
-
// TODO - PARTISAN: This category is still being fleshed out. Just an example below.
|
|
2284
|
-
let paPartisan: PartisanCategory;
|
|
2285
|
-
|
|
2286
|
-
{
|
|
2287
|
-
paPartisan = {
|
|
2288
|
-
score: 100,
|
|
2289
|
-
metrics: {
|
|
2290
|
-
partisanBias: 0.15,
|
|
2291
|
-
responsiveness: 2.0
|
|
2292
|
-
},
|
|
2293
|
-
details: {},
|
|
2294
|
-
datasets: {
|
|
2295
|
-
election: "2016 Presidential, US Senate, Governor, and AG election results"
|
|
2296
|
-
},
|
|
2297
|
-
resources: {
|
|
2298
|
-
planScore: "https://planscore.org/plan.html?20180219T202039.596761160Z"
|
|
2299
|
-
}
|
|
2300
|
-
}
|
|
2301
|
-
}
|
|
2302
|
-
|
|
2303
|
-
// MINORITY CATEGORY
|
|
2304
|
-
//
|
|
2305
|
-
// TODO - MINORITY: This category is still being fleshed out. Just an example below.
|
|
2306
|
-
let paMinority: MinorityCategory;
|
|
2307
|
-
|
|
2308
|
-
{
|
|
2309
|
-
paMinority = {
|
|
2310
|
-
score: null,
|
|
2311
|
-
metrics: {
|
|
2312
|
-
nBlack37to50: 1,
|
|
2313
|
-
nBlackMajority: 12,
|
|
2314
|
-
nHispanic37to50: 0,
|
|
2315
|
-
nHispanicMajority: 0,
|
|
2316
|
-
nPacific37to50: 0,
|
|
2317
|
-
nPacificMajority: 0,
|
|
2318
|
-
nAsian37to50: 0,
|
|
2319
|
-
nAsianMajority: 0,
|
|
2320
|
-
nNative37to50: 0,
|
|
2321
|
-
nNativeMajority: 0,
|
|
2322
|
-
nMinority37to50: 0,
|
|
2323
|
-
nMinorityMajority: 0,
|
|
2324
|
-
|
|
2325
|
-
averageDVoteShare: 0.90
|
|
2326
|
-
},
|
|
2327
|
-
details: {
|
|
2328
|
-
vap: true,
|
|
2329
|
-
comboCategories: true
|
|
2330
|
-
},
|
|
2331
|
-
datasets: {
|
|
2332
|
-
vap: "2010 Voting Age Population"
|
|
2333
|
-
},
|
|
2334
|
-
resources: {}
|
|
2335
|
-
}
|
|
2336
|
-
}
|
|
2337
|
-
|
|
2338
|
-
// PLAN ANALYTICS
|
|
2339
|
-
let pa: PlanAnalytics = {
|
|
2340
|
-
requirements: paRequirements,
|
|
2341
|
-
compactness: paCompactness,
|
|
2342
|
-
// TODO - Not implemented yet
|
|
2343
|
-
splitting: paSplitting,
|
|
2344
|
-
partisan: paPartisan,
|
|
2345
|
-
minority: paMinority
|
|
2346
|
-
}
|
|
2347
|
-
|
|
2348
|
-
return pa;
|
|
2349
|
-
}
|
|
2350
|
-
*/
|
|
2351
2225
|
function prepareRequirementsChecklist(s, bLog = false) {
|
|
2352
2226
|
if (!(s.bPostProcessingDone)) {
|
|
2353
2227
|
doAnalyzePostProcessing(s);
|
|
@@ -2355,34 +2229,40 @@ function prepareRequirementsChecklist(s, bLog = false) {
|
|
|
2355
2229
|
// REQUIREMENTS CATEGORY
|
|
2356
2230
|
let paRequirements;
|
|
2357
2231
|
{
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2232
|
+
const completeTest = s.getTest(0 /* Complete */);
|
|
2233
|
+
const contiguousTest = s.getTest(1 /* Contiguous */);
|
|
2234
|
+
const freeOfHolesTest = s.getTest(2 /* FreeOfHoles */);
|
|
2235
|
+
const equalPopulationTest = s.getTest(3 /* EqualPopulation */);
|
|
2362
2236
|
// Combine individual checks into an overall score
|
|
2363
|
-
//
|
|
2237
|
+
// NOTE - Until we add three-state support top to bottom in
|
|
2364
2238
|
// requirements/validations, map booleans to tri-states here.
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2239
|
+
const completeMetric = U.mapBooleanToTriState(completeTest['score']);
|
|
2240
|
+
const contiguousMetric = U.mapBooleanToTriState(contiguousTest['score']);
|
|
2241
|
+
const freeOfHolesMetric = U.mapBooleanToTriState(freeOfHolesTest['score']);
|
|
2242
|
+
const equalPopulationMetric = U.mapBooleanToTriState(equalPopulationTest['score']);
|
|
2369
2243
|
let reqScore = 0 /* Green */;
|
|
2370
|
-
|
|
2244
|
+
const checks = [completeMetric, contiguousMetric, freeOfHolesMetric, equalPopulationMetric];
|
|
2371
2245
|
if (checks.includes(1 /* Yellow */))
|
|
2372
2246
|
reqScore = 1 /* Yellow */;
|
|
2373
2247
|
if (checks.includes(2 /* Red */))
|
|
2374
2248
|
reqScore = 2 /* Red */;
|
|
2375
2249
|
// Get values to support details entries
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2250
|
+
const unassignedFeaturesDetail = U.deepCopy(completeTest['details']['unassignedFeatures']) || [];
|
|
2251
|
+
const emptyDistrictsDetail = U.deepCopy(completeTest['details']['emptyDistricts']) || [];
|
|
2252
|
+
const discontiguousDistrictsDetail = U.deepCopy(contiguousTest['details']['discontiguousDistricts']) || [];
|
|
2253
|
+
const embeddedDistrictsDetail = U.deepCopy(freeOfHolesTest['details']['embeddedDistricts']) || [];
|
|
2254
|
+
// TODO - SCORE: DELETE - This code is hooked correctly to use dra-score
|
|
2255
|
+
// const scorecard = s._scorecard as Score.Scorecard;
|
|
2256
|
+
// const populationDeviation = scorecard.best.populationDeviation.raw;
|
|
2257
|
+
const populationDeviationDetail = U.deepCopy(equalPopulationTest['details']['deviation']);
|
|
2258
|
+
// console.log("Population deviations =", populationDeviationDetail, populationDeviation);
|
|
2259
|
+
// const deviationThreshold = scorecard.best.populationDeviation.notes['threshold'];
|
|
2260
|
+
const deviationThresholdDetail = U.trim(s.populationDeviationThreshold());
|
|
2261
|
+
// console.log("Population deviation thresholds =", deviationThresholdDetail, deviationThreshold);
|
|
2262
|
+
const xx = s.state.xx;
|
|
2383
2263
|
// TODO - JSON: Is there a better / easier way to work with the variable?
|
|
2384
|
-
|
|
2385
|
-
|
|
2264
|
+
const stateReqsDict = state_reqs_json_1.default;
|
|
2265
|
+
const reqLinkToStateReqs = stateReqsDict[xx];
|
|
2386
2266
|
// Populate the category
|
|
2387
2267
|
paRequirements = {
|
|
2388
2268
|
score: reqScore,
|
|
@@ -2645,19 +2525,28 @@ exports.doConfigureScales = doConfigureScales;
|
|
|
2645
2525
|
// Postprocess analytics - Normalize numeric results and derive secondary tests.
|
|
2646
2526
|
// Do this after analytics have been run and before preparing a test log or scorecard.
|
|
2647
2527
|
function doAnalyzePostProcessing(s, bLog = false) {
|
|
2648
|
-
//
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2528
|
+
// TODO - SCORE: Toggle
|
|
2529
|
+
if (s.useLegacy()) {
|
|
2530
|
+
// Normalize the raw scores for all the numerics tests
|
|
2531
|
+
let testResults = U.getNumericObjectKeys(testDefns);
|
|
2532
|
+
for (let testID of testResults) {
|
|
2533
|
+
if (testDefns[testID]['normalize']) {
|
|
2534
|
+
let testResult = s.getTest(testID);
|
|
2535
|
+
let rawScore = testResult['score'];
|
|
2536
|
+
let normalizedScore;
|
|
2537
|
+
normalizedScore = U.normalize(rawScore, s.testScales[testID]);
|
|
2538
|
+
testResult['normalizedScore'] = normalizedScore;
|
|
2539
|
+
// Add the scale used to normalize the raw score to the details
|
|
2540
|
+
testResult['details']['scale'] = s.testScales[testID].scale;
|
|
2541
|
+
}
|
|
2659
2542
|
}
|
|
2660
2543
|
}
|
|
2544
|
+
else {
|
|
2545
|
+
// Just populate the normalized population deviation score in the test
|
|
2546
|
+
const scorecard = s._scorecard;
|
|
2547
|
+
let test = s.getTest(4 /* PopulationDeviation */);
|
|
2548
|
+
test['normalizedScore'] = scorecard.best.populationDeviation.normalized;
|
|
2549
|
+
}
|
|
2661
2550
|
// Derive secondary tests
|
|
2662
2551
|
analyze_1.doDeriveSecondaryTests(s, bLog);
|
|
2663
2552
|
// Toggle the semaphore, so postprocessing isn't for both the testlog & scorecard
|
|
@@ -2740,13 +2629,11 @@ function makeNakedCxD(s, bLog = false) {
|
|
|
2740
2629
|
let CxD = [];
|
|
2741
2630
|
// Remove the unassigned & total dummy "districts"
|
|
2742
2631
|
for (let districtID = 1; districtID <= s.state.nDistricts; districtID++) {
|
|
2743
|
-
// TODO - SCORE: OH has an extra county!?!
|
|
2744
2632
|
const splits = U.deepCopy(adornedCxD[districtID].slice(1));
|
|
2745
2633
|
CxD.push(splits);
|
|
2746
2634
|
}
|
|
2747
2635
|
return CxD;
|
|
2748
2636
|
}
|
|
2749
|
-
// TODO - SCORE: Convert dict of geo props to array by district index
|
|
2750
2637
|
function makeArrayOfGeoProps(s, bLog = false) {
|
|
2751
2638
|
let geometryByDistrict = [];
|
|
2752
2639
|
for (let districtID = 1; districtID <= s.state.nDistricts; districtID++) {
|
|
@@ -2780,9 +2667,27 @@ function makeArrayOfDemographics(s, bLog = false) {
|
|
|
2780
2667
|
return demographicsArray;
|
|
2781
2668
|
}
|
|
2782
2669
|
// SCORE A PLAN
|
|
2783
|
-
function scorePlan(p, bLog = false) {
|
|
2670
|
+
function scorePlan(s, p, bLog = false, overridesJSON) {
|
|
2784
2671
|
let scorer = new Score.Scorer();
|
|
2785
|
-
|
|
2672
|
+
const scorecard = scorer.score(p, overridesJSON);
|
|
2673
|
+
// TODO - SCORE: Toggle: Before returning, create a dummy population deviation
|
|
2674
|
+
// test, for doHasEqualPopulations() to use later. This is preserving the old
|
|
2675
|
+
// calling sequence.
|
|
2676
|
+
let test = s.getTest(4 /* PopulationDeviation */);
|
|
2677
|
+
// TODO - SCORE: U.trim(popDev)???
|
|
2678
|
+
// Get the raw population deviation
|
|
2679
|
+
const popDev = scorecard.best.populationDeviation.raw;
|
|
2680
|
+
// Populate the test entry
|
|
2681
|
+
test['score'] = popDev;
|
|
2682
|
+
test['details'] = { 'maxDeviation': scorecard.best.populationDeviation.notes['maxDeviation'] };
|
|
2683
|
+
// Populate the N+1 summary "district" in district.statistics
|
|
2684
|
+
let totalPop = s.districts.statistics[D.DistrictField.TotalPop];
|
|
2685
|
+
let popDevPct = s.districts.statistics[D.DistrictField.PopDevPct];
|
|
2686
|
+
let summaryRow = s.districts.numberOfRows() - 1;
|
|
2687
|
+
totalPop[summaryRow] = p.populationProfile.targetSize;
|
|
2688
|
+
popDevPct[summaryRow] = popDev;
|
|
2689
|
+
//
|
|
2690
|
+
return scorecard;
|
|
2786
2691
|
}
|
|
2787
2692
|
exports.scorePlan = scorePlan;
|
|
2788
2693
|
|
|
@@ -2866,6 +2771,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
2866
2771
|
return result;
|
|
2867
2772
|
};
|
|
2868
2773
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
2774
|
+
const DT = __importStar(__webpack_require__(/*! @dra2020/dra-types */ "@dra2020/dra-types"));
|
|
2869
2775
|
const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"));
|
|
2870
2776
|
// PLAN HELPERS
|
|
2871
2777
|
// Is a "neighbor" in state?
|
|
@@ -2893,7 +2799,6 @@ function getDistrict(plan, geoID) {
|
|
|
2893
2799
|
}
|
|
2894
2800
|
exports.getDistrict = getDistrict;
|
|
2895
2801
|
// WORKING WITH GEOIDS
|
|
2896
|
-
// TODO - VFEATURE
|
|
2897
2802
|
function parseGeoID(geoID) {
|
|
2898
2803
|
let bVfeature = false;
|
|
2899
2804
|
// Rewrite vfeature GEOIDs to enable lexical parsing of higher-level parts
|
|
@@ -2901,12 +2806,7 @@ function parseGeoID(geoID) {
|
|
|
2901
2806
|
// Example: vfeature_39153153ASV_0_28e0bc2c8163e5982e1da2d61e2388a8325c575e
|
|
2902
2807
|
if (geoID.indexOf('vfeature') >= 0) {
|
|
2903
2808
|
bVfeature = true;
|
|
2904
|
-
|
|
2905
|
-
let parentGeoID = geoID.slice(9);
|
|
2906
|
-
// Strip off trailing goo
|
|
2907
|
-
const uPos = parentGeoID.indexOf('_');
|
|
2908
|
-
parentGeoID = parentGeoID.slice(0, uPos);
|
|
2909
|
-
geoID = parentGeoID;
|
|
2809
|
+
geoID = DT.vgeoidToGeoid(geoID);
|
|
2910
2810
|
}
|
|
2911
2811
|
const parts = {
|
|
2912
2812
|
vfeature: bVfeature,
|
|
@@ -2914,29 +2814,9 @@ function parseGeoID(geoID) {
|
|
|
2914
2814
|
county: geoID.substring(2, 5),
|
|
2915
2815
|
rest: geoID.slice(5)
|
|
2916
2816
|
};
|
|
2917
|
-
// let l: number = geoID.length;
|
|
2918
|
-
// if (l >= 11)
|
|
2919
|
-
// {
|
|
2920
|
-
// parts['tract'] = geoID.substring(0, 11);
|
|
2921
|
-
// }
|
|
2922
|
-
// if (l >= 12)
|
|
2923
|
-
// {
|
|
2924
|
-
// parts['bg'] = geoID.substring(0, 12);
|
|
2925
|
-
// }
|
|
2926
|
-
// if (l == 15)
|
|
2927
|
-
// {
|
|
2928
|
-
// parts['block'] = geoID;
|
|
2929
|
-
// }
|
|
2930
2817
|
return parts;
|
|
2931
2818
|
}
|
|
2932
2819
|
exports.parseGeoID = parseGeoID;
|
|
2933
|
-
// TODO - VFEATURE
|
|
2934
|
-
// export function getFIPSFromCountyGeoID(geoID: string): string
|
|
2935
|
-
// {
|
|
2936
|
-
// const fips = geoID.substring(2, 5);
|
|
2937
|
-
// if (isNaN(Number(fips))) console.log("Non-numeric GEOID =", geoID);
|
|
2938
|
-
// return fips;
|
|
2939
|
-
// }
|
|
2940
2820
|
function isWaterOnly(geoID) {
|
|
2941
2821
|
let waterOnlySignature = 'ZZZZZZ';
|
|
2942
2822
|
if (geoID.indexOf(waterOnlySignature) >= 0)
|
|
@@ -3386,6 +3266,17 @@ module.exports = require("@dra2020/dra-score");
|
|
|
3386
3266
|
|
|
3387
3267
|
/***/ }),
|
|
3388
3268
|
|
|
3269
|
+
/***/ "@dra2020/dra-types":
|
|
3270
|
+
/*!*************************************!*\
|
|
3271
|
+
!*** external "@dra2020/dra-types" ***!
|
|
3272
|
+
\*************************************/
|
|
3273
|
+
/*! no static exports found */
|
|
3274
|
+
/***/ (function(module, exports) {
|
|
3275
|
+
|
|
3276
|
+
module.exports = require("@dra2020/dra-types");
|
|
3277
|
+
|
|
3278
|
+
/***/ }),
|
|
3279
|
+
|
|
3389
3280
|
/***/ "@dra2020/poly":
|
|
3390
3281
|
/*!********************************!*\
|
|
3391
3282
|
!*** external "@dra2020/poly" ***!
|