// src/components/MapDisplay.tsx import React, { useState, useEffect, useCallback } from 'react'; import { MapContainer, TileLayer, CircleMarker, Tooltip, useMapEvents } from 'react-leaflet'; import { HeatmapLayer } from 'react-leaflet-heatmap-layer-v3'; import 'leaflet/dist/leaflet.css'; import 'leaflet.heat'; import type { HeatmapPoint, MapMode } from '../App'; import MarkerClusterGroup from 'react-leaflet-cluster'; import Legend from './Legend'; import MapBoundsManager from './MapBoundsManager'; import { LatLngBounds } from 'leaflet'; interface MapDisplayProps { heatmapData: HeatmapPoint[]; radiusMultiplier: number; viewMode: MapMode; } // This component listens to map events and calls a callback when the view changes const DynamicLegendHandler = ({ onBoundsChange }: { onBoundsChange: (bounds: LatLngBounds) => void }) => { const map = useMapEvents({ zoomend: () => onBoundsChange(map.getBounds()), moveend: () => onBoundsChange(map.getBounds()), load: () => onBoundsChange(map.getBounds()), // Handle initial load }); return null; }; const MapDisplay: React.FC = ({ heatmapData, radiusMultiplier, viewMode }) => { const germanyCenter: [number, number] = [51.1657, 10.4515]; const [visibleData, setVisibleData] = useState(heatmapData); const dynamicMaxCount = Math.max(...visibleData.map(p => p.count), 1); const calculateRadius = (count: number) => { return 3 + Math.log(count + 1) * 5 * radiusMultiplier; }; const getColor = (count: number) => { const ratio = count / dynamicMaxCount; if (ratio > 0.8) return '#d73027'; // Red if (ratio > 0.5) return '#fdae61'; // Orange if (ratio > 0.2) return '#fee08b'; // Yellow return '#66bd63'; // Green }; const updateVisibleData = useCallback((bounds: LatLngBounds) => { const visible = heatmapData.filter(p => bounds.contains([p.lat, p.lon]) ); setVisibleData(visible.length > 0 ? visible : heatmapData); // Fallback to all data if none are visible }, [heatmapData]); // Reset visible data when heatmapData changes useEffect(() => { setVisibleData(heatmapData); }, [heatmapData]); const renderPoints = () => ( {heatmapData.map((point, idx) => ( PLZ: {point.plz}
Count: {point.count} {point.attributes_summary && Object.entries(point.attributes_summary).map(([attr, values]) => (
{attr}: {values.join(', ')}
))}
))}
); const renderHeatmap = () => ( p.lon} latitudeExtractor={(p: HeatmapPoint) => p.lat} intensityExtractor={(p: HeatmapPoint) => p.count} radius={25} blur={20} max={dynamicMaxCount * 0.1} /> ); if (heatmapData.length === 0) { return (

No data to display on the map.

); } return ( {viewMode === 'points' ? renderPoints() : renderHeatmap()} {viewMode === 'points' && } ); }; export default MapDisplay;