Skip to content

Commit

Permalink
Merge pull request #309 from 12rambau/master
Browse files Browse the repository at this point in the history
v_2.4.0
  • Loading branch information
12rambau authored Oct 19, 2021
2 parents 67757ae + 411cc5a commit 2db8899
Show file tree
Hide file tree
Showing 17 changed files with 327 additions and 84 deletions.
2 changes: 1 addition & 1 deletion .cz.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ commitizen:
changelog_file: CHANGELOG.md
tag_format: v_$major.$minor.$patch$prerelease
update_changelog_on_bump: false
version: 2.3.0
version: 2.4.0
version_files:
- setup.py:version
- sepal_ui/__init__.py:__version__
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
## v_2.4.0 (2021-10-19)

### Refactor

- make v_model default and empty value as None instead of empty string
- be consistent when concatenating

### Fix

- replace default v_model fon VectorField as trait
- doc build failed
- only display SepalWarning in Alerts
- this assignation was overwritting the w_asset dict
- vector field method. closes #306

### Feat

- filter by column and value in AOI. - closes: #296

## v_2.3.0 (2021-10-06)

### Fix
Expand Down
4 changes: 4 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ Sepal_ui
.. image:: https://badge.fury.io/py/sepal-ui.svg
:target: https://badge.fury.io/py/sepal-ui
:alt: PyPI version

.. image:: https://img.shields.io/pypi/dm/sepal-ui?color=307CC2&logo=python&logoColor=gainsboro
:target: https://pypi.org/project/sepal-ui/
:alt: PyPI - Downloads

.. image:: https://github.com/12rambau/sepal_ui/actions/workflows/unit.yml/badge.svg
:target: https://github.com/12rambau/sepal_ui/actions/workflows/unit.yml
Expand Down
8 changes: 8 additions & 0 deletions docs/source/modules/sepal_ui.scripts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ sepal\_ui.scripts.utils module
:undoc-members:
:show-inheritance:

sepal\_ui.scripts.warning module
------------------------------

.. automodule:: sepal_ui.scripts.warning
:members:
:undoc-members:
:show-inheritance:

Module contents
---------------

Expand Down
2 changes: 1 addition & 1 deletion sepal_ui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

__author__ = """Pierrick Rambaud"""
__email__ = "[email protected]"
__version__ = "2.3.0"
__version__ = "2.4.0"

