Standard camera events
The common planar/spherical camera supports MapLibre-shaped immediate move, zoom, rotation, and pitch lifecycle events. Use them to refresh your own scale bar, zoom buttons, compass and overlays. The caller owns rendering and UI; sqs.viewchange remains available for compatibility.
// @gis-api 0.5-session
// @permissions ["project.read", "map.write"]
import {map} from 'sqsmap';
export const sessionMode = 'persistent';
map.on('move', event => {
const {camera, projection, viewport} = event.cameraState;
// camera: WGS84 center, zoom, bearing, padding; viewport uses CSS pixels.
// Send these values to your own UI through your host integration.
});
map.jumpTo({center:[110,20],zoom:4,bearing:30});
await map.sqs.flush({render:true});
export default {ready:true};
event.cameraState has the same structure as await map.sqs.cameraState(). Center is [lng,lat]; zoom uses the 512-pixel convention. Snapshots are deeply frozen and preserve each applied update rather than the last frame. target is the current script map; identity and safe-integer sequences belong to the host event domain. Unsubscribed, stopped and reset sessions do not receive old notifications.
Unchanged and failed commands emit no move notification. A move confirms applied camera state, which may not yet be drawn. Use await map.sqs.flush({render:true}) to require a new frame. Command confirmation does not wait for event callbacks: register map.once('move') before changing the camera and await it explicitly. Move does not imply network readiness, animation completion or idle.
The standalone SDK offers view.on('move', handler) and view.onCameraMove(handler), returning an unsubscribe function. Main-thread and strict Worker rendering use the same messages. Each SDK event supports 32 listeners, with a 128-item camera notification queue and conservative pre-mutation reservation; observer errors do not retroactively fail applied commands.
Desktop common camera operations and external planar gestures, native C polling, retained native QuickJS sessions and browser scripts are connected. Native one-shot execution still rejects subscriptions explicitly; use retained sessions. Spherical pitch events are described in Globe API. Other GIS CRSs and mobile hosts have separate acceptance. Globe animation events are connected; complete render/idle events remain separate work; this is not complete MapLibre Evented compatibility.
Complete shared script · Browser host · Commands and lifecycle. Public distribution requires deployment; use the matching preview origin for local acceptance.
Start and end of immediate commands
Shared events are movestart/move/moveend, zoomstart/zoom/zoomend and rotatestart/rotate/rotateend. Each camera command uses the following order, including only channels that actually changed:
movestart → zoomstart? → rotatestart? → move → zoom? → rotate?
→ zoomend? → rotateend? → moveend
Start event.cameraState contains the authoritative snapshot before that update; all other snapshots contain its applied result. Both are deeply frozen. Consecutive commands preserve their individual histories rather than substituting future state. Unchanged, failed and read-only calls emit no lifecycle notifications. Actual center, padding, viewport or projection changes also produce a movement cycle; zoom/bearing changes alone enable their corresponding channels.
Asynchronous start callbacks are not synchronous pre-mutation hooks. A separate camera query inside a callback may already see a later update; use the event snapshot. Every immediate command forms its own cycle. External desktop gestures describe changes observed each frame, not the beginning/end of a whole drag, inertia or animation. Moveend ends the immediate notification cycle and does not promise network readiness, rendering, animation completion or idle.
// @gis-api 0.5-session
// @permissions ["project.read", "map.write"]
import {map} from 'sqsmap';
if (!map.sqs.events().includes('moveend')) throw Error('Camera lifecycle is unavailable in this host');
const ended = map.once('moveend');
map.jumpTo({center:[110,20],zoom:4,bearing:30});
await map.sqs.flush({render:true});
const event = await ended;
export default {camera:event.cameraState};
Standalone SDK callers use view.on('zoomend',handler) or view.onCameraLifecycle((type,event)=>{}), which subscribes to all twelve types and returns an idempotent unsubscribe function. SDK budgets allow 32 listeners per event; script sessions allow 32 in total. The SDK occupies its 128-item queue by actual notification count and conservatively reserves up to 13 before mutation. Native public queues allow 128 events and reserve up to 13 including compatibility notices; C hosts must poll continuously. Observer errors do not undo applied changes, and subscriber overflow is explicit rather than silent loss of discrete events.
Browser support requires capabilities.cameraLifecycle and the actual helper. C callers first check SQSM_MAP_CAMERA_LIFECYCLE (512). Scripts inspect map.sqs.events(); old hosts and native one-shot execution do not promise these subscriptions. Removing UI, stopping scripts and resetting hosts must unsubscribe; old-domain events never enter a new session.
Complete common lifecycle script · Complete C example · Caller-owned tools.
Retained native execution and the browser runner deliver already queued event envelopes before resuming scripts awaiting the corresponding command. Listeners registered after initialization do not receive earlier initialization events. Asynchronous callbacks are not awaited and may request further map operations. Native pumping advances at most one host request per turn; max_events bounds each event batch without changing order.
Native independent subscriptions accept 1–64 event kinds while retaining subscription-count, event-count, and byte budgets. The limit of 32 user listeners is unchanged.
Standalone SDK canonical source/layer names, aliases and observer policy: event names.