fix: fix kesalahan oengambilan properti distance pada units layer
This commit is contained in:
parent
58f033d0e4
commit
849b3c1ae3
|
@ -1,42 +1,42 @@
|
||||||
"use client";
|
// "use client";
|
||||||
|
|
||||||
import { useState, useEffect } from 'react';
|
// import { useState, useEffect } from 'react';
|
||||||
import mapboxgl from 'mapbox-gl';
|
// import mapboxgl from 'mapbox-gl';
|
||||||
import EWSAlertLayer from '../layers/ews-alert-layer';
|
// import EWSAlertLayer from '../layers/ews-alert-layer';
|
||||||
|
|
||||||
import { IIncidentLog } from '@/app/_utils/types/ews';
|
// import { IIncidentLog } from '@/app/_utils/types/ews';
|
||||||
|
|
||||||
interface AlertLayerContainerProps {
|
// interface AlertLayerContainerProps {
|
||||||
map: mapboxgl.Map | null;
|
// map: mapboxgl.Map | null;
|
||||||
activeLayer: string;
|
// activeLayer: string;
|
||||||
incidents: IIncidentLog[];
|
// incidents: IIncidentLog[];
|
||||||
onIncidentResolved?: (id: string) => void;
|
// onIncidentResolved?: (id: string) => void;
|
||||||
}
|
// }
|
||||||
|
|
||||||
export default function AlertLayerContainer({
|
// export default function AlertLayerContainer({
|
||||||
map,
|
// map,
|
||||||
activeLayer,
|
// activeLayer,
|
||||||
incidents,
|
// incidents,
|
||||||
onIncidentResolved,
|
// onIncidentResolved,
|
||||||
}: AlertLayerContainerProps) {
|
// }: AlertLayerContainerProps) {
|
||||||
const [ewsVisible, setEwsVisible] = useState(false);
|
// const [ewsVisible, setEwsVisible] = useState(false);
|
||||||
|
|
||||||
// Determine which layers to show based on activeLayer
|
// // Determine which layers to show based on activeLayer
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
const isAlertLayer = activeLayer === 'alerts';
|
// const isAlertLayer = activeLayer === 'alerts';
|
||||||
setEwsVisible(isAlertLayer);
|
// setEwsVisible(isAlertLayer);
|
||||||
}, [activeLayer]);
|
// }, [activeLayer]);
|
||||||
|
|
||||||
|
|
||||||
return (
|
// return (
|
||||||
<>
|
// <>
|
||||||
{/* EWS Alert Layer for emergency notifications */}
|
// {/* EWS Alert Layer for emergency notifications */}
|
||||||
<EWSAlertLayer
|
// <EWSAlertLayer
|
||||||
map={map}
|
// map={map}
|
||||||
incidents={incidents}
|
// incidents={incidents}
|
||||||
onIncidentResolved={onIncidentResolved}
|
// onIncidentResolved={onIncidentResolved}
|
||||||
visible={ewsVisible}
|
// visible={ewsVisible}
|
||||||
/>
|
// />
|
||||||
</>
|
// </>
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
|
@ -25,6 +25,11 @@ import Layers from "./layers/layers"
|
||||||
|
|
||||||
import { useGetUnitsQuery } from "@/app/(pages)/(admin)/dashboard/crime-management/units/_queries/queries"
|
import { useGetUnitsQuery } from "@/app/(pages)/(admin)/dashboard/crime-management/units/_queries/queries"
|
||||||
import { IDistrictFeature } from "@/app/_utils/types/map"
|
import { IDistrictFeature } from "@/app/_utils/types/map"
|
||||||
|
import EWSAlertLayer from "./layers/ews-alert-layer"
|
||||||
|
import { IIncidentLog } from "@/app/_utils/types/ews"
|
||||||
|
import { addMockIncident, getAllIncidents, resolveIncident } from "@/app/_utils/mock/ews-data"
|
||||||
|
import { useMap } from "react-map-gl/mapbox"
|
||||||
|
import PanicButtonDemo from "./controls/panic-button-demo"
|
||||||
|
|
||||||
export default function CrimeMap() {
|
export default function CrimeMap() {
|
||||||
const [sidebarCollapsed, setSidebarCollapsed] = useState(true)
|
const [sidebarCollapsed, setSidebarCollapsed] = useState(true)
|
||||||
|
@ -45,9 +50,16 @@ export default function CrimeMap() {
|
||||||
const [useAllYears, setUseAllYears] = useState<boolean>(false)
|
const [useAllYears, setUseAllYears] = useState<boolean>(false)
|
||||||
const [useAllMonths, setUseAllMonths] = useState<boolean>(false)
|
const [useAllMonths, setUseAllMonths] = useState<boolean>(false)
|
||||||
const [showEWS, setShowEWS] = useState<boolean>(true)
|
const [showEWS, setShowEWS] = useState<boolean>(true)
|
||||||
|
const [ewsIncidents, setEwsIncidents] = useState<IIncidentLog[]>([])
|
||||||
|
const [showPanicDemo, setShowPanicDemo] = useState(true)
|
||||||
|
const [displayPanicDemo, setDisplayPanicDemo] = useState(showEWS && showPanicDemo)
|
||||||
|
|
||||||
const mapContainerRef = useRef<HTMLDivElement>(null)
|
const mapContainerRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
const { current: mapInstance } = useMap()
|
||||||
|
|
||||||
|
const mapboxMap = mapInstance?.getMap() || null
|
||||||
|
|
||||||
const { isFullscreen } = useFullscreen(mapContainerRef)
|
const { isFullscreen } = useFullscreen(mapContainerRef)
|
||||||
|
|
||||||
const { data: availableSourceTypes, isLoading: isTypeLoading } = useGetCrimeTypes()
|
const { data: availableSourceTypes, isLoading: isTypeLoading } = useGetCrimeTypes()
|
||||||
|
@ -142,6 +154,29 @@ export default function CrimeMap() {
|
||||||
}
|
}
|
||||||
}, [selectedSourceType, activeControl]);
|
}, [selectedSourceType, activeControl]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setEwsIncidents(getAllIncidents())
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleTriggerAlert = useCallback((priority: "high" | "medium" | "low") => {
|
||||||
|
const newIncident = addMockIncident({ priority })
|
||||||
|
setEwsIncidents(getAllIncidents())
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleResolveIncident = useCallback((id: string) => {
|
||||||
|
resolveIncident(id)
|
||||||
|
setEwsIncidents(getAllIncidents())
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleResolveAllAlerts = useCallback(() => {
|
||||||
|
ewsIncidents.forEach((incident) => {
|
||||||
|
if (incident.status === "active") {
|
||||||
|
resolveIncident(incident.id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
setEwsIncidents(getAllIncidents())
|
||||||
|
}, [ewsIncidents])
|
||||||
|
|
||||||
const handleSourceTypeChange = useCallback((sourceType: string) => {
|
const handleSourceTypeChange = useCallback((sourceType: string) => {
|
||||||
setSelectedSourceType(sourceType);
|
setSelectedSourceType(sourceType);
|
||||||
|
|
||||||
|
@ -283,6 +318,7 @@ export default function CrimeMap() {
|
||||||
sourceType={selectedSourceType}
|
sourceType={selectedSourceType}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
||||||
{isFullscreen && (
|
{isFullscreen && (
|
||||||
<>
|
<>
|
||||||
<div className="absolute flex w-full p-2">
|
<div className="absolute flex w-full p-2">
|
||||||
|
@ -304,6 +340,19 @@ export default function CrimeMap() {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{mapboxMap && (
|
||||||
|
<EWSAlertLayer map={mapboxMap} incidents={ewsIncidents} onIncidentResolved={handleResolveIncident} />
|
||||||
|
)}
|
||||||
|
{displayPanicDemo && (
|
||||||
|
<div className="absolute top-0 right-20 z-50 p-2">
|
||||||
|
<PanicButtonDemo
|
||||||
|
onTriggerAlert={handleTriggerAlert}
|
||||||
|
onResolveAllAlerts={handleResolveAllAlerts}
|
||||||
|
activeIncidents={ewsIncidents.filter((inc) => inc.status === "active")}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<CrimeSidebar
|
<CrimeSidebar
|
||||||
crimes={filteredCrimes || []}
|
crimes={filteredCrimes || []}
|
||||||
defaultCollapsed={sidebarCollapsed}
|
defaultCollapsed={sidebarCollapsed}
|
||||||
|
@ -312,18 +361,18 @@ export default function CrimeMap() {
|
||||||
selectedMonth={selectedMonth}
|
selectedMonth={selectedMonth}
|
||||||
sourceType={selectedSourceType} // Pass the sourceType
|
sourceType={selectedSourceType} // Pass the sourceType
|
||||||
/>
|
/>
|
||||||
{isFullscreen && (
|
|
||||||
<div className="absolute bottom-20 right-0 z-20 p-2">
|
|
||||||
{showClusters && (
|
|
||||||
<MapLegend position="bottom-right" />
|
|
||||||
)}
|
|
||||||
{showUnclustered && !showClusters && (
|
|
||||||
<MapLegend position="bottom-right" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{isFullscreen && showUnitsLayer && (
|
<div className="absolute bottom-20 right-0 z-20 p-2">
|
||||||
|
{showClusters && (
|
||||||
|
<MapLegend position="bottom-right" />
|
||||||
|
)}
|
||||||
|
{showUnclustered && !showClusters && (
|
||||||
|
<MapLegend position="bottom-right" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{showUnitsLayer && (
|
||||||
<div className="absolute bottom-20 right-0 z-10 p-2">
|
<div className="absolute bottom-20 right-0 z-10 p-2">
|
||||||
<UnitsLegend
|
<UnitsLegend
|
||||||
categories={categories}
|
categories={categories}
|
||||||
|
@ -332,7 +381,7 @@ export default function CrimeMap() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isFullscreen && showTimelineLayer && (
|
{showTimelineLayer && (
|
||||||
<div className="absolute flex bottom-20 right-0 z-10 p-2">
|
<div className="absolute flex bottom-20 right-0 z-10 p-2">
|
||||||
<TimelineLegend position="bottom-right" />
|
<TimelineLegend position="bottom-right" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -340,17 +389,17 @@ export default function CrimeMap() {
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isFullscreen && (
|
|
||||||
<div className="absolute flex w-full bottom-0">
|
<div className="absolute flex w-full bottom-0">
|
||||||
<CrimeTimelapse
|
<CrimeTimelapse
|
||||||
startYear={2020}
|
startYear={2020}
|
||||||
endYear={2024}
|
endYear={2024}
|
||||||
autoPlay={false}
|
autoPlay={false}
|
||||||
onChange={handleTimelineChange}
|
onChange={handleTimelineChange}
|
||||||
onPlayingChange={handleTimelinePlayingChange}
|
onPlayingChange={handleTimelinePlayingChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</MapView>
|
</MapView>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -93,6 +93,7 @@ export default function Layers({
|
||||||
sourceType = "cbt",
|
sourceType = "cbt",
|
||||||
}: LayersProps) {
|
}: LayersProps) {
|
||||||
const animationRef = useRef<number | null>(null)
|
const animationRef = useRef<number | null>(null)
|
||||||
|
|
||||||
const { current: map } = useMap()
|
const { current: map } = useMap()
|
||||||
|
|
||||||
if (!map) {
|
if (!map) {
|
||||||
|
@ -115,28 +116,28 @@ export default function Layers({
|
||||||
const [showPanicDemo, setShowPanicDemo] = useState(true)
|
const [showPanicDemo, setShowPanicDemo] = useState(true)
|
||||||
const [displayPanicDemo, setDisplayPanicDemo] = useState(showEWS && showPanicDemo)
|
const [displayPanicDemo, setDisplayPanicDemo] = useState(showEWS && showPanicDemo)
|
||||||
|
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
setEwsIncidents(getAllIncidents())
|
// setEwsIncidents(getAllIncidents())
|
||||||
}, [])
|
// }, [])
|
||||||
|
|
||||||
const handleTriggerAlert = useCallback((priority: "high" | "medium" | "low") => {
|
// const handleTriggerAlert = useCallback((priority: "high" | "medium" | "low") => {
|
||||||
const newIncident = addMockIncident({ priority })
|
// const newIncident = addMockIncident({ priority })
|
||||||
setEwsIncidents(getAllIncidents())
|
// setEwsIncidents(getAllIncidents())
|
||||||
}, [])
|
// }, [])
|
||||||
|
|
||||||
const handleResolveIncident = useCallback((id: string) => {
|
// const handleResolveIncident = useCallback((id: string) => {
|
||||||
resolveIncident(id)
|
// resolveIncident(id)
|
||||||
setEwsIncidents(getAllIncidents())
|
// setEwsIncidents(getAllIncidents())
|
||||||
}, [])
|
// }, [])
|
||||||
|
|
||||||
const handleResolveAllAlerts = useCallback(() => {
|
// const handleResolveAllAlerts = useCallback(() => {
|
||||||
ewsIncidents.forEach((incident) => {
|
// ewsIncidents.forEach((incident) => {
|
||||||
if (incident.status === "active") {
|
// if (incident.status === "active") {
|
||||||
resolveIncident(incident.id)
|
// resolveIncident(incident.id)
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
setEwsIncidents(getAllIncidents())
|
// setEwsIncidents(getAllIncidents())
|
||||||
}, [ewsIncidents])
|
// }, [ewsIncidents])
|
||||||
|
|
||||||
const handlePopupClose = useCallback(() => {
|
const handlePopupClose = useCallback(() => {
|
||||||
selectedDistrictRef.current = null
|
selectedDistrictRef.current = null
|
||||||
|
@ -534,9 +535,9 @@ export default function Layers({
|
||||||
|
|
||||||
<FaultLinesLayer map={mapboxMap} />
|
<FaultLinesLayer map={mapboxMap} />
|
||||||
|
|
||||||
{showEWS && <EWSAlertLayer map={mapboxMap} incidents={ewsIncidents} onIncidentResolved={handleResolveIncident} />}
|
{/* {showEWS && <EWSAlertLayer map={mapboxMap} incidents={ewsIncidents} onIncidentResolved={handleResolveIncident} />} */}
|
||||||
|
|
||||||
{showEWS && displayPanicDemo && (
|
{/* {showEWS && displayPanicDemo && (
|
||||||
<div className="absolute top-0 right-20 z-50 p-2">
|
<div className="absolute top-0 right-20 z-50 p-2">
|
||||||
<PanicButtonDemo
|
<PanicButtonDemo
|
||||||
onTriggerAlert={handleTriggerAlert}
|
onTriggerAlert={handleTriggerAlert}
|
||||||
|
@ -544,7 +545,7 @@ export default function Layers({
|
||||||
activeIncidents={ewsIncidents.filter((inc) => inc.status === "active")}
|
activeIncidents={ewsIncidents.filter((inc) => inc.status === "active")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)} */}
|
||||||
|
|
||||||
|
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -30,6 +30,20 @@ interface IDistrictIncidents {
|
||||||
timestamp: Date
|
timestamp: Date
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New interface to better type the incident properties
|
||||||
|
interface IncidentProperties {
|
||||||
|
id: string
|
||||||
|
description: string
|
||||||
|
category: string
|
||||||
|
date: string
|
||||||
|
district: string
|
||||||
|
district_id: string
|
||||||
|
categoryColor: string
|
||||||
|
distance_to_unit: number | "Unknown"
|
||||||
|
longitude: number
|
||||||
|
latitude: number
|
||||||
|
}
|
||||||
|
|
||||||
export default function UnitsLayer({ crimes, units = [], filterCategory, visible = false, map }: UnitsLayerProps) {
|
export default function UnitsLayer({ crimes, units = [], filterCategory, visible = false, map }: UnitsLayerProps) {
|
||||||
const [loadedUnits, setLoadedUnits] = useState<IUnits[]>([])
|
const [loadedUnits, setLoadedUnits] = useState<IUnits[]>([])
|
||||||
const loadedUnitsRef = useRef<IUnits[]>([])
|
const loadedUnitsRef = useRef<IUnits[]>([])
|
||||||
|
@ -50,6 +64,9 @@ export default function UnitsLayer({ crimes, units = [], filterCategory, visible
|
||||||
5
|
5
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Add a ref to store pre-processed incidents by district for optimization
|
||||||
|
const districtIncidentsCache = useRef<Map<string, IDistrictIncidents[]>>(new Map());
|
||||||
|
|
||||||
// Use either provided units or loaded units
|
// Use either provided units or loaded units
|
||||||
const unitsData = useMemo(() => {
|
const unitsData = useMemo(() => {
|
||||||
return units.length > 0 ? units : loadedUnits || []
|
return units.length > 0 ? units : loadedUnits || []
|
||||||
|
@ -105,7 +122,15 @@ export default function UnitsLayer({ crimes, units = [], filterCategory, visible
|
||||||
const incidentsGeoJSON = useMemo(() => {
|
const incidentsGeoJSON = useMemo(() => {
|
||||||
const features: any[] = []
|
const features: any[] = []
|
||||||
|
|
||||||
|
// Also build the district incidents cache while processing crime data
|
||||||
|
const newDistrictIncidentsCache = new Map<string, IDistrictIncidents[]>();
|
||||||
|
|
||||||
crimes.forEach((crime) => {
|
crimes.forEach((crime) => {
|
||||||
|
// Initialize the array for this district if it doesn't exist yet
|
||||||
|
if (!newDistrictIncidentsCache.has(crime.district_id)) {
|
||||||
|
newDistrictIncidentsCache.set(crime.district_id, []);
|
||||||
|
}
|
||||||
|
|
||||||
crime.crime_incidents.forEach((incident) => {
|
crime.crime_incidents.forEach((incident) => {
|
||||||
// Skip incidents without location data or filtered by category
|
// Skip incidents without location data or filtered by category
|
||||||
if (
|
if (
|
||||||
|
@ -115,6 +140,22 @@ export default function UnitsLayer({ crimes, units = [], filterCategory, visible
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
// Ensure distance_to_unit is properly initialized
|
||||||
|
const distance = incident.locations.distance_to_unit !== undefined
|
||||||
|
? incident.locations.distance_to_unit
|
||||||
|
: "Unknown";
|
||||||
|
|
||||||
|
// Add to district incidents cache for quicker lookup
|
||||||
|
if (incident.locations.distance_to_unit !== undefined) {
|
||||||
|
newDistrictIncidentsCache.get(crime.district_id)?.push({
|
||||||
|
incident_id: incident.id,
|
||||||
|
category_name: incident.crime_categories.name,
|
||||||
|
incident_description: incident.description || "No description",
|
||||||
|
distance_meters: incident.locations.distance_to_unit!,
|
||||||
|
timestamp: incident.timestamp,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
features.push({
|
features.push({
|
||||||
type: "Feature" as const,
|
type: "Feature" as const,
|
||||||
properties: {
|
properties: {
|
||||||
|
@ -125,7 +166,7 @@ export default function UnitsLayer({ crimes, units = [], filterCategory, visible
|
||||||
district: crime.districts.name,
|
district: crime.districts.name,
|
||||||
district_id: crime.district_id,
|
district_id: crime.district_id,
|
||||||
categoryColor: categoryColorMap[incident.crime_categories.name] || "#22c55e",
|
categoryColor: categoryColorMap[incident.crime_categories.name] || "#22c55e",
|
||||||
distance_to_unit: incident.locations.distance_to_unit || "Unknown",
|
distance_to_unit: distance,
|
||||||
},
|
},
|
||||||
geometry: {
|
geometry: {
|
||||||
type: "Point" as const,
|
type: "Point" as const,
|
||||||
|
@ -135,6 +176,9 @@ export default function UnitsLayer({ crimes, units = [], filterCategory, visible
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Update the cache ref with our new data
|
||||||
|
districtIncidentsCache.current = newDistrictIncidentsCache;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: "FeatureCollection" as const,
|
type: "FeatureCollection" as const,
|
||||||
features,
|
features,
|
||||||
|
@ -245,32 +289,43 @@ export default function UnitsLayer({ crimes, units = [], filterCategory, visible
|
||||||
|
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
|
|
||||||
// Find all incidents in the same district as the unit
|
// Early exit if district_id is not available
|
||||||
const districtIncidents: IDistrictIncidents[] = []
|
if (!unit.district_id) {
|
||||||
crimes.forEach((crime) => {
|
console.log("Unit has no district ID")
|
||||||
|
setUnitIncident([])
|
||||||
|
setIsLoading(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
console.log("Processing crime:", crime.district_id, unit.district_id) // Debug log
|
// Use the pre-processed district incidents from cache
|
||||||
|
let districtIncidents = districtIncidentsCache.current.get(unit.district_id) || [];
|
||||||
|
|
||||||
// Check if this crime is in the same district as the unit
|
// If we don't have them in cache for some reason, compute them now
|
||||||
if (crime.district_id === unit.district_id) {
|
if (districtIncidents.length === 0) {
|
||||||
crime.crime_incidents.forEach((incident) => {
|
const tempIncidents: IDistrictIncidents[] = [];
|
||||||
if (incident.locations && typeof incident.locations.distance_to_unit !== "undefined") {
|
|
||||||
districtIncidents.push({
|
// Only process crimes for this specific district
|
||||||
incident_id: incident.id,
|
crimes
|
||||||
category_name: incident.crime_categories.name,
|
.filter(crime => crime.district_id === unit.district_id)
|
||||||
incident_description: incident.description || "No description",
|
.forEach(crime => {
|
||||||
distance_meters: incident.locations.distance_to_unit!,
|
crime.crime_incidents.forEach(incident => {
|
||||||
timestamp: incident.timestamp,
|
if (incident.locations && typeof incident.locations.distance_to_unit !== "undefined") {
|
||||||
})
|
tempIncidents.push({
|
||||||
}
|
incident_id: incident.id,
|
||||||
})
|
category_name: incident.crime_categories.name,
|
||||||
}
|
incident_description: incident.description || "No description",
|
||||||
})
|
distance_meters: incident.locations.distance_to_unit!,
|
||||||
|
timestamp: incident.timestamp,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
districtIncidents = tempIncidents;
|
||||||
|
}
|
||||||
|
|
||||||
// Sort by distance (closest first)
|
// Sort by distance (closest first)
|
||||||
districtIncidents.sort((a, b) => a.distance_meters - b.distance_meters)
|
districtIncidents.sort((a, b) => a.distance_meters - b.distance_meters);
|
||||||
|
|
||||||
// console.log("Sorted district incidents:", districtIncidents)
|
|
||||||
|
|
||||||
// Update the state with the distance results
|
// Update the state with the distance results
|
||||||
setUnitIncident(districtIncidents)
|
setUnitIncident(districtIncidents)
|
||||||
|
@ -350,6 +405,10 @@ export default function UnitsLayer({ crimes, units = [], filterCategory, visible
|
||||||
duration: BASE_DURATION,
|
duration: BASE_DURATION,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Ensure distance_to_unit has a value - use the value from GeoJSON properties directly
|
||||||
|
// This ensures we use the same data that was calculated for the GeoJSON
|
||||||
|
let distanceToUnit = properties.distance_to_unit;
|
||||||
|
|
||||||
// Create incident object from properties
|
// Create incident object from properties
|
||||||
const incident = {
|
const incident = {
|
||||||
id: properties.id,
|
id: properties.id,
|
||||||
|
@ -358,14 +417,11 @@ export default function UnitsLayer({ crimes, units = [], filterCategory, visible
|
||||||
date: properties.date,
|
date: properties.date,
|
||||||
district: properties.district,
|
district: properties.district,
|
||||||
district_id: properties.district_id,
|
district_id: properties.district_id,
|
||||||
distance_to_unit: properties.distance_to_unit,
|
distance_to_unit: distanceToUnit,
|
||||||
longitude,
|
longitude,
|
||||||
latitude,
|
latitude,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug log
|
|
||||||
console.log("Incident clicked:", incident)
|
|
||||||
|
|
||||||
// Set the selected incident and query parameters
|
// Set the selected incident and query parameters
|
||||||
setSelectedIncident(incident)
|
setSelectedIncident(incident)
|
||||||
setSelectedUnit(null) // Clear any selected unit
|
setSelectedUnit(null) // Clear any selected unit
|
||||||
|
@ -386,6 +442,7 @@ export default function UnitsLayer({ crimes, units = [], filterCategory, visible
|
||||||
district: properties.district,
|
district: properties.district,
|
||||||
category: properties.category,
|
category: properties.category,
|
||||||
description: properties.description,
|
description: properties.description,
|
||||||
|
distance_to_unit: distanceToUnit,
|
||||||
longitude,
|
longitude,
|
||||||
latitude,
|
latitude,
|
||||||
},
|
},
|
||||||
|
@ -438,11 +495,11 @@ export default function UnitsLayer({ crimes, units = [], filterCategory, visible
|
||||||
if (!map || !visible) return
|
if (!map || !visible) return
|
||||||
|
|
||||||
// Debug log untuk memeriksa keberadaan layer
|
// Debug log untuk memeriksa keberadaan layer
|
||||||
console.log("Setting up event handlers, map layers:",
|
// console.log("Setting up event handlers, map layers:",
|
||||||
map.getStyle().layers?.filter(l =>
|
// map.getStyle().layers?.filter(l =>
|
||||||
l.id === "units-points" || l.id === "incidents-points"
|
// l.id === "units-points" || l.id === "incidents-points"
|
||||||
).map(l => l.id)
|
// ).map(l => l.id)
|
||||||
)
|
// )
|
||||||
|
|
||||||
// Define event handlers that can be referenced for both adding and removing
|
// Define event handlers that can be referenced for both adding and removing
|
||||||
const handleMouseEnter = () => {
|
const handleMouseEnter = () => {
|
||||||
|
@ -461,7 +518,7 @@ export default function UnitsLayer({ crimes, units = [], filterCategory, visible
|
||||||
map.on("click", "units-points", unitClickHandler)
|
map.on("click", "units-points", unitClickHandler)
|
||||||
map.on("mouseenter", "units-points", handleMouseEnter)
|
map.on("mouseenter", "units-points", handleMouseEnter)
|
||||||
map.on("mouseleave", "units-points", handleMouseLeave)
|
map.on("mouseleave", "units-points", handleMouseLeave)
|
||||||
console.log("✅ Unit points handler attached")
|
// console.log("✅ Unit points handler attached")
|
||||||
} else {
|
} else {
|
||||||
console.log("❌ units-points layer not found")
|
console.log("❌ units-points layer not found")
|
||||||
}
|
}
|
||||||
|
@ -472,7 +529,7 @@ export default function UnitsLayer({ crimes, units = [], filterCategory, visible
|
||||||
map.on("click", "incidents-points", incidentClickHandler)
|
map.on("click", "incidents-points", incidentClickHandler)
|
||||||
map.on("mouseenter", "incidents-points", handleMouseEnter)
|
map.on("mouseenter", "incidents-points", handleMouseEnter)
|
||||||
map.on("mouseleave", "incidents-points", handleMouseLeave)
|
map.on("mouseleave", "incidents-points", handleMouseLeave)
|
||||||
console.log("✅ Incident points handler attached")
|
// console.log("✅ Incident points handler attached")
|
||||||
} else {
|
} else {
|
||||||
console.log("❌ incidents-points layer not found")
|
console.log("❌ incidents-points layer not found")
|
||||||
}
|
}
|
||||||
|
@ -516,7 +573,7 @@ export default function UnitsLayer({ crimes, units = [], filterCategory, visible
|
||||||
|
|
||||||
// Reset map filters when popup is closed
|
// Reset map filters when popup is closed
|
||||||
const handleClosePopup = useCallback(() => {
|
const handleClosePopup = useCallback(() => {
|
||||||
console.log("Closing popup, clearing selected states")
|
// console.log("Closing popup, clearing selected states")
|
||||||
setSelectedUnit(null)
|
setSelectedUnit(null)
|
||||||
setSelectedIncident(null)
|
setSelectedIncident(null)
|
||||||
setSelectedEntityId(undefined)
|
setSelectedEntityId(undefined)
|
||||||
|
@ -545,13 +602,13 @@ export default function UnitsLayer({ crimes, units = [], filterCategory, visible
|
||||||
}, [visible, handleClosePopup])
|
}, [visible, handleClosePopup])
|
||||||
|
|
||||||
// Debug untuk komponen render
|
// Debug untuk komponen render
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
console.log("Render state:", {
|
// console.log("Render state:", {
|
||||||
selectedUnit: selectedUnit?.code_unit,
|
// selectedUnit: selectedUnit?.code_unit,
|
||||||
selectedIncident: selectedIncident?.id,
|
// selectedIncident: selectedIncident?.id,
|
||||||
visible
|
// visible
|
||||||
})
|
// })
|
||||||
}, [selectedUnit, selectedIncident, visible])
|
// }, [selectedUnit, selectedIncident, visible])
|
||||||
|
|
||||||
if (!visible) return null
|
if (!visible) return null
|
||||||
|
|
||||||
|
|
|
@ -117,28 +117,6 @@ export default function TimelinePopup({
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
{/* Connection line */}
|
|
||||||
<div
|
|
||||||
className="absolute top-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 top-0 left-1/2 transform -translate-x-1/2 -translate-y-full"
|
|
||||||
style={{
|
|
||||||
width: '6px',
|
|
||||||
height: '6px',
|
|
||||||
backgroundColor: 'red',
|
|
||||||
borderRadius: '50%',
|
|
||||||
marginBottom: '20px',
|
|
||||||
boxShadow: '0 0 4px rgba(0, 0, 0, 0.3)'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</Popup>
|
</Popup>
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue