mapshaper 0.5.94 → 0.5.97

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mapshaper",
3
- "version": "0.5.94",
3
+ "version": "0.5.97",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
@@ -3728,24 +3728,36 @@
3728
3728
  this.addHistoryState(stashedUndo, redo);
3729
3729
  }, this);
3730
3730
 
3731
- // undo/redo vertex dragging
3732
- gui.on('vertex_dragstart', function(e) {
3733
- stashedUndo = this.makeVertexSetter(e.FID, e.vertex_ids);
3734
- }, this);
3735
-
3736
3731
  gui.on('vertex_dragend', function(e) {
3737
- var redo = this.makeVertexSetter(e.FID, e.vertex_ids);
3738
- this.addHistoryState(stashedUndo, redo);
3732
+ var target = gui.model.getActiveLayer();
3733
+ var arcs = target.dataset.arcs;
3734
+ var startPoint = e.points[0];
3735
+ var endPoint = internal.getVertexCoords(e.ids[0], arcs);
3736
+ var undo = function() {
3737
+ if (e.insertion) {
3738
+ internal.deleteVertex(arcs, e.ids[0]);
3739
+ } else {
3740
+ snapVerticesToPoint(e.ids, startPoint, arcs, true);
3741
+ }
3742
+ };
3743
+ var redo = function() {
3744
+ if (e.insertion) {
3745
+ internal.insertVertex(arcs, e.ids[0], e.points[0]);
3746
+ }
3747
+ snapVerticesToPoint(e.ids, endPoint, arcs, true);
3748
+ };
3749
+ this.addHistoryState(undo, redo);
3739
3750
  }, this);
3740
3751
 
