@dra2020/district-analytics 10.0.3 → 10.0.6

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.
@@ -143,11 +143,6 @@ class AnalyticsSession {
143
143
  this.districts = new D.Districts(this, SessionRequest['districtShapes']);
144
144
  }
145
145
  processConfig(config) {
146
- // NOTE - Session settings are required:
147
- // - Analytics suites can be defaulted to all with [], but
148
- // - Dataset keys must be explicitly specified with 'dataset'
149
- // NOTE - Legacy feature: Always calc everything
150
- config['suites'] = [0 /* Legal */, 1 /* Fair */, 2 /* Best */];
151
146
  // Default the Census & redistricting cycle to 2010
152
147
  if (!(U.keyExists('cycle', config)))
153
148
  config['cycle'] = 2010;
@@ -230,58 +225,6 @@ class AnalyticsSession {
230
225
  // 1. All polygons in a multi-polygon; and
231
226
  // 2. All holes in a otherwise cohesive polygon.
232
227
  // Note that all non-cohesive features are always simple polygons.
233
- /*
234
- let i: number, j: number;
235
- let nPoly: number = 0;
236
- for (i = 0;nPoly == 0 && i < this.cacheDistricts.features.length;i++)
237
- {
238
- let f = this.cacheDistricts.features[i];
239
-
240
- if (f.geometry.type === 'MultiPolygon')
241
- nPoly += f.geometry.coordinates.length;
242
- else if (f.geometry.type === 'Polygon' && f.geometry.coordinates.length)
243
- nPoly += (f.geometry.coordinates.length - 1);
244
- }
245
- if (nPoly)
246
- {
247
- this.cacheNoncohesive = {type: 'FeatureCollection', features: []};
248
- let af: any = this.cacheNoncohesive.features;
249
- let oUnique: any = {};
250
-
251
- // First add discontiguous polygons
252
- for (i = 0;i < this.cacheDistricts.features.length;i++)
253
- {
254
- let f = this.cacheDistricts.features[i];
255
-
256
- if (f.geometry.type === 'MultiPolygon')
257
- {
258
- // Push all non-contiguous polygons
259
- for (j = 0;j < f.geometry.coordinates.length;j++)
260
- {
261
- let p: any = f.geometry.coordinates[j];
262
- oUnique[Hash.qhash(p[0])] = true;
263
- af.push({type: 'Feature', properties: {id: `${af.length + 1}`}, geometry: {type: 'Polygon', coordinates: p}});
264
- }
265
- }
266
- }
267
-
268
- // Now add unique holes
269
- for (i = 0;i < this.cacheDistricts.features.length;i++)
270
- {
271
- let f = this.cacheDistricts.features[i];
272
-
273
- if (f.geometry.type === 'Polygon')
274
- {
275
- // Push all holes from this polygon
276
- for (j = 1;j < f.geometry.coordinates.length;j++)
277
- {
278
- let p: any = f.geometry.coordinates[j];
279
- if (oUnique[Hash.qhash(p)] === undefined)
280
- af.push({type: 'Feature', properties: {id: `${af.length + 1}`}, geometry: {type: 'Polygon', coordinates: [p]}});
281
- }
282
- }
283
- }
284
- } */
285
228
  // HELPERS USED INTERNALLY
286
229
  // Get an individual test, so you can drive UI with the results.
287
230
  getTest(testID) {
@@ -1056,12 +999,11 @@ function doAnalyzePlan(s, bLog = false) {
1056
999
  s.bPostProcessingDone = false;
1057
1000
  }
1058
1001
  exports.doAnalyzePlan = doAnalyzePlan;
1002
+ //
1059
1003
  // Derive secondary analytics that are based on primary tests.
1060
1004
  // This concept allows Population Deviation to be a primary numeric test and
1061
1005
  // Equal Population to be secondary pass/fail validation.
1062
1006
  //
1063
- // NOTE - Should this be conditionalized on the test suites requested?
1064
- // Those are encapsulated in reports.ts right now, so not doing that.
1065
1007
  function doDeriveSecondaryTests(s, bLog = false) {
1066
1008
  s.tests[3 /* EqualPopulation */] = equal_1.doHasEqualPopulations(s, bLog);
1067
1009
  }
@@ -1265,14 +1207,12 @@ const Poly = __importStar(__webpack_require__(/*! @dra2020/poly */ "@dra2020/pol
1265
1207
  const Compactness = __importStar(__webpack_require__(/*! @dra2020/compactness */ "@dra2020/compactness"));
1266
1208
  const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
1267
1209
  // HELPER TO EXTRACT PROPERTIES OF DISTRICT SHAPES
1268
- // TODO - Create an array, as opposed to a dict
1269
1210
  function extractDistrictProperties(s, bLog = false) {
1270
1211
  // NOTE - I am assuming that district IDs are integers 1–N
1271
1212
  for (let i = 1; i <= s.state.nDistricts; i++) {
1272
1213
  const poly = s.districts.getDistrictShapeByID(i);
1273
1214
  // Guard against no shape for empty districts AND null shapes
1274
1215
  if (isAShape(poly)) {
1275
- // TODO - OPTIMIZE: Bundle these calls?
1276
1216
  const area = Poly.polyAreaFlat(poly);
1277
1217
  const perimeter = Poly.polyPerimeterFlat(poly);
1278
1218
  const diameter = Poly.polyDiameterFlat(poly);
@@ -1755,34 +1695,6 @@ function prepareRequirementsChecklist(s, bLog = false) {
1755
1695
  return paRequirements;
1756
1696
  }
1757
1697
  exports.prepareRequirementsChecklist = prepareRequirementsChecklist;
1758
- /* 10-23-2020 - Removed the district statistics sample.
1759
-
1760
- export const sampleDistrictStatistics: DistrictStatistics = {
1761
- table: [
1762
- // District 0 is the dummy unassigned district
1763
- // HACK - Total VAP #'s at the end are just so the same matches the type
1764
- [0, 0, 0, null, null, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
1765
- [1, 653133, -0.0950, T.TriState.Green, T.TriState.Red, T.TriState.Green, T.TriState.Green, 0.4177, 0.5823, 0.8631, 0.1369, 0.0734, 0.0360, 0.0009, 0.0235, 0.0064, 50000],
1766
- [2, 620961, -0.1396, T.TriState.Green, T.TriState.Red, T.TriState.Red, T.TriState.Green, 0.8820, 0.1180, 0.3129, 0.6871, 0.6169, 0.0391, 0.0013, 0.0310, 0.0099, 50000],
1767
- [3, 971777, 0.3465, T.TriState.Green, T.TriState.Red, T.TriState.Green, T.TriState.Green, 0.7261, 0.2739, 0.5174, 0.4826, 0.1745, 0.1572, 0.0020, 0.1531, 0.0090, 50000],
1768
- [4, 863420, 0.1964, T.TriState.Green, T.TriState.Red, T.TriState.Green, T.TriState.Green, 0.8957, 0.1043, 0.1734, 0.8266, 0.6489, 0.1348, 0.0020, 0.0496, 0.0127, 50000],
1769
- [5, 805029, 0.1155, T.TriState.Green, T.TriState.Red, T.TriState.Green, T.TriState.Yellow, 0.5743, 0.4257, 0.6587, 0.3413, 0.2494, 0.0363, 0.0012, 0.0536, 0.0081, 50000],
1770
- [6, 824741, 0.1428, T.TriState.Green, T.TriState.Red, T.TriState.Green, T.TriState.Red, 0.5341, 0.4659, 0.7045, 0.2955, 0.1619, 0.0526, 0.0018, 0.0782, 0.0090, 50000],
1771
- [7, 549714, -0.2383, T.TriState.Green, T.TriState.Green, T.TriState.Green, T.TriState.Green, 0.5025, 0.4975, 0.6906, 0.3094, 0.2468, 0.0319, 0.0013, 0.0258, 0.0111, 50000],
1772
- [8, 484777, -0.3283, T.TriState.Green, T.TriState.Green, T.TriState.Green, T.TriState.Green, 0.4105, 0.5895, 0.8370, 0.1630, 0.1074, 0.0316, 0.0013, 0.0197, 0.0077, 50000],
1773
- // District N+1 is the dummy state-summary district
1774
- [9, 721694, 0.6748, T.TriState.Green, T.TriState.Red, T.TriState.Red, T.TriState.Red, 0.6293, 0.3707, 0.5722, 0.4278, 0.2925, 0.0729, 0.0015, 0.0618, 0.0093, 400000]
1775
- ],
1776
- details: {},
1777
- datasets: {
1778
- shapes: "2010 VTD shapes",
1779
- census: "2010 Census Total Population",
1780
- vap: "2010 Voting Age Population",
1781
- election: "2016 Presidential, US Senate, Governor, and AG election results"
1782
- },
1783
- resources: {}
1784
- }
1785
- */
1786
1698
  // Create a DistrictStatistics instance, deep copying the underlying values.
1787
1699
  function prepareDistrictStatistics(s, bLog = false) {
1788
1700
  if (!(s.bPostProcessingDone)) {
@@ -1841,76 +1753,6 @@ function prepareDistrictStatistics(s, bLog = false) {
1841
1753
  return ds;
1842
1754
  }
1843
1755
  exports.prepareDistrictStatistics = prepareDistrictStatistics;
1844
- // META-DATA FOR TESTS/ANALYTICS
1845
- //
1846
- // NOTE - This structure is a vestige of having created a metadata-driven
1847
- // scorecard w/in district-analytics at first. It works for creating the
1848
- // unstyled results structures, so it isn't a high priority to rationalize.
1849
- var TestType;
1850
- (function (TestType) {
1851
- TestType[TestType["PassFail"] = 0] = "PassFail";
1852
- TestType[TestType["Percentage"] = 1] = "Percentage";
1853
- TestType[TestType["Number"] = 2] = "Number";
1854
- })(TestType || (TestType = {}));
1855
- const completeDefn = {
1856
- ID: 0 /* Complete */,
1857
- name: "Complete",
1858
- normalize: false,
1859
- externalType: TestType.PassFail,
1860
- suites: [0 /* Legal */]
1861
- };
1862
- const contiguousDefn = {
1863
- ID: 1 /* Contiguous */,
1864
- name: "Contiguous",
1865
- normalize: false,
1866
- externalType: TestType.PassFail,
1867
- suites: [0 /* Legal */]
1868
- };
1869
- const freeOfHolesDefn = {
1870
- ID: 2 /* FreeOfHoles */,
1871
- name: "Free of Holes",
1872
- normalize: false,
1873
- externalType: TestType.PassFail,
1874
- suites: [0 /* Legal */]
1875
- };
1876
- const equalPopulationDefn = {
1877
- ID: 3 /* EqualPopulation */,
1878
- name: "Equal Population",
1879
- normalize: false,
1880
- externalType: TestType.PassFail,
1881
- suites: [0 /* Legal */]
1882
- };
1883
- const populationDeviationDefn = {
1884
- ID: 4 /* PopulationDeviation */,
1885
- name: "Population Deviation",
1886
- normalize: true,
1887
- externalType: TestType.Percentage,
1888
- suites: [0 /* Legal */, 2 /* Best */] // Both so EqualPopulation can be assessed
1889
- };
1890
- const unexpectedCountySplitsDefn = {
1891
- ID: 5 /* UnexpectedCountySplits */,
1892
- name: "Unexpected County Splits",
1893
- normalize: false,
1894
- externalType: TestType.Percentage,
1895
- suites: [2 /* Best */]
1896
- };
1897
- const VTDSplitsDefn = {
1898
- ID: 6 /* VTDSplits */,
1899
- name: "VTD Splits",
1900
- normalize: false,
1901
- externalType: TestType.Number,
1902
- suites: [2 /* Best */]
1903
- };
1904
- // All the tests that have been defined (can be reported on)
1905
- const testDefns = {
1906
- [0 /* Complete */]: completeDefn,
1907
- [1 /* Contiguous */]: contiguousDefn,
1908
- [2 /* FreeOfHoles */]: freeOfHolesDefn,
1909
- [3 /* EqualPopulation */]: equalPopulationDefn,
1910
- [4 /* PopulationDeviation */]: populationDeviationDefn,
1911
- [5 /* UnexpectedCountySplits */]: unexpectedCountySplitsDefn,
1912
- [6 /* VTDSplits */]: VTDSplitsDefn,
1913
- };
1914
1756
  // Postprocess analytics - Normalize numeric results and derive secondary tests.
1915
1757
  // Do this after analytics have been run and before preparing a test log or scorecard.
1916
1758
  function doAnalyzePostProcessing(s, bLog = false) {
@@ -2148,7 +1990,6 @@ exports.PRECISION = 4;
2148
1990
  exports.NORMALIZED_RANGE = 100;
2149
1991
  // The dummy district ID for features not assigned districts yet
2150
1992
  exports.NOT_ASSIGNED = 0;
2151
- // TODO - DASHBOARD: Discuss w/ Dave
2152
1993
  // # of items to report as problematic (e.g., features, districts, etc.)
2153
1994
  exports.NUMBER_OF_ITEMS_TO_REPORT = 10;
2154
1995
  // The virtual geoID for "neighbors" in other states
@@ -2282,28 +2123,6 @@ function isUninhabited(geoID, s) {
2282
2123
  return bUninhabited;
2283
2124
  }
2284
2125
  exports.isUninhabited = isUninhabited;
2285
- // NORMALIZING RESULTS
2286
- function normalize(rawScore, testScale) {
2287
- let rangeMin = testScale.scale[0];
2288
- let rangeMax = testScale.scale[1];
2289
- // Invert the raw value if necessary to make bigger = better
2290
- // TODO - This works for Population Deviation, because the max is 1.0.
2291
- // Generalize this???
2292
- if (testScale.bInvertRaw) {
2293
- rawScore = 1.0 - rawScore;
2294
- }
2295
- // Coerce the value to be w/in the given range
2296
- let coercedValue = Math.min(Math.max(rawScore, rangeMin), rangeMax);
2297
- // Scale the bounded value w/in the range [0 - (rangeMax - rangeMin)]
2298
- let scaledValue = (coercedValue - rangeMin) / (rangeMax - rangeMin);
2299
- // Invert the scaled value if necessary to make bigger = better
2300
- if (testScale.bInvertScaled) {
2301
- scaledValue = 1.0 - scaledValue;
2302
- }
2303
- // Finally, make the range [0-100]
2304
- return Math.round(scaledValue * S.NORMALIZED_RANGE);
2305
- }
2306
- exports.normalize = normalize;
2307
2126
  // Round a fractional number [0-1] to the desired level of PRECISION.
2308
2127
  function trim(fullFraction, digits = undefined) {
2309
2128
  if (digits == 0) {
@@ -2341,14 +2160,11 @@ function andArray(arr) {
2341
2160
  }
2342
2161
  exports.andArray = andArray;
2343
2162
  // WORKING WITH OBJECT KEYS/PROPERTIES
2344
- // TODO - TERRY, is this copesetic?
2345
- // TODO - Handle integer keys?
2346
2163
  // Does an object have a key/property?
2347
2164
  function keyExists(k, o) {
2348
2165
  return k in o;
2349
2166
  }
2350
2167
  exports.keyExists = keyExists;
2351
- // TODO - TERRY, can these three be combined into a generic isEmpty() check?
2352
2168
  // Does an object (dict) have any keys/properties?
2353
2169
  function isObjectEmpty(o) {
2354
2170
  return Object.keys(o).length === 0;
@@ -2375,7 +2191,6 @@ function getObjectKeys(o) {
2375
2191
  return Object.keys(o);
2376
2192
  }
2377
2193
  exports.getObjectKeys = getObjectKeys;
2378
- // TODO - Convert getNumericObjectKeys() idiom to for..of where possible
2379
2194
  function getNumericObjectKeys(o) {
2380
2195
  return Object.keys(o).map(Number);
2381
2196
  }
@@ -2447,7 +2262,6 @@ function mapBooleanToTriState(bool) {
2447
2262
  return (bool) ? 0 /* Green */ : 2 /* Red */;
2448
2263
  }
2449
2264
  exports.mapBooleanToTriState = mapBooleanToTriState;
2450
- // TODO - TERRY: What is this the simple explanation of what this thing is doing?
2451
2265
  var util_1 = __webpack_require__(/*! @dra2020/util */ "@dra2020/util");
2452
2266
  exports.depthof = util_1.depthof;
2453
2267
 
@@ -2593,38 +2407,6 @@ function doIsContiguous(s, bLog = false) {
2593
2407
  return test;
2594
2408
  }
2595
2409
  exports.doIsContiguous = doIsContiguous;
2596
- /* GRAPH - Removed 10/05/2020. Using dra-graph instead.
2597
- // Are the features in a district fully connected?
2598
- export function isConnected(districtGeos: Set<string>, graph: D.Graph): boolean
2599
- {
2600
- // TODO - TERRY, why does this constructor need a <T> type specification?
2601
- let visited = new Set<string>();
2602
- let toProcess: string[] = [];
2603
-
2604
- // Start processing with the first geoID in the district
2605
- let iter = districtGeos.values();
2606
- toProcess.push(iter.next().value);
2607
-
2608
- // While there are geoIDs in the district that haven't been processed
2609
- while (toProcess.length > 0)
2610
- {
2611
- // Grab a geoID and process it
2612
- let node = toProcess.pop() as string;
2613
- visited.add(node);
2614
-
2615
- // Get its actual, in-state neighbors
2616
- let actualNeighbors = graph.peerNeighbors(node).filter(x => U.isInState(x));
2617
-
2618
- // Add neighbors to visit, if they're in the same district Y haven't already been visited
2619
- let neighborsToVisit = actualNeighbors.filter(x => districtGeos.has(x) && (!visited.has(x)));
2620
- // TODO - TERRY, is this the quickest/best way to do this?
2621
- toProcess.push(...neighborsToVisit);
2622
- }
2623
-
2624
- // Stop when you've visited all the geoIDs in the district
2625
- return visited.size == districtGeos.size;
2626
- }
2627
- */
2628
2410
  //
2629
2411
  // FREE OF HOLES - Are any districts fully embedded w/in another district?
2630
2412
  //
@@ -2635,9 +2417,6 @@ export function isConnected(districtGeos: Set<string>, graph: D.Graph): boolean
2635
2417
  // To test this, load the NC 2010 map 'SAMPLE-BG-map-hole.csv'. District 1,
2636
2418
  // Buncombe County (37021), is a donut hole w/in District 3.
2637
2419
  //
2638
- // TODO - OPTIMIZE: This to take advantage of district boundary info, if/when
2639
- // we cache one to optimize compactness.
2640
- //
2641
2420
  function doIsFreeOfHoles(s, bLog = false) {
2642
2421
  let test = s.getTest(2 /* FreeOfHoles */);
2643
2422
  // Initialize values
@@ -2667,89 +2446,6 @@ function doIsFreeOfHoles(s, bLog = false) {
2667
2446
  return test;
2668
2447
  }
2669
2448
  exports.doIsFreeOfHoles = doIsFreeOfHoles;
2670
- /* GRAPH - Removed 10/05/2020. Using dra-graph instead.
2671
- // Test whether one district is embedded w/in any other.
2672
- export function isEmbedded(districtID: number, geoIDs: Set<string>, plan: D.Plan, graph: D.Graph): boolean
2673
- {
2674
- // NOTE - "features" here = "geoIDs." These aren't "features" proper, just
2675
- // identifier strings.
2676
- let features = geoIDs;
2677
- let planByGeo = plan.byGeoID();
2678
-
2679
- // Assume the district is embedded
2680
- let bEmbedded = true;
2681
- // Keep track of the neighoring districts
2682
- let neighboringDistricts = new Set();
2683
-
2684
- // TODO - OPTIMIZE: Use just the boundary features, when available
2685
- // Get the features for the real district
2686
- let featuresToCheck = Array.from(features);
2687
-
2688
- // If the district has features, check whether it is embedded
2689
- if (!(U.isArrayEmpty(featuresToCheck)))
2690
- {
2691
- // For each feature that needs to be checked (see above)
2692
- for (let feature of featuresToCheck)
2693
- {
2694
- // Get its neighbors (including the virtual "out of state" ones)
2695
- let neighbors = graph.peerNeighbors(feature);
2696
-
2697
- for (let neighbor of neighbors)
2698
- {
2699
- if (U.isOutOfState(neighbor))
2700
- {
2701
- bEmbedded = false;
2702
- // No need to check any more neighbors
2703
- break;
2704
- }
2705
- else
2706
- {
2707
- let neighboringDistrict = U.getDistrict(planByGeo, neighbor);
2708
-
2709
- // Assume that a missing district assignment (= None) means that the
2710
- // feature is "water-only" AND part of the state border (vs. internal)
2711
- // and, therefore, not in the plan/map.
2712
-
2713
- if (neighboringDistrict == undefined)
2714
- {
2715
- bEmbedded = false;
2716
- // No need to check any more neighbors
2717
- break;
2718
- }
2719
- else
2720
- {
2721
- // TODO - OPTIMIZE: Since we're checking *all* features in a district right
2722
- // now, not just boundary features and neighbors in other districts,
2723
- // prune out the current district. If/when we optimize compactness
2724
- // to cache district boundaries (as before in my Python implementation),
2725
- // we won't have to guard adding "neighboring" districts in this way.
2726
- if (neighboringDistrict != districtID)
2727
- {
2728
- neighboringDistricts.add(neighboringDistrict);
2729
- }
2730
-
2731
- if (neighboringDistricts.size > 1)
2732
- {
2733
- bEmbedded = false;
2734
- // No need to check any more neighbors
2735
- break;
2736
- }
2737
- }
2738
- }
2739
- }
2740
- // If a district is not embedded, there's no need to check anymore
2741
- // border geos.
2742
- if (!bEmbedded)
2743
- {
2744
- break;
2745
- }
2746
- }
2747
-
2748
- }
2749
-
2750
- return bEmbedded;
2751
- }
2752
- */
2753
2449
 
2754
2450
 
2755
2451
  /***/ }),