@deck.gl/carto
Version:
CARTO official integration with Deck.gl. Build geospatial applications using CARTO and Deck.gl.
202 lines (177 loc) • 5.72 kB
text/typescript
// deck.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors
import {
Accessor,
CompositeLayer,
CompositeLayerProps,
Layer,
LayersList,
DefaultProps,
PickingInfo
} from '@deck.gl/core';
import {ColumnLayer, ColumnLayerProps} from '@deck.gl/layers';
import {quadbinToOffset} from './quadbin-utils';
import {Raster} from './schema/carto-raster-tile-loader';
import vs from './raster-layer-vertex.glsl';
import {createBinaryProxy} from '../utils';
import {RTTModifier} from './post-process-utils';
const defaultProps: DefaultProps<RasterLayerProps> = {
...ColumnLayer.defaultProps,
extruded: false,
diskResolution: 4,
vertices: [
[-0.5, -0.5],
[0.5, -0.5],
[0.5, 0.5],
[-0.5, 0.5]
]
};
// Modified ColumnLayer with custom vertex shader
// Use RTT to avoid inter-tile seams
class RasterColumnLayer extends RTTModifier(ColumnLayer) {
static layerName = 'RasterColumnLayer';
getShaders() {
const shaders = super.getShaders();
const data = this.props.data as unknown as {data: Raster; length: number};
const BLOCK_WIDTH = data.data.blockSize ?? Math.sqrt(data.length);
return {...shaders, defines: {...shaders.defines, BLOCK_WIDTH}, vs};
}
initializeState() {
// Only add attributes needed by shader
const attributeManager = this.getAttributeManager()!;
/* eslint-disable max-len */
attributeManager.addInstanced({
instanceElevations: {
size: 1,
transition: true,
accessor: 'getElevation'
},
instanceFillColors: {
size: this.props.colorFormat.length,
type: 'unorm8',
transition: true,
accessor: 'getFillColor',
defaultValue: [0, 0, 0, 255]
},
instanceLineColors: {
size: this.props.colorFormat.length,
type: 'unorm8',
transition: true,
accessor: 'getLineColor',
defaultValue: [255, 255, 255, 255]
}
});
}
}
/** All properties supported by RasterLayer. */
export type RasterLayerProps<DataT = unknown> = _RasterLayerProps &
ColumnLayerProps<DataT> &
CompositeLayerProps;
/** Properties added by RasterLayer. */
type _RasterLayerProps = {
/**
* Quadbin index of tile
*/
tileIndex: bigint;
};
type RasterColumnLayerData = {
data: Raster;
length: number;
};
function wrappedDataComparator(oldData: RasterColumnLayerData, newData: RasterColumnLayerData) {
return oldData.data === newData.data && oldData.length === newData.length;
}
// Adapter layer around RasterColumnLayer that converts data & accessors into correct format
export default class RasterLayer<DataT = any, ExtraProps = {}> extends CompositeLayer<
Required<RasterLayerProps<DataT>> & ExtraProps
> {
static layerName = 'RasterLayer';
static defaultProps = defaultProps;
state!: {
highlightedObjectIndex: number;
highlightColor: number[];
};
renderLayers(): Layer | null | LayersList {
// Rendering props underlying layer
const {
data,
getElevation,
getFillColor,
getLineColor,
getLineWidth,
tileIndex,
updateTriggers
} = this.props as typeof this.props & {data: Raster};
if (!data || !tileIndex || (data as any).length === 0) return null;
const blockSize = data.blockSize ?? 0;
const [xOffset, yOffset, scale] = quadbinToOffset(tileIndex);
const offset = [xOffset, yOffset];
const lineWidthScale = scale / blockSize;
// Filled Column Layer
const CellLayer = this.getSubLayerClass('column', RasterColumnLayer);
const {highlightedObjectIndex, highlightColor} = this.state;
return new CellLayer(
this.props,
this.getSubLayerProps({
id: 'cell',
updateTriggers,
getElevation: this.getSubLayerAccessor(getElevation),
getFillColor: this.getSubLayerAccessor(getFillColor),
getLineColor: this.getSubLayerAccessor(getLineColor),
getLineWidth: this.getSubLayerAccessor(getLineWidth)
}),
{
data: {
data, // Pass through data for getSubLayerAccessor()
length: blockSize * blockSize
},
dataComparator: wrappedDataComparator,
offset,
lineWidthScale, // Re-use widthScale prop to pass cell scale,
highlightedObjectIndex,
highlightColor
}
);
}
protected getSubLayerAccessor<In, Out>(accessor: Accessor<In, Out>): Accessor<In, Out> {
if (typeof accessor !== 'function') {
return super.getSubLayerAccessor(accessor);
}
// Proxy values back in standard feature format
return (object, info) => {
const {data, index} = info;
const binaryData = (data as unknown as {data: Raster}).data;
const proxy = createBinaryProxy(binaryData.cells, index);
// @ts-ignore (TS2349) accessor is always function
return accessor({properties: proxy}, info);
};
}
getPickingInfo(params: any) {
const info = super.getPickingInfo(params);
if (info.index !== -1) {
info.object = this.getSubLayerAccessor((x: any) => x)(undefined, {
data: this.props,
index: info.index
});
}
return info;
}
_updateAutoHighlight(info: PickingInfo) {
const {highlightedObjectIndex} = this.state;
let newHighlightedObjectIndex: number = -1;
if (info.index !== -1) {
newHighlightedObjectIndex = info.index;
}
if (highlightedObjectIndex !== newHighlightedObjectIndex) {
let {highlightColor} = this.props;
if (typeof highlightColor === 'function') {
highlightColor = highlightColor(info);
}
this.setState({
highlightColor,
highlightedObjectIndex: newHighlightedObjectIndex
});
}
}
}