From e84469727c699fde6f3c3fcc8ca056c65e752f91 Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Tue, 16 Feb 2016 19:15:38 -0800 Subject: [PATCH] combine featuresIn and featuresAt internally fix #1822 --- js/data/feature_tree.js | 43 +++--- js/source/geojson_source.js | 3 +- js/source/image_source.js | 6 +- js/source/raster_tile_source.js | 6 +- js/source/source.js | 34 +---- js/source/tile_pyramid.js | 63 ++++---- js/source/vector_tile_source.js | 3 +- js/source/video_source.js | 6 +- js/style/style.js | 12 +- js/ui/map.js | 21 ++- test/js/data/feature_tree.test.js | 27 ++-- test/js/source/tile_pyramid.test.js | 222 +++++++++++++++------------- test/js/style/style.test.js | 114 +++++++------- test/js/ui/map.test.js | 12 +- 14 files changed, 259 insertions(+), 313 deletions(-) diff --git a/js/data/feature_tree.js b/js/data/feature_tree.js index faf9f00c722..03eaa65fd5e 100644 --- a/js/data/feature_tree.js +++ b/js/data/feature_tree.js @@ -49,8 +49,6 @@ FeatureTree.prototype.query = function(args, styleLayersByID) { if (this.toBeInserted.length) this._load(); var params = args.params || {}, - x = args.x, - y = args.y, pixelsToTileUnits = EXTENT / args.tileSize / args.scale, result = []; @@ -73,25 +71,25 @@ FeatureTree.prototype.query = function(args, styleLayersByID) { additionalRadius = Math.max(additionalRadius, styleLayerDistance * pixelsToTileUnits); } - var bounds, symbolQueryBox, queryPolygon; - if (x !== undefined && y !== undefined) { - // a point query - bounds = [x - additionalRadius, y - additionalRadius, x + additionalRadius, y + additionalRadius]; - symbolQueryBox = new CollisionBox(new Point(x, y), 0, 0, 0, 0, args.scale, null); - queryPolygon = [new Point(x, y)]; - } else { - // a rectangle query - bounds = [ args.minX, args.minY, args.maxX, args.maxY ]; - symbolQueryBox = new CollisionBox(new Point(args.minX, args.minY), 0, 0, args.maxX - args.minX, args.maxY - args.minY, args.scale, null); - queryPolygon = [ - new Point(args.minX, args.minY), - new Point(args.maxX, args.minY), - new Point(args.maxX, args.maxY), - new Point(args.minX, args.maxY), - new Point(args.minX, args.minY) - ]; + var queryGeometry = args.queryGeometry.map(function(p) { + return new Point(p.x, p.y); + }); + + var minX = Infinity; + var minY = Infinity; + var maxX = -Infinity; + var maxY = -Infinity; + for (var i = 0; i < queryGeometry.length; i++) { + var p = queryGeometry[i]; + minX = Math.min(minX, p.x); + minY = Math.min(minY, p.y); + maxX = Math.max(maxX, p.x); + maxY = Math.max(maxY, p.y); } + var bounds = [minX - additionalRadius, minY - additionalRadius, maxX + additionalRadius, maxY + additionalRadius]; + var symbolQueryBox = new CollisionBox(new Point(minX, minY), 0, 0, maxX - minX, maxY - minY, args.scale, null); + var matching = this.rtree.search(bounds).concat(this.collisionTile.getFeaturesAt(symbolQueryBox, args.scale)); for (var k = 0; k < matching.length; k++) { @@ -146,7 +144,7 @@ FeatureTree.prototype.query = function(args, styleLayersByID) { function translate(translate, translateAnchor) { if (!translate[0] && !translate[1]) { - return queryPolygon; + return queryGeometry; } translate = Point.convert(translate); @@ -156,8 +154,8 @@ FeatureTree.prototype.query = function(args, styleLayersByID) { } var translated = []; - for (var i = 0; i < queryPolygon.length; i++) { - translated.push(queryPolygon[i].sub(translate._mult(pixelsToTileUnits))); + for (var i = 0; i < queryGeometry.length; i++) { + translated.push(queryGeometry[i].sub(translate._mult(pixelsToTileUnits))); } return translated; } @@ -199,6 +197,7 @@ function polygonIntersectsBufferedMultiPoint(polygon, rings, radius) { if (pointIntersectsBufferedLine(point, polygon, radius)) return true; } } + return false; } function polygonIntersectsMultiPolygon(polygon, multiPolygon) { diff --git a/js/source/geojson_source.js b/js/source/geojson_source.js index e38ac05bea8..53aa1c9cea0 100644 --- a/js/source/geojson_source.js +++ b/js/source/geojson_source.js @@ -137,8 +137,7 @@ GeoJSONSource.prototype = util.inherit(Evented, /** @lends GeoJSONSource.prototy getVisibleCoordinates: Source._getVisibleCoordinates, getTile: Source._getTile, - featuresAt: Source._vectorFeaturesAt, - featuresIn: Source._vectorFeaturesIn, + queryFeatures: Source._queryVectorFeatures, getTileData: Source._getVectorTileData, _updateData: function() { diff --git a/js/source/image_source.js b/js/source/image_source.js index 1b9e9d4f96f..f5ca1ba4229 100644 --- a/js/source/image_source.js +++ b/js/source/image_source.js @@ -156,11 +156,7 @@ ImageSource.prototype = util.inherit(Evented, { * be selectable, so always return an empty array. * @private */ - featuresAt: function(point, params, callback) { - return callback(null, []); - }, - - featuresIn: function(bbox, params, callback) { + queryFeatures: function(point, params, callback) { return callback(null, []); }, diff --git a/js/source/raster_tile_source.js b/js/source/raster_tile_source.js index 801bfb19dad..ee18b9a6a6b 100644 --- a/js/source/raster_tile_source.js +++ b/js/source/raster_tile_source.js @@ -116,11 +116,7 @@ RasterTileSource.prototype = util.inherit(Evented, { if (tile.texture) this.map.painter.saveTexture(tile.texture); }, - featuresAt: function(point, params, callback) { - callback(null, []); - }, - - featuresIn: function(bbox, params, callback) { + queryFeatures: function(point, params, callback) { callback(null, []); }, diff --git a/js/source/source.js b/js/source/source.js index f55687790ea..97d6b2c005e 100644 --- a/js/source/source.js +++ b/js/source/source.js @@ -67,34 +67,11 @@ exports._getVisibleCoordinates = function() { else return this._pyramid.renderedIDs().map(TileCoord.fromID); }; -exports._vectorFeaturesAt = function(coord, params, classes, zoom, bearing, callback) { +exports._queryVectorFeatures = function(queryGeometry, params, classes, zoom, bearing, callback) { if (!this._pyramid) return callback(null, []); - var result = this._pyramid.tileAt(coord); - if (!result) - return callback(null, []); - - this.dispatcher.send('query features', { - uid: result.tile.uid, - x: result.x, - y: result.y, - scale: result.scale, - tileSize: result.tileSize, - classes: classes, - zoom: zoom, - bearing: bearing, - source: this.id, - params: params - }, callback, result.tile.workerID); -}; - - -exports._vectorFeaturesIn = function(bounds, params, classes, zoom, bearing, callback) { - if (!this._pyramid) - return callback(null, []); - - var results = this._pyramid.tilesIn(bounds); + var results = this._pyramid.tilesIn(queryGeometry); if (!results) return callback(null, []); @@ -102,12 +79,9 @@ exports._vectorFeaturesIn = function(bounds, params, classes, zoom, bearing, cal this.dispatcher.send('query features', { uid: result.tile.uid, source: this.id, - minX: result.minX, - maxX: result.maxX, - minY: result.minY, - maxY: result.maxY, + queryGeometry: result.queryGeometry, scale: result.scale, - tileSize: result.tileSize, + tileSize: result.tile.tileSize, classes: classes, zoom: zoom, bearing: bearing, diff --git a/js/source/tile_pyramid.js b/js/source/tile_pyramid.js index 0618c9df2e8..b14fa75a424 100644 --- a/js/source/tile_pyramid.js +++ b/js/source/tile_pyramid.js @@ -4,6 +4,7 @@ var Tile = require('./tile'); var TileCoord = require('./tile_coord'); var Point = require('point-geometry'); var Cache = require('../util/lru_cache'); +var Coordinate = require('../geo/coordinate'); var util = require('../util/util'); var EXTENT = require('../data/bucket').EXTENT; @@ -377,57 +378,51 @@ TilePyramid.prototype = { this._cache.reset(); }, - /** - * For a given coordinate, search through our current tiles and attempt - * to find a tile at that point - * @param {Coordinate} coord - * @returns {Object} tile - * @private - */ - tileAt: function(coord) { - var ids = this.orderedIDs(); - for (var i = 0; i < ids.length; i++) { - var tile = this._tiles[ids[i]]; - var pos = tile.positionAt(coord); - if (pos && pos.x >= 0 && pos.x < EXTENT && pos.y >= 0 && pos.y < EXTENT) { - // The click is within the viewport. There is only ever one tile in - // a layer that has this property. - return { - tile: tile, - x: pos.x, - y: pos.y, - scale: Math.pow(2, this.transform.zoom - tile.coord.z), - tileSize: tile.tileSize - }; - } - } - }, - /** * Search through our current tiles and attempt to find the tiles that * cover the given bounds. - * @param {Array} bounds [minxminy, maxxmaxy] coordinates of the corners of bounding rectangle + * @param {Array} queryGeometry coordinates of the corners of bounding rectangle * @returns {Array} result items have {tile, minX, maxX, minY, maxY}, where min/max bounding values are the given bounds transformed in into the coordinate space of this tile. * @private */ - tilesIn: function(bounds) { + tilesIn: function(queryGeometry) { var result = []; var ids = this.orderedIDs(); + var minX = Infinity; + var minY = Infinity; + var maxX = -Infinity; + var maxY = -Infinity; + var z = queryGeometry[0].zoom; + + for (var k = 0; k < queryGeometry.length; k++) { + var p = queryGeometry[k]; + minX = Math.min(minX, p.column); + minY = Math.min(minY, p.row); + maxX = Math.max(maxX, p.column); + maxY = Math.max(maxY, p.row); + } + for (var i = 0; i < ids.length; i++) { var tile = this._tiles[ids[i]]; + var tileSpaceBounds = [ - tile.positionAt(bounds[0]), - tile.positionAt(bounds[1]) + tile.positionAt(new Coordinate(minX, minY, z)), + tile.positionAt(new Coordinate(maxX, maxY, z)) ]; + if (tileSpaceBounds[0].x < EXTENT && tileSpaceBounds[0].y < EXTENT && tileSpaceBounds[1].x >= 0 && tileSpaceBounds[1].y >= 0) { + + var tileSpaceQueryGeometry = []; + for (var j = 0; j < queryGeometry.length; j++) { + tileSpaceQueryGeometry.push(tile.positionAt(queryGeometry[j])); + } + result.push({ tile: tile, - minX: tileSpaceBounds[0].x, - maxX: tileSpaceBounds[1].x, - minY: tileSpaceBounds[0].y, - maxY: tileSpaceBounds[1].y + queryGeometry: tileSpaceQueryGeometry, + scale: Math.pow(2, this.transform.zoom - tile.coord.z) }); } } diff --git a/js/source/vector_tile_source.js b/js/source/vector_tile_source.js index 722c880a5de..5a49ea25e75 100644 --- a/js/source/vector_tile_source.js +++ b/js/source/vector_tile_source.js @@ -53,8 +53,7 @@ VectorTileSource.prototype = util.inherit(Evented, { getVisibleCoordinates: Source._getVisibleCoordinates, getTile: Source._getTile, - featuresAt: Source._vectorFeaturesAt, - featuresIn: Source._vectorFeaturesIn, + queryFeatures: Source._queryVectorFeatures, getTileData: Source._getVectorTileData, _loadTile: function(tile) { diff --git a/js/source/video_source.js b/js/source/video_source.js index e1c74de298c..5eac79c04d1 100644 --- a/js/source/video_source.js +++ b/js/source/video_source.js @@ -179,11 +179,7 @@ VideoSource.prototype = util.inherit(Evented, /** @lends VideoSource.prototype * return this.tile; }, - featuresAt: function(point, params, callback) { - return callback(null, []); - }, - - featuresIn: function(bbox, params, callback) { + queryFeatures: function(point, params, callback) { return callback(null, []); }, diff --git a/js/style/style.js b/js/style/style.js index d8e6a40a1f4..d14e1cf56ad 100644 --- a/js/style/style.js +++ b/js/style/style.js @@ -410,15 +410,7 @@ Style.prototype = util.inherit(Evented, { }, function(value) { return value !== undefined; }); }, - featuresAt: function(coord, params, classes, zoom, bearing, callback) { - this._queryFeatures('featuresAt', coord, params, classes, zoom, bearing, callback); - }, - - featuresIn: function(bbox, params, classes, zoom, bearing, callback) { - this._queryFeatures('featuresIn', bbox, params, classes, zoom, bearing, callback); - }, - - _queryFeatures: function(queryType, bboxOrCoords, params, classes, zoom, bearing, callback) { + queryFeatures: function(queryGeometry, params, classes, zoom, bearing, callback) { var features = []; var error = null; @@ -428,7 +420,7 @@ Style.prototype = util.inherit(Evented, { util.asyncAll(Object.keys(this.sources), function(id, callback) { var source = this.sources[id]; - source[queryType](bboxOrCoords, params, classes, zoom, bearing, function(err, result) { + source.queryFeatures(queryGeometry, params, classes, zoom, bearing, function(err, result) { if (result) features = features.concat(result); if (err) error = err; callback(); diff --git a/js/ui/map.js b/js/ui/map.js index 157b1ba30e5..aa2749d7e0b 100644 --- a/js/ui/map.js +++ b/js/ui/map.js @@ -389,7 +389,7 @@ util.extend(Map.prototype, /** @lends Map.prototype */{ featuresAt: function(point, params, callback) { var location = this.unproject(point).wrap(); var coord = this.transform.locationCoordinate(location); - this.style.featuresAt(coord, params, this._classes, this.transform.zoom, this.transform.angle, callback); + this.style.queryFeatures([coord], params, this._classes, this.transform.zoom, this.transform.angle, callback); return this; }, @@ -421,17 +421,14 @@ util.extend(Map.prototype, /** @lends Map.prototype */{ ]; } bounds = bounds.map(Point.convert.bind(Point)); - bounds = [ - new Point( - Math.min(bounds[0].x, bounds[1].x), - Math.min(bounds[0].y, bounds[1].y) - ), - new Point( - Math.max(bounds[0].x, bounds[1].x), - Math.max(bounds[0].y, bounds[1].y) - ) - ].map(this.transform.pointCoordinate.bind(this.transform)); - this.style.featuresIn(bounds, params, this._classes, this.transform.zoom, this.transform.angle, callback); + + var queryGeometry = [ + bounds[0], + new Point(bounds[1].x, bounds[0].y), + bounds[1], + new Point(bounds[0].x, bounds[1].y), + bounds[0]].map(this.transform.pointCoordinate.bind(this.transform)); + this.style.queryFeatures(queryGeometry, params, this._classes, this.transform.zoom, this.transform.angle, callback); return this; }, diff --git a/test/js/data/feature_tree.test.js b/test/js/data/feature_tree.test.js index 63c2e4358f1..4f0c2cf3a7c 100644 --- a/test/js/data/feature_tree.test.js +++ b/test/js/data/feature_tree.test.js @@ -3,6 +3,7 @@ var test = require('prova'); var vt = require('vector-tile'); var fs = require('fs'); +var Point = require('point-geometry'); var Protobuf = require('pbf'); var FeatureTree = require('../../../js/data/feature_tree'); var path = require('path'); @@ -42,8 +43,7 @@ test('featuretree', function(t) { scale: 1, tileSize: 512, params: { }, - x: 0, - y: 0 + queryGeometry: [new Point(0, 0)] }, styleLayers), []); t.end(); }); @@ -65,8 +65,7 @@ test('featuretree with args', function(t) { params: {}, scale: 1, tileSize: 512, - x: 0, - y: 0 + queryGeometry: [new Point(0, 0)] }, styleLayers), []); t.end(); }); @@ -86,8 +85,7 @@ test('featuretree point query', function(t) { params: { includeGeometry: true }, - x: -180, - y: 1780 + queryGeometry: [new Point(-180, 1780)] }, styleLayers); t.notEqual(features.length, 0, 'non-empty results for queryFeatures'); features.forEach(function(f) { @@ -116,10 +114,13 @@ test('featuretree rect query', function(t) { params: { includeGeometry: true }, - minX: 0, - minY: 3072, - maxX: 2048, - maxY: 4096 + queryGeometry: [ + new Point(0, 3072), + new Point(2048, 3072), + new Point(2048, 4096), + new Point(0, 4096), + new Point(0, 3072) + ] }, styleLayers); t.notEqual(features.length, 0, 'non-empty results for queryFeatures'); features.forEach(function(f) { @@ -164,8 +165,7 @@ test('featuretree query with layerIds', function(t) { params: { layerIds: ['water'] }, - x: -180, - y: 1780 + queryGeometry: [new Point(-180, 1780)] }, styleLayers); t.equal(features.length, 1); @@ -177,8 +177,7 @@ test('featuretree query with layerIds', function(t) { params: { layerIds: ['none'] }, - x: 1842, - y: 2014 + queryGeometry: [new Point(1842, 2014)] }, styleLayers); t.equal(features2.length, 0); diff --git a/test/js/source/tile_pyramid.test.js b/test/js/source/tile_pyramid.test.js index 93b2a79f147..e95d27995d6 100644 --- a/test/js/source/tile_pyramid.test.js +++ b/test/js/source/tile_pyramid.test.js @@ -241,81 +241,6 @@ test('TilePyramid#removeTile', function(t) { }); }); -test('TilePyramid#tileAt', function(t) { - t.test('regular tile', function(t) { - var pyramid = createPyramid({ - load: function(tile) { tile.loaded = true; }, - minzoom: 1, - maxzoom: 1, - tileSize: 512 - }); - - var transform = new Transform(); - transform.resize(512, 512); - transform.zoom = 1.5; - pyramid.update(true, transform); - - var result = pyramid.tileAt(new Coordinate(0, 3, 2)); - - t.deepEqual(result.tile.coord.id, 65); - t.deepEqual(result.scale, 1.4142135623730951); - t.deepEqual(result.tileSize, 512); - t.deepEqual(result.x, 0); - t.deepEqual(result.y, 4096); - - t.end(); - }); - - t.test('reparsed overscaled tile', function(t) { - var pyramid = createPyramid({ - load: function(tile) { tile.loaded = true; }, - reparseOverscaled: true, - minzoom: 1, - maxzoom: 1, - tileSize: 512 - }); - - var transform = new Transform(); - transform.resize(512, 512); - transform.zoom = 2.5; - pyramid.update(true, transform); - - var result = pyramid.tileAt(new Coordinate(0, 3, 2)); - - t.deepEqual(result.tile.coord.id, 130); - t.deepEqual(result.scale, 1.4142135623730951); - t.deepEqual(result.tileSize, 1024); - t.deepEqual(result.x, 0); - t.deepEqual(result.y, 4096); - t.end(); - }); - - t.test('overscaled tile', function(t) { - var pyramid = createPyramid({ - load: function(tile) { tile.loaded = true; }, - minzoom: 1, - maxzoom: 1, - tileSize: 512 - }); - - var transform = new Transform(); - transform.resize(512, 512); - transform.zoom = 2.5; - pyramid.update(true, transform); - - var result = pyramid.tileAt(new Coordinate(0, 3, 2)); - - t.deepEqual(result.tile.coord.id, 65); - t.deepEqual(result.scale, 2 * 1.4142135623730951); - t.deepEqual(result.tileSize, 512); - t.deepEqual(result.x, 0); - t.deepEqual(result.y, 4096); - - t.end(); - }); - t.end(); -}); - test('TilePyramid#update', function(t) { t.test('loads no tiles if used is false', function(t) { var transform = new Transform(); @@ -540,46 +465,131 @@ test('TilePyramid#clearTiles', function(t) { }); test('TilePyramid#tilesIn', function (t) { - var transform = new Transform(); - transform.resize(511, 511); - transform.zoom = 1; + t.test('regular tiles', function(t) { + var transform = new Transform(); + transform.resize(511, 511); + transform.zoom = 1; - var pyramid = createPyramid({ - load: function(tile) { - tile.loaded = true; - } + var pyramid = createPyramid({ + load: function(tile) { + tile.loaded = true; + } + }); + + pyramid.update(true, transform); + + t.deepEqual(pyramid.orderedIDs(), [ + new TileCoord(1, 0, 0).id, + new TileCoord(1, 1, 0).id, + new TileCoord(1, 0, 1).id, + new TileCoord(1, 1, 1).id + ]); + + var tiles = pyramid.tilesIn([ + new Coordinate(0.5, 0.25, 1), + new Coordinate(1.5, 0.75, 1) + ]); + + tiles.sort(function (a, b) { return a.tile.coord.x - b.tile.coord.x; }); + tiles.forEach(function (result) { delete result.tile.uid; }); + + t.equal(tiles[0].tile.coord.id, 1); + t.equal(tiles[0].tile.tileSize, 512); + t.equal(tiles[0].scale, 1); + t.deepEqual(tiles[0].queryGeometry, [{x: 4096, y: 2048}, {x:12288, y: 6144}]); + + t.equal(tiles[1].tile.coord.id, 33); + t.equal(tiles[1].tile.tileSize, 512); + t.equal(tiles[1].scale, 1); + t.deepEqual(tiles[1].queryGeometry, [{x: -4096, y: 2048}, {x: 4096, y: 6144}]); + + t.end(); }); - pyramid.update(true, transform); + t.test('reparsed overscaled tiles', function(t) { + var pyramid = createPyramid({ + load: function(tile) { tile.loaded = true; }, + reparseOverscaled: true, + minzoom: 1, + maxzoom: 1, + tileSize: 512 + }); - t.deepEqual(pyramid.orderedIDs(), [ - new TileCoord(1, 0, 0).id, - new TileCoord(1, 1, 0).id, - new TileCoord(1, 0, 1).id, - new TileCoord(1, 1, 1).id - ]); + var transform = new Transform(); + transform.resize(512, 512); + transform.zoom = 2.0; + pyramid.update(true, transform); - var tiles = pyramid.tilesIn([ - new Coordinate(0.5, 0.25, 1), - new Coordinate(1.5, 0.75, 1) - ]); + t.deepEqual(pyramid.orderedIDs(), [ + new TileCoord(2, 0, 0).id, + new TileCoord(2, 1, 0).id, + new TileCoord(2, 0, 1).id, + new TileCoord(2, 1, 1).id + ]); - tiles.sort(function (a, b) { return a.tile.coord.x - b.tile.coord.x; }); - tiles.forEach(function (result) { delete result.tile.uid; }); + var tiles = pyramid.tilesIn([ + new Coordinate(0.5, 0.25, 1), + new Coordinate(1.5, 0.75, 1) + ]); + + tiles.sort(function (a, b) { return a.tile.coord.x - b.tile.coord.x; }); + tiles.forEach(function (result) { delete result.tile.uid; }); + + t.equal(tiles[0].tile.coord.id, 2); + t.equal(tiles[0].tile.tileSize, 1024); + t.equal(tiles[0].scale, 1); + t.deepEqual(tiles[0].queryGeometry, [{x: 4096, y: 2048}, {x:12288, y: 6144}]); + + t.equal(tiles[1].tile.coord.id, 34); + t.equal(tiles[1].tile.tileSize, 1024); + t.equal(tiles[1].scale, 1); + t.deepEqual(tiles[1].queryGeometry, [{x: -4096, y: 2048}, {x: 4096, y: 6144}]); - t.equal(tiles[0].tile.coord.id, 1); - t.equal(tiles[0].minX, 4096); - t.equal(tiles[0].maxX, 12288); - t.equal(tiles[0].minY, 2048); - t.equal(tiles[0].maxY, 6144); + t.end(); + }); + + t.test('overscaled tiles', function(t) { + var pyramid = createPyramid({ + load: function(tile) { tile.loaded = true; }, + reparseOverscaled: false, + minzoom: 1, + maxzoom: 1, + tileSize: 512 + }); - t.equal(tiles[1].tile.coord.id, 33); - t.equal(tiles[1].minX, -4096); - t.equal(tiles[1].maxX, 4096); - t.equal(tiles[1].minY, 2048); - t.equal(tiles[1].maxY, 6144); + var transform = new Transform(); + transform.resize(512, 512); + transform.zoom = 2.0; + pyramid.update(true, transform); - t.equal(tiles.length, 2); + + t.deepEqual(pyramid.orderedIDs(), [ + new TileCoord(1, 0, 0).id, + new TileCoord(1, 1, 0).id, + new TileCoord(1, 0, 1).id, + new TileCoord(1, 1, 1).id + ]); + + var tiles = pyramid.tilesIn([ + new Coordinate(0.5, 0.25, 1), + new Coordinate(1.5, 0.75, 1) + ]); + + tiles.sort(function (a, b) { return a.tile.coord.x - b.tile.coord.x; }); + tiles.forEach(function (result) { delete result.tile.uid; }); + + t.equal(tiles[0].tile.coord.id, 1); + t.equal(tiles[0].tile.tileSize, 512); + t.equal(tiles[0].scale, 2); + t.deepEqual(tiles[0].queryGeometry, [{x: 4096, y: 2048}, {x:12288, y: 6144}]); + + t.equal(tiles[1].tile.coord.id, 33); + t.equal(tiles[1].tile.tileSize, 512); + t.equal(tiles[1].scale, 2); + t.deepEqual(tiles[1].queryGeometry, [{x: -4096, y: 2048}, {x: 4096, y: 6144}]); + + t.end(); + }); t.end(); }); diff --git a/test/js/style/style.test.js b/test/js/style/style.test.js index 32af5fa7310..7906fa5b1d8 100644 --- a/test/js/style/style.test.js +++ b/test/js/style/style.test.js @@ -855,7 +855,7 @@ test('Style#setLayerZoomRange', function(t) { }); }); -test('Style#featuresAt - race condition', function(t) { +test('Style#queryFeatures - race condition', function(t) { var style = new Style({ "version": 8, "sources": { @@ -883,7 +883,7 @@ test('Style#featuresAt - race condition', function(t) { style._cascade([]); style._recalculate(0); - style.sources.mapbox.featuresAt = function(position, params, classes, zoom, bearing, callback) { + style.sources.mapbox.queryFeatures = function(position, params, classes, zoom, bearing, callback) { var features = [{ type: 'Feature', layer: 'land', @@ -895,8 +895,8 @@ test('Style#featuresAt - race condition', function(t) { }, 10); }; - t.test('featuresAt race condition', function(t) { - style.featuresAt([256, 256], {}, {}, 0, 0, function(err, results) { + t.test('queryFeatures race condition', function(t) { + style.queryFeatures([256, 256], {}, {}, 0, 0, function(err, results) { t.error(err); t.equal(results.length, 0); t.end(); @@ -906,7 +906,7 @@ test('Style#featuresAt - race condition', function(t) { }); }); -test('Style#featuresAt', function(t) { +test('Style#queryFeatures', function(t) { var style = new Style({ "version": 8, "sources": { @@ -942,7 +942,7 @@ test('Style#featuresAt', function(t) { style._cascade([]); style._recalculate(0); - style.sources.mapbox.featuresAt = style.sources.mapbox.featuresIn = function(position, params, classes, zoom, bearing, callback) { + style.sources.mapbox.queryFeatures = function(position, params, classes, zoom, bearing, callback) { var features = [{ type: 'Feature', layer: 'land', @@ -974,77 +974,71 @@ test('Style#featuresAt', function(t) { }, 10); }; - [ - style.featuresAt.bind(style, [256, 256]), - style.featuresIn.bind(style, [256, 256, 512, 512]) - ].forEach(function (featuresInOrAt) { - t.test('returns feature type', function(t) { - featuresInOrAt({}, {}, 0, 0, function(err, results) { - t.error(err); - t.equal(results[0].geometry.type, 'Polygon'); - t.end(); - }); + t.test('returns feature type', function(t) { + style.queryFeatures([{column: 1, row: 1, zoom: 1}], {}, {}, 0, 0, function(err, results) { + t.error(err); + t.equal(results[0].geometry.type, 'Polygon'); + t.end(); }); + }); - t.test('filters by `layer` option', function(t) { - featuresInOrAt({layer: 'land'}, {}, 0, 0, function(err, results) { - t.error(err); - t.equal(results.length, 2); - t.end(); - }); + t.test('filters by `layer` option', function(t) { + style.queryFeatures([{column: 1, row: 1, zoom: 1}], {layer: 'land'}, {}, 0, 0, function(err, results) { + t.error(err); + t.equal(results.length, 2); + t.end(); }); + }); - t.test('includes layout properties', function(t) { - featuresInOrAt({}, {}, 0, 0, function(err, results) { - t.error(err); - var layout = results[0].layer.layout; - t.deepEqual(layout['line-cap'], 'round'); - t.end(); - }); + t.test('includes layout properties', function(t) { + style.queryFeatures([{column: 1, row: 1, zoom: 1}], {}, {}, 0, 0, function(err, results) { + t.error(err); + var layout = results[0].layer.layout; + t.deepEqual(layout['line-cap'], 'round'); + t.end(); }); + }); - t.test('includes paint properties', function(t) { - featuresInOrAt({}, {}, 0, 0, function(err, results) { - t.error(err); - t.deepEqual(results[0].layer.paint['line-color'], 'red'); - t.end(); - }); + t.test('includes paint properties', function(t) { + style.queryFeatures([{column: 1, row: 1, zoom: 1}], {}, {}, 0, 0, function(err, results) { + t.error(err); + t.deepEqual(results[0].layer.paint['line-color'], 'red'); + t.end(); }); + }); - t.test('ref layer inherits properties', function(t) { - featuresInOrAt({}, {}, 0, 0, function(err, results) { - t.error(err); + t.test('ref layer inherits properties', function(t) { + style.queryFeatures([{column: 1, row: 1, zoom: 1}], {}, {}, 0, 0, function(err, results) { + t.error(err); - var layer = results[1].layer; - var refLayer = results[2].layer; - t.deepEqual(layer.layout, refLayer.layout); - t.deepEqual(layer.type, refLayer.type); - t.deepEqual(layer.id, refLayer.ref); - t.notEqual(layer.paint, refLayer.paint); + var layer = results[1].layer; + var refLayer = results[2].layer; + t.deepEqual(layer.layout, refLayer.layout); + t.deepEqual(layer.type, refLayer.type); + t.deepEqual(layer.id, refLayer.ref); + t.notEqual(layer.paint, refLayer.paint); - t.end(); - }); + t.end(); }); + }); - t.test('includes metadata', function(t) { - featuresInOrAt({}, {}, 0, 0, function(err, results) { - t.error(err); + t.test('includes metadata', function(t) { + style.queryFeatures([{column: 1, row: 1, zoom: 1}], {}, {}, 0, 0, function(err, results) { + t.error(err); - var layer = results[0].layer; - t.equal(layer.metadata.something, 'else'); + var layer = results[0].layer; + t.equal(layer.metadata.something, 'else'); - t.end(); - }); + t.end(); }); + }); - t.test('include multiple layers', function(t) { - featuresInOrAt({layer: ['land', 'landref']}, {}, 0, 0, function(err, results) { - t.error(err); - t.equals(results.length, 3); - t.end(); - }); + t.test('include multiple layers', function(t) { + style.queryFeatures([{column: 1, row: 1, zoom: 1}], {layer: ['land', 'landref']}, {}, 0, 0, function(err, results) { + t.error(err); + t.equals(results.length, 3); + t.end(); }); - }); diff --git a/test/js/ui/map.test.js b/test/js/ui/map.test.js index 9b52011b5a1..731df508f0c 100644 --- a/test/js/ui/map.test.js +++ b/test/js/ui/map.test.js @@ -491,8 +491,8 @@ test('Map', function(t) { var opts = {}; t.test('normal coords', function(t) { - map.style.featuresAt = function (coords, o, classes, zoom, bearing, cb) { - t.deepEqual(coords, { column: 0.5, row: 0.5, zoom: 0 }); + map.style.queryFeatures = function (coords, o, classes, zoom, bearing, cb) { + t.deepEqual(coords, [{ column: 0.5, row: 0.5, zoom: 0 }]); t.equal(o, opts); t.equal(cb, callback); t.deepEqual(classes, map._classes); @@ -505,11 +505,11 @@ test('Map', function(t) { }); t.test('wraps coords', function(t) { - map.style.featuresAt = function (coords, o, classes, zoom, bearing, cb) { + map.style.queryFeatures = function (coords, o, classes, zoom, bearing, cb) { // avoid floating point issues - t.equal(parseFloat(coords.column.toFixed(4)), 0.5); - t.equal(coords.row, 0.5); - t.equal(coords.zoom, 0); + t.equal(parseFloat(coords[0].column.toFixed(4)), 0.5); + t.equal(coords[0].row, 0.5); + t.equal(coords[0].zoom, 0); t.equal(o, opts); t.deepEqual(classes, map._classes);