@dra2020/district-analytics 1.0.9 → 2.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 +77 -70
- package/dist/cli.js.map +1 -1
- package/dist/district-analytics.js +73 -65
- package/dist/district-analytics.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/package.json +1 -1
|
@@ -118,11 +118,6 @@ 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
|
-
// TODO - DASHBOARD: Delete
|
|
122
|
-
// import {
|
|
123
|
-
// doConfigureScales, doAnalyzePostProcessing
|
|
124
|
-
// doPrepareScorecard, doPrepareTestLog, Scorecard
|
|
125
|
-
// } from './report'
|
|
126
121
|
const results_1 = __webpack_require__(/*! ./results */ "./src/results.ts");
|
|
127
122
|
const results_2 = __webpack_require__(/*! ./results */ "./src/results.ts");
|
|
128
123
|
const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
@@ -153,7 +148,7 @@ class AnalyticsSession {
|
|
|
153
148
|
// NOTE - Session settings are required:
|
|
154
149
|
// - Analytics suites can be defaulted to all with [], but
|
|
155
150
|
// - Dataset keys must be explicitly specified with 'dataset'
|
|
156
|
-
// TODO - Remove this mechanism. Always run everything.
|
|
151
|
+
// TODO - DASHBOARD: Remove this mechanism. Always run everything.
|
|
157
152
|
let defaultSuites = [0 /* Legal */, 1 /* Fair */, 2 /* Best */];
|
|
158
153
|
// If the config passed in has no suites = [], use the default suites
|
|
159
154
|
if (U.isArrayEmpty(config['suites'])) {
|
|
@@ -333,10 +328,10 @@ class Districts {
|
|
|
333
328
|
}
|
|
334
329
|
// This is the workhorse computational routine!
|
|
335
330
|
//
|
|
336
|
-
// TODO -
|
|
337
|
-
// TODO -
|
|
331
|
+
// TODO - OPTIMIZE for getting multiple properties from the same feature?
|
|
332
|
+
// TODO - OPTIMIZE by only re-calc'ing districts that have changed?
|
|
338
333
|
// In this case, special attention to getting county-splits right.
|
|
339
|
-
// TODO -
|
|
334
|
+
// TODO - OPTIMIZE by async'ing this?
|
|
340
335
|
// TODO - Is there a way to do this programmatically off data? Does it matter?
|
|
341
336
|
recalcStatistics(bLog = false) {
|
|
342
337
|
// Compute these once per recalc cycle
|
|
@@ -345,7 +340,7 @@ class Districts {
|
|
|
345
340
|
let planByDistrict = this._session.plan.byDistrictID();
|
|
346
341
|
let plan = this._session.plan;
|
|
347
342
|
let graph = this._session.graph;
|
|
348
|
-
//
|
|
343
|
+
// NOTE - SPLITTING
|
|
349
344
|
// Add an extra 0th virtual county bucket for county-district splitting analysis
|
|
350
345
|
let nCountyBuckets = this._session.counties.nCounties + 1;
|
|
351
346
|
// INITIALIZE STATE VALUES THAT WILL BE ACCUMULATED
|
|
@@ -371,7 +366,7 @@ class Districts {
|
|
|
371
366
|
// INITIALIZE DISTRICT VALUES THAT WILL BE ACCUMULATED (VS. DERIVED)
|
|
372
367
|
let featurePop;
|
|
373
368
|
let totalPop = 0;
|
|
374
|
-
//
|
|
369
|
+
// NOTE - SPLITTING
|
|
375
370
|
let countySplits = U.initArray(nCountyBuckets, 0);
|
|
376
371
|
let demVotes = 0;
|
|
377
372
|
let repVotes = 0;
|
|
@@ -402,31 +397,37 @@ class Districts {
|
|
|
402
397
|
// Map from geoID to feature index
|
|
403
398
|
let featureID = outerThis._session.features.featureID(geoID);
|
|
404
399
|
let f = outerThis._session.features.featureByIndex(featureID);
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
400
|
+
if (f == undefined) {
|
|
401
|
+
console.log("Skipping undefined feature in district statistics: GEOID =", geoID, "Feature ID =", featureID);
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
// ACCUMULATE VALUES
|
|
405
|
+
// Total population of each feature
|
|
406
|
+
// NOTE - This result is used more than once
|
|
407
|
+
featurePop = outerThis._session.features.fieldForFeature(f, "CENSUS" /* CENSUS */, "Tot" /* TotalPop */);
|
|
408
|
+
// Total district population
|
|
409
|
+
totalPop += featurePop;
|
|
410
|
+
// NOTE - SPLITTING
|
|
411
|
+
// Total population by counties w/in a district,
|
|
412
|
+
// except the dummy unassigned district 0
|
|
413
|
+
if (i > 0)
|
|
414
|
+
countySplits[outerThis.getCountyIndex(geoID)] += featurePop;
|
|
415
|
+
// Democratic and Republican vote totals
|
|
416
|
+
demVotes += outerThis._session.features.fieldForFeature(f, "ELECTION" /* ELECTION */, "D" /* DemVotes */);
|
|
417
|
+
repVotes += outerThis._session.features.fieldForFeature(f, "ELECTION" /* ELECTION */, "R" /* RepVotes */);
|
|
418
|
+
// Voting-age demographic breakdowns (or citizen voting-age)
|
|
419
|
+
totalVAP += outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "Tot" /* TotalPop */);
|
|
420
|
+
whitePop += outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "Wh" /* WhitePop */);
|
|
421
|
+
blackPop += outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "BlC" /* BlackPop */);
|
|
422
|
+
hispanicPop += outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "His" /* HispanicPop */);
|
|
423
|
+
pacificPop += outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "PacC" /* PacificPop */);
|
|
424
|
+
asianPop += outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "AsnC" /* AsianPop */);
|
|
425
|
+
nativePop += outerThis._session.features.fieldForFeature(f, "VAP" /* VAP */, "NatC" /* NativePop */);
|
|
426
|
+
// 4 - MORE ...
|
|
427
|
+
}
|
|
428
428
|
}
|
|
429
|
-
|
|
429
|
+
else
|
|
430
|
+
console.log("Skipping water-only feature in district statistics:", geoID);
|
|
430
431
|
});
|
|
431
432
|
// COMPUTE DERIVED VALUES
|
|
432
433
|
// Population deviation % and equal population (boolean) by district.
|
|
@@ -624,6 +625,8 @@ exports.Features = Features;
|
|
|
624
625
|
// f is a direct GeoJSON feature
|
|
625
626
|
// p is a geoID
|
|
626
627
|
function _getFeatures(f, datasetKey, p) {
|
|
628
|
+
// Echo parameters for debugging
|
|
629
|
+
// console.log("f =", f, "k = ", datasetKey, "p =", p);
|
|
627
630
|
// Shim to load sample data2.json from disk for command-line scaffolding
|
|
628
631
|
if (f.properties && f.properties['datasets']) {
|
|
629
632
|
return f.properties['datasets'][datasetKey][p];
|
|
@@ -710,7 +713,7 @@ class Plan {
|
|
|
710
713
|
// return newPlan;
|
|
711
714
|
// }
|
|
712
715
|
invertPlan() {
|
|
713
|
-
//
|
|
716
|
+
// NOTE - UNASSIGNED
|
|
714
717
|
this._planByDistrictID = invertPlan(this._planByGeoID, this._session);
|
|
715
718
|
this.districtIDs = U.getNumericObjectKeys(this._planByDistrictID);
|
|
716
719
|
}
|
|
@@ -726,8 +729,8 @@ function invertPlan(plan, s) {
|
|
|
726
729
|
let invertedPlan = {};
|
|
727
730
|
// Add a dummy 'unassigned' district
|
|
728
731
|
invertedPlan[S.NOT_ASSIGNED] = new Set();
|
|
729
|
-
//
|
|
730
|
-
//
|
|
732
|
+
// NOTE - UNASSIGNED
|
|
733
|
+
// The feature assignments coming from DRA do not include unassigned ones.
|
|
731
734
|
// - In the DRA-calling context, there's an analytics session with a reference
|
|
732
735
|
// to the features. Loop over all the features to find the unassigned ones,
|
|
733
736
|
// and add them to the dummy unassigned district explicitly.
|
|
@@ -740,7 +743,7 @@ function invertPlan(plan, s) {
|
|
|
740
743
|
// to the dummy unassigned district 0.
|
|
741
744
|
if (!(U.keyExists(geoID, plan)))
|
|
742
745
|
invertedPlan[S.NOT_ASSIGNED].add(geoID);
|
|
743
|
-
//
|
|
746
|
+
// NOTE - NOT skipping WATER-ONLY features here, because we're
|
|
744
747
|
// not skipping them below when they are explicitly assigned in plans. Should
|
|
745
748
|
// we skip them in both places?
|
|
746
749
|
}
|
|
@@ -767,7 +770,13 @@ class Graph {
|
|
|
767
770
|
peerNeighbors(node) {
|
|
768
771
|
// Get the neighboring geoIDs connected to a geoID
|
|
769
772
|
// Ignore the lengths of the shared borders (the values), for now
|
|
770
|
-
|
|
773
|
+
// Protect against getting a GEOID that's not in the graph
|
|
774
|
+
if (U.keyExists(node, this._graph)) {
|
|
775
|
+
return U.getObjectKeys(this._graph[node]);
|
|
776
|
+
}
|
|
777
|
+
else
|
|
778
|
+
return [];
|
|
779
|
+
// return U.getObjectKeys(this._graph[node]);
|
|
771
780
|
}
|
|
772
781
|
}
|
|
773
782
|
exports.Graph = Graph;
|
|
@@ -1510,7 +1519,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
1510
1519
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1511
1520
|
const Poly = __importStar(__webpack_require__(/*! @dra2020/poly */ "@dra2020/poly"));
|
|
1512
1521
|
// CARTESIAN SHIMS OVER 'POLY' FUNCTIONS
|
|
1513
|
-
// TODO -
|
|
1522
|
+
// TODO - POLY: Confirm Cartesian calculations
|
|
1514
1523
|
function gfArea(poly) {
|
|
1515
1524
|
let area = _polygonArea(poly);
|
|
1516
1525
|
return area;
|
|
@@ -1545,7 +1554,7 @@ function _polygonSimpleArea(p) {
|
|
|
1545
1554
|
// Generalizes the above for MultiPolygons -- cloned from polyArea() in 'poly'
|
|
1546
1555
|
function _polygonArea(poly) {
|
|
1547
1556
|
let polyOptions = { noLatitudeCorrection: true }; // NO-OP?
|
|
1548
|
-
poly = Poly.polyNormalize(poly, polyOptions); // TODO - Discuss w/ Terry
|
|
1557
|
+
poly = Poly.polyNormalize(poly, polyOptions); // TODO - POLY: Discuss w/ Terry
|
|
1549
1558
|
let a = 0;
|
|
1550
1559
|
// A MultiPolygon is a set of polygons
|
|
1551
1560
|
for (let i = 0; poly && i < poly.length; i++) {
|
|
@@ -1558,7 +1567,7 @@ function _polygonArea(poly) {
|
|
|
1558
1567
|
}
|
|
1559
1568
|
return a;
|
|
1560
1569
|
}
|
|
1561
|
-
// TODO -
|
|
1570
|
+
// TODO - POLY: Confirm Cartesian calculations w/ Terry
|
|
1562
1571
|
// The perimeter calculation already just computes cartesian distance if you
|
|
1563
1572
|
// pass in the noLatitudeCorrection flag. You would need to divide by
|
|
1564
1573
|
// Poly.EARTH_RADIUS to go from the returned units of meters to Lat/Lon “units”.
|
|
@@ -1567,7 +1576,7 @@ function gfPerimeter(poly) {
|
|
|
1567
1576
|
return perimeter;
|
|
1568
1577
|
}
|
|
1569
1578
|
exports.gfPerimeter = gfPerimeter;
|
|
1570
|
-
// TODO -
|
|
1579
|
+
// TODO - POLY: Confirm Cartesian calculations w/ Terry
|
|
1571
1580
|
// Cloned from polyPerimeter() in 'poly' and revised to use Cartesian distance
|
|
1572
1581
|
// NOTE: No conversion of degrees to radians!
|
|
1573
1582
|
function _polygonPerimeter(poly) {
|
|
@@ -1591,7 +1600,7 @@ function _distance(x1, y1, x2, y2) {
|
|
|
1591
1600
|
d = Math.sqrt((dLat * dLat) + (dLon * dLon));
|
|
1592
1601
|
return d;
|
|
1593
1602
|
}
|
|
1594
|
-
// TODO -
|
|
1603
|
+
// TODO - POLY: Confirm Cartesian calculations w/ Terry
|
|
1595
1604
|
// As I mentioned, the polyCircle code was already just treating the coordinate
|
|
1596
1605
|
// system as Cartesian. I then did polyFromCircle to convert it to a polygon that
|
|
1597
1606
|
// then could be passed to polyArea in order to take into account the projection.
|
|
@@ -1625,6 +1634,7 @@ function __export(m) {
|
|
|
1625
1634
|
}
|
|
1626
1635
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1627
1636
|
__export(__webpack_require__(/*! ./_api */ "./src/_api.ts"));
|
|
1637
|
+
__export(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
1628
1638
|
__export(__webpack_require__(/*! ./results */ "./src/results.ts"));
|
|
1629
1639
|
__export(__webpack_require__(/*! ./types */ "./src/types.ts"));
|
|
1630
1640
|
|
|
@@ -1800,7 +1810,7 @@ function doPreprocessData(s, bLog = false) {
|
|
|
1800
1810
|
doPreprocessElection(s, bLog);
|
|
1801
1811
|
s.bOneTimeProcessingDone = true;
|
|
1802
1812
|
}
|
|
1803
|
-
//
|
|
1813
|
+
// NOTE - UNASSIGNED: Made both the planByGeoID & DistrictID are right
|
|
1804
1814
|
// Invert the plan by district ID
|
|
1805
1815
|
s.plan.invertPlan();
|
|
1806
1816
|
// Create a map of geoIDs to feature IDs
|
|
@@ -1852,7 +1862,7 @@ function doPreprocessCensus(s, bLog = false) {
|
|
|
1852
1862
|
let fipsCodes = U.getObjectKeys(totalByCounty);
|
|
1853
1863
|
// Sort the results
|
|
1854
1864
|
fipsCodes = fipsCodes.sort();
|
|
1855
|
-
//
|
|
1865
|
+
// NOTE - SPLITTING
|
|
1856
1866
|
// Add a dummy county, for county-district splitting analysis
|
|
1857
1867
|
fipsCodes.unshift('000');
|
|
1858
1868
|
// Create the ID-ordinal map
|
|
@@ -1883,7 +1893,7 @@ function doPreprocessCensus(s, bLog = false) {
|
|
|
1883
1893
|
// Loop over the counties
|
|
1884
1894
|
for (let county in fipsCodes) {
|
|
1885
1895
|
let fipsCode = fipsCodes[county];
|
|
1886
|
-
//
|
|
1896
|
+
// NOTE - SPLITTING
|
|
1887
1897
|
// Skip the dummy county
|
|
1888
1898
|
if (fipsCode == '000')
|
|
1889
1899
|
continue;
|
|
@@ -1941,8 +1951,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
1941
1951
|
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
1942
1952
|
const S = __importStar(__webpack_require__(/*! ./settings */ "./src/settings.ts"));
|
|
1943
1953
|
const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
1944
|
-
// TODO - DASHBOARD: Delete
|
|
1945
|
-
// import { doAnalyzePostProcessing } from './report'
|
|
1946
1954
|
const analyze_1 = __webpack_require__(/*! ./analyze */ "./src/analyze.ts");
|
|
1947
1955
|
const state_reqs_json_1 = __importDefault(__webpack_require__(/*! ../static/state-reqs.json */ "./static/state-reqs.json"));
|
|
1948
1956
|
// Example
|
|
@@ -1993,12 +2001,12 @@ let sampleSplitting = {
|
|
|
1993
2001
|
],
|
|
1994
2002
|
unexpectedAffected: 0.3096,
|
|
1995
2003
|
nSplitVTDs: 12,
|
|
1996
|
-
splitVTDs: []
|
|
2004
|
+
splitVTDs: ["VTD-01", "VTD-02", "VTD-03", "VTD-04", "VTD-05", "VTD-06", "VTD-07", "VTD-08", "VTD-09", "VTD-10", "VTD-11", "VTD-12"]
|
|
1997
2005
|
},
|
|
1998
2006
|
datasets: {},
|
|
1999
2007
|
resources: {}
|
|
2000
2008
|
};
|
|
2001
|
-
// TODO - This category is still being fleshed out.
|
|
2009
|
+
// TODO - PARTISAN: This category is still being fleshed out.
|
|
2002
2010
|
let samplePartisan = {
|
|
2003
2011
|
score: 100,
|
|
2004
2012
|
metrics: {
|
|
@@ -2013,7 +2021,7 @@ let samplePartisan = {
|
|
|
2013
2021
|
planScore: "https://planscore.org/plan.html?20180219T202039.596761160Z"
|
|
2014
2022
|
}
|
|
2015
2023
|
};
|
|
2016
|
-
// TODO - This category is still being fleshed out.
|
|
2024
|
+
// TODO - MINORITY: This category is still being fleshed out.
|
|
2017
2025
|
let sampleMinority = {
|
|
2018
2026
|
score: null,
|
|
2019
2027
|
metrics: {
|
|
@@ -2131,9 +2139,9 @@ function preparePlanAnalytics(s, bLog = false) {
|
|
|
2131
2139
|
// None at this time
|
|
2132
2140
|
},
|
|
2133
2141
|
datasets: {
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
// shapes:
|
|
2142
|
+
// NOTE - DATASETS
|
|
2143
|
+
shapes: U.deepCopy(s.config['descriptions']['SHAPES'])
|
|
2144
|
+
// shapes: "2010 VTD shapes"
|
|
2137
2145
|
},
|
|
2138
2146
|
resources: {
|
|
2139
2147
|
// None at this time
|
|
@@ -2384,7 +2392,7 @@ const polsbyPopperDefn = {
|
|
|
2384
2392
|
externalType: TestType.Number,
|
|
2385
2393
|
suites: [2 /* Best */]
|
|
2386
2394
|
};
|
|
2387
|
-
//
|
|
2395
|
+
// NOTE - SPLITTING
|
|
2388
2396
|
const unexpectedCountySplitsDefn = {
|
|
2389
2397
|
ID: 7 /* UnexpectedCountySplits */,
|
|
2390
2398
|
name: "Unexpected County Splits",
|
|
@@ -2392,7 +2400,7 @@ const unexpectedCountySplitsDefn = {
|
|
|
2392
2400
|
externalType: TestType.Percentage,
|
|
2393
2401
|
suites: [2 /* Best */]
|
|
2394
2402
|
};
|
|
2395
|
-
//
|
|
2403
|
+
// NOTE - SPLITTING
|
|
2396
2404
|
const VTDSplitsDefn = {
|
|
2397
2405
|
ID: 10 /* VTDSplits */,
|
|
2398
2406
|
name: "VTD Splits",
|
|
@@ -2400,7 +2408,7 @@ const VTDSplitsDefn = {
|
|
|
2400
2408
|
externalType: TestType.Number,
|
|
2401
2409
|
suites: [2 /* Best */]
|
|
2402
2410
|
};
|
|
2403
|
-
//
|
|
2411
|
+
// NOTE - SPLITTING
|
|
2404
2412
|
const countySplittingDefn = {
|
|
2405
2413
|
ID: 8 /* CountySplitting */,
|
|
2406
2414
|
name: "County Splitting",
|
|
@@ -2408,7 +2416,7 @@ const countySplittingDefn = {
|
|
|
2408
2416
|
externalType: TestType.Number,
|
|
2409
2417
|
suites: [2 /* Best */]
|
|
2410
2418
|
};
|
|
2411
|
-
//
|
|
2419
|
+
// NOTE - SPLITTING
|
|
2412
2420
|
const districtSplittingDefn = {
|
|
2413
2421
|
ID: 9 /* DistrictSplitting */,
|
|
2414
2422
|
name: "District Splitting",
|
|
@@ -2432,7 +2440,7 @@ const testDefns = {
|
|
|
2432
2440
|
[4 /* PopulationDeviation */]: populationDeviationDefn,
|
|
2433
2441
|
[5 /* Reock */]: reockDefn,
|
|
2434
2442
|
[6 /* PolsbyPopper */]: polsbyPopperDefn,
|
|
2435
|
-
//
|
|
2443
|
+
// NOTE - SPLITTING
|
|
2436
2444
|
[7 /* UnexpectedCountySplits */]: unexpectedCountySplitsDefn,
|
|
2437
2445
|
[10 /* VTDSplits */]: VTDSplitsDefn,
|
|
2438
2446
|
[8 /* CountySplitting */]: countySplittingDefn,
|
|
@@ -2460,7 +2468,7 @@ function doConfigureScales(s) {
|
|
|
2460
2468
|
s.testScales[6 /* PolsbyPopper */] = { scale: [0.10, 0.50] };
|
|
2461
2469
|
const nDistricts = s.state.nDistricts;
|
|
2462
2470
|
const nCounties = s.counties.nCounties;
|
|
2463
|
-
//
|
|
2471
|
+
// NOTE - SPLITTING: Experiment w/ this multiplier. Only allowing the expected
|
|
2464
2472
|
// number of county splits seems too stringent, empirically.
|
|
2465
2473
|
const allowableCountySplitsMultiplier = 1.5;
|
|
2466
2474
|
const nAllowableSplits = Math.min(allowableCountySplitsMultiplier * (nDistricts - 1));
|
|
@@ -2519,7 +2527,7 @@ exports.PRECISION = 4;
|
|
|
2519
2527
|
exports.NORMALIZED_RANGE = 100;
|
|
2520
2528
|
// The dummy district ID for features not assigned districts yet
|
|
2521
2529
|
exports.NOT_ASSIGNED = 0;
|
|
2522
|
-
// TODO -
|
|
2530
|
+
// TODO - DASHBOARD: Discuss w/ Dave
|
|
2523
2531
|
// # of items to report as problematic (e.g., features, districts, etc.)
|
|
2524
2532
|
exports.NUMBER_OF_ITEMS_TO_REPORT = 10;
|
|
2525
2533
|
// The virtual geoID for "neighbors" in other states
|
|
@@ -2584,7 +2592,7 @@ function isOutOfState(geoID) {
|
|
|
2584
2592
|
return geoID == S.OUT_OF_STATE;
|
|
2585
2593
|
}
|
|
2586
2594
|
exports.isOutOfState = isOutOfState;
|
|
2587
|
-
//
|
|
2595
|
+
// NOTE - UNASSIGNED
|
|
2588
2596
|
// Get the districtID to which a geoID is assigned
|
|
2589
2597
|
function getDistrict(plan, geoID) {
|
|
2590
2598
|
// All geoIDs in a state *should be* assigned to a district (including the
|
|
@@ -2642,7 +2650,7 @@ function normalize(rawScore, testScale) {
|
|
|
2642
2650
|
let coercedValue = Math.min(Math.max(rawScore, rangeMin), rangeMax);
|
|
2643
2651
|
// Scale the bounded value w/in the range [0 - (rangeMax - rangeMin)]
|
|
2644
2652
|
let scaledValue = (coercedValue - rangeMin) / (rangeMax - rangeMin);
|
|
2645
|
-
//
|
|
2653
|
+
// NOTE - SPLITTING
|
|
2646
2654
|
// Invert the scaled value if necessary to make bigger = better
|
|
2647
2655
|
if (testScale.bInvertScaled) {
|
|
2648
2656
|
scaledValue = 1.0 - scaledValue;
|