import Feature from 'ol/Feature'; import Point from 'ol/geom/Point'; import OlMap from 'ol/Map'; import VectorLayer from 'ol/layer/Vector'; import VectorSource from 'ol/source/Vector'; import { fromLonLat } from 'ol/proj'; import { getIconPath } from '@/utils/index'; type PointLayerStyleFactory = (feature: Feature) => any; type PointLayerManagerOptions = { map: OlMap | null; createStyle: PointLayerStyleFactory; }; export class PointLayerManager { private map: OlMap | null; private createStyle: PointLayerStyleFactory; private layerRegistry: Map> = new Map(); private layerFeatureIndexes: Map< string, Map> > = new Map(); constructor(options: PointLayerManagerOptions) { this.map = options.map; this.createStyle = options.createStyle; } // 备注:同步更新地图实例,供地图初始化后把真实 map 注入点图层管理器。 setMap(map: OlMap | null) { this.map = map; } // 备注:获取当前屏幕可视区域内的所有点要素(仅包含图层可见且图例可见的要素)。 getFeaturesInViewport(): Feature[] { if (!this.map) return []; const extent = this.map.getView().calculateExtent(this.map.getSize()); if (!extent) return []; const [minX, minY, maxX, maxY] = extent; const result: Feature[] = []; this.forEachFeature((feature, layer) => { // 跳过图层本身不可见的要素 if (!layer.getVisible()) return; const geom = feature.getGeometry(); if (!geom || geom.getType() !== 'Point') return; const coords = (geom as Point).getCoordinates(); const [x, y] = coords; if (x >= minX && x <= maxX && y >= minY && y <= maxY) { result.push(feature); } }); return result; } // 备注:统一返回当前点图层注册表,供外层做遍历或清理。 getRegistry() { return this.layerRegistry; } // 备注:按图层 key 获取已注册的点图层实例。 getLayer(layerKey: string) { return this.layerRegistry.get(layerKey); } // 备注:返回当前所有点图层实例,供命中检测和批量刷新使用。 getLayers(): VectorLayer[] { return Array.from(this.layerRegistry.values()); } // 备注:判断指定图层 key 的点图层是否已存在。 hasLayer(layerKey: string): boolean { return this.layerRegistry.has(layerKey); } // 备注:按需获取或创建点图层实例,并自动完成注册与挂载。 ensureLayer(layerKey: string, layerType?: string) { if (!this.map || !layerKey) return null; let vectorLayer = this.layerRegistry.get(layerKey); if (vectorLayer) { return vectorLayer; } const vectorSource = new VectorSource(); vectorLayer = new VectorLayer({ source: vectorSource, zIndex: 100, declutter: true, style: feature => this.createStyle(feature as Feature) }); (vectorLayer as any).type = layerType || layerKey; this.layerRegistry.set(layerKey, vectorLayer); this.map.addLayer(vectorLayer); return vectorLayer; } // 备注:把后端点位数组转换为 OpenLayers Feature,并写入指定点图层。 addDataLayer(pointData: any, layerType: string): void { if (!this.map) return; let dataArray: any[] = []; let targetLayerKey = layerType; if (Array.isArray(pointData)) { dataArray = pointData; } else { dataArray = pointData?.data || []; targetLayerKey = pointData?.key || layerType; } if (!targetLayerKey) { console.warn('缺少图层 Key,无法加载描点'); return; } const vectorLayer = this.ensureLayer(targetLayerKey, layerType); if (!vectorLayer) return; const vectorSource = vectorLayer.getSource(); if (!vectorSource) { return; } vectorSource.clear(); this.resetLayerFeatureIndexes(targetLayerKey); if (dataArray.length === 0) { return; } const features: Feature[] = []; dataArray.forEach((item: any) => { const feature = this.createFeature(item, targetLayerKey); if (feature) { this.indexFeature(targetLayerKey, feature); features.push(feature); } }); if (features.length > 0) { vectorSource.addFeatures(features); } } // 备注:统一控制整个点图层显隐,供图层树勾选联动使用。 setLayerVisible(layerKey: string, visible?: boolean): void { const vectorLayer = this.layerRegistry.get(layerKey); if (vectorLayer && vectorLayer.getVisible() !== visible) { vectorLayer.setVisible(visible); vectorLayer.changed(); } } // 备注:按图层 key 删除点图层并清空其 source 数据。 removeLayer(layerKey: string): void { if (!this.map || !layerKey) { console.warn('removePointLayer: 无效的图层 key 或地图未初始化'); return; } const vectorLayer = this.layerRegistry.get(layerKey); if (!vectorLayer) { console.warn(`未找到标识为 [${layerKey}] 的描点图层实例`); return; } const source = vectorLayer.getSource(); if (source) { source.clear(); } this.map.removeLayer(vectorLayer); this.layerRegistry.delete(layerKey); this.layerFeatureIndexes.delete(layerKey); } // 备注:按图层遍历所有点图层实例,供 hover 刷新和区域裁切复用。 forEachLayer( callback: (layer: VectorLayer, layerKey: string) => void ): void { this.layerRegistry.forEach((layer, layerKey) => { callback(layer, layerKey); }); } // 备注:遍历所有点要素,供区域裁切或批量状态更新使用。 forEachFeature( callback: ( feature: Feature, layer: VectorLayer, layerKey: string ) => void ): void { this.forEachLayer((layer, layerKey) => { const source = layer.getSource(); if (!source) return; source.getFeatures().forEach(feature => { callback(feature as Feature, layer, layerKey); }); }); } // 备注:按图例字段值统一控制要素显隐,供图例和容量筛选联动复用。 setLegendVisibleByField( layerKey: string, matchValue: string, checked: boolean, fieldKey: string = 'anchoPointState' ): void { const vectorLayer = this.layerRegistry.get(layerKey); if (!vectorLayer) return; const source = vectorLayer.getSource(); if (!source) return; if (!matchValue) { source.getFeatures().forEach(feature => { if (feature.get('_legendVisible') !== false) { feature.set('_legendVisible', false); feature.changed(); } }); vectorLayer.changed(); return; } const targetFeatures = this.getIndexedFeatures( layerKey, fieldKey, matchValue ); if (targetFeatures.length === 0) return; targetFeatures.forEach(feature => { if (feature.get('_legendVisible') !== checked) { feature.set('_legendVisible', checked); feature.changed(); } }); vectorLayer.changed(); } // 备注:统一销毁所有点图层并清空注册表,供地图销毁阶段复用。 destroy(): void { this.forEachLayer(layer => { const source = layer.getSource(); if (source) { source.clear(); } if (this.map && this.map.getLayers().getArray().includes(layer)) { this.map.removeLayer(layer); } if ((layer as any).setMap) { (layer as any).setMap(null); } }); this.layerRegistry.clear(); this.layerFeatureIndexes.clear(); } // 备注:统一把业务点位数据转换为 Feature,并补齐样式依赖字段。 private createFeature(item: any, targetLayerKey: string): Feature | null { const { lgtd, lttd, stnm, iconCode, titleName, ennm } = item; if (lgtd == null || lttd == null) { return null; } const lon = Number(lgtd); const lat = Number(lttd); if (isNaN(lon) || isNaN(lat) || !isFinite(lon) || !isFinite(lat)) { return null; } if (Math.abs(lon) > 180 || Math.abs(lat) > 90) { return null; } const coord = fromLonLat([lon, lat]); if (!isFinite(coord[0]) || !isFinite(coord[1])) { return null; } let iconUrl = iconCode ? getIconPath(iconCode) : ''; if (!iconUrl) { iconUrl = getIconPath('default') || ''; } const feature = new Feature({ geometry: new Point(coord), ...item, _layerKey: targetLayerKey }); feature.setId(item.stcd || item._id); feature.set('_iconUrl', iconUrl); feature.set( '_labelText', item.sttpMap === 'ylfb' ? item.ftp || '' : titleName || stnm || ennm || '' ); feature.set('_legendVisible', true); feature.set('_sttpMap', item.sttpMap); if (item.popupHtml) { feature.set('popupHtml', item.popupHtml); } return feature; } // 备注:重置单个图层的字段索引,供重新灌入数据或删除图层时复用。 private resetLayerFeatureIndexes(layerKey: string): void { this.layerFeatureIndexes.set(layerKey, new Map()); } // 备注:按字段值给要素建立索引,供图例切换时快速命中目标锚点集合。 private indexFeature(layerKey: string, feature: Feature): void { const fieldIndexMap = this.layerFeatureIndexes.get(layerKey) || new Map>(); const legendFieldKey = 'anchoPointState'; const legendFieldValue = feature.get(legendFieldKey); if (legendFieldValue != null) { const valueMap = fieldIndexMap.get(legendFieldKey) || new Map(); const normalizedValue = String(legendFieldValue); const targetFeatures = valueMap.get(normalizedValue) || []; targetFeatures.push(feature); valueMap.set(normalizedValue, targetFeatures); fieldIndexMap.set(legendFieldKey, valueMap); } this.layerFeatureIndexes.set(layerKey, fieldIndexMap); } // 备注:根据图层 key、字段名和值快速返回目标要素集合,避免图例切换时整层遍历。 private getIndexedFeatures( layerKey: string, fieldKey: string, matchValue: string ): Feature[] { const fieldIndexMap = this.layerFeatureIndexes.get(layerKey); if (!fieldIndexMap) return []; const valueMap = fieldIndexMap.get(fieldKey); if (!valueMap) return []; return valueMap.get(String(matchValue)) || []; } }