Little Geo Stories: A Tale Of 180th Meridian

A little story about a very common mistake when working with Earth coordinates around 180th meridiane.

Several days ago, I got a bug request: no airports around a certain hotel were being displayed.

There certainly were some airports relatively close to the hotel, and judging by all conditions they should have been there. This hotel’s page could boast with very good air connections, but it did not.

Like a proper seasoned Geo-expert should do in this case, first thing I did – was checking the location of the hotel.

Of course, it was located on Fiji.

The problem immediately became absolutely clear to me.

What the Earth looks like

The Earth is not flat.

(If you think it is, do not read further. You’ll be disappointed.)

If we want to be precise, we will say that the Earth has a shape of a geoid, which sounds weird. “The Earth has the shape of the Earth”. Seriously? Does this really explain something?

Anyway, for many calculations we use a sphere as a decent approximation. Places on this sphere we can describe as having a latitude and a longitude. Latitude changes if you go from north to south, or the other way around, and longitude changes if you go from east to west.

This is how latitude goes - horizontal
This is how latitude goes – horizontal “cake layers” from north to south; and how longitude goes – vertical “orange slices” from west to east.

Now, since the Earth is a sphere, and longitude changes as you walk along the equator, or along any arbitrary latitude “ring”, strictly maintaining the longitude, sooner or later you will come to the point where you started walking. This “start” line from north to south, going through all latitudes, is called “180th meridian”.

If you want, you can think of it as of a place where you “stitch” the Earth. It is sort of a seam – for coordinates.

180th meridian, a vertical line along one of the longitude
180th meridian, a vertical line along one of the longitude “slices”, acts like a seam: coordinates on the “left” go up to 180 degrees, and coordinates on the “right” start from -180 degrees and increase. Until it’s 180 degrees again, where it must wrap again.

The longitude on west side of the “seam” goes from -180° to 0° and then to +180° again, where it meets and itself again from the eastern side. It is precisely the 360-degree circle. Neat.

Now, what does it all have to do with Fiji, you’re asking?

180th meridian problems

This is the code we had.

We got the hotel point, built a bounding box around it, which is simply a range of coordinates a bit to the east, west, north and south, and searched for airport points in this range.

# This is the point of the hotel around which we'll look for airports.
my $point = { longitude => XXX, latitude => YYY };

# This is how we calculate the bounding box - range of coordinates - for querying airports around the hotel.
my $longitude_east = $point->{longitude} + $radius;
my $longitude_west = $point->{longitude} - $radius;

# And this is the query to get airports from the Airport table
# which has columns "latitude" and "longitude".
$sql += "AND (longitude BETWEEN $longitude_west AND $longitude_east)";

Let’s imagine we run this with the following parameters:

  • longitude of the hotel = 4.83
  • latitude is not really important here, but let’s say 52.303
  • and the radius is 0.5?

We’ll get this SQL query:

SELECT ... FROM Airport
WHERE ...
    AND (longitude BETWEEN 4.33 AND 5.33)

Awesome, should work perfectly fine, shouldn’t it?

This is the code we’ve had for years.

A corner case

What happens when we run:

  • longitude is 179.95
  • and the radius is still 0.5?

Let’s see:

longitude_west = 179.95 - 0.5 => 179.45 <--- great
longitude_east = 179.95 + 0.5 => 180.45 <--- OOPS...

Here we have longitude that is invalid. The Earth only can get from -180° to +180°, right? There cannot possibly be more than this number of degrees in a circle.

We need a piece of code fixing that.

my $longitude_east = $point->{longitude} + $radius;
my $longitude_west = $point->{longitude} - $radius;

# Let's wrap it around it if it's too long:
if ($longitude_east > 180) {
    $longitude_east = 360 - $longitude_east;
} elsif ($longitude_east < -180) {
    $longitude_east += 360;
}

# Of course, same goes for longitude_west.

Now, for the same input, we will have:

longitude_west = 179.45
longitude_east = -179.55

And the result query is:

SELECT ... FROM Airport
WHERE ...
    AND (longitude BETWEEN 179.45 AND -179.55)

Hmm. Something is wrong here, for sure.

All the Earth, except for the place we need

When you wrap your longitude this way, your query will go crazy! It will capture the whole world except for the place you need!

Imagine that! Like a goldfish told you you can have everything in the world, but only not that thing you want the most. Crazy, huh?

When you query longitude values in this way, you get everything but the place you need
When you query longitude values in this way, you get everything but the place you need

Why Fiji, again?

There are only several countries that are located very closely to the 180th meridian – the “seam” of the Earth.

Fiji, New Zealand, Russia, Kiribati are the countries that have territories on both sides of the “seam”, very-very close to it.

Fiji is particularly noticeable because people live quite close to this line there.

Also Fiji has three dots in one word in a row. Can it get better than this?

How to solve? Practical steps

Geo math is hard.

Because of that, it would be wise to use existing libraries and databases supporting geospatial data. The standard called OGC, developed by Open Geospatial Consortium, advises to take close-to-sphere Earth shape into account. (Not flat!) Lots of libraries written in many languages support this standard and are great in all aspects.

Many databases also support proper storage of spatial data and operations with it:

So, if you were doing it all from scratch, you would:

  1. Pick a database with spatial extensions.
  2. Create your Airports table with a geometry field.
  3. Write a query that uses the spatial power. Something like:
SELECT * from Airports airports where ST_Intersects(airports.geom,   ST_Envelope(     ST_Buffer(       ST_GeomFromText('POINT (-179.95 43)', 4326),       radius_you_need     )   ) );

And boom! The database has handled everything for you. No worries about the weird Earth shape at all!

Why I couldn’t use these great steps

I fixed this little cute bug several times in my life in different projects. Yesterday I couldn’t fix it in the usual way.

We have a very old codebase and a very huge database, both of which cannot possibly be rewritten only by me.

We store the airports data in a simple table with two columns for geometry: longitude and latitude.

Of course, the database has no way of knowing that these fields are supposed to be spatial and handled smartly.

And the developer who wrote the code many years ago didn’t think of this problem.

So if you, like me, already have a ton of code in a language without an OGC-compliant library, store data in the database without spatial extensions, and just want to fix this small thing – there is a way too.

How did I solve it

What we need to do here – is to check whether the new bounding box is split by our “seam”. If it is true – then we’ll make two bounding boxes.

The way of getting two bounding boxes: one
Two bounding boxes
# This is what we have at the moment.
$longitude_west = 179.45;
$longitude_east = 0.45;

# Now, let's check for the seam in the belly of the bounding box:
if ($longitude_east < $longitude_west) {
    # Yes, there is the seam.
    # Let's have two conditions for each side of the seam.
    $sql += "AND (
          (longitude BETWEEN $longitude_west AND 180)
          OR
          (longitude BETWEEN -180 AND $longitude_east)
        )";
} else {
    # Same old query
}

And the result query is:

SELECT ... FROM Airport
WHERE ...
  AND (
    (longitude between 179.45 AND 180)
    OR
    (longitude between -180 AND -179.55)
  )

Quick and dirty.

Doesn’t the same apply to latitude?

Of course it does.

Latitude goes from -90° to +90° degrees and not a single step more.

This means latitude queries will suffer exactly from the same problem – if you query anything around the poles.

Currently we don’t have any data around the poles because there are no hotels and no airports. But who knows…