satupeta-main/app/(modules)/admin/mapset-upload/_components/map/GeoPreview.tsx

101 lines
2.8 KiB
TypeScript

// app/admin/upload/_components/map/GeoPreview.tsx
"use client";
import React, { useEffect, useRef } from "react";
import Map from "ol/Map";
import View from "ol/View";
import { Tile as TileLayer, Vector as VectorLayer } from "ol/layer";
import { OSM } from "ol/source";
import VectorSource from "ol/source/Vector";
import WKT from "ol/format/WKT";
import { Circle as CircleStyle, Fill, Stroke, Style } from "ol/style";
import { fromLonLat } from "ol/proj";
const GeoPreview = ({ features }: { features: any[] }) => {
const mapRef = useRef<HTMLDivElement>(null);
const mapObj = useRef<Map | null>(null);
useEffect(() => {
if (!features || features.length === 0 || !mapRef.current) return;
// Bersihkan map lama jika ada re-render strict mode
if (mapObj.current) {
mapObj.current.setTarget(undefined);
}
const wktFormat = new WKT();
const vectorSource = new VectorSource();
features.forEach((item) => {
try {
const feature = wktFormat.readFeature(item.geometry, {
dataProjection: `EPSG:4326`,
featureProjection: "EPSG:3857",
});
vectorSource.addFeature(feature);
} catch (err) {
console.error("WKT parse error:", err);
}
});
const vectorLayer = new VectorLayer({
source: vectorSource,
style: (feature) => {
const type = feature.getGeometry()?.getType();
if (type === "Polygon" || type === "MultiPolygon") {
return new Style({
fill: new Fill({ color: "rgba(0, 153, 255, 0.4)" }),
stroke: new Stroke({ color: "#0099ff", width: 2 }),
});
}
if (type === "LineString" || type === "MultiLineString") {
return new Style({
stroke: new Stroke({ color: "#0099ff", width: 3 }),
});
}
if (type === "Point" || type === "MultiPoint") {
return new Style({
image: new CircleStyle({
radius: 6,
fill: new Fill({ color: "#0099ff" }),
stroke: new Stroke({ color: "#ffffff", width: 2 }),
}),
});
}
},
});
mapObj.current = new Map({
target: mapRef.current,
layers: [
new TileLayer({ source: new OSM() }),
vectorLayer,
],
view: new View({
center: fromLonLat([110, -6]),
zoom: 5,
}),
});
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);
};
}, [features]);
return (
<div
ref={mapRef}
style={{ width: "100%", height: "100%", minHeight: "400px", border: "1px solid #ccc", borderRadius: "8px" }}
/>
);
};
export default GeoPreview;