diff --git a/src/ol/renderer/webgl/TileLayer.js b/src/ol/renderer/webgl/TileLayer.js index 1e3ad1ed14..16b3c3309e 100644 --- a/src/ol/renderer/webgl/TileLayer.js +++ b/src/ol/renderer/webgl/TileLayer.js @@ -97,6 +97,14 @@ function getRenderExtent(frameState, extent) { fromUserExtent(layerState.extent, frameState.viewState.projection) ); } + const source = + /** {import("../../source/Tile.js").default} */ layerState.layer.getSource(); + if (!source.getWrapX()) { + const gridExtent = source.tileGrid.getExtent(); + if (gridExtent) { + extent = getIntersection(extent, gridExtent); + } + } return extent; } @@ -236,11 +244,13 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer { * @return {boolean} Layer is ready to be rendered. */ prepareFrameInternal(frameState) { - if (isEmpty(getRenderExtent(frameState, frameState.extent))) { + const layer = this.getLayer(); + const source = layer.getSource(); + if (!source) { return false; } - const source = this.getLayer().getSource(); - if (!source) { + + if (isEmpty(getRenderExtent(frameState, frameState.extent))) { return false; } return source.getState() === State.READY; @@ -248,7 +258,7 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer { /** * @param {import("../../PluggableMap.js").FrameState} frameState Frame state. - * @param {import("../../extent.js").Extent} extent The extent. + * @param {import("../../extent.js").Extent} extent The extent to be rendered. * @param {number} z The zoom level. * @param {Object>} tileTexturesByZ The zoom level. */ @@ -341,10 +351,10 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer { this.preRender(gl, frameState); const viewState = frameState.viewState; - const extent = getRenderExtent(frameState, frameState.extent); const tileLayer = this.getLayer(); const tileSource = tileLayer.getSource(); const tileGrid = tileSource.getTileGridForProjection(viewState.projection); + const extent = getRenderExtent(frameState, frameState.extent); const z = tileGrid.getZForResolution( viewState.resolution, tileSource.zDirection diff --git a/src/ol/source/DataTile.js b/src/ol/source/DataTile.js index 903d3c0b35..5b3cf452c1 100644 --- a/src/ol/source/DataTile.js +++ b/src/ol/source/DataTile.js @@ -31,7 +31,7 @@ import {toPromise} from '../functions.js'; * @property {boolean} [opaque=false] Whether the layer is opaque. * @property {import("./State.js").default} [state] The source state. * @property {number} [tilePixelRatio] Tile pixel ratio. - * @property {boolean} [wrapX=true] Render tiles beyond the antimeridian. + * @property {boolean} [wrapX=false] Render tiles beyond the antimeridian. * @property {number} [transition] Transition time when fading in new tiles (in miliseconds). * @property {number} [bandCount=4] Number of bands represented in the data. */ diff --git a/src/ol/source/GeoTIFF.js b/src/ol/source/GeoTIFF.js index 784cc28852..88b81a6657 100644 --- a/src/ol/source/GeoTIFF.js +++ b/src/ol/source/GeoTIFF.js @@ -300,6 +300,7 @@ function getMaxForDataType(array) { * @property {boolean} [opaque=false] Whether the layer is opaque. * @property {number} [transition=250] Duration of the opacity transition for rendering. * To disable the opacity transition, pass `transition: 0`. + * @property {boolean} [wrapX=false] Render tiles beyond the tile grid extent. */ /** @@ -318,6 +319,7 @@ class GeoTIFFSource extends DataTile { projection: null, opaque: options.opaque, transition: options.transition, + wrapX: options.wrapX, }); /** diff --git a/src/ol/source/Tile.js b/src/ol/source/Tile.js index 8d9b16207f..03a8038b51 100644 --- a/src/ol/source/Tile.js +++ b/src/ol/source/Tile.js @@ -34,7 +34,7 @@ import {scale as scaleSize, toSize} from '../size.js'; * @property {import("../proj.js").ProjectionLike} [projection] Projection. * @property {import("./State.js").default} [state] State. * @property {import("../tilegrid/TileGrid.js").default} [tileGrid] TileGrid. - * @property {boolean} [wrapX=true] WrapX. + * @property {boolean} [wrapX=false] WrapX. * @property {number} [transition] Transition. * @property {string} [key] Key. * @property {number|import("../array.js").NearestDirectionFunction} [zDirection=0] ZDirection. diff --git a/test/browser/spec/ol/source/GeoTIFF.test.js b/test/browser/spec/ol/source/GeoTIFF.test.js index 2ad410dacf..778f965956 100644 --- a/test/browser/spec/ol/source/GeoTIFF.test.js +++ b/test/browser/spec/ol/source/GeoTIFF.test.js @@ -27,6 +27,29 @@ describe('ol/source/GeoTIFF', function () { expect(source.readMethod_).to.be('readRGB'); }); + it('defaults to wrapX: false', function () { + const source = new GeoTIFFSource({ + sources: [ + { + url: 'spec/ol/source/images/0-0-0.tif', + }, + ], + }); + expect(source.getWrapX()).to.be(false); + }); + + it('allows wrapX to be set', function () { + const source = new GeoTIFFSource({ + wrapX: true, + sources: [ + { + url: 'spec/ol/source/images/0-0-0.tif', + }, + ], + }); + expect(source.getWrapX()).to.be(true); + }); + it('generates Float32Array data if normalize is set to false', (done) => { const source = new GeoTIFFSource({ normalize: false, diff --git a/test/browser/spec/ol/webgl/TileTexture.test.js b/test/browser/spec/ol/webgl/TileTexture.test.js index 3ad9a1bb19..e454a3c53c 100644 --- a/test/browser/spec/ol/webgl/TileTexture.test.js +++ b/test/browser/spec/ol/webgl/TileTexture.test.js @@ -7,6 +7,7 @@ import WebGLArrayBuffer from '../../../../../src/ol/webgl/Buffer.js'; import WebGLTileLayer from '../../../../../src/ol/layer/WebGLTile.js'; import {EXTENT as EPSG3857_EXTENT} from '../../../../../src/ol/proj/epsg3857.js'; import {createCanvasContext2D} from '../../../../../src/ol/dom.js'; +import {get as getProjection} from '../../../../../src/ol/proj.js'; describe('ol/webgl/TileTexture', function () { /** @type {TileTexture} */ @@ -31,6 +32,9 @@ describe('ol/webgl/TileTexture', function () { renderer = layer.createRenderer(); renderer.prepareFrame({ + viewState: { + projection: getProjection('EPSG:3857'), + }, extent: EPSG3857_EXTENT, layerIndex: 0, layerStatesArray: [layer.getLayerState()], diff --git a/test/rendering/cases/webgl-data-tile-no-wrap/expected.png b/test/rendering/cases/webgl-data-tile-no-wrap/expected.png new file mode 100644 index 0000000000..df2b332db1 Binary files /dev/null and b/test/rendering/cases/webgl-data-tile-no-wrap/expected.png differ diff --git a/test/rendering/cases/webgl-data-tile-no-wrap/main.js b/test/rendering/cases/webgl-data-tile-no-wrap/main.js new file mode 100644 index 0000000000..3ea6865880 --- /dev/null +++ b/test/rendering/cases/webgl-data-tile-no-wrap/main.js @@ -0,0 +1,63 @@ +import DataTile from '../../../../src/ol/source/DataTile.js'; +import Map from '../../../../src/ol/Map.js'; +import TileLayer from '../../../../src/ol/layer/WebGLTile.js'; +import View from '../../../../src/ol/View.js'; +import XYZ from '../../../../src/ol/source/XYZ.js'; + +const labelCanvasSize = 256; + +const labelCanvas = document.createElement('canvas'); +labelCanvas.width = labelCanvasSize; +labelCanvas.height = labelCanvasSize; + +const labelContext = labelCanvas.getContext('2d'); +labelContext.textAlign = 'center'; +labelContext.font = '16px sans-serif'; +const labelLineHeight = 16; + +new Map({ + layers: [ + new TileLayer({ + source: new XYZ({ + url: '/data/tiles/satellite/{z}/{x}/{y}.jpg', + transition: 0, + }), + }), + new TileLayer({ + source: new DataTile({ + loader: function (z, x, y) { + const half = labelCanvasSize / 2; + + labelContext.clearRect(0, 0, labelCanvasSize, labelCanvasSize); + + labelContext.fillStyle = 'white'; + labelContext.fillText(`z: ${z}`, half, half - labelLineHeight); + labelContext.fillText(`x: ${x}`, half, half); + labelContext.fillText(`y: ${y}`, half, half + labelLineHeight); + + labelContext.strokeStyle = 'white'; + labelContext.lineWidth = 2; + labelContext.strokeRect(0, 0, labelCanvasSize, labelCanvasSize); + + const data = labelContext.getImageData( + 0, + 0, + labelCanvasSize, + labelCanvasSize + ).data; + return new Uint8Array(data.buffer); + }, + transition: 0, + }), + }), + ], + target: 'map', + view: new View({ + center: [15700000, 2700000], + zoom: 2, + }), +}); + +render({ + message: 'data tiles outside the world are not rendered', +}); diff --git a/test/rendering/cases/webgl-mixed-layers/main.js b/test/rendering/cases/webgl-mixed-layers/main.js index 31f4e24f19..d35267d0b0 100644 --- a/test/rendering/cases/webgl-mixed-layers/main.js +++ b/test/rendering/cases/webgl-mixed-layers/main.js @@ -46,6 +46,7 @@ new Map({ }), new TileLayer({ source: new DataTile({ + wrapX: true, loader: function (z, x, y) { const half = labelCanvasSize / 2; diff --git a/test/rendering/cases/webgl-multiple-layers/main.js b/test/rendering/cases/webgl-multiple-layers/main.js index d0021531e8..b7336d6f1d 100644 --- a/test/rendering/cases/webgl-multiple-layers/main.js +++ b/test/rendering/cases/webgl-multiple-layers/main.js @@ -44,6 +44,7 @@ new Map({ }), new TileLayer({ source: new DataTile({ + wrapX: true, loader: function (z, x, y) { const half = labelCanvasSize / 2;