MySQL function ST_Distance_Sphere isn't using Haversine formula? - mysql

I'm trying to calculate distances between two geographic points using MySQL, but I think ST_Distance_Sphere function is incorrect or is not using Haversine formula.
To describe this problem, I'll use these two points and this Earth radius:
Point A: 41.8902142, 12.4900422
Point B: 48.8583736, 2.2922926
Earth radius: 6378000
I've made this script in JavaScript that uses Haversine formula:
Number.prototype.toRad = function() {
return this * Math.PI / 180;
}
const sin2 = (x) => {
return Math.pow( Math.sin(x), 2 );
}
function computeDistanceBetween(x1, y1, x2, y2, rad = 6378000) {
const x_diff = x2 - x1;
const y_diff = y2 - y1;
let a = sin2(x_diff.toRad() / 2) + Math.cos(x1.toRad()) * Math.cos(x2.toRad()) * sin2(y_diff.toRad() / 2);
a = 2 * rad * Math.asin(Math.sqrt(a))
return a;
}
console.log(
computeDistanceBetween(
41.8902142, 12.4900422,
48.8583736, 2.2922926
) / 1000 // Convert to Km
);
This script gives me this result: 1110.6415064d524447, if I check this in Google Maps, the result is approximately the same (1,109.43 km)
Now, MySQL is the problem, I'm trying to do the same with exactly that two points, but the result is very different:
SELECT
ST_Distance_Sphere(
POINT(41.8902142, 12.4900422), POINT(48.8583736, 2.2922926), 6378000
) / 1000 as distances
It gives me 1370.65792958018 as result, there are 260 Km of difference, so the result is very wrong, note that I'm using exactly the same points and the same radius in both cases, so MySQL is wrong or something? How can I get the same result as in JavaScript, but using spatial functions in MySQL? Thanks a lot for your help

Ok, this was very tricky, the function POINT receives first the longitude parameter and after receives the latitude parameter, so, the correct query should be this:
SELECT ST_Distance_Sphere(POINT(12.4900422, 41.8902142),
POINT( 2.2922926, 48.8583736),
6378000 ) /1000 as distances
So this returns me 1110.641506452445 which is correct result.

Related

calculate walking distance between gps points

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.

Calculate approximate distance between two points

I am trying to calculate approximate distance between two points with respect to road map on Google Maps. 75% of the time the points are not on a straight line, and they form a triangle on the road map. I am taking right angle as well as obtuse angle into consideration. And the line created by the points to be the hypotenuse.
Lets take the line formed by the points: "x"
Other Two lines: "y" & "z"
Assuming y and z equal
Now I am taking two options as stated earlier for best result:
Option 1:
y = x(sin 45)/(sin90)
Total Distance = 2y
Option 2:
y = x(sin 30)/(sin120)
Total Distance = 2y
On calculation the distance by getting the all the coordinates of the distance. It is different from both at and approximately 10 to 20%.
In almost all cases the second one provides the best value on checking with the Google Maps vehicle distance.
Is there any better alternative for maximum accuracy?
Did you try Haversine formula?
which is:
dlon = lon2 - lon1
dlat = lat2 - lat1
a = (sin(dlat/2))^2 + cos(lat1) * cos(lat2) * (sin(dlon/2))^2
c = 2 * atan2(sqrt(a), sqrt(1-a))
d = R * c

Retrieving destination coordinates from direction services by distance

