@dra2020/district-analytics 4.3.4 → 5.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/README.md +1 -1
- package/dist/cli.js +19 -1134
- package/dist/cli.js.map +1 -1
- package/dist/district-analytics.js +17 -1042
- package/dist/district-analytics.js.map +1 -1
- package/package.json +2 -2
|
@@ -121,7 +121,6 @@ const preprocess_1 = __webpack_require__(/*! ./preprocess */ "./src/preprocess.t
|
|
|
121
121
|
const analyze_1 = __webpack_require__(/*! ./analyze */ "./src/analyze.ts");
|
|
122
122
|
const score_1 = __webpack_require__(/*! ./score */ "./src/score.ts");
|
|
123
123
|
const results_1 = __webpack_require__(/*! ./results */ "./src/results.ts");
|
|
124
|
-
// import { doConfigureScales, doAnalyzePostProcessing, RequirementsChecklist} from './results' // TODO - DELETE
|
|
125
124
|
const results_2 = __webpack_require__(/*! ./results */ "./src/results.ts");
|
|
126
125
|
const geofeature_1 = __webpack_require__(/*! ./geofeature */ "./src/geofeature.ts");
|
|
127
126
|
const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
@@ -132,7 +131,6 @@ class AnalyticsSession {
|
|
|
132
131
|
this.bOneTimeProcessingDone = false;
|
|
133
132
|
this.bPlanAnalyzed = false;
|
|
134
133
|
this.bPostProcessingDone = false;
|
|
135
|
-
// testScales = {} as T.TestScales; TODO - DELETE
|
|
136
134
|
this.tests = {};
|
|
137
135
|
this.title = SessionRequest['title'];
|
|
138
136
|
this.legislativeDistricts = SessionRequest['legislativeDistricts'];
|
|
@@ -143,42 +141,18 @@ class AnalyticsSession {
|
|
|
143
141
|
this.features = new D.Features(this, SessionRequest['data'], this.config['datasets']);
|
|
144
142
|
this.plan = new D.Plan(this, SessionRequest['plan']);
|
|
145
143
|
this.districts = new D.Districts(this, SessionRequest['districtShapes']);
|
|
146
|
-
// TODO - DELETE
|
|
147
|
-
// if (this.useLegacy())
|
|
148
|
-
// {
|
|
149
|
-
// console.log("Using legacy district-analytics.")
|
|
150
|
-
// // NOTE: I've pulled these out of the individual analytics to here. Eventually,
|
|
151
|
-
// // we could want them to passed into an analytics session as data, along with
|
|
152
|
-
// // everything else. For now, this keeps branching out of the main code.
|
|
153
|
-
// doConfigureScales(this);
|
|
154
|
-
// }
|
|
155
|
-
// else
|
|
156
|
-
// {
|
|
157
|
-
// console.log("Using dra-score analytics.")
|
|
158
|
-
// // TODO - SCORE: Temporary HACK. Query dra-score for threshold.
|
|
159
|
-
// doConfigureScales(this);
|
|
160
|
-
// }
|
|
161
144
|
}
|
|
162
145
|
processConfig(config) {
|
|
163
146
|
// NOTE - Session settings are required:
|
|
164
147
|
// - Analytics suites can be defaulted to all with [], but
|
|
165
148
|
// - Dataset keys must be explicitly specified with 'dataset'
|
|
166
|
-
//
|
|
149
|
+
// NOTE - Legacy feature: Always calc everything
|
|
167
150
|
config['suites'] = [0 /* Legal */, 1 /* Fair */, 2 /* Best */];
|
|
168
151
|
// Default the Census & redistricting cycle to 2010
|
|
169
152
|
if (!(U.keyExists('cycle', config)))
|
|
170
153
|
config['cycle'] = 2010;
|
|
171
154
|
return config;
|
|
172
155
|
}
|
|
173
|
-
/* TODO - DELETE
|
|
174
|
-
useLegacy(): boolean
|
|
175
|
-
{
|
|
176
|
-
// TODO - SCORE: Opt-out
|
|
177
|
-
return (U.keyExists('useScore', this.config) && (this.config['useScore'] != null) && (!(this.config['useScore']))) ? true : false;
|
|
178
|
-
// TODO - SCORE: Opt-in
|
|
179
|
-
// return (U.keyExists('useScore', this.config) && (this.config['useScore'])) ? false : true;
|
|
180
|
-
}
|
|
181
|
-
*/
|
|
182
156
|
// Using the the data in the analytics session, calculate all the
|
|
183
157
|
// analytics & validations, saving/updating the individual test results.
|
|
184
158
|
analyzePlan(bLog = false, overridesJSON) {
|
|
@@ -186,7 +160,6 @@ class AnalyticsSession {
|
|
|
186
160
|
preprocess_1.doPreprocessData(this, bLog);
|
|
187
161
|
analyze_1.doAnalyzeDistricts(this, bLog);
|
|
188
162
|
analyze_1.doAnalyzePlan(this, bLog);
|
|
189
|
-
// TODO - SCORE
|
|
190
163
|
this._profile = score_1.profilePlan(this, bLog);
|
|
191
164
|
this._scorecard = score_1.scorePlan(this, this._profile, bLog, overridesJSON);
|
|
192
165
|
results_1.doAnalyzePostProcessing(this, bLog);
|
|
@@ -201,15 +174,12 @@ class AnalyticsSession {
|
|
|
201
174
|
getDistrictStatistics(bLog = false) {
|
|
202
175
|
return results_2.prepareDistrictStatistics(this, bLog);
|
|
203
176
|
}
|
|
204
|
-
// TODO - SCORE
|
|
205
177
|
getPlanProfile(bLog = false) {
|
|
206
178
|
return this._profile;
|
|
207
179
|
}
|
|
208
|
-
// TODO - SCORE
|
|
209
180
|
getPlanScorecard(bLog = false) {
|
|
210
181
|
return this._scorecard;
|
|
211
182
|
}
|
|
212
|
-
// TODO - SCORE
|
|
213
183
|
// NOTE - This assumes that analyzePlan() has been run!
|
|
214
184
|
getRequirementsChecklist(bLog = false) {
|
|
215
185
|
return results_2.prepareRequirementsChecklist(this, bLog);
|
|
@@ -310,21 +280,10 @@ class AnalyticsSession {
|
|
|
310
280
|
}
|
|
311
281
|
// NOTE - Not sure why this has to be up here ...
|
|
312
282
|
populationDeviationThreshold() {
|
|
313
|
-
// TODO - DELETE
|
|
314
|
-
// if (this.useLegacy())
|
|
315
|
-
// {
|
|
316
|
-
// return 1 - this.testScales[T.Test.PopulationDeviation]['scale'][0];
|
|
317
|
-
// }
|
|
318
|
-
// else
|
|
319
|
-
// {
|
|
320
283
|
// NOTE - This assumes the plan has been profiled
|
|
321
284
|
const scorer = new Score.Scorer();
|
|
322
285
|
const popdev = scorer.populationDeviationThreshold(this.legislativeDistricts);
|
|
323
286
|
return popdev;
|
|
324
|
-
// TODO - SCORE: Temporary HACK. Query dra-score for threshold.
|
|
325
|
-
// NOTE - The plan may not have been scored yet, i.e., no scorecard yet.
|
|
326
|
-
// return 1 - this.testScales[T.Test.PopulationDeviation]['scale'][0];
|
|
327
|
-
// }
|
|
328
287
|
}
|
|
329
288
|
}
|
|
330
289
|
exports.AnalyticsSession = AnalyticsSession;
|
|
@@ -438,11 +397,11 @@ class Districts {
|
|
|
438
397
|
}
|
|
439
398
|
// This is the workhorse computational routine!
|
|
440
399
|
//
|
|
441
|
-
//
|
|
442
|
-
//
|
|
400
|
+
// NOTE - OPTIMIZE for getting multiple properties from the same feature?
|
|
401
|
+
// NOTE - OPTIMIZE by only re-calc'ing districts that have changed?
|
|
443
402
|
// In this case, special attention to getting county-splits right.
|
|
444
|
-
//
|
|
445
|
-
//
|
|
403
|
+
// NOTE - OPTIMIZE by async'ing this?
|
|
404
|
+
// NOTE - Is there a way to do this programmatically off data? Does it matter?
|
|
446
405
|
recalcStatistics(bLog = false) {
|
|
447
406
|
// Initialize debug counters
|
|
448
407
|
nMissingDataset = 0;
|
|
@@ -453,7 +412,6 @@ class Districts {
|
|
|
453
412
|
let planByDistrict = this._session.plan.byDistrictID();
|
|
454
413
|
let plan = this._session.plan;
|
|
455
414
|
let graph = this._session.graph;
|
|
456
|
-
// NOTE - SPLITTING
|
|
457
415
|
// Add an extra 0th virtual county bucket for county-district splitting analysis
|
|
458
416
|
let nCountyBuckets = this._session.counties.nCounties + 1;
|
|
459
417
|
// INITIALIZE STATE VALUES THAT WILL BE ACCUMULATED
|
|
@@ -479,7 +437,6 @@ class Districts {
|
|
|
479
437
|
// INITIALIZE DISTRICT VALUES THAT WILL BE ACCUMULATED (VS. DERIVED)
|
|
480
438
|
let featurePop;
|
|
481
439
|
let totalPop = 0;
|
|
482
|
-
// NOTE - SPLITTING
|
|
483
440
|
let countySplits = U.initArray(nCountyBuckets, 0);
|
|
484
441
|
let demVotes = 0;
|
|
485
442
|
let repVotes = 0;
|
|
@@ -523,7 +480,6 @@ class Districts {
|
|
|
523
480
|
featurePop = outerThis._session.features.fieldForFeature(f, "CENSUS" /* CENSUS */, "Tot" /* TotalPop */);
|
|
524
481
|
// Total district population
|
|
525
482
|
totalPop += featurePop;
|
|
526
|
-
// NOTE - SPLITTING
|
|
527
483
|
// Total population by counties w/in a district,
|
|
528
484
|
// except the dummy unassigned district 0
|
|
529
485
|
if (i > 0)
|
|
@@ -727,7 +683,7 @@ class Features {
|
|
|
727
683
|
}
|
|
728
684
|
resetDataset(d, k) {
|
|
729
685
|
this._keys[d] = k;
|
|
730
|
-
//
|
|
686
|
+
// NOTE - RECALC: Does anything need to be recalc'd now when a dataset is changed?
|
|
731
687
|
}
|
|
732
688
|
mapGeoIDsToFeatureIDs() {
|
|
733
689
|
for (let i = 0; i < this._session.features.nFeatures(); i++) {
|
|
@@ -842,7 +798,6 @@ class Plan {
|
|
|
842
798
|
// return newPlan;
|
|
843
799
|
// }
|
|
844
800
|
invertPlan() {
|
|
845
|
-
// NOTE - UNASSIGNED
|
|
846
801
|
this._planByDistrictID = invertPlan(this._planByGeoID, this._session);
|
|
847
802
|
this.districtIDs = U.getNumericObjectKeys(this._planByDistrictID);
|
|
848
803
|
}
|
|
@@ -858,7 +813,6 @@ function invertPlan(plan, s) {
|
|
|
858
813
|
let invertedPlan = {};
|
|
859
814
|
// Add a dummy 'unassigned' district
|
|
860
815
|
invertedPlan[S.NOT_ASSIGNED] = new Set();
|
|
861
|
-
// NOTE - UNASSIGNED
|
|
862
816
|
// The feature assignments coming from DRA do not include unassigned ones.
|
|
863
817
|
// - In the DRA-calling context, there's an analytics session with a reference
|
|
864
818
|
// to the features. Loop over all the features to find the unassigned ones,
|
|
@@ -901,7 +855,6 @@ class Graph {
|
|
|
901
855
|
// Ignore the lengths of the shared borders (the values), for now
|
|
902
856
|
// Protect against getting a GEOID that's not in the graph
|
|
903
857
|
if (U.keyExists(node, this._graph)) {
|
|
904
|
-
// NOTE - CONTIGUITY GRAPHS
|
|
905
858
|
// Handle both unweighted & weighted neighbors
|
|
906
859
|
let n = this._graph[node];
|
|
907
860
|
let l = (n instanceof Array) ? n : U.getObjectKeys(n);
|
|
@@ -933,22 +886,7 @@ exports.Graph = Graph;
|
|
|
933
886
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
934
887
|
const valid_1 = __webpack_require__(/*! ./valid */ "./src/valid.ts");
|
|
935
888
|
const equal_1 = __webpack_require__(/*! ./equal */ "./src/equal.ts");
|
|
936
|
-
// import { doPopulationDeviation, doHasEqualPopulations } from './equal'; TODO - DELETE
|
|
937
|
-
// import { doReock, doPolsbyPopper } from './compact'; TODO - DELETE
|
|
938
889
|
const cohesive_1 = __webpack_require__(/*! ./cohesive */ "./src/cohesive.ts");
|
|
939
|
-
/* TODO - DELETE
|
|
940
|
-
import
|
|
941
|
-
{
|
|
942
|
-
doFindCountiesSplitUnexpectedly, doFindSplitVTDs,
|
|
943
|
-
doCountySplitting, doDistrictSplitting
|
|
944
|
-
} from './cohesive';
|
|
945
|
-
import
|
|
946
|
-
{
|
|
947
|
-
doSeatsBias, doVotesBias,
|
|
948
|
-
doResponsiveness, doResponsiveDistricts, doEfficiencyGap
|
|
949
|
-
} from './political';
|
|
950
|
-
import { doMajorityMinorityDistricts } from './minority'
|
|
951
|
-
*/
|
|
952
890
|
// Compile district-level info for plan/map-level analytics
|
|
953
891
|
function doAnalyzeDistricts(s, bLog = false) {
|
|
954
892
|
s.districts.recalcStatistics(bLog);
|
|
@@ -960,56 +898,9 @@ exports.doAnalyzeDistricts = doAnalyzeDistricts;
|
|
|
960
898
|
// NOTE - I could make this table-driven, but I'm thinking that the explicit
|
|
961
899
|
// calls might make chunking for aync easier.
|
|
962
900
|
function doAnalyzePlan(s, bLog = false) {
|
|
963
|
-
// TODO - DELETE
|
|
964
|
-
// if (s.useLegacy())
|
|
965
|
-
// {
|
|
966
|
-
// // Disable most legacy analytics
|
|
967
|
-
// // Get the requested suites, and only execute those tests
|
|
968
|
-
// let requestedSuites = s.config['suites'];
|
|
969
|
-
// // Tests in the "Legal" suite, i.e., pass/ fail constraints
|
|
970
|
-
// if (requestedSuites.includes(T.Suite.Legal))
|
|
971
|
-
// {
|
|
972
|
-
// s.tests[T.Test.Complete] = doIsComplete(s, bLog);
|
|
973
|
-
// s.tests[T.Test.Contiguous] = doIsContiguous(s, bLog);
|
|
974
|
-
// s.tests[T.Test.FreeOfHoles] = doIsFreeOfHoles(s, bLog);
|
|
975
|
-
// // s.tests[T.Test.PopulationDeviation] = doPopulationDeviation(s, bLog); TODO - DELETE
|
|
976
|
-
// // NOTE - I can't check whether a population deviation is legal or not, until
|
|
977
|
-
// // the raw % is normalized. A zero (0) would mean "too much" / "not legal," for
|
|
978
|
-
// // the given type of district (CD vs. LD). The EqualPopulation test is derived
|
|
979
|
-
// // from PopulationDeviation, as part of scorecard or test log preparation.
|
|
980
|
-
// // Create an empty test entry here though ...
|
|
981
|
-
// s.tests[T.Test.EqualPopulation] = s.getTest(T.Test.EqualPopulation) as T.TestEntry;
|
|
982
|
-
// }
|
|
983
|
-
// // Tests in the "Fair" suite
|
|
984
|
-
// if (requestedSuites.includes(T.Suite.Fair))
|
|
985
|
-
// {
|
|
986
|
-
// /* TODO - DELETE
|
|
987
|
-
// s.tests[T.Test.SeatsBias] = doSeatsBias(s, bLog);
|
|
988
|
-
// s.tests[T.Test.VotesBias] = doVotesBias(s, bLog);
|
|
989
|
-
// s.tests[T.Test.Responsiveness] = doResponsiveness(s, bLog);
|
|
990
|
-
// s.tests[T.Test.ResponsiveDistricts] = doResponsiveDistricts(s, bLog);
|
|
991
|
-
// s.tests[T.Test.EfficiencyGap] = doEfficiencyGap(s, bLog);
|
|
992
|
-
// s.tests[T.Test.MajorityMinorityDistricts] = doMajorityMinorityDistricts(s, bLog);
|
|
993
|
-
// */
|
|
994
|
-
// }
|
|
995
|
-
// // Tests in the "Best" suite, i.e., criteria for better/worse
|
|
996
|
-
// if (requestedSuites.includes(T.Suite.Best))
|
|
997
|
-
// {
|
|
998
|
-
// // s.tests[T.Test.Reock] = doReock(s, bLog); TODO - DELETE
|
|
999
|
-
// // s.tests[T.Test.PolsbyPopper] = doPolsbyPopper(s, bLog); TODO - DELETE
|
|
1000
|
-
// s.tests[T.Test.UnexpectedCountySplits] = doFindCountiesSplitUnexpectedly(s, bLog);
|
|
1001
|
-
// s.tests[T.Test.VTDSplits] = doFindSplitVTDs(s, bLog);
|
|
1002
|
-
// // s.tests[T.Test.CountySplitting] = doCountySplitting(s, bLog); TODO - DELETE
|
|
1003
|
-
// // s.tests[T.Test.DistrictSplitting] = doDistrictSplitting(s, bLog); TODO - DELETE
|
|
1004
|
-
// }
|
|
1005
|
-
// }
|
|
1006
|
-
// else
|
|
1007
|
-
// {
|
|
1008
|
-
// TODO - SCORE: Except these. Continue to do these here vs. dra-score.
|
|
1009
901
|
s.tests[0 /* Complete */] = valid_1.doIsComplete(s, bLog);
|
|
1010
902
|
s.tests[1 /* Contiguous */] = valid_1.doIsContiguous(s, bLog);
|
|
1011
903
|
s.tests[2 /* FreeOfHoles */] = valid_1.doIsFreeOfHoles(s, bLog);
|
|
1012
|
-
// s.tests[T.Test.PopulationDeviation] = doPopulationDeviation(s, bLog); TODO - DELETE
|
|
1013
904
|
// NOTE - I can't check whether a population deviation is legal or not, until
|
|
1014
905
|
// the raw % is normalized. A zero (0) would mean "too much" / "not legal," for
|
|
1015
906
|
// the given type of district (CD vs. LD). The EqualPopulation test is derived
|
|
@@ -1018,7 +909,6 @@ function doAnalyzePlan(s, bLog = false) {
|
|
|
1018
909
|
s.tests[3 /* EqualPopulation */] = s.getTest(3 /* EqualPopulation */);
|
|
1019
910
|
s.tests[5 /* UnexpectedCountySplits */] = cohesive_1.doFindCountiesSplitUnexpectedly(s, bLog);
|
|
1020
911
|
s.tests[6 /* VTDSplits */] = cohesive_1.doFindSplitVTDs(s, bLog);
|
|
1021
|
-
// }
|
|
1022
912
|
// Enable a Test Log and Scorecard to be generated
|
|
1023
913
|
s.bPlanAnalyzed = true;
|
|
1024
914
|
s.bPostProcessingDone = false;
|
|
@@ -1061,277 +951,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
1061
951
|
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
1062
952
|
const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
1063
953
|
// NOTE - The active code is below the long multi-line section to be deleted.
|
|
1064
|
-
/* TODO - DELETE
|
|
1065
|
-
// CALCULATE ENHANCED SQRT ENTROPY METRIC
|
|
1066
|
-
|
|
1067
|
-
export function doCountySplitting(s: AnalyticsSession, bLog: boolean = false): T.TestEntry
|
|
1068
|
-
{
|
|
1069
|
-
let test = s.getTest(T.Test.CountySplitting) as T.TestEntry;
|
|
1070
|
-
|
|
1071
|
-
let CxD = s.districts.statistics[D.DistrictField.CountySplits].slice(0, -1);
|
|
1072
|
-
let countyTotals = s.counties.totalPopulation;
|
|
1073
|
-
let districtTotals = s.districts.statistics[D.DistrictField.TotalPop].slice(0, -1);
|
|
1074
|
-
|
|
1075
|
-
let f = calcCountyFractions(CxD, countyTotals);
|
|
1076
|
-
let w = calcCountyWeights(countyTotals);
|
|
1077
|
-
|
|
1078
|
-
let SqEnt_DC = countySplitting(f, w, bLog);
|
|
1079
|
-
|
|
1080
|
-
let CxDreducedC = U.deepCopy(CxD);
|
|
1081
|
-
reduceCSplits(CxDreducedC, districtTotals);
|
|
1082
|
-
|
|
1083
|
-
let fReduced = calcCountyFractions(CxDreducedC, countyTotals);
|
|
1084
|
-
let wReduced = calcCountyWeights(countyTotals);
|
|
1085
|
-
|
|
1086
|
-
let SqEnt_DCreduced = countySplitting(fReduced, wReduced, bLog);
|
|
1087
|
-
|
|
1088
|
-
test['score'] = SqEnt_DCreduced;
|
|
1089
|
-
test['details']['SqEnt_DC'] = SqEnt_DC;
|
|
1090
|
-
|
|
1091
|
-
return test;
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
export function doDistrictSplitting(s: AnalyticsSession, bLog: boolean = false): T.TestEntry
|
|
1095
|
-
{
|
|
1096
|
-
let test = s.getTest(T.Test.DistrictSplitting) as T.TestEntry;
|
|
1097
|
-
|
|
1098
|
-
let CxD = s.districts.statistics[D.DistrictField.CountySplits].slice(0, -1);
|
|
1099
|
-
let countyTotals = s.counties.totalPopulation;
|
|
1100
|
-
let districtTotals = s.districts.statistics[D.DistrictField.TotalPop].slice(0, -1);
|
|
1101
|
-
|
|
1102
|
-
let g = calcDistrictFractions(CxD, districtTotals);
|
|
1103
|
-
let x = calcDistrictWeights(districtTotals);
|
|
1104
|
-
|
|
1105
|
-
let SqEnt_CD = districtSplitting(g, x, bLog);
|
|
1106
|
-
|
|
1107
|
-
let CxDreducedD = U.deepCopy(CxD);
|
|
1108
|
-
reduceDSplits(CxDreducedD, countyTotals);
|
|
1109
|
-
|
|
1110
|
-
let gReduced = calcDistrictFractions(CxDreducedD, districtTotals);
|
|
1111
|
-
let xReduced = calcDistrictWeights(districtTotals);
|
|
1112
|
-
|
|
1113
|
-
let SqEnt_CDreduced = districtSplitting(gReduced, xReduced, bLog);
|
|
1114
|
-
|
|
1115
|
-
test['score'] = SqEnt_CDreduced;
|
|
1116
|
-
test['details']['SqEnt_CD'] = SqEnt_CD;
|
|
1117
|
-
|
|
1118
|
-
return test;
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
// HELPERS
|
|
1123
|
-
|
|
1124
|
-
// Loop over all the county-district combos, skipping the virtual district 0
|
|
1125
|
-
// and virtual county 0.
|
|
1126
|
-
//
|
|
1127
|
-
// NOTE - The county-district splits and the county & district totals may all,
|
|
1128
|
-
// in general, be fractional/decimal numbers as opposed to integers, due to
|
|
1129
|
-
// dissaggregation & re-aggregation. Hence, comparisons need to approximate
|
|
1130
|
-
// equality.
|
|
1131
|
-
|
|
1132
|
-
// Consolidate districts (rows) consisting of just one county (column)
|
|
1133
|
-
// UP into the dummy district (0).
|
|
1134
|
-
function reduceCSplits(CxDreducedC: number[][], districtTotals: number[]): void
|
|
1135
|
-
{
|
|
1136
|
-
let nD = CxDreducedC.length;
|
|
1137
|
-
let nC = CxDreducedC[0].length;
|
|
1138
|
-
|
|
1139
|
-
for (let j = 1; j < nC; j++)
|
|
1140
|
-
{
|
|
1141
|
-
for (let i = 1; i < nD; i++)
|
|
1142
|
-
{
|
|
1143
|
-
let split_total = CxDreducedC[i][j];
|
|
1144
|
-
|
|
1145
|
-
if (split_total > 0)
|
|
1146
|
-
{
|
|
1147
|
-
if (areRoughlyEqual(split_total, districtTotals[i]))
|
|
1148
|
-
{
|
|
1149
|
-
CxDreducedC[0][j] += split_total;
|
|
1150
|
-
CxDreducedC[i][j] = 0;
|
|
1151
|
-
}
|
|
1152
|
-
}
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
|
|
1157
|
-
// Consolidate whole counties (columns) in a district (row) LEFT into the
|
|
1158
|
-
// dummy county (0).
|
|
1159
|
-
function reduceDSplits(CxDreducedD: number[][], countyTotals: number[]): void
|
|
1160
|
-
{
|
|
1161
|
-
let nD = CxDreducedD.length;
|
|
1162
|
-
let nC = CxDreducedD[0].length;
|
|
1163
|
-
|
|
1164
|
-
for (let i = 1; i < nD; i++)
|
|
1165
|
-
{
|
|
1166
|
-
for (let j = 1; j < nC; j++)
|
|
1167
|
-
{
|
|
1168
|
-
let split_total = CxDreducedD[i][j];
|
|
1169
|
-
|
|
1170
|
-
if (split_total > 0)
|
|
1171
|
-
{
|
|
1172
|
-
if (areRoughlyEqual(split_total, countyTotals[j]))
|
|
1173
|
-
{
|
|
1174
|
-
CxDreducedD[i][0] += split_total;
|
|
1175
|
-
CxDreducedD[i][j] = 0;
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
}
|
|
1181
|
-
|
|
1182
|
-
function calcCountyWeights(countyTotals: number[]): number[]
|
|
1183
|
-
{
|
|
1184
|
-
let nC: number = countyTotals.length;
|
|
1185
|
-
let cTotal: number = U.sumArray(countyTotals);
|
|
1186
|
-
|
|
1187
|
-
let w: number[] = U.initArray(nC, 0.0);
|
|
1188
|
-
|
|
1189
|
-
for (let j = 0; j < nC; j++)
|
|
1190
|
-
{
|
|
1191
|
-
w[j] = countyTotals[j] / cTotal;
|
|
1192
|
-
}
|
|
1193
|
-
|
|
1194
|
-
return w;
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
function calcDistrictWeights(districtTotals: number[]): number[]
|
|
1198
|
-
{
|
|
1199
|
-
let nD = districtTotals.length;
|
|
1200
|
-
let dTotal: number = U.sumArray(districtTotals);
|
|
1201
|
-
|
|
1202
|
-
let x: number[] = U.initArray(nD, 0.0);
|
|
1203
|
-
|
|
1204
|
-
for (let i = 0; i < nD; i++)
|
|
1205
|
-
{
|
|
1206
|
-
x[i] = districtTotals[i] / dTotal;
|
|
1207
|
-
}
|
|
1208
|
-
|
|
1209
|
-
return x;
|
|
1210
|
-
}
|
|
1211
|
-
|
|
1212
|
-
function calcCountyFractions(CxDreducedD: number[][], countyTotals: number[]): number[][]
|
|
1213
|
-
{
|
|
1214
|
-
let nD = CxDreducedD.length;
|
|
1215
|
-
let nC = CxDreducedD[0].length;
|
|
1216
|
-
|
|
1217
|
-
let f: number[][] = new Array(nD).fill(0.0).map(() => new Array(nC).fill(0.0));
|
|
1218
|
-
|
|
1219
|
-
for (let j = 0; j < nC; j++)
|
|
1220
|
-
{
|
|
1221
|
-
for (let i = 0; i < nD; i++)
|
|
1222
|
-
{
|
|
1223
|
-
if (countyTotals[j] > 0)
|
|
1224
|
-
{
|
|
1225
|
-
f[i][j] = CxDreducedD[i][j] / countyTotals[j];
|
|
1226
|
-
}
|
|
1227
|
-
else
|
|
1228
|
-
{
|
|
1229
|
-
f[i][j] = 0.0;
|
|
1230
|
-
}
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
|
|
1234
|
-
return f;
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1237
|
-
function calcDistrictFractions(CxDreducedC: number[][], districtTotals: number[]): number[][]
|
|
1238
|
-
{
|
|
1239
|
-
let nD = CxDreducedC.length;
|
|
1240
|
-
let nC = CxDreducedC[0].length;
|
|
1241
|
-
|
|
1242
|
-
let g: number[][] = new Array(nD).fill(0.0).map(() => new Array(nC).fill(0.0));
|
|
1243
|
-
|
|
1244
|
-
for (let j = 0; j < nC; j++)
|
|
1245
|
-
{
|
|
1246
|
-
for (let i = 0; i < nD; i++)
|
|
1247
|
-
{
|
|
1248
|
-
if (districtTotals[i] > 0)
|
|
1249
|
-
{
|
|
1250
|
-
g[i][j] = CxDreducedC[i][j] / districtTotals[i];
|
|
1251
|
-
}
|
|
1252
|
-
else
|
|
1253
|
-
{
|
|
1254
|
-
g[i][j] = 0.0;
|
|
1255
|
-
}
|
|
1256
|
-
}
|
|
1257
|
-
}
|
|
1258
|
-
|
|
1259
|
-
return g;
|
|
1260
|
-
}
|
|
1261
|
-
|
|
1262
|
-
// Deal with decimal census "counts" due to disagg/re-agg
|
|
1263
|
-
function areRoughlyEqual(x: number, y: number): boolean
|
|
1264
|
-
{
|
|
1265
|
-
let delta = Math.abs(x - y);
|
|
1266
|
-
let result = (delta < S.EQUAL_TOLERANCE) ? true : false;
|
|
1267
|
-
|
|
1268
|
-
return result;
|
|
1269
|
-
}
|
|
1270
|
-
|
|
1271
|
-
// For all districts in a county, sum the split score.
|
|
1272
|
-
function countySplitScore(j: number, f: number[][], numD: number, bLog: boolean = false): number
|
|
1273
|
-
{
|
|
1274
|
-
let e = 0.0;
|
|
1275
|
-
|
|
1276
|
-
for (let i = 0; i < numD; i++)
|
|
1277
|
-
{
|
|
1278
|
-
e += Math.sqrt(f[i][j]);
|
|
1279
|
-
}
|
|
1280
|
-
|
|
1281
|
-
return e;
|
|
1282
|
-
}
|
|
1283
|
-
|
|
1284
|
-
// For all counties, sum the weighted county splits.
|
|
1285
|
-
function countySplitting(f: number[][], w: number[], bLog: boolean = false): number
|
|
1286
|
-
{
|
|
1287
|
-
let numC = f[0].length;
|
|
1288
|
-
let numD = f.length;
|
|
1289
|
-
|
|
1290
|
-
let e = 0.0;
|
|
1291
|
-
|
|
1292
|
-
for (let j = 0; j < numC; j++)
|
|
1293
|
-
{
|
|
1294
|
-
let splitScore = countySplitScore(j, f, numD, bLog);
|
|
1295
|
-
e += w[j] * splitScore;
|
|
1296
|
-
|
|
1297
|
-
if (bLog) console.log("County splitting =", j, w[j], splitScore, e);
|
|
1298
|
-
}
|
|
1299
|
-
|
|
1300
|
-
return U.trim(e, 3);
|
|
1301
|
-
}
|
|
1302
|
-
|
|
1303
|
-
// For all counties in a district, sum the split score.
|
|
1304
|
-
function districtSplitScore(i: number, g: number[][], numC: number, bLog: boolean = false): number
|
|
1305
|
-
{
|
|
1306
|
-
let e = 0.0;
|
|
1307
|
-
|
|
1308
|
-
for (let j = 0; j < numC; j++)
|
|
1309
|
-
{
|
|
1310
|
-
e += Math.sqrt(g[i][j]);
|
|
1311
|
-
}
|
|
1312
|
-
|
|
1313
|
-
return e;
|
|
1314
|
-
}
|
|
1315
|
-
|
|
1316
|
-
// For all districts, sum the weighted district splits.
|
|
1317
|
-
function districtSplitting(g: number[][], x: number[], bLog: boolean = false): number
|
|
1318
|
-
{
|
|
1319
|
-
let numC = g[0].length;
|
|
1320
|
-
let numD = g.length;
|
|
1321
|
-
|
|
1322
|
-
let e = 0.0;
|
|
1323
|
-
|
|
1324
|
-
for (let i = 0; i < numD; i++)
|
|
1325
|
-
{
|
|
1326
|
-
let splitScore = districtSplitScore(i, g, numC, bLog);
|
|
1327
|
-
e += x[i] * splitScore;
|
|
1328
|
-
|
|
1329
|
-
if (bLog) console.log("District split score =", i, x[i], splitScore, e);
|
|
1330
|
-
}
|
|
1331
|
-
|
|
1332
|
-
return U.trim(e, 3);
|
|
1333
|
-
}
|
|
1334
|
-
*/
|
|
1335
954
|
// ANALYZE SIMPLE COUNTY & VTD SPLITTING
|
|
1336
955
|
/*
|
|
1337
956
|
|
|
@@ -1495,164 +1114,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
1495
1114
|
// NOTE - This file will NOT be empty, when legacy code is deleted.
|
|
1496
1115
|
const Poly = __importStar(__webpack_require__(/*! @dra2020/poly */ "@dra2020/poly"));
|
|
1497
1116
|
const geofeature_1 = __webpack_require__(/*! ./geofeature */ "./src/geofeature.ts");
|
|
1498
|
-
// TODO - DELETE
|
|
1499
|
-
// Measures of compactness compare district shapes to various ideally compact
|
|
1500
|
-
// benchmarks, such as circles. All else equal, more compact districts are better.
|
|
1501
|
-
//
|
|
1502
|
-
// There are four popular measures of compactness. They either focus on how
|
|
1503
|
-
// dispersed or how indented a shapes are.
|
|
1504
|
-
//
|
|
1505
|
-
// These first two measures are the most important:
|
|
1506
|
-
//
|
|
1507
|
-
// Reock is the primary measure of the dispersion of district shapes, calculated
|
|
1508
|
-
// as “the area of the district to the area of the minimum spanning circle that
|
|
1509
|
-
// can enclose the district.”
|
|
1510
|
-
//
|
|
1511
|
-
// R = A / A(Minimum Bounding Circle)
|
|
1512
|
-
// R = A / (π * (D / 2)^2)
|
|
1513
|
-
//
|
|
1514
|
-
// R = 4A / πD^2
|
|
1515
|
-
//
|
|
1516
|
-
// where A is the area of the district and D is the diameter of the minimum
|
|
1517
|
-
// bounding circle.
|
|
1518
|
-
//
|
|
1519
|
-
// Polsby-Popper is the primary measure of the indendentation of district shapes,
|
|
1520
|
-
// calculated as the “the ratio of the area of the district to the area of a circle
|
|
1521
|
-
// whose circumference is equal to the perimeter of the district.”
|
|
1522
|
-
//
|
|
1523
|
-
// PP = A / A(C)
|
|
1524
|
-
//
|
|
1525
|
-
// where C is that circle. In other words:
|
|
1526
|
-
//
|
|
1527
|
-
// P = 2πRc and A(C) = π(P / 2π)^2
|
|
1528
|
-
//
|
|
1529
|
-
// where P is the perimeter of the district and Rc is the radius of the circle.
|
|
1530
|
-
//
|
|
1531
|
-
// Hence, the measure simplifies to:
|
|
1532
|
-
//
|
|
1533
|
-
// PP = 4π * (A / P^2)
|
|
1534
|
-
//
|
|
1535
|
-
// I propose that we use these two, normalize them, and weight equally to determine
|
|
1536
|
-
// our compactness value.
|
|
1537
|
-
//
|
|
1538
|
-
// These second two measures may be used to complement the primary ones above:
|
|
1539
|
-
//
|
|
1540
|
-
// Convex Hull is a secondary measure of the dispersion of district shapes, calculated
|
|
1541
|
-
// as “the ratio of the district area to the area of the minimum convex bounding
|
|
1542
|
-
// polygon (also known as a convex hull) enclosing the district.”
|
|
1543
|
-
//
|
|
1544
|
-
// CH = A / A(Convex Hull)
|
|
1545
|
-
//
|
|
1546
|
-
// where a convex hull is the minimum perimeter that encloses all points in a shape,
|
|
1547
|
-
// basically the shortest unstretched rubber band that fits around the shape.
|
|
1548
|
-
//
|
|
1549
|
-
// Schwartzberg is a secondary measure of the degree of indentation of district
|
|
1550
|
-
// shapes, calculated as “the ratio of the perimeter of the district to the circumference
|
|
1551
|
-
// of a circle whose area is equal to the area of the district.”
|
|
1552
|
-
//
|
|
1553
|
-
// S = 1 / (P / C)
|
|
1554
|
-
//
|
|
1555
|
-
// where P is the perimeter of the district and C is the circumference of the circle.
|
|
1556
|
-
// The radius of the circle is:
|
|
1557
|
-
//
|
|
1558
|
-
// Rc = SQRT(A / π)
|
|
1559
|
-
//
|
|
1560
|
-
// So, the circumference of the circle is:
|
|
1561
|
-
//
|
|
1562
|
-
// C = 2πRc or C = 2π * SQRT(A / π)
|
|
1563
|
-
//
|
|
1564
|
-
// Hence:
|
|
1565
|
-
//
|
|
1566
|
-
// S = 1 (P / 2π * SQRT(A / π))
|
|
1567
|
-
//
|
|
1568
|
-
// S = (2π * SQRT(A / π)) / P
|
|
1569
|
-
//
|
|
1570
|
-
// All these measures produce values between 0 and 1, with 0 being the least compact
|
|
1571
|
-
// and 1 being the most compact. Sometimes these values are multiplied by 100 to
|
|
1572
|
-
// give values between 0 and 100.
|
|
1573
|
-
//
|
|
1574
|
-
// For each measure, the compactness of a set of Congressional districts is the
|
|
1575
|
-
// average of that measure for all the districts.
|
|
1576
|
-
//
|
|
1577
|
-
/* TODO - DELETE
|
|
1578
|
-
// Calculate Reock compactness:
|
|
1579
|
-
// reock = (4 * a) / (math.pi * d**2)
|
|
1580
|
-
// NOTE - Depends on extractDistrictProperties running first
|
|
1581
|
-
export function doReock(s: AnalyticsSession, bLog: boolean = false): T.TestEntry
|
|
1582
|
-
{
|
|
1583
|
-
let test = s.getTest(T.Test.Reock) as T.TestEntry;
|
|
1584
|
-
|
|
1585
|
-
// Calculate Reock scores by district
|
|
1586
|
-
let scores: number[] = [];
|
|
1587
|
-
|
|
1588
|
-
for (let districtID = 1; districtID <= s.state.nDistricts; districtID++)
|
|
1589
|
-
{
|
|
1590
|
-
let districtProps = s.districts.getGeoProperties(districtID);
|
|
1591
|
-
// Guard against no shape and no properties
|
|
1592
|
-
if (districtProps)
|
|
1593
|
-
{
|
|
1594
|
-
let a = districtProps[T.DistrictShapeProperty.Area];
|
|
1595
|
-
let d = districtProps[T.DistrictShapeProperty.Diameter];
|
|
1596
|
-
|
|
1597
|
-
let reock = (4 * a) / (Math.PI * d ** 2);
|
|
1598
|
-
|
|
1599
|
-
// Save each district score
|
|
1600
|
-
scores.push(reock);
|
|
1601
|
-
|
|
1602
|
-
// Echo the results by district
|
|
1603
|
-
if (bLog) console.log("Reock for district", districtID, "=", reock);
|
|
1604
|
-
}
|
|
1605
|
-
}
|
|
1606
|
-
|
|
1607
|
-
// Populate the test entry ... for the shapes that exist!
|
|
1608
|
-
let averageReock = U.avgArray(scores);
|
|
1609
|
-
|
|
1610
|
-
test['score'] = U.trim(averageReock);
|
|
1611
|
-
test['details'] = {}; // TODO - Any details?
|
|
1612
|
-
|
|
1613
|
-
return test;
|
|
1614
|
-
}
|
|
1615
|
-
|
|
1616
|
-
// Calculate Polsby-Popper compactness measures:
|
|
1617
|
-
// polsby_popper = (4 * math.pi) * (a / p**2)
|
|
1618
|
-
// NOTE - Depends on extractDistrictProperties running first
|
|
1619
|
-
export function doPolsbyPopper(s: AnalyticsSession, bLog: boolean = false): T.TestEntry
|
|
1620
|
-
{
|
|
1621
|
-
let test = s.getTest(T.Test.PolsbyPopper) as T.TestEntry;
|
|
1622
|
-
|
|
1623
|
-
// Calculate Polsby-Popper scores by district
|
|
1624
|
-
let scores: number[] = [];
|
|
1625
|
-
|
|
1626
|
-
for (let districtID = 1; districtID <= s.state.nDistricts; districtID++)
|
|
1627
|
-
{
|
|
1628
|
-
let districtProps = s.districts.getGeoProperties(districtID);
|
|
1629
|
-
// Guard against no shape and no properties
|
|
1630
|
-
if (districtProps)
|
|
1631
|
-
{
|
|
1632
|
-
let a = districtProps[T.DistrictShapeProperty.Area];
|
|
1633
|
-
let p = districtProps[T.DistrictShapeProperty.Perimeter];
|
|
1634
|
-
|
|
1635
|
-
let polsbyPopper = (4 * Math.PI) * (a / p ** 2);
|
|
1636
|
-
|
|
1637
|
-
// Save each district score
|
|
1638
|
-
scores.push(polsbyPopper);
|
|
1639
|
-
|
|
1640
|
-
// Echo the results by district
|
|
1641
|
-
if (bLog) console.log("Polsby-Popper for district", districtID, "=", polsbyPopper);
|
|
1642
|
-
}
|
|
1643
|
-
}
|
|
1644
|
-
|
|
1645
|
-
// Populate the test entry ... for the shapes that exist!
|
|
1646
|
-
let averagePolsbyPopper = U.avgArray(scores);
|
|
1647
|
-
|
|
1648
|
-
test['score'] = U.trim(averagePolsbyPopper);
|
|
1649
|
-
test['details'] = {}; // TODO - Any details?
|
|
1650
|
-
|
|
1651
|
-
return test;
|
|
1652
|
-
}
|
|
1653
|
-
*/
|
|
1654
1117
|
// HELPER TO EXTRACT PROPERTIES OF DISTRICT SHAPES
|
|
1655
|
-
// TODO -
|
|
1118
|
+
// TODO - Create an array, as opposed to a dict
|
|
1656
1119
|
function extractDistrictProperties(s, bLog = false) {
|
|
1657
1120
|
// NOTE - I am assuming that district IDs are integers 1–N
|
|
1658
1121
|
for (let i = 1; i <= s.state.nDistricts; i++) {
|
|
@@ -1729,52 +1192,6 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
1729
1192
|
};
|
|
1730
1193
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1731
1194
|
const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
1732
|
-
/* TODO - DELETE
|
|
1733
|
-
export function doPopulationDeviation(s: AnalyticsSession, bLog: boolean = false): T.TestEntry
|
|
1734
|
-
{
|
|
1735
|
-
let test = s.getTest(T.Test.PopulationDeviation) as T.TestEntry;
|
|
1736
|
-
|
|
1737
|
-
let targetSize = s.state.totalPop / s.state.nDistricts;
|
|
1738
|
-
|
|
1739
|
-
// Compute the min & max district populations
|
|
1740
|
-
// ... excluding the dummy the 'unassigned' 0 and N+1 summary "districts"
|
|
1741
|
-
let totPopByDistrict = s.districts.statistics[D.DistrictField.TotalPop];
|
|
1742
|
-
totPopByDistrict = totPopByDistrict.slice(1, -1);
|
|
1743
|
-
|
|
1744
|
-
// Remove empty districts
|
|
1745
|
-
totPopByDistrict = totPopByDistrict.filter(x => x > 0);
|
|
1746
|
-
|
|
1747
|
-
let min = 0;
|
|
1748
|
-
let max = 0;
|
|
1749
|
-
|
|
1750
|
-
// If there's more than 1 non-empty district, calculate a non-zero deviation
|
|
1751
|
-
if (totPopByDistrict.length > 1)
|
|
1752
|
-
{
|
|
1753
|
-
min = U.minArray(totPopByDistrict);
|
|
1754
|
-
max = U.maxArray(totPopByDistrict);
|
|
1755
|
-
}
|
|
1756
|
-
|
|
1757
|
-
// Calculate the raw population deviation
|
|
1758
|
-
let popDev = (max - min) / targetSize;
|
|
1759
|
-
|
|
1760
|
-
// Round the raw value to the desired level of precision
|
|
1761
|
-
popDev = U.trim(popDev);
|
|
1762
|
-
|
|
1763
|
-
// Populate the test entry
|
|
1764
|
-
test['score'] = popDev;
|
|
1765
|
-
test['details'] = { 'maxDeviation': max - min };
|
|
1766
|
-
|
|
1767
|
-
// Populate the N+1 summary "district" in district.statistics
|
|
1768
|
-
let totalPop = s.districts.statistics[D.DistrictField.TotalPop];
|
|
1769
|
-
let popDevPct = s.districts.statistics[D.DistrictField.PopDevPct];
|
|
1770
|
-
let summaryRow = s.districts.numberOfRows() - 1;
|
|
1771
|
-
|
|
1772
|
-
totalPop[summaryRow] = targetSize;
|
|
1773
|
-
popDevPct[summaryRow] = popDev;
|
|
1774
|
-
|
|
1775
|
-
return test;
|
|
1776
|
-
}
|
|
1777
|
-
*/
|
|
1778
1195
|
// NOTE - This validity check is *derived* and depends on population deviation %
|
|
1779
1196
|
// being computed (above) and normalized in test log & scorecard generation.
|
|
1780
1197
|
function doHasEqualPopulations(s, bLog = false) {
|
|
@@ -1901,7 +1318,7 @@ function gfPerimeter(poly) {
|
|
|
1901
1318
|
exports.gfPerimeter = gfPerimeter;
|
|
1902
1319
|
// TODO - POLY: Confirm Cartesian calculations w/ Terry
|
|
1903
1320
|
// Cloned from polyPerimeter() in 'poly' and revised to use Cartesian distance
|
|
1904
|
-
// NOTE
|
|
1321
|
+
// NOTE - No conversion of degrees to radians!
|
|
1905
1322
|
function _polygonPerimeter(poly) {
|
|
1906
1323
|
let polyOptions = { noLatitudeCorrection: true }; // Cartesian distance
|
|
1907
1324
|
poly = Poly.polyNormalize(poly, polyOptions);
|
|
@@ -1979,112 +1396,6 @@ __export(__webpack_require__(/*! ./types */ "./src/types.ts"));
|
|
|
1979
1396
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1980
1397
|
// NOTE - This file will NOT be empty, when legacy code is deleted.
|
|
1981
1398
|
const assert_1 = __webpack_require__(/*! assert */ "assert");
|
|
1982
|
-
/* TODO - DELETE
|
|
1983
|
-
import * as D from './_data'
|
|
1984
|
-
import { AnalyticsSession } from './_api';
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
// Partisan analytics need the following data:
|
|
1988
|
-
//
|
|
1989
|
-
// An "election model" by geo_id, where each item has 4 pieces of data:
|
|
1990
|
-
//
|
|
1991
|
-
// { geo_id, Democratic votes, Republican votes, Total votes }
|
|
1992
|
-
//
|
|
1993
|
-
// NOTE: D + R <= Total, because there could be third-party or write-in votes.
|
|
1994
|
-
//
|
|
1995
|
-
// An election model can simply represent one election, e.g., President 2012,
|
|
1996
|
-
// or combine multiple elections in some fashion. An election model is used to
|
|
1997
|
-
// computer a single index of the likely partisan weight / lean / preference
|
|
1998
|
-
// for the districts in a plan. An election model and the associated index go
|
|
1999
|
-
// hand in hand. So much so that the index frequently stands in as the name for
|
|
2000
|
-
// both, e.g., Cook's PVI is one example, Nagle's 7s, Hofeller's Formula, NDRC's
|
|
2001
|
-
// DPI.
|
|
2002
|
-
//
|
|
2003
|
-
// I'm labelling this general concept a "Voter Preference Index (VPI)," a
|
|
2004
|
-
// conscious +1 letter play on Cook's "PVI" acronymn.
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
// MEASURING BIAS & RESPONSIVENESS (NAGLE'S METHOD)
|
|
2008
|
-
|
|
2009
|
-
export function doSeatsBias(s: AnalyticsSession, bLog: boolean = false): T.TestEntry
|
|
2010
|
-
{
|
|
2011
|
-
let test = s.getTest(T.Test.SeatsBias) as T.TestEntry;
|
|
2012
|
-
|
|
2013
|
-
if (bLog) console.log("TODO - Calculating seats bias ...");
|
|
2014
|
-
|
|
2015
|
-
return test;
|
|
2016
|
-
}
|
|
2017
|
-
|
|
2018
|
-
export function doVotesBias(s: AnalyticsSession, bLog: boolean = false): T.TestEntry
|
|
2019
|
-
{
|
|
2020
|
-
let test = s.getTest(T.Test.VotesBias) as T.TestEntry;
|
|
2021
|
-
|
|
2022
|
-
if (bLog) console.log("TODO - Calculating votes bias ...");
|
|
2023
|
-
|
|
2024
|
-
return test;
|
|
2025
|
-
}
|
|
2026
|
-
|
|
2027
|
-
export function doResponsiveness(s: AnalyticsSession, bLog: boolean = false): T.TestEntry
|
|
2028
|
-
{
|
|
2029
|
-
let test = s.getTest(T.Test.Responsiveness) as T.TestEntry;
|
|
2030
|
-
|
|
2031
|
-
if (bLog) console.log("TODO - Calculating responsiveness ...");
|
|
2032
|
-
|
|
2033
|
-
return test;
|
|
2034
|
-
}
|
|
2035
|
-
|
|
2036
|
-
export function doResponsiveDistricts(s: AnalyticsSession, bLog: boolean = false): T.TestEntry
|
|
2037
|
-
{
|
|
2038
|
-
let test = s.getTest(T.Test.ResponsiveDistricts) as T.TestEntry;
|
|
2039
|
-
|
|
2040
|
-
if (bLog) console.log("TODO - Calculating # of responsive districts ...");
|
|
2041
|
-
|
|
2042
|
-
return test;
|
|
2043
|
-
}
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
// OTHER MEASURES OF PARTISAN BIAS
|
|
2047
|
-
|
|
2048
|
-
// TODO - PARTISAN: This formula might need to be inverted for D vs. R +/-
|
|
2049
|
-
// TODO - Normalize the results.
|
|
2050
|
-
export function doEfficiencyGap(s: AnalyticsSession, bLog: boolean = false): T.TestEntry
|
|
2051
|
-
{
|
|
2052
|
-
if (bLog) console.log("TODO - Calculating the efficiency gap ...");
|
|
2053
|
-
|
|
2054
|
-
let test = s.getTest(T.Test.EfficiencyGap) as T.TestEntry;
|
|
2055
|
-
|
|
2056
|
-
// Get partisan statistics by districts.
|
|
2057
|
-
// Use Democratic votes, seats, and shares by convention.
|
|
2058
|
-
let DVotes = s.districts.statistics[D.DistrictField.DemVotes];
|
|
2059
|
-
let DSeats = s.districts.statistics[D.DistrictField.DemSeat];
|
|
2060
|
-
let TPVotes = s.districts.statistics[D.DistrictField.TwoPartyVote];
|
|
2061
|
-
|
|
2062
|
-
// Exclude the dummy unassigned '0' and N+1 summary "districts"
|
|
2063
|
-
DVotes = DVotes.slice(1, -1);
|
|
2064
|
-
DSeats = DSeats.slice(1, -1);
|
|
2065
|
-
TPVotes = TPVotes.slice(1, -1);
|
|
2066
|
-
|
|
2067
|
-
// Calculate D vote share & D seat share
|
|
2068
|
-
let DVoteShare = U.sumArray(DVotes) / U.sumArray(TPVotes);
|
|
2069
|
-
let DSeatShare = U.sumArray(DSeats) / s.state.nDistricts;
|
|
2070
|
-
|
|
2071
|
-
// Finally, calculate the Efficiency Gap
|
|
2072
|
-
let efficiencyGap = (DSeatShare - 0.5) - (2.0 * (DVoteShare - 0.5));
|
|
2073
|
-
|
|
2074
|
-
// Round the raw value to the desired level of precision
|
|
2075
|
-
efficiencyGap = U.trim(efficiencyGap);
|
|
2076
|
-
|
|
2077
|
-
// Populate the test entry
|
|
2078
|
-
test['score'] = efficiencyGap;
|
|
2079
|
-
// test['normalizedScore'] = 0; // TODO - Normalize the raw score
|
|
2080
|
-
test['details'] = {}; // TODO - Add details, if any
|
|
2081
|
-
|
|
2082
|
-
return test;
|
|
2083
|
-
}
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
// HELPERS
|
|
2087
|
-
*/
|
|
2088
1399
|
function fptpWin(demPct) {
|
|
2089
1400
|
// Vote shares should be fractions in the range [0.0 – 1.0]
|
|
2090
1401
|
assert_1.strict((demPct <= 1.0) && (demPct >= 0.));
|
|
@@ -2122,10 +1433,9 @@ function doPreprocessData(s, bLog = false) {
|
|
|
2122
1433
|
if (!s.bOneTimeProcessingDone) {
|
|
2123
1434
|
doPreprocessCountyFeatures(s, bLog);
|
|
2124
1435
|
doPreprocessCensus(s, bLog);
|
|
2125
|
-
doPreprocessElection(s, bLog);
|
|
1436
|
+
// doPreprocessElection(s, bLog);
|
|
2126
1437
|
s.bOneTimeProcessingDone = true;
|
|
2127
1438
|
}
|
|
2128
|
-
// NOTE - UNASSIGNED: Made both the planByGeoID & DistrictID are right
|
|
2129
1439
|
// Invert the plan by district ID
|
|
2130
1440
|
s.plan.invertPlan();
|
|
2131
1441
|
// Create a map of geoIDs to feature IDs
|
|
@@ -2207,7 +1517,6 @@ function doPreprocessCensus(s, bLog = false) {
|
|
|
2207
1517
|
// Loop over the counties
|
|
2208
1518
|
for (let county in fipsCodes) {
|
|
2209
1519
|
let fipsCode = fipsCodes[county];
|
|
2210
|
-
// NOTE - SPLITTING
|
|
2211
1520
|
// Skip the dummy county
|
|
2212
1521
|
if (fipsCode == '000')
|
|
2213
1522
|
continue;
|
|
@@ -2231,10 +1540,10 @@ function doPreprocessCensus(s, bLog = false) {
|
|
|
2231
1540
|
s.state.expectedAffected = expectedAffected;
|
|
2232
1541
|
}
|
|
2233
1542
|
// PREPROCESS ELECTION RESULTS
|
|
2234
|
-
function doPreprocessElection(s, bLog = false)
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
}
|
|
1543
|
+
// function doPreprocessElection(s: AnalyticsSession, bLog: boolean = false): void
|
|
1544
|
+
// {
|
|
1545
|
+
// if (bLog) console.log("Preprocessing election data ...");
|
|
1546
|
+
// }
|
|
2238
1547
|
|
|
2239
1548
|
|
|
2240
1549
|
/***/ }),
|
|
@@ -2267,234 +1576,6 @@ const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"
|
|
|
2267
1576
|
const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
2268
1577
|
const analyze_1 = __webpack_require__(/*! ./analyze */ "./src/analyze.ts");
|
|
2269
1578
|
const state_reqs_json_1 = __importDefault(__webpack_require__(/*! ../static/state-reqs.json */ "./static/state-reqs.json"));
|
|
2270
|
-
// PLAN ANALYTICS
|
|
2271
|
-
/* TODO - DELETE
|
|
2272
|
-
export type RequirementsCategory = {
|
|
2273
|
-
score: T.TriState;
|
|
2274
|
-
metrics: {
|
|
2275
|
-
complete: T.TriState;
|
|
2276
|
-
contiguous: T.TriState;
|
|
2277
|
-
freeOfHoles: T.TriState;
|
|
2278
|
-
equalPopulation: T.TriState;
|
|
2279
|
-
};
|
|
2280
|
-
details: {
|
|
2281
|
-
unassignedFeatures: string[]; // A possibly empty list of GEOIDs
|
|
2282
|
-
emptyDistricts: number[]; // A possibly empty list of district IDs
|
|
2283
|
-
discontiguousDistricts: number[]; // Ditto
|
|
2284
|
-
embeddedDistricts: number[]; // Ditto
|
|
2285
|
-
populationDeviation: number; // A fraction [0.0 – 1.0] to represent as a %
|
|
2286
|
-
deviationThreshold: number; // A fraction [0.0 – 1.0] to represent as a %
|
|
2287
|
-
};
|
|
2288
|
-
datasets: T.Datasets;
|
|
2289
|
-
resources: {
|
|
2290
|
-
stateReqs: string;
|
|
2291
|
-
};
|
|
2292
|
-
};
|
|
2293
|
-
*/
|
|
2294
|
-
/* TODO - DELETE
|
|
2295
|
-
export type CompactnessCategory = {
|
|
2296
|
-
score: number; // An integer score [0–100]
|
|
2297
|
-
metrics: {
|
|
2298
|
-
reock: number; // A decimal number [0.0–1.0]
|
|
2299
|
-
polsby: number; // A decimal number [0.0–1.0]
|
|
2300
|
-
};
|
|
2301
|
-
details: {
|
|
2302
|
-
// None at this time
|
|
2303
|
-
};
|
|
2304
|
-
datasets: T.Datasets;
|
|
2305
|
-
resources: {
|
|
2306
|
-
// None at this time
|
|
2307
|
-
};
|
|
2308
|
-
};
|
|
2309
|
-
*/
|
|
2310
|
-
/* TODO - DELETE
|
|
2311
|
-
export type SplittingCategory = {
|
|
2312
|
-
score: number; // An integer score [0–100]
|
|
2313
|
-
metrics: {
|
|
2314
|
-
sqEnt_DCreduced: number, // A decimal number [1.0 – < 2.0]
|
|
2315
|
-
sqEnt_CDreduced: number // A decimal number [1.0 – < 2.0]
|
|
2316
|
-
};
|
|
2317
|
-
details: {
|
|
2318
|
-
countiesSplitUnexpectedly: string[], // A possibly empty list of county names
|
|
2319
|
-
unexpectedAffected: number, // A fraction [0.0 – 1.0] to represent as a %
|
|
2320
|
-
nSplitVTDs: number, // An integer, possibly 0
|
|
2321
|
-
splitVTDs: string[] // A possibly empty list of GEOIDs
|
|
2322
|
-
};
|
|
2323
|
-
datasets: T.Datasets;
|
|
2324
|
-
resources: {
|
|
2325
|
-
// None at this time
|
|
2326
|
-
};
|
|
2327
|
-
};
|
|
2328
|
-
*/
|
|
2329
|
-
/* TODO - DELETE
|
|
2330
|
-
export type PartisanCategory = {
|
|
2331
|
-
score: number; // An integer score [0–100]
|
|
2332
|
-
metrics: {
|
|
2333
|
-
partisanBias: 0.15, // TBD
|
|
2334
|
-
responsiveness: 2.0 // TBD
|
|
2335
|
-
};
|
|
2336
|
-
details: {
|
|
2337
|
-
// TODO - Need to flesh this out
|
|
2338
|
-
};
|
|
2339
|
-
datasets: T.Datasets;
|
|
2340
|
-
resources: {
|
|
2341
|
-
planScore?: string;
|
|
2342
|
-
};
|
|
2343
|
-
};
|
|
2344
|
-
*/
|
|
2345
|
-
/* TODO - DELETE
|
|
2346
|
-
export type MinorityCategory = {
|
|
2347
|
-
score: null; // Explicitly NOT scored
|
|
2348
|
-
metrics: {
|
|
2349
|
-
nBlack37to50: number, // Integer >= 0; two-digit maximum
|
|
2350
|
-
nBlackMajority: number, // Ditto
|
|
2351
|
-
nHispanic37to50: number, // Ditto
|
|
2352
|
-
nHispanicMajority: number, // Ditto
|
|
2353
|
-
nPacific37to50: number, // Ditto
|
|
2354
|
-
nPacificMajority: number, // Ditto
|
|
2355
|
-
nAsian37to50: number, // Ditto
|
|
2356
|
-
nAsianMajority: number, // Ditto
|
|
2357
|
-
nNative37to50: number, // Ditto
|
|
2358
|
-
nNativeMajority: number, // Ditto
|
|
2359
|
-
nMinority37to50: number, // Ditto
|
|
2360
|
-
nMinorityMajority: number, // Ditto
|
|
2361
|
-
|
|
2362
|
-
averageDVoteShare: number // A fraction [0.0 – 1.0] to represent as a %
|
|
2363
|
-
};
|
|
2364
|
-
details: {
|
|
2365
|
-
vap: true, // true = using VAP data; false = CVAP data
|
|
2366
|
-
comboCategories: true // true = using combo fields; false = mutually exclusive
|
|
2367
|
-
};
|
|
2368
|
-
datasets: T.Datasets;
|
|
2369
|
-
resources: {
|
|
2370
|
-
// TODO - Add these ...
|
|
2371
|
-
};
|
|
2372
|
-
};
|
|
2373
|
-
*/
|
|
2374
|
-
/* TODO - DELETE
|
|
2375
|
-
export type PlanAnalytics = {
|
|
2376
|
-
requirements: RequirementsCategory;
|
|
2377
|
-
compactness: CompactnessCategory;
|
|
2378
|
-
// TODO - Don't show these categories yet
|
|
2379
|
-
splitting: SplittingCategory;
|
|
2380
|
-
partisan: PartisanCategory;
|
|
2381
|
-
minority: MinorityCategory;
|
|
2382
|
-
}
|
|
2383
|
-
*/
|
|
2384
|
-
// EXAMPLE
|
|
2385
|
-
/* TODO - DELETE
|
|
2386
|
-
let sampleRequirements: RequirementsCategory = {
|
|
2387
|
-
score: T.TriState.Red,
|
|
2388
|
-
metrics: {
|
|
2389
|
-
complete: T.TriState.Green,
|
|
2390
|
-
contiguous: T.TriState.Red,
|
|
2391
|
-
freeOfHoles: T.TriState.Yellow,
|
|
2392
|
-
equalPopulation: T.TriState.Red
|
|
2393
|
-
},
|
|
2394
|
-
details: {
|
|
2395
|
-
unassignedFeatures: [],
|
|
2396
|
-
emptyDistricts: [],
|
|
2397
|
-
discontiguousDistricts: [2],
|
|
2398
|
-
embeddedDistricts: [],
|
|
2399
|
-
populationDeviation: 0.6748,
|
|
2400
|
-
deviationThreshold: 0.75 / 100
|
|
2401
|
-
},
|
|
2402
|
-
datasets: {
|
|
2403
|
-
census: "2010 Census Total Population"
|
|
2404
|
-
},
|
|
2405
|
-
resources: {
|
|
2406
|
-
stateReqs: "https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_20.pdf"
|
|
2407
|
-
}
|
|
2408
|
-
}
|
|
2409
|
-
*/
|
|
2410
|
-
/* TODO - DELETE
|
|
2411
|
-
let sampleCompactness: CompactnessCategory = {
|
|
2412
|
-
score: 60,
|
|
2413
|
-
metrics: {
|
|
2414
|
-
reock: 0.3773,
|
|
2415
|
-
polsby: 0.3815
|
|
2416
|
-
},
|
|
2417
|
-
details: {},
|
|
2418
|
-
datasets: {
|
|
2419
|
-
shapes: "2010 VTD shapes"
|
|
2420
|
-
},
|
|
2421
|
-
resources: {}
|
|
2422
|
-
}
|
|
2423
|
-
*/
|
|
2424
|
-
/* TODO - DELETE
|
|
2425
|
-
let sampleSplitting: SplittingCategory = {
|
|
2426
|
-
score: 73,
|
|
2427
|
-
metrics: {
|
|
2428
|
-
sqEnt_DCreduced: 1.531,
|
|
2429
|
-
sqEnt_CDreduced: 1.760
|
|
2430
|
-
},
|
|
2431
|
-
details: {
|
|
2432
|
-
countiesSplitUnexpectedly: [
|
|
2433
|
-
"Bladen", "Buncombe", "Catawba", "Cumberland", "Durham", "Guilford", "Iredell", "Johnston", "Pitt", "Rowan", "Wilson"
|
|
2434
|
-
],
|
|
2435
|
-
unexpectedAffected: 0.3096,
|
|
2436
|
-
nSplitVTDs: 12,
|
|
2437
|
-
splitVTDs: ["VTD-01", "VTD-02", "VTD-03", "VTD-04", "VTD-05", "VTD-06", "VTD-07", "VTD-08", "VTD-09", "VTD-10", "VTD-11", "VTD-12"]
|
|
2438
|
-
},
|
|
2439
|
-
datasets: {},
|
|
2440
|
-
resources: {}
|
|
2441
|
-
}
|
|
2442
|
-
*/
|
|
2443
|
-
/* TODO - DELETE
|
|
2444
|
-
let samplePartisan: PartisanCategory = {
|
|
2445
|
-
score: 100,
|
|
2446
|
-
metrics: {
|
|
2447
|
-
partisanBias: 0.15,
|
|
2448
|
-
responsiveness: 2.0
|
|
2449
|
-
},
|
|
2450
|
-
details: {},
|
|
2451
|
-
datasets: {
|
|
2452
|
-
election: "2016 Presidential, US Senate, Governor, and AG election results"
|
|
2453
|
-
},
|
|
2454
|
-
resources: {
|
|
2455
|
-
planScore: "https://planscore.org/plan.html?20180219T202039.596761160Z"
|
|
2456
|
-
}
|
|
2457
|
-
}
|
|
2458
|
-
*/
|
|
2459
|
-
/* TODO - DELETE
|
|
2460
|
-
let sampleMinority: MinorityCategory = {
|
|
2461
|
-
score: null,
|
|
2462
|
-
metrics: {
|
|
2463
|
-
nBlack37to50: 1,
|
|
2464
|
-
nBlackMajority: 12,
|
|
2465
|
-
nHispanic37to50: 0,
|
|
2466
|
-
nHispanicMajority: 0,
|
|
2467
|
-
nPacific37to50: 0,
|
|
2468
|
-
nPacificMajority: 0,
|
|
2469
|
-
nAsian37to50: 0,
|
|
2470
|
-
nAsianMajority: 0,
|
|
2471
|
-
nNative37to50: 0,
|
|
2472
|
-
nNativeMajority: 0,
|
|
2473
|
-
nMinority37to50: 0,
|
|
2474
|
-
nMinorityMajority: 0,
|
|
2475
|
-
|
|
2476
|
-
averageDVoteShare: 0.90
|
|
2477
|
-
},
|
|
2478
|
-
details: {
|
|
2479
|
-
vap: true,
|
|
2480
|
-
comboCategories: true
|
|
2481
|
-
},
|
|
2482
|
-
datasets: {
|
|
2483
|
-
vap: "2010 Voting Age Population"
|
|
2484
|
-
},
|
|
2485
|
-
resources: {}
|
|
2486
|
-
}
|
|
2487
|
-
*/
|
|
2488
|
-
/* TODO - DELETE
|
|
2489
|
-
export const samplePlanAnalytics: PlanAnalytics = {
|
|
2490
|
-
requirements: sampleRequirements,
|
|
2491
|
-
compactness: sampleCompactness,
|
|
2492
|
-
// TODO - Don't show these categories yet
|
|
2493
|
-
splitting: sampleSplitting,
|
|
2494
|
-
partisan: samplePartisan,
|
|
2495
|
-
minority: sampleMinority
|
|
2496
|
-
}
|
|
2497
|
-
*/
|
|
2498
1579
|
function prepareRequirementsChecklist(s, bLog = false) {
|
|
2499
1580
|
if (!(s.bPostProcessingDone)) {
|
|
2500
1581
|
doAnalyzePostProcessing(s);
|
|
@@ -2524,14 +1605,8 @@ function prepareRequirementsChecklist(s, bLog = false) {
|
|
|
2524
1605
|
const emptyDistrictsDetail = U.deepCopy(completeTest['details']['emptyDistricts']) || [];
|
|
2525
1606
|
const discontiguousDistrictsDetail = U.deepCopy(contiguousTest['details']['discontiguousDistricts']) || [];
|
|
2526
1607
|
const embeddedDistrictsDetail = U.deepCopy(freeOfHolesTest['details']['embeddedDistricts']) || [];
|
|
2527
|
-
// TODO - SCORE: DELETE - This code is hooked correctly to use dra-score
|
|
2528
|
-
// const scorecard = s._scorecard as Score.Scorecard;
|
|
2529
|
-
// const populationDeviation = scorecard.best.populationDeviation.raw;
|
|
2530
1608
|
const populationDeviationDetail = U.deepCopy(equalPopulationTest['details']['deviation']);
|
|
2531
|
-
// console.log("Population deviations =", populationDeviationDetail, populationDeviation);
|
|
2532
|
-
// const deviationThreshold = scorecard.best.populationDeviation.notes['threshold'];
|
|
2533
1609
|
const deviationThresholdDetail = U.trim(s.populationDeviationThreshold());
|
|
2534
|
-
// console.log("Population deviation thresholds =", deviationThresholdDetail, deviationThreshold);
|
|
2535
1610
|
const xx = s.state.xx;
|
|
2536
1611
|
// TODO - JSON: Is there a better / easier way to work with the variable?
|
|
2537
1612
|
const stateReqsDict = state_reqs_json_1.default;
|
|
@@ -2614,8 +1689,8 @@ function prepareDistrictStatistics(s, bLog = false) {
|
|
|
2614
1689
|
s.districts.statistics[D.DistrictField.AsianPct][i],
|
|
2615
1690
|
s.districts.statistics[D.DistrictField.NativePct][i]
|
|
2616
1691
|
];
|
|
2617
|
-
//
|
|
2618
|
-
//
|
|
1692
|
+
// NOTE - Until we add three-state support top to bottom in Requirements,
|
|
1693
|
+
// map booleans to tri-states here.
|
|
2619
1694
|
rawRow[3 /* bEqualPop */] = U.mapBooleanToTriState(rawRow[3 /* bEqualPop */]);
|
|
2620
1695
|
rawRow[4 /* bNotEmpty */] = U.mapBooleanToTriState(rawRow[4 /* bNotEmpty */]);
|
|
2621
1696
|
rawRow[5 /* bContiguous */] = U.mapBooleanToTriState(rawRow[5 /* bContiguous */]);
|
|
@@ -2690,24 +1765,6 @@ const populationDeviationDefn = {
|
|
|
2690
1765
|
externalType: TestType.Percentage,
|
|
2691
1766
|
suites: [0 /* Legal */, 2 /* Best */] // Both so EqualPopulation can be assessed
|
|
2692
1767
|
};
|
|
2693
|
-
/* TODO - DELETE
|
|
2694
|
-
const reockDefn: T.Dict = {
|
|
2695
|
-
ID: T.Test.Reock,
|
|
2696
|
-
name: "Reock",
|
|
2697
|
-
normalize: true,
|
|
2698
|
-
externalType: TestType.Number,
|
|
2699
|
-
suites: [T.Suite.Best]
|
|
2700
|
-
};
|
|
2701
|
-
|
|
2702
|
-
const polsbyPopperDefn: T.Dict = {
|
|
2703
|
-
ID: T.Test.PolsbyPopper,
|
|
2704
|
-
name: "Polsby-Popper",
|
|
2705
|
-
normalize: true,
|
|
2706
|
-
externalType: TestType.Number,
|
|
2707
|
-
suites: [T.Suite.Best]
|
|
2708
|
-
};
|
|
2709
|
-
*/
|
|
2710
|
-
// NOTE - SPLITTING
|
|
2711
1768
|
const unexpectedCountySplitsDefn = {
|
|
2712
1769
|
ID: 5 /* UnexpectedCountySplits */,
|
|
2713
1770
|
name: "Unexpected County Splits",
|
|
@@ -2715,7 +1772,6 @@ const unexpectedCountySplitsDefn = {
|
|
|
2715
1772
|
externalType: TestType.Percentage,
|
|
2716
1773
|
suites: [2 /* Best */]
|
|
2717
1774
|
};
|
|
2718
|
-
// NOTE - SPLITTING
|
|
2719
1775
|
const VTDSplitsDefn = {
|
|
2720
1776
|
ID: 6 /* VTDSplits */,
|
|
2721
1777
|
name: "VTD Splits",
|
|
@@ -2730,87 +1786,16 @@ const testDefns = {
|
|
|
2730
1786
|
[2 /* FreeOfHoles */]: freeOfHolesDefn,
|
|
2731
1787
|
[3 /* EqualPopulation */]: equalPopulationDefn,
|
|
2732
1788
|
[4 /* PopulationDeviation */]: populationDeviationDefn,
|
|
2733
|
-
// [T.Test.Reock]: reockDefn, TODO - DELETE
|
|
2734
|
-
// [T.Test.PolsbyPopper]: polsbyPopperDefn, TODO - DELETE
|
|
2735
1789
|
[5 /* UnexpectedCountySplits */]: unexpectedCountySplitsDefn,
|
|
2736
1790
|
[6 /* VTDSplits */]: VTDSplitsDefn,
|
|
2737
1791
|
};
|
|
2738
|
-
/* TODO - DELETE
|
|
2739
|
-
// NORMALIZE RAW ANALYTICS
|
|
2740
|
-
// Raw numeric analytics, such as population deviation, compactness, etc. are
|
|
2741
|
-
// normalized as part of creating a scorecard, so the code to normalize results
|
|
2742
|
-
// is encapsulated here.
|
|
2743
|
-
|
|
2744
|
-
// Configure scale parameters for normalizing each raw test result.
|
|
2745
|
-
// Scales consist of a minimum & a maximum *raw* value. If the values get
|
|
2746
|
-
// inverted (to make bigger better), these will switch.
|
|
2747
|
-
// This process needs to be separate from the test configuration info above,
|
|
2748
|
-
// because some scales need access to the analytics session object.
|
|
2749
|
-
export function doConfigureScales(s: AnalyticsSession): void
|
|
2750
|
-
{
|
|
2751
|
-
// Scale defn for PopulationDeviation
|
|
2752
|
-
const CDLimit = 0.75 / 100; // Deviation threshold for CD's
|
|
2753
|
-
const LDLimit = 10.00 / 100; // Deviation threshold for LD's
|
|
2754
|
-
|
|
2755
|
-
const CDGoodEnough = 0.20 / 100;
|
|
2756
|
-
const LDGoodEnough = (CDGoodEnough / CDLimit) * LDLimit;
|
|
2757
|
-
const popDevScale = (s.legislativeDistricts) ? [1.0 - LDLimit, 1.0 - LDGoodEnough] : [1.0 - CDLimit, 1.0 - CDGoodEnough];
|
|
2758
|
-
// const scale = [1.0 - CDLimit, 1.0 - CDGoodEnough];
|
|
2759
|
-
|
|
2760
|
-
s.testScales[T.Test.PopulationDeviation] = { scale: popDevScale, bInvertRaw: true };
|
|
2761
|
-
|
|
2762
|
-
s.testScales[T.Test.Reock] = { scale: [0.25, 0.50] };
|
|
2763
|
-
s.testScales[T.Test.PolsbyPopper] = { scale: [0.10, 0.50] };
|
|
2764
|
-
|
|
2765
|
-
const nDistricts = s.state.nDistricts;
|
|
2766
|
-
const nCounties = s.counties.nCounties;
|
|
2767
|
-
|
|
2768
|
-
// NOTE - SPLITTING: Experiment w/ this multiplier. Only allowing the expected
|
|
2769
|
-
// number of county splits seems too stringent, empirically.
|
|
2770
|
-
const allowableCountySplitsMultiplier = 1.5;
|
|
2771
|
-
const nAllowableSplits = Math.min(allowableCountySplitsMultiplier * (nDistricts - 1));
|
|
2772
|
-
const countySplittingThreshold = ((nAllowableSplits * 1.71) + ((nCounties - nAllowableSplits) * 1.0)) / nCounties;
|
|
2773
|
-
const countySplittingScale = [1.0, countySplittingThreshold];
|
|
2774
|
-
s.testScales[T.Test.CountySplitting] = { scale: countySplittingScale, bInvertScaled: true };
|
|
2775
|
-
|
|
2776
|
-
const districtSplittingThreshold = 1.5;
|
|
2777
|
-
const districtSplittingScale = [1.0, districtSplittingThreshold];
|
|
2778
|
-
s.testScales[T.Test.DistrictSplitting] = { scale: districtSplittingScale, bInvertScaled: true };
|
|
2779
|
-
|
|
2780
|
-
// TODO - More analytics ...
|
|
2781
|
-
}
|
|
2782
|
-
*/
|
|
2783
1792
|
// Postprocess analytics - Normalize numeric results and derive secondary tests.
|
|
2784
1793
|
// Do this after analytics have been run and before preparing a test log or scorecard.
|
|
2785
1794
|
function doAnalyzePostProcessing(s, bLog = false) {
|
|
2786
|
-
// TODO - DELETE
|
|
2787
|
-
// if (s.useLegacy())
|
|
2788
|
-
// {
|
|
2789
|
-
// // Normalize the raw scores for all the numerics tests
|
|
2790
|
-
// let testResults = U.getNumericObjectKeys(testDefns);
|
|
2791
|
-
// for (let testID of testResults)
|
|
2792
|
-
// {
|
|
2793
|
-
// if (testDefns[testID]['normalize'])
|
|
2794
|
-
// {
|
|
2795
|
-
// let testResult = s.getTest(testID) as T.TestEntry;
|
|
2796
|
-
// let rawScore = testResult['score'] as number;
|
|
2797
|
-
// let normalizedScore: number;
|
|
2798
|
-
// normalizedScore = U.normalize(rawScore, s.testScales[testID]);
|
|
2799
|
-
// testResult['normalizedScore'] = normalizedScore;
|
|
2800
|
-
// // Add the scale used to normalize the raw score to the details
|
|
2801
|
-
// testResult['details']['scale'] = s.testScales[testID].scale;
|
|
2802
|
-
// }
|
|
2803
|
-
// }
|
|
2804
|
-
// }
|
|
2805
|
-
// else
|
|
2806
|
-
// {
|
|
2807
1795
|
// Just populate the normalized population deviation score in the test
|
|
2808
1796
|
const scorecard = s._scorecard;
|
|
2809
1797
|
let popDev = s.getTest(4 /* PopulationDeviation */);
|
|
2810
1798
|
popDev['normalizedScore'] = scorecard.traditionalPrinciples.populationDeviation.normalized;
|
|
2811
|
-
// TODO - DELETE
|
|
2812
|
-
// test['normalizedScore'] = scorecard.best.populationDeviation.normalized;
|
|
2813
|
-
// TODO - SCORE: Add datasets used to details by tab
|
|
2814
1799
|
const datasets = {
|
|
2815
1800
|
shapes: S.SHAPES,
|
|
2816
1801
|
census: U.deepCopy(s.config['descriptions']['CENSUS']),
|
|
@@ -2821,12 +1806,10 @@ function doAnalyzePostProcessing(s, bLog = false) {
|
|
|
2821
1806
|
scorecard.minority.details['vap'] = datasets.vap;
|
|
2822
1807
|
scorecard.traditionalPrinciples.details['shapes'] = datasets.shapes;
|
|
2823
1808
|
scorecard.traditionalPrinciples.details['census'] = datasets.census;
|
|
2824
|
-
// TODO - SCORE: Add legacy splits details
|
|
2825
1809
|
const simpleSplits = s.getTest(5 /* UnexpectedCountySplits */);
|
|
2826
1810
|
scorecard.traditionalPrinciples.details['unexpectedAffected'] = simpleSplits['score'];
|
|
2827
1811
|
scorecard.traditionalPrinciples.details['countiesSplitUnexpectedly'] = U.deepCopy(simpleSplits['details']['countiesSplitUnexpectedly']);
|
|
2828
1812
|
// NOTE - Add split precincts in dra-client directly
|
|
2829
|
-
// }
|
|
2830
1813
|
// Derive secondary tests
|
|
2831
1814
|
analyze_1.doDeriveSecondaryTests(s, bLog);
|
|
2832
1815
|
// Toggle the semaphore, so postprocessing isn't for both the testlog & scorecard
|
|
@@ -2965,19 +1948,14 @@ function getStatewideDemographics(s, bLog = false) {
|
|
|
2965
1948
|
function scorePlan(s, p, bLog = false, overridesJSON) {
|
|
2966
1949
|
let scorer = new Score.Scorer();
|
|
2967
1950
|
const scorecard = scorer.score(p, overridesJSON);
|
|
2968
|
-
//
|
|
2969
|
-
//
|
|
2970
|
-
// calling sequence.
|
|
1951
|
+
// Before returning, create a dummy population deviation test, for
|
|
1952
|
+
// doHasEqualPopulations() to use later.This is preserving the old calling sequence.
|
|
2971
1953
|
let test = s.getTest(4 /* PopulationDeviation */);
|
|
2972
|
-
// TODO - SCORE: U.trim(popDev)???
|
|
2973
|
-
// const popDev = scorecard.best.populationDeviation.raw;
|
|
2974
1954
|
// Get the raw population deviation
|
|
2975
1955
|
const popDev = scorecard.traditionalPrinciples.populationDeviation.raw;
|
|
2976
1956
|
// Populate the test entry
|
|
2977
1957
|
test['score'] = popDev;
|
|
2978
1958
|
test['details'] = { 'maxDeviation': scorecard.traditionalPrinciples.populationDeviation.notes['maxDeviation'] };
|
|
2979
|
-
// TODO - DELETE
|
|
2980
|
-
// test['details'] = { 'maxDeviation': scorecard.best.populationDeviation.notes['maxDeviation'] };
|
|
2981
1959
|
// Populate the N+1 summary "district" in district.statistics
|
|
2982
1960
|
let totalPop = s.districts.statistics[D.DistrictField.TotalPop];
|
|
2983
1961
|
let popDevPct = s.districts.statistics[D.DistrictField.PopDevPct];
|
|
@@ -3042,7 +2020,6 @@ exports.SHAPES = "2010 VTD shapes";
|
|
|
3042
2020
|
// TYPE DEFINITIONS
|
|
3043
2021
|
//
|
|
3044
2022
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3045
|
-
// TODO - SCORE
|
|
3046
2023
|
var dra_score_1 = __webpack_require__(/*! @dra2020/dra-score */ "@dra2020/dra-score");
|
|
3047
2024
|
exports.sampleProfile = dra_score_1.sampleProfile;
|
|
3048
2025
|
exports.sampleScorecard = dra_score_1.sampleScorecard;
|
|
@@ -3084,7 +2061,6 @@ function isOutOfState(geoID) {
|
|
|
3084
2061
|
return geoID == S.OUT_OF_STATE;
|
|
3085
2062
|
}
|
|
3086
2063
|
exports.isOutOfState = isOutOfState;
|
|
3087
|
-
// NOTE - UNASSIGNED
|
|
3088
2064
|
// Get the districtID to which a geoID is assigned
|
|
3089
2065
|
function getDistrict(plan, geoID) {
|
|
3090
2066
|
// All geoIDs in a state *should be* assigned to a district (including the
|
|
@@ -3139,7 +2115,6 @@ function normalize(rawScore, testScale) {
|
|
|
3139
2115
|
let coercedValue = Math.min(Math.max(rawScore, rangeMin), rangeMax);
|
|
3140
2116
|
// Scale the bounded value w/in the range [0 - (rangeMax - rangeMin)]
|
|
3141
2117
|
let scaledValue = (coercedValue - rangeMin) / (rangeMax - rangeMin);
|
|
3142
|
-
// NOTE - SPLITTING
|
|
3143
2118
|
// Invert the scaled value if necessary to make bigger = better
|
|
3144
2119
|
if (testScale.bInvertScaled) {
|
|
3145
2120
|
scaledValue = 1.0 - scaledValue;
|