# Query Example (Auto-Label Positions by Survey Point ID)

No manual labeling required—automatically label positions on CAD drawings in the web client based on survey point IDs

# Requirements Analysis

The CAD drawing looks like this:

image-20220913205333681

The backend database only has survey point IDs and values, as in the list below:

Survey Point ID Type Value

DZ129-2 Type 1 Value 1

DZ128-2 Type 1 Value 2

DZ127-2 Type 1 Value 3

J28 Type 2 Value 5

J26 Type 2 Value 6

J27 Type 2 Value 7

J29 Type 2 Value 8

.......

How can we display these survey point values at the corresponding positions on the CAD drawing? See below:

Since the backend database has no survey point coordinates, the traditional approach is to manually label each survey point on the map, save the positions to the database, and then retrieve both positions and survey data for display. The drawback is obvious: with many survey points, manual labeling is time-consuming and labor-intensive.

Is there a better way to compute survey point positions from the drawing with one click?

# Implementation

# Pattern Analysis

To find the geographic location for each survey point ID, we need to know the pattern or rules for each type.

For the two survey point types in the example:

Survey Type 1

image-20220913210838860

Pattern: Each survey point has a filled triangular symbol beside it

Survey Type 2

image-20220913210939752

Pattern: Each survey point has a filled red circular symbol beside it

# Find Corresponding Graphic Symbols by Pattern

There are several ways to search, such as image recognition or geometry analysis. Image recognition is sensitive to noise and slower. Geometry analysis is faster and more accurate. We use geometry analysis here.

Steps:

(1) Traverse all entity types in the drawing

(2) Determine matches by entity type and extent size

# Search for Survey Point IDs Near Graphic Symbols and Build Associations

Steps:

(1) Search for text within a certain range of the symbol (e.g., a multiple of the symbol extent) and pick the nearest text

(2) Filter out non–survey point IDs by content rules (e.g., in this drawing survey IDs start with DZ or J and have limited length)

(3) Among all valid survey point IDs, pick the one closest to the symbol as the final survey point ID

(4) Associate the survey point ID with the symbol coordinates.

# Key Code Implementation

 const queryMapText = async () => {
            let query = await svc.conditionQueryFeature({
                // Query all text (single-line, multiline, block annotation, attribute text). See "Server-side Condition Query and Expression Query - Supported CAD Entity Types" for type numbers
                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 // Query all
            })
            if (query.error) {
                message.error(query.error)
            } else {
                query.result.forEach(rst => {
                    rst.position = vjmap.GeoPoint.fromString(rst.location); // Get coordinates
                })
                return  query.result;
            }
        }
        // Get text info from the drawing
        let mapAllText = await queryMapText();
        console.log(mapAllText);

        // Find all hatches
        const queryHatch = async () => {
            let query = await svc.conditionQueryFeature({
                condition: `(name='11') `, // Type hatch
                fields: "",
                includegeom: true,
                limit: 100000 // Query all
            })
            if (query.error) {
                message.error(query.error)
            } else {
                query.result.forEach(rst => {
                    rst.geoBounds = vjmap.GeoBounds.fromString(rst.bounds); // Get extent
                    let w = rst.geoBounds.width();
                    let h = rst.geoBounds.height();
                    // If radius is 2, it's a circle, J_ type
                    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 triangle hatch
                            if (vjmap.isZero(w - 2.5, 1) && vjmap.isZero(h - 2.5, 1))
                            {
                                rst.type = "triangle";
                            }

                        }
                    }

                })

                return  query.result;
            }
        }
        // Get hatch info from the drawing
        let mapAllCircleHatch = await queryHatch();
        console.log(mapAllCircleHatch);

        // Circles are paired with text starting with J
        let dataCircleTextG = mapAllCircleHatch.filter(rst => rst.type == "circle_28")
        dataCircleTextG.forEach(circle => {
            // Find the nearest text starting with 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) // Find matching text
            // Pick the nearest
            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"
                })
            }

        })

        // Triangles are paired with text starting with DZ
        let dataTriangleTextDz = mapAllCircleHatch.filter(rst => rst.type == "triangle")
        dataTriangleTextDz.forEach(tri => {
            // Find the nearest text starting with 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) // Find matching text
            // Pick the nearest
            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)
        // Display data on the map
        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();// Remove existing first
            }
            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

# Final Result

image-20220913212244279

The example code above is open source. For data privacy, the demo uses sample maps from the VJMap website; the approach is the same. Visit https://vjmap.com/demo/#/demo/map/service/22findtextbyrules (opens new window) to see the effect and code.