From f2bb070f6862a627d4d70dd62fd8bfaa51ea255e Mon Sep 17 00:00:00 2001 From: staudtMarius Date: Tue, 8 Nov 2022 08:54:51 +0100 Subject: [PATCH 1/5] Adding new feature to GeoUtils --- src/main/java/edu/ie3/util/geo/GeoUtils.java | 43 ++++++++++++++----- .../edu/ie3/util/geo/GeoUtilsTest.groovy | 34 ++++++++++++--- 2 files changed, 59 insertions(+), 18 deletions(-) diff --git a/src/main/java/edu/ie3/util/geo/GeoUtils.java b/src/main/java/edu/ie3/util/geo/GeoUtils.java index 6dcc1e13..d650fb50 100644 --- a/src/main/java/edu/ie3/util/geo/GeoUtils.java +++ b/src/main/java/edu/ie3/util/geo/GeoUtils.java @@ -5,7 +5,8 @@ */ package edu.ie3.util.geo; -import static edu.ie3.util.quantities.PowerSystemUnits.*; +import static edu.ie3.util.quantities.PowerSystemUnits.METRE; +import static edu.ie3.util.quantities.PowerSystemUnits.RADIAN; import static java.lang.Math.*; import edu.ie3.util.exceptions.GeoException; @@ -16,15 +17,7 @@ import javax.measure.quantity.Angle; import javax.measure.quantity.Length; import org.locationtech.jts.algorithm.ConvexHull; -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.GeometryCollection; -import org.locationtech.jts.geom.GeometryFactory; -import org.locationtech.jts.geom.LineString; -import org.locationtech.jts.geom.LinearRing; -import org.locationtech.jts.geom.Point; -import org.locationtech.jts.geom.Polygon; -import org.locationtech.jts.geom.PrecisionModel; +import org.locationtech.jts.geom.*; import org.locationtech.jts.geom.impl.CoordinateArraySequence; import org.locationtech.jts.math.Vector2D; import tech.units.indriya.ComparableQuantity; @@ -34,7 +27,7 @@ public class GeoUtils { public static final GeometryFactory DEFAULT_GEOMETRY_FACTORY = new GeometryFactory(new PrecisionModel(), 4326); - private static final ComparableQuantity EARTH_RADIUS = + public static final ComparableQuantity EARTH_RADIUS = Quantities.getQuantity(6378137.0, METRE); protected GeoUtils() { @@ -211,6 +204,34 @@ public static ComparableQuantity calcHaversine(LineString lineString) { return y; } + /** + * Method to turn a distance into a latitude and longitude deltas. The methode can be found here: + * source + * + * @param coordinate the starting point for the calculation + * @param distance maximal distance in x- and y-direction + * @return x- and y-delta in degree as a coordinate + */ + public static Coordinate calculateXYDelta(Point coordinate, ComparableQuantity distance) { + // y-degrees are evenly spaced, so we can just divide a distance + // by the earth's radius to get a y-delta in radians + double deltaY = distance.divide(EARTH_RADIUS).getValue().doubleValue(); + + // because the spacing between x-degrees change between the equator + // and the poles, we need to calculate the x-delta using the inverse + // haversine formula + double sinus = Math.sin(deltaY / 2); + double squaredSinus = Math.pow(sinus, 2); + double cosine = Math.cos(Math.toRadians(coordinate.getY())); + double squaredCosine = Math.pow(cosine, 2); + + double deltaX = 2 * Math.asin(Math.sqrt(squaredSinus / squaredCosine)); + + // converting the deltas to degree and returning them as a coordinate + return new Coordinate(Math.toDegrees(deltaX), Math.toDegrees(deltaY)); + } + /** * Builds a convex hull from a set of latitude/longitude coordinates. * diff --git a/src/test/groovy/edu/ie3/util/geo/GeoUtilsTest.groovy b/src/test/groovy/edu/ie3/util/geo/GeoUtilsTest.groovy index 44f1522e..998804c6 100644 --- a/src/test/groovy/edu/ie3/util/geo/GeoUtilsTest.groovy +++ b/src/test/groovy/edu/ie3/util/geo/GeoUtilsTest.groovy @@ -5,14 +5,8 @@ */ package edu.ie3.util.geo -import static edu.ie3.util.quantities.PowerSystemUnits.METRE - -import edu.ie3.util.quantities.PowerSystemUnits import edu.ie3.util.quantities.QuantityUtil -import org.locationtech.jts.geom.Coordinate -import org.locationtech.jts.geom.GeometryFactory -import org.locationtech.jts.geom.LineString -import org.locationtech.jts.geom.PrecisionModel +import org.locationtech.jts.geom.* import org.locationtech.jts.io.geojson.GeoJsonReader import spock.lang.Shared import spock.lang.Specification @@ -21,6 +15,8 @@ import tech.units.indriya.quantity.Quantities import javax.measure.quantity.Length +import static edu.ie3.util.quantities.PowerSystemUnits.METRE + class GeoUtilsTest extends Specification { @Shared @@ -294,4 +290,28 @@ class GeoUtilsTest extends Specification { Math.abs(actual.x - 7.5) < 1e-9 Math.abs(actual.y - 50d) < 1e-9 } + + def "GeoUtils calculates x-delta (longitude) correctly"(){ + given: + Point coordinate = GeoUtils.buildPoint(50, 7) + ComparableQuantity distanceX = GeoUtils.calcHaversine(50, 6, 50, 5) + + when: + Coordinate deltas = GeoUtils.calculateXYDelta(coordinate, distanceX) + + then: + deltas.getX() == 1 + } + + def "GeoUtils calculates x-delta (latitude) correctly"(){ + given: + Point coordinate = GeoUtils.buildPoint(50, 7) + ComparableQuantity distanceX = GeoUtils.calcHaversine(52, 7, 51, 7) + + when: + Coordinate deltas = GeoUtils.calculateXYDelta(coordinate, distanceX) + + then: + deltas.getY() == 1 + } } \ No newline at end of file From 16b6dd25e42cd8ac3e447061a2f6d0c427fb8ebd Mon Sep 17 00:00:00 2001 From: staudtMarius Date: Tue, 8 Nov 2022 08:56:33 +0100 Subject: [PATCH 2/5] Adding to CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e53329f6..0351e4c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `OsmEntity` and `OsmContainer` to provide a simple, lightweight representation of openstreetmap data - QuantityUtils previously implemented in SIMONA [#288](https://github.com/ie3-institute/PowerSystemUtils/issues/288) - Enhanced `RichQuantityDouble` with new units and generic method [#312](https://github.com/ie3-institute/PowerSystemUtils/issues/312) +- Calculation of x- and y-deltas to `GeoUtils` [#320](https://github.com/ie3-institute/PowerSystemUtils/issues/320) ### Changed - Refactored `GeoUtils`, moved them to the scala package and tailored them toward the `loactiontec.jts` Geometries used in the `OsmContainer` [#163](https://github.com/ie3-institute/PowerSystemUtils/issues/163) From 60a650610314be658278389472875d90feee77e4 Mon Sep 17 00:00:00 2001 From: staudtMarius Date: Fri, 11 Nov 2022 11:46:04 +0100 Subject: [PATCH 3/5] Implementing requested changes. --- CHANGELOG.md | 2 +- src/main/java/edu/ie3/util/geo/GeoUtils.java | 16 +++++++---- .../edu/ie3/util/geo/GeoUtilsTest.groovy | 28 ++++++++++++++----- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0351e4c0..fc3f1d62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `OsmEntity` and `OsmContainer` to provide a simple, lightweight representation of openstreetmap data - QuantityUtils previously implemented in SIMONA [#288](https://github.com/ie3-institute/PowerSystemUtils/issues/288) - Enhanced `RichQuantityDouble` with new units and generic method [#312](https://github.com/ie3-institute/PowerSystemUtils/issues/312) -- Calculation of x- and y-deltas to `GeoUtils` [#320](https://github.com/ie3-institute/PowerSystemUtils/issues/320) +- Calculation of bounding box to `GeoUtils` [#320](https://github.com/ie3-institute/PowerSystemUtils/issues/320) ### Changed - Refactored `GeoUtils`, moved them to the scala package and tailored them toward the `loactiontec.jts` Geometries used in the `OsmContainer` [#163](https://github.com/ie3-institute/PowerSystemUtils/issues/163) diff --git a/src/main/java/edu/ie3/util/geo/GeoUtils.java b/src/main/java/edu/ie3/util/geo/GeoUtils.java index d650fb50..fb3afce2 100644 --- a/src/main/java/edu/ie3/util/geo/GeoUtils.java +++ b/src/main/java/edu/ie3/util/geo/GeoUtils.java @@ -205,15 +205,17 @@ public static ComparableQuantity calcHaversine(LineString lineString) { } /** - * Method to turn a distance into a latitude and longitude deltas. The methode can be found here: + * Method for calculating a bounding box around a point. This method uses an inverse haversine to turn + * a distance into x- and y-deltas. These deltas are used to create an envelope which represents the + * bounding box. The methode can be found here: * source * * @param coordinate the starting point for the calculation * @param distance maximal distance in x- and y-direction - * @return x- and y-delta in degree as a coordinate + * @return envelope */ - public static Coordinate calculateXYDelta(Point coordinate, ComparableQuantity distance) { + public static Envelope calculateBoundingBox(Point coordinate, ComparableQuantity distance) { // y-degrees are evenly spaced, so we can just divide a distance // by the earth's radius to get a y-delta in radians double deltaY = distance.divide(EARTH_RADIUS).getValue().doubleValue(); @@ -228,8 +230,12 @@ public static Coordinate calculateXYDelta(Point coordinate, ComparableQuantity distanceX = GeoUtils.calcHaversine(50, 6, 50, 5) + ComparableQuantity distanceX = GeoUtils.calcHaversine(50, 7, 50, 6) when: - Coordinate deltas = GeoUtils.calculateXYDelta(coordinate, distanceX) + Envelope envelope = GeoUtils.calculateBoundingBox(coordinate, distanceX) then: - deltas.getX() == 1 + envelope.getMinX() == 6 + envelope.getMaxX() == 8 + + double yMin = 49.35721717797476 + double yMax = 50.64278282202524 + + envelope.getMinY() == yMin + envelope.getMaxY() == yMax } - def "GeoUtils calculates x-delta (latitude) correctly"(){ + def "GeoUtils calculates y-delta (latitude) correctly"(){ given: Point coordinate = GeoUtils.buildPoint(50, 7) - ComparableQuantity distanceX = GeoUtils.calcHaversine(52, 7, 51, 7) + ComparableQuantity distanceX = GeoUtils.calcHaversine(51, 7, 52, 7) when: - Coordinate deltas = GeoUtils.calculateXYDelta(coordinate, distanceX) + Envelope envelope = GeoUtils.calculateBoundingBox(coordinate, distanceX) then: - deltas.getY() == 1 + double xMin = 5.444248126340358 + double xMax = 8.555751873659641 + + envelope.getMinX() == xMin + envelope.getMaxX() == xMax + + envelope.getMinY() == 49 + envelope.getMaxY() == 51 } } \ No newline at end of file From ad3621151cd5880ef867873ba30d5af7f321fe22 Mon Sep 17 00:00:00 2001 From: staudtMarius Date: Fri, 11 Nov 2022 12:21:37 +0100 Subject: [PATCH 4/5] fmt --- src/main/java/edu/ie3/util/geo/GeoUtils.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/main/java/edu/ie3/util/geo/GeoUtils.java b/src/main/java/edu/ie3/util/geo/GeoUtils.java index fb3afce2..4f1538d2 100644 --- a/src/main/java/edu/ie3/util/geo/GeoUtils.java +++ b/src/main/java/edu/ie3/util/geo/GeoUtils.java @@ -205,17 +205,17 @@ public static ComparableQuantity calcHaversine(LineString lineString) { } /** - * Method for calculating a bounding box around a point. This method uses an inverse haversine to turn - * a distance into x- and y-deltas. These deltas are used to create an envelope which represents the - * bounding box. The methode can be found here: - * source * * @param coordinate the starting point for the calculation * @param distance maximal distance in x- and y-direction * @return envelope */ - public static Envelope calculateBoundingBox(Point coordinate, ComparableQuantity distance) { + public static Envelope calculateBoundingBox( + Point coordinate, ComparableQuantity distance) { // y-degrees are evenly spaced, so we can just divide a distance // by the earth's radius to get a y-delta in radians double deltaY = distance.divide(EARTH_RADIUS).getValue().doubleValue(); @@ -234,8 +234,13 @@ public static Envelope calculateBoundingBox(Point coordinate, ComparableQuantity double deltaXDegree = Math.toDegrees(deltaX); double deltaYDegree = Math.toDegrees(deltaY); - // calculating minimums and maximums for longitude and latitude and returning them as an envelope - return new Envelope(coordinate.getX() - deltaXDegree, coordinate.getX() + deltaXDegree, coordinate.getY() - deltaYDegree, coordinate.getY() + deltaYDegree); + // calculating minimums and maximums for longitude and latitude and returning them as an + // envelope + return new Envelope( + coordinate.getX() - deltaXDegree, + coordinate.getX() + deltaXDegree, + coordinate.getY() - deltaYDegree, + coordinate.getY() + deltaYDegree); } /** From 08f27e65e14b005bb91702aa543625a523f88246 Mon Sep 17 00:00:00 2001 From: staudtMarius Date: Fri, 25 Nov 2022 10:32:31 +0100 Subject: [PATCH 5/5] Fixing ``codacy`` errors. --- src/main/java/edu/ie3/util/geo/GeoUtils.java | 14 ++++++------- .../edu/ie3/util/geo/GeoUtilsTest.groovy | 20 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/main/java/edu/ie3/util/geo/GeoUtils.java b/src/main/java/edu/ie3/util/geo/GeoUtils.java index 4f1538d2..ca936db4 100644 --- a/src/main/java/edu/ie3/util/geo/GeoUtils.java +++ b/src/main/java/edu/ie3/util/geo/GeoUtils.java @@ -223,16 +223,16 @@ public static Envelope calculateBoundingBox( // because the spacing between x-degrees change between the equator // and the poles, we need to calculate the x-delta using the inverse // haversine formula - double sinus = Math.sin(deltaY / 2); - double squaredSinus = Math.pow(sinus, 2); - double cosine = Math.cos(Math.toRadians(coordinate.getY())); - double squaredCosine = Math.pow(cosine, 2); + double sinus = sin(deltaY / 2); + double squaredSinus = pow(sinus, 2); + double cosine = cos(toRadians(coordinate.getY())); + double squaredCosine = pow(cosine, 2); - double deltaX = 2 * Math.asin(Math.sqrt(squaredSinus / squaredCosine)); + double deltaX = 2 * asin(sqrt(squaredSinus / squaredCosine)); // turning the deltas to degree - double deltaXDegree = Math.toDegrees(deltaX); - double deltaYDegree = Math.toDegrees(deltaY); + double deltaXDegree = toDegrees(deltaX); + double deltaYDegree = toDegrees(deltaY); // calculating minimums and maximums for longitude and latitude and returning them as an // envelope diff --git a/src/test/groovy/edu/ie3/util/geo/GeoUtilsTest.groovy b/src/test/groovy/edu/ie3/util/geo/GeoUtilsTest.groovy index 4f575d88..badade3d 100644 --- a/src/test/groovy/edu/ie3/util/geo/GeoUtilsTest.groovy +++ b/src/test/groovy/edu/ie3/util/geo/GeoUtilsTest.groovy @@ -291,7 +291,7 @@ class GeoUtilsTest extends Specification { Math.abs(actual.y - 50d) < 1e-9 } - def "GeoUtils calculates x-delta (longitude) correctly"(){ + def "GeoUtils calculates x-delta (longitude) correctly"() { given: Point coordinate = GeoUtils.buildPoint(50, 7) ComparableQuantity distanceX = GeoUtils.calcHaversine(50, 7, 50, 6) @@ -300,17 +300,17 @@ class GeoUtilsTest extends Specification { Envelope envelope = GeoUtils.calculateBoundingBox(coordinate, distanceX) then: - envelope.getMinX() == 6 - envelope.getMaxX() == 8 + envelope.minX == 6 + envelope.maxX == 8 double yMin = 49.35721717797476 double yMax = 50.64278282202524 - envelope.getMinY() == yMin - envelope.getMaxY() == yMax + envelope.minY == yMin + envelope.maxY == yMax } - def "GeoUtils calculates y-delta (latitude) correctly"(){ + def "GeoUtils calculates y-delta (latitude) correctly"() { given: Point coordinate = GeoUtils.buildPoint(50, 7) ComparableQuantity distanceX = GeoUtils.calcHaversine(51, 7, 52, 7) @@ -322,10 +322,10 @@ class GeoUtilsTest extends Specification { double xMin = 5.444248126340358 double xMax = 8.555751873659641 - envelope.getMinX() == xMin - envelope.getMaxX() == xMax + envelope.minX == xMin + envelope.maxX == xMax - envelope.getMinY() == 49 - envelope.getMaxY() == 51 + envelope.minY == 49 + envelope.maxY == 51 } } \ No newline at end of file