satupeta-main/shared/utils/wms.ts

268 lines
7.8 KiB
TypeScript
Raw Normal View History

2026-01-27 02:31:12 +00:00
import { WMSLayerConfig } from "@/app/(modules)/maps/state/active-layers";
import { LatLngBoundsExpression } from "leaflet";
interface GetLegendUrlOptions {
baseUrl: string;
layerName: string;
format?: string;
width?: number;
height?: number;
version?: string;
sld_version?: string;
}
export const parseWmsUrl = (url: string | undefined | null) => {
if (!url) return null;
try {
const [rawBaseUrl] = url.split("?");
const parsedUrl = new URL(url);
const params = parsedUrl.searchParams;
const bbox = params.get("bbox");
return {
baseUrl: rawBaseUrl,
params: {
service: params.get("service") ?? "WMS",
request: "GetMap",
version: params.get("version") ?? "1.1.1",
layers: params.get("layers") ?? "",
styles: params.get("styles") ?? "",
format: params.get("format") ?? "image/png",
bounds: bbox ? parseBBoxToBounds(bbox) : null,
transparent: "true",
srs: params.get("srs") ?? params.get("crs") ?? "EPSG:4326",
},
};
} catch (error) {
console.error("Invalid WMS URL:", error);
return null;
}
};
export const constructWfsUrl = (wmsConfig: WMSLayerConfig) => {
const { url, layers } = wmsConfig;
if (!url) return;
// Replace 'wms' with 'ows' in the base URL
const baseUrl = url.replace("/wms", "/ows");
// Construct query parameters
const params = {
service: "WFS",
version: "1.0.0",
request: "GetFeature",
typeName: layers,
maxFeatures: 50,
outputFormat: "application/json",
};
const queryString = Object.entries(params)
.filter(([value]) => value !== undefined)
.map(([key, value]) => `${key}=${encodeURIComponent(String(value))}`)
.join("&");
return `${baseUrl}?${queryString}`;
};
export const getWmsTileLayerUrl = (url: string | undefined | null) => {
const parsed = parseWmsUrl(url);
if (!parsed) return null;
const { baseUrl, params } = parsed;
const version = params.version;
const crsParamKey = version === "1.3.0" ? "crs" : "srs";
const wmsParams = new URLSearchParams({
service: params.service,
request: params.request,
version: version,
layers: params.layers,
styles: params.styles,
format: "image/png",
transparent: params.transparent,
[crsParamKey]: params.srs,
});
return `${baseUrl}?${wmsParams.toString()}`;
};
export function parseBBoxToBounds(
bboxString: string
): LatLngBoundsExpression | null {
try {
if (!bboxString) return null;
const [minX, minY, maxX, maxY] = bboxString.split(",").map(Number);
if ([minX, minY, maxX, maxY].some(isNaN)) return null;
const bounds: LatLngBoundsExpression = [
[minY, minX],
[maxY, maxX],
];
return bounds;
} catch (e) {
console.error("Invalid bbox input:", e);
return null;
}
}
export const getLegendUrl = ({
baseUrl,
layerName,
format = "image/png",
width,
height,
version = "1.1.1",
sld_version = "1.1.0",
}: GetLegendUrlOptions): string => {
const url = new URL(baseUrl);
url.searchParams.set("SERVICE", "WMS");
url.searchParams.set("VERSION", version);
url.searchParams.set("REQUEST", "GetLegendGraphic");
url.searchParams.set("FORMAT", format);
url.searchParams.set("LAYER", layerName);
url.searchParams.set("SLD_VERSION", sld_version);
if (width) url.searchParams.set("WIDTH", width.toString());
if (height) url.searchParams.set("HEIGHT", height.toString());
return url.toString();
};
export const getWmsLayerBounds = async (
baseUrl: string,
layerName: string
): Promise<L.LatLngBoundsExpression | undefined> => {
try {
const capabilitiesUrl =
`${baseUrl}?` +
new URLSearchParams({
service: "WMS",
version: "1.3.0",
request: "GetCapabilities",
});
const response = await fetch(capabilitiesUrl);
if (!response.ok) return undefined;
const text = await response.text();
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(text, "text/xml");
// Find the requested layer in capabilities
const layerElements = xmlDoc.getElementsByTagName("Layer");
for (const element of layerElements) {
const nameElement = element.getElementsByTagName("Name")[0];
if (nameElement && nameElement.textContent === layerName) {
const bboxElement =
element.getElementsByTagName("BoundingBox")[0] ||
element.getElementsByTagName("EX_GeographicBoundingBox")[0];
if (bboxElement) {
// Extract bounds based on the bbox type
if (bboxElement.tagName === "BoundingBox") {
const minx = parseFloat(bboxElement.getAttribute("minx") ?? "0");
const miny = parseFloat(bboxElement.getAttribute("miny") ?? "0");
const maxx = parseFloat(bboxElement.getAttribute("maxx") ?? "0");
const maxy = parseFloat(bboxElement.getAttribute("maxy") ?? "0");
return [
[miny, minx],
[maxy, maxx],
];
} else {
const westBound = parseFloat(
bboxElement.getElementsByTagName("westBoundLongitude")[0]
?.textContent ?? "0"
);
const southBound = parseFloat(
bboxElement.getElementsByTagName("southBoundLatitude")[0]
?.textContent ?? "0"
);
const eastBound = parseFloat(
bboxElement.getElementsByTagName("eastBoundLongitude")[0]
?.textContent ?? "0"
);
const northBound = parseFloat(
bboxElement.getElementsByTagName("northBoundLatitude")[0]
?.textContent ?? "0"
);
return [
[southBound, westBound],
[northBound, eastBound],
];
}
}
}
}
return undefined;
} catch (error) {
console.error("Error fetching layer bounds:", error);
return undefined;
}
};
export const getWmsToGeoJsonUrl = (wmsUrl: string | undefined | null) => {
if (!wmsUrl) return null;
const parsedWms = parseWmsUrl(wmsUrl);
if (!parsedWms) return null;
const { baseUrl, params } = parsedWms;
const downloadUrl = new URL(baseUrl);
// Set parameters for GeoJSON download
downloadUrl.searchParams.set("service", "WFS");
downloadUrl.searchParams.set("version", "1.0.0");
downloadUrl.searchParams.set("request", "GetFeature");
downloadUrl.searchParams.set("typeName", params.layers);
downloadUrl.searchParams.set("outputFormat", "application/json");
downloadUrl.searchParams.set("srsName", params.srs);
return downloadUrl.toString();
};
export const getWmsToShpUrl = (wmsUrl: string | undefined | null) => {
if (!wmsUrl) return null;
const parsedWms = parseWmsUrl(wmsUrl);
if (!parsedWms) return null;
const { baseUrl, params } = parsedWms;
const downloadUrl = new URL(baseUrl);
downloadUrl.searchParams.set("service", "WFS");
downloadUrl.searchParams.set("version", "1.0.0");
downloadUrl.searchParams.set("request", "GetFeature");
downloadUrl.searchParams.set("typeName", params.layers);
downloadUrl.searchParams.set("outputFormat", "SHAPE-ZIP");
downloadUrl.searchParams.set("srsName", params.srs);
return downloadUrl.toString();
};
export const getWmsToKmlUrl = (wmsUrl: string | undefined | null) => {
if (!wmsUrl) return null;
const parsedWms = parseWmsUrl(wmsUrl);
if (!parsedWms) return null;
const { baseUrl, params } = parsedWms;
const downloadUrl = new URL(baseUrl);
downloadUrl.searchParams.set("service", "WFS");
downloadUrl.searchParams.set("version", "1.0.0");
downloadUrl.searchParams.set("request", "GetFeature");
downloadUrl.searchParams.set("typeName", params.layers);
downloadUrl.searchParams.set(
"outputFormat",
"application/vnd.google-earth.kml+xml"
);
downloadUrl.searchParams.set("srsName", params.srs);
return downloadUrl.toString();
};