How can I measure distance and create a bounding box based on two latitude+longitude points in Java?
Asked Answered
N

12

76

I am wanting to find the distance between two different points. This I know can be accomplished with the great circle distance. http://www.meridianworlddata.com/Distance-calculation.asp

Once done, with a point and distance I would like to find the point that distance north, and that distance east in order to create a box around the point.

Nasia answered 23/9, 2008 at 10:45 Comment(1)
refer to this blog xebee.xebia.in/2010/10/28/working-with-geolocationsNim
W
21

We've had some success using OpenMap to plot a lot of positional data. There's a LatLonPoint class that has some basic functionality, including distance.

Wrigley answered 23/9, 2008 at 11:16 Comment(4)
Warning to possible adopters: I just ran into a BIG problem with OpenMap; they use floats internally for decimal lat/lon, which limits the accuracy depending how close to the equator you are. They plan to support the option for doubles with their OMGraphic classes starting in version 4.7, but current stable is only 4.6.5 (as of March 2010). Source: openmap.bbn.com/mailArchives/openmap-users/2006-01/4522.htmlKresic
Also note that the current OpenMap Software License Agreement openmap.bbn.com/license.html has been deemed non-free. web.archiveorange.com/archive/v/XyE55YoXwS3lME936I0U Some discussions with BBN has occurred to change licenses but nothing has happened yet.Dustan
This answer is now outdated since both links are offline.Catchword
I just fixed the links to the new domain at openmap-java.org. License is now here: openmap-java.org/License.html (no idea if it was changed since the comment from @LeifGruenwoldtSepta
H
156

Here is a Java implementation of Haversine formula. I use this in a project to calculate distance in miles between lat/longs.

public static double distFrom(double lat1, double lng1, double lat2, double lng2) {
    double earthRadius = 3958.75; // miles (or 6371.0 kilometers)
    double dLat = Math.toRadians(lat2-lat1);
    double dLng = Math.toRadians(lng2-lng1);
    double sindLat = Math.sin(dLat / 2);
    double sindLng = Math.sin(dLng / 2);
    double a = Math.pow(sindLat, 2) + Math.pow(sindLng, 2)
            * Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2));
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
    double dist = earthRadius * c;

    return dist;
    }
Howling answered 23/9, 2008 at 19:42 Comment(8)
Just a note on this, it will return the distance in miles (because of the earthRadius setting). For other units change the earthRadius (see en.wikipedia.org/wiki/Earth_radius for more)Thurman
Is there a reason you're using floats instead of doubles? If I understand properly you can increase the accuracy of your results by simply changing your input parameter typesBlesbok
There is also no need to calculate the sine of half the deltas twice. Calculate it once and multiply it with itself. It is definitely worth in in a tight loop.Subheading
The derivation for this can be found at mathforum.org/library/drmath/view/51879.htmlHeliometer
This was rendered incorrect somewhere along the edits. I hope someone will fix it, because I don't see where it got messed up. The original code is correct. (tested on lat/lng 42,-82 and 42,-83)Hocker
Earth is not a perfect sphereNatalia
Use earthRadius as 6371 to get result in kilometers.Lefty
Kotlin version of formula here https://mcmap.net/q/265526/-how-can-i-measure-distance-and-create-a-bounding-box-based-on-two-latitude-longitude-points-in-javaPasteurizer
F
45

Or you could use SimpleLatLng. Apache 2.0 licensed and used in one production system that I know of: mine.

Short story:

I was searching for a simple geo library and couldn't find one to fit my needs. And who wants to write and test and debug these little geo tools over and over again in every application? There's got to be a better way!

So SimpleLatLng was born as a way to store latitude-longitude data, do distance calculations, and create shaped boundaries.

I know I'm two years too late to help the original poster, but my aim is to help the people like me who find this question in a search. I would love to have some people use it and contribute to the testing and vision of this little lightweight utility.

Finnigan answered 28/10, 2010 at 23:17 Comment(2)
this might help me! did you create it? Do you use the Haversine formula for distance calculations?? I'll try to jump in if I find the time!Lavernalaverne
Correct, it uses Haversine for distance calculations with an emphasis on (although admittedly not an obsession with) speed and a low memory profile. I think it has some other nice number-handling properties as well, like considering coordinates that are "really close" to be equal.Finnigan
W
21

We've had some success using OpenMap to plot a lot of positional data. There's a LatLonPoint class that has some basic functionality, including distance.

