Add map projection framework and integrate initial SwissGrid and UTM projections

- Introduced `MapProjection` interface for handling coordinate projections and unprojections.
- Added `SwissGridProjection` and `SwissGridVariant` for Swiss coordinate systems.
- Implemented `UtmProjection` and `UtmResult` for UTM coordinate systems, including hemisphere and zone handling.
- Created supporting classes such as `GeodeticDatum`, `MapDate`, and projection-specific result types for extensibility.
- Extended `BalloonLiveParser` and added `TrackParser` interface to structure track parsing functionality.
This commit is contained in:
Jan Meinl
2026-05-14 16:20:41 +02:00
parent 015a780760
commit 7b3209b161
10 changed files with 139 additions and 2 deletions
@@ -0,0 +1,7 @@
package dev.coph.flightscore.backend.map;
public record GeodeticDatum(double semiMajorAxis, double flattening, double dx, double dy, double dz) {
public static final GeodeticDatum WGS84 = new GeodeticDatum(6378137.0, 1 / 298.257223563, 0, 0, 0);
public static final GeodeticDatum ETRS89 = new GeodeticDatum(6378137.0, 1 / 298.257222101, 0, 0, 0);
public static final GeodeticDatum BESSEL_CH = new GeodeticDatum(6377397.155, 1 / 299.1528128, 674.374, 15.056, 405.346);
}
@@ -0,0 +1,6 @@
package dev.coph.flightscore.backend.map;
public class MapDate {
}
@@ -0,0 +1,10 @@
package dev.coph.flightscore.backend.map.projection;
import dev.coph.flightscore.backend.coordinate.Coordinate;
import dev.coph.flightscore.backend.map.GeodeticDatum;
public interface MapProjection<T> {
T project(double latitude, double longitude, GeodeticDatum sourceDatum);
Coordinate unproject(T projectedCoordinate, GeodeticDatum sourceDatum);
}
@@ -0,0 +1,24 @@
package dev.coph.flightscore.backend.map.projection.swiss;
import dev.coph.flightscore.backend.coordinate.Coordinate;
import dev.coph.flightscore.backend.map.GeodeticDatum;
import dev.coph.flightscore.backend.map.projection.MapProjection;
public class SwissGridProjection implements MapProjection<SwissResult> {
private final GeodeticDatum targetDatum = GeodeticDatum.BESSEL_CH;
private final SwissGridVariant variant;
public SwissGridProjection(SwissGridVariant variant) {
this.variant = variant;
}
@Override
public SwissResult project(double latitude, double longitude, GeodeticDatum sourceDatum) {
return null;
}
@Override
public Coordinate unproject(SwissResult projectedCoordinate, GeodeticDatum sourceDatum) {
return null;
}
}
@@ -0,0 +1,8 @@
package dev.coph.flightscore.backend.map.projection.swiss;
public enum SwissGridVariant {
LV03,
LV95;
}
@@ -0,0 +1,6 @@
package dev.coph.flightscore.backend.map.projection.swiss;
public record SwissResult(double east, double north) {
}
@@ -0,0 +1,52 @@
package dev.coph.flightscore.backend.map.projection.utm;
import dev.coph.flightscore.backend.coordinate.Coordinate;
import dev.coph.flightscore.backend.map.GeodeticDatum;
import dev.coph.flightscore.backend.map.projection.MapProjection;
public class UtmProjection implements MapProjection<UtmResult> {
private final int zone;
private final boolean isNorthernHemisphere;
private final GeodeticDatum targetDatum;
public UtmProjection(int zone, boolean isNorthernHemisphere, GeodeticDatum targetDatum) {
this.zone = zone;
this.isNorthernHemisphere = isNorthernHemisphere;
this.targetDatum = targetDatum;
}
@Override
public UtmResult project(double latitude, double longitude, GeodeticDatum sourceDatum) {
return null;
}
@Override
public Coordinate unproject(UtmResult projectedCoordinate, GeodeticDatum sourceDatum) {
return null;
}
private char getUtmLetterDesignator(double lat) {
if (lat <= 84 && lat >= 72) return 'X';
else if (lat < 72 && lat >= 64) return 'W';
else if (lat < 64 && lat >= 56) return 'V';
else if (lat < 56 && lat >= 48) return 'U';
else if (lat < 48 && lat >= 40) return 'T';
else if (lat < 40 && lat >= 32) return 'S';
else if (lat < 32 && lat >= 24) return 'R';
else if (lat < 24 && lat >= 16) return 'Q';
else if (lat < 16 && lat >= 8) return 'P';
else if (lat < 8 && lat >= 0) return 'N';
else if (lat < 0 && lat >= -8) return 'M';
else if (lat < -8 && lat >= -16) return 'L';
else if (lat < -16 && lat >= -24) return 'K';
else if (lat < -24 && lat >= -32) return 'J';
else if (lat < -32 && lat >= -40) return 'H';
else if (lat < -40 && lat >= -48) return 'G';
else if (lat < -48 && lat >= -56) return 'F';
else if (lat < -56 && lat >= -64) return 'E';
else if (lat < -64 && lat >= -72) return 'D';
else if (lat < -72 && lat >= -80) return 'C';
else return 'Z';
}
}
@@ -0,0 +1,7 @@
package dev.coph.flightscore.backend.map.projection.utm;
public record UtmResult(double easting, double northing, int zone, char band, String fullZone) {
public UtmResult(double easting, double northing, int zone, char band) {
this(easting, northing, zone, band, zone + String.valueOf(band));
}
}
@@ -1,7 +1,12 @@
package dev.coph.flightscore.backend.track.parser;
public class BalloonLiveParser {
import dev.coph.flightscore.backend.track.Track;
public class BalloonLiveParser implements TrackParser{
@Override
public Track parse(String[] lines) {
}
}
@@ -0,0 +1,12 @@
package dev.coph.flightscore.backend.track.parser;
import dev.coph.flightscore.backend.coordinate.Coordinate;
import dev.coph.flightscore.backend.track.Track;
import java.io.File;
public interface TrackParser {
public Track parse(String[] lines, Coordinate referenceCoordinate );
}