import { Map as OlMap } from "ol"; import WebGLVectorTileLayer from "ol/layer/WebGLVectorTile"; import VectorTileSource from "ol/source/VectorTile"; import { FlatStyleLike } from "ol/style/flat"; import { config } from "@/config/config"; import { VectorTileStyleSession, versionedPropertyCase, } from "@components/olmap/core/vectorTileStyleSession"; import { getAreaColor } from "./utils"; const JUNCTION_LAYER_VALUE = "junctions"; const RENDER_OWNER_KEY = "junction-area-render-owner"; export type JunctionAreaRenderPayload = { nodeAreaMap: Record; areaIds?: string[]; areaColors?: Record; }; type ApplyJunctionAreaRenderOptions = { propertyKey?: string; }; const DEFAULT_PROPERTY_KEY = "junction_area_render_index"; const getJunctionLayer = (map: OlMap) => map .getAllLayers() .find( (layer) => layer instanceof WebGLVectorTileLayer && layer.get("value") === JUNCTION_LAYER_VALUE, ) as WebGLVectorTileLayer | undefined; export const applyJunctionAreaRender = ( map: OlMap, payload: JunctionAreaRenderPayload, options: ApplyJunctionAreaRenderOptions = {}, ) => { const propertyKey = options.propertyKey ?? DEFAULT_PROPERTY_KEY; const junctionLayer = getJunctionLayer(map); if (!junctionLayer) { return () => {}; } const source = junctionLayer.getSource() as VectorTileSource | null; if (!source) { return () => {}; } const ownerId = `${propertyKey}-${Date.now().toString(36)}-${Math.random() .toString(36) .slice(2, 8)}`; const normalizedNodeAreaMap = Object.fromEntries( Object.entries(payload.nodeAreaMap ?? {}).map(([nodeId, areaId]) => [ String(nodeId), String(areaId), ]), ); const areaIds = ( payload.areaIds?.length ? payload.areaIds : Array.from(new Set(Object.values(normalizedNodeAreaMap))) ) .map(String) .filter(Boolean); if (Object.keys(normalizedNodeAreaMap).length === 0 || areaIds.length === 0) { junctionLayer.setStyle(config.MAP_DEFAULT_STYLE as FlatStyleLike); return () => {}; } const areaIdToIndex = new Map(); areaIds.forEach((areaId, index) => { areaIdToIndex.set(areaId, index + 1); }); const nodeAreaIndexMap = new Map(); Object.entries(normalizedNodeAreaMap).forEach(([nodeId, areaId]) => { const areaIndex = areaIdToIndex.get(areaId); if (areaIndex !== undefined) { nodeAreaIndexMap.set(nodeId, areaIndex); } }); const fillCases: any[] = []; areaIds.forEach((areaId, index) => { fillCases.push( ["==", ["get", propertyKey], index + 1], payload.areaColors?.[areaId] ?? getAreaColor(areaId), ); }); const defaultFillColor = String(config.MAP_DEFAULT_STYLE["circle-fill-color"]); const defaultStrokeColor = String( config.MAP_DEFAULT_STYLE["circle-stroke-color"], ); junctionLayer.set(RENDER_OWNER_KEY, ownerId); const session = new VectorTileStyleSession({ layer: junctionLayer, source, propertyKey, defaultStyle: config.MAP_DEFAULT_STYLE as FlatStyleLike, buildStyle: (statePropertyKey, versionKey, version) => ({ ...config.MAP_DEFAULT_STYLE, "circle-fill-color": versionedPropertyCase( statePropertyKey, versionKey, version, fillCases, defaultFillColor, ), "circle-stroke-color": versionedPropertyCase( statePropertyKey, versionKey, version, fillCases, defaultStrokeColor, ), }) as FlatStyleLike, }); session.commit(nodeAreaIndexMap); return () => { session.dispose(); if (junctionLayer.get(RENDER_OWNER_KEY) === ownerId) { junctionLayer.unset(RENDER_OWNER_KEY, true); junctionLayer.setStyle(config.MAP_DEFAULT_STYLE as FlatStyleLike); } }; };