A demo of d3-tile when projecting to fit a bounding box.
Tiles copyright OpenStreetMap contributors.
license: gpl-3.0 | |
height: 600 | |
redirect: https://observablehq.com/@d3/raster-vector-ii |
A demo of d3-tile when projecting to fit a bounding box.
Tiles copyright OpenStreetMap contributors.
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
svg, | |
#tiles { | |
position: absolute; | |
width: 960px; | |
height: 600px; | |
overflow: hidden; | |
} | |
</style> | |
<div id="tiles"></div> | |
<svg width="960" height="600"></svg> | |
<script src="//d3js.org/d3.v4.min.js"></script> | |
<script src="//d3js.org/d3-tile.v0.0.min.js"></script> | |
<script src="//d3js.org/topojson.v1.min.js"></script> | |
<script> | |
var pi = Math.PI, | |
tau = 2 * pi; | |
var svg = d3.select("svg"), | |
width = +svg.attr("width"), | |
height = +svg.attr("height"); | |
// Initialize the projection to fit the world in a 1×1 square centered at the origin. | |
var projection = d3.geoMercator() | |
.scale(1 / tau) | |
.translate([0, 0]); | |
// Compute the projected bounding box given a geographic bounding box (here, California). | |
// This assumes parallels are horizontal and meridians are vertical… | |
// but you could use path.bounds to handle arbitrary shapes. | |
// Note that the y-dimension is flipped relative to latitude! | |
var bounds = [[-124.408585, 32.534291], [-114.138271, 42.007768]], | |
p0 = projection([bounds[0][0], bounds[1][1]]), | |
p1 = projection([bounds[1][0], bounds[0][1]]); | |
// Convert this to a scale k and translate tx, ty for the projection. | |
// For crisp image tiles, clamp to the nearest power of two. | |
var k = floor(0.95 / Math.max((p1[0] - p0[0]) / width, (p1[1] - p0[1]) / height)), | |
tx = (width - k * (p1[0] + p0[0])) / 2, | |
ty = (height - k * (p1[1] + p0[1])) / 2; | |
projection | |
.scale(k / tau) | |
.translate([tx, ty]); | |
// Lastly convert this to the corresponding tile.scale and tile.translate; | |
// see http://bl.ocks.org/mbostock/4150951 for a related example. | |
var tiles = d3.tile() | |
.size([width, height]) | |
.scale(k) | |
.translate([tx, ty]) | |
(); | |
d3.select("#tiles") | |
.selectAll("img").data(tiles).enter().append("img") | |
.style("position", "absolute") | |
.attr("src", function(d, i) { return "http://" + "abc"[d[1] % 3] + ".tile.openstreetmap.org/" + d[2] + "/" + d[0] + "/" + d[1] + ".png"; }) | |
.style("left", function(d) { return (d[0] + tiles.translate[0]) * tiles.scale + "px"; }) | |
.style("top", function(d) { return (d[1] + tiles.translate[1]) * tiles.scale + "px"; }) | |
.attr("width", tiles.scale) | |
.attr("height", tiles.scale); | |
d3.json("/mbostock/raw/4090846/us.json", function(error, us) { | |
if (error) throw error; | |
svg.append("path") | |
.datum(topojson.mesh(us, us.objects.states)) | |
.attr("d", d3.geoPath().projection(projection)) | |
.attr("fill", "none") | |
.attr("stroke", "#000"); | |
}); | |
function floor(k) { | |
return Math.pow(2, Math.floor(Math.log(k) / Math.LN2)); | |
} | |
</script> |