# direct access to colors
from sepal_ui.frontend import styles
Expand Down
34 changes: 24 additions & 10 deletions sepal_ui/aoi/aoi_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ class AoiModel(Model):
feature_collection (ee.FeatureCollection): the featurecollection of the aoi (only in ee model)
gdf (geopandas.GeoDataFrame): the geopandas representation of the aoi
ipygeojson (GeoJson layer): the ipyleaflet representation of the selected aoi
..deprecated:: 2.3.2 : 'asset_name' will be used as variable to store 'ASSET' method info. To get the destination saved asset id, please use 'dst_asset_id' variable.
"""

# const params
Expand Down Expand Up @@ -133,7 +136,9 @@ def set_default(self, vector=None, admin=None, asset=None):

# save the default values
self.default_vector = vector
self.default_asset = self.asset_name = asset
self.default_asset = self.asset_name = (
{"pathname": asset, "column": "ALL", "value": None} if asset else None
)
self.default_admin = self.admin = admin

# cast the vector to json
Expand Down Expand Up @@ -187,11 +192,22 @@ def set_object(self, method=None):
def _from_asset(self, asset_name):
"""set the ee.FeatureCollection output from an existing asset"""

# check that I have access to the asset
ee_col = ee.FeatureCollection(asset_name)
ee_col.geometry().bounds().coordinates().get(
0
).getInfo() # it will raise and error if we cannot access the asset
if not (asset_name["pathname"]):
raise Exception("Please select an asset.")

if asset_name["column"] != "ALL":
if asset_name["value"] is None:
raise Exception("Please select a value.")

self.name = Path(asset_name["pathname"]).stem.replace(self.ASSET_SUFFIX, "")
ee_col = ee.FeatureCollection(asset_name["pathname"])

if asset_name["column"] != "ALL":

column = asset_name["column"]
value = asset_name["value"]
ee_col = ee_col.filterMetadata(column, "equals", value)
self.name = f"{self.name}_{column}_{value}"

# set the feature collection
self.feature_collection = ee_col
Expand All @@ -204,7 +220,6 @@ def _from_asset(self, asset_name):
).set_crs(epsg=4326)

# set the name
self.name = Path(asset_name).stem.replace(self.ASSET_SUFFIX, "")

return self

Expand Down Expand Up @@ -264,7 +279,7 @@ def _from_vector(self, vector_json):
self.name = vector_file.stem

# filter it if necessary
if vector_json["value"]:
if vector_json["value"] != None:
self.gdf = self.gdf[self.gdf[vector_json["column"]] == vector_json["value"]]
self.name = f"{self.name}_{vector_json['column']}_{vector_json['value']}"

Expand Down Expand Up @@ -501,8 +516,7 @@ def export_to_asset(self):
asset_name = self.ASSET_SUFFIX + self.name
asset_id = str(Path(self.folder, asset_name))

# set the asset name
self.asset_name = asset_id
self.dst_asset_id = asset_id

# check if the table already exist
if asset_id in [a["name"] for a in gee.get_assets(self.folder)]:
Expand Down
4 changes: 2 additions & 2 deletions sepal_ui/aoi/aoi_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,8 @@ def __init__(self, methods="ALL", map_=None, gee=True, folder=None, **kwargs):
if self.map_:
self.w_draw = TextField(label=ms.aoi_sel.aoi_name).hide()
if self.ee:
self.w_asset = sw.AssetSelect(
label=ms.aoi_sel.asset, folder=self.folder, types=["TABLE"]
self.w_asset = sw.VectorField(
label=ms.aoi_sel.asset, gee=True, folder=self.folder, types=["TABLE"]
).hide()

# group them together with the same key as the select_method object
Expand Down
36 changes: 30 additions & 6 deletions sepal_ui/scripts/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

import sepal_ui

from .warning import SepalWarning


def hide_component(widget):
"""
Expand Down Expand Up @@ -251,16 +253,38 @@ def wrapper_loading(self, *args, **kwargs):
value = None
try:
# Catch warnings in the process function
with warnings.catch_warnings(record=True) as w:
with warnings.catch_warnings(record=True) as w_list:
value = func(self, *args, **kwargs)

