Deck GL Basics

DeckGL is a WebGL based visualization library, built by the Uber Vis Team.

While DeckGL has a lot of built-in geospatial capabilities, it by itself does not require the use of any mapping libraries. We'll go over a few basic examples here of using DeckGL with and without maps, to highlight some of the basic concepts.

For these examples, we'll be using a sample of data from the NYC Taxi Trip Records.

How it works

DeckGL uses the concept of layers for displaying data. This can be any type of data, but the most common use case is for geospatial data, or data that contains a latitude, and a longitude.

You can think of a layer as a transparent view that the data will be rendered too. This is typically overlaid on top of a map, but that's by no means a requirement. Here, we're using the DeckGL ScatterplotLayer to render all of the NYC Taxi pickups and drop-offs. Assuming our data looks something like:

1 2 3 4 5 [ { "position": [-73.96710205,40.80395889], "type": "pickup" }, { "position": [-73.96305084,40.77251816], "type": "dropoff" }, { "position": [-73.96154022,40.77421188], "type": "pickup" } ]

We can create a new ScatterplotLayer to pass into our DeckGL class

1 2 3 4 5 6 7 8 9 10 11 new ScatterplotLayer({ id: 'scatterplot', data: '/data/taxi.json', // load data from server getPosition: d => d.position, // get lng,lat from each point getColor: d => [0, 188, 255], getRadius: d => 25, opacity: 0.9, pickable: false, radiusMinPixels: 0.25, radiusMaxPixels: 30, })

And the rendered result

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 import React from 'react'; import DeckGL, { ScatterplotLayer } from 'deck.gl'; const style = { position: "relative", width: "100%", height: "600px", border: "1px solid black", }; // Viewport settings const viewState = { longitude: -73.95, latitude: 40.74, zoom: 11, minZoom: 5, maxZoom: 16, pitch: 0, bearing: 0, }; const DeckGLBasicStaticMap = () => { const layers = [ new ScatterplotLayer({ id: 'scatterplot', data: '/data/taxi.json', // load data from server getPosition: d => d.position, getColor: d => [0, 188, 255], getRadius: d => 25, opacity: 0.9, pickable: false, radiusMinPixels: 0.25, radiusMaxPixels: 30, }), ]; return ( <DeckGL viewState={viewState} layers={layers} style={style} getCursor={() => 'default'} /> ); } export default DeckGLBasicStaticMap;

You'll notice that even though we don't have a map visible, the data is displayed in a geospatial projection. That's because DeckGL uses the Map View by default. This renders the data using the Web Mercator Projection, which is the standard map view you'll see in most mapping libraries (Google, Mapbox, Leaflet).

Interacting with the View

In the last example, we have a variable for the viewState, which defines the current zoom and focus point of the MapView.

1 2 3 4 5 6 7 8 9 const viewState = { longitude: -73.95, latitude: 40.74, zoom: 11, minZoom: 5, maxZoom: 16, pitch: 0, bearing: 0, };

If we want to interact with the MapView in the ways we'd expect (Pan & Zoom), we need a way to update the viewState on interaction. Fortunately, the DeckGL component has an onViewStateChanged function that we can use to keep the state in sync with the map.

1 2 3 4 5 6 7 8 9 10 11 12 <DeckGL viewState={viewState} layers={layers} style={style} getCursor={() => 'default'} onViewStateChange={ (nextViewState) => { setViewState(nextViewState.viewState); } } controller />
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 import React, { useState } from 'react'; import DeckGL, { ScatterplotLayer } from 'deck.gl'; const style = { position: "relative", width: "100%", height: "600px", border: "1px solid black", }; // Viewport settings const initialViewState = { longitude: -73.95, latitude: 40.74, zoom: 11, minZoom: 5, maxZoom: 16, pitch: 0, bearing: 0, }; const DeckGLBasicDynamicMap = () => { const [viewState, setViewState] = useState(initialViewState); const layers = [ new ScatterplotLayer({ id: 'scatterplot', data: '/data/taxi.json', // load data from server getPosition: d => d.position, getColor: d => [0, 188, 255], getRadius: d => 25, opacity: 0.9, pickable: false, radiusMinPixels: 0.25, radiusMaxPixels: 30, }), ]; return ( <DeckGL viewState={viewState} layers={layers} style={style} getCursor={() => 'default'} onViewStateChange={ (nextViewState) => { setViewState(nextViewState.viewState); } } controller /> ); } export default DeckGLBasicDynamicMap;

You'll also noticed we added a controller prop. In order for a map to be interactive, we need to specify a controller to use. By setting the prop to true we are using the default MapController. For most use-cases, the MapController will suffice, but you can read up on other types of controllers in DeckGL Controllers.

Adding a Map

Now that we have map-like interactivity, let's go ahead and overlay our visualization on top of a map.

DeckGL integrates nicely with the react-map-gl library, which can be used for rendering maps that work with MapboxGL. For this example, we're going to be using Mapbox as the tile provider, so you'll need to sign up at mapbox.com for an API Key.

Looking back at the last example, we're able to interact with the data in the ways that we're familiar with using maps on the web: click & drag to pan, zoom to scroll.

By using react-map-gl as a child component to the DeckGL class, DeckGL will keep the underlying map view in-sync with the data visualization layers. This is sort of the Super Power of using DeckGL for visualizations, in that you can de-couple your visualization logic from your map, and DeckGL will keep everything in sync.

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 import React, { useState } from 'react'; import DeckGL, { ScatterplotLayer } from 'deck.gl'; import { StaticMap } from 'react-map-gl'; import config from '../config'; const canvasStyle = { position: "relative", width: "100%", height: "600px", border: "1px solid black", }; const mapStyle = 'mapbox://styles/mapbox/light-v9'; const mapboxApiAccessToken = config('mapboxApiAccessToken'); // Viewport settings const initialViewState = { longitude: -73.95, latitude: 40.74, zoom: 11, minZoom: 5, maxZoom: 16, pitch: 0, bearing: 0, }; const DeckGLBasicMap = () => { const [viewState, setViewState] = useState(initialViewState); const layers = [ new ScatterplotLayer({ id: 'scatterplot', data: '/data/taxi.json', // load data from server getPosition: d => d.position, getColor: d => [0, 188, 255], getRadius: d => 25, opacity: 0.9, pickable: false, radiusMinPixels: 0.25, radiusMaxPixels: 30, }), ]; return ( <DeckGL viewState={viewState} layers={layers} style={canvasStyle} getCursor={() => 'default'} onViewStateChange={ (nextViewState) => { setViewState(nextViewState.viewState); } } controller > <StaticMap mapboxApiAccessToken={mapboxApiAccessToken} mapStyle={mapStyle} /> </DeckGL> ); } export default DeckGLBasicMap;