import React, { useState, useEffect, useRef } from 'react'; import L from 'leaflet'; import 'leaflet/dist/leaflet.css'; import 'leaflet.heat'; import './HeatmapView.css'; const HeatmapView = () => { const mapRef = useRef(null); const mapInstanceRef = useRef(null); const heatLayerRef = useRef(null); const [heatmapData, setHeatmapData] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [total, setTotal] = useState(0); // Filters const [filterType, setFilterType] = useState('all'); const [ageGroup, setAgeGroup] = useState(''); const [season, setSeason] = useState(''); const [yearFrom, setYearFrom] = useState(''); const [yearTo, setYearTo] = useState(''); // Layer visibility const [showHeatmap, setShowHeatmap] = useState(true); // Initialize map useEffect(() => { if (!mapRef.current || mapInstanceRef.current) return; // Create map centered on Russia const map = L.map(mapRef.current).setView([55.7558, 37.6173], 5); // Add OpenStreetMap tiles L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap contributors', maxZoom: 18 }).addTo(map); mapInstanceRef.current = map; return () => { if (mapInstanceRef.current) { mapInstanceRef.current.remove(); mapInstanceRef.current = null; } }; }, []); // Fetch heatmap data const fetchHeatmapData = async () => { setLoading(true); setError(null); try { const params = new URLSearchParams({ type: filterType }); if (filterType === 'age' && ageGroup) { params.append('age_group', ageGroup); } if (filterType === 'season' && season) { params.append('season', season); } if (yearFrom) { params.append('year_from', yearFrom); } if (yearTo) { params.append('year_to', yearTo); } const response = await fetch(`http://192.168.0.99:8000/api/v1/stats/heatmap?${params}`); if (!response.ok) { throw new Error('Failed to fetch heatmap data'); } const data = await response.json(); setHeatmapData(data.points); setTotal(data.total); } catch (err) { setError(err.message); } finally { setLoading(false); } }; // Update heatmap layer useEffect(() => { if (!mapInstanceRef.current) return; // Remove existing heat layer if (heatLayerRef.current) { mapInstanceRef.current.removeLayer(heatLayerRef.current); heatLayerRef.current = null; } // Add new heat layer if visible and data exists if (showHeatmap && heatmapData.length > 0) { const heatData = heatmapData.map(point => [point.lat, point.lon, point.intensity]); const heatLayer = L.heatLayer(heatData, { radius: 25, blur: 15, maxZoom: 10, max: 1.0, gradient: { 0.0: 'blue', 0.5: 'lime', 0.7: 'yellow', 1.0: 'red' } }); heatLayer.addTo(mapInstanceRef.current); heatLayerRef.current = heatLayer; } }, [heatmapData, showHeatmap]); // Load data on mount and filter change useEffect(() => { fetchHeatmapData(); }, [filterType, ageGroup, season, yearFrom, yearTo]); const handleFilterTypeChange = (type) => { setFilterType(type); // Reset specific filters if (type !== 'age') setAgeGroup(''); if (type !== 'season') setSeason(''); }; return (

Тепловая карта находок

{filterType === 'age' && (
)} {filterType === 'season' && (
)}
setYearFrom(e.target.value)} min="2000" max="2100" /> — setYearTo(e.target.value)} min="2000" max="2100" />

Всего точек: {total}

{loading &&

Загрузка...

} {error &&

Ошибка: {error}

}

Легенда

Низкая плотность
Высокая плотность
); }; export default HeatmapView;