唯杰地图 logo唯杰地图
VJMAP
EN

query-features

vjmap/references/backend/query-features.md 33.9 KB835 行
原文

本文回答:完整的 CAD 实体类型 name='xx' 列表、后台数据库表结构、所有 SQL 字段含义对照、各类查询接口(条件 / 点 / 矩形 / 多边形 / 分页)的用法、查询结果的字段与绘制姿势。

本文是后台数据查询的"主参考"。简化速查见 query-sql;表达式查询(内存模式)见 query-expression;综合用例见 query-cases;全文搜索见 query-fulltext;服务端高亮 / 过滤见 svr-style-var。

一、查询的前置条件

  • 几何渲染方式打开(GeomRender):后台走几何渲染查询命令。能用 rectQueryFeature / pointQueryFeature(空间查询)。

    await svc.openMap({ mapid, mapopenway: vjmap.MapOpenWay.GeomRender });
    

    ✅ 公共云 sys_zp、GeomRender 下:conditionQueryFeature 在 GeomRender 下可用——condition:"objectid>0" + limit:3 返回 3 条(recordCount=6992),空条件返回全部。字段条件、limit、beginpos 分页都生效。

    注意区分:conditionQueryFeature(SQL 条件)是几何渲染专属——内存模式进程收到 querytype:"condition" 直接返回 {error:"not support query type!"};exprQueryFeature(表达式查询)在 GeomRender / Memory 下都可用(GeomRender 下能跑但不走空间库,较慢)。

  • 内存模式打开(Memory):只能用 query-expression 的 exprQueryFeature 与 pointQueryFeature/rectQueryFeature(点查询恒返回 1 条,几何只给外包矩形),不能用 conditionQueryFeature。

  • limit/beginpos 分页:GeomRender 生效——rect 全图 + limit:3 返回 3 条(不传 limit 默认上限 100),condition + limit/beginpos 正常翻页。0 命中时没有 result 键:条件查询返回 {recordCount:0},点/矩形查询返回 {}——统一 res.result ?? [] 兜底。

  • 任何查询 SDK 调用都返回 { error?, recordCount, result: [...] },业务上必须先判 error。

二、后台数据库表结构

把图以"几何渲染"方式打开后,每个实体在后台都是一行记录。SQL 条件只写 where 之后的部分(不要写 select * from xxx where)。

字段名 类型 说明
id integer 自增 ID
objectid text 实体唯一 ID(图重新加载后稳定)。命名规则见下文
layerindex integer 图层索引
name text 实体类型值(字符串,如 '1' = AcDbLine)。所有取值见第三节
color int 颜色(RGB+Alpha 编码,常见为负数,见第二节末)
data blob 几何数据(二进制,前端不直接用)
s1 text 线型
s2 text 扩展字典数据
s3 text 字符型属性 3(含义随实体类型变,见第四节)
s4 text 字符型属性 4(含义随实体类型变)
s5 text 字符型属性 5(含义随实体类型变)
n1 float 颜色索引
n2 float 线型比例
n3 float 线宽
n4 float 透明度
n5 float 数值型属性 5(含义随实体类型变)
n6 float 数值型属性 6(含义随实体类型变)
n7 float 数值型属性 7(含义随实体类型变)
n8 float 数值型属性 8(含义随实体类型变)
n9 float 数值型属性 9(含义随实体类型变)
n10 float 数值型属性 10(含义随实体类型变)
geom GEOMETRY 实体外包矩形(用于空间查询,墨卡托坐标)

objectid 命名规则(重要)

  • 普通实体:{实体句柄},如 1A;同一实体拆成多行时追加 _{序号}(如 9EA9_0、1A_1…)
  • 块内实体:{块参照id链}_{实体id}_{标记},如 9BE_5C_3F_#。id 链按"最外层块参照 → 直接所属块"排列;尾部标记区分来源:_ = 一般组合实体、_@ = 表格内、_# = 块参照内(无标注块)、_$ = 标注或含标注块的块内
  • 组(group):{实体id}${组id},如 5C$2A(实体在前,组在后)
  • 表格(table):{块参照id链}_{实体id}_@,如 1A_5C_@

用 objectid like 'EA_%' 可查到最外层块参照 id 为 EA 的所有子实体。

