mapshaper 0.7.11 → 0.7.12

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
@@ -48764,7 +48764,7 @@ ${svg}
48764
48764
  return layerHasGeometry(lyr) && !layerIsFullyEnclosed(lyr, dataset, clipData);
48765
48765
  });
48766
48766
  if (layers.length > 0) {
48767
- clipLayersInPlace(layers, clipData, dataset, 'clip');
48767
+ clipLayersInPlace(layers, clipData, dataset, 'clip', getInternalClipOpts());
48768
48768
  return true;
48769
48769
  }
48770
48770
  return false;
@@ -48803,13 +48803,21 @@ ${svg}
48803
48803
  }
48804
48804
 
48805
48805
  function datasetCrossesLon(dataset, lon) {
48806
- var crosses = 0;
48807
- dataset.arcs.forEachSegment(function(i, j, xx, yy) {
48808
- var ax = xx[i],
48809
- bx = xx[j];
48810
- if (ax <= lon && bx >= lon || ax >= lon && bx <= lon) crosses++;
48806
+ var crosses = false;
48807
+ dataset.layers.filter(layerHasPaths).forEach(function(lyr) {
48808
+ if (crosses) return;
48809
+ lyr.shapes.forEach(function(shp) {
48810
+ if (crosses || !shp) return;
48811
+ forEachSegmentInShape(shp, dataset.arcs, function(i, j, xx, yy) {
48812
+ var ax = xx[i],
48813
+ bx = xx[j];
48814
+ if (ax <= lon && bx >= lon || ax >= lon && bx <= lon) {
48815
+ crosses = true;
48816
+ }
48817
+ });
48818
+ });
48811
48819
  });
48812
- return crosses > 0;
48820
+ return crosses;
48813
48821
  }
48814
48822
 
48815
48823
  function insertVerticalCut(dataset, lon) {
@@ -48820,29 +48828,38 @@ ${svg}
48820
48828
  // densify (so cut line can curve, e.g. Cupola projection)
48821
48829
  var geojson = bboxToPolygon(bbox, {interval: 0.5});
48822
48830
  var clip = importGeoJSON(geojson);
48823
- clipLayersInPlace(pathLayers, clip, dataset, 'erase');
48831
+ clipLayersInPlace(pathLayers, clip, dataset, 'erase', getInternalClipOpts());
48832
+ }
48833
+
48834
+ function getInternalClipOpts() {
48835
+ return {no_cleanup: true, no_warn: true};
48824
48836
  }
48825
48837
 
48826
- // Converts a Proj.4 projection name (e.g. lcc, tmerc) to a Proj.4 string
48838
+ // Converts a Proj.4 projection name (e.g. lcc, tmerc, utm) to a Proj.4 string
48827
48839
  // by picking parameters that are appropriate to the extent of the dataset
48828
48840
  // being projected (e.g. standard parallels, longitude of origin)
48829
- // Works for lcc, aea, tmerc, etc.
48841
+ // Works for lcc, aea, tmerc, utm, etc.
48830
48842
  // TODO: add more projections
48831
48843
  //
48832
48844
  function expandProjDefn(str, dataset, targetLayers) {
48833
48845
  var mproj = require$1('mproj');
48834
- var proj4, params, bbox, isConic2SP, isCentered, decimals;
48846
+ var proj4, params, bbox, isConic2SP, isCentered, isUtm, decimals;
48835
48847
  if (str in mproj.internal.pj_list === false) {
48836
48848
  // not a bare projection code -- assume valid projection string in other format
48837
48849
  return str;
48838
48850
  }
48839
48851
  isConic2SP = ['lcc', 'aea'].includes(str);
48840
48852
  isCentered = ['tmerc', 'etmerc'].includes(str);
48853
+ isUtm = str == 'utm';
48841
48854
  proj4 = '+proj=' + str;
48842
- if (isConic2SP || isCentered) {
48855
+ if (isConic2SP || isCentered || isUtm) {
48843
48856
  bbox = getBBox(dataset, targetLayers); // TODO: support projected datasets
48844
48857
  decimals = getBoundsPrecisionForDisplay(bbox);
48845
- params = isCentered ? getCenterParams(bbox, decimals) : getConicParams(bbox, decimals);
48858
+ if (isUtm) {
48859
+ params = getUtmParams(bbox);
48860
+ } else {
48861
+ params = isCentered ? getCenterParams(bbox, decimals) : getConicParams(bbox, decimals);
48862
+ }
48846
48863
  proj4 += ' ' + params;
48847
48864
  message(`Converted "${str}" to "${proj4}"`);
48848
48865
  }
@@ -48858,7 +48875,77 @@ ${svg}
48858
48875
  if (!isLatLngCRS(getDatasetCRS(dataset))) {
48859
48876
  stop$1('Expected unprojected data');
48860
48877
  }
48861
- return getDatasetBounds(source).toArray();
48878
+ return getAutoFitBBox(source);
48879
+ }
48880
+
48881
+ function getAutoFitBBox(dataset) {
48882
+ var bbox = getDatasetBounds(dataset).toArray();
48883
+ if (bbox[2] - bbox[0] > 180) {
48884
+ bbox = getWrappedBBox(dataset, bbox) || bbox;
48885
+ }
48886
+ return bbox;
48887
+ }
48888
+
48889
+ function getWrappedBBox(dataset, bbox) {
48890
+ var xmaxW = -Infinity,
48891
+ xminE = Infinity, xmaxE = -Infinity,
48892
+ ymin = bbox[1], ymax = bbox[3],
48893
+ gap;
48894
+
48895
+ // Detect dateline clustering with a streaming east/west split. The gap
48896
+ // between the two boxes can only shrink as more coordinates are scanned.
48897
+ forEachDatasetCoord(dataset, function(x) {
48898
+ if (x < 0) {
48899
+ if (x > xmaxW) xmaxW = x;
48900
+ } else {
48901
+ if (x < xminE) xminE = x;
48902
+ if (x > xmaxE) xmaxE = x;
48903
+ }
48904
+ if (xmaxW > -Infinity && xmaxE > -Infinity) {
48905
+ gap = xminE - xmaxW;
48906
+ return gap > 180;
48907
+ }
48908
+ });
48909
+
48910
+ if (xmaxW == -Infinity || xmaxE == -Infinity) return null;
48911
+ if (xminE - xmaxW <= 180) return null;
48912
+ return [xminE, ymin, xmaxW + 360, ymax];
48913
+ }
48914
+
48915
+ function forEachDatasetCoord(dataset, cb) {
48916
+ var arcs = dataset.arcs;
48917
+ var usedArcs = arcs ? new Uint8Array(arcs.size()) : null;
48918
+ var keepGoing = true;
48919
+ dataset.layers.forEach(function(lyr) {
48920
+ if (!keepGoing) return;
48921
+ if (lyr.geometry_type == 'point') {
48922
+ keepGoing = scanPointCoords(lyr.shapes, cb);
48923
+ } else if (arcs && (lyr.geometry_type == 'polygon' || lyr.geometry_type == 'polyline')) {
48924
+ forEachArcId(lyr.shapes, function(id) {
48925
+ var absId, iter;
48926
+ if (!keepGoing) return;
48927
+ absId = id < 0 ? ~id : id;
48928
+ if (usedArcs[absId]) return;
48929
+ usedArcs[absId] = 1;
48930
+ iter = arcs.getArcIter(absId);
48931
+ while (keepGoing && iter.hasNext()) {
48932
+ keepGoing = cb(iter.x, iter.y) !== false;
48933
+ }
48934
+ });
48935
+ }
48936
+ });
48937
+ }
48938
+
48939
+ function scanPointCoords(shapes, cb) {
48940
+ var shp, p;
48941
+ for (var i=0, n=shapes.length; i<n; i++) {
48942
+ shp = shapes[i];
48943
+ for (var j=0, m=shp ? shp.length : 0; j<m; j++) {
48944
+ p = shp[j];
48945
+ if (cb(p[0], p[1]) === false) return false;
48946
+ }
48947
+ }
48948
+ return true;
48862
48949
  }
48863
48950
 
48864
48951
  // See: Savric & Jenny, "Automating the selection of standard parallels for conic map projections"
@@ -48877,11 +48964,21 @@ ${svg}
48877
48964
  return `+lon_0=${ cx.toFixed(decimals) } +lat_0=${ cy.toFixed(decimals) }`;
48878
48965
  }
48879
48966
 
48967
+ function getUtmParams(bbox) {
48968
+ var cx = (bbox[0] + bbox[2]) / 2;
48969
+ var cy = (bbox[1] + bbox[3]) / 2;
48970
+ var zone = Math.floor((cx + 180) / 6) + 1;
48971
+ zone = Math.max(1, Math.min(60, zone));
48972
+ return `+zone=${ zone }` + (cy < 0 ? ' +south' : '');
48973
+ }
48974
+
48880
48975
  var ProjectionParams = /*#__PURE__*/Object.freeze({
48881
48976
  __proto__: null,
48882
48977
  expandProjDefn: expandProjDefn,
48978
+ getAutoFitBBox: getAutoFitBBox,
48883
48979
  getCenterParams: getCenterParams,
48884
- getConicParams: getConicParams
48980
+ getConicParams: getConicParams,
48981
+ getUtmParams: getUtmParams
48885
48982
  });
48886
48983
 
48887
48984
  cmd.proj = function(dataset, catalog, opts, targetLayers) {
@@ -55431,7 +55528,7 @@ ${svg}
55431
55528
  });
55432
55529
  }
55433
55530
 
55434
- var version = "0.7.11";
55531
+ var version = "0.7.12";
55435
55532
 
55436
55533
  // Parse command line args into commands and run them
55437
55534
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.7.11",
3
+ "version": "0.7.12",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
@@ -28,10 +28,10 @@
28
28
  "docs": "node build-docs.mjs",
29
29
  "roadmap": "node build-roadmap.mjs",
30
30
  "lint": "eslint --ext mjs src/",
31
- "prepublishOnly": "npm test; ./pre-publish",
31
+ "prepublishOnly": "npm test && npm run test:browser && ./pre-publish",
32
32
  "postpublish": "./release_web_ui; ./release_github_version",
33
33
  "dev": "rollup --config --watch",
34
- "test:browser": "playwright test"
34
+ "test:browser": "playwright test --fully-parallel --workers=6"
35
35
  },
36
36
  "main": "./mapshaper.js",
37
37
  "files": [
@@ -6726,13 +6726,11 @@
6726
6726
 
6727
6727
  function message() {
6728
6728
  var msg = GUI.formatMessageArgs(arguments);
6729
- if (gui.notify) {
6730
- gui.notify({severity: 'info', body: msg});
6731
- } else {
6729
+ if (!gui.notify) {
6732
6730
  // Fallback for early messages before MessageControl is constructed
6733
6731
  gui.message(msg);
6734
- internal.logArgs(arguments);
6735
6732
  }
6733
+ internal.logArgs(arguments);
6736
6734
  }
6737
6735
 
6738
6736
  // CLI warnings used to surface as modal alerts, which interrupt the user
package/www/mapshaper.js CHANGED
@@ -48764,7 +48764,7 @@ ${svg}
48764
48764
  return layerHasGeometry(lyr) && !layerIsFullyEnclosed(lyr, dataset, clipData);
48765
48765
  });
