@dra2020/district-analytics 1.0.9 → 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/_api.d.ts +27 -0
- package/dist/_data.d.ts +130 -0
- package/dist/analyze.d.ts +4 -0
- package/dist/cli.js +1344 -1730
- package/dist/cli.js.map +1 -1
- package/dist/cohesive.d.ts +4 -0
- package/dist/compact.d.ts +5 -0
- package/dist/constants.d.ts +6 -0
- package/dist/district-analytics.js +1205 -1315
- package/dist/district-analytics.js.map +1 -1
- package/dist/equal.d.ts +4 -0
- package/dist/geofeature.d.ts +3 -0
- package/dist/index.d.ts +2 -0
- package/dist/minority.d.ts +3 -0
- package/dist/political.d.ts +8 -0
- package/dist/preprocess.d.ts +2 -0
- package/dist/report.d.ts +15 -0
- package/dist/settings.d.ts +5 -0
- package/dist/src/_api.d.ts +4 -4
- package/dist/src/_data.d.ts +2 -4
- package/dist/src/analyze.d.ts +1 -1
- package/dist/src/cohesive.d.ts +2 -4
- package/dist/src/equal.d.ts +2 -2
- package/dist/src/index.d.ts +0 -1
- package/dist/src/minority.d.ts +1 -1
- package/dist/src/political.d.ts +5 -5
- package/dist/src/preprocess.d.ts +1 -1
- package/dist/src/report.d.ts +15 -0
- package/dist/src/settings.d.ts +0 -3
- package/dist/src/types.d.ts +11 -24
- package/dist/src/utils.d.ts +3 -4
- package/dist/src/valid.d.ts +3 -3
- package/dist/test/_cli.d.ts +0 -2
- package/dist/types.d.ts +110 -0
- package/dist/utils.d.ts +28 -0
- package/dist/valid.d.ts +8 -0
- package/package.json +4 -6
- package/dist/src/results.d.ts +0 -141
package/dist/cli.js
CHANGED
|
@@ -8952,25 +8952,12 @@ function sort(keys, values, left, right, compare) {
|
|
|
8952
8952
|
//
|
|
8953
8953
|
// THE NODE PACKAGE API
|
|
8954
8954
|
//
|
|
8955
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
8956
|
-
if (mod && mod.__esModule) return mod;
|
|
8957
|
-
var result = {};
|
|
8958
|
-
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
8959
|
-
result["default"] = mod;
|
|
8960
|
-
return result;
|
|
8961
|
-
};
|
|
8962
8955
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8963
8956
|
const preprocess_1 = __webpack_require__(/*! ./preprocess */ "./src/preprocess.ts");
|
|
8964
8957
|
const analyze_1 = __webpack_require__(/*! ./analyze */ "./src/analyze.ts");
|
|
8965
|
-
|
|
8966
|
-
|
|
8967
|
-
|
|
8968
|
-
// doPrepareScorecard, doPrepareTestLog, Scorecard
|
|
8969
|
-
// } from './report'
|
|
8970
|
-
const results_1 = __webpack_require__(/*! ./results */ "./src/results.ts");
|
|
8971
|
-
const results_2 = __webpack_require__(/*! ./results */ "./src/results.ts");
|
|
8972
|
-
const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
8973
|
-
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
8958
|
+
const report_1 = __webpack_require__(/*! ./report */ "./src/report.ts");
|
|
8959
|
+
const D = __webpack_require__(/*! ./_data */ "./src/_data.ts");
|
|
8960
|
+
const U = __webpack_require__(/*! ./utils */ "./src/utils.ts");
|
|
8974
8961
|
class AnalyticsSession {
|
|
8975
8962
|
constructor(SessionRequest) {
|
|
8976
8963
|
this.config = {};
|
|
@@ -8979,6 +8966,7 @@ class AnalyticsSession {
|
|
|
8979
8966
|
this.bPostProcessingDone = false;
|
|
8980
8967
|
this.testScales = {};
|
|
8981
8968
|
this.tests = {};
|
|
8969
|
+
this.scorecard = {};
|
|
8982
8970
|
this.title = SessionRequest['title'];
|
|
8983
8971
|
this.legislativeDistricts = SessionRequest['legislativeDistricts'];
|
|
8984
8972
|
this.config = this.processConfig(SessionRequest['config']);
|
|
@@ -8991,13 +8979,12 @@ class AnalyticsSession {
|
|
|
8991
8979
|
// NOTE: I've pulled these out of the individual analytics to here. Eventually,
|
|
8992
8980
|
// we could want them to passed into an analytics session as data, along with
|
|
8993
8981
|
// everything else. For now, this keeps branching out of the main code.
|
|
8994
|
-
|
|
8982
|
+
report_1.doConfigureScales(this);
|
|
8995
8983
|
}
|
|
8996
8984
|
processConfig(config) {
|
|
8997
8985
|
// NOTE - Session settings are required:
|
|
8998
8986
|
// - Analytics suites can be defaulted to all with [], but
|
|
8999
8987
|
// - Dataset keys must be explicitly specified with 'dataset'
|
|
9000
|
-
// TODO - Remove this mechanism. Always run everything.
|
|
9001
8988
|
let defaultSuites = [0 /* Legal */, 1 /* Fair */, 2 /* Best */];
|
|
9002
8989
|
// If the config passed in has no suites = [], use the default suites
|
|
9003
8990
|
if (U.isArrayEmpty(config['suites'])) {
|
|
@@ -9012,10 +8999,10 @@ class AnalyticsSession {
|
|
|
9012
8999
|
// analytics & validations, saving/updating the individual test results.
|
|
9013
9000
|
analyzePlan(bLog = false) {
|
|
9014
9001
|
try {
|
|
9015
|
-
preprocess_1.doPreprocessData(this
|
|
9002
|
+
preprocess_1.doPreprocessData(this);
|
|
9016
9003
|
analyze_1.doAnalyzeDistricts(this, bLog);
|
|
9017
9004
|
analyze_1.doAnalyzePlan(this, bLog);
|
|
9018
|
-
|
|
9005
|
+
report_1.doAnalyzePostProcessing(this);
|
|
9019
9006
|
}
|
|
9020
9007
|
catch (_a) {
|
|
9021
9008
|
console.log("Exception caught by analyzePlan()");
|
|
@@ -9023,36 +9010,6 @@ class AnalyticsSession {
|
|
|
9023
9010
|
}
|
|
9024
9011
|
return true;
|
|
9025
9012
|
}
|
|
9026
|
-
// NOTE - This assumes that analyzePlan() has been run!
|
|
9027
|
-
getPlanAnalytics(bLog = false) {
|
|
9028
|
-
return results_2.preparePlanAnalytics(this, bLog);
|
|
9029
|
-
}
|
|
9030
|
-
// NOTE - This assumes that analyzePlan() has been run!
|
|
9031
|
-
getDistrictStatistics(bLog = false) {
|
|
9032
|
-
return results_2.prepareDistrictStatistics(this, bLog);
|
|
9033
|
-
}
|
|
9034
|
-
// NOTE - This assumes that analyzePlan() has been run!
|
|
9035
|
-
getDiscontiguousDistrictFeatures(bLog = false) {
|
|
9036
|
-
// Get the (possibly empty) list of discontiguous district IDs
|
|
9037
|
-
let contiguousTest = this.getTest(1 /* Contiguous */);
|
|
9038
|
-
let discontiguousDistrictIDs = contiguousTest['details']['discontiguousDistricts'] || [];
|
|
9039
|
-
// Convert them into a (possibly empty) list of features
|
|
9040
|
-
let discontiguousDistrictFeatures = { type: 'FeatureCollection', features: [] };
|
|
9041
|
-
if (!(U.isArrayEmpty(discontiguousDistrictIDs))) {
|
|
9042
|
-
for (let i = 0; i < discontiguousDistrictIDs.length; i++) {
|
|
9043
|
-
let poly = this.districts.getShape(i);
|
|
9044
|
-
discontiguousDistrictFeatures.features.push(poly);
|
|
9045
|
-
}
|
|
9046
|
-
}
|
|
9047
|
-
return discontiguousDistrictFeatures;
|
|
9048
|
-
}
|
|
9049
|
-
// TODO - DASHBOARD: Delete, when cut over to the new analytics UI.
|
|
9050
|
-
// Prepare a scorecard for rendering
|
|
9051
|
-
// NOTE - This assumes that analyzePlan() has been run!
|
|
9052
|
-
// prepareScorecard(): any {
|
|
9053
|
-
// return doPrepareScorecard(this);
|
|
9054
|
-
// }
|
|
9055
|
-
// HELPERS USED INTERNALLY
|
|
9056
9013
|
// Get an individual test, so you can drive UI with the results.
|
|
9057
9014
|
getTest(testID) {
|
|
9058
9015
|
// Get the existing test entries
|
|
@@ -9067,15 +9024,18 @@ class AnalyticsSession {
|
|
|
9067
9024
|
// Return a pointer to the the test entry for this test
|
|
9068
9025
|
return this.tests[testID];
|
|
9069
9026
|
}
|
|
9070
|
-
//
|
|
9027
|
+
// Prepare a scorecard for rendering
|
|
9028
|
+
// NOTE - This assumes that analyzePlan() has been run!
|
|
9029
|
+
prepareScorecard() {
|
|
9030
|
+
return report_1.doPrepareScorecard(this);
|
|
9031
|
+
}
|
|
9071
9032
|
// Prepare test results for rendering
|
|
9072
9033
|
// NOTE - This assumes that analyzePlan() has been run!
|
|
9073
|
-
|
|
9074
|
-
|
|
9075
|
-
|
|
9076
|
-
// NOTE - Not sure why this has to be up here.
|
|
9034
|
+
prepareTestLog() {
|
|
9035
|
+
return report_1.doPrepareTestLog(this);
|
|
9036
|
+
}
|
|
9077
9037
|
populationDeviationThreshold() {
|
|
9078
|
-
return 1 - this.testScales[4 /* PopulationDeviation */]['
|
|
9038
|
+
return 1 - this.testScales[4 /* PopulationDeviation */]['testScale'][0];
|
|
9079
9039
|
}
|
|
9080
9040
|
}
|
|
9081
9041
|
exports.AnalyticsSession = AnalyticsSession;
|
|
@@ -9095,16 +9055,8 @@ exports.AnalyticsSession = AnalyticsSession;
|
|
|
9095
9055
|
//
|
|
9096
9056
|
// DATA ABSTRACTION LAYER
|
|
9097
9057
|
//
|
|
9098
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
9099
|
-
if (mod && mod.__esModule) return mod;
|
|
9100
|
-
var result = {};
|
|
9101
|
-
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
9102
|
-
result["default"] = mod;
|
|
9103
|
-
return result;
|
|
9104
|
-
};
|
|
9105
9058
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9106
|
-
const U =
|
|
9107
|
-
const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"));
|
|
9059
|
+
const U = __webpack_require__(/*! ./utils */ "./src/utils.ts");
|
|
9108
9060
|
const valid_1 = __webpack_require__(/*! ./valid */ "./src/valid.ts");
|
|
9109
9061
|
const compact_1 = __webpack_require__(/*! ./compact */ "./src/compact.ts");
|
|
9110
9062
|
const political_1 = __webpack_require__(/*! ./political */ "./src/political.ts");
|
|
@@ -9143,6 +9095,23 @@ var DistrictField;
|
|
|
9143
9095
|
DistrictField[DistrictField["NativePct"] = 27] = "NativePct"; // Display
|
|
9144
9096
|
// 1 - MORE ...
|
|
9145
9097
|
})(DistrictField = exports.DistrictField || (exports.DistrictField = {}));
|
|
9098
|
+
// The fields to display in a District Statistics pane
|
|
9099
|
+
exports.DisplayFields = [
|
|
9100
|
+
DistrictField.TotalPop,
|
|
9101
|
+
DistrictField.PopDevPct,
|
|
9102
|
+
DistrictField.bEqualPop,
|
|
9103
|
+
DistrictField.bNotEmpty,
|
|
9104
|
+
DistrictField.bContiguous,
|
|
9105
|
+
DistrictField.bNotEmbedded,
|
|
9106
|
+
DistrictField.DemPct,
|
|
9107
|
+
DistrictField.WhitePct,
|
|
9108
|
+
DistrictField.MinorityPct,
|
|
9109
|
+
DistrictField.BlackPct,
|
|
9110
|
+
DistrictField.HispanicPct,
|
|
9111
|
+
DistrictField.PacificPct,
|
|
9112
|
+
DistrictField.AsianPct,
|
|
9113
|
+
DistrictField.NativePct
|
|
9114
|
+
];
|
|
9146
9115
|
class Districts {
|
|
9147
9116
|
constructor(s, ds) {
|
|
9148
9117
|
this._geoProperties = {};
|
|
@@ -9151,13 +9120,7 @@ class Districts {
|
|
|
9151
9120
|
this.statistics = this.initStatistics();
|
|
9152
9121
|
}
|
|
9153
9122
|
getShape(i) { return this._shapes.features[i]; }
|
|
9154
|
-
getGeoProperties(i) {
|
|
9155
|
-
// Make sure the district shape exists & has geo properties
|
|
9156
|
-
if (i in this._geoProperties)
|
|
9157
|
-
return this._geoProperties[i];
|
|
9158
|
-
else
|
|
9159
|
-
return null;
|
|
9160
|
-
}
|
|
9123
|
+
getGeoProperties(i) { return this._geoProperties[i]; }
|
|
9161
9124
|
setGeoProperties(i, p) { this._geoProperties[i] = p; }
|
|
9162
9125
|
numberOfColumns() { return U.countEnumValues(DistrictField); }
|
|
9163
9126
|
// +1 for dummy unassigned 0 "district" and +1 for N+1 summary "district" for
|
|
@@ -9180,7 +9143,7 @@ class Districts {
|
|
|
9180
9143
|
// TODO - Optimize for getting multiple properties from the same feature?
|
|
9181
9144
|
// TODO - Optimize by only re-calc'ing districts that have changed?
|
|
9182
9145
|
// In this case, special attention to getting county-splits right.
|
|
9183
|
-
// TODO - Optimize by
|
|
9146
|
+
// TODO - Optimize by asyn'ing this?
|
|
9184
9147
|
// TODO - Is there a way to do this programmatically off data? Does it matter?
|
|
9185
9148
|
recalcStatistics(bLog = false) {
|
|
9186
9149
|
// Compute these once per recalc cycle
|
|
@@ -9189,9 +9152,6 @@ class Districts {
|
|
|
9189
9152
|
let planByDistrict = this._session.plan.byDistrictID();
|
|
9190
9153
|
let plan = this._session.plan;
|
|
9191
9154
|
let graph = this._session.graph;
|
|
9192
|
-
// TODO - SPLITTING
|
|
9193
|
-
// Add an extra 0th virtual county bucket for county-district splitting analysis
|
|
9194
|
-
let nCountyBuckets = this._session.counties.nCounties + 1;
|
|
9195
9155
|
// INITIALIZE STATE VALUES THAT WILL BE ACCUMULATED
|
|
9196
9156
|
let stateTPVote = 0;
|
|
9197
9157
|
let stateDemVote = 0;
|
|
@@ -9207,7 +9167,7 @@ class Districts {
|
|
|
9207
9167
|
// NOTE - These plan-level booleans are set in their respective analytics:
|
|
9208
9168
|
// - Equal population (bEqualPop)
|
|
9209
9169
|
// - Complete (bNotEmpty)
|
|
9210
|
-
// -
|
|
9170
|
+
// - Contiguos (bContiguous)
|
|
9211
9171
|
// - Free of holes (bNotEmbedded)
|
|
9212
9172
|
// 2 - MORE ...
|
|
9213
9173
|
// Loop over the districts (including the dummy unassigned one)
|
|
@@ -9215,8 +9175,7 @@ class Districts {
|
|
|
9215
9175
|
// INITIALIZE DISTRICT VALUES THAT WILL BE ACCUMULATED (VS. DERIVED)
|
|
9216
9176
|
let featurePop;
|
|
9217
9177
|
let totalPop = 0;
|
|
9218
|
-
|
|
9219
|
-
let countySplits = U.initArray(nCountyBuckets, 0);
|
|
9178
|
+
let countySplits = U.initArray(this._session.counties.nCounties, 0);
|
|
9220
9179
|
let demVotes = 0;
|
|
9221
9180
|
let repVotes = 0;
|
|
9222
9181
|
let totalVAP = 0;
|
|
@@ -9226,178 +9185,156 @@ class Districts {
|
|
|
9226
9185
|
let pacificPop = 0;
|
|
9227
9186
|
let asianPop = 0;
|
|
9228
9187
|
let nativePop = 0;
|
|
9229
|
-
// NOTE - Only report explicitly found validity issues
|
|
9230
|
-
let bNotEmpty = false;
|
|
9231
|
-
let bContiguous = true;
|
|
9232
|
-
let bNotEmbedded = true;
|
|
9233
|
-
let bEqualPop = true;
|
|
9234
9188
|
// 3 - MORE ...
|
|
9235
9189
|
// HACK - Because "this" gets ghosted inside the forEach loop below
|
|
9236
9190
|
let outerThis = this;
|
|
9237
|
-
// Get the geoIDs assigned to
|
|
9238
|
-
// Guard against empty districts
|
|
9191
|
+
// Get the geoIDs assigned to it ...
|
|
9239
9192
|
let geoIDs = this._session.plan.geoIDsForDistrictID(i);
|
|
9240
|
-
|
|
9241
|
-
|
|
9242
|
-
//
|
|
9243
|
-
|
|
9244
|
-
|
|
9245
|
-
|
|
9246
|
-
|
|
9247
|
-
|
|
9248
|
-
|
|
9249
|
-
|
|
9250
|
-
|
|
9251
|
-
|
|
9252
|
-
|
|
9253
|
-
|
|
9254
|
-
|
|
9255
|
-
|
|
9256
|
-
|
|
9257
|
-
|
|
9258
|
-
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
|
|
9265
|
-
|
|
9266
|
-
|
|
9267
|
-
|
|
9268
|
-
|
|
9269
|
-
|
|
9270
|
-
|
|
9271
|
-
|
|
9272
|
-
|
|
9273
|
-
|
|
9274
|
-
|
|
9275
|
-
|
|
9276
|
-
|
|
9277
|
-
|
|
9278
|
-
|
|
9279
|
-
|
|
9280
|
-
|
|
9281
|
-
|
|
9282
|
-
|
|
9283
|
-
//
|
|
9284
|
-
//
|
|
9285
|
-
|
|
9286
|
-
|
|
9287
|
-
|
|
9288
|
-
|
|
9289
|
-
|
|
9290
|
-
|
|
9291
|
-
|
|
9292
|
-
|
|
9293
|
-
|
|
9294
|
-
|
|
9295
|
-
|
|
9296
|
-
|
|
9297
|
-
|
|
9298
|
-
|
|
9299
|
-
|
|
9300
|
-
|
|
9301
|
-
|
|
9302
|
-
|
|
9303
|
-
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
|
|
9308
|
-
|
|
9309
|
-
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
|
|
9314
|
-
|
|
9315
|
-
|
|
9316
|
-
|
|
9317
|
-
|
|
9318
|
-
|
|
9319
|
-
|
|
9193
|
+
// ... loop over them creating district-by-district statistics
|
|
9194
|
+
geoIDs.forEach(function (geoID) {
|
|
9195
|
+
// Map from geoID to feature index
|
|
9196
|
+
let featureID = outerThis._session.features.featureID(geoID);
|
|
9197
|
+
let f = outerThis._session.features.featureByIndex(featureID);
|
|
9198
|
+
// ACCUMULATE VALUES
|
|
9199
|
+
// Total population of each feature (used more than once)
|
|
9200
|
+
featurePop = outerThis._session.features.fieldForFeature(f, "CENSUS" /* CENSUS */, "Tot" /* TotalPop */);
|
|
9201
|
+
// Total district population
|
|
9202
|
+
totalPop += featurePop;
|
|
9203
|
+
// Total population by counties w/in a district
|
|
9204
|
+
countySplits[outerThis.getCountyIndex(geoID)] += featurePop;
|
|
9205
|
+
// Democratic and Republican vote totals
|
|
9206
|
+
demVotes += outerThis._session.features.fieldForFeature(f, "ELECTION" /* ELECTION */, "D" /* DemVotes */);
|
|
9207
|
+
repVotes += outerThis._session.features.fieldForFeature(f, "ELECTION" /* ELECTION */, "R" /* RepVotes */);
|
|
9208
|
+
// Voting-age demographic breakdowns (or citizen voting-age)
|
|
9209
|
+
// Guard againt null/NaN values
|
|
9210
|
+
let _totalVAP = outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "Tot" /* TotalPop */);
|
|
9211
|
+
let _whitePop = outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "Wh" /* WhitePop */);
|
|
9212
|
+
let _blackPop = outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "BlC" /* BlackPop */);
|
|
9213
|
+
let _hispanicPop = outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "His" /* HispanicPop */);
|
|
9214
|
+
let _pacificPop = outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "PacC" /* PacificPop */);
|
|
9215
|
+
let _asianPop = outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "AsnC" /* AsianPop */);
|
|
9216
|
+
let _nativePop = outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "NatC" /* NativePop */);
|
|
9217
|
+
if (_totalVAP)
|
|
9218
|
+
totalVAP += _totalVAP;
|
|
9219
|
+
if (_whitePop)
|
|
9220
|
+
whitePop += _whitePop;
|
|
9221
|
+
if (_blackPop)
|
|
9222
|
+
blackPop += _blackPop;
|
|
9223
|
+
if (_hispanicPop)
|
|
9224
|
+
hispanicPop += _hispanicPop;
|
|
9225
|
+
if (_pacificPop)
|
|
9226
|
+
pacificPop += _pacificPop;
|
|
9227
|
+
if (_asianPop)
|
|
9228
|
+
asianPop += _asianPop;
|
|
9229
|
+
if (_nativePop)
|
|
9230
|
+
nativePop += _nativePop;
|
|
9231
|
+
// TODO - DELETE
|
|
9232
|
+
// totalVAP += outerThis._session.features.fieldForFeature(f, Dataset.VAP, FeatureField.TotalPop);
|
|
9233
|
+
// whitePop += outerThis._session.features.fieldForFeature(f, Dataset.VAP, FeatureField.WhitePop);
|
|
9234
|
+
// blackPop += outerThis._session.features.fieldForFeature(f, Dataset.VAP, FeatureField.BlackPop);
|
|
9235
|
+
// hispanicPop += outerThis._session.features.fieldForFeature(f, Dataset.VAP, FeatureField.HispanicPop);
|
|
9236
|
+
// pacificPop += outerThis._session.features.fieldForFeature(f, Dataset.VAP, FeatureField.PacificPop);
|
|
9237
|
+
// asianPop += outerThis._session.features.fieldForFeature(f, Dataset.VAP, FeatureField.AsianPop);
|
|
9238
|
+
// nativePop += outerThis._session.features.fieldForFeature(f, Dataset.VAP, FeatureField.NativePop);
|
|
9239
|
+
// 4 - MORE ...
|
|
9240
|
+
});
|
|
9241
|
+
// COMPUTE DERIVED VALUES
|
|
9242
|
+
// Population deviation % and equal population (boolean) by district.
|
|
9243
|
+
// Leave the values null for the dummy unassigned district.
|
|
9244
|
+
let popDevPct = null;
|
|
9245
|
+
let bEqualPop = null;
|
|
9246
|
+
if (i > 0) {
|
|
9247
|
+
popDevPct = (totalPop - targetSize) / targetSize;
|
|
9248
|
+
bEqualPop = (popDevPct <= deviationThreshold);
|
|
9249
|
+
}
|
|
9250
|
+
// Total two-party (not total total!) votes, Democratic and Republican vote
|
|
9251
|
+
// shares, and Democratic first-past-the-post win (= 1) or loss (= 0).
|
|
9252
|
+
let totVotes;
|
|
9253
|
+
let demPct = 0;
|
|
9254
|
+
let repPct = 0;
|
|
9255
|
+
let DemSeat = 0;
|
|
9256
|
+
totVotes = demVotes + repVotes;
|
|
9257
|
+
if (totVotes > 0) {
|
|
9258
|
+
demPct = demVotes / totVotes;
|
|
9259
|
+
repPct = repVotes / totVotes;
|
|
9260
|
+
DemSeat = political_1.fptpWin(demPct);
|
|
9261
|
+
}
|
|
9262
|
+
// Total minority VAP
|
|
9263
|
+
let minorityPop = totalVAP - whitePop;
|
|
9264
|
+
// Voting-age demographic proportions (or citizen voting-age)
|
|
9265
|
+
let whitePct = 0;
|
|
9266
|
+
let minorityPct = 0;
|
|
9267
|
+
let blackPct = 0;
|
|
9268
|
+
let hispanicPct = 0;
|
|
9269
|
+
let pacificPct = 0;
|
|
9270
|
+
let asianPct = 0;
|
|
9271
|
+
let nativePct = 0;
|
|
9272
|
+
if (totalVAP > 0) {
|
|
9273
|
+
whitePct = whitePop / totalVAP;
|
|
9274
|
+
minorityPct = minorityPop / totalVAP;
|
|
9275
|
+
blackPct = blackPop / totalVAP;
|
|
9276
|
+
hispanicPct = hispanicPop / totalVAP;
|
|
9277
|
+
pacificPct = pacificPop / totalVAP;
|
|
9278
|
+
asianPct = asianPop / totalVAP;
|
|
9279
|
+
nativePct = nativePop / totalVAP;
|
|
9280
|
+
}
|
|
9281
|
+
// 5 - MORE ...
|
|
9282
|
+
// COMPUTE DISTRICT-LEVEL VALUES
|
|
9283
|
+
// Validations
|
|
9284
|
+
let bNotEmpty = (!U.isSetEmpty(geoIDs));
|
|
9285
|
+
let bContiguous = null;
|
|
9286
|
+
let bNotEmbedded = null;
|
|
9287
|
+
// Leave the values null for the dummy unassigned district,
|
|
9288
|
+
// and districts that are empty.
|
|
9289
|
+
if (i > 0) {
|
|
9290
|
+
if (bNotEmpty) {
|
|
9320
9291
|
bContiguous = valid_1.isConnected(geoIDs, graph);
|
|
9321
9292
|
bNotEmbedded = (!valid_1.isEmbedded(i, planByDistrict[i], plan, graph));
|
|
9322
9293
|
}
|
|
9323
|
-
// 6 - MORE ...
|
|
9324
|
-
{ // UPDATE THE DISTRICT STATISTICS
|
|
9325
|
-
// NOTE - These are set below for both non-empty & empty districts:
|
|
9326
|
-
// this.statistics[DistrictField.bNotEmpty][i] = bNotEmpty;
|
|
9327
|
-
// this.statistics[DistrictField.bContiguous][i] = bContiguous;
|
|
9328
|
-
// this.statistics[DistrictField.bNotEmbedded][i] = bNotEmbedded;
|
|
9329
|
-
// this.statistics[DistrictField.TotalPop][i] = totalPop;
|
|
9330
|
-
// this.statistics[DistrictField.bEqualPop][i] = bEqualPop;
|
|
9331
|
-
this.statistics[DistrictField.PopDevPct][i] = popDevPct;
|
|
9332
|
-
this.statistics[DistrictField.DemVotes][i] = demVotes;
|
|
9333
|
-
this.statistics[DistrictField.RepVotes][i] = repVotes;
|
|
9334
|
-
this.statistics[DistrictField.TwoPartyVote][i] = totVotes;
|
|
9335
|
-
this.statistics[DistrictField.DemPct][i] = demPct;
|
|
9336
|
-
this.statistics[DistrictField.RepPct][i] = repPct;
|
|
9337
|
-
this.statistics[DistrictField.DemSeat][i] = DemSeat;
|
|
9338
|
-
this.statistics[DistrictField.WhitePop][i] = whitePop;
|
|
9339
|
-
this.statistics[DistrictField.MinorityPop][i] = minorityPop;
|
|
9340
|
-
this.statistics[DistrictField.BlackPop][i] = blackPop;
|
|
9341
|
-
this.statistics[DistrictField.HispanicPop][i] = hispanicPop;
|
|
9342
|
-
this.statistics[DistrictField.PacificPop][i] = pacificPop;
|
|
9343
|
-
this.statistics[DistrictField.AsianPop][i] = asianPop;
|
|
9344
|
-
this.statistics[DistrictField.NativePop][i] = nativePop;
|
|
9345
|
-
this.statistics[DistrictField.TotalVAP][i] = totalVAP;
|
|
9346
|
-
this.statistics[DistrictField.WhitePct][i] = whitePct;
|
|
9347
|
-
this.statistics[DistrictField.MinorityPct][i] = minorityPct;
|
|
9348
|
-
this.statistics[DistrictField.BlackPct][i] = blackPct;
|
|
9349
|
-
this.statistics[DistrictField.HispanicPct][i] = hispanicPct;
|
|
9350
|
-
this.statistics[DistrictField.PacificPct][i] = pacificPct;
|
|
9351
|
-
this.statistics[DistrictField.AsianPct][i] = asianPct;
|
|
9352
|
-
this.statistics[DistrictField.NativePct][i] = nativePct;
|
|
9353
|
-
}
|
|
9354
|
-
// 7 - MORE ...
|
|
9355
|
-
{ // ACCUMULATE STATE STATISTICS FROM DISTRICT TOTALS
|
|
9356
|
-
stateTPVote += totVotes;
|
|
9357
|
-
stateDemVote += demVotes;
|
|
9358
|
-
stateRepVote += repVotes;
|
|
9359
|
-
stateVAPPop += totalVAP;
|
|
9360
|
-
stateWhitePop += whitePop;
|
|
9361
|
-
stateMinorityPop += minorityPop;
|
|
9362
|
-
stateBlackPop += blackPop;
|
|
9363
|
-
stateHispanicPop += hispanicPop;
|
|
9364
|
-
statePacificPop += pacificPop;
|
|
9365
|
-
stateAsianPop += asianPop;
|
|
9366
|
-
stateNativePop += nativePop;
|
|
9367
|
-
}
|
|
9368
|
-
}
|
|
9369
|
-
else { // If a district is empty, zero these results (vs. null)
|
|
9370
|
-
this.statistics[DistrictField.PopDevPct][i] = 0.0;
|
|
9371
|
-
this.statistics[DistrictField.DemVotes][i] = 0;
|
|
9372
|
-
this.statistics[DistrictField.RepVotes][i] = 0;
|
|
9373
|
-
this.statistics[DistrictField.TwoPartyVote][i] = 0;
|
|
9374
|
-
this.statistics[DistrictField.DemPct][i] = 0;
|
|
9375
|
-
this.statistics[DistrictField.RepPct][i] = 0;
|
|
9376
|
-
this.statistics[DistrictField.DemSeat][i] = 0;
|
|
9377
|
-
this.statistics[DistrictField.WhitePop][i] = 0;
|
|
9378
|
-
this.statistics[DistrictField.MinorityPop][i] = 0;
|
|
9379
|
-
this.statistics[DistrictField.BlackPop][i] = 0;
|
|
9380
|
-
this.statistics[DistrictField.HispanicPop][i] = 0;
|
|
9381
|
-
this.statistics[DistrictField.PacificPop][i] = 0;
|
|
9382
|
-
this.statistics[DistrictField.AsianPop][i] = 0;
|
|
9383
|
-
this.statistics[DistrictField.NativePop][i] = 0;
|
|
9384
|
-
this.statistics[DistrictField.TotalVAP][i] = 0;
|
|
9385
|
-
this.statistics[DistrictField.WhitePct][i] = 0;
|
|
9386
|
-
this.statistics[DistrictField.MinorityPct][i] = 0;
|
|
9387
|
-
this.statistics[DistrictField.BlackPct][i] = 0;
|
|
9388
|
-
this.statistics[DistrictField.HispanicPct][i] = 0;
|
|
9389
|
-
this.statistics[DistrictField.PacificPct][i] = 0;
|
|
9390
|
-
this.statistics[DistrictField.AsianPct][i] = 0;
|
|
9391
|
-
this.statistics[DistrictField.NativePct][i] = 0;
|
|
9392
|
-
}
|
|
9393
|
-
{ // UPDATE THESE DISTRICT STATISTICS, EVEN WHEN THEY ARE EMPTY
|
|
9394
|
-
this.statistics[DistrictField.TotalPop][i] = totalPop;
|
|
9395
|
-
this.statistics[DistrictField.bNotEmpty][i] = bNotEmpty;
|
|
9396
|
-
this.statistics[DistrictField.bContiguous][i] = bContiguous;
|
|
9397
|
-
this.statistics[DistrictField.bNotEmbedded][i] = bNotEmbedded;
|
|
9398
|
-
this.statistics[DistrictField.bEqualPop][i] = bEqualPop;
|
|
9399
|
-
this.statistics[DistrictField.CountySplits][i] = countySplits;
|
|
9400
9294
|
}
|
|
9295
|
+
// 6 - MORE ...
|
|
9296
|
+
// UPDATE THE DISTRICT STATISTICS
|
|
9297
|
+
this.statistics[DistrictField.bNotEmpty][i] = bNotEmpty;
|
|
9298
|
+
this.statistics[DistrictField.bContiguous][i] = bContiguous;
|
|
9299
|
+
this.statistics[DistrictField.bNotEmbedded][i] = bNotEmbedded;
|
|
9300
|
+
this.statistics[DistrictField.TotalPop][i] = totalPop;
|
|
9301
|
+
this.statistics[DistrictField.PopDevPct][i] = popDevPct;
|
|
9302
|
+
this.statistics[DistrictField.bEqualPop][i] = bEqualPop;
|
|
9303
|
+
this.statistics[DistrictField.CountySplits][i] = countySplits;
|
|
9304
|
+
this.statistics[DistrictField.DemVotes][i] = demVotes;
|
|
9305
|
+
this.statistics[DistrictField.RepVotes][i] = repVotes;
|
|
9306
|
+
this.statistics[DistrictField.TwoPartyVote][i] = totVotes;
|
|
9307
|
+
this.statistics[DistrictField.DemPct][i] = demPct;
|
|
9308
|
+
this.statistics[DistrictField.RepPct][i] = repPct;
|
|
9309
|
+
this.statistics[DistrictField.DemSeat][i] = DemSeat;
|
|
9310
|
+
this.statistics[DistrictField.WhitePop][i] = whitePop;
|
|
9311
|
+
this.statistics[DistrictField.MinorityPop][i] = minorityPop;
|
|
9312
|
+
this.statistics[DistrictField.BlackPop][i] = blackPop;
|
|
9313
|
+
this.statistics[DistrictField.HispanicPop][i] = hispanicPop;
|
|
9314
|
+
this.statistics[DistrictField.PacificPop][i] = pacificPop;
|
|
9315
|
+
this.statistics[DistrictField.AsianPop][i] = asianPop;
|
|
9316
|
+
this.statistics[DistrictField.NativePop][i] = nativePop;
|
|
9317
|
+
this.statistics[DistrictField.TotalVAP][i] = totalVAP;
|
|
9318
|
+
this.statistics[DistrictField.WhitePct][i] = whitePct;
|
|
9319
|
+
this.statistics[DistrictField.MinorityPct][i] = minorityPct;
|
|
9320
|
+
this.statistics[DistrictField.BlackPct][i] = blackPct;
|
|
9321
|
+
this.statistics[DistrictField.HispanicPct][i] = hispanicPct;
|
|
9322
|
+
this.statistics[DistrictField.PacificPct][i] = pacificPct;
|
|
9323
|
+
this.statistics[DistrictField.AsianPct][i] = asianPct;
|
|
9324
|
+
this.statistics[DistrictField.NativePct][i] = nativePct;
|
|
9325
|
+
// 7 - MORE ...
|
|
9326
|
+
// ACCUMULATE STATE STATISTICS FROM DISTRICT TOTALS
|
|
9327
|
+
stateTPVote += totVotes;
|
|
9328
|
+
stateDemVote += demVotes;
|
|
9329
|
+
stateRepVote += repVotes;
|
|
9330
|
+
stateVAPPop += totalVAP;
|
|
9331
|
+
stateWhitePop += whitePop;
|
|
9332
|
+
stateMinorityPop += minorityPop;
|
|
9333
|
+
stateBlackPop += blackPop;
|
|
9334
|
+
stateHispanicPop += hispanicPop;
|
|
9335
|
+
statePacificPop += pacificPop;
|
|
9336
|
+
stateAsianPop += asianPop;
|
|
9337
|
+
stateNativePop += nativePop;
|
|
9401
9338
|
}
|
|
9402
9339
|
// UPDATE STATE STATISTICS
|
|
9403
9340
|
let summaryRow = this.numberOfRows() - 1;
|
|
@@ -9431,6 +9368,16 @@ class Districts {
|
|
|
9431
9368
|
}
|
|
9432
9369
|
}
|
|
9433
9370
|
exports.Districts = Districts;
|
|
9371
|
+
exports.DatasetDescriptions = {
|
|
9372
|
+
D16F: "2016 ACS Total Population",
|
|
9373
|
+
D16T: "2016 ACS Voting Age Population",
|
|
9374
|
+
E16GPR: "2016 Presidential Election",
|
|
9375
|
+
D10F: "2010 Census Total Population",
|
|
9376
|
+
D10T: "2010 Voting Age Population",
|
|
9377
|
+
C16GCO: "2016 Presidential, US Senate, Governor, and AG election results"
|
|
9378
|
+
// TODO - What other potential datasets?
|
|
9379
|
+
// MORE ...
|
|
9380
|
+
};
|
|
9434
9381
|
// Wrap data by feature, to abstract the specifics of the internal structure
|
|
9435
9382
|
class Features {
|
|
9436
9383
|
constructor(s, data, keys) {
|
|
@@ -9441,18 +9388,15 @@ class Features {
|
|
|
9441
9388
|
}
|
|
9442
9389
|
nFeatures() { return this._data.features.length; }
|
|
9443
9390
|
featureByIndex(i) { return this._data.features[i]; }
|
|
9444
|
-
|
|
9445
|
-
|
|
9446
|
-
let value = f.properties['GEOID10'] || f.properties['GEOID20'] || f.properties['GEOID'];
|
|
9447
|
-
return value;
|
|
9448
|
-
}
|
|
9391
|
+
// TODO - Generalize this
|
|
9392
|
+
geoIDForFeature(f) { return f.properties['GEOID10']; }
|
|
9449
9393
|
fieldForFeature(f, dt, fk) {
|
|
9450
9394
|
let dk = this._keys[dt];
|
|
9451
9395
|
return _getFeatures(f, dk, fk);
|
|
9452
9396
|
}
|
|
9453
9397
|
resetDataset(d, k) {
|
|
9454
9398
|
this._keys[d] = k;
|
|
9455
|
-
// TODO -
|
|
9399
|
+
// TODO - Does anything need to be recalc'd now when a dataset is changed?
|
|
9456
9400
|
}
|
|
9457
9401
|
mapGeoIDsToFeatureIDs() {
|
|
9458
9402
|
for (let i = 0; i < this._session.features.nFeatures(); i++) {
|
|
@@ -9486,15 +9430,12 @@ function _getFeatures(f, datasetKey, p) {
|
|
|
9486
9430
|
return o[p];
|
|
9487
9431
|
}
|
|
9488
9432
|
else {
|
|
9489
|
-
if (o['datasets'] && o['datasets'][datasetKey])
|
|
9490
|
-
|
|
9491
|
-
if ((!(v == null)) && (!(v == undefined)))
|
|
9433
|
+
if (o['datasets'] && o['datasets'][datasetKey])
|
|
9434
|
+
if (o['datasets'][datasetKey][p])
|
|
9492
9435
|
return o['datasets'][datasetKey][p];
|
|
9493
|
-
}
|
|
9494
9436
|
}
|
|
9495
9437
|
}
|
|
9496
9438
|
}
|
|
9497
|
-
console.log(`${p} value undefined for ${f.properties['GEOID10']}!`);
|
|
9498
9439
|
return undefined;
|
|
9499
9440
|
}
|
|
9500
9441
|
function _fGetJoined(f) {
|
|
@@ -9503,13 +9444,13 @@ function _fGetJoined(f) {
|
|
|
9503
9444
|
// Wrap data by county, to abstract the specifics of the internal structure
|
|
9504
9445
|
class Counties {
|
|
9505
9446
|
constructor(s, data) {
|
|
9506
|
-
this._countyNameLookup = {};
|
|
9507
9447
|
this.index = {};
|
|
9508
|
-
this.totalPopulation = [];
|
|
9509
9448
|
this._session = s;
|
|
9510
9449
|
this._data = data;
|
|
9511
9450
|
this.nCounties = this._data.features.length;
|
|
9451
|
+
this._countyNameLookup = {};
|
|
9512
9452
|
}
|
|
9453
|
+
// nCounties(): number { return this._data.features.length; }
|
|
9513
9454
|
countyByIndex(i) { return this._data.features[i]; }
|
|
9514
9455
|
propertyForCounty(f, pk) { return f.properties[pk]; }
|
|
9515
9456
|
mapFIPSToName(fips, name) { this._countyNameLookup[fips] = name; }
|
|
@@ -9538,24 +9479,8 @@ class Plan {
|
|
|
9538
9479
|
this._planByDistrictID = {};
|
|
9539
9480
|
this.districtIDs = []; // Set when the plan in inverted
|
|
9540
9481
|
}
|
|
9541
|
-
// NOTE - DON'T remove water-only features from the plan, as they may be required
|
|
9542
|
-
// for contiguity. Just skip them in aggregating district statistics.
|
|
9543
|
-
// removeWaterOnlyFeatures(plan: T.PlanByGeoID): T.PlanByGeoID {
|
|
9544
|
-
// let newPlan = {} as T.PlanByGeoID;
|
|
9545
|
-
// for (let geoID in plan) {
|
|
9546
|
-
// // Remove water-only features
|
|
9547
|
-
// if (!(U.isWaterOnly(geoID))) {
|
|
9548
|
-
// newPlan[geoID] = plan[geoID];
|
|
9549
|
-
// }
|
|
9550
|
-
// else {
|
|
9551
|
-
// console.log("Removing water-only feature", geoID);
|
|
9552
|
-
// }
|
|
9553
|
-
// }
|
|
9554
|
-
// return newPlan;
|
|
9555
|
-
// }
|
|
9556
9482
|
invertPlan() {
|
|
9557
|
-
|
|
9558
|
-
this._planByDistrictID = invertPlan(this._planByGeoID, this._session);
|
|
9483
|
+
this._planByDistrictID = U.invertPlan(this._planByGeoID);
|
|
9559
9484
|
this.districtIDs = U.getNumericObjectKeys(this._planByDistrictID);
|
|
9560
9485
|
}
|
|
9561
9486
|
initializeDistrict(i) { this._planByDistrictID[i] = new Set(); }
|
|
@@ -9565,49 +9490,12 @@ class Plan {
|
|
|
9565
9490
|
geoIDsForDistrictID(i) { return this._planByDistrictID[i]; }
|
|
9566
9491
|
}
|
|
9567
9492
|
exports.Plan = Plan;
|
|
9568
|
-
// Invert a feature assignment structure to sets of ids by district
|
|
9569
|
-
function invertPlan(plan, s) {
|
|
9570
|
-
let invertedPlan = {};
|
|
9571
|
-
// Add a dummy 'unassigned' district
|
|
9572
|
-
invertedPlan[S.NOT_ASSIGNED] = new Set();
|
|
9573
|
-
// TODO - UNASSIGNED
|
|
9574
|
-
// NOTE - The feature assignments coming from DRA do not include unassigned ones.
|
|
9575
|
-
// - In the DRA-calling context, there's an analytics session with a reference
|
|
9576
|
-
// to the features. Loop over all the features to find the unassigned ones,
|
|
9577
|
-
// and add them to the dummy unassigned district explicitly.
|
|
9578
|
-
// - In the CLI-calling context, there's no session (yet) but the plan is complete.
|
|
9579
|
-
if (!(s == undefined)) {
|
|
9580
|
-
for (let i = 0; i < s.features.nFeatures(); i++) {
|
|
9581
|
-
let f = s.features.featureByIndex(i);
|
|
9582
|
-
let geoID = s.features.geoIDForFeature(f);
|
|
9583
|
-
// If the feature is NOT explicitly assigned to a district, add the geoID
|
|
9584
|
-
// to the dummy unassigned district 0.
|
|
9585
|
-
if (!(U.keyExists(geoID, plan)))
|
|
9586
|
-
invertedPlan[S.NOT_ASSIGNED].add(geoID);
|
|
9587
|
-
// TODO - WATER-ONLY: NOT skipping water-only features here, because we're
|
|
9588
|
-
// not skipping them below when they are explicitly assigned in plans. Should
|
|
9589
|
-
// we skip them in both places?
|
|
9590
|
-
}
|
|
9591
|
-
}
|
|
9592
|
-
for (let geoID in plan) {
|
|
9593
|
-
let districtID = plan[geoID];
|
|
9594
|
-
// Make sure the set for the districtID exists
|
|
9595
|
-
if (!(U.objectContains(invertedPlan, districtID))) {
|
|
9596
|
-
invertedPlan[districtID] = new Set();
|
|
9597
|
-
}
|
|
9598
|
-
// Add the geoID to the districtID's set
|
|
9599
|
-
invertedPlan[districtID].add(geoID);
|
|
9600
|
-
if (U.isWaterOnly(geoID))
|
|
9601
|
-
console.log("Water-only feature still in plan!", geoID);
|
|
9602
|
-
}
|
|
9603
|
-
return invertedPlan;
|
|
9604
|
-
}
|
|
9605
|
-
exports.invertPlan = invertPlan;
|
|
9606
9493
|
class Graph {
|
|
9607
9494
|
constructor(s, graph) {
|
|
9608
9495
|
this._session = s;
|
|
9609
9496
|
this._graph = graph;
|
|
9610
9497
|
}
|
|
9498
|
+
// TODO - Rework this, when we support MIXED MAPS.
|
|
9611
9499
|
peerNeighbors(node) {
|
|
9612
9500
|
// Get the neighboring geoIDs connected to a geoID
|
|
9613
9501
|
// Ignore the lengths of the shared borders (the values), for now
|
|
@@ -9644,20 +9532,19 @@ function doAnalyzeDistricts(s, bLog = false) {
|
|
|
9644
9532
|
s.districts.extractDistrictShapeProperties(bLog);
|
|
9645
9533
|
}
|
|
9646
9534
|
exports.doAnalyzeDistricts = doAnalyzeDistricts;
|
|
9535
|
+
// TODO - I could make this table-driven, but I'm thinking that the explicit
|
|
9536
|
+
// calls might make chunking for aync easier.
|
|
9647
9537
|
// Calculate the analytics & validations and cache the results
|
|
9648
9538
|
// NOTE - doAnalyzePlan() depends on doAnalyzeDistricts() having run first.
|
|
9649
|
-
// NOTE - I could make this table-driven, but I'm thinking that the explicit
|
|
9650
|
-
// calls might make chunking for aync easier.
|
|
9651
9539
|
function doAnalyzePlan(s, bLog = false) {
|
|
9652
|
-
// TODO - Remove this mechanism. Always run all tests
|
|
9653
9540
|
// Get the requested suites, and only execute those tests
|
|
9654
9541
|
let requestedSuites = s.config['suites'];
|
|
9655
9542
|
// Tests in the "Legal" suite, i.e., pass/ fail constraints
|
|
9656
9543
|
if (requestedSuites.includes(0 /* Legal */)) {
|
|
9657
|
-
s.tests[0 /* Complete */] = valid_1.doIsComplete(s
|
|
9658
|
-
s.tests[1 /* Contiguous */] = valid_1.doIsContiguous(s
|
|
9659
|
-
s.tests[2 /* FreeOfHoles */] = valid_1.doIsFreeOfHoles(s
|
|
9660
|
-
s.tests[4 /* PopulationDeviation */] = equal_1.doPopulationDeviation(s
|
|
9544
|
+
s.tests[0 /* Complete */] = valid_1.doIsComplete(s);
|
|
9545
|
+
s.tests[1 /* Contiguous */] = valid_1.doIsContiguous(s);
|
|
9546
|
+
s.tests[2 /* FreeOfHoles */] = valid_1.doIsFreeOfHoles(s);
|
|
9547
|
+
s.tests[4 /* PopulationDeviation */] = equal_1.doPopulationDeviation(s);
|
|
9661
9548
|
// NOTE - I can't check whether a population deviation is legal or not, until
|
|
9662
9549
|
// the raw % is normalized. A zero (0) would mean "too much" / "not legal," for
|
|
9663
9550
|
// the given type of district (CD vs. LD). The EqualPopulation test is derived
|
|
@@ -9667,21 +9554,19 @@ function doAnalyzePlan(s, bLog = false) {
|
|
|
9667
9554
|
}
|
|
9668
9555
|
// Tests in the "Fair" suite
|
|
9669
9556
|
if (requestedSuites.includes(1 /* Fair */)) {
|
|
9670
|
-
s.tests[
|
|
9671
|
-
s.tests[
|
|
9672
|
-
s.tests[
|
|
9673
|
-
s.tests[
|
|
9674
|
-
s.tests[
|
|
9675
|
-
s.tests[
|
|
9557
|
+
s.tests[9 /* SeatsBias */] = political_1.doSeatsBias(s);
|
|
9558
|
+
s.tests[10 /* VotesBias */] = political_1.doVotesBias(s);
|
|
9559
|
+
s.tests[11 /* Responsiveness */] = political_1.doResponsiveness(s);
|
|
9560
|
+
s.tests[12 /* ResponsiveDistricts */] = political_1.doResponsiveDistricts(s);
|
|
9561
|
+
s.tests[13 /* EfficiencyGap */] = political_1.doEfficiencyGap(s);
|
|
9562
|
+
s.tests[14 /* MajorityMinorityDistricts */] = minority_1.doMajorityMinorityDistricts(s);
|
|
9676
9563
|
}
|
|
9677
9564
|
// Tests in the "Best" suite, i.e., criteria for better/worse
|
|
9678
9565
|
if (requestedSuites.includes(2 /* Best */)) {
|
|
9679
9566
|
s.tests[5 /* Reock */] = compact_1.doReock(s, bLog);
|
|
9680
9567
|
s.tests[6 /* PolsbyPopper */] = compact_1.doPolsbyPopper(s, bLog);
|
|
9681
|
-
s.tests[7 /*
|
|
9682
|
-
s.tests[
|
|
9683
|
-
s.tests[8 /* CountySplitting */] = cohesive_1.doCountySplitting(s, bLog);
|
|
9684
|
-
s.tests[9 /* DistrictSplitting */] = cohesive_1.doDistrictSplitting(s, bLog);
|
|
9568
|
+
s.tests[7 /* CountySplits */] = cohesive_1.doCountySplits(s);
|
|
9569
|
+
s.tests[8 /* Complexity */] = cohesive_1.doPlanComplexity(s);
|
|
9685
9570
|
}
|
|
9686
9571
|
// Enable a Test Log and Scorecard to be generated
|
|
9687
9572
|
s.bPlanAnalyzed = true;
|
|
@@ -9694,8 +9579,8 @@ exports.doAnalyzePlan = doAnalyzePlan;
|
|
|
9694
9579
|
//
|
|
9695
9580
|
// NOTE - Should this be conditionalized on the test suites requested?
|
|
9696
9581
|
// Those are encapsulated in reports.ts right now, so not doing that.
|
|
9697
|
-
function doDeriveSecondaryTests(s
|
|
9698
|
-
s.tests[3 /* EqualPopulation */] = equal_1.doHasEqualPopulations(s
|
|
9582
|
+
function doDeriveSecondaryTests(s) {
|
|
9583
|
+
s.tests[3 /* EqualPopulation */] = equal_1.doHasEqualPopulations(s);
|
|
9699
9584
|
}
|
|
9700
9585
|
exports.doDeriveSecondaryTests = doDeriveSecondaryTests;
|
|
9701
9586
|
|
|
@@ -9712,237 +9597,19 @@ exports.doDeriveSecondaryTests = doDeriveSecondaryTests;
|
|
|
9712
9597
|
"use strict";
|
|
9713
9598
|
|
|
9714
9599
|
//
|
|
9715
|
-
//
|
|
9600
|
+
// "COHESIVE" - We're naming this category which is about county splitting.
|
|
9716
9601
|
//
|
|
9717
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
9718
|
-
if (mod && mod.__esModule) return mod;
|
|
9719
|
-
var result = {};
|
|
9720
|
-
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
9721
|
-
result["default"] = mod;
|
|
9722
|
-
return result;
|
|
9723
|
-
};
|
|
9724
9602
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9725
|
-
const U =
|
|
9726
|
-
const
|
|
9727
|
-
|
|
9728
|
-
|
|
9729
|
-
function doCountySplitting(s, bLog = false) {
|
|
9730
|
-
let test = s.getTest(8 /* CountySplitting */);
|
|
9731
|
-
let CxD = s.districts.statistics[D.DistrictField.CountySplits].slice(0, -1);
|
|
9732
|
-
let countyTotals = s.counties.totalPopulation;
|
|
9733
|
-
let districtTotals = s.districts.statistics[D.DistrictField.TotalPop].slice(0, -1);
|
|
9734
|
-
let f = calcCountyFractions(CxD, countyTotals);
|
|
9735
|
-
let w = calcCountyWeights(countyTotals);
|
|
9736
|
-
let SqEnt_DC = countySplitting(f, w, bLog);
|
|
9737
|
-
let CxDreducedC = U.deepCopy(CxD);
|
|
9738
|
-
reduceCSplits(CxDreducedC, districtTotals);
|
|
9739
|
-
let fReduced = calcCountyFractions(CxDreducedC, countyTotals);
|
|
9740
|
-
let wReduced = calcCountyWeights(countyTotals);
|
|
9741
|
-
let SqEnt_DCreduced = countySplitting(fReduced, wReduced, bLog);
|
|
9742
|
-
test['score'] = SqEnt_DCreduced;
|
|
9743
|
-
test['details']['SqEnt_DC'] = SqEnt_DC;
|
|
9744
|
-
return test;
|
|
9745
|
-
}
|
|
9746
|
-
exports.doCountySplitting = doCountySplitting;
|
|
9747
|
-
function doDistrictSplitting(s, bLog = false) {
|
|
9748
|
-
let test = s.getTest(9 /* DistrictSplitting */);
|
|
9749
|
-
let CxD = s.districts.statistics[D.DistrictField.CountySplits].slice(0, -1);
|
|
9750
|
-
let countyTotals = s.counties.totalPopulation;
|
|
9751
|
-
let districtTotals = s.districts.statistics[D.DistrictField.TotalPop].slice(0, -1);
|
|
9752
|
-
let g = calcDistrictFractions(CxD, districtTotals);
|
|
9753
|
-
let x = calcDistrictWeights(districtTotals);
|
|
9754
|
-
let SqEnt_CD = districtSplitting(g, x, bLog);
|
|
9755
|
-
let CxDreducedD = U.deepCopy(CxD);
|
|
9756
|
-
reduceDSplits(CxDreducedD, countyTotals);
|
|
9757
|
-
let gReduced = calcDistrictFractions(CxDreducedD, districtTotals);
|
|
9758
|
-
let xReduced = calcDistrictWeights(districtTotals);
|
|
9759
|
-
let SqEnt_CDreduced = districtSplitting(gReduced, xReduced, bLog);
|
|
9760
|
-
test['score'] = SqEnt_CDreduced;
|
|
9761
|
-
test['details']['SqEnt_CD'] = SqEnt_CD;
|
|
9762
|
-
return test;
|
|
9763
|
-
}
|
|
9764
|
-
exports.doDistrictSplitting = doDistrictSplitting;
|
|
9765
|
-
// HELPERS
|
|
9766
|
-
// Loop over all the county-district combos, skipping the virtual district 0
|
|
9767
|
-
// and virtual county 0.
|
|
9768
|
-
//
|
|
9769
|
-
// NOTE - The county-district splits and the county & district totals may all,
|
|
9770
|
-
// in general, be fractional/decimal numbers as opposed to integers, due to
|
|
9771
|
-
// dissaggregation & re-aggregation. Hence, comparisons need to approximate
|
|
9772
|
-
// equality.
|
|
9773
|
-
// Consolidate districts (rows) consisting of just one county (column)
|
|
9774
|
-
// UP into the dummy district (0).
|
|
9775
|
-
function reduceCSplits(CxDreducedC, districtTotals) {
|
|
9776
|
-
let nD = CxDreducedC.length;
|
|
9777
|
-
let nC = CxDreducedC[0].length;
|
|
9778
|
-
for (let j = 1; j < nC; j++) {
|
|
9779
|
-
for (let i = 1; i < nD; i++) {
|
|
9780
|
-
let split_total = CxDreducedC[i][j];
|
|
9781
|
-
if (split_total > 0) {
|
|
9782
|
-
if (areRoughlyEqual(split_total, districtTotals[i])) {
|
|
9783
|
-
CxDreducedC[0][j] += split_total;
|
|
9784
|
-
CxDreducedC[i][j] = 0;
|
|
9785
|
-
}
|
|
9786
|
-
}
|
|
9787
|
-
}
|
|
9788
|
-
}
|
|
9789
|
-
}
|
|
9790
|
-
// Consolidate whole counties (columns) in a district (row) LEFT into the
|
|
9791
|
-
// dummy county (0).
|
|
9792
|
-
function reduceDSplits(CxDreducedD, countyTotals) {
|
|
9793
|
-
let nD = CxDreducedD.length;
|
|
9794
|
-
let nC = CxDreducedD[0].length;
|
|
9795
|
-
for (let i = 1; i < nD; i++) {
|
|
9796
|
-
for (let j = 1; j < nC; j++) {
|
|
9797
|
-
let split_total = CxDreducedD[i][j];
|
|
9798
|
-
if (split_total > 0) {
|
|
9799
|
-
if (areRoughlyEqual(split_total, countyTotals[j])) {
|
|
9800
|
-
CxDreducedD[i][0] += split_total;
|
|
9801
|
-
CxDreducedD[i][j] = 0;
|
|
9802
|
-
}
|
|
9803
|
-
}
|
|
9804
|
-
}
|
|
9805
|
-
}
|
|
9806
|
-
}
|
|
9807
|
-
function calcCountyWeights(countyTotals) {
|
|
9808
|
-
let nC = countyTotals.length;
|
|
9809
|
-
let cTotal = U.sumArray(countyTotals);
|
|
9810
|
-
let w = U.initArray(nC, 0.0);
|
|
9811
|
-
for (let j = 0; j < nC; j++) {
|
|
9812
|
-
w[j] = countyTotals[j] / cTotal;
|
|
9813
|
-
}
|
|
9814
|
-
return w;
|
|
9815
|
-
}
|
|
9816
|
-
function calcDistrictWeights(districtTotals) {
|
|
9817
|
-
let nD = districtTotals.length;
|
|
9818
|
-
let dTotal = U.sumArray(districtTotals);
|
|
9819
|
-
let x = U.initArray(nD, 0.0);
|
|
9820
|
-
for (let i = 0; i < nD; i++) {
|
|
9821
|
-
x[i] = districtTotals[i] / dTotal;
|
|
9822
|
-
}
|
|
9823
|
-
return x;
|
|
9824
|
-
}
|
|
9825
|
-
function calcCountyFractions(CxDreducedD, countyTotals) {
|
|
9826
|
-
let nD = CxDreducedD.length;
|
|
9827
|
-
let nC = CxDreducedD[0].length;
|
|
9828
|
-
let f = new Array(nD).fill(0.0).map(() => new Array(nC).fill(0.0));
|
|
9829
|
-
for (let j = 0; j < nC; j++) {
|
|
9830
|
-
for (let i = 0; i < nD; i++) {
|
|
9831
|
-
if (countyTotals[j] > 0) {
|
|
9832
|
-
f[i][j] = CxDreducedD[i][j] / countyTotals[j];
|
|
9833
|
-
}
|
|
9834
|
-
else {
|
|
9835
|
-
f[i][j] = 0.0;
|
|
9836
|
-
}
|
|
9837
|
-
}
|
|
9838
|
-
}
|
|
9839
|
-
return f;
|
|
9840
|
-
}
|
|
9841
|
-
function calcDistrictFractions(CxDreducedC, districtTotals) {
|
|
9842
|
-
let nD = CxDreducedC.length;
|
|
9843
|
-
let nC = CxDreducedC[0].length;
|
|
9844
|
-
let g = new Array(nD).fill(0.0).map(() => new Array(nC).fill(0.0));
|
|
9845
|
-
for (let j = 0; j < nC; j++) {
|
|
9846
|
-
for (let i = 0; i < nD; i++) {
|
|
9847
|
-
if (districtTotals[i] > 0) {
|
|
9848
|
-
g[i][j] = CxDreducedC[i][j] / districtTotals[i];
|
|
9849
|
-
}
|
|
9850
|
-
else {
|
|
9851
|
-
g[i][j] = 0.0;
|
|
9852
|
-
}
|
|
9853
|
-
}
|
|
9854
|
-
}
|
|
9855
|
-
return g;
|
|
9856
|
-
}
|
|
9857
|
-
// Deal with decimal census "counts" due to disagg/re-agg
|
|
9858
|
-
function areRoughlyEqual(x, y) {
|
|
9859
|
-
let delta = Math.abs(x - y);
|
|
9860
|
-
let result = (delta < S.EQUAL_TOLERANCE) ? true : false;
|
|
9861
|
-
return result;
|
|
9862
|
-
}
|
|
9863
|
-
// For all districts in a county, sum the split score.
|
|
9864
|
-
function countySplitScore(j, f, numD, bLog = false) {
|
|
9865
|
-
let e = 0.0;
|
|
9866
|
-
for (let i = 0; i < numD; i++) {
|
|
9867
|
-
e += Math.sqrt(f[i][j]);
|
|
9868
|
-
}
|
|
9869
|
-
return e;
|
|
9870
|
-
}
|
|
9871
|
-
// For all counties, sum the weighted county splits.
|
|
9872
|
-
function countySplitting(f, w, bLog = false) {
|
|
9873
|
-
let numC = f[0].length;
|
|
9874
|
-
let numD = f.length;
|
|
9875
|
-
let e = 0.0;
|
|
9876
|
-
for (let j = 0; j < numC; j++) {
|
|
9877
|
-
let splitScore = countySplitScore(j, f, numD, bLog);
|
|
9878
|
-
e += w[j] * splitScore;
|
|
9879
|
-
if (bLog)
|
|
9880
|
-
console.log("County splitting =", j, w[j], splitScore, e);
|
|
9881
|
-
}
|
|
9882
|
-
return U.trim(e, 3);
|
|
9883
|
-
}
|
|
9884
|
-
// For all counties in a district, sum the split score.
|
|
9885
|
-
function districtSplitScore(i, g, numC, bLog = false) {
|
|
9886
|
-
let e = 0.0;
|
|
9887
|
-
for (let j = 0; j < numC; j++) {
|
|
9888
|
-
e += Math.sqrt(g[i][j]);
|
|
9889
|
-
}
|
|
9890
|
-
return e;
|
|
9891
|
-
}
|
|
9892
|
-
// For all districts, sum the weighted district splits.
|
|
9893
|
-
function districtSplitting(g, x, bLog = false) {
|
|
9894
|
-
let numC = g[0].length;
|
|
9895
|
-
let numD = g.length;
|
|
9896
|
-
let e = 0.0;
|
|
9897
|
-
for (let i = 0; i < numD; i++) {
|
|
9898
|
-
let splitScore = districtSplitScore(i, g, numC, bLog);
|
|
9899
|
-
e += x[i] * splitScore;
|
|
9900
|
-
if (bLog)
|
|
9901
|
-
console.log("District split score =", i, x[i], splitScore, e);
|
|
9902
|
-
}
|
|
9903
|
-
return U.trim(e, 3);
|
|
9904
|
-
}
|
|
9905
|
-
// ANALYZE SIMPLE COUNTY & VTD SPLITTING
|
|
9906
|
-
/*
|
|
9907
|
-
|
|
9908
|
-
Sample results for NC 2016 dongressional plan
|
|
9909
|
-
________________________________________________________________________________
|
|
9910
|
-
|
|
9911
|
-
State: NC
|
|
9912
|
-
Census: 2010
|
|
9913
|
-
Total population: 9,535,483
|
|
9914
|
-
Number of districts: 13
|
|
9915
|
-
Target district size: 733,499
|
|
9916
|
-
Number of counties: 100
|
|
9917
|
-
|
|
9918
|
-
Equal Population: 11.24% deviation
|
|
9919
|
-
Compactness: None
|
|
9920
|
-
Proportionality: 27.67% gap
|
|
9921
|
-
Cohesiveness: 11 unexpected splits, affecting 27.14% of the total population
|
|
9922
|
-
|
|
9923
|
-
These counties are split unexpectedly:
|
|
9924
|
-
|
|
9925
|
-
• Bladen
|
|
9926
|
-
• Buncombe
|
|
9927
|
-
• Catawba
|
|
9928
|
-
• Cumberland
|
|
9929
|
-
• Durham
|
|
9930
|
-
• Guilford
|
|
9931
|
-
• Iredell
|
|
9932
|
-
• Johnston
|
|
9933
|
-
• Pitt
|
|
9934
|
-
• Rowan
|
|
9935
|
-
• Wilson
|
|
9936
|
-
|
|
9937
|
-
*/
|
|
9938
|
-
function doFindCountiesSplitUnexpectedly(s, bLog = false) {
|
|
9939
|
-
let test = s.getTest(7 /* UnexpectedCountySplits */);
|
|
9603
|
+
const U = __webpack_require__(/*! ./utils */ "./src/utils.ts");
|
|
9604
|
+
const D = __webpack_require__(/*! ./_data */ "./src/_data.ts");
|
|
9605
|
+
function doCountySplits(s) {
|
|
9606
|
+
let test = s.getTest(7 /* CountySplits */);
|
|
9940
9607
|
// THE THREE VALUES TO DETERMINE FOR A PLAN
|
|
9941
9608
|
let unexpectedSplits = 0;
|
|
9942
9609
|
let unexpectedAffected = 0;
|
|
9943
9610
|
let countiesSplitUnexpectedly = [];
|
|
9944
|
-
// FIRST, ANALYZE THE COUNTY
|
|
9945
|
-
//
|
|
9611
|
+
// FIRST, ANALYZE THE COUNTY SPLITING FOR THE PLAN
|
|
9612
|
+
// Pivot census totals into county-district "splits"
|
|
9946
9613
|
let countiesByDistrict = s.districts.statistics[D.DistrictField.CountySplits];
|
|
9947
9614
|
// countiesByDistrict = countiesByDistrict.slice(1, -1);
|
|
9948
9615
|
// Find the single-county districts, i.e., districts NOT split across counties.
|
|
@@ -9952,13 +9619,10 @@ function doFindCountiesSplitUnexpectedly(s, bLog = false) {
|
|
|
9952
9619
|
// See if there's only one county partition
|
|
9953
9620
|
let nCountiesInDistrict = 0;
|
|
9954
9621
|
for (let c = 0; c < s.counties.nCounties; c++) {
|
|
9955
|
-
|
|
9956
|
-
|
|
9957
|
-
if (
|
|
9958
|
-
|
|
9959
|
-
if (nCountiesInDistrict > 1) {
|
|
9960
|
-
break;
|
|
9961
|
-
}
|
|
9622
|
+
if (countiesByDistrict[d][c] > 0) {
|
|
9623
|
+
nCountiesInDistrict += 1;
|
|
9624
|
+
if (nCountiesInDistrict > 1) {
|
|
9625
|
+
break;
|
|
9962
9626
|
}
|
|
9963
9627
|
}
|
|
9964
9628
|
}
|
|
@@ -9978,14 +9642,11 @@ function doFindCountiesSplitUnexpectedly(s, bLog = false) {
|
|
|
9978
9642
|
let nCountyParts = 0;
|
|
9979
9643
|
let subtotal = 0;
|
|
9980
9644
|
for (let d = 1; d <= s.state.nDistricts; d++) {
|
|
9981
|
-
|
|
9982
|
-
|
|
9983
|
-
|
|
9984
|
-
|
|
9985
|
-
|
|
9986
|
-
if (!(U.arrayContains(singleCountyDistricts, d))) {
|
|
9987
|
-
subtotal += countiesByDistrict[d][c];
|
|
9988
|
-
}
|
|
9645
|
+
if (countiesByDistrict[d][c] > 0) {
|
|
9646
|
+
nPartitionsOverall += 1;
|
|
9647
|
+
nCountyParts += 1;
|
|
9648
|
+
if (!(U.arrayContains(singleCountyDistricts, d))) {
|
|
9649
|
+
subtotal += countiesByDistrict[d][c];
|
|
9989
9650
|
}
|
|
9990
9651
|
}
|
|
9991
9652
|
}
|
|
@@ -10023,21 +9684,48 @@ function doFindCountiesSplitUnexpectedly(s, bLog = false) {
|
|
|
10023
9684
|
countiesSplitUnexpectedly.push(s.counties.nameFromFIPS(fips));
|
|
10024
9685
|
}
|
|
10025
9686
|
countiesSplitUnexpectedly = countiesSplitUnexpectedly.sort();
|
|
10026
|
-
|
|
10027
|
-
test['
|
|
10028
|
-
test['details']
|
|
9687
|
+
// Cache the results in the test
|
|
9688
|
+
test['score'] = unexpectedAffected; // TODO - Use Moon's complexity metric here
|
|
9689
|
+
test['details'] = {
|
|
9690
|
+
'unexpectedSplits': unexpectedSplits,
|
|
9691
|
+
'unexpectedAffected': unexpectedAffected,
|
|
9692
|
+
'countiesSplitUnexpectedly': countiesSplitUnexpectedly
|
|
9693
|
+
};
|
|
10029
9694
|
return test;
|
|
10030
9695
|
}
|
|
10031
|
-
exports.
|
|
10032
|
-
|
|
10033
|
-
|
|
10034
|
-
|
|
10035
|
-
|
|
10036
|
-
|
|
10037
|
-
|
|
9696
|
+
exports.doCountySplits = doCountySplits;
|
|
9697
|
+
// 2 - THE COMPLEXITY ANALYTIC NEEDS THE FOLLOWING DATA:
|
|
9698
|
+
//
|
|
9699
|
+
// If a map is already in simplified (mixed) form, the complexity analytic needs
|
|
9700
|
+
// two pieces of data:
|
|
9701
|
+
// - The counts of features by summary level--i.e., the numbers of counties, tracts,
|
|
9702
|
+
// block groups, and blocks in a state; and
|
|
9703
|
+
// - The map -- So it can count the features by summary level in the map,
|
|
9704
|
+
// as well as the number of BG’s that are split.
|
|
9705
|
+
//
|
|
9706
|
+
// TODO - Where would the state counts come from? Preprocessed and passed in, or
|
|
9707
|
+
// done in a one-time initialization call (which would require a full set of
|
|
9708
|
+
// block geo_id's for the state).
|
|
9709
|
+
//
|
|
9710
|
+
// However, if a map is not yet (fully) simplified, then determining the
|
|
9711
|
+
// complexity of a map also requires a preprocessed summary level hierarchy, so
|
|
9712
|
+
// you can get the child features (e.g., tracts) of a parent feature (e.g.,
|
|
9713
|
+
// a county).
|
|
9714
|
+
//
|
|
9715
|
+
// NOTE - I have script for producing this hierarchy which we could repurpose.
|
|
9716
|
+
//
|
|
9717
|
+
// TODO - For mixed map processing--specfically to find the neighbors of a feature
|
|
9718
|
+
// that are actually in the map (as opposed to just neighbors at the same
|
|
9719
|
+
// summary level in the static graph)--you need a special hierarchy that
|
|
9720
|
+
// distinguishes between the 'interior' and 'edge children of a feature.
|
|
9721
|
+
//
|
|
9722
|
+
// NOTE - The script noted above does this.
|
|
9723
|
+
function doPlanComplexity(s) {
|
|
9724
|
+
let test = s.getTest(8 /* Complexity */);
|
|
9725
|
+
console.log("TODO - Calculating plan complexity ...");
|
|
10038
9726
|
return test;
|
|
10039
9727
|
}
|
|
10040
|
-
exports.
|
|
9728
|
+
exports.doPlanComplexity = doPlanComplexity;
|
|
10041
9729
|
|
|
10042
9730
|
|
|
10043
9731
|
/***/ }),
|
|
@@ -10054,17 +9742,9 @@ exports.doFindSplitVTDs = doFindSplitVTDs;
|
|
|
10054
9742
|
//
|
|
10055
9743
|
// COMPACT
|
|
10056
9744
|
//
|
|
10057
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
10058
|
-
if (mod && mod.__esModule) return mod;
|
|
10059
|
-
var result = {};
|
|
10060
|
-
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
10061
|
-
result["default"] = mod;
|
|
10062
|
-
return result;
|
|
10063
|
-
};
|
|
10064
9745
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10065
|
-
const Poly = __importStar(__webpack_require__(/*! @dra2020/poly */ "./node_modules/@dra2020/poly/dist/poly.js"));
|
|
10066
9746
|
const geofeature_1 = __webpack_require__(/*! ./geofeature */ "./src/geofeature.ts");
|
|
10067
|
-
const U =
|
|
9747
|
+
const U = __webpack_require__(/*! ./utils */ "./src/utils.ts");
|
|
10068
9748
|
// Measures of compactness compare district shapes to various ideally compact
|
|
10069
9749
|
// benchmarks, such as circles. All else equal, more compact districts are better.
|
|
10070
9750
|
//
|
|
@@ -10152,19 +9832,16 @@ function doReock(s, bLog = false) {
|
|
|
10152
9832
|
let scores = [];
|
|
10153
9833
|
for (let districtID = 1; districtID <= s.state.nDistricts; districtID++) {
|
|
10154
9834
|
let districtProps = s.districts.getGeoProperties(districtID);
|
|
10155
|
-
|
|
10156
|
-
|
|
10157
|
-
|
|
10158
|
-
|
|
10159
|
-
|
|
10160
|
-
|
|
10161
|
-
|
|
10162
|
-
|
|
10163
|
-
|
|
10164
|
-
|
|
10165
|
-
}
|
|
10166
|
-
}
|
|
10167
|
-
// Populate the test entry ... for the shapes that exist!
|
|
9835
|
+
let a = districtProps[0 /* Area */];
|
|
9836
|
+
let d = districtProps[1 /* Diameter */];
|
|
9837
|
+
let reock = (4 * a) / (Math.PI * Math.pow(d, 2));
|
|
9838
|
+
// Save each district score
|
|
9839
|
+
scores.push(reock);
|
|
9840
|
+
// Echo the results by district
|
|
9841
|
+
if (bLog)
|
|
9842
|
+
console.log("Reock for district", districtID, "=", reock);
|
|
9843
|
+
}
|
|
9844
|
+
// Populate the test entry
|
|
10168
9845
|
let averageReock = U.avgArray(scores);
|
|
10169
9846
|
test['score'] = U.trim(averageReock);
|
|
10170
9847
|
test['details'] = {}; // TODO - Any details?
|
|
@@ -10180,19 +9857,16 @@ function doPolsbyPopper(s, bLog = false) {
|
|
|
10180
9857
|
let scores = [];
|
|
10181
9858
|
for (let districtID = 1; districtID <= s.state.nDistricts; districtID++) {
|
|
10182
9859
|
let districtProps = s.districts.getGeoProperties(districtID);
|
|
10183
|
-
|
|
10184
|
-
|
|
10185
|
-
|
|
10186
|
-
|
|
10187
|
-
|
|
10188
|
-
|
|
10189
|
-
|
|
10190
|
-
|
|
10191
|
-
|
|
10192
|
-
|
|
10193
|
-
}
|
|
10194
|
-
}
|
|
10195
|
-
// Populate the test entry ... for the shapes that exist!
|
|
9860
|
+
let a = districtProps[0 /* Area */];
|
|
9861
|
+
let p = districtProps[2 /* Perimeter */];
|
|
9862
|
+
let polsbyPopper = (4 * Math.PI) * (a / Math.pow(p, 2));
|
|
9863
|
+
// Save each district score
|
|
9864
|
+
scores.push(polsbyPopper);
|
|
9865
|
+
// Echo the results by district
|
|
9866
|
+
if (bLog)
|
|
9867
|
+
console.log("Polsby-Popper for district", districtID, "=", polsbyPopper);
|
|
9868
|
+
}
|
|
9869
|
+
// Populate the test entry
|
|
10196
9870
|
let averagePolsbyPopper = U.avgArray(scores);
|
|
10197
9871
|
test['score'] = U.trim(averagePolsbyPopper);
|
|
10198
9872
|
test['details'] = {}; // TODO - Any details?
|
|
@@ -10202,24 +9876,19 @@ exports.doPolsbyPopper = doPolsbyPopper;
|
|
|
10202
9876
|
// HELPER TO EXTRACT PROPERTIES OF DISTRICT SHAPES
|
|
10203
9877
|
function extractDistrictProperties(s, bLog = false) {
|
|
10204
9878
|
for (let i = 1; i <= s.state.nDistricts; i++) {
|
|
10205
|
-
let j = i - 1; // TODO -
|
|
9879
|
+
let j = i - 1; // TODO - Terry: How do you get away w/o this?!?
|
|
10206
9880
|
let poly = s.districts.getShape(j);
|
|
10207
|
-
//
|
|
10208
|
-
let
|
|
10209
|
-
let
|
|
10210
|
-
|
|
10211
|
-
|
|
10212
|
-
|
|
10213
|
-
|
|
10214
|
-
|
|
10215
|
-
|
|
10216
|
-
|
|
10217
|
-
|
|
10218
|
-
props[2 /* Perimeter */] = perimeter;
|
|
10219
|
-
s.districts.setGeoProperties(i, props);
|
|
10220
|
-
if (bLog)
|
|
10221
|
-
console.log("District", i, "A =", area, "P =", perimeter, "D =", diameter);
|
|
10222
|
-
}
|
|
9881
|
+
// TODO - Bundle these calls?
|
|
9882
|
+
let area = geofeature_1.gfArea(poly);
|
|
9883
|
+
let perimeter = geofeature_1.gfPerimeter(poly);
|
|
9884
|
+
let diameter = geofeature_1.gfDiameter(poly);
|
|
9885
|
+
let props = [0, 0, 0]; // TODO - Terry?!?
|
|
9886
|
+
props[0 /* Area */] = area;
|
|
9887
|
+
props[1 /* Diameter */] = diameter;
|
|
9888
|
+
props[2 /* Perimeter */] = perimeter;
|
|
9889
|
+
s.districts.setGeoProperties(i, props);
|
|
9890
|
+
if (bLog)
|
|
9891
|
+
console.log("District", i, "A =", area, "P =", perimeter, "D =", diameter);
|
|
10223
9892
|
}
|
|
10224
9893
|
}
|
|
10225
9894
|
exports.extractDistrictProperties = extractDistrictProperties;
|
|
@@ -10267,17 +9936,10 @@ exports.extractDistrictProperties = extractDistrictProperties;
|
|
|
10267
9936
|
//
|
|
10268
9937
|
// EQUAL POPULATION
|
|
10269
9938
|
//
|
|
10270
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
10271
|
-
if (mod && mod.__esModule) return mod;
|
|
10272
|
-
var result = {};
|
|
10273
|
-
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
10274
|
-
result["default"] = mod;
|
|
10275
|
-
return result;
|
|
10276
|
-
};
|
|
10277
9939
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10278
|
-
const U =
|
|
10279
|
-
const D =
|
|
10280
|
-
function doPopulationDeviation(s
|
|
9940
|
+
const U = __webpack_require__(/*! ./utils */ "./src/utils.ts");
|
|
9941
|
+
const D = __webpack_require__(/*! ./_data */ "./src/_data.ts");
|
|
9942
|
+
function doPopulationDeviation(s) {
|
|
10281
9943
|
let test = s.getTest(4 /* PopulationDeviation */);
|
|
10282
9944
|
// Compute the min, max, and average district populations,
|
|
10283
9945
|
// excluding the dummy 'unassigned' 0 and N+1 summary "districts."
|
|
@@ -10306,7 +9968,7 @@ function doPopulationDeviation(s, bLog = false) {
|
|
|
10306
9968
|
exports.doPopulationDeviation = doPopulationDeviation;
|
|
10307
9969
|
// NOTE - This validity check is *derived* and depends on population deviation %
|
|
10308
9970
|
// being computed (above) and normalized in test log & scorecard generation.
|
|
10309
|
-
function doHasEqualPopulations(s
|
|
9971
|
+
function doHasEqualPopulations(s) {
|
|
10310
9972
|
let test = s.getTest(3 /* EqualPopulation */);
|
|
10311
9973
|
// Get the normalized population deviation %
|
|
10312
9974
|
let popDevTest = s.getTest(4 /* PopulationDeviation */);
|
|
@@ -10344,19 +10006,14 @@ exports.doHasEqualPopulations = doHasEqualPopulations;
|
|
|
10344
10006
|
//
|
|
10345
10007
|
// GEO-FEATURES UTILITIES
|
|
10346
10008
|
//
|
|
10347
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
10348
|
-
if (mod && mod.__esModule) return mod;
|
|
10349
|
-
var result = {};
|
|
10350
|
-
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
10351
|
-
result["default"] = mod;
|
|
10352
|
-
return result;
|
|
10353
|
-
};
|
|
10354
10009
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10355
|
-
const Poly =
|
|
10010
|
+
const Poly = __webpack_require__(/*! @dra2020/poly */ "./node_modules/@dra2020/poly/dist/poly.js");
|
|
10356
10011
|
// CARTESIAN SHIMS OVER 'POLY' FUNCTIONS
|
|
10357
|
-
// TODO -
|
|
10012
|
+
// TODO - Terry: Confirm Cartesian calculations
|
|
10358
10013
|
function gfArea(poly) {
|
|
10359
10014
|
let area = _polygonArea(poly);
|
|
10015
|
+
// let polyOptions = { noLatitudeCorrection: false } DELETE
|
|
10016
|
+
// let area: number = Poly.polyArea(poly, polyOptions);
|
|
10360
10017
|
return area;
|
|
10361
10018
|
}
|
|
10362
10019
|
exports.gfArea = gfArea;
|
|
@@ -10402,16 +10059,19 @@ function _polygonArea(poly) {
|
|
|
10402
10059
|
}
|
|
10403
10060
|
return a;
|
|
10404
10061
|
}
|
|
10405
|
-
// TODO -
|
|
10062
|
+
// TODO - Terry: Confirm Cartesian calculations
|
|
10406
10063
|
// The perimeter calculation already just computes cartesian distance if you
|
|
10407
10064
|
// pass in the noLatitudeCorrection flag. You would need to divide by
|
|
10408
10065
|
// Poly.EARTH_RADIUS to go from the returned units of meters to Lat/Lon “units”.
|
|
10409
10066
|
function gfPerimeter(poly) {
|
|
10410
10067
|
let perimeter = _polygonPerimeter(poly);
|
|
10068
|
+
// let polyOptions = { noLatitudeCorrection: true } // Cartesian distance
|
|
10069
|
+
// let perimeter: number = Poly.polyPerimeter(poly, polyOptions); DELETE
|
|
10411
10070
|
return perimeter;
|
|
10071
|
+
// return perimeter / Poly.EARTH_RADIUS; DELETE
|
|
10412
10072
|
}
|
|
10413
10073
|
exports.gfPerimeter = gfPerimeter;
|
|
10414
|
-
// TODO -
|
|
10074
|
+
// TODO - Terry: Confirm Cartesian calculations
|
|
10415
10075
|
// Cloned from polyPerimeter() in 'poly' and revised to use Cartesian distance
|
|
10416
10076
|
// NOTE: No conversion of degrees to radians!
|
|
10417
10077
|
function _polygonPerimeter(poly) {
|
|
@@ -10423,8 +10083,10 @@ function _polygonPerimeter(poly) {
|
|
|
10423
10083
|
let p = poly[i][0];
|
|
10424
10084
|
for (let j = 0; j < p.length - 1; j++)
|
|
10425
10085
|
perimeter += _distance(p[j][0], p[j][1], p[j + 1][0], p[j + 1][1]);
|
|
10086
|
+
// perimeter += haversine(p[j][0], p[j][1], p[j + 1][0], p[j + 1][1], options); DELETE
|
|
10426
10087
|
if (p.length > 2 && (p[0][0] != p[p.length - 1][0] || p[0][1] != p[p.length - 1][1]))
|
|
10427
10088
|
perimeter += _distance(p[0][0], p[0][1], p[p.length - 1][0], p[p.length - 1][1]);
|
|
10089
|
+
// perimeter += haversine(p[0][0], p[0][1], p[p.length - 1][0], p[p.length - 1][1], options); DELETE
|
|
10428
10090
|
}
|
|
10429
10091
|
return perimeter;
|
|
10430
10092
|
}
|
|
@@ -10435,7 +10097,7 @@ function _distance(x1, y1, x2, y2) {
|
|
|
10435
10097
|
d = Math.sqrt((dLat * dLat) + (dLon * dLon));
|
|
10436
10098
|
return d;
|
|
10437
10099
|
}
|
|
10438
|
-
// TODO -
|
|
10100
|
+
// TODO - Terry: Confirm Cartesian calculations
|
|
10439
10101
|
// As I mentioned, the polyCircle code was already just treating the coordinate
|
|
10440
10102
|
// system as Cartesian. I then did polyFromCircle to convert it to a polygon that
|
|
10441
10103
|
// then could be passed to polyArea in order to take into account the projection.
|
|
@@ -10444,6 +10106,9 @@ function _distance(x1, y1, x2, y2) {
|
|
|
10444
10106
|
function gfDiameter(poly) {
|
|
10445
10107
|
let polyOptions = { noLatitudeCorrection: true }; // NO-OP
|
|
10446
10108
|
let circle = Poly.polyToCircle(poly, polyOptions);
|
|
10109
|
+
// let circleArea: number = Poly.polyArea(Poly.polyFromCircle(circle, undefined, polyOptions), polyOptions);
|
|
10110
|
+
// let circleRadius: number = Math.sqrt(circleArea / Math.PI);
|
|
10111
|
+
// let diameter: number = circleRadius * 2; DELETE
|
|
10447
10112
|
let diameter = circle.r * 2;
|
|
10448
10113
|
return diameter;
|
|
10449
10114
|
}
|
|
@@ -10465,17 +10130,61 @@ exports.gfDiameter = gfDiameter;
|
|
|
10465
10130
|
// PROTECTS MINORITIES
|
|
10466
10131
|
//
|
|
10467
10132
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10468
|
-
|
|
10469
|
-
|
|
10470
|
-
|
|
10471
|
-
|
|
10133
|
+
// TODO - This definition is wrong. Need to fix it.
|
|
10134
|
+
//
|
|
10135
|
+
// MINORITY-PROTECTION ANALYTICS NEED THE FOLLOWING DATA:
|
|
10136
|
+
//
|
|
10137
|
+
// The TOTAL, WHITE, BLACK, and HISPANIC counts from the Census, aggregated by
|
|
10138
|
+
// district. We *might* also ultimately need TOTAL18, WHITE18, BLACK18, and
|
|
10139
|
+
// HISPANIC18 counts by feature for these analytics.
|
|
10140
|
+
//
|
|
10141
|
+
// The minority population of a feature will probably be calculated as everyone
|
|
10142
|
+
// exceot non-Hispanic Whites:
|
|
10143
|
+
//
|
|
10144
|
+
// MINORITY = TOTAL - (WHITE - HISPANIC)
|
|
10145
|
+
//
|
|
10146
|
+
// That could be calculated as part of preprocessing the Census data, or it
|
|
10147
|
+
// could be computed on the fly. Since it's derived data and the formula might
|
|
10148
|
+
// change, it's probably best to compute it on the fly.
|
|
10149
|
+
//
|
|
10150
|
+
// In addition to the Census extract, these analytics need:
|
|
10151
|
+
// - The # of districts in the map, for determining minority proportionality
|
|
10152
|
+
// - Minorities as a % of the total population; possibly the voting age share
|
|
10153
|
+
//
|
|
10154
|
+
// TODO - Is the # of districts passed as a parameter or inferred from the # in
|
|
10155
|
+
// the map?
|
|
10156
|
+
// TODO - Is minority share preprocessed once and passed as a parameter or
|
|
10157
|
+
// computed in a initialization routine?
|
|
10158
|
+
function doMajorityMinorityDistricts(s) {
|
|
10159
|
+
let test = s.getTest(14 /* MajorityMinorityDistricts */);
|
|
10160
|
+
console.log("TODO - Calculating # of majority-minority districts ...");
|
|
10472
10161
|
return test;
|
|
10473
10162
|
}
|
|
10474
10163
|
exports.doMajorityMinorityDistricts = doMajorityMinorityDistricts;
|
|
10475
|
-
//
|
|
10476
|
-
//
|
|
10477
|
-
//
|
|
10478
|
-
// -
|
|
10164
|
+
// SAVE THESE NOTES, IN CASE WE NEED TO REWORK HOW WE DO PERFORM THESE CALCS.
|
|
10165
|
+
// THEY REFLECT HOW I/ALEC DID THESE IN PYTHON.
|
|
10166
|
+
//
|
|
10167
|
+
// MINORITY-PROTECTION ANALYTICS WILL NEED THE FOLLOWING DATA,
|
|
10168
|
+
// IN ADDITION TO THE MAP (IDEALLY, GEO_IDS INDEXED BY DISTRICT_ID)
|
|
10169
|
+
//
|
|
10170
|
+
// Census data by geo_id - { total population | white | black | hispanic }
|
|
10171
|
+
//
|
|
10172
|
+
// The minority population of a feature will probably be calculated as the # of
|
|
10173
|
+
// non-White Hispanics:
|
|
10174
|
+
//
|
|
10175
|
+
// MINORITY = TOTAL - (WHITE - HISPANIC)
|
|
10176
|
+
//
|
|
10177
|
+
// That could be calculated as part of preprocessing the Census data, or it
|
|
10178
|
+
// could be computed on the fly.
|
|
10179
|
+
//
|
|
10180
|
+
// And probably:
|
|
10181
|
+
// 'districts' - The # of districts for determining minority proportionality.
|
|
10182
|
+
// 'minority_share' - Minorities as a % of the total population
|
|
10183
|
+
//
|
|
10184
|
+
// TODO - Is the # of districts passed as a parameter or inferred from the # in
|
|
10185
|
+
// the map?
|
|
10186
|
+
// TODO - Is minority share preprocessed once and passed as a parameter or
|
|
10187
|
+
// computed in a initialization routine?
|
|
10479
10188
|
|
|
10480
10189
|
|
|
10481
10190
|
/***/ }),
|
|
@@ -10492,17 +10201,10 @@ exports.doMajorityMinorityDistricts = doMajorityMinorityDistricts;
|
|
|
10492
10201
|
//
|
|
10493
10202
|
// FAIR/PROPORTIONAL
|
|
10494
10203
|
//
|
|
10495
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
10496
|
-
if (mod && mod.__esModule) return mod;
|
|
10497
|
-
var result = {};
|
|
10498
|
-
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
10499
|
-
result["default"] = mod;
|
|
10500
|
-
return result;
|
|
10501
|
-
};
|
|
10502
10204
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10503
10205
|
const assert_1 = __webpack_require__(/*! assert */ "assert");
|
|
10504
|
-
const U =
|
|
10505
|
-
const D =
|
|
10206
|
+
const U = __webpack_require__(/*! ./utils */ "./src/utils.ts");
|
|
10207
|
+
const D = __webpack_require__(/*! ./_data */ "./src/_data.ts");
|
|
10506
10208
|
// Partisan analytics need the following data:
|
|
10507
10209
|
//
|
|
10508
10210
|
// An "election model" by geo_id, where each item has 4 pieces of data:
|
|
@@ -10522,41 +10224,36 @@ const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
|
10522
10224
|
// I'm labelling this general concept a "Voter Preference Index (VPI)," a
|
|
10523
10225
|
// conscious +1 letter play on Cook's "PVI" acronymn.
|
|
10524
10226
|
// MEASURING BIAS & RESPONSIVENESS (NAGLE'S METHOD)
|
|
10525
|
-
function doSeatsBias(s
|
|
10526
|
-
let test = s.getTest(
|
|
10527
|
-
|
|
10528
|
-
console.log("TODO - Calculating seats bias ...");
|
|
10227
|
+
function doSeatsBias(s) {
|
|
10228
|
+
let test = s.getTest(9 /* SeatsBias */);
|
|
10229
|
+
console.log("TODO - Calculating seats bias ...");
|
|
10529
10230
|
return test;
|
|
10530
10231
|
}
|
|
10531
10232
|
exports.doSeatsBias = doSeatsBias;
|
|
10532
|
-
function doVotesBias(s
|
|
10533
|
-
let test = s.getTest(
|
|
10534
|
-
|
|
10535
|
-
console.log("TODO - Calculating votes bias ...");
|
|
10233
|
+
function doVotesBias(s) {
|
|
10234
|
+
let test = s.getTest(10 /* VotesBias */);
|
|
10235
|
+
console.log("TODO - Calculating votes bias ...");
|
|
10536
10236
|
return test;
|
|
10537
10237
|
}
|
|
10538
10238
|
exports.doVotesBias = doVotesBias;
|
|
10539
|
-
function doResponsiveness(s
|
|
10540
|
-
let test = s.getTest(
|
|
10541
|
-
|
|
10542
|
-
console.log("TODO - Calculating responsiveness ...");
|
|
10239
|
+
function doResponsiveness(s) {
|
|
10240
|
+
let test = s.getTest(11 /* Responsiveness */);
|
|
10241
|
+
console.log("TODO - Calculating responsiveness ...");
|
|
10543
10242
|
return test;
|
|
10544
10243
|
}
|
|
10545
10244
|
exports.doResponsiveness = doResponsiveness;
|
|
10546
|
-
function doResponsiveDistricts(s
|
|
10547
|
-
let test = s.getTest(
|
|
10548
|
-
|
|
10549
|
-
console.log("TODO - Calculating # of responsive districts ...");
|
|
10245
|
+
function doResponsiveDistricts(s) {
|
|
10246
|
+
let test = s.getTest(12 /* ResponsiveDistricts */);
|
|
10247
|
+
console.log("TODO - Calculating # of responsive districts ...");
|
|
10550
10248
|
return test;
|
|
10551
10249
|
}
|
|
10552
10250
|
exports.doResponsiveDistricts = doResponsiveDistricts;
|
|
10553
10251
|
// OTHER MEASURES OF PARTISAN BIAS
|
|
10554
|
-
// TODO -
|
|
10252
|
+
// TODO - This formula might need to be inverted for D vs. R +/-
|
|
10555
10253
|
// TODO - Normalize the results.
|
|
10556
|
-
function doEfficiencyGap(s
|
|
10557
|
-
|
|
10558
|
-
|
|
10559
|
-
let test = s.getTest(15 /* EfficiencyGap */);
|
|
10254
|
+
function doEfficiencyGap(s) {
|
|
10255
|
+
console.log("TODO - Calculating the efficiency gap ...");
|
|
10256
|
+
let test = s.getTest(13 /* EfficiencyGap */);
|
|
10560
10257
|
// Get partisan statistics by districts.
|
|
10561
10258
|
// Use Democratic votes, seats, and shares by convention.
|
|
10562
10259
|
let DVotes = s.districts.statistics[D.DistrictField.DemVotes];
|
|
@@ -10603,25 +10300,17 @@ exports.fptpWin = fptpWin;
|
|
|
10603
10300
|
//
|
|
10604
10301
|
// PREPROCESS DATA
|
|
10605
10302
|
//
|
|
10606
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
10607
|
-
if (mod && mod.__esModule) return mod;
|
|
10608
|
-
var result = {};
|
|
10609
|
-
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
10610
|
-
result["default"] = mod;
|
|
10611
|
-
return result;
|
|
10612
|
-
};
|
|
10613
10303
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10614
|
-
const U =
|
|
10304
|
+
const U = __webpack_require__(/*! ./utils */ "./src/utils.ts");
|
|
10615
10305
|
// NOTE - Do preprocessing separately, so the constructor returns quickly.
|
|
10616
|
-
function doPreprocessData(s
|
|
10306
|
+
function doPreprocessData(s) {
|
|
10617
10307
|
// If necessary, do one-time preprocessing
|
|
10618
10308
|
if (!s.bOneTimeProcessingDone) {
|
|
10619
|
-
doPreprocessCountyFeatures(s
|
|
10620
|
-
doPreprocessCensus(s
|
|
10621
|
-
doPreprocessElection(s
|
|
10309
|
+
doPreprocessCountyFeatures(s);
|
|
10310
|
+
doPreprocessCensus(s);
|
|
10311
|
+
doPreprocessElection(s);
|
|
10622
10312
|
s.bOneTimeProcessingDone = true;
|
|
10623
10313
|
}
|
|
10624
|
-
// TODO - UNASSIGNED: Made both the planByGeoID & DistrictID are right
|
|
10625
10314
|
// Invert the plan by district ID
|
|
10626
10315
|
s.plan.invertPlan();
|
|
10627
10316
|
// Create a map of geoIDs to feature IDs
|
|
@@ -10629,7 +10318,7 @@ function doPreprocessData(s, bLog = false) {
|
|
|
10629
10318
|
}
|
|
10630
10319
|
exports.doPreprocessData = doPreprocessData;
|
|
10631
10320
|
// CREATE A FIPS CODE TO COUNTY NAME LOOKUP
|
|
10632
|
-
function doPreprocessCountyFeatures(s
|
|
10321
|
+
function doPreprocessCountyFeatures(s) {
|
|
10633
10322
|
for (let i = 0; i < s.counties.nCounties; i++) {
|
|
10634
10323
|
let county = s.counties.countyByIndex(i);
|
|
10635
10324
|
let fips = s.counties.propertyForCounty(county, 'COUNTYFP');
|
|
@@ -10638,58 +10327,29 @@ function doPreprocessCountyFeatures(s, bLog = false) {
|
|
|
10638
10327
|
}
|
|
10639
10328
|
}
|
|
10640
10329
|
// ANALYZE THE CENSUS BY COUNTY
|
|
10641
|
-
function doPreprocessCensus(s
|
|
10330
|
+
function doPreprocessCensus(s) {
|
|
10642
10331
|
// The county-splitting analytic needs the following info, using NC as an example:
|
|
10643
10332
|
// '_stateTotal' = The total state population, e.g., 9,535,483 for NC's 2010 Census
|
|
10644
10333
|
// 'totalByCounty' = The total population by county FIPS code
|
|
10645
|
-
// SUM TOTAL POPULATION BY COUNTY
|
|
10646
10334
|
let totalByCounty = {};
|
|
10647
10335
|
// NOTE - This works w/o GEOIDs, because you're looping over all features.
|
|
10648
10336
|
for (let i = 0; i < s.features.nFeatures(); i++) {
|
|
10649
10337
|
let f = s.features.featureByIndex(i);
|
|
10650
10338
|
let geoID = s.features.geoIDForFeature(f);
|
|
10651
|
-
|
|
10652
|
-
|
|
10653
|
-
|
|
10654
|
-
|
|
10655
|
-
|
|
10656
|
-
|
|
10657
|
-
|
|
10658
|
-
|
|
10659
|
-
|
|
10660
|
-
|
|
10661
|
-
|
|
10662
|
-
|
|
10663
|
-
// Sum total population by county
|
|
10664
|
-
totalByCounty[countyFIPS] += value;
|
|
10665
|
-
}
|
|
10666
|
-
// else {
|
|
10667
|
-
// console.log("Skipping water-only feature in Census preprocessing:", geoID);
|
|
10668
|
-
// }
|
|
10339
|
+
let value = s.features.fieldForFeature(f, "CENSUS" /* CENSUS */, "Tot" /* TotalPop */);
|
|
10340
|
+
// Sum total population across the state
|
|
10341
|
+
s.state.totalPop += value;
|
|
10342
|
+
// Get the county FIPS code for the feature
|
|
10343
|
+
let county = U.parseGeoID(geoID)['county'];
|
|
10344
|
+
let countyFIPS = U.getFIPSFromCountyGeoID(county);
|
|
10345
|
+
// If a subtotal for the county doesn't exist, initialize one
|
|
10346
|
+
if (!(U.keyExists(countyFIPS, totalByCounty))) {
|
|
10347
|
+
totalByCounty[countyFIPS] = 0;
|
|
10348
|
+
}
|
|
10349
|
+
// Sum total population by county
|
|
10350
|
+
totalByCounty[countyFIPS] += value;
|
|
10669
10351
|
}
|
|
10670
10352
|
// NOTE - The above could be replaced, if I got totals on county.geojson.
|
|
10671
|
-
// CREATE A FIPS CODE-ORDINAL MAP
|
|
10672
|
-
// Get the county FIPS codes
|
|
10673
|
-
let fipsCodes = U.getObjectKeys(totalByCounty);
|
|
10674
|
-
// Sort the results
|
|
10675
|
-
fipsCodes = fipsCodes.sort();
|
|
10676
|
-
// TODO - SPLITTING
|
|
10677
|
-
// Add a dummy county, for county-district splitting analysis
|
|
10678
|
-
fipsCodes.unshift('000');
|
|
10679
|
-
// Create the ID-ordinal map
|
|
10680
|
-
for (let i in fipsCodes) {
|
|
10681
|
-
s.counties.index[fipsCodes[i]] = Number(i);
|
|
10682
|
-
}
|
|
10683
|
-
// MAKE AN ARRAY OF TOTAL POPULATIONS BY COUNTY INDEX
|
|
10684
|
-
// Add an extra 0th virtual county bucket for county-district splitting analysis
|
|
10685
|
-
let nCountyBuckets = s.counties.nCounties + 1;
|
|
10686
|
-
let countyTotals = U.initArray(nCountyBuckets, 0);
|
|
10687
|
-
for (let fipsCode in totalByCounty) {
|
|
10688
|
-
let i = s.counties.indexFromFIPS(fipsCode);
|
|
10689
|
-
countyTotals[i] = totalByCounty[fipsCode];
|
|
10690
|
-
}
|
|
10691
|
-
s.counties.totalPopulation = countyTotals;
|
|
10692
|
-
// ANALYZE THE COUNTIES
|
|
10693
10353
|
// 'target_size': 733499, # calc as total / districts
|
|
10694
10354
|
let targetSize = Math.round(s.state.totalPop / s.state.nDistricts);
|
|
10695
10355
|
// Find counties that are bigger than the target district size.
|
|
@@ -10701,13 +10361,18 @@ function doPreprocessCensus(s, bLog = false) {
|
|
|
10701
10361
|
let tooBigName = [];
|
|
10702
10362
|
let expectedSplits = 0;
|
|
10703
10363
|
let expectedAffected = 0;
|
|
10364
|
+
// Create a FIPS code-ordinal map
|
|
10365
|
+
// Get the county FIPS codes
|
|
10366
|
+
let fipsCodes = U.getObjectKeys(totalByCounty);
|
|
10367
|
+
// Sort the results
|
|
10368
|
+
fipsCodes = fipsCodes.sort();
|
|
10369
|
+
// Create the ID-ordinal map
|
|
10370
|
+
for (let i in fipsCodes) {
|
|
10371
|
+
s.counties.index[fipsCodes[i]] = Number(i);
|
|
10372
|
+
}
|
|
10704
10373
|
// Loop over the counties
|
|
10705
10374
|
for (let county in fipsCodes) {
|
|
10706
10375
|
let fipsCode = fipsCodes[county];
|
|
10707
|
-
// TODO - SPLITTING
|
|
10708
|
-
// Skip the dummy county
|
|
10709
|
-
if (fipsCode == '000')
|
|
10710
|
-
continue;
|
|
10711
10376
|
let countyAffected = 0;
|
|
10712
10377
|
// Find the number of required splits, assuming target district size.
|
|
10713
10378
|
let rawQuotient = totalByCounty[fipsCode] / (targetSize + 1);
|
|
@@ -10728,428 +10393,34 @@ function doPreprocessCensus(s, bLog = false) {
|
|
|
10728
10393
|
s.state.expectedAffected = expectedAffected;
|
|
10729
10394
|
}
|
|
10730
10395
|
// PREPROCESS ELECTION RESULTS
|
|
10731
|
-
function doPreprocessElection(s
|
|
10732
|
-
|
|
10733
|
-
console.log("TODO - Preprocessing election data ...");
|
|
10396
|
+
function doPreprocessElection(s) {
|
|
10397
|
+
console.log("TODO - Preprocessing election data ...");
|
|
10734
10398
|
}
|
|
10735
10399
|
|
|
10736
10400
|
|
|
10737
10401
|
/***/ }),
|
|
10738
10402
|
|
|
10739
|
-
/***/ "./src/
|
|
10740
|
-
|
|
10741
|
-
!*** ./src/
|
|
10742
|
-
|
|
10403
|
+
/***/ "./src/report.ts":
|
|
10404
|
+
/*!***********************!*\
|
|
10405
|
+
!*** ./src/report.ts ***!
|
|
10406
|
+
\***********************/
|
|
10743
10407
|
/*! no static exports found */
|
|
10744
10408
|
/***/ (function(module, exports, __webpack_require__) {
|
|
10745
10409
|
|
|
10746
10410
|
"use strict";
|
|
10747
10411
|
|
|
10748
10412
|
//
|
|
10749
|
-
//
|
|
10413
|
+
// GENERATE REPORTS
|
|
10414
|
+
// - A test log: a simple enumeration of all analytics & validations w/ raw results
|
|
10415
|
+
// - A scorecard: a structured subset of analytics & validations w/ normalized
|
|
10416
|
+
// results, cateories, and an overall score
|
|
10750
10417
|
//
|
|
10751
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
10752
|
-
if (mod && mod.__esModule) return mod;
|
|
10753
|
-
var result = {};
|
|
10754
|
-
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
10755
|
-
result["default"] = mod;
|
|
10756
|
-
return result;
|
|
10757
|
-
};
|
|
10758
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
10759
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10760
|
-
};
|
|
10761
10418
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10762
|
-
const U =
|
|
10763
|
-
const S =
|
|
10764
|
-
const D =
|
|
10765
|
-
// TODO - DASHBOARD: Delete
|
|
10766
|
-
// import { doAnalyzePostProcessing } from './report'
|
|
10419
|
+
const U = __webpack_require__(/*! ./utils */ "./src/utils.ts");
|
|
10420
|
+
const S = __webpack_require__(/*! ./settings */ "./src/settings.ts");
|
|
10421
|
+
const D = __webpack_require__(/*! ./_data */ "./src/_data.ts");
|
|
10767
10422
|
const analyze_1 = __webpack_require__(/*! ./analyze */ "./src/analyze.ts");
|
|
10768
|
-
|
|
10769
|
-
// Example
|
|
10770
|
-
let sampleRequirements = {
|
|
10771
|
-
score: 2 /* Red */,
|
|
10772
|
-
metrics: {
|
|
10773
|
-
complete: 0 /* Green */,
|
|
10774
|
-
contiguous: 2 /* Red */,
|
|
10775
|
-
freeOfHoles: 1 /* Yellow */,
|
|
10776
|
-
equalPopulation: 2 /* Red */
|
|
10777
|
-
},
|
|
10778
|
-
details: {
|
|
10779
|
-
unassignedFeatures: [],
|
|
10780
|
-
emptyDistricts: [],
|
|
10781
|
-
discontiguousDistricts: [2],
|
|
10782
|
-
embeddedDistricts: [],
|
|
10783
|
-
populationDeviation: 0.6748,
|
|
10784
|
-
deviationThreshold: 0.75 / 100
|
|
10785
|
-
},
|
|
10786
|
-
datasets: {
|
|
10787
|
-
census: "2010 Census Total Population"
|
|
10788
|
-
},
|
|
10789
|
-
resources: {
|
|
10790
|
-
stateReqs: "https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_20.pdf"
|
|
10791
|
-
}
|
|
10792
|
-
};
|
|
10793
|
-
let sampleCompactness = {
|
|
10794
|
-
score: 60,
|
|
10795
|
-
metrics: {
|
|
10796
|
-
reock: 0.3773,
|
|
10797
|
-
polsby: 0.3815
|
|
10798
|
-
},
|
|
10799
|
-
details: {},
|
|
10800
|
-
datasets: {
|
|
10801
|
-
shapes: "2010 VTD shapes"
|
|
10802
|
-
},
|
|
10803
|
-
resources: {}
|
|
10804
|
-
};
|
|
10805
|
-
let sampleSplitting = {
|
|
10806
|
-
score: 73,
|
|
10807
|
-
metrics: {
|
|
10808
|
-
sqEnt_DCreduced: 1.531,
|
|
10809
|
-
sqEnt_CDreduced: 1.760
|
|
10810
|
-
},
|
|
10811
|
-
details: {
|
|
10812
|
-
countiesSplitUnexpectedly: [
|
|
10813
|
-
"Bladen", "Buncombe", "Catawba", "Cumberland", "Durham", "Guilford", "Iredell", "Johnston", "Pitt", "Rowan", "Wilson"
|
|
10814
|
-
],
|
|
10815
|
-
unexpectedAffected: 0.3096,
|
|
10816
|
-
nSplitVTDs: 12,
|
|
10817
|
-
splitVTDs: []
|
|
10818
|
-
},
|
|
10819
|
-
datasets: {},
|
|
10820
|
-
resources: {}
|
|
10821
|
-
};
|
|
10822
|
-
// TODO - This category is still being fleshed out.
|
|
10823
|
-
let samplePartisan = {
|
|
10824
|
-
score: 100,
|
|
10825
|
-
metrics: {
|
|
10826
|
-
partisanBias: 0.15,
|
|
10827
|
-
responsiveness: 2.0
|
|
10828
|
-
},
|
|
10829
|
-
details: {},
|
|
10830
|
-
datasets: {
|
|
10831
|
-
election: "2016 Presidential, US Senate, Governor, and AG election results"
|
|
10832
|
-
},
|
|
10833
|
-
resources: {
|
|
10834
|
-
planScore: "https://planscore.org/plan.html?20180219T202039.596761160Z"
|
|
10835
|
-
}
|
|
10836
|
-
};
|
|
10837
|
-
// TODO - This category is still being fleshed out.
|
|
10838
|
-
let sampleMinority = {
|
|
10839
|
-
score: null,
|
|
10840
|
-
metrics: {
|
|
10841
|
-
nBlack37to50: 1,
|
|
10842
|
-
nBlackMajority: 12,
|
|
10843
|
-
nHispanic37to50: 0,
|
|
10844
|
-
nHispanicMajority: 0,
|
|
10845
|
-
nPacific37to50: 0,
|
|
10846
|
-
nPacificMajority: 0,
|
|
10847
|
-
nAsian37to50: 0,
|
|
10848
|
-
nAsianMajority: 0,
|
|
10849
|
-
nNative37to50: 0,
|
|
10850
|
-
nNativeMajority: 0,
|
|
10851
|
-
nMinority37to50: 0,
|
|
10852
|
-
nMinorityMajority: 0,
|
|
10853
|
-
averageDVoteShare: 0.90
|
|
10854
|
-
},
|
|
10855
|
-
details: {
|
|
10856
|
-
vap: true,
|
|
10857
|
-
comboCategories: true
|
|
10858
|
-
},
|
|
10859
|
-
datasets: {
|
|
10860
|
-
vap: "2010 Voting Age Population"
|
|
10861
|
-
},
|
|
10862
|
-
resources: {}
|
|
10863
|
-
};
|
|
10864
|
-
exports.samplePlanAnalytics = {
|
|
10865
|
-
requirements: sampleRequirements,
|
|
10866
|
-
compactness: sampleCompactness,
|
|
10867
|
-
// TODO - Don't show these categories yet
|
|
10868
|
-
splitting: sampleSplitting,
|
|
10869
|
-
partisan: samplePartisan,
|
|
10870
|
-
minority: sampleMinority
|
|
10871
|
-
};
|
|
10872
|
-
function preparePlanAnalytics(s, bLog = false) {
|
|
10873
|
-
if (!(s.bPostProcessingDone)) {
|
|
10874
|
-
doAnalyzePostProcessing(s);
|
|
10875
|
-
}
|
|
10876
|
-
// REQUIREMENTS CATEGORY
|
|
10877
|
-
let paRequirements;
|
|
10878
|
-
{
|
|
10879
|
-
let completeTest = s.getTest(0 /* Complete */);
|
|
10880
|
-
let contiguousTest = s.getTest(1 /* Contiguous */);
|
|
10881
|
-
let freeOfHolesTest = s.getTest(2 /* FreeOfHoles */);
|
|
10882
|
-
let equalPopulationTest = s.getTest(3 /* EqualPopulation */);
|
|
10883
|
-
// Combine individual checks into an overall score
|
|
10884
|
-
// TODO - DASHBOARD: Until we add three-state support top to bottom in
|
|
10885
|
-
// requirements/validations, map booleans to tri-states here.
|
|
10886
|
-
let completeMetric = U.mapBooleanToTriState(completeTest['score']);
|
|
10887
|
-
let contiguousMetric = U.mapBooleanToTriState(contiguousTest['score']);
|
|
10888
|
-
let freeOfHolesMetric = U.mapBooleanToTriState(freeOfHolesTest['score']);
|
|
10889
|
-
let equalPopulationMetric = U.mapBooleanToTriState(equalPopulationTest['score']);
|
|
10890
|
-
let reqScore = 0 /* Green */;
|
|
10891
|
-
let checks = [completeMetric, contiguousMetric, freeOfHolesMetric, equalPopulationMetric];
|
|
10892
|
-
if (checks.includes(1 /* Yellow */))
|
|
10893
|
-
reqScore = 1 /* Yellow */;
|
|
10894
|
-
if (checks.includes(2 /* Red */))
|
|
10895
|
-
reqScore = 2 /* Red */;
|
|
10896
|
-
// Get values to support details entries
|
|
10897
|
-
let unassignedFeaturesDetail = U.deepCopy(completeTest['details']['unassignedFeatures']) || [];
|
|
10898
|
-
let emptyDistrictsDetail = U.deepCopy(completeTest['details']['emptyDistricts']) || [];
|
|
10899
|
-
let discontiguousDistrictsDetail = U.deepCopy(contiguousTest['details']['discontiguousDistricts']) || [];
|
|
10900
|
-
let embeddedDistrictsDetail = U.deepCopy(freeOfHolesTest['details']['embeddedDistricts']) || [];
|
|
10901
|
-
let populationDeviationDetail = U.deepCopy(equalPopulationTest['details']['deviation']);
|
|
10902
|
-
let deviationThresholdDetail = U.trim(s.populationDeviationThreshold());
|
|
10903
|
-
let xx = s.state.xx;
|
|
10904
|
-
// TODO - JSON: Is there a better / easier way to work with the variable?
|
|
10905
|
-
let stateReqsDict = state_reqs_json_1.default;
|
|
10906
|
-
let reqLinkToStateReqs = stateReqsDict[xx];
|
|
10907
|
-
// Populate the category
|
|
10908
|
-
paRequirements = {
|
|
10909
|
-
score: reqScore,
|
|
10910
|
-
metrics: {
|
|
10911
|
-
complete: completeMetric,
|
|
10912
|
-
contiguous: contiguousMetric,
|
|
10913
|
-
freeOfHoles: freeOfHolesMetric,
|
|
10914
|
-
equalPopulation: equalPopulationMetric
|
|
10915
|
-
},
|
|
10916
|
-
details: {
|
|
10917
|
-
unassignedFeatures: unassignedFeaturesDetail,
|
|
10918
|
-
emptyDistricts: emptyDistrictsDetail,
|
|
10919
|
-
discontiguousDistricts: discontiguousDistrictsDetail,
|
|
10920
|
-
embeddedDistricts: embeddedDistrictsDetail,
|
|
10921
|
-
populationDeviation: populationDeviationDetail,
|
|
10922
|
-
deviationThreshold: deviationThresholdDetail
|
|
10923
|
-
},
|
|
10924
|
-
datasets: {
|
|
10925
|
-
census: U.deepCopy(s.config['descriptions']['CENSUS']),
|
|
10926
|
-
},
|
|
10927
|
-
resources: {
|
|
10928
|
-
stateReqs: reqLinkToStateReqs
|
|
10929
|
-
}
|
|
10930
|
-
};
|
|
10931
|
-
}
|
|
10932
|
-
// COMPACTNESS CATEGORY
|
|
10933
|
-
let paCompactness;
|
|
10934
|
-
{
|
|
10935
|
-
let reockWeight = 0.5;
|
|
10936
|
-
let polsbyWeight = 1.0 - reockWeight;
|
|
10937
|
-
let reockTest = s.getTest(5 /* Reock */);
|
|
10938
|
-
let polsbyTest = s.getTest(6 /* PolsbyPopper */);
|
|
10939
|
-
let normalizedReock = reockTest['normalizedScore'];
|
|
10940
|
-
let normalizedPolsby = reockTest['normalizedScore'];
|
|
10941
|
-
let compactnessScore = U.trim((reockWeight * normalizedReock) + (polsbyWeight * normalizedPolsby), 0);
|
|
10942
|
-
let reockMetric = U.deepCopy(reockTest['score']);
|
|
10943
|
-
let polsbyMetric = U.deepCopy(polsbyTest['score']);
|
|
10944
|
-
// Populate the category
|
|
10945
|
-
paCompactness = {
|
|
10946
|
-
score: compactnessScore,
|
|
10947
|
-
metrics: {
|
|
10948
|
-
reock: reockMetric,
|
|
10949
|
-
polsby: polsbyMetric
|
|
10950
|
-
},
|
|
10951
|
-
details: {
|
|
10952
|
-
// None at this time
|
|
10953
|
-
},
|
|
10954
|
-
datasets: {
|
|
10955
|
-
shapes: "2010 VTD shapes"
|
|
10956
|
-
// TODO - DATASETS
|
|
10957
|
-
// shapes: U.deepCopy(s.config['descriptions']['SHAPES'])
|
|
10958
|
-
},
|
|
10959
|
-
resources: {
|
|
10960
|
-
// None at this time
|
|
10961
|
-
}
|
|
10962
|
-
};
|
|
10963
|
-
}
|
|
10964
|
-
// SPLITTING CATEGORY
|
|
10965
|
-
let paSplitting;
|
|
10966
|
-
{
|
|
10967
|
-
let unexpectedCountySplittingTest = s.getTest(7 /* UnexpectedCountySplits */);
|
|
10968
|
-
let VTDSplitsTest = s.getTest(10 /* VTDSplits */);
|
|
10969
|
-
let countySplittingTest = s.getTest(8 /* CountySplitting */);
|
|
10970
|
-
let districtSplittingTest = s.getTest(9 /* DistrictSplitting */);
|
|
10971
|
-
let unexpectedAffectedMetric = U.deepCopy(unexpectedCountySplittingTest['score']);
|
|
10972
|
-
let countiesSplitUnexpectedlyDetail = U.deepCopy(unexpectedCountySplittingTest['details']['countiesSplitUnexpectedly']);
|
|
10973
|
-
let nVTDSplitsMetric = U.deepCopy(VTDSplitsTest['score']);
|
|
10974
|
-
let splitVTDsDetail = U.deepCopy(VTDSplitsTest['details']['splitVTDs']);
|
|
10975
|
-
let SqEnt_DCreducedMetric = U.deepCopy(countySplittingTest['score']);
|
|
10976
|
-
let SqEnt_CDreducedMetric = U.deepCopy(districtSplittingTest['score']);
|
|
10977
|
-
let countySplittingNormalized = countySplittingTest['normalizedScore'];
|
|
10978
|
-
let districtSplittingNormalized = districtSplittingTest['normalizedScore'];
|
|
10979
|
-
let splittingScore = U.trim((S.COUNTY_SPLITTING_WEIGHT * countySplittingNormalized) +
|
|
10980
|
-
+(S.DISTRICT_SPLITTING_WEIGHT * districtSplittingNormalized), 0);
|
|
10981
|
-
paSplitting = {
|
|
10982
|
-
score: splittingScore,
|
|
10983
|
-
metrics: {
|
|
10984
|
-
sqEnt_DCreduced: SqEnt_DCreducedMetric,
|
|
10985
|
-
sqEnt_CDreduced: SqEnt_CDreducedMetric
|
|
10986
|
-
// NOTE - The un-reduced raw values
|
|
10987
|
-
// sqEnt_DC : SqEnt_DCMetric,
|
|
10988
|
-
// sqEnt_CD : SqEnt_CDMetric
|
|
10989
|
-
},
|
|
10990
|
-
details: {
|
|
10991
|
-
countiesSplitUnexpectedly: countiesSplitUnexpectedlyDetail,
|
|
10992
|
-
unexpectedAffected: unexpectedAffectedMetric,
|
|
10993
|
-
nSplitVTDs: nVTDSplitsMetric,
|
|
10994
|
-
splitVTDs: splitVTDsDetail
|
|
10995
|
-
},
|
|
10996
|
-
datasets: {
|
|
10997
|
-
// None at this time
|
|
10998
|
-
},
|
|
10999
|
-
resources: {
|
|
11000
|
-
// None at this time
|
|
11001
|
-
}
|
|
11002
|
-
};
|
|
11003
|
-
}
|
|
11004
|
-
// PARTISAN CATEGORY
|
|
11005
|
-
//
|
|
11006
|
-
// TODO - PARTISAN: This category is still being fleshed out. Just an example below.
|
|
11007
|
-
let paPartisan;
|
|
11008
|
-
{
|
|
11009
|
-
paPartisan = {
|
|
11010
|
-
score: 100,
|
|
11011
|
-
metrics: {
|
|
11012
|
-
partisanBias: 0.15,
|
|
11013
|
-
responsiveness: 2.0
|
|
11014
|
-
},
|
|
11015
|
-
details: {},
|
|
11016
|
-
datasets: {
|
|
11017
|
-
election: "2016 Presidential, US Senate, Governor, and AG election results"
|
|
11018
|
-
},
|
|
11019
|
-
resources: {
|
|
11020
|
-
planScore: "https://planscore.org/plan.html?20180219T202039.596761160Z"
|
|
11021
|
-
}
|
|
11022
|
-
};
|
|
11023
|
-
}
|
|
11024
|
-
// MINORITY CATEGORY
|
|
11025
|
-
//
|
|
11026
|
-
// TODO - MINORITY: This category is still being fleshed out. Just an example below.
|
|
11027
|
-
let paMinority;
|
|
11028
|
-
{
|
|
11029
|
-
paMinority = {
|
|
11030
|
-
score: null,
|
|
11031
|
-
metrics: {
|
|
11032
|
-
nBlack37to50: 1,
|
|
11033
|
-
nBlackMajority: 12,
|
|
11034
|
-
nHispanic37to50: 0,
|
|
11035
|
-
nHispanicMajority: 0,
|
|
11036
|
-
nPacific37to50: 0,
|
|
11037
|
-
nPacificMajority: 0,
|
|
11038
|
-
nAsian37to50: 0,
|
|
11039
|
-
nAsianMajority: 0,
|
|
11040
|
-
nNative37to50: 0,
|
|
11041
|
-
nNativeMajority: 0,
|
|
11042
|
-
nMinority37to50: 0,
|
|
11043
|
-
nMinorityMajority: 0,
|
|
11044
|
-
averageDVoteShare: 0.90
|
|
11045
|
-
},
|
|
11046
|
-
details: {
|
|
11047
|
-
vap: true,
|
|
11048
|
-
comboCategories: true
|
|
11049
|
-
},
|
|
11050
|
-
datasets: {
|
|
11051
|
-
vap: "2010 Voting Age Population"
|
|
11052
|
-
},
|
|
11053
|
-
resources: {}
|
|
11054
|
-
};
|
|
11055
|
-
}
|
|
11056
|
-
// PLAN ANALYTICS
|
|
11057
|
-
let pa = {
|
|
11058
|
-
requirements: paRequirements,
|
|
11059
|
-
compactness: paCompactness,
|
|
11060
|
-
// TODO - Not implemented yet
|
|
11061
|
-
splitting: paSplitting,
|
|
11062
|
-
partisan: paPartisan,
|
|
11063
|
-
minority: paMinority
|
|
11064
|
-
};
|
|
11065
|
-
return pa;
|
|
11066
|
-
}
|
|
11067
|
-
exports.preparePlanAnalytics = preparePlanAnalytics;
|
|
11068
|
-
// Example
|
|
11069
|
-
exports.sampleDistrictStatistics = {
|
|
11070
|
-
table: [
|
|
11071
|
-
// District 0 is the dummy unassigned district
|
|
11072
|
-
[0, 0, 0, null, null, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
11073
|
-
[1, 653133, -0.0950, 0 /* Green */, 2 /* Red */, 0 /* Green */, 0 /* Green */, 0.4177, 0.5823, 0.8631, 0.1369, 0.0734, 0.0360, 0.0009, 0.0235, 0.0064],
|
|
11074
|
-
[2, 620961, -0.1396, 0 /* Green */, 2 /* Red */, 2 /* Red */, 0 /* Green */, 0.8820, 0.1180, 0.3129, 0.6871, 0.6169, 0.0391, 0.0013, 0.0310, 0.0099],
|
|
11075
|
-
[3, 971777, 0.3465, 0 /* Green */, 2 /* Red */, 0 /* Green */, 0 /* Green */, 0.7261, 0.2739, 0.5174, 0.4826, 0.1745, 0.1572, 0.0020, 0.1531, 0.0090],
|
|
11076
|
-
[4, 863420, 0.1964, 0 /* Green */, 2 /* Red */, 0 /* Green */, 0 /* Green */, 0.8957, 0.1043, 0.1734, 0.8266, 0.6489, 0.1348, 0.0020, 0.0496, 0.0127],
|
|
11077
|
-
[5, 805029, 0.1155, 0 /* Green */, 2 /* Red */, 0 /* Green */, 1 /* Yellow */, 0.5743, 0.4257, 0.6587, 0.3413, 0.2494, 0.0363, 0.0012, 0.0536, 0.0081],
|
|
11078
|
-
[6, 824741, 0.1428, 0 /* Green */, 2 /* Red */, 0 /* Green */, 2 /* Red */, 0.5341, 0.4659, 0.7045, 0.2955, 0.1619, 0.0526, 0.0018, 0.0782, 0.0090],
|
|
11079
|
-
[7, 549714, -0.2383, 0 /* Green */, 0 /* Green */, 0 /* Green */, 0 /* Green */, 0.5025, 0.4975, 0.6906, 0.3094, 0.2468, 0.0319, 0.0013, 0.0258, 0.0111],
|
|
11080
|
-
[8, 484777, -0.3283, 0 /* Green */, 0 /* Green */, 0 /* Green */, 0 /* Green */, 0.4105, 0.5895, 0.8370, 0.1630, 0.1074, 0.0316, 0.0013, 0.0197, 0.0077],
|
|
11081
|
-
// District N+1 is the dummy state-summary district
|
|
11082
|
-
[9, 721694, 0.6748, 0 /* Green */, 2 /* Red */, 2 /* Red */, 2 /* Red */, 0.6293, 0.3707, 0.5722, 0.4278, 0.2925, 0.0729, 0.0015, 0.0618, 0.0093]
|
|
11083
|
-
],
|
|
11084
|
-
details: {},
|
|
11085
|
-
datasets: {
|
|
11086
|
-
shapes: "2010 VTD shapes",
|
|
11087
|
-
census: "2010 Census Total Population",
|
|
11088
|
-
vap: "2010 Voting Age Population",
|
|
11089
|
-
election: "2016 Presidential, US Senate, Governor, and AG election results"
|
|
11090
|
-
},
|
|
11091
|
-
resources: {}
|
|
11092
|
-
};
|
|
11093
|
-
// Create a DistrictStatistics instance, deep copying the underlying values.
|
|
11094
|
-
function prepareDistrictStatistics(s, bLog = false) {
|
|
11095
|
-
if (!(s.bPostProcessingDone)) {
|
|
11096
|
-
doAnalyzePostProcessing(s);
|
|
11097
|
-
}
|
|
11098
|
-
let dsTable = [];
|
|
11099
|
-
for (let i = 0; i < s.districts.numberOfRows(); i++) {
|
|
11100
|
-
let rawRow = [
|
|
11101
|
-
i,
|
|
11102
|
-
s.districts.statistics[D.DistrictField.TotalPop][i],
|
|
11103
|
-
s.districts.statistics[D.DistrictField.PopDevPct][i],
|
|
11104
|
-
s.districts.statistics[D.DistrictField.bEqualPop][i],
|
|
11105
|
-
s.districts.statistics[D.DistrictField.bNotEmpty][i],
|
|
11106
|
-
s.districts.statistics[D.DistrictField.bContiguous][i],
|
|
11107
|
-
s.districts.statistics[D.DistrictField.bNotEmbedded][i],
|
|
11108
|
-
s.districts.statistics[D.DistrictField.DemPct][i],
|
|
11109
|
-
s.districts.statistics[D.DistrictField.RepPct][i],
|
|
11110
|
-
s.districts.statistics[D.DistrictField.WhitePct][i],
|
|
11111
|
-
s.districts.statistics[D.DistrictField.MinorityPct][i],
|
|
11112
|
-
s.districts.statistics[D.DistrictField.BlackPct][i],
|
|
11113
|
-
s.districts.statistics[D.DistrictField.HispanicPct][i],
|
|
11114
|
-
s.districts.statistics[D.DistrictField.PacificPct][i],
|
|
11115
|
-
s.districts.statistics[D.DistrictField.AsianPct][i],
|
|
11116
|
-
s.districts.statistics[D.DistrictField.NativePct][i]
|
|
11117
|
-
];
|
|
11118
|
-
// TODO - DASHBOARD: Until we add three-state support top to bottom in
|
|
11119
|
-
// requirements/validations, map booleans to tri-states here.
|
|
11120
|
-
rawRow[3 /* bEqualPop */] = U.mapBooleanToTriState(rawRow[3 /* bEqualPop */]);
|
|
11121
|
-
rawRow[4 /* bNotEmpty */] = U.mapBooleanToTriState(rawRow[4 /* bNotEmpty */]);
|
|
11122
|
-
rawRow[5 /* bContiguous */] = U.mapBooleanToTriState(rawRow[5 /* bContiguous */]);
|
|
11123
|
-
rawRow[6 /* bNotEmbedded */] = U.mapBooleanToTriState(rawRow[6 /* bNotEmbedded */]);
|
|
11124
|
-
let readyRow = U.deepCopy(rawRow);
|
|
11125
|
-
dsTable.push(readyRow);
|
|
11126
|
-
}
|
|
11127
|
-
let dsDetails = {
|
|
11128
|
-
// None at this time
|
|
11129
|
-
};
|
|
11130
|
-
let dsDatasets = {
|
|
11131
|
-
shapes: "2010 VTD shapes",
|
|
11132
|
-
census: U.deepCopy(s.config['descriptions']['CENSUS']),
|
|
11133
|
-
vap: U.deepCopy(s.config['descriptions']['VAP']),
|
|
11134
|
-
election: U.deepCopy(s.config['descriptions']['ELECTION'])
|
|
11135
|
-
};
|
|
11136
|
-
let dsResources = {
|
|
11137
|
-
// None at this time
|
|
11138
|
-
};
|
|
11139
|
-
let ds = {
|
|
11140
|
-
table: dsTable,
|
|
11141
|
-
details: dsDetails,
|
|
11142
|
-
datasets: dsDatasets,
|
|
11143
|
-
resources: dsResources
|
|
11144
|
-
};
|
|
11145
|
-
return ds;
|
|
11146
|
-
}
|
|
11147
|
-
exports.prepareDistrictStatistics = prepareDistrictStatistics;
|
|
11148
|
-
// META-DATA FOR TESTS/ANALYTICS
|
|
11149
|
-
//
|
|
11150
|
-
// NOTE - This structure is a vestige of having created a metadata-driven
|
|
11151
|
-
// scorecard w/in district-analytics at first. It works for creating the
|
|
11152
|
-
// unstyled results structures, so it isn't a high priority to rationalize.
|
|
10423
|
+
// TEST META-DATA
|
|
11153
10424
|
var TestType;
|
|
11154
10425
|
(function (TestType) {
|
|
11155
10426
|
TestType[TestType["PassFail"] = 0] = "PassFail";
|
|
@@ -11161,6 +10432,7 @@ const completeDefn = {
|
|
|
11161
10432
|
name: "Complete",
|
|
11162
10433
|
normalize: false,
|
|
11163
10434
|
externalType: TestType.PassFail,
|
|
10435
|
+
detailsFn: doPrepareCompleteDetails,
|
|
11164
10436
|
suites: [0 /* Legal */]
|
|
11165
10437
|
};
|
|
11166
10438
|
const contiguousDefn = {
|
|
@@ -11168,6 +10440,7 @@ const contiguousDefn = {
|
|
|
11168
10440
|
name: "Contiguous",
|
|
11169
10441
|
normalize: false,
|
|
11170
10442
|
externalType: TestType.PassFail,
|
|
10443
|
+
detailsFn: doPrepareContiguousDetails,
|
|
11171
10444
|
suites: [0 /* Legal */]
|
|
11172
10445
|
};
|
|
11173
10446
|
const freeOfHolesDefn = {
|
|
@@ -11175,6 +10448,7 @@ const freeOfHolesDefn = {
|
|
|
11175
10448
|
name: "Free of Holes",
|
|
11176
10449
|
normalize: false,
|
|
11177
10450
|
externalType: TestType.PassFail,
|
|
10451
|
+
detailsFn: doPrepareFreeOfHolesDetails,
|
|
11178
10452
|
suites: [0 /* Legal */]
|
|
11179
10453
|
};
|
|
11180
10454
|
const equalPopulationDefn = {
|
|
@@ -11182,6 +10456,7 @@ const equalPopulationDefn = {
|
|
|
11182
10456
|
name: "Equal Population",
|
|
11183
10457
|
normalize: false,
|
|
11184
10458
|
externalType: TestType.PassFail,
|
|
10459
|
+
detailsFn: doPrepareEqualPopulationDetails,
|
|
11185
10460
|
suites: [0 /* Legal */]
|
|
11186
10461
|
};
|
|
11187
10462
|
const populationDeviationDefn = {
|
|
@@ -11189,6 +10464,7 @@ const populationDeviationDefn = {
|
|
|
11189
10464
|
name: "Population Deviation",
|
|
11190
10465
|
normalize: true,
|
|
11191
10466
|
externalType: TestType.Percentage,
|
|
10467
|
+
detailsFn: doPreparePopulationDeviationDetails,
|
|
11192
10468
|
suites: [0 /* Legal */, 2 /* Best */] // Both so EqualPopulation can be assessed
|
|
11193
10469
|
};
|
|
11194
10470
|
const reockDefn = {
|
|
@@ -11196,6 +10472,7 @@ const reockDefn = {
|
|
|
11196
10472
|
name: "Reock",
|
|
11197
10473
|
normalize: true,
|
|
11198
10474
|
externalType: TestType.Number,
|
|
10475
|
+
detailsFn: doPrepareReockDetails,
|
|
11199
10476
|
suites: [2 /* Best */]
|
|
11200
10477
|
};
|
|
11201
10478
|
const polsbyPopperDefn = {
|
|
@@ -11203,45 +10480,24 @@ const polsbyPopperDefn = {
|
|
|
11203
10480
|
name: "Polsby-Popper",
|
|
11204
10481
|
normalize: true,
|
|
11205
10482
|
externalType: TestType.Number,
|
|
10483
|
+
detailsFn: doPreparePolsbyPopperDetails,
|
|
11206
10484
|
suites: [2 /* Best */]
|
|
11207
10485
|
};
|
|
11208
|
-
|
|
11209
|
-
|
|
11210
|
-
|
|
11211
|
-
|
|
11212
|
-
normalize: false,
|
|
11213
|
-
externalType: TestType.Percentage,
|
|
11214
|
-
suites: [2 /* Best */]
|
|
11215
|
-
};
|
|
11216
|
-
// TODO - SPLITTING
|
|
11217
|
-
const VTDSplitsDefn = {
|
|
11218
|
-
ID: 10 /* VTDSplits */,
|
|
11219
|
-
name: "VTD Splits",
|
|
11220
|
-
normalize: false,
|
|
11221
|
-
externalType: TestType.Number,
|
|
11222
|
-
suites: [2 /* Best */]
|
|
11223
|
-
};
|
|
11224
|
-
// TODO - SPLITTING
|
|
11225
|
-
const countySplittingDefn = {
|
|
11226
|
-
ID: 8 /* CountySplitting */,
|
|
11227
|
-
name: "County Splitting",
|
|
11228
|
-
normalize: true,
|
|
11229
|
-
externalType: TestType.Number,
|
|
11230
|
-
suites: [2 /* Best */]
|
|
11231
|
-
};
|
|
11232
|
-
// TODO - SPLITTING
|
|
11233
|
-
const districtSplittingDefn = {
|
|
11234
|
-
ID: 9 /* DistrictSplitting */,
|
|
11235
|
-
name: "District Splitting",
|
|
10486
|
+
const countySplitsDefn = {
|
|
10487
|
+
ID: 7 /* CountySplits */,
|
|
10488
|
+
name: "County splits",
|
|
10489
|
+
trailer: "of the population had their county split unexpectedly.",
|
|
11236
10490
|
normalize: true,
|
|
11237
|
-
externalType: TestType.
|
|
10491
|
+
externalType: TestType.Percentage,
|
|
10492
|
+
detailsFn: doPrepareCountySplitDetails,
|
|
11238
10493
|
suites: [2 /* Best */]
|
|
11239
10494
|
};
|
|
11240
10495
|
const efficiencyGapDefn = {
|
|
11241
|
-
ID:
|
|
10496
|
+
ID: 13 /* EfficiencyGap */,
|
|
11242
10497
|
name: "Efficiency Gap",
|
|
11243
10498
|
normalize: false,
|
|
11244
10499
|
externalType: TestType.Percentage,
|
|
10500
|
+
detailsFn: doPrepareEfficiencyGapDetails,
|
|
11245
10501
|
suites: [1 /* Fair */]
|
|
11246
10502
|
};
|
|
11247
10503
|
// All the tests that have been defined (can be reported on)
|
|
@@ -11253,13 +10509,72 @@ const testDefns = {
|
|
|
11253
10509
|
[4 /* PopulationDeviation */]: populationDeviationDefn,
|
|
11254
10510
|
[5 /* Reock */]: reockDefn,
|
|
11255
10511
|
[6 /* PolsbyPopper */]: polsbyPopperDefn,
|
|
11256
|
-
// TODO -
|
|
11257
|
-
[7 /*
|
|
11258
|
-
[
|
|
11259
|
-
|
|
11260
|
-
|
|
11261
|
-
|
|
11262
|
-
|
|
10512
|
+
// TODO - Flesh out county splits
|
|
10513
|
+
[7 /* CountySplits */]: countySplitsDefn,
|
|
10514
|
+
[13 /* EfficiencyGap */]: efficiencyGapDefn
|
|
10515
|
+
/* TODO - More tests ... */
|
|
10516
|
+
};
|
|
10517
|
+
// Scorecard category definitions
|
|
10518
|
+
const validCategory = {
|
|
10519
|
+
catName: "Map Validations",
|
|
10520
|
+
catTests: [
|
|
10521
|
+
{ testID: 0 /* Complete */ },
|
|
10522
|
+
{ testID: 1 /* Contiguous */ },
|
|
10523
|
+
{ testID: 2 /* FreeOfHoles */ },
|
|
10524
|
+
{ testID: 3 /* EqualPopulation */ }
|
|
10525
|
+
],
|
|
10526
|
+
catNumeric: false,
|
|
10527
|
+
catWeight: undefined,
|
|
10528
|
+
catPrepareFn: doPrepareValidSection
|
|
10529
|
+
};
|
|
10530
|
+
const fairCategory = {
|
|
10531
|
+
// TODO - Change this label
|
|
10532
|
+
catName: "Is the map fair?",
|
|
10533
|
+
catTests: [
|
|
10534
|
+
{
|
|
10535
|
+
testID: 13 /* EfficiencyGap */,
|
|
10536
|
+
testWeight: 100
|
|
10537
|
+
}
|
|
10538
|
+
],
|
|
10539
|
+
catNumeric: true,
|
|
10540
|
+
catWeight: undefined,
|
|
10541
|
+
catPrepareFn: doPrepareFairSection
|
|
10542
|
+
};
|
|
10543
|
+
// TODO - Decide on the relative weights of these tests!
|
|
10544
|
+
// NOTE: 'testWeights' are simply relative, i.e., each normalized score is
|
|
10545
|
+
// multiplied by the associated 'testWeight', and the sum of those is divided
|
|
10546
|
+
// by the total weight. Weights don't have to add to 100.
|
|
10547
|
+
const bestCategory = {
|
|
10548
|
+
catName: "Traditional Districting Principles",
|
|
10549
|
+
catTests: [
|
|
10550
|
+
{
|
|
10551
|
+
testID: 4 /* PopulationDeviation */,
|
|
10552
|
+
testWeight: 10
|
|
10553
|
+
},
|
|
10554
|
+
{
|
|
10555
|
+
testID: 5 /* Reock */,
|
|
10556
|
+
testWeight: 25
|
|
10557
|
+
},
|
|
10558
|
+
{
|
|
10559
|
+
testID: 6 /* PolsbyPopper */,
|
|
10560
|
+
testWeight: 25
|
|
10561
|
+
}
|
|
10562
|
+
// TODO - Re-enable, when the metric is implemented
|
|
10563
|
+
// {
|
|
10564
|
+
// testID: T.Test.CountySplits,
|
|
10565
|
+
// testWeight: 50
|
|
10566
|
+
// }
|
|
10567
|
+
],
|
|
10568
|
+
catNumeric: true,
|
|
10569
|
+
catWeight: undefined,
|
|
10570
|
+
catPrepareFn: doPrepareBestSection
|
|
10571
|
+
};
|
|
10572
|
+
// The overall scorecard definition
|
|
10573
|
+
const scorecardDefn = {
|
|
10574
|
+
[0 /* Legal */]: validCategory,
|
|
10575
|
+
// TODO - NIY
|
|
10576
|
+
// [T.Suite.Fair]: fairCategory,
|
|
10577
|
+
[2 /* Best */]: bestCategory
|
|
11263
10578
|
};
|
|
11264
10579
|
// NORMALIZE RAW ANALYTICS
|
|
11265
10580
|
// Raw numeric analytics, such as population deviation, compactness, etc. are
|
|
@@ -11274,29 +10589,19 @@ function doConfigureScales(s) {
|
|
|
11274
10589
|
const LDLimit = 10.00 / 100; // Deviation threshold for LD's
|
|
11275
10590
|
const CDGoodEnough = 0.20 / 100;
|
|
11276
10591
|
const LDGoodEnough = (CDGoodEnough / CDLimit) * LDLimit;
|
|
11277
|
-
const
|
|
10592
|
+
const scale = (s.legislativeDistricts) ? [1.0 - LDLimit, 1.0 - LDGoodEnough] : [1.0 - CDLimit, 1.0 - CDGoodEnough];
|
|
11278
10593
|
// const scale = [1.0 - CDLimit, 1.0 - CDGoodEnough];
|
|
11279
|
-
s.testScales[4 /* PopulationDeviation */] = {
|
|
11280
|
-
s.testScales[5 /* Reock */] = {
|
|
11281
|
-
s.testScales[6 /* PolsbyPopper */] = {
|
|
11282
|
-
const
|
|
11283
|
-
|
|
11284
|
-
// TODO - SPLITTING: Experiment w/ this multiplier. Only allowing the expected
|
|
11285
|
-
// number of county splits seems too stringent, empirically.
|
|
11286
|
-
const allowableCountySplitsMultiplier = 1.5;
|
|
11287
|
-
const nAllowableSplits = Math.min(allowableCountySplitsMultiplier * (nDistricts - 1));
|
|
11288
|
-
const countySplittingThreshold = ((nAllowableSplits * 1.71) + ((nCounties - nAllowableSplits) * 1.0)) / nCounties;
|
|
11289
|
-
const countySplittingScale = [1.0, countySplittingThreshold];
|
|
11290
|
-
s.testScales[8 /* CountySplitting */] = { scale: countySplittingScale, bInvertScaled: true };
|
|
11291
|
-
const districtSplittingThreshold = 1.5;
|
|
11292
|
-
const districtSplittingScale = [1.0, districtSplittingThreshold];
|
|
11293
|
-
s.testScales[9 /* DistrictSplitting */] = { scale: districtSplittingScale, bInvertScaled: true };
|
|
10594
|
+
s.testScales[4 /* PopulationDeviation */] = { testScale: scale, testInvertp: true };
|
|
10595
|
+
s.testScales[5 /* Reock */] = { testScale: [0.25, 0.50], testInvertp: false };
|
|
10596
|
+
s.testScales[6 /* PolsbyPopper */] = { testScale: [0.10, 0.50], testInvertp: false };
|
|
10597
|
+
const SPLITLimit = 0.1; // TODO - Just a placeholder default maximum (10%)
|
|
10598
|
+
s.testScales[7 /* CountySplits */] = { testScale: [1.0 - SPLITLimit, 1.0], testInvertp: true };
|
|
11294
10599
|
// TODO - More analytics ...
|
|
11295
10600
|
}
|
|
11296
10601
|
exports.doConfigureScales = doConfigureScales;
|
|
11297
10602
|
// Postprocess analytics - Normalize numeric results and derive secondary tests.
|
|
11298
10603
|
// Do this after analytics have been run and before preparing a test log or scorecard.
|
|
11299
|
-
function doAnalyzePostProcessing(s
|
|
10604
|
+
function doAnalyzePostProcessing(s) {
|
|
11300
10605
|
// Normalize the raw scores for all the numerics tests
|
|
11301
10606
|
let testResults = U.getNumericObjectKeys(testDefns);
|
|
11302
10607
|
for (let testID of testResults) {
|
|
@@ -11304,18 +10609,642 @@ function doAnalyzePostProcessing(s, bLog = false) {
|
|
|
11304
10609
|
let testResult = s.getTest(testID);
|
|
11305
10610
|
let rawScore = testResult['score'];
|
|
11306
10611
|
let normalizedScore;
|
|
11307
|
-
|
|
10612
|
+
let { testScale, testInvertp } = s.testScales[testID];
|
|
10613
|
+
normalizedScore = U.normalize(rawScore, testScale, testInvertp);
|
|
11308
10614
|
testResult['normalizedScore'] = normalizedScore;
|
|
11309
10615
|
// Add the scale used to normalize the raw score to the details
|
|
11310
|
-
testResult['details']['scale'] =
|
|
10616
|
+
testResult['details']['scale'] = testScale;
|
|
11311
10617
|
}
|
|
11312
10618
|
}
|
|
11313
10619
|
// Derive secondary tests
|
|
11314
|
-
analyze_1.doDeriveSecondaryTests(s
|
|
10620
|
+
analyze_1.doDeriveSecondaryTests(s);
|
|
11315
10621
|
// Toggle the semaphore, so postprocessing isn't for both the testlog & scorecard
|
|
11316
10622
|
s.bPostProcessingDone = true;
|
|
11317
10623
|
}
|
|
11318
10624
|
exports.doAnalyzePostProcessing = doAnalyzePostProcessing;
|
|
10625
|
+
// Prepare a structured but unformatted scorecard, from the test results
|
|
10626
|
+
function doGenerateScorecard(s) {
|
|
10627
|
+
if (!(s.bPostProcessingDone)) {
|
|
10628
|
+
doAnalyzePostProcessing(s);
|
|
10629
|
+
}
|
|
10630
|
+
// Create a new scorecard
|
|
10631
|
+
let scorecard = {};
|
|
10632
|
+
// Filter the defined scorecard categories by the requested test suites
|
|
10633
|
+
let categories = U.getNumericObjectKeys(scorecardDefn);
|
|
10634
|
+
let suitesRequested = s.config['suites'];
|
|
10635
|
+
categories = categories.filter(x => suitesRequested.includes(x));
|
|
10636
|
+
// ... and initialize each one in the new scorecard
|
|
10637
|
+
for (let c of categories) {
|
|
10638
|
+
scorecard[c] = {};
|
|
10639
|
+
scorecard[c]['catName'] = scorecardDefn[c]['catName'];
|
|
10640
|
+
scorecard[c]['catTests'] = {};
|
|
10641
|
+
// scorecard[c]['catScore'] = undefined;
|
|
10642
|
+
}
|
|
10643
|
+
// For each scorecard category
|
|
10644
|
+
for (let c of categories) {
|
|
10645
|
+
// Grab the scorecard category definition
|
|
10646
|
+
let { catName, catTests, catNumeric } = scorecardDefn[c];
|
|
10647
|
+
let numericCategoryScore = 0;
|
|
10648
|
+
let totalWeight = 0;
|
|
10649
|
+
let booleanCategoryScore = true;
|
|
10650
|
+
// Process the results for each test result in the category
|
|
10651
|
+
for (let testDefn of catTests) {
|
|
10652
|
+
// Get the config info for the test
|
|
10653
|
+
let testID = testDefn['testID'];
|
|
10654
|
+
// ... and the actual test result
|
|
10655
|
+
let testResult = s.getTest(testID);
|
|
10656
|
+
// Create a new test entry for the scorecard
|
|
10657
|
+
let testReport = U.deepCopy(testResult);
|
|
10658
|
+
// Add the name
|
|
10659
|
+
testReport['name'] = testDefns[testID]['name'];
|
|
10660
|
+
if (catNumeric) {
|
|
10661
|
+
// Normalize raw numeric scores ... moved to FIRST PASS above
|
|
10662
|
+
// Accumulate a category score
|
|
10663
|
+
let normalizedScore = testReport['normalizedScore'];
|
|
10664
|
+
numericCategoryScore += normalizedScore * testDefn['testWeight'];
|
|
10665
|
+
totalWeight += testDefn['testWeight'];
|
|
10666
|
+
}
|
|
10667
|
+
else {
|
|
10668
|
+
// AND together pass/fail tests into a category score
|
|
10669
|
+
if (!testReport['score']) {
|
|
10670
|
+
booleanCategoryScore = false;
|
|
10671
|
+
}
|
|
10672
|
+
}
|
|
10673
|
+
scorecard[c]['catTests'][testID] = testReport;
|
|
10674
|
+
}
|
|
10675
|
+
// Set the category score
|
|
10676
|
+
if (catNumeric) {
|
|
10677
|
+
scorecard[c]['catScore'] = Math.round(numericCategoryScore / totalWeight);
|
|
10678
|
+
}
|
|
10679
|
+
else {
|
|
10680
|
+
scorecard[c]['catScore'] = booleanCategoryScore;
|
|
10681
|
+
}
|
|
10682
|
+
}
|
|
10683
|
+
// TODO - Compute an overall score from the category weights
|
|
10684
|
+
return scorecard;
|
|
10685
|
+
}
|
|
10686
|
+
// Prepare a formatted scorecard suitable for rendering
|
|
10687
|
+
function doPrepareScorecard(s) {
|
|
10688
|
+
// Initialize the output format
|
|
10689
|
+
let text = { data: [] };
|
|
10690
|
+
let blocks = text.data;
|
|
10691
|
+
// If the plan as already been analyzed, prepare a scorecard
|
|
10692
|
+
if (s.bPlanAnalyzed) {
|
|
10693
|
+
// Create and cache a new, unformatted scorecard
|
|
10694
|
+
s.scorecard = doGenerateScorecard(s);
|
|
10695
|
+
// Create a scorecard header
|
|
10696
|
+
blocks.push({ variant: 'h4', text: `Analysis - NC 2019 Special Edition` });
|
|
10697
|
+
blocks.push({ variant: 'body1', text: `In response to the recent court ruling in North Carolina and the court's requirement for transparency, we are pleased to provide the general public with early access to this base set of redistricting analytics. Stay tuned for more updates!` });
|
|
10698
|
+
blocks.push({ variant: 'body1', text: `For more details, see our blog post on Medium.` });
|
|
10699
|
+
// Prepare each scorecard category
|
|
10700
|
+
blocks.push({ variant: 'beginExpansion', text: `Overall Plan` });
|
|
10701
|
+
let categories = U.getNumericObjectKeys(s.scorecard);
|
|
10702
|
+
for (let c of categories) {
|
|
10703
|
+
let sectionPrepareFn = scorecardDefn[c]['catPrepareFn'];
|
|
10704
|
+
let sectionText = sectionPrepareFn(s, c);
|
|
10705
|
+
blocks.push(...sectionText);
|
|
10706
|
+
}
|
|
10707
|
+
// blocks.push({ variant: 'body1', text: `` });
|
|
10708
|
+
// blocks.push({ variant: 'body1', text: `There is much more analysis coming of county splitting, partisan fairness, and the opportunity for minority representation! For now, you can glean a lot from the district statistics below.` });
|
|
10709
|
+
blocks.push({ variant: 'endExpansion' });
|
|
10710
|
+
// Report district statistics
|
|
10711
|
+
blocks.push({ variant: 'beginExpansion', text: `Individual Districts` });
|
|
10712
|
+
let districtStatisticsText = doPrepareDistrictStatistics(s);
|
|
10713
|
+
blocks.push(...districtStatisticsText);
|
|
10714
|
+
blocks.push({ variant: 'endExpansion' });
|
|
10715
|
+
// Report what datasets were used
|
|
10716
|
+
let c = s.config['datasets']["CENSUS" /* CENSUS */];
|
|
10717
|
+
let v = s.config['datasets']["VAP" /* VAP */];
|
|
10718
|
+
let e = s.config['datasets']["ELECTION" /* ELECTION */];
|
|
10719
|
+
blocks.push({ variant: 'beginExpansion', text: `About the Data` });
|
|
10720
|
+
blocks.push({ variant: 'body1', text: `These are the 4 datasets used in this analysis:` });
|
|
10721
|
+
// TODO - Get the shape "dataset" from dra-client
|
|
10722
|
+
blocks.push({ variant: 'body1', text: `* Shapes: 2010 VTD shapes` });
|
|
10723
|
+
blocks.push({ variant: 'body1', text: `* Census: ${D.DatasetDescriptions[c]}` });
|
|
10724
|
+
blocks.push({ variant: 'body1', text: `* VAP: ${D.DatasetDescriptions[v]}` });
|
|
10725
|
+
blocks.push({ variant: 'body1', text: `* Elections: ${D.DatasetDescriptions[e]}` });
|
|
10726
|
+
blocks.push({ variant: 'endExpansion' });
|
|
10727
|
+
blocks.push({ variant: 'body1', text: `` });
|
|
10728
|
+
blocks.push({
|
|
10729
|
+
"variant": "link",
|
|
10730
|
+
"text": "Questions or comments?",
|
|
10731
|
+
"label": "analytics@davesredistricting.org",
|
|
10732
|
+
"link": "mailto:analytics@davesredistricting.org"
|
|
10733
|
+
});
|
|
10734
|
+
}
|
|
10735
|
+
// Otherwise, return a blank scorecard
|
|
10736
|
+
return text;
|
|
10737
|
+
}
|
|
10738
|
+
exports.doPrepareScorecard = doPrepareScorecard;
|
|
10739
|
+
function doPrepareDistrictStatistics(s) {
|
|
10740
|
+
let text = { data: [] };
|
|
10741
|
+
let blocks = text.data;
|
|
10742
|
+
blocks.push({ variant: 'beginTable' });
|
|
10743
|
+
blocks.push({ variant: 'row', cells: ['ID', 'Total', 'Δ%', 'OK?', '*', 'Dem', 'Rep', 'White', 'Minority', 'Black', 'Hispanic', 'Pacific', 'Asian', 'Native'] });
|
|
10744
|
+
for (let d = 0; d < s.districts.numberOfRows(); d++) {
|
|
10745
|
+
let tot = s.districts.statistics[D.DistrictField.TotalPop][d];
|
|
10746
|
+
if (tot == 0)
|
|
10747
|
+
blocks.push({ variant: 'row', cells: [String(d), '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-'] });
|
|
10748
|
+
else {
|
|
10749
|
+
tot = Math.round(tot);
|
|
10750
|
+
let dev = fractionToPercentage(s.districts.statistics[D.DistrictField.PopDevPct][d]);
|
|
10751
|
+
let bEq = s.districts.statistics[D.DistrictField.bEqualPop][d];
|
|
10752
|
+
let bC = s.districts.statistics[D.DistrictField.bContiguous][d]
|
|
10753
|
+
&& s.districts.statistics[D.DistrictField.bNotEmbedded][d];
|
|
10754
|
+
let dPct = fractionToPercentage(s.districts.statistics[D.DistrictField.DemPct][d]);
|
|
10755
|
+
let rPct = fractionToPercentage(s.districts.statistics[D.DistrictField.RepPct][d]);
|
|
10756
|
+
let wPct = fractionToPercentage(s.districts.statistics[D.DistrictField.WhitePct][d]);
|
|
10757
|
+
let mPct = fractionToPercentage(s.districts.statistics[D.DistrictField.MinorityPct][d]);
|
|
10758
|
+
let bPct = fractionToPercentage(s.districts.statistics[D.DistrictField.BlackPct][d]);
|
|
10759
|
+
let hPct = fractionToPercentage(s.districts.statistics[D.DistrictField.HispanicPct][d]);
|
|
10760
|
+
let pPct = fractionToPercentage(s.districts.statistics[D.DistrictField.PacificPct][d]);
|
|
10761
|
+
let aPct = fractionToPercentage(s.districts.statistics[D.DistrictField.AsianPct][d]);
|
|
10762
|
+
let nPct = fractionToPercentage(s.districts.statistics[D.DistrictField.NativePct][d]);
|
|
10763
|
+
let id;
|
|
10764
|
+
if (d == 0)
|
|
10765
|
+
id = "??";
|
|
10766
|
+
else if (d == (s.districts.numberOfRows() - 1))
|
|
10767
|
+
id = " ";
|
|
10768
|
+
else
|
|
10769
|
+
id = String(d);
|
|
10770
|
+
blocks.push({
|
|
10771
|
+
variant: 'row',
|
|
10772
|
+
cells: [
|
|
10773
|
+
`${id}`,
|
|
10774
|
+
`${formatInteger(tot)}`,
|
|
10775
|
+
`${formatPercentage(dev)}%`,
|
|
10776
|
+
`${pfBoolToString(bEq)}`,
|
|
10777
|
+
`${pfBoolToString(bC)}`,
|
|
10778
|
+
`${formatPercentage(dPct)}%`,
|
|
10779
|
+
`${formatPercentage(rPct)}%`,
|
|
10780
|
+
`${formatPercentage(wPct)}%`,
|
|
10781
|
+
`${formatPercentage(mPct)}%`,
|
|
10782
|
+
`${formatPercentage(bPct)}%`,
|
|
10783
|
+
`${formatPercentage(hPct)}%`,
|
|
10784
|
+
`${formatPercentage(pPct)}%`,
|
|
10785
|
+
`${formatPercentage(aPct)}%`,
|
|
10786
|
+
`${formatPercentage(nPct)}%`
|
|
10787
|
+
]
|
|
10788
|
+
});
|
|
10789
|
+
}
|
|
10790
|
+
}
|
|
10791
|
+
blocks.push({ variant: 'endTable' });
|
|
10792
|
+
return blocks;
|
|
10793
|
+
}
|
|
10794
|
+
// TEST LOG
|
|
10795
|
+
// Prepare formatted test results for rendering
|
|
10796
|
+
function doPrepareTestLog(s) {
|
|
10797
|
+
// Initialize the output format
|
|
10798
|
+
let text = { data: [] };
|
|
10799
|
+
let blocks = text.data;
|
|
10800
|
+
// If the plan as already been analyzed, prepare a test log
|
|
10801
|
+
if (s.bPlanAnalyzed) {
|
|
10802
|
+
if (!(s.bPostProcessingDone)) {
|
|
10803
|
+
doAnalyzePostProcessing(s);
|
|
10804
|
+
}
|
|
10805
|
+
// Create a test log header
|
|
10806
|
+
blocks.push({ variant: 'h4', text: `Test Log` });
|
|
10807
|
+
let testResults = U.getNumericObjectKeys(testDefns);
|
|
10808
|
+
let suitesRequested = new Set(s.config['suites']);
|
|
10809
|
+
for (let testID of testResults) {
|
|
10810
|
+
// Filter the defined tests by the requested test suites
|
|
10811
|
+
let inSuites = testDefns[testID]['suites'];
|
|
10812
|
+
if (!(U.isArrayEmpty(inSuites.filter(x => suitesRequested.has(x))))) {
|
|
10813
|
+
// Get the test result
|
|
10814
|
+
let testResult = s.getTest(testID);
|
|
10815
|
+
// Prepare the text for it, and append it to the output
|
|
10816
|
+
let testText = prepareTestEntry(testID, testResult);
|
|
10817
|
+
blocks.push(...testText);
|
|
10818
|
+
}
|
|
10819
|
+
}
|
|
10820
|
+
}
|
|
10821
|
+
// Otherwise, return a blank test log
|
|
10822
|
+
return text;
|
|
10823
|
+
}
|
|
10824
|
+
exports.doPrepareTestLog = doPrepareTestLog;
|
|
10825
|
+
function prepareTestEntry(testID, testResult) {
|
|
10826
|
+
let text = { data: [] };
|
|
10827
|
+
let blocks = text.data;
|
|
10828
|
+
let testName = testDefns[testID]['name'];
|
|
10829
|
+
let testNameTrailer = "";
|
|
10830
|
+
if (U.keyExists('trailer', testDefns[testID])) {
|
|
10831
|
+
testNameTrailer = testDefns[testID]['trailer'];
|
|
10832
|
+
}
|
|
10833
|
+
let testType = testDefns[testID]['externalType'];
|
|
10834
|
+
let bNormalize = testDefns[testID]['normalize'];
|
|
10835
|
+
let detailsFn = testDefns[testID]['detailsFn'];
|
|
10836
|
+
let detailsText = detailsFn(testResult);
|
|
10837
|
+
let score; // NOTE - Won't be undefined here
|
|
10838
|
+
let normalizedScore;
|
|
10839
|
+
let scoreText;
|
|
10840
|
+
// Get the score ...
|
|
10841
|
+
score = testResult['score'];
|
|
10842
|
+
// ... and format it for rendering
|
|
10843
|
+
switch (testType) {
|
|
10844
|
+
case TestType.PassFail: {
|
|
10845
|
+
scoreText = pfBoolToString(score);
|
|
10846
|
+
blocks.push({ variant: 'body1', text: `<b>${testName}</b>: ${scoreText} ${testNameTrailer}` });
|
|
10847
|
+
break;
|
|
10848
|
+
}
|
|
10849
|
+
case TestType.Percentage: {
|
|
10850
|
+
score = fractionToPercentage(score);
|
|
10851
|
+
if (bNormalize) {
|
|
10852
|
+
normalizedScore = testResult['normalizedScore'];
|
|
10853
|
+
blocks.push({ variant: 'body1', text: `<b>${testName}</b>: ${normalizedScore} / 100 : ${formatPercentage(score)}% ${testNameTrailer}` });
|
|
10854
|
+
}
|
|
10855
|
+
else {
|
|
10856
|
+
blocks.push({ variant: 'body1', text: `<b>${testName}</b>: ${formatPercentage(score)}% ${testNameTrailer}` });
|
|
10857
|
+
}
|
|
10858
|
+
break;
|
|
10859
|
+
}
|
|
10860
|
+
case TestType.Number: {
|
|
10861
|
+
if (bNormalize) {
|
|
10862
|
+
normalizedScore = testResult['normalizedScore'];
|
|
10863
|
+
blocks.push({ variant: 'body1', text: `<b>${testName}</b>: ${normalizedScore} / 100 : ${formatNumber(score)} ${testNameTrailer}` });
|
|
10864
|
+
}
|
|
10865
|
+
else {
|
|
10866
|
+
blocks.push({ variant: 'body1', text: `<b>${testName}</b>: ${formatNumber(score)} ${testNameTrailer}` });
|
|
10867
|
+
}
|
|
10868
|
+
break;
|
|
10869
|
+
}
|
|
10870
|
+
default: {
|
|
10871
|
+
// Unknown test type
|
|
10872
|
+
throw new RangeError();
|
|
10873
|
+
}
|
|
10874
|
+
}
|
|
10875
|
+
// Add the details text
|
|
10876
|
+
blocks.push(...detailsText);
|
|
10877
|
+
return blocks;
|
|
10878
|
+
}
|
|
10879
|
+
// FORMATTERS FOR TEST DETAILS
|
|
10880
|
+
function doPrepareCompleteDetails(testResult) {
|
|
10881
|
+
let text = { data: [] };
|
|
10882
|
+
let blocks = text.data;
|
|
10883
|
+
if (!U.isObjectEmpty(testResult['details'])) {
|
|
10884
|
+
let unassignedText = "";
|
|
10885
|
+
let emptyText = "";
|
|
10886
|
+
let missingText = "";
|
|
10887
|
+
if (U.keyExists('unassignedFeatures', testResult['details'])) {
|
|
10888
|
+
let unassignedFeatures = testResult['details']['unassignedFeatures'];
|
|
10889
|
+
let unassignedList = prepareListItems(unassignedFeatures);
|
|
10890
|
+
let unassignedTextTemplates = [
|
|
10891
|
+
`GEOID ${unassignedList} is not assigned to a district.`,
|
|
10892
|
+
`GEOIDs ${unassignedList} are not assigned to districts.`,
|
|
10893
|
+
`Several GEOIDs are not assigned to districts, including ${unassignedList}.`
|
|
10894
|
+
];
|
|
10895
|
+
unassignedText = prepareListText(unassignedFeatures, unassignedTextTemplates);
|
|
10896
|
+
}
|
|
10897
|
+
if (U.keyExists('emptyDistricts', testResult['details'])) {
|
|
10898
|
+
let emptyDistricts = testResult['details']['emptyDistricts'];
|
|
10899
|
+
let emptyList = prepareListItems(emptyDistricts);
|
|
10900
|
+
let emptyTextTemplates = [
|
|
10901
|
+
`District ${emptyList} is empty.`,
|
|
10902
|
+
`Districts ${emptyList} are empty.`,
|
|
10903
|
+
`Several districts are empty, including ${emptyList}.`
|
|
10904
|
+
];
|
|
10905
|
+
emptyText = prepareListText(emptyDistricts, emptyTextTemplates);
|
|
10906
|
+
}
|
|
10907
|
+
if (U.keyExists('missingDistricts', testResult['details'])) {
|
|
10908
|
+
missingText = `Not enough districts have been defined. `;
|
|
10909
|
+
}
|
|
10910
|
+
let detailsText = " " + unassignedText + emptyText + missingText;
|
|
10911
|
+
blocks.push({ variant: 'body1', text: detailsText });
|
|
10912
|
+
}
|
|
10913
|
+
return blocks;
|
|
10914
|
+
}
|
|
10915
|
+
function doPrepareContiguousDetails(testResult) {
|
|
10916
|
+
let text = { data: [] };
|
|
10917
|
+
let blocks = text.data;
|
|
10918
|
+
if (!U.isObjectEmpty(testResult['details'])) {
|
|
10919
|
+
let discontiguousDistricts = testResult['details']['discontiguousDistricts'];
|
|
10920
|
+
let discontiguousList = prepareListItems(discontiguousDistricts);
|
|
10921
|
+
let discontiguousTextTemplates = [
|
|
10922
|
+
`District ${discontiguousList} is not contiguous.`,
|
|
10923
|
+
`Districts ${discontiguousList} are not contiguous.`,
|
|
10924
|
+
`Several districts are not contiguous, including ${discontiguousList}.`
|
|
10925
|
+
];
|
|
10926
|
+
let detailsText = prepareListText(discontiguousDistricts, discontiguousTextTemplates);
|
|
10927
|
+
blocks.push({ variant: 'body1', text: detailsText });
|
|
10928
|
+
}
|
|
10929
|
+
return blocks;
|
|
10930
|
+
}
|
|
10931
|
+
function doPrepareFreeOfHolesDetails(testResult) {
|
|
10932
|
+
let text = { data: [] };
|
|
10933
|
+
let blocks = text.data;
|
|
10934
|
+
if (!U.isObjectEmpty(testResult['details'])) {
|
|
10935
|
+
let embeddedDistricts = testResult['details']['embeddedDistricts'];
|
|
10936
|
+
let embeddedList = prepareListItems(embeddedDistricts);
|
|
10937
|
+
let embeddedTextTemplates = [
|
|
10938
|
+
`District ${embeddedList} is fully embedded within another district.`,
|
|
10939
|
+
`Both districts ${embeddedList} are fully embedded within other districts.`,
|
|
10940
|
+
`Several districts are fully embedded within other districts, including ${embeddedList}.`
|
|
10941
|
+
];
|
|
10942
|
+
let detailsText = prepareListText(embeddedDistricts, embeddedTextTemplates);
|
|
10943
|
+
blocks.push({ variant: 'body1', text: detailsText });
|
|
10944
|
+
}
|
|
10945
|
+
return blocks;
|
|
10946
|
+
}
|
|
10947
|
+
function doPrepareEqualPopulationDetails(testResult) {
|
|
10948
|
+
let text = { data: [] };
|
|
10949
|
+
let blocks = text.data;
|
|
10950
|
+
if (!U.isObjectEmpty(testResult['details'])) {
|
|
10951
|
+
let popDevPct = fractionToPercentage(testResult['details']['deviation']);
|
|
10952
|
+
let thresholdPct = fractionToPercentage(1.0 - testResult['details']['thresholds'][0]);
|
|
10953
|
+
let detailsText = '';
|
|
10954
|
+
if (!(testResult['score'])) {
|
|
10955
|
+
detailsText = `The ${formatPercentage(popDevPct)}% population deviation is greater than the ${formatPercentage(thresholdPct)}% threshold tolerated by courts.`;
|
|
10956
|
+
}
|
|
10957
|
+
blocks.push({ variant: 'body1', text: detailsText });
|
|
10958
|
+
}
|
|
10959
|
+
return blocks;
|
|
10960
|
+
}
|
|
10961
|
+
function doPreparePopulationDeviationDetails(testResult) {
|
|
10962
|
+
let text = { data: [] };
|
|
10963
|
+
let blocks = text.data;
|
|
10964
|
+
let n = Math.round(testResult['details']['maxDeviation']);
|
|
10965
|
+
let term = "people";
|
|
10966
|
+
if (n == 1) {
|
|
10967
|
+
term = "person";
|
|
10968
|
+
}
|
|
10969
|
+
blocks.push({ variant: 'body1', text: `The maximum population deviation between districts is ${formatInteger(n)} ${term}.` });
|
|
10970
|
+
return blocks;
|
|
10971
|
+
}
|
|
10972
|
+
function doPrepareReockDetails(testResult) {
|
|
10973
|
+
let text = { data: [] };
|
|
10974
|
+
let blocks = text.data;
|
|
10975
|
+
// TODO - No details implemented yet
|
|
10976
|
+
return blocks;
|
|
10977
|
+
}
|
|
10978
|
+
function doPreparePolsbyPopperDetails(testResult) {
|
|
10979
|
+
let text = { data: [] };
|
|
10980
|
+
let blocks = text.data;
|
|
10981
|
+
// TODO - No details implemented yet
|
|
10982
|
+
return blocks;
|
|
10983
|
+
}
|
|
10984
|
+
function doPrepareEfficiencyGapDetails(testResult) {
|
|
10985
|
+
let text = { data: [] };
|
|
10986
|
+
let blocks = text.data;
|
|
10987
|
+
// TODO - Not yet implemented
|
|
10988
|
+
return blocks;
|
|
10989
|
+
}
|
|
10990
|
+
function doPrepareCountySplitDetails(testResult) {
|
|
10991
|
+
let text = { data: [] };
|
|
10992
|
+
let blocks = text.data;
|
|
10993
|
+
let unexpectedAffected = fractionToPercentage(testResult['details']['unexpectedAffected']);
|
|
10994
|
+
let affectedText = `affecting ${formatPercentage(unexpectedAffected)}% of the total population.`;
|
|
10995
|
+
let countiesSplitUnexpectedly = testResult['details']['countiesSplitUnexpectedly'];
|
|
10996
|
+
let nCountiesSplitUnexpectedly = countiesSplitUnexpectedly.length;
|
|
10997
|
+
let nUnexpectedSplits = testResult['details']['unexpectedSplits'];
|
|
10998
|
+
let splitList = prepareListItems(countiesSplitUnexpectedly);
|
|
10999
|
+
let splitTextTemplates = [
|
|
11000
|
+
`${splitList} county is split unexpectedly, `,
|
|
11001
|
+
`${splitList} counties are split unexpectedly, `,
|
|
11002
|
+
// `These ${formatInteger(nCountiesSplitUnexpectedly)} counties are split unexpectedly--${splitList}--`
|
|
11003
|
+
`${splitList} counties are split unexpectedly ${formatInteger(nUnexpectedSplits)} times, `
|
|
11004
|
+
];
|
|
11005
|
+
let detailsText = prepareListText(countiesSplitUnexpectedly, splitTextTemplates) + affectedText;
|
|
11006
|
+
blocks.push({ variant: 'body1', text: detailsText });
|
|
11007
|
+
return blocks;
|
|
11008
|
+
}
|
|
11009
|
+
// FORMATTERS FOR CATEGORIES
|
|
11010
|
+
// This function parses & formats the 'Valid' section of a scorecard.
|
|
11011
|
+
function doPrepareValidSection(s, c) {
|
|
11012
|
+
// Get the category meta data
|
|
11013
|
+
let { catName, catScore, catTests } = s.scorecard[c];
|
|
11014
|
+
let testReport;
|
|
11015
|
+
// Initialize the section text
|
|
11016
|
+
let text = { data: [] };
|
|
11017
|
+
let blocks = text.data;
|
|
11018
|
+
let testText;
|
|
11019
|
+
// Section header
|
|
11020
|
+
let stringScore = pfBoolToString(catScore);
|
|
11021
|
+
blocks.push({ variant: 'beginExpansion', text: `${catName}` });
|
|
11022
|
+
// blocks.push({ variant: 'beginExpansion', text: `${catName} ${stringScore}` });
|
|
11023
|
+
// Complete
|
|
11024
|
+
testReport = catTests[0 /* Complete */];
|
|
11025
|
+
testText = prepareTestEntry(0 /* Complete */, testReport);
|
|
11026
|
+
blocks.push(...testText);
|
|
11027
|
+
// Contiguous
|
|
11028
|
+
testReport = catTests[1 /* Contiguous */];
|
|
11029
|
+
testText = prepareTestEntry(1 /* Contiguous */, testReport);
|
|
11030
|
+
blocks.push(...testText);
|
|
11031
|
+
// Free of holes (no embedded districts)
|
|
11032
|
+
testReport = catTests[2 /* FreeOfHoles */];
|
|
11033
|
+
testText = prepareTestEntry(2 /* FreeOfHoles */, testReport);
|
|
11034
|
+
blocks.push(...testText);
|
|
11035
|
+
// Equal population (w/in the appropriate legal threshold)
|
|
11036
|
+
testReport = catTests[3 /* EqualPopulation */];
|
|
11037
|
+
testText = prepareTestEntry(3 /* EqualPopulation */, testReport);
|
|
11038
|
+
blocks.push(...testText);
|
|
11039
|
+
blocks.push({ variant: 'endExpansion' });
|
|
11040
|
+
return blocks;
|
|
11041
|
+
}
|
|
11042
|
+
// TODO - NIY
|
|
11043
|
+
// This function parses & formats the 'Fair' section of a scorecard.
|
|
11044
|
+
function doPrepareFairSection(s, c) {
|
|
11045
|
+
// Get the category meta data
|
|
11046
|
+
let { catName, catScore, catTests } = s.scorecard[c];
|
|
11047
|
+
let testReport;
|
|
11048
|
+
// Initialize the section text
|
|
11049
|
+
let text = { data: [] };
|
|
11050
|
+
let blocks = text.data;
|
|
11051
|
+
let testText;
|
|
11052
|
+
// Section header
|
|
11053
|
+
blocks.push({ variant: 'beginExpansion', text: `${catName} ${catScore} of 100` });
|
|
11054
|
+
// TODO - Flesh this out
|
|
11055
|
+
// There's only one test: Population Deviation.
|
|
11056
|
+
// testReport = catTests[T.Test.PopulationDeviation];
|
|
11057
|
+
// testText = prepareTestEntry(T.Test.PopulationDeviation, testReport);
|
|
11058
|
+
// blocks.push(...testText);
|
|
11059
|
+
blocks.push({ variant: 'endExpansion' });
|
|
11060
|
+
return blocks;
|
|
11061
|
+
}
|
|
11062
|
+
// This function parses & formats the 'Best' section of a scorecard.
|
|
11063
|
+
function doPrepareBestSection(s, c) {
|
|
11064
|
+
// Get the category meta data
|
|
11065
|
+
let { catName, catScore, catTests } = s.scorecard[c];
|
|
11066
|
+
let testReport;
|
|
11067
|
+
// Initialize the section text
|
|
11068
|
+
let text = { data: [] };
|
|
11069
|
+
let blocks = text.data;
|
|
11070
|
+
let testText;
|
|
11071
|
+
// Section header
|
|
11072
|
+
blocks.push({ variant: 'beginExpansion', text: `${catName} (${catScore} of 100)` });
|
|
11073
|
+
// Population deviation
|
|
11074
|
+
testReport = catTests[4 /* PopulationDeviation */];
|
|
11075
|
+
testText = prepareTestEntry(4 /* PopulationDeviation */, testReport);
|
|
11076
|
+
blocks.push(...testText);
|
|
11077
|
+
// Compactness
|
|
11078
|
+
testReport = catTests[5 /* Reock */];
|
|
11079
|
+
let normalizedReock = testReport['normalizedScore'];
|
|
11080
|
+
let reockTestText = prepareTestEntry(5 /* Reock */, testReport);
|
|
11081
|
+
testReport = catTests[6 /* PolsbyPopper */];
|
|
11082
|
+
let normalizedPolsbyPopper = testReport['normalizedScore'];
|
|
11083
|
+
let polsbyPopperTestText = prepareTestEntry(6 /* PolsbyPopper */, testReport);
|
|
11084
|
+
let compactnessScore = (normalizedReock + normalizedPolsbyPopper) / 2;
|
|
11085
|
+
blocks.push({ variant: 'body1', text: `Compactness: ${compactnessScore} / 100` });
|
|
11086
|
+
blocks.push(...reockTestText);
|
|
11087
|
+
blocks.push(...polsbyPopperTestText);
|
|
11088
|
+
// County splits
|
|
11089
|
+
// TODO - Re-enable this, when the county-splitting metric is implemented.
|
|
11090
|
+
// testReport = catTests[T.Test.CountySplits];
|
|
11091
|
+
// testText = prepareTestEntry(T.Test.CountySplits, testReport);
|
|
11092
|
+
// blocks.push(...testText);
|
|
11093
|
+
blocks.push({ variant: 'endExpansion' });
|
|
11094
|
+
return blocks;
|
|
11095
|
+
}
|
|
11096
|
+
// FORMATTING HELPERS
|
|
11097
|
+
// Convert a boolean representing Pass/Fail to a string
|
|
11098
|
+
function pfBoolToString(score) {
|
|
11099
|
+
if (score) {
|
|
11100
|
+
return "Yes";
|
|
11101
|
+
}
|
|
11102
|
+
else {
|
|
11103
|
+
return "No";
|
|
11104
|
+
}
|
|
11105
|
+
}
|
|
11106
|
+
function fractionToPercentage(f) {
|
|
11107
|
+
return f * 100;
|
|
11108
|
+
}
|
|
11109
|
+
function formatNumber(n) {
|
|
11110
|
+
let p = S.PRECISION;
|
|
11111
|
+
return n.toLocaleString('en-US', { minimumFractionDigits: p, maximumFractionDigits: p });
|
|
11112
|
+
}
|
|
11113
|
+
function formatPercentage(n) {
|
|
11114
|
+
let p = (S.PRECISION / 2);
|
|
11115
|
+
return n.toLocaleString('en-US', { minimumFractionDigits: p, maximumFractionDigits: p });
|
|
11116
|
+
}
|
|
11117
|
+
function formatInteger(i) {
|
|
11118
|
+
return new Intl.NumberFormat().format(i);
|
|
11119
|
+
}
|
|
11120
|
+
// Prepare the items in a list for rendering
|
|
11121
|
+
function prepareListItems(list) {
|
|
11122
|
+
let nItems = list.length;
|
|
11123
|
+
let listStr;
|
|
11124
|
+
switch (nItems) {
|
|
11125
|
+
case 1: {
|
|
11126
|
+
listStr = list[0];
|
|
11127
|
+
break;
|
|
11128
|
+
}
|
|
11129
|
+
case 2: {
|
|
11130
|
+
listStr = list[0] + " and " + list[1];
|
|
11131
|
+
break;
|
|
11132
|
+
}
|
|
11133
|
+
default: {
|
|
11134
|
+
let listWithCommas = list.join(', ');
|
|
11135
|
+
let lastCommaIndex = listWithCommas.length - ((list[list.length - 1].length) + 1);
|
|
11136
|
+
let beforeAnd = listWithCommas.substr(0, lastCommaIndex);
|
|
11137
|
+
let afterAnd = listWithCommas.substr(lastCommaIndex + 1);
|
|
11138
|
+
listStr = beforeAnd + " and " + afterAnd;
|
|
11139
|
+
break;
|
|
11140
|
+
}
|
|
11141
|
+
}
|
|
11142
|
+
return listStr;
|
|
11143
|
+
}
|
|
11144
|
+
// Pick the rendering text for the appropriate list length
|
|
11145
|
+
function prepareListText(list, listTemplates) {
|
|
11146
|
+
let nItems = list.length;
|
|
11147
|
+
switch (nItems) {
|
|
11148
|
+
case 1: {
|
|
11149
|
+
return listTemplates[0];
|
|
11150
|
+
break;
|
|
11151
|
+
}
|
|
11152
|
+
case 2: {
|
|
11153
|
+
return listTemplates[1];
|
|
11154
|
+
break;
|
|
11155
|
+
}
|
|
11156
|
+
default: {
|
|
11157
|
+
return listTemplates[2];
|
|
11158
|
+
break;
|
|
11159
|
+
}
|
|
11160
|
+
}
|
|
11161
|
+
}
|
|
11162
|
+
/* COMMAND-LINE TESTS
|
|
11163
|
+
|
|
11164
|
+
node main.js scorecard -v -x NC -n 13 -p ~/src/district-analytics/data/SAMPLE-BG-map.csv -d ~/src/district-analytics/data/SAMPLE-BG-data2.json -s ~/src/district-analytics/data/SAMPLE-BG-shapes.geojson -g ~/src/district-analytics/data/SAMPLE-BG-graph.json -c ~/src/district-analytics/data/SAMPLE-COUNTY.geojson
|
|
11165
|
+
node --inspect --inspect-brk main.js scorecard -v -x NC -n 13 -p ~/src/district-analytics/data/SAMPLE-BG-map.csv -d ~/src/district-analytics/data/SAMPLE-BG-data2.json -s ~/src/district-analytics/data/SAMPLE-BG-shapes.geojson -g ~/src/district-analytics/data/SAMPLE-BG-graph.json -c ~/src/district-analytics/data/SAMPLE-COUNTY.geojson
|
|
11166
|
+
|
|
11167
|
+
-or-
|
|
11168
|
+
|
|
11169
|
+
./main.js scorecard -v -x NC -n 13 -p ../data/SAMPLE-BG-map.csv -d ../data/SAMPLE-BG-data2.json -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -c ../data/SAMPLE-COUNTY.geojson
|
|
11170
|
+
./main.js --inspect --inspect-brk scorecard -v -x NC -n 13 -p ../data/SAMPLE-BG-map.csv -d ../data/SAMPLE-BG-data2.json -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -c ../data/SAMPLE-COUNTY.geojson
|
|
11171
|
+
|
|
11172
|
+
These calls work at the project directory, using samples in the data/ directory:
|
|
11173
|
+
|
|
11174
|
+
./main.js testlog -v -x NC -n 13 -p ../data/SAMPLE-BG-map.csv -d ../data/SAMPLE-BG-data2.json -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -c ../data/SAMPLE-COUNTY.geojson
|
|
11175
|
+
|
|
11176
|
+
TODO - Fix this
|
|
11177
|
+
./main.js testlog -v -x NC -n 13 -p ../data/SAMPLE-BG-map-missing.csv -d ../data/SAMPLE-BG-data2.json -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -c ../data/SAMPLE-COUNTY.geojson
|
|
11178
|
+
./main.js scorecard -v -x NC-n 13 -p ../data/SAMPLE-BG-map-missing.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
|
|
11179
|
+
|
|
11180
|
+
./main.js testlog -v -x NC -n 13 -p ../data/SAMPLE-BG-map-unassigned.csv -d ../data/SAMPLE-BG-data2.json -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -c ../data/SAMPLE-COUNTY.geojson
|
|
11181
|
+
./main.js scorecard -v -x NC -n 13 -p ../data/SAMPLE-BG-map-unassigned.csv -d ../data/SAMPLE-BG-data2.json -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -c ../data/SAMPLE-COUNTY.geojson
|
|
11182
|
+
|
|
11183
|
+
TODO - HERE
|
|
11184
|
+
|
|
11185
|
+
./main.js -v -x NC testlog -p ../data/SAMPLE-BG-map.csv -d ../data/SAMPLE-BG-data2.json -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -c ../data/SAMPLE-COUNTY.geojson --empty
|
|
11186
|
+
./main.js -v -x NC scorecard -p ../data/SAMPLE-BG-map.csv -d ../data/SAMPLE-BG-data2.json -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -c ../data/SAMPLE-COUNTY.geojson --empty
|
|
11187
|
+
|
|
11188
|
+
TODO
|
|
11189
|
+
|
|
11190
|
+
./main.js testlog -n 13 -p ../data/SAMPLE-BG-map-discontiguous.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
|
|
11191
|
+
./main.js scorecard -n 13 -p ../data/SAMPLE-BG-map-discontiguous.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
|
|
11192
|
+
|
|
11193
|
+
./main.js testlog -n 13 -p ../data/SAMPLE-BG-map-hole.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
|
|
11194
|
+
./main.js scorecard -n 13 -p ../data/SAMPLE-BG-map-hole.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
|
|
11195
|
+
|
|
11196
|
+
|
|
11197
|
+
node --inspect --inspect-brk main.js scorecard -v -n 13 -p ../data/SAMPLE-BG-map.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
|
|
11198
|
+
node main.js scorecard -v -n 13 -p ../data/SAMPLE-BG-map.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
|
|
11199
|
+
|
|
11200
|
+
|
|
11201
|
+
These calls test NC block-level data stored in my ~/data/redistricting-data/2010/compact/sample directory:
|
|
11202
|
+
|
|
11203
|
+
node --inspect --inspect-brk main.js scorecard -v -n 13 -p ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-map.csv -c ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-census.csv -s ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-shapes.geojson -g ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-graph.json -e ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-election.csv -f ~/data/redistricting-data/2010/compact/sample/SAMPLE-FIPS-county-name-map.csv
|
|
11204
|
+
node --inspect --inspect-brk main.js scorecard -v -n 13 -p ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-map.csv -c ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-census.csv -s ~/data/redistricting-data/2010/compact/sample/tl_2018_37_tabblock10.json -g ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-graph.json -e ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-election.csv -f ~/data/redistricting-data/2010/compact/sample/SAMPLE-FIPS-county-name-map.csv
|
|
11205
|
+
|
|
11206
|
+
node main.js scorecard -v -n 13 -p ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-map.csv -c ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-census.csv -s ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-shapes.geojson -g ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-graph.json -e ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-election.csv -f ~/data/redistricting-data/2010/compact/sample/SAMPLE-FIPS-county-name-map.csv
|
|
11207
|
+
node main.js scorecard -v -n 13 -p ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-map.csv -c ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-census.csv -s ~/data/redistricting-data/2010/compact/sample/tl_2018_37_tabblock10.json -g ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-graph.json -e ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-election.csv -f ~/data/redistricting-data/2010/compact/sample/SAMPLE-FIPS-county-name-map.csv
|
|
11208
|
+
|
|
11209
|
+
|
|
11210
|
+
|
|
11211
|
+
*/
|
|
11212
|
+
/* TODO - DELETE
|
|
11213
|
+
|
|
11214
|
+
These calls work at the project directory, using samples in the data/ directory:
|
|
11215
|
+
|
|
11216
|
+
./main.js testlog -n 13 -p ../data/SAMPLE-BG-map.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
|
|
11217
|
+
./main.js scorecard -n 13 -p ../data/SAMPLE-BG-map.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
|
|
11218
|
+
|
|
11219
|
+
./main.js testlog -n 13 -p ../data/SAMPLE-BG-map-missing.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
|
|
11220
|
+
./main.js scorecard -n 13 -p ../data/SAMPLE-BG-map-missing.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
|
|
11221
|
+
|
|
11222
|
+
./main.js testlog -n 13 -p ../data/SAMPLE-BG-map-unassigned.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
|
|
11223
|
+
./main.js scorecard -n 13 -p ../data/SAMPLE-BG-map-unassigned.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
|
|
11224
|
+
|
|
11225
|
+
./main.js testlog -n 13 -p ../data/SAMPLE-BG-map.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv --empty
|
|
11226
|
+
./main.js scorecard -n 13 -p ../data/SAMPLE-BG-map.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv --empty
|
|
11227
|
+
|
|
11228
|
+
./main.js testlog -n 13 -p ../data/SAMPLE-BG-map-discontiguous.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
|
|
11229
|
+
./main.js scorecard -n 13 -p ../data/SAMPLE-BG-map-discontiguous.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
|
|
11230
|
+
|
|
11231
|
+
./main.js testlog -n 13 -p ../data/SAMPLE-BG-map-hole.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
|
|
11232
|
+
./main.js scorecard -n 13 -p ../data/SAMPLE-BG-map-hole.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
|
|
11233
|
+
|
|
11234
|
+
|
|
11235
|
+
node --inspect --inspect-brk main.js scorecard -v -n 13 -p ../data/SAMPLE-BG-map.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
|
|
11236
|
+
node main.js scorecard -v -n 13 -p ../data/SAMPLE-BG-map.csv -c ../data/SAMPLE-BG-census.csv -s ../data/SAMPLE-BG-shapes.geojson -g ../data/SAMPLE-BG-graph.json -e ../data/SAMPLE-BG-election.csv -f ../data/SAMPLE-FIPS-county-name-map.csv
|
|
11237
|
+
|
|
11238
|
+
|
|
11239
|
+
These calls test NC block-level data stored in my ~/data/redistricting-data/2010/compact/sample directory:
|
|
11240
|
+
|
|
11241
|
+
node --inspect --inspect-brk main.js scorecard -v -n 13 -p ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-map.csv -c ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-census.csv -s ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-shapes.geojson -g ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-graph.json -e ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-election.csv -f ~/data/redistricting-data/2010/compact/sample/SAMPLE-FIPS-county-name-map.csv
|
|
11242
|
+
node --inspect --inspect-brk main.js scorecard -v -n 13 -p ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-map.csv -c ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-census.csv -s ~/data/redistricting-data/2010/compact/sample/tl_2018_37_tabblock10.json -g ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-graph.json -e ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-election.csv -f ~/data/redistricting-data/2010/compact/sample/SAMPLE-FIPS-county-name-map.csv
|
|
11243
|
+
|
|
11244
|
+
node main.js scorecard -v -n 13 -p ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-map.csv -c ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-census.csv -s ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-shapes.geojson -g ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-graph.json -e ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-election.csv -f ~/data/redistricting-data/2010/compact/sample/SAMPLE-FIPS-county-name-map.csv
|
|
11245
|
+
node main.js scorecard -v -n 13 -p ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-map.csv -c ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-census.csv -s ~/data/redistricting-data/2010/compact/sample/tl_2018_37_tabblock10.json -g ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-graph.json -e ~/data/redistricting-data/2010/compact/sample/SAMPLE-BLOCK-election.csv -f ~/data/redistricting-data/2010/compact/sample/SAMPLE-FIPS-county-name-map.csv
|
|
11246
|
+
|
|
11247
|
+
*/
|
|
11319
11248
|
|
|
11320
11249
|
|
|
11321
11250
|
/***/ }),
|
|
@@ -11340,17 +11269,11 @@ exports.PRECISION = 4;
|
|
|
11340
11269
|
exports.NORMALIZED_RANGE = 100;
|
|
11341
11270
|
// The dummy district ID for features not assigned districts yet
|
|
11342
11271
|
exports.NOT_ASSIGNED = 0;
|
|
11343
|
-
// TODO -
|
|
11272
|
+
// TODO - Discuss w/ Dave & Terry
|
|
11344
11273
|
// # of items to report as problematic (e.g., features, districts, etc.)
|
|
11345
11274
|
exports.NUMBER_OF_ITEMS_TO_REPORT = 10;
|
|
11346
11275
|
// The virtual geoID for "neighbors" in other states
|
|
11347
11276
|
exports.OUT_OF_STATE = "OUT_OF_STATE";
|
|
11348
|
-
// "Roughly equal" = average census block size / 2
|
|
11349
|
-
const AVERAGE_BLOCK_SIZE = 30;
|
|
11350
|
-
exports.EQUAL_TOLERANCE = AVERAGE_BLOCK_SIZE / 2;
|
|
11351
|
-
// County & district splitting weights
|
|
11352
|
-
exports.COUNTY_SPLITTING_WEIGHT = 0.8;
|
|
11353
|
-
exports.DISTRICT_SPLITTING_WEIGHT = 1.0 - exports.COUNTY_SPLITTING_WEIGHT;
|
|
11354
11277
|
|
|
11355
11278
|
|
|
11356
11279
|
/***/ }),
|
|
@@ -11367,15 +11290,8 @@ exports.DISTRICT_SPLITTING_WEIGHT = 1.0 - exports.COUNTY_SPLITTING_WEIGHT;
|
|
|
11367
11290
|
//
|
|
11368
11291
|
// UTILITIES
|
|
11369
11292
|
//
|
|
11370
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
11371
|
-
if (mod && mod.__esModule) return mod;
|
|
11372
|
-
var result = {};
|
|
11373
|
-
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
11374
|
-
result["default"] = mod;
|
|
11375
|
-
return result;
|
|
11376
|
-
};
|
|
11377
11293
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11378
|
-
const S =
|
|
11294
|
+
const S = __webpack_require__(/*! ./settings */ "./src/settings.ts");
|
|
11379
11295
|
// PLAN HELPERS
|
|
11380
11296
|
// Is a "neighbor" in state?
|
|
11381
11297
|
function isInState(geoID) {
|
|
@@ -11387,7 +11303,6 @@ function isOutOfState(geoID) {
|
|
|
11387
11303
|
return geoID == S.OUT_OF_STATE;
|
|
11388
11304
|
}
|
|
11389
11305
|
exports.isOutOfState = isOutOfState;
|
|
11390
|
-
// TODO - UNASSIGNED
|
|
11391
11306
|
// Get the districtID to which a geoID is assigned
|
|
11392
11307
|
function getDistrict(plan, geoID) {
|
|
11393
11308
|
// All geoIDs in a state *should be* assigned to a district (including the
|
|
@@ -11401,6 +11316,24 @@ function getDistrict(plan, geoID) {
|
|
|
11401
11316
|
}
|
|
11402
11317
|
}
|
|
11403
11318
|
exports.getDistrict = getDistrict;
|
|
11319
|
+
// Invert a feature assignment structure to sets of ids by district
|
|
11320
|
+
// NOTE - This is here vs. _data.ts, so it can also be used in cli.ts
|
|
11321
|
+
function invertPlan(plan) {
|
|
11322
|
+
let invertedPlan = {};
|
|
11323
|
+
// Add a dummy 'unassigned' district
|
|
11324
|
+
invertedPlan[S.NOT_ASSIGNED] = new Set();
|
|
11325
|
+
for (let geoID in plan) {
|
|
11326
|
+
let districtID = plan[geoID];
|
|
11327
|
+
// Make sure the set for the districtID exists
|
|
11328
|
+
if (!(objectContains(invertedPlan, districtID))) {
|
|
11329
|
+
invertedPlan[districtID] = new Set();
|
|
11330
|
+
}
|
|
11331
|
+
// Add the geoID to the districtID's set
|
|
11332
|
+
invertedPlan[districtID].add(geoID);
|
|
11333
|
+
}
|
|
11334
|
+
return invertedPlan;
|
|
11335
|
+
}
|
|
11336
|
+
exports.invertPlan = invertPlan;
|
|
11404
11337
|
// WORKING WITH GEOIDS
|
|
11405
11338
|
function parseGeoID(geoID) {
|
|
11406
11339
|
let parts = {};
|
|
@@ -11423,46 +11356,27 @@ function getFIPSFromCountyGeoID(geoID) {
|
|
|
11423
11356
|
return geoID.substring(2, 5);
|
|
11424
11357
|
}
|
|
11425
11358
|
exports.getFIPSFromCountyGeoID = getFIPSFromCountyGeoID;
|
|
11426
|
-
function isWaterOnly(geoID) {
|
|
11427
|
-
let waterOnlySignature = 'ZZZZZZ';
|
|
11428
|
-
if (geoID.indexOf(waterOnlySignature) >= 0)
|
|
11429
|
-
return true;
|
|
11430
|
-
else
|
|
11431
|
-
return false;
|
|
11432
|
-
}
|
|
11433
|
-
exports.isWaterOnly = isWaterOnly;
|
|
11434
11359
|
// NORMALIZING RESULTS
|
|
11435
|
-
|
|
11436
|
-
|
|
11437
|
-
|
|
11438
|
-
|
|
11439
|
-
// TODO - This works for Population Deviation, because the max is 1.0.
|
|
11440
|
-
// Generalize this???
|
|
11441
|
-
if (testScale.bInvertRaw) {
|
|
11360
|
+
// Convert a raw score [0-1] into a normalized score [0-100]
|
|
11361
|
+
function normalize(rawScore, scale, invertp = false) {
|
|
11362
|
+
// Invert the axis if necessary to make bigger = better
|
|
11363
|
+
if (invertp) {
|
|
11442
11364
|
rawScore = 1.0 - rawScore;
|
|
11443
11365
|
}
|
|
11444
11366
|
// Coerce the value to be w/in the given range
|
|
11367
|
+
let rangeMin = scale[0];
|
|
11368
|
+
let rangeMax = scale[1];
|
|
11445
11369
|
let coercedValue = Math.min(Math.max(rawScore, rangeMin), rangeMax);
|
|
11446
11370
|
// Scale the bounded value w/in the range [0 - (rangeMax - rangeMin)]
|
|
11447
11371
|
let scaledValue = (coercedValue - rangeMin) / (rangeMax - rangeMin);
|
|
11448
|
-
// TODO - SPLITTING
|
|
11449
|
-
// Invert the scaled value if necessary to make bigger = better
|
|
11450
|
-
if (testScale.bInvertScaled) {
|
|
11451
|
-
scaledValue = 1.0 - scaledValue;
|
|
11452
|
-
}
|
|
11453
11372
|
// Finally, make the range [0-100]
|
|
11454
11373
|
return Math.round(scaledValue * S.NORMALIZED_RANGE);
|
|
11455
11374
|
}
|
|
11456
11375
|
exports.normalize = normalize;
|
|
11457
11376
|
// Round a fractional number [0-1] to the desired level of PRECISION.
|
|
11458
|
-
function trim(fullFraction
|
|
11459
|
-
|
|
11460
|
-
|
|
11461
|
-
}
|
|
11462
|
-
else {
|
|
11463
|
-
let shiftPlaces = Math.pow(10, (digits || S.PRECISION));
|
|
11464
|
-
return Math.round(fullFraction * shiftPlaces) / shiftPlaces;
|
|
11465
|
-
}
|
|
11377
|
+
function trim(fullFraction) {
|
|
11378
|
+
let shiftPlaces = Math.pow(10, S.PRECISION);
|
|
11379
|
+
return Math.round(fullFraction * shiftPlaces) / shiftPlaces;
|
|
11466
11380
|
}
|
|
11467
11381
|
exports.trim = trim;
|
|
11468
11382
|
// ARRAY HELPERS
|
|
@@ -11491,14 +11405,13 @@ function andArray(arr) {
|
|
|
11491
11405
|
}
|
|
11492
11406
|
exports.andArray = andArray;
|
|
11493
11407
|
// WORKING WITH OBJECT KEYS/PROPERTIES
|
|
11494
|
-
// TODO -
|
|
11495
|
-
// TODO - Handle integer keys?
|
|
11408
|
+
// TODO - Terry, is this copesetic?
|
|
11496
11409
|
// Does an object have a key/property?
|
|
11497
11410
|
function keyExists(k, o) {
|
|
11498
11411
|
return k in o;
|
|
11499
11412
|
}
|
|
11500
11413
|
exports.keyExists = keyExists;
|
|
11501
|
-
// TODO -
|
|
11414
|
+
// TODO - Terry, can these three be combined into a generic isEmpty() check?
|
|
11502
11415
|
// Does an object (dict) have any keys/properties?
|
|
11503
11416
|
function isObjectEmpty(o) {
|
|
11504
11417
|
return Object.keys(o).length === 0;
|
|
@@ -11592,12 +11505,7 @@ function deepCopy(src) {
|
|
|
11592
11505
|
return src;
|
|
11593
11506
|
}
|
|
11594
11507
|
exports.deepCopy = deepCopy;
|
|
11595
|
-
// TODO -
|
|
11596
|
-
function mapBooleanToTriState(bool) {
|
|
11597
|
-
return (bool) ? 0 /* Green */ : 2 /* Red */;
|
|
11598
|
-
}
|
|
11599
|
-
exports.mapBooleanToTriState = mapBooleanToTriState;
|
|
11600
|
-
// TODO - TERRY: What is this the simple explanation of what this thing is doing?
|
|
11508
|
+
// TODO - Terry: What is this the simple explanation of what this thing is doing?
|
|
11601
11509
|
var util_1 = __webpack_require__(/*! @dra2020/util */ "./node_modules/@dra2020/util/dist/util.js");
|
|
11602
11510
|
exports.depthof = util_1.depthof;
|
|
11603
11511
|
|
|
@@ -11616,54 +11524,40 @@ exports.depthof = util_1.depthof;
|
|
|
11616
11524
|
//
|
|
11617
11525
|
// MAP/PLAN VALIDATIONS
|
|
11618
11526
|
//
|
|
11619
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
11620
|
-
if (mod && mod.__esModule) return mod;
|
|
11621
|
-
var result = {};
|
|
11622
|
-
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
11623
|
-
result["default"] = mod;
|
|
11624
|
-
return result;
|
|
11625
|
-
};
|
|
11626
11527
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11627
|
-
const U =
|
|
11628
|
-
const S =
|
|
11629
|
-
const D =
|
|
11528
|
+
const U = __webpack_require__(/*! ./utils */ "./src/utils.ts");
|
|
11529
|
+
const S = __webpack_require__(/*! ./settings */ "./src/settings.ts");
|
|
11530
|
+
const D = __webpack_require__(/*! ./_data */ "./src/_data.ts");
|
|
11630
11531
|
//
|
|
11631
11532
|
// COMPLETE - Are all geo's assigned to a district, and do all districts have
|
|
11632
11533
|
// at least one geo assigned to them?
|
|
11633
11534
|
//
|
|
11634
|
-
function doIsComplete(s
|
|
11535
|
+
function doIsComplete(s) {
|
|
11635
11536
|
let test = s.getTest(0 /* Complete */);
|
|
11636
|
-
|
|
11537
|
+
let bAllAssigned = true;
|
|
11538
|
+
let bNoneEmpty = true;
|
|
11539
|
+
let unassignedFeatures = [];
|
|
11540
|
+
let emptyDistricts = [];
|
|
11541
|
+
// Get the by district results, including the dummy unassigned district,
|
|
11637
11542
|
// but ignoring the N+1 summary district
|
|
11638
11543
|
let bNotEmptyByDistrict = s.districts.statistics[D.DistrictField.bNotEmpty];
|
|
11639
11544
|
bNotEmptyByDistrict = bNotEmptyByDistrict.slice(0, -1);
|
|
11640
11545
|
// Are all features assigned to districts?
|
|
11641
11546
|
// Check the dummy district that holds any unassigned features.
|
|
11642
|
-
|
|
11643
|
-
let bAllAssigned = (!bNotEmptyByDistrict[S.NOT_ASSIGNED]);
|
|
11547
|
+
bAllAssigned = (!bNotEmptyByDistrict[S.NOT_ASSIGNED]);
|
|
11644
11548
|
if (!bAllAssigned) {
|
|
11645
11549
|
let unassignedDistrict = s.plan.geoIDsForDistrictID(S.NOT_ASSIGNED);
|
|
11646
11550
|
unassignedFeatures = Array.from(unassignedDistrict);
|
|
11647
11551
|
unassignedFeatures = unassignedFeatures.slice(0, S.NUMBER_OF_ITEMS_TO_REPORT);
|
|
11648
11552
|
}
|
|
11649
11553
|
// Do all real districts have at least one feature assigned to them?
|
|
11650
|
-
|
|
11651
|
-
let bNoneEmpty = true;
|
|
11652
|
-
bNotEmptyByDistrict = bNotEmptyByDistrict.slice(1);
|
|
11653
|
-
let districtID = 1;
|
|
11654
|
-
bNotEmptyByDistrict.forEach(function (bNotEmpty) {
|
|
11655
|
-
if (!bNotEmpty) {
|
|
11656
|
-
bNoneEmpty = false;
|
|
11657
|
-
emptyDistricts.push(districtID);
|
|
11658
|
-
}
|
|
11659
|
-
districtID += 1;
|
|
11660
|
-
});
|
|
11554
|
+
bNoneEmpty = U.andArray(bNotEmptyByDistrict.slice(1));
|
|
11661
11555
|
// Case 1 - One or more districts are missing:
|
|
11662
11556
|
// The # of enumerated districts minus the dummy NOT_ASSIGNED one should
|
|
11663
11557
|
// equal the number apportioned districts. This guards against a district
|
|
11664
11558
|
// not being included in a map that is imported.
|
|
11665
11559
|
//
|
|
11666
|
-
//
|
|
11560
|
+
// TODO - I'm no longer checking for this, but DRA should!
|
|
11667
11561
|
// Case 2 - Or a district is explicitly named but empty:
|
|
11668
11562
|
// Note, this can happen if a district is created, and then all features
|
|
11669
11563
|
// are removed from it (in DRA).
|
|
@@ -11690,7 +11584,7 @@ exports.doIsComplete = doIsComplete;
|
|
|
11690
11584
|
//
|
|
11691
11585
|
// To test this, load the NC 2010 map 'SAMPLE-BG-map-discontiguous.csv'.
|
|
11692
11586
|
//
|
|
11693
|
-
function doIsContiguous(s
|
|
11587
|
+
function doIsContiguous(s) {
|
|
11694
11588
|
let test = s.getTest(1 /* Contiguous */);
|
|
11695
11589
|
// Get the contiguity of each district. Ignore dummy unassigned district
|
|
11696
11590
|
// and the N+1 summary district.
|
|
@@ -11721,7 +11615,7 @@ exports.doIsContiguous = doIsContiguous;
|
|
|
11721
11615
|
// Are the features in a district fully connected?
|
|
11722
11616
|
function isConnected(districtGeos, graph) {
|
|
11723
11617
|
// export function isConnected(districtGeos: Set<string>, graph: T.ContiguityGraph): boolean {
|
|
11724
|
-
// TODO -
|
|
11618
|
+
// TODO - Terry, why does this constructor need a <T> type specification?
|
|
11725
11619
|
let visited = new Set();
|
|
11726
11620
|
let toProcess = [];
|
|
11727
11621
|
// Start processing with the first geoID in the district
|
|
@@ -11736,7 +11630,7 @@ function isConnected(districtGeos, graph) {
|
|
|
11736
11630
|
let actualNeighbors = graph.peerNeighbors(node).filter(x => U.isInState(x));
|
|
11737
11631
|
// Add neighbors to visit, if they're in the same district Y haven't already been visited
|
|
11738
11632
|
let neighborsToVisit = actualNeighbors.filter(x => districtGeos.has(x) && (!visited.has(x)));
|
|
11739
|
-
// TODO -
|
|
11633
|
+
// TODO - Terry, is this the quickest/best way to do this?
|
|
11740
11634
|
toProcess.push(...neighborsToVisit);
|
|
11741
11635
|
}
|
|
11742
11636
|
// Stop when you've visited all the geoIDs in the district
|
|
@@ -11753,10 +11647,10 @@ exports.isConnected = isConnected;
|
|
|
11753
11647
|
// To test this, load the NC 2010 map 'SAMPLE-BG-map-hole.csv'. District 1,
|
|
11754
11648
|
// Buncombe County (37021), is a donut hole w/in District 3.
|
|
11755
11649
|
//
|
|
11756
|
-
// TODO -
|
|
11650
|
+
// TODO - Optimize this to take advantage of district boundary info, if/when
|
|
11757
11651
|
// we cache one to optimize compactness.
|
|
11758
11652
|
//
|
|
11759
|
-
function doIsFreeOfHoles(s
|
|
11653
|
+
function doIsFreeOfHoles(s) {
|
|
11760
11654
|
let test = s.getTest(2 /* FreeOfHoles */);
|
|
11761
11655
|
// Initialize values
|
|
11762
11656
|
let bFreeOfHoles = true;
|
|
@@ -11787,7 +11681,7 @@ function doIsFreeOfHoles(s, bLog = false) {
|
|
|
11787
11681
|
exports.doIsFreeOfHoles = doIsFreeOfHoles;
|
|
11788
11682
|
// Test whether one district is embedded w/in any other.
|
|
11789
11683
|
function isEmbedded(districtID, geoIDs, plan, graph) {
|
|
11790
|
-
//
|
|
11684
|
+
// TODO - Make "features" = "geoIDs." These aren't "features" proper, just
|
|
11791
11685
|
// identifier strings.
|
|
11792
11686
|
let features = geoIDs;
|
|
11793
11687
|
let planByGeo = plan.byGeoID();
|
|
@@ -11795,7 +11689,7 @@ function isEmbedded(districtID, geoIDs, plan, graph) {
|
|
|
11795
11689
|
let bEmbedded = true;
|
|
11796
11690
|
// Keep track of the neighoring districts
|
|
11797
11691
|
let neighboringDistricts = new Set();
|
|
11798
|
-
// TODO -
|
|
11692
|
+
// TODO - Use just the boundary features, when available
|
|
11799
11693
|
// Get the features for the real district
|
|
11800
11694
|
let featuresToCheck = Array.from(features);
|
|
11801
11695
|
// If the district has features, check whether it is embedded
|
|
@@ -11821,7 +11715,7 @@ function isEmbedded(districtID, geoIDs, plan, graph) {
|
|
|
11821
11715
|
break;
|
|
11822
11716
|
}
|
|
11823
11717
|
else {
|
|
11824
|
-
// TODO -
|
|
11718
|
+
// TODO - Since we're checking *all* features in a district right
|
|
11825
11719
|
// now, not just boundary features and neighbors in other districts,
|
|
11826
11720
|
// prune out the current district. If/when we optimize compactness
|
|
11827
11721
|
// to cache district boundaries (as before in my Python implementation),
|
|
@@ -11847,19 +11741,16 @@ function isEmbedded(districtID, geoIDs, plan, graph) {
|
|
|
11847
11741
|
return bEmbedded;
|
|
11848
11742
|
}
|
|
11849
11743
|
exports.isEmbedded = isEmbedded;
|
|
11744
|
+
// TODO - MIXED MAPS
|
|
11745
|
+
// - When we generalize to mixed maps, the determination of "neighbors in
|
|
11746
|
+
// the map" -- which is the core function in determining connectedness -- becomes
|
|
11747
|
+
// *much* more complicated and dynamic.
|
|
11748
|
+
// - I can write up (if not implement) the logic for this. It is a bit tricky
|
|
11749
|
+
// and requires special preprocessing of the summary level hierarchy (which I
|
|
11750
|
+
// also have a script for that we can repurpose) to distinguish between 'interior'
|
|
11751
|
+
// and 'edge' children.
|
|
11850
11752
|
|
|
11851
11753
|
|
|
11852
|
-
/***/ }),
|
|
11853
|
-
|
|
11854
|
-
/***/ "./static/state-reqs.json":
|
|
11855
|
-
/*!********************************!*\
|
|
11856
|
-
!*** ./static/state-reqs.json ***!
|
|
11857
|
-
\********************************/
|
|
11858
|
-
/*! 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 */
|
|
11859
|
-
/***/ (function(module) {
|
|
11860
|
-
|
|
11861
|
-
module.exports = JSON.parse("{\"AL\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_01.pdf\",\"AK\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_02.pdf\",\"AZ\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_03.pdf\",\"AR\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_04.pdf\",\"CA\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_05.pdf\",\"CO\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_06.pdf\",\"CT\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_07.pdf\",\"DE\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_08.pdf\",\"FL\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_09.pdf\",\"GA\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_10.pdf\",\"HI\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_11.pdf\",\"ID\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_12.pdf\",\"IL\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_13.pdf\",\"IN\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_14.pdf\",\"IA\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_15.pdf\",\"KS\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_16.pdf\",\"KY\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_17.pdf\",\"LA\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_18.pdf\",\"ME\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_19.pdf\",\"MD\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_20.pdf\",\"MA\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_21.pdf\",\"MI\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_22.pdf\",\"MN\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_23.pdf\",\"MS\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_24.pdf\",\"MO\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_25.pdf\",\"MT\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_26.pdf\",\"NE\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_27.pdf\",\"NV\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_28.pdf\",\"NH\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_29.pdf\",\"NJ\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_30.pdf\",\"NM\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_31.pdf\",\"NY\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_32.pdf\",\"NC\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_33.pdf\",\"ND\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_34.pdf\",\"OH\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_35.pdf\",\"OK\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_36.pdf\",\"OR\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_37.pdf\",\"PA\":\"https://www.brennancenter.org/sites/default/files/publications/2018_05_50States_FINALsinglepages_38.pdf\",\"RI\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_39.pdf\",\"SC\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_40.pdf\",\"SD\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_41.pdf\",\"TN\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_42.pdf\",\"TX\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_43.pdf\",\"UT\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_44.pdf\",\"VT\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_45.pdf\",\"VA\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_46.pdf\",\"WA\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_47.pdf\",\"WV\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_48.pdf\",\"WI\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_49.pdf\",\"WY\":\"https://www.brennancenter.org/sites/default/files/publications/2019_06_50States_FINALsinglepages_50.pdf\"}");
|
|
11862
|
-
|
|
11863
11754
|
/***/ }),
|
|
11864
11755
|
|
|
11865
11756
|
/***/ "./test/_cli.ts":
|
|
@@ -11870,107 +11761,35 @@ module.exports = JSON.parse("{\"AL\":\"https://www.brennancenter.org/sites/defau
|
|
|
11870
11761
|
/***/ (function(module, exports, __webpack_require__) {
|
|
11871
11762
|
|
|
11872
11763
|
"use strict";
|
|
11873
|
-
|
|
11764
|
+
/* WEBPACK VAR INJECTION */(function(__dirname) {
|
|
11874
11765
|
//
|
|
11875
11766
|
// A SIMPLE COMMAND-LINE INTERFACE FOR EXERCISING THE ANALYTICS & VALIDATIONS
|
|
11876
11767
|
//
|
|
11877
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
11878
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
11879
|
-
};
|
|
11880
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
11881
|
-
if (mod && mod.__esModule) return mod;
|
|
11882
|
-
var result = {};
|
|
11883
|
-
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
11884
|
-
result["default"] = mod;
|
|
11885
|
-
return result;
|
|
11886
|
-
};
|
|
11887
11768
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11888
|
-
|
|
11889
|
-
|
|
11890
|
-
|
|
11891
|
-
|
|
11892
|
-
|
|
11893
|
-
|
|
11894
|
-
|
|
11895
|
-
* api-analyze (analyze) - Analyzes a plan and logs the results.
|
|
11896
|
-
|
|
11897
|
-
* unit-valid (valid) - Exercises the validations.
|
|
11898
|
-
* unit-equal (equal) - Exercises the population deviation analytic.
|
|
11899
|
-
* unit-compact (compact) - Exercises the compactness analytics.
|
|
11900
|
-
* unit-cohesive (cohesive) - Exercises the splitting analytics.
|
|
11901
|
-
* unit-political (political) - Exercises the partisan analytics.
|
|
11902
|
-
* unit-minority (minority) - Exercises the minority analytics.
|
|
11903
|
-
|
|
11904
|
-
* invalid-unassigned (valid) - Tests a plan w/ unassigned features.
|
|
11905
|
-
* invalid-missing (valid) - Tests a plan w/ a missing district.
|
|
11906
|
-
* invalid-empty (valid) - Tests a plan w/ an empty district.
|
|
11907
|
-
* invalid-discontiguous (valid) - Tests a plan w/ a discontiguous district.
|
|
11908
|
-
* invalid-hole (valid) - Tests a plan w/ an embedded district.
|
|
11909
|
-
|
|
11910
|
-
|
|
11911
|
-
COMMANDS
|
|
11912
|
-
|
|
11913
|
-
Tests can also be exercised from the commmand line from the project directory.
|
|
11914
|
-
This is a sample. Use "node --inspect --inspect-brk main.js ..." to attach a debugger.
|
|
11915
|
-
|
|
11916
|
-
1 - Plain vanilla run:
|
|
11917
|
-
./main.js analyze -x NC -n 13 -p data/SAMPLE-BG-map.csv -d data/SAMPLE-BG-data2.json -s data/SAMPLE-BG-shapes.geojson -g data/SAMPLE-BG-graph.json -c data/SAMPLE-COUNTY.geojson
|
|
11918
|
-
|
|
11919
|
-
2 - Missing district:
|
|
11920
|
-
./main.js analyze -x NC -n 13 -p data/SAMPLE-BG-map-missing.csv -d data/SAMPLE-BG-data2.json -s data/SAMPLE-BG-shapes.geojson -g data/SAMPLE-BG-graph.json -c data/SAMPLE-COUNTY.geojson
|
|
11921
|
-
|
|
11922
|
-
3 - Empty district:
|
|
11923
|
-
./main.js valid -x NC -n 13 -p data/SAMPLE-BG-map.csv -d data/SAMPLE-BG-data2.json -s data/SAMPLE-BG-shapes.geojson -g data/SAMPLE-BG-graph.json -c data/SAMPLE-COUNTY.geojson --empty
|
|
11924
|
-
|
|
11925
|
-
4 - Unassigned features:
|
|
11926
|
-
./main.js analyze -x NC -n 13 -p data/SAMPLE-BG-map-unassigned.csv -d data/SAMPLE-BG-data2.json -s data/SAMPLE-BG-shapes.geojson -g data/SAMPLE-BG-graph.json -c data/SAMPLE-COUNTY.geojson
|
|
11927
|
-
|
|
11928
|
-
5 - Discontiguous districts:
|
|
11929
|
-
./main.js analyze -n 13 -p data/SAMPLE-BG-map-discontiguous.csv -d data/SAMPLE-BG-data2.json -s data/SAMPLE-BG-shapes.geojson -g data/SAMPLE-BG-graph.json -c data/SAMPLE-COUNTY.geojson
|
|
11930
|
-
|
|
11931
|
-
6 - Embedded district:
|
|
11932
|
-
./main.js analyze -n 13 -p data/SAMPLE-BG-map-hole.csv -d data/SAMPLE-BG-data2.json -s data/SAMPLE-BG-shapes.geojson -g data/SAMPLE-BG-graph.json -c data/SAMPLE-COUNTY.geojson
|
|
11933
|
-
|
|
11934
|
-
7 - Test using NC block-level data <<< TODO - Need BLOCK-data2.json & COUNTY.geojson to run this again.
|
|
11935
|
-
|
|
11936
|
-
cd ~/Documents/Professional/Projects/Redistricting/DRA2020/Analytics/compact/classic/ts_sample
|
|
11937
|
-
./main.js analyze -v -n 13 -p SAMPLE-BLOCK-map.csv -d SAMPLE-BLOCK-data2.json -s SAMPLE-BLOCK-shapes.geojson -g SAMPLE-BLOCK-graph.json -c SAMPLE-COUNTY.geojson
|
|
11938
|
-
|
|
11939
|
-
*/
|
|
11940
|
-
const yargs_1 = __importDefault(__webpack_require__(/*! yargs */ "yargs"));
|
|
11941
|
-
// TODO - Fix this import, so I don't have to do the 'union' workaround below.
|
|
11942
|
-
const PC = __importStar(__webpack_require__(/*! polygon-clipping */ "./node_modules/polygon-clipping/dist/polygon-clipping.esm.js"));
|
|
11943
|
-
const Poly = __importStar(__webpack_require__(/*! @dra2020/poly */ "./node_modules/@dra2020/poly/dist/poly.js"));
|
|
11944
|
-
const fs = __importStar(__webpack_require__(/*! fs */ "fs"));
|
|
11945
|
-
const path = __importStar(__webpack_require__(/*! path */ "path"));
|
|
11946
|
-
const sync_1 = __importDefault(__webpack_require__(/*! csv-parse/lib/sync */ "./node_modules/csv-parse/lib/sync.js"));
|
|
11769
|
+
const yargs = __webpack_require__(/*! yargs */ "yargs");
|
|
11770
|
+
const PC = __webpack_require__(/*! polygon-clipping */ "./node_modules/polygon-clipping/dist/polygon-clipping.esm.js");
|
|
11771
|
+
const Poly = __webpack_require__(/*! @dra2020/poly */ "./node_modules/@dra2020/poly/dist/poly.js");
|
|
11772
|
+
const fs = __webpack_require__(/*! fs */ "fs");
|
|
11773
|
+
const path = __webpack_require__(/*! path */ "path");
|
|
11774
|
+
const parse = __webpack_require__(/*! csv-parse/lib/sync */ "./node_modules/csv-parse/lib/sync.js");
|
|
11947
11775
|
const _api_1 = __webpack_require__(/*! ../src/_api */ "./src/_api.ts");
|
|
11948
11776
|
const preprocess_1 = __webpack_require__(/*! ../src/preprocess */ "./src/preprocess.ts");
|
|
11949
11777
|
const analyze_1 = __webpack_require__(/*! ../src/analyze */ "./src/analyze.ts");
|
|
11950
|
-
const results_1 = __webpack_require__(/*! ../src/results */ "./src/results.ts");
|
|
11951
|
-
// TODO - DASHBOARD: Delete
|
|
11952
|
-
// import { doAnalyzePostProcessing } from '../src/report'
|
|
11953
11778
|
const valid_1 = __webpack_require__(/*! ../src/valid */ "./src/valid.ts");
|
|
11954
11779
|
const equal_1 = __webpack_require__(/*! ../src/equal */ "./src/equal.ts");
|
|
11955
11780
|
const compact_1 = __webpack_require__(/*! ../src/compact */ "./src/compact.ts");
|
|
11956
11781
|
const cohesive_1 = __webpack_require__(/*! ../src/cohesive */ "./src/cohesive.ts");
|
|
11957
11782
|
const political_1 = __webpack_require__(/*! ../src/political */ "./src/political.ts");
|
|
11958
11783
|
const minority_1 = __webpack_require__(/*! ../src/minority */ "./src/minority.ts");
|
|
11959
|
-
const U =
|
|
11960
|
-
const S =
|
|
11961
|
-
const D = __importStar(__webpack_require__(/*! ../src/_data */ "./src/_data.ts"));
|
|
11784
|
+
const U = __webpack_require__(/*! ../src/utils */ "./src/utils.ts");
|
|
11785
|
+
const S = __webpack_require__(/*! ../src/settings */ "./src/settings.ts");
|
|
11962
11786
|
// Simulate DRA unioning district shapes in the background
|
|
11963
11787
|
function addToPoly(poly, polys) {
|
|
11964
|
-
|
|
11965
|
-
// return PC.union(poly, ...polys);
|
|
11966
|
-
let union = PC.union;
|
|
11967
|
-
if (union === undefined)
|
|
11968
|
-
union = PC.default.union;
|
|
11969
|
-
return union(poly, ...polys);
|
|
11788
|
+
return PC.union(poly, ...polys);
|
|
11970
11789
|
}
|
|
11971
11790
|
console.log("Starting command @ ", new Date());
|
|
11972
11791
|
// COMMAND LINE
|
|
11973
|
-
let argv =
|
|
11792
|
+
let argv = yargs
|
|
11974
11793
|
.usage('Usage: $0 command [options]')
|
|
11975
11794
|
.example('$0 equal -f foo.geojson', 'Calculate population deviation')
|
|
11976
11795
|
.demandCommand(1, 'You must specify a command to execute.')
|
|
@@ -12050,9 +11869,15 @@ let xx = argv.state;
|
|
|
12050
11869
|
let bLegislativeDistricts = argv.legislative;
|
|
12051
11870
|
let nDistricts = argv.n;
|
|
12052
11871
|
let planByGeoID = readPlanCSV(argv.plan);
|
|
12053
|
-
let graph =
|
|
12054
|
-
let data =
|
|
12055
|
-
let counties =
|
|
11872
|
+
let graph = {};
|
|
11873
|
+
let data = {};
|
|
11874
|
+
let counties = {};
|
|
11875
|
+
// Don't load other datasets, if just calculating compactness
|
|
11876
|
+
if (command != 'compact') {
|
|
11877
|
+
graph = readJSON(argv.graph);
|
|
11878
|
+
data = readJSON(argv.data);
|
|
11879
|
+
counties = readJSON(argv.counties);
|
|
11880
|
+
}
|
|
12056
11881
|
let shapes = readJSON(argv.shapes);
|
|
12057
11882
|
let bEmpty = argv.empty;
|
|
12058
11883
|
let suites = argv.suites;
|
|
@@ -12062,23 +11887,16 @@ let datasetKeys = {
|
|
|
12062
11887
|
VAP: "D16T",
|
|
12063
11888
|
ELECTION: "E16GPR"
|
|
12064
11889
|
};
|
|
12065
|
-
let datasetDescriptions = {
|
|
12066
|
-
CENSUS: "2016 ACS Total Population",
|
|
12067
|
-
VAP: "2016 ACS Voting Age Population",
|
|
12068
|
-
ELECTION: "2016 Presidential Election",
|
|
12069
|
-
};
|
|
12070
11890
|
// Session settings are required:
|
|
12071
11891
|
// - Analytics suites can be defaulted to all with [], but
|
|
12072
11892
|
// - Dataset must be explicitly specified
|
|
12073
11893
|
let sessionSettings = {
|
|
12074
11894
|
'suites': suites,
|
|
12075
|
-
'datasets': datasetKeys
|
|
12076
|
-
'descriptions': datasetDescriptions
|
|
11895
|
+
'datasets': datasetKeys
|
|
12077
11896
|
};
|
|
12078
11897
|
let bLog = argv.verbose;
|
|
12079
11898
|
// Invert the plan, so you can create the district shapes for the SessionRequest.
|
|
12080
|
-
|
|
12081
|
-
let planByDistrictID = D.invertPlan(planByGeoID);
|
|
11899
|
+
let planByDistrictID = U.invertPlan(planByGeoID);
|
|
12082
11900
|
// SIMULATE THE HOST
|
|
12083
11901
|
// 1 - Create district shapes & extract properties (area, diameter, perimeter)
|
|
12084
11902
|
const polyOptions = {
|
|
@@ -12139,33 +11957,30 @@ districtShapes['type'] = "FeatureCollection";
|
|
|
12139
11957
|
districtShapes['features'] = [];
|
|
12140
11958
|
for (let districtID = 1; districtID <= nDistricts; districtID++) {
|
|
12141
11959
|
let districtFeatures = [];
|
|
12142
|
-
//
|
|
12143
|
-
|
|
12144
|
-
|
|
12145
|
-
|
|
12146
|
-
|
|
12147
|
-
|
|
12148
|
-
|
|
12149
|
-
|
|
12150
|
-
|
|
12151
|
-
|
|
12152
|
-
|
|
12153
|
-
|
|
12154
|
-
|
|
12155
|
-
|
|
12156
|
-
|
|
12157
|
-
|
|
12158
|
-
|
|
12159
|
-
|
|
12160
|
-
|
|
12161
|
-
|
|
12162
|
-
|
|
12163
|
-
|
|
12164
|
-
|
|
12165
|
-
|
|
12166
|
-
};
|
|
12167
|
-
districtShapes.features.push(f);
|
|
12168
|
-
} // End non-empty districts
|
|
11960
|
+
// Collect Features by district
|
|
11961
|
+
planByDistrictID[districtID].forEach(function (geoID) {
|
|
11962
|
+
districtFeatures.push(shapesByGeoID[geoID]);
|
|
11963
|
+
});
|
|
11964
|
+
// Union them together
|
|
11965
|
+
let poly1Coordinates = Poly.polyNormalize(districtFeatures[0], polyOptions);
|
|
11966
|
+
let polyNCoordinates = districtFeatures.slice(1).map((i) => Poly.polyNormalize(i, polyOptions));
|
|
11967
|
+
let poly = addToPoly(poly1Coordinates, polyNCoordinates);
|
|
11968
|
+
// Convert the result to a Feature
|
|
11969
|
+
let bPolygon = U.depthof(poly) == 4;
|
|
11970
|
+
if (!bPolygon && poly.length == 1) {
|
|
11971
|
+
bPolygon = true;
|
|
11972
|
+
poly = poly[0];
|
|
11973
|
+
}
|
|
11974
|
+
eliminateAnomalousHoles(poly);
|
|
11975
|
+
let f = {
|
|
11976
|
+
type: 'Feature',
|
|
11977
|
+
properties: { districtID: `${districtID}` },
|
|
11978
|
+
geometry: {
|
|
11979
|
+
type: bPolygon ? 'Polygon' : 'MultiPolygon',
|
|
11980
|
+
coordinates: poly
|
|
11981
|
+
}
|
|
11982
|
+
};
|
|
11983
|
+
districtShapes.features.push(f);
|
|
12169
11984
|
}
|
|
12170
11985
|
// CONSTRUCT A SESSION REQUEST
|
|
12171
11986
|
let SessionRequest = {};
|
|
@@ -12183,7 +11998,21 @@ SessionRequest['config'] = sessionSettings;
|
|
|
12183
11998
|
let s = new _api_1.AnalyticsSession(SessionRequest);
|
|
12184
11999
|
// DISPATCH TO REQUESTED COMMAND W/ NEEDED SCAFFOLDING
|
|
12185
12000
|
let t;
|
|
12186
|
-
|
|
12001
|
+
let text;
|
|
12002
|
+
function echoTestResult(test, t) {
|
|
12003
|
+
console.log("");
|
|
12004
|
+
console.log("Test:", test);
|
|
12005
|
+
console.log("Score:", t['score']);
|
|
12006
|
+
let keys = U.getObjectKeys(t['details']);
|
|
12007
|
+
if (keys.length > 0) {
|
|
12008
|
+
console.log("Details:");
|
|
12009
|
+
for (let i in keys) {
|
|
12010
|
+
let key = keys[i];
|
|
12011
|
+
console.log("-", key, "=", t['details'][key]);
|
|
12012
|
+
}
|
|
12013
|
+
}
|
|
12014
|
+
console.log("___");
|
|
12015
|
+
}
|
|
12187
12016
|
switch (command) {
|
|
12188
12017
|
case 'valid': {
|
|
12189
12018
|
preprocess_1.doPreprocessData(s);
|
|
@@ -12195,17 +12024,16 @@ switch (command) {
|
|
|
12195
12024
|
s.plan.initializeDistrict(randomDistrict);
|
|
12196
12025
|
}
|
|
12197
12026
|
analyze_1.doAnalyzeDistricts(s);
|
|
12198
|
-
|
|
12199
|
-
|
|
12200
|
-
|
|
12201
|
-
|
|
12202
|
-
|
|
12203
|
-
|
|
12204
|
-
|
|
12205
|
-
echoTestResult("
|
|
12206
|
-
|
|
12207
|
-
echoTestResult("
|
|
12208
|
-
echoTestResult("Equal populations:", t5);
|
|
12027
|
+
t = valid_1.doIsComplete(s);
|
|
12028
|
+
echoTestResult("Complete:", t);
|
|
12029
|
+
t = valid_1.doIsContiguous(s);
|
|
12030
|
+
echoTestResult("Contiguous:", t);
|
|
12031
|
+
t = valid_1.doIsFreeOfHoles(s);
|
|
12032
|
+
echoTestResult("Free of holes:", t);
|
|
12033
|
+
t = equal_1.doPopulationDeviation(s);
|
|
12034
|
+
echoTestResult("Population deviation (%):", t);
|
|
12035
|
+
t = equal_1.doHasEqualPopulations(s);
|
|
12036
|
+
echoTestResult("Equal populations:", t);
|
|
12209
12037
|
break;
|
|
12210
12038
|
}
|
|
12211
12039
|
case 'equal': {
|
|
@@ -12213,313 +12041,98 @@ switch (command) {
|
|
|
12213
12041
|
analyze_1.doAnalyzeDistricts(s);
|
|
12214
12042
|
t = equal_1.doPopulationDeviation(s);
|
|
12215
12043
|
echoTestResult("Population deviation (%):", t);
|
|
12216
|
-
results_1.doAnalyzePostProcessing(s);
|
|
12217
12044
|
break;
|
|
12218
12045
|
}
|
|
12219
12046
|
case 'compact': {
|
|
12220
12047
|
preprocess_1.doPreprocessData(s);
|
|
12221
12048
|
analyze_1.doAnalyzeDistricts(s);
|
|
12222
|
-
|
|
12223
|
-
|
|
12224
|
-
|
|
12225
|
-
echoTestResult("
|
|
12226
|
-
echoTestResult("Polsby-Popper:", t2);
|
|
12049
|
+
t = compact_1.doReock(s, bLog);
|
|
12050
|
+
echoTestResult("Reock:", t);
|
|
12051
|
+
t = compact_1.doPolsbyPopper(s, bLog);
|
|
12052
|
+
echoTestResult("Polsby-Popper:", t);
|
|
12227
12053
|
break;
|
|
12228
12054
|
}
|
|
12229
12055
|
case 'cohesive': {
|
|
12230
12056
|
preprocess_1.doPreprocessData(s);
|
|
12231
12057
|
analyze_1.doAnalyzeDistricts(s);
|
|
12232
|
-
|
|
12233
|
-
|
|
12234
|
-
|
|
12235
|
-
let t3 = cohesive_1.doCountySplitting(s);
|
|
12236
|
-
let t4 = cohesive_1.doDistrictSplitting(s);
|
|
12237
|
-
results_1.doAnalyzePostProcessing(s);
|
|
12238
|
-
echoTestResult("Counties split unexpectedly:", t1);
|
|
12239
|
-
echoTestResult("Split VTDs:", t2);
|
|
12240
|
-
echoTestResult("County splitting:", t3);
|
|
12241
|
-
echoTestResult("District splitting:", t4);
|
|
12058
|
+
t = cohesive_1.doCountySplits(s);
|
|
12059
|
+
echoTestResult("County splits:", t);
|
|
12060
|
+
cohesive_1.doPlanComplexity(s);
|
|
12242
12061
|
break;
|
|
12243
12062
|
}
|
|
12244
12063
|
case 'political': {
|
|
12245
12064
|
preprocess_1.doPreprocessData(s);
|
|
12246
12065
|
analyze_1.doAnalyzeDistricts(s);
|
|
12247
|
-
|
|
12248
|
-
|
|
12249
|
-
|
|
12250
|
-
|
|
12251
|
-
|
|
12252
|
-
|
|
12253
|
-
// echoTestResult("Seats Bias:", t1); TODO
|
|
12254
|
-
// echoTestResult("Votes Bias:", t2); TODO
|
|
12255
|
-
// echoTestResult("Responsiveness:", t3); TODO
|
|
12256
|
-
// echoTestResult("Responsive Districts:", t4); TODO
|
|
12257
|
-
echoTestResult("Efficiency gap (%):", t5);
|
|
12066
|
+
political_1.doSeatsBias(s);
|
|
12067
|
+
political_1.doVotesBias(s);
|
|
12068
|
+
political_1.doResponsiveness(s);
|
|
12069
|
+
political_1.doResponsiveDistricts(s);
|
|
12070
|
+
t = political_1.doEfficiencyGap(s);
|
|
12071
|
+
echoTestResult("Efficiency gap (%):", t);
|
|
12258
12072
|
break;
|
|
12259
12073
|
}
|
|
12260
12074
|
case 'minority': {
|
|
12261
12075
|
preprocess_1.doPreprocessData(s);
|
|
12262
12076
|
analyze_1.doAnalyzeDistricts(s);
|
|
12263
|
-
|
|
12264
|
-
results_1.doAnalyzePostProcessing(s);
|
|
12265
|
-
// echoTestResult("Majority-Minority Districts:", t1); TODO
|
|
12077
|
+
minority_1.doMajorityMinorityDistricts(s);
|
|
12266
12078
|
break;
|
|
12267
12079
|
}
|
|
12268
|
-
// TODO - DASHBOARD: DELETE
|
|
12269
|
-
// case 'analyze': {
|
|
12270
|
-
// console.log("");
|
|
12271
|
-
// console.log("Analyzing a plan ...");
|
|
12272
|
-
// console.log("");
|
|
12273
|
-
// s.analyzePlan(bLog);
|
|
12274
|
-
// break;
|
|
12275
|
-
// }
|
|
12276
|
-
// TODO - DASHBOARD: DELETE
|
|
12277
|
-
// case 'scorecard': {
|
|
12278
|
-
// s.analyzePlan(bLog);
|
|
12279
|
-
// text = s.prepareScorecard();
|
|
12280
|
-
// console.log("");
|
|
12281
|
-
// for (let line of text.data) {
|
|
12282
|
-
// switch (line['variant']) {
|
|
12283
|
-
// case 'beginTable':
|
|
12284
|
-
// case 'endTable':
|
|
12285
|
-
// console.log(line['variant']);
|
|
12286
|
-
// break;
|
|
12287
|
-
// case 'row':
|
|
12288
|
-
// console.log(line['variant'], line['cells']);
|
|
12289
|
-
// break;
|
|
12290
|
-
// default:
|
|
12291
|
-
// console.log(line['variant'], line['text']);
|
|
12292
|
-
// break;
|
|
12293
|
-
// }
|
|
12294
|
-
// }
|
|
12295
|
-
// console.log("");
|
|
12296
|
-
// break;
|
|
12297
|
-
// }
|
|
12298
|
-
// TODO - DASHBOARD: Print the structures out
|
|
12299
12080
|
case 'analyze': {
|
|
12081
|
+
console.log("");
|
|
12082
|
+
console.log("Analyzing a plan ...");
|
|
12083
|
+
console.log("");
|
|
12300
12084
|
s.analyzePlan(bLog);
|
|
12301
|
-
let planAnalytics = s.getPlanAnalytics(bLog);
|
|
12302
|
-
let districtStatistics = s.getDistrictStatistics(bLog);
|
|
12303
|
-
echoPlanAnalytics(planAnalytics);
|
|
12304
|
-
echoDistrictStatistics(districtStatistics);
|
|
12305
12085
|
break;
|
|
12306
12086
|
}
|
|
12307
|
-
|
|
12308
|
-
|
|
12309
|
-
|
|
12310
|
-
|
|
12311
|
-
|
|
12312
|
-
|
|
12313
|
-
|
|
12314
|
-
|
|
12315
|
-
|
|
12316
|
-
|
|
12317
|
-
|
|
12318
|
-
|
|
12319
|
-
|
|
12320
|
-
|
|
12321
|
-
|
|
12322
|
-
|
|
12323
|
-
|
|
12324
|
-
// switch (line['variant']) {
|
|
12325
|
-
// case 'beginTable':
|
|
12326
|
-
// case 'endTable':
|
|
12327
|
-
// console.log(line['variant']);
|
|
12328
|
-
// break;
|
|
12329
|
-
// case 'row':
|
|
12330
|
-
// console.log(line['variant'], line['cells']);
|
|
12331
|
-
// break;
|
|
12332
|
-
// default:
|
|
12333
|
-
// console.log(line['variant'], line['text']);
|
|
12334
|
-
// break;
|
|
12335
|
-
// }
|
|
12336
|
-
// }
|
|
12337
|
-
// console.log("");
|
|
12338
|
-
// break;
|
|
12339
|
-
// }
|
|
12340
|
-
}
|
|
12341
|
-
console.log("Ending command @ ", new Date());
|
|
12342
|
-
// HELPER FUNCTIONS FOR ECHOING RESULTS
|
|
12343
|
-
function echoTestResult(test, t) {
|
|
12344
|
-
console.log("");
|
|
12345
|
-
console.log("Test:", test);
|
|
12346
|
-
console.log("Score:", t['score']);
|
|
12347
|
-
let keys = U.getObjectKeys(t['details']);
|
|
12348
|
-
if (keys.length > 0) {
|
|
12349
|
-
console.log("Details:");
|
|
12350
|
-
for (let i in keys) {
|
|
12351
|
-
let key = keys[i];
|
|
12352
|
-
console.log("-", key, "=", t['details'][key]);
|
|
12087
|
+
case 'scorecard': {
|
|
12088
|
+
s.analyzePlan(bLog);
|
|
12089
|
+
text = s.prepareScorecard();
|
|
12090
|
+
console.log("");
|
|
12091
|
+
for (let line of text.data) {
|
|
12092
|
+
switch (line['variant']) {
|
|
12093
|
+
case 'beginTable':
|
|
12094
|
+
case 'endTable':
|
|
12095
|
+
console.log(line['variant']);
|
|
12096
|
+
break;
|
|
12097
|
+
case 'row':
|
|
12098
|
+
console.log(line['variant'], line['cells']);
|
|
12099
|
+
break;
|
|
12100
|
+
default:
|
|
12101
|
+
console.log(line['variant'], line['text']);
|
|
12102
|
+
break;
|
|
12103
|
+
}
|
|
12353
12104
|
}
|
|
12105
|
+
console.log("");
|
|
12106
|
+
break;
|
|
12354
12107
|
}
|
|
12355
|
-
|
|
12356
|
-
|
|
12357
|
-
|
|
12358
|
-
|
|
12359
|
-
|
|
12360
|
-
|
|
12361
|
-
}
|
|
12362
|
-
else {
|
|
12363
|
-
let nItems = list.length;
|
|
12364
|
-
let listStr;
|
|
12365
|
-
switch (nItems) {
|
|
12366
|
-
case 1: {
|
|
12367
|
-
listStr = list[0];
|
|
12368
|
-
break;
|
|
12369
|
-
}
|
|
12370
|
-
case 2: {
|
|
12371
|
-
listStr = list[0] + " and " + list[1];
|
|
12372
|
-
break;
|
|
12373
|
-
}
|
|
12374
|
-
default: {
|
|
12375
|
-
let listWithCommas = list.join(', ');
|
|
12376
|
-
let lastCommaIndex = listWithCommas.length - ((list[list.length - 1].length) + 1);
|
|
12377
|
-
let beforeAnd = listWithCommas.substr(0, lastCommaIndex);
|
|
12378
|
-
let afterAnd = listWithCommas.substr(lastCommaIndex + 1);
|
|
12379
|
-
listStr = beforeAnd + " and " + afterAnd;
|
|
12380
|
-
break;
|
|
12381
|
-
}
|
|
12108
|
+
case 'testlog': {
|
|
12109
|
+
s.analyzePlan(bLog);
|
|
12110
|
+
text = s.prepareTestLog();
|
|
12111
|
+
console.log("");
|
|
12112
|
+
for (let line of text.data) {
|
|
12113
|
+
console.log(line['variant'], line['text']);
|
|
12382
12114
|
}
|
|
12383
|
-
|
|
12115
|
+
console.log("");
|
|
12116
|
+
break;
|
|
12384
12117
|
}
|
|
12385
|
-
|
|
12386
|
-
|
|
12387
|
-
|
|
12388
|
-
|
|
12389
|
-
|
|
12390
|
-
|
|
12391
|
-
|
|
12392
|
-
|
|
12393
|
-
|
|
12394
|
-
|
|
12395
|
-
|
|
12396
|
-
|
|
12397
|
-
|
|
12398
|
-
|
|
12399
|
-
|
|
12400
|
-
console.log("* Details:");
|
|
12401
|
-
console.log(" - Empty districts:", prepareListItems(pa.requirements.details.emptyDistricts));
|
|
12402
|
-
console.log(" - Unassigned features:", prepareListItems(pa.requirements.details.unassignedFeatures));
|
|
12403
|
-
console.log(" - Population deviation:", pa.requirements.details.populationDeviation);
|
|
12404
|
-
console.log(" - Deviation threshold:", pa.requirements.details.deviationThreshold);
|
|
12405
|
-
console.log(" - Discontiguous districts:", prepareListItems(pa.requirements.details.discontiguousDistricts));
|
|
12406
|
-
console.log(" - Embedded districts:", prepareListItems(pa.requirements.details.embeddedDistricts));
|
|
12407
|
-
console.log("* Datasets:");
|
|
12408
|
-
console.log("- Shapes:", pa.requirements.datasets.shapes);
|
|
12409
|
-
console.log("- Census:", pa.requirements.datasets.census);
|
|
12410
|
-
console.log("- VAP:", pa.requirements.datasets.vap);
|
|
12411
|
-
console.log("- Election:", pa.requirements.datasets.election);
|
|
12412
|
-
console.log("* Resources:");
|
|
12413
|
-
console.log("- State requirements:", pa.requirements.resources.stateReqs);
|
|
12414
|
-
console.log("");
|
|
12415
|
-
console.log("Compactness:");
|
|
12416
|
-
console.log("* Score:", pa.compactness.score);
|
|
12417
|
-
console.log("* Metrics:");
|
|
12418
|
-
console.log(" - Reock:", pa.compactness.metrics.reock);
|
|
12419
|
-
console.log(" - Polsby-Popper:", pa.compactness.metrics.polsby);
|
|
12420
|
-
console.log("* Details:");
|
|
12421
|
-
console.log(" - N/A");
|
|
12422
|
-
console.log("* Datasets:");
|
|
12423
|
-
console.log("- Shapes:", pa.compactness.datasets.shapes);
|
|
12424
|
-
console.log("- Census:", pa.compactness.datasets.census);
|
|
12425
|
-
console.log("- VAP:", pa.compactness.datasets.vap);
|
|
12426
|
-
console.log("- Election:", pa.compactness.datasets.election);
|
|
12427
|
-
console.log("* Resources:");
|
|
12428
|
-
console.log("- N/A");
|
|
12429
|
-
console.log("");
|
|
12430
|
-
console.log("Splitting:");
|
|
12431
|
-
console.log("* Score:", pa.splitting.score);
|
|
12432
|
-
console.log("* Metrics:");
|
|
12433
|
-
console.log(" - sqEnt_DCreduced:", pa.splitting.metrics.sqEnt_DCreduced);
|
|
12434
|
-
console.log(" - sqEnt_CDreduced:", pa.splitting.metrics.sqEnt_CDreduced);
|
|
12435
|
-
console.log("* Details:");
|
|
12436
|
-
console.log(" - countiesSplitUnexpectedly:", prepareListItems(pa.splitting.details.countiesSplitUnexpectedly));
|
|
12437
|
-
console.log(" - unexpectedAffected:", pa.splitting.details.unexpectedAffected);
|
|
12438
|
-
console.log(" - splitVTDs:", pa.splitting.details.splitVTDs);
|
|
12439
|
-
console.log("* Datasets:");
|
|
12440
|
-
console.log("- Shapes:", pa.splitting.datasets.shapes);
|
|
12441
|
-
console.log("- Census:", pa.splitting.datasets.census);
|
|
12442
|
-
console.log("- VAP:", pa.splitting.datasets.vap);
|
|
12443
|
-
console.log("- Election:", pa.splitting.datasets.election);
|
|
12444
|
-
console.log("* Resources:");
|
|
12445
|
-
console.log("- N/A");
|
|
12446
|
-
console.log("");
|
|
12447
|
-
console.log("Partisan:");
|
|
12448
|
-
console.log("* Score:", pa.partisan.score);
|
|
12449
|
-
console.log("* Metrics:");
|
|
12450
|
-
console.log(" - sqEnt_DCreduced:", pa.partisan.metrics.partisanBias);
|
|
12451
|
-
console.log(" - sqEnt_CDreduced:", pa.partisan.metrics.responsiveness);
|
|
12452
|
-
console.log("* Details:");
|
|
12453
|
-
console.log(" - N/A");
|
|
12454
|
-
console.log("* Datasets:");
|
|
12455
|
-
console.log("- Shapes:", pa.partisan.datasets.shapes);
|
|
12456
|
-
console.log("- Census:", pa.partisan.datasets.census);
|
|
12457
|
-
console.log("- VAP:", pa.partisan.datasets.vap);
|
|
12458
|
-
console.log("- Election:", pa.partisan.datasets.election);
|
|
12459
|
-
console.log("* Resources:");
|
|
12460
|
-
console.log(" - Plan Score:", pa.partisan.resources.planScore);
|
|
12461
|
-
console.log("");
|
|
12462
|
-
console.log("Minority:");
|
|
12463
|
-
console.log("* Score:", pa.minority.score);
|
|
12464
|
-
console.log("* Metrics:");
|
|
12465
|
-
console.log(" - nBlack37to50:", pa.minority.metrics.nBlack37to50);
|
|
12466
|
-
console.log(" - nBlackMajority:", pa.minority.metrics.nBlackMajority);
|
|
12467
|
-
console.log(" - nHispanic37to50:", pa.minority.metrics.nHispanic37to50);
|
|
12468
|
-
console.log(" - nHispanicMajority:", pa.minority.metrics.nHispanicMajority);
|
|
12469
|
-
console.log(" - nPacific37to50:", pa.minority.metrics.nPacific37to50);
|
|
12470
|
-
console.log(" - nPacificMajority:", pa.minority.metrics.nPacificMajority);
|
|
12471
|
-
console.log(" - nAsian37to50:", pa.minority.metrics.nAsian37to50);
|
|
12472
|
-
console.log(" - nAsianMajority:", pa.minority.metrics.nAsianMajority);
|
|
12473
|
-
console.log(" - nNative37to50:", pa.minority.metrics.nNative37to50);
|
|
12474
|
-
console.log(" - nNativeMajority:", pa.minority.metrics.nNativeMajority);
|
|
12475
|
-
console.log(" - nMinority37to50:", pa.minority.metrics.nMinority37to50);
|
|
12476
|
-
console.log(" - nMinorityMajority:", pa.minority.metrics.nMinorityMajority);
|
|
12477
|
-
console.log("* Details:");
|
|
12478
|
-
console.log(" - VAP:", pa.minority.details.vap);
|
|
12479
|
-
console.log(" - comboCategoroes:", pa.minority.details.comboCategories);
|
|
12480
|
-
console.log("* Datasets:");
|
|
12481
|
-
console.log("- Shapes:", pa.minority.datasets.shapes);
|
|
12482
|
-
console.log("- Census:", pa.minority.datasets.census);
|
|
12483
|
-
console.log("- VAP:", pa.minority.datasets.vap);
|
|
12484
|
-
console.log("- Election:", pa.minority.datasets.election);
|
|
12485
|
-
console.log("* Resources:");
|
|
12486
|
-
console.log("- N/A");
|
|
12487
|
-
console.log("");
|
|
12488
|
-
}
|
|
12489
|
-
function echoDistrictStatistics(ds) {
|
|
12490
|
-
console.log("District Statistics:");
|
|
12491
|
-
for (let row of ds.table) {
|
|
12492
|
-
let DistrictID = row[0 /* DistrictID */];
|
|
12493
|
-
let TotalPop = row[1 /* TotalPop */];
|
|
12494
|
-
let PopDevPct = row[2 /* PopDevPct */];
|
|
12495
|
-
let bEqualPop = row[3 /* bEqualPop */];
|
|
12496
|
-
let bNotEmpty = row[4 /* bNotEmpty */];
|
|
12497
|
-
let bContiguous = row[5 /* bContiguous */];
|
|
12498
|
-
let bNotEmbedded = row[6 /* bNotEmbedded */];
|
|
12499
|
-
let DemPct = row[7 /* DemPct */];
|
|
12500
|
-
let RepPct = row[8 /* RepPct */];
|
|
12501
|
-
let WhitePct = row[9 /* WhitePct */];
|
|
12502
|
-
let MinorityPct = row[10 /* MinorityPct */];
|
|
12503
|
-
let BlackPct = row[11 /* BlackPct */];
|
|
12504
|
-
let HispanicPct = row[12 /* HispanicPct */];
|
|
12505
|
-
let PacificPct = row[13 /* PacificPct */];
|
|
12506
|
-
let AsianPct = row[14 /* AsianPct */];
|
|
12507
|
-
let NativePct = row[15 /* NativePct */];
|
|
12508
|
-
console.log(DistrictID, formatNumber(TotalPop), formatNumber(PopDevPct), bEqualPop, bNotEmpty, bContiguous, bNotEmbedded, formatNumber(DemPct), formatNumber(RepPct), formatNumber(WhitePct), formatNumber(MinorityPct), formatNumber(BlackPct), formatNumber(HispanicPct), formatNumber(PacificPct), formatNumber(AsianPct), formatNumber(NativePct));
|
|
12118
|
+
case 'limit': {
|
|
12119
|
+
s.analyzePlan(bLog);
|
|
12120
|
+
text = s.prepareScorecard();
|
|
12121
|
+
console.log("");
|
|
12122
|
+
for (let line of text.data) {
|
|
12123
|
+
console.log(line['variant'], line['text']);
|
|
12124
|
+
}
|
|
12125
|
+
console.log("");
|
|
12126
|
+
text = s.prepareTestLog();
|
|
12127
|
+
console.log("");
|
|
12128
|
+
for (let line of text.data) {
|
|
12129
|
+
console.log(line['variant'], line['text']);
|
|
12130
|
+
}
|
|
12131
|
+
console.log("");
|
|
12132
|
+
break;
|
|
12509
12133
|
}
|
|
12510
|
-
console.log("");
|
|
12511
|
-
console.log("* Details:");
|
|
12512
|
-
console.log(" - N/A");
|
|
12513
|
-
console.log("* Datasets:");
|
|
12514
|
-
console.log("- Shapes:", ds.datasets.shapes);
|
|
12515
|
-
console.log("- Census:", ds.datasets.census);
|
|
12516
|
-
console.log("- VAP:", ds.datasets.vap);
|
|
12517
|
-
console.log("- Election:", ds.datasets.election);
|
|
12518
|
-
console.log("* Resources:");
|
|
12519
|
-
console.log("- N/A");
|
|
12520
|
-
console.log("");
|
|
12521
12134
|
}
|
|
12522
|
-
|
|
12135
|
+
console.log("Ending command @ ", new Date());
|
|
12523
12136
|
// HELPERS TO LOAD SAMPLE DATA FROM DISK
|
|
12524
12137
|
// A clone of 'carefulRead' in DRA-CLI
|
|
12525
12138
|
function readJSONcareful(file) {
|
|
@@ -12540,7 +12153,7 @@ function readJSON(file) {
|
|
|
12540
12153
|
fullPath = file;
|
|
12541
12154
|
}
|
|
12542
12155
|
else {
|
|
12543
|
-
fullPath = path.
|
|
12156
|
+
fullPath = path.join(__dirname, file);
|
|
12544
12157
|
}
|
|
12545
12158
|
return readJSONcareful(fullPath);
|
|
12546
12159
|
}
|
|
@@ -12549,7 +12162,7 @@ exports.readJSON = readJSON;
|
|
|
12549
12162
|
function readCSV(file) {
|
|
12550
12163
|
try {
|
|
12551
12164
|
let input = fs.readFileSync(file, 'utf8');
|
|
12552
|
-
let dictRows =
|
|
12165
|
+
let dictRows = parse(input, {
|
|
12553
12166
|
columns: true,
|
|
12554
12167
|
skip_empty_lines: true
|
|
12555
12168
|
});
|
|
@@ -12569,7 +12182,7 @@ function readPlanCSV(file) {
|
|
|
12569
12182
|
fullPath = file;
|
|
12570
12183
|
}
|
|
12571
12184
|
else {
|
|
12572
|
-
fullPath = path.
|
|
12185
|
+
fullPath = path.join(__dirname, file);
|
|
12573
12186
|
}
|
|
12574
12187
|
var csvArray = readCSV(fullPath);
|
|
12575
12188
|
for (let dictRow of csvArray) {
|
|
@@ -12580,8 +12193,9 @@ function readPlanCSV(file) {
|
|
|
12580
12193
|
return plan;
|
|
12581
12194
|
}
|
|
12582
12195
|
exports.readPlanCSV = readPlanCSV;
|
|
12583
|
-
//
|
|
12196
|
+
// FIN
|
|
12584
12197
|
|
|
12198
|
+
/* WEBPACK VAR INJECTION */}.call(this, "/"))
|
|
12585
12199
|
|
|
12586
12200
|
/***/ }),
|
|
12587
12201
|
|