2021-09-28 17:30:48 +02:00
|
|
|
import * as turf from "@turf/turf";
|
|
|
|
import {TileRange, Tiles} from "../Models/TileRange";
|
2021-10-27 03:52:19 +02:00
|
|
|
import {GeoOperations} from "./GeoOperations";
|
2021-09-28 17:30:48 +02:00
|
|
|
|
|
|
|
export class BBox {
|
|
|
|
|
|
|
|
readonly maxLat: number;
|
|
|
|
readonly maxLon: number;
|
|
|
|
readonly minLat: number;
|
|
|
|
readonly minLon: number;
|
|
|
|
static global: BBox = new BBox([[-180, -90], [180, 90]]);
|
|
|
|
|
|
|
|
constructor(coordinates) {
|
|
|
|
this.maxLat = -90;
|
|
|
|
this.maxLon = -180;
|
|
|
|
this.minLat = 90;
|
|
|
|
this.minLon = 180;
|
|
|
|
|
|
|
|
|
|
|
|
for (const coordinate of coordinates) {
|
|
|
|
this.maxLon = Math.max(this.maxLon, coordinate[0]);
|
|
|
|
this.maxLat = Math.max(this.maxLat, coordinate[1]);
|
|
|
|
this.minLon = Math.min(this.minLon, coordinate[0]);
|
|
|
|
this.minLat = Math.min(this.minLat, coordinate[1]);
|
|
|
|
}
|
2021-10-27 03:52:19 +02:00
|
|
|
|
2021-10-01 01:33:07 +02:00
|
|
|
this.maxLon = Math.min(this.maxLon, 180)
|
|
|
|
this.maxLat = Math.min(this.maxLat, 90)
|
|
|
|
this.minLon = Math.max(this.minLon, -180)
|
|
|
|
this.minLat = Math.max(this.minLat, -90)
|
|
|
|
|
|
|
|
|
2021-09-28 17:30:48 +02:00
|
|
|
this.check();
|
|
|
|
}
|
|
|
|
|
|
|
|
static fromLeafletBounds(bounds) {
|
|
|
|
return new BBox([[bounds.getWest(), bounds.getNorth()], [bounds.getEast(), bounds.getSouth()]])
|
|
|
|
}
|
|
|
|
|
|
|
|
static get(feature): BBox {
|
|
|
|
if (feature.bbox?.overlapsWith === undefined) {
|
|
|
|
const turfBbox: number[] = turf.bbox(feature)
|
|
|
|
feature.bbox = new BBox([[turfBbox[0], turfBbox[1]], [turfBbox[2], turfBbox[3]]]);
|
|
|
|
}
|
|
|
|
return feature.bbox;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructs a tilerange which fully contains this bbox (thus might be a bit larger)
|
|
|
|
* @param zoomlevel
|
|
|
|
*/
|
2021-10-01 01:33:07 +02:00
|
|
|
public containingTileRange(zoomlevel): TileRange {
|
|
|
|
return Tiles.TileRangeBetween(zoomlevel, this.minLat, this.minLon, this.maxLat, this.maxLon)
|
2021-09-28 17:30:48 +02:00
|
|
|
}
|
2021-10-01 01:33:07 +02:00
|
|
|
|
2021-09-28 17:30:48 +02:00
|
|
|
public overlapsWith(other: BBox) {
|
|
|
|
if (this.maxLon < other.minLon) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (this.maxLat < other.minLat) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (this.minLon > other.maxLon) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return this.minLat <= other.maxLat;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public isContainedIn(other: BBox) {
|
|
|
|
if (this.maxLon > other.maxLon) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (this.maxLat > other.maxLat) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (this.minLon < other.minLon) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (this.minLat < other.minLat) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private check() {
|
|
|
|
if (isNaN(this.maxLon) || isNaN(this.maxLat) || isNaN(this.minLon) || isNaN(this.minLat)) {
|
|
|
|
console.log(this);
|
|
|
|
throw "BBOX has NAN";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static fromTile(z: number, x: number, y: number): BBox {
|
|
|
|
return new BBox(Tiles.tile_bounds_lon_lat(z, x, y))
|
|
|
|
}
|
|
|
|
|
|
|
|
static fromTileIndex(i: number): BBox {
|
|
|
|
if (i === 0) {
|
|
|
|
return BBox.global
|
|
|
|
}
|
|
|
|
return BBox.fromTile(...Tiles.tile_from_index(i))
|
|
|
|
}
|
|
|
|
|
|
|
|
getEast() {
|
|
|
|
return this.maxLon
|
|
|
|
}
|
|
|
|
|
|
|
|
getNorth() {
|
|
|
|
return this.maxLat
|
|
|
|
}
|
|
|
|
|
|
|
|
getWest() {
|
|
|
|
return this.minLon
|
|
|
|
}
|
|
|
|
|
|
|
|
getSouth() {
|
|
|
|
return this.minLat
|
|
|
|
}
|
2021-11-03 00:44:53 +01:00
|
|
|
|
|
|
|
contains(lonLat: [number, number]){
|
|
|
|
return this.minLat <= lonLat[1] && lonLat[1] <= this.maxLat
|
|
|
|
&& this.minLon<= lonLat[0] && lonLat[0] <= this.maxLon
|
|
|
|
}
|
2021-09-28 17:30:48 +02:00
|
|
|
|
2021-10-11 21:23:14 +02:00
|
|
|
pad(factor: number, maxIncrease = 2): BBox {
|
2021-10-27 03:52:19 +02:00
|
|
|
|
2021-10-11 21:23:14 +02:00
|
|
|
const latDiff = Math.min(maxIncrease / 2, Math.abs(this.maxLat - this.minLat) * factor)
|
2021-10-27 03:52:19 +02:00
|
|
|
const lonDiff = Math.min(maxIncrease / 2, Math.abs(this.maxLon - this.minLon) * factor)
|
2021-09-28 17:30:48 +02:00
|
|
|
return new BBox([[
|
2021-10-11 21:23:14 +02:00
|
|
|
this.minLon - lonDiff,
|
2021-10-27 03:52:19 +02:00
|
|
|
this.minLat - latDiff
|
2021-10-11 21:23:14 +02:00
|
|
|
], [this.maxLon + lonDiff,
|
|
|
|
this.maxLat + latDiff]])
|
2021-09-28 17:30:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
toLeaflet() {
|
|
|
|
return [[this.minLat, this.minLon], [this.maxLat, this.maxLon]]
|
|
|
|
}
|
|
|
|
|
|
|
|
asGeoJson(properties: any): any {
|
|
|
|
return {
|
|
|
|
type: "Feature",
|
|
|
|
properties: properties,
|
|
|
|
geometry: {
|
|
|
|
type: "Polygon",
|
|
|
|
coordinates: [[
|
|
|
|
|
|
|
|
[this.minLon, this.minLat],
|
|
|
|
[this.maxLon, this.minLat],
|
|
|
|
[this.maxLon, this.maxLat],
|
|
|
|
[this.minLon, this.maxLat],
|
|
|
|
[this.minLon, this.minLat],
|
|
|
|
|
|
|
|
]]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Expands the BBOx so that it contains complete tiles for the given zoomlevel
|
|
|
|
* @param zoomlevel
|
|
|
|
*/
|
2021-10-01 01:33:07 +02:00
|
|
|
expandToTileBounds(zoomlevel: number): BBox {
|
2021-09-28 17:30:48 +02:00
|
|
|
const ul = Tiles.embedded_tile(this.minLat, this.minLon, zoomlevel)
|
|
|
|
const lr = Tiles.embedded_tile(this.maxLat, this.maxLon, zoomlevel)
|
|
|
|
const boundsul = Tiles.tile_bounds_lon_lat(ul.z, ul.x, ul.y)
|
|
|
|
const boundslr = Tiles.tile_bounds_lon_lat(lr.z, lr.x, lr.y)
|
|
|
|
return new BBox([].concat(boundsul, boundslr))
|
|
|
|
}
|
2021-10-27 03:52:19 +02:00
|
|
|
|
|
|
|
toMercator(): { minLat: number, maxLat: number, minLon: number, maxLon: number } {
|
|
|
|
const [minLon, minLat] = GeoOperations.ConvertWgs84To900913([this.minLon, this.minLat])
|
|
|
|
const [maxLon, maxLat] = GeoOperations.ConvertWgs84To900913([this.maxLon, this.maxLat])
|
|
|
|
|
|
|
|
return {
|
|
|
|
minLon, maxLon,
|
|
|
|
minLat, maxLat
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
2021-09-28 17:30:48 +02:00
|
|
|
}
|