From f9455270f1c6ef367534766aff0dfe08f776c0cf Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Thu, 6 Dec 2012 15:07:37 -0500 Subject: [PATCH] Do pyramid loading in both directions, use transforms for tile sizing. Fixes #190 --- css/app.css | 14 +++++++ js/id/renderer/background.js | 74 +++++++++++++++++++++++++++++++----- js/id/renderer/map.js | 16 +++++--- 3 files changed, 89 insertions(+), 15 deletions(-) diff --git a/css/app.css b/css/app.css index e3de1f714..cceffbb90 100644 --- a/css/app.css +++ b/css/app.css @@ -451,3 +451,17 @@ a.selected { border-bottom-color: #000000; border-width: 0 5px 5px; } + +img.tile { + position:absolute; + transform-origin:0 0; + -ms-transform-origin:0 0; + -webkit-transform-origin:0 0; + -moz-transform-origin:0 0; + -o-transform-origin:0 0; +} + +#tile-g { + position:absolute; + top:0; +} diff --git a/js/id/renderer/background.js b/js/id/renderer/background.js index 92d1f2464..8bacbf019 100644 --- a/js/id/renderer/background.js +++ b/js/id/renderer/background.js @@ -2,8 +2,33 @@ iD.Background = function() { var tile = d3.geo.tile(), scaleExtent = [0, 20], projection, + cache = {}, + transformProp = iD.util.prefixProperty('Transform'), source = d3.functor(''); + function atZoom(t, distance) { + var power = Math.pow(2, distance); + var az = [ + Math.floor(t[0] * power), + Math.floor(t[1] * power), + t[2] + distance]; + az.push(source(az)); + return az; + } + + function upZoom(t, distance) { + var az = atZoom(t, distance), + tiles = []; + for (var x = 0; x < 2; x++) { + for (var y = 0; y < 2; y++) { + var up = [az[0] + x, az[1] + y, az[2]]; + up.push(source(up)); + tiles.push(up); + } + } + return tiles; + } + // derive the tiles onscreen, remove those offscreen and position tiles // correctly for the currentstate of `projection` function background() { @@ -17,23 +42,52 @@ iD.Background = function() { projection.scale() / 2 - projection.translate()[0], projection.scale() / 2 - projection.translate()[1]]; - tiles.forEach(function(t) { t.push(source(t)); }); + var ups = {}; + + tiles.forEach(function(d) { + d.push(source(d)); + // this tile has not been loaded yet + if (!cache[d] && + cache[atZoom(d, -1)] && + !ups[atZoom(d, -1)]) { + ups[atZoom(d, -1)] = true; + tiles.push(atZoom(d, -1)); + } else if (!cache[d]) { + upZoom(d, 1).forEach(function(u) { + if (cache[u] && !ups[u]) { + ups[u] = true; + tiles.push(u); + } + }); + } + }); + var image = this - .selectAll("image") + .selectAll('img') .data(tiles, function(d) { return d; }); image.exit().remove(); - image.enter().append("image") - .attr("xlink:href", function(d) { return d[3]; }); + image.enter().append('img') + .attr('class', 'tile') + .attr('src', function(d) { return d[3]; }) + .on('load', function(d) { + cache[d] = true; + }); - image.attr('transform', function(d) { + function tileSize(d) { + return Math.ceil(256 * Math.pow(2, z - d[2])) / 256; + } + + image.style(transformProp, function(d) { + var _ts = 256 * Math.pow(2, z - d[2]); + var scale = tileSize(d); return 'translate(' + - Math.round((d[0] * ts) - tile_origin[0]) + ',' + - Math.round((d[1] * ts) - tile_origin[1]) + ')'; - }) - .attr("width", Math.ceil(ts)) - .attr("height", Math.ceil(ts)); + Math.round((d[0] * _ts) - tile_origin[0]) + 'px,' + + Math.round((d[1] * _ts) - tile_origin[1]) + 'px) scale(' + scale + ',' + scale + ')'; + }); + + if (Object.keys(cache).length > 100) cache = {}; } background.projection = function(_) { diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index 3d3978b5d..8c3a7b860 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -66,7 +66,12 @@ iD.Map = function() { supersurface, surface, defs, tilegroup, r, g, alength; function map() { - supersurface = this.append('div').call(zoom); + tilegroup = this.append('div') + .attr('id', 'tile-g'); + + supersurface = this.append('div') + .style('position', 'absolute') + .call(zoom); surface = supersurface.append('svg') .on('mouseup', resetTransform) @@ -79,10 +84,6 @@ iD.Map = function() { .attr('id', 'clip-rect') .attr({ x: 0, y: 0 }); - tilegroup = surface.append('g') - .attr('id', 'tile-g') - .attr('clip-path', 'url(#clip)'); - r = surface.append('g') .on('mouseover', hoverIn) .on('mouseout', hoverOut) @@ -306,9 +307,13 @@ iD.Map = function() { var a = d3.event.translate, b = translateStart; if (support3d) { + tilegroup.style(transformProp, + 'translate3d(' + ~~(a[0] - b[0]) + 'px,' + ~~(a[1] - b[1]) + 'px, 0px)'); surface.style(transformProp, 'translate3d(' + ~~(a[0] - b[0]) + 'px,' + ~~(a[1] - b[1]) + 'px, 0px)'); } else { + tilegroup.style(transformProp, + 'translate(' + ~~(a[0] - b[0]) + 'px,' + ~~(a[1] - b[1]) + 'px)'); surface.style(transformProp, 'translate(' + ~~(a[0] - b[0]) + 'px,' + ~~(a[1] - b[1]) + 'px)'); } @@ -322,6 +327,7 @@ iD.Map = function() { if (!surface.style(transformProp)) return; translateStart = null; surface.style(transformProp, ''); + tilegroup.style(transformProp, ''); redraw(); }