eotdl 2023.11.2.post5__tar.gz → 2023.11.3.post2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/PKG-INFO +5 -3
  2. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/README.md +1 -1
  3. eotdl-2023.11.3.post2/eotdl/__init__.py +1 -0
  4. eotdl-2023.11.3.post2/eotdl/access/__init__.py +7 -0
  5. eotdl-2023.11.3.post2/eotdl/access/airbus/__init__.py +6 -0
  6. eotdl-2023.11.3.post2/eotdl/access/airbus/client.py +385 -0
  7. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/access/airbus/parameters.py +19 -4
  8. eotdl-2023.11.3.post2/eotdl/access/airbus/utils.py +34 -0
  9. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/access/download.py +30 -14
  10. eotdl-2023.11.3.post2/eotdl/access/search.py +26 -0
  11. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/access/sentinelhub/__init__.py +5 -1
  12. eotdl-2023.11.3.post2/eotdl/access/sentinelhub/client.py +113 -0
  13. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/access/sentinelhub/evalscripts.py +38 -39
  14. eotdl-2023.11.3.post2/eotdl/access/sentinelhub/parameters.py +90 -0
  15. eotdl-2023.11.3.post2/eotdl/access/sentinelhub/utils.py +100 -0
  16. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/auth/errors.py +2 -1
  17. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/commands/auth.py +3 -3
  18. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/curation/__init__.py +5 -1
  19. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/curation/stac/__init__.py +5 -1
  20. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/curation/stac/assets.py +55 -32
  21. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/curation/stac/dataframe.py +20 -14
  22. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/curation/stac/dataframe_bck.py +2 -2
  23. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/curation/stac/dataframe_labeling.py +15 -12
  24. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/curation/stac/extensions/__init__.py +6 -2
  25. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/curation/stac/extensions/base.py +8 -4
  26. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/curation/stac/extensions/dem.py +6 -3
  27. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/curation/stac/extensions/eo.py +10 -6
  28. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/curation/stac/extensions/label/__init__.py +5 -1
  29. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/curation/stac/extensions/label/base.py +40 -26
  30. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/curation/stac/extensions/label/image_name_labeler.py +64 -43
  31. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/curation/stac/extensions/label/scaneo.py +59 -56
  32. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/curation/stac/extensions/ml_dataset.py +154 -56
  33. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/curation/stac/extensions/projection.py +11 -9
  34. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/curation/stac/extensions/raster.py +22 -14
  35. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/curation/stac/extensions/sar.py +12 -7
  36. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/curation/stac/extent.py +67 -40
  37. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/curation/stac/parsers.py +18 -10
  38. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/curation/stac/stac.py +81 -62
  39. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/datasets/__init__.py +1 -1
  40. eotdl-2023.11.3.post2/eotdl/datasets/download.py +106 -0
  41. eotdl-2023.11.3.post2/eotdl/datasets/ingest.py +112 -0
  42. eotdl-2023.11.3.post2/eotdl/files/__init__.py +1 -0
  43. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/files/ingest.py +3 -1
  44. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/models/download.py +1 -1
  45. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/repos/AuthAPIRepo.py +0 -1
  46. eotdl-2023.11.3.post2/eotdl/repos/DatasetsAPIRepo.py +125 -0
  47. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/repos/FilesAPIRepo.py +7 -92
  48. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/repos/ModelsAPIRepo.py +0 -1
  49. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/tools/__init__.py +5 -1
  50. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/tools/geo_utils.py +78 -48
  51. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/tools/metadata.py +13 -11
  52. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/tools/paths.py +14 -14
  53. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/tools/stac.py +36 -31
  54. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/tools/time_utils.py +53 -26
  55. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/tools/tools.py +84 -50
  56. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/pyproject.toml +6 -2
  57. eotdl-2023.11.2.post5/eotdl/__init__.py +0 -1
  58. eotdl-2023.11.2.post5/eotdl/access/__init__.py +0 -4
  59. eotdl-2023.11.2.post5/eotdl/access/airbus/__init__.py +0 -2
  60. eotdl-2023.11.2.post5/eotdl/access/airbus/client.py +0 -367
  61. eotdl-2023.11.2.post5/eotdl/access/airbus/utils.py +0 -29
  62. eotdl-2023.11.2.post5/eotdl/access/search.py +0 -15
  63. eotdl-2023.11.2.post5/eotdl/access/sentinelhub/client.py +0 -110
  64. eotdl-2023.11.2.post5/eotdl/access/sentinelhub/parameters.py +0 -70
  65. eotdl-2023.11.2.post5/eotdl/access/sentinelhub/utils.py +0 -90
  66. eotdl-2023.11.2.post5/eotdl/datasets/download.py +0 -119
  67. eotdl-2023.11.2.post5/eotdl/datasets/ingest.py +0 -55
  68. eotdl-2023.11.2.post5/eotdl/files/__init__.py +0 -1
  69. eotdl-2023.11.2.post5/eotdl/repos/DatasetsAPIRepo.py +0 -249
  70. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/auth/__init__.py +0 -0
  71. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/auth/auth.py +0 -0
  72. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/auth/is_logged.py +0 -0
  73. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/auth/logout.py +0 -0
  74. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/cli.py +0 -0
  75. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/commands/__init__.py +0 -0
  76. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/commands/datasets.py +0 -0
  77. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/commands/models.py +0 -0
  78. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/datasets/metadata.py +0 -0
  79. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/datasets/retrieve.py +0 -0
  80. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/datasets/usecases/__init__.py +0 -0
  81. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/datasets/usecases/datasets/DownloadFile.py +0 -0
  82. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/datasets/usecases/datasets/DownloadFileURL.py +0 -0
  83. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/datasets/usecases/datasets/IngestDataset.py +0 -0
  84. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/datasets/usecases/datasets/IngestLargeDataset.py +0 -0
  85. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/datasets/usecases/datasets/IngestLargeDatasetParallel.py +0 -0
  86. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/datasets/usecases/datasets/IngestSTAC.py +0 -0
  87. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/datasets/usecases/datasets/__init__.py +0 -0
  88. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/models/__init__.py +0 -0
  89. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/models/ingest.py +0 -0
  90. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/models/metadata.py +0 -0
  91. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/models/retrieve.py +0 -0
  92. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/repos/APIRepo.py +0 -0
  93. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/repos/AuthRepo.py +0 -0
  94. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/repos/__init__.py +0 -0
  95. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/shared/__init__.py +0 -0
  96. {eotdl-2023.11.2.post5 → eotdl-2023.11.3.post2}/eotdl/shared/checksum.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: eotdl
