Differences in distance calculated between javascript function and sql server STD distance - javascript

DECLARE #orig geography = geography::Point(17, 78, 4326);
Select #orig.STDistance(geography::Point(17.001, 78.00001, 4326))/1000
gives
110.674385214845
function calculateDistance(lat1, lon1, lat2, lon2, units) {
var R = 6371; // Radius of the earth in km
var dLat = deg2rad(lat2 - lat1); // deg2rad below
var dLon = deg2rad(lon2 - lon1);
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(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; // Distance in km
return d;
}
function deg2rad(deg) {
return deg * (Math.PI / 180)
}`
gives
0.11120001099379416 km
difference of 0.6 mtrs

The reason is because the Haversine formula you have used above assumes a spherical earth, with a circumference of 6371 km whereas the Geography datatype uses the WGS 84 ellipsoid. The main difference between the two is that in WGS 84 the earth is an oblate spheroid (ie, squashed at the poles) and is assumed to have a circumference of 6378.13 km around the equator, but a circumference of 6356.75 km around the poles. The full equations for calculations using the WGS 84 ellipsoid are quite complex and Haversine is considered a good approximation in most circumstances.
If you look at the introduction to this excellent resource, http://www.movable-type.co.uk/scripts/latlong.html, the author makes this exact point and suggests that the error from ignoring the ellipsoid is about 0.3% on average (yours is around 0.4%). I repeat the author's quote here, just in case the page ever goes down.
All these formulæ are for calculations on the basis of a spherical earth (ignoring ellipsoidal effects) – which is accurate enough* for most purposes… [In fact, the earth is very slightly ellipsoidal; using a spherical model gives errors typically up to 0.3% – see notes for further details].

Related

Lat/Long equation in Javascript

I am trying to find an equation for finding the lat/long point between two lat/long points in Javascript. It would work something like this.
getMiddle(lat1, lng1, lat2, lng2) <= would return [lat3, lat3] halfway distance wise (going around the earth obviously).
I found this:
Link
Date: 10/11/2001 at 11:41:08
From: Doctor Rick
Subject: Re: Determining lat and long of a point between two given
points
Hi, Chuck.
This will probably be a little more complicated than you think. The
easiest way for me to do it is to think in terms of vectors. Some of
the items in our Archives on the topic of latitude and longitude use
the vector approach, so you can see them for background. For example:
Distance Between Two Points on the Earth
Link
Let's call the two points A and B, and choose a rectangular coordinate
system in which the equator is in the x-y plane and the longitude of
point A is in the x-z plane. Let lat1 be the latitude of A, let lat2
be the latitude of B, and let dlat be the longitude of B minus the
longitude of A. Finally, use distance units such that the radius of
the earth is 1. Then the vectors from the center of the earth to A and
B are
A = (cos(lat1), 0, sin(lat1)) B = (cos(lat2)*cos(dlon),
cos(lat2)*sin(dlon), sin(lat2))
Point C, the midpoint of the shortest line between A and B, lies along
the sum of vectors A and B. (This works because A and B have the same
length, so the sum of the vectors is the diagonal of a rhombus, and
this diagonal bisects the angle.)
A+B = (cos(lat1)+cos(lat2)*cos(dlon),
cos(lat2)*sin(dlon),
sin(lat1)+sin(lat2))
To get the actual vector C, we need to scale this vector to length R
so it ends at the surface of the earth. Thus we have to divide it by
|A+B|, that is, the length of vector A+B. That would get pretty messy.
But we can find the latitude lat3 and longitude difference (lon3-lon1)
by looking at ratios of the coordinates of A+B, and these ratios are
the same whether we scale the vector or not. To see this, look back at
the formula for vector B. Knowing that vector, we can recover lat2 and
dlon:
dlon = tan^-1(B_y/B_x) lat2 = tan^-1(B_z/sqrt(B_x^2+B_y^2))
Here, B_x, B_y, and B_z are the x, y, and z coordinates of vector B.
We can do the same thing with vector A+B to find the latitude and
longitude of point C:
dlon3 = tan^-1(cos(lat2)*sin(dlon)/
(cos(lat1)+cos(lat2)*cos(dlon))) lat3 = tan^-1((sin(lat1)+sin(lat2))/
sqrt((cos(lat1)+cos(lat2)*cos(dlon))^2+
(cos(lat2)*sin(dlon))^2))
That's the formula you seek. Since both formulas involve division, we
must consider the special cases. The longitude calculation fails when
C_x = 0, that is, when C is 90 degrees away from A in longitude, so
dlon3 will be +90 or -90; the sign depends on the sign of dlon. The
latitude calculation fails when C_x and C_y are both zero, thus we
know that in this case, lat3 = 0. A complete algorithm will check for
these cases, but they won't occur if you're interested only in the
continental US.
When I plug in the latitudes and longitudes for LA and NYC, I get:
LA 34.122222 118.4111111 NYC 40.66972222 73.94388889 midpt 39.54707861
97.201534
I hope this is helpful to you.
Doctor Rick, The Math Forum Link
Which I believe has the answer but not as Javascript.
This is what I am working with:
I have spent some time creating map point and determining distance between them, but to find a midway point between them has been difficult for me. Converting equations I have found so far has been unsuccessful.
Once I can find the midpoint I want to use recursion + (how many points I need between two points based on distance) to build properly spaced dotted lines.
Any help on this piece would be greatly appreciated.
Answer in python: http://code.activestate.com/recipes/577713-midpoint-of-two-gps-points/
Convert to js
Profit
Additionally: For those wanting to go straight to profit (yes I know I patched Math. Borrowed shamelessly from here: https://nickthecoder.wordpress.com/2012/04/15/radian-and-degree-conversion-in-javascript/)
getMidpoint: function(lat1, lng1, lat2, lng2) {
Math.degrees = function(rad) {
return rad * (180 / Math.PI);
}
Math.radians = function(deg) {
return deg * (Math.PI / 180);
}
lat1 = Math.radians(lat1);
lng1 = Math.radians(lng1);
lat2 = Math.radians(lat2);
lng = Math.radians(lng2);
bx = Math.cos(lat2) * Math.cos(lng - lng1)
by = Math.cos(lat2) * Math.sin(lng - lng1)
lat3 = Math.atan2(Math.sin(lat1) + Math.sin(lat2), Math.sqrt((Math.cos(lat1) + bx) * (Math.cos(lat1) + bx) + Math.pow(by, 2)));
lon3 = lng1 + Math.atan2(by, Math.cos(lat1) + bx);
return [Math.round(Math.degrees(lat3), 5), Math.round(Math.degrees(lon3), 5)]
}
I think this is the formula you're looking for:
This is the half-way point along a great circle path between the two points.
Formula: Bx = cos φ2 ⋅ cos Δλ
By = cos φ2 ⋅ sin Δλ
φm = atan2( sin φ1 + sin φ2, √(cos φ1 + Bx)² + By² )
λm = λ1 + atan2(By, cos(φ1)+Bx)
JavaScript:
(all angles
in radians)
var Bx = Math.cos(φ2) * Math.cos(λ2-λ1);
var By = Math.cos(φ2) * Math.sin(λ2-λ1);
var φ3 = Math.atan2(Math.sin(φ1) + Math.sin(φ2),
Math.sqrt( (Math.cos(φ1)+Bx)*(Math.cos(φ1)+Bx) + By*By ) );
var λ3 = λ1 + Math.atan2(By, Math.cos(φ1) + Bx);
source: http://www.movable-type.co.uk/scripts/latlong.html

Which one of these azimuth values is the right one? And why?

First of all, let's make clear that I want the Azimuth on the surface of the Earth, i.e. the angle between two locations, for example New York and Moscow.
I am testing some azimuth calculations with my JS functions (shown below). For the points A(-170, -89) to B(10, 89), I get ~90º.
JS function for Azimuth on sphere (from Wikipedia)
var dLon = lon2 - lon1;
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 angle = Math.atan2(y, x) * 180 / Math.PI;
JS function for Azimuth on oblate spheroid (from Wikipedia)
var dLon = lon2 - lon1;
var f = 1 / 298.257223563; /* Flattening for WGS 84 */
var b = (1 - f) * (1 - f);
var tanLat2 = Math.tan(lat2);
var y = Math.sin(dLon);
var x;
if (lat1 === 0) {
var x = b * tanLat2;
} else {
var a = f * (2 - f);
var tanLat1 = Math.tan(lat1);
var c = 1 + b * tanLat2 * tanLat2;
var d = 1 + b * tanLat1 * tanLat1;
var t = b * Math.tan(lat2) / Math.tan(lat1) + a * Math.sqrt(c / d);
var x = (t - Math.cos(dLon)) * Math.sin(lat1);
}
var angle = Math.atan2(y, x) * 180 / Math.PI;
In Calculator 2, I get 90º.
In PostGIS, I get 270º
In Calculator 1, I get 180º.
I know the Azimuth gets more and more distorted near the Poles, but that's exactly why I am testing at these spots. This variety of different solutions are confusing me. Could you please help me getting the right answer for this?
It depends on the reference used for azimuth, e.g. map-types use 0° for North and positive is clockwise, while math-types uses 0° for East and positive is anticlockwise.
The pair of coordinates A(-170, -89) and B(10, 89) are antipodes which are a special case for finding minimum distances and azimuths. Your question can be answered with a thought exercise.
First note that the half-circumferences of the earth are:
Equatorial: 20037.5085 km
Meridional (north-to-south): 20003.93 km
For a pair of antipodes on the north and south poles, there are an infinite number of azimuths, since the distance is the same along each longitude. (What direction would you go from the south pole to the north pole?)
For a pair of antipodes on the equator, the shortest distances are north or south, since it is slightly shorter along the meridional direction.
For any other pair of antipodes, it is the same answer as from the equator: north or south.
Update
To investigate the problem a bit more with the PostGIS SQL query:
SELECT ST_Distance(A, B), degrees(ST_Azimuth(A, B))
FROM (
SELECT 'POINT(-170 -89)'::geography A, 'POINT(10 89)'::geography B
) f;
With PostGIS 2.0 and 2.1, the incorrect results are:
st_distance | degrees
-----------------+------------------
20003900.583699 | 270.005278779849
But with PostGIS 2.2 (and PROJ 4.9.1), the correct results are now:
st_distance | degrees
------------------+---------
20003931.4586255 | 180

OpenLayers: Get distance from gpx

I have a gpx file. I'm displaying it on top of OSM using OpenLayers and this example.
My requirement is to get distance of the route. How can i achieve that?
Please help,
Thanks.
Basically, A GPX is an XML file defining a set of Track Segment which holds a list of Track Points which are logically connected in order.
<trkseg>
<trkpt lat='float' lon='float'/>
<trkpt lat='float' lon='float'/>
<trkpt lat='float' lon='float'/>
...
</trkseg>
All you need is to compute All the track segment length using distance from point to point (the points hold the lat and lon)
The distance from point to point will be compute using the following formula :
var radius = 6378137.0 ; // earth radius in meter
var DE2RA = 0.01745329252; // degre to radian conversion
// return the distance between (lat1,lon1) and (lat2,lon2) in meter.
GCDistance= function (lat1, lon1, lat2, lon2) {
if (lat1 == lat2 && lon1 == lon2) return 0;
lat1 *= DE2RA;
lon1 *= DE2RA;
lat2 *= DE2RA;
lon2 *= DE2RA;
var d = Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos(lon1 - lon2);
return (radius * Math.acos(d));
};
Be aware of the fact that Track Segment are NOT logically connected.
Parsing the GPX using DOM is straight forward.
this code is extract from a lib which in use for years.
here a sample of where I use it.
Hope this help.
G.

JavaScript function nearest geographical neighbor

I am looking for a JavaScript function who returns the nearest neighbor of a number. e.g: I am having a coordinate 12,323432/12,234223 and i want to know the nearest coordinate of a set of 20 other coordinates in a database.
How to handle that?
The following 3 functions find the nearest coordinate from a javascript array using the Haversine formula.
function toRad(Value) {
/** Converts numeric degrees to radians */
return Value * Math.PI / 180;
}
function haversine(lat1,lat2,lng1,lng2){
rad = 6372.8; // for km Use 3961 for miles
deltaLat = toRad(lat2-lat1);
deltaLng = toRad(lng2-lng1);
lat1 = toRad(lat1);
lat2 = toRad(lat2);
a = Math.sin(deltaLat/2) * Math.sin(deltaLat/2) + Math.sin(deltaLng/2) * Math.sin(deltaLng/2) * Math.cos(lat1) * Math.cos(lat2);
c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
return rad * c;
}
function calculate(){
var result = haversine(lat1,coordArray [0][0],lng1,coordArray [0][1]);
for (var i=1;i<coordArray.length;i++){
var ans = haversine(lat1,coordArray [i][0],lng1,coordArray [i][1]);
if (ans < result){//nearest
result = ans;
}
}
document.write("Result " +result);
}
Less rounding errors with the Vincenty formula
The Haversine formula suffers from rounding errors for the special (and somewhat unusual) case of antipodal points (on opposite ends of the sphere).
A better choice is therefore the Vincenty formula for the special case of a sphere. It is computationally not more demanding, but suffers less from machine rounding errors.
Here is my Python3 implementation, which runs in any browser using Brython or which one can easily manually transcode to JavaScript:
from math import radians, sin, cos, atan2, sqrt
def vincenty_sphere(lat1,lat2,lon1,lon2):
lat1 = radians(lat1)
lat2 = radians(lat2)
delta_lon = radians(lon2-lon1)
term1 = (cos(lat2) * sin(delta_lon))**2
term2 = (cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(delta_lon))**2
numerator = sqrt(term1 + term2)
denominator = sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(delta_lon)
central_angle = atan2(numerator, denominator)
radius = 6372.8 # km
return radius * central_angle
def station_near(geo):
lat = geo['latitude']
lon = geo['longitude']
nearest = 40042.0 # km
for s in range(len(STATIONS)):
distance = vincenty_sphere(lat, STATIONS[s].lat, lon, STATIONS[s].lon)
if(distance < nearest):
nearest = distance
station = s
return station

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/

Categories