D3 3.0 supports three-axis rotation for geographic projections. This example demonstrates rotating lambda (λ), phi (φ) and gamma (γ) in three side-by-side bottomley projections.
forked from mbostock's block: Three-Axis Rotation
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
.title { | |
display: inline-block; | |
font-size: 48px; | |
line-height: 90px; | |
text-align: center; | |
} | |
</style> | |
<body> | |
<script src="//d3js.org/d3.v3.min.js"></script> | |
<script src="//d3js.org/topojson.v1.min.js"></script> | |
<script> | |
var π = Math.PI, halfπ=π/2; | |
function bottomleyRaw(ψ) { | |
function forward(λ, φ) { | |
var ρ = halfπ - φ, | |
η = ρ ? λ * Math.sin(ψ) * Math.sin(ρ) / ρ : ρ; | |
return [ | |
ρ * Math.sin(η) / Math.sin(ψ), | |
halfπ - ρ * Math.cos(η) | |
]; | |
} | |
forward.invert = function(x, y) { | |
var x1 = x * Math.sin(ψ), | |
y1 = halfπ - y; | |
var ρ = Math.sqrt( x1 * x1 + y1 * y1 ), | |
η = Math.atan( x1 / y1 ); | |
return [ | |
(ρ ? ρ / Math.sin(ρ) : 1) * η / Math.sin(ψ), | |
halfπ - ρ | |
]; | |
}; | |
return forward; | |
} | |
(d3.geo.bottomley = function() { | |
var ψ = π/6, | |
mutate = d3.geo.projectionMutator(bottomleyRaw), | |
projection = mutate(ψ); | |
projection.variant = function(_) { | |
if (!arguments.length) return ψ; | |
return mutate(ψ = _); | |
}; | |
return projection; | |
}).raw = bottomleyRaw; | |
var diameter = 960 / 3, | |
radius = diameter >> 1, | |
velocity = .01, | |
then = Date.now(); | |
var projection = d3.geo.bottomley() | |
.scale(radius/2 - 2) | |
.translate([radius, radius]) | |
//.clipAngle(90) | |
.precision(0); | |
d3.select("body").selectAll(".title") | |
.data(["λ", "φ", "γ"]) | |
.enter().append("div") | |
.attr("class", "title") | |
.style("width", diameter + "px") | |
.text(function(d) { return d; }); | |
var canvas = d3.select("body").selectAll("canvas") | |
.data(d3.range(3)) | |
.enter().append("canvas") | |
.attr("width", diameter) | |
.attr("height", diameter); | |
var path = d3.geo.path() | |
.projection(projection); | |
d3.json("https://gist.githubusercontent.com/mbostock/4090846/raw/d534aba169207548a8a3d670c9c2cc719ff05c47/world-110m.json", function(error, world) { | |
if (error) throw error; | |
var land = topojson.feature(world, world.objects.land), | |
globe = {type: "Sphere"}; | |
d3.timer(function() { | |
var angle = velocity * (Date.now() - then); | |
canvas.each(function(i) { | |
var rotate = [0, 0, 0], context = this.getContext("2d"); | |
rotate[i] = angle, projection.rotate(rotate); | |
context.clearRect(0, 0, diameter, diameter); | |
context.beginPath(), path.context(context)(land), context.fill(); | |
context.beginPath(), path(globe), context.stroke(); | |
}); | |
}); | |
}); | |
</script> |