diff --git a/src/ol/ol.js b/src/ol/ol.js index c0c458349a..bd96b4817a 100644 --- a/src/ol/ol.js +++ b/src/ol/ol.js @@ -28,6 +28,13 @@ ol.DEFAULT_MAX_ZOOM = 42; ol.DEFAULT_MIN_ZOOM = 0; +/** + * @define {number} Default maximum allowed threshold (in pixels) for + * reprojection triangulation. Default is `0.5`. + */ +ol.DEFAULT_RASTER_REPROJ_ERROR_THRESHOLD = 0.5; + + /** * @define {number} Default high water mark. */ @@ -166,6 +173,39 @@ ol.OVERVIEWMAP_MAX_RATIO = 0.75; ol.OVERVIEWMAP_MIN_RATIO = 0.1; +/** + * @define {number} Maximum number of source tiles for raster reprojection. + * If too many source tiles are determined to be loaded to create a single + * reprojected tile the browser can become unresponsive or even crash. + * This can happen if the developer defines projections improperly and/or + * with unlimited extents. + * If too many tiles are required, no tiles are loaded and + * `ol.TileState.ERROR` state is set. Default is `100`. + */ +ol.RASTER_REPROJ_MAX_SOURCE_TILES = 100; + + +/** + * @define {number} Maximum number of subdivision steps during raster + * reprojection triangulation. Prevents high memory usage and large + * number of proj4 calls when for certain transformations and areas. + * At most `2*(4^this)` triangles are created. Default is `5`. + */ +ol.RASTER_REPROJ_MAX_SUBDIVISION = 5; + + +/** + * @define {number} Maximum allowed size of triangle relative to world width. + * When transforming corners of world extent between certain projections, + * The resulting triangulation seems to have zero error and no subdivision + * is performed. + * If the triangle width is more than this (relative to world width; 0-1), + * subdivison is forced (respecting `ol.RASTER_REPROJ_MAX_SUBDIVISION`). + * Default is `0.25`. + */ +ol.RASTER_REPROJ_MAX_TRIANGLE_WIDTH = 0.25; + + /** * @define {number} Tolerance for geometry simplification in device pixels. */ diff --git a/src/ol/reproj/image.js b/src/ol/reproj/image.js index 246dceb873..cfc713a3fc 100644 --- a/src/ol/reproj/image.js +++ b/src/ol/reproj/image.js @@ -63,7 +63,7 @@ ol.reproj.Image = function(sourceProj, targetProj, * @type {!ol.reproj.Triangulation} */ this.triangulation_ = new ol.reproj.Triangulation( - sourceProj, targetProj, limitedTargetExtent, this.maxSourceExtent_); + sourceProj, targetProj, limitedTargetExtent, this.maxSourceExtent_, 0); /** * @private diff --git a/src/ol/reproj/tile.js b/src/ol/reproj/tile.js index bec8ca7b59..cc6e599c87 100644 --- a/src/ol/reproj/tile.js +++ b/src/ol/reproj/tile.js @@ -99,7 +99,7 @@ ol.reproj.Tile = function(sourceProj, sourceTileGrid, var targetResolution = targetTileGrid.getResolution(z); - var errorThresholdInPixels = 0.5; + var errorThresholdInPixels = ol.DEFAULT_RASTER_REPROJ_ERROR_THRESHOLD; // in source units var errorThreshold = targetResolution * errorThresholdInPixels * @@ -111,7 +111,7 @@ ol.reproj.Tile = function(sourceProj, sourceTileGrid, */ this.triangulation_ = new ol.reproj.Triangulation( sourceProj, targetProj, limitedTargetExtent, maxSourceExtent, - 5, errorThreshold); + errorThreshold); if (this.triangulation_.getTriangles().length === 0) { // no valid triangles -> EMPTY @@ -166,11 +166,11 @@ ol.reproj.Tile = function(sourceProj, sourceTileGrid, xRange = goog.array.range(srcRange.minX, srcRange.maxX + 1); } - if (xRange.length * srcRange.getHeight() > 100) { - // Too many source tiles are needed -- something probably went wrong - // This sometimes happens for certain non-global projections - // if no extent is specified. - // TODO: detect somehow better? or at least make this a define + var tilesRequired = xRange.length * srcRange.getHeight(); + goog.asserts.assert(tilesRequired < ol.RASTER_REPROJ_MAX_SOURCE_TILES, + 'reasonable number of tiles is required'); + + if (tilesRequired > ol.RASTER_REPROJ_MAX_SOURCE_TILES) { this.state = ol.TileState.ERROR; return; } diff --git a/src/ol/reproj/triangulation.js b/src/ol/reproj/triangulation.js index 384297ef44..067e7444cd 100644 --- a/src/ol/reproj/triangulation.js +++ b/src/ol/reproj/triangulation.js @@ -20,13 +20,12 @@ ol.reproj.Triangle; * @param {ol.proj.Projection} sourceProj * @param {ol.proj.Projection} targetProj * @param {ol.Extent} targetExtent - * @param {ol.Extent=} opt_maxSourceExtent - * @param {number=} opt_maxSubdiv Maximal subdivision. - * @param {number=} opt_errorThreshold Acceptable error (in source units). + * @param {ol.Extent} maxSourceExtent + * @param {number} errorThreshold Acceptable error (in source units). * @constructor */ ol.reproj.Triangulation = function(sourceProj, targetProj, targetExtent, - opt_maxSourceExtent, opt_maxSubdiv, opt_errorThreshold) { + maxSourceExtent, errorThreshold) { /** * @type {ol.proj.Projection} @@ -50,11 +49,8 @@ ol.reproj.Triangulation = function(sourceProj, targetProj, targetExtent, * @type {ol.Extent} * @private */ - this.maxSourceExtent_ = goog.isDef(opt_maxSourceExtent) ? - opt_maxSourceExtent : null; + this.maxSourceExtent_ = maxSourceExtent; - var errorThreshold = goog.isDef(opt_errorThreshold) ? - opt_errorThreshold : 0; //TODO: define /** * @type {number} * @private @@ -99,7 +95,7 @@ ol.reproj.Triangulation = function(sourceProj, targetProj, targetExtent, this.addQuadIfValid_(tlDst, trDst, brDst, blDst, tlDstSrc, trDstSrc, brDstSrc, blDstSrc, - opt_maxSubdiv || 0); + ol.RASTER_REPROJ_MAX_SUBDIVISION); }; @@ -155,7 +151,7 @@ ol.reproj.Triangulation.prototype.addQuadIfValid_ = function(a, b, c, d, if (maxSubdiv > 0) { var needsSubdivision = !wrapsX && this.sourceProj_.isGlobal() && - srcCoverageX > 0.25; //TODO: define + srcCoverageX > ol.RASTER_REPROJ_MAX_TRIANGLE_WIDTH; var center = [(a[0] + c[0]) / 2, (a[1] + c[1]) / 2]; var centerSrc = this.transformInv_(center);