Feature query API
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. Legacy GIS layers, internal basemap layers, symbols, imagery, globe 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.
See selection identity for stable identities, state and data-update rules.