48766
48766
  if (layers.length > 0) {
48767
- clipLayersInPlace(layers, clipData, dataset, 'clip');
48767
+ clipLayersInPlace(layers, clipData, dataset, 'clip', getInternalClipOpts());
48768
48768
  return true;
48769
48769
  }
48770
48770
  return false;
@@ -48803,13 +48803,21 @@ ${svg}
48803
48803
  }
48804
48804
 
48805
48805
  function datasetCrossesLon(dataset, lon) {
48806
- var crosses = 0;
48807
- dataset.arcs.forEachSegment(function(i, j, xx, yy) {
48808
- var ax = xx[i],
48809
- bx = xx[j];
48810
- if (ax <= lon && bx >= lon || ax >= lon && bx <= lon) crosses++;
48806
+ var crosses = false;
48807
+ dataset.layers.filter(layerHasPaths).forEach(function(lyr) {
48808
+ if (crosses) return;
48809
+ lyr.shapes.forEach(function(shp) {
48810
+ if (crosses || !shp) return;
48811
+ forEachSegmentInShape(shp, dataset.arcs, function(i, j, xx, yy) {
48812
+ var ax = xx[i],
48813
+ bx = xx[j];
48814
+ if (ax <= lon && bx >= lon || ax >= lon && bx <= lon) {
48815
+ crosses = true;
48816
+ }
48817
+ });
48818
+ });
48811
48819
  });
48812
- return crosses > 0;
48820
+ return crosses;
48813
48821
  }
48814
48822
 
48815
48823
  function insertVerticalCut(dataset, lon) {
@@ -48820,29 +48828,38 @@ ${svg}
48820
48828
  // densify (so cut line can curve, e.g. Cupola projection)
48821
48829
  var geojson = bboxToPolygon(bbox, {interval: 0.5});
48822
48830
  var clip = importGeoJSON(geojson);
48823
- clipLayersInPlace(pathLayers, clip, dataset, 'erase');
48831
+ clipLayersInPlace(pathLayers, clip, dataset, 'erase', getInternalClipOpts());
48832
+ }
48833
+
48834
+ function getInternalClipOpts() {
48835
+ return {no_cleanup: true, no_warn: true};
48824
48836
  }
48825
48837
 
48826
- // Converts a Proj.4 projection name (e.g. lcc, tmerc) to a Proj.4 string
48838
+ // Converts a Proj.4 projection name (e.g. lcc, tmerc, utm) to a Proj.4 string
48827
48839
  // by picking parameters that are appropriate to the extent of the dataset
48828
48840
  // being projected (e.g. standard parallels, longitude of origin)
48829
- // Works for lcc, aea, tmerc, etc.
48841
+ // Works for lcc, aea, tmerc, utm, etc.
48830
48842
  // TODO: add more projections
48831
48843
  //
48832
48844
  function expandProjDefn(str, dataset, targetLayers) {
48833
48845
  var mproj = require$1('mproj');
48834
- var proj4, params, bbox, isConic2SP, isCentered, decimals;
48846
+ var proj4, params, bbox, isConic2SP, isCentered, isUtm, decimals;
48835
48847
  if (str in mproj.internal.pj_list === false) {
48836
48848
  // not a bare projection code -- assume valid projection string in other format
48837
48849
  return str;
48838
48850
  }
48839
48851
  isConic2SP = ['lcc', 'aea'].includes(str);
48840
48852
  isCentered = ['tmerc', 'etmerc'].includes(str);
48853
+ isUtm = str == 'utm';
48841
48854
  proj4 = '+proj=' + str;
48842
- if (isConic2SP || isCentered) {
48855
+ if (isConic2SP || isCentered || isUtm) {
48843
48856
  bbox = getBBox(dataset, targetLayers); // TODO: support projected datasets
48844
48857
  decimals = getBoundsPrecisionForDisplay(bbox);
48845
- params = isCentered ? getCenterParams(bbox, decimals) : getConicParams(bbox, decimals);
48858
+ if (isUtm) {
48859
+ params = getUtmParams(bbox);
48860
+ } else {
48861
+ params = isCentered ? getCenterParams(bbox, decimals) : getConicParams(bbox, decimals);
48862
+ }
48846
48863
  proj4 += ' ' + params;
48847
48864
  message(`Converted "${str}" to "${proj4}"`);
48848
48865
  }
@@ -48858,7 +48875,77 @@ ${svg}
48858
48875
  if (!isLatLngCRS(getDatasetCRS(dataset))) {
48859
48876
  stop$1('Expected unprojected data');
48860
48877
  }
48861
- return getDatasetBounds(source).toArray();
48878
+ return getAutoFitBBox(source);
48879
+ }
48880
+
48881
+ function getAutoFitBBox(dataset) {
48882
+ var bbox = getDatasetBounds(dataset).toArray();
48883
+ if (bbox[2] - bbox[0] > 180) {
48884
+ bbox = getWrappedBBox(dataset, bbox) || bbox;
48885
+ }
48886
+ return bbox;
48887
+ }
48888
+
48889
+ function getWrappedBBox(dataset, bbox) {
48890
+ var xmaxW = -Infinity,
48891
+ xminE = Infinity, xmaxE = -Infinity,
48892
+ ymin = bbox[1], ymax = bbox[3],
48893
+ gap;
48894
+
48895
+ // Detect dateline clustering with a streaming east/west split. The gap
48896
+ // between the two boxes can only shrink as more coordinates are scanned.
48897
+ forEachDatasetCoord(dataset, function(x) {
48898
+ if (x < 0) {
48899
+ if (x > xmaxW) xmaxW = x;
48900
+ } else {
48901
+ if (x < xminE) xminE = x;
48902
+ if (x > xmaxE) xmaxE = x;
48903
+ }
48904
+ if (xmaxW > -Infinity && xmaxE > -Infinity) {
48905
+ gap = xminE - xmaxW;
48906
+ return gap > 180;
48907
+ }
48908
+ });
48909
+
48910
+ if (xmaxW == -Infinity || xmaxE == -Infinity) return null;
48911
+ if (xminE - xmaxW <= 180) return null;
48912
+ return [xminE, ymin, xmaxW + 360, ymax];
48913
+ }
48914
+
48915
+ function forEachDatasetCoord(dataset, cb) {
48916
+ var arcs = dataset.arcs;
48917
+ var usedArcs = arcs ? new Uint8Array(arcs.size()) : null;
48918
+ var keepGoing = true;
48919
+ dataset.layers.forEach(function(lyr) {
48920
+ if (!keepGoing) return;
48921
+ if (lyr.geometry_type == 'point') {
48922
+ keepGoing = scanPointCoords(lyr.shapes, cb);
48923
+ } else if (arcs && (lyr.geometry_type == 'polygon' || lyr.geometry_type == 'polyline')) {
48924
+ forEachArcId(lyr.shapes, function(id) {
48925
+ var absId, iter;
48926
+ if (!keepGoing) return;
48927
+ absId = id < 0 ? ~id : id;
48928
+ if (usedArcs[absId]) return;
48929
+ usedArcs[absId] = 1;
48930
+ iter = arcs.getArcIter(absId);
48931
+ while (keepGoing && iter.hasNext()) {
48932
+ keepGoing = cb(iter.x, iter.y) !== false;
48933
+ }
48934
+ });
48935
+ }
48936
+ });
48937
+ }
48938
+
48939
+ function scanPointCoords(shapes, cb) {
48940
+ var shp, p;
48941
+ for (var i=0, n=shapes.length; i<n; i++) {
48942
+ shp = shapes[i];
48943
+ for (var j=0, m=shp ? shp.length : 0; j<m; j++) {
48944
+ p = shp[j];
48945
+ if (cb(p[0], p[1]) === false) return false;
48946
+ }
48947
+ }
48948
+ return true;
48862
48949
  }
48863
48950
 
48864
48951
  // See: Savric & Jenny, "Automating the selection of standard parallels for conic map projections"
@@ -48877,11 +48964,21 @@ ${svg}
48877
48964
  return `+lon_0=${ cx.toFixed(decimals) } +lat_0=${ cy.toFixed(decimals) }`;
48878
48965
  }
48879
48966
 
48967
+ function getUtmParams(bbox) {
48968
+ var cx = (bbox[0] + bbox[2]) / 2;
48969
+ var cy = (bbox[1] + bbox[3]) / 2;
48970
+ var zone = Math.floor((cx + 180) / 6) + 1;
48971
+ zone = Math.max(1, Math.min(60, zone));
48972
+ return `+zone=${ zone }` + (cy < 0 ? ' +south' : '');
48973
+ }
48974
+
48880
48975
  var ProjectionParams = /*#__PURE__*/Object.freeze({
48881
48976
  __proto__: null,
48882
48977
  expandProjDefn: expandProjDefn,
48978
+ getAutoFitBBox: getAutoFitBBox,
48883
48979
  getCenterParams: getCenterParams,
48884
- getConicParams: getConicParams
48980
+ getConicParams: getConicParams,
48981
+ getUtmParams: getUtmParams
48885
48982
  });
48886
48983
 
48887
48984
  cmd.proj = function(dataset, catalog, opts, targetLayers) {
@@ -55431,7 +55528,7 @@ ${svg}
55431
55528
  });
55432
55529
  }
55433
55530
 
55434
- var version = "0.7.11";
55531
+ var version = "0.7.12";
55435
55532
 
55436
55533
  // Parse command line args into commands and run them
55437
55534
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.