mapshaper 0.6.87 → 0.6.89

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
@@ -5004,11 +5004,12 @@
5004
5004
  }
5005
5005
 
5006
5006
  function toLngLat(xy, P) {
5007
- var proj;
5008
- if (isLatLngCRS(P)) {
5009
- return xy.concat();
5010
- }
5011
- proj = getProjTransform2(P, parseCrsString('wgs84'));
5007
+ return projectPoint(xy, P, parseCrsString('wgs84'));
5008
+ }
5009
+
5010
+ function projectPoint(xy, crsFrom, crsTo) {
5011
+ if (crsAreEqual(crsFrom, crsTo)) return xy.concat();
5012
+ var proj = getProjTransform2(crsFrom, crsTo);
5012
5013
  return proj(xy[0], xy[1]);
5013
5014
  }
5014
5015
 
@@ -5242,6 +5243,7 @@
5242
5243
  getProjTransform: getProjTransform,
5243
5244
  getProjTransform2: getProjTransform2,
5244
5245
  toLngLat: toLngLat,
5246
+ projectPoint: projectPoint,
5245
5247
  getProjInfo: getProjInfo,
5246
5248
  crsToProj4: crsToProj4,
5247
5249
  crsToPrj: crsToPrj,
@@ -5582,6 +5584,7 @@
5582
5584
  var arcOffs = _ii[i];
5583
5585
  var j = i * 4;
5584
5586
  var b = calcArcBounds(_xx, _yy, arcOffs, arcLen);
5587
+ // NOTE: if arcLen is 0, bounds coords are undefined, coerced to NaN in _bb.
5585
5588
  _bb[j++] = b[0];
5586
5589
  _bb[j++] = b[1];
5587
5590
  _bb[j++] = b[2];
@@ -5994,12 +5997,14 @@
5994
5997
  this.arcIntersectsBBox = function(i, b1) {
5995
5998
  var b2 = _bb,
5996
5999
  j = i * 4;
6000
+ // returns false if _bb bounds are NaN
5997
6001
  return b2[j] <= b1[2] && b2[j+2] >= b1[0] && b2[j+3] >= b1[1] && b2[j+1] <= b1[3];
5998
6002
  };
5999
6003
 
6000
6004
  this.arcIsContained = function(i, b1) {
6001
6005
  var b2 = _bb,
6002
6006
  j = i * 4;
6007
+ // returns false if _bb bounds are NaN
6003
6008
  return b2[j] >= b1[0] && b2[j+2] <= b1[2] && b2[j+1] >= b1[1] && b2[j+3] <= b1[3];
6004
6009
  };
6005
6010
 
@@ -6036,22 +6041,21 @@
6036
6041
  return bounds;
6037
6042
  };
6038
6043
 
6039
- this.getSimpleShapeBounds2 = function(arcIds, arr) {
6044
+ this.getSimpleShapeBbox = function(arcIds, arr) {
6040
6045
  var bbox = arr || [],
6041
6046
  bb = _bb,
6042
- id = absArcId(arcIds[0]) * 4;
6043
- bbox[0] = bb[id];
6044
- bbox[1] = bb[++id];
6045
- bbox[2] = bb[++id];
6046
- bbox[3] = bb[++id];
6047
- for (var i=1, n=arcIds.length; i<n; i++) {
6048
- id = absArcId(arcIds[i]) * 4;
6049
- if (bb[id] < bbox[0]) bbox[0] = bb[id];
6050
- if (bb[++id] < bbox[1]) bbox[1] = bb[id];
6051
- if (bb[++id] > bbox[2]) bbox[2] = bb[id];
6052
- if (bb[++id] > bbox[3]) bbox[3] = bb[id];
6047
+ arcId, offs;
6048
+ bbox[0] = bbox[1] = Infinity;
6049
+ bbox[2] = bbox[3] = -Infinity;
6050
+ for (var i=0, n=arcIds.length; i<n; i++) {
6051
+ arcId = absArcId(arcIds[i]);
6052
+ offs = arcId * 4;
6053
+ if (bb[offs] < bbox[0]) bbox[0] = bb[offs];
6054
+ if (bb[++offs] < bbox[1]) bbox[1] = bb[offs];
6055
+ if (bb[++offs] > bbox[2]) bbox[2] = bb[offs];
6056
+ if (bb[++offs] > bbox[3]) bbox[3] = bb[offs];
6053
6057
  }
6054
- return bbox;
6058
+ return bbox[0] == Infinity ? [] : bbox;
6055
6059
  };
6056
6060
 
6057
6061
  // TODO: move this and similar methods out of ArcCollection
@@ -6068,7 +6072,9 @@
6068
6072
  this.mergeArcBounds = function(arcId, bounds) {
6069
6073
  if (arcId < 0) arcId = ~arcId;
6070
6074
  var offs = arcId * 4;
6071
- bounds.mergeBounds(_bb[offs], _bb[offs+1], _bb[offs+2], _bb[offs+3]);
6075
+ if (_nn[arcId] > 0) {
6076
+ bounds.mergeBounds(_bb[offs], _bb[offs+1], _bb[offs+2], _bb[offs+3]);
6077
+ }
6072
6078
  };
6073
6079
  }
6074
6080
 
@@ -17254,7 +17260,7 @@
17254
17260
  }
