diff --git a/src/ol/DataTile.js b/src/ol/DataTile.js index 8f40b3fbb8..ecd9fc6509 100644 --- a/src/ol/DataTile.js +++ b/src/ol/DataTile.js @@ -13,9 +13,11 @@ import TileState from './TileState.js'; /** * @typedef {Object} Options * @property {import("./tilecoord.js").TileCoord} tileCoord Tile coordinate. - * @property {function() : Promise} loader Data loader. + * @property {function(): Promise} loader Data loader. * @property {number} [transition=250] A duration for tile opacity * transitions in milliseconds. A duration of 0 disables the opacity transition. + * @property {boolean} [interpolate=false] Use interpolated values when resampling. By default, + * the nearest neighbor is used when resampling. * @api */ @@ -26,10 +28,27 @@ class DataTile extends Tile { constructor(options) { const state = TileState.IDLE; - super(options.tileCoord, state, {transition: options.transition}); + super(options.tileCoord, state, { + transition: options.transition, + interpolate: options.interpolate, + }); + /** + * @type {function(): Promise} + * @private + */ this.loader_ = options.loader; + + /** + * @type {Data} + * @private + */ this.data_ = null; + + /** + * @type {Error} + * @private + */ this.error_ = null; } diff --git a/src/ol/Tile.js b/src/ol/Tile.js index e9d78edcac..feb1097f3c 100644 --- a/src/ol/Tile.js +++ b/src/ol/Tile.js @@ -63,6 +63,8 @@ import {easeIn} from './easing.js'; * @typedef {Object} Options * @property {number} [transition=250] A duration for tile opacity * transitions in milliseconds. A duration of 0 disables the opacity transition. + * @property {boolean} [interpolate=false] Use interpolated values when resampling. By default, + * the nearest neighbor is used when resampling. * @api */ @@ -123,6 +125,11 @@ class Tile extends EventTarget { * @type {Object} */ this.transitionStarts_ = {}; + + /** + * @type {boolean} + */ + this.interpolate = !!options.interpolate; } /** diff --git a/src/ol/source/DataTile.js b/src/ol/source/DataTile.js index 5b3cf452c1..cd96405ad6 100644 --- a/src/ol/source/DataTile.js +++ b/src/ol/source/DataTile.js @@ -34,6 +34,8 @@ import {toPromise} from '../functions.js'; * @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. + * @property {boolean} [interpolate=false] Use interpolated values when resampling. By default, + * the nearest neighbor is used when resampling. */ /** @@ -71,6 +73,7 @@ class DataTileSource extends TileSource { tilePixelRatio: options.tilePixelRatio, wrapX: options.wrapX, transition: options.transition, + interpolate: options.interpolate, }); /** @@ -90,6 +93,12 @@ class DataTileSource extends TileSource { * @type {number} */ this.bandCount = options.bandCount === undefined ? 4 : options.bandCount; // assume RGBA if undefined + + /** + * @type {boolean} + * @private + */ + this.interpolate_ = !!options.interpolate; } /** diff --git a/src/ol/source/GeoTIFF.js b/src/ol/source/GeoTIFF.js index aca900f779..99a733b7cd 100644 --- a/src/ol/source/GeoTIFF.js +++ b/src/ol/source/GeoTIFF.js @@ -315,6 +315,8 @@ function getMaxForDataType(array) { * @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. + * @property {boolean} [interpolate=true] Use interpolated values when resampling. By default, + * the linear interpolation is used to resample the data. If false, nearest neighbor is used. */ /** @@ -333,6 +335,7 @@ class GeoTIFFSource extends DataTile { projection: null, opaque: options.opaque, transition: options.transition, + interpolate: options.interpolate !== false, wrapX: options.wrapX, }); diff --git a/src/ol/source/Tile.js b/src/ol/source/Tile.js index 03a8038b51..cb1e8c0dfa 100644 --- a/src/ol/source/Tile.js +++ b/src/ol/source/Tile.js @@ -38,6 +38,8 @@ import {scale as scaleSize, toSize} from '../size.js'; * @property {number} [transition] Transition. * @property {string} [key] Key. * @property {number|import("../array.js").NearestDirectionFunction} [zDirection=0] ZDirection. + * @property {boolean} [interpolate=false] Use interpolated values when resampling. By default, + * the nearest neighbor is used when resampling. */ /** @@ -122,7 +124,10 @@ class TileSource extends Source { * @protected * @type {import("../Tile.js").Options} */ - this.tileOptions = {transition: options.transition}; + this.tileOptions = { + transition: options.transition, + interpolate: options.interpolate, + }; /** * zDirection hint, read by the renderer. Indicates which resolution should be used diff --git a/src/ol/source/TileImage.js b/src/ol/source/TileImage.js index 40beffa40f..b316134b79 100644 --- a/src/ol/source/TileImage.js +++ b/src/ol/source/TileImage.js @@ -86,6 +86,7 @@ class TileImage extends UrlTile { urls: options.urls, wrapX: options.wrapX, transition: options.transition, + interpolate: options.imageSmoothing !== false, key: options.key, attributionsCollapsible: options.attributionsCollapsible, zDirection: options.zDirection, diff --git a/src/ol/source/UrlTile.js b/src/ol/source/UrlTile.js index db86b77038..1eae6da345 100644 --- a/src/ol/source/UrlTile.js +++ b/src/ol/source/UrlTile.js @@ -26,6 +26,8 @@ import {getUid} from '../util.js'; * @property {number} [transition] Transition. * @property {string} [key] Key. * @property {number|import("../array.js").NearestDirectionFunction} [zDirection=0] ZDirection. + * @property {boolean} [interpolate=false] Use interpolated values when resampling. By default, + * the nearest neighbor is used when resampling. */ /** @@ -49,6 +51,7 @@ class UrlTile extends TileSource { tilePixelRatio: options.tilePixelRatio, wrapX: options.wrapX, transition: options.transition, + interpolate: options.interpolate, key: options.key, attributionsCollapsible: options.attributionsCollapsible, zDirection: options.zDirection, diff --git a/src/ol/webgl/TileTexture.js b/src/ol/webgl/TileTexture.js index d67a804d9b..a9a5261382 100644 --- a/src/ol/webgl/TileTexture.js +++ b/src/ol/webgl/TileTexture.js @@ -11,21 +11,28 @@ import WebGLArrayBuffer from './Buffer.js'; import {ARRAY_BUFFER, STATIC_DRAW} from '../webgl.js'; import {toSize} from '../size.js'; -function bindAndConfigure(gl, texture) { +/** + * @param {WebGLRenderingContext} gl The WebGL context. + * @param {WebGLTexture} texture The texture. + * @param {boolean} interpolate Interpolate when resampling. + */ +function bindAndConfigure(gl, texture, interpolate) { + const resampleFilter = interpolate ? gl.LINEAR : gl.NEAREST; gl.bindTexture(gl.TEXTURE_2D, texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, resampleFilter); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, resampleFilter); } /** * @param {WebGLRenderingContext} gl The WebGL context. * @param {WebGLTexture} texture The texture. * @param {HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} image The image. + * @param {boolean} interpolate Interpolate when resampling. */ -function uploadImageTexture(gl, texture, image) { - bindAndConfigure(gl, texture); +function uploadImageTexture(gl, texture, image, interpolate) { + bindAndConfigure(gl, texture, interpolate); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); } @@ -36,10 +43,18 @@ function uploadImageTexture(gl, texture, image) { * @param {import("../DataTile.js").Data} data The pixel data. * @param {import("../size.js").Size} size The pixel size. * @param {number} bandCount The band count. + * @param {boolean} interpolate Interpolate when resampling. */ -function uploadDataTexture(helper, texture, data, size, bandCount) { +function uploadDataTexture( + helper, + texture, + data, + size, + bandCount, + interpolate +) { const gl = helper.getGL(); - bindAndConfigure(gl, texture); + bindAndConfigure(gl, texture, interpolate); const bytesPerRow = data.byteLength / size[1]; let unpackAlignment = 1; @@ -174,7 +189,7 @@ class TileTexture extends EventTarget { const texture = gl.createTexture(); this.textures.push(texture); this.bandCount = 4; - uploadImageTexture(gl, texture, tile.getImage()); + uploadImageTexture(gl, texture, tile.getImage(), tile.interpolate); return; } @@ -191,7 +206,14 @@ class TileTexture extends EventTarget { if (textureCount === 1) { const texture = gl.createTexture(); this.textures.push(texture); - uploadDataTexture(helper, texture, data, this.size, this.bandCount); + uploadDataTexture( + helper, + texture, + data, + this.size, + this.bandCount, + tile.interpolate + ); return; } @@ -229,7 +251,14 @@ class TileTexture extends EventTarget { const texture = this.textures[textureIndex]; const textureData = textureDataArrays[textureIndex]; const bandCount = textureData.length / pixelCount; - uploadDataTexture(helper, texture, textureData, this.size, bandCount); + uploadDataTexture( + helper, + texture, + textureData, + this.size, + bandCount, + tile.interpolate + ); } } diff --git a/test/rendering/cases/webgl-data-tile-interpolate-false/expected.png b/test/rendering/cases/webgl-data-tile-interpolate-false/expected.png new file mode 100644 index 0000000000..68cc13c505 Binary files /dev/null and b/test/rendering/cases/webgl-data-tile-interpolate-false/expected.png differ diff --git a/test/rendering/cases/webgl-data-tile-interpolate-false/main.js b/test/rendering/cases/webgl-data-tile-interpolate-false/main.js new file mode 100644 index 0000000000..a4b64bd472 --- /dev/null +++ b/test/rendering/cases/webgl-data-tile-interpolate-false/main.js @@ -0,0 +1,31 @@ +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'; + +const size = 256; + +const data = new Uint8Array(size * size); +for (let row = 0; row < size; ++row) { + for (let col = 0; col < size; ++col) { + data[row * size + col] = (row + col) % 2 === 0 ? 255 : 0; + } +} + +new Map({ + target: 'map', + layers: [ + new TileLayer({ + source: new DataTile({ + maxZoom: 0, + loader: () => data, + }), + }), + ], + view: new View({ + center: [0, 0], + zoom: 4, + }), +}); + +render(); diff --git a/test/rendering/cases/webgl-data-tile-interpolate-true/expected.png b/test/rendering/cases/webgl-data-tile-interpolate-true/expected.png new file mode 100644 index 0000000000..b2bc536109 Binary files /dev/null and b/test/rendering/cases/webgl-data-tile-interpolate-true/expected.png differ diff --git a/test/rendering/cases/webgl-data-tile-interpolate-true/main.js b/test/rendering/cases/webgl-data-tile-interpolate-true/main.js new file mode 100644 index 0000000000..9e31b82195 --- /dev/null +++ b/test/rendering/cases/webgl-data-tile-interpolate-true/main.js @@ -0,0 +1,32 @@ +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'; + +const size = 256; + +const data = new Uint8Array(size * size); +for (let row = 0; row < size; ++row) { + for (let col = 0; col < size; ++col) { + data[row * size + col] = (row + col) % 2 === 0 ? 255 : 0; + } +} + +new Map({ + target: 'map', + layers: [ + new TileLayer({ + source: new DataTile({ + maxZoom: 0, + interpolate: true, + loader: () => data, + }), + }), + ], + view: new View({ + center: [0, 0], + zoom: 4, + }), +}); + +render(); diff --git a/test/rendering/cases/webgl-mixed-layers/expected.png b/test/rendering/cases/webgl-mixed-layers/expected.png index f058d91062..681d53e683 100644 Binary files a/test/rendering/cases/webgl-mixed-layers/expected.png and b/test/rendering/cases/webgl-mixed-layers/expected.png differ diff --git a/test/rendering/cases/webgl-multiple-layers/expected.png b/test/rendering/cases/webgl-multiple-layers/expected.png index 675e3d666d..0a01e8cfed 100644 Binary files a/test/rendering/cases/webgl-multiple-layers/expected.png and b/test/rendering/cases/webgl-multiple-layers/expected.png differ