Visualize.Quantity (Visualize v0.3.0)

Copy Markdown View Source

A fixed list of quantities, each with a base unit and a display-scaling family, and the display scaling they choose (spec/05 §4.2–4.3, D-138).

A quantity is what a unit measures. A unit (a UCUM code, Visualize.Unit) and a quantity together are a measure, %{unit: code, quantity: quantity}, the two facts a field description carries. They are facts about the data, not knobs: a measure does not change a value, it says what the value already is, so a display can choose how to show it — 0–1,500 ms as 0–1.5 s, 2,500,000 By as 2.5 MB.

The list is fixed and grows by proposal:

QuantityBase unitDisplay family
:durationsns us ms s min h d
:informationByBy kBy MBy GBy; binary KiBy MiBy GiBy; bits
:data_rateBy/sthe information families over /s
:frequencyHzHz kHz MHz GHz; events /h /min /s
:ratio1—
:count1— (SI from a thousand)
:temperatureCel—
:lengthmmm m km
:speedm/s—
:powerWW kW MW GW
:energyJJ kJ MJ GJ; watt-hours W.h kW.h MW.h GW.h
:pressurePaPa kPa MPa

Information is decimal by default — UCUM's kBy and Telemetry.Metrics' :kilobyte are 1,000 bytes — and a unit written with a binary prefix (MiBy) or in bit scales in its own family: the data picks it, no setting does.

Tick formats are built on Visualize.Format.Auto (spec/05 §3), and every function that formats takes its locale:.

Examples

iex> Visualize.Quantity.display_unit([0, 1500], %{unit: "ms"})
"s"

iex> scale = Visualize.Scale.linear() |> Visualize.Scale.domain([0, 1500])
iex> Visualize.Quantity.formatter(scale, %{unit: "ms"}).(1500)
"1.5 s"

iex> Visualize.Quantity.label(%{unit: "{request}"})
"request"

Summary

Types

A unit and a quantity, either or both; the two facts a field description carries.

Why a measure does not resolve.

t()

One of the fixed list of quantities.

Functions

The fixed list of quantities (spec/05 §4.2).

A quantity's base unit, the unit its values are in when no unit is given.

The family member that fits a domain in a measure (spec/05 §4.3): the largest in which the domain's magnitude is at least 1, else the smallest. A measure with no family, a unit without a factor (Cel) or a zero magnitude keeps its unit; no unit is nil.

The display family a unit of the quantity scales in, smallest first; [] for a quantity displayed in its own unit (spec/05 §4.2). The unit picks among a quantity's families: bits, binary prefixes, hertz, watt-hours.

The tick formatter of a realised scale in a measure (spec/05 §4.3): each tick scaled into the display unit, written by Visualize.Format.Auto's notation of the scaled domain, then followed by the unit's symbol. A log or symlog scale gives each tick its own display unit; any other scale but the numeric ones is Visualize.Format.Auto.formatter/2, the measure unused. Raises ArgumentError for a measure resolve/1 refuses.

The label rule (spec/05 §4.3): the unit, else the quantity, else nothing — the unit's symbol with its annotations written when it is not empty, else the quantity's name with spaces for underscores, else nil. Raises ArgumentError for a measure resolve/1 refuses.

The quantity a code's dimension implies, or nil when none of the list has it. A dimensionless code holding % is a :ratio, any other a :count.

A measure completed and checked (spec/05 §4.2): a unit alone implies its quantity, a quantity alone means its base unit, and a unit whose dimension is not its quantity's is refused. A :ratio or a :count takes any dimensionless unit.

What follows a number written in a unit (spec/05 §4.3): its symbol after a space, % and a symbol beginning with / directly, and nothing for an empty symbol or no unit.

Types

measure()

@type measure() :: %{
  optional(:unit) => Visualize.Unit.code() | nil,
  optional(:quantity) => t() | nil
}

A unit and a quantity, either or both; the two facts a field description carries.

reason()

