From 6ee5ec60d590d23546971f477978eb62c94b6c65 Mon Sep 17 00:00:00 2001 From: Rambaud Pierrick <12rambau@users.noreply.github.com> Date: Fri, 9 Jul 2021 12:22:48 +0200 Subject: [PATCH 1/7] typo --- doc/en.rst | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/doc/en.rst b/doc/en.rst index e6cb508..7e3fd5e 100644 --- a/doc/en.rst +++ b/doc/en.rst @@ -35,15 +35,4 @@ The second drawer will allow you to create a grid on top of any AOI. THe grid is an extra column is added to the grid called "batch" you can select the number of cell you want to add to each batch. The naming will be set automatically according to your AOI name and the batch number. -By validating, yuou will create ageojson file that will live in :code:`module_results/aoi/.geojson`and launch the creation of the same grid in your GEE assets. - - - - - - - - - - - +By validating, yuou will create ageojson file that will live in :code:`module_results/aoi/.geojson` and launch the creation of the same grid in your GEE assets. From 4ea3d884d3c105f527691e313b002d79762a5664 Mon Sep 17 00:00:00 2001 From: Rambaud Pierrick <12rambau@users.noreply.github.com> Date: Thu, 7 Oct 2021 12:22:50 +0200 Subject: [PATCH 2/7] docs: typo --- doc/en.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en.rst b/doc/en.rst index 7e3fd5e..ff56ff9 100644 --- a/doc/en.rst +++ b/doc/en.rst @@ -35,4 +35,4 @@ The second drawer will allow you to create a grid on top of any AOI. THe grid is an extra column is added to the grid called "batch" you can select the number of cell you want to add to each batch. The naming will be set automatically according to your AOI name and the batch number. -By validating, yuou will create ageojson file that will live in :code:`module_results/aoi/.geojson` and launch the creation of the same grid in your GEE assets. +By validating, you will create a geojson file that will live in :code:`module_results/aoi/.geojson` and launch the creation of the same grid in your GEE assets. From cad813643ff0fc0bea9ac4788c1d3d8d53faf8c7 Mon Sep 17 00:00:00 2001 From: 12rambau Date: Tue, 23 Nov 2021 11:09:07 +0000 Subject: [PATCH 3/7] fix grid building --- component/scripts/tiling.py | 17 ++++------- component/tile/tile_tile.py | 5 ++-- no_ui.ipynb | 2 +- tiling_ui.ipynb | 58 +++++-------------------------------- 4 files changed, 17 insertions(+), 65 deletions(-) diff --git a/component/scripts/tiling.py b/component/scripts/tiling.py index 2965982..52c88e9 100644 --- a/component/scripts/tiling.py +++ b/component/scripts/tiling.py @@ -60,20 +60,15 @@ def set_grid(aoi, grid_batch, grid_name, output): output.add_live_msg(cm.build_grid) + # count the number of batch cell in width + batch_width = (len(lat_filter)-1) // grid_batch # create the grid - batch = [] - x = [] - y = [] - names = [] - squares = [] - for i, coords in enumerate(product(range(len(lon_filter)-1), range(len(lat_filter)-1))): - - # get the x and y index - ix = coords[0] - iy = coords[1] + batch, x, y, names, squares = [], [], [], [], [] + for ix, iy in product(range(len(lon_filter)-1), range(len(lat_filter)-1)): # fill the grid values - batch.append(i//grid_batch) + batch_id = (ix // grid_batch) * (batch_width + 1) + (iy // grid_batch) + batch.append(batch_id) x.append(ix + x_offset) y.append(iy + y_offset) names.append(f'L15-{x[-1]:4d}E-{y[-1]:4d}N.tif') diff --git a/component/tile/tile_tile.py b/component/tile/tile_tile.py index fc29e16..849ce2e 100644 --- a/component/tile/tile_tile.py +++ b/component/tile/tile_tile.py @@ -38,8 +38,7 @@ def __init__(self, m, aoi_model): self.batch_size = sw.NumberField( label = cm.tile.nb_batch_lbl, v_model=1, - max_=100, - type = 'number' + max_=100 ) # the aoi default btn is not set to btn anymore (to avoid conflict with the standard btn) @@ -78,7 +77,7 @@ def create_grid(self, widget, data, event): asset = os.path.join(folder, grid_name) # export - if not gee.is_asset(grid_name, folder): + if not gee.is_asset(asset, folder): task_config = { 'collection': grid, diff --git a/no_ui.ipynb b/no_ui.ipynb index ef18812..3f717e5 100644 --- a/no_ui.ipynb +++ b/no_ui.ipynb @@ -108,7 +108,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.9" + "version": "3.8.10" } }, "nbformat": 4, diff --git a/tiling_ui.ipynb b/tiling_ui.ipynb index b54e196..d0c0a98 100644 --- a/tiling_ui.ipynb +++ b/tiling_ui.ipynb @@ -2,38 +2,9 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "a966567e90c64e33b912eec072750652", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Styles()" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "a2cdc1414dfc4fe78490eab47cd3a5b3", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "ResizeTrigger()" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "from sepal_ui import aoi\n", "from component import tile\n", @@ -42,24 +13,11 @@ }, { "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "462aad9dc7ed488e95370d805f03252b", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "AoiTile(align_center=True, children=[Card(children=[Html(children=['Select AOI'], tag='h2'), Flex(children=[La…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], "source": [ "#create the tile \n", "ig_aoi = aoi.AoiTile(methods = ['ADMIN0', 'ADMIN1', 'ADMIN2', 'ASSET'])\n", @@ -106,7 +64,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.9" + "version": "3.8.10" } }, "nbformat": 4, From a213bcf35ebf38a10e404c8bfe5253a4d490d480 Mon Sep 17 00:00:00 2001 From: 12rambau Date: Tue, 23 Nov 2021 11:12:57 +0000 Subject: [PATCH 4/7] deploy module requirements --- requirements.txt | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..9bd9713 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,24 @@ +# these libs are requested to build common python libs +# if you are an advance user and are sure to not use them you can comment the following lines +wheel +Cython +pybind11 +pre-commit + +# if you require GDAL and or pyproj in your module please uncomment these lines +# there are set up to be inlined with SEPAL implementation of GDAL and PROJ version +GDAL==3.0.4 +pyproj<3.0.0 + +# the base lib to run any sepal_ui based app +# don't forget to fix it to a specific version when you're app is ready +sepal_ui==2.4.0 + +# custom libs +geopandas==0.10.2 +ipyvuetify==1.8.1 +numpy==1.17.4 +pyperclip==1.8.2 +Shapely==1.8.0 +traitlets==5.1.1 +Unidecode==1.3.2 From 123fe4b97c60ab80f6284af31f45b53218517423 Mon Sep 17 00:00:00 2001 From: 12rambau Date: Tue, 23 Nov 2021 11:21:18 +0000 Subject: [PATCH 5/7] apply pre-commits hooks --- .pre-commit-config.yaml | 13 ++ about_ui.ipynb | 8 +- component/message/__init__.py | 18 +-- component/message/test_translation.ipynb | 8 +- component/parameter/__init__.py | 2 +- component/parameter/directory.py | 10 +- component/scripts/__init__.py | 2 +- component/scripts/tiling.py | 113 +++++++++-------- component/scripts/utils.py | 13 +- component/tile/__init__.py | 2 +- component/tile/download_tile.py | 149 ++++++++++++----------- component/tile/tile_tile.py | 129 ++++++++++---------- component/widget/__init__.py | 2 +- component/widget/link_dialog.py | 58 +++++---- download_ui.ipynb | 6 +- no_ui.ipynb | 2 +- tiling_ui.ipynb | 8 +- ui.ipynb | 35 ++---- 18 files changed, 293 insertions(+), 285 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..659f68d --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,13 @@ +default_language_version: + python: python3 + +repos: +- repo: https://github.com/psf/black + rev: 8a59528c2d8ae1ef5f366039c728614aaf1a470b + hooks: + - id: black + - id: black-jupyter +- repo: https://github.com/kynan/nbstripout + rev: 0.5.0 + hooks: + - id: nbstripout \ No newline at end of file diff --git a/about_ui.ipynb b/about_ui.ipynb index 3e94e26..e1ddddf 100644 --- a/about_ui.ipynb +++ b/about_ui.ipynb @@ -8,10 +8,10 @@ "source": [ "from sepal_ui import sepalwidgets as sw\n", "\n", - "#create the Html body of your about section \n", - "ig_about = sw.TileAbout('utils/about.md')\n", + "# create the Html body of your about section\n", + "ig_about = sw.TileAbout(\"utils/about.md\")\n", "\n", - "#create a disclaimer\n", + "# create a disclaimer\n", "ig_disclaimer = sw.TileDisclaimer()" ] }, @@ -21,7 +21,7 @@ "metadata": {}, "outputs": [], "source": [ - "#will only be displayed if voila is launch from this tile\n", + "# will only be displayed if voila is launch from this tile\n", "ig_about" ] }, diff --git a/component/message/__init__.py b/component/message/__init__.py index eb1b797..0e78b9c 100644 --- a/component/message/__init__.py +++ b/component/message/__init__.py @@ -3,17 +3,17 @@ from sepal_ui.translator import Translator -# the sepal_ui allows you to create a translation interface -# at the moment this variable is not yet available but it's a good practice to build your app translatio-ready +# the sepal_ui allows you to create a translation interface +# at the moment this variable is not yet available but it's a good practice to build your app translatio-ready # first select the language env variable to load the destination locale # it will default to en -lang = 'en' -if 'CUSTOM_LANGUAGE' in os.environ: - lang = os.environ['CUSTOM_LANGUAGE'] +lang = "en" +if "CUSTOM_LANGUAGE" in os.environ: + lang = os.environ["CUSTOM_LANGUAGE"] -# create a ms object that will be used to translate all the messages -# the base language is english and every untranslated messages will be fallback to the english key -# complete the json file the add keys in the app +# create a ms object that will be used to translate all the messages +# the base language is english and every untranslated messages will be fallback to the english key +# complete the json file the add keys in the app # avoid hard written messages at all cost -cm = Translator(Path(__file__).parent, lang) \ No newline at end of file +cm = Translator(Path(__file__).parent, lang) diff --git a/component/message/test_translation.ipynb b/component/message/test_translation.ipynb index 99f55ff..be3c85d 100644 --- a/component/message/test_translation.ipynb +++ b/component/message/test_translation.ipynb @@ -8,7 +8,7 @@ "outputs": [], "source": [ "# this notebook is only here to provide the position of the missing keys\n", - "# the base dict is the english one. \n", + "# the base dict is the english one.\n", "# it should be complete\n", "\n", "from pathlib import Path\n", @@ -23,8 +23,8 @@ "metadata": {}, "outputs": [], "source": [ - "# select the language you want to test \n", - "locale = 'en'" + "# select the language you want to test\n", + "locale = \"en\"" ] }, { @@ -45,7 +45,7 @@ "outputs": [], "source": [ "# normally there is only one key lissing ('not_tranlated')\n", - "# at the root of the file \n", + "# at the root of the file\n", "print(ms.missing_keys())" ] }, diff --git a/component/parameter/__init__.py b/component/parameter/__init__.py index 5d38036..d33f132 100644 --- a/component/parameter/__init__.py +++ b/component/parameter/__init__.py @@ -1 +1 @@ -from .directory import * \ No newline at end of file +from .directory import * diff --git a/component/parameter/directory.py b/component/parameter/directory.py index b13056a..5e7639c 100644 --- a/component/parameter/directory.py +++ b/component/parameter/directory.py @@ -1,9 +1,7 @@ from pathlib import Path -module_res_dir = Path('~', 'module_results').expanduser() -module_res_dir.mkdir(exist_ok = True) - -down_dir = module_res_dir.joinpath('aoi') -down_dir.mkdir(exist_ok = True) - +module_res_dir = Path("~", "module_results").expanduser() +module_res_dir.mkdir(exist_ok=True) +down_dir = module_res_dir.joinpath("aoi") +down_dir.mkdir(exist_ok=True) diff --git a/component/scripts/__init__.py b/component/scripts/__init__.py index 948cbfe..3550a9b 100644 --- a/component/scripts/__init__.py +++ b/component/scripts/__init__.py @@ -1,2 +1,2 @@ from .utils import * -from .tiling import * \ No newline at end of file +from .tiling import * diff --git a/component/scripts/tiling.py b/component/scripts/tiling.py index 52c88e9..e97fea1 100644 --- a/component/scripts/tiling.py +++ b/component/scripts/tiling.py @@ -16,18 +16,19 @@ d.getcontext().prec = 15 + def set_grid(aoi, grid_batch, grid_name, output): """compute a grid around a given aoi (ee.FeatureCollection) that fits the Planet Lab requirements""" - - # get the shape of the aoi in EPSG:4326 proj - aoi_gdf = aoi.to_crs('EPSG:3857') - + + # get the shape of the aoi in EPSG:4326 proj + aoi_gdf = aoi.to_crs("EPSG:3857") + output.add_live_msg(cm.digest_aoi) - + # retreive the bounding box aoi_bb = sg.box(*aoi_gdf.total_bounds) aoi_bb.bounds - + # compute the longitude and latitude in the apropriate CRS crs_4326 = CRS.from_epsg(4326) crs_3857 = CRS.from_epsg(3857) @@ -37,77 +38,83 @@ def set_grid(aoi, grid_batch, grid_name, output): bl = proj.transform(crs_min_x, crs_min_y) tr = proj.transform(crs_max_x, crs_max_y) - # the planet grid is constructing a 2048x2048 grid of SQUARES. + # the planet grid is constructing a 2048x2048 grid of SQUARES. # The latitude extends is bigger (20048966.10m VS 20026376.39) so to ensure the "squariness" - # Planet lab have based the numerotation and extends of it square grid on the longitude only. + # Planet lab have based the numerotation and extends of it square grid on the longitude only. # the extreme -90 and +90 band it thus exlucded but there are no forest there so we don't care - longitudes = np.linspace(bl[0], tr[0], 2048+1) + longitudes = np.linspace(bl[0], tr[0], 2048 + 1) # the planet grid size cut the world in 248 squares vertically and horizontally - box_size = (tr[0]-bl[0])/2048 + box_size = (tr[0] - bl[0]) / 2048 - # filter with the geometry bounds min_lon, min_lat, max_lon, max_lat = aoi_gdf.total_bounds - # filter lon and lat - lon_filter = longitudes[(longitudes > (min_lon - box_size)) & (longitudes < max_lon + box_size)] - lat_filter = longitudes[(longitudes > (min_lat - box_size)) & (longitudes < max_lat + box_size)] + # filter lon and lat + lon_filter = longitudes[ + (longitudes > (min_lon - box_size)) & (longitudes < max_lon + box_size) + ] + lat_filter = longitudes[ + (longitudes > (min_lat - box_size)) & (longitudes < max_lat + box_size) + ] - # get the index offset + # get the index offset x_offset = np.nonzero(longitudes == lon_filter[0])[0][0] y_offset = np.nonzero(longitudes == lat_filter[0])[0][0] - + output.add_live_msg(cm.build_grid) - - # count the number of batch cell in width - batch_width = (len(lat_filter)-1) // grid_batch + + # count the number of batch cell in width + batch_width = (len(lat_filter) - 1) // grid_batch # create the grid batch, x, y, names, squares = [], [], [], [], [] - for ix, iy in product(range(len(lon_filter)-1), range(len(lat_filter)-1)): - + for ix, iy in product(range(len(lon_filter) - 1), range(len(lat_filter) - 1)): + # fill the grid values batch_id = (ix // grid_batch) * (batch_width + 1) + (iy // grid_batch) - batch.append(batch_id) + batch.append(batch_id) x.append(ix + x_offset) y.append(iy + y_offset) - names.append(f'L15-{x[-1]:4d}E-{y[-1]:4d}N.tif') - squares.append(sg.box(lon_filter[ix], lat_filter[iy], lon_filter[ix+1], lat_filter[iy+1])) - + names.append(f"L15-{x[-1]:4d}E-{y[-1]:4d}N.tif") + squares.append( + sg.box( + lon_filter[ix], lat_filter[iy], lon_filter[ix + 1], lat_filter[iy + 1] + ) + ) + # create a buffer grid in lat-long - grid = gpd.GeoDataFrame({'batch': batch, 'x':x, 'y':y, 'names':names, 'geometry':squares}, crs='EPSG:3857') + grid = gpd.GeoDataFrame( + {"batch": batch, "x": x, "y": y, "names": names, "geometry": squares}, + crs="EPSG:3857", + ) - # cut the grid to the aoi extends - mask = grid.intersects(aoi_gdf.dissolve()['geometry'][0]) + # cut the grid to the aoi extends + mask = grid.intersects(aoi_gdf.dissolve()["geometry"][0]) grid = grid.loc[mask] - + # project back to 4326 - grid = grid.to_crs('EPSG:4326') - - # export the grid as a json file - path = cp.down_dir.joinpath(f'{grid_name}.geojson') - grid.to_file(path, driver='GeoJSON') - - output.add_live_msg(cm.grid_complete, 'success') - + grid = grid.to_crs("EPSG:4326") + + # export the grid as a json file + path = cp.down_dir.joinpath(f"{grid_name}.geojson") + grid.to_file(path, driver="GeoJSON") + + output.add_live_msg(cm.grid_complete, "success") + return geemap.geojson_to_ee(grid.__geo_interface__) + def preview_square(geometry, grid_size): - + # get the center of the aoi - center = geometry.centroid().getInfo()['coordinates'] - - # create the square - square = gpd.GeoDataFrame({'geometry': [Point(center[0], center[1])]}, crs='EPSG:4326') \ - .to_crs('EPSG:3857') \ - .buffer(grid_size*1000) \ - .envelope \ - .to_crs('EPSG:4326') - + center = geometry.centroid().getInfo()["coordinates"] + + # create the square + square = ( + gpd.GeoDataFrame({"geometry": [Point(center[0], center[1])]}, crs="EPSG:4326") + .to_crs("EPSG:3857") + .buffer(grid_size * 1000) + .envelope.to_crs("EPSG:4326") + ) + return geemap.geojson_to_ee(square.__geo_interface__) - - - - - - \ No newline at end of file diff --git a/component/scripts/utils.py b/component/scripts/utils.py index 9d28f2c..78a5cff 100644 --- a/component/scripts/utils.py +++ b/component/scripts/utils.py @@ -6,11 +6,12 @@ ee.Initialize() + def display_asset(output, asset): """remove the manifest from the asset name and display it to the user""" - - asset = asset.replace('projects/earthengine-legacy/assets/', '') - - output.add_msg(cm.asset_created.format(asset), 'success') - - return asset \ No newline at end of file + + asset = asset.replace("projects/earthengine-legacy/assets/", "") + + output.add_msg(cm.asset_created.format(asset), "success") + + return asset diff --git a/component/tile/__init__.py b/component/tile/__init__.py index ad56cd8..5912850 100644 --- a/component/tile/__init__.py +++ b/component/tile/__init__.py @@ -1,2 +1,2 @@ from .download_tile import * -from .tile_tile import * \ No newline at end of file +from .tile_tile import * diff --git a/component/tile/download_tile.py b/component/tile/download_tile.py index 6b57739..d4cc816 100644 --- a/component/tile/download_tile.py +++ b/component/tile/download_tile.py @@ -2,124 +2,127 @@ import unidecode import ipyvuetify as v -from ipyvuetify.extra import FileInput -from sepal_ui import sepalwidgets as sw +from ipyvuetify.extra import FileInput +from sepal_ui import sepalwidgets as sw from sepal_ui.scripts import utils as su from component import parameter as cp from component.message import cm + class DownloadTile(sw.Tile): - - SELECT_TYPE = [ - 'Shape file (.shp, .shx, .dbf, .prj)', - 'Table file (.csv)' - ] - + + SELECT_TYPE = ["Shape file (.shp, .shx, .dbf, .prj)", "Table file (.csv)"] + # the first 3 one are compulsory - SHP_SUFFIX = ['.shp', '.dbf', '.shx', '.cpg', '.prj', '.sbn', '.sbx'] - + SHP_SUFFIX = [".shp", ".dbf", ".shx", ".cpg", ".prj", ".sbn", ".sbx"] + def __init__(self): - + self.select_type = v.Select( - label= cm.download.select_type, - items= self.SELECT_TYPE, - v_model = None, + label=cm.download.select_type, + items=self.SELECT_TYPE, + v_model=None, ) - + self.input_file = FileInput( - disabled = True, + disabled=True, ) - + super().__init__( - 'aoi_tile', + "aoi_tile", cm.download.title, - inputs = [self.select_type, self.input_file], - alert = sw.Alert(), - btn = sw.Btn(cm.download.import_btn, icon = 'mdi-check', class_='mt-4') + inputs=[self.select_type, self.input_file], + alert=sw.Alert(), + btn=sw.Btn(cm.download.import_btn, icon="mdi-check", class_="mt-4"), ) - - self.select_type.observe(self.on_type_change, 'v_model') - self.btn.on_event('click', self.load_file) - - + + self.select_type.observe(self.on_type_change, "v_model") + self.btn.on_event("click", self.load_file) + def on_type_change(self, change): - + self.input_file.clear() self.input_file.disabled = False - + # shape files - if change['new'] == self.SELECT_TYPE[0]: + if change["new"] == self.SELECT_TYPE[0]: self.input_file.multiple = True - # table file - elif change['new'] == self.SELECT_TYPE[1]: + # table file + elif change["new"] == self.SELECT_TYPE[1]: self.input_file.multiple = False else: self.input_file.disabled = True - + return - + @su.loading_button(debug=False) def load_file(self, widget, data, event): - + self.alert.add_msg(cm.download.start) - + # load the files myfiles = self.input_file.get_files() - - # test that the file is not empty - if not self.alert.check_input(myfiles, cm.download.no_file): return - + + # test that the file is not empty + if not self.alert.check_input(myfiles, cm.download.no_file): + return + #################################### ## test the file sended ## #################################### - + # table type if self.select_type.v_model == self.SELECT_TYPE[1]: - - if Path(myfiles[0]['name']).suffix != '.csv': - self.alert.add_msg(cm.download.not_csv, 'error') + + if Path(myfiles[0]["name"]).suffix != ".csv": + self.alert.add_msg(cm.download.not_csv, "error") return - + # shp type if self.select_type.v_model == self.SELECT_TYPE[0]: - - name = set([Path(f['name']).stem for f in myfiles]) + + name = set([Path(f["name"]).stem for f in myfiles]) if len(name) > 1: - self.alert.add_msg(cm.download.naming_bug, 'error') - return - - suffixes = [Path(f['name']).suffix for f in myfiles] - if not all(ext in suffixes for ext in self.SHP_SUFFIX[:3]): - self.alert.add_msg(cm.download.missing_files.format(", ".join(self.SHP_SUFFIX[:3])), 'error') - return - - if not all(ext in self.SHP_SUFFIX for ext in suffixes): - self.alert.add_msg(cm.download.unknown_extention.format(", ".join(self.SHP_SUFFIX)), 'error') - return - + self.alert.add_msg(cm.download.naming_bug, "error") + return + + suffixes = [Path(f["name"]).suffix for f in myfiles] + if not all(ext in suffixes for ext in self.SHP_SUFFIX[:3]): + self.alert.add_msg( + cm.download.missing_files.format(", ".join(self.SHP_SUFFIX[:3])), + "error", + ) + return + + if not all(ext in self.SHP_SUFFIX for ext in suffixes): + self.alert.add_msg( + cm.download.unknown_extention.format(", ".join(self.SHP_SUFFIX)), + "error", + ) + return + ###################################### ## download all the files ## ###################################### - + for file in myfiles: - + # create a path - path = cp.down_dir.joinpath(unidecode.unidecode(file['name'])) - + path = cp.down_dir.joinpath(unidecode.unidecode(file["name"])) + if path.is_file(): - self.alert.add_msg(cm.download.already_exist.format(path), 'warning') + self.alert.add_msg(cm.download.already_exist.format(path), "warning") break - - src = file['file_obj'] - with path.open('wb') as dst: + + src = file["file_obj"] + with path.open("wb") as dst: content = bytes(src.read()) dst.write(content) - - + if self.select_type.v_model == self.SELECT_TYPE[0]: - path = path.stem + '.shp' - - self.alert.add_msg(cm.download.complete.format(path), 'success') - - return \ No newline at end of file + path = path.stem + ".shp" + + self.alert.add_msg(cm.download.complete.format(path), "success") + + return diff --git a/component/tile/tile_tile.py b/component/tile/tile_tile.py index 849ce2e..ff2f059 100644 --- a/component/tile/tile_tile.py +++ b/component/tile/tile_tile.py @@ -14,109 +14,106 @@ ee.Initialize() + class TileTile(sw.Tile): - + updated = Int(0).tag(sync=True) - + def __init__(self, m, aoi_model): - - # to store the final asset - self.assetId = None - + + # to store the final asset + self.assetId = None + # the map to display the final tile self.m = m - - #store the aoi_model + + # store the aoi_model self.aoi_model = aoi_model - - # inputs - self.grid_name = v.TextField( - label = cm.tile.asset_lbl, - v_model = None - ) - + + # inputs + self.grid_name = v.TextField(label=cm.tile.asset_lbl, v_model=None) + self.batch_size = sw.NumberField( - label = cm.tile.nb_batch_lbl, - v_model=1, - max_=100 + label=cm.tile.nb_batch_lbl, v_model=1, max_=100 ) - + # the aoi default btn is not set to btn anymore (to avoid conflict with the standard btn) # to mimic its behaviour in the dialog interface we wire 2 attribute btn and aoi_btn to the same Btn object self.model = SimpleNamespace(asset_name=None) - + super().__init__( - 'tile_widget', + "tile_widget", cm.tile.title, - inputs = [self.batch_size, self.grid_name], - alert = sw.Alert(), - btn = sw.Btn(cm.tile.btn, icon = 'mdi-check') + inputs=[self.batch_size, self.grid_name], + alert=sw.Alert(), + btn=sw.Btn(cm.tile.btn, icon="mdi-check"), ) - - # link the component together - self.btn.on_event('click', self.create_grid) - self.batch_size.observe(self.write_name, 'v_model') - + + # link the component together + self.btn.on_event("click", self.create_grid) + self.batch_size.observe(self.write_name, "v_model") + @su.loading_button(debug=False) def create_grid(self, widget, data, event): - - # read the data - aoi = self.aoi_model + + # read the data + aoi = self.aoi_model grid_name = self.grid_name.v_model grid_batch = int(self.batch_size.v_model) - - #check the vars - if not self.alert.check_input(aoi.name, cm.no_aoi): return - if not self.alert.check_input(grid_batch, cm.no_size): return - if not self.alert.check_input(grid_name, cm.no_name): return - + + # check the vars + if not self.alert.check_input(aoi.name, cm.no_aoi): + return + if not self.alert.check_input(grid_batch, cm.no_size): + return + if not self.alert.check_input(grid_name, cm.no_name): + return + grid = cs.set_grid(aoi.gdf, grid_batch, grid_name, self.alert) - - # get exportation parameters - folder = ee.data.getAssetRoots()[0]['id'] + + # get exportation parameters + folder = ee.data.getAssetRoots()[0]["id"] asset = os.path.join(folder, grid_name) - + # export if not gee.is_asset(asset, folder): - + task_config = { - 'collection': grid, - 'description':grid_name, - 'assetId': asset + "collection": grid, + "description": grid_name, + "assetId": asset, } - + task = ee.batch.Export.table.toAsset(**task_config) task.start() gee.wait_for_completion(grid_name, self.alert) - + self.assetId = asset - + # remove the preview square from the map for layer in self.m.layers: - if layer.name == 'preview square size': + if layer.name == "preview square size": self.m.remove_layer(layer) - - # display the asset on the map + + # display the asset on the map self.m.addLayer( - ee.FeatureCollection(asset), - {'color': color.accent}, - cm.tile.grid_layer + ee.FeatureCollection(asset), {"color": color.accent}, cm.tile.grid_layer ) - + self.model.asset_name = cs.display_asset(self.alert, asset) - + self.updated += 1 - + return - + def write_name(self, change): - - # read the inputs + + # read the inputs aoi_name = self.aoi_model.name grid_batch = int(self.batch_size.v_model) if self.batch_size.v_model else 0 - - name = f'{aoi_name}_Grid_{grid_batch}' if aoi_name else None - + + name = f"{aoi_name}_Grid_{grid_batch}" if aoi_name else None + self.grid_name.v_model = name - - return \ No newline at end of file + + return diff --git a/component/widget/__init__.py b/component/widget/__init__.py index 7842baf..919622d 100644 --- a/component/widget/__init__.py +++ b/component/widget/__init__.py @@ -1 +1 @@ -from .link_dialog import * \ No newline at end of file +from .link_dialog import * diff --git a/component/widget/link_dialog.py b/component/widget/link_dialog.py index 9f9d2cf..24631f4 100644 --- a/component/widget/link_dialog.py +++ b/component/widget/link_dialog.py @@ -1,50 +1,48 @@ -import ipyvuetify as v +import ipyvuetify as v import pyperclip from sepal_ui import sepalwidgets as sw from component.message import cm + class LinkDialog(sw.SepalWidget, v.Dialog): - def __init__(self, tile): - + self.tile = tile - + self.title = v.CardTitle(children=[cm.dialog.title]) - self.text = v.CardText(children = [cm.dialog.msg]) - + self.text = v.CardText(children=[cm.dialog.msg]) + self.link = v.TextField( - class_ = "ma-5", - v_model = 'je suis un link', - outlined = True, - label = cm.dialog.link_lbl, - readonly = True, - append_icon = 'mdi-clipboard-outline' - ) - + class_="ma-5", + v_model="je suis un link", + outlined=True, + label=cm.dialog.link_lbl, + readonly=True, + append_icon="mdi-clipboard-outline", + ) + self.card = v.Card( - children = [ + children=[ self.title, self.text, self.link, ] ) - - super().__init__( - value = False, - max_width = '600px', - children = [self.card] - ) - - self.tile.observe(self.fire_dialog, 'updated') - + + super().__init__(value=False, max_width="600px", children=[self.card]) + + self.tile.observe(self.fire_dialog, "updated") + def fire_dialog(self, link): - + # the toggle btn has changed let's see if it's for a good reason - if self.tile.alert.type == 'success': - + if self.tile.alert.type == "success": + self.value = True - self.link.v_model = self.tile.model.asset_name.replace('projects/earthengine-legacy/assets/', '') - - return \ No newline at end of file + self.link.v_model = self.tile.model.asset_name.replace( + "projects/earthengine-legacy/assets/", "" + ) + + return diff --git a/download_ui.ipynb b/download_ui.ipynb index 3f6ee27..7051478 100644 --- a/download_ui.ipynb +++ b/download_ui.ipynb @@ -29,11 +29,11 @@ "metadata": {}, "outputs": [], "source": [ - "#create the tile \n", - "ig_export = aoi.AoiTile(methods = ['DRAW', 'SHAPE', 'POINTS'])\n", + "# create the tile\n", + "ig_export = aoi.AoiTile(methods=[\"DRAW\", \"SHAPE\", \"POINTS\"])\n", "ig_export.set_title(cm.to_gee.title)\n", "\n", - "#this tile will only be displayed if voila is launch from this notebook \n", + "# this tile will only be displayed if voila is launch from this notebook\n", "ig_export" ] }, diff --git a/no_ui.ipynb b/no_ui.ipynb index 3f717e5..dfcb650 100644 --- a/no_ui.ipynb +++ b/no_ui.ipynb @@ -6,7 +6,7 @@ "metadata": {}, "outputs": [], "source": [ - "#add all the tiles\n", + "# add all the tiles\n", "%run 'download_ui.ipynb'\n", "%run 'about_ui.ipynb'\n", "%run 'tiling_ui.ipynb'" diff --git a/tiling_ui.ipynb b/tiling_ui.ipynb index d0c0a98..3480351 100644 --- a/tiling_ui.ipynb +++ b/tiling_ui.ipynb @@ -19,11 +19,11 @@ }, "outputs": [], "source": [ - "#create the tile \n", - "ig_aoi = aoi.AoiTile(methods = ['ADMIN0', 'ADMIN1', 'ADMIN2', 'ASSET'])\n", - "ig_aoi._metadata['mount_id'] = 'tile_widget'\n", + "# create the tile\n", + "ig_aoi = aoi.AoiTile(methods=[\"ADMIN0\", \"ADMIN1\", \"ADMIN2\", \"ASSET\"])\n", + "ig_aoi._metadata[\"mount_id\"] = \"tile_widget\"\n", "\n", - "#this tile will only be displayed if voila is launch from this notebook \n", + "# this tile will only be displayed if voila is launch from this notebook\n", "ig_aoi" ] }, diff --git a/ui.ipynb b/ui.ipynb index f26eca4..8d03954 100644 --- a/ui.ipynb +++ b/ui.ipynb @@ -16,7 +16,7 @@ "metadata": {}, "outputs": [], "source": [ - "#create an appBar \n", + "# create an appBar\n", "ig_appBar = sw.AppBar(cm.app.title)" ] }, @@ -26,19 +26,12 @@ "metadata": {}, "outputs": [], "source": [ - "#add tiles \n", + "# add tiles\n", "%run 'download_ui.ipynb'\n", "%run 'about_ui.ipynb'\n", "%run 'tiling_ui.ipynb'\n", "\n", - "ig_content = [\n", - " ig_import,\n", - " ig_export,\n", - " ig_aoi,\n", - " ig_tiling,\n", - " ig_about,\n", - " ig_disclaimer\n", - "]" + "ig_content = [ig_import, ig_export, ig_aoi, ig_tiling, ig_about, ig_disclaimer]" ] }, { @@ -47,18 +40,18 @@ "metadata": {}, "outputs": [], "source": [ - "#create a drawer \n", + "# create a drawer\n", "items = [\n", - " sw.DrawerItem(cm.app.drawer_item.file, 'mdi-file-swap', card=\"aoi_tile\"),\n", - " sw.DrawerItem(cm.app.drawer_item.bfast_tile, 'mdi-border-all', card=\"tile_widget\"),\n", - " sw.DrawerItem(cm.app.drawer_item.about, 'mdi-help-circle', card=\"about_tile\")\n", + " sw.DrawerItem(cm.app.drawer_item.file, \"mdi-file-swap\", card=\"aoi_tile\"),\n", + " sw.DrawerItem(cm.app.drawer_item.bfast_tile, \"mdi-border-all\", card=\"tile_widget\"),\n", + " sw.DrawerItem(cm.app.drawer_item.about, \"mdi-help-circle\", card=\"about_tile\"),\n", "]\n", "\n", "code_link = \"https://github.com/openforis/import_to_gee\"\n", "wiki_link = \"https://github.com/openforis/import_to_gee/blob/master/README.md\"\n", "issue_link = \"https://github.com/openforis/import_to_gee/issues/new\"\n", "\n", - "ig_drawer = sw.NavDrawer(items, code = code_link, wiki = wiki_link, issue = issue_link)" + "ig_drawer = sw.NavDrawer(items, code=code_link, wiki=wiki_link, issue=issue_link)" ] }, { @@ -67,12 +60,10 @@ "metadata": {}, "outputs": [], "source": [ - "#build the app \n", - "ig_app = sw.App(\n", - " tiles=ig_content, \n", - " appBar=ig_appBar, \n", - " navDrawer=ig_drawer\n", - ").show_tile('aoi_tile')" + "# build the app\n", + "ig_app = sw.App(tiles=ig_content, appBar=ig_appBar, navDrawer=ig_drawer).show_tile(\n", + " \"aoi_tile\"\n", + ")" ] }, { @@ -100,7 +91,7 @@ "metadata": {}, "outputs": [], "source": [ - "#display the app\n", + "# display the app\n", "ig_app" ] } From 5a71eb1b89d1c0c45ab4095fc33f800814a5a96f Mon Sep 17 00:00:00 2001 From: 12rambau Date: Tue, 23 Nov 2021 11:25:58 +0000 Subject: [PATCH 6/7] add badges in readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 22b5112..8d58221 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ # Vector file manager +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) +[![Black badge](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) + ## About + This module is a 2 step wrapper to upload AOI to Google Earth Engine ![results](./doc/img/full_input.png) From 07b800c0da85957f1355356f239597ab0d742f82 Mon Sep 17 00:00:00 2001 From: 12rambau Date: Tue, 23 Nov 2021 11:51:50 +0000 Subject: [PATCH 7/7] improve tiling description --- component/message/en.json | 2 +- doc/en.rst | 4 ++-- ui.ipynb | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/component/message/en.json b/component/message/en.json index fd3da76..88862d2 100644 --- a/component/message/en.json +++ b/component/message/en.json @@ -38,7 +38,7 @@ }, "tile": { "asset_lbl": "Asset name", - "nb_batch_lbl": "Select number of tiles in a batch", + "nb_batch_lbl": "Select batch size", "btn": "Tile the AOI", "title": "Tiling interface", "grid_layer": "grid" diff --git a/doc/en.rst b/doc/en.rst index ff56ff9..a63579d 100644 --- a/doc/en.rst +++ b/doc/en.rst @@ -31,8 +31,8 @@ The second drawer will allow you to create a grid on top of any AOI. THe grid is .. note:: - the planet grid is constructing a 2048x2048 grid of SQUARES. The latitude extends is bigger (20048966.10m VS 20026376.39) so to ensure the "squariness" Planet lab have based the numerotation and extends of it square grid on the longitude only. the extreme -90 and +90 band it thus exlucded but there are no so relevant cells for forestry observation + the planet grid is constructing a 2048x2048 grid of SQUARES. The latitude extends is bigger (20048966.10m VS 20026376.39) so to ensure the "squariness" Planet lab have based the numerotation and extends of it square grid on the longitude only. the extreme -90 and +90 bands are thus exlucded but there are no so relevant cells for forestry observation -an extra column is added to the grid called "batch" you can select the number of cell you want to add to each batch. The naming will be set automatically according to your AOI name and the batch number. +An extra column is added to the grid called "batch" you can select the size of the batch by changing the width of the batch usin the initial grid cell as unit. E.g. by setting :guilabel:`grid size` to 10 you'll create grid batch of 10x10 cells. The naming will be set automatically according to your AOI name and the batch size. By validating, you will create a geojson file that will live in :code:`module_results/aoi/.geojson` and launch the creation of the same grid in your GEE assets. diff --git a/ui.ipynb b/ui.ipynb index 8d03954..a54fc1d 100644 --- a/ui.ipynb +++ b/ui.ipynb @@ -112,7 +112,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.9" + "version": "3.8.10" } }, "nbformat": 4,