MIF_E31221222/sigap-website/app/_components/map/layers/district-extrusion-layer.tsx

195 lines
7.9 KiB
TypeScript

"use client"
import { useEffect, useRef } from "react"
import { useMap } from "react-map-gl/mapbox"
import { CRIME_RATE_COLORS } from "@/app/_utils/const/map"
import { $Enums } from "@prisma/client"
export interface DistrictExtrusionLayerProps {
visible?: boolean
focusedDistrictId: string | null
crimeDataByDistrict: Record<string, { number_of_crime?: number; level?: $Enums.crime_rates }>
tilesetId: string
beforeId?: string
}
export default function DistrictExtrusionLayer({
visible = true,
focusedDistrictId,
crimeDataByDistrict,
tilesetId,
beforeId,
}: DistrictExtrusionLayerProps) {
const { current: map } = useMap()
const layersAdded = useRef(false)
useEffect(() => {
if (!map || !visible) return
const onStyleLoad = () => {
if (!map) return
try {
// Make sure the districts source exists
if (!map.getMap().getSource("districts")) {
map.getMap().addSource("districts", {
type: "vector",
url: `mapbox://${tilesetId}`,
})
}
// Get the first symbol layer
let firstSymbolId = beforeId
if (!firstSymbolId) {
const layers = map.getStyle().layers
for (const layer of layers) {
if (layer.type === "symbol") {
firstSymbolId = layer.id
break
}
}
}
if (!map.getMap().getLayer("district-extrusion")) {
map.getMap().addLayer(
{
id: "district-extrusion",
type: "fill-extrusion",
source: "districts",
"source-layer": "Districts",
paint: {
"fill-extrusion-color": [
"case",
["has", "kode_kec"],
[
"match",
["get", "kode_kec"],
focusedDistrictId || "",
crimeDataByDistrict[focusedDistrictId || ""]?.level === "low"
? CRIME_RATE_COLORS.low
: crimeDataByDistrict[focusedDistrictId || ""]?.level === "medium"
? CRIME_RATE_COLORS.medium
: crimeDataByDistrict[focusedDistrictId || ""]?.level === "high"
? CRIME_RATE_COLORS.high
: CRIME_RATE_COLORS.default,
"transparent",
],
"transparent",
],
"fill-extrusion-height": [
"case",
["has", "kode_kec"],
["match", ["get", "kode_kec"], focusedDistrictId || "", 500, 0],
0,
],
"fill-extrusion-base": 0,
"fill-extrusion-opacity": 0.8,
},
filter: ["==", ["get", "kode_kec"], focusedDistrictId || ""],
},
firstSymbolId,
)
layersAdded.current = true
}
} catch (error) {
console.error("Error adding extrusion layer:", error)
}
}
if (map.isStyleLoaded()) {
onStyleLoad()
} else {
map.once("style.load", onStyleLoad)
}
}, [map, visible, tilesetId, beforeId])
useEffect(() => {
if (!map || !layersAdded.current) return
try {
if (map.getMap().getLayer("district-extrusion")) {
map.getMap().setFilter("district-extrusion", ["==", ["get", "kode_kec"], focusedDistrictId || ""])
map.getMap().setPaintProperty("district-extrusion", "fill-extrusion-color", [
"case",
["has", "kode_kec"],
[
"match",
["get", "kode_kec"],
focusedDistrictId || "",
crimeDataByDistrict[focusedDistrictId || ""]?.level === "low"
? CRIME_RATE_COLORS.low
: crimeDataByDistrict[focusedDistrictId || ""]?.level === "medium"
? CRIME_RATE_COLORS.medium
: crimeDataByDistrict[focusedDistrictId || ""]?.level === "high"
? CRIME_RATE_COLORS.high
: CRIME_RATE_COLORS.default,
"transparent",
],
"transparent",
])
if (focusedDistrictId) {
const startHeight = 0
const targetHeight = 800
const duration = 700
const startTime = performance.now()
const animateHeight = (currentTime: number) => {
const elapsed = currentTime - startTime
const progress = Math.min(elapsed / duration, 1)
const easedProgress = progress * (2 - progress)
const currentHeight = startHeight + (targetHeight - startHeight) * easedProgress
map
.getMap()
.setPaintProperty("district-extrusion", "fill-extrusion-height", [
"case",
["has", "kode_kec"],
["match", ["get", "kode_kec"], focusedDistrictId, currentHeight, 0],
0,
])
if (progress < 1) {
requestAnimationFrame(animateHeight)
}
}
requestAnimationFrame(animateHeight)
} else {
const startHeight = 800
const targetHeight = 0
const duration = 500
const startTime = performance.now()
const animateHeightDown = (currentTime: number) => {
const elapsed = currentTime - startTime
const progress = Math.min(elapsed / duration, 1)
const easedProgress = progress * (2 - progress)
const currentHeight = startHeight + (targetHeight - startHeight) * easedProgress
map
.getMap()
.setPaintProperty("district-extrusion", "fill-extrusion-height", [
"case",
["has", "kode_kec"],
["match", ["get", "kode_kec"], "", currentHeight, 0],
0,
])
if (progress < 1) {
requestAnimationFrame(animateHeightDown)
}
}
requestAnimationFrame(animateHeightDown)
}
}
} catch (error) {
console.error("Error updating extrusion layer:", error)
}
}, [map, focusedDistrictId, crimeDataByDistrict])
return null
}