aletrn commited on
Commit
c05e7c7
·
1 Parent(s): 2f08493

[refactor] get constants from env variables, handle possible exceptions on tiles download/merge/crop

Browse files
src/__init__.py CHANGED
@@ -1,5 +1,7 @@
1
  """Get machine learning predictions from geodata raster images"""
2
  from aws_lambda_powertools import Logger
 
 
3
  from pathlib import Path
4
 
5
  from src.utilities.constants import SERVICE_NAME
 
1
  """Get machine learning predictions from geodata raster images"""
2
  from aws_lambda_powertools import Logger
3
+ # not used here but contextily_tile is imported in src.io.tms2geotiff
4
+ from contextily import tile as contextily_tile
5
  from pathlib import Path
6
 
7
  from src.utilities.constants import SERVICE_NAME
src/io/tms2geotiff.py CHANGED
@@ -1,14 +1,22 @@
 
1
  from numpy import ndarray
2
 
3
  from src import app_logger
4
- from src.utilities.constants import OUTPUT_CRS_STRING, DRIVER_RASTERIO_GTIFF, OSM_MAX_RETRIES, OSM_N_CONNECTIONS, \
5
- OSM_WAIT, OSM_ZOOM_AUTO, OSM_USE_CACHE
6
  from src.utilities.type_hints import tuple_ndarray_transform, tuple_float
7
 
8
 
9
- def download_extent(w: float, s: float, e: float, n: float, zoom: int or str = OSM_ZOOM_AUTO, source: str = None,
10
- wait: int = OSM_WAIT, max_retries: int = OSM_MAX_RETRIES, n_connections: int = OSM_N_CONNECTIONS,
11
- use_cache: bool = OSM_USE_CACHE) -> tuple_ndarray_transform:
 
 
 
 
 
 
 
12
  """
13
  Download, merge and crop a list of tiles into a single geo-referenced image or a raster geodata
14
 
@@ -42,17 +50,24 @@ def download_extent(w: float, s: float, e: float, n: float, zoom: int or str = O
42
  Returns:
43
  parsed request input
44
  """
45
- from contextily.tile import bounds2img
46
- from src.io.coordinates_pixel_conversion import _from4326_to3857
47
-
48
- app_logger.debug(f"download raster from source:{source} with bounding box w:{w}, s:{s}, e:{e}, n:{n}.")
49
- downloaded_raster, bbox_raster = bounds2img(
50
- w, s, e, n, zoom=zoom, source=source, ll=True, wait=wait, max_retries=max_retries, n_connections=n_connections,
51
- use_cache=use_cache)
52
- xp0, yp0 = _from4326_to3857(n, e)
53
- xp1, yp1 = _from4326_to3857(s, w)
54
- cropped_image_ndarray, cropped_transform = crop_raster(yp1, xp1, yp0, xp0, downloaded_raster, bbox_raster)
55
- return cropped_image_ndarray, cropped_transform
 
 
 
 
 
 
 
56
 
57
 
