绘制与编辑 API
入口与范围
浏览器使用 MapView.editing;原生使用 sqsm_map_edit_json。两者使用同一份 Rust 命令协议,不需要加载空间引擎。能力查询中的 editing: true 表示绑定已提供此入口;自定义 WASM 必须重新构建并包含 editCall。
本接口编辑 map.addGeoJSON 或 sqsm_map_add_geojson 返回的数字图层。字符串样式图层、聚合和实时图源应先取出数据,建立独立的数字图层再编辑;addSource 图源的数据更新仍使用 GeoJSON 图源 API。这些入口的编号不能互换。
编辑限二维俯视视图,同一地图只能有一个会话。编辑期间不要切换投影、替换图层或移除编辑对象。所有地理坐标为 WGS84 [经度, 纬度],所有屏幕点、矩形和捕捉半径为 CSS/逻辑像素,原生同样如此;库会换算设备像素比。墨卡托下拒绝超过约 ±85.051129° 的纬度。
这是编辑数据与辅助交互的接口。宿主负责工具栏、指针/键盘绑定、绘制过程预览、完成和取消按钮;调用 draw 提交一份完整几何,不会自动启用一套鼠标绘制控件。复杂几何计算、通用坐标转换和文件写出仍由空间引擎或宿主完成。
此页对应开发源码快照;使用匹配的 JS、Worker、WASM 或动态库,不能据此假定已发布的 npm 版本包含新接口。
JS:绘制、修改与保存结果
在绘制与编辑演示中可直接画点线面、拖动节点、撤销重做和导出成果;演示同时提供可下载的完整 HTML。
import { MapView } from '@sqsmap/core';
const map = await MapView.create(document.getElementById('map')!);
const layer = map.addGeoJSON('采集成果', {
type: 'FeatureCollection', features: []
});
await map.flush(); // 等待图层实际添加到地图/Worker
await map.editing.begin(layer);
await map.editing.draw({
type: 'Polygon',
coordinates: [[[116.4, 39.9], [116.41, 39.9], [116.41, 39.91]]]
}, { name: '地块一' }); // 自动补上闭合坐标;一次调用是一条撤销记录
const { vertices } = await map.editing.vertices(0);
await map.editing.moveVertex(0, vertices[0]!, [116.399, 39.899]);
await map.editing.undo();
await map.editing.redo();
const result = await map.editing.getData(); // 必须在结束会话前读取
await map.editing.apply(); // 结果保留在地图中,结束会话;不写磁盘
const url = URL.createObjectURL(new Blob([JSON.stringify(result)], {
type: 'application/geo+json'
}));
const a = document.createElement('a');
a.href = url;
a.download = '成果.geojson';
a.click();
setTimeout(() => URL.revokeObjectURL(url), 1000);
// 如需放弃本会话的所有改动,改为 await map.editing.discard()。
新增的图层为空时不会自动改变相机;宿主可先设置作业区域。要采点,可在开始会话后通过指针事件中的 event.lngLat 取坐标,按需调用 snap,再用 draw({type:'Point', coordinates:...}) 提交。勾绘线面时由宿主累计点列,完成时一次提交,拖动时仅预览、松开后调用一次移动接口,即可保持一次操作一条历史。
JS 方法与 C 命令字段
map.editing.execute(command) 可直接发送下表命令;C 传入完全相同的 JSON 对象,不再套 args 数组。所有写操作成功返回 JSON null;JS 的便利方法均返回 Promise,必须等待完成后再读取结果。
| JS 方法/命令 method | JSON 字段 | 结果与语义 |
|---|---|---|
begin(layerId) / begin | layerId | 开始会话,保存恢复检查点 |
apply() / apply | 无 | 保留数据并结束会话 |
discard() / discard | 无 | 恢复会话开始时的数据并结束 |
state() / state | 无 | layerId、canUndo、canRedo、undoCount、redoCount、undoLabel、redoLabel;无会话时编号为 null、计数为零 |
undo()、redo() | 无 | 撤销/重做会话内一步;没有记录时不修改数据 |
getData() / getData | 无 | 当前编辑图层的经纬度 FeatureCollection;属性采用核心编辑模型的字符串值 |
draw(geometry, properties?) / draw | geometry、可选 properties | 绘制 Point、LineString 或 Polygon,面环自动闭合 |
append(features) / append | features | 一次添加一组 GeoJSON Feature;去掉输入 id,由核心分配要素身份 |
replace(features, label) / replace | features、label | 一条历史替换全层;供宿主计算完成后写回成果,说明为 1~256 UTF-8 字节 |
vertices(feature, limit?) / vertices | feature、可选 limit | {vertices, totalCount, truncated};默认 10000,最多 100000 个 |
hitVertex、hitSegment | feature、point:[x,y]、radius | 命中的顶点/线段起点地址,未命中为 null |
featuresInBox(bounds) / featuresInBox | bounds:[左,上,右,下] | 屏幕矩形命中的要素序号 |
verticesInBox(feature, bounds) / verticesInBox | feature、bounds | 框内顶点,目标要素最多 100000 个顶点 |
select(features) / select | features | 高亮要素序号数组,空数组清除高亮 |
moveVertex(feature, vertex, coordinate, topology?) | 同名字段 | 移动一个节点;topology:true 联动邻接要素的共享节点 |
moveVertices(feature, vertices, from, to) | 同名字段 | 对选中顶点施加显示坐标中 from → to 的统一位移 |
insertVertex(feature, vertex, coordinate) | 同名字段 | 在指定线段起点之后插入节点 |
deleteVertices(feature, vertices, topology?) | 同名字段 | 删除节点,可联动共享节点;破坏环最小节点数时拒绝整次操作 |
deleteFeatures(features) | features | 一次删除一组要素 |
translate(features, from, to) | 同名字段 | 整组平移要素,一条历史 |
setAttribute(feature, field, value) | 同名字段,value 为字符串 | 修改已有字段;字段缺失、序号失效或值未改变时拒绝 |
snap(coordinate, radius, options?) / snap | coordinate、radius、可选 vertex、segment、gridMeters | 顶点/线段默认均开启,可加米制网格;未命中为 null,临时编辑叠加层不参与捕捉 |
trace(from, to, radius) / trace | 同名字段 | 沿已有线或面环追踪,返回经纬度点列,未找到路径为 null |
顶点结构为 {partIndex, vertexIndex, coordinate:[lng,lat], closed}。要素序号、部件序号、顶点序号均从零开始;它们是当前数据的位置地址,删除、替换或撤销后应重新查询,不要当作永久身份保存。命中线段返回该线段的前一顶点地址。闭合环由核心维护。
draw 只接受二维点、线、面,线至少两个点,面环闭合后至少四个坐标。append、replace 也接受合法的二维多部件/集合几何。每条请求最多 16 MiB;捕捉半径为 0~1024 逻辑像素;网格间距必须大于零。核心编辑检查点另有 64 MiB 恢复快照预算。
C ABI
#include "sqs_map.h"
#include <stdio.h>
#include <string.h>
static int edit(sqsm_map_t map, const char *json, int print) {
sqsm_map_bytes_t out = {0};
int status = sqsm_map_edit_json(map, (const uint8_t *)json,
(uint64_t)strlen(json), &out);
if (status != 0) {
fprintf(stderr, "%s\n", sqsm_map_last_error());
return status;
}
if (print) fwrite(out.data, 1, (size_t)out.len, stdout);
sqsm_map_bytes_free(out.allocation);
return 0;
}
/* map 与 layer 来自 sqsm_map_create / sqsm_map_add_geojson。 */
static int draw_point(sqsm_map_t map, uint64_t layer) {
char begin[128];
snprintf(begin, sizeof begin, "{\"method\":\"begin\",\"layerId\":%llu}",
(unsigned long long)layer);
if (edit(map, begin, 0)) return 1;
if (edit(map, "{\"method\":\"draw\",\"geometry\":{\"type\":\"Point\","
"\"coordinates\":[116.4,39.9]},\"properties\":{\"name\":\"A\"}}", 0))
return edit(map, "{\"method\":\"discard\"}", 0), 1;
if (edit(map, "{\"method\":\"getData\"}", 1)) return 1;
return edit(map, "{\"method\":\"apply\"}", 0);
}
调用必须在地图创建线程上进行;json 是 UTF-8,len 不包含终止零字节。out 在失败时清空,成功后归调用方独立持有,使用 sqsm_map_bytes_free 释放。写操作会在改变数据前分配返回缓冲区,输出预算不足时不修改数据。修改本身不触发文件保存或应用层确认框,宿主按自身编辑流程决定何时允许应用/丢弃。
可下载完整 C 示例,使用匹配头文件与动态库编译。
需要地图重绘时继续执行原有宿主渲染循环;编辑写命令进入原生命令确认日志,状态变化沿用 sqs.layerschange。JS SDK 在确认写操作后请求重绘,主线程与 Worker 使用相同路径。公开头文件及原生签名参考应与动态库配套。