Skip to content

Commit

Permalink
Merge branch 'catch_error' of github.com:12rambau/sepal_ui into catch…
Browse files Browse the repository at this point in the history
…_error
  • Loading branch information
12rambau committed Sep 17, 2023
2 parents 2903c2a + 20900dd commit 940cdde
Show file tree
Hide file tree
Showing 22 changed files with 396 additions and 208 deletions.
5 changes: 5 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "Python 3",
"image": "mcr.microsoft.com/devcontainers/python",
"postCreateCommand": "python -m pip install nox pre-commit && pre-commit install"
}
4 changes: 1 addition & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ jobs:
- uses: actions/setup-python@v2

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine build
run: pip install setuptools wheel twine build

- name: Build and publish
env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.8", "3.9", "3.10"]
python-version: ["3.8", "3.9", "3.10", "3.11"]
include:
- os: macos-latest # macos test
python-version: "3.10"
Expand Down
7 changes: 5 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ classifiers = [
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
]
requires-python = ">=3.8"
dependencies = [
Expand All @@ -37,6 +38,8 @@ dependencies = [
"planet>=2",
"pyarrow",
"localtileserver>=0.7.0", # first pure rio version
"pygaul>=0.3.1", # use the class implementation
"pygadm>=0.5.0", # use the class implementation
# miscellaneous
"python-box",
"tqdm",
Expand Down Expand Up @@ -83,7 +86,7 @@ test = [
"pytest-regressions",
]
doc = [
"sphinx",
"sphinx<7",
"jupyter-sphinx",
"pydata-sphinx-theme",
"sphinx-notfound-page",
Expand Down Expand Up @@ -174,4 +177,4 @@ ignore-path-errors = ["docs/source/index.rst;D000"]
using = "PEP631:test;dev;doc"

[tool.codespell]
skip = 'CHANGELOG.md,sepal_ui/message/**/*.json'
skip = 'CHANGELOG.md,sepal_ui/message/**/*.json,sepal_ui/data/gaul_iso.json'
77 changes: 17 additions & 60 deletions sepal_ui/aoi/aoi_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import ee
import geopandas as gpd
import pandas as pd
import pygadm
import pygaul
import traitlets as t
from ipyleaflet import GeoJSON
from typing_extensions import Self
Expand All @@ -27,28 +29,8 @@ class AoiModel(Model):
# ### dataset const ###
# ###########################################################################

FILE: List[Path] = [
Path(__file__).parents[1] / "data" / "gadm_database.parquet",
Path(__file__).parents[1] / "data" / "gaul_database.parquet",
]
"Paths to the GADM(0) and GAUL(1) database"

CODE: List[str] = ["GID_{}", "ADM{}_CODE"]
"GADM(0) and GAUL(1) administrative codes key format"

NAME: List[str] = ["NAME_{}", "ADM{}_NAME"]
"GADM(0) and GAUL(1) naming key format"

ISO: List[str] = ["GID_0", "ISO 3166-1 alpha-3"]
"GADM(0) and GAUL(1) iso codes key"

GADM_BASE_URL: str = (
"https://geodata.ucdavis.edu/gadm/gadm4.1/json/gadm41_{}_{}.json"
)
"The base url to download gadm maps"

GAUL_ASSET: str = "FAO/GAUL/2015/level{}"
"The GAUL asset name"
MAPPING: Path = Path(__file__).parents[1] / "data" / "gaul_iso.json"
"GAUL -> ISO-3 mapping of country code"

ASSET_SUFFIX: str = "aoi_"
"The suffix to identify the asset in GEE"
Expand Down Expand Up @@ -391,56 +373,31 @@ def _from_geo_json(self, geo_json: dict) -> Self:
return self

def _from_admin(self, admin: str) -> Self:
"""Set the object according to given an administrative number in the GADM norm.
"""Set the object according to the given an administrative code in the GADM/GAUL codes.
Args:
admin: the admin code corresponding to FAO GAUl (if gee) or GADM
"""
if not admin:
raise Exception(ms.aoi_sel.exception.no_admlyr)

# get the admin level corresponding to the given admin code
df = pd.read_parquet(self.FILE[self.gee]).astype(str)

# extract the first element that include this administrative code and set the level accordingly
is_in = df.filter([self.CODE[self.gee].format(i) for i in range(3)]).isin(
[admin]
)

if not is_in.any().any():
raise Exception(ms.aoi_sel.exception.invalid_code)

index = 3 if self.gee else -1
level = is_in[~((~is_in).all(axis=1))].idxmax(1).iloc[0][index]

# get the data from either the pygaul or the pygadm libs
# pygaul needs extra work as ISO codes are not included in the GEE dataset
if self.gee:

# get the feature_collection
self.feature_collection = ee.FeatureCollection(
self.GAUL_ASSET.format(level)
).filter(ee.Filter.eq(f"ADM{level}_CODE", int(admin)))

# transform it into gdf
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

else:
# save the country iso_code
iso_3 = admin[:3]

# read the data from server
level_gdf = gpd.read_file(self.GADM_BASE_URL.format(iso_3, level))
level_gdf.rename(columns={"COUNTRY": "NAME_0"}, inplace=True)
self.gdf = level_gdf[level_gdf[self.CODE[self.gee].format(level)] == admin]

# set the name using the layer
r = df[df[self.CODE[self.gee].format(level)] == admin].iloc[0]
names = [
su.normalize_str(r[self.NAME[self.gee].format(i)])
if i
else r[self.ISO[self.gee]]
for i in range(int(level) + 1)
]
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)

return self
Expand Down
44 changes: 17 additions & 27 deletions sepal_ui/aoi/aoi_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
from typing import Dict, List, Optional, Union

import ipyvuetify as v
import pandas as pd
import pygadm
import pygaul
import traitlets as t
from deprecated.sphinx import versionadded
from typing_extensions import Self
Expand Down Expand Up @@ -111,7 +112,7 @@ def __init__(
) -> None:
"""An admin level selector.
It is binded to ee (GAUL 2015) or not (GADM 2021). allows to select administrative codes taking into account the administrative parent code and displaying humanly readable administrative names.
It is binded to ee (GAUL 2015) or not (GADM). Allows to select administrative codes taking into account the administrative parent code and displaying humanly readable administrative names.
Args:
level: The administrative level of the field
Expand Down Expand Up @@ -144,30 +145,19 @@ def get_items(self, filter_: str = "") -> Self:
r"""Update the item list based on the given filter.
Args:
filter\_ (str): The code of the parent v_model to filter the current results
filter\_: The code of the parent v_model to filter the current results
"""
# extract the level list
df = (
pd.read_parquet(AoiModel.FILE[self.gee])
.astype(str)
.drop_duplicates(subset=AoiModel.CODE[self.gee].format(self.level))
.sort_values(AoiModel.NAME[self.gee].format(self.level))
)

# filter it
if filter_:
df = df[df[AoiModel.CODE[self.gee].format(self.level - 1)] == filter_]
AdmNames = pygaul.AdmNames if self.gee else pygadm.AdmNames
df = AdmNames(admin=filter_, content_level=self.level)
df = df.sort_values(by=[df.columns[0]])

# formatted as a item list for a select component
self.items = [
{
"text": su.normalize_str(
r[AoiModel.NAME[self.gee].format(self.level)], folder=False
),
"value": r[AoiModel.CODE[self.gee].format(self.level)],
}
for _, r in df.iterrows()
]
# first column will be the name, second the code
items = []
for _, r in df.iterrows():
text = su.normalize_str(r.iloc[0], folder=False)
items.append({"text": text, "value": str(r.iloc[1])})
self.items = items

return self

Expand Down Expand Up @@ -415,12 +405,12 @@ def _activate(self, change: dict) -> None:
else:
self.aoi_dc.hide()

# init the name to the current value
now = dt.now().strftime("%Y-%m-%d_%H-%M-%S")
self.w_draw.v_model = None if change["new"] is None else f"Manual_aoi_{now}"

# activate the correct widget
w = next((w for k, w in self.components.items() if k == change["new"]), None)
w is None or w.show()

# init the name to the current value
now = dt.now().strftime("%Y-%m-%d_%H-%M-%S")
self.w_draw.v_model = None if change["new"] is None else f"Manual_aoi_{now}"

return
Binary file removed sepal_ui/data/gadm_database.parquet
Binary file not shown.
Binary file removed sepal_ui/data/gaul_database.parquet
Binary file not shown.
Loading

0 comments on commit 940cdde

Please sign in to comment.