From 51ab398101738c7cdfe821a3aab072d7c8f4c725 Mon Sep 17 00:00:00 2001 From: ryanhamley Date: Mon, 10 Feb 2020 14:34:12 -0800 Subject: [PATCH] Evaluate query results --- src/data/feature_index.js | 13 ++++++--- src/source/query_features.js | 4 +++ src/source/tile.js | 3 +- src/style/style.js | 55 ++++++++++++++++++++++++++++++------ 4 files changed, 62 insertions(+), 13 deletions(-) diff --git a/src/data/feature_index.js b/src/data/feature_index.js index eca434d7c1b..a18a5ea9038 100644 --- a/src/data/feature_index.js +++ b/src/data/feature_index.js @@ -101,7 +101,7 @@ class FeatureIndex { } // Finds non-symbol features in this tile at a particular position. - query(args: QueryParameters, styleLayers: {[string]: StyleLayer}, sourceFeatureState: SourceFeatureState): {[string]: Array<{ featureIndex: number, feature: GeoJSONFeature }>} { + query(args: QueryParameters, styleLayers: {[string]: StyleLayer}, serializedLayers: {[string]: Object}, sourceFeatureState: SourceFeatureState): {[string]: Array<{ featureIndex: number, feature: GeoJSONFeature }>} { this.loadVTLayers(); const params = args.params || {}, @@ -146,6 +146,7 @@ class FeatureIndex { filter, params.layers, styleLayers, + serializedLayers, (feature: VectorTileFeature, styleLayer: StyleLayer, id: string | number | void) => { if (!featureGeometry) { featureGeometry = loadGeometry(feature); @@ -171,6 +172,7 @@ class FeatureIndex { filter: FeatureFilter, filterLayerIDs: Array, styleLayers: {[string]: StyleLayer}, + serializedLayers: {[string]: Object}, intersectionTest?: (feature: VectorTileFeature, styleLayer: StyleLayer, id: string | number | void) => boolean | number) { const layerIDs = this.bucketLayerIDs[bucketIndex]; @@ -202,19 +204,21 @@ class FeatureIndex { continue; } + const serializedLayer = serializedLayers[layerID]; const geojsonFeature = new GeoJSONFeature(feature, this.z, this.x, this.y, id); - (geojsonFeature: any).layer = styleLayer.serialize(); + (geojsonFeature: any).layer = serializedLayer; let layerResult = result[layerID]; if (layerResult === undefined) { layerResult = result[layerID] = []; } - layerResult.push({featureIndex, feature: geojsonFeature, intersectionZ}); + layerResult.push({featureIndex, feature: geojsonFeature, vtFeature: feature, intersectionZ}); } } // Given a set of symbol indexes that have already been looked up, // return a matching set of GeoJSONFeatures lookupSymbolFeatures(symbolFeatureIndexes: Array, + serializedLayers: {[string]: Object}, bucketIndex: number, sourceLayerIndex: number, filterSpec: FilterSpecification, @@ -233,7 +237,8 @@ class FeatureIndex { symbolFeatureIndex, filter, filterLayerIDs, - styleLayers + styleLayers, + serializedLayers ); } diff --git a/src/source/query_features.js b/src/source/query_features.js index 038690e0cdf..3efde5ec997 100644 --- a/src/source/query_features.js +++ b/src/source/query_features.js @@ -40,6 +40,7 @@ function queryIncludes3DLayer(layers?: Array, styleLayers: {[string]: St export function queryRenderedFeatures(sourceCache: SourceCache, styleLayers: {[string]: StyleLayer}, + serializedLayers: {[string]: Object}, queryGeometry: Array, params: { filter: FilterSpecification, layers: Array }, transform: Transform) { @@ -57,6 +58,7 @@ export function queryRenderedFeatures(sourceCache: SourceCache, wrappedTileID: tileIn.tileID.wrapped().key, queryResults: tileIn.tile.queryRenderedFeatures( styleLayers, + serializedLayers, sourceCache._state, tileIn.queryGeometry, tileIn.cameraQueryGeometry, @@ -86,6 +88,7 @@ export function queryRenderedFeatures(sourceCache: SourceCache, } export function queryRenderedSymbols(styleLayers: {[string]: StyleLayer}, + serializedLayers: {[string]: Object}, sourceCaches: {[string]: SourceCache}, queryGeometry: Array, params: { filter: FilterSpecification, layers: Array }, @@ -102,6 +105,7 @@ export function queryRenderedSymbols(styleLayers: {[string]: StyleLayer}, for (const queryData of bucketQueryData) { const bucketSymbols = queryData.featureIndex.lookupSymbolFeatures( renderedSymbols[queryData.bucketInstanceId], + serializedLayers, queryData.bucketIndex, queryData.sourceLayerIndex, params.filter, diff --git a/src/source/tile.js b/src/source/tile.js index d1d502cbf11..09c11348b52 100644 --- a/src/source/tile.js +++ b/src/source/tile.js @@ -265,6 +265,7 @@ class Tile { // Queries non-symbol features rendered for this tile. // Symbol features are queried globally queryRenderedFeatures(layers: {[string]: StyleLayer}, + serializedLayers: {[string]: Object}, sourceFeatureState: SourceFeatureState, queryGeometry: Array, cameraQueryGeometry: Array, @@ -285,7 +286,7 @@ class Tile { transform, params, queryPadding: this.queryPadding * maxPitchScaleFactor - }, layers, sourceFeatureState); + }, layers, serializedLayers, sourceFeatureState); } querySourceFeatures(result: Array, params: any) { diff --git a/src/style/style.js b/src/style/style.js index 27890d5dcde..a6e5ab4dedb 100644 --- a/src/style/style.js +++ b/src/style/style.js @@ -38,6 +38,7 @@ import PauseablePlacement from './pauseable_placement'; import ZoomHistory from './zoom_history'; import CrossTileSymbolIndex from '../symbol/cross_tile_symbol_index'; import {validateCustomStyleLayer} from './style_layer/custom_style_layer'; +import {PossiblyEvaluated} from './properties'; // We're skipping validation errors with the `source.canvas` identifier in order // to continue to allow canvas sources to be added at runtime/updated in @@ -1055,6 +1056,38 @@ class Style extends Evented { return features; } + evaluateQueryResults(queryResults, featureState) { + const availableImages = this.imageManager.listImages(); + if (Object.keys(queryResults).length) { + for (const layerName in queryResults) { + const layer = this._layers[layerName]; + const layoutValues = layer.layout._values; + const paintValues = layer.paint._values; + + queryResults[layerName].forEach(result => { + if (layoutValues) { + const evaluatedLayout = {}; + Object.getOwnPropertyNames(layoutValues).forEach(property => { + const possiblyEvaluatedProperty = layoutValues[property]; + evaluatedLayout[property] = possiblyEvaluatedProperty && possiblyEvaluatedProperty.evaluate ? possiblyEvaluatedProperty.evaluate(result.vtFeature, featureState, availableImages) : possiblyEvaluatedProperty; + }); + result.feature.layer.layout = evaluatedLayout; + } + + if (paintValues) { + const evaluatedPaint = {}; + Object.getOwnPropertyNames(paintValues).forEach(property => { + const possiblyEvaluatedProperty = paintValues[property] + evaluatedPaint[property] = possiblyEvaluatedProperty && possiblyEvaluatedProperty.evaluate ? possiblyEvaluatedProperty.evaluate(result.vtFeature, featureState, availableImages) : possiblyEvaluatedProperty; + }); + result.feature.layer.paint = evaluatedPaint; + } + }); + } + } + return queryResults; + } + queryRenderedFeatures(queryGeometry: any, params: any, transform: Transform) { if (params && params.filter) { this._validate(validateStyle.filter, 'queryRenderedFeatures.filter', params.filter, null, params); @@ -1078,17 +1111,22 @@ class Style extends Evented { } const sourceResults = []; + const serializedLayers = {}; + for (const layer in this._layers) { + serializedLayers[layer] = this._layers[layer].serialize(); + } for (const id in this.sourceCaches) { if (params.layers && !includedSources[id]) continue; - sourceResults.push( - queryRenderedFeatures( - this.sourceCaches[id], - this._layers, - queryGeometry, - params, - transform) - ); + let queryResults = queryRenderedFeatures( + this.sourceCaches[id], + this._layers, + serializedLayers, + queryGeometry, + params, + transform); + queryResults = this.evaluateQueryResults(queryResults, this.sourceCaches[id]._state); + sourceResults.push(queryResults); } if (this.placement) { @@ -1097,6 +1135,7 @@ class Style extends Evented { sourceResults.push( queryRenderedSymbols( this._layers, + serializedLayers, this.sourceCaches, queryGeometry, params,