Files
iD/js/id/renderer/background.js
2015-06-15 10:14:07 +10:00

278 lines
8.0 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)));
iD.ui.MapInMap.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());
iD.ui.MapInMap.gpxLayer.enable(!iD.ui.MapInMap.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)));
iD.ui.MapInMap.gpxLayer.geojson(toGeoJSON.gpx(toDom(gpxTxt)));
dispatch.change();
}
});
}
};
return d3.rebind(background, dispatch, 'on');
};