Skip to content

Commit

Permalink
remove queryRenderedFeaturesAsync
Browse files Browse the repository at this point in the history
let's try to get by with just queryRenderedFeatures
  • Loading branch information
ansis committed Mar 24, 2016
1 parent f96e807 commit e79a7ec
Show file tree
Hide file tree
Showing 12 changed files with 59 additions and 363 deletions.
3 changes: 1 addition & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

- `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. An async version `queryRenderedFeaturesAsync` exists for really slow queries.
- `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.
Expand All @@ -18,7 +18,6 @@ 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.queryRenderedFeaturesAsync` is an asynchronous version of `map.queryRenderedFeatures`
- `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`

Expand Down
87 changes: 13 additions & 74 deletions js/data/feature_tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,6 @@ var FeatureIndexArray = new StructArrayType({
{ type: 'Uint16', name: 'bucketIndex' }
]});

var FilteredFeatureIndexArray = new StructArrayType({
members: [
// the index of the feature in the original vectortile
{ type: 'Uint32', name: 'featureIndex' },
// the source layer the feature appears in
{ type: 'Uint16', name: 'sourceLayerIndex' },
// the bucket the feature appears in
{ type: 'Uint16', name: 'bucketIndex' },
// the layer the feature appears in
{ type: 'Uint16', name: 'layerIndex' }
]});

module.exports = FeatureTree;

function FeatureTree(coord, overscaling, collisionTile) {
Expand Down Expand Up @@ -104,15 +92,13 @@ function translateDistance(translate) {
}

// Finds features in this tile at a particular position.
FeatureTree.prototype.query = function(args, styleLayers, returnGeoJSON) {
FeatureTree.prototype.query = function(args, styleLayers) {
if (!this.vtLayers) {
this.vtLayers = new vt.VectorTile(new Protobuf(new Uint8Array(this.rawTileData))).layers;
this.sourceLayerCoder = new DictionaryCoder(this.vtLayers ? Object.keys(this.vtLayers).sort() : ['_geojsonTileLayer']);
}

var result = returnGeoJSON ?
{} :
new FilteredFeatureIndexArray();
var result = {};

var params = args.params || {},
pixelsToTileUnits = EXTENT / args.tileSize / args.scale,
Expand Down Expand Up @@ -155,11 +141,11 @@ FeatureTree.prototype.query = function(args, styleLayers, returnGeoJSON) {

var matching = this.grid.query(minX - additionalRadius, minY - additionalRadius, maxX + additionalRadius, maxY + additionalRadius);
matching.sort(topDownFeatureComparator);
this.filterMatching(result, matching, this.featureIndexArray, queryGeometry, filter, params.layers, styleLayers, args.bearing, pixelsToTileUnits, returnGeoJSON);
this.filterMatching(result, matching, this.featureIndexArray, queryGeometry, filter, params.layers, styleLayers, args.bearing, pixelsToTileUnits);

var matchingSymbols = this.collisionTile.queryRenderedSymbols(minX, minY, maxX, maxY, args.scale);
matchingSymbols.sort();
this.filterMatching(result, matchingSymbols, this.collisionTile.collisionBoxArray, queryGeometry, filter, params.layers, styleLayers, args.bearing, pixelsToTileUnits, returnGeoJSON);
this.filterMatching(result, matchingSymbols, this.collisionTile.collisionBoxArray, queryGeometry, filter, params.layers, styleLayers, args.bearing, pixelsToTileUnits);

return result;
};
Expand All @@ -176,7 +162,7 @@ function getLineWidth(paint) {
}
}

FeatureTree.prototype.filterMatching = function(result, matching, array, queryGeometry, filter, filterLayerIDs, styleLayers, bearing, pixelsToTileUnits, returnGeoJSON) {
FeatureTree.prototype.filterMatching = function(result, matching, array, queryGeometry, filter, filterLayerIDs, styleLayers, bearing, pixelsToTileUnits) {
var previousIndex;
for (var k = 0; k < matching.length; k++) {
var index = matching[k];
Expand Down Expand Up @@ -241,66 +227,19 @@ FeatureTree.prototype.filterMatching = function(result, matching, array, queryGe
}
}

if (returnGeoJSON) {
var geojsonFeature = new GeoJSONFeature(feature, this.z, this.x, this.y);
geojsonFeature.layer = styleLayer.serialize({
includeRefProperties: true
});
var layerResult = result[layerID];
if (layerResult === undefined) {
layerResult = result[layerID] = [];
}
layerResult.push(geojsonFeature);

} else {
result.emplaceBack(match.featureIndex, match.sourceLayerIndex, match.bucketIndex, l);
var geojsonFeature = new GeoJSONFeature(feature, this.z, this.x, this.y);
geojsonFeature.layer = styleLayer.serialize({
includeRefProperties: true
});
var layerResult = result[layerID];
if (layerResult === undefined) {
layerResult = result[layerID] = [];
}
layerResult.push(geojsonFeature);
}
}
};

FeatureTree.prototype.makeGeoJSON = function(featureIndexArray, styleLayers) {
if (!this.vtLayers) {
this.vtLayers = new vt.VectorTile(new Protobuf(new Uint8Array(this.rawTileData))).layers;
this.sourceLayerCoder = new DictionaryCoder(this.vtLayers ? Object.keys(this.vtLayers).sort() : ['_geojsonTileLayer']);
}

var result = {};

featureIndexArray = new FilteredFeatureIndexArray(featureIndexArray);

var cachedLayerFeatures = {};
for (var i = 0; i < featureIndexArray.length; i++) {
var indexes = featureIndexArray.get(i);
var sourceLayerName = this.sourceLayerCoder.decode(indexes.sourceLayerIndex);
var sourceLayer = this.vtLayers[sourceLayerName];
var featureIndex = indexes.featureIndex;

var cachedFeatures = cachedLayerFeatures[sourceLayerName];
if (cachedFeatures === undefined) {
cachedFeatures = cachedLayerFeatures[sourceLayerName] = {};
}

var feature = cachedFeatures[featureIndex] = cachedFeatures[featureIndex] || sourceLayer.feature(featureIndex);

var layerID = this.bucketLayerIDs[indexes.bucketIndex][indexes.layerIndex];
var styleLayer = styleLayers[layerID];
if (!styleLayer) continue;

var geojsonFeature = new GeoJSONFeature(feature, this.z, this.x, this.y);
geojsonFeature.layer = styleLayer.serialize({
includeRefProperties: true
});
var layerResult = result[layerID];
if (layerResult === undefined) {
layerResult = result[layerID] = [];
}
layerResult.push(geojsonFeature);
}

return result;
};

function translate(queryGeometry, translate, translateAnchor, bearing, pixelsToTileUnits) {
if (!translate[0] && !translate[1]) {
return queryGeometry;
Expand Down
1 change: 0 additions & 1 deletion js/source/geojson_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ GeoJSONSource.prototype = util.inherit(Evented, /** @lends GeoJSONSource.prototy
getTile: Source._getTile,

queryRenderedFeatures: Source._queryRenderedVectorFeatures,
queryRenderedFeaturesAsync: Source._queryRenderedVectorFeaturesAsync,
querySourceFeatures: Source._querySourceFeatures,

_updateData: function() {
Expand Down
41 changes: 1 addition & 40 deletions js/source/source.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,45 +92,6 @@ function mergeRenderedFeatureLayers(tiles) {
return result;
}

exports._queryRenderedVectorFeaturesAsync = function(queryGeometry, params, classes, zoom, bearing, callback) {
if (!this._pyramid)
return callback(null, {});

var tilesIn = this._pyramid.tilesIn(queryGeometry);

tilesIn.sort(sortTilesIn);

var styleLayers = this.map.style._layers;

util.asyncAll(tilesIn, function(tileIn, callback) {

if (!tileIn.tile.loaded || !tileIn.tile.featureTree) return callback();

var featureTree = tileIn.tile.featureTree.serialize();
var collisionTile = tileIn.tile.collisionTile.serialize();

this.dispatcher.send('query rendered features', {
uid: tileIn.tile.uid,
source: this.id,
queryGeometry: tileIn.queryGeometry,
scale: tileIn.scale,
tileSize: tileIn.tile.tileSize,
classes: classes,
zoom: zoom,
bearing: bearing,
params: params,
featureTree: featureTree.data,
collisionTile: collisionTile.data,
rawTileData: tileIn.tile.rawTileData.slice(0)
}, function(err, data) {
callback(err, data ? tileIn.tile.featureTree.makeGeoJSON(data, styleLayers) : {});
}, tileIn.tile.workerID);
}.bind(this), function(err, renderedFeatureLayers) {
callback(err, mergeRenderedFeatureLayers(renderedFeatureLayers));
});
};


exports._queryRenderedVectorFeatures = function(queryGeometry, params, classes, zoom, bearing) {
if (!this._pyramid)
return {};
Expand All @@ -152,7 +113,7 @@ exports._queryRenderedVectorFeatures = function(queryGeometry, params, classes,
tileSize: tileIn.tile.tileSize,
bearing: bearing,
params: params
}, styleLayers, true));
}, styleLayers));
}
return mergeRenderedFeatureLayers(renderedFeatureLayers);
};
Expand Down
1 change: 0 additions & 1 deletion js/source/vector_tile_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ VectorTileSource.prototype = util.inherit(Evented, {
getTile: Source._getTile,

queryRenderedFeatures: Source._queryRenderedVectorFeatures,
queryRenderedFeaturesAsync: Source._queryRenderedVectorFeaturesAsync,
querySourceFeatures: Source._querySourceFeatures,

_loadTile: function(tile) {
Expand Down
54 changes: 0 additions & 54 deletions js/source/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ var Actor = require('../util/actor');
var WorkerTile = require('./worker_tile');
var util = require('../util/util');
var ajax = require('../util/ajax');
var StyleLayer = require('../style/style_layer');
var vt = require('vector-tile');
var Protobuf = require('pbf');
var supercluster = require('supercluster');
Expand All @@ -14,9 +13,6 @@ var rewind = require('geojson-rewind');
var GeoJSONWrapper = require('./geojson_wrapper');
var vtpbf = require('vt-pbf');

var CollisionTile = require('../symbol/collision_tile');
var FeatureTree = require('../data/feature_tree');

module.exports = function(self) {
return new Worker(self);
};
Expand All @@ -34,25 +30,6 @@ function Worker(self) {
util.extend(Worker.prototype, {
'set layers': function(layers) {
this.layers = layers;
this.styleLayersByID = {};

var layer;
this._recalculatedZoom = null;
this._cascadedClasses = null;

for (var i = 0; i < layers.length; i++) {
layer = layers[i];
if (!layer.ref) {
this.styleLayersByID[layer.id] = StyleLayer.create(layer);
}
}

for (var k = 0; k < layers.length; k++) {
layer = layers[k];
if (layer.ref) {
this.styleLayersByID[layer.id] = StyleLayer.create(layer, this.styleLayersByID[layer.ref]);
}
}
},

'update layers': function(layers) {
Expand Down Expand Up @@ -192,36 +169,5 @@ util.extend(Worker.prototype, {
} else {
return callback(null, null); // nothing in the given tile
}
},

'query rendered features': function(params, callback) {
var tile = this.loaded[params.source] && this.loaded[params.source][params.uid];
if (tile) {

var id;

var classString = Object.keys(params.classes).join(' ');
if (this._cascadedClasses !== classString) {
this._cascadedClasses = classString;
for (id in this.styleLayersByID) {
this.styleLayersByID[id].cascade(params.classes, {transition: false}, {});
}
}

if (this._recalculatedZoom !== params.zoom) {
this._recalculatedZoom = params.zoom;
for (id in this.styleLayersByID) {
this.styleLayersByID[id].recalculate(params.zoom, { lastIntegerZoom: Infinity, lastIntegerZoomTime: 0, lastZoom: 0 });
}
}

var collisionTile = new CollisionTile(params.collisionTile, tile.collisionBoxArray);
var featureTree = new FeatureTree(params.featureTree, params.rawTileData, collisionTile);

var featureArray = featureTree.query(params, this.styleLayersByID, false).serialize();
callback(null, featureArray, [featureArray.arrayBuffer]);
} else {
callback(null, []);
}
}
});
14 changes: 0 additions & 14 deletions js/style/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -426,20 +426,6 @@ Style.prototype = util.inherit(Evented, {
return features;
},

queryRenderedFeaturesAsync: function(queryGeometry, params, classes, zoom, bearing, callback) {
util.asyncAll(Object.keys(this.sources), function(id, callback) {
var source = this.sources[id];
if (source.queryRenderedFeaturesAsync) {
source.queryRenderedFeaturesAsync(queryGeometry, params, classes, zoom, bearing, callback);
} else {
callback(null, {});
}
}.bind(this), function(err, sourceResults) {
if (err) return callback(err);
callback(null, this._flattenRenderedFeatures(sourceResults));
}.bind(this));
},

queryRenderedFeatures: function(queryGeometry, params, classes, zoom, bearing) {
var sourceResults = [];
for (var id in this.sources) {
Expand Down
49 changes: 0 additions & 49 deletions js/ui/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -378,38 +378,6 @@ util.extend(Map.prototype, /** @lends Map.prototype */{
return this.transform.pointLocation(Point.convert(point));
},

/**
* Query rendered features within a point or rectangle.
*
* @param {Point|Array<number>|Array<Point>|Array<Array<number>>} [pointOrBox] Either [x, y] pixel coordinates of a point, or [[x1, y1], [x2, y2]] pixel coordinates of opposite corners of bounding rectangle. Optional: use entire viewport if omitted.
* @param {Object} params
* @param {Array<string>} [params.layers] Only query features from layers with these layer IDs.
* @param {Array} [params.filter] A mapbox-gl-style-spec filter.
* @param {featuresCallback} callback function that receives the results
*
* @returns {Map} `this`
*
* @example
* map.queryRenderedFeaturesAsync([20, 35], { layers: ['my-layer-name'] }, function(err, features) {
* console.log(features);
* });
*
* @example
* map.queryRenderedFeaturesAsync([[10, 20], [30, 50]], { layers: ['my-layer-name'] }, function(err, features) {
* console.log(features);
* });
*/
queryRenderedFeaturesAsync: function(pointOrBox, params, callback) {
if (!(pointOrBox instanceof Point || Array.isArray(pointOrBox))) {
callback = params;
params = pointOrBox;
pointOrBox = undefined;
}
var queryGeometry = this._makeQueryGeometry(pointOrBox);
this.style.queryRenderedFeaturesAsync(queryGeometry, params, this._classes, this.transform.zoom, this.transform.angle, callback);
return this;
},

/**
* Query rendered features within a point or rectangle.
*
Expand Down Expand Up @@ -1013,23 +981,6 @@ util.extend(Map.prototype, /** @lends Map.prototype */{
}
});


/**
* Callback to receive results from `Map#queryRenderFeaturesAsync`
*
* Note: because features come from vector tiles or GeoJSON data that is converted to vector tiles internally, the returned features will be:
*
* 1. Truncated at tile boundaries.
* 2. Duplicated across tile boundaries.
*
* For example, suppose there is a highway running through your rectangle in a `queryRenderFeatures` query. `queryRenderFeatures` will only give you the parts of the highway feature that lie within the map tiles covering your rectangle, even if the road actually extends into other tiles. Also, the portion of the highway within each map tile will come back as a separate feature.
*
* @callback featuresCallback
* @param {?Error} err - An error that occurred during query processing, if any. If this parameter is non-null, the `features` parameter will be null.
* @param {?Array<Object>} features - An array of [GeoJSON](http://geojson.org/) features matching the query parameters. The GeoJSON properties of each feature are taken from the original source. Each feature object also contains a top-level `layer` property whose value is an object representing the style layer to which the feature belongs. Layout and paint properties in this object contain values which are fully evaluated for the given zoom level and feature.
*/


util.extendAll(Map.prototype, /** @lends Map.prototype */{

/**
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"express": "^4.13.4",
"gl": "^2.1.5",
"istanbul": "^0.4.2",
"mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#37fb41dad2d9651ee25f92ffd61ed97e516d5e8e",
"mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#13bfab2d5937b241259586cbf545713b251305e5",
"nyc": "^6.1.1",
"sinon": "^1.15.4",
"st": "^1.0.0",
Expand Down
Loading

0 comments on commit e79a7ec

Please sign in to comment.