Files
gpx.studio/website/src/lib/components/map/gpx-layer/distance-markers.ts

140 lines
5.0 KiB
TypeScript
Raw Normal View History

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';
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
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',
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',
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',
},
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
}
} 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() {
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++) {
if (
statistics.local.distance.total[i] >=
2025-10-19 14:15:52 +02:00
getConvertedDistanceToKilometers(currentTargetDistance)
) {
2024-05-23 11:21:57 +02:00
let distance = currentTargetDistance.toFixed(0);
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',
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,
},
2024-05-23 11:21:57 +02:00
} as GeoJSON.Feature);
currentTargetDistance += 1;
}
}
return {
type: 'FeatureCollection',
features,
2024-05-23 11:21:57 +02:00
};
}
}