From bb0fb1b3a03072e13b4d96022827ee407f13c7eb Mon Sep 17 00:00:00 2001 From: Rosie Lickorish Date: Tue, 10 Feb 2026 11:51:14 +0000 Subject: [PATCH 01/18] Add cdsapi dependency --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 280d9d4..08c3383 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,8 +17,9 @@ authors = [ ] dependencies = [ "aiohttp>=3.13.3", - "contextily>=1.6.2", "boto3", + "contextily>=1.6.2", + "cdsapi>=0.7.7", "dask==2025.7.0", "folium>=0.20.0", "h5netcdf>=1.7.3", From 5bc42ddb01adb4b460bd4c2c892bfbbe67537754 Mon Sep 17 00:00:00 2001 From: Rosie Lickorish Date: Tue, 10 Feb 2026 14:15:21 +0000 Subject: [PATCH 02/18] Update docs and API Key instructions for CDS --- .env_template | 2 ++ .secrets.baseline | 30 ++++++++++++++++++++++-------- README.md | 15 ++++++++++++--- docs/download_data.md | 15 ++++++++++++--- 4 files changed, 48 insertions(+), 14 deletions(-) diff --git a/.env_template b/.env_template index 565453a..5d02b25 100644 --- a/.env_template +++ b/.env_template @@ -1,3 +1,5 @@ SH_CLIENT_ID="" SH_CLIENT_SECRET="" NASA_EARTH_BEARER_TOKEN="" +CDSAPI_URL="https://cds.climate.copernicus.eu/api" +CDSAPI_KEY="" \ No newline at end of file diff --git a/.secrets.baseline b/.secrets.baseline index 0b3ec5c..ec4039d 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "^.secrets.baseline$", "lines": null }, - "generated_at": "2025-11-19T10:10:18Z", + "generated_at": "2026-02-10T12:52:37Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -85,24 +85,31 @@ "type": "Secret Keyword", "verified_result": null }, + { + "hashed_secret": "365a32402f2062038d01718da7de099f94555775", + "is_verified": false, + "line_number": 98, + "type": "Secret Keyword", + "verified_result": null + }, { "hashed_secret": "d3270f852a922a83e8e2dabb8535a68464ec5165", "is_verified": false, - "line_number": 113, + "line_number": 121, "type": "Secret Keyword", "verified_result": null }, { "hashed_secret": "4e1e76c221c6dace8f9a2c943e00d3328366366a", "is_verified": false, - "line_number": 122, + "line_number": 130, "type": "Secret Keyword", "verified_result": null }, { "hashed_secret": "54a38e82cf07a4f36900b855ca7dd0584ca9f3ff", "is_verified": false, - "line_number": 124, + "line_number": 132, "type": "Secret Keyword", "verified_result": null } @@ -111,28 +118,35 @@ { "hashed_secret": "5204df45fc8c724684bbc61cd4107a726a6b9204", "is_verified": false, - "line_number": 175, + "line_number": 176, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "365a32402f2062038d01718da7de099f94555775", + "is_verified": false, + "line_number": 178, "type": "Secret Keyword", "verified_result": null }, { "hashed_secret": "d401c389d24671398ced85b259e17975a4fcb65e", "is_verified": false, - "line_number": 192, + "line_number": 201, "type": "Secret Keyword", "verified_result": null }, { "hashed_secret": "4e1e76c221c6dace8f9a2c943e00d3328366366a", "is_verified": false, - "line_number": 201, + "line_number": 210, "type": "Secret Keyword", "verified_result": null }, { "hashed_secret": "54a38e82cf07a4f36900b855ca7dd0584ca9f3ff", "is_verified": false, - "line_number": 203, + "line_number": 212, "type": "Secret Keyword", "verified_result": null } diff --git a/README.md b/README.md index 06b28da..73252b1 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ The following data connectors and associated collections are available: | sentinelhub | s2_l1c, dem, s1_grd, hls_l30, s2_l2a, hls_s30 | | nasa_earthdata | HLSL30_2.0, HLSS30_2.0 | | sentinel_aws | sentinel-2-l2a | +| climate_data_store| derived-era5-single-levels-daily-statistics | | IBMResearchSTAC | ukcp18-land-cpm-uk-2.2km, ch4, sentinel-5p-l3grd-ch4-wfmd | | TheWeatherCompany | weathercompany-daily-forecast | @@ -94,19 +95,27 @@ Each data connector has a different access requirements. For connecting to Senti SH_CLIENT_ID="" SH_CLIENT_SECRET="" NASA_EARTH_BEARER_TOKEN="" +CDSAPI_KEY="" ``` ### NASA Earthdata -To access NASA Earthdata, register for an Earthdata Login profile and requests a bearer token. https://urs.earthdata.nasa.gov/profile +To access NASA Earthdata, register for an Earthdata Login profile and requests a bearer token. [https://urs.earthdata.nasa.gov/profile](https://urs.earthdata.nasa.gov/profile) ### Sentinel Hub -To access sentinel hub, register for an account and requests an OAuth client using the Sentinel Hub dashboard https://www.planet.com +To access sentinel hub, register for an account and requests an OAuth client using the Sentinel Hub dashboard [https://www.planet.com](https://www.planet.com) ### Sentinel AWS Access sentinel AWS data is open and does not require any credentials. +### Climate Data Store +Create an account at [https://cds.climate.copernicus.eu/](https://cds.climate.copernicus.eu/). Once created, find your API key under the `Profile` section and add to your `.env` file. Each dataset may also require accepting the licence agreement. If this is the case, the first time a request is made, an error will be returned with the url to visit to accept the terms. + +Available collections include: +- [ERA5 post-processed daily statistics on single levels from 1940 to present](https://cds.climate.copernicus.eu/datasets/derived-era5-single-levels-daily-statistics?tab=overview) +- [CORDEX regional climate model data on single levels](https://cds.climate.copernicus.eu/datasets/projections-cordex-domains-single-levels?tab=overview) + ### The Weather Company -To access The Weather Company, register for an account and requests an API Key https://www.weathercompany.com/weather-data-apis/. Once you have an API key, set the following environment variable: +To access The Weather Company, register for an account and requests an API Key [https://www.weathercompany.com/weather-data-apis/](https://www.weathercompany.com/weather-data-apis/). Once you have an API key, set the following environment variable: ``` THE_WEATHER_COMPANY_API_KEY="<>" diff --git a/docs/download_data.md b/docs/download_data.md index 80b1560..9b59cab 100644 --- a/docs/download_data.md +++ b/docs/download_data.md @@ -164,6 +164,7 @@ The following data connectors and associated collections are available: | sentinelhub | s2_l1c, dem, s1_grd, hls_l30, s2_l2a, hls_s30 | | nasa_earthdata | HLSL30_2.0, HLSS30_2.0 | | sentinel_aws | sentinel-2-l2a | +| climate_data_store| derived-era5-single-levels-daily-statistics | | IBMResearchSTAC | 'HLSS30', 'esa-sentinel-2A-msil1c', 'HLS_S30',, 'atmospheric-weather-era5', 'deforestation-umd', 'Radar-10min', 'tasmax-rcp85-land-cpm-uk-2.2km', 'vector-osm-power', 'ukcp18-land-cpm-uk-2.2km', 'treecovermaps-eudr', 'ch4' + more| | TheWeatherCompany | weathercompany-daily-forecast | @@ -174,19 +175,27 @@ Each data connector has a different access requirements. For example, connecting SH_CLIENT_ID="" SH_CLIENT_SECRET="" NASA_EARTH_BEARER_TOKEN="" +CDSAPI_KEY="" ``` ### NASA Earthdata -To access NASA Earthdata, register for an Earthdata Login profile and requests a bearer token. https://urs.earthdata.nasa.gov/profile +To access NASA Earthdata, register for an Earthdata Login profile and requests a bearer token. [https://urs.earthdata.nasa.gov/profile](https://urs.earthdata.nasa.gov/profile) ### Sentinel Hub -To access sentinel hub, register for an account and requests an OAuth client using the Sentinel Hub dashboard https://www.planet.com +To access sentinel hub, register for an account and requests an OAuth client using the Sentinel Hub dashboard [https://www.planet.com](https://www.planet.com) ### Sentinel AWS Access sentinel AWS data is open and does not require any credentials. +### Climate Data Store +Create an account at [https://cds.climate.copernicus.eu/](https://cds.climate.copernicus.eu/). Once created, find your API key under the `Profile` section. Each dataset may also require accepting the licence agreement. If this is the case, the first time a request is made, an error will be returned with the url to visit to accept the terms. + +Available collections include: +- [ERA5 post-processed daily statistics on single levels from 1940 to present](https://cds.climate.copernicus.eu/datasets/derived-era5-single-levels-daily-statistics?tab=overview) +- [CORDEX regional climate model data on single levels](https://cds.climate.copernicus.eu/datasets/projections-cordex-domains-single-levels?tab=overview) + ### The Weather Company -To access The Weather Company, register for an account and requests an API Key https://www.weathercompany.com/weather-data-apis/. Once you have an API key, set the following environment variable: +To access The Weather Company, register for an account and requests an API Key [https://www.weathercompany.com/weather-data-apis/](https://www.weathercompany.com/weather-data-apis/). Once you have an API key, set the following environment variable: ``` THE_WEATHER_COMPANY_API_KEY="" From f481bb5d1a9500a98440e4b247faa75aa77f640e Mon Sep 17 00:00:00 2001 From: Rosie Lickorish Date: Tue, 10 Feb 2026 14:24:23 +0000 Subject: [PATCH 03/18] Update lock file --- requirements.txt | 3 +++ uv.lock | 49 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a0ce660..18e0161 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,6 +12,7 @@ attrs==25.4.0 boto3==1.42.25 botocore==1.42.25 branca==0.8.2 +cdsapi==0.7.7 certifi==2026.1.4 charset-normalizer==3.4.4 click==8.3.1 @@ -25,6 +26,7 @@ contourpy==1.3.3 ; python_full_version >= '3.11' cycler==0.12.1 dask==2025.7.0 dataclasses-json==0.6.7 +ecmwf-datastores-client==0.4.2 exceptiongroup==1.3.1 ; python_full_version < '3.11' filelock==3.20.3 folium==0.20.0 @@ -57,6 +59,7 @@ marshmallow==3.26.2 matplotlib==3.10.8 mercantile==1.2.1 multidict==6.7.0 +multiurl==0.3.7 mypy-extensions==1.1.0 nest-asyncio==1.6.0 numpy==2.2.6 ; python_full_version < '3.11' diff --git a/uv.lock b/uv.lock index f9a9723..6bfa2b8 100644 --- a/uv.lock +++ b/uv.lock @@ -12,6 +12,7 @@ resolution-markers = [ name = "aenum" version = "3.1.16" source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/09/7a/61ed58e8be9e30c3fe518899cc78c284896d246d51381bab59b5db11e1f3/aenum-3.1.16.tar.gz", hash = "sha256:bfaf9589bdb418ee3a986d85750c7318d9d2839c1b1a1d6fe8fc53ec201cf140", size = 137693, upload-time = "2026-01-12T22:34:38.819Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/e3/52/6ad8f63ec8da1bf40f96996d25d5b650fdd38f5975f8c813732c47388f18/aenum-3.1.16-py3-none-any.whl", hash = "sha256:9035092855a98e41b66e3d0998bd7b96280e85ceb3a04cc035636138a1943eaf", size = 165627, upload-time = "2025-04-25T03:17:58.89Z" }, ] @@ -392,6 +393,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7e/50/fc9680058e63161f2f63165b84c957a0df1415431104c408e8104a3a18ef/branca-0.8.2-py3-none-any.whl", hash = "sha256:2ebaef3983e3312733c1ae2b793b0a8ba3e1c4edeb7598e10328505280cf2f7c", size = 26193, upload-time = "2025-10-06T10:28:19.255Z" }, ] +[[package]] +name = "cdsapi" +version = "0.7.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ecmwf-datastores-client" }, + { name = "requests" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/f3/6cb5b4bf077c441978c5d5be3a568d37e1f07f3e7177a17fa66aec2594b6/cdsapi-0.7.7.tar.gz", hash = "sha256:bc0cf807c1b78aceba6a11c3a5180f885f47f71a4e58205e324cfedcee16f10b", size = 13322, upload-time = "2025-09-30T19:11:22.404Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/f4/4a65460d5cb6784128019fd707a87993f378db25e796eba01400a0903f62/cdsapi-0.7.7-py2.py3-none-any.whl", hash = "sha256:384c1658572d6dc53f4111f6dd46fcdfe6fea54a688af9756d71f6fe9118b66d", size = 12293, upload-time = "2025-09-30T19:11:21.184Z" }, +] + [[package]] name = "certifi" version = "2026.1.4" @@ -1027,6 +1042,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, ] +[[package]] +name = "ecmwf-datastores-client" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "multiurl" }, + { name = "requests" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/51/60/f86eb3e57baf2b1780a7046148c234e9e57b0aeb550d30f39e50991da253/ecmwf_datastores_client-0.4.2.tar.gz", hash = "sha256:7cee1f5e5dab34edcc794cd62bee02c603fafb6f4cc2121c5f012806e0f7934d", size = 48205, upload-time = "2026-01-21T15:27:31.665Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/40/2ccf4c87a5f9c8198fe71600d5f307f5dada201c091af8774a9c1e360865/ecmwf_datastores_client-0.4.2-py3-none-any.whl", hash = "sha256:d22a675b35263286de09969502ec897da9ceb9e4c8ec4d709f7ebb3b90d3ae98", size = 29092, upload-time = "2026-01-21T15:27:30.452Z" }, +] + [[package]] name = "exceptiongroup" version = "1.3.1" @@ -2698,6 +2728,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/da/7d22601b625e241d4f23ef1ebff8acfc60da633c9e7e7922e24d10f592b3/multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3", size = 12317, upload-time = "2025-10-06T14:52:29.272Z" }, ] +[[package]] +name = "multiurl" +version = "0.3.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "requests" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6c/ce/a00c9b44f43c4620ca004e4acb4c1e266b1ab48d2f6ad9e402f6bdbe44d4/multiurl-0.3.7.tar.gz", hash = "sha256:4201563fc8989baca7b525fdc69d4cd5a6c0cef4f303559710b9890021aab6d9", size = 18945, upload-time = "2025-07-29T11:57:04.109Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/cf/be4e93afbfa0def2cd6fac9302071db0bd6d0617999ecbf53f92b9398de3/multiurl-0.3.7-py3-none-any.whl", hash = "sha256:054f42974064f103be0ed55b43f0c32fc435a47dc7353a9adaffa643b99fa380", size = 21524, upload-time = "2025-07-29T11:57:03.191Z" }, +] + [[package]] name = "mypy" version = "1.17.1" @@ -4981,6 +5026,7 @@ source = { editable = "." } dependencies = [ { name = "aiohttp" }, { name = "boto3" }, + { name = "cdsapi" }, { name = "contextily" }, { name = "dask" }, { name = "folium" }, @@ -5037,6 +5083,7 @@ dev = [ requires-dist = [ { name = "aiohttp", specifier = ">=3.13.3" }, { name = "boto3" }, + { name = "cdsapi", specifier = ">=0.7.7" }, { name = "contextily", specifier = ">=1.6.2" }, { name = "dask", specifier = "==2025.7.0" }, { name = "folium", specifier = ">=0.20.0" }, @@ -5061,7 +5108,7 @@ requires-dist = [ { name = "tacoreader", specifier = "==0.5.6" }, { name = "tacotoolbox", specifier = "==0.5.2" }, { name = "tqdm", specifier = "==4.67.1" }, - { name = "urllib3", specifier = "==2.6.3" }, + { name = "urllib3", specifier = ">=2.6.3" }, { name = "xarray", specifier = ">=2023.1.0" }, ] From 3fb50b1bbbb43493bbe426cb45ec114b08e430ef Mon Sep 17 00:00:00 2001 From: Rosie Lickorish Date: Tue, 10 Feb 2026 16:12:26 +0000 Subject: [PATCH 04/18] Update check_header script to accommodate new year --- scripts/check_header.py | 135 +++++++++++++++++++++++++++++++++++----- 1 file changed, 120 insertions(+), 15 deletions(-) diff --git a/scripts/check_header.py b/scripts/check_header.py index 21c6950..81e2d19 100644 --- a/scripts/check_header.py +++ b/scripts/check_header.py @@ -1,32 +1,139 @@ -# © Copyright IBM Corporation 2025 +# © Copyright IBM Corporation 2025-2026 # SPDX-License-Identifier: Apache-2.0 import argparse import os +import re +import subprocess +from datetime import datetime from pathlib import Path -def check_header(file_path: Path, header: str): +def get_current_year(): + """Get the current year.""" + return datetime.now().year + + +def extract_copyright_year(content: str): + """Extract year(s) from existing copyright header.""" + match = re.search(r"© Copyright IBM Corporation (\d{4}(?:-\d{4})?)", content) + return match.group(1) if match else None + + +def is_new_file(file_path: Path): + """Check if file is new (not in git history).""" + try: + result = subprocess.run( + ["git", "log", "--oneline", str(file_path)], + capture_output=True, + text=True, + cwd=file_path.parent, + ) + return len(result.stdout.strip()) == 0 + except Exception: + return True # Assume new if git check fails + + +def get_file_creation_year(file_path: Path): + """Get the year when file was first committed to git.""" + try: + result = subprocess.run( + [ + "git", + "log", + "--follow", + "--format=%ad", + "--date=format:%Y", + "--", + str(file_path), + ], + capture_output=True, + text=True, + cwd=file_path.parent, + ) + years = result.stdout.strip().split("\n") + if years and years[-1]: # Get oldest commit year + return int(years[-1]) + return None + except Exception: + return None + + +def generate_copyright_year(file_path: Path, existing_year: str | None = None): + """Generate appropriate copyright year string.""" + current_year = get_current_year() + + # If no existing year in file, check git history + if existing_year is None: + creation_year = get_file_creation_year(file_path) + if creation_year is None: + # File not in git yet - use current year + return str(current_year) + elif creation_year == current_year: + return str(current_year) + else: + return f"{creation_year}-{current_year}" + + # Parse existing year(s) from file + if "-" in existing_year: + start_year, end_year = existing_year.split("-") + if int(end_year) == current_year: + return existing_year # Already up to date + return f"{start_year}-{current_year}" + else: + if int(existing_year) == current_year: + return existing_year + return f"{existing_year}-{current_year}" + + +def check_header(file_path: Path): """ - Reads a Python file and prepends the HEADER_STRING if it's not present. + Reads a Python file and updates/adds copyright header with appropriate year. Args: file_path: A Path object representing the Python file. """ try: - # Read the entire file, specify encoding for compatibility + # Read the entire file original_content = file_path.read_text(encoding="utf-8") - # Check if the file already starts with the header + # Extract existing copyright year if present + existing_year = extract_copyright_year(original_content) + + # Generate appropriate year string + copyright_year = generate_copyright_year(file_path, existing_year) + + # Create header with appropriate year + header = f"# © Copyright IBM Corporation {copyright_year}\n# SPDX-License-Identifier: Apache-2.0\n\n\n" + + # Check if header needs updating if original_content.startswith(header): return - # If the header is not present, create - print(f"Updating: header in '{file_path}'") + # Remove old header if present + if existing_year: + # Remove old copyright lines + lines = original_content.split("\n") + new_lines = [] + skip_count = 0 + for line in lines: + if skip_count < 4 and ( + line.startswith("# ©") + or line.startswith("# SPDX") + or line.strip() == "#" + or line.strip() == "" + ): + skip_count += 1 + continue + new_lines.append(line) + original_content = "\n".join(new_lines) + + # Add new header + print(f"Updating: header in '{file_path}' with year {copyright_year}") new_content = header + original_content - # Write the new content back to the file + # Write back to file file_path.write_text(new_content, encoding="utf-8") except IOError as e: @@ -39,7 +146,7 @@ def check_header(file_path: Path, header: str): def main(): parser = argparse.ArgumentParser( - description="Prepend a header if it doesn't exist.", + description="Prepend/update copyright header with appropriate year.", formatter_class=argparse.RawTextHelpFormatter, ) group = parser.add_mutually_exclusive_group(required=True) @@ -58,9 +165,7 @@ def main(): ) args = parser.parse_args() - header = "# © Copyright IBM Corporation 2025\n# SPDX-License-Identifier: Apache-2.0\n\n\n" - - # user specified a directory, search and apply headers + # User specified a directory, search and apply headers if args.files is None: target_directory = Path(args.directory) @@ -77,9 +182,9 @@ def main(): exit(1) for file_path in files: - check_header(file_path, header) + check_header(file_path) - # user specified a list of files, loop over these and apply headers + # User specified a list of files, loop over these and apply headers else: for file_arg in args.files: file_path = Path(file_arg) @@ -88,7 +193,7 @@ def main(): exit(1) if str(file_arg).endswith(".py"): - check_header(file_path, header) + check_header(file_path) if __name__ == "__main__": From c71e6bb6fd9ca1a8836c0f9353e7a293f864c4f6 Mon Sep 17 00:00:00 2001 From: Rosie Lickorish Date: Tue, 10 Feb 2026 16:22:34 +0000 Subject: [PATCH 05/18] Add climate_data_store connector type --- terrakit/__init__.py | 3 +- .../data_connectors/climate_data_store.py | 151 ++++++++++++++++++ terrakit/terrakit.py | 9 +- terrakit/validate/data_connector.py | 3 +- .../test_climate_data_store.py | 46 ++++++ 5 files changed, 207 insertions(+), 5 deletions(-) create mode 100644 terrakit/download/data_connectors/climate_data_store.py create mode 100644 tests/component_tests/download/data_connectors/test_climate_data_store.py diff --git a/terrakit/__init__.py b/terrakit/__init__.py index c9af368..a8a3741 100644 --- a/terrakit/__init__.py +++ b/terrakit/__init__.py @@ -1,4 +1,4 @@ -# © Copyright IBM Corporation 2025 +# © Copyright IBM Corporation 2025-2026 # SPDX-License-Identifier: Apache-2.0 @@ -23,6 +23,7 @@ from .download.data_connectors.sentinelhub import SentinelHub # noqa from .download.data_connectors.nasa_earthdata import NASA_EarthData # noqa from .download.data_connectors.sentinel_aws import Sentinel_AWS # noqa +from .download.data_connectors.climate_data_store import CDS # noqa from .terrakit import DataConnector # noqa from .download.download_data import download_data # noqa from .chip import tiling # noqa diff --git a/terrakit/download/data_connectors/climate_data_store.py b/terrakit/download/data_connectors/climate_data_store.py new file mode 100644 index 0000000..3ee0dff --- /dev/null +++ b/terrakit/download/data_connectors/climate_data_store.py @@ -0,0 +1,151 @@ +# © Copyright IBM Corporation 2026 +# SPDX-License-Identifier: Apache-2.0 + + +import logging +import xarray as xr +from typing import Any, Union + +from ..connector import Connector +from ..geodata_utils import ( + load_and_list_collections, +) + +logger = logging.getLogger(__name__) + +###################################################################################################### +### Supporting functions +###################################################################################################### + + +###################################################################################################### +### Connector class +###################################################################################################### + + +class CDS(Connector): + """ + Attributes: + connector_type (str): Name of connector + collections (list): A list of available collections. + collections_details (list): Detailed information about the collections. + """ + + def __init__(self): + """ + Initialize CDS with collections and configuration. + """ + self.connector_type: str = "climate_data_store" + self.collections: list[Any] = load_and_list_collections( + connector_type=self.connector_type + ) + self.collections_details: list[Any] = load_and_list_collections( + as_json=True, connector_type=self.connector_type + ) + + def list_collections(self) -> list[Any]: + """ + Lists the available collections. + + Returns: + list: A list of collection names. + """ + logger.info("Listing available collections") + return self.collections + + def find_data( + self, + data_collection_name: str, + date_start: str, + date_end: str, + area_polygon=None, + bbox=None, + bands=[], + maxcc=100, + data_connector_spec=None, + ) -> Union[tuple[list[Any], list[dict[str, Any]]], tuple[None, None]]: + """ + This function retrieves unique dates and corresponding data results from a specified Sentinel Hub data collection. + + Args: + data_collection_name (str): The name of the Sentinel Hub data collection to search. + date_start (str): The start date for the time interval in 'YYYY-MM-DD' format. + date_end (str): The end date for the time interval in 'YYYY-MM-DD' format. + area_polygon (Polygon, optional): A polygon defining the area of interest. + bbox (tuple, optional): A bounding box defining the area of interest in the format (minx, miny, maxx, maxy). + bands (list, optional): A list of bands to retrieve. Defaults to []. + maxcc (int, optional): The maximum cloud cover percentage for the data. Default is 100 (no cloud cover filter). + data_connector_spec (list, optional): A dictionary containing the data connector specification. + + Returns: + tuple: A tuple containing a sorted list of unique dates and a list of data results. + """ + unique_dates: list[str] = [] + results: list[dict[str, Any]] = [{}] + return unique_dates, results + + def get_data( + self, + data_collection_name, + date_start, + date_end, + area_polygon=None, + bbox=None, + bands=[], + query_params={}, + data_connector_spec=None, + save_file=None, + working_dir=".", + ): + """ + Fetches data from SentinelHub for the specified collection, date range, area, and bands. + + Args: + data_collection_name (str): Name of the data collection to fetch data from. + date_start (str): Start date for the data retrieval (inclusive), in 'YYYY-MM-DD' format. + date_end (str): End date for the data retrieval (inclusive), in 'YYYY-MM-DD' format. + area_polygon (list, optional): Polygon defining the area of interest. Defaults to None. + bbox (list, optional): Bounding box defining the area of interest. Defaults to None. + bands (list, optional): List of bands to retrieve. Defaults to all bands. + maxcc (int, optional): Maximum cloud cover threshold (0-100). Defaults to 100. + data_connector_spec (dict, optional): Data connector specification. Defaults to None. + save_file (str, optional): Path to save the output file. Defaults to None. + working_dir (str, optional): Working directory for temporary files. Defaults to '.'. + + Returns: + xarray: An xarray Datasets containing the fetched data with dimensions (time, band, y, x). + """ + # da = xr.DataArray() + + # First construct the query + # request = { + # "variable": [ + # "10m_u_component_of_wind", + # "10m_v_component_of_wind", + # "2m_temperature", + # "total_precipitation", + # "10m_wind_gust_since_previous_post_processing", + # ], + # "product_type": "reanalysis", + # "year": "2025", + # "month": ["01"], + # "day": ["01", "02", "03", "04"], + # "time_zone": "utc+00:00", + # "area": [90, -180, -90, 180], + # "daily_statistic": "daily_mean", + # "frequency": "6_hourly", + # "format": "netcdf", + # } + + # # + # cds = cdsapi.Client() + # downloaded_filename = cds.retrieve(data_collection_name, request).download() + + da = xr.DataArray() + return da + + +#### If licenses have not been accepted. +# HTTPError: 403 Client Error: Forbidden for url: https://cds.climate.copernicus.eu/api/retrieve/v1/processes/derived-era5-single-levels-daily-statistics/execution +# required licences not accepted +# Not all the required licences have been accepted; please visit https://cds.climate.copernicus.eu/datasets/derived-era5-single-levels-daily-statistics?tab=download#manage-licences to accept the required licence(s). diff --git a/terrakit/terrakit.py b/terrakit/terrakit.py index 5a5fd6a..2b9022f 100644 --- a/terrakit/terrakit.py +++ b/terrakit/terrakit.py @@ -1,4 +1,4 @@ -# © Copyright IBM Corporation 2025 +# © Copyright IBM Corporation 2025-2026 # SPDX-License-Identifier: Apache-2.0 @@ -7,13 +7,13 @@ from pydantic import ValidationError -from terrakit.download.data_connectors.theweathercompany import TheWeatherCompany - from .download.connector import Connector from .download.data_connectors.nasa_earthdata import NASA_EarthData from .download.data_connectors.sentinel_aws import Sentinel_AWS from .download.data_connectors.ibmresearch_stac import IBMResearchSTAC from .download.data_connectors.sentinelhub import SentinelHub +from .download.data_connectors.climate_data_store import CDS +from .download.data_connectors.theweathercompany import TheWeatherCompany from .general_utils.exceptions import TerrakitValidationError from .validate.data_connector import ConnectorType @@ -37,6 +37,7 @@ def get_connector(connector_type: ConnectorType) -> Connector: - "sentinelhub" - "nasa_earthdata" - "sentinel_aws" + - "climate_data_store" Returns: object: An instance of the specified data connector class. @@ -54,6 +55,8 @@ def get_connector(connector_type: ConnectorType) -> Connector: return IBMResearchSTAC() elif connector_type.connector_type == "TheWeatherCompany": return TheWeatherCompany() + elif connector_type.connector_type == "climate_data_store": + return CDS() # -----> Include new connectors here < ------ # elif connector_type == "" # return NewConnectorClass() diff --git a/terrakit/validate/data_connector.py b/terrakit/validate/data_connector.py index 71d4254..f5f4ae8 100644 --- a/terrakit/validate/data_connector.py +++ b/terrakit/validate/data_connector.py @@ -1,4 +1,4 @@ -# © Copyright IBM Corporation 2025 +# © Copyright IBM Corporation 2025-2026 # SPDX-License-Identifier: Apache-2.0 @@ -27,5 +27,6 @@ class ConnectorType(BaseModel): "sentinel_aws", "IBMResearchSTAC", "TheWeatherCompany", + "climate_data_store", ] """The type of connector to be use to download data. nasa_earthdata, sentinelhub, sentinel_aws, IBMResearchSTAC or TheWeatherCompany""" diff --git a/tests/component_tests/download/data_connectors/test_climate_data_store.py b/tests/component_tests/download/data_connectors/test_climate_data_store.py new file mode 100644 index 0000000..38495f9 --- /dev/null +++ b/tests/component_tests/download/data_connectors/test_climate_data_store.py @@ -0,0 +1,46 @@ +# © Copyright IBM Corporation 2026 +# SPDX-License-Identifier: Apache-2.0 + + +import pytest +from terrakit import DataConnector +from terrakit.general_utils.exceptions import ( + TerrakitValidationError, +) + + +class TestClimateDataStore: + connector_type = "climate_data_store" + + @pytest.mark.skip() + def test_valid_data_connector(self): + dc = DataConnector(connector_type=self.connector_type) + assert dc.connector is not None + + @pytest.mark.skip() + def test_list_collections_sentinel_aws( + self, + **kwargs, + ): + expected_collections = ["sentinel-2-l2a"] + dc = DataConnector(connector_type=self.connector_type) + collections = dc.connector.list_collections() + assert collections == expected_collections + + @pytest.mark.skip() + def test__missing_credentials( + self, + unset_evn_vars, + connector_type, + collection, + start_date, + bbox, + reset_dot_env, + ): + """ + Test that find_data only runs if credentials are provided. + """ + + with pytest.raises(TerrakitValidationError, match="Error: Missing credentials"): + dc = DataConnector(connector_type=connector_type) + dc.connector.find_data(collection, start_date, start_date, bbox=bbox) From 842e3163ec274fce9654cc7c8a3397165c0485f7 Mon Sep 17 00:00:00 2001 From: Rosie Lickorish Date: Tue, 10 Feb 2026 19:54:01 +0000 Subject: [PATCH 06/18] Add projections-cordex-domains-single-levels to list_collections --- README.md | 2 +- terrakit/download/collections.json | 1147 +++++++++++++++++ .../test_climate_data_store.py | 9 +- 3 files changed, 1153 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 73252b1..de000db 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ The following data connectors and associated collections are available: | sentinelhub | s2_l1c, dem, s1_grd, hls_l30, s2_l2a, hls_s30 | | nasa_earthdata | HLSL30_2.0, HLSS30_2.0 | | sentinel_aws | sentinel-2-l2a | -| climate_data_store| derived-era5-single-levels-daily-statistics | +| climate_data_store| derived-era5-single-levels-daily-statistics, projections-cordex-domains-single-levels| | IBMResearchSTAC | ukcp18-land-cpm-uk-2.2km, ch4, sentinel-5p-l3grd-ch4-wfmd | | TheWeatherCompany | weathercompany-daily-forecast | diff --git a/terrakit/download/collections.json b/terrakit/download/collections.json index b88e55a..4a13129 100644 --- a/terrakit/download/collections.json +++ b/terrakit/download/collections.json @@ -1066,5 +1066,1152 @@ "description": "The Daily Forecast API is sourced from The Weather Company (TWC) Forecast system and provides weather forecasts starting from the current day. The number of forecast days returned in the API response is determined by your content licensing agreement with TWC and is restricted by the API key issued to your company", "url": "https://developer.weather.com/forecastsandinsights/docs/daily-forecast-v-3-0-1", "id": "24738c68-96d6-4000-899e-c3cb201312fc" + }, + { + "connector": "climate_data_store", + "collection_name": "projections-cordex-domains-single-levels" + }, + { + "connector": "climate_data_store", + "modality_tag": "", + "resolution_m": 10, + "collection_name": "derived-era5-single-levels-daily-statistics", + "collection_long_name": "ERA5 post-processed daily statistics on single levels from 1940 to present", + "description": "", + "url": "https://cds.climate.copernicus.eu/datasets/derived-era5-single-levels-daily-statistics?tab=overview", + "id": "", + "query_options": { + "product_type": { + "title": "Product type", + "schema": { + "enum": ["reanalysis", "ensemble_mean", "ensemble_members"], + "type": "string", + "default": "reanalysis" + } + }, + "variable": { + "title": "Variable", + "schema": { + "type": "array", + "items": { + "enum": ["100m_u_component_of_wind", "100m_v_component_of_wind", "10m_u_component_of_neutral_wind", "10m_u_component_of_wind", "10m_v_component_of_neutral_wind", "10m_v_component_of_wind", "10m_wind_gust_since_previous_post_processing", "2m_dewpoint_temperature", "2m_temperature", "air_density_over_the_oceans", "angle_of_sub_gridscale_orography", "anisotropy_of_sub_gridscale_orography", "benjamin_feir_index", "boundary_layer_dissipation", "boundary_layer_height", "charnock", "clear_sky_direct_solar_radiation_at_surface", "cloud_base_height", "coefficient_of_drag_with_waves", "convective_available_potential_energy", "convective_inhibition", "convective_precipitation", "convective_rain_rate", "convective_snowfall", "convective_snowfall_rate_water_equivalent", "downward_uv_radiation_at_the_surface", "duct_base_height", "eastward_gravity_wave_surface_stress", "eastward_turbulent_surface_stress", "evaporation", "forecast_albedo", "forecast_logarithm_of_surface_roughness_for_heat", "forecast_surface_roughness", "free_convective_velocity_over_the_oceans", "friction_velocity", "geopotential", "gravity_wave_dissipation", "high_cloud_cover", "high_vegetation_cover", "ice_temperature_layer_1", "ice_temperature_layer_2", "ice_temperature_layer_3", "ice_temperature_layer_4", "instantaneous_10m_wind_gust", "instantaneous_eastward_turbulent_surface_stress", "instantaneous_large_scale_surface_precipitation_fraction", "instantaneous_moisture_flux", "instantaneous_northward_turbulent_surface_stress", "instantaneous_surface_sensible_heat_flux", "k_index", "lake_bottom_temperature", "lake_cover", "lake_depth", "lake_ice_depth", "lake_ice_temperature", "lake_mix_layer_depth", "lake_mix_layer_temperature", "lake_shape_factor", "lake_total_layer_temperature", "land_sea_mask", "large_scale_precipitation", "large_scale_precipitation_fraction", "large_scale_rain_rate", "large_scale_snowfall", "large_scale_snowfall_rate_water_equivalent", "leaf_area_index_high_vegetation", "leaf_area_index_low_vegetation", "low_cloud_cover", "low_vegetation_cover", "maximum_2m_temperature_since_previous_post_processing", "maximum_individual_wave_height", "maximum_total_precipitation_rate_since_previous_post_processing", "mean_boundary_layer_dissipation", "mean_convective_precipitation_rate", "mean_convective_snowfall_rate", "mean_direction_of_total_swell", "mean_direction_of_wind_waves", "mean_eastward_gravity_wave_surface_stress", "mean_eastward_turbulent_surface_stress", "mean_evaporation_rate", "mean_gravity_wave_dissipation", "mean_large_scale_precipitation_fraction", "mean_large_scale_precipitation_rate", "mean_large_scale_snowfall_rate", "mean_northward_gravity_wave_surface_stress", "mean_northward_turbulent_surface_stress", "mean_period_of_total_swell", "mean_period_of_wind_waves", "mean_potential_evaporation_rate", "mean_runoff_rate", "mean_sea_level_pressure", "mean_snow_evaporation_rate", "mean_snowfall_rate", "mean_snowmelt_rate", "mean_square_slope_of_waves", "mean_sub_surface_runoff_rate", "mean_surface_direct_short_wave_radiation_flux", "mean_surface_direct_short_wave_radiation_flux_clear_sky", "mean_surface_downward_long_wave_radiation_flux", "mean_surface_downward_long_wave_radiation_flux_clear_sky", "mean_surface_downward_short_wave_radiation_flux", "mean_surface_downward_short_wave_radiation_flux_clear_sky", "mean_surface_downward_uv_radiation_flux", "mean_surface_latent_heat_flux", "mean_surface_net_long_wave_radiation_flux", "mean_surface_net_long_wave_radiation_flux_clear_sky", "mean_surface_net_short_wave_radiation_flux", "mean_surface_net_short_wave_radiation_flux_clear_sky", "mean_surface_runoff_rate", "mean_surface_sensible_heat_flux", "mean_top_downward_short_wave_radiation_flux", "mean_top_net_long_wave_radiation_flux", "mean_top_net_long_wave_radiation_flux_clear_sky", "mean_top_net_short_wave_radiation_flux", "mean_top_net_short_wave_radiation_flux_clear_sky", "mean_total_precipitation_rate", "mean_vertical_gradient_of_refractivity_inside_trapping_layer", "mean_vertically_integrated_moisture_divergence", "mean_wave_direction", "mean_wave_direction_of_first_swell_partition", "mean_wave_direction_of_second_swell_partition", "mean_wave_direction_of_third_swell_partition", "mean_wave_period", "mean_wave_period_based_on_first_moment", "mean_wave_period_based_on_first_moment_for_swell", "mean_wave_period_based_on_first_moment_for_wind_waves", "mean_wave_period_based_on_second_moment_for_swell", "mean_wave_period_based_on_second_moment_for_wind_waves", "mean_wave_period_of_first_swell_partition", "mean_wave_period_of_second_swell_partition", "mean_wave_period_of_third_swell_partition", "mean_zero_crossing_wave_period", "medium_cloud_cover", "minimum_2m_temperature_since_previous_post_processing", "minimum_total_precipitation_rate_since_previous_post_processing", "minimum_vertical_gradient_of_refractivity_inside_trapping_layer", "model_bathymetry", "near_ir_albedo_for_diffuse_radiation", "near_ir_albedo_for_direct_radiation", "normalized_energy_flux_into_ocean", "normalized_energy_flux_into_waves", "normalized_stress_into_ocean", "northward_gravity_wave_surface_stress", "northward_turbulent_surface_stress", "ocean_surface_stress_equivalent_10m_neutral_wind_direction", "ocean_surface_stress_equivalent_10m_neutral_wind_speed", "peak_wave_period", "period_corresponding_to_maximum_individual_wave_height", "potential_evaporation", "precipitation_type", "runoff", "sea_ice_cover", "sea_surface_temperature", "significant_height_of_combined_wind_waves_and_swell", "significant_height_of_total_swell", "significant_height_of_wind_waves", "significant_wave_height_of_first_swell_partition", "significant_wave_height_of_second_swell_partition", "significant_wave_height_of_third_swell_partition", "skin_reservoir_content", "skin_temperature", "slope_of_sub_gridscale_orography", "snow_albedo", "snow_density", "snow_depth", "snow_evaporation", "snowfall", "snowmelt", "soil_temperature_level_1", "soil_temperature_level_2", "soil_temperature_level_3", "soil_temperature_level_4", "soil_type", "standard_deviation_of_filtered_subgrid_orography", "standard_deviation_of_orography", "sub_surface_runoff", "surface_latent_heat_flux", "surface_net_solar_radiation", "surface_net_solar_radiation_clear_sky", "surface_net_thermal_radiation", "surface_net_thermal_radiation_clear_sky", "surface_pressure", "surface_runoff", "surface_sensible_heat_flux", "surface_solar_radiation_downward_clear_sky", "surface_solar_radiation_downwards", "surface_thermal_radiation_downward_clear_sky", "surface_thermal_radiation_downwards", "temperature_of_snow_layer", "toa_incident_solar_radiation", "top_net_solar_radiation", "top_net_solar_radiation_clear_sky", "top_net_thermal_radiation", "top_net_thermal_radiation_clear_sky", "total_cloud_cover", "total_column_cloud_ice_water", "total_column_cloud_liquid_water", "total_column_ozone", "total_column_rain_water", "total_column_snow_water", "total_column_supercooled_liquid_water", "total_column_water", "total_column_water_vapour", "total_precipitation", "total_sky_direct_solar_radiation_at_surface", "total_totals_index", "trapping_layer_base_height", "trapping_layer_top_height", "type_of_high_vegetation", "type_of_low_vegetation", "u_component_stokes_drift", "uv_visible_albedo_for_diffuse_radiation", "uv_visible_albedo_for_direct_radiation", "v_component_stokes_drift", "vertical_integral_of_divergence_of_cloud_frozen_water_flux", "vertical_integral_of_divergence_of_cloud_liquid_water_flux", "vertical_integral_of_divergence_of_geopotential_flux", "vertical_integral_of_divergence_of_kinetic_energy_flux", "vertical_integral_of_divergence_of_mass_flux", "vertical_integral_of_divergence_of_moisture_flux", "vertical_integral_of_divergence_of_ozone_flux", "vertical_integral_of_divergence_of_thermal_energy_flux", "vertical_integral_of_divergence_of_total_energy_flux", "vertical_integral_of_eastward_cloud_frozen_water_flux", "vertical_integral_of_eastward_cloud_liquid_water_flux", "vertical_integral_of_eastward_geopotential_flux", "vertical_integral_of_eastward_heat_flux", "vertical_integral_of_eastward_kinetic_energy_flux", "vertical_integral_of_eastward_mass_flux", "vertical_integral_of_eastward_ozone_flux", "vertical_integral_of_eastward_total_energy_flux", "vertical_integral_of_eastward_water_vapour_flux", "vertical_integral_of_energy_conversion", "vertical_integral_of_kinetic_energy", "vertical_integral_of_mass_of_atmosphere", "vertical_integral_of_mass_tendency", "vertical_integral_of_northward_cloud_frozen_water_flux", "vertical_integral_of_northward_cloud_liquid_water_flux", "vertical_integral_of_northward_geopotential_flux", "vertical_integral_of_northward_heat_flux", "vertical_integral_of_northward_kinetic_energy_flux", "vertical_integral_of_northward_mass_flux", "vertical_integral_of_northward_ozone_flux", "vertical_integral_of_northward_total_energy_flux", "vertical_integral_of_northward_water_vapour_flux", "vertical_integral_of_potential_and_internal_energy", "vertical_integral_of_potential_internal_and_latent_energy", "vertical_integral_of_temperature", "vertical_integral_of_thermal_energy", "vertical_integral_of_total_energy", "vertically_integrated_moisture_divergence", "volumetric_soil_water_layer_1", "volumetric_soil_water_layer_2", "volumetric_soil_water_layer_3", "volumetric_soil_water_layer_4", "wave_spectral_directional_width", "wave_spectral_directional_width_for_swell", "wave_spectral_directional_width_for_wind_waves", "wave_spectral_kurtosis", "wave_spectral_peakedness", "wave_spectral_skewness", "zero_degree_level"], + "type": "string" + } + } + }, + "year": { + "title": "Year", + "schema": { + "enum": ["1940", "1941", "1942", "1943", "1944", "1945", "1946", "1947", "1948", "1949", "1950", "1951", "1952", "1953", "1954", "1955", "1956", "1957", "1958", "1959", "1960", "1961", "1962", "1963", "1964", "1965", "1966", "1967", "1968", "1969", "1970", "1971", "1972", "1973", "1974", "1975", "1976", "1977", "1978", "1979", "1980", "1981", "1982", "1983", "1984", "1985", "1986", "1987", "1988", "1989", "1990", "1991", "1992", "1993", "1994", "1995", "1996", "1997", "1998", "1999", "2000", "2001", "2002", "2003", "2004", "2005", "2006", "2007", "2008", "2009", "2010", "2011", "2012", "2013", "2014", "2015", "2016", "2017", "2018", "2019", "2020", "2021", "2022", "2023", "2024", "2025", "2026"], + "type": "string" + } + }, + "month": { + "title": "Month", + "schema": { + "type": "array", + "items": { + "enum": ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"], + "type": "string" + } + } + }, + "day": { + "title": "Day", + "schema": { + "type": "array", + "items": { + "enum": ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31"], + "type": "string" + } + } + }, + "daily_statistic": { + "title": "Daily statistic", + "schema": { + "enum": ["daily_sum", "daily_mean", "daily_maximum", "daily_minimum"], + "type": "string", + "default": "daily_mean" + } + }, + "time_zone": { + "title": "Time zone", + "schema": { + "enum": ["utc+00:00", "utc+01:00", "utc+02:00", "utc+03:00", "utc+04:00", "utc+05:00", "utc+06:00", "utc+07:00", "utc+08:00", "utc+09:00", "utc+10:00", "utc+11:00", "utc+12:00", "utc+13:00", "utc+14:00", "utc-01:00", "utc-02:00", "utc-03:00", "utc-04:00", "utc-05:00", "utc-06:00", "utc-07:00", "utc-08:00", "utc-09:00", "utc-10:00", "utc-11:00", "utc-12:00"], + "type": "string", + "default": "utc+00:00" + } + }, + "frequency": { + "title": "Frequency", + "schema": { + "enum": ["1_hourly", "3_hourly", "6_hourly"], + "type": "string" + } + }, + "area": { + "title": "Sub-region extraction", + "schema": { + "maxItems": 4, + "minItems": 4, + "type": "array", + "default": [90, -180, -90, 180], + "items": { + "type": "number" + } + } + } + }, + "bands": [ + {"band_name": "100m_u_component_of_wind", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "100m_v_component_of_wind", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "10m_u_component_of_neutral_wind", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "10m_u_component_of_wind", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "10m_v_component_of_neutral_wind", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "10m_v_component_of_wind", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "10m_wind_gust_since_previous_post_processing", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "2m_dewpoint_temperature", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "2m_temperature", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "air_density_over_the_oceans", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "angle_of_sub_gridscale_orography", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "anisotropy_of_sub_gridscale_orography", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "benjamin_feir_index", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "boundary_layer_dissipation", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "boundary_layer_height", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "charnock", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "clear_sky_direct_solar_radiation_at_surface", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "cloud_base_height", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "coefficient_of_drag_with_waves", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "convective_available_potential_energy", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "convective_inhibition", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "convective_precipitation", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "convective_rain_rate", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "convective_snowfall", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "convective_snowfall_rate_water_equivalent", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "downward_uv_radiation_at_the_surface", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "duct_base_height", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "eastward_gravity_wave_surface_stress", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "eastward_turbulent_surface_stress", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "evaporation", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "forecast_albedo", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "forecast_logarithm_of_surface_roughness_for_heat", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "forecast_surface_roughness", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "free_convective_velocity_over_the_oceans", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "friction_velocity", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "geopotential", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "gravity_wave_dissipation", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "high_cloud_cover", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "high_vegetation_cover", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "ice_temperature_layer_1", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "ice_temperature_layer_2", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "ice_temperature_layer_3", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "ice_temperature_layer_4", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "instantaneous_10m_wind_gust", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "instantaneous_eastward_turbulent_surface_stress", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "instantaneous_large_scale_surface_precipitation_fraction", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "instantaneous_moisture_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "instantaneous_northward_turbulent_surface_stress", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "instantaneous_surface_sensible_heat_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "k_index", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "lake_bottom_temperature", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "lake_cover", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "lake_depth", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "lake_ice_depth", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "lake_ice_temperature", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "lake_mix_layer_depth", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "lake_mix_layer_temperature", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "lake_shape_factor", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "lake_total_layer_temperature", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "land_sea_mask", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "large_scale_precipitation", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "large_scale_precipitation_fraction", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "large_scale_rain_rate", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "large_scale_snowfall", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "large_scale_snowfall_rate_water_equivalent", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "leaf_area_index_high_vegetation", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "leaf_area_index_low_vegetation", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "low_cloud_cover", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "low_vegetation_cover", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "maximum_2m_temperature_since_previous_post_processing", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "maximum_individual_wave_height", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "maximum_total_precipitation_rate_since_previous_post_processing", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_boundary_layer_dissipation", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_convective_precipitation_rate", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_convective_snowfall_rate", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_direction_of_total_swell", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_direction_of_wind_waves", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_eastward_gravity_wave_surface_stress", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_eastward_turbulent_surface_stress", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_evaporation_rate", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_gravity_wave_dissipation", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_large_scale_precipitation_fraction", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_large_scale_precipitation_rate", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_large_scale_snowfall_rate", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_northward_gravity_wave_surface_stress", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_northward_turbulent_surface_stress", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_period_of_total_swell", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_period_of_wind_waves", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_potential_evaporation_rate", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_runoff_rate", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_sea_level_pressure", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_snow_evaporation_rate", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_snowfall_rate", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_snowmelt_rate", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_square_slope_of_waves", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_sub_surface_runoff_rate", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_surface_direct_short_wave_radiation_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_surface_direct_short_wave_radiation_flux_clear_sky", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_surface_downward_long_wave_radiation_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_surface_downward_long_wave_radiation_flux_clear_sky", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_surface_downward_short_wave_radiation_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_surface_downward_short_wave_radiation_flux_clear_sky", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_surface_downward_uv_radiation_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_surface_latent_heat_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_surface_net_long_wave_radiation_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_surface_net_long_wave_radiation_flux_clear_sky", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_surface_net_short_wave_radiation_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_surface_net_short_wave_radiation_flux_clear_sky", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_surface_runoff_rate", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_surface_sensible_heat_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_top_downward_short_wave_radiation_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_top_net_long_wave_radiation_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_top_net_long_wave_radiation_flux_clear_sky", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_top_net_short_wave_radiation_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_top_net_short_wave_radiation_flux_clear_sky", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_total_precipitation_rate", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_vertical_gradient_of_refractivity_inside_trapping_layer", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_vertically_integrated_moisture_divergence", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_wave_direction", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_wave_direction_of_first_swell_partition", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_wave_direction_of_second_swell_partition", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_wave_direction_of_third_swell_partition", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_wave_period", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_wave_period_based_on_first_moment", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_wave_period_based_on_first_moment_for_swell", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_wave_period_based_on_first_moment_for_wind_waves", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_wave_period_based_on_second_moment_for_swell", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_wave_period_based_on_second_moment_for_wind_waves", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_wave_period_of_first_swell_partition", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_wave_period_of_second_swell_partition", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_wave_period_of_third_swell_partition", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "mean_zero_crossing_wave_period", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "medium_cloud_cover", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "minimum_2m_temperature_since_previous_post_processing", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "minimum_total_precipitation_rate_since_previous_post_processing", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "minimum_vertical_gradient_of_refractivity_inside_trapping_layer", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "model_bathymetry", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "near_ir_albedo_for_diffuse_radiation", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "near_ir_albedo_for_direct_radiation", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "normalized_energy_flux_into_ocean", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "normalized_energy_flux_into_waves", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "normalized_stress_into_ocean", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "northward_gravity_wave_surface_stress", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "northward_turbulent_surface_stress", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "ocean_surface_stress_equivalent_10m_neutral_wind_direction", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "ocean_surface_stress_equivalent_10m_neutral_wind_speed", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "peak_wave_period", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "period_corresponding_to_maximum_individual_wave_height", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "potential_evaporation", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "precipitation_type", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "runoff", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "sea_ice_cover", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "sea_surface_temperature", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "significant_height_of_combined_wind_waves_and_swell", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "significant_height_of_total_swell", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "significant_height_of_wind_waves", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "significant_wave_height_of_first_swell_partition", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "significant_wave_height_of_second_swell_partition", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "significant_wave_height_of_third_swell_partition", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "skin_reservoir_content", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "skin_temperature", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "slope_of_sub_gridscale_orography", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "snow_albedo", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "snow_density", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "snow_depth", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "snow_evaporation", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "snowfall", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "snowmelt", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "soil_temperature_level_1", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "soil_temperature_level_2", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "soil_temperature_level_3", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "soil_temperature_level_4", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "soil_type", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "standard_deviation_of_filtered_subgrid_orography", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "standard_deviation_of_orography", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "sub_surface_runoff", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "surface_latent_heat_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "surface_net_solar_radiation", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "surface_net_solar_radiation_clear_sky", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "surface_net_thermal_radiation", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "surface_net_thermal_radiation_clear_sky", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "surface_pressure", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "surface_runoff", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "surface_sensible_heat_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "surface_solar_radiation_downward_clear_sky", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "surface_solar_radiation_downwards", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "surface_thermal_radiation_downward_clear_sky", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "surface_thermal_radiation_downwards", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "temperature_of_snow_layer", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "toa_incident_solar_radiation", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "top_net_solar_radiation", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "top_net_solar_radiation_clear_sky", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "top_net_thermal_radiation", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "top_net_thermal_radiation_clear_sky", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "total_cloud_cover", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "total_column_cloud_ice_water", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "total_column_cloud_liquid_water", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "total_column_ozone", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "total_column_rain_water", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "total_column_snow_water", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "total_column_supercooled_liquid_water", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "total_column_water", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "total_column_water_vapour", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "total_precipitation", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "total_sky_direct_solar_radiation_at_surface", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "total_totals_index", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "trapping_layer_base_height", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "trapping_layer_top_height", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "type_of_high_vegetation", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "type_of_low_vegetation", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "u_component_stokes_drift", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "uv_visible_albedo_for_diffuse_radiation", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "uv_visible_albedo_for_direct_radiation", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "v_component_stokes_drift", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_divergence_of_cloud_frozen_water_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_divergence_of_cloud_liquid_water_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_divergence_of_geopotential_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_divergence_of_kinetic_energy_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_divergence_of_mass_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_divergence_of_moisture_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_divergence_of_ozone_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_divergence_of_thermal_energy_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_divergence_of_total_energy_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_eastward_cloud_frozen_water_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_eastward_cloud_liquid_water_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_eastward_geopotential_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_eastward_heat_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_eastward_kinetic_energy_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_eastward_mass_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_eastward_ozone_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_eastward_total_energy_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_eastward_water_vapour_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_energy_conversion", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_kinetic_energy", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_mass_of_atmosphere", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_mass_tendency", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_northward_cloud_frozen_water_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_northward_cloud_liquid_water_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_northward_geopotential_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_northward_heat_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_northward_kinetic_energy_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_northward_mass_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_northward_ozone_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_northward_total_energy_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_northward_water_vapour_flux", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_potential_and_internal_energy", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_potential_internal_and_latent_energy", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_temperature", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_thermal_energy", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertical_integral_of_total_energy", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "vertically_integrated_moisture_divergence", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "volumetric_soil_water_layer_1", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "volumetric_soil_water_layer_2", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "volumetric_soil_water_layer_3", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "volumetric_soil_water_layer_4", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "wave_spectral_directional_width", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "wave_spectral_directional_width_for_swell", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "wave_spectral_directional_width_for_wind_waves", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "wave_spectral_kurtosis", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "wave_spectral_peakedness", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "wave_spectral_skewness", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""}, + {"band_name": "zero_degree_level", + "alt_names": [], + "resolution": "0.25 deg", + "description": ""} + ] + } ] \ No newline at end of file diff --git a/tests/component_tests/download/data_connectors/test_climate_data_store.py b/tests/component_tests/download/data_connectors/test_climate_data_store.py index 38495f9..481597c 100644 --- a/tests/component_tests/download/data_connectors/test_climate_data_store.py +++ b/tests/component_tests/download/data_connectors/test_climate_data_store.py @@ -12,17 +12,18 @@ class TestClimateDataStore: connector_type = "climate_data_store" - @pytest.mark.skip() def test_valid_data_connector(self): dc = DataConnector(connector_type=self.connector_type) assert dc.connector is not None - @pytest.mark.skip() - def test_list_collections_sentinel_aws( + def test_list_collections_climate_data_store( self, **kwargs, ): - expected_collections = ["sentinel-2-l2a"] + expected_collections = [ + "projections-cordex-domains-single-levels", + "derived-era5-single-levels-daily-statistics", + ] dc = DataConnector(connector_type=self.connector_type) collections = dc.connector.list_collections() assert collections == expected_collections From 612a5f9e2ecb0d4f685ac4e9a818f40923520ecb Mon Sep 17 00:00:00 2001 From: Rosie Lickorish Date: Tue, 10 Feb 2026 19:59:25 +0000 Subject: [PATCH 07/18] Ensure connector_template has template connect name in docstrings --- terrakit/download/data_connectors/connector_template.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/terrakit/download/data_connectors/connector_template.py b/terrakit/download/data_connectors/connector_template.py index cdc6675..c0b5793 100644 --- a/terrakit/download/data_connectors/connector_template.py +++ b/terrakit/download/data_connectors/connector_template.py @@ -1,4 +1,4 @@ -# © Copyright IBM Corporation 2025 +# © Copyright IBM Corporation 2025-2026 # SPDX-License-Identifier: Apache-2.0 @@ -66,10 +66,10 @@ def find_data( data_connector_spec=None, ) -> Union[tuple[list[Any], list[dict[str, Any]]], tuple[None, None]]: """ - This function retrieves unique dates and corresponding data results from a specified Sentinel Hub data collection. + This function retrieves unique dates and corresponding data results from a specified data collection. Args: - data_collection_name (str): The name of the Sentinel Hub data collection to search. + data_collection_name (str): The name of the data collection to search. date_start (str): The start date for the time interval in 'YYYY-MM-DD' format. date_end (str): The end date for the time interval in 'YYYY-MM-DD' format. area_polygon (Polygon, optional): A polygon defining the area of interest. From 4a71608bb3d047a54a13e403b827eb407d30df3d Mon Sep 17 00:00:00 2001 From: Rosie Lickorish Date: Tue, 10 Feb 2026 20:40:58 +0000 Subject: [PATCH 08/18] Ensure CDS API Key is present --- .env_template | 1 - .secrets.baseline | 4 ++-- .../data_connectors/climate_data_store.py | 17 +++++++++++++---- tests/component_tests/download/conftest.py | 5 ++++- .../data_connectors/test_climate_data_store.py | 7 ++----- 5 files changed, 21 insertions(+), 13 deletions(-) diff --git a/.env_template b/.env_template index 5d02b25..333c780 100644 --- a/.env_template +++ b/.env_template @@ -1,5 +1,4 @@ SH_CLIENT_ID="" SH_CLIENT_SECRET="" NASA_EARTH_BEARER_TOKEN="" -CDSAPI_URL="https://cds.climate.copernicus.eu/api" CDSAPI_KEY="" \ No newline at end of file diff --git a/.secrets.baseline b/.secrets.baseline index ec4039d..7e3da4c 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "^.secrets.baseline$", "lines": null }, - "generated_at": "2026-02-10T12:52:37Z", + "generated_at": "2026-02-10T20:40:41Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -282,7 +282,7 @@ { "hashed_secret": "20adce7ba26cdb1b654e8a53d42ef471580b92ad", "is_verified": false, - "line_number": 69, + "line_number": 73, "type": "Secret Keyword", "verified_result": null } diff --git a/terrakit/download/data_connectors/climate_data_store.py b/terrakit/download/data_connectors/climate_data_store.py index 3ee0dff..0191951 100644 --- a/terrakit/download/data_connectors/climate_data_store.py +++ b/terrakit/download/data_connectors/climate_data_store.py @@ -3,13 +3,16 @@ import logging +import os import xarray as xr + from typing import Any, Union from ..connector import Connector from ..geodata_utils import ( load_and_list_collections, ) +from terrakit.general_utils.exceptions import TerrakitValidationError logger = logging.getLogger(__name__) @@ -17,6 +20,7 @@ ### Supporting functions ###################################################################################################### +CDSAPI_URL = "https://cds.climate.copernicus.eu/api" ###################################################################################################### ### Connector class @@ -33,7 +37,7 @@ class CDS(Connector): def __init__(self): """ - Initialize CDS with collections and configuration. + Initialize climate_data_store with collections and configuration. """ self.connector_type: str = "climate_data_store" self.collections: list[Any] = load_and_list_collections( @@ -65,10 +69,10 @@ def find_data( data_connector_spec=None, ) -> Union[tuple[list[Any], list[dict[str, Any]]], tuple[None, None]]: """ - This function retrieves unique dates and corresponding data results from a specified Sentinel Hub data collection. + This function retrieves unique dates and corresponding data results from a specified Climate Data Store data collection. Args: - data_collection_name (str): The name of the Sentinel Hub data collection to search. + data_collection_name (str): The name of the Climate Data Store data collection to search. date_start (str): The start date for the time interval in 'YYYY-MM-DD' format. date_end (str): The end date for the time interval in 'YYYY-MM-DD' format. area_polygon (Polygon, optional): A polygon defining the area of interest. @@ -80,6 +84,11 @@ def find_data( Returns: tuple: A tuple containing a sorted list of unique dates and a list of data results. """ + if "CDSAPI_KEY" not in os.environ: + raise TerrakitValidationError( + message="Error: Missing credentials 'CDSAPI_KEY'. Please update .env with correct credentials." + ) + unique_dates: list[str] = [] results: list[dict[str, Any]] = [{}] return unique_dates, results @@ -98,7 +107,7 @@ def get_data( working_dir=".", ): """ - Fetches data from SentinelHub for the specified collection, date range, area, and bands. + Fetches data from for the specified collection, date range, area, and bands. Args: data_collection_name (str): Name of the data collection to fetch data from. diff --git a/tests/component_tests/download/conftest.py b/tests/component_tests/download/conftest.py index 4912c44..a632c08 100644 --- a/tests/component_tests/download/conftest.py +++ b/tests/component_tests/download/conftest.py @@ -1,4 +1,4 @@ -# © Copyright IBM Corporation 2025 +# © Copyright IBM Corporation 2025-2026 # SPDX-License-Identifier: Apache-2.0 @@ -57,10 +57,12 @@ def unset_evn_vars(): os.getenv("SH_CLIENT_ID") and os.getenv("SH_CLIENT_SECRET") and os.getenv("NASA_EARTH_BEARER_TOKEN") + and os.getenv("CDSAPI_KEY") ): del os.environ["SH_CLIENT_ID"] del os.environ["SH_CLIENT_SECRET"] del os.environ["NASA_EARTH_BEARER_TOKEN"] + del os.environ["CDSAPI_KEY"] @pytest.fixture @@ -68,6 +70,7 @@ def invalid_evn_vars(): os.environ["SH_CLIENT_ID"] = "" os.environ["SH_CLIENT_SECRET"] = "" os.environ["NASA_EARTH_BEARER_TOKEN"] = "" + os.environ["CDSAPI_KEY"] = "" @pytest.fixture diff --git a/tests/component_tests/download/data_connectors/test_climate_data_store.py b/tests/component_tests/download/data_connectors/test_climate_data_store.py index 481597c..c93b198 100644 --- a/tests/component_tests/download/data_connectors/test_climate_data_store.py +++ b/tests/component_tests/download/data_connectors/test_climate_data_store.py @@ -28,12 +28,9 @@ def test_list_collections_climate_data_store( collections = dc.connector.list_collections() assert collections == expected_collections - @pytest.mark.skip() def test__missing_credentials( self, unset_evn_vars, - connector_type, - collection, start_date, bbox, reset_dot_env, @@ -41,7 +38,7 @@ def test__missing_credentials( """ Test that find_data only runs if credentials are provided. """ - + collection = "derived-era5-single-levels-daily-statistics" with pytest.raises(TerrakitValidationError, match="Error: Missing credentials"): - dc = DataConnector(connector_type=connector_type) + dc = DataConnector(connector_type=self.connector_type) dc.connector.find_data(collection, start_date, start_date, bbox=bbox) From 8410295e83b767343ca9cf1ef28d3654e5d13e56 Mon Sep 17 00:00:00 2001 From: Rosie Lickorish Date: Wed, 11 Feb 2026 12:01:32 +0000 Subject: [PATCH 09/18] find cds data given collection constraints --- .../cds_utils/cordex_domains.json | 172 +++++++++ .../data_connectors/cds_utils/cordex_utils.py | 132 +++++++ ...e-levels-daily-statistics_constraints.json | 158 ++++++++ ...dex-domains-single-levels_constraints.json | 120 ++++++ .../data_connectors/climate_data_store.py | 350 +++++++++++++++++- terrakit/validate/helpers.py | 94 +++-- .../test_climate_data_store.py | 83 ++++- 7 files changed, 1075 insertions(+), 34 deletions(-) create mode 100644 terrakit/download/data_connectors/cds_utils/cordex_domains.json create mode 100644 terrakit/download/data_connectors/cds_utils/cordex_utils.py create mode 100644 terrakit/download/data_connectors/cds_utils/derived-era5-single-levels-daily-statistics_constraints.json create mode 100644 terrakit/download/data_connectors/cds_utils/projections-cordex-domains-single-levels_constraints.json diff --git a/terrakit/download/data_connectors/cds_utils/cordex_domains.json b/terrakit/download/data_connectors/cds_utils/cordex_domains.json new file mode 100644 index 0000000..f7852ff --- /dev/null +++ b/terrakit/download/data_connectors/cds_utils/cordex_domains.json @@ -0,0 +1,172 @@ +{ + "metadata": { + "version": "1.0", + "source": "CORDEX Domain Specification", + "last_updated": "2026-02-11", + "description": "CORDEX regional climate domains with geographic extents and resolutions" + }, + "domains": { + "AFR-44": { + "name": "Africa", + "bbox": [-24.64, -45.76, 60.28, 42.24], + "resolution": "0.44°", + "resolution_km": "~50 km" + }, + "AFR-22": { + "name": "Africa", + "bbox": [-24.64, -45.76, 60.28, 42.24], + "resolution": "0.22°", + "resolution_km": "~25 km" + }, + "ANT-44": { + "name": "Antarctica", + "bbox": [-180.0, -89.5, 180.0, -60.0], + "resolution": "0.44°", + "resolution_km": "~50 km" + }, + "ARC-44": { + "name": "Arctic", + "bbox": [-180.0, 60.0, 180.0, 90.0], + "resolution": "0.44°", + "resolution_km": "~50 km" + }, + "AUS-44": { + "name": "Australasia", + "bbox": [89.5, -52.36, 179.99, 12.21], + "resolution": "0.44°", + "resolution_km": "~50 km" + }, + "AUS-22": { + "name": "Australasia", + "bbox": [89.5, -52.36, 179.99, 12.21], + "resolution": "0.22°", + "resolution_km": "~25 km" + }, + "CAM-44": { + "name": "Central America", + "bbox": [-122.0, -19.76, -59.52, 34.24], + "resolution": "0.44°", + "resolution_km": "~50 km" + }, + "CAM-22": { + "name": "Central America", + "bbox": [-122.0, -19.76, -59.52, 34.24], + "resolution": "0.22°", + "resolution_km": "~25 km" + }, + "CAS-44": { + "name": "Central Asia", + "bbox": [34.0, 18.0, 115.0, 70.0], + "resolution": "0.44°", + "resolution_km": "~50 km" + }, + "CAS-22": { + "name": "Central Asia", + "bbox": [34.0, 18.0, 115.0, 70.0], + "resolution": "0.22°", + "resolution_km": "~25 km" + }, + "EAS-44": { + "name": "East Asia", + "bbox": [65.0, -15.0, 155.0, 65.0], + "resolution": "0.44°", + "resolution_km": "~50 km" + }, + "EAS-22": { + "name": "East Asia", + "bbox": [65.0, -15.0, 155.0, 65.0], + "resolution": "0.22°", + "resolution_km": "~25 km" + }, + "EUR-44": { + "name": "Europe", + "bbox": [-44.0, 22.0, 65.0, 72.0], + "resolution": "0.44°", + "resolution_km": "~50 km" + }, + "EUR-22": { + "name": "Europe", + "bbox": [-44.0, 22.0, 65.0, 72.0], + "resolution": "0.22°", + "resolution_km": "~25 km" + }, + "EUR-11": { + "name": "Europe", + "bbox": [-44.0, 22.0, 65.0, 72.0], + "resolution": "0.11°", + "resolution_km": "~12.5 km" + }, + "MED-44": { + "name": "Mediterranean", + "bbox": [-10.0, 30.0, 40.0, 48.0], + "resolution": "0.44°", + "resolution_km": "~50 km" + }, + "MED-22": { + "name": "Mediterranean", + "bbox": [-10.0, 30.0, 40.0, 48.0], + "resolution": "0.22°", + "resolution_km": "~25 km" + }, + "MNA-44": { + "name": "Middle East and North Africa", + "bbox": [-25.0, 0.0, 75.0, 50.0], + "resolution": "0.44°", + "resolution_km": "~50 km" + }, + "MNA-22": { + "name": "Middle East and North Africa", + "bbox": [-25.0, 0.0, 75.0, 50.0], + "resolution": "0.22°", + "resolution_km": "~25 km" + }, + "NAM-44": { + "name": "North America", + "bbox": [-172.0, 12.0, -35.0, 76.0], + "resolution": "0.44°", + "resolution_km": "~50 km" + }, + "NAM-22": { + "name": "North America", + "bbox": [-172.0, 12.0, -35.0, 76.0], + "resolution": "0.22°", + "resolution_km": "~25 km" + }, + "SAM-44": { + "name": "South America", + "bbox": [-93.0, -56.0, -25.0, 18.0], + "resolution": "0.44°", + "resolution_km": "~50 km" + }, + "SAM-22": { + "name": "South America", + "bbox": [-93.0, -56.0, -25.0, 18.0], + "resolution": "0.22°", + "resolution_km": "~25 km" + }, + "WAS-44": { + "name": "South Asia (West Asia)", + "bbox": [20.0, -15.0, 115.0, 45.0], + "resolution": "0.44°", + "resolution_km": "~50 km" + }, + "WAS-22": { + "name": "South Asia (West Asia)", + "bbox": [20.0, -15.0, 115.0, 45.0], + "resolution": "0.22°", + "resolution_km": "~25 km" + }, + "SEA-44": { + "name": "Southeast Asia", + "bbox": [89.0, -15.0, 146.0, 27.0], + "resolution": "0.44°", + "resolution_km": "~50 km" + }, + "SEA-22": { + "name": "Southeast Asia", + "bbox": [89.0, -15.0, 146.0, 27.0], + "resolution": "0.22°", + "resolution_km": "~25 km" + } + } +} \ No newline at end of file diff --git a/terrakit/download/data_connectors/cds_utils/cordex_utils.py b/terrakit/download/data_connectors/cds_utils/cordex_utils.py new file mode 100644 index 0000000..2a0733c --- /dev/null +++ b/terrakit/download/data_connectors/cds_utils/cordex_utils.py @@ -0,0 +1,132 @@ +# © Copyright IBM Corporation 2026 +# SPDX-License-Identifier: Apache-2.0 + + +import json +from pathlib import Path +from typing import Any, Dict, List + +# Load CORDEX domains from JSON file +_CORDEX_DOMAINS_FILE = Path(__file__).parent / "cordex_domains.json" + + +def _load_cordex_domains() -> Dict[str, Any]: + """Load CORDEX domains from JSON file.""" + with open(_CORDEX_DOMAINS_FILE, "r") as f: + data: Dict[str, Any] = json.load(f) + domains: Dict[str, Any] = data["domains"] + return domains + + +# Cache the domains data +CORDEX_DOMAINS = _load_cordex_domains() + + +def get_domain_info(domain_code: str) -> Dict[str, Any]: + """ + Get information for a specific CORDEX domain. + + Args: + domain_code: CORDEX domain code (e.g., 'EUR-11', 'AFR-44') + + Returns: + Dictionary with domain information including name, bbox, and resolution + + Raises: + ValueError: If domain code is not found + """ + if domain_code not in CORDEX_DOMAINS: + available = list_available_domains() + raise ValueError( + f"Unknown CORDEX domain: {domain_code}. " + f"Available domains: {', '.join(available)}" + ) + domain_info: Dict[str, Any] = CORDEX_DOMAINS[domain_code] + return domain_info + + +def list_available_domains() -> List[str]: + """ + List all available CORDEX domain codes. + + Returns: + List of domain codes (e.g., ['AFR-44', 'EUR-11', ...]) + """ + return sorted(CORDEX_DOMAINS.keys()) + + +def get_domains_by_region(region_name: str) -> List[str]: + """ + Get all domain codes for a specific region. + + Args: + region_name: Region name (e.g., 'Europe', 'Africa') + + Returns: + List of domain codes matching the region + """ + return [ + code + for code, info in CORDEX_DOMAINS.items() + if region_name.lower() in info["name"].lower() + ] + + +def get_domains_by_resolution(resolution_degrees: float) -> List[str]: + """ + Get all domains with a specific resolution. + + Args: + resolution_degrees: Resolution in degrees (e.g., 0.44, 0.22, 0.11) + + Returns: + List of domain codes with matching resolution + """ + return [ + code + for code, info in CORDEX_DOMAINS.items() + if info["resolution_degrees"] == resolution_degrees + ] + + +def bbox_intersects(bbox1: List[float], bbox2: List[float]) -> bool: + """ + Check if two bounding boxes intersect. + + Args: + bbox1: [min_lon, min_lat, max_lon, max_lat] + bbox2: [min_lon, min_lat, max_lon, max_lat] + + Returns: + True if bboxes intersect, False otherwise + """ + min_lon1, min_lat1, max_lon1, max_lat1 = bbox1 + min_lon2, min_lat2, max_lon2, max_lat2 = bbox2 + + # Check if boxes DON'T intersect + if ( + max_lon1 < min_lon2 + or min_lon1 > max_lon2 + or max_lat1 < min_lat2 + or min_lat1 > max_lat2 + ): + return False + + return True + + +def find_matching_domains(bbox: List[float]) -> List[str]: + """ + Find all CORDEX domains that intersect with the given bounding box. + + Args: + bbox: User's bounding box [min_lon, min_lat, max_lon, max_lat] + + Returns: + List of domain codes that intersect with the bbox + """ + matching = [] + for domain_code, domain_info in CORDEX_DOMAINS.items(): + if bbox_intersects(bbox, domain_info["bbox"]): + matching.append(domain_code) + return matching diff --git a/terrakit/download/data_connectors/cds_utils/derived-era5-single-levels-daily-statistics_constraints.json b/terrakit/download/data_connectors/cds_utils/derived-era5-single-levels-daily-statistics_constraints.json new file mode 100644 index 0000000..abf1fcb --- /dev/null +++ b/terrakit/download/data_connectors/cds_utils/derived-era5-single-levels-daily-statistics_constraints.json @@ -0,0 +1,158 @@ +{ + "type": "Collection", + "id": "derived-era5-single-levels-daily-statistics", + "stac_version": "1.1.0", + "title": "ERA5 post-processed daily statistics on single levels from 1940 to present", + "description": "ERA5 is the fifth generation ECMWF reanalysis for the global climate and weather for the past 8 decades.\nData is available from 1940 onwards.\nERA5 replaces the ERA-Interim reanalysis.\nReanalysis combines model data with observations from across the world into a globally complete and consistent dataset using the laws of physics. This principle, called data assimilation, is based on the method used by numerical weather prediction centres, where every so many hours (12 hours at ECMWF) a previous forecast is combined with newly available observations in an optimal way to produce a new best estimate of the state of the atmosphere, called analysis, from which an updated, improved forecast is issued. Reanalysis works in the same way, but at reduced resolution to allow for the provision of a dataset spanning back several decades. Reanalysis does not have the constraint of issuing timely forecasts, so there is more time to collect observations, and when going further back in time, to allow for the ingestion of improved versions of the original observations, which all benefit the quality of the reanalysis product.\nThis catalogue entry provides post-processed ERA5 hourly single-level data aggregated to daily time steps. In addition to the data selection options found on the hourly page, the following options can be selected for the daily statistic calculation:\n\nThe daily aggregation statistic (daily mean, daily max, daily min, daily sum*)\nThe sub-daily frequency sampling of the original data (1 hour, 3 hours, 6 hours)\nThe option to shift to any local time zone in UTC (no shift means the statistic is computed from UTC+00:00)\n\n*The daily sum is only available for the accumulated variables (see ERA5 documentation for more details).\nUsers should be aware that the daily aggregation is calculated during the retrieval process and is not part of a permanently archived dataset. For more details on how the daily statistics are calculated, including demonstrative code, please see the documentation.\nFor more details on the hourly data used to calculate the daily statistics, please refer to the ERA5 hourly single-level data catalogue entry and the documentation found therein.", + "summaries": {}, + "providers": [ + { + "name": "ECMWF" + } + ], + "keywords": [ + "Product type: Reanalysis", + "Temporal coverage: Past", + "Spatial coverage: Global", + "Variable domain: Atmosphere (surface)", + "Variable domain: Atmosphere (upper air)", + "Provider: Copernicus C3S" + ], + "license": "CC-BY-4.0", + "extent": { + "spatial": { + "bbox": [ + [ + 0.0, + -89.0, + 360.0, + 89.0 + ] + ] + }, + "temporal": { + "interval": [ + [ + "1940-01-01T00:00:00+00:00", + "2026-02-04T00:00:00+00:00" + ] + ] + } + }, + "links": [ + { + "rel": "self", + "type": "application/json", + "href": "https://cds.climate.copernicus.eu/api/catalogue/v1/collections/derived-era5-single-levels-daily-statistics" + }, + { + "rel": "parent", + "type": "application/json", + "href": "https://cds.climate.copernicus.eu/api/catalogue/v1/" + }, + { + "rel": "root", + "type": "application/json", + "href": "https://cds.climate.copernicus.eu/api/catalogue/v1/" + }, + { + "rel": "license", + "href": "https://spdx.org/licenses/CC-BY-4.0", + "title": "CC-BY licence", + "rev": 1, + "id": "cc-by" + }, + { + "rel": "form", + "href": "https://object-store.os-api.cci2.ecmwf.int:443/cci2-prod-catalogue/resources/derived-era5-single-levels-daily-statistics/form_ac2e65bb4fdcb8de00c9df3abdf3c6871e4826bd9667a00ea3a22e12fe1d4411.json", + "type": "application/json" + }, + { + "rel": "constraints", + "href": "https://object-store.os-api.cci2.ecmwf.int:443/cci2-prod-catalogue/resources/derived-era5-single-levels-daily-statistics/constraints_a0b1ad068fce54320fa1350cc56b8692d66b395cf066d0c8bbf1579816e074b8.json", + "type": "application/json" + }, + { + "rel": "retrieve", + "href": "https://cds.climate.copernicus.eu/api/retrieve/v1/processes/derived-era5-single-levels-daily-statistics", + "type": "application/json" + }, + { + "rel": "costing_api", + "href": "https://cds.climate.copernicus.eu/api/retrieve/v1/processes/derived-era5-single-levels-daily-statistics/costing", + "type": "application/json" + }, + { + "rel": "layout", + "href": "https://object-store.os-api.cci2.ecmwf.int:443/cci2-prod-catalogue/resources/derived-era5-single-levels-daily-statistics/layout_49b6cd183b8a61bbe6984fa1b1a059a0450dd15d8d169d8120c641be7af2197c.json", + "type": "application/json" + }, + { + "rel": "related", + "href": "https://cds.climate.copernicus.eu/api/catalogue/v1/collections/derived-era5-pressure-levels-daily-statistics", + "title": "ERA5 post-processed daily statistics on pressure levels from 1940 to present" + }, + { + "rel": "related", + "href": "https://cds.climate.copernicus.eu/api/catalogue/v1/collections/reanalysis-era5-single-levels-timeseries", + "title": "ERA5 hourly time-series data on single levels from 1940 to present" + }, + { + "rel": "related", + "href": "https://cds.climate.copernicus.eu/api/catalogue/v1/collections/reanalysis-era5-pressure-levels-monthly-means", + "title": "ERA5 monthly averaged data on pressure levels from 1940 to present" + }, + { + "rel": "related", + "href": "https://cds.climate.copernicus.eu/api/catalogue/v1/collections/reanalysis-era5-single-levels", + "title": "ERA5 hourly data on single levels from 1940 to present" + }, + { + "rel": "related", + "href": "https://cds.climate.copernicus.eu/api/catalogue/v1/collections/reanalysis-era5-complete", + "title": "Complete ERA5 global atmospheric reanalysis" + }, + { + "rel": "related", + "href": "https://cds.climate.copernicus.eu/api/catalogue/v1/collections/reanalysis-era5-pressure-levels", + "title": "ERA5 hourly data on pressure levels from 1940 to present" + }, + { + "rel": "related", + "href": "https://cds.climate.copernicus.eu/api/catalogue/v1/collections/reanalysis-era5-single-levels-monthly-means", + "title": "ERA5 monthly averaged data on single levels from 1940 to present" + }, + { + "rel": "related", + "href": "https://cds.climate.copernicus.eu/api/catalogue/v1/collections/sis-european-risk-extreme-precipitation-indicators", + "title": "Extreme precipitation risk indicators for Europe and European cities from 1950 to 2019" + }, + { + "rel": "related", + "href": "https://cds.climate.copernicus.eu/api/catalogue/v1/collections/derived-utci-historical", + "title": "Thermal comfort indices derived from ERA5 reanalysis" + }, + { + "rel": "messages", + "href": "https://cds.climate.copernicus.eu/api/catalogue/v1/collections/derived-era5-single-levels-daily-statistics/messages", + "title": "All active messages on ERA5 post-processed daily statistics on single levels from 1940 to present" + } + ], + "assets": { + "thumbnail": { + "href": "https://object-store.os-api.cci2.ecmwf.int:443/cci2-prod-catalogue/resources/derived-era5-single-levels-daily-statistics/overview_593e6a72773f0dd1e6b665ff4fe2e184f1cd7b175c143e28caa61c569fe82dd3.png", + "type": "image/jpg", + "roles": [ + "thumbnail" + ] + } + }, + "published": "2024-10-14T00:00:00Z", + "updated": "2026-02-10T00:00:00Z", + "sci:doi": "10.24381/cds.4991cf48", + "cads:disabled_reason": null, + "cads:sanity_check": { + "status": "unknown", + "timestamp": null + } +} \ No newline at end of file diff --git a/terrakit/download/data_connectors/cds_utils/projections-cordex-domains-single-levels_constraints.json b/terrakit/download/data_connectors/cds_utils/projections-cordex-domains-single-levels_constraints.json new file mode 100644 index 0000000..17840fd --- /dev/null +++ b/terrakit/download/data_connectors/cds_utils/projections-cordex-domains-single-levels_constraints.json @@ -0,0 +1,120 @@ +{ + "type": "Collection", + "id": "projections-cordex-domains-single-levels", + "stac_version": "1.1.0", + "title": "CORDEX regional climate model data on single levels", + "description": "This catalogue entry provides Regional Climate Model (RCM) data on single levels from a number of experiments, models, domains, resolutions, ensemble members, time frequencies and periods computed over several regional domains all over the World in the framework of the Coordinated Regional Climate Downscaling Experiment (CORDEX). The term \"single levels\" is used to express that the variables are 2D-matrices computed on one vertical level which can be surface (or a level close to the surface) or a dedicated pressure level in the atmosphere. Multiple vertical levels are excluded from this catalogue entry.\nHigh-resolution Regional Climate Models (RCMs) can provide climate change information on regional and local scales in relatively fine detail, which cannot be obtained from coarse scale Global Climate Models (GCMs). This is manifested in better description of small-scale regional climate characteristics and also in more accurate representation of extreme events. Consequently, outputs of such RCMs are indispensable in supporting regional and local climate impact studies and adaptation decisions. RCMs are not independent from the GCMs, since the GCMs provide lateral and lower boundary conditions to the regional models. In that sense RCMs can be viewed as magnifying glasses of the GCMs.\nThe CORDEX experiments consist of RCM simulations representing different future socio-economic scenarios (forcings), different combinations of GCMs and RCMs and different ensemble members of the same GCM-RCM combinations. This experiment design through the ensemble members allows for studies addressing questions related to the key uncertainties in future climate change. These uncertainties come from differences in the scenarios of future socio-economic development, the imperfection of regional and global models used and the internal (natural) variability of the climate system. This experiment design allows for studies addressing questions related to the key uncertainties in future climate change:\n\nwhat will future climate forcing be?\nwhat will be the response of the climate system to changes in forcing?\nwhat is the uncertainty related to natural variability of the climate system?\n\nThe term \"experiment\" in the CDS form refers to three main categories:\n\nEvaluation: CORDEX experiment driven by ECMWF ERA-Interim reanalysis for a past period. These experiments can be used to evaluate the quality of the RCMs using perfect boundary conditions as provided by a reanalysis system. The period covered is typically 1980-2010;\nHistorical: CORDEX experiment which covers a period for which modern climate observations exist. Boundary conditions are provided by GCMs. These experiments, that follow the observed changes in climate forcing, show how the RCMs perform for the past climate when forced by GCMs and can be used as a reference period for comparison with scenario runs for the future. The period covered is typically 1950-2005;\nScenario: Ensemble of CORDEX climate projection experiments using RCP (Representative Concentration Pathways) forcing scenarios. These scenarios are the RCP 2.6, 4.5 and 8.5 scenarios providing different pathways of the future climate forcing. Boundary conditions are provided by GCMs. The period covered is typically 2006-2100.\n\nIn CORDEX, the same experiments were done using different RCMs (labelled as “Regional Climate Model” in the CDS form).\nIn addition, for each RCM, there is a variety of GCMs, which can be used as lateral boundary conditions. The GCMs used are coming from the CMIP5 (5th phase of the Coupled Model Intercomparison Project) archive. These GCM boundary conditions are labelled as “Global Climate Model” in the form and are also available in the CDS.\nAdditionally, the uncertainty related to internal variability of the climate system is sampled by running several simulations with the same RCM-GCM combination. On the forms, these are indexed as separate ensemble members (the naming convention for ensemble members is available in the documentation). For each GCM, the same experiment was repeatedly done using slightly different conditions (like initial conditions or different physical parameterisations for instance) producing in that way an ensemble of experiments closely related. More details behind these sequential ensemble numbers is available in the detailed documentation.\nThe data are produced by the institutes and modelling centres participating in the different CORDEX domains with partial support from different international and national contributions including support from COPERNICUS for some of the EURO-CORDEX runs.\nThe data can be used for commercial purposes (unrestricted use) with the exception of the simulations from the following RCMs: BOUN-RegCM4-3 model (for Central Asia and Middle East and North Africa domains) and RU-CORE-RegCM4-3 model (for South-East Asia domain). Precise terms of use are provided in the CORDEX licence.", + "summaries": {}, + "providers": [ + { + "name": "ECMWF" + } + ], + "keywords": [ + "Temporal coverage: Past", + "Spatial coverage: Europe", + "Temporal coverage: Present", + "Variable domain: Atmosphere (surface)", + "Variable domain: Atmosphere (upper air)", + "Product type: Climate projections", + "Temporal coverage: Future" + ], + "license": "other", + "extent": { + "spatial": { + "bbox": [ + [ + 360.0, + -90.0, + 0.0, + 90.0 + ] + ] + }, + "temporal": { + "interval": [ + [ + "1950-01-01T00:00:00+00:00", + "2100-12-31T00:00:00+00:00" + ] + ] + } + }, + "links": [ + { + "rel": "self", + "type": "application/json", + "href": "https://cds.climate.copernicus.eu/api/catalogue/v1/collections/projections-cordex-domains-single-levels" + }, + { + "rel": "parent", + "type": "application/json", + "href": "https://cds.climate.copernicus.eu/api/catalogue/v1/" + }, + { + "rel": "root", + "type": "application/json", + "href": "https://cds.climate.copernicus.eu/api/catalogue/v1/" + }, + { + "rel": "qa", + "href": "https://cds.climate.copernicus.eu/datasets/projections-cordex-domains-single-levels?tab=quality_assurance_tab", + "title": "Quality assessment of the dataset", + "type": "text/html" + }, + { + "rel": "license", + "href": "https://object-store.os-api.cci2.ecmwf.int:443/cci2-prod-catalogue/licences/cordex-licence/cordex-licence_08fc76dd4edee86a8ac7ae6a7368c9a25b87a23bc5a1a60f11e9af6ed48eea35.pdf", + "title": "CORDEX licence", + "rev": 1, + "id": "cordex-licence" + }, + { + "rel": "form", + "href": "https://object-store.os-api.cci2.ecmwf.int:443/cci2-prod-catalogue/resources/projections-cordex-domains-single-levels/form_de59d5b3df31700387af03031b8c27f72264384b37d01d08bddeeb32c3470d6d.json", + "type": "application/json" + }, + { + "rel": "constraints", + "href": "https://object-store.os-api.cci2.ecmwf.int:443/cci2-prod-catalogue/resources/projections-cordex-domains-single-levels/constraints_953327d50d70e11c67d715c45af818d43db99e18eace62ef26adc2cdf0c6184d.json", + "type": "application/json" + }, + { + "rel": "retrieve", + "href": "https://cds.climate.copernicus.eu/api/retrieve/v1/processes/projections-cordex-domains-single-levels", + "type": "application/json" + }, + { + "rel": "costing_api", + "href": "https://cds.climate.copernicus.eu/api/retrieve/v1/processes/projections-cordex-domains-single-levels/costing", + "type": "application/json" + }, + { + "rel": "layout", + "href": "https://object-store.os-api.cci2.ecmwf.int:443/cci2-prod-catalogue/resources/projections-cordex-domains-single-levels/layout_39f4e7d4f881ff9e4dbf87bdc9ba52bb106a62598498e5150c2765104d4601cb.json", + "type": "application/json" + }, + { + "rel": "messages", + "href": "https://cds.climate.copernicus.eu/api/catalogue/v1/collections/projections-cordex-domains-single-levels/messages", + "title": "All active messages on CORDEX regional climate model data on single levels" + } + ], + "assets": { + "thumbnail": { + "href": "https://object-store.os-api.cci2.ecmwf.int:443/cci2-prod-catalogue/resources/projections-cordex-domains-single-levels/overview_a414d6d59c8c872f6765b2cbc73ee0d7c043535d442867864c02964ba08524db.png", + "type": "image/jpg", + "roles": [ + "thumbnail" + ] + } + }, + "published": "2019-08-27T00:00:00Z", + "updated": "2019-08-27T00:00:00Z", + "sci:doi": "10.24381/cds.bc91edc3", + "cads:disabled_reason": null, + "cads:sanity_check": { + "status": "unknown", + "timestamp": null + } +} \ No newline at end of file diff --git a/terrakit/download/data_connectors/climate_data_store.py b/terrakit/download/data_connectors/climate_data_store.py index 0191951..2141d70 100644 --- a/terrakit/download/data_connectors/climate_data_store.py +++ b/terrakit/download/data_connectors/climate_data_store.py @@ -2,17 +2,31 @@ # SPDX-License-Identifier: Apache-2.0 +import cdsapi +import json import logging import os import xarray as xr - -from typing import Any, Union +from datetime import datetime, timedelta +from shapely.geometry import box +from typing import Any, Dict, Union +from pathlib import Path from ..connector import Connector from ..geodata_utils import ( load_and_list_collections, ) -from terrakit.general_utils.exceptions import TerrakitValidationError +from terrakit.general_utils.exceptions import ( + TerrakitValidationError, +) +from terrakit.validate.helpers import ( + check_collection_exists, + check_date_format, + check_start_end_date_in_correct_order, + basic_bbox_validation, +) +from .cds_utils.cordex_utils import CORDEX_DOMAINS, get_domain_info + logger = logging.getLogger(__name__) @@ -20,8 +34,6 @@ ### Supporting functions ###################################################################################################### -CDSAPI_URL = "https://cds.climate.copernicus.eu/api" - ###################################################################################################### ### Connector class ###################################################################################################### @@ -40,12 +52,298 @@ def __init__(self): Initialize climate_data_store with collections and configuration. """ self.connector_type: str = "climate_data_store" + self.CDSAPI_URL: str = "https://cds.climate.copernicus.eu/api" + self.stac_url: str = "https://cds.climate.copernicus.eu/api/catalogue/v1/" self.collections: list[Any] = load_and_list_collections( connector_type=self.connector_type ) self.collections_details: list[Any] = load_and_list_collections( as_json=True, connector_type=self.connector_type ) + self.metadata_dir = Path(__file__).parent / "cds_utils" + + # Load CORDEX domains + self.cordex_domains = CORDEX_DOMAINS + + def _is_cordex_collection(self, collection_name: str) -> bool: + """Check if collection is a CORDEX dataset.""" + return "cordex" in collection_name.lower() + + def _get_cordex_domain_from_bbox(self, bbox: list) -> str: + """ + Map user bbox to appropriate CORDEX domain code. + + Args: + bbox: User's bounding box [min_lon, min_lat, max_lon, max_lat] + + Returns: + str: CORDEX domain code (e.g., 'EUR-11') + + Raises: + TerrakitValidationError: If no matching domain found + """ + from .cds_utils.cordex_utils import find_matching_domains + + matching_domains = find_matching_domains(bbox) + + if not matching_domains: + raise TerrakitValidationError( + message=f"Bbox {bbox} does not intersect with any CORDEX domain. " + f"Use list_cordex_domains() to see available domains." + ) + + if len(matching_domains) == 1: + return matching_domains[0] + + # Multiple matches - return best match based on overlap + return self._find_best_cordex_match(bbox, matching_domains) + + def _find_best_cordex_match(self, bbox: list, domain_codes: list) -> str: + """ + Find CORDEX domain with maximum overlap with user bbox. + + Args: + bbox: User's bounding box + domain_codes: List of candidate domain codes + + Returns: + str: Best matching domain code + """ + + user_box = box(bbox[0], bbox[1], bbox[2], bbox[3]) + best_domain: str = domain_codes[0] # Initialize with first domain + max_overlap = 0 + + for domain_code in domain_codes: + domain_bbox = self.cordex_domains[domain_code]["bbox"] + domain_box = box( + domain_bbox[0], domain_bbox[1], domain_bbox[2], domain_bbox[3] + ) + + overlap_area = user_box.intersection(domain_box).area + if overlap_area > max_overlap: + max_overlap = overlap_area + best_domain = domain_code + + logger.info( + f"Multiple CORDEX domains match bbox. Selected {best_domain} with largest overlap." + ) + return best_domain + + def list_cordex_domains(self) -> Dict[str, Any]: + """ + List all available CORDEX domains with their information. + + Returns: + dict: Dictionary of domain codes and their information + """ + cordex_domains: Dict[str, Any] = self.cordex_domains + return cordex_domains + + def get_cordex_domain_info(self, domain_code: str) -> dict: + """ + Get information for a specific CORDEX domain. + + Args: + domain_code: CORDEX domain code (e.g., 'EUR-11') + + Returns: + dict: Domain information including name, bbox, and resolution + + Raises: + TerrakitValueError: If domain code not found + """ + return get_domain_info(domain_code) + + def _get_constraint_value( + self, constraints: dict, *keys: str, collection_name: str = "" + ): + """ + Safely extract nested values from constraints with clear error messages. + + Args: + constraints: The constraints dictionary + *keys: Sequence of keys to traverse (e.g., 'extent', 'temporal', 'interval') + collection_name: Optional collection name for better error messages + + Returns: + The value at the specified path + + Raises: + TerrakitValidationError: If any key in the path is missing + """ + if not constraints: + raise TerrakitValidationError( + message=f"No constraints metadata available{f' for {collection_name}' if collection_name else ''}" + ) + + value = constraints + path = [] + + for key in keys: + path.append(key) + if not isinstance(value, dict) or key not in value: + path_str = " -> ".join(path) + raise TerrakitValidationError( + message=f"Collection constraints missing required field: '{path_str}'" + f"{f' for {collection_name}' if collection_name else ''}" + ) + value = value[key] + + if value is None: + path_str = " -> ".join(path) + raise TerrakitValidationError( + message=f"Collection constraints field is null: '{path_str}'" + f"{f' for {collection_name}' if collection_name else ''}" + ) + return value + + def _validate_temporal( + self, + date_start: str, + date_end: str, + constraints: dict, + collection_name: str = "", + ): + """Validate dates against collection constraints.""" + + # Check dates are valid + check_start_end_date_in_correct_order(date_start, date_end) + check_date_format(date_start, start_or_end="start") + check_date_format(date_start, start_or_end="end") + + # Get temporal interval using helper + intervals = self._get_constraint_value( + constraints, + "extent", + "temporal", + "interval", + collection_name=collection_name, + ) + + if not intervals or not intervals[0] or len(intervals[0]) < 2: + raise TerrakitValidationError( + message=f"Invalid temporal interval format in constraints" + f"{f' for {collection_name}' if collection_name else ''}" + ) + + try: + # Get allowed date range + allowed_start = datetime.fromisoformat( + intervals[0][0].replace("+00:00", "") + ) + allowed_end = datetime.fromisoformat(intervals[0][1].replace("+00:00", "")) + print(allowed_start, allowed_end) + # Parse requested dates + req_start = datetime.strptime(date_start, "%Y-%m-%d") + req_end = datetime.strptime(date_end, "%Y-%m-%d") + + # Validate start date + if req_start < allowed_start: + raise TerrakitValidationError( + message=f"Start date {date_start} is before allowed start date {allowed_start.date()}" + ) + + # Validate end date + if req_end > allowed_end: + raise TerrakitValidationError( + message=f"End date {date_end} is after allowed end date {allowed_end.date()}" + ) + + except ValueError as e: + raise TerrakitValidationError(message=f"Invalid date format: {e}") + + def _validate_spatial( + self, bbox: list, constraints: dict, collection_name: str = "" + ): + """Validate bbox against collection constraints.""" + + basic_bbox_validation(bbox, self.connector_type) + + # For CORDEX collections, map bbox to domain + if self._is_cordex_collection(collection_name): + try: + domain_code = self._get_cordex_domain_from_bbox(bbox) + logger.info(f"Mapped bbox to CORDEX domain: {domain_code}") + # Store domain for later use in find_data + self._selected_cordex_domain = domain_code + except TerrakitValidationError: + raise + else: + # Get spatial bbox using helper + bbox_list = self._get_constraint_value( + constraints, + "extent", + "spatial", + "bbox", + collection_name=collection_name, + ) + + if not bbox_list or not bbox_list[0] or len(bbox_list[0]) != 4: + raise TerrakitValidationError( + message=f"Invalid spatial bbox format in constraints" + f"{f' for {collection_name}' if collection_name else ''}" + ) + + allowed_bbox = bbox_list[0] + + # Unpack for clarity + min_lon, min_lat, max_lon, max_lat = bbox + allowed_min_lon, allowed_min_lat, allowed_max_lon, allowed_max_lat = ( + allowed_bbox + ) + + # Validate each bound + errors = [] + if min_lon < allowed_min_lon: + errors.append(f"min_lon {min_lon} < allowed {allowed_min_lon}") + if min_lat < allowed_min_lat: + errors.append(f"min_lat {min_lat} < allowed {allowed_min_lat}") + if max_lon > allowed_max_lon: + errors.append(f"max_lon {max_lon} > allowed {allowed_max_lon}") + if max_lat > allowed_max_lat: + errors.append(f"max_lat {max_lat} > allowed {allowed_max_lat}") + + if errors: + raise TerrakitValidationError( + message=f"Bounding box out of range: {'; '.join(errors)}" + ) + + def _load_constraints(self, collection_name: str) -> dict: + """Load constraints metadata from local file.""" + constraints_file = self.metadata_dir / f"{collection_name}_constraints.json" + + if not constraints_file.exists(): + raise TerrakitValidationError( + message=f"No constraints file found for collection '{collection_name}'. " + f"Expected: {constraints_file}" + ) + + try: + with open(constraints_file, "r") as f: + constraints: Dict[str, Any] = json.load(f) + except json.JSONDecodeError as e: + raise TerrakitValidationError( + message=f"Invalid JSON in constraints file for '{collection_name}': {e}" + ) + except Exception as e: + raise TerrakitValidationError( + message=f"Error loading constraints for '{collection_name}': {e}" + ) + return constraints + + def _connect_to_cds(self) -> cdsapi.Client: + """ + Connect to climate data store. + """ + + try: + client = cdsapi.Client(url=self.CDSAPI_URL, key=os.getenv("CDSAPI_KEY")) + except Exception as err: + error_msg = f"Unable to connect to Climate Data Store. {err}" + logger.error(error_msg) + raise TerrakitValidationError(error_msg) + return client def list_collections(self) -> list[Any]: """ @@ -89,8 +387,38 @@ def find_data( message="Error: Missing credentials 'CDSAPI_KEY'. Please update .env with correct credentials." ) - unique_dates: list[str] = [] - results: list[dict[str, Any]] = [{}] + # Check data_collection_name exists in self.collections. + check_collection_exists(data_collection_name, self.collections) + + # Load constraints + constraints = self._load_constraints(data_collection_name) + + # Validate contsraint parameters using collection name for better errors + self._validate_temporal(date_start, date_end, constraints, data_collection_name) + self._validate_spatial(bbox, constraints, data_collection_name) + + # Generate dates + + start = datetime.strptime(date_start, "%Y-%m-%d") + end = datetime.strptime(date_end, "%Y-%m-%d") + + unique_dates = [] + value = start + while value <= end: + unique_dates.append(value.strftime("%Y-%m-%d")) + value += timedelta(days=1) + + results = [ + { + "collection": data_collection_name, + "date_range": f"{date_start} to {date_end}", + "total_dates": len(unique_dates), + "temporal_extent": constraints.get("extent", {}).get("temporal"), + "spatial_extent": constraints.get("extent", {}).get("spatial"), + } + ] + + # TODO: filter by cloud cover return unique_dates, results def get_data( @@ -124,6 +452,9 @@ def get_data( Returns: xarray: An xarray Datasets containing the fetched data with dimensions (time, band, y, x). """ + + # cds_client = self._connect_to_cds() + # da = xr.DataArray() # First construct the query @@ -158,3 +489,8 @@ def get_data( # HTTPError: 403 Client Error: Forbidden for url: https://cds.climate.copernicus.eu/api/retrieve/v1/processes/derived-era5-single-levels-daily-statistics/execution # required licences not accepted # Not all the required licences have been accepted; please visit https://cds.climate.copernicus.eu/datasets/derived-era5-single-levels-daily-statistics?tab=download#manage-licences to accept the required licence(s). + + +# https://object-store.os-api.cci2.ecmwf.int/cci2-prod-catalogue/resources/derived-era5-single-levels-daily-statistics/constraints_a0b1ad068fce54320fa1350cc56b8692d66b395cf066d0c8bbf1579816e074b8.json +# 532 entries +# dict_keys(['daily_statistic', 'day', 'frequency', 'month', 'product_type', 'time_zone', 'variable', 'year']) diff --git a/terrakit/validate/helpers.py b/terrakit/validate/helpers.py index 4e71b4a..394fbce 100644 --- a/terrakit/validate/helpers.py +++ b/terrakit/validate/helpers.py @@ -1,15 +1,18 @@ -# © Copyright IBM Corporation 2025 +# © Copyright IBM Corporation 2025-2026 # SPDX-License-Identifier: Apache-2.0 import logging from datetime import datetime, date +from typing import cast, Literal from terrakit.general_utils.exceptions import TerrakitValueError logger = logging.getLogger(__name__) +HISTORIC_TIME_LIMIT = "1950-01-01" + def check_collection_exists(data_collection_name: str, collections: list): """ @@ -28,20 +31,15 @@ def check_collection_exists(data_collection_name: str, collections: list): raise TerrakitValueError(error_msg) -def check_start_end_date(date_start: str, date_end: str) -> None: +def check_start_end_date_in_correct_order(date_start: str, date_end: str) -> None: """ Validate the start and end dates ensuring the end date is after the start date. - Parameters: date_start (str): The start date in ISO format (YYYY-MM-DD). date_end (str): The end date in ISO format (YYYY-MM-DD). - Raises: - TerrakitValueError: If the date range is invalid. + TerrakitValueError: If the start date before end date. """ - check_datetime(start=True, date_str=date_start) - check_datetime(start=False, date_str=date_end) - start = date.fromisoformat(date_start) end = date.fromisoformat(date_end) delta = end - start @@ -51,38 +49,68 @@ def check_start_end_date(date_start: str, date_end: str) -> None: raise TerrakitValueError(err_msg) -def check_datetime(start: bool, date_str: str) -> None: +def check_start_end_date(date_start: str, date_end: str) -> None: """ - Validate a date string ensuring it's in ISO format and not in the future. + Validate the start and end dates ensuring the end date is after the start date. Parameters: - start (bool): True if validating the start date, False for end date. - date_str (str): The date string to validate. + date_start (str): The start date in ISO format (YYYY-MM-DD). + date_end (str): The end date in ISO format (YYYY-MM-DD). Raises: - TerrakitValueError: If the date format is incorrect or the date is in the future. + TerrakitValueError: If the date range is invalid. + """ + # Check start date and end date independently. Ensures valid format, dates are in future or + check_datetime(start=True, date_str=date_start) + check_datetime(start=False, date_str=date_end) + + check_start_end_date_in_correct_order(date_start, date_end) + + +def check_date_format(date_str: str, start_or_end: Literal["start", "end"]) -> date: + """ + Validate a date string ensuring it's in ISO format. + Parameters: + date_str (str): The date string to validate. + start_or_end (bool): True if validating the start date, False for end date. + Returns: + date: The date as a datetime object. + Raises: + TerrakitValueError: If the date format is incorrect. """ - if start: - start_or_end = "start" - else: - start_or_end = "end" try: query_date = date.fromisoformat(date_str) except ValueError as e: err_msg = f"Invalid {start_or_end} date format: {date_str}. Please use ISO format (YYYY-MM-DD)." logger.error(err_msg) raise TerrakitValueError(err_msg, e) # type: ignore [arg-type] + return query_date + + +def check_datetime(start: bool, date_str: str) -> None: + """ + Validate a date string ensuring it's in ISO format and not in the future or before a set time period. + + Parameters: + start (bool): True if validating the start date, False for end date. + date_str (str): The date string to validate. - if query_date > datetime.date(datetime.now()): + Raises: + TerrakitValueError: If the date format is incorrect or the date is in the future. + """ + start_or_end = cast(Literal["start", "end"], "start" if start else "end") + + # Call check_date_format to validate and parse the date + query_date = check_date_format(date_str, start_or_end) + + if query_date > datetime.now().date(): err_msg = f"Invalid {start_or_end} date: {date_str}. Date must be in the past." logger.error(err_msg) raise TerrakitValueError( err_msg, ) - if query_date < datetime.strptime("01/01/1950", "%d/%m/%Y").date(): - err_msg = ( - f"Invalid {start_or_end} date: {date_str}. Date must be after 01/01/1950." - ) + if query_date < date.fromisoformat(HISTORIC_TIME_LIMIT): + err_msg = f"Invalid {start_or_end} date: {date_str}. Date must be after {HISTORIC_TIME_LIMIT}." logger.error(err_msg) raise TerrakitValueError( err_msg, @@ -106,14 +134,11 @@ def check_area_polygon(area_polygon, connector_type: str) -> None: raise TerrakitValueError(err_msg) -def check_bbox(bbox: list, connector_type: str) -> None: +def basic_bbox_validation(bbox: list | None, connector_type: str) -> None: """ Validate the bounding box ensuring it's a list of four floats and not a degenerate rectangle. - Parameters: bbox (list): The bounding box to check. - connector_type (str): The type of connector. - Raises: TerrakitValueError: If the bounding box is invalid. """ @@ -129,6 +154,23 @@ def check_bbox(bbox: list, connector_type: str) -> None: err_msg = f"Error: Issue finding data from {connector_type} with bbox '{bbox}'. Please specify 'bbox' as a list of length 4." logger.error(err_msg) raise TerrakitValueError(err_msg) + + +def check_bbox(bbox: list | None, connector_type: str) -> None: + """ + Validate the bounding box ensuring it's a list of four floats and not a degenerate rectangle. + + Parameters: + bbox (list): The bounding box to check. + connector_type (str): The type of connector. + + Raises: + TerrakitValueError: If the bounding box is invalid. + """ + basic_bbox_validation(bbox, connector_type) + if bbox is None: + return # basic_bbox_validation already raised an error + for item in bbox: try: float(item) diff --git a/tests/component_tests/download/data_connectors/test_climate_data_store.py b/tests/component_tests/download/data_connectors/test_climate_data_store.py index c93b198..736c486 100644 --- a/tests/component_tests/download/data_connectors/test_climate_data_store.py +++ b/tests/component_tests/download/data_connectors/test_climate_data_store.py @@ -2,15 +2,19 @@ # SPDX-License-Identifier: Apache-2.0 +import pandas as pd import pytest + from terrakit import DataConnector from terrakit.general_utils.exceptions import ( TerrakitValidationError, + TerrakitValueError, ) class TestClimateDataStore: connector_type = "climate_data_store" + bands = ["2m_temperature_max", "2m_temperature_min"] def test_valid_data_connector(self): dc = DataConnector(connector_type=self.connector_type) @@ -28,7 +32,7 @@ def test_list_collections_climate_data_store( collections = dc.connector.list_collections() assert collections == expected_collections - def test__missing_credentials( + def test__missing_credentials_cds( self, unset_evn_vars, start_date, @@ -42,3 +46,80 @@ def test__missing_credentials( with pytest.raises(TerrakitValidationError, match="Error: Missing credentials"): dc = DataConnector(connector_type=self.connector_type) dc.connector.find_data(collection, start_date, start_date, bbox=bbox) + + def test_invalid_collection(self, start_date, bbox): + """ + Test that an invalid collection raises a TerrakitValidationError. + """ + collection = "invalid-collection" + dc = DataConnector(connector_type=self.connector_type) + with pytest.raises(TerrakitValueError, match="Invalid collection"): + dc.connector.find_data(collection, start_date, start_date, bbox=bbox) + + @pytest.fixture + def expected_dates_cds(self): + dates = pd.date_range("2024-01-01", "2024-01-31").strftime("%Y-%m-%d").tolist() + return dates + + @pytest.mark.parametrize( + "collection", + [ + ("derived-era5-single-levels-daily-statistics"), + ("projections-cordex-domains-single-levels"), + ], + ) + def test_find_available_data_cds( + self, + collection, + expected_dates_cds, + start_date, + end_date, + bbox, + ): + dc = DataConnector(connector_type=self.connector_type) + unique_dates, results = dc.connector.find_data( + data_collection_name=collection, + date_start=start_date, + date_end=end_date, + bbox=bbox, + bands=self.bands, + ) + assert unique_dates == expected_dates_cds + + @pytest.mark.parametrize( + ("collection", "start_date", "end_date", "expected_dates_cds"), + [ + ( + "derived-era5-single-levels-daily-statistics", + "1949-01-01", + "1949-01-02", + ["1949-01-01, 1949-01-02"], + ), + ( + "projections-cordex-domains-single-levels", + "2100-01-01", + "2100-01-02", + ["2100-01-01, 2100-01-02"], + ), + ], + ) + def test_find_available_data_cds_start_data_given_constraints( + self, + collection, + start_date, + end_date, + expected_dates_cds, + bbox, + ): + """ + Test the find_data method with a given start date within the collection constraints. + """ + dc = DataConnector(connector_type=self.connector_type) + unique_dates, results = dc.connector.find_data( + data_collection_name=collection, + date_start=start_date, + date_end=end_date, + bbox=bbox, + bands=self.bands, + ) + assert unique_dates == [start_date, end_date] From c513f27ef6aba714743dea911c4fb5c552c109a7 Mon Sep 17 00:00:00 2001 From: Rosie Lickorish Date: Wed, 11 Feb 2026 12:14:26 +0000 Subject: [PATCH 10/18] Update docs to include cordex domain details --- .secrets.baseline | 14 ++++---- README.md | 2 +- docs/cordex_domains.md | 66 +++++++++++++++++++++++++++++++++++++ docs/data_connectors.md | 72 +++++++++++++++++++++++++++++++++++++++++ docs/download_data.md | 69 ++------------------------------------- mkdocs.yml | 3 ++ 6 files changed, 151 insertions(+), 75 deletions(-) create mode 100644 docs/cordex_domains.md create mode 100644 docs/data_connectors.md diff --git a/.secrets.baseline b/.secrets.baseline index 7e3da4c..a7b6683 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "^.secrets.baseline$", "lines": null }, - "generated_at": "2026-02-10T20:40:41Z", + "generated_at": "2026-02-11T12:14:01Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -114,39 +114,39 @@ "verified_result": null } ], - "docs/download_data.md": [ + "docs/data_connectors.md": [ { "hashed_secret": "5204df45fc8c724684bbc61cd4107a726a6b9204", "is_verified": false, - "line_number": 176, + "line_number": 28, "type": "Secret Keyword", "verified_result": null }, { "hashed_secret": "365a32402f2062038d01718da7de099f94555775", "is_verified": false, - "line_number": 178, + "line_number": 30, "type": "Secret Keyword", "verified_result": null }, { "hashed_secret": "d401c389d24671398ced85b259e17975a4fcb65e", "is_verified": false, - "line_number": 201, + "line_number": 53, "type": "Secret Keyword", "verified_result": null }, { "hashed_secret": "4e1e76c221c6dace8f9a2c943e00d3328366366a", "is_verified": false, - "line_number": 210, + "line_number": 62, "type": "Secret Keyword", "verified_result": null }, { "hashed_secret": "54a38e82cf07a4f36900b855ca7dd0584ca9f3ff", "is_verified": false, - "line_number": 212, + "line_number": 64, "type": "Secret Keyword", "verified_result": null } diff --git a/README.md b/README.md index de000db..2817209 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ Data connectors are classes which enable a user to search for data and query dat * get_data() -### Available data connectors +### Available collections The following data connectors and associated collections are available: | Connectors | Collections | diff --git a/docs/cordex_domains.md b/docs/cordex_domains.md new file mode 100644 index 0000000..45797e4 --- /dev/null +++ b/docs/cordex_domains.md @@ -0,0 +1,66 @@ +// Copyright (c) 2026 Copyright 2024 IBM Corp +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +## CORDEX Domain Reference + +CORDEX (Coordinated Regional Climate Downscaling Experiment) defines specific regional domains for climate projections. Each domain covers a geographic region at one or more resolutions. + +### Available CORDEX Domains + +| Domain Code | Region Name | Bounding Box (min_lon, min_lat, max_lon, max_lat) | Resolution | Approx. Grid Spacing | +|-------------|-------------|---------------------------------------------------|------------|---------------------| +| **AFR-44** | Africa | -24.64, -45.76, 60.28, 42.24 | 0.44° | ~50 km | +| **AFR-22** | Africa | -24.64, -45.76, 60.28, 42.24 | 0.22° | ~25 km | +| **ANT-44** | Antarctica | -180.0, -89.5, 180.0, -60.0 | 0.44° | ~50 km | +| **ARC-44** | Arctic | -180.0, 60.0, 180.0, 90.0 | 0.44° | ~50 km | +| **AUS-44** | Australasia | 89.5, -52.36, 179.99, 12.21 | 0.44° | ~50 km | +| **AUS-22** | Australasia | 89.5, -52.36, 179.99, 12.21 | 0.22° | ~25 km | +| **CAM-44** | Central America | -122.0, -19.76, -59.52, 34.24 | 0.44° | ~50 km | +| **CAM-22** | Central America | -122.0, -19.76, -59.52, 34.24 | 0.22° | ~25 km | +| **CAS-44** | Central Asia | 34.0, 18.0, 115.0, 70.0 | 0.44° | ~50 km | +| **CAS-22** | Central Asia | 34.0, 18.0, 115.0, 70.0 | 0.22° | ~25 km | +| **EAS-44** | East Asia | 65.0, -15.0, 155.0, 65.0 | 0.44° | ~50 km | +| **EAS-22** | East Asia | 65.0, -15.0, 155.0, 65.0 | 0.22° | ~25 km | +| **EUR-44** | Europe | -44.0, 22.0, 65.0, 72.0 | 0.44° | ~50 km | +| **EUR-22** | Europe | -44.0, 22.0, 65.0, 72.0 | 0.22° | ~25 km | +| **EUR-11** | Europe | -44.0, 22.0, 65.0, 72.0 | 0.11° | ~12.5 km | +| **MED-44** | Mediterranean | -10.0, 30.0, 40.0, 48.0 | 0.44° | ~50 km | +| **MED-22** | Mediterranean | -10.0, 30.0, 40.0, 48.0 | 0.22° | ~25 km | +| **MNA-44** | Middle East & North Africa | -25.0, 0.0, 75.0, 50.0 | 0.44° | ~50 km | +| **MNA-22** | Middle East & North Africa | -25.0, 0.0, 75.0, 50.0 | 0.22° | ~25 km | +| **NAM-44** | North America | -172.0, 12.0, -35.0, 76.0 | 0.44° | ~50 km | +| **NAM-22** | North America | -172.0, 12.0, -35.0, 76.0 | 0.22° | ~25 km | +| **SAM-44** | South America | -93.0, -56.0, -25.0, 18.0 | 0.44° | ~50 km | +| **SAM-22** | South America | -93.0, -56.0, -25.0, 18.0 | 0.22° | ~25 km | +| **WAS-44** | South Asia (West Asia) | 20.0, -15.0, 115.0, 45.0 | 0.44° | ~50 km | +| **WAS-22** | South Asia (West Asia) | 20.0, -15.0, 115.0, 45.0 | 0.22° | ~25 km | +| **SEA-44** | Southeast Asia | 89.0, -15.0, 146.0, 27.0 | 0.44° | ~50 km | +| **SEA-22** | Southeast Asia | 89.0, -15.0, 146.0, 27.0 | 0.22° | ~25 km | + +### Domain Resolution Notes + +- **0.44°** (~50 km): Standard resolution, available for all domains +- **0.22°** (~25 km): High resolution, available for most domains +- **0.11°** (~12.5 km): Very high resolution, currently only available for Europe (EUR-11) + +### Usage with TerraKit + +When using CORDEX data with TerraKit's Climate Data Store connector, you can either: + +1. **Specify a bounding box** - TerraKit will automatically map it to the appropriate CORDEX domain: + ```python + dc.connector.find_data( + collection="projections-cordex-domains-single-levels", + date_start="2020-01-01", + date_end="2020-12-31", + bbox=[-10, 35, 30, 60] # Automatically mapped to EUR-44 or EUR-22 + ) + ``` + +2. **List available domains programmatically**: + + ```python + domains = dc.connector.list_cordex_domains() + ``` \ No newline at end of file diff --git a/docs/data_connectors.md b/docs/data_connectors.md new file mode 100644 index 0000000..56b1f8c --- /dev/null +++ b/docs/data_connectors.md @@ -0,0 +1,72 @@ +# TerraKit Data Connectors +Data connectors are classes which enable a user to search for data and query data from a particular data source using a common set of functions. The TerraKit Pipeline makes use of the Data Connectors, but they can also be used independently to explore and retrieve EO data. + +Each data connector has the following mandatory methods: + +* list_collections() +* find_data() +* get_data() + + +## Available collections +The following data connectors and associated collections are available: + +| Connectors | Collections | +| ----------------- | ----------- | +| sentinelhub | s2_l1c, dem, s1_grd, hls_l30, s2_l2a, hls_s30 | +| nasa_earthdata | HLSL30_2.0, HLSS30_2.0 | +| sentinel_aws | sentinel-2-l2a | +| climate_data_store| derived-era5-single-levels-daily-statistics, projections-cordex-domains-single-levels | +| IBMResearchSTAC | 'HLSS30', 'esa-sentinel-2A-msil1c', 'HLS_S30',, 'atmospheric-weather-era5', 'deforestation-umd', 'Radar-10min', 'tasmax-rcp85-land-cpm-uk-2.2km', 'vector-osm-power', 'ukcp18-land-cpm-uk-2.2km', 'treecovermaps-eudr', 'ch4' + more| +| TheWeatherCompany | weathercompany-daily-forecast | + +## Data connector access +Each data connector has a different access requirements. For example, connecting to SentinelHub and NASA EarthData, you will need to obtain credentials from each provider. Once these have been obtained, they can be added to a `.env` file at the root directory level using the following syntax: + +```.env +SH_CLIENT_ID="" +SH_CLIENT_SECRET="" +NASA_EARTH_BEARER_TOKEN="" +CDSAPI_KEY="" +``` + +### NASA Earthdata +To access NASA Earthdata, register for an Earthdata Login profile and requests a bearer token. [https://urs.earthdata.nasa.gov/profile](https://urs.earthdata.nasa.gov/profile) + +### Sentinel Hub +To access sentinel hub, register for an account and requests an OAuth client using the Sentinel Hub dashboard [https://www.planet.com](https://www.planet.com) + +### Sentinel AWS +Access sentinel AWS data is open and does not require any credentials. + +### Climate Data Store +Create an account at [https://cds.climate.copernicus.eu/](https://cds.climate.copernicus.eu/). Once created, find your API key under the `Profile` section. Each dataset may also require accepting the licence agreement. If this is the case, the first time a request is made, an error will be returned with the url to visit to accept the terms. + +Available collections include: +- [ERA5 post-processed daily statistics on single levels from 1940 to present](https://cds.climate.copernicus.eu/datasets/derived-era5-single-levels-daily-statistics?tab=overview) +- [CORDEX regional climate model data on single levels](https://cds.climate.copernicus.eu/datasets/projections-cordex-domains-single-levels?tab=overview) + +### The Weather Company +To access The Weather Company, register for an account and requests an API Key [https://www.weathercompany.com/weather-data-apis/](https://www.weathercompany.com/weather-data-apis/). Once you have an API key, set the following environment variable: + +``` +THE_WEATHER_COMPANY_API_KEY="" +``` + +### IBM Research STAC +Access IBM Research STAC is currently restricted to IBMers and partners. If you're elegible, you need to register for an IBM AppID account and set the following environment variables: + +``` +APPID_ISSUER= +APPID_USERNAME= +APPID_PASSWORD= +CLIENT_ID= +CLIENT_SECRET= +``` + +Please reach out the maintainers of this repo. + +IBMers don't need credentials to access the internal instance of the STAC service. + +## Try out +Data Connectors can be used outside the TerraKit Pipeline. Take a look at the [TerraKit: Easy geospatial data search and query](examples/terrakit_download.ipynb) notebook for more help getting started with TerraKit Data Connectors. \ No newline at end of file diff --git a/docs/download_data.md b/docs/download_data.md index 9b59cab..aacf786 100644 --- a/docs/download_data.md +++ b/docs/download_data.md @@ -149,72 +149,7 @@ The shapefile `{dataset_name}_labels.shp` must contain a `datetime` field and `g Flag to preserve shapefiles in the working directory once they have been used by the download data step. Downloaded files will not be removed. Set to `True` to ensure shapefiles remain in place. ## Data Connectors -Data connectors are classes which enable a user to search for data and query data from a particular data source using a common set of functions. Each data connector has the following mandatory methods: - -* list_collections() -* find_data() -* get_data() - - -## Available data connectors -The following data connectors and associated collections are available: - -| Connectors | Collections | -| ----------------- | ----------- | -| sentinelhub | s2_l1c, dem, s1_grd, hls_l30, s2_l2a, hls_s30 | -| nasa_earthdata | HLSL30_2.0, HLSS30_2.0 | -| sentinel_aws | sentinel-2-l2a | -| climate_data_store| derived-era5-single-levels-daily-statistics | -| IBMResearchSTAC | 'HLSS30', 'esa-sentinel-2A-msil1c', 'HLS_S30',, 'atmospheric-weather-era5', 'deforestation-umd', 'Radar-10min', 'tasmax-rcp85-land-cpm-uk-2.2km', 'vector-osm-power', 'ukcp18-land-cpm-uk-2.2km', 'treecovermaps-eudr', 'ch4' + more| -| TheWeatherCompany | weathercompany-daily-forecast | - -## Data connector access -Each data connector has a different access requirements. For example, connecting to SentinelHub and NASA EarthData, you will need to obtain credentials from each provider. Once these have been obtained, they can be added to a `.env` file at the root directory level using the following syntax: - -```.env -SH_CLIENT_ID="" -SH_CLIENT_SECRET="" -NASA_EARTH_BEARER_TOKEN="" -CDSAPI_KEY="" -``` - -### NASA Earthdata -To access NASA Earthdata, register for an Earthdata Login profile and requests a bearer token. [https://urs.earthdata.nasa.gov/profile](https://urs.earthdata.nasa.gov/profile) - -### Sentinel Hub -To access sentinel hub, register for an account and requests an OAuth client using the Sentinel Hub dashboard [https://www.planet.com](https://www.planet.com) - -### Sentinel AWS -Access sentinel AWS data is open and does not require any credentials. - -### Climate Data Store -Create an account at [https://cds.climate.copernicus.eu/](https://cds.climate.copernicus.eu/). Once created, find your API key under the `Profile` section. Each dataset may also require accepting the licence agreement. If this is the case, the first time a request is made, an error will be returned with the url to visit to accept the terms. - -Available collections include: -- [ERA5 post-processed daily statistics on single levels from 1940 to present](https://cds.climate.copernicus.eu/datasets/derived-era5-single-levels-daily-statistics?tab=overview) -- [CORDEX regional climate model data on single levels](https://cds.climate.copernicus.eu/datasets/projections-cordex-domains-single-levels?tab=overview) - -### The Weather Company -To access The Weather Company, register for an account and requests an API Key [https://www.weathercompany.com/weather-data-apis/](https://www.weathercompany.com/weather-data-apis/). Once you have an API key, set the following environment variable: - -``` -THE_WEATHER_COMPANY_API_KEY="" -``` - -### IBM Research STAC -Access IBM Research STAC is currently restricted to IBMers and partners. If you're elegible, you need to register for an IBM AppID account and set the following environment variables: - -``` -APPID_ISSUER= -APPID_USERNAME= -APPID_PASSWORD= -CLIENT_ID= -CLIENT_SECRET= -``` - -Please reach out the maintainers of this repo. - -IBMers don't need credentials to access the internal instance of the STAC service. +Data connectors are classes which enable a user to search for data and query data from a particular data source using a common set of functions. Check out the [TerraKit Data Connectors](#data-connectors) section for more information. ## Try out -Data Connectors can be used outside the TerraKit Pipeline. Take a look at the [TerraKit: Easy geospatial data search and query](examples/terrakit_download.ipynb) notebook for more help getting started with TerraKit Data Connectors. \ No newline at end of file +Try out the TerraKit data pipeline workflow using the [Terrakit: Labels to dataset pipeline](examples/labels_to_data.ipynb) notebook for more help getting started with TerraKit Data Connectors. \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 64369e5..782435d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -9,6 +9,9 @@ nav: - Tile and Chip: chip.md - Store: store.md - Upload: upload.md + - TerraKit DataConnectors: + - Data Connectors: data_connectors.md + - Cordex Domains: cordex_domains.md - Examples: - examples/labels_to_data.ipynb - examples/terrakit_download.ipynb From 7667cdf39d82654745bdf944e2b46d96acb89424 Mon Sep 17 00:00:00 2001 From: Rosie Lickorish Date: Wed, 11 Feb 2026 13:45:09 +0000 Subject: [PATCH 11/18] Include API docs for CDS --- docs/api/data_connectors/climate_data_store.md | 16 ++++++++++++++++ mkdocs.yml | 1 + 2 files changed, 17 insertions(+) create mode 100644 docs/api/data_connectors/climate_data_store.md diff --git a/docs/api/data_connectors/climate_data_store.md b/docs/api/data_connectors/climate_data_store.md new file mode 100644 index 0000000..319a05e --- /dev/null +++ b/docs/api/data_connectors/climate_data_store.md @@ -0,0 +1,16 @@ + +# Climate Data Store Data Connector Documentation + +Documentation for the `terrakit.download.data_connectors.climate_data_store` data connector module. + + +::: terrakit.download.data_connectors.climate_data_store + handler: python + options: + members: + - CDS + show_root_heading: True + show_source: true + show_object_full_path: false + show_signature: false + diff --git a/mkdocs.yml b/mkdocs.yml index 782435d..29c0467 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -31,6 +31,7 @@ nav: - Sentinel AWS: api/data_connectors/sentinelaws.md - Sentinel Hub: api/data_connectors/sentinel_hub.md - NASA Earthdata: api/data_connectors/nasa_earthdata.md + - Climate Data Store: api/data_connectors/climate_data_store.md - IBM Research STAC: api/data_connectors/ibmresearchstac.md - The Weather Company: api/data_connectors/theweathercompany.md - Download Transformations: api/download_transformations/transformations.md From 345e514f1d288551a542d0b02f0edd634976a8dd Mon Sep 17 00:00:00 2001 From: Rosie Lickorish Date: Wed, 11 Feb 2026 23:16:47 +0000 Subject: [PATCH 12/18] Implement CDS get_data for CORDEX and ERA5 datasets --- terrakit/download/collections.json | 2422 ++++++++++++----- .../data_connectors/climate_data_store.py | 687 ++++- tests/component_tests/download/conftest.py | 81 + .../test_climate_data_store.py | 128 +- .../era5_daily_statistics_test_data.zip | Bin 0 -> 15096 bytes 5 files changed, 2639 insertions(+), 679 deletions(-) create mode 100644 tests/resources/climate_data_store/era5_daily_statistics_test_data.zip diff --git a/terrakit/download/collections.json b/terrakit/download/collections.json index 4a13129..53a8384 100644 --- a/terrakit/download/collections.json +++ b/terrakit/download/collections.json @@ -717,7 +717,7 @@ }, "collection_name": "HLSS30_2.0", "collection_long_name": "HLS Sentinel-2 Multi-spectral Instrument Surface Reflectance Daily Global 30m v2.0", - "description": "The Harmonized Landsat Sentinel-2 (HLS) project provides consistent surface reflectance data from the Operational Land Imager (OLI) aboard the joint NASA/USGS Landsat 8 satellite and the Multi-Spectral Instrument (MSI) aboard Europe’s Copernicus Sentinel-2A, Sentinel-2B, and Sentinel-2C satellites. The combined measurement enables global observations of the land every 2–3 days at 30-meter (m) spatial resolution. The HLS project uses a set of algorithms to obtain seamless products from OLI and MSI that include atmospheric correction, cloud and cloud-shadow masking, spatial co-registration and common gridding, illumination and view angle normalization, and spectral bandpass adjustment.", + "description": "The Harmonized Landsat Sentinel-2 (HLS) project provides consistent surface reflectance data from the Operational Land Imager (OLI) aboard the joint NASA/USGS Landsat 8 satellite and the Multi-Spectral Instrument (MSI) aboard Europe's Copernicus Sentinel-2A, Sentinel-2B, and Sentinel-2C satellites. The combined measurement enables global observations of the land every 2–3 days at 30-meter (m) spatial resolution. The HLS project uses a set of algorithms to obtain seamless products from OLI and MSI that include atmospheric correction, cloud and cloud-shadow masking, spatial co-registration and common gridding, illumination and view angle normalization, and spectral bandpass adjustment.", "url": "https://www.earthdata.nasa.gov/data/catalog/lpcloud-hlss30-2.0", "id": "02964b17-7f4a-44a1-be98-0649391d5b73" }, @@ -817,7 +817,7 @@ }, "collection_name": "HLSL30_2.0", "collection_long_name": "HLS Landsat Operational Land Imager Surface Reflectance and TOA Brightness Daily Global 30m v2.0", - "description": "The Harmonized Landsat Sentinel-2 (HLS) project provides consistent surface reflectance (SR) and top of atmosphere (TOA) brightness data from a virtual constellation of satellite sensors. The Operational Land Imager (OLI) is housed aboard the joint NASA/USGS Landsat 8 and Landsat 9 satellites, while the Multi-Spectral Instrument (MSI) is mounted aboard Europe’s Copernicus Sentinel-2A, Sentinel-2B, and Sentinel-2C satellites. The combined measurement enables global observations of the land every 2–3 days at 30-meter (m) spatial resolution. The HLS project uses a set of algorithms to obtain seamless products from OLI and MSI that include atmospheric correction, cloud and cloud-shadow masking, spatial co-registration and common gridding, illumination and view angle normalization, and spectral bandpass adjustment.", + "description": "The Harmonized Landsat Sentinel-2 (HLS) project provides consistent surface reflectance (SR) and top of atmosphere (TOA) brightness data from a virtual constellation of satellite sensors. The Operational Land Imager (OLI) is housed aboard the joint NASA/USGS Landsat 8 and Landsat 9 satellites, while the Multi-Spectral Instrument (MSI) is mounted aboard Europe's Copernicus Sentinel-2A, Sentinel-2B, and Sentinel-2C satellites. The combined measurement enables global observations of the land every 2–3 days at 30-meter (m) spatial resolution. The HLS project uses a set of algorithms to obtain seamless products from OLI and MSI that include atmospheric correction, cloud and cloud-shadow masking, spatial co-registration and common gridding, illumination and view angle normalization, and spectral bandpass adjustment.", "url": "https://www.earthdata.nasa.gov/data/catalog/lpcloud-hlsl30-2.0", "id": "b2b5db61-d69c-4bb0-ad95-0025aa8d113f" }, @@ -875,7 +875,7 @@ "band_name": "narrative", "alt_names": [], "resolution": "10km", - "description": "The narrative forecast for the 24-hour period. When the max or min temperature is over 100°F or below 0°F, this field will revert to different wording. See the sample section for examples. When the specific temperature is provided rather than a range, the temperature will show along with the unit (°F) ." + "description": "The narrative forecast for the 24-hour period. When the max or min temperature is over 100 degF or below 0 degF, this field will revert to different wording. See the sample section for examples. When the specific temperature is provided rather than a range, the temperature will show along with the unit ( degF) ." }, { "band_name": "qpf", @@ -989,13 +989,13 @@ "band_name": "temperatureHeatIndex", "alt_names": [], "resolution": "10km", - "description": "An apparent temperature. It represents what the air temperature “feels like” on exposed human skin due to the combined effect of warm temperatures and high humidity. Units - Expressed in fahrenheit when units=e, expressed in celsius when units=m, s, or h." + "description": "An apparent temperature. It represents what the air temperature \"feels like\" on exposed human skin due to the combined effect of warm temperatures and high humidity. Units - Expressed in fahrenheit when units=e, expressed in celsius when units=m, s, or h." }, { "band_name": "temperatureWindChill", "alt_names": [], "resolution": "10km", - "description": "An apparent temperature. It represents what the air temperature “feels like” on exposed human skin due to the combined effect of the cold temperatures and wind speed. Units - Expressed in fahrenheit when units=e, expressed in celsius when units=m, s, or h." + "description": "An apparent temperature. It represents what the air temperature \"feels like\" on exposed human skin due to the combined effect of the cold temperatures and wind speed. Units - Expressed in fahrenheit when units=e, expressed in celsius when units=m, s, or h." }, { "band_name": "thunderCategory", @@ -1043,7 +1043,7 @@ "band_name": "windSpeed", "alt_names": [], "resolution": "10km", - "description": "The forecast of the maximum sustained wind speed over the 12 hour forecast period. The wind is treated as a vector; hence, winds must have direction and magnitude (speed). The wind information reported in the forecast corresponds to a 10-minute average called the sustained wind speed. Sudden or brief variations in the wind speed are known as “wind gusts” and are reported in a separate data field. Wind directions are always expressed as from whence the wind blows meaning that a North wind blows from North to South. If you face North in a North wind the wind is at your face. Face southward and the North wind is at your back." + "description": "The forecast of the maximum sustained wind speed over the 12 hour forecast period. The wind is treated as a vector; hence, winds must have direction and magnitude (speed). The wind information reported in the forecast corresponds to a 10-minute average called the sustained wind speed. Sudden or brief variations in the wind speed are known as \"wind gusts\" and are reported in a separate data field. Wind directions are always expressed as from whence the wind blows meaning that a North wind blows from North to South. If you face North in a North wind the wind is at your face. Face southward and the North wind is at your back." }, { "band_name": "wxPhraseLong", @@ -1069,1149 +1069,2337 @@ }, { "connector": "climate_data_store", - "collection_name": "projections-cordex-domains-single-levels" + "collection_name": "projections-cordex-domains-single-levels", + "collection_long_name": "CORDEX regional climate model data on single levels", + "modality_tag": "climate", + "resolution_m": null, + "description": "Regional climate projections from the Coordinated Regional Climate Downscaling Experiment (CORDEX). Provides high-resolution climate data for specific geographic domains at 0.11 deg, 0.22 deg, and 0.44 deg resolutions.", + "url": "https://cds.climate.copernicus.eu/datasets/projections-cordex-domains-single-levels?tab=overview", + "temporal_coverage": { + "start": "1950-01-01", + "end": "2100-12-31" + }, + "spatial_coverage": "Regional domains (see CORDEX domain specifications)", + "query_options": { + "domain": { + "title": "CORDEX Domain", + "description": "Geographic domain for regional climate data", + "schema": { + "enum": [ + "AFR-44", + "AFR-22", + "ANT-44", + "ARC-44", + "AUS-44", + "CAM-44", + "CAM-22", + "CAS-44", + "CAS-22", + "EAS-44", + "EAS-22", + "EUR-44", + "EUR-11", + "MED-44", + "MNA-44", + "MNA-22", + "NAM-44", + "NAM-22", + "SAM-44", + "SAM-20", + "SEA-44", + "SEA-22", + "WAS-44", + "WAS-22" + ], + "type": "string" + } + }, + "experiment": { + "title": "Climate Experiment", + "description": "Climate scenario or historical experiment", + "schema": { + "enum": [ + "historical", + "rcp_2_6", + "rcp_4_5", + "rcp_8_5" + ], + "type": "string", + "default": "historical" + } + }, + "gcm_model": { + "title": "Global Climate Model (GCM)", + "description": "Driving global climate model", + "schema": { + "enum": [ + "cnrm_cerfacs_cnrm_cm5", + "ichec_ec_earth", + "ipsl_ipsl_cm5a_mr", + "mohc_hadgem2_es", + "mpi_m_mpi_esm_lr", + "ncc_noresm1_m" + ], + "type": "string" + } + }, + "rcm_model": { + "title": "Regional Climate Model (RCM)", + "description": "Regional downscaling model", + "schema": { + "enum": [ + "clmcom_cclm4_8_17", + "clmcom_eth_cosmo_crclim", + "dmi_hirham5", + "gerics_remo2015", + "ictp_regcm4_6", + "knmi_racmo22e", + "smhi_rca4" + ], + "type": "string" + } + }, + "variable": { + "title": "Climate Variable", + "description": "Climate variable to download", + "schema": { + "enum": [ + "2m_temperature", + "2m_temperature_max", + "2m_temperature_min", + "precipitation", + "surface_pressure", + "10m_wind_speed", + "relative_humidity", + "surface_solar_radiation_downwards", + "surface_thermal_radiation_downwards" + ], + "type": "string" + } + }, + "temporal_aggregation": { + "title": "Temporal Aggregation", + "description": "Time averaging period", + "schema": { + "enum": [ + "daily", + "monthly", + "seasonal", + "annual" + ], + "type": "string", + "default": "daily" + } + } + }, + "bands": [ + { + "band_name": "2m_temperature", + "description": "Air temperature at 2 meters above surface", + "units": "K" + }, + { + "band_name": "2m_temperature_max", + "description": "Maximum air temperature at 2 meters", + "units": "K" + }, + { + "band_name": "2m_temperature_min", + "description": "Minimum air temperature at 2 meters", + "units": "K" + }, + { + "band_name": "precipitation", + "description": "Total precipitation", + "units": "kg m-2" + }, + { + "band_name": "surface_pressure", + "description": "Surface air pressure", + "units": "Pa" + }, + { + "band_name": "10m_wind_speed", + "description": "Wind speed at 10 meters above surface", + "units": "m s-1" + }, + { + "band_name": "relative_humidity", + "description": "Relative humidity", + "units": "%" + }, + { + "band_name": "surface_solar_radiation_downwards", + "description": "Downward solar radiation at surface", + "units": "W m-2" + }, + { + "band_name": "surface_thermal_radiation_downwards", + "description": "Downward thermal radiation at surface", + "units": "W m-2" + } + ], + "notes": [ + "CORDEX data is organized by geographic domains rather than bounding boxes", + "TerraKit automatically maps user bounding boxes to appropriate CORDEX domains", + "Resolution varies by domain: 0.11 deg (~12km), 0.22 deg (~25km), or 0.44 deg (~50km)", + "Historical data: 1950-2005, Future projections: 2006-2100", + "RCP scenarios: 2.6 (low emissions), 4.5 (medium), 8.5 (high emissions)" + ] }, { "connector": "climate_data_store", - "modality_tag": "", - "resolution_m": 10, + "modality_tag": "climate", + "resolution_m": null, "collection_name": "derived-era5-single-levels-daily-statistics", "collection_long_name": "ERA5 post-processed daily statistics on single levels from 1940 to present", - "description": "", + "description": "ERA5 is the fifth generation ECMWF atmospheric reanalysis of the global climate. This dataset provides daily statistics (mean, min, max, sum) computed from hourly ERA5 data at 0.25 deg resolution (~31km). Covers atmospheric, land-surface, and ocean-wave parameters from 1940 to present with ~5-day latency.", "url": "https://cds.climate.copernicus.eu/datasets/derived-era5-single-levels-daily-statistics?tab=overview", - "id": "", + "temporal_coverage": { + "start": "1940-01-01", + "end": "present" + }, + "spatial_coverage": "Global coverage at 0.25 deg resolution", "query_options": { "product_type": { - "title": "Product type", - "schema": { - "enum": ["reanalysis", "ensemble_mean", "ensemble_members"], - "type": "string", - "default": "reanalysis" - } - }, - "variable": { - "title": "Variable", - "schema": { - "type": "array", - "items": { - "enum": ["100m_u_component_of_wind", "100m_v_component_of_wind", "10m_u_component_of_neutral_wind", "10m_u_component_of_wind", "10m_v_component_of_neutral_wind", "10m_v_component_of_wind", "10m_wind_gust_since_previous_post_processing", "2m_dewpoint_temperature", "2m_temperature", "air_density_over_the_oceans", "angle_of_sub_gridscale_orography", "anisotropy_of_sub_gridscale_orography", "benjamin_feir_index", "boundary_layer_dissipation", "boundary_layer_height", "charnock", "clear_sky_direct_solar_radiation_at_surface", "cloud_base_height", "coefficient_of_drag_with_waves", "convective_available_potential_energy", "convective_inhibition", "convective_precipitation", "convective_rain_rate", "convective_snowfall", "convective_snowfall_rate_water_equivalent", "downward_uv_radiation_at_the_surface", "duct_base_height", "eastward_gravity_wave_surface_stress", "eastward_turbulent_surface_stress", "evaporation", "forecast_albedo", "forecast_logarithm_of_surface_roughness_for_heat", "forecast_surface_roughness", "free_convective_velocity_over_the_oceans", "friction_velocity", "geopotential", "gravity_wave_dissipation", "high_cloud_cover", "high_vegetation_cover", "ice_temperature_layer_1", "ice_temperature_layer_2", "ice_temperature_layer_3", "ice_temperature_layer_4", "instantaneous_10m_wind_gust", "instantaneous_eastward_turbulent_surface_stress", "instantaneous_large_scale_surface_precipitation_fraction", "instantaneous_moisture_flux", "instantaneous_northward_turbulent_surface_stress", "instantaneous_surface_sensible_heat_flux", "k_index", "lake_bottom_temperature", "lake_cover", "lake_depth", "lake_ice_depth", "lake_ice_temperature", "lake_mix_layer_depth", "lake_mix_layer_temperature", "lake_shape_factor", "lake_total_layer_temperature", "land_sea_mask", "large_scale_precipitation", "large_scale_precipitation_fraction", "large_scale_rain_rate", "large_scale_snowfall", "large_scale_snowfall_rate_water_equivalent", "leaf_area_index_high_vegetation", "leaf_area_index_low_vegetation", "low_cloud_cover", "low_vegetation_cover", "maximum_2m_temperature_since_previous_post_processing", "maximum_individual_wave_height", "maximum_total_precipitation_rate_since_previous_post_processing", "mean_boundary_layer_dissipation", "mean_convective_precipitation_rate", "mean_convective_snowfall_rate", "mean_direction_of_total_swell", "mean_direction_of_wind_waves", "mean_eastward_gravity_wave_surface_stress", "mean_eastward_turbulent_surface_stress", "mean_evaporation_rate", "mean_gravity_wave_dissipation", "mean_large_scale_precipitation_fraction", "mean_large_scale_precipitation_rate", "mean_large_scale_snowfall_rate", "mean_northward_gravity_wave_surface_stress", "mean_northward_turbulent_surface_stress", "mean_period_of_total_swell", "mean_period_of_wind_waves", "mean_potential_evaporation_rate", "mean_runoff_rate", "mean_sea_level_pressure", "mean_snow_evaporation_rate", "mean_snowfall_rate", "mean_snowmelt_rate", "mean_square_slope_of_waves", "mean_sub_surface_runoff_rate", "mean_surface_direct_short_wave_radiation_flux", "mean_surface_direct_short_wave_radiation_flux_clear_sky", "mean_surface_downward_long_wave_radiation_flux", "mean_surface_downward_long_wave_radiation_flux_clear_sky", "mean_surface_downward_short_wave_radiation_flux", "mean_surface_downward_short_wave_radiation_flux_clear_sky", "mean_surface_downward_uv_radiation_flux", "mean_surface_latent_heat_flux", "mean_surface_net_long_wave_radiation_flux", "mean_surface_net_long_wave_radiation_flux_clear_sky", "mean_surface_net_short_wave_radiation_flux", "mean_surface_net_short_wave_radiation_flux_clear_sky", "mean_surface_runoff_rate", "mean_surface_sensible_heat_flux", "mean_top_downward_short_wave_radiation_flux", "mean_top_net_long_wave_radiation_flux", "mean_top_net_long_wave_radiation_flux_clear_sky", "mean_top_net_short_wave_radiation_flux", "mean_top_net_short_wave_radiation_flux_clear_sky", "mean_total_precipitation_rate", "mean_vertical_gradient_of_refractivity_inside_trapping_layer", "mean_vertically_integrated_moisture_divergence", "mean_wave_direction", "mean_wave_direction_of_first_swell_partition", "mean_wave_direction_of_second_swell_partition", "mean_wave_direction_of_third_swell_partition", "mean_wave_period", "mean_wave_period_based_on_first_moment", "mean_wave_period_based_on_first_moment_for_swell", "mean_wave_period_based_on_first_moment_for_wind_waves", "mean_wave_period_based_on_second_moment_for_swell", "mean_wave_period_based_on_second_moment_for_wind_waves", "mean_wave_period_of_first_swell_partition", "mean_wave_period_of_second_swell_partition", "mean_wave_period_of_third_swell_partition", "mean_zero_crossing_wave_period", "medium_cloud_cover", "minimum_2m_temperature_since_previous_post_processing", "minimum_total_precipitation_rate_since_previous_post_processing", "minimum_vertical_gradient_of_refractivity_inside_trapping_layer", "model_bathymetry", "near_ir_albedo_for_diffuse_radiation", "near_ir_albedo_for_direct_radiation", "normalized_energy_flux_into_ocean", "normalized_energy_flux_into_waves", "normalized_stress_into_ocean", "northward_gravity_wave_surface_stress", "northward_turbulent_surface_stress", "ocean_surface_stress_equivalent_10m_neutral_wind_direction", "ocean_surface_stress_equivalent_10m_neutral_wind_speed", "peak_wave_period", "period_corresponding_to_maximum_individual_wave_height", "potential_evaporation", "precipitation_type", "runoff", "sea_ice_cover", "sea_surface_temperature", "significant_height_of_combined_wind_waves_and_swell", "significant_height_of_total_swell", "significant_height_of_wind_waves", "significant_wave_height_of_first_swell_partition", "significant_wave_height_of_second_swell_partition", "significant_wave_height_of_third_swell_partition", "skin_reservoir_content", "skin_temperature", "slope_of_sub_gridscale_orography", "snow_albedo", "snow_density", "snow_depth", "snow_evaporation", "snowfall", "snowmelt", "soil_temperature_level_1", "soil_temperature_level_2", "soil_temperature_level_3", "soil_temperature_level_4", "soil_type", "standard_deviation_of_filtered_subgrid_orography", "standard_deviation_of_orography", "sub_surface_runoff", "surface_latent_heat_flux", "surface_net_solar_radiation", "surface_net_solar_radiation_clear_sky", "surface_net_thermal_radiation", "surface_net_thermal_radiation_clear_sky", "surface_pressure", "surface_runoff", "surface_sensible_heat_flux", "surface_solar_radiation_downward_clear_sky", "surface_solar_radiation_downwards", "surface_thermal_radiation_downward_clear_sky", "surface_thermal_radiation_downwards", "temperature_of_snow_layer", "toa_incident_solar_radiation", "top_net_solar_radiation", "top_net_solar_radiation_clear_sky", "top_net_thermal_radiation", "top_net_thermal_radiation_clear_sky", "total_cloud_cover", "total_column_cloud_ice_water", "total_column_cloud_liquid_water", "total_column_ozone", "total_column_rain_water", "total_column_snow_water", "total_column_supercooled_liquid_water", "total_column_water", "total_column_water_vapour", "total_precipitation", "total_sky_direct_solar_radiation_at_surface", "total_totals_index", "trapping_layer_base_height", "trapping_layer_top_height", "type_of_high_vegetation", "type_of_low_vegetation", "u_component_stokes_drift", "uv_visible_albedo_for_diffuse_radiation", "uv_visible_albedo_for_direct_radiation", "v_component_stokes_drift", "vertical_integral_of_divergence_of_cloud_frozen_water_flux", "vertical_integral_of_divergence_of_cloud_liquid_water_flux", "vertical_integral_of_divergence_of_geopotential_flux", "vertical_integral_of_divergence_of_kinetic_energy_flux", "vertical_integral_of_divergence_of_mass_flux", "vertical_integral_of_divergence_of_moisture_flux", "vertical_integral_of_divergence_of_ozone_flux", "vertical_integral_of_divergence_of_thermal_energy_flux", "vertical_integral_of_divergence_of_total_energy_flux", "vertical_integral_of_eastward_cloud_frozen_water_flux", "vertical_integral_of_eastward_cloud_liquid_water_flux", "vertical_integral_of_eastward_geopotential_flux", "vertical_integral_of_eastward_heat_flux", "vertical_integral_of_eastward_kinetic_energy_flux", "vertical_integral_of_eastward_mass_flux", "vertical_integral_of_eastward_ozone_flux", "vertical_integral_of_eastward_total_energy_flux", "vertical_integral_of_eastward_water_vapour_flux", "vertical_integral_of_energy_conversion", "vertical_integral_of_kinetic_energy", "vertical_integral_of_mass_of_atmosphere", "vertical_integral_of_mass_tendency", "vertical_integral_of_northward_cloud_frozen_water_flux", "vertical_integral_of_northward_cloud_liquid_water_flux", "vertical_integral_of_northward_geopotential_flux", "vertical_integral_of_northward_heat_flux", "vertical_integral_of_northward_kinetic_energy_flux", "vertical_integral_of_northward_mass_flux", "vertical_integral_of_northward_ozone_flux", "vertical_integral_of_northward_total_energy_flux", "vertical_integral_of_northward_water_vapour_flux", "vertical_integral_of_potential_and_internal_energy", "vertical_integral_of_potential_internal_and_latent_energy", "vertical_integral_of_temperature", "vertical_integral_of_thermal_energy", "vertical_integral_of_total_energy", "vertically_integrated_moisture_divergence", "volumetric_soil_water_layer_1", "volumetric_soil_water_layer_2", "volumetric_soil_water_layer_3", "volumetric_soil_water_layer_4", "wave_spectral_directional_width", "wave_spectral_directional_width_for_swell", "wave_spectral_directional_width_for_wind_waves", "wave_spectral_kurtosis", "wave_spectral_peakedness", "wave_spectral_skewness", "zero_degree_level"], - "type": "string" - } - } - }, - "year": { - "title": "Year", - "schema": { - "enum": ["1940", "1941", "1942", "1943", "1944", "1945", "1946", "1947", "1948", "1949", "1950", "1951", "1952", "1953", "1954", "1955", "1956", "1957", "1958", "1959", "1960", "1961", "1962", "1963", "1964", "1965", "1966", "1967", "1968", "1969", "1970", "1971", "1972", "1973", "1974", "1975", "1976", "1977", "1978", "1979", "1980", "1981", "1982", "1983", "1984", "1985", "1986", "1987", "1988", "1989", "1990", "1991", "1992", "1993", "1994", "1995", "1996", "1997", "1998", "1999", "2000", "2001", "2002", "2003", "2004", "2005", "2006", "2007", "2008", "2009", "2010", "2011", "2012", "2013", "2014", "2015", "2016", "2017", "2018", "2019", "2020", "2021", "2022", "2023", "2024", "2025", "2026"], + "title": "Product type", + "schema": { + "enum": [ + "reanalysis", + "ensemble_mean", + "ensemble_members" + ], + "type": "string", + "default": "reanalysis" + } + }, + "variable": { + "title": "Variable", + "schema": { + "type": "array", + "items": { + "enum": [ + "100m_u_component_of_wind", + "100m_v_component_of_wind", + "10m_u_component_of_neutral_wind", + "10m_u_component_of_wind", + "10m_v_component_of_neutral_wind", + "10m_v_component_of_wind", + "10m_wind_gust_since_previous_post_processing", + "2m_dewpoint_temperature", + "2m_temperature", + "air_density_over_the_oceans", + "angle_of_sub_gridscale_orography", + "anisotropy_of_sub_gridscale_orography", + "benjamin_feir_index", + "boundary_layer_dissipation", + "boundary_layer_height", + "charnock", + "clear_sky_direct_solar_radiation_at_surface", + "cloud_base_height", + "coefficient_of_drag_with_waves", + "convective_available_potential_energy", + "convective_inhibition", + "convective_precipitation", + "convective_rain_rate", + "convective_snowfall", + "convective_snowfall_rate_water_equivalent", + "downward_uv_radiation_at_the_surface", + "duct_base_height", + "eastward_gravity_wave_surface_stress", + "eastward_turbulent_surface_stress", + "evaporation", + "forecast_albedo", + "forecast_logarithm_of_surface_roughness_for_heat", + "forecast_surface_roughness", + "free_convective_velocity_over_the_oceans", + "friction_velocity", + "geopotential", + "gravity_wave_dissipation", + "high_cloud_cover", + "high_vegetation_cover", + "ice_temperature_layer_1", + "ice_temperature_layer_2", + "ice_temperature_layer_3", + "ice_temperature_layer_4", + "instantaneous_10m_wind_gust", + "instantaneous_eastward_turbulent_surface_stress", + "instantaneous_large_scale_surface_precipitation_fraction", + "instantaneous_moisture_flux", + "instantaneous_northward_turbulent_surface_stress", + "instantaneous_surface_sensible_heat_flux", + "k_index", + "lake_bottom_temperature", + "lake_cover", + "lake_depth", + "lake_ice_depth", + "lake_ice_temperature", + "lake_mix_layer_depth", + "lake_mix_layer_temperature", + "lake_shape_factor", + "lake_total_layer_temperature", + "land_sea_mask", + "large_scale_precipitation", + "large_scale_precipitation_fraction", + "large_scale_rain_rate", + "large_scale_snowfall", + "large_scale_snowfall_rate_water_equivalent", + "leaf_area_index_high_vegetation", + "leaf_area_index_low_vegetation", + "low_cloud_cover", + "low_vegetation_cover", + "maximum_2m_temperature_since_previous_post_processing", + "maximum_individual_wave_height", + "maximum_total_precipitation_rate_since_previous_post_processing", + "mean_boundary_layer_dissipation", + "mean_convective_precipitation_rate", + "mean_convective_snowfall_rate", + "mean_direction_of_total_swell", + "mean_direction_of_wind_waves", + "mean_eastward_gravity_wave_surface_stress", + "mean_eastward_turbulent_surface_stress", + "mean_evaporation_rate", + "mean_gravity_wave_dissipation", + "mean_large_scale_precipitation_fraction", + "mean_large_scale_precipitation_rate", + "mean_large_scale_snowfall_rate", + "mean_northward_gravity_wave_surface_stress", + "mean_northward_turbulent_surface_stress", + "mean_period_of_total_swell", + "mean_period_of_wind_waves", + "mean_potential_evaporation_rate", + "mean_runoff_rate", + "mean_sea_level_pressure", + "mean_snow_evaporation_rate", + "mean_snowfall_rate", + "mean_snowmelt_rate", + "mean_square_slope_of_waves", + "mean_sub_surface_runoff_rate", + "mean_surface_direct_short_wave_radiation_flux", + "mean_surface_direct_short_wave_radiation_flux_clear_sky", + "mean_surface_downward_long_wave_radiation_flux", + "mean_surface_downward_long_wave_radiation_flux_clear_sky", + "mean_surface_downward_short_wave_radiation_flux", + "mean_surface_downward_short_wave_radiation_flux_clear_sky", + "mean_surface_downward_uv_radiation_flux", + "mean_surface_latent_heat_flux", + "mean_surface_net_long_wave_radiation_flux", + "mean_surface_net_long_wave_radiation_flux_clear_sky", + "mean_surface_net_short_wave_radiation_flux", + "mean_surface_net_short_wave_radiation_flux_clear_sky", + "mean_surface_runoff_rate", + "mean_surface_sensible_heat_flux", + "mean_top_downward_short_wave_radiation_flux", + "mean_top_net_long_wave_radiation_flux", + "mean_top_net_long_wave_radiation_flux_clear_sky", + "mean_top_net_short_wave_radiation_flux", + "mean_top_net_short_wave_radiation_flux_clear_sky", + "mean_total_precipitation_rate", + "mean_vertical_gradient_of_refractivity_inside_trapping_layer", + "mean_vertically_integrated_moisture_divergence", + "mean_wave_direction", + "mean_wave_direction_of_first_swell_partition", + "mean_wave_direction_of_second_swell_partition", + "mean_wave_direction_of_third_swell_partition", + "mean_wave_period", + "mean_wave_period_based_on_first_moment", + "mean_wave_period_based_on_first_moment_for_swell", + "mean_wave_period_based_on_first_moment_for_wind_waves", + "mean_wave_period_based_on_second_moment_for_swell", + "mean_wave_period_based_on_second_moment_for_wind_waves", + "mean_wave_period_of_first_swell_partition", + "mean_wave_period_of_second_swell_partition", + "mean_wave_period_of_third_swell_partition", + "mean_zero_crossing_wave_period", + "medium_cloud_cover", + "minimum_2m_temperature_since_previous_post_processing", + "minimum_total_precipitation_rate_since_previous_post_processing", + "minimum_vertical_gradient_of_refractivity_inside_trapping_layer", + "model_bathymetry", + "near_ir_albedo_for_diffuse_radiation", + "near_ir_albedo_for_direct_radiation", + "normalized_energy_flux_into_ocean", + "normalized_energy_flux_into_waves", + "normalized_stress_into_ocean", + "northward_gravity_wave_surface_stress", + "northward_turbulent_surface_stress", + "ocean_surface_stress_equivalent_10m_neutral_wind_direction", + "ocean_surface_stress_equivalent_10m_neutral_wind_speed", + "peak_wave_period", + "period_corresponding_to_maximum_individual_wave_height", + "potential_evaporation", + "precipitation_type", + "runoff", + "sea_ice_cover", + "sea_surface_temperature", + "significant_height_of_combined_wind_waves_and_swell", + "significant_height_of_total_swell", + "significant_height_of_wind_waves", + "significant_wave_height_of_first_swell_partition", + "significant_wave_height_of_second_swell_partition", + "significant_wave_height_of_third_swell_partition", + "skin_reservoir_content", + "skin_temperature", + "slope_of_sub_gridscale_orography", + "snow_albedo", + "snow_density", + "snow_depth", + "snow_evaporation", + "snowfall", + "snowmelt", + "soil_temperature_level_1", + "soil_temperature_level_2", + "soil_temperature_level_3", + "soil_temperature_level_4", + "soil_type", + "standard_deviation_of_filtered_subgrid_orography", + "standard_deviation_of_orography", + "sub_surface_runoff", + "surface_latent_heat_flux", + "surface_net_solar_radiation", + "surface_net_solar_radiation_clear_sky", + "surface_net_thermal_radiation", + "surface_net_thermal_radiation_clear_sky", + "surface_pressure", + "surface_runoff", + "surface_sensible_heat_flux", + "surface_solar_radiation_downward_clear_sky", + "surface_solar_radiation_downwards", + "surface_thermal_radiation_downward_clear_sky", + "surface_thermal_radiation_downwards", + "temperature_of_snow_layer", + "toa_incident_solar_radiation", + "top_net_solar_radiation", + "top_net_solar_radiation_clear_sky", + "top_net_thermal_radiation", + "top_net_thermal_radiation_clear_sky", + "total_cloud_cover", + "total_column_cloud_ice_water", + "total_column_cloud_liquid_water", + "total_column_ozone", + "total_column_rain_water", + "total_column_snow_water", + "total_column_supercooled_liquid_water", + "total_column_water", + "total_column_water_vapour", + "total_precipitation", + "total_sky_direct_solar_radiation_at_surface", + "total_totals_index", + "trapping_layer_base_height", + "trapping_layer_top_height", + "type_of_high_vegetation", + "type_of_low_vegetation", + "u_component_stokes_drift", + "uv_visible_albedo_for_diffuse_radiation", + "uv_visible_albedo_for_direct_radiation", + "v_component_stokes_drift", + "vertical_integral_of_divergence_of_cloud_frozen_water_flux", + "vertical_integral_of_divergence_of_cloud_liquid_water_flux", + "vertical_integral_of_divergence_of_geopotential_flux", + "vertical_integral_of_divergence_of_kinetic_energy_flux", + "vertical_integral_of_divergence_of_mass_flux", + "vertical_integral_of_divergence_of_moisture_flux", + "vertical_integral_of_divergence_of_ozone_flux", + "vertical_integral_of_divergence_of_thermal_energy_flux", + "vertical_integral_of_divergence_of_total_energy_flux", + "vertical_integral_of_eastward_cloud_frozen_water_flux", + "vertical_integral_of_eastward_cloud_liquid_water_flux", + "vertical_integral_of_eastward_geopotential_flux", + "vertical_integral_of_eastward_heat_flux", + "vertical_integral_of_eastward_kinetic_energy_flux", + "vertical_integral_of_eastward_mass_flux", + "vertical_integral_of_eastward_ozone_flux", + "vertical_integral_of_eastward_total_energy_flux", + "vertical_integral_of_eastward_water_vapour_flux", + "vertical_integral_of_energy_conversion", + "vertical_integral_of_kinetic_energy", + "vertical_integral_of_mass_of_atmosphere", + "vertical_integral_of_mass_tendency", + "vertical_integral_of_northward_cloud_frozen_water_flux", + "vertical_integral_of_northward_cloud_liquid_water_flux", + "vertical_integral_of_northward_geopotential_flux", + "vertical_integral_of_northward_heat_flux", + "vertical_integral_of_northward_kinetic_energy_flux", + "vertical_integral_of_northward_mass_flux", + "vertical_integral_of_northward_ozone_flux", + "vertical_integral_of_northward_total_energy_flux", + "vertical_integral_of_northward_water_vapour_flux", + "vertical_integral_of_potential_and_internal_energy", + "vertical_integral_of_potential_internal_and_latent_energy", + "vertical_integral_of_temperature", + "vertical_integral_of_thermal_energy", + "vertical_integral_of_total_energy", + "vertically_integrated_moisture_divergence", + "volumetric_soil_water_layer_1", + "volumetric_soil_water_layer_2", + "volumetric_soil_water_layer_3", + "volumetric_soil_water_layer_4", + "wave_spectral_directional_width", + "wave_spectral_directional_width_for_swell", + "wave_spectral_directional_width_for_wind_waves", + "wave_spectral_kurtosis", + "wave_spectral_peakedness", + "wave_spectral_skewness", + "zero_degree_level" + ], "type": "string" } - }, - "month": { - "title": "Month", - "schema": { - "type": "array", - "items": { - "enum": ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"], - "type": "string" - } - } - }, - "day": { - "title": "Day", - "schema": { - "type": "array", - "items": { - "enum": ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31"], - "type": "string" - } - } - }, - "daily_statistic": { - "title": "Daily statistic", - "schema": { - "enum": ["daily_sum", "daily_mean", "daily_maximum", "daily_minimum"], - "type": "string", - "default": "daily_mean" - } - }, - "time_zone": { - "title": "Time zone", - "schema": { - "enum": ["utc+00:00", "utc+01:00", "utc+02:00", "utc+03:00", "utc+04:00", "utc+05:00", "utc+06:00", "utc+07:00", "utc+08:00", "utc+09:00", "utc+10:00", "utc+11:00", "utc+12:00", "utc+13:00", "utc+14:00", "utc-01:00", "utc-02:00", "utc-03:00", "utc-04:00", "utc-05:00", "utc-06:00", "utc-07:00", "utc-08:00", "utc-09:00", "utc-10:00", "utc-11:00", "utc-12:00"], - "type": "string", - "default": "utc+00:00" + } + }, + "year": { + "title": "Year", + "schema": { + "enum": [ + "1940", + "1941", + "1942", + "1943", + "1944", + "1945", + "1946", + "1947", + "1948", + "1949", + "1950", + "1951", + "1952", + "1953", + "1954", + "1955", + "1956", + "1957", + "1958", + "1959", + "1960", + "1961", + "1962", + "1963", + "1964", + "1965", + "1966", + "1967", + "1968", + "1969", + "1970", + "1971", + "1972", + "1973", + "1974", + "1975", + "1976", + "1977", + "1978", + "1979", + "1980", + "1981", + "1982", + "1983", + "1984", + "1985", + "1986", + "1987", + "1988", + "1989", + "1990", + "1991", + "1992", + "1993", + "1994", + "1995", + "1996", + "1997", + "1998", + "1999", + "2000", + "2001", + "2002", + "2003", + "2004", + "2005", + "2006", + "2007", + "2008", + "2009", + "2010", + "2011", + "2012", + "2013", + "2014", + "2015", + "2016", + "2017", + "2018", + "2019", + "2020", + "2021", + "2022", + "2023", + "2024", + "2025", + "2026" + ], + "type": "string" + } + }, + "month": { + "title": "Month", + "schema": { + "type": "array", + "items": { + "enum": [ + "01", + "02", + "03", + "04", + "05", + "06", + "07", + "08", + "09", + "10", + "11", + "12" + ], + "type": "string" } - }, - "frequency": { - "title": "Frequency", - "schema": { - "enum": ["1_hourly", "3_hourly", "6_hourly"], + } + }, + "day": { + "title": "Day", + "schema": { + "type": "array", + "items": { + "enum": [ + "01", + "02", + "03", + "04", + "05", + "06", + "07", + "08", + "09", + "10", + "11", + "12", + "13", + "14", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31" + ], "type": "string" } - }, - "area": { - "title": "Sub-region extraction", - "schema": { - "maxItems": 4, - "minItems": 4, - "type": "array", - "default": [90, -180, -90, 180], - "items": { - "type": "number" - } + } + }, + "daily_statistic": { + "title": "Daily statistic", + "schema": { + "enum": [ + "daily_sum", + "daily_mean", + "daily_maximum", + "daily_minimum" + ], + "type": "string", + "default": "daily_mean" + } + }, + "time_zone": { + "title": "Time zone", + "schema": { + "enum": [ + "utc+00:00", + "utc+01:00", + "utc+02:00", + "utc+03:00", + "utc+04:00", + "utc+05:00", + "utc+06:00", + "utc+07:00", + "utc+08:00", + "utc+09:00", + "utc+10:00", + "utc+11:00", + "utc+12:00", + "utc+13:00", + "utc+14:00", + "utc-01:00", + "utc-02:00", + "utc-03:00", + "utc-04:00", + "utc-05:00", + "utc-06:00", + "utc-07:00", + "utc-08:00", + "utc-09:00", + "utc-10:00", + "utc-11:00", + "utc-12:00" + ], + "type": "string", + "default": "utc+00:00" + } + }, + "frequency": { + "title": "Frequency", + "schema": { + "enum": [ + "1_hourly", + "3_hourly", + "6_hourly" + ], + "type": "string" + } + }, + "area": { + "title": "Sub-region extraction", + "schema": { + "maxItems": 4, + "minItems": 4, + "type": "array", + "default": [ + 90, + -180, + -90, + 180 + ], + "items": { + "type": "number" } } + } }, "bands": [ - {"band_name": "100m_u_component_of_wind", + { + "band_name": "100m_u_component_of_wind", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "100m_v_component_of_wind", + "description": "" + }, + { + "band_name": "100m_v_component_of_wind", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "10m_u_component_of_neutral_wind", + "description": "" + }, + { + "band_name": "10m_u_component_of_neutral_wind", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "10m_u_component_of_wind", - "alt_names": [], + "description": "" + }, + { + "band_name": "10m_u_component_of_wind", + "alt_names": [ + "u10" + ], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "10m_v_component_of_neutral_wind", + "description": "Eastward component of wind at 10 meters above surface", + "units": "m s-1" + }, + { + "band_name": "10m_v_component_of_neutral_wind", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "10m_v_component_of_wind", - "alt_names": [], + "description": "Northward component of neutral wind at 10 meters", + "units": "m s-1" + }, + { + "band_name": "10m_v_component_of_wind", + "alt_names": [ + "v10" + ], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "10m_wind_gust_since_previous_post_processing", + "description": "Northward component of wind at 10 meters above surface", + "units": "m s-1" + }, + { + "band_name": "10m_wind_gust_since_previous_post_processing", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "2m_dewpoint_temperature", - "alt_names": [], + "description": "" + }, + { + "band_name": "2m_dewpoint_temperature", + "alt_names": [ + "d2m" + ], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "2m_temperature", - "alt_names": [], + "description": "Dewpoint temperature at 2 meters above surface", + "units": "K" + }, + { + "band_name": "2m_temperature", + "alt_names": [ + "t2m", + "temp_2m" + ], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "air_density_over_the_oceans", + "description": "Air temperature at 2 meters above surface", + "units": "K" + }, + { + "band_name": "air_density_over_the_oceans", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "angle_of_sub_gridscale_orography", + "description": "" + }, + { + "band_name": "angle_of_sub_gridscale_orography", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "anisotropy_of_sub_gridscale_orography", + "description": "" + }, + { + "band_name": "anisotropy_of_sub_gridscale_orography", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "benjamin_feir_index", + "description": "" + }, + { + "band_name": "benjamin_feir_index", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "boundary_layer_dissipation", + "description": "" + }, + { + "band_name": "boundary_layer_dissipation", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "boundary_layer_height", + "description": "" + }, + { + "band_name": "boundary_layer_height", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "charnock", + "description": "" + }, + { + "band_name": "charnock", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "clear_sky_direct_solar_radiation_at_surface", + "description": "" + }, + { + "band_name": "clear_sky_direct_solar_radiation_at_surface", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "cloud_base_height", + "description": "" + }, + { + "band_name": "cloud_base_height", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "coefficient_of_drag_with_waves", + "description": "" + }, + { + "band_name": "coefficient_of_drag_with_waves", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "convective_available_potential_energy", + "description": "" + }, + { + "band_name": "convective_available_potential_energy", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "convective_inhibition", + "description": "" + }, + { + "band_name": "convective_inhibition", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "convective_precipitation", + "description": "" + }, + { + "band_name": "convective_precipitation", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "convective_rain_rate", + "description": "" + }, + { + "band_name": "convective_rain_rate", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "convective_snowfall", + "description": "" + }, + { + "band_name": "convective_snowfall", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "convective_snowfall_rate_water_equivalent", + "description": "" + }, + { + "band_name": "convective_snowfall_rate_water_equivalent", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "downward_uv_radiation_at_the_surface", + "description": "" + }, + { + "band_name": "downward_uv_radiation_at_the_surface", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "duct_base_height", + "description": "" + }, + { + "band_name": "duct_base_height", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "eastward_gravity_wave_surface_stress", + "description": "" + }, + { + "band_name": "eastward_gravity_wave_surface_stress", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "eastward_turbulent_surface_stress", + "description": "" + }, + { + "band_name": "eastward_turbulent_surface_stress", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "evaporation", + "description": "" + }, + { + "band_name": "evaporation", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "forecast_albedo", + "description": "" + }, + { + "band_name": "forecast_albedo", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "forecast_logarithm_of_surface_roughness_for_heat", + "description": "" + }, + { + "band_name": "forecast_logarithm_of_surface_roughness_for_heat", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "forecast_surface_roughness", + "description": "" + }, + { + "band_name": "forecast_surface_roughness", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "free_convective_velocity_over_the_oceans", + "description": "" + }, + { + "band_name": "free_convective_velocity_over_the_oceans", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "friction_velocity", + "description": "" + }, + { + "band_name": "friction_velocity", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "geopotential", + "description": "" + }, + { + "band_name": "geopotential", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "gravity_wave_dissipation", + "description": "" + }, + { + "band_name": "gravity_wave_dissipation", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "high_cloud_cover", + "description": "" + }, + { + "band_name": "high_cloud_cover", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "high_vegetation_cover", + "description": "" + }, + { + "band_name": "high_vegetation_cover", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "ice_temperature_layer_1", + "description": "" + }, + { + "band_name": "ice_temperature_layer_1", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "ice_temperature_layer_2", + "description": "" + }, + { + "band_name": "ice_temperature_layer_2", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "ice_temperature_layer_3", + "description": "" + }, + { + "band_name": "ice_temperature_layer_3", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "ice_temperature_layer_4", + "description": "" + }, + { + "band_name": "ice_temperature_layer_4", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "instantaneous_10m_wind_gust", + "description": "" + }, + { + "band_name": "instantaneous_10m_wind_gust", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "instantaneous_eastward_turbulent_surface_stress", + "description": "" + }, + { + "band_name": "instantaneous_eastward_turbulent_surface_stress", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "instantaneous_large_scale_surface_precipitation_fraction", + "description": "" + }, + { + "band_name": "instantaneous_large_scale_surface_precipitation_fraction", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "instantaneous_moisture_flux", + "description": "" + }, + { + "band_name": "instantaneous_moisture_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "instantaneous_northward_turbulent_surface_stress", + "description": "" + }, + { + "band_name": "instantaneous_northward_turbulent_surface_stress", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "instantaneous_surface_sensible_heat_flux", + "description": "" + }, + { + "band_name": "instantaneous_surface_sensible_heat_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "k_index", + "description": "" + }, + { + "band_name": "k_index", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "lake_bottom_temperature", + "description": "" + }, + { + "band_name": "lake_bottom_temperature", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "lake_cover", + "description": "" + }, + { + "band_name": "lake_cover", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "lake_depth", + "description": "" + }, + { + "band_name": "lake_depth", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "lake_ice_depth", + "description": "" + }, + { + "band_name": "lake_ice_depth", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "lake_ice_temperature", + "description": "" + }, + { + "band_name": "lake_ice_temperature", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "lake_mix_layer_depth", + "description": "" + }, + { + "band_name": "lake_mix_layer_depth", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "lake_mix_layer_temperature", + "description": "" + }, + { + "band_name": "lake_mix_layer_temperature", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "lake_shape_factor", + "description": "" + }, + { + "band_name": "lake_shape_factor", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "lake_total_layer_temperature", + "description": "" + }, + { + "band_name": "lake_total_layer_temperature", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "land_sea_mask", + "description": "" + }, + { + "band_name": "land_sea_mask", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "large_scale_precipitation", + "description": "" + }, + { + "band_name": "large_scale_precipitation", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "large_scale_precipitation_fraction", + "description": "" + }, + { + "band_name": "large_scale_precipitation_fraction", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "large_scale_rain_rate", + "description": "" + }, + { + "band_name": "large_scale_rain_rate", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "large_scale_snowfall", + "description": "" + }, + { + "band_name": "large_scale_snowfall", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "large_scale_snowfall_rate_water_equivalent", + "description": "" + }, + { + "band_name": "large_scale_snowfall_rate_water_equivalent", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "leaf_area_index_high_vegetation", + "description": "" + }, + { + "band_name": "leaf_area_index_high_vegetation", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "leaf_area_index_low_vegetation", + "description": "" + }, + { + "band_name": "leaf_area_index_low_vegetation", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "low_cloud_cover", + "description": "" + }, + { + "band_name": "low_cloud_cover", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "low_vegetation_cover", + "description": "" + }, + { + "band_name": "low_vegetation_cover", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "maximum_2m_temperature_since_previous_post_processing", + "description": "" + }, + { + "band_name": "maximum_2m_temperature_since_previous_post_processing", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "maximum_individual_wave_height", + "description": "" + }, + { + "band_name": "maximum_individual_wave_height", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "maximum_total_precipitation_rate_since_previous_post_processing", + "description": "" + }, + { + "band_name": "maximum_total_precipitation_rate_since_previous_post_processing", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_boundary_layer_dissipation", + "description": "" + }, + { + "band_name": "mean_boundary_layer_dissipation", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_convective_precipitation_rate", + "description": "" + }, + { + "band_name": "mean_convective_precipitation_rate", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_convective_snowfall_rate", + "description": "" + }, + { + "band_name": "mean_convective_snowfall_rate", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_direction_of_total_swell", + "description": "" + }, + { + "band_name": "mean_direction_of_total_swell", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_direction_of_wind_waves", + "description": "" + }, + { + "band_name": "mean_direction_of_wind_waves", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_eastward_gravity_wave_surface_stress", + "description": "" + }, + { + "band_name": "mean_eastward_gravity_wave_surface_stress", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_eastward_turbulent_surface_stress", + "description": "" + }, + { + "band_name": "mean_eastward_turbulent_surface_stress", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_evaporation_rate", + "description": "" + }, + { + "band_name": "mean_evaporation_rate", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_gravity_wave_dissipation", + "description": "" + }, + { + "band_name": "mean_gravity_wave_dissipation", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_large_scale_precipitation_fraction", + "description": "" + }, + { + "band_name": "mean_large_scale_precipitation_fraction", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_large_scale_precipitation_rate", + "description": "" + }, + { + "band_name": "mean_large_scale_precipitation_rate", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_large_scale_snowfall_rate", + "description": "" + }, + { + "band_name": "mean_large_scale_snowfall_rate", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_northward_gravity_wave_surface_stress", + "description": "" + }, + { + "band_name": "mean_northward_gravity_wave_surface_stress", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_northward_turbulent_surface_stress", + "description": "" + }, + { + "band_name": "mean_northward_turbulent_surface_stress", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_period_of_total_swell", + "description": "" + }, + { + "band_name": "mean_period_of_total_swell", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_period_of_wind_waves", + "description": "" + }, + { + "band_name": "mean_period_of_wind_waves", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_potential_evaporation_rate", + "description": "" + }, + { + "band_name": "mean_potential_evaporation_rate", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_runoff_rate", + "description": "" + }, + { + "band_name": "mean_runoff_rate", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_sea_level_pressure", - "alt_names": [], + "description": "" + }, + { + "band_name": "mean_sea_level_pressure", + "alt_names": [ + "msl", + "slp" + ], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_snow_evaporation_rate", + "description": "Atmospheric pressure adjusted to mean sea level", + "units": "Pa" + }, + { + "band_name": "mean_snow_evaporation_rate", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_snowfall_rate", + "description": "" + }, + { + "band_name": "mean_snowfall_rate", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_snowmelt_rate", + "description": "" + }, + { + "band_name": "mean_snowmelt_rate", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_square_slope_of_waves", + "description": "" + }, + { + "band_name": "mean_square_slope_of_waves", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_sub_surface_runoff_rate", + "description": "" + }, + { + "band_name": "mean_sub_surface_runoff_rate", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_surface_direct_short_wave_radiation_flux", + "description": "" + }, + { + "band_name": "mean_surface_direct_short_wave_radiation_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_surface_direct_short_wave_radiation_flux_clear_sky", + "description": "" + }, + { + "band_name": "mean_surface_direct_short_wave_radiation_flux_clear_sky", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_surface_downward_long_wave_radiation_flux", + "description": "" + }, + { + "band_name": "mean_surface_downward_long_wave_radiation_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_surface_downward_long_wave_radiation_flux_clear_sky", + "description": "" + }, + { + "band_name": "mean_surface_downward_long_wave_radiation_flux_clear_sky", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_surface_downward_short_wave_radiation_flux", + "description": "" + }, + { + "band_name": "mean_surface_downward_short_wave_radiation_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_surface_downward_short_wave_radiation_flux_clear_sky", + "description": "" + }, + { + "band_name": "mean_surface_downward_short_wave_radiation_flux_clear_sky", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_surface_downward_uv_radiation_flux", + "description": "" + }, + { + "band_name": "mean_surface_downward_uv_radiation_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_surface_latent_heat_flux", + "description": "" + }, + { + "band_name": "mean_surface_latent_heat_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_surface_net_long_wave_radiation_flux", + "description": "" + }, + { + "band_name": "mean_surface_net_long_wave_radiation_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_surface_net_long_wave_radiation_flux_clear_sky", + "description": "" + }, + { + "band_name": "mean_surface_net_long_wave_radiation_flux_clear_sky", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_surface_net_short_wave_radiation_flux", + "description": "" + }, + { + "band_name": "mean_surface_net_short_wave_radiation_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_surface_net_short_wave_radiation_flux_clear_sky", + "description": "" + }, + { + "band_name": "mean_surface_net_short_wave_radiation_flux_clear_sky", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_surface_runoff_rate", + "description": "" + }, + { + "band_name": "mean_surface_runoff_rate", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_surface_sensible_heat_flux", + "description": "" + }, + { + "band_name": "mean_surface_sensible_heat_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_top_downward_short_wave_radiation_flux", + "description": "" + }, + { + "band_name": "mean_top_downward_short_wave_radiation_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_top_net_long_wave_radiation_flux", + "description": "" + }, + { + "band_name": "mean_top_net_long_wave_radiation_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_top_net_long_wave_radiation_flux_clear_sky", + "description": "" + }, + { + "band_name": "mean_top_net_long_wave_radiation_flux_clear_sky", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_top_net_short_wave_radiation_flux", + "description": "" + }, + { + "band_name": "mean_top_net_short_wave_radiation_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_top_net_short_wave_radiation_flux_clear_sky", + "description": "" + }, + { + "band_name": "mean_top_net_short_wave_radiation_flux_clear_sky", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_total_precipitation_rate", + "description": "" + }, + { + "band_name": "mean_total_precipitation_rate", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_vertical_gradient_of_refractivity_inside_trapping_layer", + "description": "" + }, + { + "band_name": "mean_vertical_gradient_of_refractivity_inside_trapping_layer", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_vertically_integrated_moisture_divergence", + "description": "" + }, + { + "band_name": "mean_vertically_integrated_moisture_divergence", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_wave_direction", + "description": "" + }, + { + "band_name": "mean_wave_direction", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_wave_direction_of_first_swell_partition", + "description": "" + }, + { + "band_name": "mean_wave_direction_of_first_swell_partition", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_wave_direction_of_second_swell_partition", + "description": "" + }, + { + "band_name": "mean_wave_direction_of_second_swell_partition", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_wave_direction_of_third_swell_partition", + "description": "" + }, + { + "band_name": "mean_wave_direction_of_third_swell_partition", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_wave_period", + "description": "" + }, + { + "band_name": "mean_wave_period", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_wave_period_based_on_first_moment", + "description": "" + }, + { + "band_name": "mean_wave_period_based_on_first_moment", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_wave_period_based_on_first_moment_for_swell", + "description": "" + }, + { + "band_name": "mean_wave_period_based_on_first_moment_for_swell", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_wave_period_based_on_first_moment_for_wind_waves", + "description": "" + }, + { + "band_name": "mean_wave_period_based_on_first_moment_for_wind_waves", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_wave_period_based_on_second_moment_for_swell", + "description": "" + }, + { + "band_name": "mean_wave_period_based_on_second_moment_for_swell", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_wave_period_based_on_second_moment_for_wind_waves", + "description": "" + }, + { + "band_name": "mean_wave_period_based_on_second_moment_for_wind_waves", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_wave_period_of_first_swell_partition", + "description": "" + }, + { + "band_name": "mean_wave_period_of_first_swell_partition", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_wave_period_of_second_swell_partition", + "description": "" + }, + { + "band_name": "mean_wave_period_of_second_swell_partition", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_wave_period_of_third_swell_partition", + "description": "" + }, + { + "band_name": "mean_wave_period_of_third_swell_partition", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "mean_zero_crossing_wave_period", + "description": "" + }, + { + "band_name": "mean_zero_crossing_wave_period", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "medium_cloud_cover", + "description": "" + }, + { + "band_name": "medium_cloud_cover", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "minimum_2m_temperature_since_previous_post_processing", + "description": "" + }, + { + "band_name": "minimum_2m_temperature_since_previous_post_processing", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "minimum_total_precipitation_rate_since_previous_post_processing", + "description": "" + }, + { + "band_name": "minimum_total_precipitation_rate_since_previous_post_processing", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "minimum_vertical_gradient_of_refractivity_inside_trapping_layer", + "description": "" + }, + { + "band_name": "minimum_vertical_gradient_of_refractivity_inside_trapping_layer", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "model_bathymetry", + "description": "" + }, + { + "band_name": "model_bathymetry", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "near_ir_albedo_for_diffuse_radiation", + "description": "" + }, + { + "band_name": "near_ir_albedo_for_diffuse_radiation", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "near_ir_albedo_for_direct_radiation", + "description": "" + }, + { + "band_name": "near_ir_albedo_for_direct_radiation", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "normalized_energy_flux_into_ocean", + "description": "" + }, + { + "band_name": "normalized_energy_flux_into_ocean", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "normalized_energy_flux_into_waves", + "description": "" + }, + { + "band_name": "normalized_energy_flux_into_waves", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "normalized_stress_into_ocean", + "description": "" + }, + { + "band_name": "normalized_stress_into_ocean", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "northward_gravity_wave_surface_stress", + "description": "" + }, + { + "band_name": "northward_gravity_wave_surface_stress", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "northward_turbulent_surface_stress", + "description": "" + }, + { + "band_name": "northward_turbulent_surface_stress", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "ocean_surface_stress_equivalent_10m_neutral_wind_direction", + "description": "" + }, + { + "band_name": "ocean_surface_stress_equivalent_10m_neutral_wind_direction", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "ocean_surface_stress_equivalent_10m_neutral_wind_speed", + "description": "" + }, + { + "band_name": "ocean_surface_stress_equivalent_10m_neutral_wind_speed", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "peak_wave_period", + "description": "" + }, + { + "band_name": "peak_wave_period", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "period_corresponding_to_maximum_individual_wave_height", + "description": "" + }, + { + "band_name": "period_corresponding_to_maximum_individual_wave_height", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "potential_evaporation", + "description": "" + }, + { + "band_name": "potential_evaporation", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "precipitation_type", + "description": "" + }, + { + "band_name": "precipitation_type", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "runoff", + "description": "" + }, + { + "band_name": "runoff", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "sea_ice_cover", + "description": "" + }, + { + "band_name": "sea_ice_cover", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "sea_surface_temperature", + "description": "" + }, + { + "band_name": "sea_surface_temperature", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "significant_height_of_combined_wind_waves_and_swell", + "description": "" + }, + { + "band_name": "significant_height_of_combined_wind_waves_and_swell", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "significant_height_of_total_swell", + "description": "" + }, + { + "band_name": "significant_height_of_total_swell", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "significant_height_of_wind_waves", + "description": "" + }, + { + "band_name": "significant_height_of_wind_waves", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "significant_wave_height_of_first_swell_partition", + "description": "" + }, + { + "band_name": "significant_wave_height_of_first_swell_partition", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "significant_wave_height_of_second_swell_partition", + "description": "" + }, + { + "band_name": "significant_wave_height_of_second_swell_partition", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "significant_wave_height_of_third_swell_partition", + "description": "" + }, + { + "band_name": "significant_wave_height_of_third_swell_partition", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "skin_reservoir_content", + "description": "" + }, + { + "band_name": "skin_reservoir_content", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "skin_temperature", + "description": "" + }, + { + "band_name": "skin_temperature", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "slope_of_sub_gridscale_orography", + "description": "" + }, + { + "band_name": "slope_of_sub_gridscale_orography", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "snow_albedo", + "description": "" + }, + { + "band_name": "snow_albedo", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "snow_density", + "description": "" + }, + { + "band_name": "snow_density", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "snow_depth", + "description": "" + }, + { + "band_name": "snow_depth", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "snow_evaporation", + "description": "" + }, + { + "band_name": "snow_evaporation", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "snowfall", + "description": "" + }, + { + "band_name": "snowfall", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "snowmelt", + "description": "" + }, + { + "band_name": "snowmelt", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "soil_temperature_level_1", + "description": "" + }, + { + "band_name": "soil_temperature_level_1", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "soil_temperature_level_2", + "description": "" + }, + { + "band_name": "soil_temperature_level_2", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "soil_temperature_level_3", + "description": "" + }, + { + "band_name": "soil_temperature_level_3", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "soil_temperature_level_4", + "description": "" + }, + { + "band_name": "soil_temperature_level_4", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "soil_type", + "description": "" + }, + { + "band_name": "soil_type", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "standard_deviation_of_filtered_subgrid_orography", + "description": "" + }, + { + "band_name": "standard_deviation_of_filtered_subgrid_orography", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "standard_deviation_of_orography", + "description": "" + }, + { + "band_name": "standard_deviation_of_orography", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "sub_surface_runoff", + "description": "" + }, + { + "band_name": "sub_surface_runoff", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "surface_latent_heat_flux", + "description": "" + }, + { + "band_name": "surface_latent_heat_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "surface_net_solar_radiation", + "description": "" + }, + { + "band_name": "surface_net_solar_radiation", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "surface_net_solar_radiation_clear_sky", + "description": "" + }, + { + "band_name": "surface_net_solar_radiation_clear_sky", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "surface_net_thermal_radiation", + "description": "" + }, + { + "band_name": "surface_net_thermal_radiation", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "surface_net_thermal_radiation_clear_sky", + "description": "" + }, + { + "band_name": "surface_net_thermal_radiation_clear_sky", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "surface_pressure", - "alt_names": [], + "description": "" + }, + { + "band_name": "surface_pressure", + "alt_names": [ + "sp" + ], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "surface_runoff", + "description": "Pressure at the surface", + "units": "Pa" + }, + { + "band_name": "surface_runoff", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "surface_sensible_heat_flux", + "description": "" + }, + { + "band_name": "surface_sensible_heat_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "surface_solar_radiation_downward_clear_sky", + "description": "" + }, + { + "band_name": "surface_solar_radiation_downward_clear_sky", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "surface_solar_radiation_downwards", - "alt_names": [], + "description": "" + }, + { + "band_name": "surface_solar_radiation_downwards", + "alt_names": [ + "ssrd" + ], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "surface_thermal_radiation_downward_clear_sky", + "description": "Downward solar radiation flux at the surface", + "units": "J m-2" + }, + { + "band_name": "surface_thermal_radiation_downward_clear_sky", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "surface_thermal_radiation_downwards", - "alt_names": [], + "description": "" + }, + { + "band_name": "surface_thermal_radiation_downwards", + "alt_names": [ + "strd" + ], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "temperature_of_snow_layer", + "description": "Downward thermal radiation flux at the surface", + "units": "J m-2" + }, + { + "band_name": "temperature_of_snow_layer", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "toa_incident_solar_radiation", + "description": "" + }, + { + "band_name": "toa_incident_solar_radiation", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "top_net_solar_radiation", + "description": "" + }, + { + "band_name": "top_net_solar_radiation", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "top_net_solar_radiation_clear_sky", + "description": "" + }, + { + "band_name": "top_net_solar_radiation_clear_sky", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "top_net_thermal_radiation", + "description": "" + }, + { + "band_name": "top_net_thermal_radiation", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "top_net_thermal_radiation_clear_sky", + "description": "" + }, + { + "band_name": "top_net_thermal_radiation_clear_sky", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "total_cloud_cover", + "description": "" + }, + { + "band_name": "total_cloud_cover", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "total_column_cloud_ice_water", + "description": "" + }, + { + "band_name": "total_column_cloud_ice_water", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "total_column_cloud_liquid_water", + "description": "" + }, + { + "band_name": "total_column_cloud_liquid_water", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "total_column_ozone", + "description": "" + }, + { + "band_name": "total_column_ozone", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "total_column_rain_water", + "description": "" + }, + { + "band_name": "total_column_rain_water", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "total_column_snow_water", + "description": "" + }, + { + "band_name": "total_column_snow_water", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "total_column_supercooled_liquid_water", + "description": "" + }, + { + "band_name": "total_column_supercooled_liquid_water", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "total_column_water", + "description": "" + }, + { + "band_name": "total_column_water", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "total_column_water_vapour", + "description": "" + }, + { + "band_name": "total_column_water_vapour", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "total_precipitation", - "alt_names": [], + "description": "" + }, + { + "band_name": "total_precipitation", + "alt_names": [ + "tp", + "precip" + ], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "total_sky_direct_solar_radiation_at_surface", + "description": "Total precipitation (rain and snow)", + "units": "m" + }, + { + "band_name": "total_sky_direct_solar_radiation_at_surface", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "total_totals_index", + "description": "" + }, + { + "band_name": "total_totals_index", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "trapping_layer_base_height", + "description": "" + }, + { + "band_name": "trapping_layer_base_height", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "trapping_layer_top_height", + "description": "" + }, + { + "band_name": "trapping_layer_top_height", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "type_of_high_vegetation", + "description": "" + }, + { + "band_name": "type_of_high_vegetation", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "type_of_low_vegetation", + "description": "" + }, + { + "band_name": "type_of_low_vegetation", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "u_component_stokes_drift", + "description": "" + }, + { + "band_name": "u_component_stokes_drift", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "uv_visible_albedo_for_diffuse_radiation", + "description": "" + }, + { + "band_name": "uv_visible_albedo_for_diffuse_radiation", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "uv_visible_albedo_for_direct_radiation", + "description": "" + }, + { + "band_name": "uv_visible_albedo_for_direct_radiation", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "v_component_stokes_drift", + "description": "" + }, + { + "band_name": "v_component_stokes_drift", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_divergence_of_cloud_frozen_water_flux", + "description": "" + }, + { + "band_name": "vertical_integral_of_divergence_of_cloud_frozen_water_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_divergence_of_cloud_liquid_water_flux", + "description": "" + }, + { + "band_name": "vertical_integral_of_divergence_of_cloud_liquid_water_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_divergence_of_geopotential_flux", + "description": "" + }, + { + "band_name": "vertical_integral_of_divergence_of_geopotential_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_divergence_of_kinetic_energy_flux", + "description": "" + }, + { + "band_name": "vertical_integral_of_divergence_of_kinetic_energy_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_divergence_of_mass_flux", + "description": "" + }, + { + "band_name": "vertical_integral_of_divergence_of_mass_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_divergence_of_moisture_flux", + "description": "" + }, + { + "band_name": "vertical_integral_of_divergence_of_moisture_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_divergence_of_ozone_flux", + "description": "" + }, + { + "band_name": "vertical_integral_of_divergence_of_ozone_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_divergence_of_thermal_energy_flux", + "description": "" + }, + { + "band_name": "vertical_integral_of_divergence_of_thermal_energy_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_divergence_of_total_energy_flux", + "description": "" + }, + { + "band_name": "vertical_integral_of_divergence_of_total_energy_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_eastward_cloud_frozen_water_flux", + "description": "" + }, + { + "band_name": "vertical_integral_of_eastward_cloud_frozen_water_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_eastward_cloud_liquid_water_flux", + "description": "" + }, + { + "band_name": "vertical_integral_of_eastward_cloud_liquid_water_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_eastward_geopotential_flux", + "description": "" + }, + { + "band_name": "vertical_integral_of_eastward_geopotential_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_eastward_heat_flux", + "description": "" + }, + { + "band_name": "vertical_integral_of_eastward_heat_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_eastward_kinetic_energy_flux", + "description": "" + }, + { + "band_name": "vertical_integral_of_eastward_kinetic_energy_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_eastward_mass_flux", + "description": "" + }, + { + "band_name": "vertical_integral_of_eastward_mass_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_eastward_ozone_flux", + "description": "" + }, + { + "band_name": "vertical_integral_of_eastward_ozone_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_eastward_total_energy_flux", + "description": "" + }, + { + "band_name": "vertical_integral_of_eastward_total_energy_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_eastward_water_vapour_flux", + "description": "" + }, + { + "band_name": "vertical_integral_of_eastward_water_vapour_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_energy_conversion", + "description": "" + }, + { + "band_name": "vertical_integral_of_energy_conversion", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_kinetic_energy", + "description": "" + }, + { + "band_name": "vertical_integral_of_kinetic_energy", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_mass_of_atmosphere", + "description": "" + }, + { + "band_name": "vertical_integral_of_mass_of_atmosphere", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_mass_tendency", + "description": "" + }, + { + "band_name": "vertical_integral_of_mass_tendency", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_northward_cloud_frozen_water_flux", + "description": "" + }, + { + "band_name": "vertical_integral_of_northward_cloud_frozen_water_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_northward_cloud_liquid_water_flux", + "description": "" + }, + { + "band_name": "vertical_integral_of_northward_cloud_liquid_water_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_northward_geopotential_flux", + "description": "" + }, + { + "band_name": "vertical_integral_of_northward_geopotential_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_northward_heat_flux", + "description": "" + }, + { + "band_name": "vertical_integral_of_northward_heat_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_northward_kinetic_energy_flux", + "description": "" + }, + { + "band_name": "vertical_integral_of_northward_kinetic_energy_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_northward_mass_flux", + "description": "" + }, + { + "band_name": "vertical_integral_of_northward_mass_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_northward_ozone_flux", + "description": "" + }, + { + "band_name": "vertical_integral_of_northward_ozone_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_northward_total_energy_flux", + "description": "" + }, + { + "band_name": "vertical_integral_of_northward_total_energy_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_northward_water_vapour_flux", + "description": "" + }, + { + "band_name": "vertical_integral_of_northward_water_vapour_flux", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_potential_and_internal_energy", + "description": "" + }, + { + "band_name": "vertical_integral_of_potential_and_internal_energy", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_potential_internal_and_latent_energy", + "description": "" + }, + { + "band_name": "vertical_integral_of_potential_internal_and_latent_energy", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_temperature", + "description": "" + }, + { + "band_name": "vertical_integral_of_temperature", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_thermal_energy", + "description": "" + }, + { + "band_name": "vertical_integral_of_thermal_energy", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertical_integral_of_total_energy", + "description": "" + }, + { + "band_name": "vertical_integral_of_total_energy", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "vertically_integrated_moisture_divergence", + "description": "" + }, + { + "band_name": "vertically_integrated_moisture_divergence", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "volumetric_soil_water_layer_1", + "description": "" + }, + { + "band_name": "volumetric_soil_water_layer_1", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "volumetric_soil_water_layer_2", + "description": "" + }, + { + "band_name": "volumetric_soil_water_layer_2", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "volumetric_soil_water_layer_3", + "description": "" + }, + { + "band_name": "volumetric_soil_water_layer_3", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "volumetric_soil_water_layer_4", + "description": "" + }, + { + "band_name": "volumetric_soil_water_layer_4", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "wave_spectral_directional_width", + "description": "" + }, + { + "band_name": "wave_spectral_directional_width", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "wave_spectral_directional_width_for_swell", + "description": "" + }, + { + "band_name": "wave_spectral_directional_width_for_swell", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "wave_spectral_directional_width_for_wind_waves", + "description": "" + }, + { + "band_name": "wave_spectral_directional_width_for_wind_waves", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "wave_spectral_kurtosis", + "description": "" + }, + { + "band_name": "wave_spectral_kurtosis", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "wave_spectral_peakedness", + "description": "" + }, + { + "band_name": "wave_spectral_peakedness", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "wave_spectral_skewness", + "description": "" + }, + { + "band_name": "wave_spectral_skewness", "alt_names": [], "resolution": "0.25 deg", - "description": ""}, - {"band_name": "zero_degree_level", + "description": "" + }, + { + "band_name": "zero_degree_level", "alt_names": [], "resolution": "0.25 deg", - "description": ""} + "description": "Height of 0 degC isotherm", + "units": "m" + } + ], + "notes": [ + "ERA5 provides global reanalysis data at 0.25 deg (~31km) spatial resolution", + "Daily statistics are computed from hourly ERA5 data using specified time zones", + "Available statistics: daily_mean, daily_minimum, daily_maximum, daily_sum", + "Data latency: approximately 5 days behind real-time", + "Over 300 atmospheric, land, and ocean variables available", + "Recommended for climate analysis, weather studies, and ML training data", + "Use 'daily_mean' for most temperature/pressure variables, 'daily_sum' for precipitation", + "Temperature variables are in Kelvin (K), convert to Celsius by subtracting 273.15", + "Precipitation is in meters (m), multiply by 1000 for millimeters" ] - } ] \ No newline at end of file diff --git a/terrakit/download/data_connectors/climate_data_store.py b/terrakit/download/data_connectors/climate_data_store.py index 2141d70..a6e4201 100644 --- a/terrakit/download/data_connectors/climate_data_store.py +++ b/terrakit/download/data_connectors/climate_data_store.py @@ -5,19 +5,27 @@ import cdsapi import json import logging +import math import os +import pandas as pd +import requests +import shutil import xarray as xr +import zipfile from datetime import datetime, timedelta +from pathlib import Path from shapely.geometry import box from typing import Any, Dict, Union -from pathlib import Path +from terrakit.download.geodata_utils import save_cog from ..connector import Connector from ..geodata_utils import ( load_and_list_collections, + save_data_array_to_file, ) from terrakit.general_utils.exceptions import ( TerrakitValidationError, + TerrakitValueError, ) from terrakit.validate.helpers import ( check_collection_exists, @@ -25,7 +33,11 @@ check_start_end_date_in_correct_order, basic_bbox_validation, ) -from .cds_utils.cordex_utils import CORDEX_DOMAINS, get_domain_info +from .cds_utils.cordex_utils import ( + CORDEX_DOMAINS, + get_domain_info, + find_matching_domains, +) logger = logging.getLogger(__name__) @@ -82,8 +94,6 @@ def _get_cordex_domain_from_bbox(self, bbox: list) -> str: Raises: TerrakitValidationError: If no matching domain found """ - from .cds_utils.cordex_utils import find_matching_domains - matching_domains = find_matching_domains(bbox) if not matching_domains: @@ -130,30 +140,416 @@ def _find_best_cordex_match(self, bbox: list, domain_codes: list) -> str: ) return best_domain - def list_cordex_domains(self) -> Dict[str, Any]: + def _convert_netcdf_to_geotiffs( + self, netcdf_path: str, bbox: list, output_dir: str, collection_name: str + ) -> list[str]: + """Convert CDS NetCDF to individual GeoTIFF files, one per date.""" + + ds = xr.open_dataset(netcdf_path) + output_files = [] + + # Determine dimension names + lon_name = "longitude" if "longitude" in ds.dims else "lon" + lat_name = "latitude" if "latitude" in ds.dims else "lat" + time_name = "time" if "time" in ds.dims else "valid_time" + + # Get the main data variable + data_vars = [ + v for v in ds.data_vars if v not in [lon_name, lat_name, time_name] + ] + var_name = data_vars[0] + + # Process each time step + for time_idx in range(len(ds[time_name])): + # Extract data for this time step + da = ds[var_name].isel({time_name: time_idx}) + + # Get the date + time_value = ds[time_name].isel({time_name: time_idx}).values + date_str = pd.Timestamp(time_value).strftime("%Y-%m-%d") + + # Add CRS and spatial dimensions + da = da.rio.write_crs("EPSG:4326") + da = da.rio.set_spatial_dims(x_dim=lon_name, y_dim=lat_name) + + # Create output filename + output_filename = f"{collection_name}_{date_str}.tif" + output_path = Path(output_dir) / output_filename + + # Use TerraKit's save_cog function + save_cog(da, str(output_path)) + output_files.append(str(output_path)) + + logger.info(f"Created GeoTIFF: {output_filename}") + + ds.close() + return output_files + + def _estimate_request_size( + self, + collection_name: str, + date_start: str, + date_end: str, + bbox: list, + bands: list, + ) -> dict: """ - List all available CORDEX domains with their information. + Estimate the size and duration of a CDS request. Returns: - dict: Dictionary of domain codes and their information + dict with keys: 'num_days', 'num_variables', 'area_km2', + 'estimated_mb', 'estimated_minutes' """ - cordex_domains: Dict[str, Any] = self.cordex_domains - return cordex_domains - def get_cordex_domain_info(self, domain_code: str) -> dict: + # Calculate number of days + start = datetime.strptime(date_start, "%Y-%m-%d") + end = datetime.strptime(date_end, "%Y-%m-%d") + num_days = (end - start).days + 1 + + # Calculate area in km² + # Approximate conversion: 1 degree ≈ 111 km at equator + lon_range = bbox[2] - bbox[0] + lat_range = bbox[3] - bbox[1] + avg_lat = (bbox[1] + bbox[3]) / 2 + + # Adjust longitude distance by latitude (cosine correction) + lon_km = lon_range * 111 * math.cos(math.radians(avg_lat)) + lat_km = lat_range * 111 + area_km2 = lon_km * lat_km + + # Number of variables + num_variables = len(bands) if bands else 1 + + # Estimate file size (rough approximations based on CDS data) + if self._is_cordex_collection(collection_name): + # CORDEX: ~0.5 MB per day per variable for typical domain + mb_per_day_per_var = 0.5 + else: + # ERA5: depends on resolution and area + # ~0.1 MB per day per variable per 10,000 km² + mb_per_day_per_var = (area_km2 / 10000) * 0.1 + + estimated_mb = num_days * num_variables * mb_per_day_per_var + + # Estimate download time + # CDS queue time: 1-5 minutes (average 2) + # Download speed: ~5 MB/min (conservative estimate) + queue_time_min = 2 + download_time_min = estimated_mb / 5 + estimated_minutes = queue_time_min + download_time_min + + return { + "num_days": num_days, + "num_variables": num_variables, + "area_km2": round(area_km2, 2), + "estimated_mb": round(estimated_mb, 2), + "estimated_minutes": round(estimated_minutes, 1), + } + + def _download_from_cds( + self, + collection_name: str, + date_start: str, + date_end: str, + bbox: list, + bands: list = [], + query_params: dict = {}, + working_dir: str = ".", + ) -> str: """ - Get information for a specific CORDEX domain. + Download data from CDS API with size and time estimates. Args: - domain_code: CORDEX domain code (e.g., 'EUR-11') + collection_name: CDS dataset name + date_start: Start date (YYYY-MM-DD) + date_end: End date (YYYY-MM-DD) + bbox: Bounding box [min_lon, min_lat, max_lon, max_lat] + bands: List of variables/bands to download + working_dir: Directory to save the downloaded zip file Returns: - dict: Domain information including name, bbox, and resolution + Path to downloaded zip file in working_dir + """ - Raises: - TerrakitValueError: If domain code not found + # Ensure working_dir exists + Path(working_dir).mkdir(parents=True, exist_ok=True) + + # Estimate request size + estimate = self._estimate_request_size( + collection_name, date_start, date_end, bbox, bands + ) + + # Log detailed information + logger.info(f"Submitting CDS request for {collection_name}") + logger.info( + f"Date range: {date_start} to {date_end} ({estimate['num_days']} days)" + ) + logger.info(f"Area: {estimate['area_km2']} km²") + logger.info(f"Variables: {estimate['num_variables']}") + logger.info(f"Estimated size: ~{estimate['estimated_mb']} MB") + logger.info(f"Estimated time: ~{estimate['estimated_minutes']} minutes") + + # Connect and build request + client = self._connect_to_cds() + request_params = self._build_request_params( + collection_name, + date_start, + date_end, + bbox, + bands, + self._load_constraints(collection_name), + query_params, + ) + + # Log request parameters for debugging + logger.debug("CDS Request Parameters:") + logger.debug(json.dumps(request_params, indent=2)) + + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + output_filename = f"cds_{collection_name}_{timestamp}.zip" + output_zip = str(Path(working_dir) / output_filename) + + logger.info("Request submitted to CDS queue. Please wait...") + + try: + start_time = datetime.now() + client.retrieve(collection_name, request_params, output_zip) + + # Log success + actual_time = (datetime.now() - start_time).total_seconds() / 60 + logger.info(f"✓ Download complete: {output_zip}") + logger.info(f"Actual time: {actual_time:.1f} minutes") + + return output_zip + + except requests.HTTPError as e: + # Parse CDS-specific error messages + error_details = self._parse_cds_error(e) + + logger.error("=" * 70) + logger.error("CLIMATE DATA STORE REQUEST FAILED") + logger.error("=" * 70) + logger.error(f"Collection: {collection_name}") + logger.error(f"Error Type: {error_details['type']}") + logger.error(f"Error Message: {error_details['message']}") + logger.error("") + logger.error("Request Parameters:") + logger.error(json.dumps(request_params, indent=2)) + logger.error("") + logger.error("Possible causes:") + for cause in error_details["possible_causes"]: + logger.error(f" - {cause}") + logger.error("=" * 70) + + raise TerrakitValidationError( + message=f"CLIMATE DATA STORE REQUEST FAILED: {error_details['message']}\n" + f"Collection: {collection_name}\n" + f"Error type: {error_details['type']}\n" + f"See logs for full request parameters and troubleshooting tips." + ) + + except Exception as e: + logger.error("=" * 70) + logger.error("UNEXPECTED ERROR DURING CDS DOWNLOAD") + logger.error("=" * 70) + logger.error(f"Collection: {collection_name}") + logger.error(f"Error: {str(e)}") + logger.error("") + logger.error("Request Parameters:") + logger.error(json.dumps(request_params, indent=2)) + logger.error("=" * 70) + + raise TerrakitValidationError( + message=f"Failed to download from CDS: {str(e)}\n" + f"Collection: {collection_name}\n" + f"See logs for full request parameters." + ) + + def _parse_cds_error(self, error: requests.HTTPError) -> dict: """ - return get_domain_info(domain_code) + Parse CDS API error and provide helpful troubleshooting information. + + Returns: + dict with keys: 'type', 'message', 'possible_causes' + """ + error_str = str(error) + + # Common CDS error patterns + if "ValueError" in error_str: + return { + "type": "ValueError", + "message": "Invalid parameter value in request", + "possible_causes": [ + "Variable/band name not valid for this collection", + "Date outside collection temporal range", + "Invalid area/bbox coordinates", + "Missing required parameters", + "Check CDS documentation for valid parameter values", + ], + } + elif "400" in error_str or "Bad Request" in error_str: + return { + "type": "Bad Request (400)", + "message": "CDS rejected the request parameters", + "possible_causes": [ + "Invalid parameter format", + "Required parameter missing", + "Parameter value out of range", + "Check parameter names match CDS API expectations", + ], + } + elif "401" in error_str or "Unauthorized" in error_str: + return { + "type": "Unauthorized (401)", + "message": "Authentication failed", + "possible_causes": [ + "Invalid or missing CDS API key", + "API key not set in environment (CDSAPI_KEY)", + "Account not activated or suspended", + ], + } + elif "403" in error_str or "Forbidden" in error_str: + return { + "type": "Forbidden (403)", + "message": "Access denied to this dataset", + "possible_causes": [ + "Dataset license not accepted", + "Visit CDS website to accept terms and conditions", + "Account lacks permissions for this dataset", + ], + } + else: + return { + "type": "Unknown Error", + "message": error_str, + "possible_causes": [ + "Check CDS service status", + "Verify request parameters", + "Review CDS API documentation", + ], + } + + def _build_request_params( + self, + collection_name: str, + date_start: str, + date_end: str, + bbox: list, + bands: list, + constraints: dict, + query_params: dict = {}, + ) -> Dict[str, Any]: + """ + Build CDS API request parameters based on collection type. + + Args: + collection_name: CDS dataset name + date_start: Start date (YYYY-MM-DD) + date_end: End date (YYYY-MM-DD) + bbox: Bounding box [min_lon, min_lat, max_lon, max_lat] + bands: List of variables/bands to download + constraints: Collection constraints from metadata + query_params: Additional collection-specific parameters (e.g., daily_statistic, frequency) + + Returns: + Dictionary of request parameters for CDS API + """ + params: Dict[str, Any] = {} + + # Handle different collection types + if self._is_cordex_collection(collection_name): + # CORDEX collections need domain instead of bbox + domain_code = self._get_cordex_domain_from_bbox(bbox) + params["domain"] = domain_code + + # Set default parameters for CORDEX collections + # These can be overridden by query_params + params["experiment"] = "historical" + params["horizontal_resolution"] = "0_44_degree_x_0_44_degree" + params["temporal_resolution"] = "daily_mean" + params["ensemble_member"] = "r1i1p1" + params["format"] = "netcdf" + + # Add start_year and end_year based on date range + start_date = datetime.strptime(date_start, "%Y-%m-%d") + end_date = datetime.strptime(date_end, "%Y-%m-%d") + params["start_year"] = [str(start_date.year)] + params["end_year"] = [str(end_date.year)] + + else: + # ERA5 and other collections use bbox directly + params["area"] = [ + bbox[0], # West + bbox[1], # South + bbox[2], # East + bbox[3], # North + ] + + # Set default parameters for ERA5 collections + # These can be overridden by query_params + params["product_type"] = "reanalysis" + params["format"] = "netcdf" + params["daily_statistic"] = "daily_mean" + params["frequency"] = "6_hourly" + params["time_zone"] = "utc+00:00" + + # Add temporal parameters + params["year"] = self._get_years_list(date_start, date_end) + params["month"] = self._get_months_list(date_start, date_end) + params["day"] = self._get_days_list(date_start, date_end) + + # Add variables/bands + if bands: + params["variable"] = bands + elif "variable" in constraints: + # Use first available variable if none specified + params["variable"] = [constraints["variable"][0]] + + # Merge query_params - these override any defaults set above + # This allows users to specify collection-specific parameters like: + # - daily_statistic: "daily_mean", "daily_maximum", "daily_minimum", "daily_standard_deviation" + # - frequency: "1hr", "3hr", "6hr", "day", "mon", "sem", "fx" + # - product_type: override default "reanalysis" + # - time_zone: override default "utc+00:00" + params.update(query_params) + + return params + + def _get_years_list(self, date_start: str, date_end: str) -> list[str]: + """Get list of years between start and end dates.""" + start = datetime.strptime(date_start, "%Y-%m-%d") + end = datetime.strptime(date_end, "%Y-%m-%d") + return [str(year) for year in range(start.year, end.year + 1)] + + def _get_months_list(self, date_start: str, date_end: str) -> list[str]: + """Get list of months between start and end dates.""" + start = datetime.strptime(date_start, "%Y-%m-%d") + end = datetime.strptime(date_end, "%Y-%m-%d") + + months = set() + current = start + while current <= end: + months.add(f"{current.month:02d}") + # Move to next month + if current.month == 12: + current = current.replace(year=current.year + 1, month=1) + else: + current = current.replace(month=current.month + 1) + + return sorted(list(months)) + + def _get_days_list(self, date_start: str, date_end: str) -> list[str]: + """Get list of days between start and end dates.""" + start = datetime.strptime(date_start, "%Y-%m-%d") + end = datetime.strptime(date_end, "%Y-%m-%d") + + days = set() + current = start + while current <= end: + days.add(f"{current.day:02d}") + current += timedelta(days=1) + + return sorted(list(days)) def _get_constraint_value( self, constraints: dict, *keys: str, collection_name: str = "" @@ -345,6 +741,31 @@ def _connect_to_cds(self) -> cdsapi.Client: raise TerrakitValidationError(error_msg) return client + def list_cordex_domains(self) -> Dict[str, Any]: + """ + List all available CORDEX domains with their information. + + Returns: + dict: Dictionary of domain codes and their information + """ + cordex_domains: Dict[str, Any] = self.cordex_domains + return cordex_domains + + def get_cordex_domain_info(self, domain_code: str) -> dict: + """ + Get information for a specific CORDEX domain. + + Args: + domain_code: CORDEX domain code (e.g., 'EUR-11') + + Returns: + dict: Domain information including name, bbox, and resolution + + Raises: + TerrakitValueError: If domain code not found + """ + return get_domain_info(domain_code) + def list_collections(self) -> list[Any]: """ Lists the available collections. @@ -355,6 +776,53 @@ def list_collections(self) -> list[Any]: logger.info("Listing available collections") return self.collections + def list_bands(self, data_collection_name: str) -> list[dict[str, Any]]: + """ + List available bands for a given collection. + + Parameters: + data_collection_name (str): The name of the collection to get bands for. + + Returns: + list[dict[str, Any]]: A list of band dictionaries containing band information. + Each dictionary contains keys like 'band_name', 'resolution', 'description', etc. + + Raises: + TerrakitValidationError: If the collection is not found or has no band information. + + Example: + ```python + from terrakit import DataConnector + dc = DataConnector(connector_type="climate_data_store") + dc = DataConnector(connector_type='climate_data_store') + bands = dc.connector.list_bands(data_collection_name='derived-era5-single-levels-daily-statistics') + print(f'\nFound {len(bands)} bands for derived-era5-single-levels-daily-statistics') + print('\nFirst 3 bands:') + for band in bands[:3]: + print(f" - {band['band_name']}: {band.get('description', 'N/A')}") + ``` + """ + # Check if collection exists + check_collection_exists(data_collection_name, self.collections) + + # Find the collection details + collection_details = None + for collection in self.collections_details: + if collection["collection_name"] == data_collection_name: + collection_details = collection + break + + if collection_details is None or "bands" not in collection_details: + raise TerrakitValidationError( + message=f"No band information found for collection '{data_collection_name}'" + ) + + bands_list: list[dict[str, Any]] = collection_details["bands"] + logger.info( + f"Found {len(bands_list)} bands for collection '{data_collection_name}'" + ) + return bands_list + def find_data( self, data_collection_name: str, @@ -433,9 +901,9 @@ def get_data( data_connector_spec=None, save_file=None, working_dir=".", - ): + ) -> Union[xr.DataArray, None]: """ - Fetches data from for the specified collection, date range, area, and bands. + Fetches data from Climate Data Store for the specified collection, date range, area, and bands. Args: data_collection_name (str): Name of the data collection to fetch data from. @@ -444,53 +912,150 @@ def get_data( area_polygon (list, optional): Polygon defining the area of interest. Defaults to None. bbox (list, optional): Bounding box defining the area of interest. Defaults to None. bands (list, optional): List of bands to retrieve. Defaults to all bands. - maxcc (int, optional): Maximum cloud cover threshold (0-100). Defaults to 100. + query_params (dict, optional): Additional query parameters. Defaults to {}. data_connector_spec (dict, optional): Data connector specification. Defaults to None. - save_file (str, optional): Path to save the output file. Defaults to None. + save_file (str, optional): Path to save the output file. If provided, individual GeoTIFF files + will be saved for each date with the naming pattern: {save_file}_{date}.tif + (e.g., 'output_2025-01-01.tif', 'output_2025-01-02.tif'). Each file contains all + requested bands for that specific date. If None, no files are saved to disk. Defaults to None. working_dir (str, optional): Working directory for temporary files. Defaults to '.'. Returns: - xarray: An xarray Datasets containing the fetched data with dimensions (time, band, y, x). + xarray.DataArray: An xarray DataArray containing all fetched data with dimensions (time, band, y, x). + All dates are stacked along the time dimension, and all bands are stacked along the band dimension. + If save_file is provided, individual date files are also saved to disk. + + Example: + ```python + import terrakit + data_connector = "climate_data_store" + dc = terrakit.DataConnector(connector_type=data_connector) + data = dc.connector.get_data( + data_collection_name="derived-era5-single-levels-daily-statistics", + date_start="2025-01-01", + date_end="2025-01-02", + bbox=[-1.32, 51.06, -1.30, 51.08], + bands=["2m_temperature"], + query_params={ + "daily_statistic": "daily_minimum", + "frequency": "1hr", + "time_zone": "utc+03:00" + } + ) + save_file="./derived-era5-single-levels-daily-statistics", + ``` """ - # cds_client = self._connect_to_cds() - - # da = xr.DataArray() - - # First construct the query - # request = { - # "variable": [ - # "10m_u_component_of_wind", - # "10m_v_component_of_wind", - # "2m_temperature", - # "total_precipitation", - # "10m_wind_gust_since_previous_post_processing", - # ], - # "product_type": "reanalysis", - # "year": "2025", - # "month": ["01"], - # "day": ["01", "02", "03", "04"], - # "time_zone": "utc+00:00", - # "area": [90, -180, -90, 180], - # "daily_statistic": "daily_mean", - # "frequency": "6_hourly", - # "format": "netcdf", - # } - - # # - # cds = cdsapi.Client() - # downloaded_filename = cds.retrieve(data_collection_name, request).download() - - da = xr.DataArray() - return da - - -#### If licenses have not been accepted. -# HTTPError: 403 Client Error: Forbidden for url: https://cds.climate.copernicus.eu/api/retrieve/v1/processes/derived-era5-single-levels-daily-statistics/execution -# required licences not accepted -# Not all the required licences have been accepted; please visit https://cds.climate.copernicus.eu/datasets/derived-era5-single-levels-daily-statistics?tab=download#manage-licences to accept the required licence(s). - + # 1. Download zip from CDS API + zip_path = self._download_from_cds( + data_collection_name, + date_start, + date_end, + bbox, + bands, + query_params, + working_dir, + ) -# https://object-store.os-api.cci2.ecmwf.int/cci2-prod-catalogue/resources/derived-era5-single-levels-daily-statistics/constraints_a0b1ad068fce54320fa1350cc56b8692d66b395cf066d0c8bbf1579816e074b8.json -# 532 entries -# dict_keys(['daily_statistic', 'day', 'frequency', 'month', 'product_type', 'time_zone', 'variable', 'year']) + # 2. Extract NetCDF from zip + extract_dir = Path(working_dir) / "temp_netcdf" + extract_dir.mkdir(parents=True, exist_ok=True) + + with zipfile.ZipFile(zip_path, "r") as zip_ref: + zip_ref.extractall(extract_dir) + + # 3. Find NetCDF file(s) + netcdf_files = list(extract_dir.glob("*.nc")) + if not netcdf_files: + raise TerrakitValueError(f"No NetCDF files found in {zip_path}") + + # 4. Load NetCDF and process into DataArray per date + # CDS may return multiple NetCDF files (one per variable) + # We need to merge them by date to avoid duplicates + + # First, collect all data organized by date + date_data_dict: Dict[ + str, Dict[str, xr.DataArray] + ] = {} # {date_str: {var_name: DataArray}} + + for netcdf_file in netcdf_files: + ds = xr.open_dataset(netcdf_file) + + # Determine dimension names + lon_name = "longitude" if "longitude" in ds.dims else "lon" + lat_name = "latitude" if "latitude" in ds.dims else "lat" + time_name = "time" if "time" in ds.dims else "valid_time" + + # Get the main data variable(s) - these are our bands + data_vars = [ + v for v in ds.data_vars if v not in [lon_name, lat_name, time_name] + ] + + # Process each time step + for time_idx in range(len(ds[time_name])): + # Extract the date for this time step + time_value = ds[time_name].isel({time_name: time_idx}).values + date_str = pd.Timestamp(time_value).strftime("%Y-%m-%d") + + # Initialize dict for this date if not exists + if date_str not in date_data_dict: + date_data_dict[date_str] = {} + + # Store each variable for this date + for var_name in data_vars: + # Extract data for this specific time step + da_var = ds[var_name].isel({time_name: time_idx}) + + # Add CRS and spatial dimensions + da_var = da_var.rio.write_crs("EPSG:4326") + da_var = da_var.rio.set_spatial_dims(x_dim=lon_name, y_dim=lat_name) + + # Store in dict + date_data_dict[date_str][var_name] = da_var + + ds.close() + + # Now process each unique date + da_list = [] + for date_str in sorted(date_data_dict.keys()): + data_date_datetime = datetime.strptime(date_str, "%Y-%m-%d") + var_dict = date_data_dict[date_str] + + # Collect all variables for this date + band_arrays = [] + band_names = [] + for var_name in sorted(var_dict.keys()): + da_var = var_dict[var_name] + # Expand dims to add band dimension + da_var = da_var.expand_dims(dim="band") + band_arrays.append(da_var) + band_names.append(var_name) + + # Concatenate all bands for this time step + da_time = xr.concat(band_arrays, dim="band") + da_time = da_time.assign_coords({"band": band_names}) + + # Expand time dimension and assign time coordinate + da_time = da_time.expand_dims(dim="time") + da_time = da_time.assign_coords({"time": [data_date_datetime]}) + + # Save individual date file if save_file is provided + if save_file is not None: + usave_file = save_file.replace(".tif", f"_{date_str}.tif") + save_data_array_to_file(da_time, usave_file) + + # Add this date's data to the list for final concatenation + da_list.append(da_time) + + # 5. Concatenate all time steps into final DataArray + da = xr.concat(da_list, dim="time", join="override", combine_attrs="override") + + # # 6. Save final concatenated file (optional, for the full time series) + # save_data_array_to_file(da, save_file) + + # 7. Cleanup temporary files + shutil.rmtree(extract_dir) + Path(zip_path).unlink() + + logger.info(f"Processed {len(da_list)} time steps into DataArray") + return da diff --git a/tests/component_tests/download/conftest.py b/tests/component_tests/download/conftest.py index a632c08..1448463 100644 --- a/tests/component_tests/download/conftest.py +++ b/tests/component_tests/download/conftest.py @@ -365,3 +365,84 @@ def mock_aws_get_data(mocker, mock_aws_find_items): ################################################################################################### + + +############################# CLIMATE DATA STORE helper functions and fixtures ############################ +@pytest.fixture +def mock_cds_client(monkeypatch): + """ + Mock CDS API client to copy test zip file instead of downloading from CDS. + + This fixture patches cdsapi.Client to return a mock that copies + ./7fbb992ea3687a2ec12f2ba1de4cc73a.zip to the requested output path. + + # The following request was used to generate the original test zip file: + # request = { + # "variable": [ + # "10m_u_component_of_wind", + # "10m_v_component_of_wind", + # "2m_temperature", + # "total_precipitation", + # "10m_wind_gust_since_previous_post_processing", + # ], + # "product_type": "reanalysis", + # "year": "2025", + # "month": ["01"], + # "day": ["01", "02"], + # "time_zone": "utc+00:00", + # "area": [90, -180, -90, 180], + # "daily_statistic": "daily_mean", + # "frequency": "6_hourly", + # "format": "netcdf", + # } + # cds = cdsapi.Client() + # downloaded_filename = cds.retrieve(data_collection_name, request).download() + + # This data is now replaced by synthetic test data, the script for generating the test zip file can be found in /Users/rosielickorish/Documents/IBMResearch/2026/terrakit/tests/resources/scripts. + + Usage: + def test_cds_download(mock_cds_client): + # CDS API calls will use the mock + dc = DataConnector(connector_type="climate_data_store") + data = dc.connector.get_data(...) + """ + import shutil + from pathlib import Path + from unittest.mock import MagicMock + + # Path to test zip file + TEST_ZIP = Path( + "./tests/resources/climate_data_store/era5_daily_statistics_test_data.zip" + ) + + # Create mock client + mock_client = MagicMock() + + def mock_retrieve(collection_name, request_params, output_path): + """Copy test zip to output_path instead of downloading.""" + if not TEST_ZIP.exists(): + raise FileNotFoundError( + f"Test data not found: {TEST_ZIP}\n" + "Please ensure /Users/rosielickorish/Documents/IBMResearch/2026/terrakit/tests/resources/scripts/generate_cds_test_data.py exists." + ) + + # Ensure output directory exists + output_file = Path(output_path) + output_file.parent.mkdir(parents=True, exist_ok=True) + + # Copy test zip to requested location + shutil.copy(TEST_ZIP, output_file) + + return str(output_file) + + # Assign mock retrieve method + mock_client.retrieve = mock_retrieve + + # Mock the cdsapi.Client class to return our mock + def mock_cdsapi_client(*args, **kwargs): + return mock_client + + # Patch cdsapi.Client + monkeypatch.setattr("cdsapi.Client", mock_cdsapi_client) + + return mock_client diff --git a/tests/component_tests/download/data_connectors/test_climate_data_store.py b/tests/component_tests/download/data_connectors/test_climate_data_store.py index 736c486..a30726e 100644 --- a/tests/component_tests/download/data_connectors/test_climate_data_store.py +++ b/tests/component_tests/download/data_connectors/test_climate_data_store.py @@ -2,8 +2,12 @@ # SPDX-License-Identifier: Apache-2.0 +import os import pandas as pd import pytest +import xarray as xr +from pathlib import Path +from rasterio.crs import CRS from terrakit import DataConnector from terrakit.general_utils.exceptions import ( @@ -12,9 +16,87 @@ ) +@pytest.fixture +def mock_cds_client(monkeypatch): + """ + Mock CDS API client to copy test zip file instead of downloading from CDS. + + This fixture patches cdsapi.Client to return a mock that copies + ./7fbb992ea3687a2ec12f2ba1de4cc73a.zip to the requested output path. + + # The following request was used to generate the test zip file: + # request = { + # "variable": [ + # "10m_u_component_of_wind", + # "10m_v_component_of_wind", + # "2m_temperature", + # "total_precipitation", + # "10m_wind_gust_since_previous_post_processing", + # ], + # "product_type": "reanalysis", + # "year": "2025", + # "month": ["01"], + # "day": ["01", "02"], + # "time_zone": "utc+00:00", + # "area": [90, -180, -90, 180], + # "daily_statistic": "daily_mean", + # "frequency": "6_hourly", + # "format": "netcdf", + # } + + # # + # cds = cdsapi.Client() + # downloaded_filename = cds.retrieve(data_collection_name, request).download() + + Usage: + def test_cds_download(mock_cds_client): + # CDS API calls will use the mock + dc = DataConnector(connector_type="climate_data_store") + data = dc.connector.get_data(...) + """ + import shutil + from unittest.mock import MagicMock + + # Path to test zip file + TEST_ZIP = Path("./7fbb992ea3687a2ec12f2ba1de4cc73a.zip") + + # Create mock client + mock_client = MagicMock() + + def mock_retrieve(collection_name, request_params, output_path): + """Copy test zip to output_path instead of downloading.""" + if not TEST_ZIP.exists(): + raise FileNotFoundError( + f"Test data not found: {TEST_ZIP}\n" + "Please ensure 7fbb992ea3687a2ec12f2ba1de4cc73a.zip exists in project root" + ) + + # Ensure output directory exists + output_file = Path(output_path) + output_file.parent.mkdir(parents=True, exist_ok=True) + + # Copy test zip to requested location + shutil.copy(TEST_ZIP, output_file) + + return str(output_file) + + # Assign mock retrieve method + mock_client.retrieve = mock_retrieve + + # Mock the cdsapi.Client class to return our mock + def mock_cdsapi_client(*args, **kwargs): + return mock_client + + # Patch cdsapi.Client + monkeypatch.setattr("cdsapi.Client", mock_cdsapi_client) + + return mock_client + + class TestClimateDataStore: connector_type = "climate_data_store" - bands = ["2m_temperature_max", "2m_temperature_min"] + # Mock data contains these 5 bands from the test zip file + bands = ["fg10", "t2m", "tp", "u10", "v10"] def test_valid_data_connector(self): dc = DataConnector(connector_type=self.connector_type) @@ -123,3 +205,47 @@ def test_find_available_data_cds_start_data_given_constraints( bands=self.bands, ) assert unique_dates == [start_date, end_date] + + @pytest.mark.parametrize( + "collection", + [ + ("derived-era5-single-levels-daily-statistics"), + # ("projections-cordex-domains-single-levels"), + ], + ) + def test_get_data_cds( + self, mock_cds_client, collection, bbox, save_file_dir, get_data_clean_up + ): + """ + Test the get_data method. + + Note: The mock returns a zip file with 5 NetCDF files (one per variable), + each containing 2 time steps (2025-01-01 and 2025-01-02). + """ + date_start = "2025-01-01" + date_end = "2025-01-02" + save_file = f"{save_file_dir}/{self.connector_type}_{collection}.tif" + dc = DataConnector(connector_type=self.connector_type) + data = dc.connector.get_data( + data_collection_name=collection, + date_start=date_start, + date_end=date_end, + bbox=bbox, + bands=self.bands, + save_file=save_file, + ) + assert data is not None + assert len(data) > 0 # Check we got data + + assert isinstance(data, xr.DataArray) + assert data.rio.crs == CRS.from_epsg(4326) + + # Mock data contains 5 bands (fg10, t2m, tp, u10, v10) - one per NetCDF file + assert len(data.coords["band"]) == 5 + + # Mock data contains 2 time steps (2025-01-01 and 2025-01-02) + assert len(data.time) == 2 + + # Check that files were created for the dates in the mock data + assert os.path.exists(save_file.replace(".tif", "_2025-01-01.tif")) is True + assert os.path.exists(save_file.replace(".tif", "_2025-01-02.tif")) is True diff --git a/tests/resources/climate_data_store/era5_daily_statistics_test_data.zip b/tests/resources/climate_data_store/era5_daily_statistics_test_data.zip new file mode 100644 index 0000000000000000000000000000000000000000..333b7eee2e0c17d9811b9489b179200465399f95 GIT binary patch literal 15096 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

&A#Vf%csw#4LIfua@G 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 XjJy;$#6JeWzfXH$ARtGMfA0PdvD>;} literal 0 HcmV?d00001 From 593369a6c5e531cb36389a9305af3a22ca8e9561 Mon Sep 17 00:00:00 2001 From: Rosie Lickorish Date: Wed, 11 Feb 2026 23:27:51 +0000 Subject: [PATCH 13/18] Fix test data path and ensure CDS API mock is only in conftest.py --- tests/component_tests/download/conftest.py | 4 +- .../test_climate_data_store.py | 78 ------------------- 2 files changed, 2 insertions(+), 80 deletions(-) diff --git a/tests/component_tests/download/conftest.py b/tests/component_tests/download/conftest.py index 1448463..742f68b 100644 --- a/tests/component_tests/download/conftest.py +++ b/tests/component_tests/download/conftest.py @@ -374,7 +374,7 @@ def mock_cds_client(monkeypatch): Mock CDS API client to copy test zip file instead of downloading from CDS. This fixture patches cdsapi.Client to return a mock that copies - ./7fbb992ea3687a2ec12f2ba1de4cc73a.zip to the requested output path. + ./tests/resources/climate_data_store/era5_daily_statistics_test_data.zip to the requested output path. # The following request was used to generate the original test zip file: # request = { @@ -423,7 +423,7 @@ def mock_retrieve(collection_name, request_params, output_path): if not TEST_ZIP.exists(): raise FileNotFoundError( f"Test data not found: {TEST_ZIP}\n" - "Please ensure /Users/rosielickorish/Documents/IBMResearch/2026/terrakit/tests/resources/scripts/generate_cds_test_data.py exists." + "Please ensure ./tests/resources/scripts/generate_cds_test_data.py exists." ) # Ensure output directory exists diff --git a/tests/component_tests/download/data_connectors/test_climate_data_store.py b/tests/component_tests/download/data_connectors/test_climate_data_store.py index a30726e..a5a5e76 100644 --- a/tests/component_tests/download/data_connectors/test_climate_data_store.py +++ b/tests/component_tests/download/data_connectors/test_climate_data_store.py @@ -6,7 +6,6 @@ import pandas as pd import pytest import xarray as xr -from pathlib import Path from rasterio.crs import CRS from terrakit import DataConnector @@ -16,83 +15,6 @@ ) -@pytest.fixture -def mock_cds_client(monkeypatch): - """ - Mock CDS API client to copy test zip file instead of downloading from CDS. - - This fixture patches cdsapi.Client to return a mock that copies - ./7fbb992ea3687a2ec12f2ba1de4cc73a.zip to the requested output path. - - # The following request was used to generate the test zip file: - # request = { - # "variable": [ - # "10m_u_component_of_wind", - # "10m_v_component_of_wind", - # "2m_temperature", - # "total_precipitation", - # "10m_wind_gust_since_previous_post_processing", - # ], - # "product_type": "reanalysis", - # "year": "2025", - # "month": ["01"], - # "day": ["01", "02"], - # "time_zone": "utc+00:00", - # "area": [90, -180, -90, 180], - # "daily_statistic": "daily_mean", - # "frequency": "6_hourly", - # "format": "netcdf", - # } - - # # - # cds = cdsapi.Client() - # downloaded_filename = cds.retrieve(data_collection_name, request).download() - - Usage: - def test_cds_download(mock_cds_client): - # CDS API calls will use the mock - dc = DataConnector(connector_type="climate_data_store") - data = dc.connector.get_data(...) - """ - import shutil - from unittest.mock import MagicMock - - # Path to test zip file - TEST_ZIP = Path("./7fbb992ea3687a2ec12f2ba1de4cc73a.zip") - - # Create mock client - mock_client = MagicMock() - - def mock_retrieve(collection_name, request_params, output_path): - """Copy test zip to output_path instead of downloading.""" - if not TEST_ZIP.exists(): - raise FileNotFoundError( - f"Test data not found: {TEST_ZIP}\n" - "Please ensure 7fbb992ea3687a2ec12f2ba1de4cc73a.zip exists in project root" - ) - - # Ensure output directory exists - output_file = Path(output_path) - output_file.parent.mkdir(parents=True, exist_ok=True) - - # Copy test zip to requested location - shutil.copy(TEST_ZIP, output_file) - - return str(output_file) - - # Assign mock retrieve method - mock_client.retrieve = mock_retrieve - - # Mock the cdsapi.Client class to return our mock - def mock_cdsapi_client(*args, **kwargs): - return mock_client - - # Patch cdsapi.Client - monkeypatch.setattr("cdsapi.Client", mock_cdsapi_client) - - return mock_client - - class TestClimateDataStore: connector_type = "climate_data_store" # Mock data contains these 5 bands from the test zip file From 347a7110ccbe37660cbc451ef79daf0f77f147c0 Mon Sep 17 00:00:00 2001 From: Rosie Lickorish Date: Wed, 11 Feb 2026 23:29:32 +0000 Subject: [PATCH 14/18] Add documentation for CDS collection domains --- .../collections/cordex_domains.md | 296 ++++++++++++ .../collections/era5_domains.md | 444 ++++++++++++++++++ docs/cordex_domains.md | 66 --- mkdocs.yml | 3 +- 4 files changed, 742 insertions(+), 67 deletions(-) create mode 100644 docs/api/data_connectors/collections/cordex_domains.md create mode 100644 docs/api/data_connectors/collections/era5_domains.md delete mode 100644 docs/cordex_domains.md diff --git a/docs/api/data_connectors/collections/cordex_domains.md b/docs/api/data_connectors/collections/cordex_domains.md new file mode 100644 index 0000000..b5bf152 --- /dev/null +++ b/docs/api/data_connectors/collections/cordex_domains.md @@ -0,0 +1,296 @@ +## CORDEX Regional Climate Model Data on Single Levels - Domain Reference + +Full details of dataset: https://cds.climate.copernicus.eu/datasets/projections-cordex-domains-single-levels?tab=overview + +CORDEX (Coordinated Regional Climate Downscaling Experiment) defines specific regional domains for climate projections. Each domain covers a geographic region at one or more resolutions. + +### Available CORDEX Domains + +| Domain Code | Region Name | Bounding Box (min_lon, min_lat, max_lon, max_lat) | Resolution | Approx. Grid Spacing | +|-------------|-------------|---------------------------------------------------|------------|---------------------| +| **AFR-44** | Africa | -24.64, -45.76, 60.28, 42.24 | 0.44° | ~50 km | +| **AFR-22** | Africa | -24.64, -45.76, 60.28, 42.24 | 0.22° | ~25 km | +| **ANT-44** | Antarctica | -180.0, -89.5, 180.0, -60.0 | 0.44° | ~50 km | +| **ARC-44** | Arctic | -180.0, 60.0, 180.0, 90.0 | 0.44° | ~50 km | +| **AUS-44** | Australasia | 89.5, -52.36, 179.99, 12.21 | 0.44° | ~50 km | +| **AUS-22** | Australasia | 89.5, -52.36, 179.99, 12.21 | 0.22° | ~25 km | +| **CAM-44** | Central America | -122.0, -19.76, -59.52, 34.24 | 0.44° | ~50 km | +| **CAM-22** | Central America | -122.0, -19.76, -59.52, 34.24 | 0.22° | ~25 km | +| **CAS-44** | Central Asia | 34.0, 18.0, 115.0, 70.0 | 0.44° | ~50 km | +| **CAS-22** | Central Asia | 34.0, 18.0, 115.0, 70.0 | 0.22° | ~25 km | +| **EAS-44** | East Asia | 65.0, -15.0, 155.0, 65.0 | 0.44° | ~50 km | +| **EAS-22** | East Asia | 65.0, -15.0, 155.0, 65.0 | 0.22° | ~25 km | +| **EUR-44** | Europe | -44.0, 22.0, 65.0, 72.0 | 0.44° | ~50 km | +| **EUR-22** | Europe | -44.0, 22.0, 65.0, 72.0 | 0.22° | ~25 km | +| **EUR-11** | Europe | -44.0, 22.0, 65.0, 72.0 | 0.11° | ~12.5 km | +| **MED-44** | Mediterranean | -10.0, 30.0, 40.0, 48.0 | 0.44° | ~50 km | +| **MED-22** | Mediterranean | -10.0, 30.0, 40.0, 48.0 | 0.22° | ~25 km | +| **MNA-44** | Middle East & North Africa | -25.0, 0.0, 75.0, 50.0 | 0.44° | ~50 km | +| **MNA-22** | Middle East & North Africa | -25.0, 0.0, 75.0, 50.0 | 0.22° | ~25 km | +| **NAM-44** | North America | -172.0, 12.0, -35.0, 76.0 | 0.44° | ~50 km | +| **NAM-22** | North America | -172.0, 12.0, -35.0, 76.0 | 0.22° | ~25 km | +| **SAM-44** | South America | -93.0, -56.0, -25.0, 18.0 | 0.44° | ~50 km | +| **SAM-22** | South America | -93.0, -56.0, -25.0, 18.0 | 0.22° | ~25 km | +| **WAS-44** | South Asia (West Asia) | 20.0, -15.0, 115.0, 45.0 | 0.44° | ~50 km | +| **WAS-22** | South Asia (West Asia) | 20.0, -15.0, 115.0, 45.0 | 0.22° | ~25 km | +| **SEA-44** | Southeast Asia | 89.0, -15.0, 146.0, 27.0 | 0.44° | ~50 km | +| **SEA-22** | Southeast Asia | 89.0, -15.0, 146.0, 27.0 | 0.22° | ~25 km | + +### Domain Resolution Notes + +- **0.44°** (~50 km): Standard resolution, available for all domains +- **0.22°** (~25 km): High resolution, available for most domains +- **0.11°** (~12.5 km): Very high resolution, currently only available for Europe (EUR-11) + +## Query Parameters + +The CDS connector supports additional query parameters to customize CORDEX data retrieval. These parameters can be passed via the `query_params` dictionary in the `get_data()` method. + +### Default Values + +When downloading CORDEX data, the following default parameters are automatically applied: + +| Parameter | Default Value | Description | +|-----------|---------------|-------------| +| `experiment` | `"historical"` | Type of CORDEX experiment | +| `horizontal_resolution` | `"0_44_degree_x_0_44_degree"` | Spatial resolution (~50 km) | +| `temporal_resolution` | `"daily_mean"` | Temporal aggregation | +| `ensemble_member` | `"r1i1p1"` | Ensemble member identifier | +| `format` | `"netcdf"` | Output file format | +| `start_year` | Derived from `date_start` | Start year for data | +| `end_year` | Derived from `date_end` | End year for data | + +### Valid Parameter Values + +#### `experiment` +Type of CORDEX experiment: + +- `"historical"` (default) - Historical period (typically 1950-2005) +- `"evaluation"` - Evaluation runs driven by ERA-Interim reanalysis (typically 1980-2010) +- `"rcp_2_6"` - RCP 2.6 scenario (low emissions) +- `"rcp_4_5"` - RCP 4.5 scenario (medium emissions) +- `"rcp_8_5"` - RCP 8.5 scenario (high emissions) + +#### `horizontal_resolution` +Spatial resolution of the model grid: + +- `"0_44_degree_x_0_44_degree"` (default) - ~50 km grid spacing +- `"0_22_degree_x_0_22_degree"` - ~25 km grid spacing (high resolution) +- `"0_11_degree_x_0_11_degree"` - ~12.5 km grid spacing (very high resolution, EUR-11 only) + +#### `temporal_resolution` +Temporal aggregation of the data: + +- `"daily_mean"` (default) - Daily mean values +- `"3_hourly"` - 3-hour intervals +- `"6_hourly"` - 6-hour intervals +- `"monthly_mean"` - Monthly mean values +- `"fixed"` - Time-invariant fields (e.g., orography, land mask) + +#### `ensemble_member` +Ensemble member identifier (format: rXiYpZ): + +- `"r1i1p1"` (default) - First realization, first initialization, first physics +- `"r0i0p0"` - Used for evaluation runs +- `"r12i1p1"`, `"r2i1p1"`, `"r3i1p1"` - Additional ensemble members +- **r** = realization (different initial conditions) +- **i** = initialization method +- **p** = physics parameterization + +#### `gcm_model` +Global Climate Model providing boundary conditions (examples): + +- `"mohc_hadgem2_es"` - Met Office Hadley Centre HadGEM2-ES +- `"mpi_m_mpi_esm_lr"` - Max Planck Institute MPI-ESM-LR +- `"ichec_ec_earth"` - EC-EARTH consortium model +- `"cnrm_cerfacs_cm5"` - CNRM-CERFACS CNRM-CM5 +- `"ncc_noresm1_m"` - Norwegian Climate Centre NorESM1-M +- `"era_interim"` - ERA-Interim reanalysis (for evaluation runs) +- Many others available + +#### `rcm_model` +Regional Climate Model (examples): + +- `"clmcom_clm_cclm4_8_17"` - CLM-Community CCLM4-8-17 +- `"smhi_rca4"` - SMHI RCA4 +- `"knmi_racmo22t"` - KNMI RACMO22T +- `"gerics_remo2009"` - GERICS REMO2009 +- `"dmi_hirham5"` - DMI HIRHAM5 +- Many others available + +## Usage Examples + +### Basic Usage - Download with Defaults + +```python +from terrakit import DataConnector + +# Initialize CDS connector +dc = DataConnector(connector_type="climate_data_store") + +# Download CORDEX data with default parameters +# (historical experiment, 0.44° resolution, daily_mean, r1i1p1 ensemble) +data = dc.connector.get_data( + data_collection_name="projections-cordex-domains-single-levels", + date_start="2000-01-01", + date_end="2000-12-31", + bbox=[-10, 35, 30, 60], # Europe region (automatically mapped to EUR-44) + bands=["2m_air_temperature", "mean_precipitation_flux"] +) + +print(f"Data shape: {data.shape}") +print(f"Dimensions: {data.dims}") +``` + +### Override Default Parameters with query_params + +```python +from terrakit import DataConnector + +dc = DataConnector(connector_type="climate_data_store") + +# Example 1: Get RCP 8.5 scenario data (future projections) +data_rcp85 = dc.connector.get_data( + data_collection_name="projections-cordex-domains-single-levels", + date_start="2050-01-01", + date_end="2050-12-31", + bbox=[-10, 35, 30, 60], + bands=["2m_air_temperature"], + query_params={"experiment": "rcp_8_5"} +) + +# Example 2: Get high-resolution data (0.22°) +data_hires = dc.connector.get_data( + data_collection_name="projections-cordex-domains-single-levels", + date_start="2000-01-01", + date_end="2000-12-31", + bbox=[-10, 35, 30, 60], + bands=["2m_air_temperature"], + query_params={"horizontal_resolution": "0_22_degree_x_0_22_degree"} +) + +# Example 3: Get 3-hourly data instead of daily mean +data_3hourly = dc.connector.get_data( + data_collection_name="projections-cordex-domains-single-levels", + date_start="2000-01-01", + date_end="2000-01-07", + bbox=[-10, 35, 30, 60], + bands=["10m_wind_speed"], + query_params={"temporal_resolution": "3_hourly"} +) + +# Example 4: Specify GCM and RCM models +data_custom_models = dc.connector.get_data( + data_collection_name="projections-cordex-domains-single-levels", + date_start="2000-01-01", + date_end="2000-12-31", + bbox=[-10, 35, 30, 60], + bands=["2m_air_temperature", "mean_precipitation_flux"], + query_params={ + "gcm_model": "mohc_hadgem2_es", + "rcm_model": "knmi_racmo22t" + } +) + +# Example 5: Multiple parameter overrides for future scenario +data_future = dc.connector.get_data( + data_collection_name="projections-cordex-domains-single-levels", + date_start="2080-01-01", + date_end="2080-12-31", + bbox=[-10, 35, 30, 60], + bands=["2m_air_temperature", "mean_precipitation_flux"], + query_params={ + "experiment": "rcp_4_5", + "horizontal_resolution": "0_22_degree_x_0_22_degree", + "temporal_resolution": "monthly_mean", + "ensemble_member": "r12i1p1", + "gcm_model": "ichec_ec_earth", + "rcm_model": "smhi_rca4" + } +) +``` + +### Working with Different CORDEX Domains + +```python +from terrakit import DataConnector + +dc = DataConnector(connector_type="climate_data_store") + +# List all available CORDEX domains +domains = dc.connector.list_cordex_domains() +print(f"Available domains: {list(domains.keys())}") + +# Get information for a specific domain +eur_info = dc.connector.get_cordex_domain_info("EUR-44") +print(f"Europe domain: {eur_info}") + +# Download data for different regions +# North America +data_nam = dc.connector.get_data( + data_collection_name="projections-cordex-domains-single-levels", + date_start="2000-01-01", + date_end="2000-12-31", + bbox=[-120, 25, -70, 50], # Automatically mapped to NAM-44 + bands=["2m_air_temperature"] +) + +# Africa +data_afr = dc.connector.get_data( + data_collection_name="projections-cordex-domains-single-levels", + date_start="2000-01-01", + date_end="2000-12-31", + bbox=[10, -10, 40, 20], # Automatically mapped to AFR-44 + bands=["2m_air_temperature"] +) +``` + +### Comparing Historical and Future Scenarios + +```python +from terrakit import DataConnector + +dc = DataConnector(connector_type="climate_data_store") + +# Get historical baseline (1980-2010) +data_historical = dc.connector.get_data( + data_collection_name="projections-cordex-domains-single-levels", + date_start="1990-01-01", + date_end="1990-12-31", + bbox=[-10, 35, 30, 60], + bands=["2m_air_temperature"], + query_params={"experiment": "historical"} +) + +# Get future projection under RCP 8.5 (2070-2100) +data_future_rcp85 = dc.connector.get_data( + data_collection_name="projections-cordex-domains-single-levels", + date_start="2090-01-01", + date_end="2090-12-31", + bbox=[-10, 35, 30, 60], + bands=["2m_air_temperature"], + query_params={"experiment": "rcp_8_5"} +) + +# Compare the two periods +print(f"Historical mean: {data_historical.mean().values}") +print(f"Future RCP 8.5 mean: {data_future_rcp85.mean().values}") +print(f"Temperature change: {data_future_rcp85.mean().values - data_historical.mean().values} K") +``` + +## Notes + +- **Domain Selection**: TerraKit automatically maps your bounding box to the appropriate CORDEX domain +- **Model Availability**: Not all GCM-RCM combinations are available for all domains, experiments, and time periods +- **Resolution**: Higher resolutions (0.22°, 0.11°) may not be available for all domains +- **Time Periods**: + - Historical: typically 1950-2005 + - Evaluation: typically 1980-2010 + - Scenarios (RCP): typically 2006-2100 +- **Ensemble Members**: Different ensemble members represent uncertainty in initial conditions and model physics + +## See Also + +- [CORDEX regional climate model data on single levels](https://cds.climate.copernicus.eu/datasets/projections-cordex-domains-single-levels?tab=overview) - further details on CORDEX regional climate model data on single levels dataset. +- [ERA5 Parameters Guide](../../era5_bands_table.md) - Parameters for ERA5 reanalysis data +- [Climate Data Store Documentation](../climate_data_store.md) - Full CDS connector API reference \ No newline at end of file diff --git a/docs/api/data_connectors/collections/era5_domains.md b/docs/api/data_connectors/collections/era5_domains.md new file mode 100644 index 0000000..2cb60e5 --- /dev/null +++ b/docs/api/data_connectors/collections/era5_domains.md @@ -0,0 +1,444 @@ +# ERA5 Daily Statistics Bands + +**Collection:** `derived-era5-single-levels-daily-statistics` + +**Total bands:** 262 + +This collection provides post-processed ERA5 hourly single-level data aggregated to daily time steps. + +## Available Bands + +| # | Band Name | Description | +|---|-----------|-------------| +| 1 | `100m_u_component_of_wind` | | +| 2 | `100m_v_component_of_wind` | | +| 3 | `10m_u_component_of_neutral_wind` | | +| 4 | `10m_u_component_of_wind` | Eastward component of wind at 10 meters above surface | +| 5 | `10m_v_component_of_neutral_wind` | Northward component of neutral wind at 10 meters | +| 6 | `10m_v_component_of_wind` | Northward component of wind at 10 meters above surface | +| 7 | `10m_wind_gust_since_previous_post_processing` | | +| 8 | `2m_dewpoint_temperature` | Dewpoint temperature at 2 meters above surface | +| 9 | `2m_temperature` | Air temperature at 2 meters above surface | +| 10 | `air_density_over_the_oceans` | | +| 11 | `angle_of_sub_gridscale_orography` | | +| 12 | `anisotropy_of_sub_gridscale_orography` | | +| 13 | `benjamin_feir_index` | | +| 14 | `boundary_layer_dissipation` | | +| 15 | `boundary_layer_height` | | +| 16 | `charnock` | | +| 17 | `clear_sky_direct_solar_radiation_at_surface` | | +| 18 | `cloud_base_height` | | +| 19 | `coefficient_of_drag_with_waves` | | +| 20 | `convective_available_potential_energy` | | +| 21 | `convective_inhibition` | | +| 22 | `convective_precipitation` | | +| 23 | `convective_rain_rate` | | +| 24 | `convective_snowfall` | | +| 25 | `convective_snowfall_rate_water_equivalent` | | +| 26 | `downward_uv_radiation_at_the_surface` | | +| 27 | `duct_base_height` | | +| 28 | `eastward_gravity_wave_surface_stress` | | +| 29 | `eastward_turbulent_surface_stress` | | +| 30 | `evaporation` | | +| 31 | `forecast_albedo` | | +| 32 | `forecast_logarithm_of_surface_roughness_for_heat` | | +| 33 | `forecast_surface_roughness` | | +| 34 | `free_convective_velocity_over_the_oceans` | | +| 35 | `friction_velocity` | | +| 36 | `geopotential` | | +| 37 | `gravity_wave_dissipation` | | +| 38 | `high_cloud_cover` | | +| 39 | `high_vegetation_cover` | | +| 40 | `ice_temperature_layer_1` | | +| 41 | `ice_temperature_layer_2` | | +| 42 | `ice_temperature_layer_3` | | +| 43 | `ice_temperature_layer_4` | | +| 44 | `instantaneous_10m_wind_gust` | | +| 45 | `instantaneous_eastward_turbulent_surface_stress` | | +| 46 | `instantaneous_large_scale_surface_precipitation_fraction` | | +| 47 | `instantaneous_moisture_flux` | | +| 48 | `instantaneous_northward_turbulent_surface_stress` | | +| 49 | `instantaneous_surface_sensible_heat_flux` | | +| 50 | `k_index` | | +| 51 | `lake_bottom_temperature` | | +| 52 | `lake_cover` | | +| 53 | `lake_depth` | | +| 54 | `lake_ice_depth` | | +| 55 | `lake_ice_temperature` | | +| 56 | `lake_mix_layer_depth` | | +| 57 | `lake_mix_layer_temperature` | | +| 58 | `lake_shape_factor` | | +| 59 | `lake_total_layer_temperature` | | +| 60 | `land_sea_mask` | | +| 61 | `large_scale_precipitation` | | +| 62 | `large_scale_precipitation_fraction` | | +| 63 | `large_scale_rain_rate` | | +| 64 | `large_scale_snowfall` | | +| 65 | `large_scale_snowfall_rate_water_equivalent` | | +| 66 | `leaf_area_index_high_vegetation` | | +| 67 | `leaf_area_index_low_vegetation` | | +| 68 | `low_cloud_cover` | | +| 69 | `low_vegetation_cover` | | +| 70 | `maximum_2m_temperature_since_previous_post_processing` | | +| 71 | `maximum_individual_wave_height` | | +| 72 | `maximum_total_precipitation_rate_since_previous_post_processing` | | +| 73 | `mean_boundary_layer_dissipation` | | +| 74 | `mean_convective_precipitation_rate` | | +| 75 | `mean_convective_snowfall_rate` | | +| 76 | `mean_direction_of_total_swell` | | +| 77 | `mean_direction_of_wind_waves` | | +| 78 | `mean_eastward_gravity_wave_surface_stress` | | +| 79 | `mean_eastward_turbulent_surface_stress` | | +| 80 | `mean_evaporation_rate` | | +| 81 | `mean_gravity_wave_dissipation` | | +| 82 | `mean_large_scale_precipitation_fraction` | | +| 83 | `mean_large_scale_precipitation_rate` | | +| 84 | `mean_large_scale_snowfall_rate` | | +| 85 | `mean_northward_gravity_wave_surface_stress` | | +| 86 | `mean_northward_turbulent_surface_stress` | | +| 87 | `mean_period_of_total_swell` | | +| 88 | `mean_period_of_wind_waves` | | +| 89 | `mean_potential_evaporation_rate` | | +| 90 | `mean_runoff_rate` | | +| 91 | `mean_sea_level_pressure` | Atmospheric pressure adjusted to mean sea level | +| 92 | `mean_snow_evaporation_rate` | | +| 93 | `mean_snowfall_rate` | | +| 94 | `mean_snowmelt_rate` | | +| 95 | `mean_square_slope_of_waves` | | +| 96 | `mean_sub_surface_runoff_rate` | | +| 97 | `mean_surface_direct_short_wave_radiation_flux` | | +| 98 | `mean_surface_direct_short_wave_radiation_flux_clear_sky` | | +| 99 | `mean_surface_downward_long_wave_radiation_flux` | | +| 100 | `mean_surface_downward_long_wave_radiation_flux_clear_sky` | | +| 101 | `mean_surface_downward_short_wave_radiation_flux` | | +| 102 | `mean_surface_downward_short_wave_radiation_flux_clear_sky` | | +| 103 | `mean_surface_downward_uv_radiation_flux` | | +| 104 | `mean_surface_latent_heat_flux` | | +| 105 | `mean_surface_net_long_wave_radiation_flux` | | +| 106 | `mean_surface_net_long_wave_radiation_flux_clear_sky` | | +| 107 | `mean_surface_net_short_wave_radiation_flux` | | +| 108 | `mean_surface_net_short_wave_radiation_flux_clear_sky` | | +| 109 | `mean_surface_runoff_rate` | | +| 110 | `mean_surface_sensible_heat_flux` | | +| 111 | `mean_top_downward_short_wave_radiation_flux` | | +| 112 | `mean_top_net_long_wave_radiation_flux` | | +| 113 | `mean_top_net_long_wave_radiation_flux_clear_sky` | | +| 114 | `mean_top_net_short_wave_radiation_flux` | | +| 115 | `mean_top_net_short_wave_radiation_flux_clear_sky` | | +| 116 | `mean_total_precipitation_rate` | | +| 117 | `mean_vertical_gradient_of_refractivity_inside_trapping_layer` | | +| 118 | `mean_vertically_integrated_moisture_divergence` | | +| 119 | `mean_wave_direction` | | +| 120 | `mean_wave_direction_of_first_swell_partition` | | +| 121 | `mean_wave_direction_of_second_swell_partition` | | +| 122 | `mean_wave_direction_of_third_swell_partition` | | +| 123 | `mean_wave_period` | | +| 124 | `mean_wave_period_based_on_first_moment` | | +| 125 | `mean_wave_period_based_on_first_moment_for_swell` | | +| 126 | `mean_wave_period_based_on_first_moment_for_wind_waves` | | +| 127 | `mean_wave_period_based_on_second_moment_for_swell` | | +| 128 | `mean_wave_period_based_on_second_moment_for_wind_waves` | | +| 129 | `mean_wave_period_of_first_swell_partition` | | +| 130 | `mean_wave_period_of_second_swell_partition` | | +| 131 | `mean_wave_period_of_third_swell_partition` | | +| 132 | `mean_zero_crossing_wave_period` | | +| 133 | `medium_cloud_cover` | | +| 134 | `minimum_2m_temperature_since_previous_post_processing` | | +| 135 | `minimum_total_precipitation_rate_since_previous_post_processing` | | +| 136 | `minimum_vertical_gradient_of_refractivity_inside_trapping_layer` | | +| 137 | `model_bathymetry` | | +| 138 | `near_ir_albedo_for_diffuse_radiation` | | +| 139 | `near_ir_albedo_for_direct_radiation` | | +| 140 | `normalized_energy_flux_into_ocean` | | +| 141 | `normalized_energy_flux_into_waves` | | +| 142 | `normalized_stress_into_ocean` | | +| 143 | `northward_gravity_wave_surface_stress` | | +| 144 | `northward_turbulent_surface_stress` | | +| 145 | `ocean_surface_stress_equivalent_10m_neutral_wind_direction` | | +| 146 | `ocean_surface_stress_equivalent_10m_neutral_wind_speed` | | +| 147 | `peak_wave_period` | | +| 148 | `period_corresponding_to_maximum_individual_wave_height` | | +| 149 | `potential_evaporation` | | +| 150 | `precipitation_type` | | +| 151 | `runoff` | | +| 152 | `sea_ice_cover` | | +| 153 | `sea_surface_temperature` | | +| 154 | `significant_height_of_combined_wind_waves_and_swell` | | +| 155 | `significant_height_of_total_swell` | | +| 156 | `significant_height_of_wind_waves` | | +| 157 | `significant_wave_height_of_first_swell_partition` | | +| 158 | `significant_wave_height_of_second_swell_partition` | | +| 159 | `significant_wave_height_of_third_swell_partition` | | +| 160 | `skin_reservoir_content` | | +| 161 | `skin_temperature` | | +| 162 | `slope_of_sub_gridscale_orography` | | +| 163 | `snow_albedo` | | +| 164 | `snow_density` | | +| 165 | `snow_depth` | | +| 166 | `snow_evaporation` | | +| 167 | `snowfall` | | +| 168 | `snowmelt` | | +| 169 | `soil_temperature_level_1` | | +| 170 | `soil_temperature_level_2` | | +| 171 | `soil_temperature_level_3` | | +| 172 | `soil_temperature_level_4` | | +| 173 | `soil_type` | | +| 174 | `standard_deviation_of_filtered_subgrid_orography` | | +| 175 | `standard_deviation_of_orography` | | +| 176 | `sub_surface_runoff` | | +| 177 | `surface_latent_heat_flux` | | +| 178 | `surface_net_solar_radiation` | | +| 179 | `surface_net_solar_radiation_clear_sky` | | +| 180 | `surface_net_thermal_radiation` | | +| 181 | `surface_net_thermal_radiation_clear_sky` | | +| 182 | `surface_pressure` | Pressure at the surface | +| 183 | `surface_runoff` | | +| 184 | `surface_sensible_heat_flux` | | +| 185 | `surface_solar_radiation_downward_clear_sky` | | +| 186 | `surface_solar_radiation_downwards` | Downward solar radiation flux at the surface | +| 187 | `surface_thermal_radiation_downward_clear_sky` | | +| 188 | `surface_thermal_radiation_downwards` | Downward thermal radiation flux at the surface | +| 189 | `temperature_of_snow_layer` | | +| 190 | `toa_incident_solar_radiation` | | +| 191 | `top_net_solar_radiation` | | +| 192 | `top_net_solar_radiation_clear_sky` | | +| 193 | `top_net_thermal_radiation` | | +| 194 | `top_net_thermal_radiation_clear_sky` | | +| 195 | `total_cloud_cover` | | +| 196 | `total_column_cloud_ice_water` | | +| 197 | `total_column_cloud_liquid_water` | | +| 198 | `total_column_ozone` | | +| 199 | `total_column_rain_water` | | +| 200 | `total_column_snow_water` | | +| 201 | `total_column_supercooled_liquid_water` | | +| 202 | `total_column_water` | | +| 203 | `total_column_water_vapour` | | +| 204 | `total_precipitation` | Total precipitation (rain and snow) | +| 205 | `total_sky_direct_solar_radiation_at_surface` | | +| 206 | `total_totals_index` | | +| 207 | `trapping_layer_base_height` | | +| 208 | `trapping_layer_top_height` | | +| 209 | `type_of_high_vegetation` | | +| 210 | `type_of_low_vegetation` | | +| 211 | `u_component_stokes_drift` | | +| 212 | `uv_visible_albedo_for_diffuse_radiation` | | +| 213 | `uv_visible_albedo_for_direct_radiation` | | +| 214 | `v_component_stokes_drift` | | +| 215 | `vertical_integral_of_divergence_of_cloud_frozen_water_flux` | | +| 216 | `vertical_integral_of_divergence_of_cloud_liquid_water_flux` | | +| 217 | `vertical_integral_of_divergence_of_geopotential_flux` | | +| 218 | `vertical_integral_of_divergence_of_kinetic_energy_flux` | | +| 219 | `vertical_integral_of_divergence_of_mass_flux` | | +| 220 | `vertical_integral_of_divergence_of_moisture_flux` | | +| 221 | `vertical_integral_of_divergence_of_ozone_flux` | | +| 222 | `vertical_integral_of_divergence_of_thermal_energy_flux` | | +| 223 | `vertical_integral_of_divergence_of_total_energy_flux` | | +| 224 | `vertical_integral_of_eastward_cloud_frozen_water_flux` | | +| 225 | `vertical_integral_of_eastward_cloud_liquid_water_flux` | | +| 226 | `vertical_integral_of_eastward_geopotential_flux` | | +| 227 | `vertical_integral_of_eastward_heat_flux` | | +| 228 | `vertical_integral_of_eastward_kinetic_energy_flux` | | +| 229 | `vertical_integral_of_eastward_mass_flux` | | +| 230 | `vertical_integral_of_eastward_ozone_flux` | | +| 231 | `vertical_integral_of_eastward_total_energy_flux` | | +| 232 | `vertical_integral_of_eastward_water_vapour_flux` | | +| 233 | `vertical_integral_of_energy_conversion` | | +| 234 | `vertical_integral_of_kinetic_energy` | | +| 235 | `vertical_integral_of_mass_of_atmosphere` | | +| 236 | `vertical_integral_of_mass_tendency` | | +| 237 | `vertical_integral_of_northward_cloud_frozen_water_flux` | | +| 238 | `vertical_integral_of_northward_cloud_liquid_water_flux` | | +| 239 | `vertical_integral_of_northward_geopotential_flux` | | +| 240 | `vertical_integral_of_northward_heat_flux` | | +| 241 | `vertical_integral_of_northward_kinetic_energy_flux` | | +| 242 | `vertical_integral_of_northward_mass_flux` | | +| 243 | `vertical_integral_of_northward_ozone_flux` | | +| 244 | `vertical_integral_of_northward_total_energy_flux` | | +| 245 | `vertical_integral_of_northward_water_vapour_flux` | | +| 246 | `vertical_integral_of_potential_and_internal_energy` | | +| 247 | `vertical_integral_of_potential_internal_and_latent_energy` | | +| 248 | `vertical_integral_of_temperature` | | +| 249 | `vertical_integral_of_thermal_energy` | | +| 250 | `vertical_integral_of_total_energy` | | +| 251 | `vertically_integrated_moisture_divergence` | | +| 252 | `volumetric_soil_water_layer_1` | | +| 253 | `volumetric_soil_water_layer_2` | | +| 254 | `volumetric_soil_water_layer_3` | | +| 255 | `volumetric_soil_water_layer_4` | | +| 256 | `wave_spectral_directional_width` | | +| 257 | `wave_spectral_directional_width_for_swell` | | +| 258 | `wave_spectral_directional_width_for_wind_waves` | | +| 259 | `wave_spectral_kurtosis` | | +| 260 | `wave_spectral_peakedness` | | +| 261 | `wave_spectral_skewness` | | +| 262 | `zero_degree_level` | Height of 0 degC isotherm | + +## Query Parameters + +The CDS connector supports additional query parameters to customize data retrieval. These parameters can be passed via the `query_params` dictionary in the `get_data()` method. + +### Default Values + +When downloading ERA5 data, the following default parameters are automatically applied: + +| Parameter | Default Value | Description | +|-----------|---------------|-------------| +| `product_type` | `"reanalysis"` | Type of ERA5 product | +| `format` | `"netcdf"` | Output file format | +| `daily_statistic` | `"daily_mean"` | Daily aggregation statistic | +| `frequency` | `"6_hourly"` | Temporal frequency of source data | +| `time_zone` | `"utc+00:00"` | Time zone for daily aggregation | + +### Valid Parameter Values + +#### `daily_statistic` +Controls how hourly data is aggregated to daily values: + +- `"daily_mean"` (default) - Daily average +- `"daily_maximum"` - Daily maximum value +- `"daily_minimum"` - Daily minimum value +- `"daily_standard_deviation"` - Daily standard deviation + +#### `frequency` +Specifies the temporal resolution of the source data: + +- `"6_hourly"` (default) - 6-hour intervals +- `"1hr"` - Hourly data +- `"3hr"` - 3-hour intervals +- `"day"` - Daily data +- `"mon"` - Monthly data +- `"sem"` - Semi-annual data +- `"fx"` - Fixed/time-invariant data + +#### `time_zone` +Time zone for daily aggregation (UTC offset format): + +- `"utc+00:00"` (default) - UTC +- `"utc+01:00"` through `"utc+12:00"` - Positive UTC offsets +- `"utc-01:00"` through `"utc-12:00"` - Negative UTC offsets + +#### `product_type` +Type of ERA5 product: + +- `"reanalysis"` (default) - Standard reanalysis product +- `"ensemble_mean"` - Ensemble mean +- `"ensemble_members"` - Individual ensemble members +- `"ensemble_spread"` - Ensemble spread + +## Usage Examples + +### Basic Usage - List Bands + +```python +from terrakit import DataConnector + +# Initialize CDS connector +dc = DataConnector(connector_type="climate_data_store") + +# List all available bands +bands = dc.connector.list_bands(data_collection_name="derived-era5-single-levels-daily-statistics") + +# Print band information +for band in bands[:5]: # First 5 bands + print(f"Band: {band['band_name']}") + print(f"Description: {band.get('description', 'N/A')}") + print() +``` + +### Download with Default Parameters + +```python +from terrakit import DataConnector + +# Initialize CDS connector +dc = DataConnector(connector_type="climate_data_store") + +# Download data with default parameters +# (daily_mean, 6_hourly frequency, reanalysis product) +data = dc.connector.get_data( + data_collection_name="derived-era5-single-levels-daily-statistics", + date_start="2025-01-01", + date_end="2025-01-02", + bbox=[-1.32, 51.06, -1.30, 51.08], # [min_lon, min_lat, max_lon, max_lat] + bands=["2m_temperature", "total_precipitation"] +) + +print(f"Data shape: {data.shape}") +print(f"Dimensions: {data.dims}") +``` + +### Override Default Parameters with query_params + +```python +from terrakit import DataConnector + +# Initialize CDS connector +dc = DataConnector(connector_type="climate_data_store") + +# Example 1: Get daily maximum temperature instead of mean +data_max = dc.connector.get_data( + data_collection_name="derived-era5-single-levels-daily-statistics", + date_start="2025-01-01", + date_end="2025-01-02", + bbox=[-1.32, 51.06, -1.30, 51.08], + bands=["2m_temperature"], + query_params={"daily_statistic": "daily_maximum"} +) + +# Example 2: Get hourly data instead of 6-hourly +data_hourly = dc.connector.get_data( + data_collection_name="derived-era5-single-levels-daily-statistics", + date_start="2025-01-01", + date_end="2025-01-02", + bbox=[-1.32, 51.06, -1.30, 51.08], + bands=["10m_u_component_of_wind", "10m_v_component_of_wind"], + query_params={"frequency": "1hr"} +) + +# Example 3: Multiple parameter overrides +data_custom = dc.connector.get_data( + data_collection_name="derived-era5-single-levels-daily-statistics", + date_start="2025-01-01", + date_end="2025-01-02", + bbox=[-1.32, 51.06, -1.30, 51.08], + bands=["2m_temperature", "total_precipitation"], + query_params={ + "daily_statistic": "daily_minimum", + "frequency": "3hr", + "time_zone": "utc+03:00", + "product_type": "ensemble_mean" + } +) + +# Example 4: Get daily standard deviation for precipitation +data_std = dc.connector.get_data( + data_collection_name="derived-era5-single-levels-daily-statistics", + date_start="2025-01-01", + date_end="2025-01-31", + bbox=[-10.0, 35.0, 5.0, 45.0], # Larger area over Europe + bands=["total_precipitation"], + query_params={"daily_statistic": "daily_standard_deviation"} +) + +print(f"Standard deviation data shape: {data_std.shape}") +``` + +### Save Data to File + +```python +from terrakit import DataConnector + +# Initialize CDS connector +dc = DataConnector(connector_type="climate_data_store") + +# Download and save data +data = dc.connector.get_data( + data_collection_name="derived-era5-single-levels-daily-statistics", + date_start="2025-01-01", + date_end="2025-01-05", + bbox=[-1.32, 51.06, -1.30, 51.08], + bands=["2m_temperature", "total_precipitation"], + query_params={"daily_statistic": "daily_mean"}, + save_file="era5_data" # Will create era5_data_2025-01-01.tif, era5_data_2025-01-02.tif, etc. +) +``` diff --git a/docs/cordex_domains.md b/docs/cordex_domains.md deleted file mode 100644 index 45797e4..0000000 --- a/docs/cordex_domains.md +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) 2026 Copyright 2024 IBM Corp -// -// This software is released under the MIT License. -// https://opensource.org/licenses/MIT - -## CORDEX Domain Reference - -CORDEX (Coordinated Regional Climate Downscaling Experiment) defines specific regional domains for climate projections. Each domain covers a geographic region at one or more resolutions. - -### Available CORDEX Domains - -| Domain Code | Region Name | Bounding Box (min_lon, min_lat, max_lon, max_lat) | Resolution | Approx. Grid Spacing | -|-------------|-------------|---------------------------------------------------|------------|---------------------| -| **AFR-44** | Africa | -24.64, -45.76, 60.28, 42.24 | 0.44° | ~50 km | -| **AFR-22** | Africa | -24.64, -45.76, 60.28, 42.24 | 0.22° | ~25 km | -| **ANT-44** | Antarctica | -180.0, -89.5, 180.0, -60.0 | 0.44° | ~50 km | -| **ARC-44** | Arctic | -180.0, 60.0, 180.0, 90.0 | 0.44° | ~50 km | -| **AUS-44** | Australasia | 89.5, -52.36, 179.99, 12.21 | 0.44° | ~50 km | -| **AUS-22** | Australasia | 89.5, -52.36, 179.99, 12.21 | 0.22° | ~25 km | -| **CAM-44** | Central America | -122.0, -19.76, -59.52, 34.24 | 0.44° | ~50 km | -| **CAM-22** | Central America | -122.0, -19.76, -59.52, 34.24 | 0.22° | ~25 km | -| **CAS-44** | Central Asia | 34.0, 18.0, 115.0, 70.0 | 0.44° | ~50 km | -| **CAS-22** | Central Asia | 34.0, 18.0, 115.0, 70.0 | 0.22° | ~25 km | -| **EAS-44** | East Asia | 65.0, -15.0, 155.0, 65.0 | 0.44° | ~50 km | -| **EAS-22** | East Asia | 65.0, -15.0, 155.0, 65.0 | 0.22° | ~25 km | -| **EUR-44** | Europe | -44.0, 22.0, 65.0, 72.0 | 0.44° | ~50 km | -| **EUR-22** | Europe | -44.0, 22.0, 65.0, 72.0 | 0.22° | ~25 km | -| **EUR-11** | Europe | -44.0, 22.0, 65.0, 72.0 | 0.11° | ~12.5 km | -| **MED-44** | Mediterranean | -10.0, 30.0, 40.0, 48.0 | 0.44° | ~50 km | -| **MED-22** | Mediterranean | -10.0, 30.0, 40.0, 48.0 | 0.22° | ~25 km | -| **MNA-44** | Middle East & North Africa | -25.0, 0.0, 75.0, 50.0 | 0.44° | ~50 km | -| **MNA-22** | Middle East & North Africa | -25.0, 0.0, 75.0, 50.0 | 0.22° | ~25 km | -| **NAM-44** | North America | -172.0, 12.0, -35.0, 76.0 | 0.44° | ~50 km | -| **NAM-22** | North America | -172.0, 12.0, -35.0, 76.0 | 0.22° | ~25 km | -| **SAM-44** | South America | -93.0, -56.0, -25.0, 18.0 | 0.44° | ~50 km | -| **SAM-22** | South America | -93.0, -56.0, -25.0, 18.0 | 0.22° | ~25 km | -| **WAS-44** | South Asia (West Asia) | 20.0, -15.0, 115.0, 45.0 | 0.44° | ~50 km | -| **WAS-22** | South Asia (West Asia) | 20.0, -15.0, 115.0, 45.0 | 0.22° | ~25 km | -| **SEA-44** | Southeast Asia | 89.0, -15.0, 146.0, 27.0 | 0.44° | ~50 km | -| **SEA-22** | Southeast Asia | 89.0, -15.0, 146.0, 27.0 | 0.22° | ~25 km | - -### Domain Resolution Notes - -- **0.44°** (~50 km): Standard resolution, available for all domains -- **0.22°** (~25 km): High resolution, available for most domains -- **0.11°** (~12.5 km): Very high resolution, currently only available for Europe (EUR-11) - -### Usage with TerraKit - -When using CORDEX data with TerraKit's Climate Data Store connector, you can either: - -1. **Specify a bounding box** - TerraKit will automatically map it to the appropriate CORDEX domain: - ```python - dc.connector.find_data( - collection="projections-cordex-domains-single-levels", - date_start="2020-01-01", - date_end="2020-12-31", - bbox=[-10, 35, 30, 60] # Automatically mapped to EUR-44 or EUR-22 - ) - ``` - -2. **List available domains programmatically**: - - ```python - domains = dc.connector.list_cordex_domains() - ``` \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 29c0467..cc420c8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -11,7 +11,8 @@ nav: - Upload: upload.md - TerraKit DataConnectors: - Data Connectors: data_connectors.md - - Cordex Domains: cordex_domains.md + - CORDEX Domains: api/data_connectors/collections/cordex_domains.md + - ERA5 Domains: api/data_connectors/collections/era5_domains.md - Examples: - examples/labels_to_data.ipynb - examples/terrakit_download.ipynb From eef789520b766fc9fe0c4d8c3913925163e19077 Mon Sep 17 00:00:00 2001 From: Rosie Lickorish Date: Wed, 11 Feb 2026 23:35:31 +0000 Subject: [PATCH 15/18] Improve docstrings for get_data for other data connectors --- .secrets.baseline | 10 +++++----- docs/examples/terrakit_download.ipynb | 4 +++- .../download/data_connectors/connector_template.py | 10 +++++++--- terrakit/download/data_connectors/nasa_earthdata.py | 10 +++++++--- terrakit/download/data_connectors/sentinel_aws.py | 10 +++++++--- terrakit/download/data_connectors/sentinelhub.py | 10 +++++++--- 6 files changed, 36 insertions(+), 18 deletions(-) diff --git a/.secrets.baseline b/.secrets.baseline index a7b6683..9be08fd 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "^.secrets.baseline$", "lines": null }, - "generated_at": "2026-02-11T12:14:01Z", + "generated_at": "2026-02-11T23:35:17Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -227,28 +227,28 @@ { "hashed_secret": "22d3fe8cf3013398e16de38c7d1ab68a5ed77e37", "is_verified": false, - "line_number": 174, + "line_number": 176, "type": "Secret Keyword", "verified_result": null }, { "hashed_secret": "97f8a44eec24e0a90c4217c103e88b3ddfb7f2ba", "is_verified": false, - "line_number": 381, + "line_number": 383, "type": "Secret Keyword", "verified_result": null }, { "hashed_secret": "7a77237e10ab85b114a83affb9567c10eb4eda32", "is_verified": false, - "line_number": 383, + "line_number": 385, "type": "Secret Keyword", "verified_result": null }, { "hashed_secret": "156d45846d67877099a7a846d3cc9c16e7dc784a", "is_verified": false, - "line_number": 475, + "line_number": 477, "type": "Secret Keyword", "verified_result": null } diff --git a/docs/examples/terrakit_download.ipynb b/docs/examples/terrakit_download.ipynb index a68877c..55930b3 100644 --- a/docs/examples/terrakit_download.ipynb +++ b/docs/examples/terrakit_download.ipynb @@ -125,7 +125,9 @@ "id": "256c5594", "metadata": {}, "source": [ - "Now to query the data, we specify the bands we want to return, plus an (optional) save filename. The `get_data()` function will query the data from the data source and return an xarray object containing the data, plus if you provide `save_file=` as an argument, it will also save a geotiff file.\n", + "Now to query the data, we specify the bands we want to return, plus an (optional) save filename. The `get_data()` function will query the data from the data source and return an xarray object containing all the fetched data with dimensions (time, band, y, x). All dates are stacked along the time dimension, and all bands are stacked along the band dimension.\n", + "\n", + "Optionally, if `save_file=` is provided as an argument to `get_data()`, individual GeoTIFF files will be saved for each date with the naming pattern: {save_file}_{date}.tif (e.g., 'output_2025-01-01.tif', 'output_2025-01-02.tif'). Each file contains all requested bands for that specific date.\n", "\n", "If the bands are not found for the collection chosen, it will try to match the alt names stored in the internal collections catalogue, otherwise it will fail and tell you the available bands." ] diff --git a/terrakit/download/data_connectors/connector_template.py b/terrakit/download/data_connectors/connector_template.py index c0b5793..f8bea68 100644 --- a/terrakit/download/data_connectors/connector_template.py +++ b/terrakit/download/data_connectors/connector_template.py @@ -99,7 +99,7 @@ def get_data( working_dir=".", ): """ - Fetches data from SentinelHub for the specified collection, date range, area, and bands. + Fetches data from for the specified collection, date range, area, and bands. Args: data_collection_name (str): Name of the data collection to fetch data from. @@ -110,11 +110,15 @@ def get_data( bands (list, optional): List of bands to retrieve. Defaults to all bands. maxcc (int, optional): Maximum cloud cover threshold (0-100). Defaults to 100. data_connector_spec (dict, optional): Data connector specification. Defaults to None. - save_file (str, optional): Path to save the output file. Defaults to None. + save_file (str, optional): Path to save the output file. If provided, individual GeoTIFF files + will be saved for each date with the naming pattern: {save_file}_{date}.tif. Each file + contains all requested bands for that specific date. If None, no files are saved to disk. Defaults to None. working_dir (str, optional): Working directory for temporary files. Defaults to '.'. Returns: - xarray: An xarray Datasets containing the fetched data with dimensions (time, band, y, x). + xarray.DataArray: An xarray DataArray containing all fetched data with dimensions (time, band, y, x). + All dates are stacked along the time dimension, and all bands are stacked along the band dimension. + If save_file is provided, individual date files are also saved to disk. """ da = xr.DataArray() return da diff --git a/terrakit/download/data_connectors/nasa_earthdata.py b/terrakit/download/data_connectors/nasa_earthdata.py index 332295a..e4b44cb 100644 --- a/terrakit/download/data_connectors/nasa_earthdata.py +++ b/terrakit/download/data_connectors/nasa_earthdata.py @@ -1,4 +1,4 @@ -# © Copyright IBM Corporation 2025 +# © Copyright IBM Corporation 2025-2026 # SPDX-License-Identifier: Apache-2.0 @@ -336,11 +336,15 @@ def get_data( bands (list): List of bands to fetch. Defaults to []. maxcc (int, optional): Maximum cloud cover percentage. Defaults to 100. data_connector_spec (dict, optional): Additional data connector specifications. Defaults to None. - save_file (str, optional): Path to save the fetched data. Defaults to None. + save_file (str, optional): Path to save the fetched data. If provided, individual GeoTIFF files + will be saved for each date with the naming pattern: {save_file}_{date}.tif. Each file + contains all requested bands for that specific date. If None, no files are saved to disk. Defaults to None. working_dir (str, optional): Working directory for temporary files. Defaults to ".". Returns: - xarray: An xarray Datasets containing the fetched data with dimensions (time, band, y, x). + xarray.DataArray: An xarray DataArray containing all fetched data with dimensions (time, band, y, x). + All dates are stacked along the time dimension, and all bands are stacked along the band dimension. + If save_file is provided, individual date files are also saved to disk. """ # Check credentials have been set correctly. if "NASA_EARTH_BEARER_TOKEN" not in os.environ: diff --git a/terrakit/download/data_connectors/sentinel_aws.py b/terrakit/download/data_connectors/sentinel_aws.py index 57d8f33..c6f6c3b 100644 --- a/terrakit/download/data_connectors/sentinel_aws.py +++ b/terrakit/download/data_connectors/sentinel_aws.py @@ -1,4 +1,4 @@ -# © Copyright IBM Corporation 2025 +# © Copyright IBM Corporation 2025-2026 # SPDX-License-Identifier: Apache-2.0 @@ -410,11 +410,15 @@ def get_data( bands (list, optional): List of bands to retrieve. maxcc (int, optional): Maximum cloud cover percentage. data_connector_spec (dict, optional): Additional data connector specifications. - save_file (str, optional): Path to save the data. + save_file (str, optional): Path to save the data. If provided, individual GeoTIFF files + will be saved for each date with the naming pattern: {save_file}_{date}.tif. Each file + contains all requested bands for that specific date. If None, no files are saved to disk. Defaults to None. working_dir (str, optional): Working directory for saving files. Returns: - xarray: An xarray Datasets containing the fetched data with dimensions (time, band, y, x). + xarray.DataArray: An xarray DataArray containing all fetched data with dimensions (time, band, y, x). + All dates are stacked along the time dimension, and all bands are stacked along the band dimension. + If save_file is provided, individual date files are also saved to disk. """ check_collection_exists(data_collection_name, self.collections) # Check that the bands the user has requested exist in the data collection diff --git a/terrakit/download/data_connectors/sentinelhub.py b/terrakit/download/data_connectors/sentinelhub.py index cd251d1..893551f 100644 --- a/terrakit/download/data_connectors/sentinelhub.py +++ b/terrakit/download/data_connectors/sentinelhub.py @@ -1,4 +1,4 @@ -# © Copyright IBM Corporation 2025 +# © Copyright IBM Corporation 2025-2026 # SPDX-License-Identifier: Apache-2.0 @@ -380,11 +380,15 @@ def get_data( bands (list, optional): List of bands to retrieve. Defaults to all bands. maxcc (int, optional): Maximum cloud cover threshold (0-100). Defaults to 100. data_connector_spec (dict, optional): Data connector specification. Defaults to None. - save_file (str, optional): Path to save the output file. Defaults to None. + save_file (str, optional): Path to save the output file. If provided, individual GeoTIFF files + will be saved for each date with the naming pattern: {save_file}_{date}.tif. Each file + contains all requested bands for that specific date. If None, no files are saved to disk. Defaults to None. working_dir (str, optional): Working directory for temporary files. Defaults to '.'. Returns: - xarray: An xarray Datasets containing the fetched data with dimensions (time, band, y, x). + xarray.DataArray: An xarray DataArray containing all fetched data with dimensions (time, band, y, x). + All dates are stacked along the time dimension, and all bands are stacked along the band dimension. + If save_file is provided, individual date files are also saved to disk. Raises: TerrakitValidationError: If a validation error occurs. From 551db539e09cc3252ca72633f8abf09fd50b4a81 Mon Sep 17 00:00:00 2001 From: Rosie Lickorish Date: Wed, 11 Feb 2026 23:40:31 +0000 Subject: [PATCH 16/18] Script to generate test data for CDS data connector --- .../scripts/generate_cds_test_data.py | 237 ++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 tests/resources/scripts/generate_cds_test_data.py diff --git a/tests/resources/scripts/generate_cds_test_data.py b/tests/resources/scripts/generate_cds_test_data.py new file mode 100644 index 0000000..0569501 --- /dev/null +++ b/tests/resources/scripts/generate_cds_test_data.py @@ -0,0 +1,237 @@ +# © Copyright IBM Corporation 2026 +# SPDX-License-Identifier: Apache-2.0 + + +#!/usr/bin/env python3 +""" +Generate dummy CDS test data for mocking CDS API responses. + +This script creates a zip file containing NetCDF files that simulate +CDS API responses for ERA5 daily statistics data. + +The generated zip file will be saved to tests/resources/climate_data_store/ +for use by the mock_cds_client fixture. + +Usage: + python tests/resources/scripts/generate_cds_test_data.py +""" + +import numpy as np +import xarray as xr +import zipfile +from pathlib import Path +from datetime import datetime + +# Output configuration +OUTPUT_DIR = Path("tests/resources/climate_data_store") +OUTPUT_ZIP_NAME = "era5_daily_statistics_test_data.zip" +TEMP_DIR = OUTPUT_DIR / "temp_netcdf" + +# Data configuration matching the mock_cds_client comment +VARIABLES = [ + "10m_u_component_of_wind", + "10m_v_component_of_wind", + "2m_temperature", + "total_precipitation", + "10m_wind_gust_since_previous_post_processing", +] + +# Spatial configuration (small test area for manageable file size) +# Using a small region instead of global to keep file size reasonable +LAT_MIN, LAT_MAX = 50.0, 52.0 +LON_MIN, LON_MAX = -2.0, 0.0 +LAT_RESOLUTION = 0.25 # 0.25 degree resolution +LON_RESOLUTION = 0.25 + +# Temporal configuration +DATES = ["2025-01-01", "2025-01-02"] # Two days as per mock comment +TIME_ZONE = "utc+00:00" + +# Variable metadata (units and typical value ranges) +VARIABLE_METADATA = { + "10m_u_component_of_wind": { + "units": "m s**-1", + "long_name": "10 metre U wind component", + "standard_name": "eastward_wind", + "mean": 0.0, + "std": 5.0, + }, + "10m_v_component_of_wind": { + "units": "m s**-1", + "long_name": "10 metre V wind component", + "standard_name": "northward_wind", + "mean": 0.0, + "std": 5.0, + }, + "2m_temperature": { + "units": "K", + "long_name": "2 metre temperature", + "standard_name": "air_temperature", + "mean": 288.0, # ~15°C + "std": 15.0, + }, + "total_precipitation": { + "units": "m", + "long_name": "Total precipitation", + "standard_name": "precipitation_amount", + "mean": 0.002, # 2mm + "std": 0.005, + }, + "10m_wind_gust_since_previous_post_processing": { + "units": "m s**-1", + "long_name": "10 metre wind gust since previous post-processing", + "mean": 8.0, + "std": 4.0, + }, +} + + +def create_netcdf_for_variable(variable_name: str, dates: list, output_dir: Path): + """ + Create a NetCDF file for a single variable with realistic dummy data. + + Args: + variable_name: Name of the variable (e.g., '2m_temperature') + dates: List of date strings in 'YYYY-MM-DD' format + output_dir: Directory to save the NetCDF file + """ + print(f"Creating NetCDF for {variable_name}...") + + # Create coordinate arrays + lats = np.arange(LAT_MIN, LAT_MAX + LAT_RESOLUTION, LAT_RESOLUTION) + lons = np.arange(LON_MIN, LON_MAX + LON_RESOLUTION, LON_RESOLUTION) + times = [datetime.strptime(d, "%Y-%m-%d") for d in dates] + + # Get variable metadata + metadata = VARIABLE_METADATA[variable_name] + + # Generate realistic dummy data + # Shape: (time, lat, lon) + shape = (len(times), len(lats), len(lons)) + + # Create spatially and temporally varying data + np.random.seed(42) # For reproducibility + + # Base random data + data = np.random.normal(metadata["mean"], metadata["std"], shape) + + # Add spatial patterns (latitude-dependent for temperature-like variables) + if "temperature" in variable_name.lower(): + # Temperature decreases with latitude + lat_effect = np.cos(np.deg2rad(lats)) * 20 # ±20K variation + data += lat_effect[np.newaxis, :, np.newaxis] + + # Ensure non-negative values for precipitation + if "precipitation" in variable_name.lower(): + data = np.abs(data) + + # Create xarray Dataset + ds = xr.Dataset( + { + variable_name: ( + ["time", "latitude", "longitude"], + data.astype(np.float32), + { + "units": metadata["units"], + "long_name": metadata["long_name"], + "standard_name": metadata.get("standard_name", variable_name), + }, + ) + }, + coords={ + "time": times, + "latitude": lats, + "longitude": lons, + }, + ) + + # Add global attributes + ds.attrs = { + "Conventions": "CF-1.6", + "history": f"Generated by generate_cds_test_data.py on {datetime.now().isoformat()}", + "source": "Dummy test data for CDS API mocking", + "institution": "IBM Research", + } + + # Add coordinate attributes + ds["time"].attrs = { + "standard_name": "time", + "long_name": "time", + } + ds["latitude"].attrs = { + "units": "degrees_north", + "standard_name": "latitude", + "long_name": "latitude", + } + ds["longitude"].attrs = { + "units": "degrees_east", + "standard_name": "longitude", + "long_name": "longitude", + } + + # Save to NetCDF + output_file = output_dir / f"{variable_name}.nc" + ds.to_netcdf(output_file, format="NETCDF4", engine="netcdf4") + print(f" Saved: {output_file}") + + return output_file + + +def create_cds_test_zip(): + """ + Create a zip file containing NetCDF files for all variables. + + This mimics the structure of a CDS API response where each variable + is in a separate NetCDF file within a zip archive. + """ + print("=" * 70) + print("Generating CDS Test Data") + print("=" * 70) + + # Create output and temporary directories + OUTPUT_DIR.mkdir(parents=True, exist_ok=True) + TEMP_DIR.mkdir(parents=True, exist_ok=True) + + # Create NetCDF files for each variable + netcdf_files = [] + for variable in VARIABLES: + nc_file = create_netcdf_for_variable(variable, DATES, TEMP_DIR) + netcdf_files.append(nc_file) + + # Create zip file + output_zip = OUTPUT_DIR / OUTPUT_ZIP_NAME + print(f"\nCreating zip file: {output_zip}") + with zipfile.ZipFile(output_zip, "w", zipfile.ZIP_DEFLATED) as zipf: + for nc_file in netcdf_files: + zipf.write(nc_file, nc_file.name) + print(f" Added: {nc_file.name}") + + # Clean up temporary directory + print(f"\nCleaning up temporary directory: {TEMP_DIR}") + import shutil + + shutil.rmtree(TEMP_DIR) + + # Print summary + print("\n" + "=" * 70) + print("SUCCESS!") + print("=" * 70) + print(f"Created: {output_zip}") + print(f"Variables: {len(VARIABLES)}") + for var in VARIABLES: + print(f" - {var}") + print(f"Dates: {len(DATES)} ({DATES[0]} to {DATES[-1]})") + print(f"Spatial extent: {LAT_MIN}°N to {LAT_MAX}°N, {LON_MIN}°E to {LON_MAX}°E") + print(f"Resolution: {LAT_RESOLUTION}° × {LON_RESOLUTION}°") + + # Get file size + file_size = output_zip.stat().st_size / (1024 * 1024) # MB + print(f"File size: {file_size:.2f} MB") + print("\nThis file can be used by the mock_cds_client fixture for testing.") + print("=" * 70) + + +if __name__ == "__main__": + create_cds_test_zip() + +# Made with Bob From 01aaafa97a45694de1982a093ed9f4ddc2c72c14 Mon Sep 17 00:00:00 2001 From: Rosie Lickorish Date: Sun, 22 Mar 2026 10:20:12 +0000 Subject: [PATCH 17/18] Improve handling of era5 get_data --- .secrets.baseline | 11 +- docs/data_connectors.md | 4 +- docs/examples/terrakit_download_cds.ipynb | 291 ++++++++++++++++++ ...el-2-l2a_2025-04-02_labels_geojson.geojson | 8 + .../data_connectors/climate_data_store.py | 69 ++++- terrakit/download/geodata_utils.py | 176 ++++++++++- tests/component_tests/download/conftest.py | 68 ++++ .../test_climate_data_store.py | 84 +++++ .../download/test_geodata_utils.py | 180 +++++++++++ 9 files changed, 869 insertions(+), 22 deletions(-) create mode 100644 docs/examples/terrakit_download_cds.ipynb create mode 100644 docs/examples/test_ship_labels/sentinel_aws_sentinel-2-l2a_2025-04-02_labels_geojson.geojson create mode 100644 tests/component_tests/download/test_geodata_utils.py diff --git a/.secrets.baseline b/.secrets.baseline index 9be08fd..45c7ef0 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "^.secrets.baseline$", "lines": null }, - "generated_at": "2026-02-11T23:35:17Z", + "generated_at": "2026-03-22T10:19:45Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -253,6 +253,15 @@ "verified_result": null } ], + "docs/examples/terrakit_download_cds.ipynb": [ + { + "hashed_secret": "94d5b98c35d4453f9e867715e61e004c60a11b12", + "is_verified": false, + "line_number": 219, + "type": "Hex High Entropy String", + "verified_result": null + } + ], "terrakit/general_utils/labels_downloader.py": [ { "hashed_secret": "34fd2a7f5faa004cd1b9e4a22aa09b16d521661b", diff --git a/docs/data_connectors.md b/docs/data_connectors.md index 56b1f8c..4c8056a 100644 --- a/docs/data_connectors.md +++ b/docs/data_connectors.md @@ -43,8 +43,8 @@ Access sentinel AWS data is open and does not require any credentials. Create an account at [https://cds.climate.copernicus.eu/](https://cds.climate.copernicus.eu/). Once created, find your API key under the `Profile` section. Each dataset may also require accepting the licence agreement. If this is the case, the first time a request is made, an error will be returned with the url to visit to accept the terms. Available collections include: -- [ERA5 post-processed daily statistics on single levels from 1940 to present](https://cds.climate.copernicus.eu/datasets/derived-era5-single-levels-daily-statistics?tab=overview) -- [CORDEX regional climate model data on single levels](https://cds.climate.copernicus.eu/datasets/projections-cordex-domains-single-levels?tab=overview) + - [ERA5 post-processed daily statistics on single levels from 1940 to present](https://cds.climate.copernicus.eu/datasets/derived-era5-single-levels-daily-statistics?tab=overview) + - [CORDEX regional climate model data on single levels](https://cds.climate.copernicus.eu/datasets/projections-cordex-domains-single-levels?tab=overview) ### The Weather Company To access The Weather Company, register for an account and requests an API Key [https://www.weathercompany.com/weather-data-apis/](https://www.weathercompany.com/weather-data-apis/). Once you have an API key, set the following environment variable: diff --git a/docs/examples/terrakit_download_cds.ipynb b/docs/examples/terrakit_download_cds.ipynb new file mode 100644 index 0000000..6ab7060 --- /dev/null +++ b/docs/examples/terrakit_download_cds.ipynb @@ -0,0 +1,291 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "baf11c7a", + "metadata": {}, + "source": [ + "\n", + "\n", + "# TerraKit: Climate Data Store search and query\n", + "\n", + "Use the TerraKit Climate Data Store Data Connector to search and query data from [CDS](https://cds.climate.copernicus.eu/datasets/). Available collections include:\n", + "\n", + " - [ERA5 post-processed daily statistics on single levels from 1940 to present](https://cds.climate.copernicus.eu/datasets/derived-era5-single-levels-daily-statistics?tab=overview)\n", + " - [CORDEX regional climate model data on single levels](https://cds.climate.copernicus.eu/datasets/projections-cordex-domains-single-levels?tab=overview)\n", + "\n", + "

\n", + "Install TerraKit For instructions on how to install TerraKit, take a look at the Welcome page.\n", + "
\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "b56cfe38", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "\n", + "from terrakit import DataConnector\n", + "from terrakit.download.transformations.impute_nans_xarray import impute_nans_xarray\n", + "from terrakit.download.transformations.scale_data_xarray import scale_data_xarray\n", + "from terrakit.download.geodata_utils import save_data_array_to_file" + ] + }, + { + "cell_type": "markdown", + "id": "675af303", + "metadata": {}, + "source": [ + "\n", + "\n", + "### Create an account\n", + "Create an account at [https://cds.climate.copernicus.eu/](https://cds.climate.copernicus.eu/). Once created, find your API key under the `Profile` section. Each dataset may also require accepting the licence agreement. If this is the case, the first time a request is made, an error will be returned with the url to visit to accept the terms.\n" + ] + }, + { + "cell_type": "markdown", + "id": "90626647", + "metadata": {}, + "source": [ + "### Connect to CDS" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "72f17643", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2026-03-16 19:12:13,321 - INFO - Initializing DataConnector with connector type: climate_data_store\n", + "2026-03-16 19:12:13,321 - INFO - climate_data_store\n", + "2026-03-16 19:12:13,323 - INFO - climate_data_store\n", + "2026-03-16 19:12:13,325 - INFO - Listing available collections\n" + ] + }, + { + "data": { + "text/plain": [ + "['projections-cordex-domains-single-levels',\n", + " 'derived-era5-single-levels-daily-statistics']" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "data_connector = \"climate_data_store\"\n", + "dc = DataConnector(connector_type=data_connector)\n", + "dc.connector.list_collections()" + ] + }, + { + "cell_type": "markdown", + "id": "b23ea8ab", + "metadata": {}, + "source": [ + "\n", + "\n", + "TerraKit defines a bounding box as a list using the format `[min_lon, min_lat, max_lon, max_lat]`, which is equivalent to `[West, South, East, North]`. Let's do that!" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "1066cd9a", + "metadata": {}, + "outputs": [], + "source": [ + "bbox = [\n", + " 34.5,\n", + " -0.5,\n", + " 35.0,\n", + " 0.0,\n", + "] # [West, South, East, North] / [min_lon, min_lat, max_lon, max_lat]" + ] + }, + { + "cell_type": "markdown", + "id": "79bfea17", + "metadata": {}, + "source": [ + "### Derived ERA5 Single Levels Daily Statistics\n" + ] + }, + { + "cell_type": "markdown", + "id": "65c6c796", + "metadata": {}, + "source": [ + "Now we can search for data from the Derived ERA5 Single Levels Daily Statistics collection.\n", + "\n", + "The `find_data()` function will search for data and return both a list of unique dates where data is available, but also the raw results from the search." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "05876bd3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1940-01-01 00:00:00 2026-02-04 00:00:00\n", + "['2025-01-01', '2025-01-02']\n", + "[{'collection': 'derived-era5-single-levels-daily-statistics', 'date_range': '2025-01-01 to 2025-01-02', 'total_dates': 2, 'temporal_extent': {'interval': [['1940-01-01T00:00:00+00:00', '2026-02-04T00:00:00+00:00']]}, 'spatial_extent': {'bbox': [[0.0, -89.0, 360.0, 89.0]]}}]\n" + ] + } + ], + "source": [ + "collection_name = \"derived-era5-single-levels-daily-statistics\"\n", + "\n", + "bands = [\"2m_temperature\", \"mean_total_precipitation_rate\"]\n", + "\n", + "unique_dates, results = dc.connector.find_data(\n", + " data_collection_name=collection_name,\n", + " date_start=\"2025-01-01\",\n", + " date_end=\"2025-01-02\",\n", + " bands=bands,\n", + " bbox=bbox,\n", + ")\n", + "\n", + "print(unique_dates)\n", + "print(results)" + ] + }, + { + "cell_type": "markdown", + "id": "256c5594", + "metadata": {}, + "source": [ + "Now to query the data, we specify the bands we want to return, plus an (optional) save filename. The `get_data()` function will query the data from the data source and return an xarray object containing all the fetched data with dimensions (time, band, y, x). All dates are stacked along the time dimension, and all bands are stacked along the band dimension.\n", + "\n", + "Optionally, if `save_file=` is provided as an argument to `get_data()`, individual GeoTIFF files will be saved for each date with the naming pattern: {save_file}_{date}.tif (e.g., 'output_2025-01-01.tif', 'output_2025-01-02.tif'). Each file contains all requested bands for that specific date." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "ee89f199", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2026-03-16 19:12:44,636 - INFO - Submitting CDS request for derived-era5-single-levels-daily-statistics\n", + "2026-03-16 19:12:44,637 - INFO - Date range: 2025-01-01 to 2025-01-02 (2 days)\n", + "2026-03-16 19:12:44,637 - INFO - Area: 3080.22 km²\n", + "2026-03-16 19:12:44,637 - INFO - Variables: 2\n", + "2026-03-16 19:12:44,637 - INFO - Estimated size: ~0.12 MB\n", + "2026-03-16 19:12:44,638 - INFO - Estimated time: ~2.0 minutes\n", + "2026-03-16 19:12:45,100 - INFO - Request submitted to CDS queue. Please wait...\n", + "2026-03-16 19:12:45,793 INFO Request ID is bbf3a88d-e41e-41ac-8213-b2ce3324b64f\n", + "2026-03-16 19:12:45,793 - INFO - Request ID is bbf3a88d-e41e-41ac-8213-b2ce3324b64f\n", + "2026-03-16 19:12:45,856 INFO status has been updated to accepted\n", + "2026-03-16 19:12:45,856 - INFO - status has been updated to accepted\n", + "2026-03-16 19:13:00,005 INFO status has been updated to successful\n", + "2026-03-16 19:13:00,005 - INFO - status has been updated to successful\n", + "2026-03-16 19:13:00,241 - INFO - Downloading https://object-store.os-api.cci2.ecmwf.int:443/cci2-prod-cache-1/2026-03-16/f8c113b5e91ec99cae15946945e28136.zip\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "6506c2aacf1d422c86bc4a0d61fa44ff", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "f8c113b5e91ec99cae15946945e28136.zip: 0%| | 0.00/49.6k [00:00 Dict[str, str]: + """ + Create a mapping from NetCDF variable names to requested band names. + + Data providers often return abbreviated variable names (e.g., "t2m" for "2m_temperature"). + This function intelligently matches NetCDF variable names to the requested band names + using multiple strategies: attribute matching, substring matching, and pattern matching. + + Args: + netcdf_files: List of paths to NetCDF files + requested_bands: List of requested band/variable names + + Returns: + Dictionary mapping NetCDF variable names to requested band names + + Example: + >>> netcdf_files = ["data.nc"] + >>> requested_bands = ["2m_temperature", "mean_total_precipitation_rate"] + >>> mapping = map_netcdf_variables_to_requested_bands(netcdf_files, requested_bands) + >>> print(mapping) + {'t2m': '2m_temperature', 'avg_tprate': 'mean_total_precipitation_rate'} + """ + netcdf_to_band_map: Dict[str, str] = {} + + # Build mapping by examining NetCDF variable attributes + for netcdf_file in netcdf_files: + ds = xr.open_dataset(netcdf_file) + + # Determine dimension names (common variations) + lon_name = "longitude" if "longitude" in ds.dims else "lon" + lat_name = "latitude" if "latitude" in ds.dims else "lat" + time_name = "time" if "time" in ds.dims else "valid_time" + + # Get data variables (exclude coordinate variables) + data_vars = [ + v for v in ds.data_vars if v not in [lon_name, lat_name, time_name] + ] + + for netcdf_var in data_vars: + if netcdf_var in netcdf_to_band_map: + continue # Already mapped + + # Try to match using variable attributes + var_attrs = ds[netcdf_var].attrs + long_name = var_attrs.get("long_name", "").lower() + standard_name = var_attrs.get("standard_name", "").lower() + + # Try to find a matching requested band + matched = False + match_method = "" + + for requested_band in requested_bands: + requested_normalized = requested_band.replace("_", " ").lower() + + # Strategy 1: Check if requested band name appears in long_name or standard_name + if ( + requested_normalized in long_name + or requested_normalized in standard_name + ): + netcdf_to_band_map[netcdf_var] = requested_band + matched = True + match_method = "attribute matching (long_name/standard_name)" + break + + # Strategy 2: Check if NetCDF var name is part of the requested band name + # e.g., "t2m" might be in "2m_temperature" (after normalization) + if netcdf_var in requested_band.replace("_", ""): + netcdf_to_band_map[netcdf_var] = requested_band + matched = True + match_method = "NetCDF variable name substring matching" + break + + # Strategy 3: Check for common abbreviation patterns + # e.g., "avg_tprate" -> "mean_total_precipitation_rate" + if "avg" in netcdf_var and "mean" in requested_band: + # Check if other parts match + netcdf_parts = netcdf_var.replace("avg_", "").replace("_", "") + requested_parts = requested_band.replace("mean_", "").replace( + "_", "" + ) + if ( + netcdf_parts in requested_parts + or requested_parts in netcdf_parts + ): + netcdf_to_band_map[netcdf_var] = requested_band + matched = True + match_method = "abbreviation pattern matching (avg->mean)" + break + + # Log the result + if matched: + logger.info( + f"Mapped NetCDF variable '{netcdf_var}' to requested band " + f"'{netcdf_to_band_map[netcdf_var]}' using {match_method}" + ) + else: + # If no match found, use the NetCDF variable name as fallback + netcdf_to_band_map[netcdf_var] = netcdf_var + logger.warning( + f"Could not match NetCDF variable '{netcdf_var}' to any requested band. " + f"Using NetCDF variable name '{netcdf_var}' as band label. " + f"Requested bands: {requested_bands}" + ) + + ds.close() + + logger.info(f"Final band name mapping: {netcdf_to_band_map}") + return netcdf_to_band_map + + def list_data_connectors(as_json: bool = False) -> Union[list, Dict[str, Any], Any]: """ List available data connectors. @@ -358,14 +472,70 @@ def save_data_array_to_file(da, save_file, imputed=False) -> None: def save_cog(ds, filename="cogeo.tif") -> None: """ - Save an xarray Dataset as a Cloud Optimized GeoTIFF. + Save an xarray Dataset as a Cloud Optimized GeoTIFF with band descriptions. Parameters: ds (xarray.Dataset): The input Dataset. filename (str): The path and filename for the output GeoTIFF. """ logger.info(f"Saving cloud optimized geotiff to {filename}") - ds.rio.to_raster(raster_path=filename, driver="COG") + + # Check if we need to set band descriptions + set_band_descriptions = False + band_names = [] + if "band" in ds.coords and hasattr(ds, "band"): + band_names = ds.band.values + set_band_descriptions = len(band_names) > 0 + + if set_band_descriptions: + # For COG with band descriptions, we need a two-step process: + # 1. Save as regular GeoTIFF with band descriptions + # 2. Convert to COG format + import tempfile + from pathlib import Path + + # Create temporary file for intermediate GeoTIFF + temp_dir = Path(filename).parent + with tempfile.NamedTemporaryFile( + suffix=".tif", dir=temp_dir, delete=False + ) as tmp: + temp_file = tmp.name + + try: + # Step 1: Save as regular GeoTIFF + ds.rio.to_raster(raster_path=temp_file, driver="GTiff") + + # Step 2: Set band descriptions on the regular GeoTIFF + with rasterio.open(temp_file, "r+") as dst: + for i, band_name in enumerate(band_names, start=1): + dst.set_band_description(i, str(band_name)) + + # Step 3: Convert to COG with band descriptions preserved + with rasterio.open(temp_file) as src: + # Create a clean profile for COG, removing unsupported options + profile = src.profile.copy() + # Remove options that COG driver doesn't support + for key in ["blockxsize", "blockysize", "tiled", "interleave"]: + profile.pop(key, None) + profile.update( + driver="COG", + compress="deflate", + ) + + with rasterio.open(filename, "w", **profile) as dst: + # Copy data + for i in range(1, src.count + 1): + dst.write(src.read(i), i) + # Copy band description + dst.set_band_description(i, src.descriptions[i - 1]) + + logger.info(f"Set band descriptions: {[str(b) for b in band_names]}") + finally: + # Clean up temporary file + Path(temp_file).unlink(missing_ok=True) + else: + # No band descriptions needed, save directly as COG + ds.rio.to_raster(raster_path=filename, driver="COG") def save_data_array_as_netcdf( diff --git a/tests/component_tests/download/conftest.py b/tests/component_tests/download/conftest.py index 742f68b..1cdb0d5 100644 --- a/tests/component_tests/download/conftest.py +++ b/tests/component_tests/download/conftest.py @@ -446,3 +446,71 @@ def mock_cdsapi_client(*args, **kwargs): monkeypatch.setattr("cdsapi.Client", mock_cdsapi_client) return mock_client + + +@pytest.fixture +def mock_cds_client_bbox_error(monkeypatch): + """ + Mock CDS API client that simulates MARS error for bbox too small. + + This fixture simulates the actual error returned by MARS when the bounding box + is smaller than the grid resolution (0.25° for ERA5). + + Usage: + def test_bbox_too_small(mock_cds_client_bbox_error): + # CDS API calls will raise HTTPError simulating MARS bbox error + dc = DataConnector(connector_type="climate_data_store") + with pytest.raises(TerrakitValidationError): + data = dc.connector.get_data(...) + """ + import json + from unittest.mock import MagicMock + from requests import HTTPError + from unittest.mock import Mock + + # Create mock client + mock_client = MagicMock() + + def mock_retrieve_bbox_error(collection_name, request_params, output_path): + """Simulate MARS error for bbox too small.""" + # Simulate the actual MARS error response + response = Mock() + response.status_code = 400 + response.reason = "Bad Request" + response.url = ( + "https://cds.climate.copernicus.eu/api/retrieve/v1/jobs/test-job-id/results" + ) + + area = request_params.get("area", []) + response.text = json.dumps( + { + "error": { + "message": "The job has failed\nMARS has returned an error, please check your selection.\n" + f"Request submitted to the MARS server:\n[{{'area': {area}}}]\n" + "Full error message:\n" + "mars - ERROR - Exception: Assertion failed: Area: non-empty area crop/mask (to at least one point)" + } + } + ) + + error_msg = ( + f"400 Client Error: Bad Request for url: {response.url}\n" + "The job has failed\n" + "MARS has returned an error, please check your selection.\n" + f"Request submitted to the MARS server:\n[{{'area': {area}}}]\n" + "Full error message:\n" + "mars - ERROR - Exception: Assertion failed: Area: non-empty area crop/mask (to at least one point)" + ) + raise HTTPError(error_msg, response=response) + + # Assign mock retrieve method + mock_client.retrieve = mock_retrieve_bbox_error + + # Mock the cdsapi.Client class to return our mock + def mock_cdsapi_client(*args, **kwargs): + return mock_client + + # Patch cdsapi.Client + monkeypatch.setattr("cdsapi.Client", mock_cdsapi_client) + + return mock_client diff --git a/tests/component_tests/download/data_connectors/test_climate_data_store.py b/tests/component_tests/download/data_connectors/test_climate_data_store.py index a5a5e76..16cbc2b 100644 --- a/tests/component_tests/download/data_connectors/test_climate_data_store.py +++ b/tests/component_tests/download/data_connectors/test_climate_data_store.py @@ -20,6 +20,12 @@ class TestClimateDataStore: # Mock data contains these 5 bands from the test zip file bands = ["fg10", "t2m", "tp", "u10", "v10"] + @pytest.fixture + def bbox(self): + """Override default bbox with one large enough for ERA5 (0.25° resolution).""" + # Kenya region: ~0.5° × 0.5° bbox (meets 0.25° minimum requirement) + return [34.5, -0.5, 35.0, 0.0] + def test_valid_data_connector(self): dc = DataConnector(connector_type=self.connector_type) assert dc.connector is not None @@ -128,6 +134,56 @@ def test_find_available_data_cds_start_data_given_constraints( ) assert unique_dates == [start_date, end_date] + def test_find_available_data_box_too_small_for_era5_resolution(self, start_date): + """ + Test that find_data raises error for bbox smaller than ERA5 grid resolution (0.25°). + """ + collection = "derived-era5-single-levels-daily-statistics" + # This bbox is only 0.02° x 0.02°, which is smaller than ERA5's 0.25° grid + tiny_bbox = [-1.32, 51.06, -1.30, 51.08] + + dc = DataConnector(connector_type=self.connector_type) + with pytest.raises( + TerrakitValidationError, + match="Bounding box too small for ERA5 data resolution", + ): + dc.connector.find_data( + collection, start_date, start_date, bbox=tiny_bbox, bands=self.bands + ) + + def test_negative_longitude_conversion( + self, mock_cds_client, start_date, bbox, save_file_dir, get_data_clean_up + ): + """ + Test that negative longitudes are automatically converted to 0-360° convention. + + CDS uses 0-360° longitude convention, but users typically use -180 to 180°. + The connector should automatically convert negative longitudes. + """ + collection = "derived-era5-single-levels-daily-statistics" + # Use bbox with negative longitude (standard -180 to 180° convention) + # Oxford, UK: -1.32° to -1.07° should be converted to 358.68° to 358.93° + negative_lon_bbox = [-1.32, 51.70, -1.07, 51.95] + save_file = f"{save_file_dir}/{self.connector_type}_{collection}.tif" + + dc = DataConnector(connector_type=self.connector_type) + + # This should work - the connector should convert negative longitudes + data = dc.connector.get_data( + data_collection_name=collection, + date_start=start_date, + date_end=start_date, + bbox=negative_lon_bbox, + bands=self.bands, + save_file=save_file, + ) + + assert data is not None + assert len(data) > 0 + + # Verify the data was retrieved successfully + assert isinstance(data, xr.DataArray) + @pytest.mark.parametrize( "collection", [ @@ -171,3 +227,31 @@ def test_get_data_cds( # Check that files were created for the dates in the mock data assert os.path.exists(save_file.replace(".tif", "_2025-01-01.tif")) is True assert os.path.exists(save_file.replace(".tif", "_2025-01-02.tif")) is True + + def test_get_data_bbox_too_small_for_era5_resolution( + self, mock_cds_client_bbox_error, start_date, save_file_dir + ): + """ + Test that get_data handles MARS error for bbox smaller than ERA5 grid resolution (0.25°). + + This test uses a mock that simulates the actual MARS error response when the bbox + is too small. The error should be caught and converted to a TerrakitValidationError + with a helpful message. + """ + collection = "derived-era5-single-levels-daily-statistics" + # This bbox is only 0.02° x 0.02°, which is smaller than ERA5's 0.25° grid + tiny_bbox = [-1.32, 51.06, -1.30, 51.08] + save_file = f"{save_file_dir}/{self.connector_type}_{collection}.tif" + + dc = DataConnector(connector_type=self.connector_type) + with pytest.raises( + TerrakitValidationError, match="CLIMATE DATA STORE REQUEST FAILED" + ): + dc.connector.get_data( + data_collection_name=collection, + date_start=start_date, + date_end=start_date, + bbox=tiny_bbox, + bands=self.bands, + save_file=save_file, + ) diff --git a/tests/component_tests/download/test_geodata_utils.py b/tests/component_tests/download/test_geodata_utils.py new file mode 100644 index 0000000..aca51b5 --- /dev/null +++ b/tests/component_tests/download/test_geodata_utils.py @@ -0,0 +1,180 @@ +# © Copyright IBM Corporation 2026 +# SPDX-License-Identifier: Apache-2.0 + + +import numpy as np +import pytest +import rasterio +import tempfile +import xarray as xr +from pathlib import Path + +from terrakit.download.geodata_utils import save_cog + + +class TestSaveCog: + """Test suite for save_cog function to ensure band names are preserved correctly.""" + + @pytest.fixture + def temp_dir(self): + """Create a temporary directory for test files.""" + with tempfile.TemporaryDirectory() as tmpdir: + yield Path(tmpdir) + + @pytest.fixture + def multi_band_dataarray(self): + """Create a multi-band xarray DataArray with named bands.""" + # Create a simple multi-band DataArray + data = np.random.rand(3, 10, 10) # 3 bands, 10x10 pixels + coords = { + "band": ["temperature", "precipitation", "wind_speed"], + "y": np.linspace(40, 41, 10), + "x": np.linspace(-91, -90, 10), + } + da = xr.DataArray( + data, + coords=coords, + dims=["band", "y", "x"], + attrs={"crs": "EPSG:4326"}, + ) + # Set spatial dimensions and CRS + da = da.rio.write_crs("EPSG:4326") + da = da.rio.set_spatial_dims(x_dim="x", y_dim="y") + return da + + @pytest.fixture + def single_band_dataarray(self): + """Create a single-band xarray DataArray.""" + data = np.random.rand(1, 10, 10) # 1 band, 10x10 pixels + coords = { + "band": ["temperature"], + "y": np.linspace(40, 41, 10), + "x": np.linspace(-91, -90, 10), + } + da = xr.DataArray( + data, + coords=coords, + dims=["band", "y", "x"], + attrs={"crs": "EPSG:4326"}, + ) + da = da.rio.write_crs("EPSG:4326") + da = da.rio.set_spatial_dims(x_dim="x", y_dim="y") + return da + + def test_save_cog_preserves_multi_band_names(self, temp_dir, multi_band_dataarray): + """Test that save_cog correctly preserves band names for multi-band GeoTIFF.""" + output_file = temp_dir / "test_multi_band.tif" + + # Save the DataArray using save_cog (it expects a Dataset) + save_cog( + ds=multi_band_dataarray, + filename=str(output_file), + ) + + # Verify file was created + assert output_file.exists(), "Output file was not created" + + # Open the saved file and check band descriptions + with rasterio.open(output_file) as src: + assert src.count == 3, f"Expected 3 bands, got {src.count}" + + # Check each band description + band_descriptions = [src.descriptions[i] for i in range(src.count)] + expected_names = ["temperature", "precipitation", "wind_speed"] + + assert band_descriptions == expected_names, ( + f"Band descriptions do not match. " + f"Expected: {expected_names}, Got: {band_descriptions}" + ) + + def test_save_cog_preserves_single_band_name(self, temp_dir, single_band_dataarray): + """Test that save_cog correctly preserves band name for single-band GeoTIFF.""" + output_file = temp_dir / "test_single_band.tif" + + # Save the DataArray using save_cog + save_cog( + ds=single_band_dataarray, + filename=str(output_file), + ) + + # Verify file was created + assert output_file.exists(), "Output file was not created" + + # Open the saved file and check band description + with rasterio.open(output_file) as src: + assert src.count == 1, f"Expected 1 band, got {src.count}" + + band_description = src.descriptions[0] + expected_name = "temperature" + + assert band_description == expected_name, ( + f"Band description does not match. " + f"Expected: {expected_name}, Got: {band_description}" + ) + + def test_save_cog_with_special_characters_in_names(self, temp_dir): + """Test that save_cog handles band names with special characters (e.g., CDS variables).""" + output_file = temp_dir / "test_special_chars.tif" + + # Create DataArray with special character names like CDS returns + data = np.random.rand(3, 10, 10) + coords = { + "band": ["2m_temperature", "mean_total_precipitation_rate", "10m_wind"], + "y": np.linspace(40, 41, 10), + "x": np.linspace(-91, -90, 10), + } + da = xr.DataArray(data, coords=coords, dims=["band", "y", "x"]) + da = da.rio.write_crs("EPSG:4326") + da = da.rio.set_spatial_dims(x_dim="x", y_dim="y") + + save_cog(ds=da, filename=str(output_file)) + + assert output_file.exists(), "Output file was not created" + + with rasterio.open(output_file) as src: + band_descriptions = [src.descriptions[i] for i in range(src.count)] + expected_names = [ + "2m_temperature", + "mean_total_precipitation_rate", + "10m_wind", + ] + assert band_descriptions == expected_names, ( + f"Band descriptions do not match. " + f"Expected: {expected_names}, Got: {band_descriptions}" + ) + + def test_save_cog_readable_in_qgis(self, temp_dir, multi_band_dataarray): + """ + Test that saved GeoTIFF has proper metadata that would be readable in QGIS. + This validates the fix for the original issue where QGIS showed all bands + with the same name. + """ + output_file = temp_dir / "test_qgis_readable.tif" + + save_cog( + ds=multi_band_dataarray, + filename=str(output_file), + ) + + # Verify all metadata that QGIS would read + with rasterio.open(output_file) as src: + # Check band count + assert src.count == 3, "Incorrect band count" + + # Check CRS + assert src.crs is not None, "CRS not set" + + # Check that each band has a unique description + descriptions = [src.descriptions[i] for i in range(src.count)] + assert len(set(descriptions)) == len(descriptions), ( + "Band descriptions are not unique - QGIS would show duplicate names" + ) + + # Verify descriptions match expected names from the fixture + expected_names = ["temperature", "precipitation", "wind_speed"] + assert descriptions == expected_names, ( + f"Band descriptions don't match. Expected: {expected_names}, Got: {descriptions}" + ) + + +# Made with Bob From 7e7b75b582e69686f0827abd23e3c519871d8a72 Mon Sep 17 00:00:00 2001 From: Rosie Lickorish Date: Tue, 24 Mar 2026 13:55:32 +0000 Subject: [PATCH 18/18] Improve ERA5 data processing --- .gitignore | 2 +- .secrets.baseline | 30 +- docs/data_connectors.md | 39 +- docs/examples/terrakit_download_cds.ipynb | 415 +++++++++++----- terrakit/download/collections.json | 2 +- .../data_connectors/climate_data_store.py | 469 +++++++++++++----- terrakit/general_utils/plotting.py | 214 +++++++- .../test_climate_data_store.py | 316 ++++++++++-- tests/integration_tests/cds.py | 116 +++++ 9 files changed, 1321 insertions(+), 282 deletions(-) create mode 100644 tests/integration_tests/cds.py diff --git a/.gitignore b/.gitignore index e3528fc..96e0711 100644 --- a/.gitignore +++ b/.gitignore @@ -115,7 +115,7 @@ dmypy.json *.tortilla *metadata.json *.nc - +*.zip # Test data test_wildfire_vector diff --git a/.secrets.baseline b/.secrets.baseline index 45c7ef0..2a53564 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "^.secrets.baseline$", "lines": null }, - "generated_at": "2026-03-22T10:19:45Z", + "generated_at": "2026-03-24T13:55:12Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -118,35 +118,35 @@ { "hashed_secret": "5204df45fc8c724684bbc61cd4107a726a6b9204", "is_verified": false, - "line_number": 28, + "line_number": 65, "type": "Secret Keyword", "verified_result": null }, { "hashed_secret": "365a32402f2062038d01718da7de099f94555775", "is_verified": false, - "line_number": 30, + "line_number": 67, "type": "Secret Keyword", "verified_result": null }, { "hashed_secret": "d401c389d24671398ced85b259e17975a4fcb65e", "is_verified": false, - "line_number": 53, + "line_number": 90, "type": "Secret Keyword", "verified_result": null }, { "hashed_secret": "4e1e76c221c6dace8f9a2c943e00d3328366366a", "is_verified": false, - "line_number": 62, + "line_number": 99, "type": "Secret Keyword", "verified_result": null }, { "hashed_secret": "54a38e82cf07a4f36900b855ca7dd0584ca9f3ff", "is_verified": false, - "line_number": 64, + "line_number": 101, "type": "Secret Keyword", "verified_result": null } @@ -255,11 +255,25 @@ ], "docs/examples/terrakit_download_cds.ipynb": [ { - "hashed_secret": "94d5b98c35d4453f9e867715e61e004c60a11b12", + "hashed_secret": "45e9f10b3b6aa19c8124c22a9ff2723498779f62", "is_verified": false, - "line_number": 219, + "line_number": 220, "type": "Hex High Entropy String", "verified_result": null + }, + { + "hashed_secret": "f7034486e49d0ed60b3bded84f7b05288be9283d", + "is_verified": false, + "line_number": 390, + "type": "Base64 High Entropy String", + "verified_result": null + }, + { + "hashed_secret": "b084221739b62e38c35cba68dfd407485d9b8d0c", + "is_verified": false, + "line_number": 425, + "type": "Base64 High Entropy String", + "verified_result": null } ], "terrakit/general_utils/labels_downloader.py": [ diff --git a/docs/data_connectors.md b/docs/data_connectors.md index 4c8056a..e9db041 100644 --- a/docs/data_connectors.md +++ b/docs/data_connectors.md @@ -1,5 +1,5 @@ # TerraKit Data Connectors -Data connectors are classes which enable a user to search for data and query data from a particular data source using a common set of functions. The TerraKit Pipeline makes use of the Data Connectors, but they can also be used independently to explore and retrieve EO data. +Data connectors are classes which enable a user to search for data and query data from a particular data source using a common set of functions. The TerraKit Pipeline makes use of the Data Connectors, but they can also be used independently to explore and retrieve EO data. Each data connector has the following mandatory methods: @@ -7,6 +7,43 @@ Each data connector has the following mandatory methods: * find_data() * get_data() +## Bounding Box Constraints + +**All TerraKit data connectors adhere to standard geographic bounding box constraints:** + +Bounding boxes must be specified in the format: `bbox = [West, South, East, North] = [min_lon, min_lat, max_lon, max_lat]` + +The following constraints are enforced: + +* **Longitude (West/East)**: `-180 <= west < east <= 180` +* **Latitude (South/North)**: `-90 <= south < north <= 90` + +These constraints ensure: + +- Valid geographic coordinates within Earth's coordinate system +- Proper ordering (minimum < maximum for both longitude and latitude) +- Consistency across all data connectors regardless of the underlying data source + +**Example of a valid bounding box:** +```python +# Valid: London area +bbox = [-0.5, 51.3, 0.3, 51.7] # [West, South, East, North] + +# Valid: Global extent +bbox = [-180, -90, 180, 90] + +# Invalid: West >= East +bbox = [0.3, 51.3, -0.5, 51.7] # ❌ West (0.3) must be < East (-0.5) + +# Invalid: Longitude out of range +bbox = [-200, 51.3, 0.3, 51.7] # ❌ West (-200) outside valid range [-180, 180] + +# Invalid: South >= North +bbox = [-0.5, 51.7, 0.3, 51.3] # ❌ South (51.7) must be < North (51.3) +``` + +**Note:** For regions crossing the antimeridian (180°/-180° longitude), split the query into two separate bounding boxes or use data connector-specific handling if available. + ## Available collections The following data connectors and associated collections are available: diff --git a/docs/examples/terrakit_download_cds.ipynb b/docs/examples/terrakit_download_cds.ipynb index 6ab7060..499fb62 100644 --- a/docs/examples/terrakit_download_cds.ipynb +++ b/docs/examples/terrakit_download_cds.ipynb @@ -2,61 +2,66 @@ "cells": [ { "cell_type": "markdown", - "id": "baf11c7a", "metadata": {}, "source": [ "\n", "\n", + "\n", + "\n", "# TerraKit: Climate Data Store search and query\n", "\n", + "\n", + "\n", "Use the TerraKit Climate Data Store Data Connector to search and query data from [CDS](https://cds.climate.copernicus.eu/datasets/). Available collections include:\n", "\n", + "\n", + "\n", " - [ERA5 post-processed daily statistics on single levels from 1940 to present](https://cds.climate.copernicus.eu/datasets/derived-era5-single-levels-daily-statistics?tab=overview)\n", + "\n", " - [CORDEX regional climate model data on single levels](https://cds.climate.copernicus.eu/datasets/projections-cordex-domains-single-levels?tab=overview)\n", "\n", + "\n", + "\n", "
\n", + "\n", "Install TerraKit For instructions on how to install TerraKit, take a look at the Welcome page.\n", - "
\n" + "\n", + "" ] }, { "cell_type": "code", - "execution_count": 1, - "id": "b56cfe38", + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ - "import numpy as np\n", - "\n", + "import matplotlib.pyplot as plt\n", "\n", "from terrakit import DataConnector\n", - "from terrakit.download.transformations.impute_nans_xarray import impute_nans_xarray\n", - "from terrakit.download.transformations.scale_data_xarray import scale_data_xarray\n", - "from terrakit.download.geodata_utils import save_data_array_to_file" + "\n", + "from terrakit.general_utils.plotting import (\n", + " plot_era5_variable,\n", + " plot_era5_variables_comparison,\n", + ")" ] }, { "cell_type": "markdown", - "id": "675af303", "metadata": {}, "source": [ - "\n", - "\n", "### Create an account\n", - "Create an account at [https://cds.climate.copernicus.eu/](https://cds.climate.copernicus.eu/). Once created, find your API key under the `Profile` section. Each dataset may also require accepting the licence agreement. If this is the case, the first time a request is made, an error will be returned with the url to visit to accept the terms.\n" + "\n", + "Create an account at [https://cds.climate.copernicus.eu/](https://cds.climate.copernicus.eu/). Once created, find your API key under the `Profile` section. Each dataset may also require accepting the licence agreement. If this is the case, the first time a request is made, an error will be returned with the url to visit to accept the terms." ] }, { "cell_type": "markdown", - "id": "90626647", "metadata": {}, "source": [ "### Connect to CDS" @@ -64,18 +69,17 @@ }, { "cell_type": "code", - "execution_count": 2, - "id": "72f17643", + "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "2026-03-16 19:12:13,321 - INFO - Initializing DataConnector with connector type: climate_data_store\n", - "2026-03-16 19:12:13,321 - INFO - climate_data_store\n", - "2026-03-16 19:12:13,323 - INFO - climate_data_store\n", - "2026-03-16 19:12:13,325 - INFO - Listing available collections\n" + "2026-03-24 10:17:08,479 - INFO - Initializing DataConnector with connector type: climate_data_store\n", + "2026-03-24 10:17:08,480 - INFO - climate_data_store\n", + "2026-03-24 10:17:08,482 - INFO - climate_data_store\n", + "2026-03-24 10:17:08,483 - INFO - Listing available collections\n" ] }, { @@ -85,7 +89,7 @@ " 'derived-era5-single-levels-daily-statistics']" ] }, - "execution_count": 2, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } @@ -98,130 +102,127 @@ }, { "cell_type": "markdown", - "id": "b23ea8ab", "metadata": {}, "source": [ - "\n", - "\n", "TerraKit defines a bounding box as a list using the format `[min_lon, min_lat, max_lon, max_lat]`, which is equivalent to `[West, South, East, North]`. Let's do that!" ] }, { "cell_type": "code", - "execution_count": 3, - "id": "1066cd9a", + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ - "bbox = [\n", - " 34.5,\n", - " -0.5,\n", - " 35.0,\n", - " 0.0,\n", - "] # [West, South, East, North] / [min_lon, min_lat, max_lon, max_lat]" - ] - }, - { - "cell_type": "markdown", - "id": "79bfea17", - "metadata": {}, - "source": [ - "### Derived ERA5 Single Levels Daily Statistics\n" + "# Small area over Western Europe [min_lon, min_lat, max_lon, max_lat]\n", + "bbox = [-10, 40, 5, 50] # [West, South, East, North]" ] }, { "cell_type": "markdown", - "id": "65c6c796", "metadata": {}, "source": [ + "### Derived ERA5 Single Levels Daily Statistics\n", + "\n", "Now we can search for data from the Derived ERA5 Single Levels Daily Statistics collection.\n", "\n", + "\n", + "\n", "The `find_data()` function will search for data and return both a list of unique dates where data is available, but also the raw results from the search." ] }, { "cell_type": "code", - "execution_count": 4, - "id": "05876bd3", + "execution_count": 5, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1940-01-01 00:00:00 2026-02-04 00:00:00\n", - "['2025-01-01', '2025-01-02']\n", - "[{'collection': 'derived-era5-single-levels-daily-statistics', 'date_range': '2025-01-01 to 2025-01-02', 'total_dates': 2, 'temporal_extent': {'interval': [['1940-01-01T00:00:00+00:00', '2026-02-04T00:00:00+00:00']]}, 'spatial_extent': {'bbox': [[0.0, -89.0, 360.0, 89.0]]}}]\n" - ] - } - ], + "outputs": [], "source": [ "collection_name = \"derived-era5-single-levels-daily-statistics\"\n", + "date_start = \"2024-01-01\"\n", + "date_end = \"2024-01-03\" # 3 days for quick testing\n", "\n", - "bands = [\"2m_temperature\", \"mean_total_precipitation_rate\"]\n", + "# Variables covering all 4 parameter classes\n", + "bands = [\n", + " # Instantaneous\n", + " \"2m_temperature\",\n", + " \"10m_u_component_of_wind\",\n", + " \"mean_sea_level_pressure\",\n", + " # Accumulated\n", + " \"total_precipitation\",\n", + " \"surface_net_solar_radiation\",\n", + " \"evaporation\",\n", + " # Mean rate\n", + " \"mean_total_precipitation_rate\",\n", + " # Min/Max\n", + " \"maximum_2m_temperature_since_previous_post_processing\",\n", + " \"minimum_2m_temperature_since_previous_post_processing\",\n", + "]\n", "\n", - "unique_dates, results = dc.connector.find_data(\n", - " data_collection_name=collection_name,\n", - " date_start=\"2025-01-01\",\n", - " date_end=\"2025-01-02\",\n", - " bands=bands,\n", - " bbox=bbox,\n", - ")\n", - "\n", - "print(unique_dates)\n", - "print(results)" + "# Additional query parameters\n", + "query_params = {\n", + " \"daily_statistic\": \"daily_mean\",\n", + " \"time_zone\": \"utc+00:00\",\n", + " \"frequency\": \"1_hourly\",\n", + "}" ] }, { "cell_type": "markdown", - "id": "256c5594", "metadata": {}, "source": [ - "Now to query the data, we specify the bands we want to return, plus an (optional) save filename. The `get_data()` function will query the data from the data source and return an xarray object containing all the fetched data with dimensions (time, band, y, x). All dates are stacked along the time dimension, and all bands are stacked along the band dimension.\n", + "Now to query the data, we specify the bands we want to return, plus an (optional) save filename. The `get_data()` function will query the data from the data source and return an **xarray.Dataset** containing all the fetched data. Each variable is a separate data variable with dimensions (time, latitude, longitude), and each variable includes a `stepType` attribute indicating its parameter class.\n", "\n", - "Optionally, if `save_file=` is provided as an argument to `get_data()`, individual GeoTIFF files will be saved for each date with the naming pattern: {save_file}_{date}.tif (e.g., 'output_2025-01-01.tif', 'output_2025-01-02.tif'). Each file contains all requested bands for that specific date." + "**Important**: The method now returns `xarray.Dataset` (not `DataArray`) to preserve parameter class (stepType) information for each variable.\n", + "\n", + "Optionally, if `save_file=` is provided as an argument to `get_data()`, individual NetCDF files will be saved for each date with the naming pattern: {save_file}_{date}.nc (e.g., 'output_2024-01-01.nc', 'output_2024-01-02.nc'). Each file contains all requested bands for that specific date." ] }, { "cell_type": "code", "execution_count": 6, - "id": "ee89f199", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "2026-03-16 19:12:44,636 - INFO - Submitting CDS request for derived-era5-single-levels-daily-statistics\n", - "2026-03-16 19:12:44,637 - INFO - Date range: 2025-01-01 to 2025-01-02 (2 days)\n", - "2026-03-16 19:12:44,637 - INFO - Area: 3080.22 km²\n", - "2026-03-16 19:12:44,637 - INFO - Variables: 2\n", - "2026-03-16 19:12:44,637 - INFO - Estimated size: ~0.12 MB\n", - "2026-03-16 19:12:44,638 - INFO - Estimated time: ~2.0 minutes\n", - "2026-03-16 19:12:45,100 - INFO - Request submitted to CDS queue. Please wait...\n", - "2026-03-16 19:12:45,793 INFO Request ID is bbf3a88d-e41e-41ac-8213-b2ce3324b64f\n", - "2026-03-16 19:12:45,793 - INFO - Request ID is bbf3a88d-e41e-41ac-8213-b2ce3324b64f\n", - "2026-03-16 19:12:45,856 INFO status has been updated to accepted\n", - "2026-03-16 19:12:45,856 - INFO - status has been updated to accepted\n", - "2026-03-16 19:13:00,005 INFO status has been updated to successful\n", - "2026-03-16 19:13:00,005 - INFO - status has been updated to successful\n", - "2026-03-16 19:13:00,241 - INFO - Downloading https://object-store.os-api.cci2.ecmwf.int:443/cci2-prod-cache-1/2026-03-16/f8c113b5e91ec99cae15946945e28136.zip\n" + "2026-03-24 10:17:21,650 - INFO - Submitting CDS request for derived-era5-single-levels-daily-statistics\n", + "2026-03-24 10:17:21,650 - INFO - Date range: 2024-01-01 to 2024-01-03 (3 days)\n", + "2026-03-24 10:17:21,650 - INFO - Area: 1306839.4 km²\n", + "2026-03-24 10:17:21,651 - INFO - Variables: 9\n", + "2026-03-24 10:17:21,651 - INFO - Estimated size: ~352.85 MB\n", + "2026-03-24 10:17:21,652 - INFO - Estimated time: ~72.6 minutes\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1940-01-01 00:00:00 2026-02-04 00:00:00\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2026-03-24 10:17:22,313 - INFO - Request submitted to CDS queue. Please wait...\n", + "2026-03-24 10:17:22,659 INFO Request ID is 60d9bd0d-3801-4961-91a8-1b873115acb3\n", + "2026-03-24 10:17:22,659 - INFO - Request ID is 60d9bd0d-3801-4961-91a8-1b873115acb3\n", + "2026-03-24 10:17:22,727 INFO status has been updated to accepted\n", + "2026-03-24 10:17:22,727 - INFO - status has been updated to accepted\n", + "2026-03-24 10:17:44,099 INFO status has been updated to successful\n", + "2026-03-24 10:17:44,099 - INFO - status has been updated to successful\n", + "2026-03-24 10:17:44,710 - INFO - Downloading https://object-store.os-api.cci2.ecmwf.int:443/cci2-prod-cache-3/2026-03-23/b19ed09eb8879c91a00e93fbe6363127.zip\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "6506c2aacf1d422c86bc4a0d61fa44ff", + "model_id": "75d5eef7ec6b4cd087e41e4f59ec5417", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "f8c113b5e91ec99cae15946945e28136.zip: 0%| | 0.00/49.6k [00:00 Size: 271kB\n", + "Dimensions: (latitude: 41, longitude: 61, time: 3)\n", + "Coordinates:\n", + " * latitude (latitude) float64 328B 50.0 49.75 ... 40.0\n", + " * longitude (longitude) float64 488B -10.0 -9.75 ... 5.0\n", + " * time (time) datetime64[ns] 24B 2024-01-01 ... 2...\n", + " number int64 8B ...\n", + " valid_time datetime64[ns] 8B 2024-01-01\n", + " spatial_ref int64 8B 0\n", + "Data variables:\n", + " 10m_u_component_of_wind (time, latitude, longitude) float32 30kB 9...\n", + " evaporation (time, latitude, longitude) float32 30kB -...\n", + " mean_sea_level_pressure (time, latitude, longitude) float32 30kB 9...\n", + " mean_total_precipitation_rate (time, latitude, longitude) float32 30kB 0...\n", + " mn2t (time, latitude, longitude) float32 30kB 2...\n", + " mx2t (time, latitude, longitude) float32 30kB 2...\n", + " surface_net_solar_radiation (time, latitude, longitude) float32 30kB 2...\n", + " t2m (time, latitude, longitude) float32 30kB 2...\n", + " total_precipitation (time, latitude, longitude) float32 30kB 0...\n", + "Attributes:\n", + " source: Climate Data Store (CDS)\n", + " dataset: derived-era5-single-levels-daily-statistics\n", + "\n", + "Variable stepTypes:\n", + " 10m_u_component_of_wind: instant\n", + " evaporation: accum\n", + " mean_sea_level_pressure: instant\n", + " mean_total_precipitation_rate: avg\n", + " mn2t: max\n", + " mx2t: max\n", + " surface_net_solar_radiation: accum\n", + " t2m: instant\n", + " total_precipitation: accum\n" + ] + } + ], + "source": [ + "# Display dataset structure\n", + "print(\"Dataset structure:\")\n", + "print(ds)\n", + "\n", + "# Show stepType for each variable\n", + "print(\"\\nVariable stepTypes:\")\n", + "for var_name in ds.data_vars:\n", + " step_type = ds[var_name].attrs.get(\"GRIB_stepType\", \"unknown\")\n", + " print(f\" {var_name}: {step_type}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Visualizing the Data\n", + "\n", + "Now let's visualize the data we downloaded. We'll create plots for each variable and time step.\n", + "\n", + "The plotting functions below work with the new Dataset format and display the stepType information for each variable." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Plot a Single Variable\n", + "\n", + "Let's visualize the first variable for the first time step:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Plotting: 10m_u_component_of_wind\n" + ] + }, + { + "data": { + "image/png": "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VemPmTNnmvv0ta+v3xtvvNF5/PHHKywTQHSMN7djvPkXxpuMN6P9nWa8WXY8wnizcow3gdTn0/95ETgHUp1m0F544YVy//33e7I8zQYePny4yWyNls3n9fpgT7NV//73v1dLFi2QarQe/ODBg01GdVWzqQEA3mK8WXsw3kRtwngTQLqhhjewg7RMg5aH0EvnAQAAAK8x3gQAAKg6angDVfTSSy+Zuqnvv/++qU9b3bO6A4hN6zxrfexYtOayDa1frxOCVkZrS2sNfyRWfn6+ucWix6Uqkw9H0rkfyk/iWp7OHaE3AEgExptAamG8WfMx3gRqFgLeQBXpRG8a9NBJ6HRSNADJpRP5Pf300zHb2Fbv0glLdWLYyuhEgTaTesJb//rXv1wnF160aJG5vHxHLFmyxHUi3PHjx1MmCUDCMN4EUgvjzZqP8SZQs1DDGwCQ1mbPni3Lly+P2ebggw+2WtaPP/4oGzZsqPTxOnXqyD777FPlbUR8Fi5caG6xaHmpnJycHVp+QUGBfP311zHbaAkrvQEAgNqH8WbNx3gTqFkIeAMAAAAAAAAAagQmrQQAAAAAAAAA1AgEvAHUOF988YWZTFR/JsKBBx5obulo1apVcvzxx0uzZs1Mn917771R++/vf//7DtdHTqR02U4AAFC7xDNe1HHZ9ddf7/k2AQBQUxHwBlBlTz31lBl4T5s2LW1674UXXjDB3HSgQVvt3/CtYcOG0qdPH7nrrruksLDQ03WNGjVKPvroIxk7dqw8++yzcthhh0kq+fbbb80HvI0bN1b7um655RZ56623qm3555xzjjmeRx55ZIXHNEgfeczDt/PPP99q2aFQSO644w4z8aLWse7du7e8+OKLUdvOmTPHHGedfLdp06Zy+umny5o1a+LePwAAUsUvv/xivtDXyab172Lbtm3lkEMOkf/85z/VXudZxy3JmOBa1xkeP9x0001R25x66qnmcR0DpLIHH3zQfN6wsW7dOrnzzjtl//33lxYtWkjjxo1l7733lpdffjlqex1LX3XVVdKmTRszN8uAAQPkk08+KdNm69at8sADD8jQoUOldevW0qBBA+nXr5889NBDEgwGY27P888/v0N9rGNenY+kbt26kpubKxdffLHk5+eXaaO/6yTaOo7TMZyux7afkrUuAEiWjKStGQCqiQ54t23bJllZWWUC3rNmzZJLL700Lfo9Oztb/vvf/5p/a7D39ddfl8svv1x++OEHeemllzxbz+effy5HH320WXbYzjvvXKH/kkUH5BMmTDBfAugHmOoOeOuH4+HDh3u+bP1ySD8kxJpUsW/fvnLZZZeVuU+PhY1rrrlGbrvtNhNU33PPPeXtt9+WU045xXw4Oemkk0rbLV261Lw+GjVqZPZXP8zojPQaGPj+++9T4pgDABDv2GHw4MGy0047mb+LGtBbsmSJfPfdd3LffffJRRddVK0Bbx23aCZ3+SvOPv74Y0kEHWvol97XXnttmfu3bNlixgc7OsFzogPezZs3N+M/N1OmTDHjoL/97W9mnzMyMsy4Wcc/4eMRSZf52muvmc8E3bp1M+Mzfe6kSZNMEDg8eaGeJ0OGDJHRo0eb5BNNEPm///s/cx49/fTTUbdFx1VXXnml1KtXr0r7O2PGDLOuXXfdVe6++24zXtPx2bx58+TDDz8sbbd27Vq54YYbzLmtyTA7cjVrItcFAMlEwBtAjeP3+9NiMB+LDtZPO+200t91gK0ZKJqtooNTzUopz3EcKSgoMNkqtlavXl0hkFwT+i+V6HHRzJkzzjhDPvvss0rbafZZ5DG3tWzZMpP9f+GFF8r9999v7vvHP/4hBxxwgFxxxRUyYsQICQQC5n4NcusH3h9//NF8gFF77bWXyXrTD3znnnvuDu8nAACp4OabbzZf7GqSQPkxjo57kiVRXypr8PaNN96Qn3/+2QQqwzTYXVRUZDJ2NeGhpthtt91MsFaz+SPHzQcffLDcfvvtZQLQ+uW+Jo5oRng42UPHZz179jTt9MsSpV+SaDKALjvsvPPOk7POOkuefPJJGTdunHTt2rXCtmhmvWaD6xcuVblq8Oqrr5YmTZqYoLIG15V+YaJf2OgXJZpprjTbfMWKFWb7NJlCkxyqKpHrAoBkoqQJgGrz008/yeGHH24GU3pZn2YTaFZEtPIo33zzjcmg0EsRdVB6zDHHVCizoGUb9DJRDfbqJXg6mNTMDR2kRWaAlK9BrVk277//vvz++++ll3qGs27C6y9/6WlldcAfffRR6dKliwkqa6Dwq6++qvRySb0MUAfDmq3dvn17M5De0ZIkGoQO130Mb6vug5bH0IyTPfbYw2zTI488UpqZooFOvQRR+0ov7dQ+KN/vGozVSzbD/RJr38vT46FlYvTDgAbIW7VqZT4MbNiwwXV/Zs6caY5Z586dzXN1MK0fIvSy1DA91hqwVVqqI7yNVb1MWLNWBg0aZOqUax/tvvvuJrMnki5XA8GasRNej01WkQ0tFaNXF+gHcDf6QVS3oyr0A2xxcbH5cBem23/BBReYrB3NfArTjCc9Z8LBbqUfCDWT/JVXXqnSegEASEULFiwwY5NoV4a1bNmyzO/69/Kf//ynKUOxyy67mDGJjhMmT55cpp2OIfXvrLbRsYSOKXScFTkm0bGV3qd0jBoeT0SORyNreOvf/Ouuu86sTwP0Ov7db7/9TKZxPAYOHGjGTXp1YyTdx3B5imhjiSOOOMKMsXXcqmPdG2+8sUL5Dt1+DQ7rF+c6ttK+0HU9/PDDVtumweKDDjrIHAddT48ePUyZkEg6vv3f//4nX375ZWkfxqp9ruuPDHYrfY5esafjbh0Th+n4T5MAIr/g12N+9tlnm/GSXgmgNLs8Mtgdpp9PwuXhytOg+z333GMSUzRxxVZeXp4pqaJJD+EAdDgQr5+fIsdn2mc6Zt5RiVwXACQbGd4AqoUOVHXQroMpDfRmZmaaYKwOWHUAq9nKkfSyQc020CCxfnjQQKp+AImsv6d1prVO8bBhw+TQQw81mSv6U7OaY9HLHDdt2mSCfzoQVTtSu/Dxxx83AV0d4OtlkDqAPuqoo8wHBw1oRwaC9f6vv/7aDKj1kkHNEtF1//bbbztcJ1o/wCn9kBX266+/ysknn2y2SzMz9IOYTkSp26j1BzWzWNtrIFe3SQf6OljXshYaiNX6zZrdqwPdqtJ16oe7M88806xn0aJFJsNYv+jQLzD0mFdGB9vaf/pcHUzr+aJfJuhP/VJEP6gce+yxpr/0slztO/3wofRLkarQy5d137VupX641Mwe/UD63nvvmQ93SvtCs6L1S4zwhyD9sKc0mKznjw09F/TLibDNmzebOpGaTeP2oUGzrfTLCf1wqR/ctL76JZdc4rpO7W/9kKznWSTdl/DjeomuZoJrZpt+OVKetv3ggw+s9hEAgFSmf0M1eKlfNmtw1o2OS3W8qWMZDfJpOQ0NDGs2cPj5mi2u2b9aJqNdu3ZmrKqBWh3XavKF/v3WsZUu49///rf5ux/+u1z+73Nk8FHL1+k4TsdwOmbQsaaObXXdWupsR+kyn3vuOVPuTMdUWp5Cs3d1vDNx4sQK7XU8p2NjTT7Rnzom0WC8bqNmQ0fSxAbNIj/hhBPMejRIql+yawa7Ji/Eon2mgWQdl2lQ+N133zVfJOjYWa9UU/oZQD8X6HboGF5pUkVVrVy50vwMjx/DYyL9kj8y2Bs5ZtJyH5FjeptlhulnA/2iQ/umKkkE+hmhpKSkwvhM+1PPAd1mryRyXQCQdA4AVNGTTz7p6NvHDz/8UGmb4cOHO1lZWc6CBQtK71u+fLnToEEDZ//996+wrIMPPtgJhUKl948aNcoJBALOxo0bze8rV650MjIyzHIjXX/99eb5I0eOLL1v0qRJ5j79GXbEEUc4HTp0qHRfFi1aVOb+8ssoKipyWrZs6fTt29cpLCwsbffoo4+adgcccEDpfc8++6zj9/udr776qswyH374YdP2m2++cWLRfalXr56zZs0ac5s/f75zyy23OD6fz+ndu3dpO90fXd7EiRPLPP/SSy8190euf/PmzU6nTp2cjh07OsFgsPR+bXfhhRfG3PfwNkX2ny5b2zz//PNlnqvbEu3+8rZu3VrhvhdffNE8d/LkyaX33XnnnVGPT2XKb2e0demx7Nmzp3PQQQeVuV/7PPI8Kt8fNrfy23n55Zebfi8oKDC/67bpuVjesGHDnNtvv9156623nMcff9zZb7/9zPKuvPJK133W5XXu3LnC/Vu2bDHLGDNmjPldX6/6+zPPPFOh7RVXXGEeC28nAADp6uOPPzZjSL0NHDjQ/C396KOPzN//8sJ/v6dNm1Z63++//+7k5OQ4xxxzTMxxy5QpUyr8XX311VcrjKHCdKwYOV4sKSkpM6ZUGzZscFq1auWcddZZFbZz/PjxMfdbxyDaTsdOs2bNKjMWfOCBB5z69eubsUF4nBkp2v6dd955Tt26dcuMDXT7dbl33XVX6X26Dzo+1nFytD52W8+hhx5aYRyz2267lemrqlq3bp3ZHh1PlV9u+fGf+t///mf2S8fqldH97NGjhxnXFRcXl3nsvffeM59TdDkqWh9XJnzORI5/w0aMGOHk5uZGfV54XKefZWwlcl0AkGyUNAHgOc1Q1SwSvZRQS1aEaS04nUhPM581YySSZtWGS2oozQ7X5eglpEprH2tGQmTZBlWdEw9F0tp1mh17/vnnl6nBqGUv9DLUSK+++qrJ5unevbvJqAnf9BJOZXOpqpa10ExmvWlZFM0U0ktU33zzzQqXcWomUCTN1NVMlfDEO0qzZLSPNSNJM5Hipfuo+63Z4ZH7qJfl6rrc9jGyzrhm6OtzteyKmj59etzbV9m6NCtJs7X1/LJdj9a/1Ix0m1tkFrdmp2t2uWZGacZYLO+88465EkInENXsKM020+MankwoFp1gNNryw3XY9fHInzZtAQBIVzo20QxvzSLWqwH16kD9m6pzZejf2/J0fKXjlzAt+6V/j7VkXLikR+RYQq/80hJsOj7Tsik7Om7R0hrhMaVmOK9fv740+zbesZBmUffu3dtcJae0vInuk2aiRxO5f5ppruMyHSvp1YJz584t01Yzs/UqvzDdB/1dx8la6iSWyPXoeEzXo3OO6FV/tlfTudG+1Kv6dNL3//znPzs0ZopGrzzVMbRezRhZskSvHtSr8vQzgpZoqSq38ZmXY7NErgsAko2SJgA8p7W3dYCs5TXK00CwDkS1Rl5kbbzImsJKy5uocD3ocOC7/AQxWkIi3LY6hdevs7lH0rIdkUH9cA0/re1XWekNmwmTdNCpl3mGB6Ua2NZLaMvT+6Nta/mSMZGX1OrjNpf4xqL7qB9MytfCtN1H/VA3YcIEU16kfFuvPvCEaekSnURIL1ONrKEe+QVLLHp+aZ3rqtJyJFpa5rjjjqvyc3Xb9MOTftjW2p+xJrPUD4/RasOHS/2EP1yGf9q0BQAgnekEezpxowYjNeitCQNaHu34448344HIwGT5sZ3Sshc6ltUxrX6ZrYHAW2+91dSg1hJh25Ou4x+3aMk5nXhag8oaSI81vqsqTTLRZet4QsuxaPJEZbSk3LXXXmtKmZRPSim/f1rnOzwJZGR/KU2sCCcwRKMl77R8oX4hof1bfj3lk0h2hCbDaNmWZ555psyknVUZM5WnyQuPPfaYqWuuJUsi6XmlgXsd18ai+xcZUNYvCvRzjNv4bEfGZolcFwCkKgLeAFKCZrlEE/mBojpUFvQsP0lPVWhAv1evXiY7N5pYtQEj+8MmyJqsganuowa7dQKkaNzqbGvdR/3wpZNSas1AzQrXZWrNTP3pFZ1UVDO8tK6m1uTUqwz0Swr9wFp+MqfK6IdlDdDb0P3WY6cfGPXDln7YjpzQSjO39AOI3qcfPMrXkIx2nritW/dJM+r1tRJ5Pq9YsaL0g2m4XeT9kfQ+3R63THQAANKJBvo0+K03Dcrq3CF6lZoGXasaRNWxg9Zp1oxwDczq31yt6b2j4xatsa1XCuoVkToe0nGVjiE0sB6etyUeWl9b57/R+uA6n8vQoUOjttNMaM2y1jHJDTfcYOYw0cQLzTLXeUi8GpfpPukE9noFpI6RdZyjx0evTNSgsRfr0aCzjve0drnOU1OejoX0C4vyyo+Zytc3137QDG79UqB8YFmTKvQKVP2iIPxlQX5+vhmX6XhPs+r12GoihH7BEaZ9rkkNbuOzaNvkJpHrAoBURcAbgOc06KeDO51QsTzNYNFJ/WyCvpHCs6/Pnz+/TNaLXlIazgLfkcB2ODtcB/vRMrrLr18zm8OlSZRm4+hkjZEZJPpBQbOJdFBvm0XsJd3Wyvo+/Hi8dB8//fRT2WeffaocdNfjpSVq9EOJTogUpn1bXrz99/rrr5sPbZopHRnM1Q+ttuvSwLxOQmRDz4WOHTvKH3/8YX7XiTfL0w9aeg7rhzv94FwZvbzX5ssD/cJAJ73SqwoiM9amTp1a+rjSS7l1WVqep7x4J8cCACDVhSfqKx/sizb+0LJkOpYN/w3WSb9HjhxpMqYjM2LLjx+rMm7RZepVgvrleOTzqhqMr4xePanjNA106qSSkWU4IunjOp7W7dAEgcgxTTTLly83pfcis7y1v5SOgSqjVy5qZrGWlYm8sjNaGbwdGf898MADcv3115uxlQaoo9Gxjq5PA9ORSQflx0xhb7/9tpnUXMdzuvxoY1oNbmvZHL2Vp+M9LSWjE9Zr6brIK/bCn0H0qks9Njo+04SQyIQLvRoh8j5biVwXAKQqangD8Jxmp2gWiQ4SI7NbV61aZbJqtbZ0rMzWaDR4rAM0nd09ktbRs6GD8miXnGrgVk2ePLlMdvejjz5a4UOSfuh5+OGHzaAwMuuj/IcdHSxqUFMvfSxPs3v1Q0J10kstNYCpl4uG6Tp1n/SDyI7UFyxP91H7SS/tLE+zmMv3SbRs/vLZ+/fee2+FtuEPU7GWF4uuSz80RWbs6zmpHzyirSvaenakhrd+KaKXT5e/6Tmk55L+e9iwYaUZ3OWvKNAvUjQ7STOfIoPteg7rFxeR57J+kNKsdc1oCtO+1XNVg9xaViVMy6toiRctKRSmXz7oB9URI0bsUB8DAJBKwlc9laeZxKp8yT0dL0XWzNa/kTqG1bFseMyiP8svU+tDl//7XZVxS7TxkAZeI8dv8dLsYw2gx5rzJtp26Fg3clxRfpz3yCOPlGmrv+sYJ7IWus16dDwTLQmhsjFZZV5++WW5+OKLTe3uyq6wVFrSpvw4X4Pwug1aDjAyIUc/G2gGv34JoFc0asJOeZq5HW28p2M3TbjQf2uWvdLxt169Gb6F+0qvFtDfNeNf66eHPfvssyaYviPjs0SuCwBSFRneAHbYE088Yco2RLuMTgfYGgDU4LZe5qfBah0M66AyWgaEm1atWpnlamaNlqjQ0heaRf3hhx9K8+bNXTNBdKCng+HRo0eby1q1hIYGHLWOuNYa1MGoBh61rIPWldbBfCQNKOo+6aQ8Gsw88cQTTeaLDpDL1/DWSyhfeeUVc+mjfujS7BodXGugUu/XbONwllF1GDNmjJmk6PDDDzeDf90nvaxRt1cznqMN2KtKL43UvtDLbjUjRD8Uah9plpReKqyTNeqHimj0yw798KDngQZ2NSirk5xGyyQKD9CvueYa86FD16HHrXztyMocccQR5oOPni9ay1LrhWuGjtaCnzlzZoV1ada6ttdLOjUrRz/87EgNb81cKl+XXmnWkZ7LevlymGY66bml/aXr1PNQvxiaNWuW3HLLLWUmwtQPTno5tp53ehm00truulytL6n9qee3BvS1nIt+QIssF6T1O/X46AcxfT3phxt9npbg0eUCAJDuNLir9aGPOeYYUz5DA7J6tZaOA/WL//J/7zTrVSe11DGTXg0WDvRG1mQ+8sgjTVBQA4YaTNSgtI4ZtFRIJM0Q1r+7t99+uwnm6vJ03BhtzhNdpmZV63bqeEXHQfpltS5f/z57QcdreotFvxjXsY5msGsf6Jha97WysoI6RtL90wQCLROj/apjQQ0i6zitMjpW1C/ydRynY0jdR00O0b4pn3WvYzJNctHxkY7ZtE3kFZaRNMnjjDPOMMdCE2TKl9vT/QuP1XVcp0FdHffrmFCXrWNk3ZfHH3+8zJWe+nlD+0LHZzp2iqQTgupNrwKIHNOF6ThMtyvaY9HcfPPNZjv1WOkk8zphuX7m0T7TMWz5ZB/9MkAz7cOZ8+EJzvXcd6uDnsh1AUBSOQBQRU8++aSOgCu9LVmyxLSbPn26c+ihhzr169d36tat6wwePNj59ttvoy7rhx9+KHP/pEmTzP36M6ykpMQZN26ck5ub69SpU8c56KCDnDlz5jjNmjVzzj///JjPzc/Pd0455RSncePG5rEOHTqUPrZgwQLn4IMPdrKzs51WrVo5V199tfPJJ59UWIZ68MEHnU6dOpm2e+yxhzN58mTngAMOMLdIRUVFzu233+7stttupm2TJk2c3Xff3ZkwYYKzadOmmP07cuRIp169eq7HQffhiCOOiPqY7tPxxx9v9jcnJ8fZa6+9nPfee69CO93HCy+8sMx90fpPtymyz8IeffRRs196PBo0aOD06tXLufLKK53ly5fH3PalS5c6xxxzjNm+Ro0aOSNGjDDP0fWOHz++TNsbb7zRadu2reP3+83jixYtqnS50bbz8ccfd7p162aOQ/fu3c05p+so/ydw7ty5zv7772/2RR/TZXkt2jGbNm2aM2zYMLOPWVlZ5vWy7777Oq+88kqF54dfL/ozUjAYdG655RazfF2GnnfPPfdc1G2YNWuWM3ToUPOa1P4/9dRTnZUrV3q8pwAAJMeHH37onHXWWeZvvv5N1b+LXbt2dS666CJn1apVUcdB+jczPFbo169fhfHfhg0bnDPPPNNp3ry5WaaOb3XcoH93y48XHnvsMadz585OIBAoM54qP14MhUKlf7vD69WxWrSxTLTxUXk6PtJ2d955Z5XHmd98842z9957mzFQmzZtzFjuo48+qjAe1O3XMYaOXQYOHGjGmLqt999/v2PjnXfecXr37m2e17FjRzNWfuKJJyqM73RcouMlHVvqY+XH2VX5XFJ+zLRt2zbn8ssvN58ntN/33HNPZ+LEiVHHwpXd3I6F7Vg+0ldffeUMGjTI9E2LFi3MeZmXl1ehnfZ3ZdsVa4ycrHUBQLL49H/JDbkDwI7TrAPNStEMEM0CBgAAAGxoBu+FF15oXSKvtjvwwANl7dq15io0AABSGTW8AaQNrX9dXrjusw7AAQAAAAAAULtRwxtA2tAagTpJpE7KqDW4v/76a1OrWmvOaZ1sAAAAAAAA1G4EvAGkDZ0cRie/1MkO8/LySiey1HImAAAAAAAAADW8AQAAAAAAAAA1AjW8AQAAAAAAAAA1Qo0vaRIKhWT58uXSoEEDMws3AAAAUN0cx5HNmzdLmzZtxO+vPTkmjL0BAEBtGGMVFBRIUVGRJ8vKysqSnJwcT5aFWhLw1mB3+/btk70ZAAAAqIWWLFki7dq1k9qCsTcAAKjpYywNdnfqUF9Wrg56srzc3FxZtGgRQW8P1fiAt2Z2h18IDRs2THiGy5o1a6RFixa1KrPHS/Qh/ZgqOBfpx1TC+UgfpgrOxcrp5MqadBEei9YW4f19+ptuUrd+INmbI0HHfQwe8IUkUfwS/7pCFlUpbdbjF0e84NZ/AYv1BMX9Slyb5awP1nNtszbo/prM9MUOYGT6SlyX0SlzrWubbIvl2LDpm0yX42RzDIotXk82Shxv3hvctrnAcQ93FFtsi81rLlHvWTbbEnTcj+WS4uaubQos+mZVYSPXNpuK68R8fEsw23UZ67fFXoatkMV5HrLoP8eijRdstsXvi/+9PNPvHrQN+BP3t9JNydYi+fbE/yZ1jKWZ3Rrs/v3HjtKwQXzvEXmbQ9Jh98VmmWR5e6fGB7zDZUw02J2MgLd+66PrJeBNHyYT5yJ9mCo4F+nHVMG5SD8mSiqV1Lv++utlwoQJZe7bZZddZO7cuebfOm697LLL5KWXXpLCwkI59NBD5cEHH5RWrVpVeX812F23QboEvBN3jAIWwRY3QYuAl816vAt4+1Im4F0QdD/n6pS4fwTOdNmnTIvDWC/L/Tjl+PwJDHhLygS8vVqO2zYHLAK2NgFvm9dcugW8c4osQkEWXxhkZ2a6tskqzor5eFFJ7MdVht89KG6DgHdl/ZteAe9UGmPVb+Azt+o+L1F1NT7gDQAAAGC73XbbTT799NPS7sjI+OvjwKhRo+T999+XV199VRo1aiT//Oc/5dhjj5VvvvmG7gMAACgn6IQk6MS/DHiPgDcAAABQS2iAW+tElrdp0yZ5/PHH5YUXXpCDDjrI3Pfkk0/KrrvuKt99953svffeUZenmeB6iyzlAgAAACQThaUBAACAWmLevHnSpk0b6dy5s5x66qnyxx9/mPt//PFHKS4uloMPPri0bffu3WWnnXaSKVOmVLq8W2+91WSDh29MFg8AAGqLkDie3OA9At4AAABALTBgwAB56qmnZOLEifLQQw/JokWLZL/99pPNmzfLypUrJSsrSxo3blzmOVq/Wx+rzNixY012ePimE8UDAADUBiGP/oP3KGkCAAAA1AKHH3546b979+5tAuAdOnSQV155RerUqbNDy8zOzjY3AAAAIFWQ4Q0AAADUQprNvfPOO8v8+fNNXe+ioiLZuHFjmTarVq2KWvMbAACgtgs6jic3eI+ANwAAAFAL5efny4IFC6R169ay++67S2Zmpnz22Welj//666+mxvfAgQOTup0AAACpiBreqYuSJgAAAEAtcPnll8uwYcNMGZPly5fL+PHjJRAIyMknn2wmnDz77LNl9OjR0rRpU2nYsKFcdNFFJti99957V3ldGRKUzBiPB3wJqlfpkxonU4KubQIJnADL71J71OZYN/CVeLJPjf3bXNt0z1rt2ibHZZttssZCCcw+K7Y40UMenBLF4g2/z31jQo77Pi0raRLz8aVFzVyXUewExAv1AwWeLMdtv7eGsj3ZpyUFsftObSiq59pmfaF7Oaz8ouyUOWe84niwLq+2N+jFMkLu70YZgaAnx8nvwd8nm+0FCHgDAAAAtcDSpUtNcHvdunXSokUL2XfffeW7774z/1b33HOP+P1+Oe6446SwsFAOPfRQefDBB5O92QAAACmb4R2MM4ivy4D3CHgDAAAAtcBLL70U8/GcnBx54IEHzA0AAAB2JU3iQcC7ehDwRsoKhhyZunCdzF+6XrrmB2RA5+YS8Puitvt+0XpZvblAWjbIkb06NY3aDgAAAAAAAEDNRsAbCWcToJ44a4VMeHe2rNgUroW2SFo3ypHxw3rIYT1bx2gnUdtVZd0AAAAAAABAzPiW45hbPOJ9PqIj4I2EsglQa5sLnpte4aKQlZsKzP0PndbftLVtV5V1AwAAAAAAADYTFMc7DXeCpvGudZjaFAkTDlBHBpwjA9T6uGZga1A62vdbzp+3a9/6n0z/fYNc+9asStspXY4uz3bdkfR5Uxask7dnLDM/w8sBAAAAAAAAkLrI8IZnYpULcQtkq6te/0U++GVFhaB0eWvzC+XYh76N2UaXqcu56IXpsmenpvLvz+ZVum7dQt22Q3rkmu2tSia4bYkUSqkAAAAAAADUHEFxzC3eZcB7BLzhCbcgsQaF3QLZm7YVyzs/l820rkydTL9sK3a/8OODWSvNzSY4rtu4aVuRdZkU28B4dQTQAQAAAAAAkDxBZ/st3mXAewS84cotCOtWS/vaI3vIT39ssOrpnm0ayqzlea7tLh+6i9z4/hzXdn/rmStLNmyVX5a5L/OaN2fKyrxCq0zwT2av9LzOODXGAQBATZHjK5EcX3xVKQNxPr+mCniUCeb3ebOcgEv1UZvtzbQ41pkW25tj0SbTjOrdxG4T8ihjL2CxLTbLKfbgUAattsWbiqghx31deaEc1zabg3ViPr64oJnrMpZva+zJa6VBZuzkLltBl74pCGa6LsOujXsoaGNB7P5VRcGAaxvHZZ98Fv3rt3gd+Cxe2jbH0ub89GKOwZDFa86r/XbbXpttKbE41gG/+7tjyOZ4u7Sx2V6AGt6ISYOw+97+uZz82HdyyUszzE/9PVzz2qbm9o3vzZb3Ztplbo89fFeT/VzZ25fer4+fPrCjVbv/nNJfrv5bD6t1L1y7VbYWBV0zwbU8yjVvutcPLyoJuZZxCdcZr2qN8ZqGmukAAAAAACAdJ62M9wbvkeGNStlkJzfMyXQtVaL6tGsoi9ZulbyCkkoD1LmNcmTvLs1MqQ9dvt4Xue5wcFsfz8rwW7XTTHTNSNfgt253tOCztm9eP1sO7tFSXvx+ieu+3PfZvJiPhwPjh977Zcy+Cbf7YOZyufmDudY1xtOl/Int9pHZDgAAAAAA0o1mm9tcIeO2DHiPgDeisplk8qIXfyobaY7hrH07S3aG3wSoI5cRLUCtJT40mF6+7nVuubrXtu10mW7B8RuH7yaN6mRZBbxbNsiW1ZsLXdtpgN/GRS/NsK4xPrBLs6QHiW0C2VWpb25b8sV23QAAAAAAAKi9CHjXUm6BQ5tJJourUFlf16HBWpsAtdJ/a0bz1IVrZf7SNdK1XQsZ0Ll5heBmuJ1bENQmOK594pYJru3/NaKPnPrfqa77PLxvG3lrxnLXduWD8JV54uuFsqWwRNZvLZKrXptpHSROdCDbNoit67v+ndnWme1kggMAAAAAgFQRcrbf4l0GvEfAuxaKFTg8dLdc+WXZJnl08gKrZY05vLs89c1iWZUXO0iswdOqBKiV3rd352bSuX5QWrZsJv5KMnm1XTjzORa3ddtkguvjuk02gfE7ju8jUxetd21353G95bQnvnfd/k/mrDa3ysQqf+LGq0C2rtftyoDLXvlZXp22ROas2Cwr89xLvui52LRetox53fsgPwAAAAAAwI4IelDSJN7nIzoC3rVMZUFLDSye/9x0aVE/S9bkF1kvr0+7xnL9UXa1tKsaoK4Obuu2LZPiZZ3xgV2bxwygq0Z1MuWQHi1lyoJ1smxjQZXKn2gm9dSF62T+0vXSNT9QIVPeq0D21W/OsroyYEtRUD6bu0Zs3T7x12oJ8gMAAAAAAKDmIeBdi8Sqyx2mwe7sDJ8cvGsr+WbBOtm0tdg1c1uDjLalStKBTRa613XG3QLjtx/Xy7R9e8YyucSl5rf6Y/0WE/CumLm9qEzmtk2t9mvfmiW/rtzsGshev6VInvhmsdgYsXs76daqvtzywVzXts3qZcq6LcVVCvIDAAAAAABUJzK8UxcB7xomVh1mm+xb9fBpe8jg7i1LM39tMrerUqokHdhkoVelfrhXAXR9ro3xb/9P3v9lhUz+bW2FxyIztzP8ftdzYm1+kdzz6Tyr9XZsVlcWr3OfrPPY/u1MHzz5zWLXki9XHtZdRr3sHuTXvgUAAAAAAEiEkOMzt3iXUVWTJ0+WO++8U3788UdZsWKFvPnmmzJ8+PDSxx3HkfHjx8tjjz0mGzdulH322Uceeugh6datm9QWBLxrELc6zN8tXGe1nLyC4ioFYVOhVEmy2O6zVwF0/d2t/Im2LygJRQ12q/Dz/u/56daTI7RskC2rNxe6trt5eC+5/LWfXYPY4f2yKfnSqE6W5TbafRkAAACqX7a/RHL8/kofD0jIk/UErKb+Th02dTrd9snvcxLWd170r03+i816Mi2Wk2nTvz73NkHH5Ri4b4r4LbYl+rW05bfFfV2ZFsspdNkem2NQ7LjveYGTabGcgGubLaFs1zYBXyju14pNmxVbG7i3Efc2NoIhf1yPq6Kge//6LPY74FEbx+XcClqcVyGL17/f5XVrew47FsHHeAOcKuB3/zto8XZl9zfBF//+2LTxWbSxOWfgbsuWLdKnTx8566yz5Nhjj63w+B133CH//ve/5emnn5ZOnTrJuHHj5NBDD5XZs2dLTk7tiJ0Q8K4hYtVh1trcXVrUkwVrtlQ5cFjTMrdTnVtg3CZIfP/J/WTZxm1y0/tzPJsJ+J4T+loFsvfu0swqiB15ZYDblyp61YJbkF/9vHSDDOjU1ExuGutKBwAAAAAAgFQqaZKXl1fm/uzsbHOL5vDDDze3aDS7+95775Vrr71Wjj76aHPfM888I61atZK33npLTjrpJKkNCHiniVgBPJs6zBrszvCJZGYGZFtRMOo6IrNva3vmdiqzCRJrrW8btx3XS+77dJ6ngeyqXhng9qWKW5A//PttH/4qUxaslyN6t5Z7Pvmt0isdAAAAAAAA4hUUv7nFt4zt2rdvX+Z+LUly/fXXV3l5ixYtkpUrV8rBBx9cel+jRo1kwIABMmXKFALeSJ9SJba1uf9zSn9zSYwGDsUi+xapyy1IbFveo0PTetUSyK7qlQFuX6rEWvd1R/aQDVuLZcK7/5Mvf1tjbrHqlhP0BgAAAAAAqWTJkiXSsGHD0t8ry+52o8FupRndkfT38GO1ARneaVyqRO+/5dieMnPpJqtlFQVDcnTftlXKvkXqihUkdqv1Xb6WdnUEsr2+MsBt3X3bN5aj7v9aSqLUatF7tJXuoy6DL3UAAAAAAEA8tOZ7vDXdw3XjNdgdGfBGfAh4VxMtMzJ14TqZv3S9dM0PyIDOzaMG2eItVTL2jVnW2xTO+qUud81nOyFkZC3tZAWyqyLWujdtK44a7A7TRzSgr/tIiR4AAAAAAJAqNby9kpuba36uWrVKWrf+K4FRf+/bt6/UFgS8E1KCZFHUGsJelSrp0qKurMorkvzCEuva3NTlrvmqWks73c8JDdR72Q4AAAAAACCddOrUyQS9P/vss9IAt06IOXXqVLnggguktiDgneASJOEawrHanf/cdDmkRyv5beVmq3VePGRnyc7wU5sbFYQzt6cuXCvzl66Rru1aVHq1QbqzrVvetG6W1RUWAAAAAAAAlQk6fnOLR7DyC9UrlZ+fL/Pnzy8zUeWMGTOkadOmstNOO8mll14qN910k3Tr1s0EwMeNGydt2rSR4cOHS21BwNtDNiVIrn5zlmT4fDL2jV9itvtk9irr9WqgTjNzqc2NaDSAu3fnZtK5flBatmwm/hoa0HWrWx427u1Zct2wHlJUEop5hQUAAAAAAEBlQuKTkMQX8A7FjGBEN23aNBk8eHDp76NHjzY/R44cKU899ZRceeWVsmXLFjn33HNl48aNsu+++8rEiRMlJ8cuUbAmIODtIZsSJOu3FMk/nv3RannnH9BZXp++TNZuLnSdeFBRmxu1mVvdcv29QU6GLF63Vc56alrUZZS/EgMAAAAAACCVHHjggeI4lQfKfT6f3HDDDeZWWxHw9pBtbeCmdTNl/dZi13a7tm4oNx7d2HriwZpQhxmozrrl+3RtLv/+fJ48NnlR1Ofra0xfTfp8LQVDeRMAAHZMPV+x1PPFl/FkI7ADWVHJ5MWFdl7ts83RCSTowsBMiwm7Aj73Nn6PJv7K9ODcLZaQe5sYwYqwkEcTnuU5f5X1i2ZNsL7rMjYH61hsi3vfFYQyrbIm3WwJZcd8vK6/yHUZfp/FcQoGXNsEQ96837mdEjalE0qC7m0yM4Kubfw+9/PTZ9HGcVyOZcjmLE+ckNv2Whwni7crz9gcA9djaXH+2vSLV68Df8D9/EwVqThpJbYj4J2EGsIXDu4qN74/x2p5lCoBqsbtSoeDdmlVacBb6VBAg+X6fL48AgAAAAAA1VfDO72+vE8XBLwTWEM4XILk9IEd5b9fL3JtR6kSYMfEutLB9koM23YAAAAAAABIHdV/nWEtrCGsyl+QEFmCJCvDb9UuWqmSo/u2NT8ptQBU75UYtu0AAAAAAEBtnbQy/hu8R8C7mmoIa4Z2JP09ciI823YAqudKDLc/Ke/OXCZbCkvMv4MhR75buE4+nrve/NTfAQAAAABA7RUSv5m7IJ6bLgPeo6RJNdYQnrpwrcxfuka6tmshAzo3r5CV7VZrGED1XYlR2WSw4d9fmLpEvpm/Tkbs3k6en/pHxCSYi0zAXJfBF1MAAAAAAACphYB3NQbV9u7cTDrXD0rLls3EX0kQO1atYQDVI3yFxYR3Z0cEsrdfYaGB7PrZmXLlaz/L7+u2yr8+/q3C87X+vgbMuRoDAAAAAIDaiUkrUxcBbwC1ktsVFu9fsp8MuvVz2VYcrPBczQLXVhow12VwVQYAAAAAALVLyIOSJKEy153DKwS8AdRasa6wmLtic9Rgd5j+SdLscA2Yc5UGAAAAAABAaiDgDQBRaNa3l+0AAKhNsn0hyYkxLU3QdfpoOwEPsqISOVVUwIPdDnjUdzb7HfC5ryszhSbbClqcD170X7GEXNusDLqvZ2OwnsW6Aq5t1pQ0dG2zpLhpzMd/25LruoyQRd9l+CpPGKmKEsd9v4tDsdvkFee4LmNDQR3XNluLMsULAX/871chx/0Y+HzeZIvarCvT7/5a8Lm0yXA/1BIM+T1p43cs+sZin7wQsDhOAYtt8XtwvG3OGZvtdSzOmZom6PjMLd5lwHtJHZ1MnjxZhg0bJm3atBGfzydvvfVWmccdx5HrrrtOWrduLXXq1JGDDz5Y5s2bl7TtBVB7aIkTL9sBAAAAAICaIyh+T27wXlJ7dcuWLdKnTx954IEHoj5+xx13yL///W95+OGHZerUqVKvXj059NBDpaCAjEoA1UvrebdulBMzj6VedkD27NiEQwEAAAAAAJAiklrS5PDDDze3aDS7+95775Vrr71Wjj76aHPfM888I61atTKZ4CeddFLU5xUWFppbWF5envkZCoXMLZF0fbofiV5vTUIf0o/JooHucUfsKhe+8JP5d7QLuLYUBuXqN3+Rm4f3ZOJKS7ymvUE/0oepgnMxdt8AAACg5go5fnOLbxlMWlmrangvWrRIVq5cacqYhDVq1EgGDBggU6ZMqTTgfeutt8qECRMq3L9mzZqEZ4brB51NmzaZoLffzyUK9GHycC7umP4t/XLLkZ3lni+WyOr84tL7W9XPlP06N5Y3flkjr0xbKus2bZHrD+somQFe55yLicFrmj5MFZyLldu8eXMCjwQAAAASzYuSJDbzT6AGBbw12K00ozuS/h5+LJqxY8fK6NGjy2R4t2/fXlq0aCENG7pP5uH1h0CtTa7rJuBNHyYT5+KOO7FlSzl+751l6sJ1smD5GunSpoUM6NzMZHQf1HOlXPLyDPls3gYp8QXkwVP6S1aGX35YvF5Wby6Ulg2yZc+OTcn+5lz0HK9p+jBVcC5WLieHOR4AAACAZEjZgPeOys7ONrfyNOCcjKCzBryTte6agj6kH5NNX76DujaXrg1D0rJl89LX8996t5H6OZly3rM/ype/rZWjHvhG8gtLZFXeX2WVtA74+GE95LCercssMxhy5PtFGhgvMBNfas1wDaLXBrym6cdUwblIP1Ynxn4AAAA1mxawCzrxfY6nCF4tC3jn5uaan6tWrZLWrf8KFOnvffv2TeKWAcBf9t+5hTz3j73k1P9OlQVrtlTompWbCuSC56bLQ6f1Lw16T5y1Qia8O1tWbCpwDYwDAAAAAIDUExK/ucW7DHgvZXu1U6dOJuj92WeflSlPMnXqVBk4cGBStw0AIvVt30TqZ0f//jBcjUsD3JrVrcFuDYBHBrsjA+P6eCR9zpQF6+TtGcvMT/0dAAAAAAAAKZjhnZ+fL/Pnzy8zUeWMGTOkadOmstNOO8mll14qN910k3Tr1s0EwMeNGydt2rSR4cOHJ3OzAaAMLU2yNr+o0l7RELUGuA+443NZk18UdUoKvc/3Z2D8kB65prwJmeAAgHTOqomVWeNPoQmaAhZXIgfMX+n4M4kCPpvlxF/izG57vSml5rZPfoueCXl0QXemRRub7dkQ+qs8XTQrg+4fo7/d2tW1zeytbVzbrCpwn4cqZHE5fUko9n4XhQKuyygKurdxLLYlZHHuBV2212xPSeztKbbZp2L3NiGLbfH53N/T/P743/f8FuuxUWxzzlisy7HYnIDLfgf8IW/226Pl2Jx7wZAv7nPcpu/sXk+JYbUtFm0cl76zWVdJSVBSRdDxm1u8y0ANC3hPmzZNBg8eXPp7eLLJkSNHylNPPSVXXnmlbNmyRc4991zZuHGj7LvvvjJx4kQmAQKQUrQOt42lG2O3CwfGn/x6kTSumylXvDazQjggWokUAAAAAACQWPrlhs0XHG7LQA0LeB944IHixPhaSyeTuuGGG8wNAFKVTjpp46g+beSdn5e7trvpgzmVPhYtExwAAAAAAADbkTcPAHHaq1NTM+lkZaFnvV8fP3HP9lbLa1wn0yoTXEupAAAAAACA5JU0ifcG79GrABAnzbIeP6yH+Xf5oHf4d318787NrALj44/azWq9q/IKqjy5JZNgAgAAAAAQv6D4PbmhhpU0AYCaQutpa11tLTWi2ddhuRrAHtajtN62/ltrcGtw26kkMN6oTpbVOv/18VzReaIy/D656f05Zdbbutx6FZNgAgAAAACAmo6ANwB4RIPLWldbS43oRJZa21vLnUTW2bYJjGsWtgasdYLKymY50CUu3VAgl7w0I+rj5Se31GC3/s4kmAAAAAAAxC/k+Mwt3mXAewS8AcBDGtwe2KVZXIHxcImUWJng/zqhjyzbsFXu+XSeRJv7N3zX1W/OkobZmXLtW7OiBs+ZBBMAAAAAgKoLeVCSRJcB79GrAJDEwPjRfduan5FZ4JGZ4Jr5HUl/1/uP699O9uzYLGqwO9L6LUVyyuNTZW1+UaVtmAQTAAAAAADUFGR4A0CKcssE1/tsNMzJkLyCEtd25ZenpVVilWcBAAAAAKC2Cjl+c4t3GfAeAW8ASNMSKRqEtnHJkG5y4/tzXNt9+dsaOXCXltKoTiYTXAIA4pLl85lbPGw+/oUkMWy2JWCxv/7S4mQxluPSxmoZVtvizQdsm+1xX0bAtU2o0plNItu4nxGFjnsSQKZL/+UG3JfRv85i8cK6wvqubdYX1XFtUxiM/dE/4HPvO8eizmzI4nwoDrof7yKLNsUlsdsEQ96c44FAot5pRPw+9/Pcjc9iGTbH0mY5NsfbbTE+j+oX2+xT0OYctuqb2I8HLN6vbNhsixNKnYQkm/PBckFpU/M6KD5zi3cZ8B5fIwBAmtKMa53csrI/j3q/Pn76wI4x24W9MX2Z7Hf753LxC9NN/fDISTUjJ8LUCTABAAAAAABSEQFvAEhT4cktVflgdvh3fTwrwx+znd7O2a+TdGtZ35Q+eWfmikonuFQT3p1typ0AAAAAAFDbS5rEe4P36FUASGNuk1vq4zbtrjmih0y8dH+56KCuMdfHBJcAAAAAAGg5kr/Kmuz4DdWBGt4AUMMnt7Rtpz+7tnSvE1mVCTMBAAAAAAASiYA3ANTwyS2r0s52IkzbdgAAAAAA1ERelCShpEn1IOANAKgwEaZOUFlZlW59XNsBAAAAAFBbBR2/ucW7DHiPXgUAWE2EGdaqYY4UB0P0GgAAAAAASDkEvAEAZVQ2wWWTupmS4ffJjCUbZeQT38umbcXm/mDIkSkL1snbM5aZn/o7AAAAAAA1mSM+CcV502XAe5Q0AQBUUNkEl1MXrZNzn/lRpi5aLyc+MkX+Pqij3PfZPFmxqaBMyRPNEtdlAABqb1ZNrMyagM+bD3cBSR1+iw+sAYs2NstJlFTalkyf+9EutvjO3V9p0ba/ZLucWZk+92W0CLhP8N0mc4Nrm5bZm13b+H3uV95tLcmSeJWE3PPltpZkurYJlZtYPZpMxxd3NMMXTK0kDMdmnxLEZ3EO22xv0OKcEH/s89MJui9Dg4LY8eMUStC557c4r2wEXY53KpUAoaRJ6kqdswQAkFLCE1we3bet+am/D+rSXF46d29pXj9b5q7cLGPe+KVMsFtp/e8LnpsuE2etKHO/Zn5/t3CdfDx3vflJJjgAJM9tt90mPp9PLr300tL7Vq5cKaeffrrk5uZKvXr1pH///vL6669zmAAAAJBWyPAGAFRJz7aN5NXzBsrB93wZNWit9+h38hPenW2yxDVQrsFv/f2v4PgiMsEBIEl++OEHeeSRR6R3795l7j/jjDNk48aN8s4770jz5s3lhRdekBNOOEGmTZsm/fr143gBAACUy56PN4M+URn4tQ0Z3gCAKluZVxAzQ1sf0eC2lkTRYLdmfNtmggMAqk9+fr6ceuqp8thjj0mTJk3KPPbtt9/KRRddJHvttZd07txZrr32WmncuLH8+OOPHBIAAIByguL35Abv0asAgCrTut427vporlzx6syo1TLD92nmN+VNACAxLrzwQjniiCPk4IMPrvDYoEGD5OWXX5b169dLKBSSl156SQoKCuTAAw+sdHmFhYWSl5dX5gYAAAAkEyVNAABVppNY2pj2x8aYj0dmgmudcABA9dEA9vTp001Jk2heeeUVOfHEE6VZs2aSkZEhdevWlTfffFO6du1a6TJvvfVWmTBhQjVuNQAAQGqipEnqIsMbAFBle3Vqampwx6o21qRupvytZ66nGeMAgB2zZMkSueSSS+T555+XnJzoX1qOGzfO1PD+9NNPTd3u0aNHmxrev/zyS6XLHTt2rGzatKn0pusBAACoDULi9+QG75HhDQCoMp2IcvywHqYGtwa9I0uWhIPgtx7bSxrVyZIPZq30LGMcALBjtA736tWrpX///qX3BYNBmTx5stx///3y66+/mp+zZs2S3XbbzTzep08f+eqrr+SBBx6Qhx9+OOpys7OzzQ0AAABIFQS8AQA75LCereWh0/qbGtyRE1LmNsoxwXB9XGtzaya4TlBZ2RSX+rhmjAMAqs+QIUMqZGqfeeaZ0r17d7nqqqtk69at5j6/v2yWUSAQMPW8AQAAUFbQ8ZlbPOJ9PqIj4A0A2GEa1D6kR66pwa1lSTRTW4PXmgHulgkedmz/tqXtAQDVo0GDBtKzZ88y99WrV8/U69b7i4uLTa3u8847T/71r3+Z+9966y355JNP5L333uOwAAAAlEMN7+10kvOZM2eaqwnLJ0ocddRRkgwEvAEAcdFgdawJJyvLBK+TGZBtxUF54uvFMrRHrvRp35gjAQBJkpmZKR988IGMGTNGhg0bJvn5+SYA/vTTT8vf/va3qi/P5ze3dBCIOSOFt/werCvgc1+GvwbWAw06IU/6N9MXkERoZfFl/pqMTa5thjauvIZ+VWwO1Yn5+NZQlusypud3cG2zoaiua5uNRbG3ReUV5lgFmmIKJO7qFMciQzNU2eWOkW1CPg/WY/Ee4XPfGJ9FG8din4Kh2O9HQYm/X7Zvizfv5Tb77dbG4m3aik3/2uy3WxubfbYR8ujvqes5U2xz1iBRJk6cKGeccYasXbu2wmM+n8+U0EsGAt4AgIRlgk9duFbmL10jXdu1kP4dmso5z0yTr+atlbOe+kHe+L9B0qFZPY4GACTIF198Ueb3bt26yeuvv07/AwAAWHAcv4Qcf9zLSGcXXXSRjBgxQq677jpp1aqVpIr07lUAQFplgu/duZkM7d7U/MzJDMhDp+0uu7VpKOu2FMnIJ76XdfmFyd5MAAAAAABcBcXnyS2drVq1SkaPHp1SwW5FwBsAkDT1szPkyTP3lHZN6sjidVvlrKenyeaCYpmyYJ28PWOZ+akTXwIAAAAAgNRy/PHHV7hqMBVQ0gQAkFQ60eXTZ+0lxz30rfy8ZKPscdOnUljyV83F1o1yzMSXWhYFAAAAAIBUoLlZNnXz3ZaRzu6//35T0uSrr76SXr16mXlhIl188cVJ2S4C3gCApOvSor6cs18nufOj38oEu9XKTQVywXPTzcSXBL0BAAAAAKkg5EEN73ifn2wvvviifPzxx5KTk2MyvXWiyjD9NwFvAECtpWVLnvvuj6iP6Rfe+idzwruzzcSXWgscAAAAAAAk1zXXXCMTJkyQMWPGiN+fOsH71NkSAECt9f2i9bJiU0Glj2vQWx/XdgAAAAAAJFtIfJ7c0llRUZGceOKJKRXsVqm1NQCAWmn15gJP2wEAAAAAUJ2Cjs+TWzobOXKkvPzyy5JqqOENAEiJiSu9bAcASC6/+CSQAhlLuh1uAhG1JmsTm75JlIDPPQ8r6JSd46M69ztkri2L9XjIk/OqX5b7TGX5zjpP9qnYpf+2Ou7b0iiw1bXN/MJWrm3m5rtPRJ7lD7q2WVdQN+bjRSXu4Q6L3bbjc19QZF3byrcn9mshGPJ7sk82ATab7fVb7Lf7etyX4dV+221P/PudyH1yrI6l2/Z68/fAq+11mwQyGEydv18QCQaDcscdd8hHH30kvXv3rjBp5d13352UbiLgDQBIur06NZXWjXLMBJXRxkk6pMltlGPaAQAAAACQbExaKfLLL79Iv379TH/MmjWrWr5M2REEvAEASacTUY4f1kMueG66CW6XD3rr7/o4E1YCAAAAAFKBqcEdZ0mSdK/hPWnSJElF1PAGAKSEw3q2lodO628yucvr1baheRwAAAAAACAWAt4AgJShQe2vrzpIXjxnb7nvpL5yzwl9zPfdvyzLk5lLNyZ78wAAAAAAMBzN8I7zpstIN8cee6zk5eVZtz/11FNl9erVkkiUNAEApBQtWzKwS7PS37+at1be+GmZ3PfpPHn873smddsAAAAAAFBaziTukiZxPj8Z3n77bVmzZo1VW8dx5N1335Ubb7xRWrZsKYlCwBsAkNL+eVBXeWvGMvls7mqT5d27XeNkbxIAAAAAALWS4ziy8847Syoj4A0ASGmdW9SX4X3bmizvf382T/47kixvAAAAAEByhRy/ucW7jNowUWXbtm0lkQh4AwDSJsv70zmr5Zelm6RXu0bJ3iQAAAAAQC1WW0uaHHDAAZLqCHgDANIiy/vovm3lTa3l/dlvZHkDQIrzi8/cki3gc98Gv6RXZpVX/RrwJWa/g04oYdtrsy6b5YScYMqcM/V9Wa5til221+a1EPC5990+OStc2+QGNrm2qesvcm0zY3N71zZFoUDMxwv87v2SX5jt2kYnlUsUt7esgN/9OJUE3c9PxyLA5jjiSd/4fLEX5LN4n7YR8DueBBb9Pg+WY7GeUMi9TTBkcSxD8R+DQCDkzTnj2kIkZHF+urUJFRHKhLv0Gt0BAGp1lrffJybLe9Yy9w9TAAAAAABUF/3Sx4sbvEfAGwCQFrq0qC9H9Wlj/n3vp/OSvTkAAAAAgFosXNIk3hu8R8AbAJA2/nlQtz+zvFeR5Q0AAAAAACog4A0ASBtdW/6V5X3fZ2R5AwAAAACSgwxvkYMOOkg2btxYoW/y8vLMY8mS0gHvYDAo48aNk06dOkmdOnWkS5cucuONN4pjM3sCAKDGZnnr3DafzF4lL0z9Xd6esUymLFgnwRB/GwAAAAAANTfgff3115vJXiNv3bt3l2T54osvpKio4mTEBQUF8tVXX0mypPTUprfffrs89NBD8vTTT8tuu+0m06ZNkzPPPFMaNWokF198cbI3DwCQpCzvPTo0kR8Wb5Cr35xVen/rRjkyflgPOaxna44LAAAAAKBG0hjpp59+Wvp7Rkbiw7szZ84s/ffs2bNl5cqVZRKYJ06cKG3btpVkSemA97fffitHH320HHHEEeb3jh07yosvvijff/99pc8pLCw0t8gUehUKhcwtkXR9mo2e6PXWJPQh/ZgqOBdTpx8nzlppgt3lrdxUIBc8N10eOKWfHNYzV2oyzkf6MFVwLsbuGwAAANRcXkw6GX5+OH4Zlp2dbW7RaIA7Nze5n3n79u1bmmEerXSJVur4z3/+I8mS0gHvQYMGyaOPPiq//fab7LzzzvLzzz/L119/LXfffXelz7n11ltlwoQJFe5fs2aNSadP9AedTZs2meCO35/S1WNSFn1IP6YKzsXU6EctW3L9O39ldUcKFzSZ8M4s6dPcJwGd3bKG4nykD1MF52LlNm/enMAjAQAAgETTz6Ahie9zZ/hzbPv27cvcP378eFO+JJp58+ZJmzZtJCcnRwYOHGhioTvttJMk0qJFi8zn+s6dO5vE5BYtWpQ+lpWVJS1btpRAICDJktIB7zFjxphvOLQWjXaSpsTffPPNcuqpp1b6nLFjx8ro0aNLf9fn60mjHd+wYUNJ9IdA/aZD103Amz5MJs5F+rCmnIvfLVwnq/OLY7ZZlV8sv2/NkL07N5Oaitc0fZgqOBcrpx9AAAAAABtLliwpE7esLLt7wIAB8tRTT8kuu+wiK1asMEm/++23n8yaNUsaNGiQsM7u0KFDSl/VmNIB71deeUWef/55eeGFF0x9mhkzZsill15qvsUYOXJk1OdUlvKvgZVkBJ01sJOsddcU9CH9mCo4F5Pfj2vyi6zb1fT3Xc5H+jBVcC5GV9Pfg+IV0NmHaxi/RYZXwJde54Vf4t9ev8U+h8T9w3LQSdwH6kxf/BlpxU5QappMi9dtl8ytrm2yfAtc2wQszokpoS4xH19e4p7wZvNW5ITcGwUt2qQSx6IEg8108N7stfuaAn73Nn6/+zlj844WCsX/vudYdJ5NGYxQ0KaN+/Z68SfX5pwJWbwOHJt9Kom9T6Fif40saaLBbptE3cMPP7z037179zYBcA0+awz17LPPlmSYN2+eTJo0SVavXl0hAH7dddclZZtSOuB9xRVXmCzvk046yfzeq1cv+f33302qfmUBbwBAzdWyQY6n7QAAAAAASHbAe0c1btzYlIGeP3++JMNjjz0mF1xwgTRv3tzUFddkmDD9NwHvKLZu3VohO0ZLm6RqujwAoHrt1amptG6UYyaojJY4oX9acxvlmHYAAAAAANRk+fn5smDBAjn99NOTsv6bbrrJlJ++6qqrJJWkznUAUQwbNsx02vvvvy+LFy+WN99800xYecwxxyR70wAASaATUY4f1sP8u/z34OHf9fGaPGElAAAAACB1MrzjvVXF5ZdfLl9++aWJk3777bcmRqrJwSeffLIkw4YNG2TEiBGSalI64P2f//xHjj/+ePm///s/2XXXXc1BPe+88+TGG29M9qYBAJLksJ6t5aHT+ptM7kj6u96vjwMAAAAAUNMC3kuXLjXBbZ208oQTTpBmzZrJd999Jy1atJBkGDFihHz88ceSalK6hrfOLnrvvfeaGwAAYRrUPqRHrnw1b438/ckfzH3vX7SfNK2fRScBAAAAAGqkl156SVJJ165dZdy4cSbornMvZmZmlnn84osvTsp2pXTAGwCAymjZkgN3aSnN6mXJui1FsnzTNgLeAAAAAICEcByfucW7jHT26KOPSv369U2ZFb1F0kkrCXgDALAD2jWtawLeSzdslZ5tG9GHAAAAAIBqFxKfucW7jHS2aNEiSUUpXcMbAAA37ZvUMT+XrN9GZwEAAAAAkGBFRUXy66+/SklJiaQCSpoAANJa+6Z1zc8lG7Yme1MAAJb8Fnk3fg8ynkLixL0M220J+PwJ65uaxmqfLU6HoBPypI0XvDh/bc/hTF8g7vUEnWLXNoUWfbfF4iXn97k3apqxxbVN86z8mI9vLXGf26Uo6B4SKQm5n58lQfdj4Fj0jc8X/zJsyiPYvDN6tRxxWY7N+eDzuZ97Povt9fu9+Zvgdhys+s6jMhZOyIvjZPEeYnGcbLYlVGzxWimO/ZpziuJ/z/PKjkw6GW0Z6Wzr1q1y0UUXydNPP21+/+2336Rz587mvrZt28qYMWOSsl21bzQFAKhR2jf5M+C9noA3AAAAACCxNbzjvaWzsWPHys8//yxffPGF5OTklN5/8MEHy8svv5y07SLDGwCQ1tr9WdJk6QZKmgAAAAAAkChvvfWWCWzvvffeZpLKsN12200WLFggyULAGwBQI0qaaMDbcZwyf2QBAAAAAKgOlDQRWbNmjbRs2bJC32zZsiWpn80paQIASGttGueYeofbioOyNr8o2ZsDAAAAAKgFKGkisscee8j7779f2ifhIPd///tfGThwYNKODRneAIC0lp0RkNyGObJiU4GZuLJFg+xkbxIAAAAAADXeLbfcIocffrjMnj1bSkpK5L777jP//vbbb+XLL79M2naR4Q0AqDF1vJm4EgAAAACQqAzvUJy3dJ+0ct9995UZM2aYYHevXr3k448/NiVOpkyZIrvvvnvStosMbwBA2mvfpK78sHgDE1cCAAAAABLCMUHv+JeR7rp06SKPPfaYpBIC3gCAtNeudOLKrcneFAAAAAAAao1QKCTz58+X1atXm39H2n///ZOyTQS8AQBpr31pSZNtyd4UAICIBHw+c0u2TF8g2ZuQ1kJS9kPrjgg68S/DVqhG5MlV/RgUOEHXNgGJ//WYafGazrQ43vV8xa5tumSudm0TrB97e+oHCl2XIdLatcXKLQ1c2xQWu4dWgqH4K8ralD7w+dxfB1YlFJzEbE/I4tx0gt5U43UsUnG1xEQi+G2Ok8Vrzh+wWI7Ly9Lmbdpx3I+BE7I4lsXuy/EV+uN6PJH0/NX/4l1GOvvuu+/klFNOkd9//73Ca0wnsAwG3f9GVQcC3gCAtNf+zwxvnbQSAAAAAIDq5nhQgzvda3iff/75sscee8j7778vrVu3NkHuVEDAGwBQYyatXL5xmwRDjgT8qfFHFgAAAACAmmrevHny2muvSdeuXSWVpM51AAAA7KDWjepIht8nxUFHVuUV0I8AAAAAgGqlJXC8uKWzAQMGmPrdqYYMbwBA2tOM7jaN68gf67fKkvVbzb8BAAAAAKguWrLaojS86zLS2UUXXSSXXXaZrFy5Unr16iWZmZllHu/du3dStouANwCgRmjf9M+A94ZtMiDZGwMAAAAAQA133HHHmZ9nnXVW6X1ax1snsGTSSgAA4tS+iU5cuU6WMnElAAAAAKCaMWmlyKJFi1LyPCPDGwBQoyauXLJ+W7I3BQAAAABQwxHwFunQoYOkIgLeAIAaoX1TzfAWWUKGNwAAAAAACbFgwQK59957Zc6cOeb3Hj16yCWXXCJdunSRZPEnbc0AAHionSlpIrJ0/Vb6FQAAAABQrUKOz5NbOvvoo49MgPv77783E1TqberUqbLbbrvJJ598krTtIsMbAFBjJq1UK/IKpKgkJFkZfKcLAOksJE78C3FCrk0Cvtr59yIk7n0TtOg/t+PkF28+yJdI0LVN0HESc14lUFC82adil2PpfqRFiiz6d6vj/noqcAKubYIW581OmetjPl7PX+i6jEYZ7qXwfvB1dG2zOOi+35u35bi2CYV8cT2+nXsbJ5S49z2fP/bZ5fNmlyRokc/puGyLrYDf7bXg/lopsThnLF5yVm3chErcX5MS9HmyLT6L5fhC8T2eSLrP8R4DL45hMo0ZM0ZGjRolt912W4X7r7rqKjnkkEOSsl21c3QHAKhxWtTPluwMvxkwrNhEHW8AAAAAAKqTljE5++yzK9x/1llnyezZsyVZCHgDAGoEn8/HxJUAAAAAgARmePvivKX3wWrRooXMmDGjwv16X8uWLSVZKGkCAKhRE1cuWLOFiSsBAAAAANUqHLSOdxnp7JxzzpFzzz1XFi5cKIMGDTL3ffPNN3L77bfL6NGjk7ZdBLwBADVG+z8nrlzCxJUAAAAAAFSrcePGSYMGDeSuu+6SsWPHmvvatGkj119/vVx88cWSLAS8AQA1buLKpRuo4Q0AAAAAqD5ajSTeiiRODSgtOmrUKHPbvHmzuU8D4MlGwBsAUGO0C2d4b9ia7E0BAAAAANRglDT5y+rVq+XXX381/+7evbup7Z1MTFoJAKiBJU3I8AYAAAAAoDppVvfpp59uypgccMAB5qb/Pu2002TTpk2SLAS8AQA1rqTJ2vxC2VYUTPbmAAAAAABqek2TeG9p7B//+IdMnTpV3n//fdm4caO5vffeezJt2jQ577zzkrZdlDQBANQYjepkSoPsDNlcWCJLN2yVbq2SXzsMAGqjoOOYW6WPi/uXkgGfLzH5PU7IYlsSlycUEvftcRO02KeQR5+w3bbXZm+KLbbXRtBin7zY71jndlUUe3QMggkKltgcpRyfe6uguL+2t4TcX3NFTsBlPe7LaBRwL4OX4Xd/v7J5uwo5PqvyCPFyLPrO5iVnsy0+n/vJ5zixt8eTt3qVaZHsYtM3FqsKWJwTboIW2xIsin2OKydo8ffJpY+dYotjXeK+HovTQSRos67YbXwWy0gYxxf/69aD130yvffee/LRRx/JvvvuW3rfoYceKo899pgcdthhSdsuMrwBADWGTpjRrun2siZMXAkAlbvtttvMe+all15a5v4pU6bIQQcdJPXq1ZOGDRvK/vvvL9u2USYKAAAAFTVr1kwaNWpU4X69r0mTJpIsBLwBADVKuybby5owcSUARPfDDz/II488Ir17964Q7NZMnKFDh8r3339v2v3zn/8Uv5+PDAAAAOXpBT9e3NLZtddeK6NHj5aVK1eW3qf/vuKKK2TcuHFJ2y5KmgAAaujEle6XqAJAbZOfny+nnnqqucz0pptuKvPYqFGj5OKLL5YxY8aU3rfLLrskYSsBAABSn+NBSRMvShkl00MPPSTz58+XnXbaydzUH3/8IdnZ2bJmzRqTZBE2ffr0hG0XAW8AQI2cuHLJei7BB4DyLrzwQjniiCPk4IMPLhPwXr16tZlwSIPhgwYNkgULFkj37t3l5ptvLlOTsbzCwkJzC8vLy6PTAQAAaonhw4dLKiLgDQCokRneSzeS4Q0AkV566SWTWaOlSspbuHCh+Xn99dfLv/71L+nbt68888wzMmTIEJk1a5Z069YtamfeeuutMmHCBDoaAADUPpqdXcsnrRw/frykIgryAQBqlHZkeANABUuWLJFLLrlEnn/+ecnJyanweCgUMj/PO+88OfPMM6Vfv35yzz33mJImTzzxRKU9OnbsWNm0aVPpTdcDAABQG1DDu2LpPL3aL/KWLGR4AwBqZIb3pm3FkldQLA1zMpO9SQCQdD/++KMpW9K/f//S+4LBoEyePFnuv/9++fXXX819PXr0KPO8XXfd1dRhrIzWZ9QbAAAAap9FixaZSc6/+OILKSgoKL3fcRzx+XxmvJkMBLwBADVKvewMaVovS9ZvKTITV+7WplGyNwkAkk5Lk/zyyy9l7tNMbq3TfdVVV0nnzp2lTZs2pYHvsN9++00OP/zwBG8tAABAGnD+vMW7jDR22mmnmeC2XhHYqlUrE+ROBQS8AQA1Tvsmdf4MeG8j4A0AItKgQQPp2bNnmb6oV6+eNGvWrPT+K664wtRh7NOnj6nh/fTTT8vcuXPltddeq3IfhsQxt3iE9DphF5m+xFRoDDrbS74kQonEnwkVtOg7r7gd56DFeVCcwP5N3Jq8EbQ4lMUSf3AhZLGeoAfrsZXlc38dhFwqtNosI+BzPyNCjvv7TNCmBq9FH7u9dENB921xgu7b4nhVM9gXfxuv3q18fouNCaRX8NGxON5SaHFOBGLvlM/inPGMz6KD3Y5lChVn1tdSvK8nz16PSfLzzz+bKwm1DF4qIeANAKhx2jWtKz8v3SRLNzBxJQDYuvTSS82lqKNGjZL169ebwPcnn3wiXbp0oRMBAABQwZ577mnmcCHgDQBANWvXpI75uXTDNvoaACqhtRbLGzNmjLkBAAAgva4KSIb//ve/cv7558uyZcvMVYOZmWXn0Ordu3dStosMbwBAjZ24Umt4AwAAAADgNUqaiKxZs0YWLFhg5oYJ0zreTFoJAIDH2jf9M+BNSRMAAAAAAKrFWWedJf369ZMXX3yRSSsBAKjuSSvDJU3C3ywDAAAAAOBpOROndpdE+f333+Wdd96Rrl27SipJoblNAQDwRts/A95bi4KyfksR3QoAAAAA8JjPo1v6Ouigg+Tnn3+WVEMNbwBAjZOdEZBWDbNlVV6hLNmwTZrVz072JgEAAAAAUKMMGzZMRo0aJb/88ov06tWrwqSVRx11VFK2i4A3AKDGTlxpAt7rt0rf9o2TvTkAAAAAgJqEkiZy/vnnm6644YYbKnSPlhYNBoPpXdJk1apVUXcuXsuWLZPTTjtNmjVrJnXq1DHfFkybNs3z9QAAaubElVrHGwBqggULFsi1114rJ598sqxevdrc9+GHH8r//ve/ZG8aAABA7Q14x3tLY6FQqNJbsoLdnmZ4r1y5UiZMmCDXXXedV4uUDRs2yD777CODBw82g/kWLVrIvHnzpEmTJp6tAwBQsyeuXLJha7I3BQDi9uWXX8rhhx9uxsaTJ0+Wm2++WVq2bGlqJj7++OPy2muvpVQvFzpBKXDi+wQXsJlw2GUVfotPkVbr8UjQok9CLtscTOAnY7dtUcVOYrY3YFHj1GZdNsvxglf7HbJoU+zEv0/BBNaQDXmwvcrv0jtuj3u5vY5HbdKNE0rUPlm8/i1ecqGAR+9HAW/OLVc2/WtzXvlc9jvHYn8sus4pcd8WX7F73q3jj70yx21/gKoEvGfOnBnz8V9//dXzDr399tulffv28uSTT5be16lTp5jPKSwsNLewvLw88zP87UIi6focx0n4emsS+pB+TBWci+nXj20a55ifWtKkpr0Pcz7Sh6mCczF233hpzJgxctNNN8no0aOlQYMGZSYKuv/++z1dFwAAAMTuC4d4v8yqAV+Gffnll/Kvf/1L5syZY37v0aOHXHHFFbLffvulfsC7b9++pvaKBirKC9+vP730zjvvyKGHHiojRowwnde2bVv5v//7PznnnHMqfc6tt95qMs3LW7NmjRQUFEiiP+hs2rTJ9I3f71n1mFqFPqQfUwXnYvr1YwNfkfm5eM3m0kv/awrOR/owVXAuVm7z5s2e9rVOBPTCCy9UuF+zvNeuXevpugAAAOBOQ6RxXtAW9/OT7bnnnpMzzzxTjj32WLn44ovNfd98840MGTJEnnrqKTnllFNSO+DdtGlTueOOO8wGR6O1A3VmTi8tXLhQHnroIZPJcvXVV8sPP/xgOi8rK0tGjhwZ9Tljx4417SMzvDVLXMuhNGzYUBL9IVC/BNB1E/CmD5OJc5E+rI3nYq/M+iLym6zaXCTNm+v60v+b8zBe0/RhquBcrFxOzvarTLzSuHFjWbFiRYWrHX/66SeTFAIAAAAk2s0332zixaNGjSq9T2O3d999t9x4442pH/DefffdZfny5dKhQ4eoj2/cuDFq9ne8H6L22GMPueWWW8zv/fr1k1mzZsnDDz9cacA7Ozvb3MrTwEoygs4a2EnWumsK+pB+TBWci+nVj20a15WA3ydFQUfWbimW3EbeBp+SjfORPkwVnIvRef0ed9JJJ8lVV10lr776qulzHSdr9szll18uZ5xxhqfrAgAAgAUvJp1M8wzvhQsXRk2APuqoo0zycrJYj8TPP/986dixY6WP77TTTmVqbXuhdevWpu5LpF133VX++OMPT9cDAKh5MgL+v+p4M3ElgDSnCSDdu3c3Vy7m5+ebMfL+++8vgwYNkmuvvTbZmwcAAFB7a3jHe0tj7du3l88++6zC/Z9++ql5LFmsM7yPOeaYmI83adKk0qzrHaWz0JefDPO3336rNMscAIBI7RrXlSXrt5mJK/fs2JTOAZC2tKTfY489JuPGjTNXPGrQW69+7NatW7I3DQAAALXUZZddZkqYzJgxwyRiKL0KUet333fffZU+b+bMmVVelyZ8ZGRkeBvwjkZ3QEuORCsh4gWt/6KdpRktJ5xwgnz//ffy6KOPmhsAAG7aN60jUxaKCXoDQE2gV1XqDQAAAMnlc7bf4l1GOrvgggskNzdX7rrrLnnllVdKq3O8/PLLcvTRR1f6vL59+5oyfbblsbVcoCZBd+7cufoD3ocffriJ4NuurKr23HNPefPNN81ElDfccIOZpOfee++VU089tVrWBwCoWdo3qWt+LqWkCYA0FDkRuxudGAgAAAAJRA3v0qogbpVBopk6daq0aNHCtZ0GxXv27FmlZccV8PZ6kspojjzySHMDAKCq2jfdHvCmhjeAdPTTTz+V+X369OlSUlIiu+yyi/lds1wCgYCZXD7VFItjbvEIWTw9KMGYjwd87nUx/R7VzgyI+3KCKTQzVchiW4IJ+LxnK5F95zbRVchiGUGPNrfY4vwsduKfJDdocf56pdgJuLYpcDJd2xS5LCdkMWVZ0IO+M+vy6H3ESfNavjsk6H4MnKBFvwTc2zgevTCL/LGXk5EZ+2/T9o3xZFP0j5hrE59LG1/A5l3Ngs/vzR/34lr4OkhjP/zwg5lMfcCAARWC2TpO1cog0RxwwAHStWtXady4sdV6dO6aOnXqWG+Xt9PHAwCQYiVNFCVNAKSjSZMmld6GDRtmPhgsXbrUBL71tmTJEhk8eLAcccQRyd5UAACA2odJK+XCCy80Y9Lyli1bZh6rjI5vbYPd6oMPPpDWrVsnJuD9yCOPSKtWreJZBAAA1abdnyVNVmzaJsVBjzIXACAJtC7irbfeaiaKD9N/33TTTeYxAAAAJKmkSby3NDZ79mzp379/hft1cnV9LFmqFPD+7rvvTIRerVixwtTUrlevXnVtGwAAcWlRP1uyMvzmyrkVGwvoTQBpKy8vT9asWVPhfr1v8+bNSdkmAAAA1G7Z2dmyatWqCvdr3DgjI65K2iZz/Kyzzqr+gPeWLVvksssuK51EZ9u2bTu0UgAAEsHv90m7JtvLmjBxJYB0phMBnXnmmfLGG2+YsiZ6e/311+Xss8+WY489NtmbBwAAUPuQ4S1Dhw6VsWPHyqZNm0q7ZePGjXL11VfLIYccElf3rl+/Xp5++ukdem6VQu1Dhgwxg+xrr71WmjZtKgcddNAOrRQAgERp36SuLFyzhYkrAaS1hx9+WC6//HI55ZRTpLi42NynWTMa8L7zzjuTvXkAAAC1jxclSdK8pMm//vUvM6Fkhw4dTBkTNWPGDFMC+9lnn4353HfeeSfm4wsXLtzh7bIOeOuEOD6fz1xOqZPk6Gzw4fs+//zzHd4AAACqUzjDm4krAaSzunXryoMPPmiC2wsWLDD3denShfKCAAAASJq2bdvKzJkz5fnnn5eff/5Z6tSpY65KPPnkkyUzMzPmc4cPH27iyo5TedRfH6/WgLfOnql0hk1NV9dU9QceeGCHVgoAQKK0b7p94solG7bS6QDSns6f07t372RvBgAAAByfmFs84n1+ioxPzz333Co/r3Xr1iah4+ijj476uGaKa8J1tZc0+eyzz2Tt2rUm0K2Res3spqwJACDVS5qopRuYdwJA+gpfWVkZrrgEAABILJ+z/RbvMmqr3XffXX788cdKA95u2d+eBbw1Lf2uu+4y/9afixcv3qGVAgCQKO2bhkuakOENIH317du3zO9ax1uzXmbNmiUjR46UVLMt5JNAqPIAvd+jZKaAS+HLTIvCmAFx3xi/xbaELC659Vusy3U9FvsU3MEPhzvCvW/c97nIYnsDCUyAC7k8HrQ4Bm7LUMUWWX0FTsCijfvH+qAH516xxXpsFFns01YnO+71bAm5L6PAiX25vSpx3M/yUIz3uzCrl6XbOeHRS9vnUYTNsTmv3FZlsS0+i/4VmzYW79M2ybahQOxzosR9ERIIuL9L+DKDrm0cv8VfKJc+9vk9Oh+2TzESe12F7tvrL3Q5CEXpnxGN7a644grZsmWLVKZr166lFUeqqkp/sTSyHs4sifw3AACpnuG9enOhvPbjEmnbuK7s1ampBLyKtgBAAtxzzz1R77/++uslPz+fYwAAAJBoTFoZl/3228+1VMoBBxywQ8u2SVYotXXrVrnsssvMv0ePHi3btnF5OAAgtX23cF1p7snlr86Ukx/7Tva9/XOZOGtFkrcMAOJ32mmnyRNPPEFXAgAA1CJabrpjx46Sk5MjAwYMkO+//15qghdffDFm1ne1BLyHDBkizZo1k2uvvVaaNm1K/W4AQErToPb/PT+9wpWUKzcVyAXPTSfoDSDtTZkyxXzQAQAAQO3w8ssvm0Tk8ePHy/Tp06VPnz5y6KGHyurVqyXdnXfeebJq1aq4l5NR1Yly8vLyTGdqYfHwfUySAwBINcGQIxPenR21bKDep1nf+vghPXIpbwIg5R177LFlftcJfFasWCHTpk2TcePGJW27AAAAaiv9TBn3pJV//tR4a6Ts7Gxzi+buu++Wc845R84880zz+8MPPyzvv/++uepvzJgxUt2aNGliXeZ6/fr1VVr2jk5SucMB73CR8AsvvFCGDh0qmzZtMunzAACkou8XrZcVmwoqfVz/jOrj2m5gl2YJ3TYAqKqGDRuW+WDh9/tll112kRtuuMGMzQEAAJBgOsupzUynbsvQuafaty9zt2Zv61wt5RUVFcmPP/4oY8eOLTMuPPjgg82Vf4lw7733lv573bp1ctNNN5kM84EDB5r7dDs++uijpCZlVGnSys8++0zWrl1rAt0nn3yyyew+6KCDqm/rAADYQas3F3jaDgCS6amnnuIAAAAA1FBLliwxCQ5hlWV3a1w2GAxKq1atytyvv8+dO1cSYeTIkaX/Pu6440wCxj//+c/S+y6++GK5//775dNPP5VRo0ZVadkffvihtG3bNrE1vOvUqSN33XWX+bf+pF4gACBVtWyQ42k7AEimzp07mwya8jZu3GgeAwAAQII5Ht3+vJov8lZZwDvVfPTRR3LYYYdVuF/v04C3mw8++KA0UD9v3jxTUcSLfa9SwHvQoEHSrl078+82bdqY37XGzFtvvSVz5syJe2MAAPDKXp2aSutGOaU10crT+/VxbQcAqW7x4sUmm6e8wsJCWbZsWVK2CQAAoFbzMOBtq3nz5hIIBCpM7Ki/5+bmSqI1a9ZM3n777Qr36336mJvWrVuXZoFfcsklnmR3V7mkiTrhhBNk//33N6nq27Ztkz322MMMwLWo+EsvvWRS2QEASLaA3yfjh/WQC56bboLbkeOIcBBcH9d2AJCq3nnnnTIZNI0aNSr9XQPgWnKwY8eOSdo6AAAAJFJWVpbsvvvuZgw4fPhwc18oFDK/R5YVSZQJEybIP/7xD/niiy9kwIAB5r6pU6fKxIkT5bHHHnN9fr9+/WSvvfaS008/3fzs27dvcgLekydPlmuuucb8+8033zSBbr2U8umnnzZFygl4AwBSxWE9W8tDp/WXCe/OLjOBZW6jHBPs1scBIJWFP8johJWR9RJVZmamCXaHSw6mki1OhvicGBeTWmQzZfpCrm0CLgsKWqzIbRnbt8W1idU+2WyP+157s55ECVpsSnGl12P9JeQ4nly+7EXfFFpMUFbgBCzauH8cL7JYzlYny7WN+3rct6XYYluCFkchFOu94U8FTqbEy2Z7C0Pu6ykIuveNY3FO2LRxZbEIn9+b17/N9vosXk+O20ZbvHAtThkrFn9WxGfRfU4o9gY5Fm98oYiJqCvfGIsmGfH/1bA5Z4JF7q8nKXY/UP4ii/Mq6Ivr8UTS88XmnHFbRlWNHj3ajAs1CVmDxDqJ5JYtW+TMM8+URPv73/8uu+66q/z73/+WN954w9ynv3/99delAfDKDB482IxxN2zYID///LMJdn/55ZfmPp03MqEBb62l0rTp9su/NVqvAe66devKEUccIVdccUVcGwMAgNc0qH1Ij1y58rWf5fXpy2T/nZvLk3/fi8xuAGlBM3ZUp06d5IcffjCXsQIAACAF7EBJkqjLqKITTzxR1qxZI9ddd52sXLnSBIo1Rlt+IstE0cD2888/X+XnTZo0qXR//u///s9kqWv1EC9UOeDdvn17mTJligl6a2eGN0Sj8UxiCQBIRVq2RIPeGvBel19EsBtA2lm0aFGyNwEAAAApQsuXJKOESWUJGvPnz5fVq1eXJmuEaVnsWF5++WUTYz7nnHNkxowZ5ncNgCc84H3ppZfKqaeeKvXr15cOHTrIgQceWFrqpFevXnFvEAAA1aFH64bm57xV+VISDElGwKNrIwGgmuiloeeee65JKtF/x3LxxRdzHAAAAGpBhncq+e677+SUU06R33//3ZS9jqSlSaJNuh6pf//+MnToUPPvm2++2QTNvVDlgLemmGuq+h9//CGHHHKI+P3bAwadO3c2NbwBAEhF7ZrUkXpZAdlSFJSFa7fIzq0aJHuTACCme+65xySaaMBb/10Z/TBBwBsAAKB21PBOJeeff76pJf7+++9L69atzbi0Kn777TcTFG/SpIkp0zJv3jzZeeedEx/wVjobqN4iaQ1vAABSld/vk+6tG8qPv2+QOSvyCHgDSKsyJpQ0AQAAQKqZN2+evPbaa9K1a9cden6bNm1k1KhR8uGHH8oll1wit9xyiyfbxfXcAIBao3vu9qzuOSs2J3tTAKBKbrjhBtm6dWuF+7dt22YeAwAAQII5Pm9uaWzAgAGmfveO6tevn+y1115y+umnm586AacXdijDGwCAdLTrn3W8567MS/amAECVTJgwwVwyWrdu3TL3axBcH7vuuuvoUQAAgESihrdcdNFFctlll8nKlSvN3I6ZmZlluqh3796Vdt/gwYNNCZQNGzbIzz//bILdX375pbnv888/j+vQEPAGANQau7YOZ3gT8AaQXnQSoGg1EfXDgc5sDwAAACTacccdZ36eddZZpffpmDU8do01aeWkSZPMzxNPPNHMGfnZZ5/JSy+95Ml2EfAGANQau+Ruz/BelVco67cUSdN6WcneJACISSfw0Q8LetMJfCKD3voBIj8/32R+p5pCJ0MynPiqJwYl5Nom4NLGZhlZFm0q/6j2l2KT5uXSxuKy5aD44nrcSwGLfXLj1fbabItNmyKLqp7FLudugeP+MXpjqOzVGFGXE8q02Bb3dRU5Adc2IZf9LrZYhs32+i1mXwtZvQ7cj1OOrzjuZdicnyUh9+WUBFOoWqxHbxE+i2PphNxX5veH4l6GVeTK4ryyqRzhs9meoC+u15stf0bIm+PksuOhEovttWlTbNF3Fn9W3HYplSZ5ZNJKiXuemZdfftmMd8855xyZMWOG+V0D4EkJeBcUFMjMmTNl9erVEgqVfQEeddRRcW8UAADVoX52huzUtK78sX6rzF2RJ4O6NqejAaS0e++912TIaNaMli5p1KhR6WNZWVnSsWNHGThwYFK3EQAAoFaipIl06NAhri7s37+/7L///qZM380332xizb///ru8+eab0qNHDxk6dGhiAt4TJ06UM844Q9auXVvhMbdUdQAAUmHiSg14z1m5mYA3gJQ3cuRI87NTp04yaNCgCnURAQAAgER655135PDDDzfjUv13LG6J0d26dTNB7WOPPbb0qsXu3bubZWvs+e6775YLLrig+gPeWox8xIgRZmKcVq1aVXmFAAAke+LKj2evMhneAJAuDjjggDJXWxYVFZV5vGHD7SWbAAAAkCCOByVWUqhEi63hw4ebSSpbtmxp/l0Z28To6dOnyz333GP+/dprr5l4808//SSvv/66iT8nJOC9atUqGT16NMFuAEB6T1y5koA3gPShl3leeeWV8sorr8i6desqPM5VlgAAAAlWS0uahCLKW5cvdb2j49wGDbZ/Tv/4449Ntrff75e9997blDfZEVWupH/88cfLF198sUMrAwAgFTK81W+r8qUkGP8fZwBIhCuuuEI+//xzeeihhyQ7O1v++9//mprebdq0kWeeeYaDAAAAgIQrKCiIexldu3aVt956S5YsWSIfffRRad1uree9o1cxVjnD+/777zclTb766ivp1atXhTqCF1988Q5tCAAAidC+SV2plxWQLUVBWbR2i3Rrtf2bZABIZe+++64JbB944IFy5plnyn777Wc+HOhEQc8//7yceuqpyd5EAACA2qWWZnhHaty4sey1116m/J6OU3XOmTp16khVaNmSU045RUaNGiVDhgwpnZBds7379esnCQl4v/jii2aFOTk5JtNb67GE6b8JeAMAUpnf75NdchvI9D82mokrCXgDSAfr16+Xzp07m39rpov+rvbdd98dqmsIAACA+Pg8qOEddw3wJPv0009l8uTJJkasdbhLSkpkjz32KA2AH3LIIVbVRHRMu2LFCunTp0/p/Rr8PuaYYxJT0uSaa64xl09u2rRJFi9eLIsWLSq9LVy4cIc2AgCAROr+Z1mTOUxcCSBNaLBbx9vhmeu1lnc487tRo0ZJ3joAAADURvvuu69cffXVJjl648aNMmnSJHMV4h133CGHHXaY9XJyc3NNNrfW7g7TzHEd9yYkw1tnhD/xxBPLbAAAAOlYx3suAW8AaULLmPz8888mW2bMmDEybNgwU2qwuLhY7r77bkk1W0JZIqFAXMvI8gVd2/gl9lwMOf4S12XYVJ4Mhf66qrUyRY77/oYs8o3cllMs7uspdjLi7jsV8Lm3yfEVx3w8U4KerCdgcc23zTEocMqW5IxmY6huzMfXl9R3XcbaEveSacUW25tKii1e05l+9+OdafPatjgnin2xtyfkuL/e8ktyXNsUBt1fTyHH/T3CsXgf8aS0gcVqIi7Uj9HI8WQ5jttirLbFoo1F5/n8FgvyWxyEQOw2PptlWGxKIODNfENucwo6QYvz12aXLN7SnEz3Bbm9VEKhNE+JroF+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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Get the first variable name from the dataset\n", + "first_var = list(ds.data_vars)[0]\n", + "print(f\"Plotting: {first_var}\")\n", + "\n", + "# Plot the first variable for the first time step\n", + "fig = plot_era5_variable(ds, first_var, time_index=0)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Plot All Variables\n", + "\n", + "Now let's create a comprehensive view of all variables for the first day:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABvcAAAX/CAYAAACXUqXaAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3QeYE9XawPE3ye7SpIgKgiKCYkUUu4hiQcXerlcUy7XgtaKiWBEb9obYsKJeC9gudkVRrNixd0VFBVFRERB2N5nveY/f5GaXeYedMNlsdv8/njxsJjPnnJlMMm/OmXNOwvM8TwAAAAAAAAAAAAA0eMliFwAAAAAAAAAAAABA3dC4BwAAAAAAAAAAAJQIGvcAAAAAAAAAAACAEkHjHgAAAAAAAAAAAFAiaNwDAAAAAAAAAAAASgSNewAAAAAAAAAAAECJoHEPAAAAAAAAAAAAKBE07gEAAAAAAAAAAAAlgsY9AAAAAAAAAAAAoETQuAcAAAA0QZMnT5ZEIpF9fPPNN9nX/vWvf2WXb7XVVlKqzjnnnOx+rLzyykucnh4LPz09RnWlefvbaZkAAAAAAFgSNO4BAAAAjUTPnj1rNNh16tRJqqurC57vkUcemc2zrKxMZsyYYa679dZbZ9ft0KGDVFVVFbx8iN9TTz0lp556qmyxxRbSrVs3admypbRq1UrWWGMNOfbYY2XatGnmtnp+HH/88dKjRw9p0aKFLL300tK3b1+56aabJJ1Ox5pXriFDhtT4fCxJg+8zzzwju+22mzuHKyoqZIUVVpCBAwfKW2+9Za6v+9ynTx9XfqthvdTLAgAAAKB+lNVTPgAAAAAK6M0335SPPvqoxrKZM2e6hpFddtmloMdee7HdeOON7m9tnLnnnnvkpJNOWmS97777Tl544YXs80GDBkl5eXnByrX99tvLUkst5f5u27ZtwfJpivbYYw9ZuHDhIss/++wz9xg7dqw8/vjji/T81AanHXbYQWbPnp1dtmDBAnnllVfc46GHHpIJEyZI8+bNlzivXHreXXvttRKHESNGyPnnn19j2Y8//ijjx4+X+++/330WDj/88BqvX3fddfLwww/Hkn9DLQsAAACA+kPjHgAAANAI3H777ebyQjfubbrppq4X1aeffuqe33nnnYGNe3fddZd4npd9HmVoyyjmzJkjbdq0cT2T9IHCSCaTsuWWW7pjnEql5LHHHpOpU6e61+bPny8HH3yw61Wn66m5c+fKP/7xj2zDXpcuXeSQQw5xjdC33nqraxh++umn5ayzzpLLLrtsifLKNW/ePDn00ENrnHv5evTRR2s0pg0YMMD1OtTGxSlTpkgmk5GjjjpKNtxwQ1lvvfWy62mvuBVXXNEt1/3UdBpTWQAAAADUL4blBAAAAEqc9mq69957s89XW2217N9acf/rr78WvAy5DXXvv/++e9T2n//8J/t37969Zd1113XDhmpjzk477SSrrLKKtGvXzvXmW2aZZdwwjNdcc80iQ3fq0IG5Qwnq/IHaOLT++uu7YR61EWhxc+7997//lQMPPFB69eolHTt2dMMZai+/tdZayw31WJfhCb/66iv55z//6cqqwxtqw8qzzz4b+di99957rvFJ91/Lr+XQ43PhhRe6hqnavv32W/n3v/+dHdZSe7npUIybb765DB06VD755BOpj/f7iy++kOeff14uuOACOe+881yvPB12NbenZm5vUm301bIrfU90eMhzzz3X9S7TYTd92sPut99+W6K8cmnaX3/9tWvw3WabbZZov3Mb0/R4P/nkk3LmmWe6c1CHDFV6Tl900UU1ttPerNOnT3fn3V577bVEZWiIZQEAAABQv2jcAwAAAEqcDrGX2xhyxx13ZIe7rKysdJX5haYNZdqjKqghT2ljjN+zL7cxUIdkHDlypGuY0AaYP/74wzVIaO+ul19+2c2Tpg1/QXOx5Q5NqEMPak8uTa8u7r77bteT8IMPPpBZs2a5BkRtSNOGMR22UBse9TXL559/LhtvvLEb+lDL+tdff7lhJXXISV1WVzfccIPrQaVDS+r+a/m1HO+++65rqNE8tGebT8u60UYbufnpvvzyS7e+Nu7qUIyvvvqqXHXVVTWGPi2UMWPGSPfu3Wss015ztRuL9PzzPfLIIzXmh1x99dWzz/fee+/s37pPEydOXKK8fNogeP3117u/9dhob8F86fugw9/6cvPXxuHcHrLae057zvm0ETZODaksAAAAAOofw3ICAAAAjWhITu29psNk9u/f3zWY+a8fd9xxBS1D586dZbvttnNz/CltULz44ouzDX7aa8unDY/7779/tgeXNtxombX32dJLL+0a2rQhUBvJtKFPe8M9+OCDrpdckJdeekm6du3qGoi0B502gC2O9hDUOfnWXHNNl6c2iPz000+uN5P2AtOhPbXH1xNPPBG4vQ57qPus6/z555+u56A2smkjyhFHHOHSXtw8f9oYp70E/YYXPQY6tKKmpw20v/zyi3z88cdy0EEHZRu79Dj8/PPP7m8ttw5rqT0HtXFPj5kei2LKbcBt3bq1G67Vl9ubs3ZjXe3nuu6+++6bd17+MKD+cJzaQKx/v/jii3ns1aLlX9w+aAOt9uzU3pWF0JDKAgAAAKD+0bgHAAAAlLAZM2bU6OW03377Zf/3G/feeecd1wttnXXWKWhZtKHJb9zTxqZJkya5Ri5toBs3blx2Pe1VtOyyy7q/W7Vq5RoetEHutddekx9++MHNoaaNlFrmDz/80K2nc7FZjXs6BKHuozbY1dUtt9ziGhE1Tx3yURvzdB6ybbfd1vWiU88995xbx+8FmUuXaU89f7hPHRZx0KBB7u/ff//dNUxqb8Iwl19+ebZhb6uttnLHy58zThu2tNee0uErtTFHhxDN7Zmox+OKK66okaY25GijVjFow6L2KPTpvIv6/vr8ufaUDpGZSxvnci1uKNnF5aWGDRvmhlfV8+Lmm2+WJZVb/rruQ6Ea1BpSWQAAAADUPxr3AAAAgBKmw1/6Q1ZqLzi/t9Mee+zh5mLzG4O0werKK68saFl2331315vMHyJUy6aNe9rg5/c28xsBfTqc5dFHH+169uUOHVjb999/b752zDHHRGrY84flPOGEE1zvOIv2xNPXO3XqtMhrOh9g7jx+etx1qFF/fsC33357sY172jjo03nScoc1Derlp4172oio77P2RtO56nRoRp0nUIe41OE9dR46nUNwcXTOtfHjxy+yXIetXFyPuSA65Kb2xvT3f+DAgW4uRYuWP+z5kualDaV6fNTVV1/telnGfUyWZB9KqSwAAAAAGh4a9wAAAIBGMiRnnz59snOKac+dnXfe2Q3j6DdmXXrppVJWVrifAM2aNXMNLTqPnNIhLrUXWe6QnNrwtOOOO2afn3766TX2IayhzVJ7OMbF0V5+OtRlWGPi4vLt0KFDjefaMKfDY/rz42nvvai9r8L4jaPam08babUxS4+t7os+fNojUnsNak/AMNpbUnu21davX7/IjXs6l93JJ5+cPZ46/KX2qvN7Ifr84UOVDj2aq/Zzv2dnvnlpg7E2cO22227uva6LxR0TLX9Ymeu6D6VWFgAAAAAND417AAAAQIl6/fXX5ZNPPqnRE0x7dQXRYS91/jht7Cgk7b3mN+7pEJG33XabPProo9nXdejK3AbG3N5JOmzovffe63qh6To67KQ2VC1O7eEYF0fT9BuH9Hjp/IC77rqrS0ePkTaKLk7tef2092TuUJJ16UnYvn37bDp9+/Z1PR8t2nDr0x6HOq+fDin60UcfuWFFtXek/q89DQ8++GD59ttvpdB0n3UuR//9Vueee66MGDEicH3teeg37n399deLNGblqj2EbNS8dP5Ev5ef9ZnQY6Sv6XyNOnzn4mj5c4Xtg55LtefBi1NDKgsAAACA+kfjHgAAAFCi6tLjrfb6hW7c055lOkzkxx9/nO2ZlztPXO6QnCq3QUyHlFx77bWzPdV0qMpCyM2zbdu2rhHR7/l133331SkNnfNNG4T8oTm1kdIfJlJtsMEGi01DG+wmTJjg/tYef9pgV3vuNB22VBsj/cY9bRzTXoLaA3KbbbZxDzV16lQ3T6H67rvv3D7W7t2VS3v2LcnQjTpHoR43nQtRVVRUuIZcf97BIHru+XMy6lyKn332mWvIVbmNuDqcrA7nuiR55WNxx0SPuZ7fb7zxhnv+0EMPydChQ7M9PHMbsXVeydq9CUu1LAAAAAAaHhr3AAAAgBKkDWbjxo3LPu/WrZur7K/tgw8+yDa0PfbYY65nV6GH6NPee6eccor7e/78+TUavHr27FljXW3c0YYedfPNN7tGiJYtW7r5+nLn6YuT36DkD5+pPfW08ezll1+WiRMn1ikNbcjT+e8OPPBANwTirbfeWqPBcJ999llsGieddJI8/PDDrhHnyy+/dMdmr732cg03f/zxh3vvXnjhBdcD0h9a8sUXX3SNWtrTb80113RzyWmvNm3c8Wnjlx7DQtJ99983NWDAAJkxY4ZcfvnlNdbTIVj9Blvdh0suucT1mNN91gY8bezVBsvc43fssce6uRuXJC/tBanHrba33nor26tRj5FuU3uI1TA6HKr28vR7yur2+l7oZ0sbVZX2Oj3ttNNqbKeNvzo/otLelrkuvPDCbKOuDmurcyeWWlkAAAAA1DMPAAAAQMm59957tVtP9nHXXXcFrjdp0qQa640aNcotf/7552ssnzZtWnabgw8+OLu8X79+kcs2Y8YML5VK1UhfH9dcc81i98N/dOrUydtuu+0Cy6FlzV1X9yXI2WefnV2na9eu2eW//vqr17lz58B8c/e99nHRMvjL119/fa9169aLbJ9MJt0+5dK8/de1TLmuu+46r6ysLLAsuY/FHa/cx9ChQ71CW1wZ/MfYsWNrbPfmm296Sy+9tLn+9ttv7/3111+x5BUk9/3NPSeiOOuss8wy6Pt/8803h+a7pPvQUMsCAAAAoP4wNgcAAABQ4kNyak8x7fEVRIe69IeOrL1doSy//PKud1Uu7U22//77L7Ku9g7SoTDXXXddKS8vd0NJ7rvvvm4+Oe2VVgg615320tNjpr2UWrRoIRtttJHr/aa9DutC54TTIRH33HNP18tM09Defzpnn+5TXR199NFuSE0dknO11VZzvcm0t5X23uvXr5/rnfXee+9l19eeWRdccIHrbbjKKqtI69at3frLLbecbLvttu79veKKK6Sh0p5g2gtP58/T8jdr1sy9B5tttpmMGTPGHT8dlrMhO++881wPT30PtBesnredOnVyQ4fqPJiHH354kywLAAAAgPqT0Ba+eswPAAAAAAAAAAAAQJ7ouQcAAAAAAAAAAACUCBr3AAAAAAAAAAAAgBJB4x4AAAAAAAAAAABQImjcAwAAAAAAAAAAAEoEjXsAAAAAAAAAAABAiaBxDwAAAAAAAAAAACgRNO4BAAAAAAAAAAAAJYLGPQAAAAAAAAAAAKBE0LgHAAAAAAAAAAAAlAga9wAAAAAAAAAAAIASQeMeAAAAAAAAAAAAUCJo3AMAAAAAAAAAAABKBI17AAAAAAAAAAAAQImgcQ8AAAAAAAAAAAAoETTuAQAAAAAAAAAAACWCxj0AAAAAAAAAAACgRNC4BwAAAAAAAAAAAJQIGvcAAAAAAAAAAACAEkHjHgAAAAAAAAAAAFAiaNwDAAAAAAAAAAAASgSNewAAAAAAAAAAAECJoHEPAAAAAAAAAAAAKBE07gEAAAAAAAAAAAAlgsY9AAAAAAAAAAAAoETQuAcAAAAAAAAAAACUCBr3AAAAAAAAAAAAgBJB4x4AAAAAAAAAAABQImjcAwAAAAAAAAAAAEoEjXsAAAAAAAAAAABAiaBxDwAAAAAAAAAAACgRNO4BAAAAAAAAAAAAJYLGPQAAAAAAAAAAAKBE0LgHAAAAAAAAAAAAlAga9wAAAAAAAAAAAIASQeMeAAAAAAAAAAAAUCJo3AMAAAAAAAAAAABKBI17AAAAAAAAAAAAQImgcQ8AAAAAAAAAAAAoETTuAQAAAAAAAAAAACWCxj0AAAAAAAAAAACgRNC4BwAAAAAAAAAAAJQIGvcAAAAAAAAAAACAEkHjHgAAAAAAAAAAAFAiaNwDAAAAAAAAAAAASgSNewAAAAAAAAAAAECJoHEPAAAAAAAAAAAAKBE07gEAAAAAAAAAAAAlgsY9AAAAAAAAAAAAoETQuAcAAAAAAAAAAACUCBr3AAAAAAAAAAAAgBJB4x4AAAAAAAAAAABQImjcAwAAAAAAAAAAAEoEjXsAAAAAAAAAAABAiaBxDwAAAAAAAAAAACgRNO4BAAAAAAAAAAAAJYLGPQAAAAAAAAAAAKBE0LgHAAAAAAAAAAAAlAga9wAAAAAAAAAAAIASQeMeAAAAAAAAAAAAUCJo3AMAAAAAAAAAAABKBI17AAAAAAAAAAAAQImgcQ8AAAAAAAAAAAAoETTuAQAAAAAAAAAAACWCxj3EauWVV5ZEIiHnnHNOkzmyTW1/Ubo8z5Nhw4ZJ586dJZlMunP3m2++iT0fTVcft99++xKnpWn46RWirAAAFIte1+K6XjZFuTECAACFQB0X6sPkyZOLXu/xr3/9y+W/1VZbFSV/APmhca+B/YD0v0zDHg35i7Z3796yySabyIorrliyQVUh8vnoo49kzz33lBVWWCH7Pp522mmB615zzTWy1lprSbNmzaRDhw5y6KGHyk8//RRbWdCwFfI8nzBhglx++eUyY8YMWWONNdxnVc+zuGm6+lhuueViTxsAGhNiS2Dx9LePxkb6OymXxhl+zAEAIA4pBOq4AAANWVmxC4CaVllllewP1Dlz5sgnn3zi/u7evXu2olwbfhqq//73v8UuQoP0xRdfyMMPPyyrr766/Pjjj+Z6Z511lowcOdL93aNHD/n+++9l7NixMmXKFHn77belZcuW9VhqNDbayKw6deokH3/8ccHyee211wqWNgCgacWWaJwjCVRXV0t5efkSpbPzzju7BwCg4Sr1OIQ6LiB+lZWVUlFR0WRiVqCgPGQ9/vjj3qabbuq1bdvWa9GihbfKKqt4//znP73Zs2d7Bx98sKeHq/bj7LPPdtsuWLDAGzFihLfqqqt65eXl3nLLLecdcsgh3s8//5xNX9fVbbp27erdf//93mqrreY1a9bM22yzzbz3339/kXfi+eefz+YzduxYt+yWW25xz1u2bOn98ccf2XWHDh3qlq+++uruuV/efv36eddee6230korubwGDBjgfffddzXy+c9//uNtuOGGbp+XWmopb4cddvCmTp1aYx0ts6an6Ybx1/OPS+4+TJgwwdtiiy285s2bu3I++uij2e3+/PNP78gjj/RWXHFFr6Kiwlt22WW9Pn36eLfffrs3bdq0wGPvn75a1m222cZbfvnl3bZ6bHR/dL9y+dtcfvnl3qBBg9y+du7c2Tv//PPd64vLx5K7v5bff/89+375aZ566qk11pk5c6Y7d/S1k046yS177733vEQi4ZZdccUVbpmeC34a9913n7feeuu5Y6rv7axZs7ybbrrJHcf27dt7Rx11lFdZWRlattrHxz/XlJ4/dXnffXPmzHFl7969u9sXLYOeT/Pnz3evV1dXu+O/5ppruveqTZs2Xv/+/b0XX3wxm0buOXPzzTdnzxn9nHz55ZfuPOrRo4fbdt99963xOfDPPz22xxxzjLf00ku79fQ46GfUp+U544wz3Gdcy6nr7b777jU+h7nH+bnnnvN69+7tyqH/T5kypcZ+v/baa96OO+7ovjv0c6br6Ge8Ps4/30svveRtv/32bn/12K6xxhrepZde6o557nsZJW09/rpO69ats+nofuqyYcOGuedfffVVNi3/uNQ+l6Icy9GjR7vjop/j/fff3xs1alR2Wz1GAFBKiC2XPLa88sorvXXXXdddq8vKylyMuOeee3qfffZZjfU+//xzb7/99vM6duzoru0rrLBCNp6qS4xSO4atHU/7/GvSySef7B100EHueqXxhL7Xn3zyibf55pu7ZRq3fPzxx6H75l/7c2OvIOl02l0P1157bRdntGvXzvvHP/7hff311+7177//3ksmk9l42/fss89my6tlmz59uruOa5yo12F9aJpXXXWVl8lkCh5PPfnkk95aa63lpVIpF+/VJYa3YiM9drlp57rtttu89ddf3+2fpqm/KXKPS27MpWnsvPPO7nfQyiuv7H5rAUBjQRxCHVeh6rj8+gX9/+KLL3Z1oMsss4x34YUXujqaAw44wGvVqpWrI/3vf/9bY1uNSTSO0ZhO4wetu7j++usjx39R6hksM2bMcPUOfiyiceTWW2/tPju+H374wdXvdurUyZW3W7du3nnnnedVVVVl17nrrru8jTbayB0D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GTNmSKtWrUK3feONN7J/9+vXz0343rlzZ/nrr78C559aeumlZYcddnB/6//6XH3wwQd1KutRRx3l/n/99dfdvFkPPvige7711ltLly5daqy7zjrruH1T++23X3a55qXzb82fP989P/vss125dRzht956a5FyT5o0ST799FO56KKLJF8HHnjgIuOp//TTT+7/XXfdNTvnXNeuXd1xueaaa6Rjx46LTVfLfdJJJ7ljXlZWJi1atJAvv/zSvRY0v90///lPN7bzsssu697r3HI0RH9fc4L5x23llVdeZJmez/W5b3o+qmbNmsnQoUOzy/X80+Otcwb6+7L//vu7/9u2bSs77bST+9s/7+LYv1122UVat27t/h44cGB2TO/PP/9c3nzzzex6fjlWXHFF2WKLLcxyhJ27/uf/mWeecZ8fPR9HjRrllumciT/88EPBzz9/n/RYtmvXrsa+6THXY58v/U5Tr7zyinuoI4880h1P3feXX37ZLdtqq63qlF7YsfTnBNTP/1JLLSWpVEr22muvvMsOAMVEbBlPbPntt9+6GLdNmzaSTCZlu+22y77mx3l+DKLXrD59+mRf7927d51ilHxsv/32Lr3cGEXnf9N99mMUNWvWLFkSeuz8+Onggw926WuMozFG7jHdc8893TGaN2+ePPbYY/Lss8/Kr7/+6srox0IaJ1966aUu1tb3Ra+zL774ohkzxx1PnXDCCdm/Ne+oMfziaBzh//7S8ur5ovu/9957Z8+ln3/+ucY2gwYNcuUIiksAoJQRh1DHVR969uzpYiGtN/XnUOzbt6+rl8iNh2rXn3z44YduG70G+/GBXsPff//9Osd/da1nCOPXL2mMteqqq7rY56677nKxSW55NS2tv9Hy7rHHHm6Zxmd+jPnbb7/J7rvvLu3bt3fl7dGjR2h5F0fjlW+++cb9fdttt7k09eHPAennq7HXNtts4/4eN26c/PHHH/L000+751qnnbsPGsOtttpqbh/WW2+9bF6166yj+sc//uHiSn3o3/7xqh1zaT1S8+bNs3FgXY7tMsssI5tttplbpu+P1nFr3fbUqVNdnVpd3sN81I5Zo9b7AfWprF5za8D0C0YrwP/zn/9kG8307zvvvFPuu+8+2WeffeqUjj9RcK7ll18+1rKuscYa7iL3/PPPy9ixY+WRRx7JfpHla80113QXzVz6JRon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l//cz8NaJ/f1upaloaDnHgAAeU9EHN8DAAAA8cZYAAAAiL8eS2nDXu4jn8Y9HUpTG9UmTZqUXabDfb7++uuy2Wab1eixt9VWW8kGG2wgY8eOlWStoaI322wzefHFF6Wqqiq77JlnnpHVV1/dDcnpr5Obj7+On09dy9KQ0LgHAAAAAAAAAACAWM2dO1feffdd91DTpk1zf3/33XeSSCTkhBNOkJEjR8ojjzwiH3zwgRx00EHSuXNn2WOPPWo07K200kpunr2ff/7ZzYE3M2cevP33318qKirksMMOk48++kjGjx8vV199tQwdOjS7zvHHHy9PPfWUXHHFFfLpp5/KOeecI2+99ZYce+yx7vW6lKWhYVhOAADykJGkpGO6RyYjDMsJAAAQZ4xFfAUAAFD8GEsb0HQ4TZ/f4HbwwQfL7bffLqeccorMmzdPjjjiCPn999+lb9++rhGuefPm2d51X375pXusuOKKNdL2/n+IUJ3zb+LEiXLMMce43n3LLrusjBgxwqXp0+E877nnHhk+fLicccYZ0qNHD5kwYYL07Nkzu87iytLQ0HMPAIAlGKs8rgcAAADijbHycd1118nKK6/sKnE22WQTeeONN0LXv//++2WNNdZw66+zzjryxBNPLFLppJVLOndLixYtpH///vLFF1/UWGf27NkyaNAgN6RVu3bt3F3nepe7b/LkybL77ru7NFq1auXmfbn77rs5XQAAQIOPsbTXncZDtR/asOf3mDvvvPNcT7wFCxbIs88+K6uttlp2+3/961+B23v/37Dn69Wrl7z00ksuje+//15OPfXURcqyzz77yGeffSYLFy6UDz/8UHbaaacary+uLA0NtYkAAAAAAKDJ0yGc9G7ys88+W9555x1Zd911ZYcddpBZs2YFHptXX31V9ttvP9cYN3XqVDdkkz60ssh36aWXyujRo2XMmDFuzhZtnNM0tcLIpw17OoSU3pn+2GOPuTljcu8013y0wurBBx+U999/Xw455BA3TJSuCwAAgKaJxj0AAPIcziDOBwAAAOKNsaK68sorZfDgwa7xbK211nINci1btpTbbrstcH2dy2XAgAEybNgwWXPNNeX888+X9ddfX6699lr3ut5RPmrUKDf8k/a80wa6O++8U3788Uc3DJT65JNP3HBPt9xyi+spqMM/XXPNNTJu3Di3ntKhozRtHU5q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bBivD9pgt9pqqxW7GAAAoBGKK8YqtfgKAACgkIix6teCBQuksrJmJ5w2bdrEmgeNewAA5CHO4Z5KbdgoHXIgqriGHAAAAI1bXDFWqcVXAAAAhUSMVXjz58+XU045Re677z759ddfF3k9nQ4d8iYy5twDAACRhxyI+mjWzBjPDgAAAAAAAI3Siy++KLvuuqt07tzZTWs0YcKERUaHGjFihHTq1ElatGgh/fv3ly+++KLGOhdccIH06dNHWrZsKe3atQvMJ5FILPIYN25cjXUmT54s66+/vqujWnXVVeX2229fJJ3rrrtOVl55ZWnevLlssskm8sYbb9R5X4cNGybPPfec3HDDDS6PW265Rc4991y373feeafEjcY9AACKPBFxlDvLzznnnEWClTXWWIP3EAAANArFiK8AAAAau2LFWPPmzZN1113XNZoFufTSS2X06NEyZswYef3116VVq1ayww47uGEtfTq85T777CNHHXVUaF5jx46VGTNmZB977LFH9rVp06bJzjvvLFtvvbW8++67csIJJ8jhhx8uTz/9dHad8ePHy9ChQ+Xss8+Wd955x5VbyzJr1qw67eujjz4q119/vey9995SVlYmW2yxhQwfPlwuvPBCufvuuyVuDMsJAECJWXvtteXZZ5/NPteAAQAAAAAAAGhIdtxxR/cIor32Ro0a5RrAdt99d7dMe7h17NjR9fAbOHCgW6a931RQT7tc2qtv+eWXlyDaeNitWze54oor3PM111xTXn75ZbnqqqtcA5668sorZfDgwXLIIYdkt3n88cfltttuk9NOO00WZ/bs2dK9e/fs/Hr6XPXt23exDZP5oOceAAAl1HPPb8zTYMV/LLvssryHAACgUaDnHgAAQNOIsbQ33cyZM91QnL62bdu64TCnTJkSOb1jjjnG1ZFtvPHGrkFOGw99ml5uPkob9fx8tHfg22+/XWOdZDLpnte1LNqwp/ukdJQtnXvP79FnDSe6JLjVHwCAPMQZ0ERNR8ce1/G6dfzvzTbbTC666CJZaaWVYikLAABAY4ixGJYTAACgcDHWnDlzaizXOeb0EYU27CntqZdLn/uv1dV5550n22yzjZuXb+LEiXL00UfL3LlzZciQIdm8gvLR/fjrr7/kt99+k3Q6HbjOp59+WqcyaI+/9957T/r16+d6+ulcg9dee61UVVW5XoFxo3EP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Ribjnh977LGB22y22Wbu9RNOOCG77JlnnnHLVbdu3VwDna7jN+bpnd06l95RRx2VTUN7yOl8f5q/eu6551zeOjdffdN5Anv37r3Icm0UnTfP/k2JeKW1DTqgHdr6aFjfJVZcFBYvWdecvNJKREwr6nUw7JhY2xi/t+PcDzOPPK4f9rUo5Ms9atyQT3mNbay6nMjruxeNbay0QupSrNes+hczj5Djbu56MhNp3636pXzSShnrJ0OCg6jlCjsm1jZWuVLGj6PQY2K8VpEM/lFRZuRtLQ9jHccyI29LKhVcL9zQYyz8r8FutdVWk2Jo0o17AADkK8658kp5zj2tAAIAAGhoMVY+aehwlAcffLDrpbbxxhu7YSi1QeuQQw5xrx900EGywgorZIf2PP7446Vfv35yxRVXyM477yzjxo2Tt956y9385N/opA1/I0eOlB49erjGPh3xQOey8xsQ11xzTRkwYIAMHjxYxowZI1VVVa4xceDAgW4938cffyyVlZWuR9+ff/4p7777rlvuNxrGRcuoaXft2rXG8qeeesqVFQAAlKZixliN2fPPPx95G40n40DjHgAAWGJa2aQNfaussoqUlRFeAACA0rPvvvu6YZVGjBghM2fOdA1n2qjVsWNH9/p3330nyeT/usT06dNH7rnnHhk+fLicccYZrgFvwoQJ0rNnz+w6p5xyimsgPOKII1wPvb59+7o0mzdvnl3n7rvvdg162267rUt/7733ltGjR9com86B9+2332af+73r9G7xOGkD5zHHHCMLFixwab/xxhty7733ugbNW265Jda8AAAASl2/fv2Klje1bwAA5IFhOf82f/58Oe644+SOO+5wzz///HPp3r27W6Z3Ip122mmcXwAAoN5jrHzT0EY2axjOyZMnL7Jsn332cQ+L9t4777zz3MPSvn1710gY5ptvvpH6cPjhh0uLFi1cg6XGefvvv7/rQXj11Ve73oQAAKA0FTvGQvxKa7I2AADQoJx++uny3nvvucqu3DvQ+/fvL+PHjy9q2QAAABDdoEGD5IsvvpC5c+e6Hozff/+9HHbYYRxKAACABoTGPQAAlmCs8rgepUqHnrr22mvdEFN6Z7pv7bXXlq+++qqoZQMAAKWH+Kq4ttlmGzd8qGrZsqV06NDB/T1nzhz3GgAAKE3EWI0Pw3ICAJBnUBTXUASl3Lin89L4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Kfvqq69k1KhR8sknn7jna621lhx//PGyyiqrFLtoqAd6OTIuSXX+sozzM5CI/pUocdb/mvkbO2mun7Z3JGm8lqgOXu6ljOMe8r5Z5UpkjBdCvnsT1htspGXlkQg5JnbmxvWj2ihwSBapyuAX0+XBeZQtsE+sqpbBr1W1jvhehZy7iXQ853uDbR+J83sjE3HfQz47mVS0PEK/s5qouGKsUo+vimW33XaT6upq2WijjWSrrbaSfv36yeabby4tWrQodtGalOrmIl7QT2Xje8m6TkRdHp6H8aEKSSvq96iZRwhzX6y0zDJ5sR0TsyowET2PhBmUhSRllStiWmGddaPWjVh5W2UNKZYko9bLuHoTaxsvUj1LKqTCwdwmYt7JkPPEyt/apszIuyxkP6y0oi4PE5Z/VHa5ou971DzKk8EBdzIkUA0qVyJZLfWBGKvwxo4dK/WptFqeAABAg/L000+7xrw33nhDevXq5R6vv/66rL322vLMM88Uu3gAAACI4LfffnPDSe24444uvttzzz3dXejawKfz8QEAAKBhoOceAAB53/EUT3eGUr6z/LTTTpMTTzxRLr744kWWn3rqqbLddtsVrWwAAKDpxlilHF8VU3l5uWvI08cZZ5whH330kVx22WVy9913y2uvvSYjR44sdhEBAEAeiLEKr1u3bqFzFH/99dex5kfjHgAAyJsOxXnfffctsvzQQw91Q3UCAACgdHz++ecyefJk93jhhRdk4cKFssUWW8jll1/uhukEAABAsBNOOKHG86qqKpk6dao89dRTMmzYMIkbjXsAAORB7yiPr+deQ53QaPGWW245effdd6VHjx41luuyDh06FK1cAACgacdYpRxfFdMaa6zh4judP1lHYlhnnXVC70AHAAClgRir8DR+CnLdddfJW2+9FXt+NO4BAJAHHekprtGeSnnUqMGDB8sRRxzhhhbo06ePW/bKK6/IJZdcIkOHDi128QAAQBONsUo5viqmIUOGyIsvvijnnXeePPbYY663nj769u0rLVu2LHbxAABAnoixikfnMj799NNl7NixsaZL4x4AAMjbWWedJa1bt5YrrrjCBSqqc+fOcs4557jKIQAAAJQOf1j133//XV566SU3NOeZZ57p5t7r3bu3u4kLAAAAdffAAw9I+/btJW407gEAkAeG5fybDtN04oknuseff/7plmljHwAAQDFjLIblXDLpdNrNE6Nz7i1YsMD9/9lnny3x+wIAAIqDGKvw9Eao3OHMPc+TmTNnys8//yzXX3997PnRuAcAQD4Yl7OGWbNmZSt8/LlaAAAAihZjMS5nXnTkhcmTJ8vHH38sSy+9tGy55ZZuGHYdmlPn3wMAACWKGKvg9thjjxrPk8mkqx/TOErryuJG4x4AAMib9tY7+uij5d5775VMJuOWpVIp2Xfffd2EwW3btuXoAgAAlIgZM2a4+ZS1Eqpnz57FLg4AAEDJOPvss+s1vwbTuHfxxRe7uXqOP/747BjvasqUKW5899dff91VFq633nry9NNPS4sWLSKln/Y896ixTNKB66Zyuk7WXTJ4sfd3RWdwPsY2EWXEzsOSNsqVyeP2Rit/q1RVIcfEkjbKlU95a58Hi1OVVx4Si7Aj1TwR/Gpags/feRn7fKv0UkZawdu0Tc0PXF6WDP5MKetjlTGG3MlnGB3P2EfrlLPySCTsN9DzgvPI62uj3Dhe1n4YyaRCjrslbeSRrgw+F7x0yPeVse9elXF8q+20zEOfttJadHnCWDd2MQ0Z5adVqg4//HCZOnWqPP7447LZZptlr916Pf/3v/8t48aNK3YRm7xCx1juO6D2KWx9lo3lIV+7+sUbvE0meloJ4wJtfLWbX+5hH9mkkUeiyou2fsaLvB8m4zKRiH75sMsVEl95KePaKdHe2+TCartgEY9J4v9vRqit7Fc7+vKaB/90qmoX/JlJt0yF5B98TDJlVsBkLA75NZcpl3iEnO/We2UxPzvJPLapj0unFeOEHRMvnrRCQ4MY993KJ/Cnajw/X+svxirh+KqY7r///mIXoWQUMsbKNBMRfdSWjBrLSLT1w/LI4/vYM9Iyv0/M60RInBFxm0QeeVjlsuoOzDqFsOtHxPImkvZF2NwmYrmSIYF11H23ypQK3Y/40koacWfKOO5WWmH1RWVGYGSlVWYsT+aRh5WWtX5YHhXJkJg7YlrJqMGioTpjx9VWXWS5ue/By6uNutGw8yesHtRSHvQjLI908kKMVXDvvPOOlJeXZ0c7ePjhh2Xs2LGy1lpryTnnnCMVFRWx5ldf4XmoN998U2688Ubp1atXjeUaEA0YMEC23357eeONN9x6xx57rOvOCAAAiu+xxx6T2267TXbYYQdp06aNe+jfN998szz66KPFLl6TR4wFAAAQP2IsAABQm97k/vnnn7u/v/76azeqVcuWLd3NU6eccoo0up57c+fOlUGDBrlKwJEjR9Z47cQTT3TjvZ922mnZZauvvnoRSgkAwKKdVCJ2BDbFlU4xLLPMMoFDb+oynacFxUOMBQBoyjFWKcdXaNiIsQAApYgYq/C0YU977Ctt0OvXr5/cc8898sorr8jAgQNr9PSPQ9G7wB1zzDGy8847S//+/WssnzVrlhvCoEOHDtKnTx/p2LGjOxgvv/xy0coKAIBPh4uK81Gqhg8fLkOHDpWZM2dml+nfw4YNk7POOquoZWvqiLEAAKWI+AoNHTEWAKAUEWMVnud5kvn/KSCeffZZ2WmnndzfXbp0kV9++aVx9dzTeXh0HFIdzqA27baodCzSyy+/3LV43nnnnbLtttvKhx9+KD169AhMc+HChe7hmzNnTgH3AACApu2GG26QL7/8UlZaaSX3UN999500a9ZMfv75Zzfstk+v+agfxFgAAKCuRo8eLUcccYQ0b97cxXFaAZXIa1Lxxi/uGIs6LAAAGo8NN9zQjU6pHdleeOEFV2empk2b5jqvNZrGvenTp7tJh5955hkXQNbmt3DqOKWHHHKI+7t3794yadIkN7fPRRddFJiuLj/33HMLXHoAQJOnve3i6nFXwj339thjj2IXAbUQYwEASlpcMVYJx1f1TUdh0KGitG6mW7duMmPGDDeKEgofY1GHBQCoN8RYBafDbuoUdBMmTJAzzzxTVl11Vbf8gQcecKNTNprGvbffftsNvbn++utnl6XTaXnxxRfl2muvlc8++8wtW2uttWpst+aaa7o7ySynn366C0xze+7pXWcAAMSJOff+dvbZZ3NiNTDEWACAUsZ8MPWvc+fO8uCDD7qho3Q4qe+//14WLFgQuK4/UkNTVIgYizosAEB9IcYqvF69eskHH3ywyPLLLrtMUqlU42nc02EJau+o3tm0xhpryKmnnirdu3d3AaYfHOVOSrjjjjua6eowYPoAAAD1a+7cudk7ln1t2rThbahnxFgAACDqHMrHHXecHHvssW44zo022miRdbTRT1/TxqymqhAxFnVYAAA0rl7+iURCVlxxRff8jTfekHvuucfd+KNDoDeaxr3WrVtLz549ayxr1aqVLLPMMtnlw4YNcz0C1l13XTdW+R133CGffvqp68YIAEBRef//iCutEqXjhmtF0OTJk2vc4U0FUPEQYwEASlpcMVYJx1f1TSub9ttvP/n222/dHefPPvusq5uJIrc3W11oxdcjjzwiK6ywgpQKYiwAQEkjxiq4/fff38VVBx54oMycOVO22247WXvtteXuu+92z0eMGNE4Gvfq4oQTTnAVhSeeeKLMnj3bNfLp2OarrLJKsYsGAGjiPC/hHnGlVaoOOOAA15Cn84jo5MBaUYOGjxgLANDYY6xSjq+K2XA1duxY2XzzzSOPiPTuu+/KSSedJEsttdRi19XY8eKLL5aFCxdKY0OMBQBoqIixCu/DDz+UjTfe2P193333udjqlVdekYkTJ8qRRx7ZuBv39K7/2k477TT3WFIZ8dyjTuvqALSG8kRS4pL2ag5dlq9qiT4sRjpkH6OyjmvaWF4V037/nXfxpEMOYZVE+yGZMdJKR0wnTEXCPk8ykoy0TSoRfOQznv35SFs/ro19t07RTNrOw0sn4vlhH7Z6tN0IzyZpJJYq3t3HnnV8F4Yc91RwwRLG+5GXhLHzQccwvq9p1MF7773n5h9ZffXVOV4NWCFjrES13n1ft49swrh4JkMuqsYlx7wQW3mHXVw84/vYMyLlsDZsq7zmMbGCgBiDnIR5UQ077sbxrTaW1xqSN5eXjPjFbLwfmQr7p0vyr6rg5Qsqo+17VbWZR2L+X4HLmxl5Z1pW2Gkt2yJweXXz8uC0yo1zNGzahkTEWCbi+ovNP2h941QIDdUilisT8gvXilmi5h0SVkui2toovuMeqyixLW1lTcLBBx/s/tf47pNPPnF/61BSdemZp6MvdejQoU75XHHFFdIYFCrGyjT3RPRRi5eM9j1m/kQPSycRbZuQagAzADKr1szffSFZGD+UE9Y+Wl/TIcfEiv0SyUy09UMCVSt/8zIRklbSSCtpvR8Rl//9mpF3xLSSEj2PlHHcrbzD8i8zgvd88iiztjH20VrfWq4qktWR9sMqb3kyHTmPlLEfSfMHm51/ylhu1R0uDAnwyo3AzNx3Y/2wPKqMoDdq3ta+Z0Lec5SWqqqq7A1SOhLCbrvt5v7WIbxnzJgRe34NqnEPAICSwnBPbk4WHVOcxj0AAECMVfpmzZolAwcOdI1W7dq1c8t+//132XrrrWXcuHGy3HLLmUO1W68F+fjjj938dAAAoB5Rj1VQOgTnmDFjZOedd3YjUJ5//vlu+Y8//hh5yPO6oH8DAADI2y233CKXXHKJmxdX7/B+//33azwAAABQOo477jj5888/5aOPPnLTo+hDh5iaM2eODBkyxNyua9eu2eHZv/vuOzf0Zm26TF9TXbp0kVQqYvdfAACABuySSy6RG2+8Ubbaais3n7FOM6d0nmF/uM440XMPAIA8MOfe337++Wf56quv5JBDDskeG63Y0cob/T+djj50NAAAaLqYD6a4nnrqKTeM1JprrpldpsNyXnfddbL99tvXKY1u3bq5oadqD9GpDYX6GvEhAAD1jxir8LRR75dffnE3RS299NLZ5UcccYS0bNky9vxo3AMAIB9ejMMZlPCwCIceeqj07t1b7r33XunYsWP2jm0AAICixlglHF8VUyaTkfLyRef/1GX6Wl34N3nVNnfuXGnevHks5QQAABERY9ULjYN0ZCu9EX7//feX1q1bS0VFBY17AACgYfn222/d8AKrrrpqsYsCAACAJbTNNtvI8ccf727c8ufE++GHH+TEE0+UbbfdNnTboUOHuv+1Ye+ss86qUYmlvfVef/11WW+99XiPAABAo60jGzBggBuGfOHChbLddtu5xj0drlOf63x8caLnHgAAedG7kePqpZYo6Qqg9957j8Y9AADQwGKs0o2viunaa6+V3XbbTVZeeWU3L56aPn269OzZU+66667QbadOnZq9Y/2DDz5wd6n79G+dd+bkk08u8B4AAIBgxFiFpjdIbbjhhq6ebJlllsku33PPPWXw4MGx50fjHgAA+WBYTmfXXXd1d3JrBc4666yzyDBOWjkEAABQ7zEWw3LmRRv03nnnHTfv3qeffuqW6fx7/fv3X+y2zz//vPtf52K++uqrpU2bNvkVAgAAxI8Yq+BeeuklefXVV2vc4KT0pikdCSFuNO4BAIC8HXnkke7/8847b5HXdEgmHYIJAAAApUNjOB1GSh/5GDt2bOxlAgAAaOgymUxgPdj333/vhueMWzLfDXVCwOHDh8t+++0ns2bNcsuefPJJ+eijj+IsHwAADfuOp7geJRy4WA8a9vJDjAUAaNKIr0revHnz3Jx7ffr0cUO3d+/evcajWIixAABNGjFWwW2//fYyatSoGjdMzZ07V84++2zZaaedGkbPvRdeeEF23HFH2XzzzeXFF1+UCy64QDp06ODGEr311lvlgQcekIZmoZeWBV7dak9TiZCx+Y0kksYLoWlFlDbKnwmpFU7HVGMclkeVUa6oeadC5kSw0grbJqqo5c2EvFblRStXOsb9yETMWyWNvbGW55O3Z7xmLS8mLxNnmULOa+OUy6QifnZS0d6nUNa+h71PCaO8zY1yheyeVx2cT6Iq+F4UL7loYp5VHqAElGKMpR+52h+7ZHXw59BanjCWu9cyxmvG4kRYvGd8LWVqjib7P9VWoewsEsaXe9L60s/jFjzPKICVd9TlLvuFwTufqDJ642ZCrkVJ4zs8ZcQGqVTw8nL7oJjvu3X+zP8reHm19aa7oCV4eVXwNsnqFmZS5c2Cf4aVtQlevrCdcaxCzhPrvLa28YIPe7iIIZNZ3rCfX8mo+2Gf11H30UorYZy7KpWOGEoVORSOUq6w8w3Idfjhh7uY5sADD5ROnTq5iq1iK7UYK1OeEakIuLZan8OA30XW76XFxjIRt0mE/f5KRMvDXB52Chn5J820ouXtXrOWW2kZy1NheURMK+yYpJJWHU+0tJIh761VLmubqOuHlzd6WmXWMTG2KUtkYsvDTiva+n/nERxoNEtWRypvecIeYSdqWqmQusOo+WeMT9sC8webzSpXuXEMw45J2vjyzacONjCfRFXkdNAwXX755TJgwABZa621ZMGCBbL//vvLF198Icsuu6zce++9DaNx77TTTpORI0fK0KFDa3Qn3GabbdzkywAANHoaxMXVON0AG7mjVpZoAPPJJ5+45xrEDBs2TLbYYotiF63kEGMBAJq8uGKsEo+vSpmO6vT444+7hrSGghgLANDkEWPVy9zFeuPQ+PHj3f/aa++www6TQYMGSYsW9s2f9dq498EHH8g999yzyHK96+mXX36Jo1wAADRo2oGjjh3C65RWqbrrrrvkkEMOkb322kuGDBnilr3yyiuy7bbbyu233+7uUkLdEWMBAJq6uGKsUo6vSt3SSy8t7du3l4aEGAsA0NQRYxVWVVWVrLHGGvLYY4+5xjx9FFpejXvt2rWTGTNmSLdu3Wosnzp1qqywwgpxlQ0AADRwOqTRpZdeKieeeGJ2mTbyXXnllXL++efTuBcRMRYAAGgIZs2a5R46j3KuXr16LXZbjQFHjBghd9xxh7Rs2VIaAmIsAABQSOXl5W4ozvqUV+PewIED5dRTT5X777/fjZ2uwZ7epX/yySfLQQcdFH8pAQBoqBMRx5VWifr6669l1113XWT5brvtJmeccUZRylTKiLEAAE1eXDFWCcdXxfT222/LwQcf7IZb9/6/+6PW++jf+n86bc9J5Lviiivkq6++ko4dO8rKK6/sKrtyvfPOO1LfiLEAAE0eMVbBHXPMMXLJJZfILbfcImVleTW9RZJXDhdeeKErqI4hqoGdzq2j/+vQW8OHD4+/lAAANDTMuedoLDBp0iRZddVVaxyeZ5991r2GaIixAABNHvPBFNWhhx4qq622mtx6662ucU4b9KLaY489pKEhxgIANHnEWAX35ptvujqyiRMnyjrrrCOtWrWq8fpDDz1U/Ma9iooKufnmm+Wss86SDz/80E0M2Lt3b+nRo0eshQMAAA3bSSed5IbhfPfdd6VPnz5umfbm1/n2rr766mIXr+QQYwEAgGKPyvDggw8ucuNWFGeffbY0NMRYAACgPoYB33vvvevtQC9R38CVVlrJPQAAaGoS3t+PuNIqVUcddZQsv/zybvil++67zy1bc801Zfz48bL77rub273//vuR89KRAupjWIOGgBgLANBUxRVjlXJ8VUzbbrutvPfee0vUuKd+//13eeCBB9zwnMOGDZP27du74Ti1N+AKK6wgxUKMBQBoqoixCm/s2LFSn+pcQzZ06NA6J3rllVfmWx4AAEoDc+5l7bnnnu4RxXrrrZedv6UuksmkfP7559K9e3dpbIixAADIwXwwRaVzxOicezpKU8+ePReZL0/nVV4cvYmrf//+0rZtW/nmm29k8ODBrnFPh6L67rvv5M4775T6QIwFAEAOYqx6M2vWLPnss8/c36uvvrp06NChuI17U6dOrfFc77iqrq52hVNa4ZZKpWSDDTaIv5QAAKDBjieeyWRkk002qbH89ddfd3HBhhtuaG6r6yy33HKLzUMbALVyqbEixgIAAA3FlClT3BDrTz755CKv6Y1Z6XS6To1q//rXv+TSSy+V1q1bZ5fvtNNOsv/++0t9IcYCAKD4XnzxRbnsssvk7bfflhkzZsh///vfGvPzap2PDumt08Bpz//NN99cbrjhhhpTwF1wwQXy+OOPuylhdKhtXa82vYFIR5d6/vnnZamllnI3K1100UU1RoCaPHmyi1M++ugj6dKliwwfPtzFLLmuu+46V96ZM2fKuuuuK9dcc41svPHGUhdz5syRY445RsaNG5eNmbRubN9993Xp6o1PRWnc04OS2zNPA7Q77rhDll56abfst99+k0MOOUS22GILaYiqxHOPusiErJaW4EA2ZUwyndSJKiNKSfA26TqWf0lkjDzSdexZsSTi3L9kyGsZK/+I2VeFvLdVXlgJAvI23vN8VHmpwOULvJp3XOaqNLbJGEcyHXH/XFoRPwteHp+dokoHHxMvHbIfqeDXvIgnY2XSXr+s3PjxHfXjFpJHwngtkbI+bSESyWhfzFWJ0p+I2E+rRGnQcsoppyzSuPfDDz/IJZdc4hrwgvTr188N96TjkdfFlltuKS1atJDGqNRjrETaW+R7QJcFrlttLA/53jPTsmKTsO83YxvtGRq4eh5fY8nqTPQAMybWEHSJquAyJavsCtrEgsrg5db+hfDKrPcwYjxhXDf/zsSLtjxj7EdYzJsKjpfEOH+kzFhf9904H5LmZyQ4HS/k15z1Wh5hnJ2H9ZYkIuadiPESGfIWJjLx5OGFxEVGWG3LxBgDlG44UZgYq4Tjq2I67rjj5IADDpCzzjrLDaGZ781fN9544yLLdThOrSirLyUdYzXP/P2ozfr+MU73hBEcWMvda1HzCPutaOSTjJhHMhFfHsl8jonxWsr6LWyVKeQiZVQrSiqZie+YRCxX2DEx04ppeRirvGXGscon/zIjaEhawUToNsF5lCfTkc+TMmObciNYLLfOH7N2VKRZsjpwecrYPyvvsNfCjmNwmarM1zJetLrL5kZaVYlU5LrWfOpHg45jxnhfG0uMNW/ePNdIduihh8pee+21yOt6M9Do0aPdNbpbt24uBtlhhx3k448/lubNm7t1KisrZZ999pHNNttMbr311kXSSKfTsvPOO7tpY1599VXXiHjQQQe5EQguvPBCt860adPcOkceeaTcfffdMmnSJDn88MOlU6dOLj+lU8xo49+YMWNcPdeoUaPca9oLry6973S0Ar256LHHHnNl9W+cOv744+Xf//63a/SLU14T1+i8OhMnTswGREr/HjlypGy//fZy0kknxVlGAADQQGmwtf766y+yvHfv3u61ulS21MUTTzwhTQExFgAAKKZff/1VTjzxxLwb9lSzZs3cneu16YhPdRm1oRCIsQAAKI4dd9zRPYJorz1tQNMedLvvvrtbpsN3axwyYcIEGThwoFt27rnnuv9vv/32wHQmTpzo6qCeffZZt61OBXP++efLqaeeKuec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vNV2HkYcFxIYWdukIuYR9ps+alpJ4ws27JikjONo7Xs+xyqViJZHWFpRt7GOST55xFWmsHJZypJ2nBp13620UnmU10zLPO72b6Ny61w0ajWt/S5P2MfKeq08WR05reaJqkjlyhhBpJWOSofUTAdJGccqrA62yqprzVTEcu4mYpkIr+HSxrfFTceijXC5DXG1aSOgPsJ07dp1kWE3g8oyderU0HV0CM66DsNZbEs0515tBxxwgOuuePnll8eZLAAAjXciYj+tiPSO6csuu8zdSbTuuuvKNddc467BpWSLLbYIfV3vtOrXr1+9lachI8YCADQZccVYeaYRNca6//773fBR33zzjRtO6pJLLpGddtrpf8XwPDf/ys033+zuRt98883lhhtucOv6dJip4447Th599FFJJpOy9957uzvKdZgp3/vvv++GlnrzzTdlueWWc+vrne5xGDt2bORtFncH+/fffy+PPPKIGz6rsrKyxmtXXnmlNBTEWACAJqPIMRYaeOPelClTanSXBAAA8Rs/frwbxmDMmDGyySabuDHAdRiBzz77LDtxcKm69957ZbfddqvzsFBNBTEWAAANL8Z69dVXZb/99pOLLrpIdtllF7nnnnvc/DDvvPOO9OzZ062j876MHj3aDSXlzyOjaX788cfZ+pNBgwbJjBkz5JlnnnHTnui8Ljo0laan5syZ4+aS6d+/vyvbBx984OaKadeunVtvScU9vcqkSZNcPNe9e3f59NNP3bHQxk9t6Fx//fWlISHGAgAATapxb6+99qrxXAM0DUR1/FQNVAEAQOHo3c46z4k/oa9W8ui44bfddpucdtppBT/0Sy+9dJ2HbdI70aP497//7SrTtDKoKSLGAgCgdGIs7V03YMAAN9S4Ov/8810D3bXXXuu21boSbSAcPnx4do7hO++80/V6mzBhggwcOFA++eQTN5eL9sjbcMMN3TraW1B7/+moSJ07d5a7777b9X7TclRUVMjaa68t7777ritvHI17cdP5+04++WQ599xzpXXr1vLggw+6xlFtxNTjVQzEWAAAoBD8OQTrIu7RC/Jq3GvTpk2NSj0dNmL11Vd346Lq3WQAADR2ehWMYyJiPy3/ruxczZo1c49cWrHz9ttvu0qT3Ouw3smtdx7XB62k8v36668ycuRIdwe6TlqstBxPP/10Xjf8hI3D3hQQYwEAmrq4Yqwo8VW+MZYur12hozGRNtypadOmueE9NQ1f27Zt3Y1Muq027un/2gPPb9hTur7m/frrr8uee+7p1tlyyy1dw15uPjoE6G+//eZuvGpItMFSR2NQZWVl8tdff7khRrXOSBs5jzrqqHovEzEWAKCpizvGwt8WN4efr643yRe8cW9xkxcCANDo6UTPxmTPeaUlIl26dKmxWOdnOeecc2os++WXXySdTi8yz4k+12GP6kPu0E06J4xW1ORONjxkyBB3x/qzzz4rJ554Yr2UqbEgxgIANHlxxVgR4qt8YyxtuAtaX5f7r/vLwtapPeSnNoi1b9++xjo6pGftNPzXGlrjng6v7s+z16lTJ/nqq69cb0P/OBcDMRYAoMmLOcbC355//nkplmQ+G+lQWXqnfm06OXRTHUYLAIAlNX36dPnjjz+yj9w7xxsq7aEXNLySLtPGvaiefPJJWWGFFaSpIsYCACBepRhflbpNN91UXn75Zfe3Di960kknyQUXXODmCdTXioEYCwAA1Jcvv/zS1Zfp6AWFHKUqr8Y9nQhZ72irbeHChfLDDz/EUS4AABo2L+bH/w8XlPsIGjJq2WWXlVQqJT/99FON5fp8+eWXl/q2zDLLyMMPP7zIcl2mry3OE088kb0b/osvvnCVbkH73VQQYwEAmrwixFf5xli6PGx9///FrTNr1qwar1dXV7t5i3PXCUojN4+GROeT0aFHlc67t+2228r48eNl5ZVXlltvvbUoZSLGAgA0eTHHWFiUdojTuGe11VZzNzjNmDHDLT/ssMPczU5FHZbzkUceyf6tLY86VrxPG/smTZrkgjUAAFAYOtfKBhts4K65e+yxh1uWyWTc89yhMeuLVtgcfvjhMnny5Gwljs4P89RTT8nNN9+82O11qCYdulN77B1//PFy4YUXSlNEjAUAQOnFWDrfsL5+wgknZJc988wz2XmIdShNbXzTddZbb73sHIAaK/nzzum6OgqSzven+avnnnvO5e3HVrrOmWeeKVVVVVJeXp7NZ/XVV499SM558+bJxRdf7MqsjY5ajlxff/31YtPIHdFJh+gcM2aMFAsxFgAAqC9av6Wx2nfffSdrrrlmdvm+++7r5mm+4oorite45we4Ovlf7nw7SgutDXtxFxAAgAYpzruVIqajAYFehzfccEPZeOONZdSoUa4i5pBDDpH69q9//csFLKNHj5aHHnrILdPnOhSTXyEVpnfv3m4fDjzwQPe/X/HV1BBjAQAQc4yVRxqLi7EOOuggN3z4RRdd5J7rjUn9+vVz9SA777yzjBs3Tt566y256aabsnUn2vA3cuRI6dGjh2vsO+uss6Rz587Za7/GTTqc+eDBg10jmDbgaWPiwIED3Xpq//33dzdU6V3fp556qnz44Ydy9dVXy1VXXRX7aaM3bb3wwgsuNtObsHQf8knjgAMOkK222kqKjRgLAIDix1hNxcSJE12nuBVXXLHGco0Dv/3229jzi9S459+xpQHpm2++6YatKBXzvDJJeMk6nYjliZp3puVKGRuljeXW+n/nY7xgbGLlYZfWZqUVp7SRRZUE73gmZOzZZIz7sdCY9HOBlzKWB39MKo311XyvIlKZKo08qkLySBtHJVP7PP9/C7y/7/CMwsp/YSY4rQVp+yvFM467tdwUsnoiGe18sPJOhJxXnlUA4yQ13o5Q1ldQwiiWl7Ez8YwPYsb6kW4tLov+TWO9H+lK+7yWquB9SVYa71U6UadlhaDvh/We5JNWFHrHz88//ywjRoyQmTNnugYx7SnXsWNHKQZtxLv77rsjb7f11lu7CqPffvtN3nvvPbcfWpmky/SO9aaklGOsZJUnyVrX8GR18HdGoip4ebI65EOQCX4tkceY9Z5ZQWnEcVVpY3U770RldfDyhcHLJWCo+7D9dpoFX4e98lSk8iasYC2sXJVVwctTIRe8VHC5EtaX3/yFxvrRv9+9CiPvVPPIaZl5pIxyWcvd5ybadTXk54kpcgwS4+XTvK550fO2QkXz0xwWK1r5Z6J91MOOrVcWvFHSjE+s77g8jknGiJeKFK8sST5x5JVPGouLsfQu7GTyfydAnz595J577pHhw4fLGWec4SpuJkyYID179syuc8opp7gGwiOOOML10Ovbt69Ls3nz/30PaRylDXo6jJOmv/fee7sbp3w6apJWFh1zzDGud5/GCVpGTTNuOprC448/Lptvvnneaegx1AbL5ZZbzjVSakPfuuuuK8VQqjFWWbNqSTWrrvNvLOsbJpk0YrKQ78qUuY0RL4X8Dre2SVqxl7V/IR/opPGambe1HyEXW2ubuJaHHZO80oparoh5h0kaxzFq3uF5WOW130OrrtfaJur6YeVKmfse7Vi5tIxtypPpaJ+10GOVjnisjN8NIa9Z+efz+UwbgVkmanAbEm+njbSsvMuTxm8mQ9j+NZYYq6mYN2+etGzZcpHlOtx6IaagidS455s2bVrsBQEAAHWnFUDFGIbTqjTRyYKDhm7acsstze2ef/75bEXa0Ucf7YZ/0jvemzJiLAAAGm6MpcOQ17bPPvu4h0VvTDjvvPPcw9K+fXvXSBimV69e8tJLL0mh6TCfWp4loXMv681b999/v9svnYNvjTXWkEGDBrleiMWYzoUYCwAAFNoWW2whd955p5x//vnZOFDryS699FJ3g3vRGvf0rjG9K0zvLsu9gyzIkCFD4igbAAANVxGH5WxIXnvtNVdJo8MLeLW6OGgQo3Pyhhk/fryrQNKhqN599133XBv7mhJiLAAAcjBkVFFpZZT2CrzjjjsC7zyP0kiodUj6+P777+Xee++V2267zaVdXW30Zo8ZMRYAADmIsQpOG/F0JAYdpr2ystKN4PDRRx+5nnuvvPJK8Rr3dCx3vctKG/fCxnXXijwa9wAAjR6Ne86RRx7p5qXR4ZvymZdl/fXXl+233979fcEFF7jef00NMRYAADmoeCoqnT/wq6++ckORag+78vKaQ0G/8847kdLTOQS1guv111+Xb775pl6HkSfGAgAgBzFWwenQ7J9//rlce+210rp1a5k7d67stddebmh1rTMrWuNe7hAGDGcAAADUF198IQ888ICsuuqqeR0QDXq0d5/e3a3zs2h6q622WpM6uMRYAACgodhjjz1iSUeHX9chOR988EE3HJVWbD322GOyzTbbSH0hxgIAAPVN50o+88wz6yWvvObc07HiTz755EWGaPjrr7/ksssuc8MsAADQmMU1EbGfVqnaZJNN3Hx7+Tbude7cWU488UR58skn5fjjj5cLL7xQmjJiLABAUxdXjFXK8VUxnX322UucxgorrOCGnxowYIDcdNNNsuuuu0qzZs2kmIixAABNHTFW4Wnd2AEHHOBGwOzRo0fB80vms9G5557ruhTWNn/+fPcaAACNnpeI91GijjvuODnppJPk9ttvl7ffflvef//9Go/F6d27t2y88cZy4IEHuv/XW289acqIsQAATR7xVYOgcd1dd93lHlOnTo207TnnnCMzZsyQ//73v/KPf/yj6A17ihgLANDkEWMVnA6/qdPWrL766rLRRhvJ1VdfLTNnzmxYPfc8zwucU+e9996T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3759y69y75133rGDM2sbZ4KkoAeIAACgOLQHFPPmzZP33ntPtthiC3nppZfsQxwUDzEWAACI4vTTT8/996BBg+Tzzz+XyZMn23H3GvtAaurUqbLzzjsHvgRmYsVyQIwFAACW9eOPP9oXpf79739LkIzycm/JVu4tWrTIDsh8xx13BL7BNWXKFLn22mvl3XfflTXWWKMo6wgAwHLi7E6zBb41vyIjRowInX7++efLRRddJC+++GKzrRPyEWMBAFp1jFWC8VWxmdZ5e+yxh211t/7669u0Xr162U8U3bt3l6+++krWXnvtvPRXX301N55fKSPGAgCUJGKsJmd6sTIvMr311lsycOBA23p/9uzZtt7L1HGV3Zh7J510kuy99972jbCg5ot//OMf5eabb7bBIQAALQZj7oUy9++PPvqouY4GAhBjAQBKEuPBFE0ymZQPP/xwpfM59thjbVeV5sGW6YVp5syZct9998lZZ50lJ554opQ6YiwAQEkixmpypgcr04351ltvLa7r2hekDjvsMLnqqqtk1KhRsZdX1JZ7pu9103WX6c5A6w5i++23l/3226/ReZqBmrODNRsLFiyIZV0BAEDjJRIJ8bzmGf8QyyPGAgAAhTAPoO666y47nlyhzFh9Jg7cfffd7UvbpovOqqoqW7k3bNiwkj4wccdYPMMCAKB8LF68WLp162b/e5VVVrHddG6wwQay2Wab2fihbCr3ZsyYYd/kGj9+vFRXVy83/YknnrA1ne+//36kfE0N6MUXXxzjmgIA0HQDEWfzKjePPvqobLzxxsVejVaJGAsAUMriirHKMb5qDul0Wv72t7/JCy+8IFtttZW0a9cub7p5G31FTGs900X72WefbbvnNN1Ymriwffv2UsqaIsbiGRYAoLkQYzW9DTfc0I49bLom33zzzeW2226z/226PG+KIeeKVrlnBmSeM2eO9OvXL29AwUmTJsno0aNtVw1ff/21dO7cOW+5Aw88UHbaaSeZMGFCYL7nnXeenHHGGXkt93r27NmEWwIAQOtz4403BqbPnz/f3uOffvppdQBhNC1iLAAAUKiPP/4495zmiy++WK7SLorKysqyetmrKWIsnmEBAFA+Tj31VPnhhx/sf48YMcKOZWy6Jjcx0bhx48qncs90z7DsWDxHHXWU9OnTR84991zp2rWrHH/88XnTTfPF6667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CgAAAGgEtzEzAQCA4IGI4/qUqpdffjn3eemll3Kf7N8AAABREF+1DJ9++qk8++yz8sQTT+R9AABAaSpWjDVp0iTZd999pUePHvalcDMeXUPm5fCLLrpI1lhjDWnTpo0MGjRIvvzyy7x55s6da8eyM71Dde7cWY4++mhZtGhRbvq3335r8172Y8YNbujhhx+WPn362JZ0m222mTzzzDOR16UloeUeAADFHIg4m1eJMmPt9ezZM6/VXjYgmjFjRtHWCwAAtPIYq4Tjq2IyYyn/9re/tePFZMfXM7KxHuPsAQBQoooUYy1evFg233xz+dOf/iQHHHDActOvuuoqufHGG+Xuu++2z5guvPBCGTJkiH3RKNudpanY++GHH2T8+PGSSqXkqKOOkuOOO07uv//+vLxeeOEF2WSTTXJ/r7rqqrn/fv311+34waNGjZJ99tnHLrv//vvLe++9J5tuummj16UloeUeAAAomAl2fvzxx+XSzVtVZhoAAABKx6mnnmpjuDlz5kjbtm3lk08+sW/cb7311jJhwoRirx4AACgxe+65p1x22WX25aFlmZeIrr/+erngggtkv/32k759+8o999wjM2fOzLXw++yzz2xvAnfeeaf0799fdtxxR7npppvkgQcesPM1ZCrzunfvnvskk8nctBtuuEH22GMPOfvss2WjjTaSSy+9VPr16yejR49u9Lq0NFTuAQBQALrlzB9bb1mme4SW+FYTAABo2eiWs7jeeOMNueSSS6Rr167iuq79mIdo5i33U045pchrBwAAyinGmjZtmsyaNct2f5nVqVMnW4lnYhLD/Gu64jQvGmWZ+V3Xlbfeeisvv9/85jfSrVs3G7ss2524yadhOYZplZctpzHrsiKmh4O77rpL/vjHP9p8dtttt7xP3FpNt5y/pNtKVfp/NbWG5y//MNJYlKlS86n3gnfZtIVdAtN/rmmn5rVgfpvAdC8dXOeaqPQC0zOJ4HTjvz1oNLoM3wveJ0vUEvRl/FRwGY6aHpyP4WaUadr2VYZcZbRilPPBzUSafem0Cj9SdbqXDM6sri6hllHnB5+nC5TzJJS2MXXKCmvHw9X3u5N2ou/IoHxCypCENk05R2N8vUEtuSLkeETbdHGVvNyQa4BWhKuc2O3a1AWmd6gMTjdWqQ6+QlQnUoHpFU70czStHKx59ctfR9OL6+QbaQatvFvOM844w/5rKvZMNwXmze6GwYwJsLbYYosiriGai5vKiOul89Nq6oNnDqgIthL6BXn+Zl0D0+s7KPfOVfSLq3bL8ZXbbfp/p3WjLemmFa6ka+vUUl/BU7YjoRxyI9W+GbZdWa90bXAh9R3VO6RaREK5FWq3tUz+T488qY7B6cpPjYL2VcQQSy0j9LYdtYw4FXDv1B5EaNeAQva7m385zKn6Odp5lazRNzBR50c6Vl6FfqAS/wnOK9Vu+WUy9c0UsNAtZ1GZOK5Dhw72v00Fn3lbfcMNN5RevXrJ1KlTi7tyrUjHiiVSmVz+N5urXMiSTvDvu6QbfGGoUOY3EhK8TJVygWurXcjMNLc+UhkJ5ULmhlz0vYg3o3o/+GabUm8GInVeyE09gio3+DdyGG1f1Xj6s0tX2Y8JZT9q66U9N11aRjz3BG37Qpcp4JmCdg5FPX8yMQbp2vc2bPtc9bvjx7bfM0o8rC2jnW9hapXvlPY9DD1PHOWY+MoyyiH3Qq4zrrJQ2DKqgHPIkdKMsRYsWJCXXFVVZT9RmMo0Y/XVV89LN39np5l/TYVdQxUVFdKlS5fcPO3bt5drr71WdthhB1vp98gjj9guN02LO1Phl81nReWsaF0a0wPCuHHjZO+997ZdfQa9DB+nVlO5BwAA4vP+++/nWu6ZMVkqKytz08x/m/7UzzrrLHY5AABACTEPoj744APbNad5U92MPWNiu9tvv13WWWedYq8eAABoIXr27Jn394gRI2TkyJFFWZeuXbvmXkI3ttlmG/uC0tVXX52r3GsOpqvQhx56SPbaa69mKY/KPQAAChBnVwRxdmnQXF5++WX7rxnE2PRb3rGj0gwFAACgCDFWKcZXLYEZZ2bx4sX2v033nPvss4/stNNOdgybBx98sNirBwAAWkiMNWPGjLxnQVFb7RlmXDxj9uzZssYaa+TSzd/Z3qDMPGYs4IbS6bTMnTs3t3wQ85LS+PHj88oy+TZk/s7m0Zh1WRHzQtR6660nzaWldvgDAABKwNixY20w99VXX8lzzz0nS5YsybXoAwAAQGkxY88ccMAB9r/Nw6nPP/9cfvrpJ/tQrSnGigEAAKXJPAtq+Cmkcs/0FGAq1V588cVcmunu0wz1MmDAAPu3+XfevHkyefLk3DwvvfSSeJ5nK/A0U6ZMyaukM/k0LMcwlX/ZchqzLity5pln2hfgm+uZGC33AAAoRCsfcy/LvCn1+9//3rbkM32Jf/nll7bLpqOPPlpWWWUV2+c5AABAozHmXlHde++98tvf/lbatWuXSzNj2gAAgBJXpBhr0aJF9oXwrGnTptmKNxNfrLXWWnLaaafJZZddJuuvv76tYLvwwgulR48edsw8Y6ONNpI99thDjj32WBkzZoykUik5+eST5eCDD7bzGXfffbdtNbflllvavx999FH529/+JnfeeWfeeHi77LKLfU5lxsQzXWi+++67tutxwzzTWtG6rMirr75qn4/9+9//lk022USSyfzxJc16xYnKPQAACkHlnmUCHxOsTJ8+3QZcWX/4wx9sf+dU7gEAgKLEWCX88lQxnX766XLCCSfY8WkOO+ww25IvkUgUe7UAAECJxlimAm3XXXfN/Z0dG++II46QcePGyTnnnGO7BD/uuONsC70dd9xRnn32Wamurs4tc99999kKvd13311c15UDDzxQbrzxxrxyLr30Uvnuu++koqJC+vTpY7sT/93vfpebvv3228v9999vuyD/85//bCvwHn/8cTvecFZj1iVM586d7UtSzYXKPQAAULDnn3/edse55ppr5qWbIMkEVQAAACgdP/zwg32I9Y9//EMOOuggadu2re2l4dBDD7UPxQAAAKIYOHBgaDeVpsWcGefXfDSmlZ+pmNMcccQR9rMiJqYxn5VZlxUNXdOcqNwDAKCIAxFn8ypV5o0m89AnqLvOQvpbBwAArVtcMVYpx1fFZN5232effeynpqZGHnvsMfswzbxxb17m+vrrr4u9igAAoADEWOXHLfYKAABQ0t0ZxPUpUTvttJPcc889eW85mUGNr7rqqrxuFwAAABqlBOIr8xKTacnWsWNH2/2SGWvYjCcTpra2Vk466SRZddVVpX379rY7qdmzZ+fNY7o5N2PAmBenunXrJmeffbak0+m8eSZMmCD9+vWzL1Gtt956tjurhiZNmiT77ruvHR/GxGWmu6lCmfUw3XLuueeetleGb7/9tuC8AABAkZVAjFUO/vnPf9reD7bbbjsbszX8xI3KPQAAUDBTiWcGHzYPferr623/5Ka/cvNg6S9/+Qt7FgAAlB1TsffJJ5/I+PHj5amnnrJxjxmbZUVj2T355JPy8MMPy8SJE2XmzJlywAEH5KZnMhlbsWfiqddff13uvvtuW3F30UUX5eaZNm2ance8QDVlyhQ79vExxxxju0hv2KvC5ptvLjfffHPB22da7Jmxbfbaay/51a9+Jddff70dP8ZsMwAAAIKZcQCPOuooWX311eX999+Xbbfd1r7Y9c0339jnZnFrNd1yLkxXS106mZfm+U7gvPNTbdR8ZizqHJg++5eOgempJWG7OLh88ZTkdPD8mfr87WpMXn7aja9PE2U/iqsskwhO970Cio9atlmkMqSgAJ62r0LKEK0MZXUd7Th5ygJ2ojJtsXLOhb1VoZSjrZeTib7f4+Jr213AiyNOKmT/assohairpXxvw/aXXxGc7jnB52La1Qe4TyYzgemOsiGVieD5O1XVqmV0rqwJTK9y05HSDVdZL+163avt3OXS6ipS8oI0Pcf37SeuvEqVqcj74osvZPTo0dKhQwf71rp5UGXeTF9jjTWKvXpoBk7aE2fZm3g6+FoiFcHXq9q1guMrY+GayrWvXfD8fgHRbcitJb683HjmbzZq7BWcnK4uctd42v05EW3+sHWt7xBxlfTbsy7Gc1GL49SfIMr6+iHnojbNzUT7bRK63VH3SVj4HDGOU9OV3zOGGr4rmbWZG7xTKmr03yyJOm1DlNgyoe+Uiprgg9V+emq5tHRajwdbYozVVPHVZ599Zseke+edd2Trrbe2aTfddJOtCLvmmmtsi7llzZ8/X+666y7bveVuu+2WG5Nlo402kjfffNO+2W3GMf7000/lhRdesA+EtthiC7n00kvl3HPPlZEjR0plZaWMGTNGevfuLddee63Nwyz/6quvynXXXWdb2BnmwdHKPDw6+OCDbYWlabVn3jq/8MILZcCAAQXnh8K0q0hJVUBMU+EEf2eTSnrbRF2k+cOmaenVzvLXi6x2rlZ+8G/ChHIj1uYPKz8pweubkug36Fo/+LlbRrkRJpSbsLZOYcuklIBisacPgaDtx4xyL9KOrRcSkNYXFOjEI6EGFDGWEWMAq+33QqQK+bETwFUDRfMzJL7966nfET/Sd8QNeeKnnQ8Z5fzV8vLCtlvb78p+dEMCUu260RxaeoxVDm655Rb7AvwhhxxiX9IyL8Cvs8469mUt0/ND3FpN5R4AALGKsyuCEo+LOnXqJOeff36xVwMAAJSDuGKs/+axYMGCvGTTneXKjAv8xhtv2K44sxV7xqBBg8R1XXnrrbdsC7dlTZ48WVKplJ0vq0+fPrLWWmvZ/Ezlnvl3s802sxV7WabC7sQTT7Qt5rbccks7T8M8svOYFnxxSSQS8tBDD9l8zX8DAIAyEXOMheWZLta33357+99t2rSRhQsX2v8+/PDDbbxnXoyPE5V7AABgpZgxZD788EOZM2eOHW+vod/85jfsXQAAUDQ9e/bM+3vEiBG2JVyhZs2aZcfDa6iiokK6dOlip2nLmJZ3plKwIVORl13G/NuwYi87PTstbB5TgblkyRL7EGllme44AQAAEF337t1tC71evXrZl7hMDw2mu3TTtbrfBC0eqdwDAKAApheJuHrqaJYu65qI6ZZq6NCh8tNPPy03zXEcO34MAABAc8dY2TxmzJghHTv+bxgNrdXe8OHDVzhesOmSs1yZbkX/8Y9/2B4ZjCuvvFJOOOGEXIXkzz//LDvttJPtOhQAAJSeuGMsLM90v/7EE0/YHhfM2HtmzOV//vOf8u677+aNtRwXKvcAAEDBhg0bJr///e9t/+HLvkkOAABQbKZir2HlnubMM8+UI488MnQeM2aKeSPb9FbQUDqdtm9pm2lBTHp9fb3Mmzcvr/Xe7Nmzc8uYf99+++285cz07LTsv9m0hvOY7VvZVnvPPfec1NX9b3y0K664wo65l11fs41Tp05dqTIAAADK2e23357r0eqkk06SVVddVV5//XXbq9Xxxx8fe3lU7gEAUAjG3Ms9UDrjjDOo2AMAACU9Hsxqq61mPysyYMAAW0lnxtHbaqutbNpLL71kH+T0798/cBkzXzKZlBdffFEOPPBAm2Yqysy4LCa/bL6XX365rTjMdvs5fvx4W3G38cYb5+Z55pln8vI282TzWBnLdhXVFF1HAQCAImLMvSZnxmA2n6yDDz7YfpqsvCbLGQCAVtCdQVyfUvW73/1OJkyYUOzVAAAAZaKlx1cbbbSR7LHHHnLsscfalnavvfaanHzyyfbBTY8ePew833//vfTp0yfXEs90dXn00UfbF6JefvllWzFoumoylXLbbbednWfw4MG2Eu/www+XDz74wLaku+CCC+xb39muRE03md98842cc8458vnnn8stt9wiDz30kO3yKWvRokUyZcoU+zHMGC/mv01FIgAAaL1aeoxVLl555RU57LDDbJxnYkLj73//u7z66quxl0XLPQAAytjaa68t3333XV7aqFGj7LgycRg9erTtltME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G8MJzsuNsaceL+I5V6tsR8rXHxKm/GjVBZmQjgEzEX8kJLRgJmwZ7TxRvrde1B9/IWVo2x66Hf7yebmRA8iWwVTsNazcK4TpStNUqr344ou5CjTT3acZS+/EE0+0fw8YMEDmzZsnkydPlq222sqmvfTSS+J5nh0PLzvP+eefL6lUSpLJpef9+PHjZcMNN7RdcmbnMeWcdtppufLNPCa9sevS0vAEFACAQpgAPc4PAAAAiK8AAACaQpGeYS1atEimTJliP8a0adPsf0+fPl0cx7GVbZdddpk88cQT8tFHH8nQoUOlR48esv/++9v5N9poI9ljjz3k2GOPlbfffltee+01Ofnkk+Xggw+28xl//OMfpbKyUo4++mj55JNP5MEHH5QbbrhBzjjjjNx6nHrqqfLss8/KtddeK59//rmMHDlS3n33XZuX0Zh1aWlaTcs9AABiZV6siqulSmm+pAUAANByYyziKwAAgKLHWKYCbdddd839na1wO+KII2TcuHFyzjnnyOLFi+W4446zLfR23HFHWwlXXV2dW+a+++6zlXC77767uK4rBx54oNx444256WbMv+eff15OOukk27qva9euctFFF9k8s7bffnu5//775YILLpA///nPsv7668vjjz8um266aW6exqxLS0LlHgAAAAAAAAAAAGI1cOBA8UNa+5kWc5dccon9aLp06WIr5sL07dtXXnnlldB5fv/739vPyqxLS0LlHgAABXB8337iEFc+AAAApS6uGIv4CgAAgBirnDHmHgAAhWDMPQAAgPiVwJjGc+fOlUMPPVQ6duwonTt3tuO7mPFkwtTW1tquolZddVVp37697U5q9uzZefOYsWf23ntvadu2rXTr1k3OPvtsSafTefNMmDBB+vXrJ1VVVbLeeuvZ7qwaGjVqlGyzzTbSoUMHm4cZI2bq1Kkxbj0AAChJJRBjIRoq9wAAAAAAABrJVOx98sknMn78eHnqqadk0qRJeWO6BDn99NPlySeflIcfflgmTpwoM2fOlAMOOCA3PZPJ2Iq9+vp6ef311+Xuu++2FXdmvJisadOm2XnMuDVTpkyR0047TY455hh57rnncvOYvE0l4ptvvmnXL5VKyeDBg+34MQAAACgfdMsJAEAh4nxbibeeAAAA4o2xmii++uyzz+TZZ5+Vd955R7beemubdtNNN8lee+0l11xz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+Kp4iLFaJmIslIJWU7lnKiV+//vfy4knnhg4PZPJ2KDIzPf666/L3XffLePGjZOLLrpIWrJ99tnHBm8vvfSSTJ48WTbffHObNmvWLCk1jzzyiBx++OFy1FFHyQcffCCvvfaa/PGPf5RSdc4550iPHj2kFH3++efieZ7cdttt8sknn8h1110nY8aMkT//+c/Skj344INyxhln2IrI9957z34fhgwZInPmzJFSMnHiRDnppJPkzTfflPHjx0sqlZLBgwfL4sWLpRS988479lzq27dvsVcFQBMgxmrZyi2+Moixml85xFjlFl8ZxFhA+SrX+KqcnmOVW4xFfFUcxFgtEzEWSobfyowdO9bv1KnTcunPPPOM77quP2vWrFzarbfe6nfs2NGvq6vzW6Iff/zRN4dw0qRJubQFCxbYtPHjx/ulJJVK+b/61a/8O++80y8H5nzq06eP/8knn9jj8f777/ul7qqrrvJ79+7tt2Tbbrutf9JJJ+X+zmQyfo8ePfxRo0b5pWzOnDn2PJo4caJfahYuXOivv/769pq0yy67+KeeemqxVwlAEyHGannKLb4yiLGKoxxjrFKOrwxiLKB1KKf4qpyeY5VbjEV8VTzEWC0PMRZKSatpubcib7zxhmy22Way+uqr59LM26gLFiywLZdaolVXXVU23HBDueeee+wbp+bNJ9M6xnTHsNVWW0kpMW8Af//99+K6rmy55ZayxhpryJ577ikff/yxlJrZs2fLscceK3//+99t9xjlYv78+dKlSxdpqcwbi+atv0GDBuXSzPlk/jbf71Lf90ZL3v8a84a8eaO04XEB0LoQYxVPOcVXBjFWcZRrjFXK8ZVBjAW0bqUYX5XTc6xyirGIr4qHGKtlIsZCKaFy779M8/+GQZGR/buldg1gxq964YUX5P3337djR5jxL/7617/Ks88+a/v6LiXffPON/XfkyJFywQUXyFNPPWW3YeDAgTJ37lwpFb7vy5FHHiknnHCCbL311lIuvvrqK7npppvk+OOPl5bqp59+sl2TBH2PW+p3uDFM96hmnLoddthBNt10UyklDzzwgP3RY8a4AdB6EWMVT7nEVwYxVvGUY4xVyvGVQYwFoBTjq3J6jlUuMRbxVXERY7U8xFgoNSVduTd8+HAbGIR9zNhh5bpd5iZs3iYwbzi98sordlB7M3jvvvvuKz/88IOU0raYH9jG+eefLwceeKB9Y2vs2LF2+sMPP1wy22EqwBYuXCjnnXeelMt3xryNtscee9j+/k2LRDQv8x03b/+ZAKOUzJgxQ0499VS577777A82AKWFGKtlx1jlEl8ZxFjEWMVQqvGVQYwFlK5yja/K6TlWucRYxFfEV8VCjAU0rwopYWeeeaZtJRVmnXXWaVRe3bt3t0HFsk3Ts9Na4naZwYfN20G//PKLdOzY0abfcsstdoB4M5iyuZkXW2O3JRvEbbzxxrn0qqoqO2369OlSbFGOiekew6x7Q6YV36GHHmqPSyl9Z2bOnCm77rqrbL/99nL77bdLS9a1a1dJJBK5722W+bu5v8NxOfnkk+13fNKkSbLmmmtKKTHdd82ZM0f69euXSzNv/ZttGT16tNTV1dnjBaBlIsZq2TFWucRXBjEWMVZzK+X4yiDGAkpXucZX5fQcq1xiLOKrlh9fleNzLGIsoPmVdOXeaqutZj9xGDBggFx++eX2YbR5g8gwwYUJNhrerFvSdtXU1Nh/TR/fDZm/s28RFVtjt8W85WQCoalTp8qOO+5o01KplHz77bfSq1cvKZXtuPHGG+Wyyy7Lqxwz/d4/+OCD0r9/fyml74xpsWcq9rJvoC17nrU0lZWVdl1ffPFF++afYb4H5m8TYJQS8zbjsGHD5LHHHpMJEyZI7969pdTsvvvu8tFHH+WlHXXUUdKnTx8599xzqdgDWjhirJYdY5VLfGUQYxFjNZdyiK8MYiygdJVrfFVOz7HKJcYivmr58VU5PccixgKKp6Qr96Iwb86Yfq/Nv6b1yJQpU2z6euutJ+3bt5fBgwfbAOjwww+Xq666yvZRbvrNNs2Jl22F1VKYYM706X3EEUfIRRddJG3atJE77rhDpk2bJnvvvbeUEhOAmnHqRowYIT179rTB0NVXX22nme4gS8Vaa62V97c5t4x11123pN4MNhV7pq94cxyuueYa+fHHH3PTWvLbQ2eccYb9PpiWkttuu61cf/31dpBuU6lUSsx15/7775d//etfdhyC7JgJnTp1st/zUmDWe9kxbNq1a2cHUC/FsW0A6IixWq5yia8MYqziKocYqxziK4MYC2gdyjG+KqfnWOUSYxFfFR8xVstBjIWS5LcSRxxxhG82d9nPyy+/nJvn22+/9ffcc0+/TZs2fteuXf0zzzzTT6VSfkv2zjvv+IMHD/a7dOnid+jQwd9uu+38Z555xi9F9fX1dp9369bNbsugQYP8jz/+2C9l06ZNs+fZ+++/75eSsWPHBn5fSuGScdNNN/lrrbWWX1lZ6W+77bb+m2++6Zcabd+b41LKdtllF//UU08t9moAiBkxVstWjvGVQYzV/Eo9xirX+MogxgLKT7nGV+X0HKscYyziq+Igxmq5iLHQ0jnm/4pdwQgAAAAAAAAAAABgxVp+B8QAAAAAAAAAAAAALCr3AAAAAAAAAAAAgBJB5R4AAAAAAAAAAABQIqjcAwAAAAAAAAAAAEoElXsAAAAAAAAAAABAiaByDwAAAAAAAAAAACgRVO4BAAAAAAAAAAAAJYLKPQAAAAAAAAAAAKBEULkHlKlvv/1WHMeRKVOmNEn+Ju/HH3+8SfIGAABoqYixAAAAiK8AoNio3AOayJFHHin7779/0fZvz5495YcffpBNN93U/j1hwgRbITdv3ryirRMAAMDKIsYCAACIF/EVAJSeimKvAICmkUgkpHv37uxeAAAAYiwAAIAWi2dYABAdLfeAIpg4caJsu+22UlVVJWussYYMHz5c0ul0bvrAgQPllFNOkXPOOUe6dOliK+lGjhyZl8fnn38uO+64o1RXV8vGG28sL7zwQl5XmQ27jDL/veuuu9r0VVZZxaabt7KMtddeW66//vq8vLfYYou88r788kvZeeedc2WNHz9+uW2aMWOGHHTQQdK5c2e7zvvtt58tFwAAoLkQYwEAABBf8QwLQGtA5R7QzL7//nvZa6+9ZJtttpEPPvhAbr31Vrnrrrvksssuy5vv7rvvlnbt2slbb70lV111lVxyySW5SrVMJmO7/Gzbtq2dfvvtt8v5558f2kXnI488Yv976tSptrvOG264oVHr63meHHDAAVJZWWnLGjNmjJx77rl586RSKRkyZIh06NBBXnnlFXnttdekffv2sscee0h9fX0BewkAACAaYiwAAIB4EV8BQMtFt5xAM7vllltsZdvo0aNtC7o+ffrIzJkzbYXZRRddJK67tM69b9++MmLECPvf66+/vp3/xRdflF//+te2ku/rr7+24+hlu968/PLL7TStewPTms7o1q2bbV3XWKZFoGkl+Nxzz0mPHj1s2hVXXCF77rlnbp4HH3zQVgLeeeeddpuMsWPH2nLMOg4ePLjg/QUAANAYxFgAAADxIr4CgJaLyj2gmX322WcyYMCAXCWYscMOO8iiRYvkP//5j6y11lq5yr2GTPedc+bMybW+MxWEDcfUM918NtX6mrKyFXuGWf+GTAvEr776yrbca6i2ttZWQgIAADQ1YiwAAADiK55hAWgtqNwDWqhkMpn3t6kMNK3j4mZaCvq+v1w3m1GYismtttpK7rvvvuWmrbbaaiu9jgAAAHEhxgIAAIgX8RUAND8q94BmttFGG9nx70yFWrb1nhmjzrR6W3PNNRuVx4YbbigzZsyQ2bNny+qrr27T3nnnndBlzJh52fH6lq18M2PwZS1YsECmTZuWt76mLDOPaT1ovPnmm3l59OvXz3bNabr87NixY6O2AQAAIE7EWAAAAPEivgKAlmvp4F4AmsT8+fNlypQpeZ/jjjvOVpYNGzbMjmX3r3/9y46td8YZZ+TG21sRM7beuuuuK0cccYR8+OGHtnLwggsusNMadvfZUK9evey0p556Sn788Ufb2s7Ybbfd5O9//7u88sor8tFHH9k8zRh9WYMGDZINNtjAppvuN818559/fl7ehx56qHTt2lX2228/O91UDpqx9k455RTb1SgAAECciLGIsQAAQLyIr4ivAJQWKveAJmQquLbccsu8z6WXXirPPPOMvP3227L55pvLCSecIEcffXSucq4xTOXb448/bivottlmGznmmGNyFW7V1dWBy/zqV7+Siy++WIYPH25b+5188sk2/bzzzpNddtlF9tlnH9l7771l//33txWHWabC8bHHHpMlS5bYcf1MWZdffnle3m3btpVJkybZ8QIPOOAA+2aX2SYz5h4t+QAAQNyIsYixAAAA8RXPsAC0Zo6/7GBbAEqSab234447yldffZVXOQcAAABiLAAAgJaCZ1gAsPKo3ANKlGlN1759e1l//fVthd6pp54qq6yyirz66qvFXjUAAICSRYwFAABAfAUALV1FsVcAQGEWLlwo5557rkyfPt2Od2fGxrv22mvZnQAAACuBGAsAACBexFcAED9a7gEAAAAAAAAAAAAlwi32CgAAAAAAAAAAAABoHCr3AAAAAAAAAAAAgBJB5R4AAAAAAAAAAABQIqjcAwAAAAAAAAAAAEoElXsAAAAAAAAAAABAiaByDwAAAAAAAAAAACgRVO4BAAAAAAAAAAAAJYLKPQAAAAAAAAAAAKBEULkHAAAAAAAA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AADKtuOPPz7sd+6Mff/99xtuw6k1hw8frua+a9GiRbCcG/P69OmjOio5nU7KzMyk+fPnqxScOr/++is9/fTTUY3aY3379qW7776b3nnnHZW5ic/jq6++olGjRqmUoMkKjXsAAAAWYORefPzxxx909dVXqyCMe14xzsvOvbzeeustqlu3btT7euSRR2jo0KHB/QAAAED8YeSeOVw55fV6tTFT6JwyAAAAkNpiHWPt3LlTze8bEM2oPZ57j+fLW7FiRVj52LFjVd3OZ599pubce++999S0KtyJqWXLlmHrcuclTtF55ZVXRp2lYNKkSerY3CDJcRN3AOf/ea5iTgGarOzxPgEAAACAaN14443kdrvVqL39+/erB//MFVu8zAwO7nh7AAAAgGTVrVs3mjp1avB37o2ek5Ojes937949rucGAAAAZRc37IU+IjXuDRs2jD766CNaunRpWMfsrVu30vTp0+mll16irl27UuvWrVUcw2nHn3nmmbB97Nq1i7p06aI6eD/33HNRnaff76fdu3fTtGnT6LffflPn8J///Ic2b95Mr7/+OjkcDkpWaNwDAACwwOe3xfQB0Vm+fLmaMLlp06bBMv6Z0zF88cUXpm4jB3gAAACQWBBfmcPpqDijAfdA57lpuAd6ICXno48+GnH7V199lT7++OPg73fddZfKasCVZr///ruFZxAAAAASUbxiLK574YY9TrO5ZMmSEvPe5eXlqf/t9vCmKm50447cARzb8Bx8PC3Lyy+/XGJ9o+Nzek/OasAj97jzE48KbNKkSdTXkKjxUsqk5fRtyyLKyAgrS8/VvxAdBfJ+HIX6cue/r8ES7B654tDhlo6h38aTIbxxDN5PNp9+X36hQdrn0u/Mb/BesQmXaC+ZGUTxptlMXbc6hlu/zOvSr+/KFXclPyfCdAR+h/58fRYa9aX7KN13b/hLtthG+mK79Hx45F05hWv3OYXzKicco0g+hs1n7j0lvtcKDF4nXuG9kyV91OlvsKsgRzyG3yU88U79k2srMnjzhHxBhbFLxxDKCw1uvCZFD/MXCjfYpn/ObUa9WKRlDuGeyHsiv81u7hh2zd58x6bfCrcLxaptCG1M0eNAjEfuFcfpFOrUqRObJwRMsZOPHMXf2Wbbqy28l3xCHzWHFJgYHl//eeyNYz84h/RFr66RzJ2vcH2mnye1r9K/J3ab1/Q9EUn3ysJ1eIXvKJ+wL5dBQJrp0H93V3Dma8v3FulT6x1wZIrHOOwQgnFR+HxdAYVugz8ZhbeblO5HqgjwuuU4w+8WnqtCYRuP/MKWPh78QgBt8wpxkYW3iM0pHFwKB90GR/Hpl9mL9OXCy+rfXQl/T3k1T67N4O+JRIyxUiW+4tho3bp1NGfOHPU/j9q74YYbqF+/flSunPCHW7FMBtxxiq1atUr1kJ8yZYrq1T5ixAiaN2/eMbgKKM5OfvUoKTbf6Ubfg3bxGBZiACkGIXOxjF2qULDCwmdDWYm94hlj2YUvYbvBa8RsjJXllCt0YxVjmY+vzMdYfuF7Xi0TX7/mYix/kUEdT6FQlyPEJoYva2mh3+TqRvXSUowlFDuEeEmKr6zEWF79U/7vYfzR39uyEmNxOszZs2fT+++/r1KG8yg6VrFiRRWrNGvWTDW+3XTTTarTEs+7x2k5Fy9erOKR0Ia9+vXrq3X27t0b3H+tWrUMj8+NgNyQt2/fPlMNeskQL6VM4x4AAAAkv8cff5xuvfVWFUhxiga2evVquv3226OeSDlg48aNaBAEAACApMUdnrhCjCuWuDGPH2bxfDlcoca4Iu3yyy+nIUOGUIcOHVQlGgAAAMDRCDSKFY8rePTdwIEDyeVy0YIFC2j06NHUo0cP1VGJYxMeLRdIMc4Nfb/++qt6hKb0jDYr0yOPPEJ33nmnOpcWLVpQWYmX0LgHAABgucdTbHpXpUrP8ljgwI9TNpxxxhnkdP4bxng8HvXz9ddfrx4BxefT455ijRo1ojPPPFM1CP78888qdRUAAACUvRgrFeIrrgzjVJxHIysrS/Vkr1evHi1atIhGjhypyjMyMig/32DYJwAAACSVeMVY0TS+8Yi6uXPnGtYF8cOq6667TtUl8Xx+aWlpJbIbFK8/SpZ4CY17AAAAFnBAFLvGPcy5F62pU6davs/cqDdo0CCV433UqFGqlxgAAACUzRgrVeIrTnXFc+u98MILwY5PZpx//vl04403Ups2bVTHp0AP+Z9++knN3QcAAABlQyrHWFOPoi4pkeMlNO4BAABA0hgwYICl7Xbs2KEqvHiyYw7K+H+enJnLuecVAAAAQDL67rvv6PPPP1e9yFu2bEnly5cPWx5pDhhOdT5mzBiVbop7zPM8N2zNmjV09dVXl+q5AwAAACRyXVKix0to3AMAALCAkwrEKttTCmSNOiqHDx+mChUqBH82ElivuPHjx6v///jjD/ryyy9VQx+X2Ww2eumll0rhrAEAACCeMVaqxFeVKlVS874czfbTp08vUT5hwoSjPDMAAABIJKkcY+3YscNweaRO34kaL6FxDwAAwAKk5Tx2KleuTH/99RfVqFFDBVTcIFfy+fCrcq/Xq91HIAXnhRdeSB988AFNmzYNaTkBAAASUCqnjLLiaNOMf/HFF4bLzznnnKPaPwAAACSGVI6xGjRooK1LCpDqkhI9XkLjHgAAACQ0niOvSpUq6uelS5da3s+zzz5Lp512Gl100UX09ddf04wZM+jmm2+O4ZkCAAAAJJfOnTuXKAut/NJVdgXismjx/r7//nuqX7++xbMEAAAAsO6HH34I+93tdquyyZMn00MPPZS08RIa9wAAAJIwLyfn+3788cdp9+7d1Lp1a3r66aepXbt24vrvvPMOjR07lrZv305NmjShRx99NDgBsDoFv1+lqXz++efp4MGD1KFDB9X4xesG7N+/n2699Vb68MMPyW63qxRQTz31FGVlZanlBQUFNHToUJVzfNOmTXTJJZfQe++9F3Yey5Ytoy5dupQ4Px6ZV6tWLe25d+rUSfuzWZwHPSMjQ/187733Ul5enuV9AQAAQClJ5ZxRFjRs2NCwJ/pvv/1muP2BAwe0lV0cN0qVXRwrTp06lSpWrBjx/DjG/L//+7+IPeIBAACglKVwjNW6desSZW3btqU6deqourXevXsnZbyUMo17Gbtt5EiPbsios8Bomf7Va3ebPyebT78vn/Cs+O3SjuRj+Jy22OzL4E1r95grd+X65J2ZvFfpHr/p87UJ20jH8DuEeyjcWyPSqGVvmv4J8WTajskHrE14SnwufbknX39e3nTzx0g7oj9hV56+3Jknv34cBfplfpfd1HPorVROPIb0OiG/UJ4lP4f23EL9MdzCB7lwfZZIFQDSdRjtKlN/v/y5+oYTf1GRuC+/R/9h6s/R3xObw1FyXb+8/7Jizpw5NHLkSJo5cyadccYZKli44IILaMuWLSp1ZXErV65UDVsPP/ywanCbPXs29erVS/UIatGihVrnscceU6kqX331VVVJxAEK73Pjxo3BBrF+/fqpRrjFixerQGbQoEE0ZMgQtT/GQUi5cuXotttuUxMMG+FzDZ0fT3feUuopbky88sor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ve+++06ltaxUqRJVrlyZLrjgAjUKLloFBQVqnmLuLP7BBx+EPZI1XjI1cu/ll18+qoMBAACUGbFsmEMDX9R4vrx//vnH8q1etGgRnX322SoQHD58OB05ckQFhDwa8JNPPqF4QYwFAAAQ4xgL8VXUFV3PPfccffbZZ6qSyeVyhS2fPHmy4fa8fOzYsTRs2LBg2vMVK1bQ0KFDVcw2YsQIiifEWAAAAPGNsZYvX0633HKLauDjTtX33nsvdevWjTZu3Ejly5en448/nv7666+wbTg2efzxx1XH7tBO2T179qRnn31W7Wf8+PGqPmfnzp0l4pfiFi5cqAal6eqTopniJVHjJcy5BwAAAEnj0UcfpbvuuktNOMw9nooHVBUqVDDc/oQTTlBBXZcuXchut9Obb75J6enpakQfB5UAAAAAyYorvnw+n6nY6Mcff1TZENiGDRvClkWTLurpp5+mGTNmhGVx4oq3k08+me6///64N+4BAABAfHEdTCieu45H8K1Zs4bOOecccjgcVKtWrbB15s+fT1dddRVlZWWp3zdv3kz79++niRMnqsZANn78eNXQ9vvvv1Pjxo0Nz+HWW2+lK6+8ksaNG0c1a9Y0fQ2JGi+hcQ8AACCOExEH9gXROe+889T/nIohlN/vj6q3FePgj9MwnH/++XTGGWeonzklAgAAAJSdGCtV4qtt27apXuDLli1TvcrNxkZHO98w97TXzUHDZcV74QMAAEDZibEOHz4cVs4dp/kRyaFDh9T/VapU0S7nRj+ekuWZZ54JljVt2pSqVq1KL774ohr5x/HNiy++SM2bN6cGDRpEPObff/9NI0eOtNSwl8jxEhr3AAAArEBazriwElC1adNG25OKg85du3YFUyKw77///qjPEQAAAJIvZRTPBzNv3jzVM5w7/XBlC2cM4MqkwHwwDRs21G779ttvq97g3BN90KBBYqUS91LX4Uop7nVe/HxGjx4d8byvvfZa1ZD30ksvqQqraHqPxxL3lOfr54q2UHPmzKEmTZoc03MBAACAYxdjBUbQBfBIOh6FZoQzDPAUKVwP06JFC+06gUa70MYwnq+POzLxfH0PPPCAKmvSpAl9+umnapqVSK644gq1PWdziofSipfQuAcAAABJo1OnTqa34eAPAAAAoLTng+nTp4+aDybUwIED1Yg6qWEvgNNMDR48OKwSKxrr1q1TPdwDjZDR6N27t2qI5JSd/LMRbvA0MmHCBHXdX3zxRbDD1FdffUWff/65qsQCAACAsonnugtN/x3NqD2OtTitJc83p5Ofn0+zZ89W89MVL7/hhhtUrMHTq/DIvSeeeIIuvvhi+u677yJmY5o+fbrqiPXll19qp3i57bbbkjJeSpjGvUceeYTuueceuv3222nq1Klhy7gXWvfu3VV+Vs63aqWSzlZQSDZ7sTK/vqna7yy2Yii7wTKT6/uFRX5hG3tevn793Dz5GIWFZIpPaL73h+ftD1+k38ZmN9ljsPgTFEral9Ar0eZwkGnC64GEfdnS5Ik67dKHmUN/jQ5h0k9/hnwMv0t/Xj5hG095eV/Se4Fy9cXl/9aXOwrl14m9SJ8OxvmP/iC2wiL9jorc4jFISDnjzy8w9/6Q3gexJryubVKPE5czdvdESs/j8ejXN7gnRsvKMqTljK+8vDzasWMHFRUVlUi5WRz3HktlpR1juWz/PkJ5hY8Fu9BN0GcQMrj9+oVeMldutKzA74pJOXOQ/rvQYTL/icvmkZdR5PSz0XDY5O9tiVcKYI22MXl8h5XupNK1COcrHtvgnojXLpyul+R7JS1z+4X4zuB1LZEG7zgc5p53t0eOq6Vnyi+8qaWQ02aXn3O7U3++PuGzgVzyvnymY5bYjYDyCR8bnkx9eWFlg9jLKfz9JT4h8nl5hfDSprm/0rplJS1nLOaD4Yql0MqlvXv30pIlS1QP9Ei4Ma/4/qPBjZFcuWamca9ixYrBEX7889G4/PLL6dtvv6XJkyfTe++9p8q4tz2XcfaEsqw0YyxdfBXLGEuKr6zGWJJYxVhpNjn2kWIshxCBSDGWFMMZ3d94xlhG0aDZGEsqt/KcpxnEsNpjGNQFuv36Lx6vwes3VvfXLryu0pzynZfiNZuwLynG8htcnxj/COxi3GcQpwp1wP404bNBCvCYeLomn0ODp8+bbjMXY1UVPi8zDd5V0ukW6k/M6zC4J5rXoi9JYyxu9Io0t28oTiPO06JwA1fdunW167z77ruqvid0bjrGDX6cRWHVqlVk/2/byezZs6ly5cr0/vvvU9++fQ2PzQ2CixYtooyMDDWCLzTTAf+sa9xLhngpIRr3uHV11qxZ2go5xkHSsU4tAQAAkMg49zj3FN+9eze1bt1aTc7brl07cf133nlH9XziYIiH/HOaKa5wCK2A4Eaw559/ng4ePKh6EvFkv6HpAXjyYp6E+MMPP1TBFAcnTz31VLBCi3ulDx06VFWCbdq0iS655JJg0BKKAynOdf7TTz+pXvBjxoxRvdqjwZVknO7qk08+0S6PZs69VIIYCwAAUt2xnA+muNdee40yMzNVKqhoGoo4zVS9evXommuuoREjRkSVZuqFF15Q8deff/6p0lsV74muq2d5+eWXtT+b5Xa76aabblIx5n/+8x9KJYixAAAAosP1TVyXxJ1duD5ISnPOuENUz549qXr16mHl3ODH9VChbUT2//7OqT4jue+++9ToOU55HmgcjCQZ4iXzXW9jLCcnh/r166cqE7mltTgOlp988kmVPx4AACDhcpXH6mEC5+TmxjFujOM54rhx74ILLqA9e/Zo11+5ciVdffXVKoXBDz/8oHoO84NTIQQ89thjNG3aNJo5cyZ98803Kv0U75Mb7AL4+5ob5BYvXhzsbTVkyJCwhjXurc49ns477zztuWzbtk2lTejSpYv6judc6zfeeKPKkx4NXp8bH/kc+VjcG/rVV19VjZAffPCBdhuulPvnn38oWlypVnzem2SEGAsAAJJSjOMr7kjEva0DD57LLhKr88Ho1uGGukipojh2euutt9Tcwlz5M2nSJLrrrrso2o5PW7duVZ2feBTfKaeconqAB/6PhNNccYVZAMdA3MGae7dHwg2Jc+fOpVSDGAsAAJJSnOqwOBUnN2rxSDvOVMCd1PnBMUioX3/9VdUzcR1Rceeffz4dOHBA7Ys7k3Pd1KBBg1RHKK5fioSzPnFazGgb9pIlXor7yD1+QriSjysBH3zwwbBlfMM4EOaecFbSUwAAACT8RMSBfZnAw/h5ThYOZBg3yH388ceqIwz3QiqOR9fx/C933nmn+p17hXMDHecc5225FxUHJTyC7tJLLw32NK9Zs6YaecfpDTh44oY07qXctm1btQ6PFuTRf5znvE6dOqpBkEf7BXKHcyNccXw87qXFHXcYV4pxrvUpU6aoxsRIOLUVp1zgc+CgrH79+irI41QQXFnHMUVxfB480i/aNAr79u0rEyMAEWMBAEBKx1j+Yz8fTChOG8Xx0+uvvx7xeNxpK3SkXVpammrk49gm0vlef/31qhGP001x7GY26xHHfjyPDI/+45iJM0Hw8bljFMecN998s+H23GGM40UeaZgq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SNavX69NLcqjAf/5559SOmsAAACIGlJGxcShQ4fUI5JXX32VqlWrRhdffHFwHmbuRX7SSSfRm2++SfXr1zfcnkcKnnrqqernQIp03h8/eFlANBkWAAAAoBSlcIxVsWLFsN/tdjs1bdpUTU3TrVu3pI2X0LgHAAAASaNjx460du1aeuSRR1R6Te71xAESp1Lg3yOpVKmS6p3VsGHDsHJOY3XccceV4pkDAAAAxN60adO0PdFff/111SEqmo5TgTRRHE8988wzNGXKFProo49UWk9OY25k6dKlR3kFAAAAAKXr5ZdfPqrtEzVeQuMeAACAFRi5FzcnnHACPf/885a27du3L9199930zjvvqB5RPp+PvvrqKxo1apRKhQAAAABxlsK9yq3giqXiPdGrV69OAwYMoHvuuSfi9jt37qTGjRurn9977z26/PLLaciQIdShQwfq3LlzqZ03AAAAHGOIsZScnBxVF1Q8pXkyxkto3AMAALCAB8rHKrkQkhSZt2fPHvUoHpC1atUqYm+rW265hY4//njyer0qhQL/f80119CYMWMsnAkAAAAkYoyVKvHVtm3bjmr7rKws2rdvH9WrV09lRAjM+ZKRkUH5+fkxOksAAACIt1SOsbZt20bDhg2jZcuWUUFBQVjGA+74zfVCyRgvoXEPAAAAksaaNWtUT/RNmzapICxUNAFZWlqaGvU3btw4Nf8e99hq06YNNWnSpJTPHAAAACDxnH/++XTjjTeqeOjnn3+m7t27q/KffvqJGjRoEO/TAwAAADhq1157rapDeumll6hmzZqm57ZL1HgpdRr3CoqI7MXKilUKBthcLnk/3vARAkGFhfryIre4K7/Hoy8vKtJvILzobPbiFxa2UDi4cB1WxHJfcTyG32t2fa/5+26SzS5/0Ph9/pjty16gf/1KV2F36Je4ypUjs/yHj+gXOBym9+WT3jvCe03cj9vc+sfq9W4T7onha0HYl+HrF6KDtJxxcf3119OJJ55IL774oqWAjCdL5hScPHKPHwHc0+rxxx9XjX5gjoNs6hHGpv9cShP6+BUJMRlzC9sc9KVpyzcX1hL3tTm/jrb86336YPyvQ/rUHB6P/B21NruutjzDKceEOg67+djHLtx3K+un2fXfEw3K79eWNy+/S9xXdedhbfmvhTW15TsLqmjLPT7zsUHNdP2x66bpr8Nlk78f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v8kmm8gTTzzRYnoQBHL++efLyiuvLHV1de65/NFHH7WYR7sa0hdy9Fncq1cvOfroo13hRnP/+te/5Hvf+55bz+DBg+XSSy9tMf2uu+6SRCLR4qPzIn7EWACActSe8RXQFsRYAIByRIxVedq1+dv9998vb775pusyqjVffvml66f8uOOOC52vsbHRfQoWLFgQy7YCANBRjBs3TkaNGiU33XSTq9jTMT723HNPmTJlivTv33+Z+V9++WU59NBDZezYsfLDH/5Q7rvvPjf+hz6DtaW80kq4a6+9Vu6++24ZMmSInHfeeS7P999/v1j5phV7M2bMkAkTJkgmk5GjjjrKPZc1v8Izd9iwYa5iULftnXfekZ///OeuIrD581srB3VbC7SCD/EixgIAAOj4MRZlWAAAoOxa7k2fPt0NOnzvvfe2+sa+FhZq6z4de++CCy4InVcLLnWg4sJHWw0AABC7IOZPBFdeeaUce+yxrnJNn41akdalSxe54447vPNfc801stdee8npp58uG2ywgStk2HLLLeW66677ZleCwFUQjh49Wvbff383Bu4f//hHN87tww8/7Ob54IMP5Mknn5TbbrvNVSjuvPPO8oc//MEVcOh8Sp/pTU1Nbjs22mgjOeSQQ+Tkk09229ucVuYNHDiw+BkwYEBp5wBexFgAgLLWTvEV0B4xFmVYAIAVhhir4rRb5d7kyZNl9uzZrnBRx8/Tz6RJk1yrAf3vXC7n5lu4cKErkOzevbuMHz9e0ul0aL5nn322676z8NHgCwCASqGVZ/oMbd6ddTKZdH+/8sor3mU0fenur7VVXmH+Tz/9VGbOnNliHn1BRivxCvPov9oCb+utty7Oo/Prul977bXiPLvssotUV1e3WI+20vv666+LadqV5+qrr+5ewNHKxPfeey+GI4MCYiwAAIDyiLEowwIAVDp9kWWbbbZxz0XtbUp7kmrem9PUqVOXGb6l8NEhZpb21VdfyaBBg9z0efPmSWfWbt1y7r777q67rua0BYKOB3TmmWdKKpVybzppoWBNTY08+uijbRqTR+fVDwAAy1MiCNwnrrx8XUn7nmnavY8WHCzd2k3//vDDD735a8Wdb35NL0wvpIXNs3SXn1qI0bt37xbzaJeeS+dRmLbSSivJeuut51r2aetAfQnn8ssvlx133NFV8Glwhu+OGAsAUM7iirHi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nuuSsy3bAgAAAAAAACAclXsAAJSZUaNGya233ip33323fPDBB3LCCSfI4sWL5aijjnLTR4wYIWeffXZx/lNOOUWefPJJueKKK9y4fDqO3z/+8Q856aSTim8Fn3rqqXLxxRfLo48+Ku+8847LQ7sFGj58uJtngw02kL322kuOPfZYef311+Wll15yyx9yyCHFLlB/+tOfusrHo48+Wt577z0ZN26cXHPNNW5727otAAAAAAAAADp5t5xLj100ZsyYsh/DiP3oWCrlfFTSvrAfHUulnI+O5OCDD5Y5c+bI+eefLzNnznRdZ2qF2YABA9z0adOmuS59CnbccUe57777ZPTo0XLOOefIOuusIw8//LBsvPHGxXnOOOMMV0F43HHHuRZ6O++8s8uzeddY9957r6uE23333V3+Bx54oFx77bUtuup66qmn5MQTT3StC7ULUd1GzTPKtqD8VMr3nP3oWDgfHQ/npGOplPMBoPK/45WyL+xHx8L56Fgq5XxU2r6gsiWCIAjaeyMAAAAAAAAAAAAAtI5uOQEAAAAAAAAAAIAyQeUeAAAAAAAAAAAAUCao3AMAAAAAAAAAAADKBJV7AAAAAAAAAAAAQJnoNJV7l1xyiey4447SpUsX6dWrl3eeadOmyb777uvm6d+/v5x++umSzWalI/v3v/8t+++/v/Tt21d69OghO++8szz33HNSrh5//HHZbrvtpK6uTlZaaSUZPny4lKvGxkbZfPPNJZFIyNtvvy3lZOrUqXL00UfLkCFD3LlYa621ZMyYMdLU1CQd3fXXXy9rrLGG1NbWumvp9ddfl3IzduxY2WabbaR79+7uXqTfgylTpkg5+93vfue+C6eeemp7bwqAmBFjdXyVFF8pYqz2Ue4xViXGV4oYC6hMlRpfVVo5ViXFWMRX7YcYq2MixkI56DSVe1op8ZOf/EROOOEE7/RcLueCIp3v5ZdflrvvvlvuuusuOf/886Uj++EPf+iCt2effVYmT54sm222mUubOXOmlJuHHnpIDj/8cDnqqKPkn//8p7z00kvy05/+VMrVGWecIaussoqUow8//FDy+bzcfPPN8t5778lVV10lN910k5xzzjnSkY0bN05GjRrlKiLffPNN933Yc889Zfbs2VJOJk2aJCeeeKK8+uqrMmHCBMlkMjJs2DBZvHixlKM33njDXUubbrppe28K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2W677aSmpkYqVeHa7+hBYDkZNGiQu2622GKLkgLy5hWFBRtuuKHLc6211op5awEAcSCWbZ0+F/VZps/JuK2ogpPlsR6rIGbhwoVy6qmnylZbbSV9+/aVuro6WXfddeW8885z0wAAKCAOQZRy0VLLPlfEddYR9evXz8Ww+okj/v2u5XBWPFpqWRSAZVV50tDOtFC8cCNesGCBfPDBB+6/11xzTXejLhSgo3NqamqS6upqee+999zfK6+8srz//vvtvVmIeP5W1HJhjjnmGPeJ0w033BBrfgCA8lPusez48ePbexPKyldffSXXXHONe8lMXzj7/PPP5aOPPpKLL75YJk+eLE888UR7byIAoBMp9zgEnUucZT377ruv+3T0+Hd5lEUBnVaAFh5//PFg++23D3r27BnU1dUFa621VnDQQQcFc+fODY444ohAD9nSnzFjxrhlGxoagvPPPz9Ye+21g3Q6HfTr1y846qijgjlz5hTz13l1mdVXXz148MEHg3XXXTeoqakJdthhh+Bf//rXMmfjueeeK67nzjvvdGm33Xab+7tLly7B/Pnzi/OOGjXKpa+33nru78L27rrrrsF1110XrLbaam5de+21VzBt2rQW6/nTn/4UbL311m6fu3XrFuy5557BW2+91WIe3WbNT/MNc+WVVwabbbZZsNJKKwVVVVVB3759gx//+MfBlClTWsz373//Ozj00EODAQMGuOO16qqrBr/61a+K0xcsWOD+XnPNNd303r17u+1asmRJi+0pHP+l97mgcPx+/etfByNGjHDHTc+rnusPPvgg2GmnnVyanoP3338/dN8+/fTTFueilOvI2k7Ns7CtBTpd//7Zz37mtl+vqTXWWKOYvvQnzuM/b9684OSTT3bXTWH6aaedFixevDh0333nQq8/PV96be27777BjBkz2nz9Nf8ONP9ofq358MMPg/32288dt+rqarcPev2/9tprxXneeecdd3z0+tL9HDJkSHDWWWcVr7Pm56H5ta/Xkn7XdXt1OT1OI0eObPGdbH4Mfv/737v1JxKJVrfbOu9Rzu+4cePcvtTW1gZ77713cM899xSPnR7Tpe9HBa3l3/w6bf4pXMu+Y/XVV18Fv/zlL4NBgwa5PPv37x8cdthhwWeffVacp/m2PPDAA+4+pt/L733ve+48AgDahlj2u8eyS8eYzWORhx9+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j+/Xisz/u1wv3/V7CQCdAbFs/LGE9ZzyPaOOO+44F6NrXK4FJRoPPvXUU/Lggw+62CPMz372M/fikMaIWkihBU4au2hBie/563tetvez8pVXXnFxl8ZEWlF3//33FwvbSonL23t/AADREIdQptbRn98vvviiKzfVbdQYUWOuRYsWyQcffLDCyjzCyojiKINbEbShhL6EpnGcvtCv3/233nrLlSWtqGMYFpMDnQkt95pZsGCBu6GfdNJJcs8998ibb77pWnup559/3v3b/Afq4sWLi/+tb30UnH322e6tCv3og+OCCy6Qo48+usWB//rrr2XChAnuv/Vf/VvpTbGtCq33brzxRvcWiBYCHHDAAcvMp4ULhQfVuHHjlmnRp28Gq7322sv9KC9su+Z77rnnFufffffdXYGF7p+lcDNXf//7391bNWeeeaZZATpp0qRiKzL1z3/+s8V0fRvk6quvLk7X/SjkX3hLufAGjBYkaCVOOVxHhW3Xt2608kQffuPHj49cwbm8jn/hetbtuuGGG4rXhB7f008/vUXrtu8iyvVXmK/59641L7zwgvz4xz92b43rsdfWgc3PQ2E/db7//ve/7r/vu+++4vJbb721N99CKwCtANNzqMewcI6/y/lrbbm2nt/CW1svvfRS8a25P//5z62ur635W/dBn8Ix1haaDz/8sPtv/U5MmTIl9BgDAKIjlv3usex3oW9d6zP48ssvd8/Rxx57rNii/quvvmrxDF36+VmILfTtca0I1BeSNLYvlbWe76KQpz7Tly480Thjt912c/H4yJEj3TPfqtgDAFQm4hDK1OJixTGF8gUtT9ByBaUxR6FHqEL5glVmoWU3hRhGy0o1dhsxYkRs2/3444+7ykKlL/cXKuy0NV5YWU9byuD69esnQ4YMcfPdddddxbKeTCbjXghrjeZVaNnWvGxYe6Sy+MrhNKYt9C5x4YUXupfB9YUuX7lXW+NRfbG8sHyhTE4bQGg8rCg3AmxU7jWjN0btolDf3th0003dj3/9Ya70b6VpBfqGx/bbb+8K0IcOHVrsZlNbvuh8+uNe3yTQ7vimTp3a4sBr95D777+/u4lqs+1CC6AoDxVdjy5TuElql4O+bh91XXqj1O05/vjjXZo279bt1Rutdh2krrrqKhk0aJCbpsdAm7jr28bNu9nRB6h2L2nRdRS6D9TKGq1A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I69kSNn34ky0svpcPtoxCVZMj0TEWJWn01TuAQBQCZ588skWf2trOq1Imzx5suyyyy4tKvO08s7nqaeecpWBTz/9tAwYMEA233xzueiii+TMM8+UCy64QKqrq12F3ZAhQ+SKK65wy2ywwQby4osvylVXXVWs3NOuMrUS8aijjnJ/6zJakXfHHXe4cfLmz58vt99+u9x3332y2267uXnuvPNOl9err74q22+/fZv2efHixa5SU1sbNjW1/KFx8sknRzp+AAAAAAAAQLmjcg8AgBJE7U6ztbzUggULWqRrd5j6CaMVaKp3794t0rW13T333OMq+Pbbbz8577zziq33XnnlFdlkk01cxV6BVtidcMIJ8t5778kWW2zh5tljjz1a5KnznHrqqe6/tZJNKxTPPvvs4vRkMumW0WWVTs9kMi3yWX/99WW11VZz87Slcu+tt96SffbZR5YsWeIq+XQ/v/zyy2J3olTuAQBQWeKKseKK0wAAACoBMVblYcw9AAA6yJh7gwcPdl1XFj46vl7oNuTzrrJtp512ko033riY/tOf/tRV7D333HOu8u1Pf/qT/OxnPytOnzlzZouKPVX4W6eFzaMVkPX19a6CTbv39M3TPA9tBdirVy9zntZot55aOfn1119LXV2da/H32WefyVZbbSWXX355m/IAAADlgzH3AAAAiLHQOlruAQDQQUyfPl169OhR/Lu1Vns69t67777rusts7rjjjiv+t7bQ03Hydt99d/nkk09krbXWknLy9ttvy8033+xaBaZSKWlsbJQ111xTLr30UjniiCPc+H0AAAAAAABAZ0LLPQAAOkjLPa3Ya/4Jq9w76aST5LHHHnOt8wYNGhS6rdttt5379+OPP3b/aleds2bNajFP4e/COH3WPLpd2oKub9++rrLNN0/zPLT7znnz5pnztCadTruKPaXdcOq4e0pbNmplKAAAqCy03AMAACDGQuuo3AMAoINU7rVFEASuYm/8+PHy7LPPypAhQ9rU+k1pCz61ww47yDvvvCOzZ88uzjNhwgRXcbfhhhsW53nmmWda5KPzaLrS7ja1a8zm82g3ofp3YR6drpVzzeeZMmWKq6ArzNMaHf/vjTfecP+96667yvnnn+/GE9TuSJt3RQoAACoDlXsAAADEWGgd3XICAFBGtCvO++67Tx555BHp3r17cew6bcmmLeq0602dvs8++0ifPn3kX//6lxu3bpdddpFNN93UzTts2DBXiXf44Ye77i01j9GjR7u8C60Fjz/+eLnuuuvkjDPOkJ///OeuIvGBBx6Qxx9/vLgto0aNcl1jbr311rLtttvK1VdfLYsXL5ajjjqquE1HH320m693796u8nDkyJGuYm/77bdv0/7+9re/lYULF7r/vuSSS2TEiBFywgknyDrrrCN33HFH7McXAAAAAAAA6Oio3AMAoASBvlkuidjyaqsbb7zR/Tt06NAW6XfeeacceeSRrkXd008/XaxoGzx4sBx44IGu8q5Au9PULj21kkwr2rp27eoq6S688MLiPNoiUCvytGLwmmuucV1/3nbbbbLnnnsW5zn44INlzpw5rjWdVhBuvvnm8uSTT8qAAQOK81x11VWuW03dBh0vT5e/4YYb2ry/WnFYoN1yav4AAKByxRVjRYmvAAAAKh0xVuWhcg8AgDKi3XKG0cq8SZMmtZrP6quvLk888UToPFqB+NZbb4XOo12E6sdSW1sr119/vfsAAAAAAAAA+O46T+VedVokmW6ZljDeBrTSW5sWlVVAGzW9FEljuMVUxHTdLOuYpIz0ZMR0XYex+qDKv0zeSHfTUkZ6OlpegZHPN8tILHlZ+x22TD7GkTSTOWvl/uRE3s4rbFoUYcfEfMHXSre+UiFftYS17xGPVegxyUdbd+hryRFvG9bQb+b33F2L/pVYu5eIML5cMS/rO5KK9t2MU9Sx8lrLCy3H2Uu08Xn75ptvcug6oJRxg8uF3sStvKy4KKYHy4rad2uoa+s+nQgLAqwHhTV/CcFBXA/uEljHKs57Zco48N1SDeYy6Rr/w74mlfWmz2nsZuaVz37TBfLSkvloD+5szn7oZeMMCiXaz5OE8b217u3W/N+sI77zbq0nVeW/3vPG9zCfzUff3pDfOj6JvD1/qsn43ZI2YrKQX/1WHBdUBW1K68gxViXHVwcccEDkZW666SbXCwLKw4eLVpZ0UL1M+n8X9fTOP3dRF296U+NS5WD/k8+G/L6znh9WSBaSlxWbJIxlkg3+dSfr7WdaTaM/PdUU7bdl0v84L+n3fshjzWSFa/nqaGVYrU2Lsu7E/JAf18Y+Zmr9Gzy1uqt/gep85GeUJREyf5AzrrnF/n1MGPOHlvEY11DtXP9CVQ1GvBSyjpSxTM28jH/+Rf70RCYXsh/+HUk0GctkQr48VvwTY/m6WXZglGUHWkfgyyes3L3RH0wFGf/xlXzIde2ZFqyg37bEWJWn81TuAQAQIyr3lp/hw4cvx9wBAEBHRsFT6x5++GE56KCD3HjLbaHjMS9atIjKPQAAOjFirMpD5R4AAOhQxowZ096bAAAA0KFde+21ba6s+/Of/7zctwcAAAArFpV7AACUgJZ7K9bkyZPlgw8+cP+90UYbua47AQBA5eGt8tY999xz0rt37zYf07/97W+y6qqrfqfzAgAAyhsxVuWhcg8AgBJQubdizJ49Ww455BCZOHGi9OrVy6XNmzdPvv/978v9998v/fr1W0FbAgAAVgQKnlq36667ysKFC6V79+6h802aNMnNu/POO8d2fgAAQHkixqo8y3+UdQAAgBKNHDnSFV699957MnfuXPd59913ZcGCBXLyySdzXAEAQKe03377SWNjY2jF3g9/+MMVuk0AAABYcajcAwCgBEGQiPUDvyeffFJuuOEG2WCDDYppG264oVx//fWuiykAAFBZ2iu+Gjt2rGyzzTauNZyOZTd8+HCZMmVKi3lmzpwphx9+uAwcOFC6du0qW265pTz00EPF6VOnTpWjjz5ahgwZInV1dbLWWmu5sYSbmppC1z106FBJJBItPscff3zoMl999ZUcdNBBks/nl5n2/PPPy7777itHHnlkpGMAAAAqF2VYlYfKPQAA0GFpgVU6nV4mXdN8hVkAAACl0JZuJ554orz66qsyYcIEyWQyMmzYMFm8eHFxnhEjRrgKv0cffVTeeecdOeCAA1wF21tvveWmf/jhhy4+ufnmm12vA1dddZXcdNNNcs4557S6/mOPPVZmzJhR/Fx66aWh8//97393vRksXYH3wgsvuBZ7RxxxhPzhD3/gYgAAAKhQjLkHAEAJ8pJwnzjElU8l2m233eSUU06R//f//p+sssoqLu3zzz+X0047TXbffff23jwAANBBY6yoeWhvAc3dddddrgXf5MmTZZdddnFpL7/8stx4442y7bbbur9Hjx7tKvB0ni222EL22msv9ylYc801XWWgLnP55ZeHrr9Lly6uRWBbaVz01FNPyfe+9z0XK11zzTXy4osvyj777COHHXaY6+UAAACgvWMsLD+dp3IvnRJJLrW7CeNCTKygBo1BxBYHQeBPzxvpYZLGvidT/nTrWLllYtreEnYjMNadN3bDTUv79yVfZaVLpPnddpnLWNsUfT+sdQTGMmG90iSsU2L0HpPMRMsnTML6GljbG7KOvHktRlt3MmuvwzyO1u0k5GtuTot4awh9pAYR0xPRriuVN+4PiaR/JUHY/STx3Y97Pp8oq4GIC3nB77rrrpMf/ehHssYaa8jgwYNd2vTp02XjjTeWe+65h8PWASQlL6m2Bvcl3NvN9Rp55VdAxxS5Er6zKevmboVkIQ+DlLGPOeNAWnmFHat82I2/nSRDAo2k5CLdX619z1vBpVu//zjWJf0BU9cqeyysrLGerBHMNBkxeiLkmFj7nsslI80fdtzjkjRiBsf4KljfkCAkDrBiBPtnoX8tiVTIfcbcMGMf09F/Kma7Gau2vrZh59A6XKmgbWkdOMb6rnnMnz/f/du7d+9i2o477ijjxo1zXV726tVLHnjgAWloaHDdaobl0zwPy7333utiG63g0/H0zjvvPFfhF0a7/dRKSV2/rmf8+PFy6KGHutaCKG9fN9ZKVVXNMukN2apI9/bsEv/8iVzI9yNrPA8a/OtINtl5Jf2PZ6la4k9P1RvzG+lumcYg0u96K8TJGWVFYctY6VbZT1h4ZZUPWOlheVllNub8TdHLRiz5JUbsZRzfIOSZah/f6M+jhBEDBNazzUjOdrHXnV4Y7bmTXuw/uemFxhfHfUf8JyW12B93JjJGXll7Hea0vJGeCwlarN524uyFJ2lcQ2ZZlTF/TbW9jqzxZcj40wNrfl1/1bI3iMQK+u3VUWKscjdq1KjIy+gLYW2JB6PqPJV7AACg7GiF3ptvvilPP/206+pK6fh7e+yxR3tvGgAAKAMLFixo8XdNTY37hNGuNU899VTZaaed3AtFBVqZd/DBB0ufPn2kqqrKVb5phdraa6/tzefjjz92XWO21mrvpz/9qay++uquNd6//vUvOfPMM12Lv7/85S+t7pe+AKUVgz/+8Y/dOIGXXXZZi33u0aNH6LoBAADQdldffbXssMMOUl0dUiHcjPascNJJJ1G5BwBARxuIOK68YEskEvKDH/zAfdS8efM4XAAAVKi4YqxCHoWW/wVjxoyRCy64IHRZHXtPx7PTwpjmtDWdxiH60lHfvn3l4YcfdmPu6Th3m2yySYt5tRtx7aLzJz/5iRtPL8xxxx1X/G/NZ+WVV3bdj3/yySeudZ6PthzUGOnb/Q1c5eODDz5Y/Fun53IhrTMAAECnEXeMFZV2Ga4vIc2cOVM222wz9wJUoatzH41pNPaaOnWqrLPOOvL73//edT/+7XYELq679dZbXXymL2VpV+g6b8HcuXNl5MiR8te//lWSyaQceOCBrivzbt26tcjniiuukFtuuUU+++wzF+P98pe/lHPPPdfcNn25S7tvb4vu3bvL8kLLPQAASkC3nCuGBm/6Rrq+Ja+0AO2hhx5yXVY98cQTLiAEAACVI+4uo7Q77+at11prtadvVj/22GPy/PPPy6BBg4rpWtGm3YVrpd9GG23k0jQO0Yo9Laxq3hXmF198Id///vddN55aUBTVdtttV2z5Z1XuPffcc5HzBQAAnVd7dsup3Zprd5YaL2mco63f9txzT9dTga+STMc51q7Gx44dKz/84Q/lvvvucz0UaM9OhV4VLr30Urn22mvl7rvvliFDhriKQM3z/fffl9raWjePjkM8Y8YMmTBhgmQyGTnqqKPcS1WaX4GOXazjGGtPC/qSlVYI6sdy5513Ss+ePdu87zfffLMMGDBAlgcq9wAAQIelgZ92NaU0GNPP3/72N/dm+umnn+4CMAAAAItW7LWla0p9a1vf7NY3sSdOnOgKiZpbsuSbQcL0re/mUqmU68azeYs9rdjbaqutXOHP0vO3xdtvv+3+1RZ8ll133TVyvgAAAO3hyiuvdD0ZaOVaoazn8ccflzvuuEPOOuusZebX1nXaA4KW+6iLLrrIlQfpi1a6rMZtV199tRvLbv/993fz/PGPf3SVaNqzwiGHHCIffPCBG5v4jTfekK233trNo60FtfWfVuRpd+g6j7b205e31ltvPTfP0jHg0o444ohI+67dry8v0aNMAABQ7M4grg/8tLuGQnda+ha9ttwbNmyYnHHGGS5AAwAAlaW94ivtivOee+5xb3Jr90kag+invr7eTV9//fXd2Hq/+MUv5PXXX3ct+bQLJy1o0jfJCxV7Q4cOldVWW80VGs2ZM6eYT4HOo3lpHkrz0QKryZMnu26nHn30URkxYoTssssusummm7ZpHMHWLFy4MNL8AACg8sQdY2k80vzT2NjoXW9TU5OLc/bYY49imr78pH+/8sor3mU0vfn8SlvlFeb/9NNPXXzVfB5tTaetAgvz6L/ajXmhYk/p/Lru1157zf2t3XWuueaarrxJK/W056hj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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Plot all variables for the first day\n", + "fig = plot_era5_variables_comparison(ds, time_index=0)\n", + "plt.show()" ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -283,9 +454,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.5" + "version": "3.8.0" } }, "nbformat": 4, - "nbformat_minor": 5 + "nbformat_minor": 4 } diff --git a/terrakit/download/collections.json b/terrakit/download/collections.json index 53a8384..521fc5d 100644 --- a/terrakit/download/collections.json +++ b/terrakit/download/collections.json @@ -1975,7 +1975,7 @@ }, { "band_name": "evaporation", - "alt_names": [], + "alt_names": ["e"], "resolution": "0.25 deg", "description": "" }, diff --git a/terrakit/download/data_connectors/climate_data_store.py b/terrakit/download/data_connectors/climate_data_store.py index bca63f1..09141e6 100644 --- a/terrakit/download/data_connectors/climate_data_store.py +++ b/terrakit/download/data_connectors/climate_data_store.py @@ -8,6 +8,7 @@ import math import os import pandas as pd +import re import requests import shutil import xarray as xr @@ -17,18 +18,14 @@ from shapely.geometry import box from typing import Any, Dict, Union -from terrakit.download.geodata_utils import save_cog from ..connector import Connector from ..geodata_utils import ( load_and_list_collections, - map_netcdf_variables_to_requested_bands, - save_data_array_to_file, ) from terrakit.general_utils.exceptions import ( TerrakitValidationError, TerrakitValueError, ) -from terrakit.general_utils.geospatial_util import _convert_to_360_degree_system from terrakit.validate.helpers import ( check_collection_exists, check_date_format, @@ -79,6 +76,57 @@ def __init__(self): # Load CORDEX domains self.cordex_domains = CORDEX_DOMAINS + # ERA5 variable name to stepType mapping + # This lookup table allows inferring stepType when it's not in the filename + VARIABLE_STEPTYPE_MAP = { + # Instantaneous parameters + "t2m": "instant", + "2m_temperature": "instant", + "u10": "instant", + "10m_u_component_of_wind": "instant", + "v10": "instant", + "10m_v_component_of_wind": "instant", + "msl": "instant", + "mean_sea_level_pressure": "instant", + "d2m": "instant", + "2m_dewpoint_temperature": "instant", + "sp": "instant", + "surface_pressure": "instant", + "skt": "instant", + "skin_temperature": "instant", + "tcc": "instant", + "total_cloud_cover": "instant", + "tcwv": "instant", + "total_column_water_vapour": "instant", + # Accumulated parameters + "tp": "accum", + "total_precipitation": "accum", + "ssr": "accum", + "surface_net_solar_radiation": "accum", + "str": "accum", + "surface_net_thermal_radiation": "accum", + "e": "accum", + "evaporation": "accum", + "ro": "accum", + "runoff": "accum", + "sf": "accum", + "snowfall": "accum", + "ssrd": "accum", + "surface_solar_radiation_downwards": "accum", + "strd": "accum", + "surface_thermal_radiation_downwards": "accum", + # Mean rate parameters + "avg_tprate": "avg", + "mean_total_precipitation_rate": "avg", + # Min/Max parameters + "mx2t": "max", + "maximum_2m_temperature_since_previous_post_processing": "max", + "mn2t": "min", + "minimum_2m_temperature_since_previous_post_processing": "min", + "fg10": "max", + "10m_wind_gust_since_previous_post_processing": "max", + } + def _is_cordex_collection(self, collection_name: str) -> bool: """Check if collection is a CORDEX dataset.""" return "cordex" in collection_name.lower() @@ -142,50 +190,48 @@ def _find_best_cordex_match(self, bbox: list, domain_codes: list) -> str: ) return best_domain - def _convert_netcdf_to_geotiffs( - self, netcdf_path: str, bbox: list, output_dir: str, collection_name: str - ) -> list[str]: - """Convert CDS NetCDF to individual GeoTIFF files, one per date.""" - - ds = xr.open_dataset(netcdf_path) - output_files = [] - - # Determine dimension names - lon_name = "longitude" if "longitude" in ds.dims else "lon" - lat_name = "latitude" if "latitude" in ds.dims else "lat" - time_name = "time" if "time" in ds.dims else "valid_time" - - # Get the main data variable - data_vars = [ - v for v in ds.data_vars if v not in [lon_name, lat_name, time_name] - ] - var_name = data_vars[0] - - # Process each time step - for time_idx in range(len(ds[time_name])): - # Extract data for this time step - da = ds[var_name].isel({time_name: time_idx}) - - # Get the date - time_value = ds[time_name].isel({time_name: time_idx}).values - date_str = pd.Timestamp(time_value).strftime("%Y-%m-%d") - - # Add CRS and spatial dimensions - da = da.rio.write_crs("EPSG:4326") - da = da.rio.set_spatial_dims(x_dim=lon_name, y_dim=lat_name) - - # Create output filename - output_filename = f"{collection_name}_{date_str}.tif" - output_path = Path(output_dir) / output_filename - - # Use TerraKit's save_cog function - save_cog(da, str(output_path)) - output_files.append(str(output_path)) - - logger.info(f"Created GeoTIFF: {output_filename}") + def _infer_steptype(self, filename: str, variable_name: str) -> str: + """ + Infer stepType from filename or variable name. + + Uses a three-tier approach: + 1. Extract from filename if present (stepType-xxx) + 2. Look up variable name in VARIABLE_STEPTYPE_MAP + 3. Fall back to "unknown" + + Parameters + ---------- + filename : str + NetCDF filename + variable_name : str + Variable name from the dataset + + Returns + ------- + str + stepType: 'instant', 'accum', 'avg', 'max', 'min', or 'unknown' + """ + # Method 1: Try extracting from filename for variables consolidated by stepType + if "stepType-" in filename: + step_type = filename.split("stepType-")[1].split(".")[0] + logger.debug(f"Extracted stepType '{step_type}' from filename: {filename}") + return step_type + + # Method 2: Look up variable name in mapping + if variable_name in self.VARIABLE_STEPTYPE_MAP: + step_type = self.VARIABLE_STEPTYPE_MAP[variable_name] + logger.debug( + f"Inferred stepType '{step_type}' from variable name: {variable_name}" + ) + return step_type - ds.close() - return output_files + # Method 3: Fall back to unknown + logger.warning( + f"Could not determine stepType for variable '{variable_name}' " + f"in file '{filename}'. Marking as 'unknown'. " + f"Consider adding this variable to VARIABLE_STEPTYPE_MAP." + ) + return "unknown" def _estimate_request_size( self, @@ -482,14 +528,13 @@ def _build_request_params( # ERA5 and other collections use bbox directly # CDS API expects area as [North, West, South, East] # Input bbox is [min_lon, min_lat, max_lon, max_lat] = [West, South, East, North] - # CDS uses 0-360° longitude convention, so convert negative longitudes - converted_lons = _convert_to_360_degree_system([bbox[0], bbox[2]]) - + # ERA5 uses -180 to 180° longitude convention (NOT 0-360°) + # Do NOT convert longitudes - use them as-is params["area"] = [ bbox[3], # North (max_lat) - converted_lons[0], # West (min_lon, converted to 0-360°) + bbox[0], # West (min_lon) - keep in -180/180 system bbox[1], # South (min_lat) - converted_lons[1], # East (max_lon, converted to 0-360°) + bbox[2], # East (max_lon) - keep in -180/180 system ] # Set default parameters for ERA5 collections @@ -665,19 +710,51 @@ def _validate_spatial( # Check minimum bbox size for ERA5 collections (0.25° grid resolution) if not self._is_cordex_collection(collection_name): - min_lon, min_lat, max_lon, max_lat = bbox + # ERA5 uses -180/180° system, so work directly with bbox values + min_lon = bbox[0] # West (min_lon in -180/180°) + min_lat = bbox[1] # South (min_lat) + max_lat = bbox[3] # North (max_lat) + max_lon = bbox[2] # East (max_lon in -180/180°) lon_span = max_lon - min_lon lat_span = max_lat - min_lat # ERA5 has 0.25° resolution, require at least 0.25° in each dimension MIN_RESOLUTION = 0.25 if lon_span < MIN_RESOLUTION or lat_span < MIN_RESOLUTION: - raise TerrakitValidationError( - message=f"Bounding box too small for ERA5 data resolution. " - f"Current size: {lon_span:.4f}° × {lat_span:.4f}°. " - f"Minimum required: {MIN_RESOLUTION}° × {MIN_RESOLUTION}° " - f"(ERA5 grid resolution is 0.25°). " - f"Please increase your bounding box size." + # Store original values for logging + orig_lon_span = lon_span + orig_lat_span = lat_span + + # Calculate how much to expand in each dimension + lon_deficit = max(0, MIN_RESOLUTION - lon_span) + lat_deficit = max(0, MIN_RESOLUTION - lat_span) + + # Expand equally on both sides to preserve center point + expand_lon = lon_deficit / 2 + expand_lat = lat_deficit / 2 + + # Calculate new bounds in -180/180 system (original bbox system) + new_min_lon = bbox[0] - expand_lon + new_max_lon = bbox[2] + expand_lon + new_min_lat = bbox[1] - expand_lat + new_max_lat = bbox[3] + expand_lat + + # Update bbox in place (keep in -180/180 system) + bbox[0] = new_min_lon # west + bbox[1] = new_min_lat # south + bbox[2] = new_max_lon # east + bbox[3] = new_max_lat # north + + # Calculate final dimensions for logging + final_lon_span = new_max_lon - new_min_lon + final_lat_span = new_max_lat - new_min_lat + + # Log warning to user + logger.warning( + f"Bounding box expanded to meet ERA5 minimum resolution requirement. " + f"Original size: {orig_lon_span:.4f}° × {orig_lat_span:.4f}°. " + f"Expanded to: {final_lon_span:.4f}° × {final_lat_span:.4f}°. " + f"New bbox: [{bbox[0]:.4f}, {bbox[1]:.4f}, {bbox[2]:.4f}, {bbox[3]:.4f}]" ) # For CORDEX collections, map bbox to domain @@ -707,20 +784,40 @@ def _validate_spatial( allowed_bbox = bbox_list[0] - # Unpack for clarity - min_lon, min_lat, max_lon, max_lat = bbox + # ERA5 uses -180/180° system, but constraints file has 0-360° format + # Convert constraints bbox from 0-360° to -180/180° for validation allowed_min_lon, allowed_min_lat, allowed_max_lon, allowed_max_lat = ( allowed_bbox ) - # Validate each bound + # Convert allowed longitude bounds from 0-360° to -180/180° + # 0° stays 0°, but 360° becomes 180° (not -180° to avoid wrap issues) + # For global coverage [0, 360] we want [-180, 180] + if allowed_min_lon == 0 and allowed_max_lon == 360: + # Global coverage case + allowed_min_lon = -180 + allowed_max_lon = 180 + else: + # Convert individual values + if allowed_min_lon > 180: + allowed_min_lon -= 360 + if allowed_max_lon > 180: + allowed_max_lon -= 360 + + # User bbox is already in -180/180° system + min_lon = bbox[0] + max_lon = bbox[2] + min_lat = bbox[1] + max_lat = bbox[3] + + # Validate each bound (using -180/180° for longitude) errors = [] if min_lon < allowed_min_lon: - errors.append(f"min_lon {min_lon} < allowed {allowed_min_lon}") + errors.append(f"min_lon {min_lon:.4f} < allowed {allowed_min_lon}") if min_lat < allowed_min_lat: errors.append(f"min_lat {min_lat} < allowed {allowed_min_lat}") if max_lon > allowed_max_lon: - errors.append(f"max_lon {max_lon} > allowed {allowed_max_lon}") + errors.append(f"max_lon {max_lon:.4f} > allowed {allowed_max_lon}") if max_lat > allowed_max_lat: errors.append(f"max_lat {max_lat} > allowed {allowed_max_lat}") @@ -925,7 +1022,7 @@ def get_data( data_connector_spec=None, save_file=None, working_dir=".", - ) -> Union[xr.DataArray, None]: + ) -> Union[xr.Dataset, None]: """ Fetches data from Climate Data Store for the specified collection, date range, area, and bands. @@ -938,16 +1035,29 @@ def get_data( bands (list, optional): List of bands to retrieve. Defaults to all bands. query_params (dict, optional): Additional query parameters. Defaults to {}. data_connector_spec (dict, optional): Data connector specification. Defaults to None. - save_file (str, optional): Path to save the output file. If provided, individual GeoTIFF files - will be saved for each date with the naming pattern: {save_file}_{date}.tif - (e.g., 'output_2025-01-01.tif', 'output_2025-01-02.tif'). Each file contains all + save_file (str, optional): Path to save the output file. If provided, individual NetCDF files + will be saved for each date with the naming pattern: {save_file}_{date}.nc + (e.g., 'output_2025-01-01.nc', 'output_2025-01-02.nc'). Each file contains all requested bands for that specific date. If None, no files are saved to disk. Defaults to None. working_dir (str, optional): Working directory for temporary files. Defaults to '.'. Returns: - xarray.DataArray: An xarray DataArray containing all fetched data with dimensions (time, band, y, x). - All dates are stacked along the time dimension, and all bands are stacked along the band dimension. - If save_file is provided, individual date files are also saved to disk. + xarray.Dataset: An xarray Dataset containing all fetched data with variables as data variables. + Each variable has dimensions (time, latitude, longitude) and includes a 'stepType' + attribute indicating the parameter class ('instant', 'accum', 'avg', 'max', 'min'). + + To convert to the old DataArray format: + data_array = dataset.to_array(dim='band') + + Note: + This method now returns xarray.Dataset instead of xarray.DataArray to preserve + parameter class (stepType) information. To convert to the old format: + + data_array = dataset.to_array(dim='band') + + This allows accessing data as before: + + temp = data_array.sel(band='2m_temperature') Example: ```python @@ -959,7 +1069,7 @@ def get_data( date_start="2025-01-01", date_end="2025-01-02", bbox=[-1.32, 51.06, -1.30, 51.08], - bands=["2m_temperature"], + bands=["2m_temperature", "total_precipitation"], query_params={ "daily_statistic": "daily_minimum", "frequency": "1hr", @@ -967,9 +1077,21 @@ def get_data( } ) save_file="./derived-era5-single-levels-daily-statistics", + + # Access variables + temperature = data['2m_temperature'] + print(temperature.attrs['stepType']) # 'instant' + + # Filter by stepType + instant_vars = [v for v in data.data_vars if data[v].attrs.get('stepType') == 'instant'] ``` """ + # Load constraints and validate parameters + constraints = self._load_constraints(data_collection_name) + self._validate_temporal(date_start, date_end, constraints, data_collection_name) + self._validate_spatial(bbox, constraints, data_collection_name) + # 1. Download zip from CDS API zip_path = self._download_from_cds( data_collection_name, @@ -988,26 +1110,19 @@ def get_data( with zipfile.ZipFile(zip_path, "r") as zip_ref: zip_ref.extractall(extract_dir) - # 3. Find NetCDF file(s) + # 3. Find NetCDF file(s) and extract stepType from filenames netcdf_files = list(extract_dir.glob("*.nc")) if not netcdf_files: raise TerrakitValueError(f"No NetCDF files found in {zip_path}") - # 4. Load NetCDF and process into DataArray per date - # CDS may return multiple NetCDF files (one per variable) - # We need to merge them by date to avoid duplicates - - # Create mapping from NetCDF variable names to requested band names - # CDS returns abbreviated names (e.g., "t2m"), but we want to use - # the requested names (e.g., "2m_temperature") as band labels - netcdf_to_band_map = map_netcdf_variables_to_requested_bands( - netcdf_files, bands - ) + # 4. Load NetCDF and process into Dataset with stepType preservation + # CDS may return multiple NetCDF files (one per stepType) + # Extract stepType from filename: data_stream-oper_stepType-{type}.nc - # First, collect all data organized by date + # Collect data organized by stepType and date date_data_dict: Dict[ - str, Dict[str, xr.DataArray] - ] = {} # {date_str: {netcdf_var_name: DataArray}} + str, Dict[str, tuple[xr.DataArray, str]] + ] = {} # {date_str: {var_name: (DataArray, stepType)}} for netcdf_file in netcdf_files: ds = xr.open_dataset(netcdf_file) @@ -1017,82 +1132,196 @@ def get_data( lat_name = "latitude" if "latitude" in ds.dims else "lat" time_name = "time" if "time" in ds.dims else "valid_time" + # Determine if this is a single-variable file or multi-variable file + # Single-variable files don't have stepType in filename + is_single_variable_file = not any( + step in netcdf_file.name + for step in ["accum", "avg", "instant", "max", "min"] + ) + # Get the main data variable(s) - these are our bands data_vars = [ v for v in ds.data_vars if v not in [lon_name, lat_name, time_name] ] + # Log variables found in this file + # all_variables_found.update(data_vars) + logger.debug(f"File {netcdf_file.name} contains variables: {data_vars}") + # Process each time step for time_idx in range(len(ds[time_name])): # Extract the date for this time step time_value = ds[time_name].isel({time_name: time_idx}).values + + # date_str = pd.Timestamp(time_value).strftime("%Y-%m-%d %H:%M") date_str = pd.Timestamp(time_value).strftime("%Y-%m-%d") # Initialize dict for this date if not exists if date_str not in date_data_dict: date_data_dict[date_str] = {} - # Store each variable for this date + # Confirm variable name by extracting from filename if needed + # If the NetCDF file doesn't contain stepType in its name and has only one variable, + # extract the variable name from the filename pattern + + # Extract variable name from filename if this is a single-variable file + extracted_var_name = None + + if is_single_variable_file: + # Extract variable name from filename pattern: variable_name_YYYYMMDD_HHMMSS.nc + match = re.match(r"^([a-zA-Z0-9_]+?)_\d", netcdf_file.name) + if match: + extracted_var_name = match.group(1) + + logger.debug( + f"Extracted variable name '{extracted_var_name}' from filename {netcdf_file.name}" + ) + + # Store each variable for this date with its stepType for var_name in data_vars: - # Extract data for this specific time step - da_var = ds[var_name].isel({time_name: time_idx}) + # Determine which variable name to use for stepType inference and data access + # For stepType inference: use extracted name if available, otherwise use original + steptype_var_name = ( + extracted_var_name if extracted_var_name else var_name + ) + # For data access: always use the original variable name from the NetCDF + data_access_var_name = var_name + + # Try to get stepType from GRIB_stepType attribute first + if "GRIB_stepType" in ds[var_name].attrs: + step_type = ds[var_name].attrs["GRIB_stepType"] + logger.debug( + f"Extracted stepType '{step_type}' from GRIB_stepType attribute for variable '{var_name}'" + ) + else: + # Fall back to inference method + step_type = self._infer_steptype( + netcdf_file.name, steptype_var_name + ) + + # Extract data for this specific time step using the original NetCDF variable name + da_var = ds[data_access_var_name].isel({time_name: time_idx}) # Add CRS and spatial dimensions da_var = da_var.rio.write_crs("EPSG:4326") da_var = da_var.rio.set_spatial_dims(x_dim=lon_name, y_dim=lat_name) - # Store in dict - date_data_dict[date_str][var_name] = da_var + # Store in dict with stepType using the appropriate variable name + # Use extracted name if available for consistency in output, otherwise use original + output_var_name = ( + extracted_var_name if extracted_var_name else var_name + ) + date_data_dict[date_str][output_var_name] = (da_var, step_type) ds.close() - # Now process each unique date - da_list = [] + # Now process each unique date and build a Dataset with stepType attributes + # Build data for each band across all time steps, tracking dates for each band + band_data: Dict[ + str, Dict[str, Any] + ] = {} # {band_name: {'data': list, 'dates': list, 'stepType': str}} + for date_str in sorted(date_data_dict.keys()): data_date_datetime = datetime.strptime(date_str, "%Y-%m-%d") var_dict = date_data_dict[date_str] - # Collect all variables for this date - band_arrays = [] - band_names = [] for var_name in sorted(var_dict.keys()): - da_var = var_dict[var_name] - # Drop time coordinate if it exists (will be added back after concatenation) + da_var, step_type = var_dict[var_name] + + # Drop time coordinate if it exists if "time" in da_var.coords: da_var = da_var.drop_vars("time") - # Expand dims to add band dimension - da_var = da_var.expand_dims(dim="band") - band_arrays.append(da_var) - # Use the requested band name instead of NetCDF variable name - requested_band_name = netcdf_to_band_map.get(var_name, var_name) - band_names.append(requested_band_name) - - # Concatenate all bands for this time step - da_time = xr.concat( - band_arrays, dim="band", coords="minimal", compat="override" + + # Use the NetCDF variable name directly as the band name + # This ensures we preserve the original variable names from CDS + band_name = var_name + + # Initialize band_data entry if needed + if band_name not in band_data: + band_data[band_name] = { + "data": [], + "dates": [], + "stepType": step_type, + } + + # Store the data array and its corresponding date + band_data[band_name]["data"].append(da_var) + band_data[band_name]["dates"].append(data_date_datetime) + + # 5. Create Dataset with stepType attributes + # Each variable gets its own time coordinate based on which dates it has data for + merged_dataset = xr.Dataset() + + for band_name, band_info in band_data.items(): + # Concatenate all time steps for this band + data_arrays = band_info["data"] + dates = band_info["dates"] + + # Check for duplicate dates + if len(dates) != len(set(dates)): + logger.warning(f"Variable {band_name} has duplicate dates: {dates}") + # Remove duplicates by keeping only unique dates + seen_dates = {} + unique_data = [] + unique_dates = [] + for da, date in zip(data_arrays, dates): + if date not in seen_dates: + seen_dates[date] = True + unique_data.append(da) + unique_dates.append(date) + data_arrays = unique_data + dates = unique_dates + logger.info( + f"After deduplication: {len(dates)} unique dates for {band_name}" + ) + + # Stack along a new dimension first + # Use coords='minimal' to avoid issues with inconsistent coordinates like 'number' + band_da = xr.concat( + data_arrays, dim="time", coords="minimal", compat="override" ) - da_time = da_time.assign_coords({"band": band_names}) - # Expand time dimension and assign time coordinate - da_time = da_time.expand_dims(dim="time") - da_time = da_time.assign_coords({"time": [data_date_datetime]}) + # Assign the time coordinate specific to this variable + band_da = band_da.assign_coords({"time": dates}) + + # Add stepType to variable attributes + band_da.attrs["stepType"] = band_info["stepType"] + + # Add to merged dataset + merged_dataset[band_name] = band_da - # Add this date's data to the list for final concatenation - da_list.append(da_time) + # Add dataset-level attributes + merged_dataset.attrs["source"] = "Climate Data Store (CDS)" + merged_dataset.attrs["dataset"] = data_collection_name - # 5. Concatenate all time steps into final DataArray - da = xr.concat(da_list, dim="time", join="override", combine_attrs="override") + # Write CRS (EPSG:4326 for CDS data) + merged_dataset.rio.write_crs("EPSG:4326", inplace=True) - # 6. Save individual date files (save_data_array_to_file handles splitting by time) + # 6. Save individual date files as NetCDF if save_file is not None: # Ensure the directory exists save_dir = Path(save_file).parent save_dir.mkdir(parents=True, exist_ok=True) - save_data_array_to_file(da, save_file) + + # Remove file extension if it exists on save_file + save_file_base = str(Path(save_file).with_suffix("")) + + # Save each date separately + unique_dates = sorted(set(merged_dataset.time.values)) + for date in unique_dates: + daily_data = merged_dataset.sel(time=date) + date_str = pd.Timestamp(date).strftime("%Y-%m-%d") + + filename = f"{save_file_base}_{date_str}.nc" + + daily_data.to_netcdf(filename) + logger.info(f"Saved {filename}") # 7. Cleanup temporary files shutil.rmtree(extract_dir) Path(zip_path).unlink() - logger.info(f"Processed {len(da_list)} time steps into DataArray") - return da + logger.info( + f"Processed {len(merged_dataset.time)} time steps and {len(merged_dataset.data_vars)} variables into Dataset" + ) + return merged_dataset diff --git a/terrakit/general_utils/plotting.py b/terrakit/general_utils/plotting.py index 49254e3..f73e356 100644 --- a/terrakit/general_utils/plotting.py +++ b/terrakit/general_utils/plotting.py @@ -1,4 +1,4 @@ -# © Copyright IBM Corporation 2025 +# © Copyright IBM Corporation 2025-2026 # SPDX-License-Identifier: Apache-2.0 @@ -266,3 +266,215 @@ def plot_chip_and_label_pairs( print( f"label_{i}: {chip_list[i].replace(chip_suffix, chip_label_suffix).split('/')[-1]}" ) + + +def plot_era5_variable(dataset, variable_name, time_index=0): + """ + Plot a single variable from the dataset with spatial map and optional profile. + Handles both datasets with time dimension and single-date datasets without time dimension. + + Parameters: + dataset: xarray Dataset with variables as data variables + variable_name: Name of the variable to plot + time_index: Time index to plot (default: 0, first day only) - ignored if no time dimension + """ + if variable_name not in dataset.data_vars: + raise ValueError( + f"Variable '{variable_name}' not found in dataset. Available: {list(dataset.data_vars)}" + ) + + # Get the variable data + var_data = dataset[variable_name] + step_type = var_data.attrs.get("GRIB_stepType", "unknown") + + # Determine coordinate names + lat_name = "latitude" if "latitude" in var_data.dims else "lat" + lon_name = "longitude" if "longitude" in var_data.dims else "lon" + time_name = ( + "time" + if "time" in var_data.dims + else ("valid_time" if "valid_time" in var_data.dims else None) + ) + + # Get spatial dimensions + n_lat = len(var_data[lat_name]) + n_lon = len(var_data[lon_name]) + + # Select data for this time step (if time dimension exists) + if time_name and time_name in var_data.dims: + spatial_slice = var_data.isel({time_name: time_index}) + time_str = str(var_data[time_name].values[time_index])[:10] + else: + # No time dimension - use data as is + spatial_slice = var_data + # Try to get time from coordinates + if "time" in var_data.coords: + time_str = str(var_data.coords["time"].values)[:10] + elif "valid_time" in var_data.coords: + time_str = str(var_data.coords["valid_time"].values)[:10] + else: + time_str = "unknown date" + + # Check if we have enough spatial data for meaningful plots + if n_lat > 1 and n_lon > 1: + # Full 2D spatial data - create profile and map + fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 5)) + + # Plot 1: Longitude profile at middle latitude + lat_idx = n_lat // 2 + spatial_profile = spatial_slice.isel({lat_name: lat_idx}) + spatial_profile.plot(ax=ax1, marker="o", markersize=6) + ax1.set_title( + f"stepType: {step_type} | Variable: {variable_name}\nLongitude Profile at lat={float(var_data[lat_name].values[lat_idx]):.2f}" + ) + ax1.set_xlabel("Longitude") + ax1.set_ylabel(f"{var_data.attrs.get('units', 'Value')}") + ax1.grid(True, alpha=0.3) + + # Plot 2: Spatial map + im = spatial_slice.plot.pcolormesh(ax=ax2, cmap="viridis", add_colorbar=True) # noqa: F841 + ax2.set_title( + f"stepType: {step_type} | Variable: {variable_name}\nSpatial Map at {time_str}" + ) + ax2.set_xlabel("Longitude") + ax2.set_ylabel("Latitude") + + elif n_lat > 1: + # Only latitude varies - plot latitude profile + fig, ax = plt.subplots(1, 1, figsize=(10, 6)) + spatial_slice.plot.pcolormesh(ax=ax, cmap="viridis", add_colorbar=True) + + ax.set_title( + f"stepType: {step_type} | Variable: {variable_name}\nLatitude Profile at {time_str}" + ) + ax.set_xlabel("Latitude") + ax.set_ylabel(f"{var_data.attrs.get('units', 'Value')}") + ax.grid(True, alpha=0.3) + + elif n_lon > 1: + # Only longitude varies - plot longitude profile + fig, ax = plt.subplots(1, 1, figsize=(10, 6)) + spatial_slice.plot.pcolormesh(ax=ax, cmap="viridis", add_colorbar=True) + + ax.set_title( + f"stepType: {step_type} | Variable: {variable_name}\nLongitude Profile at {time_str}" + ) + ax.set_xlabel("Longitude") + ax.set_ylabel(f"{var_data.attrs.get('units', 'Value')}") + ax.grid(True, alpha=0.3) + + else: + # Single point - just display the value + fig, ax = plt.subplots(1, 1, figsize=(8, 6)) + value = float(spatial_slice.values) + ax.text( + 0.5, + 0.5, + f"{variable_name}\n{value:.4f} {var_data.attrs.get('units', '')}", + ha="center", + va="center", + fontsize=16, + transform=ax.transAxes, + ) + ax.set_title(f"stepType: {step_type} | Single Point Value at {time_str}") + ax.axis("off") + + plt.tight_layout() + return fig + + +def plot_era5_variables_comparison(dataset, time_index=0): + """ + Plot all variables side by side with spatial maps for the first day. + Handles both datasets with time dimension and single-date datasets without time dimension. + + Parameters: + dataset: xarray Dataset with variables as data variables + time_index: Time index to plot (default: 0, first day only) - ignored if no time dimension + """ + variables = list(dataset.data_vars) + n_vars = len(variables) + + # Determine coordinate names from first variable + first_var = dataset[variables[0]] + time_name = ( + "time" + if "time" in first_var.dims + else ("valid_time" if "valid_time" in first_var.dims else None) + ) + lat_name = "latitude" if "latitude" in first_var.dims else "lat" + lon_name = "longitude" if "longitude" in first_var.dims else "lon" + + # Check spatial dimensions + n_lat = len(first_var[lat_name]) + n_lon = len(first_var[lon_name]) + + # Get time string + if time_name and time_name in first_var.dims: + time_str = str(first_var[time_name].values[time_index])[:10] + elif "time" in first_var.coords: + time_str = str(first_var.coords["time"].values)[:10] + elif "valid_time" in first_var.coords: + time_str = str(first_var.coords["valid_time"].values)[:10] + else: + time_str = "unknown date" + + # Determine grid layout + n_cols = min(3, n_vars) # Max 3 columns + n_rows = (n_vars + n_cols - 1) // n_cols + + fig, axes = plt.subplots(n_rows, n_cols, figsize=(6 * n_cols, 5 * n_rows)) + + if n_vars == 1: + axes = [axes] + else: + axes = axes.flatten() + + for v_idx, var_name in enumerate(variables): + ax = axes[v_idx] + var_data = dataset[var_name] + + # Select data for this time step (if time dimension exists) + if time_name and time_name in var_data.dims: + data = var_data.isel({time_name: time_index}) + else: + data = var_data + + step_type = var_data.attrs.get("GRIB_stepType", "unknown") + + # Plot based on spatial dimensions + if n_lat > 1 and n_lon > 1: + # 2D spatial map + data.plot.pcolormesh(ax=ax, cmap="viridis", add_colorbar=True) + ax.set_xlabel("Longitude") + ax.set_ylabel("Latitude") + elif n_lat > 1 or n_lon > 1: + # 1D profile + data.plot(ax=ax, marker="o") + ax.set_ylabel(f"{var_data.attrs.get('units', 'Value')}") + ax.grid(True, alpha=0.3) + else: + # Single point + value = float(data.values) + ax.text( + 0.5, + 0.5, + f"{value:.4f}\n{var_data.attrs.get('units', '')}", + ha="center", + va="center", + fontsize=12, + transform=ax.transAxes, + ) + ax.axis("off") + + ax.set_title( + f"stepType: {step_type} | {var_name}", fontsize=10, fontweight="bold" + ) + + # Hide extra subplots + for idx in range(n_vars, len(axes)): + axes[idx].axis("off") + + plt.suptitle(f"All Variables - {time_str}", fontsize=14, fontweight="bold", y=1.02) + plt.tight_layout() + return fig diff --git a/tests/component_tests/download/data_connectors/test_climate_data_store.py b/tests/component_tests/download/data_connectors/test_climate_data_store.py index 16cbc2b..bffa6cb 100644 --- a/tests/component_tests/download/data_connectors/test_climate_data_store.py +++ b/tests/component_tests/download/data_connectors/test_climate_data_store.py @@ -2,6 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 +import logging import os import pandas as pd import pytest @@ -26,6 +27,11 @@ def bbox(self): # Kenya region: ~0.5° × 0.5° bbox (meets 0.25° minimum requirement) return [34.5, -0.5, 35.0, 0.0] + @pytest.fixture + def expected_dates_cds(self): + dates = pd.date_range("2024-01-01", "2024-01-31").strftime("%Y-%m-%d").tolist() + return dates + def test_valid_data_connector(self): dc = DataConnector(connector_type=self.connector_type) assert dc.connector is not None @@ -42,7 +48,7 @@ def test_list_collections_climate_data_store( collections = dc.connector.list_collections() assert collections == expected_collections - def test__missing_credentials_cds( + def test_missing_credentials_cds( self, unset_evn_vars, start_date, @@ -66,11 +72,6 @@ def test_invalid_collection(self, start_date, bbox): with pytest.raises(TerrakitValueError, match="Invalid collection"): dc.connector.find_data(collection, start_date, start_date, bbox=bbox) - @pytest.fixture - def expected_dates_cds(self): - dates = pd.date_range("2024-01-01", "2024-01-31").strftime("%Y-%m-%d").tolist() - return dates - @pytest.mark.parametrize( "collection", [ @@ -113,7 +114,7 @@ def test_find_available_data_cds( ), ], ) - def test_find_available_data_cds_start_data_given_constraints( + def test_find_available_data_cds__start_date_given_constraints( self, collection, start_date, @@ -134,37 +135,212 @@ def test_find_available_data_cds_start_data_given_constraints( ) assert unique_dates == [start_date, end_date] - def test_find_available_data_box_too_small_for_era5_resolution(self, start_date): + def test_find_available_data_cds__bbox_expansion_for_small_bbox( + self, start_date, caplog + ): """ - Test that find_data raises error for bbox smaller than ERA5 grid resolution (0.25°). + Test that find_data expands bbox smaller than ERA5 grid resolution (0.25°) and logs warning. """ + collection = "derived-era5-single-levels-daily-statistics" # This bbox is only 0.02° x 0.02°, which is smaller than ERA5's 0.25° grid tiny_bbox = [-1.32, 51.06, -1.30, 51.08] + original_bbox = tiny_bbox.copy() dc = DataConnector(connector_type=self.connector_type) - with pytest.raises( - TerrakitValidationError, - match="Bounding box too small for ERA5 data resolution", - ): - dc.connector.find_data( + + # Capture logs at WARNING level + with caplog.at_level(logging.WARNING): + unique_dates, results = dc.connector.find_data( collection, start_date, start_date, bbox=tiny_bbox, bands=self.bands ) - def test_negative_longitude_conversion( + # Verify bbox was expanded + assert tiny_bbox != original_bbox, "Bbox should have been modified" + + # Verify dimensions are at least 0.25° + lon_span = tiny_bbox[2] - tiny_bbox[0] + lat_span = tiny_bbox[3] - tiny_bbox[1] + assert lon_span >= 0.25, f"Longitude span {lon_span} should be >= 0.25°" + assert lat_span >= 0.25, f"Latitude span {lat_span} should be >= 0.25°" + + # Verify warning was logged + assert any( + "Bounding box expanded" in record.message for record in caplog.records + ), "Warning about bbox expansion should be logged" + assert any("Original size" in record.message for record in caplog.records), ( + "Warning should include original size" + ) + + def test_find_available_data_cds__bbox_expansion_preserves_center_point( + self, start_date + ): + """ + Test that bbox expansion preserves the center point of the original bbox. + """ + collection = "derived-era5-single-levels-daily-statistics" + # Small bbox centered at (0.0, 0.0) + tiny_bbox = [-0.01, -0.01, 0.01, 0.01] + + # Calculate original center + orig_center_lon = (tiny_bbox[0] + tiny_bbox[2]) / 2 + orig_center_lat = (tiny_bbox[1] + tiny_bbox[3]) / 2 + + dc = DataConnector(connector_type=self.connector_type) + dc.connector.find_data( + collection, start_date, start_date, bbox=tiny_bbox, bands=self.bands + ) + + # Calculate new center + new_center_lon = (tiny_bbox[0] + tiny_bbox[2]) / 2 + new_center_lat = (tiny_bbox[1] + tiny_bbox[3]) / 2 + + # Verify center is preserved (within floating point tolerance) + assert abs(new_center_lon - orig_center_lon) < 1e-6, ( + f"Center longitude changed from {orig_center_lon} to {new_center_lon}" + ) + assert abs(new_center_lat - orig_center_lat) < 1e-6, ( + f"Center latitude changed from {orig_center_lat} to {new_center_lat}" + ) + + def test_find_available_data_cds__bbox_expansion_only_in_deficient_dimension( + self, start_date + ): + """ + Test that bbox expansion only expands dimensions that are too small. + """ + collection = "derived-era5-single-levels-daily-statistics" + # Bbox with sufficient longitude but insufficient latitude + bbox_small_lat = [34.0, -0.01, 34.5, 0.01] # 0.5° lon, 0.02° lat + original_lon_span = bbox_small_lat[2] - bbox_small_lat[0] + + dc = DataConnector(connector_type=self.connector_type) + dc.connector.find_data( + collection, start_date, start_date, bbox=bbox_small_lat, bands=self.bands + ) + + # Verify longitude span unchanged (was already sufficient) + new_lon_span = bbox_small_lat[2] - bbox_small_lat[0] + assert abs(new_lon_span - original_lon_span) < 1e-6, ( + "Longitude span should not change when already sufficient" + ) + + # Verify latitude span expanded to minimum + new_lat_span = bbox_small_lat[3] - bbox_small_lat[1] + assert new_lat_span >= 0.25, f"Latitude span {new_lat_span} should be >= 0.25°" + + def test_find_available_data_cds__bbox_no_expansion_when_sufficient( + self, start_date, caplog + ): + """ + Test that bbox is not expanded when it already meets minimum requirements. + """ + + collection = "derived-era5-single-levels-daily-statistics" + # Bbox that already meets minimum (0.5° x 0.5°) + sufficient_bbox = [34.0, -0.25, 34.5, 0.25] + original_bbox = sufficient_bbox.copy() + + dc = DataConnector(connector_type=self.connector_type) + + with caplog.at_level(logging.WARNING): + dc.connector.find_data( + collection, + start_date, + start_date, + bbox=sufficient_bbox, + bands=self.bands, + ) + + # Verify bbox was NOT modified + assert sufficient_bbox == original_bbox, ( + "Bbox should not be modified when already sufficient" + ) + + # Verify no warning was logged about expansion + assert not any( + "Bounding box expanded" in record.message for record in caplog.records + ), "No warning should be logged when bbox is already sufficient" + + def test_find_available_data_cds__bbox_expansion_not_applied_to_cordex( + self, start_date + ): + """ + Test that bbox expansion is NOT applied to CORDEX collections (they use domain mapping). + """ + collection = "projections-cordex-domains-single-levels" + # Small bbox that would trigger expansion for ERA5 + tiny_bbox = [10.0, 45.0, 10.02, 45.02] + + dc = DataConnector(connector_type=self.connector_type) + + # CORDEX should use domain mapping, not bbox expansion + # This should work without expanding the bbox (it maps to a domain instead) + unique_dates, results = dc.connector.find_data( + collection, start_date, start_date, bbox=tiny_bbox, bands=self.bands + ) + + # For CORDEX, the bbox might be modified by domain mapping, but not by the + # expansion logic. We just verify it doesn't raise an error about being too small. + assert unique_dates is not None + + def test_get_data_cds__bbox_expansion( + self, mock_cds_client, start_date, save_file_dir, get_data_clean_up, caplog + ): + """ + Test that get_data also expands small bboxes and logs warning. + """ + + collection = "derived-era5-single-levels-daily-statistics" + # Small bbox + tiny_bbox = [-1.32, 51.06, -1.30, 51.08] + original_bbox = tiny_bbox.copy() + save_file = f"{save_file_dir}/{self.connector_type}_{collection}_small_bbox.nc" + + dc = DataConnector(connector_type=self.connector_type) + + with caplog.at_level(logging.WARNING): + data = dc.connector.get_data( + data_collection_name=collection, + date_start=start_date, + date_end=start_date, + bbox=tiny_bbox, + bands=self.bands, + save_file=save_file, + ) + + # Verify bbox was expanded + assert tiny_bbox != original_bbox, "Bbox should have been modified in get_data" + + # Verify dimensions are at least 0.25° + lon_span = tiny_bbox[2] - tiny_bbox[0] + lat_span = tiny_bbox[3] - tiny_bbox[1] + assert lon_span >= 0.25, f"Longitude span {lon_span} should be >= 0.25°" + assert lat_span >= 0.25, f"Latitude span {lat_span} should be >= 0.25°" + + # Verify warning was logged + assert any( + "Bounding box expanded" in record.message for record in caplog.records + ), "Warning about bbox expansion should be logged in get_data" + + # Verify data was retrieved successfully + assert data is not None + assert isinstance(data, xr.Dataset) + + def test_get_data__negative_longitude_conversion( self, mock_cds_client, start_date, bbox, save_file_dir, get_data_clean_up ): """ - Test that negative longitudes are automatically converted to 0-360° convention. + Test that negative longitudes work correctly with ERA5 data. - CDS uses 0-360° longitude convention, but users typically use -180 to 180°. - The connector should automatically convert negative longitudes. + ERA5 uses -180 to 180° longitude convention (not 0-360°). + This test verifies that negative longitudes are handled correctly. """ collection = "derived-era5-single-levels-daily-statistics" # Use bbox with negative longitude (standard -180 to 180° convention) # Oxford, UK: -1.32° to -1.07° should be converted to 358.68° to 358.93° negative_lon_bbox = [-1.32, 51.70, -1.07, 51.95] - save_file = f"{save_file_dir}/{self.connector_type}_{collection}.tif" + save_file = f"{save_file_dir}/{self.connector_type}_{collection}.nc" dc = DataConnector(connector_type=self.connector_type) @@ -182,7 +358,83 @@ def test_negative_longitude_conversion( assert len(data) > 0 # Verify the data was retrieved successfully - assert isinstance(data, xr.DataArray) + assert isinstance(data, xr.Dataset) + + # Verify stepType attributes are preserved + for var in data.data_vars: + assert "stepType" in data[var].attrs, ( + f"Variable {var} missing stepType attribute" + ) + + def test_get_data__longitude_system_no_wraparound( + self, mock_cds_client, start_date, save_file_dir, get_data_clean_up + ): + """ + Test that bbox spanning negative to positive longitudes doesn't cause wraparound. + + This is a regression test for a bug where bbox [-10, 40, 5, 50] was incorrectly + converted to [50, 350, 40, 5] in the CDS API request, causing the API to interpret + it as wrapping around the globe and returning only a single longitude point (177.5°). + + The fix ensures ERA5 data uses -180/180° system without conversion to 0-360°, + and uses coords='minimal' in xr.concat to handle inconsistent coordinates. + """ + collection = "derived-era5-single-levels-daily-statistics" + # Bbox spanning from negative to positive longitude (Western Europe) + # This should NOT wrap around the globe + bbox_europe = [-10, 40, 5, 50] # Portugal to Germany + save_file = f"{save_file_dir}/{self.connector_type}_{collection}_europe.nc" + + dc = DataConnector(connector_type=self.connector_type) + + data = dc.connector.get_data( + data_collection_name=collection, + date_start=start_date, + date_end=start_date, + bbox=bbox_europe, + bands=self.bands, + save_file=save_file, + ) + + assert data is not None + assert isinstance(data, xr.Dataset) + + # Verify we got a proper 2D grid, not a single longitude point + # The bbox spans 15° longitude, so at 0.25° resolution we should have ~60 points + for var in data.data_vars: + if var != "spatial_ref": # Skip the CRS variable + lon_dim = "longitude" if "longitude" in data[var].dims else "lon" + lat_dim = "latitude" if "latitude" in data[var].dims else "lat" + + # Check we have multiple longitude points (not just 1) + assert len(data[var][lon_dim]) > 1, ( + f"Expected multiple longitude points, got {len(data[var][lon_dim])}. " + "This suggests the bbox caused a wraparound issue." + ) + + # Check we have multiple latitude points + assert len(data[var][lat_dim]) > 1, ( + f"Expected multiple latitude points, got {len(data[var][lat_dim])}" + ) + + # Verify longitude range is correct (should be close to -10 to 5) + lon_values = data[var][lon_dim].values + assert lon_values.min() >= -11, ( + f"Min longitude {lon_values.min()} outside expected range" + ) + assert lon_values.max() <= 6, ( + f"Max longitude {lon_values.max()} outside expected range" + ) + + # Verify latitude range is correct (should be close to 40 to 50) + # Allow some tolerance for grid alignment (±2°) + lat_values = data[var][lat_dim].values + assert lat_values.min() >= 38, ( + f"Min latitude {lat_values.min()} outside expected range" + ) + assert lat_values.max() <= 52, ( + f"Max latitude {lat_values.max()} outside expected range" + ) @pytest.mark.parametrize( "collection", @@ -202,7 +454,7 @@ def test_get_data_cds( """ date_start = "2025-01-01" date_end = "2025-01-02" - save_file = f"{save_file_dir}/{self.connector_type}_{collection}.tif" + save_file = f"{save_file_dir}/{self.connector_type}_{collection}.nc" dc = DataConnector(connector_type=self.connector_type) data = dc.connector.get_data( data_collection_name=collection, @@ -215,18 +467,26 @@ def test_get_data_cds( assert data is not None assert len(data) > 0 # Check we got data - assert isinstance(data, xr.DataArray) + # Now returns Dataset instead of DataArray + assert isinstance(data, xr.Dataset) assert data.rio.crs == CRS.from_epsg(4326) - # Mock data contains 5 bands (fg10, t2m, tp, u10, v10) - one per NetCDF file - assert len(data.coords["band"]) == 5 + # Verify stepType attributes are preserved + for var in data.data_vars: + assert "stepType" in data[var].attrs, ( + f"Variable {var} missing stepType attribute" + ) + + # Mock data contains 5 variables (fg10, t2m, tp, u10, v10) - one per NetCDF file + # Note: Dataset has variables as data_vars, not a 'band' coordinate + assert len(data.data_vars) == 5 # Mock data contains 2 time steps (2025-01-01 and 2025-01-02) assert len(data.time) == 2 - # Check that files were created for the dates in the mock data - assert os.path.exists(save_file.replace(".tif", "_2025-01-01.tif")) is True - assert os.path.exists(save_file.replace(".tif", "_2025-01-02.tif")) is True + # Check that NetCDF files were created for the dates in the mock data + assert os.path.exists(save_file.replace(".nc", "_2025-01-01.nc")) is True + assert os.path.exists(save_file.replace(".nc", "_2025-01-02.nc")) is True def test_get_data_bbox_too_small_for_era5_resolution( self, mock_cds_client_bbox_error, start_date, save_file_dir @@ -241,7 +501,7 @@ def test_get_data_bbox_too_small_for_era5_resolution( collection = "derived-era5-single-levels-daily-statistics" # This bbox is only 0.02° x 0.02°, which is smaller than ERA5's 0.25° grid tiny_bbox = [-1.32, 51.06, -1.30, 51.08] - save_file = f"{save_file_dir}/{self.connector_type}_{collection}.tif" + save_file = f"{save_file_dir}/{self.connector_type}_{collection}.nc" dc = DataConnector(connector_type=self.connector_type) with pytest.raises( diff --git a/tests/integration_tests/cds.py b/tests/integration_tests/cds.py new file mode 100644 index 0000000..d251ee9 --- /dev/null +++ b/tests/integration_tests/cds.py @@ -0,0 +1,116 @@ +# © Copyright IBM Corporation 2025-2026 +# SPDX-License-Identifier: Apache-2.0 + + +# TerraKit - easy Geospatial data search and query +# Requires tokens for data connectors in .env +import os +import xarray as xr + +from glob import glob +from shutil import rmtree + +from rasterio.crs import CRS + +from terrakit import DataConnector + + +############## CLEAN UP FUNCTIONS ################# +def get_data_clean_up(save_file_dir): + files = glob(f"{save_file_dir}/*.nc") + print(f"Test clean up. Deleting {files}") + for f in files: + os.remove(f) + + +SAVE_FILE_DIR = "./tests/resources/intergration_test_data" +rmtree(SAVE_FILE_DIR, ignore_errors=True) +os.makedirs(SAVE_FILE_DIR, exist_ok=True) +################################################### + +################ SET UP ########################### +bbox = [-10, 40, 5, 50] +date_start = "2024-01-01" +date_end = "2024-01-03" +################################################### + + +# Example 1 +data_connector = "climate_data_store" +dc = DataConnector(connector_type=data_connector) +dc.connector.list_collections() + +collection_name = "derived-era5-single-levels-daily-statistics" + +bands = [ + # Instantaneous + "2m_temperature", + "10m_u_component_of_wind", + "mean_sea_level_pressure", + # Accumulated + "total_precipitation", + "surface_net_solar_radiation", + "evaporation", + # Mean rate + "mean_total_precipitation_rate", + # Min/Max + "maximum_2m_temperature_since_previous_post_processing", + "minimum_2m_temperature_since_previous_post_processing", +] + +# Additional query parameters +query_params = { + "daily_statistic": "daily_mean", + "time_zone": "utc+00:00", + "frequency": "1_hourly", +} + +unique_dates, results = dc.connector.find_data( + data_collection_name=collection_name, + date_start=date_start, + date_end=date_end, + bbox=bbox, + bands=bands, +) + +# print(unique_dates) + +save_file = f"{SAVE_FILE_DIR}/{data_connector}_{collection_name}.nc" + +# Single day +ds = dc.connector.get_data( + data_collection_name=collection_name, + date_start=unique_dates[0], + date_end=unique_dates[0], + bbox=bbox, + bands=bands, + save_file=save_file, +) + +assert isinstance(ds, xr.Dataset) +assert len(ds.data_vars) == len(bands) +assert ds.rio.crs == CRS.from_epsg(4326) +# assert len(ds.coords["band"]) == len(bands) +assert len(ds.time) == 1 +assert os.path.exists(save_file.replace(".nc", f"_{unique_dates[0]}.nc")) is True +assert len(glob(f"{SAVE_FILE_DIR}/*.nc")) == 1 +get_data_clean_up(SAVE_FILE_DIR) + +# Multi day +da = dc.connector.get_data( + data_collection_name=collection_name, + date_start=unique_dates[0], + date_end=unique_dates[-1], + bbox=bbox, + bands=bands, + save_file=save_file, +) + +assert isinstance(ds, xr.Dataset) +assert len(ds.data_vars) == len(bands) +assert ds.rio.crs == CRS.from_epsg(4326) +# assert len(ds.coords["band"]) == len(bands) +assert len(ds.time) == len(unique_dates) +assert os.path.exists(save_file.replace(".nc", f"_{unique_dates[0]}.nc")) is True +assert len(glob(f"{SAVE_FILE_DIR}/*.nc")) == len(unique_dates) +get_data_clean_up(SAVE_FILE_DIR)