Skip to content

Instantly share code, notes, and snippets.

@calvinmetcalf
Created May 23, 2013 18:05
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save calvinmetcalf/5638105 to your computer and use it in GitHub Desktop.
Save calvinmetcalf/5638105 to your computer and use it in GitHub Desktop.
Satellite Raster
<!DOCTYPE html>
<meta charset="utf-8">
<title>Satellite Raster</title>
<style>
@import url(../../maps.css);
#map {
position: relative;
margin: 0 auto;
overflow: hidden;
clip-path: url(#clip);
}
canvas {
cursor: auto;
}
.layer0 {
-webkit-transform: scale(.5);
-webkit-transform-origin: 0 0 0;
}
.layer {
-webkit-transform-origin: 0 0 0;
}
.tile {
position: absolute;
}
</style>
<div id="map"></div>
<script src="http://www.jasondavies.com/d3.min.js"></script>
<script src="http://www.jasondavies.com/maps/d3.geo.projection.min.js"></script>
<script src="http://www.jasondavies.com/maps/topojson.min.js"></script>
<script src="http://www.jasondavies.com/maps/d3.quadtiles.js"></script>
<script>
var ratio = 2,
radians = Math.PI / 180,
width = 960 * ratio,
height = 960 * ratio,
p = .5;
var projection = d3.geo.satellite()
.translate([width / 2, .65 * height / 2])
.scale(5500 * ratio)
.distance(1.1)
.rotate([76.00, -34.50, 32.12])
.center([-2, 5])
.tilt(25)
.clipAngle(Math.acos(1 / 1.1) * 180 / Math.PI - 1e-6)
.clipExtent([[p, p], [width - p, height - p]]);
var path = d3.geo.path().projection(projection);
var layer = d3.select("#map")
.style("width", width / ratio + "px")
.style("height", height / ratio + "px")
.append("div").attr("class", "layer0")
.append("div").attr("class", "layer")
var imgCanvas = document.createElement("canvas"),
imgContext = imgCanvas.getContext("2d");
function onload(d, canvas, pot) {
var t = projection.translate(),
s = projection.scale(),
c = projection.clipExtent(),
cr = Math.cos(projection.clipAngle() * radians),
image = d.image,
dx = image.width,
dy = image.height,
k = d.key,
width = 1 << k[2];
projection.translate([0, 0]).scale(1 << pot).clipExtent(null);
imgCanvas.width = dx, imgCanvas.height = dy;
imgContext.drawImage(image, 0, 0, dx, dy);
var bounds = path.bounds(d),
x0 = d.x0 = bounds[0][0] | 0,
y0 = d.y0 = bounds[0][1] | 0,
x1 = bounds[1][0] + 1 | 0,
y1 = bounds[1][1] + 1 | 0;
var λ0 = k[0] / width * 360 - 180,
λ1 = (k[0] + 1) / width * 360 - 180,
φ1 = mercatorφ(k[1] / width * 360 - 180),
φ0 = mercatorφ((k[1] + 1) / width * 360 - 180);
var width = canvas.width = x1 - x0,
height = canvas.height = y1 - y0,
context = canvas.getContext("2d");
if (width > 0 && height > 0) {
var sourceData = imgContext.getImageData(0, 0, dx, dy).data,
target = context.createImageData(width, height),
targetData = target.data;
for (var y = y0, i = -1; y < y1; ++y) {
for (var x = x0; x < x1; ++x) {
var p = projection.invert([x, y]), λ = p[0], φ = p[1];
if (isNaN(λ) || isNaN(φ) || λ > λ1 || λ < λ0 || φ > φ1 || φ < φ0) { i += 4; continue; }
var q = (((λ - λ0) / (λ1 - λ0) * dx | 0) + ((φ1 - φ) / (φ1 - φ0) * dy | 0) * dx) * 4;
//var q = ((90 - φ) / 180 * dy | 0) * dx + ((180 + λ) / 360 * dx | 0) << 2;
targetData[++i] = sourceData[q];
targetData[++i] = sourceData[++q];
targetData[++i] = sourceData[++q];
targetData[++i] = 255;
}
}
context.putImageData(target, 0, 0);
}
d3.selectAll([canvas])
.style("left", x0 + "px")
.style("top", y0 + "px");
projection.translate(t).scale(s).clipExtent(c);
}
redraw();
function redraw() {
// TODO improve zoom level computation
var pot = Math.log(projection.scale()) / Math.LN2 | 0,
ds = projection.scale() / (1 << pot),
t = projection.translate(),
z = pot - 6;
layer.style("-webkit-transform", "translate(" + t.map(pixel) + ")scale(" + ds + ")");
var tile = layer.selectAll(".tile")
.data(d3.quadTiles(projection, z), key);
tile.enter().append("canvas")
.attr("class", "tile")
.each(function(d) {
var canvas = this;
var image = d.image = new Image;
image.crossOrigin = true;
image.onload = function() { setTimeout(function() { onload(d, canvas, pot); }, 1); };
var k = d.key;
image.src = "http://" + ["a", "b", "c"][(k[0] * 31 + k[1]) % 3] + ".tile.stamen.com/watercolor/" + k[2] + "/" + k[0] + "/" + k[1] + ".jpg";
});
tile.exit().remove();
}
function key(d) { return d.key.join(", "); }
function mercatorφ(y) {
return Math.atan(Math.exp(-y * Math.PI / 180)) * 360 / Math.PI - 90;
}
function pixel(d) { return (d | 0) + "px"; }
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment