eotdl 2024.10.7__tar.gz → 2025.2.10__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 (136) hide show
  1. eotdl-2025.2.10/.gitignore +40 -0
  2. {eotdl-2024.10.7 → eotdl-2025.2.10}/PKG-INFO +21 -33
  3. {eotdl-2024.10.7 → eotdl-2025.2.10}/README.md +2 -2
  4. eotdl-2025.2.10/eotdl/__init__.py +1 -0
  5. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/access/download.py +3 -2
  6. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/access/search.py +0 -2
  7. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/access/sentinelhub/client.py +2 -2
  8. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/access/sentinelhub/parameters.py +1 -1
  9. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/access/sentinelhub/utils.py +1 -5
  10. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/curation/stac/extensions/ml_dataset.py +1 -1
  11. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/datasets/ingest.py +2 -1
  12. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/models/download.py +3 -3
  13. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/repos/APIRepo.py +1 -1
  14. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/tools/geo_utils.py +7 -2
  15. eotdl-2025.2.10/eotdl.png +0 -0
  16. eotdl-2025.2.10/main.py +5 -0
  17. eotdl-2025.2.10/pyproject.toml +35 -0
  18. eotdl-2025.2.10/tests/__init__.py +0 -0
  19. eotdl-2025.2.10/tests/_test_access.py +24 -0
  20. eotdl-2025.2.10/tests/_test_hello.py +6 -0
  21. eotdl-2025.2.10/tests/unit/__init__.py +0 -0
  22. eotdl-2025.2.10/tests/unit/_test_auth.py +97 -0
  23. eotdl-2025.2.10/tests/unit/access/__init__.py +0 -0
  24. eotdl-2025.2.10/tests/unit/curation/__init__.py +0 -0
  25. eotdl-2025.2.10/tests/unit/curation/stac/__init__.py +0 -0
  26. eotdl-2025.2.10/tests/unit/curation/stac/fixtures.py +76 -0
  27. eotdl-2025.2.10/tests/unit/curation/stac/test_data/jaca_dataset_stac/catalog.json +18 -0
  28. eotdl-2025.2.10/tests/unit/curation/stac/test_data/jaca_dataset_stac/sentinel-2-l2a/Jaca_1/Jaca_1.json +222 -0
  29. eotdl-2025.2.10/tests/unit/curation/stac/test_data/jaca_dataset_stac/sentinel-2-l2a/Jaca_2/Jaca_2.json +222 -0
  30. eotdl-2025.2.10/tests/unit/curation/stac/test_data/jaca_dataset_stac/sentinel-2-l2a/Jaca_3/Jaca_3.json +222 -0
  31. eotdl-2025.2.10/tests/unit/curation/stac/test_data/jaca_dataset_stac/sentinel-2-l2a/Jaca_4/Jaca_4.json +222 -0
  32. eotdl-2025.2.10/tests/unit/curation/stac/test_data/jaca_dataset_stac/sentinel-2-l2a/collection.json +59 -0
  33. eotdl-2025.2.10/tests/unit/curation/stac/test_data/jaca_dataset_stac_labels/catalog.json +23 -0
  34. eotdl-2025.2.10/tests/unit/curation/stac/test_data/jaca_dataset_stac_labels/labels/Jaca_1/Jaca_1.json +98 -0
  35. eotdl-2025.2.10/tests/unit/curation/stac/test_data/jaca_dataset_stac_labels/labels/Jaca_2/Jaca_2.json +98 -0
  36. eotdl-2025.2.10/tests/unit/curation/stac/test_data/jaca_dataset_stac_labels/labels/Jaca_3/Jaca_3.json +98 -0
  37. eotdl-2025.2.10/tests/unit/curation/stac/test_data/jaca_dataset_stac_labels/labels/Jaca_4/Jaca_4.json +98 -0
  38. eotdl-2025.2.10/tests/unit/curation/stac/test_data/jaca_dataset_stac_labels/labels/collection.json +77 -0
  39. eotdl-2025.2.10/tests/unit/curation/stac/test_data/jaca_dataset_stac_labels/sentinel-2-l2a/Jaca_1/Jaca_1.json +222 -0
  40. eotdl-2025.2.10/tests/unit/curation/stac/test_data/jaca_dataset_stac_labels/sentinel-2-l2a/Jaca_2/Jaca_2.json +222 -0
  41. eotdl-2025.2.10/tests/unit/curation/stac/test_data/jaca_dataset_stac_labels/sentinel-2-l2a/Jaca_3/Jaca_3.json +222 -0
  42. eotdl-2025.2.10/tests/unit/curation/stac/test_data/jaca_dataset_stac_labels/sentinel-2-l2a/Jaca_4/Jaca_4.json +222 -0
  43. eotdl-2025.2.10/tests/unit/curation/stac/test_data/jaca_dataset_stac_labels/sentinel-2-l2a/collection.json +59 -0
  44. eotdl-2025.2.10/tests/unit/curation/stac/test_data/labels_scaneo/Jaca_1_labels.geojson +1 -0
  45. eotdl-2025.2.10/tests/unit/curation/stac/test_data/labels_scaneo/Jaca_2_labels.geojson +1 -0
  46. eotdl-2025.2.10/tests/unit/curation/stac/test_data/labels_scaneo/Jaca_3_labels.geojson +1 -0
  47. eotdl-2025.2.10/tests/unit/curation/stac/test_data/labels_scaneo/Jaca_4_labels.geojson +1 -0
  48. eotdl-2025.2.10/tests/unit/curation/stac/test_data/labels_scaneo/labels.json +1 -0
  49. eotdl-2025.2.10/tests/unit/curation/stac/test_data/sentinel_2/Boadella_2020-01-13.tif +0 -0
  50. eotdl-2025.2.10/tests/unit/curation/stac/test_data/sentinel_2/Boadella_2020-01-13_labels.geojson +1 -0
  51. eotdl-2025.2.10/tests/unit/curation/stac/test_data/sentinel_2/Boadella_2020-01-28.tif +0 -0
  52. eotdl-2025.2.10/tests/unit/curation/stac/test_data/sentinel_2/Boadella_2020-02-02_labels.geojson +6255 -0
  53. eotdl-2025.2.10/tests/unit/curation/stac/test_dataframe.py +39 -0
  54. eotdl-2025.2.10/tests/unit/curation/stac/test_extent.py +33 -0
  55. eotdl-2025.2.10/tests/unit/curation/stac/test_labels.py +37 -0
  56. eotdl-2025.2.10/tests/unit/curation/stac/test_ml_dataset.py +96 -0
  57. eotdl-2025.2.10/tests/unit/curation/stac/test_stac.py +89 -0
  58. eotdl-2025.2.10/tests/unit/datasets/__init__.py +0 -0
  59. eotdl-2025.2.10/tests/unit/datasets/_test_datasets.py +114 -0
  60. eotdl-2025.2.10/tests/unit/datasets/_test_download_dataset.py +19 -0
  61. eotdl-2025.2.10/tests/unit/datasets/_test_retrieve_datasets.py +117 -0
  62. eotdl-2025.2.10/tests/unit/tools/__init__.py +0 -0
  63. eotdl-2025.2.10/tests/unit/tools/test_geo_utils.py +132 -0
  64. eotdl-2025.2.10/tests/unit/tools/test_metadata.py +50 -0
  65. eotdl-2025.2.10/tests/unit/tools/test_paths.py +40 -0
  66. eotdl-2025.2.10/tests/unit/tools/test_time_utils.py +107 -0
  67. eotdl-2025.2.10/tests/unit/tools/test_tools.py +6 -0
  68. eotdl-2024.10.7/eotdl/__init__.py +0 -1
  69. eotdl-2024.10.7/eotdl/wrappers/utils.py +0 -35
  70. /eotdl-2024.10.7/pyproject.toml → /eotdl-2025.2.10/_pyproject.toml +0 -0
  71. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/access/__init__.py +0 -0
  72. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/access/airbus/__init__.py +0 -0
  73. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/access/airbus/client.py +0 -0
  74. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/access/airbus/parameters.py +0 -0
  75. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/access/airbus/utils.py +0 -0
  76. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/access/sentinelhub/__init__.py +0 -0
  77. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/access/sentinelhub/evalscripts.py +0 -0
  78. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/auth/__init__.py +0 -0
  79. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/auth/auth.py +0 -0
  80. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/auth/errors.py +0 -0
  81. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/auth/is_logged.py +0 -0
  82. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/auth/logout.py +0 -0
  83. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/cli.py +0 -0
  84. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/commands/__init__.py +0 -0
  85. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/commands/auth.py +0 -0
  86. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/commands/datasets.py +0 -0
  87. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/commands/models.py +0 -0
  88. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/curation/__init__.py +0 -0
  89. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/curation/stac/__init__.py +0 -0
  90. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/curation/stac/assets.py +0 -0
  91. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/curation/stac/dataframe.py +0 -0
  92. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/curation/stac/dataframe_bck.py +0 -0
  93. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/curation/stac/dataframe_labeling.py +0 -0
  94. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/curation/stac/extensions/__init__.py +0 -0
  95. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/curation/stac/extensions/base.py +0 -0
  96. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/curation/stac/extensions/dem.py +0 -0
  97. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/curation/stac/extensions/eo.py +0 -0
  98. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/curation/stac/extensions/label/__init__.py +0 -0
  99. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/curation/stac/extensions/label/base.py +0 -0
  100. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/curation/stac/extensions/label/image_name_labeler.py +0 -0
  101. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/curation/stac/extensions/label/scaneo.py +0 -0
  102. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/curation/stac/extensions/projection.py +0 -0
  103. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/curation/stac/extensions/raster.py +0 -0
  104. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/curation/stac/extensions/sar.py +0 -0
  105. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/curation/stac/extent.py +0 -0
  106. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/curation/stac/parsers.py +0 -0
  107. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/curation/stac/stac.py +0 -0
  108. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/datasets/__init__.py +0 -0
  109. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/datasets/download.py +0 -0
  110. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/datasets/metadata.py +0 -0
  111. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/datasets/retrieve.py +0 -0
  112. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/datasets/update.py +0 -0
  113. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/files/__init__.py +0 -0
  114. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/files/ingest.py +0 -0
  115. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/files/list_files.py +0 -0
  116. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/models/__init__.py +0 -0
  117. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/models/ingest.py +0 -0
  118. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/models/metadata.py +0 -0
  119. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/models/retrieve.py +0 -0
  120. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/models/update.py +0 -0
  121. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/repos/AuthAPIRepo.py +0 -0
  122. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/repos/AuthRepo.py +0 -0
  123. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/repos/DatasetsAPIRepo.py +0 -0
  124. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/repos/FilesAPIRepo.py +0 -0
  125. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/repos/ModelsAPIRepo.py +0 -0
  126. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/repos/__init__.py +0 -0
  127. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/shared/__init__.py +0 -0
  128. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/shared/checksum.py +0 -0
  129. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/tools/__init__.py +0 -0
  130. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/tools/metadata.py +0 -0
  131. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/tools/paths.py +0 -0
  132. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/tools/stac.py +0 -0
  133. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/tools/time_utils.py +0 -0
  134. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/tools/tools.py +0 -0
  135. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/wrappers/__init__.py +0 -0
  136. {eotdl-2024.10.7 → eotdl-2025.2.10}/eotdl/wrappers/models.py +0 -0