58
  def crop_raster(w: float, s: float, e: float, n: float, raster: ndarray, raster_bbox: tuple_float,
@@ -77,38 +92,42 @@ def crop_raster(w: float, s: float, e: float, n: float, raster: ndarray, raster_
77
  Returns:
78
  cropped raster with its Affine transform
79
  """
80
- from rasterio.io import MemoryFile
81
- from rasterio.plot import reshape_as_image
82
- from rasterio.mask import mask as rio_mask
83
- from shapely.geometry import Polygon
84
- from geopandas import GeoSeries
85
-
86
- app_logger.debug(f"raster: type {type(raster)}, raster_ext:{type(raster_bbox)}, {raster_bbox}.")
87
- img_to_save, transform = get_transform_raster(raster, raster_bbox)
88
- img_height, img_width, number_bands = img_to_save.shape
89
- # https://rasterio.readthedocs.io/en/latest/topics/memory-files.html
90
- with MemoryFile() as rio_mem_file:
91
- app_logger.debug("writing raster in-memory to crop it with rasterio.mask.mask()")
92
- with rio_mem_file.open(
93
- driver=driver,
94
- height=img_height,
95
- width=img_width,
96
- count=number_bands,
97
- dtype=str(img_to_save.dtype.name),
98
- crs=crs,
99
- transform=transform,
100
- ) as src_raster_rw:
101
- for band in range(number_bands):
102
- src_raster_rw.write(img_to_save[:, :, band], band + 1)
103
- app_logger.debug("cropping raster in-memory with rasterio.mask.mask()")
104
- with rio_mem_file.open() as src_raster_ro:
105
- shapes_crop_polygon = Polygon([(n, e), (s, e), (s, w), (n, w), (n, e)])
106
- shapes_crop = GeoSeries([shapes_crop_polygon])
107
- app_logger.debug(f"cropping with polygon::{shapes_crop_polygon}.")
108
- cropped_image, cropped_transform = rio_mask(src_raster_ro, shapes=shapes_crop, crop=True)
109
- cropped_image_ndarray = reshape_as_image(cropped_image)
110
- app_logger.info(f"cropped image::{cropped_image_ndarray.shape}.")
111
- return cropped_image_ndarray, cropped_transform
 
 
 
 
112
 
113
 
114
  def get_transform_raster(raster: ndarray, raster_bbox: tuple_float) -> tuple_ndarray_transform:
@@ -122,20 +141,37 @@ def get_transform_raster(raster: ndarray, raster_bbox: tuple_float) -> tuple_nda
122
  Returns:
123
  rgb raster image and its Affine transform
124
  """
125
- from rasterio.transform import from_origin
126
- from numpy import array as np_array, linspace as np_linspace, uint8 as np_uint8
127
- from PIL.Image import fromarray
128
-
129
- app_logger.debug(f"raster: type {type(raster)}, raster_ext:{type(raster_bbox)}, {raster_bbox}.")
130
- rgb = fromarray(np_uint8(raster)).convert('RGB')
131
- np_rgb = np_array(rgb)
132
- img_height, img_width, _ = np_rgb.shape
133
-
134
- min_x, max_x, min_y, max_y = raster_bbox
135
- app_logger.debug(f"raster rgb shape:{np_rgb.shape}, raster rgb bbox {raster_bbox}.")
136
- x = np_linspace(min_x, max_x, img_width)
137
- y = np_linspace(min_y, max_y, img_height)
138
- res_x = (x[-1] - x[0]) / img_width
139
- res_y = (y[-1] - y[0]) / img_height
140
- transform = from_origin(x[0] - res_x / 2, y[-1] + res_y / 2, res_x, res_y)
141
- return np_rgb, transform
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
  from numpy import ndarray
3
 
4
  from src import app_logger
5
+ from src.utilities.constants import (OUTPUT_CRS_STRING, DRIVER_RASTERIO_GTIFF, N_MAX_RETRIES, N_CONNECTION, N_WAIT,
6
+ ZOOM_AUTO, BOOL_USE_CACHE)
7
  from src.utilities.type_hints import tuple_ndarray_transform, tuple_float
8
 
9
 
10
+ bool_use_cache = int(os.getenv("BOOL_USE_CACHE", BOOL_USE_CACHE))
11
+ n_connection = int(os.getenv("N_CONNECTION", N_CONNECTION))
12
+ n_max_retries = int(os.getenv("N_MAX_RETRIES", N_MAX_RETRIES))
13
+ n_wait = int(os.getenv("N_WAIT", N_WAIT))
14
+ zoom_auto_string = os.getenv("ZOOM_AUTO", ZOOM_AUTO)
15
+
16
+
17
+ def download_extent(w: float, s: float, e: float, n: float, zoom: int or str = zoom_auto_string, source: str = None,
18
+ wait: int = n_wait, max_retries: int = n_max_retries, n_connections: int = n_connection,
19
+ use_cache: bool = bool_use_cache) -> tuple_ndarray_transform:
20
  """
21
  Download, merge and crop a list of tiles into a single geo-referenced image or a raster geodata
22
 
 
50
  Returns:
51
  parsed request input
52
  """
53
+ try:
54
+ from src import contextily_tile
55
+ from src.io.coordinates_pixel_conversion import _from4326_to3857
56
+
57
+ app_logger.info(f"connection number:{n_connections}, type:{type(n_connections)}.")
58
+ app_logger.info(f"zoom:{zoom}, type:{type(zoom)}.")
59
+ app_logger.debug(f"download raster from source:{source} with bounding box w:{w}, s:{s}, e:{e}, n:{n}.")
60
+ app_logger.debug(f"types w:{type(w)}, s:{type(s)}, e:{type(e)}, n:{type(n)}.")
61
+ downloaded_raster, bbox_raster = contextily_tile.bounds2img(
62
+ w, s, e, n, zoom=zoom, source=source, ll=True, wait=wait, max_retries=max_retries, n_connections=n_connections,
63
+ use_cache=use_cache)
64
+ xp0, yp0 = _from4326_to3857(n, e)
65
+ xp1, yp1 = _from4326_to3857(s, w)
66
+ cropped_image_ndarray, cropped_transform = crop_raster(yp1, xp1, yp0, xp0, downloaded_raster, bbox_raster)
67
+ return cropped_image_ndarray, cropped_transform
68
+ except Exception as e_download_extent:
69
+ app_logger.exception(f"e_download_extent:{e_download_extent}.", exc_info=True)
70
+ raise e_download_extent
71
 
72
 
73
  def crop_raster(w: float, s: float, e: float, n: float, raster: ndarray, raster_bbox: tuple_float,
 
92
  Returns:
93
  cropped raster with its Affine transform
94
  """
95
+ try:
96
+ from rasterio.io import MemoryFile
97
+ from rasterio.mask import mask as rio_mask
98
+ from shapely.geometry import Polygon
99
+ from geopandas import GeoSeries
100
+
101
+ app_logger.debug(f"raster: type {type(raster)}, raster_ext:{type(raster_bbox)}, {raster_bbox}.")
102
+ img_to_save, transform = get_transform_raster(raster, raster_bbox)
103
+ img_height, img_width, number_bands = img_to_save.shape
104
+ # https://rasterio.readthedocs.io/en/latest/topics/memory-files.html
105
+ with MemoryFile() as rio_mem_file:
106
+ app_logger.debug("writing raster in-memory to crop it with rasterio.mask.mask()")
107
+ with rio_mem_file.open(
108
+ driver=driver,
109
+ height=img_height,
110
+ width=img_width,
111
+ count=number_bands,
112
+ dtype=str(img_to_save.dtype.name),
113
+ crs=crs,
114
+ transform=transform,
115
+ ) as src_raster_rw:
116
+ for band in range(number_bands):
117
+ src_raster_rw.write(img_to_save[:, :, band], band + 1)
118
+ app_logger.debug("cropping raster in-memory with rasterio.mask.mask()")
119
+ with rio_mem_file.open() as src_raster_ro:
120
+ shapes_crop_polygon = Polygon([(n, e), (s, e), (s, w), (n, w), (n, e)])
121
+ shapes_crop = GeoSeries([shapes_crop_polygon])
122
+ app_logger.debug(f"cropping with polygon::{shapes_crop_polygon}.")
123
+ cropped_image, cropped_transform = rio_mask(src_raster_ro, shapes=shapes_crop, crop=True)
124
+ cropped_image_ndarray = reshape_as_image(cropped_image)
125
+ app_logger.info(f"cropped image::{cropped_image_ndarray.shape}.")
126
+ return cropped_image_ndarray, cropped_transform
127
+ except Exception as e_crop_raster:
128
+ app_logger.exception(f"arguments raster: {type(raster)}, {raster}.")
129
+ app_logger.exception(f"e_crop_raster:{e_crop_raster}.", exc_info=True)
130
+ raise e_crop_raster
131
 
132
 
133
  def get_transform_raster(raster: ndarray, raster_bbox: tuple_float) -> tuple_ndarray_transform:
 
141
  Returns:
142
  rgb raster image and its Affine transform
143
  """
144
+ try:
145
+ from rasterio.transform import from_origin
146
+ from numpy import array as np_array, linspace as np_linspace, uint8 as np_uint8
147
+ from PIL.Image import fromarray
148
+
149
+ app_logger.debug(f"raster: type {type(raster)}, raster_ext:{type(raster_bbox)}, {raster_bbox}.")
150
+ rgb = fromarray(np_uint8(raster)).convert('RGB')
151
+ np_rgb = np_array(rgb)
152
+ img_height, img_width, _ = np_rgb.shape
153
+
154
+ min_x, max_x, min_y, max_y = raster_bbox
155
+ app_logger.debug(f"raster rgb shape:{np_rgb.shape}, raster rgb bbox {raster_bbox}.")
156
+ x = np_linspace(min_x, max_x, img_width)
157
+ y = np_linspace(min_y, max_y, img_height)
158
+ res_x = (x[-1] - x[0]) / img_width
159
+ res_y = (y[-1] - y[0]) / img_height
160
+ transform = from_origin(x[0] - res_x / 2, y[-1] + res_y / 2, res_x, res_y)
161
+ return np_rgb, transform
162
+ except Exception as e_get_transform_raster:
163
+ app_logger.exception(f"arguments raster: {type(raster)}, {raster}.")
164
+ app_logger.exception(f"arguments raster_bbox: {type(raster_bbox)}, {raster_bbox}.")
165
+ app_logger.exception(f"e_get_transform_raster:{e_get_transform_raster}.")
166
+ raise e_get_transform_raster
167
+
168
+
169
+ def reshape_as_image(arr):
170
+ try:
171
+ from numpy import swapaxes
172
+
173
+ return swapaxes(swapaxes(arr, 0, 2), 0, 1)
174
+ except Exception as e_reshape_as_image:
175
+ app_logger.exception(f"arguments: {type(arr)}, {arr}.")
176
+ app_logger.exception(f"e_reshape_as_image:{e_reshape_as_image}.", exc_info=True)
177
+ raise e_reshape_as_image
src/utilities/constants.py CHANGED
@@ -26,8 +26,8 @@ DEFAULT_LOG_LEVEL = 'INFO'
26
  RETRY_DOWNLOAD = 3
27
  TIMEOUT_DOWNLOAD = 60
28
  CALLBACK_INTERVAL_DOWNLOAD = 0.05
29
- OSM_USE_CACHE = True
30
- OSM_WAIT = 0
31
- OSM_MAX_RETRIES = 2
32
- OSM_N_CONNECTIONS = 1
33
- OSM_ZOOM_AUTO = "auto"
 
26
  RETRY_DOWNLOAD = 3
27
  TIMEOUT_DOWNLOAD = 60
28
  CALLBACK_INTERVAL_DOWNLOAD = 0.05
29
+ BOOL_USE_CACHE = True
30
+ N_WAIT = 0
31
+ N_MAX_RETRIES = 2
32
+ N_CONNECTION = 2
33
+ ZOOM_AUTO = "auto"