File: core.py

package info (click to toggle)
python-ulmo 0.8.8%2Bdfsg1-1.1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,064 kB
  • sloc: python: 6,135; makefile: 144; sh: 5
file content (175 lines) | stat: -rw-r--r-- 5,391 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
"""
    ulmo.nasa.daymet.core
    ~~~~~~~~~~~~~~~~~~~~~

    This module provides direct access to `NASA EARTHDATA ORNL DAAC 
    Daymet`_ web services.

    .. _NASA EARTHDATA ORNL DAAC Daymet: https://daymet.ornl.gov/dataaccess.html

"""
from future import standard_library
standard_library.install_aliases()
from builtins import str
from past.builtins import basestring
import contextlib
import io
import datetime
import time
import logging

import requests
import pandas as pd

from ulmo import util

VARIABLES = {"tmax": "maximum temperature",
            "tmin": "minimum temperature",
            "srad": "shortwave radiation",
            "vp": "vapor pressure",
            "swe": "snow-water equivalent",
            "prcp": "precipitation",
            "dayl": "daylength"}
MIN_YEAR = 1980
MAX_Year = int(time.strftime("%Y"))-1
MIN_LAT = 14.5
MAX_LAT = 52.0
MIN_LON = -131.0
MAX_LON = -53.0


DAYMET_SINGLEPIXEL_URL = "https://daymet.ornl.gov/data/send/saveData?lat={lat}&lon={lon}&measuredParams={vars}"

# configure logging
LOG_FORMAT = '%(message)s'
logging.basicConfig(format=LOG_FORMAT)
log = logging.getLogger(__name__)
log.setLevel(logging.INFO)


def get_variables():
    """retrieve a list of variables available

    Parameters
    ----------
    None

    Returns
    -------
        dictionary of variables with variable abbreviations as keys
        and description as values
    """
    return VARIABLES


def get_daymet_singlepixel(latitude, longitude, 
                           variables=['tmax', 'tmin', 'prcp'], years=None,
                           as_dataframe=True):
    """Fetches a time series of climate variables from the DAYMET single pixel extraction

    Parameters
    ----------
    latitude: float
        The latitude (WGS84), value between 52.0 and 14.5.
    longitude: float
        The longitude (WGS84), value between -131.0 and -53.0.
    variables : list of str
        Daymet parameters to fetch. default = ['tmax', 'tmin', 'prcp'].
        Available options:
            * 'tmax': maximum temperature
            * 'tmin': minimum temperature
            * 'srad': shortwave radiation
            * 'vp': vapor pressure
            * 'swe': snow-water equivalent
            * 'prcp': precipitation;
            * 'dayl' : daylength.
    years: list of int
        List of years to return. 
        Daymet version 2 available 1980 to the latest full calendar year.
        If ``None`` (default), all years will be returned
    as_dataframe : ``True`` (default) or ``False``
        if ``True`` return pandas dataframe
        if ``False`` return open file with contents in csv format

    Returns
    -------
    single_pixel_timeseries : pandas dataframe or csv filename
    """

    _check_coordinates(latitude, longitude)
    _check_variables(variables)
    if not years is None:
        _check_years(years)

    url_params = {'lat': latitude,
                  'lon': longitude,
                  'vars': _as_str(variables)}
    if years:
        url_params['years'] = _as_str(years)

    url = _get_service_url(url_params)
    log.info("making request for latitude, longitude: {}, {}".format(latitude, longitude))
    df = pd.read_csv(url, header=6)
    df.year, df.yday = df.year.astype('int'), df.yday.astype('int')
    df.index = pd.to_datetime(df.year.astype('str') + '-' + df.yday.astype('str'), format="%Y-%j")
    df.columns = [c[:c.index('(')].strip() if '(' in c else c for c in df.columns ]
    if as_dataframe:
        return df
    else:
        results = {}
        for key in df.columns:
            if key not in ['yday', 'year']:
                results[key] = dict(zip(df[key].index.format(), df[key]))
        return results


def _check_variables(variables):
    """make sure all variables are in list
    """
    bad_variables = [v for v in variables if v not in VARIABLES.keys()]
    if bad_variables:
        
        raise ValueError("the variable(s) provided ('{}') not\none of available options: '{}'".format(
            "', '".join(bad_variables), str(VARIABLES.keys())[2:-2]))


def _check_years(years):
    """make sure all years are in available year range
    """
    bad_years = [str(year) for year in years if not MIN_YEAR <= year <= MAX_Year]
    if bad_years:
        raise ValueError("the year(s) provided ({}) \nnot in available timerange ({}-{})".format(
            ", ".join(bad_years), MIN_YEAR, MAX_Year))


def _check_coordinates(lat, lon):
    """make sure the passed coordinates are in the available data range
    """
    bad_lat = not MIN_LAT <= lat <= MAX_LAT
    bad_lon = not MIN_LON <= lon <= MAX_LON
    
    if bad_lat or bad_lon:
        err_msg = "The specified latitude, longitude pair ({}, {})".format(lat, lon)
        err_msg += "\nis not in the available range of data:\n"
        err_msg += "\tLatitude = [{} - {}]".format(MIN_LAT, MAX_LAT)
        err_msg += "\n\tLongitude = [{} - {}]".format(MIN_LON, MAX_LON)
        raise ValueError(err_msg)


def _as_str(arg):
    """if arg is a list, convert to comma delimited string
    """
    if isinstance(arg, basestring):
        return arg
    else:
        return ','.join([str(a) for a in arg])


def _get_service_url(url_params):
    """return formatted url
    """
    url = DAYMET_SINGLEPIXEL_URL.format(**url_params)

    if 'years' in url_params:
        url += "&year={}".format(url_params['years'])
    return url