# `Sidereon.Geofence`
[🔗](https://github.com/neilberkman/sidereon-ex/blob/main/lib/sidereon/geofence.ex#L1)

WGS84 geodesic polygon geofencing with position uncertainty.

Fence vertices and query positions are supplied in geodetic degrees at this
boundary. Boundary distances are metres, positive inside the fence and
negative outside it. Probabilistic calls accept horizontal covariance or
radius uncertainty and return inside probabilities from the core geofence
model.

# `covariance_m2`

```elixir
@type covariance_m2() :: [[number()]]
```

Three-by-three covariance matrix in square metres.

# `crossing_event`

```elixir
@type crossing_event() :: %{
  sample_index: non_neg_integer(),
  kind: :entered | :left,
  inside_probability: float()
}
```

Fence crossing event.

# `error_reason`

```elixir
@type error_reason() ::
  :too_few_vertices
  | :invalid_uncertainty
  | :invalid_probability_method
  | :covariance_rotation_failed
  | {:invalid_position, term()}
  | {:invalid_uncertainty, term()}
  | {:invalid_input, String.t(), String.t()}
  | {:geodesic, String.t()}
  | {:uncertainty_validation_failed, atom()}
```

Typed geofence error returned by this module.

# `position`

```elixir
@type position() ::
  {number(), number()}
  | {number(), number(), number()}
  | %{
      :lat_deg =&gt; number(),
      :lon_deg =&gt; number(),
      optional(:height_m) =&gt; number()
    }
```

Geodetic position in public degree units.

# `probability_method`

```elixir
@type probability_method() :: :boundary_normal | :planar_quadrature
```

Probability integration method.

# `uncertainty`

```elixir
@type uncertainty() ::
  {:enu_covariance_m2, covariance_m2()}
  | {:ecef_covariance_m2, covariance_m2()}
  | {:cep_radius_m, number()}
  | {:horizontal_radius_m, number(), number()}
  | map()
```

Position uncertainty descriptor for probabilistic geofencing.

Supported forms are `{:enu_covariance_m2, matrix}`,
`{:ecef_covariance_m2, matrix}`, `{:cep_radius_m, radius_m}`, and
`{:horizontal_radius_m, probability, radius_m}`.

# `vertex`

```elixir
@type vertex() :: {float(), float(), float()}
```

Fence vertex in public degree units.

# `containment`

```elixir
@spec containment(Sidereon.Geofence.Fence.t(), position()) ::
  {:ok, boolean()} | {:error, error_reason()}
```

Return whether `position` is contained by `fence`.

Points within the core boundary tolerance are classified as contained.

# `containment_probability`

```elixir
@spec containment_probability(
  Sidereon.Geofence.Fence.t(),
  position(),
  uncertainty(),
  keyword()
) ::
  {:ok, float()} | {:error, error_reason()}
```

Return inside probability for one uncertain position.

Options:

  * `:method` selects `:boundary_normal` or `:planar_quadrature`, default
    `:boundary_normal`.

# `crossing`

```elixir
@spec crossing(Sidereon.Geofence.Fence.t(), [position()]) ::
  {:ok, [crossing_event()]} | {:error, error_reason()}
```

Return boolean crossing events for a sequence of positions.

The returned events identify the first sample index whose containment changed.

# `crossing_probability`

```elixir
@spec crossing_probability(Sidereon.Geofence.Fence.t(), [term()], keyword()) ::
  {:ok, [crossing_event()]} | {:error, error_reason()}
```

Return probabilistic crossing events for uncertain position samples.

Samples are `{position, uncertainty}` tuples or maps containing `:position`
and `:uncertainty`. Options:

  * `:hysteresis` is a `Sidereon.Geofence.Hysteresis` struct or keyword list.
    Defaults to enter and leave confidence `0.95`.
  * `:method` selects `:boundary_normal` or `:planar_quadrature`, default
    `:boundary_normal`.

# `distance_to_boundary`

```elixir
@spec distance_to_boundary(Sidereon.Geofence.Fence.t(), position()) ::
  {:ok, float()} | {:error, error_reason()}
```

Return signed distance from `position` to the nearest fence boundary, metres.

Positive distances are inside the fence, negative distances are outside, and
values near zero are on the boundary.

# `new`

```elixir
@spec new([position()]) ::
  {:ok, Sidereon.Geofence.Fence.t()} | {:error, error_reason()}
```

Construct a geodesic polygon fence from WGS84 degree vertices.

A closing vertex equal to the first vertex is accepted. Height is retained in
`Fence.vertices` but ignored by containment and distance calculations.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
