Automated class transform

npx lebab --replace src --transform class
This commit is contained in:
Tim Schaub
2018-07-16 16:18:16 -06:00
parent 60e85e7d89
commit 7b4a73f3b9
145 changed files with 32887 additions and 33714 deletions
+149 -145
View File
@@ -32,171 +32,175 @@ import Triangulation from '../reproj/Triangulation.js';
* @param {module:ol/reproj/Image~FunctionType} getImageFunction
* Function returning source images (extent, resolution, pixelRatio).
*/
const ReprojImage = function(sourceProj, targetProj,
targetExtent, targetResolution, pixelRatio, getImageFunction) {
class ReprojImage {
constructor(
sourceProj,
targetProj,
targetExtent,
targetResolution,
pixelRatio,
getImageFunction
) {
/**
* @private
* @type {module:ol/proj/Projection}
*/
this.targetProj_ = targetProj;
/**
* @private
* @type {module:ol/proj/Projection}
*/
this.targetProj_ = targetProj;
/**
* @private
* @type {module:ol/extent~Extent}
*/
this.maxSourceExtent_ = sourceProj.getExtent();
const maxTargetExtent = targetProj.getExtent();
/**
* @private
* @type {module:ol/extent~Extent}
*/
this.maxSourceExtent_ = sourceProj.getExtent();
const maxTargetExtent = targetProj.getExtent();
const limitedTargetExtent = maxTargetExtent ?
getIntersection(targetExtent, maxTargetExtent) : targetExtent;
const limitedTargetExtent = maxTargetExtent ?
getIntersection(targetExtent, maxTargetExtent) : targetExtent;
const targetCenter = getCenter(limitedTargetExtent);
const sourceResolution = calculateSourceResolution(
sourceProj, targetProj, targetCenter, targetResolution);
const targetCenter = getCenter(limitedTargetExtent);
const sourceResolution = calculateSourceResolution(
sourceProj, targetProj, targetCenter, targetResolution);
const errorThresholdInPixels = ERROR_THRESHOLD;
const errorThresholdInPixels = ERROR_THRESHOLD;
/**
* @private
* @type {!module:ol/reproj/Triangulation}
*/
this.triangulation_ = new Triangulation(
sourceProj, targetProj, limitedTargetExtent, this.maxSourceExtent_,
sourceResolution * errorThresholdInPixels);
/**
* @private
* @type {!module:ol/reproj/Triangulation}
*/
this.triangulation_ = new Triangulation(
sourceProj, targetProj, limitedTargetExtent, this.maxSourceExtent_,
sourceResolution * errorThresholdInPixels);
/**
* @private
* @type {number}
*/
this.targetResolution_ = targetResolution;
/**
* @private
* @type {number}
*/
this.targetResolution_ = targetResolution;
/**
* @private
* @type {module:ol/extent~Extent}
*/
this.targetExtent_ = targetExtent;
/**
* @private
* @type {module:ol/extent~Extent}
*/
this.targetExtent_ = targetExtent;
const sourceExtent = this.triangulation_.calculateSourceExtent();
const sourceExtent = this.triangulation_.calculateSourceExtent();
/**
* @private
* @type {module:ol/ImageBase}
*/
this.sourceImage_ =
getImageFunction(sourceExtent, sourceResolution, pixelRatio);
/**
* @private
* @type {module:ol/ImageBase}
*/
this.sourceImage_ =
getImageFunction(sourceExtent, sourceResolution, pixelRatio);
/**
* @private
* @type {number}
*/
this.sourcePixelRatio_ =
this.sourceImage_ ? this.sourceImage_.getPixelRatio() : 1;
/**
* @private
* @type {number}
*/
this.sourcePixelRatio_ =
this.sourceImage_ ? this.sourceImage_.getPixelRatio() : 1;
/**
* @private
* @type {HTMLCanvasElement}
*/
this.canvas_ = null;
/**
* @private
* @type {HTMLCanvasElement}
*/
this.canvas_ = null;
/**
* @private
* @type {?module:ol/events~EventsKey}
*/
this.sourceListenerKey_ = null;
/**
* @private
* @type {?module:ol/events~EventsKey}
*/
this.sourceListenerKey_ = null;
let state = ImageState.LOADED;
let state = ImageState.LOADED;
if (this.sourceImage_) {
state = ImageState.IDLE;
if (this.sourceImage_) {
state = ImageState.IDLE;
}
ImageBase.call(this, targetExtent, targetResolution, this.sourcePixelRatio_, state);
}
ImageBase.call(this, targetExtent, targetResolution, this.sourcePixelRatio_, state);
};
/**
* @inheritDoc
*/
disposeInternal() {
if (this.state == ImageState.LOADING) {
this.unlistenSource_();
}
ImageBase.prototype.disposeInternal.call(this);
}
/**
* @inheritDoc
*/
getImage() {
return this.canvas_;
}
/**
* @return {module:ol/proj/Projection} Projection.
*/
getProjection() {
return this.targetProj_;
}
/**
* @private
*/
reproject_() {
const sourceState = this.sourceImage_.getState();
if (sourceState == ImageState.LOADED) {
const width = getWidth(this.targetExtent_) / this.targetResolution_;
const height = getHeight(this.targetExtent_) / this.targetResolution_;
this.canvas_ = renderReprojected(width, height, this.sourcePixelRatio_,
this.sourceImage_.getResolution(), this.maxSourceExtent_,
this.targetResolution_, this.targetExtent_, this.triangulation_, [{
extent: this.sourceImage_.getExtent(),
image: this.sourceImage_.getImage()
}], 0);
}
this.state = sourceState;
this.changed();
}
/**
* @inheritDoc
*/
load() {
if (this.state == ImageState.IDLE) {
this.state = ImageState.LOADING;
this.changed();
const sourceState = this.sourceImage_.getState();
if (sourceState == ImageState.LOADED || sourceState == ImageState.ERROR) {
this.reproject_();
} else {
this.sourceListenerKey_ = listen(this.sourceImage_,
EventType.CHANGE, function(e) {
const sourceState = this.sourceImage_.getState();
if (sourceState == ImageState.LOADED || sourceState == ImageState.ERROR) {
this.unlistenSource_();
this.reproject_();
}
}, this);
this.sourceImage_.load();
}
}
}
/**
* @private
*/
unlistenSource_() {
unlistenByKey(/** @type {!module:ol/events~EventsKey} */ (this.sourceListenerKey_));
this.sourceListenerKey_ = null;
}
}
inherits(ReprojImage, ImageBase);
/**
* @inheritDoc
*/
ReprojImage.prototype.disposeInternal = function() {
if (this.state == ImageState.LOADING) {
this.unlistenSource_();
}
ImageBase.prototype.disposeInternal.call(this);
};
/**
* @inheritDoc
*/
ReprojImage.prototype.getImage = function() {
return this.canvas_;
};
/**
* @return {module:ol/proj/Projection} Projection.
*/
ReprojImage.prototype.getProjection = function() {
return this.targetProj_;
};
/**
* @private
*/
ReprojImage.prototype.reproject_ = function() {
const sourceState = this.sourceImage_.getState();
if (sourceState == ImageState.LOADED) {
const width = getWidth(this.targetExtent_) / this.targetResolution_;
const height = getHeight(this.targetExtent_) / this.targetResolution_;
this.canvas_ = renderReprojected(width, height, this.sourcePixelRatio_,
this.sourceImage_.getResolution(), this.maxSourceExtent_,
this.targetResolution_, this.targetExtent_, this.triangulation_, [{
extent: this.sourceImage_.getExtent(),
image: this.sourceImage_.getImage()
}], 0);
}
this.state = sourceState;
this.changed();
};
/**
* @inheritDoc
*/
ReprojImage.prototype.load = function() {
if (this.state == ImageState.IDLE) {
this.state = ImageState.LOADING;
this.changed();
const sourceState = this.sourceImage_.getState();
if (sourceState == ImageState.LOADED || sourceState == ImageState.ERROR) {
this.reproject_();
} else {
this.sourceListenerKey_ = listen(this.sourceImage_,
EventType.CHANGE, function(e) {
const sourceState = this.sourceImage_.getState();
if (sourceState == ImageState.LOADED || sourceState == ImageState.ERROR) {
this.unlistenSource_();
this.reproject_();
}
}, this);
this.sourceImage_.load();
}
}
};
/**
* @private
*/
ReprojImage.prototype.unlistenSource_ = function() {
unlistenByKey(/** @type {!module:ol/events~EventsKey} */ (this.sourceListenerKey_));
this.sourceListenerKey_ = null;
};
export default ReprojImage;
+254 -246
View File
@@ -38,275 +38,283 @@ import Triangulation from '../reproj/Triangulation.js';
* @param {number=} opt_errorThreshold Acceptable reprojection error (in px).
* @param {boolean=} opt_renderEdges Render reprojection edges.
*/
const ReprojTile = function(sourceProj, sourceTileGrid,
targetProj, targetTileGrid, tileCoord, wrappedTileCoord,
pixelRatio, gutter, getTileFunction,
opt_errorThreshold, opt_renderEdges) {
Tile.call(this, tileCoord, TileState.IDLE);
class ReprojTile {
constructor(
sourceProj,
sourceTileGrid,
targetProj,
targetTileGrid,
tileCoord,
wrappedTileCoord,
pixelRatio,
gutter,
getTileFunction,
opt_errorThreshold,
opt_renderEdges
) {
Tile.call(this, tileCoord, TileState.IDLE);
/**
* @private
* @type {boolean}
*/
this.renderEdges_ = opt_renderEdges !== undefined ? opt_renderEdges : false;
/**
* @private
* @type {boolean}
*/
this.renderEdges_ = opt_renderEdges !== undefined ? opt_renderEdges : false;
/**
* @private
* @type {number}
*/
this.pixelRatio_ = pixelRatio;
/**
* @private
* @type {number}
*/
this.pixelRatio_ = pixelRatio;
/**
* @private
* @type {number}
*/
this.gutter_ = gutter;
/**
* @private
* @type {number}
*/
this.gutter_ = gutter;
/**
* @private
* @type {HTMLCanvasElement}
*/
this.canvas_ = null;
/**
* @private
* @type {HTMLCanvasElement}
*/
this.canvas_ = null;
/**
* @private
* @type {module:ol/tilegrid/TileGrid}
*/
this.sourceTileGrid_ = sourceTileGrid;
/**
* @private
* @type {module:ol/tilegrid/TileGrid}
*/
this.sourceTileGrid_ = sourceTileGrid;
/**
* @private
* @type {module:ol/tilegrid/TileGrid}
*/
this.targetTileGrid_ = targetTileGrid;
/**
* @private
* @type {module:ol/tilegrid/TileGrid}
*/
this.targetTileGrid_ = targetTileGrid;
/**
* @private
* @type {module:ol/tilecoord~TileCoord}
*/
this.wrappedTileCoord_ = wrappedTileCoord ? wrappedTileCoord : tileCoord;
/**
* @private
* @type {module:ol/tilecoord~TileCoord}
*/
this.wrappedTileCoord_ = wrappedTileCoord ? wrappedTileCoord : tileCoord;
/**
* @private
* @type {!Array.<module:ol/Tile>}
*/
this.sourceTiles_ = [];
/**
* @private
* @type {!Array.<module:ol/Tile>}
*/
this.sourceTiles_ = [];
/**
* @private
* @type {Array.<module:ol/events~EventsKey>}
*/
this.sourcesListenerKeys_ = null;
/**
* @private
* @type {Array.<module:ol/events~EventsKey>}
*/
this.sourcesListenerKeys_ = null;
/**
* @private
* @type {number}
*/
this.sourceZ_ = 0;
/**
* @private
* @type {number}
*/
this.sourceZ_ = 0;
const targetExtent = targetTileGrid.getTileCoordExtent(this.wrappedTileCoord_);
const maxTargetExtent = this.targetTileGrid_.getExtent();
let maxSourceExtent = this.sourceTileGrid_.getExtent();
const targetExtent = targetTileGrid.getTileCoordExtent(this.wrappedTileCoord_);
const maxTargetExtent = this.targetTileGrid_.getExtent();
let maxSourceExtent = this.sourceTileGrid_.getExtent();
const limitedTargetExtent = maxTargetExtent ?
getIntersection(targetExtent, maxTargetExtent) : targetExtent;
const limitedTargetExtent = maxTargetExtent ?
getIntersection(targetExtent, maxTargetExtent) : targetExtent;
if (getArea(limitedTargetExtent) === 0) {
// Tile is completely outside range -> EMPTY
// TODO: is it actually correct that the source even creates the tile ?
this.state = TileState.EMPTY;
return;
}
const sourceProjExtent = sourceProj.getExtent();
if (sourceProjExtent) {
if (!maxSourceExtent) {
maxSourceExtent = sourceProjExtent;
} else {
maxSourceExtent = getIntersection(maxSourceExtent, sourceProjExtent);
if (getArea(limitedTargetExtent) === 0) {
// Tile is completely outside range -> EMPTY
// TODO: is it actually correct that the source even creates the tile ?
this.state = TileState.EMPTY;
return;
}
}
const targetResolution = targetTileGrid.getResolution(
this.wrappedTileCoord_[0]);
const targetCenter = getCenter(limitedTargetExtent);
const sourceResolution = calculateSourceResolution(
sourceProj, targetProj, targetCenter, targetResolution);
if (!isFinite(sourceResolution) || sourceResolution <= 0) {
// invalid sourceResolution -> EMPTY
// probably edges of the projections when no extent is defined
this.state = TileState.EMPTY;
return;
}
const errorThresholdInPixels = opt_errorThreshold !== undefined ?
opt_errorThreshold : ERROR_THRESHOLD;
/**
* @private
* @type {!module:ol/reproj/Triangulation}
*/
this.triangulation_ = new Triangulation(
sourceProj, targetProj, limitedTargetExtent, maxSourceExtent,
sourceResolution * errorThresholdInPixels);
if (this.triangulation_.getTriangles().length === 0) {
// no valid triangles -> EMPTY
this.state = TileState.EMPTY;
return;
}
this.sourceZ_ = sourceTileGrid.getZForResolution(sourceResolution);
let sourceExtent = this.triangulation_.calculateSourceExtent();
if (maxSourceExtent) {
if (sourceProj.canWrapX()) {
sourceExtent[1] = clamp(
sourceExtent[1], maxSourceExtent[1], maxSourceExtent[3]);
sourceExtent[3] = clamp(
sourceExtent[3], maxSourceExtent[1], maxSourceExtent[3]);
} else {
sourceExtent = getIntersection(sourceExtent, maxSourceExtent);
}
}
if (!getArea(sourceExtent)) {
this.state = TileState.EMPTY;
} else {
const sourceRange = sourceTileGrid.getTileRangeForExtentAndZ(
sourceExtent, this.sourceZ_);
for (let srcX = sourceRange.minX; srcX <= sourceRange.maxX; srcX++) {
for (let srcY = sourceRange.minY; srcY <= sourceRange.maxY; srcY++) {
const tile = getTileFunction(this.sourceZ_, srcX, srcY, pixelRatio);
if (tile) {
this.sourceTiles_.push(tile);
}
const sourceProjExtent = sourceProj.getExtent();
if (sourceProjExtent) {
if (!maxSourceExtent) {
maxSourceExtent = sourceProjExtent;
} else {
maxSourceExtent = getIntersection(maxSourceExtent, sourceProjExtent);
}
}
if (this.sourceTiles_.length === 0) {
const targetResolution = targetTileGrid.getResolution(
this.wrappedTileCoord_[0]);
const targetCenter = getCenter(limitedTargetExtent);
const sourceResolution = calculateSourceResolution(
sourceProj, targetProj, targetCenter, targetResolution);
if (!isFinite(sourceResolution) || sourceResolution <= 0) {
// invalid sourceResolution -> EMPTY
// probably edges of the projections when no extent is defined
this.state = TileState.EMPTY;
return;
}
const errorThresholdInPixels = opt_errorThreshold !== undefined ?
opt_errorThreshold : ERROR_THRESHOLD;
/**
* @private
* @type {!module:ol/reproj/Triangulation}
*/
this.triangulation_ = new Triangulation(
sourceProj, targetProj, limitedTargetExtent, maxSourceExtent,
sourceResolution * errorThresholdInPixels);
if (this.triangulation_.getTriangles().length === 0) {
// no valid triangles -> EMPTY
this.state = TileState.EMPTY;
return;
}
this.sourceZ_ = sourceTileGrid.getZForResolution(sourceResolution);
let sourceExtent = this.triangulation_.calculateSourceExtent();
if (maxSourceExtent) {
if (sourceProj.canWrapX()) {
sourceExtent[1] = clamp(
sourceExtent[1], maxSourceExtent[1], maxSourceExtent[3]);
sourceExtent[3] = clamp(
sourceExtent[3], maxSourceExtent[1], maxSourceExtent[3]);
} else {
sourceExtent = getIntersection(sourceExtent, maxSourceExtent);
}
}
if (!getArea(sourceExtent)) {
this.state = TileState.EMPTY;
} else {
const sourceRange = sourceTileGrid.getTileRangeForExtentAndZ(
sourceExtent, this.sourceZ_);
for (let srcX = sourceRange.minX; srcX <= sourceRange.maxX; srcX++) {
for (let srcY = sourceRange.minY; srcY <= sourceRange.maxY; srcY++) {
const tile = getTileFunction(this.sourceZ_, srcX, srcY, pixelRatio);
if (tile) {
this.sourceTiles_.push(tile);
}
}
}
if (this.sourceTiles_.length === 0) {
this.state = TileState.EMPTY;
}
}
}
};
/**
* @inheritDoc
*/
disposeInternal() {
if (this.state == TileState.LOADING) {
this.unlistenSources_();
}
Tile.prototype.disposeInternal.call(this);
}
/**
* Get the HTML Canvas element for this tile.
* @return {HTMLCanvasElement} Canvas.
*/
getImage() {
return this.canvas_;
}
/**
* @private
*/
reproject_() {
const sources = [];
this.sourceTiles_.forEach(function(tile, i, arr) {
if (tile && tile.getState() == TileState.LOADED) {
sources.push({
extent: this.sourceTileGrid_.getTileCoordExtent(tile.tileCoord),
image: tile.getImage()
});
}
}.bind(this));
this.sourceTiles_.length = 0;
if (sources.length === 0) {
this.state = TileState.ERROR;
} else {
const z = this.wrappedTileCoord_[0];
const size = this.targetTileGrid_.getTileSize(z);
const width = typeof size === 'number' ? size : size[0];
const height = typeof size === 'number' ? size : size[1];
const targetResolution = this.targetTileGrid_.getResolution(z);
const sourceResolution = this.sourceTileGrid_.getResolution(this.sourceZ_);
const targetExtent = this.targetTileGrid_.getTileCoordExtent(
this.wrappedTileCoord_);
this.canvas_ = renderReprojected(width, height, this.pixelRatio_,
sourceResolution, this.sourceTileGrid_.getExtent(),
targetResolution, targetExtent, this.triangulation_, sources,
this.gutter_, this.renderEdges_);
this.state = TileState.LOADED;
}
this.changed();
}
/**
* @inheritDoc
*/
load() {
if (this.state == TileState.IDLE) {
this.state = TileState.LOADING;
this.changed();
let leftToLoad = 0;
this.sourcesListenerKeys_ = [];
this.sourceTiles_.forEach(function(tile, i, arr) {
const state = tile.getState();
if (state == TileState.IDLE || state == TileState.LOADING) {
leftToLoad++;
const sourceListenKey = listen(tile, EventType.CHANGE,
function(e) {
const state = tile.getState();
if (state == TileState.LOADED ||
state == TileState.ERROR ||
state == TileState.EMPTY) {
unlistenByKey(sourceListenKey);
leftToLoad--;
if (leftToLoad === 0) {
this.unlistenSources_();
this.reproject_();
}
}
}, this);
this.sourcesListenerKeys_.push(sourceListenKey);
}
}.bind(this));
this.sourceTiles_.forEach(function(tile, i, arr) {
const state = tile.getState();
if (state == TileState.IDLE) {
tile.load();
}
});
if (leftToLoad === 0) {
setTimeout(this.reproject_.bind(this), 0);
}
}
}
/**
* @private
*/
unlistenSources_() {
this.sourcesListenerKeys_.forEach(unlistenByKey);
this.sourcesListenerKeys_ = null;
}
}
inherits(ReprojTile, Tile);
/**
* @inheritDoc
*/
ReprojTile.prototype.disposeInternal = function() {
if (this.state == TileState.LOADING) {
this.unlistenSources_();
}
Tile.prototype.disposeInternal.call(this);
};
/**
* Get the HTML Canvas element for this tile.
* @return {HTMLCanvasElement} Canvas.
*/
ReprojTile.prototype.getImage = function() {
return this.canvas_;
};
/**
* @private
*/
ReprojTile.prototype.reproject_ = function() {
const sources = [];
this.sourceTiles_.forEach(function(tile, i, arr) {
if (tile && tile.getState() == TileState.LOADED) {
sources.push({
extent: this.sourceTileGrid_.getTileCoordExtent(tile.tileCoord),
image: tile.getImage()
});
}
}.bind(this));
this.sourceTiles_.length = 0;
if (sources.length === 0) {
this.state = TileState.ERROR;
} else {
const z = this.wrappedTileCoord_[0];
const size = this.targetTileGrid_.getTileSize(z);
const width = typeof size === 'number' ? size : size[0];
const height = typeof size === 'number' ? size : size[1];
const targetResolution = this.targetTileGrid_.getResolution(z);
const sourceResolution = this.sourceTileGrid_.getResolution(this.sourceZ_);
const targetExtent = this.targetTileGrid_.getTileCoordExtent(
this.wrappedTileCoord_);
this.canvas_ = renderReprojected(width, height, this.pixelRatio_,
sourceResolution, this.sourceTileGrid_.getExtent(),
targetResolution, targetExtent, this.triangulation_, sources,
this.gutter_, this.renderEdges_);
this.state = TileState.LOADED;
}
this.changed();
};
/**
* @inheritDoc
*/
ReprojTile.prototype.load = function() {
if (this.state == TileState.IDLE) {
this.state = TileState.LOADING;
this.changed();
let leftToLoad = 0;
this.sourcesListenerKeys_ = [];
this.sourceTiles_.forEach(function(tile, i, arr) {
const state = tile.getState();
if (state == TileState.IDLE || state == TileState.LOADING) {
leftToLoad++;
const sourceListenKey = listen(tile, EventType.CHANGE,
function(e) {
const state = tile.getState();
if (state == TileState.LOADED ||
state == TileState.ERROR ||
state == TileState.EMPTY) {
unlistenByKey(sourceListenKey);
leftToLoad--;
if (leftToLoad === 0) {
this.unlistenSources_();
this.reproject_();
}
}
}, this);
this.sourcesListenerKeys_.push(sourceListenKey);
}
}.bind(this));
this.sourceTiles_.forEach(function(tile, i, arr) {
const state = tile.getState();
if (state == TileState.IDLE) {
tile.load();
}
});
if (leftToLoad === 0) {
setTimeout(this.reproject_.bind(this), 0);
}
}
};
/**
* @private
*/
ReprojTile.prototype.unlistenSources_ = function() {
this.sourcesListenerKeys_.forEach(unlistenByKey);
this.sourcesListenerKeys_ = null;
};
export default ReprojTile;
+275 -279
View File
@@ -48,309 +48,305 @@ const MAX_TRIANGLE_WIDTH = 0.25;
* @param {number} errorThreshold Acceptable error (in source units).
* @constructor
*/
const Triangulation = function(sourceProj, targetProj, targetExtent,
maxSourceExtent, errorThreshold) {
class Triangulation {
constructor(sourceProj, targetProj, targetExtent, maxSourceExtent, errorThreshold) {
/**
* @type {module:ol/proj/Projection}
* @private
*/
this.sourceProj_ = sourceProj;
/**
* @type {module:ol/proj/Projection}
* @private
*/
this.sourceProj_ = sourceProj;
/**
* @type {module:ol/proj/Projection}
* @private
*/
this.targetProj_ = targetProj;
/**
* @type {module:ol/proj/Projection}
* @private
*/
this.targetProj_ = targetProj;
/** @type {!Object.<string, module:ol/coordinate~Coordinate>} */
let transformInvCache = {};
const transformInv = getTransform(this.targetProj_, this.sourceProj_);
/** @type {!Object.<string, module:ol/coordinate~Coordinate>} */
let transformInvCache = {};
const transformInv = getTransform(this.targetProj_, this.sourceProj_);
/**
* @param {module:ol/coordinate~Coordinate} c A coordinate.
* @return {module:ol/coordinate~Coordinate} Transformed coordinate.
* @private
*/
this.transformInv_ = function(c) {
const key = c[0] + '/' + c[1];
if (!transformInvCache[key]) {
transformInvCache[key] = transformInv(c);
}
return transformInvCache[key];
};
/**
* @type {module:ol/extent~Extent}
* @private
*/
this.maxSourceExtent_ = maxSourceExtent;
/**
* @type {number}
* @private
*/
this.errorThresholdSquared_ = errorThreshold * errorThreshold;
/**
* @type {Array.<module:ol/reproj/Triangulation~Triangle>}
* @private
*/
this.triangles_ = [];
/**
* Indicates that the triangulation crosses edge of the source projection.
* @type {boolean}
* @private
*/
this.wrapsXInSource_ = false;
/**
* @type {boolean}
* @private
*/
this.canWrapXInSource_ = this.sourceProj_.canWrapX() &&
!!maxSourceExtent &&
!!this.sourceProj_.getExtent() &&
(getWidth(maxSourceExtent) == getWidth(this.sourceProj_.getExtent()));
/**
* @type {?number}
* @private
*/
this.sourceWorldWidth_ = this.sourceProj_.getExtent() ?
getWidth(this.sourceProj_.getExtent()) : null;
/**
* @type {?number}
* @private
*/
this.targetWorldWidth_ = this.targetProj_.getExtent() ?
getWidth(this.targetProj_.getExtent()) : null;
const destinationTopLeft = getTopLeft(targetExtent);
const destinationTopRight = getTopRight(targetExtent);
const destinationBottomRight = getBottomRight(targetExtent);
const destinationBottomLeft = getBottomLeft(targetExtent);
const sourceTopLeft = this.transformInv_(destinationTopLeft);
const sourceTopRight = this.transformInv_(destinationTopRight);
const sourceBottomRight = this.transformInv_(destinationBottomRight);
const sourceBottomLeft = this.transformInv_(destinationBottomLeft);
this.addQuad_(
destinationTopLeft, destinationTopRight,
destinationBottomRight, destinationBottomLeft,
sourceTopLeft, sourceTopRight, sourceBottomRight, sourceBottomLeft,
MAX_SUBDIVISION);
if (this.wrapsXInSource_) {
let leftBound = Infinity;
this.triangles_.forEach(function(triangle, i, arr) {
leftBound = Math.min(leftBound,
triangle.source[0][0], triangle.source[1][0], triangle.source[2][0]);
});
// Shift triangles to be as close to `leftBound` as possible
// (if the distance is more than `worldWidth / 2` it can be closer.
this.triangles_.forEach(function(triangle) {
if (Math.max(triangle.source[0][0], triangle.source[1][0],
triangle.source[2][0]) - leftBound > this.sourceWorldWidth_ / 2) {
const newTriangle = [[triangle.source[0][0], triangle.source[0][1]],
[triangle.source[1][0], triangle.source[1][1]],
[triangle.source[2][0], triangle.source[2][1]]];
if ((newTriangle[0][0] - leftBound) > this.sourceWorldWidth_ / 2) {
newTriangle[0][0] -= this.sourceWorldWidth_;
}
if ((newTriangle[1][0] - leftBound) > this.sourceWorldWidth_ / 2) {
newTriangle[1][0] -= this.sourceWorldWidth_;
}
if ((newTriangle[2][0] - leftBound) > this.sourceWorldWidth_ / 2) {
newTriangle[2][0] -= this.sourceWorldWidth_;
}
// Rarely (if the extent contains both the dateline and prime meridian)
// the shift can in turn break some triangles.
// Detect this here and don't shift in such cases.
const minX = Math.min(
newTriangle[0][0], newTriangle[1][0], newTriangle[2][0]);
const maxX = Math.max(
newTriangle[0][0], newTriangle[1][0], newTriangle[2][0]);
if ((maxX - minX) < this.sourceWorldWidth_ / 2) {
triangle.source = newTriangle;
}
/**
* @param {module:ol/coordinate~Coordinate} c A coordinate.
* @return {module:ol/coordinate~Coordinate} Transformed coordinate.
* @private
*/
this.transformInv_ = function(c) {
const key = c[0] + '/' + c[1];
if (!transformInvCache[key]) {
transformInvCache[key] = transformInv(c);
}
}.bind(this));
return transformInvCache[key];
};
/**
* @type {module:ol/extent~Extent}
* @private
*/
this.maxSourceExtent_ = maxSourceExtent;
/**
* @type {number}
* @private
*/
this.errorThresholdSquared_ = errorThreshold * errorThreshold;
/**
* @type {Array.<module:ol/reproj/Triangulation~Triangle>}
* @private
*/
this.triangles_ = [];
/**
* Indicates that the triangulation crosses edge of the source projection.
* @type {boolean}
* @private
*/
this.wrapsXInSource_ = false;
/**
* @type {boolean}
* @private
*/
this.canWrapXInSource_ = this.sourceProj_.canWrapX() &&
!!maxSourceExtent &&
!!this.sourceProj_.getExtent() &&
(getWidth(maxSourceExtent) == getWidth(this.sourceProj_.getExtent()));
/**
* @type {?number}
* @private
*/
this.sourceWorldWidth_ = this.sourceProj_.getExtent() ?
getWidth(this.sourceProj_.getExtent()) : null;
/**
* @type {?number}
* @private
*/
this.targetWorldWidth_ = this.targetProj_.getExtent() ?
getWidth(this.targetProj_.getExtent()) : null;
const destinationTopLeft = getTopLeft(targetExtent);
const destinationTopRight = getTopRight(targetExtent);
const destinationBottomRight = getBottomRight(targetExtent);
const destinationBottomLeft = getBottomLeft(targetExtent);
const sourceTopLeft = this.transformInv_(destinationTopLeft);
const sourceTopRight = this.transformInv_(destinationTopRight);
const sourceBottomRight = this.transformInv_(destinationBottomRight);
const sourceBottomLeft = this.transformInv_(destinationBottomLeft);
this.addQuad_(
destinationTopLeft, destinationTopRight,
destinationBottomRight, destinationBottomLeft,
sourceTopLeft, sourceTopRight, sourceBottomRight, sourceBottomLeft,
MAX_SUBDIVISION);
if (this.wrapsXInSource_) {
let leftBound = Infinity;
this.triangles_.forEach(function(triangle, i, arr) {
leftBound = Math.min(leftBound,
triangle.source[0][0], triangle.source[1][0], triangle.source[2][0]);
});
// Shift triangles to be as close to `leftBound` as possible
// (if the distance is more than `worldWidth / 2` it can be closer.
this.triangles_.forEach(function(triangle) {
if (Math.max(triangle.source[0][0], triangle.source[1][0],
triangle.source[2][0]) - leftBound > this.sourceWorldWidth_ / 2) {
const newTriangle = [[triangle.source[0][0], triangle.source[0][1]],
[triangle.source[1][0], triangle.source[1][1]],
[triangle.source[2][0], triangle.source[2][1]]];
if ((newTriangle[0][0] - leftBound) > this.sourceWorldWidth_ / 2) {
newTriangle[0][0] -= this.sourceWorldWidth_;
}
if ((newTriangle[1][0] - leftBound) > this.sourceWorldWidth_ / 2) {
newTriangle[1][0] -= this.sourceWorldWidth_;
}
if ((newTriangle[2][0] - leftBound) > this.sourceWorldWidth_ / 2) {
newTriangle[2][0] -= this.sourceWorldWidth_;
}
// Rarely (if the extent contains both the dateline and prime meridian)
// the shift can in turn break some triangles.
// Detect this here and don't shift in such cases.
const minX = Math.min(
newTriangle[0][0], newTriangle[1][0], newTriangle[2][0]);
const maxX = Math.max(
newTriangle[0][0], newTriangle[1][0], newTriangle[2][0]);
if ((maxX - minX) < this.sourceWorldWidth_ / 2) {
triangle.source = newTriangle;
}
}
}.bind(this));
}
transformInvCache = {};
}
transformInvCache = {};
};
/**
* Adds triangle to the triangulation.
* @param {module:ol/coordinate~Coordinate} a The target a coordinate.
* @param {module:ol/coordinate~Coordinate} b The target b coordinate.
* @param {module:ol/coordinate~Coordinate} c The target c coordinate.
* @param {module:ol/coordinate~Coordinate} aSrc The source a coordinate.
* @param {module:ol/coordinate~Coordinate} bSrc The source b coordinate.
* @param {module:ol/coordinate~Coordinate} cSrc The source c coordinate.
* @private
*/
Triangulation.prototype.addTriangle_ = function(a, b, c,
aSrc, bSrc, cSrc) {
this.triangles_.push({
source: [aSrc, bSrc, cSrc],
target: [a, b, c]
});
};
/**
* Adds quad (points in clock-wise order) to the triangulation
* (and reprojects the vertices) if valid.
* Performs quad subdivision if needed to increase precision.
*
* @param {module:ol/coordinate~Coordinate} a The target a coordinate.
* @param {module:ol/coordinate~Coordinate} b The target b coordinate.
* @param {module:ol/coordinate~Coordinate} c The target c coordinate.
* @param {module:ol/coordinate~Coordinate} d The target d coordinate.
* @param {module:ol/coordinate~Coordinate} aSrc The source a coordinate.
* @param {module:ol/coordinate~Coordinate} bSrc The source b coordinate.
* @param {module:ol/coordinate~Coordinate} cSrc The source c coordinate.
* @param {module:ol/coordinate~Coordinate} dSrc The source d coordinate.
* @param {number} maxSubdivision Maximal allowed subdivision of the quad.
* @private
*/
Triangulation.prototype.addQuad_ = function(a, b, c, d,
aSrc, bSrc, cSrc, dSrc, maxSubdivision) {
const sourceQuadExtent = boundingExtent([aSrc, bSrc, cSrc, dSrc]);
const sourceCoverageX = this.sourceWorldWidth_ ?
getWidth(sourceQuadExtent) / this.sourceWorldWidth_ : null;
const sourceWorldWidth = /** @type {number} */ (this.sourceWorldWidth_);
// when the quad is wrapped in the source projection
// it covers most of the projection extent, but not fully
const wrapsX = this.sourceProj_.canWrapX() &&
sourceCoverageX > 0.5 && sourceCoverageX < 1;
let needsSubdivision = false;
if (maxSubdivision > 0) {
if (this.targetProj_.isGlobal() && this.targetWorldWidth_) {
const targetQuadExtent = boundingExtent([a, b, c, d]);
const targetCoverageX = getWidth(targetQuadExtent) / this.targetWorldWidth_;
needsSubdivision |=
targetCoverageX > MAX_TRIANGLE_WIDTH;
}
if (!wrapsX && this.sourceProj_.isGlobal() && sourceCoverageX) {
needsSubdivision |=
sourceCoverageX > MAX_TRIANGLE_WIDTH;
}
/**
* Adds triangle to the triangulation.
* @param {module:ol/coordinate~Coordinate} a The target a coordinate.
* @param {module:ol/coordinate~Coordinate} b The target b coordinate.
* @param {module:ol/coordinate~Coordinate} c The target c coordinate.
* @param {module:ol/coordinate~Coordinate} aSrc The source a coordinate.
* @param {module:ol/coordinate~Coordinate} bSrc The source b coordinate.
* @param {module:ol/coordinate~Coordinate} cSrc The source c coordinate.
* @private
*/
addTriangle_(a, b, c, aSrc, bSrc, cSrc) {
this.triangles_.push({
source: [aSrc, bSrc, cSrc],
target: [a, b, c]
});
}
if (!needsSubdivision && this.maxSourceExtent_) {
if (!intersects(sourceQuadExtent, this.maxSourceExtent_)) {
// whole quad outside source projection extent -> ignore
return;
}
}
/**
* Adds quad (points in clock-wise order) to the triangulation
* (and reprojects the vertices) if valid.
* Performs quad subdivision if needed to increase precision.
*
* @param {module:ol/coordinate~Coordinate} a The target a coordinate.
* @param {module:ol/coordinate~Coordinate} b The target b coordinate.
* @param {module:ol/coordinate~Coordinate} c The target c coordinate.
* @param {module:ol/coordinate~Coordinate} d The target d coordinate.
* @param {module:ol/coordinate~Coordinate} aSrc The source a coordinate.
* @param {module:ol/coordinate~Coordinate} bSrc The source b coordinate.
* @param {module:ol/coordinate~Coordinate} cSrc The source c coordinate.
* @param {module:ol/coordinate~Coordinate} dSrc The source d coordinate.
* @param {number} maxSubdivision Maximal allowed subdivision of the quad.
* @private
*/
addQuad_(a, b, c, d, aSrc, bSrc, cSrc, dSrc, maxSubdivision) {
if (!needsSubdivision) {
if (!isFinite(aSrc[0]) || !isFinite(aSrc[1]) ||
!isFinite(bSrc[0]) || !isFinite(bSrc[1]) ||
!isFinite(cSrc[0]) || !isFinite(cSrc[1]) ||
!isFinite(dSrc[0]) || !isFinite(dSrc[1])) {
if (maxSubdivision > 0) {
needsSubdivision = true;
} else {
const sourceQuadExtent = boundingExtent([aSrc, bSrc, cSrc, dSrc]);
const sourceCoverageX = this.sourceWorldWidth_ ?
getWidth(sourceQuadExtent) / this.sourceWorldWidth_ : null;
const sourceWorldWidth = /** @type {number} */ (this.sourceWorldWidth_);
// when the quad is wrapped in the source projection
// it covers most of the projection extent, but not fully
const wrapsX = this.sourceProj_.canWrapX() &&
sourceCoverageX > 0.5 && sourceCoverageX < 1;
let needsSubdivision = false;
if (maxSubdivision > 0) {
if (this.targetProj_.isGlobal() && this.targetWorldWidth_) {
const targetQuadExtent = boundingExtent([a, b, c, d]);
const targetCoverageX = getWidth(targetQuadExtent) / this.targetWorldWidth_;
needsSubdivision |=
targetCoverageX > MAX_TRIANGLE_WIDTH;
}
if (!wrapsX && this.sourceProj_.isGlobal() && sourceCoverageX) {
needsSubdivision |=
sourceCoverageX > MAX_TRIANGLE_WIDTH;
}
}
if (!needsSubdivision && this.maxSourceExtent_) {
if (!intersects(sourceQuadExtent, this.maxSourceExtent_)) {
// whole quad outside source projection extent -> ignore
return;
}
}
}
if (maxSubdivision > 0) {
if (!needsSubdivision) {
const center = [(a[0] + c[0]) / 2, (a[1] + c[1]) / 2];
const centerSrc = this.transformInv_(center);
let dx;
if (wrapsX) {
const centerSrcEstimX =
(modulo(aSrc[0], sourceWorldWidth) +
modulo(cSrc[0], sourceWorldWidth)) / 2;
dx = centerSrcEstimX -
modulo(centerSrc[0], sourceWorldWidth);
} else {
dx = (aSrc[0] + cSrc[0]) / 2 - centerSrc[0];
if (!isFinite(aSrc[0]) || !isFinite(aSrc[1]) ||
!isFinite(bSrc[0]) || !isFinite(bSrc[1]) ||
!isFinite(cSrc[0]) || !isFinite(cSrc[1]) ||
!isFinite(dSrc[0]) || !isFinite(dSrc[1])) {
if (maxSubdivision > 0) {
needsSubdivision = true;
} else {
return;
}
}
const dy = (aSrc[1] + cSrc[1]) / 2 - centerSrc[1];
const centerSrcErrorSquared = dx * dx + dy * dy;
needsSubdivision = centerSrcErrorSquared > this.errorThresholdSquared_;
}
if (needsSubdivision) {
if (Math.abs(a[0] - c[0]) <= Math.abs(a[1] - c[1])) {
// split horizontally (top & bottom)
const bc = [(b[0] + c[0]) / 2, (b[1] + c[1]) / 2];
const bcSrc = this.transformInv_(bc);
const da = [(d[0] + a[0]) / 2, (d[1] + a[1]) / 2];
const daSrc = this.transformInv_(da);
this.addQuad_(
a, b, bc, da, aSrc, bSrc, bcSrc, daSrc, maxSubdivision - 1);
this.addQuad_(
da, bc, c, d, daSrc, bcSrc, cSrc, dSrc, maxSubdivision - 1);
} else {
// split vertically (left & right)
const ab = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];
const abSrc = this.transformInv_(ab);
const cd = [(c[0] + d[0]) / 2, (c[1] + d[1]) / 2];
const cdSrc = this.transformInv_(cd);
if (maxSubdivision > 0) {
if (!needsSubdivision) {
const center = [(a[0] + c[0]) / 2, (a[1] + c[1]) / 2];
const centerSrc = this.transformInv_(center);
this.addQuad_(
a, ab, cd, d, aSrc, abSrc, cdSrc, dSrc, maxSubdivision - 1);
this.addQuad_(
ab, b, c, cd, abSrc, bSrc, cSrc, cdSrc, maxSubdivision - 1);
let dx;
if (wrapsX) {
const centerSrcEstimX =
(modulo(aSrc[0], sourceWorldWidth) +
modulo(cSrc[0], sourceWorldWidth)) / 2;
dx = centerSrcEstimX -
modulo(centerSrc[0], sourceWorldWidth);
} else {
dx = (aSrc[0] + cSrc[0]) / 2 - centerSrc[0];
}
const dy = (aSrc[1] + cSrc[1]) / 2 - centerSrc[1];
const centerSrcErrorSquared = dx * dx + dy * dy;
needsSubdivision = centerSrcErrorSquared > this.errorThresholdSquared_;
}
if (needsSubdivision) {
if (Math.abs(a[0] - c[0]) <= Math.abs(a[1] - c[1])) {
// split horizontally (top & bottom)
const bc = [(b[0] + c[0]) / 2, (b[1] + c[1]) / 2];
const bcSrc = this.transformInv_(bc);
const da = [(d[0] + a[0]) / 2, (d[1] + a[1]) / 2];
const daSrc = this.transformInv_(da);
this.addQuad_(
a, b, bc, da, aSrc, bSrc, bcSrc, daSrc, maxSubdivision - 1);
this.addQuad_(
da, bc, c, d, daSrc, bcSrc, cSrc, dSrc, maxSubdivision - 1);
} else {
// split vertically (left & right)
const ab = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];
const abSrc = this.transformInv_(ab);
const cd = [(c[0] + d[0]) / 2, (c[1] + d[1]) / 2];
const cdSrc = this.transformInv_(cd);
this.addQuad_(
a, ab, cd, d, aSrc, abSrc, cdSrc, dSrc, maxSubdivision - 1);
this.addQuad_(
ab, b, c, cd, abSrc, bSrc, cSrc, cdSrc, maxSubdivision - 1);
}
return;
}
return;
}
if (wrapsX) {
if (!this.canWrapXInSource_) {
return;
}
this.wrapsXInSource_ = true;
}
this.addTriangle_(a, c, d, aSrc, cSrc, dSrc);
this.addTriangle_(a, b, c, aSrc, bSrc, cSrc);
}
if (wrapsX) {
if (!this.canWrapXInSource_) {
return;
}
this.wrapsXInSource_ = true;
/**
* Calculates extent of the 'source' coordinates from all the triangles.
*
* @return {module:ol/extent~Extent} Calculated extent.
*/
calculateSourceExtent() {
const extent = createEmpty();
this.triangles_.forEach(function(triangle, i, arr) {
const src = triangle.source;
extendCoordinate(extent, src[0]);
extendCoordinate(extent, src[1]);
extendCoordinate(extent, src[2]);
});
return extent;
}
this.addTriangle_(a, c, d, aSrc, cSrc, dSrc);
this.addTriangle_(a, b, c, aSrc, bSrc, cSrc);
};
/**
* @return {Array.<module:ol/reproj/Triangulation~Triangle>} Array of the calculated triangles.
*/
getTriangles() {
return this.triangles_;
}
}
/**
* Calculates extent of the 'source' coordinates from all the triangles.
*
* @return {module:ol/extent~Extent} Calculated extent.
*/
Triangulation.prototype.calculateSourceExtent = function() {
const extent = createEmpty();
this.triangles_.forEach(function(triangle, i, arr) {
const src = triangle.source;
extendCoordinate(extent, src[0]);
extendCoordinate(extent, src[1]);
extendCoordinate(extent, src[2]);
});
return extent;
};
/**
* @return {Array.<module:ol/reproj/Triangulation~Triangle>} Array of the calculated triangles.
*/
Triangulation.prototype.getTriangles = function() {
return this.triangles_;
};
export default Triangulation;