hn-map 1.1.18 → 1.1.19

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.
@@ -4,396 +4,405 @@
4
4
  */
5
5
  import CesiumPopup from "./cesium_popup"; // 导入弹窗类
6
6
  export default class CesiumEntity {
7
- /** 配置选项 */
8
- protected option: any = null;
9
- /** 事件监听器集合 */
10
- private event: Record<string, Function> = {};
11
- /** 图形实例 */
12
- protected graphic: any = null;
13
- /** 地图实例 */
14
- protected hnMap: any = null;
15
- /** 屏幕空间事件处理器 */
16
- private screenSpaceEventHandler: any = null;
17
- /** 弹窗实例 */
18
- private popup: any = null;
19
- /** 弹窗内容缓存 */
20
- private popupContent: string | null = null;
21
- /** 是否显示弹窗 */
22
- private showPopupOnClick: boolean = false;
23
- /** 弹窗样式配置 */
24
- private popupStyle: any = null;
25
- /**
26
- * 构造函数
27
- * @param hnMap 地图实例
28
- */
29
- constructor(hnMap: any) {
30
- this.hnMap = hnMap;
31
- this.event = {};
32
- // 创建弹窗实例
33
- if (hnMap && hnMap.map && hnMap.map.map) {
7
+ /** 配置选项 */
8
+ protected option: any = null;
9
+ /** 事件监听器集合 */
10
+ event: Record<string, Function> = {};
11
+ /** 图形实例 */
12
+ graphic: any = null;
13
+ /** 地图实例 */
14
+ protected hnMap: any = null;
15
+ /** 弹窗实例 */
16
+ private popup: any = null;
17
+ /** 弹窗内容缓存 */
18
+ private popupContent: string | null = null;
19
+ /** 是否显示弹窗 */
20
+ enablePopup: boolean = false;
21
+ /** 弹窗样式配置 */
22
+ private popupStyle: any = null;
23
+
24
+ private _popupClickHandler: ((movement: any) => void) | null = null;
25
+ private _userClickHandler: ((movement: any) => void) | null = null;
26
+
27
+ /**
28
+ * 构造函数
29
+ * @param hnMap 地图实例
30
+ */
31
+ constructor(hnMap: any) {
32
+ this.hnMap = hnMap;
33
+ // 创建弹窗实例
34
+ if (hnMap && hnMap.map && hnMap.map.map) {
34
35
 
36
+ }
35
37
  }
36
- }
37
-
38
- /**
39
- * 设置弹窗样式
40
- * @param style 弹窗样式配置
41
- */
42
- setPopupStyle(style: any): void {
43
- this.popupStyle = style;
44
- // 如果弹窗已存在,重新创建以应用新样式
45
- if (this.popup && this.hnMap && this.hnMap.map && this.hnMap.map.map) {
46
- this.popup.destroy();
47
- const PopupClass = CesiumPopup(this.hnMap);
48
- this.popup = new PopupClass(this.hnMap.map.map, {
49
- style: this.popupStyle,
50
- });
38
+
39
+ /**
40
+ * 设置弹窗样式
41
+ * @param style 弹窗样式配置
42
+ */
43
+ setPopupStyle(style: any): void {
44
+ this.popupStyle = style;
45
+ // 如果弹窗已存在,重新创建以应用新样式
46
+ if (this.popup && this.hnMap && this.hnMap.map && this.hnMap.map.map) {
47
+ this.popup.destroy();
48
+ const PopupClass = CesiumPopup(this.hnMap);
49
+ this.popup = new PopupClass(this.hnMap.map.map, {
50
+ style: this.popupStyle,
51
+ });
52
+ }
51
53
  }
52
- }
53
-
54
- /**
55
- * 添加属性弹窗
56
- * 点击要素时显示包含要素属性信息的弹窗
57
- */
58
- addPopupByAttr(): void {
59
- this.showPopupOnClick = true;
60
-
61
- if (this.option && this.option.data) {
62
- // 生成属性弹窗内容
63
- let content =
64
- '<div style="background:#ffffff;color:#666;padding:12px;max-height: 300px; overflow-y: auto;">';
65
-
66
- for (const key in this.option.data) {
67
- if (this.option.data.hasOwnProperty(key)) {
68
- content += `
69
- <div>${key + ":" + this.option.data[key]}</div>
70
-
71
- `;
54
+
55
+ // 新增方法:清理现有弹窗相关状态
56
+ private clearPopupState(): void {
57
+ // 销毁弹窗
58
+ if (this.popup) {
59
+ this.popup.destroy();
60
+ this.popup = null;
72
61
  }
73
- }
74
62
 
75
- content += "</div>";
76
- this.popupContent = content;
63
+ // 清理弹窗回调
64
+ if (this._popupClickHandler && this.graphic && this.hnMap?.map?.map) {
65
+ const handlers = this.hnMap.map.entityClickHandlers.get(this.graphic);
66
+ if (handlers) {
67
+ const index = handlers.indexOf(this._popupClickHandler);
68
+ if (index !== -1) {
69
+ handlers.splice(index, 1);
70
+ if (handlers.length === 0) {
71
+ this.hnMap.map.entityClickHandlers.delete(this.graphic);
72
+ }
73
+ }
74
+ }
75
+ this._popupClickHandler = null;
76
+ }
77
77
 
78
- // 设置点击事件
79
- this.bindClickEvent();
78
+ this.popupContent = null;
79
+ this.enablePopup = false;
80
80
  }
81
- }
82
-
83
- /**
84
- * 添加自定义DOM弹窗
85
- * 点击要素时显示自定义DOM结构的弹窗
86
- * @param getCustomDom 自定义DOM生成函数,接收要素数据,返回DOM字符串
87
-
88
- */
89
- addCustomPopup(getCustomDom: (data: any) => Promise<string> | string): void {
90
- this.showPopupOnClick = true;
91
-
92
- const data = this.option && this.option.data ? this.option.data : {};
93
-
94
- try {
95
- const customDom = getCustomDom(data);
96
- if (customDom instanceof Promise) {
97
- customDom
98
- .then((dom: string) => {
99
- this.popupContent = dom;
100
- this.bindClickEvent();
101
- })
102
- .catch((error: any) => {
103
- console.error("生成自定义弹窗DOM失败:", error);
104
- });
105
- } else {
106
- this.popupContent = customDom;
107
- this.bindClickEvent();
108
- }
109
- } catch (error) {
110
- console.error("生成自定义弹窗DOM失败:", error);
81
+
82
+
83
+ /**
84
+ * 添加属性弹窗
85
+ * 点击要素时显示包含要素属性信息的弹窗
86
+ */
87
+ addPopupByAttr(): void {
88
+ this.clearPopupState();
89
+
90
+ if (!this.option?.data || !this.graphic || !this.hnMap?.map?.map) return;
91
+ // 构建内容
92
+ let content = '<div style="background:#ffffff;color:#666;padding:12px;max-height:300px;overflow-y:auto;">';
93
+ for (const key in this.option.data) {
94
+ if (this.option.data.hasOwnProperty(key)) {
95
+ content += `<div>${key}: ${this.option.data[key]}</div>`;
96
+ }
97
+ }
98
+ content += '</div>';
99
+ this.popupContent = content;
100
+
101
+ // 注册弹窗专用回调
102
+ const popupHandler = (movement: any) => {
103
+ this.showPopupAtPosition(movement.position);
104
+ };
105
+
106
+ // 存储以便清理
107
+ this._popupClickHandler = popupHandler;
108
+
109
+ // 👇 关键:添加到回调数组
110
+ if (!this.hnMap.map.entityClickHandlers.has(this.graphic)) {
111
+ this.hnMap.map.entityClickHandlers.set(this.graphic, []);
112
+ }
113
+ this.hnMap.map.entityClickHandlers.get(this.graphic)!.push(popupHandler);
111
114
  }
112
- }
113
-
114
- /**
115
- * 绑定点击事件
116
- */
117
- private bindClickEvent(): void {
118
- // 如果已经绑定了,直接返回
119
- if (this.screenSpaceEventHandler) {
120
- return;
115
+
116
+ /**
117
+ * 添加自定义DOM弹窗
118
+ * 点击要素时显示自定义DOM结构的弹窗
119
+ * @param getCustomDom 自定义DOM生成函数,接收要素数据,返回DOM字符串
120
+
121
+ */
122
+ addCustomPopup(getCustomDom: (data: any) => Promise<string> | string): void {
123
+ this.clearPopupState();
124
+ this.enablePopup = true;
125
+
126
+ const data = this.option && this.option.data ? this.option.data : {};
127
+
128
+ try {
129
+ const customDom = getCustomDom(data);
130
+ if (customDom instanceof Promise) {
131
+ customDom
132
+ .then((dom: string) => {
133
+ this.popupContent = dom;
134
+ })
135
+ .catch((error: any) => {
136
+ console.error("生成自定义弹窗DOM失败:", error);
137
+ });
138
+ } else {
139
+ this.popupContent = customDom;
140
+ }
141
+
142
+ // 注册弹窗专用回调
143
+ const popupHandler = (movement: any) => {
144
+ this.showPopupAtPosition(movement.position);
145
+ };
146
+
147
+ // 存储以便清理
148
+ this._popupClickHandler = popupHandler;
149
+
150
+ // 👇 关键:添加到回调数组
151
+ if (!this.hnMap.map.entityClickHandlers.has(this.graphic)) {
152
+ this.hnMap.map.entityClickHandlers.set(this.graphic, []);
153
+ }
154
+ this.hnMap.map.entityClickHandlers.get(this.graphic)!.push(popupHandler);
155
+ } catch (error) {
156
+ console.error("生成自定义弹窗DOM失败:", error);
157
+ }
121
158
  }
122
159
 
123
- if (!this.graphic || !this.hnMap?.map?.map) {
124
- return;
160
+ /**
161
+ * 在指定位置显示弹窗
162
+ * @param position 屏幕坐标位置
163
+ */
164
+ public showPopupAtPosition(position: any): void {
165
+ if (!this.popupContent) return;
166
+
167
+ // 获取点击的世界坐标
168
+ const ray = this.hnMap.map.map.camera.getPickRay(position);
169
+ if (!ray) return;
170
+
171
+ // 获取笛卡尔坐标
172
+ const cartesian = this.hnMap.map.map.scene.globe.pick(
173
+ ray,
174
+ this.hnMap.map.map.scene
175
+ );
176
+ if (!cartesian) return;
177
+ const PopupClass = CesiumPopup(this.hnMap);
178
+ this.popup = new PopupClass(this.hnMap.map.map, {style: this.popupStyle});
179
+ // 显示弹窗
180
+ this.popup.show({cartesian}, this.popupContent);
125
181
  }
126
182
 
127
- this.screenSpaceEventHandler = new Cesium.ScreenSpaceEventHandler(
128
- this.hnMap.map.map.canvas
129
- );
130
-
131
- this.screenSpaceEventHandler.setInputAction((movement: any) => {
132
- // 检查是否点击到了当前实体
133
- const pickedObject = this.hnMap.map.map.scene.pick(movement.position);
134
-
135
- if (pickedObject && pickedObject.id === this.graphic) {
136
- this.showPopupAtPosition(movement.position);
137
- }
138
- }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
139
- }
140
-
141
- /**
142
- * 在指定位置显示弹窗
143
- * @param position 屏幕坐标位置
144
- */
145
- private showPopupAtPosition(position: any): void {
146
- if (!this.popupContent) return;
147
-
148
- // 获取点击的世界坐标
149
- const ray = this.hnMap.map.map.camera.getPickRay(position);
150
- if (!ray) return;
151
-
152
- // 获取笛卡尔坐标
153
- const cartesian = this.hnMap.map.map.scene.globe.pick(
154
- ray,
155
- this.hnMap.map.map.scene
156
- );
157
- if (!cartesian) return;
158
- const PopupClass = CesiumPopup(this.hnMap);
159
- this.popup = new PopupClass(this.hnMap.map.map, { style: this.popupStyle });
160
- // 显示弹窗
161
- this.popup.show({ cartesian }, this.popupContent);
162
- }
163
- /**
164
- * 飞行定位到要素
165
- * @param option 飞行定位选项
166
- */
167
- flyTo(option: any = {}): void {
168
- if (this.graphic && this.hnMap && this.hnMap.map.map) {
169
- try {
170
- // 根据实体类型确定飞行目标
171
- let target: any = null;
172
-
173
- // 优先判断具体类型
174
- if (this.graphic.polyline && this.graphic.polyline.positions) {
175
- // 线实体
176
- const positions = this.graphic.polyline.positions.getValue(
177
- Cesium.JulianDate.now()
178
- );
179
- console.log("线实体的坐标:", positions);
180
- if (positions && positions.length > 0) {
181
- const boundingSphere = Cesium.BoundingSphere.fromPoints(positions); // 计算包围球
182
- // 增加包围球半径,确保有足够的视野
183
- const radiusMultiplier = option.radiusMultiplier || 3.0;
184
- this.hnMap.map.map.camera.flyToBoundingSphere(boundingSphere, {
185
- duration: option.duration || 2.0,
186
- offset: new Cesium.HeadingPitchRange(
187
- Cesium.Math.toRadians(option.heading || 0),
188
- Cesium.Math.toRadians(option.pitch || -90),
189
- boundingSphere.radius * radiusMultiplier // 增加视野范围
190
- ),
191
- }); // 飞行到包围球
192
- return;
193
- }
194
- } else if (this.graphic.polygon && this.graphic.polygon.hierarchy) {
195
- // 面实体
196
- const hierarchy = this.graphic.polygon.hierarchy.getValue(
197
- Cesium.JulianDate.now()
198
- );
199
-
200
- if (hierarchy && hierarchy.positions) {
201
- // console.log("面实体的坐标:", hierarchy.positions);
202
-
203
- const boundingSphere = Cesium.BoundingSphere.fromPoints(
204
- hierarchy.positions
205
- );
206
- // 增加包围球半径,确保有足够的视野
207
- const radiusMultiplier = option.radiusMultiplier || 3.0;
208
- this.hnMap.map.map.camera.flyToBoundingSphere(boundingSphere, {
209
- duration: option.duration || 2.0,
210
- offset: new Cesium.HeadingPitchRange(
211
- Cesium.Math.toRadians(option.heading || 0),
212
- Cesium.Math.toRadians(option.pitch || -90),
213
- boundingSphere.radius * radiusMultiplier // 增加视野范围
214
- ),
215
- });
216
- return;
217
- }
218
- } else if (
219
- this.graphic.rectangle &&
220
- this.graphic.rectangle.coordinates
221
- ) {
222
- // 矩形实体
223
- target = this.graphic.rectangle.coordinates.getValue(
224
- Cesium.JulianDate.now()
225
- );
226
- if (target) {
227
- // 为矩形增加一些边距
228
- const west = target.west - 0.001;
229
- const south = target.south - 0.001;
230
- const east = target.east + 0.001;
231
- const north = target.north + 0.001;
232
-
233
- const expandedRectangle = new Cesium.Rectangle(
234
- west,
235
- south,
236
- east,
237
- north
238
- );
239
-
240
- this.hnMap.map.map.camera.flyTo({
241
- destination: expandedRectangle,
242
- duration: option.duration || 2.0,
243
- orientation: {
244
- heading: Cesium.Math.toRadians(option.heading || 0),
245
- pitch: Cesium.Math.toRadians(option.pitch || -90),
246
- roll: Cesium.Math.toRadians(option.roll || 0),
247
- },
248
- });
249
- return;
250
- }
251
- } else if (this.graphic.position) {
252
- // 点实体或其他具有position属性的实体
253
- target = this.graphic.position.getValue(Cesium.JulianDate.now());
254
- // console.log("点实体的坐标:", target);
255
-
256
- if (target) {
257
- // 对于点实体,设置一个合适的高度以确保视野
258
- const cartographic = Cesium.Cartographic.fromCartesian(target); // 转换为经纬度
259
- // 调整默认高度和俯仰角,确保点在视图中央
260
- const defaultHeight = option.height || 1000; // 减小默认高度到1000米
261
- const finalHeight = Math.max(
262
- cartographic.height + defaultHeight,
263
- defaultHeight
264
- );
265
- const finalPosition = Cesium.Cartesian3.fromRadians(
266
- cartographic.longitude,
267
- cartographic.latitude,
268
- finalHeight
269
- ); // 转换回笛卡尔坐标
270
-
271
- this.hnMap.map.map.camera.flyTo({
272
- destination: finalPosition,
273
- duration: option.duration || 2.0,
274
- orientation: {
275
- heading: Cesium.Math.toRadians(option.heading || 0), // 默认朝向正北
276
- // 调整俯仰角为-90度(垂直向下看),确保点在视图中央
277
- pitch: Cesium.Math.toRadians(option.pitch || -90),
278
- roll: Cesium.Math.toRadians(option.roll || 0), // 默认滚转角
279
- },
280
- });
281
- return;
282
- }
283
- }
183
+ // 新增方法:注册点击回调(由 map 统一调用)
184
+ public registerClick(handler: (movement: any) => void): void {
185
+ if (!this.graphic || !this.hnMap?.map?.map) return;
284
186
 
285
- console.warn("无法确定实体类型或实体没有有效的位置信息", this.graphic);
286
- } catch (error) {
287
- console.error("飞行定位失败:", error);
288
- }
187
+ // 将回调注册到 map 的注册表中
188
+ this.hnMap.map.entityClickHandlers.set(this.graphic, handler);
289
189
  }
290
- }
291
-
292
- /**
293
- * 销毁要素
294
- * 从地图中移除并销毁要素
295
- */
296
- destroy(): void {
297
- // 移除所有事件监听
298
- this.offAll();
299
-
300
- if (this.graphic && this.hnMap && this.hnMap.map) {
301
- try {
302
- // 从地图实体集合中移除
303
- if (this.hnMap.map.entities) {
304
- this.hnMap.map.entities.remove(this.graphic);
305
- }
306
190
 
307
- // 如果是数据源中的实体
308
- if (this.graphic.dataSource) {
309
- this.graphic.dataSource.entities.remove(this.graphic);
191
+ // 取消注册(用于销毁时)
192
+ public unregisterClick(): void {
193
+ if (this.graphic && this.hnMap?.map?.map) {
194
+ this.hnMap.map.entityClickHandlers.delete(this.graphic);
310
195
  }
311
-
312
- this.graphic = null;
313
- } catch (error) {
314
- console.error("销毁实体失败:", error);
315
- }
316
196
  }
317
- }
318
-
319
- /**
320
- * 监听事件
321
- * @param eventType 事件类型
322
- * @param callback 事件回调函数
323
- */
324
- on(eventType: string, callback: (data: any) => void): void {
325
- this.off(eventType);
326
-
327
- if (this.graphic && this.hnMap && this.hnMap.map && this.hnMap.map.map) {
328
- // 初始化屏幕空间事件处理器
329
- if (!this.screenSpaceEventHandler) {
330
- this.screenSpaceEventHandler = new Cesium.ScreenSpaceEventHandler(
331
- this.hnMap.map.map.canvas
332
- ); // 创建屏幕空间事件处理器
333
- }
334
-
335
- switch (eventType) {
336
- case "click":
337
- this.event[eventType] = (click: any) => {
338
- // console.log("点击事件触发...");
339
- // 检查是否点击到了当前实体
340
- const pickedObject = this.hnMap.map.map.scene.pick(click.position);
341
- if (pickedObject && pickedObject.id === this.graphic) {
342
- const data =
343
- this.option && this.option.data ? this.option.data : {};
344
- callback(data);
197
+
198
+ /**
199
+ * 飞行定位到要素
200
+ * @param option 飞行定位选项
201
+ */
202
+ flyTo(option: any = {}): void {
203
+ if (this.graphic && this.hnMap && this.hnMap.map.map) {
204
+ try {
205
+ // 根据实体类型确定飞行目标
206
+ let target: any = null;
207
+
208
+ // 优先判断具体类型
209
+ if (this.graphic.polyline && this.graphic.polyline.positions) {
210
+ // 线实体
211
+ const positions = this.graphic.polyline.positions.getValue(
212
+ Cesium.JulianDate.now()
213
+ );
214
+ // console.log("线实体的坐标:", positions);
215
+ if (positions && positions.length > 0) {
216
+ const boundingSphere = Cesium.BoundingSphere.fromPoints(positions); // 计算包围球
217
+ // 增加包围球半径,确保有足够的视野
218
+ const radiusMultiplier = option.radiusMultiplier || 3.0;
219
+ this.hnMap.map.map.camera.flyToBoundingSphere(boundingSphere, {
220
+ duration: option.duration || 2.0,
221
+ offset: new Cesium.HeadingPitchRange(
222
+ Cesium.Math.toRadians(option.heading || 0),
223
+ Cesium.Math.toRadians(option.pitch || -90),
224
+ boundingSphere.radius * radiusMultiplier // 增加视野范围
225
+ ),
226
+ }); // 飞行到包围球
227
+ return;
228
+ }
229
+ } else if (this.graphic.polygon && this.graphic.polygon.hierarchy) {
230
+ // 面实体
231
+ const hierarchy = this.graphic.polygon.hierarchy.getValue(
232
+ Cesium.JulianDate.now()
233
+ );
234
+
235
+ if (hierarchy && hierarchy.positions) {
236
+ // console.log("面实体的坐标:", hierarchy.positions);
237
+
238
+ const boundingSphere = Cesium.BoundingSphere.fromPoints(
239
+ hierarchy.positions
240
+ );
241
+ // 增加包围球半径,确保有足够的视野
242
+ const radiusMultiplier = option.radiusMultiplier || 3.0;
243
+ this.hnMap.map.map.camera.flyToBoundingSphere(boundingSphere, {
244
+ duration: option.duration || 2.0,
245
+ offset: new Cesium.HeadingPitchRange(
246
+ Cesium.Math.toRadians(option.heading || 0),
247
+ Cesium.Math.toRadians(option.pitch || -90),
248
+ boundingSphere.radius * radiusMultiplier // 增加视野范围
249
+ ),
250
+ });
251
+ return;
252
+ }
253
+ } else if (
254
+ this.graphic.rectangle &&
255
+ this.graphic.rectangle.coordinates
256
+ ) {
257
+ // 矩形实体
258
+ target = this.graphic.rectangle.coordinates.getValue(
259
+ Cesium.JulianDate.now()
260
+ );
261
+ if (target) {
262
+ // 为矩形增加一些边距
263
+ const west = target.west - 0.001;
264
+ const south = target.south - 0.001;
265
+ const east = target.east + 0.001;
266
+ const north = target.north + 0.001;
267
+
268
+ const expandedRectangle = new Cesium.Rectangle(
269
+ west,
270
+ south,
271
+ east,
272
+ north
273
+ );
274
+
275
+ this.hnMap.map.map.camera.flyTo({
276
+ destination: expandedRectangle,
277
+ duration: option.duration || 2.0,
278
+ orientation: {
279
+ heading: Cesium.Math.toRadians(option.heading || 0),
280
+ pitch: Cesium.Math.toRadians(option.pitch || -90),
281
+ roll: Cesium.Math.toRadians(option.roll || 0),
282
+ },
283
+ });
284
+ return;
285
+ }
286
+ } else if (this.graphic.position) {
287
+ // 点实体或其他具有position属性的实体
288
+ target = this.graphic.position.getValue(Cesium.JulianDate.now());
289
+ // console.log("点实体的坐标:", target);
290
+
291
+ if (target) {
292
+ // 对于点实体,设置一个合适的高度以确保视野
293
+ const cartographic = Cesium.Cartographic.fromCartesian(target); // 转换为经纬度
294
+ // 调整默认高度和俯仰角,确保点在视图中央
295
+ const defaultHeight = option.height || 1000; // 减小默认高度到1000米
296
+ const finalHeight = Math.max(
297
+ cartographic.height + defaultHeight,
298
+ defaultHeight
299
+ );
300
+ const finalPosition = Cesium.Cartesian3.fromRadians(
301
+ cartographic.longitude,
302
+ cartographic.latitude,
303
+ finalHeight
304
+ ); // 转换回笛卡尔坐标
305
+
306
+ this.hnMap.map.map.camera.flyTo({
307
+ destination: finalPosition,
308
+ duration: option.duration || 2.0,
309
+ orientation: {
310
+ heading: Cesium.Math.toRadians(option.heading || 0), // 默认朝向正北
311
+ // 调整俯仰角为-90度(垂直向下看),确保点在视图中央
312
+ pitch: Cesium.Math.toRadians(option.pitch || -90),
313
+ roll: Cesium.Math.toRadians(option.roll || 0), // 默认滚转角
314
+ },
315
+ });
316
+ return;
317
+ }
318
+ }
319
+
320
+ console.warn("无法确定实体类型或实体没有有效的位置信息", this.graphic);
321
+ } catch (error) {
322
+ console.error("飞行定位失败:", error);
345
323
  }
346
- };
347
-
348
- this.screenSpaceEventHandler.setInputAction(
349
- this.event[eventType],
350
- Cesium.ScreenSpaceEventType.LEFT_CLICK
351
- );
352
- break;
353
- }
324
+ }
354
325
  }
355
- }
356
-
357
- /**
358
- * 移除事件监听
359
- * @param eventType 事件类型
360
- */
361
- off(eventType: string): void {
362
- if (this.event[eventType] && this.screenSpaceEventHandler) {
363
- try {
364
- switch (eventType) {
365
- case "click":
366
- this.screenSpaceEventHandler.removeInputAction(
367
- Cesium.ScreenSpaceEventType.LEFT_CLICK
368
- );
369
- break;
326
+
327
+ /**
328
+ * 销毁要素
329
+ * 从地图中移除并销毁要素
330
+ */
331
+ destroy(): void {
332
+ // 清理所有事件监听器
333
+ Object.keys(this.event).forEach((eventType) => {
334
+ this.off(eventType);
335
+ });
336
+
337
+ this.clearPopupState();
338
+
339
+ if (this.graphic && this.hnMap && this.hnMap.map) {
340
+ try {
341
+ // 从地图实体集合中移除
342
+ if (this.hnMap.map.entities) {
343
+ this.hnMap.map.entities.remove(this.graphic);
344
+ }
345
+
346
+ // 如果是数据源中的实体
347
+ if (this.graphic.dataSource) {
348
+ this.graphic.dataSource.entities.remove(this.graphic);
349
+ }
350
+
351
+ this.graphic = null;
352
+ } catch (error) {
353
+ console.error("销毁实体失败:", error);
354
+ }
370
355
  }
371
- } catch (error) {
372
- console.warn("移除事件监听失败:", error);
373
- }
356
+ }
374
357
 
375
- delete this.event[eventType];
358
+ /**
359
+ * 监听事件
360
+ * @param eventType 事件类型
361
+ * @param callback 事件回调函数
362
+ */
363
+ on(eventType: string, callback: (data: any) => void): void {
364
+ this.off(eventType);
365
+
366
+ if (!this.graphic || !this.hnMap?.map?.map) return;
367
+
368
+ if (eventType === "click") {
369
+ // 包装回调(与之前一致)
370
+ const handler = (movement: any) => {
371
+ const data = this.option?.data || {};
372
+ callback(data);
373
+ };
374
+
375
+ // 存储以便 off 使用
376
+ this._userClickHandler = handler;
377
+
378
+ // 添加到回调数组
379
+ if (!this.hnMap.map.entityClickHandlers.has(this.graphic)) {
380
+ this.hnMap.map.entityClickHandlers.set(this.graphic, []);
381
+ }
382
+ this.hnMap.map.entityClickHandlers.get(this.graphic)!.push(handler);
383
+ this.event[eventType] = handler;
384
+ }
385
+ // 其他事件类型(如 mouseover)暂不处理,或按需扩展
376
386
  }
377
- }
378
-
379
- /**
380
- * 移除所有事件监听
381
- */
382
- private offAll(): void {
383
- // 清理所有事件监听器
384
- Object.keys(this.event).forEach((eventType) => {
385
- this.off(eventType);
386
- });
387
-
388
- // 销毁屏幕空间事件处理器
389
- if (this.screenSpaceEventHandler) {
390
- try {
391
- this.screenSpaceEventHandler.destroy();
392
- } catch (error) {
393
- console.warn("销毁屏幕空间处理器失败:", error);
394
- }
395
- this.screenSpaceEventHandler = null;
387
+
388
+ /**
389
+ * 移除事件监听
390
+ * @param eventType 事件类型
391
+ */
392
+ off(eventType: string): void {
393
+ if (eventType === 'click' && this._userClickHandler && this.graphic && this.hnMap?.map?.map) {
394
+ const handlers = this.hnMap.map.entityClickHandlers.get(this.graphic);
395
+ if (handlers) {
396
+ const index = handlers.indexOf(this._userClickHandler);
397
+ if (index !== -1) {
398
+ handlers.splice(index, 1);
399
+ if (handlers.length === 0) {
400
+ this.hnMap.map.entityClickHandlers.delete(this.graphic);
401
+ }
402
+ }
403
+ }
404
+ this._userClickHandler = null;
405
+ }
396
406
  }
397
- }
398
407
 
399
408
  }