refactor: streamline map constants and enhance animation handling in layers
This commit is contained in:
parent
6c96c1140c
commit
8a1582c994
|
@ -88,29 +88,29 @@ export default function DistrictFillLineLayer({
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait a moment before selecting the new district to ensure clean transitions
|
// Wait a moment before selecting the new district to ensure clean transitions
|
||||||
setTimeout(() => {
|
// setTimeout(() => {
|
||||||
const district = processDistrictFeature(feature, e, districtId, crimeDataByDistrict, crimes, year, month)
|
// const district = processDistrictFeature(feature, e, districtId, crimeDataByDistrict, crimes, year, month)
|
||||||
if (!district || !setFocusedDistrictId) return
|
// if (!district || !setFocusedDistrictId) return
|
||||||
|
|
||||||
setFocusedDistrictId(district.id)
|
// setFocusedDistrictId(district.id)
|
||||||
|
|
||||||
// Fly to the new district
|
// // Fly to the new district
|
||||||
map.flyTo({
|
// map.flyTo({
|
||||||
center: [district.longitude, district.latitude],
|
// center: [district.longitude, district.latitude],
|
||||||
zoom: 12.5,
|
// zoom: 12.5,
|
||||||
pitch: 75,
|
// pitch: 75,
|
||||||
bearing: 0,
|
// bearing: 0,
|
||||||
duration: 1500,
|
// duration: 1500,
|
||||||
easing: (t) => t * (2 - t), // easeOutQuad
|
// easing: (t) => t * (2 - t), // easeOutQuad
|
||||||
})
|
// })
|
||||||
|
|
||||||
// Use onDistrictClick if available, otherwise fall back to onClick
|
// // Use onDistrictClick if available, otherwise fall back to onClick
|
||||||
if (onDistrictClick) {
|
// if (onDistrictClick) {
|
||||||
onDistrictClick(district)
|
// onDistrictClick(district)
|
||||||
} else if (onClick) {
|
// } else if (onClick) {
|
||||||
onClick(district)
|
// onClick(district)
|
||||||
}
|
// }
|
||||||
}, 100)
|
// }, 100)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import { useState, useRef, useEffect, useCallback } from "react"
|
import { useState, useRef, useEffect, useCallback } from "react"
|
||||||
import { useMap } from "react-map-gl/mapbox"
|
import { useMap } from "react-map-gl/mapbox"
|
||||||
import { BASE_BEARING, BASE_PITCH, BASE_ZOOM, MAPBOX_TILESET_ID } from "@/app/_utils/const/map"
|
import { BASE_BEARING, BASE_DURATION, BASE_PITCH, BASE_ZOOM, MAPBOX_TILESET_ID } from "@/app/_utils/const/map"
|
||||||
import DistrictPopup from "../pop-up/district-popup"
|
import DistrictPopup from "../pop-up/district-popup"
|
||||||
import DistrictExtrusionLayer from "./district-extrusion-layer"
|
import DistrictExtrusionLayer from "./district-extrusion-layer"
|
||||||
import ClusterLayer from "./cluster-layer"
|
import ClusterLayer from "./cluster-layer"
|
||||||
|
@ -91,6 +91,7 @@ export default function Layers({
|
||||||
showEWS = true,
|
showEWS = true,
|
||||||
sourceType = "cbt",
|
sourceType = "cbt",
|
||||||
}: LayersProps) {
|
}: LayersProps) {
|
||||||
|
const animationRef = useRef<number | null>(null)
|
||||||
const { current: map } = useMap()
|
const { current: map } = useMap()
|
||||||
|
|
||||||
if (!map) {
|
if (!map) {
|
||||||
|
@ -148,7 +149,7 @@ export default function Layers({
|
||||||
zoom: BASE_ZOOM,
|
zoom: BASE_ZOOM,
|
||||||
pitch: BASE_PITCH,
|
pitch: BASE_PITCH,
|
||||||
bearing: BASE_BEARING,
|
bearing: BASE_BEARING,
|
||||||
duration: 1500,
|
duration: BASE_DURATION,
|
||||||
easing: (t) => t * (2 - t),
|
easing: (t) => t * (2 - t),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -166,10 +167,75 @@ export default function Layers({
|
||||||
}
|
}
|
||||||
}, [map, crimeDataByDistrict])
|
}, [map, crimeDataByDistrict])
|
||||||
|
|
||||||
|
|
||||||
|
const animateExtrusionDown = () => {
|
||||||
|
if (!map || !map.getLayer("district-extrusion") || !focusedDistrictId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (animationRef.current) {
|
||||||
|
cancelAnimationFrame(animationRef.current)
|
||||||
|
animationRef.current = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current height from the layer (default to 800 if not found)
|
||||||
|
let currentHeight = 800
|
||||||
|
|
||||||
|
try {
|
||||||
|
const paint = map.getPaintProperty("district-extrusion", "fill-extrusion-height")
|
||||||
|
if (Array.isArray(paint) && paint.length > 0) {
|
||||||
|
// Try to extract the current height from the expression
|
||||||
|
const idx = paint.findIndex((v) => v === focusedDistrictId)
|
||||||
|
if (idx !== -1 && typeof paint[idx + 1] === "number") {
|
||||||
|
currentHeight = paint[idx + 1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// fallback to default
|
||||||
|
}
|
||||||
|
|
||||||
|
const startHeight = currentHeight
|
||||||
|
const targetHeight = 0
|
||||||
|
const duration = 700
|
||||||
|
const startTime = performance.now()
|
||||||
|
|
||||||
|
const animate = (currentTime: number) => {
|
||||||
|
const elapsed = currentTime - startTime
|
||||||
|
const progress = Math.min(elapsed / duration, 1)
|
||||||
|
const easedProgress = progress * (2 - progress)
|
||||||
|
const newHeight = startHeight + (targetHeight - startHeight) * easedProgress
|
||||||
|
|
||||||
|
try {
|
||||||
|
map.getMap().setPaintProperty("district-extrusion", "fill-extrusion-height", [
|
||||||
|
"case",
|
||||||
|
["has", "kode_kec"],
|
||||||
|
["match", ["get", "kode_kec"], focusedDistrictId, newHeight, 0],
|
||||||
|
0,
|
||||||
|
])
|
||||||
|
|
||||||
|
if (progress < 1) {
|
||||||
|
animationRef.current = requestAnimationFrame(animate)
|
||||||
|
} else {
|
||||||
|
animationRef.current = null
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
if (animationRef.current) {
|
||||||
|
cancelAnimationFrame(animationRef.current)
|
||||||
|
animationRef.current = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
animationRef.current = requestAnimationFrame(animate)
|
||||||
|
}
|
||||||
|
|
||||||
const handleCloseDistrictPopup = useCallback(() => {
|
const handleCloseDistrictPopup = useCallback(() => {
|
||||||
console.log("Closing district popup")
|
console.log("Closing district popup")
|
||||||
|
|
||||||
|
animateExtrusionDown()
|
||||||
handlePopupClose()
|
handlePopupClose()
|
||||||
}, [handlePopupClose])
|
}, [handlePopupClose, animateExtrusionDown])
|
||||||
|
|
||||||
const handleCloseIncidentPopup = useCallback(() => {
|
const handleCloseIncidentPopup = useCallback(() => {
|
||||||
console.log("Closing incident popup")
|
console.log("Closing incident popup")
|
||||||
|
@ -197,10 +263,10 @@ export default function Layers({
|
||||||
if (map && feature.longitude && feature.latitude) {
|
if (map && feature.longitude && feature.latitude) {
|
||||||
map.flyTo({
|
map.flyTo({
|
||||||
center: [feature.longitude, feature.latitude],
|
center: [feature.longitude, feature.latitude],
|
||||||
zoom: 12,
|
zoom: 12.5,
|
||||||
pitch: 45,
|
pitch: 60,
|
||||||
bearing: 0,
|
bearing: 0,
|
||||||
duration: 1500,
|
duration: BASE_DURATION,
|
||||||
easing: (t) => t * (2 - t),
|
easing: (t) => t * (2 - t),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -47,14 +47,14 @@ export default function DistrictPopup({
|
||||||
const [activeTab, setActiveTab] = useState("overview")
|
const [activeTab, setActiveTab] = useState("overview")
|
||||||
|
|
||||||
// Add debug log when the component is rendered
|
// Add debug log when the component is rendered
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
console.log("DistrictPopup mounted:", {
|
// console.log("DistrictPopup mounted:", {
|
||||||
district: district.name,
|
// district: district.name,
|
||||||
coords: [longitude, latitude],
|
// coords: [longitude, latitude],
|
||||||
year,
|
// year,
|
||||||
month
|
// month
|
||||||
});
|
// });
|
||||||
}, [district, longitude, latitude, year, month]);
|
// }, [district, longitude, latitude, year, month]);
|
||||||
|
|
||||||
// Extract all crime incidents from the district data and apply filtering if needed
|
// Extract all crime incidents from the district data and apply filtering if needed
|
||||||
const allCrimeIncidents = useMemo(() => {
|
const allCrimeIncidents = useMemo(() => {
|
||||||
|
@ -332,28 +332,6 @@ export default function DistrictPopup({
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Card>
|
</Card>
|
||||||
{/* Connection line */}
|
|
||||||
<div
|
|
||||||
className="absolute bottom-0 left-1/2 transform -translate-x-1/2 translate-y-full"
|
|
||||||
style={{
|
|
||||||
width: '2px',
|
|
||||||
height: '20px',
|
|
||||||
backgroundColor: 'red',
|
|
||||||
boxShadow: '0 0 4px rgba(0, 0, 0, 0.3)'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{/* Connection dot */}
|
|
||||||
<div
|
|
||||||
className="absolute bottom-0 left-1/2 transform -translate-x-1/2 translate-y-full"
|
|
||||||
style={{
|
|
||||||
width: '6px',
|
|
||||||
height: '6px',
|
|
||||||
backgroundColor: 'red',
|
|
||||||
borderRadius: '50%',
|
|
||||||
marginTop: '20px',
|
|
||||||
boxShadow: '0 0 4px rgba(0, 0, 0, 0.3)'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</Popup>
|
</Popup>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
export const BASE_ZOOM = 9.5; // Default zoom level for the map
|
|
||||||
export const BASE_PITCH = 0; // Default pitch for the map
|
export const MAPBOX_ACCESS_TOKEN = process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN;
|
||||||
export const BASE_BEARING = 0; // Default bearing for the map
|
export const MAPBOX_TILESET_ID = process.env.NEXT_PUBLIC_MAPBOX_TILESET_ID;
|
||||||
|
|
||||||
|
export const BASE_ZOOM = 9.5;
|
||||||
|
export const BASE_PITCH = 0;
|
||||||
|
export const BASE_BEARING = 0;
|
||||||
export const BASE_LATITUDE = -8.17; // Default latitude for the map center (Jember region)
|
export const BASE_LATITUDE = -8.17; // Default latitude for the map center (Jember region)
|
||||||
export const BASE_LONGITUDE = 113.65; // Default longitude for the map center (Jember region)
|
export const BASE_LONGITUDE = 113.65; // Default longitude for the map center (Jember region)
|
||||||
export const MAPBOX_ACCESS_TOKEN = process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN;
|
export const BASE_DURATION = 2000; // Default duration for map flyTo animation
|
||||||
export const MAPBOX_TILESET_ID = process.env.NEXT_PUBLIC_MAPBOX_TILESET_ID; // Replace with your tileset ID
|
export const PITCH_3D = 60; // Default pitch for 3D view
|
||||||
|
export const ZOOM_3D = 12.5; // Default zoom for 3D view
|
||||||
|
|
||||||
|
|
||||||
// export const CRIME_RATE_COLORS = {
|
// export const CRIME_RATE_COLORS = {
|
||||||
// low: '#FFB74D', // green
|
// low: '#FFB74D', // green
|
||||||
|
|
Loading…
Reference in New Issue