Wrigley answered 23/9, 2008 at 11:16 Comment(4)
Warning to possible adopters: I just ran into a BIG problem with OpenMap; they use floats internally for decimal lat/lon, which limits the accuracy depending how close to the equator you are. They plan to support the option for doubles with their OMGraphic classes starting in version 4.7, but current stable is only 4.6.5 (as of March 2010). Source: openmap.bbn.com/mailArchives/openmap-users/2006-01/4522.htmlKresic
Also note that the current OpenMap Software License Agreement openmap.bbn.com/license.html has been deemed non-free. web.archiveorange.com/archive/v/XyE55YoXwS3lME936I0U Some discussions with BBN has occurred to change licenses but nothing has happened yet.Dustan
This answer is now outdated since both links are offline.Catchword
I just fixed the links to the new domain at openmap-java.org. License is now here: openmap-java.org/License.html (no idea if it was changed since the comment from @LeifGruenwoldtSepta
O
11

For a more accurate distance (0.5mm) you can also use the Vincenty approximation:

/**
 * Calculates geodetic distance between two points specified by latitude/longitude using Vincenty inverse formula
 * for ellipsoids
 * 
 * @param lat1
 *            first point latitude in decimal degrees
 * @param lon1
 *            first point longitude in decimal degrees
 * @param lat2
 *            second point latitude in decimal degrees
 * @param lon2
 *            second point longitude in decimal degrees
 * @returns distance in meters between points with 5.10<sup>-4</sup> precision
 * @see <a href="http://www.movable-type.co.uk/scripts/latlong-vincenty.html">Originally posted here</a>
 */
public static double distVincenty(double lat1, double lon1, double lat2, double lon2) {
    double a = 6378137, b = 6356752.314245, f = 1 / 298.257223563; // WGS-84 ellipsoid params
    double L = Math.toRadians(lon2 - lon1);
    double U1 = Math.atan((1 - f) * Math.tan(Math.toRadians(lat1)));
    double U2 = Math.atan((1 - f) * Math.tan(Math.toRadians(lat2)));
    double sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
    double sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);

    double sinLambda, cosLambda, sinSigma, cosSigma, sigma, sinAlpha, cosSqAlpha, cos2SigmaM;
    double lambda = L, lambdaP, iterLimit = 100;
    do {
        sinLambda = Math.sin(lambda);
        cosLambda = Math.cos(lambda);
        sinSigma = Math.sqrt((cosU2 * sinLambda) * (cosU2 * sinLambda)
                + (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda) * (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda));
        if (sinSigma == 0)
            return 0; // co-incident points
        cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
        sigma = Math.atan2(sinSigma, cosSigma);
        sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
        cosSqAlpha = 1 - sinAlpha * sinAlpha;
        cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha;
        if (Double.isNaN(cos2SigmaM))
            cos2SigmaM = 0; // equatorial line: cosSqAlpha=0 (§6)
        double C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
        lambdaP = lambda;
        lambda = L + (1 - C) * f * sinAlpha
                * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));
    } while (Math.abs(lambda - lambdaP) > 1e-12 && --iterLimit > 0);

    if (iterLimit == 0)
        return Double.NaN; // formula failed to converge

    double uSq = cosSqAlpha * (a * a - b * b) / (b * b);
    double A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
    double B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
    double deltaSigma = B
            * sinSigma
            * (cos2SigmaM + B
                    / 4
                    * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B / 6 * cos2SigmaM
                            * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
    double dist = b * A * (sigma - deltaSigma);

    return dist;
}

This code was freely adapted from http://www.movable-type.co.uk/scripts/latlong-vincenty.html

Osteo answered 22/3, 2012 at 12:43 Comment(0)
T
6

Corrected Haversine Distance formula....

public static double HaverSineDistance(double lat1, double lng1, double lat2, double lng2) 
{
    // mHager 08-12-2012
    // http://en.wikipedia.org/wiki/Haversine_formula
    // Implementation

    // convert to radians
    lat1 = Math.toRadians(lat1);
    lng1 = Math.toRadians(lng1);
    lat2 = Math.toRadians(lat2);
    lng2 = Math.toRadians(lng2);

    double dlon = lng2 - lng1;
    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.atan2(Math.sqrt(a), Math.sqrt(1-a));

    return EARTH_RADIUS * c;
}   
Tifanytiff answered 23/8, 2012 at 23:20 Comment(3)
Earth is not a perfect sphereNatalia
Correct, sometimes close is good enough. IE: Why Mr. Inman created it. I'd tell him he is wrong, but he is dead. :o( If you need to calculate for the oblong shape of the world, there are better formulas for that. There are also some great apache libraries that you can use as well. If you just need something simple, this is a good quick example. :)Tifanytiff
Ya, Haversine formula is built on the 'close is good enough' principal. At the time we were measuring distances that were < 50 miles to determine proximity from one location to another as a 'heuristic'.Tifanytiff
S
2

http://www.movable-type.co.uk/scripts/latlong.html

