hj-gis-sdk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +824 -0
- package/README_UERPC.md +667 -0
- package/dist/sdk.esm.js +4143 -0
- package/dist/sdk.esm.js.map +1 -0
- package/dist/sdk.js +4148 -0
- package/dist/sdk.js.map +1 -0
- package/dist/sdk.umd.js +2 -0
- package/dist/sdk.umd.js.map +1 -0
- package/dist/types/hj-gis-sdk/addons/animation/animate-clip.d.ts +39 -0
- package/dist/types/hj-gis-sdk/addons/animation/animate-clip.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/addons/animation/animation-action.d.ts +31 -0
- package/dist/types/hj-gis-sdk/addons/animation/animation-action.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/addons/animation/animation-mixer.d.ts +18 -0
- package/dist/types/hj-gis-sdk/addons/animation/animation-mixer.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/addons/animation/index.d.ts +5 -0
- package/dist/types/hj-gis-sdk/addons/animation/index.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/addons/camera.d.ts +59 -0
- package/dist/types/hj-gis-sdk/addons/camera.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/addons/clock.d.ts +40 -0
- package/dist/types/hj-gis-sdk/addons/clock.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/addons/commander/context.d.ts +24 -0
- package/dist/types/hj-gis-sdk/addons/commander/context.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/addons/commander/dispatcher.d.ts +43 -0
- package/dist/types/hj-gis-sdk/addons/commander/dispatcher.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/addons/commander/excutor/base.d.ts +22 -0
- package/dist/types/hj-gis-sdk/addons/commander/excutor/base.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/addons/loader/index.d.ts +6 -0
- package/dist/types/hj-gis-sdk/addons/loader/index.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/addons/loader/loader.d.ts +4 -0
- package/dist/types/hj-gis-sdk/addons/loader/loader.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/addons/node/base.d.ts +63 -0
- package/dist/types/hj-gis-sdk/addons/node/base.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/addons/node/ue-node.d.ts +82 -0
- package/dist/types/hj-gis-sdk/addons/node/ue-node.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/addons/proxy.d.ts +83 -0
- package/dist/types/hj-gis-sdk/addons/proxy.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/addons/tools/base.d.ts +7 -0
- package/dist/types/hj-gis-sdk/addons/tools/base.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/addons/tools/building.d.ts +36 -0
- package/dist/types/hj-gis-sdk/addons/tools/building.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/addons/tools/daytime.d.ts +7 -0
- package/dist/types/hj-gis-sdk/addons/tools/daytime.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/addons/tools/fence.d.ts +61 -0
- package/dist/types/hj-gis-sdk/addons/tools/fence.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/addons/tools/ghost.d.ts +14 -0
- package/dist/types/hj-gis-sdk/addons/tools/ghost.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/addons/tools/heat-map.d.ts +65 -0
- package/dist/types/hj-gis-sdk/addons/tools/heat-map.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/addons/tools/index.d.ts +26 -0
- package/dist/types/hj-gis-sdk/addons/tools/index.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/addons/tools/match-view.d.ts +7 -0
- package/dist/types/hj-gis-sdk/addons/tools/match-view.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/addons/tools/measurement.d.ts +13 -0
- package/dist/types/hj-gis-sdk/addons/tools/measurement.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/addons/tools/pick-cast.d.ts +26 -0
- package/dist/types/hj-gis-sdk/addons/tools/pick-cast.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/addons/tools/scatter.d.ts +57 -0
- package/dist/types/hj-gis-sdk/addons/tools/scatter.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/addons/tools/weather.d.ts +31 -0
- package/dist/types/hj-gis-sdk/addons/tools/weather.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/addons/transform.d.ts +91 -0
- package/dist/types/hj-gis-sdk/addons/transform.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/addons/world.d.ts +76 -0
- package/dist/types/hj-gis-sdk/addons/world.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/core/ue-rpc.d.ts +54 -0
- package/dist/types/hj-gis-sdk/core/ue-rpc.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/index.d.ts +27 -0
- package/dist/types/hj-gis-sdk/index.d.ts.map +1 -0
- package/dist/types/hj-gis-sdk/utils.d.ts +16 -0
- package/dist/types/hj-gis-sdk/utils.d.ts.map +1 -0
- package/dist/types/src/animation.d.ts +2 -0
- package/dist/types/src/animation.d.ts.map +1 -0
- package/dist/types/src/building.d.ts +2 -0
- package/dist/types/src/building.d.ts.map +1 -0
- package/dist/types/src/common.d.ts +5 -0
- package/dist/types/src/common.d.ts.map +1 -0
- package/dist/types/src/fence.d.ts +2 -0
- package/dist/types/src/fence.d.ts.map +1 -0
- package/dist/types/src/ghost.d.ts +2 -0
- package/dist/types/src/ghost.d.ts.map +1 -0
- package/dist/types/src/heatmap.d.ts +2 -0
- package/dist/types/src/heatmap.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +2 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/keyframe.d.ts +2 -0
- package/dist/types/src/keyframe.d.ts.map +1 -0
- package/dist/types/src/measurement.d.ts +2 -0
- package/dist/types/src/measurement.d.ts.map +1 -0
- package/dist/types/src/scatter.d.ts +2 -0
- package/dist/types/src/scatter.d.ts.map +1 -0
- package/dist/types/src/weather.d.ts +2 -0
- package/dist/types/src/weather.d.ts.map +1 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,824 @@
|
|
|
1
|
+
# GIS SDK 文档
|
|
2
|
+
|
|
3
|
+
## 1. 简介
|
|
4
|
+
|
|
5
|
+
GIS SDK 是一个基于 Unreal Engine Pixel Streaming 技术的地理信息系统开发工具包,提供了与 Unreal Engine 进行 RPC 通信的能力,支持加载地图、生成静态网格体、操作相机、创建热力图、电子围栏等丰富功能。
|
|
6
|
+
|
|
7
|
+
### 1.1 主要特性
|
|
8
|
+
|
|
9
|
+
- **Unreal Engine 通信**:基于 Pixel Streaming DataChannel 的 RPC 通信机制
|
|
10
|
+
- **地图管理**:加载、卸载地图和关卡
|
|
11
|
+
- **对象管理**:生成、销毁静态网格体和蓝图对象
|
|
12
|
+
- **相机控制**:管理相机位置、旋转和变换
|
|
13
|
+
- **屏幕拾取**:支持屏幕坐标到 3D 世界坐标的转换
|
|
14
|
+
- **建筑分解**:支持建筑整体或分层展开、折叠动画
|
|
15
|
+
- **动画系统**:支持骨骼动画播放、关键帧动画控制
|
|
16
|
+
- **热力图**:基于灰度图或点集生成热力图
|
|
17
|
+
- **电子围栏**:创建和管理多边形电子围栏
|
|
18
|
+
- **天气系统**:控制场景天气效果
|
|
19
|
+
- **散点图**:创建和管理散点标记
|
|
20
|
+
- **测量工具**:支持距离、面积、高度测量
|
|
21
|
+
|
|
22
|
+
## 2. 安装
|
|
23
|
+
|
|
24
|
+
### 2.1 依赖项
|
|
25
|
+
|
|
26
|
+
- Node.js 18.14.0+ 或 20.0.0+ 或 22.0.0+ 或 24.0.0+
|
|
27
|
+
- @epicgames-ps/lib-pixelstreamingfrontend-ue5.5
|
|
28
|
+
|
|
29
|
+
### 2.2 安装 SDK
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install @epicgames-ps/lib-pixelstreamingfrontend-ue5.5 @epicgames-ps/lib-pixelstreamingcommon-ue5.5 hj-gis-sdk
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 2.3 引入 SDK
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
import { Core, Addons } from 'hj-gis-sdk'
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## 3. 核心概念
|
|
42
|
+
|
|
43
|
+
### 3.1 UERPCNode
|
|
44
|
+
|
|
45
|
+
UERPCNode 类代表场景中的一个对象节点,如地图、静态网格体或蓝图对象。它提供了对象的基本操作,如添加、移除和变换设置。每个 UERPCNode 实例都有一个唯一的句柄(Handle),用于与 Unreal Engine 进行通信。
|
|
46
|
+
|
|
47
|
+
### 3.2 World
|
|
48
|
+
|
|
49
|
+
World 类代表整个场景世界,是场景中所有对象的根容器。它管理场景中的地图、静态网格体、蓝图对象等,并提供了丰富的工具类访问接口,如建筑分解、热力图、电子围栏等。
|
|
50
|
+
|
|
51
|
+
### 3.3 Camera
|
|
52
|
+
|
|
53
|
+
Camera 类代表场景中的相机,用于控制视角和观察场景。它支持位置、旋转、缩放和相机参数(如 FOV、焦距)的设置和获取。SDK 提供了 PerspectiveCamera 等不同类型的相机实现。
|
|
54
|
+
|
|
55
|
+
### 3.4 Transform
|
|
56
|
+
|
|
57
|
+
Transform 类代表对象的变换信息,包括位置(Location)、旋转(Rotation)和缩放(Scale)。每个属性都包含 X、Y、Z 三个分量,用于精确控制对象的空间状态。
|
|
58
|
+
|
|
59
|
+
### 3.5 UERPC
|
|
60
|
+
|
|
61
|
+
UERPC 是 SDK 的核心通信类,负责与 Unreal Engine 进行 RPC 通信。它封装了 Pixel Streaming 的 DataChannel,提供了发送命令和接收响应的机制。所有与 Unreal Engine 的交互都通过 UERPC 类进行。
|
|
62
|
+
|
|
63
|
+
## 4. 快速开始
|
|
64
|
+
|
|
65
|
+
### 4.1 初始化应用
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
import {
|
|
69
|
+
PixelStreaming,
|
|
70
|
+
Config,
|
|
71
|
+
Flags
|
|
72
|
+
} from '@epicgames-ps/lib-pixelstreamingfrontend-ue5.5'
|
|
73
|
+
import { Addons } from 'hj-gis-sdk'
|
|
74
|
+
import { Logger, LogLevel } from '@epicgames-ps/lib-pixelstreamingcommon-ue5.5'
|
|
75
|
+
|
|
76
|
+
// 初始化日志
|
|
77
|
+
Logger.InitLogging(LogLevel.Info, false)
|
|
78
|
+
|
|
79
|
+
const { World, PerspectiveCamera } = Addons
|
|
80
|
+
|
|
81
|
+
async function initializeApp() {
|
|
82
|
+
// 创建 PixelStreaming 实例
|
|
83
|
+
const url = `ws://your-server-url:9001/player`
|
|
84
|
+
const config = new Config({
|
|
85
|
+
initialSettings: {
|
|
86
|
+
ss: url,
|
|
87
|
+
StreamerId: '',
|
|
88
|
+
AutoPlayVideo: true,
|
|
89
|
+
StartVideoMuted: true,
|
|
90
|
+
[Flags.HoveringMouseMode]: true, // 启用悬停鼠标模式
|
|
91
|
+
[Flags.TouchInput]: true, // 启用触摸输入
|
|
92
|
+
[Flags.FakeMouseWithTouches]: true // 使用触摸模拟鼠标
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
// 创建 PixelStreaming 实例并设置视频元素容器
|
|
97
|
+
const stream = new PixelStreaming(config, {
|
|
98
|
+
videoElementParent: document.querySelector('#ue')
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
// 连接到 Pixel Streaming 服务
|
|
102
|
+
stream.connect()
|
|
103
|
+
|
|
104
|
+
// 等待流播放事件
|
|
105
|
+
let { resolve, promise } = Promise.withResolvers()
|
|
106
|
+
stream.addEventListener('playStream', () => resolve(null))
|
|
107
|
+
await promise
|
|
108
|
+
|
|
109
|
+
// 创建 World 和 Camera 实例
|
|
110
|
+
const world = new World(stream)
|
|
111
|
+
|
|
112
|
+
// 匹配视图
|
|
113
|
+
world.tools.matchView.excute()
|
|
114
|
+
|
|
115
|
+
// 创建相机实例
|
|
116
|
+
const camera = new PerspectiveCamera(world.rpc)
|
|
117
|
+
|
|
118
|
+
// 设置相机初始位置
|
|
119
|
+
camera.transform.position.set(0, 0, 2)
|
|
120
|
+
|
|
121
|
+
return { world, camera }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// 初始化应用
|
|
125
|
+
initializeApp()
|
|
126
|
+
.then(({ world, camera }) => {
|
|
127
|
+
console.log('应用初始化完成')
|
|
128
|
+
// 在这里可以使用 world 和 camera 对象
|
|
129
|
+
})
|
|
130
|
+
.catch((err) => console.error('应用初始化失败:', err))
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### 4.2 基本使用流程
|
|
134
|
+
|
|
135
|
+
1. **创建 HTML 容器**
|
|
136
|
+
|
|
137
|
+
```html
|
|
138
|
+
<!DOCTYPE html>
|
|
139
|
+
<html lang="en">
|
|
140
|
+
<head>
|
|
141
|
+
<meta charset="UTF-8" />
|
|
142
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
143
|
+
<title>GIS SDK - 示例</title>
|
|
144
|
+
<style>
|
|
145
|
+
.ue-wrap {
|
|
146
|
+
position: fixed;
|
|
147
|
+
width: 100vw;
|
|
148
|
+
height: 100vh;
|
|
149
|
+
}
|
|
150
|
+
</style>
|
|
151
|
+
</head>
|
|
152
|
+
|
|
153
|
+
<body style="margin: 0">
|
|
154
|
+
<div class="ue-wrap">
|
|
155
|
+
<div id="ue"></div>
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
<!-- 引入SDK -->
|
|
159
|
+
<script type="module">
|
|
160
|
+
// 导入初始化代码
|
|
161
|
+
import './src/common.js'
|
|
162
|
+
</script>
|
|
163
|
+
</body>
|
|
164
|
+
</html>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
2. **使用模块化功能**
|
|
168
|
+
|
|
169
|
+
```javascript
|
|
170
|
+
// 导入需要的模块
|
|
171
|
+
import { Addons } from 'hj-gis-sdk'
|
|
172
|
+
import { initializeApp } from './common'
|
|
173
|
+
|
|
174
|
+
const { World, PerspectiveCamera } = Addons
|
|
175
|
+
|
|
176
|
+
// 初始化应用
|
|
177
|
+
const { world, camera } = await initializeApp(
|
|
178
|
+
'ws://your-server-url:9001/player',
|
|
179
|
+
document.querySelector('#ue')
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
// 加载地图
|
|
183
|
+
const map = await world.loadLevelByName('plgz')
|
|
184
|
+
|
|
185
|
+
// 创建热力图
|
|
186
|
+
const heatmap = await world.tools.heatMap.create({
|
|
187
|
+
url: 'http://example.com/heatmap.png',
|
|
188
|
+
X: 0,
|
|
189
|
+
Y: 0,
|
|
190
|
+
Z: 0,
|
|
191
|
+
ScaleX: 100,
|
|
192
|
+
ScaleY: 100,
|
|
193
|
+
ScaleZ: 100
|
|
194
|
+
})
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## 5. 详细使用示例
|
|
198
|
+
|
|
199
|
+
### 5.1 地图管理
|
|
200
|
+
|
|
201
|
+
地图管理功能用于加载、卸载和管理场景中的地图。
|
|
202
|
+
|
|
203
|
+
```javascript
|
|
204
|
+
// 加载地图
|
|
205
|
+
const map = await world.loadLevelByName('plgz')
|
|
206
|
+
|
|
207
|
+
// 设置地图位置
|
|
208
|
+
map.transform.position.set(100, 200, 300)
|
|
209
|
+
|
|
210
|
+
// 销毁地图
|
|
211
|
+
map.remove()
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### 5.2 静态网格体管理
|
|
215
|
+
|
|
216
|
+
静态网格体是场景中最基本的可见对象,可以是模型、建筑等。
|
|
217
|
+
|
|
218
|
+
```javascript
|
|
219
|
+
// 生成静态网格体
|
|
220
|
+
const staticMesh = await world.spawnStaticMeshActor({
|
|
221
|
+
assetPath: '/Game/Mesh/test/63',
|
|
222
|
+
transform: new Transform({
|
|
223
|
+
position: { x: 100, y: 100, z: 100 },
|
|
224
|
+
rotation: { pitch: 0, yaw: 0, roll: 0 },
|
|
225
|
+
scale: { x: 1, y: 1, z: 1 }
|
|
226
|
+
})
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
// 修改静态网格体位置
|
|
230
|
+
staticMesh.transform.position.x = 200
|
|
231
|
+
|
|
232
|
+
// 修改静态网格体旋转
|
|
233
|
+
staticMesh.transform.rotation.yaw = 45
|
|
234
|
+
|
|
235
|
+
// 修改静态网格体缩放
|
|
236
|
+
staticMesh.transform.scale.set(2, 2, 2)
|
|
237
|
+
|
|
238
|
+
// 销毁静态网格体
|
|
239
|
+
staticMesh.remove()
|
|
240
|
+
|
|
241
|
+
// 通过对象直接设置变换
|
|
242
|
+
staticMesh.transform.position.set(300, 300, 300)
|
|
243
|
+
staticMesh.transform.rotation.set(0, 90, 0)
|
|
244
|
+
staticMesh.transform.scale.set(1.5, 1.5, 1.5)
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### 5.3 蓝图对象管理
|
|
248
|
+
|
|
249
|
+
蓝图对象是基于 Unreal Engine 蓝图创建的复杂对象,可以包含多个组件和自定义逻辑。
|
|
250
|
+
|
|
251
|
+
```javascript
|
|
252
|
+
// 生成本地蓝图对象
|
|
253
|
+
const blueprint = await world.spawnLocalBlueprintActor({
|
|
254
|
+
assetPath: '/Game/levels/smith/SHQPXJ_Blueprint'
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
// 设置蓝图对象位置
|
|
258
|
+
blueprint.transform.position.set(300, 400, 500)
|
|
259
|
+
|
|
260
|
+
// 销毁蓝图对象
|
|
261
|
+
blueprint.remove()
|
|
262
|
+
|
|
263
|
+
// 生成带初始变换的蓝图对象
|
|
264
|
+
const blueprintWithTransform = await world.spawnLocalBlueprintActor({
|
|
265
|
+
assetPath: '/Game/Blueprints/BP_TestActor',
|
|
266
|
+
transform: {
|
|
267
|
+
Location: { X: 0, Y: 0, Z: 200 },
|
|
268
|
+
Rotation: { Pitch: 0, Yaw: 90, Roll: 0 },
|
|
269
|
+
Scale: { X: 1, Y: 1, Z: 1 }
|
|
270
|
+
}
|
|
271
|
+
})
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### 5.4 相机控制
|
|
275
|
+
|
|
276
|
+
相机控制用于调整场景的观察视角,可以设置位置、旋转和相机参数。
|
|
277
|
+
|
|
278
|
+
```javascript
|
|
279
|
+
// 设置相机位置
|
|
280
|
+
camera.transform.position.set(1000, 2000, 3000)
|
|
281
|
+
|
|
282
|
+
// 设置相机旋转
|
|
283
|
+
camera.transform.rotation.set(-12.26, 126.94, 0)
|
|
284
|
+
|
|
285
|
+
// 设置相机缩放
|
|
286
|
+
camera.transform.scale.set(1, 1, 1)
|
|
287
|
+
|
|
288
|
+
// 强制同步远程相机参数到本地
|
|
289
|
+
await camera.forceSyncRemoteToLocal()
|
|
290
|
+
|
|
291
|
+
// 保存相机复位位姿
|
|
292
|
+
await camera.setResetTransform({
|
|
293
|
+
Location: { X: 0, Y: 0, Z: 500 },
|
|
294
|
+
Rotation: { Pitch: -20, Yaw: 0, Roll: 0 }
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
// 复位相机到保存的位姿
|
|
298
|
+
await camera.resetToSavedTransform()
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### 5.5 屏幕拾取
|
|
302
|
+
|
|
303
|
+
屏幕拾取功能用于将屏幕坐标转换为 3D 世界坐标,并获取该位置的对象信息。
|
|
304
|
+
|
|
305
|
+
```javascript
|
|
306
|
+
// 设置屏幕拾取回调
|
|
307
|
+
const hoverHandle = world.tools.pickCast.hover((data) => {
|
|
308
|
+
console.log('拾取到的对象:', data)
|
|
309
|
+
console.log('世界坐标:', data.World)
|
|
310
|
+
console.log('地理坐标:', data.Geo)
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
// 取消屏幕拾取
|
|
314
|
+
hoverHandle()
|
|
315
|
+
|
|
316
|
+
// 世界坐标可以通过屏幕拾取回调获取
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### 5.6 视觉虚化
|
|
320
|
+
|
|
321
|
+
视觉虚化功能用于将场景中的对象设置为半透明状态,便于查看复杂场景。
|
|
322
|
+
|
|
323
|
+
```javascript
|
|
324
|
+
// 将对象设置为虚化状态
|
|
325
|
+
let ghostHandle = await world.tools.ghost.ghost(staticMesh)
|
|
326
|
+
|
|
327
|
+
// 恢复对象正常显示
|
|
328
|
+
if (ghostHandle) {
|
|
329
|
+
ghostHandle.restore()
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### 5.7 建筑分解
|
|
334
|
+
|
|
335
|
+
建筑分解功能用于将建筑模型按照楼层或部分进行展开和折叠,便于查看内部结构。
|
|
336
|
+
|
|
337
|
+
```javascript
|
|
338
|
+
// 加载建筑蓝图
|
|
339
|
+
const building = await world.spawnLocalBlueprintActor({
|
|
340
|
+
assetPath: '/Game/Mesh/Lou/Lou.Lou'
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
// 展开建筑
|
|
344
|
+
const expandHandler = await world.tools.building.expand(building.key!, {
|
|
345
|
+
spacing: 50, // 展开间距
|
|
346
|
+
duration: 2.0 // 动画时长
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
// 展开特定楼层
|
|
350
|
+
await expandHandler.expandFloor({
|
|
351
|
+
floorIndex: 2, // 楼层索引
|
|
352
|
+
direction: { X: -1, Y: 0, Z: 0 }, // 展开方向
|
|
353
|
+
distance: 100, // 展开距离
|
|
354
|
+
duration: 1.5 // 动画时长
|
|
355
|
+
})
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### 5.8 动画系统
|
|
359
|
+
|
|
360
|
+
动画系统支持骨骼动画播放和基于关键帧的动画控制。
|
|
361
|
+
|
|
362
|
+
```javascript
|
|
363
|
+
// 加载骨骼网格体
|
|
364
|
+
const skeletalMesh = await world.spawnSkeletalMesh({
|
|
365
|
+
assetPath: '/Game/Mesh/Human/Jog_In_Circle.Jog_In_Circle'
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
// 创建动画混合器
|
|
369
|
+
const animationMixer = new Animation.AnimationMixer(world.rpc)
|
|
370
|
+
|
|
371
|
+
// 创建动画片段
|
|
372
|
+
const animateClip = new Animation.PresetAnimateClip('/Game/Mesh/Human/Jog_In_Circle_Anim.Jog_In_Circle_Anim')
|
|
373
|
+
|
|
374
|
+
// 创建动画动作
|
|
375
|
+
const animateAction = animationMixer.clipAction(animateClip, skeletalMesh)
|
|
376
|
+
|
|
377
|
+
// 设置动画参数
|
|
378
|
+
animateAction.type = 'loop' // 循环播放
|
|
379
|
+
animateAction.rate = 1.0 // 播放速度
|
|
380
|
+
|
|
381
|
+
// 播放动画
|
|
382
|
+
animateAction.play()
|
|
383
|
+
|
|
384
|
+
// 暂停动画
|
|
385
|
+
animateAction.pause()
|
|
386
|
+
|
|
387
|
+
// 创建关键帧动画
|
|
388
|
+
const keyframeClip = new Animation.KeyFrameAnimateClip({
|
|
389
|
+
startTransform: Transform.default(),
|
|
390
|
+
endTransform: Transform.fromRPCObject({
|
|
391
|
+
Location: {
|
|
392
|
+
X: 100,
|
|
393
|
+
Y: 100,
|
|
394
|
+
Z: 0
|
|
395
|
+
},
|
|
396
|
+
Rotation: {
|
|
397
|
+
Pitch: 0,
|
|
398
|
+
Yaw: 180,
|
|
399
|
+
Roll: 0
|
|
400
|
+
}
|
|
401
|
+
})
|
|
402
|
+
})
|
|
403
|
+
|
|
404
|
+
// 创建关键帧动画动作
|
|
405
|
+
const keyframeAction = animationMixer.clipAction(keyframeClip, skeletalMesh)
|
|
406
|
+
keyframeAction.duration = 3.0 // 动画时长
|
|
407
|
+
|
|
408
|
+
// 播放关键帧动画
|
|
409
|
+
keyframeAction.play()
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### 5.9 热力图
|
|
413
|
+
|
|
414
|
+
热力图功能用于基于数据创建可视化的热力分布效果。
|
|
415
|
+
|
|
416
|
+
```javascript
|
|
417
|
+
// 创建Transform对象
|
|
418
|
+
const transform = Transform.default()
|
|
419
|
+
transform.scale.scale(100) // 设置缩放
|
|
420
|
+
|
|
421
|
+
// 从灰度图创建热力图
|
|
422
|
+
const heatmapFromImage = await world.tools.heatMap.create({
|
|
423
|
+
url: 'http://example.com/heatmap.png',
|
|
424
|
+
transform: transform
|
|
425
|
+
})
|
|
426
|
+
|
|
427
|
+
// 从点集创建热力图
|
|
428
|
+
const heatmapFromPoints = await world.tools.heatMap.create({
|
|
429
|
+
transform: transform,
|
|
430
|
+
points: [
|
|
431
|
+
{ x: 0, y: 0, count: 100 },
|
|
432
|
+
{ x: 10, y: 10, count: 200 },
|
|
433
|
+
{ x: 20, y: 20, count: 150 }
|
|
434
|
+
]
|
|
435
|
+
})
|
|
436
|
+
|
|
437
|
+
// 销毁热力图
|
|
438
|
+
heatmapFromImage.remove()
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### 5.10 电子围栏
|
|
442
|
+
|
|
443
|
+
电子围栏功能用于创建和管理多边形的虚拟围栏。
|
|
444
|
+
|
|
445
|
+
```javascript
|
|
446
|
+
// 创建电子围栏
|
|
447
|
+
const fence = await world.tools.fence.create({
|
|
448
|
+
vectorArray: [
|
|
449
|
+
{ X: 0, Y: 0, Z: 0 },
|
|
450
|
+
{ X: 1000, Y: 0, Z: 0 },
|
|
451
|
+
{ X: 1000, Y: 1000, Z: 0 },
|
|
452
|
+
{ X: 0, Y: 1000, Z: 0 }
|
|
453
|
+
],
|
|
454
|
+
height: 10, // 围栏高度
|
|
455
|
+
color: { R: 0, G: 0, B: 255, A: 255 } // 围栏颜色
|
|
456
|
+
})
|
|
457
|
+
|
|
458
|
+
// 删除围栏
|
|
459
|
+
await fence.remove()
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### 5.11 天气系统
|
|
463
|
+
|
|
464
|
+
天气系统用于控制场景中的天气效果,如晴天、雨天、雪天等。
|
|
465
|
+
|
|
466
|
+
```javascript
|
|
467
|
+
// 设置天气类型
|
|
468
|
+
await world.tools.weather.setWeather({
|
|
469
|
+
Preset: 'Rain' // 可选:'Sunny', 'Rain', 'Snow', 'Foggy'
|
|
470
|
+
})
|
|
471
|
+
|
|
472
|
+
// 设置时间
|
|
473
|
+
await world.tools.weather.setDayTime({
|
|
474
|
+
value: 1430 // 时间格式:HHMM,1430表示14:30
|
|
475
|
+
})
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### 5.12 散点图
|
|
479
|
+
|
|
480
|
+
散点图功能用于在场景中创建和管理标记点。
|
|
481
|
+
|
|
482
|
+
```javascript
|
|
483
|
+
// 创建散点
|
|
484
|
+
const scatter = await world.tools.scatter.create({
|
|
485
|
+
pointParams: [
|
|
486
|
+
{
|
|
487
|
+
location: { X: 0, Y: 0, Z: 10 },
|
|
488
|
+
textContent: '标记点1',
|
|
489
|
+
tag: 'Demo'
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
location: { X: 100, Y: 100, Z: 10 },
|
|
493
|
+
textContent: '标记点2',
|
|
494
|
+
tag: 'Demo'
|
|
495
|
+
}
|
|
496
|
+
]
|
|
497
|
+
})
|
|
498
|
+
|
|
499
|
+
// 删除所有散点
|
|
500
|
+
await scatter.removeAll()
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
### 5.13 测量工具
|
|
504
|
+
|
|
505
|
+
测量工具用于测量场景中的距离、面积和高度。
|
|
506
|
+
|
|
507
|
+
```javascript
|
|
508
|
+
// 开始路径测量
|
|
509
|
+
await world.tools.measurement.draw(MeasurementType.Path)
|
|
510
|
+
|
|
511
|
+
// 开始面积测量
|
|
512
|
+
await world.tools.measurement.draw(MeasurementType.Area)
|
|
513
|
+
|
|
514
|
+
// 开始高度测量
|
|
515
|
+
await world.tools.measurement.draw(MeasurementType.Height)
|
|
516
|
+
|
|
517
|
+
// 取消测量
|
|
518
|
+
await world.tools.measurement.clean()
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
## 6. 核心功能实现代码
|
|
522
|
+
|
|
523
|
+
以下是从 GUI 示例中提取的核心功能实现代码(不包含 GUI 部分):
|
|
524
|
+
|
|
525
|
+
### 6.1 初始化应用
|
|
526
|
+
|
|
527
|
+
```javascript
|
|
528
|
+
import { PixelStreaming, Config, Logger, LogLevel, Flags } from '@epicgames-ps/lib-pixelstreamingfrontend-ue5.5'
|
|
529
|
+
import { Addons } from 'hj-gis-sdk'
|
|
530
|
+
|
|
531
|
+
const { World, PerspectiveCamera, UERPCNode } = Addons
|
|
532
|
+
|
|
533
|
+
Logger.InitLogging(LogLevel.Info, false)
|
|
534
|
+
const AppElement = document.querySelector('#ue')! as HTMLElement
|
|
535
|
+
|
|
536
|
+
let world: InstanceType<typeof World> | undefined = undefined
|
|
537
|
+
let camera: InstanceType<typeof PerspectiveCamera> | undefined = undefined
|
|
538
|
+
let blueprint: InstanceType<typeof UERPCNode> | undefined = undefined
|
|
539
|
+
let staticmesh: InstanceType<typeof UERPCNode> | undefined = undefined
|
|
540
|
+
let map: InstanceType<typeof UERPCNode> | undefined = undefined
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* 初始化应用
|
|
544
|
+
*/
|
|
545
|
+
async function initializeApp() {
|
|
546
|
+
const url = `ws://120.27.198.158:9001/player`
|
|
547
|
+
const config = new Config({
|
|
548
|
+
initialSettings: {
|
|
549
|
+
ss: url,
|
|
550
|
+
StreamerId: '',
|
|
551
|
+
AutoPlayVideo: true,
|
|
552
|
+
StartVideoMuted: true,
|
|
553
|
+
[Flags.HoveringMouseMode]: true,
|
|
554
|
+
[Flags.TouchInput]: true,
|
|
555
|
+
[Flags.FakeMouseWithTouches]: true
|
|
556
|
+
}
|
|
557
|
+
})
|
|
558
|
+
const stream = new PixelStreaming(config, {
|
|
559
|
+
videoElementParent: AppElement
|
|
560
|
+
})
|
|
561
|
+
stream.connect()
|
|
562
|
+
let { resolve, promise } = Promise.withResolvers()
|
|
563
|
+
stream.addEventListener('playStream', () => {
|
|
564
|
+
console.log('PlayStreamEvent')
|
|
565
|
+
resolve(null)
|
|
566
|
+
})
|
|
567
|
+
await promise
|
|
568
|
+
world = new World(stream)
|
|
569
|
+
world.tools.matchView.excute()
|
|
570
|
+
camera = new PerspectiveCamera(world.rpc)
|
|
571
|
+
camera.transform.position.set(0, 0, 2)
|
|
572
|
+
}
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
### 6.2 地图加载与销毁
|
|
576
|
+
|
|
577
|
+
```javascript
|
|
578
|
+
// 加载地图
|
|
579
|
+
async function loadMap(selectedMap: string) {
|
|
580
|
+
map = await world!.loadLevelByName(selectedMap)
|
|
581
|
+
|
|
582
|
+
// 设置相机视角
|
|
583
|
+
camera!.transform.rotation.pitch = -12.259792797845645
|
|
584
|
+
camera!.transform.rotation.yaw = 126.9423080594274
|
|
585
|
+
camera!.transform.rotation.roll = 0.00003001662846458654
|
|
586
|
+
camera!.transform.position.set(
|
|
587
|
+
-234809.24784008358,
|
|
588
|
+
-74593.71088085348,
|
|
589
|
+
13755.085257765979
|
|
590
|
+
)
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// 销毁地图
|
|
594
|
+
function destroyMap() {
|
|
595
|
+
map!.remove()
|
|
596
|
+
}
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
### 6.3 蓝图对象加载与销毁
|
|
600
|
+
|
|
601
|
+
```javascript
|
|
602
|
+
// 加载蓝图对象
|
|
603
|
+
async function loadBlueprint(assetPath: string) {
|
|
604
|
+
blueprint = await world!.spawnLocalBlueprintActor({
|
|
605
|
+
assetPath: assetPath
|
|
606
|
+
})
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// 销毁蓝图对象
|
|
610
|
+
function destroyBlueprint() {
|
|
611
|
+
blueprint!.remove()
|
|
612
|
+
}
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
### 6.4 静态网格体加载与销毁
|
|
616
|
+
|
|
617
|
+
```javascript
|
|
618
|
+
// 加载静态网格体
|
|
619
|
+
async function loadStaticMesh(assetPath: string) {
|
|
620
|
+
staticmesh = await world!.spawnStaticMeshActor({
|
|
621
|
+
assetPath: assetPath
|
|
622
|
+
})
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// 销毁静态网格体
|
|
626
|
+
function destroyStaticMesh() {
|
|
627
|
+
staticmesh!.remove()
|
|
628
|
+
}
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
### 6.5 相机参数控制
|
|
632
|
+
|
|
633
|
+
```javascript
|
|
634
|
+
// 设置相机位置
|
|
635
|
+
function setCameraPosition(x: number, y: number, z: number) {
|
|
636
|
+
if (camera) {
|
|
637
|
+
camera.transform.position.x = x
|
|
638
|
+
camera.transform.position.y = y
|
|
639
|
+
camera.transform.position.z = z
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// 设置相机旋转
|
|
644
|
+
function setCameraRotation(pitch: number, yaw: number, roll: number) {
|
|
645
|
+
if (camera) {
|
|
646
|
+
camera.transform.rotation.pitch = pitch
|
|
647
|
+
camera.transform.rotation.yaw = yaw
|
|
648
|
+
camera.transform.rotation.roll = roll
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
### 6.6 静态网格体参数控制
|
|
654
|
+
|
|
655
|
+
```javascript
|
|
656
|
+
// 设置静态网格体位置
|
|
657
|
+
function setStaticMeshPosition(x: number, y: number, z: number) {
|
|
658
|
+
if (staticmesh) {
|
|
659
|
+
staticmesh.transform.position.x = x
|
|
660
|
+
staticmesh.transform.position.y = y
|
|
661
|
+
staticmesh.transform.position.z = z
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// 设置静态网格体旋转
|
|
666
|
+
function setStaticMeshRotation(pitch: number, yaw: number, roll: number) {
|
|
667
|
+
if (staticmesh) {
|
|
668
|
+
staticmesh.transform.rotation.pitch = pitch
|
|
669
|
+
staticmesh.transform.rotation.yaw = yaw
|
|
670
|
+
staticmesh.transform.rotation.roll = roll
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// 设置静态网格体缩放
|
|
675
|
+
function setStaticMeshScale(x: number, y: number, z: number) {
|
|
676
|
+
if (staticmesh) {
|
|
677
|
+
staticmesh.transform.scale.x = x
|
|
678
|
+
staticmesh.transform.scale.y = y
|
|
679
|
+
staticmesh.transform.scale.z = z
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
### 6.7 屏幕拾取功能
|
|
685
|
+
|
|
686
|
+
```javascript
|
|
687
|
+
let hoverHandle: any
|
|
688
|
+
|
|
689
|
+
// 开始屏幕拾取
|
|
690
|
+
function startHover() {
|
|
691
|
+
console.log(`pick`)
|
|
692
|
+
hoverHandle = world!.tools.pickCast.hover((data) => {
|
|
693
|
+
console.log(`hover data`, data)
|
|
694
|
+
})
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// 结束屏幕拾取
|
|
698
|
+
function stopHover() {
|
|
699
|
+
if (hoverHandle) {
|
|
700
|
+
hoverHandle()
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
### 6.8 应用初始化
|
|
706
|
+
|
|
707
|
+
```javascript
|
|
708
|
+
// 判断 window 是否初始化完成
|
|
709
|
+
if (document.readyState === 'loading') {
|
|
710
|
+
// 文档正在加载,等待DOMContentLoaded事件
|
|
711
|
+
document.addEventListener('DOMContentLoaded', initializeApp)
|
|
712
|
+
} else {
|
|
713
|
+
// 文档已经加载完成,直接初始化应用
|
|
714
|
+
initializeApp()
|
|
715
|
+
}
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
## 7. 常见问题
|
|
719
|
+
|
|
720
|
+
### 7.1 连接失败
|
|
721
|
+
|
|
722
|
+
- 确保 Pixel Streaming 服务器正在运行
|
|
723
|
+
- 检查 URL 是否正确,格式应为 `ws://your-server-url:9001/player`
|
|
724
|
+
- 确保服务器端口未被防火墙阻止
|
|
725
|
+
- 检查网络连接是否正常
|
|
726
|
+
|
|
727
|
+
### 7.2 对象不显示
|
|
728
|
+
|
|
729
|
+
- 确保对象的资源路径正确,格式应为 `/Game/Path/To/Asset`
|
|
730
|
+
- 检查对象的位置是否在相机的可见范围内
|
|
731
|
+
- 确保对象的缩放比例合适,避免过大或过小
|
|
732
|
+
- 检查对象的材质和渲染设置是否正确
|
|
733
|
+
|
|
734
|
+
### 7.3 性能问题
|
|
735
|
+
|
|
736
|
+
- 优化 RPC 调用频率,避免频繁的远程操作
|
|
737
|
+
- 减少同时显示的对象数量,特别是复杂模型
|
|
738
|
+
- 合理设置相机的视锥体和渲染距离
|
|
739
|
+
- 避免在短时间内创建和销毁大量对象
|
|
740
|
+
- 使用批量操作代替单个操作
|
|
741
|
+
|
|
742
|
+
### 7.4 动画不播放
|
|
743
|
+
|
|
744
|
+
- 确保骨骼动画资源路径正确
|
|
745
|
+
- 检查动画名称是否与 Unreal Engine 中的动画名称一致
|
|
746
|
+
- 确保动画组件已正确配置在蓝图中
|
|
747
|
+
- 检查是否有其他动画正在播放并冲突
|
|
748
|
+
|
|
749
|
+
### 7.5 屏幕拾取不工作
|
|
750
|
+
|
|
751
|
+
- 确保鼠标坐标转换正确,考虑画布的缩放和偏移
|
|
752
|
+
- 检查拾取的最大距离设置是否足够
|
|
753
|
+
- 确保场景中有可拾取的对象
|
|
754
|
+
- 检查 Unreal Engine 中的碰撞设置是否正确
|
|
755
|
+
|
|
756
|
+
### 7.6 建筑分解失败
|
|
757
|
+
|
|
758
|
+
- 确保建筑蓝图支持分解功能
|
|
759
|
+
- 检查 BuildingID 是否正确
|
|
760
|
+
- 确保建筑模型的楼层数据已正确配置
|
|
761
|
+
- 检查展开参数是否在合理范围内
|
|
762
|
+
|
|
763
|
+
### 7.7 热力图显示异常
|
|
764
|
+
|
|
765
|
+
- 确保灰度图 URL 可访问且格式正确
|
|
766
|
+
- 检查 MeshSize 和 MeshSegment 参数设置是否合理
|
|
767
|
+
- 调整 Opacity 和 EmissiveStrength 参数以获得更好的视觉效果
|
|
768
|
+
- 确保热力图的位置和缩放设置正确
|
|
769
|
+
|
|
770
|
+
## 8. 底层API参考
|
|
771
|
+
|
|
772
|
+
本GIS SDK是对底层UERPC功能的封装,旨在为开发者提供更便捷、更友好的调用接口。
|
|
773
|
+
|
|
774
|
+
### 8.1 UERPC文档链接
|
|
775
|
+
|
|
776
|
+
如果您需要了解更多底层功能或希望构建更灵活的架构,可以参考完整的UERPC文档:
|
|
777
|
+
|
|
778
|
+
[UERPC前端API使用指南](./README_UERPC.md)
|
|
779
|
+
|
|
780
|
+
### 8.2 关于封装说明
|
|
781
|
+
|
|
782
|
+
GIS SDK的所有接口调用均基于UERPC文档中提供的功能进行封装。这些封装旨在简化开发流程,提供更直观的API设计和更强大的类型支持。
|
|
783
|
+
|
|
784
|
+
如果您对当前架构不满意,或需要实现更复杂的功能,可以直接基于`README_UERPC.md`文档中提供的UERPC核心功能进行自定义封装,以满足您的特定需求。
|
|
785
|
+
|
|
786
|
+
### 8.3 封装与原生API的关系
|
|
787
|
+
|
|
788
|
+
- **便捷性**:GIS SDK将复杂的UERPC调用封装为简单的对象方法
|
|
789
|
+
- **类型安全**:提供完整的TypeScript类型定义,减少开发错误
|
|
790
|
+
- **架构优化**:采用面向对象的设计,使代码更易于维护和扩展
|
|
791
|
+
- **灵活性**:保留了UERPC的核心功能,同时提供了更高级的抽象
|
|
792
|
+
|
|
793
|
+
无论您选择使用GIS SDK的封装接口还是直接使用UERPC原生API,都可以实现与Unreal Engine的高效通信和交互。
|
|
794
|
+
|
|
795
|
+
## 9. 更新日志
|
|
796
|
+
|
|
797
|
+
### 1.0.0
|
|
798
|
+
|
|
799
|
+
- 初始版本发布
|
|
800
|
+
- 支持地图管理
|
|
801
|
+
- 支持静态网格体和蓝图对象管理
|
|
802
|
+
- 支持相机控制
|
|
803
|
+
- 支持屏幕拾取
|
|
804
|
+
|
|
805
|
+
### 1.1.0
|
|
806
|
+
|
|
807
|
+
- 新增建筑分解功能
|
|
808
|
+
- 新增动画系统(骨骼动画和关键帧动画)
|
|
809
|
+
- 新增热力图功能
|
|
810
|
+
- 新增电子围栏功能
|
|
811
|
+
|
|
812
|
+
### 1.2.0
|
|
813
|
+
|
|
814
|
+
- 新增天气系统
|
|
815
|
+
- 新增散点图功能
|
|
816
|
+
- 新增测量工具
|
|
817
|
+
- 新增视觉虚化功能
|
|
818
|
+
|
|
819
|
+
### 1.3.0
|
|
820
|
+
|
|
821
|
+
- 优化 RPC 通信性能
|
|
822
|
+
- 改进文档和示例
|
|
823
|
+
- 修复已知 bug
|
|
824
|
+
- 增强跨浏览器兼容性
|