diff --git a/src/ol/reproj/Image.js b/src/ol/reproj/Image.js index e08778002c..7a5cfd7240 100644 --- a/src/ol/reproj/Image.js +++ b/src/ol/reproj/Image.js @@ -47,7 +47,7 @@ class ReprojImage extends ImageBase { const triangulation = new Triangulation( sourceProj, targetProj, limitedTargetExtent, maxSourceExtent, - sourceResolution * errorThresholdInPixels); + sourceResolution * errorThresholdInPixels, targetResolution); const sourceExtent = triangulation.calculateSourceExtent(); const sourceImage = getImageFunction(sourceExtent, sourceResolution, pixelRatio); diff --git a/src/ol/reproj/Tile.js b/src/ol/reproj/Tile.js index 73d508d203..7defc9e48b 100644 --- a/src/ol/reproj/Tile.js +++ b/src/ol/reproj/Tile.js @@ -160,7 +160,7 @@ class ReprojTile extends Tile { */ this.triangulation_ = new Triangulation( sourceProj, targetProj, limitedTargetExtent, maxSourceExtent, - sourceResolution * errorThresholdInPixels); + sourceResolution * errorThresholdInPixels, targetResolution); if (this.triangulation_.getTriangles().length === 0) { // no valid triangles -> EMPTY diff --git a/src/ol/reproj/Triangulation.js b/src/ol/reproj/Triangulation.js index ebdab668da..ba49bac8c4 100644 --- a/src/ol/reproj/Triangulation.js +++ b/src/ol/reproj/Triangulation.js @@ -1,7 +1,7 @@ /** * @module ol/reproj/Triangulation */ -import {boundingExtent, createEmpty, extendCoordinate, getBottomLeft, getBottomRight, +import {boundingExtent, createEmpty, extendCoordinate, getArea, getBottomLeft, getBottomRight, getTopLeft, getTopRight, getWidth, intersects} from '../extent.js'; import {modulo} from '../math.js'; import {getTransform} from '../proj.js'; @@ -49,8 +49,9 @@ class Triangulation { * @param {import("../extent.js").Extent} targetExtent Target extent to triangulate. * @param {import("../extent.js").Extent} maxSourceExtent Maximal source extent that can be used. * @param {number} errorThreshold Acceptable error (in source units). + * @param {?number} opt_destinationResolution The (optional) resolution of the destination. */ - constructor(sourceProj, targetProj, targetExtent, maxSourceExtent, errorThreshold) { + constructor(sourceProj, targetProj, targetExtent, maxSourceExtent, errorThreshold, opt_destinationResolution) { /** * @type {import("../proj/Projection.js").default} @@ -138,11 +139,26 @@ class Triangulation { const sourceBottomRight = this.transformInv_(destinationBottomRight); const sourceBottomLeft = this.transformInv_(destinationBottomLeft); + /* + * The maxSubdivision controls how many splittings of the target area can + * be done. The idea here is to do a linear mapping of the target areas + * but the actual overal reprojection (can be) extremely non-linear. The + * default value of MAX_SUBDIVISION was chosen based on mapping a 256x256 + * tile size. However this function is also called to remap canvas rendered + * layers which can be much larger. This calculation increases the maxSubdivision + * value by the right factor so that each 256x256 pixel area has + * MAX_SUBDIVISION divisions. + */ + const maxSubdivision = MAX_SUBDIVISION + (opt_destinationResolution ? + Math.max(0, Math.ceil(Math.log2(getArea(targetExtent) / + (opt_destinationResolution * opt_destinationResolution * 256 * 256)))) + : 0); + this.addQuad_( destinationTopLeft, destinationTopRight, destinationBottomRight, destinationBottomLeft, sourceTopLeft, sourceTopRight, sourceBottomRight, sourceBottomLeft, - MAX_SUBDIVISION); + maxSubdivision); if (this.wrapsXInSource_) { let leftBound = Infinity;