3
- Version: 2023.11.2.post5
3
+ Version: 2023.11.3.post2
4
4
  Summary: Earth Observation Training Data Lab
5
5
  License: MIT
6
6
  Author: EarthPulse
@@ -12,11 +12,13 @@ Classifier: Programming Language :: Python :: 3.8
12
12
  Classifier: Programming Language :: Python :: 3.9
13
13
  Classifier: Programming Language :: Python :: 3.10
14
14
  Classifier: Programming Language :: Python :: 3.11
15
+ Requires-Dist: black (>=23.10.1,<24.0.0)
15
16
  Requires-Dist: geomet (>=1.0.0,<2.0.0)
16
17
  Requires-Dist: geopandas (>=0.13.2,<0.14.0)
18
+ Requires-Dist: mypy (>=1.6.1,<2.0.0)
17
19
  Requires-Dist: pydantic (>=1.10.6,<2.0.0)
18
20
  Requires-Dist: pyjwt (>=2.6.0,<3.0.0)
19
- Requires-Dist: pystac (>=1.8.2,<2.0.0)
21
+ Requires-Dist: pystac[validation] (==1.8.2)
20
22
  Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
21
23
  Requires-Dist: rasterio (>=1.3.9,<2.0.0)
22
24
  Requires-Dist: requests (>=2.28.2,<3.0.0)
@@ -27,7 +29,7 @@ Description-Content-Type: text/markdown
27
29
 
28
30
  <p align="center">
29
31
  <a href="https://www.eotdl.com/">
30
- <img src="eotdl.png" width="350px" alt="EOTDL" />
32
+ <img src="eotdl.png" alt="EOTDL" />
31
33
  </a>
