272 lines
9.5 KiB
TypeScript
272 lines
9.5 KiB
TypeScript
"use client"
|
|
|
|
import { BASE_BEARING, BASE_DURATION, BASE_PITCH, BASE_ZOOM } from "@/app/_utils/const/map"
|
|
import { createFillColorExpression, getFillOpacity, processDistrictFeature } from "@/app/_utils/map/common"
|
|
import type { IDistrictLayerProps } from "@/app/_utils/types/map"
|
|
import { useEffect } from "react"
|
|
|
|
export default function DistrictFillLineLayer({
|
|
visible = true,
|
|
map,
|
|
onClick,
|
|
onDistrictClick,
|
|
year,
|
|
month,
|
|
filterCategory = "all",
|
|
crimes = [],
|
|
tilesetId,
|
|
focusedDistrictId,
|
|
setFocusedDistrictId,
|
|
crimeDataByDistrict,
|
|
showFill = true,
|
|
activeControl,
|
|
}: IDistrictLayerProps & { onDistrictClick?: (district: any) => void }) {
|
|
useEffect(() => {
|
|
if (!map || !visible) return
|
|
|
|
const handleDistrictClick = (e: any) => {
|
|
const possibleLayers = [
|
|
"unclustered-point",
|
|
"clusters",
|
|
"crime-points",
|
|
"units-points",
|
|
"incidents-points",
|
|
"timeline-markers",
|
|
"recent-incidents",
|
|
]
|
|
const availableLayers = possibleLayers.filter(layer => map.getLayer(layer))
|
|
const incidentFeatures = map.queryRenderedFeatures(e.point, {
|
|
layers: availableLayers,
|
|
})
|
|
|
|
if (incidentFeatures && incidentFeatures.length > 0) {
|
|
return
|
|
}
|
|
|
|
if (!map || !e.features || e.features.length === 0) return
|
|
|
|
const feature = e.features[0]
|
|
const districtId = feature.properties.kode_kec
|
|
|
|
if (focusedDistrictId === districtId) {
|
|
if (setFocusedDistrictId) {
|
|
setFocusedDistrictId(null)
|
|
}
|
|
|
|
map.easeTo({
|
|
zoom: BASE_ZOOM,
|
|
pitch: BASE_PITCH,
|
|
bearing: BASE_BEARING,
|
|
duration: BASE_DURATION,
|
|
easing: (t) => t * (2 - t),
|
|
})
|
|
|
|
const fillColorExpression = createFillColorExpression(null, crimeDataByDistrict)
|
|
map.setPaintProperty("district-fill", "fill-color", fillColorExpression as any)
|
|
|
|
if (map.getLayer("clusters")) {
|
|
map.setLayoutProperty("clusters", "visibility", "visible")
|
|
}
|
|
if (map.getLayer("unclustered-point")) {
|
|
map.setLayoutProperty("unclustered-point", "visibility", "visible")
|
|
}
|
|
|
|
return
|
|
} else if (focusedDistrictId) {
|
|
if (setFocusedDistrictId) {
|
|
setFocusedDistrictId(null)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
const district = processDistrictFeature(feature, e, districtId, crimeDataByDistrict, crimes, year, month)
|
|
|
|
if (!district) return
|
|
|
|
const focusedFillColorExpression = createFillColorExpression(district.id, crimeDataByDistrict)
|
|
map.setPaintProperty("district-fill", "fill-color", focusedFillColorExpression as any)
|
|
|
|
if (setFocusedDistrictId) {
|
|
setFocusedDistrictId(district.id)
|
|
}
|
|
|
|
if (map.getLayer("clusters")) {
|
|
map.setLayoutProperty("clusters", "visibility", "none")
|
|
}
|
|
if (map.getLayer("unclustered-point")) {
|
|
map.setLayoutProperty("unclustered-point", "visibility", "none")
|
|
}
|
|
|
|
if (onDistrictClick) {
|
|
onDistrictClick(district)
|
|
} else if (onClick) {
|
|
onClick(district)
|
|
}
|
|
}
|
|
|
|
const onStyleLoad = () => {
|
|
if (!map) return
|
|
|
|
try {
|
|
if (!map.getSource("districts")) {
|
|
const layers = map.getStyle().layers
|
|
let firstSymbolId: string | undefined
|
|
for (const layer of layers) {
|
|
if (layer.type === "symbol") {
|
|
firstSymbolId = layer.id
|
|
break
|
|
}
|
|
}
|
|
|
|
map.addSource("districts", {
|
|
type: "vector",
|
|
url: `mapbox://${tilesetId}`,
|
|
})
|
|
|
|
let fillColorExpression = createFillColorExpression(focusedDistrictId, crimeDataByDistrict)
|
|
if (
|
|
Array.isArray(fillColorExpression) &&
|
|
fillColorExpression[0] === "match" &&
|
|
fillColorExpression.length < 4
|
|
) {
|
|
fillColorExpression = [
|
|
"match",
|
|
["get", "kode_kec"],
|
|
"",
|
|
"#90a4ae",
|
|
"#90a4ae"
|
|
]
|
|
}
|
|
|
|
const fillOpacity = getFillOpacity(activeControl, showFill)
|
|
|
|
if (!map.getLayer("district-fill")) {
|
|
map.addLayer(
|
|
{
|
|
id: "district-fill",
|
|
type: "fill",
|
|
source: "districts",
|
|
"source-layer": "Districts",
|
|
paint: {
|
|
"fill-color": fillColorExpression as any,
|
|
"fill-opacity": fillOpacity,
|
|
},
|
|
},
|
|
firstSymbolId,
|
|
)
|
|
}
|
|
|
|
if (!map.getLayer("district-line")) {
|
|
map.addLayer(
|
|
{
|
|
id: "district-line",
|
|
type: "line",
|
|
source: "districts",
|
|
"source-layer": "Districts",
|
|
paint: {
|
|
"line-color": "#ffffff",
|
|
"line-width": 1,
|
|
"line-opacity": 0.5,
|
|
},
|
|
},
|
|
firstSymbolId,
|
|
)
|
|
}
|
|
|
|
map.on("mouseenter", "district-fill", () => {
|
|
map.getCanvas().style.cursor = "pointer"
|
|
})
|
|
|
|
map.on("mouseleave", "district-fill", () => {
|
|
map.getCanvas().style.cursor = ""
|
|
})
|
|
|
|
map.off("click", "district-fill", handleDistrictClick)
|
|
map.on("click", "district-fill", handleDistrictClick)
|
|
} else {
|
|
if (map.getLayer("district-fill")) {
|
|
let fillColorExpression = createFillColorExpression(focusedDistrictId, crimeDataByDistrict)
|
|
if (
|
|
Array.isArray(fillColorExpression) &&
|
|
fillColorExpression[0] === "match" &&
|
|
fillColorExpression.length < 4
|
|
) {
|
|
fillColorExpression = [
|
|
"match",
|
|
["get", "kode_kec"],
|
|
"",
|
|
"#90a4ae",
|
|
"#90a4ae"
|
|
]
|
|
}
|
|
map.setPaintProperty("district-fill", "fill-color", fillColorExpression as any)
|
|
|
|
const fillOpacity = getFillOpacity(activeControl, showFill)
|
|
map.setPaintProperty("district-fill", "fill-opacity", fillOpacity)
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error("Error adding district layers:", error)
|
|
}
|
|
}
|
|
|
|
if (map.isStyleLoaded()) {
|
|
onStyleLoad()
|
|
} else {
|
|
map.once("style.load", onStyleLoad)
|
|
}
|
|
|
|
return () => {
|
|
if (map) {
|
|
map.off("click", "district-fill", handleDistrictClick)
|
|
}
|
|
}
|
|
}, [
|
|
map,
|
|
visible,
|
|
tilesetId,
|
|
crimes,
|
|
filterCategory,
|
|
year,
|
|
month,
|
|
focusedDistrictId,
|
|
crimeDataByDistrict,
|
|
onClick,
|
|
onDistrictClick,
|
|
setFocusedDistrictId,
|
|
showFill,
|
|
activeControl,
|
|
])
|
|
|
|
useEffect(() => {
|
|
if (!map || !map.getLayer("district-fill")) return
|
|
|
|
try {
|
|
let fillColorExpression = createFillColorExpression(focusedDistrictId, crimeDataByDistrict)
|
|
if (
|
|
Array.isArray(fillColorExpression) &&
|
|
fillColorExpression[0] === "match" &&
|
|
fillColorExpression.length < 4
|
|
) {
|
|
fillColorExpression = [
|
|
"match",
|
|
["get", "kode_kec"],
|
|
"",
|
|
"#90a4ae",
|
|
"#90a4ae"
|
|
]
|
|
}
|
|
map.setPaintProperty("district-fill", "fill-color", fillColorExpression as any)
|
|
|
|
const fillOpacity = getFillOpacity(activeControl, showFill)
|
|
map.setPaintProperty("district-fill", "fill-opacity", fillOpacity)
|
|
} catch (error) {
|
|
console.error("Error updating district fill colors or opacity:", error)
|
|
}
|
|
}, [map, focusedDistrictId, crimeDataByDistrict, activeControl, showFill])
|
|
|
|
return null
|
|
}
|
|
|