UNPKG

itowns

Version:

A JS/WebGL framework for 3D geospatial data visualization

205 lines (195 loc) 6.77 kB
import * as THREE from 'three'; import { Coordinates, CRS, Extent } from '@itowns/geographic'; import { getInfoTms, getCountTiles } from "./TileGrid.js"; const _tmsCoord = new THREE.Vector2(); const _dimensionTile = new THREE.Vector2(); const r = { row: 0, col: 0, invDiff: 0 }; function _rowColfromParent(tile, zoom) { const diffLevel = tile.zoom - zoom; const diff = 2 ** diffLevel; r.invDiff = 1 / diff; r.row = (tile.row - tile.row % diff) * r.invDiff; r.col = (tile.col - tile.col % diff) * r.invDiff; return r; } const _extent = new Extent('EPSG:4326'); const _extent2 = new Extent('EPSG:4326'); const _c = new Coordinates('EPSG:4326', 0, 0); class Tile { /** * A tile is a geographical bounding rectangle uniquely defined by its zoom, * row and column. * * @param crs - projection of limit values. * @param zoom - `zoom` value. Default is 0. * @param row - `row` value. Default is 0. * @param col - `column` value. Default is 0. */ constructor(crs) { let zoom = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; let row = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; let col = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; this.isTile = true; this.crs = crs; this.zoom = zoom; this.row = row; this.col = col; } /** * Returns a new tile with the same bounds and crs as this one. */ clone() { return new Tile(this.crs, this.zoom, this.row, this.col); } /** * Converts this tile to the specified extent. * @param crs - target's projection. * @param target - The target to store the projected extent. If this not * provided a new extent will be created. */ toExtent(crs) { let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Extent('EPSG:4326'); CRS.isValid(crs); const { epsg, globalExtent, globalDimension } = getInfoTms(this.crs); const countTiles = getCountTiles(this.crs, this.zoom); _dimensionTile.set(1, 1).divide(countTiles).multiply(globalDimension); target.west = globalExtent.west + (globalDimension.x - _dimensionTile.x * (countTiles.x - this.col)); target.east = target.west + _dimensionTile.x; target.south = globalExtent.south + _dimensionTile.y * (countTiles.y - this.row - 1); target.north = target.south + _dimensionTile.y; target.crs = epsg; return crs == epsg ? target : target.as(crs, target); } /** * Checks whether another tile is inside this tile. * * @param extent - the tile to check. */ isInside(tile) { if (this.zoom == tile.zoom) { return this.row == tile.row && this.col == tile.col; } else if (this.zoom < tile.zoom) { return false; } else { const r = _rowColfromParent(this, tile.zoom); return r.row == tile.row && r.col == tile.col; } } /** * Returns the translation and scale to transform this tile to the input * tile. * * @param tile - the input tile. * @param target - copy the result to target. */ offsetToParent(tile) { let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new THREE.Vector4(); if (this.crs != tile.crs) { throw new Error('unsupported mix'); } const r = _rowColfromParent(this, tile.zoom); return target.set(this.col * r.invDiff - r.col, this.row * r.invDiff - r.row, r.invDiff, r.invDiff); } /** * Returns the parent tile at the given level. * * @param levelParent - the level of the parent tile. */ tiledExtentParent(levelParent) { if (levelParent && levelParent < this.zoom) { const r = _rowColfromParent(this, levelParent); return new Tile(this.crs, levelParent, r.row, r.col); } else { return this; } } /** * Sets zoom, row and column values. * * @param zoom - zoom value. * @param row - row value. * @param col - column value. */ set() { let zoom = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; let row = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; let col = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; this.zoom = zoom; this.row = row; this.col = col; return this; } /** * Copies the passed tile to this tile. * @param tile - tile to copy. */ copy(tile) { this.crs = tile.crs; return this.set(tile.zoom, tile.row, tile.col); } /** * Return values of tile in string, separated by the separator input. * @param separator - string separator */ toString() { let separator = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; return `${this.zoom}${separator}${this.row}${separator}${this.col}`; } } export function tiledCovering(e, tms) { if (e.crs == 'EPSG:4326' && tms == 'EPSG:3857') { const WMTS_PM = []; const extent = _extent.copy(e).as(tms, _extent2); const { globalExtent, globalDimension, sTs } = getInfoTms(tms); extent.clampByExtent(globalExtent); extent.planarDimensions(_dimensionTile); const zoom = Math.floor(Math.log2(Math.round(globalDimension.x / (_dimensionTile.x * sTs.x)))); const countTiles = getCountTiles(tms, zoom); const center = extent.center(_c); _tmsCoord.x = center.x - globalExtent.west; _tmsCoord.y = globalExtent.north - extent.north; _tmsCoord.divide(globalDimension).multiply(countTiles).floor(); // ]N; N+1] => N const maxRow = Math.ceil((globalExtent.north - extent.south) / globalDimension.x * countTiles.y) - 1; for (let r = maxRow; r >= _tmsCoord.y; r--) { WMTS_PM.push(new Tile(tms, zoom, r, _tmsCoord.x)); } return WMTS_PM; } else { const target = new Tile(tms, 0, 0, 0); const { globalExtent, globalDimension, sTs, isInverted } = getInfoTms(e.crs); const center = e.center(_c); e.planarDimensions(_dimensionTile); // Each level has 2^n * 2^n tiles... // ... so we count how many tiles of the same width as tile we can fit // in the layer // ... 2^zoom = tilecount => zoom = log2(tilecount) const zoom = Math.floor(Math.log2(Math.round(globalDimension.x / (_dimensionTile.x * sTs.x)))); const countTiles = getCountTiles(tms, zoom); // Now that we have computed zoom, we can deduce x and y (or row / // column) _tmsCoord.x = center.x - globalExtent.west; _tmsCoord.y = isInverted ? globalExtent.north - center.y : center.y - globalExtent.south; _tmsCoord.divide(globalDimension).multiply(countTiles).floor(); target.set(zoom, _tmsCoord.y, _tmsCoord.x); return [target]; } } export default Tile;