Skip to content

Commit

Permalink
fix querying circles with -pitch-alignment=map
Browse files Browse the repository at this point in the history
`"circle-alignment-scaling": "map"` draws circles on the map plane
instead of the viewport plane. This fixes querying for those circles.
  • Loading branch information
ansis committed Mar 6, 2018
1 parent 3346dc6 commit dbe3c23
Show file tree
Hide file tree
Showing 20 changed files with 278 additions and 30 deletions.
11 changes: 7 additions & 4 deletions src/data/feature_index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type CollisionIndex from '../symbol/collision_index';
import type StyleLayer from '../style/style_layer';
import type {FeatureFilter} from '../style-spec/feature_filter';
import type {CollisionBoxArray} from './array_types';
import type Transform from '../geo/transform';

const {FeatureIndexArray} = require('./array_types');

Expand All @@ -25,6 +26,7 @@ type QueryParameters = {
bearing: number,
cameraToCenterDistance: number,
posMatrix: Float32Array,
transform: Transform,
tileSize: number,
queryGeometry: Array<Array<Point>>,
queryPadding: number,
Expand Down Expand Up @@ -119,13 +121,13 @@ class FeatureIndex {

const matching = this.grid.query(minX - queryPadding, minY - queryPadding, maxX + queryPadding, maxY + queryPadding);
matching.sort(topDownFeatureComparator);
this.filterMatching(result, matching, this.featureIndexArray, queryGeometry, filter, params.layers, styleLayers, args.bearing, pixelsToTileUnits, args.cameraToCenterDistance, args.posMatrix);
this.filterMatching(result, matching, this.featureIndexArray, queryGeometry, filter, params.layers, styleLayers, args.bearing, pixelsToTileUnits, args.cameraToCenterDistance, args.posMatrix, args.transform);

const matchingSymbols = args.collisionIndex ?
args.collisionIndex.queryRenderedSymbols(queryGeometry, this.tileID, args.tileSize / EXTENT, args.collisionBoxArray, args.sourceID, args.bucketInstanceIds) :
[];
matchingSymbols.sort();
this.filterMatching(result, matchingSymbols, args.collisionBoxArray, queryGeometry, filter, params.layers, styleLayers, args.bearing, pixelsToTileUnits, args.cameraToCenterDistance, args.posMatrix);
this.filterMatching(result, matchingSymbols, args.collisionBoxArray, queryGeometry, filter, params.layers, styleLayers, args.bearing, pixelsToTileUnits, args.cameraToCenterDistance, args.posMatrix, args.transform);

return result;
}
Expand All @@ -141,7 +143,8 @@ class FeatureIndex {
bearing: number,
pixelsToTileUnits: number,
cameraToCenterDistance: number,
posMatrix: Float32Array
posMatrix: Float32Array,
transform: Transform
) {
let previousIndex;
for (let k = 0; k < matching.length; k++) {
Expand Down Expand Up @@ -179,7 +182,7 @@ class FeatureIndex {
if (!geometry) {
geometry = loadGeometry(feature);
}
if (!styleLayer.queryIntersectsFeature(queryGeometry, feature, geometry, this.z, bearing, pixelsToTileUnits, cameraToCenterDistance, posMatrix)) {
if (!styleLayer.queryIntersectsFeature(queryGeometry, feature, geometry, this.z, bearing, pixelsToTileUnits, cameraToCenterDistance, posMatrix, transform)) {
continue;
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/geo/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,9 @@ class Transform {
}

getPitchScaleFactor() {
// calcMatrices hasn't run yet
if (!this.pixelMatrixInverse) return 1;

const coord = this.pointCoordinate(new Point(0, 0)).zoomTo(this.zoom);
const p = [coord.column * this.tileSize, coord.row * this.tileSize, 0, 1];
const topPoint = vec4.transformMat4(p, p, this.pixelMatrix);
Expand Down
11 changes: 6 additions & 5 deletions src/source/query_features.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import type SourceCache from './source_cache';
import type StyleLayer from '../style/style_layer';
import type Coordinate from '../geo/coordinate';
import type CollisionIndex from '../symbol/collision_index';
import type Transform from '../geo/transform';

exports.rendered = function(sourceCache: SourceCache,
styleLayers: {[string]: StyleLayer},
queryGeometry: Array<Coordinate>,
params: { filter: FilterSpecification, layers: Array<string> },
zoom: number,
bearing: number,
transform: Transform,
collisionIndex: ?CollisionIndex) {
const pitchScaleFactor = sourceCache.transform.getPitchScaleFactor();
const pitchScaleFactor = transform.getPitchScaleFactor();
const tilesIn = sourceCache.tilesIn(queryGeometry, pitchScaleFactor);

tilesIn.sort(sortTilesIn);
Expand All @@ -26,8 +26,9 @@ exports.rendered = function(sourceCache: SourceCache,
tileIn.queryGeometry,
tileIn.scale,
params,
bearing,
sourceCache.transform.cameraToCenterDistance,
transform.angle,
transform.cameraToCenterDistance,
transform,
pitchScaleFactor,
sourceCache.transform.calculatePosMatrix(tileIn.tileID.toUnwrapped()),
sourceCache.id,
Expand Down
3 changes: 3 additions & 0 deletions src/source/tile.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import type VertexBuffer from '../gl/vertex_buffer';
import type {OverscaledTileID} from './tile_id';
import type Framebuffer from '../gl/framebuffer';
import type {PerformanceResourceTiming} from '../types/performance_resource_timing';
import type Transform from '../geo/transform';

export type TileState =
| 'loading' // Tile data is in the process of loading.
Expand Down Expand Up @@ -243,6 +244,7 @@ class Tile {
params: { filter: FilterSpecification, layers: Array<string> },
bearing: number,
cameraToCenterDistance: number,
transform: Transform,
pitchScaleFactor: number,
posMatrix: Float32Array,
sourceID: string,
Expand Down Expand Up @@ -270,6 +272,7 @@ class Tile {
bearing: bearing,
cameraToCenterDistance: cameraToCenterDistance,
posMatrix: posMatrix,
transform: transform,
params: params,
queryPadding: this.queryPadding * pitchScaleFactor,
collisionBoxArray: this.collisionBoxArray,
Expand Down
4 changes: 2 additions & 2 deletions src/style/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -796,7 +796,7 @@ class Style extends Evented {
return features;
}

queryRenderedFeatures(queryGeometry: any, params: any, zoom: number, bearing: number) {
queryRenderedFeatures(queryGeometry: any, params: any, transform: Transform) {
if (params && params.filter) {
this._validate(validateStyle.filter, 'queryRenderedFeatures.filter', params.filter);
}
Expand All @@ -821,7 +821,7 @@ class Style extends Evented {
const sourceResults = [];
for (const id in this.sourceCaches) {
if (params.layers && !includedSources[id]) continue;
const results = QueryFeatures.rendered(this.sourceCaches[id], this._layers, queryGeometry, params, zoom, bearing, this.placement ? this.placement.collisionIndex : null);
const results = QueryFeatures.rendered(this.sourceCaches[id], this._layers, queryGeometry, params, transform, this.placement ? this.placement.collisionIndex : null);
sourceResults.push(results);
}
return this._flattenRenderedFeatures(sourceResults);
Expand Down
4 changes: 3 additions & 1 deletion src/style/style_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import type Point from '@mapbox/point-geometry';
import type {FeatureFilter} from '../style-spec/feature_filter';
import type {TransitionParameters} from './properties';
import type EvaluationParameters from './evaluation_parameters';
import type Transform from '../geo/transform';

const TRANSITION_SUFFIX = '-transition';

Expand Down Expand Up @@ -51,7 +52,8 @@ class StyleLayer extends Evented {
bearing: number,
pixelsToTileUnits: number,
cameraToCenterDistance: number,
posMatrix: Float32Array) => boolean;
posMatrix: Float32Array,
transform: Transform) => boolean;

constructor(layer: LayerSpecification, properties: {layout?: Properties<*>, paint: Properties<*>}) {
super();
Expand Down
42 changes: 34 additions & 8 deletions src/style/style_layer/circle_style_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ const {multiPolygonIntersectsBufferedPoint} = require('../../util/intersection_t
const {getMaximumPaintValue, translateDistance, translate} = require('../query_utils');
const properties = require('./circle_style_layer_properties');
const {vec4} = require('@mapbox/gl-matrix');
const Point = require('@mapbox/point-geometry');

import type Transform from '../../geo/transform';

const {
Transitionable,
Expand All @@ -14,7 +17,6 @@ const {
} = require('../properties');

import type {Bucket, BucketParameters} from '../../data/bucket';
import type Point from '@mapbox/point-geometry';
import type {PaintProps} from './circle_style_layer_properties';

class CircleStyleLayer extends StyleLayer {
Expand Down Expand Up @@ -44,30 +46,54 @@ class CircleStyleLayer extends StyleLayer {
bearing: number,
pixelsToTileUnits: number,
cameraToCenterDistance: number,
posMatrix: Float32Array): boolean {
posMatrix: Float32Array,
transform: Transform): boolean {
const translatedPolygon = translate(queryGeometry,
this.paint.get('circle-translate'),
this.paint.get('circle-translate-anchor'),
bearing, pixelsToTileUnits);
const radius = this.paint.get('circle-radius').evaluate(feature) * pixelsToTileUnits;
const stroke = this.paint.get('circle-stroke-width').evaluate(feature) * pixelsToTileUnits;
const radius = this.paint.get('circle-radius').evaluate(feature);
const stroke = this.paint.get('circle-stroke-width').evaluate(feature);
const size = radius + stroke;

const alignWithMap = this.paint.get('circle-pitch-alignment') === 'map';
const transformedPolygon = alignWithMap ? translatedPolygon : projectQueryGeometry(translatedPolygon, posMatrix, transform);
const transformedSize = alignWithMap ? size * pixelsToTileUnits : size;

for (const ring of geometry) {
for (const point of ring) {
let adjustedSize = size;

if (this.paint.get('circle-pitch-scale') === 'viewport') {
const projectedCenter = vec4.transformMat4([], [point.x, point.y, 0, 1], posMatrix);
const transformedPoint = alignWithMap ? point : projectPoint(point, posMatrix, transform);

let adjustedSize = transformedSize;
const projectedCenter = vec4.transformMat4([], [point.x, point.y, 0, 1], posMatrix);
if (this.paint.get('circle-pitch-scale') === 'viewport' && this.paint.get('circle-pitch-alignment') === 'map') {
adjustedSize *= projectedCenter[3] / cameraToCenterDistance;
} else if (this.paint.get('circle-pitch-scale') === 'map' && this.paint.get('circle-pitch-alignment') === 'viewport') {
adjustedSize *= cameraToCenterDistance / projectedCenter[3];
}

if (multiPolygonIntersectsBufferedPoint(translatedPolygon, point, adjustedSize)) return true;
if (multiPolygonIntersectsBufferedPoint(transformedPolygon, transformedPoint, adjustedSize)) return true;
}
}

return false;
}
}

function projectPoint(p: Point, posMatrix: Float32Array, transform: Transform) {
const point = vec4.transformMat4([], [p.x, p.y, 0, 1], posMatrix);
return new Point(
(point[0] / point[3] + 1) * transform.width * 0.5,
(point[1] / point[3] + 1) * transform.height * 0.5);
}

function projectQueryGeometry(queryGeometry: Array<Array<Point>>, posMatrix: Float32Array, transform: Transform) {
return queryGeometry.map((r) => {
return r.map((p) => {
return projectPoint(p, posMatrix, transform);
});
});
}

module.exports = CircleStyleLayer;
3 changes: 1 addition & 2 deletions src/ui/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -845,8 +845,7 @@ class Map extends Camera {
return this.style.queryRenderedFeatures(
this._makeQueryGeometry(geometry),
options,
this.transform.zoom,
this.transform.angle
this.transform
);

function isPointLike(input) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"geometry": {
"type": "Point",
"coordinates": [
-84.3310546875,
33.92512970007199
]
},
"type": "Feature",
"properties": {}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"version": 8,
"metadata": {
"test": {
"height": 256,
"queryGeometry": [
292,
35
]
}
},
"center": [
-92.3780691249957,
-20
],
"zoom": 2,
"pitch": 60,
"sources": {
"geojson": {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-84.333565, 33.925575]
}
}]
}
}
},
"layers": [
{
"id": "road",
"type": "circle",
"source": "geojson",
"paint": {
"circle-pitch-alignment": "viewport",
"circle-pitch-scale": "map",
"circle-radius": 20
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"version": 8,
"metadata": {
"test": {
"height": 256,
"queryGeometry": [
295,
35
]
}
},
"center": [
-92.3780691249957,
-20
],
"zoom": 2,
"pitch": 60,
"sources": {
"geojson": {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-84.333565, 33.925575]
}
}]
}
}
},
"layers": [
{
"id": "road",
"type": "circle",
"source": "geojson",
"paint": {
"circle-pitch-alignment": "viewport",
"circle-pitch-scale": "map",
"circle-radius": 20
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"geometry": {
"type": "Point",
"coordinates": [
-84.3310546875,
33.92512970007199
]
},
"type": "Feature",
"properties": {}
}
]
Loading

0 comments on commit dbe3c23

Please sign in to comment.