17255
17261
 
17256
17262
  function procShapeRing(path) {
17257
- currRingBbox = arcs.getSimpleShapeBounds2(path);
17263
+ currRingBbox = arcs.getSimpleShapeBbox(path);
17258
17264
  ringIndex.setIds(path);
17259
17265
  procArcIds(path);
17260
17266
  ringIndex.clear();
@@ -38149,7 +38155,7 @@ ${svg}
38149
38155
 
38150
38156
  // Assumes that ring boundaries to not cross
38151
38157
  function ringHasHoles(ring, rings, arcs) {
38152
- var bbox = arcs.getSimpleShapeBounds2(ring);
38158
+ var bbox = arcs.getSimpleShapeBbox(ring);
38153
38159
  var sibling, p;
38154
38160
  for (var i=0, n=rings.length; i<n; i++) {
38155
38161
  sibling = rings[i];
@@ -45578,7 +45584,7 @@ ${svg}
45578
45584
  });
45579
45585
  }
45580
45586
 
45581
- var version = "0.6.87";
45587
+ var version = "0.6.89";
45582
45588
 
45583
45589
  // Parse command line args into commands and run them
45584
45590
  // 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.6.87",
3
+ "version": "0.6.89",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
@@ -56,7 +56,7 @@
56
56
  "iconv-lite": "^0.6.3",
57
57
  "idb-keyval": "^6.2.0",
58
58
  "kdbush": "^3.0.0",
59
- "mproj": "0.0.38",
59
+ "mproj": "0.0.39",
60
60
  "msgpackr": "^1.10.1",
61
61
  "opn": "^5.3.0",
62
62
  "rw": "~1.3.3",
package/www/index.html CHANGED
@@ -123,7 +123,7 @@
123
123
  <div class="erase-btn btn sidebar-btn">Erase</div>
124
124
  <div class="rect-btn btn sidebar-btn">Rectangle</div>
125
125
  <div class="frame-btn btn sidebar-btn">Frame</div>
126
- <div class="info-btn btn sidebar-btn">Coords</div>
126
+ <div class="info-btn btn sidebar-btn">Bounds</div>
127
127
  <div class="box-coords selectable"></div>
128
128
  <div class="cancel-btn btn sidebar-btn">Cancel</div>
129
129
  </div>
@@ -135,7 +135,7 @@
135
135
  <div class="filter-btn btn sidebar-btn">Keep</div>
136
136
  <div class="duplicate-btn btn sidebar-btn">Duplicate</div>
137
137
  <div class="split-btn btn sidebar-btn">Split</div>
138
- <div class="coords-btn btn sidebar-btn toggle-btn">Coords</div>
138
+ <div class="coords-btn btn sidebar-btn toggle-btn">Bounds</div>
139
139
  <div class="box-coords selectable"></div>
140
140
  <div class="data-btn btn sidebar-btn toggle-btn">Edit data</div>
141
141
  <div class="cancel-btn btn sidebar-btn">Clear</div>
@@ -3500,15 +3500,6 @@
3500
3500
  }
3501
3501
  }
3502
3502
 
