Files
iD/js/id/renderer/background.js
Denys Digtiar be1e3738f1 Error handling is added to the GPX loading callback.
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.
2015-06-14 16:46:20 -07:00

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');
};