dclimate-client-py 0.1.1__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 (53) hide show
  1. dclimate_client_py-0.1.1/LICENSE +21 -0
  2. dclimate_client_py-0.1.1/PKG-INFO +183 -0
  3. dclimate_client_py-0.1.1/README.md +142 -0
  4. dclimate_client_py-0.1.1/dclimate_client_py/.pytest_cache/.gitignore +2 -0
  5. dclimate_client_py-0.1.1/dclimate_client_py/.pytest_cache/CACHEDIR.TAG +4 -0
  6. dclimate_client_py-0.1.1/dclimate_client_py/.pytest_cache/README.md +8 -0
  7. dclimate_client_py-0.1.1/dclimate_client_py/.pytest_cache/v/cache/nodeids +1 -0
  8. dclimate_client_py-0.1.1/dclimate_client_py/.pytest_cache/v/cache/stepwise +1 -0
  9. dclimate_client_py-0.1.1/dclimate_client_py/__init__.py +32 -0
  10. dclimate_client_py-0.1.1/dclimate_client_py/cids.json +1 -0
  11. dclimate_client_py-0.1.1/dclimate_client_py/client.py +203 -0
  12. dclimate_client_py-0.1.1/dclimate_client_py/concatenate.py +207 -0
  13. dclimate_client_py-0.1.1/dclimate_client_py/datasets.py +667 -0
  14. dclimate_client_py-0.1.1/dclimate_client_py/dclimate_client.py +204 -0
  15. dclimate_client_py-0.1.1/dclimate_client_py/dclimate_zarr_errors.py +73 -0
  16. dclimate_client_py-0.1.1/dclimate_client_py/encryption_codec.py +109 -0
  17. dclimate_client_py-0.1.1/dclimate_client_py/geotemporal_data.py +722 -0
  18. dclimate_client_py-0.1.1/dclimate_client_py/ipfs_retrieval.py +144 -0
  19. dclimate_client_py-0.1.1/dclimate_client_py/s3_retrieval.py +105 -0
  20. dclimate_client_py-0.1.1/dclimate_client_py/zarr_metadata.py +107 -0
  21. dclimate_client_py-0.1.1/pyproject.toml +76 -0
  22. dclimate_client_py-0.1.1/tests/__init__.py +0 -0
  23. dclimate_client_py-0.1.1/tests/conftest.py +177 -0
  24. dclimate_client_py-0.1.1/tests/data/zarr-dev/metadata/Data Catalog.json +39 -0
  25. dclimate_client_py-0.1.1/tests/data/zarr-dev/metadata/Empty Data Catalog.json +21 -0
  26. dclimate_client_py-0.1.1/tests/data/zarr-dev/metadata/collections/CHIRPS.json +86 -0
  27. dclimate_client_py-0.1.1/tests/etc/central_ca_farm.geojson +8 -0
  28. dclimate_client_py-0.1.1/tests/etc/central_northern_ca_counties.cpg +1 -0
  29. dclimate_client_py-0.1.1/tests/etc/central_northern_ca_counties.dbf +0 -0
  30. dclimate_client_py-0.1.1/tests/etc/central_northern_ca_counties.geojson +28 -0
  31. dclimate_client_py-0.1.1/tests/etc/central_northern_ca_counties.prj +1 -0
  32. dclimate_client_py-0.1.1/tests/etc/central_northern_ca_counties.shp +0 -0
  33. dclimate_client_py-0.1.1/tests/etc/central_northern_ca_counties.shx +0 -0
  34. dclimate_client_py-0.1.1/tests/etc/forecast_retrieval_test.zip +0 -0
  35. dclimate_client_py-0.1.1/tests/etc/northern_ca_counties.geojson +55 -0
  36. dclimate_client_py-0.1.1/tests/etc/northern_ca_points.geojson +60 -0
  37. dclimate_client_py-0.1.1/tests/etc/retrieval_test.zip +0 -0
  38. dclimate_client_py-0.1.1/tests/etc/sample_zarrs/bafyreibh56lc4n5mqaj25m7q5dkcwda7qt5hyygd7vu32idupejaoan2li.zip +0 -0
  39. dclimate_client_py-0.1.1/tests/etc/sample_zarrs/bafyreicbu5gbtj5kgyzf2qmsvqhlog7fj5yrnksjqmkqmcinn2yrcsaynq.zip +0 -0
  40. dclimate_client_py-0.1.1/tests/etc/sample_zarrs/bafyreiccejigv2gura4mqitpnrfg2moarvjx4jrcgkompqeuzpejhtc4ra.zip +0 -0
  41. dclimate_client_py-0.1.1/tests/etc/sample_zarrs/bafyreiglm3xvfcwkjbdqlwg3mc6zgngxuyfj6tkgfb6qobtmlzobpp63sq.zip +0 -0
  42. dclimate_client_py-0.1.1/tests/etc/sample_zarrs/bafyreiheqhx2wbdx5p3mbxeawlif7g72ixfgwoonuy3bhghsodc45xwjbi.zip +0 -0
  43. dclimate_client_py-0.1.1/tests/etc/sample_zarrs/gfs_temp_max_test.zip +0 -0
  44. dclimate_client_py-0.1.1/tests/etc/stac_metadata/bafyreibtdfcfyyineq7pv2xunl4sxq6w6ziibswflmelaiydgbqwjk2sku.json +77 -0
  45. dclimate_client_py-0.1.1/tests/etc/stac_metadata/bafyreidduui6mg33udlulg3kyc4nlujbdf7tqztj35twq7bm3xj4izibdq.json +77 -0
  46. dclimate_client_py-0.1.1/tests/etc/stac_metadata/bafyreieker5cwplgsz3wdoeuwhsbcoqajri5dz5jrzxayw5qzaafd3z5ay.json +66 -0
  47. dclimate_client_py-0.1.1/tests/etc/stac_metadata/bafyreifihizlrcxvxtwj7k3dfy6efy67g7sjdvgspxorzni3xthrlfxle4.json +77 -0
  48. dclimate_client_py-0.1.1/tests/test_client.py +369 -0
  49. dclimate_client_py-0.1.1/tests/test_dataset_catalog.py +670 -0
  50. dclimate_client_py-0.1.1/tests/test_geotemporal_data.py +235 -0
  51. dclimate_client_py-0.1.1/tests/test_s3_retrieval.py +106 -0
  52. dclimate_client_py-0.1.1/tests/test_zarr_encryption_ipfs.py +204 -0
  53. dclimate_client_py-0.1.1/tests/test_zarr_metadata.py +320 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 dClimate
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,183 @@
1
+ Metadata-Version: 2.1
2
+ Name: dclimate-client-py
3
+ Version: 0.1.1
4
+ Summary: Python client library for accessing dClimate weather and climate data
5
+ Keywords: climate,weather,data,dclimate,meteorology,climate-data
6
+ Author-Email: Phil Van Meppelen Scheppink <phil@dclimate.net>, Evan Schechter <evan@arbol.io>, Chris Rossi <chris@christophermrossi.com>, Robert Banick <robert.banick@arbol.io>
7
+ License: MIT
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
15
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
16
+ Project-URL: Homepage, https://dclimate.net/
17
+ Requires-Python: >=3.12
18
+ Requires-Dist: aiobotocore
19
+ Requires-Dist: xarray>=2025.3.0
20
+ Requires-Dist: rioxarray
21
+ Requires-Dist: zarr>=3.0.8
22
+ Requires-Dist: numpy>=2.1.3
23
+ Requires-Dist: py_hamt>=3.2.0
24
+ Requires-Dist: requests
25
+ Requires-Dist: pyarrow
26
+ Requires-Dist: geopandas
27
+ Requires-Dist: pandas
28
+ Requires-Dist: s3fs
29
+ Requires-Dist: shapely
30
+ Requires-Dist: scipy
31
+ Requires-Dist: pycryptodome>=3.21.0
32
+ Requires-Dist: pytest-asyncio>=1.3.0
33
+ Provides-Extra: testing
34
+ Requires-Dist: pytest; extra == "testing"
35
+ Requires-Dist: pytest-cov; extra == "testing"
36
+ Requires-Dist: pytest-mock; extra == "testing"
37
+ Provides-Extra: dev
38
+ Requires-Dist: pre-commit>=4.1.0; extra == "dev"
39
+ Requires-Dist: ruff>=0.9.5; extra == "dev"
40
+ Description-Content-Type: text/markdown
41
+
42
+ <p align="center">
43
+ <a href="https://dclimate.net/" target="_blank" rel="noopener noreferrer">
44
+ <img width="50%" src="https://user-images.githubusercontent.com/41392423/173133333-79ef15d0-6671-4be3-ac97-457344e9e958.svg" alt="dClimate logo">
45
+ </a>
46
+ </p>
47
+
48
+ # dClimate-Client-Py
49
+ [![codecov](https://codecov.io/gh/dClimate/dClimate-Zarr-Client/graph/badge.svg?token=AovaMO6DX5)](https://codecov.io/gh/dClimate/dClimate-Zarr-Client)
50
+
51
+ Retrieve dClimate GIS zarr datasets stored on IPFS
52
+
53
+ Uses [py-hamt](https://github.com/dClimate/py-hamt) to access Zarr data structures stored efficiently on IPFS.
54
+
55
+ Filtering and aggregation are packaged into convenience functions optimized for flexibility and performance.
56
+
57
+ > **Looking for JavaScript?** Check out our [JavaScript client](https://www.npmjs.com/package/@dclimate/dclimate-client-js) for Node.js and browser environments.
58
+
59
+ ## Usage
60
+
61
+ ```python
62
+ from datetime import datetime
63
+ import dclimate_client_py as client
64
+ from dclimate_client_py import dClimateClient
65
+
66
+ # --- Recommended: Using dClimateClient (async context manager) ---
67
+
68
+ async def main():
69
+ # The client manages IPFS connections automatically
70
+ # No need to import or configure KuboCAS directly!
71
+ async with dClimateClient() as dclimate:
72
+ # Load datasets by name from the internal catalog
73
+ # For datasets with multiple variants, you must specify which variant
74
+ dataset = await dclimate.load_dataset(
75
+ dataset="2m_temperature",
76
+ collection="era5",
77
+ variant="finalized", # Required for multi-variant datasets
78
+ return_xarray=False # Returns GeotemporalData wrapper (default)
79
+ )
80
+
81
+ # Apply queries using the GeotemporalData interface
82
+ dataset_filtered = dataset.point(latitude=40.875, longitude=-104.875)
83
+ dataset_filtered = dataset_filtered.time_range(
84
+ datetime(2023, 1, 1),
85
+ datetime(2023, 1, 5)
86
+ )
87
+ data_dict = dataset_filtered.as_dict()
88
+ print(data_dict['data'])
89
+
90
+ # Custom IPFS endpoints (optional)
91
+ async def main_custom_ipfs():
92
+ async with dClimateClient(
93
+ gateway_base_url="https://ipfs.io",
94
+ rpc_base_url="http://localhost:5001"
95
+ ) as dclimate:
96
+ dataset = await dclimate.load_dataset(
97
+ dataset="2m_temperature",
98
+ collection="era5",
99
+ variant="finalized"
100
+ )
101
+ # Query dataset...
102
+
103
+ # Get raw xarray.Dataset directly
104
+ async def main_xarray():
105
+ async with dClimateClient() as dclimate:
106
+ xr_dataset = await dclimate.load_dataset(
107
+ dataset="2m_temperature",
108
+ collection="era5",
109
+ variant="finalized",
110
+ return_xarray=True # Returns xarray.Dataset
111
+ )
112
+ print(xr_dataset)
113
+
114
+ # List available datasets in the catalog (synchronous helper)
115
+ catalog = client.list_dataset_catalog()
116
+ for collection in catalog:
117
+ print(f"Collection: {collection['collection']}")
118
+ for dataset in collection['datasets']:
119
+ print(f" Dataset: {dataset['dataset']}")
120
+ for variant in dataset['variants']:
121
+ print(f" Variant: {variant['variant']}")
122
+
123
+ ```
124
+
125
+ > More examples can be found at [dClimate Jupyter Notebooks](https://github.com/dClimate/jupyter-notebooks/tree/main/notebooks). To run your own IPFS gateway follow the instructions for [installing ipfs](https://docs.ipfs.tech/install/command-line/#install-official-binary-distributions). For additional assistance find us on [Discord](https://discord.com/invite/bYWVdNDMpe ), if you are an organization or business reach out to us at community at dclimate dot net.
126
+
127
+ ## Create and activate a virtual environment:
128
+
129
+ ``` shell
130
+ uv venv .venv
131
+ source .venv/bin/activate # macOS/Linux
132
+ .\.venv\Scripts\activate # Windows
133
+ ```
134
+
135
+ ## Install Dependencies
136
+
137
+ ```shell
138
+ uv sync --extra dev --extra testing
139
+ ```
140
+
141
+ ## Run tests for your local environment
142
+ ```shell
143
+ uv run pytest tests/
144
+ ```
145
+
146
+ ## Use Coverage
147
+
148
+ ```shell
149
+ uv run pytest --cov=dclimate_client_py tests/ --cov-report=xml
150
+ ```
151
+
152
+ ## Environment requirements
153
+
154
+ - Optionally you can run your own IPFS Server to host your own datasets or connect to others.
155
+
156
+
157
+ ## File breakdown:
158
+
159
+ ### client.py
160
+
161
+ Entrypoint to code, contains `geo_temporal_query`, which combines all possible subsetting
162
+ and aggregation logic in a single function. Can output the data as either a `dict`
163
+ or `bytes` representing an `xarray` dataset.
164
+
165
+ ---
166
+
167
+ ### dclimate_zarr_errors.py
168
+
169
+ Various exceptions to be raised for bad or invalid user input.
170
+
171
+ ---
172
+
173
+ ### geo_utils.py
174
+
175
+ Functions to manipulate `xarray` datasets. Contains polygon, rectangle, circle and point spatial
176
+ subsetting options, as well as temporal subsetting. Also allows for both spatial and temporal
177
+ aggregations.
178
+
179
+ ---
180
+
181
+ ### ipfs_retrieval.py
182
+
183
+ Functions for resolving IPNS names, traversing the dClimate STAC catalog stored on IPFS, and loading Zarr datasets using `py-hamt`. Handles interaction with IPFS gateways and RPC endpoints.
@@ -0,0 +1,142 @@
1
+ <p align="center">
2
+ <a href="https://dclimate.net/" target="_blank" rel="noopener noreferrer">
3
+ <img width="50%" src="https://user-images.githubusercontent.com/41392423/173133333-79ef15d0-6671-4be3-ac97-457344e9e958.svg" alt="dClimate logo">
4
+ </a>
5
+ </p>
6
+
7
+ # dClimate-Client-Py
8
+ [![codecov](https://codecov.io/gh/dClimate/dClimate-Zarr-Client/graph/badge.svg?token=AovaMO6DX5)](https://codecov.io/gh/dClimate/dClimate-Zarr-Client)
9
+
10
+ Retrieve dClimate GIS zarr datasets stored on IPFS
11
+
12
+ Uses [py-hamt](https://github.com/dClimate/py-hamt) to access Zarr data structures stored efficiently on IPFS.
13
+
14
+ Filtering and aggregation are packaged into convenience functions optimized for flexibility and performance.
15
+
16
+ > **Looking for JavaScript?** Check out our [JavaScript client](https://www.npmjs.com/package/@dclimate/dclimate-client-js) for Node.js and browser environments.
17
+
18
+ ## Usage
19
+
20
+ ```python
21
+ from datetime import datetime
22
+ import dclimate_client_py as client
23
+ from dclimate_client_py import dClimateClient
24
+
25
+ # --- Recommended: Using dClimateClient (async context manager) ---
26
+
27
+ async def main():
28
+ # The client manages IPFS connections automatically
29
+ # No need to import or configure KuboCAS directly!
30
+ async with dClimateClient() as dclimate:
31
+ # Load datasets by name from the internal catalog
32
+ # For datasets with multiple variants, you must specify which variant
33
+ dataset = await dclimate.load_dataset(
34
+ dataset="2m_temperature",
35
+ collection="era5",
36
+ variant="finalized", # Required for multi-variant datasets
37
+ return_xarray=False # Returns GeotemporalData wrapper (default)
38
+ )
39
+
40
+ # Apply queries using the GeotemporalData interface
41
+ dataset_filtered = dataset.point(latitude=40.875, longitude=-104.875)
42
+ dataset_filtered = dataset_filtered.time_range(
43
+ datetime(2023, 1, 1),
44
+ datetime(2023, 1, 5)
45
+ )
46
+ data_dict = dataset_filtered.as_dict()
47
+ print(data_dict['data'])
48
+
49
+ # Custom IPFS endpoints (optional)
50
+ async def main_custom_ipfs():
51
+ async with dClimateClient(
52
+ gateway_base_url="https://ipfs.io",
53
+ rpc_base_url="http://localhost:5001"
54
+ ) as dclimate:
55
+ dataset = await dclimate.load_dataset(
56
+ dataset="2m_temperature",
57
+ collection="era5",
58
+ variant="finalized"
59
+ )
60
+ # Query dataset...
61
+
62
+ # Get raw xarray.Dataset directly
63
+ async def main_xarray():
64
+ async with dClimateClient() as dclimate:
65
+ xr_dataset = await dclimate.load_dataset(
66
+ dataset="2m_temperature",
67
+ collection="era5",
68
+ variant="finalized",
69
+ return_xarray=True # Returns xarray.Dataset
70
+ )
71
+ print(xr_dataset)
72
+
73
+ # List available datasets in the catalog (synchronous helper)
74
+ catalog = client.list_dataset_catalog()
75
+ for collection in catalog:
76
+ print(f"Collection: {collection['collection']}")
77
+ for dataset in collection['datasets']:
78
+ print(f" Dataset: {dataset['dataset']}")
79
+ for variant in dataset['variants']:
80
+ print(f" Variant: {variant['variant']}")
81
+
82
+ ```
83
+
84
+ > More examples can be found at [dClimate Jupyter Notebooks](https://github.com/dClimate/jupyter-notebooks/tree/main/notebooks). To run your own IPFS gateway follow the instructions for [installing ipfs](https://docs.ipfs.tech/install/command-line/#install-official-binary-distributions). For additional assistance find us on [Discord](https://discord.com/invite/bYWVdNDMpe ), if you are an organization or business reach out to us at community at dclimate dot net.
85
+
86
+ ## Create and activate a virtual environment:
87
+
88
+ ``` shell
89
+ uv venv .venv
90
+ source .venv/bin/activate # macOS/Linux
91
+ .\.venv\Scripts\activate # Windows
92
+ ```
93
+
94
+ ## Install Dependencies
95
+
96
+ ```shell
97
+ uv sync --extra dev --extra testing
98
+ ```
99
+
100
+ ## Run tests for your local environment
101
+ ```shell
102
+ uv run pytest tests/
103
+ ```
104
+
105
+ ## Use Coverage
106
+
107
+ ```shell
108
+ uv run pytest --cov=dclimate_client_py tests/ --cov-report=xml
109
+ ```
110
+
111
+ ## Environment requirements
112
+
113
+ - Optionally you can run your own IPFS Server to host your own datasets or connect to others.
114
+
115
+
116
+ ## File breakdown:
117
+
118
+ ### client.py
119
+
120
+ Entrypoint to code, contains `geo_temporal_query`, which combines all possible subsetting
121
+ and aggregation logic in a single function. Can output the data as either a `dict`
122
+ or `bytes` representing an `xarray` dataset.
123
+
124
+ ---
125
+
126
+ ### dclimate_zarr_errors.py
127
+
128
+ Various exceptions to be raised for bad or invalid user input.
129
+
130
+ ---
131
+
132
+ ### geo_utils.py
133
+
134
+ Functions to manipulate `xarray` datasets. Contains polygon, rectangle, circle and point spatial
135
+ subsetting options, as well as temporal subsetting. Also allows for both spatial and temporal
136
+ aggregations.
137
+
138
+ ---
139
+
140
+ ### ipfs_retrieval.py
141
+
142
+ Functions for resolving IPNS names, traversing the dClimate STAC catalog stored on IPFS, and loading Zarr datasets using `py-hamt`. Handles interaction with IPFS gateways and RPC endpoints.
@@ -0,0 +1,2 @@
1
+ # Created by pytest automatically.
2
+ *
@@ -0,0 +1,4 @@
1
+ Signature: 8a477f597d28d172789f06886806bc55
2
+ # This file is a cache directory tag created by pytest.
3
+ # For information about cache directory tags, see:
4
+ # https://bford.info/cachedir/spec.html
@@ -0,0 +1,8 @@
1
+ # pytest cache directory #
2
+
3
+ This directory contains data from the pytest's cache plugin,
4
+ which provides the `--lf` and `--ff` options, as well as the `cache` fixture.
5
+
6
+ **Do not** commit this to version control.
7
+
8
+ See [the docs](https://docs.pytest.org/en/stable/how-to/cache.html) for more information.
@@ -0,0 +1,32 @@
1
+ # public API
2
+ from .client import (
3
+ load_s3,
4
+ geo_temporal_query,
5
+ )
6
+ from .dclimate_client import dClimateClient
7
+ from .geotemporal_data import GeotemporalData
8
+ from .encryption_codec import (
9
+ EncryptionCodec,
10
+ )
11
+ from .datasets import (
12
+ list_dataset_catalog,
13
+ fetch_cid_from_url,
14
+ DatasetCatalog,
15
+ CatalogCollection,
16
+ CatalogDataset,
17
+ DatasetVariantConfig,
18
+ )
19
+
20
+ __all__ = [
21
+ "dClimateClient",
22
+ "load_s3",
23
+ "geo_temporal_query",
24
+ "list_dataset_catalog",
25
+ "fetch_cid_from_url",
26
+ "GeotemporalData",
27
+ "EncryptionCodec",
28
+ "DatasetCatalog",
29
+ "CatalogCollection",
30
+ "CatalogDataset",
31
+ "DatasetVariantConfig",
32
+ ]
@@ -0,0 +1 @@
1
+ {"cpc-precip-conus": "bafyr4iff6vuvpkb4zexkx7exoafsjvfnno4ofidyc37gsto2aaxixg5zia", "cpc-precip-global": "bafyr4ihe35o55qz47y2dbau6ikvfkbrthqn57pdhem5jdxhvqf5u7qlrbm", "chirps-final-p05": "bafyr4ic2n63fkxdvwgh2uzealcrbeubncivcqaptttfrr4vp5ecbe5e65a"}
@@ -0,0 +1,203 @@
1
+ """
2
+ Functions that will map to endpoints in the flask app
3
+ """
4
+
5
+ import datetime
6
+ import typing
7
+ import xarray as xr
8
+
9
+ from .dclimate_zarr_errors import (
10
+ ConflictingGeoRequestError,
11
+ ConflictingAggregationRequestError,
12
+ InvalidExportFormatError,
13
+ InvalidSelectionError,
14
+ )
15
+ from .geotemporal_data import GeotemporalData, DEFAULT_POINT_LIMIT
16
+ from .s3_retrieval import get_dataset_from_s3
17
+ from .ipfs_retrieval import (
18
+ _get_dataset_by_ipfs_cid,
19
+ )
20
+ from .datasets import (
21
+ resolve_dataset_source,
22
+ get_concatenable_variants,
23
+ find_dataset_by_name,
24
+ find_collection_by_name,
25
+ fetch_cid_from_url,
26
+ DATASET_CATALOG_INTERNAL,
27
+ DatasetCatalog,
28
+ )
29
+ from .concatenate import concatenate_datasets
30
+
31
+ def load_s3(
32
+ dataset_name: str,
33
+ bucket_name: str,
34
+ ) -> GeotemporalData:
35
+ """
36
+ Load a Geotemporal dataset from an S3 bucket.
37
+
38
+ Parameters
39
+ ----------
40
+
41
+ dataset_name: str
42
+ The name of the dataset in the bucket.
43
+ bucket_name: str
44
+ S3 bucket name where the dataset is going to be fetched
45
+ """
46
+ ds = get_dataset_from_s3(dataset_name, bucket_name)
47
+ return GeotemporalData(ds, dataset_name=dataset_name)
48
+
49
+
50
+ def geo_temporal_query(
51
+ dataset_name: str,
52
+ source: typing.Literal["s3"] = "s3",
53
+ bucket_name: str = None,
54
+ var_name: str = None,
55
+ gateway_uri_stem: str | None = None,
56
+ rpc_uri_stem: str | None = None,
57
+ forecast_reference_time: str = None,
58
+ point_kwargs: dict = None,
59
+ circle_kwargs: dict = None,
60
+ rectangle_kwargs: dict = None,
61
+ polygon_kwargs: dict = None,
62
+ multiple_points_kwargs: dict = None,
63
+ spatial_agg_kwargs: dict = None,
64
+ temporal_agg_kwargs: dict = None,
65
+ rolling_agg_kwargs: dict = None,
66
+ time_range: typing.Optional[typing.List[datetime.datetime]] = None,
67
+ # as_of: typing.Optional[datetime.datetime] = None, # Removed as_of
68
+ point_limit: int = DEFAULT_POINT_LIMIT,
69
+ output_format: str = "array",
70
+ ) -> typing.Union[dict, bytes]:
71
+ """Filter an XArray dataset
72
+
73
+ Filter an XArray dataset by specified spatial and/or temporal bounds and aggregate
74
+ according to spatial and/or temporal logic, if desired. Before aggregating check
75
+ that the filtered data fits within specified point and area maximums to avoid
76
+ computationally expensive retrieval and processing operations. When bounds or
77
+ aggregation logic are not provided, pass the dataset along untouched.
78
+
79
+ Return either a numpy array of data values or a NetCDF file.
80
+
81
+ Only one of point, circle, rectangle, or polygon kwargs may be provided. Only one of
82
+ temporal or rolling aggregation kwargs may be provided, although they can be chained
83
+ with spatial aggregations if desired.
84
+
85
+ Args:
86
+ dataset_name (str): Name used to identify the dataset within the STAC catalog (for IPFS)
87
+ or the dataset name in the bucket (for S3).
88
+ source: (typing.Literal["ipfs", "s3"]): how to pull data. Defaults to "ipfs".
89
+ bucket_name (str): S3 bucket name where the datasets are going to be fetched (required if source="s3").
90
+ var_name (str, optional): Specific data variable to use within the dataset.
91
+ gateway_uri_stem (str | None, optional): Custom IPFS HTTP Gateway URI stem for IPFS source.
92
+ rpc_uri_stem (str | None, optional): Custom IPFS RPC API URI stem for IPFS source.
93
+ forecast_reference_time (str): Isoformatted string representing the desire date
94
+ to return all available forecasts for
95
+ circle_kwargs (dict, optional): a dictionary of parameters relevant to a
96
+ circular query
97
+ rectangle_kwargs (dict, optional): a dictionary of parameters relevant to a
98
+ rectangular query
99
+ polygon_kwargs (dict, optional): a dictionary of parameters relevant to a
100
+ polygonal query
101
+ multiple_points_kwargs (dict, optional): Parameters for querying multiple specific points.
102
+ point_kwargs (dict, optional): Parameters for querying a single point.
103
+ spatial_agg_kwargs (dict, optional): a dictionary of parameters relevant to a
104
+ spatial aggregation operation
105
+ temporal_agg_kwargs (dict, optional): a dictionary of parameters relevant to a
106
+ temporal aggregation operation
107
+ rolling_agg_kwargs (dict, optional): a dictionary of parameters relevant to a
108
+ rolling aggregation operation
109
+ time_range (typing.Optional[typing.List[datetime.datetime]], optional):
110
+ time range in which to subset data.
111
+ Defaults to None.
112
+ # REMOVED as_of (typing.Optional[datetime.datetime], optional):
113
+ # pull in most recent data created before this time. If None, just get most
114
+ # recent. Defaults to None.
115
+ point_limit (int, optional): maximum number of data points user can request.
116
+ Defaults to DEFAULT_POINT_LIMIT.
117
+ output_format (str, optional): Current supported formats are `array` and
118
+ `netcdf`. Defaults to "array", which provides a dict of data
119
+ values and coordinates.
120
+
121
+ Returns:
122
+ typing.Union[dict, bytes]: Output data as dict (default) or NetCDF bytes.
123
+ """
124
+ # Check for incompatible request parameters
125
+ if (
126
+ len(
127
+ [
128
+ kwarg_dict
129
+ for kwarg_dict in [
130
+ circle_kwargs,
131
+ rectangle_kwargs,
132
+ polygon_kwargs,
133
+ multiple_points_kwargs,
134
+ point_kwargs,
135
+ ]
136
+ if kwarg_dict is not None
137
+ ]
138
+ )
139
+ > 1
140
+ ):
141
+ raise ConflictingGeoRequestError(
142
+ "User requested more than one type of geographic query, but only one can "
143
+ "be submitted at a time"
144
+ )
145
+ if spatial_agg_kwargs and point_kwargs:
146
+ raise ConflictingGeoRequestError(
147
+ "User requested spatial aggregation methods on a single point, "
148
+ "but these are mutually exclusive parameters. Only one may be requested at "
149
+ "a time."
150
+ )
151
+ if temporal_agg_kwargs and rolling_agg_kwargs:
152
+ raise ConflictingAggregationRequestError(
153
+ "User requested both rolling and temporal aggregation, but these are "
154
+ "mutually exclusive operations. Only one may be requested at a time."
155
+ )
156
+ if output_format not in ["array", "netcdf"]:
157
+ raise InvalidExportFormatError(
158
+ "User requested an invalid export format. Only 'array' or 'netcdf' "
159
+ "permitted."
160
+ )
161
+
162
+ # Set defaults to avoid Nones accidentally passed by users causing a TypeError
163
+ if not point_limit:
164
+ point_limit = DEFAULT_POINT_LIMIT
165
+
166
+ # Load the dataset based on the source
167
+ if source == "s3":
168
+ if not bucket_name:
169
+ raise ValueError("bucket_name is required when source is 's3'")
170
+ data = load_s3(dataset_name, bucket_name)
171
+ else:
172
+ raise ValueError(
173
+ "Invalid source specified. Must be 's3'. "
174
+ "IPFS source is deprecated - use dClimateClient instead."
175
+ )
176
+
177
+ # If specific variable is requested, use that
178
+ if var_name is not None:
179
+ data = data.use(var_name)
180
+
181
+ # Filter data down temporally, then spatially, and check that the size of resulting
182
+ # dataset fits within the limit. While a user can get the entire DS by providing no
183
+ # filters, this will almost certainly cause the size checks to fail
184
+
185
+ data = data.query(
186
+ forecast_reference_time=forecast_reference_time,
187
+ point_kwargs=point_kwargs,
188
+ circle_kwargs=circle_kwargs,
189
+ rectangle_kwargs=rectangle_kwargs,
190
+ polygon_kwargs=polygon_kwargs,
191
+ multiple_points_kwargs=multiple_points_kwargs,
192
+ spatial_agg_kwargs=spatial_agg_kwargs,
193
+ temporal_agg_kwargs=temporal_agg_kwargs,
194
+ rolling_agg_kwargs=rolling_agg_kwargs,
195
+ time_range=time_range,
196
+ point_limit=point_limit,
197
+ )
198
+
199
+ # Export
200
+ if output_format == "netcdf":
201
+ return data.to_netcdf()
202
+ else: # "array"
203
+ return data.as_dict()