Skip to content

Commit

Permalink
fix box queries that cross the dateline
Browse files Browse the repository at this point in the history
  • Loading branch information
ansis committed Mar 24, 2016
1 parent e79a7ec commit 7531605
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 70 deletions.
91 changes: 56 additions & 35 deletions js/data/feature_tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,20 +123,25 @@ FeatureTree.prototype.query = function(args, styleLayers) {
additionalRadius = Math.max(additionalRadius, styleLayerDistance * pixelsToTileUnits);
}

var queryGeometry = args.queryGeometry.map(function(p) {
return new Point(p.x, p.y);
var queryGeometry = args.queryGeometry.map(function(q) {
return q.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 ring = queryGeometry[i];
for (var k = 0; k < ring.length; k++) {
var p = ring[k];
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 matching = this.grid.query(minX - additionalRadius, minY - additionalRadius, maxX + additionalRadius, maxY + additionalRadius);
Expand Down Expand Up @@ -210,20 +215,20 @@ FeatureTree.prototype.filterMatching = function(result, matching, array, queryGe
if (paint['line-offset']) {
geometry = offsetLine(geometry, paint['line-offset'] * pixelsToTileUnits);
}
if (!polygonIntersectsBufferedMultiLine(translatedPolygon, geometry, halfWidth)) continue;
if (!multiPolygonIntersectsBufferedMultiLine(translatedPolygon, geometry, halfWidth)) continue;

} else if (styleLayer.type === 'fill') {
translatedPolygon = translate(queryGeometry,
paint['fill-translate'], paint['fill-translate-anchor'],
bearing, pixelsToTileUnits);
if (!polygonIntersectsMultiPolygon(translatedPolygon, geometry)) continue;
if (!multiPolygonIntersectsMultiPolygon(translatedPolygon, geometry)) continue;

} else if (styleLayer.type === 'circle') {
translatedPolygon = translate(queryGeometry,
paint['circle-translate'], paint['circle-translate-anchor'],
bearing, pixelsToTileUnits);
var circleRadius = paint['circle-radius'] * pixelsToTileUnits;
if (!polygonIntersectsBufferedMultiPoint(translatedPolygon, geometry, circleRadius)) continue;
if (!multiPolygonIntersectsBufferedMultiPoint(translatedPolygon, geometry, circleRadius)) continue;
}
}

Expand Down Expand Up @@ -253,7 +258,12 @@ function translate(queryGeometry, translate, translateAnchor, bearing, pixelsToT

var translated = [];
for (var i = 0; i < queryGeometry.length; i++) {
translated.push(queryGeometry[i].sub(translate._mult(pixelsToTileUnits)));
var ring = queryGeometry[i];
var translatedRing = [];
for (var k = 0; k < ring.length; k++) {
translatedRing.push(ring[k].sub(translate._mult(pixelsToTileUnits)));
}
translated.push(translatedRing);
}
return translated;
}
Expand Down Expand Up @@ -282,52 +292,63 @@ function offsetLine(rings, offset) {
return newRings;
}

function polygonIntersectsBufferedMultiPoint(polygon, rings, radius) {
for (var i = 0; i < rings.length; i++) {
var ring = rings[i];
for (var k = 0; k < ring.length; k++) {
var point = ring[k];
if (polygonContainsPoint(polygon, point)) return true;
if (pointIntersectsBufferedLine(point, polygon, radius)) return true;
function multiPolygonIntersectsBufferedMultiPoint(multiPolygon, rings, radius) {
for (var j = 0; j < multiPolygon.length; j++) {
var polygon = multiPolygon[j];
for (var i = 0; i < rings.length; i++) {
var ring = rings[i];
for (var k = 0; k < ring.length; k++) {
var point = ring[k];
if (polygonContainsPoint(polygon, point)) return true;
if (pointIntersectsBufferedLine(point, polygon, radius)) return true;
}
}
}
return false;
}

function polygonIntersectsMultiPolygon(polygon, multiPolygon) {
function multiPolygonIntersectsMultiPolygon(multiPolygonA, multiPolygonB) {

if (polygon.length === 1) {
return multiPolygonContainsPoint(multiPolygon, polygon[0]);
if (multiPolygonA.length === 1 && multiPolygonA[0].length === 1) {
return multiPolygonContainsPoint(multiPolygonB, multiPolygonA[0][0]);
}

for (var m = 0; m < multiPolygon.length; m++) {
var ring = multiPolygon[m];
for (var m = 0; m < multiPolygonB.length; m++) {
var ring = multiPolygonB[m];
for (var n = 0; n < ring.length; n++) {
if (polygonContainsPoint(polygon, ring[n])) return true;
if (multiPolygonContainsPoint(multiPolygonA, ring[n])) return true;
}
}

for (var i = 0; i < polygon.length; i++) {
if (multiPolygonContainsPoint(multiPolygon, polygon[i])) return true;
}
for (var j = 0; j < multiPolygonA.length; j++) {
var polygon = multiPolygonA[j];
for (var i = 0; i < polygon.length; i++) {
if (multiPolygonContainsPoint(multiPolygonB, polygon[i])) return true;
}

for (var k = 0; k < multiPolygon.length; k++) {
if (lineIntersectsLine(polygon, multiPolygon[k])) return true;
for (var k = 0; k < multiPolygonB.length; k++) {
if (lineIntersectsLine(polygon, multiPolygonB[k])) return true;
}
}

return false;
}

function polygonIntersectsBufferedMultiLine(polygon, multiLine, radius) {
function multiPolygonIntersectsBufferedMultiLine(multiPolygon, multiLine, radius) {
for (var i = 0; i < multiLine.length; i++) {
var line = multiLine[i];

if (polygon.length >= 3) {
for (var k = 0; k < line.length; k++) {
if (polygonContainsPoint(polygon, line[k])) return true;
for (var j = 0; j < multiPolygon.length; j++) {
var polygon = multiPolygon[j];

if (polygon.length >= 3) {
for (var k = 0; k < line.length; k++) {
if (polygonContainsPoint(polygon, line[k])) return true;
}
}
}

if (lineIntersectsBufferedLine(polygon, line, radius)) return true;
if (lineIntersectsBufferedLine(polygon, line, radius)) return true;
}
}
return false;
}
Expand Down
15 changes: 0 additions & 15 deletions js/source/tile.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,6 @@ function Tile(coord, size, sourceMaxZoom) {

Tile.prototype = {

/**
* Given a coordinate position, zoom that coordinate to my zoom and
* scale and return a position in x, y, scale
* @param {Coordinate} coord
* @returns {Object} position
* @private
*/
positionAt: function(coord) {
var zoomedCoord = coord.zoomTo(Math.min(this.coord.z, this.sourceMaxZoom));
return {
x: (zoomedCoord.column - this.coord.x) * Bucket.EXTENT,
y: (zoomedCoord.row - this.coord.y) * Bucket.EXTENT
};
},

/**
* Given a data object with a 'buffers' property, load it into
* this tile's elementGroups and buffers properties and set loaded
Expand Down
47 changes: 37 additions & 10 deletions js/source/tile_pyramid.js
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ TilePyramid.prototype = {
* @private
*/
tilesIn: function(queryGeometry) {
var result = [];
var tileResults = {};
var ids = this.orderedIDs();

var minX = Infinity;
Expand All @@ -405,32 +405,59 @@ TilePyramid.prototype = {

for (var i = 0; i < ids.length; i++) {
var tile = this._tiles[ids[i]];
var coord = TileCoord.fromID(ids[i]);

var tileSpaceBounds = [
tile.positionAt(new Coordinate(minX, minY, z)),
tile.positionAt(new Coordinate(maxX, maxY, z))
coordinateToTilePoint(coord, tile.sourceMaxZoom, new Coordinate(minX, minY, z)),
coordinateToTilePoint(coord, tile.sourceMaxZoom, 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]));
tileSpaceQueryGeometry.push(coordinateToTilePoint(coord, tile.sourceMaxZoom, queryGeometry[j]));
}

result.push({
tile: tile,
queryGeometry: tileSpaceQueryGeometry,
scale: Math.pow(2, this.transform.zoom - tile.coord.z)
});
var tileResult = tileResults[tile.coord.id];
if (tileResult === undefined) {
tileResult = tileResults[tile.coord.id] = {
tile: tile,
queryGeometry: [],
scale: Math.pow(2, this.transform.zoom - tile.coord.z)
};
}

// Wrapped tiles share one tileResult object but can have multiple queryGeometry parts
tileResult.queryGeometry.push(tileSpaceQueryGeometry);
}
}

return result;
var results = [];
for (var t in tileResults) {
results.push(tileResults[t]);
}
return results;
}
};

/**
* Convert a coordinate to a point in a tile's coordinate space.
* @param {Coordinate} tileCoord
* @param {Coordinate} coord
* @returns {Object} position
* @private
*/
function coordinateToTilePoint(tileCoord, sourceMaxZoom, coord) {
var zoomedCoord = coord.zoomTo(Math.min(tileCoord.z, sourceMaxZoom));
return {
x: (zoomedCoord.column - (tileCoord.x + tileCoord.w * Math.pow(2, tileCoord.z))) * EXTENT,
y: (zoomedCoord.row - tileCoord.y) * EXTENT
};

}

function compareKeyZoom(a, b) {
return (a % 32) - (b % 32);
}
2 changes: 1 addition & 1 deletion js/ui/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ util.extend(Map.prototype, /** @lends Map.prototype */{
}

queryGeometry = queryGeometry.map(function(p) {
return this.transform.locationCoordinate(this.transform.pointLocation(p).wrap());
return this.transform.pointCoordinate(p);
}.bind(this));

return queryGeometry;
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#13bfab2d5937b241259586cbf545713b251305e5",
"mapbox-gl-test-suite": "mapbox/mapbox-gl-test-suite#5e94c9d0f90aaa6fd557625a35b3c4c00aba901d",
"nyc": "^6.1.1",
"sinon": "^1.15.4",
"st": "^1.0.0",
Expand Down
12 changes: 6 additions & 6 deletions test/js/source/tile_pyramid.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -504,12 +504,12 @@ test('TilePyramid#tilesIn', function (t) {
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.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.deepEqual(tiles[1].queryGeometry, [[{x: -4096, y: 2048}, {x: 4096, y: 6144}]]);

t.end();
});
Expand Down Expand Up @@ -546,12 +546,12 @@ test('TilePyramid#tilesIn', function (t) {
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.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.deepEqual(tiles[1].queryGeometry, [[{x: -4096, y: 2048}, {x: 4096, y: 6144}]]);

t.end();
});
Expand Down Expand Up @@ -589,12 +589,12 @@ test('TilePyramid#tilesIn', function (t) {
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.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.deepEqual(tiles[1].queryGeometry, [[{x: -4096, y: 2048}, {x: 4096, y: 6144}]]);

t.end();
});
Expand Down
4 changes: 2 additions & 2 deletions test/js/ui/map.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -508,10 +508,10 @@ test('Map', function(t) {
map.queryRenderedFeatures(map.project(new LngLat(0, 0)), opts);
});

t.test('wraps coords', function(t) {
t.test('does not wrap coords', function(t) {
map.style.queryRenderedFeatures = function (coords, o, classes, zoom, bearing) {
// avoid floating point issues
t.equal(parseFloat(coords[0].column.toFixed(4)), 0.5);
t.equal(parseFloat(coords[0].column.toFixed(4)), 1.5);
t.equal(coords[0].row, 0.5);
t.equal(coords[0].zoom, 0);

Expand Down

0 comments on commit 7531605

Please sign in to comment.