color 编码

  • 颜色由 RGB + Alpha 组合:color = 0xAABBGGRR(低字节是 R)
  • 例:黑色不透明 = 0xFF000000,按有符号 32 位读取就是 -16777216
  • 例:红色不透明 = 0xFF0000FF(红在低位),即 -16776961
  • 实际查询时用整数:name='13' and color=-16776961(红色单行文字)

前端拿到后转 HTML 颜色:vjmap.entColorToHtmlColor(color)。

三、所有实体类型 name='xx' 完整列表

⚠️ name 是字符串,SQL 里要写 name='1' 而不是 name=1。

name 类名 中文 备注
'1' AcDbLine 直线
'2' AcDbPolyline 多段线(LWPolyline)
'3' AcDb2dPolyline 二维折线
'4' AcDb3dPolyline 三维多段线
'5' AcDbSpline 样条曲线
'6' AcDbArc 圆弧
'7' AcDbCircle 圆
'8' AcDbEllipse 椭圆
'9' AcDbCurve 曲线
'10' AcDbBlockReference 块参照 子实体的 objectid 以块 ID 开头
'11' AcDbHatch 填充
'12' AcDbMText 多行文本
'13' AcDbText 单行文本
'14' AcDbShape 型实体
'15' AcDbRasterImage 栅格图片
'16' AcDbWipeout 遮罩实体
'17' AcDb2LineAngularDimension 角度标注(两条线)
'18' AcDb3PointAngularDimension 角度标注(三点)
'19' AcDbAlignedDimension 对齐标注
'20' AcDbArcDimension 圆弧标注
'21' AcDbDiametricDimension 直径标注
'22' AcDbOrdinateDimension 坐标标注
'23' AcDbRadialDimension 半径标注
'24' AcDbRadialDimensionLarge 半径折线标注
'25' AcDbRotatedDimension 转角标注
'26' AcDbAttributeDefinition 属性注记 文本类
'27' AcDbAttribute 块属性 文本类,块内
'28' AcDbTable 表格
'29' AcDbMLeader 引线标注
'30' AcDbRegion 面域
'31' AcDbPolyFaceMesh 多面网格实体
'32' AcDbPolygonMesh 多边形网格
'33' AcDbSurface 曲面
'34' AcDb3dSolid 三维实体
'35' AcDbFace 三维面
'36' AcDbSolid 实线实体

常用分组(写 SQL 时常合并查询)

-- 所有"线段"类(直线、二三维多段线)
name='1' or name='2' or name='3' or name='4'

-- 所有"曲线"类(圆弧、圆、椭圆、样条、曲线)
name='5' or name='6' or name='7' or name='8' or name='9'

-- 所有"文字"类(单行 / 多行 / 属性注记 / 块属性)
name='12' or name='13' or name='26' or name='27'

-- 所有"标注"类
name='17' or name='18' or name='19' or name='20' or name='21'
or name='22' or name='23' or name='24' or name='25' or name='29'

-- 所有"网格 / 面 / 体"类
name='30' or name='31' or name='32' or name='33' or name='34' or name='35'

不要硬编码 ID(推荐做法)

后台返回的 name ID 在不同版本可能调整。生产代码里用映射表:

const { entTypeIdMap } = await svc.getConstData();
// entTypeIdMap 形如 { '1': 'AcDbLine', '2': 'AcDbPolyline', ... }

const getTypeIdByName = name =>
  Object.keys(entTypeIdMap).find(k => entTypeIdMap[k] === name);

const lineId = getTypeIdByName('AcDbLine');
const cond = `name='${lineId}'`;

四、字段含义随类型变化(s3/s4/s5/n5~n10)

下表显示各实体类型对应字段的实际含义。空白处表示该类型不使用该字段。

