Add geographic-to-grid and grid-to-geographic projection methods in CoordinateConverter

- Introduced `toGrid` method enabling projection of geographic coordinates to grid coordinates with altitude source handling.
- Added `toCoordinate` method for inverse projection, converting grid coordinates back to geographic coordinates.
- Improved internal handling of altitude and ellipsoidal height for transformations.
This commit is contained in:
Jan Meinl
2026-05-15 04:31:16 +02:00
parent 7bcf1bb0d6
commit abadc40ed6
@@ -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}.
* <p>
* 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}.
*/