32
34
  </p>
33
35
 
@@ -1,6 +1,6 @@
1
1
  <p align="center">
2
2
  <a href="https://www.eotdl.com/">
3
- <img src="eotdl.png" width="350px" alt="EOTDL" />
3
+ <img src="eotdl.png" alt="EOTDL" />
4
4
  </a>
5
5
  </p>
6
6
 
@@ -0,0 +1 @@
1
+ __version__ = "2023.11.03-2"
@@ -0,0 +1,7 @@
1
+ """
2
+ Data access module for eotdl package.
3
+ """
4
+
5
+ from .download import download_sentinel_imagery, search_and_download_sentinel_imagery
6
+ from .search import search_sentinel_imagery
7
+ from .sentinelhub.parameters import SUPPORTED_SENSORS
@@ -0,0 +1,6 @@
1
+ """
2
+ Airbus access module.
3
+ """
4
+
5
+ from .client import AirbusClient
6
+ from .utils import get_airbus_access_token
@@ -0,0 +1,385 @@
1
+ """
2
+ Module for managing the Airbus configuration and data access
3
+ """
4
+
5
+ import json
6
+ import time
7
+ from typing import Optional, Iterable
8
+ from os.path import join, exists
9
+ import requests
10
+ from requests.exceptions import ConnectTimeout, ReadTimeout
11
+
12
+ from .parameters import AirbusURL, AirbusProductType, AirbusImageFormat, AirbusRadiometricProcessing
13
+ from .utils import get_airbus_access_token
14
+ from ...tools import expand_time_interval, bbox_to_coordinates
15
+
16
+
17
+ class AirbusClient:
18
+ """
19
+ Client class to manage the Sentinel Hub Python interface.
20
+ """
21
+
22
+ def __init__(
23
+ self,
24
+ access_token: str,
25
+ api_key: str,
26
+ ) -> None:
27
+ """
28
+ Constructor
29
+
30
+ Params
31
+ ----------
32
+ access_token: str
33
+ Access token
34
+ api_key: str
35
+ API key
36
+ """
37
+ self.airbus_access_token = access_token
38
+ self._api_key = api_key
39
+
40
+ def get_total_products_price(
41
+ self, payload: dict, all_info: Optional[bool] = False
42
+ ) -> dict:
43
+ """
44
+ Get total products price
45
+ """
46
+ if all_info:
47
+ response = []
48
+ else:
49
+ response = 0
50
+
51
+ for _, location_data in payload.items():
52
+ product_id = location_data["image"]
53
+ if product_id:
54
+ price_response = self.get_product_price(
55
+ product_id, location_data["bounding_box"]
56
+ )
57
+ if all_info:
58
+ response.append(price_response)
59
+ else:
60
+ response += price_response["price"]["credits"]
61
+
62
+ return response
63
+
64
+ def get_product_price(
65
+ self,
66
+ product_id: str,
67
+ coordinates: Iterable,
68
+ product_type: Optional[AirbusProductType] = AirbusProductType.MULTISPECTRAL,
69
+ image_format: Optional[AirbusImageFormat] = AirbusImageFormat.GEOTIFF,
70
+ processing: Optional[
71
+ AirbusRadiometricProcessing
72
+ ] = AirbusRadiometricProcessing.REFLECTANCE,
73
+ ) -> dict:
74
+ """
75
+ Get product price
76
+
77
+ Params
78
+ ----------
79
+ product_id: str
80
+ Product ID
81
+ coordinates: tuple
82
+ Polygon coordinates
83
+ product_type: AirbusProductType
84
+ Product type
85
+ image_format: AirbusImageFormat
86
+ Image format
87
+ processing: AirbusRadiometricProcessing
88
+ Radiometric processing
89
+ Types are defined at parameters.py
90
+
91
+ Returns
92
+ ----------
93
+ dict
94
+ Product price
95
+ """
96
+ if isinstance(coordinates, (list, tuple)) and len(
97
+ coordinates
98
+ ) == 4:
99
+ coordinates = bbox_to_coordinates(coordinates)
100
+
101
+ headers = {
102
+ "Authorization": f"Bearer {self.airbus_access_token}",
103
+ "Content-Type": "application/json",
104
+ "Cache-Control": "no-cache",
105
+ }
106
+
107
+ # TODO make productType, imageformat and processing configurable with paramters controlled by classes like
108
+ # TODO AirbusProductType.multiSpectral, AirbusImageFormat.geotiff, AirbusProcessing
109
+ payload = {
110
+ "kind": "order.data.product",
111
+ "products": [
112
+ {
113
+ "productType": product_type,
114
+ "radiometricProcessing": processing,
115
+ "imageFormat": image_format,
116
+ "crsCode": "urn:ogc:def:crs:EPSG::4326",
117
+ "id": product_id,
118
+ "aoi": {"type": "Polygon", "coordinates": [coordinates]},
119
+ }
120
+ ],
121
+ }
122
+ response = requests.request(
123
+ "POST", AirbusURL.PRICES, json=payload, headers=headers, timeout=60000
124
+ )
125
+
126
+ return response.json()
127
+
128
+ def place_product_order(
129
+ self,
130
+ product_id: str,
131
+ coordinates: Iterable,
132
+ product_type: Optional[AirbusProductType] = AirbusProductType.MULTISPECTRAL,
133
+ image_format: Optional[AirbusImageFormat] = AirbusImageFormat.GEOTIFF,
134
+ processing: Optional[
135
+ AirbusRadiometricProcessing
136
+ ] = AirbusRadiometricProcessing.REFLECTANCE,
137
+ ) -> dict:
138
+ """
139
+ Place product order
140
+
141
+ Params
142
+ ----------
143
+ product_id: str
144
+ Product ID
145
+ bounding_box: tuple
146
+ Bounding box
147
+ product_type: AirbusProductType
148
+ Product type
149
+ image_format: AirbusImageFormat
150
+ Image format
151
+ processing: AirbusRadiometricProcessing
152
+ Radiometric processing
153
+ Types are defined at parameters.py
154
+
155
+ Returns
156
+ ----------
157
+ dict
158
+ Order data
159
+ """
160
+ if isinstance(coordinates, (list, tuple)) and len(
161
+ coordinates
162
+ ) == 4:
163
+ coordinates = bbox_to_coordinates(coordinates)
164
+
165
+ payload = {
166
+ "kind": "order.data.product",
167
+ "products": [
168
+ {
169
+ "productType": product_type,
170
+ "radiometricProcessing": processing,
171
+ "imageFormat": image_format,
172
+ "crsCode": "urn:ogc:def:crs:EPSG::4326",
173
+ "id": product_id,
174
+ "aoi": {"type": "Polygon", "coordinates": [coordinates]},
175
+ }
176
+ ],
177
+ }
178
+
179
+ headers = {
180
+ "Authorization": f"Bearer {self.airbus_access_token}",
181
+ }
182
+
183
+ response = requests.request(
184
+ "POST", AirbusURL.ORDERS, json=payload, headers=headers, timeout=60000
185
+ )
186
+
187
+ return response.json()
188
+
189
+ def search_image(
190
+ self,
191
+ bounding_box: Iterable,
192
+ acquisition_date: Iterable,
193
+ timeout: Optional[int] = 10,
194
+ ) -> dict:
195
+ """
196
+ Search image
197
+
198
+ Params
199
+ ----------
200
+ bounding_box: tuple or list
201
+ Bounding box
202
+ acquisition_date: tuple or list
203
+ Acquisition date
204
+ timeout: int
205
+ Timeout
206
+
207
+ Returns
208
+ ----------
209
+ dict
210
+ Image data
211
+ """
212
+ if isinstance(acquisition_date, (tuple, list)):
213
+ acquisition_date = "[" + ",".join(acquisition_date) + "]"
214
+ if isinstance(bounding_box, (tuple, list)) and len(
215
+ bounding_box
216
+ ) == 4:
217
+ bounding_box = ",".join(str(num) for num in bounding_box)
218
+
219
+ querystring = {"acquisitionDate": str(acquisition_date), "bbox": bounding_box}
220
+
221
+ headers = {
222
+ "authorization": f"Bearer {self.airbus_access_token}",
223
+ "cache-control": "no-cache",
224
+ }
225
+
226
+ try:
227
+ response = requests.request(
228
+ "GET",
229
+ AirbusURL.SEARCH,
230
+ headers=headers,
231
+ params=querystring,
232
+ verify=False,
233
+ timeout=timeout,
234
+ )
235
+ return response.json()
236
+ except json.decoder.JSONDecodeError:
237
+ print("JSONDecodeError")
238
+ print(response)
239
+ except ReadTimeout:
240
+ print("ReadTimeout")
241
+ print(response)
242
+
243
+ def search_images_close_in_time(
244
+ self,
245
+ payload_dict: dict,
246
+ path: Optional[str] = None,
247
+ max_days: Optional[int] = 30,
248
+ ) -> dict:
249
+ """
250
+ Search images close in time
251
+
252
+ Params
253
+ ----------
254
+ payload_dict: dict
255
+ Payload dictionary
256
+ max_days: int
257
+ Maximum days to search
258
+
259
+ Returns
260
+ ----------
261
+ dict
262
+ Dictionary with the image data, as {location_id: image_data}, to
263
+ maintain track of the location
264
+ """
265
+ responses = {}
266
+ if path:
267
+ responses_path = join(path, "airbus_images_response.json")
268
+ if exists(responses_path):
269
+ with open(responses_path, "r", encoding="utf-8") as f:
270
+ responses = json.load(f)
271
+
272
+ for location_id, location_info in list(payload_dict.items()):
273
+ bounding_box, time_interval = (
274
+ location_info["bounding_box"],
275
+ location_info["time_interval"],
276
+ )
277
+ days = 1
278
+
279
+ if location_id in responses:
280
+ continue
281
+
282
+ while days <= max_days:
283
+ try:
284
+ response = self.search_image(bounding_box, time_interval)
285
+ except ConnectTimeout or TimeoutError:
286
+ # Wait 5 seconds and try again
287
+ time.sleep(5)
288
+ # Restart the connection
289
+ self.airbus_access_token = get_airbus_access_token(self._api_key)
290
+ # Continue with the search
291
+ response = self.search_image(bounding_box, time_interval)
292
+
293
+ total_results = response["totalResults"]
294
+
295
+ if total_results > 0:
296
+ # By default, the search results are sorted per acquisition date
297
+ # (newest data is displayed first) and per cloud coverage (less cloudy images are displayed first).
298
+ # So, we can stop the search when we find the first image
299
+ if total_results > 1:
300
+ response["features"] = [response["features"][0]]
301
+ responses[location_id] = response["features"][0]
302
+ if path:
303
+ with open(responses_path, "w", encoding="utf-8") as f:
304
+ json.dump(responses, f)
305
+ break
306
+
307
+ time_interval = expand_time_interval(time_interval)
308
+ days += 1
309
+
310
+ # If no image is found, we add a None value to the dictionary
311
+ if total_results == 0:
312
+ responses[location_id] = None
313
+ if path:
314
+ with open(responses_path, "w", encoding="utf-8") as f:
315
+ json.dump(responses, f)
316
+
317
+ return responses
318
+
319
+ def format_product_payload(
320
+ self, location_payload: dict, images_response: dict
321
+ ) -> dict:
322
+ """
323
+ Format product payload
324
+ """
325
+ for product_id, _ in location_payload.items():
326
+ # Add new key to the dictionary
327
+ location_payload[product_id]["image_response"] = (
328
+ images_response[product_id] if product_id in images_response else None
329
+ )
330
+
331
+ return location_payload
332
+
333
+ def split_product_payload(self, product_payload: dict) -> dict:
334
+ """
335
+ Split product payload
336
+ """
337
+ # split the product payload depending on if 'image' is None or not
338
+ product_payload_with_image = {}
339
+ product_payload_without_image = {}
340
+
341
+ for product_id, info in product_payload.items():
342
+ if info["image"]:
343
+ product_payload_with_image[product_id] = info
344
+ else:
345
+ product_payload_without_image[product_id] = info
346
+
347
+ return product_payload_with_image, product_payload_without_image
348
+
349
+ def get_all_order_status(self):
350
+ """
351
+ Get all order status
352
+ """
353
+ headers = {
354
+ "Authorization": f"Bearer {self.airbus_access_token}",
355
+ "Content-Type": "application/json",
356
+ "Cache-Control": "no-cache",
357
+ }
358
+
359
+ response = requests.request("GET", AirbusURL.ALL_ORDERS_STATUS, headers=headers, timeout=60000)
360
+
361
+ return response.text
362
+
363
+ def get_account_information(self):
364
+ """
365
+ Get account information
366
+ """
367
+ headers = {
368
+ "Authorization": f"Bearer {self.airbus_access_token}",
369
+ "Content-Type": "application/json",
370
+ "Cache-Control": "no-cache",
371
+ }
372
+
373
+ response = requests.request("GET", AirbusURL.ACCOUNT, headers=headers, timeout=60000)
374
+
375
+ return response.json()
376
+
377
+ def get_user_roles(self):
378
+ """
379
+ Get user roles
380
+ """
381
+ headers = {"Authorization": f"Bearer {self.airbus_access_token}"}
382
+
383
+ response = requests.request("GET", AirbusURL.ROLES, headers=headers, timeout=60000)
384
+
385
+ return response.json()
@@ -2,7 +2,11 @@
2
2
  Parameters to access Airbus data
