UNPKG

@deck.gl/geo-layers

Version:

deck.gl layers supporting geospatial use cases and GIS formats

436 lines (380 loc) 10.2 kB
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