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;
}
}