mapshaper 0.6.99 → 0.6.100

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.6.99",
3
+ "version": "0.6.100",
4
4
  "description": "A tool for editing vector datasets for mapping and GIS.",
5
5
  "keywords": [
6
6
  "shapefile",
@@ -876,16 +876,36 @@
876
876
  };
877
877
 
878
878
  // Filter out delayed click events, e.g. so users can highlight and copy text
879
+ // Filter out context menu clicks
879
880
  GUI.onClick = function(el, cb) {
880
881
  var time;
881
882
  el.on('mousedown', function() {
882
883
  time = +new Date();
883
884
  });
884
885
  el.on('mouseup', function(e) {
885
- if (+new Date() - time < 300) cb(e);
886
+ if (looksLikeContextClick(e)) {
887
+ return;
888
+ }
889
+ if (+new Date() - time < 300) {
890
+ cb(e);
891
+ }
892
+ });
893
+ };
894
+
895
+ GUI.onContextClick = function(el, cb) {
896
+ el.on('mouseup', function(e) {
897
+ if (looksLikeContextClick(e)) {
898
+ e.stopPropagation();
899
+ e.preventDefault();
900
+ cb(e);
901
+ }
886
902
  });
887
903
  };
888
904
 
905
+ function looksLikeContextClick(e) {
906
+ return e.button > 1 || e.ctrlKey;
907
+ }
908
+
889
909
  // tests if filename is a type that can be used
890
910
  // GUI.isReadableFileType = function(filename) {
891
911
  // return !!internal.guessInputFileType(filename) || internal.couldBeDsvFile(filename) ||
@@ -5003,6 +5023,201 @@
5003
5023
  };
5004
5024
  }
5005
5025
 
