1

I'm trying to figure out how to represent geographic locations, and I can't seem to find any relevant classes in the SE documentation.

I would like to do something like this:

Location locA = new Location(aa.aaaaaaa, bb.bbbbbbb); //lat/long coordinates
Location locB = .....
int meters = locA.distanceTo(locB);

Ideally I would want something like the Android Location since most of my location data will be sent from android devices anyway. From my understanding it contains information about accuracy, latitude and speed, which would be useful (but not required).

I have an idea of how to implement it myself, but an open source alternative would save me some time.

It would also be extremely helpful if I could do some IP/Location lookup. But I guess that's a whole other issue.

Community
  • 1
  • 1
Frigo
  • 284
  • 4
  • 15

1 Answers1

0

Here is a method to calculate distance between 2 points, taken from Android Location class with small modification. It looks a little bit complicated--this is because it uses Vincenty's formulae to perform iterative calculations on WGS84 ellipsoid:

/** distance between 2 geographic points on Earth, in km **/
public static double geoDistance(GeoPoint gp1, GeoPoint gp2) {
        // Based on http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf
        // using the "Inverse Formula" (section 4)

        int MAXITERS = 20;
        // Convert lat/long to radians
        double lat1 = gp1.getLat() * Math.PI / 180.0;
        double lat2 = gp2.getLat() * Math.PI / 180.0;
        double lon1 = gp1.getLon() * Math.PI / 180.0;
        double lon2 = gp2.getLon() * Math.PI / 180.0;

        double a = 6378.137; // WGS84 major axis
        double b = 6356.7523142; // WGS84 semi-major axis
        double f = (a - b) / a;
        double aSqMinusBSqOverBSq = (a * a - b * b) / (b * b);

        double L = lon2 - lon1;
        double A = 0.0;
        double U1 = Math.atan((1.0 - f) * Math.tan(lat1));
        double U2 = Math.atan((1.0 - f) * Math.tan(lat2));

        double cosU1 = Math.cos(U1);
        double cosU2 = Math.cos(U2);
        double sinU1 = Math.sin(U1);
        double sinU2 = Math.sin(U2);
        double cosU1cosU2 = cosU1 * cosU2;
        double sinU1sinU2 = sinU1 * sinU2;

        double sigma = 0.0;
        double deltaSigma = 0.0;
        double cosSqAlpha = 0.0;
        double cos2SM = 0.0;
        double cosSigma = 0.0;
        double sinSigma = 0.0;
        double cosLambda = 0.0;
        double sinLambda = 0.0;

        double lambda = L; // initial guess
        for (int iter = 0; iter < MAXITERS; iter++) {
            double lambdaOrig = lambda;
            cosLambda = Math.cos(lambda);
            sinLambda = Math.sin(lambda);
            double t1 = cosU2 * sinLambda;
            double t2 = cosU1 * sinU2 - sinU1 * cosU2 * cosLambda;
            double sinSqSigma = t1 * t1 + t2 * t2; // (14)
            sinSigma = Math.sqrt(sinSqSigma);
            cosSigma = sinU1sinU2 + cosU1cosU2 * cosLambda; // (15)
            sigma = Math.atan2(sinSigma, cosSigma); // (16)
            double sinAlpha = (sinSigma == 0) ? 0.0 :
                cosU1cosU2 * sinLambda / sinSigma; // (17)
            cosSqAlpha = 1.0 - sinAlpha * sinAlpha;
            cos2SM = (cosSqAlpha == 0) ? 0.0 :
                cosSigma - 2.0 * sinU1sinU2 / cosSqAlpha; // (18)

            double uSquared = cosSqAlpha * aSqMinusBSqOverBSq; // defn
            A = 1 + (uSquared / 16384.0) * // (3)
                (4096.0 + uSquared *
                 (-768 + uSquared * (320.0 - 175.0 * uSquared)));
            double B = (uSquared / 1024.0) * // (4)
                (256.0 + uSquared *
                 (-128.0 + uSquared * (74.0 - 47.0 * uSquared)));
            double C = (f / 16.0) *
                cosSqAlpha *
                (4.0 + f * (4.0 - 3.0 * cosSqAlpha)); // (10)
            double cos2SMSq = cos2SM * cos2SM;
            deltaSigma = B * sinSigma * // (6)
                (cos2SM + (B / 4.0) *
                 (cosSigma * (-1.0 + 2.0 * cos2SMSq) -
                  (B / 6.0) * cos2SM *
                  (-3.0 + 4.0 * sinSigma * sinSigma) *
                  (-3.0 + 4.0 * cos2SMSq)));

            lambda = L +
                (1.0 - C) * f * sinAlpha *
                (sigma + C * sinSigma *
                 (cos2SM + C * cosSigma *
                  (-1.0 + 2.0 * cos2SM * cos2SM))); // (11)

            double delta = (lambda - lambdaOrig) / lambda;
            if (Math.abs(delta) < 1.0e-12) {
                break;
            }
        }

        return b * A * (sigma - deltaSigma);
    }

GeoPoint class looks like the following:

/**
 * Immutable point in geo coordinates (latitude, longitude) with accuracy in km
 */
public class GeoPoint {

    private final double lat;
    private final double lon;
    private final double accuracy;

    /**
     * New geo point without accuracy
     */
    public GeoPoint(double lat, double lon){
        this(lat, lon, -1d);
    }

    /**
     * New geo point with specified accuracy
     * @param accuracy  accuracy in km
     */
    public GeoPoint(double lat, double lon, double accuracy){
        this.lat = lat;
        this.lon = lon;
        this.accuracy = accuracy < 0 ? -1d : accuracy;
    }

    public double getLat(){
        return this.lat;
    }

    public double getLon(){
        return this.lon;
    }

    /**
     * @return accuracy in km. If < 0, accuracy is not defined
     */
    public double getAccuracy(){
        return this.accuracy;
    }

    @Override
    public String toString(){
        return "lat = " + this.lat + "; lon = " + this.lon + (this.accuracy < 0 ? "" : ("; accuracy = " + this.accuracy));
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof GeoPoint) || o == null) return false;
        GeoPoint g = (GeoPoint) o;
        return g.lat == this.lat && g.lon == this.lon && g.accuracy == this.accuracy; 
    }


}
Alex Salauyou
  • 14,185
  • 5
  • 45
  • 67
  • Hey thanks for your answer. I ended up doing something similar to that, but using another method for calculating distance as seen in [this answer](http://stackoverflow.com/a/16794680/2279621). And implemented my own GeoLoc class. It doesn't have quite all the functionality I was looking for, but it's enough for the time being. – Frigo Apr 27 '15 at 12:58
  • That didn't took much time because I have ready code. Formula from answer you mentioned is okay for short distances since it treats the Earth as sphere, not ellipsoid. Anyway, you're welcome! – Alex Salauyou Apr 27 '15 at 13:03