# 查询示例(根据测点编号自动标注位置)

无需人工标注位置,一键在Web端CAD图上根据测点编号自动标注位置

# 需求分析

CAD图如下图所示

image-20220913205333681

在后台数据库中只有测点编号及测点值的信息,如下列列表

测点编号 测点类型 测点值

DZ129-2 类型1 值1

DZ128-2 类型1 值2

DZ127-2 类型1 值3

J28 类型2 值5

J26 类型2 值6

J27 类型2 值7

J29 类型2 值8

.......

那怎么把上面的测点值在CAD图相应位置展示出来呢?如下图

因为后台数据库中没有测点坐标信息,传统的方法一般是事先人工在地图上手工标注每个测点对应的位置,然后把位置信息保存至后台数据库。下次再从数据中获取位置信息和测点信息显示在地图上。这个方法的缺点也很明显,如果测点多时,人工标注每个测点对应的位置是一件费时费力的事情。

那么有没有好的方法根据图的信息一键计算出相应测点的位置呢?

# 实现

# 规律分析

要找出测点编号对应的地理位置就得知道每个类型的测点的规律或规则是什么。

如上例图的两种不同的测点:

测点类型1

image-20220913210838860

规律为:每个测点旁边有一个填充的三角形测点符号

测点类型2

image-20220913210939752

规律为:每个测点旁边有一个填充的红色的圆形符号

# 根据规律查找对应的图形符号

查找方法有多种,如图像识别,图形分析等。因为图像识别干扰因素多,同时性能慢。而图形分析速度快,准确率高。这里我们采用图形分析来查找。

步骤为:

(1) 遍历图中所有实体类型

(2)通过实体类型和实体范围大小来确定是否满足条件

# 根据图形符号搜索旁边对应的测点编号,并建立关联关系

步骤为:

(1) 搜索符号旁边一定范围(如可设置为符号范围的几倍),设置旁边最近的文字

(2) 根据文字的内容规则去排查非测点编号名称(如此图中测点编号是以 DZ或J开头的,长度也有限制)

(3) 在所有满足条件的测点编号名称中查找一个距离最近的做为最终的测点编号名称

(4) 把测点编号名称和图形符号的坐标建立关联关系。

# 关键代码实现

 const queryMapText = async () => {
            let query = await svc.conditionQueryFeature({
                // 查询所有文字(包括单行文本,多行文本、块注记文字,属性文字) 具体类型数字参考文档"服务端条件查询和表达式查询-支持的cad实体类型"
                condition: `(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%')`,
                fields: "",
                limit: 100000 // 全部查吧
            })
            if (query.error) {
                message.error(query.error)
            } else {
                query.result.forEach(rst => {
                    rst.position = vjmap.GeoPoint.fromString(rst.location); // 获取坐标
                })
                return  query.result;
            }
        }
        // 获取图中的文字信息
        let mapAllText = await queryMapText();
        console.log(mapAllText);

        // 查找所有hatch
        const queryHatch = async () => {
            let query = await svc.conditionQueryFeature({
                condition: `(name='11') `, // 类型为hatch
                fields: "",
                includegeom: true,
                limit: 100000 // 全部查吧
            })
            if (query.error) {
                message.error(query.error)
            } else {
                query.result.forEach(rst => {
                    rst.geoBounds = vjmap.GeoBounds.fromString(rst.bounds); // 获取范围
                    let w = rst.geoBounds.width();
                    let h = rst.geoBounds.height();
                    // 如果半径为2,则是圆,旁边是J_的
                    if (vjmap.isZero(w - 2, 0.1) && vjmap.isZero(h - 2, 0.1))
                    {
                        rst.type = "circle_20";
                    } else {
                        if (rst.geom.geometries[0].type == "Polygon" && rst.geom.geometries[0].coordinates[0].length == 5) {
                            // 如果是三角形填充
                            if (vjmap.isZero(w - 2.5, 1) && vjmap.isZero(h - 2.5, 1))
                            {
                                rst.type = "triangle";
                            }

                        }
                    }

                })

                return  query.result;
            }
        }
        // 获取图中的文字信息
        let mapAllCircleHatch = await queryHatch();
        console.log(mapAllCircleHatch);

        // 圆旁边是文字J开头的
        let dataCircleTextG = mapAllCircleHatch.filter(rst => rst.type == "circle_28")
        dataCircleTextG.forEach(circle => {
            // 查找距离最近的一个以J开头的文字
            let texts = mapAllText.filter(t => t.text.substr(0, 1) == "J")
            let findTexts = texts.filter(t => t.position.distanceTo(circle.geoBounds.center()) <= 2.8 * 3) // 查找符合条件的文字
            // 找一个距离最近的
            if (findTexts.length > 0) {
                findTexts = findTexts.sort((a, b) => a.position.distanceTo(circle.geoBounds.center()) - b.position.distanceTo(circle.geoBounds.center()) )
                drawDatas.push({
                    point: circle.geoBounds.center(),
                    name: findTexts[0].text,
                    color: "#0f0"
                })
            }

        })

        // 三角形旁边是文字DZ开头的
        let dataTriangleTextDz = mapAllCircleHatch.filter(rst => rst.type == "triangle")
        dataTriangleTextDz.forEach(tri => {
            // 查找距离最近的一个以DZ开头的文字
            let texts = mapAllText.filter(t => t.text.substr(0, 2) == "DZ")
            let findTexts = texts.filter(t => t.position.distanceTo(tri.geoBounds.center()) <= 2.8 * 3) // 查找符合条件的文字
            // 找一个距离最近的
            if (findTexts.length > 0) {
                findTexts = findTexts.sort((a, b) => a.position.distanceTo(tri.geoBounds.center()) - b.position.distanceTo(tri.geoBounds.center()) )
                drawDatas.push({
                    point: tri.geoBounds.center(),
                    name: findTexts[0].text,
                    color: "#f0f"
                })
            }
        })

        console.log(drawDatas)
        // 把数据显示到图上
        let symbols;
        const showDataInMap = (datas)=> {
            let geoDatas = []
            for(let i = 0; i < datas.length; i++) {
                const pt = datas[i].point;
                const data = {
                    point: map.toLngLat(pt),
                    properties: {
                        name:  datas[i].name,
                        textColor: datas[i].color
                    }
                }
                geoDatas.push(data);
            }
            if (symbols) {
                symbols.remove();// 如果有先删除了
            }
            symbols = new vjmap.Symbol({
                data: geoDatas,
                iconImage: "stretchTextBackImg",
                iconAnchor: "bottom",
                iconOpacity: 0.5,
                iconOffset: [-2, -10],
                textTranslate: [-2, -6],
                textAnchor: "bottom",
                textField: ['get', 'name'],
                textFont: ['Arial Unicode MS Regular'],
                textSize: 14,
                textColor: ['get', 'textColor'],
                iconTextFit: "both",
                iconAllowOverlap: true,
                textAllowOverlap: true
            });
            symbols.addTo(map);
        }
        showDataInMap(drawDatas);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132

# 最效效果

image-20220913212244279

上面的案例代码已开源,出于对用户数据保密的需要,案例代码用唯杰地图官网的示例图进行了替换,思路一致。访问 https://vjmap.com/demo/#/demo/map/service/22findtextbyrules (opens new window) ,查看效果和代码即可。