@deck.gl/geo-layers
Version:
deck.gl layers supporting geospatial use cases and GIS formats
277 lines (254 loc) • 6.57 kB
JavaScript
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