diff --git a/co-simulation/bundle/src/assembly/resources/scenarios/Town04/scenario_config.json b/co-simulation/bundle/src/assembly/resources/scenarios/Town04/scenario_config.json index 09f4c3d1..4c5cb1aa 100755 --- a/co-simulation/bundle/src/assembly/resources/scenarios/Town04/scenario_config.json +++ b/co-simulation/bundle/src/assembly/resources/scenarios/Town04/scenario_config.json @@ -21,7 +21,8 @@ "csNet": "10.4.0.0", "serverNet": "10.5.0.0", "tmcNet": "10.6.0.0" - } + }, + "georeference":"+proj=tmerc +lat_0=0 +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs" }, "federates": { "application": true, diff --git a/co-simulation/legal/templates/leidos-license-header-2024.txt b/co-simulation/legal/templates/leidos-license-header-2024.txt new file mode 100644 index 00000000..11d41749 --- /dev/null +++ b/co-simulation/legal/templates/leidos-license-header-2024.txt @@ -0,0 +1,13 @@ +Copyright (C) 2024 LEIDOS. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not +use this file except in compliance with the License. You may obtain a copy of +the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations under +the License. \ No newline at end of file diff --git a/co-simulation/lib/mosaic-geomath/pom.xml b/co-simulation/lib/mosaic-geomath/pom.xml index 9e330c0f..21f991b2 100644 --- a/co-simulation/lib/mosaic-geomath/pom.xml +++ b/co-simulation/lib/mosaic-geomath/pom.xml @@ -18,6 +18,14 @@ com.google.code.gson gson + + org.locationtech.proj4j + proj4j + + + org.locationtech.proj4j + proj4j-epsg + @@ -33,6 +41,19 @@ + + org.apache.maven.plugins + maven-shade-plugin + 3.4.1 + + + package + + shade + + + + diff --git a/co-simulation/lib/mosaic-geomath/src/main/java/org/eclipse/mosaic/lib/transform/Proj4Projection.java b/co-simulation/lib/mosaic-geomath/src/main/java/org/eclipse/mosaic/lib/transform/Proj4Projection.java new file mode 100644 index 00000000..aa1317fe --- /dev/null +++ b/co-simulation/lib/mosaic-geomath/src/main/java/org/eclipse/mosaic/lib/transform/Proj4Projection.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2024 LEIDOS. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + + package org.eclipse.mosaic.lib.transform; + +import static java.lang.Math.toDegrees; +import static java.lang.Math.toRadians; + + +import org.eclipse.mosaic.lib.geo.CartesianPoint; +import org.eclipse.mosaic.lib.geo.GeoPoint; +import org.eclipse.mosaic.lib.geo.MutableCartesianPoint; +import org.eclipse.mosaic.lib.geo.MutableGeoPoint; +import org.eclipse.mosaic.lib.geo.MutableUtmPoint; +import org.eclipse.mosaic.lib.geo.UtmPoint; +import org.eclipse.mosaic.lib.geo.UtmZone; +import org.eclipse.mosaic.lib.math.MathUtils; +import org.eclipse.mosaic.lib.math.Vector3d; + + +import org.locationtech.proj4j.CRSFactory; +import org.locationtech.proj4j.CoordinateReferenceSystem; +import org.locationtech.proj4j.CoordinateTransform; +import org.locationtech.proj4j.CoordinateTransformFactory; +import org.locationtech.proj4j.ProjCoordinate; + + + +public class Proj4Projection extends GeoProjection { + + // private final String georeference; + private GeoPoint geoOrigin; + UtmZone zone; + + private double x_offset; + private double y_offset; + + private String wgs84ReferenceString; + private String cartesianReferenceString; + private CoordinateReferenceSystem wgs84CRS; + private CoordinateReferenceSystem cartesianCRS; + private CoordinateTransformFactory ctFactory; + + + public Proj4Projection(GeoPoint origin, double x_offset, double y_offset, String georef){ + this.geoOrigin = origin; + this.x_offset = x_offset; + this.y_offset = y_offset; + + // The proj library being used currently doesn't support use of vertical keywords + //such as geodgrids and vunits, resulting in no vertical accuracy in the transforms calculated + //Isssue tracked under: https://github.com/locationtech/proj4j/issues/20 + //Remove unaccepted parameters from georeference + String regex = "(\\+geoidgrids=[^\\s]+\\s?)|(\\+vunits=[^\\s]+\\s?)"; + + this.cartesianReferenceString = georef.replaceAll(regex, "").trim();; + this.wgs84ReferenceString = "EPSG:4326"; + + CRSFactory crsFactory = new CRSFactory(); + this.cartesianCRS = crsFactory.createFromParameters("custom_proj", this.cartesianReferenceString); + this.wgs84CRS = crsFactory.createFromName(wgs84ReferenceString); + + this.ctFactory = new CoordinateTransformFactory(); + + setGeoCalculator(new UtmGeoCalculator(this)); + } + + @Override + public Vector3d geographicToVector(GeoPoint geographic, Vector3d result){ + getGeoCalculator().distanceBetween(geoOrigin, geographic, result); + return result; + } + + @Override + public MutableGeoPoint vectorToGeographic(Vector3d vector3d, MutableGeoPoint result) { + getGeoCalculator().pointFromDirection(geoOrigin, vector3d, result); + return result; + } + + @Override + public MutableCartesianPoint geographicToCartesian(GeoPoint geographic, MutableCartesianPoint result) { + + ProjCoordinate sourceCoord = new ProjCoordinate(geographic.getLongitude(),geographic.getLatitude()); + ProjCoordinate targetCoord = new ProjCoordinate(); + CoordinateTransform transform = this.ctFactory.createTransform(wgs84CRS, cartesianCRS); + + transform.transform(sourceCoord, targetCoord); + result.set(targetCoord.x, targetCoord.y, 0.0); + + return result; + } + + @Override + public MutableGeoPoint cartesianToGeographic(CartesianPoint cartesian, MutableGeoPoint result) { + + ProjCoordinate sourceCoord = new ProjCoordinate(cartesian.getX() - this.x_offset, cartesian.getY() - this.y_offset); + ProjCoordinate targetCoord = new ProjCoordinate(); + + CoordinateTransform transform = this.ctFactory.createTransform(cartesianCRS, wgs84CRS); + + transform.transform(sourceCoord, targetCoord); + //Transform returned as lat,lon = (targetCoord.y, targetCoord.x) + result.set(targetCoord.y, targetCoord.x, 0.0); + + return result; + } + + @Override + public Vector3d utmToVector(UtmPoint utm, Vector3d result) { + return geographicToVector(utmToGeographic(utm)); + } + + @Override + public MutableUtmPoint vectorToUtm(Vector3d vector, MutableUtmPoint result) { + return geographicToUtm(vectorToGeographic(vector), result); + } + + @Override + public MutableUtmPoint geographicToUtm(GeoPoint geoPoint, MutableUtmPoint result) { + + ProjCoordinate sourceCoord = new ProjCoordinate(geoPoint.getLongitude(),geoPoint.getLatitude()); + ProjCoordinate targetCoord = new ProjCoordinate(); + + //UTMCRS created dynamically since it depends on the zone + zone = getUTMZone(geoPoint); + String utm_proj_str = "+proj=utm +zone=" + zone.number; + CRSFactory crsFactory = new CRSFactory(); + CoordinateReferenceSystem utmCRS = crsFactory.createFromParameters("custom_proj", utm_proj_str); + + CoordinateTransform transform = this.ctFactory.createTransform(wgs84CRS, utmCRS); + + transform.transform(sourceCoord, targetCoord); + result.set(targetCoord.x, targetCoord.y, 0.0, zone); + + return result; + } + + @Override + public MutableGeoPoint utmToGeographic(UtmPoint utmPoint, MutableGeoPoint result) { + + ProjCoordinate sourceCoord = new ProjCoordinate(utmPoint.getEasting(), utmPoint.getNorthing()); + ProjCoordinate targetCoord = new ProjCoordinate(); + + //UTMCRS created dynamically since it depends on the zone + String utm_proj_str = "+proj=utm +zone=" + utmPoint.getZone().number; + CRSFactory crsFactory = new CRSFactory(); + CoordinateReferenceSystem utmCRS = crsFactory.createFromParameters("custom_proj", utm_proj_str); + + CoordinateTransform transform = this.ctFactory.createTransform(utmCRS, wgs84CRS); + + transform.transform(sourceCoord, targetCoord); + //Transform returned as lon,lat = (targetCoord.x, targetCoord.y) + result.set(targetCoord.y, targetCoord.x, 0.0); + + return result; + } + + public UtmZone getUTMZone(GeoPoint geoPoint){ + int zoneNumber; + + double longTemp = (geoPoint.getLongitude() + 180) - (int) ((geoPoint.getLongitude() + 180) / 360) * 360 - 180; + + zoneNumber = (int) ((longTemp + 180) / 6) + 1; + + if (geoPoint.getLatitude() >= 56.0 && geoPoint.getLatitude() < 64.0 && longTemp >= 3.0 && longTemp < 12.0) { + zoneNumber = 32; + } + + // Special zones for Svalbard + if (geoPoint.getLatitude() >= 72.0 && geoPoint.getLatitude() < 84.0) { + if (longTemp >= 0.0 && longTemp < 9.0) { + zoneNumber = 31; + } else if (longTemp >= 9.0 && longTemp < 21.0) { + zoneNumber = 33; + } else if (longTemp >= 21.0 && longTemp < 33.0) { + zoneNumber = 35; + } else if (longTemp >= 33.0 && longTemp < 42.0) { + zoneNumber = 37; + } + } + + return UtmZone.from(zoneNumber, UtmZone.getLetter(zoneNumber, geoPoint.getLatitude())); + } +} \ No newline at end of file diff --git a/co-simulation/lib/mosaic-geomath/src/test/java/org/eclipse/mosaic/lib/transform/Proj4ProjectionTest.java b/co-simulation/lib/mosaic-geomath/src/test/java/org/eclipse/mosaic/lib/transform/Proj4ProjectionTest.java new file mode 100644 index 00000000..bb6423f3 --- /dev/null +++ b/co-simulation/lib/mosaic-geomath/src/test/java/org/eclipse/mosaic/lib/transform/Proj4ProjectionTest.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2024 LEIDOS. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.eclipse.mosaic.lib.transform; + +import static org.junit.Assert.assertEquals; + +import org.eclipse.mosaic.lib.geo.CartesianPoint; +import org.eclipse.mosaic.lib.geo.GeoPoint; +import org.eclipse.mosaic.lib.geo.MutableGeoPoint; +import org.eclipse.mosaic.lib.geo.MutableCartesianPoint; +import org.eclipse.mosaic.lib.geo.MutableUtmPoint; +import org.eclipse.mosaic.lib.geo.UtmPoint; +import org.eclipse.mosaic.lib.geo.UtmZone; +import org.eclipse.mosaic.lib.math.Vector3d; + +import org.junit.Test; + + +public class Proj4ProjectionTest { + @Test + public void convertCartesianToGeographic() { + + String georeference = " +proj=tmerc +lat_0=0 +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +geoidgrids=egm96_15.gtx +vunits=m +no_defs"; + + MutableCartesianPoint cartesianOffset = new MutableCartesianPoint(200.00, 300.00, 0); + GeoProjection transform = new Proj4Projection(GeoPoint.latLon(0.0, 0.0), cartesianOffset.getX(), cartesianOffset.getY(), georeference); + + MutableCartesianPoint testCartesianPoint = new MutableCartesianPoint(400.00, 600.00, 0); + GeoPoint actualGeoPoint = transform.cartesianToGeographic(testCartesianPoint); + + assertEquals(actualGeoPoint.getLatitude(), 0.0027131084297879367, 0.0001d); + assertEquals(actualGeoPoint.getLongitude(), 0.0017966305699434167, 0.0001d); + assertEquals(actualGeoPoint.getAltitude(), 0.0, 0.0001d); + + } + + @Test + public void convertGeographicToCartesian(){ + String georeference = " +proj=tmerc +lat_0=0 +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +geoidgrids=egm96_15.gtx +vunits=m +no_defs"; + GeoProjection transform = new Proj4Projection(GeoPoint.latLon(0.0, 0.0), 0.0,0.0, georeference); + + GeoPoint testGeoPoint = GeoPoint.latLon(0.000,0.000); + + CartesianPoint actualCartesian = transform.geographicToCartesian(testGeoPoint); + assertEquals(actualCartesian.getX(), 0.0, 1d); + assertEquals(actualCartesian.getY(), 0.0, 1d); + assertEquals(actualCartesian.getZ(), 0.0, 1d); + + } + + + @Test + public void convertGeographictoUTM(){ + String georeference = " +proj=tmerc +lat_0=42.30059341574939 +lon_0=-83.69928318881136 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +geoidgrids=egm96_15.gtx +vunits=m +no_defs"; + + GeoProjection transform = new Proj4Projection(GeoPoint.latLon(42.30059341574939, -83.69928318881136), 0.0, 0.0, georeference); + + GeoPoint testGeoPoint = GeoPoint.latLon(38.9548994, -77.1481211); + UtmZone zone = UtmZone.from(18,'n'); + UtmPoint actualUtmPoint = new MutableUtmPoint(313863.1656767028, 4313966.065972516, 0.0, zone); + + + UtmPoint calculatedUtmPoint = transform.geographicToUtm(testGeoPoint); + assertEquals(actualUtmPoint.getEasting(), calculatedUtmPoint.getEasting(), 1d); + assertEquals(actualUtmPoint.getNorthing(), calculatedUtmPoint.getNorthing(), 1d); + assertEquals(actualUtmPoint.getAltitude(), calculatedUtmPoint.getAltitude(), 1d); + assertEquals(18, calculatedUtmPoint.getZone().number); + + //Test Special Zone Norway + GeoPoint testGeoPoint2 = GeoPoint.latLon(77.8750, 20.9752); + UtmPoint calculatedUtmPoint2 = transform.geographicToUtm(testGeoPoint2); + assertEquals(33, calculatedUtmPoint2.getZone().number); + + //Test convert back to geographic + GeoPoint calculatedGeoPoint = transform.utmToGeographic(calculatedUtmPoint); + assertEquals(calculatedGeoPoint.getLatitude(), testGeoPoint.getLatitude(), 0.0001d); + assertEquals(calculatedGeoPoint.getLongitude(),testGeoPoint.getLongitude(), 0.0001d); + } + + @Test + public void geo_utm_vector_conversion() { + String georeference = " +proj=tmerc +lat_0=0.0 +lon_0=0.0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +geoidgrids=egm96_15.gtx +vunits=m +no_defs"; + GeoProjection transform = new Proj4Projection(GeoPoint.latLon(0.0, 0.0), 0.0, 0.0, georeference); + GeoPoint origin = GeoPoint.latLon(0.0, 0.0); + + + for (int i = 0; i < 3; i++) { + double x = Math.cos(i * Math.PI / 5) * 0.1; + double y = Math.sin(i * Math.PI / 5) * 0.1; + + GeoPoint tstGeoPt = GeoPoint.latLon(origin.getLatitude() + y, origin.getLongitude() + x); + + UtmPoint geoToUtm = transform.geographicToUtm(tstGeoPt); + Vector3d geoToVec = transform.geographicToVector(tstGeoPt); + + Vector3d utmToVec = transform.utmToVector(geoToUtm); + GeoPoint utmToGeo = transform.utmToGeographic(geoToUtm); + + GeoPoint vecToGeo = transform.vectorToGeographic(geoToVec); + UtmPoint vecToUtm = transform.vectorToUtm(geoToVec); + + assertEquals(geoToVec.x, utmToVec.x, 0.01); + assertEquals(geoToVec.y, utmToVec.y, 0.01); + assertEquals(geoToVec.z, utmToVec.z, 0.01); + + assertEquals(geoToUtm.getNorthing(), vecToUtm.getNorthing(), 0.01); + assertEquals(geoToUtm.getEasting(), vecToUtm.getEasting(), 0.01); + assertEquals(geoToUtm.getAltitude(), vecToUtm.getAltitude(), 0.01); + + assertEquals(utmToGeo.getLatitude(), vecToGeo.getLatitude(), 0.01); + assertEquals(utmToGeo.getLongitude(), vecToGeo.getLongitude(), 0.01); + assertEquals(utmToGeo.getAltitude(), vecToGeo.getAltitude(), 0.01); + } + + } + +} \ No newline at end of file diff --git a/co-simulation/pom.xml b/co-simulation/pom.xml index cb71b377..5faa7301 100644 --- a/co-simulation/pom.xml +++ b/co-simulation/pom.xml @@ -94,7 +94,7 @@ jacoco reuseReports ${project.basedir}/target/site/jacoco/jacoco.xml - java + java 0.8.8 22.1-SNAPSHOT @@ -152,6 +152,16 @@ + + org.locationtech.proj4j + proj4j + 1.3.0 + + + org.locationtech.proj4j + proj4j-epsg + 1.3.0 + ch.qos.logback @@ -475,6 +485,7 @@
${parent.dir}/legal/templates/cosimulation-license-header-2021.txt
${parent.dir}/legal/templates/leidos-license-header-2022.txt
${parent.dir}/legal/templates/leidos-license-header-2023.txt
+
${parent.dir}/legal/templates/leidos-license-header-2024.txt
diff --git a/co-simulation/rti/mosaic-rti-api/src/main/java/org/eclipse/mosaic/rti/config/CProjection.java b/co-simulation/rti/mosaic-rti-api/src/main/java/org/eclipse/mosaic/rti/config/CProjection.java index fef97906..33aeb156 100644 --- a/co-simulation/rti/mosaic-rti-api/src/main/java/org/eclipse/mosaic/rti/config/CProjection.java +++ b/co-simulation/rti/mosaic-rti-api/src/main/java/org/eclipse/mosaic/rti/config/CProjection.java @@ -18,6 +18,8 @@ import org.eclipse.mosaic.lib.geo.CartesianPoint; import org.eclipse.mosaic.lib.geo.GeoPoint; +import edu.umd.cs.findbugs.annotations.Nullable; + /** * Configuration for the Projection of geographic coordinates to cartesian coordinates. */ @@ -32,5 +34,8 @@ public class CProjection { * The cartesian offset which is considered when transformation from or to geographic coordinates. */ public CartesianPoint cartesianOffset; + + @Nullable + public String georeference; } diff --git a/co-simulation/rti/mosaic-starter/src/main/java/org/eclipse/mosaic/starter/MosaicSimulation.java b/co-simulation/rti/mosaic-starter/src/main/java/org/eclipse/mosaic/starter/MosaicSimulation.java index 741b35db..f9abc9fd 100644 --- a/co-simulation/rti/mosaic-starter/src/main/java/org/eclipse/mosaic/starter/MosaicSimulation.java +++ b/co-simulation/rti/mosaic-starter/src/main/java/org/eclipse/mosaic/starter/MosaicSimulation.java @@ -15,10 +15,12 @@ package org.eclipse.mosaic.starter; +import org.eclipse.mosaic.lib.geo.MutableGeoPoint; import org.eclipse.mosaic.lib.geo.UtmPoint; import org.eclipse.mosaic.lib.geo.UtmZone; import org.eclipse.mosaic.lib.objects.addressing.IpResolver; import org.eclipse.mosaic.lib.transform.GeoProjection; +import org.eclipse.mosaic.lib.transform.Proj4Projection; import org.eclipse.mosaic.lib.transform.UtmGeoCalculator; import org.eclipse.mosaic.lib.transform.Wgs84Projection; import org.eclipse.mosaic.lib.util.NameGenerator; @@ -255,6 +257,13 @@ private GeoProjection createTransformation(CScenario scenarioConfiguration) { Validate.notNull(projectionConfig.cartesianOffset, "Invalid Wgs84UtmTransform configuration: no cartesian offset given"); + // Check if georeference is specified + if (scenarioConfiguration.simulation.georeference != null) + { + MutableGeoPoint origin = new MutableGeoPoint(projectionConfig.centerCoordinates.getLongitude(), projectionConfig.centerCoordinates.getLongitude(), projectionConfig.centerCoordinates.getAltitude()); + return new Proj4Projection(origin, projectionConfig.cartesianOffset.getX(), projectionConfig.cartesianOffset.getY(), scenarioConfiguration.simulation.georeference); + } + UtmPoint origin = UtmPoint.eastNorth( UtmZone.from(projectionConfig.centerCoordinates), -projectionConfig.cartesianOffset.getX(), diff --git a/co-simulation/rti/mosaic-starter/src/main/java/org/eclipse/mosaic/starter/config/CScenario.java b/co-simulation/rti/mosaic-starter/src/main/java/org/eclipse/mosaic/starter/config/CScenario.java index c4284598..178da377 100644 --- a/co-simulation/rti/mosaic-starter/src/main/java/org/eclipse/mosaic/starter/config/CScenario.java +++ b/co-simulation/rti/mosaic-starter/src/main/java/org/eclipse/mosaic/starter/config/CScenario.java @@ -82,6 +82,9 @@ public static class Simulation { */ @SerializedName("network") public CIpResolver networkConfig = new CIpResolver(); + + @Nullable + public String georeference; } }