mapshaper 0.5.97 → 0.5.100
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 +104 -36
- package/package.json +2 -1
- package/www/images/thumb-map.jpg +0 -0
- package/www/images/thumb-satellite.jpg +0 -0
- package/www/index.html +15 -1
- package/www/mapshaper-gui.js +780 -385
- package/www/mapshaper.js +104 -36
- package/www/page.css +89 -4
package/www/mapshaper-gui.js
CHANGED
|
@@ -1736,6 +1736,516 @@
|
|
|
1736
1736
|
|
|
1737
1737
|
utils$1.inherit(Slider, EventDispatcher);
|
|
1738
1738
|
|
|
1739
|
+
// Create low-detail versions of large arc collections for faster rendering
|
|
1740
|
+
// at zoomed-out scales.
|
|
1741
|
+
function enhanceArcCollectionForDisplay(unfilteredArcs) {
|
|
1742
|
+
var size = unfilteredArcs.getPointCount(),
|
|
1743
|
+
filteredArcs, filteredSegLen;
|
|
1744
|
+
|
|
1745
|
+
// Only generate low-detail arcs for larger datasets
|
|
1746
|
+
if (size > 5e5) {
|
|
1747
|
+
if (!!unfilteredArcs.getVertexData().zz) {
|
|
1748
|
+
// Use precalculated simplification data for vertex filtering, if available
|
|
1749
|
+
filteredArcs = initFilteredArcs(unfilteredArcs);
|
|
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;
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
unfilteredArcs.getScaledArcs = function(ext) {
|
|
1769
|
+
if (filteredArcs) {
|
|
1770
|
+
// match simplification of unfiltered arcs
|
|
1771
|
+
filteredArcs.setRetainedInterval(unfilteredArcs.getRetainedInterval());
|
|
1772
|
+
}
|
|
1773
|
+
// switch to filtered version of arcs at small scales
|
|
1774
|
+
var unitsPerPixel = 1/ext.getTransform().mx,
|
|
1775
|
+
useFiltering = filteredArcs && unitsPerPixel > filteredSegLen * 1.5;
|
|
1776
|
+
return useFiltering ? filteredArcs : unfilteredArcs;
|
|
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;
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1812
|
+
for (var i=0; i<n; i++) {
|
|
1813
|
+
row = i % blockSize;
|
|
1814
|
+
col = Math.floor(i / blockSize);
|
|
1815
|
+
x = col * (cellWidth + gutter);
|
|
1816
|
+
y = cellHeight * (blockSize - row);
|
|
1817
|
+
if (arcs) {
|
|
1818
|
+
arcs.push(getArc(x, y, cellWidth, cellHeight));
|
|
1819
|
+
shapes.push([[i]]);
|
|
1820
|
+
} else {
|
|
1821
|
+
shapes.push([[x, y]]);
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
function getArc(x, y, w, h) {
|
|
1826
|
+
return [[x, y], [x + w, y], [x + w, y - h], [x, y - h], [x, y]];
|
|
1827
|
+
}
|
|
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
|
+
}
|
|
1838
|
+
|
|
1839
|
+
// Assumes projections are available
|
|
1840
|
+
|
|
1841
|
+
function needReprojectionForDisplay(sourceCRS, displayCRS) {
|
|
1842
|
+
if (!sourceCRS || !displayCRS) {
|
|
1843
|
+
return false;
|
|
1844
|
+
}
|
|
1845
|
+
if (internal.crsAreEqual(sourceCRS, displayCRS)) {
|
|
1846
|
+
return false;
|
|
1847
|
+
}
|
|
1848
|
+
return true;
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
function projectArcsForDisplay(arcs, src, dest) {
|
|
1852
|
+
var copy = arcs.getCopy(); // need to flatten first?
|
|
1853
|
+
var destIsWebMerc = internal.isWebMercator(dest);
|
|
1854
|
+
if (destIsWebMerc && internal.isWebMercator(src)) {
|
|
1855
|
+
return copy;
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
var wgs84 = internal.getCRS('wgs84');
|
|
1859
|
+
var toWGS84 = internal.getProjTransform2(src, wgs84);
|
|
1860
|
+
var fromWGS84 = internal.getProjTransform2(wgs84, dest);
|
|
1861
|
+
|
|
1862
|
+
try {
|
|
1863
|
+
// first try projectArcs() -- it's fast and preserves arc ids
|
|
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);
|
|
1884
|
+
}
|
|
1885
|
+
return copy;
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
function clampY(arcs) {
|
|
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
|
+
});
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
function projectPointsForDisplay(lyr, src, dest) {
|
|
1900
|
+
var copy = utils$1.extend({}, lyr);
|
|
1901
|
+
var proj = internal.getProjTransform2(src, dest);
|
|
1902
|
+
copy.shapes = internal.cloneShapes(lyr.shapes);
|
|
1903
|
+
internal.projectPointLayer(copy, proj);
|
|
1904
|
+
return copy;
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
function toWebMercator(lng, lat) {
|
|
1908
|
+
var R = 6378137;
|
|
1909
|
+
var D2R = Math.PI / 180;
|
|
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
|
+
}
|
|
1915
|
+
|
|
1916
|
+
function fromWebMercator(x, y) {
|
|
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];
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
function clampToMapboxBounds(bounds) {
|
|
1925
|
+
var ymax = toWebMercator(0, 84)[1];
|
|
1926
|
+
var ymin = toWebMercator(0, -84)[1];
|
|
1927
|
+
var hmin = 1000;
|
|
1928
|
+
bounds.ymin = Math.max(bounds.ymin, ymin);
|
|
1929
|
+
bounds.ymax = Math.max(bounds.ymax, ymin + hmin);
|
|
1930
|
+
bounds.ymin = Math.min(bounds.ymin, ymax - hmin);
|
|
1931
|
+
bounds.ymax = Math.min(bounds.ymax, ymax);
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
|
|
1935
|
+
// Update map extent and trigger redraw, after a new display CRS has been applied
|
|
1936
|
+
function projectMapExtent(ext, src, dest, newBounds) {
|
|
1937
|
+
var oldBounds = ext.getBounds();
|
|
1938
|
+
var oldScale = ext.scale();
|
|
1939
|
+
var newCP, proj;
|
|
1940
|
+
|
|
1941
|
+
if (dest && internal.isWebMercator(dest)) {
|
|
1942
|
+
clampToMapboxBounds(newBounds);
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
// if source or destination CRS is unknown, show full extent
|
|
1946
|
+
// if map is at full extent, show full extent
|
|
1947
|
+
// TODO: handle case that scale is 1 and map is panned away from center
|
|
1948
|
+
if (ext.scale() == 1 || !dest) {
|
|
1949
|
+
ext.setBounds(newBounds);
|
|
1950
|
+
ext.home(); // sets full extent and triggers redraw
|
|
1951
|
+
} else {
|
|
1952
|
+
// if map is zoomed, stay centered on the same geographic location, at the same relative scale
|
|
1953
|
+
proj = internal.getProjTransform2(src, dest);
|
|
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
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
// Called from console; for testing dynamic crs
|
|
1966
|
+
function setDisplayProjection(gui, cmd) {
|
|
1967
|
+
var arg = cmd.replace(/^projd[ ]*/, '');
|
|
1968
|
+
if (arg) {
|
|
1969
|
+
gui.map.setDisplayCRS(internal.getCRS(arg));
|
|
1970
|
+
} else {
|
|
1971
|
+
gui.map.setDisplayCRS(null);
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1975
|
+
function filterLayerByIds(lyr, ids) {
|
|
1976
|
+
var shapes;
|
|
1977
|
+
if (lyr.shapes) {
|
|
1978
|
+
shapes = ids.map(function(id) {
|
|
1979
|
+
return lyr.shapes[id];
|
|
1980
|
+
});
|
|
1981
|
+
return utils$1.defaults({shapes: shapes, data: null}, lyr);
|
|
1982
|
+
}
|
|
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
|
+
}
|
|
2015
|
+
|
|
2016
|
+
function setZ(lyr, z) {
|
|
2017
|
+
lyr.source.dataset.arcs.setRetainedInterval(z);
|
|
2018
|
+
if (isProjectedLayer(lyr)) {
|
|
2019
|
+
lyr.arcs.setRetainedInterval(z);
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
|
|
2023
|
+
function updateZ(lyr) {
|
|
2024
|
+
if (isProjectedLayer(lyr) && !lyr.source.dataset.arcs.isFlat()) {
|
|
2025
|
+
lyr.arcs.setThresholds(lyr.source.dataset.arcs.getVertexData().zz);
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
function insertVertex$1(lyr, id, dataPoint) {
|
|
2030
|
+
internal.insertVertex(lyr.source.dataset.arcs, id, dataPoint);
|
|
2031
|
+
if (isProjectedLayer(lyr)) {
|
|
2032
|
+
internal.insertVertex(lyr.arcs, id, lyr.projectPoint(dataPoint[0], dataPoint[1]));
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
function deleteVertex$1(lyr, id) {
|
|
2037
|
+
internal.deleteVertex(lyr.arcs, id);
|
|
2038
|
+
if (isProjectedLayer(lyr)) {
|
|
2039
|
+
internal.deleteVertex(lyr.source.dataset.arcs, id);
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
function translateDisplayPoint(lyr, p) {
|
|
2044
|
+
return isProjectedLayer(lyr) ? lyr.invertPoint(p[0], p[1]) : p;
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
function getPointCoords(lyr, fid) {
|
|
2048
|
+
return internal.cloneShape(lyr.source.layer.shapes[fid]);
|
|
2049
|
+
}
|
|
2050
|
+
|
|
2051
|
+
// bbox: display coords
|
|
2052
|
+
// intended to work with rectangular projections like Mercator
|
|
2053
|
+
function getBBoxCoords(lyr, bbox) {
|
|
2054
|
+
if (!isProjectedLayer(lyr)) return bbox;
|
|
2055
|
+
var a = translateDisplayPoint(lyr, [bbox[0], bbox[1]]);
|
|
2056
|
+
var b = translateDisplayPoint(lyr, [bbox[2], bbox[3]]);
|
|
2057
|
+
var bounds = new internal.Bounds();
|
|
2058
|
+
bounds.mergePoint(a[0], a[1]);
|
|
2059
|
+
bounds.mergePoint(b[0], b[1]);
|
|
2060
|
+
return bounds.toArray();
|
|
2061
|
+
}
|
|
2062
|
+
|
|
2063
|
+
function getVertexCoords(lyr, id) {
|
|
2064
|
+
return lyr.source.dataset.arcs.getVertex2(id);
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
function setVertexCoords(lyr, ids, dataPoint) {
|
|
2068
|
+
internal.snapVerticesToPoint(ids, dataPoint, lyr.source.dataset.arcs, true);
|
|
2069
|
+
if (isProjectedLayer(lyr)) {
|
|
2070
|
+
var p = lyr.projectPoint(dataPoint[0], dataPoint[1]);
|
|
2071
|
+
internal.snapVerticesToPoint(ids, p, lyr.arcs, true);
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2075
|
+
function setPointCoords(lyr, fid, coords) {
|
|
2076
|
+
lyr.source.layer.shapes[fid] = coords;
|
|
2077
|
+
if (isProjectedLayer(lyr)) {
|
|
2078
|
+
lyr.layer.shapes[fid] = projectPointCoords(coords, lyr.projectPoint);
|
|
2079
|
+
}
|
|
2080
|
+
}
|
|
2081
|
+
|
|
2082
|
+
function updateVertexCoords(lyr, ids) {
|
|
2083
|
+
if (!isProjectedLayer(lyr)) return;
|
|
2084
|
+
var p = lyr.arcs.getVertex2(ids[0]);
|
|
2085
|
+
internal.snapVerticesToPoint(ids, lyr.invertPoint(p[0], p[1]), lyr.source.dataset.arcs, true);
|
|
2086
|
+
}
|
|
2087
|
+
|
|
2088
|
+
function isProjectedLayer(lyr) {
|
|
2089
|
+
// TODO: could do some validation on the layer's contents
|
|
2090
|
+
return !!(lyr.source && lyr.invertPoint);
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2093
|
+
// Update data coordinates by projecting display coordinates
|
|
2094
|
+
function updatePointCoords(lyr, fid) {
|
|
2095
|
+
if (!isProjectedLayer(lyr)) return;
|
|
2096
|
+
var displayShp = lyr.layer.shapes[fid];
|
|
2097
|
+
lyr.source.layer.shapes[fid] = projectPointCoords(displayShp, lyr.invertPoint);
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
function projectPointCoords(src, proj) {
|
|
2101
|
+
var dest = [], p;
|
|
2102
|
+
for (var i=0; i<src.length; i++) {
|
|
2103
|
+
p = proj(src[i][0], src[i][1]);
|
|
2104
|
+
if (p) dest.push(p);
|
|
2105
|
+
}
|
|
2106
|
+
return dest.length ? dest : null;
|
|
2107
|
+
}
|
|
2108
|
+
|
|
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
|
+
|
|
1739
2249
|
/*
|
|
1740
2250
|
How changes in the simplify control should affect other components
|
|
1741
2251
|
|
|
@@ -1904,6 +2414,7 @@
|
|
|
1904
2414
|
var opts = getSimplifyOptions();
|
|
1905
2415
|
mapshaper.simplify(dataset, opts);
|
|
1906
2416
|
gui.session.simplificationApplied(getSimplifyOptionsAsString());
|
|
2417
|
+
updateZ(gui.map.getActiveLayer()); // question: does this update all display layers?
|
|
1907
2418
|
model.updated({
|
|
1908
2419
|
// trigger filtered arc rebuild without redraw if pct is 1
|
|
1909
2420
|
simplify_method: opts.percentage == 1,
|
|
@@ -1955,7 +2466,8 @@
|
|
|
1955
2466
|
function onChange(pct) {
|
|
1956
2467
|
if (_value != pct) {
|
|
1957
2468
|
_value = pct;
|
|
1958
|
-
model.getActiveLayer().dataset.arcs.setRetainedInterval(fromPct(pct));
|
|
2469
|
+
// model.getActiveLayer().dataset.arcs.setRetainedInterval(fromPct(pct));
|
|
2470
|
+
setZ(gui.map.getActiveLayer(), fromPct(pct));
|
|
1959
2471
|
gui.session.updateSimplificationPct(pct);
|
|
1960
2472
|
model.updated({'simplify_amount': true});
|
|
1961
2473
|
updateSliderDisplay();
|
|
@@ -2191,77 +2703,6 @@
|
|
|
2191
2703
|
}
|
|
2192
2704
|
}
|
|
2193
2705
|
|
|
2194
|
-
// Assumes projections are available
|
|
2195
|
-
|
|
2196
|
-
function needReprojectionForDisplay(sourceCRS, displayCRS) {
|
|
2197
|
-
if (!sourceCRS || !displayCRS) {
|
|
2198
|
-
return false;
|
|
2199
|
-
}
|
|
2200
|
-
if (internal.crsAreEqual(sourceCRS, displayCRS)) {
|
|
2201
|
-
return false;
|
|
2202
|
-
}
|
|
2203
|
-
return true;
|
|
2204
|
-
}
|
|
2205
|
-
|
|
2206
|
-
function projectArcsForDisplay_v1(arcs, src, dest) {
|
|
2207
|
-
var copy = arcs.getCopy(); // need to flatten first?
|
|
2208
|
-
var proj = internal.getProjTransform(src, dest);
|
|
2209
|
-
internal.projectArcs(copy, proj); // need to densify arcs?
|
|
2210
|
-
return copy;
|
|
2211
|
-
}
|
|
2212
|
-
|
|
2213
|
-
function projectArcsForDisplay(arcs, src, dest) {
|
|
2214
|
-
var copy = arcs.getCopy(); // need to flatten first?
|
|
2215
|
-
var proj = internal.getProjTransform2(src, dest);
|
|
2216
|
-
internal.projectArcs2(copy, proj); // need to densify arcs?
|
|
2217
|
-
return copy;
|
|
2218
|
-
}
|
|
2219
|
-
|
|
2220
|
-
function projectPointsForDisplay(lyr, src, dest) {
|
|
2221
|
-
var copy = utils$1.extend({}, lyr);
|
|
2222
|
-
var proj = internal.getProjTransform2(src, dest);
|
|
2223
|
-
copy.shapes = internal.cloneShapes(lyr.shapes);
|
|
2224
|
-
internal.projectPointLayer(copy, proj);
|
|
2225
|
-
return copy;
|
|
2226
|
-
}
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
// Update map extent and trigger redraw, after a new display CRS has been applied
|
|
2230
|
-
function projectMapExtent(ext, src, dest, newBounds) {
|
|
2231
|
-
var oldBounds = ext.getBounds();
|
|
2232
|
-
var oldScale = ext.scale();
|
|
2233
|
-
var newCP, proj;
|
|
2234
|
-
|
|
2235
|
-
// if source or destination CRS is unknown, show full extent
|
|
2236
|
-
// if map is at full extent, show full extent
|
|
2237
|
-
// TODO: handle case that scale is 1 and map is panned away from center
|
|
2238
|
-
if (ext.scale() == 1 || !dest) {
|
|
2239
|
-
ext.setBounds(newBounds);
|
|
2240
|
-
ext.home(); // sets full extent and triggers redraw
|
|
2241
|
-
} else {
|
|
2242
|
-
// if map is zoomed, stay centered on the same geographic location, at the same relative scale
|
|
2243
|
-
proj = internal.getProjTransform2(src, dest);
|
|
2244
|
-
newCP = proj(oldBounds.centerX(), oldBounds.centerY());
|
|
2245
|
-
ext.setBounds(newBounds);
|
|
2246
|
-
if (!newCP) {
|
|
2247
|
-
// projection of center point failed; use center of bounds
|
|
2248
|
-
// (also consider just resetting the view using ext.home())
|
|
2249
|
-
newCP = [newBounds.centerX(), newBounds.centerY()];
|
|
2250
|
-
}
|
|
2251
|
-
ext.recenter(newCP[0], newCP[1], oldScale);
|
|
2252
|
-
}
|
|
2253
|
-
}
|
|
2254
|
-
|
|
2255
|
-
// Called from console; for testing dynamic crs
|
|
2256
|
-
function setDisplayProjection(gui, cmd) {
|
|
2257
|
-
var arg = cmd.replace(/^projd[ ]*/, '');
|
|
2258
|
-
if (arg) {
|
|
2259
|
-
gui.map.setDisplayCRS(internal.getCRS(arg));
|
|
2260
|
-
} else {
|
|
2261
|
-
gui.map.setDisplayCRS(null);
|
|
2262
|
-
}
|
|
2263
|
-
}
|
|
2264
|
-
|
|
2265
2706
|
function Console(gui) {
|
|
2266
2707
|
var model = gui.model;
|
|
2267
2708
|
var CURSOR = '$ ';
|
|
@@ -2346,8 +2787,8 @@
|
|
|
2346
2787
|
// when an instance loses focus.
|
|
2347
2788
|
internal.setLoggingFunctions(consoleMessage, consoleError, consoleStop);
|
|
2348
2789
|
gui.container.addClass('console-open');
|
|
2349
|
-
gui.dispatchEvent('resize');
|
|
2350
2790
|
el.show();
|
|
2791
|
+
gui.dispatchEvent('resize');
|
|
2351
2792
|
input.node().focus();
|
|
2352
2793
|
history = getHistory();
|
|
2353
2794
|
}
|
|
@@ -2902,47 +3343,6 @@
|
|
|
2902
3343
|
|
|
2903
3344
|
utils$1.inherit(RepairControl, EventDispatcher);
|
|
2904
3345
|
|
|
2905
|
-
function filterLayerByIds(lyr, ids) {
|
|
2906
|
-
var shapes;
|
|
2907
|
-
if (lyr.shapes) {
|
|
2908
|
-
shapes = ids.map(function(id) {
|
|
2909
|
-
return lyr.shapes[id];
|
|
2910
|
-
});
|
|
2911
|
-
return utils$1.defaults({shapes: shapes, data: null}, lyr);
|
|
2912
|
-
}
|
|
2913
|
-
return lyr;
|
|
2914
|
-
}
|
|
2915
|
-
|
|
2916
|
-
function formatLayerNameForDisplay(name) {
|
|
2917
|
-
return name || '[unnamed]';
|
|
2918
|
-
}
|
|
2919
|
-
|
|
2920
|
-
function cleanLayerName(raw) {
|
|
2921
|
-
return raw.replace(/[\n\t/\\]/g, '')
|
|
2922
|
-
.replace(/^[\.\s]+/, '').replace(/[\.\s]+$/, '');
|
|
2923
|
-
}
|
|
2924
|
-
|
|
2925
|
-
function updateLayerStackOrder(layers) {
|
|
2926
|
-
// 1. assign ascending ids to unassigned layers above the range of other layers
|
|
2927
|
-
layers.forEach(function(o, i) {
|
|
2928
|
-
if (!o.layer.stack_id) o.layer.stack_id = 1e6 + i;
|
|
2929
|
-
});
|
|
2930
|
-
// 2. sort in ascending order
|
|
2931
|
-
layers.sort(function(a, b) {
|
|
2932
|
-
return a.layer.stack_id - b.layer.stack_id;
|
|
2933
|
-
});
|
|
2934
|
-
// 3. assign consecutve ids
|
|
2935
|
-
layers.forEach(function(o, i) {
|
|
2936
|
-
o.layer.stack_id = i + 1;
|
|
2937
|
-
});
|
|
2938
|
-
return layers;
|
|
2939
|
-
}
|
|
2940
|
-
|
|
2941
|
-
function sortLayersForMenuDisplay(layers) {
|
|
2942
|
-
layers = updateLayerStackOrder(layers);
|
|
2943
|
-
return layers.reverse();
|
|
2944
|
-
}
|
|
2945
|
-
|
|
2946
3346
|
// import { groupLayersByDataset } from '../dataset/mapshaper-target-utils';
|
|
2947
3347
|
|
|
2948
3348
|
// Export buttons and their behavior
|
|
@@ -3669,7 +4069,6 @@
|
|
|
3669
4069
|
offset = 0;
|
|
3670
4070
|
}
|
|
3671
4071
|
|
|
3672
|
-
|
|
3673
4072
|
function isUndoEvt(e) {
|
|
3674
4073
|
return (e.ctrlKey || e.metaKey) && !e.shiftKey && e.key == 'z';
|
|
3675
4074
|
}
|
|
@@ -3691,18 +4090,17 @@
|
|
|
3691
4090
|
e.stopPropagation();
|
|
3692
4091
|
e.preventDefault();
|
|
3693
4092
|
}
|
|
3694
|
-
|
|
3695
4093
|
}, this, 10);
|
|
3696
4094
|
|
|
3697
|
-
// undo/redo point/symbol dragging
|
|
3698
|
-
//
|
|
3699
|
-
gui.on('symbol_dragstart', function(e) {
|
|
3700
|
-
stashedUndo = this.makePointSetter(e.FID);
|
|
3701
|
-
}, this);
|
|
3702
|
-
|
|
3703
4095
|
gui.on('symbol_dragend', function(e) {
|
|
3704
|
-
var
|
|
3705
|
-
|
|
4096
|
+
var target = e.data.target;
|
|
4097
|
+
var undo = function() {
|
|
4098
|
+
setPointCoords(target, e.FID, e.startCoords);
|
|
4099
|
+
};
|
|
4100
|
+
var redo = function() {
|
|
4101
|
+
setPointCoords(target, e.FID, e.endCoords);
|
|
4102
|
+
};
|
|
4103
|
+
this.addHistoryState(undo, redo);
|
|
3706
4104
|
}, this);
|
|
3707
4105
|
|
|
3708
4106
|
// undo/redo label dragging
|
|
@@ -3729,35 +4127,33 @@
|
|
|
3729
4127
|
}, this);
|
|
3730
4128
|
|
|
3731
4129
|
gui.on('vertex_dragend', function(e) {
|
|
3732
|
-
var target =
|
|
3733
|
-
var
|
|
3734
|
-
var
|
|
3735
|
-
var endPoint = internal.getVertexCoords(e.ids[0], arcs);
|
|
4130
|
+
var target = e.data.target;
|
|
4131
|
+
var startPoint = e.points[0]; // in data coords
|
|
4132
|
+
var endPoint = getVertexCoords(target, e.ids[0]);
|
|
3736
4133
|
var undo = function() {
|
|
3737
4134
|
if (e.insertion) {
|
|
3738
|
-
|
|
4135
|
+
deleteVertex$1(target, e.ids[0]);
|
|
3739
4136
|
} else {
|
|
3740
|
-
|
|
4137
|
+
setVertexCoords(target, e.ids, startPoint);
|
|
3741
4138
|
}
|
|
3742
4139
|
};
|
|
3743
4140
|
var redo = function() {
|
|
3744
4141
|
if (e.insertion) {
|
|
3745
|
-
|
|
4142
|
+
insertVertex$1(target, e.ids[0], endPoint);
|
|
3746
4143
|
}
|
|
3747
|
-
|
|
4144
|
+
setVertexCoords(target, e.ids, endPoint);
|
|
3748
4145
|
};
|
|
3749
4146
|
this.addHistoryState(undo, redo);
|
|
3750
4147
|
}, this);
|
|
3751
4148
|
|
|
3752
4149
|
gui.on('vertex_delete', function(e) {
|
|
3753
|
-
|
|
3754
|
-
var
|
|
3755
|
-
var p = arcs.getVertex2(e.vertex_id);
|
|
4150
|
+
// get vertex coords in data coordinates (not display coordinates);
|
|
4151
|
+
var p = getVertexCoords(e.data.target, e.vertex_id);
|
|
3756
4152
|
var redo = function() {
|
|
3757
|
-
|
|
4153
|
+
deleteVertex$1(e.data.target, e.vertex_id);
|
|
3758
4154
|
};
|
|
3759
4155
|
var undo = function() {
|
|
3760
|
-
|
|
4156
|
+
insertVertex$1(e.data.target, e.vertex_id, p);
|
|
3761
4157
|
};
|
|
3762
4158
|
this.addHistoryState(undo, redo);
|
|
3763
4159
|
}, this);
|
|
@@ -3766,14 +4162,6 @@
|
|
|
3766
4162
|
reset();
|
|
3767
4163
|
};
|
|
3768
4164
|
|
|
3769
|
-
this.makePointSetter = function(i) {
|
|
3770
|
-
var target = gui.model.getActiveLayer();
|
|
3771
|
-
var shp = cloneShape(target.layer.shapes[i]);
|
|
3772
|
-
return function() {
|
|
3773
|
-
target.layer.shapes[i] = shp;
|
|
3774
|
-
};
|
|
3775
|
-
};
|
|
3776
|
-
|
|
3777
4165
|
this.makeDataSetter = function(id) {
|
|
3778
4166
|
var target = gui.model.getActiveLayer();
|
|
3779
4167
|
var rec = copyRecord(target.layer.data.getRecordAt(id));
|
|
@@ -3783,15 +4171,6 @@
|
|
|
3783
4171
|
};
|
|
3784
4172
|
};
|
|
3785
4173
|
|
|
3786
|
-
this.makeVertexSetter = function(ids) {
|
|
3787
|
-
var target = gui.model.getActiveLayer();
|
|
3788
|
-
var arcs = target.dataset.arcs;
|
|
3789
|
-
var p = internal.getVertexCoords(ids[0], arcs);
|
|
3790
|
-
return function() {
|
|
3791
|
-
snapVerticesToPoint(ids, p, arcs, true);
|
|
3792
|
-
};
|
|
3793
|
-
};
|
|
3794
|
-
|
|
3795
4174
|
this.addHistoryState = function(undo, redo) {
|
|
3796
4175
|
if (offset > 0) {
|
|
3797
4176
|
history.splice(-offset);
|
|
@@ -5503,6 +5882,8 @@
|
|
|
5503
5882
|
// avoid re-allocating memory
|
|
5504
5883
|
var xx2 = new Float64Array(data.xx.buffer, 0, n-1);
|
|
5505
5884
|
var yy2 = new Float64Array(data.yy.buffer, 0, n-1);
|
|
5885
|
+
var zz2 = arcs.isFlat() ? null : new Float64Array(data.zz.buffer, 0, n-1);
|
|
5886
|
+
var z = arcs.getRetainedInterval();
|
|
5506
5887
|
var count = 0;
|
|
5507
5888
|
var found = false;
|
|
5508
5889
|
for (var j=0; j<nn.length; j++) {
|
|
@@ -5516,24 +5897,31 @@
|
|
|
5516
5897
|
utils.copyElements(data.yy, 0, yy2, 0, i);
|
|
5517
5898
|
utils.copyElements(data.xx, i+1, xx2, i, n-i-1);
|
|
5518
5899
|
utils.copyElements(data.yy, i+1, yy2, i, n-i-1);
|
|
5519
|
-
|
|
5900
|
+
if (zz2) {
|
|
5901
|
+
utils.copyElements(data.zz, 0, zz2, 0, i);
|
|
5902
|
+
utils.copyElements(data.zz, i+1, zz2, i, n-i-1);
|
|
5903
|
+
}
|
|
5904
|
+
arcs.updateVertexData(nn, xx2, yy2, zz2);
|
|
5905
|
+
arcs.setRetainedInterval(z);
|
|
5520
5906
|
}
|
|
5521
5907
|
|
|
5522
5908
|
function insertVertex(arcs, i, p) {
|
|
5523
|
-
// TODO: add extra bytes to the buffers, to reduce new memory allocation
|
|
5524
5909
|
var data = arcs.getVertexData();
|
|
5525
5910
|
var nn = data.nn;
|
|
5526
5911
|
var n = data.xx.length;
|
|
5527
5912
|
var count = 0;
|
|
5528
5913
|
var found = false;
|
|
5529
|
-
var xx2, yy2;
|
|
5914
|
+
var xx2, yy2, zz2;
|
|
5530
5915
|
// avoid re-allocating memory on each insertion
|
|
5531
5916
|
if (data.xx.buffer.byteLength >= data.xx.length * 8 + 8) {
|
|
5532
5917
|
xx2 = new Float64Array(data.xx.buffer, 0, n+1);
|
|
5533
5918
|
yy2 = new Float64Array(data.yy.buffer, 0, n+1);
|
|
5534
5919
|
} else {
|
|
5535
|
-
xx2 = new Float64Array(new ArrayBuffer((n +
|
|
5536
|
-
yy2 = new Float64Array(new ArrayBuffer((n +
|
|
5920
|
+
xx2 = new Float64Array(new ArrayBuffer((n + 50) * 8), 0, n+1);
|
|
5921
|
+
yy2 = new Float64Array(new ArrayBuffer((n + 50) * 8), 0, n+1);
|
|
5922
|
+
}
|
|
5923
|
+
if (!arcs.isFlat()) {
|
|
5924
|
+
zz2 = new Float64Array(new ArrayBuffer((n + 1) * 8), 0, n+1);
|
|
5537
5925
|
}
|
|
5538
5926
|
for (var j=0; j<nn.length; j++) {
|
|
5539
5927
|
count += nn[j];
|
|
@@ -5548,7 +5936,12 @@
|
|
|
5548
5936
|
utils.copyElements(data.yy, i, yy2, i+1, n-i);
|
|
5549
5937
|
xx2[i] = p[0];
|
|
5550
5938
|
yy2[i] = p[1];
|
|
5551
|
-
|
|
5939
|
+
if (zz2) {
|
|
5940
|
+
zz2[i] = Infinity;
|
|
5941
|
+
utils.copyElements(data.zz, 0, zz2, 0, i);
|
|
5942
|
+
utils.copyElements(data.zz, i, zz2, i+1, n-i);
|
|
5943
|
+
}
|
|
5944
|
+
arcs.updateVertexData(nn, xx2, yy2, zz2);
|
|
5552
5945
|
}
|
|
5553
5946
|
|
|
5554
5947
|
function getShapeHitTest(displayLayer, ext, interactionMode) {
|
|
@@ -5675,7 +6068,7 @@
|
|
|
5675
6068
|
hits.push(id);
|
|
5676
6069
|
}
|
|
5677
6070
|
});
|
|
5678
|
-
//
|
|
6071
|
+
// TODO: add info on what part of a shape gets hit
|
|
5679
6072
|
return {
|
|
5680
6073
|
ids: utils$1.uniq(hits) // multipoint features can register multiple hits
|
|
5681
6074
|
};
|
|
@@ -6283,15 +6676,18 @@
|
|
|
6283
6676
|
function onMouseChange(e) {
|
|
6284
6677
|
if (!enabled) return;
|
|
6285
6678
|
if (isOverMap(e)) {
|
|
6286
|
-
displayCoords(
|
|
6679
|
+
displayCoords(gui.map.translatePixelCoords(e.x, e.y));
|
|
6287
6680
|
} else {
|
|
6288
6681
|
clearCoords();
|
|
6289
6682
|
}
|
|
6290
6683
|
}
|
|
6291
6684
|
|
|
6292
6685
|
function displayCoords(p) {
|
|
6293
|
-
var
|
|
6294
|
-
var
|
|
6686
|
+
var p1 = gui.map.translatePixelCoords(0, ext.height());
|
|
6687
|
+
var p2 = gui.map.translatePixelCoords(ext.width(), 0);
|
|
6688
|
+
var bbox = p1.concat(p2);
|
|
6689
|
+
var decimals = internal.getBoundsPrecisionForDisplay(bbox);
|
|
6690
|
+
var str = internal.getRoundedCoordString(p, decimals);
|
|
6295
6691
|
readout.text(str).show();
|
|
6296
6692
|
}
|
|
6297
6693
|
|
|
@@ -6756,6 +7152,10 @@
|
|
|
6756
7152
|
ext.zoomToExtent(e.value, _fx, _fy);
|
|
6757
7153
|
});
|
|
6758
7154
|
|
|
7155
|
+
mouse.on('click', function(e) {
|
|
7156
|
+
gui.dispatchEvent('map_click', e);
|
|
7157
|
+
});
|
|
7158
|
+
|
|
6759
7159
|
mouse.on('dblclick', function(e) {
|
|
6760
7160
|
if (disabled()) return;
|
|
6761
7161
|
zoomByPct(getZoomInPct(), e.x / ext.width(), e.y / ext.height());
|
|
@@ -7491,31 +7891,38 @@
|
|
|
7491
7891
|
}
|
|
7492
7892
|
|
|
7493
7893
|
function initPointDragging(gui, ext, hit) {
|
|
7494
|
-
|
|
7894
|
+
var symbolInfo;
|
|
7495
7895
|
function active(e) {
|
|
7496
7896
|
return e.id > -1 && gui.interaction.getMode() == 'location';
|
|
7497
7897
|
}
|
|
7498
7898
|
|
|
7499
7899
|
hit.on('dragstart', function(e) {
|
|
7500
7900
|
if (!active(e)) return;
|
|
7501
|
-
|
|
7901
|
+
var target = hit.getHitTarget();
|
|
7902
|
+
symbolInfo = {
|
|
7903
|
+
FID: e.id,
|
|
7904
|
+
startCoords: getPointCoords(target, e.id),
|
|
7905
|
+
target: target
|
|
7906
|
+
};
|
|
7502
7907
|
});
|
|
7503
7908
|
|
|
7504
7909
|
hit.on('drag', function(e) {
|
|
7505
7910
|
if (!active(e)) return;
|
|
7506
|
-
|
|
7507
|
-
var p = getPointCoordsById(e.id,
|
|
7911
|
+
// TODO: support multi points... get id of closest part to the pointer
|
|
7912
|
+
var p = getPointCoordsById(e.id, symbolInfo.target.layer);
|
|
7508
7913
|
if (!p) return;
|
|
7509
7914
|
var diff = translateDeltaDisplayCoords(e.dx, e.dy, ext);
|
|
7510
7915
|
p[0] += diff[0];
|
|
7511
7916
|
p[1] += diff[1];
|
|
7512
7917
|
gui.dispatchEvent('map-needs-refresh');
|
|
7513
|
-
gui.dispatchEvent('symbol_drag', {FID: e.id});
|
|
7514
7918
|
});
|
|
7515
7919
|
|
|
7516
7920
|
hit.on('dragend', function(e) {
|
|
7517
|
-
if (!active(e)) return;
|
|
7518
|
-
|
|
7921
|
+
if (!active(e) || !symbolInfo ) return;
|
|
7922
|
+
updatePointCoords(symbolInfo.target, e.id);
|
|
7923
|
+
symbolInfo.endCoords = getPointCoords(symbolInfo.target, e.id);
|
|
7924
|
+
gui.dispatchEvent('symbol_dragend', symbolInfo);
|
|
7925
|
+
symbolInfo = null;
|
|
7519
7926
|
});
|
|
7520
7927
|
|
|
7521
7928
|
function translateDeltaDisplayCoords(dx, dy, ext) {
|
|
@@ -7569,8 +7976,11 @@
|
|
|
7569
7976
|
if (pixelDist > HOVER_THRESHOLD) {
|
|
7570
7977
|
return null;
|
|
7571
7978
|
}
|
|
7572
|
-
var points = nearestIds.map(function(i) {
|
|
7979
|
+
var points = nearestIds.map(function(i) {
|
|
7980
|
+
return getVertexCoords(target, i); // data coordinates
|
|
7981
|
+
});
|
|
7573
7982
|
return {
|
|
7983
|
+
target: target,
|
|
7574
7984
|
ids: nearestIds,
|
|
7575
7985
|
points: points
|
|
7576
7986
|
};
|
|
@@ -7580,8 +7990,7 @@
|
|
|
7580
7990
|
var target = hit.getHitTarget();
|
|
7581
7991
|
if (!target.arcs.isFlat()) return null; // vertex insertion not supported with simplification
|
|
7582
7992
|
var p = ext.translatePixelCoords(e.x, e.y);
|
|
7583
|
-
var
|
|
7584
|
-
var midpoint = findNearestMidpoint(p, shp, target.arcs);
|
|
7993
|
+
var midpoint = findNearestMidpoint(p, e.id, target);
|
|
7585
7994
|
if (!midpoint ||
|
|
7586
7995
|
midpoint.distance / ext.getPixelSize() > MIDPOINT_THRESHOLD) return null;
|
|
7587
7996
|
return midpoint;
|
|
@@ -7591,8 +8000,9 @@
|
|
|
7591
8000
|
if (!active()) return;
|
|
7592
8001
|
if (insertionPoint) {
|
|
7593
8002
|
var target = hit.getHitTarget();
|
|
7594
|
-
|
|
8003
|
+
insertVertex$1(target, insertionPoint.i, insertionPoint.point);
|
|
7595
8004
|
dragInfo = {
|
|
8005
|
+
target: target,
|
|
7596
8006
|
insertion: true,
|
|
7597
8007
|
ids: [insertionPoint.i],
|
|
7598
8008
|
points: [insertionPoint.point]
|
|
@@ -7624,6 +8034,7 @@
|
|
|
7624
8034
|
// kludge to get dataset to recalculate internal bounding boxes
|
|
7625
8035
|
hit.getHitTarget().arcs.transformPoints(function() {});
|
|
7626
8036
|
clearHoverVertex();
|
|
8037
|
+
updateVertexCoords(dragInfo.target, dragInfo.ids);
|
|
7627
8038
|
gui.dispatchEvent('vertex_dragend', dragInfo);
|
|
7628
8039
|
gui.dispatchEvent('map-needs-refresh');
|
|
7629
8040
|
dragInfo = null;
|
|
@@ -7641,9 +8052,10 @@
|
|
|
7641
8052
|
return;
|
|
7642
8053
|
}
|
|
7643
8054
|
gui.dispatchEvent('vertex_delete', {
|
|
8055
|
+
target: target,
|
|
7644
8056
|
vertex_id: vId
|
|
7645
8057
|
});
|
|
7646
|
-
|
|
8058
|
+
deleteVertex$1(target, vId);
|
|
7647
8059
|
clearHoverVertex();
|
|
7648
8060
|
gui.dispatchEvent('map-needs-refresh');
|
|
7649
8061
|
});
|
|
@@ -7661,7 +8073,7 @@
|
|
|
7661
8073
|
// if hovering near a segment midpoint: show the midpoint and save midpoint info
|
|
7662
8074
|
insertionPoint = findVertexInsertionPoint(e);
|
|
7663
8075
|
if (insertionPoint) {
|
|
7664
|
-
hit.setHoverVertex(insertionPoint.
|
|
8076
|
+
hit.setHoverVertex(insertionPoint.displayPoint);
|
|
7665
8077
|
} else {
|
|
7666
8078
|
// pointer is not over a vertex: clear any hover effect
|
|
7667
8079
|
clearHoverVertex();
|
|
@@ -7672,7 +8084,9 @@
|
|
|
7672
8084
|
|
|
7673
8085
|
// Given a location @p (e.g. corresponding to the mouse pointer location),
|
|
7674
8086
|
// find the midpoint of two vertices on @shp suitable for inserting a new vertex
|
|
7675
|
-
function findNearestMidpoint(p,
|
|
8087
|
+
function findNearestMidpoint(p, fid, target) {
|
|
8088
|
+
var arcs = target.arcs;
|
|
8089
|
+
var shp = target.layer.shapes[fid];
|
|
7676
8090
|
var minDist = Infinity, v;
|
|
7677
8091
|
internal.forEachSegmentInShape(shp, arcs, function(i, j, xx, yy) {
|
|
7678
8092
|
var x1 = xx[i],
|
|
@@ -7681,6 +8095,7 @@
|
|
|
7681
8095
|
y2 = yy[j],
|
|
7682
8096
|
cx = (x1 + x2) / 2,
|
|
7683
8097
|
cy = (y1 + y2) / 2,
|
|
8098
|
+
midpoint = [cx, cy],
|
|
7684
8099
|
dist = geom.distance2D(cx, cy, p[0], p[1]);
|
|
7685
8100
|
if (dist < minDist) {
|
|
7686
8101
|
minDist = dist;
|
|
@@ -7688,7 +8103,8 @@
|
|
|
7688
8103
|
i: (i < j ? i : j) + 1, // insertion point
|
|
7689
8104
|
segment: [i, j],
|
|
7690
8105
|
segmentLen: geom.distance2D(x1, y1, x2, y2),
|
|
7691
|
-
|
|
8106
|
+
displayPoint: midpoint,
|
|
8107
|
+
point: translateDisplayPoint(target, midpoint),
|
|
7692
8108
|
distance: dist
|
|
7693
8109
|
};
|
|
7694
8110
|
}
|
|
@@ -9062,7 +9478,7 @@
|
|
|
9062
9478
|
var popup = gui.container.findChild('.box-tool-options');
|
|
9063
9479
|
var coords = popup.findChild('.box-coords');
|
|
9064
9480
|
var _on = false;
|
|
9065
|
-
var
|
|
9481
|
+
var bboxDisplayCoords, bboxPixels, bboxDataCoords;
|
|
9066
9482
|
|
|
9067
9483
|
var infoBtn = new SimpleButton(popup.findChild('.info-btn')).on('click', function() {
|
|
9068
9484
|
if (coords.visible()) hideCoords(); else showCoords();
|
|
@@ -9093,7 +9509,7 @@
|
|
|
9093
9509
|
});
|
|
9094
9510
|
|
|
9095
9511
|
new SimpleButton(popup.findChild('.clip-btn')).on('click', function() {
|
|
9096
|
-
runCommand('-clip bbox2=' +
|
|
9512
|
+
runCommand('-clip bbox2=' + bboxDataCoords.join(','));
|
|
9097
9513
|
});
|
|
9098
9514
|
|
|
9099
9515
|
gui.addMode('box_tool', turnOn, turnOff);
|
|
@@ -9109,8 +9525,8 @@
|
|
|
9109
9525
|
// Update the visible rectangle when the map view changes
|
|
9110
9526
|
// (e.g. during zooming or panning)
|
|
9111
9527
|
ext.on('change', function() {
|
|
9112
|
-
if (!_on || !box.visible() || !
|
|
9113
|
-
var b = coordsToPix(
|
|
9528
|
+
if (!_on || !box.visible() || !bboxDisplayCoords) return;
|
|
9529
|
+
var b = coordsToPix(bboxDisplayCoords);
|
|
9114
9530
|
var pos = ext.position();
|
|
9115
9531
|
var dx = pos.pageX,
|
|
9116
9532
|
dy = pos.pageY;
|
|
@@ -9125,7 +9541,7 @@
|
|
|
9125
9541
|
gui.on('box_drag', function(e) {
|
|
9126
9542
|
var b = e.page_bbox;
|
|
9127
9543
|
bboxPixels = e.map_bbox;
|
|
9128
|
-
|
|
9544
|
+
bboxDisplayCoords = pixToCoords(bboxPixels);
|
|
9129
9545
|
if (_on || inZoomMode()) {
|
|
9130
9546
|
box.show(b[0], b[1], b[2], b[3]);
|
|
9131
9547
|
}
|
|
@@ -9133,7 +9549,8 @@
|
|
|
9133
9549
|
|
|
9134
9550
|
gui.on('box_drag_end', function(e) {
|
|
9135
9551
|
bboxPixels = e.map_bbox;
|
|
9136
|
-
|
|
9552
|
+
bboxDisplayCoords = pixToCoords(bboxPixels);
|
|
9553
|
+
bboxDataCoords = getBBoxCoords(gui.map.getActiveLayer(), bboxDisplayCoords);
|
|
9137
9554
|
if (inZoomMode()) {
|
|
9138
9555
|
box.hide();
|
|
9139
9556
|
nav.zoomToBbox(bboxPixels);
|
|
@@ -9157,7 +9574,7 @@
|
|
|
9157
9574
|
|
|
9158
9575
|
function showCoords() {
|
|
9159
9576
|
El(infoBtn.node()).addClass('selected-btn');
|
|
9160
|
-
coords.text(
|
|
9577
|
+
coords.text(bboxDataCoords.join(','));
|
|
9161
9578
|
coords.show();
|
|
9162
9579
|
GUI.selectElement(coords.node());
|
|
9163
9580
|
}
|
|
@@ -9204,238 +9621,200 @@
|
|
|
9204
9621
|
return self;
|
|
9205
9622
|
}
|
|
9206
9623
|
|
|
9207
|
-
|
|
9208
|
-
|
|
9209
|
-
|
|
9210
|
-
|
|
9211
|
-
|
|
9212
|
-
|
|
9213
|
-
|
|
9214
|
-
|
|
9215
|
-
|
|
9216
|
-
|
|
9217
|
-
|
|
9218
|
-
|
|
9219
|
-
|
|
9220
|
-
|
|
9221
|
-
|
|
9222
|
-
|
|
9223
|
-
|
|
9224
|
-
|
|
9225
|
-
|
|
9226
|
-
|
|
9227
|
-
|
|
9228
|
-
|
|
9229
|
-
|
|
9230
|
-
|
|
9231
|
-
|
|
9232
|
-
|
|
9233
|
-
|
|
9624
|
+
function loadScript(url, cb) {
|
|
9625
|
+
var script = document.createElement('script');
|
|
9626
|
+
script.onload = cb;
|
|
9627
|
+
script.src = url;
|
|
9628
|
+
document.head.appendChild(script);
|
|
9629
|
+
}
|
|
9630
|
+
|
|
9631
|
+
function loadStylesheet(url) {
|
|
9632
|
+
var el = document.createElement('link');
|
|
9633
|
+
el.rel = 'stylesheet';
|
|
9634
|
+
el.type = 'text/css';
|
|
9635
|
+
el.media = 'screen';
|
|
9636
|
+
el.href = url;
|
|
9637
|
+
document.head.appendChild(el);
|
|
9638
|
+
}
|
|
9639
|
+
|
|
9640
|
+
function Basemap(gui, ext) {
|
|
9641
|
+
var menu = gui.container.findChild('.basemap-options');
|
|
9642
|
+
var list = menu.findChild('.basemap-styles');
|
|
9643
|
+
var container = gui.container.findChild('.basemap-container');
|
|
9644
|
+
var basemapBtn = gui.container.findChild('.basemap-btn');
|
|
9645
|
+
var basemapNote = gui.container.findChild('.basemap-note');
|
|
9646
|
+
var basemapWarning = gui.container.findChild('.basemap-warning');
|
|
9647
|
+
var mapEl = gui.container.findChild('.basemap');
|
|
9648
|
+
var extentNote = El('div').addClass('basemap-prompt').appendTo(container).hide();
|
|
9649
|
+
var params = window.mapboxParams;
|
|
9650
|
+
var map;
|
|
9651
|
+
var activeStyle;
|
|
9652
|
+
var loading = false;
|
|
9653
|
+
|
|
9654
|
+
if (params) {
|
|
9655
|
+
init();
|
|
9656
|
+
} else {
|
|
9657
|
+
basemapBtn.hide();
|
|
9234
9658
|
}
|
|
9235
9659
|
|
|
9236
|
-
|
|
9237
|
-
|
|
9238
|
-
|
|
9239
|
-
|
|
9240
|
-
|
|
9241
|
-
//
|
|
9242
|
-
var unitsPerPixel = 1/ext.getTransform().mx,
|
|
9243
|
-
useFiltering = filteredArcs && unitsPerPixel > filteredSegLen * 1.5;
|
|
9244
|
-
return useFiltering ? filteredArcs : unfilteredArcs;
|
|
9245
|
-
};
|
|
9246
|
-
|
|
9247
|
-
return unfilteredArcs;
|
|
9248
|
-
}
|
|
9660
|
+
function init() {
|
|
9661
|
+
gui.addMode('basemap', turnOn, turnOff, basemapBtn);
|
|
9662
|
+
// model.on('select', function() {
|
|
9663
|
+
// TODO: hide basemap
|
|
9664
|
+
// if (gui.getMode() == 'basemap') gui.clearMode();
|
|
9665
|
+
// });
|
|
9249
9666
|
|
|
9250
|
-
|
|
9251
|
-
|
|
9252
|
-
|
|
9253
|
-
|
|
9254
|
-
gutter = 6,
|
|
9255
|
-
arcs = [],
|
|
9256
|
-
shapes = [],
|
|
9257
|
-
aspectRatio = 1.1,
|
|
9258
|
-
x, y, col, row, blockSize;
|
|
9667
|
+
new SimpleButton(menu.findChild('.close-btn')).on('click', function() {
|
|
9668
|
+
gui.clearMode();
|
|
9669
|
+
turnOff();
|
|
9670
|
+
});
|
|
9259
9671
|
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
cellHeight = 4;
|
|
9265
|
-
aspectRatio = 1.45;
|
|
9266
|
-
} else if (n > 5000) {
|
|
9267
|
-
cellWidth = 5;
|
|
9268
|
-
gutter = 3;
|
|
9269
|
-
aspectRatio = 1.45;
|
|
9270
|
-
} else if (n > 1000) {
|
|
9271
|
-
gutter = 3;
|
|
9272
|
-
cellWidth = 8;
|
|
9273
|
-
aspectRatio = 1.3;
|
|
9274
|
-
}
|
|
9672
|
+
gui.on('map_click', function() {
|
|
9673
|
+
// close menu if user click on the map
|
|
9674
|
+
if (gui.getMode() == 'basemap') gui.clearMode();
|
|
9675
|
+
});
|
|
9275
9676
|
|
|
9276
|
-
|
|
9277
|
-
|
|
9278
|
-
|
|
9279
|
-
|
|
9677
|
+
params.styles.forEach(function(style) {
|
|
9678
|
+
var btn = El('div').html(`<div class="basemap-style-btn"><img src="${style.icon}"></img></div><div class="basemap-style-label">${style.name}</div>`);
|
|
9679
|
+
btn.findChild('.basemap-style-btn').on('click', function() {
|
|
9680
|
+
updateStyle(style == activeStyle ? null : style);
|
|
9681
|
+
updateButtons();
|
|
9682
|
+
});
|
|
9683
|
+
btn.appendTo(list);
|
|
9684
|
+
});
|
|
9280
9685
|
}
|
|
9281
9686
|
|
|
9282
|
-
|
|
9283
|
-
|
|
9284
|
-
|
|
9285
|
-
|
|
9286
|
-
|
|
9287
|
-
if (
|
|
9288
|
-
|
|
9289
|
-
|
|
9687
|
+
function updateStyle(style) {
|
|
9688
|
+
activeStyle = style || null;
|
|
9689
|
+
if (!style) {
|
|
9690
|
+
gui.map.setDisplayCRS(null);
|
|
9691
|
+
hide();
|
|
9692
|
+
} else if (map) {
|
|
9693
|
+
map.setStyle(style.url);
|
|
9694
|
+
refresh();
|
|
9290
9695
|
} else {
|
|
9291
|
-
|
|
9696
|
+
initMap();
|
|
9292
9697
|
}
|
|
9293
9698
|
}
|
|
9294
9699
|
|
|
9295
|
-
function
|
|
9296
|
-
|
|
9297
|
-
|
|
9298
|
-
|
|
9299
|
-
return {
|
|
9300
|
-
layer: {
|
|
9301
|
-
geometry_type: arcs ? 'polygon' : 'point',
|
|
9302
|
-
shapes: shapes,
|
|
9303
|
-
data: table
|
|
9304
|
-
},
|
|
9305
|
-
arcs: arcs ? new internal.ArcCollection(arcs) : null
|
|
9306
|
-
};
|
|
9307
|
-
}
|
|
9308
|
-
|
|
9309
|
-
// displayCRS: CRS to use for display, or null (which clears any current display CRS)
|
|
9310
|
-
function projectDisplayLayer(lyr, displayCRS) {
|
|
9311
|
-
var sourceCRS = internal.getDatasetCRS(lyr.source.dataset);
|
|
9312
|
-
var lyr2;
|
|
9313
|
-
if (!lyr.geographic || !sourceCRS) {
|
|
9314
|
-
return lyr;
|
|
9315
|
-
}
|
|
9316
|
-
if (lyr.dynamic_crs && internal.crsAreEqual(sourceCRS, lyr.dynamic_crs)) {
|
|
9317
|
-
return lyr;
|
|
9318
|
-
}
|
|
9319
|
-
lyr2 = getDisplayLayer(lyr.source.layer, lyr.source.dataset, {crs: displayCRS});
|
|
9320
|
-
// kludge: copy projection-related properties to original layer
|
|
9321
|
-
lyr.dynamic_crs = lyr2.dynamic_crs;
|
|
9322
|
-
lyr.layer = lyr2.layer;
|
|
9323
|
-
if (lyr.style && lyr.style.ids) {
|
|
9324
|
-
// re-apply layer filter
|
|
9325
|
-
lyr.layer = filterLayerByIds(lyr.layer, lyr.style.ids);
|
|
9700
|
+
function updateButtons() {
|
|
9701
|
+
list.findChildren('.basemap-style-btn').forEach(function(el, i) {
|
|
9702
|
+
el.classed('active', params.styles[i] == activeStyle);
|
|
9703
|
+
});
|
|
9326
9704
|
}
|
|
9327
|
-
lyr.bounds = lyr2.bounds;
|
|
9328
|
-
lyr.arcs = lyr2.arcs;
|
|
9329
|
-
}
|
|
9330
|
-
|
|
9331
|
-
|
|
9332
|
-
// Wrap a layer in an object along with information needed for rendering
|
|
9333
|
-
function getDisplayLayer(layer, dataset, opts) {
|
|
9334
|
-
var obj = {
|
|
9335
|
-
layer: null,
|
|
9336
|
-
arcs: null,
|
|
9337
|
-
// display_arcs: null,
|
|
9338
|
-
style: null,
|
|
9339
|
-
source: {
|
|
9340
|
-
layer: layer,
|
|
9341
|
-
dataset: dataset
|
|
9342
|
-
},
|
|
9343
|
-
empty: internal.getFeatureCount(layer) === 0
|
|
9344
|
-
};
|
|
9345
9705
|
|
|
9346
|
-
|
|
9347
|
-
|
|
9348
|
-
|
|
9349
|
-
|
|
9350
|
-
|
|
9351
|
-
|
|
9352
|
-
|
|
9353
|
-
|
|
9354
|
-
|
|
9355
|
-
|
|
9356
|
-
displayArcs = projectArcsForDisplay(dataset.arcs, sourceCRS, displayCRS);
|
|
9706
|
+
function turnOn() {
|
|
9707
|
+
var activeLyr = gui.model.getActiveLayer();
|
|
9708
|
+
var dataCRS = internal.getDatasetCRS(activeLyr.dataset);
|
|
9709
|
+
var displayCRS = gui.map.getDisplayCRS();
|
|
9710
|
+
var warning;
|
|
9711
|
+
|
|
9712
|
+
if (!crsIsUsable(displayCRS) || !crsIsUsable(dataCRS)) {
|
|
9713
|
+
warning = 'The current layer is not compatible with the projection used by the basemaps.';
|
|
9714
|
+
basemapWarning.html(warning).show();
|
|
9715
|
+
basemapNote.hide();
|
|
9357
9716
|
} else {
|
|
9358
|
-
|
|
9717
|
+
basemapNote.show();
|
|
9359
9718
|
}
|
|
9719
|
+
menu.show();
|
|
9720
|
+
}
|
|
9360
9721
|
|
|
9361
|
-
|
|
9362
|
-
|
|
9722
|
+
function turnOff() {
|
|
9723
|
+
basemapWarning.hide();
|
|
9724
|
+
basemapNote.hide();
|
|
9725
|
+
menu.hide();
|
|
9363
9726
|
}
|
|
9364
9727
|
|
|
9365
|
-
|
|
9366
|
-
|
|
9367
|
-
obj.furniture_type = internal.getFurnitureLayerType(layer);
|
|
9368
|
-
obj.layer = layer;
|
|
9369
|
-
// treating furniture layers (other than frame) as tabular for now,
|
|
9370
|
-
// so there is something to show if they are selected
|
|
9371
|
-
obj.tabular = obj.furniture_type != 'frame';
|
|
9372
|
-
} else if (obj.empty) {
|
|
9373
|
-
obj.layer = {shapes: []}; // ideally we should avoid empty layers
|
|
9374
|
-
} else if (!layer.geometry_type) {
|
|
9375
|
-
obj.tabular = true;
|
|
9376
|
-
} else {
|
|
9377
|
-
obj.geographic = true;
|
|
9378
|
-
obj.layer = layer;
|
|
9379
|
-
obj.arcs = displayArcs;
|
|
9728
|
+
function enabled() {
|
|
9729
|
+
return !!(mapEl && params);
|
|
9380
9730
|
}
|
|
9381
9731
|
|
|
9382
|
-
|
|
9383
|
-
|
|
9732
|
+
function show() {
|
|
9733
|
+
gui.container.addClass('basemap-on');
|
|
9734
|
+
mapEl.node().style.display = 'block';
|
|
9384
9735
|
}
|
|
9385
9736
|
|
|
9386
|
-
|
|
9387
|
-
|
|
9388
|
-
|
|
9389
|
-
if (internal.layerHasPoints(layer)) {
|
|
9390
|
-
obj.layer = projectPointsForDisplay(layer, sourceCRS, displayCRS);
|
|
9391
|
-
} else if (internal.layerHasPaths(layer)) {
|
|
9392
|
-
emptyArcs = findEmptyArcs(displayArcs);
|
|
9393
|
-
if (emptyArcs.length > 0) {
|
|
9394
|
-
// Don't try to draw paths containing coordinates that failed to project
|
|
9395
|
-
obj.layer = internal.filterPathLayerByArcIds(obj.layer, emptyArcs);
|
|
9396
|
-
}
|
|
9397
|
-
}
|
|
9737
|
+
function hide() {
|
|
9738
|
+
gui.container.removeClass('basemap-on');
|
|
9739
|
+
mapEl.node().style.display = 'none';
|
|
9398
9740
|
}
|
|
9399
9741
|
|
|
9400
|
-
|
|
9401
|
-
|
|
9402
|
-
|
|
9742
|
+
function getLonLatBounds() {
|
|
9743
|
+
var bbox = ext.getBounds().toArray();
|
|
9744
|
+
var tr = fromWebMercator(bbox[2], bbox[3]);
|
|
9745
|
+
var bl = fromWebMercator(bbox[0], bbox[1]);
|
|
9746
|
+
return bl.concat(tr);
|
|
9747
|
+
}
|
|
9403
9748
|
|
|
9404
9749
|
|
|
9405
|
-
|
|
9406
|
-
|
|
9407
|
-
|
|
9408
|
-
|
|
9750
|
+
function initMap() {
|
|
9751
|
+
if (!enabled() || map || loading) return;
|
|
9752
|
+
loading = true;
|
|
9753
|
+
loadStylesheet(params.css);
|
|
9754
|
+
loadScript(params.js, function() {
|
|
9755
|
+
map = new window.mapboxgl.Map({
|
|
9756
|
+
accessToken: params.key,
|
|
9757
|
+
logoPosition: 'bottom-left',
|
|
9758
|
+
container: mapEl.node(),
|
|
9759
|
+
style: activeStyle.url,
|
|
9760
|
+
bounds: getLonLatBounds(),
|
|
9761
|
+
doubleClickZoom: false,
|
|
9762
|
+
dragPan: false,
|
|
9763
|
+
dragRotate: false,
|
|
9764
|
+
scrollZoom: false,
|
|
9765
|
+
interactive: false,
|
|
9766
|
+
keyboard: false,
|
|
9767
|
+
maxPitch: 0,
|
|
9768
|
+
renderWorldCopies: true // false // false prevents panning off the map
|
|
9769
|
+
});
|
|
9770
|
+
map.on('load', function() {
|
|
9771
|
+
loading = false;
|
|
9772
|
+
refresh();
|
|
9773
|
+
});
|
|
9774
|
+
});
|
|
9775
|
+
}
|
|
9409
9776
|
|
|
9410
|
-
|
|
9411
|
-
|
|
9412
|
-
if (
|
|
9413
|
-
|
|
9414
|
-
|
|
9415
|
-
} else {
|
|
9416
|
-
// if a point layer has no extent (e.g. contains only a single point),
|
|
9417
|
-
// then merge with arc bounds, to place the point in context.
|
|
9418
|
-
bounds = arcBounds.mergeBounds(lyrBounds);
|
|
9419
|
-
}
|
|
9777
|
+
function checkBounds(bbox) {
|
|
9778
|
+
var msg;
|
|
9779
|
+
if (bbox[1] >= -85 && bbox[3] <= 85) {
|
|
9780
|
+
extentNote.hide();
|
|
9781
|
+
return true;
|
|
9420
9782
|
}
|
|
9783
|
+
if (bbox[1] > 0) msg = 'pan south to see the basemap';
|
|
9784
|
+
else if (bbox[3] < 0) msg = 'pan north to see the basemap';
|
|
9785
|
+
else msg = msg = 'zoom in to see the basemap';
|
|
9786
|
+
extentNote.html(msg).show();
|
|
9787
|
+
return false;
|
|
9421
9788
|
}
|
|
9422
9789
|
|
|
9423
|
-
|
|
9424
|
-
|
|
9790
|
+
function crsIsUsable(crs) {
|
|
9791
|
+
if (!crs) return false;
|
|
9792
|
+
if (!internal.isInvertibleCRS(crs)) return false;
|
|
9793
|
+
return true;
|
|
9425
9794
|
}
|
|
9426
|
-
return bounds;
|
|
9427
|
-
}
|
|
9428
9795
|
|
|
9429
|
-
|
|
9430
|
-
|
|
9431
|
-
|
|
9432
|
-
|
|
9433
|
-
|
|
9434
|
-
|
|
9435
|
-
|
|
9796
|
+
function refresh() {
|
|
9797
|
+
if (!enabled() || !map || loading || !activeStyle) return;
|
|
9798
|
+
var crs = gui.map.getDisplayCRS();
|
|
9799
|
+
if (!crsIsUsable(crs)) {
|
|
9800
|
+
hide();
|
|
9801
|
+
return;
|
|
9802
|
+
}
|
|
9803
|
+
if (!internal.isWebMercator(crs)) {
|
|
9804
|
+
gui.map.setDisplayCRS(internal.getCRS('webmercator'));
|
|
9805
|
+
}
|
|
9806
|
+
var bbox = getLonLatBounds();
|
|
9807
|
+
if (!checkBounds(bbox)) {
|
|
9808
|
+
// map does not display outside these bounds
|
|
9809
|
+
hide();
|
|
9810
|
+
} else {
|
|
9811
|
+
show();
|
|
9812
|
+
map.resize();
|
|
9813
|
+
map.fitBounds(bbox, {animate: false});
|
|
9436
9814
|
}
|
|
9437
9815
|
}
|
|
9438
|
-
|
|
9816
|
+
|
|
9817
|
+
return {refresh: refresh}; // called by map when extent changes
|
|
9439
9818
|
}
|
|
9440
9819
|
|
|
9441
9820
|
utils$1.inherit(MshpMap, EventDispatcher);
|
|
@@ -9458,6 +9837,8 @@
|
|
|
9458
9837
|
_inspector, _stack,
|
|
9459
9838
|
_dynamicCRS;
|
|
9460
9839
|
|
|
9840
|
+
var _basemap = new Basemap(gui, _ext);
|
|
9841
|
+
|
|
9461
9842
|
if (gui.options.showMouseCoordinates) {
|
|
9462
9843
|
new CoordinatesDisplay(gui, _ext, _mouse);
|
|
9463
9844
|
}
|
|
@@ -9482,6 +9863,8 @@
|
|
|
9482
9863
|
|
|
9483
9864
|
model.on('update', onUpdate);
|
|
9484
9865
|
|
|
9866
|
+
|
|
9867
|
+
|
|
9485
9868
|
// Update display of segment intersections
|
|
9486
9869
|
this.setIntersectionLayer = function(lyr, dataset) {
|
|
9487
9870
|
if (lyr == _intersectionLyr) return; // no change
|
|
@@ -9499,6 +9882,12 @@
|
|
|
9499
9882
|
target.layer.pinned = !!pinned;
|
|
9500
9883
|
};
|
|
9501
9884
|
|
|
9885
|
+
this.translatePixelCoords = function(x, y) {
|
|
9886
|
+
var p = _ext.translatePixelCoords(x, y);
|
|
9887
|
+
if (!_dynamicCRS) return p;
|
|
9888
|
+
return internal.toLngLat(p, _dynamicCRS);
|
|
9889
|
+
};
|
|
9890
|
+
|
|
9502
9891
|
this.getCenterLngLat = function() {
|
|
9503
9892
|
var bounds = _ext.getBounds();
|
|
9504
9893
|
var crs = this.getDisplayCRS();
|
|
@@ -9520,6 +9909,7 @@
|
|
|
9520
9909
|
this.getExtent = function() {return _ext;};
|
|
9521
9910
|
this.isActiveLayer = isActiveLayer;
|
|
9522
9911
|
this.isVisibleLayer = isVisibleLayer;
|
|
9912
|
+
this.getActiveLayer = function() { return _activeLyr; };
|
|
9523
9913
|
|
|
9524
9914
|
// called by layer menu after layer visibility is updated
|
|
9525
9915
|
this.redraw = function() {
|
|
@@ -9551,6 +9941,7 @@
|
|
|
9551
9941
|
|
|
9552
9942
|
// Update map extent (also triggers redraw)
|
|
9553
9943
|
projectMapExtent(_ext, oldCRS, this.getDisplayCRS(), getFullBounds());
|
|
9944
|
+
_fullBounds = getFullBounds(); // update this so map extent doesn't get reset after next update
|
|
9554
9945
|
};
|
|
9555
9946
|
|
|
9556
9947
|
// Refresh map display in response to data changes, layer selection, etc.
|
|
@@ -9604,6 +9995,7 @@
|
|
|
9604
9995
|
needReset = mapNeedsReset(fullBounds, _fullBounds, _ext.getBounds(), e.flags);
|
|
9605
9996
|
}
|
|
9606
9997
|
|
|
9998
|
+
|
|
9607
9999
|
if (isFrameView()) {
|
|
9608
10000
|
_nav.setZoomFactor(0.05); // slow zooming way down to allow fine-tuning frame placement // 0.03
|
|
9609
10001
|
_ext.setFrame(getFullBounds()); // TODO: remove redundancy with drawLayers()
|
|
@@ -9643,6 +10035,7 @@
|
|
|
9643
10035
|
}
|
|
9644
10036
|
|
|
9645
10037
|
_ext.on('change', function(e) {
|
|
10038
|
+
if (_basemap) _basemap.refresh(); // keep basemap synced up (if enabled)
|
|
9646
10039
|
if (e.reset) return; // don't need to redraw map here if extent has been reset
|
|
9647
10040
|
if (isFrameView()) {
|
|
9648
10041
|
updateFrameExtent();
|
|
@@ -10013,7 +10406,9 @@
|
|
|
10013
10406
|
startEditing = function() {};
|
|
10014
10407
|
|
|
10015
10408
|
window.addEventListener('beforeunload', function(e) {
|
|
10016
|
-
if (
|
|
10409
|
+
// don't prompt if there are no datasets (this means the last layer was deleted,
|
|
10410
|
+
// hitting the 'cancel' button would leave the interface in a bad state)
|
|
10411
|
+
if (gui.session.unsavedChanges() && !gui.model.isEmpty()) {
|
|
10017
10412
|
e.returnValue = 'There are unsaved changes.';
|
|
10018
10413
|
e.preventDefault();
|
|
10019
10414
|
}
|