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 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
- arcs = [],
6415
+ arcSrcXX = [],
6416
+ arcSrcYY = [],
6417
+ arcStart = [],
6418
+ arcEnd = [],
6410
6419
  arcPoints = 0;
6411
6420
 
6412
- utils.initializeArray(hashTable, -1);
6421
+ initializeArray(hashTable, -1);
6413
6422
 
6414
- this.addArc = function(xx, yy) {
6415
- var end = xx.length - 1,
6416
- key = hash(xx[end], yy[end]),
6417
- chainId = hashTable[key],
6418
- arcId = arcs.length;
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
- arcs.push([xx, yy]);
6421
- arcPoints += xx.length;
6422
- chainIds.push(chainId);
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 in the
6427
- // opposite direction. (This program uses the convention of CW for space-enclosing rings, CCW for holes,
6428
- // so coincident boundaries should contain the same points in reverse sequence).
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
- // (Abnormal topology, but we're accepting it because in-the-wild
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
- arcX, arcY, len;
6449
-
6459
+ sx, sy, s, e;
6450
6460
  while (arcId != -1) {
6451
- // check endpoints and one segment...
6452
- // it would be more rigorous but slower to identify a match
6453
- // by comparing all segments in the coordinate sequence
6454
- arcX = arcs[arcId][0];
6455
- arcY = arcs[arcId][1];
6456
- len = arcX.length;
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 xx = new Float64Array(arcPoints),
6468
- yy = new Float64Array(arcPoints),
6469
- nn = new Uint32Array(arcs.length),
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
- arc, len;
6472
- for (var i=0, n=arcs.length; i<n; i++) {
6473
- arc = arcs[i];
6474
- len = arc[0].length;
6475
- utils.copyElements(arc[0], 0, xx, copied, len);
6476
- utils.copyElements(arc[1], 0, yy, copied, len);
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: xx,
6482
- yy: 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 id + 1;
6625
+ return nextId;
6610
6626
  }
6611
- var len = nn[partId];
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 id - 1;
6634
+ return prevId;
6620
6635
  }
6621
- var len = nn[partId];
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 sameXY(a, b) {
6626
- return xx[a] == xx[b] && yy[a] == yy[b];
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 = usingTypedArrays() ? Float64Array : Array,
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
- arcId = index.addArc(mergeArcParts(xx, start1, end1, start2, end2),
6727
- mergeArcParts(yy, start1, end1, start2, end2));
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(slice.call(xx, start, end + 1),
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' || 'text';
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 features, fields, columns, tableName, targetSrs;
24561
+ var fields, columns, tableName, targetSrs, state;
24497
24562
  if (!lyr.geometry_type) return;
24498
- features = exportLayerAsGeoJSON(lyr, dataset, opts, true, null)
24499
- .filter(feat => !!(feat && feat.geometry));
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
- for (var i = 0; i < features.length; i++) {
24519
- var feat = features[i];
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 ' + i + ': ' + e.message);
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 inferFieldTypes(features) {
24624
+ function inferFieldTypesFromLayer(lyr, dataset, opts) {
24625
+ var sawFeature = false;
24558
24626
  var index = {};
24559
- features.forEach(function(feat) {
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: 4326,
24640
- organization: 'EPSG',
24641
- organization_coordsys_id: 4326
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: target.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 == null) {
24825
- normalized.srs_id = normalized.organization_coordsys_id || 4326;
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
- normalized.srs_id = +normalized.srs_id || 4326;
24828
- if (!normalized.organization) {
24829
- normalized.organization = 'EPSG';
25033
+ if (normalized.organization) {
25034
+ normalized.organization = String(normalized.organization).toUpperCase();
24830
25035
  }
24831
- normalized.organization = String(normalized.organization).toUpperCase();
24832
- if (normalized.organization_coordsys_id == null) {
24833
- normalized.organization_coordsys_id = normalized.srs_id || 4326;
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
- if (utils.isString(content)) {
31467
- source = content;
31468
- } else if (!runningInBrowser()) {
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
- datasets = readFeatureTableDatasets(gpkg, opts);
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
- return mergeDatasets(datasets);
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
- fs.writeFileSync(tmpPath, Buffer.from(new Uint8Array(content)));
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 tables = gpkg.getFeatureTables() || [];
31523
- return tables.map(function(table) {
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 convertOrgToProj4(crs) {
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 && crs.definition !== 'undefined') {
32060
+ if (isUsableWkt1Definition(crs?.definition)) {
31557
32061
  dataset.info.wkt1 = crs.definition;
31558
32062
  } else if (crs?.organization && crs.organization !== 'undefined') {
31559
- dataset.info.crs_string = convertOrgToProj4(crs);
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
- if (!feature.geometry) return null;
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
- catalog.addDataset(dataset);
31894
- return dataset;
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
- dataset.layers = cmd.mergeLayers(dataset.layers);
33130
+ datasets[0].layers = cmd.mergeLayers(datasets[0].layers);
31933
33131
  }
31934
33132
 
31935
- catalog.addDataset(dataset);
31936
- return dataset;
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') && !cached) {
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.map(function(fname) {
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 dataset = importFile(fname, importOpts);
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
- if (dataset.arcs && dataset.arcs.size() > 0 && dataset.info.input_formats[0] != 'topojson') {
32147
- unbuiltTopology = true;
32148
- }
32149
- return dataset;
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 dataset = await importFileAsync(fname, importOpts);
32169
- if (dataset.arcs && dataset.arcs.size() > 0 && dataset.info.input_formats[0] != 'topojson') {
32170
- unbuiltTopology = true;
32171
- }
32172
- datasets.push(dataset);
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.116";
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.