Rover.Components (Rover v0.1.0)

Copy Markdown View Source

The <.map> component.

import Rover.Components

<.map id="clients" center={{45.75, 4.85}} zoom={12} markers={@clients} />

That is the whole API for the common case. Everything below is about the less common ones.

How updates reach the map

Rover renders your markers into a data-rover-markers attribute. When you assign/3 a new list, LiveView diffs the attribute and sends only that attribute down the wire; the JavaScript runtime then diffs the list by marker id and touches only the OpenLayers features that actually changed. Adding one marker to a list of five hundred adds one feature — it does not rebuild the layer, and it does not interrupt a pan, a zoom or an open popup.

This is why Rover.Marker insists on a stable :id.

Events

Each on_* attribute takes the name of an event your LiveView handles:

<.map id="clients" markers={@clients} on_marker_click="select_client" />

def handle_event("select_client", %{"id" => id}, socket) do
  {:noreply, assign(socket, selected: id)}
end
AttributePayload
on_marker_click%{"id" => id, "lat" => lat, "lon" => lon, "data" => data}
on_map_click%{"lat" => lat, "lon" => lon}
on_move_end%{"center" => [lat, lon], "zoom" => zoom, "bbox" => %{"south" =>, "west" =>, "north" =>, "east" =>}}
on_marker_drag_end%{"id" => id, "lat" => lat, "lon" => lon}

Inside a Phoenix.LiveComponent, route the events to yourself with target={@myself}.

Viewports can straddle the antimeridian

Longitudes are wrapped into -180..180, so a user looking at Fiji or New Zealand gets a bbox where west is greater than east. When that happens the map adds "crosses_antimeridian" => true, because the obvious query — where: m.lon >= ^west and m.lon <= ^east — matches nothing for those users. Split the range in two when you see the flag.

Controlled view, uncontrolled panning

center and zoom are applied when they change on the server. A user panning the map does not push new values back unless you ask for them with on_move_end, and a re-render triggered by something unrelated will not yank the view back to where it started. Assign a new center and the map animates to it.

When you give no center at all, Rover derives a starting frame from the markers. That derived value is explicitly not treated as an instruction — otherwise moving one marker would shift the centroid and drag the view along with it on every update.

Framing versus refitting

Two separate things:

  • The first frame. With no center, "put my markers on screen" is the whole instruction, so the map always fits the markers once when it appears. The client does it, because only the client knows the viewport size.
  • Refitting. fit governs what happens afterwards. false leaves the view alone, :once does nothing more, true refits on every change.

Summary

Functions

Renders an interactive map.

Functions

map(assigns)

@spec map(map()) :: Phoenix.LiveView.Rendered.t()

Renders an interactive map.

Examples

Three markers around Lyon, clickable:

<.map
  id="clients"
  center={{45.75, 4.85}}
  zoom={12}
  markers={@clients}
  on_marker_click="select_client"
/>

Fit the view to whatever is on the map instead of choosing a center:

<.map id="fleet" markers={@vehicles} fit={true} tiles={:carto_dark} height="60vh" />

Read markers out of an Ecto schema that names its fields differently:

<.map
  id="stores"
  markers={@stores}
  marker_fields={[lat: :latitude, lon: :longitude, label: :trade_name]}
/>

Attributes

  • id (:string) (required) - DOM id. Required — the map is a stateful hook and LiveView needs to track it.

  • center (:any) - The {lat, lon} the view is centred on. Defaults to the centre of markers when they are given, and to {0.0, 0.0} otherwise.

    Defaults to nil.

  • zoom (:any) - Zoom level, roughly 0 (world) to 20 (building). Defaults to nil.

  • min_zoom (:any) - Lowest zoom the user can reach. Defaults to nil.

  • max_zoom (:any) - Highest zoom the user can reach. Defaults to nil.

  • markers (:list) - Anything Rover.Marker.new!/2 accepts: maps, structs, Rover.Markers. Defaults to [].

  • marker_fields (:list) - Field mapping passed to Rover.Marker.new!/2, e.g. [lat: :latitude]. Defaults to [].

  • tiles (:any) - A Rover.Tiles preset, {:xyz, url}, or :none. Defaults to :osm.

  • fit (:any) - Controls refitting as markers change: true refits on every change, :once or false do not. Defaults to :once when no center is given, false otherwise. Note that a map given no center always fits once when it first appears, whatever fit says — see "Framing versus refitting".

    Defaults to nil.

  • fit_padding (:integer) - Pixels kept clear around a fitted view. Defaults to 48.

  • controls (:list) - Any of :zoom, :attribution, :scale_line, :full_screen, :rotate. Defaults to [:zoom, :attribution].

  • interactive (:boolean) - When false the map becomes a picture: no panning, zooming, dragging, tooltips, cursor changes or click events, and the zoom, fullscreen and rotate controls are withheld. The attribution stays — it is a licence obligation, not an interaction — and so does the scale line if you asked for one.

    Defaults to true.

  • on_marker_click (:string) - Defaults to nil.

  • on_map_click (:string) - Defaults to nil.

  • on_move_end (:string) - Defaults to nil.

  • on_marker_drag_end (:string) - Defaults to nil.

  • target (:any) - @myself to route events to the enclosing Phoenix.LiveComponent. Defaults to nil.

  • height (:string) - CSS height. Set to nil to style it yourself. Defaults to "24rem".

  • class (:any) - Extra classes on the map container. Defaults to nil.

  • Global attributes are accepted.