Visualize.Contour (Visualize v0.2.35)

Copy Markdown View Source

Contour generation using the marching squares algorithm.

Computes contour polygons from gridded data. Useful for topographic maps, density visualizations, and isoline plots.

Examples

# Create contours from a 2D grid of values
grid = [
  [0, 0, 0, 0],
  [0, 5, 5, 0],
  [0, 5, 10, 5],
  [0, 0, 5, 0]
]

contours = Visualize.Contour.new()
  |> Visualize.Contour.size(4, 4)
  |> Visualize.Contour.thresholds([2.5, 7.5])
  |> Visualize.Contour.compute(grid)

# Each contour has: value, type: "MultiPolygon", coordinates

# For density estimation from points, use Visualize.Contour.Density

Summary

Functions

Computes contours from a 2D grid of values.

The work one compute/2 does, as counts (spec/07 §6.3, #271): reads, the padded cells the marching visits, once per threshold; segments, the segments it builds; hops, the points the ring-following steps through. D-72's claim — the tracing is linear in the segments and the grid is read once per threshold — is these numbers: hops never exceeds segments, and reads is the padded cells times the thresholds, whatever the grid's size; a test holds it by count rather than by a clock.

Creates a new contour generator

Renders contours as SVG path data.

Sets the grid dimensions

Enables or disables smoothing

Sets the threshold values for contour generation.

Types

contour_result()

@type contour_result() :: %{
  value: number(),
  type: String.t(),
  coordinates: [[[{number(), number()}]]]
}

t()

@type t() :: %Visualize.Contour{
  height: pos_integer(),
  smooth?: boolean(),
  thresholds: [number()] | pos_integer(),
  width: pos_integer()
}

Functions

compute(contour, grid)

@spec compute(t(), [[number()]]) :: [contour_result()]

Computes contours from a 2D grid of values.

Returns a list of contour objects, each with:

  • value: The threshold value
  • type: "MultiPolygon"
  • coordinates: GeoJSON-style coordinates

cost(contour, grid)

@spec cost(t(), [[number()]] | [number()]) :: %{
  reads: non_neg_integer(),
  segments: non_neg_integer(),
  hops: non_neg_integer()
}

The work one compute/2 does, as counts (spec/07 §6.3, #271): reads, the padded cells the marching visits, once per threshold; segments, the segments it builds; hops, the points the ring-following steps through. D-72's claim — the tracing is linear in the segments and the grid is read once per threshold — is these numbers: hops never exceeds segments, and reads is the padded cells times the thresholds, whatever the grid's size; a test holds it by count rather than by a clock.

iex> grid = for y <- 0..3, x <- 0..3, do: x + y
iex> cost = Visualize.Contour.new() |> Visualize.Contour.size(4, 4) |> Visualize.Contour.thresholds([3]) |> Visualize.Contour.cost(grid)
iex> cost.reads
25
iex> cost.hops <= cost.segments
true

new()

@spec new() :: t()

Creates a new contour generator

render(contour, grid)

@spec render(t(), [[number()]]) :: [%{value: number(), path: String.t()}]

Renders contours as SVG path data.

Returns a list of path strings, one per threshold.

size(contour, width, height)

@spec size(t(), pos_integer(), pos_integer()) :: t()

Sets the grid dimensions

smooth(contour, smooth?)

@spec smooth(t(), boolean()) :: t()

Enables or disables smoothing

thresholds(contour, values)

@spec thresholds(t(), [number()] | pos_integer()) :: t()

Sets the threshold values for contour generation.

Can be a list of specific values or a count (generates that many levels).