mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-19 21:31:46 +00:00
25bdf56d16
- Mapbox GL provider alongside Leaflet for trip and journey maps (opt-in in settings with token, style presets incl. 3D on satellite, quality mode, experimental badge). - GPS "blue dot" with heading cone on mobile; three-state FAB (off / show / follow), geodesic accuracy circle, desktop-hidden since browser IP geo is too coarse for navigation. - Marker drift fix: outer wrap no longer carries inline position/transform, so mapbox's translate keeps the pin pinned at every zoom and pitch. - Journey map popup (mapbox-gl): Apple-Maps-style tooltip on marker highlight/click showing entry title + location / date subline. - Journey feed reorder: up/down controls to the left of each entry reorder sort_order within a day. Server endpoint, optimistic store update, rollback on failure. - Journey entry editor: desktop modal now centers over the feed column only, backdrop still blurs the whole page (map included). - Scroll-sync guard on journey: marker click locks the sync so smooth-scroll can't steer the highlight to a neighbouring entry mid-animation. - Misc: map top-padding aligned with hero, live/synced badges replaced by a compact back-button in the hero, skeleton entries no longer pollute the journey map, journey detail no longer shows map on mobile path when combined view is active.
102 lines
3.8 KiB
TypeScript
102 lines
3.8 KiB
TypeScript
import type mapboxgl from 'mapbox-gl'
|
|
|
|
// "mapbox/standard" and "mapbox/standard-satellite" ship their own 3D
|
|
// buildings and terrain. For every other style we inject a fill-extrusion
|
|
// layer against the classic `composite` vector source so the user still
|
|
// gets real 3D buildings (not just a tilted 2D view) when they toggle 3D.
|
|
export function isStandardFamily(style: string): boolean {
|
|
return style === 'mapbox://styles/mapbox/standard' || style === 'mapbox://styles/mapbox/standard-satellite'
|
|
}
|
|
|
|
// Terrain is only genuinely useful for the satellite imagery styles — on
|
|
// clean flat styles like streets/light/dark it nudges route lines onto
|
|
// the DEM while our HTML markers stay at Z=0, which causes the visible
|
|
// offset when the map is pitched. Restrict terrain to satellite.
|
|
export function wantsTerrain(style: string): boolean {
|
|
return style === 'mapbox://styles/mapbox/satellite-v9'
|
|
|| style === 'mapbox://styles/mapbox/satellite-streets-v12'
|
|
}
|
|
|
|
// 3D can be added to every style now — the standard family has it built-in
|
|
// and for everything else we either reuse the style's own `composite`
|
|
// building layer or attach the public `mapbox-streets-v8` tileset as an
|
|
// extra source (needed for pure satellite, which has no vector data).
|
|
export function supportsCustom3d(style: string): boolean {
|
|
return !isStandardFamily(style)
|
|
}
|
|
|
|
// Add a 3D buildings extrusion layer to a non-Standard Mapbox style. For
|
|
// the pure satellite style we lazily attach `mapbox-streets-v8` as a
|
|
// fallback source so real building volumes sit on top of the imagery —
|
|
// the Apple Maps-style "3D satellite" look the user asked for.
|
|
export function addCustom3dBuildings(map: mapboxgl.Map, dark: boolean) {
|
|
if (map.getLayer('trek-3d-buildings')) return
|
|
const baseColor = dark ? '#3b3b3f' : '#cfd2d6'
|
|
|
|
// Styles without a `composite` source (pure satellite) need a fallback
|
|
// vector tileset for building geometry.
|
|
let sourceId = 'composite'
|
|
if (!map.getSource('composite')) {
|
|
sourceId = 'mapbox-streets-v8'
|
|
if (!map.getSource(sourceId)) {
|
|
try {
|
|
map.addSource(sourceId, { type: 'vector', url: 'mapbox://mapbox.mapbox-streets-v8' })
|
|
} catch { return }
|
|
}
|
|
}
|
|
|
|
try {
|
|
// Place extrusions below the first label layer so text stays readable.
|
|
const layers = map.getStyle()?.layers || []
|
|
const firstSymbolId = layers.find(l => l.type === 'symbol')?.id
|
|
map.addLayer({
|
|
id: 'trek-3d-buildings',
|
|
source: sourceId,
|
|
'source-layer': 'building',
|
|
filter: ['==', 'extrude', 'true'],
|
|
type: 'fill-extrusion',
|
|
minzoom: 14,
|
|
paint: {
|
|
'fill-extrusion-color': baseColor,
|
|
'fill-extrusion-height': [
|
|
'interpolate', ['linear'], ['zoom'],
|
|
14, 0,
|
|
15.5, ['coalesce', ['get', 'height'], 0],
|
|
],
|
|
'fill-extrusion-base': [
|
|
'interpolate', ['linear'], ['zoom'],
|
|
14, 0,
|
|
15.5, ['coalesce', ['get', 'min_height'], 0],
|
|
],
|
|
'fill-extrusion-opacity': 0.85,
|
|
},
|
|
}, firstSymbolId)
|
|
} catch { /* building source-layer unavailable */ }
|
|
}
|
|
|
|
// Terrain + sky that works against any style that has the DEM source.
|
|
// The Standard family already handles terrain internally, skip there.
|
|
export function addTerrainAndSky(map: mapboxgl.Map) {
|
|
try {
|
|
if (!map.getSource('mapbox-dem')) {
|
|
map.addSource('mapbox-dem', {
|
|
type: 'raster-dem',
|
|
url: 'mapbox://mapbox.mapbox-terrain-dem-v1',
|
|
tileSize: 512,
|
|
maxzoom: 14,
|
|
})
|
|
}
|
|
map.setTerrain({ source: 'mapbox-dem', exaggeration: 1.2 })
|
|
if (!map.getLayer('sky')) {
|
|
map.addLayer({
|
|
id: 'sky',
|
|
type: 'sky',
|
|
paint: {
|
|
'sky-type': 'atmosphere',
|
|
'sky-atmosphere-sun-intensity': 15,
|
|
} as unknown as mapboxgl.SkyLayerSpecification['paint'],
|
|
})
|
|
}
|
|
} catch { /* style doesn't support terrain */ }
|
|
}
|