158 lines
4.5 KiB
TypeScript
158 lines
4.5 KiB
TypeScript
import { BBox } from "../Logic/BBox"
|
|
|
|
export interface TileRange {
|
|
xstart: number
|
|
ystart: number
|
|
xend: number
|
|
yend: number
|
|
total: number
|
|
zoomlevel: number
|
|
}
|
|
|
|
export class Tiles {
|
|
public static MapRange<T>(tileRange: TileRange, f: (x: number, y: number) => T): T[] {
|
|
const result: T[] = []
|
|
const total = tileRange.total
|
|
if (total > 100000) {
|
|
throw `Tilerange too big (z is ${tileRange.zoomlevel}, total tiles needed: ${tileRange.total})`
|
|
}
|
|
for (let x = tileRange.xstart; x <= tileRange.xend; x++) {
|
|
for (let y = tileRange.ystart; y <= tileRange.yend; y++) {
|
|
const t = f(x, y)
|
|
result.push(t)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
/**
|
|
* Calculates the tile bounds of the
|
|
* @param z
|
|
* @param x
|
|
* @param y
|
|
* @returns [[maxlat, minlon], [minlat, maxlon]]
|
|
*/
|
|
static tile_bounds(z: number, x: number, y: number): [[number, number], [number, number]] {
|
|
return [
|
|
[Tiles.tile2lat(y, z), Tiles.tile2long(x, z)],
|
|
[Tiles.tile2lat(y + 1, z), Tiles.tile2long(x + 1, z)],
|
|
]
|
|
}
|
|
|
|
static tile_bounds_lon_lat(
|
|
z: number,
|
|
x: number,
|
|
y: number
|
|
): [[number, number], [number, number]] {
|
|
return [
|
|
[Tiles.tile2long(x, z), Tiles.tile2lat(y, z)],
|
|
[Tiles.tile2long(x + 1, z), Tiles.tile2lat(y + 1, z)],
|
|
]
|
|
}
|
|
|
|
/**
|
|
* Returns the centerpoint [lon, lat] of the specified tile
|
|
* @param z
|
|
* @param x
|
|
* @param y
|
|
*/
|
|
static centerPointOf(z: number, x: number, y: number): [number, number] {
|
|
return [
|
|
(Tiles.tile2long(x, z) + Tiles.tile2long(x + 1, z)) / 2,
|
|
(Tiles.tile2lat(y, z) + Tiles.tile2lat(y + 1, z)) / 2,
|
|
]
|
|
}
|
|
|
|
static tile_index(z: number, x: number, y: number): number {
|
|
return (x * (2 << z) + y) * 100 + z
|
|
}
|
|
|
|
/**
|
|
* Given a tile index number, returns [z, x, y]
|
|
* @param index
|
|
* @returns 'zxy'
|
|
*/
|
|
static tile_from_index(index: number): [number, number, number] {
|
|
const z = index % 100
|
|
const factor = 2 << z
|
|
index = Math.floor(index / 100)
|
|
const x = Math.floor(index / factor)
|
|
return [z, x, index % factor]
|
|
}
|
|
|
|
/**
|
|
* Return x, y of the tile containing (lat, lon) on the given zoom level
|
|
*/
|
|
static embedded_tile(lat: number, lon: number, z: number): { x: number; y: number; z: number } {
|
|
return { x: Tiles.lon2tile(lon, z), y: Tiles.lat2tile(lat, z), z }
|
|
}
|
|
|
|
static tileRangeFrom(bbox: BBox, zoomlevel: number) {
|
|
return Tiles.TileRangeBetween(
|
|
zoomlevel,
|
|
bbox.getNorth(),
|
|
bbox.getWest(),
|
|
bbox.getSouth(),
|
|
bbox.getEast()
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Construct a tilerange which (at least) contains the given coordinates.
|
|
* This means that the actual iterated area might be a bit bigger then the the passed in coordinates
|
|
* @param zoomlevel
|
|
* @param lat0
|
|
* @param lon0
|
|
* @param lat1
|
|
* @param lon1
|
|
* @constructor
|
|
*/
|
|
static TileRangeBetween(
|
|
zoomlevel: number,
|
|
lat0: number,
|
|
lon0: number,
|
|
lat1: number,
|
|
lon1: number
|
|
): TileRange {
|
|
const t0 = Tiles.embedded_tile(lat0, lon0, zoomlevel)
|
|
const t1 = Tiles.embedded_tile(lat1, lon1, zoomlevel)
|
|
|
|
const xstart = Math.min(t0.x, t1.x)
|
|
const xend = Math.max(t0.x, t1.x)
|
|
const ystart = Math.min(t0.y, t1.y)
|
|
const yend = Math.max(t0.y, t1.y)
|
|
const total = (1 + xend - xstart) * (1 + yend - ystart)
|
|
|
|
return {
|
|
xstart: xstart,
|
|
xend: xend,
|
|
ystart: ystart,
|
|
yend: yend,
|
|
total: total,
|
|
zoomlevel: zoomlevel,
|
|
}
|
|
}
|
|
|
|
private static tile2long(x, z) {
|
|
return (x / Math.pow(2, z)) * 360 - 180
|
|
}
|
|
|
|
private static tile2lat(y, z) {
|
|
const n = Math.PI - (2 * Math.PI * y) / Math.pow(2, z)
|
|
return (180 / Math.PI) * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)))
|
|
}
|
|
|
|
private static lon2tile(lon, zoom) {
|
|
return Math.floor(((lon + 180) / 360) * Math.pow(2, zoom))
|
|
}
|
|
|
|
private static lat2tile(lat, zoom) {
|
|
return Math.floor(
|
|
((1 -
|
|
Math.log(Math.tan((lat * Math.PI) / 180) + 1 / Math.cos((lat * Math.PI) / 180)) /
|
|
Math.PI) /
|
|
2) *
|
|
Math.pow(2, zoom)
|
|
)
|
|
}
|
|
}
|