|
<!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.polyhedron.waterman() |
|
.rotate([20, 0]) |
|
.scale(118) |
|
.translate([width / 2, height / 2]) |
|
.precision(.1); |
|
|
|
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> |