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 c4cb4db1cade5191a99942ed8c0fc07be94f9ed0
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Wed Jun 10 20:22:34 2026 +0200

    Move the code that search for Z and M values in a separated classes.
    It make easier to manage the ranges, the flags about Z and M presence
    and the dimensions all together.
---
 .../sis/geometry/wrapper/jts/GeometryWalker.java   | 184 +++++++++++++++++++++
 .../apache/sis/geometry/wrapper/jts/Wrapper.java   | 112 +++----------
 2 files changed, 209 insertions(+), 87 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/jts/GeometryWalker.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/jts/GeometryWalker.java
new file mode 100644
index 0000000000..96033f0ad8
--- /dev/null
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/jts/GeometryWalker.java
@@ -0,0 +1,184 @@
+/*
+ * 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.geometry.wrapper.jts;
+
+import org.locationtech.jts.geom.CoordinateSequence;
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.GeometryCollection;
+import org.locationtech.jts.geom.LineString;
+import org.locationtech.jts.geom.Point;
+import org.locationtech.jts.geom.Polygon;
+import org.apache.sis.geometry.GeneralEnvelope;
+import org.apache.sis.util.resources.Errors;
+
+
+/**
+ * Walks through the component of a geometry for finding the number of 
dimensions
+ * and the range of <abbr>z</abbr> and <abbr>m</abbr> values.
+ *
+ * @author  Johann Sorel (Geomatys)
+ * @author  Martin Desruisseaux (Geomatys)
+ */
+final class GeometryWalker {
+    /**
+     * Whether to search for ranges of <var>z</var> and <var>M</var> values.
+     * If {@code false}, this method will check only for the number of 
dimensions.
+     */
+    private boolean wantZM;
+
+    /**
+     * Whether the coordinate sequence declares to have <var>z</var> or 
<var>M</var> values.
+     */
+    private boolean hasZ, hasM;
+
+    /**
+     * Range of <var>z</var> values.
+     */
+    private double minZ, maxZ;
+
+    /**
+     * Range of <var>M</var> values.
+     */
+    private double minM, maxM;
+
+    /**
+     * The maximal number of <em>vertex</em> dimensions found. Note that this 
is different than the
+     * {@linkplain Geometry#getDimension() geometry topological dimension}, 
which can be 0, 1 or 2.
+     * The <abbr>JTS</abbr> vertex dimension can be 2, 3 or 4.
+     */
+    private int dimension;
+
+    /**
+     * Creates a new walker.
+     * By default, the walker does <em>not</em> search for ranges of 
<var>z</var> and <var>M</var> values.
+     * If this search is desired, the {@link #wantZM()} method must be invoked 
after construction.
+     */
+    GeometryWalker() {
+    }
+
+    /**
+     * Requests the search of ranges of <var>z</var> and <var>M</var> values.
+     */
+    final void wantZM() {
+        wantZM = true;
+        minZ = minM = Double.POSITIVE_INFINITY;
+        maxZ = maxM = Double.NEGATIVE_INFINITY;
+    }
+
+    /**
+     * Returns the number of dimensions found by the calls to {@link 
#scan(Geometry)}.
+     */
+    final int dimension() {
+        if (hasZ & hasM) return Math.max(4, dimension);
+        if (hasZ | hasM) return Math.max(3, dimension);
+        return dimension;
+    }
+
+    /**
+     * Sets the <var>z</var> and <var>M</var> coordinates in the given 
envelope.
+     *
+     * @param  env  the envelope where to set the coordinates.
+     */
+    final void setZM(final GeneralEnvelope env) {
+        if (wantZM) {
+            final int limit = env.getDimension();
+            int target = Factory.BIDIMENSIONAL;
+            if (target < limit) {
+                if (minZ <= maxZ) {
+                    env.setRange(target, minZ, maxZ);
+                }
+                if (minM <= maxM) {
+                    if (hasZ) {
+                        // Increment even if the range of Z was undefined.
+                        target = Factory.TRIDIMENSIONAL;
+                        if (target >= limit) return;
+                    }
+                    env.setRange(target, minM, maxM);
+                }
+            }
+        }
+    }
+
+    /**
+     * Scans the given geometry for its number of dimensions and range of 
<var>z</var> and <var>M</var> values.
+     * This method may invoke itself recursively for walking through 
components of the given geometry.
+     *
+     * @param  geometry  the geometry to inspect.
+     * @throws IllegalArgumentException if the type of the given geometry is 
not recognized.
+     */
+    final void scan(final Geometry geometry) {
+        if (geometry instanceof Point) {
+            // Most efficient method (no allocation) in JTS 1.18.
+            addDimensionAndRanges(((Point) geometry).getCoordinateSequence());
+        } else if (geometry instanceof LineString) {
+            // Most efficient method (no allocation) in JTS 1.18.
+            addDimensionAndRanges(((LineString) 
geometry).getCoordinateSequence());
+        } else if (geometry instanceof Polygon) {
+            final var polygon = (Polygon) geometry;
+            scan(polygon.getExteriorRing());
+            if (wantZM) {
+                final int n = polygon.getNumInteriorRing();
+                for (int i=0; i<n; i++) {
+                    scan(polygon.getInteriorRingN(i));
+                }
+            }
+        } else if (geometry instanceof GeometryCollection) {
+            final var gc = (GeometryCollection) geometry;
+            final int n = gc.getNumGeometries();
+            if (n > 0) {
+                for (int i=0; i<n; i++) {
+                    scan(gc.getGeometryN(i));
+                }
+            } else if (dimension == 0) {
+                // Undefined coordinates, JTS assumes 3 for empty geometries.
+                dimension = Factory.TRIDIMENSIONAL;
+            }
+        } else {
+            throw new 
IllegalArgumentException(Errors.format(Errors.Keys.UnknownType_1, 
geometry.getGeometryType()));
+        }
+    }
+
+    /**
+     * Updates the number of dimensions and the range of <var>z</var> and 
<var>M</var> values
+     * from the given coordinate sequence.
+     *
+     * @param  cs  the coordinate sequence from which to get the number of 
dimensions
+     *             and the range of (<var>z</var>, <var>M</var>) values.
+     */
+    private void addDimensionAndRanges(final CoordinateSequence cs) {
+        dimension = Math.max(dimension, cs.getDimension());
+        if (wantZM) {
+            final int n = cs.size();
+            if (cs.hasZ()) {
+                hasZ = true;
+                for (int i=0; i<n; i++) {
+                    double z = cs.getZ(i);
+                    if (z < minZ) minZ = z;
+                    if (z > maxZ) maxZ = z;
+                }
+            }
+            if (cs.hasM()) {
+                hasM = true;
+                for (int i=0; i<n; i++) {
+                    double m = cs.getM(i);
+                    if (m < minM) minM = m;
+                    if (m > maxM) maxM = m;
+                }
+            }
+        }
+    }
+}
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/jts/Wrapper.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/jts/Wrapper.java
index cd670f0c8f..4f77c2e54d 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/jts/Wrapper.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/jts/Wrapper.java
@@ -26,7 +26,6 @@ import java.util.function.BiFunction;
 import java.util.function.BiPredicate;
 import java.util.function.IntFunction;
 import org.locationtech.jts.geom.Coordinate;
