diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index a955eb7..24375a3 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -15,7 +15,7 @@ Steps to reproduce the behavior: > Ex. > -> 1. Install stactools-ephemeral +> 1. Install stactools-noaa-hrrr > 2. Run `scripts/test` > 3. See error diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index abf994c..db5b1c8 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -27,7 +27,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.9", "3.10", "3.11", "3.12"] defaults: run: shell: bash -l {0} diff --git a/.gitignore b/.gitignore index 94fbcfd..486fdac 100644 --- a/.gitignore +++ b/.gitignore @@ -138,3 +138,5 @@ dmypy.json # Cython debug symbols cython_debug/ + +scratch diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c7a6bc..b18ebcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,4 +25,4 @@ number as needed. - Nothing. -[Unreleased]: +[Unreleased]: diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 171ec16..eaf5f71 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -6,7 +6,7 @@ In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, -level of experience, education, socio-economic status, nationality, personal +level of experience, education, socioeconomic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards diff --git a/LICENSE b/LICENSE index 9f4fda8..cc74381 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ This software is licensed under the Apache 2 license, quoted below. -Copyright 2022 COMPANY [COMPANY WEBPAGE URL] +Copyright 2024 Development Seed [https://developmentseed.org/] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of diff --git a/README-template.md b/README-template.md deleted file mode 100644 index 13459f4..0000000 --- a/README-template.md +++ /dev/null @@ -1,67 +0,0 @@ -# stactools-ephemeral - -[![PyPI](https://img.shields.io/pypi/v/stactools-ephemeral?style=for-the-badge)](https://pypi.org/project/stactools-ephemeral/) -![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/stactools-packages/ephemeral/continuous-integration.yml?style=for-the-badge) - -- Name: ephemeral -- Package: `stactools.ephemeral` -- [stactools-ephemeral on PyPI](https://pypi.org/project/stactools-ephemeral/) -- Owner: @githubusername -- [Dataset homepage](http://example.com) -- STAC extensions used: - - [proj](https://github.com/stac-extensions/projection/) -- Extra fields: - - `ephemeral:custom`: A custom attribute -- [Browse the example in human-readable form](https://radiantearth.github.io/stac-browser/#/external/raw.githubusercontent.com/stactools-packages/ephemeral/main/examples/collection.json) -- [Browse a notebook demonstrating the example item and collection](https://github.com/stactools-packages/ephemeral/tree/main/docs/example.ipynb) - -A short description of the package and its usage. - -## STAC examples - -- [Collection](examples/collection.json) -- [Item](examples/item/item.json) - -## Installation - -```shell -pip install stactools-ephemeral -``` - -## Command-line usage - -Description of the command line functions - -```shell -stac ephemeral create-item source destination -``` - -Use `stac ephemeral --help` to see all subcommands and options. - -## Contributing - -We use [pre-commit](https://pre-commit.com/) to check any changes. -To set up your development environment: - -```shell -pip install -e '.[dev]' -pre-commit install -``` - -To check all files: - -```shell -pre-commit run --all-files -``` - -To run the tests: - -```shell -pytest -vv -``` - -If you've updated the STAC metadata output, update the examples: - -```shell -scripts/update-examples -``` diff --git a/README.md b/README.md index 1ecbf27..2709e43 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,82 @@ -# stactools-template - -This is a template repo used for creating new packages for `stactools`. - -## How to use - -1. Clone this template repository as your package name, e.g. `landsat`. - This name should be short, memorable, and a valid Python package name (i.e. - it shouldn't start with a number, etc). It can, however, include a hyphen, in - which case the name for Python imports will be the underscored version, e.g. - `landsat-8` goes to `stactools.landsat_8`. Your name will be used on PyPI to - publish the package in the stactools namespace, e.g. `stactools-landsat`. -2. Change into the top-level directory of your package and run `scripts/rename`. - This will update _most_ of the files in the repository with your new package name. -3. Update `pyproject.toml` with your package description and such. -4. Install with the development requirements (`pip install -e '.[dev]'`). -5. Update the LICENSE with your company's information (or whomever holds the copyright). -6. Edit or replace the existing functions to create stac Items and Collections - for your dataset. -7. Add example Items (and Collections and Catalogs, if included) to an - `examples/` directory. -8. Delete this file, and rename `README-template.md` to `README.md`. Update your - new README to provide information about how to use your package. +# stactools-noaa-hrrr + +[![PyPI](https://img.shields.io/pypi/v/stactools-noaa-hrrr?style=for-the-badge)](https://pypi.org/project/stactools-noaa-hrrr/) +![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/stactools-packages/noaa-hrrr/continuous-integration.yml?style=for-the-badge) + +- Name: noaa-hrrr +- Package: `stactools.noaa_hrrr` +- [stactools-noaa-hrrr on PyPI](https://pypi.org/project/stactools-noaa-hrrr/) +- Owner: @hrodmn +- [Dataset homepage](https://rapidrefresh.noaa.gov/hrrr/) +- STAC extensions used: + - [forecast](https://github.com/stac-extensions/forecast) + - [item-assets](https://github.com/stac-extensions/item-assets) + - [datacube](https://github.com/stac-extensions/datacube) (coming soon) +- Extra fields: + - `noaa-hrrr:forecast_cycle_type`: either standard (18-hour) or extended (48-hour) + - `noaa-hrrr:region`: either `conus` or `alaska` +- [Browse the example in human-readable form](https://radiantearth.github.io/stac-browser/#/external/raw.githubusercontent.com/stactools-packages/noaa-hrrr/main/examples/collection.json) +- [Browse a notebook demonstrating the example item and collection](https://github.com/stactools-packages/noaa-hrrr/tree/main/docs/example.ipynb) + +A short description of the package and its usage. + +## STAC examples + +- [Collection](examples/collection.json) +- [Item](examples/item/item.json) + +## Installation + +Install `stactools-noaa-hrrr` with pip: + +```shell +pip install stactools-noaa-hrrr +``` + +## Command-line usage + +Description of the command line functions + +```shell +stac noaahrrr create-item 2024-05-01T12 10 conus azure example-item.json +``` + +### Docker + +You can launch a jupyterhub server in a docker container with all of the +dependencies installed using these commands: + +```shell +docker/build +docker/jupyter +``` + +Use `stac noaahrrr --help` to see all subcommands and options. + +## Contributing + +We use [pre-commit](https://pre-commit.com/) to check any changes. +To set up your development environment: + +```shell +pip install -e '.[dev]' +pre-commit install +``` + +To check all files: + +```shell +pre-commit run --all-files +``` + +To run the tests: + +```shell +pytest -vv +``` + +If you've updated the STAC metadata output, update the examples: + +```shell +scripts/update-examples +``` diff --git a/docker/Dockerfile b/docker/Dockerfile index 4491aef..fc1390b 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -9,23 +9,22 @@ RUN conda update conda && \ FROM base as dependencies ENV PATH="/opt/venv/bin:$PATH" -WORKDIR /opt/stactools-ephemeral +WORKDIR /opt/stactools-noaa-hrrr COPY --from=base /opt/conda /opt/conda COPY pyproject.toml ./ -COPY src/stactools/ephemeral/__init__.py src/stactools/ephemeral/ +COPY src/stactools/noaa_hrrr/__init__.py src/stactools/noaa_hrrr/ RUN apt-get -y -q update \ && apt-get -y -q install build-essential \ && rm -rf /var/lib/apt/lists/ RUN python -m venv /opt/venv RUN pip install . \ - && ls /opt/venv \ - && rm -r /opt/venv/lib/python3.11/site-packages/stactools/ephemeral - + && ls /opt/venv/lib/ \ + && rm -r /opt/venv/lib/python3.12/site-packages/stactools/noaa_hrrr FROM dependencies as builder ENV PATH="/opt/venv/bin:$PATH" -WORKDIR /opt/stactools-ephemeral +WORKDIR /opt/stactools-noaa-hrrr COPY --from=base /opt/conda /opt/conda COPY --from=dependencies /opt/venv /opt/venv COPY pyproject.toml ./ @@ -33,25 +32,24 @@ COPY src ./src RUN pip install -U pip \ && pip install . WORKDIR / -RUN rm -rf /opt/stactools-ephemeral -CMD [ "stac", "ephemeralcmd" ] +RUN rm -rf /opt/stactools-noaa-hrrr +CMD [ "stac", "noaahrrr" ] FROM dependencies as dev-dependencies ENV PATH="/opt/venv/bin:$PATH" -WORKDIR /opt/stactools-ephemeral +WORKDIR /opt/stactools-noaa-hrrr COPY --from=dependencies /opt/venv /opt/venv COPY pyproject.toml . RUN pip install -e '.[dev]' \ && git init - FROM dev-dependencies as dev-builder ENV PATH="/opt/venv/bin:$PATH" -WORKDIR /opt/stactools-ephemeral +WORKDIR /opt/stactools-noaa-hrrr COPY --from=base /opt/conda /opt/conda COPY --from=dependencies /opt/venv /opt/venv COPY . . -CMD [ "stac", "ephemeralcmd" ] +CMD [ "stac", "noaahrrr" ] diff --git a/docker/build b/docker/build index da2e90d..ba8b1d0 100755 --- a/docker/build +++ b/docker/build @@ -38,12 +38,12 @@ if [ "${BASH_SOURCE[0]}" = "${0}" ]; then usage else docker build $PULL \ - -t stactools-packages/ephemeral:latest \ + -t stactools-packages/noaa-hrrr:latest \ -f docker/Dockerfile \ --target builder \ . docker build $PULL \ - -t stactools-packages/ephemeral:dev \ + -t stactools-packages/noaa-hrrr:dev \ -f docker/Dockerfile \ --target dev-builder \ . diff --git a/docker/cibuild b/docker/cibuild index 96c6e68..0e08bad 100755 --- a/docker/cibuild +++ b/docker/cibuild @@ -15,11 +15,12 @@ Runs CI in the docker dev container. if [ "${BASH_SOURCE[0]}" = "${0}" ]; then docker build $PULL \ - -t stactools-packages/ephemeral:dev \ + -t stactools-packages/noaa-hrrr:dev \ -f docker/Dockerfile \ --target dev-builder \ . docker run --rm \ + -e DOCKER=true \ --entrypoint scripts/cibuild \ - stactools-packages/ephemeral:dev + stactools-packages/noaa-hrrr:dev fi diff --git a/docker/console b/docker/console index df84631..3029020 100755 --- a/docker/console +++ b/docker/console @@ -15,8 +15,8 @@ Run a console in a docker container with all prerequisites installed. if [ "${BASH_SOURCE[0]}" = "${0}" ]; then docker run --rm -it \ - -v "$(pwd)":/opt/stactools-ephemeral \ + -v "$(pwd)":/opt/stactools-noaa-hrrr \ -p 8000:8000 \ --entrypoint /bin/bash \ - stactools-packages/ephemeral:dev + stactools-packages/noaa-hrrr:dev fi diff --git a/docker/jupyter b/docker/jupyter new file mode 100755 index 0000000..f18e747 --- /dev/null +++ b/docker/jupyter @@ -0,0 +1,23 @@ +#!/bin/bash + +set -e + +if [[ -n "${STACTOOLS_DEBUG}" ]]; then + set -x +fi + +function usage() { + echo -n \ + "Usage: $(basename "$0") +Run a jupyter notebook server in a docker container with all prerequisites installed. +" +} + +if [ "${BASH_SOURCE[0]}" = "${0}" ]; then + docker run --rm -it \ + -v "$(pwd)":/opt/stactools-noaa-hrrr \ + -p 8000:8000 \ + --entrypoint /bin/bash \ + stactools-packages/noaa-hrrr:dev \ + -c "source /opt/conda/bin/activate && source /opt/venv/bin/activate && jupyter lab --ip=0.0.0.0 --port=8000 --no-browser --allow-root" +fi diff --git a/docker/lint b/docker/lint index afd37ad..d0735d2 100755 --- a/docker/lint +++ b/docker/lint @@ -15,7 +15,7 @@ Run code formatters in a docker container with all prerequisites installed. if [ "${BASH_SOURCE[0]}" = "${0}" ]; then docker run --rm -it \ - -v "$(pwd)":/opt/stactools-ephemeral \ + -v "$(pwd)":/opt/stactools-noaa-hrrr \ --entrypoint scripts/lint \ - stactools-packages/ephemeral:dev + stactools-packages/noaa-hrrr:dev fi diff --git a/docker/stac b/docker/stac index b800f1e..9278ff2 100755 --- a/docker/stac +++ b/docker/stac @@ -17,6 +17,6 @@ will be reflected in the execution. if [ "${BASH_SOURCE[0]}" = "${0}" ]; then docker run --rm -it \ - -v "$(pwd)":/opt/stactools-ephemeral \ - stactools-packages/ephemeral:dev "${@}" + -v "$(pwd)":/opt/stactools-noaa-hrrr \ + stactools-packages/noaa-hrrr:dev "${@}" fi diff --git a/docker/test b/docker/test index 4175aca..49d5e2a 100755 --- a/docker/test +++ b/docker/test @@ -15,7 +15,7 @@ Run linting and tests in a docker container with all prerequisites installed. if [ "${BASH_SOURCE[0]}" = "${0}" ]; then docker run --rm -it \ - -v "$(pwd)":/opt/stactools-ephemeral \ + -v "$(pwd)":/opt/stactools-noaa-hrrr \ --entrypoint scripts/test \ - stactools-packages/ephemeral:dev + stactools-packages/noaa-hrrr:dev fi diff --git a/docs/example.ipynb b/docs/example.ipynb index 1b3024b..135ac60 100644 --- a/docs/example.ipynb +++ b/docs/example.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# stactools-ephemeral\n", + "# stactools-noaa-hrrr\n", "\n", "This is a small notebook demonstrating use of this package and its generated items and collections.\n", "It is intended to be executed from the root of this repository, since it relies on files that have been checked into this repo." @@ -23,7 +23,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -34,16 +34,32 @@ "\n", "
\n", "
\n", @@ -51,78 +67,75 @@ " \n", " \n", " \n", - "
  • \n", - " type\n", - " \"Collection\"\n", + "
  • \n", + " type\n", + " \"Collection\"\n", "
  • \n", " \n", " \n", " \n", " \n", " \n", - "
  • \n", - " id\n", - " \"example-collection\"\n", + "
  • \n", + " id\n", + " \"noaa-hrrr\"\n", "
  • \n", " \n", " \n", " \n", " \n", " \n", - "
  • \n", - " stac_version\n", - " \"1.0.0\"\n", + "
  • \n", + " stac_version\n", + " \"1.0.0\"\n", "
  • \n", " \n", " \n", " \n", " \n", " \n", - "
  • \n", - " description\n", - " \"An example collection\"\n", + "
  • \n", + " description\n", + " \"The NOAA HRRR is a real-time 3km resolution, hourly updated, cloud-resolving, convection-allowing atmospheric model, initialized by 3km grids with 3km radar assimilation. Radar data is assimilated in the HRRR every 15 min over a 1-hour period adding further detail to that provided by the hourly data assimilation from the 13km radar-enhanced Rapid Refresh (RAP) system.\"\n", "
  • \n", " \n", " \n", " \n", " \n", "
  • \n", - " \n", - " links\n", - " [] 3 items\n", - " \n", + " links[] 5 items\n", " \n", "
      \n", " \n", " \n", " \n", "
    • \n", - " 0\n", + " 0\n", "
        \n", " \n", " \n", " \n", - "
      • \n", - " rel\n", - " \"self\"\n", + "
      • \n", + " rel\n", + " \"self\"\n", "
      • \n", " \n", " \n", " \n", " \n", " \n", - "
      • \n", - " href\n", - " \"/Users/gadomski/Code/stactools-packages/template/examples/collection.json\"\n", + "
      • \n", + " href\n", + " \"/opt/stactools-noaa-hrrr/examples/collection.json\"\n", "
      • \n", " \n", " \n", " \n", " \n", " \n", - "
      • \n", - " type\n", - " \"application/json\"\n", + "
      • \n", + " type\n", + " \"application/json\"\n", "
      • \n", " \n", " \n", @@ -139,41 +152,41 @@ " \n", " \n", "
      • \n", - " 1\n", + " 1\n", "
          \n", " \n", " \n", " \n", - "
        • \n", - " rel\n", - " \"root\"\n", + "
        • \n", + " rel\n", + " \"root\"\n", "
        • \n", " \n", " \n", " \n", " \n", " \n", - "
        • \n", - " href\n", - " \"/Users/gadomski/Code/stactools-packages/template/examples/collection.json\"\n", + "
        • \n", + " href\n", + " \"/opt/stactools-noaa-hrrr/examples/collection.json\"\n", "
        • \n", " \n", " \n", " \n", " \n", " \n", - "
        • \n", - " type\n", - " \"application/json\"\n", + "
        • \n", + " type\n", + " \"application/json\"\n", "
        • \n", " \n", " \n", " \n", " \n", " \n", - "
        • \n", - " title\n", - " \"Example collection\"\n", + "
        • \n", + " title\n", + " \"NOAA High Resolution Rapid Refresh (HRRR) collection\"\n", "
        • \n", " \n", " \n", @@ -190,32 +203,41 @@ " \n", " \n", "
        • \n", - " 2\n", + " 2\n", "
            \n", " \n", " \n", " \n", - "
          • \n", - " rel\n", - " \"item\"\n", + "
          • \n", + " rel\n", + " \"license\"\n", + "
          • \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
          • \n", + " href\n", + " \"https://creativecommons.org/licenses/by/4.0/\"\n", "
          • \n", " \n", " \n", " \n", " \n", " \n", - "
          • \n", - " href\n", - " \"./example-item/example-item.json\"\n", + "
          • \n", + " type\n", + " \"text/html\"\n", "
          • \n", " \n", " \n", " \n", " \n", " \n", - "
          • \n", - " type\n", - " \"application/json\"\n", + "
          • \n", + " title\n", + " \"CC-BY-4.0 license\"\n", "
          • \n", " \n", " \n", @@ -227,23 +249,107 @@ " \n", "
          \n", " \n", - "
        • \n", + "
            \n", + " \n", + " \n", + " \n", + "
          • \n", + " 3\n", + "
              \n", + " \n", + " \n", " \n", + "
            • \n", + " rel\n", + " \"documentation\"\n", + "
            • \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - "
            • \n", - " custom_attribute\n", - " \"foo\"\n", + "
            • \n", + " href\n", + " \"https://rapidrefresh.noaa.gov/hrrr/\"\n", + "
            • \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
            • \n", + " type\n", + " \"text/html\"\n", + "
            • \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
            • \n", + " title\n", + " \"NOAA HRRR documentation\"\n", + "
            • \n", + " \n", + " \n", + " \n", + "
            \n", + "
          • \n", + " \n", + " \n", + " \n", + "
          \n", + " \n", + "
            \n", + " \n", + " \n", + " \n", + "
          • \n", + " 4\n", + "
              \n", + " \n", + " \n", + " \n", + "
            • \n", + " rel\n", + " \"item\"\n", + "
            • \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
            • \n", + " href\n", + " \"./hrrr-conus-2024-05-10T12-FH0/hrrr-conus-2024-05-10T12-FH0.json\"\n", + "
            • \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
            • \n", + " type\n", + " \"application/json\"\n", "
            • \n", " \n", + " \n", + " \n", + "
            \n", + "
          • \n", + " \n", + " \n", + " \n", + "
          \n", + " \n", + "
      • \n", " \n", " \n", " \n", " \n", - "
      • \n", - " title\n", - " \"Example collection\"\n", + "
      • \n", + " title\n", + " \"NOAA High Resolution Rapid Refresh (HRRR) collection\"\n", "
      • \n", " \n", " \n", @@ -251,38 +357,32 @@ " \n", " \n", "
      • \n", - " extent\n", + " extent\n", "
          \n", " \n", " \n", " \n", "
        • \n", - " spatial\n", + " spatial\n", "
            \n", " \n", " \n", "
          • \n", - " \n", - " bbox\n", - " [] 1 items\n", - " \n", + " bbox[] 1 items\n", " \n", "
              \n", " \n", " \n", "
            • \n", - " \n", - " 0\n", - " [] 4 items\n", - " \n", + " 0[] 4 items\n", " \n", "
                \n", " \n", " \n", " \n", - "
              • \n", - " 0\n", - " -61.287624\n", + "
              • \n", + " 0\n", + " -134.12142793280145\n", "
              • \n", " \n", " \n", @@ -293,9 +393,9 @@ " \n", " \n", " \n", - "
              • \n", - " 1\n", - " 72.229798\n", + "
              • \n", + " 1\n", + " 21.122192719272277\n", "
              • \n", " \n", " \n", @@ -306,9 +406,9 @@ " \n", " \n", " \n", - "
              • \n", - " 2\n", - " -52.301599\n", + "
              • \n", + " 2\n", + " -60.891244531606546\n", "
              • \n", " \n", " \n", @@ -319,9 +419,9 @@ " \n", " \n", " \n", - "
              • \n", - " 3\n", - " 74.622043\n", + "
              • \n", + " 3\n", + " 52.62870335266728\n", "
              • \n", " \n", " \n", @@ -344,32 +444,26 @@ " \n", " \n", "
              • \n", - " temporal\n", + " temporal\n", "
                  \n", " \n", " \n", "
                • \n", - " \n", - " interval\n", - " [] 1 items\n", - " \n", + " interval[] 1 items\n", " \n", "
                    \n", " \n", " \n", "
                  • \n", - " \n", - " 0\n", - " [] 2 items\n", - " \n", + " 0[] 2 items\n", " \n", "
                      \n", " \n", " \n", " \n", - "
                    • \n", - " 0\n", - " \"2023-09-12T11:48:03.199913Z\"\n", + "
                    • \n", + " 0\n", + " \"2024-05-10T12:00:00Z\"\n", "
                    • \n", " \n", " \n", @@ -380,9 +474,9 @@ " \n", " \n", " \n", - "
                    • \n", - " 1\n", - " \"2023-09-12T11:48:03.199913Z\"\n", + "
                    • \n", + " 1\n", + " \"2024-05-10T12:00:00Z\"\n", "
                    • \n", " \n", " \n", @@ -409,103 +503,50 @@ " \n", " \n", " \n", - "
                    • \n", - " license\n", - " \"proprietary\"\n", - "
                    • \n", - " \n", - " \n", - " \n", - "
                    \n", - "
  • \n", - "
    " - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "\n", - "\n", - "\n", - "
    \n", - "
    \n", - "
      \n", - " \n", - " \n", - " \n", - "
    • \n", - " type\n", - " \"Feature\"\n", + "
    • \n", + " license\n", + " \"CC-BY-4.0\"\n", "
    • \n", " \n", " \n", " \n", " \n", - " \n", - "
    • \n", - " stac_version\n", - " \"1.0.0\"\n", - "
    • \n", - " \n", - " \n", + "
    • \n", + " keywords[] 5 items\n", + " \n", + "
        \n", + " \n", " \n", " \n", - " \n", - "
      • \n", - " id\n", - " \"example-item\"\n", + "
      • \n", + " 0\n", + " \"NOAA\"\n", "
      • \n", " \n", - " \n", " \n", - " \n", - " \n", - "
      • \n", - " properties\n", + " \n", + "
      \n", + " \n", "
        \n", " \n", " \n", " \n", - "
      • \n", - " proj:epsg\n", - " 32621\n", + "
      • \n", + " 1\n", + " \"HRRR\"\n", "
      • \n", " \n", " \n", " \n", - " \n", - "
      • \n", - " \n", - " proj:transform\n", - " [] 6 items\n", - " \n", + "
      \n", " \n", "
        \n", " \n", " \n", " \n", - "
      • \n", - " 0\n", - " 100.01126757344893\n", + "
      • \n", + " 2\n", + " \"forecast\"\n", "
      • \n", " \n", " \n", @@ -516,9 +557,9 @@ " \n", " \n", " \n", - "
      • \n", - " 1\n", - " 0.0\n", + "
      • \n", + " 3\n", + " \"atmospheric\"\n", "
      • \n", " \n", " \n", @@ -529,107 +570,202 @@ " \n", " \n", " \n", - "
      • \n", - " 2\n", - " 373185.0\n", + "
      • \n", + " 4\n", + " \"weather\"\n", "
      • \n", " \n", " \n", " \n", "
      \n", " \n", + "
    • \n", + " \n", + " \n", + " \n", + "
    • \n", + " providers[] 1 items\n", + " \n", + "
        \n", + " \n", + " \n", + " \n", + "
      • \n", + " 0\n", "
          \n", " \n", " \n", " \n", - "
        • \n", - " 3\n", - " 0.0\n", + "
        • \n", + " name\n", + " \"NOAA\"\n", "
        • \n", " \n", " \n", " \n", - "
        \n", + " \n", + "
      • \n", + " roles[] 1 items\n", " \n", "
          \n", " \n", " \n", " \n", - "
        • \n", - " 4\n", - " -100.01126757344893\n", + "
        • \n", + " 0\n", + " \"producer\"\n", "
        • \n", " \n", " \n", " \n", "
        \n", " \n", - "
          \n", + "
      • \n", + " \n", " \n", " \n", " \n", - "
      • \n", - " 5\n", - " 8286015.0\n", + "
      • \n", + " url\n", + " \"https://www.noaa.gov/\"\n", "
      • \n", " \n", " \n", " \n", "
      \n", + "
    • \n", + " \n", + " \n", + " \n", + "
    \n", " \n", " \n", + " \n", " \n", - " \n", + " \n", + "
    \n", + "
    " + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "
    \n", + "
    \n", + "
      \n", " \n", - "
    • \n", - " \n", - " proj:shape\n", - " [] 2 items\n", - " \n", - " \n", - "
        \n", - " \n", + " \n", + " \n", + "
      • \n", + " type\n", + " \"Feature\"\n", + "
      • \n", + " \n", + " \n", " \n", " \n", - "
      • \n", - " 0\n", - " 2667\n", + " \n", + "
      • \n", + " stac_version\n", + " \"1.0.0\"\n", "
      • \n", " \n", + " \n", " \n", - " \n", - "
      \n", - " \n", + " \n", + " \n", + "
    • \n", + " id\n", + " \"hrrr-conus-2024-05-10T12-FH0\"\n", + "
    • \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
    • \n", + " properties\n", "
        \n", " \n", " \n", " \n", - "
      • \n", - " 1\n", - " 2658\n", + "
      • \n", + " forecast:reference_time\n", + " \"2024-05-10T12:00:00\"\n", "
      • \n", " \n", " \n", " \n", - "
      \n", + " \n", + " \n", + "
    • \n", + " forecast:horizon\n", + " \"PT0H\"\n", + "
    • \n", + " \n", + " \n", " \n", - "
    • \n", + " \n", + " \n", + "
    • \n", + " noaa-hrrr:forecast_cycle_type\n", + " \"extended\"\n", + "
    • \n", + " \n", " \n", " \n", " \n", " \n", - "
    • \n", - " custom_attribute\n", - " \"foo\"\n", + "
    • \n", + " noaa-hrrr:region\n", + " \"conus\"\n", "
    • \n", " \n", " \n", " \n", " \n", " \n", - "
    • \n", - " datetime\n", - " \"2023-09-12T11:48:03.199913Z\"\n", + "
    • \n", + " datetime\n", + " \"2024-05-10T12:00:00Z\"\n", "
    • \n", " \n", " \n", @@ -642,50 +778,41 @@ " \n", " \n", "
    • \n", - " geometry\n", + " geometry\n", "
        \n", " \n", " \n", " \n", - "
      • \n", - " type\n", - " \"Polygon\"\n", + "
      • \n", + " type\n", + " \"Polygon\"\n", "
      • \n", " \n", " \n", " \n", " \n", "
      • \n", - " \n", - " coordinates\n", - " [] 1 items\n", - " \n", + " coordinates[] 1 items\n", " \n", "
          \n", " \n", " \n", "
        • \n", - " \n", - " 0\n", - " [] 5 items\n", - " \n", + " 0[] 5 items\n", " \n", "
            \n", " \n", " \n", "
          • \n", - " \n", - " 0\n", - " [] 2 items\n", - " \n", + " 0[] 2 items\n", " \n", "
              \n", " \n", " \n", " \n", - "
            • \n", - " 0\n", - " -52.916275\n", + "
            • \n", + " 0\n", + " -60.891244531606546\n", "
            • \n", " \n", " \n", @@ -696,9 +823,9 @@ " \n", " \n", " \n", - "
            • \n", - " 1\n", - " 72.229798\n", + "
            • \n", + " 1\n", + " 21.122192719272277\n", "
            • \n", " \n", " \n", @@ -714,18 +841,15 @@ " \n", " \n", "
            • \n", - " \n", - " 1\n", - " [] 2 items\n", - " \n", + " 1[] 2 items\n", " \n", "
                \n", " \n", " \n", " \n", - "
              • \n", - " 0\n", - " -52.301599\n", + "
              • \n", + " 0\n", + " -60.891244531606546\n", "
              • \n", " \n", " \n", @@ -736,9 +860,9 @@ " \n", " \n", " \n", - "
              • \n", - " 1\n", - " 74.613784\n", + "
              • \n", + " 1\n", + " 52.62870335266728\n", "
              • \n", " \n", " \n", @@ -754,18 +878,15 @@ " \n", " \n", "
              • \n", - " \n", - " 2\n", - " [] 2 items\n", - " \n", + " 2[] 2 items\n", " \n", "
                  \n", " \n", " \n", " \n", - "
                • \n", - " 0\n", - " -61.287624\n", + "
                • \n", + " 0\n", + " -134.12142793280145\n", "
                • \n", " \n", " \n", @@ -776,9 +897,9 @@ " \n", " \n", " \n", - "
                • \n", - " 1\n", - " 74.622043\n", + "
                • \n", + " 1\n", + " 52.62870335266728\n", "
                • \n", " \n", " \n", @@ -794,18 +915,15 @@ " \n", " \n", "
                • \n", - " \n", - " 3\n", - " [] 2 items\n", - " \n", + " 3[] 2 items\n", " \n", "
                    \n", " \n", " \n", " \n", - "
                  • \n", - " 0\n", - " -60.726346\n", + "
                  • \n", + " 0\n", + " -134.12142793280145\n", "
                  • \n", " \n", " \n", @@ -816,9 +934,9 @@ " \n", " \n", " \n", - "
                  • \n", - " 1\n", - " 72.236891\n", + "
                  • \n", + " 1\n", + " 21.122192719272277\n", "
                  • \n", " \n", " \n", @@ -834,18 +952,15 @@ " \n", " \n", "
                  • \n", - " \n", - " 4\n", - " [] 2 items\n", - " \n", + " 4[] 2 items\n", " \n", "
                      \n", " \n", " \n", " \n", - "
                    • \n", - " 0\n", - " -52.916275\n", + "
                    • \n", + " 0\n", + " -60.891244531606546\n", "
                    • \n", " \n", " \n", @@ -856,9 +971,9 @@ " \n", " \n", " \n", - "
                    • \n", - " 1\n", - " 72.229798\n", + "
                    • \n", + " 1\n", + " 21.122192719272277\n", "
                    • \n", " \n", " \n", @@ -885,51 +1000,48 @@ " \n", " \n", "
                    • \n", - " \n", - " links\n", - " [] 4 items\n", - " \n", + " links[] 4 items\n", " \n", "
                        \n", " \n", " \n", " \n", "
                      • \n", - " 0\n", + " 0\n", "
                          \n", " \n", " \n", " \n", - "
                        • \n", - " rel\n", - " \"root\"\n", + "
                        • \n", + " rel\n", + " \"root\"\n", "
                        • \n", " \n", " \n", " \n", " \n", " \n", - "
                        • \n", - " href\n", - " \"/Users/gadomski/Code/stactools-packages/template/examples/collection.json\"\n", + "
                        • \n", + " href\n", + " \"/opt/stactools-noaa-hrrr/examples/collection.json\"\n", "
                        • \n", " \n", " \n", " \n", " \n", " \n", - "
                        • \n", - " type\n", - " \"application/json\"\n", + "
                        • \n", + " type\n", + " \"application/json\"\n", "
                        • \n", " \n", " \n", " \n", " \n", " \n", - "
                        • \n", - " title\n", - " \"Example collection\"\n", + "
                        • \n", + " title\n", + " \"NOAA High Resolution Rapid Refresh (HRRR) collection\"\n", "
                        • \n", " \n", " \n", @@ -946,41 +1058,41 @@ " \n", " \n", "
                        • \n", - " 1\n", + " 1\n", "
                            \n", " \n", " \n", " \n", - "
                          • \n", - " rel\n", - " \"collection\"\n", + "
                          • \n", + " rel\n", + " \"collection\"\n", "
                          • \n", " \n", " \n", " \n", " \n", " \n", - "
                          • \n", - " href\n", - " \"../collection.json\"\n", + "
                          • \n", + " href\n", + " \"../collection.json\"\n", "
                          • \n", " \n", " \n", " \n", " \n", " \n", - "
                          • \n", - " type\n", - " \"application/json\"\n", + "
                          • \n", + " type\n", + " \"application/json\"\n", "
                          • \n", " \n", " \n", " \n", " \n", " \n", - "
                          • \n", - " title\n", - " \"Example collection\"\n", + "
                          • \n", + " title\n", + " \"NOAA High Resolution Rapid Refresh (HRRR) collection\"\n", "
                          • \n", " \n", " \n", @@ -997,32 +1109,32 @@ " \n", " \n", "
                          • \n", - " 2\n", + " 2\n", "
                              \n", " \n", " \n", " \n", - "
                            • \n", - " rel\n", - " \"self\"\n", + "
                            • \n", + " rel\n", + " \"self\"\n", "
                            • \n", " \n", " \n", " \n", " \n", " \n", - "
                            • \n", - " href\n", - " \"/Users/gadomski/Code/stactools-packages/template/examples/example-item/example-item.json\"\n", + "
                            • \n", + " href\n", + " \"/opt/stactools-noaa-hrrr/examples/hrrr-conus-2024-05-10T12-FH0/hrrr-conus-2024-05-10T12-FH0.json\"\n", "
                            • \n", " \n", " \n", " \n", " \n", " \n", - "
                            • \n", - " type\n", - " \"application/json\"\n", + "
                            • \n", + " type\n", + " \"application/json\"\n", "
                            • \n", " \n", " \n", @@ -1039,41 +1151,41 @@ " \n", " \n", "
                            • \n", - " 3\n", + " 3\n", "
                                \n", " \n", " \n", " \n", - "
                              • \n", - " rel\n", - " \"parent\"\n", + "
                              • \n", + " rel\n", + " \"parent\"\n", "
                              • \n", " \n", " \n", " \n", " \n", " \n", - "
                              • \n", - " href\n", - " \"/Users/gadomski/Code/stactools-packages/template/examples/collection.json\"\n", + "
                              • \n", + " href\n", + " \"/opt/stactools-noaa-hrrr/examples/collection.json\"\n", "
                              • \n", " \n", " \n", " \n", " \n", " \n", - "
                              • \n", - " type\n", - " \"application/json\"\n", + "
                              • \n", + " type\n", + " \"application/json\"\n", "
                              • \n", " \n", " \n", " \n", " \n", " \n", - "
                              • \n", - " title\n", - " \"Example collection\"\n", + "
                              • \n", + " title\n", + " \"NOAA High Resolution Rapid Refresh (HRRR) collection\"\n", "
                              • \n", " \n", " \n", @@ -1091,38 +1203,62 @@ " \n", " \n", "
                              • \n", - " assets\n", + " assets\n", "
                                  \n", " \n", " \n", " \n", "
                                • \n", - " data\n", + " prs\n", "
                                    \n", " \n", " \n", " \n", - "
                                  • \n", - " href\n", - " \"../../tests/data/asset.tif\"\n", + "
                                  • \n", + " href\n", + " \"https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfprsf00.grib2\"\n", + "
                                  • \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
                                  • \n", + " type\n", + " \"application/wmo-GRIB2\"\n", + "
                                  • \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
                                  • \n", + " title\n", + " \"3D Pressure Levels\"\n", + "
                                  • \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
                                  • \n", + " description\n", + " \"3D Pressure Level forecast data as a grib2 file. Subsets of the data can be loaded using the provided byte range.\"\n", "
                                  • \n", " \n", " \n", " \n", " \n", "
                                  • \n", - " \n", - " roles\n", - " [] 1 items\n", - " \n", + " roles[] 1 items\n", " \n", "
                                      \n", " \n", " \n", " \n", - "
                                    • \n", - " 0\n", - " \"data\"\n", + "
                                    • \n", + " 0\n", + " \"data\"\n", "
                                    • \n", " \n", " \n", @@ -1137,87 +1273,262 @@ " \n", " \n", " \n", - "
                                    \n", - "
                                  • \n", - " \n", - " \n", " \n", " \n", - "
                                  • \n", - " \n", - " bbox\n", - " [] 4 items\n", - " \n", - " \n", + "
                                  • \n", + " nat\n", "
                                      \n", " \n", " \n", " \n", - "
                                    • \n", - " 0\n", - " -61.287624\n", + "
                                    • \n", + " href\n", + " \"https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfnatf00.grib2\"\n", "
                                    • \n", " \n", " \n", " \n", - "
                                    \n", - " \n", - "
                                      \n", - " \n", " \n", " \n", - "
                                    • \n", - " 1\n", - " 72.229798\n", + "
                                    • \n", + " type\n", + " \"application/wmo-GRIB2\"\n", "
                                    • \n", " \n", " \n", " \n", - "
                                    \n", - " \n", - "
                                      \n", - " \n", " \n", " \n", - "
                                    • \n", - " 2\n", - " -52.301599\n", + "
                                    • \n", + " title\n", + " \"Native Levels\"\n", "
                                    • \n", " \n", " \n", " \n", - "
                                    \n", - " \n", - "
                                      \n", - " \n", " \n", " \n", - "
                                    • \n", - " 3\n", - " 74.622043\n", + "
                                    • \n", + " description\n", + " \"Native Level forecast data as a grib2 file. Subsets of the data can be loaded using the provided byte range.\"\n", "
                                    • \n", " \n", " \n", " \n", - "
                                    \n", + " \n", + "
                                  • \n", + " roles[] 1 items\n", + " \n", + "
                                      \n", + " \n", + " \n", + " \n", + "
                                    • \n", + " 0\n", + " \"data\"\n", + "
                                    • \n", + " \n", + " \n", + " \n", + "
                                    \n", + " \n", + "
                                  • \n", + " \n", + " \n", + "
                                  \n", + "
                                • \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
                                • \n", + " sfc\n", + "
                                    \n", + " \n", + " \n", + " \n", + "
                                  • \n", + " href\n", + " \"https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2\"\n", + "
                                  • \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
                                  • \n", + " type\n", + " \"application/wmo-GRIB2\"\n", + "
                                  • \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
                                  • \n", + " title\n", + " \"2D Surface Levels\"\n", + "
                                  • \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
                                  • \n", + " description\n", + " \"2D Surface Level forecast data as a grib2 file. Subsets of the data can be loaded using the provided byte range.\"\n", + "
                                  • \n", + " \n", + " \n", + " \n", + " \n", + "
                                  • \n", + " roles[] 1 items\n", + " \n", + "
                                      \n", + " \n", + " \n", + " \n", + "
                                    • \n", + " 0\n", + " \"data\"\n", + "
                                    • \n", + " \n", + " \n", + " \n", + "
                                    \n", + " \n", + "
                                  • \n", + " \n", + " \n", + "
                                  \n", + "
                                • \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
                                • \n", + " subh\n", + "
                                    \n", + " \n", + " \n", + " \n", + "
                                  • \n", + " href\n", + " \"https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsubhf00.grib2\"\n", + "
                                  • \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
                                  • \n", + " type\n", + " \"application/wmo-GRIB2\"\n", + "
                                  • \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
                                  • \n", + " title\n", + " \"2D Surface Levels - Sub Hourly\"\n", + "
                                  • \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
                                  • \n", + " description\n", + " \"2D Surface Level forecast data (sub-hourly, 15 minute intervals) as a grib2 file. Subsets of the data can be loaded using the provided byte range.\"\n", + "
                                  • \n", + " \n", + " \n", + " \n", + " \n", + "
                                  • \n", + " roles[] 1 items\n", + " \n", + "
                                      \n", + " \n", + " \n", + " \n", + "
                                    • \n", + " 0\n", + " \"data\"\n", + "
                                    • \n", + " \n", + " \n", + " \n", + "
                                    \n", " \n", "
                                  • \n", + " \n", + " \n", + "
                                  \n", + "
                                • \n", + " \n", + " \n", + " \n", + "
                                \n", + "
                              • \n", + " \n", " \n", " \n", " \n", "
                              • \n", - " \n", - " stac_extensions\n", - " [] 1 items\n", - " \n", + " bbox[] 4 items\n", + " \n", + "
                                  \n", + " \n", + " \n", + " \n", + "
                                • \n", + " 0\n", + " -134.12142793280145\n", + "
                                • \n", + " \n", + " \n", + " \n", + "
                                \n", " \n", "
                                  \n", " \n", " \n", " \n", - "
                                • \n", - " 0\n", - " \"https://stac-extensions.github.io/projection/v1.1.0/schema.json\"\n", + "
                                • \n", + " 1\n", + " 21.122192719272277\n", + "
                                • \n", + " \n", + " \n", + " \n", + "
                                \n", + " \n", + "
                                  \n", + " \n", + " \n", + " \n", + "
                                • \n", + " 2\n", + " -60.891244531606546\n", + "
                                • \n", + " \n", + " \n", + " \n", + "
                                \n", + " \n", + "
                                  \n", + " \n", + " \n", + " \n", + "
                                • \n", + " 3\n", + " 52.62870335266728\n", "
                                • \n", " \n", " \n", @@ -1228,10 +1539,17 @@ " \n", " \n", " \n", + "
                                • \n", + " stac_extensions[] 0 items\n", + " \n", + "
                                • \n", + " \n", + " \n", + " \n", " \n", - "
                                • \n", - " collection\n", - " \"example-collection\"\n", + "
                                • \n", + " collection\n", + " \"noaa-hrrr\"\n", "
                                • \n", " \n", " \n", @@ -1241,7 +1559,7 @@ "
    " ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1251,22 +1569,26 @@ "name": "stdout", "output_type": "stream", "text": [ - "This is the custom attribute: foo\n" + "This is the forecast cycle type: extended\n" ] } ], "source": [ + "from datetime import datetime, timedelta\n", "from pathlib import Path\n", "from pystac import Collection, Item\n", "from IPython.display import display\n", - "import stactools.ephemeral.stac\n", + "\n", + "import stactools.noaa_hrrr.stac\n", + "from stactools.noaa_hrrr.constants import Region, CloudProvider\n", + "\n", "\n", "root = Path().cwd().parent\n", "collection = Collection.from_file(str(root / \"examples\" / \"collection.json\"))\n", "display(collection)\n", "item = next(collection.get_items())\n", "display(item)\n", - "print(f\"This is the custom attribute: {item.properties['custom_attribute']}\")" + "print(f\"This is the forecast cycle type: {item.properties['noaa-hrrr:forecast_cycle_type']}\")" ] }, { @@ -1275,14 +1597,16 @@ "source": [ "## Creating items and collections\n", "\n", - "Items can be created from an href to a single tif file.\n", + "Items can be created from a `reference_datetime`, `forecast_hour`, `region`, and `cloud_provider`.\n", "Items are added to collections using the standard pystac interface." ] }, { "cell_type": "code", "execution_count": 5, - "metadata": {}, + "metadata": { + "scrolled": true + }, "outputs": [ { "data": { @@ -1292,16 +1616,32 @@ "\n", "
    \n", "
    \n", @@ -1309,78 +1649,84 @@ " \n", " \n", " \n", - "
  • \n", - " type\n", - " \"Collection\"\n", + "
  • \n", + " type\n", + " \"Collection\"\n", "
  • \n", " \n", " \n", " \n", " \n", " \n", - "
  • \n", - " id\n", - " \"example-collection\"\n", + "
  • \n", + " id\n", + " \"noaa-hrrr\"\n", "
  • \n", " \n", " \n", " \n", " \n", " \n", - "
  • \n", - " stac_version\n", - " \"1.0.0\"\n", + "
  • \n", + " stac_version\n", + " \"1.0.0\"\n", "
  • \n", " \n", " \n", " \n", " \n", " \n", - "
  • \n", - " description\n", - " \"An example collection\"\n", + "
  • \n", + " description\n", + " \"The NOAA HRRR is a real-time 3km resolution, hourly updated, cloud-resolving, convection-allowing atmospheric model, initialized by 3km grids with 3km radar assimilation. Radar data is assimilated in the HRRR every 15 min over a 1-hour period adding further detail to that provided by the hourly data assimilation from the 13km radar-enhanced Rapid Refresh (RAP) system.\"\n", "
  • \n", " \n", " \n", " \n", " \n", "
  • \n", - " \n", - " links\n", - " [] 1 items\n", - " \n", + " links[] 3 items\n", " \n", "
      \n", " \n", " \n", " \n", "
    • \n", - " 0\n", + " 0\n", "
        \n", " \n", " \n", " \n", - "
      • \n", - " rel\n", - " \"item\"\n", + "
      • \n", + " rel\n", + " \"license\"\n", + "
      • \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
      • \n", + " href\n", + " \"https://creativecommons.org/licenses/by/4.0/\"\n", "
      • \n", " \n", " \n", " \n", " \n", " \n", - "
      • \n", - " href\n", - " None\n", + "
      • \n", + " type\n", + " \"text/html\"\n", "
      • \n", " \n", " \n", " \n", " \n", " \n", - "
      • \n", - " type\n", - " \"application/json\"\n", + "
      • \n", + " title\n", + " \"CC-BY-4.0 license\"\n", "
      • \n", " \n", " \n", @@ -1392,23 +1738,107 @@ " \n", "
      \n", " \n", - "
    • \n", + "
        \n", + " \n", + " \n", " \n", + "
      • \n", + " 1\n", + "
          \n", + " \n", " \n", " \n", - " \n", - "
        • \n", - " custom_attribute\n", - " \"foo\"\n", + "
        • \n", + " rel\n", + " \"documentation\"\n", + "
        • \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
        • \n", + " href\n", + " \"https://rapidrefresh.noaa.gov/hrrr/\"\n", + "
        • \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
        • \n", + " type\n", + " \"text/html\"\n", + "
        • \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
        • \n", + " title\n", + " \"NOAA HRRR documentation\"\n", "
        • \n", " \n", + " \n", + " \n", + "
        \n", + "
      • \n", + " \n", + " \n", + " \n", + "
      \n", + " \n", + "
        \n", + " \n", + " \n", + " \n", + "
      • \n", + " 2\n", + "
          \n", + " \n", + " \n", + " \n", + "
        • \n", + " rel\n", + " \"item\"\n", + "
        • \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
        • \n", + " href\n", + " None\n", + "
        • \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
        • \n", + " type\n", + " \"application/json\"\n", + "
        • \n", + " \n", + " \n", + " \n", + "
        \n", + "
      • \n", + " \n", + " \n", + " \n", + "
      \n", + " \n", + "
  • \n", " \n", " \n", " \n", " \n", - "
  • \n", - " title\n", - " \"Example collection\"\n", + "
  • \n", + " title\n", + " \"NOAA High Resolution Rapid Refresh (HRRR) collection\"\n", "
  • \n", " \n", " \n", @@ -1416,38 +1846,32 @@ " \n", " \n", "
  • \n", - " extent\n", + " extent\n", "
      \n", " \n", " \n", " \n", "
    • \n", - " spatial\n", + " spatial\n", "
        \n", " \n", " \n", "
      • \n", - " \n", - " bbox\n", - " [] 1 items\n", - " \n", + " bbox[] 2 items\n", " \n", "
          \n", " \n", " \n", "
        • \n", - " \n", - " 0\n", - " [] 4 items\n", - " \n", + " 0[] 4 items\n", " \n", "
            \n", " \n", " \n", " \n", - "
          • \n", - " 0\n", - " -180.0\n", + "
          • \n", + " 0\n", + " -134.12142793280145\n", "
          • \n", " \n", " \n", @@ -1458,9 +1882,9 @@ " \n", " \n", " \n", - "
          • \n", - " 1\n", - " 90.0\n", + "
          • \n", + " 1\n", + " 21.122192719272277\n", "
          • \n", " \n", " \n", @@ -1471,9 +1895,9 @@ " \n", " \n", " \n", - "
          • \n", - " 2\n", - " 180.0\n", + "
          • \n", + " 2\n", + " -60.891244531606546\n", "
          • \n", " \n", " \n", @@ -1484,9 +1908,72 @@ " \n", " \n", " \n", - "
          • \n", - " 3\n", - " -90.0\n", + "
          • \n", + " 3\n", + " 52.62870335266728\n", + "
          • \n", + " \n", + " \n", + " \n", + "
          \n", + " \n", + "
        • \n", + " \n", + " \n", + "
        \n", + " \n", + "
          \n", + " \n", + " \n", + "
        • \n", + " 1[] 4 items\n", + " \n", + "
            \n", + " \n", + " \n", + " \n", + "
          • \n", + " 0\n", + " -174.8849\n", + "
          • \n", + " \n", + " \n", + " \n", + "
          \n", + " \n", + "
            \n", + " \n", + " \n", + " \n", + "
          • \n", + " 1\n", + " 41.596\n", + "
          • \n", + " \n", + " \n", + " \n", + "
          \n", + " \n", + "
            \n", + " \n", + " \n", + " \n", + "
          • \n", + " 2\n", + " -115.6988\n", + "
          • \n", + " \n", + " \n", + " \n", + "
          \n", + " \n", + "
            \n", + " \n", + " \n", + " \n", + "
          • \n", + " 3\n", + " 76.3464\n", "
          • \n", " \n", " \n", @@ -1509,32 +1996,26 @@ " \n", " \n", "
          • \n", - " temporal\n", + " temporal\n", "
              \n", " \n", " \n", "
            • \n", - " \n", - " interval\n", - " [] 1 items\n", - " \n", + " interval[] 1 items\n", " \n", "
                \n", " \n", " \n", "
              • \n", - " \n", - " 0\n", - " [] 2 items\n", - " \n", + " 0[] 2 items\n", " \n", "
                  \n", " \n", " \n", " \n", - "
                • \n", - " 0\n", - " \"2023-09-18T21:35:51.039996Z\"\n", + "
                • \n", + " 0\n", + " \"2021-03-21T00:00:00Z\"\n", "
                • \n", " \n", " \n", @@ -1545,9 +2026,9 @@ " \n", " \n", " \n", - "
                • \n", - " 1\n", - " None\n", + "
                • \n", + " 1\n", + " None\n", "
                • \n", " \n", " \n", @@ -1574,11 +2055,143 @@ " \n", " \n", " \n", - "
                • \n", - " license\n", - " \"proprietary\"\n", + "
                • \n", + " license\n", + " \"CC-BY-4.0\"\n", + "
                • \n", + " \n", + " \n", + " \n", + " \n", + "
                • \n", + " keywords[] 5 items\n", + " \n", + "
                    \n", + " \n", + " \n", + " \n", + "
                  • \n", + " 0\n", + " \"NOAA\"\n", + "
                  • \n", + " \n", + " \n", + " \n", + "
                  \n", + " \n", + "
                    \n", + " \n", + " \n", + " \n", + "
                  • \n", + " 1\n", + " \"HRRR\"\n", + "
                  • \n", + " \n", + " \n", + " \n", + "
                  \n", + " \n", + "
                    \n", + " \n", + " \n", + " \n", + "
                  • \n", + " 2\n", + " \"forecast\"\n", + "
                  • \n", + " \n", + " \n", + " \n", + "
                  \n", + " \n", + "
                    \n", + " \n", + " \n", + " \n", + "
                  • \n", + " 3\n", + " \"atmospheric\"\n", "
                  • \n", " \n", + " \n", + " \n", + "
                  \n", + " \n", + "
                    \n", + " \n", + " \n", + " \n", + "
                  • \n", + " 4\n", + " \"weather\"\n", + "
                  • \n", + " \n", + " \n", + " \n", + "
                  \n", + " \n", + "
                • \n", + " \n", + " \n", + " \n", + "
                • \n", + " providers[] 1 items\n", + " \n", + "
                    \n", + " \n", + " \n", + " \n", + "
                  • \n", + " 0\n", + "
                      \n", + " \n", + " \n", + " \n", + "
                    • \n", + " name\n", + " \"NOAA\"\n", + "
                    • \n", + " \n", + " \n", + " \n", + " \n", + "
                    • \n", + " roles[] 1 items\n", + " \n", + "
                        \n", + " \n", + " \n", + " \n", + "
                      • \n", + " 0\n", + " \"producer\"\n", + "
                      • \n", + " \n", + " \n", + " \n", + "
                      \n", + " \n", + "
                    • \n", + " \n", + " \n", + " \n", + " \n", + "
                    • \n", + " url\n", + " \"https://www.noaa.gov/\"\n", + "
                    • \n", + " \n", + " \n", + " \n", + "
                    \n", + "
                  • \n", + " \n", + " \n", + " \n", + "
                  \n", + " \n", + "
                • \n", " \n", " \n", "
                \n", @@ -1586,7 +2199,7 @@ "
  • " ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1594,9 +2207,13 @@ } ], "source": [ - "tif_path = root / \"tests\" / \"data\" / \"asset.tif\"\n", - "item = stactools.ephemeral.stac.create_item(str(tif_path))\n", - "collection = stactools.ephemeral.stac.create_collection()\n", + "item = stactools.noaa_hrrr.stac.create_item(\n", + " reference_datetime=datetime.now() - timedelta(days=7),\n", + " forecast_hour=6,\n", + " region=Region.conus,\n", + " cloud_provider=CloudProvider.azure,\n", + ")\n", + "collection = stactools.noaa_hrrr.stac.create_collection(cloud_provider=CloudProvider.azure)\n", "collection.add_item(item)\n", "display(collection)" ] @@ -1604,7 +2221,7 @@ ], "metadata": { "kernelspec": { - "display_name": ".venv", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -1618,10 +2235,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.2" - }, - "orig_nbformat": 4 + "version": "3.12.2" + } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/environment.yml b/environment.yml index 1d92b42..70374bc 100644 --- a/environment.yml +++ b/environment.yml @@ -1,9 +1,13 @@ -name: stactools-ephemeral +name: stactools-noaa-hrrr channels: - conda-forge - defaults dependencies: + - conda-forge::sqlite + - conda-forge::curl - conda-forge::gdal>=3.3 - conda-forge::geos>=3.3 + - conda-forge::jupyter + - conda-forge::nb_conda_kernels - conda-forge::rasterio>=1.3 - conda-forge::libstdcxx-ng # gdal dependency. Make sure it's from the same channel as gdal diff --git a/examples/collection.json b/examples/collection.json index d6db4b8..36ce5dd 100644 --- a/examples/collection.json +++ b/examples/collection.json @@ -1,42 +1,69 @@ { "type": "Collection", - "id": "example-collection", + "id": "noaa-hrrr-sfc", "stac_version": "1.0.0", - "description": "An example collection", + "description": "The NOAA HRRR is a real-time 3km resolution, hourly updated, cloud-resolving, convection-allowing atmospheric model, initialized by 3km grids with 3km radar assimilation. Radar data is assimilated in the HRRR every 15 min over a 1-hour period adding further detail to that provided by the hourly data assimilation from the 13km radar-enhanced Rapid Refresh (RAP) system. This specific collection represents 2D surface levels.", "links": [ { "rel": "root", "href": "./collection.json", "type": "application/json", - "title": "Example collection" + "title": "NOAA High Resolution Rapid Refresh (HRRR) - 2D surface levels" + }, + { + "rel": "license", + "href": "https://creativecommons.org/licenses/by/4.0/", + "type": "text/html", + "title": "CC-BY-4.0 license" + }, + { + "rel": "documentation", + "href": "https://rapidrefresh.noaa.gov/hrrr/", + "type": "text/html", + "title": "NOAA HRRR documentation" }, { "rel": "item", - "href": "./example-item/example-item.json", + "href": "./hrrr-conus-sfc-2024-05-10T12-FH0/hrrr-conus-sfc-2024-05-10T12-FH0.json", "type": "application/json" } ], - "custom_attribute": "foo", - "title": "Example collection", + "title": "NOAA High Resolution Rapid Refresh (HRRR) - 2D surface levels", "extent": { "spatial": { "bbox": [ [ - -61.287624, - 72.229798, - -52.301599, - 74.622043 + -134.12142793280145, + 21.122192719272277, + -60.891244531606546, + 52.62870335266728 ] ] }, "temporal": { "interval": [ [ - "2023-09-12T11:48:03.199913Z", - "2023-09-12T11:48:03.199913Z" + "2024-05-10T12:00:00Z", + "2024-05-10T12:00:00Z" ] ] } }, - "license": "proprietary" + "license": "CC-BY-4.0", + "keywords": [ + "NOAA", + "HRRR", + "forecast", + "atmospheric", + "weather" + ], + "providers": [ + { + "name": "NOAA", + "roles": [ + "producer" + ], + "url": "https://www.noaa.gov/" + } + ] } \ No newline at end of file diff --git a/examples/example-item/example-item.json b/examples/example-item/example-item.json deleted file mode 100644 index 17cf5cf..0000000 --- a/examples/example-item/example-item.json +++ /dev/null @@ -1,87 +0,0 @@ -{ - "type": "Feature", - "stac_version": "1.0.0", - "id": "example-item", - "properties": { - "proj:epsg": 32621, - "proj:transform": [ - 100.01126757344893, - 0.0, - 373185.0, - 0.0, - -100.01126757344893, - 8286015.0 - ], - "proj:shape": [ - 2667, - 2658 - ], - "custom_attribute": "foo", - "datetime": "2023-09-12T11:48:03.199913Z" - }, - "geometry": { - "type": "Polygon", - "coordinates": [ - [ - [ - -52.916275, - 72.229798 - ], - [ - -52.301599, - 74.613784 - ], - [ - -61.287624, - 74.622043 - ], - [ - -60.726346, - 72.236891 - ], - [ - -52.916275, - 72.229798 - ] - ] - ] - }, - "links": [ - { - "rel": "root", - "href": "../collection.json", - "type": "application/json", - "title": "Example collection" - }, - { - "rel": "collection", - "href": "../collection.json", - "type": "application/json", - "title": "Example collection" - }, - { - "rel": "parent", - "href": "../collection.json", - "type": "application/json", - "title": "Example collection" - } - ], - "assets": { - "data": { - "href": "../../tests/data/asset.tif", - "roles": [ - "data" - ] - } - }, - "bbox": [ - -61.287624, - 72.229798, - -52.301599, - 74.622043 - ], - "stac_extensions": [ - "https://stac-extensions.github.io/projection/v1.1.0/schema.json" - ], - "collection": "example-collection" -} \ No newline at end of file diff --git a/examples/hrrr-conus-sfc-2024-05-10T12-FH0/hrrr-conus-sfc-2024-05-10T12-FH0.json b/examples/hrrr-conus-sfc-2024-05-10T12-FH0/hrrr-conus-sfc-2024-05-10T12-FH0.json new file mode 100644 index 0000000..7814412 --- /dev/null +++ b/examples/hrrr-conus-sfc-2024-05-10T12-FH0/hrrr-conus-sfc-2024-05-10T12-FH0.json @@ -0,0 +1,1698 @@ +{ + "type": "Feature", + "stac_version": "1.0.0", + "id": "hrrr-conus-sfc-2024-05-10T12-FH0", + "properties": { + "forecast:reference_time": "2024-05-10T12:00:00", + "forecast:horizon": "PT0H", + "noaa-hrrr:forecast_cycle_type": "extended", + "noaa-hrrr:region": "conus", + "datetime": "2024-05-10T12:00:00Z" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -60.891244531606546, + 21.122192719272277 + ], + [ + -60.891244531606546, + 52.62870335266728 + ], + [ + -134.12142793280145, + 52.62870335266728 + ], + [ + -134.12142793280145, + 21.122192719272277 + ], + [ + -60.891244531606546, + 21.122192719272277 + ] + ] + ] + }, + "links": [ + { + "rel": "root", + "href": "../collection.json", + "type": "application/json", + "title": "NOAA High Resolution Rapid Refresh (HRRR) - 2D surface levels" + }, + { + "rel": "collection", + "href": "../collection.json", + "type": "application/json", + "title": "NOAA High Resolution Rapid Refresh (HRRR) - 2D surface levels" + }, + { + "rel": "parent", + "href": "../collection.json", + "type": "application/json", + "title": "NOAA High Resolution Rapid Refresh (HRRR) - 2D surface levels" + } + ], + "assets": { + "grib": { + "href": "https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "2D Surface Levels", + "description": "2D Surface Level forecast data as a grib2 file. Subsets of the data can be loaded using the provided byte range.", + "roles": [ + "data" + ] + }, + "index": { + "href": "https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2.idx", + "type": "application/x-ndjson", + "title": "Index file", + "description": "The index file contains information on each message within the GRIB2 file.", + "roles": [ + "index" + ] + }, + "REFC__entire_atmosphere__analysis": { + "href": "/vsisubfile/0_238534,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "REFC - entire atmosphere - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "RETOP__cloud_top__analysis": { + "href": "/vsisubfile/238534_138642,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "RETOP - cloud top - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "var_discipline=0_center=7_local_table=1_parmcat=16_parm=201__entire_atmosphere__analysis": { + "href": "/vsisubfile/377176_268771,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "var discipline=0 center=7 local table=1 parmcat=16 parm=201 - entire atmosphere - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "VIL__entire_atmosphere__analysis": { + "href": "/vsisubfile/645947_169134,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "VIL - entire atmosphere - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "VIS__surface__analysis": { + "href": "/vsisubfile/815081_1369441,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "VIS - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "REFD__1000_m_above_ground__analysis": { + "href": "/vsisubfile/2184522_118078,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "REFD - 1000 m above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "REFD__4000_m_above_ground__analysis": { + "href": "/vsisubfile/2302600_109117,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "REFD - 4000 m above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "REFD__263_K_level__analysis": { + "href": "/vsisubfile/2411717_124632,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "REFD - 263 K level - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "GUST__surface__analysis": { + "href": "/vsisubfile/2536349_1240744,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "GUST - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "UGRD__250_mb__analysis": { + "href": "/vsisubfile/3777093_732558,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "UGRD - 250 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "VGRD__250_mb__analysis": { + "href": "/vsisubfile/4509651_720863,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "VGRD - 250 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "UGRD__300_mb__analysis": { + "href": "/vsisubfile/5230514_729032,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "UGRD - 300 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "VGRD__300_mb__analysis": { + "href": "/vsisubfile/5959546_714891,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "VGRD - 300 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "HGT__500_mb__analysis": { + "href": "/vsisubfile/6674437_731818,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "HGT - 500 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "TMP__500_mb__analysis": { + "href": "/vsisubfile/7406255_542238,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "TMP - 500 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "DPT__500_mb__analysis": { + "href": "/vsisubfile/7948493_917161,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "DPT - 500 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "UGRD__500_mb__analysis": { + "href": "/vsisubfile/8865654_586092,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "UGRD - 500 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "VGRD__500_mb__analysis": { + "href": "/vsisubfile/9451746_586054,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "VGRD - 500 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "HGT__700_mb__analysis": { + "href": "/vsisubfile/10037800_727565,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "HGT - 700 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "TMP__700_mb__analysis": { + "href": "/vsisubfile/10765365_551835,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "TMP - 700 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "DPT__700_mb__analysis": { + "href": "/vsisubfile/11317200_1044186,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "DPT - 700 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "DZDT__700_mb__analysis": { + "href": "/vsisubfile/12361386_311459,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "DZDT - 700 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "UGRD__700_mb__analysis": { + "href": "/vsisubfile/12672845_596025,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "UGRD - 700 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "VGRD__700_mb__analysis": { + "href": "/vsisubfile/13268870_585229,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "VGRD - 700 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "HGT__850_mb__analysis": { + "href": "/vsisubfile/13854099_744890,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "HGT - 850 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "TMP__850_mb__analysis": { + "href": "/vsisubfile/14598989_588228,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "TMP - 850 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "DPT__850_mb__analysis": { + "href": "/vsisubfile/15187217_1131448,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "DPT - 850 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "UGRD__850_mb__analysis": { + "href": "/vsisubfile/16318665_622425,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "UGRD - 850 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "VGRD__850_mb__analysis": { + "href": "/vsisubfile/16941090_612216,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "VGRD - 850 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "TMP__925_mb__analysis": { + "href": "/vsisubfile/17553306_613266,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "TMP - 925 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "DPT__925_mb__analysis": { + "href": "/vsisubfile/18166572_1165161,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "DPT - 925 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "UGRD__925_mb__analysis": { + "href": "/vsisubfile/19331733_635702,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "UGRD - 925 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "VGRD__925_mb__analysis": { + "href": "/vsisubfile/19967435_632673,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "VGRD - 925 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "TMP__1000_mb__analysis": { + "href": "/vsisubfile/20600108_634362,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "TMP - 1000 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "DPT__1000_mb__analysis": { + "href": "/vsisubfile/21234470_1127119,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "DPT - 1000 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "UGRD__1000_mb__analysis": { + "href": "/vsisubfile/22361589_631178,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "UGRD - 1000 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "VGRD__1000_mb__analysis": { + "href": "/vsisubfile/22992767_624376,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "VGRD - 1000 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "MAXUVV__100-1000_mb_above_ground__instantaneous__max": { + "href": "/vsisubfile/23617143_63006,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "MAXUVV - 100-1000 mb above ground - instantaneous - max", + "hrrr:forecast_layer_type": "instantaneous", + "hrrr:start_timedelta": "2024-05-10T12:00:00", + "hrrr:end_timedelta": "2024-05-10T12:00:00", + "hrrr:statistic_type": "max", + "roles": [ + "data" + ] + }, + "MAXDVV__100-1000_mb_above_ground__instantaneous__max": { + "href": "/vsisubfile/23680149_336900,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "MAXDVV - 100-1000 mb above ground - instantaneous - max", + "hrrr:forecast_layer_type": "instantaneous", + "hrrr:start_timedelta": "2024-05-10T12:00:00", + "hrrr:end_timedelta": "2024-05-10T12:00:00", + "hrrr:statistic_type": "max", + "roles": [ + "data" + ] + }, + "DZDT__0.5-0.8_sigma_layer__instantaneous__ave": { + "href": "/vsisubfile/24017049_172939,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "DZDT - 0.5-0.8 sigma layer - instantaneous - ave", + "hrrr:forecast_layer_type": "instantaneous", + "hrrr:start_timedelta": "2024-05-10T12:00:00", + "hrrr:end_timedelta": "2024-05-10T12:00:00", + "hrrr:statistic_type": "ave", + "roles": [ + "data" + ] + }, + "MSLMA__mean_sea_level__analysis": { + "href": "/vsisubfile/24189988_623746,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "MSLMA - mean sea level - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "HGT__1000_mb__analysis": { + "href": "/vsisubfile/24813734_712569,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "HGT - 1000 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "MAXREF__1000_m_above_ground__instantaneous__max": { + "href": "/vsisubfile/25526303_109966,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "MAXREF - 1000 m above ground - instantaneous - max", + "hrrr:forecast_layer_type": "instantaneous", + "hrrr:start_timedelta": "2024-05-10T12:00:00", + "hrrr:end_timedelta": "2024-05-10T12:00:00", + "hrrr:statistic_type": "max", + "roles": [ + "data" + ] + }, + "REFD__263_K_level__instantaneous__max": { + "href": "/vsisubfile/25636269_124687,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "REFD - 263 K level - instantaneous - max", + "hrrr:forecast_layer_type": "instantaneous", + "hrrr:start_timedelta": "2024-05-10T12:00:00", + "hrrr:end_timedelta": "2024-05-10T12:00:00", + "hrrr:statistic_type": "max", + "roles": [ + "data" + ] + }, + "MXUPHL__5000-2000_m_above_ground__instantaneous__max": { + "href": "/vsisubfile/25760956_212,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "MXUPHL - 5000-2000 m above ground - instantaneous - max", + "hrrr:forecast_layer_type": "instantaneous", + "hrrr:start_timedelta": "2024-05-10T12:00:00", + "hrrr:end_timedelta": "2024-05-10T12:00:00", + "hrrr:statistic_type": "max", + "roles": [ + "data" + ] + }, + "MNUPHL__5000-2000_m_above_ground__instantaneous__min": { + "href": "/vsisubfile/25761168_212,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "MNUPHL - 5000-2000 m above ground - instantaneous - min", + "hrrr:forecast_layer_type": "instantaneous", + "hrrr:start_timedelta": "2024-05-10T12:00:00", + "hrrr:end_timedelta": "2024-05-10T12:00:00", + "hrrr:statistic_type": "min", + "roles": [ + "data" + ] + }, + "MXUPHL__2000-0_m_above_ground__instantaneous__max": { + "href": "/vsisubfile/25761380_212,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "MXUPHL - 2000-0 m above ground - instantaneous - max", + "hrrr:forecast_layer_type": "instantaneous", + "hrrr:start_timedelta": "2024-05-10T12:00:00", + "hrrr:end_timedelta": "2024-05-10T12:00:00", + "hrrr:statistic_type": "max", + "roles": [ + "data" + ] + }, + "MNUPHL__2000-0_m_above_ground__instantaneous__min": { + "href": "/vsisubfile/25761592_212,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "MNUPHL - 2000-0 m above ground - instantaneous - min", + "hrrr:forecast_layer_type": "instantaneous", + "hrrr:start_timedelta": "2024-05-10T12:00:00", + "hrrr:end_timedelta": "2024-05-10T12:00:00", + "hrrr:statistic_type": "min", + "roles": [ + "data" + ] + }, + "MXUPHL__3000-0_m_above_ground__instantaneous__max": { + "href": "/vsisubfile/25761804_212,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "MXUPHL - 3000-0 m above ground - instantaneous - max", + "hrrr:forecast_layer_type": "instantaneous", + "hrrr:start_timedelta": "2024-05-10T12:00:00", + "hrrr:end_timedelta": "2024-05-10T12:00:00", + "hrrr:statistic_type": "max", + "roles": [ + "data" + ] + }, + "MNUPHL__3000-0_m_above_ground__instantaneous__min": { + "href": "/vsisubfile/25762016_212,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "MNUPHL - 3000-0 m above ground - instantaneous - min", + "hrrr:forecast_layer_type": "instantaneous", + "hrrr:start_timedelta": "2024-05-10T12:00:00", + "hrrr:end_timedelta": "2024-05-10T12:00:00", + "hrrr:statistic_type": "min", + "roles": [ + "data" + ] + }, + "RELV__2000-0_m_above_ground__instantaneous__max": { + "href": "/vsisubfile/25762228_212,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "RELV - 2000-0 m above ground - instantaneous - max", + "hrrr:forecast_layer_type": "instantaneous", + "hrrr:start_timedelta": "2024-05-10T12:00:00", + "hrrr:end_timedelta": "2024-05-10T12:00:00", + "hrrr:statistic_type": "max", + "roles": [ + "data" + ] + }, + "RELV__1000-0_m_above_ground__instantaneous__max": { + "href": "/vsisubfile/25762440_212,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "RELV - 1000-0 m above ground - instantaneous - max", + "hrrr:forecast_layer_type": "instantaneous", + "hrrr:start_timedelta": "2024-05-10T12:00:00", + "hrrr:end_timedelta": "2024-05-10T12:00:00", + "hrrr:statistic_type": "max", + "roles": [ + "data" + ] + }, + "HAIL__entire_atmosphere__instantaneous__max": { + "href": "/vsisubfile/25762652_96888,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "HAIL - entire atmosphere - instantaneous - max", + "hrrr:forecast_layer_type": "instantaneous", + "hrrr:start_timedelta": "2024-05-10T12:00:00", + "hrrr:end_timedelta": "2024-05-10T12:00:00", + "hrrr:statistic_type": "max", + "roles": [ + "data" + ] + }, + "HAIL__0.1_sigma_level__instantaneous__max": { + "href": "/vsisubfile/25859540_6809,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "HAIL - 0.1 sigma level - instantaneous - max", + "hrrr:forecast_layer_type": "instantaneous", + "hrrr:start_timedelta": "2024-05-10T12:00:00", + "hrrr:end_timedelta": "2024-05-10T12:00:00", + "hrrr:statistic_type": "max", + "roles": [ + "data" + ] + }, + "HAIL__surface__instantaneous__max": { + "href": "/vsisubfile/25866349_212,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "HAIL - surface - instantaneous - max", + "hrrr:forecast_layer_type": "instantaneous", + "hrrr:start_timedelta": "2024-05-10T12:00:00", + "hrrr:end_timedelta": "2024-05-10T12:00:00", + "hrrr:statistic_type": "max", + "roles": [ + "data" + ] + }, + "TCOLG__entire_atmosphere_(considered_as_a_single_layer)__instantaneous__max": { + "href": "/vsisubfile/25866561_11898,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "TCOLG - entire atmosphere (considered as a single layer) - instantaneous - max", + "hrrr:forecast_layer_type": "instantaneous", + "hrrr:start_timedelta": "2024-05-10T12:00:00", + "hrrr:end_timedelta": "2024-05-10T12:00:00", + "hrrr:statistic_type": "max", + "roles": [ + "data" + ] + }, + "LTNGSD__1_m_above_ground__analysis": { + "href": "/vsisubfile/25878459_188,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "LTNGSD - 1 m above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "LTNGSD__2_m_above_ground__analysis": { + "href": "/vsisubfile/25878647_53022,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "LTNGSD - 2 m above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "LTNG__entire_atmosphere__analysis": { + "href": "/vsisubfile/25931669_188,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "LTNG - entire atmosphere - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "UGRD__80_m_above_ground__analysis": { + "href": "/vsisubfile/25931857_1129423,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "UGRD - 80 m above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "VGRD__80_m_above_ground__analysis": { + "href": "/vsisubfile/27061280_1147648,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "VGRD - 80 m above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "PRES__surface__analysis": { + "href": "/vsisubfile/28208928_1509864,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "PRES - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "HGT__surface__analysis": { + "href": "/vsisubfile/29718792_2153695,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "HGT - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "TMP__surface__analysis": { + "href": "/vsisubfile/31872487_1263576,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "TMP - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "ASNOW__surface__instantaneous__acc": { + "href": "/vsisubfile/33136063_323,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "ASNOW - surface - instantaneous - acc", + "hrrr:forecast_layer_type": "instantaneous", + "hrrr:start_timedelta": "2024-05-10T12:00:00", + "hrrr:end_timedelta": "2024-05-10T12:00:00", + "hrrr:statistic_type": "acc", + "roles": [ + "data" + ] + }, + "MSTAV__0_m_underground__analysis": { + "href": "/vsisubfile/33136386_1482639,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "MSTAV - 0 m underground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "CNWAT__surface__analysis": { + "href": "/vsisubfile/34619025_87150,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "CNWAT - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "WEASD__surface__analysis": { + "href": "/vsisubfile/34706175_79078,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "WEASD - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "SNOWC__surface__analysis": { + "href": "/vsisubfile/34785253_51912,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "SNOWC - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "SNOD__surface__analysis": { + "href": "/vsisubfile/34837165_64489,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "SNOD - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "TMP__2_m_above_ground__analysis": { + "href": "/vsisubfile/34901654_1166814,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "TMP - 2 m above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "POT__2_m_above_ground__analysis": { + "href": "/vsisubfile/36068468_1159402,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "POT - 2 m above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "SPFH__2_m_above_ground__analysis": { + "href": "/vsisubfile/37227870_1468901,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "SPFH - 2 m above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "DPT__2_m_above_ground__analysis": { + "href": "/vsisubfile/38696771_1169022,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "DPT - 2 m above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "RH__2_m_above_ground__analysis": { + "href": "/vsisubfile/39865793_1578809,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "RH - 2 m above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "MASSDEN__8_m_above_ground__analysis": { + "href": "/vsisubfile/41444602_761369,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "MASSDEN - 8 m above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "UGRD__10_m_above_ground__analysis": { + "href": "/vsisubfile/42205971_2381615,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "UGRD - 10 m above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "VGRD__10_m_above_ground__analysis": { + "href": "/vsisubfile/44587586_2143472,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "VGRD - 10 m above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "WIND__10_m_above_ground__instantaneous__max": { + "href": "/vsisubfile/46731058_1217507,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "WIND - 10 m above ground - instantaneous - max", + "hrrr:forecast_layer_type": "instantaneous", + "hrrr:start_timedelta": "2024-05-10T12:00:00", + "hrrr:end_timedelta": "2024-05-10T12:00:00", + "hrrr:statistic_type": "max", + "roles": [ + "data" + ] + }, + "MAXUW__10_m_above_ground__instantaneous__max": { + "href": "/vsisubfile/47948565_1195150,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "MAXUW - 10 m above ground - instantaneous - max", + "hrrr:forecast_layer_type": "instantaneous", + "hrrr:start_timedelta": "2024-05-10T12:00:00", + "hrrr:end_timedelta": "2024-05-10T12:00:00", + "hrrr:statistic_type": "max", + "roles": [ + "data" + ] + }, + "MAXVW__10_m_above_ground__instantaneous__max": { + "href": "/vsisubfile/49143715_1185667,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "MAXVW - 10 m above ground - instantaneous - max", + "hrrr:forecast_layer_type": "instantaneous", + "hrrr:start_timedelta": "2024-05-10T12:00:00", + "hrrr:end_timedelta": "2024-05-10T12:00:00", + "hrrr:statistic_type": "max", + "roles": [ + "data" + ] + }, + "CPOFP__surface__analysis": { + "href": "/vsisubfile/50329382_188,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "CPOFP - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "PRATE__surface__analysis": { + "href": "/vsisubfile/50329570_188,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "PRATE - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "APCP__surface__instantaneous__acc": { + "href": "/vsisubfile/50329758_212,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "APCP - surface - instantaneous - acc", + "hrrr:forecast_layer_type": "instantaneous", + "hrrr:start_timedelta": "2024-05-10T12:00:00", + "hrrr:end_timedelta": "2024-05-10T12:00:00", + "hrrr:statistic_type": "acc", + "roles": [ + "data" + ] + }, + "WEASD__surface__instantaneous__acc": { + "href": "/vsisubfile/50329970_212,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "WEASD - surface - instantaneous - acc", + "hrrr:forecast_layer_type": "instantaneous", + "hrrr:start_timedelta": "2024-05-10T12:00:00", + "hrrr:end_timedelta": "2024-05-10T12:00:00", + "hrrr:statistic_type": "acc", + "roles": [ + "data" + ] + }, + "FROZR__surface__instantaneous__acc": { + "href": "/vsisubfile/50330182_212,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "FROZR - surface - instantaneous - acc", + "hrrr:forecast_layer_type": "instantaneous", + "hrrr:start_timedelta": "2024-05-10T12:00:00", + "hrrr:end_timedelta": "2024-05-10T12:00:00", + "hrrr:statistic_type": "acc", + "roles": [ + "data" + ] + }, + "FRZR__surface__instantaneous__acc": { + "href": "/vsisubfile/50330394_14876,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "FRZR - surface - instantaneous - acc", + "hrrr:forecast_layer_type": "instantaneous", + "hrrr:start_timedelta": "2024-05-10T12:00:00", + "hrrr:end_timedelta": "2024-05-10T12:00:00", + "hrrr:statistic_type": "acc", + "roles": [ + "data" + ] + }, + "SSRUN__surface__instantaneous__acc": { + "href": "/vsisubfile/50345270_212,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "SSRUN - surface - instantaneous - acc", + "hrrr:forecast_layer_type": "instantaneous", + "hrrr:start_timedelta": "2024-05-10T12:00:00", + "hrrr:end_timedelta": "2024-05-10T12:00:00", + "hrrr:statistic_type": "acc", + "roles": [ + "data" + ] + }, + "BGRUN__surface__instantaneous__acc": { + "href": "/vsisubfile/50345482_212,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "BGRUN - surface - instantaneous - acc", + "hrrr:forecast_layer_type": "instantaneous", + "hrrr:start_timedelta": "2024-05-10T12:00:00", + "hrrr:end_timedelta": "2024-05-10T12:00:00", + "hrrr:statistic_type": "acc", + "roles": [ + "data" + ] + }, + "CSNOW__surface__analysis": { + "href": "/vsisubfile/50345694_188,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "CSNOW - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "CICEP__surface__analysis": { + "href": "/vsisubfile/50345882_188,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "CICEP - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "CFRZR__surface__analysis": { + "href": "/vsisubfile/50346070_188,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "CFRZR - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "CRAIN__surface__analysis": { + "href": "/vsisubfile/50346258_188,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "CRAIN - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "SFCR__surface__analysis": { + "href": "/vsisubfile/50346446_1893461,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "SFCR - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "FRICV__surface__analysis": { + "href": "/vsisubfile/52239907_1058245,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "FRICV - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "SHTFL__surface__analysis": { + "href": "/vsisubfile/53298152_1215256,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "SHTFL - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "LHTFL__surface__analysis": { + "href": "/vsisubfile/54513408_1157219,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "LHTFL - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "VEG__surface__analysis": { + "href": "/vsisubfile/55670627_1471458,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "VEG - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "VEGMIN__surface__analysis": { + "href": "/vsisubfile/57142085_1116137,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "VEGMIN - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "VEGMAX__surface__analysis": { + "href": "/vsisubfile/58258222_876934,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "VEGMAX - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "LAI__surface__analysis": { + "href": "/vsisubfile/59135156_835225,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "LAI - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "GFLUX__surface__analysis": { + "href": "/vsisubfile/59970381_587415,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "GFLUX - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "VGTYP__surface__analysis": { + "href": "/vsisubfile/60557796_781172,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "VGTYP - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "LFTX__500-1000_mb__analysis": { + "href": "/vsisubfile/61338968_960907,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "LFTX - 500-1000 mb - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "CAPE__surface__analysis": { + "href": "/vsisubfile/62299875_373062,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "CAPE - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "CIN__surface__analysis": { + "href": "/vsisubfile/62672937_236119,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "CIN - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "PWAT__entire_atmosphere_(considered_as_a_single_layer)__analysis": { + "href": "/vsisubfile/62909056_964440,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "PWAT - entire atmosphere (considered as a single layer) - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "AOTK__entire_atmosphere_(considered_as_a_single_layer)__analysis": { + "href": "/vsisubfile/63873496_188,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "AOTK - entire atmosphere (considered as a single layer) - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "COLMD__entire_atmosphere_(considered_as_a_single_layer)__analysis": { + "href": "/vsisubfile/63873684_956913,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "COLMD - entire atmosphere (considered as a single layer) - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "TCOLW__entire_atmosphere__analysis": { + "href": "/vsisubfile/64830597_1011101,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "TCOLW - entire atmosphere - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "TCOLI__entire_atmosphere__analysis": { + "href": "/vsisubfile/65841698_665989,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "TCOLI - entire atmosphere - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "TCDC__boundary_layer_cloud_layer__analysis": { + "href": "/vsisubfile/66507687_658472,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "TCDC - boundary layer cloud layer - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "LCDC__low_cloud_layer__analysis": { + "href": "/vsisubfile/67166159_798254,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "LCDC - low cloud layer - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "MCDC__middle_cloud_layer__analysis": { + "href": "/vsisubfile/67964413_258527,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "MCDC - middle cloud layer - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "HCDC__high_cloud_layer__analysis": { + "href": "/vsisubfile/68222940_266246,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "HCDC - high cloud layer - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "TCDC__entire_atmosphere__analysis": { + "href": "/vsisubfile/68489186_866399,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "TCDC - entire atmosphere - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "HGT__cloud_ceiling__analysis": { + "href": "/vsisubfile/69355585_1078460,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "HGT - cloud ceiling - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "HGT__cloud_base__analysis": { + "href": "/vsisubfile/70434045_1957211,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "HGT - cloud base - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "PRES__cloud_base__analysis": { + "href": "/vsisubfile/72391256_936211,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "PRES - cloud base - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "PRES__cloud_top__analysis": { + "href": "/vsisubfile/73327467_607404,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "PRES - cloud top - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "HGT__cloud_top__analysis": { + "href": "/vsisubfile/73934871_928535,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "HGT - cloud top - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "ULWRF__top_of_atmosphere__analysis": { + "href": "/vsisubfile/74863406_1745120,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "ULWRF - top of atmosphere - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "DSWRF__surface__analysis": { + "href": "/vsisubfile/76608526_1341640,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "DSWRF - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "DLWRF__surface__analysis": { + "href": "/vsisubfile/77950166_2022791,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "DLWRF - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "USWRF__surface__analysis": { + "href": "/vsisubfile/79972957_1050995,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "USWRF - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "ULWRF__surface__analysis": { + "href": "/vsisubfile/81023952_1623028,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "ULWRF - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "CFNSF__surface__analysis": { + "href": "/vsisubfile/82646980_5630,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "CFNSF - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "VBDSF__surface__analysis": { + "href": "/vsisubfile/82652610_1153003,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "VBDSF - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "VDDSF__surface__analysis": { + "href": "/vsisubfile/83805613_1326009,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "VDDSF - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "USWRF__top_of_atmosphere__analysis": { + "href": "/vsisubfile/85131622_1459932,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "USWRF - top of atmosphere - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "HLCY__3000-0_m_above_ground__analysis": { + "href": "/vsisubfile/86591554_1160137,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "HLCY - 3000-0 m above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "HLCY__1000-0_m_above_ground__analysis": { + "href": "/vsisubfile/87751691_1894852,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "HLCY - 1000-0 m above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "USTM__0-6000_m_above_ground__analysis": { + "href": "/vsisubfile/89646543_1007020,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "USTM - 0-6000 m above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "VSTM__0-6000_m_above_ground__analysis": { + "href": "/vsisubfile/90653563_949078,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "VSTM - 0-6000 m above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "VUCSH__0-1000_m_above_ground__analysis": { + "href": "/vsisubfile/91602641_2381615,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "VUCSH - 0-1000 m above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "VVCSH__0-1000_m_above_ground__analysis": { + "href": "/vsisubfile/93984256_2381615,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "VVCSH - 0-1000 m above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "VUCSH__0-6000_m_above_ground__analysis": { + "href": "/vsisubfile/96365871_2619757,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "VUCSH - 0-6000 m above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "VVCSH__0-6000_m_above_ground__analysis": { + "href": "/vsisubfile/98985628_2619757,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "VVCSH - 0-6000 m above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "HGT__0C_isotherm__analysis": { + "href": "/vsisubfile/101605385_1851539,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "HGT - 0C isotherm - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "RH__0C_isotherm__analysis": { + "href": "/vsisubfile/103456924_715500,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "RH - 0C isotherm - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "PRES__0C_isotherm__analysis": { + "href": "/vsisubfile/104172424_716404,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "PRES - 0C isotherm - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "HGT__highest_tropospheric_freezing_level__analysis": { + "href": "/vsisubfile/104888828_711102,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "HGT - highest tropospheric freezing level - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "RH__highest_tropospheric_freezing_level__analysis": { + "href": "/vsisubfile/105599930_692988,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "RH - highest tropospheric freezing level - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "PRES__highest_tropospheric_freezing_level__analysis": { + "href": "/vsisubfile/106292918_698244,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "PRES - highest tropospheric freezing level - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "HGT__263_K_level__analysis": { + "href": "/vsisubfile/106991162_664196,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "HGT - 263 K level - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "HGT__253_K_level__analysis": { + "href": "/vsisubfile/107655358_623095,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "HGT - 253 K level - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "4LFTX__180-0_mb_above_ground__analysis": { + "href": "/vsisubfile/108278453_988520,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "4LFTX - 180-0 mb above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "CAPE__180-0_mb_above_ground__analysis": { + "href": "/vsisubfile/109266973_450930,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "CAPE - 180-0 mb above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "CIN__180-0_mb_above_ground__analysis": { + "href": "/vsisubfile/109717903_281601,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "CIN - 180-0 mb above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "HPBL__surface__analysis": { + "href": "/vsisubfile/109999504_2841509,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "HPBL - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "HGT__level_of_adiabatic_condensation_from_sfc__analysis": { + "href": "/vsisubfile/112841013_2871871,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "HGT - level of adiabatic condensation from sfc - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "CAPE__90-0_mb_above_ground__analysis": { + "href": "/vsisubfile/115712884_335763,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "CAPE - 90-0 mb above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "CIN__90-0_mb_above_ground__analysis": { + "href": "/vsisubfile/116048647_253547,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "CIN - 90-0 mb above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "CAPE__255-0_mb_above_ground__analysis": { + "href": "/vsisubfile/116302194_445177,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "CAPE - 255-0 mb above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "CIN__255-0_mb_above_ground__analysis": { + "href": "/vsisubfile/116747371_289674,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "CIN - 255-0 mb above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "HGT__equilibrium_level__analysis": { + "href": "/vsisubfile/117037045_2112637,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "HGT - equilibrium level - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "PLPL__255-0_mb_above_ground__analysis": { + "href": "/vsisubfile/119149682_1307181,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "PLPL - 255-0 mb above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "CAPE__0-3000_m_above_ground__analysis": { + "href": "/vsisubfile/120456863_539006,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "CAPE - 0-3000 m above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "HGT__level_of_free_convection__analysis": { + "href": "/vsisubfile/120995869_2720495,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "HGT - level of free convection - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "EFHL__surface__analysis": { + "href": "/vsisubfile/123716364_749578,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "EFHL - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "CANGLE__0-500_m_above_ground__analysis": { + "href": "/vsisubfile/124465942_2073519,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "CANGLE - 0-500 m above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "LAYTH__261_K_level_-_256_K_level__analysis": { + "href": "/vsisubfile/126539461_1275884,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "LAYTH - 261 K level - 256 K level - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "ESP__0-3000_m_above_ground__analysis": { + "href": "/vsisubfile/127815345_420946,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "ESP - 0-3000 m above ground - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "RHPW__entire_atmosphere__analysis": { + "href": "/vsisubfile/128236291_1207426,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "RHPW - entire atmosphere - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "LAND__surface__analysis": { + "href": "/vsisubfile/129443717_50465,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "LAND - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "ICEC__surface__analysis": { + "href": "/vsisubfile/129494182_418,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "ICEC - surface - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "SBT123__top_of_atmosphere__analysis": { + "href": "/vsisubfile/129494600_1441112,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "SBT123 - top of atmosphere - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "SBT124__top_of_atmosphere__analysis": { + "href": "/vsisubfile/130935712_1617420,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "SBT124 - top of atmosphere - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "SBT113__top_of_atmosphere__analysis": { + "href": "/vsisubfile/132553132_1315788,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "SBT113 - top of atmosphere - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + }, + "SBT114__top_of_atmosphere__analysis": { + "href": "/vsisubfile/133868920_..,/vsicurl/https://noaahrrr.blob.core.windows.net/hrrr/hrrr.20240510/conus/hrrr.t12z.wrfsfcf00.grib2", + "type": "application/wmo-GRIB2", + "title": "SBT114 - top of atmosphere - analysis", + "hrrr:forecast_layer_type": "analysis", + "roles": [ + "data" + ] + } + }, + "bbox": [ + -134.12142793280145, + 21.122192719272277, + -60.891244531606546, + 52.62870335266728 + ], + "stac_extensions": [], + "collection": "noaa-hrrr-sfc" +} \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index b71978d..7bd4262 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,10 @@ [project] -name = "stactools-ephemeral" +name = "stactools-noaa-hrrr" version = "0.1.0" -description = "PROVIDE DESCRIPTION HERE" +description = "NOAA High-Resolution Rapid Refresh (HRRR) stactools package" readme = "README.md" -authors = [{ name = "Your name here", email = "your-email@example.com" }] -maintainers = [{ name = "Your name here", email = "your-email@example.com" }] +authors = [{ name = "Henry Rodman", email = "henry@developmentseed.org" }] +maintainers = [{ name = "Henry Rodman", email = "henry@developmentseed.org" }] keywords = ["stactools", "pystac", "catalog", "STAC"] license = { text = "Apache-2.0" } classifiers = [ @@ -19,12 +19,17 @@ classifiers = [ "Programming Language :: Python :: 3.11", ] requires-python = ">=3.8" -dependencies = ["stactools>=0.4.0"] +dependencies = [ + "httpx", + "pandas~=2.2.2", + "stactools>=0.4.0", +] [project.optional-dependencies] dev = [ "black~=23.9", "codespell~=2.2", + "jupyterlab~=4.2.1", "mypy~=1.5", "pre-commit~=3.4", "pytest-cov~=4.1", @@ -34,8 +39,8 @@ dev = [ docs = ["pystac~=1.8", "ipykernel~=6.25", "jinja2~=3.1"] [project.urls] -Github = "https://github.com/stactools-packges/ephemeral" -Issues = "https://github.com/stactools-packges/ephemeral/issues" +Github = "https://github.com/stactools-packges/noaa-hrrr" +Issues = "https://github.com/stactools-packges/noaa-hrrr/issues" [build-system] requires = ["setuptools", "wheel"] @@ -53,3 +58,6 @@ mypy_path = "src" [tool.ruff] select = ["E", "F", "I"] + +[tool.setuptools.package-data] +"stactools.noaa_hrrr.data" = ["*.csv.gz"] diff --git a/scripts/cibuild b/scripts/cibuild index c637853..25aeeab 100755 --- a/scripts/cibuild +++ b/scripts/cibuild @@ -17,6 +17,9 @@ if [ "${BASH_SOURCE[0]}" = "${0}" ]; then if [ "${1:-}" = "--help" ]; then usage else + if [ "${DOCKER}" = "true" ]; then + source /opt/conda/bin/activate + fi ./scripts/update ./scripts/test fi diff --git a/scripts/generate-inventory b/scripts/generate-inventory new file mode 100755 index 0000000..17c6ab4 --- /dev/null +++ b/scripts/generate-inventory @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +import logging +from pathlib import Path + +from stactools.noaa_hrrr.inventory import generate_inventory_csv_gzs + +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", + handlers=[ + logging.FileHandler("/tmp/noaa_hrrr_generate_inventory.log"), + logging.StreamHandler(), + ], +) + +logger = logging.getLogger(__name__) + +root = Path(__file__).parents[1] +data_dir = root / "src" / "stactools" / "noaa_hrrr" / "data" + +generate_inventory_csv_gzs(data_dir) diff --git a/scripts/test b/scripts/test index 4f04f9f..26937d9 100755 --- a/scripts/test +++ b/scripts/test @@ -24,7 +24,7 @@ if [ "${BASH_SOURCE[0]}" = "${0}" ]; then *.py ./**/*.py \ *.md - pytest --cov=stactools.ephemeral tests + pytest --cov=stactools.noaa_hrrr tests coverage xml fi fi diff --git a/scripts/update-examples b/scripts/update-examples index d0772ed..641ca8b 100755 --- a/scripts/update-examples +++ b/scripts/update-examples @@ -1,20 +1,35 @@ #!/usr/bin/env python import shutil +from datetime import datetime from pathlib import Path -import stactools.ephemeral.stac +import stactools.noaa_hrrr.stac from pystac import CatalogType +from stactools.noaa_hrrr.constants import ( + CloudProvider, + Product, + Region, +) root = Path(__file__).parents[1] examples = root / "examples" -collection = stactools.ephemeral.stac.create_collection() -item = stactools.ephemeral.stac.create_item(str(root / "tests" / "data" / "asset.tif")) +collection = stactools.noaa_hrrr.stac.create_collection( + product=Product.sfc, + cloud_provider=CloudProvider.azure, +) +item = stactools.noaa_hrrr.stac.create_item( + product=Product.sfc, + reference_datetime=datetime(year=2024, month=5, day=10, hour=12), + forecast_hour=0, + region=Region.conus, + cloud_provider=CloudProvider.azure, +) collection.add_item(item) collection.update_extent_from_items() collection.normalize_hrefs(str(examples)) -collection.make_all_asset_hrefs_relative() +# collection.make_all_asset_hrefs_relative() if examples.exists(): shutil.rmtree(examples) examples.mkdir() diff --git a/src/stactools/ephemeral/commands.py b/src/stactools/ephemeral/commands.py deleted file mode 100644 index 637b471..0000000 --- a/src/stactools/ephemeral/commands.py +++ /dev/null @@ -1,48 +0,0 @@ -import logging - -import click -from click import Command, Group -from stactools.ephemeral import stac - -logger = logging.getLogger(__name__) - - -def create_ephemeralcmd_command(cli: Group) -> Command: - """Creates the stactools-ephemeral command line utility.""" - - @cli.group( - "ephemeralcmd", - short_help=("Commands for working with stactools-ephemeral"), - ) - def ephemeralcmd() -> None: - pass - - @ephemeralcmd.command( - "create-collection", - short_help="Creates a STAC collection", - ) - @click.argument("destination") - def create_collection_command(destination: str) -> None: - """Creates a STAC Collection - - Args: - destination: An HREF for the Collection JSON - """ - collection = stac.create_collection() - collection.set_self_href(destination) - collection.save_object() - - @ephemeralcmd.command("create-item", short_help="Create a STAC item") - @click.argument("source") - @click.argument("destination") - def create_item_command(source: str, destination: str) -> None: - """Creates a STAC Item - - Args: - source: HREF of the Asset associated with the Item - destination: An HREF for the STAC Item - """ - item = stac.create_item(source) - item.save_object(dest_href=destination) - - return ephemeralcmd diff --git a/src/stactools/ephemeral/stac.py b/src/stactools/ephemeral/stac.py deleted file mode 100644 index 346aaa5..0000000 --- a/src/stactools/ephemeral/stac.py +++ /dev/null @@ -1,67 +0,0 @@ -from datetime import datetime, timezone - -import stactools.core.create -from pystac import ( - Collection, - Extent, - Item, - SpatialExtent, - TemporalExtent, -) - - -def create_collection() -> Collection: - """Creates a STAC Collection. - - This function should create a collection for this dataset. See `the STAC - specification - `_ - for information about collection fields, and - `Collection`_ - for information about the PySTAC class. - - Returns: - Collection: STAC Collection object - """ - extent = Extent( - SpatialExtent([[-180.0, 90.0, 180.0, -90.0]]), - TemporalExtent([[datetime.now(tz=timezone.utc), None]]), - ) - - collection = Collection( - id="example-collection", - title="Example collection", - description="An example collection", - extent=extent, - extra_fields={"custom_attribute": "foo"}, - ) - return collection - - -def create_item(asset_href: str) -> Item: - """Creates a STAC item from a raster asset. - - This example function uses :py:func:`stactools.core.utils.create_item` to - generate an example item. Datasets should customize the item with - dataset-specific information, e.g. extracted from metadata files. - - See `the STAC specification - `_ - for information about an item's fields, and - `Item`_ for - information on the PySTAC class. - - This function should be updated to take all hrefs needed to build the item. - It is an anti-pattern to assume that related files (e.g. metadata) are in - the same directory as the primary file. - - Args: - asset_href (str): The HREF pointing to an asset associated with the item - - Returns: - Item: STAC Item object - """ - item = stactools.core.create.item(asset_href) - item.id = "example-item" - item.properties["custom_attribute"] = "foo" - return item diff --git a/src/stactools/ephemeral/__init__.py b/src/stactools/noaa_hrrr/__init__.py similarity index 52% rename from src/stactools/ephemeral/__init__.py rename to src/stactools/noaa_hrrr/__init__.py index a65af21..76915af 100644 --- a/src/stactools/ephemeral/__init__.py +++ b/src/stactools/noaa_hrrr/__init__.py @@ -1,6 +1,6 @@ import stactools.core from stactools.cli.registry import Registry -from stactools.ephemeral.stac import create_collection, create_item +from stactools.noaa_hrrr.stac import create_collection, create_item __all__ = ["create_collection", "create_item"] @@ -8,6 +8,6 @@ def register_plugin(registry: Registry) -> None: - from stactools.ephemeral import commands + from stactools.noaa_hrrr import commands - registry.register_subcommand(commands.create_ephemeralcmd_command) + registry.register_subcommand(commands.create_noaahrrr_command) diff --git a/src/stactools/noaa_hrrr/commands.py b/src/stactools/noaa_hrrr/commands.py new file mode 100644 index 0000000..fa9f97e --- /dev/null +++ b/src/stactools/noaa_hrrr/commands.py @@ -0,0 +1,127 @@ +import logging +import os +from datetime import datetime + +import click +from click import Command, Group +from stactools.noaa_hrrr import stac +from stactools.noaa_hrrr.constants import ( + EXTENDED_FORECAST_MAX_HOUR, + CloudProvider, + Product, + Region, +) + +logger = logging.getLogger(__name__) + + +def create_noaahrrr_command(cli: Group) -> Command: + """Creates the stactools-noaa-hrrr command line utility.""" + + @cli.group( + "noaahrrr", + short_help=("Commands for working with stactools-noaa-hrrr"), + ) + def noaahrrr() -> None: + pass + + @noaahrrr.command( + "create-collection", + short_help="Creates a STAC collection", + ) + @click.argument("product", type=click.STRING) + @click.argument("cloud_provider", type=click.STRING) + @click.argument("destination", type=click.STRING) + def create_collection_command( + product: str, + cloud_provider: str, + destination: str, + ) -> None: + """Creates a STAC Collection + + Args: + product (str): one of 'sfc', 'nat', 'prs', or 'subh' + cloud_provider (str): one of 'azure', 'aws', or 'google' + destination: An HREF for the Collection JSON + """ + collection = stac.create_collection( + product=Product.from_str(product), + cloud_provider=CloudProvider.from_str(cloud_provider), + ) + collection.set_self_href(destination) + collection.save_object() + + @noaahrrr.command("create-item", short_help="Create a STAC item") + @click.argument("region", type=click.STRING) + @click.argument("product", type=click.STRING) + @click.argument("cloud_provider", type=click.STRING) + @click.argument("reference_datetime", type=click.DateTime(formats=["%Y-%m-%dT%H"])) + @click.argument("forecast_hour", type=click.IntRange(0, EXTENDED_FORECAST_MAX_HOUR)) + @click.argument("destination", type=click.STRING) + def create_item_command( + region: str, + product: str, + cloud_provider: str, + reference_datetime: datetime, + forecast_hour: int, + destination: str, + ) -> None: + """Creates a STAC Item + + Args: + region (str): either 'conus' or 'alaska' + product (str): one of 'sfc', 'nat', 'prs', or 'subh' + cloud_provider (str): one of 'azure', 'aws', or 'google' + reference_datetime (datetime): datetime with year, month, day, and hour that + represents when the forecast was generated (cycle run hour) + forecast_hour (int): number of hours out from the reference_datetime for the + forecast + """ + item = stac.create_item( + product=Product.from_str(product), + reference_datetime=reference_datetime, + forecast_hour=forecast_hour, + region=Region.from_str(region), + cloud_provider=CloudProvider.from_str(cloud_provider), + ) + item.save_object(dest_href=destination) + + @noaahrrr.command( + "create-item-collection", + short_help="Create a collection of STAC items for a date range", + ) + @click.argument("region", type=click.STRING) + @click.argument("product", type=click.STRING) + @click.argument("cloud_provider", type=click.STRING) + @click.argument("start_date", type=click.DateTime(formats=["%Y-%m-%d"])) + @click.argument("end_date", type=click.DateTime(formats=["%Y-%m-%d"])) + @click.argument("destination", type=click.STRING) + def create_item_collection_command( + region: str, + product: str, + cloud_provider: str, + start_date: datetime, + end_date: datetime, + destination: str, + ) -> None: + """Creates a STAC Item + + Args: + region (str): either 'conus' or 'alaska' + product (str): one of 'sfc', 'nat', 'prs', or 'subh' + cloud_provider (str): one of 'azure', 'aws', or 'google' + start_date, end_datetime (datetime): date range for which you want to create + an item collection + destination (str): destination path to save files + """ + item_collection = stac.create_item_collection( + product=Product.from_str(product), + region=Region.from_str(region), + cloud_provider=CloudProvider.from_str(cloud_provider), + start_date=start_date, + end_date=end_date, + ) + for item in item_collection: + item.save_object(dest_href=os.path.join(destination, f"{item.id}.json")) + + return noaahrrr diff --git a/src/stactools/noaa_hrrr/constants.py b/src/stactools/noaa_hrrr/constants.py new file mode 100644 index 0000000..ba76245 --- /dev/null +++ b/src/stactools/noaa_hrrr/constants.py @@ -0,0 +1,411 @@ +import re +from dataclasses import dataclass +from datetime import datetime, timedelta +from enum import Enum +from pathlib import Path +from typing import Any, Generator, List, Optional, Type, TypeVar, Union + +from rasterio.crs import CRS +from rasterio.warp import transform_bounds + +DATA_DIR = Path(__file__).parent / "data" + +T = TypeVar("T", bound="StrEnum") + +ITEM_ID_FORMAT = "hrrr-{region}-{product}-{reference_datetime}-FH{forecast_hour}" +COLLECTION_ID_FORMAT = "noaa-hrrr-{product}" + +EXTENDED_FORECAST_MAX_HOUR = 48 +STANDARD_FORECAST_MAX_HOUR = 18 + +RESOLUTION_METERS = 3000 + +VSI_PATH_FORMAT = "/vsisubfile/{start_byte}_{byte_size},/vsicurl/{grib_url}" + + +class StrEnum(str, Enum): + """A string-based enum, that can lookup an enum value from a string. + + This is built-in in Python 3.11 but if you're not there yet... + """ + + @classmethod + def from_str(cls: Type[T], s: str) -> T: + """Look up an enum value by string.""" + for value in cls: + if value == s: + return value + raise ValueError(f"Could not parse value from string: {s}") + + +class CloudProvider(StrEnum): + """Cloud storage provider sources""" + + azure = "azure" + aws = "aws" + google = "google" + + +class Region(StrEnum): + """Values for the 'region' parameter in the HRRR hrefs""" + + conus = "conus" + alaska = "alaska" + + +class Product(StrEnum): + """Values for the 'product' parameter in the HRRR hrefs""" + + prs = "prs" + nat = "nat" + sfc = "sfc" + subh = "subh" + + +class ForecastHourSet(StrEnum): + """Forecast hour sets + + Either FH00-01 or FH02-48, or FH00 or FH01-18 for sub-hourly. + The inventory of layers within a GRIB file depends on which set it is in + """ + + # for subhourly + FH00 = "fh00" + FH01_18 = "fh01-18" + + # everything else + FH00_01 = "fh00-01" + FH02_48 = "fh02-48" + + @classmethod + def from_forecast_hour_and_product( + cls, forecast_hour: int, product: Product + ) -> "ForecastHourSet": + """Pick the enum value given a forecast hour as an integer""" + if not 0 <= forecast_hour <= 48: + raise ValueError("integer must within 0-48") + if product == Product.subh: + return cls.FH00 if forecast_hour == 0 else cls.FH01_18 + else: + return cls.FH00_01 if forecast_hour < 2 else cls.FH02_48 + + def generate_forecast_hours(self) -> Generator[int, None, None]: + forecast_hour_range = [int(i) for i in self.value.replace("fh", "").split("-")] + + if len(forecast_hour_range) == 1: + yield forecast_hour_range[0] + else: + assert len(forecast_hour_range) == 2 + for i in range(forecast_hour_range[0], forecast_hour_range[1] + 1): + yield i + + +@dataclass +class ForecastCycleType: + """Forecast cycle types + + Standard forecasts are generated every hour in CONUS and every three hours in + Alaska, extended (48 hour) forecasts are generated every six hours. + """ + + type: str + + def __post_init__(self) -> None: + if self.type not in ["standard", "extended"]: + raise ValueError("Invalid forecast cycle type") + + self.max_forecast_hour = ( + STANDARD_FORECAST_MAX_HOUR + if self.type == "standard" + else EXTENDED_FORECAST_MAX_HOUR + ) + + @classmethod + def from_timestamp(cls, reference_datetime: datetime) -> "ForecastCycleType": + """Determine the forecast cycle type based on the timestamp of the cycle run + hour + + Extended forecasts are generated every six hours starting at hour 00 + """ + + extended = reference_datetime.hour % 6 == 0 + return cls("extended" if extended else "standard") + + def generate_forecast_hours(self) -> Generator[int, None, None]: + """Generate a list of forecast hours for the given forecast cycle type""" + + for i in range(0, self.max_forecast_hour + 1): + yield i + + def validate_forecast_hour(self, forecast_hour: int) -> None: + """Check if forecast hour is valid for the forecast type. + + Standard forecast cycles allow 0-18 + Extended forecast cycles allow 0-48 + """ + valid = 0 <= forecast_hour <= self.max_forecast_hour + if not valid: + raise ValueError( + ( + f"The provided forecast_hour ({forecast_hour}) is not compatible " + f"with the forecast cycle type ({str(self)})" + ) + ) + + def __str__(self) -> str: + return self.type + + +@dataclass +class ForecastLayerType: + """Each GRIB file has many forecast layers. Each one represents either a + real-time forecast (analysis), a point in time forecast, or a summary + statistic of the forecast for a period of time. + """ + + forecast_layer_type: str + start_timedelta: Optional[timedelta] = None + end_timedelta: Optional[timedelta] = None + statistic_type: Optional[str] = None + + @classmethod + def from_str(cls, forecast_str: str) -> "ForecastLayerType": + unit_lookup = { + "min": "minutes", + "hour": "hours", + "day": "days", + } + + if forecast_str == "anl": + return cls(forecast_layer_type="analysis") + + point_in_time_match = re.match(r"(\d+)\s(hour|min) fcst", forecast_str) + if point_in_time_match: + unit = point_in_time_match.group(2) + end = point_in_time_match.group(1) + + return cls( + forecast_layer_type="point_in_time", + end_timedelta=timedelta(**{unit_lookup[unit]: float(end)}), + ) + + summary_match = re.match( + r"(\d+)-(\d+)\s(hour|min|day)\s(max|ave|min|acc) fcst", forecast_str + ) + if summary_match: + start = int(summary_match.group(1)) + end = int(summary_match.group(2)) + unit = summary_match.group(3) + statistic_type = summary_match.group(4) + + if start == end: + forecast_layer_type = "instantaneous" + elif (start == 0) & (start < end): + forecast_layer_type = ( + "periodic_summary" + if ((end == 1) & (unit == "hour")) + else "cumulative_summary" + ) + elif (start > 0) & (start < end): + forecast_layer_type = "periodic_summary" + else: + raise ValueError( + f"could not parse the forecast_layer_type from '{forecast_str}'" + ) + + return cls( + forecast_layer_type=forecast_layer_type, + start_timedelta=timedelta(**{unit_lookup[unit]: start}), + end_timedelta=timedelta(**{unit_lookup[unit]: end}), + statistic_type=statistic_type, + ) + + else: + raise ValueError( + f"{forecast_str} cannot be parsed into a ForecastLayerType" + ) + + def asset_properties( + self, reference_datetime: datetime + ) -> dict[str, Union[str, datetime]]: + """Write the specific HRRR attributes out in a dictionary to be added to + asset metadata + """ + return { + f"hrrr:{attr}": (reference_datetime + val).strftime("%Y-%m-%dT%H:%M:%S") + if isinstance(val, timedelta) + else val + for attr, val in self.__dict__.items() + if val is not None + } + + def __str__(self) -> str: + out = self.forecast_layer_type + if self.statistic_type: + out = out.replace("summary", self.statistic_type) + + return out + + +@dataclass +class ProductConfig: + description: str + forecast_hour_sets: List[ForecastHourSet] + + +PRODUCT_CONFIGS = { + Product.sfc: ProductConfig( + description="2D surface levels", + forecast_hour_sets=[ForecastHourSet.FH00_01, ForecastHourSet.FH02_48], + ), + Product.prs: ProductConfig( + description="3D pressure levels", + forecast_hour_sets=[ForecastHourSet.FH00_01, ForecastHourSet.FH02_48], + ), + Product.nat: ProductConfig( + description="native levels", + forecast_hour_sets=[ForecastHourSet.FH00_01, ForecastHourSet.FH02_48], + ), + Product.subh: ProductConfig( + description="sub-hourly 2D surface levels", + forecast_hour_sets=[ForecastHourSet.FH00, ForecastHourSet.FH01_18], + ), +} + + +@dataclass +class RegionConfig: + """Since all items within a single region share the same exact extent and a few + other properties, store that information as a constant that can be used during STAC + metadata creation + """ + + item_bbox_proj: tuple[float, float, float, float] + item_crs: CRS + cycle_run_hours: List[int] + grib_url_format: str + + def __post_init__(self) -> None: + """Get bounding box and geometry in EPSG:4326""" + self.bbox_4326 = transform_bounds( + self.item_crs, + CRS.from_epsg(4326), + *self.item_bbox_proj, + densify_pts=3, + ) + + @property + def geometry_4326(self) -> dict[str, Any]: + return { + "type": "Polygon", + "coordinates": ( + ( + (self.bbox_4326[2], self.bbox_4326[1]), + (self.bbox_4326[2], self.bbox_4326[3]), + (self.bbox_4326[0], self.bbox_4326[3]), + (self.bbox_4326[0], self.bbox_4326[1]), + (self.bbox_4326[2], self.bbox_4326[1]), + ), + ), + } + + def format_grib_url( + self, + product: Product, + reference_datetime: datetime, + forecast_hour: int, + idx: bool = False, + ) -> str: + url = self.grib_url_format.format( + product=product.value, + date=reference_datetime, + fxx=forecast_hour, + ) + + if idx: + url += ".idx" + + return url + + +REGION_CONFIGS = { + Region.conus: RegionConfig( + item_bbox_proj=( + -2699020.142521929, + -1588806.152556665, + 2697979.857478071, + 1588193.847443335, + ), + item_crs=CRS.from_dict( + { + "proj": "lcc", + "lat_0": 38.5, + "lon_0": -97.5, + "lat_1": 38.5, + "lat_2": 38.5, + "x_0": 0, + "y_0": 0, + "R": 6371229, + "units": "m", + "no_defs": True, + } + ), + cycle_run_hours=[i for i in range(0, 24)], + grib_url_format="hrrr.{date:%Y%m%d}/conus/hrrr.t{date:%H}z.wrf{product}f{fxx:02d}.grib2", + ), + Region.alaska: RegionConfig( + item_bbox_proj=( + -3426551.0294707343, + -4100304.1031459086, + 470448.9705292657, + -1343304.1031459086, + ), + item_crs=CRS.from_dict( + { + "proj": "stere", + "lat_0": 90, + "lat_ts": 60, + "lon_0": 225, + "x_0": 0, + "y_0": 0, + "R": 6371229, + "units": "m", + "no_defs": True, + } + ), + cycle_run_hours=[i for i in range(0, 24, 3)], + grib_url_format="hrrr.{date:%Y%m%d}/alaska/hrrr.t{date:%H}z.wrf{product}f{fxx:02d}.ak.grib2", + ), +} + +# override bbox for alaska since rasterio can't handle it (sets xmin to +156) +REGION_CONFIGS[Region.alaska].bbox_4326 = (-174.8849, 41.5960, -115.6988, 76.3464) + + +@dataclass +class CloudProviderConfig: + start_date: datetime + url_base: str + + +CLOUD_PROVIDER_CONFIGS = { + CloudProvider.aws: CloudProviderConfig( + start_date=datetime(year=2014, month=7, day=30), + url_base="https://noaa-hrrr-bdp-pds.s3.amazonaws.com/", + ), + CloudProvider.azure: CloudProviderConfig( + start_date=datetime(year=2021, month=3, day=21), + url_base="https://noaahrrr.blob.core.windows.net/hrrr/", + ), + CloudProvider.google: CloudProviderConfig( + start_date=datetime(year=2014, month=7, day=30), + url_base="https://storage.googleapis.com/high-resolution-rapid-refresh/", + ), +} + + +class ItemType(StrEnum): + """STAC item types""" + + GRIB = "grib" + INDEX = "index" diff --git a/src/stactools/noaa_hrrr/data/inventory__alaska__nat.csv.gz b/src/stactools/noaa_hrrr/data/inventory__alaska__nat.csv.gz new file mode 100644 index 0000000..ca7a30b Binary files /dev/null and b/src/stactools/noaa_hrrr/data/inventory__alaska__nat.csv.gz differ diff --git a/src/stactools/noaa_hrrr/data/inventory__alaska__prs.csv.gz b/src/stactools/noaa_hrrr/data/inventory__alaska__prs.csv.gz new file mode 100644 index 0000000..96fd58f Binary files /dev/null and b/src/stactools/noaa_hrrr/data/inventory__alaska__prs.csv.gz differ diff --git a/src/stactools/noaa_hrrr/data/inventory__alaska__sfc.csv.gz b/src/stactools/noaa_hrrr/data/inventory__alaska__sfc.csv.gz new file mode 100644 index 0000000..254b229 Binary files /dev/null and b/src/stactools/noaa_hrrr/data/inventory__alaska__sfc.csv.gz differ diff --git a/src/stactools/noaa_hrrr/data/inventory__alaska__subh.csv.gz b/src/stactools/noaa_hrrr/data/inventory__alaska__subh.csv.gz new file mode 100644 index 0000000..14fa50c Binary files /dev/null and b/src/stactools/noaa_hrrr/data/inventory__alaska__subh.csv.gz differ diff --git a/src/stactools/noaa_hrrr/data/inventory__conus__nat.csv.gz b/src/stactools/noaa_hrrr/data/inventory__conus__nat.csv.gz new file mode 100644 index 0000000..670d2e9 Binary files /dev/null and b/src/stactools/noaa_hrrr/data/inventory__conus__nat.csv.gz differ diff --git a/src/stactools/noaa_hrrr/data/inventory__conus__prs.csv.gz b/src/stactools/noaa_hrrr/data/inventory__conus__prs.csv.gz new file mode 100644 index 0000000..45f7969 Binary files /dev/null and b/src/stactools/noaa_hrrr/data/inventory__conus__prs.csv.gz differ diff --git a/src/stactools/noaa_hrrr/data/inventory__conus__sfc.csv.gz b/src/stactools/noaa_hrrr/data/inventory__conus__sfc.csv.gz new file mode 100644 index 0000000..e3ab4ee Binary files /dev/null and b/src/stactools/noaa_hrrr/data/inventory__conus__sfc.csv.gz differ diff --git a/src/stactools/noaa_hrrr/data/inventory__conus__subh.csv.gz b/src/stactools/noaa_hrrr/data/inventory__conus__subh.csv.gz new file mode 100644 index 0000000..2ab1fc2 Binary files /dev/null and b/src/stactools/noaa_hrrr/data/inventory__conus__subh.csv.gz differ diff --git a/src/stactools/noaa_hrrr/inventory.py b/src/stactools/noaa_hrrr/inventory.py new file mode 100644 index 0000000..d731d0d --- /dev/null +++ b/src/stactools/noaa_hrrr/inventory.py @@ -0,0 +1,343 @@ +"""Each .grib file in the HRRR dataset contains dozens or hundreds of distinct variables +that represent data along several dimensions. The inventory files published by NOAA are +useful for the human-readable descriptions, but more reliable inventory dataframes can +be generated by reading the sidecar .grib2.idx files. + +The functions in this module generate the metadata required to define the coordinates +along the forecast_time x level dimensions on which specific variables have data. These +dataframes are used to populate the datacube extension metadata for each collection. + +The dimensions of interest are: +1. forecast time: either the average, minimum, maximum, or accumulated value for a + specific time range, e.g. 3-4 hours, 0-1 day, etc. + For forecast hour 0, the level is "analysis" +2. level: the models generate predictions of many of the variables for various levels + in the atmosphere, e.g. 0-9000 ft, cloud surface, top of atmosphere, etc. +""" +import logging +import multiprocessing as mp +from datetime import datetime, timedelta +from io import StringIO +from pathlib import Path +from typing import Optional + +import httpx +import pandas as pd +from stactools.noaa_hrrr.constants import ( + CLOUD_PROVIDER_CONFIGS, + DATA_DIR, + PRODUCT_CONFIGS, + REGION_CONFIGS, + CloudProvider, + ForecastCycleType, + ForecastHourSet, + Product, + Region, +) + +INVENTORY_CSV_GZ_FORMAT = "__".join( + [ + "inventory", + "{region}", + "{product}.csv.gz", + ] +) + +FORECAST_HOUR = "forecast_hour" +INVENTORY_COLS = [ + "grib_message", + "variable", + "level", + "forecast_time", +] +DESCRIPTION_COLS = [ + "description", + "unit", +] + +# URLs for the published inventory for each region/product/forecast_hour_set from NOAA +# https://www.nco.ncep.noaa.gov/pmb/products/hrrr/#CO +NOAA_INVENTORY_URLS = { + ( + Region.conus, + Product.prs, + ForecastHourSet.FH00_01, + ): "https://www.nco.ncep.noaa.gov/pmb/products/hrrr/hrrr.t00z.wrfprsf00.grib2.shtml", + ( + Region.conus, + Product.prs, + ForecastHourSet.FH02_48, + ): "https://www.nco.ncep.noaa.gov/pmb/products/hrrr/hrrr.t00z.wrfprsf02.grib2.shtml", + ( + Region.conus, + Product.nat, + ForecastHourSet.FH00_01, + ): "https://www.nco.ncep.noaa.gov/pmb/products/hrrr/hrrr.t00z.wrfnatf00.grib2.shtml", + ( + Region.conus, + Product.nat, + ForecastHourSet.FH02_48, + ): "https://www.nco.ncep.noaa.gov/pmb/products/hrrr/hrrr.t00z.wrfnatf02.grib2.shtml", + ( + Region.conus, + Product.sfc, + ForecastHourSet.FH00_01, + ): "https://www.nco.ncep.noaa.gov/pmb/products/hrrr/hrrr.t00z.wrfsfcf00.grib2.shtml", + ( + Region.conus, + Product.sfc, + ForecastHourSet.FH02_48, + ): "https://www.nco.ncep.noaa.gov/pmb/products/hrrr/hrrr.t00z.wrfsfcf02.grib2.shtml", + ( + Region.conus, + Product.subh, + ForecastHourSet.FH00, + ): "https://www.nco.ncep.noaa.gov/pmb/products/hrrr/hrrr.t00z.wrfsubhf00.grib2.shtml", + ( + Region.conus, + Product.subh, + ForecastHourSet.FH01_18, + ): "https://www.nco.ncep.noaa.gov/pmb/products/hrrr/hrrr.t00z.wrfsubhf02.grib2.shtml", + ( + Region.alaska, + Product.prs, + ForecastHourSet.FH00_01, + ): "https://www.nco.ncep.noaa.gov/pmb/products/hrrr/hrrr.t00z.wrfprsf00.ak.grib2.shtml", + ( + Region.alaska, + Product.prs, + ForecastHourSet.FH02_48, + ): "https://www.nco.ncep.noaa.gov/pmb/products/hrrr/hrrr.t00z.wrfprsf02.ak.grib2.shtml", + ( + Region.alaska, + Product.nat, + ForecastHourSet.FH00_01, + ): "https://www.nco.ncep.noaa.gov/pmb/products/hrrr/hrrr.t00z.wrfnatf00.ak.grib2.shtml", + ( + Region.alaska, + Product.nat, + ForecastHourSet.FH02_48, + ): "https://www.nco.ncep.noaa.gov/pmb/products/hrrr/hrrr.t00z.wrfnatf02.ak.grib2.shtml", + ( + Region.alaska, + Product.sfc, + ForecastHourSet.FH00_01, + ): "https://www.nco.ncep.noaa.gov/pmb/products/hrrr/hrrr.t00z.wrfsfcf00.ak.grib2.shtml", + ( + Region.alaska, + Product.sfc, + ForecastHourSet.FH02_48, + ): "https://www.nco.ncep.noaa.gov/pmb/products/hrrr/hrrr.t00z.wrfsfcf02.ak.grib2.shtml", + ( + Region.alaska, + Product.subh, + ForecastHourSet.FH00, + ): "https://www.nco.ncep.noaa.gov/pmb/products/hrrr/hrrr.t00z.wrfsubhf00.ak.grib2.shtml", + ( + Region.alaska, + Product.subh, + ForecastHourSet.FH01_18, + ): "https://www.nco.ncep.noaa.gov/pmb/products/hrrr/hrrr.t00z.wrfsubhf02.ak.grib2.shtml", +} + +# dummy datetime for generating inventory csv files +dummy_datetime = datetime(year=2024, month=5, day=1) + + +def load_inventory_df( + region: Region, + product: Product, + forecast_hour: Optional[int] = None, +) -> pd.DataFrame: + """Load variable inventory DataFrame + + Load the inventory of variables with descriptions, units, and level/forecast time + dimension values for a given region/product/forecast_hour_set/forecast_cycle_type + """ + inventory_df = pd.read_csv( + DATA_DIR + / INVENTORY_CSV_GZ_FORMAT.format( + region=region.value, + product=product.value, + ), + index_col=FORECAST_HOUR, + ) + + # optionally subset down to a specific forecast hour + if forecast_hour is not None: + inventory_df = inventory_df.loc[forecast_hour] + + return inventory_df + + +# Define custom exceptions +class NotFoundError(Exception): + pass + + +def read_idx( + region: Region, + product: Product, + cloud_provider: CloudProvider, + reference_datetime: datetime, + forecast_hour: int, +) -> pd.DataFrame: + """Read the contents of a .idx file, heavily cribbed from Herbie""" + region_config = REGION_CONFIGS[region] + cloud_provider_config = CLOUD_PROVIDER_CONFIGS[cloud_provider] + + idx_url = cloud_provider_config.url_base + region_config.format_grib_url( + product=product, + reference_datetime=reference_datetime, + forecast_hour=forecast_hour, + idx=True, + ) + + read_this_idx = None + + response = httpx.get(idx_url, timeout=20) + + try: + response.raise_for_status() # Raise an HTTPStatusError for 4xx/5xx status codes + except httpx.HTTPStatusError as e: + if e.response.status_code == 404: + response.close() + raise NotFoundError(f"404 Not Found: {idx_url}") + else: + response.close() + raise e + + read_this_idx = StringIO(response.text) + response.close() + + df = pd.read_csv( + read_this_idx, + sep=":", + names=[ + "grib_message", + "start_byte", + "reference_time", + "variable", + "level", + "forecast_time", + "?", + "??", + "???", + ], + ) + + # Format the DataFrame + df["reference_time"] = pd.to_datetime(df.reference_time, format="d=%Y%m%d%H") + df["valid_time"] = df["reference_time"] + timedelta(hours=forecast_hour) + df["start_byte"] = df["start_byte"].astype(int) + df["byte_size"] = (df["start_byte"].shift(-1) - df["start_byte"]).astype( + pd.Int64Dtype() + ) + df = df.reindex( + columns=[ + "grib_message", + "start_byte", + "byte_size", + "reference_time", + "valid_time", + "variable", + "level", + "forecast_time", + "?", + "??", + "???", + ] + ) + + return df.dropna(how="all", axis=1) + + +def generate_single_inventory_df( + region: Region, + product: Product, + cycle_run_hour: int, + forecast_hour: int, +) -> pd.DataFrame: + """Generate a single inventory DataFrame + + Loads the inventory dataframe from a .idx file""" + idx_df = read_idx( + region=region, + product=product, + cloud_provider=CloudProvider.azure, + reference_datetime=dummy_datetime + timedelta(hours=cycle_run_hour), + forecast_hour=forecast_hour, + ) + out = idx_df.assign( + region=region.value, + product=product.value, + forecast_hour=forecast_hour, + )[[FORECAST_HOUR] + INVENTORY_COLS] + + assert isinstance(out, pd.DataFrame) + + return out + + +def generate_inventory_csv_gzs(dest_dir: Path) -> None: + """Generate all inventory dataframes and write to .csv.gz files""" + forecast_cycle_type = ForecastCycleType("extended") + cycle_run_hour = 0 + for region in Region: + for product in Product: + product_config = PRODUCT_CONFIGS[product] + inventory_dfs = [] + allowed_forecast_hours = list(forecast_cycle_type.generate_forecast_hours()) + + for forecast_hour_set in product_config.forecast_hour_sets: + tasks = [] + for forecast_hour in list( + set(forecast_hour_set.generate_forecast_hours()) + & set(allowed_forecast_hours) + ): + tasks.append( + ( + region, + product, + cycle_run_hour, + forecast_hour, + ) + ) + + with mp.Pool() as pool: + dfs = pool.starmap(generate_single_inventory_df, tasks) + + inventory_df = pd.concat(dfs) + + n_rows = inventory_df.shape[0] + + # get the variable descriptions from the NOAA inventory tables + noaa_inventory = pd.read_html( + NOAA_INVENTORY_URLS[region, product, forecast_hour_set], + )[1] + + noaa_inventory[["description", "unit"]] = noaa_inventory[ + "Description" + ].str.extract(r"(.+?) \[(.+?)\]") + + variable_descriptions = ( + noaa_inventory[["Parameter", "description", "unit"]] + .drop_duplicates() + .rename(columns={"Parameter": "variable"}) + ) + + # add the variable descriptions + inventory_df = inventory_df.merge( + variable_descriptions, on="variable", how="left" + ) + assert inventory_df.shape[0] == n_rows + + inventory_dfs.append(inventory_df) + + inventory_csv_gz = dest_dir / INVENTORY_CSV_GZ_FORMAT.format( + region=region.value, + product=product.value, + ) + pd.concat(inventory_dfs).to_csv(inventory_csv_gz, index=False) + + logging.info(f"Data successfully written to {inventory_csv_gz}") diff --git a/src/stactools/noaa_hrrr/stac.py b/src/stactools/noaa_hrrr/stac.py new file mode 100644 index 0000000..6bedb52 --- /dev/null +++ b/src/stactools/noaa_hrrr/stac.py @@ -0,0 +1,363 @@ +import logging +import multiprocessing as mp +from datetime import datetime, timedelta +from typing import Union + +import pandas as pd +import pystac +from pystac import ( + Collection, + Extent, + Item, + SpatialExtent, + TemporalExtent, +) +from pystac.asset import Asset +from pystac.catalog import CatalogType +from pystac.extensions.item_assets import AssetDefinition +from pystac.item_collection import ItemCollection +from pystac.provider import Provider, ProviderRole +from stactools.noaa_hrrr.constants import ( + CLOUD_PROVIDER_CONFIGS, + COLLECTION_ID_FORMAT, + ITEM_ID_FORMAT, + PRODUCT_CONFIGS, + REGION_CONFIGS, + VSI_PATH_FORMAT, + CloudProvider, + ForecastCycleType, + ForecastLayerType, + ItemType, + Product, + Region, +) +from stactools.noaa_hrrr.inventory import NotFoundError, read_idx + +GRIB2_MEDIA_TYPE = "application/wmo-GRIB2" +NDJSON_MEDIA_TYPE = "application/x-ndjson" +INDEX_ASSET_DEFINITION = AssetDefinition( + { + "type": NDJSON_MEDIA_TYPE, + "roles": ["index"], + "title": "Index file", + "description": ( + "The index file contains information on each message within " + "the GRIB2 file." + ), + } +) + +ITEM_BASE_ASSETS = { + Product.sfc: { + ItemType.GRIB: AssetDefinition( + { + "type": GRIB2_MEDIA_TYPE, + "roles": ["data"], + "title": "2D Surface Levels", + "description": ( + "2D Surface Level forecast data as a grib2 file. Subsets of the " + "data can be loaded using the provided byte range." + ), + } + ), + ItemType.INDEX: INDEX_ASSET_DEFINITION, + }, + Product.subh: { + ItemType.GRIB: AssetDefinition( + { + "type": GRIB2_MEDIA_TYPE, + "roles": ["data"], + "title": "2D Surface Levels - Sub Hourly", + "description": ( + "2D Surface Level forecast data (sub-hourly, 15 minute intervals) " + "as a grib2 file. Subsets of the data can be loaded using the " + "provided byte range." + ), + } + ), + ItemType.INDEX: INDEX_ASSET_DEFINITION, + }, + Product.prs: { + ItemType.GRIB: AssetDefinition( + { + "type": GRIB2_MEDIA_TYPE, + "roles": ["data"], + "title": "3D Pressure Levels", + "description": ( + "3D Pressure Level forecast data as a grib2 file. Subsets of the " + "data can be loaded using the provided byte range." + ), + } + ), + ItemType.INDEX: INDEX_ASSET_DEFINITION, + }, + Product.nat: { + ItemType.GRIB: AssetDefinition( + { + "type": GRIB2_MEDIA_TYPE, + "roles": ["data"], + "title": "Native Levels", + "description": ( + "Native Level forecast data as a grib2 file. Subsets of the data " + "can be loaded using the provided byte range." + ), + } + ), + ItemType.INDEX: INDEX_ASSET_DEFINITION, + }, +} + + +def create_collection( + product: Product, + cloud_provider: CloudProvider, +) -> Collection: + """Creates a STAC Collection. + + Args: + product (Product): The product for this collection, must be one of the members + of the Product Enum. + cloud_provider (CloudProvider): cloud provider for the assets. Must be a member + of the CloudProvider Enum. Each cloud_provider has data available from a + different start date. + Returns: + Collection: STAC Collection object + """ + product_config = PRODUCT_CONFIGS[product] + cloud_provider_config = CLOUD_PROVIDER_CONFIGS[cloud_provider] + extent = Extent( + SpatialExtent( + [region_config.bbox_4326 for region_config in REGION_CONFIGS.values()] + ), + TemporalExtent([[cloud_provider_config.start_date, None]]), + ) + + providers = [ + Provider( + name="NOAA", + roles=[ProviderRole.PRODUCER], + url="https://www.noaa.gov/", + ) + ] + + links = [ + pystac.Link( + rel=pystac.RelType.LICENSE, + target="https://creativecommons.org/licenses/by/4.0/", + media_type="text/html", + title="CC-BY-4.0 license", + ), + pystac.Link( + rel="documentation", + target="https://rapidrefresh.noaa.gov/hrrr/", + media_type="text/html", + title="NOAA HRRR documentation", + ), + ] + + keywords = [ + "NOAA", + "HRRR", + "forecast", + "atmospheric", + "weather", + ] + + collection = Collection( + id=COLLECTION_ID_FORMAT.format( + product=product.value, + ), + title=( + "NOAA High Resolution Rapid Refresh (HRRR) - " + f"{product_config.description}" + ), + description=( + "The NOAA HRRR is a real-time 3km resolution, hourly updated, " + "cloud-resolving, convection-allowing atmospheric model, " + "initialized by 3km grids with 3km radar assimilation. Radar data is " + "assimilated in the HRRR every 15 min over a 1-hour period adding further " + "detail to that provided by the hourly data assimilation from the 13km " + "radar-enhanced Rapid Refresh (RAP) system. " + f"This specific collection represents {product_config.description}." + ), + extent=extent, + license="CC-BY-4.0", + providers=providers, + catalog_type=CatalogType.RELATIVE_PUBLISHED, + keywords=keywords, + ) + + collection.add_links(links) + + return collection + + +def create_item( + region: Region, + product: Product, + cloud_provider: CloudProvider, + reference_datetime: datetime, + forecast_hour: int, +) -> Item: + """Creates a STAC item for a region x product x cloud provider x reference_datetime + (cycle run hour) combination. + + Args: + region (Region): Either Region.conus or Region.Alaska + product (Product): The product for this collection, must be one of the members + of the Product Enum. + cloud_provider (CloudProvider): cloud provider for the assets. Must be a member + of the CloudProvider Enum. Each cloud_provider has data available from a + different start date. + reference_datetime (datetime): The reference datetime for the forecast data, + corresponds to 'date' + 'cycle run hour' + forecast_hour (int): The forecast hour (FH) for the item. + This will set the item's datetime property ('date' + 'cycle run hour' + + 'forecast hour') + + Returns: + Item: STAC Item object + """ + region_config = REGION_CONFIGS[region] + cloud_provider_config = CLOUD_PROVIDER_CONFIGS[cloud_provider] + + # make sure there is data for the reference_datetime + # (Alaska only runs the model every three hours) + if cycle_run_hour := reference_datetime.hour not in region_config.cycle_run_hours: + cycle_run_hours = [str(hour) for hour in region_config.cycle_run_hours] + raise ValueError( + f"{cycle_run_hour} is not a valid cycle run hour for {region.value}\n" + f"Please select one of {' ,'.join(cycle_run_hours)}" + ) + + # set up item + forecast_datetime = reference_datetime + timedelta(hours=forecast_hour) + + # the forecast_cycle_type defines the available forecast hours and products + forecast_cycle_type = ForecastCycleType.from_timestamp( + reference_datetime=reference_datetime + ) + + forecast_cycle_type.validate_forecast_hour(forecast_hour) + + item = Item( + ITEM_ID_FORMAT.format( + product=product.value, + reference_datetime=reference_datetime.strftime("%Y-%m-%dT%H"), + forecast_hour=forecast_hour, + region=region.value, + ), + geometry=region_config.geometry_4326, + bbox=region_config.bbox_4326, + datetime=forecast_datetime, + properties={ + "forecast:reference_time": reference_datetime.strftime("%Y-%m-%dT%H:%M:%S"), + "forecast:horizon": f"PT{forecast_hour}H", + "noaa-hrrr:forecast_cycle_type": str(forecast_cycle_type), + "noaa-hrrr:region": region.value, + }, + ) + + grib_url = cloud_provider_config.url_base + region_config.format_grib_url( + product=product, + reference_datetime=reference_datetime, + forecast_hour=forecast_hour, + idx=False, + ) + idx_url = grib_url + ".idx" + + item.assets[ItemType.GRIB.value] = ITEM_BASE_ASSETS[product][ + ItemType.GRIB + ].create_asset(grib_url) + + item.assets[ItemType.INDEX.value] = ITEM_BASE_ASSETS[product][ + ItemType.INDEX + ].create_asset(idx_url) + + # create an asset for each row in the inventory dataframe + idx_df = read_idx( + region=region, + product=product, + cloud_provider=cloud_provider, + reference_datetime=reference_datetime, + forecast_hour=forecast_hour, + ) + for _, row in idx_df.iterrows(): + forecast_layer_type = ForecastLayerType.from_str(row.forecast_time) + + asset_key = "__".join( + [ + row.variable.replace(" ", "_"), + row.level.replace(" ", "_"), + str(forecast_layer_type), + ] + ) + byte_size = row.byte_size + if pd.isna(byte_size): + byte_size = ".." + + asset_href = VSI_PATH_FORMAT.format( + start_byte=row.start_byte, + byte_size=byte_size, + grib_url=grib_url, + ) + + item.assets[asset_key] = Asset( + href=asset_href, + title=asset_key.replace("__", " - ").replace("_", " "), + media_type=GRIB2_MEDIA_TYPE, + roles=["data"], + extra_fields=forecast_layer_type.asset_properties( + reference_datetime=reference_datetime + ), + ) + + return item + + +def create_item_safe( + region: Region, + product: Product, + cloud_provider: CloudProvider, + reference_datetime: datetime, + forecast_hour: int, +) -> Union[Item, None]: + try: + return create_item( + region, product, cloud_provider, reference_datetime, forecast_hour + ) + except NotFoundError as e: + logging.warning(e) + return None + + +def create_item_collection( + region: Region, + product: Product, + cloud_provider: CloudProvider, + start_date: datetime, + end_date: datetime, +) -> pystac.ItemCollection: + """Create an item collection containing all items for a date range""" + + region_config = REGION_CONFIGS[region] + + one_day = timedelta(days=1) + tasks = [] + reference_date = start_date.replace(hour=0, minute=0, second=0, microsecond=0) + while reference_date <= end_date: + for cycle_run_hour in region_config.cycle_run_hours: + reference_datetime = reference_date + timedelta(hours=cycle_run_hour) + forecast_cycle_type = ForecastCycleType.from_timestamp(reference_datetime) + for forecast_hour in forecast_cycle_type.generate_forecast_hours(): + tasks.append( + (region, product, cloud_provider, reference_datetime, forecast_hour) + ) + + reference_date += one_day + + print(f"creating {len(tasks)} items") + with mp.Pool(8) as pool: + items = pool.starmap(create_item_safe, tasks) + + return ItemCollection(item for item in items if item is not None) diff --git a/tests/data/asset.tif b/tests/data/asset.tif deleted file mode 100644 index 54c83c7..0000000 Binary files a/tests/data/asset.tif and /dev/null differ diff --git a/tests/test_commands.py b/tests/test_commands.py index c468913..e759159 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -1,23 +1,40 @@ from pathlib import Path +import pytest from click import Group from click.testing import CliRunner from pystac import Collection, Item -from stactools.ephemeral.commands import create_ephemeralcmd_command - -from . import test_data - -command = create_ephemeralcmd_command(Group()) - - -def test_create_collection(tmp_path: Path) -> None: +from stactools.noaa_hrrr.commands import create_noaahrrr_command +from stactools.noaa_hrrr.constants import ( + CloudProvider, + Product, +) + +command = create_noaahrrr_command(Group()) + + +@pytest.mark.parametrize("product", list(Product)) # type: ignore +@pytest.mark.parametrize("cloud_provider", list(CloudProvider)) # type: ignore +def test_create_collection( + product: Product, + cloud_provider: CloudProvider, + tmp_path: Path, +) -> None: # Smoke test for the command line create-collection command # # Most checks should be done in test_stac.py::test_create_collection path = str(tmp_path / "collection.json") runner = CliRunner() - result = runner.invoke(command, ["create-collection", path]) + result = runner.invoke( + command, + [ + "create-collection", + product.value, + cloud_provider.value, + path, + ], + ) assert result.exit_code == 0, "\n{}".format(result.output) collection = Collection.from_file(path) collection.validate() @@ -27,10 +44,12 @@ def test_create_item(tmp_path: Path) -> None: # Smoke test for the command line create-item command # # Most checks should be done in test_stac.py::test_create_item - asset_href = test_data.get_path("data/asset.tif") path = str(tmp_path / "item.json") runner = CliRunner() - result = runner.invoke(command, ["create-item", asset_href, path]) + result = runner.invoke( + command, + ["create-item", "conus", "sfc", "azure", "2024-05-01T12", "0", path], + ) assert result.exit_code == 0, "\n{}".format(result.output) item = Item.from_file(path) item.validate() diff --git a/tests/test_inventory.py b/tests/test_inventory.py new file mode 100644 index 0000000..1037864 --- /dev/null +++ b/tests/test_inventory.py @@ -0,0 +1,48 @@ +import pandas as pd +import pytest +from stactools.noaa_hrrr.constants import ( + PRODUCT_CONFIGS, + Product, + Region, +) +from stactools.noaa_hrrr.inventory import ( + DESCRIPTION_COLS, + INVENTORY_COLS, + generate_single_inventory_df, + load_inventory_df, +) + +product_forecast_hour_combinations = [ + (product, fh_set) + for product, product_config in PRODUCT_CONFIGS.items() + for fh_set in product_config.forecast_hour_sets +] + + +@pytest.mark.parametrize("region", list(Region)) # type: ignore +@pytest.mark.parametrize("product", list(Product)) # type: ignore +def test_load_inventory_df( + region: Region, + product: Product, +) -> None: + inventory_df = load_inventory_df( + region=region, + product=product, + ) + + assert isinstance(inventory_df, pd.DataFrame) + + assert list(inventory_df.keys()) == INVENTORY_COLS + DESCRIPTION_COLS + + +@pytest.mark.parametrize("region", list(Region)) # type: ignore +@pytest.mark.parametrize("product", list(Product)) # type: ignore +def test_generate_single_inventory_df( + region: Region, + product: Product, +) -> None: + inventory_df = generate_single_inventory_df( + region=region, product=product, cycle_run_hour=0, forecast_hour=0 + ) + + assert isinstance(inventory_df, pd.DataFrame) diff --git a/tests/test_stac.py b/tests/test_stac.py index 644b006..f956033 100644 --- a/tests/test_stac.py +++ b/tests/test_stac.py @@ -1,24 +1,152 @@ -from stactools.ephemeral import stac +from datetime import datetime, timedelta -from . import test_data +import pytest +from stactools.noaa_hrrr import stac +from stactools.noaa_hrrr.constants import ( + COLLECTION_ID_FORMAT, + ITEM_ID_FORMAT, + REGION_CONFIGS, + CloudProvider, + ForecastCycleType, + Product, + Region, +) -def test_create_collection() -> None: +@pytest.mark.parametrize("product", list(Product)) # type: ignore +@pytest.mark.parametrize("cloud_provider", list(CloudProvider)) # type: ignore +def test_create_collection( + product: Product, + cloud_provider: CloudProvider, +) -> None: # This function should be updated to exercise the attributes of interest on # the collection - collection = stac.create_collection() + collection = stac.create_collection( + product=product, + cloud_provider=cloud_provider, + ) collection.set_self_href(None) # required for validation to pass - assert collection.id == "example-collection" - assert collection.extra_fields["custom_attribute"] == "foo" + assert collection.id == COLLECTION_ID_FORMAT.format( + product=product.value, + ) collection.validate() -def test_create_item() -> None: - # This function should be updated to exercise the attributes of interest on - # a typical item - - item = stac.create_item(test_data.get_path("data/asset.tif")) - assert item.id == "example-item" - assert item.properties["custom_attribute"] == "foo" +@pytest.mark.parametrize("product", list(Product)) # type: ignore +@pytest.mark.parametrize("cloud_provider", list(CloudProvider)) # type: ignore +@pytest.mark.parametrize("region", list(Region)) # type: ignore +def test_create_item( + product: Product, + cloud_provider: CloudProvider, + region: Region, +) -> None: + reference_datetime = datetime( + year=2024, month=1, day=1, hour=6 + ) # pick hour=6 because alaska + forecast_hour = 12 + item = stac.create_item( + product=product, + reference_datetime=reference_datetime, + forecast_hour=forecast_hour, + region=region, + cloud_provider=cloud_provider, + ) + assert item.id == ITEM_ID_FORMAT.format( + region=region.value, + reference_datetime=reference_datetime.strftime("%Y-%m-%dT%H"), + forecast_hour=forecast_hour, + product=product.value, + ) + assert item.properties["forecast:reference_time"] == reference_datetime.strftime( + "%Y-%m-%dT%H:%M:%S" + ) item.validate() + + assert ( + item.properties["noaa-hrrr:forecast_cycle_type"] == "extended" + ) # because hour=6 + + +def test_create_item_collection() -> None: + start_date = datetime(year=2024, month=5, day=1) + item_collection = stac.create_item_collection( + product=Product.sfc, + cloud_provider=CloudProvider.azure, + region=Region.alaska, + start_date=start_date, + end_date=start_date, + ) + + n_expected_items = 0 + region_config = REGION_CONFIGS[Region.alaska] + for cycle_run_hour in region_config.cycle_run_hours: + forecast_cycle_type = ForecastCycleType.from_timestamp( + start_date + timedelta(hours=cycle_run_hour) + ) + for _ in forecast_cycle_type.generate_forecast_hours(): + n_expected_items += 1 + + assert len(item_collection) == n_expected_items + + +def test_create_item_forecast_cycle_type() -> None: + # try making an invalid forecast for a stand forecast cycle + with pytest.raises(ValueError): + _ = stac.create_item( + product=Product.sfc, + reference_datetime=datetime(year=2024, month=5, day=1, hour=3), + forecast_hour=30, + region=Region.conus, + cloud_provider=CloudProvider.azure, + ) + + valid_extended_forecast_item = stac.create_item( + product=Product.sfc, + reference_datetime=datetime(year=2024, month=5, day=1, hour=6), + forecast_hour=30, + region=Region.conus, + cloud_provider=CloudProvider.azure, + ) + assert ( + valid_extended_forecast_item.properties["noaa-hrrr:forecast_cycle_type"] + == "extended" + ) + + +def test_create_item_alaska() -> None: + # Alaska only runs forecasts every three hours (no forecast for hour=2) + with pytest.raises(ValueError): + _ = stac.create_item( + product=Product.sfc, + reference_datetime=datetime(year=2024, month=5, day=1, hour=2), + forecast_hour=0, + region=Region.alaska, + cloud_provider=CloudProvider.azure, + ) + + # extended forecasts are generated on hours 0, 6, 12, 18 + item = stac.create_item( + product=Product.sfc, + reference_datetime=datetime(year=2024, month=5, day=1, hour=0), + forecast_hour=19, + region=Region.alaska, + cloud_provider=CloudProvider.azure, + ) + + assert ( + item.properties["noaa-hrrr:forecast_cycle_type"] == "extended" + ) # because hour=6 + + # standard forecasts are generated on hours 0, 3, 6, 9, 12, 15, 18, 21 + item = stac.create_item( + product=Product.sfc, + reference_datetime=datetime(year=2024, month=5, day=1, hour=3), + forecast_hour=12, + region=Region.alaska, + cloud_provider=CloudProvider.azure, + ) + + assert ( + item.properties["noaa-hrrr:forecast_cycle_type"] == "standard" + ) # because hour=3 (not divisible by 6)