diff --git a/algorithms-modules/algorithms-miscellaneous-7/src/main/java/com/baeldung/algorithms/latlondistance/EquirectangularApproximation.java b/algorithms-modules/algorithms-miscellaneous-7/src/main/java/com/baeldung/algorithms/latlondistance/EquirectangularApproximation.java new file mode 100644 index 0000000000..e42475c17e --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-7/src/main/java/com/baeldung/algorithms/latlondistance/EquirectangularApproximation.java @@ -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; + } +} \ No newline at end of file diff --git a/algorithms-modules/algorithms-miscellaneous-7/src/main/java/com/baeldung/algorithms/latlondistance/HaversineDistance.java b/algorithms-modules/algorithms-miscellaneous-7/src/main/java/com/baeldung/algorithms/latlondistance/HaversineDistance.java new file mode 100644 index 0000000000..69074ec559 --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-7/src/main/java/com/baeldung/algorithms/latlondistance/HaversineDistance.java @@ -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); + } +} diff --git a/algorithms-modules/algorithms-miscellaneous-7/src/main/java/com/baeldung/algorithms/latlondistance/VincentyDistance.java b/algorithms-modules/algorithms-miscellaneous-7/src/main/java/com/baeldung/algorithms/latlondistance/VincentyDistance.java new file mode 100644 index 0000000000..7d0b0b907f --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-7/src/main/java/com/baeldung/algorithms/latlondistance/VincentyDistance.java @@ -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; + } +} \ No newline at end of file diff --git a/algorithms-modules/algorithms-miscellaneous-7/src/test/java/com/baeldung/algorithms/latlondistance/GeoDistanceUnitTest.java b/algorithms-modules/algorithms-miscellaneous-7/src/test/java/com/baeldung/algorithms/latlondistance/GeoDistanceUnitTest.java new file mode 100644 index 0000000000..9e72f86287 --- /dev/null +++ b/algorithms-modules/algorithms-miscellaneous-7/src/test/java/com/baeldung/algorithms/latlondistance/GeoDistanceUnitTest.java @@ -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); + + } + +} \ No newline at end of file