类型 类名 中文 s3 s4 s5 n5 n6 n7 n8 n9 n10
'1' AcDbLine 直线 points thickness length
'2' AcDbPolyline 多段线 points bulge data3d area isclosed lineTypeScale thickness elevation
'3' AcDb2dPolyline 二维折线 points area isclosed polyType thickness elevation
'4' AcDb3dPolyline 三维多段线 points area isclosed polyType
'5' AcDbSpline 样条曲线 points area isclosed isFit fitTol degree
'6' AcDbArc 圆弧 center points area isclosed radius startAngle endAngle thickness
'7' AcDbCircle 圆 center area isclosed radius thickness
'8' AcDbEllipse 椭圆 center majorAxis points area isclosed radiusRatio startAngle endAngle majorRadius
'9' AcDbCurve 曲线 area isclosed
'10' AcDbBlockReference 块参照 positon attributeDef blockname rotate scaleX scaleY scaleZ
'11' AcDbHatch 填充 points patternName elevation patternAngle patternScale
'12' AcDbMText 多行文本 location contents text height width rotate textHeight actualHeight actualWidth
'13' AcDbText 单行文本 location text textStyle height rotate horzMode
'14' AcDbShape 型实体 positon normal rotate size
'15' AcDbRasterImage 栅格图片 origin sourceFileName
'16' AcDbWipeout 遮罩实体 origin sourceFileName
'26' AcDbAttributeDefinition 属性注记 location text contents height rotate horzMode
'27' AcDbAttribute 块属性 location text contents height rotate horzMode
'28' AcDbTable 表格 positon attributeDef contents rotate scaleX scaleY scaleZ
'29' AcDbMLeader 引线标注 location contents text height width rotate textHeight actualHeight actualWidth
'30' AcDbRegion 面域 points indices data3d area perimeter
'31' AcDbPolyFaceMesh 多面网格 points indices data3d
'32' AcDbPolygonMesh 多边形网格 points indices data3d
'33' AcDbSurface 曲面 points indices data3d
'34' AcDb3dSolid 三维实体 points indices data3d
'35' AcDbFace 三维面 points indices data3d
'36' AcDbSolid 实线实体 points thickness length isclosed

标注类('17'~'25')共用同一套专属字段:s3=location(标注点)、s4=text(标注文字)、n5=rotate、n6=measure(测量值)。 椭圆还有 minorRadius(次半径)字段定义,但已超出 n10 列上限,查询结果中不出现。

字段含义速查(按字段名)

字段 类型 说明
points text 坐标序列(x,y;x,y;...)
center text 圆 / 圆弧 / 椭圆中心点
location text 文字插入点
positon text 块 / 表格 / 型实体定位点(注:服务端拼写就是 positon)
origin text 栅格图片基点
area float 面积
length float 长度(直线 / 实体)
radius float 半径
startAngle / endAngle float 圆弧 / 椭圆起止角
thickness float 厚度
elevation float 高度
isclosed float 是否闭合(0/1)
polyType float 多段线类型
bulge text 多段线凸度
text text 文本内容(短)
contents text 多行文字(带样式)
height float 文字高(标注高)
width float 文字宽
rotate float 旋转角度
textHeight / actualHeight / actualWidth float 多行文字几何尺寸
horzMode float 水平对齐模式
blockname text 块名
attributeDef text 块属性定义
scaleX / scaleY / scaleZ float 块缩放
patternName text 填充图案名
patternAngle / patternScale float 填充角度 / 比例
majorAxis text 椭圆长轴
normal text 法向量
size float 大小
isFit / fitTol / degree float 样条拟合参数
indices text 网格 / 面索引序列
data3d text 3D 实体材质 / 顶点扩展
perimeter float 面域周长
sourceFileName text 栅格图片 / 遮罩的源文件名
textStyle text 单行文本的文字样式名
radiusRatio float 椭圆半径比(次轴/主轴)
majorRadius float 椭圆主轴半径(minorRadius 超出 n10 不上库)
measure float 标注测量值
lineTypeScale float 多段线线型比例(注意大写 T;通用字段 n2 是 linetypeScale 小写 t,两者不同名)

真实查询时不必死记字段对应。常见做法:先查少量数据 console.log 一次,把字段名记下来再写正式代码。

五、SQL 条件示例(按场景分类)

5.1 按类型查

-- 所有直线
name='1'

-- 所有圆
name='7'

-- 所有圆弧 + 圆 + 椭圆
name='6' or name='7' or name='8'

-- 所有文字
name='12' or name='13' or name='26' or name='27'

-- 所有块参照
name='10'

5.2 类型 + 图层

-- 0 号图层的三维多段线
name='4' and layerindex=0

-- 0 / 1 / 3 号图层的所有圆
name='7' and (layerindex=0 or layerindex=1 or layerindex=3)

5.3 类型 + 颜色

-- 红色单行文字(0xFF0000FF -> -16776961)
name='13' and color=-16776961

-- 蓝色直线(0xFFFF0000 -> -65536)
name='1' and color=-65536

-- 不是黑色的所有文字
(name='12' or name='13') and color != -16777216

5.4 类型 + 几何属性

-- 长度为 10 的直线(n6 = length)
name='1' and n6=10

-- 长度大于 100 的直线
name='1' and n6 > 100

-- 半径为 5 的圆(n7 = radius)
name='7' and n7=5

-- 半径在 [10, 20] 之间的圆
name='7' and n7 >= 10 and n7 <= 20

-- 闭合的多段线(n6 = isclosed)
name='2' and n6=1

-- 面积 > 1000 的面域(n5 = area)
name='30' and n5 > 1000

5.5 类型 + 文字内容

-- 文字以 "DZ" 开头(s4 = text 字段)
name='13' and s4 like 'DZ%'

-- 文字长度 [2, 12] 且以 J 或 DZ 开头
(name='12' or name='13' or name='26' or name='27')
  and LENGTH(s4) >= 2 and LENGTH(s4) <= 12
  and (s4 like 'J%' or s4 like 'DZ%')

-- 多行文字内容包含"水泵"
name='12' and s4 like '%水泵%'

5.6 按 objectid 查

-- 指定 objectid(普通实体就是句柄;块内实体带 id 链和尾部标记)
objectid='1A'
objectid='9BE_5C_3F_#'

-- 块 EA / 10D / 19A 下所有子实体
objectid like 'EA_%' or objectid like '10D_%' or objectid like '19A_%'

-- 某个块的子实体
objectid like '9BE_%'

5.7 按块名查(块参照)

-- 块名为 "门" 的所有块参照(s5 = blockname)
name='10' and s5='门'

-- 块名以 "Door" 开头
name='10' and s5 like 'Door%'

5.8 按填充图案查

-- 填充图案为 ANSI31
name='11' and s4='ANSI31'

六、查询接口

6.1 条件查询 conditionQueryFeature

✅ 仅 GeomRender 可用(objectid>0 条件 + limit/beginpos 分页正常,见本文 §一)。返回 {error:"not support query type!"} 表示请求被路由到了内存进程(图是 Memory 方式打开,或请求里 geom 为 false)——显式传 geom: true,并确认该图已以 GeomRender 方式打开过。

svc.conditionQueryFeature(param: IConditionQueryFeatures, cb?): Promise<any>;

interface IConditionQueryFeatures extends IQueryBaseFeatures {
  /** SQL where 条件,必填 */
  condition: string;
  /** 矩形范围([x1,y1,x2,y2])或多边形点数组([[x,y],...])—— CAD 坐标 */
  bounds?: [number, number, number, number] | [number, number][];
  /** bounds 为点数组时,是否求交点;默认 false */
  isGetIntersections?: boolean;
  /** 起始记录位置(分页用)*/
  beginpos?: number;
  /** 是否包含几何信息(默认仅在 recordCount==1 时返回真实 geojson)*/
  includegeom?: boolean;
  /** 是否每条都返回真实几何 geojson(数据量大时慎用)*/
  realgeom?: boolean;
  /** 范围查询时:true=包含 / false=相交(默认)*/
  isContains?: boolean;
}

最小用法:

const q = await svc.conditionQueryFeature({
  condition: `name='13' or name='12' or name='26' or name='27'`,
  fields: "objectid,text,location,envelop",
  geom: true,
  limit: 100000  // 默认 100,要全量必须放大
});
if (q.error) { console.error(q.error); return; }
console.log(q.recordCount, q.result);

带空间过滤:

// 矩形 + 类型
await svc.conditionQueryFeature({
  condition: `name='7'`,
  bounds: [100, 200, 500, 800],
  isContains: false,   // false=相交(默认);true=必须完全在矩形内
  limit: 100000
});

// 多边形 + 类型
await svc.conditionQueryFeature({
  condition: `name='10'`,
  bounds: [[0,0],[100,0],[100,100],[0,100]],
  isContains: true
});

6.2 点查询 pointQueryFeature

svc.pointQueryFeature(param: IPointQueryFeatures, cb?): Promise<any>;

interface IPointQueryFeatures extends IQueryBaseFeatures {
  /** 查询点 X(CAD 坐标)*/
  x: number;
  /** 查询点 Y */
  y: number;
  /** 像素容差(屏幕几像素内都算命中)*/
  pixelsize?: number;
  /** 进一步用 SQL 过滤 */
  condition?: string;
  /** 单实体最大返回字节数(防 OOM)*/
  maxGeomBytesSize?: number;
  /** 一像素表示的几何长度(不传则按 zoom 算)*/
  pixelToGeoLength?: number;
}

典型场景——鼠标点击选实体:

map.on("click", async (e) => {
  const cad = map.fromLngLat(e.lngLat);
  const q = await svc.pointQueryFeature({
    x: cad.x, y: cad.y,
    pixelsize: 5,
    zoom: map.getZoom(),
    limit: 20,
    geom: true,
    toMapCoordinate: true
  });
  console.log(q.result);
});

6.3 矩形查询 rectQueryFeature

svc.rectQueryFeature(param: IRectQueryFeatures, cb?): Promise<any>;

interface IRectQueryFeatures extends IQueryBaseFeatures {
  /** 矩形左下点 X(不传则查整图)*/
  x1?: number;
  y1?: number;
  /** 矩形右上点 X */
  x2?: number;
  y2?: number;
  /** 进一步用 SQL 过滤 */
  condition?: string;
  /** 单实体最大几何字节 */
  maxGeomBytesSize?: number;
}
await svc.rectQueryFeature({
  x1: 0, y1: 0, x2: 1000, y2: 1000,
  condition: `name='7'`,
  limit: 100000,
  geom: true
});

矩形查询也可改用 conditionQueryFeature({ bounds: [x1,y1,x2,y2] }),效果相同。

6.4 多边形查询

vjmap 没有独立的多边形查询接口,使用 conditionQueryFeature + bounds 数组:

await svc.conditionQueryFeature({
  condition: `name='10'`,
  bounds: [[100,100],[500,100],[500,500],[100,500]],   // 多边形顶点
  isContains: true     // true=完全在多边形内;false=相交
});

6.5 分页查询(数据量大时必须)

limit 不能无限大,超过后台限制会被截断。万级以上结果集分页:

async function queryAll(condition, fields = "") {
  const all = [];
  let begin = 0;
  while (true) {
    const q = await svc.conditionQueryFeature({
      condition, fields,
      beginpos: begin,
      limit: 10000   // 每次 1 万
    });
    if (q.error) throw new Error(q.error);
    all.push(...(q.result || []));
    begin += q.result.length;
    if (all.length >= q.recordCount) break;
  }
  return all;
}

const lines = await queryAll(
  `name='1' or name='2' or name='3' or name='4'`,
  "objectid,points,envelop"
);

七、统一查询参数 IQueryBaseFeatures

所有查询接口都继承下面这组通用参数:

interface IQueryBaseFeatures {
  /** 当前缩放级别(影响 pixel→几何转换、几何简化精度)*/
  zoom?: number;
  /** 地图 ID(不传则用当前已 open 的图)*/
  mapid?: string;
  /** 地图版本 */
  version?: string;
  /** 图层名 */
  layer?: string;
  /** 最大返回条数。条件/矩形默认 100,要全量必须自己设大;点查询默认只返回 1 条(Memory 下恒为 1) */
  limit?: number;
  /** 字段过滤,逗号分隔。如 "objectid,text,envelop"。空字符串 "" 表示返回所有字段。
      既可写属性名(objectid/text/area),也可写原始列名 s1..s5 / n1..n10(结果里以列名为键)*/
  fields?: string;
  /** 是否走几何渲染空间库查询(不是"是否返回几何");不传时 SDK 取当前已打开图的打开方式。要几何用 includegeom/realgeom */
  geom?: boolean;
  /** GeoJSON 简化容差(墨卡托单位),减少传输量。0=不简化 */
  simplifyTolerance?: boolean;
  /** 启用查询缓存(仅内存模式) */
  useCache?: boolean;
  /** true=直接返回 CAD 坐标;false=返回墨卡托坐标(默认)*/
  toMapCoordinate?: boolean;
}

字段过滤建议

不写 fields 会返回所有字段,移动端 / 弱网下流量大。建议明确列出:

// 只要 ID 和外包矩形
fields: "objectid,envelop"

// 文本类查询
fields: "objectid,text,location,height,rotate"

// 圆查询
fields: "objectid,center,radius,area"

八、查询返回结果字段

q.result[i] 中可能出现的字段(取决于 fields 与实体类型):

字段 类型 说明
objectid text 实体唯一 ID(可作为 key)
name text 实体类型 ID(同上文)
layerindex int 图层索引
color int 颜色(用 vjmap.entColorToHtmlColor() 转 HTML——要求 0xAABBGGRR 打包 32 位整数;小整数如 7 的 hex 不足 8 位会得到畸形 "#",见文末坑位)
linetype text 线型
linetypeScale float 线型比例
lineWidth float 线宽
alpha float 透明度
xdata any 扩展数据
bounds text 外包矩形(CAD 坐标,[minx,miny,maxx,maxy] 字符串)
envelop text 外包矩形(墨卡托 WKT,map.getEnvelopBounds() 解析)
geojson text 真实几何 GeoJSON 字符串(默认原始墨卡托坐标;toMapCoordinate:true 时为 CAD 坐标;条件查询仅 includegeom:true 时才有值——不加 realgeom 时多条结果给的是外包矩形多边形;指定 fields 时须把 geojson 列进去)
geom object SDK _processQueryResult 转换后的几何(渲染经纬度,可直接进 source;isMapCoordinate 或 geojson 为空时不生成)——坐标空间细节见 coord/query-coords
isEnvelop bool 查询时是否仅给外包盒
points text 坐标序列 x,y;x,y;...
indices text 索引序列(网格 / 面)
center text 圆心
radius float 半径
startAngle / endAngle float 弧度
area / length / perimeter float 几何度量
isclosed bool 是否闭合
text / contents text 文本
height / width / textHeight / actualHeight / actualWidth float 文字几何
rotate / scaleX / scaleY / scaleZ float 块 / 表格变换
blockname text 块名
location / positon / origin text 位置点
normal text 法向量
thickness / elevation float 厚度 / 高程
bulge text 多段线凸度
polyType float 多段线类型
data3d text 3D 材质 / 数据
majorAxis text 椭圆长轴
u / v float UV 坐标
size float 大小
isFit / fitTol / degree float 样条参数
horzMode float 水平对齐

九、绘制查询结果到地图

9.1 用外包矩形画 Polygon(最快,适合大量结果高亮)

const polygons = q.result.map(r => ({
  points: map.toLngLat(map.getEnvelopBounds(r.envelop).toPointArray()),
  properties: {
    id: r.objectid,
    color: vjmap.entColorToHtmlColor(r.color),
    text: r.text
  }
}));

const layer = new vjmap.Polygon({
  data: polygons,
  fillColor: ['case', ['to-boolean', ['feature-state', 'hover']], 'yellow', '#f0f'],
  fillOpacity: ['case', ['to-boolean', ['feature-state', 'hover']], 0.6, 0.4],
  fillOutlineColor: ['get', 'color'],
  isHoverPointer: true,
  isHoverFeatureState: true
});
layer.addTo(map);
layer.hoverFeatureState(e => {
  if (e.features) {
    map.logInfo(`选中 ${e.features[0].properties.id}: ${e.features[0].properties.text}`);
  }
});

map.logInfo(text, type?):向 document.body 末尾追加 #vjmapinfolog 容器、内部每条消息是一个 span.vjmapinfolog-{type}(type 如 success)——不是挂在地图容器内的 DOM,自动化断言或截图取景时别在 map 容器里找它。同类 logWarn/logError 不存在,只有 logInfo 一个入口。

9.2 用真实几何画 GeoJSON 线 / 面

