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 7c618a7feb0f9d548bd48bf2b0381382edec6e4a
Author: jsorel <[email protected]>
AuthorDate: Mon Oct 19 09:00:30 2026 +0200

    refactor(NDRange): move intersection with tolerance on NDRange class
---
 .../internal/shared/DefaultNurbCurve.java          |  3 +-
 .../internal/shared/DefaultNurbSurface.java        |  3 +-
 .../sis/geometries/operation/Intersection.java     | 21 ++----------
 .../main/org/apache/sis/maths/NDRange.java         | 34 +++++++++++++++++-
 .../test/org/apache/sis/maths/NDRangeTest.java     | 40 ++++++++++++++++++++++
 5 files changed, 77 insertions(+), 24 deletions(-)

diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultNurbCurve.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultNurbCurve.java
index b63279026e..073967c85e 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultNurbCurve.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultNurbCurve.java
@@ -19,7 +19,6 @@ package org.apache.sis.geometries.internal.shared;
 import java.util.Arrays;
 import java.util.List;
 import javax.measure.Quantity;
-import org.opengis.geometry.Envelope;
 import org.apache.sis.geometries.DataPoints;
 import org.apache.sis.geometries.DataPointsType;
 import org.apache.sis.geometries.Geometries;
@@ -313,7 +312,7 @@ public non-sealed class DefaultNurbCurve extends 
AbstractGeometry implements Nur
      * @return the bounding box of this curve
      */
     @Override
