跳到主要内容

场景静态资源与图层显隐

GeoJSON源归属、同源多层、状态控制、查询、署名和完整HTML

15.2在共同生命周期之上接通静态GeoJSON源与其全部共同circle/line/fill样式图层。网页主线程、严格Worker、独立原生QuickJS和桌面脚本使用同一接口。图层面板和工具按钮由用户自行实现。播放、轨迹、线动画、影像/地形资源归属和移动真实设备仍是后续工作;sceneStaticResources只表示此处公开范围。

import {map} from 'sqsmap';
if (!(await map.sqs.getCapabilities()).sceneStaticResources) throw Error('当前宿主不支持静态场景');
const scene = await map.sqs.scenes.create({id:'survey'});
map.addSource('survey',{type:'geojson',data:{type:'FeatureCollection',features:[]}});
await map.sqs.flush();
const source = await map.getSource('survey');
await scene.attachSource(source);
map.addLayer({id:'points',type:'circle',source:'survey'});
await map.sqs.flush();
await scene.start();
await scene.pause();
await scene.resume();
await scene.stop();
await scene.detachSource(source);
await map.sqs.scenes.remove(scene);

addSource/getSource/addLayer保持MapLibre风格;场景扩展使用map.sqs.scenes,不改写样式JSON。attachSource和detachSource都需await,可传本次源句柄或纯{id,sourceInstanceId};不能只传字符串id。源仍由地图持有,其所有共同样式图层随归属源显隐,不能仅绑定其中一个子图层。新增图层前先附着源可避免草稿内容闪现。URL GeoJSON仍按图源资源协议加载,静态归属只控制显示,不暂停独立URL加载。

状态静态显示实时接收器
draft隐藏归属接收器不联网
running按各层layout/zoom显示按既有归属规则运行
paused保留显示与已有数据关闭归属连接,保留数据
stopped隐藏,不能重新start/resume停止,终态
remove释放静态归属,恢复各层自身设置已停止接收器仍停止

场景显隐与图层layout.visibility及缩放范围取交集,不会覆盖用户隐藏,也不会修改原始layout。queryRenderedFeatures和getAttributions遵循相同有效显隐;查询源数据仍可取得隐藏数据。detachSource不删除数据和图层,也不停止独立接收器。remove保留数据/图层并恢复普通显隐;不再有场景控制。脚本结束不会解绑,地图销毁释放全部资源。

同一静态源最多归属一个场景;另一个场景绑定返回CONFLICT。源的实时接收器若指定owner,须与静态归属一致。未指定owner的接收器仍独立运行,静态绑定不会自动收养它。源和接收器分别计数:128项总资源预算,包括停止登记;地图16场景和8接收器预算仍适用。重复同owner附着/解绑幂等;停止场景不能附着新资源。旧、跨地图、删除重建的源/场景身份返回STALE_HANDLE,不能按同名自动寻找新实例。

getState()新增冻结sources:[{id,sourceInstanceId}],resourceCount=sources.length+receivers.length。sqs.scenechange新增attachSource/detachSource原因,历史源列表不回写。实际静态显隐变化使用sqs.layerschange,method为sqs.scenes.attachSource/start/stop/remove等;暂停不改变静态显隐时不虚发图层通知。署名变化通过sqs.attributionschange。先订阅再查询状态;取消订阅和销毁由使用方实现。所有归属、缓存、GPU计划、事件和外部输出先准备,失败保留原状态与已显示画面。

原生C沿用sqsm_map_scene_json和ABI1。attachSource/detachSource请求为{"method":"attachSource","args":[{"id":"survey","sceneInstanceId":"..."},{"id":"survey","sourceInstanceId":"..."}]};生命周期请求只有场景身份一个参数。身份/修订是不透明字符串,不能转number;输出必须使用sqsm_map_bytes_free释放。完整C示例展示创建、源/层登记、附着、状态控制、解绑、删除和清理。

完整HTML、同一跨宿主脚本、匹配SDK和接收器归属规则可直接参考。将HTML放在解压SDK根目录,使用HTTP服务访问;下方包含全部CSS、按钮事件、状态、署名和销毁代码。

<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>SqsMap static scenes</title>
<style>body{margin:0;font:16px system-ui}#map{height:70vh}#tools{display:flex;flex-wrap:wrap;gap:.5rem;padding:1rem}button{font:inherit;padding:.4rem}pre,footer{padding:0 1rem;white-space:pre-wrap}</style>
<div id="map"></div><div id="tools"><button id="start">Start</button><button id="pause">Pause</button><button id="resume">Resume</button><button id="stop">Stop</button><button id="detach">Detach source</button><button id="remove">Remove scene</button></div><pre id="status">Loading</pre><footer id="credits"></footer>
<script type="module">
import {MapView} from './dist/index.js';
let map,scene,source;const offs=[],status=document.querySelector('#status');
async function show(){status.textContent=JSON.stringify({scene:scene?await scene.getState():null,layer:(await map.getStyleLayers()).find(layer=>layer.id==='points')},null,2);}
async function act(fn){try{await fn();await show();}catch(error){status.textContent=String(error);}}
function requireScene(){if(!scene)throw Error('Scene has been removed.');return scene;}
try{
 map=await MapView.create(document.querySelector('#map'),{worker:false});if(!map.capabilities.sceneStaticResources)throw Error('Matching static scene SDK/WASM required.');
 map.jumpTo({center:[0,0],zoom:12});scene=await map.sqs.scenes.create({id:'sample',name:'Static points'});
 map.addSource('points',{type:'geojson',attribution:'Synthetic example data, supplied by this page',data:{type:'FeatureCollection',features:[-.003,.003].map((lng,id)=>({type:'Feature',id,properties:{},geometry:{type:'Point',coordinates:[lng,0]}}))}});await map.flush();source=await map.getSource('points');await scene.attachSource(source);
 // Add layers after attaching so the draft never flashes visible data.
 map.addLayer({id:'points',type:'circle',source:'points',paint:{'circle-color':'#ff0000','circle-radius':8}});await map.flush();
 offs.push(map.on('sqs.scenechange',event=>{status.textContent=JSON.stringify({reason:event.reason,scene:event.scene},null,2);}));
 offs.push(map.onStyleLayersChange(()=>{act(show);}));
 offs.push(map.onAttributionsChange(event=>{document.querySelector('#credits').textContent=event.attributions.text;}));
 document.querySelector('#credits').textContent=(await map.getAttributions()).text;await show();
 for(const method of ['start','pause','resume','stop'])document.querySelector('#'+method).onclick=()=>act(()=>requireScene()[method]());
 document.querySelector('#detach').onclick=()=>act(()=>requireScene().detachSource(source));
 document.querySelector('#remove').onclick=()=>act(async()=>{await map.sqs.scenes.remove(requireScene());scene=null;});
}catch(error){status.textContent=String(error);}
window.addEventListener('pagehide',()=>{for(const off of offs)off();map?.destroy();},{once:true});
</script></html>