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/mapshaper.js +705 -558
- package/package.json +1 -1
- package/www/mapshaper-gui.js +249 -151
- package/www/mapshaper.js +705 -558
- package/www/page.css +4 -3
package/package.json
CHANGED
package/www/mapshaper-gui.js
CHANGED
|
@@ -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 (
|
|
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 ? '• ' : 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 = '• ' + 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
|
-
|
|
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
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
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 = '• ';
|
|
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 = '• ' + 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;
|