Google Maps - How to get the distance between two point in metre? - javascript

I have these coordinate :
(45.463688, 9.18814)
(46.0438317, 9.75936230000002)
and I need (trought Google API V3, I think) to get the distance between those 2 points in metre. How can I do it?

If you're looking to use the v3 google maps API, here is a function to use:
Note: you must add &libraries=geometry to your script source
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false&libraries=geometry"></script>
<script>
var p1 = new google.maps.LatLng(45.463688, 9.18814);
var p2 = new google.maps.LatLng(46.0438317, 9.75936230000002);
alert(calcDistance(p1, p2));
//calculates distance between two points in km's
function calcDistance(p1, p2) {
return (google.maps.geometry.spherical.computeDistanceBetween(p1, p2) / 1000).toFixed(2);
}
</script>

I think you could do without any specific API, and calculate distance with plain Javascript:
This site has good info about geographical calculations and Javascript sample for distance calculation.
Ok, quick glance at Google API page and it seems, you could do it by:
Call DirectionsService().route() to get DirectionsResult with routes
For one or each route go through its legs property and calculate sum of distances

http://www.csgnetwork.com/gpsdistcalc.html
Has nothing to do with coding btw
EDIT
if you're looking for some code
Calculate distance between two points in google maps V3

This is primarily done with math outside of the google api, so you shouldn't need to use the API for anything other than finding the correct coordinates.
Knowing that, here's a link to a previously answered question relating to Javascript.
Calculate distance between 2 GPS coordinates

Try this
CLLocationCoordinate2D coord1;
coord1.latitude = 45.463688;
coord1.longitude = 9.18814;
CLLocation *loc1 = [[[CLLocation alloc] initWithLatitude:coord1.latitude longitude:coord1.longitude] autorelease];
CLLocationCoordinate2D coord2;
coord2.latitude = 46.0438317;
coord2.longitude = 9.75936230000002;
CLLocation *loc2 = [[[CLLocation alloc] initWithLatitude:46.0438317 longitude:9.75936230000002] autorelease];
CLLocationDistance d1 = [loc1 distanceFromLocation:loc2];

Try this:
const const toRadians = (val) => {
return val * Math.PI / 180;
}
const toDegrees = (val) => {
return val * 180 / Math.PI;
}
// Calculate a point winthin a circle
// circle ={center:LatLong, radius: number} // in metres
const pointInsideCircle = (point, circle) => {
let center = circle.center;
let distance = distanceBetween(point, center);
//alert(distance);
return distance < circle.radius;
};
const distanceBetween = (point1, point2) => {
//debugger;
var R = 6371e3; // metres
var φ1 = toRadians(point1.latitude);
var φ2 = toRadians(point2.latitude);
var Δφ = toRadians(point2.latitude - point1.latitude);
var Δλ = toRadians(point2.longitude - point1.longitude);
var a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
Math.cos(φ1) * Math.cos(φ2) *
Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c;
}
References: http://www.movable-type.co.uk/scripts/latlong.html

Are you referring to distance as in length of the entire path or you want to know only the displacement (straight line distance)? I see no one is pointing the difference between distance and displacement here. For distance calculate each route point given by JSON/XML data, as for displacement there is a built-in solution using Spherical class.

Are you referring to distance as in length of the entire path or you want to know only the displacement (straight line distance)? I see no one is pointing the difference between distance and displacement here. For distance calculate each route point given by JSON/XML data, as for displacement there is a built-in solution using Spherical class. The idea is if your are referring to distance then you just use the computeDistanceBetween on each path point and concatenate it.
//calculates distance between two points in km's
function calcDistance(p1, p2) {
return (google.maps.geometry.spherical.computeDistanceBetween(p1, p2) / 1000).toFixed(2);
}

Related

Geolocation highly accurate at times but very wrong at other times

