Skip to main content

Image expressions and icon fallbacks

Select available icons using image, coalesce and lexical variables

A matching development SDK with commonLineSymbols also supports shared planar line symbols, including true per-glyph curved geometry, target ownership, joint icon/text groups and shared budgets. Earlier point-only scope and pending line-symbol notes below are superseded for this bounded subset. Complex shaping, tilted/globe/terrain placement and complete road stitching remain pending.

Shared planar point symbol layers support typed image expressions in icon-image, reported by commonIconImageExpressions. ['image', name] synchronously checks registered or already loaded images. It returns a typed image when available and actual null when missing, allowing coalesce to choose another available image.

import { map } from 'sqsmap';
const rgba = (width, height, color) => ({
  width, height,
  data: Array.from({ length: width * height }, () => color).flat()
});
map.addImage('fallback', rgba(12, 12, [0, 0, 255, 255]));
map.addSource('points', { type: 'geojson', data: {
  type: 'Feature', id: 7, properties: { icon: 'primary' },
  geometry: { type: 'Point', coordinates: [116.4, 39.9] }
} });
map.addLayer({ id: 'icons', type: 'symbol', source: 'points',
  layout: { 'icon-image': ['let', 'name', ['get', 'icon'],
    ['let', 'pic', ['image', ['var', 'name']],
      ['coalesce', ['var', 'pic'], ['image', 'fallback']]]] }
});
await map.sqs.flush();
// Missing primary initially selects the blue fallback. Adding it switches the same layer.
map.addImage('primary', rgba(16, 8, [255, 0, 0, 255]), { pixelRatio: 2 });
await map.sqs.flush();

Plain strings retain their existing semantics: they select a name without testing availability. ['coalesce', 'missing', ['image', 'fallback']] selects the nonnull string missing and draws nothing when that resource does not exist. Use image for availability fallback. An empty string is also nonnull and means no icon; explicit ['image', ''] is invalid. If all image candidates are absent, neither an icon nor a corresponding rendered query hit is produced.

case, match, coalesce and lexical/lazy let/var preserve typed image identity. Every binding captures its parent environment and is evaluated only when referenced. Sibling bindings cannot refer to one another; nest scopes as shown. Unused bindings and unselected branches are not evaluated. Image variables cannot enter ordinary string, number or boolean operations; formatted values cannot be returned from icon-image. Existing shared evaluation limits apply: 8192 steps/64 levels, and 32 expression levels/1024 nodes.

All existing icon parameters and RGBA/SDF paths remain in use. RGBA icons retain bitmap colors multiplied by icon-opacity; SDF icons use icon-color and icon-opacity. Size is image pixels divided by pixelRatio, multiplied by icon-size. Anchors, offsets, rotation, collision, ordering and queries use the selected image's actual region. SDF icons remain available, while formatted image sections still reject SDF explicitly.

Only names actually attempted by visible features contribute to styleimagemissing. An available primary prevents evaluation and notification of unused fallbacks. Image add/update/remove and source property changes re-evaluate selection and queries. See missing style images for deduplication, generations and persistent scripts. Native and WASM image writes validate the common symbol candidate before commit. Invalid layer/source/image candidates and failed output preparation preserve existing resources, expressions, layer identity and rendering.

Names are limited to 256 bytes. Existing image registry, atlas, point, collision and display budgets remain in force. This capability covers shared planar point icons, excluding patterns, new downloads, URL-basemap expression domains, line placement and 3D combinations. Mobile application acceptance and formal release remain separate. Semantics follow the MapLibre image specification, implemented independently in this project.

Copy the portable script, or use the complete browser page.

Copy portable-icon-image.js, or use the complete browser page. The same script checks 16 availability fallback, scope/data update, actual missing demand, RGBA/SDF, atomic failure and identity behaviors, then verifies actual pixels of the final 12×12 blue icon.