BAEL-6868: Implement Distance Calculation formulas. (#14576)
This commit is contained in:
parent
854f240059
commit
12a678018f
|
@ -0,0 +1,19 @@
|
|||
package com.baeldung.algorithms.latlondistance;
|
||||
|
||||
public class EquirectangularApproximation {
|
||||
|
||||
private static final int EARTH_RADIUS = 6371; // Approx Earth radius in KM
|
||||
|
||||
public static double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
|
||||
double lat1Rad = Math.toRadians(lat1);
|
||||
double lat2Rad = Math.toRadians(lat2);
|
||||
double lon1Rad = Math.toRadians(lon1);
|
||||
double lon2Rad = Math.toRadians(lon2);
|
||||
|
||||
double x = (lon2Rad - lon1Rad) * Math.cos((lat1Rad + lat2Rad) / 2);
|
||||
double y = (lat2Rad - lat1Rad);
|
||||
double distance = Math.sqrt(x * x + y * y) * EARTH_RADIUS;
|
||||
|
||||
return distance;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package com.baeldung.algorithms.latlondistance;
|
||||
|
||||
public class HaversineDistance {
|
||||
private static final int EARTH_RADIUS = 6371; // Approx Earth radius in KM
|
||||
|
||||
public static double calculateDistance(double startLat, double startLong,
|
||||
double endLat, double endLong) {
|
||||
|
||||
double dLat = Math.toRadians((endLat - startLat));
|
||||
double dLong = Math.toRadians((endLong - startLong));
|
||||
|
||||
startLat = Math.toRadians(startLat);
|
||||
endLat = Math.toRadians(endLat);
|
||||
|
||||
double a = haversine(dLat) + Math.cos(startLat) * Math.cos(endLat) * haversine(dLong);
|
||||
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||
|
||||
return EARTH_RADIUS * c;
|
||||
}
|
||||
|
||||
public static double haversine(double val) {
|
||||
return Math.pow(Math.sin(val / 2), 2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package com.baeldung.algorithms.latlondistance;
|
||||
|
||||
public class VincentyDistance {
|
||||
|
||||
// Constants for WGS84 ellipsoid model of Earth
|
||||
private static final double SEMI_MAJOR_AXIS_MT = 6378137;
|
||||
private static final double SEMI_MINOR_AXIS_MT = 6356752.314245;
|
||||
private static final double FLATTENING = 1 / 298.257223563;
|
||||
private static final double ERROR_TOLERANCE = 1e-12;
|
||||
|
||||
public static double calculateDistance(double latitude1, double longitude1, double latitude2, double longitude2) {
|
||||
double U1 = Math.atan((1 - FLATTENING) * Math.tan(Math.toRadians(latitude1)));
|
||||
double U2 = Math.atan((1 - FLATTENING) * Math.tan(Math.toRadians(latitude2)));
|
||||
|
||||
double sinU1 = Math.sin(U1);
|
||||
double cosU1 = Math.cos(U1);
|
||||
double sinU2 = Math.sin(U2);
|
||||
double cosU2 = Math.cos(U2);
|
||||
|
||||
double longitudeDifference = Math.toRadians(longitude2 - longitude1);
|
||||
double previousLongitudeDifference;
|
||||
|
||||
double sinSigma, cosSigma, sigma, sinAlpha, cosSqAlpha, cos2SigmaM;
|
||||
|
||||
do {
|
||||
sinSigma = Math.sqrt(Math.pow(cosU2 * Math.sin(longitudeDifference), 2) +
|
||||
Math.pow(cosU1 * sinU2 - sinU1 * cosU2 * Math.cos(longitudeDifference), 2));
|
||||
cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * Math.cos(longitudeDifference);
|
||||
sigma = Math.atan2(sinSigma, cosSigma);
|
||||
sinAlpha = cosU1 * cosU2 * Math.sin(longitudeDifference) / sinSigma;
|
||||
cosSqAlpha = 1 - Math.pow(sinAlpha, 2);
|
||||
cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha;
|
||||
if (Double.isNaN(cos2SigmaM)) {
|
||||
cos2SigmaM = 0;
|
||||
}
|
||||
previousLongitudeDifference = longitudeDifference;
|
||||
double C = FLATTENING / 16 * cosSqAlpha * (4 + FLATTENING * (4 - 3 * cosSqAlpha));
|
||||
longitudeDifference = Math.toRadians(longitude2 - longitude1) + (1 - C) * FLATTENING * sinAlpha *
|
||||
(sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * Math.pow(cos2SigmaM, 2))));
|
||||
} while (Math.abs(longitudeDifference - previousLongitudeDifference) > ERROR_TOLERANCE);
|
||||
|
||||
double uSq = cosSqAlpha * (Math.pow(SEMI_MAJOR_AXIS_MT, 2) - Math.pow(SEMI_MINOR_AXIS_MT, 2)) / Math.pow(SEMI_MINOR_AXIS_MT, 2);
|
||||
|
||||
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 * Math.pow(cos2SigmaM, 2)) -
|
||||
B / 6 * cos2SigmaM * (-3 + 4 * Math.pow(sinSigma, 2)) * (-3 + 4 * Math.pow(cos2SigmaM, 2))));
|
||||
|
||||
double distanceMt = SEMI_MINOR_AXIS_MT * A * (sigma - deltaSigma);
|
||||
return distanceMt / 1000;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.baeldung.algorithms.latlondistance;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class GeoDistanceUnitTest {
|
||||
@Test
|
||||
public void testCalculateDistance() {
|
||||
double lat1 = 40.714268; // New York
|
||||
double lon1 = -74.005974;
|
||||
double lat2 = 34.0522; // Los Angeles
|
||||
double lon2 = -118.2437;
|
||||
|
||||
double equirectangularDistance = EquirectangularApproximation.calculateDistance(lat1, lon1, lat2, lon2);
|
||||
double haversineDistance = HaversineDistance.calculateDistance(lat1, lon1, lat2, lon2);
|
||||
double vincentyDistance = VincentyDistance.calculateDistance(lat1, lon1, lat2, lon2);
|
||||
|
||||
double expectedDistance = 3944;
|
||||
assertTrue(Math.abs(equirectangularDistance - expectedDistance) < 100);
|
||||
assertTrue(Math.abs(haversineDistance - expectedDistance) < 10);
|
||||
assertTrue(Math.abs(vincentyDistance - expectedDistance) < 0.5);
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue