mapshaper 0.6.74 → 0.6.76
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 +122 -84
- package/package.json +1 -1
- package/www/index.html +21 -25
- package/www/mapshaper-gui.js +671 -577
- package/www/mapshaper.js +122 -84
- package/www/page.css +119 -51
package/www/mapshaper-gui.js
CHANGED
|
@@ -760,10 +760,7 @@
|
|
|
760
760
|
//
|
|
761
761
|
//El.prototype.__domevents = utils.arrayToIndex("click,mousedown,mousemove,mouseup".split(','));
|
|
762
762
|
El.prototype.__on = El.prototype.on;
|
|
763
|
-
El.prototype.on = function(type, func
|
|
764
|
-
if (ctx) {
|
|
765
|
-
error$1("[El#on()] Third argument no longer supported.");
|
|
766
|
-
}
|
|
763
|
+
El.prototype.on = function(type, func) {
|
|
767
764
|
if (this.constructor == El) {
|
|
768
765
|
this.el.addEventListener(type, func);
|
|
769
766
|
} else {
|
|
@@ -1144,10 +1141,18 @@
|
|
|
1144
1141
|
return self;
|
|
1145
1142
|
};
|
|
1146
1143
|
|
|
1147
|
-
self.close = function() {
|
|
1148
|
-
|
|
1144
|
+
self.close = function(action) {
|
|
1145
|
+
var ms = 0;
|
|
1146
|
+
var _el = el;
|
|
1147
|
+
if (action == 'fade' && _el) {
|
|
1148
|
+
ms = 1000;
|
|
1149
|
+
_el.addClass('fade-out');
|
|
1150
|
+
}
|
|
1149
1151
|
if (_close) _close();
|
|
1150
1152
|
el = _cancel = _close = null;
|
|
1153
|
+
setTimeout(function() {
|
|
1154
|
+
if (_el) _el.remove();
|
|
1155
|
+
}, ms);
|
|
1151
1156
|
};
|
|
1152
1157
|
return self;
|
|
1153
1158
|
}
|
|
@@ -1361,10 +1366,12 @@
|
|
|
1361
1366
|
init();
|
|
1362
1367
|
|
|
1363
1368
|
async function init() {
|
|
1364
|
-
var enabled = await isStorageEnabled();
|
|
1365
|
-
if (!enabled) return;
|
|
1366
|
-
|
|
1367
1369
|
btn = gui.buttons.addButton('#ribbon-icon').addClass('menu-btn save-btn');
|
|
1370
|
+
var enabled = await isStorageEnabled();
|
|
1371
|
+
if (!enabled) {
|
|
1372
|
+
btn.remove();
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1368
1375
|
menu = El('div').addClass('nav-sub-menu save-menu').appendTo(btn.node());
|
|
1369
1376
|
await initialCleanup();
|
|
1370
1377
|
|
|
@@ -1436,14 +1443,6 @@
|
|
|
1436
1443
|
renderMenu();
|
|
1437
1444
|
}).text('remove');
|
|
1438
1445
|
});
|
|
1439
|
-
|
|
1440
|
-
// if (snapshots.length >= 1) {
|
|
1441
|
-
// addMenuLink({
|
|
1442
|
-
// slug: 'clear',
|
|
1443
|
-
// label: 'remove all',
|
|
1444
|
-
// action: clearData
|
|
1445
|
-
// });
|
|
1446
|
-
// }
|
|
1447
1446
|
}
|
|
1448
1447
|
|
|
1449
1448
|
function addMenuLink(item) {
|
|
@@ -1753,6 +1752,9 @@
|
|
|
1753
1752
|
}],
|
|
1754
1753
|
info: {}
|
|
1755
1754
|
};
|
|
1755
|
+
if (type == 'polygon' || type == 'point') {
|
|
1756
|
+
dataset.arcs = new internal.ArcCollection();
|
|
1757
|
+
}
|
|
1756
1758
|
if (crsInfo) {
|
|
1757
1759
|
internal.setDatasetCrsInfo(dataset, crsInfo);
|
|
1758
1760
|
}
|
|
@@ -2426,7 +2428,10 @@
|
|
|
2426
2428
|
}
|
|
2427
2429
|
|
|
2428
2430
|
function error() {
|
|
2429
|
-
|
|
2431
|
+
var msg = GUI.formatMessageArgs(arguments);
|
|
2432
|
+
console.error(msg);
|
|
2433
|
+
gui.alert('An unkown error occured');
|
|
2434
|
+
throw new Error(msg);
|
|
2430
2435
|
}
|
|
2431
2436
|
|
|
2432
2437
|
function message() {
|
|
@@ -2540,7 +2545,7 @@
|
|
|
2540
2545
|
// Convert a point from display CRS coordinates to data coordinates.
|
|
2541
2546
|
// These are only different when using dynamic reprojection (basemap view).
|
|
2542
2547
|
function translateDisplayPoint(lyr, p) {
|
|
2543
|
-
return isProjectedLayer(lyr) ? lyr.invertPoint(p[0], p[1]) : p;
|
|
2548
|
+
return isProjectedLayer(lyr) ? lyr.gui.invertPoint(p[0], p[1]) : p;
|
|
2544
2549
|
}
|
|
2545
2550
|
|
|
2546
2551
|
// bbox: display coords
|
|
@@ -2557,7 +2562,7 @@
|
|
|
2557
2562
|
|
|
2558
2563
|
function isProjectedLayer(lyr) {
|
|
2559
2564
|
// TODO: could do some validation on the layer's contents
|
|
2560
|
-
return !!
|
|
2565
|
+
return !!lyr.gui.invertPoint;
|
|
2561
2566
|
}
|
|
2562
2567
|
|
|
2563
2568
|
var darkStroke = "#334",
|
|
@@ -2807,7 +2812,7 @@
|
|
|
2807
2812
|
strokeWidth: 1.2,
|
|
2808
2813
|
vertices: true,
|
|
2809
2814
|
vertex_overlay_color: o.hit_type == 'vertex' ? violet : black,
|
|
2810
|
-
vertex_overlay_scale: o.hit_type == 'vertex' ? 2.
|
|
2815
|
+
vertex_overlay_scale: o.hit_type == 'vertex' ? 2.5 : 2,
|
|
2811
2816
|
vertex_overlay: o.hit_coordinates || null,
|
|
2812
2817
|
selected_points: o.selected_points || null,
|
|
2813
2818
|
fillColor: null
|
|
@@ -2919,22 +2924,22 @@
|
|
|
2919
2924
|
}
|
|
2920
2925
|
|
|
2921
2926
|
function flattenArcs(lyr) {
|
|
2922
|
-
lyr.source.dataset.arcs.flatten();
|
|
2927
|
+
lyr.gui.source.dataset.arcs.flatten();
|
|
2923
2928
|
if (isProjectedLayer(lyr)) {
|
|
2924
|
-
lyr.
|
|
2929
|
+
lyr.gui.displayArcs.flatten();
|
|
2925
2930
|
}
|
|
2926
2931
|
}
|
|
2927
2932
|
|
|
2928
2933
|
function setZ(lyr, z) {
|
|
2929
|
-
lyr.source.dataset.arcs.setRetainedInterval(z);
|
|
2934
|
+
lyr.gui.source.dataset.arcs.setRetainedInterval(z);
|
|
2930
2935
|
if (isProjectedLayer(lyr)) {
|
|
2931
|
-
lyr.
|
|
2936
|
+
lyr.gui.displayArcs.setRetainedInterval(z);
|
|
2932
2937
|
}
|
|
2933
2938
|
}
|
|
2934
2939
|
|
|
2935
2940
|
function updateZ(lyr) {
|
|
2936
|
-
if (isProjectedLayer(lyr) && !lyr.source.dataset.arcs.isFlat()) {
|
|
2937
|
-
lyr.
|
|
2941
|
+
if (isProjectedLayer(lyr) && !lyr.gui.source.dataset.arcs.isFlat()) {
|
|
2942
|
+
lyr.gui.displayArcs.setThresholds(lyr.gui.source.dataset.arcs.getVertexData().zz);
|
|
2938
2943
|
}
|
|
2939
2944
|
}
|
|
2940
2945
|
|
|
@@ -2970,103 +2975,107 @@
|
|
|
2970
2975
|
}
|
|
2971
2976
|
|
|
2972
2977
|
function deleteLastPath(lyr) {
|
|
2973
|
-
var arcId = lyr.
|
|
2974
|
-
if (lyr.
|
|
2975
|
-
lyr.
|
|
2978
|
+
var arcId = lyr.gui.displayArcs.size() - 1;
|
|
2979
|
+
if (lyr.data) {
|
|
2980
|
+
lyr.data.getRecords().pop();
|
|
2976
2981
|
}
|
|
2977
|
-
var shp = lyr.
|
|
2978
|
-
internal.deleteLastArc(lyr.
|
|
2982
|
+
var shp = lyr.shapes.pop();
|
|
2983
|
+
internal.deleteLastArc(lyr.gui.displayArcs);
|
|
2979
2984
|
if (isProjectedLayer(lyr)) {
|
|
2980
|
-
internal.deleteLastArc(lyr.source.dataset.arcs);
|
|
2985
|
+
internal.deleteLastArc(lyr.gui.source.dataset.arcs);
|
|
2981
2986
|
}
|
|
2982
2987
|
}
|
|
2983
2988
|
|
|
2984
2989
|
// p1, p2: two points in source data CRS coords.
|
|
2985
2990
|
function appendNewPath(lyr, p1, p2) {
|
|
2986
|
-
var arcId = lyr.
|
|
2987
|
-
internal.appendEmptyArc(lyr.
|
|
2988
|
-
lyr.
|
|
2991
|
+
var arcId = lyr.gui.displayArcs.size();
|
|
2992
|
+
internal.appendEmptyArc(lyr.gui.displayArcs);
|
|
2993
|
+
lyr.shapes.push([[arcId]]);
|
|
2989
2994
|
if (isProjectedLayer(lyr)) {
|
|
2990
|
-
|
|
2991
|
-
internal.appendEmptyArc(lyr.source.dataset.arcs);
|
|
2995
|
+
internal.appendEmptyArc(lyr.gui.source.dataset.arcs);
|
|
2992
2996
|
}
|
|
2993
2997
|
appendVertex$1(lyr, p1);
|
|
2994
2998
|
appendVertex$1(lyr, p2);
|
|
2995
|
-
appendNewDataRecord(lyr
|
|
2999
|
+
appendNewDataRecord(lyr);
|
|
2996
3000
|
}
|
|
2997
3001
|
|
|
2998
3002
|
// p: point in source data CRS coords.
|
|
2999
3003
|
function insertVertex$1(lyr, id, p) {
|
|
3000
|
-
internal.insertVertex(lyr.source.dataset.arcs, id, p);
|
|
3004
|
+
internal.insertVertex(lyr.gui.source.dataset.arcs, id, p);
|
|
3001
3005
|
if (isProjectedLayer(lyr)) {
|
|
3002
|
-
internal.insertVertex(lyr.
|
|
3006
|
+
internal.insertVertex(lyr.gui.displayArcs, id, lyr.gui.projectPoint(p[0], p[1]));
|
|
3003
3007
|
}
|
|
3004
3008
|
}
|
|
3005
3009
|
|
|
3006
3010
|
function appendVertex$1(lyr, p) {
|
|
3007
|
-
var n = lyr.source.dataset.arcs.getPointCount();
|
|
3011
|
+
var n = lyr.gui.source.dataset.arcs.getPointCount();
|
|
3008
3012
|
insertVertex$1(lyr, n, p);
|
|
3009
3013
|
}
|
|
3010
3014
|
|
|
3011
3015
|
// TODO: make sure we're not also removing an entire arc
|
|
3012
3016
|
function deleteLastVertex(lyr) {
|
|
3013
|
-
deleteVertex$1(lyr, lyr.
|
|
3017
|
+
deleteVertex$1(lyr, lyr.gui.displayArcs.getPointCount() - 1);
|
|
3014
3018
|
}
|
|
3015
3019
|
|
|
3016
3020
|
function deleteVertex$1(lyr, id) {
|
|
3017
|
-
internal.deleteVertex(lyr.
|
|
3021
|
+
internal.deleteVertex(lyr.gui.displayArcs, id);
|
|
3018
3022
|
if (isProjectedLayer(lyr)) {
|
|
3019
|
-
internal.deleteVertex(lyr.source.dataset.arcs, id);
|
|
3023
|
+
internal.deleteVertex(lyr.gui.source.dataset.arcs, id);
|
|
3020
3024
|
}
|
|
3021
3025
|
}
|
|
3022
3026
|
|
|
3023
3027
|
function getLastArcCoords(target) {
|
|
3024
|
-
var arcId = target.source.dataset.arcs.size() - 1;
|
|
3025
|
-
return internal.getUnfilteredArcCoords(arcId, target.source.dataset.arcs);
|
|
3028
|
+
var arcId = target.gui.source.dataset.arcs.size() - 1;
|
|
3029
|
+
return internal.getUnfilteredArcCoords(arcId, target.gui.source.dataset.arcs);
|
|
3030
|
+
}
|
|
3031
|
+
|
|
3032
|
+
function getLastVertexCoords(target) {
|
|
3033
|
+
var arcs = target.gui.source.dataset.arcs;
|
|
3034
|
+
return internal.getVertexCoords(arcs.getPointCount() - 1, arcs);
|
|
3026
3035
|
}
|
|
3027
3036
|
|
|
3028
3037
|
function getLastArcLength(target) {
|
|
3029
|
-
var arcId = target.source.dataset.arcs.size() - 1;
|
|
3030
|
-
return internal.getUnfilteredArcLength(arcId, target.source.dataset.arcs);
|
|
3038
|
+
var arcId = target.gui.source.dataset.arcs.size() - 1;
|
|
3039
|
+
return internal.getUnfilteredArcLength(arcId, target.gui.source.dataset.arcs);
|
|
3031
3040
|
}
|
|
3032
3041
|
|
|
3033
3042
|
function getPointCoords(lyr, fid) {
|
|
3034
|
-
return internal.cloneShape(lyr.
|
|
3043
|
+
return internal.cloneShape(lyr.shapes[fid]);
|
|
3035
3044
|
}
|
|
3036
3045
|
|
|
3037
3046
|
function getVertexCoords(lyr, id) {
|
|
3038
|
-
return lyr.source.dataset.arcs.getVertex2(id);
|
|
3047
|
+
return lyr.gui.source.dataset.arcs.getVertex2(id);
|
|
3039
3048
|
}
|
|
3040
3049
|
|
|
3041
3050
|
// set data coords (not display coords) of one or more vertices.
|
|
3042
3051
|
function setVertexCoords(lyr, ids, dataPoint) {
|
|
3043
|
-
internal.snapVerticesToPoint(ids, dataPoint, lyr.source.dataset.arcs
|
|
3052
|
+
internal.snapVerticesToPoint(ids, dataPoint, lyr.gui.source.dataset.arcs);
|
|
3044
3053
|
if (isProjectedLayer(lyr)) {
|
|
3045
|
-
var p = lyr.projectPoint(dataPoint[0], dataPoint[1]);
|
|
3046
|
-
internal.snapVerticesToPoint(ids, p, lyr.
|
|
3054
|
+
var p = lyr.gui.projectPoint(dataPoint[0], dataPoint[1]);
|
|
3055
|
+
internal.snapVerticesToPoint(ids, p, lyr.gui.displayArcs);
|
|
3047
3056
|
}
|
|
3048
3057
|
}
|
|
3049
3058
|
|
|
3050
3059
|
// coords: [x, y] point in data CRS (not display CRS)
|
|
3051
3060
|
function setPointCoords(lyr, fid, coords) {
|
|
3052
|
-
lyr.
|
|
3061
|
+
lyr.shapes[fid] = coords;
|
|
3053
3062
|
if (isProjectedLayer(lyr)) {
|
|
3054
|
-
lyr.
|
|
3063
|
+
lyr.shapes[fid] = projectPointCoords(coords, lyr.gui.projectPoint);
|
|
3055
3064
|
}
|
|
3056
3065
|
}
|
|
3057
3066
|
|
|
3058
3067
|
function updateVertexCoords(lyr, ids) {
|
|
3059
3068
|
if (!isProjectedLayer(lyr)) return;
|
|
3060
|
-
var p = lyr.
|
|
3061
|
-
internal.snapVerticesToPoint(ids, lyr.invertPoint(p[0], p[1]), lyr.source.dataset.arcs
|
|
3069
|
+
var p = lyr.gui.displayArcs.getVertex2(ids[0]);
|
|
3070
|
+
internal.snapVerticesToPoint(ids, lyr.gui.invertPoint(p[0], p[1]), lyr.gui.source.dataset.arcs);
|
|
3062
3071
|
}
|
|
3063
3072
|
|
|
3064
3073
|
function setRectangleCoords(lyr, ids, coords) {
|
|
3065
3074
|
ids.forEach(function(id, i) {
|
|
3066
3075
|
var p = coords[i];
|
|
3067
|
-
internal.snapVerticesToPoint([id], p, lyr.source.dataset.arcs
|
|
3076
|
+
internal.snapVerticesToPoint([id], p, lyr.gui.source.dataset.arcs);
|
|
3068
3077
|
if (isProjectedLayer(lyr)) {
|
|
3069
|
-
internal.snapVerticesToPoint([id], lyr.projectPoint(p[0], p[1]), lyr.
|
|
3078
|
+
internal.snapVerticesToPoint([id], lyr.gui.projectPoint(p[0], p[1]), lyr.gui.displayArcs);
|
|
3070
3079
|
}
|
|
3071
3080
|
});
|
|
3072
3081
|
}
|
|
@@ -3074,8 +3083,8 @@
|
|
|
3074
3083
|
// Update source data coordinates by projecting display coordinates
|
|
3075
3084
|
function updatePointCoords(lyr, fid) {
|
|
3076
3085
|
if (!isProjectedLayer(lyr)) return;
|
|
3077
|
-
var displayShp = lyr.
|
|
3078
|
-
lyr.
|
|
3086
|
+
var displayShp = lyr.shapes[fid];
|
|
3087
|
+
lyr.shapes[fid] = projectPointCoords(displayShp, lyr.gui.invertPoint);
|
|
3079
3088
|
}
|
|
3080
3089
|
|
|
3081
3090
|
function projectPointCoords(src, proj) {
|
|
@@ -4212,6 +4221,11 @@
|
|
|
4212
4221
|
return layers.reverse();
|
|
4213
4222
|
}
|
|
4214
4223
|
|
|
4224
|
+
async function saveFileContentToClipboard(content) {
|
|
4225
|
+
var str = utils$1.isString(content) ? content : content.toString();
|
|
4226
|
+
await navigator.clipboard.writeText(str);
|
|
4227
|
+
}
|
|
4228
|
+
|
|
4215
4229
|
// Export buttons and their behavior
|
|
4216
4230
|
var ExportControl = function(gui) {
|
|
4217
4231
|
var model = gui.model;
|
|
@@ -4355,12 +4369,6 @@
|
|
|
4355
4369
|
} else {
|
|
4356
4370
|
await utils$1.promisify(internal.writeFiles)(files, opts);
|
|
4357
4371
|
}
|
|
4358
|
-
|
|
4359
|
-
}
|
|
4360
|
-
|
|
4361
|
-
async function saveFileContentToClipboard(content) {
|
|
4362
|
-
var str = utils$1.isString(content) ? content : content.toString();
|
|
4363
|
-
await navigator.clipboard.writeText(str);
|
|
4364
4372
|
}
|
|
4365
4373
|
|
|
4366
4374
|
function initLayerItem(o, i) {
|
|
@@ -4920,13 +4928,10 @@
|
|
|
4920
4928
|
return internal.getLayerSourceFile(lyr, dataset);
|
|
4921
4929
|
}
|
|
4922
4930
|
|
|
4923
|
-
|
|
4924
4931
|
function isPinnable(lyr) {
|
|
4925
|
-
return internal.
|
|
4932
|
+
return internal.layerIsGeometric(lyr) || internal.layerHasFurniture(lyr);
|
|
4926
4933
|
}
|
|
4927
4934
|
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
4935
|
function rowHTML(c1, c2, cname) {
|
|
4931
4936
|
return utils$1.format('<div class="row%s"><div class="col1">%s</div>' +
|
|
4932
4937
|
'<div class="col2">%s</div></div>', cname ? ' ' + cname : '', c1, c2);
|
|
@@ -5205,10 +5210,10 @@
|
|
|
5205
5210
|
|
|
5206
5211
|
gui.on('path_extend', function(e) {
|
|
5207
5212
|
var redo = function() {
|
|
5208
|
-
gui.dispatchEvent('redo_path_extend', {p: e.p});
|
|
5213
|
+
gui.dispatchEvent('redo_path_extend', {p: e.p, shapes: e.shapes2});
|
|
5209
5214
|
};
|
|
5210
5215
|
var undo = function() {
|
|
5211
|
-
gui.dispatchEvent('undo_path_extend');
|
|
5216
|
+
gui.dispatchEvent('undo_path_extend', {shapes: e.shapes1});
|
|
5212
5217
|
};
|
|
5213
5218
|
addHistoryState(undo, redo);
|
|
5214
5219
|
});
|
|
@@ -5381,19 +5386,23 @@
|
|
|
5381
5386
|
function KeyboardEvents(gui) {
|
|
5382
5387
|
var self = this;
|
|
5383
5388
|
var shiftDown = false;
|
|
5389
|
+
var ctrlDown = false;
|
|
5384
5390
|
document.addEventListener('keyup', function(e) {
|
|
5385
5391
|
if (!GUI.isActiveInstance(gui)) return;
|
|
5386
5392
|
if (e.keyCode == 16) shiftDown = false;
|
|
5393
|
+
if (e.keyCode == 17) ctrlDown = false;
|
|
5387
5394
|
self.dispatchEvent('keyup', getEventData(e));
|
|
5388
5395
|
});
|
|
5389
5396
|
|
|
5390
5397
|
document.addEventListener('keydown', function(e) {
|
|
5391
5398
|
if (!GUI.isActiveInstance(gui)) return;
|
|
5392
5399
|
if (e.keyCode == 16) shiftDown = true;
|
|
5400
|
+
if (e.keyCode == 17) ctrlDown = true;
|
|
5393
5401
|
self.dispatchEvent('keydown', getEventData(e));
|
|
5394
5402
|
});
|
|
5395
5403
|
|
|
5396
5404
|
this.shiftIsPressed = function() { return shiftDown; };
|
|
5405
|
+
this.ctrlIsPressed = function() { return ctrlDown; };
|
|
5397
5406
|
|
|
5398
5407
|
this.onMenuSubmit = function(menuEl, cb) {
|
|
5399
5408
|
gui.on('enter_key', function(e) {
|
|
@@ -5410,6 +5419,7 @@
|
|
|
5410
5419
|
9: 'tab',
|
|
5411
5420
|
13: 'enter',
|
|
5412
5421
|
16: 'shift',
|
|
5422
|
+
17: 'ctrl',
|
|
5413
5423
|
27: 'esc',
|
|
5414
5424
|
32: 'space',
|
|
5415
5425
|
37: 'left',
|
|
@@ -5431,7 +5441,7 @@
|
|
|
5431
5441
|
var menus = {
|
|
5432
5442
|
standard: ['info', 'selection', 'box'],
|
|
5433
5443
|
polygons: ['info', 'selection', 'box', 'drawing'],
|
|
5434
|
-
rectangles: ['info', 'selection', 'box', 'rectangles'],
|
|
5444
|
+
rectangles: ['info', 'selection', 'box', 'rectangles'],
|
|
5435
5445
|
lines: ['info', 'selection', 'box' , 'drawing'],
|
|
5436
5446
|
table: ['info', 'selection'],
|
|
5437
5447
|
labels: ['info', 'selection', 'box', 'labels', 'location', 'add-points'],
|
|
@@ -5452,7 +5462,7 @@
|
|
|
5452
5462
|
labels: 'position labels',
|
|
5453
5463
|
location: 'drag points',
|
|
5454
5464
|
vertices: 'edit vertices',
|
|
5455
|
-
selection: '
|
|
5465
|
+
selection: 'selection tool',
|
|
5456
5466
|
'add-points': 'add points',
|
|
5457
5467
|
drawing: 'draw/reshape tool',
|
|
5458
5468
|
rectangles: 'drag-to-resize',
|
|
@@ -5543,22 +5553,23 @@
|
|
|
5543
5553
|
if (!o || !o.layer) {
|
|
5544
5554
|
return menus.standard; // TODO: more sensible handling of missing layer
|
|
5545
5555
|
}
|
|
5546
|
-
if (!
|
|
5556
|
+
if (!o.layer.geometry_type) {
|
|
5547
5557
|
return menus.table;
|
|
5548
5558
|
}
|
|
5549
5559
|
if (internal.layerHasLabels(o.layer)) {
|
|
5550
5560
|
return menus.labels;
|
|
5551
5561
|
}
|
|
5552
|
-
if (
|
|
5562
|
+
if (o.layer.geometry_type == 'point') {
|
|
5553
5563
|
return menus.points;
|
|
5554
5564
|
}
|
|
5555
|
-
if (
|
|
5565
|
+
if (o.layer.geometry_type == 'polyline') {
|
|
5556
5566
|
return menus.lines;
|
|
5557
5567
|
}
|
|
5558
|
-
if (
|
|
5568
|
+
if (o.layer.geometry_type == 'polygon') {
|
|
5559
5569
|
return internal.layerOnlyHasRectangles(o.layer, o.dataset.arcs) ?
|
|
5560
5570
|
menus.rectangles : menus.polygons;
|
|
5561
5571
|
}
|
|
5572
|
+
|
|
5562
5573
|
return menus.standard;
|
|
5563
5574
|
}
|
|
5564
5575
|
|
|
@@ -5908,13 +5919,13 @@
|
|
|
5908
5919
|
}
|
|
5909
5920
|
|
|
5910
5921
|
// Format an array of (preferably short) strings in columns for console logging.
|
|
5911
|
-
function formatStringsAsGrid(arr) {
|
|
5922
|
+
function formatStringsAsGrid(arr, width) {
|
|
5912
5923
|
// TODO: variable column width
|
|
5913
5924
|
var longest = arr.reduce(function(len, str) {
|
|
5914
5925
|
return Math.max(len, str.length);
|
|
5915
5926
|
}, 0),
|
|
5916
5927
|
colWidth = longest + 2,
|
|
5917
|
-
perLine = Math.floor(80 / colWidth) || 1;
|
|
5928
|
+
perLine = Math.floor((width || 80) / colWidth) || 1;
|
|
5918
5929
|
return arr.reduce(function(memo, name, i) {
|
|
5919
5930
|
var col = i % perLine;
|
|
5920
5931
|
if (i > 0 && col === 0) memo += '\n';
|
|
@@ -6575,7 +6586,7 @@
|
|
|
6575
6586
|
}
|
|
6576
6587
|
|
|
6577
6588
|
function getGenericComparator(asc) {
|
|
6578
|
-
asc = asc !== false;
|
|
6589
|
+
asc = asc !== false && asc != 'descending'; // ascending is the default
|
|
6579
6590
|
return function(a, b) {
|
|
6580
6591
|
var retn = 0;
|
|
6581
6592
|
if (b == null) {
|
|
@@ -7244,11 +7255,11 @@
|
|
|
7244
7255
|
|
|
7245
7256
|
// featureFilter: optional test function, accepts feature id
|
|
7246
7257
|
//
|
|
7247
|
-
function getShapeHitTest(
|
|
7248
|
-
var geoType = displayLayer.
|
|
7258
|
+
function getShapeHitTest(layer, ext, interactionMode, featureFilter) {
|
|
7259
|
+
var geoType = layer.gui.displayLayer.geometry_type;
|
|
7249
7260
|
var test;
|
|
7250
|
-
if (geoType == 'point' &&
|
|
7251
|
-
test = getGraduatedCircleTest(getRadiusFunction(
|
|
7261
|
+
if (geoType == 'point' && layer.gui.style.type == 'styled') {
|
|
7262
|
+
test = getGraduatedCircleTest(getRadiusFunction(layer.gui.style));
|
|
7252
7263
|
} else if (geoType == 'point') {
|
|
7253
7264
|
test = pointTest;
|
|
7254
7265
|
} else if (interactionMode == 'drawing' && geoType == 'polygon') {
|
|
@@ -7288,13 +7299,13 @@
|
|
|
7288
7299
|
cand, hitId;
|
|
7289
7300
|
for (var i=0; i<cands.length; i++) {
|
|
7290
7301
|
cand = cands[i];
|
|
7291
|
-
if (geom.testPointInPolygon(x, y, cand.shape,
|
|
7302
|
+
if (geom.testPointInPolygon(x, y, cand.shape, layer.gui.displayArcs)) {
|
|
7292
7303
|
hits.push(cand);
|
|
7293
7304
|
}
|
|
7294
7305
|
}
|
|
7295
7306
|
if (cands.length > 0 && hits.length === 0) {
|
|
7296
7307
|
// secondary detection: proximity, if not inside a polygon
|
|
7297
|
-
sortByDistance(x, y, cands,
|
|
7308
|
+
sortByDistance(x, y, cands, layer.gui.displayArcs);
|
|
7298
7309
|
hits = pickNearestCandidates(cands, 0, maxDist);
|
|
7299
7310
|
}
|
|
7300
7311
|
return {
|
|
@@ -7319,7 +7330,7 @@
|
|
|
7319
7330
|
bufPix = bufArg >= 0 ? bufArg : 0.05, // tiny threshold for hitting almost-identical lines
|
|
7320
7331
|
bufDist = getZoomAdjustedHitBuffer(bufPix),
|
|
7321
7332
|
cands = findHitCandidates(x, y, maxDist);
|
|
7322
|
-
sortByDistance(x, y, cands,
|
|
7333
|
+
sortByDistance(x, y, cands, layer.gui.displayArcs);
|
|
7323
7334
|
cands = pickNearestCandidates(cands, bufDist, maxDist);
|
|
7324
7335
|
return {
|
|
7325
7336
|
ids: utils$1.pluck(cands, 'id')
|
|
@@ -7360,7 +7371,7 @@
|
|
|
7360
7371
|
hits = [];
|
|
7361
7372
|
|
|
7362
7373
|
// inlining forEachPoint() does not not appreciably speed this up
|
|
7363
|
-
internal.forEachPoint(displayLayer.
|
|
7374
|
+
internal.forEachPoint(layer.gui.displayLayer.shapes, function(p, id) {
|
|
7364
7375
|
var dist = geom.distance2D(x, y, p[0], p[1]) * toPx;
|
|
7365
7376
|
if (dist > hitThreshold) return;
|
|
7366
7377
|
if (dist < hitThreshold && hitThreshold > bullseyeDist) {
|
|
@@ -7394,7 +7405,7 @@
|
|
|
7394
7405
|
directHit = false,
|
|
7395
7406
|
hitRadius = 0,
|
|
7396
7407
|
hitDist;
|
|
7397
|
-
internal.forEachPoint(displayLayer.
|
|
7408
|
+
internal.forEachPoint(layer.gui.displayLayer.shapes, function(p, id) {
|
|
7398
7409
|
var distSq = geom.distanceSq(x, y, p[0], p[1]);
|
|
7399
7410
|
var isHit = false;
|
|
7400
7411
|
var isOver, isNear, r, d, rpix;
|
|
@@ -7434,11 +7445,11 @@
|
|
|
7434
7445
|
|
|
7435
7446
|
// Returns array of shape ids for shapes that pass a buffered bounding-box test
|
|
7436
7447
|
function findHitCandidates(x, y, dist) {
|
|
7437
|
-
var arcs =
|
|
7448
|
+
var arcs = layer.gui.displayArcs,
|
|
7438
7449
|
index = {},
|
|
7439
7450
|
cands = [],
|
|
7440
7451
|
bbox = [];
|
|
7441
|
-
displayLayer.
|
|
7452
|
+
layer.gui.displayLayer.shapes.forEach(function(shp, shpId) {
|
|
7442
7453
|
var cand;
|
|
7443
7454
|
if (featureFilter && !featureFilter(shpId)) {
|
|
7444
7455
|
return;
|
|
@@ -7570,7 +7581,7 @@
|
|
|
7570
7581
|
|
|
7571
7582
|
function getPointerHitTest(mapLayer, ext, interactionMode, featureFilter) {
|
|
7572
7583
|
var shapeTest, targetLayer;
|
|
7573
|
-
if (!mapLayer || !internal.layerHasGeometry(mapLayer.
|
|
7584
|
+
if (!mapLayer || !internal.layerHasGeometry(mapLayer.gui?.displayLayer)) {
|
|
7574
7585
|
return function() {return {ids: []};};
|
|
7575
7586
|
}
|
|
7576
7587
|
shapeTest = getShapeHitTest(mapLayer, ext, interactionMode, featureFilter);
|
|
@@ -7609,6 +7620,13 @@
|
|
|
7609
7620
|
// pan navigation
|
|
7610
7621
|
var priority = 2;
|
|
7611
7622
|
|
|
7623
|
+
mouse.on('contextmenu', function(e) {
|
|
7624
|
+
if (isOverMap(e)) {
|
|
7625
|
+
e.originalEvent.preventDefault();
|
|
7626
|
+
}
|
|
7627
|
+
triggerHitEvent('contextmenu', e);
|
|
7628
|
+
}, false);
|
|
7629
|
+
|
|
7612
7630
|
// init keyboard controls for pinned features
|
|
7613
7631
|
gui.keyboard.on('keydown', function(evt) {
|
|
7614
7632
|
var e = evt.originalEvent;
|
|
@@ -7636,7 +7654,7 @@
|
|
|
7636
7654
|
// to help protect against inadvertent deletion, don't delete
|
|
7637
7655
|
// when console is open or a popup menu is open
|
|
7638
7656
|
if (!gui.getMode() && !gui.consoleIsOpen()) {
|
|
7639
|
-
internal.deleteFeatureById(targetLayer
|
|
7657
|
+
internal.deleteFeatureById(targetLayer, pinnedId());
|
|
7640
7658
|
self.clearSelection();
|
|
7641
7659
|
gui.model.updated({flags: 'filter'}); // signal map to update
|
|
7642
7660
|
}
|
|
@@ -7679,6 +7697,7 @@
|
|
|
7679
7697
|
|
|
7680
7698
|
function pinnable() {
|
|
7681
7699
|
return clickable() && !selectable();
|
|
7700
|
+
// return clickable();
|
|
7682
7701
|
}
|
|
7683
7702
|
|
|
7684
7703
|
function draggable() {
|
|
@@ -7773,7 +7792,7 @@
|
|
|
7773
7792
|
|
|
7774
7793
|
self.getTargetDataTable = function() {
|
|
7775
7794
|
var targ = self.getHitTarget();
|
|
7776
|
-
return targ
|
|
7795
|
+
return targ?.data || null;
|
|
7777
7796
|
};
|
|
7778
7797
|
|
|
7779
7798
|
// get function for selecting next or prev feature within the current set of
|
|
@@ -7786,7 +7805,7 @@
|
|
|
7786
7805
|
|
|
7787
7806
|
// diff: 1 or -1
|
|
7788
7807
|
function advanceSelectedFeature(diff) {
|
|
7789
|
-
var n = internal.getFeatureCount(targetLayer
|
|
7808
|
+
var n = internal.getFeatureCount(targetLayer);
|
|
7790
7809
|
if (n < 2 || pinnedId() == -1) return;
|
|
7791
7810
|
storedData.id = (pinnedId() + n + diff) % n;
|
|
7792
7811
|
storedData.ids = [storedData.id];
|
|
@@ -7831,12 +7850,15 @@
|
|
|
7831
7850
|
|
|
7832
7851
|
mouse.on('click', function(e) {
|
|
7833
7852
|
if (!hitTest || !active) return;
|
|
7853
|
+
if (!eventIsEnabled('click')) return;
|
|
7854
|
+
|
|
7834
7855
|
e.stopPropagation();
|
|
7835
7856
|
|
|
7836
7857
|
// TODO: move pinning to inspection control?
|
|
7837
7858
|
if (clickable()) {
|
|
7838
7859
|
updateSelectionState(convertClickDataToSelectionData(hitTest(e)));
|
|
7839
7860
|
}
|
|
7861
|
+
|
|
7840
7862
|
triggerHitEvent('click', e);
|
|
7841
7863
|
}, null, priority);
|
|
7842
7864
|
|
|
@@ -7855,6 +7877,7 @@
|
|
|
7855
7877
|
}
|
|
7856
7878
|
}, null, priority);
|
|
7857
7879
|
|
|
7880
|
+
|
|
7858
7881
|
function targetIsRollover(target) {
|
|
7859
7882
|
while (target.parentNode && target != target.parentNode) {
|
|
7860
7883
|
if (target.className && String(target.className).indexOf('rollover') > -1) {
|
|
@@ -7893,6 +7916,19 @@
|
|
|
7893
7916
|
return hitData;
|
|
7894
7917
|
}
|
|
7895
7918
|
|
|
7919
|
+
function mergeSelectionModeHoverData(hitData) {
|
|
7920
|
+
if (hitData.ids.length === 0 || selectionIds.includes(hitData.ids[0])) {
|
|
7921
|
+
hitData.ids = selectionIds;
|
|
7922
|
+
hitData.pinned = storedData.pinned;
|
|
7923
|
+
} else {
|
|
7924
|
+
//
|
|
7925
|
+
}
|
|
7926
|
+
|
|
7927
|
+
// kludge to inhibit hover effect while dragging a box
|
|
7928
|
+
if (gui.keydown) hitData.id = -1;
|
|
7929
|
+
return hitData;
|
|
7930
|
+
}
|
|
7931
|
+
|
|
7896
7932
|
function mergeHoverData(hitData) {
|
|
7897
7933
|
if (storedData.pinned) {
|
|
7898
7934
|
hitData.id = storedData.id;
|
|
@@ -7947,14 +7983,21 @@
|
|
|
7947
7983
|
return true; // special case -- using hover for line drawing animation
|
|
7948
7984
|
}
|
|
7949
7985
|
|
|
7986
|
+
if (type == 'click' && gui.keyboard.ctrlIsPressed()) {
|
|
7987
|
+
return false; // don't fire if context menu might open
|
|
7988
|
+
}
|
|
7989
|
+
if (type == 'click' && gui.contextMenu.isOpen()) {
|
|
7990
|
+
return false;
|
|
7991
|
+
}
|
|
7992
|
+
if (type == 'click' && interactionMode == 'drawing') {
|
|
7993
|
+
return true; // click events are triggered even if no shape is hit
|
|
7994
|
+
}
|
|
7995
|
+
|
|
7950
7996
|
// ignore pointer events when no features are being hit
|
|
7951
7997
|
// (don't block pan and other navigation when events aren't being used for editing)
|
|
7952
7998
|
var hitId = self.getHitId();
|
|
7953
7999
|
if (hitId == -1) return false;
|
|
7954
8000
|
|
|
7955
|
-
if (type == 'click' && !clickable()) {
|
|
7956
|
-
return false;
|
|
7957
|
-
}
|
|
7958
8001
|
if ((type == 'drag' || type == 'dragstart' || type == 'dragend') && !draggable()) {
|
|
7959
8002
|
return false;
|
|
7960
8003
|
}
|
|
@@ -7983,10 +8026,13 @@
|
|
|
7983
8026
|
// evt: event data (may be a pointer event object, an ordinary object or null)
|
|
7984
8027
|
function triggerHitEvent(type, evt) {
|
|
7985
8028
|
var eventData = {
|
|
7986
|
-
mode: interactionMode
|
|
7987
|
-
overMap: evt ? isOverMap(evt) : null,
|
|
7988
|
-
originalEvent: evt ? evt : null
|
|
8029
|
+
mode: interactionMode
|
|
7989
8030
|
};
|
|
8031
|
+
if (evt) {
|
|
8032
|
+
eventData.coordinates = translateDisplayPoint(targetLayer, ext.translatePixelCoords(evt.x, evt.y));
|
|
8033
|
+
eventData.originalEvent = evt;
|
|
8034
|
+
eventData.overMap = isOverMap(evt);
|
|
8035
|
+
}
|
|
7990
8036
|
// Merge stored hit data into the event data
|
|
7991
8037
|
utils$1.defaults(eventData, evt && evt.data || {}, storedData);
|
|
7992
8038
|
// utils.extend(eventData, storedData);
|
|
@@ -8033,7 +8079,7 @@
|
|
|
8033
8079
|
|
|
8034
8080
|
function enabled() {
|
|
8035
8081
|
var lyr = gui.map.getActiveLayer();
|
|
8036
|
-
return !!(lyr && lyr.
|
|
8082
|
+
return !!(lyr && lyr.gui.displayLayer.geometry_type);
|
|
8037
8083
|
}
|
|
8038
8084
|
|
|
8039
8085
|
gui.model.on('update', function(e) {
|
|
@@ -8078,7 +8124,7 @@
|
|
|
8078
8124
|
var p2 = gui.map.translatePixelCoords(ext.width(), 0);
|
|
8079
8125
|
var bbox = p1.concat(p2);
|
|
8080
8126
|
var decimals = internal.getBoundsPrecisionForDisplay(bbox);
|
|
8081
|
-
|
|
8127
|
+
var str = internal.getRoundedCoordString(p, decimals);
|
|
8082
8128
|
readout.text(str).show();
|
|
8083
8129
|
}
|
|
8084
8130
|
|
|
@@ -8359,6 +8405,9 @@
|
|
|
8359
8405
|
element.addEventListener('mouseout', onAreaOut);
|
|
8360
8406
|
element.addEventListener('mousedown', onAreaDown);
|
|
8361
8407
|
element.addEventListener('dblclick', onAreaDblClick);
|
|
8408
|
+
document.addEventListener('contextmenu', function(e) {
|
|
8409
|
+
_self.dispatchEvent('contextmenu', procMouseEvent(e));
|
|
8410
|
+
});
|
|
8362
8411
|
|
|
8363
8412
|
this.enable = function() {
|
|
8364
8413
|
if (!_disabled) return;
|
|
@@ -8926,8 +8975,8 @@
|
|
|
8926
8975
|
gui.on('interaction_mode_change', function(e) {
|
|
8927
8976
|
if (e.mode === 'selection') {
|
|
8928
8977
|
gui.enterMode('selection_tool');
|
|
8929
|
-
} else if (
|
|
8930
|
-
|
|
8978
|
+
} else if (_on) {
|
|
8979
|
+
turnOff();
|
|
8931
8980
|
}
|
|
8932
8981
|
});
|
|
8933
8982
|
|
|
@@ -9393,8 +9442,17 @@
|
|
|
9393
9442
|
}
|
|
9394
9443
|
});
|
|
9395
9444
|
|
|
9445
|
+
hit.on('contextmenu', function(e) {
|
|
9446
|
+
var target = hit.getHitTarget();
|
|
9447
|
+
if (!e.overMap || !target) return;
|
|
9448
|
+
|
|
9449
|
+
if (e.mode == 'drawing') return; // TODO: make special menu for this mode
|
|
9450
|
+
gui.contextMenu.open(e, hit.getHitTarget());
|
|
9451
|
+
});
|
|
9452
|
+
|
|
9396
9453
|
hit.on('change', function(e) {
|
|
9397
9454
|
if (!inspecting()) return;
|
|
9455
|
+
if (gui.keyboard.ctrlIsPressed()) return;
|
|
9398
9456
|
var ids;
|
|
9399
9457
|
if (e.mode == 'selection') {
|
|
9400
9458
|
ids = e.pinned && e.ids || [];
|
|
@@ -9407,8 +9465,8 @@
|
|
|
9407
9465
|
// id: Id of a feature in the active layer, or -1
|
|
9408
9466
|
function inspect(id, ids, pin) {
|
|
9409
9467
|
var target = hit.getHitTarget();
|
|
9410
|
-
if ((id > -1 || ids && ids.length > 0) && inspecting() && target && target
|
|
9411
|
-
_popup.show(id, ids, target
|
|
9468
|
+
if ((id > -1 || ids && ids.length > 0) && inspecting() && target && target) {
|
|
9469
|
+
_popup.show(id, ids, target, pin);
|
|
9412
9470
|
} else {
|
|
9413
9471
|
_popup.hide();
|
|
9414
9472
|
}
|
|
@@ -9512,15 +9570,20 @@
|
|
|
9512
9570
|
|
|
9513
9571
|
function initLabelDragging(gui, ext, hit) {
|
|
9514
9572
|
var downEvt;
|
|
9515
|
-
var activeId;
|
|
9573
|
+
var activeId = -1;
|
|
9574
|
+
var prevHitEvt;
|
|
9516
9575
|
var activeRecord;
|
|
9517
9576
|
|
|
9518
|
-
function active(
|
|
9519
|
-
return
|
|
9577
|
+
function active() {
|
|
9578
|
+
return gui.interaction.getMode() == 'labels';
|
|
9579
|
+
}
|
|
9580
|
+
|
|
9581
|
+
function labelSelected(e) {
|
|
9582
|
+
return e.id > -1 && active();
|
|
9520
9583
|
}
|
|
9521
9584
|
|
|
9522
9585
|
hit.on('dragstart', function(e) {
|
|
9523
|
-
if (!
|
|
9586
|
+
if (!labelSelected(e)) return;
|
|
9524
9587
|
var symNode = getSymbolTarget(e);
|
|
9525
9588
|
var table = hit.getTargetDataTable();
|
|
9526
9589
|
if (!symNode || !table) {
|
|
@@ -9533,8 +9596,25 @@
|
|
|
9533
9596
|
gui.dispatchEvent('label_dragstart', {FID: activeId});
|
|
9534
9597
|
});
|
|
9535
9598
|
|
|
9599
|
+
hit.on('change', function(e) {
|
|
9600
|
+
if (!active()) return;
|
|
9601
|
+
if (prevHitEvt) clearLabelHighlight(prevHitEvt);
|
|
9602
|
+
showLabelHighlight(e);
|
|
9603
|
+
prevHitEvt = e;
|
|
9604
|
+
});
|
|
9605
|
+
|
|
9606
|
+
function clearLabelHighlight(e) {
|
|
9607
|
+
var txt = getTextNode(getSymbolTarget(e));
|
|
9608
|
+
if (txt) txt.classList.remove('active-label');
|
|
9609
|
+
}
|
|
9610
|
+
|
|
9611
|
+
function showLabelHighlight(e) {
|
|
9612
|
+
var txt = getTextNode(getSymbolTarget(e));
|
|
9613
|
+
if (txt) txt.classList.add('active-label');
|
|
9614
|
+
}
|
|
9615
|
+
|
|
9536
9616
|
hit.on('drag', function(e) {
|
|
9537
|
-
if (!
|
|
9617
|
+
if (!labelSelected(e) || activeId == -1) return;
|
|
9538
9618
|
if (e.id != activeId) {
|
|
9539
9619
|
error$1("Mismatched hit ids:", e.id, activeId);
|
|
9540
9620
|
}
|
|
@@ -9548,7 +9628,7 @@
|
|
|
9548
9628
|
// to anchor point, for better placement when eventual display font is
|
|
9549
9629
|
// different from mapshaper's font.
|
|
9550
9630
|
// if (!isMultilineLabel(textNode)) {
|
|
9551
|
-
autoUpdateTextAnchor(textNode, activeRecord, getDisplayCoordsById(activeId, hit.getHitTarget()
|
|
9631
|
+
autoUpdateTextAnchor(textNode, activeRecord, getDisplayCoordsById(activeId, hit.getHitTarget(), ext));
|
|
9552
9632
|
// }
|
|
9553
9633
|
updateNumber(activeRecord, 'dx', internal.roundToDigits(+activeRecord.dx, 3));
|
|
9554
9634
|
updateNumber(activeRecord, 'dy', internal.roundToDigits(+activeRecord.dy, 3));
|
|
@@ -9558,7 +9638,7 @@
|
|
|
9558
9638
|
});
|
|
9559
9639
|
|
|
9560
9640
|
hit.on('dragend', function(e) {
|
|
9561
|
-
if (!
|
|
9641
|
+
if (!labelSelected(e) || activeId == -1) return;
|
|
9562
9642
|
gui.dispatchEvent('label_dragend', {FID: e.id});
|
|
9563
9643
|
activeId = -1;
|
|
9564
9644
|
activeRecord = null;
|
|
@@ -9579,19 +9659,25 @@
|
|
|
9579
9659
|
}
|
|
9580
9660
|
|
|
9581
9661
|
function getSymbolTarget(e) {
|
|
9582
|
-
|
|
9583
|
-
return getSymbolNodeById(e.id, e.container);
|
|
9662
|
+
return e.id > -1 ? getSymbolNodeById(e.id) : null;
|
|
9584
9663
|
}
|
|
9585
9664
|
|
|
9586
9665
|
function getTextNode(symNode) {
|
|
9666
|
+
if (!symNode) return null;
|
|
9587
9667
|
if (symNode.tagName == 'text') return symNode;
|
|
9588
9668
|
return symNode.querySelector('text');
|
|
9589
9669
|
}
|
|
9590
9670
|
|
|
9591
|
-
function
|
|
9671
|
+
// function getSymbolNodeById_OLD(id, parent) {
|
|
9672
|
+
// var sel = '[data-id="' + id + '"]';
|
|
9673
|
+
// return parent.querySelector(sel);
|
|
9674
|
+
// }
|
|
9675
|
+
|
|
9676
|
+
function getSymbolNodeById(id) {
|
|
9592
9677
|
// TODO: optimize selector
|
|
9593
9678
|
var sel = '[data-id="' + id + '"]';
|
|
9594
|
-
|
|
9679
|
+
var activeLayer = hit.getHitTarget();
|
|
9680
|
+
return activeLayer.gui.svg_container.querySelector(sel);
|
|
9595
9681
|
}
|
|
9596
9682
|
|
|
9597
9683
|
// function getTextTarget2(e) {
|
|
@@ -9638,7 +9724,7 @@
|
|
|
9638
9724
|
// fails when symbol includes a dot (<g><circle/><text/></g> structure)
|
|
9639
9725
|
function updateSymbolNode(node, d, id) {
|
|
9640
9726
|
var o = internal.svg.renderStyledLabel(d); // TODO: symbol support
|
|
9641
|
-
var activeLayer = hit.getHitTarget()
|
|
9727
|
+
var activeLayer = hit.getHitTarget();
|
|
9642
9728
|
var xy = activeLayer.shapes[id][0];
|
|
9643
9729
|
var g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
9644
9730
|
var node2;
|
|
@@ -9671,7 +9757,7 @@
|
|
|
9671
9757
|
hit.on('drag', function(e) {
|
|
9672
9758
|
if (!active(e)) return;
|
|
9673
9759
|
// TODO: support multi points... get id of closest part to the pointer
|
|
9674
|
-
var p = getPointCoordsById(e.id, symbolInfo.target
|
|
9760
|
+
var p = getPointCoordsById(e.id, symbolInfo.target);
|
|
9675
9761
|
if (!p) return;
|
|
9676
9762
|
var diff = translateDeltaDisplayCoords(e.dx, e.dy, ext);
|
|
9677
9763
|
p[0] += diff[0];
|
|
@@ -9702,20 +9788,22 @@
|
|
|
9702
9788
|
}
|
|
9703
9789
|
}
|
|
9704
9790
|
|
|
9705
|
-
//
|
|
9706
|
-
var HOVER_THRESHOLD
|
|
9791
|
+
// pixel distance threshold for hovering near a vertex or segment midpoint
|
|
9792
|
+
var HOVER_THRESHOLD = 10;
|
|
9707
9793
|
|
|
9708
9794
|
function initLineEditing(gui, ext, hit) {
|
|
9709
9795
|
var hoverVertexInfo;
|
|
9710
9796
|
var prevClickEvent;
|
|
9711
9797
|
var prevHoverEvent;
|
|
9712
9798
|
var initialArcCount = -1;
|
|
9799
|
+
var initialShapeCount = -1;
|
|
9713
9800
|
var drawingId = -1; // feature id of path being drawn
|
|
9801
|
+
var sessionCount = 0;
|
|
9714
9802
|
var alert;
|
|
9715
9803
|
var _dragging = false;
|
|
9716
9804
|
|
|
9717
9805
|
function active() {
|
|
9718
|
-
return
|
|
9806
|
+
return initialArcCount >= 0;
|
|
9719
9807
|
}
|
|
9720
9808
|
|
|
9721
9809
|
function dragging() {
|
|
@@ -9727,7 +9815,7 @@
|
|
|
9727
9815
|
}
|
|
9728
9816
|
|
|
9729
9817
|
function polygonMode() {
|
|
9730
|
-
return active() && hit.getHitTarget().
|
|
9818
|
+
return active() && hit.getHitTarget().geometry_type == 'polygon';
|
|
9731
9819
|
}
|
|
9732
9820
|
|
|
9733
9821
|
function clearHoverVertex() {
|
|
@@ -9743,7 +9831,7 @@
|
|
|
9743
9831
|
if (e.mode == 'drawing') {
|
|
9744
9832
|
gui.enterMode('drawing_tool');
|
|
9745
9833
|
} else if (active()) {
|
|
9746
|
-
|
|
9834
|
+
turnOff();
|
|
9747
9835
|
}
|
|
9748
9836
|
}, null, 10); // higher priority than hit control, so turnOff() has correct hit target
|
|
9749
9837
|
|
|
@@ -9753,7 +9841,7 @@
|
|
|
9753
9841
|
appendNewPath(target, e.p1, e.p2);
|
|
9754
9842
|
deleteLastVertex(target); // second vertex is a placeholder
|
|
9755
9843
|
gui.undo.redo(); // add next vertex in the path
|
|
9756
|
-
|
|
9844
|
+
fullRedraw();
|
|
9757
9845
|
});
|
|
9758
9846
|
|
|
9759
9847
|
gui.on('undo_path_add', function(e) {
|
|
@@ -9769,6 +9857,9 @@
|
|
|
9769
9857
|
} else {
|
|
9770
9858
|
appendVertex$1(target, e.p);
|
|
9771
9859
|
}
|
|
9860
|
+
if (e.shapes) {
|
|
9861
|
+
replaceDrawnShapes(e.shapes);
|
|
9862
|
+
}
|
|
9772
9863
|
});
|
|
9773
9864
|
|
|
9774
9865
|
gui.on('undo_path_extend', function(e) {
|
|
@@ -9782,75 +9873,74 @@
|
|
|
9782
9873
|
if (getLastArcLength(target) < 2) {
|
|
9783
9874
|
gui.undo.undo(); // remove the path
|
|
9784
9875
|
}
|
|
9876
|
+
if (e.shapes) {
|
|
9877
|
+
replaceDrawnShapes(e.shapes);
|
|
9878
|
+
}
|
|
9785
9879
|
});
|
|
9786
9880
|
|
|
9787
9881
|
function turnOn() {
|
|
9788
9882
|
var target = hit.getHitTarget();
|
|
9789
|
-
|
|
9883
|
+
initialArcCount = target.gui.displayArcs.size();
|
|
9884
|
+
initialShapeCount = target.shapes.length;
|
|
9885
|
+
if (sessionCount === 0) {
|
|
9886
|
+
showInstructions();
|
|
9887
|
+
}
|
|
9888
|
+
sessionCount++;
|
|
9889
|
+
}
|
|
9890
|
+
|
|
9891
|
+
function showInstructions() {
|
|
9790
9892
|
var isMac = navigator.userAgent.includes('Mac');
|
|
9791
9893
|
var symbol = isMac ? '⌘' : '^';
|
|
9894
|
+
var pathStr = polygonMode() ? 'closed paths' : 'paths';
|
|
9792
9895
|
var msg = `Instructions: Click on the map to draw ${pathStr}. Drag vertices to reshape a path. Type ${symbol}Z/${symbol}Y to undo/redo.`;
|
|
9793
|
-
|
|
9794
|
-
|
|
9896
|
+
alert = showPopupAlert(msg, null, {
|
|
9897
|
+
non_blocking: true, max_width: '360px'});
|
|
9898
|
+
}
|
|
9899
|
+
|
|
9900
|
+
function hideInstructions() {
|
|
9901
|
+
if (!alert) return;
|
|
9902
|
+
alert.close('fade');
|
|
9903
|
+
alert = null;
|
|
9795
9904
|
}
|
|
9796
9905
|
|
|
9797
9906
|
function turnOff() {
|
|
9798
|
-
|
|
9907
|
+
var removed = 0;
|
|
9908
|
+
finishCurrentPath();
|
|
9799
9909
|
if (polygonMode()) {
|
|
9800
|
-
|
|
9910
|
+
removed = removeOpenPolygons();
|
|
9801
9911
|
}
|
|
9802
9912
|
clearDrawingInfo();
|
|
9803
|
-
|
|
9804
|
-
alert = null;
|
|
9913
|
+
hideInstructions();
|
|
9805
9914
|
initialArcCount = -1;
|
|
9915
|
+
initialShapeCount = -1;
|
|
9806
9916
|
if (gui.interaction.getMode() == 'drawing') {
|
|
9807
9917
|
// mode change was not initiated by interactive menu -- turn off interactivity
|
|
9808
9918
|
gui.interaction.turnOff();
|
|
9809
9919
|
}
|
|
9810
|
-
|
|
9811
|
-
|
|
9812
|
-
function finish() {
|
|
9813
|
-
if (polygonMode()) {
|
|
9814
|
-
finishPolygons();
|
|
9920
|
+
if (removed > 0) {
|
|
9921
|
+
fullRedraw();
|
|
9815
9922
|
}
|
|
9816
9923
|
}
|
|
9817
9924
|
|
|
9818
|
-
|
|
9819
|
-
|
|
9925
|
+
// returns number of removed shapes
|
|
9926
|
+
function removeOpenPolygons() {
|
|
9820
9927
|
var target = hit.getHitTarget();
|
|
9821
|
-
var
|
|
9822
|
-
|
|
9823
|
-
|
|
9824
|
-
var
|
|
9825
|
-
|
|
9826
|
-
|
|
9827
|
-
|
|
9828
|
-
// use one of the newly created records as a template (they should all be the same)
|
|
9829
|
-
templateRecord = polygonRecords.pop();
|
|
9830
|
-
polygonRecords.splice(initialShapeCount); // remove new records
|
|
9831
|
-
}
|
|
9832
|
-
var polylineLyr = {
|
|
9833
|
-
geometry_type: 'polyline',
|
|
9834
|
-
shapes: polygonLyr.shapes.splice(initialShapeCount) // move new shapes to polyline layer
|
|
9835
|
-
};
|
|
9836
|
-
|
|
9837
|
-
// step2: convert polylines to polygons
|
|
9838
|
-
//
|
|
9839
|
-
// create a temp dataset containing both layers (so original arcs are preserved)
|
|
9840
|
-
var tmp = Object.assign({}, target.source.dataset);
|
|
9841
|
-
tmp.layers = tmp.layers.concat(polylineLyr);
|
|
9842
|
-
var outputLayers = mapshaper.cmd.polygons([polylineLyr], tmp, {});
|
|
9843
|
-
|
|
9844
|
-
// step3: add new polygons to the original polygons
|
|
9845
|
-
outputLayers[0].shapes.forEach(function(shp) {
|
|
9846
|
-
var rec = polygonRecords ? internal.copyRecord(templateRecord) : null;
|
|
9847
|
-
polygonLyr.shapes.push(shp);
|
|
9848
|
-
if (polygonRecords) {
|
|
9849
|
-
polygonRecords.push(rec);
|
|
9928
|
+
var arcs = target.gui.source.dataset.arcs;
|
|
9929
|
+
var n = target.shapes.length;
|
|
9930
|
+
// delete open paths
|
|
9931
|
+
for (var i=initialShapeCount; i<n; i++) {
|
|
9932
|
+
var shp = target.shapes[i];
|
|
9933
|
+
if (!geom.pathIsClosed(shp[0], arcs)) { // assume open paths have one arc
|
|
9934
|
+
target.shapes[i] = null;
|
|
9850
9935
|
}
|
|
9851
|
-
}
|
|
9936
|
+
}
|
|
9937
|
+
// removes features with wrong winding order or null geometry
|
|
9938
|
+
mapshaper.cmd.filterFeatures(target, arcs, {remove_empty: true, quiet: true});
|
|
9939
|
+
return n - target.shapes.length;
|
|
9940
|
+
}
|
|
9852
9941
|
|
|
9853
|
-
|
|
9942
|
+
// updates display arcs and redraws all layers
|
|
9943
|
+
function fullRedraw() {
|
|
9854
9944
|
gui.model.updated({arc_count: true});
|
|
9855
9945
|
}
|
|
9856
9946
|
|
|
@@ -9861,10 +9951,25 @@
|
|
|
9861
9951
|
prevClickEvent = prevHoverEvent = null;
|
|
9862
9952
|
}
|
|
9863
9953
|
|
|
9954
|
+
hit.on('contextmenu', function(e) {
|
|
9955
|
+
if (!active() || drawing() || dragging()) return;
|
|
9956
|
+
var target = hit.getHitTarget();
|
|
9957
|
+
var vInfo = hoverVertexInfo;
|
|
9958
|
+
if (hoverVertexInfo?.type == 'vertex' && !vertexIsEndpoint(vInfo, target)) {
|
|
9959
|
+
e.deleteVertex = function() {
|
|
9960
|
+
deleteActiveVertex(e, vInfo);
|
|
9961
|
+
};
|
|
9962
|
+
}
|
|
9963
|
+
// don't allow copying of open paths as geojson in polygon mode
|
|
9964
|
+
gui.contextMenu.open(e, target);
|
|
9965
|
+
});
|
|
9966
|
+
|
|
9864
9967
|
hit.on('dragstart', function(e) {
|
|
9865
9968
|
if (!active() || drawing() || !hoverVertexInfo) return;
|
|
9969
|
+
hideInstructions();
|
|
9866
9970
|
e.originalEvent.stopPropagation();
|
|
9867
9971
|
_dragging = true;
|
|
9972
|
+
updateCursor();
|
|
9868
9973
|
if (hoverVertexInfo.type == 'interpolated') {
|
|
9869
9974
|
insertVertex$1(hit.getHitTarget(), hoverVertexInfo.i, hoverVertexInfo.point);
|
|
9870
9975
|
hoverVertexInfo.ids = [hoverVertexInfo.i];
|
|
@@ -9878,11 +9983,10 @@
|
|
|
9878
9983
|
var target = hit.getHitTarget();
|
|
9879
9984
|
var p = ext.translatePixelCoords(e.x, e.y);
|
|
9880
9985
|
if (gui.keyboard.shiftIsPressed()) {
|
|
9881
|
-
internal.snapPointToArcEndpoint(p, hoverVertexInfo.ids, target.
|
|
9986
|
+
internal.snapPointToArcEndpoint(p, hoverVertexInfo.ids, target.gui.displayArcs);
|
|
9882
9987
|
}
|
|
9883
|
-
internal.snapVerticesToPoint(hoverVertexInfo.ids, p, target.
|
|
9988
|
+
internal.snapVerticesToPoint(hoverVertexInfo.ids, p, target.gui.displayArcs);
|
|
9884
9989
|
hit.setHoverVertex(p, '');
|
|
9885
|
-
|
|
9886
9990
|
// redrawing the whole map updates the data layer as well as the overlay layer
|
|
9887
9991
|
// gui.dispatchEvent('map-needs-refresh');
|
|
9888
9992
|
});
|
|
@@ -9890,9 +9994,9 @@
|
|
|
9890
9994
|
hit.on('dragend', function(e) {
|
|
9891
9995
|
if (!dragging()) return;
|
|
9892
9996
|
_dragging = false;
|
|
9893
|
-
// kludge to get dataset to recalculate internal bounding boxes
|
|
9894
9997
|
var target = hit.getHitTarget();
|
|
9895
|
-
|
|
9998
|
+
// kludge to get dataset to recalculate internal bounding boxes
|
|
9999
|
+
target.gui.displayArcs.transformPoints(function() {});
|
|
9896
10000
|
updateVertexCoords(target, hoverVertexInfo.ids);
|
|
9897
10001
|
gui.dispatchEvent('vertex_dragend', hoverVertexInfo);
|
|
9898
10002
|
gui.dispatchEvent('map-needs-refresh'); // redraw basemap
|
|
@@ -9903,12 +10007,10 @@
|
|
|
9903
10007
|
// double-click finishes a path (when drawing)
|
|
9904
10008
|
hit.on('dblclick', function(e) {
|
|
9905
10009
|
if (!active()) return;
|
|
10010
|
+
// double click finishes a path
|
|
10011
|
+
// note: if the preceding 'click' finished the path, this does not fire
|
|
9906
10012
|
if (drawing()) {
|
|
9907
|
-
|
|
9908
|
-
// before: dblclick is preceded by two clicks, need another vertex delete
|
|
9909
|
-
// now: second click is suppressed
|
|
9910
|
-
// deleteLastVertex(hit.getHitTarget());
|
|
9911
|
-
finishPath();
|
|
10013
|
+
finishCurrentPath();
|
|
9912
10014
|
e.originalEvent.stopPropagation(); // prevent dblclick zoom
|
|
9913
10015
|
return;
|
|
9914
10016
|
}
|
|
@@ -9918,9 +10020,10 @@
|
|
|
9918
10020
|
// ... or the closest point along the segment (for adding a new vertex)
|
|
9919
10021
|
hit.on('hover', function(e) {
|
|
9920
10022
|
if (!active() || dragging()) return;
|
|
10023
|
+
|
|
9921
10024
|
if (drawing()) {
|
|
9922
10025
|
if (!e.overMap) {
|
|
9923
|
-
|
|
10026
|
+
finishCurrentPath();
|
|
9924
10027
|
return;
|
|
9925
10028
|
}
|
|
9926
10029
|
if (gui.keyboard.shiftIsPressed()) {
|
|
@@ -9940,6 +10043,7 @@
|
|
|
9940
10043
|
} else {
|
|
9941
10044
|
clearHoverVertex();
|
|
9942
10045
|
}
|
|
10046
|
+
updateCursor();
|
|
9943
10047
|
prevHoverEvent = e;
|
|
9944
10048
|
}, null, 100);
|
|
9945
10049
|
|
|
@@ -9948,27 +10052,26 @@
|
|
|
9948
10052
|
if (!active()) return;
|
|
9949
10053
|
if (detectDoubleClick(e)) return; // ignore second click of a dblclick
|
|
9950
10054
|
var p = pixToDataCoords(e.x, e.y);
|
|
9951
|
-
if (drawing()
|
|
9952
|
-
|
|
9953
|
-
p = hoverVertexInfo.point;
|
|
9954
|
-
extendPath(p);
|
|
9955
|
-
finishPath();
|
|
9956
|
-
} else if (drawing()) {
|
|
9957
|
-
extendPath(p);
|
|
10055
|
+
if (drawing()) {
|
|
10056
|
+
extendCurrentPath(hoverVertexInfo?.point || p);
|
|
9958
10057
|
} else if (gui.keyboard.shiftIsPressed()) {
|
|
9959
10058
|
deleteActiveVertex(e);
|
|
9960
10059
|
} else {
|
|
9961
|
-
|
|
10060
|
+
startNewPath(p);
|
|
10061
|
+
hideInstructions();
|
|
10062
|
+
updateCursor();
|
|
9962
10063
|
}
|
|
9963
10064
|
prevClickEvent = e;
|
|
9964
10065
|
});
|
|
9965
10066
|
|
|
9966
|
-
// esc key finishes a path
|
|
10067
|
+
// esc or enter key finishes a path
|
|
9967
10068
|
gui.keyboard.on('keydown', function(e) {
|
|
9968
|
-
if (active() && e.keyName == 'esc') {
|
|
9969
|
-
|
|
10069
|
+
if (active() && (e.keyName == 'esc' || e.keyName == 'enter')) {
|
|
10070
|
+
e.stopPropagation();
|
|
10071
|
+
finishCurrentPath();
|
|
10072
|
+
e.originalEvent.preventDefault(); // block console "enter"
|
|
9970
10073
|
}
|
|
9971
|
-
});
|
|
10074
|
+
}, null, 10);
|
|
9972
10075
|
|
|
9973
10076
|
// detect second 'click' event of a double-click action
|
|
9974
10077
|
function detectDoubleClick(evt) {
|
|
@@ -9980,16 +10083,24 @@
|
|
|
9980
10083
|
return dbl;
|
|
9981
10084
|
}
|
|
9982
10085
|
|
|
9983
|
-
function
|
|
9984
|
-
var
|
|
9985
|
-
|
|
10086
|
+
function updateCursor() {
|
|
10087
|
+
var useArrow = hoverVertexInfo && !hoverVertexInfo.extendable && !drawing();
|
|
10088
|
+
gui.container.findChild('.map-layers').classed('dragging', useArrow);
|
|
10089
|
+
}
|
|
10090
|
+
|
|
10091
|
+
function vertexIsEndpoint(info, target) {
|
|
10092
|
+
var vId = info.ids[0];
|
|
10093
|
+
return internal.vertexIsArcStart(vId, target.gui.displayArcs) ||
|
|
10094
|
+
internal.vertexIsArcEnd(vId, target.gui.displayArcs);
|
|
10095
|
+
}
|
|
10096
|
+
|
|
10097
|
+
// info: optional vertex info object
|
|
10098
|
+
function deleteActiveVertex(e, infoArg) {
|
|
10099
|
+
var info = infoArg || findDraggableVertices(e);
|
|
10100
|
+
if (!info || info.type != 'vertex') return;
|
|
9986
10101
|
var vId = info.ids[0];
|
|
9987
10102
|
var target = hit.getHitTarget();
|
|
9988
|
-
if (
|
|
9989
|
-
internal.vertexIsArcEnd(vId, target.arcs)) {
|
|
9990
|
-
// TODO: support removing arc endpoints
|
|
9991
|
-
return;
|
|
9992
|
-
}
|
|
10103
|
+
if (vertexIsEndpoint(info, target)) return;
|
|
9993
10104
|
gui.dispatchEvent('vertex_delete', {
|
|
9994
10105
|
target: target,
|
|
9995
10106
|
vertex_id: vId
|
|
@@ -10015,28 +10126,30 @@
|
|
|
10015
10126
|
var dist2 = dist / Math.sqrt(2);
|
|
10016
10127
|
var minDist = Infinity;
|
|
10017
10128
|
var cands = [
|
|
10018
|
-
{
|
|
10019
|
-
{
|
|
10020
|
-
{
|
|
10021
|
-
{
|
|
10022
|
-
{
|
|
10023
|
-
{
|
|
10024
|
-
{
|
|
10025
|
-
{
|
|
10129
|
+
{dx: 0, dy: dist},
|
|
10130
|
+
{dx: 0, dy: -dist},
|
|
10131
|
+
{dx: dist, dy: 0},
|
|
10132
|
+
{dx: -dist, dy: 0},
|
|
10133
|
+
{dx: dist2, dy: dist2},
|
|
10134
|
+
{dx: dist2, dy: -dist2},
|
|
10135
|
+
{dx: -dist2, dy: dist2},
|
|
10136
|
+
{dx: -dist2, dy: -dist2}
|
|
10026
10137
|
];
|
|
10027
10138
|
var snapped = cands.reduce(function(memo, cand) {
|
|
10028
|
-
var dist = geom.distance2D(thisEvt.x, thisEvt.y, cand.
|
|
10139
|
+
var dist = geom.distance2D(thisEvt.x, thisEvt.y, x0 + cand.dx, y0 + cand.dy);
|
|
10029
10140
|
if (dist < minDist) {
|
|
10030
10141
|
minDist = dist;
|
|
10031
10142
|
return cand;
|
|
10032
10143
|
}
|
|
10033
10144
|
return memo;
|
|
10034
10145
|
}, null);
|
|
10035
|
-
thisEvt.x = snapped.
|
|
10036
|
-
thisEvt.y = snapped.
|
|
10146
|
+
thisEvt.x = x0 + snapped.dx;
|
|
10147
|
+
thisEvt.y = y0 + snapped.dy;
|
|
10148
|
+
|
|
10149
|
+
return null;
|
|
10037
10150
|
}
|
|
10038
10151
|
|
|
10039
|
-
function
|
|
10152
|
+
function finishCurrentPath() {
|
|
10040
10153
|
if (!drawing()) return;
|
|
10041
10154
|
var target = hit.getHitTarget();
|
|
10042
10155
|
if (getLastArcLength(target) <= 2) { // includes hover point
|
|
@@ -10045,33 +10158,64 @@
|
|
|
10045
10158
|
deleteLastVertex(target);
|
|
10046
10159
|
}
|
|
10047
10160
|
clearDrawingInfo();
|
|
10048
|
-
|
|
10161
|
+
fullRedraw();
|
|
10049
10162
|
}
|
|
10050
10163
|
|
|
10051
10164
|
// p: [x, y] source data coordinates
|
|
10052
|
-
function
|
|
10165
|
+
function startNewPath(p2) {
|
|
10053
10166
|
var target = hit.getHitTarget();
|
|
10054
|
-
var p1 = hoverVertexInfo
|
|
10167
|
+
var p1 = hoverVertexInfo?.point || p2;
|
|
10055
10168
|
appendNewPath(target, p1, p2);
|
|
10056
10169
|
gui.dispatchEvent('path_add', {target, p1, p2});
|
|
10057
|
-
drawingId = target.
|
|
10170
|
+
drawingId = target.shapes.length - 1;
|
|
10058
10171
|
hit.setDrawingId(drawingId);
|
|
10059
10172
|
}
|
|
10060
10173
|
|
|
10061
|
-
// p: [x, y] source data coordinates
|
|
10062
|
-
function
|
|
10174
|
+
// p: [x, y] source data coordinates of new point on path
|
|
10175
|
+
function extendCurrentPath(p) {
|
|
10063
10176
|
var target = hit.getHitTarget();
|
|
10064
|
-
|
|
10177
|
+
var shapes1, shapes2;
|
|
10178
|
+
// finish the path if a vertex is selected (but not an interpolated point)
|
|
10179
|
+
var finish = hoverVertexInfo?.type == 'vertex';
|
|
10180
|
+
if (getLastArcLength(target) < 2) {
|
|
10181
|
+
error$1('Defective path');
|
|
10182
|
+
}
|
|
10183
|
+
if (finish && polygonMode()) {
|
|
10184
|
+
shapes1 = target.shapes.slice(initialShapeCount);
|
|
10185
|
+
shapes2 = convertClosedPaths(shapes1);
|
|
10186
|
+
}
|
|
10187
|
+
if (shapes2) {
|
|
10188
|
+
replaceDrawnShapes(shapes2);
|
|
10189
|
+
gui.dispatchEvent('path_extend', {target, p, shapes1, shapes2});
|
|
10190
|
+
clearDrawingInfo();
|
|
10191
|
+
fullRedraw();
|
|
10192
|
+
|
|
10193
|
+
} else {
|
|
10194
|
+
appendVertex$1(target, p);
|
|
10065
10195
|
gui.dispatchEvent('path_extend', {target, p});
|
|
10196
|
+
hit.triggerChangeEvent(); // trigger overlay redraw
|
|
10197
|
+
}
|
|
10198
|
+
}
|
|
10199
|
+
|
|
10200
|
+
function replaceDrawnShapes(shapes) {
|
|
10201
|
+
var target = hit.getHitTarget();
|
|
10202
|
+
var records = target.data?.getRecords();
|
|
10203
|
+
var prevLen = target.shapes.length;
|
|
10204
|
+
var newLen = initialShapeCount + shapes.length;
|
|
10205
|
+
var recordCount = records?.length || 0;
|
|
10206
|
+
target.shapes = target.shapes.slice(0, initialShapeCount).concat(shapes);
|
|
10207
|
+
while (records && records.length > newLen) {
|
|
10208
|
+
records.pop();
|
|
10209
|
+
}
|
|
10210
|
+
while (records && records.length < newLen) {
|
|
10211
|
+
appendNewDataRecord(target);
|
|
10066
10212
|
}
|
|
10067
|
-
appendVertex$1(target, p);
|
|
10068
|
-
hit.triggerChangeEvent();
|
|
10069
10213
|
}
|
|
10070
10214
|
|
|
10071
10215
|
// p: [x, y] source data coordinates
|
|
10072
10216
|
function updatePathEndpoint(p) {
|
|
10073
10217
|
var target = hit.getHitTarget();
|
|
10074
|
-
var i = target.
|
|
10218
|
+
var i = target.gui.displayArcs.getPointCount() - 1;
|
|
10075
10219
|
if (hoverVertexInfo) {
|
|
10076
10220
|
p = hoverVertexInfo.point; // snap to selected point
|
|
10077
10221
|
}
|
|
@@ -10081,8 +10225,8 @@
|
|
|
10081
10225
|
|
|
10082
10226
|
function findPathStartInfo(e) {
|
|
10083
10227
|
var target = hit.getHitTarget();
|
|
10084
|
-
var arcId = target.
|
|
10085
|
-
var data = target.
|
|
10228
|
+
var arcId = target.gui.displayArcs.size() - 1;
|
|
10229
|
+
var data = target.gui.displayArcs.getVertexData();
|
|
10086
10230
|
var i = data.ii[arcId];
|
|
10087
10231
|
var x = data.xx[i];
|
|
10088
10232
|
var y = data.yy[i];
|
|
@@ -10090,7 +10234,7 @@
|
|
|
10090
10234
|
var dist = geom.distance2D(p[0], p[1], x, y);
|
|
10091
10235
|
var pathLen = data.nn[arcId];
|
|
10092
10236
|
var pixelDist = dist / ext.getPixelSize();
|
|
10093
|
-
if (pixelDist > HOVER_THRESHOLD
|
|
10237
|
+
if (pixelDist > HOVER_THRESHOLD || pathLen < 4) {
|
|
10094
10238
|
return null;
|
|
10095
10239
|
}
|
|
10096
10240
|
var point = translateDisplayPoint(target, [x, y]);
|
|
@@ -10104,41 +10248,38 @@
|
|
|
10104
10248
|
//
|
|
10105
10249
|
function findDraggableVertices(e) {
|
|
10106
10250
|
var target = hit.getHitTarget();
|
|
10107
|
-
var shp = target.
|
|
10251
|
+
var shp = target.shapes[e.id];
|
|
10108
10252
|
var p = ext.translatePixelCoords(e.x, e.y);
|
|
10109
|
-
var ids = internal.findNearestVertices(p, shp, target.
|
|
10110
|
-
var p2 = target.
|
|
10253
|
+
var ids = internal.findNearestVertices(p, shp, target.gui.displayArcs);
|
|
10254
|
+
var p2 = target.gui.displayArcs.getVertex2(ids[0]);
|
|
10111
10255
|
var dist = geom.distance2D(p[0], p[1], p2[0], p2[1]);
|
|
10112
10256
|
var pixelDist = dist / ext.getPixelSize();
|
|
10113
|
-
if (pixelDist > HOVER_THRESHOLD
|
|
10257
|
+
if (pixelDist > HOVER_THRESHOLD) {
|
|
10114
10258
|
return null;
|
|
10115
10259
|
}
|
|
10116
10260
|
var point = getVertexCoords(target, ids[0]); // data coordinates
|
|
10117
10261
|
// find out if the vertex is the endpoint of a single path
|
|
10118
10262
|
// (which could be extended by a newly drawn path)
|
|
10119
10263
|
var extendable = ids.length == 1 &&
|
|
10120
|
-
internal.vertexIsArcEndpoint(ids[0], target.
|
|
10121
|
-
var displayPoint = target.
|
|
10264
|
+
internal.vertexIsArcEndpoint(ids[0], target.gui.displayArcs);
|
|
10265
|
+
var displayPoint = target.gui.displayArcs.getVertex2(ids[0]);
|
|
10122
10266
|
return {target, ids, extendable, point, displayPoint, type: 'vertex'};
|
|
10123
10267
|
}
|
|
10124
10268
|
|
|
10269
|
+
|
|
10125
10270
|
function findInterpolatedPoint(e) {
|
|
10126
10271
|
var target = hit.getHitTarget();
|
|
10127
10272
|
//// vertex insertion not supported with simplification
|
|
10128
10273
|
// if (!target.arcs.isFlat()) return null;
|
|
10129
10274
|
var p = ext.translatePixelCoords(e.x, e.y);
|
|
10130
10275
|
var minDist = Infinity;
|
|
10131
|
-
var shp = target.
|
|
10276
|
+
var shp = target.shapes[e.id];
|
|
10132
10277
|
var closest;
|
|
10133
|
-
internal.forEachSegmentInShape(shp, target.
|
|
10278
|
+
internal.forEachSegmentInShape(shp, target.gui.displayArcs, function(i, j, xx, yy) {
|
|
10134
10279
|
var x1 = xx[i],
|
|
10135
10280
|
y1 = yy[i],
|
|
10136
10281
|
x2 = xx[j],
|
|
10137
10282
|
y2 = yy[j],
|
|
10138
|
-
// switching from midpoint to nearest point to the mouse
|
|
10139
|
-
// cx = (x1 + x2) / 2,
|
|
10140
|
-
// cy = (y1 + y2) / 2,
|
|
10141
|
-
// p2 = [cx, cy],
|
|
10142
10283
|
p2 = internal.findClosestPointOnSeg(p[0], p[1], x1, y1, x2, y2, 0),
|
|
10143
10284
|
dist = geom.distance2D(p2[0], p2[1], p[0], p[1]);
|
|
10144
10285
|
if (dist < minDist) {
|
|
@@ -10151,7 +10292,7 @@
|
|
|
10151
10292
|
}
|
|
10152
10293
|
});
|
|
10153
10294
|
|
|
10154
|
-
if (closest.distance / ext.getPixelSize() > HOVER_THRESHOLD
|
|
10295
|
+
if (closest.distance / ext.getPixelSize() > HOVER_THRESHOLD) {
|
|
10155
10296
|
return null;
|
|
10156
10297
|
}
|
|
10157
10298
|
closest.point = translateDisplayPoint(target, closest.displayPoint);
|
|
@@ -10160,6 +10301,45 @@
|
|
|
10160
10301
|
return closest;
|
|
10161
10302
|
}
|
|
10162
10303
|
|
|
10304
|
+
// Try to form polygon shapes from an array of path shapes
|
|
10305
|
+
// shapes: array of all shapes that have been drawn in the current session
|
|
10306
|
+
function convertClosedPaths(shapes) {
|
|
10307
|
+
var target = hit.getHitTarget();
|
|
10308
|
+
// try to convert paths to polygons
|
|
10309
|
+
// NOTE: added "no_cuts" option to prevent polygons function from modifying
|
|
10310
|
+
// arcs, which would break undo/redo and cause other problems
|
|
10311
|
+
var tmpLyr = {
|
|
10312
|
+
geometry_type: 'polyline',
|
|
10313
|
+
shapes: shapes.concat()
|
|
10314
|
+
};
|
|
10315
|
+
var output = mapshaper.cmd.polygons([tmpLyr], target.gui.source.dataset, {no_cuts: true});
|
|
10316
|
+
var closedShapes = output[0].shapes;
|
|
10317
|
+
|
|
10318
|
+
// find paths that were not convertible to polygons
|
|
10319
|
+
var isOpenPath = getOpenPathTest(closedShapes);
|
|
10320
|
+
var openShapes = shapes.filter(function(shp) { return isOpenPath(shp); });
|
|
10321
|
+
|
|
10322
|
+
// retain both converted polygons and unconverted polylines
|
|
10323
|
+
return openShapes.concat(closedShapes);
|
|
10324
|
+
}
|
|
10325
|
+
|
|
10326
|
+
// Returns a function for testing if a shape is an unclosed path, and doesn't
|
|
10327
|
+
// overlap with an array of polygon shapes
|
|
10328
|
+
function getOpenPathTest(polygonShapes) {
|
|
10329
|
+
var polygonArcs = [];
|
|
10330
|
+
internal.forEachArcId(polygonShapes, function(arcId) {
|
|
10331
|
+
polygonArcs.push(internal.absArcId(arcId));
|
|
10332
|
+
});
|
|
10333
|
+
|
|
10334
|
+
return function(shp) {
|
|
10335
|
+
// assume that any compound shape is a polygon
|
|
10336
|
+
return shapeHasOneFwdArc(shp) && !polygonArcs.includes(shp[0][0]);
|
|
10337
|
+
};
|
|
10338
|
+
}
|
|
10339
|
+
|
|
10340
|
+
function shapeHasOneFwdArc(shp) {
|
|
10341
|
+
return shp.length == 1 && shp[0].length == 1 && shp[0][0] >= 0;
|
|
10342
|
+
}
|
|
10163
10343
|
}
|
|
10164
10344
|
|
|
10165
10345
|
function initPointDrawing(gui, ext, hit) {
|
|
@@ -10182,8 +10362,7 @@
|
|
|
10182
10362
|
// x, y: pixel coordinates
|
|
10183
10363
|
function addPoint(x, y) {
|
|
10184
10364
|
var p = ext.translatePixelCoords(x, y);
|
|
10185
|
-
var
|
|
10186
|
-
var lyr = target.layer;
|
|
10365
|
+
var lyr = hit.getHitTarget();
|
|
10187
10366
|
var fid = lyr.shapes.length;
|
|
10188
10367
|
var d = appendNewDataRecord(lyr);
|
|
10189
10368
|
if (d) {
|
|
@@ -10192,194 +10371,17 @@
|
|
|
10192
10371
|
lyr.data.getRecords()[fid] = d;
|
|
10193
10372
|
}
|
|
10194
10373
|
lyr.shapes[fid] = [p];
|
|
10195
|
-
updatePointCoords(
|
|
10374
|
+
updatePointCoords(lyr, fid);
|
|
10196
10375
|
}
|
|
10197
10376
|
|
|
10198
10377
|
|
|
10199
10378
|
}
|
|
10200
10379
|
|
|
10201
|
-
// pointer thresholds for hovering near a vertex or segment midpoint
|
|
10202
|
-
var HOVER_THRESHOLD = 8;
|
|
10203
|
-
var MIDPOINT_THRESHOLD = 11;
|
|
10204
|
-
|
|
10205
|
-
function initVertexDragging(gui, ext, hit) {
|
|
10206
|
-
var insertionPoint;
|
|
10207
|
-
var dragInfo;
|
|
10208
|
-
|
|
10209
|
-
function active() {
|
|
10210
|
-
return gui.interaction.getMode() == 'vertices';
|
|
10211
|
-
}
|
|
10212
|
-
|
|
10213
|
-
function dragging() {
|
|
10214
|
-
return active() && !!dragInfo;
|
|
10215
|
-
}
|
|
10216
|
-
|
|
10217
|
-
function setHoverVertex(id) {
|
|
10218
|
-
var target = hit.getHitTarget();
|
|
10219
|
-
hit.setHoverVertex(target.arcs.getVertex2(id));
|
|
10220
|
-
}
|
|
10221
|
-
|
|
10222
|
-
function clearHoverVertex() {
|
|
10223
|
-
hit.clearHoverVertex();
|
|
10224
|
-
}
|
|
10225
|
-
|
|
10226
|
-
// return data on the nearest vertex (or identical vertices) to the pointer
|
|
10227
|
-
// (if within a distance threshold)
|
|
10228
|
-
//
|
|
10229
|
-
function findDraggableVertices(e) {
|
|
10230
|
-
var target = hit.getHitTarget();
|
|
10231
|
-
var shp = target.layer.shapes[e.id];
|
|
10232
|
-
var p = ext.translatePixelCoords(e.x, e.y);
|
|
10233
|
-
var nearestIds = internal.findNearestVertices(p, shp, target.arcs);
|
|
10234
|
-
var p2 = target.arcs.getVertex2(nearestIds[0]);
|
|
10235
|
-
var dist = geom.distance2D(p[0], p[1], p2[0], p2[1]);
|
|
10236
|
-
var pixelDist = dist / ext.getPixelSize();
|
|
10237
|
-
if (pixelDist > HOVER_THRESHOLD) {
|
|
10238
|
-
return null;
|
|
10239
|
-
}
|
|
10240
|
-
var points = nearestIds.map(function(i) {
|
|
10241
|
-
return getVertexCoords(target, i); // data coordinates
|
|
10242
|
-
});
|
|
10243
|
-
return {
|
|
10244
|
-
target: target,
|
|
10245
|
-
ids: nearestIds,
|
|
10246
|
-
points: points
|
|
10247
|
-
};
|
|
10248
|
-
}
|
|
10249
|
-
|
|
10250
|
-
function findVertexInsertionPoint(e) {
|
|
10251
|
-
var target = hit.getHitTarget();
|
|
10252
|
-
//// vertex insertion not supported with simplification
|
|
10253
|
-
// if (!target.arcs.isFlat()) return null;
|
|
10254
|
-
var p = ext.translatePixelCoords(e.x, e.y);
|
|
10255
|
-
var midpoint = findNearestMidpoint(p, e.id, target);
|
|
10256
|
-
if (!midpoint ||
|
|
10257
|
-
midpoint.distance / ext.getPixelSize() > MIDPOINT_THRESHOLD) return null;
|
|
10258
|
-
return midpoint;
|
|
10259
|
-
}
|
|
10260
|
-
|
|
10261
|
-
hit.on('dragstart', function(e) {
|
|
10262
|
-
if (!active()) return;
|
|
10263
|
-
if (insertionPoint) {
|
|
10264
|
-
var target = hit.getHitTarget();
|
|
10265
|
-
insertVertex$1(target, insertionPoint.i, insertionPoint.point);
|
|
10266
|
-
dragInfo = {
|
|
10267
|
-
target: target,
|
|
10268
|
-
insertion: true,
|
|
10269
|
-
ids: [insertionPoint.i],
|
|
10270
|
-
points: [insertionPoint.point]
|
|
10271
|
-
};
|
|
10272
|
-
insertionPoint = null;
|
|
10273
|
-
} else {
|
|
10274
|
-
dragInfo = findDraggableVertices(e);
|
|
10275
|
-
}
|
|
10276
|
-
if (dragInfo) {
|
|
10277
|
-
setHoverVertex(dragInfo.ids[0]);
|
|
10278
|
-
}
|
|
10279
|
-
});
|
|
10280
|
-
|
|
10281
|
-
hit.on('drag', function(e) {
|
|
10282
|
-
if (!dragging()) return;
|
|
10283
|
-
var target = hit.getHitTarget();
|
|
10284
|
-
var p = ext.translatePixelCoords(e.x, e.y);
|
|
10285
|
-
if (gui.keyboard.shiftIsPressed()) {
|
|
10286
|
-
internal.snapPointToArcEndpoint(p, dragInfo.ids, target.arcs);
|
|
10287
|
-
}
|
|
10288
|
-
internal.snapVerticesToPoint(dragInfo.ids, p, target.arcs);
|
|
10289
|
-
setHoverVertex(dragInfo.ids[0]);
|
|
10290
|
-
// redrawing the whole map updates the data layer as well as the overlay layer
|
|
10291
|
-
// gui.dispatchEvent('map-needs-refresh');
|
|
10292
|
-
});
|
|
10293
|
-
|
|
10294
|
-
hit.on('dragend', function(e) {
|
|
10295
|
-
if (!dragging()) return;
|
|
10296
|
-
// kludge to get dataset to recalculate internal bounding boxes
|
|
10297
|
-
hit.getHitTarget().arcs.transformPoints(function() {});
|
|
10298
|
-
clearHoverVertex();
|
|
10299
|
-
updateVertexCoords(dragInfo.target, dragInfo.ids);
|
|
10300
|
-
gui.dispatchEvent('vertex_dragend', dragInfo);
|
|
10301
|
-
gui.dispatchEvent('map-needs-refresh');
|
|
10302
|
-
dragInfo = null;
|
|
10303
|
-
});
|
|
10304
|
-
|
|
10305
|
-
hit.on('dblclick', function(e) {
|
|
10306
|
-
if (!active()) return;
|
|
10307
|
-
var info = findDraggableVertices(e); // same selection criteria as for dragging
|
|
10308
|
-
if (!info) return;
|
|
10309
|
-
var target = hit.getHitTarget();
|
|
10310
|
-
var vId = info.ids[0];
|
|
10311
|
-
if (internal.vertexIsArcStart(vId, target.arcs) ||
|
|
10312
|
-
internal.vertexIsArcEnd(vId, target.arcs)) {
|
|
10313
|
-
// TODO: support removing arc endpoints
|
|
10314
|
-
return;
|
|
10315
|
-
}
|
|
10316
|
-
gui.dispatchEvent('vertex_delete', {
|
|
10317
|
-
target: target,
|
|
10318
|
-
vertex_id: vId
|
|
10319
|
-
});
|
|
10320
|
-
deleteVertex$1(target, vId);
|
|
10321
|
-
clearHoverVertex();
|
|
10322
|
-
gui.dispatchEvent('map-needs-refresh');
|
|
10323
|
-
});
|
|
10324
|
-
|
|
10325
|
-
// highlight hit vertex in path edit mode
|
|
10326
|
-
hit.on('hover', function(e) {
|
|
10327
|
-
insertionPoint = null;
|
|
10328
|
-
if (!active() || dragging()) return; // no hover effect while dragging
|
|
10329
|
-
var info = findDraggableVertices(e);
|
|
10330
|
-
if (info) {
|
|
10331
|
-
// hovering near a vertex: highlight the vertex
|
|
10332
|
-
setHoverVertex(info.ids[0]);
|
|
10333
|
-
return;
|
|
10334
|
-
}
|
|
10335
|
-
// if hovering near a segment midpoint: show the midpoint and save midpoint info
|
|
10336
|
-
insertionPoint = findVertexInsertionPoint(e);
|
|
10337
|
-
if (insertionPoint) {
|
|
10338
|
-
hit.setHoverVertex(insertionPoint.displayPoint);
|
|
10339
|
-
} else {
|
|
10340
|
-
// pointer is not over a vertex: clear any hover effect
|
|
10341
|
-
clearHoverVertex();
|
|
10342
|
-
}
|
|
10343
|
-
}, null, 100);
|
|
10344
|
-
}
|
|
10345
|
-
|
|
10346
|
-
|
|
10347
|
-
// Given a location @p (e.g. corresponding to the mouse pointer location),
|
|
10348
|
-
// find the midpoint of two vertices on @shp suitable for inserting a new vertex
|
|
10349
|
-
function findNearestMidpoint(p, fid, target) {
|
|
10350
|
-
var arcs = target.arcs;
|
|
10351
|
-
var shp = target.layer.shapes[fid];
|
|
10352
|
-
var minDist = Infinity, v;
|
|
10353
|
-
internal.forEachSegmentInShape(shp, arcs, function(i, j, xx, yy) {
|
|
10354
|
-
var x1 = xx[i],
|
|
10355
|
-
y1 = yy[i],
|
|
10356
|
-
x2 = xx[j],
|
|
10357
|
-
y2 = yy[j],
|
|
10358
|
-
cx = (x1 + x2) / 2,
|
|
10359
|
-
cy = (y1 + y2) / 2,
|
|
10360
|
-
midpoint = [cx, cy],
|
|
10361
|
-
dist = geom.distance2D(cx, cy, p[0], p[1]);
|
|
10362
|
-
if (dist < minDist) {
|
|
10363
|
-
minDist = dist;
|
|
10364
|
-
v = {
|
|
10365
|
-
i: (i < j ? i : j) + 1, // insertion point
|
|
10366
|
-
segment: [i, j],
|
|
10367
|
-
segmentLen: geom.distance2D(x1, y1, x2, y2),
|
|
10368
|
-
displayPoint: midpoint,
|
|
10369
|
-
point: translateDisplayPoint(target, midpoint),
|
|
10370
|
-
distance: dist
|
|
10371
|
-
};
|
|
10372
|
-
}
|
|
10373
|
-
});
|
|
10374
|
-
return v || null;
|
|
10375
|
-
}
|
|
10376
|
-
|
|
10377
10380
|
function initInteractiveEditing(gui, ext, hit) {
|
|
10378
10381
|
initLabelDragging(gui, ext, hit);
|
|
10379
10382
|
initPointDragging(gui, ext, hit);
|
|
10380
10383
|
initPointDrawing(gui, ext, hit);
|
|
10381
10384
|
initLineEditing(gui, ext, hit);
|
|
10382
|
-
initVertexDragging(gui, ext, hit);
|
|
10383
10385
|
}
|
|
10384
10386
|
|
|
10385
10387
|
function MapExtent(_position) {
|
|
@@ -10753,47 +10755,46 @@
|
|
|
10753
10755
|
}
|
|
10754
10756
|
|
|
10755
10757
|
// TODO: consider moving this upstream
|
|
10756
|
-
function getArcsForRendering(
|
|
10757
|
-
var dataset =
|
|
10758
|
+
function getArcsForRendering(lyr, ext) {
|
|
10759
|
+
var dataset = lyr.gui.source.dataset;
|
|
10758
10760
|
var sourceArcs = dataset.arcs;
|
|
10759
|
-
if (
|
|
10760
|
-
return dataset.displayArcs.getScaledArcs(ext);
|
|
10761
|
+
if (lyr.gui.geographic && dataset.gui?.displayArcs) {
|
|
10762
|
+
return dataset.gui.displayArcs.getScaledArcs(ext);
|
|
10761
10763
|
}
|
|
10762
|
-
return
|
|
10764
|
+
return lyr.gui.displayArcs;
|
|
10763
10765
|
}
|
|
10764
10766
|
|
|
10765
|
-
function drawOutlineLayerToCanvas(
|
|
10767
|
+
function drawOutlineLayerToCanvas(lyr, canv, ext) {
|
|
10766
10768
|
var arcs;
|
|
10767
|
-
var style =
|
|
10769
|
+
var style = lyr.gui.style;
|
|
10770
|
+
var arcCounts = lyr.gui.arcCounts;
|
|
10768
10771
|
var darkStyle = {strokeWidth: style.strokeWidth, strokeColor: style.strokeColors[1]},
|
|
10769
10772
|
lightStyle = {strokeWidth: style.strokeWidth, strokeColor: style.strokeColors[0]};
|
|
10770
10773
|
var filter;
|
|
10771
|
-
if (internal.layerHasPaths(
|
|
10772
|
-
if (!
|
|
10773
|
-
|
|
10774
|
-
internal.countArcsInShapes(
|
|
10774
|
+
if (internal.layerHasPaths(lyr.gui.displayLayer)) {
|
|
10775
|
+
if (!arcCounts) {
|
|
10776
|
+
arcCounts = lyr.gui.arcCounts = new Uint8Array(lyr.gui.displayArcs.size());
|
|
10777
|
+
internal.countArcsInShapes(lyr.gui.displayLayer.shapes, arcCounts);
|
|
10775
10778
|
}
|
|
10776
|
-
|
|
10777
|
-
|
|
10778
|
-
|
|
10779
|
-
|
|
10780
|
-
|
|
10781
|
-
|
|
10782
|
-
|
|
10783
|
-
|
|
10784
|
-
canv.drawArcs(arcs, darkStyle, filter);
|
|
10785
|
-
}
|
|
10779
|
+
arcs = getArcsForRendering(lyr, ext);
|
|
10780
|
+
if (lightStyle.strokeColor) {
|
|
10781
|
+
filter = getArcFilter(arcs, ext, false, arcCounts);
|
|
10782
|
+
canv.drawArcs(arcs, lightStyle, filter);
|
|
10783
|
+
}
|
|
10784
|
+
if (darkStyle.strokeColor && lyr.gui.displayLayer.geometry_type != 'point') {
|
|
10785
|
+
filter = getArcFilter(arcs, ext, true, arcCounts);
|
|
10786
|
+
canv.drawArcs(arcs, darkStyle, filter);
|
|
10786
10787
|
}
|
|
10787
10788
|
}
|
|
10788
|
-
if (
|
|
10789
|
-
canv.drawSquareDots(
|
|
10789
|
+
if (lyr.gui.displayLayer.geometry_type == 'point') {
|
|
10790
|
+
canv.drawSquareDots(lyr.gui.displayLayer.shapes, style);
|
|
10790
10791
|
}
|
|
10791
10792
|
}
|
|
10792
10793
|
|
|
10793
|
-
function drawStyledLayerToCanvas(
|
|
10794
|
+
function drawStyledLayerToCanvas(lyr, canv, ext) {
|
|
10794
10795
|
// TODO: add filter for out-of-view shapes
|
|
10795
|
-
var style =
|
|
10796
|
-
var layer =
|
|
10796
|
+
var style = lyr.gui.style;
|
|
10797
|
+
var layer = lyr.gui.displayLayer;
|
|
10797
10798
|
var arcs, filter;
|
|
10798
10799
|
if (layer.geometry_type == 'point') {
|
|
10799
10800
|
if (style.type == 'styled') {
|
|
@@ -10802,7 +10803,7 @@
|
|
|
10802
10803
|
canv.drawSquareDots(layer.shapes, style);
|
|
10803
10804
|
}
|
|
10804
10805
|
} else {
|
|
10805
|
-
arcs = getArcsForRendering(
|
|
10806
|
+
arcs = getArcsForRendering(lyr, ext);
|
|
10806
10807
|
filter = getShapeFilter(arcs, ext);
|
|
10807
10808
|
canv.drawStyledPaths(layer.shapes, arcs, style, filter);
|
|
10808
10809
|
if (style.vertices) {
|
|
@@ -11396,12 +11397,12 @@
|
|
|
11396
11397
|
}
|
|
11397
11398
|
};
|
|
11398
11399
|
|
|
11399
|
-
el.reposition = function(
|
|
11400
|
+
el.reposition = function(lyr, type) {
|
|
11400
11401
|
resize(ext);
|
|
11401
|
-
reposition(
|
|
11402
|
+
reposition(lyr, type, ext);
|
|
11402
11403
|
};
|
|
11403
11404
|
|
|
11404
|
-
el.drawLayer = function(
|
|
11405
|
+
el.drawLayer = function(lyr, type) {
|
|
11405
11406
|
var g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
11406
11407
|
var html = '';
|
|
11407
11408
|
// generate a unique id so layer can be identified when symbols are repositioned
|
|
@@ -11409,24 +11410,25 @@
|
|
|
11409
11410
|
var id = utils$1.getUniqueName();
|
|
11410
11411
|
var classNames = [id, 'mapshaper-svg-layer', 'mapshaper-' + type + '-layer'];
|
|
11411
11412
|
g.setAttribute('class', classNames.join(' '));
|
|
11412
|
-
|
|
11413
|
+
lyr.gui.svg_id = id;
|
|
11414
|
+
lyr.gui.svg_container = g;
|
|
11413
11415
|
resize(ext);
|
|
11414
11416
|
if (type == 'label' || type == 'symbol') {
|
|
11415
|
-
html = renderSymbols(
|
|
11417
|
+
html = renderSymbols(lyr.gui.displayLayer, ext);
|
|
11416
11418
|
} else if (type == 'furniture') {
|
|
11417
|
-
html = renderFurniture(
|
|
11419
|
+
html = renderFurniture(lyr.gui.displayLayer, ext);
|
|
11418
11420
|
}
|
|
11419
11421
|
g.innerHTML = html;
|
|
11420
11422
|
svg.append(g);
|
|
11421
11423
|
|
|
11422
11424
|
// prevent svg hit detection on inactive layers
|
|
11423
|
-
if (!
|
|
11425
|
+
if (!lyr.active) {
|
|
11424
11426
|
g.style.pointerEvents = 'none';
|
|
11425
11427
|
}
|
|
11426
11428
|
};
|
|
11427
11429
|
|
|
11428
|
-
function reposition(
|
|
11429
|
-
var container = el.findChild('.' +
|
|
11430
|
+
function reposition(lyr, type, ext) {
|
|
11431
|
+
var container = el.findChild('.' + lyr.gui.svg_id);
|
|
11430
11432
|
if (!container || !container.node()) {
|
|
11431
11433
|
console.error('[reposition] missing SVG container');
|
|
11432
11434
|
return;
|
|
@@ -11434,9 +11436,9 @@
|
|
|
11434
11436
|
var elements;
|
|
11435
11437
|
if (type == 'symbol') {
|
|
11436
11438
|
elements = El.findAll('.mapshaper-svg-symbol', container.node());
|
|
11437
|
-
repositionSymbols(elements,
|
|
11439
|
+
repositionSymbols(elements, lyr.gui.displayLayer, ext);
|
|
11438
11440
|
} else if (type == 'furniture') {
|
|
11439
|
-
repositionFurniture(container.node(),
|
|
11441
|
+
repositionFurniture(container.node(), lyr.gui.displayLayer, ext);
|
|
11440
11442
|
} else {
|
|
11441
11443
|
// container.getElementsByTagName('text')
|
|
11442
11444
|
error('Unsupported symbol type:', type);
|
|
@@ -11470,7 +11472,7 @@
|
|
|
11470
11472
|
_svg.clear();
|
|
11471
11473
|
}
|
|
11472
11474
|
layers.forEach(function(lyr) {
|
|
11473
|
-
var isSvgLayer = internal.layerHasSvgSymbols(lyr
|
|
11475
|
+
var isSvgLayer = internal.layerHasSvgSymbols(lyr) || internal.layerHasLabels(lyr);
|
|
11474
11476
|
if (isSvgLayer && !needSvgRedraw) {
|
|
11475
11477
|
_svg.reposition(lyr, 'symbol');
|
|
11476
11478
|
} else if (isSvgLayer) {
|
|
@@ -11519,7 +11521,7 @@
|
|
|
11519
11521
|
|
|
11520
11522
|
function drawCanvasLayer(lyr, canv) {
|
|
11521
11523
|
if (!lyr) return;
|
|
11522
|
-
if (lyr.style.type == 'outline') {
|
|
11524
|
+
if (lyr.gui.style.type == 'outline') {
|
|
11523
11525
|
drawOutlineLayerToCanvas(lyr, canv, ext);
|
|
11524
11526
|
} else {
|
|
11525
11527
|
drawStyledLayerToCanvas(lyr, canv, ext);
|
|
@@ -11587,8 +11589,8 @@
|
|
|
11587
11589
|
gui.on('interaction_mode_change', function(e) {
|
|
11588
11590
|
if (e.mode === 'box') {
|
|
11589
11591
|
gui.enterMode('box_tool');
|
|
11590
|
-
} else if (
|
|
11591
|
-
|
|
11592
|
+
} else if (_on) {
|
|
11593
|
+
turnOff();
|
|
11592
11594
|
}
|
|
11593
11595
|
});
|
|
11594
11596
|
|
|
@@ -11691,8 +11693,8 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
11691
11693
|
gui.on('interaction_mode_change', function(e) {
|
|
11692
11694
|
if (e.mode === 'rectangles') {
|
|
11693
11695
|
gui.enterMode('rectangle_tool');
|
|
11694
|
-
} else if (
|
|
11695
|
-
|
|
11696
|
+
} else if (_on) {
|
|
11697
|
+
turnOff();
|
|
11696
11698
|
}
|
|
11697
11699
|
});
|
|
11698
11700
|
|
|
@@ -11702,8 +11704,8 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
11702
11704
|
var id = e.id;
|
|
11703
11705
|
if (e.id > -1 && e.pinned) {
|
|
11704
11706
|
var target = hit.getHitTarget();
|
|
11705
|
-
var path = target.
|
|
11706
|
-
var bbox = target.
|
|
11707
|
+
var path = target.shapes[e.id][0];
|
|
11708
|
+
var bbox = target.gui.displayArcs.getSimpleShapeBounds(path).toArray();
|
|
11707
11709
|
box.setDataCoords(bbox);
|
|
11708
11710
|
dragInfo = {
|
|
11709
11711
|
id: e.id,
|
|
@@ -11711,7 +11713,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
11711
11713
|
ids: [],
|
|
11712
11714
|
points: []
|
|
11713
11715
|
};
|
|
11714
|
-
var iter = target.
|
|
11716
|
+
var iter = target.gui.displayArcs.getShapeIter(path);
|
|
11715
11717
|
while (iter.hasNext()) {
|
|
11716
11718
|
dragInfo.points.push([iter.x, iter.y]);
|
|
11717
11719
|
dragInfo.ids.push(iter._arc.i);
|
|
@@ -11810,8 +11812,9 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
11810
11812
|
};
|
|
11811
11813
|
}
|
|
11812
11814
|
|
|
11813
|
-
function getDisplayLayerForTable(
|
|
11814
|
-
var
|
|
11815
|
+
function getDisplayLayerForTable(tableArg) {
|
|
11816
|
+
var table = tableArg || new internal.DataTable(0),
|
|
11817
|
+
n = table.size(),
|
|
11815
11818
|
cellWidth = 12,
|
|
11816
11819
|
cellHeight = 5,
|
|
11817
11820
|
gutter = 6,
|
|
@@ -11869,54 +11872,45 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
11869
11872
|
};
|
|
11870
11873
|
}
|
|
11871
11874
|
|
|
11875
|
+
// lyr: a map layer with gui property
|
|
11872
11876
|
// displayCRS: CRS to use for display, or null (which clears any current display CRS)
|
|
11873
|
-
function
|
|
11874
|
-
var crsInfo = getDatasetCrsInfo(lyr.source.dataset);
|
|
11875
|
-
var sourceCRS = crsInfo.crs;
|
|
11876
|
-
|
|
11877
|
-
|
|
11878
|
-
|
|
11879
|
-
if (
|
|
11880
|
-
return
|
|
11881
|
-
}
|
|
11882
|
-
|
|
11883
|
-
|
|
11884
|
-
}
|
|
11885
|
-
lyr2 = getDisplayLayer(lyr.source.layer, lyr.source.dataset, {crs: displayCRS});
|
|
11886
|
-
// kludge: copy projection-related properties to original layer
|
|
11887
|
-
lyr.dynamic_crs = lyr2.dynamic_crs;
|
|
11888
|
-
lyr.layer = lyr2.layer;
|
|
11889
|
-
|
|
11890
|
-
if (lyr.style && lyr.style.ids) {
|
|
11877
|
+
function projectLayerForDisplay(lyr, displayCRS) {
|
|
11878
|
+
var crsInfo = getDatasetCrsInfo(lyr.gui.source.dataset);
|
|
11879
|
+
var sourceCRS = crsInfo.crs || null; // let enhanceLayerForDisplay() handle null case
|
|
11880
|
+
if (!lyr.gui.geographic) {
|
|
11881
|
+
return;
|
|
11882
|
+
}
|
|
11883
|
+
if (lyr.gui.dynamic_crs && internal.crsAreEqual(sourceCRS, lyr.gui.dynamic_crs)) {
|
|
11884
|
+
return;
|
|
11885
|
+
}
|
|
11886
|
+
enhanceLayerForDisplay(lyr, lyr.gui.source.dataset, {crs: displayCRS});
|
|
11887
|
+
if (lyr.gui.style?.ids) {
|
|
11891
11888
|
// re-apply layer filter
|
|
11892
|
-
lyr.
|
|
11889
|
+
lyr.gui.displayLayer = filterLayerByIds(lyr.gui.displayLayer, lyr.gui.style.ids);
|
|
11893
11890
|
}
|
|
11894
|
-
lyr.invertPoint = lyr2.invertPoint;
|
|
11895
|
-
lyr.projectPoint = lyr2.projectPoint;
|
|
11896
|
-
lyr.bounds = lyr2.bounds;
|
|
11897
|
-
lyr.arcs = lyr2.arcs;
|
|
11898
11891
|
}
|
|
11899
11892
|
|
|
11900
11893
|
|
|
11901
|
-
//
|
|
11902
|
-
function
|
|
11903
|
-
var
|
|
11904
|
-
|
|
11905
|
-
|
|
11906
|
-
|
|
11894
|
+
// Supplement a layer with information needed for rendering
|
|
11895
|
+
function enhanceLayerForDisplay(layer, dataset, opts) {
|
|
11896
|
+
var gui = {
|
|
11897
|
+
empty: internal.getFeatureCount(layer) === 0,
|
|
11898
|
+
geographic: false,
|
|
11899
|
+
displayArcs: null,
|
|
11900
|
+
displayLayer: null,
|
|
11901
|
+
source: {dataset},
|
|
11902
|
+
bounds: null,
|
|
11907
11903
|
style: null,
|
|
11904
|
+
dynamic_crs: null,
|
|
11908
11905
|
invertPoint: null,
|
|
11909
|
-
projectPoint: null
|
|
11910
|
-
source: {
|
|
11911
|
-
layer: layer,
|
|
11912
|
-
dataset: dataset
|
|
11913
|
-
},
|
|
11914
|
-
empty: internal.getFeatureCount(layer) === 0
|
|
11906
|
+
projectPoint: null
|
|
11915
11907
|
};
|
|
11916
11908
|
|
|
11917
11909
|
var displayCRS = opts.crs || null;
|
|
11918
|
-
// display arcs may have been generated when another layer in the dataset
|
|
11919
|
-
|
|
11910
|
+
// display arcs may have been generated when another layer in the dataset
|
|
11911
|
+
// was converted for display... re-use if available
|
|
11912
|
+
var displayArcs = dataset.gui?.displayArcs;
|
|
11913
|
+
var unprojectable = false;
|
|
11920
11914
|
var sourceCRS;
|
|
11921
11915
|
var emptyArcs;
|
|
11922
11916
|
|
|
@@ -11924,8 +11918,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
11924
11918
|
var crsInfo = getDatasetCrsInfo(dataset);
|
|
11925
11919
|
if (crsInfo.error) {
|
|
11926
11920
|
// unprojectable dataset -- return empty layer
|
|
11927
|
-
|
|
11928
|
-
obj.geographic = true;
|
|
11921
|
+
gui.unprojectable = true;
|
|
11929
11922
|
} else {
|
|
11930
11923
|
sourceCRS = crsInfo.crs;
|
|
11931
11924
|
}
|
|
@@ -11933,7 +11926,8 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
11933
11926
|
|
|
11934
11927
|
// Assume that dataset.displayArcs is in the display CRS
|
|
11935
11928
|
// (it must be deleted upstream if reprojection is needed)
|
|
11936
|
-
if (!obj.empty && dataset.arcs && !displayArcs) {
|
|
11929
|
+
// if (!obj.empty && dataset.arcs && !displayArcs) {
|
|
11930
|
+
if (dataset.arcs && !displayArcs && !gui.unprojectable) {
|
|
11937
11931
|
// project arcs, if needed
|
|
11938
11932
|
if (needReprojectionForDisplay(sourceCRS, displayCRS)) {
|
|
11939
11933
|
displayArcs = projectArcsForDisplay(dataset.arcs, sourceCRS, displayCRS);
|
|
@@ -11943,50 +11937,46 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
11943
11937
|
}
|
|
11944
11938
|
|
|
11945
11939
|
enhanceArcCollectionForDisplay(displayArcs);
|
|
11946
|
-
dataset.
|
|
11940
|
+
dataset.gui = {displayArcs}; // stash these in the dataset for other layers to use
|
|
11947
11941
|
}
|
|
11948
11942
|
|
|
11949
11943
|
if (internal.layerHasFurniture(layer)) {
|
|
11950
|
-
obj.layer = layer;
|
|
11951
|
-
obj.tabular = true;
|
|
11952
11944
|
// TODO: consider how to render furniture in GUI
|
|
11953
|
-
// obj.furniture = true;
|
|
11954
|
-
// obj.furniture_type = internal.getFurnitureLayerType(layer);
|
|
11955
11945
|
// treating furniture layers (other than frame) as tabular for now,
|
|
11956
11946
|
// so there is something to show if they are selected
|
|
11957
|
-
// obj.tabular = obj.furniture_type != 'frame';
|
|
11958
|
-
} else if (obj.empty) {
|
|
11959
|
-
obj.layer = {shapes: []}; // ideally we should avoid empty layers
|
|
11960
|
-
} else if (!layer.geometry_type) {
|
|
11961
|
-
obj.tabular = true;
|
|
11962
|
-
} else {
|
|
11963
|
-
obj.geographic = true;
|
|
11964
|
-
obj.layer = layer;
|
|
11965
|
-
obj.arcs = displayArcs;
|
|
11966
11947
|
}
|
|
11967
11948
|
|
|
11968
|
-
if (
|
|
11969
|
-
|
|
11949
|
+
if (gui.unprojectable) {
|
|
11950
|
+
gui.displayLayer = {shapes: []}; // TODO: improve
|
|
11951
|
+
} else if (layer.geometry_type) {
|
|
11952
|
+
gui.geographic = true;
|
|
11953
|
+
gui.displayLayer = layer;
|
|
11954
|
+
gui.displayArcs = displayArcs;
|
|
11955
|
+
} else {
|
|
11956
|
+
var table = getDisplayLayerForTable(layer.data);
|
|
11957
|
+
gui.tabular = true;
|
|
11958
|
+
gui.displayLayer = table.layer;
|
|
11959
|
+
gui.displayArcs = table.arcs;
|
|
11970
11960
|
}
|
|
11971
11961
|
|
|
11972
11962
|
// dynamic reprojection (arcs were already reprojected above)
|
|
11973
|
-
if (
|
|
11974
|
-
|
|
11975
|
-
|
|
11976
|
-
|
|
11963
|
+
if (gui.geographic && needReprojectionForDisplay(sourceCRS, displayCRS)) {
|
|
11964
|
+
gui.dynamic_crs = displayCRS;
|
|
11965
|
+
gui.invertPoint = internal.getProjTransform2(displayCRS, sourceCRS);
|
|
11966
|
+
gui.projectPoint = internal.getProjTransform2(sourceCRS, displayCRS);
|
|
11977
11967
|
if (internal.layerHasPoints(layer)) {
|
|
11978
|
-
|
|
11968
|
+
gui.displayLayer = projectPointsForDisplay(layer, sourceCRS, displayCRS);
|
|
11979
11969
|
} else if (internal.layerHasPaths(layer)) {
|
|
11980
11970
|
emptyArcs = findEmptyArcs(displayArcs);
|
|
11981
11971
|
if (emptyArcs.length > 0) {
|
|
11982
11972
|
// Don't try to draw paths containing coordinates that failed to project
|
|
11983
|
-
|
|
11973
|
+
gui.displayLayer = internal.filterPathLayerByArcIds(gui.displayLayer, emptyArcs);
|
|
11984
11974
|
}
|
|
11985
11975
|
}
|
|
11986
11976
|
}
|
|
11987
11977
|
|
|
11988
|
-
|
|
11989
|
-
|
|
11978
|
+
gui.bounds = getDisplayBounds(gui.displayLayer, gui.displayArcs);
|
|
11979
|
+
layer.gui = gui;
|
|
11990
11980
|
}
|
|
11991
11981
|
|
|
11992
11982
|
|
|
@@ -12300,13 +12290,19 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12300
12290
|
|
|
12301
12291
|
model.on('update', onUpdate);
|
|
12302
12292
|
|
|
12293
|
+
document.addEventListener('visibilitychange', function(e) {
|
|
12294
|
+
// refresh map when browser tab is re-activated (Chrome on mac has been
|
|
12295
|
+
// blanking the canvas after several other tabs are visited)
|
|
12296
|
+
if (document.visibilityState == 'visible') drawLayers();
|
|
12297
|
+
});
|
|
12303
12298
|
|
|
12304
12299
|
// Update display of segment intersections
|
|
12305
12300
|
this.setIntersectionLayer = function(lyr, dataset) {
|
|
12306
12301
|
if (lyr == _intersectionLyr) return; // no change
|
|
12307
12302
|
if (lyr) {
|
|
12308
|
-
|
|
12309
|
-
_intersectionLyr
|
|
12303
|
+
enhanceLayerForDisplay(lyr, dataset, getDisplayOptions());
|
|
12304
|
+
_intersectionLyr = lyr;
|
|
12305
|
+
_intersectionLyr.gui.style = getIntersectionStyle(_intersectionLyr.gui.displayLayer, getGlobalStyleOptions());
|
|
12310
12306
|
} else {
|
|
12311
12307
|
_intersectionLyr = null;
|
|
12312
12308
|
}
|
|
@@ -12335,9 +12331,9 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12335
12331
|
};
|
|
12336
12332
|
|
|
12337
12333
|
this.getDisplayCRS = function() {
|
|
12338
|
-
if (!_activeLyr || !_activeLyr.geographic) return null;
|
|
12339
|
-
if (_activeLyr.dynamic_crs) return _activeLyr.dynamic_crs;
|
|
12340
|
-
var info = getDatasetCrsInfo(_activeLyr.source.dataset);
|
|
12334
|
+
if (!_activeLyr || !_activeLyr.gui.geographic) return null;
|
|
12335
|
+
if (_activeLyr.gui.dynamic_crs) return _activeLyr.gui.dynamic_crs;
|
|
12336
|
+
var info = getDatasetCrsInfo(_activeLyr.gui.source.dataset);
|
|
12341
12337
|
return info.crs || null;
|
|
12342
12338
|
};
|
|
12343
12339
|
|
|
@@ -12376,7 +12372,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12376
12372
|
|
|
12377
12373
|
// Reproject all visible map layers
|
|
12378
12374
|
getContentLayers().forEach(function(lyr) {
|
|
12379
|
-
|
|
12375
|
+
projectLayerForDisplay(lyr, newCRS);
|
|
12380
12376
|
});
|
|
12381
12377
|
|
|
12382
12378
|
// kludge to make sure all layers have styles
|
|
@@ -12387,13 +12383,13 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12387
12383
|
updateFullBounds();
|
|
12388
12384
|
};
|
|
12389
12385
|
|
|
12390
|
-
function getGlobalStyleOptions() {
|
|
12386
|
+
function getGlobalStyleOptions(opts) {
|
|
12391
12387
|
var mode = gui.state.interaction_mode;
|
|
12392
|
-
return {
|
|
12388
|
+
return Object.assign({
|
|
12393
12389
|
darkMode: !!gui.state.dark_basemap,
|
|
12394
12390
|
outlineMode: mode == 'vertices',
|
|
12395
12391
|
interactionMode: mode
|
|
12396
|
-
};
|
|
12392
|
+
}, opts);
|
|
12397
12393
|
}
|
|
12398
12394
|
|
|
12399
12395
|
// Refresh map display in response to data changes, layer selection, etc.
|
|
@@ -12401,14 +12397,13 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12401
12397
|
var prevLyr = _activeLyr || null;
|
|
12402
12398
|
var fullBounds;
|
|
12403
12399
|
var needReset;
|
|
12404
|
-
|
|
12405
12400
|
if (!prevLyr) {
|
|
12406
12401
|
initMap(); // first call
|
|
12407
12402
|
}
|
|
12408
12403
|
|
|
12409
12404
|
if (arcsMayHaveChanged(e.flags)) {
|
|
12410
12405
|
// regenerate filtered arcs the next time they are needed for rendering
|
|
12411
|
-
// delete e.dataset.displayArcs
|
|
12406
|
+
// delete e.dataset.gui.displayArcs
|
|
12412
12407
|
clearAllDisplayArcs();
|
|
12413
12408
|
|
|
12414
12409
|
// reset simplification after projection (thresholds have changed)
|
|
@@ -12427,8 +12422,9 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12427
12422
|
return;
|
|
12428
12423
|
}
|
|
12429
12424
|
|
|
12430
|
-
|
|
12431
|
-
_activeLyr
|
|
12425
|
+
enhanceLayerForDisplay(e.layer, e.dataset, getDisplayOptions());
|
|
12426
|
+
_activeLyr = e.layer;
|
|
12427
|
+
_activeLyr.gui.style = getActiveLayerStyle(_activeLyr.gui.displayLayer, getGlobalStyleOptions());
|
|
12432
12428
|
_activeLyr.active = true;
|
|
12433
12429
|
|
|
12434
12430
|
if (popupCanStayOpen(e.flags)) {
|
|
@@ -12442,7 +12438,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12442
12438
|
updateVisibleMapLayers();
|
|
12443
12439
|
fullBounds = calcFullBounds();
|
|
12444
12440
|
|
|
12445
|
-
if (!prevLyr || prevLyr.tabular || _activeLyr.tabular) {
|
|
12441
|
+
if (!prevLyr || prevLyr.gui.tabular || _activeLyr.gui.tabular) {
|
|
12446
12442
|
needReset = true;
|
|
12447
12443
|
} else {
|
|
12448
12444
|
needReset = mapNeedsReset(fullBounds, _ext.getFullBounds(), _ext.getBounds(), e.flags);
|
|
@@ -12485,12 +12481,11 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12485
12481
|
}
|
|
12486
12482
|
|
|
12487
12483
|
function updateOverlayLayer(e) {
|
|
12488
|
-
var style = getOverlayStyle(_activeLyr.
|
|
12484
|
+
var style = getOverlayStyle(_activeLyr.gui.displayLayer, e, getGlobalStyleOptions());
|
|
12489
12485
|
if (style) {
|
|
12490
|
-
|
|
12491
|
-
|
|
12492
|
-
|
|
12493
|
-
}, _activeLyr);
|
|
12486
|
+
var displayLayer = filterLayerByIds(_activeLyr.gui.displayLayer, style.ids);
|
|
12487
|
+
var gui = Object.assign({}, _activeLyr.gui, {style, displayLayer});
|
|
12488
|
+
_overlayLyr = utils$1.defaults({gui}, _activeLyr);
|
|
12494
12489
|
} else {
|
|
12495
12490
|
_overlayLyr = null;
|
|
12496
12491
|
}
|
|
@@ -12521,7 +12516,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12521
12516
|
var b = new Bounds();
|
|
12522
12517
|
var layers = getContentLayers();
|
|
12523
12518
|
layers.forEach(function(lyr) {
|
|
12524
|
-
b.mergeBounds(lyr.bounds);
|
|
12519
|
+
b.mergeBounds(lyr.gui.bounds);
|
|
12525
12520
|
});
|
|
12526
12521
|
|
|
12527
12522
|
if (!b.hasBounds()) {
|
|
@@ -12544,7 +12539,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12544
12539
|
var widthPx = _ext.width();
|
|
12545
12540
|
var marginPct = widthPx < 700 && 3.5 || widthPx < 800 && 3 || 2.5;
|
|
12546
12541
|
if (isTableView()) {
|
|
12547
|
-
var n = internal.getFeatureCount(_activeLyr
|
|
12542
|
+
var n = internal.getFeatureCount(_activeLyr);
|
|
12548
12543
|
marginPct = n < 5 && 20 || n < 100 && 10 || 4;
|
|
12549
12544
|
}
|
|
12550
12545
|
b.scale(1 + marginPct / 100 * 2);
|
|
@@ -12556,7 +12551,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12556
12551
|
}
|
|
12557
12552
|
|
|
12558
12553
|
function isActiveLayer(lyr) {
|
|
12559
|
-
return _activeLyr && lyr == _activeLyr
|
|
12554
|
+
return _activeLyr && lyr == _activeLyr || false;
|
|
12560
12555
|
}
|
|
12561
12556
|
|
|
12562
12557
|
function isVisibleLayer(lyr) {
|
|
@@ -12564,12 +12559,12 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12564
12559
|
}
|
|
12565
12560
|
|
|
12566
12561
|
function isTableView() {
|
|
12567
|
-
return !!_activeLyr.tabular;
|
|
12562
|
+
return !!_activeLyr.gui.tabular;
|
|
12568
12563
|
}
|
|
12569
12564
|
|
|
12570
12565
|
function findFrameLayer() {
|
|
12571
12566
|
return getVisibleMapLayers().find(function(lyr) {
|
|
12572
|
-
return internal.isFrameLayer(lyr.
|
|
12567
|
+
return internal.isFrameLayer(lyr.gui.displayLayer, lyr.gui.displayArcs);
|
|
12573
12568
|
});
|
|
12574
12569
|
}
|
|
12575
12570
|
|
|
@@ -12580,12 +12575,15 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12580
12575
|
|
|
12581
12576
|
function getFrameData() {
|
|
12582
12577
|
var lyr = findFrameLayer();
|
|
12583
|
-
return lyr && internal.getFrameLayerData(lyr
|
|
12578
|
+
return lyr && internal.getFrameLayerData(lyr, lyr.gui.displayArcs) || null;
|
|
12584
12579
|
}
|
|
12585
12580
|
|
|
12586
12581
|
function clearAllDisplayArcs() {
|
|
12582
|
+
model.forEachLayer(function(lyr) {
|
|
12583
|
+
if (lyr.gui) delete lyr.gui.arcCounts;
|
|
12584
|
+
});
|
|
12587
12585
|
model.getDatasets().forEach(function(o) {
|
|
12588
|
-
delete o.
|
|
12586
|
+
delete o.gui;
|
|
12589
12587
|
});
|
|
12590
12588
|
}
|
|
12591
12589
|
|
|
@@ -12596,7 +12594,8 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12596
12594
|
if (isActiveLayer(o.layer)) {
|
|
12597
12595
|
layers.push(_activeLyr);
|
|
12598
12596
|
} else if (!isTableView()) {
|
|
12599
|
-
|
|
12597
|
+
enhanceLayerForDisplay(o.layer, o.dataset, getDisplayOptions());
|
|
12598
|
+
layers.push(o.layer);
|
|
12600
12599
|
}
|
|
12601
12600
|
});
|
|
12602
12601
|
_visibleLayers = layers;
|
|
@@ -12618,13 +12617,13 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12618
12617
|
return findActiveLayer(layers);
|
|
12619
12618
|
}
|
|
12620
12619
|
return layers.filter(function(o) {
|
|
12621
|
-
return !!o.geographic;
|
|
12620
|
+
return !!o.gui.geographic;
|
|
12622
12621
|
});
|
|
12623
12622
|
}
|
|
12624
12623
|
|
|
12625
12624
|
function getDrawableContentLayers() {
|
|
12626
12625
|
return getContentLayers().filter(function(lyr) {
|
|
12627
|
-
if (isActiveLayer(lyr
|
|
12626
|
+
if (isActiveLayer(lyr) && lyr.hidden) return false;
|
|
12628
12627
|
return true;
|
|
12629
12628
|
});
|
|
12630
12629
|
}
|
|
@@ -12638,31 +12637,33 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12638
12637
|
|
|
12639
12638
|
function updateLayerStyles(layers) {
|
|
12640
12639
|
layers.forEach(function(mapLayer, i) {
|
|
12640
|
+
var style;
|
|
12641
12641
|
if (mapLayer.active) {
|
|
12642
12642
|
// regenerating active style everytime, to support style change when
|
|
12643
12643
|
// switching between outline and preview modes.
|
|
12644
|
-
|
|
12645
|
-
if (
|
|
12644
|
+
style = getActiveLayerStyle(mapLayer.gui.displayLayer, getGlobalStyleOptions());
|
|
12645
|
+
if (style.type != 'styled' && layers.length > 1 && style.strokeColors) {
|
|
12646
12646
|
// kludge to hide ghosted layers when reference layers are present
|
|
12647
12647
|
// TODO: consider never showing ghosted layers (which appear after
|
|
12648
12648
|
// commands like dissolve and filter).
|
|
12649
|
-
|
|
12650
|
-
strokeColors: [null,
|
|
12651
|
-
},
|
|
12649
|
+
style = utils$1.defaults({
|
|
12650
|
+
strokeColors: [null, style.strokeColors[1]]
|
|
12651
|
+
}, style);
|
|
12652
12652
|
}
|
|
12653
12653
|
} else {
|
|
12654
|
-
if (mapLayer
|
|
12654
|
+
if (mapLayer == _activeLyr) {
|
|
12655
12655
|
console.error("Error: shared map layer");
|
|
12656
12656
|
}
|
|
12657
|
-
|
|
12657
|
+
style = getReferenceLayerStyle(mapLayer.gui.displayLayer, getGlobalStyleOptions());
|
|
12658
12658
|
}
|
|
12659
|
+
mapLayer.gui.style = style;
|
|
12659
12660
|
});
|
|
12660
12661
|
}
|
|
12661
12662
|
|
|
12662
12663
|
function sortMapLayers(layers) {
|
|
12663
12664
|
layers.sort(function(a, b) {
|
|
12664
12665
|
// assume that each layer has a menu_order (assigned by updateLayerStackOrder())
|
|
12665
|
-
return a.
|
|
12666
|
+
return a.menu_order - b.menu_order;
|
|
12666
12667
|
});
|
|
12667
12668
|
}
|
|
12668
12669
|
|
|
@@ -12742,6 +12743,99 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12742
12743
|
});
|
|
12743
12744
|
}
|
|
12744
12745
|
|
|
12746
|
+
function ContextMenu() {
|
|
12747
|
+
var body = document.querySelector('body');
|
|
12748
|
+
var menu = El('div').addClass('contextmenu rollover').appendTo(body);
|
|
12749
|
+
var _open = false;
|
|
12750
|
+
var _openCount = 0;
|
|
12751
|
+
document.addEventListener('mousedown', close);
|
|
12752
|
+
|
|
12753
|
+
this.isOpen = function() {
|
|
12754
|
+
return _open;
|
|
12755
|
+
};
|
|
12756
|
+
|
|
12757
|
+
function close() {
|
|
12758
|
+
var count = _openCount;
|
|
12759
|
+
if (!_open) return;
|
|
12760
|
+
setTimeout(function() {
|
|
12761
|
+
if (count == _openCount) {
|
|
12762
|
+
menu.hide();
|
|
12763
|
+
_open = false;
|
|
12764
|
+
}
|
|
12765
|
+
}, 300);
|
|
12766
|
+
}
|
|
12767
|
+
|
|
12768
|
+
function addMenuItem(label, func) {
|
|
12769
|
+
El('div')
|
|
12770
|
+
.appendTo(menu)
|
|
12771
|
+
.addClass('contextmenu-item')
|
|
12772
|
+
.text(label)
|
|
12773
|
+
.on('click', func)
|
|
12774
|
+
.show();
|
|
12775
|
+
}
|
|
12776
|
+
|
|
12777
|
+
this.open = function(e, lyr) {
|
|
12778
|
+
_open = true;
|
|
12779
|
+
_openCount++;
|
|
12780
|
+
var rspace = body.clientWidth - e.pageX;
|
|
12781
|
+
var xoffs = 10;
|
|
12782
|
+
menu.empty().show();
|
|
12783
|
+
if (rspace > 150) {
|
|
12784
|
+
menu.css('left', e.pageX + xoffs + 'px');
|
|
12785
|
+
} else {
|
|
12786
|
+
menu.css('right', (body.clientWidth - e.pageX + xoffs) + 'px');
|
|
12787
|
+
}
|
|
12788
|
+
menu.css('top', (e.pageY - 15) + 'px');
|
|
12789
|
+
|
|
12790
|
+
// menu contents
|
|
12791
|
+
if (e.deleteVertex) {
|
|
12792
|
+
addMenuItem('Delete vertex', e.deleteVertex);
|
|
12793
|
+
}
|
|
12794
|
+
if (e.coordinates) {
|
|
12795
|
+
addCopyCoords();
|
|
12796
|
+
}
|
|
12797
|
+
if (e.ids?.length) {
|
|
12798
|
+
addMenuItem('Copy as GeoJSON', copyGeoJSON);
|
|
12799
|
+
}
|
|
12800
|
+
|
|
12801
|
+
function addCopyCoords() {
|
|
12802
|
+
var bbox = internal.getLayerBounds(lyr, lyr.gui.source.dataset.arcs).toArray();
|
|
12803
|
+
var p = internal.getRoundedCoords(e.coordinates, internal.getBoundsPrecisionForDisplay(bbox));
|
|
12804
|
+
var coordStr = p.join(',');
|
|
12805
|
+
addMenuItem(coordStr, function() {
|
|
12806
|
+
saveFileContentToClipboard(coordStr);
|
|
12807
|
+
});
|
|
12808
|
+
}
|
|
12809
|
+
|
|
12810
|
+
function copyGeoJSON() {
|
|
12811
|
+
var opts = {
|
|
12812
|
+
no_replace: true,
|
|
12813
|
+
ids: e.ids,
|
|
12814
|
+
quiet: true
|
|
12815
|
+
};
|
|
12816
|
+
var dataset = lyr.gui.source.dataset;
|
|
12817
|
+
var layer = mapshaper.cmd.filterFeatures(lyr, dataset.arcs, opts);
|
|
12818
|
+
// the drawing tool can send open paths with 'polygon' geometry type,
|
|
12819
|
+
// should be changed to 'polyline'
|
|
12820
|
+
if (layerHasOpenPaths(layer, dataset.arcs)) {
|
|
12821
|
+
layer.geometry_type = 'polyline';
|
|
12822
|
+
}
|
|
12823
|
+
var features = internal.exportLayerAsGeoJSON(layer, dataset, {rfc7946: true, prettify: true}, true, 'string');
|
|
12824
|
+
var str = internal.geojson.formatCollection({"type": "FeatureCollection"}, features);
|
|
12825
|
+
saveFileContentToClipboard(str);
|
|
12826
|
+
}
|
|
12827
|
+
};
|
|
12828
|
+
}
|
|
12829
|
+
|
|
12830
|
+
|
|
12831
|
+
function layerHasOpenPaths(layer, arcs) {
|
|
12832
|
+
var retn = false;
|
|
12833
|
+
internal.editShapes(layer.shapes, function(part) {
|
|
12834
|
+
if (!geom.pathIsClosed(part, arcs)) retn = true;
|
|
12835
|
+
});
|
|
12836
|
+
return retn;
|
|
12837
|
+
}
|
|
12838
|
+
|
|
12745
12839
|
function GuiInstance(container, opts) {
|
|
12746
12840
|
var gui = new ModeSwitcher();
|
|
12747
12841
|
opts = utils$1.extend({
|
|
@@ -12760,15 +12854,15 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12760
12854
|
gui.model = new Model(gui);
|
|
12761
12855
|
gui.keyboard = new KeyboardEvents(gui);
|
|
12762
12856
|
gui.buttons = new SidebarButtons(gui);
|
|
12763
|
-
gui.map = new MshpMap(gui);
|
|
12764
|
-
gui.interaction = new InteractionMode(gui);
|
|
12765
12857
|
gui.session = new SessionHistory(gui);
|
|
12858
|
+
gui.contextMenu = new ContextMenu();
|
|
12766
12859
|
gui.undo = new Undo(gui);
|
|
12767
|
-
gui.
|
|
12768
|
-
|
|
12860
|
+
gui.map = new MshpMap(gui);
|
|
12769
12861
|
if (opts.saveControl) {
|
|
12770
12862
|
new SessionSnapshots(gui);
|
|
12771
12863
|
}
|
|
12864
|
+
gui.interaction = new InteractionMode(gui);
|
|
12865
|
+
gui.state = {};
|
|
12772
12866
|
|
|
12773
12867
|
var msgCount = 0;
|
|
12774
12868
|
var clearMsg;
|