-    public Envelope getEnvelope() {
+    public NDRange getEnvelope() {
         final Vector<?> start = Vectors.create(getCoordinateReferenceSystem(), 
DataType.DOUBLE).set(controlPoints[0]);
         final NDRange bbox = new NDRange(start, start);
         for (final Vector<?> p : controlPoints) {
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultNurbSurface.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultNurbSurface.java
index 6f8cc6d840..bb0d07ea84 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultNurbSurface.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/internal/shared/DefaultNurbSurface.java
@@ -21,7 +21,6 @@ import java.util.Arrays;
 import java.util.List;
 import javax.measure.Quantity;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.opengis.geometry.Envelope;
 import org.apache.sis.geometries.Curve;
 import org.apache.sis.geometries.DataPoints;
 import org.apache.sis.geometries.Geometry;
@@ -316,7 +315,7 @@ public non-sealed class DefaultNurbSurface extends 
AbstractGeometry implements N
      *
      * @return the bounding box of this surface
      */
-    public Envelope getEnvelope() {
+    public NDRange getEnvelope() {
         final Vector<?> start = Vectors.create(getCoordinateReferenceSystem(), 
DataType.DOUBLE).set(controlPoints[0][0]);
         final NDRange bbox = new NDRange(start, start);
         for (final Vector<?>[] row : controlPoints) {
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/Intersection.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/Intersection.java
index 652d15c6af..db21ba0719 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/Intersection.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/operation/Intersection.java
@@ -22,7 +22,6 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Optional;
 import java.util.stream.Stream;
-import org.opengis.geometry.Envelope;
 import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.geometries.Geometries;
 import org.apache.sis.geometries.GeometryFactory;
@@ -64,22 +63,6 @@ public final class Intersection {
         return a.copy().scale(s);
     }
 
-    /**
-     * Tests whether two bounding boxes overlap, within the given tolerance.
-     * Boxes separated by less than the tolerance are still considered as 
overlapping,
-     * which {@link Envelope#intersects Envelope.intersects(…)} cannot express.
-     */
-    private static boolean boxesOverlap(final Envelope box1, final Envelope 
box2, final double tol) {
-        for (int i = 0, n = box1.getDimension(); i < n; i++) {
-            if (box1.getMaximum(i) + tol < box2.getMinimum(i)
-             || box2.getMaximum(i) + tol < box1.getMinimum(i)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-
     /**
      * Inherit attributes from TIN.
      * The POSITION attribute is ignored.
@@ -248,7 +231,7 @@ public final class Intersection {
         }
 
         private static void intersectRecursive(final DefaultNurbCurve c1, 
final DefaultNurbCurve c2, final double tol, final List<CurveIntersectionPoint> 
results) {
-            if (!boxesOverlap(c1.getEnvelope(), c2.getEnvelope(), tol)) {
+            if (!c1.getEnvelope().intersects(c2.getEnvelope(), tol)) {
                 return;
             }
 
@@ -370,7 +353,7 @@ public final class Intersection {
 
         private static void intersectRecursive(final DefaultNurbSurface s1, 
final DefaultNurbSurface s2, final double tol,
                 final List<SurfaceIntersectionPoint> results, final int depth) 
{
-            if (!boxesOverlap(s1.getEnvelope(), s2.getEnvelope(), tol)) {
+            if (!s1.getEnvelope().intersects(s2.getEnvelope(), tol)) {
                 return;
             }
 
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/maths/NDRange.java 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/maths/NDRange.java
index 9293d42f9a..4b078c5874 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/maths/NDRange.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/maths/NDRange.java
@@ -378,12 +378,44 @@ public final class NDRange implements Envelope {
         return value >= lower.get(dimension) && value <= upper.get(dimension);
     }
 
+    /**
+     * Returns whether the given envelope intersects this range, within the 
given tolerance.
+     * Envelopes separated by no more than the tolerance in every dimension 
are considered as intersecting,
+     * so that envelopes touching on their border intersect even with a 
tolerance of zero.
+     * A NaN bound in this range or in the envelope makes them disjoint, the 
range being empty in that dimension.
+     * The envelope is assumed in the reference system of this range, without 
verification.
+     *
+     * @param  envelope   the envelope to test.
+     * @param  tolerance  the largest distance between the envelope and this 
range for considering them as intersecting.
+     * @return whether the envelope intersects this range or is separated from 
it by no more than the tolerance.
+     * @throws MismatchedDimensionException if the envelope does not have the 
dimension of this range.
+     * @throws IllegalArgumentException if the tolerance is negative or NaN.
+     */
+    public boolean intersects(final Envelope envelope, final double tolerance) 
throws MismatchedDimensionException {
+        ensureDimensionMatches("envelope", envelope.getDimension());
+        ArgumentChecks.ensurePositive("tolerance", tolerance);
+        for (int i = 0; i < lower.getDimension(); i++) {
+            // Written for being false if a bound is NaN.
+            if (!(envelope.getMinimum(i) <= upper.get(i) + tolerance && 
lower.get(i) <= envelope.getMaximum(i) + tolerance)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     /**
      * Verifies that a position has the dimension of this range.
      */
     private void ensureDimensionMatches(final int dimension) {
+        ensureDimensionMatches("position", dimension);
+    }
+
+    /**
+     * Verifies that an object of the given name, for example a position or an 
envelope, has the dimension of this range.
+     */
+    private void ensureDimensionMatches(final String name, final int 
dimension) {
         if (dimension != lower.getDimension()) {
-            throw new MismatchedDimensionException("The position has " + 
dimension
+            throw new MismatchedDimensionException("The " + name + " has " + 
dimension
                     + " dimensions but the range has " + lower.getDimension() 
+ '.');
         }
     }
diff --git 
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/maths/NDRangeTest.java
 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/maths/NDRangeTest.java
index 156abbd09b..156e6cc146 100644
--- 
a/incubator/src/org.apache.sis.geometry/test/org/apache/sis/maths/NDRangeTest.java
+++ 
b/incubator/src/org.apache.sis.geometry/test/org/apache/sis/maths/NDRangeTest.java
@@ -183,6 +183,46 @@ public class NDRangeTest {
         assertThrows(MismatchedDimensionException.class, () -> 
range.contains(Vectors.createDouble(2)));
     }
 
+    /**
+     * Ranges intersect if they overlap, touch, or are separated by no more 
than the tolerance in every dimension.
+     */
+    @Test
+    public void testIntersects() {
+        final NDRange range = range();
+        final NDRange overlapping = new NDRange(vector(3, 5, 3), vector(7, 8, 
3));
+        assertTrue(range.intersects(overlapping, 0));
+        assertTrue(overlapping.intersects(range, 0));
+        assertTrue(range.intersects(range(), 0), "A range intersects itself.");
+        assertTrue(range.intersects(new NDRange(vector(2, 3, 3), vector(3, 4, 
3)), 0), "A contained range intersects.");
+        assertTrue(range.intersects(new NDRange(vector(4, 6, 3), vector(5, 7, 
3)), 0), "Touching ranges intersect.");
+
+        // Separated by 0.5 along the first axis only.
+        final NDRange near = new NDRange(vector(4.5, 2, 3), vector(5, 6, 3));
+        assertFalse(range.intersects(near, 0));
+        assertFalse(range.intersects(near, 0.25));
+        assertTrue (range.intersects(near, 0.5));
+        assertTrue (near.intersects(range, 0.5));
+
+        // Overlapping along two axes is not enough.
+        assertFalse(range.intersects(new NDRange(vector(1, 2, 4), vector(4, 6, 
5)), 0));
+
+        // Any envelope implementation is accepted.
+        final GeneralEnvelope envelope = new GeneralEnvelope(CRS);
+        envelope.setEnvelope(0, 0, 0, 1, 2, 3);
+        assertTrue (range.intersects(envelope, 0));
+        envelope.setEnvelope(0, 0, 0, 0.9, 2, 3);
+        assertFalse(range.intersects(envelope, 0));
+
+        // A NaN bound makes the range empty.
+        final NDRange empty = new NDRange(vector(Double.NaN, 2, 3), 
vector(Double.NaN, 6, 3));
+        assertFalse(range.intersects(empty, 1));
+        assertFalse(empty.intersects(range, 1));
+
+        assertThrows(MismatchedDimensionException.class, () -> 
range.intersects(new NDRange(2), 0));
+        assertThrows(IllegalArgumentException.class, () -> 
range.intersects(overlapping, -1));
+        assertThrows(IllegalArgumentException.class, () -> 
range.intersects(overlapping, Double.NaN));
+    }
+
     /**
      * The constructors without bounds create a range of the given data type, 
{@code DOUBLE} by default,
      * with all bounds at zero.

Reply via email to