diff --git a/src/ol/renderer/webgl/TileLayer.js b/src/ol/renderer/webgl/TileLayer.js index 565496d06e..3b16543cc5 100644 --- a/src/ol/renderer/webgl/TileLayer.js +++ b/src/ol/renderer/webgl/TileLayer.js @@ -12,8 +12,11 @@ import {AttributeType} from '../../webgl/Helper.js'; import {ELEMENT_ARRAY_BUFFER, STATIC_DRAW} from '../../webgl.js'; import { apply as applyTransform, - compose as composeTransform, create as createTransform, + reset as resetTransform, + rotate as rotateTransform, + scale as scaleTransform, + translate as translateTransform, } from '../../transform.js'; import {containsCoordinate, getIntersection, isEmpty} from '../../extent.js'; import { @@ -519,6 +522,10 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer { const tileSize = toSize(tileGrid.getTileSize(tileZ), this.tempSize_); const tileOrigin = tileGrid.getOrigin(tileZ); + const tileWidthWithGutter = tileSize[0] + 2 * gutter; + const tileHeightWithGutter = tileSize[1] + 2 * gutter; + const aspectRatio = tileWidthWithGutter / tileHeightWithGutter; + const centerI = (centerX - tileOrigin[0]) / (tileSize[0] * tileResolution); const centerJ = @@ -540,17 +547,20 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer { const tileCenterI = tileCoord[1]; const tileCenterJ = tileCoord[2]; - composeTransform( + resetTransform(this.tileTransform_); + scaleTransform( this.tileTransform_, - 0, - 0, - 2 / ((frameState.size[0] * tileScale) / (tileSize[0] + 2 * gutter)), - -2 / ((frameState.size[1] * tileScale) / (tileSize[1] + 2 * gutter)), - viewState.rotation, - ((tileCenterI - centerI - gutter / tileSize[0]) * tileSize[0]) / - (tileSize[0] + 2 * gutter), - ((tileCenterJ - centerJ - gutter / tileSize[1]) * tileSize[1]) / - (tileSize[1] + 2 * gutter) + 2 / ((frameState.size[0] * tileScale) / tileWidthWithGutter), + -2 / ((frameState.size[1] * tileScale) / tileWidthWithGutter) + ); + rotateTransform(this.tileTransform_, viewState.rotation); + scaleTransform(this.tileTransform_, 1, 1 / aspectRatio); + translateTransform( + this.tileTransform_, + (tileSize[0] * (tileCenterI - centerI) - gutter) / + tileWidthWithGutter, + (tileSize[1] * (tileCenterJ - centerJ) - gutter) / + tileHeightWithGutter ); this.helper.setUniformMatrixValue( @@ -602,11 +612,11 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer { this.helper.setUniformFloatValue(Uniforms.DEPTH, depth); this.helper.setUniformFloatValue( Uniforms.TEXTURE_PIXEL_WIDTH, - tileSize[0] + 2 * gutter + tileWidthWithGutter ); this.helper.setUniformFloatValue( Uniforms.TEXTURE_PIXEL_HEIGHT, - tileSize[1] + 2 * gutter + tileHeightWithGutter ); this.helper.setUniformFloatValue( Uniforms.TEXTURE_RESOLUTION, diff --git a/test/rendering/cases/webgl-tile-non-square/expected.png b/test/rendering/cases/webgl-tile-non-square/expected.png new file mode 100644 index 0000000000..a2e0583442 Binary files /dev/null and b/test/rendering/cases/webgl-tile-non-square/expected.png differ diff --git a/test/rendering/cases/webgl-tile-non-square/main.js b/test/rendering/cases/webgl-tile-non-square/main.js new file mode 100644 index 0000000000..9839da3018 --- /dev/null +++ b/test/rendering/cases/webgl-tile-non-square/main.js @@ -0,0 +1,62 @@ +import DataTile from '../../../../src/ol/source/DataTile.js'; +import Map from '../../../../src/ol/Map.js'; +import Projection from '../../../../src/ol/proj/Projection.js'; +import TileLayer from '../../../../src/ol/layer/WebGLTile.js'; +import View from '../../../../src/ol/View.js'; + +const extent = [-1000, -500, 1000, 500]; +const projection = new Projection({ + code: 'test', + units: 'pixels', + extent: extent, +}); + +const width = 200; +const height = 100; + +const canvas = document.createElement('canvas'); +canvas.width = width; +canvas.height = height; + +const context = canvas.getContext('2d'); +context.strokeStyle = 'red'; +context.textAlign = 'center'; +context.font = '16px sans-serif'; +const lineHeight = 20; + +new Map({ + target: 'map', + layers: [ + new TileLayer({ + source: new DataTile({ + projection: projection, + tileSize: [width, height], + loader: function (z, x, y) { + const halfWidth = width / 2; + const halfHeight = height / 2; + context.clearRect(0, 0, width, height); + context.fillStyle = 'rgba(100, 100, 100, 0.5)'; + context.fillRect(0, 0, width, height); + context.fillStyle = 'black'; + context.fillText(`z: ${z}`, halfWidth, halfHeight - lineHeight); + context.fillText(`x: ${x}`, halfWidth, halfHeight); + context.fillText(`y: ${y}`, halfWidth, halfHeight + lineHeight); + context.strokeRect(0, 0, width, height); + return context.getImageData(0, 0, width, height).data; + }, + transition: 0, + }), + }), + ], + view: new View({ + projection: projection, + showFullExtent: true, + center: [0, 0], + rotation: Math.PI / 4, + zoom: 0, + }), +}); + +render({ + message: 'properly renders rotated non-square tiles', +});