# `Macaw.Data.Window`
[🔗](https://github.com/dcoai/macaw_data/blob/v0.5.3/lib/macaw/data/window.ex#L1)

Windows, boxes and regions over a source's rows (spec/01 §2.3, §2.4).

- A **window** is `nil` (whatever a source holds) or `[from, to]`, either bound `nil`
  for open, over the window field: numbers for a `:number` field, instants (`DateTime`,
  `NaiveDateTime` or `Date`) for a `:time` one (`applies?/2`).
- A **box** is `%{west, south, east, north}` in degrees over a `:point` window field
  (`Macaw.Data.Source.Request.box?/1`); `west` east of `east` crosses the antimeridian.
- A **region** is ranges over named `:time` or `:number` fields, `%{field => [from, to]}`.

These are the comparisons, the trims and the numeric position that finishing
(`Macaw.Data.Source.Finish`) and push share. Fields are read by either spelling of their
name (`Macaw.Data.Table.get/2`). Ported from visualize's `Visualize.Feed.Window`, its box
rewritten for a geographic box and its ranges kept as the region.

## Examples

    iex> Macaw.Data.Window.within?(5, [0, 10])
    true
    iex> Macaw.Data.Window.trim([%{t: 1}, %{t: 5}, %{t: 9}], :t, [2, nil])
    [%{t: 5}, %{t: 9}]
    iex> Macaw.Data.Window.in_box?({179.5, 0}, %{west: 170, south: -10, east: -170, north: 10})
    true

# `t`

```elixir
@type t() :: [time() | nil] | nil
```

# `time`

```elixir
@type time() :: number() | DateTime.t() | NaiveDateTime.t() | Date.t()
```

# `applies?`

```elixir
@spec applies?(t(), :time | :number) :: boolean()
```

Whether a window applies to a window field of the type: its bounds instants for
`:time`, numbers for `:number`; a `nil` window, and a window with no bound, to either.

    iex> Macaw.Data.Window.applies?([~U[2026-10-12 00:00:00Z], nil], :number)
    false

# `check`

```elixir
@spec check(Macaw.Data.Source.Request.t(), Macaw.Data.Schema.t()) ::
  :ok | {:error, [{list(), term()}, ...]}
```

Whether a schema can answer a request's window, box, region and latest: `:ok`, or
`{:error, faults}`, every fault `{path, reason}` by path. A window must apply to a
`:time` or `:number` window field; a box needs a `:point` window field; each region
field is described, `:time` or `:number`, its range two ends of its type, not inverted;
`latest` needs a `:time` or `:number` window field and a declared identity. A request asking none of them is
`:ok`.

    iex> alias Macaw.Data.{Schema, Source.Request}
    iex> schema = Schema.new!(%{t: %{type: :number, window: true}, s: %{type: :category}})
    iex> Macaw.Data.Window.check(Request.new!(now: ~U[2026-10-27 00:00:00Z], region: %{t: [9, 1], s: ["a", "b"]}, latest: true), schema)
    {:error,
     [
       {[:latest], {:window, :no_identity}},
       {[:region, :s], {:invalid_for, :category}},
       {[:region, :t], {:window, :inverted}}
     ]}

# `compare`

```elixir
@spec compare(time(), time()) :: :lt | :eq | :gt
```

`:lt`, `:eq` or `:gt` between two times of one kind.

# `earliest`

```elixir
@spec earliest(time() | nil, time() | nil) :: time() | nil
```

The earlier of two times, a `nil` ignored.

# `in_box?`

```elixir
@spec in_box?(term(), map()) :: boolean()
```

Whether a position `{longitude, latitude}` lies in a box, bounds inclusive; a box whose
`west` is east of its `east` crosses the antimeridian. Anything but a position lies in
none.

# `instant?`

```elixir
@spec instant?(term()) :: boolean()
```

Whether a value is an instant: a `DateTime`, a `NaiveDateTime` or a `Date`.

# `intersect`

```elixir
@spec intersect(t(), t()) :: t()
```

The intersection of two windows, either `nil` for everything.

# `latest`

```elixir
@spec latest(time() | nil, time() | nil) :: time() | nil
```

The later of two times, a `nil` ignored.

# `latest_rows`

```elixir
@spec latest_rows([map()], (map() -&gt; number() | nil), (map() -&gt; term())) :: [map()]
```

Each identity's last row: of the rows, for each identity `identify` gives, the row whose
`time` (a number) is greatest, the later in order on a tie, kept in their order. A row
whose identity or time is `nil` names nothing and is dropped.

    iex> rows = [%{id: "a", t: 1}, %{id: "b", t: 2}, %{id: "a", t: 3}, %{id: nil, t: 4}]
    iex> Macaw.Data.Window.latest_rows(rows, & &1.t, & &1.id)
    [%{id: "b", t: 2}, %{id: "a", t: 3}]

# `newer`

```elixir
@spec newer([map()], Macaw.Data.Table.name(), time() | nil) :: [map()]
```

The rows whose `field` lies strictly after `time`; every row for `nil`. A push's seam.

# `position`

```elixir
@spec position(term()) :: number() | nil
```

A time as a number, for a reduction's `x`: a number itself, a `DateTime` its Unix
microseconds, a `NaiveDateTime` its Gregorian seconds, a `Date` its Gregorian days;
anything else `nil`, a gap.

# `time?`

```elixir
@spec time?(term()) :: boolean()
```

Whether a value is one a window can be over: a number or an instant.

# `trim`

```elixir
@spec trim([map()], Macaw.Data.Table.name(), t()) :: [map()]
```

The rows whose `field` lies within the window, in their order.

# `trim_box`

```elixir
@spec trim_box([map()], Macaw.Data.Table.name(), map() | nil) :: [map()]
```

The rows whose `field` lies in the box, in their order; every row for `nil`.

# `trim_region`

```elixir
@spec trim_region([map()], %{required(Macaw.Data.Table.name()) =&gt; t()} | nil) :: [
  map()
]
```

The rows whose every ranged field lies in its range; every row for `nil`.

# `within?`

```elixir
@spec within?(time() | nil, t()) :: boolean()
```

Whether a time lies within a window, both bounds inclusive; `nil` lies in none but `nil`.

---

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