I'm quite confused about distance calculations.
I'm using this website as a reference:
https://www.omnicalculator.com/other/latitude-longitude-distance
Then I got an implementation of a distance calculation (haversine) from here:
https://tutorialspoint.dev/algorithm/geometric-algorithms/program-distance-two-points-earth
And I want to compare this with various different implementations from Spatial4J.
I'm using these coordinates to test with:
Point 1: 40.688939, -74.04455
Point 2: 40.746853, -73.985633
And I'm getting pretty big differences between different ways of calculating the distance between these two points.
First, what mainly concerns me is that the website (omnicalculator) and the implementation from tutorialspoint agree completely on the distance: 8.132 km
But none of my Spatial4J calculations agree with that number. The one that comes closest to it is the CartesianDistCalc implementation at 8.262 km. The tutorialspoint demo code claims to be using haversine but the output of Spatial4J haversine DistCalc implementation is quite a ways off at 7.313 km.
But can somebody explain to me where these differences are coming from and what the "correct" one is?
Below is my experimental code:
import org.junit.jupiter.api.Test;
import org.locationtech.spatial4j.context.SpatialContext;
import org.locationtech.spatial4j.distance.CartesianDistCalc;
import org.locationtech.spatial4j.distance.GeodesicSphereDistCalc;
class GeodesicCalculationTest {
#Test
void testGeodesicCalculations(){
SpatialContext ctx = SpatialContext.GEO;
var startPoint = ctx.getShapeFactory().pointLatLon(40.688939, -74.04455);
var endPoint = ctx.getShapeFactory().pointLatLon(40.746853, -73.985633);
System.out.println("GEO spatial context: " + ctx.calcDistance(startPoint, endPoint) * 100);
System.out.println("Haversine: " + new GeodesicSphereDistCalc.Haversine().distance(startPoint, endPoint) * 100);
System.out.println("Law of cosine: " + new GeodesicSphereDistCalc.LawOfCosines().distance(startPoint, endPoint) * 100);
System.out.println("Vincenty: " + new GeodesicSphereDistCalc.Vincenty().distance(startPoint, endPoint) * 100);
System.out.println("Cartesian: " + new CartesianDistCalc().distance(startPoint, endPoint) * 100);
System.out.println("Tutorials Point (haversine): " + distance(startPoint.getLat(), endPoint.getLat(), startPoint.getLon(), endPoint.getLon()));
}
public static double distance(double lat1, double lat2, double lon1, double lon2) {
// The math module contains a function
// named toRadians which converts from
// degrees to radians.
lon1 = Math.toRadians(lon1);
lon2 = Math.toRadians(lon2);
lat1 = Math.toRadians(lat1);
lat2 = Math.toRadians(lat2);
// Haversine formula
double dlon = lon2 - lon1;
double dlat = lat2 - lat1;
double a = Math.pow(Math.sin(dlat / 2), 2)
+ Math.cos(lat1) * Math.cos(lat2)
* Math.pow(Math.sin(dlon / 2),2);
double c = 2 * Math.asin(Math.sqrt(a));
// Radius of earth in kilometers. Use 3956
// for miles
double r = 6371;
// calculate the result
return(c * r);
}
}
And the output of running it:
GEO spatial context: 7.31307025220976
Haversine: 7.31307025220976
Law of cosine: 7.313070251733588
Vincenty: 7.3130702522095286
Cartesian: 8.261503667613857
Tutorials Point (haversine): 8.131763102409689
I'm multiplying the Spatial4J calculations by 100 which is also confusing to me... it doesn't really make sense that Spatial4J is giving me answers as 1/100th of a kilometer???
I realise I must be doing something wrong or completely misunderstanding some premises here. I would really appreciate some help in understanding what that is.
The result of SpatialContext.calcDistance(Point p, Point p2) is in degrees. To convert those degrees to km you need to multiply it by the constant DistanceUtils.DEG_TO_KM
SpatialContext ctx = SpatialContext.GEO;
var startPoint = ctx.getShapeFactory().pointLatLon(40.688939, -74.04455);
var endPoint = ctx.getShapeFactory().pointLatLon(40.746853, -73.985633);
double distanceInDegrees = ctx.calcDistance(startPoint, endPoint);
double distanceInKm = distanceInDegrees * DistanceUtils.DEG_TO_KM;
System.out.println("GEO spatial context: " + distanceInKm);
This gives you the output:
GEO spatial context: 8.131774297975046
Which is very close to your example from
Tutorials Point (haversine): 8.131763102409689
The difference is because Spatial4j is using the value 6371.0087714 for the radius of the earth in km, where as in your example you are using 6371.
Related
First I'm a newbie in GPS system.
I have GPS coordinates in WGS-84 receive from a basic GPS in USB.
I want to calculate the distance beetween this two point.
But I want the plane distances. So I have first to convert this coordinates to an other CRS example "EPSG:2154" for Lambert 93. And after calulate the distance.
I try to use Geotools but the examples in the docs are not releveant for me and I d'ont understand how to do this.
I thought first I have to found the matching transform like this :
DefaultGeographicCRS crs = DefaultGeographicCRS.WGS84;
//EPSG:2154 RGF93 / Lambert-93 (Google it)
CoordinateReferenceSystem crs2 = CRS.decode("EPSG:2154");
MathTransform transform = CRS.findMathTransform(crs, crs2);
But after reading docs it seems the transformation works for x,y coordinates not lat/long. And i do not understand how to use the MathTransform as it doesn't accept point with lat/long. I also try the example below but executing the code doesn't give the same results for me.
Calculating distance between two points, using latitude longitude?
So if anyone with good knowledge in GPS and Geotools is able to help me.
Thanks
If you simply want the distance between two GPS points then you can use the GeodeticCalculator to calculate this in metres (and the Units library to convert it to any distance unit you like:
import javax.measure.MetricPrefix;
import javax.measure.Quantity;
import javax.measure.quantity.Length;
import org.geotools.referencing.CRS;
import org.geotools.referencing.CRS.AxisOrder;
import org.geotools.referencing.GeodeticCalculator;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import si.uom.SI;
import systems.uom.common.USCustomary;
import tech.units.indriya.quantity.Quantities;
public class OrthodromicDistance2 {
/**
* take two pairs of lat/long and return bearing and distance.
*
* #param args
*/
public static void main(String[] args) {
DefaultGeographicCRS crs = DefaultGeographicCRS.WGS84;
if (args.length != 4) {
System.err.println("Need 4 numbers lat_1 lon_1 lat_2 lon_2");
return;
}
GeometryFactory geomFactory = new GeometryFactory();
Point[] points = new Point[2];
for (int i = 0, k = 0; i < 2; i++, k += 2) {
double x = Double.parseDouble(args[k]);
double y = Double.parseDouble(args[k + 1]);
if (CRS.getAxisOrder(crs).equals(AxisOrder.NORTH_EAST)) {
System.out.println("working with a lat/lon crs");
points[i] = geomFactory.createPoint(new Coordinate(x, y));
} else {
System.out.println("working with a lon/lat crs");
points[i] = geomFactory.createPoint(new Coordinate(y, x));
}
}
System.out.println(points[0]);
System.out.println(points[1]);
double distance = 0.0;
GeodeticCalculator calc = new GeodeticCalculator(crs);
calc.setStartingGeographicPoint(points[0].getX(), points[0].getY());
calc.setDestinationGeographicPoint(points[1].getX(), points[1].getY());
distance = calc.getOrthodromicDistance();
double bearing = calc.getAzimuth();
Quantity<Length> dist = Quantities.getQuantity(distance, SI.METRE);
System.out.println(dist.to(MetricPrefix.KILO(SI.METRE)).getValue() + " Km");
System.out.println(dist.to(USCustomary.MILE).getValue() + " miles");
System.out.println("Bearing " + bearing + " degrees");
}
}
This will work for any points on the globe no matter how far apart they are and makes use of GeographicLib by Charles F. F. Karney and gives an accuracy of nanometres.
If however, you want to carry out more geometry operations on your points/lines etc then you are right to want to transform your points to a projected CRS (such as Lambert 93):
CoordinateReferenceSystem wgs84= CRS.decode("EPSG:4326", true);
CoordinateReferenceSystem lambert = CRS.decode("EPSG:2154", true);
MathTransform toMeters= CRS.findMathTransform(wgs84, lambert);
Geometry output1 = JTS.transform(input1, toMeters);
Geometry output2 = JTS.transform(input2, toMeters);
double distance = output1.distance(output2);
I'm using firebase (android) to store data and I'm saving users like below:
users{
abcdefghi{
name:"abc",
lat:"12.988",
long:-0.123,
desc:"all other desc"
},KLMNGHT{
name:"def",
lat:"11.988",
long:-1.123,
desc:" other desc"
}
}
I want to display all users who comes into my radius zone(proximity zone) which are defined by me according to my location . I am out of ideas . I looked upon Haversine formula. But i don't know to achieve that.
What is the best algorithm to find user?
private const double EARTH_RADIUS = 6378.137;
private static double rad(double d)
{
return d * Math.PI / 180.0;
}
public static double GetDistance(double lat1, double lng1, double lat2, double lng2)
{
double radLat1 = rad(lat1);
double radLat2 = rad(lat2);
double a = radLat1 - radLat2;
double b = rad(lng1) - rad(lng2);
double s = 2 * Math.Asin(Math.Sqrt(Math.Pow(Math.Sin(a/2),2) +
Math.Cos(radLat1)*Math.Cos(radLat2)*Math.Pow(Math.Sin(b/2),2)));
s = s * EARTH_RADIUS;
s = Math.Round(s * 10000) / 10000;
return s;
}
Maybe this code can help you.
So is the question strictly: given two points on earth A(lan1,lon1) B(lan2,lon2) find the distance between A and B? When you are saying 'best algorithm',do you mean best in terms of development time,time,memory?!
Anyways,assuming 'best' in terms of development time you could use:
1.Google's API explained here: https://developers.google.com/maps/documentation/distance-matrix/intro#DistanceMatrixRequests
2.Here is a simple implementation- but I haven't tested it myself yet-
https://www.geodatasource.com/developers/java
While integrating google API in Java, I need to calculate another place's lat/long using existing lat/long, distance and direction. How to calculate that point?
The following PHP code provides the point from $lat1,$lng1 a distance $dist (kms) along a bearing $brng.
function destination($lat1,$lng1,$dist,$brng){
$alpha = $dist/6371; // km
$lat2 = asin((sin($lat1)*cos($alpha)) +(cos($lat1)*sin($alpha)*cos($brng)) );
$lng2 = $lng1 + atan2(sin($brng)*sin($alpha)*cos($lat1),cos($alpha)-sin($lat1)*sin($lat2));
return array(toDeg($lat2),toDeg($lng2));
}
$lat1,$lng1 & $brng in radians
function toRad($deg) {
// Converts numeric degrees to radians
return $deg * pi() / 180;
}
function toDeg($rad){
return $rad * 180 / pi();
}
I hope you understand my question my english is not too good.
Anyway, I’m working on a application with locations. Its only a fun app which should help me to learn more.
Its a iOS application and the server is a WebObjects/WOnder application(Java). What im trying to do is on the iOS app I fetch the user location then send the data to the server. And on the server I fetch annotation points from a database. but only send the annotations which are in near of the users location back.
My only problem now is I don’t know how to calculate the locations in the near of the user. I googled a lot but did not find something that work.
Only something which give me the „bounding box“ of the user.
//To calculate the search bounds...
//First we need to calculate the corners of the map so we get the points
CGPoint nePoint = CGPointMake(self.mapView.bounds.origin.x + mapView.bounds.size.width, mapView.bounds.origin.y);
CGPoint swPoint = CGPointMake((self.mapView.bounds.origin.x), (mapView.bounds.origin.y + mapView.bounds.size.height));
//Then transform those point into lat,lng values
CLLocationCoordinate2D neCoord;
neCoord = [mapView convertPoint:nePoint toCoordinateFromView:mapView];
CLLocationCoordinate2D swCoord;
swCoord = [mapView convertPoint:swPoint toCoordinateFromView:mapView];
Dose anyone have a good idea how to do that in Java?
I use c# code to calculate the distance. I enumerate all of the data in the database and if the data is in range of my distance I add it to the array and after that I pass it to the device. In my code I provide lat and lon for user current position and lat and lon for object from database. I also provide unit K - km and M - miles. This is a c# code but you can easy convert it to java:
public double GetDistanceFromLatLong(double lat1, double lon1, double lat2, double lon2, string unit)
{
double ReturnValue = 0;
double theta = 0;
double dist = 0;
theta = lon1 - lon2;
dist = Math.Sin(DegreesToRadians(lat1)) * Math.Sin(DegreesToRadians(lat2)) + Math.Cos(DegreesToRadians(lat1)) *
Math.Cos(DegreesToRadians(lat2)) * Math.Cos(DegreesToRadians(theta));
dist = Math.Acos(dist);
dist = RadiansToDegrees(dist);
ReturnValue = dist * 60 * 1.1515;
switch (unit.ToUpper())
{
case "K":
ReturnValue = ReturnValue * 1.609344;
break;
case "M":
ReturnValue = ReturnValue * 0.8684;
break;
}
return ReturnValue;
}
"I'm working with J2ME in eclipse, and need to use the method acos() belongs to the Math package, the question is that the package available for J2ME Math is not that function (limitations of mobiles), then I want an algorithm or method alternative to calculate the cosine.
I need the "acos" to calculate the following formula:
long2 = cood[i].getLongitud();
lat2 = cood[i].getLatitud();
dlong = (long1 - long2);
c = dlong * degtorad;
dvalue = (Math.sin(a) * Math.sin(b))+(Math.cos(a) * Math.cos(b)*Math.cos(c));
dd = Math.acos(dvalue) * radtodeg;
km = (dd * 111.302);
This function allows me from two geographical coordinates to calculate the distance in kilometers between them. If there is an alternative method to calculate this distance (where the cosine is not used), I also would be useful.
Any help on this?
public static double acos(double a)
{
final double epsilon=1.0E-7;
double x=a;
do {
x-=(Math.sin(x)-a)/Math.cos(x);
}
while (Math.abs(Math.sin(x)-a)>epsilon);
// returned angle is in radians
return -1*(x-Math.PI/2);
}
You could try to implement the arccosine as shown here.
If you have the Location API available you can use the distance method from the Coordinates class. From the Coordinates API:
public float distance(Coordinates to)
Calculates the geodetic distance between the two points
according to the ellipsoid model of WGS84.
Altitude is neglected from calculations.
The implementation shall calculate this as exactly as it can.
However, it is required that the result is within 0.35% of the correct result.