Most of a map belongs in props. Some of it does not.

When to reach for it

"Fly to the result the user just picked" is an event, not a value. Re-rendering the same centre twice should not re-animate, and animating is the whole point. That is the category HoloMap.API exists for.

SituationUse
The camera reflects application stateHoloMap.Map's center / zoom props
The camera should animate in response to an eventfly_to/2, ease_to/2
A layer's paint reflects stateHoloMap.Layer.*'s paint prop
A one-off highlight that should not survive a re-renderset_paint_property/4
Hover and selection stylingset_feature_state/3

Anything set imperatively is overwritten the next time the declarative specification for that object changes. The reconciler treats rendered props as the source of truth.

Calling it

Always from an action, always with the map's cid first:

def action(:show_parcel, params, component) do
  HoloMap.API.fly_to("explorer", center: {params.lng, params.lat}, zoom: 17)
  put_state(component, :selected, params.id)
end

Actions only

These calls compile to no-ops on the server. They work in action/3 and in functions an action reaches; they do nothing in init/3 or command/3.

To move the camera at mount time, chain an action from init:

def init(_params, component, _server) do
  component
  |> put_state(:style, @style_url)
  |> put_action(:frame_data)
end

Calls made before the style finishes loading are queued and replayed, so an action scheduled from init is not lost.

Camera

HoloMap.API.fly_to("explorer", center: {-70.66, 19.45}, zoom: 14, duration: 1_200)
HoloMap.API.ease_to("explorer", bearing: 45, pitch: 60)
HoloMap.API.jump_to("explorer", center: {-70.66, 19.45})
HoloMap.API.fit_bounds("explorer", [-71.0, 19.0, -70.0, 20.0], padding: 48)
HoloMap.API.resize("explorer")

fly_to/2 arcs out and back in, which reads well over long distances. ease_to/2 travels in a straight line, which reads better over short ones. jump_to/2 does not animate, which is what a center prop change does.

fit_bounds/3 accepts any shape HoloMap.Spec.bounds/1 understands: a pair of corners, or a flat [west, south, east, north] list.

resize/1 is almost never needed. MapLibre observes its own container, so a map whose container changes size follows along by itself and this call does nothing. The one case where it is required is a map built with track_resize={false}, which switches that observer off; the demo's camera page has one set up that way so the difference is visible.

Layers

HoloMap.API.set_filter("explorer", "parcels-fill", ["==", ["get", "zone"], "R1"])
HoloMap.API.set_paint_property("explorer", "parcels-fill", :fill_color, "#ef4444")
HoloMap.API.set_layout_property("explorer", "parcels-fill", :visibility, "none")

Property names may be atoms in Elixir style or MapLibre specification names; both reach MapLibre as the latter.

These duplicate what the paint, layout and filter props already do, and in almost every case the props are the better choice, because they survive a re-render. Reach here for something genuinely transient.

Feature state

Feature state is the mechanism behind hover and selection styling, and it has no declarative equivalent: it belongs to a feature, not to a component.

HoloMap.API.set_feature_state("explorer", %{source: "parcels", id: 12}, %{hover: true})
HoloMap.API.remove_feature_state("explorer", %{source: "parcels", id: 12}, "hover")
HoloMap.API.remove_feature_state("explorer", %{source: "parcels", id: 12})

Add :source_layer for a vector source. Every feature needs an id, which GeoJSON features often lack. Set generate_id on the source, or point promote_id at a property that is already unique. There is a worked example in Events.

Anything else

HoloMap.Runtime.call/3 reaches any method on the underlying maplibregl.Map:

HoloMap.Runtime.call("explorer", "setRenderWorldCopies", [false])

It queues against style readiness like everything else, but it does not normalise its arguments. They are passed to MapLibre as given.

From JavaScript

A MapLibre plugin (a draw control, a geocoder, a deck.gl overlay) needs the raw map object, and Hologram's interop cannot hand one to a third-party script. Every booted map is published on window:

window.HoloMap.map("explorer")      // the maplibregl.Map, or null
window.HoloMap.cids()               // every live map's cid
window.HoloMap.destroy("explorer")  // release its WebGL context

Prefer HoloMap.API from Elixir: it queues correctly against the style's readiness, which a raw handle does not.