Source: https://gometry.monicz.dev/api/toplevel/index-join/

# Index & join

Spatial joins and nearest-neighbor queries over geometry columns.

**Related:** [Spatial indexing & joins](https://gometry.monicz.dev/guide/indexing/)

## join

Accepted call forms

```python
join(left: Geometry | (GeometryArray[GeometryRowT] | Iterable[GeometryRowT])[Geometry | None] | Iterable[Geometry | Buffer | Mapping[str, Any] | SupportsGeoInterface | None], right: Geometry | (GeometryArray[GeometryRowT] | Iterable[GeometryRowT])[Geometry | None] | Iterable[Geometry | Buffer | Mapping[str, Any] | SupportsGeoInterface | None], *, predicate: Literal['dwithin'], distance: float, unit: Literal['planar', 'meters'] | None = None) -> tuple[ndarray[tuple[int], dtype[int64]], ndarray[tuple[int], dtype[int64]]]
```

```python
join(left: Geometry | (GeometryArray[GeometryRowT] | Iterable[GeometryRowT])[Geometry | None] | Iterable[Geometry | Buffer | Mapping[str, Any] | SupportsGeoInterface | None], right: Geometry | (GeometryArray[GeometryRowT] | Iterable[GeometryRowT])[Geometry | None] | Iterable[Geometry | Buffer | Mapping[str, Any] | SupportsGeoInterface | None], *, predicate: Literal['intersects', 'contains', 'contains_properly', 'covers', 'within', 'covered_by', 'equals', 'touches', 'crosses', 'overlaps'] = 'intersects', distance: None = None, unit: Literal['planar', 'meters'] | None = None) -> tuple[ndarray[tuple[int], dtype[int64]], ndarray[tuple[int], dtype[int64]]]
```

Perform a spatial join between two geometry collections via an internal index.

Parameters:

| Name | Type | Description | Default |
| --- | --- | --- | --- |
| `left` | `Geometry, GeometryArray, or iterable of geometry-like values or None` | The two geometry collections to join. Missing rows produce no pairs; their original row positions are preserved in every returned id. | *required* |
| `right` | `Geometry, GeometryArray, or iterable of geometry-like values or None` | The two geometry collections to join. Missing rows produce no pairs; their original row positions are preserved in every returned id. | *required* |
| `predicate` | `str` | Spatial predicate each returned pair must satisfy — one of `'intersects'`, `'contains'`, `'contains_properly'`, `'covers'`, `'within'`, `'covered_by'`, `'equals'`, `'dwithin'`, `'touches'`, `'crosses'`, or `'overlaps'`. Topological forms omit `predicate` to use `'intersects'`; `'dwithin'` requires it explicitly. | `'intersects'` |
| `distance` | `float` | Required when `predicate='dwithin'`: the maximum separation, in CRS-natural units — geodesic meters on a geographic CRS, native linear units on a projected CRS, raw coordinate units when CRS-free. Finite non-negative values and positive infinity are accepted; missing or empty rows produce no pairs. | `None` |
| `unit` | `('planar', 'meters')` | `None` (the default) follows the CRS: geographic shape area and length use ellipsoidal chart-edge measures while point distances use geodesics; projected measurements use native linear units, and CRS-free measurements use raw coordinate units (area results square the corresponding unit). `'planar'` uses Cartesian coordinate space, with geographic coordinates in their native angular units. `'meters'` uses the CRS-aware metric and requires a CRS. | `'planar'` |

Returns:

| Type | Description |
| --- | --- |
| `tuple of numpy.ndarray` | `(left, right)` parallel int64 row-id columns satisfying the predicate. |

Raises:

| Type | Description |
| --- | --- |
| `CRSMismatchError` | If the operands' CRS or coordinate-epoch metadata differ. |
| `GeometryError` | If `predicate` is unknown, `distance` is missing or invalid for `predicate='dwithin'`, or `unit='meters'` is requested for a CRS-free geometry. |
| `CRSError` | If geographic `dwithin` refinement cannot prove a result within the point-edge evaluation budget. |

Examples:

```pycon
>>> import gometry as gm
>>> stops = gm.GeometryArray([gm.Point(0.5, 0.5), gm.Point(9, 9)])
>>> zones = gm.GeometryArray([gm.box(0, 0, 1, 1)])
>>> left, right = gm.join(stops, zones, predicate='within')
>>> (left, right)
(array([0]), array([0]))
```

## nearest

Accepted call forms

```python
nearest(geom: Geometry, values: Geometry | GeometryArray[Geometry | None] | Iterable[Geometry | Buffer | Mapping[str, Any] | SupportsGeoInterface | None], *, k: int = 1, max_distance: float | None = None, return_distance: Literal[False] = False, unit: Literal['planar', 'meters'] | None = None, exclusive: bool = False, ties: bool = False) -> ndarray[tuple[int], dtype[int64]]
```

```python
nearest(geom: Geometry, values: Geometry | GeometryArray[Geometry | None] | Iterable[Geometry | Buffer | Mapping[str, Any] | SupportsGeoInterface | None], *, k: int = 1, max_distance: float | None = None, return_distance: Literal[True], unit: Literal['planar', 'meters'] | None = None, exclusive: bool = False, ties: bool = False) -> tuple[ndarray[tuple[int], dtype[int64]], NDArray[float64]]
```

```python
nearest(geom: (GeometryArray[GeometryRowT] | Iterable[GeometryRowT])[Geometry], values: Geometry | GeometryArray[Geometry | None] | Iterable[Geometry | Buffer | Mapping[str, Any] | SupportsGeoInterface | None], *, k: int = 1, max_distance: float | None = None, return_distance: Literal[False] = False, unit: Literal['planar', 'meters'] | None = None, exclusive: bool = False, ties: bool = False) -> Groups[ndarray[tuple[int], dtype[int64]]]
```

```python
nearest(geom: (GeometryArray[GeometryRowT] | Iterable[GeometryRowT])[Geometry | None], values: Geometry | GeometryArray[Geometry | None] | Iterable[Geometry | Buffer | Mapping[str, Any] | SupportsGeoInterface | None], *, k: int = 1, max_distance: float | None = None, return_distance: Literal[False] = False, unit: Literal['planar', 'meters'] | None = None, exclusive: bool = False, ties: bool = False) -> Groups[ndarray[tuple[int], dtype[int64]] | None]
```

```python
nearest(geom: (GeometryArray[GeometryRowT] | Iterable[GeometryRowT])[Geometry], values: Geometry | GeometryArray[Geometry | None] | Iterable[Geometry | Buffer | Mapping[str, Any] | SupportsGeoInterface | None], *, k: int = 1, max_distance: float | None = None, return_distance: Literal[True], unit: Literal['planar', 'meters'] | None = None, exclusive: bool = False, ties: bool = False) -> tuple[Groups[ndarray[tuple[int], dtype[int64]]], NDArray[float64]]
```

```python
nearest(geom: (GeometryArray[GeometryRowT] | Iterable[GeometryRowT])[Geometry | None], values: Geometry | GeometryArray[Geometry | None] | Iterable[Geometry | Buffer | Mapping[str, Any] | SupportsGeoInterface | None], *, k: int = 1, max_distance: float | None = None, return_distance: Literal[True], unit: Literal['planar', 'meters'] | None = None, exclusive: bool = False, ties: bool = False) -> tuple[Groups[ndarray[tuple[int], dtype[int64]] | None], NDArray[float64]]
```

```python
nearest(geom: Geometry, values: Geometry | GeometryArray[Geometry | None] | Iterable[Geometry | Buffer | Mapping[str, Any] | SupportsGeoInterface | None], *, k: int = 1, max_distance: float | None = None, return_distance: bool = False, unit: Literal['planar', 'meters'] | None = None, exclusive: bool = False, ties: bool = False) -> NDArray[int64] | tuple[NDArray[int64], NDArray[float64]]
```

```python
nearest(geom: (GeometryArray[GeometryRowT] | Iterable[GeometryRowT])[Geometry], values: Geometry | GeometryArray[Geometry | None] | Iterable[Geometry | Buffer | Mapping[str, Any] | SupportsGeoInterface | None], *, k: int = 1, max_distance: float | None = None, return_distance: bool = False, unit: Literal['planar', 'meters'] | None = None, exclusive: bool = False, ties: bool = False) -> Groups[ndarray[tuple[int], dtype[int64]]] | tuple[Groups[ndarray[tuple[int], dtype[int64]]], NDArray[float64]]
```

```python
nearest(geom: (GeometryArray[GeometryRowT] | Iterable[GeometryRowT])[Geometry | None], values: Geometry | GeometryArray[Geometry | None] | Iterable[Geometry | Buffer | Mapping[str, Any] | SupportsGeoInterface | None], *, k: int = 1, max_distance: float | None = None, return_distance: bool = False, unit: Literal['planar', 'meters'] | None = None, exclusive: bool = False, ties: bool = False) -> Groups[NDArray[int64] | None] | tuple[Groups[NDArray[int64] | None], NDArray[float64]]
```

```python
nearest(geom: Geometry | (GeometryArray[GeometryRowT] | Iterable[GeometryRowT])[Geometry | None], values: Geometry | GeometryArray[Geometry | None] | Iterable[Geometry | Buffer | Mapping[str, Any] | SupportsGeoInterface | None], *, k: int = 1, max_distance: float | None = None, return_distance: bool = False, unit: Literal['planar', 'meters'] | None = None, exclusive: bool = False, ties: bool = False) -> ndarray[tuple[int], dtype[int64]] | Groups[ndarray[tuple[int], dtype[int64]] | None] | tuple[ndarray[tuple[int], dtype[int64]], NDArray[float64]] | tuple[Groups[ndarray[tuple[int], dtype[int64]] | None], NDArray[float64]]
```

Find the nearest of `values` to each query geometry (builds an index).

Parameters:

| Name | Type | Description | Default |
| --- | --- | --- | --- |
| `geom` | `Geometry, GeometryArray, or iterable of Geometry or None` | Query geometry or one query per sequence row. Missing rows produce missing result groups and retain their logical positions. | *required* |
| `values` | `Geometry, GeometryArray, or iterable of geometry-like values or None` | Candidate geometries to index and search. | *required* |
| `k` | `int` | Number of nearest neighbors to return per query; `0` returns no neighbors. | `1` |
| `max_distance` | `float` | Ignore candidates farther than this from the query. Must be finite and non-negative. | `None` |
| `return_distance` | `bool` | If `True`, return distances alongside handles — `(indices, distances)` for a scalar query, `(matches, distances)` for an array query. The distance-returning form requires `return_distance=True`. | `False` |
| `unit` | `('planar', 'meters')` | `None` (the default) follows the CRS: geographic shape area and length use ellipsoidal chart-edge measures while point distances use geodesics; projected measurements use native linear units, and CRS-free measurements use raw coordinate units (area results square the corresponding unit). `'planar'` uses Cartesian coordinate space, with geographic coordinates in their native angular units. `'meters'` uses the CRS-aware metric and requires a CRS. | `'planar'` |
| `exclusive` | `bool` | If `True`, skip candidates structurally equal to the query geometry (same exact coordinates) — "the nearest *other* feature". | `False` |
| `ties` | `bool` | Also return every candidate TYING the k-th nearest distance (exact comparison) — results can then exceed `k`. | `False` |

Returns:

| Type | Description |
| --- | --- |
| `int64 numpy.ndarray, Groups, or tuple` | Indices into `values` of the nearest geometries — an `int64` ndarray for a scalar query, CSR `Groups` for an array or iterable query. With `return_distance=True`, plain tuple field order is `(indices, distances)` for a scalar query or `(matches, distances)` for an array or iterable query (distances parallel to `matches.values`). |

Raises:

| Type | Description |
| --- | --- |
| `GeometryError` | If `k` is negative, `max_distance` is negative or non-finite, or `unit='meters'` is requested for a CRS-free geometry. |
| `CRSMismatchError` | If query and indexed geometries have different CRS or coordinate-epoch metadata. |

Examples:

```pycon
>>> import gometry as gm
>>> sites = [gm.Point(0, 0), gm.Point(5, 5)]
>>> gm.nearest(gm.Point(4, 4), sites)
array([1])
```
