@dra2020/district-analytics 4.3.6 → 5.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
- // TODO - SCORE: Delete
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
- // TODO - OPTIMIZE for getting multiple properties from the same feature?
442
- // TODO - OPTIMIZE by only re-calc'ing districts that have changed?
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
- // TODO - OPTIMIZE by async'ing this?
445
- // TODO - Is there a way to do this programmatically off data? Does it matter?
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
- // TODO - RECALC: Does anything need to be recalc'd now when a dataset is changed?
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 - SCORE: Create an array, as opposed to a dict
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: No conversion of degrees to radians!
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);
@@ -1962,6 +1379,49 @@ __export(__webpack_require__(/*! ./results */ "./src/results.ts"));
1962
1379
  __export(__webpack_require__(/*! ./types */ "./src/types.ts"));
1963
1380
 
1964
1381
 
1382
+ /***/ }),
1383
+
1384
+ /***/ "./src/minority.ts":
1385
+ /*!*************************!*\
1386
+ !*** ./src/minority.ts ***!
1387
+ \*************************/
1388
+ /*! no static exports found */
1389
+ /***/ (function(module, exports, __webpack_require__) {
1390
+
1391
+ "use strict";
1392
+
1393
+ //
1394
+ // PROTECTS MINORITIES
1395
+ //
1396
+ var __importDefault = (this && this.__importDefault) || function (mod) {
1397
+ return (mod && mod.__esModule) ? mod : { "default": mod };
1398
+ };
1399
+ Object.defineProperty(exports, "__esModule", { value: true });
1400
+ const majority_minority_json_1 = __importDefault(__webpack_require__(/*! ../static/majority-minority.json */ "./static/majority-minority.json"));
1401
+ const vra5_preclearance_json_1 = __importDefault(__webpack_require__(/*! ../static/vra5-preclearance.json */ "./static/vra5-preclearance.json"));
1402
+ // import stateContacts from '../static/state-contacts.json';
1403
+ // import demographicDefs from '../static/demographic-defns.json';
1404
+ // TODO - 2020: Update/revise this, when the update comes out in September:
1405
+ // Sources for majority-minority info:
1406
+ // - https://en.wikipedia.org/wiki/List_of_majority-minority_United_States_congressional_districts
1407
+ // - http://www.ncsl.org/Portals/1/Documents/Redistricting/Redistricting_2010.pdf (PP. 80–84)
1408
+ // - https://www.justice.gov/crt/jurisdictions-previously-covered-section-5
1409
+ function getMajorityMinority(s) {
1410
+ const xx = s.state.xx;
1411
+ const mMDict = majority_minority_json_1.default;
1412
+ const stateMM = mMDict[xx];
1413
+ return stateMM;
1414
+ }
1415
+ exports.getMajorityMinority = getMajorityMinority;
1416
+ function getVRASection5(s) {
1417
+ const xx = s.state.xx;
1418
+ const vraPreDict = vra5_preclearance_json_1.default;
1419
+ const stateVRAPre = vraPreDict[xx];
1420
+ return stateVRAPre;
1421
+ }
1422
+ exports.getVRASection5 = getVRASection5;
1423
+
1424
+
1965
1425
  /***/ }),
1966
1426
 
1967
1427
  /***/ "./src/political.ts":
@@ -1979,112 +1439,6 @@ __export(__webpack_require__(/*! ./types */ "./src/types.ts"));
1979
1439
  Object.defineProperty(exports, "__esModule", { value: true });
1980
1440
  // NOTE - This file will NOT be empty, when legacy code is deleted.
1981
1441
  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
