UNPKG

@deck.gl/geo-layers

Version:

deck.gl layers supporting geospatial use cases and GIS formats

277 lines (254 loc) 6.57 kB
import { CompositeLayer, log } from '@deck.gl/core'; import { SimpleMeshLayer } from '@deck.gl/mesh-layers'; import { COORDINATE_SYSTEM } from '@deck.gl/core'; import { TerrainWorkerLoader } from '@loaders.gl/terrain'; import TileLayer from '../tile-layer/tile-layer'; import { urlType, getURLFromTemplate } from '../tile-layer/utils'; const DUMMY_DATA = [1]; const defaultProps = { ...TileLayer.defaultProps, elevationData: urlType, texture: { ...urlType, optional: true }, meshMaxError: { type: 'number', value: 4.0 }, bounds: { type: 'array', value: null, optional: true, compare: true }, color: { type: 'color', value: [255, 255, 255] }, elevationDecoder: { type: 'object', value: { rScaler: 1, gScaler: 0, bScaler: 0, offset: 0 } }, workerUrl: { type: 'string', value: null }, wireframe: false, material: true, loaders: [TerrainWorkerLoader] }; function urlTemplateToUpdateTrigger(template) { if (Array.isArray(template)) { return template.join(';'); } return template; } export default class TerrainLayer extends CompositeLayer { updateState({ props, oldProps }) { const elevationDataChanged = props.elevationData !== oldProps.elevationData; if (elevationDataChanged) { const { elevationData } = props; const isTiled = elevationData && (Array.isArray(elevationData) || elevationData.includes('{x}') && elevationData.includes('{y}')); this.setState({ isTiled }); } const shouldReload = elevationDataChanged || props.meshMaxError !== oldProps.meshMaxError || props.elevationDecoder !== oldProps.elevationDecoder || props.bounds !== oldProps.bounds; if (!this.state.isTiled && shouldReload) { const terrain = this.loadTerrain(props); this.setState({ terrain }); } if (props.workerUrl) { log.removed('workerUrl', 'loadOptions.terrain.workerUrl')(); } } loadTerrain({ elevationData, bounds, elevationDecoder, meshMaxError, signal }) { var _loadOptions; if (!elevationData) { return null; } let loadOptions = this.getLoadOptions(); loadOptions = { ...loadOptions, terrain: { skirtHeight: this.state.isTiled ? meshMaxError * 2 : 0, ...((_loadOptions = loadOptions) === null || _loadOptions === void 0 ? void 0 : _loadOptions.terrain), bounds, meshMaxError, elevationDecoder } }; const { fetch } = this.props; return fetch(elevationData, { propName: 'elevationData', layer: this, loadOptions, signal }); } getTiledTerrainData(tile) { const { elevationData, fetch, texture, elevationDecoder, meshMaxError } = this.props; const { viewport } = this.context; const dataUrl = getURLFromTemplate(elevationData, tile); const textureUrl = getURLFromTemplate(texture, tile); const { bbox, signal } = tile; const bottomLeft = viewport.isGeospatial ? viewport.projectFlat([bbox.west, bbox.south]) : [bbox.left, bbox.bottom]; const topRight = viewport.isGeospatial ? viewport.projectFlat([bbox.east, bbox.north]) : [bbox.right, bbox.top]; const bounds = [bottomLeft[0], bottomLeft[1], topRight[0], topRight[1]]; const terrain = this.loadTerrain({ elevationData: dataUrl, bounds, elevationDecoder, meshMaxError, signal }); const surface = textureUrl ? fetch(textureUrl, { propName: 'texture', layer: this, loaders: [], signal }).catch(_ => null) : Promise.resolve(null); return Promise.all([terrain, surface]); } renderSubLayers(props) { const SubLayerClass = this.getSubLayerClass('mesh', SimpleMeshLayer); const { data, color } = props; if (!data) { return null; } const [mesh, texture] = data; return new SubLayerClass(props, { data: DUMMY_DATA, mesh, texture, _instanced: false, coordinateSystem: COORDINATE_SYSTEM.CARTESIAN, getPosition: d => [0, 0, 0], getColor: color }); } onViewportLoad(tiles) { if (!tiles) { return; } const { zRange } = this.state; const ranges = tiles.map(tile => tile.content).filter(Boolean).map(arr => { const bounds = arr[0].header.boundingBox; return bounds.map(bound => bound[2]); }); if (ranges.length === 0) { return; } const minZ = Math.min(...ranges.map(x => x[0])); const maxZ = Math.max(...ranges.map(x => x[1])); if (!zRange || minZ < zRange[0] || maxZ > zRange[1]) { this.setState({ zRange: [minZ, maxZ] }); } } renderLayers() { const { color, material, elevationData, texture, wireframe, meshMaxError, elevationDecoder, tileSize, maxZoom, minZoom, extent, maxRequests, onTileLoad, onTileUnload, onTileError, maxCacheSize, maxCacheByteSize, refinementStrategy } = this.props; if (this.state.isTiled) { return new TileLayer(this.getSubLayerProps({ id: 'tiles' }), { wireframe, color, material, getTileData: this.getTiledTerrainData.bind(this), renderSubLayers: this.renderSubLayers.bind(this), updateTriggers: { getTileData: { elevationData: urlTemplateToUpdateTrigger(elevationData), texture: urlTemplateToUpdateTrigger(texture), meshMaxError, elevationDecoder } }, onViewportLoad: this.onViewportLoad.bind(this), zRange: this.state.zRange || null, tileSize, maxZoom, minZoom, extent, maxRequests, onTileLoad, onTileUnload, onTileError, maxCacheSize, maxCacheByteSize, refinementStrategy }); } const SubLayerClass = this.getSubLayerClass('mesh', SimpleMeshLayer); return new SubLayerClass(this.getSubLayerProps({ id: 'mesh' }), { data: DUMMY_DATA, mesh: this.state.terrain, texture, _instanced: false, getPosition: d => [0, 0, 0], getColor: color, material, wireframe }); } } TerrainLayer.layerName = 'TerrainLayer'; TerrainLayer.defaultProps = defaultProps; //# sourceMappingURL=terrain-layer.js.map