Skip to content

Instantly share code, notes, and snippets.

@mbostock
Forked from jasondavies/README.md
Last active September 30, 2021 22:46
Show Gist options
  • Save mbostock/3788999 to your computer and use it in GitHub Desktop.
Save mbostock/3788999 to your computer and use it in GitHub Desktop.
Antimeridian Cutting
license: gpl-3.0
redirect: https://observablehq.com/@d3/antimeridian-cutting

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 centered at the prime meridian and cut the globe along ±180° longitude, which is called the antimeridian.

But what happens to shapes that cross the antimeridian, 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 antimeridian would also cross the map, leading to distracting visual artifacts!

To avoid this problem, many freely-available shapefiles are already cut along the antimeridian. 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 antimeridian 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">
<canvas width="960" height="500"></canvas>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<script>
var canvas = d3.select("canvas"),
context = canvas.node().getContext("2d"),
width = canvas.property("width"),
height = canvas.property("height");
var lambda = d3.scaleLinear()
.domain([0, width])
.range([-180, 180]);
var phi = d3.scaleLinear()
.domain([0, height])
.range([90, -90]);
var projection = d3.geoConicEqualArea()
.scale(150)
.center([0, 33])
.translate([width / 2, height / 2])
.precision(0.3);
var path = d3.geoPath()
.projection(projection)
.context(context);
d3.json("https://d3js.org/world-110m.v1.json", function(error, world) {
if (error) throw error;
var land = topojson.feature(world, world.objects.land),
sphere = {type: "Sphere"},
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([lambda(p[0]), phi(p[1])]), draw();
d3.event.preventDefault();
}
function draw() {
context.clearRect(0, 0, width, height);
context.beginPath();
path(land);
context.fill();
context.beginPath();
path(sphere);
context.stroke();
}
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment