Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

queryRenderFeatures continued [not ready] #2224

Merged
merged 48 commits into from
Mar 24, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
e34197e
fix polygonIntersectsMultiPolygon
ansis Feb 24, 2016
b62895a
index collision boxes with a grid instead of rtree
ansis Mar 1, 2016
11c8bb5
store CollisionBoxes in an ArrayBuffer
ansis Mar 1, 2016
9b4f1b3
store the featureTree array in a StructArray
ansis Mar 1, 2016
3e45db3
optimize queryRenderedFeatures
ansis Mar 2, 2016
16655f7
use grid index instead of rtree for querying features
ansis Mar 2, 2016
9bd9b0a
move queryRenderedFeatures to the main thread
ansis Mar 2, 2016
571a8e7
index each line/ring of a geometry separately
ansis Mar 2, 2016
dd4f2a0
don't load geometries for symbol properties
ansis Mar 2, 2016
e72a467
remove `includeGeometry` and lazily build geojson geometry
ansis Mar 2, 2016
3da7cd4
optimize feature query filtering
ansis Mar 2, 2016
845e9d2
move querySourceFeatures to main thread
ansis Mar 2, 2016
0bbd180
remove StuctArray._struct
ansis Mar 3, 2016
73a6e57
fix queryRenderedFeatures after rotation
ansis Mar 3, 2016
abc7b93
add async version of queryRenderedFeatures
ansis Mar 3, 2016
2b971f5
fix query geojson layers
ansis Mar 4, 2016
8dc1d43
optimize polygonIntersectsBufferedMultiLine
ansis Mar 4, 2016
1227aa0
add layer property when creation geojson
ansis Mar 4, 2016
e89fe1c
add synchronous version of queryRenderedFeatures
ansis Mar 4, 2016
c84642f
only create Int32Array for non-empty cells
ansis Mar 8, 2016
a92d4e0
sort queryRenderedFeatures top-down
ansis Mar 8, 2016
a31b379
fix query tests
ansis Mar 8, 2016
14df0f3
split up sync and async query functions
ansis Mar 8, 2016
cfdda7b
fix queryRenderedFeatures for line-gap-width
ansis Mar 8, 2016
5f60ecc
fix queryRenderedFeature no-ops for raster sources
ansis Mar 8, 2016
066462e
remove callback from querySourceFeatures
ansis Mar 8, 2016
099ec01
fix symbol queryRenderedFeatures bug
ansis Mar 9, 2016
1625f6d
fix tests for queryRenderedFeatures
ansis Mar 9, 2016
d76b23f
fix render tests for queryRenderedFeatures
ansis Mar 9, 2016
0fdd868
fix queryRenderedFeatures documentation
ansis Mar 9, 2016
f8aeecf
combine duplicate parts of Buffer and StructArray
ansis Mar 9, 2016
1594938
remove dead code
ansis Mar 14, 2016
e56a49e
fix querying empty geojson tiles
ansis Mar 15, 2016
dfb96c8
rename layers property from `layer` to `layers`
ansis Mar 15, 2016
3164a64
fix querying after reloading vector tiles
ansis Mar 15, 2016
b6fe1c0
test both sync and async queries
ansis Mar 16, 2016
64a77b9
add queryRenderedFeatures changes to changelog
ansis Mar 16, 2016
41c4b70
fix querying symbols with `-allow-overlap: true`
ansis Mar 16, 2016
20cc526
update examples for new queryRenderedFeatures
ansis Mar 16, 2016
956a6e9
add query benchmarks
ansis Mar 17, 2016
d886a2e
cleanup StructArrays and queryRenderedFeatures
ansis Mar 18, 2016
25df374
make bucketIndex -> layerIDs slightly cleaner
ansis Mar 18, 2016
afa28ea
get rid of Struct._setIndex
ansis Mar 21, 2016
b1517ef
fix queryRendered features for empty tiles
ansis Mar 22, 2016
f96e807
rename StringNumberMapping -> DictionaryCoder
ansis Mar 22, 2016
e79a7ec
remove queryRenderedFeaturesAsync
ansis Mar 23, 2016
7531605
fix box queries that cross the dateline
ansis Mar 24, 2016
056fbc0
rename FeatureTree to FeatureIndex
ansis Mar 24, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
## master

#### Breaking Changes

- `map.featuresAt` and `map.featuresIn` are removed. Use `map.queryRenderedFeatures` or `map.querySourceFeatures` instead. To migrate:
- replace `featuresAt` and `featuresIn` with `queryRenderedFeatures`
- `queryRenderedFeatures` is synchronous. Remove the callback and use the return value.
- rename the `layer` parameters to `layers` and make it an array of strings.
- remove the `radius` parameter. `radius` was often used with `featuresAt` to account for style properties like `line-width` and `circle-radius`. `queryRenderedFeatures` automatically accounts for these style properties. If you need to query a larger area, make the first argument a box instead of a point.
- remove the `includeGeometry` parameter. `queryRenderedFeatures` always includes geometries.

#### New Features & Improvements

