diff --git a/examples/mapbox-vector-tiles-advanced.js b/examples/mapbox-vector-tiles-advanced.js
index 4bda0c39c9..b960ad8964 100644
--- a/examples/mapbox-vector-tiles-advanced.js
+++ b/examples/mapbox-vector-tiles-advanced.js
@@ -43,6 +43,7 @@ function tileUrlFunction(tileCoord) {
var map = new ol.Map({
layers: [
new ol.layer.VectorTile({
+ renderMode: 'vector',
preload: Infinity,
source: new ol.source.VectorTile({
attributions: '© Mapbox ' +
diff --git a/externs/olx.js b/externs/olx.js
index 730c14f24c..a1f80ec65d 100644
--- a/externs/olx.js
+++ b/externs/olx.js
@@ -3798,6 +3798,7 @@ olx.layer.VectorOptions.prototype.visible;
* maxResolution: (number|undefined),
* opacity: (number|undefined),
* renderBuffer: (number|undefined),
+ * renderMode: (ol.layer.VectorTileRenderType|string|undefined),
* renderOrder: (function(ol.Feature, ol.Feature):number|undefined),
* source: (ol.source.VectorTile|undefined),
* style: (ol.style.Style|Array.
|ol.style.StyleFunction|undefined),
@@ -3822,6 +3823,22 @@ olx.layer.VectorTileOptions;
olx.layer.VectorTileOptions.prototype.renderBuffer;
+/**
+ * Render mode for vector tiles:
+ * * `'image'`: Vector tiles are rendered as images. Great performance, but
+ * point symbols and texts are always rotated with the view and pixels are
+ * scaled during zoom animations.
+ * * `'hybrid'`: Polygon and line elements are rendered as images, so pixels
+ * are scaled during zoom animations. Point symbols and texts are accurately
+ * rendered as vectors and can stay upright on rotated views.
+ * * `'vector'`: Vector tiles are rendered as vectors. Most accurate rendering
+ * even during animations, but slower performance than the other options.
+ * The default is `'hybrid'`.
+ * @type {ol.layer.VectorTileRenderType|string|undefined}
+ * @api
+ */
+olx.layer.VectorTileOptions.prototype.renderMode;
+
/**
* Render order. Function to be used when sorting features before rendering. By
* default features are drawn in the order that they are created.
@@ -4361,7 +4378,6 @@ olx.source.TileImageOptions.prototype.wrapX;
* cacheSize: (number|undefined),
* format: (ol.format.Feature|undefined),
* logo: (string|olx.LogoOptions|undefined),
- * opaque: (boolean|undefined),
* projection: ol.proj.ProjectionLike,
* state: (ol.source.State|undefined),
* tileClass: (function(new: ol.VectorTile, ol.TileCoord,
@@ -4412,14 +4428,6 @@ olx.source.VectorTileOptions.prototype.format;
olx.source.VectorTileOptions.prototype.logo;
-/**
- * Whether the layer is opaque.
- * @type {boolean|undefined}
- * @api
- */
-olx.source.VectorTileOptions.prototype.opaque;
-
-
/**
* Projection.
* @type {ol.proj.ProjectionLike}
diff --git a/src/ol/layer/vectortilelayer.js b/src/ol/layer/vectortilelayer.js
index 0631c893b0..a1ecd639b7 100644
--- a/src/ol/layer/vectortilelayer.js
+++ b/src/ol/layer/vectortilelayer.js
@@ -1,5 +1,6 @@
goog.provide('ol.layer.VectorTile');
+goog.require('goog.asserts');
goog.require('ol.layer.Vector');
goog.require('ol.object');
@@ -13,6 +14,26 @@ ol.layer.VectorTileProperty = {
};
+/**
+ * @enum {string}
+ * Render mode for vector tiles:
+ * * `'image'`: Vector tiles are rendered as images. Great performance, but
+ * point symbols and texts are always rotated with the view and pixels are
+ * scaled during zoom animations.
+ * * `'hybrid'`: Polygon and line elements are rendered as images, so pixels
+ * are scaled during zoom animations. Point symbols and texts are accurately
+ * rendered as vectors and can stay upright on rotated views.
+ * * `'vector'`: Vector tiles are rendered as vectors. Most accurate rendering
+ * even during animations, but slower performance than the other options.
+ * @api
+ */
+ol.layer.VectorTileRenderType = {
+ IMAGE: 'image',
+ HYBRID: 'hybrid',
+ VECTOR: 'vector'
+};
+
+
/**
* @classdesc
* Layer for vector tile data that is rendered client-side.
@@ -38,6 +59,18 @@ ol.layer.VectorTile = function(opt_options) {
this.setUseInterimTilesOnError(options.useInterimTilesOnError ?
options.useInterimTilesOnError : true);
+ goog.asserts.assert(options.renderMode == undefined ||
+ options.renderMode == ol.layer.VectorTileRenderType.IMAGE ||
+ options.renderMode == ol.layer.VectorTileRenderType.HYBRID ||
+ options.renderMode == ol.layer.VectorTileRenderType.VECTOR,
+ 'renderMode needs to be \'image\', \'hybrid\' or \'vector\'');
+
+ /**
+ * @private
+ * @type {ol.layer.VectorTileRenderType|string}
+ */
+ this.renderMode_ = options.renderMode || ol.layer.VectorTileRenderType.HYBRID;
+
};
goog.inherits(ol.layer.VectorTile, ol.layer.Vector);
@@ -53,6 +86,14 @@ ol.layer.VectorTile.prototype.getPreload = function() {
};
+/**
+ * @return {ol.layer.VectorTileRenderType|string} The render mode.
+ */
+ol.layer.VectorTile.prototype.getRenderMode = function() {
+ return this.renderMode_;
+};
+
+
/**
* Whether we use interim tiles on error.
* @return {boolean} Use interim tiles on error.
diff --git a/src/ol/render/canvas/canvasreplay.js b/src/ol/render/canvas/canvasreplay.js
index b907b04eb5..976839a403 100644
--- a/src/ol/render/canvas/canvasreplay.js
+++ b/src/ol/render/canvas/canvasreplay.js
@@ -1995,41 +1995,41 @@ ol.render.canvas.ReplayGroup.prototype.isEmpty = function() {
* @param {number} viewRotation View rotation.
* @param {Object.} skippedFeaturesHash Ids of features
* to skip.
- * @param {boolean=} opt_clip Clip at `maxExtent`. Default is true.
+ * @param {Array.=} opt_replayTypes Ordered replay types
+ * to replay. Default is {@link ol.render.REPLAY_ORDER}
*/
ol.render.canvas.ReplayGroup.prototype.replay = function(context, pixelRatio,
- transform, viewRotation, skippedFeaturesHash, opt_clip) {
+ transform, viewRotation, skippedFeaturesHash, opt_replayTypes) {
/** @type {Array.} */
var zs = Object.keys(this.replaysByZIndex_).map(Number);
zs.sort(ol.array.numberSafeCompareFunction);
- if (opt_clip !== false) {
- // setup clipping so that the parts of over-simplified geometries are not
- // visible outside the current extent when panning
- var maxExtent = this.maxExtent_;
- var minX = maxExtent[0];
- var minY = maxExtent[1];
- var maxX = maxExtent[2];
- var maxY = maxExtent[3];
- var flatClipCoords = [minX, minY, minX, maxY, maxX, maxY, maxX, minY];
- ol.geom.flat.transform.transform2D(
- flatClipCoords, 0, 8, 2, transform, flatClipCoords);
- context.save();
- context.beginPath();
- context.moveTo(flatClipCoords[0], flatClipCoords[1]);
- context.lineTo(flatClipCoords[2], flatClipCoords[3]);
- context.lineTo(flatClipCoords[4], flatClipCoords[5]);
- context.lineTo(flatClipCoords[6], flatClipCoords[7]);
- context.closePath();
- context.clip();
- }
+ // setup clipping so that the parts of over-simplified geometries are not
+ // visible outside the current extent when panning
+ var maxExtent = this.maxExtent_;
+ var minX = maxExtent[0];
+ var minY = maxExtent[1];
+ var maxX = maxExtent[2];
+ var maxY = maxExtent[3];
+ var flatClipCoords = [minX, minY, minX, maxY, maxX, maxY, maxX, minY];
+ ol.geom.flat.transform.transform2D(
+ flatClipCoords, 0, 8, 2, transform, flatClipCoords);
+ context.save();
+ context.beginPath();
+ context.moveTo(flatClipCoords[0], flatClipCoords[1]);
+ context.lineTo(flatClipCoords[2], flatClipCoords[3]);
+ context.lineTo(flatClipCoords[4], flatClipCoords[5]);
+ context.lineTo(flatClipCoords[6], flatClipCoords[7]);
+ context.closePath();
+ context.clip();
+ var replayTypes = opt_replayTypes ? opt_replayTypes : ol.render.REPLAY_ORDER;
var i, ii, j, jj, replays, replay;
for (i = 0, ii = zs.length; i < ii; ++i) {
replays = this.replaysByZIndex_[zs[i].toString()];
- for (j = 0, jj = ol.render.REPLAY_ORDER.length; j < jj; ++j) {
- replay = replays[ol.render.REPLAY_ORDER[j]];
+ for (j = 0, jj = replayTypes.length; j < jj; ++j) {
+ replay = replays[replayTypes[j]];
if (replay !== undefined) {
replay.replay(context, pixelRatio, transform, viewRotation,
skippedFeaturesHash);
diff --git a/src/ol/renderer/canvas/canvastilelayerrenderer.js b/src/ol/renderer/canvas/canvastilelayerrenderer.js
index 34ef387313..583f7a404f 100644
--- a/src/ol/renderer/canvas/canvastilelayerrenderer.js
+++ b/src/ol/renderer/canvas/canvastilelayerrenderer.js
@@ -9,9 +9,9 @@ goog.require('ol.TileState');
goog.require('ol.array');
goog.require('ol.dom');
goog.require('ol.extent');
-goog.require('ol.layer.Tile');
goog.require('ol.render.EventType');
goog.require('ol.renderer.canvas.Layer');
+goog.require('ol.size');
goog.require('ol.source.Tile');
goog.require('ol.vec.Mat4');
@@ -19,29 +19,29 @@ goog.require('ol.vec.Mat4');
/**
* @constructor
* @extends {ol.renderer.canvas.Layer}
- * @param {ol.layer.Tile} tileLayer Tile layer.
+ * @param {ol.layer.Tile|ol.layer.VectorTile} tileLayer Tile layer.
*/
ol.renderer.canvas.TileLayer = function(tileLayer) {
goog.base(this, tileLayer);
/**
- * @private
+ * @protected
* @type {CanvasRenderingContext2D}
*/
- this.context_ = ol.dom.createCanvasContext2D();
+ this.context = ol.dom.createCanvasContext2D();
/**
- * @private
+ * @protected
* @type {Array.}
*/
- this.renderedTiles_ = null;
+ this.renderedTiles = null;
/**
- * @private
+ * @protected
* @type {ol.Extent}
*/
- this.tmpExtent_ = ol.extent.createEmpty();
+ this.tmpExtent = ol.extent.createEmpty();
/**
* @private
@@ -55,6 +55,12 @@ ol.renderer.canvas.TileLayer = function(tileLayer) {
*/
this.imageTransform_ = goog.vec.Mat4.createNumber();
+ /**
+ * @protected
+ * @type {number}
+ */
+ this.zDirection = 0;
+
};
goog.inherits(ol.renderer.canvas.TileLayer, ol.renderer.canvas.Layer);
@@ -64,127 +70,9 @@ goog.inherits(ol.renderer.canvas.TileLayer, ol.renderer.canvas.Layer);
*/
ol.renderer.canvas.TileLayer.prototype.composeFrame = function(
frameState, layerState, context) {
- var pixelRatio = frameState.pixelRatio;
- var viewState = frameState.viewState;
- var center = viewState.center;
- var projection = viewState.projection;
- var resolution = viewState.resolution;
- var rotation = viewState.rotation;
- var size = frameState.size;
- var offsetX = Math.round(pixelRatio * size[0] / 2);
- var offsetY = Math.round(pixelRatio * size[1] / 2);
- var pixelScale = pixelRatio / resolution;
- var layer = this.getLayer();
- var source = layer.getSource();
- goog.asserts.assertInstanceof(source, ol.source.Tile,
- 'source is an ol.source.Tile');
- var tileGutter = source.getGutter(projection);
-
var transform = this.getTransform(frameState, 0);
-
this.dispatchPreComposeEvent(context, frameState, transform);
-
- var renderContext = context;
- var hasRenderListeners = layer.hasListener(ol.render.EventType.RENDER);
- var drawOffsetX, drawOffsetY, drawScale, drawSize;
- if (rotation || hasRenderListeners) {
- renderContext = this.context_;
- var renderCanvas = renderContext.canvas;
- var tilePixelRatio = source.getTilePixelRatio(pixelRatio);
- drawScale = tilePixelRatio / pixelRatio;
- var width = context.canvas.width * drawScale;
- var height = context.canvas.height * drawScale;
- // Make sure the canvas is big enough for all possible rotation angles
- drawSize = Math.round(Math.sqrt(width * width + height * height));
- if (renderCanvas.width != drawSize) {
- renderCanvas.width = renderCanvas.height = drawSize;
- } else {
- renderContext.clearRect(0, 0, drawSize, drawSize);
- }
- drawOffsetX = (drawSize - width) / 2 / drawScale;
- drawOffsetY = (drawSize - height) / 2 / drawScale;
- pixelScale *= drawScale;
- offsetX = Math.round(drawScale * (offsetX + drawOffsetX))
- offsetY = Math.round(drawScale * (offsetY + drawOffsetY));
- }
-
- // for performance reasons, context.save / context.restore is not used
- // to save and restore the transformation matrix and the opacity.
- // see http://jsperf.com/context-save-restore-versus-variable
- var alpha = renderContext.globalAlpha;
- renderContext.globalAlpha = layerState.opacity;
-
- var tileGrid = source.getTileGridForProjection(projection);
- var tilesToDraw = this.renderedTiles_;
-
- var pixelExtents;
- var opaque = source.getOpaque(projection) && layerState.opacity == 1;
- if (!opaque) {
- tilesToDraw.reverse();
- pixelExtents = [];
- }
- for (var i = 0, ii = tilesToDraw.length; i < ii; ++i) {
- var tile = tilesToDraw[i];
- var tileCoord = tile.getTileCoord();
- var tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent_);
- var currentZ = tileCoord[0];
- // Calculate all insert points by tile widths from a common origin to avoid
- // gaps caused by rounding
- var origin = ol.extent.getBottomLeft(tileGrid.getTileCoordExtent(
- tileGrid.getTileCoordForCoordAndZ(center, currentZ, this.tmpTileCoord_)));
- var w = Math.round(ol.extent.getWidth(tileExtent) * pixelScale);
- var h = Math.round(ol.extent.getHeight(tileExtent) * pixelScale);
- var left = Math.round((tileExtent[0] - origin[0]) * pixelScale / w) * w +
- offsetX + Math.round((origin[0] - center[0]) * pixelScale);
- var top = Math.round((origin[1] - tileExtent[3]) * pixelScale / h) * h +
- offsetY + Math.round((center[1] - origin[1]) * pixelScale);
- if (!opaque) {
- var pixelExtent = [left, top, left + w, top + h];
- // Create a clip mask for regions in this low resolution tile that are
- // already filled by a higher resolution tile
- renderContext.save();
- for (var j = 0, jj = pixelExtents.length; j < jj; ++j) {
- var clipExtent = pixelExtents[j];
- if (ol.extent.intersects(pixelExtent, clipExtent)) {
- renderContext.beginPath();
- // counter-clockwise (outer ring) for current tile
- renderContext.moveTo(pixelExtent[0], pixelExtent[1]);
- renderContext.lineTo(pixelExtent[0], pixelExtent[3]);
- renderContext.lineTo(pixelExtent[2], pixelExtent[3]);
- renderContext.lineTo(pixelExtent[2], pixelExtent[1]);
- // clockwise (inner ring) for higher resolution tile
- renderContext.moveTo(clipExtent[0], clipExtent[1]);
- renderContext.lineTo(clipExtent[2], clipExtent[1]);
- renderContext.lineTo(clipExtent[2], clipExtent[3]);
- renderContext.lineTo(clipExtent[0], clipExtent[3]);
- renderContext.closePath();
- renderContext.clip();
- }
- }
- pixelExtents.push(pixelExtent);
- }
- var tilePixelSize = source.getTilePixelSize(currentZ, pixelRatio, projection);
- renderContext.drawImage(tile.getImage(), tileGutter, tileGutter,
- tilePixelSize[0], tilePixelSize[1], left, top, w, h);
- if (!opaque) {
- renderContext.restore();
- }
- }
-
- if (hasRenderListeners) {
- var dX = drawOffsetX - offsetX / drawScale + offsetX;
- var dY = drawOffsetY - offsetY / drawScale + offsetY;
- var imageTransform = ol.vec.Mat4.makeTransform2D(this.imageTransform_,
- drawSize / 2 - dX, drawSize / 2 - dY, pixelScale, -pixelScale,
- -rotation, -center[0] + dX / pixelScale, -center[1] - dY / pixelScale);
- this.dispatchRenderEvent(renderContext, frameState, imageTransform);
- }
- if (rotation || hasRenderListeners) {
- context.drawImage(renderContext.canvas, -Math.round(drawOffsetX),
- -Math.round(drawOffsetY), drawSize / drawScale, drawSize / drawScale);
- }
- renderContext.globalAlpha = alpha;
-
+ this.renderTileImages(context, frameState, layerState);
this.dispatchPostComposeEvent(context, frameState, transform);
};
@@ -200,11 +88,11 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame = function(
var projection = viewState.projection;
var tileLayer = this.getLayer();
- goog.asserts.assertInstanceof(tileLayer, ol.layer.Tile,
- 'layer is an instance of ol.layer.Tile');
var tileSource = tileLayer.getSource();
+ goog.asserts.assertInstanceof(tileSource, ol.source.Tile,
+ 'source is an ol.source.Tile');
var tileGrid = tileSource.getTileGridForProjection(projection);
- var z = tileGrid.getZForResolution(viewState.resolution);
+ var z = tileGrid.getZForResolution(viewState.resolution, this.zDirection);
var tileResolution = tileGrid.getResolution(z);
var center = viewState.center;
var extent;
@@ -291,7 +179,7 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame = function(
}
}
}
- this.renderedTiles_ = renderables;
+ this.renderedTiles = renderables;
this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange);
this.manageTilePyramid(frameState, tileSource, tileGrid, pixelRatio,
@@ -308,13 +196,13 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame = function(
*/
ol.renderer.canvas.TileLayer.prototype.forEachLayerAtPixel = function(
pixel, frameState, callback, thisArg) {
- var canvas = this.context_.canvas;
+ var canvas = this.context.canvas;
var size = frameState.size;
canvas.width = size[0];
canvas.height = size[1];
- this.composeFrame(frameState, this.getLayer().getLayerState(), this.context_);
+ this.composeFrame(frameState, this.getLayer().getLayerState(), this.context);
- var imageData = this.context_.getImageData(
+ var imageData = this.context.getImageData(
pixel[0], pixel[1], 1, 1).data;
if (imageData[3] > 0) {
@@ -323,3 +211,140 @@ ol.renderer.canvas.TileLayer.prototype.forEachLayerAtPixel = function(
return undefined;
}
};
+
+
+/**
+ * @param {CanvasRenderingContext2D} context Context.
+ * @param {olx.FrameState} frameState Frame state.
+ * @param {ol.layer.LayerState} layerState Layer state.
+ * @protected
+ */
+ol.renderer.canvas.TileLayer.prototype.renderTileImages = function(context, frameState, layerState) {
+ var pixelRatio = frameState.pixelRatio;
+ var viewState = frameState.viewState;
+ var center = viewState.center;
+ var projection = viewState.projection;
+ var resolution = viewState.resolution;
+ var rotation = viewState.rotation;
+ var size = frameState.size;
+ var offsetX = Math.round(pixelRatio * size[0] / 2);
+ var offsetY = Math.round(pixelRatio * size[1] / 2);
+ var pixelScale = pixelRatio / resolution;
+ var layer = this.getLayer();
+ var source = layer.getSource();
+ goog.asserts.assertInstanceof(source, ol.source.Tile,
+ 'source is an ol.source.Tile');
+ var tileGutter = source.getGutter(projection);
+ var tileGrid = source.getTileGridForProjection(projection);
+
+ var hasRenderListeners = layer.hasListener(ol.render.EventType.RENDER);
+ var renderContext = context;
+ var drawOffsetX, drawOffsetY, drawScale, drawSize;
+ if (rotation || hasRenderListeners) {
+ renderContext = this.context;
+ var renderCanvas = renderContext.canvas;
+ var drawZ = tileGrid.getZForResolution(resolution);
+ var drawTileSize = source.getTilePixelSize(drawZ, pixelRatio, projection);
+ var tileSize = ol.size.toSize(tileGrid.getTileSize(drawZ));
+ drawScale = drawTileSize[0] / tileSize[0];
+ var width = context.canvas.width * drawScale;
+ var height = context.canvas.height * drawScale;
+ // Make sure the canvas is big enough for all possible rotation angles
+ drawSize = Math.round(Math.sqrt(width * width + height * height));
+ if (renderCanvas.width != drawSize) {
+ renderCanvas.width = renderCanvas.height = drawSize;
+ } else {
+ renderContext.clearRect(0, 0, drawSize, drawSize);
+ }
+ drawOffsetX = (drawSize - width) / 2 / drawScale;
+ drawOffsetY = (drawSize - height) / 2 / drawScale;
+ pixelScale *= drawScale;
+ offsetX = Math.round(drawScale * (offsetX + drawOffsetX))
+ offsetY = Math.round(drawScale * (offsetY + drawOffsetY));
+ }
+ // for performance reasons, context.save / context.restore is not used
+ // to save and restore the transformation matrix and the opacity.
+ // see http://jsperf.com/context-save-restore-versus-variable
+ var alpha = renderContext.globalAlpha;
+ renderContext.globalAlpha = layerState.opacity;
+
+ var tilesToDraw = this.renderedTiles;
+
+ var pixelExtents;
+ var opaque = source.getOpaque(projection) && layerState.opacity == 1;
+ if (!opaque) {
+ tilesToDraw.reverse();
+ pixelExtents = [];
+ }
+ for (var i = 0, ii = tilesToDraw.length; i < ii; ++i) {
+ var tile = tilesToDraw[i];
+ var tileCoord = tile.getTileCoord();
+ var tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent);
+ var currentZ = tileCoord[0];
+ // Calculate all insert points by tile widths from a common origin to avoid
+ // gaps caused by rounding
+ var origin = ol.extent.getBottomLeft(tileGrid.getTileCoordExtent(
+ tileGrid.getTileCoordForCoordAndZ(center, currentZ, this.tmpTileCoord_)));
+ var w = Math.round(ol.extent.getWidth(tileExtent) * pixelScale);
+ var h = Math.round(ol.extent.getHeight(tileExtent) * pixelScale);
+ var left = Math.round((tileExtent[0] - origin[0]) * pixelScale / w) * w +
+ offsetX + Math.round((origin[0] - center[0]) * pixelScale);
+ var top = Math.round((origin[1] - tileExtent[3]) * pixelScale / h) * h +
+ offsetY + Math.round((center[1] - origin[1]) * pixelScale);
+ if (!opaque) {
+ var pixelExtent = [left, top, left + w, top + h];
+ // Create a clip mask for regions in this low resolution tile that are
+ // already filled by a higher resolution tile
+ renderContext.save();
+ for (var j = 0, jj = pixelExtents.length; j < jj; ++j) {
+ var clipExtent = pixelExtents[j];
+ if (ol.extent.intersects(pixelExtent, clipExtent)) {
+ renderContext.beginPath();
+ // counter-clockwise (outer ring) for current tile
+ renderContext.moveTo(pixelExtent[0], pixelExtent[1]);
+ renderContext.lineTo(pixelExtent[0], pixelExtent[3]);
+ renderContext.lineTo(pixelExtent[2], pixelExtent[3]);
+ renderContext.lineTo(pixelExtent[2], pixelExtent[1]);
+ // clockwise (inner ring) for higher resolution tile
+ renderContext.moveTo(clipExtent[0], clipExtent[1]);
+ renderContext.lineTo(clipExtent[2], clipExtent[1]);
+ renderContext.lineTo(clipExtent[2], clipExtent[3]);
+ renderContext.lineTo(clipExtent[0], clipExtent[3]);
+ renderContext.closePath();
+ renderContext.clip();
+ }
+ }
+ pixelExtents.push(pixelExtent);
+ }
+ var tilePixelSize = source.getTilePixelSize(currentZ, pixelRatio, projection);
+ var image = tile.getImage();
+ if (image) {
+ renderContext.drawImage(image, tileGutter, tileGutter,
+ tilePixelSize[0], tilePixelSize[1], left, top, w, h);
+ }
+ if (!opaque) {
+ renderContext.restore();
+ }
+ }
+
+ if (hasRenderListeners) {
+ var dX = drawOffsetX - offsetX / drawScale + offsetX;
+ var dY = drawOffsetY - offsetY / drawScale + offsetY;
+ var imageTransform = ol.vec.Mat4.makeTransform2D(this.imageTransform_,
+ drawSize / 2 - dX, drawSize / 2 - dY, pixelScale, -pixelScale,
+ -rotation, -center[0] + dX / pixelScale, -center[1] - dY / pixelScale);
+ this.dispatchRenderEvent(renderContext, frameState, imageTransform);
+ }
+ if (rotation || hasRenderListeners) {
+ context.drawImage(renderContext.canvas, -Math.round(drawOffsetX),
+ -Math.round(drawOffsetY), drawSize / drawScale, drawSize / drawScale);
+ }
+ renderContext.globalAlpha = alpha;
+};
+
+
+/**
+ * @function
+ * @return {ol.layer.Tile|ol.layer.VectorTile}
+ */
+ol.renderer.canvas.TileLayer.prototype.getLayer;
diff --git a/src/ol/renderer/canvas/canvasvectortilelayerrenderer.js b/src/ol/renderer/canvas/canvasvectortilelayerrenderer.js
index 51a39fa76c..974dea56ce 100644
--- a/src/ol/renderer/canvas/canvasvectortilelayerrenderer.js
+++ b/src/ol/renderer/canvas/canvasvectortilelayerrenderer.js
@@ -4,91 +4,107 @@ goog.require('goog.asserts');
goog.require('ol.events');
goog.require('goog.vec.Mat4');
goog.require('ol.Feature');
-goog.require('ol.TileRange');
-goog.require('ol.TileState');
goog.require('ol.VectorTile');
-goog.require('ol.ViewHint');
goog.require('ol.array');
-goog.require('ol.dom');
goog.require('ol.extent');
-goog.require('ol.geom.flat.transform');
goog.require('ol.layer.VectorTile');
goog.require('ol.proj');
goog.require('ol.proj.Units');
goog.require('ol.render.EventType');
+goog.require('ol.render.canvas');
goog.require('ol.render.canvas.ReplayGroup');
-goog.require('ol.renderer.canvas.Layer');
+goog.require('ol.renderer.canvas.TileLayer');
goog.require('ol.renderer.vector');
goog.require('ol.size');
goog.require('ol.source.VectorTile');
goog.require('ol.vec.Mat4');
+/**
+ * @const
+ * @type {!Object.>}
+ */
+ol.renderer.canvas.IMAGE_REPLAYS = {
+ 'image': ol.render.REPLAY_ORDER,
+ 'hybrid': [ol.render.ReplayType.POLYGON, ol.render.ReplayType.LINE_STRING]
+};
+
+
+/**
+ * @const
+ * @type {!Object.>}
+ */
+ol.renderer.canvas.VECTOR_REPLAYS = {
+ 'hybrid': [ol.render.ReplayType.IMAGE, ol.render.ReplayType.TEXT],
+ 'vector': ol.render.REPLAY_ORDER
+};
+
+
/**
* @constructor
- * @extends {ol.renderer.canvas.Layer}
+ * @extends {ol.renderer.canvas.TileLayer}
* @param {ol.layer.VectorTile} layer VectorTile layer.
*/
ol.renderer.canvas.VectorTileLayer = function(layer) {
goog.base(this, layer);
- /**
- * @private
- * @type {CanvasRenderingContext2D}
- */
- this.context_ = ol.dom.createCanvasContext2D();
-
/**
* @private
* @type {boolean}
*/
this.dirty_ = false;
- /**
- * @private
- * @type {Array.}
- */
- this.renderedTiles_ = [];
-
- /**
- * @private
- * @type {ol.Extent}
- */
- this.tmpExtent_ = ol.extent.createEmpty();
-
- /**
- * @private
- * @type {ol.Size}
- */
- this.tmpSize_ = [NaN, NaN];
-
/**
* @private
* @type {!goog.vec.Mat4.Number}
*/
this.tmpTransform_ = goog.vec.Mat4.createNumber();
+ // Use lower resolution for pure vector rendering. Closest resolution otherwise.
+ this.zDirection =
+ layer.getRenderMode() == ol.layer.VectorTileRenderType.VECTOR ? 1 : 0;
+
};
-goog.inherits(ol.renderer.canvas.VectorTileLayer, ol.renderer.canvas.Layer);
+goog.inherits(ol.renderer.canvas.VectorTileLayer, ol.renderer.canvas.TileLayer);
/**
* @inheritDoc
*/
-ol.renderer.canvas.VectorTileLayer.prototype.composeFrame = function(frameState, layerState, context) {
+ol.renderer.canvas.VectorTileLayer.prototype.composeFrame = function(
+ frameState, layerState, context) {
+ var transform = this.getTransform(frameState, 0);
+ this.dispatchPreComposeEvent(context, frameState, transform);
+ this.renderTileImages(context, frameState, layerState);
+ this.renderTileReplays_(context, frameState, layerState);
+ this.dispatchPostComposeEvent(context, frameState, transform);
+};
+
+/**
+ * @param {CanvasRenderingContext2D} context Context.
+ * @param {olx.FrameState} frameState Frame state.
+ * @param {ol.layer.LayerState} layerState Layer state.
+ * @private
+ */
+ol.renderer.canvas.VectorTileLayer.prototype.renderTileReplays_ = function(
+ context, frameState, layerState) {
+
+ var layer = this.getLayer();
+ var replays = ol.renderer.canvas.VECTOR_REPLAYS[layer.getRenderMode()];
+ if (!replays) {
+ return;
+ }
var pixelRatio = frameState.pixelRatio;
var skippedFeatureUids = layerState.managed ?
frameState.skippedFeatureUids : {};
var viewState = frameState.viewState;
var center = viewState.center;
- var projection = viewState.projection;
var resolution = viewState.resolution;
var rotation = viewState.rotation;
var size = frameState.size;
var pixelScale = pixelRatio / resolution;
- var layer = this.getLayer();
var source = layer.getSource();
goog.asserts.assertInstanceof(source, ol.source.VectorTile,
'Source is an ol.source.VectorTile');
@@ -96,14 +112,12 @@ ol.renderer.canvas.VectorTileLayer.prototype.composeFrame = function(frameState,
var transform = this.getTransform(frameState, 0);
- this.dispatchPreComposeEvent(context, frameState, transform);
-
var replayContext;
if (layer.hasListener(ol.render.EventType.RENDER)) {
// resize and clear
- this.context_.canvas.width = context.canvas.width;
- this.context_.canvas.height = context.canvas.height;
- replayContext = this.context_;
+ this.context.canvas.width = context.canvas.width;
+ this.context.canvas.height = context.canvas.height;
+ replayContext = this.context;
} else {
replayContext = context;
}
@@ -113,80 +127,39 @@ ol.renderer.canvas.VectorTileLayer.prototype.composeFrame = function(frameState,
var alpha = replayContext.globalAlpha;
replayContext.globalAlpha = layerState.opacity;
- var tilesToDraw = this.renderedTiles_;
+ var tilesToDraw = this.renderedTiles;
var tileGrid = source.getTileGrid();
- var currentZ, height, i, ii, insertPoint, insertTransform, offsetX, offsetY;
- var origin, pixelSpace, replayState, resolutionRatio, tile, tileCenter;
- var tileContext, tileExtent, tilePixelResolution, tilePixelSize;
- var tileResolution, tileSize, tileTransform, width;
+ var currentZ, i, ii, offsetX, offsetY, origin, pixelSpace, replayState;
+ var tile, tileExtent, tilePixelResolution, tileResolution, tileTransform;
for (i = 0, ii = tilesToDraw.length; i < ii; ++i) {
tile = tilesToDraw[i];
replayState = tile.getReplayState();
tileExtent = tileGrid.getTileCoordExtent(
- tile.getTileCoord(), this.tmpExtent_);
+ tile.getTileCoord(), this.tmpExtent);
currentZ = tile.getTileCoord()[0];
- tileSize = ol.size.toSize(tileGrid.getTileSize(currentZ), this.tmpSize_);
pixelSpace = tile.getProjection().getUnits() == ol.proj.Units.TILE_PIXELS;
tileResolution = tileGrid.getResolution(currentZ);
tilePixelResolution = tileResolution / tilePixelRatio;
- resolutionRatio = tileResolution / resolution;
offsetX = Math.round(pixelRatio * size[0] / 2);
offsetY = Math.round(pixelRatio * size[1] / 2);
- width = tileSize[0] * pixelRatio * resolutionRatio;
- height = tileSize[1] * pixelRatio * resolutionRatio;
- var unscaledPixelTileSize = tileSize[0] * pixelRatio;
- if (width < unscaledPixelTileSize / 4 || width > unscaledPixelTileSize * 4) {
- if (pixelSpace) {
- origin = ol.extent.getTopLeft(tileExtent);
- tileTransform = ol.vec.Mat4.makeTransform2D(this.tmpTransform_,
- offsetX, offsetY,
- pixelScale * tilePixelResolution,
- pixelScale * tilePixelResolution,
- rotation,
- (origin[0] - center[0]) / tilePixelResolution,
- (center[1] - origin[1]) / tilePixelResolution);
- } else {
- tileTransform = transform;
- }
- replayState.replayGroup.replay(replayContext, pixelRatio,
- tileTransform, rotation, skippedFeatureUids);
+
+ if (pixelSpace) {
+ origin = ol.extent.getTopLeft(tileExtent);
+ tileTransform = ol.vec.Mat4.makeTransform2D(this.tmpTransform_,
+ offsetX, offsetY,
+ pixelScale * tilePixelResolution,
+ pixelScale * tilePixelResolution,
+ rotation,
+ (origin[0] - center[0]) / tilePixelResolution,
+ (center[1] - origin[1]) / tilePixelResolution);
} else {
- tilePixelSize = source.getTilePixelSize(currentZ, pixelRatio, projection);
- if (pixelSpace) {
- tileTransform = ol.vec.Mat4.makeTransform2D(this.tmpTransform_,
- 0, 0,
- pixelScale * tilePixelResolution, pixelScale * tilePixelResolution,
- rotation,
- -tilePixelSize[0] / 2, -tilePixelSize[1] / 2);
- } else {
- tileCenter = ol.extent.getCenter(tileExtent);
- tileTransform = ol.vec.Mat4.makeTransform2D(this.tmpTransform_,
- 0, 0,
- pixelScale, -pixelScale,
- -rotation,
- -tileCenter[0], -tileCenter[1]);
- }
- tileContext = tile.getContext();
- if (replayState.resolution !== resolution ||
- replayState.rotation !== rotation) {
- replayState.resolution = resolution;
- replayState.rotation = rotation;
- tileContext.canvas.width = width + 0.5;
- tileContext.canvas.height = height + 0.5;
- tileContext.translate(width / 2, height / 2);
- tileContext.rotate(-rotation);
- replayState.replayGroup.replay(tileContext, pixelRatio,
- tileTransform, rotation, skippedFeatureUids, false);
- }
- insertTransform = ol.vec.Mat4.makeTransform2D(this.tmpTransform_,
- 0, 0, pixelScale, -pixelScale, 0, -center[0], -center[1]);
- insertPoint = ol.geom.flat.transform.transform2D(
- ol.extent.getTopLeft(tileExtent), 0, 1, 2, insertTransform);
- replayContext.drawImage(tileContext.canvas,
- Math.round(insertPoint[0] + offsetX),
- Math.round(insertPoint[1]) + offsetY);
+ tileTransform = transform;
}
+ ol.render.canvas.rotateAtOffset(replayContext, -rotation, offsetX, offsetY);
+ replayState.replayGroup.replay(replayContext, pixelRatio,
+ tileTransform, rotation, skippedFeatureUids, replays);
+ ol.render.canvas.rotateAtOffset(replayContext, rotation, offsetX, offsetY);
}
if (replayContext != context) {
@@ -194,19 +167,18 @@ ol.renderer.canvas.VectorTileLayer.prototype.composeFrame = function(frameState,
context.drawImage(replayContext.canvas, 0, 0);
}
replayContext.globalAlpha = alpha;
-
- this.dispatchPostComposeEvent(context, frameState, transform);
};
/**
* @param {ol.VectorTile} tile Tile.
- * @param {ol.layer.VectorTile} layer Vector tile layer.
- * @param {number} pixelRatio Pixel ratio.
- * @param {ol.proj.Projection} projection Projection.
+ * @param {olx.FrameState} frameState Frame state.
*/
ol.renderer.canvas.VectorTileLayer.prototype.createReplayGroup = function(tile,
- layer, pixelRatio, projection) {
+ frameState) {
+ var layer = this.getLayer();
+ var pixelRatio = frameState.pixelRatio;
+ var projection = frameState.viewState.projection;
var revision = layer.getRevision();
var renderOrder = layer.getRenderOrder() || null;
@@ -226,21 +198,20 @@ ol.renderer.canvas.VectorTileLayer.prototype.createReplayGroup = function(tile,
var tileCoord = tile.getTileCoord();
var tileProjection = tile.getProjection();
var pixelSpace = tileProjection.getUnits() == ol.proj.Units.TILE_PIXELS;
- var extent, reproject;
+ var resolution = tileGrid.getResolution(tileCoord[0]);
+ var extent, reproject, tileResolution;
if (pixelSpace) {
- var tilePixelSize = source.getTilePixelSize(tileCoord[0], pixelRatio,
- tile.getProjection());
- extent = [0, 0, tilePixelSize[0], tilePixelSize[1]];
+ var tilePixelRatio = tileResolution = source.getTilePixelRatio(pixelRatio);
+ var tileSize = ol.size.toSize(tileGrid.getTileSize(tileCoord[0]));
+ extent = [0, 0, tileSize[0] * tilePixelRatio, tileSize[1] * tilePixelRatio];
} else {
+ tileResolution = resolution;
extent = tileGrid.getTileCoordExtent(tileCoord);
if (!ol.proj.equivalent(projection, tileProjection)) {
reproject = true;
tile.setProjection(projection);
}
}
- var resolution = tileGrid.getResolution(tileCoord[0]);
- var tileResolution =
- pixelSpace ? source.getTilePixelRatio(pixelRatio) : resolution;
replayState.dirty = false;
var replayGroup = new ol.render.canvas.ReplayGroup(0, extent,
tileResolution, layer.getRenderBuffer());
@@ -307,7 +278,7 @@ ol.renderer.canvas.VectorTileLayer.prototype.forEachFeatureAtCoordinate = functi
/** @type {Object.} */
var features = {};
- var replayables = this.renderedTiles_;
+ var replayables = this.renderedTiles;
var source = layer.getSource();
goog.asserts.assertInstanceof(source, ol.source.VectorTile,
'Source is an ol.source.VectorTile');
@@ -319,7 +290,7 @@ ol.renderer.canvas.VectorTileLayer.prototype.forEachFeatureAtCoordinate = functi
tile = replayables[i];
tileCoord = tile.getTileCoord();
tileExtent = source.getTileGrid().getTileCoordExtent(tileCoord,
- this.tmpExtent_);
+ this.tmpExtent);
if (!ol.extent.containsCoordinate(tileExtent, coordinate)) {
continue;
}
@@ -369,116 +340,17 @@ ol.renderer.canvas.VectorTileLayer.prototype.handleStyleImageChange_ = function(
* @inheritDoc
*/
ol.renderer.canvas.VectorTileLayer.prototype.prepareFrame = function(frameState, layerState) {
- var layer = /** @type {ol.layer.Vector} */ (this.getLayer());
- goog.asserts.assertInstanceof(layer, ol.layer.VectorTile,
- 'layer is an instance of ol.layer.VectorTile');
- var source = layer.getSource();
- goog.asserts.assertInstanceof(source, ol.source.VectorTile,
- 'Source is an ol.source.VectorTile');
-
- this.updateAttributions(
- frameState.attributions, source.getAttributions());
- this.updateLogos(frameState, source);
-
- var animating = frameState.viewHints[ol.ViewHint.ANIMATING];
- var interacting = frameState.viewHints[ol.ViewHint.INTERACTING];
- var updateWhileAnimating = layer.getUpdateWhileAnimating();
- var updateWhileInteracting = layer.getUpdateWhileInteracting();
-
- if (!this.dirty_ && (!updateWhileAnimating && animating) ||
- (!updateWhileInteracting && interacting)) {
- return true;
- }
-
- var extent = frameState.extent;
- if (layerState.extent) {
- extent = ol.extent.getIntersection(extent, layerState.extent);
- }
- if (ol.extent.isEmpty(extent)) {
- // Return false to prevent the rendering of the layer.
- return false;
- }
-
- var viewState = frameState.viewState;
- var projection = viewState.projection;
- var resolution = viewState.resolution;
- var pixelRatio = frameState.pixelRatio;
-
- var tileGrid = source.getTileGrid();
- var resolutions = tileGrid.getResolutions();
- var z = resolutions.length - 1;
- while (z > 0 && resolutions[z] < resolution) {
- --z;
- }
- var tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z);
- this.updateUsedTiles(frameState.usedTiles, source, z, tileRange);
- this.manageTilePyramid(frameState, source, tileGrid, pixelRatio,
- projection, extent, z, layer.getPreload());
- this.scheduleExpireCache(frameState, source);
-
- /**
- * @type {Object.>}
- */
- var tilesToDrawByZ = {};
- tilesToDrawByZ[z] = {};
-
- var findLoadedTiles = this.createLoadedTileFinder(source, projection,
- tilesToDrawByZ);
-
- var useInterimTilesOnError = layer.getUseInterimTilesOnError();
-
- var tmpExtent = this.tmpExtent_;
- var tmpTileRange = new ol.TileRange(0, 0, 0, 0);
- var childTileRange, fullyLoaded, tile, tileState, x, y;
- for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
- for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
-
- tile = source.getTile(z, x, y, pixelRatio, projection);
- goog.asserts.assertInstanceof(tile, ol.VectorTile,
- 'Tile is an ol.VectorTile');
- tileState = tile.getState();
- if (tileState == ol.TileState.LOADED ||
- tileState == ol.TileState.EMPTY ||
- (tileState == ol.TileState.ERROR && !useInterimTilesOnError)) {
- tilesToDrawByZ[z][tile.tileCoord.toString()] = tile;
- continue;
- }
-
- fullyLoaded = tileGrid.forEachTileCoordParentTileRange(
- tile.tileCoord, findLoadedTiles, null, tmpTileRange, tmpExtent);
- if (!fullyLoaded) {
- childTileRange = tileGrid.getTileCoordChildTileRange(
- tile.tileCoord, tmpTileRange, tmpExtent);
- if (childTileRange) {
- findLoadedTiles(z + 1, childTileRange);
- }
- }
-
+ var prepared = goog.base(this, 'prepareFrame', frameState, layerState);
+ if (prepared) {
+ var skippedFeatures = Object.keys(frameState.skippedFeatureUids_ || {});
+ for (var i = 0, ii = this.renderedTiles.length; i < ii; ++i) {
+ var tile = this.renderedTiles[i];
+ goog.asserts.assertInstanceof(tile, ol.VectorTile, 'got an ol.VectorTile');
+ this.createReplayGroup(tile, frameState);
+ this.renderTileImage(tile, frameState, layerState, skippedFeatures);
}
}
-
- this.dirty_ = false;
-
- /** @type {Array.} */
- var zs = Object.keys(tilesToDrawByZ).map(Number);
- zs.sort(ol.array.numberSafeCompareFunction);
- var replayables = [];
- var i, ii, currentZ, tileCoordKey, tilesToDraw;
- for (i = 0, ii = zs.length; i < ii; ++i) {
- currentZ = zs[i];
- tilesToDraw = tilesToDrawByZ[currentZ];
- for (tileCoordKey in tilesToDraw) {
- tile = tilesToDraw[tileCoordKey];
- if (tile.getState() == ol.TileState.LOADED) {
- replayables.push(tile);
- this.createReplayGroup(tile, layer, pixelRatio, projection);
- }
- }
- }
-
- this.renderedTiles_ = replayables;
-
- return true;
+ return prepared;
};
@@ -508,3 +380,65 @@ ol.renderer.canvas.VectorTileLayer.prototype.renderFeature = function(feature, s
}
return loading;
};
+
+
+/**
+ * @param {ol.VectorTile} tile Tile.
+ * @param {olx.FrameState} frameState Frame state.
+ * @param {ol.layer.LayerState} layerState Layer state.
+ * @param {Array.} skippedFeatures Skipped features.
+ */
+ol.renderer.canvas.VectorTileLayer.prototype.renderTileImage = function(
+ tile, frameState, layerState, skippedFeatures) {
+ var layer = this.getLayer();
+ var replays = ol.renderer.canvas.IMAGE_REPLAYS[layer.getRenderMode()];
+ if (!replays) {
+ return;
+ }
+ var pixelRatio = frameState.pixelRatio;
+ var replayState = tile.getReplayState();
+ var revision = layer.getRevision();
+ if (!ol.array.equals(replayState.skippedFeatures, skippedFeatures) ||
+ replayState.renderedTileRevision !== revision) {
+ replayState.skippedFeatures = skippedFeatures;
+ replayState.renderedTileRevision = revision;
+ var tileContext = tile.getContext();
+ var source = layer.getSource();
+ var tileGrid = source.getTileGrid();
+ var currentZ = tile.getTileCoord()[0];
+ var resolution = tileGrid.getResolution(currentZ);
+ var tileSize = ol.size.toSize(tileGrid.getTileSize(currentZ));
+ var tileResolution = tileGrid.getResolution(currentZ);
+ var resolutionRatio = tileResolution / resolution;
+ var width = tileSize[0] * pixelRatio * resolutionRatio;
+ var height = tileSize[1] * pixelRatio * resolutionRatio;
+ tileContext.canvas.width = width / resolutionRatio + 0.5;
+ tileContext.canvas.height = height / resolutionRatio + 0.5;
+ tileContext.scale(1 / resolutionRatio, 1 / resolutionRatio)
+ tileContext.translate(width / 2, height / 2);
+ var pixelSpace = tile.getProjection().getUnits() == ol.proj.Units.TILE_PIXELS;
+ var pixelScale = pixelRatio / resolution;
+ var tilePixelRatio = source.getTilePixelRatio(pixelRatio);
+ var tilePixelResolution = tileResolution / tilePixelRatio;
+ var tileExtent = tileGrid.getTileCoordExtent(
+ tile.getTileCoord(), this.tmpExtent);
+ var tileTransform;
+ if (pixelSpace) {
+ tileTransform = ol.vec.Mat4.makeTransform2D(this.tmpTransform_,
+ 0, 0,
+ pixelScale * tilePixelResolution, pixelScale * tilePixelResolution,
+ 0,
+ -tileSize[0] * tilePixelRatio / 2, -tileSize[1] * tilePixelRatio / 2);
+ } else {
+ var tileCenter = ol.extent.getCenter(tileExtent);
+ tileTransform = ol.vec.Mat4.makeTransform2D(this.tmpTransform_,
+ 0, 0,
+ pixelScale, -pixelScale,
+ 0,
+ -tileCenter[0], -tileCenter[1]);
+ }
+
+ replayState.replayGroup.replay(tileContext, pixelRatio,
+ tileTransform, 0, frameState.skippedFeatureUids || {}, replays);
+ }
+}
diff --git a/src/ol/source/vectortilesource.js b/src/ol/source/vectortilesource.js
index bbb6a825d6..8b7a57adba 100644
--- a/src/ol/source/vectortilesource.js
+++ b/src/ol/source/vectortilesource.js
@@ -5,6 +5,7 @@ goog.require('ol.VectorTile');
goog.require('ol.events');
goog.require('ol.events.EventType');
goog.require('ol.featureloader');
+goog.require('ol.size');
goog.require('ol.source.UrlTile');
@@ -31,7 +32,7 @@ ol.source.VectorTile = function(options) {
cacheSize: options.cacheSize !== undefined ? options.cacheSize : 128,
extent: options.extent,
logo: options.logo,
- opaque: options.opaque,
+ opaque: false,
projection: options.projection,
state: options.state,
tileGrid: options.tileGrid,
@@ -88,6 +89,15 @@ ol.source.VectorTile.prototype.getTile = function(z, x, y, pixelRatio, projectio
};
+/**
+ * @inheritDoc
+ */
+ol.source.VectorTile.prototype.getTilePixelSize = function(z, pixelRatio, projection) {
+ var tileSize = ol.size.toSize(this.tileGrid.getTileSize(z));
+ return [tileSize[0] * pixelRatio, tileSize[1] * pixelRatio];
+};
+
+
/**
* @param {ol.VectorTile} vectorTile Vector tile.
* @param {string} url URL.
diff --git a/src/ol/tilegrid/tilegrid.js b/src/ol/tilegrid/tilegrid.js
index 859fd62217..2c6717c393 100644
--- a/src/ol/tilegrid/tilegrid.js
+++ b/src/ol/tilegrid/tilegrid.js
@@ -472,10 +472,15 @@ ol.tilegrid.TileGrid.prototype.getFullTileRange = function(z) {
/**
* @param {number} resolution Resolution.
+ * @param {number=} opt_direction If 0, the nearest resolution will be used.
+ * If 1, the nearest lower resolution will be used. If -1, the nearest
+ * higher resolution will be used. Default is 0.
* @return {number} Z.
*/
-ol.tilegrid.TileGrid.prototype.getZForResolution = function(resolution) {
- var z = ol.array.linearFindNearest(this.resolutions_, resolution, 0);
+ol.tilegrid.TileGrid.prototype.getZForResolution = function(
+ resolution, opt_direction) {
+ var z = ol.array.linearFindNearest(this.resolutions_, resolution,
+ opt_direction || 0);
return ol.math.clamp(z, this.minZoom, this.maxZoom);
};
diff --git a/src/ol/vectortile.js b/src/ol/vectortile.js
index 85fb41e349..5aa00d4e00 100644
--- a/src/ol/vectortile.js
+++ b/src/ol/vectortile.js
@@ -12,8 +12,10 @@ goog.require('ol.proj.Projection');
* @typedef {{
* dirty: boolean,
* renderedRenderOrder: (null|function(ol.Feature, ol.Feature):number),
+ * renderedTileRevision: number,
* renderedRevision: number,
- * replayGroup: ol.render.IReplayGroup}}
+ * replayGroup: ol.render.IReplayGroup,
+ * skippedFeatures: Array.}}
*/
ol.TileReplayState;
@@ -70,7 +72,9 @@ ol.VectorTile = function(tileCoord, state, src, format, tileLoadFunction) {
dirty: false,
renderedRenderOrder: null,
renderedRevision: -1,
- replayGroup: null
+ renderedTileRevision: -1,
+ replayGroup: null,
+ skippedFeatures: []
};
/**
@@ -100,8 +104,9 @@ ol.VectorTile.prototype.getContext = function() {
/**
* @inheritDoc
*/
-ol.VectorTile.prototype.disposeInternal = function() {
- goog.base(this, 'disposeInternal');
+ol.VectorTile.prototype.getImage = function() {
+ return this.replayState_.renderedTileRevision == -1 ?
+ null : this.context_.canvas;
};
diff --git a/test/spec/ol/layer/vectortilelayer.test.js b/test/spec/ol/layer/vectortilelayer.test.js
index fcbbde71c2..44a5a73495 100644
--- a/test/spec/ol/layer/vectortilelayer.test.js
+++ b/test/spec/ol/layer/vectortilelayer.test.js
@@ -28,6 +28,29 @@ describe('ol.layer.VectorTile', function() {
expect(layer.getUseInterimTilesOnError()).to.be(true);
});
+ it('provides default renderMode', function() {
+ expect(layer.getRenderMode()).to.be('hybrid');
+ })
+
+ });
+
+ describe('constructor (options)', function() {
+ var layer = new ol.layer.VectorTile({
+ renderMode: 'vector',
+ source: new ol.source.VectorTile({})
+ });
+ expect(layer.getRenderMode()).to.be('vector');
+ layer = new ol.layer.VectorTile({
+ renderMode: 'image',
+ source: new ol.source.VectorTile({})
+ });
+ expect(layer.getRenderMode()).to.be('image');
+ expect(function() {
+ layer = new ol.layer.VectorTile({
+ renderMode: 'foo',
+ source: new ol.source.VectorTile({})
+ });
+ }).to.throwException();
});
});
diff --git a/test/spec/ol/renderer/canvas/canvasvectortilelayerrenderer.test.js b/test/spec/ol/renderer/canvas/canvasvectortilelayerrenderer.test.js
index e29d0ac0d2..0b1f13ccfa 100644
--- a/test/spec/ol/renderer/canvas/canvasvectortilelayerrenderer.test.js
+++ b/test/spec/ol/renderer/canvas/canvasvectortilelayerrenderer.test.js
@@ -52,16 +52,24 @@ describe('ol.renderer.canvas.VectorTileLayer', function() {
map.addLayer(layer);
});
- it('creates a new instance', function() {
- var renderer = new ol.renderer.canvas.VectorTileLayer(layer);
- expect(renderer).to.be.a(ol.renderer.canvas.VectorTileLayer);
- });
-
afterEach(function() {
document.body.removeChild(target);
map.dispose();
});
+ it('creates a new instance', function() {
+ var renderer = new ol.renderer.canvas.VectorTileLayer(layer);
+ expect(renderer).to.be.a(ol.renderer.canvas.VectorTileLayer);
+ expect(renderer.zDirection).to.be(0);
+ });
+
+ it('uses lower resolution for pure vector rendering', function() {
+ layer.renderMode_ = 'vector';
+ var renderer = new ol.renderer.canvas.VectorTileLayer(layer);
+ expect(renderer).to.be.a(ol.renderer.canvas.VectorTileLayer);
+ expect(renderer.zDirection).to.be(1);
+ });
+
it('gives precedence to feature styles over layer styles', function() {
var spy = sinon.spy(map.getRenderer().getLayerRenderer(layer),
'renderFeature');
@@ -134,7 +142,7 @@ describe('ol.renderer.canvas.VectorTileLayer', function() {
}
};
frameState.layerStates[goog.getUid(layer)] = {};
- renderer.renderedTiles_ = [new TileClass([0, 0, -1])];
+ renderer.renderedTiles = [new TileClass([0, 0, -1])];
renderer.forEachFeatureAtCoordinate(
coordinate, frameState, spy, undefined);
expect(spy.callCount).to.be(1);
diff --git a/test/spec/ol/tilegrid/tilegrid.test.js b/test/spec/ol/tilegrid/tilegrid.test.js
index 08ed45679e..9599cbb0cb 100644
--- a/test/spec/ol/tilegrid/tilegrid.test.js
+++ b/test/spec/ol/tilegrid/tilegrid.test.js
@@ -1028,6 +1028,53 @@ describe('ol.tilegrid.TileGrid', function() {
});
});
+ describe('getZForResolution (lower)', function() {
+ it('returns the expected z value', function() {
+ var tileGrid = new ol.tilegrid.TileGrid({
+ resolutions: resolutions,
+ origin: origin,
+ tileSize: tileSize
+ });
+
+ expect(tileGrid.getZForResolution(2000, 1)).to.eql(0);
+ expect(tileGrid.getZForResolution(1000, 1)).to.eql(0);
+ expect(tileGrid.getZForResolution(900, 1)).to.eql(0);
+ expect(tileGrid.getZForResolution(750, 1)).to.eql(0);
+ expect(tileGrid.getZForResolution(625, 1)).to.eql(0);
+ expect(tileGrid.getZForResolution(500, 1)).to.eql(1);
+ expect(tileGrid.getZForResolution(475, 1)).to.eql(1);
+ expect(tileGrid.getZForResolution(375, 1)).to.eql(1);
+ expect(tileGrid.getZForResolution(250, 1)).to.eql(2);
+ expect(tileGrid.getZForResolution(200, 1)).to.eql(2);
+ expect(tileGrid.getZForResolution(125, 1)).to.eql(2);
+ expect(tileGrid.getZForResolution(100, 1)).to.eql(3);
+ expect(tileGrid.getZForResolution(50, 1)).to.eql(3);
+ });
+ });
+
+ describe('getZForResolution (higher)', function() {
+ it('returns the expected z value', function() {
+ var tileGrid = new ol.tilegrid.TileGrid({
+ resolutions: resolutions,
+ origin: origin,
+ tileSize: tileSize
+ });
+
+ expect(tileGrid.getZForResolution(2000, -1)).to.eql(0);
+ expect(tileGrid.getZForResolution(1000, -1)).to.eql(0);
+ expect(tileGrid.getZForResolution(900, -1)).to.eql(1);
+ expect(tileGrid.getZForResolution(750, -1)).to.eql(1);
+ expect(tileGrid.getZForResolution(625, -1)).to.eql(1);
+ expect(tileGrid.getZForResolution(500, -1)).to.eql(1);
+ expect(tileGrid.getZForResolution(475, -1)).to.eql(2);
+ expect(tileGrid.getZForResolution(375, -1)).to.eql(2);
+ expect(tileGrid.getZForResolution(250, -1)).to.eql(2);
+ expect(tileGrid.getZForResolution(200, -1)).to.eql(3);
+ expect(tileGrid.getZForResolution(125, -1)).to.eql(3);
+ expect(tileGrid.getZForResolution(100, -1)).to.eql(3);
+ expect(tileGrid.getZForResolution(50, -1)).to.eql(3);
+ });
+ });
});
goog.require('ol.Coordinate');