From b90964b80c04eda1804f844a06c467292d489b9f Mon Sep 17 00:00:00 2001 From: Rambaud Pierrick <12rambau@users.noreply.github.com> Date: Thu, 11 Mar 2021 14:26:51 +0100 Subject: [PATCH 1/4] Create en.rst --- doc/en.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 doc/en.rst diff --git a/doc/en.rst b/doc/en.rst new file mode 100644 index 0000000..3d3d79a --- /dev/null +++ b/doc/en.rst @@ -0,0 +1,10 @@ +Clip time series +================ + +.. warning:: + + The english documentation of the module have not been set. + +.. tip:: + + Please open an issue on their repository : https://github.com/openforis/clip-time-series/issues/new From be44e4c89dd5495cf3e6c88a4f40416e137df5c0 Mon Sep 17 00:00:00 2001 From: Pierrick Rambaud Date: Mon, 29 Mar 2021 16:56:22 +0000 Subject: [PATCH 2/4] new grid computation process --- component/scripts/tiling.py | 100 ++++++++++++------ component/tile/tile_tile.py | 64 ++++++------ no_ui.ipynb | 200 ++++++++++++++++++++++++++++++++---- tiling_ui.ipynb | 90 ++++++++++++++-- 4 files changed, 361 insertions(+), 93 deletions(-) diff --git a/component/scripts/tiling.py b/component/scripts/tiling.py index a58d6ec..a204459 100644 --- a/component/scripts/tiling.py +++ b/component/scripts/tiling.py @@ -1,70 +1,104 @@ import json +from itertools import product import numpy as np +import decimal as d + import geemap -from shapely.geometry import shape, Point +from shapely import geometry as sg from shapely.ops import unary_union -from itertools import product import geopandas as gpd +from pyproj import CRS, Transformer +from pathlib import Path from component.message import cm +from component import parameter as cp + +d.getcontext().prec = 15 -def set_grid(aoi, grid_batch, output): - """compute a grid around a given aoi (ee.FeatureCollection)""" +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_json = geemap.ee_to_geojson(aoi) - aoi_shp = unary_union([shape(feat['geometry']) for feat in aoi_json['features']]) - aoi_gdf = gpd.GeoDataFrame({'geometry': [aoi_shp]}, crs="EPSG:4326") + aoi_shp = unary_union([sg.shape(feat['geometry']) for feat in aoi_json['features']]) + aoi_gdf = gpd.GeoDataFrame({'geometry': [aoi_shp]}, crs="EPSG:4326").to_crs('EPSG:3857') output.add_live_msg(cm.digest_aoi) # extract the aoi shape aoi_shp_proj = aoi_gdf['geometry'][0] - - # the size is based on the planet grid size - # the planet grid is composed of squared grid that split the world width in 2048 squares - diametre = 360/2048 - radius = diametre/2 + # retreive the bounding box + aoi_bb = sg.box(*aoi_gdf.total_bounds) + aoi_bb.bounds - # compute the longitudes and latitudes for the whole world - longitudes = np.arange(-180, 180, diametre) - latitudes = np.arange(-90, 90, diametre) + # compute the longitude and latitude in the apropriate CRS + # Here I suppose it's 3857 but it's not working so I'll need to change it + crs_4326 = CRS.from_epsg(4326) + crs_3857 = CRS.from_epsg(3857) + crs_min_x, crs_min_y, crs_max_x, crs_max_y = crs_3857.area_of_use.bounds + + proj = Transformer.from_crs(4326, 3857, always_xy=True) + bl = proj.transform(crs_min_x, crs_min_y) + tr = proj.transform(crs_max_x, crs_max_y) + + longitudes = np.linspace(bl[0], tr[0], 2048+1) + latitudes = np.linspace(bl[1], tr[1], 2048+1) + + # the planet grid sizez cut the world in 248 squares vertically and horizontally + box_size = (tr[0]-bl[0])/2048 + # filter with the geometry bounds min_lon, min_lat, max_lon, max_lat = aoi_gdf.total_bounds - longitudes = longitudes[(longitudes > (min_lon - radius)) & (longitudes < max_lon + radius)] - latitudes = latitudes[(latitudes > (min_lat - radius)) & (latitudes < max_lat + radius)] + + # filter lon and lat + lon_filter = longitudes[(longitudes > (min_lon - box_size)) & (longitudes < max_lon + box_size)] + lat_filter = latitudes[(latitudes > (min_lat - box_size)) & (latitudes < max_lat + box_size)] + + # get the index offset + x_offset = np.nonzero(longitudes == lon_filter[0])[0][0] + y_offset = np.nonzero(latitudes == lat_filter[0])[0][0] output.add_live_msg(cm.build_grid) - #create the grid - points = [] + # create the grid batch = [] - for i, coords in enumerate(product(longitudes, latitudes)): + 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] - # create grid geometry - points.append(Point(coords[0], coords[1])) + # fill the grid values + batch.append(i//grid_batch) + 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])) - # add a batch number - batch.append(i//grid_batch) - # create a buffer grid in lat-long - grid = gpd.GeoDataFrame({'batch': batch, 'geometry':points}, crs='EPSG:4326') \ - .buffer(diametre) \ - .envelope \ - .intersection(aoi_shp_proj) \ + 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_shp_proj) + grid = grid.loc[mask] - # filter empty geometries - grid = grid[np.invert(grid.is_empty)] + # project back to 4326 + grid = grid.to_crs('EPSG:4326') - # convert gdp to GeoJson - json_df = json.loads(grid.to_json()) + # 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(json_df) + return geemap.geojson_to_ee(json.loads(grid.to_json())) def preview_square(geometry, grid_size): diff --git a/component/tile/tile_tile.py b/component/tile/tile_tile.py index 64c838c..05fcecc 100644 --- a/component/tile/tile_tile.py +++ b/component/tile/tile_tile.py @@ -75,43 +75,43 @@ def create_grid(self, widget, data, event): if not self.output.check_input(grid_name, cm.no_name): return widget.toggle_loading() - try: - grid = cs.set_grid(aoi.get_aoi_ee(), grid_batch, self.output) + #try: + grid = cs.set_grid(aoi.get_aoi_ee(), grid_batch, grid_name, self.output) - # get exportation parameters - folder = ee.data.getAssetRoots()[0]['id'] - asset = os.path.join(folder, grid_name) - - # export - if not cs.isAsset(grid_name, folder): - task_config = { - 'collection': grid, - 'description':grid_name, - 'assetId': asset - } + # get exportation parameters + folder = ee.data.getAssetRoots()[0]['id'] + asset = os.path.join(folder, grid_name) + + # export + if not cs.isAsset(grid_name, folder): + task_config = { + 'collection': grid, + 'description':grid_name, + 'assetId': asset + } - task = ee.batch.Export.table.toAsset(**task_config) - task.start() - gee.wait_for_completion(grid_name, self.output) + task = ee.batch.Export.table.toAsset(**task_config) + task.start() + gee.wait_for_completion(grid_name, self.output) - self.assetId = asset + self.assetId = asset - # remove the preview square from the map - for layer in self.m.layers: - if layer.name == 'preview square size': - self.m.remove_layer(layer) - - # display the asset on the map - self.m.addLayer( - ee.FeatureCollection(asset), - {'color': v.theme.themes.dark.accent}, - cm.tile.grid_layer - ) - - self.io.assetId = cs.display_asset(self.output, asset) + # remove the preview square from the map + for layer in self.m.layers: + if layer.name == 'preview square size': + self.m.remove_layer(layer) + + # display the asset on the map + self.m.addLayer( + ee.FeatureCollection(asset), + {'color': v.theme.themes.dark.accent}, + cm.tile.grid_layer + ) + + self.io.assetId = cs.display_asset(self.output, asset) - except Exception as e: - self.output.add_live_msg(str(e), 'error') + #except Exception as e: + # self.output.add_live_msg(str(e), 'error') # toggle the loading widget.toggle_loading() diff --git a/no_ui.ipynb b/no_ui.ipynb index d60bf4e..19048f5 100644 --- a/no_ui.ipynb +++ b/no_ui.ipynb @@ -2,9 +2,38 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "22108dea7f3246e092181423fd9a80aa", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "HTML(value='\\n