I'm trying to find a function lng = f(lat) that would help me draw a line between 2 given GPS coordinates, (lat1, lng1) and (lat2, lng2).
I've tried the traditional Cartesian formula y=mx+b where m=(y2-y1)/(x2-x1), but GPS coordinates don't seem to behave that way.
What would be a formula/algorithm that could help me achieve my goal.
PS: I'm using Google Maps API but let's keep this implementation agnostic if possible.
UPDATE: My implementation was wrong and it seems the algorithm is actually working as stated by some of the answers. My bad :(
What you want to do should actually work. Keep in mind however that if north is on top, the horizontal (x) axis is the LONGITUDE and the vertical (y) axis is the LATITUDE (I think you might have confused this).
If you parametrize the line as lat = func(long) you will run into trouble with vertical lines (i.e. those going exactly north south) as the latitude varies while the longitude is fixed.
Therefore I'd rather use another parametrization:
long(alpha) = long_1 + alpha * (long_2 - long_1)
lat(alpha) = lat_1 + alpha * (lat_2 - lat_1)
and vary alpha from 0 to 1.
This will not exactly coincide with a great circle (shortest path on a sphere) but the smaller the region you are looking at, the less noticeable the difference will be (as others posters here pointed out).
Here is a distance formula I use that may help. This is using javascript.
function Distance(lat1, lat2, lon1, lon2) {
var R = 6371; // km
var dLat = toRad(lat2 - lat1);
var dLon = toRad(lon2 - lon1);
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = R * c * 0.621371;
var r = Math.round(d * 100) / 100;
return r;
}
For short distances, where the earth curvature doesn't make a significant difference, it works fine to draw a line using regular two-dimensional geometry.
For longer distances the shortest way between two lines does not project as a straight line on a map, but as a curve. (For example, the shortest way from Sweden to Alaska would be straight over the noth pole, not past Canada and Iceland.) You would have to use three-dimensional geometry to draw a line on a surface of a sphere, then project that onto the map in the same way the earth surface is projected on the map.
Is your goal to find this equation or to actually draw a line?
If the latter, since you're using the Maps API, specify geodesic: true and draw it with a Polyline:
http://code.google.com/apis/maps/documentation/javascript/reference.html#Polyline
Related
I'm working on a project that hopes to convert Google maps static images into larger, stitched together maps.
I have created an algorithm that given a starting and ending, latitude and longitude, it will fetch the Google Maps Static image for that lat,lng pair, then increment lng by the pixel width of the fetched image, in this case 700px, using an algorithm for determining pixel to longitude ratio using a couple of formulas.
The formulas seem to be working perfectly for latitude, as you can see in my attachment, the vertically tiling images line up almost perfectly.
Yet horizontally, the longitude increment seems to be off by a factor of 2, or slightly more.
To increment latitude, I use a constant metres to latitude ratio
const metresToLatRatio = 0.001 / 111 // Get lat value from metres
const metresPerPxLat = getMetresPerPxLng(currentLat, zoom)
const latIncrement = metresToLatRatio * (metresPerPxLat * 700)
But for longitude, I replaces the metresToLngRatio constant with a variable derived from this formula
const metresToLngRatio = getMetresToLngRatio(currentLat)
const metresPerPxLng = getMetresPerPxLng(currentLat, zoom)
lngIncrement = (metresToLngRatio * (metresPerPxLng * 700))
Where getMetresToLngRatio and getMetresPerPxLng are
function getMetresPerPxLng(lat, zoom = 19, scale = 2) {
return Math.abs((156543.03392 * Math.cos(lat * Math.PI / 180) / (2 ** zoom)) / scale)
}
function getMetresToLngRatio(lat) {
return 1 / Math.abs(111111 * Math.cos(lat))
}
The getMetresPerPxLng function is derived from this post and this answer: https://groups.google.com/g/google-maps-js-api-v3/c/hDRO4oHVSeM / https://gis.stackexchange.com/questions/7430/what-ratio-scales-do-google-maps-zoom-levels-correspond-to
What I noticed is that if I change getMetresToLng Ratio to return (1 / Math.abs(111111 * Math.cos(lat))) * 2, the stitching appears more accurate, only off by a few tens of pixels, instead of almost half the image.
With * 2
Without * 2
Am I doing something wrong with my longitude equation? I know that 111111*cos(lat) is a rough estimate, so I'm wondering if there's a more accurate formula
You are using the wrong earth radius. From the answer in https://gis.stackexchange.com/questions/7430/what-ratio-scales-do-google-maps-zoom-levels-correspond-to, you need to update the equation using a radius of 6371010 instead of 6378137 meters.
Please note that stitching tiles together for some other use is likely a violation of the Terms of Service.
I am trying to calculate bearing between two lat/lon points as given in this link. I see that the bearing we get initially using the below equation is initial bearing.
public static double GetBearing(double latitude1, double longitude1, double latitude2, double longitude2)
{
var lat1 = ToRadians(latitude1);
var lat2 = ToRadians(latitude2);
var longdiff = ToRadians(longitude1 - longitude2);
var X = Math.Cos(lat2) * Math.Sin(longdiff);
var Y = Math.Cos(lat1) * Math.Sin(lat2) - Math.Sin(lat1) * Math.Cos(lat2) * Math.Cos(longdiff);
var bearing =ToDegrees(Math.Atan2(X, Y));
return (bearing+360)%360;
}
It is given that
For final bearing, simply take the initial bearing from the end point to the start point and reverse it (using θ = (θ+180) % 360).
I am confused about the difference between initial bearing and final bearing.
What is this initial and final bearing and which bearing should we take as the final answer for bearing between two points.
The bearing is the angle between direction along the shortest path to destination and direction to North. The reason we have initial and final one is that we live on sphere, so the shortest path is geodesic line. It is a straight line on globe, bit if you draw it on flat map - it will be a curve.
There are two ways to think about it. Thinking on flat map: as you travel from A to B, this curve changes direction slightly, so the angle between this line and North changes, i.e. bearing changes.
Or you can think on sphere, and then think about triangle A - B - North Pole. The bearing is angle between between AB and appropriate meridian. Initial bearing is angle between AB and meridian crossing A. Final one is angle between AB and meridian crossing B. They are different.
The single "final answer" bearing only makes sense when distance between A and B is short. Then the curvature of Earth does not matter much, and the initial and final bearings are very close to each other, so depending on precision needed one can talk about single bearing.
FYI: bearing and many related computations are implemented in the R package geosphere
The bearing function returns the initial bearing, but you can invert the coordinates to get the final bearing.
library(geosphere)
bearing(cbind(0,0),cbind(20,20))
#[1] 43.4035
finalb <- bearing(cbind(20,20),cbind(0,0))
(finalb + 180) %% 360
#[1] 46.9656
(these results should be more precise than the ones you get with algorithm you refer to)
def bearing(lat1, lon1, lat2, lon2):
# Convert latitude and longitude to radians
lat1 = math.radians(lat1)
lon1 = math.radians(lon1)
lat2 = math.radians(lat2)
lon2 = math.radians(lon2)
y = math.sin(lon2-lon1) * math.cos(lat2)
x = math.cos(lat1)*math.sin(lat2) - math.sin(lat1)*math.cos(lat2)*math.cos(lon2-lon1)
initial_bearing = math.degrees(math.atan2(y, x))
final_bearing = (initial_bearing + 180) % 360 if initial_bearing < 180 else (initial_bearing - 180) % 360
return initial_bearing, final_bearing
I have pairs of gps coordinates (longitude latitude) and I would like to calculate the walking distance between them. i.e. using road data (from google maps or another open source) calculate the km of the shortest route between the two gps points. I could do it using google maps, but I have thousands of pairs so I would like to find a more automated way.
Does somebody know how to do it?
I am not quite sure about what you looking for. Just share some thoughts here:
1) If you want to calculate great circle distance between two points in lat/lon, you could use haversine formula distance. Example in JS:
function Haversine_distance(lat1,lon1,lat2,lon2) {
var R = 6371; // in km
var x1 = lat2 - lat1;
var dLat = x1.toRad();
var x2 = lon2 - lon1;
var dLon = x2.toRad();
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = R * c;
return d*1.0;
};
2) If you need more accurate distance calculation you need some correction factor, since earth is not a perfer sphere. It is always easier to project the locations you have to an appropriate projection and calculate distance there. For instance, project to UTM zones using proj4js, then calculate the distance to reduce the inaccuracy.
3) If you are talking about walking distance in cities, then it is network distance. It is required to have your road network build up first, then calculate from there. Without the road network, giving only point locations will not be enough to calculate the walking distance. Commercial data for road network is available from such as TeleAtlas. Free data can also be found via OpenStreetMap.
Ok pretty self explanatory. I'm using google maps and I'm trying to find out if a lat,long point is within a circle of radius say x (x is chosen by the user).
Bounding box will not work for this. I have already tried using the following code:
distlatLng = new google.maps.LatLng(dist.latlng[0],dist.latlng[1]);
var latLngBounds = circle.getBounds();
if(latLngBounds.contains(distlatLng)){
dropPins(distlatLng,dist.f_addr);
}
This still results in markers being places outside the circle.
I'm guess this is some simple maths requiring the calculation of the curvature or an area but I'm not sure where to begin. Any suggestions?
Unfortunately Pythagoras is no help on a sphere. Thus Stuart Beard's answer is incorrect; longitude differences don't have a fixed ratio to metres but depend on the latitude.
The correct way is to use the formula for great circle distances. A good approximation, assuming a spherical earth, is this (in C++):
/** Find the great-circle distance in metres, assuming a spherical earth, between two lat-long points in degrees. */
inline double GreatCircleDistanceInMeters(double aLong1,double aLat1,double aLong2,double aLat2)
{
aLong1 *= KDegreesToRadiansDouble;
aLat1 *= KDegreesToRadiansDouble;
aLong2 *= KDegreesToRadiansDouble;
aLat2 *= KDegreesToRadiansDouble;
double cos_angle = sin(aLat1) * sin(aLat2) + cos(aLat1) * cos(aLat2) * cos(aLong2 - aLong1);
/*
Inaccurate trig functions can cause cos_angle to be a tiny amount
greater than 1 if the two positions are very close. That in turn causes
acos to give a domain error and return the special floating point value
-1.#IND000000000000, meaning 'indefinite'. Observed on VS2008 on 64-bit Windows.
*/
if (cos_angle >= 1)
return 0;
double angle = acos(cos_angle);
return angle * KEquatorialRadiusInMetres;
}
where
const double KPiDouble = 3.141592654;
const double KDegreesToRadiansDouble = KPiDouble / 180.0;
and
/**
A constant to convert radians to metres for the Mercator and other projections.
It is the semi-major axis (equatorial radius) used by the WGS 84 datum (see http://en.wikipedia.org/wiki/WGS84).
*/
const int32 KEquatorialRadiusInMetres = 6378137;
Use Google Maps API geometry library to calculate distance between circle's center and your marker, and then compare it with your radius.
var pointIsInsideCircle = google.maps.geometry.spherical.computeDistanceBetween(circle.getCenter(), point) <= circle.getRadius();
It's very simple. You just have to calculate distance between centre and given point and compare it to radius. You can Get Help to calculate distance between two lat lang from here
The following code works for me: my marker cannot be dragged outside the circle, instead it just hangs at its edge (in any direction) and the last valid position is preserved.
The function is the eventhandler for the markers 'drag' event.
_markerDragged : function() {
var latLng = this.marker.getPosition();
var center = this.circle.getCenter();
var radius = this.circle.getRadius();
if (this.circleBounds.contains(latLng) &&
(google.maps.geometry.spherical.computeDistanceBetween(latLng, center) <= radius)) {
this.lastMarkerPos = latLng;
this._geocodePosition(latLng);
} else {
// Prevent dragging marker outside circle
// see (comments of) http://unserkaiser.com/code/google-maps-marker-check-if-in-circle/
// see http://www.mvjantzen.com/blog/?p=3190 and source code of http://mvjantzen.com/cabi/trips4q2012.html
this.marker.setPosition(this.lastMarkerPos);
}
},
Thanks to http://unserkaiser.com/code/google-maps-marker-check-if-in-circle/
and http://www.mvjantzen.com/blog/?p=3190 .
I've been a bit silly really. Thinking about it we can use Pythagorus' theorem.
We have a maximum distance away from a point (X miles), and two latitudes and two longitudes. If we form a triangle using these then we can solve for the distance from the point.
So say we know point1 with coordinates lat1,lng1 is the center of the circle and point2 with coordinates lat2,lng2 is the point we are trying to decide is in the circle or not.
We form a right angled triangle using a point determined by point1 and point2. This, point3 would have coordinates lat1,lng2 or lat2,lng1 (it doesn't matter which). We then calculate the differences (or if you prefer) distances - latDiff = lat2-lat1 and lngDiff = lng2-lng1
we then calculate the distance from the center using Pythagorus - dist=sqrt(lngDiff^2+latDiff^2).
We have to translate everything into meters so that it works correctly with google maps so miles are multiplied by 1609 (approx) and degrees of latitude/longitude by 111000 (approx). This isn't exactly accurate but it does an adequate job.
Hope that all makes sense.
I'm constructing a geolocation based application and I'm trying to figure out a way to make my application realise when a user is facing the direction of the given location (a particular long / lat co-ord). I've got the math figured, I just have the triangle to construct.
//UPDATE
So I've figured out a good bit of this...
Below is a method which takes in a long / lat value and attempts to compute a triangle finding a point 700 meters away and one to its left + right. It'd then use these to construct the triangle. It computes the correct longitude but the latitude ends up somewhere off the coast of east Africa. (I'm in Ireland!).
public void drawtri(double currlng,double currlat, double bearing){
bearing = (bearing < 0 ? -bearing : bearing);
System.out.println("RUNNING THE DRAW TRIANGLE METHOD!!!!!");
System.out.println("CURRENT LNG" + currlng);
System.out.println("CURRENT LAT" + currlat);
System.out.println("CURRENT BEARING" + bearing);
//Find point X(x,y)
double distance = 0.7; //700 meters.
double R = 6371.0; //The radius of the earth.
//Finding X's y value.
Math.toRadians(currlng);
Math.toRadians(currlat);
Math.toRadians(bearing);
distance = distance/R;
Global.Alat = Math.asin(Math.sin(currlat)*Math.cos(distance)+
Math.cos(currlat)*Math.sin(distance)*Math.cos(bearing));
System.out.println("CURRENT ALAT!!: " + Global.Alat);
//Finding X's x value.
Global.Alng = currlng + Math.atan2(Math.sin(bearing)*Math.sin(distance)
*Math.cos(currlat), Math.cos(distance)-Math.sin(currlat)*Math.sin(Global.Alat));
Math.toDegrees(Global.Alat);
Math.toDegrees(Global.Alng);
//Co-ord of Point B(x,y)
// Note: Lng = X axis, Lat = Y axis.
Global.Blat = Global.Alat+ 00.007931;
Global.Blng = Global.Alng;
//Co-ord of Point C(x,y)
Global.Clat = Global.Alat - 00.007931;
Global.Clng = Global.Alng;
}
From debugging I've determined the problem lies with the computation of the latitude done here..
Global.Alat = Math.asin(Math.sin(currlat)*Math.cos(distance)+
Math.cos(currlat)*Math.sin(distance)*Math.cos(bearing));
I have no idea why though and don't know how to fix it. I got the formula from this site..
http://www.movable-type.co.uk/scripts/latlong.html
It appears correct and I've tested multiple things...
I've tried converting to Radians then post computations back to degrees, etc. etc.
Anyone got any ideas how to fix this method so that it will map the triangle ONLY 700 meters in from my current location in the direction that I am facing?
Thanks,
for long distance: http://www.dtcenter.org/met/users/docs/write_ups/gc_simple.pdf
but for short distance You can try simple 2d math to simulate "classic" compass using: http://en.wikipedia.org/wiki/Compass#Using_a_compass. For example you can get pixel coordinates from points A and B and find angle between line connecting those points and vertical line.
also You probably should consider magnetic declination: http://www.ngdc.noaa.gov/geomagmodels/Declination.jsp
//edit:
I was trying to give intuitive solution. However calculating screen coordinates from long/lat wouldn't be easy so You probably should use formulas provided in links.
Maybe its because I don't know javascript, but don't you have to do something like
currlat = Math.toRadians(currlat);
to actually change the currlat value to be radians.
Problem was no matter what I piped in java would output in Radians, Trick was to change everything to Radians and then output came in radians, convert to degrees.