Skip to main content

Shared map facade

Generated from the same registry as the Chinese reference. Contract version: 0.5-session. Edit the registry and reviewed English catalog, then rerun script_contract; do not edit this file. See the script guide.

Import map from sqsmap. Current application/web contract: 0.5-session. This page documents the connected facade, not the full planned camera/source/style surface.

map.on(type, listener): Subscription
map.off(type, listener): Map
map.once(type, listener): Map
map.once(type): Promise<MapEvent>
map.sqs.ready(): Promise<Map>
map.sqs.flush(options?: FlushOptions): Promise<MapCommitResult>
map.sqs.events(): readonly AvailableMapEvent[]
map.sqs.disposeSession(): Promise<void>

on returns a cancellable subscription, following the frozen MapLibre 6.13.0 reference. Listener-form once and off return the map. ready confirms host connection, not network readiness or idle. flush confirms earlier map operations; render: true waits for actual rendering. Later writes do not extend the captured wait. Application-host watermarks count map requests; SDK/native-library watermarks count backend commands. They are separate ordering domains. The older published npm SDK rejects confirmation as unavailable rather than returning a fabricated result. Call events() to check actual available events; unsupported event types or unsupported delegated event types reject explicitly.

Declare export const sessionMode = 'persistent' to keep listeners after initialization. Stop the runner or dispose its session to cancel listeners and pending waits. Stopping preserves already applied map changes. The standalone native library has its own matching declaration and coverage, with additional native-view convenience calls; do not assume its subset includes application GIS methods.

See persistent sessions, commands and lifecycle, and map-script design.

Shared planar camera (06.3)

The desktop console and local browser SDK with matching WASM use the same calls as standalone native scripts. Authoritative camera queries return real promises supporting await, catch, and finally. A query waits for shared camera writes submitted before that query. Writes return map; confirm them with map.sqs.flush(). An unobserved write failure still fails the script rather than disappearing when no barrier is awaited.

// @gis-api 0.5-session
// @permissions ["project.read", "map.write"]
import { map } from 'sqsmap';
map.jumpTo({ center: [116.4, 39.9], zoom: 8, bearing: 30, padding: { left: 40 } });
const applied = await map.sqs.flush();
const center = await map.getCenter();
const screen = await map.project(center);
const position = await map.unproject(screen);
export default { center, screen, position, applied };

Queries: getCenter/getZoom/getBearing/getPitch/getRoll/getPadding/getBounds/getProjection/project/unproject/cameraForBounds, plus sqs.cameraState. Writes: jumpTo/fitBounds/setCenter/setZoom/setBearing/setPitch/setRoll/setPadding/resetNorth/zoomIn/zoomOut/zoomTo/rotateTo/panTo/panBy/resetNorthPitch. Coordinates are WGS84, longitude first; screen positions and padding use logical pixels; zoom uses a 512-pixel world. See camera API.

Shared planar views support ease/flight animations. Spherical views support physical pitch, great-circle motion and anchored navigation. Nonzero planar pitch, nonzero roll, spherical bounds/nonzero padding, unknown options and unready maps reject explicitly. Desktop GIS-specific CRSs retain the legacy GIS APIs. The empty desktop basemap cannot yet move its padded raw focus beyond the north/south Mercator limits. The mobile host is not connected or marked supported. Website development distribution uses SDK/WASM matching the core commit. External older packages return CAPABILITY_UNAVAILABLE when these methods are absent. Browser GPU/Worker, desktop GUI, mobile devices, and external Windows/macOS acceptance are tracked separately.

Anchored navigation (12.2)

zoomTo/rotateTo and zoomIn/zoomOut/resetNorth accept around (preserve a geographic point on screen) or offset (preserve the location under an offset screen point); the two anchors are mutually exclusive. panTo positions the supplied location at the padded center plus offset. Positive panBy moves content oppositely using logical pixels. resetNorthPitch restores north and zero pitch. Relative zoom stops at actual limits; out-of-range zoomTo rejects. getRoll is zero and setRoll only accepts zero. Navigation supports duration/animate/easing/essential; duration:0 applies immediately. See anchored navigation for all supported options.

Shared animation: easeTo/flyTo/stop, isEasing/isMoving/isZooming/isRotating, sqs.animationState and sqs.setReducedMotion. flush confirms admission, not animation completion. Register once("moveend") before starting and inspect animation.status. See camera animation.

Shared default gesture switches

await map.dragPan.enable()/disable()/isEnabled(), await map.scrollZoom.enable()/disable()/isEnabled() and await map.doubleClickZoom.enable()/disable()/isEnabled() control implemented default navigation. await map.sqs.gesturesState() returns an independent immutable snapshot. Disabling navigation preserves pointer events and programmatic camera commands. Inertia/rate/anchor options and extra arguments are rejected. Unlike the similarly named synchronous MapLibre methods, shared cross-thread queries and writes require await. Keyboard/box selection/rotation/two-finger input, are not published in this phase. Native shells own physical input and must honor this policy; an offscreen C map is not a built-in desktop widget. Identical portable script, full guide.

Policy changes: map.on('sqs.gestureschange', event => ...) exposes an immutable historical event.gestures snapshot. Subscribe, then query await map.sqs.gesturesState() to initialize your UI. Idempotent or failed writes emit nothing; there is no initial replay. Independent SDK users call view.onGesturesChange. Input activity is described below. Identical event script, complete HTML/CSS.

Recognized input activity: await map.dragPan.isActive(), scrollZoom.isActive() and doubleClickZoom.isActive() distinguish input from enabled policy and camera animation. A recognized held drag is active until release/cancel/capture loss or disable. Enabling does not resume it. Current instantaneous wheel/double-click handling is inactive after processing; no settling timer or inertia is invented. C shells report their own input lifecycle. New capability: gestureActivity/131072. Identical activity script, complete HTML/CSS.

Keyboard navigation uses await map.keyboard.enable()/disable()/isEnabled()/isActive(). On a focused planar canvas, arrows pan100 logical pixels and zoom keys change one level, or two with Shift. Editable fields keep their input. See keyboard guide for shortcuts and pending pitch and 3D input.

Keyboard arrow option: await map.keyboard.enableRotation()/disableRotation() controls all arrow panning and Shift-left/right15-degree bearing rotation, preserving zoom. Independent keyboardRotation survives overall keyboard disable/enable. Shift-up/down pitch remains pending. See keyboard navigation.

See shared perspective camera for perspective math and projection range normalization. Nonzero common planar pitch bindings remain active work; mathematical support alone does not certify that API.

Two-contact touch controls

Shared touchZoomRotate/touchPitch provide enable/disable/isEnabled/isActive; touchZoomRotate also provides enableRotation/disableRotation. Await calls. Policy includes six defaults and keyboardRotation/touchRotation. Browser main/Worker and Android source hosts recognize touch; C shells still own input. Release/cancel/disable/destruction end activity; re-enable starts with the next gesture. Seetouch navigation for options/anchors,0–65 degree pitch and device scope.

Android standalone map/script hosting is now implemented; mobile GIS integration/iOS and devices remain separate. Historical stage limits above should be read with the newer perspective and touch guides.

Shared style replacement (13.3)

