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

commit 7b6223be12fabde62b3a807ee61484f0a9faf2a5
Author: jsorel <[email protected]>
AuthorDate: Wed Oct 14 16:58:39 2026 +0200

    feat(Geometry): implement toPrimitive for PolyhedralSurface
---
 .../geometries/operation/GeometryProcessor.java    |  4 +++
 .../sis/geometries/operation/ToPrimitive.java      | 35 +++++++++++++++++++
 .../sis/geometries/operation/ToPrimitiveTest.java  | 39 ++++++++++++++++++++++
 3 files changed, 78 insertions(+)

diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/GeometryProcessor.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/GeometryProcessor.java
index 2d4c54560b..6a1d27d284 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/GeometryProcessor.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/GeometryProcessor.java
@@ -38,6 +38,7 @@ import org.apache.sis.geometries.mesh.MeshPrimitive;
 import org.apache.sis.geometries.mesh.MeshPrimitiveVisitor;
 import org.apache.sis.geometries.mesh.MultiMeshPrimitive;
 import org.apache.sis.geometries.surface.Polygon;
+import org.apache.sis.geometries.surface.PolyhedralSurface;
 import org.apache.sis.geometries.surface.Triangle;
 import org.apache.sis.maths.Array;
 import org.apache.sis.maths.DataType;
