跳到主要内容

宿主指针输入

原生与底层 WASM 宿主共用的平面与球面指针快照。

宿主指针输入

共享核心通过已应用的平面或球面相机与 GeoJSON 样式图层查询生成指针快照。原生宿主调用 sqsm_map_pointer_json,底层 WASM 宿主调用 MapView.pointerEvent(json);SDK dispatchPointerEvent(input) 负责有界排队并转发画布输入。通用脚本的八类指针事件及图层委托已接通原生持续脚本与网页宿主。桌面工作台已接通地图区域鼠标输入;移动应用宿主仍待完成。

命名和 point / lngLat 结构参考 MapLibre MapMouseEvent。SqsMap 独立实现可序列化快照,不包含浏览器 originalEvent、DOM target 或异步 preventDefault();宿主在转发输入前决定默认手势行为。

{"type":"click","point":{"x":64,"y":48},"shiftKey":true,"layers":["trees"]}

支持 click、dblclick、mousedown、mouseup、mousemove、mouseenter、mouseleave、contextmenu。坐标使用当前视口内的逻辑/CSS 像素,计算包含内边距与旋转;高分屏宿主不能改传画布物理像素。pointerType 可为 mouse(默认)、pen、touch;button 为 0–4,buttons 为 0–31,altKey/ctrlKey/metaKey/shiftKey 默认 false。这是指针描述,尚非多点触控手势支持。

返回 JSON 包含 type、point {x,y}、lngLat {lng,lat} 或 null、features、cameraState 和 input。要素沿用 queryRenderedFeatures:当前可见的通用 GeoJSON circle/line/fill 图层,上层优先,保留原逻辑要素的标识/属性、要素状态与图源/图层实例来源。后续相机、图源或图层修改不会改变此前快照。layers 仅限制本次查询,不安装监听器;图层缺失报告 NOT_FOUND。已有查询扫描与结果预算同样适用。

输入 JSON 最多 8 KiB,完整快照最多 64 KiB。未知字段/类型、无效坐标或按键报告 INVALID_ARGUMENT;队列、输出分配及结果预算不足报告 RESOURCE_LIMIT。过大结果整体拒绝;可缩小 layers 范围后重试,不会静默截断命中要素。

原生 SQSM_MAP_POINTER_INPUT(4096)标识此宿主接口。须在地图创建线程调用,每个成功输出通过 sqsm_map_bytes_free 释放。成功输入还通过 sqsm_map_poll_events_json 发布一条事件,快照位于 pointerEvent。输出分配或公共队列失败不改变序号,也不发布事件;先轮询队列再重试。独立订阅仍遵守已有有界队列溢出规则。物理输入不进入脚本命令日志、不修改选择、不推进动画、不执行手势。

完整 C 宿主 · 公开头文件

WASM capabilities().pointerInput 标识快照绑定。pointerEvent() 立即返回快照,宿主负责排队和监听分发,不加入 cameraEvents。球面采用同一地球仪相机和共同 circle/line/fill 查询;三维全几何查询仍未接通。匹配 WASM 与原生生成参考已进入开发版 SDK 快照,尚非正式发行。

验收覆盖 Linux 真实硬件 GPU 动态库地图、旋转/内边距相机、高分屏逻辑像素 WASM 输入、命中顺序、历史快照、错误输入、输出缓冲耗尽、队列满载及 64 KiB 拒绝/恢复。本阶段还验证原生持续脚本与网页主线程/严格 Worker 的同一份脚本,以及真实鼠标点击兼容;Windows/macOS 外机、移动设备及浏览器硬件 WebGPU 仍待验收。

指针事件与图层委托

地图门面支持 click、dblclick、mousedown、mouseup、mousemove、contextmenu。使用 map.sqs.events() 判断当前宿主是否真正提供输入;原生动态库持续脚本需要宿主提交输入,网页 SDK 转发画布输入。桌面工作台已接通地图区域鼠标输入;移动应用宿主仍待接通,不能仅凭类型声明判断支持。

import { map } from 'sqsmap';
export const sessionMode = 'persistent';

if (map.sqs.events().includes('click')) {
  const show = event => console.log(event.lngLat, event.features);
  const subscription = map.on('click', 'trees', show);
  map.once('click', ['trees', 'roads'], event => console.log(event.point));
  // 需要停止监听时:subscription.unsubscribe();
  // 或 map.off('click', 'trees', show);
}

on(type, listener) 接收地图范围内的输入;on(type, layerId | layerIds, listener) 仅接收命中相应图层的输入,并将 features 筛为相应图层。普通命中事件在图层暂不存在或没有命中时不通知,也不消耗 once;mouseenter/mouseleave 使用命中转换,离开通知的 features 为空;同名图层重建后监听继续有效。图层数组规范化并去重,同一函数、事件及图层集合重复注册不会重复通知。off 按同一集合与函数取消,once(type, layers) 返回可等待的 Promise。图层委托只适用于上述八类事件。

