Detect and handle triangles (tiles) crossing the dateline (projection edge)
This commit is contained in:
@@ -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();
|
||||
});
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user