mapshaper 0.5.91 → 0.5.92

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.
@@ -1232,6 +1232,7 @@
1232
1232
  }
1233
1233
  }
1234
1234
  model.updated({select: true});
1235
+
1235
1236
  }
1236
1237
 
1237
1238
  function clearQueuedFiles() {
@@ -2187,6 +2188,13 @@
2187
2188
  // expose this function, so other components can run commands (e.g. box tool)
2188
2189
  this.runMapshaperCommands = runMapshaperCommands;
2189
2190
 
2191
+ this.runInitialCommands = function(str) {
2192
+ str = str.trim();
2193
+ if (!str) return;
2194
+ turnOn();
2195
+ submit(str);
2196
+ };
2197
+
2190
2198
  consoleMessage(PROMPT);
2191
2199
  gui.keyboard.on('keydown', onKeyDown);
2192
2200
  window.addEventListener('beforeunload', saveHistory); // save history if console is open on refresh
@@ -3549,7 +3557,7 @@
3549
3557
 
3550
3558
  // import { cloneShape } from '../paths/mapshaper-shape-utils';
3551
3559
  // import { copyRecord } from '../datatable/mapshaper-data-utils';
3552
- var snapVerticesToPoint$1 = internal.snapVerticesToPoint;
3560
+ var snapVerticesToPoint = internal.snapVerticesToPoint;
3553
3561
  var cloneShape = internal.cloneShape;
3554
3562
  var copyRecord = internal.copyRecord;
3555
3563
 
@@ -3658,7 +3666,7 @@
3658
3666
  var arcs = target.dataset.arcs;
3659
3667
  var p = internal.getVertexCoords(ids[0], arcs);
3660
3668
  return function() {
3661
- snapVerticesToPoint$1(ids, p, arcs, true);
3669
+ snapVerticesToPoint(ids, p, arcs, true);
3662
3670
  };
3663
3671
  };
3664
3672
 
@@ -4098,13 +4106,38 @@
4098
4106
  return self;
4099
4107
  }
4100
4108
 
4101
- function getShapeHitTest(displayLayer, ext) {
4109
+ function absArcId(arcId) {
4110
+ return arcId >= 0 ? arcId : ~arcId;
4111
+ }
4112
+
4113
+ function calcArcBounds(xx, yy, start, len) {
4114
+ var i = start | 0,
4115
+ n = isNaN(len) ? xx.length - i : len + i,
4116
+ x, y, xmin, ymin, xmax, ymax;
4117
+ if (n > 0) {
4118
+ xmin = xmax = xx[i];
4119
+ ymin = ymax = yy[i];
4120
+ }
4121
+ for (i++; i<n; i++) {
4122
+ x = xx[i];
4123
+ y = yy[i];
4124
+ if (x < xmin) xmin = x;
4125
+ if (x > xmax) xmax = x;
4126
+ if (y < ymin) ymin = y;
4127
+ if (y > ymax) ymax = y;
4128
+ }
4129
+ return [xmin, ymin, xmax, ymax];
4130
+ }
4131
+
4132
+ function getShapeHitTest(displayLayer, ext, interactionMode) {
4102
4133
  var geoType = displayLayer.layer.geometry_type;
4103
4134
  var test;
4104
4135
  if (geoType == 'point' && displayLayer.style.type == 'styled') {
4105
4136
  test = getGraduatedCircleTest(getRadiusFunction(displayLayer.style));
4106
4137
  } else if (geoType == 'point') {
4107
4138
  test = pointTest;
4139
+ } else if (interactionMode == 'vertices') {
4140
+ test = vertexTest;
4108
4141
  } else if (geoType == 'polyline') {
4109
4142
  test = polylineTest;
4110
4143
  } else if (geoType == 'polygon') {
@@ -4130,14 +4163,14 @@
4130
4163
  }
4131
4164
 
4132
4165
  function polygonTest(x, y) {
4133
- var maxDist = getZoomAdjustedHitBuffer(5, 1),
4166
+ var maxDist = getZoomAdjustedHitBuffer(10, 1),
4134
4167
  cands = findHitCandidates(x, y, maxDist),
4135
4168
  hits = [],
4136
4169
  cand, hitId;
4137
4170
  for (var i=0; i<cands.length; i++) {
4138
4171
  cand = cands[i];
4139
4172
  if (geom.testPointInPolygon(x, y, cand.shape, displayLayer.arcs)) {
4140
- hits.push(cand.id);
4173
+ hits.push(cand);
4141
4174
  }
4142
4175
  }
4143
4176
  if (cands.length > 0 && hits.length === 0) {
@@ -4145,7 +4178,9 @@
4145
4178
  sortByDistance(x, y, cands, displayLayer.arcs);
4146
4179
  hits = pickNearestCandidates(cands, 0, maxDist);
4147
4180
  }
4148
- return hits;
4181
+ return {
4182
+ ids: utils.pluck(hits, 'id')
4183
+ };
4149
4184
  }
4150
4185
 
4151
4186
  function pickNearestCandidates(sorted, bufDist, maxDist) {
@@ -4160,22 +4195,41 @@
4160
4195
  } else if (cand.dist - minDist > bufDist) {
4161
4196
  break;
4162
4197
  }
4163
- hits.push(cand.id);
4198
+ hits.push(cand);
4164
4199
  }
4165
4200
  return hits;
4166
4201
  }
