improve layer stacking

This commit is contained in:
vcoppe
2026-01-30 21:30:37 +01:00
parent 2a4dfe010e
commit 9c6e03f4a8
9 changed files with 155 additions and 124 deletions

View File

@@ -48,7 +48,7 @@
language = 'en'; language = 'en';
} }
map.init(PUBLIC_MAPBOX_TOKEN, language, hash, geocoder, geolocate); map.init(language, hash, geocoder, geolocate);
}); });
onDestroy(() => { onDestroy(() => {

View File

@@ -3,7 +3,7 @@ import { gpxStatistics } from '$lib/logic/statistics';
import { getConvertedDistanceToKilometers } from '$lib/units'; import { getConvertedDistanceToKilometers } from '$lib/units';
import type { GeoJSONSource } from 'mapbox-gl'; import type { GeoJSONSource } from 'mapbox-gl';
import { get } from 'svelte/store'; import { get } from 'svelte/store';
import { map } from '$lib/components/map/map'; import { ANCHOR_LAYER_KEY, map } from '$lib/components/map/map';
import { allHidden } from '$lib/logic/hidden'; import { allHidden } from '$lib/logic/hidden';
const { distanceMarkers, distanceUnits } = settings; const { distanceMarkers, distanceUnits } = settings;
@@ -44,44 +44,45 @@ export class DistanceMarkers {
}); });
} }
if (!map_.getLayer('distance-markers')) { if (!map_.getLayer('distance-markers')) {
map_.addLayer({ map_.addLayer(
id: 'distance-markers', {
type: 'symbol', id: 'distance-markers',
source: 'distance-markers', type: 'symbol',
filter: [ source: 'distance-markers',
'match', filter: [
['get', 'level'], 'match',
100, ['get', 'level'],
['>=', ['zoom'], 0], 100,
50, ['>=', ['zoom'], 0],
['>=', ['zoom'], 7], 50,
25, ['>=', ['zoom'], 7],
[ 25,
'any', [
['all', ['>=', ['zoom'], 8], ['<=', ['zoom'], 9]], 'any',
['all', ['>=', ['zoom'], 8], ['<=', ['zoom'], 9]],
['>=', ['zoom'], 11],
],
10,
['>=', ['zoom'], 10],
5,
['>=', ['zoom'], 11], ['>=', ['zoom'], 11],
1,
['>=', ['zoom'], 13],
false,
], ],
10, layout: {
['>=', ['zoom'], 10], 'text-field': ['get', 'distance'],
5, 'text-size': 14,
['>=', ['zoom'], 11], 'text-font': ['Open Sans Bold'],
1, },
['>=', ['zoom'], 13], paint: {
false, 'text-color': 'black',
], 'text-halo-width': 2,
layout: { 'text-halo-color': 'white',
'text-field': ['get', 'distance'], },
'text-size': 14,
'text-font': ['Open Sans Bold'],
}, },
paint: { ANCHOR_LAYER_KEY.distanceMarkers
'text-color': 'black', );
'text-halo-width': 2,
'text-halo-color': 'white',
},
});
} else {
map_.moveLayer('distance-markers');
} }
} else { } else {
if (map_.getLayer('distance-markers')) { if (map_.getLayer('distance-markers')) {

View File

@@ -1,6 +1,6 @@
import { get, type Readable } from 'svelte/store'; import { get, type Readable } from 'svelte/store';
import mapboxgl, { type FilterSpecification } from 'mapbox-gl'; import mapboxgl, { type FilterSpecification } from 'mapbox-gl';
import { map } from '$lib/components/map/map'; import { ANCHOR_LAYER_KEY, map } from '$lib/components/map/map';
import { waypointPopup, trackpointPopup } from './gpx-layer-popup'; import { waypointPopup, trackpointPopup } from './gpx-layer-popup';
import { import {
ListTrackSegmentItem, ListTrackSegmentItem,
@@ -196,20 +196,23 @@ export class GPXLayer {
} }
if (!_map.getLayer(this.fileId)) { if (!_map.getLayer(this.fileId)) {
_map.addLayer({ _map.addLayer(
id: this.fileId, {
type: 'line', id: this.fileId,
source: this.fileId, type: 'line',
layout: { source: this.fileId,
'line-join': 'round', layout: {
'line-cap': 'round', 'line-join': 'round',
'line-cap': 'round',
},
paint: {
'line-color': ['get', 'color'],
'line-width': ['get', 'width'],
'line-opacity': ['get', 'opacity'],
},
}, },
paint: { ANCHOR_LAYER_KEY.tracks
'line-color': ['get', 'color'], );
'line-width': ['get', 'width'],
'line-opacity': ['get', 'opacity'],
},
});
_map.on('click', this.fileId, this.layerOnClickBinded); _map.on('click', this.fileId, this.layerOnClickBinded);
_map.on('contextmenu', this.fileId, this.layerOnContextMenuBinded); _map.on('contextmenu', this.fileId, this.layerOnContextMenuBinded);
@@ -232,18 +235,21 @@ export class GPXLayer {
} }
if (!_map.getLayer(this.fileId + '-waypoints')) { if (!_map.getLayer(this.fileId + '-waypoints')) {
_map.addLayer({ _map.addLayer(
id: this.fileId + '-waypoints', {
type: 'symbol', id: this.fileId + '-waypoints',
source: this.fileId + '-waypoints', type: 'symbol',
layout: { source: this.fileId + '-waypoints',
'icon-image': ['get', 'icon'], layout: {
'icon-size': 0.3, 'icon-image': ['get', 'icon'],
'icon-anchor': 'bottom', 'icon-size': 0.3,
'icon-padding': 0, 'icon-anchor': 'bottom',
'icon-allow-overlap': true, 'icon-padding': 0,
'icon-allow-overlap': true,
},
}, },
}); ANCHOR_LAYER_KEY.waypoints
);
_map.on( _map.on(
'mouseenter', 'mouseenter',
@@ -292,7 +298,7 @@ export class GPXLayer {
'text-halo-color': 'white', 'text-halo-color': 'white',
}, },
}, },
_map.getLayer('distance-markers') ? 'distance-markers' : undefined ANCHOR_LAYER_KEY.directionMarkers
); );
} }
} else { } else {
@@ -393,13 +399,13 @@ export class GPXLayer {
return; return;
} }
if (_map.getLayer(this.fileId)) { if (_map.getLayer(this.fileId)) {
_map.moveLayer(this.fileId); _map.moveLayer(this.fileId, ANCHOR_LAYER_KEY.tracks);
} }
if (_map.getLayer(this.fileId + '-waypoints')) { if (_map.getLayer(this.fileId + '-waypoints')) {
_map.moveLayer(this.fileId + '-waypoints'); _map.moveLayer(this.fileId + '-waypoints', ANCHOR_LAYER_KEY.waypoints);
} }
if (_map.getLayer(this.fileId + '-direction')) { if (_map.getLayer(this.fileId + '-direction')) {
_map.moveLayer(this.fileId + '-direction'); _map.moveLayer(this.fileId + '-direction', ANCHOR_LAYER_KEY.directionMarkers);
} }
} }