-import org.locationtech.jts.geom.CoordinateSequence;
 import org.locationtech.jts.geom.Envelope;
 import org.locationtech.jts.geom.Geometry;
 import org.locationtech.jts.geom.GeometryCollection;
@@ -68,7 +67,7 @@ import org.opengis.filter.DistanceOperatorName;
 
 
 /**
- * The wrapper of Java Topology Suite (JTS) geometries.
+ * The wrapper of Java Topology Suite (<abbr>JTS</abbr>) geometries.
  *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
@@ -147,72 +146,13 @@ final class Wrapper extends GeometryWrapper {
      */
     @Override
     public int getCoordinateDimension() {
-        return getCoordinatesDimension(geometry, null);
-    }
-
-    /**
-     * Gets the number of dimensions of geometry vertex (sequence of 
coordinate tuples), which can be 2 or 3.
-     * Note that this is different than the {@linkplain 
Geometry#getDimension() geometry topological dimension},
-     * which can be 0, 1 or 2.
-     *
-     * @param geometry  the geometry for which to get <em>vertex</em> (not 
topological) dimension.
-     * @param bounds if defined, compute bounds, array must be of size 8 for 
[minX,minY,minZ,minM,maxX,maxY,maxZ,maxM]
-     * @return vertex dimension of the given geometry.
-     * @throws IllegalArgumentException if the type of the given geometry is 
not recognized.
-     */
-    private static int getCoordinatesDimension(final Geometry geometry, 
Double[] bounds) {
-        int dim = 0;
-        if (geometry instanceof Point) {
-            // Most efficient method (no allocation) in JTS 1.18.
-            dim = getCoordinatesDimension(dim, ((Point) 
geometry).getCoordinateSequence(), bounds);
-        } else if (geometry instanceof LineString) {
-            // Most efficient method (no allocation) in JTS 1.18.
-            dim = getCoordinatesDimension(dim, ((LineString) 
geometry).getCoordinateSequence(), bounds);
-        } else if (geometry instanceof Polygon) {
-            final Polygon polygon = (Polygon) geometry;
-            dim = getCoordinatesDimension(dim, ((LineString) 
polygon.getExteriorRing()).getCoordinateSequence(), bounds);
-            for (int i = 0, n = polygon.getNumInteriorRing(); i < n; i++) {
-                dim = getCoordinatesDimension(dim, ((LineString) 
polygon.getInteriorRingN(i)).getCoordinateSequence(), bounds);
-            }
-
-        } else if (geometry instanceof GeometryCollection) {
-            final GeometryCollection gc = (GeometryCollection) geometry;
-            final int n = gc.getNumGeometries();
-            if (n == 0) {
-                dim = Factory.TRIDIMENSIONAL;// Undefined coordinates, JTS 
assumes 3 for empty geometries.
-            }
-            for (int i = 0; i < n; i++) {
-                dim = Math.max(dim, 
getCoordinatesDimension(gc.getGeometryN(i), bounds));
-            }
-        } else {
-            throw new 
IllegalArgumentException(Errors.format(Errors.Keys.UnknownType_1, 
geometry.getGeometryType()));
+        int dimension = CRS.getDimensionOrZero(crs);
+        if (dimension == 0) {
+            final var walker = new GeometryWalker();
+            walker.scan(geometry);
+            dimension = walker.dimension();
         }
-        return dim;
-    }
-
-    /**
-     * Get dimension and bounds of coordinate sequence.
-     *
-     * @param dim previously computed dimension
-     * @param cs the coordinate sequence for which to get <em>vertex</em> (not 
topological) dimension and bounds
-     * @param bounds if defined, compute bounds, array must be of size 8 for 
[minX,minY,minZ,minM,maxX,maxY,maxZ,maxM]
-     * @return coordinate sequence dimension or previous dimension, returning 
the largest one.
-     */
-    private static int getCoordinatesDimension(int dim, CoordinateSequence cs, 
Double[] bounds) {
-        final int dimension = cs.getDimension();
-        if (bounds != null) {
-            for (int i = 0, d = 0, n = cs.size(); i < n; d++) {
-                if (d == dimension) {d = -1; i++; continue;}
-                final double val = cs.getOrdinate(i, d);
-                //check min
-                if (bounds[d] == null) bounds[d] = val;
-                else if (!bounds[d].isNaN()) bounds[d] = Double.min(bounds[d], 
val);
-                //check max
-                if (bounds[d+4] == null) bounds[d+4] = val;
-                else if (!bounds[d+4].isNaN()) bounds[d+4] = 
Double.max(bounds[d+4], val);
-            }
-        }
-        return Math.max(dim, dimension);
+        return dimension;
     }
 
     /**
@@ -223,29 +163,27 @@ final class Wrapper extends GeometryWrapper {
      */
     @Override
     public GeneralEnvelope getEnvelope() {
-        if (crs != null && crs.getCoordinateSystem().getDimension() == 2) {
-            //we can use JTS envelope internal
-            final Envelope bounds = geometry.getEnvelopeInternal();
-            final var env = new GeneralEnvelope(crs);
-            env.setToNaN();
-            if (!bounds.isNull()) {
-                env.setRange(0, bounds.getMinX(), bounds.getMaxX());
-                env.setRange(1, bounds.getMinY(), bounds.getMaxY());
+        final var walker = new GeometryWalker();
+        final GeneralEnvelope env;
+        if (crs != null) {
+            env = new GeneralEnvelope(crs);
+            if (env.getDimension() >= Factory.TRIDIMENSIONAL) {
+                walker.wantZM();
             }
-            return env;
+            walker.scan(geometry);
         } else {
-            //geometry has unknown or more then 2 dimensions
-            final Double[] bounds = new Double[8];
-            final int dim = getCoordinatesDimension(geometry, bounds);
-            final var env = (crs != null) ? new GeneralEnvelope(crs) : new 
GeneralEnvelope(dim);
-            env.setToNaN();
-            for (int i = 0, n = Math.min(dim, env.getDimension()); i < n; i++) 
{
-                if (bounds[i] != null){
-                    env.setRange(i, bounds[i], bounds[i+4]);
-                }
-            }
-            return env;
+            walker.wantZM();
+            walker.scan(geometry);
+            env = new GeneralEnvelope(walker.dimension());
+        }
+        env.setToNaN();
+        final Envelope bounds = geometry.getEnvelopeInternal();
+        if (!bounds.isNull()) {
+            env.setRange(0, bounds.getMinX(), bounds.getMaxX());
+            env.setRange(1, bounds.getMinY(), bounds.getMaxY());
         }
+        walker.setZM(env);
+        return env;
     }
 
     /**

Reply via email to