cdse-client 0.3.0__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.
- cdse_client-0.3.0/LICENSE +21 -0
- cdse_client-0.3.0/PKG-INFO +321 -0
- cdse_client-0.3.0/README.md +231 -0
- cdse_client-0.3.0/pyproject.toml +168 -0
- cdse_client-0.3.0/setup.cfg +4 -0
- cdse_client-0.3.0/src/cdse/__init__.py +101 -0
- cdse_client-0.3.0/src/cdse/async_client.py +335 -0
- cdse_client-0.3.0/src/cdse/auth.py +145 -0
- cdse_client-0.3.0/src/cdse/catalog.py +455 -0
- cdse_client-0.3.0/src/cdse/cli.py +356 -0
- cdse_client-0.3.0/src/cdse/client.py +504 -0
- cdse_client-0.3.0/src/cdse/converters.py +232 -0
- cdse_client-0.3.0/src/cdse/downloader.py +676 -0
- cdse_client-0.3.0/src/cdse/exceptions.py +43 -0
- cdse_client-0.3.0/src/cdse/geocoding.py +246 -0
- cdse_client-0.3.0/src/cdse/geometry.py +496 -0
- cdse_client-0.3.0/src/cdse/product.py +153 -0
- cdse_client-0.3.0/src/cdse/py.typed +0 -0
- cdse_client-0.3.0/src/cdse_client.egg-info/PKG-INFO +321 -0
- cdse_client-0.3.0/src/cdse_client.egg-info/SOURCES.txt +32 -0
- cdse_client-0.3.0/src/cdse_client.egg-info/dependency_links.txt +1 -0
- cdse_client-0.3.0/src/cdse_client.egg-info/entry_points.txt +2 -0
- cdse_client-0.3.0/src/cdse_client.egg-info/requires.txt +67 -0
- cdse_client-0.3.0/src/cdse_client.egg-info/top_level.txt +1 -0
- cdse_client-0.3.0/tests/test_async_client.py +27 -0
- cdse_client-0.3.0/tests/test_auth.py +125 -0
- cdse_client-0.3.0/tests/test_catalog.py +205 -0
- cdse_client-0.3.0/tests/test_cli.py +328 -0
- cdse_client-0.3.0/tests/test_client.py +149 -0
- cdse_client-0.3.0/tests/test_converters.py +279 -0
- cdse_client-0.3.0/tests/test_downloader.py +180 -0
- cdse_client-0.3.0/tests/test_geocoding.py +213 -0
- cdse_client-0.3.0/tests/test_geometry.py +188 -0
- cdse_client-0.3.0/tests/test_product.py +128 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2026 Vito D'Elia
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cdse-client
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Python client for Copernicus Data Space Ecosystem (CDSE) - Drop-in replacement for sentinelsat
|
|
5
|
+
Author-email: Vito D'Elia <75219756+VTvito@users.noreply.github.com>
|
|
6
|
+
Maintainer-email: Vito D'Elia <75219756+VTvito@users.noreply.github.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/VTvito/cdse-client
|
|
9
|
+
Project-URL: Documentation, https://github.com/VTvito/cdse-client#readme
|
|
10
|
+
Project-URL: Repository, https://github.com/VTvito/cdse-client.git
|
|
11
|
+
Project-URL: Issues, https://github.com/VTvito/cdse-client/issues
|
|
12
|
+
Project-URL: Changelog, https://github.com/VTvito/cdse-client/blob/main/CHANGELOG.md
|
|
13
|
+
Keywords: copernicus,sentinel,satellite,remote-sensing,earth-observation,CDSE,STAC,sentinelsat
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: Science/Research
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: GIS
|
|
25
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
26
|
+
Requires-Python: >=3.9
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Requires-Dist: requests>=2.28.0
|
|
30
|
+
Requires-Dist: requests-oauthlib>=1.3.0
|
|
31
|
+
Requires-Dist: oauthlib>=3.2.0
|
|
32
|
+
Requires-Dist: tqdm>=4.64.0
|
|
33
|
+
Requires-Dist: python-dateutil>=2.8.0
|
|
34
|
+
Provides-Extra: dev
|
|
35
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
36
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
37
|
+
Requires-Dist: pytest-mock>=3.10.0; extra == "dev"
|
|
38
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
39
|
+
Requires-Dist: responses>=0.22.0; extra == "dev"
|
|
40
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
41
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
42
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
43
|
+
Requires-Dist: build>=1.0.0; extra == "dev"
|
|
44
|
+
Requires-Dist: twine>=4.0.0; extra == "dev"
|
|
45
|
+
Requires-Dist: bandit>=1.7.0; extra == "dev"
|
|
46
|
+
Requires-Dist: types-requests>=2.28.0; extra == "dev"
|
|
47
|
+
Requires-Dist: types-tqdm>=4.64.0; extra == "dev"
|
|
48
|
+
Provides-Extra: docs
|
|
49
|
+
Requires-Dist: mkdocs>=1.5.0; extra == "docs"
|
|
50
|
+
Requires-Dist: mkdocs-material>=9.5.0; extra == "docs"
|
|
51
|
+
Requires-Dist: mkdocstrings[python]>=0.24.0; extra == "docs"
|
|
52
|
+
Requires-Dist: mkdocs-gen-files>=0.5.0; extra == "docs"
|
|
53
|
+
Requires-Dist: mkdocs-literate-nav>=0.6.0; extra == "docs"
|
|
54
|
+
Requires-Dist: mkdocs-section-index>=0.3.0; extra == "docs"
|
|
55
|
+
Provides-Extra: geo
|
|
56
|
+
Requires-Dist: shapely>=2.0.0; extra == "geo"
|
|
57
|
+
Requires-Dist: geojson>=3.0.0; extra == "geo"
|
|
58
|
+
Requires-Dist: geopy>=2.4.0; extra == "geo"
|
|
59
|
+
Requires-Dist: geopandas>=0.14.0; extra == "geo"
|
|
60
|
+
Provides-Extra: dataframe
|
|
61
|
+
Requires-Dist: pandas>=2.0.0; extra == "dataframe"
|
|
62
|
+
Provides-Extra: async
|
|
63
|
+
Requires-Dist: aiohttp>=3.9.0; extra == "async"
|
|
64
|
+
Requires-Dist: aiofiles>=23.0.0; extra == "async"
|
|
65
|
+
Provides-Extra: processing
|
|
66
|
+
Requires-Dist: rasterio>=1.3.0; extra == "processing"
|
|
67
|
+
Requires-Dist: shapely>=2.0.0; extra == "processing"
|
|
68
|
+
Requires-Dist: numpy>=1.24.0; extra == "processing"
|
|
69
|
+
Requires-Dist: Pillow>=10.0.0; extra == "processing"
|
|
70
|
+
Requires-Dist: matplotlib>=3.7.0; extra == "processing"
|
|
71
|
+
Provides-Extra: all
|
|
72
|
+
Requires-Dist: shapely>=2.0.0; extra == "all"
|
|
73
|
+
Requires-Dist: geojson>=3.0.0; extra == "all"
|
|
74
|
+
Requires-Dist: geopy>=2.4.0; extra == "all"
|
|
75
|
+
Requires-Dist: geopandas>=0.14.0; extra == "all"
|
|
76
|
+
Requires-Dist: pandas>=2.0.0; extra == "all"
|
|
77
|
+
Requires-Dist: aiohttp>=3.9.0; extra == "all"
|
|
78
|
+
Requires-Dist: aiofiles>=23.0.0; extra == "all"
|
|
79
|
+
Requires-Dist: rasterio>=1.3.0; extra == "all"
|
|
80
|
+
Requires-Dist: numpy>=1.24.0; extra == "all"
|
|
81
|
+
Requires-Dist: Pillow>=10.0.0; extra == "all"
|
|
82
|
+
Requires-Dist: matplotlib>=3.7.0; extra == "all"
|
|
83
|
+
Requires-Dist: mkdocs>=1.5.0; extra == "all"
|
|
84
|
+
Requires-Dist: mkdocs-material>=9.5.0; extra == "all"
|
|
85
|
+
Requires-Dist: mkdocstrings[python]>=0.24.0; extra == "all"
|
|
86
|
+
Requires-Dist: mkdocs-gen-files>=0.5.0; extra == "all"
|
|
87
|
+
Requires-Dist: mkdocs-literate-nav>=0.6.0; extra == "all"
|
|
88
|
+
Requires-Dist: mkdocs-section-index>=0.3.0; extra == "all"
|
|
89
|
+
Dynamic: license-file
|
|
90
|
+
|
|
91
|
+
# cdse-client
|
|
92
|
+
|
|
93
|
+
[](https://badge.fury.io/py/cdse-client)
|
|
94
|
+
[](https://pypi.org/project/cdse-client/)
|
|
95
|
+
[](https://opensource.org/licenses/MIT)
|
|
96
|
+
|
|
97
|
+
**Python client for Copernicus Data Space Ecosystem (CDSE)** — a modern replacement for `sentinelsat`.
|
|
98
|
+
|
|
99
|
+
Requires Python >= 3.9.
|
|
100
|
+
|
|
101
|
+
## Installation
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
pip install cdse-client # Core
|
|
105
|
+
pip install cdse-client[geo] # + shapely, geopandas, geopy
|
|
106
|
+
pip install cdse-client[dataframe] # + pandas
|
|
107
|
+
pip install cdse-client[processing] # + rasterio, numpy, pillow, matplotlib, shapely
|
|
108
|
+
pip install cdse-client[async] # + aiohttp, aiofiles
|
|
109
|
+
pip install cdse-client[all] # Everything
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Setup
|
|
113
|
+
|
|
114
|
+
1. Register at [Copernicus Data Space](https://dataspace.copernicus.eu/)
|
|
115
|
+
2. Create OAuth2 credentials in [Account Settings](https://dataspace.copernicus.eu/profile)
|
|
116
|
+
|
|
117
|
+
Environment variables:
|
|
118
|
+
|
|
119
|
+
- macOS/Linux (bash/zsh)
|
|
120
|
+
```bash
|
|
121
|
+
export CDSE_CLIENT_ID="your-client-id"
|
|
122
|
+
export CDSE_CLIENT_SECRET="your-client-secret"
|
|
123
|
+
```
|
|
124
|
+
- Windows (PowerShell)
|
|
125
|
+
```powershell
|
|
126
|
+
$env:CDSE_CLIENT_ID = "your-client-id"
|
|
127
|
+
$env:CDSE_CLIENT_SECRET = "your-client-secret"
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Quick start
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from cdse import CDSEClient
|
|
134
|
+
|
|
135
|
+
client = CDSEClient() # Uses environment variables
|
|
136
|
+
|
|
137
|
+
# Search Sentinel-2 products
|
|
138
|
+
products = client.search(
|
|
139
|
+
bbox=[9.0, 45.0, 9.5, 45.5], # Milan area
|
|
140
|
+
start_date="2024-01-01",
|
|
141
|
+
end_date="2024-01-31",
|
|
142
|
+
collection="sentinel-2-l2a",
|
|
143
|
+
cloud_cover_max=20,
|
|
144
|
+
limit=5
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
# Download
|
|
148
|
+
for product in products:
|
|
149
|
+
client.download(product)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Search methods
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
# By bounding box
|
|
156
|
+
products = client.search(bbox=[lon_min, lat_min, lon_max, lat_max], ...)
|
|
157
|
+
|
|
158
|
+
# By geographic point
|
|
159
|
+
products = client.search_by_point(lon=9.19, lat=45.46, buffer_km=10, ...)
|
|
160
|
+
|
|
161
|
+
# By city name (requires [geo])
|
|
162
|
+
products = client.search_by_city(city_name="Milano, Italia", ...)
|
|
163
|
+
|
|
164
|
+
# By product name (OData catalogue)
|
|
165
|
+
products = client.search_by_name("S2A_MSIL2A_20240115T102351...", exact=True)
|
|
166
|
+
|
|
167
|
+
# By UUID (OData catalogue)
|
|
168
|
+
product = client.search_by_id("a1b2c3d4-e5f6...")
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Note: `search()` returns STAC results; product identifiers there are not guaranteed to be OData UUIDs. If you need a UUID, use `search_by_name(..., exact=True)`.
|
|
172
|
+
|
|
173
|
+
**Collections**: `sentinel-1-grd`, `sentinel-2-l1c`, `sentinel-2-l2a`, `sentinel-3-olci`, `sentinel-3-slstr`, `sentinel-5p-l2`
|
|
174
|
+
|
|
175
|
+
## Download methods
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
# Single product
|
|
179
|
+
client.download(product, output_dir="./downloads")
|
|
180
|
+
|
|
181
|
+
# Multiple products (parallel)
|
|
182
|
+
client.download_all(products, parallel=True, max_workers=4)
|
|
183
|
+
|
|
184
|
+
# With checksum verification
|
|
185
|
+
client.download_with_checksum(product)
|
|
186
|
+
|
|
187
|
+
# Quicklook preview only
|
|
188
|
+
client.download_quicklook(product)
|
|
189
|
+
client.download_all_quicklooks(products)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Data export (sentinelsat compatible)
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
# DataFrame for sorting/filtering
|
|
196
|
+
df = client.to_dataframe(products)
|
|
197
|
+
df.sort_values('cloud_cover').to_csv("products.csv")
|
|
198
|
+
|
|
199
|
+
# GeoJSON footprints
|
|
200
|
+
geojson = client.to_geojson(products)
|
|
201
|
+
|
|
202
|
+
# GeoDataFrame for spatial analysis (requires [geo])
|
|
203
|
+
gdf = client.to_geodataframe(products)
|
|
204
|
+
gdf.plot()
|
|
205
|
+
|
|
206
|
+
# Total size
|
|
207
|
+
size_gb = client.get_products_size(products)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Geometry utilities
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
from cdse import read_geojson, geojson_to_wkt, bbox_to_geojson
|
|
214
|
+
|
|
215
|
+
geojson = read_geojson("area.geojson")
|
|
216
|
+
wkt = geojson_to_wkt(geojson)
|
|
217
|
+
geojson = bbox_to_geojson([9.0, 45.0, 9.5, 45.5])
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Processing
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
pip install cdse-client[processing]
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
```python
|
|
227
|
+
from cdse.processing import calculate_ndvi, crop_and_stack, preview_product
|
|
228
|
+
|
|
229
|
+
# Extract bands, crop to AOI, stack into GeoTIFF
|
|
230
|
+
result = crop_and_stack(
|
|
231
|
+
safe_path="S2A_MSIL2A_20240115.zip",
|
|
232
|
+
bbox=[9.15, 45.45, 9.25, 45.55],
|
|
233
|
+
bands=["B04", "B03", "B02", "B08"],
|
|
234
|
+
resolution=10
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
# Calculate NDVI
|
|
238
|
+
ndvi = calculate_ndvi(nir_path="B08.tif", red_path="B04.tif")
|
|
239
|
+
|
|
240
|
+
# Preview in Jupyter
|
|
241
|
+
preview_product(safe_path="...", bbox=[...], display=True)
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Async support
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
pip install cdse-client[async]
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
import asyncio
|
|
252
|
+
from cdse import CDSEClientAsync
|
|
253
|
+
|
|
254
|
+
async def main():
|
|
255
|
+
async with CDSEClientAsync(client_id, client_secret) as client:
|
|
256
|
+
products = await client.search(...)
|
|
257
|
+
paths = await client.download_all(products)
|
|
258
|
+
|
|
259
|
+
asyncio.run(main())
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## CLI
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
# Search
|
|
266
|
+
cdse search --bbox 9.0,45.0,9.5,45.5 -s 2024-01-01 -e 2024-01-31 -c 20 -l 5
|
|
267
|
+
|
|
268
|
+
# Download by name/UUID
|
|
269
|
+
cdse download --name S2A_MSIL2A_20240115T102351...
|
|
270
|
+
cdse download --uuid a1b2c3d4-... [--quicklook] [--checksum]
|
|
271
|
+
|
|
272
|
+
# List collections
|
|
273
|
+
cdse collections
|
|
274
|
+
|
|
275
|
+
# Help
|
|
276
|
+
cdse --help
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## Migration from sentinelsat
|
|
280
|
+
|
|
281
|
+
| sentinelsat | cdse-client |
|
|
282
|
+
|-------------|-------------|
|
|
283
|
+
| `SentinelAPI(user, password)` | `CDSEClient(client_id, client_secret)` |
|
|
284
|
+
| `api.query(area, date, ...)` | `client.search(bbox, start_date, ...)` |
|
|
285
|
+
| `api.download(uuid)` | `client.download(product)` |
|
|
286
|
+
| `api.download_all(products)` | `client.download_all(products)` |
|
|
287
|
+
| `api.download_quicklook(uuid)` | `client.download_quicklook(product)` |
|
|
288
|
+
| `api.to_dataframe(products)` | `client.to_dataframe(products)` |
|
|
289
|
+
| `api.to_geojson(products)` | `client.to_geojson(products)` |
|
|
290
|
+
| `read_geojson(path)` | `read_geojson(path)` |
|
|
291
|
+
| `geojson_to_wkt(geojson)` | `geojson_to_wkt(geojson)` |
|
|
292
|
+
|
|
293
|
+
## Resources
|
|
294
|
+
|
|
295
|
+
- [Copernicus Data Space](https://dataspace.copernicus.eu/)
|
|
296
|
+
- [CDSE API Documentation](https://documentation.dataspace.copernicus.eu/)
|
|
297
|
+
|
|
298
|
+
## Documentation (MkDocs)
|
|
299
|
+
|
|
300
|
+
Build and preview locally:
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
pip install -e ".[docs]"
|
|
304
|
+
mkdocs serve
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## Disclaimer
|
|
308
|
+
|
|
309
|
+
This is an **unofficial** client library and is not affiliated with, endorsed by, or connected to ESA, the European Commission, or the Copernicus Programme.
|
|
310
|
+
|
|
311
|
+
Copernicus Data Space Ecosystem and Sentinel data are provided by ESA and the European Commission. Users must:
|
|
312
|
+
|
|
313
|
+
1. Register at [dataspace.copernicus.eu](https://dataspace.copernicus.eu/)
|
|
314
|
+
2. Comply with the [Terms and Conditions](https://dataspace.copernicus.eu/terms-and-conditions)
|
|
315
|
+
3. Respect [API quotas and fair usage policies](https://documentation.dataspace.copernicus.eu/Quotas.html)
|
|
316
|
+
|
|
317
|
+
Sentinel data is available under a **free, full, and open** data policy for any use, including commercial. See the [Sentinel Data Legal Notice](https://sentinels.copernicus.eu/documents/247904/690755/Sentinel_Data_Legal_Notice).
|
|
318
|
+
|
|
319
|
+
## License
|
|
320
|
+
|
|
321
|
+
MIT License - see [LICENSE](LICENSE)
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# cdse-client
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/py/cdse-client)
|
|
4
|
+
[](https://pypi.org/project/cdse-client/)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
**Python client for Copernicus Data Space Ecosystem (CDSE)** — a modern replacement for `sentinelsat`.
|
|
8
|
+
|
|
9
|
+
Requires Python >= 3.9.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install cdse-client # Core
|
|
15
|
+
pip install cdse-client[geo] # + shapely, geopandas, geopy
|
|
16
|
+
pip install cdse-client[dataframe] # + pandas
|
|
17
|
+
pip install cdse-client[processing] # + rasterio, numpy, pillow, matplotlib, shapely
|
|
18
|
+
pip install cdse-client[async] # + aiohttp, aiofiles
|
|
19
|
+
pip install cdse-client[all] # Everything
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Setup
|
|
23
|
+
|
|
24
|
+
1. Register at [Copernicus Data Space](https://dataspace.copernicus.eu/)
|
|
25
|
+
2. Create OAuth2 credentials in [Account Settings](https://dataspace.copernicus.eu/profile)
|
|
26
|
+
|
|
27
|
+
Environment variables:
|
|
28
|
+
|
|
29
|
+
- macOS/Linux (bash/zsh)
|
|
30
|
+
```bash
|
|
31
|
+
export CDSE_CLIENT_ID="your-client-id"
|
|
32
|
+
export CDSE_CLIENT_SECRET="your-client-secret"
|
|
33
|
+
```
|
|
34
|
+
- Windows (PowerShell)
|
|
35
|
+
```powershell
|
|
36
|
+
$env:CDSE_CLIENT_ID = "your-client-id"
|
|
37
|
+
$env:CDSE_CLIENT_SECRET = "your-client-secret"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Quick start
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from cdse import CDSEClient
|
|
44
|
+
|
|
45
|
+
client = CDSEClient() # Uses environment variables
|
|
46
|
+
|
|
47
|
+
# Search Sentinel-2 products
|
|
48
|
+
products = client.search(
|
|
49
|
+
bbox=[9.0, 45.0, 9.5, 45.5], # Milan area
|
|
50
|
+
start_date="2024-01-01",
|
|
51
|
+
end_date="2024-01-31",
|
|
52
|
+
collection="sentinel-2-l2a",
|
|
53
|
+
cloud_cover_max=20,
|
|
54
|
+
limit=5
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Download
|
|
58
|
+
for product in products:
|
|
59
|
+
client.download(product)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Search methods
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
# By bounding box
|
|
66
|
+
products = client.search(bbox=[lon_min, lat_min, lon_max, lat_max], ...)
|
|
67
|
+
|
|
68
|
+
# By geographic point
|
|
69
|
+
products = client.search_by_point(lon=9.19, lat=45.46, buffer_km=10, ...)
|
|
70
|
+
|
|
71
|
+
# By city name (requires [geo])
|
|
72
|
+
products = client.search_by_city(city_name="Milano, Italia", ...)
|
|
73
|
+
|
|
74
|
+
# By product name (OData catalogue)
|
|
75
|
+
products = client.search_by_name("S2A_MSIL2A_20240115T102351...", exact=True)
|
|
76
|
+
|
|
77
|
+
# By UUID (OData catalogue)
|
|
78
|
+
product = client.search_by_id("a1b2c3d4-e5f6...")
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Note: `search()` returns STAC results; product identifiers there are not guaranteed to be OData UUIDs. If you need a UUID, use `search_by_name(..., exact=True)`.
|
|
82
|
+
|
|
83
|
+
**Collections**: `sentinel-1-grd`, `sentinel-2-l1c`, `sentinel-2-l2a`, `sentinel-3-olci`, `sentinel-3-slstr`, `sentinel-5p-l2`
|
|
84
|
+
|
|
85
|
+
## Download methods
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
# Single product
|
|
89
|
+
client.download(product, output_dir="./downloads")
|
|
90
|
+
|
|
91
|
+
# Multiple products (parallel)
|
|
92
|
+
client.download_all(products, parallel=True, max_workers=4)
|
|
93
|
+
|
|
94
|
+
# With checksum verification
|
|
95
|
+
client.download_with_checksum(product)
|
|
96
|
+
|
|
97
|
+
# Quicklook preview only
|
|
98
|
+
client.download_quicklook(product)
|
|
99
|
+
client.download_all_quicklooks(products)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Data export (sentinelsat compatible)
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
# DataFrame for sorting/filtering
|
|
106
|
+
df = client.to_dataframe(products)
|
|
107
|
+
df.sort_values('cloud_cover').to_csv("products.csv")
|
|
108
|
+
|
|
109
|
+
# GeoJSON footprints
|
|
110
|
+
geojson = client.to_geojson(products)
|
|
111
|
+
|
|
112
|
+
# GeoDataFrame for spatial analysis (requires [geo])
|
|
113
|
+
gdf = client.to_geodataframe(products)
|
|
114
|
+
gdf.plot()
|
|
115
|
+
|
|
116
|
+
# Total size
|
|
117
|
+
size_gb = client.get_products_size(products)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Geometry utilities
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
from cdse import read_geojson, geojson_to_wkt, bbox_to_geojson
|
|
124
|
+
|
|
125
|
+
geojson = read_geojson("area.geojson")
|
|
126
|
+
wkt = geojson_to_wkt(geojson)
|
|
127
|
+
geojson = bbox_to_geojson([9.0, 45.0, 9.5, 45.5])
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Processing
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
pip install cdse-client[processing]
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from cdse.processing import calculate_ndvi, crop_and_stack, preview_product
|
|
138
|
+
|
|
139
|
+
# Extract bands, crop to AOI, stack into GeoTIFF
|
|
140
|
+
result = crop_and_stack(
|
|
141
|
+
safe_path="S2A_MSIL2A_20240115.zip",
|
|
142
|
+
bbox=[9.15, 45.45, 9.25, 45.55],
|
|
143
|
+
bands=["B04", "B03", "B02", "B08"],
|
|
144
|
+
resolution=10
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
# Calculate NDVI
|
|
148
|
+
ndvi = calculate_ndvi(nir_path="B08.tif", red_path="B04.tif")
|
|
149
|
+
|
|
150
|
+
# Preview in Jupyter
|
|
151
|
+
preview_product(safe_path="...", bbox=[...], display=True)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Async support
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
pip install cdse-client[async]
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
import asyncio
|
|
162
|
+
from cdse import CDSEClientAsync
|
|
163
|
+
|
|
164
|
+
async def main():
|
|
165
|
+
async with CDSEClientAsync(client_id, client_secret) as client:
|
|
166
|
+
products = await client.search(...)
|
|
167
|
+
paths = await client.download_all(products)
|
|
168
|
+
|
|
169
|
+
asyncio.run(main())
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## CLI
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
# Search
|
|
176
|
+
cdse search --bbox 9.0,45.0,9.5,45.5 -s 2024-01-01 -e 2024-01-31 -c 20 -l 5
|
|
177
|
+
|
|
178
|
+
# Download by name/UUID
|
|
179
|
+
cdse download --name S2A_MSIL2A_20240115T102351...
|
|
180
|
+
cdse download --uuid a1b2c3d4-... [--quicklook] [--checksum]
|
|
181
|
+
|
|
182
|
+
# List collections
|
|
183
|
+
cdse collections
|
|
184
|
+
|
|
185
|
+
# Help
|
|
186
|
+
cdse --help
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Migration from sentinelsat
|
|
190
|
+
|
|
191
|
+
| sentinelsat | cdse-client |
|
|
192
|
+
|-------------|-------------|
|
|
193
|
+
| `SentinelAPI(user, password)` | `CDSEClient(client_id, client_secret)` |
|
|
194
|
+
| `api.query(area, date, ...)` | `client.search(bbox, start_date, ...)` |
|
|
195
|
+
| `api.download(uuid)` | `client.download(product)` |
|
|
196
|
+
| `api.download_all(products)` | `client.download_all(products)` |
|
|
197
|
+
| `api.download_quicklook(uuid)` | `client.download_quicklook(product)` |
|
|
198
|
+
| `api.to_dataframe(products)` | `client.to_dataframe(products)` |
|
|
199
|
+
| `api.to_geojson(products)` | `client.to_geojson(products)` |
|
|
200
|
+
| `read_geojson(path)` | `read_geojson(path)` |
|
|
201
|
+
| `geojson_to_wkt(geojson)` | `geojson_to_wkt(geojson)` |
|
|
202
|
+
|
|
203
|
+
## Resources
|
|
204
|
+
|
|
205
|
+
- [Copernicus Data Space](https://dataspace.copernicus.eu/)
|
|
206
|
+
- [CDSE API Documentation](https://documentation.dataspace.copernicus.eu/)
|
|
207
|
+
|
|
208
|
+
## Documentation (MkDocs)
|
|
209
|
+
|
|
210
|
+
Build and preview locally:
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
pip install -e ".[docs]"
|
|
214
|
+
mkdocs serve
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Disclaimer
|
|
218
|
+
|
|
219
|
+
This is an **unofficial** client library and is not affiliated with, endorsed by, or connected to ESA, the European Commission, or the Copernicus Programme.
|
|
220
|
+
|
|
221
|
+
Copernicus Data Space Ecosystem and Sentinel data are provided by ESA and the European Commission. Users must:
|
|
222
|
+
|
|
223
|
+
1. Register at [dataspace.copernicus.eu](https://dataspace.copernicus.eu/)
|
|
224
|
+
2. Comply with the [Terms and Conditions](https://dataspace.copernicus.eu/terms-and-conditions)
|
|
225
|
+
3. Respect [API quotas and fair usage policies](https://documentation.dataspace.copernicus.eu/Quotas.html)
|
|
226
|
+
|
|
227
|
+
Sentinel data is available under a **free, full, and open** data policy for any use, including commercial. See the [Sentinel Data Legal Notice](https://sentinels.copernicus.eu/documents/247904/690755/Sentinel_Data_Legal_Notice).
|
|
228
|
+
|
|
229
|
+
## License
|
|
230
|
+
|
|
231
|
+
MIT License - see [LICENSE](LICENSE)
|