mirror of
https://github.com/FoggedLens/iD.git
synced 2026-03-06 19:31:41 +00:00
Refactor background in preparation for multiple overlays
This commit is contained in:
@@ -46,7 +46,8 @@
|
||||
|
||||
<script src='js/id/renderer/background.js'></script>
|
||||
<script src='js/id/renderer/background_source.js'></script>
|
||||
<script src='js/id/renderer/localgpx.js'></script>
|
||||
<script src='js/id/renderer/gpx_layer.js'></script>
|
||||
<script src='js/id/renderer/tile_layer.js'></script>
|
||||
<script src='js/id/renderer/map.js'></script>
|
||||
|
||||
<script src="js/id/svg.js"></script>
|
||||
|
||||
59
js/id/id.js
59
js/id/id.js
@@ -21,7 +21,6 @@ window.iD = function () {
|
||||
mode,
|
||||
container,
|
||||
ui = iD.ui(context),
|
||||
map = iD.Map(context),
|
||||
connection = iD.Connection(),
|
||||
locale = iD.detect().locale,
|
||||
localePath;
|
||||
@@ -62,7 +61,6 @@ window.iD = function () {
|
||||
context.ui = function() { return ui; };
|
||||
context.connection = function() { return connection; };
|
||||
context.history = function() { return history; };
|
||||
context.map = function() { return map; };
|
||||
|
||||
/* History */
|
||||
context.graph = history.graph;
|
||||
@@ -77,7 +75,6 @@ window.iD = function () {
|
||||
context.flush = function() {
|
||||
history.reset();
|
||||
connection.flush();
|
||||
map.redraw();
|
||||
return context;
|
||||
};
|
||||
|
||||
@@ -131,32 +128,26 @@ window.iD = function () {
|
||||
context.surface().call(behavior.off);
|
||||
};
|
||||
|
||||
/* Projection */
|
||||
context.projection = d3.geo.mercator()
|
||||
.scale(512 / Math.PI)
|
||||
.precision(0);
|
||||
|
||||
/* Background */
|
||||
var background = iD.Background(context);
|
||||
context.background = function() { return background; };
|
||||
|
||||
/* Map */
|
||||
var map = iD.Map(context);
|
||||
context.map = function() { return map; };
|
||||
context.layers = function() { return map.layers; };
|
||||
context.background = function() { return map.layers[0]; };
|
||||
context.surface = function() { return map.surface; };
|
||||
context.mouse = map.mouse;
|
||||
context.projection = map.projection;
|
||||
context.extent = map.extent;
|
||||
context.redraw = map.redraw;
|
||||
context.pan = map.pan;
|
||||
context.zoomIn = map.zoomIn;
|
||||
context.zoomOut = map.zoomOut;
|
||||
|
||||
/* Background */
|
||||
var backgroundSources = iD.data.imagery.map(function(source) {
|
||||
if (source.sourcetag === 'Bing') {
|
||||
return iD.BackgroundSource.Bing(source, context.background().dispatch);
|
||||
} else {
|
||||
return iD.BackgroundSource.template(source);
|
||||
}
|
||||
});
|
||||
backgroundSources.push(iD.BackgroundSource.Custom);
|
||||
|
||||
context.backgroundSources = function() {
|
||||
return backgroundSources;
|
||||
};
|
||||
|
||||
/* Presets */
|
||||
var presets = iD.presets()
|
||||
.load(iD.data.presets);
|
||||
@@ -172,34 +163,6 @@ window.iD = function () {
|
||||
return context;
|
||||
};
|
||||
|
||||
var q = iD.util.stringQs(location.hash.substring(1)),
|
||||
detected = false,
|
||||
background = q.background || q.layer;
|
||||
|
||||
if (background && background.indexOf('custom:') === 0) {
|
||||
context.layers()[0]
|
||||
.source(iD.BackgroundSource.template({
|
||||
template: background.replace(/^custom:/, ''),
|
||||
name: 'Custom'
|
||||
}));
|
||||
detected = true;
|
||||
} else if (background) {
|
||||
context.layers()[0]
|
||||
.source(_.find(backgroundSources, function(l) {
|
||||
if (l.data.sourcetag === background) {
|
||||
detected = true;
|
||||
return true;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
if (!detected) {
|
||||
context.background()
|
||||
.source(_.find(backgroundSources, function(l) {
|
||||
return l.data.name === 'Bing aerial imagery';
|
||||
}));
|
||||
}
|
||||
|
||||
var embed = false;
|
||||
context.embed = function(_) {
|
||||
if (!arguments.length) return embed;
|
||||
|
||||
@@ -1,210 +1,125 @@
|
||||
iD.Background = function(backgroundType) {
|
||||
iD.Background = function(context) {
|
||||
var dispatch = d3.dispatch('change'),
|
||||
baseLayer = iD.TileLayer()
|
||||
.projection(context.projection),
|
||||
gpxLayer = iD.GpxLayer(context, dispatch)
|
||||
.projection(context.projection),
|
||||
overlayLayer = iD.TileLayer('overlay')
|
||||
.projection(context.projection),
|
||||
layers = [baseLayer, gpxLayer, overlayLayer];
|
||||
|
||||
backgroundType = backgroundType || 'background';
|
||||
|
||||
var tileSize = 256,
|
||||
tile = d3.geo.tile(),
|
||||
projection,
|
||||
cache = {},
|
||||
offset = [0, 0],
|
||||
offsets = {},
|
||||
tileOrigin,
|
||||
z,
|
||||
transformProp = iD.util.prefixCSSProperty('Transform'),
|
||||
source = d3.functor('');
|
||||
|
||||
function tileSizeAtZoom(d, z) {
|
||||
return Math.ceil(tileSize * Math.pow(2, z - d[2])) / tileSize;
|
||||
}
|
||||
|
||||
function atZoom(t, distance) {
|
||||
var power = Math.pow(2, distance);
|
||||
return [
|
||||
Math.floor(t[0] * power),
|
||||
Math.floor(t[1] * power),
|
||||
t[2] + distance];
|
||||
}
|
||||
|
||||
function lookUp(d) {
|
||||
for (var up = -1; up > -d[2]; up--) {
|
||||
var tile = atZoom(d, up);
|
||||
if (cache[source(tile)] !== false) {
|
||||
return tile;
|
||||
}
|
||||
var backgroundSources = iD.data.imagery.map(function(source) {
|
||||
if (source.sourcetag === 'Bing') {
|
||||
return iD.BackgroundSource.Bing(source, dispatch);
|
||||
} else {
|
||||
return iD.BackgroundSource.template(source);
|
||||
}
|
||||
});
|
||||
|
||||
backgroundSources.push(iD.BackgroundSource.Custom);
|
||||
|
||||
function findSource(sourcetag) {
|
||||
return _.find(backgroundSources, function(d) {
|
||||
return d.data.sourcetag && d.data.sourcetag === sourcetag;
|
||||
});
|
||||
}
|
||||
|
||||
function uniqueBy(a, n) {
|
||||
var o = [], seen = {};
|
||||
for (var i = 0; i < a.length; i++) {
|
||||
if (seen[a[i][n]] === undefined) {
|
||||
o.push(a[i]);
|
||||
seen[a[i][n]] = true;
|
||||
}
|
||||
}
|
||||
return o;
|
||||
function background(supersurface) {
|
||||
layers.forEach(function(layer) {
|
||||
supersurface.call(layer);
|
||||
});
|
||||
}
|
||||
|
||||
function addSource(d) {
|
||||
d.push(source(d));
|
||||
return d;
|
||||
}
|
||||
|
||||
// Update tiles based on current state of `projection`.
|
||||
function background(selection) {
|
||||
var layer = selection.selectAll('.' + backgroundType + '-layer')
|
||||
.data([background]);
|
||||
|
||||
layer.enter().append('div')
|
||||
.attr('class', 'layer-layer ' + backgroundType + '-layer', true);
|
||||
|
||||
tile.scale(projection.scale() * 2 * Math.PI)
|
||||
.translate(projection.translate());
|
||||
|
||||
tileOrigin = [
|
||||
projection.scale() * Math.PI - projection.translate()[0],
|
||||
projection.scale() * Math.PI - projection.translate()[1]];
|
||||
|
||||
z = Math.max(Math.log(projection.scale() * 2 * Math.PI) / Math.log(2) - 8, 0);
|
||||
|
||||
render(layer);
|
||||
}
|
||||
|
||||
// Derive the tiles onscreen, remove those offscreen and position them.
|
||||
// Important that this part not depend on `projection` because it's
|
||||
// rentered when tiles load/error (see #644).
|
||||
function render(selection) {
|
||||
var requests = [];
|
||||
|
||||
if (tile.scaleExtent()[0] <= z) {
|
||||
tile().forEach(function(d) {
|
||||
addSource(d);
|
||||
requests.push(d);
|
||||
if (cache[d[3]] === false && lookUp(d)) {
|
||||
requests.push(addSource(lookUp(d)));
|
||||
}
|
||||
});
|
||||
|
||||
requests = uniqueBy(requests, 3).filter(function(r) {
|
||||
// don't re-request tiles which have failed in the past
|
||||
return cache[r[3]] !== false;
|
||||
});
|
||||
}
|
||||
|
||||
var pixelOffset = [
|
||||
Math.round(offset[0] * Math.pow(2, z)),
|
||||
Math.round(offset[1] * Math.pow(2, z))
|
||||
];
|
||||
|
||||
function load(d) {
|
||||
cache[d[3]] = true;
|
||||
d3.select(this)
|
||||
.on('error', null)
|
||||
.on('load', null)
|
||||
.classed('tile-loaded', true);
|
||||
render(selection);
|
||||
}
|
||||
|
||||
function error(d) {
|
||||
cache[d[3]] = false;
|
||||
d3.select(this)
|
||||
.on('error', null)
|
||||
.on('load', null)
|
||||
.remove();
|
||||
render(selection);
|
||||
}
|
||||
|
||||
function imageTransform(d) {
|
||||
var _ts = tileSize * Math.pow(2, z - d[2]);
|
||||
var scale = tileSizeAtZoom(d, z);
|
||||
return 'translate(' +
|
||||
(Math.round((d[0] * _ts) - tileOrigin[0]) + pixelOffset[0]) + 'px,' +
|
||||
(Math.round((d[1] * _ts) - tileOrigin[1]) + pixelOffset[1]) + 'px)' +
|
||||
'scale(' + scale + ',' + scale + ')';
|
||||
}
|
||||
|
||||
var image = selection
|
||||
.selectAll('img')
|
||||
.data(requests, function(d) { return d[3]; });
|
||||
|
||||
image.exit()
|
||||
.style(transformProp, imageTransform)
|
||||
.classed('tile-removing', true)
|
||||
.each(function() {
|
||||
var tile = d3.select(this);
|
||||
window.setTimeout(function() {
|
||||
if (tile.classed('tile-removing')) {
|
||||
tile.remove();
|
||||
}
|
||||
}, 300);
|
||||
});
|
||||
|
||||
image.enter().append('img')
|
||||
.attr('class', 'tile')
|
||||
.attr('src', function(d) { return d[3]; })
|
||||
.on('error', error)
|
||||
.on('load', load);
|
||||
|
||||
image
|
||||
.style(transformProp, imageTransform)
|
||||
.classed('tile-removing', false);
|
||||
}
|
||||
|
||||
background.offset = function(_) {
|
||||
if (!arguments.length) return offset;
|
||||
offset = _;
|
||||
if (source.data) offsets[source.data.name] = offset;
|
||||
return background;
|
||||
};
|
||||
|
||||
background.nudge = function(_, zoomlevel) {
|
||||
offset[0] += _[0] / Math.pow(2, zoomlevel);
|
||||
offset[1] += _[1] / Math.pow(2, zoomlevel);
|
||||
return background;
|
||||
};
|
||||
|
||||
background.projection = function(_) {
|
||||
if (!arguments.length) return projection;
|
||||
projection = _;
|
||||
return background;
|
||||
background.sources = function(extent) {
|
||||
return backgroundSources.filter(function(layer) {
|
||||
return !layer.data.extent ||
|
||||
iD.geo.Extent(layer.data.extent).intersects(extent);
|
||||
});
|
||||
};
|
||||
|
||||
background.dimensions = function(_) {
|
||||
if (!arguments.length) return tile.size();
|
||||
tile.size(_);
|
||||
layers.forEach(function(layer) {
|
||||
layer.dimensions(_);
|
||||
});
|
||||
};
|
||||
|
||||
background.baseLayerSource = function(d) {
|
||||
if (!arguments.length) return baseLayer.source();
|
||||
baseLayer.source(d);
|
||||
dispatch.change();
|
||||
if (d.data.name === 'Custom (customized)') {
|
||||
context.history()
|
||||
.imagery_used('Custom (' + d.data.template + ')');
|
||||
} else {
|
||||
context.history()
|
||||
.imagery_used(d.data.sourcetag || d.data.name);
|
||||
}
|
||||
return background;
|
||||
};
|
||||
|
||||
function setHash(source) {
|
||||
var tag = source.data && source.data.sourcetag;
|
||||
if (!tag && source.data && source.data.name === 'Custom') {
|
||||
tag = 'custom:' + source.data.template;
|
||||
background.bing = function() {
|
||||
background.baseLayerSource(findSource("Bing"));
|
||||
};
|
||||
|
||||
background.hasGpxLayer = function() {
|
||||
return !_.isEmpty(gpxLayer.geojson());
|
||||
};
|
||||
|
||||
background.showsGpxLayer = function() {
|
||||
return background.hasGpxLayer() && gpxLayer.enable();
|
||||
};
|
||||
|
||||
background.zoomToGpxLayer = function() {
|
||||
if (background.hasGpxLayer()) {
|
||||
context.map()
|
||||
.extent(d3.geo.bounds(gpxLayer.geojson()));
|
||||
}
|
||||
var q = iD.util.stringQs(location.hash.substring(1));
|
||||
if (tag) {
|
||||
q[backgroundType] = tag;
|
||||
location.replace('#' + iD.util.qsString(q, true));
|
||||
};
|
||||
|
||||
background.toggleGpxLayer = function() {
|
||||
gpxLayer.enable(!gpxLayer.enable());
|
||||
dispatch.change();
|
||||
};
|
||||
|
||||
background.showsLayer = function(d) {
|
||||
var overlay = overlayLayer.source();
|
||||
return d.data.name === baseLayer.source().data.name ||
|
||||
(overlay.data && overlay.data.name === d.data.name);
|
||||
};
|
||||
|
||||
background.toggleOverlayLayer = function(d) {
|
||||
if (overlayLayer.source() === d) {
|
||||
overlayLayer.source(d3.functor(''));
|
||||
} else {
|
||||
location.replace('#' + iD.util.qsString(_.omit(q, backgroundType), true));
|
||||
overlayLayer.source(d);
|
||||
}
|
||||
dispatch.change();
|
||||
};
|
||||
|
||||
background.nudge = function(d, zoom) {
|
||||
baseLayer.nudge(d, zoom);
|
||||
dispatch.change();
|
||||
return background;
|
||||
};
|
||||
|
||||
background.offset = function(d) {
|
||||
if (!arguments.length) return baseLayer.offset();
|
||||
baseLayer.offset(d);
|
||||
dispatch.change();
|
||||
return background;
|
||||
};
|
||||
|
||||
var q = iD.util.stringQs(location.hash.substring(1)),
|
||||
chosen = q.background || q.layer;
|
||||
|
||||
if (chosen && chosen.indexOf('custom:') === 0) {
|
||||
background.baseLayerSource(iD.BackgroundSource.template({
|
||||
template: chosen.replace(/^custom:/, ''),
|
||||
name: 'Custom'
|
||||
}));
|
||||
} else {
|
||||
background.baseLayerSource(findSource(chosen) || findSource("Bing"));
|
||||
}
|
||||
|
||||
background.dispatch = d3.dispatch('change');
|
||||
|
||||
background.source = function(_) {
|
||||
if (!arguments.length) return source;
|
||||
source = _;
|
||||
if (source.data) {
|
||||
offset = offsets[source.data.name] = offsets[source.data.name] || [0, 0];
|
||||
} else {
|
||||
offset = [0, 0];
|
||||
}
|
||||
cache = {};
|
||||
tile.scaleExtent((source.data && source.data.scaleExtent) || [1, 20]);
|
||||
setHash(source);
|
||||
background.dispatch.change();
|
||||
return background;
|
||||
};
|
||||
|
||||
return d3.rebind(background, background.dispatch, 'on');
|
||||
return d3.rebind(background, dispatch, 'on');
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
iD.LocalGpx = function(context) {
|
||||
iD.GpxLayer = function(context, dispatch) {
|
||||
var projection,
|
||||
gj = {},
|
||||
enable = true,
|
||||
@@ -74,7 +74,7 @@ iD.LocalGpx = function(context) {
|
||||
|
||||
reader.onload = function(e) {
|
||||
render.geojson(toGeoJSON.gpx(toDom(e.target.result)));
|
||||
context.redraw();
|
||||
dispatch.change();
|
||||
context.map().pan([0, 0]);
|
||||
};
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
iD.Map = function(context) {
|
||||
var dimensions = [1, 1],
|
||||
dispatch = d3.dispatch('move', 'drawn'),
|
||||
projection = d3.geo.mercator()
|
||||
.scale(512 / Math.PI)
|
||||
.precision(0),
|
||||
projection = context.projection,
|
||||
roundedProjection = iD.svg.RoundProjection(projection),
|
||||
zoom = d3.behavior.zoom()
|
||||
.translate(projection.translate())
|
||||
@@ -14,11 +12,6 @@ iD.Map = function(context) {
|
||||
transformStart,
|
||||
transformed = false,
|
||||
minzoom = 0,
|
||||
layers = [
|
||||
iD.Background().projection(projection),
|
||||
iD.LocalGpx(context).projection(projection),
|
||||
iD.Background('overlay').projection(projection)
|
||||
],
|
||||
transformProp = iD.util.prefixCSSProperty('Transform'),
|
||||
points = iD.svg.Points(roundedProjection, context),
|
||||
vertices = iD.svg.Vertices(roundedProjection, context),
|
||||
@@ -32,15 +25,15 @@ iD.Map = function(context) {
|
||||
function map(selection) {
|
||||
context.history()
|
||||
.on('change.map', redraw);
|
||||
context.background()
|
||||
.on('change.map', redraw);
|
||||
|
||||
selection.call(zoom);
|
||||
|
||||
supersurface = selection.append('div')
|
||||
.attr('id', 'supersurface');
|
||||
|
||||
layers.forEach(function(layer) {
|
||||
supersurface.call(layer);
|
||||
});
|
||||
supersurface.call(context.background());
|
||||
|
||||
// Need a wrapper div because Opera can't cope with an absolutely positioned
|
||||
// SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16
|
||||
@@ -221,9 +214,7 @@ iD.Map = function(context) {
|
||||
}
|
||||
|
||||
if (!difference) {
|
||||
layers.forEach(function(layer) {
|
||||
supersurface.call(layer);
|
||||
});
|
||||
supersurface.call(context.background());
|
||||
}
|
||||
|
||||
if (map.editable()) {
|
||||
@@ -320,9 +311,7 @@ iD.Map = function(context) {
|
||||
var center = map.center();
|
||||
dimensions = _;
|
||||
surface.dimensions(dimensions);
|
||||
layers.forEach(function(layer) {
|
||||
layer.dimensions(dimensions);
|
||||
});
|
||||
context.background().dimensions(dimensions);
|
||||
projection.clipExtent([[0, 0], dimensions]);
|
||||
setCenter(center);
|
||||
return redraw();
|
||||
@@ -425,9 +414,5 @@ iD.Map = function(context) {
|
||||
return map;
|
||||
};
|
||||
|
||||
map.layers = layers;
|
||||
map.projection = projection;
|
||||
map.redraw = redraw;
|
||||
|
||||
return d3.rebind(map, dispatch, 'on');
|
||||
};
|
||||
|
||||
207
js/id/renderer/tile_layer.js
Normal file
207
js/id/renderer/tile_layer.js
Normal file
@@ -0,0 +1,207 @@
|
||||
iD.TileLayer = function(backgroundType) {
|
||||
|
||||
backgroundType = backgroundType || 'background';
|
||||
|
||||
var tileSize = 256,
|
||||
tile = d3.geo.tile(),
|
||||
projection,
|
||||
cache = {},
|
||||
offset = [0, 0],
|
||||
offsets = {},
|
||||
tileOrigin,
|
||||
z,
|
||||
transformProp = iD.util.prefixCSSProperty('Transform'),
|
||||
source = d3.functor('');
|
||||
|
||||
function tileSizeAtZoom(d, z) {
|
||||
return Math.ceil(tileSize * Math.pow(2, z - d[2])) / tileSize;
|
||||
}
|
||||
|
||||
function atZoom(t, distance) {
|
||||
var power = Math.pow(2, distance);
|
||||
return [
|
||||
Math.floor(t[0] * power),
|
||||
Math.floor(t[1] * power),
|
||||
t[2] + distance];
|
||||
}
|
||||
|
||||
function lookUp(d) {
|
||||
for (var up = -1; up > -d[2]; up--) {
|
||||
var tile = atZoom(d, up);
|
||||
if (cache[source(tile)] !== false) {
|
||||
return tile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function uniqueBy(a, n) {
|
||||
var o = [], seen = {};
|
||||
for (var i = 0; i < a.length; i++) {
|
||||
if (seen[a[i][n]] === undefined) {
|
||||
o.push(a[i]);
|
||||
seen[a[i][n]] = true;
|
||||
}
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
function addSource(d) {
|
||||
d.push(source(d));
|
||||
return d;
|
||||
}
|
||||
|
||||
// Update tiles based on current state of `projection`.
|
||||
function background(selection) {
|
||||
var layer = selection.selectAll('.' + backgroundType + '-layer')
|
||||
.data([background]);
|
||||
|
||||
layer.enter().append('div')
|
||||
.attr('class', 'layer-layer ' + backgroundType + '-layer', true);
|
||||
|
||||
tile.scale(projection.scale() * 2 * Math.PI)
|
||||
.translate(projection.translate());
|
||||
|
||||
tileOrigin = [
|
||||
projection.scale() * Math.PI - projection.translate()[0],
|
||||
projection.scale() * Math.PI - projection.translate()[1]];
|
||||
|
||||
z = Math.max(Math.log(projection.scale() * 2 * Math.PI) / Math.log(2) - 8, 0);
|
||||
|
||||
render(layer);
|
||||
}
|
||||
|
||||
// Derive the tiles onscreen, remove those offscreen and position them.
|
||||
// Important that this part not depend on `projection` because it's
|
||||
// rentered when tiles load/error (see #644).
|
||||
function render(selection) {
|
||||
var requests = [];
|
||||
|
||||
if (tile.scaleExtent()[0] <= z) {
|
||||
tile().forEach(function(d) {
|
||||
addSource(d);
|
||||
requests.push(d);
|
||||
if (cache[d[3]] === false && lookUp(d)) {
|
||||
requests.push(addSource(lookUp(d)));
|
||||
}
|
||||
});
|
||||
|
||||
requests = uniqueBy(requests, 3).filter(function(r) {
|
||||
// don't re-request tiles which have failed in the past
|
||||
return cache[r[3]] !== false;
|
||||
});
|
||||
}
|
||||
|
||||
var pixelOffset = [
|
||||
Math.round(offset[0] * Math.pow(2, z)),
|
||||
Math.round(offset[1] * Math.pow(2, z))
|
||||
];
|
||||
|
||||
function load(d) {
|
||||
cache[d[3]] = true;
|
||||
d3.select(this)
|
||||
.on('error', null)
|
||||
.on('load', null)
|
||||
.classed('tile-loaded', true);
|
||||
render(selection);
|
||||
}
|
||||
|
||||
function error(d) {
|
||||
cache[d[3]] = false;
|
||||
d3.select(this)
|
||||
.on('error', null)
|
||||
.on('load', null)
|
||||
.remove();
|
||||
render(selection);
|
||||
}
|
||||
|
||||
function imageTransform(d) {
|
||||
var _ts = tileSize * Math.pow(2, z - d[2]);
|
||||
var scale = tileSizeAtZoom(d, z);
|
||||
return 'translate(' +
|
||||
(Math.round((d[0] * _ts) - tileOrigin[0]) + pixelOffset[0]) + 'px,' +
|
||||
(Math.round((d[1] * _ts) - tileOrigin[1]) + pixelOffset[1]) + 'px)' +
|
||||
'scale(' + scale + ',' + scale + ')';
|
||||
}
|
||||
|
||||
var image = selection
|
||||
.selectAll('img')
|
||||
.data(requests, function(d) { return d[3]; });
|
||||
|
||||
image.exit()
|
||||
.style(transformProp, imageTransform)
|
||||
.classed('tile-removing', true)
|
||||
.each(function() {
|
||||
var tile = d3.select(this);
|
||||
window.setTimeout(function() {
|
||||
if (tile.classed('tile-removing')) {
|
||||
tile.remove();
|
||||
}
|
||||
}, 300);
|
||||
});
|
||||
|
||||
image.enter().append('img')
|
||||
.attr('class', 'tile')
|
||||
.attr('src', function(d) { return d[3]; })
|
||||
.on('error', error)
|
||||
.on('load', load);
|
||||
|
||||
image
|
||||
.style(transformProp, imageTransform)
|
||||
.classed('tile-removing', false);
|
||||
}
|
||||
|
||||
background.offset = function(_) {
|
||||
if (!arguments.length) return offset;
|
||||
offset = _;
|
||||
if (source.data) offsets[source.data.name] = offset;
|
||||
return background;
|
||||
};
|
||||
|
||||
background.nudge = function(_, zoomlevel) {
|
||||
offset[0] += _[0] / Math.pow(2, zoomlevel);
|
||||
offset[1] += _[1] / Math.pow(2, zoomlevel);
|
||||
return background;
|
||||
};
|
||||
|
||||
background.projection = function(_) {
|
||||
if (!arguments.length) return projection;
|
||||
projection = _;
|
||||
return background;
|
||||
};
|
||||
|
||||
background.dimensions = function(_) {
|
||||
if (!arguments.length) return tile.size();
|
||||
tile.size(_);
|
||||
return background;
|
||||
};
|
||||
|
||||
function setHash(source) {
|
||||
var tag = source.data && source.data.sourcetag;
|
||||
if (!tag && source.data && source.data.name === 'Custom') {
|
||||
tag = 'custom:' + source.data.template;
|
||||
}
|
||||
var q = iD.util.stringQs(location.hash.substring(1));
|
||||
if (tag) {
|
||||
q[backgroundType] = tag;
|
||||
location.replace('#' + iD.util.qsString(q, true));
|
||||
} else {
|
||||
location.replace('#' + iD.util.qsString(_.omit(q, backgroundType), true));
|
||||
}
|
||||
}
|
||||
|
||||
background.source = function(_) {
|
||||
if (!arguments.length) return source;
|
||||
source = _;
|
||||
if (source.data) {
|
||||
offset = offsets[source.data.name] = offsets[source.data.name] || [0, 0];
|
||||
} else {
|
||||
offset = [0, 0];
|
||||
}
|
||||
cache = {};
|
||||
tile.scaleExtent((source.data && source.data.scaleExtent) || [1, 20]);
|
||||
setHash(source);
|
||||
return background;
|
||||
};
|
||||
|
||||
return background;
|
||||
};
|
||||
@@ -2,13 +2,13 @@ iD.ui.Attribution = function(context) {
|
||||
var selection;
|
||||
|
||||
function update() {
|
||||
if (!context.background().source()) {
|
||||
if (!context.background().baseLayerSource()) {
|
||||
selection.html('');
|
||||
return;
|
||||
}
|
||||
|
||||
var attribution = selection.selectAll('.provided-by')
|
||||
.data([context.background().source()], function(d) { return d.data.name; });
|
||||
.data([context.background().baseLayerSource()], function(d) { return d.data.name; });
|
||||
|
||||
attribution.enter()
|
||||
.append('span')
|
||||
|
||||
@@ -6,18 +6,9 @@ iD.ui.Background = function(context) {
|
||||
['top', [0, -1]],
|
||||
['right', [-1, 0]],
|
||||
['bottom', [0, 1]]],
|
||||
layers = context.backgroundSources(),
|
||||
opacityDefault = (context.storage('background-opacity') !== undefined) ?
|
||||
(+context.storage('background-opacity')) : 0.5;
|
||||
|
||||
function getSources() {
|
||||
var ext = context.map().extent();
|
||||
return layers.filter(function(layer) {
|
||||
return !layer.data.extent ||
|
||||
iD.geo.Extent(layer.data.extent).intersects(ext);
|
||||
});
|
||||
}
|
||||
|
||||
function background(selection) {
|
||||
|
||||
function setOpacity(d) {
|
||||
@@ -34,9 +25,7 @@ iD.ui.Background = function(context) {
|
||||
|
||||
function selectLayer() {
|
||||
function active(d) {
|
||||
var overlay = context.map().layers[2].source();
|
||||
return d.data.name === context.background().source().data.name ||
|
||||
(overlay.data && overlay.data.name === d.data.name);
|
||||
return context.background().showsLayer(d);
|
||||
}
|
||||
|
||||
content.selectAll('label.layer')
|
||||
@@ -55,43 +44,28 @@ iD.ui.Background = function(context) {
|
||||
}
|
||||
d = configured;
|
||||
}
|
||||
context.background().source(d);
|
||||
if (d.data.name === 'Custom (customized)') {
|
||||
context.history()
|
||||
.imagery_used('Custom (' + d.data.template + ')');
|
||||
} else {
|
||||
context.history()
|
||||
.imagery_used(d.data.sourcetag || d.data.name);
|
||||
}
|
||||
context.redraw();
|
||||
context.background().baseLayerSource(d);
|
||||
selectLayer();
|
||||
}
|
||||
|
||||
function clickSetOverlay(d) {
|
||||
d3.event.preventDefault();
|
||||
var overlay = context.map().layers[2];
|
||||
if (overlay.source() === d) {
|
||||
overlay.source(d3.functor(''));
|
||||
} else {
|
||||
overlay.source(d);
|
||||
}
|
||||
context.redraw();
|
||||
context.background().toggleOverlayLayer(d);
|
||||
selectLayer();
|
||||
}
|
||||
|
||||
function clickGpx() {
|
||||
context.map().layers[1]
|
||||
.enable(!context.map().layers[1].enable());
|
||||
context.redraw();
|
||||
context.background().toggleGpxLayer();
|
||||
update();
|
||||
}
|
||||
|
||||
function drawList(layerList, type, change, filter) {
|
||||
var sources = context.background()
|
||||
.sources(context.map().extent())
|
||||
.filter(filter);
|
||||
|
||||
var layerLinks = layerList.selectAll('label.layer')
|
||||
.data(getSources().filter(filter), function(d) {
|
||||
return d.data.name;
|
||||
});
|
||||
.data(sources, function(d) { return d.data.name; });
|
||||
|
||||
var layerInner = layerLinks.enter()
|
||||
.append('label')
|
||||
@@ -130,12 +104,11 @@ iD.ui.Background = function(context) {
|
||||
return d.data.overlay;
|
||||
});
|
||||
|
||||
var gpxLayer = context.map().layers[1],
|
||||
hasGpx = !_.isEmpty(gpxLayer.geojson()),
|
||||
showsGpx = hasGpx && gpxLayer.enable();
|
||||
var hasGpx = context.background().hasGpxLayer(),
|
||||
showsGpx = context.background().showsGpxLayer();
|
||||
|
||||
gpxLayerItem
|
||||
.classed('active', hasGpx && gpxLayer.enable())
|
||||
.classed('active', showsGpx)
|
||||
.selectAll('input')
|
||||
.property('disabled', !hasGpx)
|
||||
.property('checked', showsGpx);
|
||||
@@ -157,10 +130,10 @@ iD.ui.Background = function(context) {
|
||||
});
|
||||
|
||||
function nudge() {
|
||||
context.background().nudge(d[1], context.map().zoom());
|
||||
var offset = context.background().offset();
|
||||
var offset = context.background()
|
||||
.nudge(d[1], context.map().zoom())
|
||||
.offset();
|
||||
resetButton.classed('disabled', offset[0] === 0 && offset[1] === 0);
|
||||
context.redraw();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,13 +247,7 @@ iD.ui.Background = function(context) {
|
||||
.on('click', function() {
|
||||
d3.event.preventDefault();
|
||||
d3.event.stopPropagation();
|
||||
if (context.map().layers[1].geojson().type) {
|
||||
context.map()
|
||||
.extent(d3.geo.bounds(context
|
||||
.map()
|
||||
.layers[1]
|
||||
.geojson()));
|
||||
}
|
||||
context.background().zoomToGpxLayer();
|
||||
})
|
||||
.append('span')
|
||||
.attr('class', 'icon geocode' );
|
||||
@@ -317,7 +284,6 @@ iD.ui.Background = function(context) {
|
||||
.on('click', function () {
|
||||
context.background().offset([0, 0]);
|
||||
resetButton.classed('disabled', true);
|
||||
context.redraw();
|
||||
});
|
||||
|
||||
resetButton.append('div')
|
||||
|
||||
@@ -9,7 +9,7 @@ iD.ui.intro = function(context) {
|
||||
// Save current map state
|
||||
var history = context.history().toJSON(),
|
||||
hash = window.location.hash,
|
||||
background = context.background().source(),
|
||||
background = context.background().baseLayerSource(),
|
||||
opacity = d3.select('.background-layer').style('opacity'),
|
||||
loadedTiles = context.connection().loadedTiles(),
|
||||
baseEntities = context.history().graph().base().entities;
|
||||
@@ -18,10 +18,7 @@ iD.ui.intro = function(context) {
|
||||
context.connection().toggle(false).flush();
|
||||
context.history().save().reset();
|
||||
context.history().merge(iD.Graph().load(JSON.parse(iD.introGraph)).entities);
|
||||
|
||||
context.background().source(_.find(context.backgroundSources(), function(d) {
|
||||
return d.data.sourcetag === "Bing";
|
||||
}));
|
||||
context.background().bing();
|
||||
|
||||
// Block saving
|
||||
var savebutton = d3.select('#bar button.save'),
|
||||
@@ -59,7 +56,7 @@ iD.ui.intro = function(context) {
|
||||
d3.select('.background-layer').style('opacity', opacity);
|
||||
context.connection().toggle(true).flush().loadedTiles(loadedTiles);
|
||||
context.history().reset().merge(baseEntities);
|
||||
context.background().source(background);
|
||||
context.background().baseLayerSource(background);
|
||||
if (history) context.history().fromJSON(history);
|
||||
window.location.replace(hash);
|
||||
window.onbeforeunload = beforeunload;
|
||||
|
||||
@@ -48,7 +48,8 @@
|
||||
<script src='../js/id/renderer/background.js'></script>
|
||||
<script src='../js/id/renderer/background_source.js'></script>
|
||||
<script src='../js/id/renderer/map.js'></script>
|
||||
<script src='../js/id/renderer/localgpx.js'></script>
|
||||
<script src='../js/id/renderer/gpx_layer.js'></script>
|
||||
<script src='../js/id/renderer/tile_layer.js'></script>
|
||||
|
||||
<script src="../js/id/svg.js"></script>
|
||||
<script src="../js/id/svg/areas.js"></script>
|
||||
@@ -230,7 +231,7 @@
|
||||
<script src="spec/core/difference.js"></script>
|
||||
<script src="spec/core/tree.js"></script>
|
||||
|
||||
<script src="spec/renderer/background.js"></script>
|
||||
<script src="spec/renderer/tile_layer.js"></script>
|
||||
<script src="spec/renderer/background_source.js"></script>
|
||||
<script src="spec/renderer/map.js"></script>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
<script src="spec/core/difference.js"></script>
|
||||
<script src="spec/core/tree.js"></script>
|
||||
|
||||
<script src="spec/renderer/background.js"></script>
|
||||
<script src="spec/renderer/tile_layer.js"></script>
|
||||
<script src="spec/renderer/background_source.js"></script>
|
||||
<script src="spec/renderer/map.js"></script>
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
describe('iD.Background', function() {
|
||||
describe('iD.TileLayer', function() {
|
||||
var c, d;
|
||||
|
||||
beforeEach(function() {
|
||||
d = d3.select(document.createElement('div'));
|
||||
c = iD.Background().projection(d3.geo.mercator());
|
||||
c = iD.TileLayer().projection(d3.geo.mercator());
|
||||
d.call(c);
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@ describe('iD.Background', function() {
|
||||
d.remove();
|
||||
});
|
||||
|
||||
describe('iD.Background', function() {
|
||||
describe('iD.TileLayer', function() {
|
||||
it('is instantiated', function() {
|
||||
expect(c).to.be.ok;
|
||||
});
|
||||
Reference in New Issue
Block a user