Bland.Geo (Elixir Technical Drawing v0.2.0)

Copy Markdown View Source

Geographic projections for plotting longitude/latitude data.

Enable a projection on a figure via Bland.figure(projection: :mercator). After that, every series treats its xs as longitude in degrees and ys as latitude in degrees, and the renderer projects each point through the named projection before applying the usual x/y scales.

Projections

  • :none (default) — identity; no projection
  • :mercator — standard web-mercator; y clamped to ±85°
  • :equirect — equirectangular / plate carrée (identity)

Helpers

iex> {x, y} = Bland.Geo.mercator({0.0, 0.0})
iex> {abs(Float.round(x, 6)), abs(Float.round(y, 6))}
{0.0, 0.0}

iex> {_x, y} = Bland.Geo.mercator({0.0, 45.0})
iex> Float.round(y, 6)
0.881374

Coastlines

ETD ships no built-in coastline data. For simple demos a bounding box or hand-drawn outline suffices; for real maps, parse a GeoJSON source (e.g. Natural Earth) externally and feed the polygons in as line/4 or area/4 series with (lon, lat) coordinates.

Summary

Functions

Equirectangular projection — a no-op pass-through, included for symmetry with mercator/1.

Generates a graticule — a list of {xs, ys} polyline tuples in lon/lat space — suitable for adding as multiple Bland.line/4 calls.

Web-Mercator projection. Longitudes pass through as radians; latitudes are transformed to ln(tan(π/4 + lat/2)). Latitudes are clamped to ±85.05112878° (the standard Mercator cutoff) to avoid infinities at the poles.

Projects {lon, lat} (degrees) through projection.

Projects a list of {lon, lat} pairs.

List of supported projection atoms.

Inscribes the world coastline-less "frame" — a rectangle at the given lon/lat range, traced as {xs, ys}. Handy as a map boundary when you don't have real coastline data.

Functions

equirect(arg)

@spec equirect({number(), number()}) :: {float(), float()}

Equirectangular projection — a no-op pass-through, included for symmetry with mercator/1.

graticule(opts \\ [])

@spec graticule(keyword()) :: [{[float()], [float()]}]

Generates a graticule — a list of {xs, ys} polyline tuples in lon/lat space — suitable for adding as multiple Bland.line/4 calls.

Options

  • :lon_step (default 30) — spacing of meridians in degrees
  • :lat_step (default 30) — spacing of parallels in degrees
  • :lat_range (default {-80, 80}) — extent of meridians (keeps polar clutter minimal on Mercator)
  • :lon_range (default {-180, 180})
  • :samples (default 60) — points per polyline (for curvature fidelity under projection)

Returns a list of {xs, ys} tuples. Each is one full meridian or parallel traced from endpoint to endpoint.

graticule = Bland.Geo.graticule(lon_step: 30, lat_step: 20)

fig =
  Enum.reduce(graticule, base_fig, fn {xs, ys}, acc ->
    Bland.line(acc, xs, ys, stroke: :dotted)
  end)

mercator(arg)

@spec mercator({number(), number()}) :: {float(), float()}

Web-Mercator projection. Longitudes pass through as radians; latitudes are transformed to ln(tan(π/4 + lat/2)). Latitudes are clamped to ±85.05112878° (the standard Mercator cutoff) to avoid infinities at the poles.

project(arg1, point)

@spec project(
  atom(),
  {number(), number()}
) :: {float(), float()}

Projects {lon, lat} (degrees) through projection.

Unknown projections fall back to the identity transform.

project_all(proj, points)

@spec project_all(atom(), [{number(), number()}]) :: [{float(), float()}]

Projects a list of {lon, lat} pairs.

projections()

@spec projections() :: [atom()]

List of supported projection atoms.

world_rect(arg1, arg2, samples \\ 40)

@spec world_rect({number(), number()}, {number(), number()}, pos_integer()) ::
  {[float()], [float()]}

Inscribes the world coastline-less "frame" — a rectangle at the given lon/lat range, traced as {xs, ys}. Handy as a map boundary when you don't have real coastline data.