@dra2020/baseclient 1.0.48 → 1.0.51
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/baseclient.js +166 -50
- package/dist/baseclient.js.map +1 -1
- package/dist/geo/geo.d.ts +8 -1
- package/dist/poly/topo.d.ts +1 -0
- package/lib/geo/geo.ts +116 -4
- package/lib/poly/polylabel.ts +66 -39
- package/lib/poly/topo.ts +9 -8
- package/package.json +1 -1
package/dist/baseclient.js
CHANGED
|
@@ -1345,18 +1345,31 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
1345
1345
|
return result;
|
|
1346
1346
|
};
|
|
1347
1347
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
1348
|
-
exports.geoIntersect = exports.geoIntersectOptions = exports.GeoMultiCollection = exports.geoMapEqual = exports.geoEqual = exports.geoTopoToCollectionNonNull = exports.geoTopoToCollection = exports.geoCollectionToTopoNonNull = exports.geoCollectionToTopo = exports.geoMapToCollectionNonNull = exports.geoMapToCollection = exports.geoCollectionToMap = exports.geoEnsureID = void 0;
|
|
1348
|
+
exports.geoIntersect = exports.geoIntersectOptions = exports.GeoMultiCollection = exports.geoMapEqual = exports.geoEqual = exports.geoTopoToCollectionNonNull = exports.geoTopoToCollection = exports.geoCollectionToTopoNonNull = exports.geoCollectionToTopo = exports.geoMapToCollectionNonNull = exports.geoMapToCollection = exports.geoCollectionToMap = exports.geoNormalizeCollection = exports.geoNormalizeFeature = exports.geoEnsureID = void 0;
|
|
1349
1349
|
const Util = __importStar(__webpack_require__(/*! ../util/all */ "./lib/util/all.ts"));
|
|
1350
1350
|
const Poly = __importStar(__webpack_require__(/*! ../poly/all */ "./lib/poly/all.ts"));
|
|
1351
|
-
|
|
1351
|
+
const NormalizeAll = { joinPolygons: true, checkRewind: true, ensureID: true };
|
|
1352
|
+
// set the canonical 'id' property from the best property value.
|
|
1353
|
+
// if joinPolygons is true, we do not enforce uniqueness.
|
|
1354
|
+
//
|
|
1355
|
+
function geoEnsureID(col, options) {
|
|
1356
|
+
options = Util.shallowAssignImmutable({}, options);
|
|
1352
1357
|
let prop;
|
|
1353
|
-
const props = ['id', 'GEOID', 'GEOID10', 'GEOID20', 'GEOID30'];
|
|
1358
|
+
const props = ['id', 'GEOID', 'GEOID10', 'GEOID20', 'GEOID30', 'DISTRICT', 'DISTRICTNO', 'DISTRICTNAME'];
|
|
1354
1359
|
if (col && col.features && col.features.length > 0) {
|
|
1355
1360
|
let f = col.features[0];
|
|
1356
1361
|
if (f.properties.id !== undefined)
|
|
1357
|
-
return;
|
|
1358
|
-
props.forEach(p => {
|
|
1359
|
-
prop
|
|
1362
|
+
return; // short-cut - assume if 'id' is set, we're all good.
|
|
1363
|
+
props.forEach(p => {
|
|
1364
|
+
if (prop === undefined)
|
|
1365
|
+
if (f.properties[p] !== undefined)
|
|
1366
|
+
prop = p;
|
|
1367
|
+
else {
|
|
1368
|
+
p = p.toLowerCase();
|
|
1369
|
+
if (f.properties[p] !== undefined)
|
|
1370
|
+
prop = p;
|
|
1371
|
+
}
|
|
1372
|
+
});
|
|
1360
1373
|
if (prop)
|
|
1361
1374
|
col.features.forEach(f => { f.properties.id = f.properties[prop]; });
|
|
1362
1375
|
else {
|
|
@@ -1366,6 +1379,77 @@ function geoEnsureID(col) {
|
|
|
1366
1379
|
}
|
|
1367
1380
|
}
|
|
1368
1381
|
exports.geoEnsureID = geoEnsureID;
|
|
1382
|
+
function geoNormalizeFeature(f, options) {
|
|
1383
|
+
options = Util.shallowAssignImmutable({}, options);
|
|
1384
|
+
if (f && f.geometry && f.geometry.type === 'MultiPolygon' && f.geometry.coordinates) {
|
|
1385
|
+
let multiPoly = f.geometry.coordinates;
|
|
1386
|
+
// Convert degenerate MultiPolygon to Polygon
|
|
1387
|
+
if (multiPoly.length == 1) {
|
|
1388
|
+
f.geometry.type = 'Polygon';
|
|
1389
|
+
f.geometry.coordinates = multiPoly[0];
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
else if (f && f.geometry && f.geometry.type === 'Point' && f.geometry.coordinates) {
|
|
1393
|
+
while (Array.isArray(f.geometry.coordinates[0]))
|
|
1394
|
+
f.geometry.coordinates = f.geometry.coordinates[0];
|
|
1395
|
+
}
|
|
1396
|
+
else if (options.checkRewind)
|
|
1397
|
+
// Various tools do not guarantee valid GeoJSON winding rules. Verify it since internal processing
|
|
1398
|
+
// assumes it is correct.
|
|
1399
|
+
Poly.featureRewind(f);
|
|
1400
|
+
return f;
|
|
1401
|
+
}
|
|
1402
|
+
exports.geoNormalizeFeature = geoNormalizeFeature;
|
|
1403
|
+
function onlyPolygons(col) {
|
|
1404
|
+
if (col && Array.isArray(col.features))
|
|
1405
|
+
for (let i = 0; i < col.features.length; i++) {
|
|
1406
|
+
let f = col.features[i];
|
|
1407
|
+
if (f.geometry && f.geometry.type === 'MultiPolygon')
|
|
1408
|
+
return false;
|
|
1409
|
+
}
|
|
1410
|
+
return true;
|
|
1411
|
+
}
|
|
1412
|
+
function mergePolygon(f1, f2) {
|
|
1413
|
+
if (!f1)
|
|
1414
|
+
return f2;
|
|
1415
|
+
if (!f2)
|
|
1416
|
+
return f1;
|
|
1417
|
+
if (f1.geometry.type !== 'Polygon' && f1.geometry.type !== 'MultiPolygon')
|
|
1418
|
+
return f1;
|
|
1419
|
+
if (f2.geometry.type !== 'Polygon' && f2.geometry.type !== 'MultiPolygon')
|
|
1420
|
+
return f2;
|
|
1421
|
+
if (f1.geometry.type === 'Polygon') {
|
|
1422
|
+
f1.geometry.type = 'MultiPolygon';
|
|
1423
|
+
if (f2.geometry.type === 'Polygon')
|
|
1424
|
+
f1.geometry.coordinates = [f1.geometry.coordinates, f2.geometry.coordinates];
|
|
1425
|
+
else
|
|
1426
|
+
f1.geometry.coordinates = [f1.geometry.coordinates, ...f2.geometry.coordinates];
|
|
1427
|
+
}
|
|
1428
|
+
else {
|
|
1429
|
+
if (f2.geometry.type === 'Polygon')
|
|
1430
|
+
f1.geometry.coordinates.push(f2.geometry.coordinates);
|
|
1431
|
+
else
|
|
1432
|
+
f1.geometry.coordinates = [...f1.geometry.coordinates, ...f2.geometry.coordinates];
|
|
1433
|
+
}
|
|
1434
|
+
return f1;
|
|
1435
|
+
}
|
|
1436
|
+
function geoNormalizeCollection(col, options) {
|
|
1437
|
+
options = Util.shallowAssignImmutable(NormalizeAll, options);
|
|
1438
|
+
// Normalize individual features
|
|
1439
|
+
if (col && Array.isArray(col.features))
|
|
1440
|
+
col.features.forEach((f) => geoNormalizeFeature(f, options));
|
|
1441
|
+
// Ensure ID
|
|
1442
|
+
if (options.ensureID)
|
|
1443
|
+
geoEnsureID(col, options);
|
|
1444
|
+
// Merge polygons into multi-polygons based on id?
|
|
1445
|
+
if (options.ensureID && options.joinPolygons && onlyPolygons(col)) {
|
|
1446
|
+
let map = {};
|
|
1447
|
+
col.features.forEach(f => { let id = f.properties.id; map[id] = mergePolygon(map[id], f); });
|
|
1448
|
+
col.features = Object.values(map);
|
|
1449
|
+
}
|
|
1450
|
+
return col;
|
|
1451
|
+
}
|
|
1452
|
+
exports.geoNormalizeCollection = geoNormalizeCollection;
|
|
1369
1453
|
function geoCollectionToMap(col) {
|
|
1370
1454
|
if (col == null)
|
|
1371
1455
|
return null;
|
|
@@ -6874,16 +6958,68 @@ function polyDistance(poly, x, y) {
|
|
|
6874
6958
|
let pp = P.polyNormalize(poly);
|
|
6875
6959
|
if (pp == null)
|
|
6876
6960
|
return 0;
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6961
|
+
// First find if it is contained in one of the outer polygons, or outside all outer polygons
|
|
6962
|
+
let iContaining = -1;
|
|
6963
|
+
let maxOutside = -Infinity;
|
|
6964
|
+
let minInside = Infinity;
|
|
6965
|
+
let noholes = true;
|
|
6966
|
+
PP.polyPackEachRing(pp, (b, iPoly, iRing, iOffset, nPoints) => {
|
|
6967
|
+
// If we have determined we are inside this polygon, keep track of whether it has holes
|
|
6968
|
+
if (iContaining == iPoly && iRing > 0)
|
|
6969
|
+
noholes = false;
|
|
6970
|
+
// Don't process rings
|
|
6971
|
+
if (iRing > 0)
|
|
6972
|
+
return;
|
|
6973
|
+
// OK, get distance
|
|
6974
|
+
let forEachPointPair = (iter) => {
|
|
6975
|
+
PP.polyPackEachRing(pp, (b, iInteriorPoly, iRing, iOffset, nPoints) => {
|
|
6976
|
+
if (iRing || iInteriorPoly != iPoly)
|
|
6977
|
+
return;
|
|
6978
|
+
let iFirst = iOffset;
|
|
6979
|
+
let iLast = iOffset + nPoints * 2;
|
|
6980
|
+
let iSecond = iLast - 2;
|
|
6981
|
+
for (; iFirst < iLast; iSecond = iFirst, iFirst += 2)
|
|
6982
|
+
iter(b[iFirst], b[iFirst + 1], b[iSecond], b[iSecond + 1]);
|
|
6983
|
+
});
|
|
6984
|
+
};
|
|
6985
|
+
let dist = pointToPolygonDist(x, y, forEachPointPair);
|
|
6986
|
+
// If inside, is it closest inside (deal with multipolygons that self-contain - think filled donut)
|
|
6987
|
+
if (dist > 0 && dist < minInside) {
|
|
6988
|
+
iContaining = iPoly;
|
|
6989
|
+
minInside = dist;
|
|
6990
|
+
noholes = true;
|
|
6991
|
+
}
|
|
6992
|
+
else if (dist < 0)
|
|
6993
|
+
maxOutside = Math.max(maxOutside, dist);
|
|
6994
|
+
});
|
|
6995
|
+
if (iContaining < 0)
|
|
6996
|
+
return maxOutside;
|
|
6997
|
+
if (noholes)
|
|
6998
|
+
return minInside;
|
|
6999
|
+
// OK, now need to worry about holes in the polygon it is contained in
|
|
7000
|
+
PP.polyPackEachRing(pp, (b, iPoly, iRing, iOffset, nPoints) => {
|
|
7001
|
+
// Only want to look at the holes for the containing polygon
|
|
7002
|
+
if (iPoly != iContaining || iRing == 0)
|
|
7003
|
+
return;
|
|
7004
|
+
// Compute distance to those holes
|
|
7005
|
+
let forEachPointPair = (iter) => {
|
|
7006
|
+
PP.polyPackEachRing(pp, (b, iInteriorPoly, iRing, iOffset, nPoints) => {
|
|
7007
|
+
if (iInteriorPoly != iContaining || iRing == 0)
|
|
7008
|
+
return;
|
|
7009
|
+
let iFirst = iOffset;
|
|
7010
|
+
let iLast = iOffset + nPoints * 2;
|
|
7011
|
+
let iSecond = iLast - 2;
|
|
7012
|
+
for (; iFirst < iLast; iSecond = iFirst, iFirst += 2)
|
|
7013
|
+
iter(b[iFirst], b[iFirst + 1], b[iSecond], b[iSecond + 1]);
|
|
7014
|
+
});
|
|
7015
|
+
};
|
|
7016
|
+
// Negate distance since dealing with holes
|
|
7017
|
+
let dist = -pointToPolygonDist(x, y, forEachPointPair);
|
|
7018
|
+
// We take the min to either get a negative value (inside hole) or a smaller positive value
|
|
7019
|
+
// (outside hole but close to hole boundary).
|
|
7020
|
+
minInside = Math.min(minInside, dist);
|
|
7021
|
+
});
|
|
7022
|
+
return minInside;
|
|
6887
7023
|
}
|
|
6888
7024
|
exports.polyDistance = polyDistance;
|
|
6889
7025
|
//
|
|
@@ -6922,7 +7058,7 @@ function polyLabel(poly, precision, debug) {
|
|
|
6922
7058
|
let iLast = iOffset + nPoints * 2;
|
|
6923
7059
|
let iSecond = iLast - 2;
|
|
6924
7060
|
for (; iFirst < iLast; iSecond = iFirst, iFirst += 2)
|
|
6925
|
-
iter(
|
|
7061
|
+
iter(b[iFirst], b[iFirst + 1], b[iSecond], b[iSecond + 1]);
|
|
6926
7062
|
});
|
|
6927
7063
|
};
|
|
6928
7064
|
// find the bounding box of the outer ring
|
|
@@ -6996,34 +7132,13 @@ class Cell {
|
|
|
6996
7132
|
}
|
|
6997
7133
|
// signed distance from point to polygon outline (negative if point is outside)
|
|
6998
7134
|
function pointToPolygonDist(x, y, forEachPointPair) {
|
|
6999
|
-
let iPolyLast = -1;
|
|
7000
|
-
let iRingLast = -1;
|
|
7001
7135
|
let inside = false;
|
|
7002
|
-
let thisInside = false;
|
|
7003
|
-
let useInside = false;
|
|
7004
|
-
let isHole = false;
|
|
7005
7136
|
let minDistSq = Infinity;
|
|
7006
|
-
forEachPointPair((
|
|
7007
|
-
if (iPoly != iPolyLast || iRing != iRingLast) {
|
|
7008
|
-
if (useInside)
|
|
7009
|
-
inside = isHole ? !thisInside : thisInside;
|
|
7010
|
-
iPolyLast = iPoly;
|
|
7011
|
-
iRingLast = iRing;
|
|
7012
|
-
thisInside = false;
|
|
7013
|
-
useInside = false;
|
|
7014
|
-
isHole = false;
|
|
7015
|
-
}
|
|
7137
|
+
forEachPointPair((ax, ay, bx, by) => {
|
|
7016
7138
|
if ((ay > y !== by > y) && (x < (bx - ax) * (y - ay) / (by - ay) + ax))
|
|
7017
|
-
|
|
7018
|
-
|
|
7019
|
-
if (thisDistSq < minDistSq) {
|
|
7020
|
-
minDistSq = thisDistSq;
|
|
7021
|
-
useInside = true;
|
|
7022
|
-
isHole = iRing != 0;
|
|
7023
|
-
}
|
|
7139
|
+
inside = !inside;
|
|
7140
|
+
minDistSq = Math.min(minDistSq, getSegDistSq(x, y, ax, ay, bx, by));
|
|
7024
7141
|
});
|
|
7025
|
-
if (useInside)
|
|
7026
|
-
inside = isHole ? !thisInside : thisInside;
|
|
7027
7142
|
return (inside ? 1 : -1) * Math.sqrt(minDistSq);
|
|
7028
7143
|
}
|
|
7029
7144
|
// get polygon centroid
|
|
@@ -7033,7 +7148,7 @@ function getCentroidCell(forEachPointPair) {
|
|
|
7033
7148
|
let y = 0;
|
|
7034
7149
|
let fx;
|
|
7035
7150
|
let fy;
|
|
7036
|
-
forEachPointPair((
|
|
7151
|
+
forEachPointPair((ax, ay, bx, by) => {
|
|
7037
7152
|
if (fx === undefined)
|
|
7038
7153
|
fx = ax, fy = ay;
|
|
7039
7154
|
let f = ax * by - bx * ay;
|
|
@@ -8520,8 +8635,9 @@ function intpt(f) {
|
|
|
8520
8635
|
return { x: x, y: y };
|
|
8521
8636
|
}
|
|
8522
8637
|
const DefaultSimplifyOptions = { minArea: 500 };
|
|
8523
|
-
function log(s) {
|
|
8524
|
-
|
|
8638
|
+
function log(emitlog, s) {
|
|
8639
|
+
if (emitlog)
|
|
8640
|
+
console.log(s);
|
|
8525
8641
|
}
|
|
8526
8642
|
//
|
|
8527
8643
|
// topoSimplifyCollection:
|
|
@@ -8545,10 +8661,10 @@ function topoSimplifyCollection(col, options) {
|
|
|
8545
8661
|
let elapsedTotal = new Util.Elapsed();
|
|
8546
8662
|
let elapsed = new Util.Elapsed();
|
|
8547
8663
|
let topo = topoFromCollection(col);
|
|
8548
|
-
log(`topoSimplifyCollection: fromCollection: ${Math.round(elapsed.ms())}ms`);
|
|
8664
|
+
log(options.log, `topoSimplifyCollection: fromCollection: ${Math.round(elapsed.ms())}ms`);
|
|
8549
8665
|
elapsed.start();
|
|
8550
8666
|
topo = TopoSimplify.presimplify(topo, TopoSimplify['sphericalTriangleArea']);
|
|
8551
|
-
log(`topoSimplifyCollection: presimplify: ${Math.round(elapsed.ms())}ms`);
|
|
8667
|
+
log(options.log, `topoSimplifyCollection: presimplify: ${Math.round(elapsed.ms())}ms`);
|
|
8552
8668
|
elapsed.start();
|
|
8553
8669
|
// Keep iterating on removing simplification from degenerate shapes
|
|
8554
8670
|
let nTries = 1;
|
|
@@ -8613,17 +8729,17 @@ function topoSimplifyCollection(col, options) {
|
|
|
8613
8729
|
keepTiny.set(f, f);
|
|
8614
8730
|
keepArcs(topo, oOld.arcs, 0); // keeps all points to avoid reprocessing these tiny shapes
|
|
8615
8731
|
nBad++, nTiny++;
|
|
8616
|
-
log(`topoSimplifyCollection: ${f.properties.id}: increasing feature fidelity because intpt dist is ${d}`);
|
|
8732
|
+
log(options.log, `topoSimplifyCollection: ${f.properties.id}: increasing feature fidelity because intpt dist is ${d}`);
|
|
8617
8733
|
}
|
|
8618
8734
|
}
|
|
8619
8735
|
}
|
|
8620
8736
|
}
|
|
8621
8737
|
});
|
|
8622
|
-
log(`topoSimplifyCollection: pass ${nTries}: ${nBad} (${nTiny} tiny) of ${col.features.length} features are degenerate`);
|
|
8738
|
+
log(options.log, `topoSimplifyCollection: pass ${nTries}: ${nBad} (${nTiny} tiny) of ${col.features.length} features are degenerate`);
|
|
8623
8739
|
// If not making progress, keep more points
|
|
8624
8740
|
if (nBad >= nBadLast) {
|
|
8625
8741
|
keepweight /= 10;
|
|
8626
|
-
log(`topoSimplifyCollection: pass ${nTries}: reducing weight limit to ${keepweight}`);
|
|
8742
|
+
log(options.log, `topoSimplifyCollection: pass ${nTries}: reducing weight limit to ${keepweight}`);
|
|
8627
8743
|
}
|
|
8628
8744
|
nBadLast = nBad;
|
|
8629
8745
|
if (nBad && nTries > MAX_TRIES)
|
|
@@ -8635,7 +8751,7 @@ function topoSimplifyCollection(col, options) {
|
|
|
8635
8751
|
}
|
|
8636
8752
|
nTries++;
|
|
8637
8753
|
}
|
|
8638
|
-
log(`topoSimplifyCollection: total elapsed time: ${bigTimeString(elapsedTotal.ms())}`);
|
|
8754
|
+
log(options.log, `topoSimplifyCollection: total elapsed time: ${bigTimeString(elapsedTotal.ms())}`);
|
|
8639
8755
|
return col;
|
|
8640
8756
|
}
|
|
8641
8757
|
exports.topoSimplifyCollection = topoSimplifyCollection;
|