diff --git a/rendering/cases/layer-group/expected.png b/rendering/cases/layer-group/expected.png new file mode 100644 index 0000000000..ab2a151bc4 Binary files /dev/null and b/rendering/cases/layer-group/expected.png differ diff --git a/rendering/cases/layer-group/main.js b/rendering/cases/layer-group/main.js new file mode 100644 index 0000000000..a6fae1260e --- /dev/null +++ b/rendering/cases/layer-group/main.js @@ -0,0 +1,30 @@ +import Map from '../../../src/ol/Map.js'; +import View from '../../../src/ol/View.js'; +import {Group as LayerGroup, Tile as TileLayer} from '../../../src/ol/layer.js'; +import XYZ from '../../../src/ol/source/XYZ.js'; + +new Map({ + target: 'map', + view: new View({ + center: [0, 0], + zoom: 3 + }), + layers: new LayerGroup({ + opacity: 0.75, + layers: [ + new TileLayer({ + opacity: 0.25, + source: new XYZ({ + url: '/data/tiles/satellite/{z}/{x}/{y}.jpg' + }) + }), + new TileLayer({ + source: new XYZ({ + url: '/data/tiles/stamen-labels/{z}/{x}/{y}.png' + }) + }) + ] + }) +}); + +render(); diff --git a/src/ol/PluggableMap.js b/src/ol/PluggableMap.js index 9a3db53e73..d4c57c4d4a 100644 --- a/src/ol/PluggableMap.js +++ b/src/ol/PluggableMap.js @@ -43,6 +43,7 @@ import {create as createTransform, apply as applyTransform} from './transform.js * @property {import("./coordinate.js").Coordinate} focus * @property {number} index * @property {Array} layerStatesArray + * @property {number} layerIndex * @property {import("./transform.js").Transform} pixelToCoordinateTransform * @property {Array} postRenderFunctions * @property {import("./size.js").Size} size @@ -1231,13 +1232,14 @@ class PluggableMap extends BaseObject { if (size !== undefined && hasArea(size) && view && view.isDef()) { const viewHints = view.getHints(this.frameState_ ? this.frameState_.viewHints : undefined); viewState = view.getState(this.pixelRatio_); - frameState = /** @type {FrameState} */ ({ + frameState = { animate: false, coordinateToPixelTransform: this.coordinateToPixelTransform_, declutterItems: previousFrameState ? previousFrameState.declutterItems : [], extent: extent, focus: this.focus_ ? this.focus_ : viewState.center, index: this.frameIndex_++, + layerIndex: 0, layerStatesArray: this.getLayerGroup().getLayerStatesArray(), pixelRatio: this.pixelRatio_, pixelToCoordinateTransform: this.pixelToCoordinateTransform_, @@ -1250,7 +1252,7 @@ class PluggableMap extends BaseObject { viewState: viewState, viewHints: viewHints, wantedTiles: {} - }); + }; } if (frameState) { diff --git a/src/ol/layer/Base.js b/src/ol/layer/Base.js index 3754ba1049..994ee5544c 100644 --- a/src/ol/layer/Base.js +++ b/src/ol/layer/Base.js @@ -83,6 +83,9 @@ class BaseLayer extends BaseObject { } /** + * This method is not meant to be called by layers or layer renderers because the state + * is incorrect if the layer is included in a layer group. + * * @param {boolean=} opt_managed Layer is managed. * @return {import("./Layer.js").State} Layer state. */ diff --git a/src/ol/layer/Layer.js b/src/ol/layer/Layer.js index 424429cc26..a1dfe0aad5 100644 --- a/src/ol/layer/Layer.js +++ b/src/ol/layer/Layer.js @@ -195,9 +195,9 @@ class Layer extends BaseLayer { */ render(frameState, target) { const layerRenderer = this.getRenderer(); - const layerState = this.getLayerState(); - if (layerRenderer.prepareFrame(frameState, layerState)) { - return layerRenderer.renderFrame(frameState, layerState, target); + + if (layerRenderer.prepareFrame(frameState)) { + return layerRenderer.renderFrame(frameState, target); } } diff --git a/src/ol/renderer/Composite.js b/src/ol/renderer/Composite.js index 3934b5b14d..6d707a9706 100644 --- a/src/ol/renderer/Composite.js +++ b/src/ol/renderer/Composite.js @@ -90,6 +90,7 @@ class CompositeMapRenderer extends MapRenderer { let previousElement = null; for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) { const layerState = layerStatesArray[i]; + frameState.layerIndex = i; if (!visibleAtResolution(layerState, viewResolution) || (layerState.sourceState != SourceState.READY && layerState.sourceState != SourceState.UNDEFINED)) { continue; diff --git a/src/ol/renderer/Layer.js b/src/ol/renderer/Layer.js index 31c052354b..cde8519918 100644 --- a/src/ol/renderer/Layer.js +++ b/src/ol/renderer/Layer.js @@ -29,10 +29,9 @@ class LayerRenderer extends Observable { * Determine whether render should be called. * @abstract * @param {import("../PluggableMap.js").FrameState} frameState Frame state. - * @param {import("../layer/Layer.js").State} layerState Layer state. * @return {boolean} Layer is ready to be rendered. */ - prepareFrame(frameState, layerState) { + prepareFrame(frameState) { return abstract(); } @@ -40,11 +39,10 @@ class LayerRenderer extends Observable { * Render the layer. * @abstract * @param {import("../PluggableMap.js").FrameState} frameState Frame state. - * @param {import("../layer/Layer.js").State} layerState Layer state. * @param {HTMLElement} target Target that may be used to render content to. * @return {HTMLElement} The rendered element. */ - renderFrame(frameState, layerState, target) { + renderFrame(frameState, target) { return abstract(); } diff --git a/src/ol/renderer/canvas/ImageLayer.js b/src/ol/renderer/canvas/ImageLayer.js index bd40b16878..5d4c7b6cda 100644 --- a/src/ol/renderer/canvas/ImageLayer.js +++ b/src/ol/renderer/canvas/ImageLayer.js @@ -38,7 +38,8 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer { /** * @inheritDoc */ - prepareFrame(frameState, layerState) { + prepareFrame(frameState) { + const layerState = frameState.layerStatesArray[frameState.layerIndex]; const pixelRatio = frameState.pixelRatio; const viewState = frameState.viewState; const viewResolution = viewState.resolution; @@ -72,11 +73,12 @@ class CanvasImageLayerRenderer extends CanvasLayerRenderer { /** * @inheritDoc */ - renderFrame(frameState, layerState, target) { + renderFrame(frameState, target) { const image = this.image_; const imageExtent = image.getExtent(); const imageResolution = image.getResolution(); const imagePixelRatio = image.getPixelRatio(); + const layerState = frameState.layerStatesArray[frameState.layerIndex]; const pixelRatio = frameState.pixelRatio; const viewState = frameState.viewState; const viewCenter = viewState.center; diff --git a/src/ol/renderer/canvas/TileLayer.js b/src/ol/renderer/canvas/TileLayer.js index dd65150e9a..f005c466e2 100644 --- a/src/ol/renderer/canvas/TileLayer.js +++ b/src/ol/renderer/canvas/TileLayer.js @@ -125,7 +125,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer { /** * @inheritDoc */ - prepareFrame(frameState, layerState) { + prepareFrame(frameState) { return true; } @@ -137,7 +137,8 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer { * @inheritDoc * @returns {HTMLElement} The rendered element. */ - renderFrame(frameState, layerState, target) { + renderFrame(frameState, target) { + const layerState = frameState.layerStatesArray[frameState.layerIndex]; const viewState = frameState.viewState; const projection = viewState.projection; const viewResolution = viewState.resolution; diff --git a/src/ol/renderer/canvas/VectorImageLayer.js b/src/ol/renderer/canvas/VectorImageLayer.js index c4b6d98c27..6340b60abd 100644 --- a/src/ol/renderer/canvas/VectorImageLayer.js +++ b/src/ol/renderer/canvas/VectorImageLayer.js @@ -63,7 +63,7 @@ class CanvasVectorImageLayerRenderer extends CanvasImageLayerRenderer { /** * @inheritDoc */ - prepareFrame(frameState, layerState) { + prepareFrame(frameState) { const pixelRatio = frameState.pixelRatio; const viewState = frameState.viewState; const viewResolution = viewState.resolution; @@ -92,10 +92,10 @@ class CanvasVectorImageLayerRenderer extends CanvasImageLayerRenderer { })); const newSkippedFeatures = Object.keys(imageFrameState.skippedFeatureUids).sort(); const image = new ImageCanvas(renderedExtent, viewResolution, pixelRatio, context.canvas, function(callback) { - if (vectorRenderer.prepareFrame(imageFrameState, layerState) && + if (vectorRenderer.prepareFrame(imageFrameState) && (vectorRenderer.replayGroupChanged || !equals(skippedFeatures, newSkippedFeatures))) { - vectorRenderer.renderFrame(imageFrameState, layerState, null); + vectorRenderer.renderFrame(imageFrameState, null); renderDeclutterItems(imageFrameState, null); skippedFeatures = newSkippedFeatures; callback(); diff --git a/src/ol/renderer/canvas/VectorLayer.js b/src/ol/renderer/canvas/VectorLayer.js index 9246bf3c6c..538aa612b9 100644 --- a/src/ol/renderer/canvas/VectorLayer.js +++ b/src/ol/renderer/canvas/VectorLayer.js @@ -80,9 +80,10 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer { /** * @inheritDoc */ - renderFrame(frameState, layerState, target) { + renderFrame(frameState, target) { const pixelRatio = frameState.pixelRatio; + const layerState = frameState.layerStatesArray[frameState.layerIndex]; // set forward and inverse pixel transforms makeScale(this.pixelTransform_, 1 / pixelRatio, 1 / pixelRatio); @@ -232,7 +233,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer { /** * @inheritDoc */ - prepareFrame(frameState, layerState) { + prepareFrame(frameState) { const vectorLayer = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer()); const vectorSource = vectorLayer.getSource(); diff --git a/src/ol/renderer/canvas/VectorTileLayer.js b/src/ol/renderer/canvas/VectorTileLayer.js index e59bcbafa5..29e58b7f97 100644 --- a/src/ol/renderer/canvas/VectorTileLayer.js +++ b/src/ol/renderer/canvas/VectorTileLayer.js @@ -218,13 +218,13 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { /** * @inheritDoc */ - prepareFrame(frameState, layerState) { + prepareFrame(frameState) { const layerRevision = this.getLayer().getRevision(); if (this.renderedLayerRevision_ != layerRevision) { this.renderedTiles.length = 0; } this.renderedLayerRevision_ = layerRevision; - return super.prepareFrame(frameState, layerState); + return super.prepareFrame(frameState); } /** @@ -390,12 +390,12 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { /** * @inheritDoc */ - renderFrame(frameState, layerState, target) { + renderFrame(frameState, target) { const viewHints = frameState.viewHints; const hifi = !(viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]); this.renderQueuedTileImages_(hifi, frameState); - super.renderFrame(frameState, layerState, target); + super.renderFrame(frameState, target); const layer = /** @type {import("../../layer/VectorTile.js").default} */ (this.getLayer()); const renderMode = layer.getRenderMode(); @@ -495,6 +495,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { } } if (declutterReplays) { + const layerState = frameState.layerStatesArray[frameState.layerIndex]; replayDeclutter(declutterReplays, context, rotation, layerState.opacity, hifi, frameState.declutterItems); } diff --git a/src/ol/renderer/webgl/PointsLayer.js b/src/ol/renderer/webgl/PointsLayer.js index 759c40d538..6d79a7d6ba 100644 --- a/src/ol/renderer/webgl/PointsLayer.js +++ b/src/ol/renderer/webgl/PointsLayer.js @@ -284,7 +284,8 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { /** * @inheritDoc */ - renderFrame(frameState, layerState) { + renderFrame(frameState) { + const layerState = frameState.layerStatesArray[frameState.layerIndex]; this.helper_.drawElements(0, this.indicesBuffer_.getArray().length); this.helper_.finalizeDraw(frameState); const canvas = this.helper_.getCanvas(); diff --git a/src/ol/source/Raster.js b/src/ol/source/Raster.js index 9759b66dbb..75a81ec633 100644 --- a/src/ol/source/Raster.js +++ b/src/ol/source/Raster.js @@ -214,6 +214,7 @@ class RasterSource extends ImageSource { extent: null, focus: null, index: 0, + layerIndex: 0, layerStatesArray: getLayerStatesArray(this.layers_), pixelRatio: 1, pixelToCoordinateTransform: createTransform(), @@ -358,7 +359,8 @@ class RasterSource extends ImageSource { const len = this.layers_.length; const imageDatas = new Array(len); for (let i = 0; i < len; ++i) { - const imageData = getImageData(this.layers_[i], frameState, frameState.layerStatesArray[i]); + frameState.layerIndex = i; + const imageData = getImageData(this.layers_[i], frameState); if (imageData) { imageDatas[i] = imageData; } else { @@ -430,21 +432,20 @@ let sharedContext = null; * Get image data from a layer. * @param {import("../layer/Layer.js").default} layer Layer to render. * @param {import("../PluggableMap.js").FrameState} frameState The frame state. - * @param {import("../layer/Layer.js").State} layerState The layer state. * @return {ImageData} The image data. */ -function getImageData(layer, frameState, layerState) { +function getImageData(layer, frameState) { const renderer = layer.getRenderer(); if (!renderer) { throw new Error('Unsupported layer type: ' + layer); } - if (!renderer.prepareFrame(frameState, layerState)) { + if (!renderer.prepareFrame(frameState)) { return null; } const width = frameState.size[0]; const height = frameState.size[1]; - const container = renderer.renderFrame(frameState, layerState, null); + const container = renderer.renderFrame(frameState, null); let element; if (container) { element = container.firstElementChild; diff --git a/test/spec/ol/renderer/canvas/vectorimage.test.js b/test/spec/ol/renderer/canvas/vectorimage.test.js index a3a0b48593..c9467ab2f2 100644 --- a/test/spec/ol/renderer/canvas/vectorimage.test.js +++ b/test/spec/ol/renderer/canvas/vectorimage.test.js @@ -35,6 +35,8 @@ describe('ol/renderer/canvas/VectorImageLayer', function() { projExtent[0] - 10000, -10000, projExtent[0] + 10000, 10000 ]; const frameState = { + layerStatesArray: [layer.getLayerState()], + layerIndex: 0, extent: extent, skippedFeatureUids: {}, viewHints: [], @@ -44,7 +46,7 @@ describe('ol/renderer/canvas/VectorImageLayer', function() { rotation: 0 } }; - renderer.prepareFrame(frameState, {}); + renderer.prepareFrame(frameState); const expected = renderer.image_.getExtent(); scaleFromCenter(extent, 2); diff --git a/test/spec/ol/renderer/canvas/vectorlayer.test.js b/test/spec/ol/renderer/canvas/vectorlayer.test.js index b2ea559139..a3921a8539 100644 --- a/test/spec/ol/renderer/canvas/vectorlayer.test.js +++ b/test/spec/ol/renderer/canvas/vectorlayer.test.js @@ -243,7 +243,7 @@ describe('ol.renderer.canvas.VectorLayer', function() { frameState.extent = [projExtent[0] - 10000, -10000, projExtent[0] + 10000, 10000]; - renderer.prepareFrame(frameState, {}); + renderer.prepareFrame(frameState); expect(renderer.replayGroup_.maxExtent_).to.eql(bufferExtent([ projExtent[0] - worldWidth + buffer, -10000, projExtent[2] + worldWidth - buffer, 10000 @@ -255,7 +255,7 @@ describe('ol.renderer.canvas.VectorLayer', function() { frameState.extent = [projExtent[0] - 10000, -10000, projExtent[1] - 10000, 10000]; - renderer.prepareFrame(frameState, {}); + renderer.prepareFrame(frameState); expect(renderer.replayGroup_.maxExtent_).to.eql(bufferExtent([ projExtent[0] - worldWidth + buffer, -10000, projExtent[2] + worldWidth - buffer, 10000 @@ -266,7 +266,7 @@ describe('ol.renderer.canvas.VectorLayer', function() { frameState.extent = [2 * projExtent[0] - 10000, -10000, 2 * projExtent[1] + 10000, 10000]; - renderer.prepareFrame(frameState, {}); + renderer.prepareFrame(frameState); expect(renderer.replayGroup_.maxExtent_).to.eql(bufferExtent([ projExtent[0] - worldWidth + buffer, -10000, projExtent[2] + worldWidth - buffer, 10000 @@ -279,7 +279,7 @@ describe('ol.renderer.canvas.VectorLayer', function() { projExtent[0] - 2 * worldWidth - 10000, -10000, projExtent[1] + 2 * worldWidth + 10000, 10000 ]; - renderer.prepareFrame(frameState, {}); + renderer.prepareFrame(frameState); expect(renderer.replayGroup_.maxExtent_).to.eql(bufferExtent([ projExtent[0] - 2 * worldWidth - 10000, -10000, projExtent[2] + 2 * worldWidth + 10000, 10000 @@ -288,9 +288,9 @@ describe('ol.renderer.canvas.VectorLayer', function() { it('sets replayGroupChanged correctly', function() { frameState.extent = [-10000, -10000, 10000, 10000]; - renderer.prepareFrame(frameState, {}); + renderer.prepareFrame(frameState); expect(renderer.replayGroupChanged).to.be(true); - renderer.prepareFrame(frameState, {}); + renderer.prepareFrame(frameState); expect(renderer.replayGroupChanged).to.be(false); }); @@ -301,13 +301,15 @@ describe('ol.renderer.canvas.VectorLayer', function() { expect(true); done(); }); + frameState.layerStatesArray = [layer.getLayerState()]; + frameState.layerIndex = 0; frameState.extent = [-10000, -10000, 10000, 10000]; frameState.size = [100, 100]; frameState.viewState.center = [0, 0]; let rendered = false; - if (renderer.prepareFrame(frameState, {})) { + if (renderer.prepareFrame(frameState)) { rendered = true; - renderer.renderFrame(frameState, layer.getLayerState(), null); + renderer.renderFrame(frameState, null); } expect(rendered).to.be(true); }); diff --git a/test/spec/ol/renderer/canvas/vectortilelayer.test.js b/test/spec/ol/renderer/canvas/vectortilelayer.test.js index 9efe8fe0d1..7c49141c4b 100644 --- a/test/spec/ol/renderer/canvas/vectortilelayer.test.js +++ b/test/spec/ol/renderer/canvas/vectortilelayer.test.js @@ -234,6 +234,8 @@ describe('ol.renderer.canvas.VectorTileLayer', function() { }; const proj = getProjection('EPSG:3857'); const frameState = { + layerStatesArray: [layer.getLayerState()], + layerIndex: 0, extent: proj.getExtent(), pixelRatio: 1, time: Date.now(), @@ -247,13 +249,13 @@ describe('ol.renderer.canvas.VectorTileLayer', function() { usedTiles: {}, wantedTiles: {} }; - renderer.renderFrame(frameState, {}); + renderer.renderFrame(frameState); const replayState = renderer.renderedTiles[0].getReplayState(layer); const revision = replayState.renderedTileRevision; - renderer.renderFrame(frameState, {}, null); + renderer.renderFrame(frameState, null); expect(replayState.renderedTileRevision).to.be(revision); layer.changed(); - renderer.renderFrame(frameState, {}, null); + renderer.renderFrame(frameState, null); expect(replayState.renderedTileRevision).to.be(revision + 1); expect(Object.keys(renderer.tileListenerKeys_).length).to.be(0); });