@@ -0,0 +1,40 @@
1
+ __pycache__
2
+ .pytest_cache
3
+ dist
4
+ .DS_Store
5
+ node_modules
6
+ build
7
+ .svelte-kit
8
+ package
9
+ .env
10
+ .env.*
11
+ .conda
12
+ !.env.example
13
+ yarn.lock
14
+ poetry.lock
15
+ .vercel
16
+ data
17
+ metadata
18
+ eurosat-stac
19
+ .ipynb_checkpoints
20
+ eurosat-stac**
21
+ .coverage
22
+ data
23
+ db
24
+ *.zip
25
+ docker-compose.prod.yml
26
+ tutorials/*md
27
+ !test.zip
28
+ .vscode
29
+ kk
30
+ auth.json
31
+ node_modules/
32
+ /test-results/
33
+ playwright-report
34
+ playwright/.cache/
35
+ test-results
36
+ lightning_logs
37
+ jobs.csv
38
+ *.parquet
39
+ download_commands.txt
40
+ _data
@@ -1,35 +1,23 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: eotdl
3
- Version: 2024.10.7
3
+ Version: 2025.2.10
4
4
  Summary: Earth Observation Training Data Lab
5
- License: MIT
6
- Author: EarthPulse
7
- Author-email: it@earthpulse.es
8
- Requires-Python: >=3.8,<4.0
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.8
12
- Classifier: Programming Language :: Python :: 3.9
13
- Classifier: Programming Language :: Python :: 3.10
14
- Classifier: Programming Language :: Python :: 3.11
15
- Classifier: Programming Language :: Python :: 3.12
16
- Requires-Dist: black (>=23.10.1,<24.0.0)
17
- Requires-Dist: geomet (>=1.0.0,<2.0.0)
18
- Requires-Dist: geopandas (>=0.13.2,<0.14.0)
19
- Requires-Dist: markdown (>=3.5.2,<4.0.0)
20
- Requires-Dist: markdownify (>=0.11.6,<0.12.0)
21
- Requires-Dist: mypy (>=1.6.1,<2.0.0)
22
- Requires-Dist: openeo (>=0.31.0,<0.32.0)
23
- Requires-Dist: pydantic (>=1.10.6,<2.0.0)
24
- Requires-Dist: pyjwt (>=2.6.0,<3.0.0)
25
- Requires-Dist: pystac[validation] (==1.8.2)
26
- Requires-Dist: python-frontmatter (>=1.1.0,<2.0.0)
27
- Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
28
- Requires-Dist: rasterio (>=1.3.9,<2.0.0)
29
- Requires-Dist: requests (>=2.28.2,<3.0.0)
30
- Requires-Dist: sentinelhub (>=3.9.1,<4.0.0)
31
- Requires-Dist: tqdm (>=4.65.0,<5.0.0)
32
- Requires-Dist: typer[all] (>=0.9.0,<0.10.0)
5
+ Author-email: earthpulse <it@earthpulse.es>
6
+ License-Expression: MIT
7
+ Requires-Python: >=3.8
8
+ Requires-Dist: geomet>=1.1.0
9
+ Requires-Dist: geopandas>=0.13.2
10
+ Requires-Dist: markdown>=3.7
11
+ Requires-Dist: pydantic>=2.10.6
12
+ Requires-Dist: pyjwt>=2.9.0
13
+ Requires-Dist: pystac>=1.8.4
14
+ Requires-Dist: python-frontmatter>=1.1.0
15
+ Requires-Dist: pyyaml>=6.0.2
16
+ Requires-Dist: rasterio>=1.3.11
17
+ Requires-Dist: requests>=2.32.3
18
+ Requires-Dist: sentinelhub>=3.11.1
19
+ Requires-Dist: tqdm>=4.67.1
20
+ Requires-Dist: typer>=0.15.1
33
21
  Description-Content-Type: text/markdown
34
22
 
35
23
  <p align="center">
@@ -49,10 +37,10 @@ Description-Content-Type: text/markdown
49
37
 
50
38
  This is the main library and CLI for the **Earth Observation Training Data Lab** (EOTDL), a complete environment that allows you, among other things, to:
51
39
 
52
- - Explore and download Training Datasets (TDS) for Earth Observation (EO) applications.
40
+ - Explore and stage Training Datasets (TDS) for Earth Observation (EO) applications.
53
41
  - Create and upload your own TDS by combining and annotating EO data from different sources.
54
42
  - Train Machine Learning (ML) models using the hosted TDS in the cloud with multi-GPU machines.
55
- - Explore and download pre-trianed ML models for EO applications.
43
+ - Explore and stage pre-trianed ML models for EO applications.
56
44
 
57
45
  In our blog you will find tutorials to learn how leverage the EOTDL to create and use TDS and ML models for your own EO applications.
58
46
 
@@ -62,4 +50,4 @@ One of the most limiting factors of AI for EO applications is the scarcity of su
62
50
 
63
51
  Generating TDS is time consuming and expensive. Data access is usually limited and costly, especially for Very High Resolution (VHR) images that allow objects like trees to be clearly identified. In some cases, domain experts or even in-person (in-situ) trips are required to manually confirm the objects in a satellite image are correctly annotated with a high degree of quality. This results in the field of AI for EO applications lagging when compared to other fields, impeding the development of new applications and limiting the full potential of AI in EO.
64
52
 
65
- The European Space Agency (ESA) Earth Observation Training Data Lab (EOTDL) will address key limitations and capability gaps for working with Machine Learning (ML) training data in EO by providing a set of open-source tools to create, share, and improve datasets as well as training ML algorithms in the cloud. EOTDL will also offer an online repository where datasets and models can be explored and accessed.
53
+ The European Space Agency (ESA) Earth Observation Training Data Lab (EOTDL) will address key limitations and capability gaps for working with Machine Learning (ML) training data in EO by providing a set of open-source tools to create, share, and improve datasets as well as training ML algorithms in the cloud. EOTDL will also offer an online repository where datasets and models can be explored and accessed.
@@ -15,10 +15,10 @@
15
15
 
16
16
  This is the main library and CLI for the **Earth Observation Training Data Lab** (EOTDL), a complete environment that allows you, among other things, to:
17
17
 
18
- - Explore and download Training Datasets (TDS) for Earth Observation (EO) applications.
18
+ - Explore and stage Training Datasets (TDS) for Earth Observation (EO) applications.
19
19
  - Create and upload your own TDS by combining and annotating EO data from different sources.
20
20
  - Train Machine Learning (ML) models using the hosted TDS in the cloud with multi-GPU machines.
21
- - Explore and download pre-trianed ML models for EO applications.
21
+ - Explore and stage pre-trianed ML models for EO applications.
22
22
 
23
23
  In our blog you will find tutorials to learn how leverage the EOTDL to create and use TDS and ML models for your own EO applications.
24
24
 
@@ -0,0 +1 @@
1
+ __version__ = "2025.02.10"
@@ -42,8 +42,9 @@ def download_sentinel_imagery(
42
42
  bulk = False
43
43
  else:
44
44
  bulk = True
45
- client.download_data(requests_list)
46
- imagery_from_tmp_to_dir(output, name=name, bulk=bulk)
45
+
46
+ data = client.download_data(requests_list)
47
+ imagery_from_tmp_to_dir(output, client.tmp_dir, name=name, bulk=bulk)
47
48
 
48
49
 
49
50
  def search_and_download_sentinel_imagery(
@@ -19,8 +19,6 @@ def search_sentinel_imagery(
19
19
  evaluate_sentinel_parameters(
20
20
  sensor, time_interval, bounding_box, output_needed=False
21
21
  )
22
-
23
22
  client = SHClient()
24
23
  parameters = SH_PARAMETERS_DICT[sensor]()
25
-
26
24
  return client.search_data(bounding_box, time_interval, parameters)
@@ -15,6 +15,7 @@ from sentinelhub import (
15
15
  SentinelHubDownloadClient,
16
16
  MimeType,
17
17
  )
18
+ import uuid
18
19
 
19
20
  from ...repos.AuthRepo import AuthRepo
20
21
  from .parameters import SHParameters
@@ -57,7 +58,7 @@ class SHClient:
57
58
  self.config.sh_client_id = creds["SH_CLIENT_ID"]
58
59
  self.config.sh_client_secret = creds["SH_CLIENT_SECRET"]
59
60
  self.catalog = SentinelHubCatalog(config=self.config)
60
- self.tmp_dir = "/tmp/sentinelhub"
61
+ self.tmp_dir = "/tmp/sentinelhub/" + str(uuid.uuid4())
61
62
 
62
63
  def search_data(
63
64
  self, bounding_box: list, time_interval: list, parameters: SHParameters
@@ -109,5 +110,4 @@ class SHClient:
109
110
  requests = [requests]
110
111
  download_requests = [request.download_list[0] for request in requests]
111
112
  data = download_client.download(download_requests)
112
-
113
113
  return data
@@ -42,7 +42,7 @@ class SHS2L1CParameters(SHParameters):
42
42
  "include": ["id", "properties.datetime", "properties.eo:cloud_cover"],
43
43
  "exclude": [],
44
44
  }
45
-
45
+ FILTER = None
46
46
 
47
47
  class SHS1Parameters(SHParameters):
48
48
  """
@@ -50,7 +50,7 @@ def evaluate_sentinel_parameters(
50
50
 
51
51
  def imagery_from_tmp_to_dir(
52
52
  output_dir: str,
53
- tmp_dir: Optional[str] = "/tmp/sentinelhub",
53
+ tmp_dir: Optional[str],
54
54
  name: Optional[str] = None,
55
55
  bulk: Optional[bool] = False,
56
56
  ) -> None:
@@ -60,9 +60,7 @@ def imagery_from_tmp_to_dir(
60
60
  downloaded_files = glob(f"{tmp_dir}/**/response.tiff")
