mapshaper 0.6.77 → 0.6.78

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
@@ -4278,6 +4278,10 @@
4278
4278
  return data;
4279
4279
  }
4280
4280
 
4281
+ function layerHasAttributeData(lyr) {
4282
+ return lyr.data && lyr.data.getFields().length > 0;
4283
+ }
4284
+
4281
4285
  function layerHasNonNullData(lyr) {
4282
4286
  return lyr.data && getFirstNonEmptyRecord(lyr.data.getRecords()) ? true : false;
4283
4287
  }
@@ -4551,6 +4555,7 @@
4551
4555
  __proto__: null,
4552
4556
  insertFieldValues: insertFieldValues,
4553
4557
  getLayerDataTable: getLayerDataTable,
4558
+ layerHasAttributeData: layerHasAttributeData,
4554
4559
  layerHasNonNullData: layerHasNonNullData,
4555
4560
  layerHasGeometry: layerHasGeometry,
4556
4561
  layerIsGeometric: layerIsGeometric,
@@ -18025,11 +18030,10 @@
18025
18030
  opts = utils.extend({}, opts);
18026
18031
  opts.rfc7946 = !opts.gj2008; // use RFC 7946 as the default
18027
18032
  var extension = opts.extension || "json";
18028
- var layerGroups, warn;
18033
+ var layerGroups;
18029
18034
 
18030
18035
  if (opts.rfc7946) {
18031
- warn = getRFC7946Warnings(dataset);
18032
- if (warn) message(warn);
18036
+ warnIfNotWgs84(dataset);
18033
18037
  }
18034
18038
 
18035
18039
  if (opts.file) {
@@ -18120,14 +18124,12 @@
18120
18124
  return feat;
18121
18125
  }
18122
18126
 
18123
- function getRFC7946Warnings(dataset) {
18127
+ function warnIfNotWgs84(dataset) {
18124
18128
  var P = getDatasetCRS(dataset);
18125
- var str;
18126
- if (!P || !isLatLngCRS(P)) {
18127
- str = 'RFC 7946 warning: non-WGS84 GeoJSON output.';
18128
- if (P) str += ' Tip: use "-proj wgs84" to convert.';
18129
- }
18130
- return str;
18129
+ if (P && isLatLngCRS(P) || datasetIsEmpty(dataset)) return;
18130
+ var str = 'RFC 7946 warning: non-WGS84 GeoJSON output.';
18131
+ if (P) str += ' Tip: use "-proj wgs84" to convert.';
18132
+ message(str);
18131
18133
  }
18132
18134
 
18133
18135
  function getDatasetBbox(dataset, rfc7946) {
@@ -18397,7 +18399,7 @@
18397
18399
  'default': GeoJSON,
18398
18400
  exportGeoJSON: exportGeoJSON,
18399
18401
  exportLayerAsGeoJSON: exportLayerAsGeoJSON,
18400
- getRFC7946Warnings: getRFC7946Warnings,
18402
+ warnIfNotWgs84: warnIfNotWgs84,
18401
18403
  getDatasetBbox: getDatasetBbox,
18402
18404
  exportDatasetAsGeoJSON: exportDatasetAsGeoJSON,
18403
18405
  preserveOriginalCRS: preserveOriginalCRS,
@@ -45530,7 +45532,7 @@ ${svg}
45530
45532
  });
45531
45533
  }
45532
45534
 
45533
- var version = "0.6.77";
45535
+ var version = "0.6.78";
45534
45536
 
45535
45537
  // Parse command line args into commands and run them
45536
45538
  // 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.77",
3
+ "version": "0.6.78",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
@@ -1738,12 +1738,12 @@
1738
1738
  var btn = el.findChild('.btn').on('click', function() {
1739
1739
  var nameStr = name.node().value.trim();
1740
1740
  var type = el.findChild('input:checked').node().value;
1741
- addLayer(gui, nameStr, type);
1741
+ addEmptyLayer(gui, nameStr, type);
1742
1742
  popup.close();
1743
1743
  });
1744
1744
  }
1745
1745
 
