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 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | 3x 3x 3x 3x 3x 3x 3x 3x 3x 26x 3x 26x 4x 22x 22x 22x 22x 22x 22x 22x 22x 3x 26x 26x 26x 20x 3x 23x 5x 5x 5x | /**
* The overlay is drawn by MapLibre itself: one line layer (grids, range rings and cue cells,
* ordered by rank), two circle layers for the cue marker, and a symbol layer for labels.
* MapLibre layers follow the globe, pitch and bearing exactly, which deck.gl's globe view
* does not.
*/
import type { GeoJSONSource, Map as MapInstance } from 'maplibre-gl'
import { CUE_COLOR } from './cue'
import { opaqueCssColor } from './features'
import type { OverlayFrame } from './overlay'
const LINES_ID = 'overlay-lines'
const CUE_ID = 'overlay-cue'
const CUE_RING_ID = 'overlay-cue-ring'
const CUE_CENTRE_ID = 'overlay-cue-centre'
const LABELS_ID = 'grid-labels'
const CUE_RING_RADIUS = 7
const CUE_RING_WIDTH = 3
const CUE_CENTRE_RADIUS = 2
// NOTE: these sources are rebuilt for the view after every move, so MapLibre need not tile
// them for far-away zoom levels or with a wide buffer. This keeps the redraw after a move quick.
const REBUILT_EACH_MOVE = { maxzoom: 14, buffer: 32, tolerance: 0.5 } as const
const emptyCollection = (): GeoJSON.FeatureCollection => ({
type: 'FeatureCollection',
features: [],
})
const addOverlayLayers = (map: MapInstance): void => {
if (map.getSource(LINES_ID)) {
return
}
const cueColor = opaqueCssColor(CUE_COLOR)
map.addSource(LINES_ID, {
type: 'geojson',
data: emptyCollection(),
...REBUILT_EACH_MOVE,
})
map.addSource(CUE_ID, { type: 'geojson', data: emptyCollection() })
map.addSource(LABELS_ID, {
type: 'geojson',
data: emptyCollection(),
...REBUILT_EACH_MOVE,
})
map.addLayer({
id: LINES_ID,
type: 'line',
source: LINES_ID,
layout: { 'line-sort-key': ['get', 'rank'], 'line-join': 'round' },
paint: { 'line-color': ['get', 'color'], 'line-width': ['get', 'width'] },
})
map.addLayer({
id: CUE_RING_ID,
type: 'circle',
source: CUE_ID,
paint: {
'circle-radius': CUE_RING_RADIUS,
'circle-color': 'rgba(0, 0, 0, 0)',
'circle-stroke-color': cueColor,
'circle-stroke-width': CUE_RING_WIDTH,
},
})
map.addLayer({
id: CUE_CENTRE_ID,
type: 'circle',
source: CUE_ID,
paint: { 'circle-radius': CUE_CENTRE_RADIUS, 'circle-color': cueColor },
})
map.addLayer({
id: LABELS_ID,
type: 'symbol',
source: LABELS_ID,
layout: {
'text-field': ['get', 'text'],
'text-font': ['Noto Sans Bold'],
'text-size': ['get', 'size'],
'text-allow-overlap': false,
'text-padding': 1,
'symbol-sort-key': ['get', 'rank'],
},
paint: {
'text-color': ['get', 'color'],
'text-halo-color': ['get', 'halo'],
'text-halo-width': 1.5,
},
})
}
/** Add the overlay layers if the style can take them yet; returns false while the style is still loading. */
export const ensureOverlayLayers = (map: MapInstance): boolean => {
try {
addOverlayLayers(map)
return true
} catch {
return false
}
}
export const setOverlay = (map: MapInstance, frame: OverlayFrame): void => {
if (ensureOverlayLayers(map)) {
map.getSource<GeoJSONSource>(LINES_ID)?.setData(frame.lines)
map.getSource<GeoJSONSource>(CUE_ID)?.setData(frame.cueMarker)
map.getSource<GeoJSONSource>(LABELS_ID)?.setData(frame.labels)
}
}
|