3503
- function deleteFeature(lyr, fid) {
3504
- var records = lyr.data?.getRecords();
3505
- if (records) records.splice(fid, 1);
3506
- lyr.shapes.splice(fid, 1);
3507
- if (isProjectedLayer(lyr) && lyr.shapes != lyr.gui.displayLayer.shapes) {
3508
- lyr.gui.displayLayer.shapes.splice(fid, 1);
3509
- }
3510
- }
3511
-
3512
3503
  // p: one point in source data coords
3513
3504
  function appendNewPoint(lyr, p) {
3514
3505
  lyr.shapes.push([p]);
@@ -3521,15 +3512,20 @@
3521
3512
  }
3522
3513
  }
3523
3514
 
3524
- function deletePoint(lyr, fid) {
3525
- deleteFeature(lyr, fid);
3515
+ function deleteFeature(lyr, fid) {
3516
+ var records = lyr.data?.getRecords();
3517
+ if (records) records.splice(fid, 1);
3518
+ lyr.shapes.splice(fid, 1);
3519
+ if (isProjectedLayer(lyr) && lyr.geometry_type == 'point') {
3520
+ lyr.gui.displayLayer.shapes.splice(fid, 1); // point layer
3521
+ }
3526
3522
  }
3527
3523
 
3528
- function insertPoint(lyr, fid, shp, d) {
3524
+ function insertFeature(lyr, fid, shp, d) {
3529
3525
  var records = lyr.data?.getRecords();
3530
3526
  if (records) records.splice(fid, 0, d);
3531
3527
  lyr.shapes.splice(fid, 0, shp);
3532
- if (isProjectedLayer(lyr)) {
3528
+ if (isProjectedLayer(lyr) && lyr.geometry_type == 'point') {
3533
3529
  var shp2 = projectPointCoords(shp, lyr.gui.projectPoint);
3534
3530
  lyr.gui.displayLayer.shapes.splice(fid, 0, shp2);
3535
3531
  }
@@ -4255,6 +4251,10 @@
4255
4251
  setDisplayProjection(gui, cmd);
4256
4252
  } else {
4257
4253
  line.hide(); // hide cursor while command is being run
4254
+ // quit certain edit modes
4255
+ if (!gui.interaction.modeWorksWithConsole(gui.interaction.getMode())) {
4256
+ gui.interaction.turnOff();
4257
+ }
4258
4258
  runMapshaperCommands(cmd, function(err, flags) {
4259
4259
  if (flags) {
4260
4260
  gui.clearMode();
@@ -5606,12 +5606,12 @@
5606
5606
  addHistoryState(undo, redo);
5607
5607
  });
5608
5608
 
5609
- gui.on('point_delete', function(e) {
5609
+ gui.on('feature_delete', function(e) {
5610
5610
  var redo = function() {
5611
- deletePoint(e.data.target, e.fid);
5611
+ deleteFeature(e.data.target, e.fid);
5612
5612
  };
5613
5613
  var undo = function() {
5614
- insertPoint(e.data.target, e.fid, e.coords, e.d);
5614
+ insertFeature(e.data.target, e.fid, e.coords, e.d);
5615
5615
  };
5616
5616
  addHistoryState(undo, redo);
5617
5617
  });
@@ -5833,7 +5833,7 @@
5833
5833
 
5834
5834
  document.addEventListener('keydown', function(e) {
5835
5835
  if (!GUI.isActiveInstance(gui) || e.repeat && e.keyCode == 32) return;
5836
- updateControlKeys(e, 'keyup');
5836
+ updateControlKeys(e, 'keydown');
5837
5837
  self.dispatchEvent('keydown', getEventData(e));
5838
5838
  });
5839
5839
 
@@ -5966,6 +5966,10 @@
5966
5966
  setMode('off');
5967
5967
  };
5968
5968
 
5969
+ this.modeWorksWithConsole = function(mode) {
5970
+ return ['off', 'info'];
5971
+ };
5972
+
5969
5973
  this.modeUsesHitDetection = function(mode) {
5970
5974
  return ['info', 'selection', 'data', 'labels', 'edit_points', 'vertices', 'rectangles', 'edit_lines', 'edit_polygons'].includes(mode);
5971
5975
  };
@@ -7904,7 +7908,7 @@
7904
7908
  return;
7905
7909
  }
7906
7910
  for (var i = 0, n = shp && shp.length; i < n; i++) {
7907
- arcs.getSimpleShapeBounds2(shp[i], bbox);
7911
+ arcs.getSimpleShapeBbox(shp[i], bbox);
7908
7912
  if (x + dist < bbox[0] || x - dist > bbox[2] ||
7909
7913
  y + dist < bbox[1] || y - dist > bbox[3]) {
7910
7914
  continue; // bbox non-intersection
@@ -8497,8 +8501,8 @@
8497
8501
  };
8498
8502
  if (evt) {
8499
8503
  // data coordinates
8500
- eventData.coordinates = translateDisplayPoint(targetLayer, ext.translatePixelCoords(evt.x, evt.y));
8501
- eventData.display_coordinates = gui.map.pixelCoordsToDisplayCoords(evt.x, evt.y);
8504
+ eventData.projected_coordinates = gui.map.pixelCoordsToProjectedCoords(evt.x, evt.y);
8505
+ eventData.lonlat_coordinates = gui.map.pixelCoordsToLngLatCoords(evt.x, evt.y);
8502
8506
  eventData.originalEvent = evt;
8503
8507
  eventData.overMap = isOverMap(evt);
8504
8508
  }
@@ -10243,8 +10247,8 @@
10243
10247
  function removePoint(target, id) {
10244
10248
  var d = target.data ? target.data.getRecords()[id] : null;
10245
10249
  var coords = target.shapes[id];
10246
- deletePoint(target, id);
10247
- gui.dispatchEvent('point_delete', {coords, d, target, fid: id});
10250
+ deleteFeature(target, id);
10251
+ gui.dispatchEvent('feature_delete', {coords, d, target, fid: id});
10248
10252
  gui.dispatchEvent('map-needs-refresh');
10249
10253
  hit.setHitId(-1);
10250
10254
  }
@@ -10316,7 +10320,7 @@
10316
10320
 
10317
10321
  function initLineEditing(gui, ext, hit) {
10318
10322
  var hoverVertexInfo;
10319
- var prevClickEvent;
10323
+ var prevVertexAddedEvent;
10320
10324
  var prevHoverEvent;
10321
10325
  var initialArcCount = -1;
10322
10326
  var initialShapeCount = -1;
@@ -10487,12 +10491,12 @@
10487
10491
  hit.clearDrawingId();
10488
10492
  drawingId = -1;
10489
10493
  hoverVertexInfo = null;
10490
- prevClickEvent = prevHoverEvent = null;
10494
+ prevVertexAddedEvent = prevHoverEvent = null;
10491
10495
  updateCursor();
10492
10496
  }
10493
10497
 
10494
10498
  gui.keyboard.on('keydown', function(e) {
10495
- if (active() && e.keyName == 'space') {
10499
+ if (pathDrawing() && e.keyName == 'space') {
10496
10500
  e.stopPropagation(); // prevent console from opening if shift-panning
10497
10501
  }
10498
10502
  }, null, 1);
@@ -10506,6 +10510,7 @@
10506
10510
  deleteActiveVertex(e, vInfo);
10507
10511
  };
10508
10512
  }
10513
+
10509
10514
  // don't allow copying of open paths as geojson in polygon mode
10510
10515
  gui.contextMenu.open(e, target);
10511
10516
  });
@@ -10544,6 +10549,7 @@
10544
10549
  hoverVertexInfo = findPathStartInfo(e);
10545
10550
  var xy = [e.x, e.y], xy2;
10546
10551
  var p = pixToDataCoords(e.x, e.y);
10552
+ var addedToPath = true;
10547
10553
  if (!pathDrawing()) {
10548
10554
  pencilPoints = [xy];
10549
10555
  startNewPath(p);
@@ -10557,7 +10563,7 @@
10557
10563
  appendVertex$1(hit.getHitTarget(), p);
10558
10564
  extendCurrentPath(p);
10559
10565
  pencilPoints = null; // stop drawing
10560
- } else if (pencilPoints.length >= 2 && pointExceedsTolerance(xy, pencilPoints, 1.4)) {
10566
+ } else if (pencilPoints.length >= 2 && pointExceedsTolerance(xy, pencilPoints, 1.2)) {
10561
10567
  xy2 = pencilPoints.pop();
10562
10568
  p = pixToDataCoords(xy2[0], xy2[1]);
10563
10569
  extendCurrentPath(p);
@@ -10569,6 +10575,11 @@
10569
10575
  // skip this point, update the hover line
10570
10576
  pencilPoints.push(xy);
10571
10577
  updatePathEndpoint(p);
10578
+ addedToPath = false;
10579
+ }
10580
+ if (addedToPath) {
10581
+ //
10582
+ prevVertexAddedEvent = e;
10572
10583
  }
10573
10584
  }, null, 3); // higher priority than hit control
10574
10585
 
@@ -10625,7 +10636,7 @@
10625
10636
  return;
10626
10637
  }
