@dra2020/district-analytics 10.0.2 → 10.0.5
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.
|
@@ -230,58 +230,6 @@ class AnalyticsSession {
|
|
|
230
230
|
// 1. All polygons in a multi-polygon; and
|
|
231
231
|
// 2. All holes in a otherwise cohesive polygon.
|
|
232
232
|
// 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
233
|
// HELPERS USED INTERNALLY
|
|
286
234
|
// Get an individual test, so you can drive UI with the results.
|
|
287
235
|
getTest(testID) {
|
|
@@ -1056,12 +1004,11 @@ function doAnalyzePlan(s, bLog = false) {
|
|
|
1056
1004
|
s.bPostProcessingDone = false;
|
|
1057
1005
|
}
|
|
1058
1006
|
exports.doAnalyzePlan = doAnalyzePlan;
|
|
1007
|
+
//
|
|
1059
1008
|
// Derive secondary analytics that are based on primary tests.
|
|
1060
1009
|
// This concept allows Population Deviation to be a primary numeric test and
|
|
1061
1010
|
// Equal Population to be secondary pass/fail validation.
|
|
1062
1011
|
//
|
|
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
1012
|
function doDeriveSecondaryTests(s, bLog = false) {
|
|
1066
1013
|
s.tests[3 /* EqualPopulation */] = equal_1.doHasEqualPopulations(s, bLog);
|
|
1067
1014
|
}
|
|
@@ -1265,14 +1212,12 @@ const Poly = __importStar(__webpack_require__(/*! @dra2020/poly */ "@dra2020/pol
|
|
|
1265
1212
|
const Compactness = __importStar(__webpack_require__(/*! @dra2020/compactness */ "@dra2020/compactness"));
|
|
1266
1213
|
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
1267
1214
|
// HELPER TO EXTRACT PROPERTIES OF DISTRICT SHAPES
|
|
1268
|
-
// TODO - Create an array, as opposed to a dict
|
|
1269
1215
|
function extractDistrictProperties(s, bLog = false) {
|
|
1270
1216
|
// NOTE - I am assuming that district IDs are integers 1–N
|
|
1271
1217
|
for (let i = 1; i <= s.state.nDistricts; i++) {
|
|
1272
1218
|
const poly = s.districts.getDistrictShapeByID(i);
|
|
1273
1219
|
// Guard against no shape for empty districts AND null shapes
|
|
1274
1220
|
if (isAShape(poly)) {
|
|
1275
|
-
// TODO - OPTIMIZE: Bundle these calls?
|
|
1276
1221
|
const area = Poly.polyAreaFlat(poly);
|
|
1277
1222
|
const perimeter = Poly.polyPerimeterFlat(poly);
|
|
1278
1223
|
const diameter = Poly.polyDiameterFlat(poly);
|
|
@@ -1755,34 +1700,6 @@ function prepareRequirementsChecklist(s, bLog = false) {
|
|
|
1755
1700
|
return paRequirements;
|
|
1756
1701
|
}
|
|
1757
1702
|
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
1703
|
// Create a DistrictStatistics instance, deep copying the underlying values.
|
|
1787
1704
|
function prepareDistrictStatistics(s, bLog = false) {
|
|
1788
1705
|
if (!(s.bPostProcessingDone)) {
|
|
@@ -1841,76 +1758,6 @@ function prepareDistrictStatistics(s, bLog = false) {
|
|
|
1841
1758
|
return ds;
|
|
1842
1759
|
}
|
|
1843
1760
|
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
1761
|
// Postprocess analytics - Normalize numeric results and derive secondary tests.
|
|
1915
1762
|
// Do this after analytics have been run and before preparing a test log or scorecard.
|
|
1916
1763
|
function doAnalyzePostProcessing(s, bLog = false) {
|
|
@@ -2148,7 +1995,6 @@ exports.PRECISION = 4;
|
|
|
2148
1995
|
exports.NORMALIZED_RANGE = 100;
|
|
2149
1996
|
// The dummy district ID for features not assigned districts yet
|
|
2150
1997
|
exports.NOT_ASSIGNED = 0;
|
|
2151
|
-
// TODO - DASHBOARD: Discuss w/ Dave
|
|
2152
1998
|
// # of items to report as problematic (e.g., features, districts, etc.)
|
|
2153
1999
|
exports.NUMBER_OF_ITEMS_TO_REPORT = 10;
|
|
2154
2000
|
// The virtual geoID for "neighbors" in other states
|
|
@@ -2282,28 +2128,6 @@ function isUninhabited(geoID, s) {
|
|
|
2282
2128
|
return bUninhabited;
|
|
2283
2129
|
}
|
|
2284
2130
|
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
2131
|
// Round a fractional number [0-1] to the desired level of PRECISION.
|
|
2308
2132
|
function trim(fullFraction, digits = undefined) {
|
|
2309
2133
|
if (digits == 0) {
|
|
@@ -2341,14 +2165,11 @@ function andArray(arr) {
|
|
|
2341
2165
|
}
|
|
2342
2166
|
exports.andArray = andArray;
|
|
2343
2167
|
// WORKING WITH OBJECT KEYS/PROPERTIES
|
|
2344
|
-
// TODO - TERRY, is this copesetic?
|
|
2345
|
-
// TODO - Handle integer keys?
|
|
2346
2168
|
// Does an object have a key/property?
|
|
2347
2169
|
function keyExists(k, o) {
|
|
2348
2170
|
return k in o;
|
|
2349
2171
|
}
|
|
2350
2172
|
exports.keyExists = keyExists;
|
|
2351
|
-
// TODO - TERRY, can these three be combined into a generic isEmpty() check?
|
|
2352
2173
|
// Does an object (dict) have any keys/properties?
|
|
2353
2174
|
function isObjectEmpty(o) {
|
|
2354
2175
|
return Object.keys(o).length === 0;
|
|
@@ -2375,7 +2196,6 @@ function getObjectKeys(o) {
|
|
|
2375
2196
|
return Object.keys(o);
|
|
2376
2197
|
}
|
|
2377
2198
|
exports.getObjectKeys = getObjectKeys;
|
|
2378
|
-
// TODO - Convert getNumericObjectKeys() idiom to for..of where possible
|
|
2379
2199
|
function getNumericObjectKeys(o) {
|
|
2380
2200
|
return Object.keys(o).map(Number);
|
|
2381
2201
|
}
|
|
@@ -2447,7 +2267,6 @@ function mapBooleanToTriState(bool) {
|
|
|
2447
2267
|
return (bool) ? 0 /* Green */ : 2 /* Red */;
|
|
2448
2268
|
}
|
|
2449
2269
|
exports.mapBooleanToTriState = mapBooleanToTriState;
|
|
2450
|
-
// TODO - TERRY: What is this the simple explanation of what this thing is doing?
|
|
2451
2270
|
var util_1 = __webpack_require__(/*! @dra2020/util */ "@dra2020/util");
|
|
2452
2271
|
exports.depthof = util_1.depthof;
|
|
2453
2272
|
|
|
@@ -2593,38 +2412,6 @@ function doIsContiguous(s, bLog = false) {
|
|
|
2593
2412
|
return test;
|
|
2594
2413
|
}
|
|
2595
2414
|
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
2415
|
//
|
|
2629
2416
|
// FREE OF HOLES - Are any districts fully embedded w/in another district?
|
|
2630
2417
|
//
|
|
@@ -2635,9 +2422,6 @@ export function isConnected(districtGeos: Set<string>, graph: D.Graph): boolean
|
|
|
2635
2422
|
// To test this, load the NC 2010 map 'SAMPLE-BG-map-hole.csv'. District 1,
|
|
2636
2423
|
// Buncombe County (37021), is a donut hole w/in District 3.
|
|
2637
2424
|
//
|
|
2638
|
-
// TODO - OPTIMIZE: This to take advantage of district boundary info, if/when
|
|
2639
|
-
// we cache one to optimize compactness.
|
|
2640
|
-
//
|
|
2641
2425
|
function doIsFreeOfHoles(s, bLog = false) {
|
|
2642
2426
|
let test = s.getTest(2 /* FreeOfHoles */);
|
|
2643
2427
|
// Initialize values
|
|
@@ -2667,89 +2451,6 @@ function doIsFreeOfHoles(s, bLog = false) {
|
|
|
2667
2451
|
return test;
|
|
2668
2452
|
}
|
|
2669
2453
|
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
2454
|
|
|
2754
2455
|
|
|
2755
2456
|
/***/ }),
|