@@ -482,6 +483,9 @@ public final class GeometryProcessor {
             return ToPrimitive.toPrimitive(cdt);
         } else if (geom instanceof Polygon cdt) {
             return ToPrimitive.toPrimitive(cdt);
+        } else if (geom instanceof PolyhedralSurface<?> cdt) {
+            //also handles TIN, which is a polyhedral surface of triangles.
+            return ToPrimitive.toPrimitive(cdt);
         } else if (geom instanceof GeometryCollection<?> cdt) {
             final GeometryType elementType = cdt.getElementType();
             switch (elementType) {
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/ToPrimitive.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/ToPrimitive.java
index 8ac878939c..0d7e8877e2 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/ToPrimitive.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/ToPrimitive.java
@@ -16,9 +16,12 @@
  */
 package org.apache.sis.geometries.operation;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.geometries.DataPoints;
 import org.apache.sis.geometries.Geometry;
 import org.apache.sis.geometries.GeometryCollection;
@@ -30,8 +33,10 @@ import org.apache.sis.geometries.mesh.MeshPrimitive;
 import org.apache.sis.geometries.mesh.MultiMeshPrimitive;
 import org.apache.sis.geometries.operation.triangulate.EarClipping;
 import org.apache.sis.geometries.surface.Polygon;
+import org.apache.sis.geometries.surface.PolyhedralSurface;
 import org.apache.sis.maths.Array;
 import org.apache.sis.maths.NDArrays;
+import org.apache.sis.maths.Vector;
 
 
 /**
@@ -84,6 +89,36 @@ public final class ToPrimitive {
         return new EarClipping().toMesh(geometry);
     }
 
+    /**
+     * Transform PolyhedralSurface, including TIN, to Primitive.
+     * Each patch is triangulated and all triangles are merged in a single 
non-indexed mesh.
+     */
+    public static MeshPrimitive.Triangles toPrimitive(PolyhedralSurface<?> 
geometry) throws OperationException {
+        final CoordinateReferenceSystem crs = 
geometry.getCoordinateReferenceSystem();
+        final int dimension = crs.getCoordinateSystem().getDimension();
+        final EarClipping triangulator = new EarClipping();
+        final List<double[]> triangles = new ArrayList<>();
+        for (int i = 0, n = geometry.getNumPatches(); i < n; i++) {
+            //the returned list is reused by the next call, so the coordinates 
are copied now.
+            for (final Vector<?>[] triangle : 
triangulator.triangulate(geometry.getPatchN(i))) {
+                final double[] coordinates = new double[3 * dimension];
+                for (int v = 0; v < 3; v++) {
+                    for (int d = 0; d < dimension; d++) {
+                        coordinates[v * dimension + d] = triangle[v].get(d);
+                    }
+                }
+                triangles.add(coordinates);
+            }
+        }
+        final double[] positions = new double[triangles.size() * 3 * 
dimension];
+        for (int i = 0; i < triangles.size(); i++) {
+            System.arraycopy(triangles.get(i), 0, positions, i * 3 * 
dimension, 3 * dimension);
+        }
+        final MeshPrimitive.Triangles primitive = new 
MeshPrimitive.Triangles();
+        primitive.setPositions(NDArrays.of(crs, positions));
+        return primitive;
+    }
+
     /**
      * Transform MultiPoint to Primitive.
      */
diff --git 
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/ToPrimitiveTest.java
 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/ToPrimitiveTest.java
index 5c3c8a360f..bb0e23be50 100644
--- 
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/ToPrimitiveTest.java
+++ 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/geometries/operation/ToPrimitiveTest.java
@@ -175,6 +175,45 @@ public class ToPrimitiveTest {
         }
     }
 
+    /**
+     * Tests the conversion of a TIN, which shall give a single mesh of all 
its triangles.
+     */
+    @Test
+    public void testTIN() {
+        final CoordinateReferenceSystem crs = CommonCRS.WGS84.geographic3D();
+        final GeometryFactory f = GeometryFactory.DEFAULT;
+        final org.apache.sis.geometries.surface.TIN tin = f.createTIN(crs, new 
org.apache.sis.geometries.surface.Triangle[] {
+            f.createTriangle(ring3D(crs, 0,0,0, 2,0,0, 0,2,1, 0,0,0)),
+            f.createTriangle(ring3D(crs, 2,0,0, 2,2,1, 0,2,1, 2,0,0))});
+        final Geometry result = new GeometryProcessor().toPrimitive(tin);
+        assertTrue(result instanceof MeshPrimitive.Triangles);
+        final MeshPrimitive primitive = (MeshPrimitive) result;
+        assertEquals(3, primitive.getPositions().getDimension());
+        assertEquals(6, primitive.getPositions().getLength(), "Expected 2 
triangles.");
+        assertEquals(2 * Math.sqrt(5), trianglesArea3D(primitive), 1E-9);
+    }
+
+    /**
+     * Tests the conversion of a polyhedral surface made of the five faces of 
an open box,
+     * including vertical ones. All faces shall be triangulated in a single 
mesh.
+     */
+    @Test
+    public void testPolyhedralSurface() {
+        final CoordinateReferenceSystem crs = CommonCRS.WGS84.geographic3D();
+        final GeometryFactory f = GeometryFactory.DEFAULT;
+        final Polygon[] faces = {
+            f.createPolygon(ring3D(crs, 0,0,0, 0,0,2, 0,2,2, 0,2,0, 0,0,0), 
null),
+            f.createPolygon(ring3D(crs, 2,0,0, 2,2,0, 2,2,2, 2,0,2, 2,0,0), 
null),
+            f.createPolygon(ring3D(crs, 0,0,0, 2,0,0, 2,0,2, 0,0,2, 0,0,0), 
null),
+            f.createPolygon(ring3D(crs, 0,2,0, 0,2,2, 2,2,2, 2,2,0, 0,2,0), 
null),
+            f.createPolygon(ring3D(crs, 0,0,0, 0,2,0, 2,2,0, 2,0,0, 0,0,0), 
null)};
+        final Geometry result = new 
GeometryProcessor().toPrimitive(f.createPolyhedralSurface(crs, faces));
+        assertTrue(result instanceof MeshPrimitive.Triangles);
+        final MeshPrimitive primitive = (MeshPrimitive) result;
+        assertEquals(30, primitive.getPositions().getLength(), "Expected 2 
triangles per face.");
+        assertEquals(20, trianglesArea3D(primitive), 1E-9);
+    }
+
     private static LinearRing ring3D(final CoordinateReferenceSystem crs, 
final double... coordinates) {
         return GeometryFactory.DEFAULT.createLinearRing(
                 GeometryFactory.DEFAULT.createDataPoints(NDArrays.of(crs, 
coordinates)));

Reply via email to