From 3afd8b4242241c778e970f31eedb25919e65e94c Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 19 Jun 2013 18:15:22 +0200 Subject: [PATCH 01/10] Do not use a fixed tile grid for vector rendering Instead, we create a new tile grid whenever renderFrame is called, no animation is active, and the resolution is not in the tile grid already. This gives better rendering results because we get vector tiles at native resolutions. --- .../canvas/canvasvectorlayerrenderer.js | 58 ++++++++++++++++--- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 1a6b19ef44..4fb70a59b2 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -1,5 +1,6 @@ goog.provide('ol.renderer.canvas.VectorLayer'); +goog.require('goog.array'); goog.require('goog.dom'); goog.require('goog.dom.TagName'); goog.require('goog.events'); @@ -22,6 +23,12 @@ goog.require('ol.renderer.canvas.VectorRenderer'); goog.require('ol.tilegrid.TileGrid'); +/** + * @define {number} The lowest supported resolution value. + */ +ol.renderer.canvas.MIN_RESOLUTION = 0.14929107086948487; + + /** * @constructor @@ -303,16 +310,49 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = resolution = view2DState.resolution, extent = frameState.extent, layer = this.getVectorLayer(), - tileGrid = this.tileGrid_; + tileGrid = this.tileGrid_, + resolutions, tileCoord, key, i; - if (goog.isNull(tileGrid)) { - // lazy tile grid creation to match the view projection - tileGrid = ol.tilegrid.createForProjection( - view2DState.projection, - 20, // should be no harm in going big here - ideally, it would be ∞ - [512, 512]); - this.tileGrid_ = tileGrid; + // lazy tile grid creation + if (!frameState.viewHints[ol.ViewHint.ANIMATING]) { + // Avoid rendering issues at very high zoom levels + var gridResolution = Math.max(resolution, + ol.renderer.canvas.MIN_RESOLUTION); + if (goog.isNull(tileGrid)) { + // start with the current resolution + resolutions = [gridResolution]; + } else if (!goog.array.contains(tileGrid.getResolutions(), + gridResolution)) { + // create a new tile grid, adding the current resolution, and update z + // tile coordinates in tile cache to match the new tile grid + var oldResolutions = tileGrid.getResolutions(); + resolutions = [gridResolution].concat(oldResolutions); + resolutions.sort(function(a, b) { return b - a; }); + var keys = this.tileCache_.getKeys(); + for (i = keys.length - 1; i >= 0; --i) { + key = keys[i]; + tileCoord = ol.TileCoord.createFromString(key); + tileCoord.z = goog.array.indexOf(resolutions, + oldResolutions[tileCoord.z]); + this.tileCache_.set(tileCoord.toString(), this.tileCache_.pop()); + } + } + if (goog.isDef(resolutions)) { + tileGrid = new ol.tilegrid.TileGrid({ + origin: [0, 0], + projection: view2DState.projection, + resolutions: resolutions, + tileSize: [512, 512] + }); + this.tileGrid_ = tileGrid; + } } + if (goog.isNull(tileGrid)) { + // We should only get here when the first call to renderFrame happens during + // an animation. Try again in the next renderFrame call. + return; + } + // set up transform for the layer canvas to be drawn to the map canvas var z = tileGrid.getZForResolution(resolution), @@ -399,7 +439,7 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = var tilesOnSketchCanvas = {}; // TODO make gutter configurable? var tileGutter = 15 * tileResolution; - var tile, tileCoord, key, tileState, x, y; + var tile, x, y; // render features by geometry type var filters = this.geometryFilters_, numFilters = filters.length, From 0aacf197bfa1979d9b408c8f398d92d43833ff68 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Thu, 20 Jun 2013 18:38:03 +0200 Subject: [PATCH 02/10] Separate method for updating the tile cache --- .../canvas/canvasvectorlayerrenderer.js | 57 ++++++++++--------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 4fb70a59b2..6d5745bf66 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -310,43 +310,27 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = resolution = view2DState.resolution, extent = frameState.extent, layer = this.getVectorLayer(), - tileGrid = this.tileGrid_, - resolutions, tileCoord, key, i; + tileGrid = this.tileGrid_; // lazy tile grid creation if (!frameState.viewHints[ol.ViewHint.ANIMATING]) { - // Avoid rendering issues at very high zoom levels - var gridResolution = Math.max(resolution, - ol.renderer.canvas.MIN_RESOLUTION); - if (goog.isNull(tileGrid)) { - // start with the current resolution - resolutions = [gridResolution]; - } else if (!goog.array.contains(tileGrid.getResolutions(), - gridResolution)) { - // create a new tile grid, adding the current resolution, and update z - // tile coordinates in tile cache to match the new tile grid - var oldResolutions = tileGrid.getResolutions(); - resolutions = [gridResolution].concat(oldResolutions); - resolutions.sort(function(a, b) { return b - a; }); - var keys = this.tileCache_.getKeys(); - for (i = keys.length - 1; i >= 0; --i) { - key = keys[i]; - tileCoord = ol.TileCoord.createFromString(key); - tileCoord.z = goog.array.indexOf(resolutions, - oldResolutions[tileCoord.z]); - this.tileCache_.set(tileCoord.toString(), this.tileCache_.pop()); - } - } - if (goog.isDef(resolutions)) { + // avoid rendering issues for very high zoom levels + var newResolution = Math.max(resolution, ol.renderer.canvas.MIN_RESOLUTION); + var oldResolutions = goog.isNull(this.tileGrid_) ? [] : + this.tileGrid_.getResolutions(); + if (!goog.array.contains(oldResolutions, newResolution)) { tileGrid = new ol.tilegrid.TileGrid({ origin: [0, 0], projection: view2DState.projection, - resolutions: resolutions, + resolutions: [newResolution].concat(oldResolutions) + .sort(function(a, b) { return b - a; }), tileSize: [512, 512] }); + this.updateTileCache_(oldResolutions, tileGrid.getResolutions()); this.tileGrid_ = tileGrid; } } + if (goog.isNull(tileGrid)) { // We should only get here when the first call to renderFrame happens during // an animation. Try again in the next renderFrame call. @@ -439,7 +423,7 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = var tilesOnSketchCanvas = {}; // TODO make gutter configurable? var tileGutter = 15 * tileResolution; - var tile, x, y; + var tile, tileCoord, key, x, y; // render features by geometry type var filters = this.geometryFilters_, numFilters = filters.length, @@ -544,6 +528,25 @@ ol.renderer.canvas.VectorLayer.prototype.pruneTileCache_ = function() { }; +/** + * @private + * @param {Array.} oldResolutions Resolutions of the old tile grid. + * @param {Array.} newResolutions Resolutions of the new tile grid. + */ +ol.renderer.canvas.VectorLayer.prototype.updateTileCache_ = + function(oldResolutions, newResolutions) { + var keys = this.tileCache_.getKeys(), + tileCoord, key, i; + for (i = keys.length - 1; i >= 0; --i) { + key = keys[i]; + tileCoord = ol.TileCoord.createFromString(key); + tileCoord.z = goog.array.indexOf(newResolutions, + oldResolutions[tileCoord.z]); + this.tileCache_.set(tileCoord.toString(), this.tileCache_.pop()); + } +}; + + /** * @type {number} */ From aa62764d900934aa9fa09b62daaad62e7bf3ad38 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Fri, 21 Jun 2013 10:19:13 +0200 Subject: [PATCH 03/10] Do not add resolutions while interacting --- src/ol/renderer/canvas/canvasvectorlayerrenderer.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 6d5745bf66..2c2683ccf8 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -313,7 +313,8 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = tileGrid = this.tileGrid_; // lazy tile grid creation - if (!frameState.viewHints[ol.ViewHint.ANIMATING]) { + if (!frameState.viewHints[ol.ViewHint.ANIMATING] && + !frameState.viewHints[ol.ViewHint.INTERACTING]) { // avoid rendering issues for very high zoom levels var newResolution = Math.max(resolution, ol.renderer.canvas.MIN_RESOLUTION); var oldResolutions = goog.isNull(this.tileGrid_) ? [] : From 89c23b556065f895e7bc5433b836c0073cb13504 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Fri, 21 Jun 2013 17:05:19 +0200 Subject: [PATCH 04/10] Use binaryInsert instead of sort --- src/ol/renderer/canvas/canvasvectorlayerrenderer.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 2c2683ccf8..da16a3f9cd 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -320,14 +320,16 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = var oldResolutions = goog.isNull(this.tileGrid_) ? [] : this.tileGrid_.getResolutions(); if (!goog.array.contains(oldResolutions, newResolution)) { + var newResolutions = oldResolutions.concat([]); + goog.array.binaryInsert(newResolutions, newResolution, + function(a, b) { return b - a; }); tileGrid = new ol.tilegrid.TileGrid({ origin: [0, 0], projection: view2DState.projection, - resolutions: [newResolution].concat(oldResolutions) - .sort(function(a, b) { return b - a; }), + resolutions: newResolutions, tileSize: [512, 512] }); - this.updateTileCache_(oldResolutions, tileGrid.getResolutions()); + this.updateTileCache_(oldResolutions, newResolutions); this.tileGrid_ = tileGrid; } } From f20e6002cca7011f7eff0574656db50d749a5e82 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Fri, 21 Jun 2013 18:53:15 +0200 Subject: [PATCH 05/10] Tile grid and cache for one resolution only --- .../canvas/canvasvectorlayerrenderer.js | 33 +++---------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index da16a3f9cd..2eb47e186a 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -316,20 +316,16 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = if (!frameState.viewHints[ol.ViewHint.ANIMATING] && !frameState.viewHints[ol.ViewHint.INTERACTING]) { // avoid rendering issues for very high zoom levels - var newResolution = Math.max(resolution, ol.renderer.canvas.MIN_RESOLUTION); - var oldResolutions = goog.isNull(this.tileGrid_) ? [] : - this.tileGrid_.getResolutions(); - if (!goog.array.contains(oldResolutions, newResolution)) { - var newResolutions = oldResolutions.concat([]); - goog.array.binaryInsert(newResolutions, newResolution, - function(a, b) { return b - a; }); + var gridResolution = Math.max(resolution, + ol.renderer.canvas.MIN_RESOLUTION); + if (gridResolution !== this.renderedResolution_) { tileGrid = new ol.tilegrid.TileGrid({ origin: [0, 0], projection: view2DState.projection, - resolutions: newResolutions, + resolutions: [gridResolution], tileSize: [512, 512] }); - this.updateTileCache_(oldResolutions, newResolutions); + this.tileCache_.clear(); this.tileGrid_ = tileGrid; } } @@ -531,25 +527,6 @@ ol.renderer.canvas.VectorLayer.prototype.pruneTileCache_ = function() { }; -/** - * @private - * @param {Array.} oldResolutions Resolutions of the old tile grid. - * @param {Array.} newResolutions Resolutions of the new tile grid. - */ -ol.renderer.canvas.VectorLayer.prototype.updateTileCache_ = - function(oldResolutions, newResolutions) { - var keys = this.tileCache_.getKeys(), - tileCoord, key, i; - for (i = keys.length - 1; i >= 0; --i) { - key = keys[i]; - tileCoord = ol.TileCoord.createFromString(key); - tileCoord.z = goog.array.indexOf(newResolutions, - oldResolutions[tileCoord.z]); - this.tileCache_.set(tileCoord.toString(), this.tileCache_.pop()); - } -}; - - /** * @type {number} */ From 61fcbd560e4a07a702d74a84cc40fa692fb3618f Mon Sep 17 00:00:00 2001 From: ahocevar Date: Fri, 21 Jun 2013 21:01:37 +0200 Subject: [PATCH 06/10] Do not render tiles during animation and interaction --- .../canvas/canvasvectorlayerrenderer.js | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 2eb47e186a..9e8e04a31b 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -147,6 +147,15 @@ ol.renderer.canvas.VectorLayer = function(mapRenderer, layer) { */ this.tileGrid_ = null; + /** + * Tile range before the current animation or interaction. This is updated + * whenever the view is idle. + * + * @private + * @type {ol.TileRange} + */ + this.tileRange_ = null; + /** * @private * @type {function()} @@ -310,11 +319,13 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = resolution = view2DState.resolution, extent = frameState.extent, layer = this.getVectorLayer(), - tileGrid = this.tileGrid_; + tileGrid = this.tileGrid_, + tileSize = [512, 512], + idle = !frameState.viewHints[ol.ViewHint.ANIMATING] && + !frameState.viewHints[ol.ViewHint.INTERACTING]; // lazy tile grid creation - if (!frameState.viewHints[ol.ViewHint.ANIMATING] && - !frameState.viewHints[ol.ViewHint.INTERACTING]) { + if (idle) { // avoid rendering issues for very high zoom levels var gridResolution = Math.max(resolution, ol.renderer.canvas.MIN_RESOLUTION); @@ -323,7 +334,7 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = origin: [0, 0], projection: view2DState.projection, resolutions: [gridResolution], - tileSize: [512, 512] + tileSize: tileSize }); this.tileCache_.clear(); this.tileGrid_ = tileGrid; @@ -338,14 +349,15 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = // set up transform for the layer canvas to be drawn to the map canvas - var z = tileGrid.getZForResolution(resolution), - tileResolution = tileGrid.getResolution(z), - tileRange = tileGrid.getTileRangeForExtentAndResolution( - extent, tileResolution), - tileRangeExtent = tileGrid.getTileRangeExtent(z, tileRange), - tileSize = tileGrid.getTileSize(z), - sketchOrigin = ol.extent.getTopLeft(tileRangeExtent), - transform = this.transform_; + var tileResolution = tileGrid.getResolution(0); + if (idle) { + this.tileRange_ = tileGrid.getTileRangeForExtentAndResolution( + extent, tileResolution); + } + var transform = this.transform_, + tileRange = this.tileRange_, + tileRangeExtent = tileGrid.getTileRangeExtent(0, tileRange), + sketchOrigin = ol.extent.getTopLeft(tileRangeExtent); goog.vec.Mat4.makeIdentity(transform); goog.vec.Mat4.translate(transform, @@ -432,11 +444,11 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = groups, group, j, numGroups, featuresObject, tileHasFeatures; for (x = tileRange.minX; x <= tileRange.maxX; ++x) { for (y = tileRange.minY; y <= tileRange.maxY; ++y) { - tileCoord = new ol.TileCoord(z, x, y); + tileCoord = new ol.TileCoord(0, x, y); key = tileCoord.toString(); if (this.tileCache_.containsKey(key)) { tilesToRender[key] = tileCoord; - } else if (!frameState.viewHints[ol.ViewHint.ANIMATING]) { + } else if (idle) { tileExtent = tileGrid.getTileCoordExtent(tileCoord); tileExtent[0] -= tileGutter; tileExtent[1] += tileGutter; From 1c5f466e61bb2352d42b15c01fd514b35adb6dfb Mon Sep 17 00:00:00 2001 From: ahocevar Date: Sun, 23 Jun 2013 15:28:59 +0200 Subject: [PATCH 07/10] Adding comment about MIN_RESOLUTION --- src/ol/renderer/canvas/canvasvectorlayerrenderer.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index 9e8e04a31b..a1acd80e6e 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -24,6 +24,11 @@ goog.require('ol.tilegrid.TileGrid'); /** + * Resolution at zoom level 21 in a web mercator default tiling scheme. This + * is a workaround for browser bugs that cause line segments to disappear when + * they get too long. TODO: Use line clipping as a better work around. See + * https://github.com/openlayers/ol3/issues/404. + * * @define {number} The lowest supported resolution value. */ ol.renderer.canvas.MIN_RESOLUTION = 0.14929107086948487; From 27c23f676b883b986735d20fa89ff022aaef48c5 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Sun, 23 Jun 2013 18:47:00 +0200 Subject: [PATCH 08/10] Fixing requires --- src/ol/renderer/canvas/canvasvectorlayerrenderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index a1acd80e6e..e2895a2f37 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -1,6 +1,5 @@ goog.provide('ol.renderer.canvas.VectorLayer'); -goog.require('goog.array'); goog.require('goog.dom'); goog.require('goog.dom.TagName'); goog.require('goog.events'); @@ -10,6 +9,7 @@ goog.require('goog.vec.Mat4'); goog.require('ol.Pixel'); goog.require('ol.TileCache'); goog.require('ol.TileCoord'); +goog.require('ol.TileRange'); goog.require('ol.ViewHint'); goog.require('ol.extent'); goog.require('ol.filter.Extent'); From af0764b1ca87e03f6d39647133f22ae77d26d63a Mon Sep 17 00:00:00 2001 From: ahocevar Date: Sun, 23 Jun 2013 18:47:49 +0200 Subject: [PATCH 09/10] Simpler tile coord access now that we only have one resolution --- src/ol/renderer/canvas/canvasvectorlayerrenderer.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index e2895a2f37..e83b25b995 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -240,8 +240,7 @@ ol.renderer.canvas.VectorLayer.prototype.getFeaturesForPixel = var layer = this.getLayer(); var location = map.getCoordinateFromPixel(pixel); - var tileCoord = this.tileGrid_.getTileCoordForCoordAndResolution( - location, this.getMap().getView().getView2D().getResolution()); + var tileCoord = this.tileGrid_.getTileCoordForCoordAndZ(location, 0); var key = tileCoord.toString(); if (this.tileCache_.containsKey(key)) { var cachedTile = this.tileCache_.get(key); From 5cc3e189eb206916aef0717d46fe19dac2ce04fc Mon Sep 17 00:00:00 2001 From: ahocevar Date: Sun, 23 Jun 2013 18:48:21 +0200 Subject: [PATCH 10/10] Need to take units into account --- src/ol/renderer/canvas/canvasvectorlayerrenderer.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js index e83b25b995..5909385894 100644 --- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js +++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js @@ -321,6 +321,7 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = var view2DState = frameState.view2DState, resolution = view2DState.resolution, + projection = view2DState.projection, extent = frameState.extent, layer = this.getVectorLayer(), tileGrid = this.tileGrid_, @@ -332,11 +333,12 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame = if (idle) { // avoid rendering issues for very high zoom levels var gridResolution = Math.max(resolution, - ol.renderer.canvas.MIN_RESOLUTION); + ol.renderer.canvas.MIN_RESOLUTION / + ol.METERS_PER_UNIT[projection.getUnits()]); if (gridResolution !== this.renderedResolution_) { tileGrid = new ol.tilegrid.TileGrid({ origin: [0, 0], - projection: view2DState.projection, + projection: projection, resolutions: [gridResolution], tileSize: tileSize });