UNPKG

@deck.gl/geo-layers

Version:

deck.gl layers supporting geospatial use cases and GIS formats

414 lines (353 loc) 10.3 kB
import { log } from '@deck.gl/core'; import { Matrix4 } from '@math.gl/core'; import { MVTWorkerLoader } from '@loaders.gl/mvt'; import { binaryToGeojson } from '@loaders.gl/gis'; import { COORDINATE_SYSTEM } from '@deck.gl/core'; import { ClipExtension } from '@deck.gl/extensions'; import TileLayer from '../tile-layer/tile-layer'; import { getURLFromTemplate, isURLTemplate } from '../tile-layer/utils'; import { transform } from './coordinate-transform'; import findIndexBinary from './find-index-binary'; import { GeoJsonLayer } from '@deck.gl/layers'; const WORLD_SIZE = 512; const defaultProps = { ...GeoJsonLayer.defaultProps, uniqueIdProperty: { type: 'string', value: '' }, highlightedFeatureId: null, loaders: [MVTWorkerLoader], binary: true }; export default class MVTLayer extends TileLayer { initializeState() { super.initializeState(); const binary = this.context.viewport.resolution !== undefined ? false : this.props.binary; this.setState({ binary, data: null, tileJSON: null }); } get isLoaded() { return this.state.data && this.state.tileset && super.isLoaded; } updateState({ props, oldProps, context, changeFlags }) { if (changeFlags.dataChanged) { this._updateTileData(); } if (this.state.data) { super.updateState({ props, oldProps, context, changeFlags }); this._setWGS84PropertyForTiles(); } const { highlightColor } = props; if (highlightColor !== oldProps.highlightColor && Array.isArray(highlightColor)) { this.setState({ highlightColor }); } } async _updateTileData() { let { data } = this.props; let tileJSON = null; if (typeof data === 'string' && !isURLTemplate(data)) { const { onDataLoad, fetch } = this.props; this.setState({ data: null, tileJSON: null }); try { tileJSON = await fetch(data, { propName: 'data', layer: this, loaders: [] }); } catch (error) { this.raiseError(error, 'loading TileJSON'); data = null; } if (onDataLoad) { onDataLoad(tileJSON); } } else if (data.tilejson) { tileJSON = data; } if (tileJSON) { data = tileJSON.tiles; } this.setState({ data, tileJSON }); } _getTilesetOptions(props) { const opts = super._getTilesetOptions(props); const { tileJSON } = this.state; if (tileJSON) { if (Number.isFinite(tileJSON.minzoom) && tileJSON.minzoom > props.minZoom) { opts.minZoom = tileJSON.minzoom; } if (Number.isFinite(tileJSON.maxzoom) && (!Number.isFinite(props.maxZoom) || tileJSON.maxzoom < props.maxZoom)) { opts.maxZoom = tileJSON.maxzoom; } } return opts; } renderLayers() { if (!this.state.data) return null; return super.renderLayers(); } getTileData(tile) { var _loadOptions; const url = getURLFromTemplate(this.state.data, tile); if (!url) { return Promise.reject('Invalid URL'); } let loadOptions = this.getLoadOptions(); const { binary } = this.state; const { fetch } = this.props; const { signal, x, y, z } = tile; loadOptions = { ...loadOptions, mimeType: 'application/x-protobuf', mvt: { ...((_loadOptions = loadOptions) === null || _loadOptions === void 0 ? void 0 : _loadOptions.mvt), coordinates: this.context.viewport.resolution ? 'wgs84' : 'local', tileIndex: { x, y, z } }, gis: binary ? { format: 'binary' } : {} }; return fetch(url, { propName: 'data', layer: this, loadOptions, signal }); } renderSubLayers(props) { const { tile } = props; const worldScale = Math.pow(2, tile.z); const xScale = WORLD_SIZE / worldScale; const yScale = -xScale; const xOffset = WORLD_SIZE * tile.x / worldScale; const yOffset = WORLD_SIZE * (1 - tile.y / worldScale); const modelMatrix = new Matrix4().scale([xScale, yScale, 1]); props.autoHighlight = false; if (!this.context.viewport.resolution) { props.modelMatrix = modelMatrix; props.coordinateOrigin = [xOffset, yOffset, 0]; props.coordinateSystem = COORDINATE_SYSTEM.CARTESIAN; props.extensions = [...(props.extensions || []), new ClipExtension()]; } const subLayers = super.renderSubLayers(props); if (this.state.binary && !(subLayers instanceof GeoJsonLayer)) { log.warn('renderSubLayers() must return GeoJsonLayer when using binary:true')(); } return subLayers; } _updateAutoHighlight(info) { const { uniqueIdProperty } = this.props; const { hoveredFeatureId, hoveredFeatureLayerName } = this.state; const hoveredFeature = info.object; let newHoveredFeatureId; let newHoveredFeatureLayerName; if (hoveredFeature) { newHoveredFeatureId = getFeatureUniqueId(hoveredFeature, uniqueIdProperty); newHoveredFeatureLayerName = getFeatureLayerName(hoveredFeature); } let { highlightColor } = this.props; if (typeof highlightColor === 'function') { highlightColor = highlightColor(info); } if (hoveredFeatureId !== newHoveredFeatureId || hoveredFeatureLayerName !== newHoveredFeatureLayerName) { this.setState({ highlightColor, hoveredFeatureId: newHoveredFeatureId, hoveredFeatureLayerName: newHoveredFeatureLayerName }); } } getPickingInfo(params) { const info = super.getPickingInfo(params); const isWGS84 = this.context.viewport.resolution; if (this.state.binary && info.index !== -1) { const { data } = params.sourceLayer.props; info.object = binaryToGeojson(data, { globalFeatureId: info.index }); } if (info.object && !isWGS84) { info.object = transformTileCoordsToWGS84(info.object, info.tile.bbox, this.context.viewport); } return info; } getSubLayerPropsByTile(tile) { return { highlightedObjectIndex: this.getHighlightedObjectIndex(tile), highlightColor: this.state.highlightColor }; } getHighlightedObjectIndex(tile) { const { hoveredFeatureId, hoveredFeatureLayerName, binary } = this.state; const { uniqueIdProperty, highlightedFeatureId } = this.props; const data = tile.content; const isHighlighted = isFeatureIdDefined(highlightedFeatureId); const isFeatureIdPresent = isFeatureIdDefined(hoveredFeatureId) || isHighlighted; if (!isFeatureIdPresent) { return -1; } const featureIdToHighlight = isHighlighted ? highlightedFeatureId : hoveredFeatureId; if (Array.isArray(data)) { return data.findIndex(feature => { const isMatchingId = getFeatureUniqueId(feature, uniqueIdProperty) === featureIdToHighlight; const isMatchingLayer = isHighlighted || getFeatureLayerName(feature) === hoveredFeatureLayerName; return isMatchingId && isMatchingLayer; }); } else if (data && binary) { return findIndexBinary(data, uniqueIdProperty, featureIdToHighlight, isHighlighted ? '' : hoveredFeatureLayerName); } return -1; } _pickObjects(maxObjects) { const { deck, viewport } = this.context; const width = viewport.width; const height = viewport.height; const x = viewport.x; const y = viewport.y; const layerIds = [this.id]; return deck.pickObjects({ x, y, width, height, layerIds, maxObjects }); } getRenderedFeatures(maxFeatures = null) { const features = this._pickObjects(maxFeatures); const featureCache = new Set(); const renderedFeatures = []; for (const f of features) { const featureId = getFeatureUniqueId(f.object, this.props.uniqueIdProperty); if (featureId === undefined) { renderedFeatures.push(f.object); } else if (!featureCache.has(featureId)) { featureCache.add(featureId); renderedFeatures.push(f.object); } } return renderedFeatures; } _setWGS84PropertyForTiles() { const propName = 'dataInWGS84'; const { tileset } = this.state; tileset.selectedTiles.forEach(tile => { if (!tile.hasOwnProperty(propName)) { Object.defineProperty(tile, propName, { get: () => { if (!tile.content) { return null; } if (this.state.binary && Array.isArray(tile.content) && !tile.content.length) { return []; } if (tile._contentWGS84 === undefined) { const content = this.state.binary ? binaryToGeojson(tile.content) : tile.content; tile._contentWGS84 = content.map(feature => transformTileCoordsToWGS84(feature, tile.bbox, this.context.viewport)); } return tile._contentWGS84; } }); } }); } } function getFeatureUniqueId(feature, uniqueIdProperty) { if (uniqueIdProperty) { return feature.properties[uniqueIdProperty]; } if ('id' in feature) { return feature.id; } return undefined; } function getFeatureLayerName(feature) { var _feature$properties; return ((_feature$properties = feature.properties) === null || _feature$properties === void 0 ? void 0 : _feature$properties.layerName) || null; } function isFeatureIdDefined(value) { return value !== undefined && value !== null && value !== ''; } function transformTileCoordsToWGS84(object, bbox, viewport) { const feature = { ...object, geometry: { type: object.geometry.type } }; Object.defineProperty(feature.geometry, 'coordinates', { get: () => { const wgs84Geom = transform(object.geometry, bbox, viewport); return wgs84Geom.coordinates; } }); return feature; } MVTLayer.layerName = 'MVTLayer'; MVTLayer.defaultProps = defaultProps; //# sourceMappingURL=mvt-layer.js.map