Skip to content

Instantly share code, notes, and snippets.

@mbostock
Last active July 20, 2019 00:48
Show Gist options
  • Save mbostock/eb0c48375fcdcdc00c54a92724733d0d to your computer and use it in GitHub Desktop.
Save mbostock/eb0c48375fcdcdc00c54a92724733d0d to your computer and use it in GitHub Desktop.
Tile by Bounding Box
license: gpl-3.0
height: 600
redirect: https://observablehq.com/@d3/raster-vector-ii
<!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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment