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