mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-24 16:03:02 +00:00
feat(client): show Dawarich stays, routes and marks where they belong
The trip planner and the journal get a "From Dawarich" list: the stays recorded for those dates, each one a row that can be dismissed or turned into a place or an entry after a review step that shows time, name, note and day. A place that came from a recording carries the Dawarich mark next to its name so its origin stays visible afterwards. The recorded route lies over the trip and journal maps as an overlay, coloured per day and cut along the reader's own timezone rather than UTC. It is fetched for the window on screen and never stored. The Atlas gets its own panel beside the map for the countries and cities the recordings prove, and the wish list is ticked off there, on the day of the visit, undoably. The phone has its own take on all of it, in the mobile shell's colours and with native sheets.
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
import { useEffect } from 'react'
|
||||
import type { Map } from 'mapbox-gl'
|
||||
import type { GeoJSONSource } from 'mapbox-gl'
|
||||
import type { DawarichTrack } from '@trek/shared'
|
||||
import { DAWARICH_TRAIL_CASING, trailGeoJson, trailSegments } from './dawarichTrail'
|
||||
|
||||
/**
|
||||
* The recorded route on the GL renderer — the imperative twin of
|
||||
* `DawarichTrailLayer`.
|
||||
*
|
||||
* The ids start with `trek-` because `applySatellite` finds the first layer
|
||||
* whose id carries one of TREK's own prefixes and inserts the satellite tile
|
||||
* beneath it. A layer named `dawarich-trail` would end up *under* the
|
||||
* satellite image and simply not be there when someone switched basemap on.
|
||||
*
|
||||
* Everything is rebuilt on `style.load`, because the whole GL map is torn down
|
||||
* and reassembled when the provider, the style or the token changes, and a
|
||||
* source added outside that effect does not survive it.
|
||||
*/
|
||||
const SOURCE = 'trek-dawarich-trail'
|
||||
const CASING_LAYER = 'trek-dawarich-trail-casing'
|
||||
const LINE_LAYER = 'trek-dawarich-trail-line'
|
||||
|
||||
export function useDawarichTrailGL(
|
||||
map: Map | null,
|
||||
ready: boolean,
|
||||
track: DawarichTrack | null | undefined,
|
||||
selectedDate?: string | null,
|
||||
/** Insert beneath this layer, so the planned route stays on top. */
|
||||
beforeId?: string,
|
||||
): void {
|
||||
useEffect(() => {
|
||||
if (!map || !ready) return
|
||||
|
||||
const data = trailGeoJson(trailSegments(track ?? null, selectedDate))
|
||||
|
||||
const draw = () => {
|
||||
if (!map.isStyleLoaded()) return
|
||||
const existing = map.getSource(SOURCE) as GeoJSONSource | undefined
|
||||
if (existing) {
|
||||
// setData rather than remove-and-add: a rebuilt source flickers, and a
|
||||
// refresh every two minutes would flicker every two minutes.
|
||||
existing.setData(data as never)
|
||||
return
|
||||
}
|
||||
map.addSource(SOURCE, { type: 'geojson', data: data as never })
|
||||
const anchor = beforeId && map.getLayer(beforeId) ? beforeId : undefined
|
||||
map.addLayer(
|
||||
{
|
||||
id: CASING_LAYER,
|
||||
type: 'line',
|
||||
source: SOURCE,
|
||||
layout: { 'line-cap': 'round', 'line-join': 'round' },
|
||||
// theme-lint-disable — map paint
|
||||
paint: { 'line-color': DAWARICH_TRAIL_CASING, 'line-width': 6, 'line-opacity': 0.55 },
|
||||
},
|
||||
anchor,
|
||||
)
|
||||
map.addLayer(
|
||||
{
|
||||
id: LINE_LAYER,
|
||||
type: 'line',
|
||||
source: SOURCE,
|
||||
layout: { 'line-cap': 'round', 'line-join': 'round' },
|
||||
paint: {
|
||||
// Per-day colour off the feature, with the same fallback the GPX
|
||||
// layer uses when a feature somehow carries none.
|
||||
'line-color': ['coalesce', ['get', 'color'], DAWARICH_TRAIL_CASING],
|
||||
'line-width': 3,
|
||||
'line-opacity': 0.85,
|
||||
// Dashed, so the recording is distinguishable from the planned
|
||||
// route without relying on colour alone.
|
||||
'line-dasharray': [2, 1.6],
|
||||
},
|
||||
},
|
||||
anchor,
|
||||
)
|
||||
}
|
||||
|
||||
draw()
|
||||
map.on('style.load', draw)
|
||||
return () => {
|
||||
map.off('style.load', draw)
|
||||
for (const layer of [LINE_LAYER, CASING_LAYER]) {
|
||||
if (map.getLayer(layer)) map.removeLayer(layer)
|
||||
}
|
||||
if (map.getSource(SOURCE)) map.removeSource(SOURCE)
|
||||
}
|
||||
}, [map, ready, track, selectedDate, beforeId])
|
||||
}
|
||||
Reference in New Issue
Block a user