3741
- gui.on('vertex_insert', function(e) {
3752
+ gui.on('vertex_delete', function(e) {
3742
3753
  var target = gui.model.getActiveLayer();
3743
3754
  var arcs = target.dataset.arcs;
3744
- var undo = function() {
3755
+ var p = arcs.getVertex2(e.vertex_id);
3756
+ var redo = function() {
3745
3757
  internal.deleteVertex(arcs, e.vertex_id);
3746
3758
  };
3747
- var redo = function() {
3748
- internal.insertVertex(arcs, e.vertex_id, e.coordinates);
3759
+ var undo = function() {
3760
+ internal.insertVertex(arcs, e.vertex_id, p);
3749
3761
  };
3750
3762
  this.addHistoryState(undo, redo);
3751
3763
  }, this);
@@ -3771,7 +3783,7 @@
3771
3783
  };
3772
3784
  };
3773
3785
 
3774
- this.makeVertexSetter = function(fid, ids) {
3786
+ this.makeVertexSetter = function(ids) {
3775
3787
  var target = gui.model.getActiveLayer();
3776
3788
  var arcs = target.dataset.arcs;
3777
3789
  var p = internal.getVertexCoords(ids[0], arcs);
@@ -5611,14 +5623,11 @@
5611
5623
  }
5612
5624
 
5613
5625
  function vertexTest(x, y) {
5614
- var maxDist = getZoomAdjustedHitBuffer(20, 2),
5615
- bufDist = getZoomAdjustedHitBuffer(0.05), // tiny threshold for hitting almost-identical lines
5626
+ var maxDist = getZoomAdjustedHitBuffer(25, 2),
5616
5627
  cands = findHitCandidates(x, y, maxDist);
5617
5628
  sortByDistance(x, y, cands, displayLayer.arcs);
5618
- cands = pickNearestCandidates(cands, bufDist, maxDist);
5619
- var arcs = cands.map(function(cand) { return absArcId(cand.info.arcId); });
5629
+ cands = pickNearestCandidates(cands, 0, maxDist);
5620
5630
  return {
5621
- arcs: utils$1.uniq(arcs),
5622
5631
  ids: utils$1.pluck(cands, 'id')
5623
5632
  };
5624
5633
  }
@@ -6003,18 +6012,6 @@
6003
6012
  triggerHitEvent('change');
6004
6013
  };
6005
6014
 
6006
- self.setSelectedVertices = function(points) {
6007
- if (!active) return;
6008
- storedData.selected_points = points;
6009
- triggerHitEvent('change');
6010
- };
6011
-
6012
- self.clearSelectedVertices = function() {
6013
- if (!storedData.selected_points) return;
6014
- delete storedData.selected_points;
6015
- triggerHitEvent('change');
6016
- };
6017
-
6018
6015
  self.clearVertexOverlay = function() {
6019
6016
  if (!storedData.hit_coordinates) return;
6020
6017
  delete storedData.hit_coordinates;
@@ -6182,9 +6179,11 @@
6182
6179
  newData = noHitData();
6183
6180
  selectionIds = [];
6184
6181
  }
6182
+
6185
6183
  if (!testHitChange(storedData, newData)) {
6186
6184
  return;
6187
6185
  }
6186
+
6188
6187
  storedData = newData;
6189
6188
  gui.container.findChild('.map-layers').classed('symbol-hit', nonEmpty);
6190
6189
  if (active) {
@@ -7534,25 +7533,20 @@
7534
7533
  }
7535
7534
  }
7536
7535
 
7536
+ // pointer thresholds for hovering near a vertex or segment midpoint
7537
7537
  var HOVER_THRESHOLD = 8;
7538
- var MIDPOINT_THRESHOLD = 12;
7539
-
7538
+ var MIDPOINT_THRESHOLD = 11;
7540
7539
 
7541
7540
  function initVertexDragging(gui, ext, hit) {
7542
- var activeShapeId = -1;
7543
- var draggedVertexIds = null;
7544
- var selectedVertexIds = null;
7545
- var activeMidpoint; // {point, segment}
7541
+ var insertionPoint;
7542
+ var dragInfo;
7546
7543
 
7547
- function active(e) {
7548
- return e.id > -1 && gui.interaction.getMode() == 'vertices';
7544
+ function active() {
7545
+ return gui.interaction.getMode() == 'vertices';
7549
7546
  }
7550
7547
 
7551
- function fire(type) {
7552
- gui.dispatchEvent(type, {
7553
- FID: activeShapeId,
7554
- vertex_ids: draggedVertexIds
7555
- });
7548
+ function dragging() {
7549
+ return active() && !!dragInfo;
7556
7550
  }
7557
7551
 
7558
7552
  function setHoverVertex(id) {
@@ -7562,7 +7556,6 @@
7562
7556
 
7563
7557
  function clearHoverVertex() {
7564
7558
  hit.clearVertexOverlay();
7565
- // gui.state.vertex_overlay = null;
7566
7559
  }
7567
7560
 
7568
7561
  function findDraggableVertices(e) {
@@ -7574,121 +7567,103 @@
7574
7567
  var dist = geom.distance2D(p[0], p[1], p2[0], p2[1]);
7575
7568
  var pixelDist = dist / ext.getPixelSize();
7576
7569
  if (pixelDist > HOVER_THRESHOLD) {
7577
- draggedVertexIds = null;
7578
7570
  return null;
7579
7571
  }
7580
- return nearestIds;
7581
- }
7582
-
7583
- function insertMidpoint(v) {
7584
- var target = hit.getHitTarget();
7585
- internal.insertVertex(target.arcs, v.i, v.point);
7572
+ var points = nearestIds.map(function(i) {return target.arcs.getVertex2(i);});
7573
+ return {
7574
+ ids: nearestIds,
7575
+ points: points
7576
+ };
7586
7577
  }
7587
7578
 
7588
- function toggleVertexSelection(ids) {
7589
- if (!ids || ids.length === 0) return;
7590
- if (!selectedVertexIds) {
7591
- selectedVertexIds = ids;
7592
- } else {
7593
- var intersection = utils$1.intersection(selectedVertexIds, ids);
7594
- var union = selectedVertexIds.concat(ids);
7595
- selectedVertexIds = utils$1.difference(union, intersection);
7596
- }
7597
- // get coordinates
7579
+ function findVertexInsertionPoint(e) {
7598
7580
  var target = hit.getHitTarget();
7599
- var points = selectedVertexIds.map(function(id) { return target.arcs.getVertex2(id); });
7600
- hit.setSelectedVertices(points);
7581
+ if (!target.arcs.isFlat()) return null; // vertex insertion not supported with simplification
7582
+ var p = ext.translatePixelCoords(e.x, e.y);
7583
+ var shp = target.layer.shapes[e.id];
7584
+ var midpoint = findNearestMidpoint(p, shp, target.arcs);
7585
+ if (!midpoint ||
7586
+ midpoint.distance / ext.getPixelSize() > MIDPOINT_THRESHOLD) return null;
7587
+ return midpoint;
7601
7588
  }
7602
7589
 
7603
7590
  hit.on('dragstart', function(e) {
7604
- if (!active(e)) return;
7605
- if (activeMidpoint) {
7606
- insertMidpoint(activeMidpoint);
7607
- draggedVertexIds = [activeMidpoint.i];
7608
- // TODO: combine vertex insertion undo/redo actions with
7609
- // vertex_dragend undo/redo actions
7610
- gui.dispatchEvent('vertex_insert', {
7611
- FID: activeShapeId,
7612
- vertex_id: activeMidpoint.i,
7613
- coordinates: activeMidpoint.point
7614
- });
7615
- activeMidpoint = null;
7591
+ if (!active()) return;
7592
+ if (insertionPoint) {
7593
+ var target = hit.getHitTarget();
7594
+ internal.insertVertex(target.arcs, insertionPoint.i, insertionPoint.point);
7595
+ dragInfo = {
7596
+ insertion: true,
7597
+ ids: [insertionPoint.i],
7598
+ points: [insertionPoint.point]
7599
+ };
7600
+ insertionPoint = null;
7616
7601
  } else {
7617
- draggedVertexIds = findDraggableVertices(e);
7602
+ dragInfo = findDraggableVertices(e);
7603
+ }
7604
+ if (dragInfo) {
7605
+ setHoverVertex(dragInfo.ids[0]);
7618
7606
  }
7619
- if (!draggedVertexIds) return;
7620
- setHoverVertex(draggedVertexIds[0]);
7621
- activeShapeId = e.id;
7622
- fire('vertex_dragstart');
7623
7607
  });
7624
7608
 
7625
7609
  hit.on('drag', function(e) {
7626
- if (!active(e) || !draggedVertexIds) return;
7610
+ if (!dragging()) return;
7627
7611
  var target = hit.getHitTarget();
7628
7612
  var p = ext.translatePixelCoords(e.x, e.y);
7629
7613
  if (gui.keyboard.shiftIsPressed()) {
7630
- internal.snapPointToArcEndpoint(p, draggedVertexIds, target.arcs);
7614
+ internal.snapPointToArcEndpoint(p, dragInfo.ids, target.arcs);
7631
7615
  }
7632
- internal.snapVerticesToPoint(draggedVertexIds, p, target.arcs);
7633
- setHoverVertex(draggedVertexIds[0]);
7616
+ internal.snapVerticesToPoint(dragInfo.ids, p, target.arcs);
7617
+ setHoverVertex(dragInfo.ids[0]);
7634
7618
  // redrawing the whole map updates the data layer as well as the overlay layer
7635
7619
  // gui.dispatchEvent('map-needs-refresh');
7636
7620
  });
7637
7621
 
7638
7622
  hit.on('dragend', function(e) {
7639
- if (!active(e) || !draggedVertexIds) return;
7623
+ if (!dragging()) return;
7640
7624
  // kludge to get dataset to recalculate internal bounding boxes
7641
7625
  hit.getHitTarget().arcs.transformPoints(function() {});
7642
7626
  clearHoverVertex();
7643
-
7644
-
7645
-
7646
- fire('vertex_dragend');
7647
- draggedVertexIds = null;
7648
- activeShapeId = -1;
7649
- // redraw data layer
7627
+ gui.dispatchEvent('vertex_dragend', dragInfo);
7650
7628
  gui.dispatchEvent('map-needs-refresh');
7629
+ dragInfo = null;
7651
7630
  });
7652
7631
 
7653
- // select clicked vertices
7654
- hit.on('click', function(e) {
7655
- if (!active(e)) return;
7656
- var vertices = findDraggableVertices(e); // same selection criteria as for dragging
7657
-
7658
- // CASE: no draggable vertices
7659
- // if selected vertices, deselect, else no-op
7660
- // CASE: draggable vertices
7661
- // if vertex is arc endpoint, ignore
7662
- // if vertex is selected, deselect
7663
- // if vertex is not selected, select
7664
-
7665
- // if (vertices) {
7666
- // toggleVertexSelection(vertices);
7667
- // } else if (selectedVertexIds) {
7668
- // toggleVertexSelection(selectedVertexIds);
7669
- // }
7670
-
7632
+ hit.on('dblclick', function(e) {
7633
+ if (!active()) return;
7634
+ var info = findDraggableVertices(e); // same selection criteria as for dragging
7635
+ if (!info) return;
7636
+ var target = hit.getHitTarget();
7637
+ var vId = info.ids[0];
7638
+ if (internal.vertexIsArcStart(vId, target.arcs) ||
7639
+ internal.vertexIsArcEnd(vId, target.arcs)) {
7640
+ // TODO: support removing arc endpoints
7641
+ return;
7642
+ }
7643
+ gui.dispatchEvent('vertex_delete', {
7644
+ vertex_id: vId
7645
+ });
7646
+ internal.deleteVertex(target.arcs, vId);
7647
+ clearHoverVertex();
7648
+ gui.dispatchEvent('map-needs-refresh');
7671
7649
  });
7672
7650
 
7673
7651
  // highlight hit vertex in path edit mode
7674
7652
  hit.on('hover', function(e) {
7675
- activeMidpoint = null;
7676
- if (!active(e) || draggedVertexIds) return; // no hover effect while dragging
7677
- var vertexIds = findDraggableVertices(e);
7678
- if (vertexIds) {
7679
- setHoverVertex(vertexIds[0]);
7653
+ insertionPoint = null;
7654
+ if (!active() || dragging()) return; // no hover effect while dragging
7655
+ var info = findDraggableVertices(e);
7656
+ if (info) {
7657
+ // hovering near a vertex: highlight the vertex
7658
+ setHoverVertex(info.ids[0]);
7680
7659
  return;
7681
7660
  }
7682
- var target = hit.getHitTarget();
7683
- // vertex insertion doesn't work yet with simplification applied
7684
- if (!target.arcs.isFlat()) return;
7685
- var shp = target.layer.shapes[e.id];
7686
- var p = ext.translatePixelCoords(e.x, e.y);
7687
- var midpoint = findNearestMidpoint(p, shp, target.arcs);
7688
- if (midpoint && midpoint.distance / ext.getPixelSize() < MIDPOINT_THRESHOLD) {
7689
- hit.setHoverVertex(midpoint.point);
7690
- activeMidpoint = midpoint;
7661
+ // if hovering near a segment midpoint: show the midpoint and save midpoint info
7662
+ insertionPoint = findVertexInsertionPoint(e);
7663
+ if (insertionPoint) {
7664
+ hit.setHoverVertex(insertionPoint.point);
7691
7665
  } else {
7666
+ // pointer is not over a vertex: clear any hover effect
7692
7667
  clearHoverVertex();
7693
7668
  }
7694
7669
  }, null, 100);
@@ -7696,15 +7671,8 @@
7696
7671
 
7697
7672
 
7698
7673
  // Given a location @p (e.g. corresponding to the mouse pointer location),
7699
- // find the midpoint of two vertices on @shp suitable for inserting a new vertex,
7700
- // but only if:
7701
- // 1. point @p is closer to the midpoint than either adjacent vertex
7702
- // 2. the segment containing @p is longer than a minimum distance in pixels.
7703
- //
7674
+ // find the midpoint of two vertices on @shp suitable for inserting a new vertex
7704
7675
  function findNearestMidpoint(p, shp, arcs) {
7705
- // var v1 = internal.findNearestVertex(p[0], p[1], shp, arcs);
7706
- // var v0 = internal.findAdjacentVertex(v1, shp, arcs, -1);
7707
- // var v2 = internal.findAdjacentVertex(v1, shp, arcs, 1);
7708
7676
  var minDist = Infinity, v;
7709
7677
  internal.forEachSegmentInShape(shp, arcs, function(i, j, xx, yy) {
7710
7678
  var x1 = xx[i],
@@ -7719,6 +7687,7 @@
7719
7687
  v = {
7720
7688
  i: (i < j ? i : j) + 1, // insertion point
7721
7689
  segment: [i, j],
7690
+ segmentLen: geom.distance2D(x1, y1, x2, y2),
7722
7691
  point: [cx, cy],
7723
7692
  distance: dist
7724
7693
  };
@@ -7901,6 +7870,20 @@
7901
7870
  return style;
7902
7871
  }
7903
7872
 
7873
+ // style for vertex edit mode
7874
+ function getVertexStyle(lyr, o) {
7875
+ return {
7876
+ ids: o.ids,
7877
+ overlay: true,
7878
+ strokeColor: black,
7879
+ strokeWidth: 1.5,
7880
+ vertices: true,
7881
+ vertex_overlay: o.hit_coordinates || null,
7882
+ selected_points: o.selected_points || null,
7883
+ fillColor: null
7884
+ };
7885
+ }
7886
+
7904
7887
  // Returns a display style for the overlay layer.
7905
7888
  // The overlay layer renders several kinds of feature, each of which is displayed
7906
7889
  // with a different style.
@@ -7910,41 +7893,45 @@
7910
7893
  // * pinned shapes
7911
7894
  //
7912
7895
  function getOverlayStyle(lyr, o) {
7896
+ if (o.mode == 'vertices') {
7897
+ return getVertexStyle(lyr, o);
7898
+ }
7913
7899
  var geomType = lyr.geometry_type;
7914
7900
  var topId = o.id; // pinned id (if pinned) or hover id
7915
7901
  var topIdx = -1;
7916
7902
  var styler = function(style, i) {
7917
7903
  utils$1.extend(style, i === topIdx ? topStyle: baseStyle);
7918
- // kludge to show vertices when editing path shapes
7919
- if (o.mode == 'vertices') {
7920
- style.vertices = true;
7921
- style.vertex_overlay = o.hit_coordinates || null;
7922
- style.selected_points = o.selected_points || null;
7923
- style.fillColor = null;
7924
- }
7925
7904
  };
7926
7905
  var baseStyle = getDefaultStyle(lyr, selectionStyles[geomType]);
7927
7906
  var topStyle;
7928
7907
  var ids = o.ids.filter(function(i) {
7929
7908
  return i != o.id; // move selected id to the end
7930
7909
  });
7931
- if (o.id > -1) {
7910
+ if (o.id > -1) { // pinned or hover style
7932
7911
  topStyle = getSelectedFeatureStyle(lyr, o);
7933
7912
  topIdx = ids.length;
7934
7913
  ids.push(o.id); // put the pinned/hover feature last in the render order
7935
7914
  }
7936
- var overlayStyle = {
7915
+ var style = {
7937
7916
  styler: styler,
7938
7917
  ids: ids,
7939
7918
  overlay: true
7940
7919
  };
7920
+ // kludge to show vertices when editing path shapes
7921
+ if (o.mode == 'vertices') {
7922
+ style.vertices = true;
7923
+ style.vertex_overlay = o.hit_coordinates || null;
7924
+ style.selected_points = o.selected_points || null;
7925
+ style.fillColor = null;
7926
+ }
7927
+
7941
7928
  if (layerHasCanvasDisplayStyle(lyr)) {
7942
7929
  if (geomType == 'point') {
7943
- overlayStyle.styler = getOverlayPointStyler(getCanvasDisplayStyle(lyr).styler, styler);
7930
+ style.styler = getOverlayPointStyler(getCanvasDisplayStyle(lyr).styler, styler);
7944
7931
  }
7945
- overlayStyle.type = 'styled';
7932
+ style.type = 'styled';
7946
7933
  }
7947
- return ids.length > 0 ? overlayStyle : null;
7934
+ return ids.length > 0 ? style : null;
7948
7935
  }
7949
7936
 
7950
7937
  function getSelectedFeatureStyle(lyr, o) {
@@ -8392,9 +8379,10 @@
8392
8379
  _self.drawVertices = function(shapes, arcs, style, filter) {
8393
8380
  var iter = new internal.ShapeIter(arcs);
8394
8381
  var t = getScaledTransform(_ext);
8382
+ var bounds = _ext.getBounds();
8395
8383
  var radius = (style.strokeWidth > 2 ? style.strokeWidth * 0.9 : 2) * GUI.getPixelRatio() * getScaledLineScale(_ext);
8396
8384
  var color = style.strokeColor || 'black';
8397
- var radius2 = radius * 1.7;
8385
+
8398
8386
  var i, j, p;
8399
8387
  _ctx.beginPath();
8400
8388
  _ctx.fillStyle = color;
@@ -8404,6 +8392,7 @@
8404
8392
  for (j=0; j<shp.length; j++) {
8405
8393
  iter.init(shp[j]);
8406
8394
  while (iter.hasNext()) {
8395
+ if (!bounds.containsPoint(iter.x, iter.y)) continue;
8407
8396
  drawCircle(iter.x * t.mx + t.bx, iter.y * t.my + t.by, radius, _ctx);
8408
8397
  }
8409
8398
  }
@@ -8411,21 +8400,10 @@
8411
8400
 
8412
8401
  if (style.vertex_overlay) {
8413
8402
  p = style.vertex_overlay;
8414
- drawCircle(p[0] * t.mx + t.bx, p[1] * t.my + t.by, radius2, _ctx);
8403
+ drawCircle(p[0] * t.mx + t.bx, p[1] * t.my + t.by, radius * 1.5, _ctx);
8415
8404
  }
8416
8405
  _ctx.fill();
8417
8406
  _ctx.closePath();
8418
-
8419
- if (style.selected_points) {
8420
- _ctx.beginPath();
8421
- _ctx.fillStyle = 'magenta';
8422
- for (i=0; i<style.selected_points.length; i++) {
8423
- p = style.selected_points[i];
8424
- drawCircle(p[0] * t.mx + t.bx, p[1] * t.my + t.by, radius2, _ctx);
8425
- }
8426
- _ctx.fill();
8427
- _ctx.closePath();
8428
- }
8429
8407
  };
8430
8408
 
8431
8409
  // Optimized to draw paths in same-style batches (faster Canvas drawing)