diff --git a/changelog/upgrade-notes.md b/changelog/upgrade-notes.md index f05ab8ae73..caa8768fcb 100644 --- a/changelog/upgrade-notes.md +++ b/changelog/upgrade-notes.md @@ -1,6 +1,17 @@ ## Upgrade notes -### v6.15.0 +### Next Release + +#### Deprecated `tilePixelRatio` option for data tile sources. + +If you were previously trying to scale data tiles using the `tilePixelRatio` property for data tile sources (this is rare), you should now use the explicit `tileSize` and `tileGrid` properties. The source's `tileSize` represents the source tile dimensions and the tile grid's `tileSize` represents the desired rendered dimensions. + +```js +const source = new DataTileSource({ + tileSize: [512, 512], // source tile size + tileGrid: createXYZ({tileSize: [256, 256]}), // rendered tile size +}); +``` #### Fixed coordinate dimension handling in `ol/proj`'s `addCoordinateTransforms` diff --git a/package-lock.json b/package-lock.json index c418db27d6..fb7a09b4be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "6.14.2-dev", "license": "BSD-2-Clause", "dependencies": { - "geotiff": "^2.0.2", + "geotiff": "2.0.4", "ol-mapbox-style": "^8.0.5", "pbf": "3.2.1", "rbush": "^3.0.1" @@ -5294,19 +5294,20 @@ } }, "node_modules/geotiff": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/geotiff/-/geotiff-2.0.5.tgz", - "integrity": "sha512-U5kVYm118YAmw2swiLu8rhfrYnDKOFI7VaMjuQwcq6Intuuid9Pyb4jjxYUxxkq8kOu2r7Am0Rmb52PObGp4pQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/geotiff/-/geotiff-2.0.4.tgz", + "integrity": "sha512-aG8h9bJccGusioPsEWsEqx8qdXpZN71A20WCvRKGxcnHSOWLKmC5ZmsAmodfxb9TRQvs+89KikGuPzxchhA+Uw==", "dependencies": { "@petamoriken/float16": "^3.4.7", "lerc": "^3.0.0", + "lru-cache": "^6.0.0", "pako": "^2.0.4", "parse-headers": "^2.0.2", - "quick-lru": "^6.1.0", "web-worker": "^1.2.0", "xml-utils": "^1.0.2" }, "engines": { + "browsers": "defaults", "node": ">=10.19" } }, @@ -6811,7 +6812,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -8033,17 +8033,6 @@ } ] }, - "node_modules/quick-lru": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.1.tgz", - "integrity": "sha512-S27GBT+F0NTRiehtbrgaSE1idUAJ5bX8dPAQTdylEyNlrdcH5X4Lz7Edz3DYzecbsCluD5zO8ZNEe04z3D3u6Q==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/quickselect": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", @@ -10334,8 +10323,7 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yargs": { "version": "17.4.1", @@ -14384,15 +14372,15 @@ "dev": true }, "geotiff": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/geotiff/-/geotiff-2.0.5.tgz", - "integrity": "sha512-U5kVYm118YAmw2swiLu8rhfrYnDKOFI7VaMjuQwcq6Intuuid9Pyb4jjxYUxxkq8kOu2r7Am0Rmb52PObGp4pQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/geotiff/-/geotiff-2.0.4.tgz", + "integrity": "sha512-aG8h9bJccGusioPsEWsEqx8qdXpZN71A20WCvRKGxcnHSOWLKmC5ZmsAmodfxb9TRQvs+89KikGuPzxchhA+Uw==", "requires": { "@petamoriken/float16": "^3.4.7", "lerc": "^3.0.0", + "lru-cache": "^6.0.0", "pako": "^2.0.4", "parse-headers": "^2.0.2", - "quick-lru": "^6.1.0", "web-worker": "^1.2.0", "xml-utils": "^1.0.2" } @@ -15499,7 +15487,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "requires": { "yallist": "^4.0.0" } @@ -16412,11 +16399,6 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, - "quick-lru": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.1.tgz", - "integrity": "sha512-S27GBT+F0NTRiehtbrgaSE1idUAJ5bX8dPAQTdylEyNlrdcH5X4Lz7Edz3DYzecbsCluD5zO8ZNEe04z3D3u6Q==" - }, "quickselect": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", @@ -18150,8 +18132,7 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "yargs": { "version": "17.4.1", diff --git a/package.json b/package.json index 66ad149936..4ace428e24 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "url": "https://opencollective.com/openlayers" }, "dependencies": { - "geotiff": "^2.0.2", + "geotiff": "2.0.4", "ol-mapbox-style": "^8.0.5", "pbf": "3.2.1", "rbush": "^3.0.1" diff --git a/src/ol/DataTile.js b/src/ol/DataTile.js index cc82621b0d..e8de7d9f92 100644 --- a/src/ol/DataTile.js +++ b/src/ol/DataTile.js @@ -18,6 +18,7 @@ import TileState from './TileState.js'; * 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. + * @property {import('./size.js').Size} [size=[256, 256]] Tile size. * @api */ @@ -50,6 +51,20 @@ class DataTile extends Tile { * @private */ this.error_ = null; + + /** + * @type {import('./size.js').Size} + * @private + */ + this.size_ = options.size || [256, 256]; + } + + /** + * Get the tile size. + * @return {import('./size.js').Size} Tile size. + */ + getSize() { + return this.size_; } /** diff --git a/src/ol/renderer/webgl/TileLayer.js b/src/ol/renderer/webgl/TileLayer.js index 3b16543cc5..58de80bad0 100644 --- a/src/ol/renderer/webgl/TileLayer.js +++ b/src/ol/renderer/webgl/TileLayer.js @@ -318,7 +318,6 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer { const tileLayer = this.getLayer(); const tileSource = tileLayer.getRenderSource(); const tileGrid = tileSource.getTileGridForProjection(viewState.projection); - const tilePixelRatio = tileSource.getTilePixelRatio(frameState.pixelRatio); const gutter = tileSource.getGutterForProjection(viewState.projection); const tileSourceKey = getUid(tileSource); @@ -371,7 +370,6 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer { tile: tile, grid: tileGrid, helper: this.helper, - tilePixelRatio: tilePixelRatio, gutter: gutter, }); tileTextureCache.set(cacheKey, tileTexture); diff --git a/src/ol/source/DataTile.js b/src/ol/source/DataTile.js index c840d37a46..6da738f026 100644 --- a/src/ol/source/DataTile.js +++ b/src/ol/source/DataTile.js @@ -11,6 +11,7 @@ import {createXYZ, extentFromProjection} from '../tilegrid.js'; import {getKeyZXY} from '../tilecoord.js'; import {getUid} from '../util.js'; import {toPromise} from '../functions.js'; +import {toSize} from '../size.js'; /** * Data tile loading function. The function is called with z, x, and y tile coordinates and @@ -26,7 +27,8 @@ import {toPromise} from '../functions.js'; * @property {boolean} [attributionsCollapsible=true] Attributions are collapsible. * @property {number} [maxZoom=42] Optional max zoom level. Not used if `tileGrid` is provided. * @property {number} [minZoom=0] Optional min zoom level. Not used if `tileGrid` is provided. - * @property {number|import("../size.js").Size} [tileSize=[256, 256]] The pixel width and height of the tiles. + * @property {number|import("../size.js").Size} [tileSize=[256, 256]] The pixel width and height of the source tiles. + * This may be different than the rendered pixel size if a `tileGrid` is provided. * @property {number} [gutter=0] The size in pixels of the gutter around data tiles to ignore. * This allows artifacts of rendering at tile edges to be ignored. * Supported data should be wider and taller than the tile size by a value of `2 x gutter`. @@ -35,7 +37,8 @@ import {toPromise} from '../functions.js'; * @property {import("../tilegrid/TileGrid.js").default} [tileGrid] Tile grid. * @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 {number} [tilePixelRatio] Deprecated. To have tiles scaled, pass a `tileSize` representing + * the source tile size and a `tileGrid` with the desired rendered tile size. * @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. @@ -89,6 +92,25 @@ class DataTileSource extends TileSource { */ this.gutter_ = options.gutter !== undefined ? options.gutter : 0; + /** + * @private + * @type {import('../size.js').Size|null} + */ + this.tileSize_ = options.tileSize ? toSize(options.tileSize) : null; + if (!this.tileSize_ && options.tilePixelRatio && tileGrid) { + const renderTileSize = toSize(tileGrid.getTileSize(0)); + this.tileSize_ = [ + renderTileSize[0] * options.tilePixelRatio, + renderTileSize[1] * options.tilePixelRatio, + ]; + } + + /** + * @private + * @type {Array|null} + */ + this.tileSizes_ = null; + /** * @private * @type {!Object} @@ -108,6 +130,34 @@ class DataTileSource extends TileSource { this.bandCount = options.bandCount === undefined ? 4 : options.bandCount; // assume RGBA if undefined } + /** + * Set the source tile sizes. The length of the array is expected to match the number of + * levels in the tile grid. + * @protected + * @param {Array} tileSizes An array of tile sizes. + */ + setTileSizes(tileSizes) { + this.tileSizes_ = tileSizes; + } + + /** + * Get the source tile size at the given zoom level. This may be different than the rendered tile + * size. + * @protected + * @param {number} z Tile zoom level. + * @return {import('../size.js').Size} The source tile size. + */ + getTileSize(z) { + if (this.tileSizes_) { + return this.tileSizes_[z]; + } + if (this.tileSize_) { + return this.tileSize_; + } + const tileGrid = this.getTileGrid(); + return tileGrid ? toSize(tileGrid.getTileSize(z)) : [256, 256]; + } + /** * @param {import("../proj/Projection.js").default} projection Projection. * @return {number} Gutter. @@ -133,6 +183,7 @@ class DataTileSource extends TileSource { * @return {!DataTile} Tile. */ getTile(z, x, y, pixelRatio, projection) { + const size = this.getTileSize(z); const tileCoordKey = getKeyZXY(z, x, y); if (this.tileCache.containsKey(tileCoordKey)) { return this.tileCache.get(tileCoordKey); @@ -146,9 +197,16 @@ class DataTileSource extends TileSource { }); } - const tile = new DataTile( - assign({tileCoord: [z, x, y], loader: loader}, this.tileOptions) + const options = assign( + { + tileCoord: [z, x, y], + loader: loader, + size: size, + }, + this.tileOptions ); + + const tile = new DataTile(options); tile.key = this.getKey(); tile.addEventListener(EventType.CHANGE, this.handleTileChange_); diff --git a/src/ol/source/GeoTIFF.js b/src/ol/source/GeoTIFF.js index 65d960d8a8..4f7fb30611 100644 --- a/src/ol/source/GeoTIFF.js +++ b/src/ol/source/GeoTIFF.js @@ -13,7 +13,6 @@ import { } from '../proj.js'; import {clamp} from '../math.js'; import {getCenter, getIntersection} from '../extent.js'; -import {toSize} from '../size.js'; import {fromCode as unitsFromCode} from '../proj/Units.js'; /** @@ -112,15 +111,17 @@ function getOrigin(image) { * the width of the image is compared with the reference image. * @param {GeoTIFFImage} image The image. * @param {GeoTIFFImage} referenceImage The reference image. - * @return {number} The image resolution. + * @return {Array} The map x and y units per pixel. */ -function getResolution(image, referenceImage) { +function getResolutions(image, referenceImage) { try { - return image.getResolution(referenceImage)[0]; + return image.getResolution(referenceImage); } catch (_) { - return ( - referenceImage.fileDirectory.ImageWidth / image.fileDirectory.ImageWidth - ); + return [ + referenceImage.fileDirectory.ImageWidth / image.fileDirectory.ImageWidth, + referenceImage.fileDirectory.ImageHeight / + image.fileDirectory.ImageHeight, + ]; } } @@ -449,7 +450,8 @@ class GeoTIFFSource extends DataTile { configure_(sources) { let extent; let origin; - let tileSizes; + let commonRenderTileSizes; + let commonSourceTileSizes; let resolutions; const samplesPerPixel = new Array(sources.length); const nodataValues = new Array(sources.length); @@ -464,6 +466,7 @@ class GeoTIFFSource extends DataTile { let sourceExtent; let sourceOrigin; const sourceTileSizes = new Array(imageCount); + const renderTileSizes = new Array(imageCount); const sourceResolutions = new Array(imageCount); nodataValues[sourceIndex] = new Array(imageCount); @@ -490,8 +493,17 @@ class GeoTIFFSource extends DataTile { sourceOrigin = getOrigin(image); } - sourceResolutions[level] = getResolution(image, images[0]); - sourceTileSizes[level] = [image.getTileWidth(), image.getTileHeight()]; + const imageResolutions = getResolutions(image, images[0]); + sourceResolutions[level] = imageResolutions[0]; + + const sourceTileSize = [image.getTileWidth(), image.getTileHeight()]; + sourceTileSizes[level] = sourceTileSize; + + const aspectRatio = imageResolutions[0] / Math.abs(imageResolutions[1]); + renderTileSizes[level] = [ + sourceTileSize[0], + sourceTileSize[1] / aspectRatio, + ]; } if (!extent) { @@ -531,11 +543,23 @@ class GeoTIFFSource extends DataTile { ); } - if (!tileSizes) { - tileSizes = sourceTileSizes; + if (!commonRenderTileSizes) { + commonRenderTileSizes = renderTileSizes; } else { assertEqual( - tileSizes.slice(minZoom, tileSizes.length), + commonRenderTileSizes.slice(minZoom, commonRenderTileSizes.length), + renderTileSizes, + 0.01, + `Tile size mismatch for source ${sourceIndex}`, + this.viewRejector + ); + } + + if (!commonSourceTileSizes) { + commonSourceTileSizes = sourceTileSizes; + } else { + assertEqual( + commonSourceTileSizes.slice(minZoom, commonSourceTileSizes.length), sourceTileSizes, 0, `Tile size mismatch for source ${sourceIndex}`, @@ -612,10 +636,11 @@ class GeoTIFFSource extends DataTile { minZoom: minZoom, origin: origin, resolutions: resolutions, - tileSizes: tileSizes, + tileSizes: commonRenderTileSizes, }); this.tileGrid = tileGrid; + this.setTileSizes(commonSourceTileSizes); this.setLoader(this.loadTile_.bind(this)); this.setState(State.READY); @@ -629,8 +654,7 @@ class GeoTIFFSource extends DataTile { } loadTile_(z, x, y) { - const size = toSize(this.tileGrid.getTileSize(z)); - + const sourceTileSize = this.getTileSize(z); const sourceCount = this.sourceImagery_.length; const requests = new Array(sourceCount); const addAlpha = this.addAlpha_; @@ -642,10 +666,10 @@ class GeoTIFFSource extends DataTile { const source = sourceInfo[sourceIndex]; const resolutionFactor = this.resolutionFactors_[sourceIndex]; const pixelBounds = [ - Math.round(x * (size[0] * resolutionFactor)), - Math.round(y * (size[1] * resolutionFactor)), - Math.round((x + 1) * (size[0] * resolutionFactor)), - Math.round((y + 1) * (size[1] * resolutionFactor)), + Math.round(x * (sourceTileSize[0] * resolutionFactor)), + Math.round(y * (sourceTileSize[1] * resolutionFactor)), + Math.round((x + 1) * (sourceTileSize[0] * resolutionFactor)), + Math.round((y + 1) * (sourceTileSize[1] * resolutionFactor)), ]; const image = this.sourceImagery_[sourceIndex][z]; let samples; @@ -671,8 +695,8 @@ class GeoTIFFSource extends DataTile { requests[sourceIndex] = image[this.readMethod_]({ window: pixelBounds, - width: size[0], - height: size[1], + width: sourceTileSize[0], + height: sourceTileSize[1], samples: samples, fillValue: fillValue, pool: getWorkerPool(), @@ -680,7 +704,7 @@ class GeoTIFFSource extends DataTile { }); } - const pixelCount = size[0] * size[1]; + const pixelCount = sourceTileSize[0] * sourceTileSize[1]; const dataLength = pixelCount * bandCount; const normalize = this.normalize_; const metadata = this.metadata_; diff --git a/src/ol/webgl/TileTexture.js b/src/ol/webgl/TileTexture.js index 35d3080e58..d67c612405 100644 --- a/src/ol/webgl/TileTexture.js +++ b/src/ol/webgl/TileTexture.js @@ -138,7 +138,6 @@ function createPixelContext() { * @property {TileType} tile The tile. * @property {import("../tilegrid/TileGrid.js").default} grid Tile grid. * @property {import("../webgl/Helper.js").default} helper WebGL helper. - * @property {number} [tilePixelRatio=1] Tile pixel ratio. * @property {number} [gutter=0] The size in pixels of the gutter around image tiles to ignore. */ @@ -162,14 +161,11 @@ class TileTexture extends EventTarget { /** * @type {import("../size.js").Size} - */ - this.size = toSize(options.grid.getTileSize(options.tile.tileCoord[0])); - - /** - * @type {number} * @private */ - this.tilePixelRatio_ = options.tilePixelRatio || 1; + this.renderSize_ = toSize( + options.grid.getTileSize(options.tile.tileCoord[0]) + ); /** * @type {number} @@ -247,9 +243,10 @@ class TileTexture extends EventTarget { return; } + const sourceTileSize = tile.getSize(); const pixelSize = [ - (this.size[0] + 2 * this.gutter_) * this.tilePixelRatio_, - (this.size[1] + 2 * this.gutter_) * this.tilePixelRatio_, + sourceTileSize[0] + 2 * this.gutter_, + sourceTileSize[1] + 2 * this.gutter_, ]; const data = tile.getData(); const isFloat = data instanceof Float32Array; @@ -339,34 +336,43 @@ class TileTexture extends EventTarget { /** * Get data for a pixel. If the tile is not loaded, null is returned. - * @param {number} col The column index. - * @param {number} row The row index. + * @param {number} renderCol The column index (in rendered tile space). + * @param {number} renderRow The row index (in rendered tile space). * @return {import("../DataTile.js").Data|null} The data. */ - getPixelData(col, row) { + getPixelData(renderCol, renderRow) { if (!this.loaded) { return null; } - - const gutter = Math.round(this.tilePixelRatio_ * this.gutter_); - col = Math.floor(this.tilePixelRatio_ * col) + gutter; - row = Math.floor(this.tilePixelRatio_ * row) + gutter; + const renderWidth = this.renderSize_[0]; + const renderHeight = this.renderSize_[1]; + const gutter = this.gutter_; if (this.tile instanceof DataTile) { + const sourceSize = this.tile.getSize(); + + const sourceWidthWithoutGutter = sourceSize[0]; + const sourceHeightWithoutGutter = sourceSize[1]; + const sourceWidth = sourceWidthWithoutGutter + 2 * gutter; + const sourceHeight = sourceHeightWithoutGutter + 2 * gutter; + + const sourceCol = + gutter + + Math.floor(sourceWidthWithoutGutter * (renderCol / renderWidth)); + + const sourceRow = + gutter + + Math.floor(sourceHeightWithoutGutter * (renderRow / renderHeight)); + const data = this.tile.getData(); - let size = this.size; - if (gutter > 0) { - size = [size[0] + 2 * this.gutter_, size[1] + 2 * this.gutter_]; - } - const pixelsPerRow = Math.floor(this.tilePixelRatio_ * size[0]); if (data instanceof DataView) { - const bytesPerPixel = data.byteLength / (size[0] * size[1]); - const offset = row * pixelsPerRow * bytesPerPixel + col * bytesPerPixel; + const bytesPerPixel = data.byteLength / (sourceWidth * sourceHeight); + const offset = bytesPerPixel * (sourceRow * sourceWidth + sourceCol); const buffer = data.buffer.slice(offset, offset + bytesPerPixel); return new DataView(buffer); } - const offset = row * pixelsPerRow * this.bandCount + col * this.bandCount; + const offset = this.bandCount * (sourceRow * sourceWidth + sourceCol); return data.slice(offset, offset + this.bandCount); } @@ -375,10 +381,23 @@ class TileTexture extends EventTarget { } pixelContext.clearRect(0, 0, 1, 1); - let data; const image = this.tile.getImage(); + const sourceWidth = image.width; + const sourceHeight = image.height; + + const sourceWidthWithoutGutter = sourceWidth - 2 * gutter; + const sourceHeightWithoutGutter = sourceHeight - 2 * gutter; + + const sourceCol = + gutter + Math.floor(sourceWidthWithoutGutter * (renderCol / renderWidth)); + + const sourceRow = + gutter + + Math.floor(sourceHeightWithoutGutter * (renderRow / renderHeight)); + + let data; try { - pixelContext.drawImage(image, col, row, 1, 1, 0, 0, 1, 1); + pixelContext.drawImage(image, sourceCol, sourceRow, 1, 1, 0, 0, 1, 1); data = pixelContext.getImageData(0, 0, 1, 1).data; } catch (err) { pixelContext = null; diff --git a/test/browser/spec/ol/DataTile.test.js b/test/browser/spec/ol/DataTile.test.js index 76c696653e..1926de55a4 100644 --- a/test/browser/spec/ol/DataTile.test.js +++ b/test/browser/spec/ol/DataTile.test.js @@ -33,6 +33,28 @@ describe('ol/DataTile', function () { }); }); + describe('#getSize()', function () { + it('returns [256, 256] by default', function () { + const tileCoord = [0, 0, 0]; + const tile = new DataTile({ + tileCoord: tileCoord, + loader: loader, + }); + expect(tile.getSize()).to.eql([256, 256]); + }); + + it('respects what is provided in the constructor', function () { + const size = [123, 456]; + const tileCoord = [0, 0, 0]; + const tile = new DataTile({ + size: size, + tileCoord: tileCoord, + loader: loader, + }); + expect(tile.getSize()).to.eql(size); + }); + }); + describe('#load()', function () { it('handles loading states correctly', function (done) { const tileCoord = [0, 0, 0]; diff --git a/test/browser/spec/ol/layer/WebGLTile.test.js b/test/browser/spec/ol/layer/WebGLTile.test.js index a8458f41ce..d8d5a63840 100644 --- a/test/browser/spec/ol/layer/WebGLTile.test.js +++ b/test/browser/spec/ol/layer/WebGLTile.test.js @@ -5,6 +5,7 @@ import View from '../../../../../src/ol/View.js'; import WebGLHelper from '../../../../../src/ol/webgl/Helper.js'; import WebGLTileLayer from '../../../../../src/ol/layer/WebGLTile.js'; import {createCanvasContext2D} from '../../../../../src/ol/dom.js'; +import {createXYZ} from '../../../../../src/ol/tilegrid.js'; import {getForViewAndSize} from '../../../../../src/ol/extent.js'; import {getRenderPixel} from '../../../../../src/ol/render.js'; @@ -81,7 +82,8 @@ describe('ol/layer/WebGLTile', function () { it('retrieves pixel data', (done) => { const layer = new WebGLTileLayer({ source: new DataTileSource({ - tilePixelRatio: 1 / 256, + tileSize: 1, + tileGrid: createXYZ(), loader(z, x, y) { return new Uint8Array([5, 4, 3, 2, 1]); }, @@ -106,7 +108,8 @@ describe('ol/layer/WebGLTile', function () { it('preserves the original data type', (done) => { const layer = new WebGLTileLayer({ source: new DataTileSource({ - tilePixelRatio: 1 / 256, + tileSize: 1, + tileGrid: createXYZ(), loader(z, x, y) { return new Float32Array([1.11, 2.22, 3.33, 4.44, 5.55]); }, diff --git a/test/browser/spec/ol/source/DataTile.test.js b/test/browser/spec/ol/source/DataTile.test.js index 94beefc616..e7f0689880 100644 --- a/test/browser/spec/ol/source/DataTile.test.js +++ b/test/browser/spec/ol/source/DataTile.test.js @@ -40,6 +40,30 @@ describe('ol/source/DataTile', function () { }); }); + describe('#getTileSize()', function () { + it('returns [256, 256] by default', function () { + const source = new DataTileSource({}); + expect(source.getTileSize(0)).to.eql([256, 256]); + }); + + it('respects a tileSize passed to the constructor', function () { + const size = [1234, 5678]; + const source = new DataTileSource({tileSize: size}); + expect(source.getTileSize(0)).to.eql(size); + }); + + it('picks from an array of sizes passed to setTileSizes()', function () { + const sizes = [ + [123, 456], + [234, 567], + [345, 678], + ]; + const source = new DataTileSource({}); + source.setTileSizes(sizes); + expect(source.getTileSize(1)).to.eql(sizes[1]); + }); + }); + describe('#getInterpolate()', function () { it('is false by default', function () { const source = new DataTileSource({loader: () => {}}); diff --git a/test/rendering/cases/webgl-data-tile-tilepixelratio2/main.js b/test/rendering/cases/webgl-data-tile-tilepixelratio2/main.js index 32b0c30e4d..258c2922c0 100644 --- a/test/rendering/cases/webgl-data-tile-tilepixelratio2/main.js +++ b/test/rendering/cases/webgl-data-tile-tilepixelratio2/main.js @@ -2,6 +2,7 @@ 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 {createXYZ} from '../../../../src/ol/tilegrid.js'; const size = 512; @@ -17,9 +18,14 @@ new Map({ layers: [ new TileLayer({ source: new DataTile({ + // remove this in the next major release + tilePixelRatio: 2, + + // instead use an explicit source and render tile size + // tileSize: size, + // tileGrid: createXYZ({maxZoom: 0}), maxZoom: 0, loader: () => data, - tilePixelRatio: 2, }), }), ], diff --git a/test/rendering/cases/webgl-tile-aspect-ratio/expected.png b/test/rendering/cases/webgl-tile-aspect-ratio/expected.png new file mode 100644 index 0000000000..10f0cbda6d Binary files /dev/null and b/test/rendering/cases/webgl-tile-aspect-ratio/expected.png differ diff --git a/test/rendering/cases/webgl-tile-aspect-ratio/main.js b/test/rendering/cases/webgl-tile-aspect-ratio/main.js new file mode 100644 index 0000000000..7aa783795a --- /dev/null +++ b/test/rendering/cases/webgl-tile-aspect-ratio/main.js @@ -0,0 +1,21 @@ +import GeoTIFF from '../../../../src/ol/source/GeoTIFF.js'; +import Map from '../../../../src/ol/Map.js'; +import TileLayer from '../../../../src/ol/layer/WebGLTile.js'; + +const source = new GeoTIFF({ + convertToRGB: true, + sources: [{url: '/data/raster/non-square-pixels.tif'}], +}); + +new Map({ + target: 'map', + layers: [new TileLayer({source})], + view: source.getView().then((config) => ({ + ...config, + rotation: Math.PI / 6, + })), +}); + +render({ + message: 'properly renders rotated non-square pixels', +}); diff --git a/test/rendering/data/raster/non-square-pixels.tif b/test/rendering/data/raster/non-square-pixels.tif new file mode 100644 index 0000000000..363409260f Binary files /dev/null and b/test/rendering/data/raster/non-square-pixels.tif differ