Skip to main content

Map sources and attribution

Complete credit queries, source identity and custom attribution UI

Shared scripts use await map.sqs.getAttributions(), the standalone browser SDK uses await view.getAttributions(), and native libraries use sqsm_map_attributions_json. These return complete plain text and provenance for current logical sources without moving the camera, polling resources or changing command/event sequences. Applications own attribution UI; change events sample at host pump or actual frame boundaries.

The attribution field follows the MapLibre source specification, implemented by SqsMap's own core. getAttributions is a SqsMap extension, under sqs in shared scripts.

Script example

import {map} from 'sqsmap';
map.addSource('trees', {
  type:'geojson', attribution:'© Data provider',
  data:{type:'FeatureCollection',features:[]}
});
map.addLayer({id:'trees',type:'circle',source:'trees'});
const credits = await map.sqs.getAttributions();
export default credits;

text is complete display text, deduplicated in first-occurrence order; items keep independently credited sources even when text matches. Records contain scope, kind, sourceId, sourceInstanceId, layerIds and text. Previously returned snapshots stay unchanged. Script/SDK objects and arrays are immutable.

  • common: shared GeoJSON sources and referencing layers, with actual non-reused source instances. Same-name recreation cannot alter previous snapshots.
  • style: style-wide credits and vector/GeoJSON sources referenced by enabled style layers. Style-wide credits are a SqsMap extension and always remain. Style sources do not yet have instance identity: sourceInstanceId is null.
  • host: sources registered through host tile APIs. Browser raster/terrain-rgb use raster:layer-number and actual resource generations. Desktop configured-raster and script-raster sources have separate IDs but no instance identity yet; they are not shared source handles.

Visibility and merging

Common/style layers use logical visibility, inclusive minzoom and exclusive maxzoom. Unreferenced sources are omitted. A source remains credited while another enabled layer references it. Opacity, feature filters, tile readiness and pixel occlusion do not remove credits, conservatively preserving potentially applicable sources.

Later TileJSON updates only its source, retaining style-wide and other-source credits. Missing/empty attribution keeps previous source text. Browser host tiles contribute according to visibility and projection compatibility; source level limits allow continued overscaling and therefore do not hide credits. The current browser globe does not draw these planar host tiles, so they do not contribute there. Desktop uses its actual style-basemap imagery suppression, marker overlays and 3D configuration rules. Offline styles preserve original style credits. External overlays with no registered credit metadata cannot acquire invented copyright information.

Imagery and terrain

The SDK and browser xyz/raster-dem script hosts accept attribution. Supply provider-required complete text and source links.

const rasterId = view.addRasterTiles(tileTemplate, {
  scheme:'mercator', attribution:providerCredit
});
await view.flush();
const credits = await view.getAttributions();
creditElement.textContent = credits.text;

This SDK fragment requires application-provided view, tileTemplate, providerCredit and creditElement. The complete event-driven UI, initial query and unsubscribe behavior are provided below.

Completeness and UI

Raw attribution must be a string of at most 4 KiB UTF-8. HTML is stripped and entities decoded once; text is no longer clipped to 180 characters. Complete snapshot JSON is limited to 64 KiB. RESOURCE_LIMIT preserves the map and previous snapshots; removing relevant layers allows queries to recover. Truncated credits or fake empty data never count as success.

Custom UI should use textContent or an equivalent text property. Never execute credit text as HTML. Original HTML links are not returned as executable UI; applications must provide provider-required links. Plain text does not replace those links or the provider's full license requirements.

Native runtime flag SQSM_MAP_ATTRIBUTION_QUERIES is 8192; ABI remains version 1. Query output is independently owned, cleared on failure and freed with sqsm_map_bytes_free. Previously returned buffers survive map destruction. Download the complete C example and exact portable query script. External machines and mobile application hosts remain pending. Software verification does not establish hardware acceptance.

Rust entry points are MapSession::attributions, BasemapSession::attributions, attribution::style_snapshot, common_items and AttributionSnapshot::combine. Explicit zoom must be finite. Queries and change events are separate capabilities; both queries and changes are connected and advertised separately.

See GeoJSON source API and custom map tools for related scope.

Complete Rust CompiledStyle literals require root_attribution; CompiledSource::GeoJson includes attribution, and matching other fields can use .. .

Attribution changes and a complete custom UI

Common scripts use map.on('sqs.attributionschange', event => ...); event.attributions is the same deeply frozen complete snapshot as the query. The SDK exposes the same event name and view.onAttributionsChange(...). Require attributionEvents === true before advertising support. Retained native scripts support this event; one-shot native scripts still reject listeners. Mobile application hosts remain pending.

The host event pump or actual frame samples logical credits. Multiple writes between samples may coalesce; published historical snapshots never change. Identical display text still produces a notice when source identity or referenced layers change. There is no initial replay: subscribe first, then query, and use a local revision counter so a slow initial query cannot overwrite a newer notice.

A complete snapshot exceeding 64 KiB emits error with context === 'attributions' and an explicit code. Identical failures are reported once; recovery publishes a change even if it restores the previous valid snapshot. The map and historical snapshots remain intact; the query continues to fail rather than returning false empty credits. Limits remain 32 subscriptions, 128 pending notices and 256 KiB of pending content. Overflow fails the script session explicitly. Stop detaches host subscriptions; destruction cancels undispatched notices and rejects pending SDK once waits with CANCELLED.

// @gis-api 0.5-session
// @permissions ["project.read"]
import { map } from 'sqsmap';
export const sessionMode = 'persistent';
let revision = 0;
let current;
map.on('sqs.attributionschange', event => {
  revision++;
  current = event.attributions;
});
const initialRevision = revision;
const initial = await map.sqs.getAttributions();
if (initialRevision === revision) current = initial;
export default { initial: current };

Download the complete HTML/CSS/script example beside the matching SDK dist/ and wasm/ directories. It includes every initial-query, event, visibility, source-recreation, destruction and cleanup handler. The application renders textContent and supplies explicit source links; provider HTML is never executed. Demo geometry is application-owned. When using OpenFreeMap, ArcGIS or other external services, retain their required credits and provider links; a plain-text query does not supply those links automatically.

The same portable event script is verified in real browser main/strict Worker hosts and retained native QuickJS. C capability bit16384 is SQSM_MAP_ATTRIBUTION_EVENTS; the existing sqsm_map_poll_events_json returns attributionEvent.attributions or attributionEvent.context/error. A full public queue remains drainable and retries an unaccepted sample on the next pump. ABI remains1 with unchanged output ownership.