mapshaper 0.6.73 → 0.6.75
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 +143 -109
- package/package.json +1 -1
- package/www/mapshaper-gui.js +224 -105
- package/www/mapshaper.js +143 -109
- package/www/page.css +14 -0
package/www/mapshaper-gui.js
CHANGED
|
@@ -1144,10 +1144,18 @@
|
|
|
1144
1144
|
return self;
|
|
1145
1145
|
};
|
|
1146
1146
|
|
|
1147
|
-
self.close = function() {
|
|
1148
|
-
|
|
1147
|
+
self.close = function(action) {
|
|
1148
|
+
var ms = 0;
|
|
1149
|
+
var _el = el;
|
|
1150
|
+
if (action == 'fade' && _el) {
|
|
1151
|
+
ms = 1000;
|
|
1152
|
+
_el.addClass('fade-out');
|
|
1153
|
+
}
|
|
1149
1154
|
if (_close) _close();
|
|
1150
1155
|
el = _cancel = _close = null;
|
|
1156
|
+
setTimeout(function() {
|
|
1157
|
+
if (_el) _el.remove();
|
|
1158
|
+
}, ms);
|
|
1151
1159
|
};
|
|
1152
1160
|
return self;
|
|
1153
1161
|
}
|
|
@@ -1753,6 +1761,9 @@
|
|
|
1753
1761
|
}],
|
|
1754
1762
|
info: {}
|
|
1755
1763
|
};
|
|
1764
|
+
if (type == 'polygon' || type == 'point') {
|
|
1765
|
+
dataset.arcs = new internal.ArcCollection();
|
|
1766
|
+
}
|
|
1756
1767
|
if (crsInfo) {
|
|
1757
1768
|
internal.setDatasetCrsInfo(dataset, crsInfo);
|
|
1758
1769
|
}
|
|
@@ -2426,7 +2437,10 @@
|
|
|
2426
2437
|
}
|
|
2427
2438
|
|
|
2428
2439
|
function error() {
|
|
2429
|
-
|
|
2440
|
+
var msg = GUI.formatMessageArgs(arguments);
|
|
2441
|
+
console.error(msg);
|
|
2442
|
+
gui.alert('An unkown error occured');
|
|
2443
|
+
throw new Error(msg);
|
|
2430
2444
|
}
|
|
2431
2445
|
|
|
2432
2446
|
function message() {
|
|
@@ -2807,7 +2821,7 @@
|
|
|
2807
2821
|
strokeWidth: 1.2,
|
|
2808
2822
|
vertices: true,
|
|
2809
2823
|
vertex_overlay_color: o.hit_type == 'vertex' ? violet : black,
|
|
2810
|
-
vertex_overlay_scale: o.hit_type == 'vertex' ? 2.
|
|
2824
|
+
vertex_overlay_scale: o.hit_type == 'vertex' ? 2.5 : 2,
|
|
2811
2825
|
vertex_overlay: o.hit_coordinates || null,
|
|
2812
2826
|
selected_points: o.selected_points || null,
|
|
2813
2827
|
fillColor: null
|
|
@@ -3025,6 +3039,11 @@
|
|
|
3025
3039
|
return internal.getUnfilteredArcCoords(arcId, target.source.dataset.arcs);
|
|
3026
3040
|
}
|
|
3027
3041
|
|
|
3042
|
+
function getLastVertexCoords(target) {
|
|
3043
|
+
var arcs = target.source.dataset.arcs;
|
|
3044
|
+
return internal.getVertexCoords(arcs.getPointCount() - 1, arcs);
|
|
3045
|
+
}
|
|
3046
|
+
|
|
3028
3047
|
function getLastArcLength(target) {
|
|
3029
3048
|
var arcId = target.source.dataset.arcs.size() - 1;
|
|
3030
3049
|
return internal.getUnfilteredArcLength(arcId, target.source.dataset.arcs);
|
|
@@ -4920,13 +4939,10 @@
|
|
|
4920
4939
|
return internal.getLayerSourceFile(lyr, dataset);
|
|
4921
4940
|
}
|
|
4922
4941
|
|
|
4923
|
-
|
|
4924
4942
|
function isPinnable(lyr) {
|
|
4925
|
-
return internal.
|
|
4943
|
+
return internal.layerIsGeometric(lyr) || internal.layerHasFurniture(lyr);
|
|
4926
4944
|
}
|
|
4927
4945
|
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
4946
|
function rowHTML(c1, c2, cname) {
|
|
4931
4947
|
return utils$1.format('<div class="row%s"><div class="col1">%s</div>' +
|
|
4932
4948
|
'<div class="col2">%s</div></div>', cname ? ' ' + cname : '', c1, c2);
|
|
@@ -5205,10 +5221,10 @@
|
|
|
5205
5221
|
|
|
5206
5222
|
gui.on('path_extend', function(e) {
|
|
5207
5223
|
var redo = function() {
|
|
5208
|
-
gui.dispatchEvent('redo_path_extend', {p: e.p});
|
|
5224
|
+
gui.dispatchEvent('redo_path_extend', {p: e.p, shapes: e.shapes2});
|
|
5209
5225
|
};
|
|
5210
5226
|
var undo = function() {
|
|
5211
|
-
gui.dispatchEvent('undo_path_extend');
|
|
5227
|
+
gui.dispatchEvent('undo_path_extend', {shapes: e.shapes1});
|
|
5212
5228
|
};
|
|
5213
5229
|
addHistoryState(undo, redo);
|
|
5214
5230
|
});
|
|
@@ -5543,22 +5559,23 @@
|
|
|
5543
5559
|
if (!o || !o.layer) {
|
|
5544
5560
|
return menus.standard; // TODO: more sensible handling of missing layer
|
|
5545
5561
|
}
|
|
5546
|
-
if (!
|
|
5562
|
+
if (!o.layer.geometry_type) {
|
|
5547
5563
|
return menus.table;
|
|
5548
5564
|
}
|
|
5549
5565
|
if (internal.layerHasLabels(o.layer)) {
|
|
5550
5566
|
return menus.labels;
|
|
5551
5567
|
}
|
|
5552
|
-
if (
|
|
5568
|
+
if (o.layer.geometry_type == 'point') {
|
|
5553
5569
|
return menus.points;
|
|
5554
5570
|
}
|
|
5555
|
-
if (
|
|
5571
|
+
if (o.layer.geometry_type == 'polyline') {
|
|
5556
5572
|
return menus.lines;
|
|
5557
5573
|
}
|
|
5558
|
-
if (
|
|
5574
|
+
if (o.layer.geometry_type == 'polygon') {
|
|
5559
5575
|
return internal.layerOnlyHasRectangles(o.layer, o.dataset.arcs) ?
|
|
5560
5576
|
menus.rectangles : menus.polygons;
|
|
5561
5577
|
}
|
|
5578
|
+
|
|
5562
5579
|
return menus.standard;
|
|
5563
5580
|
}
|
|
5564
5581
|
|
|
@@ -5908,13 +5925,13 @@
|
|
|
5908
5925
|
}
|
|
5909
5926
|
|
|
5910
5927
|
// Format an array of (preferably short) strings in columns for console logging.
|
|
5911
|
-
function formatStringsAsGrid(arr) {
|
|
5928
|
+
function formatStringsAsGrid(arr, width) {
|
|
5912
5929
|
// TODO: variable column width
|
|
5913
5930
|
var longest = arr.reduce(function(len, str) {
|
|
5914
5931
|
return Math.max(len, str.length);
|
|
5915
5932
|
}, 0),
|
|
5916
5933
|
colWidth = longest + 2,
|
|
5917
|
-
perLine = Math.floor(80 / colWidth) || 1;
|
|
5934
|
+
perLine = Math.floor((width || 80) / colWidth) || 1;
|
|
5918
5935
|
return arr.reduce(function(memo, name, i) {
|
|
5919
5936
|
var col = i % perLine;
|
|
5920
5937
|
if (i > 0 && col === 0) memo += '\n';
|
|
@@ -6575,7 +6592,7 @@
|
|
|
6575
6592
|
}
|
|
6576
6593
|
|
|
6577
6594
|
function getGenericComparator(asc) {
|
|
6578
|
-
asc = asc !== false;
|
|
6595
|
+
asc = asc !== false && asc != 'descending'; // ascending is the default
|
|
6579
6596
|
return function(a, b) {
|
|
6580
6597
|
var retn = 0;
|
|
6581
6598
|
if (b == null) {
|
|
@@ -9262,6 +9279,7 @@
|
|
|
9262
9279
|
} else {
|
|
9263
9280
|
rec = getMultiRecord(recIds, table);
|
|
9264
9281
|
}
|
|
9282
|
+
rec = rec || {};
|
|
9265
9283
|
utils$1.forEachProperty(rec, function(v, k) {
|
|
9266
9284
|
// missing GeoJSON fields are set to undefined on import; skip these
|
|
9267
9285
|
if (v === undefined) return;
|
|
@@ -9709,12 +9727,14 @@
|
|
|
9709
9727
|
var prevClickEvent;
|
|
9710
9728
|
var prevHoverEvent;
|
|
9711
9729
|
var initialArcCount = -1;
|
|
9730
|
+
var initialShapeCount = -1;
|
|
9712
9731
|
var drawingId = -1; // feature id of path being drawn
|
|
9732
|
+
var sessionCount = 0;
|
|
9713
9733
|
var alert;
|
|
9714
9734
|
var _dragging = false;
|
|
9715
9735
|
|
|
9716
9736
|
function active() {
|
|
9717
|
-
return
|
|
9737
|
+
return initialArcCount >= 0;
|
|
9718
9738
|
}
|
|
9719
9739
|
|
|
9720
9740
|
function dragging() {
|
|
@@ -9752,7 +9772,7 @@
|
|
|
9752
9772
|
appendNewPath(target, e.p1, e.p2);
|
|
9753
9773
|
deleteLastVertex(target); // second vertex is a placeholder
|
|
9754
9774
|
gui.undo.redo(); // add next vertex in the path
|
|
9755
|
-
|
|
9775
|
+
fullRedraw();
|
|
9756
9776
|
});
|
|
9757
9777
|
|
|
9758
9778
|
gui.on('undo_path_add', function(e) {
|
|
@@ -9768,8 +9788,28 @@
|
|
|
9768
9788
|
} else {
|
|
9769
9789
|
appendVertex$1(target, e.p);
|
|
9770
9790
|
}
|
|
9791
|
+
if (e.shapes) {
|
|
9792
|
+
replaceShapes(e.shapes);
|
|
9793
|
+
}
|
|
9771
9794
|
});
|
|
9772
9795
|
|
|
9796
|
+
function replaceShapes(shapes) {
|
|
9797
|
+
var target = hit.getHitTarget();
|
|
9798
|
+
var records = target.layer.data?.getRecords();
|
|
9799
|
+
var prevLen = target.layer.shapes.length;
|
|
9800
|
+
var newLen = initialShapeCount + shapes.length;
|
|
9801
|
+
var recordCount = records?.length || 0;
|
|
9802
|
+
target.layer.shapes = target.layer.shapes.slice(0, initialShapeCount).concat(shapes);
|
|
9803
|
+
while (records && records.length > newLen) {
|
|
9804
|
+
records.pop();
|
|
9805
|
+
}
|
|
9806
|
+
while (records && records.length < newLen) {
|
|
9807
|
+
appendNewDataRecord(target.layer);
|
|
9808
|
+
}
|
|
9809
|
+
}
|
|
9810
|
+
|
|
9811
|
+
|
|
9812
|
+
|
|
9773
9813
|
gui.on('undo_path_extend', function(e) {
|
|
9774
9814
|
var target = hit.getHitTarget();
|
|
9775
9815
|
if (drawing() && prevHoverEvent) {
|
|
@@ -9781,72 +9821,70 @@
|
|
|
9781
9821
|
if (getLastArcLength(target) < 2) {
|
|
9782
9822
|
gui.undo.undo(); // remove the path
|
|
9783
9823
|
}
|
|
9824
|
+
if (e.shapes) {
|
|
9825
|
+
replaceShapes(e.shapes);
|
|
9826
|
+
}
|
|
9784
9827
|
});
|
|
9785
9828
|
|
|
9786
9829
|
function turnOn() {
|
|
9787
9830
|
var target = hit.getHitTarget();
|
|
9788
|
-
|
|
9831
|
+
initialArcCount = target.arcs.size();
|
|
9832
|
+
initialShapeCount = target.layer.shapes.length;
|
|
9833
|
+
if (sessionCount === 0) {
|
|
9834
|
+
showInstructions();
|
|
9835
|
+
}
|
|
9836
|
+
sessionCount++;
|
|
9837
|
+
}
|
|
9838
|
+
|
|
9839
|
+
function showInstructions() {
|
|
9789
9840
|
var isMac = navigator.userAgent.includes('Mac');
|
|
9790
9841
|
var symbol = isMac ? '⌘' : '^';
|
|
9842
|
+
var pathStr = polygonMode() ? 'closed paths' : 'paths';
|
|
9791
9843
|
var msg = `Instructions: Click on the map to draw ${pathStr}. Drag vertices to reshape a path. Type ${symbol}Z/${symbol}Y to undo/redo.`;
|
|
9792
|
-
|
|
9793
|
-
|
|
9844
|
+
alert = showPopupAlert(msg, null, {
|
|
9845
|
+
non_blocking: true, max_width: '360px'});
|
|
9794
9846
|
}
|
|
9795
9847
|
|
|
9796
9848
|
function turnOff() {
|
|
9797
|
-
|
|
9849
|
+
var removed = 0;
|
|
9850
|
+
finishCurrentPath();
|
|
9798
9851
|
if (polygonMode()) {
|
|
9799
|
-
|
|
9852
|
+
removed = removeOpenPolygons();
|
|
9800
9853
|
}
|
|
9801
9854
|
clearDrawingInfo();
|
|
9802
|
-
alert
|
|
9803
|
-
|
|
9855
|
+
if (alert) {
|
|
9856
|
+
alert.close();
|
|
9857
|
+
alert = null;
|
|
9858
|
+
}
|
|
9804
9859
|
initialArcCount = -1;
|
|
9860
|
+
initialShapeCount = -1;
|
|
9805
9861
|
if (gui.interaction.getMode() == 'drawing') {
|
|
9806
9862
|
// mode change was not initiated by interactive menu -- turn off interactivity
|
|
9807
9863
|
gui.interaction.turnOff();
|
|
9808
9864
|
}
|
|
9809
|
-
|
|
9810
|
-
|
|
9811
|
-
function finish() {
|
|
9812
|
-
if (polygonMode()) {
|
|
9813
|
-
finishPolygons();
|
|
9865
|
+
if (removed > 0) {
|
|
9866
|
+
fullRedraw();
|
|
9814
9867
|
}
|
|
9815
9868
|
}
|
|
9816
9869
|
|
|
9817
|
-
|
|
9818
|
-
|
|
9870
|
+
// returns number of removed shapes
|
|
9871
|
+
function removeOpenPolygons() {
|
|
9819
9872
|
var target = hit.getHitTarget();
|
|
9820
|
-
|
|
9821
|
-
var
|
|
9822
|
-
|
|
9823
|
-
var
|
|
9824
|
-
|
|
9825
|
-
|
|
9826
|
-
|
|
9827
|
-
polygonRecords.splice(initialArcCount); // remove new records
|
|
9828
|
-
}
|
|
9829
|
-
var polylineLyr = {
|
|
9830
|
-
geometry_type: 'polyline',
|
|
9831
|
-
shapes: polygonLyr.shapes.splice(initialArcCount) // move new shapes to polyline layer
|
|
9832
|
-
};
|
|
9833
|
-
|
|
9834
|
-
// step2: convert polylines to polygons
|
|
9835
|
-
//
|
|
9836
|
-
// create a temp dataset containing both layers (so original arcs are preserved)
|
|
9837
|
-
var tmp = Object.assign({}, target.source.dataset);
|
|
9838
|
-
tmp.layers = tmp.layers.concat(polylineLyr);
|
|
9839
|
-
var outputLayers = mapshaper.cmd.polygons([polylineLyr], tmp, {});
|
|
9840
|
-
|
|
9841
|
-
// step3: add new polygons to the original polygons
|
|
9842
|
-
outputLayers[0].shapes.forEach(function(shp) {
|
|
9843
|
-
polygonLyr.shapes.push(shp);
|
|
9844
|
-
if (polygonRecords) {
|
|
9845
|
-
polygonRecords.push(internal.copyRecord(templateRecord));
|
|
9873
|
+
var arcs = target.source.dataset.arcs;
|
|
9874
|
+
var n = target.layer.shapes.length;
|
|
9875
|
+
// delete open paths (should only occur on single-arc shapes)
|
|
9876
|
+
for (var i=initialShapeCount; i<n; i++) {
|
|
9877
|
+
var shp = target.layer.shapes[i];
|
|
9878
|
+
if (!geom.pathIsClosed(shp[0], arcs)) {
|
|
9879
|
+
target.layer.shapes[i] = null;
|
|
9846
9880
|
}
|
|
9847
|
-
}
|
|
9881
|
+
}
|
|
9882
|
+
// removes polygons with wrong winding order and null geometry
|
|
9883
|
+
mapshaper.cmd.filterFeatures(target.layer, arcs, {remove_empty: true, quiet: true});
|
|
9884
|
+
return n - target.layer.shapes.length;
|
|
9885
|
+
}
|
|
9848
9886
|
|
|
9849
|
-
|
|
9887
|
+
function fullRedraw() {
|
|
9850
9888
|
gui.model.updated({arc_count: true});
|
|
9851
9889
|
}
|
|
9852
9890
|
|
|
@@ -9859,6 +9897,9 @@
|
|
|
9859
9897
|
|
|
9860
9898
|
hit.on('dragstart', function(e) {
|
|
9861
9899
|
if (!active() || drawing() || !hoverVertexInfo) return;
|
|
9900
|
+
if (alert) {
|
|
9901
|
+
alert.close('fade');
|
|
9902
|
+
}
|
|
9862
9903
|
e.originalEvent.stopPropagation();
|
|
9863
9904
|
_dragging = true;
|
|
9864
9905
|
if (hoverVertexInfo.type == 'interpolated') {
|
|
@@ -9899,12 +9940,10 @@
|
|
|
9899
9940
|
// double-click finishes a path (when drawing)
|
|
9900
9941
|
hit.on('dblclick', function(e) {
|
|
9901
9942
|
if (!active()) return;
|
|
9943
|
+
// double click finishes a path
|
|
9944
|
+
// note: if the preceding 'click' finished the path, this does not fire
|
|
9902
9945
|
if (drawing()) {
|
|
9903
|
-
|
|
9904
|
-
// before: dblclick is preceded by two clicks, need another vertex delete
|
|
9905
|
-
// now: second click is suppressed
|
|
9906
|
-
// deleteLastVertex(hit.getHitTarget());
|
|
9907
|
-
finishPath();
|
|
9946
|
+
finishCurrentPath();
|
|
9908
9947
|
e.originalEvent.stopPropagation(); // prevent dblclick zoom
|
|
9909
9948
|
return;
|
|
9910
9949
|
}
|
|
@@ -9916,7 +9955,7 @@
|
|
|
9916
9955
|
if (!active() || dragging()) return;
|
|
9917
9956
|
if (drawing()) {
|
|
9918
9957
|
if (!e.overMap) {
|
|
9919
|
-
|
|
9958
|
+
finishCurrentPath();
|
|
9920
9959
|
return;
|
|
9921
9960
|
}
|
|
9922
9961
|
if (gui.keyboard.shiftIsPressed()) {
|
|
@@ -9944,27 +9983,27 @@
|
|
|
9944
9983
|
if (!active()) return;
|
|
9945
9984
|
if (detectDoubleClick(e)) return; // ignore second click of a dblclick
|
|
9946
9985
|
var p = pixToDataCoords(e.x, e.y);
|
|
9947
|
-
if (drawing()
|
|
9948
|
-
|
|
9949
|
-
p
|
|
9950
|
-
extendPath(p);
|
|
9951
|
-
finishPath();
|
|
9952
|
-
} else if (drawing()) {
|
|
9953
|
-
extendPath(p);
|
|
9986
|
+
if (drawing()) {
|
|
9987
|
+
extendCurrentPath(hoverVertexInfo?.point || p);
|
|
9988
|
+
// extendCurrentPath(p); // just extend to current mouse position (not hover vertex)
|
|
9954
9989
|
} else if (gui.keyboard.shiftIsPressed()) {
|
|
9955
9990
|
deleteActiveVertex(e);
|
|
9956
9991
|
} else {
|
|
9957
|
-
|
|
9992
|
+
startNewPath(p);
|
|
9993
|
+
if (alert) {
|
|
9994
|
+
alert.close('fade');
|
|
9995
|
+
}
|
|
9958
9996
|
}
|
|
9959
9997
|
prevClickEvent = e;
|
|
9960
9998
|
});
|
|
9961
9999
|
|
|
9962
10000
|
// esc key finishes a path
|
|
9963
10001
|
gui.keyboard.on('keydown', function(e) {
|
|
9964
|
-
if (active() && e.keyName == 'esc') {
|
|
9965
|
-
|
|
10002
|
+
if (active() && (e.keyName == 'esc' || e.keyName == 'enter')) {
|
|
10003
|
+
e.stopPropagation();
|
|
10004
|
+
finishCurrentPath();
|
|
9966
10005
|
}
|
|
9967
|
-
});
|
|
10006
|
+
}, null, 10);
|
|
9968
10007
|
|
|
9969
10008
|
// detect second 'click' event of a double-click action
|
|
9970
10009
|
function detectDoubleClick(evt) {
|
|
@@ -10032,7 +10071,7 @@
|
|
|
10032
10071
|
thisEvt.y = snapped.y;
|
|
10033
10072
|
}
|
|
10034
10073
|
|
|
10035
|
-
function
|
|
10074
|
+
function finishCurrentPath() {
|
|
10036
10075
|
if (!drawing()) return;
|
|
10037
10076
|
var target = hit.getHitTarget();
|
|
10038
10077
|
if (getLastArcLength(target) <= 2) { // includes hover point
|
|
@@ -10041,27 +10080,44 @@
|
|
|
10041
10080
|
deleteLastVertex(target);
|
|
10042
10081
|
}
|
|
10043
10082
|
clearDrawingInfo();
|
|
10044
|
-
|
|
10083
|
+
fullRedraw();
|
|
10045
10084
|
}
|
|
10046
10085
|
|
|
10047
10086
|
// p: [x, y] source data coordinates
|
|
10048
|
-
function
|
|
10087
|
+
function startNewPath(p2) {
|
|
10049
10088
|
var target = hit.getHitTarget();
|
|
10050
|
-
var p1 = hoverVertexInfo
|
|
10089
|
+
var p1 = hoverVertexInfo?.point || p2;
|
|
10051
10090
|
appendNewPath(target, p1, p2);
|
|
10052
10091
|
gui.dispatchEvent('path_add', {target, p1, p2});
|
|
10053
10092
|
drawingId = target.layer.shapes.length - 1;
|
|
10054
10093
|
hit.setDrawingId(drawingId);
|
|
10055
10094
|
}
|
|
10056
10095
|
|
|
10057
|
-
// p: [x, y] source data coordinates
|
|
10058
|
-
function
|
|
10096
|
+
// p: [x, y] source data coordinates of new point on path
|
|
10097
|
+
function extendCurrentPath(p) {
|
|
10059
10098
|
var target = hit.getHitTarget();
|
|
10060
|
-
|
|
10099
|
+
var shapes1, shapes2;
|
|
10100
|
+
// finish the path if a vertex is selected (but not an interpolated point)
|
|
10101
|
+
var finish = hoverVertexInfo?.type == 'vertex';
|
|
10102
|
+
if (getLastArcLength(target) < 2) {
|
|
10103
|
+
error$1('Defective path');
|
|
10104
|
+
}
|
|
10105
|
+
if (finish && polygonMode()) {
|
|
10106
|
+
shapes1 = target.layer.shapes.slice(initialShapeCount);
|
|
10107
|
+
shapes2 = tryToClosePath(shapes1);
|
|
10108
|
+
}
|
|
10109
|
+
if (shapes2) {
|
|
10110
|
+
replaceShapes(shapes2);
|
|
10111
|
+
gui.dispatchEvent('path_extend', {target, p, shapes1, shapes2});
|
|
10112
|
+
clearDrawingInfo();
|
|
10113
|
+
fullRedraw();
|
|
10114
|
+
|
|
10115
|
+
} else {
|
|
10116
|
+
appendVertex$1(target, p);
|
|
10061
10117
|
gui.dispatchEvent('path_extend', {target, p});
|
|
10118
|
+
hit.triggerChangeEvent(); // trigger overlay redraw
|
|
10062
10119
|
}
|
|
10063
|
-
|
|
10064
|
-
hit.triggerChangeEvent();
|
|
10120
|
+
|
|
10065
10121
|
}
|
|
10066
10122
|
|
|
10067
10123
|
// p: [x, y] source data coordinates
|
|
@@ -10156,6 +10212,55 @@
|
|
|
10156
10212
|
return closest;
|
|
10157
10213
|
}
|
|
10158
10214
|
|
|
10215
|
+
// shapes: shapes that have been drawn in the current session
|
|
10216
|
+
//
|
|
10217
|
+
function tryToClosePath(shapes) {
|
|
10218
|
+
var target = hit.getHitTarget();
|
|
10219
|
+
var tmpLyr = {
|
|
10220
|
+
geometry_type: 'polyline',
|
|
10221
|
+
shapes: shapes.concat()
|
|
10222
|
+
};
|
|
10223
|
+
// create a temp dataset containing tmp layer and original layers
|
|
10224
|
+
// (so original arcs are retained)
|
|
10225
|
+
var tmpDataset = Object.assign({}, target.source.dataset);
|
|
10226
|
+
tmpDataset.layers = tmpDataset.layers.concat(tmpLyr);
|
|
10227
|
+
// NOTE: added "no_cuts" option to prevent polygons function from modifying
|
|
10228
|
+
// arcs, which would break undo/redo and cause other problems
|
|
10229
|
+
var outputLyr = mapshaper.cmd.polygons([tmpLyr], tmpDataset, {no_cuts: true})[0];
|
|
10230
|
+
var isOpenPath = getOpenPathTest(outputLyr.shapes);
|
|
10231
|
+
var shapes2 = [];
|
|
10232
|
+
shapes.forEach(function(shp) {
|
|
10233
|
+
if (isOpenPath(shp)) {
|
|
10234
|
+
shapes2.push(shp);
|
|
10235
|
+
}
|
|
10236
|
+
});
|
|
10237
|
+
return shapes2.concat(outputLyr.shapes);
|
|
10238
|
+
}
|
|
10239
|
+
|
|
10240
|
+
// Returns a function for testing if a shape is an unclosed path, and doesn't
|
|
10241
|
+
// overlap with an array of polygon shapes
|
|
10242
|
+
// polygons: array of polygon shapes
|
|
10243
|
+
function getOpenPathTest(polygons) {
|
|
10244
|
+
var arcs = [];
|
|
10245
|
+
internal.forEachArcId(polygons, function(arcId) {
|
|
10246
|
+
if (arcId < 0) arcId = ~arcId;
|
|
10247
|
+
arcs.push(arcId);
|
|
10248
|
+
});
|
|
10249
|
+
return function(shp) {
|
|
10250
|
+
// assume that compound shapes are already polygons
|
|
10251
|
+
var isOpen = false;
|
|
10252
|
+
if (shapeHasOneFwdArc(shp)) {
|
|
10253
|
+
var arcId = shp[0][0];
|
|
10254
|
+
if (arcId < 0) arcId = ~arcId;
|
|
10255
|
+
isOpen = !arcs.includes(arcId);
|
|
10256
|
+
}
|
|
10257
|
+
return isOpen;
|
|
10258
|
+
};
|
|
10259
|
+
}
|
|
10260
|
+
|
|
10261
|
+
function shapeHasOneFwdArc(shp) {
|
|
10262
|
+
return shp.length == 1 && shp[0].length == 1 && shp[0][0] >= 0;
|
|
10263
|
+
}
|
|
10159
10264
|
}
|
|
10160
10265
|
|
|
10161
10266
|
function initPointDrawing(gui, ext, hit) {
|
|
@@ -10752,8 +10857,8 @@
|
|
|
10752
10857
|
function getArcsForRendering(obj, ext) {
|
|
10753
10858
|
var dataset = obj.source.dataset;
|
|
10754
10859
|
var sourceArcs = dataset.arcs;
|
|
10755
|
-
if (obj.geographic && dataset.displayArcs) {
|
|
10756
|
-
return dataset.displayArcs.getScaledArcs(ext);
|
|
10860
|
+
if (obj.geographic && dataset.gui?.displayArcs) {
|
|
10861
|
+
return dataset.gui.displayArcs.getScaledArcs(ext);
|
|
10757
10862
|
}
|
|
10758
10863
|
return obj.arcs;
|
|
10759
10864
|
}
|
|
@@ -11760,6 +11865,10 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
11760
11865
|
|
|
11761
11866
|
// Only generate low-detail arcs for larger datasets
|
|
11762
11867
|
if (size > 5e5) {
|
|
11868
|
+
update();
|
|
11869
|
+
}
|
|
11870
|
+
|
|
11871
|
+
function update() {
|
|
11763
11872
|
if (unfilteredArcs.getVertexData().zz) {
|
|
11764
11873
|
// Use precalculated simplification data for vertex filtering, if available
|
|
11765
11874
|
filteredArcs = initFilteredArcs(unfilteredArcs);
|
|
@@ -11783,6 +11892,14 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
11783
11892
|
|
|
11784
11893
|
// TODO: better job of detecting arc change... e.g. revision number
|
|
11785
11894
|
unfilteredArcs.getScaledArcs = function(ext) {
|
|
11895
|
+
// check for changes in the number of arcs (probably due to editing)
|
|
11896
|
+
if (filteredArcs && filteredArcs.size() != unfilteredArcs.size()) {
|
|
11897
|
+
// arc count has changed... probably due to editing
|
|
11898
|
+
update();
|
|
11899
|
+
if (filteredArcs.size() != unfilteredArcs.size()) {
|
|
11900
|
+
throw Error('Internal error');
|
|
11901
|
+
}
|
|
11902
|
+
}
|
|
11786
11903
|
if (filteredArcs) {
|
|
11787
11904
|
// match simplification of unfiltered arcs
|
|
11788
11905
|
filteredArcs.setRetainedInterval(unfilteredArcs.getRetainedInterval());
|
|
@@ -11861,10 +11978,10 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
11861
11978
|
//if (!lyr.geographic || !sourceCRS) {
|
|
11862
11979
|
// let getDisplayLayer() handle case of unprojectable source
|
|
11863
11980
|
if (!lyr.geographic) {
|
|
11864
|
-
return
|
|
11981
|
+
return;
|
|
11865
11982
|
}
|
|
11866
11983
|
if (lyr.dynamic_crs && internal.crsAreEqual(sourceCRS, lyr.dynamic_crs)) {
|
|
11867
|
-
return
|
|
11984
|
+
return;
|
|
11868
11985
|
}
|
|
11869
11986
|
lyr2 = getDisplayLayer(lyr.source.layer, lyr.source.dataset, {crs: displayCRS});
|
|
11870
11987
|
// kludge: copy projection-related properties to original layer
|
|
@@ -11900,7 +12017,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
11900
12017
|
|
|
11901
12018
|
var displayCRS = opts.crs || null;
|
|
11902
12019
|
// display arcs may have been generated when another layer in the dataset was converted for display... re-use if available
|
|
11903
|
-
var displayArcs = dataset.displayArcs || null;
|
|
12020
|
+
var displayArcs = dataset.gui?.displayArcs || null;
|
|
11904
12021
|
var sourceCRS;
|
|
11905
12022
|
var emptyArcs;
|
|
11906
12023
|
|
|
@@ -11917,7 +12034,8 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
11917
12034
|
|
|
11918
12035
|
// Assume that dataset.displayArcs is in the display CRS
|
|
11919
12036
|
// (it must be deleted upstream if reprojection is needed)
|
|
11920
|
-
if (!obj.empty && dataset.arcs && !displayArcs) {
|
|
12037
|
+
// if (!obj.empty && dataset.arcs && !displayArcs) {
|
|
12038
|
+
if (dataset.arcs && !displayArcs) {
|
|
11921
12039
|
// project arcs, if needed
|
|
11922
12040
|
if (needReprojectionForDisplay(sourceCRS, displayCRS)) {
|
|
11923
12041
|
displayArcs = projectArcsForDisplay(dataset.arcs, sourceCRS, displayCRS);
|
|
@@ -11927,7 +12045,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
11927
12045
|
}
|
|
11928
12046
|
|
|
11929
12047
|
enhanceArcCollectionForDisplay(displayArcs);
|
|
11930
|
-
dataset.
|
|
12048
|
+
dataset.gui = {displayArcs}; // stash these in the dataset for other layers to use
|
|
11931
12049
|
}
|
|
11932
12050
|
|
|
11933
12051
|
if (internal.layerHasFurniture(layer)) {
|
|
@@ -11939,14 +12057,14 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
11939
12057
|
// treating furniture layers (other than frame) as tabular for now,
|
|
11940
12058
|
// so there is something to show if they are selected
|
|
11941
12059
|
// obj.tabular = obj.furniture_type != 'frame';
|
|
11942
|
-
} else if (
|
|
11943
|
-
obj.layer = {shapes: []}; // ideally we should avoid empty layers
|
|
11944
|
-
} else if (!layer.geometry_type) {
|
|
11945
|
-
obj.tabular = true;
|
|
11946
|
-
} else {
|
|
12060
|
+
} else if (layer.geometry_type) {
|
|
11947
12061
|
obj.geographic = true;
|
|
11948
12062
|
obj.layer = layer;
|
|
11949
12063
|
obj.arcs = displayArcs;
|
|
12064
|
+
} else if (!obj.empty) {
|
|
12065
|
+
obj.tabular = true;
|
|
12066
|
+
} else {
|
|
12067
|
+
obj.layer = {shapes: []}; // ideally we should avoid empty layers
|
|
11950
12068
|
}
|
|
11951
12069
|
|
|
11952
12070
|
if (obj.tabular) {
|
|
@@ -12371,13 +12489,13 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12371
12489
|
updateFullBounds();
|
|
12372
12490
|
};
|
|
12373
12491
|
|
|
12374
|
-
function getGlobalStyleOptions() {
|
|
12492
|
+
function getGlobalStyleOptions(opts) {
|
|
12375
12493
|
var mode = gui.state.interaction_mode;
|
|
12376
|
-
return {
|
|
12494
|
+
return Object.assign({
|
|
12377
12495
|
darkMode: !!gui.state.dark_basemap,
|
|
12378
12496
|
outlineMode: mode == 'vertices',
|
|
12379
12497
|
interactionMode: mode
|
|
12380
|
-
};
|
|
12498
|
+
}, opts);
|
|
12381
12499
|
}
|
|
12382
12500
|
|
|
12383
12501
|
// Refresh map display in response to data changes, layer selection, etc.
|
|
@@ -12392,7 +12510,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12392
12510
|
|
|
12393
12511
|
if (arcsMayHaveChanged(e.flags)) {
|
|
12394
12512
|
// regenerate filtered arcs the next time they are needed for rendering
|
|
12395
|
-
// delete e.dataset.displayArcs
|
|
12513
|
+
// delete e.dataset.gui.displayArcs
|
|
12396
12514
|
clearAllDisplayArcs();
|
|
12397
12515
|
|
|
12398
12516
|
// reset simplification after projection (thresholds have changed)
|
|
@@ -12490,7 +12608,8 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12490
12608
|
}
|
|
12491
12609
|
|
|
12492
12610
|
function getStrictBounds() {
|
|
12493
|
-
if (internal.isWebMercator(map.getDisplayCRS())) {
|
|
12611
|
+
// if (internal.isWebMercator(map.getDisplayCRS())) {
|
|
12612
|
+
if (_dynamicCRS && internal.isWebMercator(map.getDisplayCRS())) {
|
|
12494
12613
|
return getMapboxBounds();
|
|
12495
12614
|
}
|
|
12496
12615
|
return null;
|
|
@@ -12568,7 +12687,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12568
12687
|
|
|
12569
12688
|
function clearAllDisplayArcs() {
|
|
12570
12689
|
model.getDatasets().forEach(function(o) {
|
|
12571
|
-
delete o.
|
|
12690
|
+
delete o.gui;
|
|
12572
12691
|
});
|
|
12573
12692
|
}
|
|
12574
12693
|
|