Convert latitude/longitude point to a pixels (x,y) on mercator projection

drunkmonkey picture drunkmonkey · Jan 15, 2013 · Viewed 92.6k times · Source

I'm trying to convert a lat/long point into a 2d point so that I can display it on an image of the world-which is a mercator projection.

I've seen various ways of doing this and a few questions on stack overflow-I've tried out the different code snippets and although I get the correct longitude to pixel, the latitude is always off-seems to be getting more reasonable though.

I need the formula to take into account the image size, width etc.

I've tried this piece of code:

double minLat = -85.05112878;
double minLong = -180;
double maxLat = 85.05112878;
double maxLong = 180;

// Map image size (in points)
double mapHeight = 768.0;
double mapWidth = 991.0;

// Determine the map scale (points per degree)
double xScale = mapWidth/ (maxLong - minLong);
double yScale = mapHeight / (maxLat - minLat);

// position of map image for point
double x = (lon - minLong) * xScale;
double y = - (lat + minLat) * yScale;

System.out.println("final coords: " + x + " " + y);

The latitude seems to be off by about 30px in the example I'm trying. Any help or advice?

Update

Based on this question:Lat/lon to xy

I've tried to use the code provided but I'm still having some problems with latitude conversion, longitude is fine.

int mapWidth = 991;
int mapHeight = 768;

double mapLonLeft = -180;
double mapLonRight = 180;
double mapLonDelta = mapLonRight - mapLonLeft;

double mapLatBottom = -85.05112878;
double mapLatBottomDegree = mapLatBottom * Math.PI / 180;
double worldMapWidth = ((mapWidth / mapLonDelta) * 360) / (2 * Math.PI);
double mapOffsetY = (worldMapWidth / 2 * Math.log((1 + Math.sin(mapLatBottomDegree)) / (1 - Math.sin(mapLatBottomDegree))));

double x = (lon - mapLonLeft) * (mapWidth / mapLonDelta);
double y = 0.1;
if (lat < 0) {
    lat = lat * Math.PI / 180;
    y = mapHeight - ((worldMapWidth / 2 * Math.log((1 + Math.sin(lat)) / (1 - Math.sin(lat)))) - mapOffsetY);
} else if (lat > 0) {
    lat = lat * Math.PI / 180;
    lat = lat * -1;
    y = mapHeight - ((worldMapWidth / 2 * Math.log((1 + Math.sin(lat)) / (1 - Math.sin(lat)))) - mapOffsetY);
    System.out.println("y before minus: " + y);
    y = mapHeight - y;
} else {
    y = mapHeight / 2;
}
System.out.println(x);
System.out.println(y);

When using the original code if the latitude value is positive it returned a negative point, so I modified it slightly and tested with the extreme latitudes-which should be point 0 and point 766, it works fine. However when I try a different latitude value ex: 58.07 (just north of the UK) it displays as north of Spain.

Answer

Michel Feldheim picture Michel Feldheim · Jan 22, 2013

The Mercator map projection is a special limiting case of the Lambert Conic Conformal map projection with the equator as the single standard parallel. All other parallels of latitude are straight lines and the meridians are also straight lines at right angles to the equator, equally spaced. It is the basis for the transverse and oblique forms of the projection. It is little used for land mapping purposes but is in almost universal use for navigation charts. As well as being conformal, it has the particular property that straight lines drawn on it are lines of constant bearing. Thus navigators may derive their course from the angle the straight course line makes with the meridians. [1.]

Mercator projection

The formulas to derive projected Easting and Northing coordinates from spherical latitude φ and longitude λ are:

E = FE + R (λ – λₒ)
N = FN + R ln[tan(π/4 + φ/2)]   

where λO is the longitude of natural origin and FE and FN are false easting and false northing. In spherical Mercator those values are actually not used, so you can simplify the formula to

derivation of the mercator projection (wikipedia)

Pseudo code example, so this can be adapted to every programming language.

latitude    = 41.145556; // (φ)
longitude   = -73.995;   // (λ)

mapWidth    = 200;
mapHeight   = 100;

// get x value
x = (longitude+180)*(mapWidth/360)

// convert from degrees to radians
latRad = latitude*PI/180;

// get y value
mercN = ln(tan((PI/4)+(latRad/2)));
y     = (mapHeight/2)-(mapWidth*mercN/(2*PI));

Sources:

  1. OGP Geomatics Committee, Guidance Note Number 7, part 2: Coordinate Conversions and Transformation
  2. Derivation of the Mercator projection
  3. National Atlas: Map Projections
  4. Mercator Map projection

EDIT Created a working example in PHP (because I suck at Java)

https://github.com/mfeldheim/mapStuff.git

EDIT2

Nice animation of the Mercator projection https://amp-reddit-com.cdn.ampproject.org/v/s/amp.reddit.com/r/educationalgifs/comments/5lhk8y/how_the_mercator_projection_distorts_the_poles/?usqp=mq331AQJCAEoAVgBgAEB&amp_js_v=0.1