Rotate map canvas after composition

This commit is contained in:
Andreas Hocevar
2016-02-13 21:55:09 +01:00
parent 98b823c5fc
commit a109062b1f
12 changed files with 171 additions and 79 deletions

View File

@@ -2,6 +2,44 @@
### v3.14.0 ### v3.14.0
#### Layer pre-/postcompose event changes
It is the responsibility of the application to undo any canvas transform changes at the end of a layer 'precompose' or 'postcompose' handler. Previously, it was ok to set a null transform. The API now guarantees a device pixel coordinate system on the canvas with its origin in the top left corner of the map. However, applications should not rely on the underlying canvas being the same size as the visible viewport.
Old code:
```js
layer.on('precompose', function(e) {
// rely on canvas dimensions to move coordinate origin to center
e.context.translate(e.context.canvas.width / 2, e.context.canvas.height / 2);
e.context.scale(3, 3);
// draw an x in the center of the viewport
e.context.moveTo(-20, -20);
e.context.lineTo(20, 20);
e.context.moveTo(-20, 20);
e.context.lineTo(20, -20);
// rely on the canvas having a null transform
e.context.setTransform(1, 0, 0, 1, 0, 0);
});
```
New code:
```js
layer.on('precompose', function(e) {
// use map size and pixel ratio to move coordinate origin to center
var size = map.getSize();
var pixelRatio = e.frameState.pixelRatio;
e.context.translate(size[0] / 2 * pixelRatio, size[1] / 2 * pixelRatio);
e.context.scale(3, 3);
// draw an x in the center of the viewport
e.context.moveTo(-20, -20);
e.context.lineTo(20, 20);
e.context.moveTo(-20, 20);
e.context.lineTo(20, -20);
// undo all transforms
e.context.scale(1 / 3, 1 / 3);
e.context.translate(-size[0] / 2 * pixelRatio, -size[1] / 2 * pixelRatio);
});
```
### v3.13.0 ### v3.13.0
#### `proj4js` integration #### `proj4js` integration

View File

