Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | 3x 3x 3x 3x 2x 3x 3x 3x 1x 2x 3x 2x 3x 3x 2x 3x 8x | import { Option } from 'effect'
import { Camera, type LatLon } from '../core/domain'
/** The cue goes in the query, as `?p=lat,lon`, so a shared link opens with the same cue. */
export const CUE_PARAM = 'p'
/** The camera goes in the hash, as `#view=zoom/lat/lon/bearing/pitch`, the same form as MapLibre's own hash. */
export const CAMERA_HASH_KEY = 'view'
const CUE_DECIMALS = 7
export const cueToUrlValue = ({ lat, lon }: LatLon.LatLon): string =>
`${lat.toFixed(CUE_DECIMALS)},${lon.toFixed(CUE_DECIMALS)}`
/** The URL with the cue and the camera written in; a missing cue removes the parameter. */
export const withState = (
href: string,
maybeCue: Option.Option<LatLon.LatLon>,
maybeCamera: Option.Option<Camera.Camera>,
): URL => {
const url = new URL(href)
Option.match(maybeCue, {
onNone: () => url.searchParams.delete(CUE_PARAM),
onSome: cue => url.searchParams.set(CUE_PARAM, cueToUrlValue(cue)),
})
if (Option.isSome(maybeCamera)) {
url.hash = `${CAMERA_HASH_KEY}=${Camera.toUrlValue(maybeCamera.value)}`
}
return url
}
export const readCueQuery = (href: string): Option.Option<string> =>
Option.fromNullishOr(new URL(href).searchParams.get(CUE_PARAM))
export const readCamera = (href: string): Option.Option<Camera.Camera> =>
Option.flatMap(
Option.fromNullishOr(
new URLSearchParams(new URL(href).hash.slice(1)).get(CAMERA_HASH_KEY),
),
Camera.fromUrlValue,
)
|