// 查询时加 includegeom:true + realgeom:true:SDK 已把每条几何转成渲染经纬度写到 r.geom
// ⚠️ 不要直接 JSON.parse(r.geojson) 进 source——那是原始墨卡托坐标,会全部出界
const fc = {
  type: "FeatureCollection",
  features: (q.result ?? []).filter(r => r.geom).map(r => ({
    type: "Feature",
    geometry: r.geom.geometries[0],
    properties: { id: r.objectid }
  }))
};
map.addSource("q", { type: "geojson", data: fc });
map.addLayer({
  id: "q-line",
  type: "line",
  source: "q",
  paint: { "line-color": "#ff0", "line-width": 2 }
});

9.3 中心点 Marker

for (const r of q.result) {
  const center = map.getEnvelopBounds(r.envelop).center();
  new vjmap.Marker()
    .setLngLat(map.toLngLat(center))
    .setPopup(new vjmap.Popup().setHTML(r.text || r.objectid))
    .addTo(map);
}

9.4 批量定位 fitBounds

const all = new vjmap.GeoBounds();
for (const r of q.result) all.updateByBounds(map.getEnvelopBounds(r.envelop));
map.fitMapBounds(all, { padding: 80 });

9.5 闪烁定位(AI MCP 常用)

async function flashBounds(boundsArray) {
  const pts = vjmap.GeoBounds.fromArray(boundsArray).toPointArray();
  pts.push(pts[0]);
  const layer = new vjmap.Polyline({
    data: [{ points: map.toLngLat(pts), properties: { opacity: 1 } }],
    lineColor: "yellow",
    lineWidth: 3,
    lineOpacity: ["get", "opacity"]
  });
  layer.addTo(map);

  await new Promise(resolve => vjmap.createAnimation({
    from: 1, to: 10, duration: 1000,
    onUpdate: v => {
      const data = layer.getData();
      data.features[0].properties.opacity = parseInt(v) % 2;
      layer.setData(data);
    },
    onComplete: () => { layer.remove(); resolve(); }
  }));
}

十、典型完整示例

10.1 查所有文字并高亮

const { entTypeIdMap } = await svc.getConstData();
const idOf = name => Object.keys(entTypeIdMap).find(k => entTypeIdMap[k] === name);

const cond = ['AcDbText','AcDbMText','AcDbAttributeDefinition','AcDbAttribute']
  .map(n => `name='${idOf(n)}'`).join(' or ');

const q = await svc.conditionQueryFeature({
  condition: cond,
  fields: "objectid,text,envelop,color",
  geom: true,
  limit: 100000
});

if (q.error) return message.error(q.error);

const polygons = q.result.map(r => ({
  points: map.toLngLat(map.getEnvelopBounds(r.envelop).toPointArray()),
  properties: {
    id: r.objectid,
    color: vjmap.entColorToHtmlColor(r.color),
    text: r.text
  }
}));
new vjmap.Polygon({
  data: polygons,
  fillColor: '#f0f', fillOpacity: 0.4,
  fillOutlineColor: ['get', 'color']
}).addTo(map);

10.2 查指定块下的所有子实体(取面积最大的)

const blockObjectId = '9BE';

const sub = await svc.conditionQueryFeature({
  condition: `objectid like '${blockObjectId}_%'`,
  fields: "objectid,name,n5",   // n5 对多段线 / 圆 / 面域 = area
  limit: 100000
});

const max = sub.result.reduce((a, b) => (a?.n5 || 0) > (b?.n5 || 0) ? a : b);
console.log("最大子实体:", max);

10.3 查鼠标点击附近的圆并高亮半径

map.on("click", async e => {
  const cad = map.fromLngLat(e.lngLat);
  const q = await svc.pointQueryFeature({
    x: cad.x, y: cad.y,
    pixelsize: 5,
    condition: `name='7'`,        // 限定只查圆
    fields: "objectid,center,radius",
    zoom: map.getZoom(),
    toMapCoordinate: true,
    limit: 5
  });
  q.result.forEach(c => console.log(`圆 ${c.objectid}: 半径 ${c.radius}`));
});

10.4 分页查所有横竖直线

const queryEntTypes = ['AcDbLine','AcDbPolyline','AcDb2dPolyline','AcDb3dPolyline'];
const cond = queryEntTypes.map(t => `name='${idOf(t)}'`).join(' or ');