3
3
  """
4
4
 
5
- class AirbusURL():
5
+
6
+ class AirbusURL:
7
+ """
8
+ AIRBUS URLs
9
+ """
6
10
  PRICES = "https://data.api.oneatlas.airbus.com/api/v1/prices"
7
11
  ORDERS = "https://data.api.oneatlas.airbus.com/api/v1/orders"
8
12
  SEARCH = "https://search.foundation.api.oneatlas.airbus.com/api/v2/opensearch?constellation=SPOT"
@@ -10,16 +14,27 @@ class AirbusURL():
10
14
  ACCOUNT = "https://data.api.oneatlas.airbus.com/api/v1/me"
11
15
  ROLES = "https://data.api.oneatlas.airbus.com/api/v1/me/services"
12
16
 
17
+
13
18
  # Types are defined at: https://www.geoapi-airbusds.com/api-catalog-v2/oad-living-library/tutorials/#order-an-individual-product
14
19
 
15
- class AirbusProductType():
20
+
21
+ class AirbusProductType:
22
+ """
23
+ Airbus product types
24
+ """
16
25
  MULTISPECTRAL = "multiSpectral"
17
26
 
18
27
 
19
- class AirbusRadiometricProcessing():
28
+ class AirbusRadiometricProcessing:
29
+ """
30
+ Airbus radiometric processing
31
+ """
20
32
  REFLECTANCE = "REFLECTANCE"
21
33
 
22
34
 
23
- class AirbusImageFormat():
35
+ class AirbusImageFormat:
36
+ """
37
+ Airbus image format
38
+ """
24
39
  GEOTIFF = "image/geotiff"
25
40
  JP2 = "image/jp2"
@@ -0,0 +1,34 @@
1
+ """
2
+ Utils
3
+ """
4
+
5
+ import requests
6
+
7
+
8
+ def get_airbus_access_token(api_key: str) -> str:
9
+ """
10
+ Get Airbus access token
11
+
12
+ Returns:
13
+ str: access token
14
+ """
15
+ headers = {
16
+ "Content-Type": "application/x-www-form-urlencoded",
17
+ }
18
+
19
+ data = [
20
+ ("apikey", api_key),
21
+ ("grant_type", "api_key"),
22
+ ("client_id", "IDP"),
23
+ ]
24
+
25
+ response = requests.post(
26
+ "https://authenticate.foundation.api.oneatlas.airbus.com/auth/realms/IDP/protocol/openid-connect/token",
27
+ headers=headers,
28
+ data=data,
29
+ timeout=60
30
+ )
31
+
32
+ access_token = response.json()["access_token"]
33
+
34
+ return access_token
@@ -1,16 +1,28 @@
1
- from .sentinelhub import SHClient, SH_PARAMETERS_DICT, evaluate_sentinel_parameters, imagery_from_tmp_to_dir
2
- from .search import search_sentinel_imagery
1
+ """
2
+ Download imagery
3
+ """
3
4
 
4
- from shutil import rmtree
5
5
  from datetime import datetime
6
6
  from typing import Union, List
7
7
 
8
+ from .sentinelhub import (
9
+ SHClient,
10
+ SH_PARAMETERS_DICT,
11
+ evaluate_sentinel_parameters,
12
+ imagery_from_tmp_to_dir,
13
+ )
14
+ from .search import search_sentinel_imagery
15
+
8
16
 
9
- def download_sentinel_imagery(output: str,
10
- time_interval: Union[str, datetime, List[Union[str, datetime]]],
11
- bounding_box: List[Union[int, float]],
12
- sensor: str,
13
- ) -> None:
17
+ def download_sentinel_imagery(
18
+ output: str,
19
+ time_interval: Union[str, datetime, List[Union[str, datetime]]],
20
+ bounding_box: List[Union[int, float]],
21
+ sensor: str,
22
+ ) -> None:
23
+ """
24
+ Download Sentinel imagery
25
+ """
14
26
  evaluate_sentinel_parameters(sensor, time_interval, bounding_box, output)
15
27
 
16
28
  client = SHClient()
@@ -21,11 +33,15 @@ def download_sentinel_imagery(output: str,
21
33
  imagery_from_tmp_to_dir(output)
22
34
 
23
35
 
24
- def search_and_download_sentinel_imagery(output: str,
25
- time_interval: Union[str, datetime, List[Union[str, datetime]]],
26
- bounding_box: List[Union[int, float]],
27
- sensor: str,
28
- ) -> None:
36
+ def search_and_download_sentinel_imagery(
37
+ output: str,
38
+ time_interval: Union[str, datetime, List[Union[str, datetime]]],
39
+ bounding_box: List[Union[int, float]],
40
+ sensor: str,
41
+ ) -> None:
42
+ """
43
+ Search and download Sentinel imagery
44
+ """
29
45
  evaluate_sentinel_parameters(sensor, time_interval, bounding_box, output)
30
46
 
31
47
  client = SHClient()
@@ -34,7 +50,7 @@ def search_and_download_sentinel_imagery(output: str,
34
50
  results = search_sentinel_imagery(time_interval, bounding_box, sensor)
35
51
  timestamps = [date.strftime("%Y-%m-%d") for date in results.get_timestamps()]
36
52
 
37
- requests_list = list()
53
+ requests_list = []
38
54
  for date in timestamps:
39
55
  requests_list.append(client.request_data(date, bounding_box, parameters))
40
56
  client.download_data(requests_list)
@@ -0,0 +1,26 @@
1
+ """
2
+ Search imagery
3
+ """
4
+
5
+ from typing import Union, List
6
+ from datetime import datetime
7
+
8
+ from .sentinelhub import SHClient, SH_PARAMETERS_DICT, evaluate_sentinel_parameters
9
+
10
+
11
+ def search_sentinel_imagery(
12
+ time_interval: Union[str, datetime, List[Union[str, datetime]]],
13
+ bounding_box: List[Union[int, float]],
14
+ sensor: str,
15
+ ) -> None:
16
+ """
17
+ Search Sentinel imagery
18
+ """
19
+ evaluate_sentinel_parameters(
20
+ sensor, time_interval, bounding_box, output_needed=False
21
+ )
22
+
23
+ client = SHClient()
24
+ parameters = SH_PARAMETERS_DICT[sensor]()
25
+
26
+ return client.search_data(bounding_box, time_interval, parameters)
@@ -1,4 +1,8 @@
1
+ """
2
+ Sentinel-Hub data access module.
3
+ """
4
+
1
5
  from .client import SHClient
2
6
  from .parameters import SHParameters, SH_PARAMETERS_DICT
3
7
  from .evalscripts import EvalScripts
4
- from .utils import evaluate_sentinel_parameters, imagery_from_tmp_to_dir
8
+ from .utils import evaluate_sentinel_parameters, imagery_from_tmp_to_dir