public static Double distanceBetweenTwoLocationsInKm(Double latitudeOne, Double longitudeOne, Double latitudeTwo, Double longitudeTwo) {
        if (latitudeOne == null || latitudeTwo == null || longitudeOne == null || longitudeTwo == null) {
            return null;
        }

        Double earthRadius = 6371.0;
        Double diffBetweenLatitudeRadians = Math.toRadians(latitudeTwo - latitudeOne);
        Double diffBetweenLongitudeRadians = Math.toRadians(longitudeTwo - longitudeOne);
        Double latitudeOneInRadians = Math.toRadians(latitudeOne);
        Double latitudeTwoInRadians = Math.toRadians(latitudeTwo);
        Double a = Math.sin(diffBetweenLatitudeRadians / 2) * Math.sin(diffBetweenLatitudeRadians / 2) + Math.cos(latitudeOneInRadians) * Math.cos(latitudeTwoInRadians) * Math.sin(diffBetweenLongitudeRadians / 2)
                * Math.sin(diffBetweenLongitudeRadians / 2);
        Double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        return (earthRadius * c);
    }
Siddons answered 7/4, 2014 at 13:14 Comment(0)
O
1

You can use the Java Geodesy Library for GPS, it uses the Vincenty's formulae which takes account of the earths surface curvature.

Implementation goes like this:

import org.gavaghan.geodesy.*;
...
GeodeticCalculator geoCalc = new GeodeticCalculator();
Ellipsoid reference = Ellipsoid.WGS84;
GlobalPosition pointA = new GlobalPosition(latitude, longitude, 0.0);
GlobalPosition userPos = new GlobalPosition(userLat, userLon, 0.0);
double distance = geoCalc.calculateGeodeticCurve(reference, userPos, pointA).getEllipsoidalDistance();

The resulting distance is in meters.

Orchestra answered 12/12, 2011 at 1:27 Comment(0)
B
1

I know that there are many answers, but in doing some research on this topic, I found that most answers here use the Haversine formula, but the Vincenty formula is actually more accurate. There was one post that adapted the calculation from a Javascript version, but it's very unwieldy. I found a version that is superior because:

  1. It also has an open license.
  2. It uses OOP principles.
  3. It has greater flexibility to choose the ellipsoid you want to use.
  4. It has more methods to allow for different calculations in the future.
  5. It is well documented.

VincentyDistanceCalculator

Brachyuran answered 20/6, 2014 at 6:2 Comment(2)
And it's gone. Might be this one: github.com/manuelhartl/opensaft/blob/…Hypocotyl
Good catch! I did some digging and the Apache Lucene tools would probably be more reliable. I'll update the answer.Brachyuran
S
1

This method would help you find the distance between to geographic location in km.

private double getDist(double lat1, double lon1, double lat2, double lon2)
{
    int R = 6373; // radius of the earth in kilometres
    double lat1rad = Math.toRadians(lat1);
    double lat2rad = Math.toRadians(lat2);
    double deltaLat = Math.toRadians(lat2-lat1);
    double deltaLon = Math.toRadians(lon2-lon1);

    double a = Math.sin(deltaLat/2) * Math.sin(deltaLat/2) +
            Math.cos(lat1rad) * Math.cos(lat2rad) *
            Math.sin(deltaLon/2) * Math.sin(deltaLon/2);
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));

    double d = R * c;
    return d;
}
Suttles answered 24/7, 2016 at 13:53 Comment(0)
P
1

Kotlin version of Haversine formula. Returned result in meters. Tested on https://www.vcalc.com/wiki/vCalc/Haversine+-+Distance

const val EARTH_RADIUS_IN_METERS = 6371007.177356707

fun distance(lat1: Double, lng1: Double, lat2: Double, lng2: Double): Double {
    val latDiff = Math.toRadians(abs(lat2 - lat1))
    val lngDiff = Math.toRadians(abs(lng2 - lng1))
    val a = sin(latDiff / 2) * sin(latDiff / 2) +
        cos(Math.toRadians(lat1)) * cos(Math.toRadians(lat2)) *
        sin(lngDiff / 2) * sin(lngDiff / 2)
    val c = 2 * atan2(sqrt(a), sqrt(1 - a))
    return EARTH_RADIUS_IN_METERS * c
}
Pasteurizer answered 4/8, 2019 at 10:59 Comment(1)
hey so I can use this as a geofence right???? The geofence code on android is not working for me for some unknown reason and I was thinking to use this as an alternativeOssuary
O
0

I typically use MATLAB with the Mapping Toolbox, and then use the code in my Java using MATLAB Builder JA. It makes my life a lot simpler. Given most schools have it for free student access, you can try it out (or get the trial version to get over your work).

Oahu answered 13/9, 2013 at 12:43 Comment(0)
S
0

For Android, there is a simple approach.

 public static float getDistanceInMeter(LatLng start, LatLng end) { 
    float[] results = new float[1];
    Location.distanceBetween(start.latitude, start.longitude, end.latitude, end.longitude, results);
    return results[0];

}

;

https://developer.android.com/reference/android/location/Location#distanceBetween(lat1,lng1,lat2,lng2,output[])

Skylark answered 30/11, 2018 at 14:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.