const result = [];
let begin = 0;
while (true) {
  const q = await svc.conditionQueryFeature({
    condition: cond,
    fields: "objectid,points,envelop",
    beginpos: begin,
    limit: 100000
  });
  if (q.error) break;
  result.push(...(q.result || []));
  begin += q.result.length;
  if (result.length >= q.recordCount) break;
}

// 过滤纯横竖
const hvLines = result.filter(e => {
  const pts = e.points.split(";");
  if (pts.length !== 2) return false;
  const [a, b] = pts.map(p => vjmap.geoPoint(p.split(",")));
  return vjmap.isZero(a.x - b.x) || vjmap.isZero(a.y - b.y);
});

十一、性能建议

  1. fields 越少越快。大数据查询禁用 *,只列必要字段。
  2. 不要用 geom: false 省流量——它会把查询路由到内存进程(条件查询直接报 not support query type!);省流量靠精简 fields、不传 includegeom。
  3. 大结果集必须分页(limit + beginpos),避免一次性请求百万记录。
  4. realgeom: true 仅在数量少(<= 几百)时用;批量结果用 envelop 即可,足以做高亮 / 定位。
  5. 绘制结果用 Polygon / Polyline 一次 setData,不要循环 addTo。
  6. 动态查询前 await map.onLoad(),否则 addLayer 报错。
  7. 能用 SQL 范围过滤就别全量后再过滤:bounds 让后台空间索引发挥作用。
  8. color 字段用整数比较,不要在 SQL 里转换 RGB 字符串。

十二、常见坑

  1. name=1 查不到结果 → name 是字符串字段,必须 name='1'。
  2. color=0xFF0000FF 写成正数没结果 → 后台保存的是 32 位有符号整数,红色用 -16776961。
  3. 拿到 envelop 直接当 CAD 坐标 → 它是墨卡托 WKT,必须 map.getEnvelopBounds(envelop) 转 CAD。
  4. 用 name=10 期望查到块的子实体 → name='10' 只是块参照本身;子实体是块定义里的实体,要用 objectid like '块id_%'。
  5. limit 没传 → 默认只返回 100 条(点查询只返回 1 条),业务上易误判"图里只有 100 条"。
  6. 多图并存时忘指定 mapid → 查到的是当前 open 的图。查非当前图时必须同时传 mapid、geom: true、layer: await svc.getStyleLayerName(mapid)——否则 SDK 沿用当前图的样式图层名,服务端找不到该样式静默返回 {};或按当前图的打开方式填 geom,被路由到内存进程报 not support query type!。
  7. isContains: true 漏实体 → 完全包含很严格,稍微越界就不算。一般 false(相交)符合直觉。
  8. fields: "" 期望返回所有字段 → 是的,空字符串等价于全字段,但写 null 或不传也是同样效果。
  9. 拼 SQL 时把用户输入直接拼接 → SQL 注入风险。SDK 没有内置防护,需要自己做白名单 / 转义。
  10. 几何渲染图却用 exprQueryFeature 全表扫 → 性能差。能用 SQL 一定用 query-sql 走空间库。
  11. 多版本下没传 version → 拿到的是当前版本;老版本数据要显式 version: "v2"。
  12. objectid 当成数字 → 它是字符串(如 "1A"、"9BE_5C_3F_#"、"5C$2A"),业务里始终当字符串处理。
  13. 服务端字段拼写:positon(不是 position)、bulge(多段线凸度)等,写 SQL 时容易拼错。
  14. entColorToHtmlColor(小整数) 得畸形 "#"→ 实现把数字按 (c>>>0).toString(16) 后定长 8 位切片(0xAABBGGRR 打包布局)。后端 color 字段是打包好的 int32(如 -65536)→ 正常;自己传小整数(如 7)hex 不足 8 位、切片全空 → 返回 "#"。字符串颜色原样返回;#757675 有 darkMode 特判。
  15. getEnvelopBounds 的 WKT 解析很脆→ 实现是 replace("POLYGON((","") 精确串匹配 + 只取前 4 个坐标点。带空格的 POLYGON ((...))、孔洞、多边形顶点 ≠4 都会解析错。仅信任 conditionQueryFeature 返回的 envelop 格式,别拿它解通用 WKT。

十三、相关文档