Skip to content

Commit

Permalink
feat: Make a lazy loading of gee gdf..close #919.
Browse files Browse the repository at this point in the history
  • Loading branch information
dfguerrerom committed May 22, 2024
1 parent 0b0af7e commit 89c7b25
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 20 deletions.
73 changes: 55 additions & 18 deletions sepal_ui/aoi/aoi_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,10 +249,6 @@ def _from_asset(self, asset_json: dict) -> Self:
# set the feature collection
self.feature_collection = ee_col

# create a gdf form the feature_collection
features = self.feature_collection.getInfo()["features"]
self.gdf = gpd.GeoDataFrame.from_features(features).set_crs(epsg=4326)

return self

def _from_points(self, point_json: dict) -> Self:
Expand Down Expand Up @@ -377,20 +373,30 @@ def _from_admin(self, admin: str) -> Self:
# pygaul needs extra work as ISO codes are not included in the GEE dataset
if self.gee:
self.feature_collection = pygaul.AdmItems(admin=admin)
features = self.feature_collection.getInfo()["features"]
self.gdf = gpd.GeoDataFrame.from_features(features).set_crs(epsg=4326)
gaul_country = str(self.gdf.ADM0_CODE.unique()[0])
iso = json.loads(self.MAPPING.read_text())[gaul_country]
self.gdf["ISO"] = iso

# get the ADM0_CODE to get the ISO code
feature = self.feature_collection.first()
iso = json.loads(self.MAPPING.read_text())[str(feature.get("ADM0_CODE").getInfo())]

names = (
(feature.propertyNames().filter(ee.Filter.stringContains("item", "NAME")))
.map(lambda col_name: feature.get(col_name))
.getInfo()
)

# generate the name from the columns
names = [su.normalize_str(name) for name in names]
names[0] = iso
self.name = "_".join(names)

else:
self.gdf = pygadm.AdmItems(admin=admin)

# generate the name from the columns
r = self.gdf.iloc[0]
names = [su.normalize_str(r[c]) for c in self.gdf.columns if "NAME" in c]
names[0] = r.ISO if self.gee else r.GID_0[:3]
self.name = "_".join(names)
# generate the name from the columns
r = self.gdf.iloc[0]
names = [su.normalize_str(r[c]) for c in self.gdf.columns if "NAME" in c]
names[0] = r.GID_0[:3]
self.name = "_".join(names)

return self

Expand Down Expand Up @@ -433,7 +439,7 @@ def get_columns(self) -> List[str]:
Returns:
sorted list of column names
"""
if self.gdf is None:
if self.gdf is None and not self.feature_collection:
raise Exception(ms.aoi_sel.exception.no_gdf)

if self.gee:
Expand All @@ -455,7 +461,7 @@ def get_fields(self, column: str) -> List[str]:
sorted list of fields value
"""
if self.gdf is None:
if self.gdf is None and not self.feature_collection:
raise Exception(ms.aoi_sel.exception.no_gdf)

if self.gee:
Expand All @@ -476,7 +482,7 @@ def get_selected(self, column: str, field: str) -> Union[ee.Feature, gpd.GeoData
Returns:
The Feature associated with the query
"""
if self.gdf is None:
if self.gdf is None and not self.feature_collection:
raise Exception(ms.aoi_sel.exception.no_gdf)

if self.gee:
Expand All @@ -492,9 +498,13 @@ def total_bounds(self) -> Tuple[float, float, float, float]:
Returns:
minxx, miny, maxx, maxy
"""
if self.gdf is None:
if self.gdf is None and not self.feature_collection:
raise ValueError(ms.aoi_sel.exception.no_gdf)

if self.gee:
coords = self.feature_collection.geometry().bounds().coordinates().get(0).getInfo()
return [coords[0][0], coords[0][1], coords[3][0], coords[3][1]]

return self.gdf.total_bounds.tolist()

def export_to_asset(self) -> Self:
Expand Down Expand Up @@ -553,3 +563,30 @@ def get_ipygeojson(self, style: Optional[dict] = None) -> GeoJSON:
self.ipygeojson = GeoJSON(data=data, style=style, name="aoi")

return self.ipygeojson

@property
def gdf(self):
"""Get the geodataframe associated with the AOI."""
if self.gee:
if not self.feature_collection:
return None

self._load_gdf()

return self._gdf

@gdf.setter
def gdf(self, value):
"""Set the gdf value. Used mainly to reset the gdf value."""
self._gdf = value

def _load_gdf(self):
"""Return a geodataframe from a feature collection."""
features = self.feature_collection.getInfo()["features"]
self._gdf = gpd.GeoDataFrame.from_features(features).set_crs(epsg=4326)

if self.method in ["ADMIN0", "ADMIN1", "ADMIN2"]:

gaul_country = str(self._gdf.ADM0_CODE.unique()[0])
iso = json.loads(self.MAPPING.read_text())[gaul_country]
self._gdf["ISO"] = iso
6 changes: 5 additions & 1 deletion sepal_ui/aoi/aoi_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,11 @@ def _update_aoi(self, *args) -> Self:
if self.map_:
self.map_.remove_layer("aoi", none_ok=True)
self.map_.zoom_bounds(self.model.total_bounds())
self.map_.add_layer(self.model.get_ipygeojson(self.map_style))

if self.gee:
self.map_.add_ee_layer(self.model.feature_collection, {}, "aoi")
else:
self.map_.add_layer(self.model.get_ipygeojson(self.map_style), "aoi")

self.aoi_dc.hide()

Expand Down
3 changes: 2 additions & 1 deletion sepal_ui/message/en/locale.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@
"no_draw": "Please draw a shape in the map",
"no_admlyr": "Select an administrative layer",
"invalid_code": "The code is not in the database",
"no_gdf": "You must set the gdf before interacting with it"
"no_gdf": "You must set the gdf before interacting with it",
"no_fc": "You have to select a feature collection first"
}
},
"mapping": {
Expand Down

0 comments on commit 89c7b25

Please sign in to comment.