diff --git a/src/ol/layer/Layer.js b/src/ol/layer/Layer.js index e495b18a04..d759df1acb 100644 --- a/src/ol/layer/Layer.js +++ b/src/ol/layer/Layer.js @@ -146,6 +146,12 @@ class Layer extends BaseLayer { */ this.renderer_ = null; + /** + * @protected + * @type {boolean} + */ + this.rendered = false; + // Overwrite default render method with a custom one if (options.render) { this.render = options.render; @@ -255,7 +261,7 @@ class Layer extends BaseLayer { * @return {Uint8ClampedArray|Uint8Array|Float32Array|DataView|null} Pixel data. */ getData(pixel) { - if (!this.renderer_) { + if (!this.renderer_ || !this.rendered) { return null; } return this.renderer_.getData(pixel); @@ -273,15 +279,26 @@ class Layer extends BaseLayer { const layerRenderer = this.getRenderer(); if (layerRenderer.prepareFrame(frameState)) { + this.rendered = true; return layerRenderer.renderFrame(frameState, target); } } + /** + * Called when a layer is not visible during a map render. + */ + unrender() { + this.rendered = false; + } + /** * For use inside the library only. * @param {import("../PluggableMap.js").default|null} map Map. */ setMapInternal(map) { + if (!map) { + this.unrender(); + } this.set(LayerProperty.MAP, map); } diff --git a/src/ol/layer/WebGLTile.js b/src/ol/layer/WebGLTile.js index 6cf7a9c90c..f8f1c9c481 100644 --- a/src/ol/layer/WebGLTile.js +++ b/src/ol/layer/WebGLTile.js @@ -440,6 +440,7 @@ class WebGLTileLayer extends BaseTileLayer { * @return {HTMLElement} The rendered element. */ render(frameState, target) { + this.rendered = true; const viewState = frameState.viewState; const sources = this.getSources(frameState.extent, viewState.resolution); let ready = true; diff --git a/src/ol/renderer/Composite.js b/src/ol/renderer/Composite.js index 00f4d37f8c..bcbfdca170 100644 --- a/src/ol/renderer/Composite.js +++ b/src/ol/renderer/Composite.js @@ -118,6 +118,7 @@ class CompositeMapRenderer extends MapRenderer { (sourceState != SourceState.READY && sourceState != SourceState.UNDEFINED) ) { + layer.unrender(); continue; } diff --git a/test/browser/spec/ol/layer/Layer.test.js b/test/browser/spec/ol/layer/Layer.test.js index 568c77665e..ead2ec4bc9 100644 --- a/test/browser/spec/ol/layer/Layer.test.js +++ b/test/browser/spec/ol/layer/Layer.test.js @@ -4,6 +4,9 @@ import Map from '../../../../../src/ol/Map.js'; import Property from '../../../../../src/ol/layer/Property.js'; import RenderEvent from '../../../../../src/ol/render/Event.js'; import Source from '../../../../../src/ol/source/Source.js'; +import TileLayer from '../../../../../src/ol/layer/Tile.js'; +import View from '../../../../../src/ol/View.js'; +import XYZ from '../../../../../src/ol/source/XYZ.js'; import {get as getProjection} from '../../../../../src/ol/proj.js'; describe('ol/layer/Layer', function () { @@ -619,6 +622,77 @@ describe('ol/layer/Layer', function () { }); }); + describe('unrender()', () => { + /** @type {Map} */ + let map; + + /** @type {TileLayer} */ + let layer; + + /** HTMLDivElement */ + let target; + + beforeEach((done) => { + target = document.createElement('div'); + target.style.width = '100px'; + target.style.height = '100px'; + document.body.appendChild(target); + + layer = new TileLayer({ + source: new XYZ({ + url: 'spec/ol/data/osm-0-0-0.png', + }), + }); + + map = new Map({ + target: target, + layers: [layer], + view: new View({ + center: [0, 0], + zoom: 0, + }), + }); + + map.once('rendercomplete', () => done()); + }); + + afterEach(() => { + map.setTarget(null); + document.body.removeChild(target); + }); + + it('is called when a layer goes from visible to not visible', () => { + const spy = sinon.spy(layer, 'unrender'); + map.renderSync(); + expect(spy.callCount).to.be(0); + + layer.setVisible(false); + map.renderSync(); + expect(spy.callCount).to.be(1); + }); + + it('is called when a layer is removed from the map', () => { + const spy = sinon.spy(layer, 'unrender'); + map.renderSync(); + expect(spy.callCount).to.be(0); + + map.removeLayer(layer); + map.renderSync(); + expect(spy.callCount).to.be(1); + }); + + it('is called when a layer goes out of range', () => { + const spy = sinon.spy(layer, 'unrender'); + map.renderSync(); + expect(spy.callCount).to.be(0); + + layer.setMaxZoom(3); + map.getView().setZoom(4); + map.renderSync(); + expect(spy.callCount).to.be(1); + }); + }); + describe('map property', () => { it('is set when a layer is added to a map', () => { const map = new Map({}); diff --git a/test/browser/spec/ol/layer/Tile.test.js b/test/browser/spec/ol/layer/Tile.test.js index 21fd6eb490..756f0028fb 100644 --- a/test/browser/spec/ol/layer/Tile.test.js +++ b/test/browser/spec/ol/layer/Tile.test.js @@ -69,6 +69,13 @@ describe('ol/layer/Tile', function () { expect(data[2]).to.be(208); expect(data[3]).to.be(255); }); + + it('gets pixel data', () => { + layer.setVisible(false); + map.renderSync(); + const data = layer.getData([50, 50]); + expect(data).to.be(null); + }); }); describe('frameState.animate after tile transition with layer opacity', function () { diff --git a/test/browser/spec/ol/layer/WebGLTile.test.js b/test/browser/spec/ol/layer/WebGLTile.test.js index 303f92691a..9290857a49 100644 --- a/test/browser/spec/ol/layer/WebGLTile.test.js +++ b/test/browser/spec/ol/layer/WebGLTile.test.js @@ -1,5 +1,4 @@ import DataTileSource from '../../../../../src/ol/source/DataTile.js'; -import GeoTIFF from '../../../../../src/ol/source/GeoTIFF.js'; import Map from '../../../../../src/ol/Map.js'; import View from '../../../../../src/ol/View.js'; import WebGLHelper from '../../../../../src/ol/webgl/Helper.js'; @@ -66,6 +65,10 @@ describe('ol/layer/WebGLTile', function () { document.body.appendChild(target); map = new Map({ target: target, + view: new View({ + center: [0, 0], + zoom: 0, + }), }); }); @@ -75,23 +78,26 @@ describe('ol/layer/WebGLTile', function () { }); it('retrieves pixel data', (done) => { - const source = new GeoTIFF({ - sources: [{url: 'spec/ol/source/images/0-0-0.tif'}], + const layer = new WebGLTileLayer({ + source: new DataTileSource({ + tilePixelRatio: 1 / 256, + loader(z, x, y) { + return new Uint8Array([5, 4, 3, 2, 1]); + }, + }), }); - const layer = new WebGLTileLayer({source: source}); - map.addLayer(layer); - map.setView(source.getView()); map.once('rendercomplete', () => { const data = layer.getData([50, 25]); expect(data).to.be.a(Uint8Array); - expect(data.length).to.be(4); - expect(data[0]).to.be(255); - expect(data[1]).to.be(189); - expect(data[2]).to.be(103); - expect(data[3]).to.be(255); + expect(data.length).to.be(5); + expect(data[0]).to.be(5); + expect(data[1]).to.be(4); + expect(data[2]).to.be(3); + expect(data[3]).to.be(2); + expect(data[4]).to.be(1); done(); }); }); @@ -107,12 +113,6 @@ describe('ol/layer/WebGLTile', function () { }); map.addLayer(layer); - map.setView( - new View({ - center: [0, 0], - zoom: 0, - }) - ); map.once('rendercomplete', () => { const data = layer.getData([50, 25]);