point 为视口逻辑像素 {x,y},lngLat 为 {lng,lat};视口内但没有地面位置的点(经纬度视图的世界之外、三维视图中没有地面交点处)为 null。事件包含 type、target、mapInstanceId、sequence、features、cameraState、input。快照只读,后续地图修改不会改变旧事件;target 是当前地图对象/引用,不是 DOM 节点。命中支持当前可见 GeoJSON circle/line/fill 图层,上层优先,平面支持 circle/line/fill,球面支持 circle/line/fill;符号与完整3D查询仍未接通。事件不提供 DOM originalEvent 或 preventDefault()。

输入转发不占用脚本命令日志,不自动选择要素、改变手势或推进动画。SDK 指针订阅、排队输入和等待中的异步回调各有 32 项预算;超过预算报告 RESOURCE_LIMIT,解除压力后可恢复。停止脚本或销毁地图会取消监听和等待。

完整共同脚本 与 宿主输入说明。独立 SDK 的 on 返回可调用的取消函数,也支持 .unsubscribe();普通地图脚本返回 Subscription。SDK 旧数字图层点击的 feature、x/y 保留,旧经纬度数组移至 legacyLngLat;标准 lngLat 使用对象。旧菜单配置回调继续保持原有结构。

SDK interactive: false 禁用默认手势,但仍转发指针事件,便于使用方自行编写交互。

桌面工作台输入

桌面工作台将地图区域中的鼠标输入交给同一共享查询,支持上述八类事件和相同的 on/off/once 图层重载。前景工具控件会截获自己的输入;坐标扣除实际地图视口的偏移。同一帧即使发生多次布局,也只转发一次。主键释放产生 click,双击追加 dblclick,右键释放产生 contextmenu;中键仅产生 down/up。默认地图导航与选择仍由桌面宿主处理。

只在存在活动脚本时启用输入查询。每帧记录此前会话的接收名单,新启动的脚本不会收到旧的排队输入;停止、重跑和退出会清理等待与监听。桌面输入队列最多 128 条,满载明确结束相关脚本并报告 RESOURCE_LIMIT,不交付截断的事件序列。独立 Rust 宿主需要在启动 ScriptService 前显式启用 pointer_input,并负责驱动输入与轮询;默认服务不宣称支持实体输入。

当前桌面支持共同二维 GeoJSON circle/line/fill 和球面 circle/line/fill 查询。3D 全几何查询和多点触控尚未接通。egui 提供的触摸模拟指针可标记 touch,但这不代表移动应用宿主或真实触摸设备已验收;原生笔类型识别及 Windows/Linux 的 Meta 键仍待补齐。

本机验收使用实际 Linux 工作台、硬件 GPU 与 QuickJS,在 eframe RawInput 边界注入输入:同一份跨端脚本通过 38 条断言,另验证旧输入隔离、新输入与停止回调,并读回实际圆点像素。它不等于操作系统 HID、Windows/macOS 或移动设备验收。

球面事件坐标

球面 cameraState 为 MapCameraState 的 globe 分支,保留事件发生时的相机和逻辑视口。lngLat 为地表 {lng,lat},射线未碰到地球时为 null;SDK 兼容字段 legacyLngLat 同时为 null。读取经纬度前应判断:

map.on('click', event => {
  if (event.lngLat === null) { console.log('点击天空'); return; }
  console.log(event.lngLat.lng, event.lngLat.lat, event.features);
});

天空位置和无命中不是同一概念:未命中图层的地表仍返回坐标,圆点的屏幕范围可能延伸至地球边缘外。球面命中遵循共同 circle/line/fill 查询;符号与完整3D查询仍明确拒绝。完整球面脚本见球面指针示例。本阶段在主线程/严格 Worker、DPR1/2和实际原生 QuickJS 中验证同一38项脚本;桌面在 eframe RawInput 边界验证点击、重跑/停止隔离和实际像素,使用软件 Vulkan。验收程序清理宿主选择以避免业务面板遮挡;产品的选择行为不因此改变。真实触摸/笔和移动宿主继续单列验收。

共同球面线与面的绘制、筛选、排序和查询见球面图层。

按图层进入/离开语义已接通,本机验收见该指南。

图层委托的mouseenter/mouseleave由每个监听的mouse/pen命中转换决定,touch不生成悬停;leave的features为空。取消/once/重跑/销毁清理状态。低层mouseleave允许有限视口外坐标,返回lngLat:null;其它输入仍须视口内。图层变更本身不伪造输入,下一快照按新状态判断,监听继续跟随重建ID。

无地表坐标的平面快照

视口内的合法平面输入也可能没有可用地表:例如换算后的经纬度落在世界范围外,或倾斜 Web Mercator 的指针射线没有有效地表交点。此时 lngLat 返回 null,事件仍成功并继续执行 queryRenderedFeatures;features 不因缺少地表坐标而整体失败。球面天空和 mouseleave 同样可返回 null。读取经纬度前先检查 event.lngLat === null,并按需独立读取 event.features,不要把 null 当作整个事件失败。