Skip to content

Commit f66b7ef

Browse files
committed
feat: Support for custom mapbox layer
Instead of using DeckGl as the container for Mapbox, we use Mapbox as the container for DeckGl layer. This lets us insert the deckgl layer between the mapbox layers, which is very handy when dealing with labels. We use DeckGL's custom Mapbox layer here. Since DeckGL's custom layer need method on the prototype, we can copy the layer props with spreading, otherwise we lose the methods. This is why the `layer` prop is added on the Layer so that we can pass the custom layer without any copying.
1 parent b2bdb91 commit f66b7ef

File tree

3 files changed

+92
-39
lines changed

3 files changed

+92
-39
lines changed

examples/deckgl-overlay/src/app.tsx

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,40 @@
11
import * as React from 'react';
22
import {render} from 'react-dom';
3-
import DeckGL, {ArcLayer} from 'deck.gl';
4-
import Map from 'react-map-gl';
3+
import {ArcLayer} from 'deck.gl';
4+
import {MapboxLayer} from '@deck.gl/mapbox';
5+
import Map, {Layer} from 'react-map-gl';
6+
import ControlPanel from './control-panel';
57

68
const TOKEN = ''; // Set your mapbox token here
79

810
export default function App() {
9-
const arcLayer = new ArcLayer({
10-
data: 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/bart-segments.json',
11-
getSourcePosition: d => d.from.coordinates,
12-
getTargetPosition: d => d.to.coordinates,
13-
getSourceColor: [255, 200, 0],
14-
getTargetColor: [0, 140, 255],
15-
getWidth: 12
16-
});
11+
const [overLabels, setOverLabels] = React.useState(true);
12+
const layer = React.useMemo(() => {
13+
return new MapboxLayer({
14+
id: 'arcs',
15+
type: ArcLayer,
16+
data: 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/bart-segments.json',
17+
getSourcePosition: d => d.from.coordinates,
18+
getTargetPosition: d => d.to.coordinates,
19+
getSourceColor: [255, 200, 0],
20+
getTargetColor: [0, 140, 255],
21+
getWidth: 12
22+
});
23+
}, []);
1724

1825
return (
19-
<DeckGL
20-
initialViewState={{
21-
longitude: -122.45,
22-
latitude: 37.75,
23-
zoom: 11,
24-
bearing: 0,
25-
pitch: 60
26-
}}
27-
controller={true}
28-
layers={[arcLayer]}
29-
>
30-
<Map mapStyle="mapbox://styles/mapbox/light-v9" mapboxAccessToken={TOKEN} />
31-
</DeckGL>
26+
<>
27+
<ControlPanel overLabels={overLabels} setOverLabels={setOverLabels} />
28+
<Map
29+
zoom={9}
30+
latitude={37.787994}
31+
longitude={-122.407437}
32+
mapStyle="mapbox://styles/mapbox/light-v9"
33+
mapboxAccessToken={TOKEN}
34+
>
35+
<Layer layer={layer} beforeId={overLabels ? 'water-label' : 'country-label-lg'} />
36+
</Map>
37+
</>
3238
);
3339
}
3440

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React from 'react';
2+
3+
function Checkbox({name, value, onChange}) {
4+
return (
5+
<div key={name} className="input">
6+
<label>{name}</label>
7+
<input type="checkbox" checked={value} onChange={evt => onChange(name, evt.target.checked)} />
8+
</div>
9+
);
10+
}
11+
12+
function ControlPanel({
13+
overLabels,
14+
setOverLabels
15+
}: {
16+
overLabels: boolean;
17+
setOverLabels: (v: boolean) => void;
18+
}) {
19+
return (
20+
<div className="control-panel">
21+
<h3>Deck.gl layer</h3>
22+
<p>A deck.gl overlay can be used and inserted inside the Mapbox layers.</p>
23+
<Checkbox
24+
name="Arcs over labels"
25+
value={overLabels}
26+
onChange={(name: string, v: boolean) => setOverLabels(v)}
27+
/>
28+
</div>
29+
);
30+
}
31+
32+
export default ControlPanel;

