Skip to content

Commit 949675f

Browse files
adacovskclaude
andcommitted
fix(geospatial): add epsilon tolerance for boundary points
Add configurable epsilon parameter to GeospatialIndex to handle points on or near cell boundaries. The epsilon is used for: - Expanding bounding box tests (bbox ± epsilon) - ConvexHull hyperplane distance tests (distance <= epsilon) Default epsilon=1e-6 works well for grids with coordinates up to ~10,000 units. This fixes errors where valid points near boundaries were incorrectly rejected as outside the model area, such as: - point (0.1, 8999.9, -16.67) in DISU grids - point (0.1, 10499.9, 310.0) in larger grids The old matplotlib Path.contains_point() approach used radius=1e-9 to include edge points. The new ConvexHull approach uses epsilon=1e-6 which provides similar edge inclusion while being more robust for larger grids. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 26281ab commit 949675f

File tree

1 file changed

+18
-5
lines changed

1 file changed

+18
-5
lines changed

flopy/utils/geospatial_index.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,17 @@ class GeospatialIndex:
2626
----------
2727
grid : Grid
2828
Any FloPy grid object (StructuredGrid, VertexGrid, UnstructuredGrid)
29+
epsilon : float, optional
30+
Tolerance for point-in-cell tests. Used for both bounding box
31+
expansion and ConvexHull hyperplane distance tests. Default is 1e-6,
32+
which works well for grids with coordinates up to ~10,000 units.
2933
3034
Attributes
3135
----------
3236
grid : Grid
3337
The grid object this index was built for
38+
epsilon : float
39+
Tolerance for geometric tests
3440
tree : scipy.spatial.KDTree
3541
KD-tree of cell centroids + vertices for fast spatial queries
3642
point_to_cell : ndarray
@@ -55,16 +61,20 @@ class GeospatialIndex:
5561
>>> cellids = index.query_points(x=[1.5, 5.5, 9.5], y=[1.5, 5.5, 9.5])
5662
"""
5763

58-
def __init__(self, grid):
64+
def __init__(self, grid, epsilon=1e-6):
5965
"""
6066
Build geospatial index for a model grid.
6167
6268
Parameters
6369
----------
6470
grid : Grid
6571
Any FloPy grid (StructuredGrid, VertexGrid, UnstructuredGrid)
72+
epsilon : float, optional
73+
Tolerance for point-in-cell tests (default 1e-6).
74+
Used for bounding box expansion and ConvexHull tests.
6675
"""
6776
self.grid = grid
77+
self.epsilon = epsilon
6878
self._build_index()
6979

7080
def _build_index(self):
@@ -357,17 +367,20 @@ def _point_in_cell_vectorized(self, point, cellid):
357367
bool
358368
True if point is inside cell
359369
"""
360-
# Fast bounding box rejection
370+
# Fast bounding box rejection with epsilon tolerance for edge cases
371+
# Expand bounding box slightly to handle points on boundaries
361372
bbox = self.bounding_boxes[cellid]
362-
if not (bbox[0] <= point[0] <= bbox[1] and bbox[2] <= point[1] <= bbox[3]):
373+
if not (bbox[0] - self.epsilon <= point[0] <= bbox[1] + self.epsilon and
374+
bbox[2] - self.epsilon <= point[1] <= bbox[3] + self.epsilon):
363375
return False
364376

365377
# Precise ConvexHull test
366378
hull_eq = self.hull_equations[cellid]
367379
if hull_eq is not None:
368-
# Vectorized hyperplane test: Ax + By + C <= tolerance
380+
# Vectorized hyperplane test: Ax + By + C <= epsilon
381+
# Tolerance allows points on edges to be included
369382
distances = point @ hull_eq[:, :-1].T + hull_eq[:, -1]
370-
return np.all(distances <= 1e-6)
383+
return np.all(distances <= self.epsilon)
371384
else:
372385
# Degenerate geometry - should rarely happen
373386
return False

0 commit comments

Comments
 (0)