@dra2020/district-analytics 4.2.0 → 4.3.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.
- package/dist/cli.js +729 -577
- package/dist/cli.js.map +1 -1
- package/dist/district-analytics.js +649 -519
- package/dist/district-analytics.js.map +1 -1
- package/dist/src/_api.d.ts +0 -2
- package/dist/src/cohesive.d.ts +0 -2
- package/dist/src/compact.d.ts +0 -3
- package/dist/src/equal.d.ts +0 -1
- package/dist/src/minority.d.ts +0 -3
- package/dist/src/political.d.ts +0 -7
- package/dist/src/results.d.ts +0 -1
- package/dist/src/types.d.ts +2 -15
- package/package.json +2 -2
|
@@ -121,6 +121,7 @@ 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
|
|
124
125
|
const results_2 = __webpack_require__(/*! ./results */ "./src/results.ts");
|
|
125
126
|
const geofeature_1 = __webpack_require__(/*! ./geofeature */ "./src/geofeature.ts");
|
|
126
127
|
const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
@@ -131,7 +132,7 @@ class AnalyticsSession {
|
|
|
131
132
|
this.bOneTimeProcessingDone = false;
|
|
132
133
|
this.bPlanAnalyzed = false;
|
|
133
134
|
this.bPostProcessingDone = false;
|
|
134
|
-
|
|
135
|
+
// testScales = {} as T.TestScales; TODO - DELETE
|
|
135
136
|
this.tests = {};
|
|
136
137
|
this.title = SessionRequest['title'];
|
|
137
138
|
this.legislativeDistricts = SessionRequest['legislativeDistricts'];
|
|
@@ -142,19 +143,21 @@ class AnalyticsSession {
|
|
|
142
143
|
this.features = new D.Features(this, SessionRequest['data'], this.config['datasets']);
|
|
143
144
|
this.plan = new D.Plan(this, SessionRequest['plan']);
|
|
144
145
|
this.districts = new D.Districts(this, SessionRequest['districtShapes']);
|
|
145
|
-
// TODO -
|
|
146
|
-
if (this.useLegacy())
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
+
// }
|
|
158
161
|
}
|
|
159
162
|
processConfig(config) {
|
|
160
163
|
// NOTE - Session settings are required:
|
|
@@ -167,13 +170,15 @@ class AnalyticsSession {
|
|
|
167
170
|
config['cycle'] = 2010;
|
|
168
171
|
return config;
|
|
169
172
|
}
|
|
170
|
-
|
|
171
|
-
useLegacy()
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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;
|
|
176
180
|
}
|
|
181
|
+
*/
|
|
177
182
|
// Using the the data in the analytics session, calculate all the
|
|
178
183
|
// analytics & validations, saving/updating the individual test results.
|
|
179
184
|
analyzePlan(bLog = false, overridesJSON) {
|
|
@@ -305,19 +310,21 @@ class AnalyticsSession {
|
|
|
305
310
|
}
|
|
306
311
|
// NOTE - Not sure why this has to be up here ...
|
|
307
312
|
populationDeviationThreshold() {
|
|
308
|
-
// TODO -
|
|
309
|
-
if (this.useLegacy())
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
313
|
+
// TODO - DELETE
|
|
314
|
+
// if (this.useLegacy())
|
|
315
|
+
// {
|
|
316
|
+
// return 1 - this.testScales[T.Test.PopulationDeviation]['scale'][0];
|
|
317
|
+
// }
|
|
318
|
+
// else
|
|
319
|
+
// {
|
|
320
|
+
// NOTE - This assumes the plan has been profiled
|
|
321
|
+
const scorer = new Score.Scorer();
|
|
322
|
+
const popdev = scorer.populationDeviationThreshold(this.legislativeDistricts);
|
|
323
|
+
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
|
+
// }
|
|
321
328
|
}
|
|
322
329
|
}
|
|
323
330
|
exports.AnalyticsSession = AnalyticsSession;
|
|
@@ -926,10 +933,22 @@ exports.Graph = Graph;
|
|
|
926
933
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
927
934
|
const valid_1 = __webpack_require__(/*! ./valid */ "./src/valid.ts");
|
|
928
935
|
const equal_1 = __webpack_require__(/*! ./equal */ "./src/equal.ts");
|
|
929
|
-
|
|
936
|
+
// import { doPopulationDeviation, doHasEqualPopulations } from './equal'; TODO - DELETE
|
|
937
|
+
// import { doReock, doPolsbyPopper } from './compact'; TODO - DELETE
|
|
930
938
|
const cohesive_1 = __webpack_require__(/*! ./cohesive */ "./src/cohesive.ts");
|
|
931
|
-
|
|
932
|
-
|
|
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
|
+
*/
|
|
933
952
|
// Compile district-level info for plan/map-level analytics
|
|
934
953
|
function doAnalyzeDistricts(s, bLog = false) {
|
|
935
954
|
s.districts.recalcStatistics(bLog);
|
|
@@ -941,58 +960,65 @@ exports.doAnalyzeDistricts = doAnalyzeDistricts;
|
|
|
941
960
|
// NOTE - I could make this table-driven, but I'm thinking that the explicit
|
|
942
961
|
// calls might make chunking for aync easier.
|
|
943
962
|
function doAnalyzePlan(s, bLog = false) {
|
|
944
|
-
// TODO -
|
|
945
|
-
if (s.useLegacy())
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
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
|
+
s.tests[0 /* Complete */] = valid_1.doIsComplete(s, bLog);
|
|
1010
|
+
s.tests[1 /* Contiguous */] = valid_1.doIsContiguous(s, bLog);
|
|
1011
|
+
s.tests[2 /* FreeOfHoles */] = valid_1.doIsFreeOfHoles(s, bLog);
|
|
1012
|
+
// s.tests[T.Test.PopulationDeviation] = doPopulationDeviation(s, bLog); TODO - DELETE
|
|
1013
|
+
// NOTE - I can't check whether a population deviation is legal or not, until
|
|
1014
|
+
// the raw % is normalized. A zero (0) would mean "too much" / "not legal," for
|
|
1015
|
+
// the given type of district (CD vs. LD). The EqualPopulation test is derived
|
|
1016
|
+
// from PopulationDeviation, as part of scorecard or test log preparation.
|
|
1017
|
+
// Create an empty test entry here though ...
|
|
1018
|
+
s.tests[3 /* EqualPopulation */] = s.getTest(3 /* EqualPopulation */);
|
|
1019
|
+
s.tests[5 /* UnexpectedCountySplits */] = cohesive_1.doFindCountiesSplitUnexpectedly(s, bLog);
|
|
1020
|
+
s.tests[6 /* VTDSplits */] = cohesive_1.doFindSplitVTDs(s, bLog);
|
|
1021
|
+
// }
|
|
996
1022
|
// Enable a Test Log and Scorecard to be generated
|
|
997
1023
|
s.bPlanAnalyzed = true;
|
|
998
1024
|
s.bPostProcessingDone = false;
|
|
@@ -1033,185 +1059,279 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
1033
1059
|
};
|
|
1034
1060
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1035
1061
|
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
1036
|
-
const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"));
|
|
1037
1062
|
const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
1063
|
+
// NOTE - The active code is below the long multi-line section to be deleted.
|
|
1064
|
+
/* TODO - DELETE
|
|
1038
1065
|
// CALCULATE ENHANCED SQRT ENTROPY METRIC
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
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;
|
|
1055
1092
|
}
|
|
1056
|
-
|
|
1057
|
-
function doDistrictSplitting(s, bLog = false)
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
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;
|
|
1073
1119
|
}
|
|
1074
|
-
|
|
1120
|
+
|
|
1121
|
+
|
|
1075
1122
|
// HELPERS
|
|
1076
|
-
|
|
1123
|
+
|
|
1124
|
+
// Loop over all the county-district combos, skipping the virtual district 0
|
|
1077
1125
|
// and virtual county 0.
|
|
1078
1126
|
//
|
|
1079
1127
|
// NOTE - The county-district splits and the county & district totals may all,
|
|
1080
1128
|
// in general, be fractional/decimal numbers as opposed to integers, due to
|
|
1081
1129
|
// dissaggregation & re-aggregation. Hence, comparisons need to approximate
|
|
1082
1130
|
// equality.
|
|
1131
|
+
|
|
1083
1132
|
// Consolidate districts (rows) consisting of just one county (column)
|
|
1084
1133
|
// UP into the dummy district (0).
|
|
1085
|
-
function reduceCSplits(CxDreducedC, districtTotals)
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
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;
|
|
1097
1151
|
}
|
|
1152
|
+
}
|
|
1098
1153
|
}
|
|
1154
|
+
}
|
|
1099
1155
|
}
|
|
1156
|
+
|
|
1100
1157
|
// Consolidate whole counties (columns) in a district (row) LEFT into the
|
|
1101
1158
|
// dummy county (0).
|
|
1102
|
-
function reduceDSplits(CxDreducedD, countyTotals)
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
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;
|
|
1114
1176
|
}
|
|
1177
|
+
}
|
|
1115
1178
|
}
|
|
1179
|
+
}
|
|
1116
1180
|
}
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
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
|
+
}
|
|
1148
1231
|
}
|
|
1149
|
-
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
return f;
|
|
1150
1235
|
}
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
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
|
+
}
|
|
1164
1256
|
}
|
|
1165
|
-
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
return g;
|
|
1166
1260
|
}
|
|
1261
|
+
|
|
1167
1262
|
// Deal with decimal census "counts" due to disagg/re-agg
|
|
1168
|
-
function areRoughlyEqual(x, y)
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
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;
|
|
1172
1269
|
}
|
|
1270
|
+
|
|
1173
1271
|
// For all districts in a county, sum the split score.
|
|
1174
|
-
function countySplitScore(j, f, numD, bLog = false)
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
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;
|
|
1180
1282
|
}
|
|
1283
|
+
|
|
1181
1284
|
// For all counties, sum the weighted county splits.
|
|
1182
|
-
function countySplitting(f, w, bLog = false)
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
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);
|
|
1193
1301
|
}
|
|
1302
|
+
|
|
1194
1303
|
// For all counties in a district, sum the split score.
|
|
1195
|
-
function districtSplitScore(i, g, numC, bLog = false)
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
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;
|
|
1201
1314
|
}
|
|
1315
|
+
|
|
1202
1316
|
// For all districts, sum the weighted district splits.
|
|
1203
|
-
function districtSplitting(g, x, bLog = false)
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
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);
|
|
1214
1333
|
}
|
|
1334
|
+
*/
|
|
1215
1335
|
// ANALYZE SIMPLE COUNTY & VTD SPLITTING
|
|
1216
1336
|
/*
|
|
1217
1337
|
|
|
@@ -1246,7 +1366,7 @@ These counties are split unexpectedly:
|
|
|
1246
1366
|
|
|
1247
1367
|
*/
|
|
1248
1368
|
function doFindCountiesSplitUnexpectedly(s, bLog = false) {
|
|
1249
|
-
let test = s.getTest(
|
|
1369
|
+
let test = s.getTest(5 /* UnexpectedCountySplits */);
|
|
1250
1370
|
// THE THREE VALUES TO DETERMINE FOR A PLAN
|
|
1251
1371
|
let unexpectedSplits = 0;
|
|
1252
1372
|
let unexpectedAffected = 0;
|
|
@@ -1339,10 +1459,10 @@ function doFindCountiesSplitUnexpectedly(s, bLog = false) {
|
|
|
1339
1459
|
return test;
|
|
1340
1460
|
}
|
|
1341
1461
|
exports.doFindCountiesSplitUnexpectedly = doFindCountiesSplitUnexpectedly;
|
|
1462
|
+
// NOTE - This function just creates an empty container that dra-client fills in when generating the UI
|
|
1342
1463
|
function doFindSplitVTDs(s, bLog = false) {
|
|
1343
|
-
let test = s.getTest(
|
|
1464
|
+
let test = s.getTest(6 /* VTDSplits */);
|
|
1344
1465
|
let splitVTDs = [];
|
|
1345
|
-
// TODO - SPLITTING: Flesh this out, using virtual VTD's ...
|
|
1346
1466
|
test['score'] = splitVTDs.length;
|
|
1347
1467
|
test['details']['splitVTDs'] = splitVTDs;
|
|
1348
1468
|
return test;
|
|
@@ -1372,10 +1492,10 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
1372
1492
|
return result;
|
|
1373
1493
|
};
|
|
1374
1494
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1495
|
+
// NOTE - This file will NOT be empty, when legacy code is deleted.
|
|
1375
1496
|
const Poly = __importStar(__webpack_require__(/*! @dra2020/poly */ "@dra2020/poly"));
|
|
1376
1497
|
const geofeature_1 = __webpack_require__(/*! ./geofeature */ "./src/geofeature.ts");
|
|
1377
|
-
|
|
1378
|
-
// TODO - SCORE: Remove legacy code
|
|
1498
|
+
// TODO - DELETE
|
|
1379
1499
|
// Measures of compactness compare district shapes to various ideally compact
|
|
1380
1500
|
// benchmarks, such as circles. All else equal, more compact districts are better.
|
|
1381
1501
|
//
|
|
@@ -1454,62 +1574,83 @@ const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
|
1454
1574
|
// For each measure, the compactness of a set of Congressional districts is the
|
|
1455
1575
|
// average of that measure for all the districts.
|
|
1456
1576
|
//
|
|
1577
|
+
/* TODO - DELETE
|
|
1457
1578
|
// Calculate Reock compactness:
|
|
1458
1579
|
// reock = (4 * a) / (math.pi * d**2)
|
|
1459
1580
|
// NOTE - Depends on extractDistrictProperties running first
|
|
1460
|
-
function doReock(s, bLog = false)
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
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);
|
|
1477
1604
|
}
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
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;
|
|
1483
1614
|
}
|
|
1484
|
-
|
|
1615
|
+
|
|
1485
1616
|
// Calculate Polsby-Popper compactness measures:
|
|
1486
1617
|
// polsby_popper = (4 * math.pi) * (a / p**2)
|
|
1487
1618
|
// NOTE - Depends on extractDistrictProperties running first
|
|
1488
|
-
function doPolsbyPopper(s, bLog = false)
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
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);
|
|
1505
1642
|
}
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
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;
|
|
1511
1652
|
}
|
|
1512
|
-
|
|
1653
|
+
*/
|
|
1513
1654
|
// HELPER TO EXTRACT PROPERTIES OF DISTRICT SHAPES
|
|
1514
1655
|
// TODO - SCORE: Create an array, as opposed to a dict
|
|
1515
1656
|
function extractDistrictProperties(s, bLog = false) {
|
|
@@ -1587,41 +1728,53 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
1587
1728
|
return result;
|
|
1588
1729
|
};
|
|
1589
1730
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1590
|
-
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
1591
1731
|
const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
1592
|
-
|
|
1593
|
-
function doPopulationDeviation(s, bLog = false)
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
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;
|
|
1623
1776
|
}
|
|
1624
|
-
|
|
1777
|
+
*/
|
|
1625
1778
|
// NOTE - This validity check is *derived* and depends on population deviation %
|
|
1626
1779
|
// being computed (above) and normalized in test log & scorecard generation.
|
|
1627
1780
|
function doHasEqualPopulations(s, bLog = false) {
|
|
@@ -1671,7 +1824,6 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
1671
1824
|
};
|
|
1672
1825
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1673
1826
|
const Poly = __importStar(__webpack_require__(/*! @dra2020/poly */ "@dra2020/poly"));
|
|
1674
|
-
// TODO - SCORE: Remove legacy code
|
|
1675
1827
|
// HELPER
|
|
1676
1828
|
function polyParts(poly) {
|
|
1677
1829
|
let parts = { type: 'FeatureCollection', features: [] };
|
|
@@ -1810,35 +1962,6 @@ __export(__webpack_require__(/*! ./results */ "./src/results.ts"));
|
|
|
1810
1962
|
__export(__webpack_require__(/*! ./types */ "./src/types.ts"));
|
|
1811
1963
|
|
|
1812
1964
|
|
|
1813
|
-
/***/ }),
|
|
1814
|
-
|
|
1815
|
-
/***/ "./src/minority.ts":
|
|
1816
|
-
/*!*************************!*\
|
|
1817
|
-
!*** ./src/minority.ts ***!
|
|
1818
|
-
\*************************/
|
|
1819
|
-
/*! no static exports found */
|
|
1820
|
-
/***/ (function(module, exports, __webpack_require__) {
|
|
1821
|
-
|
|
1822
|
-
"use strict";
|
|
1823
|
-
|
|
1824
|
-
//
|
|
1825
|
-
// PROTECTS MINORITIES
|
|
1826
|
-
//
|
|
1827
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1828
|
-
// TODO - SCORE: Remove legacy code
|
|
1829
|
-
function doMajorityMinorityDistricts(s, bLog = false) {
|
|
1830
|
-
let test = s.getTest(16 /* MajorityMinorityDistricts */);
|
|
1831
|
-
if (bLog)
|
|
1832
|
-
console.log("TODO - Calculating # of majority-minority districts ...");
|
|
1833
|
-
return test;
|
|
1834
|
-
}
|
|
1835
|
-
exports.doMajorityMinorityDistricts = doMajorityMinorityDistricts;
|
|
1836
|
-
// Sources for majority-minority info:
|
|
1837
|
-
// - https://en.wikipedia.org/wiki/List_of_majority-minority_United_States_congressional_districts
|
|
1838
|
-
// TODO - 2020: Update/revise this, when the update comes out in September:
|
|
1839
|
-
// - http://www.ncsl.org/Portals/1/Documents/Redistricting/Redistricting_2010.pdf
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
1965
|
/***/ }),
|
|
1843
1966
|
|
|
1844
1967
|
/***/ "./src/political.ts":
|
|
@@ -1853,18 +1976,14 @@ exports.doMajorityMinorityDistricts = doMajorityMinorityDistricts;
|
|
|
1853
1976
|
//
|
|
1854
1977
|
// FAIR/PROPORTIONAL
|
|
1855
1978
|
//
|
|
1856
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
1857
|
-
if (mod && mod.__esModule) return mod;
|
|
1858
|
-
var result = {};
|
|
1859
|
-
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
1860
|
-
result["default"] = mod;
|
|
1861
|
-
return result;
|
|
1862
|
-
};
|
|
1863
1979
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1980
|
+
// NOTE - This file will NOT be empty, when legacy code is deleted.
|
|
1864
1981
|
const assert_1 = __webpack_require__(/*! assert */ "assert");
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1982
|
+
/* TODO - DELETE
|
|
1983
|
+
import * as D from './_data'
|
|
1984
|
+
import { AnalyticsSession } from './_api';
|
|
1985
|
+
|
|
1986
|
+
|
|
1868
1987
|
// Partisan analytics need the following data:
|
|
1869
1988
|
//
|
|
1870
1989
|
// An "election model" by geo_id, where each item has 4 pieces of data:
|
|
@@ -1883,66 +2002,89 @@ const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
|
1883
2002
|
//
|
|
1884
2003
|
// I'm labelling this general concept a "Voter Preference Index (VPI)," a
|
|
1885
2004
|
// conscious +1 letter play on Cook's "PVI" acronymn.
|
|
2005
|
+
|
|
2006
|
+
|
|
1886
2007
|
// MEASURING BIAS & RESPONSIVENESS (NAGLE'S METHOD)
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
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;
|
|
1892
2016
|
}
|
|
1893
|
-
|
|
1894
|
-
function doVotesBias(s, bLog = false)
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
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;
|
|
1899
2025
|
}
|
|
1900
|
-
|
|
1901
|
-
function doResponsiveness(s, bLog = false)
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
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;
|
|
1906
2034
|
}
|
|
1907
|
-
|
|
1908
|
-
function doResponsiveDistricts(s, bLog = false)
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
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;
|
|
1913
2043
|
}
|
|
1914
|
-
|
|
2044
|
+
|
|
2045
|
+
|
|
1915
2046
|
// OTHER MEASURES OF PARTISAN BIAS
|
|
2047
|
+
|
|
1916
2048
|
// TODO - PARTISAN: This formula might need to be inverted for D vs. R +/-
|
|
1917
2049
|
// TODO - Normalize the results.
|
|
1918
|
-
function doEfficiencyGap(s, bLog = false)
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
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;
|
|
1943
2083
|
}
|
|
1944
|
-
|
|
2084
|
+
|
|
2085
|
+
|
|
1945
2086
|
// HELPERS
|
|
2087
|
+
*/
|
|
1946
2088
|
function fptpWin(demPct) {
|
|
1947
2089
|
// Vote shares should be fractions in the range [0.0 – 1.0]
|
|
1948
2090
|
assert_1.strict((demPct <= 1.0) && (demPct >= 0.));
|
|
@@ -2548,24 +2690,26 @@ const populationDeviationDefn = {
|
|
|
2548
2690
|
externalType: TestType.Percentage,
|
|
2549
2691
|
suites: [0 /* Legal */, 2 /* Best */] // Both so EqualPopulation can be assessed
|
|
2550
2692
|
};
|
|
2551
|
-
|
|
2552
|
-
const reockDefn = {
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
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]
|
|
2558
2700
|
};
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
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]
|
|
2565
2708
|
};
|
|
2709
|
+
*/
|
|
2566
2710
|
// NOTE - SPLITTING
|
|
2567
2711
|
const unexpectedCountySplitsDefn = {
|
|
2568
|
-
ID:
|
|
2712
|
+
ID: 5 /* UnexpectedCountySplits */,
|
|
2569
2713
|
name: "Unexpected County Splits",
|
|
2570
2714
|
normalize: false,
|
|
2571
2715
|
externalType: TestType.Percentage,
|
|
@@ -2573,35 +2717,12 @@ const unexpectedCountySplitsDefn = {
|
|
|
2573
2717
|
};
|
|
2574
2718
|
// NOTE - SPLITTING
|
|
2575
2719
|
const VTDSplitsDefn = {
|
|
2576
|
-
ID:
|
|
2720
|
+
ID: 6 /* VTDSplits */,
|
|
2577
2721
|
name: "VTD Splits",
|
|
2578
2722
|
normalize: false,
|
|
2579
2723
|
externalType: TestType.Number,
|
|
2580
2724
|
suites: [2 /* Best */]
|
|
2581
2725
|
};
|
|
2582
|
-
// NOTE - SPLITTING
|
|
2583
|
-
const countySplittingDefn = {
|
|
2584
|
-
ID: 8 /* CountySplitting */,
|
|
2585
|
-
name: "County Splitting",
|
|
2586
|
-
normalize: true,
|
|
2587
|
-
externalType: TestType.Number,
|
|
2588
|
-
suites: [2 /* Best */]
|
|
2589
|
-
};
|
|
2590
|
-
// NOTE - SPLITTING
|
|
2591
|
-
const districtSplittingDefn = {
|
|
2592
|
-
ID: 9 /* DistrictSplitting */,
|
|
2593
|
-
name: "District Splitting",
|
|
2594
|
-
normalize: true,
|
|
2595
|
-
externalType: TestType.Number,
|
|
2596
|
-
suites: [2 /* Best */]
|
|
2597
|
-
};
|
|
2598
|
-
const efficiencyGapDefn = {
|
|
2599
|
-
ID: 15 /* EfficiencyGap */,
|
|
2600
|
-
name: "Efficiency Gap",
|
|
2601
|
-
normalize: false,
|
|
2602
|
-
externalType: TestType.Percentage,
|
|
2603
|
-
suites: [1 /* Fair */]
|
|
2604
|
-
};
|
|
2605
2726
|
// All the tests that have been defined (can be reported on)
|
|
2606
2727
|
const testDefns = {
|
|
2607
2728
|
[0 /* Complete */]: completeDefn,
|
|
@@ -2609,94 +2730,103 @@ const testDefns = {
|
|
|
2609
2730
|
[2 /* FreeOfHoles */]: freeOfHolesDefn,
|
|
2610
2731
|
[3 /* EqualPopulation */]: equalPopulationDefn,
|
|
2611
2732
|
[4 /* PopulationDeviation */]: populationDeviationDefn,
|
|
2612
|
-
[
|
|
2613
|
-
[
|
|
2614
|
-
|
|
2615
|
-
[
|
|
2616
|
-
[10 /* VTDSplits */]: VTDSplitsDefn,
|
|
2617
|
-
[8 /* CountySplitting */]: countySplittingDefn,
|
|
2618
|
-
[9 /* DistrictSplitting */]: districtSplittingDefn,
|
|
2619
|
-
// TODO - More tests ...
|
|
2620
|
-
[15 /* EfficiencyGap */]: efficiencyGapDefn
|
|
2733
|
+
// [T.Test.Reock]: reockDefn, TODO - DELETE
|
|
2734
|
+
// [T.Test.PolsbyPopper]: polsbyPopperDefn, TODO - DELETE
|
|
2735
|
+
[5 /* UnexpectedCountySplits */]: unexpectedCountySplitsDefn,
|
|
2736
|
+
[6 /* VTDSplits */]: VTDSplitsDefn,
|
|
2621
2737
|
};
|
|
2738
|
+
/* TODO - DELETE
|
|
2622
2739
|
// NORMALIZE RAW ANALYTICS
|
|
2623
2740
|
// Raw numeric analytics, such as population deviation, compactness, etc. are
|
|
2624
2741
|
// normalized as part of creating a scorecard, so the code to normalize results
|
|
2625
2742
|
// is encapsulated here.
|
|
2743
|
+
|
|
2626
2744
|
// Configure scale parameters for normalizing each raw test result.
|
|
2627
2745
|
// Scales consist of a minimum & a maximum *raw* value. If the values get
|
|
2628
2746
|
// inverted (to make bigger better), these will switch.
|
|
2629
2747
|
// This process needs to be separate from the test configuration info above,
|
|
2630
2748
|
// because some scales need access to the analytics session object.
|
|
2631
|
-
function doConfigureScales(s)
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
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
|
+
*/
|
|
2657
2783
|
// Postprocess analytics - Normalize numeric results and derive secondary tests.
|
|
2658
2784
|
// Do this after analytics have been run and before preparing a test log or scorecard.
|
|
2659
2785
|
function doAnalyzePostProcessing(s, bLog = false) {
|
|
2660
|
-
// TODO -
|
|
2661
|
-
if (s.useLegacy())
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
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
|
+
// Just populate the normalized population deviation score in the test
|
|
2808
|
+
const scorecard = s._scorecard;
|
|
2809
|
+
let popDev = s.getTest(4 /* PopulationDeviation */);
|
|
2810
|
+
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
|
+
const datasets = {
|
|
2815
|
+
shapes: S.SHAPES,
|
|
2816
|
+
census: U.deepCopy(s.config['descriptions']['CENSUS']),
|
|
2817
|
+
vap: U.deepCopy(s.config['descriptions']['VAP']),
|
|
2818
|
+
election: U.deepCopy(s.config['descriptions']['ELECTION'])
|
|
2819
|
+
};
|
|
2820
|
+
scorecard.partisan.details['election'] = datasets.election;
|
|
2821
|
+
scorecard.minority.details['vap'] = datasets.vap;
|
|
2822
|
+
scorecard.traditionalPrinciples.details['shapes'] = datasets.shapes;
|
|
2823
|
+
scorecard.traditionalPrinciples.details['census'] = datasets.census;
|
|
2824
|
+
// TODO - SCORE: Add legacy splits details
|
|
2825
|
+
const simpleSplits = s.getTest(5 /* UnexpectedCountySplits */);
|
|
2826
|
+
scorecard.traditionalPrinciples.details['unexpectedAffected'] = simpleSplits['score'];
|
|
2827
|
+
scorecard.traditionalPrinciples.details['countiesSplitUnexpectedly'] = U.deepCopy(simpleSplits['details']['countiesSplitUnexpectedly']);
|
|
2828
|
+
// NOTE - Add split precincts in dra-client directly
|
|
2829
|
+
// }
|
|
2700
2830
|
// Derive secondary tests
|
|
2701
2831
|
analyze_1.doDeriveSecondaryTests(s, bLog);
|
|
2702
2832
|
// Toggle the semaphore, so postprocessing isn't for both the testlog & scorecard
|