4167
4202
 
4203
+ function vertexTest(x, y) {
4204
+ var maxDist = getZoomAdjustedHitBuffer(15, 2),
4205
+ bufDist = getZoomAdjustedHitBuffer(0.05), // tiny threshold for hitting almost-identical lines
4206
+ cands = findHitCandidates(x, y, maxDist);
4207
+ sortByDistance(x, y, cands, displayLayer.arcs);
4208
+ cands = pickNearestCandidates(cands, bufDist, maxDist);
4209
+ var arcs = cands.map(function(cand) { return absArcId(cand.info.arcId); });
4210
+ return {
4211
+ arcs: utils.uniq(arcs),
4212
+ ids: utils.pluck(cands, 'id')
4213
+ };
4214
+ }
4215
+
4168
4216
  function polylineTest(x, y) {
4169
4217
  var maxDist = getZoomAdjustedHitBuffer(15, 2),
4170
4218
  bufDist = getZoomAdjustedHitBuffer(0.05), // tiny threshold for hitting almost-identical lines
4171
4219
  cands = findHitCandidates(x, y, maxDist);
4172
4220
  sortByDistance(x, y, cands, displayLayer.arcs);
4173
- return pickNearestCandidates(cands, bufDist, maxDist);
4221
+ cands = pickNearestCandidates(cands, bufDist, maxDist);
4222
+ return {
4223
+ ids: utils.pluck(cands, 'id')
4224
+ };
4174
4225
  }
4175
4226
 
4176
4227
  function sortByDistance(x, y, cands, arcs) {
4228
+ var cand;
4177
4229
  for (var i=0; i<cands.length; i++) {
4178
- cands[i].dist = geom.getPointToShapeDistance(x, y, cands[i].shape, arcs);
4230
+ cand = cands[i];
4231
+ cand.info = geom.getPointToShapeInfo(x, y, cands[i].shape, arcs);
4232
+ cand.dist = cand.info.distance;
4179
4233
  }
4180
4234
  utils.sortOn(cands, 'dist');
4181
4235
  }
@@ -4203,7 +4257,9 @@
4203
4257
  }
4204
4258
  });
4205
4259
  // console.log(hitThreshold, bullseye);
4206
- return utils.uniq(hits); // multipoint features can register multiple hits
4260
+ return {
4261
+ ids: utils.uniq(hits) // multipoint features can register multiple hits
4262
+ };
4207
4263
  }
4208
4264
 
4209
4265
  function getRadiusFunction(style) {
@@ -4257,7 +4313,9 @@
4257
4313
  hits.push(id);
4258
4314
  }
4259
4315
  });
4260
- return hits;
4316
+ return {
4317
+ ids: hits
4318
+ };
4261
4319
  };
4262
4320
  }
4263
4321
 
@@ -4394,20 +4452,18 @@
4394
4452
 
4395
4453
  }
4396
4454
 
4397
- function getPointerHitTest(mapLayer, ext) {
4455
+ function getPointerHitTest(mapLayer, ext, interactionMode) {
4398
4456
  var shapeTest, svgTest, targetLayer;
4399
4457
  if (!mapLayer || !internal.layerHasGeometry(mapLayer.layer)) {
4400
- return null;
4458
+ return function() {return {ids: []};};
4401
4459
  }
4402
- shapeTest = getShapeHitTest(mapLayer, ext);
4460
+ shapeTest = getShapeHitTest(mapLayer, ext, interactionMode);
4403
4461
  svgTest = getSvgHitTest(mapLayer);
4404
4462
 
4405
4463
  // e: pointer event
4406
4464
  return function(e) {
4407
4465
  var p = ext.translatePixelCoords(e.x, e.y);
4408
- var data = {
4409
- ids: shapeTest(p[0], p[1]) || []
4410
- };
4466
+ var data = shapeTest(p[0], p[1]) || {ids:[]};
4411
4467
  var svgData = svgTest(e); // null or a data object
4412
4468
  if (svgData) { // mouse is over an SVG symbol
4413
4469
  utils.extend(data, svgData);
@@ -4467,22 +4523,25 @@
4467
4523
  }, !!'capture'); // preempt the layer control's arrow key handler
4468
4524
 
4469
4525
  self.setLayer = function(mapLayer) {
4470
- hitTest = getPointerHitTest(mapLayer, ext);
4471
- if (!hitTest) {
4472
- hitTest = function() {return {ids: []};};
4473
- }
4474
4526
  targetLayer = mapLayer;
4527
+ updateHitTest();
4475
4528
  };
4476
4529
 
4530
+ function updateHitTest() {
4531
+ hitTest = getPointerHitTest(targetLayer, ext, interactionMode);
4532
+ }
4533
+
4477
4534
  function turnOn(mode) {
4478
4535
  interactionMode = mode;
4479
4536
  active = true;
4537
+ updateHitTest();
4480
4538
  }
4481
4539
 
4482
4540
  function turnOff() {
4483
4541
  if (active) {
4484
4542
  updateSelectionState(null); // no hit data, no event
4485
4543
  active = false;
4544
+ hitTest = null;
4486
4545
  }
4487
4546
  }
4488
4547
 
@@ -4526,6 +4585,20 @@
4526
4585
  }
4527
4586
  };
