A modified version of Mike Bostock’s mesmerising stereographic projection example.
A NASA Blue Marble raster image is reprojected using d3.geo.stereographic().invert from the geo.projection D3 plugin.
A modified version of Mike Bostock’s mesmerising stereographic projection example.
A NASA Blue Marble raster image is reprojected using d3.geo.stereographic().invert from the geo.projection D3 plugin.
| <!DOCTYPE html> | |
| <meta charset="utf-8"> | |
| <style> | |
| body { padding: 0px 230px; background: #000; } | |
| </style> | |
| <script src="http://d3js.org/d3.v3.min.js"></script> | |
| <script src="https://raw.github.com/d3/d3-plugins/master/geo/projection/projection.js"></script> | |
| <canvas id="canvas" width="540" height="500" style="width: 1080px; height: 1000px"></canvas> | |
| <script> | |
| var width = 500 / 2, | |
| height = 500 / 2; | |
| var projection = d3.geo.stereographic() | |
| .scale(50) | |
| .translate([width / 2, height / 2]); | |
| var path = d3.geo.path() | |
| .projection(projection); | |
| var x = d3.scale.linear() | |
| .domain([180, -180]) | |
| .range([0, width * 2]); | |
| var y = d3.scale.linear() | |
| .domain([90, -90]) | |
| .range([height * 2, 0]); | |
| var image = new Image; | |
| image.onload = onload; | |
| image.src = "readme-world.jpeg"; | |
| function onload() { | |
| var canvas = document.getElementById("canvas"), | |
| context = canvas.getContext("2d"), | |
| sourceWidth = image.width, | |
| sourceHeight = image.height; | |
| context.drawImage(image, 0, 0, sourceWidth, sourceHeight); | |
| var source = context.getImageData(0, 0, sourceWidth, sourceHeight).data, | |
| out = context.createImageData(width, height), | |
| data = out.data, | |
| interpolate = bilinear(function(x, y, offset) { | |
| return source[(y * sourceWidth + x) * 4 + offset]; | |
| }); | |
| context.clearRect(0, 0, sourceWidth, sourceHeight); | |
| refresh(); | |
| d3.select(window).on("mousemove", function() { | |
| var p = d3.mouse(canvas); | |
| projection.rotate([x.invert(p[0]), y.invert(p[1])]); | |
| refresh(); | |
| }); | |
| function refresh() { | |
| for (var y = 0; y < height; y++) { | |
| for (var x = 0; x < width; x++) { | |
| var i = (y * width + x) * 4, | |
| location = projection.invert([x, y]), | |
| sx = (180 + location[0]) / 360 * sourceWidth, | |
| sy = (90 - location[1]) / 180 * sourceHeight; | |
| data[i++] = interpolate(sx, sy, 0); | |
| data[i++] = interpolate(sx, sy, 1); | |
| data[i++] = interpolate(sx, sy, 2); | |
| data[i++] = 0xff; | |
| } | |
| } | |
| context.putImageData(out, 0, 0); | |
| } | |
| } | |
| function bilinear(f) { | |
| return function(x, y, o) { | |
| var x0 = Math.floor(x), | |
| y0 = Math.floor(y), | |
| x1 = Math.ceil(x), | |
| y1 = Math.ceil(y); | |
| if (x0 === x1 || y0 === y1) return f(x0, y0, o); | |
| return (f(x0, y0, o) * (x1 - x) * (y1 - y) | |
| + f(x1, y0, o) * (x - x0) * (y1 - y) | |
| + f(x0, y1, o) * (x1 - x) * (y - y0) | |
| + f(x1, y1, o) * (x - x0) * (y - y0)) / ((x1 - x0) * (y1 - y0)); | |
| }; | |
| } | |
| </script> |