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.
