| 
									
										
										
										
											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 { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |     static global: BBox = new BBox([[-180, -90], [180, 90]]); | 
					
						
							| 
									
										
										
										
											2021-09-28 17:30:48 +02:00
										 |  |  |     readonly maxLat: number; | 
					
						
							|  |  |  |     readonly maxLon: number; | 
					
						
							|  |  |  |     readonly minLat: number; | 
					
						
							|  |  |  |     readonly minLon: number; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-07 17:46:57 +01:00
										 |  |  |     /*** | 
					
						
							|  |  |  |      * Coordinates should be [[lon, lat],[lon, lat]] | 
					
						
							|  |  |  |      * @param coordinates | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-09-28 17:30:48 +02:00
										 |  |  |     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; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-01-26 20:47:08 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     static bboxAroundAll(bboxes: BBox[]): BBox { | 
					
						
							| 
									
										
										
										
											2021-12-07 17:46:57 +01:00
										 |  |  |         let maxLat: number = -90; | 
					
						
							| 
									
										
										
										
											2022-01-26 20:47:08 +01:00
										 |  |  |         let maxLon: number = -180; | 
					
						
							|  |  |  |         let minLat: number = 80; | 
					
						
							|  |  |  |         let minLon: number = 180; | 
					
						
							| 
									
										
										
										
											2021-12-07 17:46:57 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         for (const bbox of bboxes) { | 
					
						
							|  |  |  |             maxLat = Math.max(maxLat, bbox.maxLat) | 
					
						
							|  |  |  |             maxLon = Math.max(maxLon, bbox.maxLon) | 
					
						
							|  |  |  |             minLat = Math.min(minLat, bbox.minLat) | 
					
						
							|  |  |  |             minLon = Math.min(minLon, bbox.minLon) | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-01-26 20:47:08 +01:00
										 |  |  |         return new BBox([[maxLon, maxLat], [minLon, minLat]]) | 
					
						
							| 
									
										
										
										
											2021-12-07 17:46:57 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-09-28 17:30:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-23 19:48:06 +01:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Calculates the BBox based on a slippy map tile number | 
					
						
							|  |  |  |      *  | 
					
						
							|  |  |  |      *  const bbox = BBox.fromTile(16, 32754, 21785) | 
					
						
							|  |  |  |      *  bbox.minLon // => -0.076904296875 
 | 
					
						
							|  |  |  |      *  bbox.maxLon // => -0.0714111328125 
 | 
					
						
							|  |  |  |      *  bbox.minLat // => 51.5292513551899 
 | 
					
						
							|  |  |  |      *  bbox.maxLat // => 51.53266860674158 
 | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |     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)) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-26 20:47:08 +01:00
										 |  |  |     public unionWith(other: BBox) { | 
					
						
							|  |  |  |         return new BBox([[ | 
					
						
							|  |  |  |             Math.max(this.maxLon, other.maxLon), | 
					
						
							|  |  |  |             Math.max(this.maxLat, other.maxLat)], | 
					
						
							|  |  |  |             [Math.min(this.minLon, other.minLon), | 
					
						
							|  |  |  |                 Math.min(this.minLat, other.minLat)]]) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-28 17:30:48 +02:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * 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; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     getEast() { | 
					
						
							|  |  |  |         return this.maxLon | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     getNorth() { | 
					
						
							|  |  |  |         return this.maxLat | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     getWest() { | 
					
						
							|  |  |  |         return this.minLon | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     getSouth() { | 
					
						
							|  |  |  |         return this.minLat | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     contains(lonLat: [number, number]) { | 
					
						
							| 
									
										
										
										
											2021-11-03 00:44:53 +01:00
										 |  |  |         return this.minLat <= lonLat[1] && lonLat[1] <= this.maxLat | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |             && this.minLon <= lonLat[0] && lonLat[0] <= this.maxLon | 
					
						
							| 
									
										
										
										
											2021-11-03 00:44:53 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											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
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-19 20:34:04 +01:00
										 |  |  |     padAbsolute(degrees: number): BBox { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return new BBox([[ | 
					
						
							|  |  |  |             this.minLon - degrees, | 
					
						
							|  |  |  |             this.minLat - degrees | 
					
						
							|  |  |  |         ], [this.maxLon + degrees, | 
					
						
							|  |  |  |             this.maxLat + degrees]]) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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-11-07 16:34:51 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-24 03:09:30 +01:00
										 |  |  |     private check() {    | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |         if (isNaN(this.maxLon) || isNaN(this.maxLat) || isNaN(this.minLon) || isNaN(this.minLat)) { | 
					
						
							| 
									
										
										
										
											2022-02-24 03:09:30 +01:00
										 |  |  |             console.trace("BBox with NaN detected:", this); | 
					
						
							| 
									
										
										
										
											2021-11-07 16:34:51 +01:00
										 |  |  |             throw  "BBOX has NAN"; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-09-28 17:30:48 +02:00
										 |  |  | } |