61
61
  if len(downloaded_files) == 0:
62
62
  return
63
-
64
63
  makedirs(output_dir, exist_ok=True)
65
-
66
64
  for downloaded_file in downloaded_files:
67
65
  request_json = downloaded_file.replace("response.tiff", "request.json")
68
66
  metadata = generate_raster_metadata(downloaded_file, request_json)
@@ -75,11 +73,9 @@ def imagery_from_tmp_to_dir(
75
73
  output_filename = f"{metadata['type']}_{metadata['acquisition-date']}"
76
74
  else:
77
75
  output_filename = metadata["type"]
78
-
79
76
  copyfile(downloaded_file, f"{output_dir}/{output_filename}.tif")
80
77
  with open(f"{output_dir}/{output_filename}.json", "w", encoding="utf-8") as f:
81
78
  json.dump(metadata, f)
82
-
83
79
  rmtree(tmp_dir)
84
80
 
85
81
 
@@ -377,7 +377,7 @@ class MLDatasetQualityMetrics:
377
377
  destination
378
378
  ) # Remove the old catalog and replace it with the new one
379
379
  catalog.set_root(catalog)
380
- catalog.normalize_and_save(root_href=destination)
380
+ catalog.normalize_and_save(root_href=destination, catalog_type=pystac.CatalogType.SELF_CONTAINED)
381
381
  print("Success!")