1746
- function addLayer(gui, name, type) {
1746
+ function addEmptyLayer(gui, name, type) {
1747
1747
  var targ = gui.model.getActiveLayer();
1748
1748
  var crsInfo = targ && internal.getDatasetCrsInfo(targ.dataset);
1749
1749
  var dataset = {
@@ -2716,7 +2716,8 @@
2716
2716
  if (opts.interactionMode == 'vertices') {
2717
2717
  return getVertexStyle(baseLyr, o);
2718
2718
  }
2719
- if (opts.interactionMode == 'drawing') {
2719
+ if (opts.interactionMode == 'edit_lines' ||
2720
+ opts.interactionMode == 'edit_polygons') {
2720
2721
  return getLineEditingStyle(o);
2721
2722
  }
2722
2723
  var geomType = baseLyr.geometry_type;
@@ -3501,10 +3502,6 @@
3501
3502
  return internal.getUnfilteredArcLength(arcId, target.gui.source.dataset.arcs);
3502
3503
  }
3503
3504
 
3504
- function getPointCoords(lyr, fid) {
3505
- return internal.cloneShape(lyr.shapes[fid]);
3506
- }
3507
-
3508
3505
  function getVertexCoords(lyr, id) {
3509
3506
  return lyr.gui.source.dataset.arcs.getVertex2(id);
3510
3507
  }
@@ -3518,14 +3515,6 @@
3518
3515
  }
3519
3516
  }
3520
3517
 
3521
- // coords: [x, y] point in data CRS (not display CRS)
3522
- function setPointCoords(lyr, fid, coords) {
3523
- lyr.shapes[fid] = coords;
3524
- if (isProjectedLayer(lyr)) {
3525
- lyr.shapes[fid] = projectPointCoords(coords, lyr.gui.projectPoint);
3526
- }
3527
- }
3528
-
3529
3518
  function updateVertexCoords(lyr, ids) {
3530
3519
  if (!isProjectedLayer(lyr)) return;
3531
3520
  var p = lyr.gui.displayArcs.getVertex2(ids[0]);
@@ -3545,10 +3534,31 @@
3545
3534
  // Update source data coordinates by projecting display coordinates
3546
3535
  function updatePointCoords(lyr, fid) {
3547
3536
  if (!isProjectedLayer(lyr)) return;
3548
- var displayShp = lyr.shapes[fid];
3537
+ var displayShp = lyr.gui.displayLayer.shapes[fid];
3549
3538
  lyr.shapes[fid] = projectPointCoords(displayShp, lyr.gui.invertPoint);
3550
3539
  }
3551
3540
 
3541
+ // coords: [[x, y]] point in data CRS (not display CRS)
3542
+ function setPointCoords(lyr, fid, coords) {
3543
+ lyr.shapes[fid] = coords;
3544
+ if (isProjectedLayer(lyr)) {
3545
+ lyr.gui.displayLayer.shapes[fid] = projectPointCoords(coords, lyr.gui.projectPoint);
3546
+ }
3547
+ }
3548
+
3549
+ // return an [[x, y]] point in data CRS
3550
+ function getPointCoords(lyr, fid) {
3551
+ var coords = lyr.geometry_type == 'point' && lyr.shapes[fid];
3552
+ if (!coords || coords.length != 1) {
3553
+ return null;
3554
+ }
3555
+ return internal.cloneShape(coords);
3556
+ }
3557
+
3558
+ // export function getPointCoords(lyr, fid) {
3559
+ // return internal.cloneShape(lyr.shapes[fid]);
3560
+ // }
3561
+
3552
3562
  function projectPointCoords(src, proj) {
3553
3563
  var dest = [], p;
3554
3564
  for (var i=0; i<src.length; i++) {
@@ -5609,7 +5619,10 @@
5609
5619
  };
5610
5620
 
5611
5621
  function updateVisibility() {
5612
- if (GUI.isActiveInstance(gui) && !_hidden && !!gui.model.getActiveLayer()) {
5622
+ var noData = !gui.model.getActiveLayer();
5623
+ if (GUI.isActiveInstance(gui) && !_hidden && !noData) {
5624
+ buttons.show();
5625
+ } else if (noData && gui.getMode() != 'import') {
5613
5626
  buttons.show();
5614
5627
  } else {
5615
5628
  buttons.hide();
@@ -5758,12 +5771,13 @@
5758
5771
 
5759
5772
  var menus = {
5760
5773
  standard: ['info', 'selection', 'box'],
5761
- polygons: ['info', 'selection', 'box', 'drawing'],
5774
+ empty: ['edit_points', 'edit_lines', 'edit_polygons'],
5775
+ polygons: ['info', 'selection', 'box', 'edit_polygons'],
5762
5776
  rectangles: ['info', 'selection', 'box', 'rectangles'],
5763
- lines: ['info', 'selection', 'box' , 'drawing'],
5777
+ lines: ['info', 'selection', 'box' , 'edit_lines'],
5764
5778
  table: ['info', 'selection'],
5765
- labels: ['info', 'selection', 'box', 'labels', 'location', 'add-points'],
5766
- points: ['info', 'selection', 'box', 'location', 'add-points']
5779
+ labels: ['info', 'selection', 'box', 'labels', 'edit_points'],
5780
+ points: ['info', 'selection', 'box', 'edit_points'] // , 'add-points'
5767
5781
  };
5768
5782
 
5769
5783
  var prompts = {
@@ -5778,11 +5792,12 @@
5778
5792
  box: 'shift-drag box tool',
5779
5793
  data: 'edit attributes',
5780
5794
  labels: 'position labels',
5781
- location: 'drag points',
5795
+ edit_points: 'draw/edit points',
5796
+ edit_lines: 'draw/edit lines',
5797
+ edit_polygons: 'draw/edit polygons',
5782
5798
  vertices: 'edit vertices',
5783
5799
  selection: 'selection tool',
5784
5800
  'add-points': 'add points',
5785
- drawing: 'draw/reshape tool',
5786
5801
  rectangles: 'drag-to-resize',
5787
5802
  off: 'turn off'
5788
5803
  };
@@ -5791,6 +5806,7 @@
5791
5806
 
5792
5807
  // state variables
5793
5808
  var _editMode = 'off';
5809
+ var _prevMode;
5794
5810
  var _menuOpen = false;
5795
5811
 
5796
5812
  // Only render edit mode button/menu if this option is present
@@ -5838,11 +5854,11 @@
5838
5854
  };
5839
5855
 
5840
5856
  this.modeUsesHitDetection = function(mode) {
5841
- return ['info', 'selection', 'data', 'labels', 'location', 'vertices', 'rectangles', 'drawing'].includes(mode);
5857
+ return ['info', 'selection', 'data', 'labels', 'edit_points', 'vertices', 'rectangles', 'edit_lines', 'edit_polygons'].includes(mode);
5842
5858
  };
5843
5859
 
5844
5860
  this.modeUsesPopup = function(mode) {
5845
- return ['info', 'selection', 'data', 'box', 'labels', 'location', 'rectangles'].includes(mode);
5861
+ return ['info', 'selection', 'data', 'box', 'labels', 'edit_points', 'rectangles'].includes(mode);
5846
5862
  };
5847
5863
 
5848
5864
  this.getMode = getInteractionMode;
@@ -5869,7 +5885,7 @@
5869
5885
  function getAvailableModes() {
5870
5886
  var o = gui.model.getActiveLayer();
5871
5887
  if (!o || !o.layer) {
5872
- return menus.standard; // TODO: more sensible handling of missing layer
5888
+ return menus.empty; // TODO: more sensible handling of missing layer
5873
5889
  }
5874
5890
  if (!o.layer.geometry_type) {
5875
5891
  return menus.table;
@@ -5952,6 +5968,7 @@
5952
5968
  var changed = mode != _editMode;
5953
5969
  if (changed) {
5954
5970
  menu.classed('active', mode != 'off');
5971
+ _prevMode = _editMode;
5955
5972
  _editMode = mode;
5956
5973
  onModeChange();
5957
5974
  updateArrowButton();
@@ -5962,7 +5979,7 @@
5962
5979
  function onModeChange() {
5963
5980
  var mode = getInteractionMode();
5964
5981
  gui.state.interaction_mode = mode;
5965
- gui.dispatchEvent('interaction_mode_change', {mode: mode});
5982
+ gui.dispatchEvent('interaction_mode_change', {mode: mode, prev_mode: _prevMode});
5966
5983
  }
5967
5984
 
5968
5985
  // Update button highlight and selected menu item highlight (if any)
@@ -7580,11 +7597,11 @@
7580
7597
  test = getGraduatedCircleTest(getRadiusFunction(layer.gui.style));
7581
7598
  } else if (geoType == 'point') {
7582
7599
  test = pointTest;
7583
- } else if (interactionMode == 'drawing' && geoType == 'polygon') {
7600
+ } else if (interactionMode == 'edit_polygons') {
7584
7601
  test = polygonVertexTest;
7585
7602
  } else if (
7586
7603
  interactionMode == 'vertices' ||
7587
- interactionMode == 'drawing') {
7604
+ interactionMode == 'edit_lines') {
7588
7605
  test = vertexTest;
7589
7606
  } else if (geoType == 'polyline') {
7590
7607
  test = polylineTest;
@@ -7684,7 +7701,8 @@
7684
7701
 
7685
7702
  function pointTest(x, y) {
7686
7703
  var bullseyeDist = 2, // hit all points w/in 2 px
7687
- hitThreshold = 25,
7704
+ // use small threshold when adding points
7705
+ hitThreshold = interactionMode == 'edit_points' ? 12 : 25,
7688
7706
  toPx = ext.getTransform().mx,
7689
7707
  hits = [];
7690
7708
 
@@ -7899,7 +7917,9 @@
7899
7917
 
7900
7918
  function getPointerHitTest(mapLayer, ext, interactionMode, featureFilter) {
7901
7919
  var shapeTest, targetLayer;
7902
- if (!mapLayer || !internal.layerHasGeometry(mapLayer.gui?.displayLayer)) {
7920
+ // need hit test on empty layers, in case we are drawing shapes
7921
+ // if (!mapLayer || !internal.layerHasGeometry(mapLayer.gui?.displayLayer)) {
7922
+ if (!mapLayer || !mapLayer.gui?.displayLayer.geometry_type) {
7903
7923
  return function() {return {ids: []};};
7904
7924
  }
7905
7925
  shapeTest = getShapeHitTest(mapLayer, ext, interactionMode, featureFilter);
@@ -8021,20 +8041,29 @@
8021
8041
  }
8022
8042
 
8023
8043
  function draggable() {
8024
- return interactionMode == 'vertices' || interactionMode == 'location' ||
8025
- interactionMode == 'labels' || interactionMode == 'drawing';
8044
+ return interactionMode == 'vertices' || interactionMode == 'edit_points' ||
8045
+ interactionMode == 'labels' || interactionMode == 'edit_lines' ||
8046
+ interactionMode == 'edit_polygons';
8026
8047
  }
8027
8048
 
8028
8049
  function clickable() {
8029
8050
  // click used to pin popup and select features
8030
8051
  return interactionMode == 'data' || interactionMode == 'info' ||
8031
- interactionMode == 'selection' || interactionMode == 'rectangles';
8052
+ interactionMode == 'selection' || interactionMode == 'rectangles' ||
8053
+ interactionMode == 'edit_points';
8032
8054
  }
8033
8055
 
8034
8056
  self.getHitId = function() {
8035
8057
  return hitTest ? storedData.id : -1;
8036
8058
  };
8037
8059
 
8060
+ self.setHitId = function(id) {
8061
+ if (storedData.id == id) return;
8062
+ storedData.id = id;
8063
+ storedData.ids = [id];
8064
+ triggerHitEvent('change');
8065
+ };
8066
+
8038
8067
  // Get a reference to the active layer, so listeners to hit events can interact
8039
8068
  // with data and shapes
8040
8069
  self.getHitTarget = function() {
@@ -8169,9 +8198,9 @@
8169
8198
 
8170
8199
 
8171
8200
  mouse.on('click', function(e) {
8201
+ var pinned = storedData.pinned;
8172
8202
  if (!hitTest || !active) return;
8173
8203
  if (!eventIsEnabled('click')) return;
8174
-
8175
8204
  e.stopPropagation();
8176
8205
 
8177
8206
  // TODO: move pinning to inspection control?
@@ -8179,6 +8208,11 @@
8179
8208
  updateSelectionState(convertClickDataToSelectionData(hitTest(e)));
8180
8209
  }
8181
8210
 
8211
+ if (pinned && interactionMode == 'edit_points') {
8212
+ // kludge: intercept the click event if popup is turning off, so
8213
+ // a new point doesn't get made
8214
+ return;
8215
+ }
8182
8216
  triggerHitEvent('click', e);
8183
8217
  }, null, priority);
8184
8218
 
@@ -8306,11 +8340,15 @@
8306
8340
  if (type == 'click' && gui.contextMenu.isOpen()) {
8307
8341
  return false;
8308
8342
  }
8309
- if (type == 'click' && interactionMode == 'drawing') {
8343
+ if (type == 'click' &&
8344
+ (interactionMode == 'edit_lines' || interactionMode == 'edit_polygons')) {
8310
8345
  return true; // click events are triggered even if no shape is hit
8311
8346
  }
8312
-
8313
- if (interactionMode == 'drawing' && (type == 'hover' || type == 'dblclick')) {
8347
+ if (type == 'click' && interactionMode == 'edit_points') {
8348
+ return true;
8349
+ }
8350
+ if ((interactionMode == 'edit_lines' || interactionMode == 'edit_polygons') &&
8351
+ (type == 'hover' || type == 'dblclick')) {
8314
8352
  return true; // special case -- using hover for line drawing animation
8315
8353
  }
8316
8354
 
@@ -8330,7 +8368,7 @@
8330
8368
  }
8331
8369
 
8332
8370
  function possiblyStopPropagation(e) {
8333
- if (interactionMode == 'drawing') {
8371
+ if (interactionMode == 'edit_lines' || interactionMode == 'edit_polygons') {
8334
8372
  // handled conditionally in the control
8335
8373
  return;
8336
8374
  }
@@ -9527,7 +9565,7 @@
9527
9565
  });
9528
9566
 
9529
9567
  self.show = function(id, ids, lyr, pinned, edit) {
9530
- var singleEdit = edit || pinned && gui.interaction.getMode() == 'data';
9568
+ var singleEdit = edit || pinned && !internal.layerHasAttributeData(lyr);
9531
9569
  var multiEdit = pinned && gui.interaction.getMode() == 'selection';
9532
9570
  var maxHeight = parent.node().clientHeight - 36;
9533
9571
 
@@ -9759,16 +9797,16 @@
9759
9797
  gui.session.dataValueUpdated(e.ids, e.field, e.value);
9760
9798
  // Refresh the display if a style variable has been changed interactively
9761
9799
  if (internal.isSupportedSvgStyleProperty(e.field)) {
9762
- // drawLayers();
9763
9800
  gui.dispatchEvent('map-needs-refresh');
9764
9801
  }
9765
9802
  });
9766
9803
 
9767
9804
  hit.on('contextmenu', function(e) {
9768
9805
  var target = hit.getHitTarget();
9769
- if (!e.overMap || !target) return;
9770
-
9771
- if (e.mode == 'drawing') return; // TODO: make special menu for this mode
9806
+ if (!e.overMap || !target || e.mode == 'edit_lines' ||
9807
+ e.mode == 'edit_polygons') {
9808
+ return;
9809
+ }
9772
9810
  gui.contextMenu.open(e, hit.getHitTarget());
9773
9811
  });
9774
9812
 
@@ -10060,14 +10098,43 @@
10060
10098
  }
10061
10099
  }
10062
10100
 
10063
- function initPointDragging(gui, ext, hit) {
10101
+ function initPointEditing(gui, ext, hit) {
10064
10102
  var symbolInfo;
10065
10103
  function active(e) {
10066
- return e.id > -1 && gui.interaction.getMode() == 'location';
10104
+ return gui.interaction.getMode() == 'edit_points';
10067
10105
  }
10068
10106
 
10069
- hit.on('dragstart', function(e) {
10107
+ function overPoint(e) {
10108
+ return active(e) && e.id > -1;
10109
+ }
10110
+
10111
+ gui.on('interaction_mode_change', function(e) {
10112
+ if (e.mode == 'edit_points' && !gui.model.getActiveLayer()) {
10113
+ addEmptyLayer(gui, undefined, 'point');
10114
+ } else if (e.prev_mode == 'edit_points') {
10115
+ gui.container.findChild('.map-layers').classed('add-points', false);
10116
+ }
10117
+
10118
+ });
10119
+
10120
+ hit.on('click', function(e) {
10121
+ if (overPoint(e) || !active(e)) return;
10122
+ // add point
10123
+ var p = pixToDataCoords(e.x, e.y);
10124
+ var target = hit.getHitTarget();
10125
+ appendNewPoint(target, p);
10126
+ gui.dispatchEvent('point_add', {p, target});
10127
+ gui.dispatchEvent('map-needs-refresh');
10128
+ hit.setHitId(target.shapes.length - 1); // highlight new point
10129
+ });
10130
+
10131
+ hit.on('change', function(e) {
10070
10132
  if (!active(e)) return;
10133
+ gui.container.findChild('.map-layers').classed('add-points', !overPoint(e));
10134
+ });
10135
+
10136
+ hit.on('dragstart', function(e) {
10137
+ if (!overPoint(e)) return;
10071
10138
  var target = hit.getHitTarget();
10072
10139
  symbolInfo = {
10073
10140
  FID: e.id,
@@ -10077,37 +10144,36 @@
10077
10144
  });
10078
10145
 
10079
10146
  hit.on('drag', function(e) {
10080
- if (!active(e)) return;
10147
+ if (!overPoint(e)) return;
10081
10148
  // TODO: support multi points... get id of closest part to the pointer
10082
- var p = getPointCoordsById(e.id, symbolInfo.target);
10083
- if (!p) return;
10149
+ // var p = getPointCoordsById(e.id, symbolInfo.target);
10150
+ var id = symbolInfo.FID;
10151
+ var shp = symbolInfo.target.gui.displayLayer.shapes[id];
10152
+ if (!shp) return;
10084
10153
  var diff = translateDeltaDisplayCoords(e.dx, e.dy, ext);
10085
- p[0] += diff[0];
10086
- p[1] += diff[1];
10154
+ shp[0][0] += diff[0];
10155
+ shp[0][1] += diff[1];
10087
10156
  gui.dispatchEvent('map-needs-refresh');
10088
10157
  });
10089
10158
 
10090
10159
  hit.on('dragend', function(e) {
10091
- if (!active(e) || !symbolInfo ) return;
10092
- updatePointCoords(symbolInfo.target, e.id);
10160
+ if (!overPoint(e) || !symbolInfo ) return;
10161
+ updatePointCoords(symbolInfo.target, symbolInfo.FID);
10093
10162
  symbolInfo.endCoords = getPointCoords(symbolInfo.target, e.id);
10094
10163
  gui.dispatchEvent('symbol_dragend', symbolInfo);
10095
10164
  symbolInfo = null;
10096
10165
  });
10097
10166
 
10167
+ function pixToDataCoords(x, y) {
10168
+ var target = hit.getHitTarget();
10169
+ return translateDisplayPoint(target, ext.translatePixelCoords(x, y));
10170
+ }
10171
+
10098
10172
  function translateDeltaDisplayCoords(dx, dy, ext) {
10099
10173
  var a = ext.translatePixelCoords(0, 0);
10100
10174
  var b = ext.translatePixelCoords(dx, dy);
10101
10175
  return [b[0] - a[0], b[1] - a[1]];
10102
10176
  }
10103
-
10104
- function getPointCoordsById(id, layer) {
10105
- var coords = layer && layer.geometry_type == 'point' && layer.shapes[id];
10106
- if (!coords || coords.length != 1) {
10107
- return null;
10108
- }
10109
- return coords[0];
10110
- }
10111
10177
  }
10112
10178
 
10113
10179
  // pixel distance threshold for hovering near a vertex or segment midpoint
@@ -10148,13 +10214,17 @@
10148
10214
  gui.addMode('drawing_tool', turnOn, turnOff);
10149
10215
 
10150
10216
  gui.on('interaction_mode_change', function(e) {
10151
- gui.container.findChild('.map-layers').classed('drawing', e.mode == 'drawing');
10152
- var prevMode = gui.getMode();
10153
- if (e.mode == 'drawing') {
10217
+ if (e.mode == 'edit_lines' || e.mode == 'edit_polygons') {
10218
+ if (!gui.model.getActiveLayer()) {
10219
+ addEmptyLayer(gui, undefined, e.mode == 'edit_lines' ? 'polyline' : 'polygon');
10220
+ }
10154
10221
  gui.enterMode('drawing_tool');
10222
+ } else if (gui.getMode() == 'drawing_tool') {
10223
+ gui.clearMode();
10155
10224
  } else if (active()) {
10156
10225
  turnOff();
10157
10226
  }
10227
+ updateCursor();
10158
10228
  }, null, 10); // higher priority than hit control, so turnOff() has correct hit target
10159
10229
 
10160
10230
  gui.on('redo_path_add', function(e) {
@@ -10201,6 +10271,7 @@
10201
10271
  });
10202
10272
 
10203
10273
  function turnOn() {
10274
+ if (active()) return;
10204
10275
  var target = hit.getHitTarget();
10205
10276
  initialArcCount = target.gui.displayArcs.size();
10206
10277
  initialShapeCount = target.shapes.length;
@@ -10227,6 +10298,7 @@
10227
10298
 
10228
10299
  function turnOff() {
10229
10300
  var removed = 0;
10301
+ var mode = gui.interaction.getMode();
10230
10302
  finishCurrentPath();
10231
10303
  if (polygonMode()) {
10232
10304
  removed = removeOpenPolygons();
@@ -10235,10 +10307,11 @@
10235
10307
  hideInstructions();
10236
10308
  initialArcCount = -1;
10237
10309
  initialShapeCount = -1;
10238
- if (gui.interaction.getMode() == 'drawing') {
10310
+ if (mode == 'edit_lines' || mode == 'edit_polygons') {
10239
10311
  // mode change was not initiated by interactive menu -- turn off interactivity
10240
10312
  gui.interaction.turnOff();
10241
10313
  }
10314
+ updateCursor();
10242
10315
  if (removed > 0) {
10243
10316
  fullRedraw();
10244
10317
  }
@@ -10406,6 +10479,7 @@
10406
10479
  }
10407
10480
 
10408
10481
  function updateCursor() {
10482
+ gui.container.findChild('.map-layers').classed('drawing', active());
10409
10483
  var useArrow = hoverVertexInfo && !hoverVertexInfo.extendable && !drawing();
10410
10484
  gui.container.findChild('.map-layers').classed('dragging', useArrow);
10411
10485
  }
@@ -10664,38 +10738,9 @@
10664
10738
  }
10665
10739
  }
10666
10740
 
10667
- function initPointDrawing(gui, ext, hit) {
10668
- var mouse = gui.map.getMouse();
10669
-
10670
- gui.on('interaction_mode_change', function(e) {
10671
- gui.container.findChild('.map-layers').classed('add-points', e.mode === 'add-points');
10672
- });
10673
-
10674
- function active() {
10675
- return gui.interaction.getMode() == 'add-points';
10676
- }
10677
-
10678
- mouse.on('click', function(e) {
10679
- if (!active()) return;
10680
- var p = pixToDataCoords(e.x, e.y);
10681
- var target = hit.getHitTarget();
10682
- appendNewPoint(target, p);
10683
- gui.dispatchEvent('point_add', {p, target});
10684
- gui.dispatchEvent('map-needs-refresh');
10685
- });
10686
-
10687
-
10688
- function pixToDataCoords(x, y) {
10689
- var target = hit.getHitTarget();
10690
- return translateDisplayPoint(target, ext.translatePixelCoords(x, y));
10691
- }
10692
-
10693
- }
10694
-
10695
10741
  function initInteractiveEditing(gui, ext, hit) {
10696
10742
  initLabelDragging(gui, ext, hit);
10697
- initPointDragging(gui, ext, hit);
10698
- initPointDrawing(gui, ext, hit);
10743
+ initPointEditing(gui, ext, hit);
10699
10744
  initLineEditing(gui, ext, hit);
10700
10745
  }
10701
10746
 
@@ -12133,8 +12178,10 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
12133
12178
  if (activeStyle) {
12134
12179
  turnOffBasemap();
12135
12180
  updateButtons();
12181
+ closeMenu();
12136
12182
  }
12137
12183
  });
12184
+
12138
12185
  fadeBtn.on('click', function() {
12139
12186
  if (faded) {
12140
12187
  mapEl.css('opacity', 1);
@@ -12161,11 +12208,19 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
12161
12208
  showBasemap(style);
12162
12209
  }
12163
12210
  updateButtons();
12211
+ closeMenu();
12164
12212
  });
12165
12213
  btn.appendTo(list);
12166
12214
  });
12167
12215
  }
12168
12216
 
12217
+ // close and turn off mode
12218
+ function closeMenu() {
12219
+ setTimeout(function() {
12220
+ gui.clearMode();
12221
+ }, 200);
12222
+ }
12223
+
12169
12224
  function turnOffBasemap() {
12170
12225
  activeStyle = null;
12171
12226
  gui.map.setDisplayCRS(null);
@@ -12923,7 +12978,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
12923
12978
  var layer = mapshaper.cmd.filterFeatures(lyr, dataset.arcs, opts);
12924
12979
  // the drawing tool can send open paths with 'polygon' geometry type,
12925
12980
  // should be changed to 'polyline'
12926
- if (layerHasOpenPaths(layer, dataset.arcs)) {
12981
+ if (layer.geometry_type == 'polygon' && layerHasOpenPaths(layer, dataset.arcs)) {
12927
12982
  layer.geometry_type = 'polyline';
12928
12983
  }
12929
12984
  var features = internal.exportLayerAsGeoJSON(layer, dataset, {rfc7946: true, prettify: true}, true, 'string');
package/www/mapshaper.js CHANGED
@@ -4278,6 +4278,10 @@
4278
4278
  return data;
4279
4279
  }
4280
4280
 
4281
+ function layerHasAttributeData(lyr) {
4282
+ return lyr.data && lyr.data.getFields().length > 0;
4283
+ }
4284
+
4281
4285
  function layerHasNonNullData(lyr) {
4282
4286
  return lyr.data && getFirstNonEmptyRecord(lyr.data.getRecords()) ? true : false;
4283
4287
  }
@@ -4551,6 +4555,7 @@
4551
4555
  __proto__: null,
4552
4556
  insertFieldValues: insertFieldValues,
4553
4557
  getLayerDataTable: getLayerDataTable,
4558
+ layerHasAttributeData: layerHasAttributeData,
4554
4559
  layerHasNonNullData: layerHasNonNullData,
4555
4560
  layerHasGeometry: layerHasGeometry,
4556
4561
  layerIsGeometric: layerIsGeometric,
@@ -18025,11 +18030,10 @@
18025
18030
  opts = utils.extend({}, opts);
18026
18031
  opts.rfc7946 = !opts.gj2008; // use RFC 7946 as the default
18027
18032
  var extension = opts.extension || "json";
18028
- var layerGroups, warn;
18033
+ var layerGroups;
18029
18034
 
18030
18035
  if (opts.rfc7946) {
18031
- warn = getRFC7946Warnings(dataset);
18032
- if (warn) message(warn);
18036
+ warnIfNotWgs84(dataset);
18033
18037
  }
18034
18038
 
18035
18039
  if (opts.file) {
@@ -18120,14 +18124,12 @@
18120
18124
  return feat;
18121
18125
  }
18122
18126
 
18123
- function getRFC7946Warnings(dataset) {
18127
+ function warnIfNotWgs84(dataset) {
18124
18128
  var P = getDatasetCRS(dataset);
18125
- var str;
18126
- if (!P || !isLatLngCRS(P)) {
18127
- str = 'RFC 7946 warning: non-WGS84 GeoJSON output.';
18128
- if (P) str += ' Tip: use "-proj wgs84" to convert.';
18129
- }
18130
- return str;
18129
+ if (P && isLatLngCRS(P) || datasetIsEmpty(dataset)) return;
18130
+ var str = 'RFC 7946 warning: non-WGS84 GeoJSON output.';
18131
+ if (P) str += ' Tip: use "-proj wgs84" to convert.';
18132
+ message(str);
18131
18133
  }
18132
18134
 
18133
18135
  function getDatasetBbox(dataset, rfc7946) {
@@ -18397,7 +18399,7 @@
18397
18399
  'default': GeoJSON,
18398
18400
  exportGeoJSON: exportGeoJSON,
18399
18401
  exportLayerAsGeoJSON: exportLayerAsGeoJSON,
18400
- getRFC7946Warnings: getRFC7946Warnings,
18402
+ warnIfNotWgs84: warnIfNotWgs84,
18401
18403
  getDatasetBbox: getDatasetBbox,
18402
18404
  exportDatasetAsGeoJSON: exportDatasetAsGeoJSON,
18403
18405
  preserveOriginalCRS: preserveOriginalCRS,
@@ -45530,7 +45532,7 @@ ${svg}
45530
45532
  });
45531
45533
  }
45532
45534
 
45533
- var version = "0.6.77";
45535
+ var version = "0.6.78";
45534
45536
 
45535
45537
  // Parse command line args into commands and run them
45536
45538
  // Function takes an optional Node-style callback. A Promise is returned if no callback is given.
package/www/page.css CHANGED
@@ -60,8 +60,6 @@ body.map-view {
60
60
 
61
61
  .selectable,.selectable * {
62
62
  user-select: default;
63
- -webkit-user-select: text;
64
- -moz-user-select: text;
65
63
  cursor: text;
66
64
  }
67
65
 
@@ -526,6 +524,10 @@ div.alert-box {
526
524
  /* border: 1px solid #aaa; */
527
525
  }
528
526
 
527
+ .info-box label {
528
+ cursor: pointer;
529
+ }
530
+
529
531
  .sidebar-buttons {
530
532
  display: none;
531
533
  position: absolute;