5026
+ var openMenu;
5027
+
5028
+ document.addEventListener('mousedown', function(e) {
5029
+ if (e.target.classList.contains('contextmenu-item')) {
5030
+ return; // don't close menu if clicking on a menu link
5031
+ }
5032
+ closeOpenMenu();
5033
+ });
5034
+
5035
+ function closeOpenMenu() {
5036
+ if (openMenu) {
5037
+ openMenu.close();
5038
+ openMenu = null;
5039
+ }
5040
+ }
5041
+
5042
+ function openContextMenu(e, lyr, parent) {
5043
+ var menu = new ContextMenu(parent);
5044
+ closeOpenMenu();
5045
+ menu.open(e, lyr);
5046
+ }
5047
+
5048
+ function ContextMenu(parentArg) {
5049
+ var body = document.querySelector('body');
5050
+ var parent = parentArg || body;
5051
+ // var menu = El('div').addClass('contextmenu rollover').appendTo(body);
5052
+ var menu = El('div').addClass('contextmenu rollover').appendTo(parent);
5053
+ var _open = false;
5054
+ var _openCount = 0;
5055
+
5056
+ this.isOpen = function() {
5057
+ return _open;
5058
+ };
5059
+
5060
+ this.close = close;
5061
+
5062
+ function close() {
5063
+ var count = _openCount;
5064
+ if (!_open) return;
5065
+ setTimeout(function() {
5066
+ if (count == _openCount) {
5067
+ menu.hide();
5068
+ _open = false;
5069
+ }
5070
+ }, 200);
5071
+ }
5072
+
5073
+ function addMenuItem(label, func, prefixArg) {
5074
+ var prefix = prefixArg === undefined ? '• &nbsp;' : prefixArg;
5075
+ var item = El('div')
5076
+ .appendTo(menu)
5077
+ .addClass('contextmenu-item')
5078
+ .html(prefix + label)
5079
+ .show();
5080
+
5081
+ GUI.onClick(item, function(e) {
5082
+ func();
5083
+ closeOpenMenu();
5084
+ });
5085
+ }
5086
+
5087
+ function addMenuLabel(label) {
5088
+ El('div')
5089
+ .appendTo(menu)
5090
+ .addClass('contextmenu-label')
5091
+ .html(label);
5092
+ }
5093
+
5094
+ this.open = function(e, lyr) {
5095
+ var copyable = e.ids?.length;
5096
+ if (_open) close();
5097
+ menu.empty();
5098
+
5099
+ if (openMenu && openMenu != this) {
5100
+ openMenu.close();
5101
+ }
5102
+ openMenu = this;
5103
+
5104
+ if (e.deleteLayer) {
5105
+ addMenuItem('delete layer', e.deleteLayer, '');
5106
+ }
5107
+ if (e.selectLayer) {
5108
+ addMenuItem('select layer', e.selectLayer, '');
5109
+ }
5110
+
5111
+ if (lyr && lyr.gui.geographic) {
5112
+ if (e.deleteVertex || e.deletePoint || copyable || e.deleteFeature) {
5113
+
5114
+ addMenuLabel('selection');
5115
+ if (e.deleteVertex) {
5116
+ addMenuItem('delete vertex', e.deleteVertex);
5117
+ }
5118
+ if (e.deletePoint) {
5119
+ addMenuItem('delete point', e.deletePoint);
5120
+ }
5121
+ if (e.ids?.length) {
5122
+ addMenuItem('copy as GeoJSON', copyGeoJSON);
5123
+ }
5124
+ if (e.deleteFeature) {
5125
+ addMenuItem(getDeleteLabel(), e.deleteFeature);
5126
+ }
5127
+ }
5128
+
5129
+ if (e.lonlat_coordinates) {
5130
+ addMenuLabel('longitude, latitude');
5131
+ addCoords(e.lonlat_coordinates);
5132
+ }
5133
+ if (e.projected_coordinates) {
5134
+ addMenuLabel('x, y');
5135
+ addCoords(e.projected_coordinates);
5136
+ }
5137
+ }
5138
+
5139
+ if (menu.node().childNodes.length === 0) {
5140
+ return;
5141
+ }
5142
+
5143
+ var rspace = body.clientWidth - e.pageX;
5144
+ var offs = getParentOffset();
5145
+ var xoffs = 10;
5146
+ if (rspace > 150) {
5147
+ menu.css('left', e.pageX - offs.left + xoffs + 'px');
5148
+ menu.css('right', null);
5149
+ } else {
5150
+ menu.css('right', (body.clientWidth - e.pageX - offs.left + xoffs) + 'px');
5151
+ menu.css('left', null);
5152
+ }
5153
+ menu.css('top', (e.pageY - offs.top - 15) + 'px');
5154
+ menu.show();
5155
+
5156
+ _open = true;
5157
+ _openCount++;
5158
+
5159
+ function getParentOffset() { // crossbrowser version
5160
+ if (parent == body) {
5161
+ return {top: 0, left: 0};
5162
+ }
5163
+
5164
+ var box = parent.getBoundingClientRect();
5165
+ var docEl = document.documentElement;
5166
+
5167
+ var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
5168
+ var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;
5169
+
5170
+ var clientTop = docEl.clientTop || body.clientTop || 0;
5171
+ var clientLeft = docEl.clientLeft || body.clientLeft || 0;
5172
+
5173
+ var top = box.top + scrollTop - clientTop;
5174
+ var left = box.left + scrollLeft - clientLeft;
5175
+
5176
+ return { top: Math.round(top), left: Math.round(left) };
5177
+ }
5178
+
5179
+ function getDeleteLabel() {
5180
+ return 'delete ' + (lyr.geometry_type == 'point' ? 'point' : 'shape');
5181
+ }
5182
+
5183
+ function addCoords(p) {
5184
+ var coordStr = p[0] + ',' + p[1];
5185
+ // var displayStr = '• &nbsp;' + coordStr.replace(/-/g, '–').replace(',', ', ');
5186
+ var displayStr = coordStr.replace(/-/g, '–').replace(',', ', ');
5187
+ addMenuItem(displayStr, function() {
5188
+ saveFileContentToClipboard(coordStr);
5189
+ });
5190
+ }
5191
+
5192
+ function copyGeoJSON() {
5193
+ var opts = {
5194
+ no_replace: true,
5195
+ ids: e.ids,
5196
+ quiet: true
5197
+ };
5198
+ var dataset = lyr.gui.source.dataset;
5199
+ var layer = mapshaper.cmd.filterFeatures(lyr, dataset.arcs, opts);
5200
+ // the drawing tool can send open paths with 'polygon' geometry type,
5201
+ // should be changed to 'polyline'
5202
+ if (layer.geometry_type == 'polygon' && layerHasOpenPaths(layer, dataset.arcs)) {
5203
+ layer.geometry_type = 'polyline';
5204
+ }
5205
+ var features = internal.exportLayerAsGeoJSON(layer, dataset, {rfc7946: true, prettify: true}, true, 'string');
5206
+ var str = internal.geojson.formatCollection({"type": "FeatureCollection"}, features);
5207
+ saveFileContentToClipboard(str);
5208
+ }
5209
+ };
5210
+ }
5211
+
5212
+
5213
+ function layerHasOpenPaths(layer, arcs) {
5214
+ var retn = false;
5215
+ internal.editShapes(layer.shapes, function(part) {
5216
+ if (!geom.pathIsClosed(part, arcs)) retn = true;
5217
+ });
5218
+ return retn;
5219
+ }
5220
+
5006
5221
  function LayerControl(gui) {
5007
5222
  var model = gui.model;
5008
5223
  var map = gui.map;
@@ -5210,7 +5425,7 @@
5210
5425
  html = '<!-- ' + lyr.menu_id + '--><div class="' + classes + '">';
5211
5426
  html += rowHTML('name', '<span class="layer-name colored-text dot-underline">' + formatLayerNameForDisplay(lyr.name) + '</span>', 'row1');
5212
5427
  html += rowHTML('contents', describeLyr(lyr, dataset));
5213
- html += '<img class="close-btn" draggable="false" src="images/close.png">';
5428
+ // html += '<img class="close-btn" draggable="false" src="images/close.png">';
5214
5429
  if (opts.pinnable) {
5215
5430
  html += '<img class="eye-btn black-eye" draggable="false" src="images/eye.png">';
5216
5431
  html += '<img class="eye-btn green-eye" draggable="false" src="images/eye2.png">';
@@ -5268,16 +5483,35 @@
5268
5483
  function initMouseEvents2(entry, id, pinnable) {
5269
5484
  initLayerDragging(entry, id);
5270
5485
 
5271
- // init delete button
5272
- GUI.onClick(entry.findChild('img.close-btn'), function(e) {
5486
+ function deleteLayer() {
5273
5487
  var target = findLayerById(id);
5274
- e.stopPropagation();
5275
5488
  if (map.isVisibleLayer(target.layer)) {
5276
5489
  // TODO: check for double map refresh after model.deleteLayer() below
5277
5490
  setLayerPinning(target.layer, false);
5278
5491
  }
5279
5492
  model.deleteLayer(target.layer, target.dataset);
5280
- });
5493
+ }
5494
+
5495
+ function selectLayer(closeMenu) {
5496
+ var target = findLayerById(id);
5497
+ // don't select if user is typing or dragging
5498
+ if (GUI.textIsSelected() || dragging) return;
5499
+ // undo any temporary hiding when layer is selected
5500
+ target.layer.hidden = false;
5501
+ if (!map.isActiveLayer(target.layer)) {
5502
+ model.selectLayer(target.layer, target.dataset);
5503
+ }
5504
+ // close menu after a delay
5505
+ if (closeMenu === true) setTimeout(function() {
5506
+ gui.clearMode();
5507
+ }, 230);
5508
+ }
5509
+
5510
+ // init delete button
5511
+ // GUI.onClick(entry.findChild('img.close-btn'), function(e) {
5512
+ // e.stopPropagation();
5513
+ // deleteLayer();
5514
+ // });
5281
5515
 
5282
5516
  if (pinnable) {
5283
5517
  // init pin button
@@ -5324,18 +5558,15 @@
5324
5558
 
5325
5559
  // init click-to-select
5326
5560
  GUI.onClick(entry, function() {
5327
- var target = findLayerById(id);
5328
- // don't select if user is typing or dragging
5329
- if (GUI.textIsSelected() || dragging) return;
5330
- // undo any temporary hiding when layer is selected
5331
- target.layer.hidden = false;
5332
- if (!map.isActiveLayer(target.layer)) {
5333
- model.selectLayer(target.layer, target.dataset);
5334
- }
5335
- // close menu after a delay
5336
- setTimeout(function() {
5337
- gui.clearMode();
5338
- }, 230);
5561
+ selectLayer(true);
5562
+ });
5563
+
5564
+ GUI.onContextClick(entry, function(e) {
5565
+ e.deleteLayer = deleteLayer;
5566
+ e.selectLayer = selectLayer;
5567
+ // contextMenu.open(e);
5568
+ // openContextMenu(e, null, entry.node())
5569
+ openContextMenu(e, null, null);
5339
5570
  });
5340
5571
  }
5341
5572
 
@@ -12964,139 +13195,6 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
12964
13195
  });
12965
13196
  }
