import { settings } from '$lib/logic/settings'; import { gpxStatistics } from '$lib/logic/statistics'; import { getConvertedDistanceToKilometers } from '$lib/units'; import type { GeoJSONSource } from 'mapbox-gl'; import { get } from 'svelte/store'; import { map } from '$lib/components/map/map'; const { distanceMarkers, distanceUnits } = settings; const stops = [ [100, 0], [50, 7], [25, 8, 10], [10, 10], [5, 11], [1, 13], ]; export class DistanceMarkers { updateBinded: () => void = this.update.bind(this); unsubscribes: (() => void)[] = []; constructor() { this.unsubscribes.push(gpxStatistics.subscribe(this.updateBinded)); this.unsubscribes.push(distanceMarkers.subscribe(this.updateBinded)); this.unsubscribes.push(distanceUnits.subscribe(this.updateBinded)); this.unsubscribes.push( map.subscribe((map_) => { if (map_) { map_.on('style.import.load', this.updateBinded); } }) ); } update() { const map_ = get(map); if (!map_) return; try { if (get(distanceMarkers)) { let distanceSource: GeoJSONSource | undefined = map_.getSource('distance-markers'); if (distanceSource) { distanceSource.setData(this.getDistanceMarkersGeoJSON()); } else { map_.addSource('distance-markers', { type: 'geojson', data: this.getDistanceMarkersGeoJSON(), }); } stops.forEach(([d, minzoom, maxzoom]) => { if (!map_.getLayer(`distance-markers-${d}`)) { map_.addLayer({ id: `distance-markers-${d}`, type: 'symbol', source: 'distance-markers', filter: d === 5 ? [ 'any', ['==', ['get', 'level'], 5], ['==', ['get', 'level'], 25], ] : ['==', ['get', 'level'], d], minzoom: minzoom, maxzoom: maxzoom ?? 24, layout: { 'text-field': ['get', 'distance'], 'text-size': 14, 'text-font': ['Open Sans Bold'], }, paint: { 'text-color': 'black', 'text-halo-width': 2, 'text-halo-color': 'white', }, }); } else { map_.moveLayer(`distance-markers-${d}`); } }); } else { stops.forEach(([d]) => { if (map_.getLayer(`distance-markers-${d}`)) { map_.removeLayer(`distance-markers-${d}`); } }); } } catch (e) { // No reliable way to check if the map is ready to add sources and layers return; } } remove() { this.unsubscribes.forEach((unsubscribe) => unsubscribe()); } getDistanceMarkersGeoJSON(): GeoJSON.FeatureCollection { let statistics = get(gpxStatistics); let features = []; let currentTargetDistance = 1; for (let i = 0; i < statistics.local.distance.total.length; i++) { if ( statistics.local.distance.total[i] >= getConvertedDistanceToKilometers(currentTargetDistance) ) { let distance = currentTargetDistance.toFixed(0); let [level, minzoom] = stops.find(([d]) => currentTargetDistance % d === 0) ?? [ 0, 0, ]; features.push({ type: 'Feature', geometry: { type: 'Point', coordinates: [ statistics.local.points[i].getLongitude(), statistics.local.points[i].getLatitude(), ], }, properties: { distance, level, minzoom, }, } as GeoJSON.Feature); currentTargetDistance += 1; } } return { type: 'FeatureCollection', features, }; } }