mapshaper 0.5.103 → 0.5.106
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 +9 -0
- package/mapshaper.js +36 -17
- package/package.json +1 -1
- package/www/mapshaper-gui.js +906 -747
- 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
|
-
var zipWriter;
|
|
2488
|
-
try {
|
|
2489
|
-
zip.createWriter(new zip.BlobWriter("application/zip"), function(writer) {
|
|
2490
|
-
zipWriter = writer;
|
|
2491
|
-
nextFile();
|
|
2492
|
-
}, zipError);
|
|
2493
|
-
} catch(e) {
|
|
2494
|
-
done("This browser doesn't support Zip file creation.");
|
|
2495
|
-
}
|
|
2312
|
+
var R = 6378137;
|
|
2313
|
+
var D2R = Math.PI / 180;
|
|
2314
|
+
var R2D = 180 / Math.PI;
|
|
2496
2315
|
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
str += ": " + msg;
|
|
2506
|
-
}
|
|
2507
|
-
done(str);
|
|
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;
|
|
2508
2324
|
}
|
|
2325
|
+
return true;
|
|
2326
|
+
}
|
|
2509
2327
|
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
} else {
|
|
2516
|
-
var obj = toAdd.pop(),
|
|
2517
|
-
blob = new Blob([obj.content]);
|
|
2518
|
-
zipWriter.add(obj.filename, new zip.BlobReader(blob), nextFile);
|
|
2519
|
-
}
|
|
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;
|
|
2520
2333
|
}
|
|
2521
|
-
}
|
|
2522
2334
|
|
|
2523
|
-
|
|
2524
|
-
var
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
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);
|
|
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);
|
|
2531
2361
|
}
|
|
2362
|
+
return copy;
|
|
2532
2363
|
}
|
|
2533
2364
|
|
|
2534
|
-
function
|
|
2535
|
-
var
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
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;
|
|
@@ -7205,7 +7007,8 @@
|
|
|
7205
7007
|
function getBoxData(e) {
|
|
7206
7008
|
var pageBox = [e.pageX, e.pageY, dragStartEvt.pageX, dragStartEvt.pageY];
|
|
7207
7009
|
var mapBox = [e.x, e.y, dragStartEvt.x, dragStartEvt.y];
|
|
7208
|
-
var
|
|
7010
|
+
var displayBox = pixToCoords(mapBox);
|
|
7011
|
+
var dataBox = getBBoxCoords(gui.map.getActiveLayer(), displayBox);
|
|
7209
7012
|
if (pageBox[0] > pageBox[2]) {
|
|
7210
7013
|
swapElements(pageBox, 0, 2);
|
|
7211
7014
|
swapElements(mapBox, 0, 2);
|
|
@@ -7216,10 +7019,20 @@
|
|
|
7216
7019
|
}
|
|
7217
7020
|
return {
|
|
7218
7021
|
map_bbox: mapBox,
|
|
7219
|
-
page_bbox: pageBox
|
|
7022
|
+
page_bbox: pageBox,
|
|
7023
|
+
// round coords, for nicer 'info' display
|
|
7024
|
+
// (rounded precision should be sub-pixel)
|
|
7025
|
+
map_display_bbox: internal.getRoundedCoords(displayBox, internal.getBoundsPrecisionForDisplay(displayBox)),
|
|
7026
|
+
map_data_bbox: internal.getRoundedCoords(dataBox, internal.getBoundsPrecisionForDisplay(dataBox))
|
|
7220
7027
|
};
|
|
7221
7028
|
}
|
|
7222
7029
|
|
|
7030
|
+
function pixToCoords(bbox) {
|
|
7031
|
+
var a = ext.translatePixelCoords(bbox[0], bbox[1]);
|
|
7032
|
+
var b = ext.translatePixelCoords(bbox[2], bbox[3]);
|
|
7033
|
+
return [a[0], b[1], b[0], a[1]];
|
|
7034
|
+
}
|
|
7035
|
+
|
|
7223
7036
|
function disabled() {
|
|
7224
7037
|
return !!gui.options.disableNavigation;
|
|
7225
7038
|
}
|
|
@@ -7302,17 +7115,16 @@
|
|
|
7302
7115
|
if (!_on) return;
|
|
7303
7116
|
var b = e.page_bbox;
|
|
7304
7117
|
box.show(b[0], b[1], b[2], b[3]);
|
|
7305
|
-
updateSelection(e.
|
|
7118
|
+
updateSelection(e.map_data_bbox, true);
|
|
7306
7119
|
});
|
|
7307
7120
|
|
|
7308
7121
|
gui.on('box_drag_end', function(e) {
|
|
7309
7122
|
if (!_on) return;
|
|
7310
7123
|
box.hide();
|
|
7311
|
-
updateSelection(e.
|
|
7124
|
+
updateSelection(e.map_data_bbox);
|
|
7312
7125
|
});
|
|
7313
7126
|
|
|
7314
|
-
function updateSelection(
|
|
7315
|
-
var bbox = bboxToCoords(bboxPixels);
|
|
7127
|
+
function updateSelection(bbox, transient) {
|
|
7316
7128
|
var active = gui.model.getActiveLayer();
|
|
7317
7129
|
var ids = internal.findShapesIntersectingBBox(bbox, active.layer, active.dataset.arcs);
|
|
7318
7130
|
if (transient) {
|
|
@@ -7326,12 +7138,6 @@
|
|
|
7326
7138
|
_on = true;
|
|
7327
7139
|
}
|
|
7328
7140
|
|
|
7329
|
-
function bboxToCoords(bbox) {
|
|
7330
|
-
var a = ext.translatePixelCoords(bbox[0], bbox[1]);
|
|
7331
|
-
var b = ext.translatePixelCoords(bbox[2], bbox[3]);
|
|
7332
|
-
return [a[0], b[1], b[0], a[1]];
|
|
7333
|
-
}
|
|
7334
|
-
|
|
7335
7141
|
function turnOff() {
|
|
7336
7142
|
reset();
|
|
7337
7143
|
_on = false;
|
|
@@ -7616,7 +7422,7 @@
|
|
|
7616
7422
|
var _self = new EventDispatcher();
|
|
7617
7423
|
|
|
7618
7424
|
gui.on('interaction_mode_change', function(e) {
|
|
7619
|
-
if (e.mode
|
|
7425
|
+
if (!gui.interaction.modeUsesPopup(e.mode)) {
|
|
7620
7426
|
inspect(-1); // clear the popup
|
|
7621
7427
|
}
|
|
7622
7428
|
});
|
|
@@ -7644,7 +7450,7 @@
|
|
|
7644
7450
|
|
|
7645
7451
|
// does the attribute inspector appear on rollover
|
|
7646
7452
|
function inspecting() {
|
|
7647
|
-
return gui.interaction && gui.interaction.getMode()
|
|
7453
|
+
return gui.interaction && gui.interaction.modeUsesPopup(gui.interaction.getMode());
|
|
7648
7454
|
}
|
|
7649
7455
|
|
|
7650
7456
|
return _self;
|
|
@@ -7986,7 +7792,8 @@
|
|
|
7986
7792
|
|
|
7987
7793
|
function findVertexInsertionPoint(e) {
|
|
7988
7794
|
var target = hit.getHitTarget();
|
|
7989
|
-
|
|
7795
|
+
//// vertex insertion not supported with simplification
|
|
7796
|
+
// if (!target.arcs.isFlat()) return null;
|
|
7990
7797
|
var p = ext.translatePixelCoords(e.x, e.y);
|
|
7991
7798
|
var midpoint = findNearestMidpoint(p, e.id, target);
|
|
7992
7799
|
if (!midpoint ||
|
|
@@ -8140,10 +7947,17 @@
|
|
|
8140
7947
|
activeStyle = { // outline style for the active layer
|
|
8141
7948
|
type: 'outline',
|
|
8142
7949
|
strokeColors: [lightStroke, darkStroke],
|
|
8143
|
-
strokeWidth: 0.
|
|
7950
|
+
strokeWidth: 0.8,
|
|
8144
7951
|
dotColor: "#223",
|
|
8145
7952
|
dotSize: 1
|
|
8146
7953
|
},
|
|
7954
|
+
activeStyleDarkMode = {
|
|
7955
|
+
type: 'outline',
|
|
7956
|
+
strokeColors: [lightStroke, 'white'],
|
|
7957
|
+
strokeWidth: 0.9,
|
|
7958
|
+
dotColor: 'white',
|
|
7959
|
+
dotSize: 1
|
|
7960
|
+
},
|
|
8147
7961
|
activeStyleForLabels = {
|
|
8148
7962
|
dotColor: "rgba(250, 0, 250, 0.45)", // violet dot with transparency
|
|
8149
7963
|
dotSize: 1
|
|
@@ -8272,12 +8086,14 @@
|
|
|
8272
8086
|
return style;
|
|
8273
8087
|
}
|
|
8274
8088
|
|
|
8275
|
-
function getActiveStyle(lyr) {
|
|
8089
|
+
function getActiveStyle(lyr, darkMode) {
|
|
8276
8090
|
var style;
|
|
8277
8091
|
if (layerHasCanvasDisplayStyle(lyr)) {
|
|
8278
8092
|
style = getCanvasDisplayStyle(lyr);
|
|
8279
8093
|
} else if (internal.layerHasLabels(lyr)) {
|
|
8280
8094
|
style = getDefaultStyle(lyr, activeStyleForLabels);
|
|
8095
|
+
} else if (darkMode) {
|
|
8096
|
+
style = getDefaultStyle(lyr, activeStyleDarkMode);
|
|
8281
8097
|
} else {
|
|
8282
8098
|
style = getDefaultStyle(lyr, activeStyle);
|
|
8283
8099
|
}
|
|
@@ -8292,6 +8108,7 @@
|
|
|
8292
8108
|
strokeColor: black,
|
|
8293
8109
|
strokeWidth: 1.5,
|
|
8294
8110
|
vertices: true,
|
|
8111
|
+
vertex_overlay_color: violet,
|
|
8295
8112
|
vertex_overlay: o.hit_coordinates || null,
|
|
8296
8113
|
selected_points: o.selected_points || null,
|
|
8297
8114
|
fillColor: null
|
|
@@ -8331,13 +8148,6 @@
|
|
|
8331
8148
|
ids: ids,
|
|
8332
8149
|
overlay: true
|
|
8333
8150
|
};
|
|
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
8151
|
|
|
8342
8152
|
if (layerHasCanvasDisplayStyle(lyr)) {
|
|
8343
8153
|
if (geomType == 'point') {
|
|
@@ -8448,6 +8258,7 @@
|
|
|
8448
8258
|
var _scale = 1,
|
|
8449
8259
|
_cx, _cy, // center in geographic units
|
|
8450
8260
|
_contentBounds,
|
|
8261
|
+
_strictBounds, // full extent must fit inside, if set
|
|
8451
8262
|
_self = this,
|
|
8452
8263
|
_frame;
|
|
8453
8264
|
|
|
@@ -8531,15 +8342,25 @@
|
|
|
8531
8342
|
};
|
|
8532
8343
|
|
|
8533
8344
|
// Update the extent of 'full' zoom without navigating the current view
|
|
8534
|
-
|
|
8345
|
+
//
|
|
8346
|
+
this.setBounds = function(contentBounds, strictBounds) {
|
|
8347
|
+
var b = contentBounds;
|
|
8535
8348
|
var prev = _contentBounds;
|
|
8536
8349
|
if (!b.hasBounds()) return; // kludge
|
|
8350
|
+
if (strictBounds) {
|
|
8351
|
+
_strictBounds = Array.isArray(strictBounds) ? new Bounds(strictBounds) : strictBounds;
|
|
8352
|
+
} else {
|
|
8353
|
+
_strictBounds = null;
|
|
8354
|
+
}
|
|
8537
8355
|
_contentBounds = _frame ? b : padBounds(b, 4); // padding if not in frame mode
|
|
8356
|
+
if (_strictBounds) {
|
|
8357
|
+
_contentBounds = fitIn(_contentBounds, _strictBounds);
|
|
8358
|
+
}
|
|
8538
8359
|
if (prev) {
|
|
8539
8360
|
_scale = _scale * fillOut(_contentBounds).width() / fillOut(prev).width();
|
|
8540
8361
|
} else {
|
|
8541
|
-
_cx =
|
|
8542
|
-
_cy =
|
|
8362
|
+
_cx = _contentBounds.centerX();
|
|
8363
|
+
_cy = _contentBounds.centerY();
|
|
8543
8364
|
}
|
|
8544
8365
|
};
|
|
8545
8366
|
|
|
@@ -8568,12 +8389,33 @@
|
|
|
8568
8389
|
|
|
8569
8390
|
function recenter(cx, cy, scale, data) {
|
|
8570
8391
|
scale = scale ? limitScale(scale) : _scale;
|
|
8571
|
-
if (
|
|
8572
|
-
|
|
8573
|
-
|
|
8574
|
-
|
|
8575
|
-
|
|
8392
|
+
if (cx == _cx && cy == _cy && scale == _scale) return;
|
|
8393
|
+
navigate(cx, cy, scale);
|
|
8394
|
+
onChange(data);
|
|
8395
|
+
}
|
|
8396
|
+
|
|
8397
|
+
function navigate(cx, cy, scale) {
|
|
8398
|
+
if (_strictBounds) {
|
|
8399
|
+
var full = fillOut(_contentBounds);
|
|
8400
|
+
var minScale = full.height() / _strictBounds.height();
|
|
8401
|
+
if (scale < minScale) {
|
|
8402
|
+
var dx = cx - _cx;
|
|
8403
|
+
cx = _cx + dx * (minScale - _scale) / (scale - _scale);
|
|
8404
|
+
scale = minScale;
|
|
8405
|
+
}
|
|
8406
|
+
var dist = full.height() / 2 / scale;
|
|
8407
|
+
var ymax = _strictBounds.ymax - dist;
|
|
8408
|
+
var ymin = _strictBounds.ymin + dist;
|
|
8409
|
+
if (cy > ymax ) {
|
|
8410
|
+
cy = ymax;
|
|
8411
|
+
}
|
|
8412
|
+
if (cy < ymin) {
|
|
8413
|
+
cy = ymin;
|
|
8414
|
+
}
|
|
8576
8415
|
}
|
|
8416
|
+
_cx = cx;
|
|
8417
|
+
_cy = cy;
|
|
8418
|
+
_scale = scale;
|
|
8577
8419
|
}
|
|
8578
8420
|
|
|
8579
8421
|
function onChange(data) {
|
|
@@ -8600,15 +8442,19 @@
|
|
|
8600
8442
|
}
|
|
8601
8443
|
|
|
8602
8444
|
function calcBounds(cx, cy, scale) {
|
|
8603
|
-
var bounds, w, h;
|
|
8445
|
+
var full, bounds, w, h;
|
|
8604
8446
|
if (_frame) {
|
|
8605
|
-
|
|
8447
|
+
full = fillOutFrameBounds(_frame);
|
|
8606
8448
|
} else {
|
|
8607
|
-
|
|
8449
|
+
full = fillOut(_contentBounds);
|
|
8608
8450
|
}
|
|
8609
|
-
|
|
8610
|
-
|
|
8611
|
-
|
|
8451
|
+
if (_strictBounds) {
|
|
8452
|
+
full = fitIn(full, _strictBounds);
|
|
8453
|
+
}
|
|
8454
|
+
w = full.width() / scale;
|
|
8455
|
+
h = full.height() / scale;
|
|
8456
|
+
bounds = new Bounds(cx - w/2, cy - h/2, cx + w/2, cy + h/2);
|
|
8457
|
+
return bounds;
|
|
8612
8458
|
}
|
|
8613
8459
|
|
|
8614
8460
|
// Calculate viewport bounds from frame data
|
|
@@ -8636,6 +8482,21 @@
|
|
|
8636
8482
|
return b;
|
|
8637
8483
|
}
|
|
8638
8484
|
|
|
8485
|
+
function fitIn(b, b2) {
|
|
8486
|
+
// only fitting vertical extent
|
|
8487
|
+
// (currently only used in basemap view to enforce Mapbox's vertical limits)
|
|
8488
|
+
if (b.height() > b2.height()) {
|
|
8489
|
+
b.scale(b2.height() / b.height());
|
|
8490
|
+
}
|
|
8491
|
+
if (b.ymin < b2.ymin) {
|
|
8492
|
+
b.shift(0, b2.ymin - b.ymin);
|
|
8493
|
+
}
|
|
8494
|
+
if (b.ymax > b2.ymax) {
|
|
8495
|
+
b.shift(0, b2.ymax - b.ymax);
|
|
8496
|
+
}
|
|
8497
|
+
return b;
|
|
8498
|
+
}
|
|
8499
|
+
|
|
8639
8500
|
// Pad bounds vertically or horizontally to match viewport aspect ratio
|
|
8640
8501
|
function fillOut(b) {
|
|
8641
8502
|
var wpix = _position.width(),
|
|
@@ -8811,13 +8672,17 @@
|
|
|
8811
8672
|
}
|
|
8812
8673
|
}
|
|
8813
8674
|
}
|
|
8675
|
+
_ctx.fill();
|
|
8676
|
+
_ctx.closePath();
|
|
8814
8677
|
|
|
8815
8678
|
if (style.vertex_overlay) {
|
|
8679
|
+
_ctx.beginPath();
|
|
8680
|
+
_ctx.fillStyle = style.vertex_overlay_color || 'black';
|
|
8816
8681
|
p = style.vertex_overlay;
|
|
8817
|
-
drawCircle(p[0] * t.mx + t.bx, p[1] * t.my + t.by, radius * 1.
|
|
8682
|
+
drawCircle(p[0] * t.mx + t.bx, p[1] * t.my + t.by, radius * 1.6, _ctx);
|
|
8683
|
+
_ctx.fill();
|
|
8684
|
+
_ctx.closePath();
|
|
8818
8685
|
}
|
|
8819
|
-
_ctx.fill();
|
|
8820
|
-
_ctx.closePath();
|
|
8821
8686
|
};
|
|
8822
8687
|
|
|
8823
8688
|
// Optimized to draw paths in same-style batches (faster Canvas drawing)
|
|
@@ -9476,7 +9341,7 @@
|
|
|
9476
9341
|
var popup = gui.container.findChild('.box-tool-options');
|
|
9477
9342
|
var coords = popup.findChild('.box-coords');
|
|
9478
9343
|
var _on = false;
|
|
9479
|
-
var
|
|
9344
|
+
var bboxData;
|
|
9480
9345
|
|
|
9481
9346
|
var infoBtn = new SimpleButton(popup.findChild('.info-btn')).on('click', function() {
|
|
9482
9347
|
if (coords.visible()) hideCoords(); else showCoords();
|
|
@@ -9492,131 +9357,374 @@
|
|
|
9492
9357
|
// reset();
|
|
9493
9358
|
// });
|
|
9494
9359
|
|
|
9495
|
-
// Removing button for creating a layer containing a single rectangle.
|
|
9496
|
-
// You can get the bbox with the Info button and create a rectangle in the console
|
|
9497
|
-
// using -rectangle bbox=<coordinates>
|
|
9498
|
-
// new SimpleButton(popup.findChild('.rectangle-btn')).on('click', function() {
|
|
9499
|
-
// runCommand('-rectangle bbox=' + bbox.join(','));
|
|
9500
|
-
// });
|
|
9360
|
+
// Removing button for creating a layer containing a single rectangle.
|
|
9361
|
+
// You can get the bbox with the Info button and create a rectangle in the console
|
|
9362
|
+
// using -rectangle bbox=<coordinates>
|
|
9363
|
+
// new SimpleButton(popup.findChild('.rectangle-btn')).on('click', function() {
|
|
9364
|
+
// runCommand('-rectangle bbox=' + bbox.join(','));
|
|
9365
|
+
// });
|
|
9366
|
+
|
|
9367
|
+
new SimpleButton(popup.findChild('.select-btn')).on('click', function() {
|
|
9368
|
+
gui.enterMode('selection_tool');
|
|
9369
|
+
gui.interaction.setMode('selection');
|
|
9370
|
+
// kludge to pass bbox to the selection tool
|
|
9371
|
+
gui.dispatchEvent('box_drag_end', bboxData);
|
|
9372
|
+
});
|
|
9373
|
+
|
|
9374
|
+
new SimpleButton(popup.findChild('.clip-btn')).on('click', function() {
|
|
9375
|
+
runCommand('-clip bbox2=' + bboxData.map_data_bbox.join(','));
|
|
9376
|
+
});
|
|
9377
|
+
|
|
9378
|
+
gui.addMode('box_tool', turnOn, turnOff);
|
|
9379
|
+
|
|
9380
|
+
gui.on('interaction_mode_change', function(e) {
|
|
9381
|
+
if (e.mode === 'box') {
|
|
9382
|
+
gui.enterMode('box_tool');
|
|
9383
|
+
} else if (gui.getMode() == 'box_tool') {
|
|
9384
|
+
gui.clearMode();
|
|
9385
|
+
}
|
|
9386
|
+
});
|
|
9387
|
+
|
|
9388
|
+
// Update the visible rectangle when the map view changes
|
|
9389
|
+
// (e.g. during zooming or panning)
|
|
9390
|
+
ext.on('change', function() {
|
|
9391
|
+
if (!_on || !box.visible() || !bboxData) return;
|
|
9392
|
+
var b = coordsToPix(bboxData.map_display_bbox);
|
|
9393
|
+
var pos = ext.position();
|
|
9394
|
+
var dx = pos.pageX,
|
|
9395
|
+
dy = pos.pageY;
|
|
9396
|
+
box.show(b[0] + dx, b[1] + dy, b[2] + dx, b[3] + dy);
|
|
9397
|
+
});
|
|
9398
|
+
|
|
9399
|
+
gui.on('box_drag_start', function() {
|
|
9400
|
+
box.classed('zooming', inZoomMode());
|
|
9401
|
+
hideCoords();
|
|
9402
|
+
});
|
|
9403
|
+
|
|
9404
|
+
gui.on('box_drag', function(e) {
|
|
9405
|
+
var b = e.page_bbox;
|
|
9406
|
+
bboxData = e.data;
|
|
9407
|
+
if (_on || inZoomMode()) {
|
|
9408
|
+
box.show(b[0], b[1], b[2], b[3]);
|
|
9409
|
+
}
|
|
9410
|
+
});
|
|
9411
|
+
|
|
9412
|
+
gui.on('box_drag_end', function(e) {
|
|
9413
|
+
bboxData = e.data;
|
|
9414
|
+
if (inZoomMode()) {
|
|
9415
|
+
box.hide();
|
|
9416
|
+
nav.zoomToBbox(e.map_bbox);
|
|
9417
|
+
} else if (_on) {
|
|
9418
|
+
popup.show();
|
|
9419
|
+
}
|
|
9420
|
+
});
|
|
9421
|
+
|
|
9422
|
+
function inZoomMode() {
|
|
9423
|
+
return !_on && gui.getMode() != 'selection_tool';
|
|
9424
|
+
}
|
|
9425
|
+
|
|
9426
|
+
function runCommand(cmd) {
|
|
9427
|
+
if (gui.console) {
|
|
9428
|
+
gui.console.runMapshaperCommands(cmd, function(err) {
|
|
9429
|
+
reset();
|
|
9430
|
+
});
|
|
9431
|
+
}
|
|
9432
|
+
// reset(); // TODO: exit interactive mode
|
|
9433
|
+
}
|
|
9434
|
+
|
|
9435
|
+
function showCoords() {
|
|
9436
|
+
El(infoBtn.node()).addClass('selected-btn');
|
|
9437
|
+
coords.text(bboxData.map_data_bbox.join(','));
|
|
9438
|
+
coords.show();
|
|
9439
|
+
GUI.selectElement(coords.node());
|
|
9440
|
+
}
|
|
9441
|
+
|
|
9442
|
+
function hideCoords() {
|
|
9443
|
+
El(infoBtn.node()).removeClass('selected-btn');
|
|
9444
|
+
coords.hide();
|
|
9445
|
+
}
|
|
9446
|
+
|
|
9447
|
+
function turnOn() {
|
|
9448
|
+
_on = true;
|
|
9449
|
+
}
|
|
9450
|
+
|
|
9451
|
+
function turnOff() {
|
|
9452
|
+
if (gui.interaction.getMode() == 'box') {
|
|
9453
|
+
// mode change was not initiated by interactive menu -- turn off interactivity
|
|
9454
|
+
gui.interaction.turnOff();
|
|
9455
|
+
}
|
|
9456
|
+
_on = false;
|
|
9457
|
+
reset();
|
|
9458
|
+
}
|
|
9459
|
+
|
|
9460
|
+
function reset() {
|
|
9461
|
+
box.hide();
|
|
9462
|
+
popup.hide();
|
|
9463
|
+
hideCoords();
|
|
9464
|
+
}
|
|
9465
|
+
|
|
9466
|
+
function coordsToPix(bbox) {
|
|
9467
|
+
var a = ext.translateCoords(bbox[0], bbox[1]);
|
|
9468
|
+
var b = ext.translateCoords(bbox[2], bbox[3]);
|
|
9469
|
+
return [a[0], b[1], b[0], a[1]];
|
|
9470
|
+
}
|
|
9471
|
+
|
|
9472
|
+
return self;
|
|
9473
|
+
}
|
|
9474
|
+
|
|
9475
|
+
// Create low-detail versions of large arc collections for faster rendering
|
|
9476
|
+
// at zoomed-out scales.
|
|
9477
|
+
function enhanceArcCollectionForDisplay(unfilteredArcs) {
|
|
9478
|
+
var size = unfilteredArcs.getPointCount(),
|
|
9479
|
+
filteredArcs, filteredSegLen;
|
|
9480
|
+
|
|
9481
|
+
// Only generate low-detail arcs for larger datasets
|
|
9482
|
+
if (size > 5e5) {
|
|
9483
|
+
if (!!unfilteredArcs.getVertexData().zz) {
|
|
9484
|
+
// Use precalculated simplification data for vertex filtering, if available
|
|
9485
|
+
filteredArcs = initFilteredArcs(unfilteredArcs);
|
|
9486
|
+
filteredSegLen = internal.getAvgSegment(filteredArcs);
|
|
9487
|
+
} else {
|
|
9488
|
+
// Use fast simplification as a fallback
|
|
9489
|
+
filteredSegLen = internal.getAvgSegment(unfilteredArcs) * 4;
|
|
9490
|
+
filteredArcs = internal.simplifyArcsFast(unfilteredArcs, filteredSegLen);
|
|
9491
|
+
}
|
|
9492
|
+
}
|
|
9493
|
+
|
|
9494
|
+
function initFilteredArcs(arcs) {
|
|
9495
|
+
var filterPct = 0.08;
|
|
9496
|
+
var nth = Math.ceil(arcs.getPointCount() / 5e5);
|
|
9497
|
+
var currInterval = arcs.getRetainedInterval();
|
|
9498
|
+
var filterZ = arcs.getThresholdByPct(filterPct, nth);
|
|
9499
|
+
var filteredArcs = arcs.setRetainedInterval(filterZ).getFilteredCopy();
|
|
9500
|
+
arcs.setRetainedInterval(currInterval); // reset current simplification
|
|
9501
|
+
return filteredArcs;
|
|
9502
|
+
}
|
|
9503
|
+
|
|
9504
|
+
unfilteredArcs.getScaledArcs = function(ext) {
|
|
9505
|
+
if (filteredArcs) {
|
|
9506
|
+
// match simplification of unfiltered arcs
|
|
9507
|
+
filteredArcs.setRetainedInterval(unfilteredArcs.getRetainedInterval());
|
|
9508
|
+
}
|
|
9509
|
+
// switch to filtered version of arcs at small scales
|
|
9510
|
+
var unitsPerPixel = 1/ext.getTransform().mx,
|
|
9511
|
+
useFiltering = filteredArcs && unitsPerPixel > filteredSegLen * 1.5;
|
|
9512
|
+
return useFiltering ? filteredArcs : unfilteredArcs;
|
|
9513
|
+
};
|
|
9514
|
+
}
|
|
9515
|
+
|
|
9516
|
+
function getDisplayLayerForTable(table) {
|
|
9517
|
+
var n = table.size(),
|
|
9518
|
+
cellWidth = 12,
|
|
9519
|
+
cellHeight = 5,
|
|
9520
|
+
gutter = 6,
|
|
9521
|
+
arcs = [],
|
|
9522
|
+
shapes = [],
|
|
9523
|
+
aspectRatio = 1.1,
|
|
9524
|
+
x, y, col, row, blockSize;
|
|
9525
|
+
|
|
9526
|
+
if (n > 10000) {
|
|
9527
|
+
arcs = null;
|
|
9528
|
+
gutter = 0;
|
|
9529
|
+
cellWidth = 4;
|
|
9530
|
+
cellHeight = 4;
|
|
9531
|
+
aspectRatio = 1.45;
|
|
9532
|
+
} else if (n > 5000) {
|
|
9533
|
+
cellWidth = 5;
|
|
9534
|
+
gutter = 3;
|
|
9535
|
+
aspectRatio = 1.45;
|
|
9536
|
+
} else if (n > 1000) {
|
|
9537
|
+
gutter = 3;
|
|
9538
|
+
cellWidth = 8;
|
|
9539
|
+
aspectRatio = 1.3;
|
|
9540
|
+
}
|
|
9541
|
+
|
|
9542
|
+
if (n < 25) {
|
|
9543
|
+
blockSize = n;
|
|
9544
|
+
} else {
|
|
9545
|
+
blockSize = Math.sqrt(n * (cellWidth + gutter) / cellHeight / aspectRatio) | 0;
|
|
9546
|
+
}
|
|
9501
9547
|
|
|
9502
|
-
|
|
9503
|
-
|
|
9504
|
-
|
|
9505
|
-
|
|
9506
|
-
|
|
9507
|
-
|
|
9548
|
+
for (var i=0; i<n; i++) {
|
|
9549
|
+
row = i % blockSize;
|
|
9550
|
+
col = Math.floor(i / blockSize);
|
|
9551
|
+
x = col * (cellWidth + gutter);
|
|
9552
|
+
y = cellHeight * (blockSize - row);
|
|
9553
|
+
if (arcs) {
|
|
9554
|
+
arcs.push(getArc(x, y, cellWidth, cellHeight));
|
|
9555
|
+
shapes.push([[i]]);
|
|
9556
|
+
} else {
|
|
9557
|
+
shapes.push([[x, y]]);
|
|
9558
|
+
}
|
|
9559
|
+
}
|
|
9508
9560
|
|
|
9509
|
-
|
|
9510
|
-
|
|
9511
|
-
}
|
|
9561
|
+
function getArc(x, y, w, h) {
|
|
9562
|
+
return [[x, y], [x + w, y], [x + w, y - h], [x, y - h], [x, y]];
|
|
9563
|
+
}
|
|
9512
9564
|
|
|
9513
|
-
|
|
9565
|
+
return {
|
|
9566
|
+
layer: {
|
|
9567
|
+
geometry_type: arcs ? 'polygon' : 'point',
|
|
9568
|
+
shapes: shapes,
|
|
9569
|
+
data: table
|
|
9570
|
+
},
|
|
9571
|
+
arcs: arcs ? new internal.ArcCollection(arcs) : null
|
|
9572
|
+
};
|
|
9573
|
+
}
|
|
9514
9574
|
|
|
9515
|
-
|
|
9516
|
-
|
|
9517
|
-
|
|
9518
|
-
|
|
9519
|
-
|
|
9520
|
-
|
|
9521
|
-
|
|
9575
|
+
// displayCRS: CRS to use for display, or null (which clears any current display CRS)
|
|
9576
|
+
function projectDisplayLayer(lyr, displayCRS) {
|
|
9577
|
+
var crsInfo = getDatasetCrsInfo(lyr.source.dataset);
|
|
9578
|
+
var sourceCRS = crsInfo.crs;
|
|
9579
|
+
var lyr2;
|
|
9580
|
+
//if (!lyr.geographic || !sourceCRS) {
|
|
9581
|
+
// let getDisplayLayer() handle case of unprojectable source
|
|
9582
|
+
if (!lyr.geographic) {
|
|
9583
|
+
return lyr;
|
|
9584
|
+
}
|
|
9585
|
+
if (lyr.dynamic_crs && internal.crsAreEqual(sourceCRS, lyr.dynamic_crs)) {
|
|
9586
|
+
return lyr;
|
|
9587
|
+
}
|
|
9588
|
+
lyr2 = getDisplayLayer(lyr.source.layer, lyr.source.dataset, {crs: displayCRS});
|
|
9589
|
+
// kludge: copy projection-related properties to original layer
|
|
9590
|
+
lyr.dynamic_crs = lyr2.dynamic_crs;
|
|
9591
|
+
lyr.layer = lyr2.layer;
|
|
9522
9592
|
|
|
9523
|
-
|
|
9524
|
-
|
|
9525
|
-
|
|
9526
|
-
|
|
9527
|
-
|
|
9528
|
-
|
|
9529
|
-
|
|
9530
|
-
|
|
9531
|
-
|
|
9532
|
-
});
|
|
9593
|
+
if (lyr.style && lyr.style.ids) {
|
|
9594
|
+
// re-apply layer filter
|
|
9595
|
+
lyr.layer = filterLayerByIds(lyr.layer, lyr.style.ids);
|
|
9596
|
+
}
|
|
9597
|
+
lyr.invertPoint = lyr2.invertPoint;
|
|
9598
|
+
lyr.projectPoint = lyr2.projectPoint;
|
|
9599
|
+
lyr.bounds = lyr2.bounds;
|
|
9600
|
+
lyr.arcs = lyr2.arcs;
|
|
9601
|
+
}
|
|
9533
9602
|
|
|
9534
|
-
gui.on('box_drag_start', function() {
|
|
9535
|
-
box.classed('zooming', inZoomMode());
|
|
9536
|
-
hideCoords();
|
|
9537
|
-
});
|
|
9538
9603
|
|
|
9539
|
-
|
|
9540
|
-
|
|
9541
|
-
|
|
9542
|
-
|
|
9543
|
-
|
|
9544
|
-
|
|
9545
|
-
|
|
9546
|
-
|
|
9604
|
+
// Wrap a layer in an object along with information needed for rendering
|
|
9605
|
+
function getDisplayLayer(layer, dataset, opts) {
|
|
9606
|
+
var obj = {
|
|
9607
|
+
layer: null,
|
|
9608
|
+
arcs: null,
|
|
9609
|
+
// display_arcs: null,
|
|
9610
|
+
style: null,
|
|
9611
|
+
invertPoint: null,
|
|
9612
|
+
projectPoint: null,
|
|
9613
|
+
source: {
|
|
9614
|
+
layer: layer,
|
|
9615
|
+
dataset: dataset
|
|
9616
|
+
},
|
|
9617
|
+
empty: internal.getFeatureCount(layer) === 0
|
|
9618
|
+
};
|
|
9547
9619
|
|
|
9548
|
-
|
|
9549
|
-
|
|
9550
|
-
|
|
9551
|
-
|
|
9552
|
-
|
|
9553
|
-
box.hide();
|
|
9554
|
-
nav.zoomToBbox(bboxPixels);
|
|
9555
|
-
} else if (_on) {
|
|
9556
|
-
popup.show();
|
|
9557
|
-
}
|
|
9558
|
-
});
|
|
9620
|
+
var displayCRS = opts.crs || null;
|
|
9621
|
+
// display arcs may have been generated when another layer in the dataset was converted for display... re-use if available
|
|
9622
|
+
var displayArcs = dataset.displayArcs || null;
|
|
9623
|
+
var sourceCRS;
|
|
9624
|
+
var emptyArcs;
|
|
9559
9625
|
|
|
9560
|
-
|
|
9561
|
-
|
|
9626
|
+
if (displayCRS && layer.geometry_type) {
|
|
9627
|
+
var crsInfo = getDatasetCrsInfo(dataset);
|
|
9628
|
+
if (crsInfo.error) {
|
|
9629
|
+
// unprojectable dataset -- return empty layer
|
|
9630
|
+
obj.empty = true;
|
|
9631
|
+
obj.geographic = true;
|
|
9632
|
+
} else {
|
|
9633
|
+
sourceCRS = crsInfo.crs;
|
|
9634
|
+
}
|
|
9562
9635
|
}
|
|
9563
9636
|
|
|
9564
|
-
|
|
9565
|
-
|
|
9566
|
-
|
|
9567
|
-
|
|
9568
|
-
|
|
9637
|
+
// Assume that dataset.displayArcs is in the display CRS
|
|
9638
|
+
// (it must be deleted upstream if reprojection is needed)
|
|
9639
|
+
if (!obj.empty && dataset.arcs && !displayArcs) {
|
|
9640
|
+
// project arcs, if needed
|
|
9641
|
+
if (needReprojectionForDisplay(sourceCRS, displayCRS)) {
|
|
9642
|
+
displayArcs = projectArcsForDisplay(dataset.arcs, sourceCRS, displayCRS);
|
|
9643
|
+
} else {
|
|
9644
|
+
// Use original arcs for display if there is no dynamic reprojection
|
|
9645
|
+
displayArcs = dataset.arcs;
|
|
9569
9646
|
}
|
|
9570
|
-
// reset(); // TODO: exit interactive mode
|
|
9571
|
-
}
|
|
9572
9647
|
|
|
9573
|
-
|
|
9574
|
-
|
|
9575
|
-
coords.text(bboxDataCoords.join(','));
|
|
9576
|
-
coords.show();
|
|
9577
|
-
GUI.selectElement(coords.node());
|
|
9648
|
+
enhanceArcCollectionForDisplay(displayArcs);
|
|
9649
|
+
dataset.displayArcs = displayArcs; // stash these in the dataset for other layers to use
|
|
9578
9650
|
}
|
|
9579
9651
|
|
|
9580
|
-
|
|
9581
|
-
|
|
9582
|
-
|
|
9652
|
+
if (internal.layerHasFurniture(layer)) {
|
|
9653
|
+
obj.furniture = true;
|
|
9654
|
+
obj.furniture_type = internal.getFurnitureLayerType(layer);
|
|
9655
|
+
obj.layer = layer;
|
|
9656
|
+
// treating furniture layers (other than frame) as tabular for now,
|
|
9657
|
+
// so there is something to show if they are selected
|
|
9658
|
+
obj.tabular = obj.furniture_type != 'frame';
|
|
9659
|
+
} else if (obj.empty) {
|
|
9660
|
+
obj.layer = {shapes: []}; // ideally we should avoid empty layers
|
|
9661
|
+
} else if (!layer.geometry_type) {
|
|
9662
|
+
obj.tabular = true;
|
|
9663
|
+
} else {
|
|
9664
|
+
obj.geographic = true;
|
|
9665
|
+
obj.layer = layer;
|
|
9666
|
+
obj.arcs = displayArcs;
|
|
9583
9667
|
}
|
|
9584
9668
|
|
|
9585
|
-
|
|
9586
|
-
|
|
9669
|
+
if (obj.tabular) {
|
|
9670
|
+
utils$1.extend(obj, getDisplayLayerForTable(layer.data));
|
|
9587
9671
|
}
|
|
9588
9672
|
|
|
9589
|
-
|
|
9590
|
-
|
|
9591
|
-
|
|
9592
|
-
|
|
9673
|
+
// dynamic reprojection (arcs were already reprojected above)
|
|
9674
|
+
if (obj.geographic && needReprojectionForDisplay(sourceCRS, displayCRS)) {
|
|
9675
|
+
obj.dynamic_crs = displayCRS;
|
|
9676
|
+
obj.invertPoint = internal.getProjTransform2(displayCRS, sourceCRS);
|
|
9677
|
+
obj.projectPoint = internal.getProjTransform2(sourceCRS, displayCRS);
|
|
9678
|
+
if (internal.layerHasPoints(layer)) {
|
|
9679
|
+
obj.layer = projectPointsForDisplay(layer, sourceCRS, displayCRS);
|
|
9680
|
+
} else if (internal.layerHasPaths(layer)) {
|
|
9681
|
+
emptyArcs = findEmptyArcs(displayArcs);
|
|
9682
|
+
if (emptyArcs.length > 0) {
|
|
9683
|
+
// Don't try to draw paths containing coordinates that failed to project
|
|
9684
|
+
obj.layer = internal.filterPathLayerByArcIds(obj.layer, emptyArcs);
|
|
9685
|
+
}
|
|
9593
9686
|
}
|
|
9594
|
-
_on = false;
|
|
9595
|
-
reset();
|
|
9596
9687
|
}
|
|
9597
9688
|
|
|
9598
|
-
|
|
9599
|
-
|
|
9600
|
-
|
|
9601
|
-
hideCoords();
|
|
9602
|
-
}
|
|
9689
|
+
obj.bounds = getDisplayBounds(obj.layer, obj.arcs);
|
|
9690
|
+
return obj;
|
|
9691
|
+
}
|
|
9603
9692
|
|
|
9604
|
-
|
|
9605
|
-
|
|
9606
|
-
|
|
9607
|
-
|
|
9608
|
-
|
|
9609
|
-
|
|
9610
|
-
|
|
9693
|
+
|
|
9694
|
+
function getDisplayBounds(lyr, arcs) {
|
|
9695
|
+
var arcBounds = arcs ? arcs.getBounds() : new Bounds(),
|
|
9696
|
+
bounds = arcBounds, // default display extent: all arcs in the dataset
|
|
9697
|
+
lyrBounds;
|
|
9698
|
+
|
|
9699
|
+
if (lyr.geometry_type == 'point') {
|
|
9700
|
+
lyrBounds = internal.getLayerBounds(lyr);
|
|
9701
|
+
if (lyrBounds && lyrBounds.hasBounds()) {
|
|
9702
|
+
if (lyrBounds.area() > 0 || !arcBounds.hasBounds()) {
|
|
9703
|
+
bounds = lyrBounds;
|
|
9704
|
+
} else {
|
|
9705
|
+
// if a point layer has no extent (e.g. contains only a single point),
|
|
9706
|
+
// then merge with arc bounds, to place the point in context.
|
|
9707
|
+
bounds = arcBounds.mergeBounds(lyrBounds);
|
|
9708
|
+
}
|
|
9709
|
+
}
|
|
9611
9710
|
}
|
|
9612
9711
|
|
|
9613
|
-
|
|
9614
|
-
|
|
9615
|
-
var b = ext.translateCoords(bbox[2], bbox[3]);
|
|
9616
|
-
return [a[0], b[1], b[0], a[1]];
|
|
9712
|
+
if (!bounds || !bounds.hasBounds()) { // empty layer
|
|
9713
|
+
bounds = new Bounds();
|
|
9617
9714
|
}
|
|
9715
|
+
return bounds;
|
|
9716
|
+
}
|
|
9618
9717
|
|
|
9619
|
-
|
|
9718
|
+
// Returns an array of ids of empty arcs (arcs can be set to empty if errors occur while projecting them)
|
|
9719
|
+
function findEmptyArcs(arcs) {
|
|
9720
|
+
var nn = arcs.getVertexData().nn;
|
|
9721
|
+
var ids = [];
|
|
9722
|
+
for (var i=0, n=nn.length; i<n; i++) {
|
|
9723
|
+
if (nn[i] === 0) {
|
|
9724
|
+
ids.push(i);
|
|
9725
|
+
}
|
|
9726
|
+
}
|
|
9727
|
+
return ids;
|
|
9620
9728
|
}
|
|
9621
9729
|
|
|
9622
9730
|
function loadScript(url, cb) {
|
|
@@ -9657,10 +9765,6 @@
|
|
|
9657
9765
|
|
|
9658
9766
|
function init() {
|
|
9659
9767
|
gui.addMode('basemap', turnOn, turnOff, basemapBtn);
|
|
9660
|
-
// model.on('select', function() {
|
|
9661
|
-
// TODO: hide basemap
|
|
9662
|
-
// if (gui.getMode() == 'basemap') gui.clearMode();
|
|
9663
|
-
// });
|
|
9664
9768
|
|
|
9665
9769
|
new SimpleButton(menu.findChild('.close-btn')).on('click', function() {
|
|
9666
9770
|
gui.clearMode();
|
|
@@ -9684,6 +9788,9 @@
|
|
|
9684
9788
|
|
|
9685
9789
|
function updateStyle(style) {
|
|
9686
9790
|
activeStyle = style || null;
|
|
9791
|
+
// TODO: consider enabling this
|
|
9792
|
+
// Make sure that the selected layer style gets updated in gui-map.js
|
|
9793
|
+
// gui.state.dark_basemap = style && style.dark || false;
|
|
9687
9794
|
if (!style) {
|
|
9688
9795
|
gui.map.setDisplayCRS(null);
|
|
9689
9796
|
hide();
|
|
@@ -9703,11 +9810,12 @@
|
|
|
9703
9810
|
|
|
9704
9811
|
function turnOn() {
|
|
9705
9812
|
var activeLyr = gui.model.getActiveLayer();
|
|
9706
|
-
var
|
|
9813
|
+
var info = getDatasetCrsInfo(activeLyr.dataset);
|
|
9814
|
+
var dataCRS = info.crs || null;
|
|
9707
9815
|
var displayCRS = gui.map.getDisplayCRS();
|
|
9708
9816
|
var warning;
|
|
9709
9817
|
|
|
9710
|
-
if (!crsIsUsable(displayCRS) || !crsIsUsable(dataCRS)) {
|
|
9818
|
+
if (!dataCRS || !displayCRS || !crsIsUsable(displayCRS) || !crsIsUsable(dataCRS)) {
|
|
9711
9819
|
warning = 'The current layer is not compatible with the projection used by the basemaps.';
|
|
9712
9820
|
basemapWarning.html(warning).show();
|
|
9713
9821
|
basemapNote.hide();
|
|
@@ -9772,16 +9880,25 @@
|
|
|
9772
9880
|
});
|
|
9773
9881
|
}
|
|
9774
9882
|
|
|
9883
|
+
// @bbox: latlon bounding box of current map extent
|
|
9775
9884
|
function checkBounds(bbox) {
|
|
9885
|
+
var mpp = ext.getBounds().width() / ext.width();
|
|
9886
|
+
var z = scaleToZoom(mpp);
|
|
9776
9887
|
var msg;
|
|
9777
|
-
if (bbox[1] >= -85 && bbox[3] <= 85) {
|
|
9888
|
+
if (bbox[1] >= -85 && bbox[3] <= 85 && z <= 20) {
|
|
9778
9889
|
extentNote.hide();
|
|
9779
9890
|
return true;
|
|
9780
9891
|
}
|
|
9781
|
-
if (
|
|
9782
|
-
|
|
9783
|
-
else
|
|
9784
|
-
|
|
9892
|
+
if (z > 20) {
|
|
9893
|
+
msg = 'zoom out';
|
|
9894
|
+
} else if (bbox[1] > 0) {
|
|
9895
|
+
msg = 'pan south';
|
|
9896
|
+
} else if (bbox[3] < 0) {
|
|
9897
|
+
msg = 'pan north';
|
|
9898
|
+
} else {
|
|
9899
|
+
msg = msg = 'zoom in';
|
|
9900
|
+
}
|
|
9901
|
+
extentNote.html(msg + ' to see the basemap').show();
|
|
9785
9902
|
return false;
|
|
9786
9903
|
}
|
|
9787
9904
|
|
|
@@ -9897,11 +10014,10 @@
|
|
|
9897
10014
|
};
|
|
9898
10015
|
|
|
9899
10016
|
this.getDisplayCRS = function() {
|
|
9900
|
-
|
|
9901
|
-
if (_activeLyr
|
|
9902
|
-
|
|
9903
|
-
|
|
9904
|
-
return crs || null;
|
|
10017
|
+
if (!_activeLyr || !_activeLyr.geographic) return null;
|
|
10018
|
+
if (_activeLyr.dynamic_crs) return _activeLyr.dynamic_crs;
|
|
10019
|
+
var info = getDatasetCrsInfo(_activeLyr.source.dataset);
|
|
10020
|
+
return info.crs || null;
|
|
9905
10021
|
};
|
|
9906
10022
|
|
|
9907
10023
|
this.getExtent = function() {return _ext;};
|
|
@@ -9974,7 +10090,8 @@
|
|
|
9974
10090
|
}
|
|
9975
10091
|
|
|
9976
10092
|
_activeLyr = getDisplayLayer(e.layer, e.dataset, getDisplayOptions());
|
|
9977
|
-
_activeLyr.style = getActiveStyle(_activeLyr.layer);
|
|
10093
|
+
_activeLyr.style = getActiveStyle(_activeLyr.layer, gui.state.dark_basemap);
|
|
10094
|
+
|
|
9978
10095
|
_activeLyr.active = true;
|
|
9979
10096
|
// if (_inspector) _inspector.updateLayer(_activeLyr);
|
|
9980
10097
|
_hit.setLayer(_activeLyr);
|
|
@@ -9993,7 +10110,6 @@
|
|
|
9993
10110
|
needReset = mapNeedsReset(fullBounds, _fullBounds, _ext.getBounds(), e.flags);
|
|
9994
10111
|
}
|
|
9995
10112
|
|
|
9996
|
-
|
|
9997
10113
|
if (isFrameView()) {
|
|
9998
10114
|
_nav.setZoomFactor(0.05); // slow zooming way down to allow fine-tuning frame placement // 0.03
|
|
9999
10115
|
_ext.setFrame(getFullBounds()); // TODO: remove redundancy with drawLayers()
|
|
@@ -10001,7 +10117,7 @@
|
|
|
10001
10117
|
} else {
|
|
10002
10118
|
_nav.setZoomFactor(1);
|
|
10003
10119
|
}
|
|
10004
|
-
_ext.setBounds(fullBounds); // update 'home' button extent
|
|
10120
|
+
_ext.setBounds(fullBounds, getStrictBounds()); // update 'home' button extent
|
|
10005
10121
|
_fullBounds = fullBounds;
|
|
10006
10122
|
if (needReset) {
|
|
10007
10123
|
_ext.reset();
|
|
@@ -10091,6 +10207,13 @@
|
|
|
10091
10207
|
_ext.reset();
|
|
10092
10208
|
}
|
|
10093
10209
|
|
|
10210
|
+
function getStrictBounds() {
|
|
10211
|
+
if (internal.isWebMercator(map.getDisplayCRS())) {
|
|
10212
|
+
return getMapboxBounds();
|
|
10213
|
+
}
|
|
10214
|
+
return null;
|
|
10215
|
+
}
|
|
10216
|
+
|
|
10094
10217
|
function getFullBounds() {
|
|
10095
10218
|
var b = new Bounds();
|
|
10096
10219
|
var marginPct = 0.025;
|
|
@@ -10257,7 +10380,7 @@
|
|
|
10257
10380
|
if (layersMayHaveChanged) {
|
|
10258
10381
|
// kludge to handle layer visibility toggling
|
|
10259
10382
|
_ext.setFrame(isPreviewView() ? getFrameData() : null);
|
|
10260
|
-
_ext.setBounds(getFullBounds());
|
|
10383
|
+
_ext.setBounds(getFullBounds(), getStrictBounds());
|
|
10261
10384
|
updateLayerStyles(contentLayers);
|
|
10262
10385
|
updateLayerStackOrder(model.getLayers());// update stack_id property of all layers
|
|
10263
10386
|
}
|
|
@@ -10275,6 +10398,40 @@
|
|
|
10275
10398
|
}
|
|
10276
10399
|
}
|
|
10277
10400
|
|
|
10401
|
+
// This is a new way to handle compatibility problems between
|
|
10402
|
+
// interactive editing modes and other interface modes
|
|
10403
|
+
// (by default, interactive modes stay on when, e.g., the user clicks
|
|
10404
|
+
// "Export" or "Console").
|
|
10405
|
+
//
|
|
10406
|
+
function initModeRules(gui) {
|
|
10407
|
+
|
|
10408
|
+
gui.on('interaction_mode_change', function(e) {
|
|
10409
|
+
var imode = e.mode;
|
|
10410
|
+
var mode = gui.getMode();
|
|
10411
|
+
|
|
10412
|
+
// simplify and vertex editing are not compatible
|
|
10413
|
+
if (imode == 'vertices') {
|
|
10414
|
+
flattenArcs(gui.map.getActiveLayer());
|
|
10415
|
+
|
|
10416
|
+
if (mode == 'simplify') {
|
|
10417
|
+
gui.clearMode(); // exit simplification
|
|
10418
|
+
}
|
|
10419
|
+
|
|
10420
|
+
}
|
|
10421
|
+
|
|
10422
|
+
});
|
|
10423
|
+
|
|
10424
|
+
gui.on('mode', function(e) {
|
|
10425
|
+
var mode = e.name;
|
|
10426
|
+
var imode = gui.interaction.getMode();
|
|
10427
|
+
|
|
10428
|
+
// simplify and vertex editing are not compatible
|
|
10429
|
+
if (mode == 'simplify' && imode == 'vertices') {
|
|
10430
|
+
gui.interaction.turnOff();
|
|
10431
|
+
}
|
|
10432
|
+
});
|
|
10433
|
+
}
|
|
10434
|
+
|
|
10278
10435
|
function GuiInstance(container, opts) {
|
|
10279
10436
|
var gui = new ModeSwitcher();
|
|
10280
10437
|
opts = utils$1.extend({
|
|
@@ -10298,6 +10455,8 @@
|
|
|
10298
10455
|
gui.undo = new Undo(gui);
|
|
10299
10456
|
gui.state = {};
|
|
10300
10457
|
|
|
10458
|
+
initModeRules(gui);
|
|
10459
|
+
|
|
10301
10460
|
gui.showProgressMessage = function(msg) {
|
|
10302
10461
|
if (!gui.progressMessage) {
|
|
10303
10462
|
gui.progressMessage = El('div').addClass('progress-message')
|
|
@@ -10322,7 +10481,7 @@
|
|
|
10322
10481
|
curr.blur();
|
|
10323
10482
|
}
|
|
10324
10483
|
GUI.__active = gui;
|
|
10325
|
-
|
|
10484
|
+
setLoggingForGUI(gui);
|
|
10326
10485
|
ImportFileProxy(gui);
|
|
10327
10486
|
WriteFilesProxy(gui);
|
|
10328
10487
|
gui.dispatchEvent('active');
|