10627
10638
  if (gui.keyboard.shiftIsPressed()) {
10628
- alignPointerPosition(e, prevClickEvent);
10639
+ alignPointerPosition(e, prevVertexAddedEvent);
10629
10640
  }
10630
10641
  updatePathEndpoint(pixToDataCoords(e.x, e.y));
10631
10642
  }
@@ -10658,12 +10669,12 @@
10658
10669
  } else {
10659
10670
  startNewPath(p);
10660
10671
  }
10661
- prevClickEvent = e;
10672
+ prevVertexAddedEvent = e;
10662
10673
  });
10663
10674
 
10664
10675
  // esc or enter key finishes a path
10665
10676
  gui.keyboard.on('keydown', function(e) {
10666
- if (active() && (e.keyName == 'esc' || e.keyName == 'enter')) {
10677
+ if (pathDrawing() && (e.keyName == 'esc' || e.keyName == 'enter')) {
10667
10678
  e.stopPropagation();
10668
10679
  finishCurrentPath();
10669
10680
  e.originalEvent.preventDefault(); // block console "enter"
@@ -10672,10 +10683,10 @@
10672
10683
 
10673
10684
  // detect second 'click' event of a double-click action
10674
10685
  function detectDoubleClick(evt) {
10675
- if (!prevClickEvent) return false;
10676
- var elapsed = evt.time - prevClickEvent.time;
10677
- var dx = Math.abs(evt.x - prevClickEvent.x);
10678
- var dy = Math.abs(evt.y - prevClickEvent.y);
10686
+ if (!prevVertexAddedEvent) return false;
10687
+ var elapsed = evt.time - prevVertexAddedEvent.time;
10688
+ var dx = Math.abs(evt.x - prevVertexAddedEvent.x);
10689
+ var dy = Math.abs(evt.y - prevVertexAddedEvent.y);
10679
10690
  var dbl = elapsed < 500 && dx <= 2 && dy <= 2;
10680
10691
  return dbl;
10681
10692
  }
@@ -12394,21 +12405,24 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
12394
12405
  target.layer.pinned = !!pinned;
12395
12406
  };
12396
12407
 
12397
- this.translatePixelCoords = function(x, y) {
12398
- var p = _ext.translatePixelCoords(x, y);
12399
- if (!_dynamicCRS) return p;
12400
- return internal.toLngLat(p, _dynamicCRS);
12408
+ this.pixelCoordsToLngLatCoords = function(x, y) {
12409
+ var crsFrom = this.getDisplayCRS();
12410
+ if (!crsFrom) return null; // e.g. table view
12411
+ var p1 = internal.toLngLat(_ext.translatePixelCoords(x, y), crsFrom);
12412
+ var p2 = internal.toLngLat(_ext.translatePixelCoords(x+1, y+1), crsFrom);
12413
+ return p1 && p2 && p1[1] <= 90 && p1[1] >= -90 ?
12414
+ formatCoordsForDisplay(p1, p2) : null;
12401
12415
  };
12402
12416
 
12403
- this.pixelCoordsToDisplayCoords = function(x, y) {
12404
- var p1 = _ext.translatePixelCoords(x, y);
12405
- var p2 = _ext.translatePixelCoords(x+1, y+1);
12406
- var crs = this.getDisplayCRS();
12407
- if (crs) {
12408
- p1 = internal.toLngLat(p1, crs) || p1;
12409
- p2 = internal.toLngLat(p2, crs) || p2;
12417
+ this.pixelCoordsToProjectedCoords = function(x, y) {
12418
+ if (!_activeLyr) return null;
12419
+ var info = getDatasetCrsInfo(_activeLyr.gui.source.dataset);
12420
+ if (info && internal.isLatLngCRS(info.crs)) {
12421
+ return null; // latlon dataset
12410
12422
  }
12411
- return formatCoordsForDisplay(p1, p2);
12423
+ var p1 = translateDisplayPoint(_activeLyr, _ext.translatePixelCoords(x, y));
12424
+ var p2 = translateDisplayPoint(_activeLyr, _ext.translatePixelCoords(x+1, y+1));
12425
+ return p1 && p2 ? formatCoordsForDisplay(p1, p2) : null;
12412
12426
  };
12413
12427
 
12414
12428
  // this.getCenterLngLat = function() {
@@ -12885,21 +12899,64 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
12885
12899
  }
12886
12900
 
12887
12901
  function addMenuItem(label, func) {
12902
+ var prefix = '• &nbsp;';
12903
+
12888
12904
  El('div')
12889
12905
  .appendTo(menu)
12890
12906
  .addClass('contextmenu-item')
12891
- .html(label)
12907
+ .html(prefix + label)
12892
12908
  .on('click', func)
12893
12909
  .show();
12894
12910
  }
12895
12911
 
12912
+ function addMenuLabel(label) {
12913
+ El('div')
12914
+ .appendTo(menu)
12915
+ .addClass('contextmenu-label')
12916
+ .html(label);
12917
+ }
12918
+
12896
12919
  this.open = function(e, lyr) {
12920
+ var copyable = e.ids?.length;
12897
12921
  if (lyr && !lyr.gui.geographic) return; // no popup for tabular data
12922
+ menu.empty();
12923
+
12924
+ // menu contents
12925
+ //
12926
+ if (e.deleteVertex || e.deletePoint || copyable || e.deleteFeature) {
12927
+
12928
+ addMenuLabel('selection');
12929
+ if (e.deleteVertex) {
12930
+ addMenuItem('delete vertex', e.deleteVertex);
12931
+ }
12932
+ if (e.deletePoint) {
12933
+ addMenuItem('delete point', e.deletePoint);
12934
+ }
12935
+ if (e.ids?.length) {
12936
+ addMenuItem('copy as GeoJSON', copyGeoJSON);
12937
+ }
12938
+ if (e.deleteFeature) {
12939
+ addMenuItem(getDeleteLabel(), e.deleteFeature);
12940
+ }
12941
+ }
12942
+
12943
+ if (e.lonlat_coordinates) {
12944
+ addMenuLabel('longitude, latitude');
12945
+ addCoords(e.lonlat_coordinates);
12946
+ }
12947
+ if (e.projected_coordinates) {
12948
+ addMenuLabel('easting, northing');
12949
+ addCoords(e.projected_coordinates);
12950
+ }
12951
+
12952
+ if (menu.node().childNodes.length === 0) {
12953
+ return;
12954
+ }
12955
+
12898
12956
  _open = true;
12899
12957
  _openCount++;
12900
12958
  var rspace = body.clientWidth - e.pageX;
12901
12959
  var xoffs = 10;
12902
- menu.empty().show();
12903
12960
  if (rspace > 150) {
12904
12961
  menu.css('left', e.pageX + xoffs + 'px');
12905
12962
  menu.css('right', null);
@@ -12908,32 +12965,16 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
12908
12965
  menu.css('left', null);
12909
12966
  }
12910
12967
  menu.css('top', (e.pageY - 15) + 'px');
12911
-
12912
- // menu contents
12913
- //
12914
- if (e.display_coordinates) {
12915
- addCoords(e.display_coordinates);
12916
- }
12917
- if (e.deleteVertex) {
12918
- addMenuItem('Delete vertex', e.deleteVertex);
12919
- }
12920
- if (e.deletePoint) {
12921
- addMenuItem('Delete point', e.deletePoint);
12922
- }
12923
- if (e.ids?.length) {
12924
- addMenuItem('Copy as GeoJSON', copyGeoJSON);
12925
- }
12926
- if (e.deleteFeature) {
12927
- addMenuItem(getDeleteLabel(), e.deleteFeature);
12928
- }
12968
+ menu.show();
12929
12969
 
12930
12970
  function getDeleteLabel() {
12931
- return 'Delete ' + (lyr.geometry_type == 'point' ? 'point' : 'shape');
12971
+ return 'delete ' + (lyr.geometry_type == 'point' ? 'point' : 'shape');
12932
12972
  }
12933
12973
 
12934
12974
  function addCoords(p) {
12935
12975
  var coordStr = p[0] + ',' + p[1];
12936
- var displayStr = '• &nbsp;' + coordStr.replace(/-/g, '–').replace(',', ', ');
12976
+ // var displayStr = '• &nbsp;' + coordStr.replace(/-/g, '–').replace(',', ', ');
12977
+ var displayStr = coordStr.replace(/-/g, '–').replace(',', ', ');
12937
12978
  addMenuItem(displayStr, function() {
12938
12979
  saveFileContentToClipboard(coordStr);
12939
12980
  });
package/www/mapshaper.js CHANGED
@@ -5004,11 +5004,12 @@
5004
5004
  }
5005
5005
 
5006
5006
  function toLngLat(xy, P) {
5007
- var proj;
5008
- if (isLatLngCRS(P)) {
5009
- return xy.concat();
5010
- }
5011
- proj = getProjTransform2(P, parseCrsString('wgs84'));
5007
+ return projectPoint(xy, P, parseCrsString('wgs84'));
5008
+ }
5009
+
5010
+ function projectPoint(xy, crsFrom, crsTo) {
5011
+ if (crsAreEqual(crsFrom, crsTo)) return xy.concat();
5012
+ var proj = getProjTransform2(crsFrom, crsTo);
5012
5013
  return proj(xy[0], xy[1]);
5013
5014
  }
5014
5015
 
@@ -5242,6 +5243,7 @@
5242
5243
  getProjTransform: getProjTransform,
5243
5244
  getProjTransform2: getProjTransform2,
5244
5245
  toLngLat: toLngLat,
5246
+ projectPoint: projectPoint,
5245
5247
  getProjInfo: getProjInfo,
5246
5248
  crsToProj4: crsToProj4,
5247
5249
  crsToPrj: crsToPrj,
@@ -5582,6 +5584,7 @@
5582
5584
  var arcOffs = _ii[i];
5583
5585
  var j = i * 4;
5584
5586
  var b = calcArcBounds(_xx, _yy, arcOffs, arcLen);
5587
+ // NOTE: if arcLen is 0, bounds coords are undefined, coerced to NaN in _bb.
5585
5588
  _bb[j++] = b[0];
5586
5589
  _bb[j++] = b[1];
5587
5590
  _bb[j++] = b[2];
@@ -5994,12 +5997,14 @@
5994
5997
  this.arcIntersectsBBox = function(i, b1) {
5995
5998
  var b2 = _bb,
5996
5999
  j = i * 4;
6000
+ // returns false if _bb bounds are NaN
5997
6001
  return b2[j] <= b1[2] && b2[j+2] >= b1[0] && b2[j+3] >= b1[1] && b2[j+1] <= b1[3];
5998
6002
  };
5999
6003
 
6000
6004
  this.arcIsContained = function(i, b1) {
6001
6005
  var b2 = _bb,
6002
6006
  j = i * 4;
6007
+ // returns false if _bb bounds are NaN
6003
6008
  return b2[j] >= b1[0] && b2[j+2] <= b1[2] && b2[j+1] >= b1[1] && b2[j+3] <= b1[3];
6004
6009
  };
6005
6010
 
@@ -6036,22 +6041,21 @@
6036
6041
  return bounds;
6037
6042
  };
6038
6043
 
6039
- this.getSimpleShapeBounds2 = function(arcIds, arr) {
6044
+ this.getSimpleShapeBbox = function(arcIds, arr) {
6040
6045
  var bbox = arr || [],
6041
6046
  bb = _bb,
6042
- id = absArcId(arcIds[0]) * 4;
6043
- bbox[0] = bb[id];
6044
- bbox[1] = bb[++id];
6045
- bbox[2] = bb[++id];
6046
- bbox[3] = bb[++id];
6047
- for (var i=1, n=arcIds.length; i<n; i++) {
6048
- id = absArcId(arcIds[i]) * 4;
6049
- if (bb[id] < bbox[0]) bbox[0] = bb[id];
6050
- if (bb[++id] < bbox[1]) bbox[1] = bb[id];
6051
- if (bb[++id] > bbox[2]) bbox[2] = bb[id];
6052
- if (bb[++id] > bbox[3]) bbox[3] = bb[id];
6047
+ arcId, offs;
6048
+ bbox[0] = bbox[1] = Infinity;
6049
+ bbox[2] = bbox[3] = -Infinity;
6050
+ for (var i=0, n=arcIds.length; i<n; i++) {
6051
+ arcId = absArcId(arcIds[i]);
6052
+ offs = arcId * 4;
6053
+ if (bb[offs] < bbox[0]) bbox[0] = bb[offs];
6054
+ if (bb[++offs] < bbox[1]) bbox[1] = bb[offs];
6055
+ if (bb[++offs] > bbox[2]) bbox[2] = bb[offs];
6056
+ if (bb[++offs] > bbox[3]) bbox[3] = bb[offs];
6053
6057
  }
6054
- return bbox;
6058
+ return bbox[0] == Infinity ? [] : bbox;
6055
6059
  };
6056
6060
 
6057
6061
  // TODO: move this and similar methods out of ArcCollection
@@ -6068,7 +6072,9 @@
6068
6072
  this.mergeArcBounds = function(arcId, bounds) {
6069
6073
  if (arcId < 0) arcId = ~arcId;
6070
6074
  var offs = arcId * 4;
6071
- bounds.mergeBounds(_bb[offs], _bb[offs+1], _bb[offs+2], _bb[offs+3]);
6075
+ if (_nn[arcId] > 0) {
6076
+ bounds.mergeBounds(_bb[offs], _bb[offs+1], _bb[offs+2], _bb[offs+3]);
6077
+ }
6072
6078
  };
6073
6079
  }
6074
6080
 
@@ -17254,7 +17260,7 @@
17254
17260
  }
17255
17261
 
17256
17262
  function procShapeRing(path) {
17257
- currRingBbox = arcs.getSimpleShapeBounds2(path);
17263
+ currRingBbox = arcs.getSimpleShapeBbox(path);
17258
17264
  ringIndex.setIds(path);
17259
17265
  procArcIds(path);
17260
17266
  ringIndex.clear();
@@ -38149,7 +38155,7 @@ ${svg}
38149
38155
 
38150
38156
  // Assumes that ring boundaries to not cross
38151
38157
  function ringHasHoles(ring, rings, arcs) {
38152
- var bbox = arcs.getSimpleShapeBounds2(ring);
38158
+ var bbox = arcs.getSimpleShapeBbox(ring);
38153
38159
  var sibling, p;
38154
38160
  for (var i=0, n=rings.length; i<n; i++) {
38155
38161
  sibling = rings[i];
@@ -45578,7 +45584,7 @@ ${svg}
45578
45584
  });
45579
45585
  }
45580
45586
 
45581
- var version = "0.6.87";
45587
+ var version = "0.6.89";
45582
45588
 
45583
45589
  // Parse command line args into commands and run them
45584
45590
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
package/www/page.css CHANGED
@@ -1206,8 +1206,9 @@ div.basemap-style-btn.active {
1206
1206
  }
1207
1207
 
1208
1208
  .basemap-overlay-btn img {
1209
- margin-left: -21px;
1210
- width: 70px;
1209
+ margin-left: -31px;
1210
+ margin-top: -5px;
1211
+ width: 90px;
1211
1212
  }
1212
1213
 
1213
1214
  .mapbox-improve-map {
@@ -1379,9 +1380,19 @@ body.pan.panning .map-layers:not(.drawing) {
1379
1380
  .contextmenu-item {
1380
1381
  background-color: #fff;
1381
1382
  white-space: nowrap;
1382
- padding: 6px 10px 7px 10px;
1383
- line-height: 11px;
1383
+ padding: 3px 10px 4px 10px;
1384
1384
  cursor: pointer;
1385
+ font-size: 13px;
1386
+ line-height: 13px;
1387
+ }
1388
+
1389
+ .contextmenu-label {
1390
+ color: #999;
1391
+ white-space: nowrap;
1392
+ padding: 4px 10px 1px 10px;
1393
+ font-size: 12px;
1394
+ line-height: 12px;
1395
+ margin-top: -2px;
1385
1396
  }
1386
1397
 
1387
1398
  .nav-menu-item:hover,