mapshaper 0.5.101 → 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 +9 -0
- package/mapshaper.js +73 -29
- package/package.json +1 -1
- package/www/images/thumb-map.jpg +0 -0
- package/www/images/thumb-satellite.jpg +0 -0
- package/www/mapshaper-gui.js +896 -733
- package/www/mapshaper.js +73 -29
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
|
-
|
|
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
|
+
});
|
|
1805
|
+
}
|
|
1840
1806
|
|
|
1841
|
-
function
|
|
1842
|
-
|
|
1843
|
-
|
|
1807
|
+
function saveBlobToDownloadFolder(filename, blob, done) {
|
|
1808
|
+
var anchor, blobUrl;
|
|
1809
|
+
if (window.navigator.msSaveBlob) {
|
|
1810
|
+
window.navigator.msSaveBlob(blob, filename);
|
|
1811
|
+
return done();
|
|
1844
1812
|
}
|
|
1845
|
-
|
|
1846
|
-
|
|
1813
|
+
try {
|
|
1814
|
+
blobUrl = URL.createObjectURL(blob);
|
|
1815
|
+
} catch(e) {
|
|
1816
|
+
done("Mapshaper can't export files from this browser. Try switching to Chrome or Firefox.");
|
|
1817
|
+
return;
|
|
1847
1818
|
}
|
|
1848
|
-
|
|
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);
|
|
1849
1832
|
}
|
|
1850
1833
|
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
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);
|
|
1856
1841
|
}
|
|
1857
1842
|
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1843
|
+
function error() {
|
|
1844
|
+
stop.apply(null, utils$1.toArray(arguments));
|
|
1845
|
+
}
|
|
1861
1846
|
|
|
1862
|
-
|
|
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);
|
|
1876
|
-
} catch(e) {
|
|
1877
|
-
console.error(e);
|
|
1878
|
-
// use the more robust projectArcs2 if projectArcs throws an error
|
|
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);
|
|
1847
|
+
function message() {
|
|
1848
|
+
internal.logArgs(arguments);
|
|
1884
1849
|
}
|
|
1885
|
-
return copy;
|
|
1886
|
-
}
|
|
1887
1850
|
|
|
1888
|
-
|
|
1889
|
-
var max = 89.9,
|
|
1890
|
-
min = -89.9,
|
|
1891
|
-
bbox = arcs.getBounds().toArray();
|
|
1892
|
-
if (bbox[1] >= min && bbox[3] <= max) return;
|
|
1893
|
-
arcs.transformPoints(function(x, y) {
|
|
1894
|
-
if (y > max) return [x, max];
|
|
1895
|
-
if (y < min) return [x, min];
|
|
1896
|
-
});
|
|
1851
|
+
internal.setLoggingFunctions(message, error, stop);
|
|
1897
1852
|
}
|
|
1898
1853
|
|
|
1899
|
-
function
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
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
|
+
});
|
|
1905
1885
|
}
|
|
1906
1886
|
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
var
|
|
1912
|
-
var y = R * Math.log(Math.tan(Math.PI * 0.25 + lat * D2R * 0.5));
|
|
1913
|
-
return [x, y];
|
|
1914
|
-
}
|
|
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;
|
|
1915
1892
|
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
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;
|
|
1908
|
+
}
|
|
1923
1909
|
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
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
|
+
});
|
|
1932
1919
|
}
|
|
1933
1920
|
|
|
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
|
+
});
|
|
1934
1930
|
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
var
|
|
1938
|
-
|
|
1939
|
-
var newCP, proj;
|
|
1940
|
-
|
|
1941
|
-
if (dest && internal.isWebMercator(dest)) {
|
|
1942
|
-
clampToMapboxBounds(newBounds);
|
|
1943
|
-
}
|
|
1931
|
+
function loadProjLibs(libs, done) {
|
|
1932
|
+
var mproj = require('mproj');
|
|
1933
|
+
var i = 0;
|
|
1934
|
+
next();
|
|
1944
1935
|
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
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
|
+
}
|
|
1945
|
+
});
|
|
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++;
|
|
1962
1957
|
}
|
|
1963
1958
|
}
|
|
1964
1959
|
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
var
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
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;
|
|
1972
1969
|
}
|
|
1970
|
+
revertLogging();
|
|
1971
|
+
return {
|
|
1972
|
+
crs: crs,
|
|
1973
|
+
error: err
|
|
1974
|
+
};
|
|
1973
1975
|
}
|
|
1974
1976
|
|
|
1975
|
-
function
|
|
1976
|
-
|
|
1977
|
-
if (lyr
|
|
1978
|
-
|
|
1979
|
-
return lyr.shapes[id];
|
|
1980
|
-
});
|
|
1981
|
-
return utils$1.defaults({shapes: shapes, data: null}, lyr);
|
|
1977
|
+
function flattenArcs(lyr) {
|
|
1978
|
+
lyr.source.dataset.arcs.flatten();
|
|
1979
|
+
if (isProjectedLayer(lyr)) {
|
|
1980
|
+
lyr.arcs.flatten();
|
|
1982
1981
|
}
|
|
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
|
-
}
|
|
1994
|
-
|
|
1995
|
-
function updateLayerStackOrder(layers) {
|
|
1996
|
-
// 1. assign ascending ids to unassigned layers above the range of other layers
|
|
1997
|
-
layers.forEach(function(o, i) {
|
|
1998
|
-
if (!o.layer.stack_id) o.layer.stack_id = 1e6 + i;
|
|
1999
|
-
});
|
|
2000
|
-
// 2. sort in ascending order
|
|
2001
|
-
layers.sort(function(a, b) {
|
|
2002
|
-
return a.layer.stack_id - b.layer.stack_id;
|
|
2003
|
-
});
|
|
2004
|
-
// 3. assign consecutve ids
|
|
2005
|
-
layers.forEach(function(o, i) {
|
|
2006
|
-
o.layer.stack_id = i + 1;
|
|
2007
|
-
});
|
|
2008
|
-
return layers;
|
|
2009
|
-
}
|
|
2010
|
-
|
|
2011
|
-
function sortLayersForMenuDisplay(layers) {
|
|
2012
|
-
layers = updateLayerStackOrder(layers);
|
|
2013
|
-
return layers.reverse();
|
|
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);
|
|
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);
|
|
2519
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
|
|
|
@@ -4053,8 +3838,6 @@
|
|
|
4053
3838
|
}
|
|
4054
3839
|
}
|
|
4055
3840
|
|
|
4056
|
-
// import { cloneShape } from '../paths/mapshaper-shape-utils';
|
|
4057
|
-
// import { copyRecord } from '../datatable/mapshaper-data-utils';
|
|
4058
3841
|
var snapVerticesToPoint = internal.snapVerticesToPoint;
|
|
4059
3842
|
var cloneShape = internal.cloneShape;
|
|
4060
3843
|
var copyRecord = internal.copyRecord;
|
|
@@ -4408,6 +4191,10 @@
|
|
|
4408
4191
|
setMode('off');
|
|
4409
4192
|
};
|
|
4410
4193
|
|
|
4194
|
+
this.modeUsesPopup = function(mode) {
|
|
4195
|
+
return ['info', 'selection', 'data', 'box', 'labels', 'location'].includes(mode);
|
|
4196
|
+
};
|
|
4197
|
+
|
|
4411
4198
|
this.getMode = getInteractionMode;
|
|
4412
4199
|
|
|
4413
4200
|
this.setMode = function(mode) {
|
|
@@ -4658,24 +4445,37 @@
|
|
|
4658
4445
|
|
|
4659
4446
|
var LOGGING = false;
|
|
4660
4447
|
var STDOUT = false; // use stdout for status messages
|
|
4661
|
-
|
|
4662
|
-
// These three functions can be reset by GUI using setLoggingFunctions();
|
|
4663
|
-
var _error = function() {
|
|
4664
|
-
var msg = utils.toArray(arguments).join(' ');
|
|
4665
|
-
throw new Error(msg);
|
|
4666
|
-
};
|
|
4667
|
-
|
|
4668
|
-
var _stop = function() {
|
|
4669
|
-
throw new UserError(formatLogArgs(arguments));
|
|
4670
|
-
};
|
|
4448
|
+
var _error, _stop, _message;
|
|
4671
4449
|
|
|
4672
4450
|
var _interrupt = function() {
|
|
4673
4451
|
throw new NonFatalError(formatLogArgs(arguments));
|
|
4674
4452
|
};
|
|
4675
4453
|
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
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
|
+
}
|
|
4679
4479
|
|
|
4680
4480
|
function enableLogging() {
|
|
4681
4481
|
LOGGING = true;
|
|
@@ -6397,7 +6197,7 @@
|
|
|
6397
6197
|
}
|
|
6398
6198
|
};
|
|
6399
6199
|
|
|
6400
|
-
self.setHoverVertex = function(p) {
|
|
6200
|
+
self.setHoverVertex = function(p, type) {
|
|
6401
6201
|
var p2 = storedData.hit_coordinates;
|
|
6402
6202
|
if (!active || !p) return;
|
|
6403
6203
|
if (p2 && p2[0] == p[0] && p2[1] == p[1]) return;
|
|
@@ -7618,7 +7418,7 @@
|
|
|
7618
7418
|
var _self = new EventDispatcher();
|
|
7619
7419
|
|
|
7620
7420
|
gui.on('interaction_mode_change', function(e) {
|
|
7621
|
-
if (e.mode
|
|
7421
|
+
if (!gui.interaction.modeUsesPopup(e.mode)) {
|
|
7622
7422
|
inspect(-1); // clear the popup
|
|
7623
7423
|
}
|
|
7624
7424
|
});
|
|
@@ -7646,7 +7446,7 @@
|
|
|
7646
7446
|
|
|
7647
7447
|
// does the attribute inspector appear on rollover
|
|
7648
7448
|
function inspecting() {
|
|
7649
|
-
return gui.interaction && gui.interaction.getMode()
|
|
7449
|
+
return gui.interaction && gui.interaction.modeUsesPopup(gui.interaction.getMode());
|
|
7650
7450
|
}
|
|
7651
7451
|
|
|
7652
7452
|
return _self;
|
|
@@ -7988,7 +7788,8 @@
|
|
|
7988
7788
|
|
|
7989
7789
|
function findVertexInsertionPoint(e) {
|
|
7990
7790
|
var target = hit.getHitTarget();
|
|
7991
|
-
|
|
7791
|
+
//// vertex insertion not supported with simplification
|
|
7792
|
+
// if (!target.arcs.isFlat()) return null;
|
|
7992
7793
|
var p = ext.translatePixelCoords(e.x, e.y);
|
|
7993
7794
|
var midpoint = findNearestMidpoint(p, e.id, target);
|
|
7994
7795
|
if (!midpoint ||
|
|
@@ -8133,19 +7934,26 @@
|
|
|
8133
7934
|
var darkStroke = "#334",
|
|
8134
7935
|
lightStroke = "#b7d9ea",
|
|
8135
7936
|
violet = "#cc6acc",
|
|
8136
|
-
violetFill = "rgba(249,
|
|
7937
|
+
violetFill = "rgba(249, 120, 249, 0.20)",
|
|
8137
7938
|
gold = "#efc100",
|
|
8138
7939
|
black = "black",
|
|
8139
7940
|
grey = "#888",
|
|
8140
7941
|
selectionFill = "rgba(237, 214, 0, 0.12)",
|
|
8141
|
-
hoverFill = "rgba(255,
|
|
7942
|
+
hoverFill = "rgba(255, 120, 255, 0.12)",
|
|
8142
7943
|
activeStyle = { // outline style for the active layer
|
|
8143
7944
|
type: 'outline',
|
|
8144
7945
|
strokeColors: [lightStroke, darkStroke],
|
|
8145
|
-
strokeWidth: 0.
|
|
7946
|
+
strokeWidth: 0.8,
|
|
8146
7947
|
dotColor: "#223",
|
|
8147
7948
|
dotSize: 1
|
|
8148
7949
|
},
|
|
7950
|
+
activeStyleDarkMode = {
|
|
7951
|
+
type: 'outline',
|
|
7952
|
+
strokeColors: [lightStroke, 'white'],
|
|
7953
|
+
strokeWidth: 0.9,
|
|
7954
|
+
dotColor: 'white',
|
|
7955
|
+
dotSize: 1
|
|
7956
|
+
},
|
|
8149
7957
|
activeStyleForLabels = {
|
|
8150
7958
|
dotColor: "rgba(250, 0, 250, 0.45)", // violet dot with transparency
|
|
8151
7959
|
dotSize: 1
|
|
@@ -8274,12 +8082,14 @@
|
|
|
8274
8082
|
return style;
|
|
8275
8083
|
}
|
|
8276
8084
|
|
|
8277
|
-
function getActiveStyle(lyr) {
|
|
8085
|
+
function getActiveStyle(lyr, darkMode) {
|
|
8278
8086
|
var style;
|
|
8279
8087
|
if (layerHasCanvasDisplayStyle(lyr)) {
|
|
8280
8088
|
style = getCanvasDisplayStyle(lyr);
|
|
8281
8089
|
} else if (internal.layerHasLabels(lyr)) {
|
|
8282
8090
|
style = getDefaultStyle(lyr, activeStyleForLabels);
|
|
8091
|
+
} else if (darkMode) {
|
|
8092
|
+
style = getDefaultStyle(lyr, activeStyleDarkMode);
|
|
8283
8093
|
} else {
|
|
8284
8094
|
style = getDefaultStyle(lyr, activeStyle);
|
|
8285
8095
|
}
|
|
@@ -8294,6 +8104,7 @@
|
|
|
8294
8104
|
strokeColor: black,
|
|
8295
8105
|
strokeWidth: 1.5,
|
|
8296
8106
|
vertices: true,
|
|
8107
|
+
vertex_overlay_color: violet,
|
|
8297
8108
|
vertex_overlay: o.hit_coordinates || null,
|
|
8298
8109
|
selected_points: o.selected_points || null,
|
|
8299
8110
|
fillColor: null
|
|
@@ -8333,13 +8144,6 @@
|
|
|
8333
8144
|
ids: ids,
|
|
8334
8145
|
overlay: true
|
|
8335
8146
|
};
|
|
8336
|
-
// kludge to show vertices when editing path shapes
|
|
8337
|
-
if (o.mode == 'vertices') {
|
|
8338
|
-
style.vertices = true;
|
|
8339
|
-
style.vertex_overlay = o.hit_coordinates || null;
|
|
8340
|
-
style.selected_points = o.selected_points || null;
|
|
8341
|
-
style.fillColor = null;
|
|
8342
|
-
}
|
|
8343
8147
|
|
|
8344
8148
|
if (layerHasCanvasDisplayStyle(lyr)) {
|
|
8345
8149
|
if (geomType == 'point') {
|
|
@@ -8450,6 +8254,7 @@
|
|
|
8450
8254
|
var _scale = 1,
|
|
8451
8255
|
_cx, _cy, // center in geographic units
|
|
8452
8256
|
_contentBounds,
|
|
8257
|
+
_strictBounds, // full extent must fit inside, if set
|
|
8453
8258
|
_self = this,
|
|
8454
8259
|
_frame;
|
|
8455
8260
|
|
|
@@ -8533,15 +8338,25 @@
|
|
|
8533
8338
|
};
|
|
8534
8339
|
|
|
8535
8340
|
// Update the extent of 'full' zoom without navigating the current view
|
|
8536
|
-
|
|
8341
|
+
//
|
|
8342
|
+
this.setBounds = function(contentBounds, strictBounds) {
|
|
8343
|
+
var b = contentBounds;
|
|
8537
8344
|
var prev = _contentBounds;
|
|
8538
8345
|
if (!b.hasBounds()) return; // kludge
|
|
8346
|
+
if (strictBounds) {
|
|
8347
|
+
_strictBounds = Array.isArray(strictBounds) ? new Bounds(strictBounds) : strictBounds;
|
|
8348
|
+
} else {
|
|
8349
|
+
_strictBounds = null;
|
|
8350
|
+
}
|
|
8539
8351
|
_contentBounds = _frame ? b : padBounds(b, 4); // padding if not in frame mode
|
|
8352
|
+
if (_strictBounds) {
|
|
8353
|
+
_contentBounds = fitIn(_contentBounds, _strictBounds);
|
|
8354
|
+
}
|
|
8540
8355
|
if (prev) {
|
|
8541
8356
|
_scale = _scale * fillOut(_contentBounds).width() / fillOut(prev).width();
|
|
8542
8357
|
} else {
|
|
8543
|
-
_cx =
|
|
8544
|
-
_cy =
|
|
8358
|
+
_cx = _contentBounds.centerX();
|
|
8359
|
+
_cy = _contentBounds.centerY();
|
|
8545
8360
|
}
|
|
8546
8361
|
};
|
|
8547
8362
|
|
|
@@ -8570,12 +8385,25 @@
|
|
|
8570
8385
|
|
|
8571
8386
|
function recenter(cx, cy, scale, data) {
|
|
8572
8387
|
scale = scale ? limitScale(scale) : _scale;
|
|
8573
|
-
if (
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
_scale = scale;
|
|
8577
|
-
onChange(data);
|
|
8388
|
+
if (cx == _cx && cy == _cy && scale == _scale) return;
|
|
8389
|
+
if (_strictBounds) {
|
|
8390
|
+
scale = Math.max(1, scale);
|
|
8578
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;
|
|
8579
8407
|
}
|
|
8580
8408
|
|
|
8581
8409
|
function onChange(data) {
|
|
@@ -8602,15 +8430,19 @@
|
|
|
8602
8430
|
}
|
|
8603
8431
|
|
|
8604
8432
|
function calcBounds(cx, cy, scale) {
|
|
8605
|
-
var bounds, w, h;
|
|
8433
|
+
var full, bounds, w, h;
|
|
8606
8434
|
if (_frame) {
|
|
8607
|
-
|
|
8435
|
+
full = fillOutFrameBounds(_frame);
|
|
8608
8436
|
} else {
|
|
8609
|
-
|
|
8437
|
+
full = fillOut(_contentBounds);
|
|
8610
8438
|
}
|
|
8611
|
-
|
|
8612
|
-
|
|
8613
|
-
|
|
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;
|
|
8614
8446
|
}
|
|
8615
8447
|
|
|
8616
8448
|
// Calculate viewport bounds from frame data
|
|
@@ -8638,6 +8470,22 @@
|
|
|
8638
8470
|
return b;
|
|
8639
8471
|
}
|
|
8640
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
|
+
|
|
8641
8489
|
// Pad bounds vertically or horizontally to match viewport aspect ratio
|
|
8642
8490
|
function fillOut(b) {
|
|
8643
8491
|
var wpix = _position.width(),
|
|
@@ -8813,13 +8661,17 @@
|
|
|
8813
8661
|
}
|
|
8814
8662
|
}
|
|
8815
8663
|
}
|
|
8664
|
+
_ctx.fill();
|
|
8665
|
+
_ctx.closePath();
|
|
8816
8666
|
|
|
8817
8667
|
if (style.vertex_overlay) {
|
|
8668
|
+
_ctx.beginPath();
|
|
8669
|
+
_ctx.fillStyle = style.vertex_overlay_color || 'black';
|
|
8818
8670
|
p = style.vertex_overlay;
|
|
8819
|
-
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();
|
|
8820
8674
|
}
|
|
8821
|
-
_ctx.fill();
|
|
8822
|
-
_ctx.closePath();
|
|
8823
8675
|
};
|
|
8824
8676
|
|
|
8825
8677
|
// Optimized to draw paths in same-style batches (faster Canvas drawing)
|
|
@@ -9501,124 +9353,379 @@
|
|
|
9501
9353
|
// runCommand('-rectangle bbox=' + bbox.join(','));
|
|
9502
9354
|
// });
|
|
9503
9355
|
|
|
9504
|
-
new SimpleButton(popup.findChild('.select-btn')).on('click', function() {
|
|
9505
|
-
gui.enterMode('selection_tool');
|
|
9506
|
-
gui.interaction.setMode('selection');
|
|
9507
|
-
// kludge to pass bbox to the selection tool
|
|
9508
|
-
gui.dispatchEvent('box_drag_end', {map_bbox: bboxPixels});
|
|
9509
|
-
});
|
|
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
|
+
}
|
|
9510
9565
|
|
|
9511
|
-
|
|
9512
|
-
|
|
9513
|
-
|
|
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
|
+
}
|
|
9514
9575
|
|
|
9515
|
-
|
|
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;
|
|
9516
9593
|
|
|
9517
|
-
|
|
9518
|
-
|
|
9519
|
-
|
|
9520
|
-
|
|
9521
|
-
|
|
9522
|
-
|
|
9523
|
-
|
|
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
|
+
}
|
|
9524
9603
|
|
|
9525
|
-
// Update the visible rectangle when the map view changes
|
|
9526
|
-
// (e.g. during zooming or panning)
|
|
9527
|
-
ext.on('change', function() {
|
|
9528
|
-
if (!_on || !box.visible() || !bboxDisplayCoords) return;
|
|
9529
|
-
var b = coordsToPix(bboxDisplayCoords);
|
|
9530
|
-
var pos = ext.position();
|
|
9531
|
-
var dx = pos.pageX,
|
|
9532
|
-
dy = pos.pageY;
|
|
9533
|
-
box.show(b[0] + dx, b[1] + dy, b[2] + dx, b[3] + dy);
|
|
9534
|
-
});
|
|
9535
9604
|
|
|
9536
|
-
|
|
9537
|
-
|
|
9538
|
-
|
|
9539
|
-
|
|
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
|
+
};
|
|
9540
9620
|
|
|
9541
|
-
|
|
9542
|
-
|
|
9543
|
-
|
|
9544
|
-
|
|
9545
|
-
|
|
9546
|
-
box.show(b[0], b[1], b[2], b[3]);
|
|
9547
|
-
}
|
|
9548
|
-
});
|
|
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;
|
|
9549
9626
|
|
|
9550
|
-
|
|
9551
|
-
|
|
9552
|
-
|
|
9553
|
-
|
|
9554
|
-
|
|
9555
|
-
|
|
9556
|
-
|
|
9557
|
-
|
|
9558
|
-
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;
|
|
9559
9635
|
}
|
|
9560
|
-
});
|
|
9561
|
-
|
|
9562
|
-
function inZoomMode() {
|
|
9563
|
-
return !_on && gui.getMode() != 'selection_tool';
|
|
9564
9636
|
}
|
|
9565
9637
|
|
|
9566
|
-
|
|
9567
|
-
|
|
9568
|
-
|
|
9569
|
-
|
|
9570
|
-
|
|
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;
|
|
9571
9647
|
}
|
|
9572
|
-
// reset(); // TODO: exit interactive mode
|
|
9573
|
-
}
|
|
9574
9648
|
|
|
9575
|
-
|
|
9576
|
-
|
|
9577
|
-
coords.text(bboxDataCoords.join(','));
|
|
9578
|
-
coords.show();
|
|
9579
|
-
GUI.selectElement(coords.node());
|
|
9649
|
+
enhanceArcCollectionForDisplay(displayArcs);
|
|
9650
|
+
dataset.displayArcs = displayArcs; // stash these in the dataset for other layers to use
|
|
9580
9651
|
}
|
|
9581
9652
|
|
|
9582
|
-
|
|
9583
|
-
|
|
9584
|
-
|
|
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;
|
|
9585
9668
|
}
|
|
9586
9669
|
|
|
9587
|
-
|
|
9588
|
-
|
|
9670
|
+
if (obj.tabular) {
|
|
9671
|
+
utils$1.extend(obj, getDisplayLayerForTable(layer.data));
|
|
9589
9672
|
}
|
|
9590
9673
|
|
|
9591
|
-
|
|
9592
|
-
|
|
9593
|
-
|
|
9594
|
-
|
|
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
|
+
}
|
|
9595
9687
|
}
|
|
9596
|
-
_on = false;
|
|
9597
|
-
reset();
|
|
9598
9688
|
}
|
|
9599
9689
|
|
|
9600
|
-
|
|
9601
|
-
|
|
9602
|
-
|
|
9603
|
-
hideCoords();
|
|
9604
|
-
}
|
|
9690
|
+
obj.bounds = getDisplayBounds(obj.layer, obj.arcs);
|
|
9691
|
+
return obj;
|
|
9692
|
+
}
|
|
9605
9693
|
|
|
9606
|
-
|
|
9607
|
-
|
|
9608
|
-
|
|
9609
|
-
|
|
9610
|
-
|
|
9611
|
-
|
|
9612
|
-
|
|
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
|
+
}
|
|
9613
9711
|
}
|
|
9614
9712
|
|
|
9615
|
-
|
|
9616
|
-
|
|
9617
|
-
var b = ext.translateCoords(bbox[2], bbox[3]);
|
|
9618
|
-
return [a[0], b[1], b[0], a[1]];
|
|
9713
|
+
if (!bounds || !bounds.hasBounds()) { // empty layer
|
|
9714
|
+
bounds = new Bounds();
|
|
9619
9715
|
}
|
|
9716
|
+
return bounds;
|
|
9717
|
+
}
|
|
9620
9718
|
|
|
9621
|
-
|
|
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;
|
|
9622
9729
|
}
|
|
9623
9730
|
|
|
9624
9731
|
function loadScript(url, cb) {
|
|
@@ -9659,10 +9766,6 @@
|
|
|
9659
9766
|
|
|
9660
9767
|
function init() {
|
|
9661
9768
|
gui.addMode('basemap', turnOn, turnOff, basemapBtn);
|
|
9662
|
-
// model.on('select', function() {
|
|
9663
|
-
// TODO: hide basemap
|
|
9664
|
-
// if (gui.getMode() == 'basemap') gui.clearMode();
|
|
9665
|
-
// });
|
|
9666
9769
|
|
|
9667
9770
|
new SimpleButton(menu.findChild('.close-btn')).on('click', function() {
|
|
9668
9771
|
gui.clearMode();
|
|
@@ -9686,6 +9789,9 @@
|
|
|
9686
9789
|
|
|
9687
9790
|
function updateStyle(style) {
|
|
9688
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;
|
|
9689
9795
|
if (!style) {
|
|
9690
9796
|
gui.map.setDisplayCRS(null);
|
|
9691
9797
|
hide();
|
|
@@ -9705,11 +9811,12 @@
|
|
|
9705
9811
|
|
|
9706
9812
|
function turnOn() {
|
|
9707
9813
|
var activeLyr = gui.model.getActiveLayer();
|
|
9708
|
-
var
|
|
9814
|
+
var info = getDatasetCrsInfo(activeLyr.dataset);
|
|
9815
|
+
var dataCRS = info.crs || null;
|
|
9709
9816
|
var displayCRS = gui.map.getDisplayCRS();
|
|
9710
9817
|
var warning;
|
|
9711
9818
|
|
|
9712
|
-
if (!crsIsUsable(displayCRS) || !crsIsUsable(dataCRS)) {
|
|
9819
|
+
if (!dataCRS || !displayCRS || !crsIsUsable(displayCRS) || !crsIsUsable(dataCRS)) {
|
|
9713
9820
|
warning = 'The current layer is not compatible with the projection used by the basemaps.';
|
|
9714
9821
|
basemapWarning.html(warning).show();
|
|
9715
9822
|
basemapNote.hide();
|
|
@@ -9774,16 +9881,25 @@
|
|
|
9774
9881
|
});
|
|
9775
9882
|
}
|
|
9776
9883
|
|
|
9884
|
+
// @bbox: latlon bounding box of current map extent
|
|
9777
9885
|
function checkBounds(bbox) {
|
|
9886
|
+
var mpp = ext.getBounds().width() / ext.width();
|
|
9887
|
+
var z = scaleToZoom(mpp);
|
|
9778
9888
|
var msg;
|
|
9779
|
-
if (bbox[1] >= -85 && bbox[3] <= 85) {
|
|
9889
|
+
if (bbox[1] >= -85 && bbox[3] <= 85 && z <= 20) {
|
|
9780
9890
|
extentNote.hide();
|
|
9781
9891
|
return true;
|
|
9782
9892
|
}
|
|
9783
|
-
if (
|
|
9784
|
-
|
|
9785
|
-
else
|
|
9786
|
-
|
|
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();
|
|
9787
9903
|
return false;
|
|
9788
9904
|
}
|
|
9789
9905
|
|
|
@@ -9899,11 +10015,10 @@
|
|
|
9899
10015
|
};
|
|
9900
10016
|
|
|
9901
10017
|
this.getDisplayCRS = function() {
|
|
9902
|
-
|
|
9903
|
-
if (_activeLyr
|
|
9904
|
-
|
|
9905
|
-
|
|
9906
|
-
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;
|
|
9907
10022
|
};
|
|
9908
10023
|
|
|
9909
10024
|
this.getExtent = function() {return _ext;};
|
|
@@ -9976,7 +10091,8 @@
|
|
|
9976
10091
|
}
|
|
9977
10092
|
|
|
9978
10093
|
_activeLyr = getDisplayLayer(e.layer, e.dataset, getDisplayOptions());
|
|
9979
|
-
_activeLyr.style = getActiveStyle(_activeLyr.layer);
|
|
10094
|
+
_activeLyr.style = getActiveStyle(_activeLyr.layer, gui.state.dark_basemap);
|
|
10095
|
+
|
|
9980
10096
|
_activeLyr.active = true;
|
|
9981
10097
|
// if (_inspector) _inspector.updateLayer(_activeLyr);
|
|
9982
10098
|
_hit.setLayer(_activeLyr);
|
|
@@ -9995,7 +10111,6 @@
|
|
|
9995
10111
|
needReset = mapNeedsReset(fullBounds, _fullBounds, _ext.getBounds(), e.flags);
|
|
9996
10112
|
}
|
|
9997
10113
|
|
|
9998
|
-
|
|
9999
10114
|
if (isFrameView()) {
|
|
10000
10115
|
_nav.setZoomFactor(0.05); // slow zooming way down to allow fine-tuning frame placement // 0.03
|
|
10001
10116
|
_ext.setFrame(getFullBounds()); // TODO: remove redundancy with drawLayers()
|
|
@@ -10003,7 +10118,7 @@
|
|
|
10003
10118
|
} else {
|
|
10004
10119
|
_nav.setZoomFactor(1);
|
|
10005
10120
|
}
|
|
10006
|
-
_ext.setBounds(fullBounds); // update 'home' button extent
|
|
10121
|
+
_ext.setBounds(fullBounds, getStrictBounds()); // update 'home' button extent
|
|
10007
10122
|
_fullBounds = fullBounds;
|
|
10008
10123
|
if (needReset) {
|
|
10009
10124
|
_ext.reset();
|
|
@@ -10093,6 +10208,13 @@
|
|
|
10093
10208
|
_ext.reset();
|
|
10094
10209
|
}
|
|
10095
10210
|
|
|
10211
|
+
function getStrictBounds() {
|
|
10212
|
+
if (internal.isWebMercator(map.getDisplayCRS())) {
|
|
10213
|
+
return getMapboxBounds();
|
|
10214
|
+
}
|
|
10215
|
+
return null;
|
|
10216
|
+
}
|
|
10217
|
+
|
|
10096
10218
|
function getFullBounds() {
|
|
10097
10219
|
var b = new Bounds();
|
|
10098
10220
|
var marginPct = 0.025;
|
|
@@ -10238,11 +10360,16 @@
|
|
|
10238
10360
|
});
|
|
10239
10361
|
}
|
|
10240
10362
|
|
|
10363
|
+
function drawLayers(action) {
|
|
10364
|
+
// This seems to smooth out navigation and keep overlay and basemap in sync.
|
|
10365
|
+
requestAnimationFrame(function() {drawLayers2(action);});
|
|
10366
|
+
}
|
|
10367
|
+
|
|
10241
10368
|
// action:
|
|
10242
10369
|
// 'nav' map was panned/zoomed -- only map extent has changed
|
|
10243
10370
|
// 'hover' highlight has changed -- only draw overlay
|
|
10244
10371
|
// (default) anything could have changed
|
|
10245
|
-
function
|
|
10372
|
+
function drawLayers2(action) {
|
|
10246
10373
|
var layersMayHaveChanged = !action;
|
|
10247
10374
|
var contentLayers = getDrawableContentLayers();
|
|
10248
10375
|
var furnitureLayers = getDrawableFurnitureLayers();
|
|
@@ -10254,7 +10381,7 @@
|
|
|
10254
10381
|
if (layersMayHaveChanged) {
|
|
10255
10382
|
// kludge to handle layer visibility toggling
|
|
10256
10383
|
_ext.setFrame(isPreviewView() ? getFrameData() : null);
|
|
10257
|
-
_ext.setBounds(getFullBounds());
|
|
10384
|
+
_ext.setBounds(getFullBounds(), getStrictBounds());
|
|
10258
10385
|
updateLayerStyles(contentLayers);
|
|
10259
10386
|
updateLayerStackOrder(model.getLayers());// update stack_id property of all layers
|
|
10260
10387
|
}
|
|
@@ -10272,6 +10399,40 @@
|
|
|
10272
10399
|
}
|
|
10273
10400
|
}
|
|
10274
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
|
+
|
|
10275
10436
|
function GuiInstance(container, opts) {
|
|
10276
10437
|
var gui = new ModeSwitcher();
|
|
10277
10438
|
opts = utils$1.extend({
|
|
@@ -10295,6 +10456,8 @@
|
|
|
10295
10456
|
gui.undo = new Undo(gui);
|
|
10296
10457
|
gui.state = {};
|
|
10297
10458
|
|
|
10459
|
+
initModeRules(gui);
|
|
10460
|
+
|
|
10298
10461
|
gui.showProgressMessage = function(msg) {
|
|
10299
10462
|
if (!gui.progressMessage) {
|
|
10300
10463
|
gui.progressMessage = El('div').addClass('progress-message')
|
|
@@ -10319,7 +10482,7 @@
|
|
|
10319
10482
|
curr.blur();
|
|
10320
10483
|
}
|
|
10321
10484
|
GUI.__active = gui;
|
|
10322
|
-
|
|
10485
|
+
setLoggingForGUI(gui);
|
|
10323
10486
|
ImportFileProxy(gui);
|
|
10324
10487
|
WriteFilesProxy(gui);
|
|
10325
10488
|
gui.dispatchEvent('active');
|