diff --git a/src/main/java/dev/coph/flightscore/backend/map/CoordinateConverter.java b/src/main/java/dev/coph/flightscore/backend/map/CoordinateConverter.java index 0fcb407..de20207 100644 --- a/src/main/java/dev/coph/flightscore/backend/map/CoordinateConverter.java +++ b/src/main/java/dev/coph/flightscore/backend/map/CoordinateConverter.java @@ -1,5 +1,9 @@ package dev.coph.flightscore.backend.map; +import dev.coph.flightscore.backend.competition.AltitudeSource; +import dev.coph.flightscore.backend.coordinate.Altitude; +import dev.coph.flightscore.backend.coordinate.Coordinate; + /** * High precision conversion utility between any combination of {@link MapDatum} * and {@link MapGrid}. @@ -32,7 +36,9 @@ package dev.coph.flightscore.backend.map; public final class CoordinateConverter { private static final double ARCSEC_TO_RAD = Math.PI / (180.0 * 3600.0); - /** Iteration tolerance for the Newton/fixed-point solvers (radians). */ + /** + * Iteration tolerance for the Newton/fixed-point solvers (radians). + */ private static final double TOLERANCE = 1.0e-14; private static final int MAX_ITERATIONS = 100; @@ -47,11 +53,11 @@ public final class CoordinateConverter { * Converts a grid coordinate from one {@code (datum, grid)} combination into * any other {@code (datum, grid)} combination. * - * @param source the coordinate in the source grid - * @param sourceDatum datum the source coordinate is referenced to - * @param sourceGrid grid the source coordinate is projected with - * @param targetDatum datum the result shall be referenced to - * @param targetGrid grid the result shall be projected with + * @param source the coordinate in the source grid + * @param sourceDatum datum the source coordinate is referenced to + * @param sourceGrid grid the source coordinate is projected with + * @param targetDatum datum the result shall be referenced to + * @param targetGrid grid the result shall be projected with * @return the coordinate expressed in the target {@code (datum, grid)} combination */ public static GridCoordinate convert(GridCoordinate source, @@ -73,6 +79,36 @@ public final class CoordinateConverter { return toGrid(transformDatum(source, sourceDatum, targetDatum), targetDatum, targetGrid); } + /** + * Projects a {@link Coordinate} (referenced to {@code datum}) onto {@code grid}. + *

+ * The {@link Coordinate#gpsAltitude() GPS altitude} is taken as the ellipsoidal + * height since it is the GNSS-derived height and therefore the closest match; + * the barometric altitude is used as a fallback and {@code 0} if neither is set. + */ + public static GridCoordinate toGrid(Coordinate coordinate, AltitudeSource altitudeSource, MapDatum datum, MapGrid grid) { + return toGrid(toGeographicCoordinate(coordinate, altitudeSource), datum, grid); + } + + /** + * Inverse projection of a grid coordinate onto a {@link Coordinate}. The + * resulting ellipsoidal height is stored as the coordinate's altitude. + */ + public static Coordinate toCoordinate(GridCoordinate grid, MapDatum datum, MapGrid mapGrid) { + GeographicCoordinate geographic = toGeographic(grid, datum, mapGrid); + return new Coordinate(geographic.latitude(), geographic.longitude(), + Altitude.fromMeters(geographic.ellipsoidalHeight())); + } + + private static GeographicCoordinate toGeographicCoordinate(Coordinate coordinate, AltitudeSource source) { + Altitude altitude = switch (source) { + case GPS -> coordinate.gpsAltitude(); + case BAROMETRIC -> coordinate.barometricAltitude(); + }; + double height = altitude != null ? altitude.meters() : 0.0; + return new GeographicCoordinate(coordinate.latitude(), coordinate.longitude(), height); + } + /** * Projects a geographic coordinate (referenced to {@code datum}) onto {@code grid}. */