Missing style images
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.
<!doctype html>
<html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>SqsMap missing style images</title>
<style>body{margin:0;font:14px system-ui}#map{height:65vh;min-height:240px}.tools{display:flex;gap:8px;padding:12px;flex-wrap:wrap}#status,#attribution{padding:8px 12px;white-space:pre-wrap}</style>
<div id="map"></div><div class="tools"><label><input id="auto" type="checkbox" checked>Complete automatically</label><button id="fill" disabled>Fill missing image</button><button id="remove" disabled>Remove image</button><button id="reload" disabled>Reload style</button><button id="destroy" disabled>Destroy map</button></div><div id="status" role="status">Starting…</div><div id="attribution"></div>
<script type="module">
import {MapView} from 'https://www.sqsmap.com/sdk/sqsmap-core/dist/index.js';
const status=document.querySelector('#status'),auto=document.querySelector('#auto'),buttons=['fill','remove','reload','destroy'].map(id=>document.getElementById(id));
const view=await MapView.create(document.getElementById('map'));const style='https://www.sqsmap.com/sdk/sqsmap-core/missing-image-style.json';let alive=true,missing;
const report=error=>{if(alive)status.textContent=String(error);};
async function fill(id,generation){
if(!alive||generation!==view.basemapStatus.styleGeneration)return;
if(await view.hasImage(id))return;
const data=new Uint8Array(16*16*4);for(let i=0;i<data.length;i+=4){data[i+2]=255;data[i+3]=255;}
view.addImage(id,{width:16,height:16,data});await view.flush({render:true});if(alive)status.textContent='Image confirmed: '+id;
}
const offMissing=view.on('styleimagemissing',event=>{missing={id:event.id,generation:event.styleGeneration};status.textContent='Missing: '+event.id;if(auto.checked)fill(event.id,event.styleGeneration).catch(report);});
const credit=document.querySelector('#attribution');const offCredit=view.onAttributionsChange(event=>{credit.textContent=event.attributions.text;});credit.textContent=(await view.getAttributions()).text;
document.querySelector('#fill').onclick=()=>{if(missing)fill(missing.id,missing.generation).catch(report);};
document.querySelector('#remove').onclick=async()=>{try{if(await view.hasImage('marker')){view.removeImage('marker');await view.flush({render:true});}}catch(error){report(error);}};
document.querySelector('#reload').onclick=async()=>{try{missing=undefined;view.setStyle(null);await view.flush();view.setStyle(style);await view.flush();}catch(error){report(error);}};
document.querySelector('#destroy').onclick=()=>{alive=false;offMissing();offCredit();view.destroy();buttons.forEach(button=>button.disabled=true);auto.disabled=true;status.textContent='Map destroyed; observers removed.';};
buttons.forEach(button=>button.disabled=false);view.jumpTo({center:[0,0],zoom:0});view.setStyle(style);await view.flush();
</script></html>
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.
For external image loading, see Load external images.
Common point image symbols also support demand completion and SDF recoloring. See Symbol images and demand events. General text/collision/3D symbols and the mobile host remain pending.