唯杰地图 logo唯杰地图
VJMAP
EN

query-coords

vjmap/references/coord/query-coords.md 9.0 KB134 行
原文

本文回答:svc.*QueryFeature 系列返回的坐标到底在哪个空间、.geom/.geojson/.points/.envelop 各是什么、怎么正确叠到图上。(与 backend/query-features 互补:那边讲接口用法,这里讲坐标空间。)

一、服务端返回什么——各字段坐标空间对照

查询接口(pointQueryFeature/rectQueryFeature/conditionQueryFeature/exprQueryFeature)返回 res.result[]。以 sys_cad2000 为例,每个字段的坐标空间并不一样:

字段 类型 坐标空间 何时有值
geojson string(GeoJSON) 原始墨卡托(默认)/ CAD 坐标(toMapCoordinate:true,或内存模式结果 isgeocoord:true) 条件查询仅 includegeom:true 时才有值(只传 realgeom 无效;多条且无 realgeom 时为外包矩形多边形);点/矩形查询总有值;指定 fields 时须包含 geojson
geom object 渲染经纬度(SDK _processQueryResult 转换产物) geojson 非空 且 isMapCoordinate!==true 时才生成;同时附 isline/isPoint/isPolygon(⚠️ isPolygon 有 SDK bug,见 §三)
envelop string WKT POLYGON((...)) 原始墨卡托(值可为负的百万级,别当 CAD!) 实体外包矩形,几乎总有;map.getEnvelopBounds(envelop) 解析为 CAD 空间 GeoBounds
bounds string "[x1,y1,x2,y2]" CAD 坐标 外包矩形,几乎总有
points string "x,y;x,y" CAD 坐标 顶点串
location/center/insertionPoint/origin string "x,y" CAD 坐标 点状实体/文字
isMapCoordinate bool — toMapCoordinate:true 时服务端置 true
isEnvelop bool — 该结果只给了外包矩形(geojson 为空)时多为 true
isgeocoord bool — 响应顶层字段:内存模式查询置 true,表示 geojson 已是 CAD 坐标(非墨卡托)

请求参数对返回的影响:

参数 效果
geom:true 几何查询模式;不等于返回 geojson
includegeom:true 返回 geojson(多条时可能只是 envelop 几何)
realgeom:true 每条都返回真实实体 geojson(配合 includegeom)
toMapCoordinate:true geojson 变 CAD 坐标 + isMapCoordinate:true;.geom 不生成
查询落空 没有 result 数组:条件查询为 {recordCount:0},点/矩形查询多为 {}——res.result ?? [] 兜底

二、SDK 侧 _processQueryResult 做了什么(Service.js)

const isgeocoord = param.isgeocoord;                    // 响应顶层字段,不是每条结果上的
const p = this.currentMapParam();
if (p) {
  prj = new GeoProjection(p.bounds);                    // 当前图范围投影
  if (!isgeocoord && p.mapBounds) mapPrj = new GeoProjection(p.mapBounds);  // 注意 !isgeocoord 才建
}
if (!isgeocoord && Array.isArray(param.metadata?.mapBounds))  // 必须是数组
  mapPrj = prj = new GeoProjection(GeoBounds.fromArray(param.metadata.mapBounds));

然后对每个 result 处理(cb 优先级最高,见 §五.8),伪代码:

  if (!data[d].geojson) → continue(envelop 模式不产生 .geom)
  if (res.isMapCoordinate === true) → continue(不生成 .geom)
  else 解析 res.geojson 到新对象,逐坐标转换后写入 res.geom:
    isgeocoord  →  prj ? prj.toLngLat(cad) : 原样保留               // CAD → 渲染经纬度
    否则        →  (mapPrj && prj) ? prj.toLngLat(mapPrj.fromMercator(mkt))  // 墨卡托→CAD→渲染经纬度
                     : Projection.mercator2LngLat(mkt)               // 纯墨卡托→经纬度
  然后写类型标记:
    if (!isLine && res.isEnvelop) isLine = true;        // ⚠️ envelop 结果被强制标记成线
    result[d].isline = isLine; result[d].isPoint = isPoint;
    if (result[d].isline === false && result[d].isline === false) result[d].isPolygon = true;
    //  ↑ SDK 源码把 isline 判断了两次(本该判 isPoint),见 §三警告

注意:被逐点改写的是 JSON.parse(res.geojson) 出来的临时对象(赋给 .geom);.geojson 字符串本身保持原始空间不变——两者坐标空间不同,别混用。

若 svc 从未 openMap(currentMapParam() 为空):prj/mapPrj 均为 null——isgeocoord 结果(内存模式)原样保留 CAD,其余走 mercator2LngLat 得到真实经纬度(此时 .geom 就不是"渲染经纬度"了)。查询前确保 map.attach(svc, prj)/svc.openMap 已建立上下文。

