Route playback
SqsMap now owns route playback in the shared map core. Browser main thread, strict Worker, desktop scripts and independent native QuickJS use the same map.sqs.animations.route / map.sqs.playbacks entry points. Sources and layers continue to use MapLibre-style addSource, getSource and addLayer; route playback is a SqsMap extension. scenePlayback and sceneRoutePlayback are true. Shared line animation and style tracks are available: sceneLinePlayback and sceneTracks are true.
Copyable script
// @gis-api 0.5-session
// @permissions ["project.read", "map.write"]
import { map } from 'sqsmap';
map.addSource('path', { type: 'geojson', data: {
type: 'LineString', coordinates: [[116.39, 39.90], [116.41, 39.90]]
}});
map.addSource('vehicle', { type: 'geojson', data: {
type: 'Point', coordinates: [116.39, 39.90]
}});
map.addLayer({ id: 'vehicle', type: 'circle', source: 'vehicle',
paint: { 'circle-color': '#e24d42', 'circle-radius': 8 } });
const scene = await map.sqs.scenes.create({ id: 'trip' });
const source = await map.getSource('path');
const target = await map.getSource('vehicle');
await scene.attachSource(target);
const playback = await map.sqs.animations.route({
id: 'drive', source, target, duration: 6000
}, { owner: scene });
const subscription = map.on('sqs.playbackchange', event => {
console.log(event.reason, event.playback.state, event.playback.position);
});
await scene.start();
const completed = await playback.finished();
console.log(completed.position);
subscription.unsubscribe();
Pending completion needs a real persistent session, provided by the desktop and browser script hosts. Independent native integrations use sqsm_map_script_session_start and keep calling sqsm_map_script_session_pump_json, while polling public map events. One-shot sqsm_map_script_run_json explicitly rejects pending completion with CAPABILITY_UNAVAILABLE; an already completed round settles immediately. Hosts may also control and poll playback through C JSON directly.
The complete HTML/CSS/JavaScript example includes map initialization, styling and every play, pause, stop and progress-slider handler. Save it as HTML to load the public website SDK; change its SDK URL when hosting your own matching distribution. It uses only generated data, labeled Synthetic example data.
Controls and completion
map.sqs.playbacks.get(id) returns an instance-bound handle; list() returns immutable snapshots; remove(playback) releases a controller. Handles provide getState(), play(), pause(), stop(), seek(timeMs) and finished(). Pure reads observe the last committed state without advancing animation or write watermarks. finished() means the endpoint's target data and display cache were accepted; use map.sqs.flush({ render: true }) to require a presented frame.
A draft controller is ready and preparation leaves target coordinates unchanged; snapshot position previews the route at positionMs. Scene start/resume starts ready controllers. Owner pause freezes the last position; resume does not catch up paused time. Manual pause is independent and is not undone by owner resume. Playback stop cancels its current wait; play in a running scene can replay and changes epoch, cancelling old-round waits. Infinite loop never completes naturally: stop, failure, removal or destruction ends a pending wait.
Owner stop cancels unfinished controllers and hides attached targets while preserving already completed finite rounds. A stopped shared scene cannot restart; create a new scene or replay within a still-running owner. Owner remove retires controllers but retains explicitly created sources, with the existing static-source display behavior after detachment. Script VM exit does not delete map-owned playback. Map destruction releases waits and resources.
Data, coordinates and limits
The source must contain one loaded, static, noncluster LineString; target must contain one Point. Geometry, Feature or a single-Feature FeatureCollection are accepted. Use current {id, sourceInstanceId} identities or getSource handles. Explicitly attach the target to the same owner before preparation. A target has at most one controller; remove it before preparing a replacement. Multiple lines or points, 3D routes and polar input explicitly fail.
Duration and offsets are exclusive, in milliseconds. Duration defaults to5000 and ranges from1 to86400000, allocating time by Web Mercator planar distance. Offsets are strictly increasing per-vertex times starting at0 and preserve dwell at repeated points. Delay is an initial delay. Input uses WGS84 with latitude limited to±85.0511287798066. Adjacent vertices take the short dateline path and positions retain continuous longitude:179 to−179 passes180 and ends181. GeoJSON accepts explicit world-copy longitude (absolute value≤1000000 degrees) without silently wrapping to another world, matching shared project/unproject. HeadingRadians uses the shared planar route direction and dwell/turn rules.
There are at most64 controllers per map; the128-resource scene budget includes static sources, receivers and controllers. Routes support at most100000 vertices; control/list/event JSON is64KiB and history holds at most128 events. Finished also follows host subscription/wait budgets. Recreated identities invalidate old handles with STALE_HANDLE. Preparation captures source instances and data revisions. User source edits/removal or ownership changes fail the old controller at the next sample or control, retaining new user data.
Target geometry, all display caches and foreign output prepare successfully before one commit. Output, resource-budget or cache failures do not publish a candidate pose or completion. Hosts must consume events to release pressure; resource pressure can be retried. sqs.playbackchange contains bounded historical preparation, control, completion, failure and ownership snapshots rather than per-frame notices. Observe continuous position with getState and sourcedata.
Native ABI and acceptance
C uses the existing sqsm_map_scene_json {method,args} channel: animation.route and playback.get/list/getState/play/pause/stop/seek/remove, with {id,playbackInstanceId} identities and no new exported function. The header, native JavaScript declarations and complete map-owned Rust rendering example match the website SDK core commit.
Native capacity checks verify64 controllers completing together with128 discrete notices, without a new write watermark. Group controls also follow host event budgets; starting an empty owner before preparing controllers individually avoids a large initial notice burst. This environment records18 actual native software-Vulkan frames covering planar and dateline preparation, movement, pause, completion, hiding and removal. The same script runs through retained native QuickJS and the actual desktop ScriptService/MapView. Browser main and strict Worker each pass31 common-script checks, plus68 browser checks and28 actual frames. Mobile device hosts, external Windows/macOS machines, hardware GPU and formal releases require independent acceptance.
When playback is removed, a pending finished wait rejects with CANCELLED; direct reads or controls through an old handle still report STALE_HANDLE. Seeking to the endpoint while paused preserves pause; resuming accepts completion. Delay and vertex offsets are limited to one day.
An external edit to route or target data ends active playback with CONFLICT and preserves the new data. RESOURCE_LIMIT reports resource or notification budget exhaustion without publishing a partial result.
The shared script verification input runs on hosts supporting the same capability. The complete HTML unregisters listeners and destroys its map on pagehide.