mirror of
https://github.com/FoggedLens/iD.git
synced 2026-03-21 10:33:34 +00:00
Previously erroneous response was corrupting the GpxLayer, which could potential corrupt the whole Map, if user were to use 'Zoom to GPX' feature. Callback logic is wrapped in the error guard to fix this.
275 lines
7.8 KiB
JavaScript
275 lines
7.8 KiB
JavaScript
iD.Background = function(context) {
|
|
var dispatch = d3.dispatch('change'),
|
|
baseLayer = iD.TileLayer()
|
|
.projection(context.projection),
|
|
gpxLayer = iD.GpxLayer(context, dispatch)
|
|
.projection(context.projection),
|
|
mapillaryLayer = iD.MapillaryLayer(context),
|
|
overlayLayers = [];
|
|
|
|
var backgroundSources;
|
|
|
|
function findSource(id) {
|
|
return _.find(backgroundSources, function(d) {
|
|
return d.id && d.id === id;
|
|
});
|
|
}
|
|
|
|
function updateImagery() {
|
|
var b = background.baseLayerSource(),
|
|
o = overlayLayers.map(function (d) { return d.source().id; }).join(','),
|
|
q = iD.util.stringQs(location.hash.substring(1));
|
|
|
|
var id = b.id;
|
|
if (id === 'custom') {
|
|
id = 'custom:' + b.template;
|
|
}
|
|
|
|
if (id) {
|
|
q.background = id;
|
|
} else {
|
|
delete q.background;
|
|
}
|
|
|
|
if (o) {
|
|
q.overlays = o;
|
|
} else {
|
|
delete q.overlays;
|
|
}
|
|
|
|
location.replace('#' + iD.util.qsString(q, true));
|
|
|
|
var imageryUsed = [b.imageryUsed()];
|
|
|
|
overlayLayers.forEach(function (d) {
|
|
var source = d.source();
|
|
if (!source.isLocatorOverlay()) {
|
|
imageryUsed.push(source.imageryUsed());
|
|
}
|
|
});
|
|
|
|
if (background.showsGpxLayer()) {
|
|
imageryUsed.push('Local GPX');
|
|
}
|
|
|
|
context.history().imageryUsed(imageryUsed);
|
|
}
|
|
|
|
function background(selection) {
|
|
var base = selection.selectAll('.background-layer')
|
|
.data([0]);
|
|
|
|
base.enter().insert('div', '.layer-data')
|
|
.attr('class', 'layer-layer background-layer');
|
|
|
|
base.call(baseLayer);
|
|
|
|
var overlays = selection.selectAll('.layer-overlay')
|
|
.data(overlayLayers, function(d) { return d.source().name(); });
|
|
|
|
overlays.enter().insert('div', '.layer-data')
|
|
.attr('class', 'layer-layer layer-overlay');
|
|
|
|
overlays.each(function(layer) {
|
|
d3.select(this).call(layer);
|
|
});
|
|
|
|
overlays.exit()
|
|
.remove();
|
|
|
|
var gpx = selection.selectAll('.layer-gpx')
|
|
.data([0]);
|
|
|
|
gpx.enter().insert('div')
|
|
.attr('class', 'layer-layer layer-gpx');
|
|
|
|
gpx.call(gpxLayer);
|
|
|
|
var mapillary = selection.selectAll('.layer-mapillary')
|
|
.data([0]);
|
|
|
|
mapillary.enter().insert('div')
|
|
.attr('class', 'layer-layer layer-mapillary');
|
|
|
|
mapillary.call(mapillaryLayer);
|
|
}
|
|
|
|
background.sources = function(extent) {
|
|
return backgroundSources.filter(function(source) {
|
|
return source.intersects(extent);
|
|
});
|
|
};
|
|
|
|
background.dimensions = function(_) {
|
|
baseLayer.dimensions(_);
|
|
gpxLayer.dimensions(_);
|
|
mapillaryLayer.dimensions(_);
|
|
|
|
overlayLayers.forEach(function(layer) {
|
|
layer.dimensions(_);
|
|
});
|
|
};
|
|
|
|
background.baseLayerSource = function(d) {
|
|
if (!arguments.length) return baseLayer.source();
|
|
|
|
baseLayer.source(d);
|
|
dispatch.change();
|
|
updateImagery();
|
|
|
|
return background;
|
|
};
|
|
|
|
background.bing = function() {
|
|
background.baseLayerSource(findSource('Bing'));
|
|
};
|
|
|
|
background.hasGpxLayer = function() {
|
|
return !_.isEmpty(gpxLayer.geojson());
|
|
};
|
|
|
|
background.showsGpxLayer = function() {
|
|
return background.hasGpxLayer() && gpxLayer.enable();
|
|
};
|
|
|
|
function toDom(x) {
|
|
return (new DOMParser()).parseFromString(x, 'text/xml');
|
|
}
|
|
|
|
background.gpxLayerFiles = function(fileList) {
|
|
var f = fileList[0],
|
|
reader = new FileReader();
|
|
|
|
reader.onload = function(e) {
|
|
gpxLayer.geojson(toGeoJSON.gpx(toDom(e.target.result)));
|
|
background.zoomToGpxLayer();
|
|
dispatch.change();
|
|
};
|
|
|
|
reader.readAsText(f);
|
|
};
|
|
|
|
background.zoomToGpxLayer = function() {
|
|
if (background.hasGpxLayer()) {
|
|
var map = context.map(),
|
|
viewport = map.trimmedExtent().polygon(),
|
|
coords = _.reduce(gpxLayer.geojson().features, function(coords, feature) {
|
|
var c = feature.geometry.coordinates;
|
|
return _.union(coords, feature.geometry.type === 'Point' ? [c] : c);
|
|
}, []);
|
|
|
|
if (!iD.geo.polygonIntersectsPolygon(viewport, coords)) {
|
|
var extent = iD.geo.Extent(d3.geo.bounds(gpxLayer.geojson()));
|
|
map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
|
|
}
|
|
}
|
|
};
|
|
|
|
background.toggleGpxLayer = function() {
|
|
gpxLayer.enable(!gpxLayer.enable());
|
|
dispatch.change();
|
|
};
|
|
|
|
background.showsMapillaryLayer = function() {
|
|
return mapillaryLayer.enable();
|
|
};
|
|
|
|
background.toggleMapillaryLayer = function() {
|
|
mapillaryLayer.enable(!mapillaryLayer.enable());
|
|
dispatch.change();
|
|
};
|
|
|
|
background.showsLayer = function(d) {
|
|
return d === baseLayer.source() ||
|
|
(d.id === 'custom' && baseLayer.source().id === 'custom') ||
|
|
overlayLayers.some(function(l) { return l.source() === d; });
|
|
};
|
|
|
|
background.overlayLayerSources = function() {
|
|
return overlayLayers.map(function (l) { return l.source(); });
|
|
};
|
|
|
|
background.toggleOverlayLayer = function(d) {
|
|
var layer;
|
|
|
|
for (var i = 0; i < overlayLayers.length; i++) {
|
|
layer = overlayLayers[i];
|
|
if (layer.source() === d) {
|
|
overlayLayers.splice(i, 1);
|
|
dispatch.change();
|
|
updateImagery();
|
|
return;
|
|
}
|
|
}
|
|
|
|
layer = iD.TileLayer()
|
|
.source(d)
|
|
.projection(context.projection)
|
|
.dimensions(baseLayer.dimensions());
|
|
|
|
overlayLayers.push(layer);
|
|
dispatch.change();
|
|
updateImagery();
|
|
};
|
|
|
|
background.nudge = function(d, zoom) {
|
|
baseLayer.source().nudge(d, zoom);
|
|
dispatch.change();
|
|
return background;
|
|
};
|
|
|
|
background.offset = function(d) {
|
|
if (!arguments.length) return baseLayer.source().offset();
|
|
baseLayer.source().offset(d);
|
|
dispatch.change();
|
|
return background;
|
|
};
|
|
|
|
background.load = function(imagery) {
|
|
backgroundSources = imagery.map(function(source) {
|
|
if (source.type === 'bing') {
|
|
return iD.BackgroundSource.Bing(source, dispatch);
|
|
} else {
|
|
return iD.BackgroundSource(source);
|
|
}
|
|
});
|
|
|
|
backgroundSources.unshift(iD.BackgroundSource.None());
|
|
|
|
var q = iD.util.stringQs(location.hash.substring(1)),
|
|
chosen = q.background || q.layer;
|
|
|
|
if (chosen && chosen.indexOf('custom:') === 0) {
|
|
background.baseLayerSource(iD.BackgroundSource.Custom(chosen.replace(/^custom:/, '')));
|
|
} else {
|
|
background.baseLayerSource(findSource(chosen) || findSource('Bing') || backgroundSources[1]);
|
|
}
|
|
|
|
var locator = _.find(backgroundSources, function(d) {
|
|
return d.overlay && d.default;
|
|
});
|
|
|
|
if (locator) {
|
|
background.toggleOverlayLayer(locator);
|
|
}
|
|
|
|
var overlays = (q.overlays || '').split(',');
|
|
overlays.forEach(function(overlay) {
|
|
overlay = findSource(overlay);
|
|
if (overlay) background.toggleOverlayLayer(overlay);
|
|
});
|
|
|
|
var gpx = q.gpx;
|
|
if (gpx) {
|
|
d3.text(gpx, function(err, gpxTxt) {
|
|
if (!err) {
|
|
gpxLayer.geojson(toGeoJSON.gpx(toDom(gpxTxt)));
|
|
dispatch.change();
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
return d3.rebind(background, dispatch, 'on');
|
|
};
|