@dra2020/district-analytics 2.0.7 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +91091 -267
- package/dist/cli.js.map +1 -1
- package/dist/district-analytics.js +448 -167
- package/dist/district-analytics.js.map +1 -1
- package/dist/src/_api.d.ts +8 -2
- package/dist/src/results.d.ts +22 -1
- package/dist/src/score.d.ts +4 -0
- package/dist/src/types.d.ts +5 -5
- package/dist/src/utils.d.ts +0 -1
- package/package.json +2 -1
|
@@ -118,6 +118,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
118
118
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
119
119
|
const preprocess_1 = __webpack_require__(/*! ./preprocess */ "./src/preprocess.ts");
|
|
120
120
|
const analyze_1 = __webpack_require__(/*! ./analyze */ "./src/analyze.ts");
|
|
121
|
+
const score_1 = __webpack_require__(/*! ./score */ "./src/score.ts");
|
|
121
122
|
const results_1 = __webpack_require__(/*! ./results */ "./src/results.ts");
|
|
122
123
|
const results_2 = __webpack_require__(/*! ./results */ "./src/results.ts");
|
|
123
124
|
const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
@@ -173,6 +174,11 @@ class AnalyticsSession {
|
|
|
173
174
|
preprocess_1.doPreprocessData(this, bLog);
|
|
174
175
|
analyze_1.doAnalyzeDistricts(this, bLog);
|
|
175
176
|
analyze_1.doAnalyzePlan(this, bLog);
|
|
177
|
+
// TODO - SCORE
|
|
178
|
+
this._profile = score_1.profilePlan(this, bLog);
|
|
179
|
+
// this._profile = Score.sampleProfile;
|
|
180
|
+
this._scorecard = score_1.scorePlan(this._profile, bLog);
|
|
181
|
+
// TODO - SCORE
|
|
176
182
|
results_1.doAnalyzePostProcessing(this, bLog);
|
|
177
183
|
}
|
|
178
184
|
catch (_a) {
|
|
@@ -182,13 +188,29 @@ class AnalyticsSession {
|
|
|
182
188
|
return true;
|
|
183
189
|
}
|
|
184
190
|
// NOTE - This assumes that analyzePlan() has been run!
|
|
185
|
-
getPlanAnalytics(bLog = false) {
|
|
186
|
-
return results_2.preparePlanAnalytics(this, bLog);
|
|
187
|
-
}
|
|
188
|
-
// NOTE - This assumes that analyzePlan() has been run!
|
|
189
191
|
getDistrictStatistics(bLog = false) {
|
|
190
192
|
return results_2.prepareDistrictStatistics(this, bLog);
|
|
191
193
|
}
|
|
194
|
+
// TODO - SCORE
|
|
195
|
+
getPlanProfile(bLog = false) {
|
|
196
|
+
return this._profile;
|
|
197
|
+
}
|
|
198
|
+
// TODO - SCORE
|
|
199
|
+
getPlanScorecard(bLog = false) {
|
|
200
|
+
return this._scorecard;
|
|
201
|
+
}
|
|
202
|
+
/* TODO - SCORE
|
|
203
|
+
// NOTE - This assumes that analyzePlan() has been run!
|
|
204
|
+
getPlanAnalytics(bLog: boolean = false): PlanAnalytics
|
|
205
|
+
{
|
|
206
|
+
return preparePlanAnalytics(this, bLog);
|
|
207
|
+
}
|
|
208
|
+
*/
|
|
209
|
+
// TODO - SCORE
|
|
210
|
+
// NOTE - This assumes that analyzePlan() has been run!
|
|
211
|
+
getRequirementsChecklist(bLog = false) {
|
|
212
|
+
return results_2.prepareRequirementsChecklist(this, bLog);
|
|
213
|
+
}
|
|
192
214
|
// NOTE - This assumes that analyzePlan() has been run!
|
|
193
215
|
getDiscontiguousDistrictFeatures(bLog = false) {
|
|
194
216
|
// Get the (possibly empty) list of discontiguous district IDs
|
|
@@ -346,7 +368,7 @@ class Districts {
|
|
|
346
368
|
nMissingDataset = 0;
|
|
347
369
|
nMissingProperty = 0;
|
|
348
370
|
// Compute these once per recalc cycle
|
|
349
|
-
let targetSize = this._session.state.totalPop / this._session.state.nDistricts;
|
|
371
|
+
let targetSize = Math.round(this._session.state.totalPop / this._session.state.nDistricts);
|
|
350
372
|
let deviationThreshold = this._session.populationDeviationThreshold();
|
|
351
373
|
let planByDistrict = this._session.plan.byDistrictID();
|
|
352
374
|
let plan = this._session.plan;
|
|
@@ -398,6 +420,7 @@ class Districts {
|
|
|
398
420
|
let outerThis = this;
|
|
399
421
|
// Default the pop dev % for the dummy Unassigned district to 0%.
|
|
400
422
|
// Default the pop dev % for real (1–N) but empty districts to 100%.
|
|
423
|
+
// TODO - SCORE
|
|
401
424
|
let popDevPct = (i > 0) ? (targetSize / targetSize) : 0 / targetSize;
|
|
402
425
|
// Get the geoIDs assigned to the district
|
|
403
426
|
// Guard against empty districts
|
|
@@ -424,6 +447,7 @@ class Districts {
|
|
|
424
447
|
// NOTE - SPLITTING
|
|
425
448
|
// Total population by counties w/in a district,
|
|
426
449
|
// except the dummy unassigned district 0
|
|
450
|
+
// TODO - VFEATURE
|
|
427
451
|
if (i > 0)
|
|
428
452
|
countySplits[outerThis.getCountyIndex(geoID)] += featurePop;
|
|
429
453
|
// Democratic and Republican vote totals
|
|
@@ -448,7 +472,7 @@ class Districts {
|
|
|
448
472
|
if (i > 0) {
|
|
449
473
|
if (totalPop > 0) {
|
|
450
474
|
popDevPct = (totalPop - targetSize) / targetSize;
|
|
451
|
-
bEqualPop = (popDevPct <= deviationThreshold);
|
|
475
|
+
bEqualPop = (Math.abs(popDevPct) <= deviationThreshold);
|
|
452
476
|
}
|
|
453
477
|
}
|
|
454
478
|
// Total two-party (not total total!) votes, Democratic and Republican vote
|
|
@@ -597,8 +621,10 @@ class Districts {
|
|
|
597
621
|
compact_1.extractDistrictProperties(this._session, bLog);
|
|
598
622
|
}
|
|
599
623
|
getCountyIndex(geoID) {
|
|
600
|
-
|
|
601
|
-
let countyFIPS = U.
|
|
624
|
+
// TODO - VFEATURE
|
|
625
|
+
let countyFIPS = U.parseGeoID(geoID)['county'];
|
|
626
|
+
// let countyGeoID = U.parseGeoID(geoID)['county'] as string;
|
|
627
|
+
// let countyFIPS = U.getFIPSFromCountyGeoID(countyGeoID);
|
|
602
628
|
let countyIndex = this._session.counties.indexFromFIPS(countyFIPS);
|
|
603
629
|
return countyIndex;
|
|
604
630
|
}
|
|
@@ -615,8 +641,9 @@ class Features {
|
|
|
615
641
|
nFeatures() { return this._data.features.length; }
|
|
616
642
|
featureByIndex(i) { return this._data.features[i]; }
|
|
617
643
|
geoIDForFeature(f) {
|
|
644
|
+
// TODO - 2020
|
|
618
645
|
// GEOIDs will be one of these properties
|
|
619
|
-
|
|
646
|
+
const value = f.properties['GEOID10'] || f.properties['GEOID20'] || f.properties['GEOID'];
|
|
620
647
|
return value;
|
|
621
648
|
}
|
|
622
649
|
fieldForFeature(f, dt, fk) {
|
|
@@ -1397,6 +1424,7 @@ function doPolsbyPopper(s, bLog = false) {
|
|
|
1397
1424
|
}
|
|
1398
1425
|
exports.doPolsbyPopper = doPolsbyPopper;
|
|
1399
1426
|
// HELPER TO EXTRACT PROPERTIES OF DISTRICT SHAPES
|
|
1427
|
+
// TODO - SCORE: Create an array, as opposed to a dict
|
|
1400
1428
|
function extractDistrictProperties(s, bLog = false) {
|
|
1401
1429
|
// NOTE - I am assuming that district IDs are integers 1–N
|
|
1402
1430
|
for (let i = 1; i <= s.state.nDistricts; i++) {
|
|
@@ -1880,8 +1908,10 @@ function doPreprocessCensus(s, bLog = false) {
|
|
|
1880
1908
|
// Sum total population across the state
|
|
1881
1909
|
s.state.totalPop += value;
|
|
1882
1910
|
// Get the county FIPS code for the feature
|
|
1883
|
-
|
|
1884
|
-
let countyFIPS = U.
|
|
1911
|
+
// TODO - VFEATURE
|
|
1912
|
+
let countyFIPS = U.parseGeoID(geoID)['county'];
|
|
1913
|
+
// let county = U.parseGeoID(geoID)['county'] as string;
|
|
1914
|
+
// let countyFIPS = U.getFIPSFromCountyGeoID(county);
|
|
1885
1915
|
// If a subtotal for the county doesn't exist, initialize one
|
|
1886
1916
|
if (!(U.keyExists(countyFIPS, totalByCounty))) {
|
|
1887
1917
|
totalByCounty[countyFIPS] = 0;
|
|
@@ -1899,7 +1929,7 @@ function doPreprocessCensus(s, bLog = false) {
|
|
|
1899
1929
|
let fipsCodes = U.getObjectKeys(totalByCounty);
|
|
1900
1930
|
// Sort the results
|
|
1901
1931
|
fipsCodes = fipsCodes.sort();
|
|
1902
|
-
//
|
|
1932
|
+
// TODO - SCORE: This was added for SPLITTING
|
|
1903
1933
|
// Add a dummy county, for county-district splitting analysis
|
|
1904
1934
|
fipsCodes.unshift('000');
|
|
1905
1935
|
// Create the ID-ordinal map
|
|
@@ -1986,7 +2016,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
1986
2016
|
};
|
|
1987
2017
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1988
2018
|
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
1989
|
-
const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"));
|
|
1990
2019
|
const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
1991
2020
|
const analyze_1 = __webpack_require__(/*! ./analyze */ "./src/analyze.ts");
|
|
1992
2021
|
const state_reqs_json_1 = __importDefault(__webpack_require__(/*! ../static/state-reqs.json */ "./static/state-reqs.json"));
|
|
@@ -2058,7 +2087,7 @@ let samplePartisan = {
|
|
|
2058
2087
|
planScore: "https://planscore.org/plan.html?20180219T202039.596761160Z"
|
|
2059
2088
|
}
|
|
2060
2089
|
};
|
|
2061
|
-
// TODO -
|
|
2090
|
+
// TODO - SCORE: Add a minority report
|
|
2062
2091
|
let sampleMinority = {
|
|
2063
2092
|
score: null,
|
|
2064
2093
|
metrics: {
|
|
@@ -2093,7 +2122,233 @@ exports.samplePlanAnalytics = {
|
|
|
2093
2122
|
partisan: samplePartisan,
|
|
2094
2123
|
minority: sampleMinority
|
|
2095
2124
|
};
|
|
2096
|
-
|
|
2125
|
+
/* TODO - SCORE
|
|
2126
|
+
export function preparePlanAnalytics(s: AnalyticsSession, bLog: boolean = false): PlanAnalytics
|
|
2127
|
+
{
|
|
2128
|
+
if (!(s.bPostProcessingDone))
|
|
2129
|
+
{
|
|
2130
|
+
doAnalyzePostProcessing(s);
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2133
|
+
// REQUIREMENTS CATEGORY
|
|
2134
|
+
let paRequirements: RequirementsCategory;
|
|
2135
|
+
|
|
2136
|
+
{
|
|
2137
|
+
let completeTest = s.getTest(T.Test.Complete) as T.TestEntry;
|
|
2138
|
+
let contiguousTest = s.getTest(T.Test.Contiguous) as T.TestEntry;
|
|
2139
|
+
let freeOfHolesTest = s.getTest(T.Test.FreeOfHoles) as T.TestEntry;
|
|
2140
|
+
let equalPopulationTest = s.getTest(T.Test.EqualPopulation) as T.TestEntry;
|
|
2141
|
+
|
|
2142
|
+
// Combine individual checks into an overall score
|
|
2143
|
+
|
|
2144
|
+
// TODO - DASHBOARD: Until we add three-state support top to bottom in
|
|
2145
|
+
// requirements/validations, map booleans to tri-states here.
|
|
2146
|
+
let completeMetric = U.mapBooleanToTriState(completeTest['score'] as boolean);
|
|
2147
|
+
let contiguousMetric = U.mapBooleanToTriState(contiguousTest['score'] as boolean);
|
|
2148
|
+
let freeOfHolesMetric = U.mapBooleanToTriState(freeOfHolesTest['score'] as boolean);
|
|
2149
|
+
let equalPopulationMetric = U.mapBooleanToTriState(equalPopulationTest['score'] as boolean);
|
|
2150
|
+
|
|
2151
|
+
let reqScore: T.TriState = T.TriState.Green;
|
|
2152
|
+
let checks = [completeMetric, contiguousMetric, freeOfHolesMetric, equalPopulationMetric];
|
|
2153
|
+
if (checks.includes(T.TriState.Yellow)) reqScore = T.TriState.Yellow;
|
|
2154
|
+
if (checks.includes(T.TriState.Red)) reqScore = T.TriState.Red;
|
|
2155
|
+
|
|
2156
|
+
// Get values to support details entries
|
|
2157
|
+
let unassignedFeaturesDetail = U.deepCopy(completeTest['details']['unassignedFeatures']) || [];
|
|
2158
|
+
let emptyDistrictsDetail = U.deepCopy(completeTest['details']['emptyDistricts']) || [];
|
|
2159
|
+
let discontiguousDistrictsDetail = U.deepCopy(contiguousTest['details']['discontiguousDistricts']) || [];
|
|
2160
|
+
let embeddedDistrictsDetail = U.deepCopy(freeOfHolesTest['details']['embeddedDistricts']) || [];
|
|
2161
|
+
|
|
2162
|
+
let populationDeviationDetail = U.deepCopy(equalPopulationTest['details']['deviation']);
|
|
2163
|
+
let deviationThresholdDetail = U.trim(s.populationDeviationThreshold());
|
|
2164
|
+
|
|
2165
|
+
let xx: string = s.state.xx;
|
|
2166
|
+
// TODO - JSON: Is there a better / easier way to work with the variable?
|
|
2167
|
+
let stateReqsDict: T.Dict = allStateReqs;
|
|
2168
|
+
let reqLinkToStateReqs: string = stateReqsDict[xx];
|
|
2169
|
+
|
|
2170
|
+
// Populate the category
|
|
2171
|
+
paRequirements = {
|
|
2172
|
+
score: reqScore,
|
|
2173
|
+
metrics: {
|
|
2174
|
+
complete: completeMetric,
|
|
2175
|
+
contiguous: contiguousMetric,
|
|
2176
|
+
freeOfHoles: freeOfHolesMetric,
|
|
2177
|
+
equalPopulation: equalPopulationMetric
|
|
2178
|
+
},
|
|
2179
|
+
details: {
|
|
2180
|
+
unassignedFeatures: unassignedFeaturesDetail,
|
|
2181
|
+
emptyDistricts: emptyDistrictsDetail,
|
|
2182
|
+
discontiguousDistricts: discontiguousDistrictsDetail,
|
|
2183
|
+
embeddedDistricts: embeddedDistrictsDetail,
|
|
2184
|
+
populationDeviation: populationDeviationDetail,
|
|
2185
|
+
deviationThreshold: deviationThresholdDetail
|
|
2186
|
+
},
|
|
2187
|
+
datasets: {
|
|
2188
|
+
census: U.deepCopy(s.config['descriptions']['CENSUS']),
|
|
2189
|
+
},
|
|
2190
|
+
resources: {
|
|
2191
|
+
stateReqs: reqLinkToStateReqs
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2196
|
+
// COMPACTNESS CATEGORY
|
|
2197
|
+
let paCompactness: CompactnessCategory;
|
|
2198
|
+
{
|
|
2199
|
+
let reockWeight = 0.5;
|
|
2200
|
+
let polsbyWeight = 1.0 - reockWeight;
|
|
2201
|
+
|
|
2202
|
+
let reockTest = s.getTest(T.Test.Reock) as T.TestEntry;
|
|
2203
|
+
let polsbyTest = s.getTest(T.Test.PolsbyPopper) as T.TestEntry;
|
|
2204
|
+
|
|
2205
|
+
let normalizedReock = reockTest['normalizedScore'] as number;
|
|
2206
|
+
let normalizedPolsby = reockTest['normalizedScore'] as number;
|
|
2207
|
+
let compactnessScore = U.trim((reockWeight * normalizedReock) + (polsbyWeight * normalizedPolsby), 0);
|
|
2208
|
+
|
|
2209
|
+
let reockMetric = U.deepCopy(reockTest['score'] as number);
|
|
2210
|
+
let polsbyMetric = U.deepCopy(polsbyTest['score'] as number);
|
|
2211
|
+
|
|
2212
|
+
// Populate the category
|
|
2213
|
+
paCompactness = {
|
|
2214
|
+
score: compactnessScore,
|
|
2215
|
+
metrics: {
|
|
2216
|
+
reock: reockMetric,
|
|
2217
|
+
polsby: polsbyMetric
|
|
2218
|
+
},
|
|
2219
|
+
details: {
|
|
2220
|
+
// None at this time
|
|
2221
|
+
},
|
|
2222
|
+
datasets: {
|
|
2223
|
+
// NOTE - DATASETS
|
|
2224
|
+
shapes: U.deepCopy(s.config['descriptions']['SHAPES'])
|
|
2225
|
+
// shapes: "2010 VTD shapes"
|
|
2226
|
+
},
|
|
2227
|
+
resources: {
|
|
2228
|
+
// None at this time
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
|
|
2233
|
+
// SPLITTING CATEGORY
|
|
2234
|
+
|
|
2235
|
+
let paSplitting: SplittingCategory
|
|
2236
|
+
|
|
2237
|
+
{
|
|
2238
|
+
let unexpectedCountySplittingTest = s.getTest(T.Test.UnexpectedCountySplits) as T.TestEntry;
|
|
2239
|
+
let VTDSplitsTest = s.getTest(T.Test.VTDSplits) as T.TestEntry;
|
|
2240
|
+
let countySplittingTest = s.getTest(T.Test.CountySplitting) as T.TestEntry;
|
|
2241
|
+
let districtSplittingTest = s.getTest(T.Test.DistrictSplitting) as T.TestEntry;
|
|
2242
|
+
|
|
2243
|
+
let unexpectedAffectedMetric = U.deepCopy(unexpectedCountySplittingTest['score']);
|
|
2244
|
+
let countiesSplitUnexpectedlyDetail = U.deepCopy(unexpectedCountySplittingTest['details']['countiesSplitUnexpectedly']);
|
|
2245
|
+
|
|
2246
|
+
let nVTDSplitsMetric = U.deepCopy(VTDSplitsTest['score']);
|
|
2247
|
+
let splitVTDsDetail = U.deepCopy(VTDSplitsTest['details']['splitVTDs']);
|
|
2248
|
+
|
|
2249
|
+
let SqEnt_DCreducedMetric = U.deepCopy(countySplittingTest['score']);
|
|
2250
|
+
let SqEnt_CDreducedMetric = U.deepCopy(districtSplittingTest['score']);
|
|
2251
|
+
|
|
2252
|
+
let countySplittingNormalized = countySplittingTest['normalizedScore'] as number;
|
|
2253
|
+
let districtSplittingNormalized = districtSplittingTest['normalizedScore'] as number;
|
|
2254
|
+
let splittingScore = U.trim((S.COUNTY_SPLITTING_WEIGHT * countySplittingNormalized) +
|
|
2255
|
+
+ (S.DISTRICT_SPLITTING_WEIGHT * districtSplittingNormalized), 0);
|
|
2256
|
+
|
|
2257
|
+
paSplitting = {
|
|
2258
|
+
score: splittingScore,
|
|
2259
|
+
metrics: {
|
|
2260
|
+
sqEnt_DCreduced: SqEnt_DCreducedMetric,
|
|
2261
|
+
sqEnt_CDreduced: SqEnt_CDreducedMetric
|
|
2262
|
+
// NOTE - The un-reduced raw values
|
|
2263
|
+
// sqEnt_DC : SqEnt_DCMetric,
|
|
2264
|
+
// sqEnt_CD : SqEnt_CDMetric
|
|
2265
|
+
},
|
|
2266
|
+
details: {
|
|
2267
|
+
countiesSplitUnexpectedly: countiesSplitUnexpectedlyDetail,
|
|
2268
|
+
unexpectedAffected: unexpectedAffectedMetric,
|
|
2269
|
+
nSplitVTDs: nVTDSplitsMetric,
|
|
2270
|
+
splitVTDs: splitVTDsDetail
|
|
2271
|
+
},
|
|
2272
|
+
datasets: {
|
|
2273
|
+
// None at this time
|
|
2274
|
+
},
|
|
2275
|
+
resources: {
|
|
2276
|
+
// None at this time
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2281
|
+
// PARTISAN CATEGORY
|
|
2282
|
+
//
|
|
2283
|
+
// TODO - PARTISAN: This category is still being fleshed out. Just an example below.
|
|
2284
|
+
let paPartisan: PartisanCategory;
|
|
2285
|
+
|
|
2286
|
+
{
|
|
2287
|
+
paPartisan = {
|
|
2288
|
+
score: 100,
|
|
2289
|
+
metrics: {
|
|
2290
|
+
partisanBias: 0.15,
|
|
2291
|
+
responsiveness: 2.0
|
|
2292
|
+
},
|
|
2293
|
+
details: {},
|
|
2294
|
+
datasets: {
|
|
2295
|
+
election: "2016 Presidential, US Senate, Governor, and AG election results"
|
|
2296
|
+
},
|
|
2297
|
+
resources: {
|
|
2298
|
+
planScore: "https://planscore.org/plan.html?20180219T202039.596761160Z"
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
// MINORITY CATEGORY
|
|
2304
|
+
//
|
|
2305
|
+
// TODO - MINORITY: This category is still being fleshed out. Just an example below.
|
|
2306
|
+
let paMinority: MinorityCategory;
|
|
2307
|
+
|
|
2308
|
+
{
|
|
2309
|
+
paMinority = {
|
|
2310
|
+
score: null,
|
|
2311
|
+
metrics: {
|
|
2312
|
+
nBlack37to50: 1,
|
|
2313
|
+
nBlackMajority: 12,
|
|
2314
|
+
nHispanic37to50: 0,
|
|
2315
|
+
nHispanicMajority: 0,
|
|
2316
|
+
nPacific37to50: 0,
|
|
2317
|
+
nPacificMajority: 0,
|
|
2318
|
+
nAsian37to50: 0,
|
|
2319
|
+
nAsianMajority: 0,
|
|
2320
|
+
nNative37to50: 0,
|
|
2321
|
+
nNativeMajority: 0,
|
|
2322
|
+
nMinority37to50: 0,
|
|
2323
|
+
nMinorityMajority: 0,
|
|
2324
|
+
|
|
2325
|
+
averageDVoteShare: 0.90
|
|
2326
|
+
},
|
|
2327
|
+
details: {
|
|
2328
|
+
vap: true,
|
|
2329
|
+
comboCategories: true
|
|
2330
|
+
},
|
|
2331
|
+
datasets: {
|
|
2332
|
+
vap: "2010 Voting Age Population"
|
|
2333
|
+
},
|
|
2334
|
+
resources: {}
|
|
2335
|
+
}
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2338
|
+
// PLAN ANALYTICS
|
|
2339
|
+
let pa: PlanAnalytics = {
|
|
2340
|
+
requirements: paRequirements,
|
|
2341
|
+
compactness: paCompactness,
|
|
2342
|
+
// TODO - Not implemented yet
|
|
2343
|
+
splitting: paSplitting,
|
|
2344
|
+
partisan: paPartisan,
|
|
2345
|
+
minority: paMinority
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2348
|
+
return pa;
|
|
2349
|
+
}
|
|
2350
|
+
*/
|
|
2351
|
+
function prepareRequirementsChecklist(s, bLog = false) {
|
|
2097
2352
|
if (!(s.bPostProcessingDone)) {
|
|
2098
2353
|
doAnalyzePostProcessing(s);
|
|
2099
2354
|
}
|
|
@@ -2153,142 +2408,9 @@ function preparePlanAnalytics(s, bLog = false) {
|
|
|
2153
2408
|
}
|
|
2154
2409
|
};
|
|
2155
2410
|
}
|
|
2156
|
-
|
|
2157
|
-
let paCompactness;
|
|
2158
|
-
{
|
|
2159
|
-
let reockWeight = 0.5;
|
|
2160
|
-
let polsbyWeight = 1.0 - reockWeight;
|
|
2161
|
-
let reockTest = s.getTest(5 /* Reock */);
|
|
2162
|
-
let polsbyTest = s.getTest(6 /* PolsbyPopper */);
|
|
2163
|
-
let normalizedReock = reockTest['normalizedScore'];
|
|
2164
|
-
let normalizedPolsby = reockTest['normalizedScore'];
|
|
2165
|
-
let compactnessScore = U.trim((reockWeight * normalizedReock) + (polsbyWeight * normalizedPolsby), 0);
|
|
2166
|
-
let reockMetric = U.deepCopy(reockTest['score']);
|
|
2167
|
-
let polsbyMetric = U.deepCopy(polsbyTest['score']);
|
|
2168
|
-
// Populate the category
|
|
2169
|
-
paCompactness = {
|
|
2170
|
-
score: compactnessScore,
|
|
2171
|
-
metrics: {
|
|
2172
|
-
reock: reockMetric,
|
|
2173
|
-
polsby: polsbyMetric
|
|
2174
|
-
},
|
|
2175
|
-
details: {
|
|
2176
|
-
// None at this time
|
|
2177
|
-
},
|
|
2178
|
-
datasets: {
|
|
2179
|
-
// NOTE - DATASETS
|
|
2180
|
-
shapes: U.deepCopy(s.config['descriptions']['SHAPES'])
|
|
2181
|
-
// shapes: "2010 VTD shapes"
|
|
2182
|
-
},
|
|
2183
|
-
resources: {
|
|
2184
|
-
// None at this time
|
|
2185
|
-
}
|
|
2186
|
-
};
|
|
2187
|
-
}
|
|
2188
|
-
// SPLITTING CATEGORY
|
|
2189
|
-
let paSplitting;
|
|
2190
|
-
{
|
|
2191
|
-
let unexpectedCountySplittingTest = s.getTest(7 /* UnexpectedCountySplits */);
|
|
2192
|
-
let VTDSplitsTest = s.getTest(10 /* VTDSplits */);
|
|
2193
|
-
let countySplittingTest = s.getTest(8 /* CountySplitting */);
|
|
2194
|
-
let districtSplittingTest = s.getTest(9 /* DistrictSplitting */);
|
|
2195
|
-
let unexpectedAffectedMetric = U.deepCopy(unexpectedCountySplittingTest['score']);
|
|
2196
|
-
let countiesSplitUnexpectedlyDetail = U.deepCopy(unexpectedCountySplittingTest['details']['countiesSplitUnexpectedly']);
|
|
2197
|
-
let nVTDSplitsMetric = U.deepCopy(VTDSplitsTest['score']);
|
|
2198
|
-
let splitVTDsDetail = U.deepCopy(VTDSplitsTest['details']['splitVTDs']);
|
|
2199
|
-
let SqEnt_DCreducedMetric = U.deepCopy(countySplittingTest['score']);
|
|
2200
|
-
let SqEnt_CDreducedMetric = U.deepCopy(districtSplittingTest['score']);
|
|
2201
|
-
let countySplittingNormalized = countySplittingTest['normalizedScore'];
|
|
2202
|
-
let districtSplittingNormalized = districtSplittingTest['normalizedScore'];
|
|
2203
|
-
let splittingScore = U.trim((S.COUNTY_SPLITTING_WEIGHT * countySplittingNormalized) +
|
|
2204
|
-
+(S.DISTRICT_SPLITTING_WEIGHT * districtSplittingNormalized), 0);
|
|
2205
|
-
paSplitting = {
|
|
2206
|
-
score: splittingScore,
|
|
2207
|
-
metrics: {
|
|
2208
|
-
sqEnt_DCreduced: SqEnt_DCreducedMetric,
|
|
2209
|
-
sqEnt_CDreduced: SqEnt_CDreducedMetric
|
|
2210
|
-
// NOTE - The un-reduced raw values
|
|
2211
|
-
// sqEnt_DC : SqEnt_DCMetric,
|
|
2212
|
-
// sqEnt_CD : SqEnt_CDMetric
|
|
2213
|
-
},
|
|
2214
|
-
details: {
|
|
2215
|
-
countiesSplitUnexpectedly: countiesSplitUnexpectedlyDetail,
|
|
2216
|
-
unexpectedAffected: unexpectedAffectedMetric,
|
|
2217
|
-
nSplitVTDs: nVTDSplitsMetric,
|
|
2218
|
-
splitVTDs: splitVTDsDetail
|
|
2219
|
-
},
|
|
2220
|
-
datasets: {
|
|
2221
|
-
// None at this time
|
|
2222
|
-
},
|
|
2223
|
-
resources: {
|
|
2224
|
-
// None at this time
|
|
2225
|
-
}
|
|
2226
|
-
};
|
|
2227
|
-
}
|
|
2228
|
-
// PARTISAN CATEGORY
|
|
2229
|
-
//
|
|
2230
|
-
// TODO - PARTISAN: This category is still being fleshed out. Just an example below.
|
|
2231
|
-
let paPartisan;
|
|
2232
|
-
{
|
|
2233
|
-
paPartisan = {
|
|
2234
|
-
score: 100,
|
|
2235
|
-
metrics: {
|
|
2236
|
-
partisanBias: 0.15,
|
|
2237
|
-
responsiveness: 2.0
|
|
2238
|
-
},
|
|
2239
|
-
details: {},
|
|
2240
|
-
datasets: {
|
|
2241
|
-
election: "2016 Presidential, US Senate, Governor, and AG election results"
|
|
2242
|
-
},
|
|
2243
|
-
resources: {
|
|
2244
|
-
planScore: "https://planscore.org/plan.html?20180219T202039.596761160Z"
|
|
2245
|
-
}
|
|
2246
|
-
};
|
|
2247
|
-
}
|
|
2248
|
-
// MINORITY CATEGORY
|
|
2249
|
-
//
|
|
2250
|
-
// TODO - MINORITY: This category is still being fleshed out. Just an example below.
|
|
2251
|
-
let paMinority;
|
|
2252
|
-
{
|
|
2253
|
-
paMinority = {
|
|
2254
|
-
score: null,
|
|
2255
|
-
metrics: {
|
|
2256
|
-
nBlack37to50: 1,
|
|
2257
|
-
nBlackMajority: 12,
|
|
2258
|
-
nHispanic37to50: 0,
|
|
2259
|
-
nHispanicMajority: 0,
|
|
2260
|
-
nPacific37to50: 0,
|
|
2261
|
-
nPacificMajority: 0,
|
|
2262
|
-
nAsian37to50: 0,
|
|
2263
|
-
nAsianMajority: 0,
|
|
2264
|
-
nNative37to50: 0,
|
|
2265
|
-
nNativeMajority: 0,
|
|
2266
|
-
nMinority37to50: 0,
|
|
2267
|
-
nMinorityMajority: 0,
|
|
2268
|
-
averageDVoteShare: 0.90
|
|
2269
|
-
},
|
|
2270
|
-
details: {
|
|
2271
|
-
vap: true,
|
|
2272
|
-
comboCategories: true
|
|
2273
|
-
},
|
|
2274
|
-
datasets: {
|
|
2275
|
-
vap: "2010 Voting Age Population"
|
|
2276
|
-
},
|
|
2277
|
-
resources: {}
|
|
2278
|
-
};
|
|
2279
|
-
}
|
|
2280
|
-
// PLAN ANALYTICS
|
|
2281
|
-
let pa = {
|
|
2282
|
-
requirements: paRequirements,
|
|
2283
|
-
compactness: paCompactness,
|
|
2284
|
-
// TODO - Not implemented yet
|
|
2285
|
-
splitting: paSplitting,
|
|
2286
|
-
partisan: paPartisan,
|
|
2287
|
-
minority: paMinority
|
|
2288
|
-
};
|
|
2289
|
-
return pa;
|
|
2411
|
+
return paRequirements;
|
|
2290
2412
|
}
|
|
2291
|
-
exports.
|
|
2413
|
+
exports.prepareRequirementsChecklist = prepareRequirementsChecklist;
|
|
2292
2414
|
// Example
|
|
2293
2415
|
exports.sampleDistrictStatistics = {
|
|
2294
2416
|
table: [
|
|
@@ -2544,6 +2666,127 @@ function doAnalyzePostProcessing(s, bLog = false) {
|
|
|
2544
2666
|
exports.doAnalyzePostProcessing = doAnalyzePostProcessing;
|
|
2545
2667
|
|
|
2546
2668
|
|
|
2669
|
+
/***/ }),
|
|
2670
|
+
|
|
2671
|
+
/***/ "./src/score.ts":
|
|
2672
|
+
/*!**********************!*\
|
|
2673
|
+
!*** ./src/score.ts ***!
|
|
2674
|
+
\**********************/
|
|
2675
|
+
/*! no static exports found */
|
|
2676
|
+
/***/ (function(module, exports, __webpack_require__) {
|
|
2677
|
+
|
|
2678
|
+
"use strict";
|
|
2679
|
+
|
|
2680
|
+
//
|
|
2681
|
+
// SCORING
|
|
2682
|
+
//
|
|
2683
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
2684
|
+
if (mod && mod.__esModule) return mod;
|
|
2685
|
+
var result = {};
|
|
2686
|
+
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
2687
|
+
result["default"] = mod;
|
|
2688
|
+
return result;
|
|
2689
|
+
};
|
|
2690
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
2691
|
+
const Score = __importStar(__webpack_require__(/*! @dra2020/dra-score */ "@dra2020/dra-score"));
|
|
2692
|
+
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
2693
|
+
const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
2694
|
+
// PROFILE A PLAN
|
|
2695
|
+
function profilePlan(s, bLog = false) {
|
|
2696
|
+
const state = s.state.xx;
|
|
2697
|
+
const planName = s.title;
|
|
2698
|
+
const nDistricts = s.state.nDistricts;
|
|
2699
|
+
const nCounties = s.counties.nCounties;
|
|
2700
|
+
const targetSize = Math.round(s.state.totalPop / nDistricts);
|
|
2701
|
+
const popByDistrict = U.deepCopy(s.districts.statistics[D.DistrictField.TotalPop].slice(1, -1));
|
|
2702
|
+
const geoPropsByDistrict = makeArrayOfGeoProps(s, bLog);
|
|
2703
|
+
const splits = makeNakedCxD(s);
|
|
2704
|
+
const summaryRow = s.districts.numberOfRows() - 1;
|
|
2705
|
+
const statewideVf = s.districts.statistics[D.DistrictField.DemPct][summaryRow];
|
|
2706
|
+
const vpiArray = U.deepCopy(s.districts.statistics[D.DistrictField.DemPct].slice(1, -1));
|
|
2707
|
+
const demographicsByDistrict = makeArrayOfDemographics(s);
|
|
2708
|
+
const profile = {
|
|
2709
|
+
state: state,
|
|
2710
|
+
planName: planName,
|
|
2711
|
+
nDistricts: nDistricts,
|
|
2712
|
+
nCounties: nCounties,
|
|
2713
|
+
legislativeDistricts: s.legislativeDistricts,
|
|
2714
|
+
populationProfile: {
|
|
2715
|
+
TotalPopByDistrict: popByDistrict,
|
|
2716
|
+
targetSize: targetSize
|
|
2717
|
+
},
|
|
2718
|
+
compactnessProfile: {
|
|
2719
|
+
GeometryByDistrict: geoPropsByDistrict
|
|
2720
|
+
},
|
|
2721
|
+
splittingProfile: {
|
|
2722
|
+
CountyPopByDistrict: splits
|
|
2723
|
+
},
|
|
2724
|
+
partisanProfile: {
|
|
2725
|
+
statewideVf: statewideVf,
|
|
2726
|
+
VfArray: vpiArray
|
|
2727
|
+
},
|
|
2728
|
+
demographicProfile: {
|
|
2729
|
+
DemographicsByDistrict: demographicsByDistrict
|
|
2730
|
+
}
|
|
2731
|
+
};
|
|
2732
|
+
return profile;
|
|
2733
|
+
}
|
|
2734
|
+
exports.profilePlan = profilePlan;
|
|
2735
|
+
// NOTE - The CxD splits structure from _data.ts includes dummy districts for
|
|
2736
|
+
// unassigned precincts & state summary and an extra 0 county. But dra-score takes
|
|
2737
|
+
// a simple 1–D x 1–C splits array (zero-based, of course).
|
|
2738
|
+
function makeNakedCxD(s, bLog = false) {
|
|
2739
|
+
const adornedCxD = s.districts.statistics[D.DistrictField.CountySplits];
|
|
2740
|
+
let CxD = [];
|
|
2741
|
+
// Remove the unassigned & total dummy "districts"
|
|
2742
|
+
for (let districtID = 1; districtID <= s.state.nDistricts; districtID++) {
|
|
2743
|
+
// TODO - SCORE: OH has an extra county!?!
|
|
2744
|
+
const splits = U.deepCopy(adornedCxD[districtID].slice(1));
|
|
2745
|
+
CxD.push(splits);
|
|
2746
|
+
}
|
|
2747
|
+
return CxD;
|
|
2748
|
+
}
|
|
2749
|
+
// TODO - SCORE: Convert dict of geo props to array by district index
|
|
2750
|
+
function makeArrayOfGeoProps(s, bLog = false) {
|
|
2751
|
+
let geometryByDistrict = [];
|
|
2752
|
+
for (let districtID = 1; districtID <= s.state.nDistricts; districtID++) {
|
|
2753
|
+
let districtProps = s.districts.getGeoProperties(districtID);
|
|
2754
|
+
// Guard against no shape and no properties
|
|
2755
|
+
if (districtProps) {
|
|
2756
|
+
let a = districtProps[0 /* Area */];
|
|
2757
|
+
let p = districtProps[2 /* Perimeter */];
|
|
2758
|
+
let d = districtProps[1 /* Diameter */];
|
|
2759
|
+
// Save each triple
|
|
2760
|
+
geometryByDistrict.push([a, p, d]);
|
|
2761
|
+
}
|
|
2762
|
+
}
|
|
2763
|
+
return geometryByDistrict;
|
|
2764
|
+
}
|
|
2765
|
+
function makeArrayOfDemographics(s, bLog = false) {
|
|
2766
|
+
let demographicsArray = [];
|
|
2767
|
+
// Remove the unassigned & total dummy "districts"
|
|
2768
|
+
for (let districtID = 1; districtID <= s.state.nDistricts; districtID++) {
|
|
2769
|
+
const districtDemographics = [
|
|
2770
|
+
U.deepCopy(s.districts.statistics[D.DistrictField.WhitePct][districtID]),
|
|
2771
|
+
U.deepCopy(s.districts.statistics[D.DistrictField.MinorityPct][districtID]),
|
|
2772
|
+
U.deepCopy(s.districts.statistics[D.DistrictField.BlackPct][districtID]),
|
|
2773
|
+
U.deepCopy(s.districts.statistics[D.DistrictField.HispanicPct][districtID]),
|
|
2774
|
+
U.deepCopy(s.districts.statistics[D.DistrictField.PacificPct][districtID]),
|
|
2775
|
+
U.deepCopy(s.districts.statistics[D.DistrictField.AsianPct][districtID]),
|
|
2776
|
+
U.deepCopy(s.districts.statistics[D.DistrictField.NativePct][districtID])
|
|
2777
|
+
];
|
|
2778
|
+
demographicsArray.push(districtDemographics);
|
|
2779
|
+
}
|
|
2780
|
+
return demographicsArray;
|
|
2781
|
+
}
|
|
2782
|
+
// SCORE A PLAN
|
|
2783
|
+
function scorePlan(p, bLog = false) {
|
|
2784
|
+
let scorer = new Score.Scorer();
|
|
2785
|
+
return scorer.score(p);
|
|
2786
|
+
}
|
|
2787
|
+
exports.scorePlan = scorePlan;
|
|
2788
|
+
|
|
2789
|
+
|
|
2547
2790
|
/***/ }),
|
|
2548
2791
|
|
|
2549
2792
|
/***/ "./src/settings.ts":
|
|
@@ -2594,6 +2837,10 @@ exports.DISTRICT_SPLITTING_WEIGHT = 1.0 - exports.COUNTY_SPLITTING_WEIGHT;
|
|
|
2594
2837
|
// TYPE DEFINITIONS
|
|
2595
2838
|
//
|
|
2596
2839
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
2840
|
+
// TODO - SCORE
|
|
2841
|
+
var dra_score_1 = __webpack_require__(/*! @dra2020/dra-score */ "@dra2020/dra-score");
|
|
2842
|
+
exports.sampleProfile = dra_score_1.sampleProfile;
|
|
2843
|
+
exports.sampleScorecard = dra_score_1.sampleScorecard;
|
|
2597
2844
|
// END
|
|
2598
2845
|
|
|
2599
2846
|
|
|
@@ -2646,27 +2893,50 @@ function getDistrict(plan, geoID) {
|
|
|
2646
2893
|
}
|
|
2647
2894
|
exports.getDistrict = getDistrict;
|
|
2648
2895
|
// WORKING WITH GEOIDS
|
|
2896
|
+
// TODO - VFEATURE
|
|
2649
2897
|
function parseGeoID(geoID) {
|
|
2650
|
-
let
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
if (
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
}
|
|
2898
|
+
let bVfeature = false;
|
|
2899
|
+
// Rewrite vfeature GEOIDs to enable lexical parsing of higher-level parts
|
|
2900
|
+
// Template: vfeature_{geoid}_{chunk}_hash
|
|
2901
|
+
// Example: vfeature_39153153ASV_0_28e0bc2c8163e5982e1da2d61e2388a8325c575e
|
|
2902
|
+
if (geoID.indexOf('vfeature') >= 0) {
|
|
2903
|
+
bVfeature = true;
|
|
2904
|
+
// Strip off leading 'vfeature_'
|
|
2905
|
+
let parentGeoID = geoID.slice(9);
|
|
2906
|
+
// Strip off trailing goo
|
|
2907
|
+
const uPos = parentGeoID.indexOf('_');
|
|
2908
|
+
parentGeoID = parentGeoID.slice(0, uPos);
|
|
2909
|
+
geoID = parentGeoID;
|
|
2910
|
+
}
|
|
2911
|
+
const parts = {
|
|
2912
|
+
vfeature: bVfeature,
|
|
2913
|
+
state: geoID.substring(0, 2),
|
|
2914
|
+
county: geoID.substring(2, 5),
|
|
2915
|
+
rest: geoID.slice(5)
|
|
2916
|
+
};
|
|
2917
|
+
// let l: number = geoID.length;
|
|
2918
|
+
// if (l >= 11)
|
|
2919
|
+
// {
|
|
2920
|
+
// parts['tract'] = geoID.substring(0, 11);
|
|
2921
|
+
// }
|
|
2922
|
+
// if (l >= 12)
|
|
2923
|
+
// {
|
|
2924
|
+
// parts['bg'] = geoID.substring(0, 12);
|
|
2925
|
+
// }
|
|
2926
|
+
// if (l == 15)
|
|
2927
|
+
// {
|
|
2928
|
+
// parts['block'] = geoID;
|
|
2929
|
+
// }
|
|
2663
2930
|
return parts;
|
|
2664
2931
|
}
|
|
2665
2932
|
exports.parseGeoID = parseGeoID;
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2933
|
+
// TODO - VFEATURE
|
|
2934
|
+
// export function getFIPSFromCountyGeoID(geoID: string): string
|
|
2935
|
+
// {
|
|
2936
|
+
// const fips = geoID.substring(2, 5);
|
|
2937
|
+
// if (isNaN(Number(fips))) console.log("Non-numeric GEOID =", geoID);
|
|
2938
|
+
// return fips;
|
|
2939
|
+
// }
|
|
2670
2940
|
function isWaterOnly(geoID) {
|
|
2671
2941
|
let waterOnlySignature = 'ZZZZZZ';
|
|
2672
2942
|
if (geoID.indexOf(waterOnlySignature) >= 0)
|
|
@@ -3105,6 +3375,17 @@ module.exports = JSON.parse("{\"AL\":\"https://www.brennancenter.org/sites/defau
|
|
|
3105
3375
|
|
|
3106
3376
|
/***/ }),
|
|
3107
3377
|
|
|
3378
|
+
/***/ "@dra2020/dra-score":
|
|
3379
|
+
/*!*************************************!*\
|
|
3380
|
+
!*** external "@dra2020/dra-score" ***!
|
|
3381
|
+
\*************************************/
|
|
3382
|
+
/*! no static exports found */
|
|
3383
|
+
/***/ (function(module, exports) {
|
|
3384
|
+
|
|
3385
|
+
module.exports = require("@dra2020/dra-score");
|
|
3386
|
+
|
|
3387
|
+
/***/ }),
|
|
3388
|
+
|
|
3108
3389
|
/***/ "@dra2020/poly":
|
|
3109
3390
|
/*!********************************!*\
|
|
3110
3391
|
!*** external "@dra2020/poly" ***!
|