@dra2020/district-analytics 1.0.6 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +102 -52
- package/dist/cli.js.map +1 -1
- package/dist/district-analytics.js +102 -52
- package/dist/district-analytics.js.map +1 -1
- package/lib/HelloWorld.d.ts +3 -0
- package/lib/HelloWorld.js +11 -0
- package/lib/_api.js +91 -0
- package/lib/_api.js.map +1 -0
- package/lib/_cli.js +434 -0
- package/lib/_cli.js.map +1 -0
- package/lib/_data.js +425 -0
- package/lib/_data.js.map +1 -0
- package/lib/analyze.d.ts +3 -0
- package/lib/analyze.js +69 -0
- package/lib/analyze.js.map +1 -0
- package/lib/api.d.ts +34 -0
- package/lib/api.js +117 -0
- package/lib/api.js.map +1 -0
- package/lib/cli.d.ts +1 -0
- package/lib/cli.js +386 -0
- package/lib/cli.js.map +1 -0
- package/lib/cohesive.d.ts +4 -0
- package/lib/cohesive.js +132 -0
- package/lib/cohesive.js.map +1 -0
- package/lib/compact.d.ts +4 -0
- package/lib/compact.js +183 -0
- package/lib/compact.js.map +1 -0
- package/lib/constants.js +367 -0
- package/lib/constants.js.map +1 -0
- package/lib/data.js +188 -0
- package/lib/data.js.map +1 -0
- package/lib/equal.d.ts +4 -0
- package/lib/equal.js +59 -0
- package/lib/equal.js.map +1 -0
- package/lib/features.js +19 -0
- package/lib/features.js.map +1 -0
- package/lib/geofeature.js +112 -0
- package/lib/geofeature.js.map +1 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.js +11 -0
- package/lib/index.js.map +1 -0
- package/lib/minority.d.ts +3 -0
- package/lib/minority.js +61 -0
- package/lib/minority.js.map +1 -0
- package/lib/political.d.ts +7 -0
- package/lib/political.js +88 -0
- package/lib/political.js.map +1 -0
- package/lib/preprocess.d.ts +4 -0
- package/lib/preprocess.js +101 -0
- package/lib/preprocess.js.map +1 -0
- package/lib/report.d.ts +14 -0
- package/lib/report.js +817 -0
- package/lib/report.js.map +1 -0
- package/lib/sample.d.ts +1 -0
- package/lib/sample.js +32 -0
- package/lib/sample.js.map +1 -0
- package/lib/scorecard.d.ts +4 -0
- package/lib/scorecard.js +237 -0
- package/lib/scorecard.js.map +1 -0
- package/lib/settings.d.ts +5 -0
- package/lib/settings.js +18 -0
- package/lib/settings.js.map +1 -0
- package/lib/types.d.ts +125 -0
- package/lib/types.js +7 -0
- package/lib/types.js.map +1 -0
- package/lib/utils.d.ts +20 -0
- package/lib/utils.js +223 -0
- package/lib/utils.js.map +1 -0
- package/lib/valid.d.ts +5 -0
- package/lib/valid.js +230 -0
- package/lib/valid.js.map +1 -0
- package/package.json +2 -2
- package/src/_api.ts +1 -0
- package/src/_data.ts +42 -19
- package/src/report.ts +59 -34
package/lib/api.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
//
|
|
3
|
+
// THE NODE PACKAGE API
|
|
4
|
+
//
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const U = require("./utils");
|
|
7
|
+
const preprocess_1 = require("./preprocess");
|
|
8
|
+
const analyze_1 = require("./analyze");
|
|
9
|
+
const report_1 = require("./report");
|
|
10
|
+
class AnalyticsSession {
|
|
11
|
+
constructor(SessionRequest) {
|
|
12
|
+
this._config = {};
|
|
13
|
+
this._testScales = {};
|
|
14
|
+
this._tests = {};
|
|
15
|
+
this._scorecard = {};
|
|
16
|
+
// Grab the session parameters & pointers to data
|
|
17
|
+
this._title = SessionRequest['title'];
|
|
18
|
+
this._xx = SessionRequest['stateXX'];
|
|
19
|
+
this._legislativeDistricts = SessionRequest['legislativeDistricts'];
|
|
20
|
+
this._nDistricts = SessionRequest['nDistricts'];
|
|
21
|
+
this._planByGeoID = SessionRequest['plan'];
|
|
22
|
+
this._data = SessionRequest['data'];
|
|
23
|
+
this._featureIDs = {};
|
|
24
|
+
// TODO - INTEGRATION: data2.json
|
|
25
|
+
this._census = SessionRequest['census'];
|
|
26
|
+
this._election = SessionRequest['election'];
|
|
27
|
+
this._districtShapes = SessionRequest['districtShapes'];
|
|
28
|
+
this._districtProperties = {};
|
|
29
|
+
this._graph = SessionRequest['graph'];
|
|
30
|
+
this._countyNameLookup = {};
|
|
31
|
+
this._countyFeatures = SessionRequest['countyFeatures'];
|
|
32
|
+
this._stateTotal = 0;
|
|
33
|
+
this._nCounties = 0;
|
|
34
|
+
this._tooBigFIPS = [];
|
|
35
|
+
this._tooBigName = [];
|
|
36
|
+
this._expectedSplits = 0;
|
|
37
|
+
this._expectedAffected = 0;
|
|
38
|
+
// TODO - INTEGRATION: DELETE
|
|
39
|
+
// Process the config request
|
|
40
|
+
// let configRequest: T.Dict = {};
|
|
41
|
+
let defaultSuites = [0 /* Legal */, 1 /* Fair */, 2 /* Best */];
|
|
42
|
+
let defaultConfig = { 'suites': defaultSuites };
|
|
43
|
+
// TODO - INTEGRATION data2.json
|
|
44
|
+
// NOTE - Session settings are required:
|
|
45
|
+
// - Analytics suites can be defaulted to all with [], but
|
|
46
|
+
// - Dataset must be explicitly specified
|
|
47
|
+
this._config = SessionRequest['config'];
|
|
48
|
+
// If a config was passed in w/ empty suites = [], use the default suites
|
|
49
|
+
if (U.isArrayEmpty(this._config['suites'])) {
|
|
50
|
+
this._config['suites'] = defaultSuites;
|
|
51
|
+
}
|
|
52
|
+
// TODO - INTEGRATION: DELETE
|
|
53
|
+
// // If no config was passed in or it's empty, use the default config
|
|
54
|
+
// if ((SessionRequest['config'] == undefined) || (!(U.keyExists('config', SessionRequest)))) {
|
|
55
|
+
// this._config = defaultConfig;
|
|
56
|
+
// }
|
|
57
|
+
// else {
|
|
58
|
+
// // If a config was passed in w/o suites, use the default suites
|
|
59
|
+
// if (!(U.keyExists('suites', SessionRequest['config']))) {
|
|
60
|
+
// this._config['suites'] = defaultSuites;
|
|
61
|
+
// }
|
|
62
|
+
// // Otherwise use the config (and suites) as passed in
|
|
63
|
+
// else {
|
|
64
|
+
// this._config = SessionRequest['config'];
|
|
65
|
+
// }
|
|
66
|
+
// }
|
|
67
|
+
this._planByDistrictID = {};
|
|
68
|
+
this._countyIndex = {};
|
|
69
|
+
this._bOneTimeProcessingDone = false;
|
|
70
|
+
this._bPlanAnalyzed = false;
|
|
71
|
+
this._bPostProcessingDone = false;
|
|
72
|
+
// NOTE: I've pulled these out of the individual analytics to here. Eventually,
|
|
73
|
+
// we could want them to passed into an analytics session as data, along with
|
|
74
|
+
// everything else. For now, this keeps branching out of the main code.
|
|
75
|
+
report_1.doConfigureScales(this);
|
|
76
|
+
}
|
|
77
|
+
// Using the the data in the analytics session, calculate all the
|
|
78
|
+
// analytics & validations, saving/updating the individual test results.
|
|
79
|
+
analyzePlan(bLog = false) {
|
|
80
|
+
try {
|
|
81
|
+
preprocess_1.doPreprocessData(this);
|
|
82
|
+
analyze_1.doAnalyzePlan(this, bLog);
|
|
83
|
+
}
|
|
84
|
+
catch (_a) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
// TODO - Terry/Dave: Do I need a guard here?
|
|
90
|
+
// Get an individual test, so you can drive UI with the results.
|
|
91
|
+
getTest(testID) {
|
|
92
|
+
// Get the existing test entries
|
|
93
|
+
// T.Test is a numeric enum, so convert the string keys to numbers
|
|
94
|
+
let testValues = U.getNumericObjectKeys(this._tests);
|
|
95
|
+
// If there's no entry for this test, create & initialize one
|
|
96
|
+
if (!(testValues.includes(testID))) {
|
|
97
|
+
this._tests[testID] = {};
|
|
98
|
+
this._tests[testID]['score'] = undefined;
|
|
99
|
+
this._tests[testID]['details'] = {};
|
|
100
|
+
this._tests[testID]['districtWIP'] = {};
|
|
101
|
+
}
|
|
102
|
+
// Return a pointer to the the test entry for this test
|
|
103
|
+
return this._tests[testID];
|
|
104
|
+
}
|
|
105
|
+
// Prepare a scorecard for rendering
|
|
106
|
+
// NOTE - This assumes that analyzePlan() has been run!
|
|
107
|
+
prepareScorecard() {
|
|
108
|
+
return report_1.doPrepareScorecard(this);
|
|
109
|
+
}
|
|
110
|
+
// Prepare test results for rendering
|
|
111
|
+
// NOTE - This assumes that analyzePlan() has been run!
|
|
112
|
+
prepareTestLog() {
|
|
113
|
+
return report_1.doPrepareTestLog(this);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
exports.AnalyticsSession = AnalyticsSession;
|
|
117
|
+
//# sourceMappingURL=api.js.map
|
package/lib/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/lib/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/lib/cli.js
ADDED
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
//
|
|
3
|
+
// A SIMPLE COMMAND-LINE INTERFACE FOR EXERCISING THE ANALYTICS & VALIDATIONS
|
|
4
|
+
//
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const yargs = require("yargs");
|
|
7
|
+
// TODO - DELETE
|
|
8
|
+
// import * as fs from 'fs';
|
|
9
|
+
// import * as path from 'path';
|
|
10
|
+
// import * as parse from 'csv-parse/lib/sync';
|
|
11
|
+
const PC = require("polygon-clipping");
|
|
12
|
+
const Poly = require("@dra2020/poly");
|
|
13
|
+
const api_1 = require("./api");
|
|
14
|
+
const preprocess_1 = require("./preprocess");
|
|
15
|
+
const valid_1 = require("./valid");
|
|
16
|
+
const equal_1 = require("./equal");
|
|
17
|
+
const compact_1 = require("./compact");
|
|
18
|
+
const cohesive_1 = require("./cohesive");
|
|
19
|
+
const political_1 = require("./political");
|
|
20
|
+
const minority_1 = require("./minority");
|
|
21
|
+
const U = require("./utils");
|
|
22
|
+
const D = require("./data");
|
|
23
|
+
// Simulate DRA unioning district shapes in the background
|
|
24
|
+
function addToPoly(poly, polys) {
|
|
25
|
+
return PC.union(poly, ...polys);
|
|
26
|
+
}
|
|
27
|
+
console.log("Starting command @ ", new Date());
|
|
28
|
+
// COMMAND LINE
|
|
29
|
+
let argv = yargs
|
|
30
|
+
.usage('Usage: $0 command [options]')
|
|
31
|
+
.example('$0 equal -f foo.geojson', 'Calculate population deviation')
|
|
32
|
+
.demandCommand(1, 'You must specify a command to execute.')
|
|
33
|
+
.command('valid', 'Perform validations.')
|
|
34
|
+
.command('equal', 'Calculate population deviation.')
|
|
35
|
+
.command('compact', 'Calculate compactness.')
|
|
36
|
+
.command('cohesive', 'Calculate cohesiveness.')
|
|
37
|
+
.command('political', 'Assess fairness.')
|
|
38
|
+
.command('minority', 'Count majority-minority districts.')
|
|
39
|
+
.command('analyze', 'API call to analyze a plan.')
|
|
40
|
+
.command('report', 'API call to generate a scorecard.')
|
|
41
|
+
.option('state', {
|
|
42
|
+
alias: 'xx',
|
|
43
|
+
describe: 'Specify the state.',
|
|
44
|
+
type: 'string'
|
|
45
|
+
})
|
|
46
|
+
.option('number', {
|
|
47
|
+
alias: 'n',
|
|
48
|
+
describe: 'Specify the number of districts.',
|
|
49
|
+
type: 'number'
|
|
50
|
+
})
|
|
51
|
+
.option('legislative', {
|
|
52
|
+
alias: 'l',
|
|
53
|
+
describe: 'Specify that districts are legislative.',
|
|
54
|
+
type: 'boolean',
|
|
55
|
+
default: false
|
|
56
|
+
})
|
|
57
|
+
.option('plan', {
|
|
58
|
+
alias: 'p',
|
|
59
|
+
describe: 'Specify the plan to evaluate.',
|
|
60
|
+
type: 'string'
|
|
61
|
+
})
|
|
62
|
+
.option('census', {
|
|
63
|
+
alias: 'c',
|
|
64
|
+
describe: 'Specify the Census data to use.',
|
|
65
|
+
type: 'string'
|
|
66
|
+
})
|
|
67
|
+
.option('shapes', {
|
|
68
|
+
alias: 's',
|
|
69
|
+
describe: 'Specify the shapes to use.',
|
|
70
|
+
type: 'string'
|
|
71
|
+
})
|
|
72
|
+
.option('graph', {
|
|
73
|
+
alias: 'g',
|
|
74
|
+
describe: 'Specify the contiguity graph to use.',
|
|
75
|
+
type: 'string'
|
|
76
|
+
})
|
|
77
|
+
.option('election', {
|
|
78
|
+
alias: 'e',
|
|
79
|
+
describe: 'Specify the election data to use.',
|
|
80
|
+
type: 'string'
|
|
81
|
+
})
|
|
82
|
+
.option('counties', {
|
|
83
|
+
alias: 'f',
|
|
84
|
+
describe: 'Specify the FIPS-to-county-name map to use.',
|
|
85
|
+
type: 'string'
|
|
86
|
+
})
|
|
87
|
+
.option('empty', {
|
|
88
|
+
describe: 'Specify whether a district should be empty.',
|
|
89
|
+
type: 'boolean',
|
|
90
|
+
default: false
|
|
91
|
+
})
|
|
92
|
+
.option('suites', {
|
|
93
|
+
describe: 'Specify the test suites to run.',
|
|
94
|
+
type: 'array',
|
|
95
|
+
default: []
|
|
96
|
+
})
|
|
97
|
+
.option('verbose', {
|
|
98
|
+
alias: 'v',
|
|
99
|
+
describe: 'Specify whether code should log to STDOUT.',
|
|
100
|
+
type: 'boolean',
|
|
101
|
+
default: false
|
|
102
|
+
})
|
|
103
|
+
.demandOption(['number', 'plan', 'census', 'shapes', 'election', 'counties'], 'Please specify all the args.')
|
|
104
|
+
.help()
|
|
105
|
+
.argv;
|
|
106
|
+
// PROCESS COMMAND LINE ARGS
|
|
107
|
+
let command = argv._[0];
|
|
108
|
+
let xx = argv.state;
|
|
109
|
+
let bLegislativeDistricts = argv.legislative;
|
|
110
|
+
let nDistricts = argv.n;
|
|
111
|
+
let planByGeoID = D.readPlanCSV(argv.plan);
|
|
112
|
+
// Don't load other datasets, if just calculating compactness
|
|
113
|
+
let graph = {};
|
|
114
|
+
let census = {};
|
|
115
|
+
// TODO - INTEGRATION: data2.json
|
|
116
|
+
let data = {};
|
|
117
|
+
// TODO - DELETE
|
|
118
|
+
let election = {};
|
|
119
|
+
let counties = {};
|
|
120
|
+
if (command != 'compact') {
|
|
121
|
+
graph = D.readJSON(argv.graph);
|
|
122
|
+
// TODO - INTEGRATION: data2.json
|
|
123
|
+
data = D.readJSON("../data/SAMPLE-BG-data2.json");
|
|
124
|
+
// TODO - DELETE
|
|
125
|
+
census = D.readCensusCSV(argv.census);
|
|
126
|
+
election = D.readElectionCSV(argv.election);
|
|
127
|
+
counties = D.readJSON(argv.counties);
|
|
128
|
+
}
|
|
129
|
+
let shapes = D.readJSON(argv.shapes);
|
|
130
|
+
let bEmpty = argv.empty;
|
|
131
|
+
// TODO - INTEGRATION:
|
|
132
|
+
// - Expand 'suites' to 'settings'
|
|
133
|
+
// - Allow 'datasets' to be passed in from the command line too
|
|
134
|
+
// - Update configs
|
|
135
|
+
let suites = argv.suites;
|
|
136
|
+
// TODO - INTEGRATION: Delete
|
|
137
|
+
// Keys for the datasets to use for analytics & display
|
|
138
|
+
// export type DSKeys = {
|
|
139
|
+
// CENSUS: string; // A total population Census dataset
|
|
140
|
+
// VAP: string; // A voting age (or citizen voting age) dataset
|
|
141
|
+
// ELECTION: string; // An election dataset
|
|
142
|
+
// }
|
|
143
|
+
let datasets = {
|
|
144
|
+
CENSUS: "D16F",
|
|
145
|
+
VAP: "D16T",
|
|
146
|
+
ELECTION: "E16GPR"
|
|
147
|
+
};
|
|
148
|
+
// } as D.DSKeys;
|
|
149
|
+
// Session settings are required:
|
|
150
|
+
// - Analytics suites can be defaulted to all with [], but
|
|
151
|
+
// - Dataset must be explicitly specified
|
|
152
|
+
let sessionSettings = {
|
|
153
|
+
'suites': suites,
|
|
154
|
+
'datasets': datasets
|
|
155
|
+
};
|
|
156
|
+
let bLog = argv.verbose;
|
|
157
|
+
// TODO - INTEGRATION: Does DRA have this to pass?
|
|
158
|
+
// Invert the plan, so you can create the district shapes for the SessionRequest.
|
|
159
|
+
let planByDistrictID = preprocess_1.invertPlan(planByGeoID);
|
|
160
|
+
// SIMULATE THE HOST
|
|
161
|
+
// 1 - Create district shapes & extract properties (area, diameter, perimeter)
|
|
162
|
+
const polyOptions = {
|
|
163
|
+
noLatitudeCorrection: false
|
|
164
|
+
};
|
|
165
|
+
// Index the shapes by geoID
|
|
166
|
+
let shapesByGeoID = {};
|
|
167
|
+
let idStr = "GEOID";
|
|
168
|
+
// TODO - Terry: But sometimes "GEOID10" hasn't been set to "GEOID"
|
|
169
|
+
let firstFeature = shapes.features[0];
|
|
170
|
+
if (!(firstFeature.properties == null)) {
|
|
171
|
+
if (U.keyExists("GEOID10", firstFeature.properties))
|
|
172
|
+
idStr = "GEOID10";
|
|
173
|
+
}
|
|
174
|
+
for (let f of shapes.features) {
|
|
175
|
+
if (!(f.properties == null)) {
|
|
176
|
+
// let idStr = "GEOID";
|
|
177
|
+
// TODO - Terry: Sometimes "GEOID10" hasn't been => to "GEOID!"
|
|
178
|
+
// if (U.keyExists("GEOID10", f.properties)) idStr = "GEOID10";
|
|
179
|
+
let geoID = f.properties[idStr];
|
|
180
|
+
shapesByGeoID[geoID] = f;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
// Find the smallest Feature
|
|
184
|
+
// COPIED from dra-client/geodistrict.ts, and lightly modified
|
|
185
|
+
let geo = shapes;
|
|
186
|
+
let minFeatureArea = Poly.polyArea(geo.features[0], polyOptions);
|
|
187
|
+
for (let i = 1; i < geo.features.length; i++) {
|
|
188
|
+
let a = Poly.polyArea(geo.features[i], polyOptions);
|
|
189
|
+
if (a < minFeatureArea)
|
|
190
|
+
minFeatureArea = a;
|
|
191
|
+
}
|
|
192
|
+
// Eliminate anomalous holes
|
|
193
|
+
// COPIED from dra-client/geodistrict.ts, and lightly modified
|
|
194
|
+
// This is a bit of a hack in the sense that it is still an open question as to whether this is a problem
|
|
195
|
+
// with either the underlying shape data or a problem in our simplification of that shape data.
|
|
196
|
+
// But in any case, what this does is eliminate any holes that are smaller than the smallest feature. Such
|
|
197
|
+
// a hole must be an anomaly. CA/2010_VT in particular seems to have many such issues.
|
|
198
|
+
function eliminateAnomalousHoles(mp) {
|
|
199
|
+
// Normalize to multipolygon
|
|
200
|
+
if (U.depthof(mp) == 4)
|
|
201
|
+
mp = [mp];
|
|
202
|
+
for (let i = 0; i < mp.length; i++) {
|
|
203
|
+
let p = mp[i];
|
|
204
|
+
let cleanp = [p[0]];
|
|
205
|
+
for (let j = 1; j < p.length; j++) {
|
|
206
|
+
let a = Poly.polyArea(p[j], polyOptions);
|
|
207
|
+
if (a >= minFeatureArea)
|
|
208
|
+
cleanp.push(p[j]);
|
|
209
|
+
}
|
|
210
|
+
if (cleanp.length < p.length)
|
|
211
|
+
mp[i] = cleanp;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// Create district shapes
|
|
215
|
+
let districtShapes = {};
|
|
216
|
+
districtShapes['type'] = "FeatureCollection";
|
|
217
|
+
districtShapes['features'] = [];
|
|
218
|
+
for (let districtID = 1; districtID <= nDistricts; districtID++) {
|
|
219
|
+
let districtFeatures = [];
|
|
220
|
+
// Collect Features by district
|
|
221
|
+
planByDistrictID[districtID].forEach(function (geoID) {
|
|
222
|
+
districtFeatures.push(shapesByGeoID[geoID]);
|
|
223
|
+
});
|
|
224
|
+
// Union them together
|
|
225
|
+
let poly1Coordinates = Poly.polyNormalize(districtFeatures[0], polyOptions);
|
|
226
|
+
let polyNCoordinates = districtFeatures.slice(1).map((i) => Poly.polyNormalize(i, polyOptions));
|
|
227
|
+
let poly = addToPoly(poly1Coordinates, polyNCoordinates);
|
|
228
|
+
// Convert the result to a Feature
|
|
229
|
+
let bPolygon = U.depthof(poly) == 4;
|
|
230
|
+
if (!bPolygon && poly.length == 1) {
|
|
231
|
+
bPolygon = true;
|
|
232
|
+
poly = poly[0];
|
|
233
|
+
}
|
|
234
|
+
eliminateAnomalousHoles(poly);
|
|
235
|
+
let f = {
|
|
236
|
+
type: 'Feature',
|
|
237
|
+
properties: { districtID: `${districtID}` },
|
|
238
|
+
geometry: {
|
|
239
|
+
type: bPolygon ? 'Polygon' : 'MultiPolygon',
|
|
240
|
+
coordinates: poly
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
districtShapes.features.push(f);
|
|
244
|
+
}
|
|
245
|
+
// CONSTRUCT A SESSION REQUEST
|
|
246
|
+
let SessionRequest = {};
|
|
247
|
+
SessionRequest['title'] = "CLI Test";
|
|
248
|
+
SessionRequest['stateXX'] = xx;
|
|
249
|
+
SessionRequest['nDistricts'] = nDistricts;
|
|
250
|
+
SessionRequest['legislativeDistricts'] = bLegislativeDistricts;
|
|
251
|
+
SessionRequest['plan'] = planByGeoID;
|
|
252
|
+
SessionRequest['data'] = data;
|
|
253
|
+
// TODO - INTEGRATION: data2.json
|
|
254
|
+
SessionRequest['census'] = census;
|
|
255
|
+
SessionRequest['election'] = election;
|
|
256
|
+
SessionRequest['districtShapes'] = districtShapes;
|
|
257
|
+
SessionRequest['graph'] = graph;
|
|
258
|
+
SessionRequest['countyFeatures'] = counties;
|
|
259
|
+
// TODO - INTEGRATION: DELETE
|
|
260
|
+
// if (!(U.isArrayEmpty(suites))) {
|
|
261
|
+
// sessionSettings['suites'] = suites;
|
|
262
|
+
// }
|
|
263
|
+
SessionRequest['config'] = sessionSettings;
|
|
264
|
+
// CREATE AND INITIALIZE AN ANALYTICS SESSION
|
|
265
|
+
let s = new api_1.AnalyticsSession(SessionRequest);
|
|
266
|
+
// DISPATCH TO REQUESTED COMMAND W/ NEEDED SCAFFOLDING
|
|
267
|
+
let t;
|
|
268
|
+
let text;
|
|
269
|
+
function echoTestResult(test, t) {
|
|
270
|
+
console.log("");
|
|
271
|
+
console.log("Test:", test);
|
|
272
|
+
console.log("Score:", t['score']);
|
|
273
|
+
let keys = U.getObjectKeys(t['details']);
|
|
274
|
+
if (keys.length > 0) {
|
|
275
|
+
console.log("Details:");
|
|
276
|
+
for (let i in keys) {
|
|
277
|
+
let key = keys[i];
|
|
278
|
+
console.log("-", key, "=", t['details'][key]);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
console.log("___");
|
|
282
|
+
}
|
|
283
|
+
if (bEmpty) {
|
|
284
|
+
// Make a district empty
|
|
285
|
+
let randomDistrict = Math.floor(Math.random() * s._nDistricts) + 1;
|
|
286
|
+
// TODO - Terry, why does this constructor not need a <T> type specification?
|
|
287
|
+
s._planByDistrictID[randomDistrict] = new Set();
|
|
288
|
+
}
|
|
289
|
+
switch (command) {
|
|
290
|
+
case 'valid': {
|
|
291
|
+
preprocess_1.doPreprocessData(s);
|
|
292
|
+
t = valid_1.doIsComplete(s);
|
|
293
|
+
echoTestResult("Complete:", t);
|
|
294
|
+
t = valid_1.doIsContiguous(s);
|
|
295
|
+
echoTestResult("Contiguous:", t);
|
|
296
|
+
t = valid_1.doIsFreeOfHoles(s);
|
|
297
|
+
echoTestResult("Free of holes:", t);
|
|
298
|
+
t = equal_1.doPopulationDeviation(s);
|
|
299
|
+
echoTestResult("Population deviation (%):", t);
|
|
300
|
+
t = equal_1.doHasEqualPopulations(s);
|
|
301
|
+
echoTestResult("Equal populations:", t);
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
case 'equal': {
|
|
305
|
+
preprocess_1.doPreprocessData(s);
|
|
306
|
+
t = equal_1.doPopulationDeviation(s);
|
|
307
|
+
echoTestResult("Population deviation (%):", t);
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
case 'compact': {
|
|
311
|
+
preprocess_1.doPreprocessData(s);
|
|
312
|
+
t = compact_1.doReock(s, bLog);
|
|
313
|
+
echoTestResult("Reock:", t);
|
|
314
|
+
t = compact_1.doPolsbyPopper(s, bLog);
|
|
315
|
+
echoTestResult("Polsby-Popper:", t);
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
case 'cohesive': {
|
|
319
|
+
preprocess_1.doPreprocessData(s);
|
|
320
|
+
t = cohesive_1.doCountySplits(s);
|
|
321
|
+
echoTestResult("County splits:", t);
|
|
322
|
+
cohesive_1.doPlanComplexity(s);
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
case 'political': {
|
|
326
|
+
preprocess_1.doPreprocessData(s);
|
|
327
|
+
political_1.doSeatsBias(s);
|
|
328
|
+
political_1.doVotesBias(s);
|
|
329
|
+
political_1.doResponsiveness(s);
|
|
330
|
+
political_1.doResponsiveDistricts(s);
|
|
331
|
+
t = political_1.doEfficiencyGap(s);
|
|
332
|
+
echoTestResult("Efficiency gap (%):", t);
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
case 'minority': {
|
|
336
|
+
preprocess_1.doPreprocessData(s);
|
|
337
|
+
minority_1.doMajorityMinorityDistricts(s);
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
case 'analyze': {
|
|
341
|
+
console.log("");
|
|
342
|
+
console.log("Analyzing a plan ...");
|
|
343
|
+
console.log("");
|
|
344
|
+
s.analyzePlan(bLog);
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
case 'scorecard': {
|
|
348
|
+
s.analyzePlan(bLog);
|
|
349
|
+
text = s.prepareScorecard();
|
|
350
|
+
console.log("");
|
|
351
|
+
for (let line of text.data) {
|
|
352
|
+
console.log(line['variant'], line['text']);
|
|
353
|
+
}
|
|
354
|
+
console.log("");
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
case 'testlog': {
|
|
358
|
+
s.analyzePlan(bLog);
|
|
359
|
+
text = s.prepareTestLog();
|
|
360
|
+
console.log("");
|
|
361
|
+
for (let line of text.data) {
|
|
362
|
+
console.log(line['variant'], line['text']);
|
|
363
|
+
}
|
|
364
|
+
console.log("");
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
case 'limit': {
|
|
368
|
+
s.analyzePlan(bLog);
|
|
369
|
+
text = s.prepareScorecard();
|
|
370
|
+
console.log("");
|
|
371
|
+
for (let line of text.data) {
|
|
372
|
+
console.log(line['variant'], line['text']);
|
|
373
|
+
}
|
|
374
|
+
console.log("");
|
|
375
|
+
text = s.prepareTestLog();
|
|
376
|
+
console.log("");
|
|
377
|
+
for (let line of text.data) {
|
|
378
|
+
console.log(line['variant'], line['text']);
|
|
379
|
+
}
|
|
380
|
+
console.log("");
|
|
381
|
+
break;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
console.log("Ending command @ ", new Date());
|
|
385
|
+
// FIN
|
|
386
|
+
//# sourceMappingURL=cli.js.map
|
package/lib/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/lib/cohesive.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
//
|
|
3
|
+
// "COHESIVE" - We're naming this category which is about county splitting.
|
|
4
|
+
//
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const U = require("./utils");
|
|
7
|
+
const D = require("./_data");
|
|
8
|
+
function doCountySplits(s) {
|
|
9
|
+
let test = s.getTest(7 /* CountySplits */);
|
|
10
|
+
// THE THREE VALUES TO DETERMINE FOR A PLAN
|
|
11
|
+
let unexpectedSplits = 0;
|
|
12
|
+
let unexpectedAffected = 0;
|
|
13
|
+
let countiesSplitUnexpectedly = [];
|
|
14
|
+
// FIRST, ANALYZE THE COUNTY SPLITING FOR THE PLAN
|
|
15
|
+
// Pivot census totals into county-district "splits"
|
|
16
|
+
let countiesByDistrict = s.districts.statistics[D.DistrictField.CountySplits];
|
|
17
|
+
// countiesByDistrict = countiesByDistrict.slice(1, -1);
|
|
18
|
+
// Find the single-county districts, i.e., districts NOT split across counties.
|
|
19
|
+
// Ignore the dummy unassigned 0 and N+1 summary "districts."
|
|
20
|
+
let singleCountyDistricts = [];
|
|
21
|
+
for (let d = 1; d <= s.state.nDistricts; d++) {
|
|
22
|
+
// See if there's only one county partition
|
|
23
|
+
let nCountiesInDistrict = 0;
|
|
24
|
+
for (let c = 0; c < s.counties.nCounties; c++) {
|
|
25
|
+
if (countiesByDistrict[d][c] > 0) {
|
|
26
|
+
nCountiesInDistrict += 1;
|
|
27
|
+
if (nCountiesInDistrict > 1) {
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// If so, save the district
|
|
33
|
+
if (nCountiesInDistrict == 1) {
|
|
34
|
+
singleCountyDistricts.push(d);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Process the splits/partitions in the plan:
|
|
38
|
+
// - Count the total # of partitions,
|
|
39
|
+
// - Find the counties split across districts, and
|
|
40
|
+
// - Accumulate the number people affected (except when single-county districts)
|
|
41
|
+
let nPartitionsOverall = 0;
|
|
42
|
+
let splitCounties = new Set(); // The counties that are split across districts
|
|
43
|
+
let totalAffected = 0; // The total population affected by those splits
|
|
44
|
+
for (let c = 0; c < s.counties.nCounties; c++) {
|
|
45
|
+
let nCountyParts = 0;
|
|
46
|
+
let subtotal = 0;
|
|
47
|
+
for (let d = 1; d <= s.state.nDistricts; d++) {
|
|
48
|
+
if (countiesByDistrict[d][c] > 0) {
|
|
49
|
+
nPartitionsOverall += 1;
|
|
50
|
+
nCountyParts += 1;
|
|
51
|
+
if (!(U.arrayContains(singleCountyDistricts, d))) {
|
|
52
|
+
subtotal += countiesByDistrict[d][c];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (nCountyParts > 1) {
|
|
57
|
+
splitCounties.add(c);
|
|
58
|
+
totalAffected += subtotal;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Convert county ordinals to FIPS codes
|
|
62
|
+
let splitCountiesFIPS = U.getSelectObjectKeys(s.counties.index, [...splitCounties]);
|
|
63
|
+
// THEN TAKE ACCOUNT OF THE COUNTY SPLITTING THAT IS EXPECTED (REQUIRED)
|
|
64
|
+
// Compute the total number of splits this way, in case any counties are split
|
|
65
|
+
// more than once. I.e., it's not just len(all_counties_split).
|
|
66
|
+
let nSplits = nPartitionsOverall - s.counties.nCounties;
|
|
67
|
+
// Determine the number of *unexpected* splits. NOTE: Prevent negative numbers,
|
|
68
|
+
// in case you have a plan the *doesn't* split counties that would have to be
|
|
69
|
+
// split due to their size.
|
|
70
|
+
unexpectedSplits = Math.max(0, nSplits - s.state.expectedSplits);
|
|
71
|
+
// Subtract off the total population that *has* to be affected by splits,
|
|
72
|
+
// because their counties are too big. NOTE: Again, prevent negative numbers,
|
|
73
|
+
// in case you have a plan the *doesn't* split counties that would have to be
|
|
74
|
+
// split due to their size.
|
|
75
|
+
unexpectedAffected = U.trim(Math.max(0, totalAffected - s.state.expectedAffected) / s.state.totalPop);
|
|
76
|
+
// Find the counties that are split *unexpectedly*. From all the counties that
|
|
77
|
+
// are split, remove those that *have* to be split, because they are bigger than
|
|
78
|
+
// a district.
|
|
79
|
+
let countiesSplitUnexpectedlyFIPS = [];
|
|
80
|
+
for (let fips of splitCountiesFIPS) {
|
|
81
|
+
if (!(U.arrayContains(s.state.tooBigFIPS, fips))) {
|
|
82
|
+
countiesSplitUnexpectedlyFIPS.push(fips);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// ... and convert the FIPS codes to county names.
|
|
86
|
+
for (let fips of countiesSplitUnexpectedlyFIPS) {
|
|
87
|
+
countiesSplitUnexpectedly.push(s.counties.nameFromFIPS(fips));
|
|
88
|
+
}
|
|
89
|
+
countiesSplitUnexpectedly = countiesSplitUnexpectedly.sort();
|
|
90
|
+
// Cache the results in the test
|
|
91
|
+
test['score'] = unexpectedAffected; // TODO - Use Moon's complexity metric here
|
|
92
|
+
test['details'] = {
|
|
93
|
+
'unexpectedSplits': unexpectedSplits,
|
|
94
|
+
'unexpectedAffected': unexpectedAffected,
|
|
95
|
+
'countiesSplitUnexpectedly': countiesSplitUnexpectedly
|
|
96
|
+
};
|
|
97
|
+
return test;
|
|
98
|
+
}
|
|
99
|
+
exports.doCountySplits = doCountySplits;
|
|
100
|
+
// 2 - THE COMPLEXITY ANALYTIC NEEDS THE FOLLOWING DATA:
|
|
101
|
+
//
|
|
102
|
+
// If a map is already in simplified (mixed) form, the complexity analytic needs
|
|
103
|
+
// two pieces of data:
|
|
104
|
+
// - The counts of features by summary level--i.e., the numbers of counties, tracts,
|
|
105
|
+
// block groups, and blocks in a state; and
|
|
106
|
+
// - The map -- So it can count the features by summary level in the map,
|
|
107
|
+
// as well as the number of BG’s that are split.
|
|
108
|
+
//
|
|
109
|
+
// TODO - Where would the state counts come from? Preprocessed and passed in, or
|
|
110
|
+
// done in a one-time initialization call (which would require a full set of
|
|
111
|
+
// block geo_id's for the state).
|
|
112
|
+
//
|
|
113
|
+
// However, if a map is not yet (fully) simplified, then determining the
|
|
114
|
+
// complexity of a map also requires a preprocessed summary level hierarchy, so
|
|
115
|
+
// you can get the child features (e.g., tracts) of a parent feature (e.g.,
|
|
116
|
+
// a county).
|
|
117
|
+
//
|
|
118
|
+
// NOTE - I have script for producing this hierarchy which we could repurpose.
|
|
119
|
+
//
|
|
120
|
+
// TODO - For mixed map processing--specfically to find the neighbors of a feature
|
|
121
|
+
// that are actually in the map (as opposed to just neighbors at the same
|
|
122
|
+
// summary level in the static graph)--you need a special hierarchy that
|
|
123
|
+
// distinguishes between the 'interior' and 'edge children of a feature.
|
|
124
|
+
//
|
|
125
|
+
// NOTE - The script noted above does this.
|
|
126
|
+
function doPlanComplexity(s) {
|
|
127
|
+
let test = s.getTest(8 /* Complexity */);
|
|
128
|
+
console.log("TODO - Calculating plan complexity ...");
|
|
129
|
+
return test;
|
|
130
|
+
}
|
|
131
|
+
exports.doPlanComplexity = doPlanComplexity;
|
|
132
|
+
//# sourceMappingURL=cohesive.js.map
|