# Check if there are warnings in the function and append them
# Use append msg due to several warnings could be triggered
if w:
[
alert_.append_msg(warning.message.args[0], type_="warning")
for warning in w
# Use append msg as several warnings could be triggered
if w_list:

# split the warning list
w_list_sepal = [
w for w in w_list if isinstance(w.message, SepalWarning)
]

# display the sepal one
ms_list = [
f"{w.category.__name__}: {w.message.args[0]}"
for w in w_list_sepal
]
[alert_.append_msg(ms, type_="warning") for ms in ms_list]

# only display them in the console if debug mode
if debug:

def custom_showwarning(w):
return warnings.showwarning(
message=w.message,
category=w.category,
filename=w.filename,
lineno=w.lineno,
line=w.line,
)

[custom_showwarning(w) for w in w_list]

except Exception as e:
alert_.add_msg(f"{e}", "error")
Expand Down
14 changes: 14 additions & 0 deletions sepal_ui/scripts/warning.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from deprecated.sphinx import versionadded


@versionadded(
version="2.3.1",
reason="Added to avoid display of unrelevant warning to the end user",
)
class SepalWarning(Warning):
"""
A custom warning class that will be the only one to be displayed in the Alert in voila.
The other normal warning such as lib DeprecationWarning will be displayed in the notebook but hidden to the end user
"""

pass
79 changes: 54 additions & 25 deletions sepal_ui/sepalwidgets/inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import json

import ipyvuetify as v
from traitlets import link, Int, Any, List, observe
from traitlets import link, Int, Any, List, observe, Dict, Unicode
from ipywidgets import jslink
import pandas as pd
import ee
Expand Down Expand Up @@ -109,6 +109,7 @@ class FileInput(v.Flex, SepalWidget):
"""

file = Any("")
v_model = Unicode(None, allow_none=True).tag(sync=True)

def __init__(
self,
Expand All @@ -125,10 +126,9 @@ def __init__(

self.extentions = extentions
self.folder = folder
self.v_model = v_model

self.selected_file = v.TextField(
readonly=True, label="Selected file", class_="ml-5 mt-5", v_model=self.file
readonly=True, label="Selected file", class_="ml-5 mt-5", v_model=None
)

self.loading = v.ProgressLinear(
Expand Down Expand Up @@ -201,13 +201,13 @@ def reset(self, *args):

root = Path("~").expanduser()

if self.v_model != "":
if self.v_model is not None:

# move to root
self._on_file_select({"new": root})

# remove v_model
self.v_model = ""
self.v_model = None

return self

Expand Down Expand Up @@ -677,11 +677,13 @@ def decrement(self, widget, event, data):

class VectorField(v.Col, SepalWidget):
"""
A custom input widget to load vector data. The user will provide a vector file compatible with fiona.
A custom input widget to load vector data. The user will provide a vector file compatible with fiona or a GEE feature collection.
The user can then select a specific shape by setting column and value fields.
Args:
label (str): the label of the file input field, default to 'vector file'.
gee (bool, optional): whether to use GEE assets or local vectors.
**asset_select_kwargs: When gee=True, extra args will be used for AssetSelect
Attributes:
original_gdf (geopandas.gdf): The originally selected dataframe
Expand All @@ -692,24 +694,33 @@ class VectorField(v.Col, SepalWidget):
w_value (v.Select): The Select widget to select the value in the selected column
"""

default_v_model = {
"pathname": None,
"column": None,
"value": None,
}

column_base_items = [
{"text": "Use all features", "value": "ALL"},
{"divider": True},
]
v_model = Dict(
{
"pathname": None,
"column": None,
"value": None,
}
)

def __init__(self, label="vector_file", **kwargs):
def __init__(self, label="vector_file", gee=False, **kwargs):

# save the df for column naming (not using a gdf as geometry are useless)
self.df = None
self.feature_collection = None

self.column_base_items = [
{"text": "Use all features", "value": "ALL"},
{"divider": True},
]

# set the 3 wigets
self.w_file = FileInput([".shp", ".geojson", ".gpkg", ".kml"], label=label)
if not gee:
self.w_file = FileInput([".shp", ".geojson", ".gpkg", ".kml"], label=label)
else:
# Don't care about 'types' arg. It will only work with tables.
asset_select_kwargs = {k: v for k, v in kwargs.items() if k in ["folder"]}
self.w_file = AssetSelect(types=["TABLE"], **asset_select_kwargs)

self.w_column = v.Select(
_metadata={"name": "column"},
items=self.column_base_items,
Expand All @@ -723,7 +734,6 @@ def __init__(self, label="vector_file", **kwargs):

# create the Col Field
self.children = [self.w_file, self.w_column, self.w_value]
self.v_model = self.default_v_model

super().__init__(**kwargs)

Expand Down Expand Up @@ -752,6 +762,7 @@ def _update_file(self, change):
self.w_column.items = self.w_value.items = []
self.w_column.v_model = self.w_value.v_model = None
self.df = None
self.feature_collection = None

# set the pathname value
self.v_model["pathname"] = change["new"]
Expand All @@ -760,13 +771,20 @@ def _update_file(self, change):
if not change["new"]:
return self

# read the file
self.df = gpd.read_file(change["new"], ignore_geometry=True)
if isinstance(self.w_file, FileInput):
# read the file
self.df = gpd.read_file(change["new"], ignore_geometry=True)
columns = self.df.columns.to_list()

elif isinstance(self.w_file, AssetSelect):
self.feature_collection = ee.FeatureCollection(change["new"])
columns = self.feature_collection.first().getInfo()["properties"]
columns = [
str(col) for col in columns if col not in ["system:index", "Shape_Area"]
]

# update the columns
self.w_column.items = self.column_base_items + sorted(
set(self.df.columns.to_list())
)
self.w_column.items = self.column_base_items + sorted(set(columns))

self.w_column.v_model = "ALL"

Expand All @@ -789,7 +807,18 @@ def _update_column(self, change):
return self

# read the colmun
self.w_value.items = sorted(set(self.df[change["new"]].to_list()))
if isinstance(self.w_file, FileInput):
values = self.df[change["new"]].to_list()

elif isinstance(self.w_file, AssetSelect):
values = (
self.feature_collection.distinct(change["new"])
.aggregate_array(change["new"])
.getInfo()
)

self.w_value.items = sorted(set(values))

su.show_component(self.w_value)

return self
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from distutils.core import setup
from pathlib import Path

version = "2.3.0"
version = "2.4.0"

setup(
name="sepal_ui",
Expand Down
Loading

0 comments on commit 2db8899

Please sign in to comment.