Source code for prereise.gather.winddata.hrrr.hrrr_api
import requests
from pandas import date_range
from tqdm import tqdm
from prereise.gather.winddata.hrrr.constants import DEFAULT_PRODUCT
from prereise.gather.winddata.hrrr.grib import GribRecordInfo
from prereise.gather.winddata.hrrr.helpers import (
formatted_filename,
get_indices_that_contain_selector,
)
[docs]class HrrrApi:
"""Class to interact with to get and download HRRR data. More information
about HRRR data can be found at `this link
<https://registry.opendata.aws/noaa-hrrr-pds/>`_ and `this link
<https://www.nco.ncep.noaa.gov/pmb/products/hrrr/>`_
:param prereise.gather.winddata.hrrr.downloader.Downloader downloader: class
that holds helper functions for downloading
:param str base_url: url to download data from. Should take "dt", "product"
and "hours_forecasted" as format variables. See
:mod:`prereise.gather.winddata.hrrr.constants` for more detail
"""
U_COMPONENT_FILTER = "UGRD:80 m above ground"
V_COMPONENT_FILTER = "VGRD:80 m above ground"
def __init__(self, downloader, base_url):
self.downloader = downloader
self.base_url = base_url
def _filename_url_iter(self, start_dt, end_dt, product):
"""Iterates from a start datetime (inclusive) to a end datetime (inclusive)
at 1 hour steps, returning a filename as well as a url based
on each intermediary datetime
:param datetime.datetime start_dt: datetime to start at
:param datetime.datetime end_dt: datetime to end at
:param str product: info at `this link <https://www.nco.ncep.noaa.gov/pmb/products/hrrr/>`_
:return: (*generator*) -- generator that yields filename and url
"""
for dt in date_range(start=start_dt, end=end_dt, freq="H").to_pydatetime():
url = self.base_url.format(dt=dt, product=product, hours_forecasted=0)
yield formatted_filename(dt, product), url
[docs] def download_meteorological_data(
self, start_dt, end_dt, directory, product, selectors=None
):
"""Iterates from a start datetime (inclusive) to a end datetime (inclusive)
at 1 hour steps, downloading data for each intermediary datetime into the
directory provided. product and selectors variables are used to control what
specific type of data to download. See `this link
<https://www.nco.ncep.noaa.gov/pmb/products/hrrr/>`_ to understand more about
product and `this link <https://github.com/blaylockbk/Herbie/blob/18945e4c5103386c98d08dcb2de590e2ac14c3d5/docs/user_guide/grib2.rst#how-grib-subsetting-works-in-herbie>`_ to understand more about what kind of strings can
be passed into selectors
:param datetime.datetime start_dt: datetime to start at
:param datetime.datetime end_dt: datetime to end at
:param str directory: file directory to download data into
:param str product: info at `this link
<https://www.nco.ncep.noaa.gov/pmb/products/hrrr/>`_
:param list selectors: list of strings that can be used to narrow down
the amount of data downloaded from a specific GRIB file.
"""
for filename, url in tqdm(self._filename_url_iter(start_dt, end_dt, product)):
grib_record_information_list = [GribRecordInfo.full_file()]
# first grab index file and figure out which bytes to download
if selectors:
index_url = f"{url}.idx"
response = requests.get(
index_url
) # index files are typically a few kb, so safe to hold in memory
raw_record_information_list = response.text.split("\n")
index_list = get_indices_that_contain_selector(
raw_record_information_list, selectors
)
grib_record_information_list = (
GribRecordInfo.generate_grib_record_information_list(
raw_record_information_list, index_list
)
)
with open(directory + filename, "ab") as f:
for grib_record_information in grib_record_information_list:
try:
self.downloader.download(
url,
f,
headers={
"Range": f"bytes={grib_record_information.byte_range_header_string()}"
},
)
except Exception:
print(
f"Failed to download data from {url} with byte range {grib_record_information.byte_range_header_string()}"
)
[docs] def download_wind_data(self, start_dt, end_dt, directory):
"""See :meth:`download_meteorological_data` for more information. Default
product used is "sfc" which represents 2D Surface Levels, and the selectors
used filter specifically for U component and V component of wind at 80 meters
above ground.
:param datetime.datetime start_dt: datetime to start at
:param datetime.datetime end_dt: datetime to end at
:param str directory: file directory to download data into
"""
self.download_meteorological_data(
start_dt,
end_dt,
directory,
product=DEFAULT_PRODUCT,
selectors=[self.U_COMPONENT_FILTER, self.V_COMPONENT_FILTER],
)