diff --git a/js/data/create_bucket.js b/js/data/create_bucket.js index 2a17bc50ff0..3217a76e9ad 100644 --- a/js/data/create_bucket.js +++ b/js/data/create_bucket.js @@ -9,7 +9,7 @@ var LayoutProperties = require('../style/layout_properties'); var featureFilter = require('feature-filter'); var StyleDeclarationSet = require('../style/style_declaration_set'); -function createBucket(layer, buffers, collision, z) { +function createBucket(layer, buffers, collision, z, overscaling) { var values = new StyleDeclarationSet('layout', layer.type, layer.layout, {}).values(), fakeZoomHistory = { lastIntegerZoom: Infinity, lastIntegerZoomTime: 0, lastZoom: 0 }, layout = {}; @@ -23,7 +23,7 @@ function createBucket(layer, buffers, collision, z) { layer.type === 'fill' ? FillBucket : layer.type === 'symbol' ? SymbolBucket : null; - var bucket = new BucketClass(buffers, new LayoutProperties[layer.type](layout), collision); + var bucket = new BucketClass(buffers, new LayoutProperties[layer.type](layout), collision, overscaling); bucket.id = layer.id; bucket.type = layer.type; diff --git a/js/data/line_bucket.js b/js/data/line_bucket.js index 593f4f84d8c..f0c8d9de4ec 100644 --- a/js/data/line_bucket.js +++ b/js/data/line_bucket.js @@ -4,10 +4,11 @@ var ElementGroups = require('./element_groups'); module.exports = LineBucket; -function LineBucket(buffers, layoutProperties) { +function LineBucket(buffers, layoutProperties, _, overscaling) { this.buffers = buffers; this.elementGroups = new ElementGroups(buffers.lineVertex, buffers.lineElement); this.layoutProperties = layoutProperties; + this.overscaling = overscaling; } LineBucket.prototype.addFeatures = function() { @@ -90,7 +91,7 @@ LineBucket.prototype.addLine = function(vertices, join, cap, miterLimit, roundLi currentVertex = vertices[i]; // Calculate how far along the line the currentVertex is - if (prevVertex) distance += currentVertex.dist(prevVertex); + if (prevVertex) distance += currentVertex.dist(prevVertex) * this.overscaling; // Calculate the normal towards the next vertex in this line. In case // there is no next vertex, pretend that the line is continuing straight, diff --git a/js/source/source.js b/js/source/source.js index e9313505948..e7ef0dbeb83 100644 --- a/js/source/source.js +++ b/js/source/source.js @@ -52,6 +52,10 @@ exports._renderTiles = function(layers, painter) { y = pos.y, w = pos.w; + // if z > maxzoom then the tile is actually a overscaled maxzoom tile, + // so calculate the matrix the maxzoom tile would use. + z = Math.min(z, this.maxzoom); + x += w * (1 << z); tile.calculateMatrices(z, x, y, painter.transform, painter); diff --git a/js/source/tile_coord.js b/js/source/tile_coord.js index a5322c7d1f9..ee728a08ed4 100644 --- a/js/source/tile_coord.js +++ b/js/source/tile_coord.js @@ -41,12 +41,12 @@ TileCoord.zoom = function(id) { }; // Given an id and a list of urls, choose a url template and return a tile URL -TileCoord.url = function(id, urls) { +TileCoord.url = function(id, urls, sourceMaxZoom) { var pos = TileCoord.fromID(id); return urls[(pos.x + pos.y) % urls.length] .replace('{prefix}', (pos.x % 16).toString(16) + (pos.y % 16).toString(16)) - .replace('{z}', pos.z) + .replace('{z}', Math.min(pos.z, sourceMaxZoom || pos.z)) .replace('{x}', pos.x) .replace('{y}', pos.y); }; @@ -54,9 +54,15 @@ TileCoord.url = function(id, urls) { /* * Given a packed integer id, return the id of its parent tile */ -TileCoord.parent = function(id) { +TileCoord.parent = function(id, sourceMaxZoom) { var pos = TileCoord.fromID(id); if (pos.z === 0) return null; + + // the id represents an overscaled tile, return the same coordinates with a lower z + if (pos.z > sourceMaxZoom) { + return TileCoord.toID(pos.z - 1, pos.x, pos.y, pos.w); + } + return TileCoord.toID(pos.z - 1, Math.floor(pos.x / 2), Math.floor(pos.y / 2), pos.w); }; @@ -74,8 +80,14 @@ TileCoord.parentWithZoom = function(id, zoom) { * Given a packed integer id, return an array of integer ids representing * its four children. */ -TileCoord.children = function(id) { +TileCoord.children = function(id, sourceMaxZoom) { var pos = TileCoord.fromID(id); + + if (pos.z >= sourceMaxZoom) { + // return a single tile id representing a an overscaled tile + return [TileCoord.toID(pos.z + 1, pos.x, pos.y, pos.w)]; + } + pos.z += 1; pos.x *= 2; pos.y *= 2; @@ -149,7 +161,7 @@ function scanTriangle(a, b, c, ymin, ymax, scanLine) { if (bc.dy) scanSpans(ca, bc, ymin, ymax, scanLine); } -TileCoord.cover = function(z, bounds) { +TileCoord.cover = function(z, bounds, actualZ) { var tiles = 1 << z; var t = {}; @@ -158,7 +170,7 @@ TileCoord.cover = function(z, bounds) { if (y >= 0 && y <= tiles) { for (x = x0; x < x1; x++) { wx = (x + tiles) % tiles; - t[TileCoord.toID(z, wx, y, Math.floor(x / tiles))] = {x: wx, y: y}; + t[TileCoord.toID(actualZ, wx, y, Math.floor(x / tiles))] = {x: wx, y: y}; } } } diff --git a/js/source/tile_pyramid.js b/js/source/tile_pyramid.js index 1a52fe371bc..aa73cf3f483 100644 --- a/js/source/tile_pyramid.js +++ b/js/source/tile_pyramid.js @@ -66,6 +66,7 @@ TilePyramid.prototype = { coveringTiles: function(transform) { var z = this.coveringZoomLevel(transform); + var actualZ = z; if (z < this.minzoom) return []; if (z > this.maxzoom) z = this.maxzoom; @@ -79,7 +80,7 @@ TilePyramid.prototype = { TileCoord.zoomTo(tr.pointCoordinate(tileCenter, {x: tr.width, y: 0}), z), TileCoord.zoomTo(tr.pointCoordinate(tileCenter, {x: tr.width, y: tr.height}), z), TileCoord.zoomTo(tr.pointCoordinate(tileCenter, {x: 0, y: tr.height}), z) - ]).sort(function(a, b) { + ], actualZ).sort(function(a, b) { return centerPoint.dist(TileCoord.fromID(a)) - centerPoint.dist(TileCoord.fromID(b)); }); @@ -90,7 +91,7 @@ TilePyramid.prototype = { findLoadedChildren: function(id, maxCoveringZoom, retain) { var complete = true; var z = TileCoord.fromID(id).z; - var ids = TileCoord.children(id); + var ids = TileCoord.children(id, this.maxzoom); for (var i = 0; i < ids.length; i++) { if (this._tiles[ids[i]] && this._tiles[ids[i]].loaded) { retain[ids[i]] = true; @@ -109,7 +110,7 @@ TilePyramid.prototype = { // adds the found tile to retain object and returns the tile if found findLoadedParent: function(id, minCoveringZoom, retain) { for (var z = TileCoord.fromID(id).z; z >= minCoveringZoom; z--) { - id = TileCoord.parent(id); + id = TileCoord.parent(id, this.maxzoom); var tile = this._tiles[id]; if (tile && tile.loaded) { retain[id] = true; diff --git a/js/source/vector_tile_source.js b/js/source/vector_tile_source.js index e6fa82b3121..7521de3b78b 100644 --- a/js/source/vector_tile_source.js +++ b/js/source/vector_tile_source.js @@ -45,15 +45,16 @@ VectorTileSource.prototype = util.inherit(Evented, { featuresAt: Source._vectorFeaturesAt, _loadTile: function(tile) { + var overscaling = tile.zoom > this.maxzoom ? Math.pow(2, tile.zoom - this.maxzoom) : 1; var params = { - url: TileCoord.url(tile.id, this.tiles), + url: TileCoord.url(tile.id, this.tiles, this.maxzoom), id: tile.uid, tileId: tile.id, zoom: tile.zoom, maxZoom: this.maxzoom, - tileSize: this.tileSize, + tileSize: this.tileSize * overscaling, source: this.id, - depth: tile.zoom >= this.maxzoom ? this.map.options.maxZoom - tile.zoom : 1 + overscaling: overscaling }; if (tile.workerID) { diff --git a/js/source/worker.js b/js/source/worker.js index 91533cde0b1..12b54a31daf 100644 --- a/js/source/worker.js +++ b/js/source/worker.js @@ -41,7 +41,7 @@ util.extend(Worker.prototype, { var tile = new WorkerTile( params.id, params.zoom, params.maxZoom, - params.tileSize, params.source, params.depth); + params.tileSize, params.source, params.overscaling); tile.data = new vt.VectorTile(new Protobuf(new Uint8Array(data))); tile.parse(tile.data, this.layers, this.actor, callback); diff --git a/js/source/worker_tile.js b/js/source/worker_tile.js index 53c51471573..e7404934761 100644 --- a/js/source/worker_tile.js +++ b/js/source/worker_tile.js @@ -16,13 +16,13 @@ function getType(feature) { module.exports = WorkerTile; -function WorkerTile(id, zoom, maxZoom, tileSize, source, depth) { +function WorkerTile(id, zoom, maxZoom, tileSize, source, overscaling) { this.id = id; this.zoom = zoom; this.maxZoom = maxZoom; this.tileSize = tileSize; this.source = source; - this.depth = depth; + this.overscaling = overscaling; } WorkerTile.prototype.parse = function(data, layers, actor, callback) { @@ -33,7 +33,7 @@ WorkerTile.prototype.parse = function(data, layers, actor, callback) { layer, bucket, buffers = new BufferSet(), - collision = new Collision(this.zoom, 4096, this.tileSize, this.depth), + collision = new Collision(this.zoom, 4096, this.tileSize), buckets = {}, bucketsInOrder = [], bucketsBySourceLayer = {}; @@ -60,7 +60,7 @@ WorkerTile.prototype.parse = function(data, layers, actor, callback) { if (visibility === 'none') continue; - bucket = createBucket(layer, buffers, collision, this.zoom); + bucket = createBucket(layer, buffers, collision, this.zoom, this.overscaling); bucket.layers = [layer.id]; buckets[bucket.id] = bucket; diff --git a/js/symbol/collision.js b/js/symbol/collision.js index c0744589e90..5b004cc1b01 100644 --- a/js/symbol/collision.js +++ b/js/symbol/collision.js @@ -6,7 +6,7 @@ var rbush = require('rbush'), module.exports = Collision; -function Collision(zoom, tileExtent, tileSize, placementDepth) { +function Collision(zoom, tileExtent, tileSize) { this.hTree = rbush(); // tree for horizontal labels this.cTree = rbush(); // tree for glyphs from curved labels @@ -23,8 +23,7 @@ function Collision(zoom, tileExtent, tileSize, placementDepth) { // We don't want to place labels all the way to 25.5. This lets too many // glyphs be placed, slowing down collision checking. Only place labels if // they will show up within the intended zoom range of the tile. - placementDepth = Math.min(3, placementDepth || 1, 25.5 - this.zoom); - this.maxPlacementScale = Math.exp(Math.LN2 * placementDepth); + this.maxPlacementScale = 2; var m = 4096; var edge = m * this.tilePixelRatio * 2; diff --git a/test/js/source/tile_pyramid.test.js b/test/js/source/tile_pyramid.test.js index 089cf836318..012e56b3d3c 100644 --- a/test/js/source/tile_pyramid.test.js +++ b/test/js/source/tile_pyramid.test.js @@ -32,7 +32,7 @@ test('TilePyramid#coveringTiles', function(t) { t.deepEqual(pyramid.coveringTiles(transform), ['16760810', '16760842', '16793578', '16793610']); transform.zoom = 11; - t.deepEqual(pyramid.coveringTiles(transform), ['16760810', '16760842', '16793578', '16793610']); + t.deepEqual(pyramid.coveringTiles(transform), ['33505259', '33505291', '33570795', '33570827']); t.end(); });