"use strict"
/* importerar Leaflet-biblioteket och Leaflets egna CSS */
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
let map;
let routeLayer;
/* Sparar POI-markörer med poi.id som nyckel så att rätt markör lätt kan hittas senare */
let poiLayers = new Map();
/**
* Initierar kartan.
* @param {string} mapId
* @param {number[]} initialView [lat, lon]
* @param {number} initialZoom
* @returns {L.Map | null}
*/
export function initMap(mapId = "map", initialView = [62.0, 15.0], initialZoom = 5) {
/* Hämtar rätt karta i DOM utifrån skickat id */
const mapElement = document.getElementById(mapId);
/* Om elementet inte finns ska funktionen inte krascha */
if (!mapElement) {
return null;
}
/* Skapar Leaflet-karta i valt HTML-element */
map = L.map(mapElement).setView(initialView, initialZoom);
/* Lägger på kartbakgrunden */
L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
maxZoom: 19,
attribution: "© OpenStreetMap contributors"
}).addTo(map);
return map;
}
/**
* Ritar rutt på kartan.
* @param {number[][]} coordinates ORS-koordinater i [lon, lat]
*/
export function drawRoute(coordinates) {
/* Om kartan inte finns - gör inget, om koordinatlistan är tom - gör inget */
if (!map || !coordinates?.length) {
return;
}
/* Om det finns en tidigare rutt på kartan, ta bort den först */
if (routeLayer) {
map.removeLayer(routeLayer);
}
/* Loopar igenom alla koordinater och byter plats på värdena för att få dem i den ordning Leaflet vill ha */
const latLngs = coordinates.map(([lon, lat]) => [lat, lon]);
/* Här ritas en linje mellan punkterna på kartan, linjen sparas i routeLayer så att den kan tas bort senare */
routeLayer = L.polyline(latLngs).addTo(map);
/* Zoomar kartan så att hela rutten syns */
map.fitBounds(routeLayer.getBounds(), { padding: [30, 30] });
}
/**
* Hämtar emoji för ett POI beroende på typ.
*
* @param {object} poi
* @returns {string}
*/
function getPoiMarkerEmoji(poi) {
if (["restaurant", "cafe", "fast_food"].includes(poi.type)) {
return "🍔";
}
if (poi.type === "toilets") {
return "🚻";
}
if (poi.type === "fuel") {
return "⛽";
}
if (["camp_site", "caravan_site"].includes(poi.type)) {
return "🏕️";
}
if (poi.type === "viewpoint") {
return "📷";
}
return "📍";
}
/**
* Skapar en enkel emoji-ikon för POI-markörer.
*
* @param {object} poi
* @returns {L.DivIcon}
*/
function createPoiMarkerIcon(poi) {
const emoji = getPoiMarkerEmoji(poi);
return L.divIcon({
className: "poi-emoji-marker",
html: `<div style="font-size: 1.5rem; line-height: 1;">${emoji}</div>`,
iconSize: [28, 28],
iconAnchor: [14, 14],
popupAnchor: [0, -14]
});
}
/**
* Ritar POI-markörer på kartan.
* @param {object[]} pois
*/
export function drawPOIs(pois) {
// Om kartan inte finns gör vi inget
if (!map) {
return;
}
// Ta bort gamla POI-markörer från kartan
poiLayers.forEach((layer) => map.removeLayer(layer));
// Töm samlingen så vi kan spara nya markörer för den aktuella sökningen
poiLayers.clear();
// Om listan är tom efter rensning, avsluta
if (!pois?.length) {
return;
}
// Loopa igenom alla POI
pois.forEach((poi) => {
const lat = poi.lat;
const lon = poi.lon;
if (lat == null || lon == null) {
return;
}
const name = poi.name || "Namnlös plats";
const placeName = poi.placeName ? `<br>${poi.placeName}` : "";
const type = poi.category || poi.type || "Okänd typ";
const marker = L.marker([lat, lon], {
icon: createPoiMarkerIcon(poi)
})
.addTo(map)
.bindPopup(`
<strong>${name}</strong><br>
${type}
${placeName}<br>
<a href="https://www.google.com/maps/search/?api=1&query=${lat},${lon}" target="_blank" rel="noopener noreferrer">
Navigera hit
</a>
`);
/* Sparar markören med POI:ens id som nyckel */
poiLayers.set(poi.id, marker);
});
}
/**
* Visar ett specifikt POI på kartan genom att zooma till markören
* och öppna dess popup.
* @param {object} poi
*/
export function showPOIOnMap(poi) {
// Om kartan eller POI saknas ska inget hända
if (!map || !poi) {
return;
}
// Hämta rätt markör via poi.id
const marker = poiLayers.get(poi.id);
// Om ingen markör hittas ska funktionen avbrytas
if (!marker) {
return;
}
// Zooma till markören
map.setView([poi.lat, poi.lon], 14);
// Öppna popup för den valda markören
marker.openPopup();
}
/*=========== SOLSIDAN ============*/
/**
* Returnerar emoji för en väderplats.
*
* @param {object} place
* @returns {string}
*/
function getSunnyPlaceMarkerEmoji(place) {
return place.weatherIcon || "☀️";
}
/**
* Skapar en enkel emoji-ikon för vädermarkörer i Leaflet.
*
* @param {object} place
* @returns {L.DivIcon}
*/
function createSunnyPlaceMarkerIcon(place) {
const emoji = getSunnyPlaceMarkerEmoji(place);
return L.divIcon({
className: "poi-emoji-marker",
html: `<div style="font-size: 1.5rem; line-height: 1;">${emoji}</div>`,
iconSize: [28, 28],
iconAnchor: [14, 14],
popupAnchor: [0, -14]
});
}
/**
* Ritar ut solplatser som markörer på kartan.
*
* @param {object[]} places
*/
export function drawSunnyPlaces(places) {
if (!map) {
return;
}
/* Ta bort gamla markörer */
poiLayers.forEach((layer) => map.removeLayer(layer));
poiLayers.clear();
if (!places?.length) {
return;
}
const latLngs = [];
places.forEach((place) => {
const marker = L.marker([place.lat, place.lon], {
icon: createSunnyPlaceMarkerIcon(place)
})
.addTo(map)
.bindPopup(`
<strong>${place.weatherIcon || "☀️"} ${place.name}</strong><br>
${place.forecastGroup ? `${place.forecastGroup}: ` : ""}${place.weatherLabel}<br>
${place.temperature}°C<br>
<a href="https://www.google.com/maps/search/?api=1&query=${place.lat},${place.lon}" target="_blank" rel="noopener noreferrer">
Navigera hit
</a>
`);
poiLayers.set(place.id, marker);
/* Spara koordinater så att kartan kan zooma in runt hela området */
latLngs.push([place.lat, place.lon]);
});
/* Centrera och zooma så alla solplatser syns */
if (latLngs.length === 1) {
map.setView(latLngs[0], 9);
return;
}
const bounds = L.latLngBounds(latLngs);
map.fitBounds(bounds, {
padding: [40, 40],
maxZoom: 9
});
}
/**
* Zoomar kartan till en specifik solplats (t.ex. vid klick i listan/modal)
* och öppnar dess popup.
* @param {object} place
*/
export function showSunnyPlaceOnMap(place) {
if (!map || !place) {
return;
}
const marker = poiLayers.get(place.id);
if (!marker) {
return;
}
map.setView([place.lat, place.lon], 10);
marker.openPopup();
}