mapshaper 0.5.84 → 0.5.88

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.84",
3
+ "version": "0.5.88",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
@@ -182,6 +182,10 @@
182
182
  return (el && (el.tagName == 'INPUT' || el.contentEditable == 'true')) ? el : null;
183
183
  };
184
184
 
185
+ GUI.textIsSelected = function() {
186
+ return !!GUI.getInputElement();
187
+ };
188
+
185
189
  GUI.selectElement = function(el) {
186
190
  var range = document.createRange(),
187
191
  sel = window.getSelection();
@@ -1027,6 +1031,10 @@
1027
1031
  var types = Array.from(e.clipboardData.types || []).join(',');
1028
1032
  var items = Array.from(e.clipboardData.items || []);
1029
1033
  var files;
1034
+ if (GUI.textIsSelected()) {
1035
+ // user is probably pasting text into an editable text field
1036
+ return;
1037
+ }
1030
1038
  block(e);
1031
1039
  // Browser compatibility (tested on MacOS only):
1032
1040
  // Chrome and Safari: full support
@@ -3395,7 +3403,7 @@
3395
3403
  GUI.onClick(entry, function() {
3396
3404
  var target = findLayerById(id);
3397
3405
  // don't select if user is typing or dragging
3398
- if (!GUI.getInputElement() && !dragging) {
3406
+ if (!GUI.textIsSelected() && !dragging) {
3399
3407
  gui.clearMode();
3400
3408
  if (!map.isActiveLayer(target.layer)) {
3401
3409
  model.selectLayer(target.layer, target.dataset);
@@ -3574,6 +3582,153 @@
3574
3582
  }
3575
3583
  }
3576
3584
 
3585
+ // import { cloneShape } from '../paths/mapshaper-shape-utils';
3586
+ // import { copyRecord } from '../datatable/mapshaper-data-utils';
3587
+ var snapVerticesToPoint$1 = internal.snapVerticesToPoint;
3588
+ var cloneShape = internal.cloneShape;
3589
+ var copyRecord = internal.copyRecord;
3590
+
3591
+ function Undo(gui) {
3592
+ var history, offset, stashedUndo;
3593
+ reset();
3594
+
3595
+ function reset() {
3596
+ history = [];
3597
+ stashedUndo = null;
3598
+ offset = 0;
3599
+ }
3600
+
3601
+
3602
+ function isUndoEvt(e) {
3603
+ return (e.ctrlKey || e.metaKey) && !e.shiftKey && e.key == 'z';
3604
+ }
3605
+
3606
+ function isRedoEvt(e) {
3607
+ return (e.ctrlKey || e.metaKey) && (e.shiftKey && e.key == 'z' || !e.shiftKey && e.key == 'y');
3608
+ }
3609
+
3610
+ gui.keyboard.on('keydown', function(evt) {
3611
+ var e = evt.originalEvent,
3612
+ kc = e.keyCode;
3613
+ if (isUndoEvt(e)) {
3614
+ this.undo();
3615
+ e.stopPropagation();
3616
+ e.preventDefault();
3617
+ }
3618
+ if (isRedoEvt(e)) {
3619
+ this.redo();
3620
+ e.stopPropagation();
3621
+ e.preventDefault();
3622
+ }
3623
+
3624
+ }, this, 10);
3625
+
3626
+ // undo/redo point/symbol dragging
3627
+ //
3628
+ gui.on('symbol_dragstart', function(e) {
3629
+ stashedUndo = this.makePointSetter(e.FID);
3630
+ }, this);
3631
+
3632
+ gui.on('symbol_dragend', function(e) {
3633
+ var redo = this.makePointSetter(e.FID);
3634
+ this.addHistoryState(stashedUndo, redo);
3635
+ }, this);
3636
+
3637
+ // undo/redo label dragging
3638
+ //
3639
+ gui.on('label_dragstart', function(e) {
3640
+ stashedUndo = this.makeDataSetter(e.FID);
3641
+ }, this);
3642
+
3643
+ gui.on('label_dragend', function(e) {
3644
+ var redo = this.makeDataSetter(e.FID);
3645
+ this.addHistoryState(stashedUndo, redo);
3646
+ }, this);
3647
+
3648
+ // undo/redo data editing
3649
+ // TODO: consider setting selected feature to the undo/redo target feature
3650
+ //
3651
+ gui.on('data_preupdate', function(e) {
3652
+ stashedUndo = this.makeDataSetter(e.FID);
3653
+ }, this);
3654
+
3655
+ gui.on('data_postupdate', function(e) {
3656
+ var redo = this.makeDataSetter(e.FID);
3657
+ this.addHistoryState(stashedUndo, redo);
3658
+ }, this);
3659
+
3660
+ // undo/redo vertex dragging
3661
+ gui.on('vertex_dragstart', function(e) {
3662
+ stashedUndo = this.makeVertexSetter(e.FID, e.vertex_ids);
3663
+ }, this);
3664
+
3665
+ gui.on('vertex_dragend', function(e) {
3666
+ var redo = this.makeVertexSetter(e.FID, e.vertex_ids);
3667
+ this.addHistoryState(stashedUndo, redo);
3668
+ }, this);
3669
+
3670
+ this.clear = function() {
3671
+ reset();
3672
+ };
3673
+
3674
+ this.makePointSetter = function(i) {
3675
+ var target = gui.model.getActiveLayer();
3676
+ var shp = cloneShape(target.layer.shapes[i]);
3677
+ return function() {
3678
+ target.layer.shapes[i] = shp;
3679
+ };
3680
+ };
3681
+
3682
+ this.makeDataSetter = function(id) {
3683
+ var target = gui.model.getActiveLayer();
3684
+ var rec = copyRecord(target.layer.data.getRecordAt(id));
3685
+ return function() {
3686
+ target.layer.data.getRecords()[id] = rec;
3687
+ gui.dispatchEvent('popup-needs-refresh');
3688
+ };
3689
+ };
3690
+
3691
+ this.makeVertexSetter = function(fid, ids) {
3692
+ var target = gui.model.getActiveLayer();
3693
+ var arcs = target.dataset.arcs;
3694
+ var p = internal.getVertexCoords(ids[0], arcs);
3695
+ return function() {
3696
+ snapVerticesToPoint$1(ids, p, arcs, true);
3697
+ };
3698
+ };
3699
+
3700
+ this.addHistoryState = function(undo, redo) {
3701
+ if (offset > 0) {
3702
+ history.splice(-offset);
3703
+ offset = 0;
3704
+ }
3705
+ history.push({undo, redo});
3706
+ };
3707
+
3708
+ this.undo = function() {
3709
+ var item = getHistoryItem();
3710
+ if (item) {
3711
+ offset++;
3712
+ item.undo();
3713
+ gui.dispatchEvent('map-needs-refresh');
3714
+ }
3715
+ };
3716
+
3717
+ this.redo = function() {
3718
+ if (offset <= 0) return;
3719
+ offset--;
3720
+ var item = getHistoryItem();
3721
+ item.redo();
3722
+ gui.dispatchEvent('map-needs-refresh');
3723
+ };
3724
+
3725
+ function getHistoryItem() {
3726
+ var item = history[history.length - offset - 1];
3727
+ return item || null;
3728
+ }
3729
+
3730
+ }
3731
+
3577
3732
  function SidebarButtons(gui) {
3578
3733
  var root = gui.container.findChild('.mshp-main-map');
3579
3734
  var buttons = El('div').addClass('nav-buttons').appendTo(root).hide();
@@ -3705,6 +3860,7 @@
3705
3860
 
3706
3861
  var menus = {
3707
3862
  standard: ['info', 'selection', 'data', 'box'],
3863
+ polygons: ['info', 'selection', 'data', 'box', 'vertices'],
3708
3864
  lines: ['info', 'selection', 'data', 'box', 'vertices'],
3709
3865
  table: ['info', 'selection', 'data'],
3710
3866
  labels: ['info', 'selection', 'data', 'box', 'labels', 'location'],
@@ -3816,6 +3972,9 @@
3816
3972
  if (internal.layerHasPaths(o.layer) && o.layer.geometry_type == 'polyline') {
3817
3973
  return menus.lines;
3818
3974
  }
3975
+ if (internal.layerHasPaths(o.layer) && o.layer.geometry_type == 'polygon') {
3976
+ return menus.polygons;
3977
+ }
3819
3978
  return menus.standard;
3820
3979
  }
3821
3980
 
@@ -3888,7 +4047,9 @@
3888
4047
  }
3889
4048
 
3890
4049
  function onModeChange() {
3891
- gui.dispatchEvent('interaction_mode_change', {mode: getInteractionMode()});
4050
+ var mode = getInteractionMode();
4051
+ gui.state.interaction_mode = mode;
4052
+ gui.dispatchEvent('interaction_mode_change', {mode: mode});
3892
4053
  }
3893
4054
 
3894
4055
  // Update button highlight and selected menu item highlight (if any)
@@ -4319,7 +4480,7 @@
4319
4480
  }
4320
4481
 
4321
4482
  // ignore keypress if no feature is selected or user is editing text
4322
- if (pinnedId() == -1 || GUI.getInputElement()) return;
4483
+ if (pinnedId() == -1 || GUI.textIsSelected()) return;
4323
4484
 
4324
4485
  if (e.keyCode == 37 || e.keyCode == 39) {
4325
4486
  // L/R arrow keys
@@ -4477,6 +4638,7 @@
4477
4638
 
4478
4639
  // Hits are re-detected on 'hover' (if hit detection is active)
4479
4640
  mouse.on('hover', function(e) {
4641
+ handlePointerEvent(e);
4480
4642
  if (storedData.pinned || !hitTest || !active) return;
4481
4643
  if (e.hover && isOverMap(e)) {
4482
4644
  // mouse is hovering directly over map area -- update hit detection
@@ -5521,7 +5683,9 @@
5521
5683
  } else {
5522
5684
  // field content has changed
5523
5685
  strval = strval2;
5686
+ gui.dispatchEvent('data_preupdate', {FID: currId}); // for undo/redo
5524
5687
  rec[key] = val2;
5688
+ gui.dispatchEvent('data_postupdate', {FID: currId});
5525
5689
  input.value(strval);
5526
5690
  setFieldClass(el, val2, type);
5527
5691
  self.dispatchEvent('update', {field: key, value: val2, id: currId});
@@ -5737,6 +5901,8 @@
5737
5901
  rec[key] = isString ? String(newVal) : newVal;
5738
5902
  }
5739
5903
 
5904
+ var snapVerticesToPoint = internal.snapVerticesToPoint;
5905
+
5740
5906
  function getDisplayCoordsById(id, layer, ext) {
5741
5907
  var coords = getPointCoordsById(id, layer);
5742
5908
  return ext.translateCoords(coords[0], coords[1]);
@@ -5757,7 +5923,7 @@
5757
5923
  }
5758
5924
 
5759
5925
 
5760
- function SymbolDragging2(gui, ext, hit) {
5926
+ function InteractiveEditor(gui, ext, hit) {
5761
5927
  // var targetTextNode; // text node currently being dragged
5762
5928
  var dragging = false;
5763
5929
  var activeRecord;
@@ -5819,6 +5985,7 @@
5819
5985
  if (e.mode != 'labels') {
5820
5986
  stopDragging();
5821
5987
  }
5988
+ gui.undo.clear(); // TODO: put this elsewhere?
5822
5989
  });
5823
5990
 
5824
5991
  // down event on svg
@@ -5830,12 +5997,17 @@
5830
5997
  // 3: on other text -> stop dragging, select new text
5831
5998
 
5832
5999
  hit.on('dragstart', function(e) {
5833
- if (labelEditingEnabled()) {
5834
- onLabelDragStart(e);
6000
+ if (e.id >= 0 === false) return;
6001
+ if (labelEditingEnabled() && onLabelDragStart(e)) {
6002
+ triggerGlobalEvent('label_dragstart', e);
6003
+ startDragging();
5835
6004
  } else if (locationEditingEnabled()) {
5836
- onLocationDragStart(e);
6005
+ triggerGlobalEvent('symbol_dragstart', e);
6006
+ startDragging();
5837
6007
  } else if (vertexEditingEnabled()) {
5838
6008
  onVertexDragStart(e);
6009
+ triggerGlobalEvent('vertex_dragstart', e);
6010
+ startDragging();
5839
6011
  }
5840
6012
  });
5841
6013
 
@@ -5851,70 +6023,77 @@
5851
6023
 
5852
6024
  hit.on('dragend', function(e) {
5853
6025
  if (locationEditingEnabled()) {
5854
- onLocationDragEnd(e);
6026
+ triggerGlobalEvent('symbol_dragend', e);
5855
6027
  stopDragging();
5856
6028
  } else if (labelEditingEnabled()) {
6029
+ triggerGlobalEvent('label_dragend', e);
5857
6030
  stopDragging();
5858
6031
  } else if (vertexEditingEnabled()) {
5859
- onVertexDragEnd(e);
6032
+ // kludge to get dataset to recalculate internal bounding boxes
6033
+ hit.getHitTarget().arcs.transformPoints(function() {});
6034
+ triggerGlobalEvent('vertex_dragend', e);
5860
6035
  stopDragging();
5861
6036
  }
5862
6037
  });
5863
6038
 
5864
6039
  hit.on('click', function(e) {
5865
6040
  if (labelEditingEnabled()) {
6041
+ var target = hit.getHitTarget();
5866
6042
  onLabelClick(e);
5867
6043
  }
5868
6044
  });
5869
6045
 
5870
- function onLocationDragStart(e) {
5871
- if (e.id >= 0) {
5872
- dragging = true;
5873
- triggerGlobalEvent('symbol_dragstart', e);
6046
+ // TODO: highlight hit vertex in path edit mode
6047
+ if (false) hit.on('hover', function(e) {
6048
+ if (vertexEditingEnabled() && !dragging) {
6049
+ onVertexHover(e);
5874
6050
  }
6051
+ }, null, 100);
6052
+
6053
+ function onVertexHover(e) {
6054
+ // hovering in vertex edit mode: find vertex insertion point
6055
+ var target = hit.getHitTarget();
6056
+ var shp = target.layer.shapes[e.id];
6057
+ var p = ext.translatePixelCoords(e.x, e.y);
6058
+ var o = internal.findInsertionPoint(p, shp, target.arcs, ext.getPixelSize());
5875
6059
  }
5876
6060
 
5877
- function onVertexDragStart(e) {
5878
- if (e.id >= 0) {
5879
- dragging = true;
5880
- }
6061
+
6062
+ function getVertexEventData(e) {
6063
+ return {
6064
+ FID: activeId,
6065
+ vertexIds: activeVertexIds
6066
+ };
5881
6067
  }
5882
6068
 
5883
6069
  function onLocationDrag(e) {
5884
6070
  var lyr = hit.getHitTarget().layer;
5885
- var p = getPointCoordsById(e.id, hit.getHitTarget().layer);
6071
+ var p = getPointCoordsById(e.id, lyr);
5886
6072
  if (!p) return;
5887
6073
  var diff = translateDeltaDisplayCoords(e.dx, e.dy, ext);
5888
6074
  p[0] += diff[0];
5889
6075
  p[1] += diff[1];
5890
- self.dispatchEvent('location_change'); // signal map to redraw
6076
+ triggerRedraw();
5891
6077
  triggerGlobalEvent('symbol_drag', e);
5892
6078
  }
5893
6079
 
5894
- function onVertexDrag(e) {
6080
+ function onVertexDragStart(e) {
5895
6081
  var target = hit.getHitTarget();
6082
+ var shp = target.layer.shapes[e.id];
5896
6083
  var p = ext.translatePixelCoords(e.x, e.y);
5897
- if (!activeVertexIds) {
5898
- activeVertexIds = internal.findNearestVertices(p, target.layer.shapes[e.id], target.arcs);
5899
- }
6084
+ activeVertexIds = internal.findNearestVertices(p, shp, target.arcs);
6085
+ activeId = e.id;
6086
+ }
6087
+
6088
+ function onVertexDrag(e) {
6089
+ var target = hit.getHitTarget();
5900
6090
  if (!activeVertexIds) return; // ignore error condition
6091
+ var p = ext.translatePixelCoords(e.x, e.y);
5901
6092
  if (gui.keyboard.shiftIsPressed()) {
5902
6093
  internal.snapPointToArcEndpoint(p, activeVertexIds, target.arcs);
5903
6094
  }
5904
- activeVertexIds.forEach(function(idx) {
5905
- internal.setVertexCoords(p[0], p[1], idx, target.arcs);
5906
- });
5907
- self.dispatchEvent('location_change'); // signal map to redraw
5908
- }
5909
-
5910
- function onLocationDragEnd(e) {
5911
- triggerGlobalEvent('symbol_dragend', e);
5912
- }
5913
-
5914
- function onVertexDragEnd(e) {
5915
- // kludge to get dataset to recalculate internal bounding boxes
5916
- hit.getHitTarget().arcs.transformPoints(function() {});
5917
- activeVertexIds = null;
6095
+ snapVerticesToPoint(activeVertexIds, p, target.arcs);
6096
+ triggerRedraw();
5918
6097
  }
5919
6098
 
5920
6099
  function onLabelClick(e) {
@@ -5927,11 +6106,19 @@
5927
6106
  }
5928
6107
  }
5929
6108
 
6109
+ function triggerRedraw() {
6110
+ gui.dispatchEvent('map-needs-refresh');
6111
+ }
6112
+
5930
6113
  function triggerGlobalEvent(type, e) {
5931
- if (e.id >= 0) {
5932
- // fire event to signal external editor that symbol coords have changed
5933
- gui.dispatchEvent(type, {FID: e.id, layer_name: hit.getHitTarget().layer.name});
5934
- }
6114
+ if (e.id >= 0 === false) return;
6115
+ var o = {
6116
+ FID: e.id,
6117
+ layer_name: hit.getHitTarget().layer.name,
6118
+ vertex_ids: activeVertexIds
6119
+ };
6120
+ // fire event to signal external editor that symbol coords have changed
6121
+ gui.dispatchEvent(type, o);
5935
6122
  }
5936
6123
 
5937
6124
  function getLabelRecordById(id) {
@@ -5953,11 +6140,11 @@
5953
6140
  function onLabelDragStart(e) {
5954
6141
  var textNode = getTextTarget3(e);
5955
6142
  var table = hit.getTargetDataTable();
5956
- if (!textNode || !table) return;
6143
+ if (!textNode || !table) return false;
5957
6144
  activeId = e.id;
5958
6145
  activeRecord = getLabelRecordById(activeId);
5959
- dragging = true;
5960
6146
  downEvt = e;
6147
+ return true;
5961
6148
  }
5962
6149
 
5963
6150
  function onLabelDrag(e) {
@@ -6006,88 +6193,17 @@
6006
6193
  }
6007
6194
  return el.tagName == 'text' ? el : null;
6008
6195
  }
6196
+ }
6009
6197
 
6010
- // svg.addEventListener('mousedown', function(e) {
6011
- // var textTarget = getTextTarget(e);
6012
- // downEvt = e;
6013
- // if (!textTarget) {
6014
- // stopEditing();
6015
- // } else if (!editing) {
6016
- // // nop
6017
- // } else if (textTarget == targetTextNode) {
6018
- // startDragging();
6019
- // } else {
6020
- // startDragging();
6021
- // editTextNode(textTarget);
6022
- // }
6023
- // });
6024
-
6025
- // up event on svg
6026
- // a: currently dragging text
6027
- // -> stop dragging
6028
- // b: clicked on a text feature
6029
- // -> start editing it
6030
-
6031
-
6032
- // svg.addEventListener('mouseup', function(e) {
6033
- // var textTarget = getTextTarget(e);
6034
- // var isClick = isClickEvent(e, downEvt);
6035
- // if (isClick && textTarget && textTarget == targetTextNode &&
6036
- // activeRecord && isMultilineLabel(targetTextNode)) {
6037
- // toggleTextAlign(targetTextNode, activeRecord);
6038
- // updateSymbol();
6039
- // }
6040
- // if (dragging) {
6041
- // stopDragging();
6042
- // } else if (isClick && textTarget) {
6043
- // editTextNode(textTarget);
6044
- // }
6045
- // });
6046
-
6047
- // block dbl-click navigation when editing
6048
- // mouse.on('dblclick', function(e) {
6049
- // if (editing) e.stopPropagation();
6050
- // }, null, eventPriority);
6051
-
6052
- // mouse.on('dragstart', function(e) {
6053
- // onLabelDrag(e);
6054
- // }, null, eventPriority);
6055
-
6056
- // mouse.on('drag', function(e) {
6057
- // var scale = ext.getSymbolScale() || 1;
6058
- // onLabelDrag(e);
6059
- // if (!dragging || !activeRecord) return;
6060
- // applyDelta(activeRecord, 'dx', e.dx / scale);
6061
- // applyDelta(activeRecord, 'dy', e.dy / scale);
6062
- // if (!isMultilineLabel(targetTextNode)) {
6063
- // // update anchor position of single-line labels based on label position
6064
- // // relative to anchor point, for better placement when eventual display font is
6065
- // // different from mapshaper's font.
6066
- // updateTextAnchor(targetTextNode, activeRecord);
6067
- // }
6068
- // // updateSymbol(targetTextNode, activeRecord);
6069
- // targetTextNode = updateSymbol2(targetTextNode, activeRecord, activeId);
6070
- // }, null, eventPriority);
6071
-
6072
- // mouse.on('dragend', function(e) {
6073
- // onLabelDrag(e);
6074
- // stopDragging();
6075
- // }, null, eventPriority);
6076
-
6077
-
6078
- // function onLabelDrag(e) {
6079
- // if (dragging) {
6080
- // e.stopPropagation();
6081
- // }
6082
- // }
6198
+ function startDragging() {
6199
+ dragging = true;
6083
6200
  }
6084
6201
 
6085
6202
  function stopDragging() {
6086
6203
  dragging = false;
6087
6204
  activeId = -1;
6088
6205
  activeRecord = null;
6089
- // targetTextNode = null;
6090
- // svg.removeAttribute('class');
6206
+ activeVertexIds = null;
6091
6207
  }
6092
6208
 
6093
6209
  function isClickEvent(up, down) {
@@ -6098,16 +6214,6 @@
6098
6214
  return dist <= 4 && elapsed < 300;
6099
6215
  }
6100
6216
 
6101
-
6102
- // function deselectText(el) {
6103
- // el.removeAttribute('class');
6104
- // }
6105
-
6106
- // function selectText(el) {
6107
- // el.setAttribute('class', 'selected');
6108
- // }
6109
-
6110
-
6111
6217
  }
6112
6218
 
6113
6219
  var darkStroke = "#334",
@@ -6151,7 +6257,7 @@
6151
6257
  dotSize: 2.5
6152
6258
  }, polyline: {
6153
6259
  strokeColor: black,
6154
- strokeWidth: 2.5
6260
+ strokeWidth: 2.5,
6155
6261
  }
6156
6262
  },
6157
6263
  unselectedHoverStyles = {
@@ -6465,6 +6571,7 @@
6465
6571
 
6466
6572
  this.maxScale = maxScale;
6467
6573
 
6574
+ // Display scale, e.g. meters per pixel or degrees per pixel
6468
6575
  this.getPixelSize = function() {
6469
6576
  return 1 / this.getTransform().mx;
6470
6577
  };
@@ -6656,7 +6763,10 @@
6656
6763
  } else {
6657
6764
  arcs = getArcsForRendering(obj, ext);
6658
6765
  filter = getShapeFilter(arcs, ext);
6659
- canv.drawPathShapes(layer.shapes, arcs, style, filter);
6766
+ canv.drawStyledPaths(layer.shapes, arcs, style, filter);
6767
+ if (style.vertices) {
6768
+ canv.drawVertices(layer.shapes, arcs, style, filter);
6769
+ }
6660
6770
  }
6661
6771
  }
6662
6772
 
@@ -6730,7 +6840,7 @@
6730
6840
 
6731
6841
  /*
6732
6842
  // Original function, not optimized
6733
- _self.drawPathShapes = function(shapes, arcs, style) {
6843
+ _self.drawStyledPaths = function(shapes, arcs, style) {
6734
6844
  var startPath = getPathStart(_ext),
6735
6845
  drawPath = getShapePencil(arcs, _ext),
6736
6846
  styler = style.styler || null;
@@ -6743,8 +6853,28 @@
6743
6853
  };
6744
6854
  */
6745
6855
 
6856
+ _self.drawVertices = function(shapes, arcs, style, filter) {
6857
+ var iter = new internal.ShapeIter(arcs);
6858
+ var t = getScaledTransform(_ext);
6859
+ var radius = (style.strokeWidth * 0.9 || 2.2) * GUI.getPixelRatio() * getScaledLineScale(_ext);
6860
+ var color = style.strokeColor || 'black';
6861
+ var shp;
6862
+ _ctx.beginPath();
6863
+ _ctx.fillStyle = color;
6864
+ for (var i=0; i<shapes.length; i++) {
6865
+ shp = shapes[i];
6866
+ if (!shp || filter && !filter(shp)) continue;
6867
+ iter.init(shp);
6868
+ while (iter.hasNext()) {
6869
+ drawCircle(iter.x * t.mx + t.bx, iter.y * t.my + t.by, radius, _ctx);
6870
+ }
6871
+ }
6872
+ _ctx.fill();
6873
+ _ctx.closePath();
6874
+ };
6875
+
6746
6876
  // Optimized to draw paths in same-style batches (faster Canvas drawing)
6747
- _self.drawPathShapes = function(shapes, arcs, style, filter) {
6877
+ _self.drawStyledPaths = function(shapes, arcs, style, filter) {
6748
6878
  var styleIndex = {};
6749
6879
  var batchSize = 1500;
6750
6880
  var startPath = getPathStart(_ext, getScaledLineScale(_ext));
@@ -6872,7 +7002,7 @@
6872
7002
  }
6873
7003
  }
6874
7004
 
6875
- // TODO: consider using drawPathShapes(), which draws paths in batches
7005
+ // TODO: consider using drawStyledPaths(), which draws paths in batches
6876
7006
  // for faster Canvas rendering. Downside: changes stacking order, which
6877
7007
  // is bad if circles are graduated.
6878
7008
  _self.drawPoints = function(shapes, style) {
@@ -7812,6 +7942,10 @@
7812
7942
  _mouse.disable();
7813
7943
  });
7814
7944
 
7945
+ gui.on('map-needs-refresh', function() {
7946
+ drawLayers();
7947
+ });
7948
+
7815
7949
  model.on('update', onUpdate);
7816
7950
 
7817
7951
  // Update display of segment intersections
@@ -7970,13 +8104,7 @@
7970
8104
  });
7971
8105
  }
7972
8106
 
7973
- if (true) { // TODO: add option to disable?
7974
- _editor = new SymbolDragging2(gui, _ext, _hit);
7975
- _editor.on('location_change', function(e) {
7976
- // TODO: look into optimizing, so only changed symbol is redrawn
7977
- drawLayers();
7978
- });
7979
- }
8107
+ _editor = new InteractiveEditor(gui, _ext, _hit);
7980
8108
 
7981
8109
  _ext.on('change', function(e) {
7982
8110
  if (e.reset) return; // don't need to redraw map here if extent has been reset
@@ -7996,6 +8124,10 @@
7996
8124
  function updateOverlayLayer(e) {
7997
8125
  var style = getOverlayStyle(_activeLyr.layer, e);
7998
8126
  if (style) {
8127
+ // kludge to show vertices when editing path shapes
8128
+ if (gui.state.interaction_mode == 'vertices') {
8129
+ style.vertices = true;
8130
+ }
7999
8131
  _overlayLyr = utils.defaults({
8000
8132
  layer: filterLayerByIds(_activeLyr.layer, style.ids),
8001
8133
  style: style
@@ -8235,6 +8367,8 @@
8235
8367
  gui.map = new MshpMap(gui);
8236
8368
  gui.interaction = new InteractionMode(gui);
8237
8369
  gui.session = new SessionHistory(gui);
8370
+ gui.undo = new Undo(gui);
8371
+ gui.state = {};
8238
8372
 
8239
8373
  gui.showProgressMessage = function(msg) {
8240
8374
  if (!gui.progressMessage) {