This article documents the system that deck.gl offers to provide extra type annotations for layer properties.
A number of deck.gl features are enabled by rich descriptions of the types of the properties in a layer:
- Asynchronous props (e.g. loading layer data from an URL)
- Type checking (during development)
- Improve render performance, see "Prop Types and Performance" section below
- Transitions and Animation
- Reflection (e.g. dynamically creating UI controls for layer props)
A layer class may supply a static member defaultProps
that defines its default property types and values:
import {Layer} from 'deck.gl';
class MyLayer extends Layer {
// implementation
}
MyLayer.defaultProps = {
texture: {type: 'object', value: null, async: true},
strokeOpacity: {type: 'number', value: 1, min: 0, max: 1},
strokeColor: {type: 'color', value: [255, 0, 0]},
getRadius: {type: 'accessor', value: d => d.radius}
};
When the user construct this layer, the props are resolved as such:
const layer = new MyLayer({id: 'my-layer', strokeOpacity: 0.5})
/**
layer.props:
{
texture: null,
strokeOpacity: 0.5,
strokeColor: [255, 0, 0],
getRadius: d => d.radius,
// other default base Layer props
}
*/
The property types system enables layers to opt-in to specifying types, and also allows a certain amount of type auto-deduction to happen based on existing default values for layers that do not opt in.
Each prop in defaultProps
may be an object in the following shape:
type
(string, required)value
(any, required) - the default value if this prop is not suppliedasync
(boolean, optional) - iftrue
, the prop can either be a Promise that resolves to its actual value, or an url string (loaded using the base Layer's fetch prop).validate
(function, optional) - receivesvalue
as argument and returnstrue
if the value is valid. Validation of layer props is only invoked in debug mode. This function is automatically populated if the prop has a built-in type.equal
(function, optional) - receivesvalue1
,value2
as argument and returnstrue
if the two values are equal. Comparison of layer props is invoked during layer update and the result is passed tochangeFlags.propsChanged
. This function is automatically populated if the prop has a built-in type.deprecatedFor
(string|array, optional) - mark this prop as deprecated. The value is the new prop name(s) that this prop has been deprecated for. If the old prop is supplied instead of the new one, its value will be transferred to the new prop. The user will get a warning about the deprecation.- Any additional options, see individual types below.
Any value.
- Default
validate
: always pass - Default
equal
: compared by truthiness
MyLayerClass.defaultProps = {
// explicit
fill: {type: 'boolean', value: false}
// inferred
fill: false
}
A numeric value.
- Options:
min
(number, optional) - the minimum allowed valuemax
(number, optional) - the maximum allowed value
- Default
validate
: value is finite and within bounds (if specified) - Default
equal
: strict equal
MyLayerClass.defaultProps = {
// explicit, with bounds
radiusScale: {type: 'number', value: 1, min: 0}
// inferred, no bounds
radiusScale: 1
}
A RGBA color.
- Default
validate
: value is an array of 3 or 4 elements - Default
equal
: deep equal
MyLayerClass.defaultProps = {
// must be explicit
fillColor: {type: 'color', value: [255, 204, 0]}
}
An array of objects.
- Options:
optional
(boolean, optional) - acceptnull
orundefined
. Defaultfalse
.compare
(boolean, optional) - compare deeply during prop comparison. Defaultfalse
.
- Default
validate
: value is an array of 3 or 4 elements - Default
equal
: shallow equal ifcompare: false
, otherwise deep equal
MyLayerClass.defaultProps = {
// explicit
coordinateOrigin: {type: 'array', value: [0, 0, 0], compare: true}
// inferred
coordinateOrigin: [0, 0, 0]
}
An accessor used to update shader attributes.
- Default
validate
: value is either a function or the same type as the default value - Default
equal
:true
if function, otherwise deep equal
MyLayerClass.defaultProps = {
// must be explicit
getColor: {type: 'accessor', value: [255, 255, 255]}
}
A function.
- Options:
optional
(boolean, optional) - acceptnull
orundefined
. Defaultfalse
.compare
(boolean, optional) - compare strictly during prop comparison. Defaulttrue
.
- Default
validate
: value is a function - Default
equal
:true
ifcompare: false
, otherwise strict equal
MyLayerClass.defaultProps = {
// explicit
sizeScale: {type: 'function', value: x => Math.sqrt(x), compare: false}
// inferred
sizeScale: x => Math.sqrt(x)
}
The performance of a deck.gl application can be greately improved by limiting the frequency of layer updates. Consider the following app:
import React from 'react';
export class App extends React.Component {
state = {
viewState: {
latitude: 49.254,
longitude: -123.13,
zoom: 11
}
};
render() {
const layers = [
new GeoJsonLayer({
id: 'geojson',
data: DATA_URL,
extruded: true,
wireframe: true,
getElevation: f => ELEVATION_SCALE(f.properties.population),
getFillColor: f => COLOR_SCALE(f.properties.income),
getLineColor: [255, 255, 255]
})
];
return (
<DeckGL
layers={layers}
viewState={this.state.viewState}
onViewStateChange={({viewState}) => this.setState({viewState})}
controller={true}
/>
);
}
}
Each time the user interacts with the viewport, the app state is updated, and render()
is called. Because getElevation
, getFillColor
and getLineColor
are functions and arrays defined inline, they have changed from the previous render.
Usually, any prop change results in updating a layer, that is, recomputing its internal states. Updating a layer could be expensive. In GeoJsonLayer's case, it creates ScatterplotLayer, PolygonLayer and PathLayer, and those layers also need to be updated recursively.
In reality, we do not want to update GeoJsonLayer, because no layer props changed from the user's perspective. In GeoJsonLayer, these props are declared as such:
const defaultProps = {
...
getElevation: {type: 'accessor', value: 1000},
getFillColor: {type: 'accessor', value: [0, 0, 0, 255]},
getLineColor: {type: 'accessor', value: [0, 0, 0, 255]}
}
The default comparator of the access
prop type ignores shallow changes in functions. As a result, deck.gl decides that no props have changed between the two renders, and the GeoJsonLayer does not need to be updated.