SqsMap JavaScript script guide
Contract version: 0.5-session, retaining compatibility with 0.4-p2, 0.3-p1, and 0.2-host. This guide describes scripts run by a SqsMap host. See the generated API reference, shared map facade, and TypeScript declarations. Native-library scripts use a smaller, separately documented native declaration.
One reference for browser and desktop scripts
Browser and desktop scripts share this guide and API reference. Common interfaces are documented once. Platform annotations describe differences in availability, parameters, or behavior; check the interface reference and the current host capabilities.
Desktop scripts run in the Ctrl+F12 console. Browser scripts run against the example map in the Examples page console, separately from the web workbench. Browsers cannot access arbitrary local filesystem paths, and desktop native GIS capabilities are not automatically available to browser scripts.
| Difference | Desktop | Browser scripts |
|---|---|---|
| Local paths and workspaces | The debug console accepts local paths and a working directory; AI writes and edits retain confirmation | Arbitrary local paths are unavailable; map.open and map.workspace.save/load are not connected in the browser script host |
| Data and analysis | Connected native GIS interfaces are available | gis.data.fromGeoJSON and data.info are connected; file readers, SQL, analysis and export in the web workbench do not imply equivalent script support |
| Map language | map.setLocale/getLocale are not connected | These methods are available on the compatibility map imported from gis |
| Map capabilities and events | Check runtime capabilities and event lists | Use the same queries and script syntax; check the active host |
Use await map.sqs.getCapabilities() for the shared map's runtime capabilities. Platform labels alone do not establish complete support.
Choose an entry point
| Entry point | Behavior |
|---|---|
| Desktop debug console | Open with Ctrl+F12, paste a script, then Run. Stop interrupts the current run. Paths may be absolute; relative paths use the configured working directory. |
| Website Examples | Browser Worker executes scripts against the current map. Check host capabilities; local filesystem paths and GDAL operations are unavailable. |
| AI assistant | Uses the same application script contract; high-impact edits and file exports still require host confirmation. |
| Native library | Optional QuickJS-NG module provides its explicitly documented map subset; application GIS APIs are not automatically bundled. |
| Mobile | Shared contract is the target. Registry platform labels do not establish a working mobile host or real-device acceptance. |
These development documents do not claim that a package already published under an older version includes subsequent source changes. Consult the matching developer API status.
Shared map scripts
Prefer sqsmap for the shared map facade. It follows the frozen MapLibre 6.13.0 event calling conventions: on returns a subscription, off returns the map, and once returns the map with a listener or a Promise without one. Only connected methods and event types are available.
// @gis-api 0.5-session
// @permissions []
import { map } from 'sqsmap';
export const sessionMode = 'persistent';
await map.sqs.ready();
const types = map.sqs.events();
let changes = 0;
if (types.includes('sqs.viewchange')) {
map.on('sqs.viewchange', () => { changes++; });
}
export default { initialized: true, availableEvents: types };
Top-level completion marks initialization. The explicit persistent session continues handling events until stopped. A one-shot script, the default, releases its interpreter and listeners at completion. Stop/restart/map destruction must clean pending waits; already applied map changes remain. ready() confirms host connection, not network loading or idle. Session documentation lists actual connected events; map script design separates planned methods from executable ones.
For a cancellable listener, retain the subscription returned by on and call subscription.unsubscribe(). off(type, fn) requires the same function identity. Unsupported layer-selector overloads reject until the host connects them. Command confirmation distinguishes submitted, applied, rendered, resource-ready, and idle states.
Shared browser and desktop example
This inline demonstration needs no local file. Run once per map instance; before repeating, remove the named layer and source or use the console cleanup action. Flush confirms application, not network or animation completion.
// @gis-api 0.5-session
// @permissions ["project.read", "map.write"]
import { map } from 'sqsmap';
map.addSource('guide-point', {
type: 'geojson',
data: { type: 'Feature', id: 1, properties: {},
geometry: { type: 'Point', coordinates: [116.4, 39.9] } }
});
map.addLayer({ id: 'guide-point', type: 'circle', source: 'guide-point',
paint: { 'circle-radius': 8, 'circle-color': '#2563eb' } });
map.jumpTo({ center: [116.4, 39.9], zoom: 8 });
await map.sqs.flush();
export default await map.getCenter();
Existing GIS and compatibility scripts
The existing gis module exports gis, its compatibility map, expr, and sleep. The new sqsmap module re-exports gis, expr, and sleep but provides a different shared map facade. Use an explicit alias when a script needs existing GIS display calls:
// @gis-api 0.5-session
// @permissions ["project.read", "map.write"]
import { gis, map as legacyMap } from 'gis';
const data = await gis.data.fromGeoJSON({
type: 'FeatureCollection',
features: [{ type: 'Feature', properties: {}, geometry: { type: 'Point', coordinates: [116.4, 39.9] } }]
});
const source = await legacyMap.sources.add('example-source', { type: 'dataset', data });
const layer = await legacyMap.layers.add({ id: 'example-points', type: 'circle', source });
export default { layer };
This is synthetic demonstration data, not a substitute for an actual user's data. Source/layer host coverage is still being expanded; use the matching capability reference before copying the example to a different host. A familiar name or a declared platform is insufficient evidence of complete support.
Place both declarations within the first 16 lines. Scripts are ES modules with top-level await; only gis and sqsmap can be imported. Return results with top-level export default, not top-level return or an export inside a function. There are no standalone data, query, or analysis module exports: use gis.data, gis.query, and gis.analysis.
Default budgets are 128 KiB source, 32 MiB interpreter memory, 64 KiB results, and 60 seconds of one-shot/initialization time. Persistent callback turns have separate bounded budgets. Avoid infinite loops, unbounded listeners, and huge serialized results.
Permissions and confirmation
| Permission | Scope |
|---|---|
| project.read | Data, fields, queries, and map state |
| map.write | Map sources, layers, styles, camera, images, and scene drafts |
| analysis.execute | Analysis, topology checking, and freezing live snapshots |
| data.edit | Attribute/geometry edits and undo/redo |
| export.write | Exporting results; AI-generated exports require confirmation |
| scene.run | Starting scenes, animations, and live collectors |
Up to eight declarations are accepted. Missing permissions reject with PERMISSION_DENIED. Online sources, styles, and live connections do not need an additional network authorization. User-run debug scripts retain their confirmation exemption; AI-generated high-impact changes still use host confirmation plans. Export entitlement remains a host/account condition.
Await operations before reading results
Existing asynchronous GIS host calls return Operation<T>. Awaiting one returns T directly:
const info = await gis.data.info(data);
const operation = gis.data.fields(data, { limit: 20 });
const page = await operation;
Do not read op.items before awaiting, or pass an unfinished operation where a completed reference is required. These mistakes reject with INVALID_ARGUMENT. op.result() returns the summary wrapper when called before await. op.cancel() requests cancellation; op.onProgress(fn) returns a cancellable subscription. Synchronous expression builders, API/capability helpers, and event-subscription methods are exceptions; they are not all Operations.
References, revisions, and pagination
Large geometry and attributes remain in native/host storage. Scripts use opaque references such as DatasetRef, RasterRef, TableRef, FeatureSetRef, MapLayerRef, SceneRef, and PlaybackRef. Do not pass a map display reference to a data-analysis API that expects a dataset or selection. For a whole display dataset, acquire a feature set with gis.query.select(layer, { scope: 'all' }); use the required conversion for subsequent display-source registration.
References are scoped to their owning task/session and source revision. Reacquire existing display layers in a new script with map.layers.get(idOrName). Dispose temporary references when no longer needed. Dataset and query changes invalidate old cursors and revision-bound selections, returning STALE_HANDLE.
Pages contain items and optional nextCursor; the default limit is 20 and the maximum is 100. Keep the same query and page limit while continuing. Table rows contain values; they are not flattened row objects.
let cursor;
do {
const page = await gis.query.rows(data, { limit: 100, cursor });
const sample = page.items.map(row => row.values);
// Consume a bounded sample or retain a result reference.
cursor = page.nextCursor;
} while (cursor);
Change plans
High-impact edits and file exports synchronously return a ChangePlan, which is not thenable. Preparing a plan is not application success. Call preview(), then apply() under the host's confirmation policy, or export the plan so the host can review and apply it. Confirmation rejection must stop the change; do not create another script to bypass it. Do not pass invented confirmed:true options.
const plan = gis.export.dataset(data, { path: 'result.geojson' });
const preview = await plan.preview();
export default { plan, preview };
Coordinates, units, and real data
Geographic coordinates use WGS84 [longitude, latitude]; four-number bounds are [minX, minY, maxX, maxY]. Logical pixels are distinct from physical pixels. Existing camera interfaces document their CRS and resolution; standard zoom follows the frozen 512-pixel tile convention where supported.
Area and distance require an explicit suitable projected measurement CRS and units. Degrees are not meters. Raster windows use source-grid pixel indices; target raster bounds use the target CRS. Query bandInfo before choosing raw or physical values and respect NoData masks. Zero is not automatically missing data. Read field pages and dictionaries before interpreting domain codes.
Use gis.places.search/get for actual place centers and bounds, and gis.places.parseCoordinate for user-supplied coordinate text. Keep multiple ambiguous candidates for user selection rather than choosing the first silently.
Host portability
Common scripts use only the built-in modules. Do not depend on browser DOM, fetch, WebSocket, console, setTimeout, local absolute paths, Intl, locale-dependent parsing, or stack-message text. Request host resources through supported APIs and check gis.capabilities.get() rather than identifying the host by its runtime string. FileRef objects from gis.files.pick can represent picker-provided resources; cancellation returns an empty list. Native-only data processing remains unavailable until an equivalent host connects it.
Explicitly host-owned scenes persist after scripts exit. Draft scenes do not display or play until started. Playback completion differs from preparation; stopping, replacement, or user camera movement can cancel it. Infinite-loop playback cannot complete naturally. Live data must be frozen before analysis. Scene, playback, image, terrain, and point-cloud limits are documented in their generated reference and platform guide.
Errors and discovery
Check error.code and error.message, with optional public argumentApi, argumentPath, and details. Stable categories include INVALID_ARGUMENT, NOT_FOUND, STALE_HANDLE, CONFLICT, CAPABILITY_UNAVAILABLE, RESOURCE_LIMIT, CANCELLED, IO_ERROR, and ENGINE_ERROR. Never parse localized message text as an error protocol.
The reference lists every registered API and signature. The method examples document inputs, units, preconditions, parameter repairs, and boundaries between method knowledge and executable capabilities. Type declarations, web declarations, API index, and parameter schemas come from the same registry. Platform declarations remain separate from actual host support and device verification.
The shared facade adds map.sqs.flush({render,since}), returning instance identity, captured watermarks, and actual camera/frame receipts. Use a watermark returned by the same entry point; application-host request sequences are distinct from SDK/C backend command sequences. Older npm SDKs reject confirmation as unavailable. See commands and lifecycle for 2D raw-camera and device-acceptance limits.
Shared style replacement (13.3)
// @gis-api 0.5-session
// @permissions ["project.read", "map.write"]
import { map } from 'sqsmap';
map.setStyle('https://tiles.openfreemap.org/styles/bright', { diff: false });
await map.sqs.flush();
export default await map.getProjection();
setStyle returns the map and chains with camera writes. Authoritative queries wait for earlier shared writes. setStyle(null) clears the style basemap. HTTP(S) URLs and full replacement are connected; full replacement is the default. diff:true and inline styles explicitly report capability unavailable. Unknown options and invalid URLs report invalid arguments without modifying the existing basemap.
Desktop and web hosts with a matching SDK implement this facade. Standalone native modules support the same syntax. Desktop style loading currently requires an empty or WebMercator business view; other GIS coordinate systems reject explicitly. The mobile script host remains under development rather than a permanent platform difference. See native resources and browser requests. Older npm SDKs explicitly reject the missing entry. Command confirmation does not mean downloads are complete.
See the shared GeoJSON source guide.
See shared style layers for circle/line/fill, shared-source updates and layer events.
See the website query guide for source queries, paging and revision checks.
The query guide covers screen point/box queries, source-query differences, holes, layer order and result budgets.
Feature state, selection/hover and budgets
GeoJSON URLs: use map.addSource(id,{type:"geojson",data:absoluteHttpUrl}), source.setData(url) and map.sqs.getSourceInfo(id). Confirm loading with sourcedataloading/sourcedata/sourcedataabort/error; see the website guide.
Anchored navigation (common planar camera)
zoomTo/rotateTo/panTo/panBy/resetNorthPitch are connected; zoomIn/zoomOut/resetNorth accept around or offset. Mutations chain and are confirmed with await map.sqs.flush(). Geography uses WGS84 and screen units use logical pixels. Zoom and rotation can preserve a chosen anchor; positive panBy offsets move content in the opposite direction. Relative zoom stops at the actual limits; zoomTo provides custom steps. getRoll returns zero and setRoll currently accepts only zero. See anchored navigation for supported options and immediate/animation boundaries.
Shared camera animation
easeTo/flyTo/stop and authoritative motion state are connected to the shared planar camera. flush does not imply completion; register moveend first and inspect animation.status. Options, native clock/event pumping and reduced-motion policy are documented in camera animation.
Shared scenes and receiver ownership
Create a draft with map.sqs.scenes.create({id,name?}); get(id) returns a current handle or undefined and list() returns immutable small snapshots. A handle has id/sceneInstanceId and awaited getState/start/pause/resume/stop. remove(scene) stops owned receivers and releases the scene identity while preserving source data. connectLive accepts owner:scene; the bridge extracts its identity. Rebuilding an id or passing a foreign map identity yields STALE_HANDLE.
Draft scenes start no network I/O. Scene pause and manual receiver pause remain independent; scene resume does not restart manually paused/stopped receivers. receiver.resume under a non-running owner yields CONFLICT. Stop is terminal. Maps own scenes/connections after script exit. Observe sqs.scenechange with reason and immutable historical scene, and owner/ownerState on source events. Confirmed control does not mean data is ready.
await map.sqs.getCapabilities() exposes implemented capabilities: sceneLifecycle/sceneReceiverOwnership are true; sceneStaticResources/scenePlayback/sceneRoutePlayback are true; sceneLinePlayback/sceneTracks are true. Limits are16 scenes and8 active/paused receivers per map including drafts; output/events/control are bounded and failed group controls change no receivers. Legacy desktop map.scenes keeps its existing compatibility scope. See shared scenes.
See route playback for shared controls, completion and native-host differences. Public types include Playback, RoutePlaybackOptions, RoutePlaybackSnapshot and PlaybackChangeEvent; the script hosts and browser SDK share their semantics.
Common resource request configuration
Use await map.sqs.resources.configure / getConfiguration / plan. Browser and desktop hosts execute the policy; native libraries leave HTTP to the external polling host. Configuration fully replaces previous settings and affects future dispatches. sqs.resourceschange provides a deeply immutable historical snapshot. The standalone Android native host executes HTTP and WS/WSS, while mobile GIS application/iOS integration and device acceptance remain pending. See the shared facade and resource policies.
Spherical shared circle/line/fill use the same addSource/addLayer/moveLayer/setPaintProperty/setFilter/queryRenderedFeatures calls and original-coordinate hits. See spherical surfaces for the precise scope, common script and complete HTML. Full 3D occlusion remains pending.
Current shared text, media and spatial styles
Named common symbol layers support joint icons/text, independent opacity and collision options, and icon-optional/text-optional dependencies. Actual external PBF glyph services and ordered text-font stacks are connected. Text wrapping keeps fitting words intact, with character fallback for oversized tokens and CJK. Complex shaping, bidirectional text and formatted runs remain separate work. See joint symbols, glyph services and typography.
Raster brightness, hue, saturation, contrast and sampling are shared across flat/pitched/globe/terrain views. Registered image/video/canvas media can drape over terrain. Circle strokes use independent opacity and an outer radius in flat/pitched/globe views. Dynamic line-width works with the supported two-entry constant dash multipliers; complete3D dash behavior remains pending. See raster material, terrain media, circle strokes and dynamic dashes.
Numeric and color conversions try candidates in order and short-circuit after success. Spatial within and distance work in shared filters, queries and paint using retained WGS84 geometry. Within tests strict point/line containment, excluding edges and holes. Distance returns meters using spherical short arcs and the documented polygon-containment model; use GIS analysis for strict measurement. Budgets and unsupported combinations reject explicitly. See conversions, within and distance.
Globe cameraForBounds/fitBounds, local scaleAt and getBounds are connected. getBounds returns an approximate ground envelope including horizon, poles and continuous date-line longitude. Globe roll is connected; persistent spherical padding is connected. See globe fitting, scale and visible bounds.
Shared global state uses chainable map.setGlobalStateProperty(name,value) and asynchronous map.getGlobalState(), with global-state expressions and inline state defaults. null resets/removes a key; style replacement resets state. Cluster aggregation and legacy CPU attribute painting reject it. See the website global-state guide.
The shared semiliteral expression evaluates array elements with attribute, lexical and state dependencies. literal preserves nested arrays unchanged. See the website expressions-inside-arrays guide.
map.sqs.viewModeState() distinguishes 2d/3d dimension, projection, pitch and terrain. Caller buttons use existing setPitch/easeTo and camera events, explicit 45/0 targets and flush confirmation. See caller-owned 2D/3D switches.
Shared symbol icon-size/icon-rotate accept numeric expressions with matching render/collision/query footprints. Layout rejects feature-state; dynamic zero size hides the icon. See icon layout expressions.
text-offset accepts x,y constants or array expressions returning two numbers, em units±256, with matching rendering/collision/query footprints. See text offset expressions.
Standalone loadImage accepts host-owned hierarchical custom URLs. Core capability does not add handlers to the desktop HTTP downloader. See image protocol URLs.
Common setStyle accepts validated custom hierarchical URLs; see custom style protocols for handlers, relative resources and host conditions.
Common setGlyphs accepts host-owned custom hierarchical templates; glyph arrival/repaint remains asynchronous. Seecustom glyph protocols.
Use synchronous map.listens(type) for regular/once/delegated listeners owned by this facade; tables are independent. Seeevent listener queries.
Common join now fixes nested array/object conversion while preserving split and budgets; seejoin value conversion.
Globe roll
setRoll/getRoll and jumpTo/easeTo/flyTo share physical optical-axis rotation. Persistent events add rollstart/roll/rollend. See globe roll.
Shared incremental style updates (SQSAPI-08.66)
In the common sqsmap module, map.setStyle(style, options) attempts incremental updates when diff is omitted or true. Use false for full replacement; null clears. Compatible sources retain identity, explicit feature state, cache and in-flight requests. Compatible GeoJSON data changes follow existing setData state rules. Source type or structural changes replace sources and invalidate previous handles; changing glyphs/sprite roots falls back to full replacement. Invalid candidates preserve the current map. URL replies compare the live state at arrival. The default changes from full replacement to incremental updates; pass {diff:false} to keep the earlier behavior.
Flush confirms writes. Inspect loading state for resources and fade completion. This describes the development implementation; refer to the website style-diff guide and SDK inventory for delivered acceptance.