Skip to main content

Query identity, selection and data updates

Applications own selection panels and hover UI. The map provides queries, feature-state and events. Selected/hover are application-defined state keys; queries do not automatically select or modify source attributes/business data.

Identity/revisionMeaning and update rule
sourceInstanceIdSource instance; removal/recreation creates a new value, preventing old handles from writing to a replacement
sourceLayer and idMVT targets require sourceLayer. Numeric/string IDs differ. Multiple style layers for the same source feature share its state
dataRevisionChanges on data updates; a stale query Feature used directly as a state target is rejected
cacheRevision/layerInstanceIdDisplay snapshot/layer identity; array position is not a persistent identity
stateRevisionChanges independently without editing dataRevision or source attributes

Explicit/promoteId identities retain state across data updates within one source. State may be assigned before an ID appears. Whole-source generateId replacement clears state to avoid selecting a reordered index. Recreated sources inherit no state. MVT rendered queries deduplicate stable typed IDs within each style layer; source queries keep tile fragments and geometry merging remains separate work.

This selection handler deliberately retains source instance and normalized ID while allowing stable IDs to survive data revisions. To require the exact query snapshot, pass the original Feature as target and retain its sqs.dataRevision check.

import {map} from 'sqsmap';
let selected;
const current = async target => {
  const source = await map.getSource(target.source);
  return source?.sourceInstanceId === target.sqs.sourceInstanceId;
};
const targetOf = feature => {
  const id = feature.id;
  if (!(typeof id === 'string' || (typeof id === 'number'
    && Number.isFinite(id) && Math.abs(id) <= Number.MAX_SAFE_INTEGER))
    || !feature.sqs?.sourceInstanceId)
    throw Error('A stable feature identity is required');
  return {source:feature.source,id:feature.id,
    ...(feature.sourceLayer ? {sourceLayer:feature.sourceLayer} : {}),
    sqs:{sourceInstanceId:feature.sqs.sourceInstanceId}};
};
export async function selectFeature(feature) {
  const next = feature ? targetOf(feature) : undefined;
  if (next && !await current(next)) throw Error('Source was replaced');
  if (selected && await current(selected))
    map.setFeatureState(selected, {selected:false});
  if (next) map.setFeatureState(next, {selected:true});
  await map.sqs.flush({render:true});
  selected = next;
}
// Caller-owned UI awaits selectFeature(hit) or selectFeature(undefined).
// Caller serializes updates and displays errors; no DOM object is passed.

Serialize calls and update UI only after awaiting the result. On failure, report the error, re-read getFeatureState and check source identity. Multiple writes plus flush are not a general transaction; successful deselection followed by failed selection must not be displayed as total success. Refresh state/queries on source changes; clear UI caches for generateId replacement or changed source instance.

Paint feature-state expressions can color/resize selected or hovered features, with explicit missing-value defaults; filter/layout rejects feature-state. Mouse/layer events and sqs.sourceschange refresh external UI. Stop unsubscribes and discards late async reads; map destruction releases source states. Stopping an ordinary script does not automatically destroy the host map.

Feature-state · Queries · Hover events · MVT deduplication. Existing native/desktop/browser evidence covers these APIs; this stage completes identity/selection guidance. Mobile GIS, device and formal-release acceptance remain separate.