src/components/layer.ts

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,26 @@ export type LayerProps = AnyLayer & {
1212
};
1313

1414
/* eslint-disable complexity, max-statements */
15-
function updateLayer(map: MapboxMap, id: string, props: LayerProps, prevProps: LayerProps) {
15+
function updateLayer(
16+
map: MapboxMap,
17+
id: string,
18+
props: LayerProps,
19+
prevProps: LayerProps,
20+
beforeId?: string
21+
) {
1622
assert(props.id === prevProps.id, 'layer id changed');
1723
assert(props.type === prevProps.type, 'layer type changed');
1824

25+
if (beforeId !== prevProps.beforeId) {
26+
map.moveLayer(id, beforeId);
27+
}
28+
1929
if (props.type === 'custom' || prevProps.type === 'custom') {
2030
return;
2131
}
2232

23-
const {layout = {}, paint = {}, filter, minzoom, maxzoom, beforeId} = props;
33+
const {layout = {}, paint = {}, filter, minzoom, maxzoom} = props;
2434

25-
if (beforeId !== prevProps.beforeId) {
26-
map.moveLayer(id, beforeId);
27-
}
2835
if (layout !== prevProps.layout) {
2936
const prevLayout = prevProps.layout || {};
3037
for (const key in layout) {
@@ -59,27 +66,35 @@ function updateLayer(map: MapboxMap, id: string, props: LayerProps, prevProps: L
5966
}
6067
}
6168

62-
function createLayer(map: MapboxMap, id: string, props: LayerProps) {
69+
function createLayer(map: MapboxMap, id: string, props: LayerProps, beforeId: string) {
6370
// @ts-ignore
6471
if (map.style && map.style._loaded && (!('source' in props) || map.getSource(props.source))) {
65-
const options: LayerProps = {...props, id};
66-
delete options.beforeId;
67-
6872
// @ts-ignore
69-
map.addLayer(options, props.beforeId);
73+
map.addLayer(props, beforeId);
7074
}
7175
}
7276

7377
/* eslint-enable complexity, max-statements */
7478

7579
let layerCounter = 0;
7680

77-
function Layer(props: LayerProps) {
81+
function Layer(props: LayerProps & {layer?: LayerProps}) {
7882
const map: MapboxMap = useContext(MapContext).map.getMap();
79-
const propsRef = useRef(props);
83+
84+
const layerProps = useMemo(() => {
85+
if (props.layer) {
86+
return props.layer;
87+
}
88+
const res = {...props};
89+
delete res.beforeId;
90+
return res;
91+
}, [props.layer, props]);
92+
93+
const layerPropsRef = useRef(layerProps);
8094
const [, setStyleLoaded] = useState(0);
95+
const beforeId = props.beforeId;
8196

82-
const id = useMemo(() => props.id || `jsx-layer-${layerCounter++}`, []);
97+
const id = useMemo(() => layerProps.id || `jsx-layer-${layerCounter++}`, []);
8398

8499
useEffect(() => {
85100
if (map) {
@@ -102,16 +117,16 @@ function Layer(props: LayerProps) {
102117
const layer = map && map.style && map.getLayer(id);
103118
if (layer) {
104119
try {
105-
updateLayer(map, id, props, propsRef.current);
120+
updateLayer(map, id, layerProps, layerPropsRef.current, beforeId);
106121
} catch (error) {
107122
console.warn(error); // eslint-disable-line
108123
}
109124
} else {
110-
createLayer(map, id, props);
125+
createLayer(map, id, layerProps, beforeId);
111126
}
112127

113128
// Store last rendered props
114-
propsRef.current = props;
129+
layerPropsRef.current = layerProps;
115130

116131
return null;
117132
}

0 commit comments

Comments
 (0)