import { GEOSERVER_WORKSPACE, MAP_URL } from "./geoserver-config"; import { GEOSERVER_WATER_NETWORK_SOURCE_IDS, SOURCE_LAYERS, SUPPLY_LAYER_CATALOG, type GeoServerWaterNetworkSourceId } from "./sources"; type GeoServerSupplyLayerCatalogItem = Extract< (typeof SUPPLY_LAYER_CATALOG)[number], { sourceLayer: string } >; export type DataDependentGeoServerSourceId = Extract< GeoServerSupplyLayerCatalogItem, { availability: "probe" } >["id"]; export const DATA_DEPENDENT_GEOSERVER_SOURCE_IDS: DataDependentGeoServerSourceId[] = SUPPLY_LAYER_CATALOG.filter( (layer): layer is Extract => layer.sourceLayer !== undefined && layer.availability === "probe" ).map((layer) => layer.id); export type GeoServerLayerAvailability = { availableSourceIds: GeoServerWaterNetworkSourceId[]; emptySourceIds: DataDependentGeoServerSourceId[]; failedSourceIds: DataDependentGeoServerSourceId[]; }; export const REQUIRED_GEOSERVER_SOURCE_IDS: GeoServerWaterNetworkSourceId[] = SUPPLY_LAYER_CATALOG.filter( (layer): layer is Extract => layer.sourceLayer !== undefined && layer.availability === "required" ).map((layer) => layer.id); export function createGeoServerLayerHitsUrl(sourceId: DataDependentGeoServerSourceId) { const params = new URLSearchParams({ service: "WFS", version: "2.0.0", request: "GetFeature", typeNames: `${GEOSERVER_WORKSPACE}:${SOURCE_LAYERS[sourceId]}`, resultType: "hits" }); return new URL(`${MAP_URL}/${GEOSERVER_WORKSPACE}/ows?${params.toString()}`); } export async function fetchGeoServerLayerFeatureCount( sourceId: DataDependentGeoServerSourceId, signal?: AbortSignal ) { const response = await fetch(createGeoServerLayerHitsUrl(sourceId), { headers: { Accept: "application/xml, text/xml" }, signal }); const payload = await response.text(); if (!response.ok) { if (/Feature type\s+\S+\s+unknown/i.test(payload)) return 0; throw new Error(`GeoServer ${sourceId} 图层探测失败:HTTP ${response.status}`); } const match = payload.match(/\bnumberMatched\s*=\s*["'](\d+|unknown)["']/i); if (!match) { throw new Error(`GeoServer ${sourceId} 图层探测响应缺少 numberMatched`); } if (match[1].toLowerCase() === "unknown") return 1; const count = Number(match[1]); if (!Number.isSafeInteger(count) || count < 0) { throw new Error(`GeoServer ${sourceId} 图层探测返回无效数量`); } return count; } export async function resolveGeoServerLayerAvailability( signal?: AbortSignal ): Promise { const results = await Promise.allSettled( DATA_DEPENDENT_GEOSERVER_SOURCE_IDS.map(async (sourceId) => ({ sourceId, count: await fetchGeoServerLayerFeatureCount(sourceId, signal) })) ); const availableOptionalSourceIds: DataDependentGeoServerSourceId[] = []; const emptySourceIds: DataDependentGeoServerSourceId[] = []; const failedSourceIds: DataDependentGeoServerSourceId[] = []; results.forEach((result, index) => { const sourceId = DATA_DEPENDENT_GEOSERVER_SOURCE_IDS[index]; if (result.status === "rejected") { failedSourceIds.push(sourceId); } else if (result.value.count > 0) { availableOptionalSourceIds.push(sourceId); } else { emptySourceIds.push(sourceId); } }); const availableSourceIdSet = new Set([ ...REQUIRED_GEOSERVER_SOURCE_IDS, ...availableOptionalSourceIds ]); return { availableSourceIds: GEOSERVER_WATER_NETWORK_SOURCE_IDS.filter((sourceId) => availableSourceIdSet.has(sourceId) ), emptySourceIds, failedSourceIds }; }