How to convert latitude and longitude coordinates into pixel offsets

Dmitriy Suverov
6 min readApr 24, 2020

--

A simple implementation of a conversion algorithm

Gerardus Mercator
Gerardus Mercator, inventor of the Mercator projection

TL;DR

Scroll to “Implementation”

Preface

Not so long ago, while working on a new front end application, I encountered quite a challenging task. I was asked to display specific geographic locations on a custom image of the world map without using existing solutions, e.g. google maps. I was provided with data that looked like an array of objects, containing: latitude, longitude, and the name of a location. It is known that any location on the Earth can be described as a point with longitude and latitude coordinates. The difficult part of the task was locations supposed to be displayed on plain*.PNG image. Unfortunately, given coordinates and a map image can’t be matched out of the box. So what can we do to match them? First of all, let’s look at how exactly a coordinate system works.

What is a coordinate system?

A Geographic coordinate system (GCS) is a three-dimensional reference system that locates points on Earth’s surface. It’s shaped like a globe — spherical. The units of measure are usually decimal degrees. A point has two coordinate values: latitude and longitude.

A Projected coordinate system (PCS) is a two-dimensional planar surface. However, the Earth’s surface is three-dimensional. Transforming three-dimensional space onto a two-dimensional surface is called projection. Using projection formulas, the data from a geographical location (latitude and longitude) on a spheroid converts to a corresponding location (x, y) on a flat, two-dimensional surface, where x refers to the distance along the horizontal axis (easting), and y refers to the distance along the vertical axis (northing). The units of measure is linear, e.g. meters.

The main difference between these two:

  • A GCS defines where the data is located on the earth’s surface.
  • A PCS tells the data how to draw on a flat surface, like on a paper map or a computer screen.
Illustrated Difference between CGS and PCS
Difference between CGS and PCS

Regarding our task, we need to project GCS coordinates (latitude and longitude) on a flat image.

The Mercator projection

The Mercator projection is a cylindrical map projection presented by a Flemish geographer and cartographer Gerardus Mercator in 1569. It became a standard projection for navigation because of its unique property to represent any course of constant bearing as a straight segment. The projection is based on the spherical model of the Earth.

As previously mentioned, PCS uses mathematical formulas to convert GCS coordinates to planar (projected) coordinates. To calculate easting and northing, the spherical Mercator projection uses the following formula:

Mercator PCS formulae for X and Y coordinates
Formula for X and Y coordinates, wiki

where:

  • λ is current longitude, in radians,
  • λ0 is the longitude of an arbitrary central meridian usually, but not always, that of the Prime meridian,
  • φ is current longitude, in radians,
  • R is a radius of a sphere.

Modern GCS and PCS.

There is a geodetic system called World Geodetic System (WGS). Nowadays, it has become a standard for use in cartography, geodesy, and satellite navigation, including GPS. It assumes the shape of the Earth is an ellipsoid and uses the equator and the Prime meridian as reference points for latitude and longitude correspondingly. The latest revision of WGS is called WGS 84.

Major world map applications, like Google maps, use Web Mercator projection (PCS), also known as WGS 84/Pseudo-Mercator. This projection is a variant of the Mercator projection. It uses meter as a unit of length. Unlike the ellipsoidal Mercator and spherical Mercator, the Web Mercator is not quite conformal due to its use of ellipsoidal datum geographical coordinates against a spherical projection. This is done for the sake of calculation performance, as the spherical form is much simpler to calculate. It is also important to note, that this projection is suitable for use only in between 85.06°S and 85.06°N, due to the mentioned simplification.

Adapting the algorithm

Now that we know how to transform geographic coordinates to projection coordinates, namely offsets, we need to amend the algorithm to meet our needs. Calculated coordinates can be easily combined with CSS’s absolute positioning of HTML elements. The only inconvenience is that the base point(0,0) of the coordinate system is located at a crossing of the Prime meridian and the equator (center of normal projection), while the absolute positioning uses the top left corner as a base point. That is where false easting would come in handy.

False easting is a linear value applied to the origin of the x coordinates to ensure that x values are positive.

False easting allows us to use the left border of our map image as a base meridian. Usually, longitude ranges from -180° to +180° westward and eastward from the Prime meridian (0°). It is for this reason, we will use 180° as the value of false easting. A similar approach will be applied to latitude values.

Time to express these thoughts in code.

Implementation of an algorithm

As previously shown, northing and easting equations both require radius. We can find radius using a simple formula:

R = C/(2*PI)

where C is circumference, namely the equator’s length.
The length of the equator on the Mercator projection equals the width of a map image. Then the radius equation will look like the following:

const radius = mapWidth / (2 * Math.PI);

Next, we are going to use false easting. Let’s declare its value:

const FE = 180;

The original conversion algorithm works with latitude and longitude values in radians. We need a function that will convert degrees to radians:

We are now ready to tackle the first step. Let’s calculate the horizontal offset.
Corresponding equation wants us to multiply the radius and the difference between referential and actual longitudes. We are going to use a meridian at the left border of the map as a reference. Thus, because the reference’s meridian longitude equals 0, we can omit subtraction, and use false easting. We will get the following expression:

const lonRad = degreesToRadians(longitude + FE);
const x = lonRad * radius;

Now we have our horizontal offset adapted to our needs.
We need to calculate vertical offset:

const latRad = degreesToRadians(latitude);
const verticalOffsetFromEquator = radius * Math.log(Math.tan(Math.PI / 4 + latRad / 2));

Value of verticalOffsetFromEquator variable shows offset from the middle horizontal line of the map, e.g. from the mapped equator. We need to calculate offset from the top border of a map image:

const y = mapHeight / 2 - verticalOffsetFromEquator;

The final conversion function is listed below:

At this point, we can use the function’s result as is.

Projecting locations onto an image

When you develop your application, make sure that map image, as well as coordinates, correspond WGS 84, otherwise, you will get poor results of coordinates conversion

I’ll try to keep things as simple and clear as possible, within the following example of the implementation of the algorithm.
A suitable projection map image was obtained from wikimedia.

Mercator projection of the Earth
Mercator projection of the Earth

Html structure:

Styles:

Let’s determine coordinates of the New York and Melbourne:

As mentioned before, the algorithm requires image dimensions. To figure out the actual size of an image:

To pinpoint our cities on the map, we will need a marker. We can use an empty span element with red borders (red dots). These pointers will be gathered in a container, their mount-node. Just for convenience, we will declare the following function creating a pointer:

Finally, we are able to call our functions and see the result:

The expected result is:

Mercator projection map with New York and Melbourne marked
New York and Melbourne locations

As you can see, our red dots are at expected places.

Full code is available here.
Also, there is a live demo

Further readings:

--

--