mapshaper 0.6.76 → 0.6.78
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 +30 -20
- package/package.json +1 -1
- package/www/index.html +35 -48
- package/www/mapshaper-gui.js +1208 -1043
- package/www/mapshaper.js +30 -20
- package/www/page.css +64 -110
package/www/mapshaper-gui.js
CHANGED
|
@@ -1475,6 +1475,7 @@
|
|
|
1475
1475
|
|
|
1476
1476
|
async function saveSnapshot(gui) {
|
|
1477
1477
|
var obj = await captureSnapshot(gui);
|
|
1478
|
+
if (!obj) return;
|
|
1478
1479
|
// storing an unpacked object is usually a bit faster (~20%)
|
|
1479
1480
|
// note: we don't know the size of unpacked snapshot objects
|
|
1480
1481
|
// obj = internal.pack(obj);
|
|
@@ -1554,12 +1555,13 @@
|
|
|
1554
1555
|
}
|
|
1555
1556
|
|
|
1556
1557
|
async function captureSnapshot(gui) {
|
|
1557
|
-
var
|
|
1558
|
-
|
|
1558
|
+
var lyr = gui.model.getActiveLayer()?.layer;
|
|
1559
|
+
if (!lyr) return null; // no data -- no snapshot
|
|
1560
|
+
lyr.active = true;
|
|
1559
1561
|
// compact: true applies compression to vector coordinates, for ~30% reduction
|
|
1560
1562
|
// in file size in a typical polygon or polyline file, but longer processing time
|
|
1561
1563
|
var opts = {compact: false};
|
|
1562
|
-
|
|
1564
|
+
var datasets = gui.model.getDatasets();
|
|
1563
1565
|
// console.time('msx');
|
|
1564
1566
|
var obj = await internal.exportDatasetsToPack(datasets, opts);
|
|
1565
1567
|
// console.timeEnd('msx')
|
|
@@ -1736,12 +1738,12 @@
|
|
|
1736
1738
|
var btn = el.findChild('.btn').on('click', function() {
|
|
1737
1739
|
var nameStr = name.node().value.trim();
|
|
1738
1740
|
var type = el.findChild('input:checked').node().value;
|
|
1739
|
-
|
|
1741
|
+
addEmptyLayer(gui, nameStr, type);
|
|
1740
1742
|
popup.close();
|
|
1741
1743
|
});
|
|
1742
1744
|
}
|
|
1743
1745
|
|
|
1744
|
-
function
|
|
1746
|
+
function addEmptyLayer(gui, name, type) {
|
|
1745
1747
|
var targ = gui.model.getActiveLayer();
|
|
1746
1748
|
var crsInfo = targ && internal.getDatasetCrsInfo(targ.dataset);
|
|
1747
1749
|
var dataset = {
|
|
@@ -1752,7 +1754,7 @@
|
|
|
1752
1754
|
}],
|
|
1753
1755
|
info: {}
|
|
1754
1756
|
};
|
|
1755
|
-
if (type == 'polygon' || type == '
|
|
1757
|
+
if (type == 'polygon' || type == 'polyline') {
|
|
1756
1758
|
dataset.arcs = new internal.ArcCollection();
|
|
1757
1759
|
}
|
|
1758
1760
|
if (crsInfo) {
|
|
@@ -1864,7 +1866,6 @@
|
|
|
1864
1866
|
var initialImport = true;
|
|
1865
1867
|
var importCount = 0;
|
|
1866
1868
|
var importTotal = 0;
|
|
1867
|
-
var overQuickView = false;
|
|
1868
1869
|
var useQuickView = false;
|
|
1869
1870
|
var queuedFiles = [];
|
|
1870
1871
|
var manifestFiles = opts.files || [];
|
|
@@ -1874,50 +1875,48 @@
|
|
|
1874
1875
|
catalog = new CatalogControl(gui, opts.catalog, downloadFiles);
|
|
1875
1876
|
}
|
|
1876
1877
|
|
|
1877
|
-
new SimpleButton('#import-
|
|
1878
|
-
new SimpleButton('#import-
|
|
1879
|
-
new DropControl(gui, 'body',
|
|
1880
|
-
new FileChooser('#
|
|
1881
|
-
new FileChooser('#import-buttons .add-btn', receiveFiles);
|
|
1878
|
+
var submitBtn = new SimpleButton('#import-options .submit-btn').on('click', importQueuedFiles);
|
|
1879
|
+
new SimpleButton('#import-options .cancel-btn').on('click', gui.clearMode);
|
|
1880
|
+
new DropControl(gui, 'body', receiveFilesWithOption);
|
|
1881
|
+
new FileChooser('#import-options .add-btn', receiveFilesWithOption);
|
|
1882
1882
|
new FileChooser('#add-file-btn', receiveFiles);
|
|
1883
1883
|
new SimpleButton('#add-empty-btn').on('click', function() {
|
|
1884
1884
|
gui.clearMode(); // close import dialog
|
|
1885
1885
|
openAddLayerPopup(gui);
|
|
1886
1886
|
});
|
|
1887
|
-
initDropArea('#import-quick-drop', true);
|
|
1888
|
-
initDropArea('#import-drop');
|
|
1887
|
+
// initDropArea('#import-quick-drop', true);
|
|
1888
|
+
// initDropArea('#import-drop');
|
|
1889
1889
|
gui.keyboard.onMenuSubmit(El('#import-options'), importQueuedFiles);
|
|
1890
1890
|
|
|
1891
1891
|
gui.addMode('import', turnOn, turnOff);
|
|
1892
1892
|
gui.enterMode('import');
|
|
1893
1893
|
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
}
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
function initDropArea(el, isQuick) {
|
|
1902
|
-
var area = El(el)
|
|
1903
|
-
.on('dragleave', onout)
|
|
1904
|
-
.on('dragover', function() {overQuickView = !!isQuick; onover();})
|
|
1905
|
-
.on('mouseover', onover)
|
|
1906
|
-
.on('mouseout', onout);
|
|
1907
|
-
|
|
1908
|
-
function onover() {
|
|
1909
|
-
area.addClass('dragover');
|
|
1894
|
+
function turnOn() {
|
|
1895
|
+
if (manifestFiles.length > 0) {
|
|
1896
|
+
downloadFiles(manifestFiles, true);
|
|
1897
|
+
manifestFiles = [];
|
|
1898
|
+
} else if (model.isEmpty()) {
|
|
1899
|
+
showImportMenu();
|
|
1910
1900
|
}
|
|
1901
|
+
}
|
|
1911
1902
|
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1903
|
+
function turnOff() {
|
|
1904
|
+
var target;
|
|
1905
|
+
if (catalog) catalog.reset(); // re-enable clickable catalog
|
|
1906
|
+
if (importCount > 0) {
|
|
1907
|
+
onImportComplete();
|
|
1908
|
+
importTotal += importCount;
|
|
1909
|
+
importCount = 0;
|
|
1915
1910
|
}
|
|
1911
|
+
gui.clearProgressMessage();
|
|
1912
|
+
initialImport = false; // unset 'quick view' mode, if on
|
|
1913
|
+
clearQueuedFiles();
|
|
1914
|
+
hideImportMenu();
|
|
1916
1915
|
}
|
|
1917
1916
|
|
|
1918
1917
|
async function importQueuedFiles() {
|
|
1919
|
-
gui.container.removeClass('queued-files');
|
|
1920
|
-
|
|
1918
|
+
// gui.container.removeClass('queued-files');
|
|
1919
|
+
hideImportMenu();
|
|
1921
1920
|
var files = queuedFiles;
|
|
1922
1921
|
try {
|
|
1923
1922
|
if (files.length > 0) {
|
|
@@ -1934,27 +1933,7 @@
|
|
|
1934
1933
|
}
|
|
1935
1934
|
}
|
|
1936
1935
|
|
|
1937
|
-
function turnOn() {
|
|
1938
|
-
if (manifestFiles.length > 0) {
|
|
1939
|
-
downloadFiles(manifestFiles, true);
|
|
1940
|
-
manifestFiles = [];
|
|
1941
|
-
} else if (model.isEmpty()) {
|
|
1942
|
-
gui.container.addClass('splash-screen');
|
|
1943
|
-
}
|
|
1944
|
-
}
|
|
1945
1936
|
|
|
1946
|
-
function turnOff() {
|
|
1947
|
-
var target;
|
|
1948
|
-
if (catalog) catalog.reset(); // re-enable clickable catalog
|
|
1949
|
-
if (importCount > 0) {
|
|
1950
|
-
onImportComplete();
|
|
1951
|
-
importTotal += importCount;
|
|
1952
|
-
importCount = 0;
|
|
1953
|
-
}
|
|
1954
|
-
gui.clearProgressMessage();
|
|
1955
|
-
initialImport = false; // unset 'quick view' mode, if on
|
|
1956
|
-
clearQueuedFiles();
|
|
1957
|
-
}
|
|
1958
1937
|
|
|
1959
1938
|
function onImportComplete() {
|
|
1960
1939
|
// display last layer of last imported dataset
|
|
@@ -1987,6 +1966,7 @@
|
|
|
1987
1966
|
}, []);
|
|
1988
1967
|
}
|
|
1989
1968
|
|
|
1969
|
+
|
|
1990
1970
|
function showQueuedFiles() {
|
|
1991
1971
|
var list = gui.container.findChild('.dropped-file-list').empty();
|
|
1992
1972
|
queuedFiles.forEach(function(f) {
|
|
@@ -2006,13 +1986,20 @@
|
|
|
2006
1986
|
}
|
|
2007
1987
|
});
|
|
2008
1988
|
});
|
|
1989
|
+
submitBtn.classed('disabled', queuedFiles.length === 0);
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
function receiveFilesWithOption(files) {
|
|
1993
|
+
var quickView = !El('.advanced-import-options').node().checked;
|
|
1994
|
+
receiveFiles(files, quickView);
|
|
2009
1995
|
}
|
|
2010
1996
|
|
|
2011
|
-
async function receiveFiles(files) {
|
|
1997
|
+
async function receiveFiles(files, quickView) {
|
|
2012
1998
|
var names = getFileNames(files);
|
|
2013
1999
|
var expanded = [];
|
|
2014
2000
|
if (files.length === 0) return;
|
|
2015
|
-
useQuickView = importTotal === 0 && (opts.quick_view ||
|
|
2001
|
+
useQuickView = importTotal === 0 && (opts.quick_view ||
|
|
2002
|
+
quickView);
|
|
2016
2003
|
try {
|
|
2017
2004
|
expanded = await expandFiles(files);
|
|
2018
2005
|
} catch(e) {
|
|
@@ -2030,12 +2017,23 @@
|
|
|
2030
2017
|
if (useQuickView) {
|
|
2031
2018
|
await importQueuedFiles();
|
|
2032
2019
|
} else {
|
|
2033
|
-
|
|
2034
|
-
El('#path-import-options').classed('hidden', !filesMayContainPaths(queuedFiles));
|
|
2035
|
-
showQueuedFiles();
|
|
2020
|
+
showImportMenu();
|
|
2036
2021
|
}
|
|
2037
2022
|
}
|
|
2038
2023
|
|
|
2024
|
+
function showImportMenu() {
|
|
2025
|
+
// gui.container.addClass('queued-files');
|
|
2026
|
+
El('#import-options').show();
|
|
2027
|
+
gui.container.classed('queued-files', queuedFiles.length > 0);
|
|
2028
|
+
El('#path-import-options').classed('hidden', !filesMayContainPaths(queuedFiles));
|
|
2029
|
+
showQueuedFiles();
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
function hideImportMenu() {
|
|
2033
|
+
// gui.container.removeClass('queued-files');
|
|
2034
|
+
El('#import-options').hide();
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2039
2037
|
function getFileNames(files) {
|
|
2040
2038
|
return Array.from(files).map(function(f) {return f.name;});
|
|
2041
2039
|
}
|
|
@@ -2531,7 +2529,11 @@
|
|
|
2531
2529
|
// prevent GUI message popup on error
|
|
2532
2530
|
internal.setLoggingForCLI();
|
|
2533
2531
|
try {
|
|
2534
|
-
|
|
2532
|
+
if (!dataset || internal.datasetIsEmpty(dataset)) {
|
|
2533
|
+
crs = internal.parseCrsString('wgs84');
|
|
2534
|
+
} else {
|
|
2535
|
+
crs = internal.getDatasetCRS(dataset);
|
|
2536
|
+
}
|
|
2535
2537
|
} catch(e) {
|
|
2536
2538
|
err = e.message;
|
|
2537
2539
|
}
|
|
@@ -2714,7 +2716,8 @@
|
|
|
2714
2716
|
if (opts.interactionMode == 'vertices') {
|
|
2715
2717
|
return getVertexStyle(baseLyr, o);
|
|
2716
2718
|
}
|
|
2717
|
-
if (opts.interactionMode == '
|
|
2719
|
+
if (opts.interactionMode == 'edit_lines' ||
|
|
2720
|
+
opts.interactionMode == 'edit_polygons') {
|
|
2718
2721
|
return getLineEditingStyle(o);
|
|
2719
2722
|
}
|
|
2720
2723
|
var geomType = baseLyr.geometry_type;
|
|
@@ -2923,235 +2926,708 @@
|
|
|
2923
2926
|
return internal.findPropertiesBySymbolGeom(fields, lyr.geometry_type);
|
|
2924
2927
|
}
|
|
2925
2928
|
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
}
|
|
2929
|
+
// Create low-detail versions of large arc collections for faster rendering
|
|
2930
|
+
// at zoomed-out scales.
|
|
2931
|
+
function enhanceArcCollectionForDisplay(unfilteredArcs) {
|
|
2932
|
+
var size = unfilteredArcs.getPointCount(),
|
|
2933
|
+
filteredArcs, filteredSegLen;
|
|
2932
2934
|
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
lyr.gui.displayArcs.setRetainedInterval(z);
|
|
2935
|
+
// Only generate low-detail arcs for larger datasets
|
|
2936
|
+
if (size > 5e5) {
|
|
2937
|
+
update();
|
|
2937
2938
|
}
|
|
2938
|
-
}
|
|
2939
2939
|
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2940
|
+
function update() {
|
|
2941
|
+
if (unfilteredArcs.getVertexData().zz) {
|
|
2942
|
+
// Use precalculated simplification data for vertex filtering, if available
|
|
2943
|
+
filteredArcs = initFilteredArcs(unfilteredArcs);
|
|
2944
|
+
filteredSegLen = internal.getAvgSegment(filteredArcs);
|
|
2945
|
+
} else {
|
|
2946
|
+
// Use fast simplification as a fallback
|
|
2947
|
+
filteredSegLen = internal.getAvgSegment(unfilteredArcs) * 4;
|
|
2948
|
+
filteredArcs = internal.simplifyArcsFast(unfilteredArcs, filteredSegLen);
|
|
2949
|
+
}
|
|
2943
2950
|
}
|
|
2944
|
-
}
|
|
2945
2951
|
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
d.r = 3; // show a black circle if layer is styled
|
|
2955
|
-
}
|
|
2956
|
-
if (layer.geometry_type == 'polyline' || layer.geometry_type == 'polygon') {
|
|
2957
|
-
if (fields.includes('stroke')) d.stroke = 'black';
|
|
2958
|
-
if (fields.includes('stroke-width')) d['stroke-width'] = 1;
|
|
2952
|
+
function initFilteredArcs(arcs) {
|
|
2953
|
+
var filterPct = 0.08;
|
|
2954
|
+
var nth = Math.ceil(arcs.getPointCount() / 5e5);
|
|
2955
|
+
var currInterval = arcs.getRetainedInterval();
|
|
2956
|
+
var filterZ = arcs.getThresholdByPct(filterPct, nth);
|
|
2957
|
+
var filteredArcs = arcs.setRetainedInterval(filterZ).getFilteredCopy();
|
|
2958
|
+
arcs.setRetainedInterval(currInterval); // reset current simplification
|
|
2959
|
+
return filteredArcs;
|
|
2959
2960
|
}
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2961
|
+
|
|
2962
|
+
// TODO: better job of detecting arc change... e.g. revision number
|
|
2963
|
+
unfilteredArcs.getScaledArcs = function(ext) {
|
|
2964
|
+
// check for changes in the number of arcs (probably due to editing)
|
|
2965
|
+
if (filteredArcs && filteredArcs.size() != unfilteredArcs.size()) {
|
|
2966
|
+
// arc count has changed... probably due to editing
|
|
2967
|
+
update();
|
|
2968
|
+
if (filteredArcs.size() != unfilteredArcs.size()) {
|
|
2969
|
+
throw Error('Internal error');
|
|
2970
|
+
}
|
|
2963
2971
|
}
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2972
|
+
if (filteredArcs) {
|
|
2973
|
+
// match simplification of unfiltered arcs
|
|
2974
|
+
filteredArcs.setRetainedInterval(unfilteredArcs.getRetainedInterval());
|
|
2975
|
+
}
|
|
2976
|
+
// switch to filtered version of arcs at small scales
|
|
2977
|
+
var unitsPerPixel = 1/ext.getTransform().mx,
|
|
2978
|
+
useFiltering = filteredArcs && unitsPerPixel > filteredSegLen * 1.5;
|
|
2979
|
+
return useFiltering ? filteredArcs : unfilteredArcs;
|
|
2980
|
+
};
|
|
2968
2981
|
}
|
|
2969
2982
|
|
|
2970
|
-
function
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2983
|
+
function getDisplayLayerForTable(tableArg) {
|
|
2984
|
+
var table = tableArg || new internal.DataTable(0),
|
|
2985
|
+
n = table.size(),
|
|
2986
|
+
cellWidth = 12,
|
|
2987
|
+
cellHeight = 5,
|
|
2988
|
+
gutter = 6,
|
|
2989
|
+
arcs = [],
|
|
2990
|
+
shapes = [],
|
|
2991
|
+
aspectRatio = 1.1,
|
|
2992
|
+
x, y, col, row, blockSize;
|
|
2976
2993
|
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2994
|
+
if (n > 10000) {
|
|
2995
|
+
arcs = null;
|
|
2996
|
+
gutter = 0;
|
|
2997
|
+
cellWidth = 4;
|
|
2998
|
+
cellHeight = 4;
|
|
2999
|
+
aspectRatio = 1.45;
|
|
3000
|
+
} else if (n > 5000) {
|
|
3001
|
+
cellWidth = 5;
|
|
3002
|
+
gutter = 3;
|
|
3003
|
+
aspectRatio = 1.45;
|
|
3004
|
+
} else if (n > 1000) {
|
|
3005
|
+
gutter = 3;
|
|
3006
|
+
cellWidth = 8;
|
|
3007
|
+
aspectRatio = 1.3;
|
|
2981
3008
|
}
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
3009
|
+
|
|
3010
|
+
if (n < 25) {
|
|
3011
|
+
blockSize = n;
|
|
3012
|
+
} else {
|
|
3013
|
+
blockSize = Math.sqrt(n * (cellWidth + gutter) / cellHeight / aspectRatio) | 0;
|
|
2986
3014
|
}
|
|
2987
|
-
}
|
|
2988
3015
|
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
3016
|
+
for (var i=0; i<n; i++) {
|
|
3017
|
+
row = i % blockSize;
|
|
3018
|
+
col = Math.floor(i / blockSize);
|
|
3019
|
+
x = col * (cellWidth + gutter);
|
|
3020
|
+
y = cellHeight * (blockSize - row);
|
|
3021
|
+
if (arcs) {
|
|
3022
|
+
arcs.push(getArc(x, y, cellWidth, cellHeight));
|
|
3023
|
+
shapes.push([[i]]);
|
|
3024
|
+
} else {
|
|
3025
|
+
shapes.push([[x, y]]);
|
|
3026
|
+
}
|
|
2996
3027
|
}
|
|
2997
|
-
appendVertex$1(lyr, p1);
|
|
2998
|
-
appendVertex$1(lyr, p2);
|
|
2999
|
-
appendNewDataRecord(lyr);
|
|
3000
|
-
}
|
|
3001
3028
|
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
internal.insertVertex(lyr.gui.source.dataset.arcs, id, p);
|
|
3005
|
-
if (isProjectedLayer(lyr)) {
|
|
3006
|
-
internal.insertVertex(lyr.gui.displayArcs, id, lyr.gui.projectPoint(p[0], p[1]));
|
|
3029
|
+
function getArc(x, y, w, h) {
|
|
3030
|
+
return [[x, y], [x + w, y], [x + w, y - h], [x, y - h], [x, y]];
|
|
3007
3031
|
}
|
|
3008
|
-
}
|
|
3009
3032
|
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3033
|
+
return {
|
|
3034
|
+
layer: {
|
|
3035
|
+
geometry_type: arcs ? 'polygon' : 'point',
|
|
3036
|
+
shapes: shapes,
|
|
3037
|
+
data: table
|
|
3038
|
+
},
|
|
3039
|
+
arcs: arcs ? new internal.ArcCollection(arcs) : null
|
|
3040
|
+
};
|
|
3013
3041
|
}
|
|
3014
3042
|
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
}
|
|
3043
|
+
var R = 6378137;
|
|
3044
|
+
var D2R = Math.PI / 180;
|
|
3045
|
+
var R2D = 180 / Math.PI;
|
|
3019
3046
|
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3047
|
+
// Assumes projections are available
|
|
3048
|
+
|
|
3049
|
+
function needReprojectionForDisplay(sourceCRS, displayCRS) {
|
|
3050
|
+
if (!sourceCRS || !displayCRS) {
|
|
3051
|
+
return false;
|
|
3052
|
+
}
|
|
3053
|
+
if (internal.crsAreEqual(sourceCRS, displayCRS)) {
|
|
3054
|
+
return false;
|
|
3024
3055
|
}
|
|
3056
|
+
return true;
|
|
3025
3057
|
}
|
|
3026
3058
|
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3059
|
+
// bbox in wgs84 coords
|
|
3060
|
+
// dest: CRS, which may also be wgs84
|
|
3061
|
+
function projectLatLonBBox(bbox, dest) {
|
|
3062
|
+
if (!dest || internal.isWGS84(dest)) {
|
|
3063
|
+
return bbox.concat();
|
|
3064
|
+
}
|
|
3065
|
+
var proj = internal.getProjTransform2(internal.parseCrsString('wgs84'), dest);
|
|
3066
|
+
var bbox2 = proj(bbox[0], bbox[1]).concat(proj(bbox[2], bbox[3]));
|
|
3067
|
+
return bbox2;
|
|
3030
3068
|
}
|
|
3031
3069
|
|
|
3032
|
-
function
|
|
3033
|
-
var
|
|
3034
|
-
|
|
3035
|
-
|
|
3070
|
+
function projectArcsForDisplay(arcs, src, dest) {
|
|
3071
|
+
var copy = arcs.getCopy(); // need to flatten first?
|
|
3072
|
+
var destIsWebMerc = internal.isWebMercator(dest);
|
|
3073
|
+
if (destIsWebMerc && internal.isWebMercator(src)) {
|
|
3074
|
+
return copy;
|
|
3075
|
+
}
|
|
3036
3076
|
|
|
3037
|
-
|
|
3038
|
-
var
|
|
3039
|
-
|
|
3077
|
+
var wgs84 = internal.parseCrsString('wgs84');
|
|
3078
|
+
var toWGS84 = internal.getProjTransform2(src, wgs84);
|
|
3079
|
+
var fromWGS84 = internal.getProjTransform2(wgs84, dest);
|
|
3080
|
+
|
|
3081
|
+
try {
|
|
3082
|
+
// first try projectArcs() -- it's fast and preserves arc ids
|
|
3083
|
+
// (so vertex editing doesn't break)
|
|
3084
|
+
if (!internal.isWGS84(src)) {
|
|
3085
|
+
// use wgs84 as a pivot CRS, so we can handle polar coordinates
|
|
3086
|
+
// that can't be projected to Mercator
|
|
3087
|
+
internal.projectArcs(copy, toWGS84);
|
|
3088
|
+
}
|
|
3089
|
+
if (destIsWebMerc) {
|
|
3090
|
+
// handle polar points by clamping them to they will project
|
|
3091
|
+
// (downside: may cause unexpected behavior when editing vertices interactively)
|
|
3092
|
+
clampY(copy);
|
|
3093
|
+
}
|
|
3094
|
+
internal.projectArcs(copy, fromWGS84);
|
|
3095
|
+
} catch(e) {
|
|
3096
|
+
console.error(e);
|
|
3097
|
+
// use the more robust projectArcs2 if projectArcs throws an error
|
|
3098
|
+
// downside: projectArcs2 discards Z values and changes arc indexing,
|
|
3099
|
+
// which will break vertex editing.
|
|
3100
|
+
var reproject = internal.getProjTransform2(src, dest);
|
|
3101
|
+
copy = arcs.getCopy();
|
|
3102
|
+
internal.projectArcs2(copy, reproject);
|
|
3103
|
+
}
|
|
3104
|
+
return copy;
|
|
3040
3105
|
}
|
|
3041
3106
|
|
|
3042
|
-
function
|
|
3043
|
-
|
|
3107
|
+
function clampY(arcs) {
|
|
3108
|
+
var max = 89.9,
|
|
3109
|
+
min = -89.9,
|
|
3110
|
+
bbox = arcs.getBounds().toArray();
|
|
3111
|
+
if (bbox[1] >= min && bbox[3] <= max) return;
|
|
3112
|
+
arcs.transformPoints(function(x, y) {
|
|
3113
|
+
if (y > max) return [x, max];
|
|
3114
|
+
if (y < min) return [x, min];
|
|
3115
|
+
});
|
|
3044
3116
|
}
|
|
3045
3117
|
|
|
3046
|
-
function
|
|
3047
|
-
|
|
3118
|
+
function projectPointsForDisplay(lyr, src, dest) {
|
|
3119
|
+
var copy = utils$1.extend({}, lyr);
|
|
3120
|
+
var proj = internal.getProjTransform2(src, dest);
|
|
3121
|
+
copy.shapes = internal.cloneShapes(lyr.shapes);
|
|
3122
|
+
internal.projectPointLayer(copy, proj);
|
|
3123
|
+
return copy;
|
|
3048
3124
|
}
|
|
3049
3125
|
|
|
3050
|
-
|
|
3051
|
-
function
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
}
|
|
3126
|
+
|
|
3127
|
+
function toWebMercator(lng, lat) {
|
|
3128
|
+
var k = Math.cos(lat * D2R);
|
|
3129
|
+
var x = R * lng * D2R;
|
|
3130
|
+
var y = R * Math.log(Math.tan(Math.PI * 0.25 + lat * D2R * 0.5));
|
|
3131
|
+
return [x, y];
|
|
3057
3132
|
}
|
|
3058
3133
|
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
lyr.shapes[fid] = projectPointCoords(coords, lyr.gui.projectPoint);
|
|
3064
|
-
}
|
|
3134
|
+
function fromWebMercator(x, y) {
|
|
3135
|
+
var lon = x / R * R2D;
|
|
3136
|
+
var lat = R2D * (Math.PI * 0.5 - 2 * Math.atan(Math.exp(-y / R)));
|
|
3137
|
+
return [lon, lat];
|
|
3065
3138
|
}
|
|
3066
3139
|
|
|
3067
|
-
function
|
|
3068
|
-
|
|
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);
|
|
3140
|
+
function scaleToZoom(metersPerPix) {
|
|
3141
|
+
return Math.log(40075017 / 512 / metersPerPix) / Math.log(2);
|
|
3071
3142
|
}
|
|
3072
3143
|
|
|
3073
|
-
function
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
if (isProjectedLayer(lyr)) {
|
|
3078
|
-
internal.snapVerticesToPoint([id], lyr.gui.projectPoint(p[0], p[1]), lyr.gui.displayArcs);
|
|
3079
|
-
}
|
|
3080
|
-
});
|
|
3144
|
+
function getMapboxBounds() {
|
|
3145
|
+
var ymax = toWebMercator(0, 84)[1];
|
|
3146
|
+
var ymin = toWebMercator(0, -84)[1];
|
|
3147
|
+
return [-Infinity, ymin, Infinity, ymax];
|
|
3081
3148
|
}
|
|
3082
3149
|
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
var
|
|
3087
|
-
|
|
3150
|
+
|
|
3151
|
+
// Update map extent and trigger redraw, after a new display CRS has been applied
|
|
3152
|
+
function projectMapExtent(ext, src, dest, newBounds) {
|
|
3153
|
+
var oldBounds = ext.getBounds();
|
|
3154
|
+
var oldScale = ext.scale();
|
|
3155
|
+
var newCP, proj, strictBounds;
|
|
3156
|
+
|
|
3157
|
+
if (dest && internal.isWebMercator(dest)) {
|
|
3158
|
+
// clampToMapboxBounds(newBounds);
|
|
3159
|
+
strictBounds = getMapboxBounds();
|
|
3160
|
+
}
|
|
3161
|
+
|
|
3162
|
+
// if source or destination CRS is unknown, show full extent
|
|
3163
|
+
// if map is at full extent, show full extent
|
|
3164
|
+
// TODO: handle case that scale is 1 and map is panned away from center
|
|
3165
|
+
if (ext.scale() == 1 || !dest) {
|
|
3166
|
+
ext.setFullBounds(newBounds, strictBounds);
|
|
3167
|
+
ext.reset();
|
|
3168
|
+
} else {
|
|
3169
|
+
// if map is zoomed, stay centered on the same geographic location, at the same relative scale
|
|
3170
|
+
proj = internal.getProjTransform2(src, dest);
|
|
3171
|
+
newCP = proj(oldBounds.centerX(), oldBounds.centerY());
|
|
3172
|
+
ext.setFullBounds(newBounds, strictBounds);
|
|
3173
|
+
if (!newCP) {
|
|
3174
|
+
// projection of center point failed; use center of bounds
|
|
3175
|
+
// (also consider just resetting the view using ext.home())
|
|
3176
|
+
newCP = [newBounds.centerX(), newBounds.centerY()];
|
|
3177
|
+
}
|
|
3178
|
+
ext.recenter(newCP[0], newCP[1], oldScale);
|
|
3179
|
+
}
|
|
3180
|
+
// trigger full redraw, in case some SVG symbols are unprojectable
|
|
3181
|
+
ext.dispatchEvent('change', {redraw: true});
|
|
3088
3182
|
}
|
|
3089
3183
|
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3184
|
+
// Called from console; for testing dynamic crs
|
|
3185
|
+
function setDisplayProjection(gui, cmd) {
|
|
3186
|
+
var arg = cmd.replace(/^projd[ ]*/, '');
|
|
3187
|
+
if (arg) {
|
|
3188
|
+
gui.map.setDisplayCRS(internal.parseCrsString(arg));
|
|
3189
|
+
} else {
|
|
3190
|
+
gui.map.setDisplayCRS(null);
|
|
3095
3191
|
}
|
|
3096
|
-
return dest.length ? dest : null;
|
|
3097
3192
|
}
|
|
3098
3193
|
|
|
3099
|
-
|
|
3100
|
-
|
|
3194
|
+
function filterLayerByIds(lyr, ids) {
|
|
3195
|
+
var shapes;
|
|
3196
|
+
if (lyr.shapes) {
|
|
3197
|
+
shapes = ids.map(function(id) {
|
|
3198
|
+
return lyr.shapes[id];
|
|
3199
|
+
});
|
|
3200
|
+
return utils$1.defaults({shapes: shapes, data: null}, lyr);
|
|
3201
|
+
}
|
|
3202
|
+
return lyr;
|
|
3203
|
+
}
|
|
3101
3204
|
|
|
3102
|
-
|
|
3103
|
-
|
|
3205
|
+
function formatLayerNameForDisplay(name) {
|
|
3206
|
+
return name || '[unnamed]';
|
|
3207
|
+
}
|
|
3104
3208
|
|
|
3105
|
-
|
|
3106
|
-
|
|
3209
|
+
function cleanLayerName(raw) {
|
|
3210
|
+
return raw.replace(/[\n\t/\\]/g, '')
|
|
3211
|
+
.replace(/^[.\s]+/, '').replace(/[.\s]+$/, '');
|
|
3212
|
+
}
|
|
3107
3213
|
|
|
3108
|
-
|
|
3109
|
-
|
|
3214
|
+
function updateLayerStackOrder(layers) {
|
|
3215
|
+
// 1. assign ascending ids to unassigned layers above the range of other layers
|
|
3216
|
+
layers.forEach(function(o, i) {
|
|
3217
|
+
if (!o.layer.menu_order) o.layer.menu_order = 1e6 + i;
|
|
3218
|
+
});
|
|
3219
|
+
// 2. sort in ascending order
|
|
3220
|
+
layers.sort(function(a, b) {
|
|
3221
|
+
return a.layer.menu_order - b.layer.menu_order;
|
|
3222
|
+
});
|
|
3223
|
+
// 3. assign consecutve ids
|
|
3224
|
+
layers.forEach(function(o, i) {
|
|
3225
|
+
o.layer.menu_order = i + 1;
|
|
3226
|
+
});
|
|
3227
|
+
return layers;
|
|
3228
|
+
}
|
|
3110
3229
|
|
|
3111
|
-
|
|
3112
|
-
|
|
3230
|
+
function sortLayersForMenuDisplay(layers) {
|
|
3231
|
+
layers = updateLayerStackOrder(layers);
|
|
3232
|
+
return layers.reverse();
|
|
3233
|
+
}
|
|
3113
3234
|
|
|
3114
|
-
|
|
3115
|
-
|
|
3235
|
+
// lyr: a map layer with gui property
|
|
3236
|
+
// displayCRS: CRS to use for display, or null (which clears any current display CRS)
|
|
3237
|
+
function projectLayerForDisplay(lyr, displayCRS) {
|
|
3238
|
+
var crsInfo = getDatasetCrsInfo(lyr.gui.source.dataset);
|
|
3239
|
+
var sourceCRS = crsInfo.crs || null; // let enhanceLayerForDisplay() handle null case
|
|
3240
|
+
if (!lyr.gui.geographic) {
|
|
3241
|
+
return;
|
|
3242
|
+
}
|
|
3243
|
+
if (lyr.gui.dynamic_crs && internal.crsAreEqual(sourceCRS, lyr.gui.dynamic_crs)) {
|
|
3244
|
+
return;
|
|
3245
|
+
}
|
|
3246
|
+
var gui = lyr.gui;
|
|
3247
|
+
enhanceLayerForDisplay(lyr, lyr.gui.source.dataset, {crs: displayCRS});
|
|
3248
|
+
utils$1.defaults(lyr.gui, gui); // re-apply any properties that were lost (e.g. svg_id)
|
|
3249
|
+
if (lyr.gui.style?.ids) {
|
|
3250
|
+
// re-apply layer filter
|
|
3251
|
+
lyr.gui.displayLayer = filterLayerByIds(lyr.gui.displayLayer, lyr.gui.style.ids);
|
|
3252
|
+
}
|
|
3253
|
+
}
|
|
3116
3254
|
|
|
3117
|
-
slider drag end
|
|
3118
|
-
-> [repair] intersection update
|
|
3119
3255
|
|
|
3120
|
-
|
|
3256
|
+
// Supplement a layer with information needed for rendering
|
|
3257
|
+
function enhanceLayerForDisplay(layer, dataset, opts) {
|
|
3258
|
+
var gui = {
|
|
3259
|
+
empty: internal.getFeatureCount(layer) === 0,
|
|
3260
|
+
geographic: false,
|
|
3261
|
+
displayArcs: null,
|
|
3262
|
+
displayLayer: null,
|
|
3263
|
+
source: {dataset},
|
|
3264
|
+
bounds: null,
|
|
3265
|
+
style: null,
|
|
3266
|
+
dynamic_crs: null,
|
|
3267
|
+
invertPoint: null,
|
|
3268
|
+
projectPoint: null
|
|
3269
|
+
};
|
|
3121
3270
|
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
var
|
|
3126
|
-
var
|
|
3127
|
-
var
|
|
3128
|
-
var
|
|
3271
|
+
var displayCRS = opts.crs || null;
|
|
3272
|
+
// display arcs may have been generated when another layer in the dataset
|
|
3273
|
+
// was converted for display... re-use if available
|
|
3274
|
+
var displayArcs = dataset.gui?.displayArcs;
|
|
3275
|
+
var unprojectable = false;
|
|
3276
|
+
var sourceCRS;
|
|
3277
|
+
var emptyArcs;
|
|
3129
3278
|
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
menu.hide();
|
|
3279
|
+
if (displayCRS && layer.geometry_type) {
|
|
3280
|
+
var crsInfo = getDatasetCrsInfo(dataset);
|
|
3281
|
+
if (crsInfo.error) {
|
|
3282
|
+
// unprojectable dataset -- return empty layer
|
|
3283
|
+
gui.unprojectable = true;
|
|
3136
3284
|
} else {
|
|
3137
|
-
|
|
3285
|
+
sourceCRS = crsInfo.crs;
|
|
3138
3286
|
}
|
|
3139
|
-
}
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3287
|
+
}
|
|
3288
|
+
|
|
3289
|
+
// Assume that dataset.displayArcs is in the display CRS
|
|
3290
|
+
// (it must be deleted upstream if reprojection is needed)
|
|
3291
|
+
// if (!obj.empty && dataset.arcs && !displayArcs) {
|
|
3292
|
+
if (dataset.arcs && !displayArcs && !gui.unprojectable) {
|
|
3293
|
+
// project arcs, if needed
|
|
3294
|
+
if (needReprojectionForDisplay(sourceCRS, displayCRS)) {
|
|
3295
|
+
displayArcs = projectArcsForDisplay(dataset.arcs, sourceCRS, displayCRS);
|
|
3143
3296
|
} else {
|
|
3144
|
-
|
|
3297
|
+
// Use original arcs for display if there is no dynamic reprojection
|
|
3298
|
+
displayArcs = dataset.arcs;
|
|
3299
|
+
}
|
|
3300
|
+
|
|
3301
|
+
enhanceArcCollectionForDisplay(displayArcs);
|
|
3302
|
+
dataset.gui = {displayArcs}; // stash these in the dataset for other layers to use
|
|
3303
|
+
}
|
|
3304
|
+
|
|
3305
|
+
if (internal.layerHasFurniture(layer)) {
|
|
3306
|
+
// TODO: consider how to render furniture in GUI
|
|
3307
|
+
// treating furniture layers (other than frame) as tabular for now,
|
|
3308
|
+
// so there is something to show if they are selected
|
|
3309
|
+
}
|
|
3310
|
+
|
|
3311
|
+
if (gui.unprojectable) {
|
|
3312
|
+
gui.displayLayer = {shapes: []}; // TODO: improve
|
|
3313
|
+
} else if (layer.geometry_type) {
|
|
3314
|
+
gui.geographic = true;
|
|
3315
|
+
gui.displayLayer = layer;
|
|
3316
|
+
gui.displayArcs = displayArcs;
|
|
3317
|
+
} else {
|
|
3318
|
+
var table = getDisplayLayerForTable(layer.data);
|
|
3319
|
+
gui.tabular = true;
|
|
3320
|
+
gui.displayLayer = table.layer;
|
|
3321
|
+
gui.displayArcs = table.arcs;
|
|
3322
|
+
}
|
|
3323
|
+
|
|
3324
|
+
// dynamic reprojection (arcs were already reprojected above)
|
|
3325
|
+
if (gui.geographic && needReprojectionForDisplay(sourceCRS, displayCRS)) {
|
|
3326
|
+
gui.dynamic_crs = displayCRS;
|
|
3327
|
+
gui.invertPoint = internal.getProjTransform2(displayCRS, sourceCRS);
|
|
3328
|
+
gui.projectPoint = internal.getProjTransform2(sourceCRS, displayCRS);
|
|
3329
|
+
if (internal.layerHasPoints(layer)) {
|
|
3330
|
+
gui.displayLayer = projectPointsForDisplay(layer, sourceCRS, displayCRS);
|
|
3331
|
+
} else if (internal.layerHasPaths(layer)) {
|
|
3332
|
+
emptyArcs = findEmptyArcs(displayArcs);
|
|
3333
|
+
if (emptyArcs.length > 0) {
|
|
3334
|
+
// Don't try to draw paths containing coordinates that failed to project
|
|
3335
|
+
gui.displayLayer = internal.filterPathLayerByArcIds(gui.displayLayer, emptyArcs);
|
|
3336
|
+
}
|
|
3337
|
+
}
|
|
3338
|
+
}
|
|
3339
|
+
|
|
3340
|
+
gui.bounds = getDisplayBounds(gui.displayLayer, gui.displayArcs);
|
|
3341
|
+
layer.gui = gui;
|
|
3342
|
+
}
|
|
3343
|
+
|
|
3344
|
+
function getDisplayBounds(lyr, arcs) {
|
|
3345
|
+
var bounds = internal.getLayerBounds(lyr, arcs) || new Bounds();
|
|
3346
|
+
if (lyr.geometry_type == 'point' && arcs && bounds.hasBounds() && bounds.area() > 0 === false) {
|
|
3347
|
+
// if a point layer has no extent (e.g. contains only a single point),
|
|
3348
|
+
// then merge with arc bounds, to place the point in context.
|
|
3349
|
+
bounds = bounds.mergeBounds(arcs.getBounds());
|
|
3350
|
+
}
|
|
3351
|
+
return bounds;
|
|
3352
|
+
}
|
|
3353
|
+
|
|
3354
|
+
// Returns an array of ids of empty arcs (arcs can be set to empty if errors occur while projecting them)
|
|
3355
|
+
function findEmptyArcs(arcs) {
|
|
3356
|
+
var nn = arcs.getVertexData().nn;
|
|
3357
|
+
var ids = [];
|
|
3358
|
+
for (var i=0, n=nn.length; i<n; i++) {
|
|
3359
|
+
if (nn[i] === 0) {
|
|
3360
|
+
ids.push(i);
|
|
3361
|
+
}
|
|
3362
|
+
}
|
|
3363
|
+
return ids;
|
|
3364
|
+
}
|
|
3365
|
+
|
|
3366
|
+
function flattenArcs(lyr) {
|
|
3367
|
+
lyr.gui.source.dataset.arcs.flatten();
|
|
3368
|
+
if (isProjectedLayer(lyr)) {
|
|
3369
|
+
lyr.gui.displayArcs.flatten();
|
|
3370
|
+
}
|
|
3371
|
+
}
|
|
3372
|
+
|
|
3373
|
+
function setZ(lyr, z) {
|
|
3374
|
+
lyr.gui.source.dataset.arcs.setRetainedInterval(z);
|
|
3375
|
+
if (isProjectedLayer(lyr)) {
|
|
3376
|
+
lyr.gui.displayArcs.setRetainedInterval(z);
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
3379
|
+
|
|
3380
|
+
function updateZ(lyr) {
|
|
3381
|
+
if (isProjectedLayer(lyr) && !lyr.gui.source.dataset.arcs.isFlat()) {
|
|
3382
|
+
lyr.gui.displayArcs.setThresholds(lyr.gui.source.dataset.arcs.getVertexData().zz);
|
|
3383
|
+
}
|
|
3384
|
+
}
|
|
3385
|
+
|
|
3386
|
+
function appendNewDataRecord(layer) {
|
|
3387
|
+
if (!layer.data) return null;
|
|
3388
|
+
var fields = layer.data.getFields();
|
|
3389
|
+
var d = getEmptyDataRecord(layer.data);
|
|
3390
|
+
// TODO: handle SVG symbol layer
|
|
3391
|
+
if (internal.layerHasLabels(layer)) {
|
|
3392
|
+
d['label-text'] = 'TBD'; // without text, new labels will be invisible
|
|
3393
|
+
} else if (layer.geometry_type == 'point' && fields.includes('r')) {
|
|
3394
|
+
d.r = 3; // show a black circle if layer is styled
|
|
3395
|
+
}
|
|
3396
|
+
if (layer.geometry_type == 'polyline' || layer.geometry_type == 'polygon') {
|
|
3397
|
+
if (fields.includes('stroke')) d.stroke = 'black';
|
|
3398
|
+
if (fields.includes('stroke-width')) d['stroke-width'] = 1;
|
|
3399
|
+
}
|
|
3400
|
+
if (layer.geometry_type == 'polygon') {
|
|
3401
|
+
if (fields.includes('fill')) {
|
|
3402
|
+
d.fill = 'rgba(0,0,0,0.10)'; // 'rgba(249,120,249,0.20)';
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
3405
|
+
// TODO: better styling
|
|
3406
|
+
layer.data.getRecords().push(d);
|
|
3407
|
+
return d;
|
|
3408
|
+
}
|
|
3409
|
+
|
|
3410
|
+
function getEmptyDataRecord(table) {
|
|
3411
|
+
return table.getFields().reduce(function(memo, name) {
|
|
3412
|
+
memo[name] = null;
|
|
3413
|
+
return memo;
|
|
3414
|
+
}, {});
|
|
3415
|
+
}
|
|
3416
|
+
|
|
3417
|
+
// p1, p2: two points in source data CRS coords.
|
|
3418
|
+
function appendNewPath(lyr, p1, p2) {
|
|
3419
|
+
var arcId = lyr.gui.displayArcs.size();
|
|
3420
|
+
internal.appendEmptyArc(lyr.gui.displayArcs);
|
|
3421
|
+
lyr.shapes.push([[arcId]]);
|
|
3422
|
+
if (isProjectedLayer(lyr)) {
|
|
3423
|
+
internal.appendEmptyArc(lyr.gui.source.dataset.arcs);
|
|
3424
|
+
}
|
|
3425
|
+
appendVertex$1(lyr, p1);
|
|
3426
|
+
appendVertex$1(lyr, p2);
|
|
3427
|
+
appendNewDataRecord(lyr);
|
|
3428
|
+
}
|
|
3429
|
+
|
|
3430
|
+
function deleteLastPath(lyr) {
|
|
3431
|
+
var arcId = lyr.gui.displayArcs.size() - 1;
|
|
3432
|
+
if (lyr.data) {
|
|
3433
|
+
lyr.data.getRecords().pop();
|
|
3434
|
+
}
|
|
3435
|
+
var shp = lyr.shapes.pop();
|
|
3436
|
+
internal.deleteLastArc(lyr.gui.displayArcs);
|
|
3437
|
+
if (isProjectedLayer(lyr)) {
|
|
3438
|
+
internal.deleteLastArc(lyr.gui.source.dataset.arcs);
|
|
3439
|
+
}
|
|
3440
|
+
}
|
|
3441
|
+
|
|
3442
|
+
// p: one point in source data coords
|
|
3443
|
+
function appendNewPoint(lyr, p) {
|
|
3444
|
+
lyr.shapes.push([p]);
|
|
3445
|
+
if (lyr.data) {
|
|
3446
|
+
appendNewDataRecord(lyr);
|
|
3447
|
+
}
|
|
3448
|
+
// this reprojects all the points... TODO: improve
|
|
3449
|
+
if (isProjectedLayer(lyr)) {
|
|
3450
|
+
projectLayerForDisplay(lyr, lyr.gui.dynamic_crs);
|
|
3451
|
+
}
|
|
3452
|
+
}
|
|
3453
|
+
|
|
3454
|
+
function deleteLastPoint(lyr) {
|
|
3455
|
+
if (lyr.data) {
|
|
3456
|
+
lyr.data.getRecords().pop();
|
|
3457
|
+
}
|
|
3458
|
+
lyr.shapes.pop();
|
|
3459
|
+
if (isProjectedLayer(lyr)) {
|
|
3460
|
+
lyr.gui.displayLayer.shapes.pop();
|
|
3461
|
+
}
|
|
3462
|
+
}
|
|
3463
|
+
|
|
3464
|
+
// p: point in source data CRS coords.
|
|
3465
|
+
function insertVertex$1(lyr, id, p) {
|
|
3466
|
+
internal.insertVertex(lyr.gui.source.dataset.arcs, id, p);
|
|
3467
|
+
if (isProjectedLayer(lyr)) {
|
|
3468
|
+
internal.insertVertex(lyr.gui.displayArcs, id, lyr.gui.projectPoint(p[0], p[1]));
|
|
3469
|
+
}
|
|
3470
|
+
}
|
|
3471
|
+
|
|
3472
|
+
function appendVertex$1(lyr, p) {
|
|
3473
|
+
var n = lyr.gui.source.dataset.arcs.getPointCount();
|
|
3474
|
+
insertVertex$1(lyr, n, p);
|
|
3475
|
+
}
|
|
3476
|
+
|
|
3477
|
+
// TODO: make sure we're not also removing an entire arc
|
|
3478
|
+
function deleteLastVertex(lyr) {
|
|
3479
|
+
deleteVertex$1(lyr, lyr.gui.displayArcs.getPointCount() - 1);
|
|
3480
|
+
}
|
|
3481
|
+
|
|
3482
|
+
|
|
3483
|
+
function deleteVertex$1(lyr, id) {
|
|
3484
|
+
internal.deleteVertex(lyr.gui.displayArcs, id);
|
|
3485
|
+
if (isProjectedLayer(lyr)) {
|
|
3486
|
+
internal.deleteVertex(lyr.gui.source.dataset.arcs, id);
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
|
|
3490
|
+
function getLastArcCoords(target) {
|
|
3491
|
+
var arcId = target.gui.source.dataset.arcs.size() - 1;
|
|
3492
|
+
return internal.getUnfilteredArcCoords(arcId, target.gui.source.dataset.arcs);
|
|
3493
|
+
}
|
|
3494
|
+
|
|
3495
|
+
function getLastVertexCoords(target) {
|
|
3496
|
+
var arcs = target.gui.source.dataset.arcs;
|
|
3497
|
+
return internal.getVertexCoords(arcs.getPointCount() - 1, arcs);
|
|
3498
|
+
}
|
|
3499
|
+
|
|
3500
|
+
function getLastArcLength(target) {
|
|
3501
|
+
var arcId = target.gui.source.dataset.arcs.size() - 1;
|
|
3502
|
+
return internal.getUnfilteredArcLength(arcId, target.gui.source.dataset.arcs);
|
|
3503
|
+
}
|
|
3504
|
+
|
|
3505
|
+
function getVertexCoords(lyr, id) {
|
|
3506
|
+
return lyr.gui.source.dataset.arcs.getVertex2(id);
|
|
3507
|
+
}
|
|
3508
|
+
|
|
3509
|
+
// set data coords (not display coords) of one or more vertices.
|
|
3510
|
+
function setVertexCoords(lyr, ids, dataPoint) {
|
|
3511
|
+
internal.snapVerticesToPoint(ids, dataPoint, lyr.gui.source.dataset.arcs);
|
|
3512
|
+
if (isProjectedLayer(lyr)) {
|
|
3513
|
+
var p = lyr.gui.projectPoint(dataPoint[0], dataPoint[1]);
|
|
3514
|
+
internal.snapVerticesToPoint(ids, p, lyr.gui.displayArcs);
|
|
3515
|
+
}
|
|
3516
|
+
}
|
|
3517
|
+
|
|
3518
|
+
function updateVertexCoords(lyr, ids) {
|
|
3519
|
+
if (!isProjectedLayer(lyr)) return;
|
|
3520
|
+
var p = lyr.gui.displayArcs.getVertex2(ids[0]);
|
|
3521
|
+
internal.snapVerticesToPoint(ids, lyr.gui.invertPoint(p[0], p[1]), lyr.gui.source.dataset.arcs);
|
|
3522
|
+
}
|
|
3523
|
+
|
|
3524
|
+
function setRectangleCoords(lyr, ids, coords) {
|
|
3525
|
+
ids.forEach(function(id, i) {
|
|
3526
|
+
var p = coords[i];
|
|
3527
|
+
internal.snapVerticesToPoint([id], p, lyr.gui.source.dataset.arcs);
|
|
3528
|
+
if (isProjectedLayer(lyr)) {
|
|
3529
|
+
internal.snapVerticesToPoint([id], lyr.gui.projectPoint(p[0], p[1]), lyr.gui.displayArcs);
|
|
3530
|
+
}
|
|
3531
|
+
});
|
|
3532
|
+
}
|
|
3533
|
+
|
|
3534
|
+
// Update source data coordinates by projecting display coordinates
|
|
3535
|
+
function updatePointCoords(lyr, fid) {
|
|
3536
|
+
if (!isProjectedLayer(lyr)) return;
|
|
3537
|
+
var displayShp = lyr.gui.displayLayer.shapes[fid];
|
|
3538
|
+
lyr.shapes[fid] = projectPointCoords(displayShp, lyr.gui.invertPoint);
|
|
3539
|
+
}
|
|
3540
|
+
|
|
3541
|
+
// coords: [[x, y]] point in data CRS (not display CRS)
|
|
3542
|
+
function setPointCoords(lyr, fid, coords) {
|
|
3543
|
+
lyr.shapes[fid] = coords;
|
|
3544
|
+
if (isProjectedLayer(lyr)) {
|
|
3545
|
+
lyr.gui.displayLayer.shapes[fid] = projectPointCoords(coords, lyr.gui.projectPoint);
|
|
3546
|
+
}
|
|
3547
|
+
}
|
|
3548
|
+
|
|
3549
|
+
// return an [[x, y]] point in data CRS
|
|
3550
|
+
function getPointCoords(lyr, fid) {
|
|
3551
|
+
var coords = lyr.geometry_type == 'point' && lyr.shapes[fid];
|
|
3552
|
+
if (!coords || coords.length != 1) {
|
|
3553
|
+
return null;
|
|
3554
|
+
}
|
|
3555
|
+
return internal.cloneShape(coords);
|
|
3556
|
+
}
|
|
3557
|
+
|
|
3558
|
+
// export function getPointCoords(lyr, fid) {
|
|
3559
|
+
// return internal.cloneShape(lyr.shapes[fid]);
|
|
3560
|
+
// }
|
|
3561
|
+
|
|
3562
|
+
function projectPointCoords(src, proj) {
|
|
3563
|
+
var dest = [], p;
|
|
3564
|
+
for (var i=0; i<src.length; i++) {
|
|
3565
|
+
p = proj(src[i][0], src[i][1]);
|
|
3566
|
+
if (p) dest.push(p);
|
|
3567
|
+
}
|
|
3568
|
+
return dest.length ? dest : null;
|
|
3569
|
+
}
|
|
3570
|
+
|
|
3571
|
+
/*
|
|
3572
|
+
How changes in the simplify control should affect other components
|
|
3573
|
+
|
|
3574
|
+
data calculated, 100% simplification
|
|
3575
|
+
-> [map] filtered arcs update
|
|
3576
|
+
|
|
3577
|
+
data calculated, <100% simplification
|
|
3578
|
+
-> [map] filtered arcs update, redraw; [repair] intersection update
|
|
3579
|
+
|
|
3580
|
+
change via text field
|
|
3581
|
+
-> [map] redraw; [repair] intersection update
|
|
3582
|
+
|
|
3583
|
+
slider drag start
|
|
3584
|
+
-> [repair] hide display
|
|
3585
|
+
|
|
3586
|
+
slider drag
|
|
3587
|
+
-> [map] redraw
|
|
3588
|
+
|
|
3589
|
+
slider drag end
|
|
3590
|
+
-> [repair] intersection update
|
|
3591
|
+
|
|
3592
|
+
*/
|
|
3593
|
+
|
|
3594
|
+
var SimplifyControl = function(gui) {
|
|
3595
|
+
var model = gui.model;
|
|
3596
|
+
var control = {};
|
|
3597
|
+
var _value = 1;
|
|
3598
|
+
var el = gui.container.findChild('.simplify-control-wrapper');
|
|
3599
|
+
var menu = gui.container.findChild('.simplify-options');
|
|
3600
|
+
var slider, text, fromPct;
|
|
3601
|
+
var menuBtn = gui.container.findChild('.simplify-btn').addClass('disabled');
|
|
3602
|
+
|
|
3603
|
+
// init settings menu
|
|
3604
|
+
new SimpleButton(menu.findChild('.submit-btn').addClass('default-btn')).on('click', onSubmit);
|
|
3605
|
+
new SimpleButton(menu.findChild('.close2-btn')).on('click', function() {
|
|
3606
|
+
if (el.visible()) {
|
|
3607
|
+
// cancel just hides menu if slider is visible
|
|
3608
|
+
menu.hide();
|
|
3609
|
+
} else {
|
|
3610
|
+
gui.clearMode();
|
|
3611
|
+
}
|
|
3612
|
+
});
|
|
3613
|
+
new SimpleButton(el.findChild('.simplify-settings-btn')).on('click', function() {
|
|
3614
|
+
if (menu.visible()) {
|
|
3615
|
+
menu.hide();
|
|
3616
|
+
} else {
|
|
3617
|
+
showMenu();
|
|
3145
3618
|
}
|
|
3146
3619
|
});
|
|
3147
3620
|
gui.keyboard.onMenuSubmit(menu, onSubmit);
|
|
3148
3621
|
|
|
3149
3622
|
// init simplify button and mode
|
|
3150
|
-
gui.addMode('simplify', turnOn, turnOff,
|
|
3151
|
-
|
|
3623
|
+
gui.addMode('simplify', turnOn, turnOff, menuBtn);
|
|
3624
|
+
|
|
3625
|
+
model.on('update', function() {
|
|
3626
|
+
menuBtn.classed('disabled', !model.getActiveLayer());
|
|
3152
3627
|
if (gui.getMode() == 'simplify') gui.clearMode();
|
|
3153
3628
|
});
|
|
3154
3629
|
|
|
3630
|
+
|
|
3155
3631
|
// exit simplify mode when user clicks off the visible part of the menu
|
|
3156
3632
|
menu.on('click', GUI.handleDirectEvent(gui.clearMode));
|
|
3157
3633
|
|
|
@@ -3331,144 +3807,6 @@
|
|
|
3331
3807
|
}
|
|
3332
3808
|
};
|
|
3333
3809
|
|
|
3334
|
-
var R = 6378137;
|
|
3335
|
-
var D2R = Math.PI / 180;
|
|
3336
|
-
var R2D = 180 / Math.PI;
|
|
3337
|
-
|
|
3338
|
-
// Assumes projections are available
|
|
3339
|
-
|
|
3340
|
-
function needReprojectionForDisplay(sourceCRS, displayCRS) {
|
|
3341
|
-
if (!sourceCRS || !displayCRS) {
|
|
3342
|
-
return false;
|
|
3343
|
-
}
|
|
3344
|
-
if (internal.crsAreEqual(sourceCRS, displayCRS)) {
|
|
3345
|
-
return false;
|
|
3346
|
-
}
|
|
3347
|
-
return true;
|
|
3348
|
-
}
|
|
3349
|
-
|
|
3350
|
-
function projectArcsForDisplay(arcs, src, dest) {
|
|
3351
|
-
var copy = arcs.getCopy(); // need to flatten first?
|
|
3352
|
-
var destIsWebMerc = internal.isWebMercator(dest);
|
|
3353
|
-
if (destIsWebMerc && internal.isWebMercator(src)) {
|
|
3354
|
-
return copy;
|
|
3355
|
-
}
|
|
3356
|
-
|
|
3357
|
-
var wgs84 = internal.parseCrsString('wgs84');
|
|
3358
|
-
var toWGS84 = internal.getProjTransform2(src, wgs84);
|
|
3359
|
-
var fromWGS84 = internal.getProjTransform2(wgs84, dest);
|
|
3360
|
-
|
|
3361
|
-
try {
|
|
3362
|
-
// first try projectArcs() -- it's fast and preserves arc ids
|
|
3363
|
-
// (so vertex editing doesn't break)
|
|
3364
|
-
if (!internal.isWGS84(src)) {
|
|
3365
|
-
// use wgs84 as a pivot CRS, so we can handle polar coordinates
|
|
3366
|
-
// that can't be projected to Mercator
|
|
3367
|
-
internal.projectArcs(copy, toWGS84);
|
|
3368
|
-
}
|
|
3369
|
-
if (destIsWebMerc) {
|
|
3370
|
-
// handle polar points by clamping them to they will project
|
|
3371
|
-
// (downside: may cause unexpected behavior when editing vertices interactively)
|
|
3372
|
-
clampY(copy);
|
|
3373
|
-
}
|
|
3374
|
-
internal.projectArcs(copy, fromWGS84);
|
|
3375
|
-
} catch(e) {
|
|
3376
|
-
console.error(e);
|
|
3377
|
-
// use the more robust projectArcs2 if projectArcs throws an error
|
|
3378
|
-
// downside: projectArcs2 discards Z values and changes arc indexing,
|
|
3379
|
-
// which will break vertex editing.
|
|
3380
|
-
var reproject = internal.getProjTransform2(src, dest);
|
|
3381
|
-
copy = arcs.getCopy();
|
|
3382
|
-
internal.projectArcs2(copy, reproject);
|
|
3383
|
-
}
|
|
3384
|
-
return copy;
|
|
3385
|
-
}
|
|
3386
|
-
|
|
3387
|
-
function clampY(arcs) {
|
|
3388
|
-
var max = 89.9,
|
|
3389
|
-
min = -89.9,
|
|
3390
|
-
bbox = arcs.getBounds().toArray();
|
|
3391
|
-
if (bbox[1] >= min && bbox[3] <= max) return;
|
|
3392
|
-
arcs.transformPoints(function(x, y) {
|
|
3393
|
-
if (y > max) return [x, max];
|
|
3394
|
-
if (y < min) return [x, min];
|
|
3395
|
-
});
|
|
3396
|
-
}
|
|
3397
|
-
|
|
3398
|
-
function projectPointsForDisplay(lyr, src, dest) {
|
|
3399
|
-
var copy = utils$1.extend({}, lyr);
|
|
3400
|
-
var proj = internal.getProjTransform2(src, dest);
|
|
3401
|
-
copy.shapes = internal.cloneShapes(lyr.shapes);
|
|
3402
|
-
internal.projectPointLayer(copy, proj);
|
|
3403
|
-
return copy;
|
|
3404
|
-
}
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
function toWebMercator(lng, lat) {
|
|
3408
|
-
var k = Math.cos(lat * D2R);
|
|
3409
|
-
var x = R * lng * D2R;
|
|
3410
|
-
var y = R * Math.log(Math.tan(Math.PI * 0.25 + lat * D2R * 0.5));
|
|
3411
|
-
return [x, y];
|
|
3412
|
-
}
|
|
3413
|
-
|
|
3414
|
-
function fromWebMercator(x, y) {
|
|
3415
|
-
var lon = x / R * R2D;
|
|
3416
|
-
var lat = R2D * (Math.PI * 0.5 - 2 * Math.atan(Math.exp(-y / R)));
|
|
3417
|
-
return [lon, lat];
|
|
3418
|
-
}
|
|
3419
|
-
|
|
3420
|
-
function scaleToZoom(metersPerPix) {
|
|
3421
|
-
return Math.log(40075017 / 512 / metersPerPix) / Math.log(2);
|
|
3422
|
-
}
|
|
3423
|
-
|
|
3424
|
-
function getMapboxBounds() {
|
|
3425
|
-
var ymax = toWebMercator(0, 84)[1];
|
|
3426
|
-
var ymin = toWebMercator(0, -84)[1];
|
|
3427
|
-
return [-Infinity, ymin, Infinity, ymax];
|
|
3428
|
-
}
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
// Update map extent and trigger redraw, after a new display CRS has been applied
|
|
3432
|
-
function projectMapExtent(ext, src, dest, newBounds) {
|
|
3433
|
-
var oldBounds = ext.getBounds();
|
|
3434
|
-
var oldScale = ext.scale();
|
|
3435
|
-
var newCP, proj, strictBounds;
|
|
3436
|
-
|
|
3437
|
-
if (dest && internal.isWebMercator(dest)) {
|
|
3438
|
-
// clampToMapboxBounds(newBounds);
|
|
3439
|
-
strictBounds = getMapboxBounds();
|
|
3440
|
-
}
|
|
3441
|
-
|
|
3442
|
-
// if source or destination CRS is unknown, show full extent
|
|
3443
|
-
// if map is at full extent, show full extent
|
|
3444
|
-
// TODO: handle case that scale is 1 and map is panned away from center
|
|
3445
|
-
if (ext.scale() == 1 || !dest) {
|
|
3446
|
-
ext.setFullBounds(newBounds, strictBounds);
|
|
3447
|
-
ext.home(); // sets full extent and triggers redraw
|
|
3448
|
-
} else {
|
|
3449
|
-
// if map is zoomed, stay centered on the same geographic location, at the same relative scale
|
|
3450
|
-
proj = internal.getProjTransform2(src, dest);
|
|
3451
|
-
newCP = proj(oldBounds.centerX(), oldBounds.centerY());
|
|
3452
|
-
ext.setFullBounds(newBounds, strictBounds);
|
|
3453
|
-
if (!newCP) {
|
|
3454
|
-
// projection of center point failed; use center of bounds
|
|
3455
|
-
// (also consider just resetting the view using ext.home())
|
|
3456
|
-
newCP = [newBounds.centerX(), newBounds.centerY()];
|
|
3457
|
-
}
|
|
3458
|
-
ext.recenter(newCP[0], newCP[1], oldScale);
|
|
3459
|
-
}
|
|
3460
|
-
}
|
|
3461
|
-
|
|
3462
|
-
// Called from console; for testing dynamic crs
|
|
3463
|
-
function setDisplayProjection(gui, cmd) {
|
|
3464
|
-
var arg = cmd.replace(/^projd[ ]*/, '');
|
|
3465
|
-
if (arg) {
|
|
3466
|
-
gui.map.setDisplayCRS(internal.parseCrsString(arg));
|
|
3467
|
-
} else {
|
|
3468
|
-
gui.map.setDisplayCRS(null);
|
|
3469
|
-
}
|
|
3470
|
-
}
|
|
3471
|
-
|
|
3472
3810
|
function Console(gui) {
|
|
3473
3811
|
var model = gui.model;
|
|
3474
3812
|
var CURSOR = '$ ';
|
|
@@ -3537,7 +3875,8 @@
|
|
|
3537
3875
|
}
|
|
3538
3876
|
|
|
3539
3877
|
function turnOn() {
|
|
3540
|
-
if (!_isOpen && !model.isEmpty()) {
|
|
3878
|
+
// if (!_isOpen && !model.isEmpty()) {
|
|
3879
|
+
if (!_isOpen) {
|
|
3541
3880
|
btn.addClass('active');
|
|
3542
3881
|
_isOpen = true;
|
|
3543
3882
|
// use console for messages while open
|
|
@@ -3876,8 +4215,8 @@
|
|
|
3876
4215
|
|
|
3877
4216
|
function applyParsedCommands(commands, done) {
|
|
3878
4217
|
var active = model.getActiveLayer(),
|
|
3879
|
-
prevArcs = active
|
|
3880
|
-
prevTable = active
|
|
4218
|
+
prevArcs = active?.dataset.arcs,
|
|
4219
|
+
prevTable = active?.layer.data,
|
|
3881
4220
|
prevTableSize = prevTable ? prevTable.size() : 0,
|
|
3882
4221
|
prevArcCount = prevArcs ? prevArcs.size() : 0,
|
|
3883
4222
|
job = new internal.Job(model);
|
|
@@ -3886,9 +4225,9 @@
|
|
|
3886
4225
|
internal.runParsedCommands(commands, job, function(err) {
|
|
3887
4226
|
var flags = getCommandFlags(commands),
|
|
3888
4227
|
active2 = model.getActiveLayer(),
|
|
3889
|
-
postArcs = active2
|
|
4228
|
+
postArcs = active2?.dataset.arcs,
|
|
3890
4229
|
postArcCount = postArcs ? postArcs.size() : 0,
|
|
3891
|
-
postTable = active2
|
|
4230
|
+
postTable = active2?.layer.data,
|
|
3892
4231
|
postTableSize = postTable ? postTable.size() : 0,
|
|
3893
4232
|
sameTable = prevTable == postTable && prevTableSize == postTableSize,
|
|
3894
4233
|
sameArcs = prevArcs == postArcs && postArcCount == prevArcCount;
|
|
@@ -3901,7 +4240,7 @@
|
|
|
3901
4240
|
if (sameTable) {
|
|
3902
4241
|
flags.same_table = true;
|
|
3903
4242
|
}
|
|
3904
|
-
if (active
|
|
4243
|
+
if (active && active?.layer == active2?.layer) {
|
|
3905
4244
|
// this can get set after some commands that don't set a new target
|
|
3906
4245
|
// (e.g. -dissolve)
|
|
3907
4246
|
flags.select = true;
|
|
@@ -3916,10 +4255,10 @@
|
|
|
3916
4255
|
function onError(err) {
|
|
3917
4256
|
if (utils$1.isString(err)) {
|
|
3918
4257
|
consoleStop(err);
|
|
3919
|
-
} else if (err.name == 'UserError') {
|
|
4258
|
+
} else if (err.name == 'UserError' ) {
|
|
3920
4259
|
// stop() has already been called, don't need to log
|
|
4260
|
+
console.error(err.stack);
|
|
3921
4261
|
} else if (err.name) {
|
|
3922
|
-
// log stack trace to browser console
|
|
3923
4262
|
console.error(err.stack);
|
|
3924
4263
|
// log to console window
|
|
3925
4264
|
consoleWarning(err.message);
|
|
@@ -4102,6 +4441,7 @@
|
|
|
4102
4441
|
return;
|
|
4103
4442
|
}
|
|
4104
4443
|
var e = model.getActiveLayer();
|
|
4444
|
+
if (!e) return;
|
|
4105
4445
|
if (internal.layerHasPaths(e.layer)) {
|
|
4106
4446
|
el.show();
|
|
4107
4447
|
checkBtn.show();
|
|
@@ -4180,47 +4520,6 @@
|
|
|
4180
4520
|
|
|
4181
4521
|
utils$1.inherit(RepairControl, EventDispatcher);
|
|
4182
4522
|
|
|
4183
|
-
function filterLayerByIds(lyr, ids) {
|
|
4184
|
-
var shapes;
|
|
4185
|
-
if (lyr.shapes) {
|
|
4186
|
-
shapes = ids.map(function(id) {
|
|
4187
|
-
return lyr.shapes[id];
|
|
4188
|
-
});
|
|
4189
|
-
return utils$1.defaults({shapes: shapes, data: null}, lyr);
|
|
4190
|
-
}
|
|
4191
|
-
return lyr;
|
|
4192
|
-
}
|
|
4193
|
-
|
|
4194
|
-
function formatLayerNameForDisplay(name) {
|
|
4195
|
-
return name || '[unnamed]';
|
|
4196
|
-
}
|
|
4197
|
-
|
|
4198
|
-
function cleanLayerName(raw) {
|
|
4199
|
-
return raw.replace(/[\n\t/\\]/g, '')
|
|
4200
|
-
.replace(/^[.\s]+/, '').replace(/[.\s]+$/, '');
|
|
4201
|
-
}
|
|
4202
|
-
|
|
4203
|
-
function updateLayerStackOrder(layers) {
|
|
4204
|
-
// 1. assign ascending ids to unassigned layers above the range of other layers
|
|
4205
|
-
layers.forEach(function(o, i) {
|
|
4206
|
-
if (!o.layer.menu_order) o.layer.menu_order = 1e6 + i;
|
|
4207
|
-
});
|
|
4208
|
-
// 2. sort in ascending order
|
|
4209
|
-
layers.sort(function(a, b) {
|
|
4210
|
-
return a.layer.menu_order - b.layer.menu_order;
|
|
4211
|
-
});
|
|
4212
|
-
// 3. assign consecutve ids
|
|
4213
|
-
layers.forEach(function(o, i) {
|
|
4214
|
-
o.layer.menu_order = i + 1;
|
|
4215
|
-
});
|
|
4216
|
-
return layers;
|
|
4217
|
-
}
|
|
4218
|
-
|
|
4219
|
-
function sortLayersForMenuDisplay(layers) {
|
|
4220
|
-
layers = updateLayerStackOrder(layers);
|
|
4221
|
-
return layers.reverse();
|
|
4222
|
-
}
|
|
4223
|
-
|
|
4224
4523
|
async function saveFileContentToClipboard(content) {
|
|
4225
4524
|
var str = utils$1.isString(content) ? content : content.toString();
|
|
4226
4525
|
await navigator.clipboard.writeText(str);
|
|
@@ -4233,7 +4532,7 @@
|
|
|
4233
4532
|
var menu = gui.container.findChild('.export-options').on('click', GUI.handleDirectEvent(gui.clearMode));
|
|
4234
4533
|
var layersArr = [];
|
|
4235
4534
|
var toggleBtn = null; // checkbox <input> for toggling layer selection
|
|
4236
|
-
var exportBtn = gui.container.findChild('.export-btn');
|
|
4535
|
+
var exportBtn = gui.container.findChild('.export-btn').addClass('disabled');
|
|
4237
4536
|
var ofileName = gui.container.findChild('#ofile-name');
|
|
4238
4537
|
new SimpleButton(menu.findChild('.close2-btn')).on('click', gui.clearMode);
|
|
4239
4538
|
|
|
@@ -4248,6 +4547,10 @@
|
|
|
4248
4547
|
return;
|
|
4249
4548
|
}
|
|
4250
4549
|
|
|
4550
|
+
model.on('update', function() {
|
|
4551
|
+
exportBtn.classed('disabled', !model.getActiveLayer());
|
|
4552
|
+
});
|
|
4553
|
+
|
|
4251
4554
|
new SimpleButton(menu.findChild('#export-btn').addClass('default-btn')).on('click', onExportClick);
|
|
4252
4555
|
gui.addMode('export', turnOn, turnOff, exportBtn);
|
|
4253
4556
|
gui.keyboard.onMenuSubmit(menu, onExportClick);
|
|
@@ -4578,6 +4881,14 @@
|
|
|
4578
4881
|
var layerOrderSlug;
|
|
4579
4882
|
|
|
4580
4883
|
gui.addMode('layer_menu', turnOn, turnOff, btn.findChild('.header-btn'));
|
|
4884
|
+
|
|
4885
|
+
// kludge to show menu button after initial import dialog is dismissed
|
|
4886
|
+
gui.on('mode', function(e) {
|
|
4887
|
+
if (!e.name) {
|
|
4888
|
+
updateMenuBtn();
|
|
4889
|
+
}
|
|
4890
|
+
});
|
|
4891
|
+
|
|
4581
4892
|
model.on('update', function(e) {
|
|
4582
4893
|
updateMenuBtn();
|
|
4583
4894
|
if (isOpen) render();
|
|
@@ -4668,8 +4979,9 @@
|
|
|
4668
4979
|
}
|
|
4669
4980
|
|
|
4670
4981
|
function updateMenuBtn() {
|
|
4671
|
-
var
|
|
4672
|
-
var
|
|
4982
|
+
var lyr = model.getActiveLayer()?.layer;
|
|
4983
|
+
var lyrName = lyr?.name || '';
|
|
4984
|
+
var menuTitle = lyrName || lyr && '[unnamed layer]' || '[no data]';
|
|
4673
4985
|
var pageTitle = lyrName || 'mapshaper';
|
|
4674
4986
|
btn.classed('active', 'true').findChild('.layer-name').html(menuTitle + " ▼");
|
|
4675
4987
|
window.document.title = pageTitle;
|
|
@@ -4681,6 +4993,8 @@
|
|
|
4681
4993
|
}
|
|
4682
4994
|
|
|
4683
4995
|
function renderSourceFileList() {
|
|
4996
|
+
el.findChild('.no-layer-note').classed('hidden', model.getActiveLayer());
|
|
4997
|
+
el.findChild('.source-file-section').classed('hidden', !model.getActiveLayer());
|
|
4684
4998
|
var list = el.findChild('.file-list');
|
|
4685
4999
|
var files = [];
|
|
4686
5000
|
list.empty();
|
|
@@ -5198,6 +5512,16 @@
|
|
|
5198
5512
|
addHistoryState(undo, redo);
|
|
5199
5513
|
});
|
|
5200
5514
|
|
|
5515
|
+
gui.on('point_add', function(e) {
|
|
5516
|
+
var redo = function() {
|
|
5517
|
+
appendNewPoint(e.data.target, e.p);
|
|
5518
|
+
};
|
|
5519
|
+
var undo = function() {
|
|
5520
|
+
deleteLastPoint(e.data.target);
|
|
5521
|
+
};
|
|
5522
|
+
addHistoryState(undo, redo);
|
|
5523
|
+
});
|
|
5524
|
+
|
|
5201
5525
|
gui.on('path_add', function(e) {
|
|
5202
5526
|
var redo = function() {
|
|
5203
5527
|
gui.dispatchEvent('redo_path_add', {p1: e.p1, p2: e.p2});
|
|
@@ -5266,6 +5590,7 @@
|
|
|
5266
5590
|
var _hidden = true;
|
|
5267
5591
|
gui.on('active', updateVisibility);
|
|
5268
5592
|
gui.on('inactive', updateVisibility);
|
|
5593
|
+
gui.model.on('update', updateVisibility);
|
|
5269
5594
|
|
|
5270
5595
|
// @iconRef: selector for an (svg) button icon
|
|
5271
5596
|
this.addButton = function(iconRef) {
|
|
@@ -5294,7 +5619,10 @@
|
|
|
5294
5619
|
};
|
|
5295
5620
|
|
|
5296
5621
|
function updateVisibility() {
|
|
5297
|
-
|
|
5622
|
+
var noData = !gui.model.getActiveLayer();
|
|
5623
|
+
if (GUI.isActiveInstance(gui) && !_hidden && !noData) {
|
|
5624
|
+
buttons.show();
|
|
5625
|
+
} else if (noData && gui.getMode() != 'import') {
|
|
5298
5626
|
buttons.show();
|
|
5299
5627
|
} else {
|
|
5300
5628
|
buttons.hide();
|
|
@@ -5315,6 +5643,7 @@
|
|
|
5315
5643
|
});
|
|
5316
5644
|
|
|
5317
5645
|
btn.on('click', function() {
|
|
5646
|
+
if (btn.hasClass('disabled')) return;
|
|
5318
5647
|
modes.enterMode(active ? null : name);
|
|
5319
5648
|
});
|
|
5320
5649
|
}
|
|
@@ -5389,14 +5718,16 @@
|
|
|
5389
5718
|
var ctrlDown = false;
|
|
5390
5719
|
document.addEventListener('keyup', function(e) {
|
|
5391
5720
|
if (!GUI.isActiveInstance(gui)) return;
|
|
5392
|
-
if
|
|
5721
|
+
// this can fail to fire if keyup occurs over a context menu
|
|
5722
|
+
// if (e.keyCode == 16) shiftDown = false;
|
|
5723
|
+
shiftDown = e.shiftKey;
|
|
5393
5724
|
if (e.keyCode == 17) ctrlDown = false;
|
|
5394
5725
|
self.dispatchEvent('keyup', getEventData(e));
|
|
5395
5726
|
});
|
|
5396
5727
|
|
|
5397
5728
|
document.addEventListener('keydown', function(e) {
|
|
5398
5729
|
if (!GUI.isActiveInstance(gui)) return;
|
|
5399
|
-
|
|
5730
|
+
shiftDown = e.shiftKey;
|
|
5400
5731
|
if (e.keyCode == 17) ctrlDown = true;
|
|
5401
5732
|
self.dispatchEvent('keydown', getEventData(e));
|
|
5402
5733
|
});
|
|
@@ -5440,12 +5771,13 @@
|
|
|
5440
5771
|
|
|
5441
5772
|
var menus = {
|
|
5442
5773
|
standard: ['info', 'selection', 'box'],
|
|
5443
|
-
|
|
5774
|
+
empty: ['edit_points', 'edit_lines', 'edit_polygons'],
|
|
5775
|
+
polygons: ['info', 'selection', 'box', 'edit_polygons'],
|
|
5444
5776
|
rectangles: ['info', 'selection', 'box', 'rectangles'],
|
|
5445
|
-
lines: ['info', 'selection', 'box' , '
|
|
5777
|
+
lines: ['info', 'selection', 'box' , 'edit_lines'],
|
|
5446
5778
|
table: ['info', 'selection'],
|
|
5447
|
-
labels: ['info', 'selection', 'box', 'labels', '
|
|
5448
|
-
points: ['info', 'selection', 'box', '
|
|
5779
|
+
labels: ['info', 'selection', 'box', 'labels', 'edit_points'],
|
|
5780
|
+
points: ['info', 'selection', 'box', 'edit_points'] // , 'add-points'
|
|
5449
5781
|
};
|
|
5450
5782
|
|
|
5451
5783
|
var prompts = {
|
|
@@ -5460,11 +5792,12 @@
|
|
|
5460
5792
|
box: 'shift-drag box tool',
|
|
5461
5793
|
data: 'edit attributes',
|
|
5462
5794
|
labels: 'position labels',
|
|
5463
|
-
|
|
5795
|
+
edit_points: 'draw/edit points',
|
|
5796
|
+
edit_lines: 'draw/edit lines',
|
|
5797
|
+
edit_polygons: 'draw/edit polygons',
|
|
5464
5798
|
vertices: 'edit vertices',
|
|
5465
5799
|
selection: 'selection tool',
|
|
5466
5800
|
'add-points': 'add points',
|
|
5467
|
-
drawing: 'draw/reshape tool',
|
|
5468
5801
|
rectangles: 'drag-to-resize',
|
|
5469
5802
|
off: 'turn off'
|
|
5470
5803
|
};
|
|
@@ -5473,6 +5806,7 @@
|
|
|
5473
5806
|
|
|
5474
5807
|
// state variables
|
|
5475
5808
|
var _editMode = 'off';
|
|
5809
|
+
var _prevMode;
|
|
5476
5810
|
var _menuOpen = false;
|
|
5477
5811
|
|
|
5478
5812
|
// Only render edit mode button/menu if this option is present
|
|
@@ -5520,11 +5854,11 @@
|
|
|
5520
5854
|
};
|
|
5521
5855
|
|
|
5522
5856
|
this.modeUsesHitDetection = function(mode) {
|
|
5523
|
-
return ['info', 'selection', 'data', 'labels', '
|
|
5857
|
+
return ['info', 'selection', 'data', 'labels', 'edit_points', 'vertices', 'rectangles', 'edit_lines', 'edit_polygons'].includes(mode);
|
|
5524
5858
|
};
|
|
5525
5859
|
|
|
5526
5860
|
this.modeUsesPopup = function(mode) {
|
|
5527
|
-
return ['info', 'selection', 'data', 'box', 'labels', '
|
|
5861
|
+
return ['info', 'selection', 'data', 'box', 'labels', 'edit_points', 'rectangles'].includes(mode);
|
|
5528
5862
|
};
|
|
5529
5863
|
|
|
5530
5864
|
this.getMode = getInteractionMode;
|
|
@@ -5551,7 +5885,7 @@
|
|
|
5551
5885
|
function getAvailableModes() {
|
|
5552
5886
|
var o = gui.model.getActiveLayer();
|
|
5553
5887
|
if (!o || !o.layer) {
|
|
5554
|
-
return menus.
|
|
5888
|
+
return menus.empty; // TODO: more sensible handling of missing layer
|
|
5555
5889
|
}
|
|
5556
5890
|
if (!o.layer.geometry_type) {
|
|
5557
5891
|
return menus.table;
|
|
@@ -5634,6 +5968,7 @@
|
|
|
5634
5968
|
var changed = mode != _editMode;
|
|
5635
5969
|
if (changed) {
|
|
5636
5970
|
menu.classed('active', mode != 'off');
|
|
5971
|
+
_prevMode = _editMode;
|
|
5637
5972
|
_editMode = mode;
|
|
5638
5973
|
onModeChange();
|
|
5639
5974
|
updateArrowButton();
|
|
@@ -5644,7 +5979,7 @@
|
|
|
5644
5979
|
function onModeChange() {
|
|
5645
5980
|
var mode = getInteractionMode();
|
|
5646
5981
|
gui.state.interaction_mode = mode;
|
|
5647
|
-
gui.dispatchEvent('interaction_mode_change', {mode: mode});
|
|
5982
|
+
gui.dispatchEvent('interaction_mode_change', {mode: mode, prev_mode: _prevMode});
|
|
5648
5983
|
}
|
|
5649
5984
|
|
|
5650
5985
|
// Update button highlight and selected menu item highlight (if any)
|
|
@@ -7262,11 +7597,11 @@
|
|
|
7262
7597
|
test = getGraduatedCircleTest(getRadiusFunction(layer.gui.style));
|
|
7263
7598
|
} else if (geoType == 'point') {
|
|
7264
7599
|
test = pointTest;
|
|
7265
|
-
} else if (interactionMode == '
|
|
7600
|
+
} else if (interactionMode == 'edit_polygons') {
|
|
7266
7601
|
test = polygonVertexTest;
|
|
7267
7602
|
} else if (
|
|
7268
7603
|
interactionMode == 'vertices' ||
|
|
7269
|
-
interactionMode == '
|
|
7604
|
+
interactionMode == 'edit_lines') {
|
|
7270
7605
|
test = vertexTest;
|
|
7271
7606
|
} else if (geoType == 'polyline') {
|
|
7272
7607
|
test = polylineTest;
|
|
@@ -7366,7 +7701,8 @@
|
|
|
7366
7701
|
|
|
7367
7702
|
function pointTest(x, y) {
|
|
7368
7703
|
var bullseyeDist = 2, // hit all points w/in 2 px
|
|
7369
|
-
|
|
7704
|
+
// use small threshold when adding points
|
|
7705
|
+
hitThreshold = interactionMode == 'edit_points' ? 12 : 25,
|
|
7370
7706
|
toPx = ext.getTransform().mx,
|
|
7371
7707
|
hits = [];
|
|
7372
7708
|
|
|
@@ -7581,7 +7917,9 @@
|
|
|
7581
7917
|
|
|
7582
7918
|
function getPointerHitTest(mapLayer, ext, interactionMode, featureFilter) {
|
|
7583
7919
|
var shapeTest, targetLayer;
|
|
7584
|
-
|
|
7920
|
+
// need hit test on empty layers, in case we are drawing shapes
|
|
7921
|
+
// if (!mapLayer || !internal.layerHasGeometry(mapLayer.gui?.displayLayer)) {
|
|
7922
|
+
if (!mapLayer || !mapLayer.gui?.displayLayer.geometry_type) {
|
|
7585
7923
|
return function() {return {ids: []};};
|
|
7586
7924
|
}
|
|
7587
7925
|
shapeTest = getShapeHitTest(mapLayer, ext, interactionMode, featureFilter);
|
|
@@ -7621,9 +7959,12 @@
|
|
|
7621
7959
|
var priority = 2;
|
|
7622
7960
|
|
|
7623
7961
|
mouse.on('contextmenu', function(e) {
|
|
7624
|
-
|
|
7625
|
-
|
|
7962
|
+
// shift key enables default menu (for development)
|
|
7963
|
+
if (gui.keyboard.shiftIsPressed()) {
|
|
7964
|
+
return;
|
|
7626
7965
|
}
|
|
7966
|
+
e.originalEvent.preventDefault();
|
|
7967
|
+
if (!targetLayer) return; // TODO: enable menu on empty map
|
|
7627
7968
|
triggerHitEvent('contextmenu', e);
|
|
7628
7969
|
}, false);
|
|
7629
7970
|
|
|
@@ -7667,7 +8008,6 @@
|
|
|
7667
8008
|
};
|
|
7668
8009
|
|
|
7669
8010
|
function updateHitTest(featureFilter) {
|
|
7670
|
-
if (!hoverable()) return;
|
|
7671
8011
|
hitTest = getPointerHitTest(targetLayer, ext, interactionMode, featureFilter);
|
|
7672
8012
|
}
|
|
7673
8013
|
|
|
@@ -7688,7 +8028,7 @@
|
|
|
7688
8028
|
}
|
|
7689
8029
|
|
|
7690
8030
|
function hoverable() {
|
|
7691
|
-
return
|
|
8031
|
+
return !!interactionMode;
|
|
7692
8032
|
}
|
|
7693
8033
|
|
|
7694
8034
|
function selectable() {
|
|
@@ -7701,20 +8041,29 @@
|
|
|
7701
8041
|
}
|
|
7702
8042
|
|
|
7703
8043
|
function draggable() {
|
|
7704
|
-
return interactionMode == 'vertices' || interactionMode == '
|
|
7705
|
-
interactionMode == 'labels' || interactionMode == '
|
|
8044
|
+
return interactionMode == 'vertices' || interactionMode == 'edit_points' ||
|
|
8045
|
+
interactionMode == 'labels' || interactionMode == 'edit_lines' ||
|
|
8046
|
+
interactionMode == 'edit_polygons';
|
|
7706
8047
|
}
|
|
7707
8048
|
|
|
7708
8049
|
function clickable() {
|
|
7709
8050
|
// click used to pin popup and select features
|
|
7710
8051
|
return interactionMode == 'data' || interactionMode == 'info' ||
|
|
7711
|
-
interactionMode == 'selection' || interactionMode == 'rectangles'
|
|
8052
|
+
interactionMode == 'selection' || interactionMode == 'rectangles' ||
|
|
8053
|
+
interactionMode == 'edit_points';
|
|
7712
8054
|
}
|
|
7713
8055
|
|
|
7714
8056
|
self.getHitId = function() {
|
|
7715
8057
|
return hitTest ? storedData.id : -1;
|
|
7716
8058
|
};
|
|
7717
8059
|
|
|
8060
|
+
self.setHitId = function(id) {
|
|
8061
|
+
if (storedData.id == id) return;
|
|
8062
|
+
storedData.id = id;
|
|
8063
|
+
storedData.ids = [id];
|
|
8064
|
+
triggerHitEvent('change');
|
|
8065
|
+
};
|
|
8066
|
+
|
|
7718
8067
|
// Get a reference to the active layer, so listeners to hit events can interact
|
|
7719
8068
|
// with data and shapes
|
|
7720
8069
|
self.getHitTarget = function() {
|
|
@@ -7849,9 +8198,9 @@
|
|
|
7849
8198
|
|
|
7850
8199
|
|
|
7851
8200
|
mouse.on('click', function(e) {
|
|
8201
|
+
var pinned = storedData.pinned;
|
|
7852
8202
|
if (!hitTest || !active) return;
|
|
7853
8203
|
if (!eventIsEnabled('click')) return;
|
|
7854
|
-
|
|
7855
8204
|
e.stopPropagation();
|
|
7856
8205
|
|
|
7857
8206
|
// TODO: move pinning to inspection control?
|
|
@@ -7859,11 +8208,17 @@
|
|
|
7859
8208
|
updateSelectionState(convertClickDataToSelectionData(hitTest(e)));
|
|
7860
8209
|
}
|
|
7861
8210
|
|
|
8211
|
+
if (pinned && interactionMode == 'edit_points') {
|
|
8212
|
+
// kludge: intercept the click event if popup is turning off, so
|
|
8213
|
+
// a new point doesn't get made
|
|
8214
|
+
return;
|
|
8215
|
+
}
|
|
7862
8216
|
triggerHitEvent('click', e);
|
|
7863
8217
|
}, null, priority);
|
|
7864
8218
|
|
|
7865
8219
|
// Hits are re-detected on 'hover' (if hit detection is active)
|
|
7866
8220
|
mouse.on('hover', function(e) {
|
|
8221
|
+
if (gui.contextMenu.isOpen()) return;
|
|
7867
8222
|
handlePointerEvent(e);
|
|
7868
8223
|
if (storedData.pinned || !hitTest || !active) return;
|
|
7869
8224
|
if (e.hover && isOverMap(e)) {
|
|
@@ -7979,19 +8334,23 @@
|
|
|
7979
8334
|
// check if an event is used in the current interaction mode
|
|
7980
8335
|
function eventIsEnabled(type) {
|
|
7981
8336
|
if (!active) return false;
|
|
7982
|
-
if (interactionMode == 'drawing' && (type == 'hover' || type == 'dblclick')) {
|
|
7983
|
-
return true; // special case -- using hover for line drawing animation
|
|
7984
|
-
}
|
|
7985
|
-
|
|
7986
8337
|
if (type == 'click' && gui.keyboard.ctrlIsPressed()) {
|
|
7987
8338
|
return false; // don't fire if context menu might open
|
|
7988
8339
|
}
|
|
7989
8340
|
if (type == 'click' && gui.contextMenu.isOpen()) {
|
|
7990
8341
|
return false;
|
|
7991
8342
|
}
|
|
7992
|
-
if (type == 'click' &&
|
|
8343
|
+
if (type == 'click' &&
|
|
8344
|
+
(interactionMode == 'edit_lines' || interactionMode == 'edit_polygons')) {
|
|
7993
8345
|
return true; // click events are triggered even if no shape is hit
|
|
7994
8346
|
}
|
|
8347
|
+
if (type == 'click' && interactionMode == 'edit_points') {
|
|
8348
|
+
return true;
|
|
8349
|
+
}
|
|
8350
|
+
if ((interactionMode == 'edit_lines' || interactionMode == 'edit_polygons') &&
|
|
8351
|
+
(type == 'hover' || type == 'dblclick')) {
|
|
8352
|
+
return true; // special case -- using hover for line drawing animation
|
|
8353
|
+
}
|
|
7995
8354
|
|
|
7996
8355
|
// ignore pointer events when no features are being hit
|
|
7997
8356
|
// (don't block pan and other navigation when events aren't being used for editing)
|
|
@@ -8009,7 +8368,7 @@
|
|
|
8009
8368
|
}
|
|
8010
8369
|
|
|
8011
8370
|
function possiblyStopPropagation(e) {
|
|
8012
|
-
if (interactionMode == '
|
|
8371
|
+
if (interactionMode == 'edit_lines' || interactionMode == 'edit_polygons') {
|
|
8013
8372
|
// handled conditionally in the control
|
|
8014
8373
|
return;
|
|
8015
8374
|
}
|
|
@@ -8848,7 +9207,7 @@
|
|
|
8848
9207
|
}
|
|
8849
9208
|
|
|
8850
9209
|
gui.on('map_reset', function() {
|
|
8851
|
-
ext.
|
|
9210
|
+
ext.reset(true);
|
|
8852
9211
|
});
|
|
8853
9212
|
|
|
8854
9213
|
zoomTween.on('change', function(e) {
|
|
@@ -8866,8 +9225,9 @@
|
|
|
8866
9225
|
|
|
8867
9226
|
mouse.on('dragstart', function(e) {
|
|
8868
9227
|
if (disabled()) return;
|
|
8869
|
-
if
|
|
8870
|
-
//
|
|
9228
|
+
// allow drawing rectangles if active layer is empty
|
|
9229
|
+
// var lyr = gui.model.getActiveLayer()?.layer;
|
|
9230
|
+
// if (lyr && !internal.layerHasGeometry(lyr)) return;
|
|
8871
9231
|
shiftDrag = !!e.shiftKey;
|
|
8872
9232
|
if (shiftDrag) {
|
|
8873
9233
|
if (useBoxZoom()) zoomBox.turnOn();
|
|
@@ -9205,7 +9565,7 @@
|
|
|
9205
9565
|
});
|
|
9206
9566
|
|
|
9207
9567
|
self.show = function(id, ids, lyr, pinned, edit) {
|
|
9208
|
-
var singleEdit = edit || pinned &&
|
|
9568
|
+
var singleEdit = edit || pinned && !internal.layerHasAttributeData(lyr);
|
|
9209
9569
|
var multiEdit = pinned && gui.interaction.getMode() == 'selection';
|
|
9210
9570
|
var maxHeight = parent.node().clientHeight - 36;
|
|
9211
9571
|
|
|
@@ -9437,16 +9797,16 @@
|
|
|
9437
9797
|
gui.session.dataValueUpdated(e.ids, e.field, e.value);
|
|
9438
9798
|
// Refresh the display if a style variable has been changed interactively
|
|
9439
9799
|
if (internal.isSupportedSvgStyleProperty(e.field)) {
|
|
9440
|
-
// drawLayers();
|
|
9441
9800
|
gui.dispatchEvent('map-needs-refresh');
|
|
9442
9801
|
}
|
|
9443
9802
|
});
|
|
9444
9803
|
|
|
9445
9804
|
hit.on('contextmenu', function(e) {
|
|
9446
9805
|
var target = hit.getHitTarget();
|
|
9447
|
-
if (!e.overMap || !target
|
|
9448
|
-
|
|
9449
|
-
|
|
9806
|
+
if (!e.overMap || !target || e.mode == 'edit_lines' ||
|
|
9807
|
+
e.mode == 'edit_polygons') {
|
|
9808
|
+
return;
|
|
9809
|
+
}
|
|
9450
9810
|
gui.contextMenu.open(e, hit.getHitTarget());
|
|
9451
9811
|
});
|
|
9452
9812
|
|
|
@@ -9738,14 +10098,43 @@
|
|
|
9738
10098
|
}
|
|
9739
10099
|
}
|
|
9740
10100
|
|
|
9741
|
-
function
|
|
10101
|
+
function initPointEditing(gui, ext, hit) {
|
|
9742
10102
|
var symbolInfo;
|
|
9743
10103
|
function active(e) {
|
|
9744
|
-
return
|
|
10104
|
+
return gui.interaction.getMode() == 'edit_points';
|
|
9745
10105
|
}
|
|
9746
10106
|
|
|
9747
|
-
|
|
10107
|
+
function overPoint(e) {
|
|
10108
|
+
return active(e) && e.id > -1;
|
|
10109
|
+
}
|
|
10110
|
+
|
|
10111
|
+
gui.on('interaction_mode_change', function(e) {
|
|
10112
|
+
if (e.mode == 'edit_points' && !gui.model.getActiveLayer()) {
|
|
10113
|
+
addEmptyLayer(gui, undefined, 'point');
|
|
10114
|
+
} else if (e.prev_mode == 'edit_points') {
|
|
10115
|
+
gui.container.findChild('.map-layers').classed('add-points', false);
|
|
10116
|
+
}
|
|
10117
|
+
|
|
10118
|
+
});
|
|
10119
|
+
|
|
10120
|
+
hit.on('click', function(e) {
|
|
10121
|
+
if (overPoint(e) || !active(e)) return;
|
|
10122
|
+
// add point
|
|
10123
|
+
var p = pixToDataCoords(e.x, e.y);
|
|
10124
|
+
var target = hit.getHitTarget();
|
|
10125
|
+
appendNewPoint(target, p);
|
|
10126
|
+
gui.dispatchEvent('point_add', {p, target});
|
|
10127
|
+
gui.dispatchEvent('map-needs-refresh');
|
|
10128
|
+
hit.setHitId(target.shapes.length - 1); // highlight new point
|
|
10129
|
+
});
|
|
10130
|
+
|
|
10131
|
+
hit.on('change', function(e) {
|
|
9748
10132
|
if (!active(e)) return;
|
|
10133
|
+
gui.container.findChild('.map-layers').classed('add-points', !overPoint(e));
|
|
10134
|
+
});
|
|
10135
|
+
|
|
10136
|
+
hit.on('dragstart', function(e) {
|
|
10137
|
+
if (!overPoint(e)) return;
|
|
9749
10138
|
var target = hit.getHitTarget();
|
|
9750
10139
|
symbolInfo = {
|
|
9751
10140
|
FID: e.id,
|
|
@@ -9755,37 +10144,36 @@
|
|
|
9755
10144
|
});
|
|
9756
10145
|
|
|
9757
10146
|
hit.on('drag', function(e) {
|
|
9758
|
-
if (!
|
|
10147
|
+
if (!overPoint(e)) return;
|
|
9759
10148
|
// TODO: support multi points... get id of closest part to the pointer
|
|
9760
|
-
var p = getPointCoordsById(e.id, symbolInfo.target);
|
|
9761
|
-
|
|
10149
|
+
// var p = getPointCoordsById(e.id, symbolInfo.target);
|
|
10150
|
+
var id = symbolInfo.FID;
|
|
10151
|
+
var shp = symbolInfo.target.gui.displayLayer.shapes[id];
|
|
10152
|
+
if (!shp) return;
|
|
9762
10153
|
var diff = translateDeltaDisplayCoords(e.dx, e.dy, ext);
|
|
9763
|
-
|
|
9764
|
-
|
|
10154
|
+
shp[0][0] += diff[0];
|
|
10155
|
+
shp[0][1] += diff[1];
|
|
9765
10156
|
gui.dispatchEvent('map-needs-refresh');
|
|
9766
10157
|
});
|
|
9767
10158
|
|
|
9768
10159
|
hit.on('dragend', function(e) {
|
|
9769
|
-
if (!
|
|
9770
|
-
updatePointCoords(symbolInfo.target,
|
|
10160
|
+
if (!overPoint(e) || !symbolInfo ) return;
|
|
10161
|
+
updatePointCoords(symbolInfo.target, symbolInfo.FID);
|
|
9771
10162
|
symbolInfo.endCoords = getPointCoords(symbolInfo.target, e.id);
|
|
9772
10163
|
gui.dispatchEvent('symbol_dragend', symbolInfo);
|
|
9773
10164
|
symbolInfo = null;
|
|
9774
10165
|
});
|
|
9775
10166
|
|
|
10167
|
+
function pixToDataCoords(x, y) {
|
|
10168
|
+
var target = hit.getHitTarget();
|
|
10169
|
+
return translateDisplayPoint(target, ext.translatePixelCoords(x, y));
|
|
10170
|
+
}
|
|
10171
|
+
|
|
9776
10172
|
function translateDeltaDisplayCoords(dx, dy, ext) {
|
|
9777
10173
|
var a = ext.translatePixelCoords(0, 0);
|
|
9778
10174
|
var b = ext.translatePixelCoords(dx, dy);
|
|
9779
10175
|
return [b[0] - a[0], b[1] - a[1]];
|
|
9780
10176
|
}
|
|
9781
|
-
|
|
9782
|
-
function getPointCoordsById(id, layer) {
|
|
9783
|
-
var coords = layer && layer.geometry_type == 'point' && layer.shapes[id];
|
|
9784
|
-
if (!coords || coords.length != 1) {
|
|
9785
|
-
return null;
|
|
9786
|
-
}
|
|
9787
|
-
return coords[0];
|
|
9788
|
-
}
|
|
9789
10177
|
}
|
|
9790
10178
|
|
|
9791
10179
|
// pixel distance threshold for hovering near a vertex or segment midpoint
|
|
@@ -9826,13 +10214,17 @@
|
|
|
9826
10214
|
gui.addMode('drawing_tool', turnOn, turnOff);
|
|
9827
10215
|
|
|
9828
10216
|
gui.on('interaction_mode_change', function(e) {
|
|
9829
|
-
|
|
9830
|
-
|
|
9831
|
-
|
|
10217
|
+
if (e.mode == 'edit_lines' || e.mode == 'edit_polygons') {
|
|
10218
|
+
if (!gui.model.getActiveLayer()) {
|
|
10219
|
+
addEmptyLayer(gui, undefined, e.mode == 'edit_lines' ? 'polyline' : 'polygon');
|
|
10220
|
+
}
|
|
9832
10221
|
gui.enterMode('drawing_tool');
|
|
10222
|
+
} else if (gui.getMode() == 'drawing_tool') {
|
|
10223
|
+
gui.clearMode();
|
|
9833
10224
|
} else if (active()) {
|
|
9834
10225
|
turnOff();
|
|
9835
10226
|
}
|
|
10227
|
+
updateCursor();
|
|
9836
10228
|
}, null, 10); // higher priority than hit control, so turnOff() has correct hit target
|
|
9837
10229
|
|
|
9838
10230
|
gui.on('redo_path_add', function(e) {
|
|
@@ -9879,6 +10271,7 @@
|
|
|
9879
10271
|
});
|
|
9880
10272
|
|
|
9881
10273
|
function turnOn() {
|
|
10274
|
+
if (active()) return;
|
|
9882
10275
|
var target = hit.getHitTarget();
|
|
9883
10276
|
initialArcCount = target.gui.displayArcs.size();
|
|
9884
10277
|
initialShapeCount = target.shapes.length;
|
|
@@ -9905,6 +10298,7 @@
|
|
|
9905
10298
|
|
|
9906
10299
|
function turnOff() {
|
|
9907
10300
|
var removed = 0;
|
|
10301
|
+
var mode = gui.interaction.getMode();
|
|
9908
10302
|
finishCurrentPath();
|
|
9909
10303
|
if (polygonMode()) {
|
|
9910
10304
|
removed = removeOpenPolygons();
|
|
@@ -9913,10 +10307,11 @@
|
|
|
9913
10307
|
hideInstructions();
|
|
9914
10308
|
initialArcCount = -1;
|
|
9915
10309
|
initialShapeCount = -1;
|
|
9916
|
-
if (
|
|
10310
|
+
if (mode == 'edit_lines' || mode == 'edit_polygons') {
|
|
9917
10311
|
// mode change was not initiated by interactive menu -- turn off interactivity
|
|
9918
10312
|
gui.interaction.turnOff();
|
|
9919
10313
|
}
|
|
10314
|
+
updateCursor();
|
|
9920
10315
|
if (removed > 0) {
|
|
9921
10316
|
fullRedraw();
|
|
9922
10317
|
}
|
|
@@ -10084,6 +10479,7 @@
|
|
|
10084
10479
|
}
|
|
10085
10480
|
|
|
10086
10481
|
function updateCursor() {
|
|
10482
|
+
gui.container.findChild('.map-layers').classed('drawing', active());
|
|
10087
10483
|
var useArrow = hoverVertexInfo && !hoverVertexInfo.extendable && !drawing();
|
|
10088
10484
|
gui.container.findChild('.map-layers').classed('dragging', useArrow);
|
|
10089
10485
|
}
|
|
@@ -10342,45 +10738,9 @@
|
|
|
10342
10738
|
}
|
|
10343
10739
|
}
|
|
10344
10740
|
|
|
10345
|
-
function initPointDrawing(gui, ext, hit) {
|
|
10346
|
-
var mouse = gui.map.getMouse();
|
|
10347
|
-
|
|
10348
|
-
gui.on('interaction_mode_change', function(e) {
|
|
10349
|
-
gui.container.findChild('.map-layers').classed('add-points', e.mode === 'add-points');
|
|
10350
|
-
});
|
|
10351
|
-
|
|
10352
|
-
function active() {
|
|
10353
|
-
return gui.interaction.getMode() == 'add-points';
|
|
10354
|
-
}
|
|
10355
|
-
|
|
10356
|
-
mouse.on('click', function(e) {
|
|
10357
|
-
if (!active()) return;
|
|
10358
|
-
addPoint(e.x, e.y);
|
|
10359
|
-
gui.dispatchEvent('map-needs-refresh');
|
|
10360
|
-
});
|
|
10361
|
-
|
|
10362
|
-
// x, y: pixel coordinates
|
|
10363
|
-
function addPoint(x, y) {
|
|
10364
|
-
var p = ext.translatePixelCoords(x, y);
|
|
10365
|
-
var lyr = hit.getHitTarget();
|
|
10366
|
-
var fid = lyr.shapes.length;
|
|
10367
|
-
var d = appendNewDataRecord(lyr);
|
|
10368
|
-
if (d) {
|
|
10369
|
-
// this seems to work even for projected layers -- the data tables
|
|
10370
|
-
// of projected and original data seem to be shared.
|
|
10371
|
-
lyr.data.getRecords()[fid] = d;
|
|
10372
|
-
}
|
|
10373
|
-
lyr.shapes[fid] = [p];
|
|
10374
|
-
updatePointCoords(lyr, fid);
|
|
10375
|
-
}
|
|
10376
|
-
|
|
10377
|
-
|
|
10378
|
-
}
|
|
10379
|
-
|
|
10380
10741
|
function initInteractiveEditing(gui, ext, hit) {
|
|
10381
10742
|
initLabelDragging(gui, ext, hit);
|
|
10382
|
-
|
|
10383
|
-
initPointDrawing(gui, ext, hit);
|
|
10743
|
+
initPointEditing(gui, ext, hit);
|
|
10384
10744
|
initLineEditing(gui, ext, hit);
|
|
10385
10745
|
}
|
|
10386
10746
|
|
|
@@ -10394,26 +10754,32 @@
|
|
|
10394
10754
|
|
|
10395
10755
|
_position.on('resize', function(e) {
|
|
10396
10756
|
if (ready()) {
|
|
10397
|
-
triggerChangeEvent({resize: true});
|
|
10757
|
+
// triggerChangeEvent({resize: true});
|
|
10758
|
+
triggerChangeEvent();
|
|
10398
10759
|
}
|
|
10399
10760
|
});
|
|
10400
10761
|
|
|
10401
10762
|
function ready() { return !!_fullBounds; }
|
|
10402
10763
|
|
|
10403
|
-
this.reset = function() {
|
|
10764
|
+
this.reset = function(fire) {
|
|
10404
10765
|
if (!ready()) return;
|
|
10405
|
-
recenter(_fullBounds.centerX(), _fullBounds.centerY(), 1
|
|
10766
|
+
recenter(_fullBounds.centerX(), _fullBounds.centerY(), 1);
|
|
10767
|
+
if (fire) {
|
|
10768
|
+
triggerChangeEvent();
|
|
10769
|
+
}
|
|
10406
10770
|
};
|
|
10407
10771
|
|
|
10408
10772
|
this.home = function() {
|
|
10409
10773
|
if (!ready()) return;
|
|
10410
10774
|
recenter(_fullBounds.centerX(), _fullBounds.centerY(), 1);
|
|
10775
|
+
triggerChangeEvent();
|
|
10411
10776
|
};
|
|
10412
10777
|
|
|
10413
10778
|
this.pan = function(xpix, ypix) {
|
|
10414
10779
|
if (!ready()) return;
|
|
10415
10780
|
var t = this.getTransform();
|
|
10416
10781
|
recenter(_cx - xpix / t.mx, _cy - ypix / t.my);
|
|
10782
|
+
triggerChangeEvent();
|
|
10417
10783
|
};
|
|
10418
10784
|
|
|
10419
10785
|
// Zoom to @w (width of the map viewport in coordinates)
|
|
@@ -10436,6 +10802,7 @@
|
|
|
10436
10802
|
cx = fx + dx2,
|
|
10437
10803
|
cy = fy + dy2;
|
|
10438
10804
|
recenter(cx, cy, scale);
|
|
10805
|
+
triggerChangeEvent();
|
|
10439
10806
|
};
|
|
10440
10807
|
|
|
10441
10808
|
this.zoomByPct = function(pct, xpct, ypct) {
|
|
@@ -10527,11 +10894,10 @@
|
|
|
10527
10894
|
return this.getTransform().invert().transform(x, y);
|
|
10528
10895
|
};
|
|
10529
10896
|
|
|
10530
|
-
function recenter(cx, cy, scale
|
|
10897
|
+
function recenter(cx, cy, scale) {
|
|
10531
10898
|
scale = scale ? limitScale(scale) : _scale;
|
|
10532
10899
|
if (cx == _cx && cy == _cy && scale == _scale) return;
|
|
10533
10900
|
navigate(cx, cy, scale);
|
|
10534
|
-
triggerChangeEvent(data);
|
|
10535
10901
|
}
|
|
10536
10902
|
|
|
10537
10903
|
function navigate(cx, cy, scale) {
|
|
@@ -10558,9 +10924,8 @@
|
|
|
10558
10924
|
_scale = scale;
|
|
10559
10925
|
}
|
|
10560
10926
|
|
|
10561
|
-
function triggerChangeEvent(
|
|
10562
|
-
|
|
10563
|
-
_self.dispatchEvent('change', data);
|
|
10927
|
+
function triggerChangeEvent() {
|
|
10928
|
+
_self.dispatchEvent('change');
|
|
10564
10929
|
}
|
|
10565
10930
|
|
|
10566
10931
|
// stop zooming before rounding errors become too obvious
|
|
@@ -11453,8 +11818,10 @@
|
|
|
11453
11818
|
return el;
|
|
11454
11819
|
}
|
|
11455
11820
|
|
|
11456
|
-
function LayerRenderer(gui, container
|
|
11821
|
+
function LayerRenderer(gui, container) {
|
|
11457
11822
|
var el = El(container),
|
|
11823
|
+
ext = gui.map.getExtent(),
|
|
11824
|
+
mouse = gui.map.getMouse(),
|
|
11458
11825
|
_mainCanv = new DisplayCanvas().appendTo(el),
|
|
11459
11826
|
_overlayCanv = new DisplayCanvas().appendTo(el),
|
|
11460
11827
|
_svg = new SvgDisplayLayer(gui, ext, mouse).appendTo(el),
|
|
@@ -11513,493 +11880,248 @@
|
|
|
11513
11880
|
});
|
|
11514
11881
|
};
|
|
11515
11882
|
|
|
11516
|
-
// kludge: skip rendering base layers if hovering, except on first hover
|
|
11517
|
-
// (because highlight shapes may be rendered to the main canvas)
|
|
11518
|
-
function skipMainLayerRedraw(action) {
|
|
11519
|
-
return action == 'hover' && _overlayCanv.visible();
|
|
11520
|
-
}
|
|
11521
|
-
|
|
11522
|
-
function drawCanvasLayer(lyr, canv) {
|
|
11523
|
-
if (!lyr) return;
|
|
11524
|
-
if (lyr.gui.style.type == 'outline') {
|
|
11525
|
-
drawOutlineLayerToCanvas(lyr, canv, ext);
|
|
11526
|
-
} else {
|
|
11527
|
-
drawStyledLayerToCanvas(lyr, canv, ext);
|
|
11528
|
-
}
|
|
11529
|
-
}
|
|
11530
|
-
|
|
11531
|
-
function getSvgLayerType(layer) {
|
|
11532
|
-
var type = null;
|
|
11533
|
-
if (internal.layerHasSvgSymbols(layer)) {
|
|
11534
|
-
type = 'symbol'; // also label + symbol
|
|
11535
|
-
} else if (internal.layerHasLabels(layer)) {
|
|
11536
|
-
type = 'symbol';
|
|
11537
|
-
}
|
|
11538
|
-
return type;
|
|
11539
|
-
}
|
|
11540
|
-
}
|
|
11541
|
-
|
|
11542
|
-
// Controls the shift-drag box editing tool
|
|
11543
|
-
//
|
|
11544
|
-
function BoxTool(gui, ext, nav) {
|
|
11545
|
-
var self = new EventDispatcher();
|
|
11546
|
-
var box = new HighlightBox(gui, {name: 'box-tool', persistent: true, handles: true, draggable: true});
|
|
11547
|
-
var popup = gui.container.findChild('.box-tool-options');
|
|
11548
|
-
var coords = popup.findChild('.box-coords');
|
|
11549
|
-
var _on = false;
|
|
11550
|
-
|
|
11551
|
-
var infoBtn = new SimpleButton(popup.findChild('.info-btn')).on('click', function() {
|
|
11552
|
-
if (coords.visible()) hideCoords(); else showCoords();
|
|
11553
|
-
});
|
|
11554
|
-
|
|
11555
|
-
new SimpleButton(popup.findChild('.cancel-btn')).on('click', function() {
|
|
11556
|
-
reset();
|
|
11557
|
-
});
|
|
11558
|
-
|
|
11559
|
-
new SimpleButton(popup.findChild('.select-btn')).on('click', function() {
|
|
11560
|
-
var coords = box.getDataCoords();
|
|
11561
|
-
if (!coords) return;
|
|
11562
|
-
gui.enterMode('selection_tool');
|
|
11563
|
-
gui.interaction.setMode('selection');
|
|
11564
|
-
// kludge to pass bbox to the selection tool
|
|
11565
|
-
gui.dispatchEvent('selection_bridge', {
|
|
11566
|
-
map_data_bbox: coords
|
|
11567
|
-
});
|
|
11568
|
-
});
|
|
11569
|
-
|
|
11570
|
-
new SimpleButton(popup.findChild('.clip-btn')).on('click', function() {
|
|
11571
|
-
runCommand('-clip bbox=' + box.getDataCoords().join(','));
|
|
11572
|
-
});
|
|
11573
|
-
|
|
11574
|
-
new SimpleButton(popup.findChild('.erase-btn')).on('click', function() {
|
|
11575
|
-
runCommand('-erase bbox=' + box.getDataCoords().join(','));
|
|
11576
|
-
});
|
|
11577
|
-
|
|
11578
|
-
new SimpleButton(popup.findChild('.rect-btn')).on('click', function() {
|
|
11579
|
-
var cmd = '-rectangle + bbox=' + box.getDataCoords().join(',');
|
|
11580
|
-
runCommand(cmd);
|
|
11581
|
-
});
|
|
11582
|
-
|
|
11583
|
-
new SimpleButton(popup.findChild('.frame-btn')).on('click', function() {
|
|
11584
|
-
openAddFramePopup(gui, box.getDataCoords());
|
|
11585
|
-
});
|
|
11586
|
-
|
|
11587
|
-
gui.addMode('box_tool', turnOn, turnOff);
|
|
11588
|
-
|
|
11589
|
-
gui.on('interaction_mode_change', function(e) {
|
|
11590
|
-
if (e.mode === 'box') {
|
|
11591
|
-
gui.enterMode('box_tool');
|
|
11592
|
-
} else if (_on) {
|
|
11593
|
-
turnOff();
|
|
11594
|
-
}
|
|
11595
|
-
});
|
|
11596
|
-
|
|
11597
|
-
gui.on('shift_drag_start', function() {
|
|
11598
|
-
// box.classed('zooming', inZoomMode());
|
|
11599
|
-
hideCoords();
|
|
11600
|
-
});
|
|
11601
|
-
|
|
11602
|
-
box.on('dragend', function(e) {
|
|
11603
|
-
if (_on) popup.show();
|
|
11604
|
-
});
|
|
11605
|
-
|
|
11606
|
-
box.on('handle_drag', function() {
|
|
11607
|
-
if (coords.visible()) {
|
|
11608
|
-
showCoords();
|
|
11609
|
-
}
|
|
11610
|
-
});
|
|
11611
|
-
|
|
11612
|
-
function inZoomMode() {
|
|
11613
|
-
return !_on && gui.getMode() != 'selection_tool';
|
|
11614
|
-
}
|
|
11615
|
-
|
|
11616
|
-
function runCommand(cmd) {
|
|
11617
|
-
if (gui.console) {
|
|
11618
|
-
gui.console.runMapshaperCommands(cmd, function(err) {
|
|
11619
|
-
reset();
|
|
11620
|
-
gui.clearMode();
|
|
11621
|
-
});
|
|
11622
|
-
}
|
|
11623
|
-
// reset(); // TODO: exit interactive mode
|
|
11624
|
-
}
|
|
11625
|
-
|
|
11626
|
-
function showCoords() {
|
|
11627
|
-
El(infoBtn.node()).addClass('selected-btn');
|
|
11628
|
-
coords.text(box.getDataCoords().join(','));
|
|
11629
|
-
coords.show();
|
|
11630
|
-
GUI.selectElement(coords.node());
|
|
11631
|
-
}
|
|
11632
|
-
|
|
11633
|
-
function hideCoords() {
|
|
11634
|
-
El(infoBtn.node()).removeClass('selected-btn');
|
|
11635
|
-
coords.hide();
|
|
11636
|
-
}
|
|
11637
|
-
|
|
11638
|
-
function turnOn() {
|
|
11639
|
-
box.turnOn();
|
|
11640
|
-
_on = true;
|
|
11641
|
-
}
|
|
11642
|
-
|
|
11643
|
-
function turnOff() {
|
|
11644
|
-
box.turnOff();
|
|
11645
|
-
if (gui.interaction.getMode() == 'box') {
|
|
11646
|
-
// mode change was not initiated by interactive menu -- turn off interactivity
|
|
11647
|
-
gui.interaction.turnOff();
|
|
11648
|
-
}
|
|
11649
|
-
_on = false;
|
|
11650
|
-
reset();
|
|
11651
|
-
}
|
|
11652
|
-
|
|
11653
|
-
function reset() {
|
|
11654
|
-
box.hide();
|
|
11655
|
-
popup.hide();
|
|
11656
|
-
hideCoords();
|
|
11657
|
-
}
|
|
11658
|
-
|
|
11659
|
-
function openAddFramePopup(gui, bbox) {
|
|
11660
|
-
var popup = showPopupAlert('', 'Add a map frame');
|
|
11661
|
-
var el = popup.container();
|
|
11662
|
-
el.addClass('option-menu');
|
|
11663
|
-
var html = `<p>Enter a width in px, cm or inches to create a frame layer
|
|
11664
|
-
for setting the size of the map for symbol scaling in the
|
|
11665
|
-
GUI and setting the size and crop of SVG output.</p><div><input type="text" class="frame-width text-input" placeholder="examples: 600px 5in"></div>
|
|
11666
|
-
<div tabindex="0" class="btn dialog-btn">Create</div></span>`;
|
|
11667
|
-
el.html(html);
|
|
11668
|
-
var input = el.findChild('.frame-width');
|
|
11669
|
-
input.node().focus();
|
|
11670
|
-
var btn = el.findChild('.btn').on('click', function() {
|
|
11671
|
-
var widthStr = input.node().value.trim();
|
|
11672
|
-
if (parseFloat(widthStr) > 0 === false) {
|
|
11673
|
-
// invalid input
|
|
11674
|
-
input.node().value = '';
|
|
11675
|
-
return;
|
|
11676
|
-
}
|
|
11677
|
-
var cmd = `-rectangle + name=frame bbox='${bbox.join(',')}' width='${widthStr}'`;
|
|
11678
|
-
runCommand(cmd);
|
|
11679
|
-
popup.close();
|
|
11680
|
-
});
|
|
11681
|
-
}
|
|
11682
|
-
|
|
11683
|
-
return self;
|
|
11684
|
-
}
|
|
11685
|
-
|
|
11686
|
-
function RectangleControl(gui, hit) {
|
|
11687
|
-
var box = new HighlightBox(gui, {name: 'rectangle-tool', persistent: true, handles: true, classname: 'rectangles', draggable: false});
|
|
11688
|
-
var _on = false;
|
|
11689
|
-
var dragInfo;
|
|
11690
|
-
|
|
11691
|
-
gui.addMode('rectangle_tool', turnOn, turnOff);
|
|
11692
|
-
|
|
11693
|
-
gui.on('interaction_mode_change', function(e) {
|
|
11694
|
-
if (e.mode === 'rectangles') {
|
|
11695
|
-
gui.enterMode('rectangle_tool');
|
|
11696
|
-
} else if (_on) {
|
|
11697
|
-
turnOff();
|
|
11698
|
-
}
|
|
11699
|
-
});
|
|
11700
|
-
|
|
11701
|
-
hit.on('change', function(e) {
|
|
11702
|
-
if (!_on) return;
|
|
11703
|
-
// TODO: handle multiple hits (see gui-inspection-control)
|
|
11704
|
-
var id = e.id;
|
|
11705
|
-
if (e.id > -1 && e.pinned) {
|
|
11706
|
-
var target = hit.getHitTarget();
|
|
11707
|
-
var path = target.shapes[e.id][0];
|
|
11708
|
-
var bbox = target.gui.displayArcs.getSimpleShapeBounds(path).toArray();
|
|
11709
|
-
box.setDataCoords(bbox);
|
|
11710
|
-
dragInfo = {
|
|
11711
|
-
id: e.id,
|
|
11712
|
-
target: target,
|
|
11713
|
-
ids: [],
|
|
11714
|
-
points: []
|
|
11715
|
-
};
|
|
11716
|
-
var iter = target.gui.displayArcs.getShapeIter(path);
|
|
11717
|
-
while (iter.hasNext()) {
|
|
11718
|
-
dragInfo.points.push([iter.x, iter.y]);
|
|
11719
|
-
dragInfo.ids.push(iter._arc.i);
|
|
11720
|
-
}
|
|
11721
|
-
gui.container.findChild('.map-layers').classed('dragging', true);
|
|
11722
|
-
|
|
11723
|
-
} else if (dragInfo) {
|
|
11724
|
-
gui.dispatchEvent('rectangle_dragend', dragInfo); // save undo state
|
|
11725
|
-
gui.container.findChild('.map-layers').classed('dragging', false);
|
|
11726
|
-
reset();
|
|
11727
|
-
} else {
|
|
11728
|
-
box.hide();
|
|
11729
|
-
}
|
|
11730
|
-
|
|
11731
|
-
});
|
|
11732
|
-
|
|
11733
|
-
box.on('handle_drag', function(e) {
|
|
11734
|
-
if (!_on || !dragInfo) return;
|
|
11735
|
-
var coords = internal.bboxToCoords(box.getDataCoords());
|
|
11736
|
-
setRectangleCoords(dragInfo.target, dragInfo.ids, coords);
|
|
11737
|
-
gui.dispatchEvent('map-needs-refresh');
|
|
11738
|
-
});
|
|
11739
|
-
|
|
11740
|
-
function turnOn() {
|
|
11741
|
-
box.turnOn();
|
|
11742
|
-
_on = true;
|
|
11883
|
+
// kludge: skip rendering base layers if hovering, except on first hover
|
|
11884
|
+
// (because highlight shapes may be rendered to the main canvas)
|
|
11885
|
+
function skipMainLayerRedraw(action) {
|
|
11886
|
+
return action == 'hover' && _overlayCanv.visible();
|
|
11743
11887
|
}
|
|
11744
11888
|
|
|
11745
|
-
function
|
|
11746
|
-
|
|
11747
|
-
if (gui.
|
|
11748
|
-
|
|
11749
|
-
|
|
11889
|
+
function drawCanvasLayer(lyr, canv) {
|
|
11890
|
+
if (!lyr) return;
|
|
11891
|
+
if (lyr.gui.style.type == 'outline') {
|
|
11892
|
+
drawOutlineLayerToCanvas(lyr, canv, ext);
|
|
11893
|
+
} else {
|
|
11894
|
+
drawStyledLayerToCanvas(lyr, canv, ext);
|
|
11750
11895
|
}
|
|
11751
|
-
_on = false;
|
|
11752
|
-
reset();
|
|
11753
11896
|
}
|
|
11754
11897
|
|
|
11755
|
-
function
|
|
11756
|
-
|
|
11757
|
-
|
|
11898
|
+
function getSvgLayerType(layer) {
|
|
11899
|
+
var type = null;
|
|
11900
|
+
if (internal.layerHasSvgSymbols(layer)) {
|
|
11901
|
+
type = 'symbol'; // also label + symbol
|
|
11902
|
+
} else if (internal.layerHasLabels(layer)) {
|
|
11903
|
+
type = 'symbol';
|
|
11904
|
+
}
|
|
11905
|
+
return type;
|
|
11758
11906
|
}
|
|
11759
11907
|
}
|
|
11760
11908
|
|
|
11761
|
-
//
|
|
11762
|
-
//
|
|
11763
|
-
function
|
|
11764
|
-
var
|
|
11765
|
-
|
|
11909
|
+
// Controls the shift-drag box editing tool
|
|
11910
|
+
//
|
|
11911
|
+
function BoxTool(gui, ext, nav) {
|
|
11912
|
+
var self = new EventDispatcher();
|
|
11913
|
+
var box = new HighlightBox(gui, {name: 'box-tool', persistent: true, handles: true, draggable: true});
|
|
11914
|
+
var popup = gui.container.findChild('.box-tool-options');
|
|
11915
|
+
var coords = popup.findChild('.box-coords');
|
|
11916
|
+
var _on = false;
|
|
11766
11917
|
|
|
11767
|
-
|
|
11768
|
-
|
|
11769
|
-
|
|
11770
|
-
}
|
|
11918
|
+
var infoBtn = new SimpleButton(popup.findChild('.info-btn')).on('click', function() {
|
|
11919
|
+
if (coords.visible()) hideCoords(); else showCoords();
|
|
11920
|
+
});
|
|
11771
11921
|
|
|
11772
|
-
|
|
11773
|
-
|
|
11774
|
-
|
|
11775
|
-
filteredArcs = initFilteredArcs(unfilteredArcs);
|
|
11776
|
-
filteredSegLen = internal.getAvgSegment(filteredArcs);
|
|
11777
|
-
} else {
|
|
11778
|
-
// Use fast simplification as a fallback
|
|
11779
|
-
filteredSegLen = internal.getAvgSegment(unfilteredArcs) * 4;
|
|
11780
|
-
filteredArcs = internal.simplifyArcsFast(unfilteredArcs, filteredSegLen);
|
|
11781
|
-
}
|
|
11782
|
-
}
|
|
11922
|
+
new SimpleButton(popup.findChild('.cancel-btn')).on('click', function() {
|
|
11923
|
+
reset();
|
|
11924
|
+
});
|
|
11783
11925
|
|
|
11784
|
-
|
|
11785
|
-
var
|
|
11786
|
-
|
|
11787
|
-
|
|
11788
|
-
|
|
11789
|
-
|
|
11790
|
-
|
|
11791
|
-
|
|
11792
|
-
|
|
11926
|
+
new SimpleButton(popup.findChild('.select-btn')).on('click', function() {
|
|
11927
|
+
var coords = box.getDataCoords();
|
|
11928
|
+
if (!coords) return;
|
|
11929
|
+
gui.enterMode('selection_tool');
|
|
11930
|
+
gui.interaction.setMode('selection');
|
|
11931
|
+
// kludge to pass bbox to the selection tool
|
|
11932
|
+
gui.dispatchEvent('selection_bridge', {
|
|
11933
|
+
map_data_bbox: coords
|
|
11934
|
+
});
|
|
11935
|
+
});
|
|
11793
11936
|
|
|
11794
|
-
|
|
11795
|
-
|
|
11796
|
-
|
|
11797
|
-
if (filteredArcs && filteredArcs.size() != unfilteredArcs.size()) {
|
|
11798
|
-
// arc count has changed... probably due to editing
|
|
11799
|
-
update();
|
|
11800
|
-
if (filteredArcs.size() != unfilteredArcs.size()) {
|
|
11801
|
-
throw Error('Internal error');
|
|
11802
|
-
}
|
|
11803
|
-
}
|
|
11804
|
-
if (filteredArcs) {
|
|
11805
|
-
// match simplification of unfiltered arcs
|
|
11806
|
-
filteredArcs.setRetainedInterval(unfilteredArcs.getRetainedInterval());
|
|
11807
|
-
}
|
|
11808
|
-
// switch to filtered version of arcs at small scales
|
|
11809
|
-
var unitsPerPixel = 1/ext.getTransform().mx,
|
|
11810
|
-
useFiltering = filteredArcs && unitsPerPixel > filteredSegLen * 1.5;
|
|
11811
|
-
return useFiltering ? filteredArcs : unfilteredArcs;
|
|
11812
|
-
};
|
|
11813
|
-
}
|
|
11937
|
+
new SimpleButton(popup.findChild('.clip-btn')).on('click', function() {
|
|
11938
|
+
runCommand('-clip bbox=' + box.getDataCoords().join(','));
|
|
11939
|
+
});
|
|
11814
11940
|
|
|
11815
|
-
|
|
11816
|
-
|
|
11817
|
-
|
|
11818
|
-
cellWidth = 12,
|
|
11819
|
-
cellHeight = 5,
|
|
11820
|
-
gutter = 6,
|
|
11821
|
-
arcs = [],
|
|
11822
|
-
shapes = [],
|
|
11823
|
-
aspectRatio = 1.1,
|
|
11824
|
-
x, y, col, row, blockSize;
|
|
11941
|
+
new SimpleButton(popup.findChild('.erase-btn')).on('click', function() {
|
|
11942
|
+
runCommand('-erase bbox=' + box.getDataCoords().join(','));
|
|
11943
|
+
});
|
|
11825
11944
|
|
|
11826
|
-
|
|
11827
|
-
|
|
11828
|
-
|
|
11829
|
-
|
|
11830
|
-
cellHeight = 4;
|
|
11831
|
-
aspectRatio = 1.45;
|
|
11832
|
-
} else if (n > 5000) {
|
|
11833
|
-
cellWidth = 5;
|
|
11834
|
-
gutter = 3;
|
|
11835
|
-
aspectRatio = 1.45;
|
|
11836
|
-
} else if (n > 1000) {
|
|
11837
|
-
gutter = 3;
|
|
11838
|
-
cellWidth = 8;
|
|
11839
|
-
aspectRatio = 1.3;
|
|
11840
|
-
}
|
|
11945
|
+
new SimpleButton(popup.findChild('.rect-btn')).on('click', function() {
|
|
11946
|
+
var cmd = '-rectangle + bbox=' + box.getDataCoords().join(',');
|
|
11947
|
+
runCommand(cmd);
|
|
11948
|
+
});
|
|
11841
11949
|
|
|
11842
|
-
|
|
11843
|
-
|
|
11844
|
-
}
|
|
11845
|
-
blockSize = Math.sqrt(n * (cellWidth + gutter) / cellHeight / aspectRatio) | 0;
|
|
11846
|
-
}
|
|
11950
|
+
new SimpleButton(popup.findChild('.frame-btn')).on('click', function() {
|
|
11951
|
+
openAddFramePopup(gui, box.getDataCoords());
|
|
11952
|
+
});
|
|
11847
11953
|
|
|
11848
|
-
|
|
11849
|
-
|
|
11850
|
-
|
|
11851
|
-
|
|
11852
|
-
|
|
11853
|
-
if (
|
|
11854
|
-
|
|
11855
|
-
shapes.push([[i]]);
|
|
11856
|
-
} else {
|
|
11857
|
-
shapes.push([[x, y]]);
|
|
11954
|
+
gui.addMode('box_tool', turnOn, turnOff);
|
|
11955
|
+
|
|
11956
|
+
gui.on('interaction_mode_change', function(e) {
|
|
11957
|
+
if (e.mode === 'box') {
|
|
11958
|
+
gui.enterMode('box_tool');
|
|
11959
|
+
} else if (_on) {
|
|
11960
|
+
turnOff();
|
|
11858
11961
|
}
|
|
11859
|
-
}
|
|
11962
|
+
});
|
|
11860
11963
|
|
|
11861
|
-
|
|
11862
|
-
|
|
11863
|
-
}
|
|
11964
|
+
gui.on('shift_drag_start', function() {
|
|
11965
|
+
hideCoords();
|
|
11966
|
+
});
|
|
11864
11967
|
|
|
11865
|
-
|
|
11866
|
-
|
|
11867
|
-
|
|
11868
|
-
shapes: shapes,
|
|
11869
|
-
data: table
|
|
11870
|
-
},
|
|
11871
|
-
arcs: arcs ? new internal.ArcCollection(arcs) : null
|
|
11872
|
-
};
|
|
11873
|
-
}
|
|
11968
|
+
box.on('dragend', function(e) {
|
|
11969
|
+
if (_on) popup.show();
|
|
11970
|
+
});
|
|
11874
11971
|
|
|
11875
|
-
|
|
11876
|
-
|
|
11877
|
-
|
|
11878
|
-
|
|
11879
|
-
|
|
11880
|
-
|
|
11881
|
-
|
|
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) {
|
|
11888
|
-
// re-apply layer filter
|
|
11889
|
-
lyr.gui.displayLayer = filterLayerByIds(lyr.gui.displayLayer, lyr.gui.style.ids);
|
|
11972
|
+
box.on('handle_drag', function() {
|
|
11973
|
+
if (coords.visible()) {
|
|
11974
|
+
showCoords();
|
|
11975
|
+
}
|
|
11976
|
+
});
|
|
11977
|
+
|
|
11978
|
+
function inZoomMode() {
|
|
11979
|
+
return !_on && gui.getMode() != 'selection_tool';
|
|
11890
11980
|
}
|
|
11891
|
-
}
|
|
11892
11981
|
|
|
11982
|
+
function runCommand(cmd) {
|
|
11983
|
+
if (gui.console) {
|
|
11984
|
+
gui.console.runMapshaperCommands(cmd, function(err) {
|
|
11985
|
+
reset();
|
|
11986
|
+
gui.clearMode();
|
|
11987
|
+
});
|
|
11988
|
+
}
|
|
11989
|
+
// reset(); // TODO: exit interactive mode
|
|
11990
|
+
}
|
|
11893
11991
|
|
|
11894
|
-
|
|
11895
|
-
|
|
11896
|
-
|
|
11897
|
-
|
|
11898
|
-
|
|
11899
|
-
|
|
11900
|
-
displayLayer: null,
|
|
11901
|
-
source: {dataset},
|
|
11902
|
-
bounds: null,
|
|
11903
|
-
style: null,
|
|
11904
|
-
dynamic_crs: null,
|
|
11905
|
-
invertPoint: null,
|
|
11906
|
-
projectPoint: null
|
|
11907
|
-
};
|
|
11992
|
+
function showCoords() {
|
|
11993
|
+
El(infoBtn.node()).addClass('selected-btn');
|
|
11994
|
+
coords.text(box.getDataCoords().join(','));
|
|
11995
|
+
coords.show();
|
|
11996
|
+
GUI.selectElement(coords.node());
|
|
11997
|
+
}
|
|
11908
11998
|
|
|
11909
|
-
|
|
11910
|
-
|
|
11911
|
-
|
|
11912
|
-
|
|
11913
|
-
var unprojectable = false;
|
|
11914
|
-
var sourceCRS;
|
|
11915
|
-
var emptyArcs;
|
|
11999
|
+
function hideCoords() {
|
|
12000
|
+
El(infoBtn.node()).removeClass('selected-btn');
|
|
12001
|
+
coords.hide();
|
|
12002
|
+
}
|
|
11916
12003
|
|
|
11917
|
-
|
|
11918
|
-
|
|
11919
|
-
|
|
11920
|
-
// unprojectable dataset -- return empty layer
|
|
11921
|
-
gui.unprojectable = true;
|
|
11922
|
-
} else {
|
|
11923
|
-
sourceCRS = crsInfo.crs;
|
|
11924
|
-
}
|
|
12004
|
+
function turnOn() {
|
|
12005
|
+
box.turnOn();
|
|
12006
|
+
_on = true;
|
|
11925
12007
|
}
|
|
11926
12008
|
|
|
11927
|
-
|
|
11928
|
-
|
|
11929
|
-
|
|
11930
|
-
|
|
11931
|
-
|
|
11932
|
-
if (needReprojectionForDisplay(sourceCRS, displayCRS)) {
|
|
11933
|
-
displayArcs = projectArcsForDisplay(dataset.arcs, sourceCRS, displayCRS);
|
|
11934
|
-
} else {
|
|
11935
|
-
// Use original arcs for display if there is no dynamic reprojection
|
|
11936
|
-
displayArcs = dataset.arcs;
|
|
12009
|
+
function turnOff() {
|
|
12010
|
+
box.turnOff();
|
|
12011
|
+
if (gui.interaction.getMode() == 'box') {
|
|
12012
|
+
// mode change was not initiated by interactive menu -- turn off interactivity
|
|
12013
|
+
gui.interaction.turnOff();
|
|
11937
12014
|
}
|
|
11938
|
-
|
|
11939
|
-
|
|
11940
|
-
dataset.gui = {displayArcs}; // stash these in the dataset for other layers to use
|
|
12015
|
+
_on = false;
|
|
12016
|
+
reset();
|
|
11941
12017
|
}
|
|
11942
12018
|
|
|
11943
|
-
|
|
11944
|
-
|
|
11945
|
-
|
|
11946
|
-
|
|
12019
|
+
function reset() {
|
|
12020
|
+
box.hide();
|
|
12021
|
+
popup.hide();
|
|
12022
|
+
hideCoords();
|
|
11947
12023
|
}
|
|
11948
12024
|
|
|
11949
|
-
|
|
11950
|
-
|
|
11951
|
-
|
|
11952
|
-
|
|
11953
|
-
|
|
11954
|
-
|
|
11955
|
-
|
|
11956
|
-
|
|
11957
|
-
|
|
11958
|
-
|
|
11959
|
-
|
|
12025
|
+
function openAddFramePopup(gui, bbox) {
|
|
12026
|
+
var popup = showPopupAlert('', 'Add a map frame');
|
|
12027
|
+
var el = popup.container();
|
|
12028
|
+
el.addClass('option-menu');
|
|
12029
|
+
var html = `<p>Enter a width in px, cm or inches to create a frame layer
|
|
12030
|
+
for setting the size of the map for symbol scaling in the
|
|
12031
|
+
GUI and setting the size and crop of SVG output.</p><div><input type="text" class="frame-width text-input" placeholder="examples: 600px 5in"></div>
|
|
12032
|
+
<div tabindex="0" class="btn dialog-btn">Create</div></span>`;
|
|
12033
|
+
el.html(html);
|
|
12034
|
+
var input = el.findChild('.frame-width');
|
|
12035
|
+
input.node().focus();
|
|
12036
|
+
var btn = el.findChild('.btn').on('click', function() {
|
|
12037
|
+
var widthStr = input.node().value.trim();
|
|
12038
|
+
if (parseFloat(widthStr) > 0 === false) {
|
|
12039
|
+
// invalid input
|
|
12040
|
+
input.node().value = '';
|
|
12041
|
+
return;
|
|
12042
|
+
}
|
|
12043
|
+
var cmd = `-rectangle + name=frame bbox='${bbox.join(',')}' width='${widthStr}'`;
|
|
12044
|
+
runCommand(cmd);
|
|
12045
|
+
popup.close();
|
|
12046
|
+
});
|
|
11960
12047
|
}
|
|
11961
12048
|
|
|
11962
|
-
|
|
11963
|
-
|
|
11964
|
-
|
|
11965
|
-
|
|
11966
|
-
|
|
11967
|
-
|
|
11968
|
-
|
|
11969
|
-
|
|
11970
|
-
|
|
11971
|
-
|
|
11972
|
-
|
|
11973
|
-
|
|
12049
|
+
return self;
|
|
12050
|
+
}
|
|
12051
|
+
|
|
12052
|
+
function RectangleControl(gui, hit) {
|
|
12053
|
+
var box = new HighlightBox(gui, {name: 'rectangle-tool', persistent: true, handles: true, classname: 'rectangles', draggable: false});
|
|
12054
|
+
var _on = false;
|
|
12055
|
+
var dragInfo;
|
|
12056
|
+
|
|
12057
|
+
gui.addMode('rectangle_tool', turnOn, turnOff);
|
|
12058
|
+
|
|
12059
|
+
gui.on('interaction_mode_change', function(e) {
|
|
12060
|
+
if (e.mode === 'rectangles') {
|
|
12061
|
+
gui.enterMode('rectangle_tool');
|
|
12062
|
+
} else if (_on) {
|
|
12063
|
+
turnOff();
|
|
12064
|
+
}
|
|
12065
|
+
});
|
|
12066
|
+
|
|
12067
|
+
hit.on('change', function(e) {
|
|
12068
|
+
if (!_on) return;
|
|
12069
|
+
// TODO: handle multiple hits (see gui-inspection-control)
|
|
12070
|
+
var id = e.id;
|
|
12071
|
+
if (e.id > -1 && e.pinned) {
|
|
12072
|
+
var target = hit.getHitTarget();
|
|
12073
|
+
var path = target.shapes[e.id][0];
|
|
12074
|
+
var bbox = target.gui.displayArcs.getSimpleShapeBounds(path).toArray();
|
|
12075
|
+
box.setDataCoords(bbox);
|
|
12076
|
+
dragInfo = {
|
|
12077
|
+
id: e.id,
|
|
12078
|
+
target: target,
|
|
12079
|
+
ids: [],
|
|
12080
|
+
points: []
|
|
12081
|
+
};
|
|
12082
|
+
var iter = target.gui.displayArcs.getShapeIter(path);
|
|
12083
|
+
while (iter.hasNext()) {
|
|
12084
|
+
dragInfo.points.push([iter.x, iter.y]);
|
|
12085
|
+
dragInfo.ids.push(iter._arc.i);
|
|
11974
12086
|
}
|
|
12087
|
+
gui.container.findChild('.map-layers').classed('dragging', true);
|
|
12088
|
+
|
|
12089
|
+
} else if (dragInfo) {
|
|
12090
|
+
gui.dispatchEvent('rectangle_dragend', dragInfo); // save undo state
|
|
12091
|
+
gui.container.findChild('.map-layers').classed('dragging', false);
|
|
12092
|
+
reset();
|
|
12093
|
+
} else {
|
|
12094
|
+
box.hide();
|
|
11975
12095
|
}
|
|
11976
|
-
}
|
|
11977
12096
|
|
|
11978
|
-
|
|
11979
|
-
layer.gui = gui;
|
|
11980
|
-
}
|
|
12097
|
+
});
|
|
11981
12098
|
|
|
12099
|
+
box.on('handle_drag', function(e) {
|
|
12100
|
+
if (!_on || !dragInfo) return;
|
|
12101
|
+
var coords = internal.bboxToCoords(box.getDataCoords());
|
|
12102
|
+
setRectangleCoords(dragInfo.target, dragInfo.ids, coords);
|
|
12103
|
+
gui.dispatchEvent('map-needs-refresh');
|
|
12104
|
+
});
|
|
11982
12105
|
|
|
11983
|
-
|
|
11984
|
-
|
|
11985
|
-
|
|
11986
|
-
// if a point layer has no extent (e.g. contains only a single point),
|
|
11987
|
-
// then merge with arc bounds, to place the point in context.
|
|
11988
|
-
bounds = bounds.mergeBounds(arcs.getBounds());
|
|
12106
|
+
function turnOn() {
|
|
12107
|
+
box.turnOn();
|
|
12108
|
+
_on = true;
|
|
11989
12109
|
}
|
|
11990
|
-
return bounds;
|
|
11991
|
-
}
|
|
11992
12110
|
|
|
11993
|
-
|
|
11994
|
-
|
|
11995
|
-
|
|
11996
|
-
|
|
11997
|
-
|
|
11998
|
-
if (nn[i] === 0) {
|
|
11999
|
-
ids.push(i);
|
|
12111
|
+
function turnOff() {
|
|
12112
|
+
box.turnOff();
|
|
12113
|
+
if (gui.interaction.getMode() == 'rectangles') {
|
|
12114
|
+
// mode change was not initiated by interactive menu -- turn off interactivity
|
|
12115
|
+
gui.interaction.turnOff();
|
|
12000
12116
|
}
|
|
12117
|
+
_on = false;
|
|
12118
|
+
reset();
|
|
12119
|
+
}
|
|
12120
|
+
|
|
12121
|
+
function reset() {
|
|
12122
|
+
box.hide();
|
|
12123
|
+
dragInfo = null;
|
|
12001
12124
|
}
|
|
12002
|
-
return ids;
|
|
12003
12125
|
}
|
|
12004
12126
|
|
|
12005
12127
|
function loadScript(url, cb) {
|
|
@@ -12052,18 +12174,14 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12052
12174
|
turnOff();
|
|
12053
12175
|
});
|
|
12054
12176
|
|
|
12055
|
-
// hideBtn.on('mousedown', function() {
|
|
12056
|
-
// if (activeStyle) {
|
|
12057
|
-
// mapEl.css('visibility', 'hidden');
|
|
12058
|
-
// hidden = true;
|
|
12059
|
-
// }
|
|
12060
|
-
// })
|
|
12061
12177
|
clearBtn.on('click', function() {
|
|
12062
12178
|
if (activeStyle) {
|
|
12063
|
-
|
|
12179
|
+
turnOffBasemap();
|
|
12064
12180
|
updateButtons();
|
|
12181
|
+
closeMenu();
|
|
12065
12182
|
}
|
|
12066
12183
|
});
|
|
12184
|
+
|
|
12067
12185
|
fadeBtn.on('click', function() {
|
|
12068
12186
|
if (faded) {
|
|
12069
12187
|
mapEl.css('opacity', 1);
|
|
@@ -12084,28 +12202,42 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12084
12202
|
params.styles.forEach(function(style) {
|
|
12085
12203
|
var btn = El('div').html(`<div class="basemap-style-btn"><img src="${style.icon}"></img></div><div class="basemap-style-label">${style.name}</div>`);
|
|
12086
12204
|
btn.findChild('.basemap-style-btn').on('click', function() {
|
|
12087
|
-
|
|
12205
|
+
if (style == activeStyle) {
|
|
12206
|
+
turnOffBasemap();
|
|
12207
|
+
} else {
|
|
12208
|
+
showBasemap(style);
|
|
12209
|
+
}
|
|
12088
12210
|
updateButtons();
|
|
12211
|
+
closeMenu();
|
|
12089
12212
|
});
|
|
12090
12213
|
btn.appendTo(list);
|
|
12091
12214
|
});
|
|
12092
12215
|
}
|
|
12093
12216
|
|
|
12094
|
-
|
|
12095
|
-
|
|
12096
|
-
|
|
12217
|
+
// close and turn off mode
|
|
12218
|
+
function closeMenu() {
|
|
12219
|
+
setTimeout(function() {
|
|
12220
|
+
gui.clearMode();
|
|
12221
|
+
}, 200);
|
|
12222
|
+
}
|
|
12223
|
+
|
|
12224
|
+
function turnOffBasemap() {
|
|
12225
|
+
activeStyle = null;
|
|
12226
|
+
gui.map.setDisplayCRS(null);
|
|
12227
|
+
refresh();
|
|
12228
|
+
}
|
|
12229
|
+
|
|
12230
|
+
function showBasemap(style) {
|
|
12231
|
+
activeStyle = style;
|
|
12232
|
+
// TODO: consider enabling dark basemap mode
|
|
12097
12233
|
// Make sure that the selected layer style gets updated in gui-map.js
|
|
12098
12234
|
// gui.state.dark_basemap = style && style.dark || false;
|
|
12099
|
-
if (
|
|
12100
|
-
gui.map.setDisplayCRS(null);
|
|
12101
|
-
refresh();
|
|
12102
|
-
} else if (map) {
|
|
12235
|
+
if (map) {
|
|
12103
12236
|
map.setStyle(style.url);
|
|
12104
12237
|
refresh();
|
|
12105
|
-
} else {
|
|
12238
|
+
} else if (prepareMapView()) {
|
|
12106
12239
|
initMap();
|
|
12107
12240
|
}
|
|
12108
|
-
|
|
12109
12241
|
}
|
|
12110
12242
|
|
|
12111
12243
|
function updateButtons() {
|
|
@@ -12115,8 +12247,9 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12115
12247
|
}
|
|
12116
12248
|
|
|
12117
12249
|
function turnOn() {
|
|
12118
|
-
|
|
12119
|
-
var
|
|
12250
|
+
// TODO: show basemap even if there is no data
|
|
12251
|
+
var activeLyr = gui.model.getActiveLayer(); // may be null
|
|
12252
|
+
var info = getDatasetCrsInfo(activeLyr?.dataset); // defaults to wgs84
|
|
12120
12253
|
var dataCRS = info.crs || null;
|
|
12121
12254
|
var displayCRS = gui.map.getDisplayCRS();
|
|
12122
12255
|
var warning;
|
|
@@ -12159,9 +12292,9 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12159
12292
|
|
|
12160
12293
|
function getLonLatBounds() {
|
|
12161
12294
|
var bbox = ext.getBounds().toArray();
|
|
12162
|
-
var
|
|
12163
|
-
|
|
12164
|
-
return
|
|
12295
|
+
var bbox2 = fromWebMercator(bbox[0], bbox[1])
|
|
12296
|
+
.concat(fromWebMercator(bbox[2], bbox[3]));
|
|
12297
|
+
return bbox2;
|
|
12165
12298
|
}
|
|
12166
12299
|
|
|
12167
12300
|
function initMap() {
|
|
@@ -12219,6 +12352,15 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12219
12352
|
return true;
|
|
12220
12353
|
}
|
|
12221
12354
|
|
|
12355
|
+
function prepareMapView() {
|
|
12356
|
+
var crs = gui.map.getDisplayCRS();
|
|
12357
|
+
if (!crs) return false;
|
|
12358
|
+
if (!internal.isWebMercator(crs)) {
|
|
12359
|
+
gui.map.setDisplayCRS(internal.parseCrsString('webmercator'));
|
|
12360
|
+
}
|
|
12361
|
+
return true;
|
|
12362
|
+
}
|
|
12363
|
+
|
|
12222
12364
|
function refresh() {
|
|
12223
12365
|
var crs = gui.map.getDisplayCRS();
|
|
12224
12366
|
var off = !crs || !enabled() || !map || loading || !activeStyle;
|
|
@@ -12230,9 +12372,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12230
12372
|
return;
|
|
12231
12373
|
}
|
|
12232
12374
|
|
|
12233
|
-
|
|
12234
|
-
gui.map.setDisplayCRS(internal.parseCrsString('webmercator'));
|
|
12235
|
-
}
|
|
12375
|
+
prepareMapView();
|
|
12236
12376
|
var bbox = getLonLatBounds();
|
|
12237
12377
|
if (!checkBounds(bbox)) {
|
|
12238
12378
|
// map does not display outside these bounds
|
|
@@ -12331,8 +12471,15 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12331
12471
|
};
|
|
12332
12472
|
|
|
12333
12473
|
this.getDisplayCRS = function() {
|
|
12334
|
-
if (!_activeLyr
|
|
12335
|
-
|
|
12474
|
+
if (!_activeLyr) {
|
|
12475
|
+
return _dynamicCRS || internal.parseCrsString('wgs84');
|
|
12476
|
+
}
|
|
12477
|
+
if (!_activeLyr.gui.geographic) {
|
|
12478
|
+
return null;
|
|
12479
|
+
}
|
|
12480
|
+
if (_activeLyr.gui.dynamic_crs) {
|
|
12481
|
+
return _activeLyr.gui.dynamic_crs;
|
|
12482
|
+
}
|
|
12336
12483
|
var info = getDatasetCrsInfo(_activeLyr.gui.source.dataset);
|
|
12337
12484
|
return info.crs || null;
|
|
12338
12485
|
};
|
|
@@ -12365,7 +12512,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12365
12512
|
var newCRS = utils$1.isString(crs) ? internal.parseCrsString(crs) : crs;
|
|
12366
12513
|
// TODO: handle case that old and new CRS are the same
|
|
12367
12514
|
_dynamicCRS = newCRS;
|
|
12368
|
-
if (!_activeLyr) return; // stop here if no layers have been selected
|
|
12515
|
+
// if (!_activeLyr) return; // stop here if no layers have been selected
|
|
12369
12516
|
|
|
12370
12517
|
// clear any stored FilteredArcs objects (so they will be recreated with the desired projection)
|
|
12371
12518
|
clearAllDisplayArcs();
|
|
@@ -12383,6 +12530,34 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12383
12530
|
updateFullBounds();
|
|
12384
12531
|
};
|
|
12385
12532
|
|
|
12533
|
+
// Initialization just before displaying the map for the first time
|
|
12534
|
+
this.init = function() {
|
|
12535
|
+
if (_renderer) return;
|
|
12536
|
+
_ext.setFullBounds(calcFullBounds());
|
|
12537
|
+
_ext.resize();
|
|
12538
|
+
_renderer = new LayerRenderer(gui, el);
|
|
12539
|
+
gui.buttons.show();
|
|
12540
|
+
|
|
12541
|
+
if (opts.inspectorControl) {
|
|
12542
|
+
_hit = new HitControl(gui, _ext, _mouse),
|
|
12543
|
+
new InspectionControl2(gui, _hit);
|
|
12544
|
+
new SelectionTool(gui, _ext, _hit),
|
|
12545
|
+
new BoxTool(gui, _ext, _nav),
|
|
12546
|
+
new RectangleControl(gui, _hit),
|
|
12547
|
+
initInteractiveEditing(gui, _ext, _hit);
|
|
12548
|
+
_hit.on('change', updateOverlayLayer);
|
|
12549
|
+
}
|
|
12550
|
+
|
|
12551
|
+
_ext.on('change', function(e) {
|
|
12552
|
+
if (_basemap) _basemap.refresh(); // keep basemap synced up (if enabled)
|
|
12553
|
+
drawLayers(e.redraw ? '' : 'nav');
|
|
12554
|
+
});
|
|
12555
|
+
|
|
12556
|
+
gui.on('resize', function() {
|
|
12557
|
+
position.update(); // kludge to detect new map size after console toggle
|
|
12558
|
+
});
|
|
12559
|
+
};
|
|
12560
|
+
|
|
12386
12561
|
function getGlobalStyleOptions(opts) {
|
|
12387
12562
|
var mode = gui.state.interaction_mode;
|
|
12388
12563
|
return Object.assign({
|
|
@@ -12398,7 +12573,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12398
12573
|
var fullBounds;
|
|
12399
12574
|
var needReset;
|
|
12400
12575
|
if (!prevLyr) {
|
|
12401
|
-
initMap(); // first call
|
|
12576
|
+
// initMap(); // first call
|
|
12402
12577
|
}
|
|
12403
12578
|
|
|
12404
12579
|
if (arcsMayHaveChanged(e.flags)) {
|
|
@@ -12422,10 +12597,14 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12422
12597
|
return;
|
|
12423
12598
|
}
|
|
12424
12599
|
|
|
12425
|
-
|
|
12426
|
-
|
|
12427
|
-
|
|
12428
|
-
|
|
12600
|
+
if (e.layer) {
|
|
12601
|
+
enhanceLayerForDisplay(e.layer, e.dataset, getDisplayOptions());
|
|
12602
|
+
_activeLyr = e.layer;
|
|
12603
|
+
_activeLyr.gui.style = getActiveLayerStyle(_activeLyr.gui.displayLayer, getGlobalStyleOptions());
|
|
12604
|
+
_activeLyr.active = true;
|
|
12605
|
+
} else {
|
|
12606
|
+
_activeLyr = null;
|
|
12607
|
+
}
|
|
12429
12608
|
|
|
12430
12609
|
if (popupCanStayOpen(e.flags)) {
|
|
12431
12610
|
// data may have changed; if popup is open, it needs to be refreshed
|
|
@@ -12438,7 +12617,11 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12438
12617
|
updateVisibleMapLayers();
|
|
12439
12618
|
fullBounds = calcFullBounds();
|
|
12440
12619
|
|
|
12441
|
-
if (
|
|
12620
|
+
if (prevLyr?.gui.tabular || _activeLyr?.gui.tabular) {
|
|
12621
|
+
needReset = true;
|
|
12622
|
+
} else if (_activeLyr && internal.layerIsEmpty(_activeLyr)) {
|
|
12623
|
+
needReset = false;
|
|
12624
|
+
} else if (!prevLyr) {
|
|
12442
12625
|
needReset = true;
|
|
12443
12626
|
} else {
|
|
12444
12627
|
needReset = mapNeedsReset(fullBounds, _ext.getFullBounds(), _ext.getBounds(), e.flags);
|
|
@@ -12448,37 +12631,12 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12448
12631
|
|
|
12449
12632
|
if (needReset) {
|
|
12450
12633
|
_ext.reset();
|
|
12634
|
+
if (_basemap) _basemap.refresh();
|
|
12451
12635
|
}
|
|
12452
12636
|
drawLayers();
|
|
12453
12637
|
map.dispatchEvent('updated');
|
|
12454
12638
|
}
|
|
12455
12639
|
|
|
12456
|
-
// Initialization just before displaying the map for the first time
|
|
12457
|
-
function initMap() {
|
|
12458
|
-
_ext.resize();
|
|
12459
|
-
_renderer = new LayerRenderer(gui, el, _ext, _mouse);
|
|
12460
|
-
gui.buttons.show();
|
|
12461
|
-
|
|
12462
|
-
if (opts.inspectorControl) {
|
|
12463
|
-
_hit = new HitControl(gui, _ext, _mouse),
|
|
12464
|
-
new InspectionControl2(gui, _hit);
|
|
12465
|
-
new SelectionTool(gui, _ext, _hit),
|
|
12466
|
-
new BoxTool(gui, _ext, _nav),
|
|
12467
|
-
new RectangleControl(gui, _hit),
|
|
12468
|
-
initInteractiveEditing(gui, _ext, _hit);
|
|
12469
|
-
_hit.on('change', updateOverlayLayer);
|
|
12470
|
-
}
|
|
12471
|
-
|
|
12472
|
-
_ext.on('change', function(e) {
|
|
12473
|
-
if (_basemap) _basemap.refresh(); // keep basemap synced up (if enabled)
|
|
12474
|
-
if (e.reset) return; // don't need to redraw map here if extent has been reset
|
|
12475
|
-
drawLayers('nav');
|
|
12476
|
-
});
|
|
12477
|
-
|
|
12478
|
-
gui.on('resize', function() {
|
|
12479
|
-
position.update(); // kludge to detect new map size after console toggle
|
|
12480
|
-
});
|
|
12481
|
-
}
|
|
12482
12640
|
|
|
12483
12641
|
function updateOverlayLayer(e) {
|
|
12484
12642
|
var style = getOverlayStyle(_activeLyr.gui.displayLayer, e, getGlobalStyleOptions());
|
|
@@ -12521,7 +12679,8 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12521
12679
|
|
|
12522
12680
|
if (!b.hasBounds()) {
|
|
12523
12681
|
// assign bounds to empty layers, to prevent rendering errors downstream
|
|
12524
|
-
b.setBounds(0,0,0,0);
|
|
12682
|
+
// b.setBounds(0,0,0,0);
|
|
12683
|
+
b.setBounds(projectLatLonBBox([11.28,33.43,32.26,46.04], _dynamicCRS));
|
|
12525
12684
|
}
|
|
12526
12685
|
return b;
|
|
12527
12686
|
}
|
|
@@ -12546,7 +12705,6 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12546
12705
|
|
|
12547
12706
|
// Inflate display bounding box by a tiny amount (gives extent to single-point layers and collapsed shapes)
|
|
12548
12707
|
b.padBounds(1e-4, 1e-4, 1e-4, 1e-4);
|
|
12549
|
-
|
|
12550
12708
|
return b;
|
|
12551
12709
|
}
|
|
12552
12710
|
|
|
@@ -12559,7 +12717,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12559
12717
|
}
|
|
12560
12718
|
|
|
12561
12719
|
function isTableView() {
|
|
12562
|
-
return !!_activeLyr
|
|
12720
|
+
return !!_activeLyr?.gui.tabular;
|
|
12563
12721
|
}
|
|
12564
12722
|
|
|
12565
12723
|
function findFrameLayer() {
|
|
@@ -12769,12 +12927,13 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12769
12927
|
El('div')
|
|
12770
12928
|
.appendTo(menu)
|
|
12771
12929
|
.addClass('contextmenu-item')
|
|
12772
|
-
.
|
|
12930
|
+
.html(label)
|
|
12773
12931
|
.on('click', func)
|
|
12774
12932
|
.show();
|
|
12775
12933
|
}
|
|
12776
12934
|
|
|
12777
12935
|
this.open = function(e, lyr) {
|
|
12936
|
+
if (!lyr || !lyr.gui.geographic) return;
|
|
12778
12937
|
_open = true;
|
|
12779
12938
|
_openCount++;
|
|
12780
12939
|
var rspace = body.clientWidth - e.pageX;
|
|
@@ -12782,27 +12941,29 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12782
12941
|
menu.empty().show();
|
|
12783
12942
|
if (rspace > 150) {
|
|
12784
12943
|
menu.css('left', e.pageX + xoffs + 'px');
|
|
12944
|
+
menu.css('right', null);
|
|
12785
12945
|
} else {
|
|
12786
12946
|
menu.css('right', (body.clientWidth - e.pageX + xoffs) + 'px');
|
|
12947
|
+
menu.css('left', null);
|
|
12787
12948
|
}
|
|
12788
12949
|
menu.css('top', (e.pageY - 15) + 'px');
|
|
12789
12950
|
|
|
12790
12951
|
// menu contents
|
|
12791
|
-
if (e.deleteVertex) {
|
|
12792
|
-
addMenuItem('Delete vertex', e.deleteVertex);
|
|
12793
|
-
}
|
|
12794
12952
|
if (e.coordinates) {
|
|
12795
12953
|
addCopyCoords();
|
|
12796
12954
|
}
|
|
12955
|
+
if (e.deleteVertex) {
|
|
12956
|
+
addMenuItem('Delete vertex', e.deleteVertex);
|
|
12957
|
+
}
|
|
12797
12958
|
if (e.ids?.length) {
|
|
12798
12959
|
addMenuItem('Copy as GeoJSON', copyGeoJSON);
|
|
12799
12960
|
}
|
|
12800
12961
|
|
|
12801
12962
|
function addCopyCoords() {
|
|
12802
12963
|
var bbox = internal.getLayerBounds(lyr, lyr.gui.source.dataset.arcs).toArray();
|
|
12803
|
-
var
|
|
12804
|
-
var
|
|
12805
|
-
addMenuItem(
|
|
12964
|
+
var coordStr = internal.getRoundedCoordString(e.coordinates, internal.getBoundsPrecisionForDisplay(bbox));
|
|
12965
|
+
var displayStr = '• ' + coordStr.replace(/-/g, '–').replace(',', ', ');
|
|
12966
|
+
addMenuItem(displayStr, function() {
|
|
12806
12967
|
saveFileContentToClipboard(coordStr);
|
|
12807
12968
|
});
|
|
12808
12969
|
}
|
|
@@ -12817,7 +12978,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12817
12978
|
var layer = mapshaper.cmd.filterFeatures(lyr, dataset.arcs, opts);
|
|
12818
12979
|
// the drawing tool can send open paths with 'polygon' geometry type,
|
|
12819
12980
|
// should be changed to 'polyline'
|
|
12820
|
-
if (layerHasOpenPaths(layer, dataset.arcs)) {
|
|
12981
|
+
if (layer.geometry_type == 'polygon' && layerHasOpenPaths(layer, dataset.arcs)) {
|
|
12821
12982
|
layer.geometry_type = 'polyline';
|
|
12822
12983
|
}
|
|
12823
12984
|
var features = internal.exportLayerAsGeoJSON(layer, dataset, {rfc7946: true, prettify: true}, true, 'string');
|
|
@@ -12868,6 +13029,7 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12868
13029
|
var clearMsg;
|
|
12869
13030
|
|
|
12870
13031
|
initModeRules(gui);
|
|
13032
|
+
gui.map.init();
|
|
12871
13033
|
|
|
12872
13034
|
gui.showProgressMessage = function(msg) {
|
|
12873
13035
|
if (!gui.progressMessage) {
|
|
@@ -12980,9 +13142,10 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
12980
13142
|
importOpts = getImportOpts(manifest),
|
|
12981
13143
|
gui = new GuiInstance('body');
|
|
12982
13144
|
|
|
12983
|
-
|
|
12984
|
-
|
|
12985
|
-
|
|
13145
|
+
// TODO: re-enable the "blurb"
|
|
13146
|
+
// if (manifest.blurb) {
|
|
13147
|
+
// El('#splash-screen-blurb').text(manifest.blurb);
|
|
13148
|
+
// }
|
|
12986
13149
|
|
|
12987
13150
|
new AlertControl(gui);
|
|
12988
13151
|
new RepairControl(gui);
|
|
@@ -13013,17 +13176,19 @@ GUI and setting the size and crop of SVG output.</p><div><input type="text" clas
|
|
|
13013
13176
|
});
|
|
13014
13177
|
|
|
13015
13178
|
// Initial display configuration
|
|
13016
|
-
gui.
|
|
13179
|
+
gui.on('mode', function(e) {
|
|
13017
13180
|
if (dataLoaded) return;
|
|
13018
13181
|
dataLoaded = true;
|
|
13182
|
+
gui.buttons.show();
|
|
13019
13183
|
El('#mode-buttons').show();
|
|
13184
|
+
El('#splash-buttons').hide();
|
|
13185
|
+
El('body').addClass('map-view');
|
|
13020
13186
|
if (importOpts.display_all) {
|
|
13021
13187
|
gui.model.getLayers().forEach(function(o) {
|
|
13022
13188
|
gui.map.setLayerPinning(o, true);
|
|
13023
13189
|
});
|
|
13024
13190
|
}
|
|
13025
13191
|
gui.console.runInitialCommands(getInitialConsoleCommands());
|
|
13026
|
-
|
|
13027
13192
|
});
|
|
13028
13193
|
};
|
|
13029
13194
|
|