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 boundnilfor open, over the window field: numbers for a:numberfield, instants (DateTime,NaiveDateTimeorDate) for a:timeone (applies?/2). - A box is
%{west, south, east, north}in degrees over a:pointwindow field (Macaw.Data.Source.Request.box?/1);westeast ofeastcrosses the antimeridian. - A region is ranges over named
:timeor:numberfields,%{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
Summary
Functions
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.
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.
:lt, :eq or :gt between two times of one kind.
The earlier of two times, a nil ignored.
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.
Whether a value is an instant: a DateTime, a NaiveDateTime or a Date.
The intersection of two windows, either nil for everything.
The later of two times, a nil ignored.
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.
The rows whose field lies strictly after time; every row for nil. A push's seam.
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.
Whether a value is one a window can be over: a number or an instant.
The rows whose field lies within the window, in their order.
The rows whose field lies in the box, in their order; every row for nil.
The rows whose every ranged field lies in its range; every row for nil.
Whether a time lies within a window, both bounds inclusive; nil lies in none but nil.
Types
@type t() :: [time() | nil] | nil
@type time() :: number() | DateTime.t() | NaiveDateTime.t() | Date.t()
Functions
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
@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}}
]}
:lt, :eq or :gt between two times of one kind.
The earlier of two times, a nil ignored.
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.
Whether a value is an instant: a DateTime, a NaiveDateTime or a Date.
The intersection of two windows, either nil for everything.
The later of two times, a nil ignored.
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}]
@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.
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.
Whether a value is one a window can be over: a number or an instant.
@spec trim([map()], Macaw.Data.Table.name(), t()) :: [map()]
The rows whose field lies within the window, in their order.
@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.
@spec trim_region([map()], %{required(Macaw.Data.Table.name()) => t()} | nil) :: [ map() ]
The rows whose every ranged field lies in its range; every row for nil.
Whether a time lies within a window, both bounds inclusive; nil lies in none but nil.