// @gis-api 0.5-session
// @permissions ["project.read", "map.write"]
import { map } from 'sqsmap';
map.setStyle('https://map.sqsh.com/sdk/sqsmap-core/style-registry-assets/styles/style.json', { 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), custom-protocol URLs and the inline scope below are connected. Current matching shared SDK map.setStyle attempts an atomic diff when diff is omitted or true; false requests full replacement. See the style-diff guide for source compatibility, live URL comparisons and resource-root fallback. Desktop application integration must be checked separately from standalone native SDK acceptance. 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.

Attribution queries

Shared scripts use const credits = await map.sqs.getAttributions(). text is complete plain text; items retain source, instance and referencing-layer provenance. Reads do not move the camera, poll resources or change command/event sequences. Style-wide credit remains; enabled layers use inclusive minzoom and exclusive maxzoom, with unreferenced sources omitted. Opacity, feature filters and tile readiness do not remove credits.

The standalone SDK uses await view.getAttributions(); native libraries use sqsm_map_attributions_json (runtime bit8192, output may be freed after map destruction). Shared GeoJSON/style credits agree; host imagery, terrain and offline scope is covered in map sources and attribution. Mobile application script hosts still need integration.

Raw credits must be strings of at most4KiB UTF-8. HTML is stripped and entities decoded once; credits are no longer clipped to180characters. Complete JSON is limited to64KiB; RESOURCE_LIMIT preserves the map and previous snapshots. Returned snapshots are immutable historical data. Applications provide provider-required links. See the exact portable-attributions.js.

Attribution changes are connected: map.on("sqs.attributionschange", event => ...) receives a deeply frozen event.attributions snapshot. Pump/frame samples may coalesce; there is no initial replay, so listen before querying. Budget errors use context=attributions; recovery notifies again. Stop/destruction clean up; 128 pending notices/256KiB fail explicitly. Complete HTML/CSS and the same portable event input.

Vector style sources now support url or inline tiles, TMS, source zoom limits and bounds overrides. See tiled vector style sources. Shared named sources support GeoJSON, image/video/canvas, raster, MVT and raster-dem. See source guides, media and elevation and hillshade for supported properties and layer combinations. GeoJSON promoteId is connected; vector-tile promoteId and MLT remain unavailable. These are distinct capabilities.

Standard raster styles now have planar painting (styleRasterPlanarSources:true). Shared raster sources and planar/pitched/globe imagery lifecycle are connected in07.7/16; remaining raster properties and DEM continue in tasks. See raster style sources.

Standard imagery now supports the shared globe projection. Cross-host planar pitch and terrain draping remain active work. Tile fading uses raster-fade-duration, defaults to 300ms, and accepts 0 for immediate display. Complete HTML and a shared script are in the imagery guide.

Inline style transactions (08.4 in progress)

Version 8 inline objects are being connected to shared GeoJSON sources and circle/line/fill layers using the live-edit validators. All sources, layers and display plans must succeed before replacing the shared registry; failure preserves the map. Sources receive new instance identities, old requests are cancelled and stale responses cannot replace new data. Scene controllers remain map-owned; old bindings follow stale-identity handling. Inputs and snapshots are bounded to4MiB. Complete basemap/business registry unification, other style types, final cross-host acceptance of standard events and mobile hosting remain in progress; this scope is not complete MapLibre style support.

Style snapshots and standard events

await map.getStyle() returns the validated current style together with editable shared sources/layers, including name, metadata and current data. URL documents not yet admitted return CAPABILITY_UNAVAILABLE. Duplicate IDs between the basemap and shared registry return CONFLICT rather than losing one side. Snapshots are bounded to4MiB; shared GeoJSON snapshots can be imported again.

styledata reports style document state changes; style.load reports admission of the current document and its dependencies, not completion of every viewport tile or presentation. Validated inline documents notify styledata then style.load. URL admission first reports pending status and finishes within the same generation when dependencies are ready. Events contain dataType=style and deeply frozen styleState (generation, revision, loaded). Validation failures emit no event and do not advance a generation. No initial replay: subscribe before querying. Clearing the basemap preserves shared layers for compatibility; complete URL/shared registry unification remains in progress.

Shared type expressions (08.5)

Supports MapLibre-style boolean/number/string/object assertions with ordered candidates, array item-type/length assertions, typeof,properties and get/has with optional objects and string-name expressions. Syntax follows the official expression reference; the shared Rust evaluator is independently implemented without the MapLibre runtime.

map.setPaintProperty('places-layer','circle-color',
  ['case',['boolean',['feature-state','selected'],false],'#ff00ff','#0000ff']);
map.setPaintProperty('places-layer','circle-radius',
  ['number',['get','height',['object',['literal',{height:18}]]],6]);
await map.sqs.flush({render:true});

Assertions do not coerce numeric strings. They select the first candidate of the requested type and short-circuit later evaluation; failure uses existing paint defaults/warnings or explicit query-filter errors. array supports value,type/value,type/length/value forms with string/number/boolean item types and nonnegative integer lengths up to65536. Array typeof results include item type/length, e.g.array<number, 2>; mixed items use value. get returns null for missing keys,has distinguishes missing from present null,and invalid object inputs fail explicitly.

This describes JSON types; collator supports Unicode search comparisons for five locales; complete locale coverage, the full format/image value system and remaining expressions stay in subsequent tasks; text-field supports format text sections with font-scale/text-color, complete PBF text-font stacks, top/center/bottom vertical-align, registered RGBA image sections and case/coalesce/match/let/var combinations, described in formatted text, font stacks, section alignment and RGBA image sections. Images use pixelRatio dimensions and retain RGBA color multiplied by text-opacity; missing images are actual null, SDF sections reject and a top-level text-field image must be wrapped in format. Existing node/depth,source and display budgets apply; feature-state is paint-only. GeoJSON/shared MVT use identical semantics; setStyle(URL) basemaps retain their separate expression scope. The complete HTML and portable script include selection,ordering,views,queries,credits and disposal.

Variable scope (08.6)

Shared paint,filter and query expressions support let/var. Variables are visible in the let result; inner bindings shadow outer ones. Binding values resolve only enclosing-scope variables. Bindings evaluate on demand,so unused runtime errors do not execute. Names use nonempty ASCII letters/digits/underscores,up to256 bytes. Unbound names and invalid structures reject before mutation.

map.setPaintProperty('places-layer','circle-color',
  ['let','selected',['boolean',['feature-state','selected'],false],
   ['case',['var','selected'],'#ff00ff','#0000ff']]);
map.setPaintProperty('places-layer','circle-radius',
  ['let','large',18,['step',['zoom'],6,4,['var','large']]]);
await map.sqs.flush({render:true});

Camera zoom may drive a terminal step/interpolate inside outer let expressions; it cannot appear in binding values or other positions. Shared layers continue reevaluating actual zoom. Each evaluation permits8192 steps/64 levels; AST limits remain1024 nodes/32 levels. Budget failures use existing paint defaults/warnings; layer/query filters report RESOURCE_LIMIT and mutations leave no partial layer/reference.

The complete HTML and portable script include all buttons,credits and cleanup through native and real WASM main-thread/strict Worker. Implementation is independent against the public expression specification,without MapLibre code. Remaining operators and complete layer types continue in tasks.

Shared global state: map.setGlobalStateProperty / map.getGlobalState, inline state defaults and global-state expressions. Global state.

Shared icon-image supports availability-aware typed images, reported by commonIconImageExpressions=true. Missing image values are actual null for coalesce; plain strings and empty strings keep their existing semantics. Lexical/lazy scope and the8192-step budget are reused; RGBA/SDF parameters and queries remain unchanged. Only attempted names contribute missing notices, and native/WASM image candidates validate before commit. See icon image expressions. Patterns, downloads, URL-style expression domains and3D remain outside this capability.

Shared planar fill patterns

fill-pattern supports uniform and feature properties/get/feature-state RGBA selection, reported separately by commonFillPatternsUniform and commonFillPatternsPerFeature. Typed image/coalesce, lexical/lazy branches, integer zoom, and global state remain supported; fall back within the name for absent state. Missing images hide only affected features and report actual visible demand. Property/state/image candidates validate before commit and preserve old state/identity on failure; holes, original feature order, and world phase remain intact. Transitions, complete line-pattern combinations, 3D/globe, and per-pixel alpha picking remain unsupported. See fill patterns.

SDF fill patterns are reported by commonFillPatternsSdf. Register with sdf:true; image alpha stores distance (.5 boundary), fill-color is the foreground, and color alpha multiplies fill-opacity. Transparent foreground is excluded from geometry queries. RGBA behavior, holes/order/world phase remain preserved. See fill patterns. Transitions/complete line-pattern combinations/3D/globe/texture-alpha picking remain pending.

Shared planar solid lines accept explicit line-cap/line-join/line-miter-limit/line-round-limit: three caps/joins and finite0..16 limits, reported by commonLineTopologySolid. Expression support is commonLineTopologyLayoutExpressions: existing property/global-state/integer-zoom and type/lexical/lazy expressions resolve per feature; layout rejects feature-state and zoom requires top-level step/numeric interpolate (let wrapping accepted). Omitting them preserves legacy fast-line boundaries. Dash/gradient/animation/3D/globe/media combinations reject; query envelopes are conservative rather than pixel-exact. See solid line topology.

Shared fill and line feature draw order (08.55)

commonLayerSortKeys provides shared planar fill-sort-key / line-sort-key. After filtering, actual displayed features draw in stable ascending finite numeric-key order; larger values cover smaller ones, and ties including ±0 retain source order. Within a layer, queryRenderedFeatures follows actual topmost order. IDs, properties, filter indices and MVT deduplication stay intact. Keys support properties/global-state/type/lexical/lazy expressions. Sort-key layout rejects feature-state and interpolate; zoom requires top-level step (outer let allowed), uses floor(zoom), and needs explicit planar Web Mercator. Each expression allows 32 levels/1024 nodes/8192 steps; candidates allow 100000 displayed features/2000000 estimated nodes.

Solid-color and RGBA/SDF fills, ordinary/dashed lines and explicit solid-line topology are supported. Explicit topology with dashes and animation/gradient/3D/globe/media combinations still reject. Failure retains sources, global values, camera, frame and identities. See feature draw order for complete examples, the 24-check portable script and HTML. Device acceptance and formal release remain pending.

Shared solid line-offset: commonLineOffsetSolid, CSS pixels/default0, signed line travel sides and polygon outer/hole insets. Numeric/property/state/global/lexical/lazy/fractional zoom, absolute128 and actual-extrusion2048 budgets. Nonzero dash/gradient/animation/pitch3D/globe/media reject; zero retains legacy paths/limits. Candidate failure preserves source/state/global/camera/identities, with conservative actual-extrusion queries rather than pixel picking. See the line-offset guide and30-check portable script.

Shared planar solid-line translation

line-translate is a uniform two-number CSS-pixel paint vector, defaulting to 0,0. line-translate-anchor accepts map/viewport and defaults to map. Constants, global-state, explicit uniform object get/has, type/lexical/lazy and continuous-zoom expressions are supported; feature properties and feature-state are unsupported. Map anchors rotate with candidate bearing; viewport anchors preserve screen directions. Original components are limited to absolute 128 and final extrusion with width/caps/joins/offset to 2048 CSS pixels. Actual nonzero dash/gradient/animation/pitch3D/globe/media combinations reject; zero retains previous paths. Capability: commonLineTranslateSolid.

Queries clip to the actual viewport before inverse per-layer translation, preserving source/feature IDs, properties and sorting with conservative hit semantics. MVT coverage includes incoming neighbors and unknown feature size bounds even when cached samples exist. The full-map visible tile cap remains 64. Source/global/paint/camera/viewport candidates are checked before commit, retaining previous state and queues on failure.

Shared planar solid-line casing

line-gap-width is a finite nonnegative CSS number, defaulting to0, with property/state/global, type/lexical/lazy and fractional-zoom expressions. Positive gap uses outer width g+2w and side thickness w; zero width leaves no casing. Real transparency reveals lower layers without erasing them. Per-original-feature4x stencil avoids repeated alpha at corners and GeometryCollection crossings, preserving interleaved zero/positive-gap sorting. Capability:commonLineGapSolid.

w<=24, g<=128, one million combined inner/outer worst-case vertices (400 per input point) and2048 CSS-pixel actual offset/translate extrusion; combined two-million node/depth32/nodes1024/steps8192 budgets remain. Render targets include MSAA/stencil and three aligned readback buffers, checked conservatively at64 bytes per physical pixel with512 MiB; only nonzero gaps introduce this constraint. MVT includes unknown dynamic gap/width/offset/join envelopes even without translation;64-tile budget failures retain source/state/camera/queues. Queries remain conservative and may hit transparent gaps. Actual nonzero dash/gradient/animation/pitch3D/globe/media combinations reject.

Shared uniform planar RGBA line patterns

commonLinePatternsUniform: uniform per-layer image/coalesce/global/explicit object/lexical/lazy/integer-zoom selection preserves property/state/continuous-zoom width/opacity and filtering/sorting. The complete image height covers actual width; period=width×imageW/imageH. RGBA colors multiply opacity, transparent pixels reveal lower layers and line-color does not tint. Width0 paints nothing. Only default butt/stable miter≤2 is connected; nonzero offset/translate/gap, SDF/self-crossings/reversals/explicit topology/dash/gradient/animation/pitch3D/globe/media reject. See line patterns for dimensions, combined static/animation budgets, missing images, rollback and conservative queries. Check capability with the matching SDK version.

Shared planar solid-line distance softness

The core has implemented and pushed commonLineBlurSolid: line-blur accepts a finite nonnegative CSS value in 0..16, with default 0 retaining previous paths and budgets. Property/state/global, type, lexical/lazy and continuous-zoom expressions are supported. True outline distance attenuates inward; butt/square transverse ends only clip, square extension sides stay soft, and round ends soften radially. Real coverage union composites color/opacity once per original feature. IDs, filtering/sorting, all three caps/joins and stable offset/translate combinations remain preserved.

Unchanged resident and candidate blur channels share at most 256 drawable records, including zero-blur records in mixed layers. Each positive original-feature group allows 256 true outline edges. ROI physical pixels multiplied by soft edges allow 100 million comparisons; CPU scans/splitting/stability share two million. Actual vertices including ROI quads allow one million, combined extrusion including AA allows 2048 CSS pixels, targets plus geometry/outline/uniform resources share 512 MiB, and full-map MVT retains 64 tiles. Nonadjacent overlaps, self-crossings, unstable geometry and positive gap/line-pattern/dash/gradient/animation/pitch3D/globe/media combinations reject explicitly. Failed candidates retain previous state, display, queues and identities. Five CPU tests, one actual GPU case and the targeted WASM check passed. The same 27 script checks and actual pixels passed native/main/Worker acceptance. Matching development SDK 0.3.0-dev.3da09ace passed member size/SHA, Linux/Android/JNI and 22 recent portable-script checks. Formal release, devices and other combinations remain pending. See distance softness.

Shared per-feature RGBA line patterns

commonLinePatternsPerFeature extends one-layer line-pattern selection through actual filtered/stably sorted property/get/match values, typed image/coalesce, type/lexical/lazy, global and integer-zoom expressions. Source/ID/properties, true width/aspect period and opacity remain preserved. Paint feature-state selection is a SqsMap shared extension, without claiming that the MapLibre specification declares this combination. A/B/A materials draw as A/B/A, without regrouping across another material. Missing or fully transparent images hide only affected records; transparent stripes retain conservative geometry hits. Only attempted missing names emit notices.

Each layer accepts at most 4096 input materials and ordered records; identical W/H/RGBA materials deduplicate. Joint static/animation limits remain 32 layers/4096 paths/100000 actual generated vertices/256MiB and ten-million CSS distance per path. Each unique texture rounds upward to 64KiB pages and adds a 48-byte uniform/256-byte binding reserve; a shared 64-byte sampler, 64 bytes per ordered record and all vertices also count. Candidate 100000 displayed features/two-million estimated nodes and existing expression limits remain. Nonzero offset/translate/gap/blur, explicit topology, SDF/dash/gradient/animation, pitched/globe/media combinations still reject. MVT phase starts at 0 per decoded path; cross-tile merging is not implemented.

Three CPU cases, the same final actual GPU case and targeted WASM checks passed. The same 33-check contract and real blue/green/transparent pixels pass native/main/Worker. Numeric coalesce now skips baked absent-global literal-null inputs, preserving legal lazy fallback; initial logs are retained. Consult SDK acceptance for the matching development version. Mobile GIS/iOS/devices, external platforms, complete styles, formal release/deployment remain pending. See per-feature patterns.

Shared GeoJSON line gradients

commonLineGradients adds planar line-gradient for noncluster GeoJSON LineString/MultiLineString sources with explicit lineMetrics:true. Progress is original Mercator part distance in 0..1; a closed part keeps its 0/1 seam. Clipping/camera/DPR/offset/translate do not renormalize it. Direct progress input to step/interpolate/LAB/HCL, including legal let aliases, supports existing curves and uniform color/type/lexical/lazy/global expressions. Gradient property/state/zoom/spatial reads and other progress arithmetic/conditional jumps reject. Width/opacity retain property/state/global/continuouszoom; all caps/joins and stable offset/translate are connected. line-color and its alpha neither tint nor hide gradients.

Each layer allows 32 step cuts/33 bands and 257 RGBA8 color samples per nonzero interval. sRGB is reconstructed within the band and decoded to linear output; alpha is unchanged. A cut selects its right band without cross-step filtering. Quantization and nonlinear/LAB/HCL reconstruction errors exist; no universal 1/255 bound or MapLibre pixel identity is claimed. Candidate ramps share two-million estimated nodes. Pattern/gradient/animation jointly retain 32 layers/4096 paths/100000 actual vertices/256MiB and ten-million CSS length per path. Accounting includes 24-byte vertices, RGBA32Float color/metadata textures each rounding up to 64KiB, a 48-byte uniform/256-byte binding reserve and 64 bytes per path draw. A path allows 5000 original nodes; same-feature part triangle pairs allow two-million checks per candidate layer.

Same-feature overlapping coverage, self-crossing/reversed/unstable meshes reject; different features retain sorted compositing. Query bounds are conservative true topology; partial transparency can hit, full-transparent ramps or width/opacity0 cannot. Gap/blur, dash/pattern/animation, MVT/media, pitched3D/globe remain unsupported. Failed source/state/global/paint/camera/viewport/DPR/output/resource candidates preserve state/frame/queue/identity. Previous native effects gradient APIs remain separate.

Three CPU cases, the same final actual GPU and targeted WASM checks passed. The same 32-check contract and real smooth/step/original-clipped-position/multipart-progress/single-alpha pixels passed native/main/Worker; the initial WGSL reserved-local-name log is retained. Consult SDK acceptance for the matching development version. Mobile GIS/iOS/devices, external platforms, complete styles and formal release/deployment remain pending. See line progress and gradients.

Shared static circle styles

Capability commonCircleStyles connects circle-blur, circle-translate/anchor, circle-sort-key and circle-pitch-scale/alignment alongside existing fill and independent outer stroke. Blur accepts representable finitef32 values, using relative inward radial smoothstep; negative values add no extra falloff, AA remains, and no convolution is used. Blur retains property/feature-state/global/continuous zoom. Translation/enums use uniform evaluation; components allow±128 CSS pixels. Defaults are blur0, translation0,0/map anchor, map scale and viewport alignment.

Map alignment follows the map plane or local globe tangent; viewport alignment faces the screen. Map scale changes with camera distance; viewport scale keeps screen size. Four combinations share actual projective drawing/clipping/query footprints, near/far clipping, back-facing globe-center rejection and business-layer ordering without depth writes. Queries retain original source/ID and visible outer-stroke bounds; hollow interiors may hit, without per-pixel alpha picking. Finite circle-sort-key values draw in stable ascending order, retaining source order on ties. Layout rejects state/interpolate; top-level step (optionally let-wrapped) uses floor(zoom), including actual pitched Mercator/globe zoom. Older fill/line restrictions remain.

All circle candidates allow100000 records/two million new-parameter node estimates. Visible layers separately allow100000 filtered Point/MultiPoint original instances, including offscreen/transparent/zero-radius entries; drawable glyphs allow100000. Each glyph costs84 CPU plus240 GPU bytes for six vertices; each contiguous layer range adds64 bytes. Complete candidates plus unchanged resident circles share256MiB. Source/state/global/paint/layout/camera/viewport/DPR and tile coverage validate before publication, preserving old state/frame/queue/identities on failure. New-circle CSS sRGB colors always decode into linear for one composition; alpha is unchanged. Native SRGB/browser UNORM RGB storage differs; acceptance retains raw samples and normalizes RGB only, without claiming pixel identity over arbitrary older backgrounds. Complete transitions/terrain draping, mobile GIS/iOS/devices and formal release remain pending.

commonStyleRegistry identifies the supported shared object/URL registry subset; see Shared style registry and resources. URLs use the same registry validation as objects, with glyphs/sprites and known relative resource slots. The existing separate compatibility basemap entry remains available.

Incremental style defaults and state

Diff compares the live registry. Compatible sources retain identity, cache, pending requests and raster fade time; data updates follow existing setData state rules. Structural changes replace sources and invalidate old handles. Glyphs/sprite root changes fall back to full replacement. New global defaults retain explicit runtime overrides; null removes an override and restores the current default. Invalid candidates do not publish partial source, layer or event changes. Flush confirms writes; source resources and final map rendering have separate readiness states. Scripts relying on the earlier full replacement default should explicitly pass {diff:false}.

Shared planar composition capability

When a matching SDK reports commonPlanarLayerComposition:true, the shared Mercator view with zero pitch/roll and no terrain/extrusion combines media, backgrounds, labels, heat and supported static line/fill effects in complete layer order. This capability supersedes earlier media-combination exclusions only within that shared planar scope. Custom line animation, tilted/globe/terrain views and other unsupported material combinations retain their respective limits. Reordering and compatible diff preserve identities, caches, pending requests and fade clocks; material, geometry and texture budgets are validated before publication. See planar layer composition.

Shared line symbols (commonLineSymbols)

A matching development SDK returning commonLineSymbols=true supports symbol-placement=line/line-center for original GeoJSON/MVT line parts in the shared planar Mercator view (zero pitch/roll, no terrain/extrusion). symbol-spacing defaults to 250 CSS pixels; text-max-angle to 45 degrees; text-keep-upright/icon-keep-upright to true/false. map/auto follows road tangents, viewport lays out the whole label horizontally, and viewport-glyph places text centers along the curve while keeping glyph axes oriented to the viewport. Explicit multiline labels reject. Existing PBF, RGBA/SDF, format, image fitting, sorting, collision and optional-group semantics apply; text that cannot fit follows text-optional and cannot leave an icon when that text is required.

Actual glyph quads and original feature/part/source identities drive placement, collision and queries. Generated anchors never modify source data. Invalid resolved symbol paint in a line candidate explicitly rejects and retains the previous frame; existing point fallback semantics remain. Legal glyph fallback keeps its existing contract. An asynchronous candidate exceeding actual template/glyph budgets rejects; legal fallback is not presented as a resource failure. MVT uses original-part arc lengths and half-open target ownership, preserving parent fallback and world copies. Radial extent relative to an anchor is limited to 1024 CSS pixels; unknown tile coverage expands by that bound while retaining 64-tile/32-request limits. Cross-tile geometry stitching, a globally consistent repetition phase along the entire road, complex shaping, tilted/globe/terrain line placement, device/external-platform acceptance and formal release remain pending.

Common paint transitions and planar playback/media

With a matching core exposing commonPaintTransitions, root transition and property-transition objects support the42 implemented uniform numeric/color/vec2 properties. Duration/delay default to300/0ms and each is bounded by3600000. Per-property fields independently override root values; controls affect subsequent retargets. Reads expose target declarations while display/query share actual-time samples. Interrupted priors keep their original clocks; compatible/no-op diffs retain identities and applicable timelines. Data/feature-state targets remain immediate. Image/dash/pattern crossfade, layout/enums/nonzeroLight and unsupported properties do not gain transitions. The current/candidate history budget, failure declaration/display distinction and full property table are documented inCommon paint transitions.

Only zero-pitch/roll Mercator views allow the newly accepted simple playback-line/media composition, with real layer order and original scene owner/playback clock. Paint writes, including controls, to a playing or paused layer retaining its visual returnCONFLICT until playback is stopped; starting playback on a layer with an active paint curve also conflicts. Other layers may transition. Existing topology/offset/gap/blur/pattern/gradient/globe/terrain animation restrictions remain. Older references to pending complete transitions do not exclude this conditional uniform-property batch; image crossfade and full style support remain pending. External platforms, devices, mobileGIS/iOS and formal publication still require acceptance.

Updating GeoJSON sources

The shared facade follows addSource/getSource/removeSource/isSourceLoaded and source setData/updateData/getData. Lookup and updates are asynchronous for cross-thread hosts. Registration creates data only. Use GeoJSON circle/line/fill style layers for display; existing GIS source/layer calls stay compatible but are not interchangeable with this registry.

import { map } from 'sqsmap';
map.addSource('trees', {type:'geojson', data:{type:'FeatureCollection',features:[]}});
const trees = await map.getSource('trees');
await trees.setData({type:'Feature', id:'oak', properties:{height:12}, geometry:{type:'Point',coordinates:[116.4,39.9]}});
await trees.updateData({update:[{id:'oak',addOrUpdateProperties:[{key:'height',value:15}]}]});
const data = await trees.getData();
map.removeSource('trees');
await map.sqs.flush();

Handles bind a public id to a non-reused sourceInstanceId. Whole-data replacement advances the data revision without replacing that identity. Deletion and re-registration invalidate old handles with STALE_HANDLE. Updates preserve the camera/order, original GeoJSON and f64 coordinates. promoteId selects display/incremental identity without rewriting original data returned by getData. generateId supplies positional display IDs for whole-data replacement only.

Incremental updates require unique explicit IDs or promoteId. Numeric and string IDs differ. Removal, addition and feature changes apply in that order; adding an existing identity replaces its feature, while unknown removals/updates are ignored. Geometry and property changes are supported. Invalid updates preserve previous data and revision. Empty/null geometries are valid; empty bounds are null. Longitude/latitude are limited to ±180/±90; positions may include altitude.

Core/C/WASM SDK limits: 16 MiB JSON, 50,000 logical features and 500,000 positions per source; 128 sources, 64 MiB retained JSON and 128 MiB resident source data per map. Shared scripts also have a 4,096-node/32-level argument budget and 64 KiB host request/response limit. Native script queues retain their 64 KiB limit; use C/SDK for larger sources. HTTP(S) GeoJSON URLs and shared Point clustering are connected; image/video/canvas and shared raster tiles are connected; remaining vector types/DEM remain in progress. Inline GeoJSON circle/line/fill displays are connected.

Desktop/web hosts and retained native sessions forward source-count and GeoJSON loading events. Matched SDK/WASM is required. Shared Point clustering is verified by the same script on native/application QuickJS and browser main/strict Worker with actual rendering; see the clustering guide. These local software results do not replace Windows/macOS, hardware WebGPU or mobile device acceptance. The mobile application host remains pending and reports CAPABILITY_UNAVAILABLE.

API references: GeoJSONSource, source diff and feature diff. Only interface descriptions are referenced; SqsMap supplies its own implementation.

Attribute/ID duplication in collections is conservatively estimated before decoding; data below raw byte/count limits can still exceed the expansion budget. Resident data limits do not describe peak memory of the entire host process.

Source-change observers

Desktop, web and retained native scripts support sqs.sourceschange. Each successful registration, deletion or data update produces a sourceCount snapshot of registered sources. Data replacement may leave the count unchanged. Queries and failed writes produce no event. The event does not confirm display layers, downloads or rendering. sequence and mapInstanceId identify ordered events on one map. Session disposal removes observers. The mobile host is still pending.

import {map} from 'sqsmap';
export const sessionMode = 'persistent';
const subscription = map.on('sqs.sourceschange', event => console.log(event.sourceCount));
// subscription.unsubscribe();

The standalone browser SDK offers view.onSourcesChange(listener) and view.on('sources', listener), returning an unsubscribe function. Snapshots are immutable; limits are 32 observers and 128 pending discrete changes. A full queue rejects before writing. Observer exceptions are logged without undoing committed writes. SDK callbacks omit script-session target/sequence; the shared host adds them. Older npm SDKs without this observer are not advertised by map.sqs.events(). Native C events retain the full state; native JavaScript also provides the shared top-level sourceCount.

A command barrier does not wait for observer callbacks. To consume a particular notification, create map.once('sqs.sourceschange') before writing and await that promise before unsubscribing.

Live source batches

Shared live point sources use addSource with sqsLive: fields declares string/number/boolean values and allowDelta defaults to false. Initial data must be an empty FeatureCollection, without URL, promoteId or generateId. The source.sqs LiveSourceAPI provides:

source.sqs.applyLiveUpdate(frame: LiveUpdate): Promise<LiveUpdateResult>
source.sqs.getLiveState(): Promise<LiveSourceState>
source.sqs.freezeLiveSnapshot(): Promise<LiveSourceSnapshot>

The protocol uses decimal u128 sequence strings; replaying the last batch returns applied:false. Protocol, source, clusters and display commit atomically; failure keeps the last good data. sourcedata.sqsLive is immutable historical state. Manual setData/updateData conflict on live sources; stale handles report STALE_HANDLE and unconfigured sources report CAPABILITY_UNAVAILABLE. Batches allow 4096 points/8MiB; scripts have a separate 64KiB response limit. Editing an independent frozen copy does not alter the map. See live source batches for complete constraints, SDK liveSourceUpdates and native SQSM_MAP_LIVE_UPDATES. Batch ingestion, HTTP polling and WebSocket are connected; receiver scene ownership is connected by15.1; static scene resources/playback/tracks and mobile hosting remain pending.

HTTP live receivers

Shared sources support source.sqs.connectLive({protocol:'gis-live-v1',transport:{type:'http-poll',url,intervalMs:2000}}). It returns an identity-bound receiver; await getState/pause/resume/stop. getLiveReceiver returns undefined when absent. Intervals500..60000ms,8 active/paused receivers per map. pause/stop cancel in-flight I/O; stop retains data; old identities are STALE_HANDLE. Manual applyLiveUpdate conflicts while active/paused. Historical sqsLiveReceiver events omit URL/credentials. HTTP errors retain data and retry; replay completion emits sourcedata without a data revision change. The map/source owns the receiver after script exit. See bilingual live-http for budgets, C pumping, state/events and full HTML. WebSocket is connected separately; static scene resources/playback/tracks/mobile remain pending.

WebSocket live receivers

Shared source.sqs.connectLive also accepts transport:{type:"websocket",url,reconnectDelayMs:2000}. Snapshot-first reconnection, cancellation and bounded queues are documented in WebSocket live receivers. Eight receivers and8frames/16MiB waiting data per map; binary messages are rejected. Core/C resource channels add no network dependency. Scene/mobile and external-device acceptance continue separately.

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 is true; scenePlayback/sceneRoutePlayback/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.

Static scenes: await scene.attachSource(await map.getSource(id)) / scene.detachSource(source). Frozen sources list identities; draft/stopped hides, paused preserves display, remove restores user layout. All same-source shared layers, rendered queries and credits use the same effective visibility. See static scenes.

Shared scene clock

await scene.getClock() returns immutable {id,sceneInstanceId,state,elapsedMs}. Draft is zero, running accumulates monotonic milliseconds, pause/stop freeze, and resume keeps time. Pure reads emit no event or write sequence. VM exit does not pause it. Running includes host background monotonic gaps; system sleep follows platform clock behavior. sceneClock:true does not promise playback/tracks; old desktop map.scenes remains a compatibility API. See the complete scene clock guide.

Standalone SDK prefers map.on('sqs.sourceschange', listener); the old sources alias addresses the same subscription. See event names.

Shared vector/raster registration,metadata state and setTiles/setUrl are connected. Shared raster layers support planar/pitched/globe views,ordering,visibility,opacity and tileState. Shared vector circle/line/fill and source-layer queries are also connected; remaining types,DEM and feature state remain pending. See shared raster tile layers.

Shared MVT layers and queries

Vector sources now display standard circle,line,fill layers through addSource/addLayer. Each layer requires source-layer. Layers share downloads and cached content, with common geometry for planar,pitched and globe views. Planar rendering interleaves fills,lines and points in logical layer order: moveLayer can place a fill above a circle while retaining spatial-cell culling and LOD.

import {map} from 'sqsmap';
map.addSource('roads',{type:'vector',tiles:['https://your-server.example/{z}/{x}/{y}.pbf'],attribution:'Your provider'});
map.addLayer({id:'roads-line',type:'line',source:'roads','source-layer':'transportation',paint:{'line-color':'#e87925','line-width':3}});
await map.sqs.flush({render:true});
console.log(await map.querySourceFeatures('roads',{sourceLayer:'transportation'}));

Scheduling/cancellation reuse shared tile handling, including TileJSON,setTiles/setUrl,XYZ/TMS,world copies and attribution. Each MVT response permits8MiB,32 source layers,50,000 features and500,000 vertices. A map permits64 visible tiles,32 pending requests and64MiB decoded tile cache; visible source data additionally obeys16MiB JSON,50,000 features,500,000 coordinates and resident source budgets. Over-budget responses fail explicitly and failed refresh retains old content. Parent/child fallback is not connected; cached invisible tiles are excluded from queries.

querySourceFeatures requires sourceLayer. queryRenderedFeatures filters by display layer and returns sourceLayer,original MVT id and attributes, with WGS84 coordinates including visible world copies. Native/shared script reads use await. A feature may appear multiple times across tiles or world copies. GeoJSON/media sources cannot specify source-layer. metadataLoaded describes metadata; tileState and loaded describe actual current-view readiness.

vectorTileSourceStore=true represents this MVT subset, not complete vector style compatibility. Shared point image/text symbols and promoteId are implemented in subsequent stages. Fill-extrusion and other types,buffer-geometry tile-boundary clipping,MLT,parent/child fallback and the mobile host remain pending. Existing setStyle(URL) basemaps retain their separately documented scope.

The complete HTML includes every button,query,credit and disposal action. The portable acceptance script uses locally generated MVT through real native QuickJS and WASM main-thread/strict Worker. Replace the template and display required provider credits for real services.

Shared MVT feature state is connected per sourceLayer/id; see feature state.

Shared MVT promoteId (07.10)

A vector source accepts a property name or a sourceLayer-to-property mapping. A string applies to every source layer. A mapping overrides listed layers only;unlisted layers retain native MVT IDs. A missing/null mapped value removes the original ID. Valid IDs are strings or finite JavaScript safe numbers;invalid values fail the candidate tile before cache publication. Source/rendered queries and sourceLayer-qualified feature state use promoted IDs. Geometry fragments remain separate query results;geometry merging is not claimed.

map.addSource('vector',{type:'vector',tiles:['https://your.example/{z}/{x}/{y}.pbf'],
 promoteId:{places:'name'}});
map.addLayer({id:'places',type:'circle',source:'vector','source-layer':'places',
 paint:{'circle-color':['case',['coalesce',['feature-state','selected'],false],'#ff00ff','#0000ff']}});
map.setFeatureState({source:'vector',sourceLayer:'places',id:'actual name'},{selected:true});
await map.sqs.flush({render:true});

Read fields and IDs from your own data. The configuration survives setTiles/setUrl and map.sqs.getSourceInfo returns promoteId. Shared addSource/addLayer and inline shared Style JSON support promotion. The independent setStyle(URL) basemap path still explicitly rejects it;TileJSON does not override this source option. Syntax references theMapLibre source specification;execution uses SqsMap's shared core. Thecomplete HTMLuses generated tiles/string IDs and contains every button handler.

volatile and persistent caching (07.11)

Shared vector/raster sources accept volatile boolean,default false. True forces tile transport no-store after map cache policy and browser transformRequest options. Desktop jobs skip disk lookup/write and send Cache-Control:no-store. Native JSON requestPlan.cache is no-store;external hosts should execute that plan. Source info reports tileMetadata.volatile and setTiles/setUrl preserve it.

map.addSource('tiles',{type:'vector',tiles:['https://your.example/{z}/{x}/{y}.pbf'],volatile:true});

Bounded decoded/rendering memory remains available. This does not imply per-frame downloads,automatic refresh or a live subscription. Cancellation/retry/fallback follow common resource rules;desktop common tile work now uses Tile-specific policy rules. Shared inline Style JSON/addSource support this option. Independent setStyle(URL) still explicitly rejects volatile:true. The name references theMapLibre source specification;SqsMap defines the browser/native persistent-cache execution behavior. Thecomplete HTMLuses generated tiles and contains all operation code.

Georeferenced images, video and dynamic frames (16.1)

Common scripts use MapLibre-shaped image/video/canvas sources, raster layers and setCoordinates/play/pause. Applications build their own layer panels and playback controls. This stage accepts host-decoded RGBA frames. Video URLs are connected in16.4 below; Canvas input is connected below; terrain draping is connected within terrain-quad; custom rendering extensions remain in progress; this is not complete media compatibility.

// @gis-api 0.5-session
// @permissions ["project.read", "map.write"]
import { map } from 'sqsmap';
map.setProjection({ type: 'mercator' });
map.addSource('camera', {
  type: 'video',
  coordinates: [[-4, 4], [4, 4], [4, -4], [-4, -4]],
  attribution: 'Your media source'
});
map.addLayer({ id: 'camera-layer', type: 'raster', source: 'camera' });
await map.sqs.flush();
const camera = await map.getSource('camera');
await camera.sqs.submitFrame({ sequence: 1, width: 1, height: 1, data: [255, 0, 0, 255] });
await camera.pause();
await camera.play();
await map.sqs.flush({ render: true });
export default await camera.sqs.getState();

Coordinates are WGS84 longitude/latitude corners in top-left, top-right, bottom-right, bottom-left order. The footprint must be finite, convex, nonintersecting and within Mercator bounds, without crossing the date line. Flat/pitched Mercator and spherical media displays are connected; terrain draping is connected within terrain-quad. Both source types accept frames; only video accepts playback control. raster-opacity supports constants and existing zoom expressions, not feature properties, feature state or filters. Standard layer APIs control opacity, zoom range, visibility and ordering. Actual four-corner georeferencing is rendered rather than its bounding box. Media has no queryable features.

Sequence numbers are positive safe integers and must increase strictly. Duplicate/late frames return CONFLICT. Paused input is discarded with applied:false, preserving sequence, revision and pixels. Resuming accepts later sequences. Input is copied before atomically replacing the sole latest frame; failures retain the old frame. Removing/recreating a source invalidates old handles and late decoder deliveries with STALE_HANDLE. Remove referencing layers before removing a source; map destruction releases resources.

Dimensions are 1–4096, with exact width×height×4 RGBA bytes, 32MiB per frame, 64MiB per map and at most 32 media sources. Script JSON arrays additionally obey script message budgets. For large frames, use the host binary API: standalone SDK source.sqs.submitFrame takes Uint8Array; WASM mediaFrame and C sqsm_map_media_frame share identity, sequence and budget semantics. C input is borrowed only during the call; output is independently owned and freed using sqsm_map_bytes_free. Failed output allocation does not commit a frame. The library includes the portable pure-Rust MP4/H.264 decoder below; it does not own browser GL contexts or HTTP. Host-decoded frames still use the same binary input.

getSourceInfo includes mediaState and loading state; a source is not loaded before its first frame. Existing sourcedataloading/sourcedata events describe source changes. Command confirmation means application; render:true waits for a completed current display. Desktop, native QuickJS and browser main-thread/strict Worker use the same acceptance script. Local software-GPU evidence does not replace external-device or formal-release acceptance.

URL image sources and updateImage (16.2)

Image sources accept a credential-free HTTP(S) url. Hosts download through the shared resource policy; the core decodes PNG/JPEG/WebP with at most4MiB encoded input,4096 pixels per edge and32MiB RGBA. No browser image object is required; existing512px sprite limits remain unchanged.

map.addSource('photo', {type:'image',url:'https://your.example/orthophoto.png',coordinates:[[-4,4],[4,4],[4,-4],[-4,-4]]});
map.addLayer({id:'photo-layer',type:'raster',source:'photo'});
await map.sqs.flush();
const photo=await map.getSource('photo');
await photo.updateImage({url:'https://your.example/new.png',coordinates:[[-5,5],[5,5],[5,-5],[-5,-5]]});

Confirmation only registers the new request; use sourcedata/isSourceLoaded for download/decode completion. New pixels and optional corners commit together. Failure retains old pixels and georeferencing, emits error and endsloading while hasData remains true. Generations reject late responses; another update or removal cancels the old request. setCoordinates returns CONFLICT while an update has captured corners; an update without corners allows independent coordinate changes. Explicit submitFrame cancels pending image requests and clears URL snapshot ownership, preventing old responses from replacing the new frame.

Capability queries expose mediaImageURLSources/mediaImageUpdates/mediaFrames/mediaRasterPlanar/mediaRasterPitched. mediaVideoURLSources is connected in16.4; Canvas named frames are connected; terrain andcustom rendering still returnfalse. Shared capability flags do not establish mobile script-host acceptance.

The complete HTML example includes styles, all button handlers, image loading events, attribution and destruction cleanup.

Spherical media rendering (16.3)

Registered image/video RGBA media and URL image sources can use map.setProjection({type:'globe'}); mediaRasterGlobe is true. Handles, frame sequences, pause state, coordinates, opacity, visibility and layer order retain their meanings. Switching Mercator/globe preserves media. Actual four-corner spherical meshes reuse shared depth, lighting and texture caches rather than a planar screenshot. Coordinates still obey the published Mercator georeferencing bounds; date-line crossing is unsupported.

Spherical lighting can change pixel values. Acceptance checks channels and actual layer occlusion rather than requiring bytes identical to flat rendering. Media produces no queryable features; other vector queries keep their spherical semantics. Cancellation/destruction releases resources. Frame confirmation checks camera,density,business revisions and media frame signatures, preventing an old texture from passing a render barrier.

URL videos and shared playback clock (16.4)

import { map } from 'sqsmap';
map.addSource('film', {
  type: 'video',
  urls: ['https://your.example/clip.mp4'],
  coordinates: [[-4,4],[4,4],[4,-4],[-4,-4]],
  attribution: 'Video author and license'
});
map.addLayer({ id:'film-layer', type:'raster', source:'film' });
await map.sqs.flush();
const film=await map.getSource('film');
await film.pause();
await film.play();
export default await film.sqs.getState();

urls contains1–8 credential-free HTTP(S) alternatives, downloaded/decoded in order under the same source identity. A terminal error occurs after all alternatives fail; RESOURCE_LIMIT/CANCELLED do not try another URL. The core does not perform HTTP: browser, desktop and C callers use the shared resource policy. sourcedata.isSourceLoaded becomes true only after an accepted decoded first frame. Registration confirmation is not loading completion. Subscribe with map.on; pausing before loading still permits the first frame.

Every host uses the same pure-Rust ordinary MP4/H.264 decoder:8-bit progressive YUV420. Fragmented MP4,WebM,HEVC,audio playback and browser-specific getVideo are unsupported. Encoded input is64MiB per file/map, with at most2 URL videos per map,1 hour/216000 samples. Coded edges are at most2048 and total pixels at most1920×1088. Display frames retain the desktop960×540 downscale rule and limited-range BT.601 conversion. Reorder buffering is at most16 frames; each map-owner tick decodes at most32 frames per video, never predecoding a whole movie. Unsupported formats fail explicitly. The existing64MiB total latest-RGBA budget still applies.

Playback starts automatically on the map-owned monotonic clock. pause freezes pixels/time;play resumes;end-of-file loops. State adds duration/currentTime in seconds;currentTime describes the accepted presentation frame. sourcedata reports frame changes. Display failure pauses and emits error while retaining the previous accepted pixels. Removal/recreation and map destruction release downloads,compressed input and decoders;late responses cannot revive a source. Explicit sqs.submitFrame takes over media ownership and stops URL decoding;paused external frames still discard.

C resource responses now allow64MiB globally and validate the smaller request budget before copying. GeoJSON8MiB and image4MiB remain unchanged;ABI1 symbols/layout/output ownership remain unchanged. Flat,pitched and spherical raster media are connected;terrain draping remains pending. mediaVideoURLSources=true describes the matching shared implementation, not mobile-host/device acceptance. The same video script and complete HTML example include loading events,play/pause,attribution and cleanup.

Canvas sources

The type:'canvas', canvas, coordinates, and animate vocabulary follows MapLibre CanvasSource; SqsMap implements it with its own core. animate defaults to true. False accepts the initial frame and stays paused; play resumes, pause freezes the latest pixels. getStyle retains the provider name and initial animate setting; query playback through sqs.getState.

Portable scripts pass a string such as canvas:'drawing'. The host produces RGBA pixels and calls source.sqs.submitFrame({sequence,width,height,data}). Script data is a byte array; SDK data is Uint8Array. Sequence numbers are increasing positive safe integers. Readiness requires an accepted first frame. sqs.fail({code:'IO_ERROR',message:'Canvas unavailable'}) ends loading and emits error while retaining accepted pixels. Play and a new valid frame recover. Old handles cannot change a recreated source.

The browser SDK accepts a HTMLCanvasElement or an existing element ID, capturing readable 2D pixels at roughly30fps with at most one submission in flight per source. Capture stays on the main thread even with strict Worker rendering. source.getCanvas() returns the bound browser element and is absent from portable script/native declarations. A string without a corresponding DOM canvas registers a manual named provider. Tainted,empty or non2D canvases fail automatic capture; WebGL/OffscreenCanvas hosts should submit RGBA explicitly. Explicit submission takes ownership and stops automatic capture. Limits remain4096 pixels per edge,32MiB per frame and64MiB for the map's retained frames.

Removal/destruction stops capture and rejects late work by source instance. Raster display supports flat,pitched and spherical views. Terrain draping and custom GL callbacks remain pending. canvasFrameSources/mediaCanvasSources identify the shared frame model; canvasDOM is reported only by the browser SDK. The mobile script host remains unimplemented. The complete HTML/CSS/JS example includes drawing,playback buttons and cleanup; the portable script shows named frame submission.

13.6c supports custom GeoJSON/TileJSON/georeferenced image/video source URLs and updates, retaining decoder/cancellation/identity rules. See source protocols; Style/glyph/loadImage/live entries retain their own scope.

Shared planar composition capability

When a matching SDK reports commonPlanarLayerComposition:true, the shared Mercator view with zero pitch/roll and no terrain/extrusion combines media, backgrounds, labels, heat and supported static line/fill effects in complete layer order. This capability supersedes earlier media-combination exclusions only within that shared planar scope. Custom line animation, tilted/globe/terrain views and other unsupported material combinations retain their respective limits. Reordering and compatible diff preserve identities, caches, pending requests and fade clocks; material, geometry and texture budgets are validated before publication. See planar layer composition. Common layers implement all ten base types: circle, line, fill, fill-extrusion, raster, heatmap, background, symbol, hillshade and color-relief. Sections below specify actual properties, projections and combinations; unsupported scope rejects explicitly. Callers own layer/tool UI through APIs and events. Common layers currently support circle/line/fill/raster,planar heatmap,source-less background and point image symbols. The type sections below specify actual properties/projection limits. Unsupported combinations reject explicitly. Callers own layer/tool UI and drive it through the shared API/events.

Shared GeoJSON style layers (07.2 / 08.1)

map.addLayer(spec, beforeId?) attaches a registered source using circle, line, or fill. Several layers may reuse one source. Source registration creates no implicit layer; adding a layer preserves the camera. Omitted beforeId appends to the shared layer group above legacy GIS vector layers. moveLayer orders this group only; legacy numeric layer calls retain their existing meaning.

import { map } from 'sqsmap';
map.addSource('trees', { type: 'geojson', data: {
  type: 'FeatureCollection', features: [{ type: 'Feature', id: 'oak',
    properties: { height: 12, enabled: true },
    geometry: { type: 'Point', coordinates: [116.4, 39.9] } }]
}});
map.addLayer({ id: 'halo', type: 'circle', source: 'trees',
  paint: { 'circle-color': '#ffffff', 'circle-radius': 12 } });
map.addLayer({ id: 'trees', type: 'circle', source: 'trees',
  paint: { 'circle-color': '#008800', 'circle-radius': ['coalesce', ['get', 'height'], 6] },
  filter: ['==', ['get', 'enabled'], true], metadata: { name: 'Trees' } });
await map.sqs.flush();
const trees = await map.getSource('trees');
await trees.updateData({ update: [{ id: 'oak', addOrUpdateProperties: [{ key: 'height', value: 15 }] }] });
map.setLayoutProperty('trees', 'visibility', 'none');
map.setPaintProperty('halo', 'circle-color', '#ff0000');
map.moveLayer('halo', 'trees');
await map.sqs.flush();
const description = await map.getLayer('trees');
const layers = await map.sqs.getLayers();
// Remove referencing layers before their source.
map.removeLayer('trees').removeLayer('halo').removeSource('trees');
await map.sqs.flush();

Queries require await. Writes are chainable; flush confirms application. getLayer returns a description copy and preserves raw metadata; editing that copy does not edit the map. A null paint/layout value restores the default. setFilter(id, null) clears the filter. Zoom ranges include the lower bound and exclude the upper bound, defaulting to 0–24. Layout visibility differs from effective visible; range changes do not rewrite layout. getLayers returns order, non-reused layerInstanceId, bound source identity, data/cache revisions, effective visibility, display feature count and dynamic-style warningCount. GIS layers, internal basemap layers and raster overlays are separate. getStyle is connected for the documented style subset; read the style guide for asynchronous loading/diff and unsupported options.

Use the SqsMap extensions map.sqs.setLayerMetadata(id, metadata) / getLayerMetadata(id) for panel labels and user information. Matching SDK methods are setLayerMetadata / getLayerMetadata. The object replaces all metadata; null clears it. Without name, the label is the layer ID. name must be a nonblank string without NUL and at most 4096 UTF-8 bytes; other keys remain raw. Metadata writes emit sqs.layerschange and preserve layer/source identities and order. A metadata-only edit does not rebuild the display cache at the same zoom.

map.sqs.setLayerMetadata('trees', { name: 'Trees', category: 'survey' });
await map.sqs.flush();
const metadata = await map.sqs.getLayerMetadata('trees');

Like property setters, setFilter accepts an optional { validate: true } third argument. false reports CAPABILITY_UNAVAILABLE; unknown keys and wrong types report INVALID_ARGUMENT.

circle selects points, fill polygons, and line includes lines and polygon outlines. Collection children inherit their parent's identity and typed properties. Display filters do not edit source data. Source replacement validates every dependent display cache before committing; normal argument, budget or version failures preserve the previous source, description, identities and frame. Referenced sources cannot be removed. Removing a shared layer through the legacy numeric delete entry also releases its logical source reference.

Canonical planar zoom drives filters, paint and visibility ranges. Browser radius/width use CSS pixels. Zoom paint retains the shared expression restriction to a top-level step/interpolate. Spherical circle/line/fill are supported; pitched common layers are connected; other CRSs and the remaining type/layout subsets retain explicit limits. Editable tiled source-layer and symbol behavior are documented in the tiled-vector and style guides; shared line-gradient uses lineMetrics. CSS color input support is documented in the color guide. validate:false cannot bypass checks. Invalid dynamic values use style fallback or skip filtered features and contribute to warningCount rather than reporting a source loading failure.

Limits: 128 shared layers and 128 total shared/legacy render layers; 64 KiB, 4096 nodes and depth 32 per description. A conservative estimate limits all display caches to 128 MiB, including collection attribute expansion and each layer's source copy. Large shared sources may therefore reject earlier. This does not bound the entire host's peak memory.

Successful logical layer writes emit sqs.layerschange with method, id and layerCount. Failed writes do not notify. Use the common on/off/once contract; flush does not consume callbacks, so await once before unsubscribing. Source-data refresh preserves layer instances/order and emits sourceschange, not fabricated layer additions. Use map.on('sqs.layerschange',callback) as the canonical observer; prior SDK aliases remain compatible. Old npm builds explicitly reject missing support. Linux hardware rendering and actual QuickJS are verified; Windows/macOS, mobile and real browser GPU runs remain separately recorded.

Shared spherical line/fill and complete HTML: spherical surfaces.

Standalone SDK on/off/once also accepts sqs.layerschange and sqs.sourceschange; aliases share subscriptions. See event names for host differences and cleanup.

For complete caller-owned UI, state/event mapping, all handlers and cleanup, see layer panels and tools. Current pitched/globe, CSS colors, media raster and tiled styles have separate documented subsets; early phase labels above do not replace their current guides.

Shared raster tile layers now reference raster sources, preserve logical layer order and share decoded pixels. See raster tiles for scope and budgets.

Shared MVT layers and queries

Vector sources now display standard circle,line,fill layers through addSource/addLayer. Each layer requires source-layer. Layers share downloads and cached content, with common geometry for planar,pitched and globe views. Planar rendering interleaves fills,lines and points in logical layer order: moveLayer can place a fill above a circle while retaining spatial-cell culling and LOD.

import {map} from 'sqsmap';
map.addSource('roads',{type:'vector',tiles:['https://your-server.example/{z}/{x}/{y}.pbf'],attribution:'Your provider'});
map.addLayer({id:'roads-line',type:'line',source:'roads','source-layer':'transportation',paint:{'line-color':'#e87925','line-width':3}});
await map.sqs.flush({render:true});
console.log(await map.querySourceFeatures('roads',{sourceLayer:'transportation'}));

Scheduling/cancellation reuse shared tile handling, including TileJSON,setTiles/setUrl,XYZ/TMS,world copies and attribution. Each MVT response permits8MiB,32 source layers,50,000 features and500,000 vertices. A map permits64 visible tiles,32 pending requests and64MiB decoded tile cache; visible source data additionally obeys16MiB JSON,50,000 features,500,000 coordinates and resident source budgets. Over-budget responses fail explicitly and failed refresh retains old content. Parent/child fallback is not connected; cached invisible tiles are excluded from queries.

querySourceFeatures requires sourceLayer. queryRenderedFeatures filters by display layer and returns sourceLayer,original MVT id and attributes, with WGS84 coordinates including visible world copies. Native/shared script reads use await. querySourceFeatures retains tile fragments; queryRenderedFeatures deduplicates stable IDs per display layer/sourceLayer, while features without IDs retain fragments. GeoJSON/media sources cannot specify source-layer. metadataLoaded describes metadata; tileState and loaded describe actual current-view readiness.

vectorTileSourceStore=true represents this MVT subset, not complete vector style compatibility. MVT promoteId,volatile,feature state and stable-ID rendered-query deduplication are connected. Other MVT types including symbols/extrusions,buffer clipping/geometry merging,MLT and MVT parent/child fallback remain pending. Android standalone-host acceptance is separate from mobile application/device acceptance. Existing setStyle(URL) basemaps retain their separately documented scope.

The complete HTML includes every button,query,credit and disposal action. The portable acceptance script uses locally generated MVT through real native QuickJS and WASM main-thread/strict Worker. Replace the template and display required provider credits for real services.

Shared MVT feature state is connected per sourceLayer/id; see feature state.

Shared type expressions (08.5)

Supports MapLibre-style boolean/number/string/object assertions with ordered candidates, array item-type/length assertions, typeof,properties and get/has with optional objects and string-name expressions. Syntax follows the official expression reference; the shared Rust evaluator is independently implemented without the MapLibre runtime.

map.setPaintProperty('places-layer','circle-color',
  ['case',['boolean',['feature-state','selected'],false],'#ff00ff','#0000ff']);
map.setPaintProperty('places-layer','circle-radius',
  ['number',['get','height',['object',['literal',{height:18}]]],6]);
await map.sqs.flush({render:true});

Assertions do not coerce numeric strings. They select the first candidate of the requested type and short-circuit later evaluation; failure uses existing paint defaults/warnings or explicit query-filter errors. array supports value,type/value,type/length/value forms with string/number/boolean item types and nonnegative integer lengths up to65536. Array typeof results include item type/length, e.g.array<number, 2>; mixed items use value. get returns null for missing keys,has distinguishes missing from present null,and invalid object inputs fail explicitly.

This describes JSON types; collator, the full format/image value system and remaining expressions stay in subsequent tasks; text-field supports format text sections with font-scale/text-color, complete PBF text-font stacks, top/center/bottom vertical-align, registered RGBA image sections and case/coalesce/match/let/var combinations, described in formatted text, font stacks, section alignment and RGBA image sections. Images use pixelRatio dimensions and retain RGBA color multiplied by text-opacity; missing images are actual null, SDF sections reject and a top-level text-field image must be wrapped in format. Existing node/depth,source and display budgets apply; feature-state is paint-only. GeoJSON/shared MVT use identical semantics; setStyle(URL) basemaps retain their separate expression scope. The complete HTML and portable script include selection,ordering,views,queries,credits and disposal.

Variable scope (08.6)

Shared paint,filter and query expressions support let/var. Variables are visible in the let result; inner bindings shadow outer ones. Binding values resolve only enclosing-scope variables. Bindings evaluate on demand,so unused runtime errors do not execute. Names use nonempty ASCII letters/digits/underscores,up to256 bytes. Unbound names and invalid structures reject before mutation.

map.setPaintProperty('places-layer','circle-color',
  ['let','selected',['boolean',['feature-state','selected'],false],
   ['case',['var','selected'],'#ff00ff','#0000ff']]);
map.setPaintProperty('places-layer','circle-radius',
  ['let','large',18,['step',['zoom'],6,4,['var','large']]]);
await map.sqs.flush({render:true});

Camera zoom may drive a terminal step/interpolate inside outer let expressions; it cannot appear in binding values or other positions. Shared layers continue reevaluating actual zoom. Each evaluation permits8192 steps/64 levels; AST limits remain1024 nodes/32 levels. Budget failures use existing paint defaults/warnings; layer/query filters report RESOURCE_LIMIT and mutations leave no partial layer/reference.

The complete HTML and portable script include all buttons,credits and cleanup through native and real WASM main-thread/strict Worker. Implementation is independent against the public expression specification,without MapLibre code. Remaining operators and complete layer types continue in tasks.

Shared planar heatmap layers

Standard addLayer(type:heatmap) now references GeoJSON points or MVT point source layers; MVT requires source-layer. Rendering reuses the independently implemented GPU heat module and shared ordering,filters,visibility,zoom,credits and state weights. Applications provide their own tool UI.

import {map} from 'sqsmap';
map.addSource('points',{type:'geojson',data:{type:'Feature',id:1,properties:{weight:1},geometry:{type:'Point',coordinates:[0,0]}}});
map.addLayer({id:'heat',type:'heatmap',source:'points',paint:{
 'heatmap-weight':['number',['get','weight'],1],
 'heatmap-radius':36,'heatmap-intensity':1,'heatmap-opacity':0.8,
 'heatmap-color':['interpolate',['linear'],['heatmap-density'],0,'#00000000',1,'#ff0000ff']
}});
await map.sqs.flush({render:true});
console.log(await map.queryRenderedFeatures(await map.project([0,0]),{layers:['heat']}));

Radius is positive and at most256 CSS pixels,intensity0..1000,opacity0..1. The color ramp uses linear heatmap-density with strictly increasing0..1 stops,at most32 constant colors. Weights accept attributes/state/variables,finite nonnegative values,and no zoom; rendered weight×intensity clamps to128. Radius/intensity/opacity accept constants and existing zoom interpolation. Owned display caches permit16384 heat points per map,including hidden layers.

Rendered queries use nonzero weight/intensity/opacity and kernel radius. Color descriptions retain the ramp rather than inventing per-point density colors; state snapshots remain source/source-layer scoped. Invalid weights are omitted and counted as warnings. Invalid/over-budget writes leave no partial layer/source reference.

Currently planar,including planar overlays on URL basemaps. Pitch,globe and combinations with shared raster layers reject explicitly and continue in3D tasks. This is a documented subset,with exponential ramps,additional parameter combinations and the mobile host still pending. Capabilities commonHeatmapLayers/heatmapPlanar are true.

The complete HTML includes weight,opacity,radius,visibility,order,query and disposal handlers. The portable script uses identical logic in native/main-thread/strict Worker. Example points are locally generated; provide required credits for real data.

Source-less background layers

Standard addLayer supports type:background without source. It creates no fake source,count,reference or credit. Supports background-color/background-opacity and shared visibility,order,zoom ranges,name/metadata. Other layer types still require real sources.

import {map} from 'sqsmap';
map.addLayer({id:'background',type:'background',paint:{
 'background-color':'#00ff00','background-opacity':0.8
}});
await map.sqs.flush({render:true});
console.log(await map.getLayer('background')); // No source member
console.log(await map.sqs.getLayers()); // This layer has source:null
map.moveLayer('background'); // Move above earlier layers
await map.sqs.flush({render:true});

A bounded fullscreen GPU pass follows logical layer order and applies opacity once. Backgrounds return no fabricated query features. source/source-layer/filter,feature attributes and feature-state reject before mutation without leaving a source. Constants/shared zoom expressions retain existing color/opacity rules.

LayerInfo.source may now be null; getLayer/getStyle omit background source. Caller-owned panels should handle layer.source?.id. Existing sourced layers keep their identities/references; cleanup never removes an empty source. Currently planar; pitch/globe/shared-raster combinations reject explicitly and continue in3D tasks.

The complete HTML includes color,opacity,visibility,ordering,query and disposal handlers. The portable script verifies identical native and real WASM main-thread/strict Worker behavior. Example data are locally generated.

Point image symbols

Standard symbol layers now support planar Point/MultiPoint and MVT points. Register RGBA8 images with addImage or use an already loaded sprite. icon-image accepts a name, attribute expression or typed image/case/match/coalesce/let-var expression; icon-size is0..16 and icon-rotate is-360..360 degrees, each accepting a number or attribute/lexical/global-state expression,and icon-anchor supports nine standard anchors. icon-opacity uses common paint expressions,state and zoom. Visibility,ordering,filters and zoom ranges use the shared layer API. setLayoutProperty edits these layouts. Updating/removing images refreshes rendering and queries without replacing layer identity.

import {map} from 'sqsmap';
map.addImage('marker',{width:40,height:20,
 data:Array.from({length:800},()=>[255,0,0,255]).flat()},{pixelRatio:2});
map.addSource('points',{type:'geojson',data:{type:'Feature',id:1,
 properties:{icon:'marker'},geometry:{type:'Point',coordinates:[0,0]}}});
map.addLayer({id:'icons',type:'symbol',source:'points',layout:{
 'icon-image':['get','icon'],'icon-allow-overlap':true,'icon-ignore-placement':true
},paint:{'icon-opacity':1}});
await map.sqs.flush({render:true});
map.setLayoutProperty('icons','icon-rotate',90);
await map.sqs.flush({render:true});

Common point collision is implemented with both options defaulting to false;explicit true controls overlap and occupied space as described below. Plain/formatted text-field is available; line placement and other undeclared layouts remain pending;SDF/recoloring and demand events are implemented below. Layout rejects feature-state; use state in paint. Missing images,zero size or zero opacity have no rendered hits. Queries use actual dimensions,pixelRatio,rotation and anchor footprints;transparent image areas currently use rectangular footprints. Each map owns at most16384 image points including hidden layers;the atlas retains the2048-edge and existing image budgets.

Planar rendering is supported;pitch,globe and common raster combinations reject explicitly. This is a symbol subset,not full symbol compatibility. commonPointIconSymbols is true;the mobile host is pending. The complete HTML includes update,rotation,anchor,opacity,visibility,image removal/restoration,order,query and disposal handlers. The portable script uses the common map API. Example points/images are generated locally;retain source attribution for real data and basemaps.

Shared symbol demand events and SDF recoloring

Planar common symbols now emit styleimagemissing for visible points/layers with nonzero size/opacity. Names are deduplicated;events carry id and styleGeneration. Filling then deleting an image,leaving then reentering demand,or replacing the style generation rearms notifications. Style and common-layer demand share one bounded set of at most128 missing names;output/event-budget failure does not acknowledge demand early. Listeners may addImage to complete it. Unsubscribe,stop and destruction release listeners.

import {map} from 'sqsmap';
export const sessionMode='persistent';
const subscription=map.on('styleimagemissing',async event=>{
 if(event.id!=='marker')return;
 map.addImage(event.id,{width:16,height:16,
  data:Array.from({length:256},()=>[0,0,0,255]).flat()},{sdf:true});
 await map.sqs.flush();
});
map.addSource('points',{type:'geojson',data:{type:'Feature',
 properties:{},geometry:{type:'Point',coordinates:[0,0]}}});
map.addLayer({id:'icons',type:'symbol',source:'points',layout:{
 'icon-image':'marker','icon-allow-overlap':true,'icon-ignore-placement':true
},paint:{'icon-color':'#ff0000','icon-opacity':0.8}});
await map.sqs.flush({render:true});
// Release with subscription.unsubscribe();stopping the script also releases it.

SDF images use the alpha distance field,a0.5 boundary and screen-derivative smoothing. icon-color supports common paint expressions,attributes/state/zoom and recolors sdf:true images only. Ordinary RGBA images retain their colors. Color alpha and icon-opacity combine once;zero final alpha produces no query hit. updateImage retains SDF,pixelRatio and identities;distance-image pixel updates refresh rendering. Transparent image areas still query by rectangular footprint,not a per-pixel mask.

Native retained scripts,WASM main-thread/strict Worker and the desktop host share these rules;the mobile host is pending. commonPointIconSDF/commonPointIconMissingImages are true. Shared text and point collision are available; line placement,globe/pitch and common raster combinations remain pending. The complete HTML includes every image/color/demand handler and11 controls. The portable retained script demonstrates callback completion. Example images/points are generated locally.

Shared point symbol collision and ordering

Planar symbols now support default collision placement. icon-allow-overlap and icon-ignore-placement accept boolean constants or expressions, defaulting to false. icon-padding accepts constants or numeric expressions in0..128 CSS pixels,default2. The first controls checking occupied space;the second controls blocking later symbols. MultiPoint positions are placed individually,and queries inspect placed points only. Hidden,missing-image,zero-alpha and offscreen symbols occupy no space. Omitting icon-image leaves an empty layout;setting it to null restores that empty layout.

symbol-sort-key accepts finite numbers or numeric attribute/zoom expressions. Ascending keys determine placement/drawing priority. symbol-z-order accepts auto/source/viewport-y,default auto. Without a sort key,auto/viewport-y draw by screenY when overlap is true or ignore-placement is false;source retains input drawing order. Upper common layers get placement priority. Queries follow actual icon drawing order. These parameter semantics follow the MapLibre symbol specification;the implementation reuses this engine's existing collision grid.

import {map} from 'sqsmap';
map.addLayer({id:'icons',type:'symbol',source:'points',layout:{
 'icon-image':['get','icon'],'symbol-sort-key':['get','rank'],
 'icon-padding':2
}});
await map.sqs.flush({render:true});
map.setLayoutProperty('icons','icon-allow-overlap',true);
map.setLayoutProperty('icons','symbol-z-order','viewport-y');
await map.sqs.flush({render:true});

Register the actual icon/rank fields and images first,or complete missing images in a listener. Invalid numeric sorting values reject;layout still rejects feature-state while paint supports it. Camera,viewport and actual style-density changes trigger placement without replacing source/layer identities. Contiguous identical icon draws batch GPU submission;MultiPoint splitting does not copy the entire point-index list. Each map owns at most16384 image points;the32-pixel grid uses at most64 buckets per box and1M comparisons. Failed candidates do not apply partial placement.

This stage covers common planar point-image layers. URL basemap labels use a separate layout domain;cross-domain collision remains pending. commonPointIconCollisions is true;commonAndURLSymbolCollisions is false. Point text is documented below;line placement,other parameters,3D/raster composition and mobile hosts remain pending. The complete HTML contains every handler for12 controls. The portable script covers placement,ordering,queries,zoom and layout reset. Example points/images are generated locally.

Shared point text labels

Standard planar symbol layers now draw Latin/Chinese text on Point/MultiPoint/MVT points. text-field accepts a string or common string expression. text-size accepts0..256 CSS pixels or a common numeric expression,default16. text-anchor supports nine anchors,default center;text-offset uses two em values and text-rotate accepts-360..360 degrees or a common numeric expression. text-color/text-opacity support common attribute,state and zoom paint expressions. Text overlap/ignore options default false and padding defaults2. Whole labels participate in common collision/order;queries inspect placed label rectangles. Image/text combinations currently require separate layers.

import {map} from 'sqsmap';
map.addSource('points',{type:'geojson',data:{type:'Feature',id:1,
 properties:{name:'Sqs地图'},geometry:{type:'Point',coordinates:[0,0]}}});
map.addLayer({id:'labels',type:'symbol',source:'points',layout:{
 'text-field':['get','name'],'text-size':18,'text-anchor':'center'
},paint:{'text-color':'#ff0000','text-opacity':1}});
await map.sqs.flush({render:true});
map.setLayoutProperty('labels','text-offset',[1,0]);
map.setPaintProperty('labels','text-color','#00ff00');
await map.sqs.flush({render:true});

Existing glyph shaping and bundled HarmonyOS Sans SC provide the same actual font on native/WASM (24px glyphs,SDF boundary near192/255). text-font retains1..8 ordered family names,but this shared text stage uses the documented bundled fallback;requested names are not advertised as loaded external fonts. New external glyph demand,complex shaping,additional text properties,line/combined image-text placement and3D/raster composition remain pending. Unsupported bundled glyphs reject explicitly and preserve the previous layer. Control language is independent from data text/font.

A separate R8 atlas preserves the user image atlas and registration budget. Glyphs add no listImages entries or fake sources. Limits are256 characters/4096bytes per label,16K common symbol points,65536 glyph quads,and8192 distinct glyphs subject also to the2048 atlas capacity. Hidden caches count. Glyph caches are reused;size,rotation,anchor/offset,source/state and real viewport/density changes refresh shared placement. Color alpha and opacity combine once;zero final alpha has no hit. The initial08.12 fixed typography is superseded by the08.13 properties below;full MapLibre shaping compatibility is not claimed.

commonPointTextLabels is true;commonTextExternalGlyphDemand is false. Native,WASM main-thread/strict Worker and desktop binding are connected;the mobile host,new desktop text GUI and external platforms remain pending. The complete HTML includes every text control handler. The portable script uses the same API. Points are generated locally;the distribution includes the font license in its third-party notices.

Configurable text typography (08.13)

text-max-width defaults to10em,range0..256;0 disables automatic wrapping while explicit newlines remain. text-line-height defaults to1.2em,range0..256;text-letter-spacing defaults to0em,range-16..256. text-justify accepts center(default),left,right,auto;auto follows the horizontal position of the evaluated text-anchor. text-transform accepts none(default),uppercase,lowercase. Unicode case conversion precedes length and glyph budgeting.

text-size,text-rotate and the three numeric typography properties support common numeric expressions using attributes or zoom. Each evaluated result must be finite and within range. text-padding accepts0..128 constants or numeric expressions. Updates refresh glyph geometry,whole-label collision and queries. Wrapping uses actual glyph advances with whitespace word boundaries and character fallback for oversized/CJK tokens. Complex shaping, bidirectional text, complete Unicode line breaking, remaining rich text types and font files remain pending; text-only format sections support different sizes and colors; external PBF glyph services are connected.

map.setLayoutProperty('labels','text-max-width',10);
map.setLayoutProperty('labels','text-line-height',1.5);
map.setLayoutProperty('labels','text-letter-spacing',0.1);
map.setLayoutProperty('labels','text-justify','center');
map.setLayoutProperty('labels','text-transform','uppercase');
map.setLayoutProperty('labels','text-size',['interpolate',['linear'],['zoom'],0,12,10,24]);
await map.sqs.flush({render:true});

Property names,units and defaults reference theMapLibre style specification. Execution and typography use SqsMap's shared core. Legacy label entry points retain their behavior. Thecomplete HTML exampleincludes every button handler. Standalone SDK code uses map.flush;script code uses map.sqs.flush.

Shared fill-extrusion buildings (08.14)

GeoJSON/MVT Polygon/MultiPolygon support fill-extrusion. Height/base are metres above the flat ground plane,default0,range0..100000;base must not exceed height. Color defaults black,opacity1,vertical-gradienttrue. Common attribute/state/zoom expressions provide numeric,color and boolean values. Metres are converted to display height at the polygon's latitude.

map.addLayer({id:'buildings',type:'fill-extrusion',source:'your-polygons',paint:{
 'fill-extrusion-height':['get','height'],
 'fill-extrusion-base':0,
 'fill-extrusion-color':'#ef4444',
 'fill-extrusion-opacity':1,
 'fill-extrusion-vertical-gradient':true
}});
map.setPitch(45);
await map.sqs.flush({render:true});

Common layer/source identity,filter/order,zoom,state and property updates are retained. Flat views show the roof footprint/courtyard holes. Perspective views use actual roofs,outer/courtyard walls and existing 3D lighting. The gradient multiplies bottom wall RGB by0.75 and preserves top color. Alpha/opacity combine once. Picking uses the same mesh,clips near/far depth and tests roof/wall projection. Hidden/transparent layers do not hit;depth occlusion between other layers is not additionally promised.

Each geometry is limited to500K vertices/1.5M indices,visible rendering retains the scene index budget,and queries inspect at most500K triangles. Mercator latitude is limited to±85.051129. Invalid height pairs/candidate budgets reject before updates. Globe,real terrain,draped buildings,patterns/translate and other standard properties remain pending;unsupported properties reject explicitly. MVT buffer clipping/fragment deduplication remain source tasks. Consumers provide UI. Thecomplete HTMLcontains all button handlers.

Elevation tiles and hillshade (07.12 / 08.15a)

Common raster-dem sources now decode lossless PNGs using mapbox (default), terrarium or custom. Heights in meters are -10000 + (R65536+G256+B)0.1 for mapbox, and R256+G+B/256-32768 for terrarium. Custom uses RredFactor+GgreenFactor+B*blueFactor+baseShift; channel factors default to 1 and baseShift to 0. Custom fields are rejected for other encodings. Factors and decoded heights have an absolute limit of 1e8. Transparent pixels are nodata, not zero-height terrain. JPEG is rejected.

map.addSource('dem',{
 type:'raster-dem',tiles:['https://your-provider/{z}/{x}/{y}.png'],
 tileSize:512,encoding:'terrarium',attribution:'Data provider credit'
});
map.addLayer({id:'shade',type:'hillshade',source:'dem',paint:{
 'hillshade-method':'basic',
 'hillshade-illumination-anchor':'map',
 'hillshade-illumination-direction':335,
 'hillshade-illumination-altitude':45,
 'hillshade-exaggeration':0.5
}});
await map.sqs.flush({render:true});

08.15c adds standard/viewport defaults and five single-light algorithms. Omitted paint now runs; multiple-light arrays still reject. Supported inputs: one direction 0..359 degrees, altitude 0..90, intensity 0..1 and shadow/highlight/accent colors. Numbers/colors use common zoom expressions; feature properties, state and filters are unavailable. Zero degrees means north; 90 means east. Our implementation mixes shadow/highlight using metric slopes and cosine illumination, then applies an accent with 25% slope weight. Intensity controls final alpha. Pixel equivalence with other engines is not promised.

Native and WASM share decoding and cached derived pixels, public layer ordering, visibility, zoom range and revisions. Paint changes update the GPU texture revision. PNG responses are limited to 4 MiB and decoded sides to 1024 pixels. Original RGBA plus f32 heights count toward the map-wide 64 MiB tile cache; derived RGBA counts toward the 128 MiB layer budget. Registration alone downloads nothing; use actual source events/tileState for readiness.

TileJSON, setTiles/setUrl, bounds, zoom limits, credits and common volatile policy are supported. getSource returns a raster-dem handle; source.sqs.getTileMetadata().encoding identifies the decoder. Source tileState includes height memory. Pixel layers have no queryable vector features or feature-state. Supply a real provider URL and display its required credits.

Tile borders currently use one-sided differences. Neighbor stitching, multiple-light arrays, globe and mobile device acceptance remain pending. Hillshade colors the DEM; setTerrain separately enables real meshes/elevation queries and allows this coloring to drape.

Names follow the MapLibre source specification and hillshade specification. SqsMap implements its own shared decoder and rendering.

Elevation coloring and common CSS colors (08.15b)

color-relief uses a common raster-dem source. color-relief-color accepts a constant CSS color, a linear interpolate ramp or a step ramp with "elevation" in meters. Up to 32 strictly increasing numeric stops are supported, each with an absolute limit of 1e8 and a constant color. Default is black. Linear interpolates RGBA and clamps outside its range; step adopts the new color at a threshold. Negative heights are supported; transparent nodata stays transparent. The input is scoped to the elevation ramp and does not become a feature field.

map.addLayer({id:'relief',type:'color-relief',source:'dem',paint:{
 'color-relief-color':['interpolate',['linear'],['elevation'],
   -100,'#2563eb',0,'lime',2000,'rgb(160 100 40)',5000,'white'],
 'color-relief-opacity':0.8
}});
map.setPaintProperty('relief','color-relief-color',[
 'step',['elevation'],'blue',0,'lime',2000,'brown',5000,'white'
]);
await map.sqs.flush({render:true});

Opacity defaults to 1, ranges from 0 to 1, and accepts common zoom expressions. Final alpha multiplies color alpha by opacity once. Layers reuse decoded DEMs; palette/opacity, ordering, zoom and visibility updates change derived texture revisions without editing elevations. Vector features, feature-state and filters are unavailable. Globe, other interpolation modes, dynamic stop colors and further elevation-specific expressions remain pending. Budgets match raster-dem/hillshade.

Common circle/line/fill/fill-extrusion, symbol, background, heatmap and elevation colors now reuse the existing CSS parser: hex, 148 named colors, RGB/RGBA and HSL/HSLA, case variations, percentages, whitespace/slash syntax and angle units. Interpolation uses sRGB bytes. Invalid/nonfinite inputs reject; other color spaces are not promised.

The complete HTML/CSS/button example uses SqsMap-generated demonstration heights, not real terrain. Replace the source and display provider-required credits. Naming follows the MapLibre color-relief specification; SqsMap implements its own rendering.

Default hillshade and light anchors (08.15c)

A hillshade layer runs without paint. Defaults are standard/viewport, direction 335 degrees, altitude 45 degrees and exaggeration 0.5. Supported single-light configurations are standard, basic, combined, igor and multidirectional. Existing direction/altitude ranges, colors, intensity and zoom expressions remain available. Multiple-light arrays are pending and reject explicitly.

map.addLayer({id:'shade',type:'hillshade',source:'dem'});
map.setBearing(90);
map.setPaintProperty('shade','hillshade-illumination-anchor','map');
map.setPaintProperty('shade','hillshade-method','combined');
await map.sqs.flush({render:true});
// null restores standard/viewport defaults.
map.setPaintProperty('shade','hillshade-method',null);
map.setPaintProperty('shade','hillshade-illumination-anchor',null);

Map lighting follows north. Viewport lighting follows the top of the viewport; current bearing is added to the actual illumination direction. Only derived layer textures refresh. Source data revisions, resource requests, credits and source events do not change due to the lighting view parameter. Native Session, browser renderChecked and desktop common-layer refresh apply the same rule before drawing. Layer instances, ordering and original DEMs remain stable.

SqsMap implements its own algorithms: standard mixes direction/altitude lighting and slope coverage; basic uses cosine illumination; combined applies a further slope factor; igor uses reduced coverage; a single-light multidirectional configuration uses cosine illumination. Accent uses a 25% slope weight, and intensity multiplies final alpha. Input shapes follow the reference specification; pixel equivalence with other engines is not promised.

Neighbor stitching, multiple-light arrays, globe and device acceptance remain separate tasks. Budgets are shared with elevation sources/coloring.

Common terrain surfaces and elevation queries

setTerrain/getTerrain/queryTerrainElevation use common raster-dem sources, our decoder and the existing real shared-depth/lighting/picking renderer. No display layer is required. Select Mercator first. Registration downloads nothing; terrain binding schedules visible tiles. Exaggeration defaults to 1 and ranges from 0 to 20. Null disables terrain and releases its reference.

import {map} from 'sqsmap';
map.setProjection({type:'mercator'});
map.addSource('dem',{type:'raster-dem',tiles:['https://your-provider/{z}/{x}/{y}.png'],
 encoding:'terrarium',attribution:'Provider credit'});
await map.setTerrain({source:'dem',exaggeration:1.5});
map.setPitch(45);
await map.sqs.flush({render:true});
const config=await map.getTerrain();
const height=await map.queryTerrainElevation([116.4,39.9]);
map.on('terrain',event=>{console.log(event.terrain);});

Common queries return number/null: bilinear decoded meters multiplied by current exaggeration. Disabled, unloaded, nodata or unavailable tiles return null, never implicit zero. Queries read the highest cached level and fetch nothing. Bound source instances remain stable; removing a referenced source conflicts, and disabling releases it. getTerrain is normalized configuration; getSourceInfo supplies actual readiness/errors/revisions. Terrain events contain historical configuration in terrain, or null. Credits participate in getAttributions; callers display them.

Enabling preserves logical camera settings and does not automatically change pitch. Display LOD grids contain up to 33x33 vertices per tile with latitude-scaled meter heights, real triangle normals and matching surface indices. Bilinear data query precision differs from mesh LOD. Conservative CPU mesh/raster/pick accounting is limited to 32 MiB; displayed z has an absolute limit of 1e9 Mercator meters. Shared PNG/elevation cache budgets apply. Style objects support the terrain root; full replacement updates/disables terrain and cancels old tile work.

Current scope is Mercator plus existing GeoJSON circle/line/fill 3D paths. Common tile rasters and hillshade/color-relief now drape in layer order. Registered image/video/canvas convex quads also drape; independent URL raster styles, fill-extrusion and symbol/heatmap/background combinations retain explicit limits. Disable terrain before globe. Neighbor stitching/skirts, adaptive LOD, overlay draping/elevated focus/terrain gesture anchors and overlay queries, and native/mobile device acceptance remain separate work; complete MapLibre terrain compatibility is not claimed.

The desktop map.sources.add local DEM path remains available. Its getTerrain may return TerrainState; queryTerrainElevation returns a TerrainElevation object with raw height, datum and nodata reason, without display exaggeration. Use common map.addSource for consistent cross-platform number/null results. Local file extensions remain capability-dependent. Standalone C ABI reuses source JSON commands setTerrain/getTerrain/queryTerrainElevation, adding no ABI symbols.

Names follow MapLibre setTerrain and elevation query. SqsMap implements its own meshes and rendering.

Terrain project/unproject now use actual display triangles and explicitly reject missing surfaces; see terrain coordinates. Common tile rasters and DEM coloring drape in layer order without double opacity; the complete imagery example includes every control and credit. Browser reset disables terrain, removes common layers/sources and resets pitch before geographic projection. Ordinary paint adds cubic Bézier interpolation and scalar math; remaining special values/properties stay separate work.

Additional ordinary style expressions

rgb/rgba/to-rgba provide property colours and channel arrays; at/length/in/index-of/slice/split/join/upcase/downcase provide bounded array and Unicode string operations. interpolate accepts equal-length numeric/colour arrays; interpolate-lab/hcl provide CIE Lab D50/HCL colours and colour arrays. HCL uses the shorter hue path, separate alpha interpolation and byte RGB clipping. String indices count code points, scans consume the 8192-step budget, and join/case output is capped at4096 bytes. Colours remain CSS strings, with no distinct Color value. Dedicated heatmap-density/elevation ramps retain their own scope. See the website colour, lookup and colour-space guides for limits.

is-supported-script checks the current simple shaping capability; complex scripts conservatively return false and font coverage is not guaranteed. See writing-system legibility.

Shared MVT point/line geometry clipping is connected in07.14a; raw source queries stay separate from rendered queries. Polygon/pixel clipping and merging remain pending. See buffer geometry.

Shared MVT polygon/hole clipping and explicit transparent fill outlines are connected in07.14b. Raw source queries remain intact; stroke pixel boundaries and merging remain pending. See polygon clipping.

08.36: text-anchor, text-justify and text-transform accept string expressions evaluated per feature from attributes/global state. Drawing, collision and queries share the refreshed layout. Invalid result types/enumerations reject the candidate; layout still rejects feature-state. Text enum guide.

08.37: Shared icon-offset accepts x,y constants/array expressions in logical pixels, multiplied by actual icon-size and rotated with icon-rotate. Icon-anchor accepts string expressions for nine anchors. Attribute/global-state updates refresh drawing, collision and queries. Offset guide.

08.38: allow-overlap/ignore-placement/optional accept boolean expressions; icon/text padding accepts numeric expressions in0..128. Policies evaluate per feature. Shared dynamic auto ordering scope is documented in the policy guide.

08.39: Shared symbol layout uses floor(zoom), while paint remains fractional. Icon/text/collision/order/missing-image demand shares this rule. See layout zoom.

Shared icon-image supports availability-aware typed images, reported by commonIconImageExpressions=true. Missing image values are actual null for coalesce; plain strings and empty strings keep their existing semantics. Lexical/lazy scope and the8192-step budget are reused; RGBA/SDF parameters and queries remain unchanged. Only attempted names contribute missing notices, and native/WASM image candidates validate before commit. See icon image expressions. Patterns, downloads, URL-style expression domains and3D remain outside this capability.

Shared planar fill-pattern supports one uniform RGBA pattern per layer, reported by commonFillPatternsUniform. It supports image/coalesce/lexical variables and integer zoom/global-state, with a world-anchored CSS period. It ignores fill-color, multiplies fill-opacity, draws/hits nothing when missing, and restores solid fill/outlines on null. Current image dimensions are powers of two from2 through512; per-feature, SDF, transitions, line and3D combinations remain unsupported. See fill patterns.

Shared number-format supports five standard options for en-US/zh-CN/de-DE/fr-FR/ja-JP, defaults to en-US and reports commonNumberFormat. Option expressions, currency precision,45 simple units/compound units,negative zero,rounding and atomic failure share one implementation. CLDR48.2 license/source records ship with the SDK;other locales and collator remain pending. See number formatting.

Shared typed collator supports five locales,three options,four sensitivities,six comparison third arguments,typeof and resolved-locale,reported by commonCollatorSearch. It uses real Unicode search data without JSON impersonation;dynamic scopes/options,budgets and atomic failures share common semantics. Existing two-argument comparison stays compatible. See locale-aware comparisons;other locales,complete styles and device acceptance remain pending.

Shared 2D line-dasharray supports 1–16 nonnegative width multiples or shared expressions, with a positive cycle and the existing 100 CSSpx per-entry / 24px dynamic-width preflight. Odd arrays join their last and first painted segments; dash zoom uses integer values. Property/state/global/lexical/branch updates preserve identity; invalid candidates preserve old content. Queries retain whole-line envelopes. Capability commonLineDashArrays; full pitched/globe dashes and cap/join options remain pending.

Shared scripts import Evented from sqsmap and use local map.fire/setEventedParent/custom events. Mixed map/Evented parent chains, upward listens, once/unsubscription and terminal/session cleanup follow custom events. Metadata cannot replace type/target; project budgets and actual host-event availability remain explicit. Local events neither mutate the map nor provide UI.

Shared line symbols (commonLineSymbols)

A matching development SDK returning commonLineSymbols=true supports symbol-placement=line/line-center for original GeoJSON/MVT line parts in the shared planar Mercator view (zero pitch/roll, no terrain/extrusion). symbol-spacing defaults to 250 CSS pixels; text-max-angle to 45 degrees; text-keep-upright/icon-keep-upright to true/false. map/auto follows road tangents, viewport lays out the whole label horizontally, and viewport-glyph places text centers along the curve while keeping glyph axes oriented to the viewport. Explicit multiline labels reject. Existing PBF, RGBA/SDF, format, image fitting, sorting, collision and optional-group semantics apply; text that cannot fit follows text-optional and cannot leave an icon when that text is required.

Actual glyph quads and original feature/part/source identities drive placement, collision and queries. Generated anchors never modify source data. Invalid resolved symbol paint in a line candidate explicitly rejects and retains the previous frame; existing point fallback semantics remain. Legal glyph fallback keeps its existing contract. An asynchronous candidate exceeding actual template/glyph budgets rejects; legal fallback is not presented as a resource failure. MVT uses original-part arc lengths and half-open target ownership, preserving parent fallback and world copies. Radial extent relative to an anchor is limited to 1024 CSS pixels; unknown tile coverage expands by that bound while retaining 64-tile/32-request limits. Cross-tile geometry stitching, a globally consistent repetition phase along the entire road, complex shaping, tilted/globe/terrain line placement, device/external-platform acceptance and formal release remain pending.

Common paint transitions and planar playback/media

With a matching core exposing commonPaintTransitions, root transition and property-transition objects support the42 implemented uniform numeric/color/vec2 properties. Duration/delay default to300/0ms and each is bounded by3600000. Per-property fields independently override root values; controls affect subsequent retargets. Reads expose target declarations while display/query share actual-time samples. Interrupted priors keep their original clocks; compatible/no-op diffs retain identities and applicable timelines. Data/feature-state targets remain immediate. Image/dash/pattern crossfade, layout/enums/nonzeroLight and unsupported properties do not gain transitions. The current/candidate history budget, failure declaration/display distinction and full property table are documented inCommon paint transitions.

Only zero-pitch/roll Mercator views allow the newly accepted simple playback-line/media composition, with real layer order and original scene owner/playback clock. Paint writes, including controls, to a playing or paused layer retaining its visual returnCONFLICT until playback is stopped; starting playback on a layer with an active paint curve also conflicts. Other layers may transition. Existing topology/offset/gap/blur/pattern/gradient/globe/terrain animation restrictions remain. Older references to pending complete transitions do not exclude this conditional uniform-property batch; image crossfade and full style support remain pending. External platforms, devices, mobileGIS/iOS and formal publication still require acceptance.

Source feature queries

await map.querySourceFeatures(sourceId, options?) follows the public MapLibre syntax. Shared in-memory GeoJSON returns current logical source features regardless of layer visibility, layer filters or camera viewport. Geometry is not tile-clipped, and collection children do not duplicate their parent. A bare Geometry becomes a Feature; null geometry is retained. Use queryRenderedFeatures below for screen queries; feature-state remains a subsequent stage. Source queries do not represent visible-feature or selection results.

import { map } from 'sqsmap';
const handle = await map.getSource('trees');
const features = await map.querySourceFeatures('trees', {
  filter: ['==', ['get', 'enabled'], true],
  sqs: { limit: 100, offset: 0, sourceInstanceId: handle.sourceInstanceId,
    dataRevision: handle.dataRevision }
});

The result is a Feature array retaining typed properties and original geometry. id is the effective promoteId/generateId identity; numeric and string IDs remain distinct. getData retains original IDs and foreign members; querying never edits the source. source and sqs are reserved result fields that replace same-named foreign members. sqs contains sourceInstanceId, dataRevision and featureIndex, the original logical feature index.

By default the complete array must fit 1000 items and a conservative 8 MiB estimate. Oversized results report RESOURCE_LIMIT without silent truncation. Explicit sqs pages use limit 1–1000 and offset 0–50000, counted after filtering. Optional instance/revision checks reject changed snapshots with CONFLICT; absent sources report NOT_FOUND. A handle's dataRevision is its lookup-time snapshot: obtain another handle after an update, or take the revision from the first page. These pagination fields are SqsMap extensions, not a claim of MapLibre parameter compatibility.

Standard options are filter and validate:true. Unknown fields/wrong types report INVALID_ARGUMENT. validate:false and GeoJSON sourceLayer report CAPABILITY_UNAVAILABLE. Filters reuse shared expressions, including legacy field filters; source queries reject zoom and feature-state. Dynamic type/evaluation errors reject the query. Options are limited to 64 KiB, 4096 nodes and depth 32. Read-only queries emit no mutation events and preserve camera and selection; results record the source revision at execution.

C hosts send the same method/args through sqsm_map_source_json. SDK and standalone native QuickJS also expose querySourceFeatures. A matching development SDK is required; do not assume npm 0.2.0 includes it. The website host rejects mismatched SDKs explicitly. Linux C/actual QuickJS are verified. SDK main-thread/Worker routing runs against real C state with rendering fixtures; actual browser GPU, external hosts and mobile devices remain separately tracked.

geometry-type / $type normalize MultiPoint, MultiLineString and MultiPolygon to Point, LineString and Polygon for matching; returned geometry remains original. GeometryCollection is queried as one logical collection without child-by-child filtering.

Rendered feature queries

await map.queryRenderedFeatures(geometryOrOptions?, options?) follows MapLibre-style screen query syntax. Supported shared GeoJSON layers are circle, line and fill. A point is a CSS-pixel pair or x/y object; two points define a box. Omit the region to query the viewport. Results follow topmost-to-bottommost style-layer order, then reverse draw order within a layer. Hidden, out-of-range and layer-filtered features are excluded; zero-opacity geometry remains queryable.

import { map } from 'sqsmap';
const point = await map.project([116.4, 39.9]);
const underPointer = await map.queryRenderedFeatures(point, { layers: ['trees'] });
const inBox = await map.queryRenderedFeatures(
  [[point.x - 20, point.y - 20], [point.x + 20, point.y + 20]],
  { layers: ['trees'], filter: ['==', ['get', 'enabled'], true] }
);

Shared projection, bearing and padding drive geometry tests; device pixel ratio does not change query coordinates. Circles use radius, lines use width and continuous centerline geometry, and fills account for holes. Queries do not inspect GPU occlusion or dash gaps and do not confirm frame presentation; use lifecycle barriers for frames. Multiple hit children of one collection return its original logical parent once per layer. Filtering uses eligible display children, while output retains parent geometry and properties. The same feature in different style layers appears separately.

Results are Feature arrays. layer contains its style description with paint evaluated for the feature/zoom. sqs records source instance, source data revision, original featureIndex, layerInstanceId and cacheRevision (display-cache revision). source/layer/sqs are reserved result fields; getData still retains raw foreign members. Queries preserve selection and business data and emit no mutation events; camera-dependent display caches refresh to the current view.

Options are layers, filter and validate:true. Missing layers report NOT_FOUND, invalid options INVALID_ARGUMENT, and validate:false CAPABILITY_UNAVAILABLE. Spherical circle/line/fill queries share the visible triangles used for drawing. Legacy GIS layers, internal basemap layers, symbols, imagery and 3D objects are not yet query targets. A complete array must fit 1000 items and a conservative 8 MiB estimate; otherwise RESOURCE_LIMIT requests a narrower region/layer set/filter, without implicit paging. At most 100,000 display features are checked and 100,000 child geometries expanded per source. Option-node count times candidate features has a conservative 2,000,000 filter-work limit. Options are limited to 64 KiB, 4096 nodes and depth 32.

C uses queryRenderedFeatures through sqsm_map_layer_json; SDK, standalone native scripts and common scripts share the method name. Windows/macOS, actual browser GPU and mobile-device acceptance remain external checks. Core geometry, application CPU host, actual QuickJS, SDK/website main-thread/Worker routing and Linux hardware readback evidence are tracked separately.

Feature state and selection/hover appearance

setFeatureState(target, state), await getFeatureState(target) and removeFeatureState(target, key?) follow the public MapLibre Map syntax with SqsMap's own shared implementation. They work with shared GeoJSON and MVT circle/line/fill layers. State does not edit business attributes. Applications build their own tool UI, query points/boxes and assign custom selected/hover keys.

import { map } from 'sqsmap';
map.addSource('trees', {type:'geojson', data:{
  type:'Feature', id:'oak', properties:{name:'Oak'},
  geometry:{type:'Point', coordinates:[116.4,39.9]}
}});
map.addLayer({id:'trees',type:'circle',source:'trees',paint:{
  'circle-radius':['case',['coalesce',['feature-state','hover'],false],12,6],
  'circle-color':['case',['coalesce',['feature-state','selected'],false],'#ff8800','#008800']
}});
await map.sqs.flush();
const pixel = await map.project([116.4,39.9]);
const hits = await map.queryRenderedFeatures(pixel,{layers:['trees']});
if (hits[0]) {
  map.setFeatureState(hits[0],{selected:true,hover:true});
  await map.sqs.flush();
  console.log(await map.getFeatureState(hits[0]));
  map.removeFeatureState({source:'trees',id:'oak'},'hover');
  await map.sqs.flush();
}

A target is {source,id,sourceLayer?}. The source must exist; GeoJSON sourceLayer reports CAPABILITY_UNAVAILABLE. Numeric and string IDs are distinct, using the same safe finite numbers/strings as sources. State may be assigned before an ID exists. Effective IDs come from raw data, promoteId or generateId. Repeated IDs and collection children share state.

Set merges top-level keys; null remains a value. Get returns an independent snapshot or {}; editing that snapshot cannot change the map. Remove with a key removes one key, with source/id clears that feature, and with source alone clears the source; a key requires id. Values may contain bounded arbitrary JSON. Missing feature-state values return null; the example uses coalesce defaults. State expressions are for paint only, never filter/layout. Invalid dynamic types/sizes use the existing paint fallback and warningCount.

stateRevision is separate from dataRevision; getData and business attributes remain unchanged. Explicit stable-ID/promoteId state survives same-source setData/updateData. Whole-data replacement with generateId clears state to avoid selecting a different feature after index changes; reapply against new IDs. Source deletion clears state and same-name recreation cannot inherit it. Query features can be targets: supplied sqs.sourceInstanceId/dataRevision are checked and stale targets report CONFLICT. Direct source/id targets refer to the current source.

Rendered queries return a state snapshot and sqs.stateRevision. source/layer/sqs/state are reserved query fields; getData retains original foreign members. State affects actual drawing, query radius/width and evaluated paint. Only state-dependent style caches need refreshing. State does not automatically edit legacy GIS selection records or attributes. Successful writes use bounded sqs.sourceschange notifications for committed changes; frame presentation has its own lifecycle contract.

Limits: 16 KiB, 256 top-level keys, 4096 nodes and depth 32 per state; targets have a 64 KiB/4096-node/depth-32 limit. A map permits 10,000 nonempty targets and a conservative 4 MiB resident estimate; empty state does not occupy a target. Input, merged-result, display/GPU preparation, output allocation or event-budget failure rejects the entire mutation. Rendered result limits of 1000 items/8 MiB include state and evaluated layer descriptions.

SDK, C sqsm_map_source_json, independent one-shot/retained QuickJS and common application/web scripts share the names. A matching development SDK is required; npm 0.2.0 does not automatically provide these methods. Linux RTX pixels, an independent C11 host, actual application QuickJS and SDK/website main-thread/Worker routing have separate evidence. Actual software GPU browser MVT state acceptance is recorded in07.9; mobile devices, hardware GPU and external Windows/macOS acceptance remain separate. Standard pointer/layer subscriptions continue in the event phase; this example does not assume they exist.

MVT source-layer state

A vector tile target requires {source,sourceLayer,id}. The same ID in different source layers is independent; numeric/string IDs remain distinct. State may be set before a feature is downloaded. Same-source refreshes and view movement preserve it; source deletion/recreation clears it. Optional sqs source/data revision checks follow GeoJSON rules. State does not change dataRevision or business attributes.

map.setFeatureState({source:'roads',sourceLayer:'transportation',id:1},{selected:true});
await map.sqs.flush({render:true});
console.log(await map.getFeatureState({source:'roads',sourceLayer:'transportation',id:1}));
map.removeFeatureState({source:'roads',sourceLayer:'transportation'}); // Clear this sublayer
map.removeFeatureState({source:'roads'}); // Clear the entire source

Rendered queries return the matching source-layer state snapshot; feature-state drives actual paint. Same-layer IDs share state across tiles/world copies, without merging query geometry. Raster/media sources explicitly reject feature state. Existing GeoJSON state budgets apply.

The complete HTML state example includes selection,queries,view switching,credits and disposal. The portable script verifies isolation,clearing and actual pixels through native and real WASM main-thread/strict Worker. Pitched views retain distance fog. Mobile host and external-platform acceptance remain pending.

Shared planar composition capability

When a matching SDK reports commonPlanarLayerComposition:true, the shared Mercator view with zero pitch/roll and no terrain/extrusion combines media, backgrounds, labels, heat and supported static line/fill effects in complete layer order. This capability supersedes earlier media-combination exclusions only within that shared planar scope. Custom line animation, tilted/globe/terrain views and other unsupported material combinations retain their respective limits. Reordering and compatible diff preserve identities, caches, pending requests and fade clocks; material, geometry and texture budgets are validated before publication. See planar layer composition.

Shared line symbols (commonLineSymbols)

A matching development SDK returning commonLineSymbols=true supports symbol-placement=line/line-center for original GeoJSON/MVT line parts in the shared planar Mercator view (zero pitch/roll, no terrain/extrusion). symbol-spacing defaults to 250 CSS pixels; text-max-angle to 45 degrees; text-keep-upright/icon-keep-upright to true/false. map/auto follows road tangents, viewport lays out the whole label horizontally, and viewport-glyph places text centers along the curve while keeping glyph axes oriented to the viewport. Explicit multiline labels reject. Existing PBF, RGBA/SDF, format, image fitting, sorting, collision and optional-group semantics apply; text that cannot fit follows text-optional and cannot leave an icon when that text is required.

Actual glyph quads and original feature/part/source identities drive placement, collision and queries. Generated anchors never modify source data. Invalid resolved symbol paint in a line candidate explicitly rejects and retains the previous frame; existing point fallback semantics remain. Legal glyph fallback keeps its existing contract. An asynchronous candidate exceeding actual template/glyph budgets rejects; legal fallback is not presented as a resource failure. MVT uses original-part arc lengths and half-open target ownership, preserving parent fallback and world copies. Radial extent relative to an anchor is limited to 1024 CSS pixels; unknown tile coverage expands by that bound while retaining 64-tile/32-request limits. Cross-tile geometry stitching, a globally consistent repetition phase along the entire road, complex shaping, tilted/globe/terrain line placement, device/external-platform acceptance and formal release remain pending.

Common paint transitions and planar playback/media

With a matching core exposing commonPaintTransitions, root transition and property-transition objects support the42 implemented uniform numeric/color/vec2 properties. Duration/delay default to300/0ms and each is bounded by3600000. Per-property fields independently override root values; controls affect subsequent retargets. Reads expose target declarations while display/query share actual-time samples. Interrupted priors keep their original clocks; compatible/no-op diffs retain identities and applicable timelines. Data/feature-state targets remain immediate. Image/dash/pattern crossfade, layout/enums/nonzeroLight and unsupported properties do not gain transitions. The current/candidate history budget, failure declaration/display distinction and full property table are documented inCommon paint transitions.

Only zero-pitch/roll Mercator views allow the newly accepted simple playback-line/media composition, with real layer order and original scene owner/playback clock. Paint writes, including controls, to a playing or paused layer retaining its visual returnCONFLICT until playback is stopped; starting playback on a layer with an active paint curve also conflicts. Other layers may transition. Existing topology/offset/gap/blur/pattern/gradient/globe/terrain animation restrictions remain. Older references to pending complete transitions do not exclude this conditional uniform-property batch; image crossfade and full style support remain pending. External platforms, devices, mobileGIS/iOS and formal publication still require acceptance.

Shared style and runtime images (10.1b)

Common and standalone native scripts use the same methods. Loaded style sprites and runtime images support queries, same-size updates and removal. Input bytes are captured at submission. Reads return caller-owned editable Uint8Array copies; missing IDs return undefined. Updates retain pixelRatio/sdf; remove/re-add creates a new identity.

map.addImage(id: string, image: MapImageData, options?: MapImageOptions): Map
map.updateImage(id: string, image: MapImageData): Map
map.removeImage(id: string): Map
map.getImage(id: string): Promise<MapImageSnapshot | undefined>
map.hasImage(id: string): Promise<boolean>
map.listImages(): Promise<readonly string[]>
map.sqs.getImageInfo(id: string): Promise<MapImageInfo | undefined>

Shared declarations include MapImageData, MapImageOptions, MapImageSnapshot and MapImagesEvent; use matching type attachments. Limits are128 user registrations/overrides,1..512 pixels per edge,1MiB per image/16MiB per map and a2048 atlas edge; pixelRatio is0.125..16. sqs.flush confirms writes, and queries wait for earlier writes. Final script summaries retain a64KiB limit; avoid exporting entire large pixel arrays.

// @gis-api 0.5-session
// @permissions ["project.read", "map.write"]
import {map} from "sqsmap";
map.addImage("pixel", {width:1,height:1,data:new Uint8Array([0,0,255,255])});
await map.sqs.flush();
const image=await map.getImage("pixel");
image.data.data.set([255,0,0,255]);
map.updateImage("pixel", image.data);
await map.sqs.flush();
export default {ids:await map.listImages(),info:await map.sqs.getImageInfo("pixel")};

Retained scripts use map.on("sqs.imageschange", listener). Historical method/id/imageCount/runtimeByteLength describe successful explicit writes, not network progress or style readiness. Unsubscribe, stop and destruction release listeners. The SDK exposes onImagesChange; C ABI exposes sqsm_map_image_json (SQSM_MAP_IMAGES=1048576). Desktop/web hosts are connected; mobile application integration and device acceptance remain pending.

Stretchable/dynamic image options and complete fonts remain pending; unsupported options fail explicitly. Guide, identical portable script, complete HTML/CSS.

Complete missing style images on demand (10.2a)

Subscribe with map.on("styleimagemissing", listener) before setting the style. Retained scripts declare export const sessionMode="persistent". The event carries type, id, target and an opaque string styleGeneration. A visible missing ID is deduplicated; filling then removing it, leaving visible demand, or changing the style generation permits another notification. Unsubscribe, stop and destruction release listeners. Earlier notifications are not replayed.

Demand comes from actual decoded, visible and filtered vector features in the current planar view: icons and the supported constant/zoom-based fill patterns. A sprite URL is not required. After the callback adds an image and confirms the command, a subsequent frame draws it. This shared cross-thread notification does not provide a synchronous current-frame resolver. Queued events retain their historical generation; recheck current map demand before applying asynchronous completion to another style.

The standalone SDK uses view.on("styleimagemissing", listener) and the imageMissingEvents capability. C event polling uses SQSM_MAP_MISSING_IMAGES=2097152; imageEvent carries id/styleGeneration. Exhausted event capacity does not consume demand; retry after draining events. The current missing set is limited to128 IDs; overflow reports RESOURCE_LIMIT. Globe/3D demand, stretchable/dynamic images and complete fonts remain subsequent work.

Identical retained script / complete HTML/CSS and handlers / guide. Mobile application script integration, Windows/macOS, hardware and formal release retain separate acceptance.

For asynchronous web completion, compare event.styleGeneration with view.basemapStatus.styleGeneration. Compare the opaque strings unchanged; never parse them as numbers. Clearing and recreating the basemap does not reuse this identity.

The event declaration is MapStyleImageMissingEvent, with type/id/target/styleGeneration. Matching declarations are distributed with the SDK.

Load external images (10.2b)

await map.loadImage(url) downloads and decodes PNG, JPEG or WebP into {data:{width,height,data:Uint8Array}}. Pass result.data directly to map.addImage. Loading does not register an image, change a style or submit a map write. Pixels belong to the caller; editing them does not change a registered image. The name and {data} response follow MapLibre conventions; SqsMap returns portable RGBA8 pixels instead of DOM image objects.

interface MapImageLoadResult {
  readonly data: {readonly width:number; readonly height:number; readonly data:Uint8Array}
}
map.loadImage(url:string):Promise<MapImageLoadResult>

Use an absolute HTTP(S) or hierarchical custom URL with a host, without credentials, at most4096 UTF-8 bytes. HTTP downloads require CORS; custom handlers are host-owned. Format detection uses actual bytes. SVG/GIF are unsupported; EXIF orientation, color-space conversions and animation frames are not implicitly applied. RGBA8 uses straight, unpremultiplied alpha.

Each encoded image is limited to4 MiB,512 pixels per edge and1 MiB decoded pixels; at most8 loads may be active. Browser/native limits are per map; desktop background downloads also have a process-wide8-job limit. Unclaimed completed native results still occupy a slot. Caller-owned pixels retained after claiming do not count as in-flight results. Decoder internals have a separate16 MiB allocation limit.

Errors include INVALID_ARGUMENT (URL), IO_ERROR (HTTP/empty/corrupt response), CAPABILITY_UNAVAILABLE (format), RESOURCE_LIMIT (budget) and CANCELLED (cancellation). Stopping a script or destroying a map cancels its owned download; late responses cannot revive released requests. The standalone browser SDK also provides view.loadImage(url,{signal}) and transformRequest(url,ResourceType.Image); function request hooks require the main-thread host. Common scripts take only a URL, without a DOM AbortSignal.

// @gis-api 0.5-session
// @permissions ["project.read", "map.write"]
import {map} from "sqsmap";
const result=await map.loadImage("https://map.sqsh.com/sdk/sqsmap-core/load-image-fixtures/icon.png");
map.addImage("marker",result.data);
await map.sqs.flush();
export default {width:result.data.width,height:result.data.height};

The public URL requires deployment of matching attachments; local acceptance uses a local server. The identical four-entry acceptance script explicitly uses a local test URL. The complete HTML/CSS with download/cancel/remove/destroy handlers and attribution events uses adjacent fixtures; copy the complete SDK distribution directory to run it.

The native library contains no HTTP client. sqsm_map_image_json method loadImage([url]) returns a requestId. Poll sqsm_map_resource_requests_json, download resourceType=Image and use the existing resource_response entry; deliver transport failures with error/errorCode. loadImageResult([requestId]) returns pending or owned data; only successful output allocation consumes a result. Failed output preserves the request. Release an error result with cancelLoadImage([requestId]). Capability SQSM_MAP_LOAD_IMAGE=4194304; ABI1 and the export count are unchanged. Retained native scripts wait for this queue; the synchronous one-shot native script entry explicitly rejects external image loading with CAPABILITY_UNAVAILABLE. Desktop ScriptService owns background transport, so its ordinary one-shot scripts can await loading.

Desktop, browser main/Worker and Linux retained native QuickJS are connected. Mobile application scripts, Windows/macOS execution, hardware backends and formal releases retain separate acceptance. Dynamic/stretchable images and complete fonts remain subsequent work.

Cancelling a delivered pending native download emits an Image descriptor with cancel:true. The host must abort its owned I/O for that generation. Unclaimed cancellation notices retain one slot until successfully delivered, bounding the notification queue.

Shared symbol demand events and SDF recoloring

Planar common symbols now emit styleimagemissing for visible points/layers with nonzero size/opacity. Names are deduplicated;events carry id and styleGeneration. Filling then deleting an image,leaving then reentering demand,or replacing the style generation rearms notifications. Style and common-layer demand share one bounded set of at most128 missing names;output/event-budget failure does not acknowledge demand early. Listeners may addImage to complete it. Unsubscribe,stop and destruction release listeners.

import {map} from 'sqsmap';
export const sessionMode='persistent';
const subscription=map.on('styleimagemissing',async event=>{
 if(event.id!=='marker')return;
 map.addImage(event.id,{width:16,height:16,
  data:Array.from({length:256},()=>[0,0,0,255]).flat()},{sdf:true});
 await map.sqs.flush();
});
map.addSource('points',{type:'geojson',data:{type:'Feature',
 properties:{},geometry:{type:'Point',coordinates:[0,0]}}});
map.addLayer({id:'icons',type:'symbol',source:'points',layout:{
 'icon-image':'marker','icon-allow-overlap':true,'icon-ignore-placement':true
},paint:{'icon-color':'#ff0000','icon-opacity':0.8}});
await map.sqs.flush({render:true});
// Release with subscription.unsubscribe();stopping the script also releases it.

SDF images use the alpha distance field,a0.5 boundary and screen-derivative smoothing. icon-color supports common paint expressions,attributes/state/zoom and recolors sdf:true images only. Ordinary RGBA images retain their colors. Color alpha and icon-opacity combine once;zero final alpha produces no query hit. updateImage retains SDF,pixelRatio and identities;distance-image pixel updates refresh rendering. Transparent image areas still query by rectangular footprint,not a per-pixel mask.

Native retained scripts,WASM main-thread/strict Worker and the desktop host share these rules;the mobile host is pending. commonPointIconSDF/commonPointIconMissingImages are true. General text/collision/line placement,globe/pitch and common raster combinations remain pending. The complete HTML includes every image/color/demand handler and11 controls. The portable retained script demonstrates callback completion. Example images/points are generated locally.

See custom protocol image loading for URI support and host requirements.

Common setGlyphs accepts host-owned custom hierarchical templates; glyph arrival/repaint remains asynchronous. Seecustom glyph protocols.

Stretchable point icons and text backgrounds (SQSAPI-08.67)

Image options add optional stretchX/stretchY/content/textFitWidth/textFitHeight. Omitted fields remain absent in queries, preserving the existing snapshot shape. Explicit metadata returns independent copies, with nested frozen intervals/content arrays. Same-size updateImage changes pixels while retaining all metadata. Text fitting uses actual shaped bounds and fixed/stretch UV segments. The complete guide and matching SDK inventory define delivered scope and budgets. textFitWidth/Height only constrain both; stretchOnly is not a natural-image-size lower bound, and unresolved combinations are not claimed compatible.

Glyph services and bundled fallback

map.getGlyphs(): Promise<string | undefined>
map.setGlyphs(url: string | null | undefined, options?: {validate?: true}): Map

The getter reads the shared parsed style. Null, undefined or an empty string disables the service. The setter is chainable; confirm using map.sqs.flush. HTTP(S) templates require exactly one fontstack and range token, at most4096 UTF-8 bytes, without credentials, fragments or authority placeholders. validate:false explicitly reports unavailable capability. An unparsed style reports NOT_FOUND; a replacement awaiting rollback completion reports CONFLICT.

A changed service cancels older downloads and rebuilds its atlas, retaining loaded sources, tiles and runtime images. An identical URL preserves active requests. The whole ordered font stack is sent; ranges contain256 codepoints and PBF responses are bounded to4 MiB. Matching SDK/WASM, default desktop and the independent native library bundle the same HarmonyOS Sans SC font. Missing remote glyphs or an unset service use shared SDF fallback; unsupported characters can remain missing.

This does not set control locale. Style text-field selects label language. font-faces, dynamic font registration and complex shaping remain pending. Native hosts use existing sqsm_map_image_json and resource generation rules. Mobile application hosts and device validation are separate.

Guide / Identical portable script / Complete HTML/CSS and handlers.

Shared point clustering

The same source options and methods work in matched browser main/Worker SDKs, desktop scripts and native QuickJS scripts. Native C uses the existing sqsm_map_source_json channel and runtime flag SQSM_MAP_CLUSTERING (16777216). All source-handle reads return Promises across hosts, including getClusterOptions; setClusterOptions also returns Promise. Only public MapLibre descriptions are referenced; the hierarchical geometry implementation is SqsMap's own code, shared with the existing desktop clustering engine.

Options: cluster defaults to false; clusterRadius defaults to 50 logical pixels per 512-pixel world tile (0..512); clusterMaxZoom defaults to 17 and rounds fractional input (0..23); clusterMinPoints defaults to 2 (2..4096). clusterProperties supports at most eight scalar statistics: numeric +, -, *, /, min and max, boolean all/any, string concat shorthands, or custom reduce expressions, as reduce, map_expression. Mapping and reduction may return finite numbers, booleans or strings; null/array/object results are unsupported and report INVALID_ARGUMENT. Shared case/match/coalesce/to-string and arithmetic expressions can be combined. accumulated is valid only during reduction; get reads a declared statistic from the incoming child, including other statistics. The first child initializes the accumulator; parents fold child results in stable identity order and single-child levels reuse values. Custom reductions may change scalar type, but subsequent numeric/boolean operators perform no implicit conversion; concat uses shared to-string conversion. Numeric text removes integer .0, formats negative zero as 0 and uses fixed/exponent thresholds at 1e-6 and 1e21. Non-associative reductions and floating-point order need not match another engine; associative operations are recommended. Each expression is limited to 32 levels, 1024 JSON nodes and an estimated 64 KiB; each source has an estimated eight-million-node evaluation budget. Strings are limited to 4096 UTF-8 bytes each; the mapping buffer and each working level are independently bounded to an estimated 8 MiB, separately from the persistent 32 MiB index. Failed updates preserve data, index, feature state and revision. zoom/feature-state dependencies still report CAPABILITY_UNAVAILABLE. SDK capability: clusterScalarProperties; C flag: SQSM_MAP_CLUSTER_SCALARS (67108864); the numeric reduction flag remains available. setClusterOptions accepts only cluster/clusterRadius/clusterMaxZoom, and normalized unchanged options preserve the existing index and IDs.

Only Feature or FeatureCollection containing non-null Points is accepted when clustering is enabled, up to 4096 points within Mercator latitude ±85.0511287798066. Stable unique explicit/promoted IDs are recommended; missing IDs use input positions. Geometry is grouped in integer zoom levels without dateline wrapping. Radius zero groups coincident points only. Above clusterMaxZoom the original features display. Registration creates no layer UI: users attach circle layers and supply their own panels and buttons.

querySourceFeatures and queryRenderedFeatures return the actual cluster at the current integer camera zoom. A synthetic point has cluster=true, numeric cluster_id, point_count, point_count_abbreviated and configured statistics in properties. Its feature.id is 'sqs:cluster:<cluster_id>', distinct from numeric original IDs. Input IDs with that prefix, or reserved cluster properties, are rejected. getData returns original GeoJSON. getClusterLeaves(id,limit=10,offset=0) returns stable original members with source-instance/data-revision provenance; pages are 1..1000, offset 0..4096 and output ≤8 MiB (script transport retains its stricter 64 KiB budget). getClusterChildren returns the first split's children and getClusterExpansionZoom the corresponding integer zoom.

Cluster IDs belong to one source/index revision and expire with STALE_HANDLE after data or configuration changes. When disabled, cluster inspection reports CAPABILITY_UNAVAILABLE. Retain the source handle but re-query clusters after writes. Config/data changes prepare both geometry and display before commit; failures retain old data, index and map. Synthetic feature state expires on rebuild; original point state remains unless generateId replacement resets all positional state. Source dataRevision advances on a compiled cluster configuration change; the standard sourcedata/sqs.sourceschange observers apply.

Public syntax references: GeoJSON options, GeoJSONSource, GetClusterOptions, SetClusterOptions. Limits and asynchronous reads above are explicit SqsMap differences. Mobile device integration, container statistics, live sources and full scene ownership remain pending.

import {map} from 'sqsmap';
map.addSource('points',{type:'geojson',cluster:true,clusterMaxZoom:8,
 clusterProperties:{total:[['+',['accumulated'],['get','total']],['get','weight']]},data:{type:'FeatureCollection',features:[
  {type:'Feature',id:'a',properties:{weight:3},geometry:{type:'Point',coordinates:[0,0]}},
  {type:'Feature',id:'b',properties:{weight:4},geometry:{type:'Point',coordinates:[.01,0]}}
 ]}});
map.addLayer({id:'points',type:'circle',source:'points',paint:{'circle-radius':10}});
map.jumpTo({center:[0,0],zoom:0});await map.sqs.flush();
const source=await map.getSource('points');
const cluster=(await map.querySourceFeatures('points')).find(f=>f.properties.cluster);
if(cluster){
 const id=cluster.properties.cluster_id;
 const page=await source.getClusterLeaves(id,10,0);
 const children=await source.getClusterChildren(id);
 const zoom=await source.getClusterExpansionZoom(id);
 map.jumpTo({center:cluster.geometry.coordinates.slice(0,2),zoom});await map.sqs.flush();
}
await source.setClusterOptions({clusterRadius:60});
const options=await source.getClusterOptions();

Host pointer input

The shared core builds pointer snapshots from the applied planar or spherical camera and GeoJSON style-layer queries. Native hosts call sqsm_map_pointer_json; low-level WASM hosts call MapView.pointerEvent(json). SDK dispatchPointerEvent(input) queues input with bounded delivery and forwards canvas input. The eight common pointer events and layer delegation are connected in persistent native scripts and web hosts. Desktop workbench mouse input is connected. The mobile application host remains pending.

Names and the point / lngLat shape follow MapLibre MapMouseEvent. SqsMap supplies an independently implemented, serializable snapshot; there is no browser originalEvent, DOM target or asynchronous preventDefault(). Hosts decide default gestures before forwarding input.

{"type":"click","point":{"x":64,"y":48},"shiftKey":true,"layers":["trees"]}

Supported types: click, dblclick, mousedown, mouseup, mousemove, mouseenter, mouseleave and contextmenu. Coordinates use logical/CSS pixels inside the current viewport, including padding and bearing. High-DPI hosts must not substitute physical canvas pixels. Optional pointerType is mouse (default), pen or touch; button is 0–4, buttons is 0–31, altKey/ctrlKey/metaKey/shiftKey default to false. This is a pointer description, not multi-touch gesture support.

The returned JSON contains type, point {x,y}, lngLat {lng,lat}, features, cameraState and input metadata. Features use queryRenderedFeatures semantics: current visible common GeoJSON circle/line/fill layers, topmost first, original logical feature IDs/properties, feature state and source/layer instance provenance. The snapshot remains historical after later camera/source/layer changes. layers restricts this query; it does not install listeners. A missing layer reports NOT_FOUND. Existing query scan/result limits also apply.

Input JSON is limited to 8 KiB and the complete snapshot to 64 KiB. Unknown fields/types, invalid coordinates or buttons report INVALID_ARGUMENT; queue, output allocation and result-budget pressure report RESOURCE_LIMIT. Oversized results reject completely; restrict layers and retry instead of expecting truncated features.

Native SQSM_MAP_POINTER_INPUT (4096) advertises this host API. Call on the map creation thread and free each successful output with sqsm_map_bytes_free. Successful native input also publishes one event through sqsm_map_poll_events_json, with its snapshot under pointerEvent. Allocation or public-queue failure leaves sequence and notifications unchanged. Drain the queue before retrying. Independent subscriptions retain their existing bounded-queue overflow rules. Physical input does not enter the script command journal, change selection, advance animation or apply gestures.

Complete C host · Public header

WASM capabilities().pointerInput advertises the snapshot binding. pointerEvent() returns its snapshot immediately; the host owns delivery scheduling and listener routing. It does not append to cameraEvents. Shared planar and spherical circle/line/fill queries are supported; arbitrary 3D queries remain pending. The matching WASM and generated native references are included in the development SDK snapshot, not a formal release.

Acceptance covers a real Linux hardware GPU C map, rotated/padded camera, logical high-DPI WASM input, ordered feature hits, historical snapshots, invalid input, exhausted output buffers, full queues and 64 KiB rejection/recovery. The next connected stage also validates the same persistent script in native/main-thread/strict Worker hosts and trusted physical mouse compatibility. External Windows/macOS, mobile and browser hardware WebGPU remain pending.

Pointer events and layer delegation

The facade supports click, dblclick, mousedown, mouseup, mousemove, mouseenter, mouseleave and contextmenu. Check map.sqs.events() for actual host input support. Persistent native-library scripts require the host to submit input; the web SDK forwards canvas input. The desktop workbench forwards mouse input from its map viewport. The mobile application host remains pending; declarations alone do not guarantee support.

import { map } from 'sqsmap';
export const sessionMode = 'persistent';

if (map.sqs.events().includes('click')) {
  const show = event => console.log(event.lngLat, event.features);
  const subscription = map.on('click', 'trees', show);
  map.once('click', ['trees', 'roads'], event => console.log(event.point));
  // To stop: subscription.unsubscribe();
  // Or map.off('click', 'trees', show);
}

on(type, listener) receives map-wide input. on(type, layerId | layerIds, listener) only receives hits in the selected layers and filters features accordingly. For ordinary hit events, missing layers or misses remain silent and do not consume once; mouseenter/mouseleave use hit transitions, with empty leave features; subscriptions follow a layer recreated with the same public ID. Layer arrays are normalized and deduplicated. The same function, event and layer set are registered once. off matches that function and normalized set; once(type, layers) returns a Promise. Delegation is limited to the eight pointer event types above.

point uses viewport logical pixels {x,y}; lngLat is {lng,lat}, or null for viewport points without a ground location (outside the world in the latitude/longitude view, or where a pitched view has no ground hit). Events include type, target, mapInstanceId, sequence, features, cameraState and input. Snapshots are immutable and historical. target is the current map object/reference, not a DOM node. Hits cover currently visible GeoJSON circle/line/fill layers, topmost first; symbol layers and complete 3D queries remain pending. There is no DOM originalEvent or preventDefault().

Forwarding input does not enter the script command journal, select features, apply gestures or advance animation. SDK pointer subscriptions, queued inputs and pending asynchronous callbacks each have a budget of 32. Pressure reports RESOURCE_LIMIT; releasing it permits recovery. Stopping a script or destroying a map cancels listeners and waits.

Complete common script and host input guide. Standalone SDK on returns a callable cancellation function that also supports .unsubscribe(); common scripts return Subscription. Legacy SDK numeric-layer clicks retain feature and x/y; their coordinate tuple is now legacyLngLat, while standard lngLat is an object. The legacy configured context-menu callback retains its original shape.

SDK interactive: false disables default gestures while preserving pointer event forwarding for caller-owned interaction.

Desktop workbench input

The desktop workbench routes mouse input inside the map viewport through the same shared query, supporting the eight events and the same layer-based on/off/once overloads. Foreground tools consume their own input. Coordinates subtract the actual viewport offset; repeated layout passes deliver a frame only once. Primary release produces click, double-click adds dblclick, secondary release produces contextmenu, and middle buttons only produce down/up. The desktop host retains its default navigation and selection behavior.

Input queries are enabled only while scripts are active. Each frame retains its previous session recipients, so a newly started script cannot receive old queued input. Stop, rerun and shutdown clear waits and listeners. The desktop input queue holds 128 entries; overflow terminates affected scripts with RESOURCE_LIMIT instead of delivering a truncated event sequence. Independent Rust hosts must explicitly enable pointer_input before starting ScriptService and drive input and polling; the default service does not advertise physical input support.

Desktop queries cover common planar GeoJSON circle/line/fill and spherical circle/line/fill layers. Complete 3D queries and multi-touch remain pending. egui touch-emulated pointers can carry touch metadata; this does not establish mobile-host or physical touch-device acceptance. Native pen identification and Windows/Linux Meta-key mapping also remain pending.

Local acceptance uses the actual Linux workbench, hardware GPU and QuickJS with input injected at eframe's RawInput boundary: the unchanged cross-platform script passes 38 assertions, followed by old-input isolation, fresh-input and stopped-callback checks, plus actual circle pixel readback. This does not establish OS HID, Windows/macOS or mobile-device acceptance.

Spherical event coordinates

cameraState uses the globe branch of MapCameraState, preserving the camera and logical viewport at notification time. lngLat is {lng,lat} on the ground and null when the pointer ray misses the sphere. The SDK compatibility tuple legacyLngLat is also null in the sky. Check before reading coordinates:

map.on('click', event => {
  if (event.lngLat === null) { console.log('Sky'); return; }
  console.log(event.lngLat.lng, event.lngLat.lat, event.features);
});

Sky and an empty feature hit are different: ground without a layer still has coordinates, and a circle billboard can extend beyond the globe edge. Spherical hits use shared circle/line/fill queries; symbols and complete 3D queries reject explicitly. See the complete spherical pointer script. This stage checks the same 38 assertions in actual native QuickJS and browser main/strict Worker at DPR1/2. Desktop acceptance uses eframe RawInput, actual circle pixels and rerun/stop isolation on software Vulkan; the probe clears caller-owned selection to prevent business panels covering later input. Product selection behavior is unchanged. Real touch/pen and the mobile host remain separate acceptance work.

Shared spherical line/fill drawing, filters, order and queries: spherical surfaces.

Layer enter/leave semantics are connected; see that guide for local evidence.

Delegated mouseenter/mouseleave follow each subscription’s mouse/pen hit transitions; touch does not create hover. Leave has empty features. Unsubscribe/once/rerun/destruction clear state. Low-level mouseleave accepts finite points outside the viewport and returns lngLat:null; other input stays inside. Style changes do not fabricate input: the next snapshot determines transitions, following recreated IDs.

Planar snapshots without ground coordinates

A valid input inside a planar viewport can lack usable ground coordinates: the converted longitude/latitude may fall outside the world, or a pitched Web Mercator pointer ray may have no valid ground intersection. In that case lngLat is null; the event still succeeds and still runs queryRenderedFeatures. Missing ground coordinates do not fail the complete event or suppress features. Globe sky and mouseleave can also return null. Check event.lngLat === null before reading coordinates and inspect event.features independently as needed.

Actual map frames and idle

await map.sqs.lifecycleState() returns initialized, firstLoadCompleted, frameSequence, idle and work (pendingResources, pendingSources, animating, pendingRender, failed). await map.loaded() requires an actual frame, no pending work and no terminal source/style failure. Reads neither draw nor sample animations.

const next = map.once('idle');
map.setPaintProperty('trees', 'circle-color', '#00ff00');
await map.sqs.flush({render:true});
const event = await next;
console.log(event.lifecycle.frameSequence, event.cameraState);

Subscribe before changing content: an asynchronous host may already have drawn the new content before a later query. Render denotes a successful actual frame; repeated drawing of stationary content does not repeat idle. A terminal load failure may end busy work while loaded remains false. Frozen historical event state does not follow later camera/source changes.

Native libraries publish when the host explicitly draws or requests a render barrier. Ordinary desktop map presentation and autonomous browser drawing also produce render/idle; scripts do not need pixel readback. Desktop receipts originate from completion of an actually executed GUI GPU submission; discarded or unsubmitted passes are not frames. Receipts preserve the camera and content being drawn, and old frames cannot make newer changes idle. Actual frame/callback counts can differ; stationary redraws do not repeat idle. Full style lifecycle, the mobile host remain task04 work. Work state for legacy desktop 3D/terrain scenes still needs integration.

The standalone SDK also provides await map.lifecycleState(). The SDK supports standard load; scripts on an existing map inspect firstLoadCompleted. SDK remove fires once after destruction starts; use only its historical payload, as other methods and pending waits cancel. The SDK admits render/load/idle listener batches against its 32-promise budget. Pressure logs RESOURCE_LIMIT and preserves undelivered load/idle for a later actual frame; old render notifications are not replayed. Rejected foreign C pixel/receipt output does not advance the successful frame or consume idle; release output budget before retrying.

Standard load fires once after necessary resources and the first complete actual drawing. Animation may still run; idle also waits for animation to finish. firstLoadCompleted remembers the initial load and does not reset after camera or style changes. loaded() describes current work, so these states serve different purposes.

Attaching a script to an already loaded map does not replay load. Subscribe first, then read await map.sqs.lifecycleState(); proceed immediately if firstLoadCompleted is true. ready() only indicates the host connection. SDK destruction sends one remove snapshot even before successful drawing; stopping a script releases its session rather than destroying the map. Shared script remove is connected; complete style lifecycle remains pending.

The legacy C constructor load notification remains compatible. The standard first-frame notification is sqs.load in raw C events and maps to load in persistent native scripts, avoiding duplicate delivery. This follows the MapLibre event semantics; SqsMap implements its own engine.

Empty planar maps also support await map.sqs.flush({render:true}). The desktop host waits for an actual GUI GPU frame completed after the request and returns its historical camera. Having no exportable layer does not prevent rendering confirmation. Even a stationary map requires a new actual frame; unsubmitted drawing or an old frame cannot satisfy the request. The barrier still does not wait for every network resource or animation to finish.

When the host destroys the actual map, common scripts receive one remove with a frozen historical camera and lifecycle snapshot. New map/host operations from the callback return CANCELLED; old requests, subscriptions and waits are cleaned up. Final callbacks have a two-second execution budget: suspended callbacks time out with RESOURCE_LIMIT, and callback failures report INVALID_ARGUMENT while map release still completes. The browser also terminates a nonresponsive script worker after 2.5 seconds. Stopping a script or clearing a project does not emit remove because the map remains alive. Desktop exit, SDK destruction and native C map destruction share this contract; the mobile host remains pending.

Shared route and line playback

map.sqs.animations.route(options,{owner:scene}) and map.sqs.animations.line(options,{owner:scene}) return map-owned Playback controllers. Use map.sqs.playbacks.get/list/remove for generic lookup/release. Controllers expose getState/play/pause/stop/seek/finished and publish immutable historical sqs.playbackchange events. Script exit does not remove scenes/controllers. Explicitly clean them up; map destruction cancels waits. One-shot scripts can wait only for already completed rounds; future completion needs a retained session.

Public types include PlaybackSnapshot, Playback, RoutePlaybackOptions, RoutePlaybackSnapshot, LinePlaybackOptions, LinePlaybackSnapshot, LineEffect, LineTiming, LineTracks and PlaybackChangeEvent. Snapshots discriminate kind=route/line. Nested line layer/frame/color/pattern values are immutable. Layer names bind the current instance asynchronously; mutable settings are captured before the first await.

Lines require loaded static non-clustered GeoJSON, lineMetrics:true, planar two-dimensional line layers and explicit source ownership. Six effects (flow/dash/reveal/arrows/glow/pattern), color/opacity/width tracks and shared timing are available. Pattern resolves caller-registered addImage names. Globe/3D lines are unavailable. Applications provide their own UI; samples never rewrite original paint or source data.

scenePlayback/sceneRoutePlayback/sceneLinePlayback/sceneTracks are true; missing/false still means unavailable in that host. External revisions or stale identities fail without overwriting user edits. Stop, replay, removal and destruction cancel waits for the current epoch; loops require explicit cancellation. Ordinary samples retain geometry caches. Complete scripts, HTML/CSS/events and device acceptance scope: route playback and line playback and style tracks.

Common resource request configuration

Browser scripts and the desktop debugger share map.sqs.resources. Native-library persistent scripts expose the same methods. This is a SqsMap extension; resource kinds and transformRequest terminology follow MapLibre. The mobile application script host is not integrated yet; a mobile-buildable library does not establish mobile script acceptance.

MethodResult and purpose
await map.sqs.resources.configure(configuration)Replace the whole configuration and return an immutable accepted snapshot; omitted fields reset to defaults
await map.sqs.resources.getConfiguration()Read the current deeply immutable configuration
await map.sqs.resources.plan(query)Read a request plan without sending it: candidate URLs, deadlines, budgets and retry conditions
// @gis-api 0.5-session
// @permissions ["project.read", "map.write"]
import { map } from 'sqsmap';

const changed = map.once('sqs.resourceschange');
await map.sqs.resources.configure({
  resourceTimeoutMs: 20000,
  resourcePolicy: {
    maxConcurrentRequests: 8,
    attemptTimeoutMs: 4000,
    retries: 1,
    retryDelayMs: 100,
    cache: 'default',
    fallbacks: [{
      prefix: 'https://primary.example/tiles/',
      replacements: ['https://backup.example/tiles/'],
      resourceTypes: ['Tile'],
    }],
  },
});
const event = await changed;
const plan = await map.sqs.resources.plan({
  url: 'https://primary.example/tiles/1/2/3',
  resourceType: 'Tile', maxBytes: 8 * 1024 * 1024,
});
export default { configuration: event.resourceConfiguration, plan };

Replace the example hosts with your accessible services. Subscribe before calling configure. The event contains context: 'resourcePolicy', the accepted resourceConfiguration, map identity, sequence and target. Historical snapshots remain unchanged after later configuration. on returns a cancellable subscription; off and once follow common event semantics. Native one-shot scripts support configuration and queries but no persistent event entry point; use a native persistent session for listeners.

Calls capture bounded plain JSON. Functions, cycles, non-finite numbers and unknown fields are rejected. Dispatched downloads retain their old plans; future requests use the new configuration. Reducing concurrency does not cancel old downloads; retry waits still consume a slot. Built-in download loops wait for capacity, while direct image loading returns RESOURCE_LIMIT when full. A resolved configuration promise does not mean network resources are ready or a new frame is displayed.

Browser sources, images and map resources share capacity and the Rust planner in both main-thread and strict render Worker maps. Function-valued browser transformRequest retains its existing main-thread restriction and is not transferred through this JSON configuration. Desktop source HTTP, images, styles/fonts/sprites and online imagery tiles share one map-owned policy and existing disk cache; offline-pack reads remain independent. This HTTP policy does not cover WebSocket, local data, offline-pack creation or separate 3D downloaders. Check resourcePolicyPlanning, resourcePolicyExecution and resourcePolicyEvents through await map.sqs.getCapabilities(). The native library has no built-in HTTP execution, so the second flag is false; its external host still executes polled plans.

See resource policies for ranges, retry/fallback, query isolation and cache modes, and native resource policies for C ABI calls and external host execution. Use matching development SDK, script declarations and runtime snapshots. This does not establish formal npm, external-machine or mobile-device acceptance.

13.6b connects shared custom-protocol planning and tile templates with original URI/response identities; see custom planning for host registration, budgets and scope.

13.6c supports custom GeoJSON/TileJSON/georeferenced image/video source URLs and updates, retaining decoder/cancellation/identity rules. See source protocols; Style/glyph/loadImage/live entries retain their own scope.

Common setStyle accepts validated custom hierarchical URLs; see custom style protocols for handlers, relative resources and host conditions.

Shared 3D lighting

map.getLight(): Promise<ResolvedLightSpecification | null>
map.setLight(light: LightSpecification, options?: {validate?: boolean}): Map
map.lighting.state(): Promise<SceneLightingState>
map.lighting.set(options: SceneLightingOptions): Promise<SceneLightingState>
map.on('sqs.lightingchange', listener): Subscription

interface LightSpecification {
  anchor?: 'map' | 'viewport';
  position?: readonly [number, number, number];
  color?: string; intensity?: number;
  'position-transition'?: {duration?: number; delay?: number};
  'color-transition'?: {duration?: number; delay?: number};
  'intensity-transition'?: {duration?: number; delay?: number};
}
interface ResolvedLightSpecification {
  readonly anchor: 'map' | 'viewport';
  readonly position: readonly [number, number, number];
  readonly color: string; readonly intensity: number;
}
interface LightingChangeEvent {
  readonly type: 'sqs.lightingchange';
  readonly method: 'setLight' | 'sqs.lighting.set';
  readonly lightingState: SceneLightingState;
}

Default constants are viewport, 1.15,210,30, #ffffff and 0.5. Partial writes update both globe/building; legacy solar extensions override one view. Unconfigured getLight returns null. Only opaque CSS names/hex/rgb/hsl constants, zero property transitions and validate:true are implemented; nonzero transitions, expressions, transparency and validate:false are rejected. Deeply frozen events retain historical state. Failed commands remain observable in the confirmation journal; applications recover with an explicit failure watermark. See the lighting guide for solar time, native entries and complete HTML.

CSS names and modern numeric RGB/HSL are available; see color input rules. Native state and persistent event lightingState snapshots are deeply frozen.