mapshaper 0.5.103 → 0.5.104
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/CHANGELOG.md +3 -0
- package/mapshaper.js +36 -17
- package/package.json +1 -1
- package/www/mapshaper-gui.js +888 -728
- package/www/mapshaper.js +36 -17
package/www/mapshaper-gui.js
CHANGED
|
@@ -1736,281 +1736,249 @@
|
|
|
1736
1736
|
|
|
1737
1737
|
utils$1.inherit(Slider, EventDispatcher);
|
|
1738
1738
|
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
var
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
filteredSegLen = internal.getAvgSegment(filteredArcs);
|
|
1751
|
-
} else {
|
|
1752
|
-
// Use fast simplification as a fallback
|
|
1753
|
-
filteredSegLen = internal.getAvgSegment(unfilteredArcs) * 4;
|
|
1754
|
-
filteredArcs = internal.simplifyArcsFast(unfilteredArcs, filteredSegLen);
|
|
1755
|
-
}
|
|
1756
|
-
}
|
|
1757
|
-
|
|
1758
|
-
function initFilteredArcs(arcs) {
|
|
1759
|
-
var filterPct = 0.08;
|
|
1760
|
-
var nth = Math.ceil(arcs.getPointCount() / 5e5);
|
|
1761
|
-
var currInterval = arcs.getRetainedInterval();
|
|
1762
|
-
var filterZ = arcs.getThresholdByPct(filterPct, nth);
|
|
1763
|
-
var filteredArcs = arcs.setRetainedInterval(filterZ).getFilteredCopy();
|
|
1764
|
-
arcs.setRetainedInterval(currInterval); // reset current simplification
|
|
1765
|
-
return filteredArcs;
|
|
1739
|
+
function saveZipFile(zipfileName, files, done) {
|
|
1740
|
+
var zip = window.zip; // assumes zip library is loaded globally
|
|
1741
|
+
var toAdd = files;
|
|
1742
|
+
var zipWriter;
|
|
1743
|
+
try {
|
|
1744
|
+
zip.createWriter(new zip.BlobWriter("application/zip"), function(writer) {
|
|
1745
|
+
zipWriter = writer;
|
|
1746
|
+
nextFile();
|
|
1747
|
+
}, zipError);
|
|
1748
|
+
} catch(e) {
|
|
1749
|
+
done("This browser doesn't support Zip file creation.");
|
|
1766
1750
|
}
|
|
1767
1751
|
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1752
|
+
function zipError(err) {
|
|
1753
|
+
var str = "Error creating Zip file";
|
|
1754
|
+
var msg = '';
|
|
1755
|
+
// error events thrown by Zip library seem to be missing a message
|
|
1756
|
+
if (err && err.message) {
|
|
1757
|
+
msg = err.message;
|
|
1772
1758
|
}
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
};
|
|
1778
|
-
}
|
|
1779
|
-
|
|
1780
|
-
function getDisplayLayerForTable(table) {
|
|
1781
|
-
var n = table.size(),
|
|
1782
|
-
cellWidth = 12,
|
|
1783
|
-
cellHeight = 5,
|
|
1784
|
-
gutter = 6,
|
|
1785
|
-
arcs = [],
|
|
1786
|
-
shapes = [],
|
|
1787
|
-
aspectRatio = 1.1,
|
|
1788
|
-
x, y, col, row, blockSize;
|
|
1789
|
-
|
|
1790
|
-
if (n > 10000) {
|
|
1791
|
-
arcs = null;
|
|
1792
|
-
gutter = 0;
|
|
1793
|
-
cellWidth = 4;
|
|
1794
|
-
cellHeight = 4;
|
|
1795
|
-
aspectRatio = 1.45;
|
|
1796
|
-
} else if (n > 5000) {
|
|
1797
|
-
cellWidth = 5;
|
|
1798
|
-
gutter = 3;
|
|
1799
|
-
aspectRatio = 1.45;
|
|
1800
|
-
} else if (n > 1000) {
|
|
1801
|
-
gutter = 3;
|
|
1802
|
-
cellWidth = 8;
|
|
1803
|
-
aspectRatio = 1.3;
|
|
1804
|
-
}
|
|
1805
|
-
|
|
1806
|
-
if (n < 25) {
|
|
1807
|
-
blockSize = n;
|
|
1808
|
-
} else {
|
|
1809
|
-
blockSize = Math.sqrt(n * (cellWidth + gutter) / cellHeight / aspectRatio) | 0;
|
|
1759
|
+
if (msg) {
|
|
1760
|
+
str += ": " + msg;
|
|
1761
|
+
}
|
|
1762
|
+
done(str);
|
|
1810
1763
|
}
|
|
1811
1764
|
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
if (arcs) {
|
|
1818
|
-
arcs.push(getArc(x, y, cellWidth, cellHeight));
|
|
1819
|
-
shapes.push([[i]]);
|
|
1765
|
+
function nextFile() {
|
|
1766
|
+
if (toAdd.length === 0) {
|
|
1767
|
+
zipWriter.close(function(blob) {
|
|
1768
|
+
saveBlobToDownloadFolder(zipfileName, blob, done);
|
|
1769
|
+
});
|
|
1820
1770
|
} else {
|
|
1821
|
-
|
|
1771
|
+
var obj = toAdd.pop(),
|
|
1772
|
+
blob = new Blob([obj.content]);
|
|
1773
|
+
zipWriter.add(obj.filename, new zip.BlobReader(blob), nextFile);
|
|
1822
1774
|
}
|
|
1823
1775
|
}
|
|
1776
|
+
}
|
|
1824
1777
|
|
|
1825
|
-
|
|
1826
|
-
|
|
1778
|
+
function saveFilesToServer(paths, data, done) {
|
|
1779
|
+
var i = -1;
|
|
1780
|
+
next();
|
|
1781
|
+
function next(err) {
|
|
1782
|
+
i++;
|
|
1783
|
+
if (err) return done(err);
|
|
1784
|
+
if (i >= data.length) return done();
|
|
1785
|
+
saveBlobToServer(paths[i], new Blob([data[i]]), next);
|
|
1827
1786
|
}
|
|
1828
|
-
|
|
1829
|
-
return {
|
|
1830
|
-
layer: {
|
|
1831
|
-
geometry_type: arcs ? 'polygon' : 'point',
|
|
1832
|
-
shapes: shapes,
|
|
1833
|
-
data: table
|
|
1834
|
-
},
|
|
1835
|
-
arcs: arcs ? new internal.ArcCollection(arcs) : null
|
|
1836
|
-
};
|
|
1837
1787
|
}
|
|
1838
1788
|
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1789
|
+
function saveBlobToServer(path, blob, done) {
|
|
1790
|
+
var q = '?file=' + encodeURIComponent(path);
|
|
1791
|
+
var url = window.location.origin + '/save' + q;
|
|
1792
|
+
window.fetch(url, {
|
|
1793
|
+
method: 'POST',
|
|
1794
|
+
credentials: 'include',
|
|
1795
|
+
body: blob
|
|
1796
|
+
}).then(function(resp) {
|
|
1797
|
+
if (resp.status == 400) {
|
|
1798
|
+
return resp.text();
|
|
1799
|
+
}
|
|
1800
|
+
}).then(function(err) {
|
|
1801
|
+
done(err);
|
|
1802
|
+
}).catch(function(resp) {
|
|
1803
|
+
done('connection to server was lost');
|
|
1804
|
+
});
|
|
1849
1805
|
}
|
|
1850
1806
|
|
|
1851
|
-
function
|
|
1852
|
-
var
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
return
|
|
1807
|
+
function saveBlobToDownloadFolder(filename, blob, done) {
|
|
1808
|
+
var anchor, blobUrl;
|
|
1809
|
+
if (window.navigator.msSaveBlob) {
|
|
1810
|
+
window.navigator.msSaveBlob(blob, filename);
|
|
1811
|
+
return done();
|
|
1856
1812
|
}
|
|
1857
|
-
|
|
1858
|
-
var wgs84 = internal.getCRS('wgs84');
|
|
1859
|
-
var toWGS84 = internal.getProjTransform2(src, wgs84);
|
|
1860
|
-
var fromWGS84 = internal.getProjTransform2(wgs84, dest);
|
|
1861
|
-
|
|
1862
1813
|
try {
|
|
1863
|
-
|
|
1864
|
-
// (so vertex editing doesn't break)
|
|
1865
|
-
if (!internal.isWGS84(src)) {
|
|
1866
|
-
// use wgs84 as a pivot CRS, so we can handle polar coordinates
|
|
1867
|
-
// that can't be projected to Mercator
|
|
1868
|
-
internal.projectArcs(copy, toWGS84);
|
|
1869
|
-
}
|
|
1870
|
-
if (destIsWebMerc) {
|
|
1871
|
-
// handle polar points by clamping them to they will project
|
|
1872
|
-
// (downside: may cause unexpected behavior when editing vertices interactively)
|
|
1873
|
-
clampY(copy);
|
|
1874
|
-
}
|
|
1875
|
-
internal.projectArcs(copy, fromWGS84);
|
|
1814
|
+
blobUrl = URL.createObjectURL(blob);
|
|
1876
1815
|
} catch(e) {
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
// downside: projectArcs2 discards Z values and changes arc indexing,
|
|
1880
|
-
// which will break vertex editing.
|
|
1881
|
-
var reproject = internal.getProjTransform2(src, dest);
|
|
1882
|
-
copy = arcs.getCopy();
|
|
1883
|
-
internal.projectArcs2(copy, reproject);
|
|
1816
|
+
done("Mapshaper can't export files from this browser. Try switching to Chrome or Firefox.");
|
|
1817
|
+
return;
|
|
1884
1818
|
}
|
|
1885
|
-
|
|
1819
|
+
anchor = El('a').attr('href', '#').appendTo('body').node();
|
|
1820
|
+
anchor.href = blobUrl;
|
|
1821
|
+
anchor.download = filename;
|
|
1822
|
+
var clickEvent = document.createEvent("MouseEvent");
|
|
1823
|
+
clickEvent.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false,
|
|
1824
|
+
false, false, false, 0, null);
|
|
1825
|
+
anchor.dispatchEvent(clickEvent);
|
|
1826
|
+
setTimeout(function() {
|
|
1827
|
+
// Revoke blob url to release memory; timeout needed in firefox
|
|
1828
|
+
URL.revokeObjectURL(blobUrl);
|
|
1829
|
+
anchor.parentNode.removeChild(anchor);
|
|
1830
|
+
done();
|
|
1831
|
+
}, 400);
|
|
1886
1832
|
}
|
|
1887
1833
|
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
});
|
|
1897
|
-
}
|
|
1834
|
+
// replace default error, stop and message functions
|
|
1835
|
+
function setLoggingForGUI(gui) {
|
|
1836
|
+
function stop() {
|
|
1837
|
+
// Show a popup error message, then throw an error
|
|
1838
|
+
var msg = GUI.formatMessageArgs(arguments);
|
|
1839
|
+
gui.alert(msg);
|
|
1840
|
+
throw new Error(msg);
|
|
1841
|
+
}
|
|
1898
1842
|
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
copy.shapes = internal.cloneShapes(lyr.shapes);
|
|
1903
|
-
internal.projectPointLayer(copy, proj);
|
|
1904
|
-
return copy;
|
|
1905
|
-
}
|
|
1843
|
+
function error() {
|
|
1844
|
+
stop.apply(null, utils$1.toArray(arguments));
|
|
1845
|
+
}
|
|
1906
1846
|
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
var k = Math.cos(lat * D2R);
|
|
1911
|
-
var x = R * lng * D2R;
|
|
1912
|
-
var y = R * Math.log(Math.tan(Math.PI * 0.25 + lat * D2R * 0.5));
|
|
1913
|
-
return [x, y];
|
|
1914
|
-
}
|
|
1847
|
+
function message() {
|
|
1848
|
+
internal.logArgs(arguments);
|
|
1849
|
+
}
|
|
1915
1850
|
|
|
1916
|
-
|
|
1917
|
-
var R = 6378137;
|
|
1918
|
-
var R2D = 180 / Math.PI;
|
|
1919
|
-
var lon = x / R * R2D;
|
|
1920
|
-
var lat = R2D * (Math.PI * 0.5 - 2 * Math.atan(Math.exp(-y / R)));
|
|
1921
|
-
return [lon, lat];
|
|
1851
|
+
internal.setLoggingFunctions(message, error, stop);
|
|
1922
1852
|
}
|
|
1923
1853
|
|
|
1924
|
-
function
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1854
|
+
function WriteFilesProxy(gui) {
|
|
1855
|
+
// replace CLI version of writeFiles()
|
|
1856
|
+
internal.replaceWriteFiles(function(files, opts, done) {
|
|
1857
|
+
var filename;
|
|
1858
|
+
if (!utils$1.isArray(files) || files.length === 0) {
|
|
1859
|
+
done("Nothing to export");
|
|
1860
|
+
} else if (GUI.canSaveToServer() && !opts.save_to_download_folder) {
|
|
1861
|
+
var paths = internal.getOutputPaths(utils$1.pluck(files, 'filename'), opts);
|
|
1862
|
+
var data = utils$1.pluck(files, 'content');
|
|
1863
|
+
saveFilesToServer(paths, data, function(err) {
|
|
1864
|
+
var msg;
|
|
1865
|
+
if (err) {
|
|
1866
|
+
msg = "<b>Direct save failed</b><br>Reason: " + err + ".";
|
|
1867
|
+
msg += "<br>Saving to download folder instead.";
|
|
1868
|
+
gui.alert(msg);
|
|
1869
|
+
// fall back to standard method if saving to server fails
|
|
1870
|
+
internal.writeFiles(files, {save_to_download_folder: true}, done);
|
|
1871
|
+
} else {
|
|
1872
|
+
if (files.length >= 1) {
|
|
1873
|
+
gui.alert('<b>Saved</b><br>' + paths.join('<br>'));
|
|
1874
|
+
}
|
|
1875
|
+
done();
|
|
1876
|
+
}
|
|
1877
|
+
});
|
|
1878
|
+
} else if (files.length == 1) {
|
|
1879
|
+
saveBlobToDownloadFolder(files[0].filename, new Blob([files[0].content]), done);
|
|
1880
|
+
} else {
|
|
1881
|
+
filename = internal.getCommonFileBase(utils$1.pluck(files, 'filename')) || "output";
|
|
1882
|
+
saveZipFile(filename + ".zip", files, done);
|
|
1883
|
+
}
|
|
1884
|
+
});
|
|
1932
1885
|
}
|
|
1933
1886
|
|
|
1887
|
+
// Replaces functions for reading from files with functions that try to match
|
|
1888
|
+
// already-loaded datasets.
|
|
1889
|
+
//
|
|
1890
|
+
function ImportFileProxy(gui) {
|
|
1891
|
+
var model = gui.model;
|
|
1934
1892
|
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1893
|
+
// Try to match an imported dataset or layer.
|
|
1894
|
+
// TODO: think about handling import options
|
|
1895
|
+
function find(src) {
|
|
1896
|
+
var datasets = model.getDatasets();
|
|
1897
|
+
var retn = datasets.reduce(function(memo, d) {
|
|
1898
|
+
var lyr;
|
|
1899
|
+
if (memo) return memo; // already found a match
|
|
1900
|
+
// try to match import filename of this dataset
|
|
1901
|
+
if (d.info.input_files[0] == src) return d;
|
|
1902
|
+
// try to match name of a layer in this dataset
|
|
1903
|
+
lyr = utils$1.find(d.layers, function(lyr) {return lyr.name == src;});
|
|
1904
|
+
return lyr ? internal.isolateLayer(lyr, d) : null;
|
|
1905
|
+
}, null);
|
|
1906
|
+
if (!retn) stop$1("Missing data layer [" + src + "]");
|
|
1907
|
+
return retn;
|
|
1943
1908
|
}
|
|
1944
1909
|
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
newCP = proj(oldBounds.centerX(), oldBounds.centerY());
|
|
1955
|
-
ext.setBounds(newBounds);
|
|
1956
|
-
if (!newCP) {
|
|
1957
|
-
// projection of center point failed; use center of bounds
|
|
1958
|
-
// (also consider just resetting the view using ext.home())
|
|
1959
|
-
newCP = [newBounds.centerX(), newBounds.centerY()];
|
|
1960
|
-
}
|
|
1961
|
-
ext.recenter(newCP[0], newCP[1], oldScale);
|
|
1962
|
-
}
|
|
1910
|
+
internal.replaceImportFile(function(src, opts) {
|
|
1911
|
+
var dataset = find(src);
|
|
1912
|
+
// Return a copy with layers duplicated, so changes won't affect original layers
|
|
1913
|
+
// This makes an (unsafe) assumption that the dataset arcs won't be changed...
|
|
1914
|
+
// need to rethink this.
|
|
1915
|
+
return utils$1.defaults({
|
|
1916
|
+
layers: dataset.layers.map(internal.copyLayer)
|
|
1917
|
+
}, dataset);
|
|
1918
|
+
});
|
|
1963
1919
|
}
|
|
1964
1920
|
|
|
1965
|
-
//
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
}
|
|
1921
|
+
// load Proj.4 CRS definition files dynamically
|
|
1922
|
+
//
|
|
1923
|
+
internal.setProjectionLoader(function(opts, done) {
|
|
1924
|
+
var mproj = require('mproj');
|
|
1925
|
+
var libs = internal.findProjLibs([opts.init || '', opts.match || '', opts.crs || ''].join(' '));
|
|
1926
|
+
// skip loaded libs
|
|
1927
|
+
libs = libs.filter(function(name) {return !mproj.internal.mproj_search_libcache(name);});
|
|
1928
|
+
loadProjLibs(libs, done);
|
|
1929
|
+
});
|
|
1930
|
+
|
|
1931
|
+
function loadProjLibs(libs, done) {
|
|
1932
|
+
var mproj = require('mproj');
|
|
1933
|
+
var i = 0;
|
|
1934
|
+
next();
|
|
1974
1935
|
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1936
|
+
function next() {
|
|
1937
|
+
var libName = libs[i];
|
|
1938
|
+
var content, req;
|
|
1939
|
+
if (!libName) return done();
|
|
1940
|
+
req = new XMLHttpRequest();
|
|
1941
|
+
req.addEventListener('load', function(e) {
|
|
1942
|
+
if (req.status == 200) {
|
|
1943
|
+
content = req.response;
|
|
1944
|
+
}
|
|
1980
1945
|
});
|
|
1981
|
-
|
|
1946
|
+
req.addEventListener('loadend', function() {
|
|
1947
|
+
if (content) {
|
|
1948
|
+
mproj.internal.mproj_insert_libcache(libName, content);
|
|
1949
|
+
}
|
|
1950
|
+
// TODO: consider stopping with an error message if no content was loaded
|
|
1951
|
+
// (currently, a less specific error will occur when mapshaper tries to use the library)
|
|
1952
|
+
next();
|
|
1953
|
+
});
|
|
1954
|
+
req.open('GET', 'assets/' + libName);
|
|
1955
|
+
req.send();
|
|
1956
|
+
i++;
|
|
1982
1957
|
}
|
|
1983
|
-
return lyr;
|
|
1984
|
-
}
|
|
1985
|
-
|
|
1986
|
-
function formatLayerNameForDisplay(name) {
|
|
1987
|
-
return name || '[unnamed]';
|
|
1988
|
-
}
|
|
1989
|
-
|
|
1990
|
-
function cleanLayerName(raw) {
|
|
1991
|
-
return raw.replace(/[\n\t/\\]/g, '')
|
|
1992
|
-
.replace(/^[\.\s]+/, '').replace(/[\.\s]+$/, '');
|
|
1993
1958
|
}
|
|
1994
1959
|
|
|
1995
|
-
function
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
1960
|
+
function getDatasetCrsInfo(dataset) {
|
|
1961
|
+
var revertLogging = internal.getLoggingSetter();
|
|
1962
|
+
var crs, err;
|
|
1963
|
+
// prevent GUI message popup on error
|
|
1964
|
+
internal.setLoggingForCLI();
|
|
1965
|
+
try {
|
|
1966
|
+
crs = internal.getDatasetCRS(dataset);
|
|
1967
|
+
} catch(e) {
|
|
1968
|
+
err = e.message;
|
|
1969
|
+
}
|
|
1970
|
+
revertLogging();
|
|
1971
|
+
return {
|
|
1972
|
+
crs: crs,
|
|
1973
|
+
error: err
|
|
1974
|
+
};
|
|
2009
1975
|
}
|
|
2010
1976
|
|
|
2011
|
-
function
|
|
2012
|
-
|
|
2013
|
-
|
|
1977
|
+
function flattenArcs(lyr) {
|
|
1978
|
+
lyr.source.dataset.arcs.flatten();
|
|
1979
|
+
if (isProjectedLayer(lyr)) {
|
|
1980
|
+
lyr.arcs.flatten();
|
|
1981
|
+
}
|
|
2014
1982
|
}
|
|
2015
1983
|
|
|
2016
1984
|
function setZ(lyr, z) {
|
|
@@ -2106,146 +2074,6 @@
|
|
|
2106
2074
|
return dest.length ? dest : null;
|
|
2107
2075
|
}
|
|
2108
2076
|
|
|
2109
|
-
// displayCRS: CRS to use for display, or null (which clears any current display CRS)
|
|
2110
|
-
function projectDisplayLayer(lyr, displayCRS) {
|
|
2111
|
-
var sourceCRS = internal.getDatasetCRS(lyr.source.dataset);
|
|
2112
|
-
var lyr2;
|
|
2113
|
-
if (!lyr.geographic || !sourceCRS) {
|
|
2114
|
-
return lyr;
|
|
2115
|
-
}
|
|
2116
|
-
if (lyr.dynamic_crs && internal.crsAreEqual(sourceCRS, lyr.dynamic_crs)) {
|
|
2117
|
-
return lyr;
|
|
2118
|
-
}
|
|
2119
|
-
lyr2 = getDisplayLayer(lyr.source.layer, lyr.source.dataset, {crs: displayCRS});
|
|
2120
|
-
// kludge: copy projection-related properties to original layer
|
|
2121
|
-
lyr.dynamic_crs = lyr2.dynamic_crs;
|
|
2122
|
-
lyr.layer = lyr2.layer;
|
|
2123
|
-
if (lyr.style && lyr.style.ids) {
|
|
2124
|
-
// re-apply layer filter
|
|
2125
|
-
lyr.layer = filterLayerByIds(lyr.layer, lyr.style.ids);
|
|
2126
|
-
}
|
|
2127
|
-
lyr.invertPoint = lyr2.invertPoint;
|
|
2128
|
-
lyr.projectPoint = lyr2.projectPoint;
|
|
2129
|
-
lyr.bounds = lyr2.bounds;
|
|
2130
|
-
lyr.arcs = lyr2.arcs;
|
|
2131
|
-
}
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
// Wrap a layer in an object along with information needed for rendering
|
|
2135
|
-
function getDisplayLayer(layer, dataset, opts) {
|
|
2136
|
-
var obj = {
|
|
2137
|
-
layer: null,
|
|
2138
|
-
arcs: null,
|
|
2139
|
-
// display_arcs: null,
|
|
2140
|
-
style: null,
|
|
2141
|
-
invertPoint: null,
|
|
2142
|
-
projectPoint: null,
|
|
2143
|
-
source: {
|
|
2144
|
-
layer: layer,
|
|
2145
|
-
dataset: dataset
|
|
2146
|
-
},
|
|
2147
|
-
empty: internal.getFeatureCount(layer) === 0
|
|
2148
|
-
};
|
|
2149
|
-
|
|
2150
|
-
var sourceCRS = opts.crs && internal.getDatasetCRS(dataset); // get src iff display CRS is given
|
|
2151
|
-
var displayCRS = opts.crs || null;
|
|
2152
|
-
// display arcs may have been generated when another layer in the dataset was converted for display... re-use if available
|
|
2153
|
-
var displayArcs = dataset.displayArcs || null;
|
|
2154
|
-
var emptyArcs;
|
|
2155
|
-
|
|
2156
|
-
// Assume that dataset.displayArcs is in the display CRS
|
|
2157
|
-
// (it must be deleted upstream if reprojection is needed)
|
|
2158
|
-
if (dataset.arcs && !displayArcs) {
|
|
2159
|
-
// project arcs, if needed
|
|
2160
|
-
if (needReprojectionForDisplay(sourceCRS, displayCRS)) {
|
|
2161
|
-
displayArcs = projectArcsForDisplay(dataset.arcs, sourceCRS, displayCRS);
|
|
2162
|
-
} else {
|
|
2163
|
-
// Use original arcs for display if there is no dynamic reprojection
|
|
2164
|
-
displayArcs = dataset.arcs;
|
|
2165
|
-
}
|
|
2166
|
-
|
|
2167
|
-
enhanceArcCollectionForDisplay(displayArcs);
|
|
2168
|
-
dataset.displayArcs = displayArcs; // stash these in the dataset for other layers to use
|
|
2169
|
-
}
|
|
2170
|
-
|
|
2171
|
-
if (internal.layerHasFurniture(layer)) {
|
|
2172
|
-
obj.furniture = true;
|
|
2173
|
-
obj.furniture_type = internal.getFurnitureLayerType(layer);
|
|
2174
|
-
obj.layer = layer;
|
|
2175
|
-
// treating furniture layers (other than frame) as tabular for now,
|
|
2176
|
-
// so there is something to show if they are selected
|
|
2177
|
-
obj.tabular = obj.furniture_type != 'frame';
|
|
2178
|
-
} else if (obj.empty) {
|
|
2179
|
-
obj.layer = {shapes: []}; // ideally we should avoid empty layers
|
|
2180
|
-
} else if (!layer.geometry_type) {
|
|
2181
|
-
obj.tabular = true;
|
|
2182
|
-
} else {
|
|
2183
|
-
obj.geographic = true;
|
|
2184
|
-
obj.layer = layer;
|
|
2185
|
-
obj.arcs = displayArcs;
|
|
2186
|
-
}
|
|
2187
|
-
|
|
2188
|
-
if (obj.tabular) {
|
|
2189
|
-
utils$1.extend(obj, getDisplayLayerForTable(layer.data));
|
|
2190
|
-
}
|
|
2191
|
-
|
|
2192
|
-
// dynamic reprojection (arcs were already reprojected above)
|
|
2193
|
-
if (obj.geographic && needReprojectionForDisplay(sourceCRS, displayCRS)) {
|
|
2194
|
-
obj.dynamic_crs = displayCRS;
|
|
2195
|
-
obj.invertPoint = internal.getProjTransform2(displayCRS, sourceCRS);
|
|
2196
|
-
obj.projectPoint = internal.getProjTransform2(sourceCRS, displayCRS);
|
|
2197
|
-
if (internal.layerHasPoints(layer)) {
|
|
2198
|
-
obj.layer = projectPointsForDisplay(layer, sourceCRS, displayCRS);
|
|
2199
|
-
} else if (internal.layerHasPaths(layer)) {
|
|
2200
|
-
emptyArcs = findEmptyArcs(displayArcs);
|
|
2201
|
-
if (emptyArcs.length > 0) {
|
|
2202
|
-
// Don't try to draw paths containing coordinates that failed to project
|
|
2203
|
-
obj.layer = internal.filterPathLayerByArcIds(obj.layer, emptyArcs);
|
|
2204
|
-
}
|
|
2205
|
-
}
|
|
2206
|
-
}
|
|
2207
|
-
|
|
2208
|
-
obj.bounds = getDisplayBounds(obj.layer, obj.arcs);
|
|
2209
|
-
return obj;
|
|
2210
|
-
}
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
function getDisplayBounds(lyr, arcs) {
|
|
2214
|
-
var arcBounds = arcs ? arcs.getBounds() : new Bounds(),
|
|
2215
|
-
bounds = arcBounds, // default display extent: all arcs in the dataset
|
|
2216
|
-
lyrBounds;
|
|
2217
|
-
|
|
2218
|
-
if (lyr.geometry_type == 'point') {
|
|
2219
|
-
lyrBounds = internal.getLayerBounds(lyr);
|
|
2220
|
-
if (lyrBounds && lyrBounds.hasBounds()) {
|
|
2221
|
-
if (lyrBounds.area() > 0 || !arcBounds.hasBounds()) {
|
|
2222
|
-
bounds = lyrBounds;
|
|
2223
|
-
} else {
|
|
2224
|
-
// if a point layer has no extent (e.g. contains only a single point),
|
|
2225
|
-
// then merge with arc bounds, to place the point in context.
|
|
2226
|
-
bounds = arcBounds.mergeBounds(lyrBounds);
|
|
2227
|
-
}
|
|
2228
|
-
}
|
|
2229
|
-
}
|
|
2230
|
-
|
|
2231
|
-
if (!bounds || !bounds.hasBounds()) { // empty layer
|
|
2232
|
-
bounds = new Bounds();
|
|
2233
|
-
}
|
|
2234
|
-
return bounds;
|
|
2235
|
-
}
|
|
2236
|
-
|
|
2237
|
-
// Returns an array of ids of empty arcs (arcs can be set to empty if errors occur while projecting them)
|
|
2238
|
-
function findEmptyArcs(arcs) {
|
|
2239
|
-
var nn = arcs.getVertexData().nn;
|
|
2240
|
-
var ids = [];
|
|
2241
|
-
for (var i=0, n=nn.length; i<n; i++) {
|
|
2242
|
-
if (nn[i] === 0) {
|
|
2243
|
-
ids.push(i);
|
|
2244
|
-
}
|
|
2245
|
-
}
|
|
2246
|
-
return ids;
|
|
2247
|
-
}
|
|
2248
|
-
|
|
2249
2077
|
/*
|
|
2250
2078
|
How changes in the simplify control should affect other components
|
|
2251
2079
|
|
|
@@ -2481,225 +2309,141 @@
|
|
|
2481
2309
|
}
|
|
2482
2310
|
};
|
|
2483
2311
|
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
}
|
|
2494
|
-
|
|
2312
|
+
var R = 6378137;
|
|
2313
|
+
var D2R = Math.PI / 180;
|
|
2314
|
+
var R2D = 180 / Math.PI;
|
|
2315
|
+
|
|
2316
|
+
// Assumes projections are available
|
|
2317
|
+
|
|
2318
|
+
function needReprojectionForDisplay(sourceCRS, displayCRS) {
|
|
2319
|
+
if (!sourceCRS || !displayCRS) {
|
|
2320
|
+
return false;
|
|
2321
|
+
}
|
|
2322
|
+
if (internal.crsAreEqual(sourceCRS, displayCRS)) {
|
|
2323
|
+
return false;
|
|
2495
2324
|
}
|
|
2325
|
+
return true;
|
|
2326
|
+
}
|
|
2496
2327
|
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
msg = err.message;
|
|
2503
|
-
}
|
|
2504
|
-
if (msg) {
|
|
2505
|
-
str += ": " + msg;
|
|
2506
|
-
}
|
|
2507
|
-
done(str);
|
|
2328
|
+
function projectArcsForDisplay(arcs, src, dest) {
|
|
2329
|
+
var copy = arcs.getCopy(); // need to flatten first?
|
|
2330
|
+
var destIsWebMerc = internal.isWebMercator(dest);
|
|
2331
|
+
if (destIsWebMerc && internal.isWebMercator(src)) {
|
|
2332
|
+
return copy;
|
|
2508
2333
|
}
|
|
2509
2334
|
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2335
|
+
var wgs84 = internal.getCRS('wgs84');
|
|
2336
|
+
var toWGS84 = internal.getProjTransform2(src, wgs84);
|
|
2337
|
+
var fromWGS84 = internal.getProjTransform2(wgs84, dest);
|
|
2338
|
+
|
|
2339
|
+
try {
|
|
2340
|
+
// first try projectArcs() -- it's fast and preserves arc ids
|
|
2341
|
+
// (so vertex editing doesn't break)
|
|
2342
|
+
if (!internal.isWGS84(src)) {
|
|
2343
|
+
// use wgs84 as a pivot CRS, so we can handle polar coordinates
|
|
2344
|
+
// that can't be projected to Mercator
|
|
2345
|
+
internal.projectArcs(copy, toWGS84);
|
|
2519
2346
|
}
|
|
2347
|
+
if (destIsWebMerc) {
|
|
2348
|
+
// handle polar points by clamping them to they will project
|
|
2349
|
+
// (downside: may cause unexpected behavior when editing vertices interactively)
|
|
2350
|
+
clampY(copy);
|
|
2351
|
+
}
|
|
2352
|
+
internal.projectArcs(copy, fromWGS84);
|
|
2353
|
+
} catch(e) {
|
|
2354
|
+
console.error(e);
|
|
2355
|
+
// use the more robust projectArcs2 if projectArcs throws an error
|
|
2356
|
+
// downside: projectArcs2 discards Z values and changes arc indexing,
|
|
2357
|
+
// which will break vertex editing.
|
|
2358
|
+
var reproject = internal.getProjTransform2(src, dest);
|
|
2359
|
+
copy = arcs.getCopy();
|
|
2360
|
+
internal.projectArcs2(copy, reproject);
|
|
2520
2361
|
}
|
|
2362
|
+
return copy;
|
|
2521
2363
|
}
|
|
2522
2364
|
|
|
2523
|
-
function
|
|
2524
|
-
var
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
if (
|
|
2530
|
-
|
|
2531
|
-
}
|
|
2532
|
-
}
|
|
2533
|
-
|
|
2534
|
-
function saveBlobToServer(path, blob, done) {
|
|
2535
|
-
var q = '?file=' + encodeURIComponent(path);
|
|
2536
|
-
var url = window.location.origin + '/save' + q;
|
|
2537
|
-
window.fetch(url, {
|
|
2538
|
-
method: 'POST',
|
|
2539
|
-
credentials: 'include',
|
|
2540
|
-
body: blob
|
|
2541
|
-
}).then(function(resp) {
|
|
2542
|
-
if (resp.status == 400) {
|
|
2543
|
-
return resp.text();
|
|
2544
|
-
}
|
|
2545
|
-
}).then(function(err) {
|
|
2546
|
-
done(err);
|
|
2547
|
-
}).catch(function(resp) {
|
|
2548
|
-
done('connection to server was lost');
|
|
2365
|
+
function clampY(arcs) {
|
|
2366
|
+
var max = 89.9,
|
|
2367
|
+
min = -89.9,
|
|
2368
|
+
bbox = arcs.getBounds().toArray();
|
|
2369
|
+
if (bbox[1] >= min && bbox[3] <= max) return;
|
|
2370
|
+
arcs.transformPoints(function(x, y) {
|
|
2371
|
+
if (y > max) return [x, max];
|
|
2372
|
+
if (y < min) return [x, min];
|
|
2549
2373
|
});
|
|
2550
2374
|
}
|
|
2551
2375
|
|
|
2552
|
-
function
|
|
2553
|
-
var
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
try {
|
|
2559
|
-
blobUrl = URL.createObjectURL(blob);
|
|
2560
|
-
} catch(e) {
|
|
2561
|
-
done("Mapshaper can't export files from this browser. Try switching to Chrome or Firefox.");
|
|
2562
|
-
return;
|
|
2563
|
-
}
|
|
2564
|
-
anchor = El('a').attr('href', '#').appendTo('body').node();
|
|
2565
|
-
anchor.href = blobUrl;
|
|
2566
|
-
anchor.download = filename;
|
|
2567
|
-
var clickEvent = document.createEvent("MouseEvent");
|
|
2568
|
-
clickEvent.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false,
|
|
2569
|
-
false, false, false, 0, null);
|
|
2570
|
-
anchor.dispatchEvent(clickEvent);
|
|
2571
|
-
setTimeout(function() {
|
|
2572
|
-
// Revoke blob url to release memory; timeout needed in firefox
|
|
2573
|
-
URL.revokeObjectURL(blobUrl);
|
|
2574
|
-
anchor.parentNode.removeChild(anchor);
|
|
2575
|
-
done();
|
|
2576
|
-
}, 400);
|
|
2376
|
+
function projectPointsForDisplay(lyr, src, dest) {
|
|
2377
|
+
var copy = utils$1.extend({}, lyr);
|
|
2378
|
+
var proj = internal.getProjTransform2(src, dest);
|
|
2379
|
+
copy.shapes = internal.cloneShapes(lyr.shapes);
|
|
2380
|
+
internal.projectPointLayer(copy, proj);
|
|
2381
|
+
return copy;
|
|
2577
2382
|
}
|
|
2578
2383
|
|
|
2579
|
-
function MessageProxy(gui) {
|
|
2580
|
-
// replace stop function
|
|
2581
|
-
var stop = function() {
|
|
2582
|
-
// Show a popup error message, then throw an error
|
|
2583
|
-
var msg = GUI.formatMessageArgs(arguments);
|
|
2584
|
-
gui.alert(msg);
|
|
2585
|
-
throw new Error(msg);
|
|
2586
|
-
};
|
|
2587
2384
|
|
|
2588
|
-
|
|
2589
|
-
var
|
|
2590
|
-
|
|
2591
|
-
|
|
2385
|
+
function toWebMercator(lng, lat) {
|
|
2386
|
+
var k = Math.cos(lat * D2R);
|
|
2387
|
+
var x = R * lng * D2R;
|
|
2388
|
+
var y = R * Math.log(Math.tan(Math.PI * 0.25 + lat * D2R * 0.5));
|
|
2389
|
+
return [x, y];
|
|
2390
|
+
}
|
|
2592
2391
|
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2392
|
+
function fromWebMercator(x, y) {
|
|
2393
|
+
var lon = x / R * R2D;
|
|
2394
|
+
var lat = R2D * (Math.PI * 0.5 - 2 * Math.atan(Math.exp(-y / R)));
|
|
2395
|
+
return [lon, lat];
|
|
2396
|
+
}
|
|
2596
2397
|
|
|
2597
|
-
|
|
2398
|
+
function scaleToZoom(metersPerPix) {
|
|
2399
|
+
return Math.log(40075017 / 512 / metersPerPix) / Math.log(2);
|
|
2598
2400
|
}
|
|
2599
2401
|
|
|
2600
|
-
function
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
if (!utils$1.isArray(files) || files.length === 0) {
|
|
2605
|
-
done("Nothing to export");
|
|
2606
|
-
} else if (GUI.canSaveToServer() && !opts.save_to_download_folder) {
|
|
2607
|
-
var paths = internal.getOutputPaths(utils$1.pluck(files, 'filename'), opts);
|
|
2608
|
-
var data = utils$1.pluck(files, 'content');
|
|
2609
|
-
saveFilesToServer(paths, data, function(err) {
|
|
2610
|
-
var msg;
|
|
2611
|
-
if (err) {
|
|
2612
|
-
msg = "<b>Direct save failed</b><br>Reason: " + err + ".";
|
|
2613
|
-
msg += "<br>Saving to download folder instead.";
|
|
2614
|
-
gui.alert(msg);
|
|
2615
|
-
// fall back to standard method if saving to server fails
|
|
2616
|
-
internal.writeFiles(files, {save_to_download_folder: true}, done);
|
|
2617
|
-
} else {
|
|
2618
|
-
if (files.length >= 1) {
|
|
2619
|
-
gui.alert('<b>Saved</b><br>' + paths.join('<br>'));
|
|
2620
|
-
}
|
|
2621
|
-
done();
|
|
2622
|
-
}
|
|
2623
|
-
});
|
|
2624
|
-
} else if (files.length == 1) {
|
|
2625
|
-
saveBlobToDownloadFolder(files[0].filename, new Blob([files[0].content]), done);
|
|
2626
|
-
} else {
|
|
2627
|
-
filename = internal.getCommonFileBase(utils$1.pluck(files, 'filename')) || "output";
|
|
2628
|
-
saveZipFile(filename + ".zip", files, done);
|
|
2629
|
-
}
|
|
2630
|
-
});
|
|
2402
|
+
function getMapboxBounds() {
|
|
2403
|
+
var ymax = toWebMercator(0, 84)[1];
|
|
2404
|
+
var ymin = toWebMercator(0, -84)[1];
|
|
2405
|
+
return [-Infinity, ymin, Infinity, ymax];
|
|
2631
2406
|
}
|
|
2632
2407
|
|
|
2633
|
-
// Replaces functions for reading from files with functions that try to match
|
|
2634
|
-
// already-loaded datasets.
|
|
2635
|
-
//
|
|
2636
|
-
function ImportFileProxy(gui) {
|
|
2637
|
-
var model = gui.model;
|
|
2638
2408
|
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
// try to match name of a layer in this dataset
|
|
2649
|
-
lyr = utils$1.find(d.layers, function(lyr) {return lyr.name == src;});
|
|
2650
|
-
return lyr ? internal.isolateLayer(lyr, d) : null;
|
|
2651
|
-
}, null);
|
|
2652
|
-
if (!retn) stop$1("Missing data layer [" + src + "]");
|
|
2653
|
-
return retn;
|
|
2409
|
+
// Update map extent and trigger redraw, after a new display CRS has been applied
|
|
2410
|
+
function projectMapExtent(ext, src, dest, newBounds) {
|
|
2411
|
+
var oldBounds = ext.getBounds();
|
|
2412
|
+
var oldScale = ext.scale();
|
|
2413
|
+
var newCP, proj, strictBounds;
|
|
2414
|
+
|
|
2415
|
+
if (dest && internal.isWebMercator(dest)) {
|
|
2416
|
+
// clampToMapboxBounds(newBounds);
|
|
2417
|
+
strictBounds = getMapboxBounds();
|
|
2654
2418
|
}
|
|
2655
2419
|
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2420
|
+
// if source or destination CRS is unknown, show full extent
|
|
2421
|
+
// if map is at full extent, show full extent
|
|
2422
|
+
// TODO: handle case that scale is 1 and map is panned away from center
|
|
2423
|
+
if (ext.scale() == 1 || !dest) {
|
|
2424
|
+
ext.setBounds(newBounds, strictBounds);
|
|
2425
|
+
ext.home(); // sets full extent and triggers redraw
|
|
2426
|
+
} else {
|
|
2427
|
+
// if map is zoomed, stay centered on the same geographic location, at the same relative scale
|
|
2428
|
+
proj = internal.getProjTransform2(src, dest);
|
|
2429
|
+
newCP = proj(oldBounds.centerX(), oldBounds.centerY());
|
|
2430
|
+
ext.setBounds(newBounds, strictBounds);
|
|
2431
|
+
if (!newCP) {
|
|
2432
|
+
// projection of center point failed; use center of bounds
|
|
2433
|
+
// (also consider just resetting the view using ext.home())
|
|
2434
|
+
newCP = [newBounds.centerX(), newBounds.centerY()];
|
|
2435
|
+
}
|
|
2436
|
+
ext.recenter(newCP[0], newCP[1], oldScale);
|
|
2437
|
+
}
|
|
2665
2438
|
}
|
|
2666
2439
|
|
|
2667
|
-
//
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
loadProjLibs(libs, done);
|
|
2675
|
-
});
|
|
2676
|
-
|
|
2677
|
-
function loadProjLibs(libs, done) {
|
|
2678
|
-
var mproj = require('mproj');
|
|
2679
|
-
var i = 0;
|
|
2680
|
-
next();
|
|
2681
|
-
|
|
2682
|
-
function next() {
|
|
2683
|
-
var libName = libs[i];
|
|
2684
|
-
var content, req;
|
|
2685
|
-
if (!libName) return done();
|
|
2686
|
-
req = new XMLHttpRequest();
|
|
2687
|
-
req.addEventListener('load', function(e) {
|
|
2688
|
-
if (req.status == 200) {
|
|
2689
|
-
content = req.response;
|
|
2690
|
-
}
|
|
2691
|
-
});
|
|
2692
|
-
req.addEventListener('loadend', function() {
|
|
2693
|
-
if (content) {
|
|
2694
|
-
mproj.internal.mproj_insert_libcache(libName, content);
|
|
2695
|
-
}
|
|
2696
|
-
// TODO: consider stopping with an error message if no content was loaded
|
|
2697
|
-
// (currently, a less specific error will occur when mapshaper tries to use the library)
|
|
2698
|
-
next();
|
|
2699
|
-
});
|
|
2700
|
-
req.open('GET', 'assets/' + libName);
|
|
2701
|
-
req.send();
|
|
2702
|
-
i++;
|
|
2440
|
+
// Called from console; for testing dynamic crs
|
|
2441
|
+
function setDisplayProjection(gui, cmd) {
|
|
2442
|
+
var arg = cmd.replace(/^projd[ ]*/, '');
|
|
2443
|
+
if (arg) {
|
|
2444
|
+
gui.map.setDisplayCRS(internal.getCRS(arg));
|
|
2445
|
+
} else {
|
|
2446
|
+
gui.map.setDisplayCRS(null);
|
|
2703
2447
|
}
|
|
2704
2448
|
}
|
|
2705
2449
|
|
|
@@ -2799,7 +2543,7 @@
|
|
|
2799
2543
|
btn.removeClass('active');
|
|
2800
2544
|
_isOpen = false;
|
|
2801
2545
|
if (GUI.isActiveInstance(gui)) {
|
|
2802
|
-
|
|
2546
|
+
setLoggingForGUI(gui); // reset stop, message and error functions
|
|
2803
2547
|
}
|
|
2804
2548
|
el.hide();
|
|
2805
2549
|
input.node().blur();
|
|
@@ -3341,7 +3085,48 @@
|
|
|
3341
3085
|
}
|
|
3342
3086
|
}
|
|
3343
3087
|
|
|
3344
|
-
utils$1.inherit(RepairControl, EventDispatcher);
|
|
3088
|
+
utils$1.inherit(RepairControl, EventDispatcher);
|
|
3089
|
+
|
|
3090
|
+
function filterLayerByIds(lyr, ids) {
|
|
3091
|
+
var shapes;
|
|
3092
|
+
if (lyr.shapes) {
|
|
3093
|
+
shapes = ids.map(function(id) {
|
|
3094
|
+
return lyr.shapes[id];
|
|
3095
|
+
});
|
|
3096
|
+
return utils$1.defaults({shapes: shapes, data: null}, lyr);
|
|
3097
|
+
}
|
|
3098
|
+
return lyr;
|
|
3099
|
+
}
|
|
3100
|
+
|
|
3101
|
+
function formatLayerNameForDisplay(name) {
|
|
3102
|
+
return name || '[unnamed]';
|
|
3103
|
+
}
|
|
3104
|
+
|
|
3105
|
+
function cleanLayerName(raw) {
|
|
3106
|
+
return raw.replace(/[\n\t/\\]/g, '')
|
|
3107
|
+
.replace(/^[\.\s]+/, '').replace(/[\.\s]+$/, '');
|
|
3108
|
+
}
|
|
3109
|
+
|
|
3110
|
+
function updateLayerStackOrder(layers) {
|
|
3111
|
+
// 1. assign ascending ids to unassigned layers above the range of other layers
|
|
3112
|
+
layers.forEach(function(o, i) {
|
|
3113
|
+
if (!o.layer.stack_id) o.layer.stack_id = 1e6 + i;
|
|
3114
|
+
});
|
|
3115
|
+
// 2. sort in ascending order
|
|
3116
|
+
layers.sort(function(a, b) {
|
|
3117
|
+
return a.layer.stack_id - b.layer.stack_id;
|
|
3118
|
+
});
|
|
3119
|
+
// 3. assign consecutve ids
|
|
3120
|
+
layers.forEach(function(o, i) {
|
|
3121
|
+
o.layer.stack_id = i + 1;
|
|
3122
|
+
});
|
|
3123
|
+
return layers;
|
|
3124
|
+
}
|
|
3125
|
+
|
|
3126
|
+
function sortLayersForMenuDisplay(layers) {
|
|
3127
|
+
layers = updateLayerStackOrder(layers);
|
|
3128
|
+
return layers.reverse();
|
|
3129
|
+
}
|
|
3345
3130
|
|
|
3346
3131
|
// import { groupLayersByDataset } from '../dataset/mapshaper-target-utils';
|
|
3347
3132
|
|
|
@@ -4406,6 +4191,10 @@
|
|
|
4406
4191
|
setMode('off');
|
|
4407
4192
|
};
|
|
4408
4193
|
|
|
4194
|
+
this.modeUsesPopup = function(mode) {
|
|
4195
|
+
return ['info', 'selection', 'data', 'box', 'labels', 'location'].includes(mode);
|
|
4196
|
+
};
|
|
4197
|
+
|
|
4409
4198
|
this.getMode = getInteractionMode;
|
|
4410
4199
|
|
|
4411
4200
|
this.setMode = function(mode) {
|
|
@@ -4656,24 +4445,37 @@
|
|
|
4656
4445
|
|
|
4657
4446
|
var LOGGING = false;
|
|
4658
4447
|
var STDOUT = false; // use stdout for status messages
|
|
4659
|
-
|
|
4660
|
-
// These three functions can be reset by GUI using setLoggingFunctions();
|
|
4661
|
-
var _error = function() {
|
|
4662
|
-
var msg = utils.toArray(arguments).join(' ');
|
|
4663
|
-
throw new Error(msg);
|
|
4664
|
-
};
|
|
4665
|
-
|
|
4666
|
-
var _stop = function() {
|
|
4667
|
-
throw new UserError(formatLogArgs(arguments));
|
|
4668
|
-
};
|
|
4448
|
+
var _error, _stop, _message;
|
|
4669
4449
|
|
|
4670
4450
|
var _interrupt = function() {
|
|
4671
4451
|
throw new NonFatalError(formatLogArgs(arguments));
|
|
4672
4452
|
};
|
|
4673
4453
|
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4454
|
+
setLoggingForCLI();
|
|
4455
|
+
|
|
4456
|
+
function getLoggingSetter() {
|
|
4457
|
+
var e = _error, s = _stop, m = _message;
|
|
4458
|
+
return function() {
|
|
4459
|
+
setLoggingFunctions(m, e, s);
|
|
4460
|
+
};
|
|
4461
|
+
}
|
|
4462
|
+
|
|
4463
|
+
function setLoggingForCLI() {
|
|
4464
|
+
function stop() {
|
|
4465
|
+
throw new UserError(formatLogArgs(arguments));
|
|
4466
|
+
}
|
|
4467
|
+
|
|
4468
|
+
function error() {
|
|
4469
|
+
var msg = utils.toArray(arguments).join(' ');
|
|
4470
|
+
throw new Error(msg);
|
|
4471
|
+
}
|
|
4472
|
+
|
|
4473
|
+
function message() {
|
|
4474
|
+
logArgs(arguments);
|
|
4475
|
+
}
|
|
4476
|
+
|
|
4477
|
+
setLoggingFunctions(message, error, stop);
|
|
4478
|
+
}
|
|
4677
4479
|
|
|
4678
4480
|
function enableLogging() {
|
|
4679
4481
|
LOGGING = true;
|
|
@@ -6395,7 +6197,7 @@
|
|
|
6395
6197
|
}
|
|
6396
6198
|
};
|
|
6397
6199
|
|
|
6398
|
-
self.setHoverVertex = function(p) {
|
|
6200
|
+
self.setHoverVertex = function(p, type) {
|
|
6399
6201
|
var p2 = storedData.hit_coordinates;
|
|
6400
6202
|
if (!active || !p) return;
|
|
6401
6203
|
if (p2 && p2[0] == p[0] && p2[1] == p[1]) return;
|
|
@@ -7616,7 +7418,7 @@
|
|
|
7616
7418
|
var _self = new EventDispatcher();
|
|
7617
7419
|
|
|
7618
7420
|
gui.on('interaction_mode_change', function(e) {
|
|
7619
|
-
if (e.mode
|
|
7421
|
+
if (!gui.interaction.modeUsesPopup(e.mode)) {
|
|
7620
7422
|
inspect(-1); // clear the popup
|
|
7621
7423
|
}
|
|
7622
7424
|
});
|
|
@@ -7644,7 +7446,7 @@
|
|
|
7644
7446
|
|
|
7645
7447
|
// does the attribute inspector appear on rollover
|
|
7646
7448
|
function inspecting() {
|
|
7647
|
-
return gui.interaction && gui.interaction.getMode()
|
|
7449
|
+
return gui.interaction && gui.interaction.modeUsesPopup(gui.interaction.getMode());
|
|
7648
7450
|
}
|
|
7649
7451
|
|
|
7650
7452
|
return _self;
|
|
@@ -7986,7 +7788,8 @@
|
|
|
7986
7788
|
|
|
7987
7789
|
function findVertexInsertionPoint(e) {
|
|
7988
7790
|
var target = hit.getHitTarget();
|
|
7989
|
-
|
|
7791
|
+
//// vertex insertion not supported with simplification
|
|
7792
|
+
// if (!target.arcs.isFlat()) return null;
|
|
7990
7793
|
var p = ext.translatePixelCoords(e.x, e.y);
|
|
7991
7794
|
var midpoint = findNearestMidpoint(p, e.id, target);
|
|
7992
7795
|
if (!midpoint ||
|
|
@@ -8140,10 +7943,17 @@
|
|
|
8140
7943
|
activeStyle = { // outline style for the active layer
|
|
8141
7944
|
type: 'outline',
|
|
8142
7945
|
strokeColors: [lightStroke, darkStroke],
|
|
8143
|
-
strokeWidth: 0.
|
|
7946
|
+
strokeWidth: 0.8,
|
|
8144
7947
|
dotColor: "#223",
|
|
8145
7948
|
dotSize: 1
|
|
8146
7949
|
},
|
|
7950
|
+
activeStyleDarkMode = {
|
|
7951
|
+
type: 'outline',
|
|
7952
|
+
strokeColors: [lightStroke, 'white'],
|
|
7953
|
+
strokeWidth: 0.9,
|
|
7954
|
+
dotColor: 'white',
|
|
7955
|
+
dotSize: 1
|
|
7956
|
+
},
|
|
8147
7957
|
activeStyleForLabels = {
|
|
8148
7958
|
dotColor: "rgba(250, 0, 250, 0.45)", // violet dot with transparency
|
|
8149
7959
|
dotSize: 1
|
|
@@ -8272,12 +8082,14 @@
|
|
|
8272
8082
|
return style;
|
|
8273
8083
|
}
|
|
8274
8084
|
|
|
8275
|
-
function getActiveStyle(lyr) {
|
|
8085
|
+
function getActiveStyle(lyr, darkMode) {
|
|
8276
8086
|
var style;
|
|
8277
8087
|
if (layerHasCanvasDisplayStyle(lyr)) {
|
|
8278
8088
|
style = getCanvasDisplayStyle(lyr);
|
|
8279
8089
|
} else if (internal.layerHasLabels(lyr)) {
|
|
8280
8090
|
style = getDefaultStyle(lyr, activeStyleForLabels);
|
|
8091
|
+
} else if (darkMode) {
|
|
8092
|
+
style = getDefaultStyle(lyr, activeStyleDarkMode);
|
|
8281
8093
|
} else {
|
|
8282
8094
|
style = getDefaultStyle(lyr, activeStyle);
|
|
8283
8095
|
}
|
|
@@ -8292,6 +8104,7 @@
|
|
|
8292
8104
|
strokeColor: black,
|
|
8293
8105
|
strokeWidth: 1.5,
|
|
8294
8106
|
vertices: true,
|
|
8107
|
+
vertex_overlay_color: violet,
|
|
8295
8108
|
vertex_overlay: o.hit_coordinates || null,
|
|
8296
8109
|
selected_points: o.selected_points || null,
|
|
8297
8110
|
fillColor: null
|
|
@@ -8331,13 +8144,6 @@
|
|
|
8331
8144
|
ids: ids,
|
|
8332
8145
|
overlay: true
|
|
8333
8146
|
};
|
|
8334
|
-
// kludge to show vertices when editing path shapes
|
|
8335
|
-
if (o.mode == 'vertices') {
|
|
8336
|
-
style.vertices = true;
|
|
8337
|
-
style.vertex_overlay = o.hit_coordinates || null;
|
|
8338
|
-
style.selected_points = o.selected_points || null;
|
|
8339
|
-
style.fillColor = null;
|
|
8340
|
-
}
|
|
8341
8147
|
|
|
8342
8148
|
if (layerHasCanvasDisplayStyle(lyr)) {
|
|
8343
8149
|
if (geomType == 'point') {
|
|
@@ -8448,6 +8254,7 @@
|
|
|
8448
8254
|
var _scale = 1,
|
|
8449
8255
|
_cx, _cy, // center in geographic units
|
|
8450
8256
|
_contentBounds,
|
|
8257
|
+
_strictBounds, // full extent must fit inside, if set
|
|
8451
8258
|
_self = this,
|
|
8452
8259
|
_frame;
|
|
8453
8260
|
|
|
@@ -8531,15 +8338,25 @@
|
|
|
8531
8338
|
};
|
|
8532
8339
|
|
|
8533
8340
|
// Update the extent of 'full' zoom without navigating the current view
|
|
8534
|
-
|
|
8341
|
+
//
|
|
8342
|
+
this.setBounds = function(contentBounds, strictBounds) {
|
|
8343
|
+
var b = contentBounds;
|
|
8535
8344
|
var prev = _contentBounds;
|
|
8536
8345
|
if (!b.hasBounds()) return; // kludge
|
|
8346
|
+
if (strictBounds) {
|
|
8347
|
+
_strictBounds = Array.isArray(strictBounds) ? new Bounds(strictBounds) : strictBounds;
|
|
8348
|
+
} else {
|
|
8349
|
+
_strictBounds = null;
|
|
8350
|
+
}
|
|
8537
8351
|
_contentBounds = _frame ? b : padBounds(b, 4); // padding if not in frame mode
|
|
8352
|
+
if (_strictBounds) {
|
|
8353
|
+
_contentBounds = fitIn(_contentBounds, _strictBounds);
|
|
8354
|
+
}
|
|
8538
8355
|
if (prev) {
|
|
8539
8356
|
_scale = _scale * fillOut(_contentBounds).width() / fillOut(prev).width();
|
|
8540
8357
|
} else {
|
|
8541
|
-
_cx =
|
|
8542
|
-
_cy =
|
|
8358
|
+
_cx = _contentBounds.centerX();
|
|
8359
|
+
_cy = _contentBounds.centerY();
|
|
8543
8360
|
}
|
|
8544
8361
|
};
|
|
8545
8362
|
|
|
@@ -8568,12 +8385,25 @@
|
|
|
8568
8385
|
|
|
8569
8386
|
function recenter(cx, cy, scale, data) {
|
|
8570
8387
|
scale = scale ? limitScale(scale) : _scale;
|
|
8571
|
-
if (
|
|
8572
|
-
|
|
8573
|
-
|
|
8574
|
-
_scale = scale;
|
|
8575
|
-
onChange(data);
|
|
8388
|
+
if (cx == _cx && cy == _cy && scale == _scale) return;
|
|
8389
|
+
if (_strictBounds) {
|
|
8390
|
+
scale = Math.max(1, scale);
|
|
8576
8391
|
}
|
|
8392
|
+
_cx = cx;
|
|
8393
|
+
_cy = cy;
|
|
8394
|
+
_scale = scale;
|
|
8395
|
+
limitExtent();
|
|
8396
|
+
onChange(data);
|
|
8397
|
+
}
|
|
8398
|
+
|
|
8399
|
+
function limitExtent() {
|
|
8400
|
+
if (!_strictBounds) return;
|
|
8401
|
+
if (_scale < 1) _scale = 1;
|
|
8402
|
+
var dist = _strictBounds.height() / 2 / _scale;
|
|
8403
|
+
var ymax = _strictBounds.ymax - dist;
|
|
8404
|
+
var ymin = _strictBounds.ymin + dist;
|
|
8405
|
+
if (_cy > ymax) _cy = ymax;
|
|
8406
|
+
if (_cy < ymin) _cy = ymin;
|
|
8577
8407
|
}
|
|
8578
8408
|
|
|
8579
8409
|
function onChange(data) {
|
|
@@ -8600,15 +8430,19 @@
|
|
|
8600
8430
|
}
|
|
8601
8431
|
|
|
8602
8432
|
function calcBounds(cx, cy, scale) {
|
|
8603
|
-
var bounds, w, h;
|
|
8433
|
+
var full, bounds, w, h;
|
|
8604
8434
|
if (_frame) {
|
|
8605
|
-
|
|
8435
|
+
full = fillOutFrameBounds(_frame);
|
|
8606
8436
|
} else {
|
|
8607
|
-
|
|
8437
|
+
full = fillOut(_contentBounds);
|
|
8608
8438
|
}
|
|
8609
|
-
|
|
8610
|
-
|
|
8611
|
-
|
|
8439
|
+
if (_strictBounds) {
|
|
8440
|
+
full = fitIn(full, _strictBounds);
|
|
8441
|
+
}
|
|
8442
|
+
w = full.width() / scale;
|
|
8443
|
+
h = full.height() / scale;
|
|
8444
|
+
bounds = new Bounds(cx - w/2, cy - h/2, cx + w/2, cy + h/2);
|
|
8445
|
+
return bounds;
|
|
8612
8446
|
}
|
|
8613
8447
|
|
|
8614
8448
|
// Calculate viewport bounds from frame data
|
|
@@ -8636,6 +8470,22 @@
|
|
|
8636
8470
|
return b;
|
|
8637
8471
|
}
|
|
8638
8472
|
|
|
8473
|
+
function fitIn(b, b2) {
|
|
8474
|
+
// only fitting vertical extent
|
|
8475
|
+
// (currently only used in basemap view to enforce Mapbox's vertical limits)
|
|
8476
|
+
|
|
8477
|
+
if (b.height() > b2.height()) {
|
|
8478
|
+
b.scale(b2.height() / b.height());
|
|
8479
|
+
}
|
|
8480
|
+
if (b.ymin < b2.ymin) {
|
|
8481
|
+
b.shift(0, b2.ymin - b.ymin);
|
|
8482
|
+
}
|
|
8483
|
+
if (b.ymax > b2.ymax) {
|
|
8484
|
+
b.shift(0, b2.ymax - b.ymax);
|
|
8485
|
+
}
|
|
8486
|
+
return b;
|
|
8487
|
+
}
|
|
8488
|
+
|
|
8639
8489
|
// Pad bounds vertically or horizontally to match viewport aspect ratio
|
|
8640
8490
|
function fillOut(b) {
|
|
8641
8491
|
var wpix = _position.width(),
|
|
@@ -8811,13 +8661,17 @@
|
|
|
8811
8661
|
}
|
|
8812
8662
|
}
|
|
8813
8663
|
}
|
|
8664
|
+
_ctx.fill();
|
|
8665
|
+
_ctx.closePath();
|
|
8814
8666
|
|
|
8815
8667
|
if (style.vertex_overlay) {
|
|
8668
|
+
_ctx.beginPath();
|
|
8669
|
+
_ctx.fillStyle = style.vertex_overlay_color || 'black';
|
|
8816
8670
|
p = style.vertex_overlay;
|
|
8817
|
-
drawCircle(p[0] * t.mx + t.bx, p[1] * t.my + t.by, radius * 1.
|
|
8671
|
+
drawCircle(p[0] * t.mx + t.bx, p[1] * t.my + t.by, radius * 1.6, _ctx);
|
|
8672
|
+
_ctx.fill();
|
|
8673
|
+
_ctx.closePath();
|
|
8818
8674
|
}
|
|
8819
|
-
_ctx.fill();
|
|
8820
|
-
_ctx.closePath();
|
|
8821
8675
|
};
|
|
8822
8676
|
|
|
8823
8677
|
// Optimized to draw paths in same-style batches (faster Canvas drawing)
|
|
@@ -9499,124 +9353,379 @@
|
|
|
9499
9353
|
// runCommand('-rectangle bbox=' + bbox.join(','));
|
|
9500
9354
|
// });
|
|
9501
9355
|
|
|
9502
|
-
new SimpleButton(popup.findChild('.select-btn')).on('click', function() {
|
|
9503
|
-
gui.enterMode('selection_tool');
|
|
9504
|
-
gui.interaction.setMode('selection');
|
|
9505
|
-
// kludge to pass bbox to the selection tool
|
|
9506
|
-
gui.dispatchEvent('box_drag_end', {map_bbox: bboxPixels});
|
|
9507
|
-
});
|
|
9356
|
+
new SimpleButton(popup.findChild('.select-btn')).on('click', function() {
|
|
9357
|
+
gui.enterMode('selection_tool');
|
|
9358
|
+
gui.interaction.setMode('selection');
|
|
9359
|
+
// kludge to pass bbox to the selection tool
|
|
9360
|
+
gui.dispatchEvent('box_drag_end', {map_bbox: bboxPixels});
|
|
9361
|
+
});
|
|
9362
|
+
|
|
9363
|
+
new SimpleButton(popup.findChild('.clip-btn')).on('click', function() {
|
|
9364
|
+
runCommand('-clip bbox2=' + bboxDataCoords.join(','));
|
|
9365
|
+
});
|
|
9366
|
+
|
|
9367
|
+
gui.addMode('box_tool', turnOn, turnOff);
|
|
9368
|
+
|
|
9369
|
+
gui.on('interaction_mode_change', function(e) {
|
|
9370
|
+
if (e.mode === 'box') {
|
|
9371
|
+
gui.enterMode('box_tool');
|
|
9372
|
+
} else if (gui.getMode() == 'box_tool') {
|
|
9373
|
+
gui.clearMode();
|
|
9374
|
+
}
|
|
9375
|
+
});
|
|
9376
|
+
|
|
9377
|
+
// Update the visible rectangle when the map view changes
|
|
9378
|
+
// (e.g. during zooming or panning)
|
|
9379
|
+
ext.on('change', function() {
|
|
9380
|
+
if (!_on || !box.visible() || !bboxDisplayCoords) return;
|
|
9381
|
+
var b = coordsToPix(bboxDisplayCoords);
|
|
9382
|
+
var pos = ext.position();
|
|
9383
|
+
var dx = pos.pageX,
|
|
9384
|
+
dy = pos.pageY;
|
|
9385
|
+
box.show(b[0] + dx, b[1] + dy, b[2] + dx, b[3] + dy);
|
|
9386
|
+
});
|
|
9387
|
+
|
|
9388
|
+
gui.on('box_drag_start', function() {
|
|
9389
|
+
box.classed('zooming', inZoomMode());
|
|
9390
|
+
hideCoords();
|
|
9391
|
+
});
|
|
9392
|
+
|
|
9393
|
+
gui.on('box_drag', function(e) {
|
|
9394
|
+
var b = e.page_bbox;
|
|
9395
|
+
bboxPixels = e.map_bbox;
|
|
9396
|
+
bboxDisplayCoords = pixToCoords(bboxPixels);
|
|
9397
|
+
if (_on || inZoomMode()) {
|
|
9398
|
+
box.show(b[0], b[1], b[2], b[3]);
|
|
9399
|
+
}
|
|
9400
|
+
});
|
|
9401
|
+
|
|
9402
|
+
gui.on('box_drag_end', function(e) {
|
|
9403
|
+
bboxPixels = e.map_bbox;
|
|
9404
|
+
bboxDisplayCoords = pixToCoords(bboxPixels);
|
|
9405
|
+
bboxDataCoords = getBBoxCoords(gui.map.getActiveLayer(), bboxDisplayCoords);
|
|
9406
|
+
if (inZoomMode()) {
|
|
9407
|
+
box.hide();
|
|
9408
|
+
nav.zoomToBbox(bboxPixels);
|
|
9409
|
+
} else if (_on) {
|
|
9410
|
+
popup.show();
|
|
9411
|
+
}
|
|
9412
|
+
});
|
|
9413
|
+
|
|
9414
|
+
function inZoomMode() {
|
|
9415
|
+
return !_on && gui.getMode() != 'selection_tool';
|
|
9416
|
+
}
|
|
9417
|
+
|
|
9418
|
+
function runCommand(cmd) {
|
|
9419
|
+
if (gui.console) {
|
|
9420
|
+
gui.console.runMapshaperCommands(cmd, function(err) {
|
|
9421
|
+
reset();
|
|
9422
|
+
});
|
|
9423
|
+
}
|
|
9424
|
+
// reset(); // TODO: exit interactive mode
|
|
9425
|
+
}
|
|
9426
|
+
|
|
9427
|
+
function showCoords() {
|
|
9428
|
+
El(infoBtn.node()).addClass('selected-btn');
|
|
9429
|
+
coords.text(bboxDataCoords.join(','));
|
|
9430
|
+
coords.show();
|
|
9431
|
+
GUI.selectElement(coords.node());
|
|
9432
|
+
}
|
|
9433
|
+
|
|
9434
|
+
function hideCoords() {
|
|
9435
|
+
El(infoBtn.node()).removeClass('selected-btn');
|
|
9436
|
+
coords.hide();
|
|
9437
|
+
}
|
|
9438
|
+
|
|
9439
|
+
function turnOn() {
|
|
9440
|
+
_on = true;
|
|
9441
|
+
}
|
|
9442
|
+
|
|
9443
|
+
function turnOff() {
|
|
9444
|
+
if (gui.interaction.getMode() == 'box') {
|
|
9445
|
+
// mode change was not initiated by interactive menu -- turn off interactivity
|
|
9446
|
+
gui.interaction.turnOff();
|
|
9447
|
+
}
|
|
9448
|
+
_on = false;
|
|
9449
|
+
reset();
|
|
9450
|
+
}
|
|
9451
|
+
|
|
9452
|
+
function reset() {
|
|
9453
|
+
box.hide();
|
|
9454
|
+
popup.hide();
|
|
9455
|
+
hideCoords();
|
|
9456
|
+
}
|
|
9457
|
+
|
|
9458
|
+
function pixToCoords(bbox) {
|
|
9459
|
+
var a = ext.translatePixelCoords(bbox[0], bbox[1]);
|
|
9460
|
+
var b = ext.translatePixelCoords(bbox[2], bbox[3]);
|
|
9461
|
+
var bbox2 = [a[0], b[1], b[0], a[1]];
|
|
9462
|
+
// round coords, for nicer 'info' display
|
|
9463
|
+
// (rounded precision should be sub-pixel)
|
|
9464
|
+
return internal.getRoundedCoords(bbox2, internal.getBoundsPrecisionForDisplay(bbox2));
|
|
9465
|
+
}
|
|
9466
|
+
|
|
9467
|
+
function coordsToPix(bbox) {
|
|
9468
|
+
var a = ext.translateCoords(bbox[0], bbox[1]);
|
|
9469
|
+
var b = ext.translateCoords(bbox[2], bbox[3]);
|
|
9470
|
+
return [a[0], b[1], b[0], a[1]];
|
|
9471
|
+
}
|
|
9472
|
+
|
|
9473
|
+
return self;
|
|
9474
|
+
}
|
|
9475
|
+
|
|
9476
|
+
// Create low-detail versions of large arc collections for faster rendering
|
|
9477
|
+
// at zoomed-out scales.
|
|
9478
|
+
function enhanceArcCollectionForDisplay(unfilteredArcs) {
|
|
9479
|
+
var size = unfilteredArcs.getPointCount(),
|
|
9480
|
+
filteredArcs, filteredSegLen;
|
|
9481
|
+
|
|
9482
|
+
// Only generate low-detail arcs for larger datasets
|
|
9483
|
+
if (size > 5e5) {
|
|
9484
|
+
if (!!unfilteredArcs.getVertexData().zz) {
|
|
9485
|
+
// Use precalculated simplification data for vertex filtering, if available
|
|
9486
|
+
filteredArcs = initFilteredArcs(unfilteredArcs);
|
|
9487
|
+
filteredSegLen = internal.getAvgSegment(filteredArcs);
|
|
9488
|
+
} else {
|
|
9489
|
+
// Use fast simplification as a fallback
|
|
9490
|
+
filteredSegLen = internal.getAvgSegment(unfilteredArcs) * 4;
|
|
9491
|
+
filteredArcs = internal.simplifyArcsFast(unfilteredArcs, filteredSegLen);
|
|
9492
|
+
}
|
|
9493
|
+
}
|
|
9494
|
+
|
|
9495
|
+
function initFilteredArcs(arcs) {
|
|
9496
|
+
var filterPct = 0.08;
|
|
9497
|
+
var nth = Math.ceil(arcs.getPointCount() / 5e5);
|
|
9498
|
+
var currInterval = arcs.getRetainedInterval();
|
|
9499
|
+
var filterZ = arcs.getThresholdByPct(filterPct, nth);
|
|
9500
|
+
var filteredArcs = arcs.setRetainedInterval(filterZ).getFilteredCopy();
|
|
9501
|
+
arcs.setRetainedInterval(currInterval); // reset current simplification
|
|
9502
|
+
return filteredArcs;
|
|
9503
|
+
}
|
|
9504
|
+
|
|
9505
|
+
unfilteredArcs.getScaledArcs = function(ext) {
|
|
9506
|
+
if (filteredArcs) {
|
|
9507
|
+
// match simplification of unfiltered arcs
|
|
9508
|
+
filteredArcs.setRetainedInterval(unfilteredArcs.getRetainedInterval());
|
|
9509
|
+
}
|
|
9510
|
+
// switch to filtered version of arcs at small scales
|
|
9511
|
+
var unitsPerPixel = 1/ext.getTransform().mx,
|
|
9512
|
+
useFiltering = filteredArcs && unitsPerPixel > filteredSegLen * 1.5;
|
|
9513
|
+
return useFiltering ? filteredArcs : unfilteredArcs;
|
|
9514
|
+
};
|
|
9515
|
+
}
|
|
9516
|
+
|
|
9517
|
+
function getDisplayLayerForTable(table) {
|
|
9518
|
+
var n = table.size(),
|
|
9519
|
+
cellWidth = 12,
|
|
9520
|
+
cellHeight = 5,
|
|
9521
|
+
gutter = 6,
|
|
9522
|
+
arcs = [],
|
|
9523
|
+
shapes = [],
|
|
9524
|
+
aspectRatio = 1.1,
|
|
9525
|
+
x, y, col, row, blockSize;
|
|
9526
|
+
|
|
9527
|
+
if (n > 10000) {
|
|
9528
|
+
arcs = null;
|
|
9529
|
+
gutter = 0;
|
|
9530
|
+
cellWidth = 4;
|
|
9531
|
+
cellHeight = 4;
|
|
9532
|
+
aspectRatio = 1.45;
|
|
9533
|
+
} else if (n > 5000) {
|
|
9534
|
+
cellWidth = 5;
|
|
9535
|
+
gutter = 3;
|
|
9536
|
+
aspectRatio = 1.45;
|
|
9537
|
+
} else if (n > 1000) {
|
|
9538
|
+
gutter = 3;
|
|
9539
|
+
cellWidth = 8;
|
|
9540
|
+
aspectRatio = 1.3;
|
|
9541
|
+
}
|
|
9542
|
+
|
|
9543
|
+
if (n < 25) {
|
|
9544
|
+
blockSize = n;
|
|
9545
|
+
} else {
|
|
9546
|
+
blockSize = Math.sqrt(n * (cellWidth + gutter) / cellHeight / aspectRatio) | 0;
|
|
9547
|
+
}
|
|
9548
|
+
|
|
9549
|
+
for (var i=0; i<n; i++) {
|
|
9550
|
+
row = i % blockSize;
|
|
9551
|
+
col = Math.floor(i / blockSize);
|
|
9552
|
+
x = col * (cellWidth + gutter);
|
|
9553
|
+
y = cellHeight * (blockSize - row);
|
|
9554
|
+
if (arcs) {
|
|
9555
|
+
arcs.push(getArc(x, y, cellWidth, cellHeight));
|
|
9556
|
+
shapes.push([[i]]);
|
|
9557
|
+
} else {
|
|
9558
|
+
shapes.push([[x, y]]);
|
|
9559
|
+
}
|
|
9560
|
+
}
|
|
9561
|
+
|
|
9562
|
+
function getArc(x, y, w, h) {
|
|
9563
|
+
return [[x, y], [x + w, y], [x + w, y - h], [x, y - h], [x, y]];
|
|
9564
|
+
}
|
|
9508
9565
|
|
|
9509
|
-
|
|
9510
|
-
|
|
9511
|
-
|
|
9566
|
+
return {
|
|
9567
|
+
layer: {
|
|
9568
|
+
geometry_type: arcs ? 'polygon' : 'point',
|
|
9569
|
+
shapes: shapes,
|
|
9570
|
+
data: table
|
|
9571
|
+
},
|
|
9572
|
+
arcs: arcs ? new internal.ArcCollection(arcs) : null
|
|
9573
|
+
};
|
|
9574
|
+
}
|
|
9512
9575
|
|
|
9513
|
-
|
|
9576
|
+
// displayCRS: CRS to use for display, or null (which clears any current display CRS)
|
|
9577
|
+
function projectDisplayLayer(lyr, displayCRS) {
|
|
9578
|
+
var crsInfo = getDatasetCrsInfo(lyr.source.dataset);
|
|
9579
|
+
var sourceCRS = crsInfo.crs;
|
|
9580
|
+
var lyr2;
|
|
9581
|
+
//if (!lyr.geographic || !sourceCRS) {
|
|
9582
|
+
// let getDisplayLayer() handle case of unprojectable source
|
|
9583
|
+
if (!lyr.geographic) {
|
|
9584
|
+
return lyr;
|
|
9585
|
+
}
|
|
9586
|
+
if (lyr.dynamic_crs && internal.crsAreEqual(sourceCRS, lyr.dynamic_crs)) {
|
|
9587
|
+
return lyr;
|
|
9588
|
+
}
|
|
9589
|
+
lyr2 = getDisplayLayer(lyr.source.layer, lyr.source.dataset, {crs: displayCRS});
|
|
9590
|
+
// kludge: copy projection-related properties to original layer
|
|
9591
|
+
lyr.dynamic_crs = lyr2.dynamic_crs;
|
|
9592
|
+
lyr.layer = lyr2.layer;
|
|
9514
9593
|
|
|
9515
|
-
|
|
9516
|
-
|
|
9517
|
-
|
|
9518
|
-
|
|
9519
|
-
|
|
9520
|
-
|
|
9521
|
-
|
|
9594
|
+
if (lyr.style && lyr.style.ids) {
|
|
9595
|
+
// re-apply layer filter
|
|
9596
|
+
lyr.layer = filterLayerByIds(lyr.layer, lyr.style.ids);
|
|
9597
|
+
}
|
|
9598
|
+
lyr.invertPoint = lyr2.invertPoint;
|
|
9599
|
+
lyr.projectPoint = lyr2.projectPoint;
|
|
9600
|
+
lyr.bounds = lyr2.bounds;
|
|
9601
|
+
lyr.arcs = lyr2.arcs;
|
|
9602
|
+
}
|
|
9522
9603
|
|
|
9523
|
-
// Update the visible rectangle when the map view changes
|
|
9524
|
-
// (e.g. during zooming or panning)
|
|
9525
|
-
ext.on('change', function() {
|
|
9526
|
-
if (!_on || !box.visible() || !bboxDisplayCoords) return;
|
|
9527
|
-
var b = coordsToPix(bboxDisplayCoords);
|
|
9528
|
-
var pos = ext.position();
|
|
9529
|
-
var dx = pos.pageX,
|
|
9530
|
-
dy = pos.pageY;
|
|
9531
|
-
box.show(b[0] + dx, b[1] + dy, b[2] + dx, b[3] + dy);
|
|
9532
|
-
});
|
|
9533
9604
|
|
|
9534
|
-
|
|
9535
|
-
|
|
9536
|
-
|
|
9537
|
-
|
|
9605
|
+
// Wrap a layer in an object along with information needed for rendering
|
|
9606
|
+
function getDisplayLayer(layer, dataset, opts) {
|
|
9607
|
+
var obj = {
|
|
9608
|
+
layer: null,
|
|
9609
|
+
arcs: null,
|
|
9610
|
+
// display_arcs: null,
|
|
9611
|
+
style: null,
|
|
9612
|
+
invertPoint: null,
|
|
9613
|
+
projectPoint: null,
|
|
9614
|
+
source: {
|
|
9615
|
+
layer: layer,
|
|
9616
|
+
dataset: dataset
|
|
9617
|
+
},
|
|
9618
|
+
empty: internal.getFeatureCount(layer) === 0
|
|
9619
|
+
};
|
|
9538
9620
|
|
|
9539
|
-
|
|
9540
|
-
|
|
9541
|
-
|
|
9542
|
-
|
|
9543
|
-
|
|
9544
|
-
box.show(b[0], b[1], b[2], b[3]);
|
|
9545
|
-
}
|
|
9546
|
-
});
|
|
9621
|
+
var displayCRS = opts.crs || null;
|
|
9622
|
+
// display arcs may have been generated when another layer in the dataset was converted for display... re-use if available
|
|
9623
|
+
var displayArcs = dataset.displayArcs || null;
|
|
9624
|
+
var sourceCRS;
|
|
9625
|
+
var emptyArcs;
|
|
9547
9626
|
|
|
9548
|
-
|
|
9549
|
-
|
|
9550
|
-
|
|
9551
|
-
|
|
9552
|
-
|
|
9553
|
-
|
|
9554
|
-
|
|
9555
|
-
|
|
9556
|
-
popup.show();
|
|
9627
|
+
if (displayCRS && layer.geometry_type) {
|
|
9628
|
+
var crsInfo = getDatasetCrsInfo(dataset);
|
|
9629
|
+
if (crsInfo.error) {
|
|
9630
|
+
// unprojectable dataset -- return empty layer
|
|
9631
|
+
obj.empty = true;
|
|
9632
|
+
obj.geographic = true;
|
|
9633
|
+
} else {
|
|
9634
|
+
sourceCRS = crsInfo.crs;
|
|
9557
9635
|
}
|
|
9558
|
-
});
|
|
9559
|
-
|
|
9560
|
-
function inZoomMode() {
|
|
9561
|
-
return !_on && gui.getMode() != 'selection_tool';
|
|
9562
9636
|
}
|
|
9563
9637
|
|
|
9564
|
-
|
|
9565
|
-
|
|
9566
|
-
|
|
9567
|
-
|
|
9568
|
-
|
|
9638
|
+
// Assume that dataset.displayArcs is in the display CRS
|
|
9639
|
+
// (it must be deleted upstream if reprojection is needed)
|
|
9640
|
+
if (!obj.empty && dataset.arcs && !displayArcs) {
|
|
9641
|
+
// project arcs, if needed
|
|
9642
|
+
if (needReprojectionForDisplay(sourceCRS, displayCRS)) {
|
|
9643
|
+
displayArcs = projectArcsForDisplay(dataset.arcs, sourceCRS, displayCRS);
|
|
9644
|
+
} else {
|
|
9645
|
+
// Use original arcs for display if there is no dynamic reprojection
|
|
9646
|
+
displayArcs = dataset.arcs;
|
|
9569
9647
|
}
|
|
9570
|
-
// reset(); // TODO: exit interactive mode
|
|
9571
|
-
}
|
|
9572
9648
|
|
|
9573
|
-
|
|
9574
|
-
|
|
9575
|
-
coords.text(bboxDataCoords.join(','));
|
|
9576
|
-
coords.show();
|
|
9577
|
-
GUI.selectElement(coords.node());
|
|
9649
|
+
enhanceArcCollectionForDisplay(displayArcs);
|
|
9650
|
+
dataset.displayArcs = displayArcs; // stash these in the dataset for other layers to use
|
|
9578
9651
|
}
|
|
9579
9652
|
|
|
9580
|
-
|
|
9581
|
-
|
|
9582
|
-
|
|
9653
|
+
if (internal.layerHasFurniture(layer)) {
|
|
9654
|
+
obj.furniture = true;
|
|
9655
|
+
obj.furniture_type = internal.getFurnitureLayerType(layer);
|
|
9656
|
+
obj.layer = layer;
|
|
9657
|
+
// treating furniture layers (other than frame) as tabular for now,
|
|
9658
|
+
// so there is something to show if they are selected
|
|
9659
|
+
obj.tabular = obj.furniture_type != 'frame';
|
|
9660
|
+
} else if (obj.empty) {
|
|
9661
|
+
obj.layer = {shapes: []}; // ideally we should avoid empty layers
|
|
9662
|
+
} else if (!layer.geometry_type) {
|
|
9663
|
+
obj.tabular = true;
|
|
9664
|
+
} else {
|
|
9665
|
+
obj.geographic = true;
|
|
9666
|
+
obj.layer = layer;
|
|
9667
|
+
obj.arcs = displayArcs;
|
|
9583
9668
|
}
|
|
9584
9669
|
|
|
9585
|
-
|
|
9586
|
-
|
|
9670
|
+
if (obj.tabular) {
|
|
9671
|
+
utils$1.extend(obj, getDisplayLayerForTable(layer.data));
|
|
9587
9672
|
}
|
|
9588
9673
|
|
|
9589
|
-
|
|
9590
|
-
|
|
9591
|
-
|
|
9592
|
-
|
|
9674
|
+
// dynamic reprojection (arcs were already reprojected above)
|
|
9675
|
+
if (obj.geographic && needReprojectionForDisplay(sourceCRS, displayCRS)) {
|
|
9676
|
+
obj.dynamic_crs = displayCRS;
|
|
9677
|
+
obj.invertPoint = internal.getProjTransform2(displayCRS, sourceCRS);
|
|
9678
|
+
obj.projectPoint = internal.getProjTransform2(sourceCRS, displayCRS);
|
|
9679
|
+
if (internal.layerHasPoints(layer)) {
|
|
9680
|
+
obj.layer = projectPointsForDisplay(layer, sourceCRS, displayCRS);
|
|
9681
|
+
} else if (internal.layerHasPaths(layer)) {
|
|
9682
|
+
emptyArcs = findEmptyArcs(displayArcs);
|
|
9683
|
+
if (emptyArcs.length > 0) {
|
|
9684
|
+
// Don't try to draw paths containing coordinates that failed to project
|
|
9685
|
+
obj.layer = internal.filterPathLayerByArcIds(obj.layer, emptyArcs);
|
|
9686
|
+
}
|
|
9593
9687
|
}
|
|
9594
|
-
_on = false;
|
|
9595
|
-
reset();
|
|
9596
9688
|
}
|
|
9597
9689
|
|
|
9598
|
-
|
|
9599
|
-
|
|
9600
|
-
|
|
9601
|
-
hideCoords();
|
|
9602
|
-
}
|
|
9690
|
+
obj.bounds = getDisplayBounds(obj.layer, obj.arcs);
|
|
9691
|
+
return obj;
|
|
9692
|
+
}
|
|
9603
9693
|
|
|
9604
|
-
|
|
9605
|
-
|
|
9606
|
-
|
|
9607
|
-
|
|
9608
|
-
|
|
9609
|
-
|
|
9610
|
-
|
|
9694
|
+
|
|
9695
|
+
function getDisplayBounds(lyr, arcs) {
|
|
9696
|
+
var arcBounds = arcs ? arcs.getBounds() : new Bounds(),
|
|
9697
|
+
bounds = arcBounds, // default display extent: all arcs in the dataset
|
|
9698
|
+
lyrBounds;
|
|
9699
|
+
|
|
9700
|
+
if (lyr.geometry_type == 'point') {
|
|
9701
|
+
lyrBounds = internal.getLayerBounds(lyr);
|
|
9702
|
+
if (lyrBounds && lyrBounds.hasBounds()) {
|
|
9703
|
+
if (lyrBounds.area() > 0 || !arcBounds.hasBounds()) {
|
|
9704
|
+
bounds = lyrBounds;
|
|
9705
|
+
} else {
|
|
9706
|
+
// if a point layer has no extent (e.g. contains only a single point),
|
|
9707
|
+
// then merge with arc bounds, to place the point in context.
|
|
9708
|
+
bounds = arcBounds.mergeBounds(lyrBounds);
|
|
9709
|
+
}
|
|
9710
|
+
}
|
|
9611
9711
|
}
|
|
9612
9712
|
|
|
9613
|
-
|
|
9614
|
-
|
|
9615
|
-
var b = ext.translateCoords(bbox[2], bbox[3]);
|
|
9616
|
-
return [a[0], b[1], b[0], a[1]];
|
|
9713
|
+
if (!bounds || !bounds.hasBounds()) { // empty layer
|
|
9714
|
+
bounds = new Bounds();
|
|
9617
9715
|
}
|
|
9716
|
+
return bounds;
|
|
9717
|
+
}
|
|
9618
9718
|
|
|
9619
|
-
|
|
9719
|
+
// Returns an array of ids of empty arcs (arcs can be set to empty if errors occur while projecting them)
|
|
9720
|
+
function findEmptyArcs(arcs) {
|
|
9721
|
+
var nn = arcs.getVertexData().nn;
|
|
9722
|
+
var ids = [];
|
|
9723
|
+
for (var i=0, n=nn.length; i<n; i++) {
|
|
9724
|
+
if (nn[i] === 0) {
|
|
9725
|
+
ids.push(i);
|
|
9726
|
+
}
|
|
9727
|
+
}
|
|
9728
|
+
return ids;
|
|
9620
9729
|
}
|
|
9621
9730
|
|
|
9622
9731
|
function loadScript(url, cb) {
|
|
@@ -9657,10 +9766,6 @@
|
|
|
9657
9766
|
|
|
9658
9767
|
function init() {
|
|
9659
9768
|
gui.addMode('basemap', turnOn, turnOff, basemapBtn);
|
|
9660
|
-
// model.on('select', function() {
|
|
9661
|
-
// TODO: hide basemap
|
|
9662
|
-
// if (gui.getMode() == 'basemap') gui.clearMode();
|
|
9663
|
-
// });
|
|
9664
9769
|
|
|
9665
9770
|
new SimpleButton(menu.findChild('.close-btn')).on('click', function() {
|
|
9666
9771
|
gui.clearMode();
|
|
@@ -9684,6 +9789,9 @@
|
|
|
9684
9789
|
|
|
9685
9790
|
function updateStyle(style) {
|
|
9686
9791
|
activeStyle = style || null;
|
|
9792
|
+
// TODO: consider enabling this
|
|
9793
|
+
// Make sure that the selected layer style gets updated in gui-map.js
|
|
9794
|
+
// gui.state.dark_basemap = style && style.dark || false;
|
|
9687
9795
|
if (!style) {
|
|
9688
9796
|
gui.map.setDisplayCRS(null);
|
|
9689
9797
|
hide();
|
|
@@ -9703,11 +9811,12 @@
|
|
|
9703
9811
|
|
|
9704
9812
|
function turnOn() {
|
|
9705
9813
|
var activeLyr = gui.model.getActiveLayer();
|
|
9706
|
-
var
|
|
9814
|
+
var info = getDatasetCrsInfo(activeLyr.dataset);
|
|
9815
|
+
var dataCRS = info.crs || null;
|
|
9707
9816
|
var displayCRS = gui.map.getDisplayCRS();
|
|
9708
9817
|
var warning;
|
|
9709
9818
|
|
|
9710
|
-
if (!crsIsUsable(displayCRS) || !crsIsUsable(dataCRS)) {
|
|
9819
|
+
if (!dataCRS || !displayCRS || !crsIsUsable(displayCRS) || !crsIsUsable(dataCRS)) {
|
|
9711
9820
|
warning = 'The current layer is not compatible with the projection used by the basemaps.';
|
|
9712
9821
|
basemapWarning.html(warning).show();
|
|
9713
9822
|
basemapNote.hide();
|
|
@@ -9772,16 +9881,25 @@
|
|
|
9772
9881
|
});
|
|
9773
9882
|
}
|
|
9774
9883
|
|
|
9884
|
+
// @bbox: latlon bounding box of current map extent
|
|
9775
9885
|
function checkBounds(bbox) {
|
|
9886
|
+
var mpp = ext.getBounds().width() / ext.width();
|
|
9887
|
+
var z = scaleToZoom(mpp);
|
|
9776
9888
|
var msg;
|
|
9777
|
-
if (bbox[1] >= -85 && bbox[3] <= 85) {
|
|
9889
|
+
if (bbox[1] >= -85 && bbox[3] <= 85 && z <= 20) {
|
|
9778
9890
|
extentNote.hide();
|
|
9779
9891
|
return true;
|
|
9780
9892
|
}
|
|
9781
|
-
if (
|
|
9782
|
-
|
|
9783
|
-
else
|
|
9784
|
-
|
|
9893
|
+
if (z > 20) {
|
|
9894
|
+
msg = 'zoom out';
|
|
9895
|
+
} else if (bbox[1] > 0) {
|
|
9896
|
+
msg = 'pan south';
|
|
9897
|
+
} else if (bbox[3] < 0) {
|
|
9898
|
+
msg = 'pan north';
|
|
9899
|
+
} else {
|
|
9900
|
+
msg = msg = 'zoom in';
|
|
9901
|
+
}
|
|
9902
|
+
extentNote.html(msg + ' to see the basemap').show();
|
|
9785
9903
|
return false;
|
|
9786
9904
|
}
|
|
9787
9905
|
|
|
@@ -9897,11 +10015,10 @@
|
|
|
9897
10015
|
};
|
|
9898
10016
|
|
|
9899
10017
|
this.getDisplayCRS = function() {
|
|
9900
|
-
|
|
9901
|
-
if (_activeLyr
|
|
9902
|
-
|
|
9903
|
-
|
|
9904
|
-
return crs || null;
|
|
10018
|
+
if (!_activeLyr || !_activeLyr.geographic) return null;
|
|
10019
|
+
if (_activeLyr.dynamic_crs) return _activeLyr.dynamic_crs;
|
|
10020
|
+
var info = getDatasetCrsInfo(_activeLyr.source.dataset);
|
|
10021
|
+
return info.crs || null;
|
|
9905
10022
|
};
|
|
9906
10023
|
|
|
9907
10024
|
this.getExtent = function() {return _ext;};
|
|
@@ -9974,7 +10091,8 @@
|
|
|
9974
10091
|
}
|
|
9975
10092
|
|
|
9976
10093
|
_activeLyr = getDisplayLayer(e.layer, e.dataset, getDisplayOptions());
|
|
9977
|
-
_activeLyr.style = getActiveStyle(_activeLyr.layer);
|
|
10094
|
+
_activeLyr.style = getActiveStyle(_activeLyr.layer, gui.state.dark_basemap);
|
|
10095
|
+
|
|
9978
10096
|
_activeLyr.active = true;
|
|
9979
10097
|
// if (_inspector) _inspector.updateLayer(_activeLyr);
|
|
9980
10098
|
_hit.setLayer(_activeLyr);
|
|
@@ -9993,7 +10111,6 @@
|
|
|
9993
10111
|
needReset = mapNeedsReset(fullBounds, _fullBounds, _ext.getBounds(), e.flags);
|
|
9994
10112
|
}
|
|
9995
10113
|
|
|
9996
|
-
|
|
9997
10114
|
if (isFrameView()) {
|
|
9998
10115
|
_nav.setZoomFactor(0.05); // slow zooming way down to allow fine-tuning frame placement // 0.03
|
|
9999
10116
|
_ext.setFrame(getFullBounds()); // TODO: remove redundancy with drawLayers()
|
|
@@ -10001,7 +10118,7 @@
|
|
|
10001
10118
|
} else {
|
|
10002
10119
|
_nav.setZoomFactor(1);
|
|
10003
10120
|
}
|
|
10004
|
-
_ext.setBounds(fullBounds); // update 'home' button extent
|
|
10121
|
+
_ext.setBounds(fullBounds, getStrictBounds()); // update 'home' button extent
|
|
10005
10122
|
_fullBounds = fullBounds;
|
|
10006
10123
|
if (needReset) {
|
|
10007
10124
|
_ext.reset();
|
|
@@ -10091,6 +10208,13 @@
|
|
|
10091
10208
|
_ext.reset();
|
|
10092
10209
|
}
|
|
10093
10210
|
|
|
10211
|
+
function getStrictBounds() {
|
|
10212
|
+
if (internal.isWebMercator(map.getDisplayCRS())) {
|
|
10213
|
+
return getMapboxBounds();
|
|
10214
|
+
}
|
|
10215
|
+
return null;
|
|
10216
|
+
}
|
|
10217
|
+
|
|
10094
10218
|
function getFullBounds() {
|
|
10095
10219
|
var b = new Bounds();
|
|
10096
10220
|
var marginPct = 0.025;
|
|
@@ -10257,7 +10381,7 @@
|
|
|
10257
10381
|
if (layersMayHaveChanged) {
|
|
10258
10382
|
// kludge to handle layer visibility toggling
|
|
10259
10383
|
_ext.setFrame(isPreviewView() ? getFrameData() : null);
|
|
10260
|
-
_ext.setBounds(getFullBounds());
|
|
10384
|
+
_ext.setBounds(getFullBounds(), getStrictBounds());
|
|
10261
10385
|
updateLayerStyles(contentLayers);
|
|
10262
10386
|
updateLayerStackOrder(model.getLayers());// update stack_id property of all layers
|
|
10263
10387
|
}
|
|
@@ -10275,6 +10399,40 @@
|
|
|
10275
10399
|
}
|
|
10276
10400
|
}
|
|
10277
10401
|
|
|
10402
|
+
// This is a new way to handle compatibility problems between
|
|
10403
|
+
// interactive editing modes and other interface modes
|
|
10404
|
+
// (by default, interactive modes stay on when, e.g., the user clicks
|
|
10405
|
+
// "Export" or "Console").
|
|
10406
|
+
//
|
|
10407
|
+
function initModeRules(gui) {
|
|
10408
|
+
|
|
10409
|
+
gui.on('interaction_mode_change', function(e) {
|
|
10410
|
+
var imode = e.mode;
|
|
10411
|
+
var mode = gui.getMode();
|
|
10412
|
+
|
|
10413
|
+
// simplify and vertex editing are not compatible
|
|
10414
|
+
if (imode == 'vertices') {
|
|
10415
|
+
flattenArcs(gui.map.getActiveLayer());
|
|
10416
|
+
|
|
10417
|
+
if (mode == 'simplify') {
|
|
10418
|
+
gui.clearMode(); // exit simplification
|
|
10419
|
+
}
|
|
10420
|
+
|
|
10421
|
+
}
|
|
10422
|
+
|
|
10423
|
+
});
|
|
10424
|
+
|
|
10425
|
+
gui.on('mode', function(e) {
|
|
10426
|
+
var mode = e.name;
|
|
10427
|
+
var imode = gui.interaction.getMode();
|
|
10428
|
+
|
|
10429
|
+
// simplify and vertex editing are not compatible
|
|
10430
|
+
if (mode == 'simplify' && imode == 'vertices') {
|
|
10431
|
+
gui.interaction.turnOff();
|
|
10432
|
+
}
|
|
10433
|
+
});
|
|
10434
|
+
}
|
|
10435
|
+
|
|
10278
10436
|
function GuiInstance(container, opts) {
|
|
10279
10437
|
var gui = new ModeSwitcher();
|
|
10280
10438
|
opts = utils$1.extend({
|
|
@@ -10298,6 +10456,8 @@
|
|
|
10298
10456
|
gui.undo = new Undo(gui);
|
|
10299
10457
|
gui.state = {};
|
|
10300
10458
|
|
|
10459
|
+
initModeRules(gui);
|
|
10460
|
+
|
|
10301
10461
|
gui.showProgressMessage = function(msg) {
|
|
10302
10462
|
if (!gui.progressMessage) {
|
|
10303
10463
|
gui.progressMessage = El('div').addClass('progress-message')
|
|
@@ -10322,7 +10482,7 @@
|
|
|
10322
10482
|
curr.blur();
|
|
10323
10483
|
}
|
|
10324
10484
|
GUI.__active = gui;
|
|
10325
|
-
|
|
10485
|
+
setLoggingForGUI(gui);
|
|
10326
10486
|
ImportFileProxy(gui);
|
|
10327
10487
|
WriteFilesProxy(gui);
|
|
10328
10488
|
gui.dispatchEvent('active');
|