mapshaper 0.6.116 → 0.6.118
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/mapshaper.js +1449 -184
- package/package.json +1 -1
- package/www/index.html +4 -0
- package/www/mapshaper-gui.js +214 -17
- package/www/mapshaper.js +1449 -184
- package/www/page.css +12 -0
package/www/mapshaper.js
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
get expandoBuffer () { return expandoBuffer; },
|
|
20
20
|
get extend () { return extend$1; },
|
|
21
21
|
get extendBuffer () { return extendBuffer; },
|
|
22
|
-
get find () { return find; },
|
|
22
|
+
get find () { return find$1; },
|
|
23
23
|
get findMedian () { return findMedian; },
|
|
24
24
|
get findQuantile () { return findQuantile; },
|
|
25
25
|
get findRankByValue () { return findRankByValue; },
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
get isBoolean () { return isBoolean; },
|
|
54
54
|
get isDate () { return isDate; },
|
|
55
55
|
get isEven () { return isEven; },
|
|
56
|
-
get isFiniteNumber () { return isFiniteNumber; },
|
|
56
|
+
get isFiniteNumber () { return isFiniteNumber$1; },
|
|
57
57
|
get isFunction () { return isFunction; },
|
|
58
58
|
get isInteger () { return isInteger; },
|
|
59
59
|
get isNonNegNumber () { return isNonNegNumber; },
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
get mergeNames () { return mergeNames; },
|
|
71
71
|
get numToStr () { return numToStr; },
|
|
72
72
|
get parseIntlNumber () { return parseIntlNumber; },
|
|
73
|
-
get parseNumber () { return parseNumber; },
|
|
73
|
+
get parseNumber () { return parseNumber$1; },
|
|
74
74
|
get parsePercent () { return parsePercent; },
|
|
75
75
|
get parseString () { return parseString; },
|
|
76
76
|
get pickOne () { return pickOne; },
|
|
@@ -188,7 +188,7 @@
|
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
// Similar to isFinite() but does not coerce strings or other types
|
|
191
|
-
function isFiniteNumber(val) {
|
|
191
|
+
function isFiniteNumber$1(val) {
|
|
192
192
|
return isValidNumber(val) && val !== Infinity && val !== -Infinity;
|
|
193
193
|
}
|
|
194
194
|
|
|
@@ -442,7 +442,7 @@
|
|
|
442
442
|
}, true);
|
|
443
443
|
}
|
|
444
444
|
|
|
445
|
-
function find(arr, test, ctx) {
|
|
445
|
+
function find$1(arr, test, ctx) {
|
|
446
446
|
var matches = arr.filter(test, ctx);
|
|
447
447
|
return matches.length === 0 ? null : matches[0];
|
|
448
448
|
}
|
|
@@ -1182,7 +1182,7 @@
|
|
|
1182
1182
|
// Use null instead of NaN for unparsable values
|
|
1183
1183
|
// (in part because if NaN is used, empty strings get converted to "NaN"
|
|
1184
1184
|
// when re-exported).
|
|
1185
|
-
function parseNumber(raw) {
|
|
1185
|
+
function parseNumber$1(raw) {
|
|
1186
1186
|
return parseToNum(raw, cleanNumericString);
|
|
1187
1187
|
}
|
|
1188
1188
|
|
|
@@ -6399,41 +6399,52 @@
|
|
|
6399
6399
|
};
|
|
6400
6400
|
}
|
|
6401
6401
|
|
|
6402
|
-
// Used for building topology
|
|
6402
|
+
// Used for building topology.
|
|
6403
|
+
//
|
|
6404
|
+
// Every arc is stored as a reference into some source coordinate arrays
|
|
6405
|
+
// (srcXX/srcYY) plus an inclusive [start..end] index range. The caller
|
|
6406
|
+
// decides which buffer to hand in: the shared xx/yy for normal arcs, or
|
|
6407
|
+
// a freshly-allocated merged buffer for split/wrap-around arcs. ArcIndex
|
|
6408
|
+
// doesn't care which — no sentinels, no special cases in its own code.
|
|
6403
6409
|
//
|
|
6404
6410
|
function ArcIndex(pointCount) {
|
|
6405
6411
|
var hashTableSize = Math.floor(pointCount * 0.25 + 1),
|
|
6406
6412
|
hash = getXYHash(hashTableSize),
|
|
6407
6413
|
hashTable = new Int32Array(hashTableSize),
|
|
6408
6414
|
chainIds = [],
|
|
6409
|
-
|
|
6415
|
+
arcSrcXX = [],
|
|
6416
|
+
arcSrcYY = [],
|
|
6417
|
+
arcStart = [],
|
|
6418
|
+
arcEnd = [],
|
|
6410
6419
|
arcPoints = 0;
|
|
6411
6420
|
|
|
6412
|
-
|
|
6421
|
+
initializeArray(hashTable, -1);
|
|
6413
6422
|
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
|
|
6418
|
-
|
|
6423
|
+
// Register an arc whose coordinates are `srcXX[start..end]`, `srcYY[start..end]`
|
|
6424
|
+
// (inclusive). Returns the new arc id.
|
|
6425
|
+
this.addArc = function(srcXX, srcYY, start, end) {
|
|
6426
|
+
var arcId = arcSrcXX.length,
|
|
6427
|
+
key = hash(srcXX[end], srcYY[end]);
|
|
6428
|
+
chainIds.push(hashTable[key]);
|
|
6419
6429
|
hashTable[key] = arcId;
|
|
6420
|
-
|
|
6421
|
-
|
|
6422
|
-
|
|
6430
|
+
arcSrcXX.push(srcXX);
|
|
6431
|
+
arcSrcYY.push(srcYY);
|
|
6432
|
+
arcStart.push(start);
|
|
6433
|
+
arcEnd.push(end);
|
|
6434
|
+
arcPoints += end - start + 1;
|
|
6423
6435
|
return arcId;
|
|
6424
6436
|
};
|
|
6425
6437
|
|
|
6426
|
-
// Look for a previously generated arc with the same sequence of coords, but
|
|
6427
|
-
// opposite direction. (This program uses the convention of CW for
|
|
6428
|
-
//
|
|
6438
|
+
// Look for a previously generated arc with the same sequence of coords, but
|
|
6439
|
+
// in the opposite direction. (This program uses the convention of CW for
|
|
6440
|
+
// space-enclosing rings, CCW for holes, so coincident boundaries should
|
|
6441
|
+
// contain the same points in reverse sequence.)
|
|
6429
6442
|
//
|
|
6430
6443
|
this.findDuplicateArc = function(xx, yy, start, end, getNext, getPrev) {
|
|
6431
|
-
// First, look for a reverse match
|
|
6432
6444
|
var arcId = findArcNeighbor(xx, yy, start, end, getNext);
|
|
6433
6445
|
if (arcId === null) {
|
|
6434
|
-
// Look for forward match
|
|
6435
|
-
//
|
|
6436
|
-
// Shapefiles sometimes have duplicate paths)
|
|
6446
|
+
// Look for forward match (abnormal topology, but we accept it because
|
|
6447
|
+
// in-the-wild Shapefiles sometimes have duplicate paths).
|
|
6437
6448
|
arcId = findArcNeighbor(xx, yy, end, start, getPrev);
|
|
6438
6449
|
} else {
|
|
6439
6450
|
arcId = ~arcId;
|
|
@@ -6445,17 +6456,14 @@
|
|
|
6445
6456
|
var next = getNext(start),
|
|
6446
6457
|
key = hash(xx[start], yy[start]),
|
|
6447
6458
|
arcId = hashTable[key],
|
|
6448
|
-
|
|
6449
|
-
|
|
6459
|
+
sx, sy, s, e;
|
|
6450
6460
|
while (arcId != -1) {
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
//
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
if (arcX[0] === xx[end] && arcX[len-1] === xx[start] && arcX[len-2] === xx[next] &&
|
|
6458
|
-
arcY[0] === yy[end] && arcY[len-1] === yy[start] && arcY[len-2] === yy[next]) {
|
|
6461
|
+
sx = arcSrcXX[arcId]; sy = arcSrcYY[arcId];
|
|
6462
|
+
s = arcStart[arcId]; e = arcEnd[arcId];
|
|
6463
|
+
// Check endpoints and one segment. A more rigorous match would compare
|
|
6464
|
+
// every segment, but that's slower and this is sufficient in practice.
|
|
6465
|
+
if (sx[s] === xx[end] && sx[e] === xx[start] && sx[e - 1] === xx[next] &&
|
|
6466
|
+
sy[s] === yy[end] && sy[e] === yy[start] && sy[e - 1] === yy[next]) {
|
|
6459
6467
|
return arcId;
|
|
6460
6468
|
}
|
|
6461
6469
|
arcId = chainIds[arcId];
|
|
@@ -6464,22 +6472,31 @@
|
|
|
6464
6472
|
}
|
|
6465
6473
|
|
|
6466
6474
|
this.getVertexData = function() {
|
|
6467
|
-
var
|
|
6468
|
-
|
|
6469
|
-
|
|
6475
|
+
var arcCount = arcSrcXX.length,
|
|
6476
|
+
destXX = new Float64Array(arcPoints),
|
|
6477
|
+
destYY = new Float64Array(arcPoints),
|
|
6478
|
+
nn = new Uint32Array(arcCount),
|
|
6470
6479
|
copied = 0,
|
|
6471
|
-
|
|
6472
|
-
for (
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6480
|
+
sx, sy, s, e, len, i;
|
|
6481
|
+
for (i = 0; i < arcCount; i++) {
|
|
6482
|
+
sx = arcSrcXX[i]; sy = arcSrcYY[i];
|
|
6483
|
+
s = arcStart[i]; e = arcEnd[i];
|
|
6484
|
+
len = e - s + 1;
|
|
6485
|
+
if (sx.subarray) {
|
|
6486
|
+
destXX.set(sx.subarray(s, e + 1), copied);
|
|
6487
|
+
destYY.set(sy.subarray(s, e + 1), copied);
|
|
6488
|
+
} else {
|
|
6489
|
+
for (var k = 0; k < len; k++) {
|
|
6490
|
+
destXX[copied + k] = sx[s + k];
|
|
6491
|
+
destYY[copied + k] = sy[s + k];
|
|
6492
|
+
}
|
|
6493
|
+
}
|
|
6477
6494
|
nn[i] = len;
|
|
6478
6495
|
copied += len;
|
|
6479
6496
|
}
|
|
6480
6497
|
return {
|
|
6481
|
-
xx:
|
|
6482
|
-
yy:
|
|
6498
|
+
xx: destXX,
|
|
6499
|
+
yy: destYY,
|
|
6483
6500
|
nn: nn
|
|
6484
6501
|
};
|
|
6485
6502
|
};
|
|
@@ -6578,18 +6595,15 @@
|
|
|
6578
6595
|
var pointCount = xx.length,
|
|
6579
6596
|
chainIds = initPointChains(xx, yy),
|
|
6580
6597
|
pathIds = initPathIds(pointCount, nn),
|
|
6598
|
+
pathIsRing = initPathIsRing(nn, xx, yy),
|
|
6599
|
+
isNode = computeIsNode(nn, xx, yy, chainIds, pathIds, pathIsRing),
|
|
6581
6600
|
index = new ArcIndex(pointCount),
|
|
6582
|
-
slice = usingTypedArrays() ? xx.subarray : Array.prototype.slice,
|
|
6583
6601
|
paths, retn;
|
|
6584
6602
|
paths = convertPaths(nn);
|
|
6585
6603
|
retn = index.getVertexData();
|
|
6586
6604
|
retn.paths = paths;
|
|
6587
6605
|
return retn;
|
|
6588
6606
|
|
|
6589
|
-
function usingTypedArrays() {
|
|
6590
|
-
return !!(xx.subarray && yy.subarray);
|
|
6591
|
-
}
|
|
6592
|
-
|
|
6593
6607
|
function convertPaths(nn) {
|
|
6594
6608
|
var paths = [],
|
|
6595
6609
|
pointId = 0,
|
|
@@ -6602,28 +6616,28 @@
|
|
|
6602
6616
|
return paths;
|
|
6603
6617
|
}
|
|
6604
6618
|
|
|
6619
|
+
// Fast neighbour lookups using the precomputed pathIsRing cache.
|
|
6620
|
+
// Used by findDuplicateArc (via addEdge/addSplitEdge) and by addRing.
|
|
6605
6621
|
function nextPoint(id) {
|
|
6606
6622
|
var partId = pathIds[id],
|
|
6607
6623
|
nextId = id + 1;
|
|
6608
6624
|
if (nextId < pointCount && pathIds[nextId] === partId) {
|
|
6609
|
-
return
|
|
6625
|
+
return nextId;
|
|
6610
6626
|
}
|
|
6611
|
-
|
|
6612
|
-
return sameXY(id, id - len + 1) ? id - len + 2 : -1;
|
|
6627
|
+
return pathIsRing[partId] ? id - nn[partId] + 2 : -1;
|
|
6613
6628
|
}
|
|
6614
6629
|
|
|
6615
6630
|
function prevPoint(id) {
|
|
6616
6631
|
var partId = pathIds[id],
|
|
6617
6632
|
prevId = id - 1;
|
|
6618
6633
|
if (prevId >= 0 && pathIds[prevId] === partId) {
|
|
6619
|
-
return
|
|
6634
|
+
return prevId;
|
|
6620
6635
|
}
|
|
6621
|
-
|
|
6622
|
-
return sameXY(id, id + len - 1) ? id + len - 2 : -1;
|
|
6636
|
+
return pathIsRing[partId] ? id + nn[partId] - 2 : -1;
|
|
6623
6637
|
}
|
|
6624
6638
|
|
|
6625
|
-
function
|
|
6626
|
-
return
|
|
6639
|
+
function pointIsArcEndpoint(id) {
|
|
6640
|
+
return isNode[id] === 1;
|
|
6627
6641
|
}
|
|
6628
6642
|
|
|
6629
6643
|
// Convert a non-topological path to one or more topological arcs
|
|
@@ -6666,49 +6680,9 @@
|
|
|
6666
6680
|
return arcIds;
|
|
6667
6681
|
}
|
|
6668
6682
|
|
|
6669
|
-
// Test if a point @id is an endpoint of a topological path
|
|
6670
|
-
function pointIsArcEndpoint(id) {
|
|
6671
|
-
var id2 = chainIds[id],
|
|
6672
|
-
prev = prevPoint(id),
|
|
6673
|
-
next = nextPoint(id),
|
|
6674
|
-
prev2, next2;
|
|
6675
|
-
if (prev == -1 || next == -1) {
|
|
6676
|
-
// @id is an endpoint if it is the start or end of an open path
|
|
6677
|
-
return true;
|
|
6678
|
-
}
|
|
6679
|
-
while (id != id2) {
|
|
6680
|
-
prev2 = prevPoint(id2);
|
|
6681
|
-
next2 = nextPoint(id2);
|
|
6682
|
-
if (prev2 == -1 || next2 == -1 || brokenEdge(prev, next, prev2, next2)) {
|
|
6683
|
-
// there is a discontinuity at @id -- point is arc endpoint
|
|
6684
|
-
return true;
|
|
6685
|
-
}
|
|
6686
|
-
id2 = chainIds[id2];
|
|
6687
|
-
}
|
|
6688
|
-
return false;
|
|
6689
|
-
}
|
|
6690
|
-
|
|
6691
|
-
// a and b are two vertices with the same x, y coordinates
|
|
6692
|
-
// test if the segments on either side of them are also identical
|
|
6693
|
-
function brokenEdge(aprev, anext, bprev, bnext) {
|
|
6694
|
-
var apx = xx[aprev],
|
|
6695
|
-
anx = xx[anext],
|
|
6696
|
-
bpx = xx[bprev],
|
|
6697
|
-
bnx = xx[bnext],
|
|
6698
|
-
apy = yy[aprev],
|
|
6699
|
-
any = yy[anext],
|
|
6700
|
-
bpy = yy[bprev],
|
|
6701
|
-
bny = yy[bnext];
|
|
6702
|
-
if (apx == bnx && anx == bpx && apy == bny && any == bpy ||
|
|
6703
|
-
apx == bpx && anx == bnx && apy == bpy && any == bny) {
|
|
6704
|
-
return false;
|
|
6705
|
-
}
|
|
6706
|
-
return true;
|
|
6707
|
-
}
|
|
6708
|
-
|
|
6709
6683
|
function mergeArcParts(src, startId, endId, startId2, endId2) {
|
|
6710
6684
|
var len = endId - startId + endId2 - startId2 + 2,
|
|
6711
|
-
ArrayClass =
|
|
6685
|
+
ArrayClass = src.subarray ? Float64Array : Array,
|
|
6712
6686
|
dest = new ArrayClass(len),
|
|
6713
6687
|
j = 0, i;
|
|
6714
6688
|
for (i=startId; i <= endId; i++) {
|
|
@@ -6723,8 +6697,12 @@
|
|
|
6723
6697
|
function addSplitEdge(start1, end1, start2, end2) {
|
|
6724
6698
|
var arcId = index.findDuplicateArc(xx, yy, start1, end2, nextPoint, prevPoint);
|
|
6725
6699
|
if (arcId === null) {
|
|
6726
|
-
|
|
6727
|
-
|
|
6700
|
+
// Coordinates for a split (wrap-around) edge don't form a contiguous
|
|
6701
|
+
// slice of xx/yy, so we build a standalone buffer and hand it to the
|
|
6702
|
+
// index as the arc's source.
|
|
6703
|
+
var mx = mergeArcParts(xx, start1, end1, start2, end2),
|
|
6704
|
+
my = mergeArcParts(yy, start1, end1, start2, end2);
|
|
6705
|
+
arcId = index.addArc(mx, my, 0, mx.length - 1);
|
|
6728
6706
|
}
|
|
6729
6707
|
return arcId;
|
|
6730
6708
|
}
|
|
@@ -6733,8 +6711,7 @@
|
|
|
6733
6711
|
// search for a matching edge that has already been generated
|
|
6734
6712
|
var arcId = index.findDuplicateArc(xx, yy, start, end, nextPoint, prevPoint);
|
|
6735
6713
|
if (arcId === null) {
|
|
6736
|
-
arcId = index.addArc(
|
|
6737
|
-
slice.call(yy, start, end + 1));
|
|
6714
|
+
arcId = index.addArc(xx, yy, start, end);
|
|
6738
6715
|
}
|
|
6739
6716
|
return arcId;
|
|
6740
6717
|
}
|
|
@@ -6777,6 +6754,87 @@
|
|
|
6777
6754
|
return pathIds;
|
|
6778
6755
|
}
|
|
6779
6756
|
|
|
6757
|
+
// Per-path flag: 1 if the path is a closed ring (first vertex coincides with
|
|
6758
|
+
// last vertex), else 0. Computed once so that prevPoint()/nextPoint() don't
|
|
6759
|
+
// need to call sameXY() at path boundaries.
|
|
6760
|
+
//
|
|
6761
|
+
function initPathIsRing(nn, xx, yy) {
|
|
6762
|
+
var pathCount = nn.length,
|
|
6763
|
+
pathIsRing = new Uint8Array(pathCount),
|
|
6764
|
+
pstart = 0, len;
|
|
6765
|
+
for (var p = 0; p < pathCount; p++) {
|
|
6766
|
+
len = nn[p];
|
|
6767
|
+
if (len > 1 &&
|
|
6768
|
+
xx[pstart] === xx[pstart + len - 1] &&
|
|
6769
|
+
yy[pstart] === yy[pstart + len - 1]) {
|
|
6770
|
+
pathIsRing[p] = 1;
|
|
6771
|
+
}
|
|
6772
|
+
pstart += len;
|
|
6773
|
+
}
|
|
6774
|
+
return pathIsRing;
|
|
6775
|
+
}
|
|
6776
|
+
|
|
6777
|
+
// Decide, for every point, whether it is a topological node (an arc
|
|
6778
|
+
// endpoint). Being a node is a property of the entire coincident-point
|
|
6779
|
+
// chain: all points sharing a location agree on the answer. So we walk
|
|
6780
|
+
// each chain at most once — O(n) total — instead of doing the walk
|
|
6781
|
+
// independently at every point (O(n * K), quadratic per chain).
|
|
6782
|
+
//
|
|
6783
|
+
// A chain's points are nodes iff:
|
|
6784
|
+
// - any member has a missing neighbour (open-path endpoint), or
|
|
6785
|
+
// - two members disagree on the unordered pair of neighbour coords.
|
|
6786
|
+
//
|
|
6787
|
+
function computeIsNode(nn, xx, yy, chainIds, pathIds, pathIsRing) {
|
|
6788
|
+
var n = xx.length,
|
|
6789
|
+
isNode = new Uint8Array(n),
|
|
6790
|
+
done = new Uint8Array(n);
|
|
6791
|
+
|
|
6792
|
+
function nextPoint(id) {
|
|
6793
|
+
var part = pathIds[id], nid = id + 1;
|
|
6794
|
+
if (nid < n && pathIds[nid] === part) return nid;
|
|
6795
|
+
return pathIsRing[part] ? id - nn[part] + 2 : -1;
|
|
6796
|
+
}
|
|
6797
|
+
|
|
6798
|
+
function prevPoint(id) {
|
|
6799
|
+
var part = pathIds[id], pid = id - 1;
|
|
6800
|
+
if (pid >= 0 && pathIds[pid] === part) return pid;
|
|
6801
|
+
return pathIsRing[part] ? id + nn[part] - 2 : -1;
|
|
6802
|
+
}
|
|
6803
|
+
|
|
6804
|
+
for (var i = 0; i < n; i++) {
|
|
6805
|
+
if (done[i]) continue;
|
|
6806
|
+
var result = chainIsBroken(i);
|
|
6807
|
+
var id = i;
|
|
6808
|
+
do {
|
|
6809
|
+
isNode[id] = result;
|
|
6810
|
+
done[id] = 1;
|
|
6811
|
+
id = chainIds[id];
|
|
6812
|
+
} while (id !== i);
|
|
6813
|
+
}
|
|
6814
|
+
return isNode;
|
|
6815
|
+
|
|
6816
|
+
// Returns 1 if the chain containing `start` has any broken neighbour
|
|
6817
|
+
// signature (i.e. all members are nodes), else 0.
|
|
6818
|
+
function chainIsBroken(start) {
|
|
6819
|
+
var prev = prevPoint(start),
|
|
6820
|
+
next = nextPoint(start);
|
|
6821
|
+
if (prev === -1 || next === -1) return 1;
|
|
6822
|
+
var refPX = xx[prev], refPY = yy[prev],
|
|
6823
|
+
refNX = xx[next], refNY = yy[next];
|
|
6824
|
+
var id = chainIds[start];
|
|
6825
|
+
while (id !== start) {
|
|
6826
|
+
var p = prevPoint(id), q = nextPoint(id);
|
|
6827
|
+
if (p === -1 || q === -1) return 1;
|
|
6828
|
+
var px = xx[p], py = yy[p], qx = xx[q], qy = yy[q];
|
|
6829
|
+
var fwd = px === refPX && py === refPY && qx === refNX && qy === refNY;
|
|
6830
|
+
var rev = px === refNX && py === refNY && qx === refPX && qy === refPY;
|
|
6831
|
+
if (!fwd && !rev) return 1;
|
|
6832
|
+
id = chainIds[id];
|
|
6833
|
+
}
|
|
6834
|
+
return 0;
|
|
6835
|
+
}
|
|
6836
|
+
}
|
|
6837
|
+
|
|
6780
6838
|
function replaceArcIds(src, replacements) {
|
|
6781
6839
|
return src.map(function(shape) {
|
|
6782
6840
|
return replaceArcsInShape(shape, replacements);
|
|
@@ -11611,7 +11669,7 @@
|
|
|
11611
11669
|
function guessInputFileType(file) {
|
|
11612
11670
|
var ext = getFileExtension(file || '').toLowerCase(),
|
|
11613
11671
|
type = null;
|
|
11614
|
-
if (ext == 'dbf' || ext == 'shp' || ext == 'kml' || ext == 'fgb' || ext == 'gpkg') {
|
|
11672
|
+
if (ext == 'dbf' || ext == 'shp' || ext == 'kml' || ext == 'svg' || ext == 'fgb' || ext == 'gpkg') {
|
|
11615
11673
|
type = ext;
|
|
11616
11674
|
} else if (isAuxiliaryInputFileType(ext)) {
|
|
11617
11675
|
type = ext;
|
|
@@ -11634,7 +11692,8 @@
|
|
|
11634
11692
|
var type = null;
|
|
11635
11693
|
if (utils.isString(content)) {
|
|
11636
11694
|
type = stringLooksLikeJSON(content) && 'json' ||
|
|
11637
|
-
stringLooksLikeKML(content) && 'kml' ||
|
|
11695
|
+
stringLooksLikeKML(content) && 'kml' ||
|
|
11696
|
+
stringLooksLikeSVG(content) && 'svg' || 'text';
|
|
11638
11697
|
} else if (utils.isObject(content) && content.type || utils.isArray(content)) {
|
|
11639
11698
|
type = 'json';
|
|
11640
11699
|
}
|
|
@@ -11654,6 +11713,11 @@
|
|
|
11654
11713
|
return str.includes('<kml ') && str.includes('xmlns="http://www.opengis.net/kml/');
|
|
11655
11714
|
}
|
|
11656
11715
|
|
|
11716
|
+
function stringLooksLikeSVG(str) {
|
|
11717
|
+
str = String(str);
|
|
11718
|
+
return str.includes('<svg ') && str.includes('xmlns="http://www.w3.org/2000/svg"');
|
|
11719
|
+
}
|
|
11720
|
+
|
|
11657
11721
|
function couldBeDsvFile(name) {
|
|
11658
11722
|
var ext = getFileExtension(name).toLowerCase();
|
|
11659
11723
|
return /csv|tsv|txt$/.test(ext);
|
|
@@ -11725,7 +11789,8 @@
|
|
|
11725
11789
|
looksLikeContentFile: looksLikeContentFile,
|
|
11726
11790
|
looksLikeImportableFile: looksLikeImportableFile,
|
|
11727
11791
|
stringLooksLikeJSON: stringLooksLikeJSON,
|
|
11728
|
-
stringLooksLikeKML: stringLooksLikeKML
|
|
11792
|
+
stringLooksLikeKML: stringLooksLikeKML,
|
|
11793
|
+
stringLooksLikeSVG: stringLooksLikeSVG
|
|
11729
11794
|
});
|
|
11730
11795
|
|
|
11731
11796
|
// input: A file path or a buffer
|
|
@@ -18717,13 +18782,13 @@
|
|
|
18717
18782
|
// default is true iff layers contain attributes
|
|
18718
18783
|
return utils.some(layers, function(lyr) {
|
|
18719
18784
|
var fields = lyr.data ? lyr.data.getFields() : [];
|
|
18720
|
-
var haveData = useFeatureProperties(fields, opts);
|
|
18785
|
+
var haveData = useFeatureProperties$1(fields, opts);
|
|
18721
18786
|
var haveId = !!getIdField(fields, opts);
|
|
18722
18787
|
return haveData || haveId;
|
|
18723
18788
|
});
|
|
18724
18789
|
}
|
|
18725
18790
|
|
|
18726
|
-
function useFeatureProperties(fields, opts) {
|
|
18791
|
+
function useFeatureProperties$1(fields, opts) {
|
|
18727
18792
|
return !(opts.drop_table || opts.cut_table || fields.length === 0 ||
|
|
18728
18793
|
fields.length == 1 && fields[0] == GeoJSON.ID_FIELD);
|
|
18729
18794
|
}
|
|
@@ -18732,7 +18797,7 @@
|
|
|
18732
18797
|
var fields = table ? table.getFields() : [],
|
|
18733
18798
|
idField = getIdField(fields, opts),
|
|
18734
18799
|
properties, records;
|
|
18735
|
-
if (!useFeatureProperties(fields, opts)) {
|
|
18800
|
+
if (!useFeatureProperties$1(fields, opts)) {
|
|
18736
18801
|
return null;
|
|
18737
18802
|
}
|
|
18738
18803
|
records = table.getRecords();
|
|
@@ -24493,12 +24558,10 @@ ${svg}
|
|
|
24493
24558
|
}
|
|
24494
24559
|
|
|
24495
24560
|
async function exportLayerToGeoPackage(lyr, dataset, gpkg, opts) {
|
|
24496
|
-
var
|
|
24561
|
+
var fields, columns, tableName, targetSrs, state;
|
|
24497
24562
|
if (!lyr.geometry_type) return;
|
|
24498
|
-
|
|
24499
|
-
|
|
24500
|
-
if (features.length === 0) return;
|
|
24501
|
-
fields = inferFieldTypes(features);
|
|
24563
|
+
fields = inferFieldTypesFromLayer(lyr, dataset, opts);
|
|
24564
|
+
if (!fields) return;
|
|
24502
24565
|
tableName = getTableName(lyr.name);
|
|
24503
24566
|
columns = fields.map(function(field) {
|
|
24504
24567
|
return {
|
|
@@ -24515,8 +24578,11 @@ ${svg}
|
|
|
24515
24578
|
// GeoJSON is EPSG:4326 and reprojects unless given EPSG:4326 metadata.
|
|
24516
24579
|
// Mapshaper output coords are already in target CRS, so suppress reprojection.
|
|
24517
24580
|
var srs = getSourceSrsWithoutReprojection(featureDao && featureDao.srs, targetSrs);
|
|
24518
|
-
|
|
24519
|
-
|
|
24581
|
+
state = initLayerFeatureExportState(lyr, dataset, opts);
|
|
24582
|
+
var featureIndex = 0;
|
|
24583
|
+
for (var i = 0; i < state.count; i++) {
|
|
24584
|
+
var feat = exportFeatureAtIndex(state, i);
|
|
24585
|
+
if (!feat || !feat.geometry) continue;
|
|
24520
24586
|
var normalized = normalizeFeature(feat, fields);
|
|
24521
24587
|
try {
|
|
24522
24588
|
await gpkg.addGeoJSONFeatureToGeoPackageWithFeatureDaoAndSrs(
|
|
@@ -24526,8 +24592,9 @@ ${svg}
|
|
|
24526
24592
|
);
|
|
24527
24593
|
} catch (e) {
|
|
24528
24594
|
throw Error('GeoPackage insert failed at layer "' + tableName +
|
|
24529
|
-
'", feature ' +
|
|
24595
|
+
'", feature ' + featureIndex + ': ' + e.message);
|
|
24530
24596
|
}
|
|
24597
|
+
featureIndex++;
|
|
24531
24598
|
}
|
|
24532
24599
|
}
|
|
24533
24600
|
|
|
@@ -24554,9 +24621,11 @@ ${svg}
|
|
|
24554
24621
|
stop$1('Unable to export GeoPackage bytes');
|
|
24555
24622
|
}
|
|
24556
24623
|
|
|
24557
|
-
function
|
|
24624
|
+
function inferFieldTypesFromLayer(lyr, dataset, opts) {
|
|
24625
|
+
var sawFeature = false;
|
|
24558
24626
|
var index = {};
|
|
24559
|
-
|
|
24627
|
+
forEachLayerExportFeature(lyr, dataset, opts, function(feat) {
|
|
24628
|
+
sawFeature = true;
|
|
24560
24629
|
var props = feat.properties || {};
|
|
24561
24630
|
Object.keys(props).forEach(function(name) {
|
|
24562
24631
|
if (!isSupportedPropertyName(name)) return;
|
|
@@ -24570,11 +24639,79 @@ ${svg}
|
|
|
24570
24639
|
}
|
|
24571
24640
|
});
|
|
24572
24641
|
});
|
|
24642
|
+
if (!sawFeature) return null;
|
|
24573
24643
|
return Object.keys(index).map(function(name) {
|
|
24574
24644
|
return {name: name, type: index[name]};
|
|
24575
24645
|
});
|
|
24576
24646
|
}
|
|
24577
24647
|
|
|
24648
|
+
function forEachLayerExportFeature(lyr, dataset, opts, cb) {
|
|
24649
|
+
var state = initLayerFeatureExportState(lyr, dataset, opts);
|
|
24650
|
+
var featureIndex = 0;
|
|
24651
|
+
for (var i = 0; i < state.count; i++) {
|
|
24652
|
+
var feature = exportFeatureAtIndex(state, i);
|
|
24653
|
+
if (!feature || !feature.geometry) continue;
|
|
24654
|
+
cb(feature, featureIndex++, i);
|
|
24655
|
+
}
|
|
24656
|
+
}
|
|
24657
|
+
|
|
24658
|
+
function initLayerFeatureExportState(lyr, dataset, opts) {
|
|
24659
|
+
var records = lyr.data ? lyr.data.getRecords() : null;
|
|
24660
|
+
var fields = lyr.data ? lyr.data.getFields() : [];
|
|
24661
|
+
var shapes = lyr.shapes || null;
|
|
24662
|
+
if (records && shapes && records.length !== shapes.length) {
|
|
24663
|
+
stop$1('Mismatch between number of properties and number of shapes');
|
|
24664
|
+
}
|
|
24665
|
+
return {
|
|
24666
|
+
lyr: lyr,
|
|
24667
|
+
dataset: dataset,
|
|
24668
|
+
opts: opts || {},
|
|
24669
|
+
fields: fields,
|
|
24670
|
+
records: records,
|
|
24671
|
+
shapes: shapes,
|
|
24672
|
+
count: Math.max(records ? records.length : 0, shapes ? shapes.length : 0),
|
|
24673
|
+
idField: getIdField(fields, opts || {}),
|
|
24674
|
+
useProps: useFeatureProperties(fields, opts || {})
|
|
24675
|
+
};
|
|
24676
|
+
}
|
|
24677
|
+
|
|
24678
|
+
function exportFeatureAtIndex(state, i) {
|
|
24679
|
+
var shape = state.shapes ? state.shapes[i] : null;
|
|
24680
|
+
var geom = shape ? GeoJSON.exporters[state.lyr.geometry_type](shape, state.dataset.arcs, state.opts) : null;
|
|
24681
|
+
var rec = state.records ? state.records[i] : null;
|
|
24682
|
+
var props = null;
|
|
24683
|
+
if (state.useProps && rec) {
|
|
24684
|
+
if (state.idField == GeoJSON.ID_FIELD) {
|
|
24685
|
+
props = Object.assign({}, rec);
|
|
24686
|
+
delete props[state.idField];
|
|
24687
|
+
} else {
|
|
24688
|
+
props = rec;
|
|
24689
|
+
}
|
|
24690
|
+
}
|
|
24691
|
+
var feat = GeoJSON.toFeature(geom, props);
|
|
24692
|
+
if (state.idField && rec) {
|
|
24693
|
+
feat.id = state.idField in rec ? rec[state.idField] : null;
|
|
24694
|
+
}
|
|
24695
|
+
if (state.opts.no_null_props && !feat.properties) {
|
|
24696
|
+
feat.properties = {};
|
|
24697
|
+
}
|
|
24698
|
+
if (Array.isArray(state.opts.hoist) && feat.properties) {
|
|
24699
|
+
feat.properties = Object.assign({}, feat.properties);
|
|
24700
|
+
state.opts.hoist.forEach(function(field) {
|
|
24701
|
+
if (Object.prototype.hasOwnProperty.call(feat.properties, field)) {
|
|
24702
|
+
feat[field] = feat.properties[field];
|
|
24703
|
+
delete feat.properties[field];
|
|
24704
|
+
}
|
|
24705
|
+
});
|
|
24706
|
+
}
|
|
24707
|
+
return feat;
|
|
24708
|
+
}
|
|
24709
|
+
|
|
24710
|
+
function useFeatureProperties(fields, opts) {
|
|
24711
|
+
return !(opts.drop_table || opts.cut_table || fields.length === 0 ||
|
|
24712
|
+
fields.length == 1 && fields[0] == GeoJSON.ID_FIELD);
|
|
24713
|
+
}
|
|
24714
|
+
|
|
24578
24715
|
function inferValueType(value) {
|
|
24579
24716
|
if (value === null || value === undefined) return null;
|
|
24580
24717
|
if (value instanceof Date) return 'DATETIME';
|
|
@@ -24593,10 +24730,6 @@ ${svg}
|
|
|
24593
24730
|
}
|
|
24594
24731
|
|
|
24595
24732
|
function normalizeFeature(feature, fields) {
|
|
24596
|
-
fields.reduce(function(memo, field) {
|
|
24597
|
-
memo[field.name] = field.type;
|
|
24598
|
-
return memo;
|
|
24599
|
-
}, {});
|
|
24600
24733
|
var props = {};
|
|
24601
24734
|
fields.forEach(function(field) {
|
|
24602
24735
|
var value = feature.properties && Object.prototype.hasOwnProperty.call(feature.properties, field.name) ?
|
|
@@ -24615,11 +24748,8 @@ ${svg}
|
|
|
24615
24748
|
|
|
24616
24749
|
function getExportSrs(dataset) {
|
|
24617
24750
|
var info = dataset.info || {};
|
|
24618
|
-
var gpkgCrs = normalizeSrsForInsert(info.geopackage_crs);
|
|
24751
|
+
var gpkgCrs = resolveExportSrs(normalizeSrsForInsert(info.geopackage_crs), info.wkt1);
|
|
24619
24752
|
if (gpkgCrs) {
|
|
24620
|
-
if (!gpkgCrs.definition && info.wkt1) {
|
|
24621
|
-
gpkgCrs.definition = info.wkt1;
|
|
24622
|
-
}
|
|
24623
24753
|
return gpkgCrs;
|
|
24624
24754
|
}
|
|
24625
24755
|
var parsed = parseCrsString(info.crs_string);
|
|
@@ -24635,13 +24765,83 @@ ${svg}
|
|
|
24635
24765
|
if (info.wkt1) {
|
|
24636
24766
|
return buildCustomSrsFromWkt(info.wkt1);
|
|
24637
24767
|
}
|
|
24768
|
+
// If the dataset has an in-memory CRS object (e.g. set by -proj) but no
|
|
24769
|
+
// metadata source, derive a WKT1 definition from it so a projected CRS
|
|
24770
|
+
// isn't lost on round-trip.
|
|
24771
|
+
var derivedWkt = getWktFromDatasetCrs(dataset);
|
|
24772
|
+
if (derivedWkt) {
|
|
24773
|
+
return buildCustomSrsFromWkt(derivedWkt);
|
|
24774
|
+
}
|
|
24775
|
+
// Unknown CRS fallback:
|
|
24776
|
+
// - probable decimal-degree coordinates => allow WGS84 assumption
|
|
24777
|
+
// - otherwise use undefined cartesian CRS (null/unknown projection)
|
|
24778
|
+
if (probablyDecimalDegreeBounds(getDatasetBounds(dataset))) {
|
|
24779
|
+
return {
|
|
24780
|
+
srs_id: 4326,
|
|
24781
|
+
organization: 'EPSG',
|
|
24782
|
+
organization_coordsys_id: 4326
|
|
24783
|
+
};
|
|
24784
|
+
}
|
|
24638
24785
|
return {
|
|
24639
|
-
srs_id:
|
|
24640
|
-
|
|
24641
|
-
|
|
24786
|
+
srs_id: -1,
|
|
24787
|
+
srs_name: 'Undefined Cartesian SRS',
|
|
24788
|
+
organization: 'NONE',
|
|
24789
|
+
organization_coordsys_id: -1
|
|
24642
24790
|
};
|
|
24643
24791
|
}
|
|
24644
24792
|
|
|
24793
|
+
function getWktFromDatasetCrs(dataset) {
|
|
24794
|
+
// Only derive from an explicitly-set CRS object (e.g. from -proj).
|
|
24795
|
+
// This avoids building a custom SRS for lat/lon datasets that were
|
|
24796
|
+
// auto-defaulted to WGS84 by getDatasetCRS().
|
|
24797
|
+
var info = dataset && dataset.info;
|
|
24798
|
+
if (!info || !info.crs) return null;
|
|
24799
|
+
try {
|
|
24800
|
+
return crsToPrj(info.crs) || null;
|
|
24801
|
+
} catch (e) {
|
|
24802
|
+
return null;
|
|
24803
|
+
}
|
|
24804
|
+
}
|
|
24805
|
+
|
|
24806
|
+
function resolveExportSrs(srs, fallbackWkt) {
|
|
24807
|
+
if (!srs) return null;
|
|
24808
|
+
var out = Object.assign({}, srs);
|
|
24809
|
+
if (!out.definition && fallbackWkt) {
|
|
24810
|
+
out.definition = fallbackWkt;
|
|
24811
|
+
}
|
|
24812
|
+
if (out.srs_id == null && out.organization_coordsys_id != null) {
|
|
24813
|
+
out.srs_id = out.organization_coordsys_id;
|
|
24814
|
+
}
|
|
24815
|
+
if (out.srs_id == null) {
|
|
24816
|
+
var fromWkt = parseWktAuthority(out.definition);
|
|
24817
|
+
if (fromWkt) {
|
|
24818
|
+
out.srs_id = fromWkt.srs_id;
|
|
24819
|
+
if (!out.organization) out.organization = fromWkt.organization;
|
|
24820
|
+
if (out.organization_coordsys_id == null) {
|
|
24821
|
+
out.organization_coordsys_id = fromWkt.organization_coordsys_id;
|
|
24822
|
+
}
|
|
24823
|
+
}
|
|
24824
|
+
}
|
|
24825
|
+
if (out.srs_id == null && out.definition) {
|
|
24826
|
+
var custom = buildCustomSrsFromWkt(out.definition);
|
|
24827
|
+
out.srs_id = custom.srs_id;
|
|
24828
|
+
if (!out.srs_name) out.srs_name = custom.srs_name;
|
|
24829
|
+
if (!out.organization) out.organization = custom.organization;
|
|
24830
|
+
if (out.organization_coordsys_id == null) {
|
|
24831
|
+
out.organization_coordsys_id = custom.organization_coordsys_id;
|
|
24832
|
+
}
|
|
24833
|
+
if (!out.description) out.description = custom.description;
|
|
24834
|
+
}
|
|
24835
|
+
if (out.srs_id == null) return null;
|
|
24836
|
+
if (out.organization_coordsys_id == null) {
|
|
24837
|
+
out.organization_coordsys_id = out.srs_id;
|
|
24838
|
+
}
|
|
24839
|
+
if (!out.organization) {
|
|
24840
|
+
out.organization = 'NONE';
|
|
24841
|
+
}
|
|
24842
|
+
return out;
|
|
24843
|
+
}
|
|
24844
|
+
|
|
24645
24845
|
function parseCrsString(str) {
|
|
24646
24846
|
if (!str || typeof str != 'string') return null;
|
|
24647
24847
|
var match = str.match(/^([a-z]+):(\d+)$/i);
|
|
@@ -24733,9 +24933,10 @@ ${svg}
|
|
|
24733
24933
|
var geopackage = require$1('@ngageoint/geopackage');
|
|
24734
24934
|
var spatialReferenceSystem = new geopackage.SpatialReferenceSystem();
|
|
24735
24935
|
spatialReferenceSystem.srs_id = srs.srs_id;
|
|
24736
|
-
spatialReferenceSystem.srs_name = srs.srs_name || (srs.organization + ':' + srs.organization_coordsys_id);
|
|
24737
|
-
spatialReferenceSystem.organization = srs.organization;
|
|
24738
|
-
spatialReferenceSystem.organization_coordsys_id = srs.organization_coordsys_id
|
|
24936
|
+
spatialReferenceSystem.srs_name = srs.srs_name || (String(srs.organization || 'NONE') + ':' + srs.organization_coordsys_id);
|
|
24937
|
+
spatialReferenceSystem.organization = srs.organization || 'NONE';
|
|
24938
|
+
spatialReferenceSystem.organization_coordsys_id = srs.organization_coordsys_id != null ?
|
|
24939
|
+
srs.organization_coordsys_id : srs.srs_id;
|
|
24739
24940
|
spatialReferenceSystem.definition = srs.definition;
|
|
24740
24941
|
spatialReferenceSystem.description = srs.description || null;
|
|
24741
24942
|
gpkg.createSpatialReferenceSystem(spatialReferenceSystem);
|
|
@@ -24744,7 +24945,9 @@ ${svg}
|
|
|
24744
24945
|
function getSourceSrsWithoutReprojection(tableSrs, fallbackSrs) {
|
|
24745
24946
|
var target = normalizeSrsForInsert(tableSrs) || normalizeSrsForInsert(fallbackSrs);
|
|
24746
24947
|
return {
|
|
24747
|
-
srs_id
|
|
24948
|
+
// geopackage writer rejects negative srs_id for source metadata;
|
|
24949
|
+
// use 0 (undefined geographic) when target is undefined cartesian (-1).
|
|
24950
|
+
srs_id: target.srs_id < 0 ? 0 : target.srs_id,
|
|
24748
24951
|
organization: 'EPSG',
|
|
24749
24952
|
organization_coordsys_id: 4326
|
|
24750
24953
|
};
|
|
@@ -24821,18 +25024,21 @@ ${svg}
|
|
|
24821
25024
|
if (normalized.organization_coordsys_id == null && normalized.organizationCoordsysId != null) {
|
|
24822
25025
|
normalized.organization_coordsys_id = normalized.organizationCoordsysId;
|
|
24823
25026
|
}
|
|
24824
|
-
if (normalized.srs_id
|
|
24825
|
-
normalized.srs_id = normalized.
|
|
25027
|
+
if (normalized.srs_id != null) {
|
|
25028
|
+
normalized.srs_id = +normalized.srs_id;
|
|
25029
|
+
if (!Number.isFinite(normalized.srs_id)) {
|
|
25030
|
+
normalized.srs_id = null;
|
|
25031
|
+
}
|
|
24826
25032
|
}
|
|
24827
|
-
|
|
24828
|
-
|
|
24829
|
-
normalized.organization = 'EPSG';
|
|
25033
|
+
if (normalized.organization) {
|
|
25034
|
+
normalized.organization = String(normalized.organization).toUpperCase();
|
|
24830
25035
|
}
|
|
24831
|
-
normalized.
|
|
24832
|
-
|
|
24833
|
-
normalized.organization_coordsys_id
|
|
25036
|
+
if (normalized.organization_coordsys_id != null) {
|
|
25037
|
+
normalized.organization_coordsys_id = +normalized.organization_coordsys_id;
|
|
25038
|
+
if (!Number.isFinite(normalized.organization_coordsys_id)) {
|
|
25039
|
+
normalized.organization_coordsys_id = null;
|
|
25040
|
+
}
|
|
24834
25041
|
}
|
|
24835
|
-
normalized.organization_coordsys_id = +normalized.organization_coordsys_id || 4326;
|
|
24836
25042
|
return normalized;
|
|
24837
25043
|
}
|
|
24838
25044
|
|
|
@@ -26862,6 +27068,10 @@ ${svg}
|
|
|
26862
27068
|
.option('name', {
|
|
26863
27069
|
describe: 'rename the imported layer(s)'
|
|
26864
27070
|
})
|
|
27071
|
+
.option('layers', {
|
|
27072
|
+
type: 'strings',
|
|
27073
|
+
describe: '[GPKG] comma-sep. list of layers to import'
|
|
27074
|
+
})
|
|
26865
27075
|
.option('geometry-type', {
|
|
26866
27076
|
// undocumented; GeoJSON import rejects all but one kind of geometry
|
|
26867
27077
|
// describe: '[GeoJSON] Import one kind of geometry (point|polygon|polyline)'
|
|
@@ -31456,24 +31666,35 @@ ${svg}
|
|
|
31456
31666
|
var geopackage = require$1('@ngageoint/geopackage');
|
|
31457
31667
|
var gpkg;
|
|
31458
31668
|
var datasets;
|
|
31459
|
-
var source;
|
|
31460
31669
|
var tmpPath = null;
|
|
31461
31670
|
|
|
31462
31671
|
if (!geopackage || !geopackage.GeoPackageAPI) {
|
|
31463
31672
|
stop$1('GeoPackage library is not loaded');
|
|
31464
31673
|
}
|
|
31465
31674
|
|
|
31466
|
-
|
|
31467
|
-
|
|
31468
|
-
|
|
31469
|
-
tmpPath = writeGeoPackageTempFile(content);
|
|
31470
|
-
source = tmpPath;
|
|
31471
|
-
} else {
|
|
31472
|
-
source = new Uint8Array(content);
|
|
31473
|
-
}
|
|
31474
|
-
gpkg = await geopackage.GeoPackageAPI.open(source);
|
|
31675
|
+
({gpkg, tmpPath} = await openGeoPackage(content, geopackage));
|
|
31676
|
+
var availableLayers;
|
|
31677
|
+
var filterApplied;
|
|
31475
31678
|
try {
|
|
31476
|
-
|
|
31679
|
+
try {
|
|
31680
|
+
({datasets, availableLayers, filterApplied} = readFeatureTableDatasets(gpkg, opts));
|
|
31681
|
+
} catch (e) {
|
|
31682
|
+
if (!runningInBrowser() && isLocalCsProjError(e)) {
|
|
31683
|
+
gpkg.close();
|
|
31684
|
+
if (tmpPath) {
|
|
31685
|
+
sanitizeGeoPackageCrsMetadata(tmpPath);
|
|
31686
|
+
} else if (utils.isString(content)) {
|
|
31687
|
+
tmpPath = copyGeoPackageTempFile(content);
|
|
31688
|
+
sanitizeGeoPackageCrsMetadata(tmpPath);
|
|
31689
|
+
} else {
|
|
31690
|
+
throw e;
|
|
31691
|
+
}
|
|
31692
|
+
gpkg = await geopackage.GeoPackageAPI.open(tmpPath);
|
|
31693
|
+
({datasets, availableLayers, filterApplied} = readFeatureTableDatasets(gpkg, opts));
|
|
31694
|
+
} else {
|
|
31695
|
+
throw e;
|
|
31696
|
+
}
|
|
31697
|
+
}
|
|
31477
31698
|
} finally {
|
|
31478
31699
|
gpkg.close();
|
|
31479
31700
|
removeTempGeoPackageFile(tmpPath);
|
|
@@ -31482,13 +31703,72 @@ ${svg}
|
|
|
31482
31703
|
if (datasets.length === 0) {
|
|
31483
31704
|
return {
|
|
31484
31705
|
layers: [{name: '', data: null}],
|
|
31485
|
-
info: {
|
|
31706
|
+
info: {
|
|
31707
|
+
_gpkg_available_layers: availableLayers,
|
|
31708
|
+
_gpkg_placeholder: !!filterApplied
|
|
31709
|
+
}
|
|
31486
31710
|
};
|
|
31487
31711
|
}
|
|
31488
31712
|
|
|
31489
31713
|
await initProjLib(datasets);
|
|
31714
|
+
var merged = mergeGeoPackageDatasets(datasets);
|
|
31715
|
+
var mergedArr = Array.isArray(merged) ? merged : [merged];
|
|
31716
|
+
mergedArr.forEach(function(ds) {
|
|
31717
|
+
ds.info = ds.info || {};
|
|
31718
|
+
ds.info._gpkg_available_layers = availableLayers;
|
|
31719
|
+
});
|
|
31720
|
+
return merged;
|
|
31721
|
+
}
|
|
31490
31722
|
|
|
31491
|
-
|
|
31723
|
+
async function openGeoPackage(content, geopackage) {
|
|
31724
|
+
var source;
|
|
31725
|
+
var tmpPath = null;
|
|
31726
|
+
if (utils.isString(content)) {
|
|
31727
|
+
if (!runningInBrowser()) {
|
|
31728
|
+
try {
|
|
31729
|
+
return {
|
|
31730
|
+
gpkg: await geopackage.GeoPackageAPI.open(content),
|
|
31731
|
+
tmpPath: null
|
|
31732
|
+
};
|
|
31733
|
+
} catch (e) {
|
|
31734
|
+
if (!isLocalCsProjError(e)) throw e;
|
|
31735
|
+
tmpPath = copyGeoPackageTempFile(content);
|
|
31736
|
+
sanitizeGeoPackageCrsMetadata(tmpPath);
|
|
31737
|
+
return {
|
|
31738
|
+
gpkg: await geopackage.GeoPackageAPI.open(tmpPath),
|
|
31739
|
+
tmpPath: tmpPath
|
|
31740
|
+
};
|
|
31741
|
+
}
|
|
31742
|
+
}
|
|
31743
|
+
source = content;
|
|
31744
|
+
} else if (!runningInBrowser()) {
|
|
31745
|
+
tmpPath = writeGeoPackageTempFile(content);
|
|
31746
|
+
try {
|
|
31747
|
+
return {
|
|
31748
|
+
gpkg: await geopackage.GeoPackageAPI.open(tmpPath),
|
|
31749
|
+
tmpPath: tmpPath
|
|
31750
|
+
};
|
|
31751
|
+
} catch (e) {
|
|
31752
|
+
if (!isLocalCsProjError(e)) throw e;
|
|
31753
|
+
sanitizeGeoPackageCrsMetadata(tmpPath);
|
|
31754
|
+
return {
|
|
31755
|
+
gpkg: await geopackage.GeoPackageAPI.open(tmpPath),
|
|
31756
|
+
tmpPath: tmpPath
|
|
31757
|
+
};
|
|
31758
|
+
}
|
|
31759
|
+
} else {
|
|
31760
|
+
source = new Uint8Array(content);
|
|
31761
|
+
}
|
|
31762
|
+
return {
|
|
31763
|
+
gpkg: await geopackage.GeoPackageAPI.open(source),
|
|
31764
|
+
tmpPath: null
|
|
31765
|
+
};
|
|
31766
|
+
}
|
|
31767
|
+
|
|
31768
|
+
function isLocalCsProjError(err) {
|
|
31769
|
+
var msg = err && err.message ? String(err.message) : '';
|
|
31770
|
+
return msg.includes("havn't handled \"_\" in keyword yet") ||
|
|
31771
|
+
msg.includes('LOCAL_CS');
|
|
31492
31772
|
}
|
|
31493
31773
|
|
|
31494
31774
|
function writeGeoPackageTempFile(content) {
|
|
@@ -31497,10 +31777,40 @@ ${svg}
|
|
|
31497
31777
|
var path = require$1('path');
|
|
31498
31778
|
var unique = Date.now() + '-' + process.pid + '-' + Math.random().toString(36).slice(2);
|
|
31499
31779
|
var tmpPath = path.join(os.tmpdir(), 'mapshaper-gpkg-import-' + unique + '.gpkg');
|
|
31500
|
-
|
|
31780
|
+
if (Buffer.isBuffer(content)) {
|
|
31781
|
+
fs.writeFileSync(tmpPath, content);
|
|
31782
|
+
} else if (content instanceof Uint8Array) {
|
|
31783
|
+
fs.writeFileSync(tmpPath, Buffer.from(content.buffer, content.byteOffset, content.byteLength));
|
|
31784
|
+
} else if (content instanceof ArrayBuffer) {
|
|
31785
|
+
fs.writeFileSync(tmpPath, Buffer.from(content));
|
|
31786
|
+
} else {
|
|
31787
|
+
fs.writeFileSync(tmpPath, Buffer.from(new Uint8Array(content)));
|
|
31788
|
+
}
|
|
31501
31789
|
return tmpPath;
|
|
31502
31790
|
}
|
|
31503
31791
|
|
|
31792
|
+
function copyGeoPackageTempFile(filepath) {
|
|
31793
|
+
var fs = require$1('fs');
|
|
31794
|
+
var os = require$1('os');
|
|
31795
|
+
var path = require$1('path');
|
|
31796
|
+
var unique = Date.now() + '-' + process.pid + '-' + Math.random().toString(36).slice(2);
|
|
31797
|
+
var tmpPath = path.join(os.tmpdir(), 'mapshaper-gpkg-import-' + unique + '.gpkg');
|
|
31798
|
+
fs.copyFileSync(filepath, tmpPath);
|
|
31799
|
+
return tmpPath;
|
|
31800
|
+
}
|
|
31801
|
+
|
|
31802
|
+
function sanitizeGeoPackageCrsMetadata(filepath) {
|
|
31803
|
+
var Database = require$1('better-sqlite3');
|
|
31804
|
+
var db = new Database(filepath);
|
|
31805
|
+
try {
|
|
31806
|
+
// GDAL may write LOCAL_CS definitions that proj4 can't parse.
|
|
31807
|
+
// Normalize to 'undefined' so GeoPackageAPI.open() won't throw.
|
|
31808
|
+
db.prepare("UPDATE gpkg_spatial_ref_sys SET definition = 'undefined' WHERE definition LIKE 'LOCAL_CS[%'").run();
|
|
31809
|
+
} finally {
|
|
31810
|
+
db.close();
|
|
31811
|
+
}
|
|
31812
|
+
}
|
|
31813
|
+
|
|
31504
31814
|
function removeTempGeoPackageFile(filepath) {
|
|
31505
31815
|
if (!filepath) return;
|
|
31506
31816
|
var fs = require$1('fs');
|
|
@@ -31519,13 +31829,207 @@ ${svg}
|
|
|
31519
31829
|
}
|
|
31520
31830
|
|
|
31521
31831
|
function readFeatureTableDatasets(gpkg, opts) {
|
|
31522
|
-
var
|
|
31523
|
-
|
|
31832
|
+
var selected = getSelectedGeoPackageLayers(opts);
|
|
31833
|
+
var availableLayers = gpkg.getFeatureTables() || [];
|
|
31834
|
+
var tables = filterGeoPackageTables(availableLayers, selected);
|
|
31835
|
+
var filterApplied = Array.isArray(selected) && selected.length > 0;
|
|
31836
|
+
var datasets = tables.map(function(table) {
|
|
31524
31837
|
return readFeatureTable(gpkg, table, opts);
|
|
31525
31838
|
});
|
|
31839
|
+
return {datasets: datasets, availableLayers: availableLayers, filterApplied: filterApplied};
|
|
31840
|
+
}
|
|
31841
|
+
|
|
31842
|
+
function getSelectedGeoPackageLayers(opts) {
|
|
31843
|
+
if (!opts) return null;
|
|
31844
|
+
return opts.gpkg_layers || opts.layers || null;
|
|
31845
|
+
}
|
|
31846
|
+
|
|
31847
|
+
function filterGeoPackageTables(tables, selected) {
|
|
31848
|
+
if (!Array.isArray(selected) || selected.length === 0) return tables;
|
|
31849
|
+
var index = selected.reduce(function(memo, name) {
|
|
31850
|
+
memo[name] = true;
|
|
31851
|
+
return memo;
|
|
31852
|
+
}, {});
|
|
31853
|
+
return tables.filter(function(name) {
|
|
31854
|
+
return !!index[name];
|
|
31855
|
+
});
|
|
31856
|
+
}
|
|
31857
|
+
|
|
31858
|
+
function mergeGeoPackageDatasets(datasets) {
|
|
31859
|
+
var groups = groupGeoPackageDatasets(datasets);
|
|
31860
|
+
var merged = groups.reduce(function(memo, group) {
|
|
31861
|
+
return memo.concat(mergeGeoPackageDatasetGroup(group));
|
|
31862
|
+
}, []);
|
|
31863
|
+
return merged.length == 1 ? merged[0] : merged;
|
|
31864
|
+
}
|
|
31865
|
+
|
|
31866
|
+
function mergeGeoPackageDatasetGroup(group) {
|
|
31867
|
+
var pathDatasets = [];
|
|
31868
|
+
var pointDatasets = [];
|
|
31869
|
+
var dataDatasets = [];
|
|
31870
|
+
|
|
31871
|
+
group.forEach(function(dataset) {
|
|
31872
|
+
var kind = getDatasetGeometryKind(dataset);
|
|
31873
|
+
if (kind == 'path') {
|
|
31874
|
+
pathDatasets.push(dataset);
|
|
31875
|
+
} else if (kind == 'point') {
|
|
31876
|
+
pointDatasets.push(dataset);
|
|
31877
|
+
} else {
|
|
31878
|
+
dataDatasets.push(dataset);
|
|
31879
|
+
}
|
|
31880
|
+
});
|
|
31881
|
+
|
|
31882
|
+
var output = [];
|
|
31883
|
+
if (dataDatasets.length > 0) {
|
|
31884
|
+
output.push(dataDatasets.length == 1 ? dataDatasets[0] : mergeDatasets(dataDatasets));
|
|
31885
|
+
}
|
|
31886
|
+
if (pointDatasets.length > 0) {
|
|
31887
|
+
output.push(pointDatasets.length == 1 ? pointDatasets[0] : mergeDatasets(pointDatasets));
|
|
31888
|
+
}
|
|
31889
|
+
if (pathDatasets.length > 0) {
|
|
31890
|
+
var groupedPaths = groupPathDatasetsBySharedArcs(pathDatasets);
|
|
31891
|
+
if (groupedPaths.mergedDataset) {
|
|
31892
|
+
output.push(groupedPaths.mergedDataset);
|
|
31893
|
+
} else {
|
|
31894
|
+
groupedPaths.components.forEach(function(component) {
|
|
31895
|
+
output.push(component.length == 1 ? component[0] : mergeDatasets(component));
|
|
31896
|
+
});
|
|
31897
|
+
}
|
|
31898
|
+
}
|
|
31899
|
+
return output;
|
|
31900
|
+
}
|
|
31901
|
+
|
|
31902
|
+
function groupGeoPackageDatasets(datasets) {
|
|
31903
|
+
var index = {};
|
|
31904
|
+
datasets.forEach(function(dataset) {
|
|
31905
|
+
var key = getGeoPackageDatasetGroupKey(dataset);
|
|
31906
|
+
if (!index[key]) index[key] = [];
|
|
31907
|
+
index[key].push(dataset);
|
|
31908
|
+
});
|
|
31909
|
+
return Object.keys(index).map(function(key) {
|
|
31910
|
+
return index[key];
|
|
31911
|
+
});
|
|
31912
|
+
}
|
|
31913
|
+
|
|
31914
|
+
function getGeoPackageDatasetGroupKey(dataset) {
|
|
31915
|
+
var info = dataset.info || {};
|
|
31916
|
+
var crs = info.geopackage_crs || null;
|
|
31917
|
+
if (isDataOnlyDataset(dataset)) {
|
|
31918
|
+
return 'data-only';
|
|
31919
|
+
}
|
|
31920
|
+
if (crs) {
|
|
31921
|
+
var org = normalizeCrsOrg(crs.organization);
|
|
31922
|
+
var code = normalizeNumericCode(crs.organization_coordsys_id);
|
|
31923
|
+
if (isTrustedCrsAuthority(org) && code !== null && !(org == 'NONE' && code <= 0)) {
|
|
31924
|
+
return 'crs:' + org + ':' + code;
|
|
31925
|
+
}
|
|
31926
|
+
if (isUsableWkt1Definition(crs.definition)) {
|
|
31927
|
+
return 'wkt:' + crs.definition;
|
|
31928
|
+
}
|
|
31929
|
+
}
|
|
31930
|
+
return probablyDecimalDegreeBounds(getDatasetBounds(dataset)) ?
|
|
31931
|
+
'unknown:unprojected' :
|
|
31932
|
+
'unknown:projected';
|
|
31933
|
+
}
|
|
31934
|
+
|
|
31935
|
+
function isDataOnlyDataset(dataset) {
|
|
31936
|
+
return (dataset.layers || []).every(function(lyr) {
|
|
31937
|
+
return !lyr.geometry_type;
|
|
31938
|
+
});
|
|
31939
|
+
}
|
|
31940
|
+
|
|
31941
|
+
function getDatasetGeometryKind(dataset) {
|
|
31942
|
+
if (isDataOnlyDataset(dataset)) return 'data';
|
|
31943
|
+
if (datasetHasPaths(dataset)) return 'path';
|
|
31944
|
+
var hasPoints = (dataset.layers || []).some(function(lyr) {
|
|
31945
|
+
return layerHasPoints(lyr);
|
|
31946
|
+
});
|
|
31947
|
+
return hasPoints ? 'point' : 'data';
|
|
31526
31948
|
}
|
|
31527
31949
|
|
|
31528
|
-
function
|
|
31950
|
+
function groupPathDatasetsBySharedArcs(datasets) {
|
|
31951
|
+
if (datasets.length < 2) {
|
|
31952
|
+
return {
|
|
31953
|
+
mergedDataset: datasets[0] || null,
|
|
31954
|
+
components: []
|
|
31955
|
+
};
|
|
31956
|
+
}
|
|
31957
|
+
var copy = datasets.map(copyDatasetForExport);
|
|
31958
|
+
var merged = mergeDatasets(copy);
|
|
31959
|
+
if (!merged.arcs || merged.arcs.size() === 0) {
|
|
31960
|
+
return {
|
|
31961
|
+
mergedDataset: null,
|
|
31962
|
+
components: datasets.map(function(dataset) { return [dataset]; })
|
|
31963
|
+
};
|
|
31964
|
+
}
|
|
31965
|
+
buildTopology(merged);
|
|
31966
|
+
var parent = datasets.map(function(_, i) { return i; });
|
|
31967
|
+
var arcOwner = new Map();
|
|
31968
|
+
merged.layers.forEach(function(layer, layerIdx) {
|
|
31969
|
+
var seen = new Set();
|
|
31970
|
+
forEachArcId(layer.shapes || [], function(arcId) {
|
|
31971
|
+
seen.add(arcId < 0 ? ~arcId : arcId);
|
|
31972
|
+
});
|
|
31973
|
+
seen.forEach(function(absId) {
|
|
31974
|
+
if (!arcOwner.has(absId)) {
|
|
31975
|
+
arcOwner.set(absId, layerIdx);
|
|
31976
|
+
} else {
|
|
31977
|
+
union(parent, layerIdx, arcOwner.get(absId));
|
|
31978
|
+
}
|
|
31979
|
+
});
|
|
31980
|
+
});
|
|
31981
|
+
var components = {};
|
|
31982
|
+
datasets.forEach(function(dataset, i) {
|
|
31983
|
+
var root = find(parent, i);
|
|
31984
|
+
if (!components[root]) components[root] = [];
|
|
31985
|
+
components[root].push(dataset);
|
|
31986
|
+
});
|
|
31987
|
+
var grouped = Object.keys(components).map(function(key) {
|
|
31988
|
+
return components[key];
|
|
31989
|
+
});
|
|
31990
|
+
if (grouped.length == 1) {
|
|
31991
|
+
return {
|
|
31992
|
+
mergedDataset: merged,
|
|
31993
|
+
components: []
|
|
31994
|
+
};
|
|
31995
|
+
}
|
|
31996
|
+
return {
|
|
31997
|
+
mergedDataset: null,
|
|
31998
|
+
components: grouped
|
|
31999
|
+
};
|
|
32000
|
+
}
|
|
32001
|
+
|
|
32002
|
+
function find(parent, i) {
|
|
32003
|
+
var p = parent[i];
|
|
32004
|
+
if (p !== i) {
|
|
32005
|
+
parent[i] = find(parent, p);
|
|
32006
|
+
}
|
|
32007
|
+
return parent[i];
|
|
32008
|
+
}
|
|
32009
|
+
|
|
32010
|
+
function union(parent, a, b) {
|
|
32011
|
+
var ra = find(parent, a);
|
|
32012
|
+
var rb = find(parent, b);
|
|
32013
|
+
if (ra !== rb) {
|
|
32014
|
+
parent[rb] = ra;
|
|
32015
|
+
}
|
|
32016
|
+
}
|
|
32017
|
+
|
|
32018
|
+
function normalizeNumericCode(val) {
|
|
32019
|
+
var n = +val;
|
|
32020
|
+
return Number.isFinite(n) ? n : null;
|
|
32021
|
+
}
|
|
32022
|
+
|
|
32023
|
+
function normalizeCrsOrg(org) {
|
|
32024
|
+
if (!org || org == 'undefined') return null;
|
|
32025
|
+
return String(org).toUpperCase();
|
|
32026
|
+
}
|
|
32027
|
+
|
|
32028
|
+
function isTrustedCrsAuthority(org) {
|
|
32029
|
+
return org == 'EPSG' || org == 'ESRI' || org == 'NONE';
|
|
32030
|
+
}
|
|
32031
|
+
|
|
32032
|
+
function convertOrgToProjString(crs) {
|
|
31529
32033
|
var org = crs.organization.toLowerCase();
|
|
31530
32034
|
if (org == 'epsg' || org == 'esri') {
|
|
31531
32035
|
return org + ':' + crs.organization_coordsys_id;
|
|
@@ -31553,14 +32057,25 @@ ${svg}
|
|
|
31553
32057
|
});
|
|
31554
32058
|
dataset.info = dataset.info || {};
|
|
31555
32059
|
dataset.info.geopackage_crs = crs;
|
|
31556
|
-
if (crs?.definition
|
|
32060
|
+
if (isUsableWkt1Definition(crs?.definition)) {
|
|
31557
32061
|
dataset.info.wkt1 = crs.definition;
|
|
31558
32062
|
} else if (crs?.organization && crs.organization !== 'undefined') {
|
|
31559
|
-
|
|
32063
|
+
var crsString = convertOrgToProjString(crs);
|
|
32064
|
+
if (crsString) {
|
|
32065
|
+
dataset.info.crs_string = crsString;
|
|
32066
|
+
}
|
|
31560
32067
|
}
|
|
31561
32068
|
return dataset;
|
|
31562
32069
|
}
|
|
31563
32070
|
|
|
32071
|
+
function isUsableWkt1Definition(defn) {
|
|
32072
|
+
if (!defn || defn === 'undefined') return false;
|
|
32073
|
+
// GDAL may emit LOCAL_CS["Undefined SRS", ...] for null CRS;
|
|
32074
|
+
// this is not a usable projected/geographic CRS for mapshaper.
|
|
32075
|
+
if (/^\s*LOCAL_CS\[/i.test(defn)) return false;
|
|
32076
|
+
return true;
|
|
32077
|
+
}
|
|
32078
|
+
|
|
31564
32079
|
function convertFeatureRow(featureRow) {
|
|
31565
32080
|
var feature = {
|
|
31566
32081
|
type: 'Feature',
|
|
@@ -31576,8 +32091,7 @@ ${svg}
|
|
|
31576
32091
|
// skip feature if geometry can't be parsed
|
|
31577
32092
|
return null;
|
|
31578
32093
|
}
|
|
31579
|
-
|
|
31580
|
-
var geomColName = featureRow.geometryColumn.name;
|
|
32094
|
+
var geomColName = featureRow.geometryColumn && featureRow.geometryColumn.name;
|
|
31581
32095
|
for (var key in featureRow.values) {
|
|
31582
32096
|
if (Object.prototype.hasOwnProperty.call(featureRow.values, key) &&
|
|
31583
32097
|
key !== geomColName) {
|
|
@@ -31620,6 +32134,672 @@ ${svg}
|
|
|
31620
32134
|
return obj;
|
|
31621
32135
|
}
|
|
31622
32136
|
|
|
32137
|
+
var INHERITED_STYLE_KEYS = [
|
|
32138
|
+
'fill', 'fill-opacity',
|
|
32139
|
+
'stroke', 'stroke-width', 'stroke-opacity', 'stroke-dasharray',
|
|
32140
|
+
'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit',
|
|
32141
|
+
'opacity', 'vector-effect',
|
|
32142
|
+
'font-family', 'font-size', 'font-style', 'font-stretch', 'font-weight',
|
|
32143
|
+
'text-anchor', 'dominant-baseline', 'letter-spacing', 'line-height'
|
|
32144
|
+
];
|
|
32145
|
+
|
|
32146
|
+
var INHERITED_STYLE_INDEX = INHERITED_STYLE_KEYS.reduce(function(memo, key) {
|
|
32147
|
+
memo[key] = true;
|
|
32148
|
+
return memo;
|
|
32149
|
+
}, {});
|
|
32150
|
+
|
|
32151
|
+
var LAYER_SUFFIX = {
|
|
32152
|
+
polygon: 'polygons',
|
|
32153
|
+
polyline: 'lines',
|
|
32154
|
+
point: 'points'
|
|
32155
|
+
};
|
|
32156
|
+
|
|
32157
|
+
function importSVG(str, optsArg) {
|
|
32158
|
+
var opts = optsArg || {};
|
|
32159
|
+
var Parser = typeof DOMParser == 'undefined' ? require$1('@xmldom/xmldom').DOMParser : DOMParser;
|
|
32160
|
+
var doc = new Parser().parseFromString(str, 'text/xml');
|
|
32161
|
+
var root = doc && doc.documentElement;
|
|
32162
|
+
var groups = getTopLevelLayerGroups(root);
|
|
32163
|
+
var layerData = [];
|
|
32164
|
+
var datasets = [];
|
|
32165
|
+
|
|
32166
|
+
groups.forEach(function(group) {
|
|
32167
|
+
var features = [];
|
|
32168
|
+
collectFeatures(group.node, {}, features, {
|
|
32169
|
+
forcePolylinePaths: layerHasOpenPaths(group.node)
|
|
32170
|
+
});
|
|
32171
|
+
if (features.length === 0) return;
|
|
32172
|
+
layerData.push({
|
|
32173
|
+
name: group.name,
|
|
32174
|
+
features: features
|
|
32175
|
+
});
|
|
32176
|
+
});
|
|
32177
|
+
|
|
32178
|
+
flipYCoordinates(layerData);
|
|
32179
|
+
|
|
32180
|
+
layerData.forEach(function(layer) {
|
|
32181
|
+
datasets.push(importLayerFeatures(layer.name, layer.features, opts));
|
|
32182
|
+
});
|
|
32183
|
+
|
|
32184
|
+
if (datasets.length === 0) {
|
|
32185
|
+
return {layers: [], info: {}};
|
|
32186
|
+
}
|
|
32187
|
+
if (datasets.length === 1) {
|
|
32188
|
+
return datasets[0];
|
|
32189
|
+
}
|
|
32190
|
+
return mergeDatasets(datasets);
|
|
32191
|
+
}
|
|
32192
|
+
|
|
32193
|
+
function importLayerFeatures(layerName, features, opts) {
|
|
32194
|
+
var dataset = importGeoJSON({
|
|
32195
|
+
type: 'FeatureCollection',
|
|
32196
|
+
features: features
|
|
32197
|
+
}, opts);
|
|
32198
|
+
if (dataset.layers.length === 1) {
|
|
32199
|
+
dataset.layers[0].name = layerName;
|
|
32200
|
+
} else {
|
|
32201
|
+
dataset.layers.forEach(function(lyr) {
|
|
32202
|
+
var suffix = LAYER_SUFFIX[lyr.geometry_type] || 'features';
|
|
32203
|
+
lyr.name = layerName + '_' + suffix;
|
|
32204
|
+
});
|
|
32205
|
+
}
|
|
32206
|
+
return dataset;
|
|
32207
|
+
}
|
|
32208
|
+
|
|
32209
|
+
function getTopLevelLayerGroups(root) {
|
|
32210
|
+
var groups = [];
|
|
32211
|
+
var childNodes = getElementChildren(root);
|
|
32212
|
+
var defaultNode = null;
|
|
32213
|
+
var layerCount = 0;
|
|
32214
|
+
|
|
32215
|
+
childNodes.forEach(function(node) {
|
|
32216
|
+
var tag = getTagName(node);
|
|
32217
|
+
if (tag == 'defs') return;
|
|
32218
|
+
if (tag == 'g') {
|
|
32219
|
+
groups.push({
|
|
32220
|
+
node: node,
|
|
32221
|
+
name: node.getAttribute('id') || getDefaultLayerName(++layerCount)
|
|
32222
|
+
});
|
|
32223
|
+
} else if (tag == 'path' || tag == 'circle' || tag == 'text') {
|
|
32224
|
+
if (!defaultNode) {
|
|
32225
|
+
defaultNode = {
|
|
32226
|
+
tagName: 'g',
|
|
32227
|
+
childNodes: [],
|
|
32228
|
+
attributes: []
|
|
32229
|
+
};
|
|
32230
|
+
groups.push({
|
|
32231
|
+
node: defaultNode,
|
|
32232
|
+
name: getDefaultLayerName(++layerCount)
|
|
32233
|
+
});
|
|
32234
|
+
}
|
|
32235
|
+
defaultNode.childNodes.push(node);
|
|
32236
|
+
}
|
|
32237
|
+
});
|
|
32238
|
+
|
|
32239
|
+
if (groups.length === 0 && root) {
|
|
32240
|
+
groups.push({
|
|
32241
|
+
node: root,
|
|
32242
|
+
name: getDefaultLayerName(1)
|
|
32243
|
+
});
|
|
32244
|
+
}
|
|
32245
|
+
return groups;
|
|
32246
|
+
}
|
|
32247
|
+
|
|
32248
|
+
function getDefaultLayerName(i) {
|
|
32249
|
+
return 'layer' + i;
|
|
32250
|
+
}
|
|
32251
|
+
|
|
32252
|
+
function collectFeatures(node, inheritedStyles, features, layerOpts) {
|
|
32253
|
+
var tag = getTagName(node);
|
|
32254
|
+
var nodeStyles = getNodeStyles(node);
|
|
32255
|
+
var inherited = extendProps(inheritedStyles, nodeStyles);
|
|
32256
|
+
|
|
32257
|
+
if (tag == 'defs') return;
|
|
32258
|
+
if (tag == 'g' || tag == 'svg') {
|
|
32259
|
+
getElementChildren(node).forEach(function(child) {
|
|
32260
|
+
collectFeatures(child, inherited, features, layerOpts);
|
|
32261
|
+
});
|
|
32262
|
+
return;
|
|
32263
|
+
}
|
|
32264
|
+
if (tag == 'path') {
|
|
32265
|
+
collectPathFeature(node, inherited, features, layerOpts);
|
|
32266
|
+
} else if (tag == 'circle') {
|
|
32267
|
+
collectCircleFeature(node, inherited, features);
|
|
32268
|
+
} else if (tag == 'text') {
|
|
32269
|
+
collectTextFeature(node, inherited, features);
|
|
32270
|
+
}
|
|
32271
|
+
}
|
|
32272
|
+
|
|
32273
|
+
function collectPathFeature(node, inherited, features, layerOpts) {
|
|
32274
|
+
var d = node.getAttribute('d');
|
|
32275
|
+
var geom = parsePathGeometry(d, layerOpts && layerOpts.forcePolylinePaths);
|
|
32276
|
+
var props;
|
|
32277
|
+
if (!geom) return;
|
|
32278
|
+
props = getFeatureProperties(node, inherited, {d: true});
|
|
32279
|
+
features.push({
|
|
32280
|
+
type: 'Feature',
|
|
32281
|
+
geometry: geom,
|
|
32282
|
+
properties: props
|
|
32283
|
+
});
|
|
32284
|
+
}
|
|
32285
|
+
|
|
32286
|
+
function collectCircleFeature(node, inherited, features) {
|
|
32287
|
+
var x = parseNumber(node.getAttribute('cx'));
|
|
32288
|
+
var y = parseNumber(node.getAttribute('cy'));
|
|
32289
|
+
var props;
|
|
32290
|
+
if (!isFiniteNumber(x) || !isFiniteNumber(y)) return;
|
|
32291
|
+
props = getFeatureProperties(node, inherited, {cx: true, cy: true});
|
|
32292
|
+
addNumericProperty(props, 'r', node.getAttribute('r'));
|
|
32293
|
+
features.push({
|
|
32294
|
+
type: 'Feature',
|
|
32295
|
+
geometry: {
|
|
32296
|
+
type: 'Point',
|
|
32297
|
+
coordinates: [x, y]
|
|
32298
|
+
},
|
|
32299
|
+
properties: props
|
|
32300
|
+
});
|
|
32301
|
+
}
|
|
32302
|
+
|
|
32303
|
+
function collectTextFeature(node, inherited, features) {
|
|
32304
|
+
var baseX = parseNumber(node.getAttribute('x')) || 0;
|
|
32305
|
+
var baseY = parseNumber(node.getAttribute('y')) || 0;
|
|
32306
|
+
var translate = parseTranslate(node.getAttribute('transform'));
|
|
32307
|
+
var hasTranslate = nodeHasTranslate(node.getAttribute('transform'));
|
|
32308
|
+
var x = hasTranslate ? translate[0] : baseX;
|
|
32309
|
+
var y = hasTranslate ? translate[1] : baseY;
|
|
32310
|
+
var text = (node.textContent || '').trim();
|
|
32311
|
+
var props = getFeatureProperties(node, inherited, {x: true, y: true, transform: true});
|
|
32312
|
+
|
|
32313
|
+
props['label-text'] = text;
|
|
32314
|
+
if (hasTranslate && node.getAttribute('x') !== null) {
|
|
32315
|
+
props.dx = baseX;
|
|
32316
|
+
}
|
|
32317
|
+
if (hasTranslate && node.getAttribute('y') !== null) {
|
|
32318
|
+
props.dy = baseY;
|
|
32319
|
+
}
|
|
32320
|
+
features.push({
|
|
32321
|
+
type: 'Feature',
|
|
32322
|
+
geometry: {
|
|
32323
|
+
type: 'Point',
|
|
32324
|
+
coordinates: [x, y]
|
|
32325
|
+
},
|
|
32326
|
+
properties: props
|
|
32327
|
+
});
|
|
32328
|
+
}
|
|
32329
|
+
|
|
32330
|
+
function getFeatureProperties(node, inherited, excluded) {
|
|
32331
|
+
var props = extendProps({}, inherited);
|
|
32332
|
+
var attrs = node.attributes || [];
|
|
32333
|
+
var styleMap = parseStyleString(node.getAttribute && node.getAttribute('style'));
|
|
32334
|
+
|
|
32335
|
+
Object.keys(styleMap).forEach(function(name) {
|
|
32336
|
+
if (name == 'fill-rule') return;
|
|
32337
|
+
props[name] = styleMap[name];
|
|
32338
|
+
});
|
|
32339
|
+
for (var i = 0; i < attrs.length; i++) {
|
|
32340
|
+
var attr = attrs[i];
|
|
32341
|
+
var key = attr && attr.name;
|
|
32342
|
+
var val = attr && attr.value;
|
|
32343
|
+
if (!key || key == 'style' || (excluded && excluded[key])) continue;
|
|
32344
|
+
if (key == 'id' || key == 'class' || key.indexOf('data-') === 0 || INHERITED_STYLE_INDEX[key]) {
|
|
32345
|
+
props[key] = val;
|
|
32346
|
+
}
|
|
32347
|
+
}
|
|
32348
|
+
return props;
|
|
32349
|
+
}
|
|
32350
|
+
|
|
32351
|
+
function getNodeStyles(node) {
|
|
32352
|
+
var styles = {};
|
|
32353
|
+
var attrs = node.attributes || [];
|
|
32354
|
+
var styleMap = parseStyleString(node.getAttribute && node.getAttribute('style'));
|
|
32355
|
+
var i, attr, key;
|
|
32356
|
+
|
|
32357
|
+
Object.keys(styleMap).forEach(function(name) {
|
|
32358
|
+
if (INHERITED_STYLE_INDEX[name]) {
|
|
32359
|
+
styles[name] = styleMap[name];
|
|
32360
|
+
}
|
|
32361
|
+
});
|
|
32362
|
+
|
|
32363
|
+
for (i = 0; i < attrs.length; i++) {
|
|
32364
|
+
attr = attrs[i];
|
|
32365
|
+
key = attr && attr.name;
|
|
32366
|
+
if (key && INHERITED_STYLE_INDEX[key]) {
|
|
32367
|
+
styles[key] = attr.value;
|
|
32368
|
+
}
|
|
32369
|
+
}
|
|
32370
|
+
return styles;
|
|
32371
|
+
}
|
|
32372
|
+
|
|
32373
|
+
function parseStyleString(style) {
|
|
32374
|
+
var out = {};
|
|
32375
|
+
var parts, i, part, idx, key, val;
|
|
32376
|
+
if (!style) return out;
|
|
32377
|
+
parts = String(style).split(';');
|
|
32378
|
+
for (i = 0; i < parts.length; i++) {
|
|
32379
|
+
part = parts[i].trim();
|
|
32380
|
+
if (!part) continue;
|
|
32381
|
+
idx = part.indexOf(':');
|
|
32382
|
+
if (idx == -1) continue;
|
|
32383
|
+
key = part.substr(0, idx).trim();
|
|
32384
|
+
val = part.substr(idx + 1).trim();
|
|
32385
|
+
if (!key) continue;
|
|
32386
|
+
out[key] = val;
|
|
32387
|
+
}
|
|
32388
|
+
return out;
|
|
32389
|
+
}
|
|
32390
|
+
|
|
32391
|
+
function parsePathGeometry(d, forcePolylinePaths) {
|
|
32392
|
+
var subpaths = parsePathData(d);
|
|
32393
|
+
var lines = [];
|
|
32394
|
+
var rings = [];
|
|
32395
|
+
var parts = [];
|
|
32396
|
+
var polygonGeom, polylineGeom;
|
|
32397
|
+
|
|
32398
|
+
subpaths.forEach(function(path) {
|
|
32399
|
+
var coords = path.coords;
|
|
32400
|
+
if (coords.length < 2) return;
|
|
32401
|
+
if (forcePolylinePaths) {
|
|
32402
|
+
lines.push(path.closed || pointsEqual(coords[0], coords[coords.length - 1]) ? closeRing(coords) : coords);
|
|
32403
|
+
return;
|
|
32404
|
+
}
|
|
32405
|
+
if (path.closed || pointsEqual(coords[0], coords[coords.length - 1])) {
|
|
32406
|
+
if (coords.length >= 3) {
|
|
32407
|
+
rings.push(closeRing(coords));
|
|
32408
|
+
}
|
|
32409
|
+
} else {
|
|
32410
|
+
lines.push(coords);
|
|
32411
|
+
}
|
|
32412
|
+
});
|
|
32413
|
+
|
|
32414
|
+
if (rings.length > 0) {
|
|
32415
|
+
polygonGeom = buildPolygonGeometry(rings);
|
|
32416
|
+
if (polygonGeom) parts.push(polygonGeom);
|
|
32417
|
+
}
|
|
32418
|
+
if (lines.length > 0) {
|
|
32419
|
+
polylineGeom = lines.length == 1 ? {
|
|
32420
|
+
type: 'LineString',
|
|
32421
|
+
coordinates: lines[0]
|
|
32422
|
+
} : {
|
|
32423
|
+
type: 'MultiLineString',
|
|
32424
|
+
coordinates: lines
|
|
32425
|
+
};
|
|
32426
|
+
parts.push(polylineGeom);
|
|
32427
|
+
}
|
|
32428
|
+
if (parts.length === 0) return null;
|
|
32429
|
+
if (parts.length === 1) return parts[0];
|
|
32430
|
+
return {
|
|
32431
|
+
type: 'GeometryCollection',
|
|
32432
|
+
geometries: parts
|
|
32433
|
+
};
|
|
32434
|
+
}
|
|
32435
|
+
|
|
32436
|
+
function buildPolygonGeometry(rings) {
|
|
32437
|
+
var ringData = rings.map(function(coords, i) {
|
|
32438
|
+
return {
|
|
32439
|
+
id: i,
|
|
32440
|
+
coords: coords,
|
|
32441
|
+
area: Math.abs(getRingArea(coords)),
|
|
32442
|
+
containers: [],
|
|
32443
|
+
depth: 0,
|
|
32444
|
+
parent: null
|
|
32445
|
+
};
|
|
32446
|
+
});
|
|
32447
|
+
|
|
32448
|
+
ringData.forEach(function(ring, i) {
|
|
32449
|
+
var point = getRingSamplePoint(ring.coords);
|
|
32450
|
+
ringData.forEach(function(other, j) {
|
|
32451
|
+
if (i == j) return;
|
|
32452
|
+
if (pointInRing(point, other.coords)) {
|
|
32453
|
+
ring.containers.push(other.id);
|
|
32454
|
+
}
|
|
32455
|
+
});
|
|
32456
|
+
ring.depth = ring.containers.length;
|
|
32457
|
+
});
|
|
32458
|
+
|
|
32459
|
+
ringData.forEach(function(ring) {
|
|
32460
|
+
var parentDepth = ring.depth - 1;
|
|
32461
|
+
if (parentDepth < 0) return;
|
|
32462
|
+
ring.containers.forEach(function(parentId) {
|
|
32463
|
+
var candidate = ringData[parentId];
|
|
32464
|
+
if (candidate.depth != parentDepth) return;
|
|
32465
|
+
if (!ring.parent || candidate.area < ring.parent.area) {
|
|
32466
|
+
ring.parent = candidate;
|
|
32467
|
+
}
|
|
32468
|
+
});
|
|
32469
|
+
});
|
|
32470
|
+
|
|
32471
|
+
var polygons = [];
|
|
32472
|
+
ringData.forEach(function(ring) {
|
|
32473
|
+
if (ring.depth % 2 !== 0) return;
|
|
32474
|
+
polygons.push([ring.coords]);
|
|
32475
|
+
});
|
|
32476
|
+
|
|
32477
|
+
ringData.forEach(function(ring) {
|
|
32478
|
+
var polygon;
|
|
32479
|
+
if (ring.depth % 2 !== 1 || !ring.parent) return;
|
|
32480
|
+
polygon = polygons.find(function(coords) {
|
|
32481
|
+
return coords[0] === ring.parent.coords;
|
|
32482
|
+
});
|
|
32483
|
+
if (polygon) {
|
|
32484
|
+
polygon.push(ring.coords);
|
|
32485
|
+
}
|
|
32486
|
+
});
|
|
32487
|
+
|
|
32488
|
+
if (polygons.length === 0) return null;
|
|
32489
|
+
if (polygons.length == 1) {
|
|
32490
|
+
return {
|
|
32491
|
+
type: 'Polygon',
|
|
32492
|
+
coordinates: polygons[0]
|
|
32493
|
+
};
|
|
32494
|
+
}
|
|
32495
|
+
return {
|
|
32496
|
+
type: 'MultiPolygon',
|
|
32497
|
+
coordinates: polygons.map(function(coords) {
|
|
32498
|
+
return [coords[0]].concat(coords.slice(1));
|
|
32499
|
+
})
|
|
32500
|
+
};
|
|
32501
|
+
}
|
|
32502
|
+
|
|
32503
|
+
function parsePathData(d) {
|
|
32504
|
+
var tokens = tokenizePath(d || '');
|
|
32505
|
+
var paths = [];
|
|
32506
|
+
var cmd = null;
|
|
32507
|
+
var i = 0;
|
|
32508
|
+
var currX = 0;
|
|
32509
|
+
var currY = 0;
|
|
32510
|
+
var startX = 0;
|
|
32511
|
+
var startY = 0;
|
|
32512
|
+
var path = null;
|
|
32513
|
+
|
|
32514
|
+
function startPath(x, y) {
|
|
32515
|
+
if (path && path.coords.length > 0) {
|
|
32516
|
+
paths.push(path);
|
|
32517
|
+
}
|
|
32518
|
+
path = {
|
|
32519
|
+
coords: [[x, y]],
|
|
32520
|
+
closed: false
|
|
32521
|
+
};
|
|
32522
|
+
currX = x;
|
|
32523
|
+
currY = y;
|
|
32524
|
+
startX = x;
|
|
32525
|
+
startY = y;
|
|
32526
|
+
}
|
|
32527
|
+
|
|
32528
|
+
function addPoint(x, y) {
|
|
32529
|
+
if (!path) {
|
|
32530
|
+
startPath(x, y);
|
|
32531
|
+
return;
|
|
32532
|
+
}
|
|
32533
|
+
path.coords.push([x, y]);
|
|
32534
|
+
currX = x;
|
|
32535
|
+
currY = y;
|
|
32536
|
+
}
|
|
32537
|
+
|
|
32538
|
+
while (i < tokens.length) {
|
|
32539
|
+
if (isPathCommand(tokens[i])) {
|
|
32540
|
+
cmd = tokens[i++];
|
|
32541
|
+
if (cmd == 'Z' || cmd == 'z') {
|
|
32542
|
+
if (path) {
|
|
32543
|
+
path.closed = true;
|
|
32544
|
+
currX = startX;
|
|
32545
|
+
currY = startY;
|
|
32546
|
+
}
|
|
32547
|
+
}
|
|
32548
|
+
continue;
|
|
32549
|
+
}
|
|
32550
|
+
if (!cmd) {
|
|
32551
|
+
i++;
|
|
32552
|
+
continue;
|
|
32553
|
+
}
|
|
32554
|
+
|
|
32555
|
+
if (cmd == 'M' || cmd == 'm') {
|
|
32556
|
+
if (!hasPair(tokens, i)) break;
|
|
32557
|
+
var moveX = Number(tokens[i++]);
|
|
32558
|
+
var moveY = Number(tokens[i++]);
|
|
32559
|
+
if (cmd == 'm') {
|
|
32560
|
+
moveX += currX;
|
|
32561
|
+
moveY += currY;
|
|
32562
|
+
}
|
|
32563
|
+
startPath(moveX, moveY);
|
|
32564
|
+
cmd = cmd == 'm' ? 'l' : 'L';
|
|
32565
|
+
continue;
|
|
32566
|
+
}
|
|
32567
|
+
|
|
32568
|
+
if (cmd == 'L' || cmd == 'l') {
|
|
32569
|
+
if (!hasPair(tokens, i)) break;
|
|
32570
|
+
var lineX = Number(tokens[i++]);
|
|
32571
|
+
var lineY = Number(tokens[i++]);
|
|
32572
|
+
if (cmd == 'l') {
|
|
32573
|
+
lineX += currX;
|
|
32574
|
+
lineY += currY;
|
|
32575
|
+
}
|
|
32576
|
+
addPoint(lineX, lineY);
|
|
32577
|
+
continue;
|
|
32578
|
+
}
|
|
32579
|
+
|
|
32580
|
+
if (cmd == 'H' || cmd == 'h') {
|
|
32581
|
+
var x = Number(tokens[i++]);
|
|
32582
|
+
if (cmd == 'h') x += currX;
|
|
32583
|
+
addPoint(x, currY);
|
|
32584
|
+
continue;
|
|
32585
|
+
}
|
|
32586
|
+
|
|
32587
|
+
if (cmd == 'V' || cmd == 'v') {
|
|
32588
|
+
var y = Number(tokens[i++]);
|
|
32589
|
+
if (cmd == 'v') y += currY;
|
|
32590
|
+
addPoint(currX, y);
|
|
32591
|
+
continue;
|
|
32592
|
+
}
|
|
32593
|
+
|
|
32594
|
+
// Unsupported command -- skip following numeric tokens until next command.
|
|
32595
|
+
while (i < tokens.length && !isPathCommand(tokens[i])) {
|
|
32596
|
+
i++;
|
|
32597
|
+
}
|
|
32598
|
+
}
|
|
32599
|
+
|
|
32600
|
+
if (path && path.coords.length > 0) {
|
|
32601
|
+
paths.push(path);
|
|
32602
|
+
}
|
|
32603
|
+
return paths;
|
|
32604
|
+
}
|
|
32605
|
+
|
|
32606
|
+
function layerHasOpenPaths(node) {
|
|
32607
|
+
var hasOpenPath = false;
|
|
32608
|
+
|
|
32609
|
+
function scan(el) {
|
|
32610
|
+
var tag = getTagName(el);
|
|
32611
|
+
if (!tag || tag == 'defs' || hasOpenPath) return;
|
|
32612
|
+
if (tag == 'path') {
|
|
32613
|
+
hasOpenPath = pathContainsOpenSubpath(el.getAttribute('d'));
|
|
32614
|
+
return;
|
|
32615
|
+
}
|
|
32616
|
+
getElementChildren(el).forEach(scan);
|
|
32617
|
+
}
|
|
32618
|
+
|
|
32619
|
+
scan(node);
|
|
32620
|
+
return hasOpenPath;
|
|
32621
|
+
}
|
|
32622
|
+
|
|
32623
|
+
function pathContainsOpenSubpath(d) {
|
|
32624
|
+
var subpaths = parsePathData(d);
|
|
32625
|
+
for (var i = 0; i < subpaths.length; i++) {
|
|
32626
|
+
var coords = subpaths[i].coords;
|
|
32627
|
+
if (coords.length < 2) continue;
|
|
32628
|
+
if (!subpaths[i].closed && !pointsEqual(coords[0], coords[coords.length - 1])) {
|
|
32629
|
+
return true;
|
|
32630
|
+
}
|
|
32631
|
+
}
|
|
32632
|
+
return false;
|
|
32633
|
+
}
|
|
32634
|
+
|
|
32635
|
+
function flipYCoordinates(layerData) {
|
|
32636
|
+
var range = getYRange(layerData);
|
|
32637
|
+
if (!range) return;
|
|
32638
|
+
layerData.forEach(function(layer) {
|
|
32639
|
+
layer.features.forEach(function(feature) {
|
|
32640
|
+
if (feature && feature.geometry) {
|
|
32641
|
+
flipGeometryY(feature.geometry, range);
|
|
32642
|
+
}
|
|
32643
|
+
});
|
|
32644
|
+
});
|
|
32645
|
+
}
|
|
32646
|
+
|
|
32647
|
+
function getYRange(layerData) {
|
|
32648
|
+
var minY = Infinity;
|
|
32649
|
+
var maxY = -Infinity;
|
|
32650
|
+
layerData.forEach(function(layer) {
|
|
32651
|
+
layer.features.forEach(function(feature) {
|
|
32652
|
+
forEachGeometryCoordinate(feature && feature.geometry, function(coord) {
|
|
32653
|
+
if (coord[1] < minY) minY = coord[1];
|
|
32654
|
+
if (coord[1] > maxY) maxY = coord[1];
|
|
32655
|
+
});
|
|
32656
|
+
});
|
|
32657
|
+
});
|
|
32658
|
+
if (!isFinite(minY) || !isFinite(maxY)) return null;
|
|
32659
|
+
return {min: minY, max: maxY};
|
|
32660
|
+
}
|
|
32661
|
+
|
|
32662
|
+
function flipGeometryY(geom, range) {
|
|
32663
|
+
forEachGeometryCoordinate(geom, function(coord) {
|
|
32664
|
+
coord[1] = range.min + range.max - coord[1];
|
|
32665
|
+
});
|
|
32666
|
+
}
|
|
32667
|
+
|
|
32668
|
+
function forEachGeometryCoordinate(geom, cb) {
|
|
32669
|
+
if (!geom || !geom.type) return;
|
|
32670
|
+
if (geom.type == 'Point') {
|
|
32671
|
+
cb(geom.coordinates);
|
|
32672
|
+
} else if (geom.type == 'LineString' || geom.type == 'MultiPoint') {
|
|
32673
|
+
geom.coordinates.forEach(cb);
|
|
32674
|
+
} else if (geom.type == 'Polygon' || geom.type == 'MultiLineString') {
|
|
32675
|
+
geom.coordinates.forEach(function(path) {
|
|
32676
|
+
path.forEach(cb);
|
|
32677
|
+
});
|
|
32678
|
+
} else if (geom.type == 'MultiPolygon') {
|
|
32679
|
+
geom.coordinates.forEach(function(poly) {
|
|
32680
|
+
poly.forEach(function(ring) {
|
|
32681
|
+
ring.forEach(cb);
|
|
32682
|
+
});
|
|
32683
|
+
});
|
|
32684
|
+
} else if (geom.type == 'GeometryCollection') {
|
|
32685
|
+
(geom.geometries || []).forEach(function(child) {
|
|
32686
|
+
forEachGeometryCoordinate(child, cb);
|
|
32687
|
+
});
|
|
32688
|
+
}
|
|
32689
|
+
}
|
|
32690
|
+
|
|
32691
|
+
function tokenizePath(d) {
|
|
32692
|
+
var rxp = /[a-zA-Z]|-?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?/ig;
|
|
32693
|
+
return String(d).match(rxp) || [];
|
|
32694
|
+
}
|
|
32695
|
+
|
|
32696
|
+
function parseTranslate(str) {
|
|
32697
|
+
var match = String(str || '').match(/translate\(\s*([^\s,()]+)(?:[\s,]+([^\s,()]+))?/i);
|
|
32698
|
+
var x = 0;
|
|
32699
|
+
var y = 0;
|
|
32700
|
+
if (match) {
|
|
32701
|
+
x = parseNumber(match[1]) || 0;
|
|
32702
|
+
y = parseNumber(match[2]) || 0;
|
|
32703
|
+
}
|
|
32704
|
+
return [x, y];
|
|
32705
|
+
}
|
|
32706
|
+
|
|
32707
|
+
function nodeHasTranslate(str) {
|
|
32708
|
+
return /translate\(/i.test(String(str || ''));
|
|
32709
|
+
}
|
|
32710
|
+
|
|
32711
|
+
function closeRing(coords) {
|
|
32712
|
+
var ring = coords.map(function(p) { return [p[0], p[1]]; });
|
|
32713
|
+
if (!pointsEqual(ring[0], ring[ring.length - 1])) {
|
|
32714
|
+
ring.push([ring[0][0], ring[0][1]]);
|
|
32715
|
+
}
|
|
32716
|
+
return ring;
|
|
32717
|
+
}
|
|
32718
|
+
|
|
32719
|
+
function getRingSamplePoint(ring) {
|
|
32720
|
+
return ring[0];
|
|
32721
|
+
}
|
|
32722
|
+
|
|
32723
|
+
function getRingArea(ring) {
|
|
32724
|
+
var area = 0;
|
|
32725
|
+
for (var i = 0, n = ring.length - 1; i < n; i++) {
|
|
32726
|
+
area += ring[i][0] * ring[i + 1][1] - ring[i + 1][0] * ring[i][1];
|
|
32727
|
+
}
|
|
32728
|
+
return area / 2;
|
|
32729
|
+
}
|
|
32730
|
+
|
|
32731
|
+
function pointInRing(point, ring) {
|
|
32732
|
+
var x = point[0];
|
|
32733
|
+
var y = point[1];
|
|
32734
|
+
var inside = false;
|
|
32735
|
+
var i, j, xi, yi, xj, yj, intersects;
|
|
32736
|
+
for (i = 0, j = ring.length - 1; i < ring.length; j = i++) {
|
|
32737
|
+
xi = ring[i][0];
|
|
32738
|
+
yi = ring[i][1];
|
|
32739
|
+
xj = ring[j][0];
|
|
32740
|
+
yj = ring[j][1];
|
|
32741
|
+
intersects = ((yi > y) != (yj > y)) &&
|
|
32742
|
+
(x < (xj - xi) * (y - yi) / ((yj - yi) || 1e-30) + xi);
|
|
32743
|
+
if (intersects) inside = !inside;
|
|
32744
|
+
}
|
|
32745
|
+
return inside;
|
|
32746
|
+
}
|
|
32747
|
+
|
|
32748
|
+
function addNumericProperty(props, key, strVal) {
|
|
32749
|
+
var num = parseNumber(strVal);
|
|
32750
|
+
if (isFiniteNumber(num)) {
|
|
32751
|
+
props[key] = num;
|
|
32752
|
+
}
|
|
32753
|
+
}
|
|
32754
|
+
|
|
32755
|
+
function parseNumber(str) {
|
|
32756
|
+
var num = Number(str);
|
|
32757
|
+
return isFiniteNumber(num) ? num : null;
|
|
32758
|
+
}
|
|
32759
|
+
|
|
32760
|
+
function hasPair(tokens, i) {
|
|
32761
|
+
return i + 1 < tokens.length && !isPathCommand(tokens[i]) && !isPathCommand(tokens[i + 1]);
|
|
32762
|
+
}
|
|
32763
|
+
|
|
32764
|
+
function isPathCommand(token) {
|
|
32765
|
+
return /^[a-z]$/i.test(token);
|
|
32766
|
+
}
|
|
32767
|
+
|
|
32768
|
+
function pointsEqual(a, b) {
|
|
32769
|
+
return a && b && a[0] == b[0] && a[1] == b[1];
|
|
32770
|
+
}
|
|
32771
|
+
|
|
32772
|
+
function isFiniteNumber(val) {
|
|
32773
|
+
return typeof val == 'number' && isFinite(val);
|
|
32774
|
+
}
|
|
32775
|
+
|
|
32776
|
+
function getTagName(node) {
|
|
32777
|
+
return node && node.tagName ? String(node.tagName).toLowerCase() : '';
|
|
32778
|
+
}
|
|
32779
|
+
|
|
32780
|
+
function getElementChildren(node) {
|
|
32781
|
+
var out = [];
|
|
32782
|
+
var childNodes = node && node.childNodes ? node.childNodes : [];
|
|
32783
|
+
for (var i = 0; i < childNodes.length; i++) {
|
|
32784
|
+
if (childNodes[i] && childNodes[i].nodeType == 1) {
|
|
32785
|
+
out.push(childNodes[i]);
|
|
32786
|
+
}
|
|
32787
|
+
}
|
|
32788
|
+
return out;
|
|
32789
|
+
}
|
|
32790
|
+
|
|
32791
|
+
function extendProps(dest, src) {
|
|
32792
|
+
var out = {};
|
|
32793
|
+
var key;
|
|
32794
|
+
if (dest) {
|
|
32795
|
+
for (key in dest) out[key] = dest[key];
|
|
32796
|
+
}
|
|
32797
|
+
if (src) {
|
|
32798
|
+
for (key in src) out[key] = src[key];
|
|
32799
|
+
}
|
|
32800
|
+
return out;
|
|
32801
|
+
}
|
|
32802
|
+
|
|
31623
32803
|
// Parse content of one or more input files and return a dataset
|
|
31624
32804
|
// @obj: file data, indexed by file type
|
|
31625
32805
|
// File data objects have two properties:
|
|
@@ -31662,6 +32842,11 @@ ${svg}
|
|
|
31662
32842
|
data = obj.kml;
|
|
31663
32843
|
dataset = importKML(data.content, opts);
|
|
31664
32844
|
|
|
32845
|
+
} else if (obj.svg) {
|
|
32846
|
+
dataFmt = 'svg';
|
|
32847
|
+
data = obj.svg;
|
|
32848
|
+
dataset = importSVG(data.content, opts);
|
|
32849
|
+
|
|
31665
32850
|
} else if (obj.fgb) {
|
|
31666
32851
|
stop$1("FlatGeobuf import requires async import path");
|
|
31667
32852
|
} else if (obj.gpkg) {
|
|
@@ -31685,6 +32870,11 @@ ${svg}
|
|
|
31685
32870
|
} else {
|
|
31686
32871
|
return importContent(obj, opts);
|
|
31687
32872
|
}
|
|
32873
|
+
if (Array.isArray(dataset)) {
|
|
32874
|
+
return dataset.map(function(ds) {
|
|
32875
|
+
return finalizeImportedDataset(ds, dataFmt, data, opts);
|
|
32876
|
+
});
|
|
32877
|
+
}
|
|
31688
32878
|
return finalizeImportedDataset(dataset, dataFmt, data, opts);
|
|
31689
32879
|
}
|
|
31690
32880
|
|
|
@@ -31886,12 +33076,18 @@ ${svg}
|
|
|
31886
33076
|
|
|
31887
33077
|
cmd.importFiles = async function(catalog, opts) {
|
|
31888
33078
|
var files = opts.files || [];
|
|
31889
|
-
var dataset;
|
|
33079
|
+
var dataset, datasets, target;
|
|
31890
33080
|
|
|
31891
33081
|
if (opts.stdin) {
|
|
31892
33082
|
dataset = await importFileAsync('/dev/stdin', opts);
|
|
31893
|
-
|
|
31894
|
-
|
|
33083
|
+
datasets = normalizeImportedDatasets(dataset);
|
|
33084
|
+
catalog.addDatasets(datasets);
|
|
33085
|
+
if (datasets.length > 1) {
|
|
33086
|
+
catalog.setDefaultTargets(datasets.map(function(ds) {
|
|
33087
|
+
return {dataset: ds, layers: ds.layers};
|
|
33088
|
+
}));
|
|
33089
|
+
}
|
|
33090
|
+
return normalizeImportedTarget(datasets);
|
|
31895
33091
|
}
|
|
31896
33092
|
|
|
31897
33093
|
if (files.length > 0 === false) {
|
|
@@ -31926,14 +33122,22 @@ ${svg}
|
|
|
31926
33122
|
} else {
|
|
31927
33123
|
dataset = await importFilesTogetherAsync(files, opts);
|
|
31928
33124
|
}
|
|
33125
|
+
datasets = normalizeImportedDatasets(dataset);
|
|
33126
|
+
datasets = validateAndCleanGpkgSelection(datasets, opts);
|
|
31929
33127
|
|
|
31930
33128
|
if (opts.merge_files && files.length > 1) {
|
|
31931
33129
|
// TODO: deprecate and remove this option (use -merge-layers cmd instead)
|
|
31932
|
-
|
|
33130
|
+
datasets[0].layers = cmd.mergeLayers(datasets[0].layers);
|
|
31933
33131
|
}
|
|
31934
33132
|
|
|
31935
|
-
catalog.
|
|
31936
|
-
|
|
33133
|
+
catalog.addDatasets(datasets);
|
|
33134
|
+
if (datasets.length > 1) {
|
|
33135
|
+
catalog.setDefaultTargets(datasets.map(function(ds) {
|
|
33136
|
+
return {dataset: ds, layers: ds.layers};
|
|
33137
|
+
}));
|
|
33138
|
+
}
|
|
33139
|
+
target = normalizeImportedTarget(datasets);
|
|
33140
|
+
return target;
|
|
31937
33141
|
};
|
|
31938
33142
|
|
|
31939
33143
|
// replace any JSON data objects with filenames and cache the data
|
|
@@ -32034,7 +33238,8 @@ ${svg}
|
|
|
32034
33238
|
|
|
32035
33239
|
cli.checkFileExists(path, cache);
|
|
32036
33240
|
|
|
32037
|
-
if ((fileType == 'shp' || fileType == 'json' || fileType == 'text' || fileType == 'dbf'
|
|
33241
|
+
if ((fileType == 'shp' || fileType == 'json' || fileType == 'text' || fileType == 'dbf' ||
|
|
33242
|
+
fileType == 'gpkg') && !cached) {
|
|
32038
33243
|
// these file types are read incrementally
|
|
32039
33244
|
content = null;
|
|
32040
33245
|
|
|
@@ -32137,17 +33342,20 @@ ${svg}
|
|
|
32137
33342
|
// Import multiple files to a single dataset
|
|
32138
33343
|
function importFilesTogether(files, opts) {
|
|
32139
33344
|
var unbuiltTopology = false;
|
|
32140
|
-
var datasets = files.
|
|
33345
|
+
var datasets = files.reduce(function(memo, fname) {
|
|
32141
33346
|
// import without topology or snapping
|
|
32142
33347
|
var importOpts = utils.defaults({no_topology: true, snap: false, snap_interval: null, files: [fname]}, opts);
|
|
32143
|
-
var
|
|
33348
|
+
var imported = normalizeImportedDatasets(importFile(fname, importOpts));
|
|
32144
33349
|
// check if dataset contains non-topological paths
|
|
32145
33350
|
// TODO: may also need to rebuild topology if multiple topojson files are merged
|
|
32146
|
-
|
|
32147
|
-
|
|
32148
|
-
|
|
32149
|
-
|
|
32150
|
-
|
|
33351
|
+
imported.forEach(function(dataset) {
|
|
33352
|
+
if (dataset.arcs && dataset.arcs.size() > 0 && dataset.info.input_formats[0] != 'topojson') {
|
|
33353
|
+
unbuiltTopology = true;
|
|
33354
|
+
}
|
|
33355
|
+
memo.push(dataset);
|
|
33356
|
+
});
|
|
33357
|
+
return memo;
|
|
33358
|
+
}, []);
|
|
32151
33359
|
var combined = mergeDatasets(datasets);
|
|
32152
33360
|
// Build topology, if needed
|
|
32153
33361
|
// TODO: consider updating topology of TopoJSON files instead of concatenating arcs
|
|
@@ -32165,12 +33373,15 @@ ${svg}
|
|
|
32165
33373
|
for (var fname of files) {
|
|
32166
33374
|
// import without topology or snapping
|
|
32167
33375
|
var importOpts = utils.defaults({no_topology: true, snap: false, snap_interval: null, files: [fname]}, opts);
|
|
32168
|
-
var
|
|
32169
|
-
|
|
32170
|
-
|
|
32171
|
-
|
|
32172
|
-
|
|
33376
|
+
var imported = normalizeImportedDatasets(await importFileAsync(fname, importOpts));
|
|
33377
|
+
imported.forEach(function(dataset) {
|
|
33378
|
+
if (dataset.arcs && dataset.arcs.size() > 0 && dataset.info.input_formats[0] != 'topojson') {
|
|
33379
|
+
unbuiltTopology = true;
|
|
33380
|
+
}
|
|
33381
|
+
datasets.push(dataset);
|
|
33382
|
+
});
|
|
32173
33383
|
}
|
|
33384
|
+
datasets = validateAndCleanGpkgSelection(datasets, opts);
|
|
32174
33385
|
var combined = mergeDatasets(datasets);
|
|
32175
33386
|
if (unbuiltTopology && !opts.no_topology) {
|
|
32176
33387
|
cleanPathsAfterImport(combined, opts);
|
|
@@ -32179,6 +33390,60 @@ ${svg}
|
|
|
32179
33390
|
return combined;
|
|
32180
33391
|
}
|
|
32181
33392
|
|
|
33393
|
+
// Validate the GeoPackage layers= selection across all imported datasets,
|
|
33394
|
+
// remove any placeholder datasets produced by filter misses, and strip the
|
|
33395
|
+
// bookkeeping metadata attached by the GeoPackage importer.
|
|
33396
|
+
function validateAndCleanGpkgSelection(datasets, opts) {
|
|
33397
|
+
var availableSet = new Set();
|
|
33398
|
+
var importedSet = new Set();
|
|
33399
|
+
var sawGpkg = false;
|
|
33400
|
+
datasets.forEach(function(ds) {
|
|
33401
|
+
var info = ds && ds.info;
|
|
33402
|
+
if (!info || !Array.isArray(info._gpkg_available_layers)) return;
|
|
33403
|
+
sawGpkg = true;
|
|
33404
|
+
info._gpkg_available_layers.forEach(function(name) { availableSet.add(name); });
|
|
33405
|
+
if (!info._gpkg_placeholder) {
|
|
33406
|
+
(ds.layers || []).forEach(function(lyr) {
|
|
33407
|
+
if (lyr && lyr.name) importedSet.add(lyr.name);
|
|
33408
|
+
});
|
|
33409
|
+
}
|
|
33410
|
+
});
|
|
33411
|
+
if (sawGpkg && Array.isArray(opts.layers) && opts.layers.length > 0) {
|
|
33412
|
+
var missing = opts.layers.filter(function(name) {
|
|
33413
|
+
return !importedSet.has(name);
|
|
33414
|
+
});
|
|
33415
|
+
if (missing.length > 0) {
|
|
33416
|
+
stop$1(
|
|
33417
|
+
'Missing GeoPackage layer(s): ' + missing.join(', ') + '\n' +
|
|
33418
|
+
'Existing layers: ' + Array.from(availableSet).join(' ')
|
|
33419
|
+
);
|
|
33420
|
+
}
|
|
33421
|
+
}
|
|
33422
|
+
var cleaned = datasets.filter(function(ds) {
|
|
33423
|
+
return !(ds && ds.info && ds.info._gpkg_placeholder);
|
|
33424
|
+
});
|
|
33425
|
+
cleaned.forEach(function(ds) {
|
|
33426
|
+
if (ds && ds.info) {
|
|
33427
|
+
delete ds.info._gpkg_available_layers;
|
|
33428
|
+
delete ds.info._gpkg_placeholder;
|
|
33429
|
+
}
|
|
33430
|
+
});
|
|
33431
|
+
return cleaned;
|
|
33432
|
+
}
|
|
33433
|
+
|
|
33434
|
+
function normalizeImportedDatasets(datasetOrArray) {
|
|
33435
|
+
return Array.isArray(datasetOrArray) ? datasetOrArray : [datasetOrArray];
|
|
33436
|
+
}
|
|
33437
|
+
|
|
33438
|
+
function normalizeImportedTarget(datasets) {
|
|
33439
|
+
if (datasets.length == 1) return datasets[0];
|
|
33440
|
+
return {
|
|
33441
|
+
layers: datasets.reduce(function(memo, dataset) {
|
|
33442
|
+
return memo.concat(dataset.layers);
|
|
33443
|
+
}, [])
|
|
33444
|
+
};
|
|
33445
|
+
}
|
|
33446
|
+
|
|
32182
33447
|
function getUnsupportedFileMessage(path) {
|
|
32183
33448
|
var ext = getFileExtension(path);
|
|
32184
33449
|
var msg = 'Unable to import ' + path;
|
|
@@ -49514,7 +50779,7 @@ ${svg}
|
|
|
49514
50779
|
});
|
|
49515
50780
|
}
|
|
49516
50781
|
|
|
49517
|
-
var version = "0.6.
|
|
50782
|
+
var version = "0.6.118";
|
|
49518
50783
|
|
|
49519
50784
|
// Parse command line args into commands and run them
|
|
49520
50785
|
// Function takes an optional Node-style callback. A Promise is returned if no callback is given.
|