Rover.Geo (Rover v0.3.0)

Copy Markdown View Source

Coordinate handling for Rover.

Rover speaks {latitude, longitude} everywhere — the order humans use when they read a coordinate out loud. OpenLayers works internally in [x, y] (i.e. [longitude, latitude]) projected to Web Mercator; that flip happens once, in the JavaScript runtime, and never leaks into your application code.

Mixing the two up is the single most common OpenLayers bug, so this module is deliberately strict: latitudes outside -90..90 and longitudes outside -180..180 raise instead of silently placing your marker in the ocean.

iex> Rover.Geo.coord!({45.75, 4.85})
{45.75, 4.85}

iex> Rover.Geo.coord!(%{lat: 45.75, lng: 4.85})
{45.75, 4.85}

Summary

Types

A bounding box, as {south, west, north, east}.

A latitude/longitude pair, in that order.

Anything Rover accepts as a coordinate: a {lat, lon} tuple, or a map with :lat/:latitude and :lon/:lng/:longitude keys (atom or string).

Functions

Returns the bounding box {south, west, north, east} enclosing coords.

Same as coord!/1 but returns {:ok, coord} or :error.

Normalises value into a {lat, lon} tuple of floats.

Great-circle distance between two coordinates, in metres (haversine).

Renders a coordinate as the [lon, lat] pair OpenLayers expects.

Types

bbox()

@type bbox() :: {lat(), lon(), lat(), lon()}

A bounding box, as {south, west, north, east}.

coord()

@type coord() :: {lat(), lon()}

A latitude/longitude pair, in that order.

coordish()

@type coordish() :: coord() | map()

Anything Rover accepts as a coordinate: a {lat, lon} tuple, or a map with :lat/:latitude and :lon/:lng/:longitude keys (atom or string).

lat()

@type lat() :: float()

lon()

@type lon() :: float()

Functions

bbox(coords)

@spec bbox([coordish()]) :: bbox() | nil

Returns the bounding box {south, west, north, east} enclosing coords.

Returns nil for an empty list. Note that this is a plain min/max box: it does not handle geometries straddling the antimeridian.

Examples

iex> Rover.Geo.bbox([{45.0, 4.0}, {46.0, 5.0}])
{45.0, 4.0, 46.0, 5.0}

iex> Rover.Geo.bbox([])
nil

coord(value)

@spec coord(coordish()) :: {:ok, coord()} | :error

Same as coord!/1 but returns {:ok, coord} or :error.

coord!(map)

@spec coord!(coordish()) :: coord()

Normalises value into a {lat, lon} tuple of floats.

Raises ArgumentError when the shape is unrecognised or the values are out of range.

Examples

iex> Rover.Geo.coord!({45.75, 4.85})
{45.75, 4.85}

iex> Rover.Geo.coord!(%{"latitude" => 48, "longitude" => 2})
{48.0, 2.0}

A latitude of 145.75 is not a latitude, so Rover rejects it rather than quietly drawing your marker somewhere impossible:

iex> Rover.Geo.coord(%{lat: 145.75, lon: 4.85})
:error

distance(a, b)

@spec distance(coordish(), coordish()) :: float()

Great-circle distance between two coordinates, in metres (haversine).

Examples

iex> Rover.Geo.distance({45.75, 4.85}, {48.85, 2.35}) |> round()
392834

to_lon_lat(value)

@spec to_lon_lat(coordish()) :: [float()]

Renders a coordinate as the [lon, lat] pair OpenLayers expects.

You should not need this — the JavaScript runtime handles the flip — but it is public so that Rover.Geo stays useful when you drop down to raw GeoJSON.

iex> Rover.Geo.to_lon_lat({45.75, 4.85})
[4.85, 45.75]