382
382
  except STACValidationError:
383
383
  # Return full callback
@@ -3,6 +3,7 @@ import yaml
3
3
  from tqdm import tqdm
4
4
  import json
5
5
  import frontmatter
6
+ import pystac
6
7
 
7
8
  from ..auth import with_auth
8
9
  from .metadata import Metadata
@@ -129,7 +130,7 @@ def ingest_stac(stac_catalog, logger=None, user=None):
129
130
  repo, files_repo = DatasetsAPIRepo(), FilesAPIRepo()
130
131
  # load catalog
131
132
  logger("Loading STAC catalog...")
132
- df = STACDataFrame.from_stac_file(stac_catalog)
133
+ df = STACDataFrame.from_stac_file(stac_catalog) # assets are absolute for file ingestion
133
134
  catalog = df[df["type"] == "Catalog"]
134
135
  assert len(catalog) == 1, "STAC catalog must have exactly one root catalog"
135
136
  dataset_name = catalog.id.iloc[0]
@@ -74,12 +74,12 @@ def download_model(
74
74
  )
75
75
  if error:
76
76
  raise Exception(error)
77
- print(gdf)
77
+ # print(gdf)
78
78
  df = STACDataFrame(gdf)
79
79
  # df.geometry = df.geometry.apply(lambda x: Polygon() if x is None else x)
