StaticMaps.Viewport (staticmaps v0.1.0)

Copy Markdown View Source

A resolved view: the centre, zoom, size and scale of one rendered image.

The viewport converts world pixels to output pixels. Everything that places something on the image goes through to_output/2, so tiles and overlays share one rounding rule and cannot disagree about where a coordinate lands.

fit/1 works out the centre and zoom when the caller gives bounds or markers instead.

Summary

Types

What fit/1 fits the view to. :center and :zoom are kept when given; :zooms is the range to choose a zoom from, highest first. :reserve is a band of that many logical pixels along the bottom, such as the attribution strip, that nothing is fitted into.

t()

Functions

Resolves the centre and zoom of a view.

Builds a viewport centred on center.

The size of the output image in pixels, size * scale.

Converts a point in world pixels to output pixels, rounding halves up.

Types

fit_request()

@type fit_request() :: %{
  optional(:reserve) => non_neg_integer(),
  center: StaticMaps.Projection.lat_lng() | nil,
  zoom: non_neg_integer() | nil,
  bounds:
    {StaticMaps.Projection.lat_lng(), StaticMaps.Projection.lat_lng()} | nil,
  markers: [StaticMaps.Marker.t()],
  padding: non_neg_integer(),
  size: {pos_integer(), pos_integer()},
  zooms: Range.t()
}

What fit/1 fits the view to. :center and :zoom are kept when given; :zooms is the range to choose a zoom from, highest first. :reserve is a band of that many logical pixels along the bottom, such as the attribution strip, that nothing is fitted into.

t()

@type t() :: %StaticMaps.Viewport{
  center: StaticMaps.Projection.lat_lng(),
  origin: StaticMaps.Projection.point(),
  scale: pos_integer(),
  size: {pos_integer(), pos_integer()},
  zoom: non_neg_integer()
}
  • :center — {lat, lng} of the middle of the image
  • :zoom — integer zoom, in the 256-pixel convention
  • :size — {width, height} in logical pixels, before :scale
  • :scale — output pixels per logical pixel
  • :origin — the top-left corner of the view, in world pixels

Functions

fit(request)

Resolves the centre and zoom of a view.

  • With :center and :zoom, returns them as given.
  • With neither, picks the highest zoom at which the bounds and every marker, pin included, fit inside :size less :padding on every side, and centres on them.
  • With :center only, keeps it and picks the highest zoom at which everything fits around it.
  • With :zoom only, centres everything at that zoom.

The centre is the middle of what is shown in world pixels, not in degrees: Mercator stretches latitudes unevenly, so the midpoint in degrees is off-centre on the map.

Longitudes are fitted by the shortest arc that covers them all, so markers either side of the antimeridian frame the antimeridian rather than the whole world. A :bounds with west > east crosses the antimeridian. Everything at a single point gets zoom 15, within :zooms, or lower if a given centre is too far from it for the point to show. When nothing fits even at the lowest zoom, the lowest zoom is used.

iex> {{lat, lng}, zoom} =
...>   StaticMaps.Viewport.fit(%{center: nil, zoom: nil, bounds: {{0, 0}, {10, 10}},
...>     markers: [], padding: 0, size: {256, 256}, zooms: 0..19})
iex> {Float.round(lat, 4), Float.round(lng, 4), zoom}
{5.0191, 5.0, 5}

new(center, zoom, size, scale)

Builds a viewport centred on center.

iex> vp = StaticMaps.Viewport.new({0, 0}, 0, {256, 256}, 1)
iex> vp.origin
{0.0, 0.0}

output_size(viewport)

@spec output_size(t()) :: {pos_integer(), pos_integer()}

The size of the output image in pixels, size * scale.

to_output(viewport, arg)

@spec to_output(t(), {number(), number()}) :: {integer(), integer()}

Converts a point in world pixels to output pixels, rounding halves up.

iex> vp = StaticMaps.Viewport.new({0, 0}, 1, {100, 100}, 2)
iex> StaticMaps.Viewport.to_output(vp, {256.0, 256.0})
{100, 100}