diff --git a/examples/layer-extent.html b/examples/layer-extent.html index 52ec7210ef..74169f8cd9 100644 --- a/examples/layer-extent.html +++ b/examples/layer-extent.html @@ -4,13 +4,12 @@ title: Limited Layer Extent shortdesc: Restricting layer rendering to a limited extent. docs: > This example uses the layer.setExtent() method to - modify the extent of the overlay layer. Use the controls below - to limit rendering based on an extent. + modify the extent of the overlay layer. Use the controls above + to limit rendering based on an extent (approximate country bounds). tags: "extent, tilejson" ---
- - - - - + + + + diff --git a/examples/layer-extent.js b/examples/layer-extent.js index 474469d4da..4e46ca1daf 100644 --- a/examples/layer-extent.js +++ b/examples/layer-extent.js @@ -3,17 +3,17 @@ goog.require('ol.View'); goog.require('ol.layer.Tile'); goog.require('ol.proj'); goog.require('ol.source.TileJSON'); +goog.require('ol.interaction.DragRotateAndZoom'); function transform(extent) { return ol.proj.transformExtent(extent, 'EPSG:4326', 'EPSG:3857'); } var extents = { - northwest: transform([-180, 0, 0, 85]), - northeast: transform([0, 0, 180, 85]), - southeast: transform([0, -85, 180, 0]), - southwest: transform([-180, -85, 0, 0]), - world: transform([-180, -85, 180, 85]) + India: transform([68.17665, 7.96553, 97.40256, 35.49401]), + Argentina: transform([-73.41544, -55.25, -53.62835, -21.83231]), + Nigeria: transform([2.6917, 4.24059, 14.57718, 13.86592]), + Sweden: transform([11.02737, 55.36174, 23.90338, 69.10625]) }; var base = new ol.layer.Tile({ @@ -25,7 +25,7 @@ var base = new ol.layer.Tile({ }); var overlay = new ol.layer.Tile({ - extent: extents.northwest, + extent: extents.India, source: new ol.source.TileJSON({ url: 'http://api.tiles.mapbox.com/v3/' + 'mapbox.world-glass.json', @@ -43,6 +43,8 @@ var map = new ol.Map({ }) }); +map.addInteraction(new ol.interaction.DragRotateAndZoom()); + for (var key in extents) { document.getElementById(key).onclick = function(event) { overlay.setExtent(extents[event.target.id]); diff --git a/src/ol/renderer/canvas/canvastilelayerrenderer.js b/src/ol/renderer/canvas/canvastilelayerrenderer.js index feaed6bffb..1d7f2c812b 100644 --- a/src/ol/renderer/canvas/canvastilelayerrenderer.js +++ b/src/ol/renderer/canvas/canvastilelayerrenderer.js @@ -276,6 +276,41 @@ ol.renderer.canvas.TileLayer.prototype.renderTileImages = function(context, fram tilesToDraw.reverse(); pixelExtents = []; } + + var extent = layerState.extent; + var clipped = extent !== undefined; + if (clipped) { + goog.asserts.assert(extent !== undefined, + 'layerState extent is defined'); + var topLeft = ol.extent.getTopLeft(extent); + var topRight = ol.extent.getTopRight(extent); + var bottomRight = ol.extent.getBottomRight(extent); + var bottomLeft = ol.extent.getBottomLeft(extent); + + ol.vec.Mat4.multVec2(frameState.coordinateToPixelMatrix, + topLeft, topLeft); + ol.vec.Mat4.multVec2(frameState.coordinateToPixelMatrix, + topRight, topRight); + ol.vec.Mat4.multVec2(frameState.coordinateToPixelMatrix, + bottomRight, bottomRight); + ol.vec.Mat4.multVec2(frameState.coordinateToPixelMatrix, + bottomLeft, bottomLeft); + + var ox = drawOffsetX || 0; + var oy = drawOffsetY || 0; + renderContext.save(); + var cx = (renderContext.canvas.width * pixelRatio) / 2; + var cy = (renderContext.canvas.height * pixelRatio) / 2; + ol.render.canvas.rotateAtOffset(renderContext, -rotation, cx, cy); + renderContext.beginPath(); + renderContext.moveTo(topLeft[0] * pixelRatio + ox, topLeft[1] * pixelRatio + oy); + renderContext.lineTo(topRight[0] * pixelRatio + ox, topRight[1] * pixelRatio + oy); + renderContext.lineTo(bottomRight[0] * pixelRatio + ox, bottomRight[1] * pixelRatio + oy); + renderContext.lineTo(bottomLeft[0] * pixelRatio + ox, bottomLeft[1] * pixelRatio + oy); + renderContext.clip(); + ol.render.canvas.rotateAtOffset(renderContext, rotation, cx, cy); + } + for (var i = 0, ii = tilesToDraw.length; i < ii; ++i) { var tile = tilesToDraw[i]; var tileCoord = tile.getTileCoord(); @@ -324,6 +359,10 @@ ol.renderer.canvas.TileLayer.prototype.renderTileImages = function(context, fram } } + if (clipped) { + renderContext.restore(); + } + if (hasRenderListeners) { var dX = drawOffsetX - offsetX / drawScale + offsetX; var dY = drawOffsetY - offsetY / drawScale + offsetY; diff --git a/test_rendering/spec/ol/layer/expected/2-layers-canvas-extent-rotate.png b/test_rendering/spec/ol/layer/expected/2-layers-canvas-extent-rotate.png new file mode 100644 index 0000000000..806e331357 Binary files /dev/null and b/test_rendering/spec/ol/layer/expected/2-layers-canvas-extent-rotate.png differ diff --git a/test_rendering/spec/ol/layer/expected/2-layers-canvas-extent.png b/test_rendering/spec/ol/layer/expected/2-layers-canvas-extent.png new file mode 100644 index 0000000000..2ec76c289f Binary files /dev/null and b/test_rendering/spec/ol/layer/expected/2-layers-canvas-extent.png differ diff --git a/test_rendering/spec/ol/layer/tile.test.js b/test_rendering/spec/ol/layer/tile.test.js index de14eaac10..25eb53b874 100644 --- a/test_rendering/spec/ol/layer/tile.test.js +++ b/test_rendering/spec/ol/layer/tile.test.js @@ -29,7 +29,7 @@ describe('ol.rendering.layer.Tile', function() { } }; - sources.forEach(function(source) { + sources.forEach(function(source, i) { source.on('tileloadstart', function(event) { tilesLoading++; }); @@ -44,7 +44,7 @@ describe('ol.rendering.layer.Tile', function() { var options = { source: source }; - ol.object.assign(options, layerOptions); + ol.object.assign(options, layerOptions[i] || layerOptions); map.addLayer(new ol.layer.Tile(options)); }); } @@ -112,6 +112,30 @@ describe('ol.rendering.layer.Tile', function() { IMAGE_TOLERANCE, done); }); }); + + function centerExtent(map) { + var c = map.getView().calculateExtent(map.getSize()); + var qw = ol.extent.getSize(c)[0] / 4; + var qh = ol.extent.getSize(c)[1] / 4; + return [c[0] + qw, c[1] + qh, c[2] - qw, c[3] - qh]; + } + + it('tests canvas layer extent clipping', function(done) { + map = createMap('canvas'); + waitForTiles([source1, source2], [{}, {extent: centerExtent(map)}], function() { + expectResemble(map, 'spec/ol/layer/expected/2-layers-canvas-extent.png', + IMAGE_TOLERANCE, done); + }); + }); + + it('tests canvas layer extent clipping with rotation', function(done) { + map = createMap('canvas'); + map.getView().setRotation(Math.PI / 2); + waitForTiles([source1, source2], [{}, {extent: centerExtent(map)}], function() { + expectResemble(map, 'spec/ol/layer/expected/2-layers-canvas-extent-rotate.png', + IMAGE_TOLERANCE, done); + }); + }); }); describe('tile layer with opacity', function() {