Mercator longitude and latitude calculations to x and y on a cropped map (of the UK)
Solution 1
I think it's worthwhile to keep in mind that not all flat maps are Mercator projections. Without knowing more about that map in particular, it's hard to be sure. You may find that most maps of a small area of the world are more likely to be a conical type projection, where the area of interest on the map is "flatter" than would be on a global Mercator projection. This is especially more important the further you get away from the equator (and the UK is far enough away for it to matter).
You may be able to get "close enough" using the calculations you're trying, but for best accuracy you may want to either use a map with a well-defined projection, or create your own map.
Solution 2
I wrote a function which does exactly what you were looking for. I know it's a bit late, but maybe there are some other people interested in.
You need a map which is a mercator projection and you need to know the lat / lon positions of your map. You get great customized mercator maps with perfect matching lat / lon positions from TileMill which is a free software from MapBox!
I'm using this script and tested it with some google earth positions. It worked perfect on a pixel level. Actually I didnt test this on different or larger maps. I hope it helps you!
Raphael ;)
<?php
$mapWidth = 1500;
$mapHeight = 1577;
$mapLonLeft = 9.8;
$mapLonRight = 10.2;
$mapLonDelta = $mapLonRight - $mapLonLeft;
$mapLatBottom = 53.45;
$mapLatBottomDegree = $mapLatBottom * M_PI / 180;
function convertGeoToPixel($lat, $lon)
{
global $mapWidth, $mapHeight, $mapLonLeft, $mapLonDelta, $mapLatBottom, $mapLatBottomDegree;
$x = ($lon - $mapLonLeft) * ($mapWidth / $mapLonDelta);
$lat = $lat * M_PI / 180;
$worldMapWidth = (($mapWidth / $mapLonDelta) * 360) / (2 * M_PI);
$mapOffsetY = ($worldMapWidth / 2 * log((1 + sin($mapLatBottomDegree)) / (1 - sin($mapLatBottomDegree))));
$y = $mapHeight - (($worldMapWidth / 2 * log((1 + sin($lat)) / (1 - sin($lat)))) - $mapOffsetY);
return array($x, $y);
}
$position = convertGeoToPixel(53.7, 9.95);
echo "x: ".$position[0]." / ".$position[1];
?>
Here is the image I created with TileMill and which I used in this example:
Solution 3
In addition to what Raphael Wichmann has posted (Thanks, by the way!), here is the reverse function, in actionscript :
function convertPixelToGeo(tx:Number, ty:Number):Point
{
/* called worldMapWidth in Raphael's Code, but I think that's the radius since it's the map width or circumference divided by 2*PI */
var worldMapRadius:Number = mapWidth / mapLonDelta * 360/(2 * Math.PI);
var mapOffsetY:Number = ( worldMapRadius / 2 * Math.log( (1 + Math.sin(mapLatBottomRadian) ) / (1 - Math.sin(mapLatBottomRadian)) ));
var equatorY:Number = mapHeight + mapOffsetY;
var a:Number = (equatorY-ty)/worldMapRadius;
var lat:Number = 180/Math.PI * (2 * Math.atan(Math.exp(a)) - Math.PI/2);
var long:Number = mapLonLeft+tx/mapWidth*mapLonDelta;
return new Point(lat,long);
}
Solution 4
Here's another Javascript implementation. This is a simplification of @Rob Willet's solution above. Instead of requiring computed values as parameters to the function, it only requires essential values and computes everything from them:
function convertGeoToPixel(latitude, longitude,
mapWidth, // in pixels
mapHeight, // in pixels
mapLngLeft, // in degrees. the longitude of the left side of the map (i.e. the longitude of whatever is depicted on the left-most part of the map image)
mapLngRight, // in degrees. the longitude of the right side of the map
mapLatBottom) // in degrees. the latitude of the bottom of the map
{
const mapLatBottomRad = mapLatBottom * Math.PI / 180
const latitudeRad = latitude * Math.PI / 180
const mapLngDelta = (mapLngRight - mapLngLeft)
const worldMapWidth = ((mapWidth / mapLngDelta) * 360) / (2 * Math.PI)
const mapOffsetY = (worldMapWidth / 2 * Math.log((1 + Math.sin(mapLatBottomRad)) / (1 - Math.sin(mapLatBottomRad))))
const x = (longitude - mapLngLeft) * (mapWidth / mapLngDelta)
const y = mapHeight - ((worldMapWidth / 2 * Math.log((1 + Math.sin(latitudeRad)) / (1 - Math.sin(latitudeRad)))) - mapOffsetY)
return {x, y} // the pixel x,y value of this point on the map image
}
Solution 5
I've converted the PHP code provided by Raphael to JavaScript and can confirm it worked and this code works myself. All credit to Raphael.
/*
var mapWidth = 1500;
var mapHeight = 1577;
var mapLonLeft = 9.8;
var mapLonRight = 10.2;
var mapLonDelta = mapLonRight - mapLonLeft;
var mapLatBottom = 53.45;
var mapLatBottomDegree = mapLatBottom * Math.PI / 180;
*/
function convertGeoToPixel(latitude, longitude ,
mapWidth , // in pixels
mapHeight , // in pixels
mapLonLeft , // in degrees
mapLonDelta , // in degrees (mapLonRight - mapLonLeft);
mapLatBottom , // in degrees
mapLatBottomDegree) // in Radians
{
var x = (longitude - mapLonLeft) * (mapWidth / mapLonDelta);
latitude = latitude * Math.PI / 180;
var worldMapWidth = ((mapWidth / mapLonDelta) * 360) / (2 * Math.PI);
var mapOffsetY = (worldMapWidth / 2 * Math.log((1 + Math.sin(mapLatBottomDegree)) / (1 - Math.sin(mapLatBottomDegree))));
var y = mapHeight - ((worldMapWidth / 2 * Math.log((1 + Math.sin(latitude)) / (1 - Math.sin(latitude)))) - mapOffsetY);
return { "x": x , "y": y};
}
betamax
Updated on July 09, 2022Comments
-
betamax almost 2 years
I have this image. It's a map of the UK (not including Southern Ireland):
I have successfully managed to get a latitude and longitude and plot it onto this map by taking the leftmost longitude and rightmost longitude of the UK and using them to work out where to put the point on the map.
This is the code (for use in Processing.js but could be used as js or anything):
// Size of the map int width = 538; int height = 811; // X and Y boundaries float westLong = -8.166667; float eastLong = 1.762833; float northLat = 58.666667; float southLat = 49.95; void drawPoint(float latitude, float longitude){ fill(#000000); x = width * ((westLong-longitude)/(westLong-eastLong)); y = (height * ((northLat-latitude)/(northLat-southLat))); console.log(x + ", " + y); ellipseMode(RADIUS); ellipse(x, y, 2, 2); }
However, I haven't been able to implement a Mercator projection on these values. The plots are reasonably accurate but they are not good enough and this projection would solve it.
I can't figure out how to do it. All the examples I find are explaining how to do it for the whole world. This is a good resource of examples explaining how to implement the projection but I haven't been able to get it to work.
Another resource is the Extreme points of the United Kingdom where I got the latitude and longitude values of the bounding box around the UK. They are also here:
northLat = 58.666667; northLong = -3.366667; eastLat = 52.481167; eastLong = 1.762833; southLat = 49.95; southLong = -5.2; westLat = 54.45; westLong = -8.166667;
If anyone could help me with this, I would greatly appreciate it!
Thanks