From a6837a533ef29e19c4fdff70dc4e28ecf05073ae Mon Sep 17 00:00:00 2001 From: Romain Gallet Date: Sat, 3 Oct 2020 20:36:46 +0100 Subject: [PATCH 1/7] Removed deprecated methods --- .../java/com/grum/geocalc/BoundingArea.java | 6 ++-- .../java/com/grum/geocalc/Coordinate.java | 11 ------- .../java/com/grum/geocalc/DMSCoordinate.java | 29 ------------------- .../com/grum/geocalc/DegreeCoordinate.java | 4 +-- src/main/java/com/grum/geocalc/EarthCalc.java | 4 +-- .../java/com/grum/geocalc/GPSCoordinate.java | 2 -- src/main/java/com/grum/geocalc/Point.java | 2 -- .../com/grum/geocalc/RadianCoordinate.java | 11 ------- 8 files changed, 4 insertions(+), 65 deletions(-) diff --git a/src/main/java/com/grum/geocalc/BoundingArea.java b/src/main/java/com/grum/geocalc/BoundingArea.java index 90d5cbc..5b38e17 100644 --- a/src/main/java/com/grum/geocalc/BoundingArea.java +++ b/src/main/java/com/grum/geocalc/BoundingArea.java @@ -35,10 +35,8 @@ import java.util.Objects; /** - * Represents an area (viewed rectangular shaped projected onto Earth), defined by its top left and bottom right + * Represents an area (rectangular shape projected onto Earth), defined by its top left and bottom right * coordinates - * - * @author rgallet */ public class BoundingArea { public final Point northEast, southWest; @@ -98,7 +96,7 @@ public boolean contains(Point point) { @Override public boolean equals(Object obj) { - if (obj == null) { + if (Objects.isNull(obj)) { return false; } diff --git a/src/main/java/com/grum/geocalc/Coordinate.java b/src/main/java/com/grum/geocalc/Coordinate.java index bd146b2..bda5eb4 100644 --- a/src/main/java/com/grum/geocalc/Coordinate.java +++ b/src/main/java/com/grum/geocalc/Coordinate.java @@ -40,22 +40,11 @@ /** * Abstraction of coordinate systems (degrees, radians, dms and gps) - * - * @author rgallet */ abstract public class Coordinate implements Serializable { abstract double degrees(); - /** - * @return degree value - * @deprecated use degrees() - */ - @Deprecated - public double getValue() { - return degrees(); - } - public static DegreeCoordinate fromDegrees(double decimalDegrees) { return new DegreeCoordinate(decimalDegrees); } diff --git a/src/main/java/com/grum/geocalc/DMSCoordinate.java b/src/main/java/com/grum/geocalc/DMSCoordinate.java index 6b5926c..b16eabb 100644 --- a/src/main/java/com/grum/geocalc/DMSCoordinate.java +++ b/src/main/java/com/grum/geocalc/DMSCoordinate.java @@ -37,8 +37,6 @@ /** * Represents coordinates given in * Degrees Minutes decimal-seconds (D M s) format - * - * @author rgallet */ public class DMSCoordinate extends Coordinate { @@ -61,33 +59,6 @@ public class DMSCoordinate extends Coordinate { return decimalDegrees; } - /** - * @return minutes - * @deprecated use minutes - */ - @Deprecated - public double getMinutes() { - return minutes; - } - - /** - * @return wholeDegrees - * @deprecated use wholeDegrees - */ - @Deprecated - public double getWholeDegrees() { - return wholeDegrees; - } - - /** - * @return seconds - * @deprecated use seconds - */ - @Deprecated - public double getSeconds() { - return seconds; - } - @Override public String toString() { return "DMSCoordinate{" + diff --git a/src/main/java/com/grum/geocalc/DegreeCoordinate.java b/src/main/java/com/grum/geocalc/DegreeCoordinate.java index 908a25e..5918d9d 100644 --- a/src/main/java/com/grum/geocalc/DegreeCoordinate.java +++ b/src/main/java/com/grum/geocalc/DegreeCoordinate.java @@ -35,12 +35,10 @@ /** * Represents coordinates given in * decimal-degrees (d) format - * - * @author rgallet */ public class DegreeCoordinate extends Coordinate { - final double decimalDegrees; + public final double decimalDegrees; DegreeCoordinate(double decimalDegrees) { this.decimalDegrees = decimalDegrees; diff --git a/src/main/java/com/grum/geocalc/EarthCalc.java b/src/main/java/com/grum/geocalc/EarthCalc.java index 7db64e5..de80447 100644 --- a/src/main/java/com/grum/geocalc/EarthCalc.java +++ b/src/main/java/com/grum/geocalc/EarthCalc.java @@ -36,12 +36,10 @@ /** * Earth related calculations. - * - * @author rgallet */ public class EarthCalc { - private static final double EARTH_RADIUS = 6371.01 * 1000; //meters + public static final double EARTH_RADIUS = 6371.01 * 1000; //meters /** * This is the half-way point along a great circle path between the two points. diff --git a/src/main/java/com/grum/geocalc/GPSCoordinate.java b/src/main/java/com/grum/geocalc/GPSCoordinate.java index 04389ed..28952af 100644 --- a/src/main/java/com/grum/geocalc/GPSCoordinate.java +++ b/src/main/java/com/grum/geocalc/GPSCoordinate.java @@ -35,8 +35,6 @@ /** * Represents coordinates given in * Degrees decimal-minutes (D m) format - * - * @author rgallet */ public class GPSCoordinate extends DMSCoordinate { diff --git a/src/main/java/com/grum/geocalc/Point.java b/src/main/java/com/grum/geocalc/Point.java index a5c0f53..c36933e 100644 --- a/src/main/java/com/grum/geocalc/Point.java +++ b/src/main/java/com/grum/geocalc/Point.java @@ -36,8 +36,6 @@ /** * Represent a point in spherical system - * - * @author rgallet */ public class Point implements Serializable { //decimal degrees diff --git a/src/main/java/com/grum/geocalc/RadianCoordinate.java b/src/main/java/com/grum/geocalc/RadianCoordinate.java index d7de850..e7e6aa9 100644 --- a/src/main/java/com/grum/geocalc/RadianCoordinate.java +++ b/src/main/java/com/grum/geocalc/RadianCoordinate.java @@ -35,8 +35,6 @@ /** * Represents coordinates given in * radian-degrees (r) format - * - * @author rgallet */ public class RadianCoordinate extends Coordinate { @@ -50,13 +48,4 @@ public class RadianCoordinate extends Coordinate { double degrees() { return Math.toDegrees(radians); } - - /** - * @return angle value - * @deprecated use radians - */ - @Deprecated - public double getRadians() { - return radians; - } } From b4606c7bcaa4c5142534d011c3fe0c75baa62e1b Mon Sep 17 00:00:00 2001 From: Romain Gallet Date: Sat, 3 Oct 2020 20:38:40 +0100 Subject: [PATCH 2/7] Moved log4j.properties to test/ --- pom.xml | 8 ++++---- src/{main => test}/resources/log4j.properties | 0 2 files changed, 4 insertions(+), 4 deletions(-) rename src/{main => test}/resources/log4j.properties (100%) diff --git a/pom.xml b/pom.xml index 46294b2..a96e296 100644 --- a/pom.xml +++ b/pom.xml @@ -7,9 +7,9 @@ com.grum geocalc jar - 0.5.8 + 0.6 ${project.groupId}.${project.artifactId} - http://www.grumlimited.co.uk + httpd://www.grumlimited.co.uk 3.1.0 @@ -24,12 +24,12 @@ Grum Ltd - http://www.grumlimited.co.uk + httpd://www.grumlimited.co.uk GR211 - romain.gallet__at__gmail.com + rgallet_at_grumlimited_co_uk Grum Limited http://www.grumlimited.co.uk diff --git a/src/main/resources/log4j.properties b/src/test/resources/log4j.properties similarity index 100% rename from src/main/resources/log4j.properties rename to src/test/resources/log4j.properties From e8012af7d3c39d4e3e252df67ec2e1824923abc3 Mon Sep 17 00:00:00 2001 From: Romain Gallet Date: Sat, 3 Oct 2020 21:48:37 +0100 Subject: [PATCH 3/7] Lombok --- pom.xml | 9 +- .../java/com/grum/geocalc/BoundingArea.java | 38 +--- .../java/com/grum/geocalc/Coordinate.java | 20 +- .../java/com/grum/geocalc/DMSCoordinate.java | 13 +- .../com/grum/geocalc/DegreeCoordinate.java | 8 +- src/main/java/com/grum/geocalc/EarthCalc.java | 129 +++++------ src/main/java/com/grum/geocalc/Point.java | 34 +-- .../com/grum/geocalc/RadianCoordinate.java | 9 +- .../java/com/grum/geocalc/CoordinateTest.java | 82 +++---- .../java/com/grum/geocalc/DistanceTest.java | 205 +++++++++--------- 10 files changed, 263 insertions(+), 284 deletions(-) diff --git a/pom.xml b/pom.xml index a96e296..effd20c 100644 --- a/pom.xml +++ b/pom.xml @@ -80,7 +80,7 @@ org.codehaus.mojo versions-maven-plugin - 2.3 + 2.8.1 @@ -93,6 +93,13 @@ 1.7.30 + + org.projectlombok + lombok + 1.18.12 + provided + + org.slf4j slf4j-log4j12 diff --git a/src/main/java/com/grum/geocalc/BoundingArea.java b/src/main/java/com/grum/geocalc/BoundingArea.java index 5b38e17..23f48b5 100644 --- a/src/main/java/com/grum/geocalc/BoundingArea.java +++ b/src/main/java/com/grum/geocalc/BoundingArea.java @@ -32,12 +32,16 @@ package com.grum.geocalc; -import java.util.Objects; +import lombok.EqualsAndHashCode; +import lombok.val; /** * Represents an area (rectangular shape projected onto Earth), defined by its top left and bottom right - * coordinates + * coordinates. + *

+ * Allows for fast geo-spatial search of all points within this rectangular area. */ +@EqualsAndHashCode public class BoundingArea { public final Point northEast, southWest; public final Point southEast, northWest; @@ -73,7 +77,7 @@ public boolean isContainedWithin(Point point) { * @return true if Point point is contained withing this bounding area */ public boolean contains(Point point) { - boolean predicate1 = point.latitude >= this.southWest.latitude && point.latitude <= this.northEast.latitude; + val predicate1 = point.latitude >= this.southWest.latitude && point.latitude <= this.northEast.latitude; if (!predicate1) { return false; @@ -83,8 +87,8 @@ public boolean contains(Point point) { if (southWest.longitude > northEast.longitude) { //area is going across the max/min longitude boundaries (ie. sort of back of the Earth) //we "split" the area in 2, longitude-wise, point only needs to be in one or the other. - boolean predicate3 = point.longitude <= northEast.longitude && point.longitude >= -180; - boolean predicate4 = point.longitude >= southWest.longitude && point.longitude <= 180; + val predicate3 = point.longitude <= northEast.longitude && point.longitude >= -180; + val predicate4 = point.longitude >= southWest.longitude && point.longitude <= 180; predicate2 = predicate3 || predicate4; } else { @@ -93,28 +97,4 @@ public boolean contains(Point point) { return predicate2; } - - @Override - public boolean equals(Object obj) { - if (Objects.isNull(obj)) { - return false; - } - - if (getClass() != obj.getClass()) { - return false; - } - - BoundingArea other = (BoundingArea) obj; - - return Objects.equals(this.northEast, other.northEast) && - Objects.equals(this.southWest, other.southWest); - } - - @Override - public int hashCode() { - int hash = 7; - hash = 13 * hash + (this.northEast != null ? this.northEast.hashCode() : 0); - hash = 13 * hash + (this.southWest != null ? this.southWest.hashCode() : 0); - return hash; - } } diff --git a/src/main/java/com/grum/geocalc/Coordinate.java b/src/main/java/com/grum/geocalc/Coordinate.java index bda5eb4..6e0705e 100644 --- a/src/main/java/com/grum/geocalc/Coordinate.java +++ b/src/main/java/com/grum/geocalc/Coordinate.java @@ -31,6 +31,8 @@ */ package com.grum.geocalc; +import lombok.val; +import lombok.var; import java.io.Serializable; import java.math.BigDecimal; @@ -62,13 +64,13 @@ public static RadianCoordinate fromRadians(double radians) { } DMSCoordinate toDMSCoordinate() { - double _wholeDegrees = (int) degrees(); - double remaining = abs(degrees() - _wholeDegrees); - double _minutes = (int) (remaining * 60); - remaining = remaining * 60 - _minutes; - double _seconds = new BigDecimal(remaining * 60).setScale(4, RoundingMode.HALF_UP).doubleValue(); + val wholeDegrees = (int) degrees(); + var remaining = abs(degrees() - wholeDegrees); + val minutes = (int) (remaining * 60); + remaining = remaining * 60 - minutes; + val seconds = new BigDecimal(remaining * 60).setScale(4, RoundingMode.HALF_UP).doubleValue(); - return new DMSCoordinate(_wholeDegrees, _minutes, _seconds); + return new DMSCoordinate(wholeDegrees, minutes, seconds); } DegreeCoordinate toDegreeCoordinate() { @@ -76,9 +78,9 @@ DegreeCoordinate toDegreeCoordinate() { } GPSCoordinate toGPSCoordinate() { - double _wholeDegrees = floor(degrees()); - double remaining = degrees() - _wholeDegrees; - double _minutes = floor(remaining * 60); + val _wholeDegrees = floor(degrees()); + val remaining = degrees() - _wholeDegrees; + val _minutes = floor(remaining * 60); return new GPSCoordinate(_wholeDegrees, _minutes); } diff --git a/src/main/java/com/grum/geocalc/DMSCoordinate.java b/src/main/java/com/grum/geocalc/DMSCoordinate.java index b16eabb..0adc434 100644 --- a/src/main/java/com/grum/geocalc/DMSCoordinate.java +++ b/src/main/java/com/grum/geocalc/DMSCoordinate.java @@ -32,25 +32,24 @@ package com.grum.geocalc; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.var; + import static java.lang.Math.abs; /** * Represents coordinates given in * Degrees Minutes decimal-seconds (D M s) format */ +@AllArgsConstructor(access = AccessLevel.PACKAGE) public class DMSCoordinate extends Coordinate { public final double wholeDegrees, minutes, seconds; - DMSCoordinate(double wholeDegrees, double minutes, double seconds) { - this.wholeDegrees = wholeDegrees; - this.minutes = minutes; - this.seconds = seconds; - } - @Override double degrees() { - double decimalDegrees = abs(wholeDegrees) + minutes / 60 + seconds / 3600; + var decimalDegrees = abs(wholeDegrees) + minutes / 60 + seconds / 3600; if (wholeDegrees < 0) { decimalDegrees = -decimalDegrees; diff --git a/src/main/java/com/grum/geocalc/DegreeCoordinate.java b/src/main/java/com/grum/geocalc/DegreeCoordinate.java index 5918d9d..abb7f8a 100644 --- a/src/main/java/com/grum/geocalc/DegreeCoordinate.java +++ b/src/main/java/com/grum/geocalc/DegreeCoordinate.java @@ -32,18 +32,18 @@ package com.grum.geocalc; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; + /** * Represents coordinates given in * decimal-degrees (d) format */ +@AllArgsConstructor(access = AccessLevel.PACKAGE) public class DegreeCoordinate extends Coordinate { public final double decimalDegrees; - DegreeCoordinate(double decimalDegrees) { - this.decimalDegrees = decimalDegrees; - } - @Override double degrees() { return decimalDegrees; diff --git a/src/main/java/com/grum/geocalc/EarthCalc.java b/src/main/java/com/grum/geocalc/EarthCalc.java index de80447..dd5d74b 100644 --- a/src/main/java/com/grum/geocalc/EarthCalc.java +++ b/src/main/java/com/grum/geocalc/EarthCalc.java @@ -32,6 +32,10 @@ package com.grum.geocalc; +import lombok.AllArgsConstructor; +import lombok.val; +import lombok.var; + import static java.lang.Math.*; /** @@ -39,7 +43,7 @@ */ public class EarthCalc { - public static final double EARTH_RADIUS = 6371.01 * 1000; //meters + public static final double EARTH_RADIUS = 6_356_752.314245D; // radius at the poles, meters /** * This is the half-way point along a great circle path between the two points. @@ -50,17 +54,17 @@ public class EarthCalc { * @see */ public static Point midPoint(Point standPoint, Point forePoint) { - double λ1 = toRadians(standPoint.longitude); - double λ2 = toRadians(forePoint.longitude); + val λ1 = toRadians(standPoint.longitude); + val λ2 = toRadians(forePoint.longitude); - double φ1 = toRadians(standPoint.latitude); - double φ2 = toRadians(forePoint.latitude); + val φ1 = toRadians(standPoint.latitude); + val φ2 = toRadians(forePoint.latitude); - double Bx = cos(φ2) * cos(λ2 - λ1); - double By = cos(φ2) * sin(λ2 - λ1); + val Bx = cos(φ2) * cos(λ2 - λ1); + val By = cos(φ2) * sin(λ2 - λ1); - double φ3 = atan2(sin(φ1) + sin(φ2), sqrt((cos(φ1) + Bx) * (cos(φ1) + Bx) + By * By)); - double λ3 = λ1 + atan2(By, cos(φ1) + Bx); + val φ3 = atan2(sin(φ1) + sin(φ2), sqrt((cos(φ1) + Bx) * (cos(φ1) + Bx) + By * By)); + val λ3 = λ1 + atan2(By, cos(φ1) + Bx); return Point.at(Coordinate.fromRadians(φ3), Coordinate.fromRadians(λ3)); } @@ -74,14 +78,13 @@ public static Point midPoint(Point standPoint, Point forePoint) { */ public static double gcdDistance(Point standPoint, Point forePoint) { - double diffLongitudes = toRadians(abs(forePoint.longitude - standPoint.longitude)); - double slat = toRadians(standPoint.latitude); - double flat = toRadians(forePoint.latitude); + val Δλ = toRadians(abs(forePoint.longitude - standPoint.longitude)); + val φ1 = toRadians(standPoint.latitude); + val φ2 = toRadians(forePoint.latitude); //spherical law of cosines - - double sphereCos = (sin(slat) * sin(flat)) + (cos(slat) * cos(flat) * cos(diffLongitudes)); - double c = acos(max(min(sphereCos, 1d), -1d)); + val sphereCos = (sin(φ1) * sin(φ2)) + (cos(φ1) * cos(φ2) * cos(Δλ)); + val c = acos(max(min(sphereCos, 1d), -1d)); return EARTH_RADIUS * c; } @@ -95,14 +98,14 @@ public static double gcdDistance(Point standPoint, Point forePoint) { */ public static double haversineDistance(Point standPoint, Point forePoint) { - double diffLongitudes = toRadians(abs(forePoint.longitude - standPoint.longitude)); - double slat = toRadians(standPoint.latitude); - double flat = toRadians(forePoint.latitude); + val Δλ = toRadians(abs(forePoint.longitude - standPoint.longitude)); + val φ1 = toRadians(standPoint.latitude); + val φ2 = toRadians(forePoint.latitude); // haversine formula - double diffLatitudes = toRadians(abs(forePoint.latitude - standPoint.latitude)); - double a = sin(diffLatitudes / 2) * sin(diffLatitudes / 2) + cos(slat) * cos(flat) * sin(diffLongitudes / 2) * sin(diffLongitudes / 2); - double c = 2 * atan2(sqrt(a), sqrt(1 - a)); //angular distance in radians + val Δφ = toRadians(abs(forePoint.latitude - standPoint.latitude)); + val a = sin(Δφ / 2) * sin(Δφ / 2) + cos(φ1) * cos(φ2) * sin(Δλ / 2) * sin(Δλ / 2); + val c = 2 * atan2(sqrt(a), sqrt(1 - a)); //angular distance in radians return EARTH_RADIUS * c; } @@ -115,53 +118,58 @@ public static double haversineDistance(Point standPoint, Point forePoint) { * @return Vincenty object which holds all 3 values */ private static Vincenty vincenty(Point standPoint, Point forePoint) { - double λ1 = toRadians(standPoint.longitude); - double λ2 = toRadians(forePoint.longitude); + val λ1 = toRadians(standPoint.longitude); + val λ2 = toRadians(forePoint.longitude); + + val φ1 = toRadians(standPoint.latitude); + val φ2 = toRadians(forePoint.latitude); - double φ1 = toRadians(standPoint.latitude); - double φ2 = toRadians(forePoint.latitude); + val a = 6_378_137D; // radius at equator + val b = EARTH_RADIUS; // Using b to keep close to academic formula. + val f = 1 / 298.257223563D; // flattening of the ellipsoid - double a = 6_378_137; - double b = 6_356_752.314245; - double f = 1 / 298.257223563; + val L = λ2 - λ1; + val tanU1 = (1 - f) * tan(φ1); + val cosU1 = 1 / sqrt((1 + tanU1 * tanU1)); + val sinU1 = tanU1 * cosU1; - double L = λ2 - λ1; - double tanU1 = (1 - f) * tan(φ1), cosU1 = 1 / sqrt((1 + tanU1 * tanU1)), sinU1 = tanU1 * cosU1; - double tanU2 = (1 - f) * tan(φ2), cosU2 = 1 / sqrt((1 + tanU2 * tanU2)), sinU2 = tanU2 * cosU2; + val tanU2 = (1 - f) * tan(φ2); + val cosU2 = 1 / sqrt((1 + tanU2 * tanU2)); + val sinU2 = tanU2 * cosU2; double λ = L, λʹ, iterationLimit = 100, cosSqα, σ, cos2σM, cosσ, sinσ, sinλ, cosλ; do { sinλ = sin(λ); cosλ = cos(λ); - double sinSqσ = (cosU2 * sinλ) * (cosU2 * sinλ) + (cosU1 * sinU2 - sinU1 * cosU2 * cosλ) * (cosU1 * sinU2 - sinU1 * cosU2 * cosλ); + val sinSqσ = (cosU2 * sinλ) * (cosU2 * sinλ) + (cosU1 * sinU2 - sinU1 * cosU2 * cosλ) * (cosU1 * sinU2 - sinU1 * cosU2 * cosλ); sinσ = sqrt(sinSqσ); - if (sinσ == 0) return new Vincenty(0, 0, 0); // co-incident points + if (sinσ == 0) return Vincenty.CO_INCIDENT_POINTS; // co-incident points cosσ = sinU1 * sinU2 + cosU1 * cosU2 * cosλ; σ = atan2(sinσ, cosσ); - double sinα = cosU1 * cosU2 * sinλ / sinσ; + val sinα = cosU1 * cosU2 * sinλ / sinσ; cosSqα = 1 - sinα * sinα; cos2σM = cosσ - 2 * sinU1 * sinU2 / cosSqα; - if (Double.isNaN(cos2σM)) cos2σM = 0; // equatorial line: cosSqα=0 (§6) - double C = f / 16 * cosSqα * (4 + f * (4 - 3 * cosSqα)); + if (Double.isNaN(cos2σM)) cos2σM = 0; // equatorial line: cosSqα=0 + val C = f / 16 * cosSqα * (4 + f * (4 - 3 * cosSqα)); λʹ = λ; λ = L + (1 - C) * f * sinα * (σ + C * sinσ * (cos2σM + C * cosσ * (-1 + 2 * cos2σM * cos2σM))); } while (abs(λ - λʹ) > 1e-12 && --iterationLimit > 0); if (iterationLimit == 0) throw new IllegalStateException("Formula failed to converge"); - double uSq = cosSqα * (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 Δσ = B * sinσ * (cos2σM + B / 4 * (cosσ * (-1 + 2 * cos2σM * cos2σM) - + val uSq = cosSqα * (a * a - b * b) / (b * b); + val A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq))); + val B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq))); + val Δσ = B * sinσ * (cos2σM + B / 4 * (cosσ * (-1 + 2 * cos2σM * cos2σM) - B / 6 * cos2σM * (-3 + 4 * sinσ * sinσ) * (-3 + 4 * cos2σM * cos2σM))); - double distance = b * A * (σ - Δσ); + val distance = b * A * (σ - Δσ); - double initialBearing = atan2(cosU2 * sinλ, cosU1 * sinU2 - sinU1 * cosU2 * cosλ); + var initialBearing = atan2(cosU2 * sinλ, cosU1 * sinU2 - sinU1 * cosU2 * cosλ); initialBearing = (initialBearing + 2 * PI) % (2 * PI); //turning value to trigonometric direction - double finalBearing = atan2(cosU1 * sinλ, -sinU1 * cosU2 + cosU1 * sinU2 * cosλ); + var finalBearing = atan2(cosU1 * sinλ, -sinU1 * cosU2 + cosU1 * sinU2 * cosλ); finalBearing = (finalBearing + 2 * PI) % (2 * PI); //turning value to trigonometric direction return new Vincenty(distance, toDegrees(initialBearing), toDegrees(finalBearing)); @@ -220,15 +228,15 @@ public static Point pointAt(Point standPoint, double bearing, double distance) { d being the distance travelled, R the earth’s radius */ - double φ1 = toRadians(standPoint.latitude); - double λ1 = toRadians(standPoint.longitude); - double θ = toRadians(bearing); - double δ = distance / EARTH_RADIUS; // normalize linear distance to radian angle + val φ1 = toRadians(standPoint.latitude); + val λ1 = toRadians(standPoint.longitude); + val θ = toRadians(bearing); + val δ = distance / EARTH_RADIUS; // normalize linear distance to radian angle - double φ2 = asin(sin(φ1) * cos(δ) + cos(φ1) * sin(δ) * cos(θ)); - double λ2 = λ1 + atan2(sin(θ) * sin(δ) * cos(φ1), cos(δ) - sin(φ1) * sin(φ2)); + val φ2 = asin(sin(φ1) * cos(δ) + cos(φ1) * sin(δ) * cos(θ)); + val λ2 = λ1 + atan2(sin(θ) * sin(δ) * cos(φ1), cos(δ) - sin(φ1) * sin(φ2)); - double λ2_harmonised = (λ2 + 3 * PI) % (2 * PI) - PI; // normalise to −180..+180° + val λ2_harmonised = (λ2 + 3 * PI) % (2 * PI) - PI; // normalise to −180..+180° return Point.at(Coordinate.fromRadians(φ2), Coordinate.fromRadians(λ2_harmonised)); } @@ -245,12 +253,12 @@ public static double bearing(Point standPoint, Point forePoint) { * Formula: θ = atan2( sin(Δlong).cos(lat2), cos(lat1).sin(lat2) − sin(lat1).cos(lat2).cos(Δlong) ) */ - double Δlong = toRadians(forePoint.longitude - standPoint.longitude); - double y = sin(Δlong) * cos(toRadians(forePoint.latitude)); - double x = cos(toRadians(standPoint.latitude)) * sin(toRadians(forePoint.latitude)) + val Δlong = toRadians(forePoint.longitude - standPoint.longitude); + val y = sin(Δlong) * cos(toRadians(forePoint.latitude)); + val x = cos(toRadians(standPoint.latitude)) * sin(toRadians(forePoint.latitude)) - sin(toRadians(standPoint.latitude)) * cos(toRadians(forePoint.latitude)) * cos(Δlong); - double bearing = (atan2(y, x) + 2 * PI) % (2 * PI); + val bearing = (atan2(y, x) + 2 * PI) % (2 * PI); return toDegrees(bearing); } @@ -266,26 +274,23 @@ public static double bearing(Point standPoint, Point forePoint) { public static BoundingArea around(Point standPoint, double distance) { //45 degrees going north-east - Point northEast = pointAt(standPoint, 45, distance); + val northEast = pointAt(standPoint, 45, distance); //225 degrees going south-west - Point southWest = pointAt(standPoint, 225, distance); + val southWest = pointAt(standPoint, 225, distance); return BoundingArea.at(northEast, southWest); } + @AllArgsConstructor private static class Vincenty { + final static Vincenty CO_INCIDENT_POINTS = new Vincenty(0, 0, 0); + /** * distance is the distance in meter * initialBearing is the initial bearing, or forward azimuth (in reference to North point), in degrees * finalBearing is the final bearing (in direction p1→p2), in degrees */ final double distance, initialBearing, finalBearing; - - Vincenty(double distance, double initialBearing, double finalBearing) { - this.distance = distance; - this.initialBearing = initialBearing; - this.finalBearing = finalBearing; - } } } diff --git a/src/main/java/com/grum/geocalc/Point.java b/src/main/java/com/grum/geocalc/Point.java index c36933e..448ba00 100644 --- a/src/main/java/com/grum/geocalc/Point.java +++ b/src/main/java/com/grum/geocalc/Point.java @@ -32,11 +32,17 @@ package com.grum.geocalc; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; + import java.io.Serializable; /** * Represent a point in spherical system */ +@AllArgsConstructor(access = AccessLevel.PACKAGE) +@EqualsAndHashCode public class Point implements Serializable { //decimal degrees public final double latitude, longitude; @@ -48,35 +54,13 @@ private Point(Coordinate latitude, Coordinate longitude) { /** * Create a new Point. - * @param latitude latitude + * + * @param latitude latitude * @param longitude longitude * @return the point */ public static Point at(Coordinate latitude, Coordinate longitude) { - return new Point(latitude, longitude); - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final Point other = (Point) obj; - if (Double.doubleToLongBits(this.latitude) != Double.doubleToLongBits(other.latitude)) { - return false; - } - return Double.doubleToLongBits(this.longitude) == Double.doubleToLongBits(other.longitude); - } - - @Override - public int hashCode() { - int hash = 3; - hash = 31 * hash + (int) (Double.doubleToLongBits(this.latitude) ^ (Double.doubleToLongBits(this.latitude) >>> 32)); - hash = 31 * hash + (int) (Double.doubleToLongBits(this.longitude) ^ (Double.doubleToLongBits(this.longitude) >>> 32)); - return hash; + return new Point(latitude.degrees(), longitude.degrees()); } @Override diff --git a/src/main/java/com/grum/geocalc/RadianCoordinate.java b/src/main/java/com/grum/geocalc/RadianCoordinate.java index e7e6aa9..53c4933 100644 --- a/src/main/java/com/grum/geocalc/RadianCoordinate.java +++ b/src/main/java/com/grum/geocalc/RadianCoordinate.java @@ -32,18 +32,19 @@ package com.grum.geocalc; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; + /** * Represents coordinates given in * radian-degrees (r) format */ +@AllArgsConstructor(access = AccessLevel.PACKAGE) + public class RadianCoordinate extends Coordinate { public final double radians; - RadianCoordinate(double radians) { - this.radians = radians; - } - @Override double degrees() { return Math.toDegrees(radians); diff --git a/src/test/java/com/grum/geocalc/CoordinateTest.java b/src/test/java/com/grum/geocalc/CoordinateTest.java index db5a8b1..7be1564 100644 --- a/src/test/java/com/grum/geocalc/CoordinateTest.java +++ b/src/test/java/com/grum/geocalc/CoordinateTest.java @@ -12,6 +12,8 @@ package com.grum.geocalc; +import lombok.val; +import lombok.var; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -26,57 +28,57 @@ public class CoordinateTest { @Test public void testDegreeCoordinate() { - DegreeCoordinate degreeCoordinate = Coordinate.fromDegrees(90); - Coordinate coordinate = degreeCoordinate.toDegreeCoordinate(); + var degreeCoordinate = Coordinate.fromDegrees(90); + var coordinate = degreeCoordinate.toDegreeCoordinate(); assertEquals(degreeCoordinate.degrees(), coordinate.degrees(), 1E-4); - DMSCoordinate dmsCoordinate = Coordinate.fromDMS(44, 37, 14); + val dmsCoordinate = Coordinate.fromDMS(44, 37, 14); degreeCoordinate = Coordinate.fromDegrees(44.620555555555555); assertEquals(degreeCoordinate.degrees(), dmsCoordinate.degrees(), 1E-4); } @Test public void testRadianCoordinate() { - RadianCoordinate radianCoordinate = Coordinate.fromRadians(Math.PI / 2); - Coordinate coordinate = radianCoordinate.toDegreeCoordinate(); + var radianCoordinate = Coordinate.fromRadians(Math.PI / 2); + val coordinate = radianCoordinate.toDegreeCoordinate(); assertEquals(radianCoordinate.degrees(), coordinate.degrees(), 1E-4); radianCoordinate = Coordinate.fromRadians(Math.PI * .57); - DegreeCoordinate degreeCoordinate = Coordinate.fromDegrees(180 * .57); + val degreeCoordinate = Coordinate.fromDegrees(180 * .57); assertEquals(degreeCoordinate.degrees(), radianCoordinate.degrees(), 1E-4); - DegreeCoordinate convertedBackDegreeCoordinate = radianCoordinate.toDegreeCoordinate(); + val convertedBackDegreeCoordinate = radianCoordinate.toDegreeCoordinate(); assertEquals(degreeCoordinate.degrees(), convertedBackDegreeCoordinate.degrees(), 1E-4); - DMSCoordinate dmsCoordinate = Coordinate.fromDMS(44, 37, 14); + val dmsCoordinate = Coordinate.fromDMS(44, 37, 14); radianCoordinate = Coordinate.fromRadians(Math.toRadians(44.620555555555555)); assertEquals(radianCoordinate.degrees(), dmsCoordinate.degrees(), 1E-4); } @Test public void testDMSCoordinate() { - DMSCoordinate dmsCoordinate = Coordinate.fromDMS(89, 59, 60.45); - Coordinate coordinate = dmsCoordinate.toDegreeCoordinate(); + var dmsCoordinate = Coordinate.fromDMS(89, 59, 60.45); + val coordinate = dmsCoordinate.toDegreeCoordinate(); assertEquals(dmsCoordinate.degrees(), coordinate.degrees(), 1E-5); dmsCoordinate = Coordinate.fromDMS(175, 8, 55.45); - DegreeCoordinate degreeCoordinate = Coordinate.fromDegrees(175.14873); + val degreeCoordinate = Coordinate.fromDegrees(175.14873); assertEquals(degreeCoordinate.degrees(), dmsCoordinate.degrees(), 1E-5); - DegreeCoordinate convertedBackDegreeCoordinate = dmsCoordinate.toDegreeCoordinate(); + val convertedBackDegreeCoordinate = dmsCoordinate.toDegreeCoordinate(); assertEquals(degreeCoordinate.degrees(), convertedBackDegreeCoordinate.degrees(), 1E-5); - RadianCoordinate radianCoordinate = Coordinate.fromRadians(Math.PI * 3 / 2); + val radianCoordinate = Coordinate.fromRadians(Math.PI * 3 / 2); dmsCoordinate = Coordinate.fromDMS(270, 0, 0); assertEquals(radianCoordinate.toDMSCoordinate().degrees(), dmsCoordinate.degrees(), 1E-5); } @Test public void testDMSCoordinateNegativeValue() { - DMSCoordinate dmsCoordinate = Coordinate.fromDMS(-46, 32, 44.16); + var dmsCoordinate = Coordinate.fromDMS(-46, 32, 44.16); assertEquals(-46.5456, dmsCoordinate.degrees(), 0); - DegreeCoordinate degreeCoordinate = Coordinate.fromDegrees(-46.5456); + val degreeCoordinate = Coordinate.fromDegrees(-46.5456); dmsCoordinate = degreeCoordinate.toDMSCoordinate(); assertEquals(-46, dmsCoordinate.wholeDegrees, 0); assertEquals(32, dmsCoordinate.minutes, 0); @@ -85,88 +87,88 @@ public void testDMSCoordinateNegativeValue() { @Test public void testGPSCoordinate() { - GPSCoordinate gpsCoordinate = Coordinate.fromGPS(89, 60); - Coordinate coordinate = gpsCoordinate.toDegreeCoordinate(); + var gpsCoordinate = Coordinate.fromGPS(89, 60); + val coordinate = gpsCoordinate.toDegreeCoordinate(); assertEquals(gpsCoordinate.degrees(), coordinate.degrees(), 1E-4); gpsCoordinate = Coordinate.fromGPS(175, 8.921999999999457); - DegreeCoordinate degreeCoordinate = Coordinate.fromDegrees(175.1487); + val degreeCoordinate = Coordinate.fromDegrees(175.1487); assertEquals(degreeCoordinate.degrees(), gpsCoordinate.degrees(), 1E-4); - DegreeCoordinate convertedBackDegreeCoordinate = gpsCoordinate.toDegreeCoordinate(); + val convertedBackDegreeCoordinate = gpsCoordinate.toDegreeCoordinate(); assertEquals(degreeCoordinate.degrees(), convertedBackDegreeCoordinate.degrees(), 1E-4); - RadianCoordinate radianCoordinate = Coordinate.fromRadians(Math.PI * 3 / 2); + val radianCoordinate = Coordinate.fromRadians(Math.PI * 3 / 2); gpsCoordinate = Coordinate.fromGPS(270, 0); assertEquals(radianCoordinate.toDMSCoordinate().degrees(), gpsCoordinate.degrees(), 1E-4); } @Test public void testContains() { - Point northEast = Point.at(Coordinate.fromDegrees(70), Coordinate.fromDegrees(145)); - Point southWest = Point.at(Coordinate.fromDegrees(50), Coordinate.fromDegrees(110)); - BoundingArea boundingArea = BoundingArea.at(northEast, southWest); + var northEast = Point.at(Coordinate.fromDegrees(70), Coordinate.fromDegrees(145)); + var southWest = Point.at(Coordinate.fromDegrees(50), Coordinate.fromDegrees(110)); + var boundingArea = BoundingArea.at(northEast, southWest); - Point point1 = Point.at(Coordinate.fromDegrees(60), Coordinate.fromDegrees(120)); + val point1 = Point.at(Coordinate.fromDegrees(60), Coordinate.fromDegrees(120)); assertTrue(boundingArea.contains(point1)); - Point point2 = Point.at(Coordinate.fromDegrees(45), Coordinate.fromDegrees(120)); + val point2 = Point.at(Coordinate.fromDegrees(45), Coordinate.fromDegrees(120)); assertFalse(boundingArea.contains(point2)); - Point point3 = Point.at(Coordinate.fromDegrees(85), Coordinate.fromDegrees(120)); + val point3 = Point.at(Coordinate.fromDegrees(85), Coordinate.fromDegrees(120)); assertFalse(boundingArea.contains(point3)); - Point point4 = Point.at(Coordinate.fromDegrees(60), Coordinate.fromDegrees(100)); + val point4 = Point.at(Coordinate.fromDegrees(60), Coordinate.fromDegrees(100)); assertFalse(boundingArea.contains(point4)); - Point point5 = Point.at(Coordinate.fromDegrees(60), Coordinate.fromDegrees(150)); + val point5 = Point.at(Coordinate.fromDegrees(60), Coordinate.fromDegrees(150)); assertFalse(boundingArea.contains(point5)); - Point point6 = Point.at(Coordinate.fromDegrees(80), Coordinate.fromDegrees(150)); + val point6 = Point.at(Coordinate.fromDegrees(80), Coordinate.fromDegrees(150)); assertFalse(boundingArea.contains(point6)); - Point point7 = Point.at(Coordinate.fromDegrees(35), Coordinate.fromDegrees(100)); + val point7 = Point.at(Coordinate.fromDegrees(35), Coordinate.fromDegrees(100)); assertFalse(boundingArea.contains(point7)); northEast = Point.at(Coordinate.fromDegrees(10), Coordinate.fromDegrees(45)); southWest = Point.at(Coordinate.fromDegrees(-30), Coordinate.fromDegrees(-35)); boundingArea = BoundingArea.at(northEast, southWest); - Point point8 = Point.at(Coordinate.fromDegrees(0), Coordinate.fromDegrees(0)); + val point8 = Point.at(Coordinate.fromDegrees(0), Coordinate.fromDegrees(0)); assertTrue(boundingArea.contains(point8)); - Point point9 = Point.at(Coordinate.fromDegrees(-5), Coordinate.fromDegrees(-30)); + val point9 = Point.at(Coordinate.fromDegrees(-5), Coordinate.fromDegrees(-30)); assertTrue(boundingArea.contains(point9)); - Point point10 = Point.at(Coordinate.fromDegrees(5), Coordinate.fromDegrees(30)); + val point10 = Point.at(Coordinate.fromDegrees(5), Coordinate.fromDegrees(30)); assertTrue(boundingArea.contains(point10)); - Point point11 = Point.at(Coordinate.fromDegrees(-35), Coordinate.fromDegrees(30)); + val point11 = Point.at(Coordinate.fromDegrees(-35), Coordinate.fromDegrees(30)); assertFalse(boundingArea.contains(point11)); northEast = Point.at(Coordinate.fromDegrees(10), Coordinate.fromDegrees(-165)); southWest = Point.at(Coordinate.fromDegrees(-30), Coordinate.fromDegrees(170)); boundingArea = BoundingArea.at(northEast, southWest); - Point point12 = Point.at(Coordinate.fromDegrees(0), Coordinate.fromDegrees(180)); + val point12 = Point.at(Coordinate.fromDegrees(0), Coordinate.fromDegrees(180)); assertTrue(boundingArea.contains(point12)); - Point point13 = Point.at(Coordinate.fromDegrees(0), Coordinate.fromDegrees(-179)); + val point13 = Point.at(Coordinate.fromDegrees(0), Coordinate.fromDegrees(-179)); assertTrue(boundingArea.contains(point13)); } @Test public void testConvertingToSelf() { - GPSCoordinate gpsCoordinate = Coordinate.fromGPS(89, 60); + val gpsCoordinate = Coordinate.fromGPS(89, 60); assertEquals(gpsCoordinate.toGPSCoordinate().degrees(), gpsCoordinate.degrees(), 10E-5); - DegreeCoordinate degreeCoordinate = Coordinate.fromDegrees(-46.5456); + val degreeCoordinate = Coordinate.fromDegrees(-46.5456); assertEquals(degreeCoordinate.toDegreeCoordinate().degrees(), degreeCoordinate.degrees(), 10E-5); - DMSCoordinate dmsCoordinate = Coordinate.fromDMS(89, 59, 60.45); + val dmsCoordinate = Coordinate.fromDMS(89, 59, 60.45); assertEquals(dmsCoordinate.toDMSCoordinate().degrees(), dmsCoordinate.degrees(), 10E-5); - RadianCoordinate radianCoordinate = Coordinate.fromRadians(Math.PI / 2); + val radianCoordinate = Coordinate.fromRadians(Math.PI / 2); assertEquals(radianCoordinate.toRadianCoordinate().degrees(), radianCoordinate.degrees(), 10E-5); } } diff --git a/src/test/java/com/grum/geocalc/DistanceTest.java b/src/test/java/com/grum/geocalc/DistanceTest.java index 4d72c78..43c4397 100644 --- a/src/test/java/com/grum/geocalc/DistanceTest.java +++ b/src/test/java/com/grum/geocalc/DistanceTest.java @@ -12,93 +12,92 @@ package com.grum.geocalc; +import lombok.extern.slf4j.Slf4j; +import lombok.val; +import lombok.var; import org.apache.log4j.Logger; import org.junit.Test; import static org.junit.Assert.assertEquals; -/** - * @author rgallet - */ +@Slf4j public class DistanceTest { - final Logger logger = Logger.getLogger(getClass()); - @Test public void testSphericalLawOfCosinesDistance() { //Kew - Coordinate lat = Coordinate.fromDegrees(51.4843774); - Coordinate lng = Coordinate.fromDegrees(-0.2912044); - Point kew = Point.at(lat, lng); + var lat = Coordinate.fromDegrees(51.4843774); + var lng = Coordinate.fromDegrees(-0.2912044); + val kew = Point.at(lat, lng); //Richmond lat = Coordinate.fromDegrees(51.4613418); lng = Coordinate.fromDegrees(-0.3035466); - Point richmond = Point.at(lat, lng); + val richmond = Point.at(lat, lng); - assertEquals(2700.3261395525724, EarthCalc.gcdDistance(richmond, kew), 10E-3); + assertEquals(2694.283076925272, EarthCalc.gcdDistance(richmond, kew), 10E-3); } @Test public void testDistanceBnaToLax() { - Coordinate lat = Coordinate.fromDegrees(36.12); - Coordinate lng = Coordinate.fromDegrees(-86.97); - Point BNA = Point.at(lat, lng); + var lat = Coordinate.fromDegrees(36.12); + var lng = Coordinate.fromDegrees(-86.97); + val BNA = Point.at(lat, lng); lat = Coordinate.fromDegrees(33.94); lng = Coordinate.fromDegrees(-118.40); - Point LAX = Point.at(lat, lng); + val LAX = Point.at(lat, lng); - assertEquals(2859586.477757082, EarthCalc.gcdDistance(LAX, BNA), 10E-3); + assertEquals(2853187.0080671725, EarthCalc.gcdDistance(LAX, BNA), 10E-3); } @Test public void testDistanceToBuenosAires() { //Kew - Coordinate lat = Coordinate.fromDMS(51, 29, 3.7572); - Coordinate lng = Coordinate.fromDMS(0, 17, 28.3338); + var lat = Coordinate.fromDMS(51, 29, 3.7572); + var lng = Coordinate.fromDMS(0, 17, 28.3338); - Point kew = Point.at(lat, lng); + val kew = Point.at(lat, lng); //Buenos Aires lat = Coordinate.fromDMS(-34, 36, 35.9994); lng = Coordinate.fromDMS(-58, 22, 11.9994); - Point buenosAires = Point.at(lat, lng); + val buenosAires = Point.at(lat, lng); - assertEquals(11146, (int) (EarthCalc.gcdDistance(buenosAires, kew) / 1000)); //km + assertEquals(11121, (int) (EarthCalc.gcdDistance(buenosAires, kew) / 1000)); //km } @Test public void testHaversineDistanceToBuenosAires() { //Kew - Coordinate lat = Coordinate.fromDMS(51, 29, 3.7572); - Coordinate lng = Coordinate.fromDMS(0, 17, 28.3338); + var lat = Coordinate.fromDMS(51, 29, 3.7572); + var lng = Coordinate.fromDMS(0, 17, 28.3338); - Point kew = Point.at(lat, lng); + val kew = Point.at(lat, lng); //Buenos Aires lat = Coordinate.fromDMS(-34, 36, 35.9994); lng = Coordinate.fromDMS(-58, 22, 11.9994); - Point buenosAires = Point.at(lat, lng); + val buenosAires = Point.at(lat, lng); - assertEquals(11146, (int) (EarthCalc.haversineDistance(buenosAires, kew) / 1000)); //km + assertEquals(11121, (int) (EarthCalc.haversineDistance(buenosAires, kew) / 1000)); //km } @Test public void testVincentyDistanceToBuenosAires() { //Kew - Coordinate lat = Coordinate.fromDMS(51, 29, 3.7572); - Coordinate lng = Coordinate.fromDMS(0, 17, 28.3338); + var lat = Coordinate.fromDMS(51, 29, 3.7572); + var lng = Coordinate.fromDMS(0, 17, 28.3338); - Point kew = Point.at(lat, lng); + val kew = Point.at(lat, lng); //Buenos Aires lat = Coordinate.fromDMS(-34, 36, 35.9994); lng = Coordinate.fromDMS(-58, 22, 11.9994); - Point buenosAires = Point.at(lat, lng); + val buenosAires = Point.at(lat, lng); assertEquals(11120, (int) (EarthCalc.vincentyDistance(buenosAires, kew) / 1000)); //km } @@ -106,14 +105,14 @@ public void testVincentyDistanceToBuenosAires() { @Test public void testSymmetricDistance() { //Kew - Coordinate lat = Coordinate.fromDegrees(51.4843774); - Coordinate lng = Coordinate.fromDegrees(-0.2912044); - Point kew = Point.at(lat, lng); + var lat = Coordinate.fromDegrees(51.4843774); + var lng = Coordinate.fromDegrees(-0.2912044); + val kew = Point.at(lat, lng); //Richmond lat = Coordinate.fromDegrees(51.4613418); lng = Coordinate.fromDegrees(-0.3035466); - Point richmond = Point.at(lat, lng); + val richmond = Point.at(lat, lng); assertEquals(EarthCalc.gcdDistance(richmond, kew), EarthCalc.gcdDistance(kew, richmond), 10E-10); } @@ -121,9 +120,9 @@ public void testSymmetricDistance() { @Test public void testZeroDistance() { //Kew - Coordinate lat = Coordinate.fromDegrees(51.4843774); - Coordinate lng = Coordinate.fromDegrees(-0.2912044); - Point kew = Point.at(lat, lng); + val lat = Coordinate.fromDegrees(51.4843774); + val lng = Coordinate.fromDegrees(-0.2912044); + val kew = Point.at(lat, lng); assertEquals(0, EarthCalc.gcdDistance(kew, kew), 0); assertEquals(0, EarthCalc.vincentyDistance(kew, kew), 0); @@ -132,72 +131,72 @@ public void testZeroDistance() { @Test public void testZeroDistanceWaldshutGermany() { - //Kew - Coordinate lat = Coordinate.fromDegrees(47.62285); - Coordinate lng = Coordinate.fromDegrees(8.20897); - Point waldshut = Point.at(lat, lng); - - assertEquals(0, EarthCalc.gcdDistance(waldshut, waldshut), 0); - assertEquals(0, EarthCalc.vincentyDistance(waldshut, waldshut), 0); - assertEquals(0, EarthCalc.haversineDistance(waldshut, waldshut), 0); + //Kew + val lat = Coordinate.fromDegrees(47.62285); + val lng = Coordinate.fromDegrees(8.20897); + val waldshut = Point.at(lat, lng); + + assertEquals(0, EarthCalc.gcdDistance(waldshut, waldshut), 0); + assertEquals(0, EarthCalc.vincentyDistance(waldshut, waldshut), 0); + assertEquals(0, EarthCalc.haversineDistance(waldshut, waldshut), 0); } - + @Test public void testBoundingAreaDistance() { //Kew - Coordinate lat = Coordinate.fromDegrees(51.4843774); - Coordinate lng = Coordinate.fromDegrees(-0.2912044); - Point kew = Point.at(lat, lng); + val lat = Coordinate.fromDegrees(51.4843774); + val lng = Coordinate.fromDegrees(-0.2912044); + val kew = Point.at(lat, lng); - BoundingArea area = EarthCalc.around(kew, 3000); + val area = EarthCalc.around(kew, 3000); - double northEastDistance = EarthCalc.gcdDistance(kew, area.northEast); + val northEastDistance = EarthCalc.gcdDistance(kew, area.northEast); assertEquals(3000d, northEastDistance, 1E-3); - double southWestDistance = EarthCalc.gcdDistance(kew, area.southWest); + val southWestDistance = EarthCalc.gcdDistance(kew, area.southWest); assertEquals(3000d, southWestDistance, 1E-3); - Point northWest = area.northWest; - double northWestDistance = EarthCalc.gcdDistance(kew, northWest); + val northWest = area.northWest; + val northWestDistance = EarthCalc.gcdDistance(kew, northWest); assertEquals(3000d, northWestDistance, 2); - Point southEast = area.southEast; - double southEastDistance = EarthCalc.gcdDistance(kew, southEast); + val southEast = area.southEast; + val southEastDistance = EarthCalc.gcdDistance(kew, southEast); assertEquals(3000d, southEastDistance, 2); - Point middleNorth = Point.at(Coordinate.fromDegrees(area.northEast.latitude), + val middleNorth = Point.at(Coordinate.fromDegrees(area.northEast.latitude), Coordinate.fromDegrees((area.southWest.longitude + area.northEast.longitude) / 2)); - double middleNorthDistance = EarthCalc.gcdDistance(kew, middleNorth); + val middleNorthDistance = EarthCalc.gcdDistance(kew, middleNorth); assertEquals(2120d, middleNorthDistance, 1); - Point middleSouth = Point.at(Coordinate.fromDegrees(area.southWest.latitude), + val middleSouth = Point.at(Coordinate.fromDegrees(area.southWest.latitude), Coordinate.fromDegrees((area.southWest.longitude + area.northEast.longitude) / 2)); - double middleSouthDistance = EarthCalc.gcdDistance(kew, middleSouth); + val middleSouthDistance = EarthCalc.gcdDistance(kew, middleSouth); assertEquals(2120d, middleSouthDistance, 2); - Point middleWest = Point.at(Coordinate.fromDegrees((area.northEast.latitude + area.southWest.latitude) / 2), + val middleWest = Point.at(Coordinate.fromDegrees((area.northEast.latitude + area.southWest.latitude) / 2), Coordinate.fromDegrees(area.northEast.longitude)); - double middleWestDistance = EarthCalc.gcdDistance(kew, middleWest); - logger.info("Middle West => " + middleWestDistance); + val middleWestDistance = EarthCalc.gcdDistance(kew, middleWest); + log.info("Middle West => " + middleWestDistance); assertEquals(2120d, middleWestDistance, 3); - Point middleEast = Point.at(Coordinate.fromDegrees((area.northEast.latitude + area.southWest.latitude) / 2), + val middleEast = Point.at(Coordinate.fromDegrees((area.northEast.latitude + area.southWest.latitude) / 2), Coordinate.fromDegrees(area.southWest.longitude)); - double middleEastDistance = EarthCalc.gcdDistance(kew, middleEast); - logger.info("Middle East => " + middleEastDistance); + val middleEastDistance = EarthCalc.gcdDistance(kew, middleEast); + log.info("Middle East => " + middleEastDistance); assertEquals(2120d, middleEastDistance, 1); } @Test public void testBoundingAreaNorthPole() { //North Pole - Coordinate lat = Coordinate.fromDegrees(90d); - Coordinate lng = Coordinate.fromDegrees(0); - Point northPole = Point.at(lat, lng); + val lat = Coordinate.fromDegrees(90d); + val lng = Coordinate.fromDegrees(0); + val northPole = Point.at(lat, lng); - BoundingArea area = EarthCalc.around(northPole, 10000); - logger.info("North East => " + area.northEast); - logger.info("South West => " + area.southWest); + val area = EarthCalc.around(northPole, 10000); + log.info("North East => " + area.northEast); + log.info("South West => " + area.southWest); assertEquals(89.91006798056583d, area.northEast.latitude, 1); assertEquals(90d, area.northEast.longitude, 1); @@ -209,13 +208,13 @@ public void testBoundingAreaNorthPole() { @Test public void testBoundingAreaNextToLondon() { //North Pole - Coordinate lat = Coordinate.fromDegrees(51.5085452); - Coordinate lng = Coordinate.fromDegrees(-0.1997387000000117); - Point northPole = Point.at(lat, lng); + val lat = Coordinate.fromDegrees(51.5085452); + val lng = Coordinate.fromDegrees(-0.1997387000000117); + val northPole = Point.at(lat, lng); - BoundingArea area = EarthCalc.around(northPole, 5); - logger.info("North East => " + area.northEast); - logger.info("South West => " + area.southWest); + val area = EarthCalc.around(northPole, 5); + log.info("North East => " + area.northEast); + log.info("South West => " + area.southWest); assertEquals(51.508576995759306d, area.northEast.latitude, 1); assertEquals(-0.19968761404347382d, area.northEast.longitude, 1); @@ -227,11 +226,11 @@ public void testBoundingAreaNextToLondon() { @Test public void testPointRadialDistanceZero() { //Kew - Coordinate lat = Coordinate.fromDegrees(51.4843774); - Coordinate lng = Coordinate.fromDegrees(-0.2912044); + val lat = Coordinate.fromDegrees(51.4843774); + val lng = Coordinate.fromDegrees(-0.2912044); Point kew = Point.at(lat, lng); - Point sameKew = EarthCalc.pointAt(kew, 45, 0); + var sameKew = EarthCalc.pointAt(kew, 45, 0); assertEquals(lat.degrees(), sameKew.latitude, 1E-10); assertEquals(lng.degrees(), sameKew.longitude, 1E-10); @@ -247,19 +246,19 @@ public void testPointRadialDistanceZero() { @Test public void testPointRadialDistance() { //Kew - Coordinate lat = Coordinate.fromDegrees(51.4843774); - Coordinate lng = Coordinate.fromDegrees(-0.2912044); - Point kew = Point.at(lat, lng); + var lat = Coordinate.fromDegrees(51.4843774); + var lng = Coordinate.fromDegrees(-0.2912044); + val kew = Point.at(lat, lng); //Richmond lat = Coordinate.fromDegrees(51.4613418); lng = Coordinate.fromDegrees(-0.3035466); - Point richmond = Point.at(lat, lng); + val richmond = Point.at(lat, lng); - double distance = EarthCalc.gcdDistance(kew, richmond); - double bearing = EarthCalc.bearing(kew, richmond); + val distance = EarthCalc.gcdDistance(kew, richmond); + val bearing = EarthCalc.bearing(kew, richmond); - Point allegedRichmond = EarthCalc.pointAt(kew, bearing, distance); + val allegedRichmond = EarthCalc.pointAt(kew, bearing, distance); assertEquals(richmond.latitude, allegedRichmond.latitude, 10E-5); assertEquals(richmond.longitude, allegedRichmond.longitude, 10E-5); @@ -268,14 +267,14 @@ public void testPointRadialDistance() { @Test public void testBearing() { //Kew - Coordinate lat = Coordinate.fromDegrees(51.4843774); - Coordinate lng = Coordinate.fromDegrees(-0.2912044); - Point kew = Point.at(lat, lng); + var lat = Coordinate.fromDegrees(51.4843774); + var lng = Coordinate.fromDegrees(-0.2912044); + val kew = Point.at(lat, lng); //Richmond, London lat = Coordinate.fromDegrees(51.4613418); lng = Coordinate.fromDegrees(-0.3035466); - Point richmond = Point.at(lat, lng); + val richmond = Point.at(lat, lng); System.out.println(EarthCalc.bearing(kew, richmond)); assertEquals(EarthCalc.bearing(kew, richmond), 198.4604614570758D, 10E-5); @@ -294,13 +293,13 @@ public void testBearingGitHubIssue3() { * but the correct result is 19.213575108209017 */ - Coordinate lat = Coordinate.fromDegrees(31.194326398628462); - Coordinate lng = Coordinate.fromDegrees(121.42127048962534); - Point standpoint = Point.at(lat, lng); + var lat = Coordinate.fromDegrees(31.194326398628462); + var lng = Coordinate.fromDegrees(121.42127048962534); + val standpoint = Point.at(lat, lng); lat = Coordinate.fromDegrees(31.194353394639606); lng = Coordinate.fromDegrees(121.4212814985147); - Point forepoint = Point.at(lat, lng); + val forepoint = Point.at(lat, lng); /** * http://www.movable-type.co.uk/scripts/latlong.html @@ -313,14 +312,14 @@ public void testBearingGitHubIssue3() { @Test public void testVincentyBearing() { //Kew - Coordinate lat = Coordinate.fromDegrees(51.4843774); - Coordinate lng = Coordinate.fromDegrees(-0.2912044); - Point kew = Point.at(lat, lng); + var lat = Coordinate.fromDegrees(51.4843774); + var lng = Coordinate.fromDegrees(-0.2912044); + val kew = Point.at(lat, lng); //Richmond, London lat = Coordinate.fromDegrees(51.4613418); lng = Coordinate.fromDegrees(-0.3035466); - Point richmond = Point.at(lat, lng); + val richmond = Point.at(lat, lng); //comparing to results from http://www.movable-type.co.uk/scripts/latlong.html assertEquals(EarthCalc.vincentyBearing(kew, richmond), Coordinate.fromDMS(198, 30, 19.58).degrees(), 10E-5); @@ -330,14 +329,14 @@ public void testVincentyBearing() { @Test public void testMidPoint() { //Kew - Coordinate lat = Coordinate.fromDegrees(51.4843774); - Coordinate lng = Coordinate.fromDegrees(-0.2912044); - Point kew = Point.at(lat, lng); + var lat = Coordinate.fromDegrees(51.4843774); + var lng = Coordinate.fromDegrees(-0.2912044); + val kew = Point.at(lat, lng); //Richmond, London lat = Coordinate.fromDegrees(51.4613418); lng = Coordinate.fromDegrees(-0.3035466); - Point richmond = Point.at(lat, lng); + val richmond = Point.at(lat, lng); //comparing to results from http://www.movable-type.co.uk/scripts/latlong.html assertEquals(EarthCalc.midPoint(richmond, kew), Point.at(Coordinate.fromDegrees(51.47285976194266), Coordinate.fromDegrees(-0.2973770580524634))); From f6289f88a23511317e66dca96fbcc100bc3a0a35 Mon Sep 17 00:00:00 2001 From: Romain Gallet Date: Sat, 3 Oct 2020 21:49:05 +0100 Subject: [PATCH 4/7] Lombok --- lombok.config | 1 + 1 file changed, 1 insertion(+) create mode 100644 lombok.config diff --git a/lombok.config b/lombok.config new file mode 100644 index 0000000..f202620 --- /dev/null +++ b/lombok.config @@ -0,0 +1 @@ +lombok.addLombokGeneratedAnnotation=true From e3cc0cbdeb49af3bbd827fbf435820c0ffec246e Mon Sep 17 00:00:00 2001 From: Romain Gallet Date: Sat, 3 Oct 2020 22:06:16 +0100 Subject: [PATCH 5/7] Moved EarthCalc methods to sub namespaces --- README.md | 20 +- src/main/java/com/grum/geocalc/EarthCalc.java | 422 +++++++++--------- .../java/com/grum/geocalc/DistanceTest.java | 71 ++- 3 files changed, 258 insertions(+), 255 deletions(-) diff --git a/README.md b/README.md index ab78830..5301809 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,7 @@ back and forth lng = Coordinate.fromDegrees(-0.3035466); Point richmond = Point.at(lat, lng); - double distance = EarthCalc.gcdDistance(richmond, kew); //in meters + double distance = EarthCalc.gcd.distance(richmond, kew); //in meters #### Harvesine formula @@ -129,7 +129,7 @@ back and forth lng = Coordinate.fromDegrees(-0.3035466); Point richmond = Point.at(lat, lng); - double distance = EarthCalc.harvesineDistance(richmond, kew); //in meters + double distance = EarthCalc.harvesine.distance(richmond, kew); //in meters #### Vincenty formula @@ -143,7 +143,7 @@ back and forth lng = Coordinate.fromDegrees(-0.3035466); Point richmond = Point.at(lat, lng); - double distance = EarthCalc.vincentyDistance(richmond, kew); //in meters + double distance = EarthCalc.vincenty.distance(richmond, kew); //in meters ### Finding a point at 'distance in meters away' from a standpoint, given a bearing @@ -156,7 +156,7 @@ back and forth Point kew = Point.at(lat, lng); //Distance away point, bearing is 45deg - Point otherPoint = EarthCalc.pointAt(kew, 45, 1000); + Point otherPoint = EarthCalc.gcd.pointAt(kew, 45, 1000); ### BoundingArea @@ -168,7 +168,7 @@ other points, you need to figure out which ones are at most, say, 3000 meters aw While this only gives an approximation, it is several order of magnitude faster than calculating the distances from each point in the set to the reference point. - BoundingArea area = EarthCalc.boundingArea(kew, 3000); + BoundingArea area = EarthCalc.gcd.boundingArea(kew, 3000); Point nw = area.northWest; Point se = area.southEast; @@ -205,7 +205,7 @@ you can determine whether a point is contained within that area using: lng = Coordinate.fromDegrees(-0.3035466); Point richmond = Point.at(lat, lng); - double bearing = EarthCalc.bearing(kew, richmond); //in decimal degrees + double bearing = EarthCalc.gcd.bearing(kew, richmond); //in decimal degrees #### Azimuth bearing - Vincenty formula @@ -219,7 +219,7 @@ you can determine whether a point is contained within that area using: lng = Coordinate.fromDegrees(-0.3035466); Point richmond = Point.at(lat, lng); - double bearing = EarthCalc.vincentyBearing(kew, richmond); //in decimal degrees + double bearing = EarthCalc.vincenty.bearing(kew, richmond); //in decimal degrees #### Final bearing - Vincenty formula @@ -233,7 +233,7 @@ you can determine whether a point is contained within that area using: lng = Coordinate.fromDegrees(-0.3035466); Point richmond = Point.at(lat, lng); - double bearing = EarthCalc.vincentyFinalBearing(kew, richmond); //in decimal degrees + double bearing = EarthCalc.vincenty.finalBearing(kew, richmond); //in decimal degrees #### Mid point - This is the half-way point along a great circle path between the two points. @@ -243,6 +243,4 @@ you can determine whether a point is contained within that area using: //Richmond, London Point richmond = Point.at(Coordinate.fromDegrees(51.4613418), Coordinate.fromDegrees(-0.3035466)); - Point midPoint = EarthCalc.midPoint(richmond, kew) // Point{latitude=51.47285976194266, longitude=-0.2973770580524634} - - \ No newline at end of file + Point midPoint = EarthCalc.gcd.midPoint(richmond, kew) // Point{latitude=51.47285976194266, longitude=-0.2973770580524634} diff --git a/src/main/java/com/grum/geocalc/EarthCalc.java b/src/main/java/com/grum/geocalc/EarthCalc.java index dd5d74b..8883c21 100644 --- a/src/main/java/com/grum/geocalc/EarthCalc.java +++ b/src/main/java/com/grum/geocalc/EarthCalc.java @@ -45,177 +45,64 @@ public class EarthCalc { public static final double EARTH_RADIUS = 6_356_752.314245D; // radius at the poles, meters - /** - * This is the half-way point along a great circle path between the two points. - * - * @param standPoint standPoint - * @param forePoint standPoint - * @return mid point - * @see - */ - public static Point midPoint(Point standPoint, Point forePoint) { - val λ1 = toRadians(standPoint.longitude); - val λ2 = toRadians(forePoint.longitude); - - val φ1 = toRadians(standPoint.latitude); - val φ2 = toRadians(forePoint.latitude); - - val Bx = cos(φ2) * cos(λ2 - λ1); - val By = cos(φ2) * sin(λ2 - λ1); - - val φ3 = atan2(sin(φ1) + sin(φ2), sqrt((cos(φ1) + Bx) * (cos(φ1) + Bx) + By * By)); - val λ3 = λ1 + atan2(By, cos(φ1) + Bx); - - return Point.at(Coordinate.fromRadians(φ3), Coordinate.fromRadians(λ3)); - } - - /** - * Returns the distance between two points at spherical law of cosines. - * - * @param standPoint The stand point - * @param forePoint The fore point - * @return The distance, in meters - */ - public static double gcdDistance(Point standPoint, Point forePoint) { + public static class gcd { + /** + * This is the half-way point along a great circle path between the two points. + * + * @param standPoint standPoint + * @param forePoint standPoint + * @return mid point + * @see + */ + public static Point midPoint(Point standPoint, Point forePoint) { + val λ1 = toRadians(standPoint.longitude); + val λ2 = toRadians(forePoint.longitude); - val Δλ = toRadians(abs(forePoint.longitude - standPoint.longitude)); - val φ1 = toRadians(standPoint.latitude); - val φ2 = toRadians(forePoint.latitude); + val φ1 = toRadians(standPoint.latitude); + val φ2 = toRadians(forePoint.latitude); - //spherical law of cosines - val sphereCos = (sin(φ1) * sin(φ2)) + (cos(φ1) * cos(φ2) * cos(Δλ)); - val c = acos(max(min(sphereCos, 1d), -1d)); + val Bx = cos(φ2) * cos(λ2 - λ1); + val By = cos(φ2) * sin(λ2 - λ1); - return EARTH_RADIUS * c; - } + val φ3 = atan2(sin(φ1) + sin(φ2), sqrt((cos(φ1) + Bx) * (cos(φ1) + Bx) + By * By)); + val λ3 = λ1 + atan2(By, cos(φ1) + Bx); - /** - * Returns the distance between two points at Haversine formula. - * - * @param standPoint The stand point - * @param forePoint The fore point - * @return The distance, in meters - */ - public static double haversineDistance(Point standPoint, Point forePoint) { - - val Δλ = toRadians(abs(forePoint.longitude - standPoint.longitude)); - val φ1 = toRadians(standPoint.latitude); - val φ2 = toRadians(forePoint.latitude); - - // haversine formula - val Δφ = toRadians(abs(forePoint.latitude - standPoint.latitude)); - val a = sin(Δφ / 2) * sin(Δφ / 2) + cos(φ1) * cos(φ2) * sin(Δλ / 2) * sin(Δλ / 2); - val c = 2 * atan2(sqrt(a), sqrt(1 - a)); //angular distance in radians - - return EARTH_RADIUS * c; - } + return Point.at(Coordinate.fromRadians(φ3), Coordinate.fromRadians(λ3)); + } - /** - * Calculate distance, (azimuth) bearing and final bearing between standPoint and forePoint. - * - * @param standPoint The stand point - * @param forePoint The fore point - * @return Vincenty object which holds all 3 values - */ - private static Vincenty vincenty(Point standPoint, Point forePoint) { - val λ1 = toRadians(standPoint.longitude); - val λ2 = toRadians(forePoint.longitude); - - val φ1 = toRadians(standPoint.latitude); - val φ2 = toRadians(forePoint.latitude); - - val a = 6_378_137D; // radius at equator - val b = EARTH_RADIUS; // Using b to keep close to academic formula. - val f = 1 / 298.257223563D; // flattening of the ellipsoid - - val L = λ2 - λ1; - val tanU1 = (1 - f) * tan(φ1); - val cosU1 = 1 / sqrt((1 + tanU1 * tanU1)); - val sinU1 = tanU1 * cosU1; - - val tanU2 = (1 - f) * tan(φ2); - val cosU2 = 1 / sqrt((1 + tanU2 * tanU2)); - val sinU2 = tanU2 * cosU2; - - double λ = L, λʹ, iterationLimit = 100, cosSqα, σ, cos2σM, cosσ, sinσ, sinλ, cosλ; - do { - sinλ = sin(λ); - cosλ = cos(λ); - val sinSqσ = (cosU2 * sinλ) * (cosU2 * sinλ) + (cosU1 * sinU2 - sinU1 * cosU2 * cosλ) * (cosU1 * sinU2 - sinU1 * cosU2 * cosλ); - sinσ = sqrt(sinSqσ); - if (sinσ == 0) return Vincenty.CO_INCIDENT_POINTS; // co-incident points - cosσ = sinU1 * sinU2 + cosU1 * cosU2 * cosλ; - σ = atan2(sinσ, cosσ); - val sinα = cosU1 * cosU2 * sinλ / sinσ; - cosSqα = 1 - sinα * sinα; - cos2σM = cosσ - 2 * sinU1 * sinU2 / cosSqα; - - if (Double.isNaN(cos2σM)) cos2σM = 0; // equatorial line: cosSqα=0 - val C = f / 16 * cosSqα * (4 + f * (4 - 3 * cosSqα)); - λʹ = λ; - λ = L + (1 - C) * f * sinα * (σ + C * sinσ * (cos2σM + C * cosσ * (-1 + 2 * cos2σM * cos2σM))); - } while (abs(λ - λʹ) > 1e-12 && --iterationLimit > 0); - - if (iterationLimit == 0) throw new IllegalStateException("Formula failed to converge"); - - val uSq = cosSqα * (a * a - b * b) / (b * b); - val A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq))); - val B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq))); - val Δσ = B * sinσ * (cos2σM + B / 4 * (cosσ * (-1 + 2 * cos2σM * cos2σM) - - B / 6 * cos2σM * (-3 + 4 * sinσ * sinσ) * (-3 + 4 * cos2σM * cos2σM))); - - val distance = b * A * (σ - Δσ); - - var initialBearing = atan2(cosU2 * sinλ, cosU1 * sinU2 - sinU1 * cosU2 * cosλ); - initialBearing = (initialBearing + 2 * PI) % (2 * PI); //turning value to trigonometric direction - - var finalBearing = atan2(cosU1 * sinλ, -sinU1 * cosU2 + cosU1 * sinU2 * cosλ); - finalBearing = (finalBearing + 2 * PI) % (2 * PI); //turning value to trigonometric direction - - return new Vincenty(distance, toDegrees(initialBearing), toDegrees(finalBearing)); - } + /** + * Returns the distance between two points at spherical law of cosines. + * + * @param standPoint The stand point + * @param forePoint The fore point + * @return The distance, in meters + */ + public static double distance(Point standPoint, Point forePoint) { - public static double vincentyDistance(Point standPoint, Point forePoint) { - return vincenty(standPoint, forePoint).distance; - } + val Δλ = toRadians(abs(forePoint.longitude - standPoint.longitude)); + val φ1 = toRadians(standPoint.latitude); + val φ2 = toRadians(forePoint.latitude); - /** - * Returns (azimuth) bearing at Vincenty formula. - * - * @param standPoint The stand point - * @param forePoint The fore point - * @return (azimuth) bearing in degrees to the North - * @see - */ - public static double vincentyBearing(Point standPoint, Point forePoint) { - return vincenty(standPoint, forePoint).initialBearing; - } + //spherical law of cosines + val sphereCos = (sin(φ1) * sin(φ2)) + (cos(φ1) * cos(φ2) * cos(Δλ)); + val c = acos(max(min(sphereCos, 1d), -1d)); - /** - * Returns final bearing in direction of standPoint→forePoint at Vincenty formula. - * - * @param standPoint The stand point - * @param forePoint The fore point - * @return (azimuth) bearing in degrees to the North - * @see - */ - public static double vincentyFinalBearing(Point standPoint, Point forePoint) { - return vincenty(standPoint, forePoint).finalBearing; - } + return EARTH_RADIUS * c; + } - /** - * Returns the coordinates of a point which is "distance" away - * from standPoint in the direction of "bearing" - *

- * Note: North is equal to 0 for bearing value - * - * @param standPoint Origin - * @param bearing Direction in degrees, clockwise from north - * @param distance distance in meters - * @return forePoint coordinates - * @see - */ - public static Point pointAt(Point standPoint, double bearing, double distance) { + /** + * Returns the coordinates of a point which is "distance" away + * from standPoint in the direction of "bearing" + *

+ * Note: North is equal to 0 for bearing value + * + * @param standPoint Origin + * @param bearing Direction in degrees, clockwise from north + * @param distance distance in meters + * @return forePoint coordinates + * @see + */ + public static Point pointAt(Point standPoint, double bearing, double distance) { /* φ2 = asin( sin φ1 ⋅ cos δ + cos φ1 ⋅ sin δ ⋅ cos θ ) λ2 = λ1 + atan2( sin θ ⋅ sin δ ⋅ cos φ1, cos δ − sin φ1 ⋅ sin φ2 ) @@ -228,69 +115,188 @@ public static Point pointAt(Point standPoint, double bearing, double distance) { d being the distance travelled, R the earth’s radius */ - val φ1 = toRadians(standPoint.latitude); - val λ1 = toRadians(standPoint.longitude); - val θ = toRadians(bearing); - val δ = distance / EARTH_RADIUS; // normalize linear distance to radian angle + val φ1 = toRadians(standPoint.latitude); + val λ1 = toRadians(standPoint.longitude); + val θ = toRadians(bearing); + val δ = distance / EARTH_RADIUS; // normalize linear distance to radian angle - val φ2 = asin(sin(φ1) * cos(δ) + cos(φ1) * sin(δ) * cos(θ)); - val λ2 = λ1 + atan2(sin(θ) * sin(δ) * cos(φ1), cos(δ) - sin(φ1) * sin(φ2)); + val φ2 = asin(sin(φ1) * cos(δ) + cos(φ1) * sin(δ) * cos(θ)); + val λ2 = λ1 + atan2(sin(θ) * sin(δ) * cos(φ1), cos(δ) - sin(φ1) * sin(φ2)); - val λ2_harmonised = (λ2 + 3 * PI) % (2 * PI) - PI; // normalise to −180..+180° + val λ2_harmonised = (λ2 + 3 * PI) % (2 * PI) - PI; // normalise to −180..+180° - return Point.at(Coordinate.fromRadians(φ2), Coordinate.fromRadians(λ2_harmonised)); - } + return Point.at(Coordinate.fromRadians(φ2), Coordinate.fromRadians(λ2_harmonised)); + } - /** - * Returns the (azimuth) bearing, in decimal degrees, from standPoint to forePoint - * - * @param standPoint Origin point - * @param forePoint Destination point - * @return (azimuth) bearing, in decimal degrees - */ - public static double bearing(Point standPoint, Point forePoint) { - /* - * Formula: θ = atan2( sin(Δlong).cos(lat2), cos(lat1).sin(lat2) − sin(lat1).cos(lat2).cos(Δlong) ) + /** + * Returns the (azimuth) bearing, in decimal degrees, from standPoint to forePoint + * + * @param standPoint Origin point + * @param forePoint Destination point + * @return (azimuth) bearing, in decimal degrees + */ + public static double bearing(Point standPoint, Point forePoint) { + /* + * Formula: θ = atan2( sin(Δlong).cos(lat2), cos(lat1).sin(lat2) − sin(lat1).cos(lat2).cos(Δlong) ) + */ + + val Δlong = toRadians(forePoint.longitude - standPoint.longitude); + val y = sin(Δlong) * cos(toRadians(forePoint.latitude)); + val x = cos(toRadians(standPoint.latitude)) * sin(toRadians(forePoint.latitude)) + - sin(toRadians(standPoint.latitude)) * cos(toRadians(forePoint.latitude)) * cos(Δlong); + + val bearing = (atan2(y, x) + 2 * PI) % (2 * PI); + + return toDegrees(bearing); + } + + /** + * Returns an area around standPoint + * + * @param standPoint The centre of the area + * @param distance Distance around standPoint, im meters + * @return The area + * @see */ + public static BoundingArea around(Point standPoint, double distance) { - val Δlong = toRadians(forePoint.longitude - standPoint.longitude); - val y = sin(Δlong) * cos(toRadians(forePoint.latitude)); - val x = cos(toRadians(standPoint.latitude)) * sin(toRadians(forePoint.latitude)) - - sin(toRadians(standPoint.latitude)) * cos(toRadians(forePoint.latitude)) * cos(Δlong); + //45 degrees going north-east + val northEast = pointAt(standPoint, 45, distance); - val bearing = (atan2(y, x) + 2 * PI) % (2 * PI); + //225 degrees going south-west + val southWest = pointAt(standPoint, 225, distance); - return toDegrees(bearing); + return BoundingArea.at(northEast, southWest); + } } - /** - * Returns an area around standPoint - * - * @param standPoint The centre of the area - * @param distance Distance around standPoint, im meters - * @return The area - * @see - */ - public static BoundingArea around(Point standPoint, double distance) { + public static class haversine { + /** + * Returns the distance between two points at Haversine formula. + * + * @param standPoint The stand point + * @param forePoint The fore point + * @return The distance, in meters + */ + public static double distance(Point standPoint, Point forePoint) { - //45 degrees going north-east - val northEast = pointAt(standPoint, 45, distance); + val Δλ = toRadians(abs(forePoint.longitude - standPoint.longitude)); + val φ1 = toRadians(standPoint.latitude); + val φ2 = toRadians(forePoint.latitude); - //225 degrees going south-west - val southWest = pointAt(standPoint, 225, distance); + // haversine formula + val Δφ = toRadians(abs(forePoint.latitude - standPoint.latitude)); + val a = sin(Δφ / 2) * sin(Δφ / 2) + cos(φ1) * cos(φ2) * sin(Δλ / 2) * sin(Δλ / 2); + val c = 2 * atan2(sqrt(a), sqrt(1 - a)); //angular distance in radians - return BoundingArea.at(northEast, southWest); + return EARTH_RADIUS * c; + } } - @AllArgsConstructor - private static class Vincenty { - final static Vincenty CO_INCIDENT_POINTS = new Vincenty(0, 0, 0); + public static class vincenty { + /** + * Calculate distance, (azimuth) bearing and final bearing between standPoint and forePoint. + * + * @param standPoint The stand point + * @param forePoint The fore point + * @return Vincenty object which holds all 3 values + */ + private static Vincenty vincenty(Point standPoint, Point forePoint) { + val λ1 = toRadians(standPoint.longitude); + val λ2 = toRadians(forePoint.longitude); + + val φ1 = toRadians(standPoint.latitude); + val φ2 = toRadians(forePoint.latitude); + + val a = 6_378_137D; // radius at equator + val b = EARTH_RADIUS; // Using b to keep close to academic formula. + val f = 1 / 298.257223563D; // flattening of the ellipsoid + + val L = λ2 - λ1; + val tanU1 = (1 - f) * tan(φ1); + val cosU1 = 1 / sqrt((1 + tanU1 * tanU1)); + val sinU1 = tanU1 * cosU1; + + val tanU2 = (1 - f) * tan(φ2); + val cosU2 = 1 / sqrt((1 + tanU2 * tanU2)); + val sinU2 = tanU2 * cosU2; + + double λ = L, λʹ, iterationLimit = 100, cosSqα, σ, cos2σM, cosσ, sinσ, sinλ, cosλ; + do { + sinλ = sin(λ); + cosλ = cos(λ); + val sinSqσ = (cosU2 * sinλ) * (cosU2 * sinλ) + (cosU1 * sinU2 - sinU1 * cosU2 * cosλ) * (cosU1 * sinU2 - sinU1 * cosU2 * cosλ); + sinσ = sqrt(sinSqσ); + if (sinσ == 0) return Vincenty.CO_INCIDENT_POINTS; // co-incident points + cosσ = sinU1 * sinU2 + cosU1 * cosU2 * cosλ; + σ = atan2(sinσ, cosσ); + val sinα = cosU1 * cosU2 * sinλ / sinσ; + cosSqα = 1 - sinα * sinα; + cos2σM = cosσ - 2 * sinU1 * sinU2 / cosSqα; + + if (Double.isNaN(cos2σM)) cos2σM = 0; // equatorial line: cosSqα=0 + val C = f / 16 * cosSqα * (4 + f * (4 - 3 * cosSqα)); + λʹ = λ; + λ = L + (1 - C) * f * sinα * (σ + C * sinσ * (cos2σM + C * cosσ * (-1 + 2 * cos2σM * cos2σM))); + } while (abs(λ - λʹ) > 1e-12 && --iterationLimit > 0); + + if (iterationLimit == 0) throw new IllegalStateException("Formula failed to converge"); + + val uSq = cosSqα * (a * a - b * b) / (b * b); + val A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq))); + val B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq))); + val Δσ = B * sinσ * (cos2σM + B / 4 * (cosσ * (-1 + 2 * cos2σM * cos2σM) - + B / 6 * cos2σM * (-3 + 4 * sinσ * sinσ) * (-3 + 4 * cos2σM * cos2σM))); + + val distance = b * A * (σ - Δσ); + + var initialBearing = atan2(cosU2 * sinλ, cosU1 * sinU2 - sinU1 * cosU2 * cosλ); + initialBearing = (initialBearing + 2 * PI) % (2 * PI); //turning value to trigonometric direction + + var finalBearing = atan2(cosU1 * sinλ, -sinU1 * cosU2 + cosU1 * sinU2 * cosλ); + finalBearing = (finalBearing + 2 * PI) % (2 * PI); //turning value to trigonometric direction + + return new Vincenty(distance, toDegrees(initialBearing), toDegrees(finalBearing)); + } + + public static double distance(Point standPoint, Point forePoint) { + return vincenty(standPoint, forePoint).distance; + } + + /** + * Returns (azimuth) bearing at Vincenty formula. + * + * @param standPoint The stand point + * @param forePoint The fore point + * @return (azimuth) bearing in degrees to the North + * @see + */ + public static double bearing(Point standPoint, Point forePoint) { + return vincenty(standPoint, forePoint).initialBearing; + } /** - * distance is the distance in meter - * initialBearing is the initial bearing, or forward azimuth (in reference to North point), in degrees - * finalBearing is the final bearing (in direction p1→p2), in degrees + * Returns final bearing in direction of standPoint→forePoint at Vincenty formula. + * + * @param standPoint The stand point + * @param forePoint The fore point + * @return (azimuth) bearing in degrees to the North + * @see */ - final double distance, initialBearing, finalBearing; + public static double finalBearing(Point standPoint, Point forePoint) { + return vincenty(standPoint, forePoint).finalBearing; + } + + @AllArgsConstructor + private static class Vincenty { + final static Vincenty CO_INCIDENT_POINTS = new Vincenty(0, 0, 0); + + /** + * distance is the distance in meter + * initialBearing is the initial bearing, or forward azimuth (in reference to North point), in degrees + * finalBearing is the final bearing (in direction p1→p2), in degrees + */ + final double distance, initialBearing, finalBearing; + } } } diff --git a/src/test/java/com/grum/geocalc/DistanceTest.java b/src/test/java/com/grum/geocalc/DistanceTest.java index 43c4397..25d8d1d 100644 --- a/src/test/java/com/grum/geocalc/DistanceTest.java +++ b/src/test/java/com/grum/geocalc/DistanceTest.java @@ -15,7 +15,6 @@ import lombok.extern.slf4j.Slf4j; import lombok.val; import lombok.var; -import org.apache.log4j.Logger; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -35,7 +34,7 @@ public void testSphericalLawOfCosinesDistance() { lng = Coordinate.fromDegrees(-0.3035466); val richmond = Point.at(lat, lng); - assertEquals(2694.283076925272, EarthCalc.gcdDistance(richmond, kew), 10E-3); + assertEquals(2694.283076925272, EarthCalc.gcd.distance(richmond, kew), 10E-3); } @Test @@ -48,7 +47,7 @@ public void testDistanceBnaToLax() { lng = Coordinate.fromDegrees(-118.40); val LAX = Point.at(lat, lng); - assertEquals(2853187.0080671725, EarthCalc.gcdDistance(LAX, BNA), 10E-3); + assertEquals(2853187.0080671725, EarthCalc.gcd.distance(LAX, BNA), 10E-3); } @Test @@ -65,7 +64,7 @@ public void testDistanceToBuenosAires() { val buenosAires = Point.at(lat, lng); - assertEquals(11121, (int) (EarthCalc.gcdDistance(buenosAires, kew) / 1000)); //km + assertEquals(11121, (int) (EarthCalc.gcd.distance(buenosAires, kew) / 1000)); //km } @Test @@ -82,7 +81,7 @@ public void testHaversineDistanceToBuenosAires() { val buenosAires = Point.at(lat, lng); - assertEquals(11121, (int) (EarthCalc.haversineDistance(buenosAires, kew) / 1000)); //km + assertEquals(11121, (int) (EarthCalc.haversine.distance(buenosAires, kew) / 1000)); //km } @Test @@ -99,7 +98,7 @@ public void testVincentyDistanceToBuenosAires() { val buenosAires = Point.at(lat, lng); - assertEquals(11120, (int) (EarthCalc.vincentyDistance(buenosAires, kew) / 1000)); //km + assertEquals(11120, (int) (EarthCalc.vincenty.distance(buenosAires, kew) / 1000)); //km } @Test @@ -114,7 +113,7 @@ public void testSymmetricDistance() { lng = Coordinate.fromDegrees(-0.3035466); val richmond = Point.at(lat, lng); - assertEquals(EarthCalc.gcdDistance(richmond, kew), EarthCalc.gcdDistance(kew, richmond), 10E-10); + assertEquals(EarthCalc.gcd.distance(richmond, kew), EarthCalc.gcd.distance(kew, richmond), 10E-10); } @Test @@ -124,9 +123,9 @@ public void testZeroDistance() { val lng = Coordinate.fromDegrees(-0.2912044); val kew = Point.at(lat, lng); - assertEquals(0, EarthCalc.gcdDistance(kew, kew), 0); - assertEquals(0, EarthCalc.vincentyDistance(kew, kew), 0); - assertEquals(0, EarthCalc.haversineDistance(kew, kew), 0); + assertEquals(0, EarthCalc.gcd.distance(kew, kew), 0); + assertEquals(0, EarthCalc.vincenty.distance(kew, kew), 0); + assertEquals(0, EarthCalc.haversine.distance(kew, kew), 0); } @Test @@ -136,9 +135,9 @@ public void testZeroDistanceWaldshutGermany() { val lng = Coordinate.fromDegrees(8.20897); val waldshut = Point.at(lat, lng); - assertEquals(0, EarthCalc.gcdDistance(waldshut, waldshut), 0); - assertEquals(0, EarthCalc.vincentyDistance(waldshut, waldshut), 0); - assertEquals(0, EarthCalc.haversineDistance(waldshut, waldshut), 0); + assertEquals(0, EarthCalc.gcd.distance(waldshut, waldshut), 0); + assertEquals(0, EarthCalc.vincenty.distance(waldshut, waldshut), 0); + assertEquals(0, EarthCalc.haversine.distance(waldshut, waldshut), 0); } @Test @@ -148,41 +147,41 @@ public void testBoundingAreaDistance() { val lng = Coordinate.fromDegrees(-0.2912044); val kew = Point.at(lat, lng); - val area = EarthCalc.around(kew, 3000); + val area = EarthCalc.gcd.around(kew, 3000); - val northEastDistance = EarthCalc.gcdDistance(kew, area.northEast); + val northEastDistance = EarthCalc.gcd.distance(kew, area.northEast); assertEquals(3000d, northEastDistance, 1E-3); - val southWestDistance = EarthCalc.gcdDistance(kew, area.southWest); + val southWestDistance = EarthCalc.gcd.distance(kew, area.southWest); assertEquals(3000d, southWestDistance, 1E-3); val northWest = area.northWest; - val northWestDistance = EarthCalc.gcdDistance(kew, northWest); + val northWestDistance = EarthCalc.gcd.distance(kew, northWest); assertEquals(3000d, northWestDistance, 2); val southEast = area.southEast; - val southEastDistance = EarthCalc.gcdDistance(kew, southEast); + val southEastDistance = EarthCalc.gcd.distance(kew, southEast); assertEquals(3000d, southEastDistance, 2); val middleNorth = Point.at(Coordinate.fromDegrees(area.northEast.latitude), Coordinate.fromDegrees((area.southWest.longitude + area.northEast.longitude) / 2)); - val middleNorthDistance = EarthCalc.gcdDistance(kew, middleNorth); + val middleNorthDistance = EarthCalc.gcd.distance(kew, middleNorth); assertEquals(2120d, middleNorthDistance, 1); val middleSouth = Point.at(Coordinate.fromDegrees(area.southWest.latitude), Coordinate.fromDegrees((area.southWest.longitude + area.northEast.longitude) / 2)); - val middleSouthDistance = EarthCalc.gcdDistance(kew, middleSouth); + val middleSouthDistance = EarthCalc.gcd.distance(kew, middleSouth); assertEquals(2120d, middleSouthDistance, 2); val middleWest = Point.at(Coordinate.fromDegrees((area.northEast.latitude + area.southWest.latitude) / 2), Coordinate.fromDegrees(area.northEast.longitude)); - val middleWestDistance = EarthCalc.gcdDistance(kew, middleWest); + val middleWestDistance = EarthCalc.gcd.distance(kew, middleWest); log.info("Middle West => " + middleWestDistance); assertEquals(2120d, middleWestDistance, 3); val middleEast = Point.at(Coordinate.fromDegrees((area.northEast.latitude + area.southWest.latitude) / 2), Coordinate.fromDegrees(area.southWest.longitude)); - val middleEastDistance = EarthCalc.gcdDistance(kew, middleEast); + val middleEastDistance = EarthCalc.gcd.distance(kew, middleEast); log.info("Middle East => " + middleEastDistance); assertEquals(2120d, middleEastDistance, 1); } @@ -194,7 +193,7 @@ public void testBoundingAreaNorthPole() { val lng = Coordinate.fromDegrees(0); val northPole = Point.at(lat, lng); - val area = EarthCalc.around(northPole, 10000); + val area = EarthCalc.gcd.around(northPole, 10000); log.info("North East => " + area.northEast); log.info("South West => " + area.southWest); @@ -212,7 +211,7 @@ public void testBoundingAreaNextToLondon() { val lng = Coordinate.fromDegrees(-0.1997387000000117); val northPole = Point.at(lat, lng); - val area = EarthCalc.around(northPole, 5); + val area = EarthCalc.gcd.around(northPole, 5); log.info("North East => " + area.northEast); log.info("South West => " + area.southWest); @@ -230,15 +229,15 @@ public void testPointRadialDistanceZero() { val lng = Coordinate.fromDegrees(-0.2912044); Point kew = Point.at(lat, lng); - var sameKew = EarthCalc.pointAt(kew, 45, 0); + var sameKew = EarthCalc.gcd.pointAt(kew, 45, 0); assertEquals(lat.degrees(), sameKew.latitude, 1E-10); assertEquals(lng.degrees(), sameKew.longitude, 1E-10); - sameKew = EarthCalc.pointAt(kew, 90, 0); + sameKew = EarthCalc.gcd.pointAt(kew, 90, 0); assertEquals(lat.degrees(), sameKew.latitude, 1E-10); assertEquals(lng.degrees(), sameKew.longitude, 1E-10); - sameKew = EarthCalc.pointAt(kew, 180, 0); + sameKew = EarthCalc.gcd.pointAt(kew, 180, 0); assertEquals(lat.degrees(), sameKew.latitude, 1E-10); assertEquals(lng.degrees(), sameKew.longitude, 1E-10); } @@ -255,10 +254,10 @@ public void testPointRadialDistance() { lng = Coordinate.fromDegrees(-0.3035466); val richmond = Point.at(lat, lng); - val distance = EarthCalc.gcdDistance(kew, richmond); - val bearing = EarthCalc.bearing(kew, richmond); + val distance = EarthCalc.gcd.distance(kew, richmond); + val bearing = EarthCalc.gcd.bearing(kew, richmond); - val allegedRichmond = EarthCalc.pointAt(kew, bearing, distance); + val allegedRichmond = EarthCalc.gcd.pointAt(kew, bearing, distance); assertEquals(richmond.latitude, allegedRichmond.latitude, 10E-5); assertEquals(richmond.longitude, allegedRichmond.longitude, 10E-5); @@ -276,8 +275,8 @@ public void testBearing() { lng = Coordinate.fromDegrees(-0.3035466); val richmond = Point.at(lat, lng); - System.out.println(EarthCalc.bearing(kew, richmond)); - assertEquals(EarthCalc.bearing(kew, richmond), 198.4604614570758D, 10E-5); + System.out.println(EarthCalc.gcd.bearing(kew, richmond)); + assertEquals(EarthCalc.gcd.bearing(kew, richmond), 198.4604614570758D, 10E-5); } @Test @@ -306,7 +305,7 @@ public void testBearingGitHubIssue3() { * returns a bearing of 019°13′50″ * and not 19.213575108209017 */ - assertEquals(EarthCalc.bearing(standpoint, forepoint), Coordinate.fromDMS(19, 13, 50).degrees(), 10E-5); + assertEquals(EarthCalc.gcd.bearing(standpoint, forepoint), Coordinate.fromDMS(19, 13, 50).degrees(), 10E-5); } @Test @@ -322,8 +321,8 @@ public void testVincentyBearing() { val richmond = Point.at(lat, lng); //comparing to results from http://www.movable-type.co.uk/scripts/latlong.html - assertEquals(EarthCalc.vincentyBearing(kew, richmond), Coordinate.fromDMS(198, 30, 19.58).degrees(), 10E-5); - assertEquals(EarthCalc.vincentyFinalBearing(kew, richmond), Coordinate.fromDMS(198, 29, 44.82).degrees(), 10E-5); + assertEquals(EarthCalc.vincenty.bearing(kew, richmond), Coordinate.fromDMS(198, 30, 19.58).degrees(), 10E-5); + assertEquals(EarthCalc.vincenty.finalBearing(kew, richmond), Coordinate.fromDMS(198, 29, 44.82).degrees(), 10E-5); } @Test @@ -339,6 +338,6 @@ public void testMidPoint() { val richmond = Point.at(lat, lng); //comparing to results from http://www.movable-type.co.uk/scripts/latlong.html - assertEquals(EarthCalc.midPoint(richmond, kew), Point.at(Coordinate.fromDegrees(51.47285976194266), Coordinate.fromDegrees(-0.2973770580524634))); + assertEquals(EarthCalc.gcd.midPoint(richmond, kew), Point.at(Coordinate.fromDegrees(51.47285976194266), Coordinate.fromDegrees(-0.2973770580524634))); } } From 11815b2ec7b4a8e16c5b562698189cdeb58a9a82 Mon Sep 17 00:00:00 2001 From: Romain Gallet Date: Sat, 3 Oct 2020 22:17:02 +0100 Subject: [PATCH 6/7] Typos --- pom.xml | 6 +++--- src/main/java/com/grum/geocalc/Coordinate.java | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index effd20c..8a75f1a 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ jar 0.6 ${project.groupId}.${project.artifactId} - httpd://www.grumlimited.co.uk + https://www.grumlimited.co.uk 3.1.0 @@ -24,14 +24,14 @@ Grum Ltd - httpd://www.grumlimited.co.uk + https://www.grumlimited.co.uk GR211 rgallet_at_grumlimited_co_uk Grum Limited - http://www.grumlimited.co.uk + https://www.grumlimited.co.uk Blackleg diff --git a/src/main/java/com/grum/geocalc/Coordinate.java b/src/main/java/com/grum/geocalc/Coordinate.java index 6e0705e..1f82bfb 100644 --- a/src/main/java/com/grum/geocalc/Coordinate.java +++ b/src/main/java/com/grum/geocalc/Coordinate.java @@ -78,11 +78,11 @@ DegreeCoordinate toDegreeCoordinate() { } GPSCoordinate toGPSCoordinate() { - val _wholeDegrees = floor(degrees()); - val remaining = degrees() - _wholeDegrees; - val _minutes = floor(remaining * 60); + val wholeDegrees = floor(degrees()); + val remaining = degrees() - wholeDegrees; + val minutes = floor(remaining * 60); - return new GPSCoordinate(_wholeDegrees, _minutes); + return new GPSCoordinate(wholeDegrees, minutes); } RadianCoordinate toRadianCoordinate() { From 4de8d7cf6a21cca37953f91b9afc8c98e3d332e3 Mon Sep 17 00:00:00 2001 From: Romain Gallet Date: Sat, 3 Oct 2020 22:20:11 +0100 Subject: [PATCH 7/7] Readme --- README.md | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/README.md b/README.md index 5301809..731aed2 100644 --- a/README.md +++ b/README.md @@ -16,36 +16,6 @@ This library is being used on [rentbarometer.com](https://www.rentbarometer.com) This library implements in Java lots of ideas from [Movable-Type](http://www.movable-type.co.uk/scripts/latlong.html). Many thanks. -## Changelog - -### 0.5.8 -* Renamed in `EarthCalc.EARTH_DIAMETER` to `EarthCalc.EARTH_RADIUS`. Thanks [pradeepmurugesan](https://github.com/pradeepmurugesan) - - -### 0.5.7 -* Bugfix in `EarthCalc.gcdDictance` [24](https://github.com/grumlimited/geocalc/issues/24). Thanks [dusiema](https://github.com/dusiema) - -### 0.5.6 -* Replaced `new BoundingArea(...)` with `BoundingArea.at` -* Bugfix in `EarthCalc.pointAt`. Normalising `λ2` so that bearing starts clockwise from north - -### 0.5.5 -* Renamed `getVincentyFinalBearing` to `vincentyFinalBearing` -* Updated copyright - -### 0.5.4 -* added `EarthCalc.midPoint(p1, p2)` -* renamed `EarthCalc.pointRadialDistance()` to `EarthCalc.pointAt(...)` -* renamed `BoundingArea.boundingArea()` to `BoundingArea.around(...)` -* removed `get...()` out of `Point` and `BoundingArea` -* added maven javadoc plugin - -### 0.5.3 -* changed constructors to default and private visibility -* removed `get...()` keyword out of `EarthCalc` methods -* `EarthCalc.getDistance()` is now `EarthCalc.gcdDistance()` -* renamed `BoundingArea.isContainedWithin(...)` to `BoundingArea.contains(...)` - ### Embed