fix([2fd88f42]): resolve infinite loop in dynamic legend handler
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
// src/components/MapDisplay.tsx
|
// src/components/MapDisplay.tsx
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
import { MapContainer, TileLayer, CircleMarker, Tooltip, useMapEvents } from 'react-leaflet';
|
import { MapContainer, TileLayer, CircleMarker, Tooltip, useMapEvents } from 'react-leaflet';
|
||||||
import { HeatmapLayer } from 'react-leaflet-heatmap-layer-v3';
|
import { HeatmapLayer } from 'react-leaflet-heatmap-layer-v3';
|
||||||
import 'leaflet/dist/leaflet.css';
|
import 'leaflet/dist/leaflet.css';
|
||||||
@@ -8,6 +8,8 @@ import type { HeatmapPoint, MapMode } from '../App';
|
|||||||
import MarkerClusterGroup from 'react-leaflet-cluster';
|
import MarkerClusterGroup from 'react-leaflet-cluster';
|
||||||
import Legend from './Legend';
|
import Legend from './Legend';
|
||||||
import MapBoundsManager from './MapBoundsManager';
|
import MapBoundsManager from './MapBoundsManager';
|
||||||
|
import { LatLngBounds } from 'leaflet';
|
||||||
|
|
||||||
|
|
||||||
interface MapDisplayProps {
|
interface MapDisplayProps {
|
||||||
heatmapData: HeatmapPoint[];
|
heatmapData: HeatmapPoint[];
|
||||||
@@ -15,20 +17,32 @@ interface MapDisplayProps {
|
|||||||
viewMode: MapMode;
|
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()),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initial load
|
||||||
|
useEffect(() => {
|
||||||
|
onBoundsChange(map.getBounds());
|
||||||
|
}, []); // Run only once on initial mount
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const MapDisplay: React.FC<MapDisplayProps> = ({ heatmapData, radiusMultiplier, viewMode }) => {
|
const MapDisplay: React.FC<MapDisplayProps> = ({ heatmapData, radiusMultiplier, viewMode }) => {
|
||||||
const germanyCenter: [number, number] = [51.1657, 10.4515];
|
const germanyCenter: [number, number] = [51.1657, 10.4515];
|
||||||
|
|
||||||
// State for the data currently visible in the map viewport
|
|
||||||
const [visibleData, setVisibleData] = useState<HeatmapPoint[]>(heatmapData);
|
const [visibleData, setVisibleData] = useState<HeatmapPoint[]>(heatmapData);
|
||||||
|
|
||||||
// The dynamic max count based only on visible data
|
|
||||||
const dynamicMaxCount = Math.max(...visibleData.map(p => p.count), 1);
|
const dynamicMaxCount = Math.max(...visibleData.map(p => p.count), 1);
|
||||||
|
|
||||||
const calculateRadius = (count: number) => {
|
const calculateRadius = (count: number) => {
|
||||||
return 3 + Math.log(count + 1) * 5 * radiusMultiplier;
|
return 3 + Math.log(count + 1) * 5 * radiusMultiplier;
|
||||||
};
|
};
|
||||||
|
|
||||||
// getColor now uses the dynamicMaxCount
|
|
||||||
const getColor = (count: number) => {
|
const getColor = (count: number) => {
|
||||||
const ratio = count / dynamicMaxCount;
|
const ratio = count / dynamicMaxCount;
|
||||||
if (ratio > 0.8) return '#d73027'; // Red
|
if (ratio > 0.8) return '#d73027'; // Red
|
||||||
@@ -36,30 +50,19 @@ const MapDisplay: React.FC<MapDisplayProps> = ({ heatmapData, radiusMultiplier,
|
|||||||
if (ratio > 0.2) return '#fee08b'; // Yellow
|
if (ratio > 0.2) return '#fee08b'; // Yellow
|
||||||
return '#66bd63'; // Green
|
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]);
|
||||||
|
|
||||||
// This component listens to map events and updates the visible data
|
|
||||||
const DynamicLegendHandler = () => {
|
|
||||||
const map = useMapEvents({
|
|
||||||
zoomend: () => updateVisibleData(),
|
|
||||||
moveend: () => updateVisibleData(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const updateVisibleData = () => {
|
|
||||||
const bounds = map.getBounds();
|
|
||||||
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
|
|
||||||
};
|
|
||||||
|
|
||||||
// Initial load
|
|
||||||
useEffect(() => {
|
|
||||||
updateVisibleData();
|
|
||||||
}, [heatmapData]);
|
|
||||||
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderPoints = () => (
|
const renderPoints = () => (
|
||||||
<MarkerClusterGroup>
|
<MarkerClusterGroup>
|
||||||
@@ -115,7 +118,7 @@ const MapDisplay: React.FC<MapDisplayProps> = ({ heatmapData, radiusMultiplier,
|
|||||||
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||||
/>
|
/>
|
||||||
<MapBoundsManager data={heatmapData} />
|
<MapBoundsManager data={heatmapData} />
|
||||||
<DynamicLegendHandler />
|
<DynamicLegendHandler onBoundsChange={updateVisibleData} />
|
||||||
{viewMode === 'points' ? renderPoints() : renderHeatmap()}
|
{viewMode === 'points' ? renderPoints() : renderHeatmap()}
|
||||||
{viewMode === 'points' && <Legend getColor={getColor} maxCount={dynamicMaxCount} />}
|
{viewMode === 'points' && <Legend getColor={getColor} maxCount={dynamicMaxCount} />}
|
||||||
</MapContainer>
|
</MapContainer>
|
||||||
|
|||||||
Reference in New Issue
Block a user