hn-map 1.1.15 → 1.1.17

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/src/util.ts CHANGED
@@ -1,3 +1,8 @@
1
+ /**
2
+ * 地图工具函数库
3
+ * 包含坐标转换、地图计算、数据处理等工具函数
4
+ */
5
+
1
6
  // import Vue from 'vue'
2
7
  //
3
8
  // // 渲染vue组件并获取dom结构
@@ -13,177 +18,257 @@
13
18
  // return app.$el
14
19
  // }
15
20
 
16
- // 对象深度合并
21
+ /**
22
+ * 对象深度合并函数
23
+ * @param target 目标对象,合并结果会存储在此对象中
24
+ * @param source 源对象,提供要合并的属性
25
+ * @returns {any} 合并后的目标对象
26
+ * @example
27
+ * ```javascript
28
+ * const target = { a: 1, b: { c: 2 } };
29
+ * const source = { b: { d: 3 }, e: 4 };
30
+ * deepMerge(target, source); // { a: 1, b: { c: 2, d: 3 }, e: 4 }
31
+ * ```
32
+ */
17
33
  export function deepMerge(target: any, source: any): any {
18
- for (let key of Object.keys(source)) {
19
- if (source[key] instanceof Object && !Array.isArray(source[key])) {
20
- if (!target[key]) Object.assign(target, {[key]: {}});
21
- deepMerge(target[key], source[key]);
22
- } else {
23
- Object.assign(target, {[key]: source[key]});
24
- }
34
+ for (let key of Object.keys(source)) {
35
+ if (source[key] instanceof Object && !Array.isArray(source[key])) {
36
+ if (!target[key]) Object.assign(target, { [key]: {} });
37
+ deepMerge(target[key], source[key]);
38
+ } else {
39
+ Object.assign(target, { [key]: source[key] });
25
40
  }
26
- return target;
41
+ }
42
+ return target;
27
43
  }
28
44
 
29
- // 递归格式化高德坐标系
45
+ /**
46
+ * 递归格式化坐标数组,将WGS-84坐标转换为GCJ-02(高德坐标)
47
+ * 支持多种坐标格式:点坐标、线坐标、面坐标和对象数组
48
+ * @param position 坐标数组或对象数组
49
+ * @returns {any} 转换后的坐标数组或对象数组
50
+ * @example
51
+ * ```javascript
52
+ * // 点坐标
53
+ * wgs84ToGcj02Format([116.404, 39.915]);
54
+ *
55
+ * // 线坐标
56
+ * wgs84ToGcj02Format([[116.404, 39.915], [116.405, 39.916]]);
57
+ *
58
+ * // 对象数组
59
+ * wgs84ToGcj02Format([{lng: 116.404, lat: 39.915}, {lng: 116.405, lat: 39.916}]);
60
+ * ```
61
+ */
30
62
  export function wgs84ToGcj02Format(position: any): any {
31
- // 判断是否是数组
32
- if (position.every((item: any) => Array.isArray(item))) {
33
- return position.map((item: any) => {
34
- return wgs84ToGcj02Format(item);
35
- });
36
- } else if (position.every((item: any) => typeof item === "object")) {
37
- return position.map((item: any) => {
38
- let data = wgs84ToGcj02Format([item.lng, item.lat]);
39
- return {
40
- ...item,
41
- lng: data[0],
42
- lat: data[1],
43
- };
44
- });
45
- } else {
46
- return wgs84ToGcj02(position[0], position[1]);
47
- }
63
+ // 判断是否是二维数组(线或面坐标)
64
+ if (position.every((item: any) => Array.isArray(item))) {
65
+ return position.map((item: any) => {
66
+ return wgs84ToGcj02Format(item);
67
+ });
68
+ }
69
+ // 判断是否是对象数组
70
+ else if (position.every((item: any) => typeof item === "object")) {
71
+ return position.map((item: any) => {
72
+ let data = wgs84ToGcj02Format([item.lng, item.lat]);
73
+ return {
74
+ ...item,
75
+ lng: data[0],
76
+ lat: data[1],
77
+ };
78
+ });
79
+ }
80
+ // 单点坐标
81
+ else {
82
+ return wgs84ToGcj02(position[0], position[1]);
83
+ }
48
84
  }
49
85
 
50
86
  /**
51
87
  * WGS-84 转 GCJ-02(高德坐标)
52
- * @param {number} wgsLat - WGS-84纬度
53
88
  * @param {number} wgsLng - WGS-84经度
54
- * @returns {[number, number]} GCJ-02经纬度数组
89
+ * @param {number} wgsLat - WGS-84纬度
90
+ * @returns {[number, number]} GCJ-02经纬度数组 [经度, 纬度]
91
+ * @example
92
+ * ```javascript
93
+ * wgs84ToGcj02(116.404, 39.915); // 返回转换后的高德坐标
94
+ * ```
55
95
  */
56
96
  export function wgs84ToGcj02(wgsLng: any, wgsLat: any): any {
57
- if (!isCorrectPosition(wgsLng, wgsLat)) {
58
- return [wgsLng, wgsLat];
59
- }
97
+ if (!isCorrectPosition(wgsLng, wgsLat)) {
98
+ return [wgsLng, wgsLat];
99
+ }
60
100
 
61
- const a = 6378245.0; // 长半轴
62
- const ee = 0.00669342162296594323; // 扁率
63
-
64
- function transformLat(x: any, y: any): any {
65
- let ret =
66
- -100.0 +
67
- 2.0 * x +
68
- 3.0 * y +
69
- 0.2 * y * y +
70
- 0.1 * x * y +
71
- 0.2 * Math.sqrt(Math.abs(x));
72
- ret +=
73
- ((20.0 * Math.sin(6.0 * x * Math.PI) +
74
- 20.0 * Math.sin(2.0 * x * Math.PI)) *
75
- 2.0) /
76
- 3.0;
77
- ret +=
78
- ((160.0 * Math.sin((y * Math.PI) / 3.0) +
79
- 320 * Math.sin((y * Math.PI) / 30.0)) *
80
- 2.0) /
81
- 3.0;
82
- return ret;
83
- }
101
+ const a = 6378245.0; // 长半轴
102
+ const ee = 0.00669342162296594323; // 扁率
84
103
 
85
- function transformLng(x: any, y: any): any {
86
- let ret =
87
- 300.0 +
88
- x +
89
- 2.0 * y +
90
- 0.1 * x * x +
91
- 0.1 * x * y +
92
- 0.1 * Math.sqrt(Math.abs(x));
93
- ret +=
94
- ((20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(x * Math.PI)) *
95
- 2.0) /
96
- 3.0;
97
- ret +=
98
- ((150.0 * Math.sin((x * Math.PI) / 3.0) +
99
- 300.0 * Math.sin((x * Math.PI) / 15.0)) *
100
- 2.0) /
101
- 3.0;
102
- return ret;
103
- }
104
+ /**
105
+ * 纬度转换函数
106
+ * @param x 经度偏移
107
+ * @param y 纬度偏移
108
+ * @returns {number} 纬度转换值
109
+ */
110
+ function transformLat(x: any, y: any): any {
111
+ let ret =
112
+ -100.0 +
113
+ 2.0 * x +
114
+ 3.0 * y +
115
+ 0.2 * y * y +
116
+ 0.1 * x * y +
117
+ 0.2 * Math.sqrt(Math.abs(x));
118
+ ret +=
119
+ ((20.0 * Math.sin(6.0 * x * Math.PI) +
120
+ 20.0 * Math.sin(2.0 * x * Math.PI)) *
121
+ 2.0) /
122
+ 3.0;
123
+ ret +=
124
+ ((160.0 * Math.sin((y * Math.PI) / 3.0) +
125
+ 320 * Math.sin((y * Math.PI) / 30.0)) *
126
+ 2.0) /
127
+ 3.0;
128
+ return ret;
129
+ }
104
130
 
105
- let dLat = transformLat(wgsLng - 105.0, wgsLat - 35.0);
106
- let dLng = transformLng(wgsLng - 105.0, wgsLat - 35.0);
107
- const radLat = (wgsLat / 180.0) * Math.PI;
108
- let magic = Math.sin(radLat);
109
- magic = 1 - ee * magic * magic;
110
- const sqrtMagic = Math.sqrt(magic);
111
- dLat = (dLat * 180.0) / (((a * (1 - ee)) / (magic * sqrtMagic)) * Math.PI);
112
- dLng = (dLng * 180.0) / ((a / sqrtMagic) * Math.cos(radLat) * Math.PI);
113
- const gcjLat = wgsLat + dLat;
114
- const gcjLng = wgsLng + dLng;
115
-
116
- return [gcjLng, gcjLat];
131
+ /**
132
+ * 经度转换函数
133
+ * @param x 经度偏移
134
+ * @param y 纬度偏移
135
+ * @returns {number} 经度转换值
136
+ */
137
+ function transformLng(x: any, y: any): any {
138
+ let ret =
139
+ 300.0 +
140
+ x +
141
+ 2.0 * y +
142
+ 0.1 * x * x +
143
+ 0.1 * x * y +
144
+ 0.1 * Math.sqrt(Math.abs(x));
145
+ ret +=
146
+ ((20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(x * Math.PI)) *
147
+ 2.0) /
148
+ 3.0;
149
+ ret +=
150
+ ((150.0 * Math.sin((x * Math.PI) / 3.0) +
151
+ 300.0 * Math.sin((x * Math.PI) / 15.0)) *
152
+ 2.0) /
153
+ 3.0;
154
+ return ret;
155
+ }
156
+
157
+ let dLat = transformLat(wgsLng - 105.0, wgsLat - 35.0);
158
+ let dLng = transformLng(wgsLng - 105.0, wgsLat - 35.0);
159
+ const radLat = (wgsLat / 180.0) * Math.PI;
160
+ let magic = Math.sin(radLat);
161
+ magic = 1 - ee * magic * magic;
162
+ const sqrtMagic = Math.sqrt(magic);
163
+ dLat = (dLat * 180.0) / (((a * (1 - ee)) / (magic * sqrtMagic)) * Math.PI);
164
+ dLng = (dLng * 180.0) / ((a / sqrtMagic) * Math.cos(radLat) * Math.PI);
165
+ const gcjLat = wgsLat + dLat;
166
+ const gcjLng = wgsLng + dLng;
167
+
168
+ return [gcjLng, gcjLat];
117
169
  }
118
170
 
119
171
  /**
120
- * 判断是否为正确的坐标
121
- * @param {number} lat - 纬度
172
+ * 判断是否为正确的坐标范围
122
173
  * @param {number} lng - 经度
123
- * @returns {boolean}
174
+ * @param {number} lat - 纬度
175
+ * @returns {boolean} 坐标是否在有效范围内
176
+ * @example
177
+ * ```javascript
178
+ * isCorrectPosition(116.404, 39.915); // true
179
+ * isCorrectPosition(200, 100); // false
180
+ * ```
124
181
  */
125
182
  export function isCorrectPosition(lng: any, lat: any): boolean {
126
- return lng <= 180 && lng >= -180 && lat <= 90 && lat >= -90;
183
+ return lng <= 180 && lng >= -180 && lat <= 90 && lat >= -90;
127
184
  }
128
185
 
129
- // 根据矩形左上角和右上角坐标计算矩形坐标对
186
+ /**
187
+ * 根据矩形左上角和右下角坐标计算矩形的完整坐标数组
188
+ * @param leftTop 左上角坐标 [经度, 纬度]
189
+ * @param rightBottom 右下角坐标 [经度, 纬度]
190
+ * @returns {Array<Array<number>>} 矩形的完整坐标数组,包含5个点(首尾相同以闭合矩形)
191
+ * @example
192
+ * ```javascript
193
+ * createRectangleCoordinates([116.404, 39.915], [116.405, 39.914]);
194
+ * // 返回:[
195
+ * // [116.404, 39.915], // 左上角
196
+ * // [116.405, 39.915], // 右上角
197
+ * // [116.405, 39.914], // 右下角
198
+ * // [116.404, 39.914], // 左下角
199
+ * // [116.404, 39.915] // 回到左上角,闭合矩形
200
+ * // ]
201
+ * ```
202
+ */
130
203
  export function createRectangleCoordinates(leftTop: any, rightBottom: any) {
131
- const [leftTopLng, leftTopLat] = leftTop;
132
- const [rightBottomLng, rightBottomLat] = rightBottom;
133
-
134
- // 计算四个角点
135
- const leftTopPoint = [Number(leftTopLng), Number(leftTopLat)];
136
- const rightTopPoint = [Number(rightBottomLng), Number(leftTopLat)];
137
- const rightBottomPoint = [Number(rightBottomLng), Number(rightBottomLat)];
138
- const leftBottomPoint = [Number(leftTopLng), Number(rightBottomLat)];
139
-
140
- // 返回五个点的坐标对(首尾相同)
141
- return [
142
- leftTopPoint,
143
- rightTopPoint,
144
- rightBottomPoint,
145
- leftBottomPoint,
146
- leftTopPoint, // 闭合矩形
147
- ];
204
+ const [leftTopLng, leftTopLat] = leftTop;
205
+ const [rightBottomLng, rightBottomLat] = rightBottom;
206
+
207
+ // 计算四个角点
208
+ const leftTopPoint = [Number(leftTopLng), Number(leftTopLat)];
209
+ const rightTopPoint = [Number(rightBottomLng), Number(leftTopLat)];
210
+ const rightBottomPoint = [Number(rightBottomLng), Number(rightBottomLat)];
211
+ const leftBottomPoint = [Number(leftTopLng), Number(rightBottomLat)];
212
+
213
+ // 返回五个点的坐标对(首尾相同,形成闭合矩形)
214
+ return [
215
+ leftTopPoint,
216
+ rightTopPoint,
217
+ rightBottomPoint,
218
+ leftBottomPoint,
219
+ leftTopPoint, // 闭合矩形
220
+ ];
148
221
  }
149
222
 
223
+ /**
224
+ * 根据地图缩放级别获取对应的高度范围
225
+ * @param level 地图缩放级别(1-18)
226
+ * @returns {Object} 包含startHeight和endHeight的对象,表示该级别的高度范围
227
+ * @example
228
+ * ```javascript
229
+ * getMapRangeHeightByLevel(11); // 返回level=11对应的高度范围
230
+ * ```
231
+ */
150
232
  export function getMapRangeHeightByLevel(level: number) {
151
- // 输入校验:限制在 1~18
152
- level = Math.max(1, Math.min(18, Math.floor(level)));
233
+ // 输入校验:限制在 1~18
234
+ level = Math.max(1, Math.min(18, Math.floor(level)));
153
235
 
154
- // 计算指定 level 对应的高度(level >= 2 时使用指数衰减)
155
- function getHeight(lvl: number) {
156
- if (lvl === 1) {
157
- return Infinity; // level=1 表示“最大高度”,逻辑上为无穷大
158
- } else {
159
- return 32000000 / Math.pow(2, lvl - 2);
160
- }
236
+ // 计算指定 level 对应的高度(level >= 2 时使用指数衰减)
237
+ function getHeight(lvl: number) {
238
+ if (lvl === 1) {
239
+ return Infinity; // level=1 表示“最大高度”,逻辑上为无穷大
240
+ } else {
241
+ return 32000000 / Math.pow(2, lvl - 2);
161
242
  }
243
+ }
162
244
 
163
- const endHeight = getHeight(level); // 当前 level 的“粗略”高度
164
- const startHeight = level < 18 ? getHeight(level + 1) : 0; // 下一级更细
165
- return {startHeight, endHeight};
245
+ const endHeight = getHeight(level); // 当前 level 的“粗略”高度
246
+ const startHeight = level < 18 ? getHeight(level + 1) : 0; // 下一级更细
247
+ return { startHeight, endHeight };
166
248
  }
167
249
 
168
250
  /**
169
- * 根据 level 返回该层级的“中间高度”(几何平均值)
170
- *
251
+ * 根据缩放级别返回该层级的“中间高度”
171
252
  * @param {number} level - 缩放级别 [1, 18]
172
253
  * @returns {number} 中间高度(米)
254
+ * @example
255
+ * ```javascript
256
+ * getLevelMiddleHeight(11); // 返回level=11对应的中间高度
257
+ * ```
173
258
  */
174
259
  export function getLevelMiddleHeight(level: number): number {
175
- // const { startHeight, endHeight } = getMapRangeHeightByLevel(level);
176
- // // 如果 startHeight 为 0(如 level=18),几何平均会为 0,不合理
177
- // // 所以 level=18 特殊处理:返回 (0 + end)/2 或直接返回 end * 0.7 左右
178
- // if (startHeight === 0) {
179
- // return endHeight * 0.7; // 经验值,贴近“中间感知”
180
- // }
181
- //
182
- // // 几何平均:√(start × end)
183
- // return Math.sqrt(startHeight * endHeight);
184
- const EARTH_RADIUS = 6378137; // 赤道半径
185
- const height = (2 * Math.PI * EARTH_RADIUS) / (256 * Math.pow(2, level))
186
- return height;
260
+ // const { startHeight, endHeight } = getMapRangeHeightByLevel(level);
261
+ // // 如果 startHeight 为 0(如 level=18),几何平均会为 0,不合理
262
+ // // 所以 level=18 特殊处理:返回 (0 + end)/2 或直接返回 end * 0.7 左右
263
+ // if (startHeight === 0) {
264
+ // return endHeight * 0.7; // 经验值,贴近“中间感知”
265
+ // }
266
+ //
267
+ // // 几何平均:√(start × end)
268
+ // return Math.sqrt(startHeight * endHeight);
269
+ const EARTH_RADIUS = 6378137; // 赤道半径
270
+ const height = (2 * Math.PI * EARTH_RADIUS) / (256 * Math.pow(2, level));
271
+ return height;
187
272
  }
188
273
 
189
274
  /**
@@ -194,109 +279,224 @@ export function getLevelMiddleHeight(level: number): number {
194
279
  *
195
280
  * @param {number} level - 缩放级别 [1, 18]
196
281
  * @returns {[number, number]} [开始高度, 结束高度](单位:米)
282
+ * @example
283
+ * ```javascript
284
+ * getLevelHeightRange(11); // 返回level=11对应的高度范围
285
+ * ```
197
286
  */
198
287
  export function getLevelHeightRange(level: number): [number, number] {
199
- const {startHeight, endHeight} = getMapRangeHeightByLevel(level);
200
- return [startHeight, endHeight];
288
+ const { startHeight, endHeight } = getMapRangeHeightByLevel(level);
289
+ return [startHeight, endHeight];
201
290
  }
202
291
 
203
292
  /**
204
- * 根据高度反推 level(1~18)
293
+ * 根据高度反推地图缩放级别(1~18)
205
294
  * @param {number} height - 当前视野高度(米)
206
- * @returns {number} level
295
+ * @returns {number} 对应的地图缩放级别
296
+ * @example
297
+ * ```javascript
298
+ * getHeightToLevel(100000); // 根据高度返回对应的缩放级别
299
+ * ```
207
300
  */
208
301
  export function getHeightToLevel(height: number): number {
209
- if (height > 32000000) return 1;
210
- if (height <= 0) return 18;
302
+ if (height > 32000000) return 1;
303
+ if (height <= 0) return 18;
211
304
 
212
- // 解公式:height = 32e6 / 2^(level-2)
213
- let level = 2 + Math.log(32000000 / height) / Math.log(2);
214
- level = Math.floor(level);
305
+ // 解公式:height = 32e6 / 2^(level-2)
306
+ let level = 2 + Math.log(32000000 / height) / Math.log(2);
307
+ level = Math.floor(level);
215
308
 
216
- // 限制在 1~18
217
- return Math.max(1, Math.min(18, level));
309
+ // 限制在 1~18
310
+ return Math.max(1, Math.min(18, level));
218
311
  }
219
312
 
220
- // 將坐标数组中的元素字符串类型统一处理为number类型
313
+ /**
314
+ * 将坐标数组中的字符串类型元素统一转换为数字类型
315
+ * 支持一维、二维、三维坐标数组
316
+ * @param position 字符串坐标数组
317
+ * @returns {number[] | number[][] | number[][][]} 转换后的数字坐标数组
318
+ * @example
319
+ * ```javascript
320
+ * convertPosition(["116.404", "39.915"]); // [116.404, 39.915]
321
+ * convertPosition([["116.404", "39.915"], ["116.405", "39.916"]]); // 二维数组转换
322
+ * ```
323
+ */
221
324
  export function convertPosition(
222
- position: string[] | string[][] | string[][][]
325
+ position: string[] | string[][] | string[][][]
223
326
  ): number[] | number[][] | number[][][] {
224
- return position.map((item) => {
225
- if (Array.isArray(item)) {
226
- return item.map((innerItem) => {
227
- if (Array.isArray(innerItem)) {
228
- // 第三层,将每个字符串转数字
229
- return innerItem.map((str) => Number(str));
230
- } else {
231
- return Number(innerItem);
232
- }
233
- });
327
+ return position.map((item) => {
328
+ if (Array.isArray(item)) {
329
+ return item.map((innerItem) => {
330
+ if (Array.isArray(innerItem)) {
331
+ // 第三层,将每个字符串转数字
332
+ return innerItem.map((str) => Number(str));
234
333
  } else {
235
- return Number(item);
334
+ return Number(innerItem);
236
335
  }
237
- }) as number[] | number[][] | number[][][];
336
+ });
337
+ } else {
338
+ return Number(item);
339
+ }
340
+ }) as number[] | number[][] | number[][][];
238
341
  }
239
342
 
240
- // mars3d 地图重写瓦片加载失败的回调,使控制台过滤掉瓦片加载失败的报错
343
+ /**
344
+ * mars3d 地图重写瓦片加载失败的回调函数
345
+ * 用于过滤掉控制台中瓦片加载失败的报错,提高开发体验
346
+ * @example
347
+ * ```javascript
348
+ * // 在地图初始化后调用
349
+ * mars3dTileErrorHandler();
350
+ * ```
351
+ */
241
352
  export function mars3dTileErrorHandler() {
242
- // 确保 Cesium 已加载
243
- if (typeof Cesium !== 'undefined' && Cesium.TileProviderError) {
244
- const originalReportError = Cesium.TileProviderError.reportError;
245
-
246
- Cesium.TileProviderError.reportError = function (
247
- existingError: any,
248
- provider: any,
249
- errorEvent: any,
250
- message: any,
251
- x: any,
252
- y: any,
253
- level: any,
254
- error: any
255
- ) {
256
- // 可选:判断是否为“瓦片不存在”类错误(如 404、fetch 失败等)
257
- const shouldIgnore =
258
- message?.includes('Failed to obtain image') ||
259
- message?.includes('无法获得图块') ||
260
- message?.includes('404') ||
261
- (error && error.status === 404) ||
262
- message?.includes('NetworkError') ||
263
- message?.includes('not found');
264
-
265
- if (shouldIgnore) {
266
- // 静默处理:不打印日志,但仍可触发 errorEvent(供自定义监听)
267
- // 注意:仍需调用 raiseEvent(如果有人监听),但跳过 console.log
268
- if (Cesium.defined(errorEvent) && errorEvent.numberOfListeners > 0) {
269
- let tileError = existingError;
270
- if (Cesium.defined(existingError)) {
271
- tileError.provider = provider;
272
- tileError.message = message;
273
- tileError.x = x;
274
- tileError.y = y;
275
- tileError.level = level;
276
- tileError.retry = false;
277
- tileError.error = error;
278
- tileError.timesRetried = Cesium.defaultValue(tileError.timesRetried, 0) + 1;
279
- } else {
280
- tileError = new Cesium.TileProviderError(
281
- provider,
282
- message,
283
- x,
284
- y,
285
- level,
286
- 0,
287
- error
288
- );
289
- }
290
- errorEvent.raiseEvent(tileError);
291
- return tileError;
292
- } else {
293
- // 没人监听,且我们不想打印 → 直接返回一个空错误对象(或 null)
294
- return existingError || new Cesium.TileProviderError(provider, message, x, y, level, 0, error);
295
- }
296
- }
297
-
298
- // 非忽略错误:走原始逻辑
299
- return originalReportError.apply(this, arguments);
300
- };
301
- }
353
+ // 确保 Cesium 已加载
354
+ if (typeof Cesium !== "undefined" && Cesium.TileProviderError) {
355
+ const originalReportError = Cesium.TileProviderError.reportError;
356
+
357
+ Cesium.TileProviderError.reportError = function (
358
+ existingError: any,
359
+ provider: any,
360
+ errorEvent: any,
361
+ message: any,
362
+ x: any,
363
+ y: any,
364
+ level: any,
365
+ error: any
366
+ ) {
367
+ // 判断是否为“瓦片不存在”类错误(如 404、fetch 失败等)
368
+ const shouldIgnore =
369
+ message?.includes("Failed to obtain image") ||
370
+ message?.includes("无法获得图块") ||
371
+ message?.includes("404") ||
372
+ (error && error.status === 404) ||
373
+ message?.includes("NetworkError") ||
374
+ message?.includes("not found");
375
+
376
+ if (shouldIgnore) {
377
+ // 静默处理:不打印日志,但仍可触发 errorEvent(供自定义监听)
378
+ if (Cesium.defined(errorEvent) && errorEvent.numberOfListeners > 0) {
379
+ let tileError = existingError;
380
+ if (Cesium.defined(existingError)) {
381
+ tileError.provider = provider;
382
+ tileError.message = message;
383
+ tileError.x = x;
384
+ tileError.y = y;
385
+ tileError.level = level;
386
+ tileError.retry = false;
387
+ tileError.error = error;
388
+ tileError.timesRetried =
389
+ Cesium.defaultValue(tileError.timesRetried, 0) + 1;
390
+ } else {
391
+ tileError = new Cesium.TileProviderError(
392
+ provider,
393
+ message,
394
+ x,
395
+ y,
396
+ level,
397
+ 0,
398
+ error
399
+ );
400
+ }
401
+ errorEvent.raiseEvent(tileError);
402
+ return tileError;
403
+ } else {
404
+ // 没人监听,直接返回错误对象
405
+ return (
406
+ existingError ||
407
+ new Cesium.TileProviderError(
408
+ provider,
409
+ message,
410
+ x,
411
+ y,
412
+ level,
413
+ 0,
414
+ error
415
+ )
416
+ );
417
+ }
418
+ }
419
+
420
+ // 非忽略错误:走原始逻辑
421
+ return originalReportError.apply(this, arguments);
422
+ };
423
+ }
424
+ }
425
+
426
+ /**
427
+ * Cesium专用工具函数
428
+ */
429
+
430
+ /**
431
+ * 将WGS84坐标转换为Cesium笛卡尔坐标
432
+ */
433
+ export function wgs84ToCartesian(
434
+ lng: number,
435
+ lat: number,
436
+ alt: number = 0
437
+ ): any {
438
+ return Cesium.Cartesian3.fromDegrees(lng, lat, alt);
439
+ }
440
+
441
+ /**
442
+ * 将Cesium笛卡尔坐标转换为WGS84坐标
443
+ */
444
+ export function cartesianToWgs84(cartesian: any): {
445
+ lng: number;
446
+ lat: number;
447
+ alt: number;
448
+ } {
449
+ const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
450
+ return {
451
+ lng: Cesium.Math.toDegrees(cartographic.longitude),
452
+ lat: Cesium.Math.toDegrees(cartographic.latitude),
453
+ alt: cartographic.height,
454
+ };
455
+ }
456
+
457
+ /**
458
+ * 计算两点间的距离(米)
459
+ */
460
+ export function calculateDistance(pos1: any, pos2: any): number {
461
+ const cartesian1 = wgs84ToCartesian(pos1.lng, pos1.lat, pos1.alt || 0);
462
+ const cartesian2 = wgs84ToCartesian(pos2.lng, pos2.lat, pos2.alt || 0);
463
+ return Cesium.Cartesian3.distance(cartesian1, cartesian2);
464
+ }
465
+
466
+ /**
467
+ * 创建颜色材质
468
+ */
469
+ export function createColorMaterial(color: string, opacity: number = 1.0): any {
470
+ return new Cesium.ColorMaterialProperty(
471
+ Cesium.Color.fromCssColorString(color).withAlpha(opacity)
472
+ );
473
+ }
474
+
475
+ /**
476
+ * 创建图片材质
477
+ */
478
+ export function createImageMaterial(imageUrl: string): any {
479
+ return new Cesium.ImageMaterialProperty({
480
+ image: imageUrl,
481
+ transparent: true,
482
+ });
483
+ }
484
+
485
+ /**
486
+ * 计算包围球
487
+ */
488
+ export function calculateBoundingSphere(
489
+ positions: Array<[number, number, number?]>
490
+ ): any {
491
+ const cartesians = positions.map((pos) =>
492
+ wgs84ToCartesian(pos[0], pos[1], pos[2] || 0)
493
+ );
494
+ return Cesium.BoundingSphere.fromPoints(cartesians);
495
+ }
496
+
497
+ /**
498
+ * 创建实体集合
499
+ */
500
+ export function createEntityCollection(id: string): any {
501
+ return new Cesium.CustomDataSource(id);
302
502
  }