huweili-cesium 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 ADDED
@@ -0,0 +1,88 @@
1
+ # huweili-cesium
2
+
3
+ 基于 Cesium 的地图工具库,从 `tzr` 项目 `src/components/cesiumMap/js` 提取,包含 **21 个** 核心 JS 模块(含 `toolbar` 子目录 4 个文件)。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ npm install huweili-cesium
9
+ ```
10
+
11
+ ### 对等依赖(需在项目中已安装)
12
+
13
+ - `cesium`
14
+ - `vue` ^3.3
15
+ - `pinia` ^2 或 ^3
16
+ - `mitt` ^3
17
+ - `qrcode` ^1.5
18
+
19
+ ## 使用前准备
20
+
21
+ 1. 在 HTML 中加载与宿主项目一致的 `constants.js`(定义 `window.DroneStatus`、`window.MapInstanceIds` 等),或自行赋值这些全局变量。
22
+ 2. 在 Vue 应用中 `app.use(pinia)`,地图组件初始化后调用各 `*Config()` 工厂函数。
23
+
24
+ ## 引入示例
25
+
26
+ ```js
27
+ // 统一入口
28
+ import { basicConfig, setPoint, movePointConfig } from 'huweili-cesium'
29
+
30
+ // 或按文件引入
31
+ import { basicConfig } from 'huweili-cesium/basis'
32
+ import { hemisphereConfig } from 'huweili-cesium/hemisphere'
33
+ import { createCustomToolbarButtons } from 'huweili-cesium/customToolbarButtons'
34
+ ```
35
+
36
+ ## 可选业务钩子
37
+
38
+ 若需光电引导、轨迹列表等业务能力,在应用启动时注入:
39
+
40
+ ```js
41
+ import { setDroneMapProvider, setOptGuideHandler } from 'huweili-cesium/config/hooks'
42
+
43
+ setDroneMapProvider(() => myDroneMap)
44
+ setOptGuideHandler((payload) => api.toOptGuide(payload))
45
+ ```
46
+
47
+ ## 发布到 npm(维护者)
48
+
49
+ ```bash
50
+ cd huweili-cesium
51
+ npm login
52
+ npm publish
53
+ ```
54
+
55
+ 首次发布前可在本地打包预览:
56
+
57
+ ```bash
58
+ npm pack
59
+ # 会生成 huweili-cesium-1.0.0.tgz,解压检查是否包含全部 js
60
+ ```
61
+
62
+ ## 从 tzr 源目录同步更新
63
+
64
+ ```bash
65
+ cd huweili-cesium
66
+ npm run sync
67
+ # 同步后需重新修复 @/ 别名为包内相对路径(见 scripts)
68
+ ```
69
+
70
+ ## 包含的文件清单
71
+
72
+ | 文件 | 说明 |
73
+ |------|------|
74
+ | basis.js | 地图基础操作 |
75
+ | setPoint.js / movePoint.js | 点位与移动 |
76
+ | setPath.js / movePath.js | 路径与轨迹 |
77
+ | groundLink.js | 地面连接线 |
78
+ | hemisphere.js | 半球区域 |
79
+ | geometry.js | 几何图形 |
80
+ | drawFence.js / drawFenceNew.js | 电子围栏 |
81
+ | cardPool.js / labelDiv.js | 信息牌 |
82
+ | clickHandler.js | 点击交互 |
83
+ | tileProviders.js | 底图瓦片 |
84
+ | customToolbarButtons.js | 工具栏 |
85
+ | toolbar/* | 指南针、底图切换、缩放、全屏 |
86
+ | droneRipple.js / qrCodeGenerator.js | 涟漪、二维码 |
87
+
88
+ 另含 `stores/mapStore.js`、`utils/*`、`config/*` 等运行依赖。
package/api/gaode.js ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * 逆地理编码(可选)
3
+ * 宿主项目可覆盖:import { setGeocodeProvider } from 'huweili-cesium/api/gaode'
4
+ */
5
+ let geocodeProvider = null
6
+
7
+ export function setGeocodeProvider(fn) {
8
+ geocodeProvider = fn
9
+ }
10
+
11
+ export function getAddressByLocation(lng, lat) {
12
+ if (geocodeProvider) {
13
+ return geocodeProvider(lng, lat)
14
+ }
15
+ return Promise.resolve('')
16
+ }
package/basis.js ADDED
@@ -0,0 +1,335 @@
1
+ /**
2
+ * 地图基本操作模块
3
+ *
4
+ * 提供在Cesium地图上基本操作的功能,如设置地图中心点、添加鼠标事件控制器等
5
+ * 支持在地图上添加鼠标事件控制器,如点击、拖动、缩放等
6
+ *
7
+ * @author huweili
8
+ * @email czxyhuweili@163.com
9
+ * @version 1.0.0
10
+ */
11
+ import * as Cesium from 'cesium'
12
+ import { useMapStore } from '../stores/mapStore.js'
13
+
14
+ export function basicConfig() {
15
+
16
+ // 获取地图store实例
17
+ const mapStore = useMapStore()
18
+
19
+ /**
20
+ * 检查坐标是否在中国境内
21
+ * @param {*} longitude - 经度
22
+ * @param {*} latitude - 纬度
23
+ * @returns - 是否在中国境内
24
+ */
25
+ const isInChina = (longitude, latitude) => {
26
+ // 中国大致经纬度范围
27
+ // 经度: 73°33′E 至 135°05′E
28
+ // 纬度: 3°51′N 至 53°33′N
29
+ return longitude >= 73.55 && longitude <= 135.08 && latitude >= 3.85 && latitude <= 53.55
30
+ }
31
+
32
+ /**
33
+ * 设置Cesium地图时间为当前北京时间
34
+ * @param mapId - 地图实例ID,可选,不传则使用当前实例
35
+ */
36
+ const setBeiJingTime = (mapId) => {
37
+ const map = mapStore.getMap(mapId)
38
+ if (!map) return
39
+ // 强制设置时间为当前北京时间
40
+ const now = new Date()
41
+ const chinaTime = new Date(now.getTime() + 8 * 60 * 60 * 1000)
42
+ map.clock.currentTime = Cesium.JulianDate.fromDate(chinaTime)
43
+ // 如果你不想动画效果,可以暂停时钟
44
+ map.clock.shouldAnimate = true
45
+ }
46
+
47
+ /**
48
+ * 鼠标事件控制器
49
+ *
50
+ * 处理地图上的鼠标事件,包括点击、拖动、缩放等、
51
+ * @param map - 地图实例
52
+ */
53
+ const mouseController = (map) => {
54
+ // 添加右键点击事件监听
55
+ map.screenSpaceEventHandler.setInputAction((click) => {
56
+ // 获取点击位置的笛卡尔坐标
57
+ const cartesian = map.camera.pickEllipsoid(click.position, map.scene.globe.ellipsoid);
58
+
59
+ if (cartesian) {
60
+ // 转换为经纬度
61
+ const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
62
+ const lng = Cesium.Math.toDegrees(cartographic.longitude);
63
+ const lat = Cesium.Math.toDegrees(cartographic.latitude);
64
+ const height = cartographic.height;
65
+
66
+ console.log('点击位置:', {
67
+ lng: Number(lng),
68
+ lat: Number(lat),
69
+ height: Number(height)
70
+ });
71
+
72
+ // 弹出alert显示坐标信息
73
+ // alert(`点击位置坐标:\n经度:${Number(lng).toFixed(6)}\n纬度:${Number(lat).toFixed(6)}\n高度:${Number(height).toFixed(2)}米`);
74
+ }
75
+ }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
76
+
77
+ // 添加左键点击事件,用于隐藏弹窗
78
+ map.screenSpaceEventHandler.setInputAction(() => {
79
+ console.log('左键点击事件触发')
80
+ }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
81
+ }
82
+
83
+ /**
84
+ * 设置地图中心点
85
+ *
86
+ * @param options - 包含地图中心点经纬度和高度的对象
87
+ * @param options.lng - 地图中心点经度
88
+ * @param options.lat - 地图中心点纬度
89
+ * @param options.alt - 可选,镜头高度/相机距离,默认 10000
90
+ * @param options.targetAlt - 可选,看向目标点的高度,例如飞机高度,默认 0
91
+ * @param options.map - 地图实例,可选,默认从store中获取
92
+ * @param options.mapId - 地图实例ID,可选,不传则使用当前实例
93
+ */
94
+ const setCesiumCenter = (options) => {
95
+ const { lng, lat, alt: altitude, targetAlt, map: mapInstance, mapId } = options
96
+
97
+ const map = mapInstance || mapStore.getMap(mapId)
98
+ if (!map) {
99
+ console.error('地图实例不存在')
100
+ return null
101
+ }
102
+
103
+ const centerInfo = mapStore.getMapInfo('center', mapId) || {}
104
+ // 优先级:传入的 altitude > 配置文件中的 centerInfo.alt > 默认值 10000
105
+ const finalAltitude = altitude !== undefined ? altitude : (centerInfo.alt || 10000)
106
+ // targetAlt 是看向目标点的高度,例如飞机高度;不要和视角高度 alt 混用
107
+ const finalTargetAlt = targetAlt !== undefined ? targetAlt : (centerInfo.targetAlt || 0)
108
+
109
+ // 检查当前是正交投影(2D)还是透视投影(3D)
110
+ const isOrthographic = map.camera.frustum instanceof Cesium.OrthographicFrustum
111
+
112
+ if (isOrthographic) {
113
+ // 2D 俯视视角
114
+ set2DView(map, lng, lat, finalAltitude)
115
+ } else {
116
+ // 3D 斜视视角
117
+ set3DView(map, lng, lat, centerInfo, finalAltitude, finalTargetAlt)
118
+ }
119
+ }
120
+
121
+
122
+
123
+ /******************************************************2、3维视角切换**************************************************************************** */
124
+
125
+ /**
126
+ * 获取当前RTK经纬度
127
+ * @param {string=} mapId 地图实例ID
128
+ * @returns {{lng: number, lat: number}|null}
129
+ */
130
+ const getCurrentRtkPosition = (mapId) => {
131
+ const rtkData = mapStore.getRtkData(mapId)
132
+ const lng = Number(rtkData?.longitude)
133
+ const lat = Number(rtkData?.latitude)
134
+
135
+ if (!Number.isFinite(lng) || !Number.isFinite(lat)) {
136
+ return null
137
+ }
138
+
139
+ return { lng, lat }
140
+ }
141
+
142
+ /**
143
+ * 恢复 3D 透视投影
144
+ * @param {object} map Cesium 地图实例
145
+ */
146
+ const restorePerspectiveFrustum = (map) => {
147
+ if (!map) return
148
+
149
+ const currentFrustum = map.camera.frustum
150
+ const perspectiveFrustum = new Cesium.PerspectiveFrustum()
151
+ perspectiveFrustum.fov = Cesium.Math.toRadians(60)
152
+ perspectiveFrustum.aspectRatio = map.canvas.clientWidth / map.canvas.clientHeight
153
+ perspectiveFrustum.near = currentFrustum.near || 1.0
154
+ perspectiveFrustum.far = currentFrustum.far || 10000000.0
155
+ map.camera.frustum = perspectiveFrustum
156
+ }
157
+
158
+ /**
159
+ * 设置 2D 俯视用的正交投影
160
+ * @param {object} map Cesium 地图实例
161
+ */
162
+ const applyOrthographicFrustum = (map) => {
163
+ if (!map) return
164
+
165
+ const canvas = map.canvas
166
+ const frustum = new Cesium.OrthographicFrustum()
167
+ const width = canvas.clientWidth
168
+ const height = canvas.clientHeight
169
+
170
+ // 检查当前是否是透视投影,只有透视投影才有 fovy
171
+ let widthValue
172
+ if (map.scene.camera.frustum instanceof Cesium.PerspectiveFrustum) {
173
+ widthValue = width * map.scene.camera.frustum.fovy * map.scene.camera.positionCartographic.height / 1000
174
+ } else {
175
+ // 如果已经是正交投影,保留当前宽度或使用默认值
176
+ widthValue = map.scene.camera.frustum.width || 20000
177
+ }
178
+
179
+ frustum.width = widthValue
180
+ frustum.aspectRatio = width / height
181
+ frustum.near = 0.1
182
+ frustum.far = 10000000.0
183
+ map.scene.camera.frustum = frustum
184
+ }
185
+
186
+ /**
187
+ * 设置并锁定2D俯视视角(禁止通过中键拖动进入3D姿态)
188
+ * @param {object} map Cesium 地图实例
189
+ * @param {number} lng 经度
190
+ * @param {number} lat 纬度
191
+ * @param {number=} height 相机高度
192
+ */
193
+ const set2DView = (map, lng, lat, height = 10000) => {
194
+ if (!map) return
195
+ applyOrthographicFrustum(map)
196
+ const cameraController = map.scene?.screenSpaceCameraController
197
+ if (cameraController) {
198
+ cameraController.enableTilt = false
199
+ cameraController.enableLook = false
200
+ }
201
+ map.camera.setView({
202
+ destination: Cesium.Cartesian3.fromDegrees(lng, lat, height),
203
+ orientation: {
204
+ heading: Cesium.Math.toRadians(0),
205
+ pitch: Cesium.Math.toRadians(-90),
206
+ roll: Cesium.Math.toRadians(0)
207
+ }
208
+ })
209
+ }
210
+
211
+ /**
212
+ * 设置3D斜视视角(恢复透视投影,让相机看向指定目标点)
213
+ *
214
+ * 简单理解:就像你站在高楼上看地面上的目标
215
+ * - 你站的位置高度 = altitude
216
+ * - 你看向的目标点 = (lng, lat, targetAlt)
217
+ * - 你的视线角度 = heading(朝向) + pitch(俯仰)
218
+ *
219
+ * @param {object} map Cesium 地图实例(必须传)
220
+ * @param {number} lng 目标点的经度(比如无人机所在位置)
221
+ * @param {number} lat 目标点的纬度
222
+ * @param {Object=} centerInfo 视角配置(可选)
223
+ * @param {number} [centerInfo.heading=0] 水平朝向角度(0=正北,90=正东)
224
+ * @param {number} [centerInfo.pitch=-35] 俯仰角度(负数=向下看,-90=正俯视)
225
+ * @param {number=} altitude 相机高度(单位:米,默认10000米)
226
+ * @param {number=} targetAlt 目标点的高度(比如无人机的飞行高度,默认0)
227
+ */
228
+ const set3DView = (map, lng, lat, centerInfo = {}, altitude = 10000, targetAlt = 0) => {
229
+ // 1. 检查地图实例是否存在,不存在就直接返回
230
+ if (!map) return
231
+
232
+ // 2. 恢复3D透视投影(让地图可以有立体感)
233
+ restorePerspectiveFrustum(map)
234
+
235
+ // 3. 获取相机控制器,允许用户之后可以倾斜和旋转视角
236
+ const cameraController = map.scene?.screenSpaceCameraController
237
+ if (cameraController) {
238
+ cameraController.enableTilt = true // 允许倾斜(上下看)
239
+ cameraController.enableLook = true // 允许旋转(左右看)
240
+ }
241
+
242
+ // 4. 处理俯仰角度(视线上下倾斜的角度)
243
+ // pitch范围:0度=水平向前看,-90度=正俯视,-35度=稍微向下看
244
+ // 这里防止配置的角度太极端(<= -80度几乎就是俯视了,强制改成-35度)
245
+ const configuredPitch = centerInfo.pitch ?? -35 // 用配置的值,没有就用默认-35度
246
+ const targetPitch = configuredPitch <= -80 ? -35 : configuredPitch // 防止角度太极端
247
+ const pitchRadians = Cesium.Math.toRadians(targetPitch) // 角度转弧度(Cesium用弧度计算)
248
+
249
+ // 5. 处理目标点高度(比如无人机的飞行高度)
250
+ const targetAltNumber = Number(targetAlt) // 转成数字
251
+ const finalTargetAlt = Number.isFinite(targetAltNumber) ? targetAltNumber : 0 // 确保是有效数字
252
+ // 把经纬度+高度转成Cesium能识别的三维坐标(笛卡尔坐标)
253
+ const targetPosition = Cesium.Cartesian3.fromDegrees(lng, lat, finalTargetAlt)
254
+
255
+ // 6. 处理相机高度(我们想让相机飞多高)
256
+ const altitudeNumber = Number(altitude)
257
+ const finalAltitude = Number.isFinite(altitudeNumber) ? altitudeNumber : 10000
258
+
259
+ // 7. 计算相机到目标点的直线距离(重点!)
260
+ // HeadingPitchRange的range是"直线距离",不是高度
261
+ // 想象一下:你站在100米高的地方,视线向下30度看地面
262
+ // 你到目标点的直线距离 = 高度 / sin(俯仰角)
263
+ // 用Math.max(..., 0.1)防止sin值太小导致距离过大
264
+ const range = finalAltitude / Math.max(Math.sin(Math.abs(pitchRadians)), 0.1)
265
+
266
+ // 8. 设置相机看向目标点
267
+ // HeadingPitchRange:一种描述相机位置的方式
268
+ // - heading: 水平朝向(比如0=正北看)
269
+ // - pitch: 俯仰角度(向下为负)
270
+ // - range: 相机到目标点的直线距离
271
+ map.camera.lookAt(
272
+ targetPosition, // 看向的目标点
273
+ new Cesium.HeadingPitchRange(
274
+ Cesium.Math.toRadians(centerInfo.heading ?? 0), // 朝向转弧度
275
+ pitchRadians, // 俯仰角(弧度)
276
+ range // 直线距离
277
+ )
278
+ )
279
+
280
+ // 9. 重置相机变换(确保lookAt生效后恢复正常状态)
281
+ map.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
282
+ }
283
+
284
+ /**
285
+ * 在两种俯仰视角之间切换
286
+ * 注意:这里只切换相机视角,不切换 Cesium 的 2D/3D 模式
287
+ * @param {string=} mapId 地图实例ID
288
+ */
289
+ const toggleViewToRtk = (mapId) => {
290
+ const map = mapStore.getMap(mapId)
291
+ const rtkPosition = getCurrentRtkPosition(mapId)
292
+
293
+ if (!map || !rtkPosition) {
294
+ return { success: false, currentMode: null }
295
+ }
296
+
297
+ const centerInfo = mapStore.getMapInfo('center', mapId) || {}
298
+ let { lng, lat } = rtkPosition
299
+
300
+ if (lng === 0 && lat === 0) {
301
+ lng = centerInfo.lng ?? lng
302
+ lat = centerInfo.lat ?? lat
303
+ }
304
+
305
+ const camera = map.camera
306
+ const isOrthographic = camera.frustum instanceof Cesium.OrthographicFrustum
307
+ const nextAlt = centerInfo.alt ?? 10000
308
+
309
+ let currentMode
310
+ // 切换视角
311
+ if (isOrthographic) {
312
+ // 当前是正交投影(俯视),切换到透视投影(斜视)
313
+ set3DView(map, lng, lat, centerInfo, 10000)
314
+ currentMode = '3D'
315
+ } else {
316
+ // 当前是透视投影,切换到正交投影(俯视)
317
+ set2DView(map, lng, lat, nextAlt)
318
+ currentMode = '2D'
319
+ }
320
+
321
+ return { success: true, currentMode }
322
+ }
323
+
324
+ return {
325
+ isInChina,
326
+ mouseController,
327
+ setCesiumCenter,
328
+ setBeiJingTime,
329
+ toggleViewToRtk,
330
+ set2DView,
331
+ set3DView,
332
+ applyOrthographicFrustum,
333
+ restorePerspectiveFrustum
334
+ }
335
+ }