I need to retrieve a destination's coordinates using the google maps api directions service. I already have the starting point coordinates, however instead of specifying an ending point in coordinates, I wish to retrieve the coordinates by specifying a distance (in km).
So I guess my question is the following: is it possible to retrieve the destination latlong coordinates (based/calculated on the road's distance and not directional/straight line) by specifying a distance (amount in km) with the directions service or perhaps any alternative way?
I have an image illustration, however unfortunately am unable to attach to this question as I do not have enough reputation. If my question is unclear in any way, or you wish to see the illustration then please contact me and I'll send it off.
I don't think you can do this as the request parameters say that origin and destination parameters are required.
I beliave it will help someone.
There is a method to get coordinates in the google maps library:
google.maps.geometry.spherical.computeOffset(fromCoordinates, distanceInMeters, headingInDegrees)
I believe you are correct. There doesn't seem to be any current method in the api which would allow you to do the following.
Instead I looped through the coordinates returned from the directions service call, and used a function to calculate the distance between coordinates. However even this was not accurate enough as the coordinates returned also seemed to be aggregated and doesn't return an accurate value/distance when calculating the distances between each coordinate as they are aggregated and therefore each coordinate is not necessary along the road.
To work around the above issue, I ended up adding a click event, and plotted the coordinates along the road myself and then stored them in a local json file which I cache and call using an xmlhttprequest.
Fortunately, for my situation I only need to calculate the distance between point A & B on one individual road, so my alternative won't work in cases when you're using multiple or generic roads/locations. You could instead use the first method described, given that you're happy to live with the aggregated data and an in-accurate calculation.
Below are the functions used to calculate the distances between coordinates and then also the final calculation to find the point & coordinates between the final two points. Please note this code relies on and uses jQuery methods.
1. Calculate distance (in meters) between two coordinates
function pointDistance( begin, end )
{
var begin = { lat: begin[0], long: begin[1] },
end = { lat: end[0], long: end[1] };
// General calculations
var earthRadius = 6371, //km
distanceLat = (end.lat - begin.lat).toRad(),
distanceLong = (end.long - begin.long).toRad();
// Convert lats to radiants
begin.lat = begin.lat.toRad();
end.lat = end.lat.toRad();
// Calculation
var a = Math.sin(distanceLat / 2) * Math.sin(distanceLat / 2) +
Math.sin(distanceLong / 2) * Math.sin(distanceLong / 2) * Math.cos(begin.lat) * Math.cos(end.lat);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var distance = (earthRadius * c) - 0.000536;
return (distance * 1000);
}
2. Fetch coordinate of final A-B coordinate (based on percentage remaining). The 'matrix' variable is a json array of coordinates.
function getCoordinates( totalDistance )
{
var lastPoint = { lat: null, long: null },
total = parseFloat(0),
position = { start: null, end: null, distance: 0 };
$(matrix).each(function()
{
if ( lastPoint.lat == null )
{
lastPoint = { lat: this[0], long: this[1] };
return;
}
var distance = pointDistance([lastPoint.lat, lastPoint.long], [this[0], this[1]]);
total = total + distance;
if ( (total / 1000) >= totalDistance )
{
position.start = new google.maps.LatLng(lastPoint.lat, lastPoint.long);
position.end = new google.maps.LatLng(this[0], this[1]);
position.distance = total;
return false;
}
lastPoint = { lat: this[0], long: this[1] };
});
return position;
}
3. Convert numeric degrees to radians
if ( typeof(Number.prototype.toRad) === 'undefined' ) {
Number.prototype.toRad = function() {
return this * Math.PI / 180;
}
}
Hope the following helps any one with the same or simular problem. I haven't investigated this as I've had no need to, but, perhaps if you're dealing with google's paid services, they don't aggregate the data returned by the call?

Function for line between 2 GPS coordinates

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

What is the unit in returned by MySQL GLength method?

I want to get the length in meters between 2 points in the surface of the Earth. But the GLength method returns an unexpected value
http://dev.mysql.com/doc/refman/5.0/en/geometry-property-functions.html#function_glength
SELECT GLength(GeomFromText(
'LINESTRING(-67.8246 -10.0073,-67.8236 -10.0082)', 4326))
actual result
0.00134536240471071
expected value in meters:
147
I guess it's about 3 years too late for the OP, but I happened across this question while researching a similar topic, so here's my tuppence worth.
According to www.movable-type.co.uk/scripts/latlong.html there are 3 ways of calculating distance across the Earth's surface, these being, from easiest to most complex (and thus from least to most accurate): Equirectangular Approximation, Spherical Law of Cosines and the Haversine Formula. The site also provides JavaScript. This is the function for Equirectangular approximation:
function _lineDistanceC(lat1, lon1, lat2, lon2) {
var R = 6371; // km
lat1 = lat1.toRad();
lon1 = lon1.toRad();
lat2 = lat2.toRad();
lon2 = lon2.toRad();
var x = (lon2-lon1) * Math.cos((lat1+lat2)/2);
var y = (lat2-lat1);
return Math.sqrt(x*x + y*y) * R;
}
This is my attempt at a SQL equivalent:
drop function if exists lineDistanceC;
delimiter //
CREATE FUNCTION lineDistanceC (la1 DOUBLE, lo1 DOUBLE, la2 DOUBLE, lo2 DOUBLE) RETURNS DOUBLE
BEGIN
SET #r = 6371;
SET #lat1 = RADIANS(la1);
SET #lon1 = RADIANS(lo1);
SET #lat2 = RADIANS(la2);
SET #lon2 = RADIANS(lo2);
SET #x = (#lon2-#lon1) * COS((#lat1+#lat2)/2);
SET #y = (#lat2 - #lat1);
RETURN (SQRT((#x*#x) + (#y*#y)) * #r);
END
//
delimiter ;
I have no idea how accurate this formula is, but the site seems very credible and definitely worth a visit for more detail. Using the coordinates in the question, the results (in kilometres) from the above JavaScript and SQL are 0.14834420231840376 and 0.1483442023182845, which makes them the same to a fraction of a millimetre, but not, curiously, the 147 metres that the OP was expecting. Enjoy.
1 degree (your input unit) ~= 69 miles. So, if you multiply your results by 69 miles and then convert to meters, you get roughly 149 meters. Not exactly what you expected but pretty close. Of course I don't think that accurately reproduces the spherical nature of the globe, but maybe I'm wrong.
I haven't found a source, but I was playing with this method and I concluded that the result is given in the same unit used in the WKT.
And the distance is calculated in planar coordinates. The SRID is not taken into account.
In a degree of latitude, there is about 69 miles. Looking at longitude, there is about 49 miles per degree. When i am trying to calculate distance between NYC and LONDON right number is 49.
Select Round(GLength(GeomFromText('LineString(40.756054 -73.986951,51.5001524 -0.1262362)')))*49;