80
80
  df.to_stac(download_path)
81
- print("----")
82
- print(df)
81
+ # print("----")
82
+ # print(df)
83
83
  # download assets
84
84
  if assets:
85
85
  if verbose:
@@ -5,7 +5,7 @@ import requests
5
5
  class APIRepo:
6
6
  def __init__(self, url=None):
7
7
  default_url = "https://api.eotdl.com/"
8
- # default_url = "http://localhost:8010/"
8
+ # default_url = "http://localhost:8001/"
9
9
  self.url = url if url else os.getenv("EOTDL_API_URL", default_url)
10
10
 
11
11
  def format_response(self, response):
@@ -127,6 +127,11 @@ def bbox_from_centroid(
127
127
  width_m = width * pixel_size
128
128
  heigth_m = height * pixel_size
129
129
 
130
+ # Initialise the transformers
131
+ utm_crs = CRS.get_utm_from_wgs84(y, x).ogc_string()
132
+ from_4326_transformer = Transformer.from_crs("EPSG:4326", utm_crs)
133
+ to_4326_transformer = Transformer.from_crs(utm_crs, "EPSG:4326")
134
+
130
135
  # Transform the centroid coordinates to meters
131
136
  centroid_m = from_4326_transformer.transform(x, y)
132
137
 
@@ -137,8 +142,8 @@ def bbox_from_centroid(
137
142
  max_y = centroid_m[1] + heigth_m / 2
138
143
 
139
144
  # Convert the bounding box coordinates back to degrees
140
- min_x, min_y = from_3857_transformer.transform(min_x, min_y)
141
- max_x, max_y = from_3857_transformer.transform(max_x, max_y)
145
+ min_x, min_y = to_4326_transformer.transform(min_x, min_y)
146
+ max_x, max_y = to_4326_transformer.transform(max_x, max_y)
142
147
 
143
148
  return [min_y, min_x, max_y, max_x]
144
149
 
Binary file
@@ -0,0 +1,5 @@
1
+ from eotdl.cli import app
2
+
3
+ if __name__ == "__main__":
4
+ app()
5
+
@@ -0,0 +1,35 @@
1
+ [project]
2
+ name = "eotdl"
3
+ version = "2025.02.10"
4
+ description = "Earth Observation Training Data Lab"
5
+ authors = [
6
+ {name = "earthpulse", email = "it@earthpulse.es"},
7
+ ]
8
+ license = "MIT"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ dependencies = [
12
+ "geomet>=1.1.0",
13
+ "geopandas>=0.13.2",
14
+ "markdown>=3.7",
15
+ "pydantic>=2.10.6",
16
+ "pyjwt>=2.9.0",
17
+ "pystac>=1.8.4",
18
+ "python-frontmatter>=1.1.0",
19
+ "pyyaml>=6.0.2",
20
+ "rasterio>=1.3.11",
21
+ "requests>=2.32.3",
22
+ "sentinelhub>=3.11.1",
23
+ "tqdm>=4.67.1",
24
+ "typer>=0.15.1",
25
+ ]
26
+
27
+ [project.scripts]
28
+ eotdl = "eotdl.cli:app"
29
+
30
+ [build-system]
31
+ requires = ["hatchling"]
32
+ build-backend = "hatchling.build"
33
+
34
+ [tool.hatch.build.targets.wheel]
35
+ packages = ["eotdl"]
File without changes
@@ -0,0 +1,24 @@
1
+ import pytest
2
+ try:
3
+ from lib.eotdl import SHClient
4
+ except ImportError:
5
+ from eotdl import SHClient
6
+
7
+
8
+ def test_sh_client():
9
+ client = SHClient(sh_client_id='my_client_id',
10
+ sh_client_secret='my_client_secret')
11
+
12
+ assert client.config.sh_client_id is not None
13
+
14
+
15
+ def test_search_available_sentinel_data():
16
+ pass
17
+
18
+
19
+ def test_request_bulk_data():
20
+ pass
21
+
22
+
23
+ def test_request_data():
24
+ pass
@@ -0,0 +1,6 @@
1
+ import pytest
2
+ from eotdl.hello import say_hello
3
+
4
+
5
+ def test_hello():
6
+ assert say_hello() == "Hello, World!"
File without changes
@@ -0,0 +1,97 @@
1
+ import pytest
2
+ from unittest import mock
3
+
4
+ from eotdl.eotdl.auth.auth import Auth
5
+ from eotdl.src.errors.auth import LoginError, AuthTimeOut
6
+ from eotdl.eotdl.auth.is_logged import IsLogged
7
+ from eotdl.eotdl.auth.logout import Logout
8
+
9
+
10
+ def test_auth():
11
+ repo = mock.Mock()
12
+ api_repo = mock.Mock()
13
+ auth = Auth(repo, api_repo)
14
+ inputs = Auth.Inputs()
15
+ api_repo.login.return_value = mock.Mock(
16
+ status_code=200,
17
+ json=mock.Mock(return_value={"code": 123, "login_url": "123123"}),
18
+ )
19
+ token_data = {"id_token": "lkajshdflkjahsldkfjhalskjdfhlkasjdhf"}
20
+ api_repo.token.return_value = mock.Mock(
21
+ status_code=200, json=mock.Mock(return_value=token_data)
22
+ )
23
+ repo.save_creds.return_value = "creds_path"
24
+ repo.decode_token.return_value = {
25
+ "uid": "test",
26
+ "name": "test name",
27
+ "email": "test email",
28
+ "picture": "test picture",
29
+ }
30
+ outputs = auth(inputs)
31
+ api_repo.login.assert_called_once()
32
+ api_repo.token.assert_called_once_with(123)
33
+ repo.save_creds.assert_called_once_with(token_data)
34
+ repo.decode_token.assert_called_once_with(token_data)
35
+ assert outputs.user["uid"] == "test"
36
+ assert outputs.user["name"] == "test name"
37
+ assert outputs.user["email"] == "test email"
38
+ assert outputs.user["picture"] == "test picture"
39
+
40
+
41
+ def test_auth_fails_if_not_logged_in():
42
+ repo = mock.Mock()
43
+ api_repo = mock.Mock()
44
+ auth = Auth(repo, api_repo)
45
+ inputs = Auth.Inputs()
46
+ api_repo.login.return_value = mock.Mock(status_code=400)
47
+ with pytest.raises(LoginError):
48
+ auth(inputs)
49
+ api_repo.login.assert_called_once()
50
+
51
+
52
+ def test_auth_fails_if_auth_timeout():
53
+ repo = mock.Mock()
54
+ api_repo = mock.Mock()
55
+ auth = Auth(
56
+ repo, api_repo, max_t=0.1, interval=0.01
57
+ ) # will try to login 10 times, then timeout
58
+ inputs = Auth.Inputs()
59
+ api_repo.login.return_value = mock.Mock(
60
+ status_code=200,
61
+ json=mock.Mock(return_value={"code": 123, "login_url": "123123"}),
62
+ )
63
+ api_repo.token.return_value = mock.Mock(status_code=400)
64
+ with pytest.raises(AuthTimeOut):
65
+ auth(inputs)
66
+
67
+
68
+ def test_is_logged_in():
69
+ repo = mock.Mock()
70
+ repo.load_creds.return_value = {"uid": 123}
71
+ is_logged = IsLogged(repo)
72
+ inputs = IsLogged.Inputs()
73
+ outputs = is_logged(inputs)
74
+ repo.load_creds.assert_called_once()
75
+ assert outputs.user == {"uid": 123}
76
+
77
+
78
+ def test_is_not_logged_in():
79
+ repo = mock.Mock()
80
+ repo.load_creds.return_value = None
81
+ is_logged = IsLogged(repo)
82
+ inputs = IsLogged.Inputs()
83
+ outputs = is_logged(inputs)
84
+ repo.load_creds.assert_called_once()
85
+ assert outputs.user is None
86
+
87
+
88
+ def test_logout():
89
+ repo = mock.Mock()
90
+ api_repo = mock.Mock()
91
+ logout = Logout(repo, api_repo)
92
+ inputs = Logout.Inputs()
93
+ api_repo.logout_url.return_value = "ñklasjdf"
94
+ outputs = logout(inputs)
95
+ api_repo.logout_url.assert_called_once()
96
+ repo.logout.assert_called_once()
97
+ assert outputs.logout_url == "ñklasjdf"
File without changes
File without changes
File without changes
@@ -0,0 +1,76 @@
1
+ import os
2
+ import shutil
3
+ import pytest
4
+
5
+ from pystac import Catalog
6
+
7
+ EXAMPLE_DATA_DIR = "tests/unit/curation/stac/test_data"
8
+
9
+
10
+ @pytest.fixture
11
+ def tmp_stac_catalog():
12
+ original_catalog_path = os.path.join(EXAMPLE_DATA_DIR, "jaca_dataset_stac")
13
+
14
+ os.makedirs("tmp", exist_ok=True)
15
+ tmp_catalog_path = os.path.join("tmp", "copied_catalog")
16
+ tmp_catalog = Catalog.from_file(os.path.join(original_catalog_path, "catalog.json"))
17
+ tmp_catalog.normalize_and_save(tmp_catalog_path)
18
+
19
+ copied_catalog = Catalog.from_file(os.path.join(tmp_catalog_path, "catalog.json"))
20
+ yield copied_catalog
21
+
22
+ shutil.rmtree("tmp")
23
+
24
+
25
+ @pytest.fixture
26
+ def tmp_stac_catalog_labels():
27
+ original_catalog_path = os.path.join(EXAMPLE_DATA_DIR, "jaca_dataset_stac_labels")
28
+
29
+ os.makedirs("tmp", exist_ok=True)
30
+ tmp_catalog_path = os.path.join("tmp", "copied_catalog")
31
+ tmp_catalog = Catalog.from_file(os.path.join(original_catalog_path, "catalog.json"))
32
+ tmp_catalog.normalize_and_save(tmp_catalog_path)
33
+
34
+ copied_catalog = Catalog.from_file(os.path.join(tmp_catalog_path, "catalog.json"))
35
+ yield copied_catalog
36
+
37
+ shutil.rmtree("tmp")
38
+
39
+
40
+ @pytest.fixture
41
+ def tmp_stac_collection():
42
+ original_collection_path = os.path.join(
43
+ EXAMPLE_DATA_DIR, "jaca_dataset_stac", "collection.json"
44
+ )
45
+
46
+ os.makedirs("tmp", exist_ok=True)
47
+ tmp_collection_path = os.path.join("tmp", "copied_collection")
48
+ shutil.copy(original_collection_path, tmp_collection_path)
49
+
50
+ copied_collection = Catalog.from_file(
51
+ os.path.join(tmp_collection_path, "collection.json")
52
+ )
53
+ yield copied_collection
54
+
55
+ shutil.rmtree("tmp")
56
+
57
+
58
+ @pytest.fixture
59
+ def sentinel_2():
60
+ os.makedirs("tmp", exist_ok=True)
61
+
62
+ yield os.path.join(EXAMPLE_DATA_DIR, 'sentinel_2')
63
+
64
+ shutil.rmtree("tmp")
65
+
66
+
67
+ @pytest.fixture
68
+ def tmp_sentinel_2():
69
+ original_dataset_path = os.path.join(EXAMPLE_DATA_DIR, "sentinel_2")
70
+
71
+ os.makedirs("tmp", exist_ok=True)
72
+ shutil.copytree(original_dataset_path, "tmp/sentinel_2")
73
+
74
+ yield "tmp/sentinel_2"
75
+
76
+ shutil.rmtree("tmp")
@@ -0,0 +1,18 @@
1
+ {
2
+ "type": "Catalog",
3
+ "id": "jaca-dataset-extensions",
4
+ "stac_version": "1.0.0",
5
+ "description": "Jaca dataset with STAC extensions",
6
+ "links": [
7
+ {
8
+ "rel": "root",
9
+ "href": "./catalog.json",
10
+ "type": "application/json"
11
+ },
12
+ {
13
+ "rel": "child",
14
+ "href": "./sentinel-2-l2a/collection.json",
15
+ "type": "application/json"
16
+ }
17
+ ]
18
+ }