So, I need to calculate distances betweeen two points (lat-lon pairs). I've read about Haversine formula and Equirectangular approximation in Movable Type Scripts.
Since I only want to calculate short distances (less than 4 KM), is the Equirectangular formula a good approximation?
Also, I've read about lat-lon storage in MySQL databases in Google Developers documentation and they implement the Haversine formula like this:
SELECT id, ( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(-122) ) + sin( radians(37) ) * sin( radians( lat ) ) ) ) AS distance FROM markers HAVING distance < 25 ORDER BY distance LIMIT 0 , 20;
My last question is, is there any big performance differences between filtering the points in the sql query (as in the example) and filtering them by code?.
When I tested haversine v equirectangular over much larger distances (1000km, within the UK), the difference was on the order of 0.1%. So for distances of 4km or less you might as well use equirectangular for speed unless you have a need for maximum accuracy.
Related
Athena only allows to calculate the distance of the buffer in decimal degrees but this value varies with respect to the latitude in the globe, tate to obtain a distance according to the following formula but it is not consistent in Mexico.
Athena function like this : ST_Buffer(geometry, double)
Athena geospatial functions
So, is posible obtain the corresponding distance in decimal degrees over a custom point in map , ex : get the decimal degree for point x, y like that distance in meters is 300 mts
Currently I use the following formula to approximate the decimal degrees but some buffers are quite horrible although it meets the minimum required
SELECT
ST_Buffer(ST_GeometryFromText( shape_wkt) ,
abs(5000.0 * 360.0 / (2.0 * pi() * cos( latitud )* 6400000.0) ) ) AS
dinamic_buffer_5000
5000 is buffer in meters
6400000.0 earth radius in meters
Some useffull questions :
gps-coordinates-in-degrees-to-calculate-distances
Calculate distance in meters using results in degrees
calculating-latitude-longitude-x-miles-from-point
A possible alternative is the following
To obtain the decimal degrees relative to a point one could:
Generate a second point at a distance d for this you would have to implement this formula, where the bearing does not matter
With this second point calculate the distance in Athena that will return the distance in decimal degrees, as input for the buffer function.
As an approximate is good alternative
Now how implement the second point ?....Here is the formula
I will try to convert to SQL code if can :
After a test I realize that even with the difference of distance it is not possible to obtain the buffer in an optimal way.
In this case the distance to the lower point was 300 meters, after obtaining the distance in decimal degrees with Athena an oblate shape is obtained, it changes the degree of inclination of the point by 90 degrees but it only generates a slightly larger shape.
Destination point given distance and bearing from start point
Source code (zory im edit for test my sql ):
destinationPoint(distance, bearing, radius=6371e3) {
// sinφ2 = sinφ1⋅cosδ + cosφ1⋅sinδ⋅cosθ
// tanΔλ = sinθ⋅sinδ⋅cosφ1 / cosδ−sinφ1⋅sinφ2
// see mathforum.org/library/drmath/view/52049.html for derivation
const dist_ang = distance / radius; // angular distance in radians
const angulo = Number(bearing).toRadians();
const rad_lat = this.lat.toRadians();
const rad_lon = this.lon.toRadians();
console.log("distance", distance);
console.log("radius", radius);
console.log("angular distance in radians", dist_ang);
console.log("bearing", Number(bearing));
console.log("bearing angulo ", angulo );
console.log("lat.toRadians", rad_lat);
console.log("lon.toRadians", rad_lon);
console.log("lon",this.lon);
console.log("lat",this.lat);
const sinφ2 = Math.sin(rad_lat) * Math.cos(dist_ang) + Math.cos(rad_lat) * Math.sin(dist_ang) * Math.cos(angulo);
const φ2 = Math.asin(sinφ2); //lat
console.log("φ2",φ2); //lat
console.log("sinφ2",sinφ2);
const y = Math.sin(angulo) * Math.sin(dist_ang) * Math.cos(rad_lat);
const x = Math.cos(dist_ang) - Math.sin(rad_lat) * sinφ2;
console.log("y",y);
console.log("x",x);
const λ2 = rad_lon + Math.atan2(y, x); //lon
console.log("λ2",λ2);
const lat = φ2.toDegrees();//lat
const lon = λ2.toDegrees();//lon
console.log("lon2",lon);
console.log("lat2",lat);
return new LatLonSpherical(lat, lon);
}
I am trying to calculate distance between two geographical coordinates and I want to convert geographical coordinates to the km. Therefore I used deg2km function. However, I realise that it is not convert points properly.
For instance, I used these two points.
p_x=[5; 10]; %degree
p_y=[8; 16]; %degree
pos_y=deg2km(p_y,6378);
pos_x=deg2km(p_x,6378);
It returns as:
pos_x= [556.58549846099 1113.17099692198]
pos_y= [890.536797537587 1781.07359507517]
When I calculate distance ( sqrt((556.5-1113.2)^2+(890.5368-1781.1)^2) ) between these points I obtained distance as : 1050.2464
However I checked it google map and also other websites it should be 1042 km.
Do you have any suggestion to calculate distance and also points as kilometers properly?
Thanks in advance!
edited as :
I've points(deg)and I need to convert them km and calculate distance between points.
LAT=[41.000173;41.010134]*pi/180;
LON=[28.995882;28.995584]*pi/180;
I used this code to calculate distance. It calculates properly.
But I can not convert my points to kilometers.
LAT=[41.000173;41.010134]*pi/180;
LON=[28.995882;28.995584]*pi/180;
R=6378; %km
for i=1:length(LAT)-1
psi(i,1) = atan2( sin (LON(i+1)-LON(i)) * cos (LAT(i+1)) , cos (LAT(i)) *sin (LAT(i+1)) - sin (LAT(i)) * cos (LAT(i+1)) * cos (LON(i+1)-LON(i)) );
a=(sin((LAT(i+1)-LAT(i))/2))^2+cos(LAT(i))*cos(LAT(i+1))*(sin((LON(i+1)-LON(i))/2))^2;
c=2*atan2(sqrt(a),sqrt(1-a));
d(i,1)=R*c;
end
I am creating an app where a user can create an event and see other events created by other users only if there are in a 10Km Radius. I am storing all the data in firestore.
This is how the app works, from the user side all the events are fetched and only those events are displayed whose distance is less than 10km.
The problem is if there are 10,000 events in the database and the user is in the 10km radius of only 10 events then obviously it will count as 10,000 reads which is too expensive.
Any suggestions for this problem?
One solution that I have in mind is to store data according to the geographical area but how to implement it is another problem.
You won't be charge for 10 000 reads but only the documents retrieved by your query, ten in your example.
Here's a good video from Firebase explaining their billing system : https://www.youtube.com/watch?time_continue=224&v=6NegFl9p_sE
Also keep in mind that for queries other than document reads, such as a request for a list of collection IDs, you are billed for one document read.
I suggest computing min and max longitude corresponding to 10 km distance from user longitude. You do the same with latitude and use those limits in Firestore query. Doing so You will have less events/reads and you can compute the exact distance to suppress events between 10 and 14 km radius... if necessary.
To compute your limit you can use the following formula from https://www.movable-type.co.uk/scripts/latlong.html (Destination point given distance and bearing from start point) and there's an online javascript calculator you can study.
φ2 = asin( sin φ1 ⋅ cos δ + cos φ1 ⋅ sin δ ⋅ cos θ )
λ2 = λ1 + atan2( sin θ ⋅ sin δ ⋅ cos φ1, cos δ − sin φ1 ⋅ sin φ2 )
where φ is latitude, λ is longitude, θ is the bearing (clockwise from north), δ is the angular distance d/R; d being the distance traveled in meters, R the earth’s radius (6371e3).
With bearing 0° you obtain LatitudeMax
With bearing 90° you obtain longitudeEast
With bearing 180° you obtain LatitudeMin
With bearing 270° you obtain longitudewest
Since earth is a sphere with longitude between -180° and 180°
longitudeMin = Min(longitudeEast, longitudeWest)
longitudeMax = Max(longitudeEast, longitudeWest)
And The Firestore part is like :
CollectionReference col = Firestore.instance.collection("mycollection");
Query latitudeMinQuery = col.where('latitude', isGreaterThan: LatitudeMin);
Query latitudeMaxQuery = latitudeMinQuery.where('latitude', isLessThan: LatitudeMin);
Query longitudeMinQuery = latitudeMaxQuery.where('longitude', isGreaterThan: LongitudeMin);
Query longitudeMaxQuery = longitudeMinQuery.where('latitude', isLessThan: LongitudeMax);
https://stackoverflow.com/a/43804487/9139407 (answers by #alex-mamo)
Hopes it helps!
I'm testing out different formulas for finding specific points on the earth within a given radius from a given latitude/longitude. I've been using the 'spherical law of cosines' and what I believe to be an implementation of Haversine.
For the following formulas, these are the variables:
[$lat/$lon] = point of origin
[latitude/longitude] = second point
[$radius] = radius
Spherical law of cosines
3959 * acos( cos( radians('.$lat.') ) * cos( radians( latitude ) ) * cos( radians( longitude ) - radians('.$lon.') ) + sin( radians('.$lat.') ) * sin( radians( latitude ) ) ) ) <= '.$radius.';
Haversine (at least I think it is!)
3959*3.1415926*sqrt((latitude-'.$lat.')*(latitude-'.$lat.') + cos(latitude/57.29578)*cos('.$lat.'/57.29578)*(longitude-'.$lon.')*(longitude-'.$lon.'))/180) <= '.$radius.';';
I initially came across a lot of information saying that Haversine was the gold standard as far as accuracy. However, there also seems to be an opinion that the spherical law of cosines is more accurate than Haversine, as long as the distance being measured is greater than 5 metres or so. Furthermore, some say that Vincenty is said to trump both in accuracy.
Three questions:
Is my Haversine formula actually Haversine or it something else?
Any thoughts on which wins out for most accurate?
Can anyone provide me with a formulation for Vincenty along the lines of the above forumlas?
thanks!
Your so-called Haversine formula is totally incorrect.
Firstly it contains 7 left parentheses and 8 right parentheses.
Secondly conversion from degrees to radians is done in some cases by dividing by 57.29578 then there's a constant pi up the front and a constant 180 down the back.
Thirdly haversine(x) = sin(x / 2) ** 2 and I don't see the / 2 anywhere.
Fourthly there should be an asin function call near the front.
Correct formula here
My answer will be more Specific for your first and second answer
Q1: Is my Haversine formula actually Haversine or it something else?
I Don not understand Your haversine formula you made it
The write one or exactly the SQl query for haversine Formula is that
From Google Developer Site Click Here For Details
SELECT id, ( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(-122) ) + sin( radians(37) ) * sin( radians( lat ) ) ) ) AS distance FROM markers HAVING distance < 25 ORDER BY distance LIMIT 0 , 20;
This SQL statement that will find the closest 20 locations that are within a radius of 25 miles to the 37, -122 coordinate. It calculates the distance based on the latitude/longitude of that row and the target latitude/longitude, and then asks for only rows where the distance value is less than 25, orders the whole query by distance, and limits it to 20 results. To search by kilometers instead of miles, replace 3959 with 6371.
You can make your Changes for this SQL query To be exactly What you want to.
Q2: Any thoughts on which wins out for most accurate?
There is no final answer for who can win?!, But we can deal with that:
1.Haversine is More Faster.
2.Spherical law of cosines is more accurate for Small distances.
And About Q3 I Know that the vincenty Formula is the most accurate But it is the slowest One
I just trying know how to implement in objective c an equation that gave my position in lat, long and the position of a point in lat,long returns the relative angle to the azimuth so I will be able to know when to paint this point knowing the azimuth. I also know more or less the equation but I don´t know how to do it in objective c:
b = arccos ( cos (90 - lat2) * cos (90 - lat1) + sin (90 - lat2) * sin (90 - lat1) * cos (lon2 - lon1) )
A = arcsin ( sin (90 - lat2) * sin (lon2 - lon1) / sin (b) )
Regarding turning that equation into Objecive-C.
'man cos' and 'man sin' say they take radians, so you'll have to convert your degree values to radians. The common method seems to be this, (90 * M_PI/180), where 90 is the degree value being converted.
The functions for arccos and arcsin are acos and asin respectively. I assume they to take radians. Their man pages don't explicitly say.
Read the man pages to learn which version of the above functions are appropriate for your case. For instance, sin has; sin(), sinl(), sinf().