From 3ec03e2526c0210a5beb22f81aefe1aac0ce51db Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Tue, 4 Dec 2012 12:36:44 -0500 Subject: [PATCH] Use d3.geo.tile in connection. Fixes #103 --- js/id/connection.js | 60 +++++++++++++++++++++++++++- js/id/renderer/background.js | 4 +- js/id/renderer/map.js | 76 ++++-------------------------------- 3 files changed, 66 insertions(+), 74 deletions(-) diff --git a/js/id/connection.js b/js/id/connection.js index 7e81fd779..07af7f442 100644 --- a/js/id/connection.js +++ b/js/id/connection.js @@ -1,10 +1,11 @@ iD.Connection = function() { - var event = d3.dispatch('auth'), + var event = d3.dispatch('auth', 'load'), apiURL = 'http://www.openstreetmap.org', connection = {}, refNodes = {}, user = {}, + apiTilesLoaded = {}, oauth = iD.OAuth().api(apiURL); // Request data within the bbox from an external OSM server. @@ -14,7 +15,7 @@ iD.Connection = function() { } function loadFromURL(url, callback) { - d3.xml(url, function(err, dom) { callback(parse(dom)); }); + d3.xml(url, function(err, dom) { callback(err, parse(dom)); }); } function getNodes(obj) { @@ -131,6 +132,55 @@ iD.Connection = function() { }); } + function tileAtZoom(t, distance) { + var power = Math.pow(2, distance); + return [ + Math.floor(t[0] * power), + Math.floor(t[1] * power), + t[2] + distance]; + } + + function tileAlreadyLoaded(c) { + if (apiTilesLoaded[c]) return false; + for (var i = 0; i < 4; i++) { + if (apiTilesLoaded[tileAtZoom(c, -i)]) return false; + } + return true; + } + + function apiRequestExtent(extent) { + bboxFromAPI(extent, event.load); + } + + function loadTiles(projection) { + var scaleExtent = [16, 16], + s = projection.scale(), + tiles = d3.geo.tile() + .scaleExtent(scaleExtent) + .scale(s) + .translate(projection.translate())(), + z = Math.max(Math.log(s) / Math.log(2) - 8, 0), + rz = Math.max(scaleExtent[0], Math.min(scaleExtent[1], Math.floor(z))), + ts = 256 * Math.pow(2, z - rz), + tile_origin = [ + s / 2 - projection.translate()[0], + s / 2 - projection.translate()[1]]; + + function apiExtentBox(c) { + var x = (c[0] * ts) - tile_origin[0]; + var y = (c[1] * ts) - tile_origin[1]; + apiTilesLoaded[c] = true; + return [ + projection.invert([x, y]), + projection.invert([x + ts, y + ts])]; + } + + return tiles + .filter(tileAlreadyLoaded) + .map(apiExtentBox) + .map(apiRequestExtent); + } + connection.url = function(_) { if (!arguments.length) return apiURL; apiURL = _; @@ -144,6 +194,11 @@ iD.Connection = function() { return connection; }; + connection.flush = function() { + apiTilesLoaded = {}; + return connection; + }; + connection.logout = function() { oauth.logout(); event.auth(); @@ -152,6 +207,7 @@ iD.Connection = function() { connection.bboxFromAPI = bboxFromAPI; connection.loadFromURL = loadFromURL; + connection.loadTiles = _.debounce(loadTiles, 1000); connection.userDetails = userDetails; connection.authenticate = authenticate; connection.authenticated = authenticated; diff --git a/js/id/renderer/background.js b/js/id/renderer/background.js index 1f53b398e..4032148ba 100644 --- a/js/id/renderer/background.js +++ b/js/id/renderer/background.js @@ -20,8 +20,7 @@ iD.Background = function() { .selectAll("image") .data(tiles, function(d) { return d; }); - image.exit() - .remove(); + image.exit().remove(); image.enter().append("image") .attr("xlink:href", source); @@ -82,4 +81,3 @@ iD.Background.Bing = function (coord) { .replace('{y}', coord[1]) .replace('{z}', coord[2]); }; - diff --git a/js/id/renderer/map.js b/js/id/renderer/map.js index e87d512ba..05f6aeffa 100644 --- a/js/id/renderer/map.js +++ b/js/id/renderer/map.js @@ -330,82 +330,19 @@ iD.Map = function() { map.size = function(_) { if (!arguments.length) return dimensions; dimensions = _; - surface .size(dimensions) .selectAll('#clip-rect') .size(dimensions); - background.size(dimensions); - - redraw(); - - return map; + return redraw(); }; - function tileAtZoom(t, distance) { - var power = Math.pow(2, distance); - return [ - Math.floor(t[0] * power), - Math.floor(t[1] * power), - t[2] + distance]; + function connectionLoad(err, result) { + history.merge(result); + drawVector(iD.Util.trueObj(Object.keys(result.entities))); } - function tileAlreadyLoaded(c) { - if (apiTilesLoaded[c]) return false; - for (var i = 0; i < 4; i++) { - if (apiTilesLoaded[tileAtZoom(c, -i)]) return false; - } - return true; - } - - function apiTiles() { - var t = projection.translate(), - s = projection.scale(), - z = Math.max(Math.log(s) / Math.log(2) - 8, 0), - rz = Math.floor(z), - ts = 512 * Math.pow(2, z - rz), - tile_origin = [s / 2 - t[0], s / 2 - t[1]], - coords = [], - cols = d3.range(Math.max(0, Math.floor(tile_origin[0] / ts)), - Math.max(0, Math.ceil((tile_origin[0] + dimensions[0]) / ts))), - rows = d3.range(Math.max(0, Math.floor(tile_origin[1] / ts)), - Math.max(0, Math.ceil((tile_origin[1] + dimensions[1]) / ts))); - - cols.forEach(function(x) { - rows.forEach(function(y) { - coords.push([x, y, rz]); - }); - }); - - function apiExtentBox(c) { - var x = (c[0] * ts) - tile_origin[0]; - var y = (c[1] * ts) - tile_origin[1]; - apiTilesLoaded[c] = true; - return [ - projection.invert([x, y]), - projection.invert([x + ts, y + ts])]; - } - - return coords.filter(tileAlreadyLoaded).map(apiExtentBox); - } - - - function apiRequestExtent(extent) { - connection.bboxFromAPI(extent, function (result) { - if (result instanceof Error) { - // TODO: handle - } else { - history.merge(result); - drawVector(iD.Util.trueObj(Object.keys(result.entities))); - } - }); - } - - var download = _.debounce(function() { - apiTiles().map(apiRequestExtent); - }, 1000); - function nameHoverIn() { var entity = d3.select(d3.event.target).data(); if (entity) d3.select('.messages').text(entity[0].tags.name || '#' + entity[0].id); @@ -501,7 +438,7 @@ iD.Map = function() { tilegroup.call(background); } if (map.zoom() > 16) { - download(); + connection.loadTiles(projection); drawVector(dragging); } else { hideVector(); @@ -577,13 +514,14 @@ iD.Map = function() { }; map.flush = function () { - apiTilesLoaded = {}; + connection.flush(); return map; }; map.connection = function(_) { if (!arguments.length) return connection; connection = _; + connection.on('load', connectionLoad); return map; };