2025-10-17 23:54:45 +02:00
|
|
|
import { settings } from '$lib/logic/settings';
|
2025-10-19 14:15:52 +02:00
|
|
|
import { gpxStatistics } from '$lib/logic/statistics';
|
|
|
|
|
import { getConvertedDistanceToKilometers } from '$lib/units';
|
2025-06-21 21:07:36 +02:00
|
|
|
import type { GeoJSONSource } from 'mapbox-gl';
|
2025-02-02 11:17:22 +01:00
|
|
|
import { get } from 'svelte/store';
|
2025-10-19 14:15:52 +02:00
|
|
|
import { map } from '$lib/components/map/map';
|
2025-10-24 20:06:54 +02:00
|
|
|
import { allHidden } from '$lib/logic/hidden';
|
2024-05-23 11:21:57 +02:00
|
|
|
|
2025-10-17 23:54:45 +02:00
|
|
|
const { distanceMarkers, distanceUnits } = settings;
|
2024-05-23 11:21:57 +02:00
|
|
|
|
2025-02-02 11:17:22 +01:00
|
|
|
const stops = [
|
|
|
|
|
[100, 0],
|
|
|
|
|
[50, 7],
|
|
|
|
|
[25, 8, 10],
|
|
|
|
|
[10, 10],
|
|
|
|
|
[5, 11],
|
|
|
|
|
[1, 13],
|
|
|
|
|
];
|
2024-10-08 16:58:04 +02:00
|
|
|
|
2024-05-23 11:21:57 +02:00
|
|
|
export class DistanceMarkers {
|
|
|
|
|
updateBinded: () => void = this.update.bind(this);
|
2024-07-13 11:42:21 +02:00
|
|
|
unsubscribes: (() => void)[] = [];
|
2024-05-23 11:21:57 +02:00
|
|
|
|
2025-10-19 14:15:52 +02:00
|
|
|
constructor() {
|
2024-07-13 11:42:21 +02:00
|
|
|
this.unsubscribes.push(gpxStatistics.subscribe(this.updateBinded));
|
|
|
|
|
this.unsubscribes.push(distanceMarkers.subscribe(this.updateBinded));
|
|
|
|
|
this.unsubscribes.push(distanceUnits.subscribe(this.updateBinded));
|
2025-10-24 20:06:54 +02:00
|
|
|
this.unsubscribes.push(allHidden.subscribe(this.updateBinded));
|
2025-10-19 14:15:52 +02:00
|
|
|
this.unsubscribes.push(
|
|
|
|
|
map.subscribe((map_) => {
|
|
|
|
|
if (map_) {
|
|
|
|
|
map_.on('style.import.load', this.updateBinded);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
);
|
2024-05-23 11:21:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
update() {
|
2025-10-19 14:15:52 +02:00
|
|
|
const map_ = get(map);
|
|
|
|
|
if (!map_) return;
|
|
|
|
|
|
2024-05-23 11:21:57 +02:00
|
|
|
try {
|
2025-10-24 20:06:54 +02:00
|
|
|
if (get(distanceMarkers) && !get(allHidden)) {
|
2025-10-19 14:15:52 +02:00
|
|
|
let distanceSource: GeoJSONSource | undefined = map_.getSource('distance-markers');
|
2024-05-23 11:21:57 +02:00
|
|
|
if (distanceSource) {
|
|
|
|
|
distanceSource.setData(this.getDistanceMarkersGeoJSON());
|
|
|
|
|
} else {
|
2025-10-19 14:15:52 +02:00
|
|
|
map_.addSource('distance-markers', {
|
2024-05-23 11:21:57 +02:00
|
|
|
type: 'geojson',
|
2025-02-02 11:17:22 +01:00
|
|
|
data: this.getDistanceMarkersGeoJSON(),
|
2024-05-23 11:21:57 +02:00
|
|
|
});
|
|
|
|
|
}
|
2024-10-08 16:58:04 +02:00
|
|
|
stops.forEach(([d, minzoom, maxzoom]) => {
|
2025-10-19 14:15:52 +02:00
|
|
|
if (!map_.getLayer(`distance-markers-${d}`)) {
|
|
|
|
|
map_.addLayer({
|
2024-10-08 16:58:04 +02:00
|
|
|
id: `distance-markers-${d}`,
|
|
|
|
|
type: 'symbol',
|
|
|
|
|
source: 'distance-markers',
|
2025-02-02 11:17:22 +01:00
|
|
|
filter:
|
|
|
|
|
d === 5
|
|
|
|
|
? [
|
|
|
|
|
'any',
|
|
|
|
|
['==', ['get', 'level'], 5],
|
|
|
|
|
['==', ['get', 'level'], 25],
|
|
|
|
|
]
|
|
|
|
|
: ['==', ['get', 'level'], d],
|
2024-10-08 16:58:04 +02:00
|
|
|
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',
|
2025-02-02 11:17:22 +01:00
|
|
|
},
|
2024-10-08 16:58:04 +02:00
|
|
|
});
|
|
|
|
|
} else {
|
2025-10-19 14:15:52 +02:00
|
|
|
map_.moveLayer(`distance-markers-${d}`);
|
2024-10-08 16:58:04 +02:00
|
|
|
}
|
|
|
|
|
});
|
2024-05-23 11:21:57 +02:00
|
|
|
} else {
|
2024-10-08 16:58:04 +02:00
|
|
|
stops.forEach(([d]) => {
|
2025-10-19 14:15:52 +02:00
|
|
|
if (map_.getLayer(`distance-markers-${d}`)) {
|
|
|
|
|
map_.removeLayer(`distance-markers-${d}`);
|
2024-10-08 16:58:04 +02:00
|
|
|
}
|
|
|
|
|
});
|
2024-05-23 11:21:57 +02:00
|
|
|
}
|
2025-02-02 11:17:22 +01:00
|
|
|
} catch (e) {
|
|
|
|
|
// No reliable way to check if the map is ready to add sources and layers
|
2024-05-23 11:21:57 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-13 11:42:21 +02:00
|
|
|
|
|
|
|
|
remove() {
|
2025-02-02 11:17:22 +01:00
|
|
|
this.unsubscribes.forEach((unsubscribe) => unsubscribe());
|
2024-07-13 11:42:21 +02:00
|
|
|
}
|
2024-05-23 11:21:57 +02:00
|
|
|
|
|
|
|
|
getDistanceMarkersGeoJSON(): GeoJSON.FeatureCollection {
|
|
|
|
|
let statistics = get(gpxStatistics);
|
|
|
|
|
|
|
|
|
|
let features = [];
|
|
|
|
|
let currentTargetDistance = 1;
|
2024-06-11 22:42:03 +02:00
|
|
|
for (let i = 0; i < statistics.local.distance.total.length; i++) {
|
2025-02-02 11:17:22 +01:00
|
|
|
if (
|
|
|
|
|
statistics.local.distance.total[i] >=
|
2025-10-19 14:15:52 +02:00
|
|
|
getConvertedDistanceToKilometers(currentTargetDistance)
|
2025-02-02 11:17:22 +01:00
|
|
|
) {
|
2024-05-23 11:21:57 +02:00
|
|
|
let distance = currentTargetDistance.toFixed(0);
|
2025-02-02 11:17:22 +01:00
|
|
|
let [level, minzoom] = stops.find(([d]) => currentTargetDistance % d === 0) ?? [
|
|
|
|
|
0, 0,
|
|
|
|
|
];
|
2024-05-23 11:21:57 +02:00
|
|
|
features.push({
|
|
|
|
|
type: 'Feature',
|
|
|
|
|
geometry: {
|
|
|
|
|
type: 'Point',
|
2025-02-02 11:17:22 +01:00
|
|
|
coordinates: [
|
|
|
|
|
statistics.local.points[i].getLongitude(),
|
|
|
|
|
statistics.local.points[i].getLatitude(),
|
|
|
|
|
],
|
2024-05-23 11:21:57 +02:00
|
|
|
},
|
|
|
|
|
properties: {
|
|
|
|
|
distance,
|
2024-10-08 16:58:04 +02:00
|
|
|
level,
|
|
|
|
|
minzoom,
|
2025-02-02 11:17:22 +01:00
|
|
|
},
|
2024-05-23 11:21:57 +02:00
|
|
|
} as GeoJSON.Feature);
|
|
|
|
|
currentTargetDistance += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
type: 'FeatureCollection',
|
2025-02-02 11:17:22 +01:00
|
|
|
features,
|
2024-05-23 11:21:57 +02:00
|
|
|
};
|
|
|
|
|
}
|
2025-02-02 11:17:22 +01:00
|
|
|
}
|