结论:res.result[i].geom 永远是"渲染经纬度"坐标——CAD 底图下是 GeoProjection 归一化经纬度,互联网底图下是真实经纬度。可直接:

map.getSource('x').setData({ type:'Feature', geometry: ent.geom.geometries[0] });
// 或点:
marker.setLngLat(ent.geom.geometries[0].coordinates);

而 toMapCoordinate:true 的结果没有 .geom,要自己 JSON.parse(res.geojson),得到 CAD 坐标。

三、⚠️ isPolygon 不可信(SDK bug,Service.js 源码核实)

判定条件重复写了 isline === false(本意应是 isPoint === false),所以纯点实体也会被打上 isPolygon: true。另外 isEnvelop 的结果会被强制 isline = true。

判断几何类型请:先看 isPoint,再看 isline,或直接读 geom.geometries[i].type。

四、三种典型用法

4.1 只要位置点画 Marker(最省流量)

const res = await svc.conditionQueryFeature({
  condition: "name='13' and text like '%泵房%'",
  fields: "objectid,points,location,text",
});
for (const e of res.result ?? []) {        // 落空时 result 可能不存在!
  const [x, y] = (e.location ?? e.points).split(';')[0].split(',').map(Number); // CAD
  new vjmap.Marker().setLngLat(map.toLngLat([x, y])).addTo(map);   // CAD→渲染经纬度
}

4.2 要真实几何高亮/GeoJSON 图层

const res = await svc.conditionQueryFeature({
  condition: "name='11' and layerindex=3",
  includegeom: true, realgeom: true,   // 关键:不传只有 envelop,geojson 为空、.geom 不生成
});
const features = (res.result ?? []).filter(e => e.geom).map(e => ({
  type: 'Feature', properties: { objectid: e.objectid }, geometry: e.geom.geometries[0],
}));
map.addSource('hl', { type:'geojson', data:{ type:'FeatureCollection', features } }); // .geom 已是渲染经纬度

4.3 要 CAD 坐标做计算/转 mapext 实体

const res = await svc.conditionQueryFeature({
  condition: "...",
  toMapCoordinate: true,               // 关键:geojson 直接给 CAD 地图坐标
  includegeom: true, realgeom: true,   // 同样必须,否则 geojson 是 ""
});
for (const e of res.result ?? []) {
  if (!e.geojson) continue;
  const gj = JSON.parse(e.geojson);    // CAD 坐标 GeoJSON(e.geom 不存在!)
  // mapext: featuresToEntities 期望 CAD 坐标 → 直接构 Feature(见 cad-mapper.md §六)
  // vjmap 叠加: map.toLngLat(gj) 转渲染经纬度
}

五、易错点

  1. toMapCoordinate:true 后去读 result[i].geom → undefined;该 JSON.parse(result[i].geojson)。
  2. 默认查询的 .geom 已是渲染经纬度,不要再 map.toLngLat——二次转换。
  3. points/location/bounds 字符串是 CAD 坐标(不是经纬度),split 后若要上图须 toLngLat。
  4. envelop 是 WKT 字符串且坐标是原始墨卡托(负的百万级数值),不是 CAD——要包络用 bounds 字段(CAD "[x1,y1,x2,y2]")或 map.getEnvelopBounds(envelop)(内部 prj.fromMercator 转成 CAD)。
  5. 不传 includegeom/realgeom,多条结果的 geojson 是空串 → .geom 不生成、只有 envelop/bounds/location。要真实几何必传。
  6. 查询落空时没有 result(条件查询为 {recordCount:0},点/矩形查询多为 {})——res.result ?? [] 兜底。
  7. 互联网底图上跑 conditionQueryFeature(查某张 CAD 图):.geom 默认转出的是该 CAD 图的渲染经纬度,不是你的互联网底图经纬度——要叠到互联网底图请用 toMapCoordinate:true 拿 CAD 坐标,再走 cmdTransform→GCJ02/WGS84(见 internet-basemap-align §四之三)。
  8. cb 回调(四个查询接口都有:pointQueryFeature(param, cb)、rectQueryFeature、conditionQueryFeature、exprQueryFeature;另外 processQueryResult(param, cb) 与 cmdCreateEntitiesGeomData(param, cb) 也走同一处理管线,签名 (point:[x,y]) => [x,y]|null|undefined)可对每个坐标点自定义处理,返回非空值就跳过 SDK 的默认转换——这是"自定义结果坐标系"的钩子(比如直接在这里做 CAD→GCJ02)。
  9. 查询入参 x/y/bounds 传渲染经纬度 → 入参要 CAD:map.fromLngLat(e.lngLat) 先转。经纬度值(如 116,39)当 CAD 传给点查询 → recordCount:0。
  10. enableLayerClickHighlight(svc, cb) 内部已做 fromLngLat → 点查询 → .geom 回填到高亮 source,全程无需手动转。