Files
leim-maps/src/components/maps/WorldMap.astro
Alexis db69bd7e6f Removed nanostore logic
This module was way too cumbersome for such a simple implementation ; now the resources are just plain json loaded in astro instead of full on API routes.
This also eliminates the need to have a backend service to serve the API and the app can be deployed statically
2023-12-08 14:01:59 +01:00

247 lines
5.8 KiB
Plaintext

---
import type { MapMarker, PlayerMarker } from '@/types/Leaflet';
import markersData from '@/components/maps/data/markers.json'
import playersData from '@/components/maps/data/players.json'
const markers = markersData.data as MapMarker[]
const players = playersData as PlayerMarker
---
<div class="world-wrapper">
<div id="world"></div>
</div>
<script lang="ts" define:vars={{ markers, players }} defer>
const mapHeight = 15168;
const mapWidth = 14658;
const map = L.map('world', {
crs: L.CRS.Simple,
maxZoom: 12,
minZoom: 2.5,
zoom: 2.5,
zoomSnap: 0,
zoomControl: false
});
L.control.zoom({
position: 'bottomright',
}).addTo(map);
// Layers
const layer = L.tileLayer.zoomify('/zoomify/alliance-kaldelienne/{g}/{z}-{x}-{y}.jpg', {
width: mapWidth,
height: mapHeight,
}).addTo(map);
const layerGroups = {
players: L.layerGroup(),
capitals: L.layerGroup(),
cities: L.layerGroup(),
towns: L.layerGroup(),
landmarks: L.layerGroup(),
};
const flyToDuration = 1.2; // In seconds
/**
* Build all markers and their related info
*/
for (let i = 0; i < markers.length; i++) {
const m = markers[i];
const coords = [m.markerCoords.y, m.markerCoords.x];
let markerIcon
switch (m.group) {
case "capitals":
markerIcon = L.icon({
iconUrl: `icon/castle.png`,
shadowUrl: `icon/castle-shadow.png`,
iconSize: [25, 25],
shadowSize: [35, 30],
iconAnchor: [12.5, 8],
shadowAnchor: [11, 12]
});
break;
case "cities":
markerIcon = L.icon({
iconUrl: `icon/circle.png`,
iconSize: [12, 12],
});
break;
case "landmarks":
markerIcon = L.icon({
iconUrl: `icon/monument.png`,
shadowUrl: `icon/monument-shadow.png`,
iconSize: [18, 18],
shadowSize: [32, 14],
iconAnchor: [12.5, 8],
shadowAnchor: [15, 6]
});
break;
case "towns":
default:
iconKey = "house"
break;
}
const marker = L.marker(coords, { icon: markerIcon }).addTo(map);
let popupContent = "";
if (m.link) {
popupContent = `
<a href="${m.link}" target="_blank" class="title">
${m.title}
</a>
<p>${m.description}</p>
`;
} else {
popupContent = `
<strong class="title">
${m.title}
</strong>
<p>${m.description}</p>
`;
}
marker.bindPopup(popupContent).openPopup();
const displayTooltip = m.group === 'capitals';
if (displayTooltip) {
L.tooltip({ permanent: true, direction: 'top', offset: [0, 2], content: m.title, className: "capital-name", opacity: 1 }).setLatLng(coords).addTo(map);
}
marker.addTo(layerGroups[m.group])
/**
* Display popup of marker
*/
document.addEventListener(`fly-to-${m.title}`, () => {
map.flyTo(coords, 5, { duration: flyToDuration })
setTimeout(() => {
marker.openPopup()
}, flyToDuration * 1000)
})
}
/**
* Add player's position marker
*/
if ( players ) {
const playersPosition = [players.markerCoords.y, players.markerCoords.x];
const playerIcon = L.icon({
iconUrl: `icon/location-pin.png`,
shadowUrl: `icon/location-pin-shadow.png`,
iconSize: [18, 24],
shadowSize: [20, 16],
iconAnchor: [11, 8],
shadowAnchor: [3, 0],
});
const playerMarker = L.marker(playersPosition, { icon: playerIcon }).addTo(map)
playerMarker.addTo(layerGroups['players'])
const popupContent = `
<strong class="title">
${players.title}
</strong>
<p>${players.description}</p>
`;
playerMarker.bindPopup(popupContent).openPopup();
/**
* Flying to players
*/
document.addEventListener('fly-to-players', () => {
map.flyTo(playersPosition, 5, { duration: flyToDuration })
setTimeout(() => {
playerMarker.openPopup()
}, flyToDuration * 1000)
})
}
map.fitBounds(layer.getBounds());
// L.control.layers({ "base": layer }, layerGroups).addTo(map);
/**
* RULER
*/
/**
* Workaround specific to leaflet version
* See https://github.com/ljagis/leaflet-measure/issues/171 for details
*/
L.Control.Measure.include({
// set icon on the capture marker
_setCaptureMarkerIcon: function () {
// disable autopan
this._captureMarker.options.autoPanOnFocus = false;
// default function
this._captureMarker.setIcon(
L.divIcon({
iconSize: this._map.getSize().multiplyBy(2)
})
);
},
});
const rulerOptions = {
position: 'topright',
primaryLengthUnit: 'kilometers',
secondaryLengthUnit: 'days',
primaryAreaUnit: 'hectares',
units: {
kilometers: {
factor: 0.00013,
display: 'Kilomètres',
},
days: {
factor: 0.000002,
display: 'Jours de marche',
decimals: 1
}
}
};
const measureControl = L.control.measure(rulerOptions);
measureControl.addTo(map);
/**
* DEBUGS
*/
map.addEventListener('click', (event) => {
let lat = Math.round(event.latlng.lat * 100000) / 100000;
let lng = Math.round(event.latlng.lng * 100000) / 100000;
console.log(lat, lng);
})
</script>
<style lang="scss">
html,
body {
height: 100vh;
margin: 0;
position: relative;
}
.world-wrapper {
position: fixed;
inset: 0;
#world {
height: 100%;
width: 100%;
}
}
.leaflet-container {
aspect-ratio: 1551 / 1605;
}
</style>