I'm using Ionic and geolocation to get the users coordinates. What I've found is that it's either really accurate or not at all.. Off by 10km or by 30 meters..
This first picture is the NOT accurate results:
The first lat/long is the user location and the second line under it is the lat/long of a landmark. The value below that is the meters away from each other.
As you can see from the first set, geolocation thinks I'm 11km away from this landmark, even though I am only 1km away.
Here is a different picture with the accurate results:
Much better, and using the ionic coords.accuracy returns 30 meters away for the accurate one, but something like 47000 for the non-accurate one. For both of these tests I was in the exact same position. The coordinates seem to change randomly every 10-20 minutes..
Here is my code:
let userLocation = {lat: 0, lng: 0}
this.geolocation.getCurrentPosition({enableHighAccuracy: true}).then((resp) => {
userLocation.lat = resp.coords.latitude;
userLocation.lng = resp.coords.longitude;
console.log(resp.coords.accuracy)
}).catch((error) => {
console.log('Error getting location', error);
});
getDistance(userLocation, {lat: 35.904912, lng: -79.046913}) //calling the function that returns the distance between each point in meters
var rad = function(x) {
return x * Math.PI / 180;
};
var getDistance = function(p1, p2) {
var R = 6378137; // Earth’s mean radius in meter
var dLat = rad(p2.lat - p1.lat);
var dLong = rad(p2.lng - p1.lng);
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(rad(p1.lat)) * Math.cos(rad(p2.lat)) *
Math.sin(dLong / 2) * Math.sin(dLong / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = R * c;
return d; // returns the distance in meter
};
Is there a way to make this more accurate? Or rather, just show the accurate one? The geolocation seems to randomly change every few minutes..
You can check the "accuracy" value that comes with the geolocation reading. It's in meters i.e. give or take. IOW draw a circle around the lat/lng with a radius of "accuracy" and you're somewhere in the circle.
If it is a new wifi point or telephone tower or you are inside then the accuracy will be lower than if you're outside with GPS. You are free to discard a reading if for example its accuracy is 2x the most accurate reading you've receive previously.
What I do Here is very similar but I also set mostAccurate back to high-values if I haven't had a valid GPS update for a while.

Converting longitude latitude to tile coordinates

I want to get a map tile from a server by typing in the longitude and latitude.
Since map tiles are arranged like a grid, i need to convert my longitude and latitude to an x and y position.
The algorithm is explained here: http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#ECMAScript_.28JavaScript.2FActionScript.2C_etc..29
I correctly implemented this algorithm, but unfortunately these are not the correct coordinates for the tileservers.
As you can see here http://tools.geofabrik.de/map/#12/52.5106/13.3989&type=Geofabrik_Standard&grid=1
the correct tilecoordinates for Berlin are (2200, 1343) on zoomlevel 12, while the algorithm gives me (2645, 1894).
Where is the mistake in the algorithm or my misunderstanding of how this conversion works?
Tilesname WebCalc seems to use the same code as presented on the slippy map tilenames wiki page and outputs the same tile names as the Geofabrik tool. So the algorithm must be correct and the error seems to be in your implementation which you didn't show us.
Oh, I'm pretty sure you just mixed up lat and lon. If I enter the coordinates in the wrong order into Tilesname WebCalc then it also returns the "wrong" tile names given in your question. So your code is fine, you just call it the wrong way.
The following code is takes lng, lat & zoom and return X & Y values and Z is the zoom. you need to put it in url and voila you get the tile
const EARTH_RADIUS = 6378137;
const MAX_LATITUDE = 85.0511287798;
function project (lat, lng)
{
var d = Math.PI / 180,
max = MAX_LATITUDE,
lat = Math.max(Math.min(max, lat), -max),
sin = Math.sin(lat * d);
return {x: EARTH_RADIUS * lng * d,
y: EARTH_RADIUS * Math.log((1 + sin) / (1 - sin)) / 2
};
}
function zoomScale (zoom)
{
return 256 * Math.pow(2, zoom);
}
function transform (point, scale) {
scale = scale || 1;
point.x = scale * (2.495320233665337e-8 * point.x + 0.5);
point.y = scale * (-2.495320233665337e-8 * point.y + 0.5);
return point;
}
var point1 = project (lat1, lng1);
var scaledZoom = zoomScale (zoom);
point1 = transform (point1, scaledZoom);
point1 is what you need the X & Y

Animated directional arrows "aroundMe" style using ngCordova

I wish to create the compass / arrow exactly like the one we see in AroundMe Mobile App that point exactly to a pin to the map accordingly with my mobile position and update the arrow when I move the phone.
I'm getting crazy to understand exactly how to do that and I can not find any guide or tutorial that explain a bit it.
What I found online is a bearing function and I created a directive around it:
app.directive('arrow', function () {
function bearing(lat1, lng1, lat2, lng2) {
var dLon = (lng2 - lng1);
var y = Math.sin(dLon) * Math.cos(lat2);
var x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
var rad = Math.atan2(y, x);
var brng = toDeg(rad);
return (brng + 360) % 360;
}
function toRad(deg) {
return deg * Math.PI / 180;
}
function toDeg(rad) {
return rad * 180 / Math.PI;
}
return {
restrict: 'E',
link: function (scope, element, attrs) {
var arrowAngle = bearing(scope.user.position.lat, scope.user.position.lng, attrs.lat, attrs.lng);
element.parent().css('transform', 'rotate(' + arrowAngle + 'deg)');
}
};
});
It seems to update the arrow in a direction but unfortunately it is not the right direction because it is not calculated using also the mobile magneticHeading position.
So I added the ngCordova plugin for Device Orientation to get the magneticHeading and now I don't know exactly how to use it and where in the bearing function.
$cordovaDeviceOrientation.getCurrentHeading().then(function(result) {
var magneticHeading = result.magneticHeading;
var arrowAngle = bearing(scope.user.position.lat, scope.user.position.lng, attrs.lat, attrs.lng, magneticHeading);
element.parent().css('transform', 'rotate(' + arrowAngle + 'deg)');
});
I tried to add it in the return statement:
return (brng - heading) % 360;
or:
return (heading - ((brng + 360) % 360));
Implementing this code with a watcher I see the arrow moving but not in the exact position... For example from my position and the pin the arrow should point to N and it is pointing to E.
Always looking online I can not find any tutorial / question to find the bearing between a lat/lng point and a magnetingHeading.
Maybe I'm close to the solution but I can not go ahead alone.
I also tried to search for a mathematical formulas but even there is a huge pain to understand and implement it.
I hope you can help.
It's hard to give a plain answer to this question because a lot depends on the actual graphical representation. For instance, in what direction do you point where rotate(0deg).
I can explain the formula you've found, which might help you to clear the issue yourself. The hard part is the following:
var dLon = (lng2 - lng1);
var y = Math.sin(dLon) * Math.cos(lat2);
var x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
var rad = Math.atan2(y, x);
What you see here is Haversines formula (https://en.wikipedia.org/wiki/Haversine_formula). Normally one could suffice with two sets of coordinates and calculate the angle between them. When working with latitude and longitude this will not work because the earth is not a flat surface. The first three lines are Haversine and the result (x and y) are coordinates on the unit circle (https://en.wikipedia.org/wiki/Unit_circle)
The next step is to calculate the angle from the point on this unit circle to the center. We can use the arctangant for this. Javascript has a helper function atan2 which simplifies this process. The result is simple the angle of your point towards the center of the circle. In other words, your position towards your point of interest. The result is in radians and needs to be converted to degrees. (https://en.wikipedia.org/wiki/Atan2)
A simplified version with a flat coordinate system would look like this:
var deltaX = poi.x - you.x;
var deltaY = you.y - poi.y;
var rotation = toDeg(Math.atan2(deltaX, deltaY));
bearingElement.css('transform', 'rotate(' + rotation + 'deg)');
Where poi is the Point of Interest and you is your position.
To compensate for your own rotation, you need to substract your own rotation. In the above sample the result would become:
var deltaX = poi.x - you.x;
var deltaY = you.y - poi.y;
var rotation = toDeg(Math.atan2(deltaX, deltaY));
rotation -= you.rotation;
bearingElement.css('transform', 'rotate(' + rotation + 'deg)');
I've made a Plunckr in which you can see a simple flat coordinate system. You can move and rotate you and the point of interest. The arrow inside 'you' will always point towards the poi, even if you rotate 'you'. This is because we compensate for our own rotation.
https://plnkr.co/edit/OJBGWsdcWp3nAkPk4lpC?p=preview
Note in the Plunckr that the 'zero'-position is always to the north. Check your app to see your 'zero'-position. Adjust it accordingly and your script will work.
Hope this helps :-)

googlemaps get bounding box around marker

In googlemaps api v2 I have one marker on map and i need to calculate a bounding box around this one. How would I get a bonding box of 5 by 5 kilometers of which this marker is the center?
I'm not sure that such a functionality is provided by google map, but math will help you to survive ;) Calculate distance, bearing and more between Latitude/Longitude points is a great reference to different calculations with geographic data. Open that page, and go to "Destination point given distance and bearing from start point" part, there are formulas, as well as online calculator, so you can check them (as well as you can see points on the map). Formula has few parameters:
(lat1,lng1) - your point (marker coordinates)
d - distance (in your case it would be 2.5km)
brng - angle..
to find bound you need to find coordinates of south, north, east and west rectangle sides, so everything you will change in parameters is angle, and in your case it will be 0, 90, 180 and 270 grads. Formulas:
var lat2 = Math.asin( Math.sin(lat1)*Math.cos(d/R) +
Math.cos(lat1)*Math.sin(d/R)*Math.cos(brng) );
var lon2 = lon1 + Math.atan2(Math.sin(brng)*Math.sin(d/R)*Math.cos(lat1),
Math.cos(d/R)-Math.sin(lat1)*Math.sin(lat2));
Well, specifying angle = 0 you find north, PI/2 - east, PI - south, 3*PI/2 - west (angles should be passed in radians).
R = earth’s radius (mean radius = 6,371km)
ADD-ON: just looked at the source code of that page, because when I enter in online form bearing = 0, distance = 2 then I see map with two points and according to the map scale the distance between them is really 2km. Well, you can use this library under a simple attribution license, without any warranty express or implied, just include it
<script src="http://www.movable-type.co.uk/scripts/latlon.js"></script>
Function you need is:
/**
* Returns the destination point from this point having travelled the given distance (in km) on the
* given initial bearing (bearing may vary before destination is reached)
*
* see http://williams.best.vwh.net/avform.htm#LL
*
* #param {Number} brng: Initial bearing in degrees
* #param {Number} dist: Distance in km
* #returns {LatLon} Destination point
*/
LatLon.prototype.destinationPoint = function(brng, dist) {
dist = typeof(dist)=='number' ? dist : typeof(dist)=='string' && dist.trim()!='' ? +dist : NaN;
dist = dist/this._radius; // convert dist to angular distance in radians
brng = brng.toRad(); //
var lat1 = this._lat.toRad(), lon1 = this._lon.toRad();
var lat2 = Math.asin( Math.sin(lat1)*Math.cos(dist) +
Math.cos(lat1)*Math.sin(dist)*Math.cos(brng) );
var lon2 = lon1 + Math.atan2(Math.sin(brng)*Math.sin(dist)*Math.cos(lat1),
Math.cos(dist)-Math.sin(lat1)*Math.sin(lat2));
lon2 = (lon2+3*Math.PI)%(2*Math.PI) - Math.PI; // normalise to -180...+180
return new LatLon(lat2.toDeg(), lon2.toDeg());
}
and when I enter in online form bearing = 0, distance = 2, this method is executed with arguments latLonCalc.destinationPoint( 0, "2" );
try it, otherwise give me input parameters so I could check what's wrong
UPDATE2 that library works with grads,and converts them to radians for calculations and then back to grads. Just performed simple test:
var latLonCalc = new new LatLon( 25, 45 );
var point = latLonCalc.destinationPoint( 0, "2" );
console.info( point );
// prints 25°01′05″N, 045°00′00″E { _lat=25.01798643211838, _lon=45.00000000000005, _radius=6371}
so the distance between entry point and final destination is a bit more than 1 minute;
earth = 2 * PI * 6371; // 40 009.98km
=> 40 009.98km / 360grad =~111.14
=> 111,14 / 60 = 1.85 (km/minute) ~2km
it was round calculation, which tells me that final point is not far away then entry point, and distance should be 2km ;)
Not the real one you required but check this article
http://www.svennerberg.com/2008/11/bounding-box-in-google-maps/

Mercator longitude and latitude calculations to x and y on a cropped map (of the UK)

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
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:
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);
}
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
}
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};
}
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.
I know the question was asked a while ago, but the Proj4JS library is ideal for transforming between different map projections in JavaScript.
UK maps tend to use the OSGB's National Grid which is based on a Transverse Mercator projection. Ie. like a conventional Mercator but turned 90 degrees, so that the "equator" becomes a meridian.
#Xarinko Actionscript snippet in Javascript (with some testing values)
var mapWidth = 1500;
var mapHeight = 1577;
var mapLonLeft = 9.8;
var mapLonRight = 10.2;
var mapLonDelta = mapLonRight - mapLonLeft;
var mapLatBottom = 53.45;
var mapLatBottomRadian = mapLatBottom * Math.PI / 180;
function convertPixelToGeo(tx, ty)
{
/* 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 = mapWidth / mapLonDelta * 360/(2 * Math.PI);
var mapOffsetY = ( worldMapRadius / 2 * Math.log( (1 + Math.sin(mapLatBottomRadian) ) / (1 - Math.sin(mapLatBottomRadian)) ));
var equatorY = mapHeight + mapOffsetY;
var a = (equatorY-ty)/worldMapRadius;
var lat = 180/Math.PI * (2 * Math.atan(Math.exp(a)) - Math.PI/2);
var long = mapLonLeft+tx/mapWidth*mapLonDelta;
return [lat,long];
}
convertPixelToGeo(241,444)
C# implementation:
private Point ConvertGeoToPixel(
double latitude, double longitude, // The coordinate to translate
int imageWidth, int imageHeight, // The dimensions of the target space (in pixels)
double mapLonLeft, double mapLonRight, double mapLatBottom // The bounds of the target space (in geo coordinates)
) {
double mapLatBottomRad = mapLatBottom * Math.PI / 180;
double latitudeRad = latitude * Math.PI / 180;
double mapLonDelta = mapLonRight - mapLonLeft;
double worldMapWidth = (imageWidth / mapLonDelta * 360) / (2 * Math.PI);
double mapOffsetY = worldMapWidth / 2 * Math.Log((1 + Math.Sin(mapLatBottomRad)) / (1 - Math.Sin(mapLatBottomRad)));
double x = (longitude - mapLonLeft) * (imageWidth / mapLonDelta);
double y = imageHeight - ((worldMapWidth / 2 * Math.Log((1 + Math.Sin(latitudeRad)) / (1 - Math.Sin(latitudeRad)))) - mapOffsetY);
return new Point()
{
X = Convert.ToInt32(x),
Y = Convert.ToInt32(y)
};
}
If you want to avoid some of the messier aspects of lat/lng projections intrinsic to Proj4JS, you can use D3, which offers many baked-in projections and renders beautifully. Here's an interactive example of several flavors of Azimuthal projections. I prefer Albers for USA maps.
If D3 is not an end-user option -- say, you need to support IE 7/8 -- you can render in D3 and then snag the xy coordinates from the resultant SVG file that D3 generates. You can then render those xy coordinates in Raphael.
This function works great for me because I want to define the mapHeight based on the map I want to plot. I'm generating PDF maps. All I need to do is pass in the map's max Lat , min Lon and it returns the pixels size for the map as [height,width].
convertGeoToPixel(maxlatitude, maxlongitude)
One note in the final step where $y is set, do not subtract the calculation from the mapHeight if your coordinate system 'xy' starts at the bottom/left , like with PDFs, this will invert the map.
$y = (($worldMapWidth / 2 * log((1 + sin($lat)) / (1 - sin($lat)))) - $mapOffsetY);

Categories