4528
4587
 
4588
+ self.setHoverVertex = function(p) {
4589
+ var p2 = storedData.hit_coordinates;
4590
+ if (!active || !p) return;
4591
+ if (p2 && p2[0] == p[0] && p2[1] == p[1]) return;
4592
+ storedData.hit_coordinates = p;
4593
+ triggerHitEvent('change');
4594
+ };
4595
+
4596
+ self.clearVertexOverlay = function() {
4597
+ if (!storedData.hit_coordinates) return;
4598
+ delete storedData.hit_coordinates;
4599
+ triggerHitEvent('change');
4600
+ };
4601
+
4529
4602
  self.clearSelection = function() {
4530
4603
  updateSelectionState(null);
4531
4604
  };
@@ -5866,50 +5939,108 @@
5866
5939
  rec[key] = isString ? String(newVal) : newVal;
5867
5940
  }
5868
5941
 
5869
- var snapVerticesToPoint = internal.snapVerticesToPoint;
5870
-
5871
- function getDisplayCoordsById(id, layer, ext) {
5872
- var coords = getPointCoordsById(id, layer);
5873
- return ext.translateCoords(coords[0], coords[1]);
5874
- }
5942
+ function initLabelDragging(gui, ext, hit) {
5943
+ var downEvt;
5944
+ var activeId;
5945
+ var activeRecord;
5875
5946
 
5876
- function getPointCoordsById(id, layer) {
5877
- var coords = layer && layer.geometry_type == 'point' && layer.shapes[id];
5878
- if (!coords || coords.length != 1) {
5879
- return null;
5947
+ function active(e) {
5948
+ return e.id > -1 && gui.interaction.getMode() == 'labels';
5880
5949
  }
5881
- return coords[0];
5882
- }
5883
5950
 
5884
- function translateDeltaDisplayCoords(dx, dy, ext) {
5885
- var a = ext.translatePixelCoords(0, 0);
5886
- var b = ext.translatePixelCoords(dx, dy);
5887
- return [b[0] - a[0], b[1] - a[1]];
5888
- }
5951
+ hit.on('dragstart', function(e) {
5952
+ if (!active(e)) return;
5953
+ var textNode = getTextTarget3(e);
5954
+ var table = hit.getTargetDataTable();
5955
+ if (!textNode || !table) return false;
5956
+ activeId = e.id;
5957
+ activeRecord = getLabelRecordById(activeId);
5958
+ downEvt = e;
5959
+ gui.dispatchEvent('label_dragstart', {FID: activeId});
5960
+ });
5889
5961
 
5962
+ hit.on('drag', function(e) {
5963
+ if (!active(e)) return;
5964
+ if (e.id != activeId) {
5965
+ error("Mismatched hit ids:", e.id, activeId);
5966
+ }
5967
+ var scale = ext.getSymbolScale() || 1;
5968
+ var textNode;
5969
+ applyDelta(activeRecord, 'dx', e.dx / scale);
5970
+ applyDelta(activeRecord, 'dy', e.dy / scale);
5971
+ textNode = getTextTarget3(e);
5972
+ if (!isMultilineLabel(textNode)) {
5973
+ // update anchor position of single-line labels based on label position
5974
+ // relative to anchor point, for better placement when eventual display font is
5975
+ // different from mapshaper's font.
5976
+ autoUpdateTextAnchor(textNode, activeRecord, getDisplayCoordsById(activeId, hit.getHitTarget().layer, ext));
5977
+ }
5978
+ // updateSymbol(targetTextNode, activeRecord);
5979
+ updateSymbol2(textNode, activeRecord, activeId);
5980
+ });
5890
5981
 
5891
- function InteractiveEditor(gui, ext, hit) {
5892
- // var targetTextNode; // text node currently being dragged
5893
- var dragging = false;
5894
- var activeRecord;
5895
- var activeId = -1;
5896
- var self = new EventDispatcher();
5897
- var activeVertexIds = null; // for vertex dragging
5982
+ hit.on('dragend', function(e) {
5983
+ if (!active(e)) return;
5984
+ gui.dispatchEvent('label_dragend', {FID: e.id});
5985
+ activeId = -1;
5986
+ activeRecord = null;
5987
+ downEvt = null;
5988
+ });
5898
5989
 
5899
- initDragging();
5990
+ function getDisplayCoordsById(id, layer, ext) {
5991
+ var coords = getPointCoordsById(id, layer);
5992
+ return ext.translateCoords(coords[0], coords[1]);
5993
+ }
5900
5994
 
5901
- return self;
5995
+ function getPointCoordsById(id, layer) {
5996
+ var coords = layer && layer.geometry_type == 'point' && layer.shapes[id];
5997
+ if (!coords || coords.length != 1) {
5998
+ return null;
5999
+ }
6000
+ return coords[0];
6001
+ }
6002
+
6003
+ function getTextTarget3(e) {
6004
+ if (e.id > -1 === false || !e.container) return null;
6005
+ return getSymbolNodeById(e.id, e.container);
6006
+ }
6007
+
6008
+ function getSymbolNodeById(id, parent) {
6009
+ // TODO: optimize selector
6010
+ var sel = '[data-id="' + id + '"]';
6011
+ return parent.querySelector(sel);
6012
+ }
5902
6013
 
5903
- function labelEditingEnabled() {
5904
- return gui.interaction && gui.interaction.getMode() == 'labels' ? true : false;
6014
+ function getTextTarget2(e) {
6015
+ var el = e && e.targetSymbol || null;
6016
+ if (el && el.tagName == 'tspan') {
6017
+ el = el.parentNode;
6018
+ }
6019
+ return el && el.tagName == 'text' ? el : null;
5905
6020
  }
5906
6021
 
5907
- function locationEditingEnabled() {
5908
- return gui.interaction && gui.interaction.getMode() == 'location' ? true : false;
6022
+ function getTextTarget(e) {
6023
+ var el = e.target;
6024
+ if (el.tagName == 'tspan') {
6025
+ el = el.parentNode;
6026
+ }
6027
+ return el.tagName == 'text' ? el : null;
5909
6028
  }
5910
6029
 
5911
- function vertexEditingEnabled() {
5912
- return gui.interaction && gui.interaction.getMode() == 'vertices' ? true : false;
6030
+ function getLabelRecordById(id) {
6031
+ var table = hit.getTargetDataTable();
6032
+ if (id >= 0 === false || !table) return null;
6033
+ // add dx and dy properties, if not available
6034
+ if (!table.fieldExists('dx')) {
6035
+ table.addField('dx', 0);
6036
+ }
6037
+ if (!table.fieldExists('dy')) {
6038
+ table.addField('dy', 0);
6039
+ }
6040
+ if (!table.fieldExists('text-anchor')) {
6041
+ table.addField('text-anchor', '');
6042
+ }
6043
+ return table.getRecordAt(id);
5913
6044
  }
5914
6045
 
5915
6046
  // update symbol by setting attributes
@@ -5936,249 +6067,167 @@
5936
6067
  gui.dispatchEvent('popup-needs-refresh');
5937
6068
  return node2;
5938
6069
  }
6070
+ }
5939
6071
 
5940
- function initDragging() {
5941
- var downEvt;
5942
- var eventPriority = 1;
5943
-
5944
- // inspector and label editing aren't fully synced - stop editing if inspector opens
5945
- // gui.on('inspector_on', function() {
5946
- // stopEditing();
5947
- // });
5948
-
5949
- gui.on('interaction_mode_change', function(e) {
5950
- if (e.mode != 'labels') {
5951
- stopDragging();
5952
- }
5953
- gui.undo.clear(); // TODO: put this elsewhere?
5954
- });
5955
-
5956
- // down event on svg
5957
- // a: off text
5958
- // -> stop editing
5959
- // b: on text
5960
- // 1: not editing -> nop
5961
- // 2: on selected text -> start dragging
5962
- // 3: on other text -> stop dragging, select new text
5963
-
5964
- hit.on('dragstart', function(e) {
5965
- if (e.id >= 0 === false) return;
5966
- if (labelEditingEnabled() && onLabelDragStart(e)) {
5967
- triggerGlobalEvent('label_dragstart', e);
5968
- startDragging();
5969
- } else if (locationEditingEnabled()) {
5970
- triggerGlobalEvent('symbol_dragstart', e);
5971
- startDragging();
5972
- } else if (vertexEditingEnabled()) {
5973
- onVertexDragStart(e);
5974
- triggerGlobalEvent('vertex_dragstart', e);
5975
- startDragging();
5976
- }
5977
- });
5978
-
5979
- hit.on('drag', function(e) {
5980
- if (labelEditingEnabled()) {
5981
- onLabelDrag(e);
5982
- } else if (locationEditingEnabled()) {
5983
- onLocationDrag(e);
5984
- } else if (vertexEditingEnabled()) {
5985
- onVertexDrag(e);
5986
- }
5987
- });
5988
-
5989
- hit.on('dragend', function(e) {
5990
- if (locationEditingEnabled()) {
5991
- triggerGlobalEvent('symbol_dragend', e);
5992
- stopDragging();
5993
- } else if (labelEditingEnabled()) {
5994
- triggerGlobalEvent('label_dragend', e);
5995
- stopDragging();
5996
- } else if (vertexEditingEnabled()) {
5997
- // kludge to get dataset to recalculate internal bounding boxes
5998
- hit.getHitTarget().arcs.transformPoints(function() {});
5999
- triggerGlobalEvent('vertex_dragend', e);
6000
- stopDragging();
6001
- }
6002
- });
6003
-
6004
- hit.on('click', function(e) {
6005
- if (labelEditingEnabled()) {
6006
- var target = hit.getHitTarget();
6007
- onLabelClick(e);
6008
- }
6009
- });
6072
+ function initPointDragging(gui, ext, hit) {
6010
6073
 
6011
- // TODO: highlight hit vertex in path edit mode
6012
- if (false) hit.on('hover', function(e) {
6013
- if (vertexEditingEnabled() && !dragging) {
6014
- onVertexHover(e);
6015
- }
6016
- }, null, 100);
6074
+ function active(e) {
6075
+ return e.id > -1 && gui.interaction.getMode() == 'location';
6076
+ }
6017
6077
 
6018
- function onVertexHover(e) {
6019
- // hovering in vertex edit mode: find vertex insertion point
6020
- var target = hit.getHitTarget();
6021
- var shp = target.layer.shapes[e.id];
6022
- var p = ext.translatePixelCoords(e.x, e.y);
6023
- var o = internal.findInsertionPoint(p, shp, target.arcs, ext.getPixelSize());
6024
- }
6078
+ hit.on('dragstart', function(e) {
6079
+ if (!active(e)) return;
6080
+ gui.dispatchEvent('symbol_dragstart', {FID: e.id});
6081
+ });
6025
6082
 
6083
+ hit.on('drag', function(e) {
6084
+ if (!active(e)) return;
6085
+ var lyr = hit.getHitTarget().layer;
6086
+ var p = getPointCoordsById(e.id, lyr);
6087
+ if (!p) return;
6088
+ var diff = translateDeltaDisplayCoords(e.dx, e.dy, ext);
6089
+ p[0] += diff[0];
6090
+ p[1] += diff[1];
6091
+ gui.dispatchEvent('map-needs-refresh');
6092
+ gui.dispatchEvent('symbol_drag', {FID: e.id});
6093
+ });
6026
6094
 
6027
- function getVertexEventData(e) {
6028
- return {
6029
- FID: activeId,
6030
- vertexIds: activeVertexIds
6031
- };
6032
- }
6095
+ hit.on('dragend', function(e) {
6096
+ if (!active(e)) return;
6097
+ gui.dispatchEvent('symbol_dragend', {FID: e.id});
6098
+ });
6033
6099
 
6034
- function onLocationDrag(e) {
6035
- var lyr = hit.getHitTarget().layer;
6036
- var p = getPointCoordsById(e.id, lyr);
6037
- if (!p) return;
6038
- var diff = translateDeltaDisplayCoords(e.dx, e.dy, ext);
6039
- p[0] += diff[0];
6040
- p[1] += diff[1];
6041
- triggerRedraw();
6042
- triggerGlobalEvent('symbol_drag', e);
6043
- }
6100
+ function translateDeltaDisplayCoords(dx, dy, ext) {
6101
+ var a = ext.translatePixelCoords(0, 0);
6102
+ var b = ext.translatePixelCoords(dx, dy);
6103
+ return [b[0] - a[0], b[1] - a[1]];
6104
+ }
6044
6105
 
6045
- function onVertexDragStart(e) {
6046
- var target = hit.getHitTarget();
6047
- var shp = target.layer.shapes[e.id];
6048
- var p = ext.translatePixelCoords(e.x, e.y);
6049
- activeVertexIds = internal.findNearestVertices(p, shp, target.arcs);
6050
- activeId = e.id;
6106
+ function getPointCoordsById(id, layer) {
6107
+ var coords = layer && layer.geometry_type == 'point' && layer.shapes[id];
6108
+ if (!coords || coords.length != 1) {
6109
+ return null;
6051
6110
  }
6111
+ return coords[0];
6112
+ }
6113
+ }
6052
6114
 
6053
- function onVertexDrag(e) {
6054
- var target = hit.getHitTarget();
6055
- if (!activeVertexIds) return; // ignore error condition
6056
- var p = ext.translatePixelCoords(e.x, e.y);
6057
- if (gui.keyboard.shiftIsPressed()) {
6058
- internal.snapPointToArcEndpoint(p, activeVertexIds, target.arcs);
6059
- }
6060
- snapVerticesToPoint(activeVertexIds, p, target.arcs);
6061
- triggerRedraw();
6062
- }
6115
+ function initVertexDragging(gui, ext, hit) {
6116
+ var activeShapeId = -1;
6117
+ var draggedVertexIds = null;
6118
+ var selectedVertexIds = null;
6063
6119
 
6064
- function onLabelClick(e) {
6065
- var textNode = getTextTarget3(e);
6066
- var rec = getLabelRecordById(e.id);
6067
- if (textNode && rec && isMultilineLabel(textNode)) {
6068
- toggleTextAlign(textNode, rec);
6069
- updateSymbol2(textNode, rec, e.id);
6070
- // e.stopPropagation(); // prevent pin/unpin on popup
6071
- }
6072
- }
6120
+ function active(e) {
6121
+ return e.id > -1 && gui.interaction.getMode() == 'vertices';
6122
+ }
6073
6123
 
6074
- function triggerRedraw() {
6075
- gui.dispatchEvent('map-needs-refresh');
6076
- }
6124
+ function fire(type) {
6125
+ gui.dispatchEvent(type, {
6126
+ FID: activeShapeId,
6127
+ vertex_ids: draggedVertexIds
6128
+ });
6129
+ }
6077
6130
 
6078
- function triggerGlobalEvent(type, e) {
6079
- if (e.id >= 0 === false) return;
6080
- var o = {
6081
- FID: e.id,
6082
- layer_name: hit.getHitTarget().layer.name,
6083
- vertex_ids: activeVertexIds
6084
- };
6085
- // fire event to signal external editor that symbol coords have changed
6086
- gui.dispatchEvent(type, o);
6087
- }
6131
+ function setHoverVertex(id) {
6132
+ var target = hit.getHitTarget();
6133
+ hit.setHoverVertex(target.arcs.getVertex2(id));
6134
+ }
6088
6135
 
6089
- function getLabelRecordById(id) {
6090
- var table = hit.getTargetDataTable();
6091
- if (id >= 0 === false || !table) return null;
6092
- // add dx and dy properties, if not available
6093
- if (!table.fieldExists('dx')) {
6094
- table.addField('dx', 0);
6095
- }
6096
- if (!table.fieldExists('dy')) {
6097
- table.addField('dy', 0);
6098
- }
6099
- if (!table.fieldExists('text-anchor')) {
6100
- table.addField('text-anchor', '');
6101
- }
6102
- return table.getRecordAt(id);
6103
- }
6136
+ function clearHoverVertex() {
6137
+ hit.clearVertexOverlay();
6138
+ // gui.state.vertex_overlay = null;
6139
+ }
6104
6140
 
6105
- function onLabelDragStart(e) {
6106
- var textNode = getTextTarget3(e);
6107
- var table = hit.getTargetDataTable();
6108
- if (!textNode || !table) return false;
6109
- activeId = e.id;
6110
- activeRecord = getLabelRecordById(activeId);
6111
- downEvt = e;
6112
- return true;
6141
+ function findDraggableVertices(e) {
6142
+ var target = hit.getHitTarget();
6143
+ var shp = target.layer.shapes[e.id];
6144
+ var p = ext.translatePixelCoords(e.x, e.y);
6145
+ var nearestIds = internal.findNearestVertices(p, shp, target.arcs);
6146
+ var p2 = target.arcs.getVertex2(nearestIds[0]);
6147
+ var dist = geom.distance2D(p[0], p[1], p2[0], p2[1]);
6148
+ var pixelDist = dist / ext.getPixelSize();
6149
+ if (pixelDist > 5) {
6150
+ draggedVertexIds = null;
6151
+ return null;
6113
6152
  }
6153
+ return nearestIds;
6154
+ }
6114
6155
 
6115
- function onLabelDrag(e) {
6116
- var scale = ext.getSymbolScale() || 1;
6117
- var textNode;
6118
- if (!dragging) return;
6119
- if (e.id != activeId) {
6120
- error("Mismatched hit ids:", e.id, activeId);
6121
- }
6122
- applyDelta(activeRecord, 'dx', e.dx / scale);
6123
- applyDelta(activeRecord, 'dy', e.dy / scale);
6124
- textNode = getTextTarget3(e);
6125
- if (!isMultilineLabel(textNode)) {
6126
- // update anchor position of single-line labels based on label position
6127
- // relative to anchor point, for better placement when eventual display font is
6128
- // different from mapshaper's font.
6129
- autoUpdateTextAnchor(textNode, activeRecord, getDisplayCoordsById(activeId, hit.getHitTarget().layer, ext));
6130
- }
6131
- // updateSymbol(targetTextNode, activeRecord);
6132
- updateSymbol2(textNode, activeRecord, activeId);
6133
- }
6156
+ hit.on('dragstart', function(e) {
6157
+ if (!active(e)) return;
6158
+ draggedVertexIds = findDraggableVertices(e);
6159
+ if (!draggedVertexIds) return;
6160
+ setHoverVertex(draggedVertexIds[0]);
6161
+ activeShapeId = e.id;
6162
+ fire('vertex_dragstart');
6163
+ });
6134
6164
 
6135
- function getSymbolNodeById(id, parent) {
6136
- // TODO: optimize selector
6137
- var sel = '[data-id="' + id + '"]';
6138
- return parent.querySelector(sel);
6165
+ hit.on('drag', function(e) {
6166
+ if (!active(e) || !draggedVertexIds) return;
6167
+ var target = hit.getHitTarget();
6168
+ var p = ext.translatePixelCoords(e.x, e.y);
6169
+ if (gui.keyboard.shiftIsPressed()) {
6170
+ internal.snapPointToArcEndpoint(p, draggedVertexIds, target.arcs);
6139
6171
  }
6172
+ internal.snapVerticesToPoint(draggedVertexIds, p, target.arcs);
6173
+ setHoverVertex(draggedVertexIds[0]);
6174
+ // redrawing the whole map updates the data layer as well as the overlay layer
6175
+ // gui.dispatchEvent('map-needs-refresh');
6176
+ });
6140
6177
 
6141
- function getTextTarget3(e) {
6142
- if (e.id > -1 === false || !e.container) return null;
6143
- return getSymbolNodeById(e.id, e.container);
6144
- }
6178
+ hit.on('dragend', function(e) {
6179
+ if (!active(e) || !draggedVertexIds) return;
6180
+ // kludge to get dataset to recalculate internal bounding boxes
6181
+ hit.getHitTarget().arcs.transformPoints(function() {});
6182
+ clearHoverVertex();
6183
+ fire('vertex_dragend');
6184
+ draggedVertexIds = null;
6185
+ activeShapeId = -1;
6186
+ // redraw data layer
6187
+ gui.dispatchEvent('map-needs-refresh');
6188
+ });
6145
6189
 
6146
- function getTextTarget2(e) {
6147
- var el = e && e.targetSymbol || null;
6148
- if (el && el.tagName == 'tspan') {
6149
- el = el.parentNode;
6150
- }
6151
- return el && el.tagName == 'text' ? el : null;
6152
- }
6190
+ // select clicked vertices
6191
+ hit.on('click', function(e) {
6192
+ if (!active(e)) return;
6193
+ var vertices = findDraggableVertices(e); // same selection criteria as for dragging
6194
+ // TODO
6195
+ });
6153
6196
 
6154
- function getTextTarget(e) {
6155
- var el = e.target;
6156
- if (el.tagName == 'tspan') {
6157
- el = el.parentNode;
6158
- }
6159
- return el.tagName == 'text' ? el : null;
6197
+ // highlight hit vertex in path edit mode
6198
+ hit.on('hover', function(e) {
6199
+ if (!active(e) || draggedVertexIds) return; // no hover effect while dragging
6200
+ var vertexIds = findDraggableVertices(e);
6201
+ if (vertexIds) {
6202
+ setHoverVertex(vertexIds[0]);
6203
+ return;
6160
6204
  }
6161
- }
6205
+ var target = hit.getHitTarget();
6206
+ var shp = target.layer.shapes[e.id];
6207
+ var p = ext.translatePixelCoords(e.x, e.y);
6208
+ var o = internal.findInsertionPoint(p, shp, target.arcs, ext.getPixelSize());
6209
+ console.log('*', o, p);
6210
+ clearHoverVertex();
6211
+ }, null, 100);
6162
6212
 
6163
- function startDragging() {
6164
- dragging = true;
6165
- }
6213
+ }
6166
6214
 
6167
- function stopDragging() {
6168
- dragging = false;
6169
- activeId = -1;
6170
- activeRecord = null;
6171
- activeVertexIds = null;
6172
- }
6215
+ function initInteractiveEditing(gui, ext, hit) {
6216
+ initLabelDragging(gui, ext, hit);
6217
+ initPointDragging(gui, ext, hit);
6218
+ initVertexDragging(gui, ext, hit);
6173
6219
 
6174
- function isClickEvent(up, down) {
6175
- var elapsed = Math.abs(down.timeStamp - up.timeStamp);
6176
- var dx = up.screenX - down.screenX;
6177
- var dy = up.screenY - down.screenY;
6178
- var dist = Math.sqrt(dx * dx + dy * dy);
6179
- return dist <= 4 && elapsed < 300;
6180
- }
6220
+ gui.on('interaction_mode_change', function(e) {
6221
+ gui.undo.clear(); // TODO: put this elsewhere?
6222
+ });
6181
6223
 
6224
+ // function isClickEvent(up, down) {
6225
+ // var elapsed = Math.abs(down.timeStamp - up.timeStamp);
6226
+ // var dx = up.screenX - down.screenX;
6227
+ // var dy = up.screenY - down.screenY;
6228
+ // var dist = Math.sqrt(dx * dx + dy * dy);
6229
+ // return dist <= 4 && elapsed < 300;
6230
+ // }
6182
6231
  }
6183
6232
 
6184
6233
  var darkStroke = "#334",
@@ -6349,8 +6398,14 @@
6349
6398
  var geomType = lyr.geometry_type;
6350
6399
  var topId = o.id; // pinned id (if pinned) or hover id
6351
6400
  var topIdx = -1;
6352
- var styler = function(o, i) {
6353
- utils.extend(o, i === topIdx ? topStyle: baseStyle);
6401
+ var styler = function(style, i) {
6402
+ utils.extend(style, i === topIdx ? topStyle: baseStyle);
6403
+ // kludge to show vertices when editing path shapes
6404
+ if (o.mode == 'vertices') {
6405
+ style.vertices = true;
6406
+ style.vertex_overlay = o.hit_coordinates || null;
6407
+ style.fillColor = null;
6408
+ }
6354
6409
  };
6355
6410
  var baseStyle = getDefaultStyle(lyr, selectionStyles[geomType]);
6356
6411
  var topStyle;
@@ -6821,19 +6876,25 @@
6821
6876
  _self.drawVertices = function(shapes, arcs, style, filter) {
6822
6877
  var iter = new internal.ShapeIter(arcs);
6823
6878
  var t = getScaledTransform(_ext);
6824
- var radius = (style.strokeWidth * 0.9 || 2.2) * GUI.getPixelRatio() * getScaledLineScale(_ext);
6879
+ var radius = (style.strokeWidth > 2 ? style.strokeWidth * 0.9 : 2) * GUI.getPixelRatio() * getScaledLineScale(_ext);
6825
6880
  var color = style.strokeColor || 'black';
6826
- var shp;
6881
+ var radius2 = radius * 2;
6827
6882
  _ctx.beginPath();
6828
6883
  _ctx.fillStyle = color;
6829
6884
  for (var i=0; i<shapes.length; i++) {
6830
- shp = shapes[i];
6885
+ var shp = shapes[i];
6831
6886
  if (!shp || filter && !filter(shp)) continue;
6832
- iter.init(shp);
6833
- while (iter.hasNext()) {
6834
- drawCircle(iter.x * t.mx + t.bx, iter.y * t.my + t.by, radius, _ctx);
6887
+ for (var j=0; j<shp.length; j++) {
6888
+ iter.init(shp[j]);
6889
+ while (iter.hasNext()) {
6890
+ drawCircle(iter.x * t.mx + t.bx, iter.y * t.my + t.by, radius, _ctx);
6891
+ }
6835
6892
  }
6836
6893
  }
6894
+ if (style.vertex_overlay) {
6895
+ var p = style.vertex_overlay;
6896
+ drawCircle(p[0] * t.mx + t.bx, p[1] * t.my + t.by, radius2, _ctx);
6897
+ }
6837
6898
  _ctx.fill();
6838
6899
  _ctx.closePath();
6839
6900
  };
@@ -7886,7 +7947,7 @@
7886
7947
  _visibleLayers = [], // cached visible map layers
7887
7948
  _fullBounds = null,
7888
7949
  _intersectionLyr, _activeLyr, _overlayLyr,
7889
- _inspector, _stack, _editor,
7950
+ _inspector, _stack,
7890
7951
  _dynamicCRS;
7891
7952
 
7892
7953
  if (gui.options.showMouseCoordinates) {
@@ -8069,7 +8130,9 @@
8069
8130
  });
8070
8131
  }
8071
8132
 
8072
- _editor = new InteractiveEditor(gui, _ext, _hit);
8133
+ if (gui.interaction) {
8134
+ initInteractiveEditing(gui, _ext, _hit);
8135
+ }
8073
8136
 
8074
8137
  _ext.on('change', function(e) {
8075
8138
  if (e.reset) return; // don't need to redraw map here if extent has been reset
@@ -8089,10 +8152,6 @@
8089
8152
  function updateOverlayLayer(e) {
8090
8153
  var style = getOverlayStyle(_activeLyr.layer, e);
8091
8154
  if (style) {
8092
- // kludge to show vertices when editing path shapes
8093
- if (gui.state.interaction_mode == 'vertices') {
8094
- style.vertices = true;
8095
- }
8096
8155
  _overlayLyr = utils.defaults({
8097
8156
  layer: filterLayerByIds(_activeLyr.layer, style.ids),
8098
8157
  style: style
@@ -8396,14 +8455,14 @@
8396
8455
  startEditing();
8397
8456
  });
8398
8457
 
8399
- function getImportOpts() {
8458
+ function getManifest() {
8459
+ return window.mapshaper.manifest || {}; // kludge -- bin/mapshaper-gui sets this
8460
+ }
8461
+
8462
+ function getImportOpts(manifest) {
8400
8463
  var vars = GUI.getUrlVars();
8401
8464
  var opts = {};
8402
- var manifest = window.mapshaper.manifest || {}; // kludge -- bin/mapshaper-gui sets this
8403
- if (Array.isArray(manifest)) {
8404
- // old-style manifest: an array of filenames
8405
- opts.files = manifest;
8406
- } else if (manifest.files) {
8465
+ if (manifest.files) {
8407
8466
  opts.files = manifest.files.concat();
8408
8467
  } else {
8409
8468
  opts.files = [];
@@ -8421,11 +8480,20 @@
8421
8480
  return opts;
8422
8481
  }
8423
8482
 
8483
+ function getInitialConsoleCommands() {
8484
+ return getManifest().commands || '';
8485
+ }
8486
+
8424
8487
  var startEditing = function() {
8425
8488
  var dataLoaded = false,
8426
- importOpts = getImportOpts(),
8489
+ manifest = getManifest(),
8490
+ importOpts = getImportOpts(manifest),
8427
8491
  gui = new GuiInstance('body');
8428
8492
 
8493
+ if (manifest.blurb) {
8494
+ El('#splash-screen-blurb').text(manifest.blurb);
8495
+ }
8496
+
8429
8497
  new AlertControl(gui);
8430
8498
  new RepairControl(gui);
8431
8499
  new SimplifyControl(gui);
@@ -8462,6 +8530,8 @@
8462
8530
  gui.map.setLayerPinning(o, true);
8463
8531
  });
8464
8532
  }
8533
+ gui.console.runInitialCommands(getInitialConsoleCommands());
8534
+
8465
8535
  });
8466
8536
  };
8467
8537