Source code for pyTMD.datasets.fetch_arcticdata

#!/usr/bin/env python
"""
fetch_arcticdata.py
Written by Tyler Sutterley (12/2025)
Download Arctic Ocean Tide Models from the NSF ArcticData archive

AODTM-5: https://arcticdata.io/catalog/view/doi:10.18739/A2901ZG3N
AOTIM-5: https://arcticdata.io/catalog/view/doi:10.18739/A2S17SS80
AOTIM-5-2018: https://arcticdata.io/catalog/view/doi:10.18739/A21R6N14K
Arc2kmTM: https://arcticdata.io/catalog/view/doi:10.18739/A2D21RK6K
Gr1kmTM: https://arcticdata.io/catalog/view/doi:10.18739/A2B853K18

CALLING SEQUENCE:
    python fetch_arcticdata.py --tide=Gr1kmTM

COMMAND LINE OPTIONS:
    --help: list the command line options
    -D X, --directory X: working data directory
    -T X, --tide X: Arctic tide model to download
        AODTM-5
        AOTIM-5
        AOTIM-5-2018
        Arc2kmTM
        Gr1kmTM
    -t X, --timeout X: timeout in seconds for blocking operations
    -M X, --mode X: Local permissions mode of the files downloaded

PYTHON DEPENDENCIES:
    future: Compatibility layer between Python 2 and Python 3
        https://python-future.org/

PROGRAM DEPENDENCIES:
    utilities.py: download and management utilities for syncing files

UPDATE HISTORY:
    Updated 12/2025: use URL class to build and operate on URLs
    Updated 10/2025: change default directory for tide models to cache
    Updated 09/2025: renamed module and function to fetch_arcticdata
        made a callable function and added function docstrings
    Updated 07/2025: add a default directory for tide models
    Updated 05/2023: added option to change connection timeout
    Updated 04/2023: using pathlib to define and expand paths
    Updated 11/2022: use f-strings for formatting verbose or ascii output
    Updated 06/2022: added Greenland 1km model (Gr1kmTM) to list of models
    Updated 04/2022: use argparse descriptions within documentation
    Updated 10/2021: using python logging for handling verbose output
    Updated 07/2021: can use prefix files to define command line arguments
    Updated 10/2020: using argparse to set command line parameters
    Written 08/2020
"""

from __future__ import print_function, annotations

import re
import logging
import pathlib
import zipfile
import argparse
import posixpath
import pyTMD.utilities

# default data directory for tide models
_default_directory = pyTMD.utilities.get_cache_path()


# PURPOSE: Download Arctic Ocean Tide Models from the NSF ArcticData archive
[docs] def fetch_arcticdata( model: str, directory: str | pathlib.Path | None = _default_directory, timeout: int | None = None, mode: oct = 0o775, ): """ Download Arctic Ocean Tide Models from the NSF ArcticData archive Parameters ---------- model: str Arctic tide model to download directory: str or pathlib.Path Working data directory timeout: int, default None Timeout in seconds for blocking operations mode: oct, default 0o775 Local permissions mode of the files downloaded """ # create logger for verbosity level logger = pyTMD.utilities.build_logger(__name__, level=logging.INFO) # digital object identifier (doi) for each Arctic tide model DOI = {} DOI["AODTM-5"] = "10.18739/A2901ZG3N" DOI["AOTIM-5"] = "10.18739/A2S17SS80" DOI["AOTIM-5-2018"] = "10.18739/A21R6N14K" DOI["Arc2kmTM"] = "10.18739/A2D21RK6K" DOI["Gr1kmTM"] = "10.18739/A2B853K18" # local subdirectory for each Arctic tide model LOCAL = {} LOCAL["AODTM-5"] = "aodtm5_tmd" LOCAL["AOTIM-5"] = "aotim5_tmd" LOCAL["AOTIM-5-2018"] = "Arc5km2018" LOCAL["Arc2kmTM"] = "Arc2kmTM" LOCAL["Gr1kmTM"] = "Gr1kmTM" # recursively create directories if non-existent directory = pyTMD.utilities.Path(directory).resolve() local_dir = directory.joinpath(LOCAL[model]) local_dir.mkdir(mode=mode, parents=True, exist_ok=True) # build host url for model resource_map_doi = f"resource_map_doi:{DOI[model]}" HOST = [ "https://arcticdata.io", "metacat", "d1", "mn", "v2", "packages", pyTMD.utilities.quote_plus(posixpath.join("application", "bagit-097")), pyTMD.utilities.quote_plus(resource_map_doi), ] URL = pyTMD.utilities.URL.from_parts(HOST) # download zipfile from host logger.info(f"{URL} -->\n") zfile = zipfile.ZipFile(URL.get(timeout=timeout)) # find model files within zip file rx = re.compile(r"(grid|h[0]?|UV[0]?|Model|xy)_(.*?)", re.VERBOSE) members = [m for m in zfile.filelist if rx.search(m.filename)] # extract each member for m in members: # strip directories from member filename m.filename = posixpath.basename(m.filename) local_file = local_dir.joinpath(m.filename) logger.info(str(local_file)) # extract file zfile.extract(m, path=local_dir) # change permissions mode local_file.chmod(mode=mode) # close the zipfile object zfile.close()
# PURPOSE: create argument parser def arguments(): parser = argparse.ArgumentParser( description="""Download Arctic Ocean Tide Models from the NSF ArcticData archive """, fromfile_prefix_chars="@", ) parser.convert_arg_line_to_args = pyTMD.utilities.convert_arg_line_to_args # command line parameters # working data directory for location of tide models parser.add_argument( "--directory", "-D", type=pathlib.Path, default=_default_directory, help="Working data directory", ) # Arctic Ocean tide model to download parser.add_argument( "--tide", "-T", metavar="TIDE", type=str, nargs="+", default=["Gr1kmTM"], choices=("AODTM-5", "AOTIM-5", "AOTIM-5-2018", "Arc2kmTM", "Gr1kmTM"), help="Arctic Ocean tide model to download", ) # connection timeout parser.add_argument( "--timeout", "-t", type=int, default=360, help="Timeout in seconds for blocking operations", ) # permissions mode of the local directories and files (number in octal) parser.add_argument( "--mode", "-M", type=lambda x: int(x, base=8), default=0o775, help="Permissions mode of the files downloaded", ) # return the parser return parser # This is the main part of the program that calls the individual functions def main(): # Read the system arguments listed after the program parser = arguments() args, _ = parser.parse_known_args() # check internet connection before attempting to run program if pyTMD.utilities.check_connection("https://arcticdata.io"): for m in args.tide: fetch_arcticdata( m, directory=args.directory, timeout=args.timeout, mode=args.mode, ) # run main program if __name__ == "__main__": main()