WholeProcessPlatform/frontend/src/components/gis/ol/point-layer-manager.ts

326 lines
9.4 KiB
TypeScript
Raw Normal View History

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<string, VectorLayer<VectorSource>> = new Map();
private layerFeatureIndexes: Map<
string,
Map<string, Map<string, Feature[]>>
> = new Map();
constructor(options: PointLayerManagerOptions) {
this.map = options.map;
this.createStyle = options.createStyle;
}
// 备注:同步更新地图实例,供地图初始化后把真实 map 注入点图层管理器。
setMap(map: OlMap | null) {
this.map = map;
}
// 备注:统一返回当前点图层注册表,供外层做遍历或清理。
getRegistry() {
return this.layerRegistry;
}
// 备注:按图层 key 获取已注册的点图层实例。
getLayer(layerKey: string) {
return this.layerRegistry.get(layerKey);
}
// 备注:返回当前所有点图层实例,供命中检测和批量刷新使用。
getLayers(): VectorLayer<VectorSource>[] {
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);
}
}
// 备注:按图层 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<VectorSource>, layerKey: string) => void
): void {
this.layerRegistry.forEach((layer, layerKey) => {
callback(layer, layerKey);
});
}
// 备注:遍历所有点要素,供区域裁切或批量状态更新使用。
forEachFeature(
callback: (
feature: Feature,
layer: VectorLayer<VectorSource>,
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', 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<string, Map<string, Feature[]>>();
const legendFieldKey = 'anchoPointState';
const legendFieldValue = feature.get(legendFieldKey);
if (legendFieldValue != null) {
const valueMap =
fieldIndexMap.get(legendFieldKey) || new Map<string, Feature[]>();
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)) || [];
}
}