/** * Cesium地图实体基类 * 提供Cesium地图要素的通用功能,如弹窗管理、事件处理、飞行定位等 */ import CesiumPopup from "./cesium_popup"; // 导入弹窗类 export default class CesiumEntity { /** 配置选项 */ protected option: any = null; /** 事件监听器集合 */ event: Record = {}; /** 图形实例 */ graphic: any = null; /** 地图实例 */ protected hnMap: any = null; /** 弹窗实例 */ private popup: any = null; /** 弹窗内容缓存 */ private popupContent: string | null = null; /** 是否显示弹窗 */ enablePopup: boolean = false; /** 弹窗样式配置 */ private popupStyle: any = null; private _popupClickHandler: ((movement: any) => void) | null = null; private _userClickHandler: ((movement: any) => void) | null = null; /** * 构造函数 * @param hnMap 地图实例 */ constructor(hnMap: any) { this.hnMap = hnMap; // 创建弹窗实例 if (hnMap && hnMap.map && hnMap.map.map) { } } /** * 设置弹窗样式 * @param style 弹窗样式配置 */ setPopupStyle(style: any): void { this.popupStyle = style; // 如果弹窗已存在,重新创建以应用新样式 if (this.popup && this.hnMap && this.hnMap.map && this.hnMap.map.map) { this.popup.destroy(); const PopupClass = CesiumPopup(this.hnMap); this.popup = new PopupClass(this.hnMap.map.map, { style: this.popupStyle, }); } } // 新增方法:清理现有弹窗相关状态 private clearPopupState(): void { // 销毁弹窗 if (this.popup) { this.popup.destroy(); this.popup = null; } // 清理弹窗回调 if (this._popupClickHandler && this.graphic && this.hnMap?.map?.map) { const handlers = this.hnMap.map.entityClickHandlers.get(this.graphic); if (handlers) { const index = handlers.indexOf(this._popupClickHandler); if (index !== -1) { handlers.splice(index, 1); if (handlers.length === 0) { this.hnMap.map.entityClickHandlers.delete(this.graphic); } } } this._popupClickHandler = null; } this.popupContent = null; this.enablePopup = false; } /** * 添加属性弹窗 * 点击要素时显示包含要素属性信息的弹窗 */ addPopupByAttr(): void { this.clearPopupState(); if (!this.option?.data || !this.graphic || !this.hnMap?.map?.map) return; // 构建内容 let content = '
'; for (const key in this.option.data) { if (this.option.data.hasOwnProperty(key)) { content += `
${key}: ${this.option.data[key]}
`; } } content += '
'; this.popupContent = content; // 注册弹窗专用回调 const popupHandler = (movement: any) => { this.showPopupAtPosition(movement.position); }; // 存储以便清理 this._popupClickHandler = popupHandler; // 👇 关键:添加到回调数组 if (!this.hnMap.map.entityClickHandlers.has(this.graphic)) { this.hnMap.map.entityClickHandlers.set(this.graphic, []); } this.hnMap.map.entityClickHandlers.get(this.graphic)!.push(popupHandler); } /** * 添加自定义DOM弹窗 * 点击要素时显示自定义DOM结构的弹窗 * @param getCustomDom 自定义DOM生成函数,接收要素数据,返回DOM字符串 */ addCustomPopup(getCustomDom: (data: any) => Promise | string): void { this.clearPopupState(); this.enablePopup = true; const data = this.option && this.option.data ? this.option.data : {}; try { const customDom = getCustomDom(data); if (customDom instanceof Promise) { customDom .then((dom: string) => { this.popupContent = dom; }) .catch((error: any) => { console.error("生成自定义弹窗DOM失败:", error); }); } else { this.popupContent = customDom; } // 注册弹窗专用回调 const popupHandler = (movement: any) => { this.showPopupAtPosition(movement.position); }; // 存储以便清理 this._popupClickHandler = popupHandler; // 👇 关键:添加到回调数组 if (!this.hnMap.map.entityClickHandlers.has(this.graphic)) { this.hnMap.map.entityClickHandlers.set(this.graphic, []); } this.hnMap.map.entityClickHandlers.get(this.graphic)!.push(popupHandler); } catch (error) { console.error("生成自定义弹窗DOM失败:", error); } } /** * 在指定位置显示弹窗 * @param position 屏幕坐标位置 */ public showPopupAtPosition(position: any): void { if (!this.popupContent) return; // 获取点击的世界坐标 const ray = this.hnMap.map.map.camera.getPickRay(position); if (!ray) return; // 获取笛卡尔坐标 const cartesian = this.hnMap.map.map.scene.globe.pick( ray, this.hnMap.map.map.scene ); if (!cartesian) return; const PopupClass = CesiumPopup(this.hnMap); this.popup = new PopupClass(this.hnMap.map.map, {style: this.popupStyle}); // 显示弹窗 this.popup.show({cartesian}, this.popupContent); } // 新增方法:注册点击回调(由 map 统一调用) public registerClick(handler: (movement: any) => void): void { if (!this.graphic || !this.hnMap?.map?.map) return; // 将回调注册到 map 的注册表中 this.hnMap.map.entityClickHandlers.set(this.graphic, handler); } // 取消注册(用于销毁时) public unregisterClick(): void { if (this.graphic && this.hnMap?.map?.map) { this.hnMap.map.entityClickHandlers.delete(this.graphic); } } /** * 飞行定位到要素 * @param option 飞行定位选项 */ flyTo(option: any = {}): void { if (this.graphic && this.hnMap && this.hnMap.map.map) { try { // 根据实体类型确定飞行目标 let target: any = null; // 优先判断具体类型 if (this.graphic.polyline && this.graphic.polyline.positions) { // 线实体 const positions = this.graphic.polyline.positions.getValue( Cesium.JulianDate.now() ); // console.log("线实体的坐标:", positions); if (positions && positions.length > 0) { const boundingSphere = Cesium.BoundingSphere.fromPoints(positions); // 计算包围球 // 增加包围球半径,确保有足够的视野 const radiusMultiplier = option.radiusMultiplier || 3.0; this.hnMap.map.map.camera.flyToBoundingSphere(boundingSphere, { duration: option.duration || 2.0, offset: new Cesium.HeadingPitchRange( Cesium.Math.toRadians(option.heading || 0), Cesium.Math.toRadians(option.pitch || -90), boundingSphere.radius * radiusMultiplier // 增加视野范围 ), }); // 飞行到包围球 return; } } else if (this.graphic.polygon && this.graphic.polygon.hierarchy) { // 面实体 const hierarchy = this.graphic.polygon.hierarchy.getValue( Cesium.JulianDate.now() ); if (hierarchy && hierarchy.positions) { // console.log("面实体的坐标:", hierarchy.positions); const boundingSphere = Cesium.BoundingSphere.fromPoints( hierarchy.positions ); // 增加包围球半径,确保有足够的视野 const radiusMultiplier = option.radiusMultiplier || 3.0; this.hnMap.map.map.camera.flyToBoundingSphere(boundingSphere, { duration: option.duration || 2.0, offset: new Cesium.HeadingPitchRange( Cesium.Math.toRadians(option.heading || 0), Cesium.Math.toRadians(option.pitch || -90), boundingSphere.radius * radiusMultiplier // 增加视野范围 ), }); return; } } else if ( this.graphic.rectangle && this.graphic.rectangle.coordinates ) { // 矩形实体 target = this.graphic.rectangle.coordinates.getValue( Cesium.JulianDate.now() ); if (target) { // 为矩形增加一些边距 const west = target.west - 0.001; const south = target.south - 0.001; const east = target.east + 0.001; const north = target.north + 0.001; const expandedRectangle = new Cesium.Rectangle( west, south, east, north ); this.hnMap.map.map.camera.flyTo({ destination: expandedRectangle, duration: option.duration || 2.0, orientation: { heading: Cesium.Math.toRadians(option.heading || 0), pitch: Cesium.Math.toRadians(option.pitch || -90), roll: Cesium.Math.toRadians(option.roll || 0), }, }); return; } } else if (this.graphic.position) { // 点实体或其他具有position属性的实体 target = this.graphic.position.getValue(Cesium.JulianDate.now()); // console.log("点实体的坐标:", target); if (target) { // 对于点实体,设置一个合适的高度以确保视野 const cartographic = Cesium.Cartographic.fromCartesian(target); // 转换为经纬度 // 调整默认高度和俯仰角,确保点在视图中央 const defaultHeight = option.height || 1000; // 减小默认高度到1000米 const finalHeight = Math.max( cartographic.height + defaultHeight, defaultHeight ); const finalPosition = Cesium.Cartesian3.fromRadians( cartographic.longitude, cartographic.latitude, finalHeight ); // 转换回笛卡尔坐标 this.hnMap.map.map.camera.flyTo({ destination: finalPosition, duration: option.duration || 2.0, orientation: { heading: Cesium.Math.toRadians(option.heading || 0), // 默认朝向正北 // 调整俯仰角为-90度(垂直向下看),确保点在视图中央 pitch: Cesium.Math.toRadians(option.pitch || -90), roll: Cesium.Math.toRadians(option.roll || 0), // 默认滚转角 }, }); return; } } console.warn("无法确定实体类型或实体没有有效的位置信息", this.graphic); } catch (error) { console.error("飞行定位失败:", error); } } } /** * 销毁要素 * 从地图中移除并销毁要素 */ destroy(): void { // 清理所有事件监听器 Object.keys(this.event).forEach((eventType) => { this.off(eventType); }); this.clearPopupState(); if (this.graphic && this.hnMap && this.hnMap.map) { try { // 从地图实体集合中移除 if (this.hnMap.map.entities) { this.hnMap.map.entities.remove(this.graphic); } // 如果是数据源中的实体 if (this.graphic.dataSource) { this.graphic.dataSource.entities.remove(this.graphic); } this.graphic = null; } catch (error) { console.error("销毁实体失败:", error); } } } /** * 监听事件 * @param eventType 事件类型 * @param callback 事件回调函数 */ on(eventType: string, callback: (data: any) => void): void { this.off(eventType); if (!this.graphic || !this.hnMap?.map?.map) return; if (eventType === "click") { // 包装回调(与之前一致) const handler = (movement: any) => { const data = this.option?.data || {}; callback(data); }; // 存储以便 off 使用 this._userClickHandler = handler; // 添加到回调数组 if (!this.hnMap.map.entityClickHandlers.has(this.graphic)) { this.hnMap.map.entityClickHandlers.set(this.graphic, []); } this.hnMap.map.entityClickHandlers.get(this.graphic)!.push(handler); this.event[eventType] = handler; } // 其他事件类型(如 mouseover)暂不处理,或按需扩展 } /** * 移除事件监听 * @param eventType 事件类型 */ off(eventType: string): void { if (eventType === 'click' && this._userClickHandler && this.graphic && this.hnMap?.map?.map) { const handlers = this.hnMap.map.entityClickHandlers.get(this.graphic); if (handlers) { const index = handlers.indexOf(this._userClickHandler); if (index !== -1) { handlers.splice(index, 1); if (handlers.length === 0) { this.hnMap.map.entityClickHandlers.delete(this.graphic); } } } this._userClickHandler = null; } } // 获取指定经纬度的地形高度 async getHeightByTerrain(lng: number, lat: number) { // if (this.hnMap.map?.map?.scene?.terrainProvider) { // const cartographic = new Cesium.Cartographic( // Cesium.Math.toRadians(lng), // Cesium.Math.toRadians(lat) // ); // // 采样地形高度 // const sampled = await Cesium.sampleTerrainMostDetailed( // this.hnMap.map.map.scene.terrainProvider, // [cartographic] // ); // return sampled[0].height ?? 0 // } return 0 } }