Files
iD/js/id/renderer/background.js
T
pnorman 3fadf873a3 Change default max zoom to 20
A lot of imagery goes to z20 now. Where the max zoom is not known it should be set high to allow the imagery to be used at its maximum resolution

Where the source imagery does not support up to z20 it will fall back to higher zoom levels. It is unlikely that editors will be zoomed in far past the max zoom of the imagery.

This does not replace the lack of max zoom support for preset imagery layers (issue #784)
2013-03-07 13:59:42 -08:00

182 lines
5.2 KiB
JavaScript

iD.Background = function() {
var tileSize = 256,
tile = d3.geo.tile(),
projection,
cache = {},
offset = [0, 0],
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--) {
if (cache[atZoom(d, up)] !== false) return atZoom(d, up);
}
}
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) {
tile.scale(projection.scale())
.translate(projection.translate());
tileOrigin = [
projection.scale() / 2 - projection.translate()[0],
projection.scale() / 2 - projection.translate()[1]];
z = Math.max(Math.log(projection.scale()) / Math.log(2) - 8, 0);
render(selection);
}
// 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 = [];
tile().forEach(function(d) {
addSource(d);
requests.push(d);
if (!cache[d[3]] && 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('load', null)
.classed('tile-loaded', true);
render(selection);
}
function error(d) {
cache[d[3]] = false;
d3.select(this)
.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-loaded', false)
.each(function() {
var tile = this;
window.setTimeout(function() {
// this tile may already be removed
if (tile.parentNode) {
tile.parentNode.removeChild(tile);
}
}, 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);
}
background.offset = function(_) {
if (!arguments.length) return offset;
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.size = function(_) {
if (!arguments.length) return tile.size();
tile.size(_);
return background;
};
function setPermalink(source) {
var tag = source.data.sourcetag;
var q = iD.util.stringQs(location.hash.substring(1));
if (tag) {
location.replace('#' + iD.util.qsString(_.assign(q, {
layer: tag
}), true));
} else {
location.replace('#' + iD.util.qsString(_.omit(q, 'layer'), true));
}
}
background.source = function(_) {
if (!arguments.length) return source;
source = _;
cache = {};
tile.scaleExtent((source.data && source.data.scaleExtent) || [1, 20]);
setPermalink(source);
return background;
};
return background;
};