diff --git a/src/ol/reproj/reproj.js b/src/ol/reproj/reproj.js index 25da89ad1e..3d5bfbf909 100644 --- a/src/ol/reproj/reproj.js +++ b/src/ol/reproj/reproj.js @@ -57,6 +57,17 @@ ol.reproj.calculateSourceResolution = function(sourceProj, targetProj, */ ol.reproj.renderTriangles = function(context, sourceResolution, targetResolution, targetExtent, triangulation, sources) { + + var renderImage = function(image) { + context.scale(sourceResolution, -sourceResolution); + + // the image has to be scaled by half a pixel in every direction + // in order to prevent artifacts between the original tiles + // that are introduced by the canvas antialiasing. + context.drawImage(image, -0.5, -0.5, + image.width + 1, image.height + 1); + }; + goog.array.forEach(triangulation, function(tri, i, arr) { context.save(); @@ -88,6 +99,11 @@ ol.reproj.renderTriangles = function(context, var u0 = tri[0][1][0] - targetTL[0], v0 = -(tri[0][1][1] - targetTL[1]), u1 = tri[1][1][0] - targetTL[0], v1 = -(tri[1][1][1] - targetTL[1]), u2 = tri[2][1][0] - targetTL[0], v2 = -(tri[2][1][1] - targetTL[1]); + if (tri.shiftDistance) { + x0 = goog.math.modulo(x0 + tri.shiftDistance, tri.shiftDistance); + x1 = goog.math.modulo(x1 + tri.shiftDistance, tri.shiftDistance); + x2 = goog.math.modulo(x2 + tri.shiftDistance, tri.shiftDistance); + } var augmentedMatrix = [ [x0, y0, 1, 0, 0, 0, u0 / targetResolution], [x1, y1, 1, 0, 0, 0, u1 / targetResolution], @@ -133,13 +149,22 @@ ol.reproj.renderTriangles = function(context, context.save(); var tlSrcFromData = ol.extent.getTopLeft(src.extent); context.translate(tlSrcFromData[0], tlSrcFromData[1]); - context.scale(sourceResolution, -sourceResolution); + if (tri.shiftDistance) { + context.save(); + context.translate(tri.shiftDistance, 0); + renderImage(src.image); + context.restore(); + renderImage(src.image); - // the image has to be scaled by half a pixel in every direction - // in order to prevent artifacts between the original tiles - // that are introduced by the canvas antialiasing. - context.drawImage(src.image, -0.5, -0.5, - src.image.width + 1, src.image.height + 1); + if (goog.DEBUG) { + context.fillStyle = + sources.length > 16 ? 'rgba(255,0,0,1)' : + (sources.length > 4 ? 'rgba(0,255,0,.3)' : 'rgba(0,0,255,.1)'); + context.fillRect(0, 0, 256, 256); + } + } else { + renderImage(src.image); + } context.restore(); }); diff --git a/src/ol/reproj/tile.js b/src/ol/reproj/tile.js index efe4c41d17..f9e6d3ccbf 100644 --- a/src/ol/reproj/tile.js +++ b/src/ol/reproj/tile.js @@ -97,6 +97,12 @@ ol.reproj.Tile = function(sourceProj, sourceTileGrid, targetExtent, sourceProj, targetProj, maxTargetExtent, maxSourceExtent); + if (this.triangles_.length === 0) { + // no valid triangles -> EMPTY + this.state = ol.TileState.EMPTY; + return; + } + var targetCenter = ol.extent.getCenter(targetExtent); var targetResolution = targetTileGrid.getResolution(z); var sourceResolution = ol.reproj.calculateSourceResolution( @@ -113,23 +119,35 @@ ol.reproj.Tile = function(sourceProj, sourceTileGrid, var srcExtent = ol.reproj.triangulation.getSourceExtent(this.triangles_); var sourceProjExtent = sourceProj.getExtent(); - if (sourceProjExtent) { + if (!sourceProj.isGlobal() && sourceProjExtent) { srcExtent = ol.extent.getIntersection(srcExtent, sourceProjExtent); } - if (!ol.extent.intersects(sourceTileGrid.getExtent(), srcExtent)) { + if (!goog.isNull(maxSourceExtent) && + !ol.extent.intersects(maxSourceExtent, srcExtent)) { this.state = ol.TileState.EMPTY; } else { var srcRange = sourceTileGrid.getTileRangeForExtentAndZ( srcExtent, this.srcZ_); var srcFullRange = sourceTileGrid.getFullTileRange(this.srcZ_); - srcRange.minX = Math.max(srcRange.minX, srcFullRange.minX); - srcRange.maxX = Math.min(srcRange.maxX, srcFullRange.maxX); srcRange.minY = Math.max(srcRange.minY, srcFullRange.minY); srcRange.maxY = Math.min(srcRange.maxY, srcFullRange.maxY); - if (srcRange.getWidth() * srcRange.getHeight() > 100) { + var xRange; + if (srcRange.minX > srcRange.maxX) { + xRange = goog.array.concat( + goog.array.range(srcRange.minX, srcFullRange.maxX + 1), + goog.array.range(srcFullRange.minX, srcRange.maxX + 1) + ); + } else { + xRange = goog.array.range( + Math.max(srcRange.minX, srcFullRange.minX), + Math.min(srcRange.maxX, srcFullRange.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. @@ -137,14 +155,14 @@ ol.reproj.Tile = function(sourceProj, sourceTileGrid, this.state = ol.TileState.ERROR; return; } - for (var srcX = srcRange.minX; srcX <= srcRange.maxX; srcX++) { + goog.array.forEach(xRange, function(srcX, i, arr) { for (var srcY = srcRange.minY; srcY <= srcRange.maxY; srcY++) { var tile = getTileFunction(this.srcZ_, srcX, srcY, pixelRatio); if (tile) { this.srcTiles_.push(tile); } } - } + }, this); if (this.srcTiles_.length === 0) { this.state = ol.TileState.EMPTY; diff --git a/src/ol/reproj/triangulation.js b/src/ol/reproj/triangulation.js index aaecb83684..786cddee60 100644 --- a/src/ol/reproj/triangulation.js +++ b/src/ol/reproj/triangulation.js @@ -24,13 +24,13 @@ ol.reproj.Triangulation; * @param {ol.Coordinate} a * @param {ol.Coordinate} b * @param {ol.Coordinate} c - * @param {ol.TransformFunction} transformFwd Forward transform (src -> dst). - * @param {ol.TransformFunction} transformInv Inverse transform (dst -> src). + * @param {ol.proj.Projection} sourceProj + * @param {ol.proj.Projection} targetProj * @param {ol.Extent=} opt_maxTargetExtent * @param {ol.Extent=} opt_maxSourceExtent */ ol.reproj.triangulation.addTriangleIfValid_ = function(triangulation, a, b, c, - transformFwd, transformInv, opt_maxTargetExtent, opt_maxSourceExtent) { + sourceProj, targetProj, opt_maxTargetExtent, opt_maxSourceExtent) { if (goog.isDefAndNotNull(opt_maxTargetExtent)) { if (!ol.extent.containsCoordinate(opt_maxTargetExtent, a) && !ol.extent.containsCoordinate(opt_maxTargetExtent, b) && @@ -43,6 +43,7 @@ ol.reproj.triangulation.addTriangleIfValid_ = function(triangulation, a, b, c, b = ol.extent.closestCoordinate(opt_maxTargetExtent, b); c = ol.extent.closestCoordinate(opt_maxTargetExtent, c); } + var transformInv = ol.proj.getTransform(targetProj, sourceProj); var aSrc = transformInv(a); var bSrc = transformInv(b); var cSrc = transformInv(c); @@ -62,12 +63,56 @@ ol.reproj.triangulation.addTriangleIfValid_ = function(triangulation, a, b, c, aSrc = ol.extent.closestCoordinate(opt_maxSourceExtent, aSrc); bSrc = ol.extent.closestCoordinate(opt_maxSourceExtent, bSrc); cSrc = ol.extent.closestCoordinate(opt_maxSourceExtent, cSrc); + var transformFwd = ol.proj.getTransform(sourceProj, targetProj); a = transformFwd(aSrc); b = transformFwd(bSrc); c = transformFwd(cSrc); } } - triangulation.push([[aSrc, a], [bSrc, b], [cSrc, c]]); + var shiftDistance = 0; + if (sourceProj.isGlobal()) { + // determine if the triangle crosses the dateline here + // This can be detected by transforming centroid of the target triangle. + // If the transformed centroid is outside the transformed triangle, + // the triangle wraps around projection extent. + // In such case, the + + var srcExtent = ol.extent.createEmpty(); + ol.extent.extendCoordinate(srcExtent, aSrc); + ol.extent.extendCoordinate(srcExtent, bSrc); + ol.extent.extendCoordinate(srcExtent, cSrc); + + var centroid = [(a[0] + b[0] + c[0]) / 3, + (a[1] + b[1] + c[1]) / 3]; + var centroidSrc = transformInv(centroid); + + var pInTriangle = function(p, p0, p1, p2) { + //TODO: move somewhere else + var A = (-p1[1] * p2[0] + p0[1] * (-p1[0] + p2[0]) + + p0[0] * (p1[1] - p2[1]) + p1[0] * p2[1]) / 2; + var sign = A < 0 ? -1 : 1; + var s = (p0[1] * p2[0] - p0[0] * p2[1] + + (p2[1] - p0[1]) * p[0] + + (p0[0] - p2[0]) * p[1]) * sign; + var t = (p0[0] * p1[1] - p0[1] * p1[0] + + (p0[1] - p1[1]) * p[0] + + (p1[0] - p0[0]) * p[1]) * sign; + + return s > 0 && t > 0 && (s + t) < 2 * A * sign; + }; + + if (!pInTriangle(centroidSrc, aSrc, bSrc, cSrc)) { + var sourceProjExtent = sourceProj.getExtent(); + shiftDistance = ol.extent.getWidth(sourceProjExtent); + } + } + var tri = [[aSrc, a], [bSrc, b], [cSrc, c]]; + // TODO: typing ! do not add properties to arrays ! + tri.shiftDistance = shiftDistance; + if (shiftDistance) { + triangulation.shiftDistance = shiftDistance; + } + triangulation.push(tri); }; @@ -85,9 +130,6 @@ ol.reproj.triangulation.addTriangleIfValid_ = function(triangulation, a, b, c, ol.reproj.triangulation.createForExtent = function(extent, sourceProj, targetProj, opt_maxTargetExtent, opt_maxSourceExtent, opt_subdiv) { - var transformFwd = ol.proj.getTransform(sourceProj, targetProj); - var transformInv = ol.proj.getTransform(targetProj, sourceProj); - var triangulation = []; var tlDst = ol.extent.getTopLeft(extent); @@ -118,10 +160,10 @@ ol.reproj.triangulation.createForExtent = function(extent, sourceProj, ol.reproj.triangulation.addTriangleIfValid_( triangulation, x0y0dst, x1y1dst, x0y1dst, - transformFwd, transformInv, opt_maxTargetExtent, opt_maxSourceExtent); + sourceProj, targetProj, opt_maxTargetExtent, opt_maxSourceExtent); ol.reproj.triangulation.addTriangleIfValid_( triangulation, x0y0dst, x1y0dst, x1y1dst, - transformFwd, transformInv, opt_maxTargetExtent, opt_maxSourceExtent); + sourceProj, targetProj, opt_maxTargetExtent, opt_maxSourceExtent); } } @@ -136,11 +178,28 @@ ol.reproj.triangulation.createForExtent = function(extent, sourceProj, ol.reproj.triangulation.getSourceExtent = function(triangulation) { var extent = ol.extent.createEmpty(); - goog.array.forEach(triangulation, function(triangle, i, arr) { - ol.extent.extendCoordinate(extent, triangle[0][0]); - ol.extent.extendCoordinate(extent, triangle[1][0]); - ol.extent.extendCoordinate(extent, triangle[2][0]); - }); + if (triangulation.shiftDistance) { + var shiftDistance = triangulation.shiftDistance; + goog.array.forEach(triangulation, function(triangle, i, arr) { + ol.extent.extendCoordinate(extent, + [goog.math.modulo(triangle[0][0][0] + shiftDistance, shiftDistance), + triangle[0][0][1]]); + ol.extent.extendCoordinate(extent, + [goog.math.modulo(triangle[1][0][0] + shiftDistance, shiftDistance), + triangle[1][0][1]]); + ol.extent.extendCoordinate(extent, + [goog.math.modulo(triangle[2][0][0] + shiftDistance, shiftDistance), + triangle[2][0][1]]); + }); + if (extent[0] > shiftDistance / 2) extent[0] -= shiftDistance; + if (extent[2] > shiftDistance / 2) extent[2] -= shiftDistance; + } else { + goog.array.forEach(triangulation, function(triangle, i, arr) { + ol.extent.extendCoordinate(extent, triangle[0][0]); + ol.extent.extendCoordinate(extent, triangle[1][0]); + ol.extent.extendCoordinate(extent, triangle[2][0]); + }); + } return extent; };