Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support for custom mapbox layer #1851

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 31 additions & 23 deletions examples/deckgl-overlay/src/app.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,42 @@
import * as React from 'react';
import {render} from 'react-dom';
import DeckGL, {ArcLayer} from 'deck.gl';
import Map from 'react-map-gl';
import {ArcLayer} from 'deck.gl';
import {MapboxLayer} from '@deck.gl/mapbox';
import Map, {Layer, MapboxMap} from 'react-map-gl';
import ControlPanel from './control-panel';

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

export default function App() {
const arcLayer = new ArcLayer({
data: 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/bart-segments.json',
getSourcePosition: d => d.from.coordinates,
getTargetPosition: d => d.to.coordinates,
getSourceColor: [255, 200, 0],
getTargetColor: [0, 140, 255],
getWidth: 12
});
const [overLabels, setOverLabels] = React.useState(true);
const layer = React.useMemo(() => {
return new MapboxLayer({
id: 'arcs',
type: ArcLayer,
data: 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/bart-segments.json',
getSourcePosition: d => d.from.coordinates,
getTargetPosition: d => d.to.coordinates,
getSourceColor: [255, 200, 0],
getTargetColor: [0, 140, 255],
getWidth: 12
});
}, []);

return (
<DeckGL
initialViewState={{
longitude: -122.45,
latitude: 37.75,
zoom: 11,
bearing: 0,
pitch: 60
}}
controller={true}
layers={[arcLayer]}
>
<Map mapStyle="mapbox://styles/mapbox/light-v9" mapboxAccessToken={TOKEN} />
</DeckGL>
<>
<ControlPanel overLabels={overLabels} setOverLabels={setOverLabels} />
<Map
initialViewState={{
zoom: 9,
longitude: -122.431297,
latitude: 37.787994
}}
mapStyle="mapbox://styles/mapbox/light-v9"
mapboxAccessToken={TOKEN}
>
<Layer layer={layer} beforeId={overLabels ? 'water-label' : 'country-label-lg'} />
</Map>
</>
);
}

Expand Down
32 changes: 32 additions & 0 deletions examples/deckgl-overlay/src/control-panel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';

function Checkbox({name, value, onChange}) {
return (
<div key={name} className="input">
<label>{name}</label>
<input type="checkbox" checked={value} onChange={evt => onChange(name, evt.target.checked)} />
</div>
);
}

function ControlPanel({
overLabels,
setOverLabels
}: {
overLabels: boolean;
setOverLabels: (v: boolean) => void;
}) {
return (
<div className="control-panel">
<h3>Deck.gl layer</h3>
<p>A deck.gl overlay can be used and inserted inside the Mapbox layers.</p>
<Checkbox
name="Arcs over labels"
value={overLabels}
onChange={(name: string, v: boolean) => setOverLabels(v)}
/>
</div>
);
}

export default ControlPanel;
57 changes: 40 additions & 17 deletions src/components/layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,30 @@ import {deepEqual} from '../utils/deep-equal';

import type {MapboxMap, AnyLayer} from '../types';

export type LayerProps = AnyLayer & {
export type DeprecatedLayerProps = AnyLayer & {
id?: string;
/** If set, the layer will be inserted before the specified layer */
beforeId?: string;
};

export type LayerProps =
| {
layer: AnyLayer;
beforeId?: string;
}
| DeprecatedLayerProps;

/* eslint-disable complexity, max-statements */
function updateLayer(map: MapboxMap, id: string, props: LayerProps, prevProps: LayerProps) {
function updateLayer(map: MapboxMap, id: string, props: AnyLayer, prevProps: AnyLayer) {
assert(props.id === prevProps.id, 'layer id changed');
assert(props.type === prevProps.type, 'layer type changed');

if (props.type === 'custom' || prevProps.type === 'custom') {
return;
}

const {layout = {}, paint = {}, filter, minzoom, maxzoom, beforeId} = props;
const {layout = {}, paint = {}, filter, minzoom, maxzoom} = props;

if (beforeId !== prevProps.beforeId) {
map.moveLayer(id, beforeId);
}
if (layout !== prevProps.layout) {
const prevLayout = prevProps.layout || {};
for (const key in layout) {
Expand Down Expand Up @@ -59,27 +63,40 @@ function updateLayer(map: MapboxMap, id: string, props: LayerProps, prevProps: L
}
}

function createLayer(map: MapboxMap, id: string, props: LayerProps) {
function isMapStyleLoaded(map: MapboxMap) {
// @ts-ignore
if (map.style && map.style._loaded && (!('source' in props) || map.getSource(props.source))) {
const options: LayerProps = {...props, id};
delete options.beforeId;
return map.style && map.style._loaded;
}

function createLayer(map: MapboxMap, id: string, layerProps: LayerProps, beforeId: string) {
// @ts-ignore
if (isMapStyleLoaded(map) && (!('source' in layerProps) || map.getSource(layerProps.source))) {
// @ts-ignore
map.addLayer(options, props.beforeId);
map.addLayer(layerProps, beforeId);
}
}

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove extra lines

let layerCounter = 0;

function Layer(props: LayerProps) {
function Layer(props: LayerProps & {layer?: AnyLayer}) {
const map: MapboxMap = useContext(MapContext).map.getMap();
const propsRef = useRef(props);

const layerProps = useMemo(() => {
if (props.layer) {
return props.layer;
}
const res = {...props};
delete res.beforeId;
return res as DeprecatedLayerProps;
}, [props.layer, props]);

const layerPropsRef = useRef(layerProps);
const [, setStyleLoaded] = useState(0);
const beforeId = props.beforeId;

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

useEffect(() => {
if (map) {
Expand All @@ -102,16 +119,22 @@ function Layer(props: LayerProps) {
const layer = map && map.style && map.getLayer(id);
if (layer) {
try {
updateLayer(map, id, props, propsRef.current);
updateLayer(map, id, layerProps, layerPropsRef.current);
} catch (error) {
console.warn(error); // eslint-disable-line
}
} else {
createLayer(map, id, props);
createLayer(map, id, layerProps, beforeId);
}

useEffect(() => {
if (beforeId && isMapStyleLoaded(map)) {
map.moveLayer(id, beforeId);
}
}, [beforeId]);

// Store last rendered props
propsRef.current = props;
layerPropsRef.current = layerProps;

return null;
}
Expand Down