Improve overall rendering performance. (#2221)
Expand All @@ -8,6 +17,9 @@ Add `Map#setMaxBounds` method (#2234)
Add `isActive` and `isEnabled` methods to interaction handlers (#2238)
Add `Map#setZoomBounds` method (#2243)
Add touch events (#2195)
- `map.queryRenderedFeatures` can be used to query the styled and rendered representations of features
- `map.querySourceFeatures` can be used to get features directly from vector tiles, independent of the style.
- interaction for labels (#303) and style-property-aware hit testing (#316) are possible with `map.queryRenderedFeatures`

#### Bugfixes

Expand Down
2 changes: 1 addition & 1 deletion bench/buffer_benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ function runSample(stylesheet, getGlyphs, getIcons, getTile, callback) {
getTile(url, function(err, response) {
if (err) throw err;
var data = new VT.VectorTile(new Protobuf(new Uint8Array(response)));
workerTile.parse(data, layers, actor, function(err) {
workerTile.parse(data, layers, actor, null, function(err) {
if (err) return callback(err);
eachCallback();
});
Expand Down
4 changes: 3 additions & 1 deletion bench/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ function main() {

var benchmarks = {
buffer: require('./buffer_benchmark'),
fps: require('./fps_benchmark')
fps: require('./fps_benchmark'),
'query-point': require('./query_point_benchmark'),
'query-box': require('./query_box_benchmark')
};

var pathnameArray = location.pathname.split('/');
Expand Down
83 changes: 83 additions & 0 deletions bench/query_box_benchmark.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
'use strict';

var Evented = require('../js/util/evented');
var util = require('../js/util/util');

var width = 1024;
var height = 768;

var numSamples = 10;

var zoomLevels = [];
for (var i = 4; i < 19; i++) {
zoomLevels.push(i);
}

module.exports = function(options) {
var evented = util.extend({}, Evented);

var sum = 0;
var count = 0;

asyncSeries(zoomLevels.length, function(n, callback) {
var zoomLevel = zoomLevels[zoomLevels.length - n];
var map = options.createMap({
width: width,
height: height,
zoom: zoomLevel,
center: [-77.032194, 38.912753],
style: 'mapbox://styles/mapbox/streets-v8'
});
document.getElementById('map').style.display = 'none';

map.on('load', function() {

var zoomSum = 0;
var zoomCount = 0;
asyncSeries(numSamples, function(n, callback) {
var start = performance.now();
map.queryRenderedFeatures();
var duration = performance.now() - start;
sum += duration;
count++;
zoomSum += duration;
zoomCount++;
callback();
}, function() {
evented.fire('log', {
message: 'zoom ' + zoomLevel + ' average: ' + (zoomSum / zoomCount).toFixed(2) + ' ms'
});
callback();
});
});
}, done);


function done() {
var average = sum / count;
evented.fire('end', {
message: (average).toFixed(2) + ' ms',
score: average
});
}
setTimeout(function() {
evented.fire('log', {
message: 'loading assets',
color: 'dark'
});
}, 0);

return evented;
};

function asyncSeries(times, work, callback) {
if (times > 0) {
work(times, function(err) {
if (err) callback(err);
else asyncSeries(times - 1, work, callback);
});
} else {
callback();
}
}

93 changes: 93 additions & 0 deletions bench/query_point_benchmark.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
'use strict';

var Evented = require('../js/util/evented');
var util = require('../js/util/util');

var width = 1024;
var height = 768;

var zoomLevels = [];
for (var i = 4; i < 19; i++) {
zoomLevels.push(i);
}

var queryPoints = [];
var d = 20;
for (var x = 0; x < d; x++) {
for (var y = 0; y < d; y++) {
queryPoints.push([
(x / d) * width,
(y / d) * height
]);
}
}

module.exports = function(options) {
var evented = util.extend({}, Evented);

var sum = 0;
var count = 0;

asyncSeries(zoomLevels.length, function(n, callback) {
var zoomLevel = zoomLevels[zoomLevels.length - n];
var map = options.createMap({
width: width,
height: height,
zoom: zoomLevel,
center: [-77.032194, 38.912753],
style: 'mapbox://styles/mapbox/streets-v8'
});
document.getElementById('map').style.display = 'none';

map.on('load', function() {

var zoomSum = 0;
var zoomCount = 0;
asyncSeries(queryPoints.length, function(n, callback) {
var queryPoint = queryPoints[queryPoints.length - n];
var start = performance.now();
map.queryRenderedFeatures(queryPoint);
var duration = performance.now() - start;
sum += duration;
count++;
zoomSum += duration;
zoomCount++;
callback();
}, function() {
evented.fire('log', {
message: 'zoom ' + zoomLevel + ' average: ' + (zoomSum / zoomCount).toFixed(2) + ' ms'
});
callback();
});
});
}, done);


function done() {
var average = sum / count;
evented.fire('end', {
message: (average).toFixed(2) + ' ms',
score: average
});
}
setTimeout(function() {
evented.fire('log', {
message: 'loading assets',
color: 'dark'
});
}, 0);

return evented;
};

function asyncSeries(times, work, callback) {
if (times > 0) {
work(times, function(err) {
if (err) callback(err);
else asyncSeries(times - 1, work, callback);
});
} else {
callback();
}
}

26 changes: 11 additions & 15 deletions docs/_posts/examples/3400-01-04-center-on-symbol.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
layout: example
category: example
title: Center the map on a clicked marker
description: Using featuresAt and flyTo to center the map on a symbol
description: Using queryRenderedFeatures and flyTo to center the map on a symbol
---
<div id='map'></div>
<script>
Expand Down Expand Up @@ -73,26 +73,22 @@
});

map.on('click', function (e) {
// Use featuresAt to get features within a given radius of the click event
// Use queryRenderedFeatures to get features at a click event's point
// Use layer option to avoid getting results from other layers
map.featuresAt(e.point, {layer: 'symbols', radius: 10, includeGeometry: true}, function (err, features) {
if (err) throw err;
// if there are features within the given radius of the click event,
// fly to the location of the click event
if (features.length) {
// Get coordinates from the symbol and center the map on those coordinates
map.flyTo({center: features[0].geometry.coordinates});
}
});
var features = map.queryRenderedFeatures(e.point, { layers: ['symbols'] });
// if there are features within the given radius of the click event,
// fly to the location of the click event
if (features.length) {
// Get coordinates from the symbol and center the map on those coordinates
map.flyTo({center: features[0].geometry.coordinates});
}
});


// Use the same approach as above to indicate that the symbols are clickable
// by changing the cursor style to 'pointer'.
map.on('mousemove', function (e) {
map.featuresAt(e.point, {layer: 'symbols', radius: 10}, function (err, features) {
if (err) throw err;
map.getCanvas().style.cursor = features.length ? 'pointer' : '';
});
var features = map.queryRenderedFeatures(e.point, { layers: ['symbols'] });
map.getCanvas().style.cursor = features.length ? 'pointer' : '';
});
</script>
18 changes: 7 additions & 11 deletions docs/_posts/examples/3400-01-04-hover-styles.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
layout: example
category: example
title: Highlight features under the mouse pointer
description: Using featuresAt and a filter to change hover styles
description: Using queryRenderedFeatures and a filter to change hover styles
---
<div id='map'></div>
<script>
Expand Down Expand Up @@ -59,16 +59,12 @@
// If a feature is found, then we'll update the filter in the route-hover
// layer to only show that state, thus making a hover effect.
map.on("mousemove", function(e) {
map.featuresAt(e.point, {
radius: 5,
layer: ["state-fills"]
}, function (err, features) {
if (!err && features.length) {
map.setFilter("route-hover", ["==", "name", features[0].properties.name]);
} else {
map.setFilter("route-hover", ["==", "name", ""]);
}
});
var features = map.queryRenderedFeatures(e.point, { layers: ["state-fills"] });
if (features.length) {
map.setFilter("route-hover", ["==", "name", features[0].properties.name]);
} else {
map.setFilter("route-hover", ["==", "name", ""]);
}
});
});
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
layout: example
category: example
title: Get features under the mouse pointer
description: Using the featuresAt API to show properties of hovered-over map elements.
description: Using the queryRenderedFeatures API to show properties of hovered-over map elements.
---
<style>
#features {
Expand Down Expand Up @@ -34,10 +34,8 @@
});

map.on('mousemove', function (e) {
map.featuresAt(e.point, {radius: 5}, function (err, features) {
if (err) throw err;
document.getElementById('features').innerHTML = JSON.stringify(features, null, 2);
});
var features = map.queryRenderedFeatures(e.point);
document.getElementById('features').innerHTML = JSON.stringify(features, null, 2);
});
});
</script>
33 changes: 11 additions & 22 deletions docs/_posts/examples/3400-01-06-polygon-popup-on-click.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,38 +43,27 @@
});
});

var popup = new mapboxgl.Popup();

// When a click event occurs near a marker icon, open a popup at the location of
// the feature, with description HTML from its properties.
map.on('click', function (e) {
map.featuresAt(e.point, {
radius: 1,
includeGeometry: true,
layer: 'states-layer'
}, function (err, features) {
var features = map.queryRenderedFeatures(e.point, { layers: ['states-layer'] });
if (!features.length) {
return;
}

if (err || !features.length) {
popup.remove();
return;
}

var feature = features[0];
var feature = features[0];

popup.setLngLat(map.unproject(e.point))
.setHTML(feature.properties.name)
.addTo(map);
});
var popup = new mapboxgl.Popup()
.setLngLat(map.unproject(e.point))
.setHTML(feature.properties.name)
.addTo(map);
});

// Use the same approach as above to indicate that the symbols are clickable
// by changing the cursor style to 'pointer'.
map.on('mousemove', function (e) {
map.featuresAt(e.point, {
radius: 1,
layer: 'states-layer'
}, function (err, features) {
map.getCanvas().style.cursor = (!err && features.length) ? 'pointer' : '';
});
var features = map.queryRenderedFeatures(e.point, { layers: ['states-layer'] });
map.getCanvas().style.cursor = (features.length) ? 'pointer' : '';
});
</script>
Loading