Skip to content

Commit

Permalink
Add pre-checking before loading feature geometry for filter expression
Browse files Browse the repository at this point in the history
  • Loading branch information
zmiao committed Feb 28, 2020
1 parent 5783244 commit e9fc60a
Show file tree
Hide file tree
Showing 12 changed files with 90 additions and 53 deletions.
47 changes: 27 additions & 20 deletions src/data/bucket/circle_bucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,27 +85,34 @@ class CircleBucket<Layer: CircleStyleLayer | HeatmapStyleLayer> implements Bucke
if (styleLayer.type === 'circle') {
circleSortKey = ((styleLayer: any): CircleStyleLayer).layout.get('circle-sort-key');
}

for (const {feature, id, index, sourceLayerIndex} of features) {
const geometry = loadGeometry(feature);
const evaluationFeature = {type: feature.type, id: ('id' in feature ? feature.id : null), properties: feature.properties, geometry};
if (this.layers[0]._featureFilter(new EvaluationParameters(this.zoom), evaluationFeature, canonical)) {
const sortKey = circleSortKey ?
circleSortKey.evaluate(evaluationFeature, {}, canonical) :
undefined;

const bucketFeature: BucketFeature = {
id,
properties: feature.properties,
type: feature.type,
sourceLayerIndex,
index,
geometry,
patterns: {},
sortKey
};

bucketFeatures.push(bucketFeature);
}
const needGeometry = this.layers[0]._featureFilter.needGeometry;
const evaluationFeature = {type: feature.type,
id: ('id' in feature ? feature.id : null),
properties: feature.properties,
geometry: needGeometry ? loadGeometry(feature) : []};

if (!this.layers[0]._featureFilter.filter(new EvaluationParameters(this.zoom), evaluationFeature, canonical)) continue;

if (!needGeometry) evaluationFeature.geometry = loadGeometry(feature);
const sortKey = circleSortKey ?
circleSortKey.evaluate(evaluationFeature, {}, canonical) :
undefined;

const bucketFeature: BucketFeature = {
id,
properties: feature.properties,
type: feature.type,
sourceLayerIndex,
index,
geometry : evaluationFeature.geometry,
patterns: {},
sortKey
};

bucketFeatures.push(bucketFeature);

}

if (circleSortKey) {
Expand Down
14 changes: 10 additions & 4 deletions src/data/bucket/fill_bucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,15 @@ class FillBucket implements Bucket {
const bucketFeatures = [];

for (const {feature, id, index, sourceLayerIndex} of features) {
const geometry = loadGeometry(feature);
const evaluationFeature = {type: feature.type, id: ('id' in feature ? feature.id : null), properties: feature.properties, geometry};
if (!this.layers[0]._featureFilter(new EvaluationParameters(this.zoom), evaluationFeature, canonical)) continue;
const needGeometry = this.layers[0]._featureFilter.needGeometry;
const evaluationFeature = {type: feature.type,
id: ('id' in feature ? feature.id : null),
properties: feature.properties,
geometry: needGeometry ? loadGeometry(feature) : []};

if (!this.layers[0]._featureFilter.filter(new EvaluationParameters(this.zoom), evaluationFeature, canonical)) continue;

if (!needGeometry) evaluationFeature.geometry = loadGeometry(feature);

const sortKey = fillSortKey ?
fillSortKey.evaluate(evaluationFeature, {}, canonical, options.availableImages) :
Expand All @@ -94,7 +100,7 @@ class FillBucket implements Bucket {
type: feature.type,
sourceLayerIndex,
index,
geometry,
geometry: evaluationFeature.geometry,
patterns: {},
sortKey
};
Expand Down
16 changes: 10 additions & 6 deletions src/data/bucket/fill_extrusion_bucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,19 @@ class FillExtrusionBucket implements Bucket {
this.hasPattern = hasPattern('fill-extrusion', this.layers, options);

for (const {feature, id, index, sourceLayerIndex} of features) {
const geometry = loadGeometry(feature);
const evaluationFeature = {type: feature.type, id: ('id' in feature ? feature.id : null), properties: feature.properties, geometry};
if (!this.layers[0]._featureFilter(new EvaluationParameters(this.zoom), evaluationFeature, canonical)) continue;
const needGeometry = this.layers[0]._featureFilter.needGeometry;
const evaluationFeature = {type: feature.type,
id: ('id' in feature ? feature.id : null),
properties: feature.properties,
geometry: needGeometry ? loadGeometry(feature) : []};

if (!this.layers[0]._featureFilter.filter(new EvaluationParameters(this.zoom), evaluationFeature, canonical)) continue;

const patternFeature: BucketFeature = {
id,
sourceLayerIndex,
index,
geometry,
geometry: needGeometry ? evaluationFeature.geometry : loadGeometry(feature),
properties: feature.properties,
type: feature.type,
patterns: {}
Expand All @@ -114,10 +118,10 @@ class FillExtrusionBucket implements Bucket {
if (this.hasPattern) {
this.features.push(addPatternDependencies('fill-extrusion', this.layers, patternFeature, this.zoom, options));
} else {
this.addFeature(patternFeature, geometry, index, canonical, {});
this.addFeature(patternFeature, patternFeature.geometry, index, canonical, {});
}

options.featureIndex.insert(feature, geometry, index, sourceLayerIndex, this.index, true);
options.featureIndex.insert(feature, patternFeature.geometry, index, sourceLayerIndex, this.index, true);
}
}

Expand Down
14 changes: 10 additions & 4 deletions src/data/bucket/line_bucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,15 @@ class LineBucket implements Bucket {
const bucketFeatures = [];

for (const {feature, id, index, sourceLayerIndex} of features) {
const geometry = loadGeometry(feature);
const evaluationFeature = {type: feature.type, id: ('id' in feature ? feature.id : null), properties: feature.properties, geometry};
if (!this.layers[0]._featureFilter(new EvaluationParameters(this.zoom), evaluationFeature, canonical)) continue;
const needGeometry = this.layers[0]._featureFilter.needGeometry;
const evaluationFeature = {type: feature.type,
id: ('id' in feature ? feature.id : null),
properties: feature.properties,
geometry: needGeometry ? loadGeometry(feature) : []};

if (!this.layers[0]._featureFilter.filter(new EvaluationParameters(this.zoom), evaluationFeature, canonical)) continue;

if (!needGeometry) evaluationFeature.geometry = loadGeometry(feature);

const sortKey = lineSortKey ?
lineSortKey.evaluate(evaluationFeature, {}, canonical) :
Expand All @@ -137,7 +143,7 @@ class LineBucket implements Bucket {
type: feature.type,
sourceLayerIndex,
index,
geometry,
geometry: evaluationFeature.geometry,
patterns: {},
sortKey
};
Expand Down
13 changes: 11 additions & 2 deletions src/data/bucket/symbol_bucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -431,10 +431,19 @@ class SymbolBucket implements Bucket {
const globalProperties = new EvaluationParameters(this.zoom);

for (const {feature, id, index, sourceLayerIndex} of features) {
const evaluationFeature = {type: feature.type, id: ('id' in feature ? feature.id : null), properties: feature.properties, geometry: loadGeometry(feature)};
if (!layer._featureFilter(globalProperties, evaluationFeature, canonical)) {

const needGeometry = layer._featureFilter.needGeometry;
const evaluationFeature = {type: feature.type,
id: ('id' in feature ? feature.id : null),
properties: feature.properties,
geometry: needGeometry ? loadGeometry(feature) : []};

if (!layer._featureFilter.filter(globalProperties, evaluationFeature, canonical)) {
continue;
}

if (!needGeometry) evaluationFeature.geometry = loadGeometry(feature);

let text: Formatted | void;
if (hasText) {
// Expression evaluation will automatically coerce to Formatted
Expand Down
3 changes: 2 additions & 1 deletion src/data/feature_index.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,9 @@ class FeatureIndex {

const sourceLayerName = this.sourceLayerCoder.decode(sourceLayerIndex);
const sourceLayer = this.vtLayers[sourceLayerName];

const feature = sourceLayer.feature(featureIndex);
if (!filter(new EvaluationParameters(this.tileID.overscaledZ), feature))
if (!filter.filter(new EvaluationParameters(this.tileID.overscaledZ), feature))
return;

const id = this.getId(feature, sourceLayerName);
Expand Down
4 changes: 1 addition & 3 deletions src/data/program_configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {PossiblyEvaluatedPropertyValue} from '../style/properties';
import {StructArrayLayout1f4, StructArrayLayout2f8, StructArrayLayout4f16, PatternLayoutArray} from './array_types';
import {clamp} from '../util/util';

import loadGeometry from './load_geometry';
import EvaluationParameters from '../style/evaluation_parameters';
import FeaturePositionMap from './feature_position_map';
import {
Expand Down Expand Up @@ -474,8 +473,7 @@ export default class ProgramConfiguration {
//AHM: Remove after https://github.com/mapbox/mapbox-gl-js/issues/6255
const value = layer.paint.get(property);
(binder: any).expression = value.value;
const evaluationFeature = {type: feature.type, id: ('id' in feature ? feature.id : null), properties: feature.properties, geometry: loadGeometry(feature)};
(binder: AttributeBinder).updatePaintArray(pos.start, pos.end, evaluationFeature, featureStates[id], imagePositions);
(binder: AttributeBinder).updatePaintArray(pos.start, pos.end, feature, featureStates[id], imagePositions);
dirty = true;
}
}
Expand Down
3 changes: 1 addition & 2 deletions src/source/tile.js
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,7 @@ class Tile {

for (let i = 0; i < layer.length; i++) {
const feature = layer.feature(i);
const evaluationFeature = {type: feature.type, id: ('id' in feature ? feature.id : null), properties: feature.properties};
if (filter(new EvaluationParameters(this.tileID.overscaledZ), evaluationFeature)) {
if (filter.filter(new EvaluationParameters(this.tileID.overscaledZ), feature)) {
const id = featureIndex.getId(feature, sourceLayer);
const geojsonFeature = new GeoJSONFeature(feature, z, x, y, id);
(geojsonFeature: any).tile = coord;
Expand Down
3 changes: 2 additions & 1 deletion src/style-spec/feature_filter/convert.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ export default function convertFilter(filter: FilterSpecification): mixed {
* produce a `false` result.
*/
function _convertFilter(filter: FilterSpecification, expectedTypes: ExpectedTypes): mixed {
if (isExpressionFilter(filter)) { return filter; }
const needGeo = false;
if (isExpressionFilter(filter, needGeo)) { return filter; }

if (!filter) return true;
const op = filter[0];
Expand Down
22 changes: 14 additions & 8 deletions src/style-spec/feature_filter/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
import {createExpression} from '../expression';
import type {GlobalProperties, Feature} from '../expression';
import type {CanonicalTileID} from '../../source/tile_id';
export type FeatureFilter = (globalProperties: GlobalProperties, feature: Feature, canonical?: CanonicalTileID) => boolean;

type FilterExpression = (globalProperties: GlobalProperties, feature: Feature, canonical?: CanonicalTileID) => boolean;
export type FeatureFilter ={filter: FilterExpression,
needGeometry: boolean};

export default createFilter;
export {isExpressionFilter};

function isExpressionFilter(filter: any) {
function isExpressionFilter(filter: any, needGeometry: boolean) {
if (filter === true || filter === false) {
return true;
}
Expand Down Expand Up @@ -38,12 +41,14 @@ function isExpressionFilter(filter: any) {
case 'any':
case 'all':
for (const f of filter.slice(1)) {
if (!isExpressionFilter(f) && typeof f !== 'boolean') {
if (!isExpressionFilter(f, needGeometry) && typeof f !== 'boolean') {
return false;
}
}
return true;

case 'within':
needGeometry = true;
return true;
default:
return true;
}
Expand Down Expand Up @@ -71,19 +76,20 @@ const filterSpec = {
*/
function createFilter(filter: any): FeatureFilter {
if (filter === null || filter === undefined) {
return () => true;
return {filter: () => true, needGeometry: false};
}

if (!isExpressionFilter(filter)) {
const withinExpr = false;
if (!isExpressionFilter(filter, withinExpr)) {
filter = convertFilter(filter);
}

const compiled = createExpression(filter, filterSpec);
if (compiled.result === 'error') {
throw new Error(compiled.value.map(err => `${err.key}: ${err.message}`).join(', '));
} else {

return (globalProperties: GlobalProperties, feature: Feature, canonical?: CanonicalTileID) => compiled.value.evaluate(globalProperties, feature, {}, canonical);
return {filter: (globalProperties: GlobalProperties, feature: Feature, canonical?: CanonicalTileID) => compiled.value.evaluate(globalProperties, feature, {}, canonical),
needGeometry: withinExpr};
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/style/style_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class StyleLayer extends Evented {

this.id = layer.id;
this.type = layer.type;
this._featureFilter = () => true;
this._featureFilter = {filter: () => true, needGeometry: false};

if (layer.type === 'custom') return;

Expand Down
2 changes: 1 addition & 1 deletion src/util/web_worker_transfer.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ register('Grid', Grid);
register('Color', Color);
register('Error', Error);
register('ResolvedImage', ResolvedImage);
register('StylePropertyFunction', StylePropertyFunction);

register('StylePropertyFunction', StylePropertyFunction);
register('StyleExpression', StyleExpression, {omit: ['_evaluator']});

register('ZoomDependentExpression', ZoomDependentExpression);
Expand Down

0 comments on commit e9fc60a

Please sign in to comment.