@type reason() ::
  {:unit, Visualize.Unit.code(), Visualize.Unit.reason()}
  | {:quantity, t(), Visualize.Unit.code()}
  | {:unknown_quantity, term()}

Why a measure does not resolve.

t()

@type t() ::
  :duration
  | :information
  | :data_rate
  | :frequency
  | :ratio
  | :count
  | :temperature
  | :length
  | :speed
  | :power
  | :energy
  | :pressure

One of the fixed list of quantities.

Functions

all()

@spec all() :: [t(), ...]

The fixed list of quantities (spec/05 §4.2).

base_unit(quantity)

@spec base_unit(t()) :: Visualize.Unit.code()

A quantity's base unit, the unit its values are in when no unit is given.

display_unit(domain, measure)

@spec display_unit([term()], measure()) :: Visualize.Unit.code() | nil

The family member that fits a domain in a measure (spec/05 §4.3): the largest in which the domain's magnitude is at least 1, else the smallest. A measure with no family, a unit without a factor (Cel) or a zero magnitude keeps its unit; no unit is nil.

family(quantity, unit)

@spec family(t(), Visualize.Unit.code() | nil) :: [Visualize.Unit.code()]

The display family a unit of the quantity scales in, smallest first; [] for a quantity displayed in its own unit (spec/05 §4.2). The unit picks among a quantity's families: bits, binary prefixes, hertz, watt-hours.

Examples

iex> Visualize.Quantity.family(:information, "MiBy")
["By", "KiBy", "MiBy", "GiBy"]

formatter(scale, measure, opts \\ [])

@spec formatter(struct(), measure(), keyword()) :: (term() -> String.t()) | nil

The tick formatter of a realised scale in a measure (spec/05 §4.3): each tick scaled into the display unit, written by Visualize.Format.Auto's notation of the scaled domain, then followed by the unit's symbol. A log or symlog scale gives each tick its own display unit; any other scale but the numeric ones is Visualize.Format.Auto.formatter/2, the measure unused. Raises ArgumentError for a measure resolve/1 refuses.

Options

  • :count - the tick count the axis asks for (default 10)
  • :locale - default "en"

label(measure)

@spec label(measure()) :: String.t() | nil

The label rule (spec/05 §4.3): the unit, else the quantity, else nothing — the unit's symbol with its annotations written when it is not empty, else the quantity's name with spaces for underscores, else nil. Raises ArgumentError for a measure resolve/1 refuses.

Examples

iex> Visualize.Quantity.label(%{unit: "ms"})
"ms"

iex> Visualize.Quantity.label(%{quantity: :count})
"count"

iex> Visualize.Quantity.label(%{})
nil

of_unit(unit)

@spec of_unit(Visualize.Unit.code()) :: t() | nil

The quantity a code's dimension implies, or nil when none of the list has it. A dimensionless code holding % is a :ratio, any other a :count.

Examples

iex> Visualize.Quantity.of_unit("kW.h")
:energy

iex> Visualize.Quantity.of_unit("{request}")
:count

resolve(measure)

@spec resolve(measure()) ::
  {:ok, %{unit: Visualize.Unit.code() | nil, quantity: t() | nil}}
  | {:error, reason()}

A measure completed and checked (spec/05 §4.2): a unit alone implies its quantity, a quantity alone means its base unit, and a unit whose dimension is not its quantity's is refused. A :ratio or a :count takes any dimensionless unit.

Examples

iex> Visualize.Quantity.resolve(%{quantity: :duration})
{:ok, %{unit: "s", quantity: :duration}}

iex> Visualize.Quantity.resolve(%{unit: "By", quantity: :duration})
{:error, {:quantity, :duration, "By"}}

suffix(unit)

@spec suffix(Visualize.Unit.code() | nil) :: String.t()

What follows a number written in a unit (spec/05 §4.3): its symbol after a space, % and a symbol beginning with / directly, and nothing for an empty symbol or no unit.

Examples

iex> Visualize.Quantity.suffix("Cel")
" °C"

iex> Visualize.Quantity.suffix("{request}/s")
"/s"