@deck.gl/core
Version:
deck.gl core library
199 lines • 8.59 kB
JavaScript
// deck.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors
import Layer from "./layer.js";
import debug from "../debug/index.js";
import { flatten } from "../utils/flatten.js";
import { PROP_TYPES_SYMBOL } from "../lifecycle/constants.js";
const TRACE_RENDER_LAYERS = 'compositeLayer.renderLayers';
class CompositeLayer extends Layer {
/** `true` if this layer renders other layers */
get isComposite() {
return true;
}
/** `true` if the layer renders to screen */
get isDrawable() {
return false;
}
/** Returns true if all async resources are loaded */
get isLoaded() {
return super.isLoaded && this.getSubLayers().every(layer => layer.isLoaded);
}
/** Return last rendered sub layers */
getSubLayers() {
return (this.internalState && this.internalState.subLayers) || [];
}
// initializeState is usually not needed for composite layers
// Provide empty definition to disable check for missing definition
// eslint-disable-next-line @typescript-eslint/no-empty-function
initializeState(context) { }
/** Updates selected state members and marks the composite layer to need rerender */
setState(updateObject) {
super.setState(updateObject);
// Trigger a layer update
// Although conceptually layer.draw and compositeLayer.renderLayers are equivalent,
// they are executed during different lifecycles.
// draw can be called without calling updateState (e.g. most viewport changes),
// while renderLayers can only be called during a recursive layer update.
this.setNeedsUpdate();
}
/** called to augment the info object that is bubbled up from a sublayer
override Layer.getPickingInfo() because decoding / setting uniform do
not apply to a composite layer. */
getPickingInfo({ info }) {
const { object } = info;
const isDataWrapped = object && object.__source && object.__source.parent && object.__source.parent.id === this.id;
if (!isDataWrapped) {
return info;
}
// override object with picked data
info.object = object.__source.object;
info.index = object.__source.index;
return info;
}
/**
* Filters sub layers at draw time. Return true if the sub layer should be drawn.
*/
filterSubLayer(context) {
return true;
}
/** Returns true if sub layer needs to be rendered */
shouldRenderSubLayer(subLayerId, data) {
return data && data.length;
}
/** Returns sub layer class for a specific sublayer */
getSubLayerClass(subLayerId, DefaultLayerClass) {
const { _subLayerProps: overridingProps } = this.props;
return ((overridingProps &&
overridingProps[subLayerId] &&
overridingProps[subLayerId].type) ||
DefaultLayerClass);
}
/** When casting user data into another format to pass to sublayers,
add reference to the original object and object index */
getSubLayerRow(row, sourceObject, sourceObjectIndex) {
// @ts-ignore (TS2339) adding undefined property
row.__source = {
parent: this,
object: sourceObject,
index: sourceObjectIndex
};
return row;
}
/** Some composite layers cast user data into another format before passing to sublayers
We need to unwrap them before calling the accessor so that they see the original data
objects */
getSubLayerAccessor(accessor) {
if (typeof accessor === 'function') {
const objectInfo = {
index: -1,
// @ts-ignore accessing resolved data
data: this.props.data,
target: []
};
return (x, i) => {
if (x && x.__source) {
objectInfo.index = x.__source.index;
// @ts-ignore (TS2349) Out is never a function
return accessor(x.__source.object, objectInfo);
}
// @ts-ignore (TS2349) Out is never a function
return accessor(x, i);
};
}
return accessor;
}
/** Returns sub layer props for a specific sublayer */
// eslint-disable-next-line complexity
getSubLayerProps(sublayerProps = {}) {
const { opacity, pickable, visible, parameters, getPolygonOffset, highlightedObjectIndex, autoHighlight, highlightColor, coordinateSystem, coordinateOrigin, wrapLongitude, positionFormat, modelMatrix, extensions, fetch, operation, _subLayerProps: overridingProps } = this.props;
const newProps = {
id: '',
updateTriggers: {},
opacity,
pickable,
visible,
parameters,
getPolygonOffset,
highlightedObjectIndex,
autoHighlight,
highlightColor,
coordinateSystem,
coordinateOrigin,
wrapLongitude,
positionFormat,
modelMatrix,
extensions,
fetch,
operation
};
const overridingSublayerProps = overridingProps && sublayerProps.id && overridingProps[sublayerProps.id];
const overridingSublayerTriggers = overridingSublayerProps && overridingSublayerProps.updateTriggers;
const sublayerId = sublayerProps.id || 'sublayer';
if (overridingSublayerProps) {
const propTypes = this.props[PROP_TYPES_SYMBOL];
const subLayerPropTypes = sublayerProps.type ? sublayerProps.type._propTypes : {};
for (const key in overridingSublayerProps) {
const propType = subLayerPropTypes[key] || propTypes[key];
// eslint-disable-next-line
if (propType && propType.type === 'accessor') {
overridingSublayerProps[key] = this.getSubLayerAccessor(overridingSublayerProps[key]);
}
}
}
Object.assign(newProps, sublayerProps,
// experimental feature that allows users to override sublayer props via parent layer prop
overridingSublayerProps);
newProps.id = `${this.props.id}-${sublayerId}`;
newProps.updateTriggers = {
all: this.props.updateTriggers?.all,
...sublayerProps.updateTriggers,
...overridingSublayerTriggers
};
// Pass through extension props
// @ts-ignore (TS2532) extensions is always defined after merging with default props
for (const extension of extensions) {
const passThroughProps = extension.getSubLayerProps.call(this, extension);
if (passThroughProps) {
Object.assign(newProps, passThroughProps, {
updateTriggers: Object.assign(newProps.updateTriggers, passThroughProps.updateTriggers)
});
}
}
return newProps;
}
/** Update sub layers to highlight the hovered object */
_updateAutoHighlight(info) {
for (const layer of this.getSubLayers()) {
layer.updateAutoHighlight(info);
}
}
/** Override base Layer method */
_getAttributeManager() {
return null;
}
/** (Internal) Called after an update to rerender sub layers */
_postUpdate(updateParams, forceUpdate) {
// @ts-ignore (TS2531) this method is only called internally when internalState is defined
let subLayers = this.internalState.subLayers;
const shouldUpdate = !subLayers || this.needsUpdate();
if (shouldUpdate) {
const subLayersList = this.renderLayers();
// Flatten the returned array, removing any null, undefined or false
// this allows layers to render sublayers conditionally
// (see CompositeLayer.renderLayers docs)
subLayers = flatten(subLayersList, Boolean);
// @ts-ignore (TS2531) this method is only called internally when internalState is defined
this.internalState.subLayers = subLayers;
}
debug(TRACE_RENDER_LAYERS, this, shouldUpdate, subLayers);
// populate reference to parent layer (this layer)
// NOTE: needs to be done even when reusing layers as the parent may have changed
for (const layer of subLayers) {
layer.parent = this;
}
}
}
CompositeLayer.layerName = 'CompositeLayer';
export default CompositeLayer;
//# sourceMappingURL=composite-layer.js.map