125 lines
4.0 KiB
TypeScript
125 lines
4.0 KiB
TypeScript
import { ChoroplethIcon, MapIcon } from "@/shared/components/icons";
|
|
import { ActiveLayer } from "../../../../state/active-layers";
|
|
import { setLayerModeAtom } from "../../../../state/active-layers";
|
|
import { useAtom, useSetAtom } from "jotai";
|
|
import { constructWfsUrl } from "@/shared/utils/wms";
|
|
import colorScaleApi from "@/shared/services/color-scale";
|
|
import { appConfig } from "@/shared/config/app-config";
|
|
import { useQuery } from "@tanstack/react-query";
|
|
import { mergeDataToGeoJSON } from "@/shared/utils/mege-data-geojson";
|
|
import jatimGeojson from "@/public/jatim.json";
|
|
import L from "leaflet";
|
|
import { mapAtom } from "@/app/(modules)/maps/state/map";
|
|
import { leafletLayerInstancesAtom } from "@/app/(modules)/maps/state/leaflet-layer-instances";
|
|
import { useState } from "react";
|
|
|
|
interface ChoropleControlProps {
|
|
layer: ActiveLayer;
|
|
}
|
|
|
|
// Add a new atom or use local state to store the original (basic) layer instance
|
|
const basicLayerInstances = new Map<string, L.Layer>();
|
|
|
|
export default function ChoroplethControl({ layer }: ChoropleControlProps) {
|
|
const setLayerMode = useSetAtom(setLayerModeAtom);
|
|
const [map] = useAtom(mapAtom);
|
|
const sourceUrl = constructWfsUrl(layer.layer) as string;
|
|
const [layerInstances, setLayerInstances] = useAtom(
|
|
leafletLayerInstancesAtom
|
|
);
|
|
|
|
// Loading state for choropleth
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
|
|
const { data: colorScale, isFetching } = useQuery({
|
|
queryKey: ["color-scale", layer.id],
|
|
queryFn: () =>
|
|
colorScaleApi
|
|
.getColorScale({
|
|
source_url: sourceUrl,
|
|
boundary_file_id: appConfig.boundaryFileId,
|
|
})
|
|
.then((res) => res),
|
|
staleTime: 5000,
|
|
});
|
|
|
|
const changeToColorpleth = async () => {
|
|
if (!colorScale?.data || !map) return;
|
|
setIsLoading(true);
|
|
|
|
const existingLayer = layerInstances.get(layer.id);
|
|
|
|
// Save the basic layer instance if not already saved
|
|
if (existingLayer && !basicLayerInstances.has(layer.id)) {
|
|
basicLayerInstances.set(layer.id, existingLayer);
|
|
}
|
|
|
|
if (existingLayer) map.removeLayer(existingLayer);
|
|
|
|
const enrichedGeoJSON = mergeDataToGeoJSON(
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
jatimGeojson as any,
|
|
colorScale.data
|
|
);
|
|
|
|
const leafletLayer = L.geoJSON(enrichedGeoJSON, {
|
|
style: (feature) => ({
|
|
fillColor: feature?.properties?.color || "#cccccc",
|
|
weight: 2,
|
|
color: "#333333",
|
|
fillOpacity: 0.7,
|
|
}),
|
|
onEachFeature: (feature, layerInstance) => {
|
|
const props = feature.properties;
|
|
layerInstance.bindPopup(
|
|
`<strong>${props.WADMKK}, ${props.WADMPR}</strong><br>Value: ${props.value}`
|
|
);
|
|
},
|
|
});
|
|
|
|
leafletLayer.addTo(map);
|
|
const newMap = new Map(layerInstances);
|
|
newMap.set(layer.id, leafletLayer);
|
|
|
|
setLayerInstances(newMap);
|
|
setIsLoading(false);
|
|
};
|
|
|
|
const restoreBasicLayer = () => {
|
|
if (!map) return;
|
|
|
|
const currentLayer = layerInstances.get(layer.id);
|
|
if (currentLayer) map?.removeLayer(currentLayer);
|
|
|
|
const basicLayer = basicLayerInstances.get(layer.id);
|
|
if (basicLayer) {
|
|
basicLayer.addTo(map);
|
|
const newMap = new Map(layerInstances);
|
|
newMap.set(layer.id, basicLayer);
|
|
setLayerInstances(newMap);
|
|
}
|
|
setLayerMode({ layerId: layer.id, mode: "basic" });
|
|
};
|
|
|
|
return (
|
|
<div className="bg-zinc-100 p-1 grid grid-cols-2 space-x-1">
|
|
<button
|
|
className="bg-white cursor-pointer rounded-md flex flex-col gap-1 justify-center items-center text-sm text-zinc-950 py-2 px-3"
|
|
onClick={restoreBasicLayer}
|
|
disabled={isLoading || isFetching}
|
|
>
|
|
<MapIcon size={32} />
|
|
Basic
|
|
</button>
|
|
<button
|
|
className="bg-white cursor-pointer rounded-md flex flex-col gap-1 justify-center items-center text-sm text-zinc-950 py-2 px-3"
|
|
onClick={changeToColorpleth}
|
|
disabled={isLoading || isFetching}
|
|
>
|
|
<ChoroplethIcon size={32} />
|
|
{isLoading || isFetching ? "Loading..." : "Choropleth"}
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|