From 247b327f38e9d6511b84ff62162a26834954266c Mon Sep 17 00:00:00 2001 From: alma Date: Sun, 4 May 2025 17:08:40 +0200 Subject: [PATCH] observatory --- components/observatory/map-component.tsx | 169 +++++++++++------------ 1 file changed, 81 insertions(+), 88 deletions(-) diff --git a/components/observatory/map-component.tsx b/components/observatory/map-component.tsx index 3b3b377f..d5a7e0b6 100644 --- a/components/observatory/map-component.tsx +++ b/components/observatory/map-component.tsx @@ -1,21 +1,11 @@ "use client"; -import { useEffect, useState } from "react"; +import { useEffect, useRef } from "react"; import L from "leaflet"; -import { MapContainer, TileLayer, Marker, Popup, ZoomControl } from "react-leaflet"; // Import leaflet CSS import "leaflet/dist/leaflet.css"; -// Add declaration for Leaflet's Icon.Default -declare module 'leaflet' { - namespace Icon { - interface Default { - _getIconUrl?: string; - } - } -} - interface CountryData { name: string; count: number; @@ -28,91 +18,94 @@ interface MapComponentProps { selectedCountry: string | null; } -// Fix Leaflet default icon path issues -function fixLeafletIcons() { - // Delete to prevent duplicate icon definitions - delete L.Icon.Default.prototype._getIconUrl; - - // Set paths to the images - L.Icon.Default.mergeOptions({ - iconRetinaUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png", - iconUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png", - shadowUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png", - }); -} - export function MapComponent({ countries, onCountrySelect, selectedCountry }: MapComponentProps) { - const [isClient, setIsClient] = useState(false); + // Use a ref to track if map is initialized + const mapRef = useRef(null); + const mapContainerRef = useRef(null); useEffect(() => { - // Fix Leaflet icons on client side - fixLeafletIcons(); - setIsClient(true); + // Only initialize map if it doesn't exist and we have a container + if (!mapRef.current && mapContainerRef.current) { + // Create the map instance + mapRef.current = L.map(mapContainerRef.current).setView([20, 0], 2); + + // Add tile layer + L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { + attribution: '© OpenStreetMap contributors' + }).addTo(mapRef.current); + + // Add zoom control + L.control.zoom({ + position: 'bottomright' + }).addTo(mapRef.current); + } + + // Cleanup function to remove the map when component unmounts + return () => { + if (mapRef.current) { + mapRef.current.remove(); + mapRef.current = null; + } + }; }, []); - // Get custom icon based on count - const getMarkerIcon = (count: number, isSelected: boolean) => { - const size = Math.min(Math.max(20, count * 5), 40); + // Update markers when countries or selection changes + useEffect(() => { + if (!mapRef.current) return; - return L.divIcon({ - html: `
${count}
`, - className: '', - iconSize: [size, size], - iconAnchor: [size/2, size/2] + // Clear existing markers + mapRef.current.eachLayer((layer) => { + if (layer instanceof L.Marker) { + mapRef.current?.removeLayer(layer); + } }); - }; - - if (!isClient) { - return ( -
-

Loading map...

-
- ); - } + + // Add markers for each country + countries.forEach((country) => { + // Create custom icon + const size = Math.min(Math.max(20, country.count * 5), 40); + const icon = L.divIcon({ + html: `
${country.count}
`, + className: '', + iconSize: [size, size], + iconAnchor: [size/2, size/2] + }); + + // Create marker + const marker = L.marker(country.position, { icon }) + .addTo(mapRef.current!); + + // Add popup + marker.bindPopup(` +
+ ${country.name} +
${country.count} news articles
+
+ `); + + // Add click handler + marker.on('click', () => { + onCountrySelect(country.name); + }); + }); + }, [countries, selectedCountry, onCountrySelect]); return ( - - - - - {countries.map((country) => ( - onCountrySelect(country.name) - }} - > - -
- {country.name} -
{country.count} news articles
-
-
-
- ))} -
+
); } \ No newline at end of file