@deck.gl/maplibre 9.4.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +19 -0
- package/README.md +5 -0
- package/dist/compatibility.d.ts +16 -0
- package/dist/compatibility.d.ts.map +1 -0
- package/dist/compatibility.js +42 -0
- package/dist/compatibility.js.map +1 -0
- package/dist/deck-utils.d.ts +24 -0
- package/dist/deck-utils.d.ts.map +1 -0
- package/dist/deck-utils.js +238 -0
- package/dist/deck-utils.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/layer-group.d.ts +18 -0
- package/dist/layer-group.d.ts.map +1 -0
- package/dist/layer-group.js +33 -0
- package/dist/layer-group.js.map +1 -0
- package/dist/layer-utils.d.ts +7 -0
- package/dist/layer-utils.d.ts.map +1 -0
- package/dist/layer-utils.js +10 -0
- package/dist/layer-utils.js.map +1 -0
- package/dist/overlay.d.ts +51 -0
- package/dist/overlay.d.ts.map +1 -0
- package/dist/overlay.js +296 -0
- package/dist/overlay.js.map +1 -0
- package/dist/resolve-layer-groups.d.ts +4 -0
- package/dist/resolve-layer-groups.d.ts.map +1 -0
- package/dist/resolve-layer-groups.js +65 -0
- package/dist/resolve-layer-groups.js.map +1 -0
- package/package.json +49 -0
- package/src/compatibility.ts +58 -0
- package/src/deck-utils.ts +309 -0
- package/src/index.ts +6 -0
- package/src/layer-group.ts +62 -0
- package/src/layer-utils.ts +17 -0
- package/src/overlay.ts +388 -0
- package/src/resolve-layer-groups.ts +84 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
// deck.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {Deck, MapView, _GlobeView as GlobeView, _flatten as flatten} from '@deck.gl/core';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
getMapLibreElevation,
|
|
9
|
+
getMapLibreProjection,
|
|
10
|
+
type MapLibreRenderParameters
|
|
11
|
+
} from './compatibility';
|
|
12
|
+
import {getMapLibreLayerGroupId} from './layer-utils';
|
|
13
|
+
|
|
14
|
+
import type {DeckProps, Layer, MapViewState, Viewport} from '@deck.gl/core';
|
|
15
|
+
import type {Parameters} from '@luma.gl/core';
|
|
16
|
+
import type {Map as MapLibreMap} from 'maplibre-gl';
|
|
17
|
+
import type MapLibreLayerGroup from './layer-group';
|
|
18
|
+
import type {MapLibreLayerProps} from './layer-utils';
|
|
19
|
+
|
|
20
|
+
export const MAPLIBRE_VIEW_ID = 'maplibre';
|
|
21
|
+
|
|
22
|
+
type UserData = {
|
|
23
|
+
currentViewport?: Viewport | null;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
type MapLibreDeckState = {
|
|
27
|
+
deck: Deck;
|
|
28
|
+
moveListener: () => void;
|
|
29
|
+
renderListener: () => void;
|
|
30
|
+
watchingMove: boolean;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const MAPLIBRE_DECK_STATES = new WeakMap<MapLibreMap, MapLibreDeckState>();
|
|
34
|
+
|
|
35
|
+
export function createMapLibreInterleavedDeck(map: MapLibreMap, props: DeckProps): Deck {
|
|
36
|
+
if (MAPLIBRE_DECK_STATES.has(map)) {
|
|
37
|
+
throw new Error('MapLibreOverlay supports one interleaved overlay per map');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const gl = map.getCanvas().getContext('webgl2');
|
|
41
|
+
if (!gl) {
|
|
42
|
+
throw new Error('MapLibreOverlay interleaved rendering requires WebGL2.');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const {device: _, ...deckProps} = props;
|
|
46
|
+
const deck = new Deck({...deckProps, gl});
|
|
47
|
+
try {
|
|
48
|
+
return createMapLibreDeckInstance(map, deck);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if (getMapLibreDeckInstance(map) === deck) {
|
|
51
|
+
removeMapLibreDeckInstance(map);
|
|
52
|
+
} else {
|
|
53
|
+
deck.finalize();
|
|
54
|
+
}
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function getMapLibreDefaultParameters(_map: MapLibreMap, interleaved: boolean): Parameters {
|
|
60
|
+
return interleaved
|
|
61
|
+
? {
|
|
62
|
+
depthWriteEnabled: true,
|
|
63
|
+
depthCompare: 'less-equal',
|
|
64
|
+
depthBias: 0,
|
|
65
|
+
blend: true,
|
|
66
|
+
blendColorSrcFactor: 'src-alpha',
|
|
67
|
+
blendColorDstFactor: 'one-minus-src-alpha',
|
|
68
|
+
blendAlphaSrcFactor: 'one',
|
|
69
|
+
blendAlphaDstFactor: 'one-minus-src-alpha',
|
|
70
|
+
blendColorOperation: 'add',
|
|
71
|
+
blendAlphaOperation: 'add'
|
|
72
|
+
}
|
|
73
|
+
: {};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function getMapLibreDefaultView(map: MapLibreMap): GlobeView | MapView {
|
|
77
|
+
return getMapLibreProjection(map) === 'globe'
|
|
78
|
+
? new GlobeView({id: MAPLIBRE_VIEW_ID})
|
|
79
|
+
: new MapView({id: MAPLIBRE_VIEW_ID});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function getMapLibreViewState(map: MapLibreMap): MapViewState & {
|
|
83
|
+
repeat: boolean;
|
|
84
|
+
padding: {top: number; bottom: number; left: number; right: number};
|
|
85
|
+
} {
|
|
86
|
+
const {lng, lat} = map.getCenter();
|
|
87
|
+
const padding = map.getPadding();
|
|
88
|
+
const viewState: MapViewState & {
|
|
89
|
+
repeat: boolean;
|
|
90
|
+
padding: {top: number; bottom: number; left: number; right: number};
|
|
91
|
+
} = {
|
|
92
|
+
longitude: ((lng + 540) % 360) - 180,
|
|
93
|
+
latitude: lat,
|
|
94
|
+
zoom: map.getZoom(),
|
|
95
|
+
bearing: map.getBearing(),
|
|
96
|
+
pitch: map.getPitch(),
|
|
97
|
+
padding: {
|
|
98
|
+
top: padding.top ?? 0,
|
|
99
|
+
bottom: padding.bottom ?? 0,
|
|
100
|
+
left: padding.left ?? 0,
|
|
101
|
+
right: padding.right ?? 0
|
|
102
|
+
},
|
|
103
|
+
repeat: map.getRenderWorldCopies()
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const elevation = getMapLibreElevation(map);
|
|
107
|
+
if (typeof elevation === 'number' && Number.isFinite(elevation)) {
|
|
108
|
+
viewState.position = [0, 0, elevation];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return viewState;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function createMapLibreDeckInstance(map: MapLibreMap, deck: Deck): Deck {
|
|
115
|
+
const existingState = MAPLIBRE_DECK_STATES.get(map);
|
|
116
|
+
if (existingState) {
|
|
117
|
+
throw new Error('MapLibreOverlay supports one interleaved overlay per map');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const customRender = deck.props._customRender;
|
|
121
|
+
const onLoad = deck.props.onLoad;
|
|
122
|
+
const state: MapLibreDeckState = {
|
|
123
|
+
deck,
|
|
124
|
+
watchingMove: false,
|
|
125
|
+
moveListener: () => {
|
|
126
|
+
if (deck.isInitialized) {
|
|
127
|
+
onMapLibreMove(deck, map);
|
|
128
|
+
} else {
|
|
129
|
+
stopWatchingMove(map, state);
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
renderListener: () => {
|
|
133
|
+
if (deck.isInitialized) {
|
|
134
|
+
afterMapLibreRender(deck, map);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
MAPLIBRE_DECK_STATES.set(map, state);
|
|
139
|
+
|
|
140
|
+
const deckProps: any = {
|
|
141
|
+
...deck.props,
|
|
142
|
+
_customRender: () => {
|
|
143
|
+
map.triggerRepaint();
|
|
144
|
+
customRender?.('');
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
deckProps.views ||= getMapLibreDefaultView(map);
|
|
148
|
+
Object.assign(deckProps, {
|
|
149
|
+
width: null,
|
|
150
|
+
height: null,
|
|
151
|
+
touchAction: 'unset',
|
|
152
|
+
viewState: getMapLibreViewState(map)
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
if (deck.isInitialized) {
|
|
156
|
+
startWatchingMove(map, state);
|
|
157
|
+
} else {
|
|
158
|
+
deckProps.onLoad = () => {
|
|
159
|
+
onLoad?.();
|
|
160
|
+
if (MAPLIBRE_DECK_STATES.get(map) === state) {
|
|
161
|
+
startWatchingMove(map, state);
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
deck.setProps(deckProps);
|
|
167
|
+
map.on('render', state.renderListener);
|
|
168
|
+
return deck;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function getMapLibreDeckInstance(map: MapLibreMap): Deck | undefined {
|
|
172
|
+
return MAPLIBRE_DECK_STATES.get(map)?.deck;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function removeMapLibreDeckInstance(map: MapLibreMap): void {
|
|
176
|
+
const state = MAPLIBRE_DECK_STATES.get(map);
|
|
177
|
+
if (!state) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
stopWatchingMove(map, state);
|
|
182
|
+
map.off('render', state.renderListener);
|
|
183
|
+
state.deck.finalize();
|
|
184
|
+
MAPLIBRE_DECK_STATES.delete(map);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function drawMapLibreLayerGroup(
|
|
188
|
+
deck: Deck,
|
|
189
|
+
map: MapLibreMap,
|
|
190
|
+
group: MapLibreLayerGroup,
|
|
191
|
+
renderParameters: MapLibreRenderParameters
|
|
192
|
+
): void {
|
|
193
|
+
if (!deck.isInitialized) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
let {currentViewport} = deck.userData as UserData;
|
|
198
|
+
let clearStack = false;
|
|
199
|
+
if (!currentViewport) {
|
|
200
|
+
currentViewport = getMapLibreViewport(deck, map, renderParameters);
|
|
201
|
+
(deck.userData as UserData).currentViewport = currentViewport;
|
|
202
|
+
clearStack = true;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (!currentViewport) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
deck._drawLayers('maplibre-repaint', {
|
|
210
|
+
viewports: [currentViewport],
|
|
211
|
+
layerFilter: params => {
|
|
212
|
+
if (deck.props.layerFilter && !deck.props.layerFilter(params)) {
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const layer = params.layer as Layer<MapLibreLayerProps>;
|
|
217
|
+
return layer.props.beforeId === group.beforeId;
|
|
218
|
+
},
|
|
219
|
+
clearStack,
|
|
220
|
+
clearCanvas: false
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function startWatchingMove(map: MapLibreMap, state: MapLibreDeckState): void {
|
|
225
|
+
if (!state.watchingMove) {
|
|
226
|
+
state.watchingMove = true;
|
|
227
|
+
map.on('move', state.moveListener);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function stopWatchingMove(map: MapLibreMap, state: MapLibreDeckState): void {
|
|
232
|
+
if (state.watchingMove) {
|
|
233
|
+
state.watchingMove = false;
|
|
234
|
+
map.off('move', state.moveListener);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function getMapLibreViewport(
|
|
239
|
+
deck: Deck,
|
|
240
|
+
map: MapLibreMap,
|
|
241
|
+
renderParameters?: MapLibreRenderParameters
|
|
242
|
+
): Viewport | null {
|
|
243
|
+
const viewState = getMapLibreViewState(map);
|
|
244
|
+
const view = (deck.getView(MAPLIBRE_VIEW_ID) || getMapLibreDefaultView(map)) as
|
|
245
|
+
| MapView
|
|
246
|
+
| GlobeView;
|
|
247
|
+
|
|
248
|
+
if (renderParameters) {
|
|
249
|
+
view.props.nearZMultiplier = 0.2;
|
|
250
|
+
const height = map.getCanvas().clientHeight;
|
|
251
|
+
if (
|
|
252
|
+
height > 0 &&
|
|
253
|
+
Number.isFinite(renderParameters.nearZ) &&
|
|
254
|
+
Number.isFinite(renderParameters.farZ)
|
|
255
|
+
) {
|
|
256
|
+
viewState.nearZ = renderParameters.nearZ / height;
|
|
257
|
+
viewState.farZ = renderParameters.farZ / height;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return view.makeViewport({
|
|
262
|
+
width: deck.width,
|
|
263
|
+
height: deck.height,
|
|
264
|
+
viewState
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function afterMapLibreRender(deck: Deck, map: MapLibreMap): void {
|
|
269
|
+
const deckLayers = flatten(deck.props.layers, Boolean) as Layer<MapLibreLayerProps>[];
|
|
270
|
+
const hasNonMapLibreLayers = deckLayers.some(
|
|
271
|
+
layer => layer && !map.getLayer(getMapLibreLayerGroupId(layer))
|
|
272
|
+
);
|
|
273
|
+
let viewports = deck.getViewports();
|
|
274
|
+
const mapLibreViewportIndex = viewports.findIndex(viewport => viewport.id === MAPLIBRE_VIEW_ID);
|
|
275
|
+
const hasNonMapLibreViews = viewports.length > 1 || mapLibreViewportIndex < 0;
|
|
276
|
+
|
|
277
|
+
if (hasNonMapLibreLayers || hasNonMapLibreViews) {
|
|
278
|
+
if (mapLibreViewportIndex >= 0) {
|
|
279
|
+
viewports = viewports.slice();
|
|
280
|
+
const mapLibreViewport = getMapLibreViewport(deck, map);
|
|
281
|
+
if (mapLibreViewport) {
|
|
282
|
+
viewports[mapLibreViewportIndex] = mapLibreViewport;
|
|
283
|
+
} else {
|
|
284
|
+
viewports.splice(mapLibreViewportIndex, 1);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
deck._drawLayers('maplibre-repaint', {
|
|
289
|
+
viewports,
|
|
290
|
+
layerFilter: params =>
|
|
291
|
+
(!deck.props.layerFilter || deck.props.layerFilter(params)) &&
|
|
292
|
+
(params.viewport.id !== MAPLIBRE_VIEW_ID ||
|
|
293
|
+
!map.getLayer(getMapLibreLayerGroupId(params.layer as Layer<MapLibreLayerProps>))),
|
|
294
|
+
clearCanvas: false
|
|
295
|
+
});
|
|
296
|
+
} else {
|
|
297
|
+
const device = (deck as any).device;
|
|
298
|
+
const gl = device?.gl;
|
|
299
|
+
deck.props.onBeforeRender?.({device, gl});
|
|
300
|
+
deck.props.onAfterRender?.({device, gl});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
(deck.userData as UserData).currentViewport = null;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function onMapLibreMove(deck: Deck, map: MapLibreMap): void {
|
|
307
|
+
deck.setProps({viewState: getMapLibreViewState(map)});
|
|
308
|
+
deck.needsRedraw({clearRedrawFlags: true});
|
|
309
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// deck.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {assert} from '@deck.gl/core';
|
|
6
|
+
|
|
7
|
+
import {getMapLibreRenderParameters} from './compatibility';
|
|
8
|
+
import {drawMapLibreLayerGroup, getMapLibreDeckInstance} from './deck-utils';
|
|
9
|
+
|
|
10
|
+
import type {CustomLayerInterface, Map as MapLibreMap} from 'maplibre-gl';
|
|
11
|
+
|
|
12
|
+
export type MapLibreLayerGroupProps = {
|
|
13
|
+
id: string;
|
|
14
|
+
renderingMode?: '2d' | '3d';
|
|
15
|
+
beforeId?: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default class MapLibreLayerGroup implements CustomLayerInterface {
|
|
19
|
+
readonly id: string;
|
|
20
|
+
readonly type = 'custom' as const;
|
|
21
|
+
readonly renderingMode: '2d' | '3d';
|
|
22
|
+
readonly beforeId?: string;
|
|
23
|
+
|
|
24
|
+
private map: MapLibreMap | null = null;
|
|
25
|
+
|
|
26
|
+
constructor(props: MapLibreLayerGroupProps) {
|
|
27
|
+
assert(props.id, 'id is required');
|
|
28
|
+
this.id = props.id;
|
|
29
|
+
this.renderingMode = props.renderingMode || '3d';
|
|
30
|
+
this.beforeId = props.beforeId;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
onAdd(map: MapLibreMap): void {
|
|
34
|
+
this.map = map;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
onRemove(): void {
|
|
38
|
+
this.map = null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
render(
|
|
42
|
+
_gl: WebGLRenderingContext | WebGL2RenderingContext,
|
|
43
|
+
parametersOrMatrix: unknown,
|
|
44
|
+
legacyParameters?: unknown
|
|
45
|
+
): void {
|
|
46
|
+
if (!this.map) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const deck = getMapLibreDeckInstance(this.map);
|
|
51
|
+
if (!deck) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
drawMapLibreLayerGroup(
|
|
56
|
+
deck,
|
|
57
|
+
this.map,
|
|
58
|
+
this,
|
|
59
|
+
getMapLibreRenderParameters(parametersOrMatrix, legacyParameters)
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// deck.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import type {Layer} from '@deck.gl/core';
|
|
6
|
+
|
|
7
|
+
export type MapLibreLayerProps = {
|
|
8
|
+
beforeId?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const MAPLIBRE_LAST_LAYER_GROUP_ID = 'deck-maplibre-layer-group-last';
|
|
12
|
+
|
|
13
|
+
export function getMapLibreLayerGroupId(layer: Layer<MapLibreLayerProps>): string {
|
|
14
|
+
return layer.props.beforeId
|
|
15
|
+
? `deck-maplibre-layer-group-before:${layer.props.beforeId}`
|
|
16
|
+
: MAPLIBRE_LAST_LAYER_GROUP_ID;
|
|
17
|
+
}
|