mapshaper 0.6.63 → 0.6.66
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 +601 -385
- package/package.json +1 -1
- package/www/index.html +4 -3
- package/www/mapshaper-gui.js +495 -245
- package/www/mapshaper.js +601 -385
- package/www/page.css +16 -1
package/www/mapshaper-gui.js
CHANGED
|
@@ -1714,6 +1714,47 @@
|
|
|
1714
1714
|
}
|
|
1715
1715
|
}
|
|
1716
1716
|
|
|
1717
|
+
function openAddLayerPopup(gui) {
|
|
1718
|
+
var popup = showPopupAlert('', 'Add empty layer');
|
|
1719
|
+
var el = popup.container();
|
|
1720
|
+
el.addClass('option-menu');
|
|
1721
|
+
var html = `<div><input type="text" class="layer-name text-input" placeholder="layer name"></div>
|
|
1722
|
+
<div style="margin: 2px 0 4px;">
|
|
1723
|
+
Type:
|
|
1724
|
+
<label><input type="radio" name="geomtype" checked value="point" class="radio">point</label>
|
|
1725
|
+
<label><input type="radio" name="geomtype" value="polygon" class="radio">polygon</label>
|
|
1726
|
+
<label><input type="radio" name="geomtype" value="polyline" class="radio">line</label>
|
|
1727
|
+
</div>
|
|
1728
|
+
<div tabindex="0" class="btn dialog-btn">Create</div></span>`;
|
|
1729
|
+
el.html(html);
|
|
1730
|
+
var name = el.findChild('.layer-name');
|
|
1731
|
+
name.node().focus();
|
|
1732
|
+
var btn = el.findChild('.btn').on('click', function() {
|
|
1733
|
+
var nameStr = name.node().value.trim();
|
|
1734
|
+
var type = el.findChild('input:checked').node().value;
|
|
1735
|
+
addLayer(gui, nameStr, type);
|
|
1736
|
+
popup.close();
|
|
1737
|
+
});
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
function addLayer(gui, name, type) {
|
|
1741
|
+
var targ = gui.model.getActiveLayer();
|
|
1742
|
+
var crsInfo = targ && internal.getDatasetCrsInfo(targ.dataset);
|
|
1743
|
+
var dataset = {
|
|
1744
|
+
layers: [{
|
|
1745
|
+
name: name || undefined,
|
|
1746
|
+
geometry_type: type,
|
|
1747
|
+
shapes: []
|
|
1748
|
+
}],
|
|
1749
|
+
info: {}
|
|
1750
|
+
};
|
|
1751
|
+
if (crsInfo) {
|
|
1752
|
+
internal.setDatasetCrsInfo(dataset, crsInfo);
|
|
1753
|
+
}
|
|
1754
|
+
gui.model.addDataset(dataset);
|
|
1755
|
+
gui.model.updated({select: true});
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1717
1758
|
// @cb function(<FileList>)
|
|
1718
1759
|
function DropControl(gui, el, cb) {
|
|
1719
1760
|
var area = El(el);
|
|
@@ -1832,7 +1873,10 @@
|
|
|
1832
1873
|
new FileChooser('#file-selection-btn', receiveFiles);
|
|
1833
1874
|
new FileChooser('#import-buttons .add-btn', receiveFiles);
|
|
1834
1875
|
new FileChooser('#add-file-btn', receiveFiles);
|
|
1835
|
-
|
|
1876
|
+
new SimpleButton('#add-empty-btn').on('click', function() {
|
|
1877
|
+
gui.clearMode(); // close import dialog
|
|
1878
|
+
openAddLayerPopup(gui);
|
|
1879
|
+
});
|
|
1836
1880
|
initDropArea('#import-quick-drop', true);
|
|
1837
1881
|
initDropArea('#import-drop');
|
|
1838
1882
|
gui.keyboard.onMenuSubmit(El('#import-options'), importQueuedFiles);
|
|
@@ -2040,18 +2084,7 @@
|
|
|
2040
2084
|
return true;
|
|
2041
2085
|
}
|
|
2042
2086
|
|
|
2043
|
-
|
|
2044
|
-
var dataset = {
|
|
2045
|
-
layers: [{
|
|
2046
|
-
name: 'New layer',
|
|
2047
|
-
geometry_type: 'point',
|
|
2048
|
-
shapes: []
|
|
2049
|
-
}],
|
|
2050
|
-
info: {}
|
|
2051
|
-
};
|
|
2052
|
-
model.addDataset(dataset);
|
|
2053
|
-
gui.clearMode();
|
|
2054
|
-
}
|
|
2087
|
+
|
|
2055
2088
|
|
|
2056
2089
|
function filesMayContainPaths(files) {
|
|
2057
2090
|
return utils$1.some(files, function(f) {
|
|
@@ -2565,6 +2598,7 @@
|
|
|
2565
2598
|
}
|
|
2566
2599
|
}
|
|
2567
2600
|
|
|
2601
|
+
// coords: [x, y] point in data CRS (not display CRS)
|
|
2568
2602
|
function setPointCoords(lyr, fid, coords) {
|
|
2569
2603
|
lyr.source.layer.shapes[fid] = coords;
|
|
2570
2604
|
if (isProjectedLayer(lyr)) {
|
|
@@ -2578,12 +2612,31 @@
|
|
|
2578
2612
|
internal.snapVerticesToPoint(ids, lyr.invertPoint(p[0], p[1]), lyr.source.dataset.arcs, true);
|
|
2579
2613
|
}
|
|
2580
2614
|
|
|
2615
|
+
function setRectangleCoords(lyr, ids, coords) {
|
|
2616
|
+
ids.forEach(function(id, i) {
|
|
2617
|
+
var p = coords[i];
|
|
2618
|
+
internal.snapVerticesToPoint([id], p, lyr.source.dataset.arcs, true);
|
|
2619
|
+
if (isProjectedLayer(lyr)) {
|
|
2620
|
+
internal.snapVerticesToPoint([id], lyr.projectPoint(p[0], p[1]), lyr.arcs, true);
|
|
2621
|
+
}
|
|
2622
|
+
});
|
|
2623
|
+
}
|
|
2624
|
+
|
|
2625
|
+
// lyr: display layer
|
|
2626
|
+
// export function updateRectangleCoords(lyr, ids, coords) {
|
|
2627
|
+
// if (!isProjectedLayer(lyr)) return;
|
|
2628
|
+
// ids.forEach(function(id, i) {
|
|
2629
|
+
// var p = coords[i];
|
|
2630
|
+
// internal.snapVerticesToPoint([id], lyr.invertPoint(p[0], p[1]), lyr.source.dataset.arcs, true);
|
|
2631
|
+
// });
|
|
2632
|
+
// }
|
|
2633
|
+
|
|
2581
2634
|
function isProjectedLayer(lyr) {
|
|
2582
2635
|
// TODO: could do some validation on the layer's contents
|
|
2583
2636
|
return !!(lyr.source && lyr.invertPoint);
|
|
2584
2637
|
}
|
|
2585
2638
|
|
|
2586
|
-
// Update data coordinates by projecting display coordinates
|
|
2639
|
+
// Update source data coordinates by projecting display coordinates
|
|
2587
2640
|
function updatePointCoords(lyr, fid) {
|
|
2588
2641
|
if (!isProjectedLayer(lyr)) return;
|
|
2589
2642
|
var displayShp = lyr.layer.shapes[fid];
|
|
@@ -3856,7 +3909,7 @@
|
|
|
3856
3909
|
async function exportMenuSelection(targets) {
|
|
3857
3910
|
var opts = getExportOpts();
|
|
3858
3911
|
// note: command line "target" option gets ignored
|
|
3859
|
-
var files = await internal.exportTargetLayers(targets, opts);
|
|
3912
|
+
var files = await internal.exportTargetLayers(model, targets, opts);
|
|
3860
3913
|
gui.session.layersExported(getTargetLayerIds(), getExportOptsAsString());
|
|
3861
3914
|
if (files.length == 1 && checkboxOn(clipboardCheckbox)) {
|
|
3862
3915
|
await saveFileContentToClipboard(files[0].content);
|
|
@@ -4637,6 +4690,19 @@
|
|
|
4637
4690
|
addHistoryState(stashedUndo, redo);
|
|
4638
4691
|
});
|
|
4639
4692
|
|
|
4693
|
+
gui.on('rectangle_dragend', function(e) {
|
|
4694
|
+
var target = e.data.target;
|
|
4695
|
+
var points1 = e.points;
|
|
4696
|
+
var points2 = e.ids.map(id => getVertexCoords(target, id));
|
|
4697
|
+
var undo = function() {
|
|
4698
|
+
setRectangleCoords(target, e.ids, points1);
|
|
4699
|
+
};
|
|
4700
|
+
var redo = function() {
|
|
4701
|
+
setRectangleCoords(target, e.ids, points2);
|
|
4702
|
+
};
|
|
4703
|
+
addHistoryState(undo, redo);
|
|
4704
|
+
});
|
|
4705
|
+
|
|
4640
4706
|
gui.on('vertex_dragend', function(e) {
|
|
4641
4707
|
var target = e.data.target;
|
|
4642
4708
|
var startPoint = e.points[0]; // in data coords
|
|
@@ -4682,6 +4748,9 @@
|
|
|
4682
4748
|
}
|
|
4683
4749
|
|
|
4684
4750
|
this.undo = function() {
|
|
4751
|
+
// firing even if history is empty
|
|
4752
|
+
// (because this event may trigger a new history state)
|
|
4753
|
+
gui.dispatchEvent('undo_redo_pre');
|
|
4685
4754
|
var item = getHistoryItem();
|
|
4686
4755
|
if (item) {
|
|
4687
4756
|
offset++;
|
|
@@ -4691,6 +4760,7 @@
|
|
|
4691
4760
|
};
|
|
4692
4761
|
|
|
4693
4762
|
this.redo = function() {
|
|
4763
|
+
gui.dispatchEvent('undo_redo_pre');
|
|
4694
4764
|
if (offset <= 0) return;
|
|
4695
4765
|
offset--;
|
|
4696
4766
|
var item = getHistoryItem();
|
|
@@ -4764,6 +4834,30 @@
|
|
|
4764
4834
|
});
|
|
4765
4835
|
}
|
|
4766
4836
|
|
|
4837
|
+
function ToggleButton(el) {
|
|
4838
|
+
var btn = El(el),
|
|
4839
|
+
self = new EventDispatcher(),
|
|
4840
|
+
on = false;
|
|
4841
|
+
|
|
4842
|
+
btn.on('click', function(e) {
|
|
4843
|
+
on = !on;
|
|
4844
|
+
btn.classed('active', on);
|
|
4845
|
+
self.dispatchEvent('click', {on: on, active: on});
|
|
4846
|
+
});
|
|
4847
|
+
|
|
4848
|
+
self.turnOff = function() {
|
|
4849
|
+
on = false;
|
|
4850
|
+
btn.removeClass('active');
|
|
4851
|
+
};
|
|
4852
|
+
|
|
4853
|
+
self.turnOn = function() {
|
|
4854
|
+
on = true;
|
|
4855
|
+
btn.addClass('active');
|
|
4856
|
+
};
|
|
4857
|
+
|
|
4858
|
+
return self;
|
|
4859
|
+
}
|
|
4860
|
+
|
|
4767
4861
|
function ModeSwitcher() {
|
|
4768
4862
|
var self = this;
|
|
4769
4863
|
var mode = null;
|
|
@@ -4852,11 +4946,11 @@
|
|
|
4852
4946
|
var menus = {
|
|
4853
4947
|
standard: ['info', 'selection', 'data', 'box'],
|
|
4854
4948
|
polygons: ['info', 'selection', 'data', 'box', 'vertices'],
|
|
4855
|
-
|
|
4949
|
+
rectangles: ['info', 'selection', 'data', 'box', 'rectangles', 'vertices'],
|
|
4950
|
+
lines: ['info', 'selection', 'data', 'box', 'vertices' /*, 'draw-lines'*/],
|
|
4856
4951
|
table: ['info', 'selection', 'data'],
|
|
4857
|
-
labels: ['info', 'selection', 'data', 'box', 'labels', 'location'],
|
|
4858
|
-
|
|
4859
|
-
points: ['info', 'selection', 'data', 'box', 'location']
|
|
4952
|
+
labels: ['info', 'selection', 'data', 'box', 'labels', 'location', 'add-points'],
|
|
4953
|
+
points: ['info', 'selection', 'data', 'box', 'location', 'add-points']
|
|
4860
4954
|
};
|
|
4861
4955
|
|
|
4862
4956
|
var prompts = {
|
|
@@ -4875,6 +4969,8 @@
|
|
|
4875
4969
|
vertices: 'edit vertices',
|
|
4876
4970
|
selection: 'select features',
|
|
4877
4971
|
'add-points': 'add points',
|
|
4972
|
+
'draw-lines': 'draw lines',
|
|
4973
|
+
rectangles: 'drag-to-resize',
|
|
4878
4974
|
off: 'turn off'
|
|
4879
4975
|
};
|
|
4880
4976
|
var btn, menu;
|
|
@@ -4929,11 +5025,11 @@
|
|
|
4929
5025
|
};
|
|
4930
5026
|
|
|
4931
5027
|
this.modeUsesSelection = function(mode) {
|
|
4932
|
-
return ['info', 'selection', 'data', 'labels', 'location', 'vertices'].includes(mode);
|
|
5028
|
+
return ['info', 'selection', 'data', 'labels', 'location', 'vertices', 'rectangles'].includes(mode);
|
|
4933
5029
|
};
|
|
4934
5030
|
|
|
4935
5031
|
this.modeUsesPopup = function(mode) {
|
|
4936
|
-
return ['info', 'selection', 'data', 'box', 'labels', 'location'].includes(mode);
|
|
5032
|
+
return ['info', 'selection', 'data', 'box', 'labels', 'location', 'rectangles'].includes(mode);
|
|
4937
5033
|
};
|
|
4938
5034
|
|
|
4939
5035
|
this.getMode = getInteractionMode;
|
|
@@ -4975,7 +5071,8 @@
|
|
|
4975
5071
|
return menus.lines;
|
|
4976
5072
|
}
|
|
4977
5073
|
if (internal.layerHasPaths(o.layer) && o.layer.geometry_type == 'polygon') {
|
|
4978
|
-
return
|
|
5074
|
+
return internal.layerOnlyHasRectangles(o.layer, o.dataset.arcs) ?
|
|
5075
|
+
menus.rectangles : menus.polygons;
|
|
4979
5076
|
}
|
|
4980
5077
|
return menus.standard;
|
|
4981
5078
|
}
|
|
@@ -5116,7 +5213,9 @@
|
|
|
5116
5213
|
};
|
|
5117
5214
|
|
|
5118
5215
|
self.selectLayer = function(lyr, dataset) {
|
|
5119
|
-
if (self.getActiveLayer().layer == lyr)
|
|
5216
|
+
if (self.getActiveLayer().layer == lyr) {
|
|
5217
|
+
return;
|
|
5218
|
+
}
|
|
5120
5219
|
self.setDefaultTarget([lyr], dataset);
|
|
5121
5220
|
self.updated({select: true});
|
|
5122
5221
|
gui.session.setTargetLayer(lyr);
|
|
@@ -6822,7 +6921,6 @@
|
|
|
6822
6921
|
return internal.svg.getTransform(p, scale);
|
|
6823
6922
|
}
|
|
6824
6923
|
|
|
6825
|
-
|
|
6826
6924
|
function repositionSymbols(elements, layer, ext) {
|
|
6827
6925
|
var el, idx, shp, p, displayOn, inView, displayBounds;
|
|
6828
6926
|
for (var i=0, n=elements.length; i<n; i++) {
|
|
@@ -6846,7 +6944,7 @@
|
|
|
6846
6944
|
}
|
|
6847
6945
|
}
|
|
6848
6946
|
|
|
6849
|
-
function renderSymbols(lyr, ext
|
|
6947
|
+
function renderSymbols(lyr, ext) {
|
|
6850
6948
|
var records = lyr.data.getRecords();
|
|
6851
6949
|
var symbols = lyr.shapes.map(function(shp, i) {
|
|
6852
6950
|
var d = records[i];
|
|
@@ -6954,6 +7052,8 @@
|
|
|
6954
7052
|
var interactionMode;
|
|
6955
7053
|
var targetLayer;
|
|
6956
7054
|
var hitTest;
|
|
7055
|
+
var pinnedOn; // used in multi-edit mode (selection) for toggling pinning behavior
|
|
7056
|
+
|
|
6957
7057
|
// event priority is higher than navigation, so stopping propagation disables
|
|
6958
7058
|
// pan navigation
|
|
6959
7059
|
var priority = 2;
|
|
@@ -7012,6 +7112,7 @@
|
|
|
7012
7112
|
updateSelectionState(null); // no hit data, no event
|
|
7013
7113
|
active = false;
|
|
7014
7114
|
hitTest = null;
|
|
7115
|
+
pinnedOn = false;
|
|
7015
7116
|
}
|
|
7016
7117
|
}
|
|
7017
7118
|
|
|
@@ -7029,7 +7130,8 @@
|
|
|
7029
7130
|
|
|
7030
7131
|
function clickable() {
|
|
7031
7132
|
// click used to pin popup and select features
|
|
7032
|
-
return interactionMode == 'data' || interactionMode == 'info' ||
|
|
7133
|
+
return interactionMode == 'data' || interactionMode == 'info' ||
|
|
7134
|
+
interactionMode == 'selection' || interactionMode == 'rectangles';
|
|
7033
7135
|
}
|
|
7034
7136
|
|
|
7035
7137
|
self.getHitId = function() {return storedData.id;};
|
|
@@ -7047,6 +7149,13 @@
|
|
|
7047
7149
|
updateSelectionState({ids: ids});
|
|
7048
7150
|
};
|
|
7049
7151
|
|
|
7152
|
+
self.setPinning = function(val) {
|
|
7153
|
+
if (pinnedOn != val) {
|
|
7154
|
+
pinnedOn = val;
|
|
7155
|
+
triggerHitEvent('change');
|
|
7156
|
+
}
|
|
7157
|
+
};
|
|
7158
|
+
|
|
7050
7159
|
self.setTransientIds = function(ids) {
|
|
7051
7160
|
// turnOn('selection');
|
|
7052
7161
|
transientIds = ids || [];
|
|
@@ -7116,7 +7225,7 @@
|
|
|
7116
7225
|
// make sure popup is unpinned and turned off when switching editing modes
|
|
7117
7226
|
// (some modes do not support pinning)
|
|
7118
7227
|
gui.on('interaction_mode_change', function(e) {
|
|
7119
|
-
|
|
7228
|
+
self.clearSelection();
|
|
7120
7229
|
// if (e.mode == 'off' || e.mode == 'box') {
|
|
7121
7230
|
if (gui.interaction.modeUsesSelection(e.mode)) {
|
|
7122
7231
|
turnOn(e.mode);
|
|
@@ -7125,6 +7234,10 @@
|
|
|
7125
7234
|
}
|
|
7126
7235
|
});
|
|
7127
7236
|
|
|
7237
|
+
gui.on('undo_redo_pre', function() {
|
|
7238
|
+
self.clearSelection();
|
|
7239
|
+
});
|
|
7240
|
+
|
|
7128
7241
|
gui.on('box_drag_start', function() {
|
|
7129
7242
|
self.clearHover();
|
|
7130
7243
|
});
|
|
@@ -7277,6 +7390,9 @@
|
|
|
7277
7390
|
if (transientIds.length) {
|
|
7278
7391
|
eventData.ids = utils$1.uniq(transientIds.concat(eventData.ids || []));
|
|
7279
7392
|
}
|
|
7393
|
+
if (pinnedOn) {
|
|
7394
|
+
eventData.pinned = true;
|
|
7395
|
+
}
|
|
7280
7396
|
self.dispatchEvent(type, eventData);
|
|
7281
7397
|
}
|
|
7282
7398
|
|
|
@@ -7787,6 +7903,10 @@
|
|
|
7787
7903
|
_on = false,
|
|
7788
7904
|
handles;
|
|
7789
7905
|
|
|
7906
|
+
if (opts.classname) {
|
|
7907
|
+
el.addClass(opts.classname);
|
|
7908
|
+
}
|
|
7909
|
+
|
|
7790
7910
|
el.hide();
|
|
7791
7911
|
|
|
7792
7912
|
gui.on('map_rendered', function() {
|
|
@@ -7829,14 +7949,17 @@
|
|
|
7829
7949
|
var scale = gui.map.getExtent().getPixelSize();
|
|
7830
7950
|
var dx = (xy.x - prevXY.x) * scale;
|
|
7831
7951
|
var dy = -(xy.y - prevXY.y) * scale;
|
|
7832
|
-
|
|
7952
|
+
var center = activeHandle.col == 'center' && activeHandle.row == 'center';
|
|
7953
|
+
if (activeHandle.col == 'left' || center) {
|
|
7833
7954
|
boxCoords[0] += dx;
|
|
7834
|
-
}
|
|
7955
|
+
}
|
|
7956
|
+
if (activeHandle.col == 'right' || center) {
|
|
7835
7957
|
boxCoords[2] += dx;
|
|
7836
7958
|
}
|
|
7837
|
-
if (activeHandle.row == 'top') {
|
|
7959
|
+
if (activeHandle.row == 'top' || center) {
|
|
7838
7960
|
boxCoords[3] += dy;
|
|
7839
|
-
}
|
|
7961
|
+
}
|
|
7962
|
+
if (activeHandle.row == 'bottom' || center) {
|
|
7840
7963
|
boxCoords[1] += dy;
|
|
7841
7964
|
}
|
|
7842
7965
|
prevXY = xy;
|
|
@@ -7857,6 +7980,11 @@
|
|
|
7857
7980
|
});
|
|
7858
7981
|
}
|
|
7859
7982
|
|
|
7983
|
+
box.setDataCoords = function(bbox) {
|
|
7984
|
+
boxCoords = bbox;
|
|
7985
|
+
redraw();
|
|
7986
|
+
};
|
|
7987
|
+
|
|
7860
7988
|
box.getDataCoords = function() {
|
|
7861
7989
|
if (!boxCoords) return null;
|
|
7862
7990
|
var dataBox = getBBoxCoords(gui.map.getActiveLayer(), boxCoords);
|
|
@@ -7883,8 +8011,8 @@
|
|
|
7883
8011
|
props = {
|
|
7884
8012
|
top: Math.min(y1, y2),
|
|
7885
8013
|
left: Math.min(x1, x2),
|
|
7886
|
-
width: Math.max(w - stroke
|
|
7887
|
-
height: Math.max(h - stroke
|
|
8014
|
+
width: Math.max(w - stroke / 2, 1),
|
|
8015
|
+
height: Math.max(h - stroke / 2, 1)
|
|
7888
8016
|
};
|
|
7889
8017
|
el.css(props);
|
|
7890
8018
|
el.show();
|
|
@@ -7942,7 +8070,7 @@
|
|
|
7942
8070
|
function initHandles(el) {
|
|
7943
8071
|
var handles = [];
|
|
7944
8072
|
for (var i=0; i<9; i++) {
|
|
7945
|
-
if (i == 4) continue;
|
|
8073
|
+
// if (i == 4) continue; // skip middle handle
|
|
7946
8074
|
var c = Math.floor(i / 3);
|
|
7947
8075
|
var r = i % 3;
|
|
7948
8076
|
handles.push({
|
|
@@ -8179,6 +8307,7 @@
|
|
|
8179
8307
|
}
|
|
8180
8308
|
|
|
8181
8309
|
function turnOff() {
|
|
8310
|
+
dataBtn.turnOff();
|
|
8182
8311
|
box.turnOff();
|
|
8183
8312
|
reset();
|
|
8184
8313
|
_on = false;
|
|
@@ -8189,10 +8318,17 @@
|
|
|
8189
8318
|
}
|
|
8190
8319
|
|
|
8191
8320
|
function reset() {
|
|
8192
|
-
|
|
8321
|
+
hidePopup();
|
|
8322
|
+
setPinning(false);
|
|
8193
8323
|
hit.clearSelection();
|
|
8194
8324
|
}
|
|
8195
8325
|
|
|
8326
|
+
function setPinning(on) {
|
|
8327
|
+
hit.setPinning(on);
|
|
8328
|
+
if (on) dataBtn.turnOn();
|
|
8329
|
+
else dataBtn.turnOff();
|
|
8330
|
+
}
|
|
8331
|
+
|
|
8196
8332
|
function getIdsOpt() {
|
|
8197
8333
|
return hit.getSelectionIds().join(',');
|
|
8198
8334
|
}
|
|
@@ -8207,11 +8343,15 @@
|
|
|
8207
8343
|
popup.show();
|
|
8208
8344
|
updateCoords();
|
|
8209
8345
|
} else {
|
|
8210
|
-
|
|
8211
|
-
hideCoords();
|
|
8346
|
+
hidePopup();
|
|
8212
8347
|
}
|
|
8213
8348
|
});
|
|
8214
8349
|
|
|
8350
|
+
function hidePopup() {
|
|
8351
|
+
popup.hide();
|
|
8352
|
+
hideCoords();
|
|
8353
|
+
}
|
|
8354
|
+
|
|
8215
8355
|
new SimpleButton(popup.findChild('.delete-btn')).on('click', function() {
|
|
8216
8356
|
var cmd = '-filter invert ids=' + getIdsOpt();
|
|
8217
8357
|
runCommand(cmd);
|
|
@@ -8227,6 +8367,10 @@
|
|
|
8227
8367
|
runCommand(cmd);
|
|
8228
8368
|
});
|
|
8229
8369
|
|
|
8370
|
+
var dataBtn = new ToggleButton(popup.findChild('.data-btn')).on('click', function(e) {
|
|
8371
|
+
setPinning(e.on);
|
|
8372
|
+
});
|
|
8373
|
+
|
|
8230
8374
|
new SimpleButton(popup.findChild('.duplicate-btn')).on('click', function() {
|
|
8231
8375
|
var cmd = '-filter + name=selection ids=' + getIdsOpt();
|
|
8232
8376
|
runCommand(cmd);
|
|
@@ -8264,19 +8408,19 @@
|
|
|
8264
8408
|
hideCoords();
|
|
8265
8409
|
return;
|
|
8266
8410
|
}
|
|
8267
|
-
El(coordsBtn.node()).addClass('
|
|
8411
|
+
El(coordsBtn.node()).addClass('active');
|
|
8268
8412
|
coords.text(bbox.join(','));
|
|
8269
8413
|
coords.show();
|
|
8270
8414
|
GUI.selectElement(coords.node());
|
|
8271
8415
|
}
|
|
8272
8416
|
|
|
8273
8417
|
function hideCoords() {
|
|
8274
|
-
El(coordsBtn.node()).removeClass('
|
|
8418
|
+
El(coordsBtn.node()).removeClass('active');
|
|
8275
8419
|
coords.hide();
|
|
8276
8420
|
}
|
|
8277
8421
|
|
|
8278
8422
|
function runCommand(cmd, turnOff) {
|
|
8279
|
-
|
|
8423
|
+
hidePopup();
|
|
8280
8424
|
gui.quiet(true);
|
|
8281
8425
|
if (gui.console) gui.console.runMapshaperCommands(cmd, function(err) {
|
|
8282
8426
|
gui.quiet(false);
|
|
@@ -8286,6 +8430,55 @@
|
|
|
8286
8430
|
}
|
|
8287
8431
|
}
|
|
8288
8432
|
|
|
8433
|
+
function openAddFieldPopup(gui, ids, lyr) {
|
|
8434
|
+
var popup = showPopupAlert('', 'Add field');
|
|
8435
|
+
var el = popup.container();
|
|
8436
|
+
el.addClass('option-menu');
|
|
8437
|
+
var html = `<div><input type="text" class="field-name text-input" placeholder="field name"></div>
|
|
8438
|
+
<div><input type="text" class="field-value text-input" placeholder="value"><div>
|
|
8439
|
+
<div tabindex="0" class="btn dialog-btn">Apply</div> <span class="inline-checkbox"><input type="checkbox" class="all" />assign value to all records</span>`;
|
|
8440
|
+
el.html(html);
|
|
8441
|
+
|
|
8442
|
+
var name = el.findChild('.field-name');
|
|
8443
|
+
name.node().focus();
|
|
8444
|
+
var val = el.findChild('.field-value');
|
|
8445
|
+
var box = el.findChild('.all');
|
|
8446
|
+
var btn = el.findChild('.btn').on('click', function() {
|
|
8447
|
+
var table = internal.getLayerDataTable(lyr); // creates new table if missing
|
|
8448
|
+
var all = box.node().checked;
|
|
8449
|
+
var nameStr = name.node().value.trim();
|
|
8450
|
+
if (!nameStr) return;
|
|
8451
|
+
if (table.fieldExists(nameStr)) {
|
|
8452
|
+
name.node().value = '';
|
|
8453
|
+
return;
|
|
8454
|
+
}
|
|
8455
|
+
var valStr = val.node().value.trim();
|
|
8456
|
+
var value = internal.parseUnknownType(valStr);
|
|
8457
|
+
// table.addField(nameStr, function(d) {
|
|
8458
|
+
// // parse each time to avoid multiple references to objects
|
|
8459
|
+
// return (all || d == rec) ? parseUnknownType(valStr) : null;
|
|
8460
|
+
// });
|
|
8461
|
+
|
|
8462
|
+
var cmdStr = `-each "d['${nameStr}'] = `;
|
|
8463
|
+
if (!all) {
|
|
8464
|
+
cmdStr += ids.length == 1 ?
|
|
8465
|
+
`this.id != ${ids[0]}` :
|
|
8466
|
+
`!${JSON.stringify(ids)}.includes(this.id)`;
|
|
8467
|
+
cmdStr += ' ? null : ';
|
|
8468
|
+
}
|
|
8469
|
+
valStr = JSON.stringify(JSON.stringify(value)); // add escapes to strings
|
|
8470
|
+
cmdStr = valStr.replace('"', cmdStr);
|
|
8471
|
+
|
|
8472
|
+
gui.console.runMapshaperCommands(cmdStr, function(err) {
|
|
8473
|
+
if (!err) {
|
|
8474
|
+
popup.close();
|
|
8475
|
+
} else {
|
|
8476
|
+
console.error(err);
|
|
8477
|
+
}
|
|
8478
|
+
});
|
|
8479
|
+
});
|
|
8480
|
+
}
|
|
8481
|
+
|
|
8289
8482
|
// toNext, toPrev: trigger functions for switching between multiple records
|
|
8290
8483
|
function Popup(gui, toNext, toPrev) {
|
|
8291
8484
|
var self = new EventDispatcher();
|
|
@@ -8299,7 +8492,6 @@
|
|
|
8299
8492
|
var navInfo = El('span').addClass('popup-nav-info').appendTo(nav);
|
|
8300
8493
|
var nextLink = El('span').addClass('popup-nav-arrow colored-text').appendTo(nav).text('▶');
|
|
8301
8494
|
var refresh = null;
|
|
8302
|
-
var currIds = [];
|
|
8303
8495
|
|
|
8304
8496
|
el.addClass('rollover'); // used as a sentinel for the hover function
|
|
8305
8497
|
|
|
@@ -8310,16 +8502,20 @@
|
|
|
8310
8502
|
});
|
|
8311
8503
|
|
|
8312
8504
|
self.show = function(id, ids, lyr, pinned) {
|
|
8313
|
-
var
|
|
8505
|
+
var singleEdit = pinned && gui.interaction.getMode() == 'data';
|
|
8506
|
+
var multiEdit = pinned && gui.interaction.getMode() == 'selection';
|
|
8314
8507
|
var maxHeight = parent.node().clientHeight - 36;
|
|
8315
|
-
|
|
8508
|
+
var recIds = multiEdit ? ids : [id];
|
|
8509
|
+
|
|
8316
8510
|
// stash a function for refreshing the current popup when data changes
|
|
8317
8511
|
// while the popup is being displayed (e.g. while dragging a label)
|
|
8318
8512
|
refresh = function() {
|
|
8319
|
-
render(content,
|
|
8513
|
+
render(content, recIds, lyr, singleEdit || multiEdit);
|
|
8320
8514
|
};
|
|
8321
8515
|
refresh();
|
|
8322
|
-
if (
|
|
8516
|
+
if (multiEdit) {
|
|
8517
|
+
showRecords(ids.length);
|
|
8518
|
+
} else if (ids && ids.length > 1 && !multiEdit) {
|
|
8323
8519
|
showNav(id, ids, pinned);
|
|
8324
8520
|
} else {
|
|
8325
8521
|
tab.hide();
|
|
@@ -8333,7 +8529,6 @@
|
|
|
8333
8529
|
self.hide = function() {
|
|
8334
8530
|
if (!isOpen()) return;
|
|
8335
8531
|
refresh = null;
|
|
8336
|
-
currIds = [];
|
|
8337
8532
|
// make sure any pending edits are made before re-rendering popup
|
|
8338
8533
|
GUI.blurActiveElement(); // this should be more selective -- could cause a glitch if typing in console
|
|
8339
8534
|
content.empty();
|
|
@@ -8347,6 +8542,13 @@
|
|
|
8347
8542
|
return el.visible();
|
|
8348
8543
|
}
|
|
8349
8544
|
|
|
8545
|
+
function showRecords(n) {
|
|
8546
|
+
navInfo.text(n);
|
|
8547
|
+
nextLink.css('display','none');
|
|
8548
|
+
prevLink.css('display','none');
|
|
8549
|
+
tab.show();
|
|
8550
|
+
}
|
|
8551
|
+
|
|
8350
8552
|
function showNav(id, ids, pinned) {
|
|
8351
8553
|
var num = ids.indexOf(id) + 1;
|
|
8352
8554
|
navInfo.text(' ' + num + ' / ' + ids.length + ' ');
|
|
@@ -8355,28 +8557,12 @@
|
|
|
8355
8557
|
tab.show();
|
|
8356
8558
|
}
|
|
8357
8559
|
|
|
8358
|
-
function render(el,
|
|
8359
|
-
var recIds = [recId]; // TODO: support multiple ids
|
|
8560
|
+
function render(el, recIds, lyr, editable) {
|
|
8360
8561
|
var table = lyr.data; // table can be null (e.g. if layer has no attribute data)
|
|
8361
|
-
var
|
|
8362
|
-
var tableEl = El('table').addClass('selectable'),
|
|
8363
|
-
rows = 0;
|
|
8364
|
-
// self.hide(); // clean up if panel is already open
|
|
8562
|
+
var tableEl = table ? renderTable(recIds, table, editable) : null;
|
|
8365
8563
|
el.empty(); // clean up if panel is already open
|
|
8366
|
-
|
|
8367
|
-
|
|
8368
|
-
// missing GeoJSON fields are set to undefined on import; skip these
|
|
8369
|
-
if (v !== undefined) {
|
|
8370
|
-
type = getFieldType(v, k, table);
|
|
8371
|
-
renderRow(tableEl, rec, k, type, editable);
|
|
8372
|
-
rows++;
|
|
8373
|
-
}
|
|
8374
|
-
});
|
|
8375
|
-
|
|
8376
|
-
tableEl.appendTo(el);
|
|
8377
|
-
if (rows > 0) {
|
|
8378
|
-
// tableEl.appendTo(el);
|
|
8379
|
-
|
|
8564
|
+
if (tableEl) {
|
|
8565
|
+
tableEl.appendTo(el);
|
|
8380
8566
|
tableEl.on('copy', function(e) {
|
|
8381
8567
|
// remove leading or trailing tabs that sometimes get copied when
|
|
8382
8568
|
// selecting from a table
|
|
@@ -8387,7 +8573,6 @@
|
|
|
8387
8573
|
e.preventDefault(); // don't copy original string with tabs
|
|
8388
8574
|
}
|
|
8389
8575
|
});
|
|
8390
|
-
|
|
8391
8576
|
} else {
|
|
8392
8577
|
// Some individual features can have undefined values for some or all of
|
|
8393
8578
|
// their data properties (properties are set to undefined when an input JSON file
|
|
@@ -8401,72 +8586,65 @@
|
|
|
8401
8586
|
var line = El('div').appendTo(el);
|
|
8402
8587
|
El('span').addClass('add-field-btn').appendTo(line).on('click', async function(e) {
|
|
8403
8588
|
// show "add field" dialog
|
|
8404
|
-
|
|
8589
|
+
openAddFieldPopup(gui, recIds, lyr);
|
|
8405
8590
|
}).text('+ add field');
|
|
8406
8591
|
}
|
|
8407
8592
|
}
|
|
8408
8593
|
|
|
8409
|
-
function
|
|
8410
|
-
var
|
|
8411
|
-
var
|
|
8412
|
-
|
|
8413
|
-
|
|
8414
|
-
|
|
8415
|
-
|
|
8416
|
-
|
|
8417
|
-
|
|
8418
|
-
|
|
8419
|
-
|
|
8420
|
-
|
|
8421
|
-
|
|
8422
|
-
|
|
8423
|
-
var
|
|
8424
|
-
|
|
8425
|
-
|
|
8426
|
-
|
|
8427
|
-
if (table.fieldExists(nameStr)) {
|
|
8428
|
-
name.node().value = '';
|
|
8429
|
-
return;
|
|
8594
|
+
function renderTable(recIds, table, editable) {
|
|
8595
|
+
var tableEl = El('table').addClass('selectable');
|
|
8596
|
+
var rows = 0;
|
|
8597
|
+
var rec;
|
|
8598
|
+
if (recIds.length == 1) {
|
|
8599
|
+
rec = editable ?
|
|
8600
|
+
table.getReadOnlyRecordAt(recIds[0]) :
|
|
8601
|
+
table.getRecordAt(recIds[0]);
|
|
8602
|
+
} else {
|
|
8603
|
+
rec = getMultiRecord(recIds, table);
|
|
8604
|
+
}
|
|
8605
|
+
utils$1.forEachProperty(rec, function(v, k) {
|
|
8606
|
+
// missing GeoJSON fields are set to undefined on import; skip these
|
|
8607
|
+
if (v === undefined) return;
|
|
8608
|
+
var rowEl = renderRow(k, v, recIds, table, editable);
|
|
8609
|
+
if (rowEl) {
|
|
8610
|
+
rowEl.appendTo(tableEl);
|
|
8611
|
+
rows++;
|
|
8430
8612
|
}
|
|
8431
|
-
|
|
8432
|
-
|
|
8433
|
-
|
|
8434
|
-
|
|
8435
|
-
|
|
8436
|
-
|
|
8437
|
-
|
|
8438
|
-
|
|
8439
|
-
|
|
8440
|
-
|
|
8441
|
-
|
|
8442
|
-
|
|
8443
|
-
|
|
8444
|
-
|
|
8445
|
-
|
|
8446
|
-
|
|
8447
|
-
|
|
8448
|
-
gui.console.runMapshaperCommands(cmdStr, function(err) {
|
|
8449
|
-
if (!err) {
|
|
8450
|
-
popup.close();
|
|
8451
|
-
} else {
|
|
8452
|
-
console.error(err);
|
|
8613
|
+
});
|
|
8614
|
+
return rows > 0 ? tableEl : null;
|
|
8615
|
+
}
|
|
8616
|
+
|
|
8617
|
+
function getMultiRecord(recIds, table) {
|
|
8618
|
+
var fields = table.getFields();
|
|
8619
|
+
var rec = {};
|
|
8620
|
+
recIds.forEach(function(id) {
|
|
8621
|
+
var d = table.getRecordAt(id) || {};
|
|
8622
|
+
var k, v;
|
|
8623
|
+
for (var i=0; i<fields.length; i++) {
|
|
8624
|
+
k = fields[i];
|
|
8625
|
+
v = d[k];
|
|
8626
|
+
if (k in rec === false) {
|
|
8627
|
+
rec[k] = v;
|
|
8628
|
+
} else if (rec[k] !== v) {
|
|
8629
|
+
rec[k] = null;
|
|
8453
8630
|
}
|
|
8454
|
-
}
|
|
8631
|
+
}
|
|
8455
8632
|
});
|
|
8633
|
+
return rec;
|
|
8456
8634
|
}
|
|
8457
8635
|
|
|
8458
|
-
|
|
8459
|
-
|
|
8636
|
+
|
|
8637
|
+
function renderRow(key, val, recIds, table, editable) {
|
|
8638
|
+
var type = getFieldType(val, key, table);
|
|
8460
8639
|
var str = formatInspectorValue(val, type);
|
|
8461
8640
|
var rowHtml = `<td class="field-name">${key}</td><td><span class="value">${utils$1.htmlEscape(str)}</span> </td>`;
|
|
8462
|
-
var
|
|
8463
|
-
|
|
8464
|
-
|
|
8465
|
-
.findChild('.value');
|
|
8466
|
-
setFieldClass(cell, val, type);
|
|
8641
|
+
var rowEl = El('tr').html(rowHtml);
|
|
8642
|
+
var cellEl = rowEl.findChild('.value');
|
|
8643
|
+
setFieldClass(cellEl, val, type);
|
|
8467
8644
|
if (editable) {
|
|
8468
|
-
editItem(
|
|
8645
|
+
editItem(cellEl, key, val, recIds, table, type);
|
|
8469
8646
|
}
|
|
8647
|
+
return rowEl;
|
|
8470
8648
|
}
|
|
8471
8649
|
|
|
8472
8650
|
function setFieldClass(el, val, type) {
|
|
@@ -8479,10 +8657,10 @@
|
|
|
8479
8657
|
el.classed('empty', isEmpty);
|
|
8480
8658
|
}
|
|
8481
8659
|
|
|
8482
|
-
function editItem(el,
|
|
8660
|
+
function editItem(el, key, val, recIds, table, type) {
|
|
8483
8661
|
var input = new ClickText2(el),
|
|
8484
|
-
strval = formatInspectorValue(
|
|
8485
|
-
parser = getInputParser(type);
|
|
8662
|
+
strval = formatInspectorValue(val, type),
|
|
8663
|
+
parser = internal.getInputParser(type);
|
|
8486
8664
|
el.parent().addClass('editable-cell');
|
|
8487
8665
|
el.addClass('colored-text dot-underline');
|
|
8488
8666
|
input.on('change', function(e) {
|
|
@@ -8494,17 +8672,27 @@
|
|
|
8494
8672
|
} else if (strval != strval2) {
|
|
8495
8673
|
// field content has changed
|
|
8496
8674
|
strval = strval2;
|
|
8497
|
-
gui.dispatchEvent('data_preupdate', {ids:
|
|
8498
|
-
rec[key] = val2;
|
|
8499
|
-
|
|
8675
|
+
gui.dispatchEvent('data_preupdate', {ids: recIds}); // for undo/redo
|
|
8676
|
+
// rec[key] = val2;
|
|
8677
|
+
updateRecords(recIds, key, val2, table);
|
|
8678
|
+
gui.dispatchEvent('data_postupdate', {ids: recIds});
|
|
8500
8679
|
input.value(strval);
|
|
8501
8680
|
setFieldClass(el, val2, type);
|
|
8502
|
-
self.dispatchEvent('data_updated', {field: key, value: val2, ids:
|
|
8681
|
+
self.dispatchEvent('data_updated', {field: key, value: val2, ids: recIds});
|
|
8503
8682
|
}
|
|
8504
8683
|
});
|
|
8505
8684
|
}
|
|
8506
8685
|
}
|
|
8507
8686
|
|
|
8687
|
+
function updateRecords(ids, f, v, table) {
|
|
8688
|
+
var records = table.getRecords();
|
|
8689
|
+
ids.forEach(function(id) {
|
|
8690
|
+
var d = records[id] || {};
|
|
8691
|
+
d[f] = v;
|
|
8692
|
+
records[id] = d;
|
|
8693
|
+
});
|
|
8694
|
+
}
|
|
8695
|
+
|
|
8508
8696
|
function formatInspectorValue(val, type) {
|
|
8509
8697
|
var str;
|
|
8510
8698
|
if (type == 'date') {
|
|
@@ -8517,60 +8705,10 @@
|
|
|
8517
8705
|
return str;
|
|
8518
8706
|
}
|
|
8519
8707
|
|
|
8520
|
-
var inputParsers = {
|
|
8521
|
-
date: function(raw) {
|
|
8522
|
-
var d = new Date(raw);
|
|
8523
|
-
return isNaN(+d) ? null : d;
|
|
8524
|
-
},
|
|
8525
|
-
string: function(raw) {
|
|
8526
|
-
return raw;
|
|
8527
|
-
},
|
|
8528
|
-
number: function(raw) {
|
|
8529
|
-
var val = Number(raw);
|
|
8530
|
-
if (raw == 'NaN') {
|
|
8531
|
-
val = NaN;
|
|
8532
|
-
} else if (isNaN(val)) {
|
|
8533
|
-
val = null;
|
|
8534
|
-
}
|
|
8535
|
-
return val;
|
|
8536
|
-
},
|
|
8537
|
-
object: function(raw) {
|
|
8538
|
-
var val = null;
|
|
8539
|
-
try {
|
|
8540
|
-
val = JSON.parse(raw);
|
|
8541
|
-
} catch(e) {}
|
|
8542
|
-
return val;
|
|
8543
|
-
},
|
|
8544
|
-
boolean: function(raw) {
|
|
8545
|
-
var val = null;
|
|
8546
|
-
if (raw == 'true') {
|
|
8547
|
-
val = true;
|
|
8548
|
-
} else if (raw == 'false') {
|
|
8549
|
-
val = false;
|
|
8550
|
-
}
|
|
8551
|
-
return val;
|
|
8552
|
-
},
|
|
8553
|
-
multiple: function(raw) {
|
|
8554
|
-
var val = Number(raw);
|
|
8555
|
-
return isNaN(val) ? raw : val;
|
|
8556
|
-
}
|
|
8557
|
-
};
|
|
8558
|
-
|
|
8559
|
-
function parseUnknownType(str) {
|
|
8560
|
-
var val = inputParsers.number(str);
|
|
8561
|
-
if (val !== null) return val;
|
|
8562
|
-
val = inputParsers.object(str);
|
|
8563
|
-
if (val !== null) return val;
|
|
8564
|
-
return str;
|
|
8565
|
-
}
|
|
8566
|
-
|
|
8567
|
-
function getInputParser(type) {
|
|
8568
|
-
return inputParsers[type || 'multiple'];
|
|
8569
|
-
}
|
|
8570
8708
|
|
|
8571
8709
|
function getFieldType(val, key, table) {
|
|
8572
8710
|
// if a field has a null value, look at entire column to identify type
|
|
8573
|
-
return internal.getValueType(val) || internal.getColumnType(key, table.getRecords());
|
|
8711
|
+
return internal.getValueType(val) || internal.getColumnType(key, table.getRecords()) ;
|
|
8574
8712
|
}
|
|
8575
8713
|
|
|
8576
8714
|
function InspectionControl2(gui, hit) {
|
|
@@ -8595,16 +8733,20 @@
|
|
|
8595
8733
|
});
|
|
8596
8734
|
|
|
8597
8735
|
hit.on('change', function(e) {
|
|
8598
|
-
var ids;
|
|
8599
8736
|
if (!inspecting()) return;
|
|
8600
|
-
|
|
8601
|
-
|
|
8737
|
+
var ids;
|
|
8738
|
+
if (e.mode == 'selection') {
|
|
8739
|
+
ids = e.pinned && e.ids || [];
|
|
8740
|
+
} else {
|
|
8741
|
+
ids = e.ids || [];
|
|
8742
|
+
}
|
|
8743
|
+
inspect(e.id, ids, e.pinned);
|
|
8602
8744
|
});
|
|
8603
8745
|
|
|
8604
8746
|
// id: Id of a feature in the active layer, or -1
|
|
8605
|
-
function inspect(id,
|
|
8747
|
+
function inspect(id, ids, pin) {
|
|
8606
8748
|
var target = hit.getHitTarget();
|
|
8607
|
-
if (id > -1 && inspecting() && target && target.layer) {
|
|
8749
|
+
if ((id > -1 || ids && ids.length > 0) && inspecting() && target && target.layer) {
|
|
8608
8750
|
_popup.show(id, ids, target.layer, pin);
|
|
8609
8751
|
} else {
|
|
8610
8752
|
_popup.hide();
|
|
@@ -8903,6 +9045,9 @@
|
|
|
8903
9045
|
hit.clearVertexOverlay();
|
|
8904
9046
|
}
|
|
8905
9047
|
|
|
9048
|
+
// return data on the nearest vertex (or identical vertices) to the pointer
|
|
9049
|
+
// (if within a distance threshold)
|
|
9050
|
+
//
|
|
8906
9051
|
function findDraggableVertices(e) {
|
|
8907
9052
|
var target = hit.getHitTarget();
|
|
8908
9053
|
var shp = target.layer.shapes[e.id];
|
|
@@ -9065,35 +9210,6 @@
|
|
|
9065
9210
|
// }
|
|
9066
9211
|
}
|
|
9067
9212
|
|
|
9068
|
-
function initPointDrawing(gui, ext, hit) {
|
|
9069
|
-
function active(e) {
|
|
9070
|
-
return e.id > -1 && gui.interaction.getMode() == 'add-points';
|
|
9071
|
-
}
|
|
9072
|
-
}
|
|
9073
|
-
|
|
9074
|
-
function Pencil(gui, mouse, hit) {
|
|
9075
|
-
var self = this;
|
|
9076
|
-
var _on = false;
|
|
9077
|
-
|
|
9078
|
-
self.turnOn = function() {
|
|
9079
|
-
_on = true;
|
|
9080
|
-
};
|
|
9081
|
-
|
|
9082
|
-
self.turnOff = function() {
|
|
9083
|
-
_on = false;
|
|
9084
|
-
};
|
|
9085
|
-
|
|
9086
|
-
|
|
9087
|
-
|
|
9088
|
-
|
|
9089
|
-
|
|
9090
|
-
|
|
9091
|
-
}
|
|
9092
|
-
|
|
9093
|
-
function initDrawing(gui, ext, mouse, hit) {
|
|
9094
|
-
initPointDrawing(gui, new Pencil(gui, mouse, hit));
|
|
9095
|
-
}
|
|
9096
|
-
|
|
9097
9213
|
var darkStroke = "#334",
|
|
9098
9214
|
lightStroke = "#b7d9ea",
|
|
9099
9215
|
violet = "#cc6acc",
|
|
@@ -9322,7 +9438,10 @@
|
|
|
9322
9438
|
var inSelection = o.ids.indexOf(o.id) > -1;
|
|
9323
9439
|
var geomType = lyr.geometry_type;
|
|
9324
9440
|
var style;
|
|
9325
|
-
if (isPinned) {
|
|
9441
|
+
if (isPinned && o.mode == 'rectangles') {
|
|
9442
|
+
// kludge for rectangle editing mode
|
|
9443
|
+
style = selectionStyles[geomType];
|
|
9444
|
+
} else if (isPinned) {
|
|
9326
9445
|
// a feature is pinned
|
|
9327
9446
|
style = pinnedStyles[geomType];
|
|
9328
9447
|
} else if (inSelection) {
|
|
@@ -9417,6 +9536,59 @@
|
|
|
9417
9536
|
return internal.findPropertiesBySymbolGeom(fields, lyr.geometry_type);
|
|
9418
9537
|
}
|
|
9419
9538
|
|
|
9539
|
+
function initPointDrawing(gui, ext, mouse, hit) {
|
|
9540
|
+
|
|
9541
|
+
gui.on('interaction_mode_change', function(e) {
|
|
9542
|
+
gui.container.findChild('.map-layers').classed('add-points', e.mode === 'add-points');
|
|
9543
|
+
});
|
|
9544
|
+
|
|
9545
|
+
function active() {
|
|
9546
|
+
return gui.interaction.getMode() == 'add-points';
|
|
9547
|
+
}
|
|
9548
|
+
|
|
9549
|
+
mouse.on('click', function(e) {
|
|
9550
|
+
if (!active()) return;
|
|
9551
|
+
addPoint(e.x, e.y);
|
|
9552
|
+
gui.dispatchEvent('map-needs-refresh');
|
|
9553
|
+
});
|
|
9554
|
+
|
|
9555
|
+
// x, y: pixel coordinates
|
|
9556
|
+
function addPoint(x, y) {
|
|
9557
|
+
var p = ext.translatePixelCoords(x, y);
|
|
9558
|
+
var target = hit.getHitTarget();
|
|
9559
|
+
var lyr = target.layer;
|
|
9560
|
+
var fid = lyr.shapes.length;
|
|
9561
|
+
var d = lyr.data ? getEmptyDataRecord(lyr.data) : null;
|
|
9562
|
+
if (d) {
|
|
9563
|
+
// this seems to work even for projected layers -- the data tables
|
|
9564
|
+
// of projected and original data seem to be shared.
|
|
9565
|
+
lyr.data.getRecords()[fid] = d;
|
|
9566
|
+
// TODO: handle SVG symbol layer
|
|
9567
|
+
if (internal.layerHasLabels(lyr)) {
|
|
9568
|
+
d['label-text'] = 'TBD'; // without text, new labels will be invisible
|
|
9569
|
+
} else if (layerHasCanvasDisplayStyle(lyr)) {
|
|
9570
|
+
d.r = 3; // show a black circle if layer is styled
|
|
9571
|
+
}
|
|
9572
|
+
}
|
|
9573
|
+
lyr.shapes[fid] = [p];
|
|
9574
|
+
updatePointCoords(target, fid);
|
|
9575
|
+
}
|
|
9576
|
+
|
|
9577
|
+
function getEmptyDataRecord(table) {
|
|
9578
|
+
return table.getFields().reduce(function(memo, name) {
|
|
9579
|
+
memo[name] = null;
|
|
9580
|
+
return memo;
|
|
9581
|
+
}, {});
|
|
9582
|
+
}
|
|
9583
|
+
}
|
|
9584
|
+
|
|
9585
|
+
// import { initLineDrawing } from './gui-draw-lines';
|
|
9586
|
+
|
|
9587
|
+
function initDrawing(gui, ext, mouse, hit) {
|
|
9588
|
+
initPointDrawing(gui, ext, mouse, hit);
|
|
9589
|
+
// initLineDrawing(gui, ext, mouse, hit);
|
|
9590
|
+
}
|
|
9591
|
+
|
|
9420
9592
|
function MapExtent(_position) {
|
|
9421
9593
|
var _scale = 1,
|
|
9422
9594
|
_cx, _cy, // center in geographic units
|
|
@@ -10444,7 +10616,7 @@
|
|
|
10444
10616
|
target.svg_id = id;
|
|
10445
10617
|
resize(ext);
|
|
10446
10618
|
if (type == 'label' || type == 'symbol') {
|
|
10447
|
-
html = renderSymbols(target.layer, ext
|
|
10619
|
+
html = renderSymbols(target.layer, ext);
|
|
10448
10620
|
} else if (type == 'furniture') {
|
|
10449
10621
|
html = renderFurniture(target.layer, ext);
|
|
10450
10622
|
}
|
|
@@ -10458,13 +10630,17 @@
|
|
|
10458
10630
|
};
|
|
10459
10631
|
|
|
10460
10632
|
function reposition(target, type, ext) {
|
|
10461
|
-
var container = el.findChild('.' + target.svg_id)
|
|
10633
|
+
var container = el.findChild('.' + target.svg_id);
|
|
10634
|
+
if (!container || !container.node()) {
|
|
10635
|
+
console.error('[reposition] missing SVG container');
|
|
10636
|
+
return;
|
|
10637
|
+
}
|
|
10462
10638
|
var elements;
|
|
10463
10639
|
if (type == 'symbol') {
|
|
10464
|
-
elements = El.findAll('.mapshaper-svg-symbol', container);
|
|
10640
|
+
elements = El.findAll('.mapshaper-svg-symbol', container.node());
|
|
10465
10641
|
repositionSymbols(elements, target.layer, ext);
|
|
10466
10642
|
} else if (type == 'furniture') {
|
|
10467
|
-
repositionFurniture(container, target.layer, ext);
|
|
10643
|
+
repositionFurniture(container.node(), target.layer, ext);
|
|
10468
10644
|
} else {
|
|
10469
10645
|
// container.getElementsByTagName('text')
|
|
10470
10646
|
error('Unsupported symbol type:', type);
|
|
@@ -10479,7 +10655,7 @@
|
|
|
10479
10655
|
return el;
|
|
10480
10656
|
}
|
|
10481
10657
|
|
|
10482
|
-
function
|
|
10658
|
+
function LayerRenderer(gui, container, ext, mouse) {
|
|
10483
10659
|
var el = El(container),
|
|
10484
10660
|
_mainCanv = new DisplayCanvas().appendTo(el),
|
|
10485
10661
|
_overlayCanv = new DisplayCanvas().appendTo(el),
|
|
@@ -10499,14 +10675,12 @@
|
|
|
10499
10675
|
}
|
|
10500
10676
|
layers.forEach(function(lyr) {
|
|
10501
10677
|
var isSvgLayer = internal.layerHasSvgSymbols(lyr.layer) || internal.layerHasLabels(lyr.layer);
|
|
10502
|
-
//if (!svgType || svgType == 'label') { // svg labels may have canvas dots
|
|
10503
|
-
if (!isSvgLayer) { // svg labels may have canvas dots
|
|
10504
|
-
drawCanvasLayer(lyr, _mainCanv);
|
|
10505
|
-
}
|
|
10506
10678
|
if (isSvgLayer && !needSvgRedraw) {
|
|
10507
10679
|
_svg.reposition(lyr, 'symbol');
|
|
10508
10680
|
} else if (isSvgLayer) {
|
|
10509
10681
|
_svg.drawLayer(lyr, 'symbol');
|
|
10682
|
+
} else {
|
|
10683
|
+
drawCanvasLayer(lyr, _mainCanv);
|
|
10510
10684
|
}
|
|
10511
10685
|
});
|
|
10512
10686
|
};
|
|
@@ -10604,7 +10778,8 @@
|
|
|
10604
10778
|
});
|
|
10605
10779
|
|
|
10606
10780
|
new SimpleButton(popup.findChild('.rect-btn')).on('click', function() {
|
|
10607
|
-
|
|
10781
|
+
var cmd = '-rectangle + bbox=' + box.getDataCoords().join(',');
|
|
10782
|
+
runCommand(cmd);
|
|
10608
10783
|
});
|
|
10609
10784
|
|
|
10610
10785
|
gui.addMode('box_tool', turnOn, turnOff);
|
|
@@ -10682,6 +10857,81 @@
|
|
|
10682
10857
|
return self;
|
|
10683
10858
|
}
|
|
10684
10859
|
|
|
10860
|
+
function RectangleControl(gui, hit) {
|
|
10861
|
+
var box = new HighlightBox(gui, {persistent: true, handles: true, classname: 'rectangles'});
|
|
10862
|
+
var _on = false;
|
|
10863
|
+
var dragInfo;
|
|
10864
|
+
|
|
10865
|
+
gui.addMode('rectangle_tool', turnOn, turnOff);
|
|
10866
|
+
|
|
10867
|
+
gui.on('interaction_mode_change', function(e) {
|
|
10868
|
+
if (e.mode === 'rectangles') {
|
|
10869
|
+
gui.enterMode('rectangle_tool');
|
|
10870
|
+
} else if (gui.getMode() == 'rectangle_tool') {
|
|
10871
|
+
gui.clearMode();
|
|
10872
|
+
}
|
|
10873
|
+
});
|
|
10874
|
+
|
|
10875
|
+
hit.on('change', function(e) {
|
|
10876
|
+
if (!_on) return;
|
|
10877
|
+
// TODO: handle multiple hits (see gui-inspection-control)
|
|
10878
|
+
var id = e.id;
|
|
10879
|
+
if (e.id > -1 && e.pinned) {
|
|
10880
|
+
var target = hit.getHitTarget();
|
|
10881
|
+
var path = target.layer.shapes[e.id][0];
|
|
10882
|
+
var bbox = target.arcs.getSimpleShapeBounds(path).toArray();
|
|
10883
|
+
box.setDataCoords(bbox);
|
|
10884
|
+
dragInfo = {
|
|
10885
|
+
id: e.id,
|
|
10886
|
+
target: target,
|
|
10887
|
+
ids: [],
|
|
10888
|
+
points: []
|
|
10889
|
+
};
|
|
10890
|
+
var iter = target.arcs.getShapeIter(path);
|
|
10891
|
+
while (iter.hasNext()) {
|
|
10892
|
+
dragInfo.points.push([iter.x, iter.y]);
|
|
10893
|
+
dragInfo.ids.push(iter._arc.i);
|
|
10894
|
+
}
|
|
10895
|
+
gui.container.findChild('.map-layers').classed('dragging', true);
|
|
10896
|
+
|
|
10897
|
+
} else if (dragInfo) {
|
|
10898
|
+
gui.dispatchEvent('rectangle_dragend', dragInfo); // save undo state
|
|
10899
|
+
gui.container.findChild('.map-layers').classed('dragging', false);
|
|
10900
|
+
reset();
|
|
10901
|
+
} else {
|
|
10902
|
+
box.hide();
|
|
10903
|
+
}
|
|
10904
|
+
|
|
10905
|
+
});
|
|
10906
|
+
|
|
10907
|
+
box.on('handle_drag', function(e) {
|
|
10908
|
+
if (!_on || !dragInfo) return;
|
|
10909
|
+
var coords = internal.bboxToCoords(box.getDataCoords());
|
|
10910
|
+
setRectangleCoords(dragInfo.target, dragInfo.ids, coords);
|
|
10911
|
+
gui.dispatchEvent('map-needs-refresh');
|
|
10912
|
+
});
|
|
10913
|
+
|
|
10914
|
+
function turnOn() {
|
|
10915
|
+
box.turnOn();
|
|
10916
|
+
_on = true;
|
|
10917
|
+
}
|
|
10918
|
+
|
|
10919
|
+
function turnOff() {
|
|
10920
|
+
box.turnOff();
|
|
10921
|
+
if (gui.interaction.getMode() == 'rectangles') {
|
|
10922
|
+
// mode change was not initiated by interactive menu -- turn off interactivity
|
|
10923
|
+
gui.interaction.turnOff();
|
|
10924
|
+
}
|
|
10925
|
+
_on = false;
|
|
10926
|
+
reset();
|
|
10927
|
+
}
|
|
10928
|
+
|
|
10929
|
+
function reset() {
|
|
10930
|
+
box.hide();
|
|
10931
|
+
dragInfo = null;
|
|
10932
|
+
}
|
|
10933
|
+
}
|
|
10934
|
+
|
|
10685
10935
|
// Create low-detail versions of large arc collections for faster rendering
|
|
10686
10936
|
// at zoomed-out scales.
|
|
10687
10937
|
function enhanceArcCollectionForDisplay(unfilteredArcs) {
|
|
@@ -11185,7 +11435,7 @@
|
|
|
11185
11435
|
_hit,
|
|
11186
11436
|
_basemap,
|
|
11187
11437
|
_intersectionLyr, _activeLyr, _overlayLyr,
|
|
11188
|
-
|
|
11438
|
+
_renderer, _dynamicCRS;
|
|
11189
11439
|
|
|
11190
11440
|
_basemap = new Basemap(gui, _ext);
|
|
11191
11441
|
|
|
@@ -11280,13 +11530,12 @@
|
|
|
11280
11530
|
clearAllDisplayArcs();
|
|
11281
11531
|
|
|
11282
11532
|
// Reproject all visible map layers
|
|
11283
|
-
|
|
11284
|
-
|
|
11285
|
-
|
|
11286
|
-
|
|
11287
|
-
|
|
11288
|
-
|
|
11289
|
-
updateLayerStyles(getDrawableContentLayers()); // kludge to make sure all layers have styles
|
|
11533
|
+
getDrawableContentLayers().forEach(function(lyr) {
|
|
11534
|
+
projectDisplayLayer(lyr, newCRS);
|
|
11535
|
+
});
|
|
11536
|
+
|
|
11537
|
+
// kludge to make sure all layers have styles
|
|
11538
|
+
updateLayerStyles(getDrawableContentLayers());
|
|
11290
11539
|
|
|
11291
11540
|
// Update map extent (also triggers redraw)
|
|
11292
11541
|
projectMapExtent(_ext, oldCRS, this.getDisplayCRS(), calcFullBounds());
|
|
@@ -11364,7 +11613,7 @@
|
|
|
11364
11613
|
// Initialization just before displaying the map for the first time
|
|
11365
11614
|
function initMap() {
|
|
11366
11615
|
_ext.resize();
|
|
11367
|
-
|
|
11616
|
+
_renderer = new LayerRenderer(gui, el, _ext, _mouse);
|
|
11368
11617
|
gui.buttons.show();
|
|
11369
11618
|
|
|
11370
11619
|
if (opts.inspectorControl) {
|
|
@@ -11372,8 +11621,9 @@
|
|
|
11372
11621
|
new InspectionControl2(gui, _hit);
|
|
11373
11622
|
new SelectionTool(gui, _ext, _hit),
|
|
11374
11623
|
new BoxTool(gui, _ext, _nav),
|
|
11624
|
+
new RectangleControl(gui, _hit),
|
|
11375
11625
|
initInteractiveEditing(gui, _ext, _hit);
|
|
11376
|
-
|
|
11626
|
+
initDrawing(gui, _ext, _mouse, _hit);
|
|
11377
11627
|
_hit.on('change', updateOverlayLayer);
|
|
11378
11628
|
}
|
|
11379
11629
|
|
|
@@ -11404,7 +11654,6 @@
|
|
|
11404
11654
|
|
|
11405
11655
|
// 'hover' avoids redrawing all svg symbols when only highlight needs to refresh
|
|
11406
11656
|
drawLayers('hover');
|
|
11407
|
-
// drawLayers();
|
|
11408
11657
|
}
|
|
11409
11658
|
|
|
11410
11659
|
function getDisplayOptions() {
|
|
@@ -11439,10 +11688,7 @@
|
|
|
11439
11688
|
_ext.setFullBounds(calcFullBounds(), getStrictBounds());
|
|
11440
11689
|
}
|
|
11441
11690
|
|
|
11442
|
-
function
|
|
11443
|
-
if (isPreviewView()) {
|
|
11444
|
-
return internal.getFrameLayerBounds(internal.findFrameLayer(model));
|
|
11445
|
-
}
|
|
11691
|
+
function getVisibleContentBounds() {
|
|
11446
11692
|
var b = new Bounds();
|
|
11447
11693
|
var layers = getDrawableContentLayers();
|
|
11448
11694
|
layers.forEach(function(lyr) {
|
|
@@ -11453,6 +11699,14 @@
|
|
|
11453
11699
|
// assign bounds to empty layers, to prevent rendering errors downstream
|
|
11454
11700
|
b.setBounds(0,0,0,0);
|
|
11455
11701
|
}
|
|
11702
|
+
return b;
|
|
11703
|
+
}
|
|
11704
|
+
|
|
11705
|
+
function calcFullBounds() {
|
|
11706
|
+
if (isPreviewView()) {
|
|
11707
|
+
return internal.getFrameLayerBounds(internal.findFrameLayer(model));
|
|
11708
|
+
}
|
|
11709
|
+
var b = getVisibleContentBounds();
|
|
11456
11710
|
|
|
11457
11711
|
// add margin
|
|
11458
11712
|
// use larger margin for small sizes
|
|
@@ -11478,10 +11732,6 @@
|
|
|
11478
11732
|
return isActiveLayer(lyr) || lyr.pinned;
|
|
11479
11733
|
}
|
|
11480
11734
|
|
|
11481
|
-
function isFrameLayer(lyr) {
|
|
11482
|
-
return !!(lyr && lyr == internal.findFrameLayer(model));
|
|
11483
|
-
}
|
|
11484
|
-
|
|
11485
11735
|
function isTableView() {
|
|
11486
11736
|
return !isPreviewView() && !!_activeLyr.tabular;
|
|
11487
11737
|
}
|
|
@@ -11613,11 +11863,11 @@
|
|
|
11613
11863
|
}
|
|
11614
11864
|
// RENDERING
|
|
11615
11865
|
// draw main content layers
|
|
11616
|
-
|
|
11866
|
+
_renderer.drawMainLayers(contentLayers, action);
|
|
11617
11867
|
// draw hover & selection overlay
|
|
11618
|
-
|
|
11868
|
+
_renderer.drawOverlayLayer(_overlayLyr, action);
|
|
11619
11869
|
// draw furniture
|
|
11620
|
-
|
|
11870
|
+
// _renderer.drawFurnitureLayers(furnitureLayers, action);
|
|
11621
11871
|
gui.dispatchEvent('map_rendered');
|
|
11622
11872
|
}
|
|
11623
11873
|
}
|