1442
  function fptpWin(demPct) {
2089
1443
  // Vote shares should be fractions in the range [0.0 – 1.0]
2090
1444
  assert_1.strict((demPct <= 1.0) && (demPct >= 0.));
@@ -2122,10 +1476,9 @@ function doPreprocessData(s, bLog = false) {
2122
1476
  if (!s.bOneTimeProcessingDone) {
2123
1477
  doPreprocessCountyFeatures(s, bLog);
2124
1478
  doPreprocessCensus(s, bLog);
2125
- doPreprocessElection(s, bLog);
1479
+ // doPreprocessElection(s, bLog);
2126
1480
  s.bOneTimeProcessingDone = true;
2127
1481
  }
2128
- // NOTE - UNASSIGNED: Made both the planByGeoID & DistrictID are right
2129
1482
  // Invert the plan by district ID
2130
1483
  s.plan.invertPlan();
2131
1484
  // Create a map of geoIDs to feature IDs
@@ -2207,7 +1560,6 @@ function doPreprocessCensus(s, bLog = false) {
2207
1560
  // Loop over the counties
2208
1561
  for (let county in fipsCodes) {
2209
1562
  let fipsCode = fipsCodes[county];
2210
- // NOTE - SPLITTING
2211
1563
  // Skip the dummy county
2212
1564
  if (fipsCode == '000')
2213
1565
  continue;
@@ -2231,10 +1583,10 @@ function doPreprocessCensus(s, bLog = false) {
2231
1583
  s.state.expectedAffected = expectedAffected;
2232
1584
  }
2233
1585
  // PREPROCESS ELECTION RESULTS
2234
- function doPreprocessElection(s, bLog = false) {
2235
- if (bLog)
2236
- console.log("TODO - Preprocessing election data ...");
2237
- }
1586
+ // function doPreprocessElection(s: AnalyticsSession, bLog: boolean = false): void
1587
+ // {
1588
+ // if (bLog) console.log("Preprocessing election data ...");
1589
+ // }
2238
1590
 
2239
1591
 
2240
1592
  /***/ }),
@@ -2267,234 +1619,6 @@ const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"
2267
1619
  const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
2268
1620
  const analyze_1 = __webpack_require__(/*! ./analyze */ "./src/analyze.ts");
2269
1621
  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
1622
  function prepareRequirementsChecklist(s, bLog = false) {
2499
1623
  if (!(s.bPostProcessingDone)) {
2500
1624
  doAnalyzePostProcessing(s);
@@ -2524,14 +1648,8 @@ function prepareRequirementsChecklist(s, bLog = false) {
2524
1648
  const emptyDistrictsDetail = U.deepCopy(completeTest['details']['emptyDistricts']) || [];
2525
1649
  const discontiguousDistrictsDetail = U.deepCopy(contiguousTest['details']['discontiguousDistricts']) || [];
2526
1650
  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
1651
  const populationDeviationDetail = U.deepCopy(equalPopulationTest['details']['deviation']);
2531
- // console.log("Population deviations =", populationDeviationDetail, populationDeviation);
2532
- // const deviationThreshold = scorecard.best.populationDeviation.notes['threshold'];
2533
1652
  const deviationThresholdDetail = U.trim(s.populationDeviationThreshold());
2534
- // console.log("Population deviation thresholds =", deviationThresholdDetail, deviationThreshold);
2535
1653
  const xx = s.state.xx;
2536
1654
  // TODO - JSON: Is there a better / easier way to work with the variable?
2537
1655
  const stateReqsDict = state_reqs_json_1.default;
@@ -2614,8 +1732,8 @@ function prepareDistrictStatistics(s, bLog = false) {
2614
1732
  s.districts.statistics[D.DistrictField.AsianPct][i],
2615
1733
  s.districts.statistics[D.DistrictField.NativePct][i]
2616
1734
  ];
2617
- // TODO - DASHBOARD: Until we add three-state support top to bottom in
2618
- // requirements/validations, map booleans to tri-states here.
1735
+ // NOTE - Until we add three-state support top to bottom in Requirements,
1736
+ // map booleans to tri-states here.
2619
1737
  rawRow[3 /* bEqualPop */] = U.mapBooleanToTriState(rawRow[3 /* bEqualPop */]);
2620
1738
  rawRow[4 /* bNotEmpty */] = U.mapBooleanToTriState(rawRow[4 /* bNotEmpty */]);
2621
1739
  rawRow[5 /* bContiguous */] = U.mapBooleanToTriState(rawRow[5 /* bContiguous */]);
@@ -2690,24 +1808,6 @@ const populationDeviationDefn = {
2690
1808
  externalType: TestType.Percentage,
2691
1809
  suites: [0 /* Legal */, 2 /* Best */] // Both so EqualPopulation can be assessed
2692
1810
  };
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
1811
  const unexpectedCountySplitsDefn = {
2712
1812
  ID: 5 /* UnexpectedCountySplits */,
2713
1813
  name: "Unexpected County Splits",
@@ -2715,7 +1815,6 @@ const unexpectedCountySplitsDefn = {
2715
1815
  externalType: TestType.Percentage,
2716
1816
  suites: [2 /* Best */]
2717
1817
  };
2718
- // NOTE - SPLITTING
2719
1818
  const VTDSplitsDefn = {
2720
1819
  ID: 6 /* VTDSplits */,
2721
1820
  name: "VTD Splits",
@@ -2730,87 +1829,16 @@ const testDefns = {
2730
1829
  [2 /* FreeOfHoles */]: freeOfHolesDefn,
2731
1830
  [3 /* EqualPopulation */]: equalPopulationDefn,
2732
1831
  [4 /* PopulationDeviation */]: populationDeviationDefn,
2733
- // [T.Test.Reock]: reockDefn, TODO - DELETE
2734
- // [T.Test.PolsbyPopper]: polsbyPopperDefn, TODO - DELETE
2735
1832
  [5 /* UnexpectedCountySplits */]: unexpectedCountySplitsDefn,
2736
1833
  [6 /* VTDSplits */]: VTDSplitsDefn,
2737
1834
  };
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
1835
  // Postprocess analytics - Normalize numeric results and derive secondary tests.
2784
1836
  // Do this after analytics have been run and before preparing a test log or scorecard.
2785
1837
  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
1838
  // Just populate the normalized population deviation score in the test
2808
1839
  const scorecard = s._scorecard;
2809
1840
  let popDev = s.getTest(4 /* PopulationDeviation */);
2810
1841
  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
1842
  const datasets = {
2815
1843
  shapes: S.SHAPES,
2816
1844
  census: U.deepCopy(s.config['descriptions']['CENSUS']),
@@ -2821,12 +1849,10 @@ function doAnalyzePostProcessing(s, bLog = false) {
2821
1849
  scorecard.minority.details['vap'] = datasets.vap;
2822
1850
  scorecard.traditionalPrinciples.details['shapes'] = datasets.shapes;
2823
1851
  scorecard.traditionalPrinciples.details['census'] = datasets.census;
2824
- // TODO - SCORE: Add legacy splits details
2825
1852
  const simpleSplits = s.getTest(5 /* UnexpectedCountySplits */);
2826
1853
  scorecard.traditionalPrinciples.details['unexpectedAffected'] = simpleSplits['score'];
2827
1854
  scorecard.traditionalPrinciples.details['countiesSplitUnexpectedly'] = U.deepCopy(simpleSplits['details']['countiesSplitUnexpectedly']);
2828
1855
  // NOTE - Add split precincts in dra-client directly
2829
- // }
2830
1856
  // Derive secondary tests
2831
1857
  analyze_1.doDeriveSecondaryTests(s, bLog);
2832
1858
  // Toggle the semaphore, so postprocessing isn't for both the testlog & scorecard
@@ -2860,6 +1886,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
2860
1886
  const Score = __importStar(__webpack_require__(/*! @dra2020/dra-score */ "@dra2020/dra-score"));
2861
1887
  const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
2862
1888
  const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
1889
+ const M = __importStar(__webpack_require__(/*! ./minority */ "./src/minority.ts"));
2863
1890
  // PROFILE A PLAN
2864
1891
  function profilePlan(s, bLog = false) {
2865
1892
  const state = s.state.xx;
@@ -2965,26 +1992,23 @@ function getStatewideDemographics(s, bLog = false) {
2965
1992
  function scorePlan(s, p, bLog = false, overridesJSON) {
2966
1993
  let scorer = new Score.Scorer();
2967
1994
  const scorecard = scorer.score(p, overridesJSON);
2968
- // TODO - SCORE: Toggle: Before returning, create a dummy population deviation
2969
- // test, for doHasEqualPopulations() to use later. This is preserving the old
2970
- // calling sequence.
1995
+ // Before returning, create a dummy population deviation test, for
1996
+ // doHasEqualPopulations() to use later.This is preserving the old calling sequence.
2971
1997
  let test = s.getTest(4 /* PopulationDeviation */);
2972
- // TODO - SCORE: U.trim(popDev)???
2973
- // const popDev = scorecard.best.populationDeviation.raw;
2974
1998
  // Get the raw population deviation
2975
1999
  const popDev = scorecard.traditionalPrinciples.populationDeviation.raw;
2976
2000
  // Populate the test entry
2977
2001
  test['score'] = popDev;
2978
2002
  test['details'] = { 'maxDeviation': scorecard.traditionalPrinciples.populationDeviation.notes['maxDeviation'] };
2979
- // TODO - DELETE
2980
- // test['details'] = { 'maxDeviation': scorecard.best.populationDeviation.notes['maxDeviation'] };
2981
2003
  // Populate the N+1 summary "district" in district.statistics
2982
2004
  let totalPop = s.districts.statistics[D.DistrictField.TotalPop];
2983
2005
  let popDevPct = s.districts.statistics[D.DistrictField.PopDevPct];
2984
2006
  let summaryRow = s.districts.numberOfRows() - 1;
2985
2007
  totalPop[summaryRow] = p.populationProfile.targetSize;
2986
2008
  popDevPct[summaryRow] = popDev;
2987
- //
2009
+ // Add minority notes
2010
+ scorecard.minority.details['majorityMinority'] = M.getMajorityMinority(s);
2011
+ scorecard.minority.details['vraPreclearance'] = M.getVRASection5(s);
2988
2012
  return scorecard;
2989
2013
  }
2990
2014
  exports.scorePlan = scorePlan;
@@ -3042,7 +2066,6 @@ exports.SHAPES = "2010 VTD shapes";
3042
2066
  // TYPE DEFINITIONS
3043
2067
  //
3044
2068
  Object.defineProperty(exports, "__esModule", { value: true });
3045
- // TODO - SCORE
3046
2069
  var dra_score_1 = __webpack_require__(/*! @dra2020/dra-score */ "@dra2020/dra-score");
3047
2070
  exports.sampleProfile = dra_score_1.sampleProfile;
3048
2071
  exports.sampleScorecard = dra_score_1.sampleScorecard;
@@ -3084,7 +2107,6 @@ function isOutOfState(geoID) {
3084
2107
  return geoID == S.OUT_OF_STATE;
3085
2108
  }
3086
2109
  exports.isOutOfState = isOutOfState;
3087
- // NOTE - UNASSIGNED
3088
2110
  // Get the districtID to which a geoID is assigned
3089
2111
  function getDistrict(plan, geoID) {
3090
2112
  // All geoIDs in a state *should be* assigned to a district (including the
@@ -3139,7 +2161,6 @@ function normalize(rawScore, testScale) {
3139
2161
  let coercedValue = Math.min(Math.max(rawScore, rangeMin), rangeMax);
3140
2162
  // Scale the bounded value w/in the range [0 - (rangeMax - rangeMin)]
3141
2163
  let scaledValue = (coercedValue - rangeMin) / (rangeMax - rangeMin);
3142
- // NOTE - SPLITTING
3143
2164
  // Invert the scaled value if necessary to make bigger = better
3144
2165
  if (testScale.bInvertScaled) {
3145
2166
  scaledValue = 1.0 - scaledValue;
@@ -3338,7 +2359,7 @@ function doIsComplete(s, bLog = false) {
3338
2359
  if (!bAllAssigned) {
3339
2360
  let unassignedDistrict = s.plan.geoIDsForDistrictID(S.NOT_ASSIGNED);
3340
2361
  unassignedFeatures = Array.from(unassignedDistrict);
3341
- unassignedFeatures = unassignedFeatures.slice(0, S.NUMBER_OF_ITEMS_TO_REPORT);
2362
+ // unassignedFeatures = unassignedFeatures.slice(0, S.NUMBER_OF_ITEMS_TO_REPORT);
3342
2363
  }
3343
2364
  // Do all real districts have at least one feature assigned to them?
3344
2365
  let emptyDistricts = [];
@@ -3542,6 +2563,17 @@ function isEmbedded(districtID, geoIDs, plan, graph) {
3542
2563
  exports.isEmbedded = isEmbedded;
3543
2564
 
3544
2565
 
2566
+ /***/ }),
2567
+
2568
+ /***/ "./static/majority-minority.json":
2569
+ /*!***************************************!*\
2570
+ !*** ./static/majority-minority.json ***!
2571
+ \***************************************/
2572
+ /*! exports provided: AL, AK, AZ, AR, CA, CO, CT, DE, FL, GA, HI, ID, IL, IN, IA, KS, KY, LA, ME, MD, MA, MI, MN, MS, MO, MT, NE, NV, NH, NJ, NM, NY, NC, ND, OH, OK, OR, PA, RI, SC, SD, TN, TX, UT, VT, VA, WA, WV, WI, WY, default */
2573
+ /***/ (function(module) {
2574
+
2575
+ module.exports = JSON.parse("{\"AL\":{\"Black\":[7],\"Hispanic\":[],\"Pacific\":[]},\"AK\":{\"Black\":[],\"Hispanic\":[],\"Pacific\":[]},\"AZ\":{\"Black\":[],\"Hispanic\":[2,4,7],\"Pacific\":[]},\"AR\":{\"Black\":[],\"Hispanic\":[],\"Pacific\":[]},\"CA\":{\"Black\":[13,37,43],\"Hispanic\":[17,18,20,21,28,31,32,34,35,38,39,43,45,47,51],\"Pacific\":[12,13,14,15,17,18,19,27,34,39,45,47,52]},\"CO\":{\"Black\":[],\"Hispanic\":[],\"Pacific\":[]},\"CT\":{\"Black\":[],\"Hispanic\":[],\"Pacific\":[]},\"DE\":{\"Black\":[],\"Hispanic\":[],\"Pacific\":[]},\"FL\":{\"Black\":[5,20,24],\"Hispanic\":[18,21,25],\"Pacific\":[]},\"GA\":{\"Black\":[2,4,5,13],\"Hispanic\":[],\"Pacific\":[]},\"HI\":{\"Black\":[],\"Hispanic\":[],\"Pacific\":[2]},\"ID\":{\"Black\":[],\"Hispanic\":[1],\"Pacific\":[]},\"IL\":{\"Black\":[1,2,7],\"Hispanic\":[4],\"Pacific\":[]},\"IN\":{\"Black\":[7],\"Hispanic\":[],\"Pacific\":[]},\"IA\":{\"Black\":[],\"Hispanic\":[],\"Pacific\":[]},\"KS\":{\"Black\":[],\"Hispanic\":[],\"Pacific\":[]},\"KY\":{\"Black\":[],\"Hispanic\":[],\"Pacific\":[]},\"LA\":{\"Black\":[2],\"Hispanic\":[],\"Pacific\":[]},\"ME\":{\"Black\":[],\"Hispanic\":[],\"Pacific\":[]},\"MD\":{\"Black\":[4,7],\"Hispanic\":[],\"Pacific\":[]},\"MA\":{\"Black\":[],\"Hispanic\":[],\"Pacific\":[]},\"MI\":{\"Black\":[13,14],\"Hispanic\":[],\"Pacific\":[]},\"MN\":{\"Black\":[],\"Hispanic\":[],\"Pacific\":[]},\"MS\":{\"Black\":[2],\"Hispanic\":[],\"Pacific\":[]},\"MO\":{\"Black\":[1,5],\"Hispanic\":[],\"Pacific\":[]},\"MT\":{\"Black\":[],\"Hispanic\":[],\"Pacific\":[]},\"NE\":{\"Black\":[],\"Hispanic\":[],\"Pacific\":[]},\"NV\":{\"Black\":[],\"Hispanic\":[],\"Pacific\":[]},\"NH\":{\"Black\":[],\"Hispanic\":[],\"Pacific\":[]},\"NJ\":{\"Black\":[10,12],\"Hispanic\":[13],\"Pacific\":[6]},\"NM\":{\"Black\":[],\"Hispanic\":[2,13],\"Pacific\":[]},\"NY\":{\"Black\":[5,8,9,13],\"Hispanic\":[12,16],\"Pacific\":[6,7,10]},\"NC\":{\"Black\":[1,12],\"Hispanic\":[],\"Pacific\":[]},\"ND\":{\"Black\":[],\"Hispanic\":[],\"Pacific\":[]},\"OH\":{\"Black\":[3,11],\"Hispanic\":[],\"Pacific\":[]},\"OK\":{\"Black\":[],\"Hispanic\":[],\"Pacific\":[]},\"OR\":{\"Black\":[],\"Hispanic\":[],\"Pacific\":[]},\"PA\":{\"Black\":[2],\"Hispanic\":[],\"Pacific\":[]},\"RI\":{\"Black\":[],\"Hispanic\":[],\"Pacific\":[]},\"SC\":{\"Black\":[6],\"Hispanic\":[],\"Pacific\":[]},\"SD\":{\"Black\":[],\"Hispanic\":[],\"Pacific\":[]},\"TN\":{\"Black\":[9],\"Hispanic\":[],\"Pacific\":[]},\"TX\":{\"Black\":[9,18,33],\"Hispanic\":[15,16,19,20,23,27,28,29,32],\"Pacific\":[]},\"UT\":{\"Black\":[],\"Hispanic\":[],\"Pacific\":[]},\"VT\":{\"Black\":[],\"Hispanic\":[],\"Pacific\":[]},\"VA\":{\"Black\":[3],\"Hispanic\":[],\"Pacific\":[]},\"WA\":{\"Black\":[],\"Hispanic\":[3],\"Pacific\":[9]},\"WV\":{\"Black\":[],\"Hispanic\":[],\"Pacific\":[]},\"WI\":{\"Black\":[4],\"Hispanic\":[],\"Pacific\":[]},\"WY\":{\"Black\":[],\"Hispanic\":[],\"Pacific\":[]}}");
2576
+
3545
2577
  /***/ }),
3546
2578
 
3547
2579
  /***/ "./static/state-reqs.json":
@@ -3555,6 +2587,17 @@ module.exports = JSON.parse("{\"AL\":\"https://www.brennancenter.org/sites/defau
3555
2587
 
3556
2588
  /***/ }),
3557
2589
 
2590
+ /***/ "./static/vra5-preclearance.json":
2591
+ /*!***************************************!*\
2592
+ !*** ./static/vra5-preclearance.json ***!
2593
+ \***************************************/
2594
+ /*! exports provided: AL, AK, AZ, CA, FL, GA, LA, MI, MS, NY, NC, SC, SD, TX, VA, default */
2595
+ /***/ (function(module) {
2596
+
2597
+ module.exports = JSON.parse("{\"AL\":\"all\",\"AK\":\"all\",\"AZ\":\"all\",\"CA\":\"parts\",\"FL\":\"parts\",\"GA\":\"all\",\"LA\":\"all\",\"MI\":\"parts\",\"MS\":\"all\",\"NY\":\"parts\",\"NC\":\"parts\",\"SC\":\"all\",\"SD\":\"parts\",\"TX\":\"all\",\"VA\":\"all\"}");
2598
+
2599
+ /***/ }),
2600
+
3558
2601
  /***/ "@dra2020/dra-score":
3559
2602
  /*!*************************************!*\
3560
2603
  !*** external "@dra2020/dra-score" ***!