Skip to content

Instantly share code, notes, and snippets.

@robinhouston
Forked from mbostock/.block
Created December 14, 2012 01:26
Show Gist options
  • Save robinhouston/4281738 to your computer and use it in GitHub Desktop.
Save robinhouston/4281738 to your computer and use it in GitHub Desktop.

A central challenge of projecting geography is that the globe is spherical while the display is planar. Projecting the globe onto the screen thus requires cutting the globe at least once. Most commonly, world maps are horizontally cented at the prime meridian and cut the globe along ±180° longitude, which is called the antemeridian.

But what happens to shapes that cross the antemeridian, such as the Eastern tip of Russia? When projecting Russia using a normal cylindrical projection, for example, the Western part of Russia appears on the right edge, while the Eastern part appears on the left edge. A naïve projection of lines that cross the antemeridian would also cross the map, leading to distracting visual artifacts!

To avoid this problem, many freely-available shapefiles are already cut along the antemeridian. This enables geographic software to ignore the topological complexity introduced by the spherical globe. Unfortunately, by relying on pre-cut input, much geographic software (including earlier versions of D3) cannot correctly handle different aspects and rotations of the globe!

D3 3.0 introduces a powerful new geographic projection system that supports antemeridian cutting, clipping, adaptive resampling, and arbitrary three-axis rotation! Use your mouse to rotate the world above and see a new aspect! Also try rotating the Winkel tripel, gnomonic, stereographic and orthographic projections, among many other examples.

Feedback, comments, questions? Discuss on HN.

<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v0.min.js"></script>
<script>
var width = 960,
height = 500;
var λ = d3.scale.linear()
.domain([0, width])
.range([-180, 180]);
var φ = d3.scale.linear()
.domain([0, height])
.range([90, -90]);
var projection = d3.geo.albers()
.scale(140)
.translate([width * .5, height * .61])
.precision(.2);
var canvas = d3.select("body").append("canvas")
.attr("width", width)
.attr("height", height);
var context = canvas.node().getContext("2d");
var path = d3.geo.path()
.projection(projection)
.context(context);
d3.json("/d/4090846/world-110m.json", function(error, world) {
var land = topojson.object(world, world.objects.land),
touch = "ontouchstart" in window;
canvas.on(touch ? "touchmove" : "mousemove", move);
draw();
function move() {
var p = touch ? d3.touches(this)[0] : d3.mouse(this);
projection.rotate([λ(p[0]), φ(p[1])]), draw();
d3.event.preventDefault();
}
function draw() {
context.clearRect(0, 0, width, height);
context.beginPath();
path(land);
context.fill();
}
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment