// app/admin/upload/_components/map/StylePreview.tsx "use client"; import React, { useEffect, useRef } from "react"; import Map from "ol/Map"; import View from "ol/View"; import VectorLayer from "ol/layer/Vector"; import VectorSource from "ol/source/Vector"; import TileLayer from "ol/layer/Tile"; import OSM from "ol/source/OSM"; import WKT from "ol/format/WKT"; import Feature from "ol/Feature"; import Style from "ol/style/Style"; import Fill from "ol/style/Fill"; import Stroke from "ol/style/Stroke"; import CircleStyle from "ol/style/Circle"; import { defaults as defaultControls } from 'ol/control'; // Import Geostyler dynamic/client side only import SldStyleParser from "geostyler-sld-parser"; import OlStyleParser from "geostyler-openlayers-parser"; // ... (Kode helper parseWKT, normalizeKey, createFeatures sama seperti sebelumnya) ... const wkt = new WKT(); function parseWKT(str: string) { try { return wkt.readGeometry(str, { dataProjection: "EPSG:4326", featureProjection: "EPSG:3857", }); } catch (e) { return null; } } function normalizeKey(key: string) { return key.replace(/[^a-zA-Z0-9_]/g, "_"); } function createFeatures(data: any[]) { if (!data) return []; return data.map((row) => { const geometry = parseWKT(row.geometry); if (!geometry) return null; // Handle invalid geometry const feat = new Feature(); Object.entries(row).forEach(([key, value]) => { if (key === "geometry") return; feat.set(key, value); const normalized = normalizeKey(key).toUpperCase(); if (normalized !== key) { feat.set(normalized, value); } }); feat.setGeometry(geometry); return feat; }).filter((f): f is Feature => f !== null); } const defaultStyle = new Style({ image: new CircleStyle({ radius: 4, fill: new Fill({ color: "#3388ff" }), stroke: new Stroke({ color: "#333", width: 1 }), }), stroke: new Stroke({ color: "#3388ff", width: 2 }), fill: new Fill({ color: "rgba(51,136,255,0.5)" }), }); const SpatialStylePreview = ({ data, styleConfig }: { data: any[], styleConfig: any }) => { const mapRef = useRef(null); const mapObj = useRef(null); const vectorLayer = useRef | null>(null); useEffect(() => { if (!mapRef.current || !data) return; if (mapObj.current) mapObj.current.setTarget(undefined); const features = createFeatures(data); const vectorSource = new VectorSource({ features }); vectorLayer.current = new VectorLayer({ source: vectorSource, style: defaultStyle, }); mapObj.current = new Map({ target: mapRef.current, controls: defaultControls({ attribution: false, zoom: true }), layers: [ new TileLayer({ source: new OSM() }), vectorLayer.current, ], view: new View({ center: [12600000, -830000], // Sesuaikan center default Indonesia zoom: 5, }), }); // Auto zoom to extent const extent = vectorSource.getExtent(); if (extent && extent[0] !== Infinity) { mapObj.current.getView().fit(extent, { padding: [20, 20, 20, 20] }); } return () => { if (mapObj.current) mapObj.current.setTarget(undefined); }; }, [data]); // Apply SLD Logic useEffect(() => { if (!vectorLayer.current) return; if (!styleConfig || styleConfig.styleType !== "sld") { vectorLayer.current.setStyle(defaultStyle); return; } const applySLD = async () => { try { const sldParser = new SldStyleParser(); const olParser = new OlStyleParser(); const sldResult = await sldParser.readStyle(styleConfig.sldContent); if (!sldResult.output) throw new Error("Empty style"); const olResult = await olParser.writeStyle(sldResult.output); const olStyle = olResult.output; if (typeof olStyle === "function") { vectorLayer.current?.setStyle((f, r) => olStyle(f, r)); } else { vectorLayer.current?.setStyle(olStyle); } vectorLayer.current?.getSource()?.changed(); } catch (err) { console.warn("SLD parsing failed", err); vectorLayer.current?.setStyle(defaultStyle); } }; applySLD(); }, [styleConfig]); return
; }; export default SpatialStylePreview;