90 lines
3.2 KiB
TypeScript
90 lines
3.2 KiB
TypeScript
import { Layer, Source } from "react-map-gl/mapbox";
|
|
import { useMemo } from "react";
|
|
import { ICrimes } from "@/app/_utils/types/crimes";
|
|
|
|
interface HeatmapLayerProps {
|
|
crimes: ICrimes[];
|
|
year: string;
|
|
month: string;
|
|
filterCategory: string | "all";
|
|
visible?: boolean;
|
|
}
|
|
|
|
export default function HeatmapLayer({ crimes, visible = true }: HeatmapLayerProps) {
|
|
// Convert crime data to GeoJSON format for the heatmap
|
|
const heatmapData = useMemo(() => {
|
|
const features = crimes.flatMap(crime =>
|
|
crime.crime_incidents
|
|
.filter(incident => incident.locations?.latitude && incident.locations?.longitude)
|
|
.map(incident => ({
|
|
type: "Feature" as const,
|
|
properties: {
|
|
id: incident.id,
|
|
category: incident.crime_categories?.name || "Unknown",
|
|
intensity: 1, // Base intensity value
|
|
},
|
|
geometry: {
|
|
type: "Point" as const,
|
|
coordinates: [incident.locations!.longitude, incident.locations!.latitude],
|
|
},
|
|
}))
|
|
);
|
|
|
|
return {
|
|
type: "FeatureCollection" as const,
|
|
features,
|
|
};
|
|
}, [crimes]);
|
|
|
|
if (!visible) return null;
|
|
|
|
return (
|
|
<Source id="crime-heatmap-data" type="geojson" data={heatmapData}>
|
|
<Layer
|
|
id="crime-heatmap"
|
|
type="heatmap"
|
|
paint={{
|
|
// Heatmap radius
|
|
'heatmap-radius': [
|
|
'interpolate',
|
|
['linear'],
|
|
['zoom'],
|
|
8, 10, // At zoom level 8, radius will be 10px
|
|
13, 25 // At zoom level 13, radius will be 25px
|
|
],
|
|
// Heatmap intensity
|
|
'heatmap-intensity': [
|
|
'interpolate',
|
|
['linear'],
|
|
['zoom'],
|
|
8, 0.5, // Less intense at zoom level 8
|
|
13, 1.5 // More intense at zoom level 13
|
|
],
|
|
// Color gradient from low to high density
|
|
'heatmap-color': [
|
|
'interpolate',
|
|
['linear'],
|
|
['heatmap-density'],
|
|
0, 'rgba(33,102,172,0)',
|
|
0.2, 'rgb(103,169,207)',
|
|
0.4, 'rgb(209,229,240)',
|
|
0.6, 'rgb(253,219,199)',
|
|
0.8, 'rgb(239,138,98)',
|
|
1, 'rgb(178,24,43)'
|
|
],
|
|
// Heatmap opacity
|
|
'heatmap-opacity': 0.8,
|
|
// Heatmap weight based on properties
|
|
'heatmap-weight': [
|
|
'interpolate',
|
|
['linear'],
|
|
['get', 'intensity'],
|
|
0, 0.5,
|
|
5, 2
|
|
],
|
|
}}
|
|
/>
|
|
</Source>
|
|
);
|
|
}
|