mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-11-13 15:51:00 +00:00
e28bd2fc99
MapLibre 6 and Mapbox GL 3 both throw out of their Map constructor without WebGL2. Every place that builds a GL map now asks one helper first, components/Map/glSupport.ts, and a browser without WebGL2 gets the Leaflet map with the user's tiles instead of a map that fails to start: - the trip map (MapViewAuto) and the journey maps (JourneyMapAuto) pick their renderer through useGlRenderer, which folds in the Mapbox token rule they each had; - the settings preview, desktop and phone, shows the Leaflet preview through useMapSettingsForm and says why under it; - the tour planner keeps the 2D map when 3D is asked for and says that 3D needs WebGL2; - the PDF route map and the Leaflet vector basemap already fell back and now report a GPU failure too. Behind the probe, buildGlMap wraps each constructor (MapViewGL, JourneyMapGL, the settings preview, the terrain map, the PDF map). A GPU that claims WebGL2 and still refuses the context is reported through utils/webgl, which turns the answer to no and tells every mounted map, so they switch to Leaflet without a reload; any other error is still thrown. Two new strings, map.webgl2.fallback and map.webgl2.terrain, in all 27 locales.
98 lines
3.5 KiB
TypeScript
98 lines
3.5 KiB
TypeScript
import { useEffect, useRef } from 'react'
|
|
import { isStandardFamily, supportsCustom3d, addCustom3dBuildings, addTerrainAndSky } from '../Map/mapboxSetup'
|
|
import { MAPBOX_DEFAULT_STYLE, normalizeStyleForProvider, type GlMapProvider } from '../Map/glProviders'
|
|
import { buildGlMap } from '../Map/glSupport'
|
|
import { fs } from '../shared/DialogShell'
|
|
|
|
interface Props {
|
|
provider?: GlMapProvider
|
|
token?: string
|
|
style: string
|
|
lat: number
|
|
lng: number
|
|
zoom: number
|
|
enable3d: boolean
|
|
quality?: boolean
|
|
onClick?: (latlng: { lat: number; lng: number }) => void
|
|
/**
|
|
* The GL engine, injected instead of imported. Both SDKs used to be pulled in
|
|
* statically here, so a single 2.8 MB chunk carried mapbox-gl and maplibre-gl
|
|
* together and every map user downloaded both while only one ever ran.
|
|
*/
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
gl: any
|
|
}
|
|
|
|
export default function GlMapPreview({ provider = 'mapbox-gl', token = '', style, lat, lng, zoom, enable3d, quality = false, onClick, gl }: Props) {
|
|
const containerRef = useRef<HTMLDivElement>(null)
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const mapRef = useRef<any | null>(null)
|
|
const onClickRef = useRef(onClick)
|
|
onClickRef.current = onClick
|
|
const isMapLibre = provider === 'maplibre-gl'
|
|
const glStyle = normalizeStyleForProvider(provider, style)
|
|
const enableMapbox3d = !isMapLibre && enable3d
|
|
|
|
useEffect(() => {
|
|
if (!containerRef.current || (!isMapLibre && !token)) return
|
|
if (!isMapLibre) gl.accessToken = token
|
|
|
|
const mapOptions: Record<string, unknown> = {
|
|
container: containerRef.current,
|
|
style: glStyle,
|
|
center: [lng, lat],
|
|
zoom,
|
|
pitch: enableMapbox3d ? 45 : 0,
|
|
attributionControl: true,
|
|
antialias: quality,
|
|
}
|
|
if (!isMapLibre) mapOptions.projection = quality ? 'globe' : 'mercator'
|
|
// MapLibre's around-center mouse rotate ping-pongs near mid-screen (#1545)
|
|
// — see MapViewGL. Keep the plain dx-based rotate everywhere.
|
|
if (isMapLibre) mapOptions.aroundCenter = false
|
|
|
|
// Null when the GPU refused WebGL2: the settings form has switched the preview to Leaflet by then.
|
|
const map = buildGlMap(() => new gl.Map(mapOptions as any))
|
|
if (!map) return
|
|
mapRef.current = map
|
|
|
|
map.on('load', () => {
|
|
if (enableMapbox3d) {
|
|
if (!isStandardFamily(glStyle)) addTerrainAndSky(map)
|
|
if (supportsCustom3d(glStyle)) {
|
|
const dark = document.documentElement.classList.contains('dark')
|
|
addCustom3dBuildings(map, dark)
|
|
}
|
|
}
|
|
if (glStyle === MAPBOX_DEFAULT_STYLE) {
|
|
try { map.setTerrain(null) } catch { /* noop */ }
|
|
}
|
|
})
|
|
|
|
map.on('click', (e) => {
|
|
onClickRef.current?.({ lat: e.lngLat.lat, lng: e.lngLat.lng })
|
|
})
|
|
|
|
return () => {
|
|
try { map.remove() } catch { /* noop */ }
|
|
mapRef.current = null
|
|
}
|
|
}, [provider, token, glStyle, enableMapbox3d, quality])
|
|
|
|
// Recenter without rebuilding the map when lat/lng/zoom change externally
|
|
useEffect(() => {
|
|
if (!mapRef.current) return
|
|
try { mapRef.current.jumpTo({ center: [lng, lat], zoom }) } catch { /* noop */ }
|
|
}, [lat, lng, zoom])
|
|
|
|
if (!isMapLibre && !token) {
|
|
return (
|
|
<div className="flex h-full items-center justify-center bg-surface-tertiary px-4 text-center font-medium text-content-muted" style={fs(12)}>
|
|
Enter a Mapbox access token to preview
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return <div ref={containerRef} className="h-full w-full overflow-hidden" />
|
|
}
|