@@ -25,8 +25,10 @@ var map = new ol.Map({
osm.on('precompose', function(event) { osm.on('precompose', function(event) {
var ctx = event.context; var ctx = event.context;
ctx.save(); ctx.save();
ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2); var pixelRatio = event.frameState.pixelRatio;
ctx.scale(3, 3); var size = map.getSize();
ctx.translate(size[0] / 2 * pixelRatio, size[1] / 2 * pixelRatio);
ctx.scale(3 * pixelRatio, 3 * pixelRatio);
ctx.translate(-75, -80); ctx.translate(-75, -80);
ctx.beginPath(); ctx.beginPath();
ctx.moveTo(75, 40); ctx.moveTo(75, 40);
@@ -37,7 +39,9 @@ osm.on('precompose', function(event) {
ctx.bezierCurveTo(130, 62.5, 130, 25, 100, 25); ctx.bezierCurveTo(130, 62.5, 130, 25, 100, 25);
ctx.bezierCurveTo(85, 25, 75, 37, 75, 40); ctx.bezierCurveTo(85, 25, 75, 37, 75, 40);
ctx.clip(); ctx.clip();
ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.translate(75, 80);
ctx.scale(1 / 3 / pixelRatio, 1 / 3 / pixelRatio);
ctx.translate(-size[0] / 2 * pixelRatio, -size[1] / 2 * pixelRatio);
}); });
osm.on('postcompose', function(event) { osm.on('postcompose', function(event) {

View File

@@ -575,18 +575,24 @@ ol.extent.getForViewAndSize = function(center, resolution, rotation, size, opt_e
var dy = resolution * size[1] / 2; var dy = resolution * size[1] / 2;
var cosRotation = Math.cos(rotation); var cosRotation = Math.cos(rotation);
var sinRotation = Math.sin(rotation); var sinRotation = Math.sin(rotation);
/** @type {Array.<number>} */ var xCos = dx * cosRotation;
var xs = [-dx, -dx, dx, dx]; var xSin = dx * sinRotation;
/** @type {Array.<number>} */ var yCos = dy * cosRotation;
var ys = [-dy, dy, -dy, dy]; var ySin = dy * sinRotation;
var i, x, y; var x = center[0];
for (i = 0; i < 4; ++i) { var y = center[1];
x = xs[i]; var x0 = x - xCos + ySin;
y = ys[i]; var x1 = x - xCos - ySin;
xs[i] = center[0] + x * cosRotation - y * sinRotation; var x2 = x + xCos - ySin;
ys[i] = center[1] + x * sinRotation + y * cosRotation; var x3 = x + xCos + ySin;
} var y0 = y - xSin - yCos;
return ol.extent.boundingExtentXYs_(xs, ys, opt_extent); var y1 = y - xSin + yCos;
var y2 = y + xSin + yCos;
var y3 = y + xSin - yCos;
return ol.extent.createOrUpdate(
Math.min(x0, x1, x2, x3), Math.min(y0, y1, y2, y3),
Math.max(x0, x1, x2, x3), Math.max(y0, y1, y2, y3),
opt_extent);
}; };

View File

@@ -147,7 +147,6 @@ ol.renderer.canvas.ImageLayer.prototype.prepareFrame = function(frameState, laye
var viewState = frameState.viewState; var viewState = frameState.viewState;
var viewCenter = viewState.center; var viewCenter = viewState.center;
var viewResolution = viewState.resolution; var viewResolution = viewState.resolution;
var viewRotation = viewState.rotation;
var image; var image;
var imageLayer = this.getLayer(); var imageLayer = this.getLayer();
@@ -195,7 +194,7 @@ ol.renderer.canvas.ImageLayer.prototype.prepareFrame = function(frameState, laye
pixelRatio * frameState.size[0] / 2, pixelRatio * frameState.size[0] / 2,
pixelRatio * frameState.size[1] / 2, pixelRatio * frameState.size[1] / 2,
scale, scale, scale, scale,
viewRotation, 0,
imagePixelRatio * (imageExtent[0] - viewCenter[0]) / imageResolution, imagePixelRatio * (imageExtent[0] - viewCenter[0]) / imageResolution,
imagePixelRatio * (viewCenter[1] - imageExtent[3]) / imageResolution); imagePixelRatio * (viewCenter[1] - imageExtent[3]) / imageResolution);
this.imageTransformInv_ = null; this.imageTransformInv_ = null;

View File

@@ -51,6 +51,8 @@ ol.renderer.canvas.Layer.prototype.composeFrame = function(frameState, layerStat
goog.asserts.assert(extent !== undefined, goog.asserts.assert(extent !== undefined,
'layerState extent is defined'); 'layerState extent is defined');
var pixelRatio = frameState.pixelRatio; var pixelRatio = frameState.pixelRatio;
var width = frameState.size[0] * pixelRatio;
var height = frameState.size[1] * pixelRatio;
var topLeft = ol.extent.getTopLeft(extent); var topLeft = ol.extent.getTopLeft(extent);
var topRight = ol.extent.getTopRight(extent); var topRight = ol.extent.getTopRight(extent);
var bottomRight = ol.extent.getBottomRight(extent); var bottomRight = ol.extent.getBottomRight(extent);
@@ -66,12 +68,18 @@ ol.renderer.canvas.Layer.prototype.composeFrame = function(frameState, layerStat
bottomLeft, bottomLeft); bottomLeft, bottomLeft);
context.save(); context.save();
context.translate(width / 2, height / 2);
context.rotate(-frameState.viewState.rotation);
context.translate(-width / 2, -height / 2);
context.beginPath(); context.beginPath();
context.moveTo(topLeft[0] * pixelRatio, topLeft[1] * pixelRatio); context.moveTo(topLeft[0] * pixelRatio, topLeft[1] * pixelRatio);
context.lineTo(topRight[0] * pixelRatio, topRight[1] * pixelRatio); context.lineTo(topRight[0] * pixelRatio, topRight[1] * pixelRatio);
context.lineTo(bottomRight[0] * pixelRatio, bottomRight[1] * pixelRatio); context.lineTo(bottomRight[0] * pixelRatio, bottomRight[1] * pixelRatio);
context.lineTo(bottomLeft[0] * pixelRatio, bottomLeft[1] * pixelRatio); context.lineTo(bottomLeft[0] * pixelRatio, bottomLeft[1] * pixelRatio);
context.clip(); context.clip();
context.translate(width / 2, height / 2);
context.rotate(frameState.viewState.rotation);
context.translate(-width / 2, -height / 2);
} }
var imageTransform = this.getImageTransform(); var imageTransform = this.getImageTransform();
@@ -83,24 +91,12 @@ ol.renderer.canvas.Layer.prototype.composeFrame = function(frameState, layerStat
// for performance reasons, context.setTransform is only used // for performance reasons, context.setTransform is only used
// when the view is rotated. see http://jsperf.com/canvas-transform // when the view is rotated. see http://jsperf.com/canvas-transform
if (frameState.viewState.rotation === 0) { var dx = goog.vec.Mat4.getElement(imageTransform, 0, 3);
var dx = goog.vec.Mat4.getElement(imageTransform, 0, 3); var dy = goog.vec.Mat4.getElement(imageTransform, 1, 3);
var dy = goog.vec.Mat4.getElement(imageTransform, 1, 3); var dw = image.width * goog.vec.Mat4.getElement(imageTransform, 0, 0);
var dw = image.width * goog.vec.Mat4.getElement(imageTransform, 0, 0); var dh = image.height * goog.vec.Mat4.getElement(imageTransform, 1, 1);
var dh = image.height * goog.vec.Mat4.getElement(imageTransform, 1, 1); context.drawImage(image, 0, 0, +image.width, +image.height,
context.drawImage(image, 0, 0, +image.width, +image.height, Math.round(dx), Math.round(dy), Math.round(dw), Math.round(dh));
Math.round(dx), Math.round(dy), Math.round(dw), Math.round(dh));
} else {
context.setTransform(
goog.vec.Mat4.getElement(imageTransform, 0, 0),
goog.vec.Mat4.getElement(imageTransform, 1, 0),
goog.vec.Mat4.getElement(imageTransform, 0, 1),
goog.vec.Mat4.getElement(imageTransform, 1, 1),
goog.vec.Mat4.getElement(imageTransform, 0, 3),
goog.vec.Mat4.getElement(imageTransform, 1, 3));
context.drawImage(image, 0, 0);
context.setTransform(1, 0, 0, 1, 0, 0);
}
context.globalAlpha = alpha; context.globalAlpha = alpha;
if (clipped) { if (clipped) {
@@ -123,6 +119,11 @@ ol.renderer.canvas.Layer.prototype.composeFrame = function(frameState, layerStat
ol.renderer.canvas.Layer.prototype.dispatchComposeEvent_ = function(type, context, frameState, opt_transform) { ol.renderer.canvas.Layer.prototype.dispatchComposeEvent_ = function(type, context, frameState, opt_transform) {
var layer = this.getLayer(); var layer = this.getLayer();
if (layer.hasListener(type)) { if (layer.hasListener(type)) {
var width = frameState.size[0] * frameState.pixelRatio;
var height = frameState.size[1] * frameState.pixelRatio;
context.translate(width / 2, height / 2);
context.rotate(-frameState.viewState.rotation);
context.translate(-width / 2, -height / 2);
var transform = opt_transform !== undefined ? var transform = opt_transform !== undefined ?
opt_transform : this.getTransform(frameState, 0); opt_transform : this.getTransform(frameState, 0);
var render = new ol.render.canvas.Immediate( var render = new ol.render.canvas.Immediate(
@@ -132,6 +133,9 @@ ol.renderer.canvas.Layer.prototype.dispatchComposeEvent_ = function(type, contex
context, null); context, null);
layer.dispatchEvent(composeEvent); layer.dispatchEvent(composeEvent);
render.flush(); render.flush();
context.translate(width / 2, height / 2);
context.rotate(frameState.viewState.rotation);
context.translate(-width / 2, -height / 2);
} }
}; };

View File

@@ -11,6 +11,7 @@ goog.require('ol.RendererType');
goog.require('ol.array'); goog.require('ol.array');
goog.require('ol.css'); goog.require('ol.css');
goog.require('ol.dom'); goog.require('ol.dom');
goog.require('ol.extent');
goog.require('ol.layer.Image'); goog.require('ol.layer.Image');
goog.require('ol.layer.Layer'); goog.require('ol.layer.Layer');
goog.require('ol.layer.Tile'); goog.require('ol.layer.Tile');
@@ -45,6 +46,12 @@ ol.renderer.canvas.Map = function(container, map) {
*/ */
this.context_ = ol.dom.createCanvasContext2D(); this.context_ = ol.dom.createCanvasContext2D();
/**
* @private
* @type {CanvasRenderingContext2D}
*/
this.renderContext_ = ol.dom.createCanvasContext2D();
/** /**
* @private * @private
* @type {HTMLCanvasElement} * @type {HTMLCanvasElement}
@@ -56,6 +63,24 @@ ol.renderer.canvas.Map = function(container, map) {
this.canvas_.className = ol.css.CLASS_UNSELECTABLE; this.canvas_.className = ol.css.CLASS_UNSELECTABLE;
goog.dom.insertChildAt(container, this.canvas_, 0); goog.dom.insertChildAt(container, this.canvas_, 0);
/**
* @private
* @type {HTMLCanvasElement}
*/
this.renderCanvas_ = this.renderContext_.canvas;
/**
* @private
* @type {ol.Coordinate}
*/
this.pixelCenter_ = [0, 0];
/**
* @private
* @type {ol.Extent}
*/
this.pixelExtent_ = ol.extent.createEmpty();
/** /**
* @private * @private
* @type {boolean} * @type {boolean}
@@ -156,14 +181,27 @@ ol.renderer.canvas.Map.prototype.renderFrame = function(frameState) {
return; return;
} }
var context = this.context_; var context;
var width = frameState.size[0] * frameState.pixelRatio; var pixelRatio = frameState.pixelRatio;
var height = frameState.size[1] * frameState.pixelRatio; var width = frameState.size[0] * pixelRatio;
if (this.canvas_.width != width || this.canvas_.height != height) { var height = frameState.size[1] * pixelRatio;
this.canvas_.width = width; this.canvas_.width = width;
this.canvas_.height = height; this.canvas_.height = height;
var rotation = frameState.viewState.rotation;
var pixelExtent;
if (rotation) {
context = this.renderContext_;
pixelExtent = ol.extent.getForViewAndSize(this.pixelCenter_, pixelRatio,
rotation, frameState.size, this.pixelExtent_);
var renderWidth = ol.extent.getWidth(pixelExtent);
var renderHeight = ol.extent.getHeight(pixelExtent);
this.renderCanvas_.width = renderWidth + 0.5;
this.renderCanvas_.height = renderHeight + 0.5;
this.renderContext_.translate(Math.round((renderWidth - width) / 2),
Math.round((renderHeight - height) / 2));
} else { } else {
context.clearRect(0, 0, this.canvas_.width, this.canvas_.height); context = this.context_;
} }
this.calculateMatrices2D(frameState); this.calculateMatrices2D(frameState);
@@ -190,6 +228,15 @@ ol.renderer.canvas.Map.prototype.renderFrame = function(frameState) {
} }
} }
if (rotation) {
this.context_.translate(width / 2, height / 2);
this.context_.rotate(rotation);
this.context_.drawImage(this.renderCanvas_,
Math.round(pixelExtent[0]), Math.round(pixelExtent[1]));
this.context_.rotate(-rotation);
this.context_.translate(-width / 2, -height / 2);
}
this.dispatchComposeEvent_( this.dispatchComposeEvent_(
ol.render.EventType.POSTCOMPOSE, frameState); ol.render.EventType.POSTCOMPOSE, frameState);

View File

@@ -61,7 +61,6 @@ ol.renderer.canvas.TileLayer.prototype.composeFrame = function(
var center = viewState.center; var center = viewState.center;
var projection = viewState.projection; var projection = viewState.projection;
var resolution = viewState.resolution; var resolution = viewState.resolution;
var rotation = viewState.rotation;
var size = frameState.size; var size = frameState.size;
var pixelScale = pixelRatio / resolution; var pixelScale = pixelRatio / resolution;
var layer = this.getLayer(); var layer = this.getLayer();
@@ -86,12 +85,6 @@ ol.renderer.canvas.TileLayer.prototype.composeFrame = function(
} }
var offsetX = Math.round(pixelRatio * size[0] / 2); var offsetX = Math.round(pixelRatio * size[0] / 2);
var offsetY = Math.round(pixelRatio * size[1] / 2); var offsetY = Math.round(pixelRatio * size[1] / 2);
// Sub-pixel overlap between tiles to avoid gaps
var overlap = (rotation * 180 / Math.PI) % 90 === 0 ? 0 :
opaque ? 0.25 : 0.125;
renderContext.translate(offsetX, offsetY);
renderContext.rotate(rotation);
// for performance reasons, context.save / context.restore is not used // for performance reasons, context.save / context.restore is not used
// to save and restore the transformation matrix and the opacity. // to save and restore the transformation matrix and the opacity.
@@ -115,25 +108,25 @@ ol.renderer.canvas.TileLayer.prototype.composeFrame = function(
// filled by a higher resolution tile // filled by a higher resolution tile
renderContext.save(); renderContext.save();
renderContext.beginPath(); renderContext.beginPath();
renderContext.moveTo((tileExtent[0] - center[0]) * pixelScale, renderContext.moveTo((tileExtent[0] - center[0]) * pixelScale + offsetX,
(center[1] - tileExtent[1]) * pixelScale); (center[1] - tileExtent[1]) * pixelScale + offsetY);
renderContext.lineTo((tileExtent[2] - center[0]) * pixelScale, renderContext.lineTo((tileExtent[2] - center[0]) * pixelScale + offsetX,
(center[1] - tileExtent[1]) * pixelScale); (center[1] - tileExtent[1]) * pixelScale + offsetY);
renderContext.lineTo((tileExtent[2] - center[0]) * pixelScale, renderContext.lineTo((tileExtent[2] - center[0]) * pixelScale + offsetX,
(center[1] - tileExtent[3]) * pixelScale); (center[1] - tileExtent[3]) * pixelScale + offsetY);
renderContext.lineTo((tileExtent[0] - center[0]) * pixelScale, renderContext.lineTo((tileExtent[0] - center[0]) * pixelScale + offsetX,
(center[1] - tileExtent[3]) * pixelScale); (center[1] - tileExtent[3]) * pixelScale + offsetY);
renderContext.closePath(); renderContext.closePath();
for (j = 0, jj = clipExtents.length; j < jj; ++j) { for (j = 0, jj = clipExtents.length; j < jj; ++j) {
clipExtent = clipExtents[j]; clipExtent = clipExtents[j];
renderContext.moveTo((clipExtent[0] - center[0]) * pixelScale, renderContext.moveTo((clipExtent[0] - center[0]) * pixelScale + offsetX,
(center[1] - clipExtent[1]) * pixelScale); (center[1] - clipExtent[1]) * pixelScale + offsetY);
renderContext.lineTo((clipExtent[0] - center[0]) * pixelScale, renderContext.lineTo((clipExtent[0] - center[0]) * pixelScale + offsetX,
(center[1] - clipExtent[3]) * pixelScale); (center[1] - clipExtent[3]) * pixelScale + offsetY);
renderContext.lineTo((clipExtent[2] - center[0]) * pixelScale, renderContext.lineTo((clipExtent[2] - center[0]) * pixelScale + offsetX,
(center[1] - clipExtent[3]) * pixelScale); (center[1] - clipExtent[3]) * pixelScale + offsetY);
renderContext.lineTo((clipExtent[2] - center[0]) * pixelScale, renderContext.lineTo((clipExtent[2] - center[0]) * pixelScale + offsetX,
(center[1] - clipExtent[1]) * pixelScale); (center[1] - clipExtent[1]) * pixelScale + offsetY);
renderContext.closePath(); renderContext.closePath();
} }
renderContext.clip(); renderContext.clip();
@@ -147,23 +140,19 @@ ol.renderer.canvas.TileLayer.prototype.composeFrame = function(
// gaps caused by rounding // gaps caused by rounding
origin = ol.extent.getBottomLeft(tileGrid.getTileCoordExtent( origin = ol.extent.getBottomLeft(tileGrid.getTileCoordExtent(
tileGrid.getTileCoordForCoordAndZ(center, currentZ))); tileGrid.getTileCoordForCoordAndZ(center, currentZ)));
tileOffsetX = Math.round((origin[0] - center[0]) * pixelScale); tileOffsetX = offsetX + Math.round((origin[0] - center[0]) * pixelScale);
tileOffsetY = Math.round((center[1] - origin[1]) * pixelScale); tileOffsetY = offsetY + Math.round((center[1] - origin[1]) * pixelScale);
renderContext.drawImage(tile.getImage(), tileGutter, tileGutter, renderContext.drawImage(tile.getImage(), tileGutter, tileGutter,
tilePixelSize[0], tilePixelSize[1], tilePixelSize[0], tilePixelSize[1],
Math.round((insertPoint[0] - origin[0]) * pixelScale / tileWidth) * Math.round((insertPoint[0] - origin[0]) * pixelScale / tileWidth) *
tileWidth + tileOffsetX - overlap, tileWidth + tileOffsetX,
Math.round((origin[1] - insertPoint[1]) * pixelScale / tileHeight) * Math.round((origin[1] - insertPoint[1]) * pixelScale / tileHeight) *
tileHeight + tileOffsetY - overlap, tileHeight + tileOffsetY, tileWidth, tileHeight);
tileWidth + 2 * overlap, tileHeight + 2 * overlap);
if (clipExtents) { if (clipExtents) {
renderContext.restore(); renderContext.restore();
} }
} }
renderContext.rotate(-rotation);
renderContext.translate(-offsetX, -offsetY);
if (renderContext != context) { if (renderContext != context) {
this.dispatchRenderEvent(renderContext, frameState, transform); this.dispatchRenderEvent(renderContext, frameState, transform);
context.drawImage(renderContext.canvas, 0, 0); context.drawImage(renderContext.canvas, 0, 0);

View File

@@ -106,6 +106,11 @@ ol.renderer.canvas.VectorLayer.prototype.composeFrame = function(frameState, lay
var alpha = replayContext.globalAlpha; var alpha = replayContext.globalAlpha;
replayContext.globalAlpha = layerState.opacity; replayContext.globalAlpha = layerState.opacity;
var width = frameState.size[0] * pixelRatio;
var height = frameState.size[1] * pixelRatio;
replayContext.translate(width / 2, height / 2);
replayContext.rotate(-rotation);
replayContext.translate(-width / 2, -height / 2);
replayGroup.replay(replayContext, pixelRatio, transform, rotation, replayGroup.replay(replayContext, pixelRatio, transform, rotation,
skippedFeatureUids); skippedFeatureUids);
if (vectorSource.getWrapX() && projection.canWrapX() && if (vectorSource.getWrapX() && projection.canWrapX() &&
@@ -135,6 +140,9 @@ ol.renderer.canvas.VectorLayer.prototype.composeFrame = function(frameState, lay
// restore original transform for render and compose events // restore original transform for render and compose events
transform = this.getTransform(frameState, 0); transform = this.getTransform(frameState, 0);
} }
replayContext.translate(width / 2, height / 2);
replayContext.rotate(rotation);
replayContext.translate(-width / 2, -height / 2);
if (replayContext != context) { if (replayContext != context) {
this.dispatchRenderEvent(replayContext, frameState, transform); this.dispatchRenderEvent(replayContext, frameState, transform);

View File

@@ -183,12 +183,9 @@ ol.renderer.canvas.VectorTileLayer.prototype.composeFrame = function(frameState,
0, 0, pixelScale, -pixelScale, 0, -center[0], -center[1]); 0, 0, pixelScale, -pixelScale, 0, -center[0], -center[1]);
insertPoint = ol.geom.flat.transform.transform2D( insertPoint = ol.geom.flat.transform.transform2D(
ol.extent.getTopLeft(tileExtent), 0, 1, 2, insertTransform); ol.extent.getTopLeft(tileExtent), 0, 1, 2, insertTransform);
replayContext.translate(offsetX, offsetY);
replayContext.rotate(rotation);
replayContext.drawImage(tileContext.canvas, replayContext.drawImage(tileContext.canvas,
Math.round(insertPoint[0]), Math.round(insertPoint[1])); Math.round(insertPoint[0] + offsetX),
replayContext.rotate(-rotation); Math.round(insertPoint[1]) + offsetY);
replayContext.translate(-offsetX, -offsetY);
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 615 B

After

Width:  |  Height:  |  Size: 753 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@@ -78,7 +78,7 @@ describe('ol.rendering.Map', function() {
map.getView().setRotation(90); map.getView().setRotation(90);
map.getView().setCenter([10, 10]); map.getView().setCenter([10, 10]);
expectResemble( expectResemble(
map, 'spec/ol/expected/rotate-canvas.png', IMAGE_TOLERANCE, done); map, 'spec/ol/expected/rotate-canvas.png', 2.8, done);
}); });
it('tests the WebGL renderer', function(done) { it('tests the WebGL renderer', function(done) {