@dra2020/district-analytics 5.0.2 → 5.2.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 +6361 -3458
- package/dist/cli.js.map +1 -1
- package/dist/district-analytics.js +9 -146
- package/dist/district-analytics.js.map +1 -1
- package/dist/src/geofeature.d.ts +0 -5
- package/package.json +3 -3
|
@@ -122,7 +122,7 @@ const analyze_1 = __webpack_require__(/*! ./analyze */ "./src/analyze.ts");
|
|
|
122
122
|
const score_1 = __webpack_require__(/*! ./score */ "./src/score.ts");
|
|
123
123
|
const results_1 = __webpack_require__(/*! ./results */ "./src/results.ts");
|
|
124
124
|
const results_2 = __webpack_require__(/*! ./results */ "./src/results.ts");
|
|
125
|
-
const
|
|
125
|
+
const Poly = __importStar(__webpack_require__(/*! @dra2020/poly */ "@dra2020/poly"));
|
|
126
126
|
const D = __importStar(__webpack_require__(/*! ./_data */ "./src/_data.ts"));
|
|
127
127
|
const U = __importStar(__webpack_require__(/*! ./utils */ "./src/utils.ts"));
|
|
128
128
|
class AnalyticsSession {
|
|
@@ -198,7 +198,7 @@ class AnalyticsSession {
|
|
|
198
198
|
// If a district has a shape & it is not contiguous, by definition,
|
|
199
199
|
// it will be a Multipolygon, i.e., it will have multiple pieces, some
|
|
200
200
|
// possibly embedded w/in other districts. Get & add all the pieces.
|
|
201
|
-
const districtParts =
|
|
201
|
+
const districtParts = Poly.polyParts(poly);
|
|
202
202
|
discontiguousDistrictFeatures.features.push(...districtParts.features);
|
|
203
203
|
// discontiguousDistrictFeatures.features.push(poly);
|
|
204
204
|
}
|
|
@@ -969,7 +969,7 @@ Compactness: None
|
|
|
969
969
|
Proportionality: 27.67% gap
|
|
970
970
|
Cohesiveness: 11 unexpected splits, affecting 27.14% of the total population
|
|
971
971
|
|
|
972
|
-
These counties are split unexpectedly:
|
|
972
|
+
These counties are split unexpectedly (meaning that they're smaller than a district):
|
|
973
973
|
|
|
974
974
|
• Bladen
|
|
975
975
|
• Buncombe
|
|
@@ -1073,6 +1073,7 @@ function doFindCountiesSplitUnexpectedly(s, bLog = false) {
|
|
|
1073
1073
|
}
|
|
1074
1074
|
countiesSplitUnexpectedly = countiesSplitUnexpectedly.sort();
|
|
1075
1075
|
test['score'] = U.trim(unexpectedAffected);
|
|
1076
|
+
test['details']['nSplits'] = nSplits;
|
|
1076
1077
|
test['details']['unexpectedSplits'] = unexpectedSplits;
|
|
1077
1078
|
test['details']['countiesSplitUnexpectedly'] = countiesSplitUnexpectedly;
|
|
1078
1079
|
return test;
|
|
@@ -1113,7 +1114,6 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
1113
1114
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1114
1115
|
// NOTE - This file will NOT be empty, when legacy code is deleted.
|
|
1115
1116
|
const Poly = __importStar(__webpack_require__(/*! @dra2020/poly */ "@dra2020/poly"));
|
|
1116
|
-
const geofeature_1 = __webpack_require__(/*! ./geofeature */ "./src/geofeature.ts");
|
|
1117
1117
|
// HELPER TO EXTRACT PROPERTIES OF DISTRICT SHAPES
|
|
1118
1118
|
// TODO - Create an array, as opposed to a dict
|
|
1119
1119
|
function extractDistrictProperties(s, bLog = false) {
|
|
@@ -1121,13 +1121,12 @@ function extractDistrictProperties(s, bLog = false) {
|
|
|
1121
1121
|
for (let i = 1; i <= s.state.nDistricts; i++) {
|
|
1122
1122
|
let poly = s.districts.getDistrictShapeByID(i);
|
|
1123
1123
|
// Guard against no shape for empty districts AND null shapes
|
|
1124
|
-
let
|
|
1125
|
-
let bNull = (!Poly.polyNormalize(poly, polyOptions));
|
|
1124
|
+
let bNull = !Poly.polyNormalize(poly);
|
|
1126
1125
|
if (poly && (!bNull)) {
|
|
1127
1126
|
// TODO - OPTIMIZE: Bundle these calls?
|
|
1128
|
-
let area =
|
|
1129
|
-
let perimeter =
|
|
1130
|
-
let diameter =
|
|
1127
|
+
let area = Poly.polyAreaFlat(poly);
|
|
1128
|
+
let perimeter = Poly.polyPerimeterFlat(poly);
|
|
1129
|
+
let diameter = Poly.polyDiameterFlat(poly);
|
|
1131
1130
|
let props = [0, 0, 0]; // TODO - TERRY?!?
|
|
1132
1131
|
props[0 /* Area */] = area;
|
|
1133
1132
|
props[1 /* Diameter */] = diameter;
|
|
@@ -1218,143 +1217,6 @@ function doHasEqualPopulations(s, bLog = false) {
|
|
|
1218
1217
|
exports.doHasEqualPopulations = doHasEqualPopulations;
|
|
1219
1218
|
|
|
1220
1219
|
|
|
1221
|
-
/***/ }),
|
|
1222
|
-
|
|
1223
|
-
/***/ "./src/geofeature.ts":
|
|
1224
|
-
/*!***************************!*\
|
|
1225
|
-
!*** ./src/geofeature.ts ***!
|
|
1226
|
-
\***************************/
|
|
1227
|
-
/*! no static exports found */
|
|
1228
|
-
/***/ (function(module, exports, __webpack_require__) {
|
|
1229
|
-
|
|
1230
|
-
"use strict";
|
|
1231
|
-
|
|
1232
|
-
//
|
|
1233
|
-
// GEO-FEATURES UTILITIES
|
|
1234
|
-
//
|
|
1235
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
1236
|
-
if (mod && mod.__esModule) return mod;
|
|
1237
|
-
var result = {};
|
|
1238
|
-
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
1239
|
-
result["default"] = mod;
|
|
1240
|
-
return result;
|
|
1241
|
-
};
|
|
1242
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1243
|
-
const Poly = __importStar(__webpack_require__(/*! @dra2020/poly */ "@dra2020/poly"));
|
|
1244
|
-
// HELPER
|
|
1245
|
-
function polyParts(poly) {
|
|
1246
|
-
let parts = { type: 'FeatureCollection', features: [] };
|
|
1247
|
-
let af = parts.features;
|
|
1248
|
-
if (poly.geometry.type === 'MultiPolygon') {
|
|
1249
|
-
// Push all non-contiguous polygons
|
|
1250
|
-
for (let j = 0; j < poly.geometry.coordinates.length; j++) {
|
|
1251
|
-
let onePoly = poly.geometry.coordinates[j];
|
|
1252
|
-
af.push({ type: 'Feature', properties: { id: `${af.length + 1}` }, geometry: { type: 'Polygon', coordinates: onePoly } });
|
|
1253
|
-
}
|
|
1254
|
-
}
|
|
1255
|
-
else {
|
|
1256
|
-
parts.features.push(poly);
|
|
1257
|
-
}
|
|
1258
|
-
return parts;
|
|
1259
|
-
}
|
|
1260
|
-
exports.polyParts = polyParts;
|
|
1261
|
-
// CARTESIAN SHIMS OVER 'POLY' FUNCTIONS
|
|
1262
|
-
// TODO - POLY: Confirm Cartesian calculations
|
|
1263
|
-
function gfArea(poly) {
|
|
1264
|
-
let area = _polygonArea(poly);
|
|
1265
|
-
return area;
|
|
1266
|
-
}
|
|
1267
|
-
exports.gfArea = gfArea;
|
|
1268
|
-
// Algorithm for the area of a simple/single planar polygon:
|
|
1269
|
-
// https://algorithmtutor.com/Computational-Geometry/Area-of-a-polygon-given-a-set-of-points/
|
|
1270
|
-
// https://mathopenref.com/coordpolygonarea2.html
|
|
1271
|
-
//
|
|
1272
|
-
// function polygonArea(X, Y, numPoints)
|
|
1273
|
-
// {
|
|
1274
|
-
// area = 0; // Accumulates area in the loop
|
|
1275
|
-
// j = numPoints-1; // The last vertex is the 'previous' one to the first
|
|
1276
|
-
// for (i=0; i<numPoints; i++)
|
|
1277
|
-
// { area = area + (X[j]+X[i]) * (Y[j]-Y[i]);
|
|
1278
|
-
// j = i; //j is previous vertex to i
|
|
1279
|
-
// }
|
|
1280
|
-
// return area/2;
|
|
1281
|
-
// }
|
|
1282
|
-
// Reimplemented to use polygons vs. X & Y vectors
|
|
1283
|
-
function _polygonSimpleArea(p) {
|
|
1284
|
-
let area = 0; // Accumulates area in the loop
|
|
1285
|
-
let n = p.length;
|
|
1286
|
-
let j = n - 1; // The last vertex is the 'previous' one to the first
|
|
1287
|
-
for (let i = 0; i < n; i++) {
|
|
1288
|
-
area = area + (p[j][0] + p[i][0]) * (p[j][1] - p[i][1]);
|
|
1289
|
-
// area = area + (X[j] + X[i]) * (Y[j] - Y[i]);
|
|
1290
|
-
j = i; // j is previous vertex to i
|
|
1291
|
-
}
|
|
1292
|
-
return Math.abs(area / 2);
|
|
1293
|
-
}
|
|
1294
|
-
// Generalizes the above for MultiPolygons -- cloned from polyArea() in 'poly'
|
|
1295
|
-
function _polygonArea(poly) {
|
|
1296
|
-
let polyOptions = { noLatitudeCorrection: true }; // NO-OP?
|
|
1297
|
-
poly = Poly.polyNormalize(poly, polyOptions); // TODO - POLY: Discuss w/ Terry
|
|
1298
|
-
let a = 0;
|
|
1299
|
-
// A MultiPolygon is a set of polygons
|
|
1300
|
-
for (let i = 0; poly && i < poly.length; i++) {
|
|
1301
|
-
// A single polygon is an exterior ring with interior holes. Holes are subtracted.
|
|
1302
|
-
let p = poly[i];
|
|
1303
|
-
for (let j = 0; j < p.length; j++) {
|
|
1304
|
-
let sp = p[j];
|
|
1305
|
-
a += _polygonSimpleArea(sp) * (j == 0 ? 1 : -1);
|
|
1306
|
-
}
|
|
1307
|
-
}
|
|
1308
|
-
return a;
|
|
1309
|
-
}
|
|
1310
|
-
// TODO - POLY: Confirm Cartesian calculations w/ Terry
|
|
1311
|
-
// The perimeter calculation already just computes cartesian distance if you
|
|
1312
|
-
// pass in the noLatitudeCorrection flag. You would need to divide by
|
|
1313
|
-
// Poly.EARTH_RADIUS to go from the returned units of meters to Lat/Lon “units”.
|
|
1314
|
-
function gfPerimeter(poly) {
|
|
1315
|
-
let perimeter = _polygonPerimeter(poly);
|
|
1316
|
-
return perimeter;
|
|
1317
|
-
}
|
|
1318
|
-
exports.gfPerimeter = gfPerimeter;
|
|
1319
|
-
// TODO - POLY: Confirm Cartesian calculations w/ Terry
|
|
1320
|
-
// Cloned from polyPerimeter() in 'poly' and revised to use Cartesian distance
|
|
1321
|
-
// NOTE - No conversion of degrees to radians!
|
|
1322
|
-
function _polygonPerimeter(poly) {
|
|
1323
|
-
let polyOptions = { noLatitudeCorrection: true }; // Cartesian distance
|
|
1324
|
-
poly = Poly.polyNormalize(poly, polyOptions);
|
|
1325
|
-
let perimeter = 0;
|
|
1326
|
-
for (let i = 0; poly && i < poly.length; i++) {
|
|
1327
|
-
// Ignore holes so only look at first polyline
|
|
1328
|
-
let p = poly[i][0];
|
|
1329
|
-
for (let j = 0; j < p.length - 1; j++)
|
|
1330
|
-
perimeter += _distance(p[j][0], p[j][1], p[j + 1][0], p[j + 1][1]);
|
|
1331
|
-
if (p.length > 2 && (p[0][0] != p[p.length - 1][0] || p[0][1] != p[p.length - 1][1]))
|
|
1332
|
-
perimeter += _distance(p[0][0], p[0][1], p[p.length - 1][0], p[p.length - 1][1]);
|
|
1333
|
-
}
|
|
1334
|
-
return perimeter;
|
|
1335
|
-
}
|
|
1336
|
-
function _distance(x1, y1, x2, y2) {
|
|
1337
|
-
let dLat = y2 - y1;
|
|
1338
|
-
let dLon = x2 - x1;
|
|
1339
|
-
let d;
|
|
1340
|
-
d = Math.sqrt((dLat * dLat) + (dLon * dLon));
|
|
1341
|
-
return d;
|
|
1342
|
-
}
|
|
1343
|
-
// TODO - POLY: Confirm Cartesian calculations w/ Terry
|
|
1344
|
-
// As I mentioned, the polyCircle code was already just treating the coordinate
|
|
1345
|
-
// system as Cartesian. I then did polyFromCircle to convert it to a polygon that
|
|
1346
|
-
// then could be passed to polyArea in order to take into account the projection.
|
|
1347
|
-
// If instead, you compute area directly from the circle as PI R squared, then
|
|
1348
|
-
// you should have your cartesian circular area.
|
|
1349
|
-
function gfDiameter(poly) {
|
|
1350
|
-
let polyOptions = { noLatitudeCorrection: true }; // NO-OP
|
|
1351
|
-
let circle = Poly.polyToCircle(poly, polyOptions);
|
|
1352
|
-
let diameter = circle.r * 2;
|
|
1353
|
-
return diameter;
|
|
1354
|
-
}
|
|
1355
|
-
exports.gfDiameter = gfDiameter;
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
1220
|
/***/ }),
|
|
1359
1221
|
|
|
1360
1222
|
/***/ "./src/index.ts":
|
|
@@ -1851,6 +1713,7 @@ function doAnalyzePostProcessing(s, bLog = false) {
|
|
|
1851
1713
|
scorecard.traditionalPrinciples.details['census'] = datasets.census;
|
|
1852
1714
|
const simpleSplits = s.getTest(5 /* UnexpectedCountySplits */);
|
|
1853
1715
|
scorecard.traditionalPrinciples.details['unexpectedAffected'] = simpleSplits['score'];
|
|
1716
|
+
scorecard.traditionalPrinciples.details['nSplits'] = simpleSplits['details']['nSplits'];
|
|
1854
1717
|
scorecard.traditionalPrinciples.details['countiesSplitUnexpectedly'] = U.deepCopy(simpleSplits['details']['countiesSplitUnexpectedly']);
|
|
1855
1718
|
// NOTE - Add split precincts in dra-client directly
|
|
1856
1719
|
// Derive secondary tests
|