12966
13197
 
12967
- function ContextMenu() {
12968
- var body = document.querySelector('body');
12969
- var menu = El('div').addClass('contextmenu rollover').appendTo(body);
12970
- var _open = false;
12971
- var _openCount = 0;
12972
- document.addEventListener('mousedown', close);
12973
-
12974
- this.isOpen = function() {
12975
- return _open;
12976
- };
12977
-
12978
- function close() {
12979
- var count = _openCount;
12980
- if (!_open) return;
12981
- setTimeout(function() {
12982
- if (count == _openCount) {
12983
- menu.hide();
12984
- _open = false;
12985
- }
12986
- }, 200);
12987
- }
12988
-
12989
- function addMenuItem(label, func) {
12990
- var prefix = '• &nbsp;';
12991
-
12992
- El('div')
12993
- .appendTo(menu)
12994
- .addClass('contextmenu-item')
12995
- .html(prefix + label)
12996
- .on('click', func)
12997
- .show();
12998
- }
12999
-
13000
- function addMenuLabel(label) {
13001
- El('div')
13002
- .appendTo(menu)
13003
- .addClass('contextmenu-label')
13004
- .html(label);
13005
- }
13006
-
13007
- this.open = function(e, lyr) {
13008
- var copyable = e.ids?.length;
13009
- if (lyr && !lyr.gui.geographic) return; // no popup for tabular data
13010
- menu.empty();
13011
-
13012
- // menu contents
13013
- //
13014
- if (e.deleteVertex || e.deletePoint || copyable || e.deleteFeature) {
13015
-
13016
- addMenuLabel('selection');
13017
- if (e.deleteVertex) {
13018
- addMenuItem('delete vertex', e.deleteVertex);
13019
- }
13020
- if (e.deletePoint) {
13021
- addMenuItem('delete point', e.deletePoint);
13022
- }
13023
- if (e.ids?.length) {
13024
- addMenuItem('copy as GeoJSON', copyGeoJSON);
13025
- }
13026
- if (e.deleteFeature) {
13027
- addMenuItem(getDeleteLabel(), e.deleteFeature);
13028
- }
13029
- }
13030
-
13031
- if (e.lonlat_coordinates) {
13032
- addMenuLabel('longitude, latitude');
13033
- addCoords(e.lonlat_coordinates);
13034
- }
13035
- if (e.projected_coordinates) {
13036
- addMenuLabel('x, y');
13037
- addCoords(e.projected_coordinates);
13038
- }
13039
-
13040
- if (menu.node().childNodes.length === 0) {
13041
- return;
13042
- }
13043
-
13044
- _open = true;
13045
- _openCount++;
13046
- var rspace = body.clientWidth - e.pageX;
13047
- var xoffs = 10;
13048
- if (rspace > 150) {
13049
- menu.css('left', e.pageX + xoffs + 'px');
13050
- menu.css('right', null);
13051
- } else {
13052
- menu.css('right', (body.clientWidth - e.pageX + xoffs) + 'px');
13053
- menu.css('left', null);
13054
- }
13055
- menu.css('top', (e.pageY - 15) + 'px');
13056
- menu.show();
13057
-
13058
- function getDeleteLabel() {
13059
- return 'delete ' + (lyr.geometry_type == 'point' ? 'point' : 'shape');
13060
- }
13061
-
13062
- function addCoords(p) {
13063
- var coordStr = p[0] + ',' + p[1];
13064
- // var displayStr = '• &nbsp;' + coordStr.replace(/-/g, '–').replace(',', ', ');
13065
- var displayStr = coordStr.replace(/-/g, '–').replace(',', ', ');
13066
- addMenuItem(displayStr, function() {
13067
- saveFileContentToClipboard(coordStr);
13068
- });
13069
- }
13070
-
13071
- function copyGeoJSON() {
13072
- var opts = {
13073
- no_replace: true,
13074
- ids: e.ids,
13075
- quiet: true
13076
- };
13077
- var dataset = lyr.gui.source.dataset;
13078
- var layer = mapshaper.cmd.filterFeatures(lyr, dataset.arcs, opts);
13079
- // the drawing tool can send open paths with 'polygon' geometry type,
13080
- // should be changed to 'polyline'
13081
- if (layer.geometry_type == 'polygon' && layerHasOpenPaths(layer, dataset.arcs)) {
13082
- layer.geometry_type = 'polyline';
13083
- }
13084
- var features = internal.exportLayerAsGeoJSON(layer, dataset, {rfc7946: true, prettify: true}, true, 'string');
13085
- var str = internal.geojson.formatCollection({"type": "FeatureCollection"}, features);
13086
- saveFileContentToClipboard(str);
13087
- }
13088
- };
13089
- }
13090
-
13091
-
13092
- function layerHasOpenPaths(layer, arcs) {
13093
- var retn = false;
13094
- internal.editShapes(layer.shapes, function(part) {
13095
- if (!geom.pathIsClosed(part, arcs)) retn = true;
13096
- });
13097
- return retn;
13098
- }
13099
-
13100
13198
  function loadScript(url, cb) {
13101
13199
  var script = document.createElement('script');
13102
13200
  script.onload = cb;