| from dataclasses import dataclass |
| from math import cos, radians, sin |
| from typing import List, Optional, Tuple |
|
|
|
|
| @dataclass |
| class BBox: |
| min_lat: float |
| max_lat: float |
| min_lon: float |
| max_lon: float |
|
|
| name: Optional[str] = None |
|
|
| def __post_init__(self): |
| if self.max_lon < self.min_lon: |
| raise ValueError("max_lon should be larger than min_lon") |
| if self.max_lat < self.min_lat: |
| raise ValueError("max_lat should be larger than min_lat") |
|
|
| self.url = ( |
| f"http://bboxfinder.com/#{self.min_lat},{self.min_lon},{self.max_lat},{self.max_lon}" |
| ) |
|
|
| def contains(self, lat: float, lon: float) -> bool: |
| return ( |
| (lat >= self.min_lat) |
| & (lat <= self.max_lat) |
| & (lon >= self.min_lon) |
| & (lon <= self.max_lon) |
| ) |
|
|
| def contains_bbox(self, bbox: "BBox") -> bool: |
| return ( |
| (bbox.min_lat >= self.min_lat) |
| & (bbox.max_lat <= self.max_lat) |
| & (bbox.min_lon >= self.min_lon) |
| & (bbox.max_lon <= self.max_lon) |
| ) |
|
|
| @property |
| def three_dimensional_points(self) -> List[float]: |
| r""" |
| If we are passing the central latitude and longitude to |
| an ML model, we want it to know the extremes are close together. |
| Mapping them to 3d space allows us to do that |
| """ |
| lat, lon = self.get_centre(in_radians=True) |
| return [cos(lat) * cos(lon), cos(lat) * sin(lon), sin(lat)] |
|
|
| def get_centre(self, in_radians: bool = True) -> Tuple[float, float]: |
| |
| lat = self.min_lat + ((self.max_lat - self.min_lat) / 2) |
| lon = self.min_lon + ((self.max_lon - self.min_lon) / 2) |
| if in_radians: |
| return radians(lat), radians(lon) |
| else: |
| return lat, lon |
|
|
| def get_identifier(self, start_date, end_date) -> str: |
| |
| min_lon = round(self.min_lon, 4) |
| min_lat = round(self.min_lat, 4) |
| max_lon = round(self.max_lon, 4) |
| max_lat = round(self.max_lat, 4) |
| return ( |
| f"min_lat={min_lat}_min_lon={min_lon}_max_lat={max_lat}_max_lon={max_lon}_" |
| f"dates={start_date}_{end_date}" |
| ) |
|
|
| def __add__(self, other_box: "BBox") -> "BBox": |
| return BBox( |
| min_lat=min([self.min_lat, other_box.min_lat]), |
| min_lon=min([self.min_lon, other_box.min_lon]), |
| max_lon=max([self.max_lon, other_box.max_lon]), |
| max_lat=max([self.max_lat, other_box.max_lat]), |
| name="_".join([x for x in [self.name, other_box.name] if x is not None]), |
| ) |
|
|