@allmaps/render
Version:
Render functions for WebGL and image buffers
351 lines (350 loc) • 11.5 kB
JavaScript
import { sizeToResolution, squaredDistance } from "@allmaps/stdlib";
function getTileZoomLevelForScale(tileZoomLevels, resourceToCanvasScale, scaleFactorCorrection, log2ScaleFactorCorrection) {
let smallestLog2ScaleFactorDiff = Number.POSITIVE_INFINITY;
let bestTileZoomLevel = tileZoomLevels.at(-1);
for (const tileZoomLevel of tileZoomLevels) {
const log2ScaleFactorDiff = Math.abs(
Math.log2(tileZoomLevel.scaleFactor) - (Math.log2(resourceToCanvasScale + scaleFactorCorrection) + log2ScaleFactorCorrection)
);
if (log2ScaleFactorDiff < smallestLog2ScaleFactorDiff) {
smallestLog2ScaleFactorDiff = log2ScaleFactorDiff;
bestTileZoomLevel = tileZoomLevel;
}
}
return bestTileZoomLevel;
}
function computeTilesCoveringRingAtTileZoomLevel(resourceRing, tileZoomLevel, imageSize) {
const scaledResourceRing = scaleResourcePoints(resourceRing, tileZoomLevel);
const tilesByColumn = ringToTilesByColumn(scaledResourceRing);
const tiles = tilesByColumnToTiles(tilesByColumn, tileZoomLevel, imageSize);
return tiles;
}
function scaleResourcePoints(resourcePoints, tileZoomLevel) {
return resourcePoints.map((point) => [
point[0] / tileZoomLevel.originalWidth,
point[1] / tileZoomLevel.originalHeight
]);
}
function ringToTilesByColumn(ring) {
const tilesByColumn = {};
for (let i = 0; i < ring.length; i++) {
const line = [ring[i], ring[(i + 1) % ring.length]];
const pixels = lineToPixels(line);
pixels.forEach(([x, y]) => {
if (!tilesByColumn[x]) {
tilesByColumn[x] = [Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY];
}
if (y < tilesByColumn[x][0]) {
tilesByColumn[x][0] = y;
}
if (y > tilesByColumn[x][1]) {
tilesByColumn[x][1] = y;
}
});
}
return tilesByColumn;
}
function lineToPixels([startPoint, endPoint]) {
let startPixelX = Math.floor(startPoint[0]);
let startPixelY = Math.floor(startPoint[1]);
const endPixelX = Math.floor(endPoint[0]);
const endPixelY = Math.floor(endPoint[1]);
const points = [[startPixelX, startPixelY]];
if (startPixelX === endPixelX && startPixelY === endPixelY) {
return points;
}
const stepX = Math.sign(endPoint[0] - startPoint[0]);
const stepY = Math.sign(endPoint[1] - startPoint[1]);
const restX = Math.abs(startPoint[0] - startPixelX - Math.max(0, stepX));
const restY = Math.abs(startPoint[1] - startPixelY - Math.max(0, stepY));
const distanceX = Math.abs(startPoint[0] - endPoint[0]);
const distanceY = Math.abs(startPoint[1] - endPoint[1]);
let restPerStepX = restX / distanceX;
let restPerStepY = restY / distanceY;
const onePerStepX = 1 / distanceX;
const onePerStepY = 1 / distanceY;
while (!(startPixelX === endPixelX && startPixelY === endPixelY)) {
if (distanceY === 0) {
startPixelX = startPixelX + stepX;
} else if (distanceX === 0) {
startPixelY = startPixelY + stepY;
} else if (restPerStepX < restPerStepY) {
restPerStepX = restPerStepX + onePerStepX;
startPixelX = startPixelX + stepX;
} else {
restPerStepY = restPerStepY + onePerStepY;
startPixelY = startPixelY + stepY;
}
points.push([startPixelX, startPixelY]);
}
return points;
}
function tilesByColumnToTiles(tilesByColumn, tileZoomLevel, imageSize) {
const tiles = [];
for (const xKey in tilesByColumn) {
const x = parseInt(xKey);
if (x < 0 || x >= tileZoomLevel.columns) {
break;
}
const fromY = Math.max(tilesByColumn[x][0], 0);
const toY = Math.min(tilesByColumn[x][1], tileZoomLevel.rows - 1);
for (let y = fromY; y <= toY; y++) {
tiles.push({
column: x,
row: y,
tileZoomLevel,
imageSize
});
}
}
return tiles;
}
function getTilesCoveringTileAtScaleFactor(tile, image, scaleFactor, validTile) {
let columnStart = Math.floor(
tile.column * tile.tileZoomLevel.scaleFactor / scaleFactor
);
columnStart = columnStart >= 0 ? columnStart : 0;
const columnEnd = Math.ceil(
(tile.column + 1) * tile.tileZoomLevel.scaleFactor / scaleFactor
);
let rowStart = Math.floor(
tile.row * tile.tileZoomLevel.scaleFactor / scaleFactor
);
rowStart = rowStart >= 0 ? rowStart : 0;
const rowEnd = Math.ceil(
(tile.row + 1) * tile.tileZoomLevel.scaleFactor / scaleFactor
);
return getTilesAtScaleFactor(
scaleFactor,
image,
columnStart,
columnEnd,
rowStart,
rowEnd,
validTile
);
}
function getTilesAtScaleFactor(scaleFactor, image, columnStart, columnEnd, rowStart, rowEnd, validTile = (_tile) => true) {
const tileZoomLevel = image.tileZoomLevels.find(
(tileZoomLevel2) => tileZoomLevel2.scaleFactor === scaleFactor
);
const imageSize = [image.width, image.height];
if (!tileZoomLevel) {
return [];
}
columnStart = columnStart ? columnStart : 0;
columnEnd = columnEnd ? columnEnd : tileZoomLevel.columns;
rowStart = rowStart ? rowStart : 0;
rowEnd = rowEnd ? rowEnd : tileZoomLevel.rows;
const tiles = [];
for (let column = columnStart; column < columnEnd; column++) {
for (let row = rowStart; row < rowEnd; row++) {
const tile = {
column,
row,
tileZoomLevel,
imageSize
};
if (validTile(tile)) {
tiles.push(tile);
}
}
}
return tiles;
}
function squaredDistanceTileToPoint(tile, point) {
return squaredDistance(tileCenter(tile), point);
}
function tileCenter(tile) {
const bbox = computeBboxTile(tile);
return [(bbox[2] - bbox[0]) / 2 + bbox[0], (bbox[3] - bbox[1]) / 2 + bbox[1]];
}
function tileToTileOriginPoint(tile) {
return [
tile.column * tile.tileZoomLevel.originalWidth,
tile.row * tile.tileZoomLevel.originalHeight
];
}
function clipTilePointToTile(tilePoint, tile) {
const tileSize = [tile.tileZoomLevel.width, tile.tileZoomLevel.height];
return tilePoint.map((coordinate, index) => {
coordinate = Math.max(coordinate, 0);
coordinate = Math.min(coordinate, tileSize[index] - 1);
return coordinate;
});
}
function resourcePointInTile(resourcePoint, tile) {
const resourceTileOrigin = tileToTileOriginPoint(tile);
return resourcePoint[0] >= resourceTileOrigin[0] && resourcePoint[0] <= resourceTileOrigin[0] + tile.tileZoomLevel.originalWidth && resourcePoint[1] >= resourceTileOrigin[1] && resourcePoint[1] <= resourceTileOrigin[1] + tile.tileZoomLevel.originalHeight;
}
function computeBboxTile(tile) {
const resourceTileOriginPoint = tileToTileOriginPoint(tile);
const resourceTileMaxX = Math.min(
resourceTileOriginPoint[0] + tile.tileZoomLevel.originalWidth,
tile.imageSize[0]
);
const resourceTileMaxY = Math.min(
resourceTileOriginPoint[1] + tile.tileZoomLevel.originalHeight,
tile.imageSize[1]
);
return [
resourceTileOriginPoint[0],
resourceTileOriginPoint[1],
resourceTileMaxX,
resourceTileMaxY
];
}
function getTileSize(tile) {
return [tile.tileZoomLevel.width, tile.tileZoomLevel.height];
}
function getTileResolution(tile) {
return sizeToResolution(getTileSize(tile));
}
function getTileZoomLevelResolution(tileZoomLevel) {
return tileZoomLevel.rows * tileZoomLevel.width * tileZoomLevel.columns * tileZoomLevel.height;
}
function getTilesAtOtherScaleFactors(tile, image, scaleFactor, maxLowerLog2ScaleFactorDiff, maxHigherLog2ScaleFactorDiff, validTile) {
const tilesAtOtherScaleFactors = [];
const tilesAtLowerScaleFactor = recursivelyGetTilesAtLowerScaleFactor(
tile,
image,
scaleFactor,
maxLowerLog2ScaleFactorDiff,
validTile
);
for (const tileAtLowerScaleFactor of tilesAtLowerScaleFactor) {
if (tileAtLowerScaleFactor) {
tilesAtOtherScaleFactors.push(tileAtLowerScaleFactor);
}
}
if (tilesAtOtherScaleFactors.length === 0) {
const maxScaleFactor = Math.max(
...image.tileZoomLevels.map((tileZoomLevel) => tileZoomLevel.scaleFactor)
);
const tileAtHigherScaleFactor = recursivelyGetTilesAtHigherScaleFactor(
tile,
image,
scaleFactor,
maxHigherLog2ScaleFactorDiff,
maxScaleFactor,
validTile
);
if (tileAtHigherScaleFactor) {
tilesAtOtherScaleFactors.push(tileAtHigherScaleFactor);
}
}
return tilesAtOtherScaleFactors;
}
function recursivelyGetTilesAtHigherScaleFactor(tile, image, scaleFactor, log2ScaleFactorDiff, maxScaleFactor, validTile) {
const higherScaleFactor = 2 ** (Math.log2(scaleFactor) + 1);
if (higherScaleFactor > maxScaleFactor || log2ScaleFactorDiff <= 0) {
return void 0;
}
const tileAtHigherScaleFactor = getTileAtHigherScaleFactor(
tile,
image,
higherScaleFactor,
validTile
);
if (tileAtHigherScaleFactor !== void 0) {
return tileAtHigherScaleFactor;
} else {
return recursivelyGetTilesAtHigherScaleFactor(
tile,
image,
higherScaleFactor,
log2ScaleFactorDiff--,
maxScaleFactor,
validTile
);
}
}
function recursivelyGetTilesAtLowerScaleFactor(tile, image, scaleFactor, log2ScaleFactorDiff, validTile) {
const lowerScaleFactor = 2 ** (Math.log2(scaleFactor) - 1);
if (lowerScaleFactor <= 0 || log2ScaleFactorDiff <= 0) {
return [];
}
const tilesAtLowerScaleFactor = getTilesAtLowerScaleFactor(
tile,
image,
lowerScaleFactor,
validTile
);
const allTilesAtLowerScaleFactor = getTilesAtLowerScaleFactor(
tile,
image,
lowerScaleFactor,
(_tile) => true
);
if (tilesAtLowerScaleFactor.length === allTilesAtLowerScaleFactor.length) {
return tilesAtLowerScaleFactor;
} else {
return [
...tilesAtLowerScaleFactor,
...recursivelyGetTilesAtLowerScaleFactor(
tile,
image,
lowerScaleFactor,
log2ScaleFactorDiff--,
validTile
)
];
}
}
function getTileAtHigherScaleFactor(tile, image, higherScaleFactor, validTile) {
const tilesCoveringTileAtHigherScaleFactor = getTilesCoveringTileAtScaleFactor(tile, image, higherScaleFactor, validTile);
if (tilesCoveringTileAtHigherScaleFactor.length === 0) {
return void 0;
}
return tilesCoveringTileAtHigherScaleFactor[0];
}
function getTilesAtLowerScaleFactor(tile, image, lowerScaleFactor, validTile) {
const tilesCoveringTileAtLowerScaleFactor = getTilesCoveringTileAtScaleFactor(
tile,
image,
lowerScaleFactor,
validTile
);
return tilesCoveringTileAtLowerScaleFactor;
}
function fetchableTileKey(fetchableTile) {
return keyFromMapIdTileUrl(fetchableTile.mapId, fetchableTile.tileUrl);
}
function keyFromMapIdTileUrl(mapId, tileUrl2) {
return `${mapId}:${tileUrl2}`;
}
function tileKey(tile) {
return keyFromScaleFactorRowColumn(
tile.tileZoomLevel.scaleFactor,
tile.row,
tile.column
);
}
function keyFromScaleFactorRowColumn(scaleFactor, row, column) {
return `${scaleFactor}:${row}:${column}`;
}
export {
clipTilePointToTile,
computeBboxTile,
computeTilesCoveringRingAtTileZoomLevel,
fetchableTileKey,
getTileAtHigherScaleFactor,
getTileResolution,
getTileSize,
getTileZoomLevelForScale,
getTileZoomLevelResolution,
getTilesAtLowerScaleFactor,
getTilesAtOtherScaleFactors,
getTilesAtScaleFactor,
getTilesCoveringTileAtScaleFactor,
keyFromMapIdTileUrl,
keyFromScaleFactorRowColumn,
recursivelyGetTilesAtHigherScaleFactor,
recursivelyGetTilesAtLowerScaleFactor,
resourcePointInTile,
squaredDistanceTileToPoint,
tileCenter,
tileKey,
tileToTileOriginPoint
};
//# sourceMappingURL=tiles.js.map