-
Notifications
You must be signed in to change notification settings - Fork 45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Crop Mask Integration #85
base: main
Are you sure you want to change the base?
Changes from 38 commits
818c364
e09fc93
d8ce1a2
78f326b
f27e94d
674944c
3f529e1
ddaab9c
475bb30
171bb73
50bf35e
52b0cc3
eff8786
7eff7d4
d2f737e
db91487
429baf6
dd9a3d2
30c7051
a804214
270e3af
b02a2bf
d16905a
e05e305
30a55d5
b391d3b
127947a
f6a2c06
f5438cc
3bffd70
765aec0
cee6c20
3698c15
ac39be3
26aacf5
84aa022
96625b4
eaa7d18
82ade08
ea3acfd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
from dataclasses import dataclass | ||
from pathlib import Path | ||
from shapely.geometry import Polygon | ||
from math import sin, cos, radians | ||
from typing import List, Tuple | ||
import re | ||
|
||
from typing import Optional | ||
|
||
|
||
@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]: | ||
|
||
# roughly calculate the centres | ||
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 | ||
|
||
@classmethod | ||
def polygon_to_bbox(cls, polygon: Polygon, name: Optional[str] = None): | ||
(min_lon, min_lat, max_lon, max_lat) = polygon.bounds | ||
return cls(min_lat, max_lat, min_lon, max_lon, name) | ||
|
||
@classmethod | ||
def from_eo_tif_file(cls, path: Path) -> "BBox": | ||
decimals_in_p = re.findall(r"=-?\d*\.?\d*", path.stem) | ||
coords = [float(d[1:]) for d in decimals_in_p[0:4]] | ||
bbox = cls(min_lat=coords[0], min_lon=coords[1], max_lat=coords[2], max_lon=coords[3]) | ||
return bbox |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,7 @@ | ||
from .countries import BBox | ||
from pathlib import Path | ||
from collections import defaultdict | ||
|
||
from .boundingbox import BBox | ||
|
||
from typing import Dict | ||
|
||
|
@@ -22,6 +25,14 @@ | |
FEATURES_DIR = "features" | ||
TEST_FEATURES_DIR = "test_features" | ||
|
||
# These values describe the structure of the data folder | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ivanzvonkov , this locks in the folder structure but I think that's fine. We could potentially have a way of over-riding this datafolder path at a package level, but otherwise I'd be for removing folder manipulation for the user entirely and controlling it here. |
||
DATAFOLDER_PATH = Path(__file__).parent.parent / "data" | ||
EO_FILEPATH = DATAFOLDER_PATH / "eo_data" | ||
TEST_EO_FILEPATH = DATAFOLDER_PATH / "test_eo_data" | ||
FEATURES_FILEPATH = DATAFOLDER_PATH / FEATURES_DIR | ||
ARRAYS_FILEPATH = FEATURES_FILEPATH / "arrays" | ||
TEST_FEATURES_FILEPATH = DATAFOLDER_PATH / TEST_FEATURES_DIR | ||
|
||
# the default seed is useful because it also seeds the deterministic | ||
# shuffling algorithm we use (in cropharvest.utils.deterministic_shuffle) | ||
# so fixing this ensures the evaluation sets consist of the same data no matter | ||
|
@@ -47,3 +58,18 @@ | |
} | ||
|
||
TEST_DATASETS = {"Togo": "togo-eval"} | ||
|
||
|
||
def test_countries_to_crops(): | ||
output_dict = defaultdict(list) | ||
for identifier, _ in TEST_REGIONS.items(): | ||
country, crop, _, _ = identifier.split("_") | ||
output_dict[country].append(crop) | ||
|
||
for country, _ in TEST_DATASETS.items(): | ||
output_dict[country].append(None) | ||
|
||
return output_dict | ||
|
||
|
||
TEST_COUNTRIES_TO_CROPS = test_countries_to_crops() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,75 +1,12 @@ | ||
from dataclasses import dataclass | ||
import geopandas | ||
from shapely.geometry import Polygon, MultiPolygon | ||
from math import sin, cos, radians | ||
from typing import List, Tuple | ||
from typing import List | ||
from pathlib import Path | ||
|
||
from typing import Optional | ||
from cropharvest.boundingbox import BBox | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you! |
||
|
||
COUNTRY_SHAPEFILE = geopandas.read_file(str(Path(__file__).parent / "country_shapefile")) | ||
|
||
|
||
@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) -> 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]: | ||
|
||
# roughly calculate the centres | ||
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 | ||
|
||
@classmethod | ||
def polygon_to_bbox(cls, polygon: Polygon, name: Optional[str] = None): | ||
(min_lon, min_lat, max_lon, max_lat) = polygon.bounds | ||
return cls(min_lat, max_lat, min_lon, max_lon, name) | ||
COUNTRY_SHAPEFILE = geopandas.read_file(str(Path(__file__).parent / "country_shapefile")) | ||
|
||
|
||
def get_country_bbox(country_name: str) -> List[BBox]: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
neato