Source code for shapelib.tools.lines.distance
import numpy as np
import geopandas as gpd
from shapely.strtree import STRtree
[docs]
def estimate_line_spacing(
lines: gpd.GeoDataFrame,
sample: float = 0.1,
min_samples: int = 30,
seed: int | None = None,
) -> float:
"""
Estimate the spacing between parallel planting lines (e.g. rows every
1.5 m or 2.4 m).
Samples a few lines, measures each one's center-to-nearest-line distance,
and returns the median (robust to far-apart edge/stray lines). Assumes
lines are roughly parallel with uniform spacing, so a small sample suffices.
Parameters
----------
lines : gpd.GeoDataFrame
Line geometries in a metric CRS (e.g. UTM).
sample : float, default 0.1
Fraction of lines to sample if ``<= 1``, else an absolute count.
min_samples : int, default 30
Minimum lines to sample, clamped to the total.
seed : int, optional
Seed for reproducible sampling.
Returns
-------
float
Estimated spacing in CRS units (meters for UTM).
"""
geometries = lines.geometry.values
n = len(geometries)
if n < 2:
return 0.0
k = int(sample) if sample > 1 else int(round(n * sample))
k = max(k, min_samples)
k = min(k, n)
rng = np.random.default_rng(seed)
sampled_indices = rng.choice(n, size=k, replace=False)
tree = STRtree(geometries)
distances = []
for i in sampled_indices:
geom = geometries[i]
if geom.is_empty:
continue
# ``exclusive=True`` skips the line itself, returning its closest
# neighbor in the index.
neighbor_index = tree.query_nearest(geom, exclusive=True)[0]
center = geom.interpolate(0.5, normalized=True)
distances.append(center.distance(geometries[neighbor_index]))
return float(np.median(distances)) if distances else 0.0