tilebox-storage 0.47.0__tar.gz → 0.48.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.
- {tilebox_storage-0.47.0 → tilebox_storage-0.48.0}/PKG-INFO +1 -1
- {tilebox_storage-0.47.0 → tilebox_storage-0.48.0}/tilebox/storage/__init__.py +12 -0
- {tilebox_storage-0.47.0 → tilebox_storage-0.48.0}/tilebox/storage/aio.py +86 -7
- {tilebox_storage-0.47.0 → tilebox_storage-0.48.0}/tilebox/storage/granule.py +25 -0
- {tilebox_storage-0.47.0 → tilebox_storage-0.48.0}/.gitignore +0 -0
- {tilebox_storage-0.47.0 → tilebox_storage-0.48.0}/README.md +0 -0
- {tilebox_storage-0.47.0 → tilebox_storage-0.48.0}/pyproject.toml +0 -0
- {tilebox_storage-0.47.0 → tilebox_storage-0.48.0}/tilebox/storage/providers.py +0 -0
|
@@ -2,6 +2,7 @@ from pathlib import Path
|
|
|
2
2
|
|
|
3
3
|
from tilebox.storage.aio import ASFStorageClient as _ASFStorageClient
|
|
4
4
|
from tilebox.storage.aio import CopernicusStorageClient as _CopernicusStorageClient
|
|
5
|
+
from tilebox.storage.aio import LocalFileSystemStorageClient as _LocalFileSystemStorageClient
|
|
5
6
|
from tilebox.storage.aio import UmbraStorageClient as _UmbraStorageClient
|
|
6
7
|
from tilebox.storage.aio import USGSLandsatStorageClient as _USGSLandsatStorageClient
|
|
7
8
|
|
|
@@ -66,3 +67,14 @@ class USGSLandsatStorageClient(_USGSLandsatStorageClient):
|
|
|
66
67
|
"""
|
|
67
68
|
super().__init__(cache_directory)
|
|
68
69
|
self._syncify()
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class LocalFileSystemStorageClient(_LocalFileSystemStorageClient):
|
|
73
|
+
def __init__(self, root: Path) -> None:
|
|
74
|
+
"""A tilebox storage client for accessing data on a local file system, or a mounted network file system.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
root: The root directory of the file system to access.
|
|
78
|
+
"""
|
|
79
|
+
super().__init__(root)
|
|
80
|
+
self._syncify()
|
|
@@ -24,6 +24,7 @@ from _tilebox.grpc.aio.syncify import Syncifiable
|
|
|
24
24
|
from tilebox.storage.granule import (
|
|
25
25
|
ASFStorageGranule,
|
|
26
26
|
CopernicusStorageGranule,
|
|
27
|
+
LocationStorageGranule,
|
|
27
28
|
UmbraStorageGranule,
|
|
28
29
|
USGSLandsatStorageGranule,
|
|
29
30
|
)
|
|
@@ -241,6 +242,10 @@ def _display_quicklook(image_data: bytes | Path, width: int, height: int, image_
|
|
|
241
242
|
|
|
242
243
|
|
|
243
244
|
class StorageClient(Syncifiable):
|
|
245
|
+
"""Base class for all storage clients."""
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
class CachingStorageClient(StorageClient):
|
|
244
249
|
def __init__(self, cache_directory: Path | None) -> None:
|
|
245
250
|
self._cache = cache_directory
|
|
246
251
|
|
|
@@ -323,7 +328,7 @@ async def _download_object(
|
|
|
323
328
|
return output_path
|
|
324
329
|
|
|
325
330
|
|
|
326
|
-
class ASFStorageClient(
|
|
331
|
+
class ASFStorageClient(CachingStorageClient):
|
|
327
332
|
def __init__(self, user: str, password: str, cache_directory: Path = Path.home() / ".cache" / "tilebox") -> None:
|
|
328
333
|
"""A tilebox storage client that downloads data from the Alaska Satellite Facility.
|
|
329
334
|
|
|
@@ -415,7 +420,7 @@ class ASFStorageClient(StorageClient):
|
|
|
415
420
|
"""
|
|
416
421
|
granule = ASFStorageGranule.from_data(datapoint)
|
|
417
422
|
if Image is None:
|
|
418
|
-
raise ImportError("IPython is not available, please use
|
|
423
|
+
raise ImportError("IPython is not available, please use download_quicklook instead.")
|
|
419
424
|
quicklook = await self._download_quicklook(datapoint)
|
|
420
425
|
_display_quicklook(quicklook, width, height, f"<code>Image {quicklook.name} © ASF {granule.time.year}</code>")
|
|
421
426
|
|
|
@@ -439,7 +444,7 @@ def _umbra_s3_prefix(datapoint: xr.Dataset | UmbraStorageGranule) -> str:
|
|
|
439
444
|
return f"sar-data/tasks/{granule.location}/"
|
|
440
445
|
|
|
441
446
|
|
|
442
|
-
class UmbraStorageClient(
|
|
447
|
+
class UmbraStorageClient(CachingStorageClient):
|
|
443
448
|
_STORAGE_PROVIDER = "Umbra"
|
|
444
449
|
_BUCKET = "umbra-open-data-catalog"
|
|
445
450
|
_REGION = "us-west-2"
|
|
@@ -539,7 +544,7 @@ def _copernicus_s3_prefix(datapoint: xr.Dataset | CopernicusStorageGranule) -> s
|
|
|
539
544
|
return granule.location.removeprefix("/eodata/")
|
|
540
545
|
|
|
541
546
|
|
|
542
|
-
class CopernicusStorageClient(
|
|
547
|
+
class CopernicusStorageClient(CachingStorageClient):
|
|
543
548
|
_STORAGE_PROVIDER = "CopernicusDataspace"
|
|
544
549
|
_BUCKET = "eodata"
|
|
545
550
|
_ENDPOINT_URL = "https://eodata.dataspace.copernicus.eu"
|
|
@@ -724,7 +729,7 @@ class CopernicusStorageClient(StorageClient):
|
|
|
724
729
|
ValueError: If no quicklook is available for the given datapoint.
|
|
725
730
|
"""
|
|
726
731
|
if Image is None:
|
|
727
|
-
raise ImportError("IPython is not available, please use
|
|
732
|
+
raise ImportError("IPython is not available, please use download_quicklook instead.")
|
|
728
733
|
granule = CopernicusStorageGranule.from_data(datapoint)
|
|
729
734
|
quicklook = await self._download_quicklook(granule)
|
|
730
735
|
_display_quicklook(quicklook, width, height, f"<code>{granule.granule_name} © ESA {granule.time.year}</code>")
|
|
@@ -750,7 +755,7 @@ def _landsat_s3_prefix(datapoint: xr.Dataset | USGSLandsatStorageGranule) -> str
|
|
|
750
755
|
return granule.location.removeprefix("s3://usgs-landsat/")
|
|
751
756
|
|
|
752
757
|
|
|
753
|
-
class USGSLandsatStorageClient(
|
|
758
|
+
class USGSLandsatStorageClient(CachingStorageClient):
|
|
754
759
|
"""
|
|
755
760
|
A client for downloading USGS Landsat data from the usgs-landsat and usgs-landsat-ard S3 bucket.
|
|
756
761
|
|
|
@@ -883,7 +888,7 @@ class USGSLandsatStorageClient(StorageClient):
|
|
|
883
888
|
ValueError: If no quicklook is available for the given datapoint.
|
|
884
889
|
"""
|
|
885
890
|
if Image is None:
|
|
886
|
-
raise ImportError("IPython is not available, please use
|
|
891
|
+
raise ImportError("IPython is not available, please use download_quicklook instead.")
|
|
887
892
|
quicklook = await self._download_quicklook(datapoint)
|
|
888
893
|
_display_quicklook(quicklook, width, height, f"<code>Image {quicklook.name} © USGS</code>")
|
|
889
894
|
|
|
@@ -901,3 +906,77 @@ class USGSLandsatStorageClient(StorageClient):
|
|
|
901
906
|
|
|
902
907
|
await download_objects(self._store, prefix, [granule.thumbnail], output_folder, show_progress=False)
|
|
903
908
|
return output_folder / granule.thumbnail
|
|
909
|
+
|
|
910
|
+
|
|
911
|
+
class LocalFileSystemStorageClient(StorageClient):
|
|
912
|
+
def __init__(self, root: Path) -> None:
|
|
913
|
+
"""A tilebox storage client for accessing data on a local file system, or a mounted network file system.
|
|
914
|
+
|
|
915
|
+
Args:
|
|
916
|
+
root: The root directory of the file system to access.
|
|
917
|
+
"""
|
|
918
|
+
super().__init__()
|
|
919
|
+
self._root = Path(root)
|
|
920
|
+
|
|
921
|
+
async def list_objects(self, datapoint: xr.Dataset | LocationStorageGranule) -> list[str]:
|
|
922
|
+
"""List all available objects for a given datapoint."""
|
|
923
|
+
granule = LocationStorageGranule.from_data(datapoint)
|
|
924
|
+
granule_path = self._root / granule.location
|
|
925
|
+
return [p.relative_to(granule_path).as_posix() for p in granule_path.rglob("**/*") if p.is_file()]
|
|
926
|
+
|
|
927
|
+
async def download(
|
|
928
|
+
self,
|
|
929
|
+
datapoint: xr.Dataset | LocationStorageGranule,
|
|
930
|
+
) -> Path:
|
|
931
|
+
"""No-op download method, as the data is already on the local file system.
|
|
932
|
+
|
|
933
|
+
Args:
|
|
934
|
+
datapoint: The datapoint to locate the data for in the local file system.
|
|
935
|
+
|
|
936
|
+
Returns:
|
|
937
|
+
The path to the data on the local file system.
|
|
938
|
+
"""
|
|
939
|
+
granule = LocationStorageGranule.from_data(datapoint)
|
|
940
|
+
granule_path = self._root / granule.location
|
|
941
|
+
if not granule_path.exists():
|
|
942
|
+
raise ValueError(f"Data not found on the local file system: {granule_path}")
|
|
943
|
+
return granule_path
|
|
944
|
+
|
|
945
|
+
async def _download_quicklook(self, datapoint: xr.Dataset | LocationStorageGranule) -> Path:
|
|
946
|
+
granule = LocationStorageGranule.from_data(datapoint)
|
|
947
|
+
if granule.thumbnail is None:
|
|
948
|
+
raise ValueError(f"No quicklook available for {granule.location}")
|
|
949
|
+
quicklook_path = self._root / granule.thumbnail
|
|
950
|
+
if not quicklook_path.exists():
|
|
951
|
+
raise ValueError(f"Quicklook not found on the local file system: {quicklook_path}")
|
|
952
|
+
return quicklook_path
|
|
953
|
+
|
|
954
|
+
async def download_quicklook(self, datapoint: xr.Dataset | LocationStorageGranule) -> Path:
|
|
955
|
+
"""No-op download_quicklook method, as the quicklook image is already on the local file system.
|
|
956
|
+
|
|
957
|
+
Args:
|
|
958
|
+
datapoint: The datapoint to locate the quicklook image for in the local file system.
|
|
959
|
+
|
|
960
|
+
Returns:
|
|
961
|
+
The path to the data on the local file system.
|
|
962
|
+
|
|
963
|
+
Raises:
|
|
964
|
+
ValueError: If no quicklook image is available for the given datapoint, or if the quicklook image is not
|
|
965
|
+
found on the local file system.
|
|
966
|
+
"""
|
|
967
|
+
return await self._download_quicklook(datapoint)
|
|
968
|
+
|
|
969
|
+
async def quicklook(
|
|
970
|
+
self, datapoint: xr.Dataset | LocationStorageGranule, width: int = 600, height: int = 600
|
|
971
|
+
) -> None:
|
|
972
|
+
"""Display the quicklook image for a given datapoint.
|
|
973
|
+
|
|
974
|
+
Args:
|
|
975
|
+
datapoint: The datapoint to display the quicklook for.
|
|
976
|
+
width: Display width of the image in pixels. Defaults to 600.
|
|
977
|
+
height: Display height of the image in pixels. Defaults to 600.
|
|
978
|
+
"""
|
|
979
|
+
quicklook_path = await self._download_quicklook(datapoint)
|
|
980
|
+
if Image is None:
|
|
981
|
+
raise ImportError("IPython is not available, please use download_quicklook instead.")
|
|
982
|
+
_display_quicklook(quicklook_path, width, height, None)
|
|
@@ -183,3 +183,28 @@ class USGSLandsatStorageGranule:
|
|
|
183
183
|
dataset.location.item().replace("s3://usgs-landsat-ard/", "s3://usgs-landsat/"),
|
|
184
184
|
thumbnail,
|
|
185
185
|
)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
@dataclass
|
|
189
|
+
class LocationStorageGranule:
|
|
190
|
+
location: str
|
|
191
|
+
thumbnail: str | None = None
|
|
192
|
+
|
|
193
|
+
@classmethod
|
|
194
|
+
def from_data(cls, dataset: "xr.Dataset | LocationStorageGranule") -> "LocationStorageGranule":
|
|
195
|
+
"""Extract the granule information from a datapoint given as xarray dataset."""
|
|
196
|
+
if isinstance(dataset, LocationStorageGranule):
|
|
197
|
+
return dataset
|
|
198
|
+
|
|
199
|
+
if "location" not in dataset:
|
|
200
|
+
raise ValueError("The given dataset has no location information.")
|
|
201
|
+
|
|
202
|
+
thumbnail = None
|
|
203
|
+
if "thumbnail" in dataset:
|
|
204
|
+
thumbnail = dataset.thumbnail.item()
|
|
205
|
+
elif "overview" in dataset:
|
|
206
|
+
thumbnail = dataset.overview.item()
|
|
207
|
+
elif "quicklook" in dataset:
|
|
208
|
+
thumbnail = dataset.quicklook.item()
|
|
209
|
+
|
|
210
|
+
return cls(dataset.location.item(), thumbnail)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|