This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
new 7bd94f14bb feat(Vector): add method to convert between lat/lon and
unit vector
7bd94f14bb is described below
commit 7bd94f14bb869d23df224c8b56794d7eb130f214
Author: jsorel <[email protected]>
AuthorDate: Wed Aug 5 14:25:39 2026 +0200
feat(Vector): add method to convert between lat/lon and unit vector
---
.../org/apache/sis/geometries/math/Vector3D.java | 29 ++++++
.../apache/sis/geometries/math/Vector3DTest.java | 108 +++++++++++++++++++++
2 files changed, 137 insertions(+)
diff --git
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Vector3D.java
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Vector3D.java
index bef1424d78..4e15b1f316 100644
---
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Vector3D.java
+++
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/math/Vector3D.java
@@ -94,6 +94,35 @@ public abstract class Vector3D<T extends Vector3D<T>>
extends AbstractTuple<T> i
return (T) this;
}
+ /**
+ * Convert latitude/longitude radian angles to a unit vector
+ * and store the values in this vector.
+ *
+ * @param latRad in range [-PI/2 .. +PI/2]
+ * @param lonRad in range [-PI .. +PI]
+ * @return this vector
+ */
+ public T setFromLatLon(double latRad, double lonRad) {
+ double cosLat = Math.cos(latRad);
+ set(0, cosLat * Math.cos(lonRad));
+ set(1, cosLat * Math.sin(lonRad));
+ set(2, Math.sin(latRad));
+ return (T) this;
+ }
+
+ /**
+ * Convert this vector to latitude/longitude radian angles.
+ * This vector is expected to be a unit vector.
+ *
+ * @return latitude/longitude in radians
+ */
+ public double[] toLatLon() {
+ final double latRad = Math.asin(Maths.clamp(get(2), -1, 1));
+ final double lonRad = Math.atan2(get(1), get(0));
+ return new double[]{latRad, lonRad};
+ }
+
+
@Override
public final int getDimension() {
return 3;
diff --git
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/math/Vector3DTest.java
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/math/Vector3DTest.java
new file mode 100644
index 0000000000..710858392b
--- /dev/null
+++
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/math/Vector3DTest.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.sis.geometries.math;
+
+// Test dependencies
+import static org.junit.jupiter.api.Assertions.*;
+import org.junit.jupiter.api.Test;
+
+
+/**
+ * Tests for {@link Vector3D#setFromLatLon(double, double) } and
+ * {@link Vector3D#toLatLon() }.
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public class Vector3DTest {
+
+ private static final double TOLERANCE = 1e-12;
+ private static final double HALF_SQRT2 = Math.sqrt(2) / 2;
+
+ /**
+ * setFromLatLon test on remarkable points : equator/meridian origin,
+ * poles, and a quadrant point.
+ */
+ @Test
+ public void setFromLatLonTest() {
+ Vector3D.Double v = new Vector3D.Double();
+
+ v.setFromLatLon(0, 0);
+ assertArrayEquals(new double[]{1, 0, 0}, v.toArrayDouble(), TOLERANCE);
+
+ v.setFromLatLon(0, Math.PI / 2);
+ assertArrayEquals(new double[]{0, 1, 0}, v.toArrayDouble(), TOLERANCE);
+
+ v.setFromLatLon(0, Math.PI);
+ assertArrayEquals(new double[]{-1, 0, 0}, v.toArrayDouble(),
TOLERANCE);
+
+ v.setFromLatLon(Math.PI / 2, 0);
+ assertArrayEquals(new double[]{0, 0, 1}, v.toArrayDouble(), TOLERANCE);
+
+ v.setFromLatLon(-Math.PI / 2, 0);
+ assertArrayEquals(new double[]{0, 0, -1}, v.toArrayDouble(),
TOLERANCE);
+
+ v.setFromLatLon(Math.PI / 4, Math.PI / 4);
+ assertArrayEquals(new double[]{0.5, 0.5, HALF_SQRT2},
v.toArrayDouble(), TOLERANCE);
+ }
+
+ /**
+ * toLatLon test on remarkable unit vectors : axis directions and a
+ * quadrant point.
+ */
+ @Test
+ public void toLatLonTest() {
+ assertArrayEquals(new double[]{0, 0},
+ new Vector3D.Double(1, 0, 0).toLatLon(), TOLERANCE);
+
+ assertArrayEquals(new double[]{0, Math.PI / 2},
+ new Vector3D.Double(0, 1, 0).toLatLon(), TOLERANCE);
+
+ assertArrayEquals(new double[]{0, Math.PI},
+ new Vector3D.Double(-1, 0, 0).toLatLon(), TOLERANCE);
+
+ assertArrayEquals(new double[]{Math.PI / 2, 0},
+ new Vector3D.Double(0, 0, 1).toLatLon(), TOLERANCE);
+
+ assertArrayEquals(new double[]{-Math.PI / 2, 0},
+ new Vector3D.Double(0, 0, -1).toLatLon(), TOLERANCE);
+
+ assertArrayEquals(new double[]{Math.PI / 4, Math.PI / 4},
+ new Vector3D.Double(0.5, 0.5, HALF_SQRT2).toLatLon(),
TOLERANCE);
+ }
+
+ /**
+ * Round trip test : converting latitude/longitude to a vector and back
+ * must return the original angles, for a set of angles away from the
+ * poles where longitude stays well defined.
+ */
+ @Test
+ public void latLonRoundTripTest() {
+ final double[] latitudes = {-Math.PI / 3, -Math.PI / 6, 0, Math.PI /
6, Math.PI / 3};
+ final double[] longitudes = {-3 * Math.PI / 4, -Math.PI / 3, 0,
Math.PI / 3, 3 * Math.PI / 4};
+
+ final Vector3D.Double v = new Vector3D.Double();
+ for (double lat : latitudes) {
+ for (double lon : longitudes) {
+ v.setFromLatLon(lat, lon);
+ final double[] latLon = v.toLatLon();
+ assertEquals(lat, latLon[0], TOLERANCE);
+ assertEquals(lon, latLon[1], TOLERANCE);
+ }
+ }
+ }
+
+}