import L from "leaflet"; import "leaflet.markercluster"; import "leaflet/dist/leaflet.css"; import data from "../data/orgas.json"; import defaultImage from "../assets/default.svg"; import limitedImage from "../assets/limited.svg"; import supportImage from "../assets/support.svg"; import { DEFAULT_ZOOM_LEVEL, ICON_SIZE, MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL } from "./config"; type Organisation = { country: string; state: string; name: string; email?: string; website?: string; phone?: string; location: { address?: string; lon: number; lat: number; approx?: boolean; }; activities?: string; identities?: string; age_restriction?: string; }; let map: L.Map; export function initializeMap() { map = L.map("map").setView([51.351, 10.454], MIN_ZOOM_LEVEL); // focus on germany document.querySelectorAll(".locate-button").forEach((item) => { item.addEventListener("click", (event) => { map.locate({ setView: true, maxZoom: DEFAULT_ZOOM_LEVEL }); }); }); L.tileLayer("https://{s}.tile.openstreetmap.de/{z}/{x}/{y}.png", { maxZoom: MAX_ZOOM_LEVEL, minZoom: MIN_ZOOM_LEVEL, attribution: "© OpenStreetMap", }).addTo(map); var markers = L.markerClusterGroup({ // Displaying the areas of each cluster is not intended. It might confuse our users // and provides no benefit. showCoverageOnHover: false, // The default lines are barely visible, especially on the German map. Therefore, thicker // and more visible lines are placed. spiderLegPolylineOptions: { color: "black", opacity: 0.8, weight: 2, }, // Our icons are too large for the default multiplier, this increases the distance between the elements. // Maybe we should even increase it further to improve the usability on mobile devices. spiderfyDistanceMultiplier: 2, // Reduce the cluster radius from 80px to 20px. This creates more and smaller clusters. maxClusterRadius: 20, // Let's use the default icon for our clusters and place a white circle inside of it. iconCreateFunction: (cluster) => { return new L.DivIcon({ html: `
${cluster.getChildCount()}
`, className: "", // empty className, so leaflet does not set any defaults. iconSize: ICON_SIZE, }); }, }); data.forEach((row) => { const marker = createMarker(row); if (marker) markers.addLayer(marker); }); map.addLayer(markers); } const defaultIcon = L.icon({ iconUrl: defaultImage, iconSize: ICON_SIZE, }); const limitedIcon = L.icon({ iconUrl: limitedImage, iconSize: ICON_SIZE, }); const supportIcon = L.icon({ iconUrl: supportImage, iconSize: ICON_SIZE, }); const createMarker = (e: Organisation): L.Marker | undefined => { const marker = L.marker([e.location.lon, e.location.lat], { alt: e.name }).bindPopup( buildContent(e), { className: "map-popup" }, ); if (e.identities && e.identities !== "") { marker.setIcon(limitedIcon); } else if (e.activities && e.activities.toLowerCase().includes("beratung")) { marker.setIcon(supportIcon); } else { marker.setIcon(defaultIcon); } return marker; }; const buildContent = (o: Organisation): string => { let result = `

${o.name}

${o.location.address ?? "auf Nachfrage"}
`; if (o.activities && o.activities !== "") result += `

Aktivitäten

${o.activities}

`; if (o.identities && o.identities !== "") result += `

Identitäten

${o.identities}

`; if (o.age_restriction && o.age_restriction !== "") result += `

Altersbeschränkung

vorhanden: ${o.age_restriction}

`; if (o.location.approx) { result += `

Bei den Daten handelt es sich um eine ungefähre Ortsangabe. Die genaue Adresse erfährst du auf Nachfrage.

`; } return result; }; export const getMap = (): L.Map => map;