View File

@@ -6,6 +6,7 @@ import { overpassQueryData } from '$lib/assets/layers';
import { MapPopup } from '$lib/components/map/map-popup'; import { MapPopup } from '$lib/components/map/map-popup';
import { settings } from '$lib/logic/settings'; import { settings } from '$lib/logic/settings';
import { db } from '$lib/db'; import { db } from '$lib/db';
import { ANCHOR_LAYER_KEY } from '$lib/components/map/map';
const { currentOverpassQueries } = settings; const { currentOverpassQueries } = settings;
@@ -85,17 +86,20 @@ export class OverpassLayer {
} }
if (!this.map.getLayer('overpass')) { if (!this.map.getLayer('overpass')) {
this.map.addLayer({ this.map.addLayer(
id: 'overpass', {
type: 'symbol', id: 'overpass',
source: 'overpass', type: 'symbol',
layout: { source: 'overpass',
'icon-image': ['get', 'icon'], layout: {
'icon-size': 0.25, 'icon-image': ['get', 'icon'],
'icon-padding': 0, 'icon-size': 0.25,
'icon-allow-overlap': ['step', ['zoom'], false, 14, true], 'icon-padding': 0,
'icon-allow-overlap': ['step', ['zoom'], false, 14, true],
},
}, },
}); ANCHOR_LAYER_KEY.overpass
);
this.map.on('mouseenter', 'overpass', this.onHoverBinded); this.map.on('mouseenter', 'overpass', this.onHoverBinded);
this.map.on('click', 'overpass', this.onHoverBinded); this.map.on('click', 'overpass', this.onHoverBinded);

View File

@@ -20,6 +20,28 @@ let fitBoundsOptions: mapboxgl.MapOptions['fitBoundsOptions'] = {
easing: () => 1, easing: () => 1,
}; };
const emptySource: mapboxgl.GeoJSONSourceSpecification = {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: [],
},
};
export const ANCHOR_LAYER_KEY = {
mapillary: 'mapillary-end',
tracks: 'tracks-end',
directionMarkers: 'direction-markers-end',
distanceMarkers: 'distance-markers-end',
interactions: 'interactions-end',
overpass: 'overpass-end',
waypoints: 'waypoints-end',
};
const anchorLayers: mapboxgl.LayerSpecification[] = Object.values(ANCHOR_LAYER_KEY).map((id) => ({
id: id,
type: 'symbol',
source: 'empty-source',
}));
export class MapboxGLMap { export class MapboxGLMap {
private _map: Writable<mapboxgl.Map | null> = writable(null); private _map: Writable<mapboxgl.Map | null> = writable(null);
private _onLoadCallbacks: ((map: mapboxgl.Map) => void)[] = []; private _onLoadCallbacks: ((map: mapboxgl.Map) => void)[] = [];
@@ -29,19 +51,15 @@ export class MapboxGLMap {
return this._map.subscribe(run, invalidate); return this._map.subscribe(run, invalidate);
} }
init( init(language: string, hash: boolean, geocoder: boolean, geolocate: boolean) {
accessToken: string,
language: string,
hash: boolean,
geocoder: boolean,
geolocate: boolean
) {
const map = new mapboxgl.Map({ const map = new mapboxgl.Map({
container: 'map', container: 'map',
style: { style: {
version: 8, version: 8,
sources: {}, sources: {
layers: [], 'empty-source': emptySource,
},
layers: anchorLayers,
imports: [ imports: [
{ {
id: 'basemap', id: 'basemap',
@@ -50,11 +68,6 @@ export class MapboxGLMap {
{ {
id: 'overlays', id: 'overlays',
url: '', url: '',
data: {
version: 8,
sources: {},
layers: [],
},
}, },
], ],
}, },

View File

@@ -2,6 +2,7 @@ import mapboxgl, { type LayerSpecification, type VectorSourceSpecification } fro
import { Viewer, type ViewerBearingEvent } from 'mapillary-js/dist/mapillary.module'; import { Viewer, type ViewerBearingEvent } from 'mapillary-js/dist/mapillary.module';
import 'mapillary-js/dist/mapillary.css'; import 'mapillary-js/dist/mapillary.css';
import { mapCursor, MapCursorState } from '$lib/logic/map-cursor'; import { mapCursor, MapCursorState } from '$lib/logic/map-cursor';
import { ANCHOR_LAYER_KEY } from '$lib/components/map/map';
const mapillarySource: VectorSourceSpecification = { const mapillarySource: VectorSourceSpecification = {
type: 'vector', type: 'vector',
@@ -99,10 +100,10 @@ export class MapillaryLayer {
this.map.addSource('mapillary', mapillarySource); this.map.addSource('mapillary', mapillarySource);
} }
if (!this.map.getLayer('mapillary-sequence')) { if (!this.map.getLayer('mapillary-sequence')) {
this.map.addLayer(mapillarySequenceLayer); this.map.addLayer(mapillarySequenceLayer, ANCHOR_LAYER_KEY.mapillary);
} }
if (!this.map.getLayer('mapillary-image')) { if (!this.map.getLayer('mapillary-image')) {
this.map.addLayer(mapillaryImageLayer); this.map.addLayer(mapillaryImageLayer, ANCHOR_LAYER_KEY.mapillary);
} }
this.map.on('style.load', this.addBinded); this.map.on('style.load', this.addBinded);
this.map.on('mouseenter', 'mapillary-image', this.onMouseEnterBinded); this.map.on('mouseenter', 'mapillary-image', this.onMouseEnterBinded);

View File

@@ -15,7 +15,7 @@
import { onDestroy, onMount } from 'svelte'; import { onDestroy, onMount } from 'svelte';
import { getURLForLanguage } from '$lib/utils'; import { getURLForLanguage } from '$lib/utils';
import { Trash2 } from '@lucide/svelte'; import { Trash2 } from '@lucide/svelte';
import { map } from '$lib/components/map/map'; import { ANCHOR_LAYER_KEY, map } from '$lib/components/map/map';
import type { GeoJSONSource } from 'mapbox-gl'; import type { GeoJSONSource } from 'mapbox-gl';
import { selection } from '$lib/logic/selection'; import { selection } from '$lib/logic/selection';
import { fileActions } from '$lib/logic/file-actions'; import { fileActions } from '$lib/logic/file-actions';
@@ -63,15 +63,18 @@
}); });
} }
if (!$map.getLayer('rectangle')) { if (!$map.getLayer('rectangle')) {
$map.addLayer({ $map.addLayer(
id: 'rectangle', {
type: 'fill', id: 'rectangle',
source: 'rectangle', type: 'fill',
paint: { source: 'rectangle',
'fill-color': 'SteelBlue', paint: {
'fill-opacity': 0.5, 'fill-color': 'SteelBlue',
'fill-opacity': 0.5,
},
}, },
}); ANCHOR_LAYER_KEY.interactions
);
} }
} }
} }

View File

@@ -1,5 +1,5 @@
import { ListItem, ListTrackSegmentItem } from '$lib/components/file-list/file-list'; import { ListItem, ListTrackSegmentItem } from '$lib/components/file-list/file-list';
import { map } from '$lib/components/map/map'; import { ANCHOR_LAYER_KEY, map } from '$lib/components/map/map';
import { fileActions } from '$lib/logic/file-actions'; import { fileActions } from '$lib/logic/file-actions';
import { GPXFileStateCollectionObserver, type GPXFileState } from '$lib/logic/file-state'; import { GPXFileStateCollectionObserver, type GPXFileState } from '$lib/logic/file-state';
import { selection } from '$lib/logic/selection'; import { selection } from '$lib/logic/selection';
@@ -144,17 +144,18 @@ export class ReducedGPXLayerCollection {
}); });
} }
if (!map_.getLayer('simplified')) { if (!map_.getLayer('simplified')) {
map_.addLayer({ map_.addLayer(
id: 'simplified', {
type: 'line', id: 'simplified',
source: 'simplified', type: 'line',
paint: { source: 'simplified',
'line-color': 'white', paint: {
'line-width': 3, 'line-color': 'white',
'line-width': 3,
},
}, },
}); ANCHOR_LAYER_KEY.interactions
} else { );
map_.moveLayer('simplified');
} }
} }

View File

@@ -8,6 +8,7 @@ import { get } from 'svelte/store';
import { fileStateCollection } from '$lib/logic/file-state'; import { fileStateCollection } from '$lib/logic/file-state';
import { fileActions } from '$lib/logic/file-actions'; import { fileActions } from '$lib/logic/file-actions';
import { mapCursor, MapCursorState } from '$lib/logic/map-cursor'; import { mapCursor, MapCursorState } from '$lib/logic/map-cursor';
import { ANCHOR_LAYER_KEY } from '$lib/components/map/map';
export class SplitControls { export class SplitControls {
map: mapboxgl.Map; map: mapboxgl.Map;
@@ -108,24 +109,25 @@ export class SplitControls {
} }
if (!this.map.getLayer('split-controls')) { if (!this.map.getLayer('split-controls')) {
this.map.addLayer({ this.map.addLayer(
id: 'split-controls', {
type: 'symbol', id: 'split-controls',
source: 'split-controls', type: 'symbol',
layout: { source: 'split-controls',
'icon-image': 'split-control', layout: {
'icon-size': 0.25, 'icon-image': 'split-control',
'icon-padding': 0, 'icon-size': 0.25,
'icon-padding': 0,
},
filter: ['<=', ['get', 'minZoom'], ['zoom']],
}, },
filter: ['<=', ['get', 'minZoom'], ['zoom']], ANCHOR_LAYER_KEY.interactions
}); );
this.map.on('mouseenter', 'split-controls', this.layerOnMouseEnterBinded); this.map.on('mouseenter', 'split-controls', this.layerOnMouseEnterBinded);
this.map.on('mouseleave', 'split-controls', this.layerOnMouseLeaveBinded); this.map.on('mouseleave', 'split-controls', this.layerOnMouseLeaveBinded);
this.map.on('click', 'split-controls', this.layerOnClickBinded); this.map.on('click', 'split-controls', this.layerOnClickBinded);
} }
this.map.moveLayer('split-controls');
} catch (e) { } catch (e) {
// No reliable way to check if the map is ready to add sources and layers // No reliable way to check if the map is ready to add sources and layers
} }