cesium 输入经纬度定位到3dtiles

在 GIS 开发中,三维地理数据的可视化和交互是非常重要的功能。本文将介绍如何使用 SmartEarth SDK(基于 Cesium 的扩展库)来加载 3D Tiles 模型,并实现模型的属性查询和高亮选择功能。

本示例主要实现了以下功能:

  1. 加载多个 3D Tiles 模型
  2. 根据经纬度定位并创建标记点
  3. 查询并显示 3D Tiles 模型的属性信息
  4. 实现模型对象的高亮显示

这些功能在智慧城市、BIM 管理、城市规划等三维可视化应用中非常常见。下面我们来逐一介绍关键技术实现。

根据经纬度创建标记点,并添加到场景中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const position = Cesium.Cartesian3.fromDegrees(lon1, lat1, 60);
let cartographicPosition = Cesium.Cartographic.fromCartesian(position);
const height = Viewer.scene.sampleHeight(cartographicPosition);

// 创建标签
const label = earthCtrl.factory.createLabel({
lon: lon1,
lat: lat1,
alt: height + 1,
text: "创建文本",
image: "https://cim.smartearth.cn/sdkdemo/master/src/static/image/mark.png",
// 文本偏移量
pixelOffset: new SmartEarth.Cesium.Cartesian2(0, -50),
// 图片偏移量
iPixelOffset: new SmartEarth.Cesium.Cartesian2(0, -20),
});

这段代码演示了如何创建一个带有图标和文本的标记点,sampleHeight方法用于获取该位置的地面高度,确保标记点正确放置。

4. 查询显示 3D Tiles 属性信息

当点击模型时,我们需要获取并显示该位置模型的属性信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 高亮逻辑
const winPosition = Viewer.scene.cartesianToCanvasCoordinates(position);
const pickObject = Viewer.scene.pick(winPosition);

// 获取所有属性
const propertyIds = pickObject.getPropertyIds();
let tooltipHTML = "";
for (let i = 0; i < propertyIds.length; ++i) {
const propertyId = propertyIds[i];
const value = pickObject.getProperty(propertyId);
value && (tooltipHTML += `${propertyId}:${value || "无"}<br>`);
}

// 显示提示框
tooltip.showAts({ position: winPosition, text: tooltipHTML });

这段代码通过pick函数获取鼠标位置下的 3D Tiles 对象,然后遍历其所有属性,生成 HTML 格式的提示内容。

5. 实现 3D Tiles 模型高亮

实现点击模型后高亮显示选中的对象:

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
// 把上一个高亮的tileset还原
if (previousTileSet) {
previousTileSet.style = new Cesium.Cesium3DTileStyle({
color: {
evaluateColor: feature => {
return SmartEarth.Cesium.Color.WHITE;
},
},
});
}

// 设置新的高亮样式
const pickId = pickObject.getProperty("id");
tileset.style = new Cesium.Cesium3DTileStyle({
color: {
evaluateColor: feature => {
const featueId = feature.getProperty("id");
if (pickId === featueId) {
return SmartEarth.Cesium.Color.YELLOW;
} else {
return SmartEarth.Cesium.Color.WHITE;
}
},
},
});
previousTileSet = tileset;

这段代码使用Cesium3DTileStyle来设置模型的颜色样式。通过比较当前选中对象的 ID 和模型中每个 feature 的 ID,将匹配的对象设置为黄色高亮显示,其余部分保持白色。

6. 相机飞行定位

最后,实现相机飞行到指定模型的功能:

1
2
3
4
5
const options = {
duration: 2,
offset: new SmartEarth.Cesium.HeadingPitchRange(1.0, -0.3, 1000),
};
earthCtrl.userScene.flyTo(tiltModel1.item, options);

这段代码使用flyTo方法使相机飞行到指定的模型位置,并设置了适当的视角和距离。

完整代码

下面是完整的 HTML 和 JavaScript 代码:

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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>初始化示例</title>
<link rel="stylesheet" href="./CimSDK/CimSDK.min.css" />
<link rel="stylesheet" href="./CimSDK/Workers/layui/css/layui.css" />
<style>
.funcBox {
position: absolute;
top: 30px;
padding: 10px;
width: 470px;
background-color: rgba(0, 0, 0, 0.7);
border-radius: 4px;
left: 20px;
color: #fff;
}

.layui-form-item {
text-align: center;
}

.layui-form-label {
width: unset !important;
padding: 9px !important;
}

.layui-input-block {
margin-left: 50px !important;
}
</style>
</head>

<body>
<div id="sdkContainer"></div>
<!--按钮-->
<div class="funcBox layui-form">
<div class="layui-form-item">
<label class="layui-form-label">lon</label>
<div class="layui-input-block">
<input id="lon" placeholder="请输入经度" class="layui-input" />
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">lat</label>
<div class="layui-input-block">
<input id="lat" placeholder="请输入纬度" class="layui-input" />
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">alt</label>
<div class="layui-input-block">
<input id="alt" placeholder="请输入高度" class="layui-input" />
</div>
</div>
<div class="layui-form-item">
<button id="btn1" class="layui-btn">定位</button>
</div>
</div>
<script src="./CimSDK/Workers/jquery-3.5.0.min.js"></script>
<script src="./CimSDK/CimSDK.min.js"></script>
<script type="module">
window.earthCtrl = new SmartEarth.EarthCtrl("sdkContainer");
window.Viewer = earthCtrl.viewer;
const Cesium = SmartEarth.Cesium;

//显示fps
earthCtrl.showFPS = true;

//预载入输入框值
const lon1 = 118.36378039081389;
$("#lon").val(lon1);

const lat1 = 31.378263226883764;
$("#lat").val(lat1);

const alt1 = 60;
$("#alt").val(alt1);

let model;
let previoisEntity = null;
let previousTileSet = null;
let tooltip = earthCtrl.core.createTooltip();
tooltip.showFun(false);
async function load() {
const lon1 = $("#lon").val();
const lat1 = $("#lat").val();
const alt1 = $("#alt").val();
// 加标记点
const position = Cesium.Cartesian3.fromDegrees(lon1, lat1, 60);
let cartographicPosition = Cesium.Cartographic.fromCartesian(position);
const height = Viewer.scene.sampleHeight(cartographicPosition);
// 移除上一个点
if (previoisEntity) {
Viewer.entities.remove(previoisEntity);
}
const label = earthCtrl.factory.createLabel({
lon: lon1,
lat: lat1,
alt: height + 1,
text: "创建文本",
image: "https://cim.smartearth.cn/sdkdemo/master/src/static/image/mark.png",
// 文本偏移量
pixelOffset: new SmartEarth.Cesium.Cartesian2(0, -50),
// 图片偏移量
iPixelOffset: new SmartEarth.Cesium.Cartesian2(0, -20),
});
previoisEntity = label._primitive;
// 高亮逻辑
const winPosition = Viewer.scene.cartesianToCanvasCoordinates(position);
const pickObject = Viewer.scene.pick(winPosition);
const tileset = pickObject.content._tileset;
// 把上一个高亮的tileset还原
if (previousTileSet) {
previousTileSet.style = new Cesium.Cesium3DTileStyle({
color: {
evaluateColor: feature => {
return SmartEarth.Cesium.Color.WHITE;
},
},
});
}
const pickId = pickObject.getProperty("id");
const propertyIds = pickObject.getPropertyIds(); // 获取所有属性
// 弹窗显示所有属性
const length = propertyIds.length;
let tooltipHTML = "";
for (let i = 0; i < length; ++i) {
const propertyId = propertyIds[i];
console.log(`${propertyId}: ${pickObject.getProperty(propertyId)}`);
const value = pickObject.getProperty(propertyId);
value && (tooltipHTML += `${propertyId}:${value || "无"}<br>`);
}
tooltip.showAts({ position: winPosition, text: tooltipHTML });
// 高亮逻辑
tileset.style = new Cesium.Cesium3DTileStyle({
color: {
evaluateColor: feature => {
console.log("feature", feature.getPropertyIds());
const featueId = feature.getProperty("id");
if (pickId === featueId) {
return SmartEarth.Cesium.Color.YELLOW;
} else {
return SmartEarth.Cesium.Color.WHITE;
}
},
},
});
previousTileSet = tileset;
}

const url1 = "http://106.120.22.26:9096/gisserver/c3dserver/c3d120240813104356/tileset.json";
const tiltModel1 = await earthCtrl.factory.create3DTilesets({
url: url1,
});

const url2 = "http://106.120.22.26:9096/gisserver/c3dserver/c3d920240813104425/tileset.json";
const tiltModel2 = await earthCtrl.factory.create3DTilesets({
url: url2,
});
const url3 = "http://106.120.22.26:9096/gisserver/c3dserver/c3d520240813104456/tileset.json";
const tiltModel3 = await earthCtrl.factory.create3DTilesets({
url: url3,
});

const options = {
duration: 2,
offset: new SmartEarth.Cesium.HeadingPitchRange(1.0, -0.3, 1000),
};
earthCtrl.userScene.flyTo(tiltModel1.item, options);
$("#btn1").on("click", () => {
load();
});
</script>
</body>
</html>
作者

wangyawei

发布于

2025-03-16

更新于

2025-03-21

许可协议