select-feature
本文回答:选择 CAD 实体 —— 点选 / 框选 / 多边形选,高亮与对象 ID。
一、前提
选择 CAD 原始实体(区别于 Draw / GeoJSON 要素)必须:
svc.openMap({ mapopenway: vjmap.MapOpenWay.GeomRender })- 内存模式(
Memory)不存实体 → 无法选择
- 内存模式(
- 后端要返回 geom 字段
二、点选:hover 高亮 + click 查询
栅格样式
map.enableLayerClickHighlight(svc, (e) => {
if (!e) return;
console.log(`name=${e.name}, id=${e.objectid}, layer=${e.layerindex}`);
});
矢量样式
const layers = svc.getMapLayers();
const { entTypeIdMap } = await svc.getConstData();
map.enableVectorLayerHoverHighlight((event, feature, layer) => {
const prop = feature.properties;
console.log(`feature=${feature.id}, layer=${layers[prop.layer].name}, type=${entTypeIdMap[prop.type]}`);
});
主动查询一点
map.on("click", async (e) => {
const pt = map.fromLngLat(e.lngLat);
const q = await svc.pointQueryFeature({
x: pt.x, y: pt.y,
pixelsize: 5,
zoom: map.getZoom()
});
console.log(q);
});
三、框选 (Rectangle)
const r = await vjmap.Draw.actionDrawRectangle(map, {
api: { getSnapFeatures: snapObj }
});
if (r.cancel) return;
const [p1, p2, p3, p4] = map.fromLngLat(r.features[0].geometry.coordinates[0]);
const bounds = [p1.x, p1.y, p3.x, p3.y];
const features = await getHighlightEntities(map, bounds, /*useGeomCoord*/ true);
推荐:用 conditionQueryFeature 内置的范围参数(官方示例 22hightselentity.js 的做法;bounds 就是上面 map.fromLngLat 得到的地图坐标,无需手写空间 SQL):
const res = await svc.conditionQueryFeature({
condition: "", // 可再叠加属性条件
bounds, // [x1, y1, x2, y2];多边形传点数组 [[x, y], ...]
isContains: true, // true = 完全包含;false(默认)= 相交
includegeom: true, // 返回几何
realgeom: true, // 每条都返回真实几何(否则多条时只给外包矩形)
fields: "",
limit: 100000
});
手写空间 SQL 的参考实现如下(getHighlightEntities 不是包内导出函数,需自己写):
async function getHighlightEntities(map, [x1, y1, x2, y2], useGeomCoord, disableHighlight, includeWholeEntity, disabledSet) {
const svc = map.getService();
// ⚠️ ST_MakeEnvelope 所有形态都返回 0——该谓词在后端
// 不被支持,老资料里的 `geom::geometry && ST_MakeEnvelope(...)` 配方已失效。
// 可用配方:ST_Intersects(geom, ST_GeomFromText(wkt, 0)),坐标必须是**实体坐标域**
const wkt = `POLYGON((${x1} ${y1}, ${x2} ${y1}, ${x2} ${y2}, ${x1} ${y2}, ${x1} ${y1}))`;
const res = await svc.conditionQueryFeature({
condition: `ST_Intersects(geom, ST_GeomFromText('${wkt}', 0))`,
fields: "objectid,name,envelop,layer,color",
geom: true,
limit: 100000
});
// 根据 res.result 生成 GeoJSON features, 再高亮
return { features: ... };
}
// ⚠️ 三个坐标/语法坑位:
// 1) geom 条件里的 WKT 必须是**实体坐标域**(envelop 字段同一空间,百万级 CAD 原始
// 坐标),不是 drawBounds/鼠标 lngLat 的显示域——两域不重叠,混用必 0 命中。
// 2) ST_Intersects/ST_Within 第二参必须写 ST_GeomFromText('...', 0)——
// 把裸 WKT 字符串直接传参会使空间条件退化为永真,返回**全表**(554 条)。
// 3) 简单矩形框选(显示域)用 `rectQueryFeature({x1,y1,x2,y2})` 更稳——
// 它用的是地图显示坐标域,与鼠标框选一致。
实际项目可引入 vjmapext 用 mapcadLayer.inputManager.getSelections(...)(vjmapext 没有名为 selectEntities 的导出函数)。
四、多边形选(Polygon)
const r = await vjmap.Draw.actionDrawPolygon(map, { api: { getSnapFeatures: snapObj } });
if (r.cancel) return;
const ring = map.fromLngLat(r.features[0].geometry.coordinates[0]);
// 推荐:内置多边形范围查询(官方 22hightselentitypoly.js)
const hit = await svc.conditionQueryFeature({
condition: "", bounds: ring.map(p => [p.x, p.y]), isContains: true,
includegeom: true, realgeom: true, fields: "", limit: 100000
});
// 或手写空间 SQL:
// 转 WKT
const wkt = `POLYGON((${ring.map(p => `${p.x} ${p.y}`).join(",")}))`;
const res = await svc.conditionQueryFeature({
condition: `ST_Within(geom, ST_GeomFromText('${wkt}', 0))`,
fields: "objectid,name,envelop",
geom: true,
limit: 100000
});
五、连续选择(actionSelect 风格)
参考 draw-action.md 的 selectFeatures 函数。关键点:
- 循环里每次
actionDrawRectangle / actionDrawPoint - 右键 →
map.fire("keyup", { keyCode: 13 })触发结束 - 累积所选要素
六、高亮方式
- 内置点击高亮:
map.enableLayerClickHighlight(svc, cb)内部用<prefix>-source(默认highlight-source)存高亮要素——注意vjmap.HighlightFeatureStyle/highlight_feature_source并不是真实导出名,要自定义样式就对highlight-source这个 source 自己配 layer。 - 自己画:对选中 features 建一个
new vjmap.Polygon / vjmap.Polyline,addTo(map)。 - 矢量样式用 feature-state:
map.setFeatureState({ source: "layer-src", sourceLayer: "...", id: 123 }, { selected: true }); // 样式中用 ["feature-state", "selected"]
七、清除选择
没有内置的 clearHighlight 方法,按高亮的实现方式分别清:
// 1) 内置点击高亮(enableLayerClickHighlight 用 <prefix>-source 存高亮要素,默认前缀 "highlight")
// 只清当前高亮、保留点击高亮模式:
map.getSource("highlight-source")
?.setData({ type: "FeatureCollection", features: [] });
// 彻底关掉:会连同 highlight-source 及其线/面图层一起删除并解绑 click(之后无需再 setData)
map.disableLayerClickHighlight();
// 2) feature-state 方式:逐条或整块清
map.removeFeatureState({ source: "layer-src", sourceLayer: "..." }); // 省 id = 清整个 source
map.removeFeatureState({ source: "layer-src", sourceLayer: "...", id }, "selected");
// 3) 自己画的高亮图层/实体
map.removeSourceEx("my-sel"); // vjmap 扩展:连同用到它的 layer 一起移除
八、批量 objectid 查询
const ids = [10, 20, 30];
const res = await svc.conditionQueryFeature({
condition: `objectid in (${ids.join(",")})`,
fields: "objectid,name,envelop",
geom: true
});
九、图层显示过滤
选择前限制"只能选某图层":
const disableSet = new Set(["LAYER_A", "LAYER_B"]);
const features = await getHighlightEntities(map, bounds, true, undefined, undefined, disableSet);
十、常见坑
- 内存模式选不了 → 切换
MapOpenWay.GeomRender或mapopenway: "GeomRender"。 - 框大了查询超时 → 后端
limit加保护。 - 栅格 + 矢量两套样式共存时 hover 不对:查看
map.hasVectorLayer()决定走哪条分支。 - PostGIS envelope 查询速度慢 → 服务端
geom字段建 GIST 索引(内置)。