Skip to content

Instantly share code, notes, and snippets.

@tmcw
Created April 28, 2017 16:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tmcw/8b0432a195ab127754ea40716bc1b83c to your computer and use it in GitHub Desktop.
Save tmcw/8b0432a195ab127754ea40716bc1b83c to your computer and use it in GitHub Desktop.

A few days ago, we dove deep into the problems with the 180th meridian, and I promised to share how to implement the IETF GeoJSON spec's recommendation to cut lines at the meridian. Here's a method for slicing lines - slicing polygons might be a bit trickier.

The task is to slice lines that use positions outside the longitude range of (-180, 180), producing MultiLineString geometries that stay inside of that range and have points exactly on the 180th meridian.

Here's our starter map, with coordinates well outside the range. Leaflet renders these as-is, on multiple worlds - other libraries like Mapbox GL JS render them on the same one.

https://jsfiddle.net/u67vqxhq/2/embedded/

First, we'll need to know where to slice lines. Usually GeoJSON LineString geometries have many different positions, representing complex paths. The 'slice' only happens between two of these positions at a time, the position right before the border crossing and the one after it.

https://jsfiddle.net/znz2w2dL/6/embedded/js,result/

Here's one possible algorithm to do that: you iterate through each coordinate in the LineString, keeping track of an offset that tracks the 'world offset', and slices lines into MultiLineStrings: you can see the thick sliced MultiLineString elements and the thin original data.

Okay, next: connecting the dots - finding those points at the 180th meridian. There are a few ways to do this: my first instinct was to treat the line as the hypotenuse of a triangle and use trigonometry, which is close to my heart. But upon closer inspection, that would be the long way. All we need to determine the point of intersection with the 180th is the line's equation.

You might remember from middle school that a line is defined by y = mx + b. I prefer words to letters, so I read that as:

We want to create an equation - in our case a function - that gives you a y value for any x value, and the ingredients currently missing are the slope and the intercept. Again, you might remember the equation for getting a slope from any two points on a line:

Once we have the slope, we can move the slope * x bit over to the left side of the equation to solve for the intercept:

All together, in JavaScript, it looks like this: a function that computes the slope first, and then the intercept using that slope, and then returns another function that provides the y coordinate of any point on the line.

function getLineFunction(a, b) {
  var slope = (b[1] - a[1]) / (b[0] - a[0]);
  var intercept = b[1] - (slope * b[0]);
  return function line(x) {
    return (slope * x) + intercept;
  };
}

We apply this method to the gaps between each of the lines in the MultiLineString we generated earlier, and we get this:

https://jsfiddle.net/dot1ec0p/1/embedded/js,result/

// https://jsfiddle.net/7pfoLmjo/1/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment