import { Layer } from "ol/layer"; import { Deck } from "@deck.gl/core"; import { toLonLat } from "ol/proj"; /** * 自定义 Layer 类来包装 deck.gl * 将 deck.gl 图层集成到 OpenLayers 地图中 */ export class DeckLayer extends Layer { private deck: Deck; private onVisibilityChange?: (layerId: string, visible: boolean) => void; private userVisibility: Map = new Map(); // 存储用户设置的可见性 /** * @param deckInstance deck.gl 实例 * @param layerProperties 可选:在构造时直接设置到 OpenLayers Layer 的 properties */ constructor(deckInstance: Deck, layerProperties?: Record) { // 将 layerProperties 作为 Layer 的 properties 传入 super({ properties: layerProperties || {} }); this.deck = deckInstance; // 再次确保属性应用到实例(兼容场景) if (layerProperties) { this.setProperties(layerProperties); } } // 设置可见性变化回调 setVisibilityChangeCallback( callback: (layerId: string, visible: boolean) => void ): void { this.onVisibilityChange = callback; } // 初始化用户可见性状态 initUserVisibility(layerId: string, visible: boolean): void { this.userVisibility.set(layerId, visible); } render(frameState: any): HTMLElement { const { size, viewState } = frameState; const [width, height] = size; const [longitude, latitude] = toLonLat(viewState.center); const zoom = viewState.zoom - 1; // 调整 zoom 以匹配 const bearing = (-viewState.rotation * 180) / Math.PI; const deckViewState = { bearing, longitude, latitude, zoom }; this.deck.setProps({ width, height, viewState: deckViewState }); this.deck.redraw(); return document.getElementById("deck-canvas") as HTMLElement; } // 获取 Deck 实例 getDeck(): Deck { return this.deck; } // 设置图层 setDeckLayers(layers: any[]): void { this.deck.setProps({ layers }); } // 获取当前图层 getDeckLayers(): any[] { return this.deck.props.layers || []; } // 添加图层 addDeckLayer(layer: any): void { const currentLayers = this.getDeckLayers(); // 如果已有同 id 图层,则替换保持顺序;否则追加 const idx = currentLayers.findIndex((l: any) => l && l.id === layer.id); if (idx >= 0) { const copy = [...currentLayers]; copy[idx] = layer; this.deck.setProps({ layers: copy }); return; } this.deck.setProps({ layers: [...currentLayers, layer] }); } // 移除图层 removeDeckLayer(layerId: string): void { const currentLayers = this.getDeckLayers(); const filteredLayers = currentLayers.filter( (layer: any) => layer && layer.id !== layerId ); this.deck.setProps({ layers: filteredLayers }); } // 根据 ID 查找图层 getDeckLayerById(layerId: string): any | undefined { const layers = this.getDeckLayers(); return layers.find((layer: any) => layer && layer.id === layerId); } // 更新特定图层:支持传入一个新的 Layer 实例或一个 props 对象 // - 如果传入的是 Layer 实例,则直接替换同 id 的图层为该实例 // - 如果传入的是 props(普通对象),则基于原图层调用 clone(props) updateDeckLayer(layerId: string, layerOrProps: any): void { const layers = this.getDeckLayers(); const updatedLayers = layers.map((layer: any) => { if (!layer || layer.id !== layerId) return layer; // 如果传入的是一个 deck.gl Layer 实例(通常包含 id 和 props) if ( layerOrProps && typeof layerOrProps === "object" && layerOrProps.id !== undefined && typeof layerOrProps.clone === "function" ) { // 替换为新的 layer 实例 return layerOrProps; } // 否则假定传入的是 props 对象,使用现有 layer.clone(props) 创建新实例 try { return layer.clone(layerOrProps); } catch (err) { // 如果 clone 失败,作为降级策略尝试手动复制 props 到新对象(保留原 layer) // 这通常不应该发生,但保证不会抛出而破坏整个 layers 列表 const newLayer = layer.clone ? layer.clone(layerOrProps) : { ...layer, props: { ...(layer.props || {}), ...(layerOrProps || {}) }, }; return newLayer; } }); this.deck.setProps({ layers: updatedLayers }); } // 获取图层的可见性(用户设置的状态,不受其他条件影响) getDeckLayerVisible(layerId: string): boolean | undefined { // 优先返回用户设置的可见性 if (this.userVisibility.has(layerId)) { return this.userVisibility.get(layerId); } // 如果没有用户设置,返回实际图层的可见性 const layer = this.getDeckLayerById(layerId); return layer ? layer.props.visible : undefined; } // 设置图层的可见性 setDeckLayerVisible(layerId: string, visible: boolean): void { // 存储用户设置的可见性 this.userVisibility.set(layerId, visible); // 更新图层(注意:实际的 visible 可能还受其他条件控制) // 优先尝试使用传入的 layer 实例替换,否则使用 clone({ visible }) 来保留图层类型 const found = this.getDeckLayerById(layerId); if (!found) return; try { // 使用 clone 来确保保持同类型实例 this.updateDeckLayer(layerId, { ...found.props, visible }); } catch (err) { // 降级:直接替换属性 this.updateDeckLayer(layerId, { visible }); } // 触发回调通知外部 if (this.onVisibilityChange) { this.onVisibilityChange(layerId, visible); } } // 切换图层的可见性 toggleDeckLayerVisible(layerId: string): void { const currentVisible = this.getDeckLayerVisible(layerId); if (currentVisible !== undefined) { this.setDeckLayerVisible(layerId, !currentVisible); } } // 可选的封装:在外部用更语义化的方式设置属性(内部可直接调用 setProperties) setLayerProperties(props: Record): void { this.setProperties(props); } }