@deck.gl/geo-layers
Version:
deck.gl layers supporting geospatial use cases and GIS formats
436 lines (380 loc) • 10.2 kB
JavaScript
import { Geometry } from '@luma.gl/core';
import { COORDINATE_SYSTEM, CompositeLayer } from '@deck.gl/core';
import { PointCloudLayer } from '@deck.gl/layers';
import { ScenegraphLayer } from '@deck.gl/mesh-layers';
import { default as _MeshLayer } from '../mesh-layer/mesh-layer';
import { log } from '@deck.gl/core';
import { load } from '@loaders.gl/core';
import { Tileset3D, TILE_TYPE } from '@loaders.gl/tiles';
import { Tiles3DLoader } from '@loaders.gl/3d-tiles';
const SINGLE_DATA = [0];
const defaultProps = {
getPointColor: {
type: 'accessor',
value: [0, 0, 0, 255]
},
pointSize: 1.0,
data: null,
loader: Tiles3DLoader,
onTilesetLoad: {
type: 'function',
value: tileset3d => {},
compare: false
},
onTileLoad: {
type: 'function',
value: tileHeader => {},
compare: false
},
onTileUnload: {
type: 'function',
value: tileHeader => {},
compare: false
},
onTileError: {
type: 'function',
value: (tile, message, url) => {},
compare: false
},
_getMeshColor: {
type: 'function',
value: tileHeader => [255, 255, 255],
compare: false
}
};
export default class Tile3DLayer extends CompositeLayer {
initializeState() {
if ('onTileLoadFail' in this.props) {
log.removed('onTileLoadFail', 'onTileError')();
}
this.state = {
layerMap: {},
tileset3d: null,
activeViewports: {},
lastUpdatedViewports: null
};
}
get isLoaded() {
const {
tileset3d
} = this.state;
return tileset3d && tileset3d.isLoaded();
}
shouldUpdateState({
changeFlags
}) {
return changeFlags.somethingChanged;
}
updateState({
props,
oldProps,
changeFlags
}) {
if (props.data && props.data !== oldProps.data) {
this._loadTileset(props.data);
}
if (changeFlags.viewportChanged) {
const {
activeViewports
} = this.state;
const viewportsNumber = Object.keys(activeViewports).length;
if (viewportsNumber) {
this._updateTileset(activeViewports);
this.state.lastUpdatedViewports = activeViewports;
this.state.activeViewports = {};
}
}
if (changeFlags.propsChanged) {
const {
layerMap
} = this.state;
for (const key in layerMap) {
layerMap[key].needsUpdate = true;
}
}
}
activateViewport(viewport) {
const {
activeViewports,
lastUpdatedViewports
} = this.state;
this.internalState.viewport = viewport;
activeViewports[viewport.id] = viewport;
const lastViewport = lastUpdatedViewports === null || lastUpdatedViewports === void 0 ? void 0 : lastUpdatedViewports[viewport.id];
if (!lastViewport || !viewport.equals(lastViewport)) {
this.setChangeFlags({
viewportChanged: true
});
this.setNeedsUpdate();
}
}
getPickingInfo({
info,
sourceLayer
}) {
const {
layerMap
} = this.state;
const layerId = sourceLayer && sourceLayer.id;
if (layerId) {
const substr = layerId.substring(this.id.length + 1);
const tileId = substr.substring(substr.indexOf('-') + 1);
info.object = layerMap[tileId] && layerMap[tileId].tile;
}
return info;
}
filterSubLayer({
layer,
viewport
}) {
const {
tile
} = layer.props;
const {
id: viewportId
} = viewport;
return tile.selected && tile.viewportIds.includes(viewportId);
}
_updateAutoHighlight(info) {
if (info.sourceLayer) {
info.sourceLayer.updateAutoHighlight(info);
}
}
async _loadTileset(tilesetUrl) {
const {
loadOptions = {}
} = this.props;
let loader = this.props.loader || this.props.loaders;
if (Array.isArray(loader)) {
loader = loader[0];
}
const options = {
loadOptions: { ...loadOptions
}
};
if (loader.preload) {
const preloadOptions = await loader.preload(tilesetUrl, loadOptions);
if (preloadOptions.headers) {
options.loadOptions.fetch = { ...options.loadOptions.fetch,
headers: preloadOptions.headers
};
}
Object.assign(options, preloadOptions);
}
const tilesetJson = await load(tilesetUrl, loader, options.loadOptions);
const tileset3d = new Tileset3D(tilesetJson, {
onTileLoad: this._onTileLoad.bind(this),
onTileUnload: this._onTileUnload.bind(this),
onTileLoadFail: this.props.onTileError,
...options
});
this.setState({
tileset3d,
layerMap: {}
});
this._updateTileset(this.state.activeViewports);
this.props.onTilesetLoad(tileset3d);
}
_onTileLoad(tileHeader) {
const {
lastUpdatedViewports
} = this.state;
this.props.onTileLoad(tileHeader);
this._updateTileset(lastUpdatedViewports);
this.setNeedsUpdate();
}
_onTileUnload(tileHeader) {
delete this.state.layerMap[tileHeader.id];
this.props.onTileUnload(tileHeader);
}
_updateTileset(viewports) {
const {
tileset3d
} = this.state;
const {
timeline
} = this.context;
const viewportsNumber = Object.keys(viewports).length;
if (!timeline || !viewportsNumber || !tileset3d) {
return;
}
const frameNumber = tileset3d.update(Object.values(viewports));
const tilesetChanged = this.state.frameNumber !== frameNumber;
if (tilesetChanged) {
this.setState({
frameNumber
});
}
}
_getSubLayer(tileHeader, oldLayer) {
if (!tileHeader.content) {
return null;
}
switch (tileHeader.type) {
case TILE_TYPE.POINTCLOUD:
return this._makePointCloudLayer(tileHeader, oldLayer);
case TILE_TYPE.SCENEGRAPH:
return this._make3DModelLayer(tileHeader, oldLayer);
case TILE_TYPE.MESH:
return this._makeSimpleMeshLayer(tileHeader, oldLayer);
default:
throw new Error("Tile3DLayer: Failed to render layer of type ".concat(tileHeader.content.type));
}
}
_makePointCloudLayer(tileHeader, oldLayer) {
const {
attributes,
pointCount,
constantRGBA,
cartographicOrigin,
modelMatrix
} = tileHeader.content;
const {
positions,
normals,
colors
} = attributes;
if (!positions) {
return null;
}
const data = oldLayer && oldLayer.props.data || {
header: {
vertexCount: pointCount
},
attributes: {
POSITION: positions,
NORMAL: normals,
COLOR_0: colors
}
};
const {
pointSize,
getPointColor
} = this.props;
const SubLayerClass = this.getSubLayerClass('pointcloud', PointCloudLayer);
return new SubLayerClass({
pointSize
}, this.getSubLayerProps({
id: 'pointcloud'
}), {
id: "".concat(this.id, "-pointcloud-").concat(tileHeader.id),
tile: tileHeader,
data,
coordinateSystem: COORDINATE_SYSTEM.METER_OFFSETS,
coordinateOrigin: cartographicOrigin,
modelMatrix,
getColor: constantRGBA || getPointColor,
_offset: 0
});
}
_make3DModelLayer(tileHeader) {
const {
gltf,
instances,
cartographicOrigin,
modelMatrix
} = tileHeader.content;
const SubLayerClass = this.getSubLayerClass('scenegraph', ScenegraphLayer);
return new SubLayerClass({
_lighting: 'pbr'
}, this.getSubLayerProps({
id: 'scenegraph'
}), {
id: "".concat(this.id, "-scenegraph-").concat(tileHeader.id),
tile: tileHeader,
data: instances || SINGLE_DATA,
scenegraph: gltf,
coordinateSystem: COORDINATE_SYSTEM.METER_OFFSETS,
coordinateOrigin: cartographicOrigin,
modelMatrix,
getTransformMatrix: instance => instance.modelMatrix,
getPosition: [0, 0, 0],
_offset: 0
});
}
_makeSimpleMeshLayer(tileHeader, oldLayer) {
const content = tileHeader.content;
const {
attributes,
indices,
modelMatrix,
cartographicOrigin,
coordinateSystem = COORDINATE_SYSTEM.METER_OFFSETS,
material,
featureIds
} = content;
const {
_getMeshColor
} = this.props;
const geometry = oldLayer && oldLayer.props.mesh || new Geometry({
drawMode: 4,
attributes: getMeshGeometry(attributes),
indices
});
const SubLayerClass = this.getSubLayerClass('mesh', _MeshLayer);
return new SubLayerClass(this.getSubLayerProps({
id: 'mesh'
}), {
id: "".concat(this.id, "-mesh-").concat(tileHeader.id),
tile: tileHeader,
mesh: geometry,
data: SINGLE_DATA,
getColor: _getMeshColor(tileHeader),
pbrMaterial: material,
modelMatrix,
coordinateOrigin: cartographicOrigin,
coordinateSystem,
featureIds,
_offset: 0
});
}
renderLayers() {
const {
tileset3d,
layerMap
} = this.state;
if (!tileset3d) {
return null;
}
return tileset3d.tiles.map(tile => {
const layerCache = layerMap[tile.id] = layerMap[tile.id] || {
tile
};
let {
layer
} = layerCache;
if (tile.selected) {
if (!layer) {
layer = this._getSubLayer(tile);
} else if (layerCache.needsUpdate) {
layer = this._getSubLayer(tile, layer);
layerCache.needsUpdate = false;
}
}
layerCache.layer = layer;
return layer;
}).filter(Boolean);
}
}
function getMeshGeometry(contentAttributes) {
const attributes = {};
attributes.positions = { ...contentAttributes.positions,
value: new Float32Array(contentAttributes.positions.value)
};
if (contentAttributes.normals) {
attributes.normals = contentAttributes.normals;
}
if (contentAttributes.texCoords) {
attributes.texCoords = contentAttributes.texCoords;
}
if (contentAttributes.colors) {
attributes.colors = contentAttributes.colors;
}
if (contentAttributes.uvRegions) {
attributes.uvRegions = contentAttributes.uvRegions;
}
return attributes;
}
Tile3DLayer.layerName = 'Tile3DLayer';
Tile3DLayer.defaultProps = defaultProps;
//# sourceMappingURL=tile-3d-layer.js.map