pynumerics 2.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pynumerics/__init__.py +73 -0
- pynumerics/_bin/Microsoft.CodeAnalysis.CSharp.dll +0 -0
- pynumerics/_bin/Microsoft.CodeAnalysis.LICENSE.txt +27 -0
- pynumerics/_bin/Microsoft.CodeAnalysis.dll +0 -0
- pynumerics/_bin/Numerics.dll +0 -0
- pynumerics/_bin/pynumerics.runtimeconfig.json +15 -0
- pynumerics/_coverage.py +3825 -0
- pynumerics/_util.py +237 -0
- pynumerics/_version.py +40 -0
- pynumerics/bootstrap.py +593 -0
- pynumerics/callbacks.py +791 -0
- pynumerics/copulas.py +910 -0
- pynumerics/data.py +2233 -0
- pynumerics/differentiation.py +348 -0
- pynumerics/distributions.py +4043 -0
- pynumerics/download.py +623 -0
- pynumerics/enums.py +119 -0
- pynumerics/errors.py +245 -0
- pynumerics/functions.py +1142 -0
- pynumerics/goodness_of_fit.py +699 -0
- pynumerics/graphs.py +556 -0
- pynumerics/hypothesis_tests.py +445 -0
- pynumerics/identity.py +144 -0
- pynumerics/integration.py +677 -0
- pynumerics/linalg.py +1408 -0
- pynumerics/machine_learning.py +1335 -0
- pynumerics/marshal.py +733 -0
- pynumerics/mathematics.py +1053 -0
- pynumerics/mcmc.py +1406 -0
- pynumerics/multivariate.py +1539 -0
- pynumerics/ode.py +156 -0
- pynumerics/optimization.py +1736 -0
- pynumerics/probability.py +966 -0
- pynumerics/py.typed +0 -0
- pynumerics/regional.py +522 -0
- pynumerics/roots.py +274 -0
- pynumerics/runtime.py +384 -0
- pynumerics/sampling.py +1758 -0
- pynumerics/special.py +1079 -0
- pynumerics/statistics.py +1958 -0
- pynumerics/timeseries.py +1326 -0
- pynumerics-2.3.0.dist-info/METADATA +142 -0
- pynumerics-2.3.0.dist-info/RECORD +46 -0
- pynumerics-2.3.0.dist-info/WHEEL +5 -0
- pynumerics-2.3.0.dist-info/licenses/LICENSE +10 -0
- pynumerics-2.3.0.dist-info/top_level.txt +1 -0
pynumerics/download.py
ADDED
|
@@ -0,0 +1,623 @@
|
|
|
1
|
+
"""Downloads of public hydrologic and climate records (``Numerics.Data.TimeSeriesDownload``).
|
|
2
|
+
|
|
3
|
+
The four providers are the U.S. Geological Survey Water Data API (:func:`from_usgs`), NOAA NCEI
|
|
4
|
+
GHCN-Daily station files (:func:`from_ghcn`), the Water Survey of Canada (:func:`from_chmn`) and the
|
|
5
|
+
Australian Bureau of Meteorology Water Data Online service (:func:`from_abom`). Each downloader
|
|
6
|
+
fetches the provider's response and parses it with the matching parser, which is also public
|
|
7
|
+
(:func:`parse_usgs`, :func:`parse_ghcn`, :func:`parse_chmn`, :func:`parse_abom`), so a saved response
|
|
8
|
+
gives the same series offline.
|
|
9
|
+
|
|
10
|
+
Every function returns a :class:`DownloadedSeries`: the :class:`~pynumerics.timeseries.TimeSeries`,
|
|
11
|
+
the raw text (USGS downloads only), the optional qualifiers, one per value, and whether the dates
|
|
12
|
+
are UTC. Requests, retries, units, gap filling and parsing all run in Numerics; nothing is parsed in
|
|
13
|
+
Python. The dates and values cross in two bulk copies, and the qualifiers in one.
|
|
14
|
+
|
|
15
|
+
``timeout`` cancels a download after that many seconds (``CancellationTokenSource.CancelAfter``) and
|
|
16
|
+
raises :class:`TimeoutError`. A provider timeout also raises :class:`TimeoutError`, a network failure
|
|
17
|
+
:class:`ConnectionError`, an invalid argument :class:`ValueError`, and a provider error or "No data
|
|
18
|
+
found" :class:`RuntimeError`, each with the Numerics message.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import time
|
|
24
|
+
from dataclasses import dataclass
|
|
25
|
+
from typing import Any
|
|
26
|
+
|
|
27
|
+
import numpy as np
|
|
28
|
+
|
|
29
|
+
from pynumerics._util import namespace, net_enum
|
|
30
|
+
from pynumerics.enums import NetEnum
|
|
31
|
+
from pynumerics.errors import net_errors, translate_public
|
|
32
|
+
from pynumerics.timeseries import TimeSeries, _net_date
|
|
33
|
+
|
|
34
|
+
_SEPARATOR = "\x1f"
|
|
35
|
+
"""Joins the qualifiers into one string for the transfer; no provider qualifier contains it."""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class TimeSeriesType(NetEnum):
|
|
39
|
+
"""The kinds of series a provider offers (``TimeSeriesDownload.TimeSeriesType``)."""
|
|
40
|
+
|
|
41
|
+
DAILY_DISCHARGE = "DailyDischarge"
|
|
42
|
+
DAILY_STAGE = "DailyStage"
|
|
43
|
+
DAILY_PRECIPITATION = "DailyPrecipitation"
|
|
44
|
+
DAILY_SNOW = "DailySnow"
|
|
45
|
+
INSTANTANEOUS_DISCHARGE = "InstantaneousDischarge"
|
|
46
|
+
INSTANTANEOUS_STAGE = "InstantaneousStage"
|
|
47
|
+
PEAK_DISCHARGE = "PeakDischarge"
|
|
48
|
+
PEAK_STAGE = "PeakStage"
|
|
49
|
+
MEASURED_DISCHARGE = "MeasuredDischarge"
|
|
50
|
+
MEASURED_STAGE = "MeasuredStage"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class DepthUnit(NetEnum):
|
|
54
|
+
"""Depth units for precipitation and snowfall (``TimeSeriesDownload.DepthUnit``)."""
|
|
55
|
+
|
|
56
|
+
MILLIMETERS = "Millimeters"
|
|
57
|
+
CENTIMETERS = "Centimeters"
|
|
58
|
+
INCHES = "Inches"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class DischargeUnit(NetEnum):
|
|
62
|
+
"""Discharge units (``TimeSeriesDownload.DischargeUnit``)."""
|
|
63
|
+
|
|
64
|
+
CUBIC_FEET_PER_SECOND = "CubicFeetPerSecond"
|
|
65
|
+
CUBIC_METERS_PER_SECOND = "CubicMetersPerSecond"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class HeightUnit(NetEnum):
|
|
69
|
+
"""Stage (gage height) units (``TimeSeriesDownload.HeightUnit``)."""
|
|
70
|
+
|
|
71
|
+
FEET = "Feet"
|
|
72
|
+
METERS = "Meters"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class UsgsStatistic(NetEnum):
|
|
76
|
+
"""The daily statistic of a USGS daily series (``TimeSeriesDownload.UsgsStatistic``)."""
|
|
77
|
+
|
|
78
|
+
MEAN = "Mean"
|
|
79
|
+
MAXIMUM = "Maximum"
|
|
80
|
+
MINIMUM = "Minimum"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@dataclass(frozen=True)
|
|
84
|
+
class DownloadedSeries:
|
|
85
|
+
"""A downloaded or parsed series with its raw text and qualifiers.
|
|
86
|
+
|
|
87
|
+
Parameters
|
|
88
|
+
----------
|
|
89
|
+
series : TimeSeries
|
|
90
|
+
The series, wrapping the ``Numerics.Data.TimeSeries`` Numerics returned.
|
|
91
|
+
raw_text : str or None
|
|
92
|
+
The text the service returned, after ``#`` comment lines naming the source, each request and the
|
|
93
|
+
retrieval time (USGS downloads only); None otherwise.
|
|
94
|
+
qualifiers : tuple of str or None
|
|
95
|
+
With ``qualifiers=True``: one entry per value, in series order (USGS qualifier field, GHCN day
|
|
96
|
+
flags, Canadian data symbol, BOM quality code; empty for a day filled as missing). None otherwise.
|
|
97
|
+
utc : bool
|
|
98
|
+
True when the dates are UTC (USGS instantaneous values and field measurements, Canadian
|
|
99
|
+
real-time values); otherwise the dates are calendar dates or the provider's local time.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
series: TimeSeries
|
|
103
|
+
raw_text: str | None
|
|
104
|
+
qualifiers: tuple[str, ...] | None
|
|
105
|
+
utc: bool
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def dates(self) -> np.ndarray:
|
|
109
|
+
"""The dates as ``datetime64[us]`` (see :attr:`utc` for their time zone)."""
|
|
110
|
+
return self.series.dates
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def values(self) -> np.ndarray:
|
|
114
|
+
"""The values; NaN marks a missing value."""
|
|
115
|
+
return self.series.values
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _download() -> Any:
|
|
119
|
+
"""Return ``Numerics.Data.TimeSeriesDownload`` after starting the runtime."""
|
|
120
|
+
return namespace("Numerics.Data").TimeSeriesDownload
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _enum(name: str, value: Any) -> Any:
|
|
124
|
+
"""Return a member of a ``TimeSeriesDownload`` nested enum."""
|
|
125
|
+
return net_enum(getattr(_download(), name), value)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _date(value: Any) -> Any:
|
|
129
|
+
"""Return a date as a ``System.DateTime``, or None."""
|
|
130
|
+
return None if value is None else _net_date(value)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _qualifier_list(qualifiers: bool) -> Any:
|
|
134
|
+
"""Return a new .NET ``List<string>`` for the qualifier overloads, or None."""
|
|
135
|
+
if not qualifiers:
|
|
136
|
+
return None
|
|
137
|
+
from System import String # noqa: PLC0415 - the runtime is running
|
|
138
|
+
from System.Collections.Generic import List # noqa: PLC0415
|
|
139
|
+
|
|
140
|
+
return List[String]()
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _result(net: Any, raw_text: Any = None, qualifiers: Any = None) -> DownloadedSeries:
|
|
144
|
+
"""Wrap a Numerics series, copying the qualifiers in one transfer."""
|
|
145
|
+
import System # noqa: PLC0415 - the runtime is running
|
|
146
|
+
|
|
147
|
+
codes = None
|
|
148
|
+
if qualifiers is not None:
|
|
149
|
+
joined = str(System.String.Join(_SEPARATOR, qualifiers))
|
|
150
|
+
codes = tuple(joined.split(_SEPARATOR)) if int(qualifiers.Count) > 0 else ()
|
|
151
|
+
utc = int(net.Count) > 0 and net[0].Index.Kind == System.DateTimeKind.Utc
|
|
152
|
+
return DownloadedSeries(TimeSeries.from_net(net), None if raw_text is None else str(raw_text), codes, bool(utc))
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _run(start: Any, timeout: float | None) -> Any:
|
|
156
|
+
"""Run a download task to completion and return its result.
|
|
157
|
+
|
|
158
|
+
``start`` takes a ``CancellationToken`` and returns the task. With a ``timeout`` the token is
|
|
159
|
+
cancelled after that many seconds. The task is awaited with ``GetAwaiter().GetResult()``, so a
|
|
160
|
+
failure surfaces as the .NET exception itself, not an ``AggregateException``; the wait polls, so
|
|
161
|
+
Ctrl+C cancels the download.
|
|
162
|
+
"""
|
|
163
|
+
import System # noqa: PLC0415 - the runtime is running
|
|
164
|
+
from System.Net.Http import HttpRequestException # noqa: PLC0415
|
|
165
|
+
from System.Threading import CancellationTokenSource # noqa: PLC0415
|
|
166
|
+
|
|
167
|
+
if timeout is not None and not timeout > 0:
|
|
168
|
+
raise ValueError("timeout must be positive.")
|
|
169
|
+
source = CancellationTokenSource()
|
|
170
|
+
try:
|
|
171
|
+
if timeout is not None:
|
|
172
|
+
source.CancelAfter(System.TimeSpan.FromSeconds(float(timeout)))
|
|
173
|
+
task = start(source.Token)
|
|
174
|
+
try:
|
|
175
|
+
while not task.IsCompleted:
|
|
176
|
+
time.sleep(0.01)
|
|
177
|
+
except KeyboardInterrupt:
|
|
178
|
+
source.Cancel()
|
|
179
|
+
raise
|
|
180
|
+
try:
|
|
181
|
+
return task.GetAwaiter().GetResult()
|
|
182
|
+
except System.OperationCanceledException as error:
|
|
183
|
+
if source.IsCancellationRequested:
|
|
184
|
+
raise TimeoutError(f"The download did not complete within {timeout} seconds.") from error
|
|
185
|
+
raise
|
|
186
|
+
except System.TimeoutException as error:
|
|
187
|
+
raise TimeoutError(str(error.Message)) from error
|
|
188
|
+
except (HttpRequestException, System.IO.IOException) as error:
|
|
189
|
+
raise ConnectionError(str(error.Message)) from error
|
|
190
|
+
finally:
|
|
191
|
+
source.Dispose()
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def from_usgs(site: str, series_type: TimeSeriesType | str = TimeSeriesType.DAILY_DISCHARGE, *, start: Any = None,
|
|
195
|
+
end: Any = None, statistic: UsgsStatistic | str = UsgsStatistic.MEAN, api_key: str | None = None,
|
|
196
|
+
qualifiers: bool = False, timeout: float | None = None) -> DownloadedSeries:
|
|
197
|
+
"""Download a series from the USGS Water Data API (``TimeSeriesDownload.FromUSGS``).
|
|
198
|
+
|
|
199
|
+
Parameters
|
|
200
|
+
----------
|
|
201
|
+
site : str
|
|
202
|
+
The USGS site number, 8 to 15 digits (for example ``"01646500"``).
|
|
203
|
+
series_type : TimeSeriesType or str, optional
|
|
204
|
+
Daily, instantaneous, peak or measured discharge or stage; default daily discharge.
|
|
205
|
+
start, end : datetime-like, optional
|
|
206
|
+
The inclusive date range; default the period of record. Calendar dates for daily values and
|
|
207
|
+
peaks, UTC for instantaneous values and field measurements.
|
|
208
|
+
statistic : UsgsStatistic or str, optional
|
|
209
|
+
The daily statistic for daily types; default the mean.
|
|
210
|
+
api_key : str, optional
|
|
211
|
+
A USGS Water Data API key for this download; default :func:`get_usgs_api_key`.
|
|
212
|
+
qualifiers : bool, optional
|
|
213
|
+
True to return the qualifier field of each value as well.
|
|
214
|
+
timeout : float, optional
|
|
215
|
+
Seconds before the download is cancelled; default no limit beyond the per-request limits.
|
|
216
|
+
|
|
217
|
+
Returns
|
|
218
|
+
-------
|
|
219
|
+
DownloadedSeries
|
|
220
|
+
The series and the raw text. Daily values form a regular one-day series with NaN for missing
|
|
221
|
+
days; instantaneous values and field measurements are irregular and UTC; peaks are irregular, and
|
|
222
|
+
a peak with a time of day is dated in the site's local standard time.
|
|
223
|
+
|
|
224
|
+
Raises
|
|
225
|
+
------
|
|
226
|
+
ValueError
|
|
227
|
+
For a site number that is not 8 to 15 digits, an unsupported type or ``start`` after ``end``.
|
|
228
|
+
TimeoutError
|
|
229
|
+
When ``timeout`` or a request's time limit is exceeded.
|
|
230
|
+
ConnectionError
|
|
231
|
+
When the service cannot be reached.
|
|
232
|
+
RuntimeError
|
|
233
|
+
When the service returns an error or no data (``"No data found ..."``).
|
|
234
|
+
"""
|
|
235
|
+
net = _download()
|
|
236
|
+
with net_errors():
|
|
237
|
+
options = net.UsgsDownloadOptions()
|
|
238
|
+
options.StartDate = _date(start)
|
|
239
|
+
options.EndDate = _date(end)
|
|
240
|
+
options.Statistic = _enum("UsgsStatistic", statistic)
|
|
241
|
+
options.ApiKey = api_key
|
|
242
|
+
kind = _enum("TimeSeriesType", series_type)
|
|
243
|
+
codes = _qualifier_list(qualifiers)
|
|
244
|
+
if codes is None:
|
|
245
|
+
result = _run(lambda token: net.FromUSGS(site, kind, options, token), timeout)
|
|
246
|
+
else:
|
|
247
|
+
result = _run(lambda token: net.FromUSGS(site, kind, options, codes, token), timeout)
|
|
248
|
+
return _result(result.Item1, result.Item2, codes)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def from_ghcn(site: str, series_type: TimeSeriesType | str = TimeSeriesType.DAILY_PRECIPITATION,
|
|
252
|
+
unit: DepthUnit | str = DepthUnit.INCHES, *, qualifiers: bool = False,
|
|
253
|
+
timeout: float | None = None) -> DownloadedSeries:
|
|
254
|
+
"""Download daily precipitation or snowfall from a GHCN-Daily station file (``TimeSeriesDownload.FromGHCN``).
|
|
255
|
+
|
|
256
|
+
Parameters
|
|
257
|
+
----------
|
|
258
|
+
site : str
|
|
259
|
+
The 11-character GHCN station code (for example ``"USW00094728"``).
|
|
260
|
+
series_type : TimeSeriesType or str, optional
|
|
261
|
+
Daily precipitation (default) or daily snow.
|
|
262
|
+
unit : DepthUnit or str, optional
|
|
263
|
+
The depth unit; default inches.
|
|
264
|
+
qualifiers : bool, optional
|
|
265
|
+
True to return the three flag characters of each day as well.
|
|
266
|
+
timeout : float, optional
|
|
267
|
+
Seconds before the download is cancelled.
|
|
268
|
+
|
|
269
|
+
Returns
|
|
270
|
+
-------
|
|
271
|
+
DownloadedSeries
|
|
272
|
+
A regular one-day series: missing days are NaN and days after today are left out.
|
|
273
|
+
|
|
274
|
+
Raises
|
|
275
|
+
------
|
|
276
|
+
ValueError
|
|
277
|
+
For an invalid station code or type.
|
|
278
|
+
TimeoutError
|
|
279
|
+
When ``timeout`` or the request's time limit is exceeded.
|
|
280
|
+
ConnectionError
|
|
281
|
+
When the service cannot be reached or the station file does not exist.
|
|
282
|
+
RuntimeError
|
|
283
|
+
When the service returns an empty file, an HTML page or a rate-limit response.
|
|
284
|
+
"""
|
|
285
|
+
net = _download()
|
|
286
|
+
with net_errors():
|
|
287
|
+
kind = _enum("TimeSeriesType", series_type)
|
|
288
|
+
depth = _enum("DepthUnit", unit)
|
|
289
|
+
codes = _qualifier_list(qualifiers)
|
|
290
|
+
if codes is None:
|
|
291
|
+
series = _run(lambda token: net.FromGHCN(site, kind, depth, token), timeout)
|
|
292
|
+
else:
|
|
293
|
+
series = _run(lambda token: net.FromGHCN(site, kind, depth, codes, token), timeout)
|
|
294
|
+
return _result(series, None, codes)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def from_chmn(station: str, series_type: TimeSeriesType | str = TimeSeriesType.DAILY_DISCHARGE,
|
|
298
|
+
discharge_unit: DischargeUnit | str = DischargeUnit.CUBIC_METERS_PER_SECOND,
|
|
299
|
+
height_unit: HeightUnit | str = HeightUnit.METERS, *, start: Any = None, end: Any = None,
|
|
300
|
+
qualifiers: bool = False, timeout: float | None = None) -> DownloadedSeries:
|
|
301
|
+
"""Download a series from the Water Survey of Canada (``TimeSeriesDownload.FromCHMN``).
|
|
302
|
+
|
|
303
|
+
Parameters
|
|
304
|
+
----------
|
|
305
|
+
station : str
|
|
306
|
+
The 7-character station id (for example ``"08MF005"``).
|
|
307
|
+
series_type : TimeSeriesType or str, optional
|
|
308
|
+
Daily, instantaneous (real-time) or peak discharge or stage; default daily discharge.
|
|
309
|
+
discharge_unit : DischargeUnit or str, optional
|
|
310
|
+
The discharge unit; default cubic meters per second.
|
|
311
|
+
height_unit : HeightUnit or str, optional
|
|
312
|
+
The stage unit; default meters.
|
|
313
|
+
start, end : datetime-like, optional
|
|
314
|
+
The inclusive date range; default the period of record (the last 18 months for real-time data).
|
|
315
|
+
qualifiers : bool, optional
|
|
316
|
+
True to return the data symbol of each value as well (for example ``"B"`` for ice conditions).
|
|
317
|
+
timeout : float, optional
|
|
318
|
+
Seconds before the download is cancelled.
|
|
319
|
+
|
|
320
|
+
Returns
|
|
321
|
+
-------
|
|
322
|
+
DownloadedSeries
|
|
323
|
+
The series; real-time dates are UTC.
|
|
324
|
+
|
|
325
|
+
Raises
|
|
326
|
+
------
|
|
327
|
+
ValueError
|
|
328
|
+
For a station id that is not 7 characters or an unsupported type.
|
|
329
|
+
TimeoutError
|
|
330
|
+
When ``timeout`` or the request's time limit is exceeded.
|
|
331
|
+
ConnectionError
|
|
332
|
+
When the service cannot be reached.
|
|
333
|
+
RuntimeError
|
|
334
|
+
When the service returns an error, or an empty, header-only or HTML response.
|
|
335
|
+
"""
|
|
336
|
+
net = _download()
|
|
337
|
+
with net_errors():
|
|
338
|
+
kind = _enum("TimeSeriesType", series_type)
|
|
339
|
+
flow = _enum("DischargeUnit", discharge_unit)
|
|
340
|
+
height = _enum("HeightUnit", height_unit)
|
|
341
|
+
first, last = _date(start), _date(end)
|
|
342
|
+
codes = _qualifier_list(qualifiers)
|
|
343
|
+
if codes is None:
|
|
344
|
+
series = _run(lambda token: net.FromCHMN(station, kind, flow, height, first, last, token), timeout)
|
|
345
|
+
else:
|
|
346
|
+
series = _run(lambda token: net.FromCHMN(station, kind, flow, height, first, last, codes, token), timeout)
|
|
347
|
+
return _result(series, None, codes)
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def from_abom(station: str, series_type: TimeSeriesType | str = TimeSeriesType.DAILY_DISCHARGE,
|
|
351
|
+
discharge_unit: DischargeUnit | str = DischargeUnit.CUBIC_METERS_PER_SECOND,
|
|
352
|
+
height_unit: HeightUnit | str = HeightUnit.METERS, depth_unit: DepthUnit | str = DepthUnit.MILLIMETERS, *,
|
|
353
|
+
start: Any = None, end: Any = None, qualifiers: bool = False,
|
|
354
|
+
timeout: float | None = None) -> DownloadedSeries:
|
|
355
|
+
"""Download a series from the BOM Water Data Online service (``TimeSeriesDownload.FromABOM``).
|
|
356
|
+
|
|
357
|
+
Parameters
|
|
358
|
+
----------
|
|
359
|
+
station : str
|
|
360
|
+
The BOM station number (for example ``"410730"``).
|
|
361
|
+
series_type : TimeSeriesType or str, optional
|
|
362
|
+
Daily or instantaneous discharge or stage, or daily precipitation; default daily discharge.
|
|
363
|
+
discharge_unit : DischargeUnit or str, optional
|
|
364
|
+
The discharge unit; default cubic meters per second.
|
|
365
|
+
height_unit : HeightUnit or str, optional
|
|
366
|
+
The stage unit; default meters.
|
|
367
|
+
depth_unit : DepthUnit or str, optional
|
|
368
|
+
The rainfall unit; default millimeters.
|
|
369
|
+
start, end : datetime-like, optional
|
|
370
|
+
The date range; default the period of record to today.
|
|
371
|
+
qualifiers : bool, optional
|
|
372
|
+
True to return the KiWIS quality code of each value as well.
|
|
373
|
+
timeout : float, optional
|
|
374
|
+
Seconds before the download is cancelled.
|
|
375
|
+
|
|
376
|
+
Returns
|
|
377
|
+
-------
|
|
378
|
+
DownloadedSeries
|
|
379
|
+
The series; daily types use the midnight-to-midnight (``.24HR``) series.
|
|
380
|
+
|
|
381
|
+
Raises
|
|
382
|
+
------
|
|
383
|
+
ValueError
|
|
384
|
+
For an invalid station number or an unsupported type.
|
|
385
|
+
TimeoutError
|
|
386
|
+
When ``timeout`` is exceeded.
|
|
387
|
+
RuntimeError
|
|
388
|
+
When the service returns an error or has no matching series.
|
|
389
|
+
"""
|
|
390
|
+
net = _download()
|
|
391
|
+
with net_errors():
|
|
392
|
+
kind = _enum("TimeSeriesType", series_type)
|
|
393
|
+
flow = _enum("DischargeUnit", discharge_unit)
|
|
394
|
+
height = _enum("HeightUnit", height_unit)
|
|
395
|
+
depth = _enum("DepthUnit", depth_unit)
|
|
396
|
+
first, last = _date(start), _date(end)
|
|
397
|
+
codes = _qualifier_list(qualifiers)
|
|
398
|
+
if codes is None:
|
|
399
|
+
series = _run(lambda token: net.FromABOM(station, kind, flow, height, depth, first, last, token), timeout)
|
|
400
|
+
else:
|
|
401
|
+
series = _run(lambda token: net.FromABOM(station, kind, flow, height, depth, first, last, codes, token),
|
|
402
|
+
timeout)
|
|
403
|
+
return _result(series, None, codes)
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def parse_usgs(text: str, series_type: TimeSeriesType | str = TimeSeriesType.DAILY_DISCHARGE,
|
|
407
|
+
peak_standard_offset_hours: float | None = None, *, qualifiers: bool = False) -> DownloadedSeries:
|
|
408
|
+
"""Parse USGS Water Data API CSV text offline (``TimeSeriesDownload.ParseUSGS``).
|
|
409
|
+
|
|
410
|
+
Parameters
|
|
411
|
+
----------
|
|
412
|
+
text : str
|
|
413
|
+
The raw text :func:`from_usgs` returns, or a Water Data API CSV response; ``#`` lines are skipped.
|
|
414
|
+
series_type : TimeSeriesType or str, optional
|
|
415
|
+
The type the text holds; default daily discharge.
|
|
416
|
+
peak_standard_offset_hours : float, optional
|
|
417
|
+
For peaks with a time of day: the standard UTC offset of the site's time zone in hours (for
|
|
418
|
+
example -5 for EST).
|
|
419
|
+
qualifiers : bool, optional
|
|
420
|
+
True to return the qualifier field of each value as well.
|
|
421
|
+
|
|
422
|
+
Returns
|
|
423
|
+
-------
|
|
424
|
+
DownloadedSeries
|
|
425
|
+
The series :func:`from_usgs` would return for the same text.
|
|
426
|
+
|
|
427
|
+
Raises
|
|
428
|
+
------
|
|
429
|
+
ValueError
|
|
430
|
+
For precipitation or snow types, or a timed peak without ``peak_standard_offset_hours``.
|
|
431
|
+
"""
|
|
432
|
+
net = _download()
|
|
433
|
+
import System # noqa: PLC0415 - the runtime is running
|
|
434
|
+
|
|
435
|
+
with net_errors():
|
|
436
|
+
kind = _enum("TimeSeriesType", series_type)
|
|
437
|
+
hours = peak_standard_offset_hours
|
|
438
|
+
offset = None if hours is None else System.TimeSpan.FromHours(float(hours))
|
|
439
|
+
codes = _qualifier_list(qualifiers)
|
|
440
|
+
if codes is None:
|
|
441
|
+
series = net.ParseUSGS(text, kind, offset)
|
|
442
|
+
else:
|
|
443
|
+
series = net.ParseUSGS(text, kind, offset, codes)
|
|
444
|
+
return _result(series, None, codes)
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
def parse_ghcn(text: str, series_type: TimeSeriesType | str = TimeSeriesType.DAILY_PRECIPITATION,
|
|
448
|
+
unit: DepthUnit | str = DepthUnit.INCHES, *, qualifiers: bool = False) -> DownloadedSeries:
|
|
449
|
+
"""Parse a GHCN-Daily station file offline (``TimeSeriesDownload.ParseGHCN``).
|
|
450
|
+
|
|
451
|
+
Parameters
|
|
452
|
+
----------
|
|
453
|
+
text : str
|
|
454
|
+
The ``.dly`` station file text.
|
|
455
|
+
series_type : TimeSeriesType or str, optional
|
|
456
|
+
Daily precipitation (default) or daily snow.
|
|
457
|
+
unit : DepthUnit or str, optional
|
|
458
|
+
The depth unit; default inches. Precipitation is stored in tenths of millimeters and snowfall in
|
|
459
|
+
millimeters.
|
|
460
|
+
qualifiers : bool, optional
|
|
461
|
+
True to return the three flag characters of each day as well.
|
|
462
|
+
|
|
463
|
+
Returns
|
|
464
|
+
-------
|
|
465
|
+
DownloadedSeries
|
|
466
|
+
A regular one-day series with NaN for missing days; days after today are left out.
|
|
467
|
+
|
|
468
|
+
Raises
|
|
469
|
+
------
|
|
470
|
+
ValueError
|
|
471
|
+
For a type other than daily precipitation or snow.
|
|
472
|
+
RuntimeError
|
|
473
|
+
For a record too short to parse (a file cut off mid-line).
|
|
474
|
+
"""
|
|
475
|
+
net = _download()
|
|
476
|
+
with net_errors():
|
|
477
|
+
kind = _enum("TimeSeriesType", series_type)
|
|
478
|
+
depth = _enum("DepthUnit", unit)
|
|
479
|
+
codes = _qualifier_list(qualifiers)
|
|
480
|
+
series = net.ParseGHCN(text, kind, depth) if codes is None else net.ParseGHCN(text, kind, depth, codes)
|
|
481
|
+
return _result(series, None, codes)
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
def parse_chmn(text: str, station: str, series_type: TimeSeriesType | str = TimeSeriesType.DAILY_DISCHARGE,
|
|
485
|
+
discharge_unit: DischargeUnit | str = DischargeUnit.CUBIC_METERS_PER_SECOND,
|
|
486
|
+
height_unit: HeightUnit | str = HeightUnit.METERS, *, qualifiers: bool = False) -> DownloadedSeries:
|
|
487
|
+
"""Parse a Water Survey of Canada CSV response offline (``TimeSeriesDownload.ParseCHMN``).
|
|
488
|
+
|
|
489
|
+
Parameters
|
|
490
|
+
----------
|
|
491
|
+
text : str
|
|
492
|
+
The CSV text of a daily_data, real_time_data or peak_data response.
|
|
493
|
+
station : str
|
|
494
|
+
The 7-character station id; rows of other stations are skipped.
|
|
495
|
+
series_type : TimeSeriesType or str, optional
|
|
496
|
+
Daily, instantaneous (real-time) or peak discharge or stage; default daily discharge.
|
|
497
|
+
discharge_unit : DischargeUnit or str, optional
|
|
498
|
+
The discharge unit; default cubic meters per second.
|
|
499
|
+
height_unit : HeightUnit or str, optional
|
|
500
|
+
The stage unit; default meters.
|
|
501
|
+
qualifiers : bool, optional
|
|
502
|
+
True to return the data symbol of each value as well.
|
|
503
|
+
|
|
504
|
+
Returns
|
|
505
|
+
-------
|
|
506
|
+
DownloadedSeries
|
|
507
|
+
The series :func:`from_chmn` would return for the same text.
|
|
508
|
+
|
|
509
|
+
Raises
|
|
510
|
+
------
|
|
511
|
+
ValueError
|
|
512
|
+
For a station id that is not 7 characters or an unsupported type.
|
|
513
|
+
"""
|
|
514
|
+
net = _download()
|
|
515
|
+
with net_errors():
|
|
516
|
+
kind = _enum("TimeSeriesType", series_type)
|
|
517
|
+
flow = _enum("DischargeUnit", discharge_unit)
|
|
518
|
+
height = _enum("HeightUnit", height_unit)
|
|
519
|
+
codes = _qualifier_list(qualifiers)
|
|
520
|
+
if codes is None:
|
|
521
|
+
series = net.ParseCHMN(text, station, kind, flow, height)
|
|
522
|
+
else:
|
|
523
|
+
series = net.ParseCHMN(text, station, kind, flow, height, codes)
|
|
524
|
+
return _result(series, None, codes)
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
def parse_abom(text: str, series_type: TimeSeriesType | str = TimeSeriesType.DAILY_DISCHARGE,
|
|
528
|
+
discharge_unit: DischargeUnit | str = DischargeUnit.CUBIC_METERS_PER_SECOND,
|
|
529
|
+
height_unit: HeightUnit | str = HeightUnit.METERS, depth_unit: DepthUnit | str = DepthUnit.MILLIMETERS,
|
|
530
|
+
*, qualifiers: bool = False) -> DownloadedSeries:
|
|
531
|
+
"""Parse a BOM KiWIS getTimeseriesValues JSON response offline (``TimeSeriesDownload.ParseABOM``).
|
|
532
|
+
|
|
533
|
+
Parameters
|
|
534
|
+
----------
|
|
535
|
+
text : str
|
|
536
|
+
The JSON response.
|
|
537
|
+
series_type : TimeSeriesType or str, optional
|
|
538
|
+
Daily or instantaneous discharge or stage, or daily precipitation; default daily discharge.
|
|
539
|
+
discharge_unit : DischargeUnit or str, optional
|
|
540
|
+
The discharge unit; default cubic meters per second.
|
|
541
|
+
height_unit : HeightUnit or str, optional
|
|
542
|
+
The stage unit; default meters.
|
|
543
|
+
depth_unit : DepthUnit or str, optional
|
|
544
|
+
The rainfall unit; default millimeters.
|
|
545
|
+
qualifiers : bool, optional
|
|
546
|
+
True to return the quality code of each value as well (empty when the response has no Quality
|
|
547
|
+
Code column).
|
|
548
|
+
|
|
549
|
+
Returns
|
|
550
|
+
-------
|
|
551
|
+
DownloadedSeries
|
|
552
|
+
The series :func:`from_abom` would return for the same response.
|
|
553
|
+
|
|
554
|
+
Raises
|
|
555
|
+
------
|
|
556
|
+
ValueError
|
|
557
|
+
For an unsupported type.
|
|
558
|
+
RuntimeError
|
|
559
|
+
For a KiWIS error response or malformed JSON.
|
|
560
|
+
"""
|
|
561
|
+
net = _download()
|
|
562
|
+
with net_errors():
|
|
563
|
+
kind = _enum("TimeSeriesType", series_type)
|
|
564
|
+
flow = _enum("DischargeUnit", discharge_unit)
|
|
565
|
+
height = _enum("HeightUnit", height_unit)
|
|
566
|
+
depth = _enum("DepthUnit", depth_unit)
|
|
567
|
+
codes = _qualifier_list(qualifiers)
|
|
568
|
+
if codes is None:
|
|
569
|
+
series = net.ParseABOM(text, kind, flow, height, depth)
|
|
570
|
+
else:
|
|
571
|
+
series = net.ParseABOM(text, kind, flow, height, depth, codes)
|
|
572
|
+
return _result(series, None, codes)
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
def is_connected_to_internet(timeout: float | None = None) -> bool:
|
|
576
|
+
"""Return whether at least one provider endpoint answers (``TimeSeriesDownload.IsConnectedToInternet``).
|
|
577
|
+
|
|
578
|
+
Parameters
|
|
579
|
+
----------
|
|
580
|
+
timeout : float, optional
|
|
581
|
+
Seconds to wait; the probe has its own two-second limit per endpoint.
|
|
582
|
+
|
|
583
|
+
Returns
|
|
584
|
+
-------
|
|
585
|
+
bool
|
|
586
|
+
True when any provider endpoint completes an HTTP request. Downloads do not need this check.
|
|
587
|
+
"""
|
|
588
|
+
net = _download()
|
|
589
|
+
with net_errors():
|
|
590
|
+
return bool(_run(lambda _token: net.IsConnectedToInternet(), timeout))
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
def get_usgs_api_key() -> str | None:
|
|
594
|
+
"""Return the default USGS Water Data API key (``TimeSeriesDownload.UsgsApiKey``).
|
|
595
|
+
|
|
596
|
+
Returns
|
|
597
|
+
-------
|
|
598
|
+
str or None
|
|
599
|
+
The key sent in the ``X-Api-Key`` header when a download gives none, or None.
|
|
600
|
+
"""
|
|
601
|
+
key = _download().UsgsApiKey
|
|
602
|
+
return None if key is None else str(key)
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
def set_usgs_api_key(key: str | None) -> None:
|
|
606
|
+
"""Set the default USGS Water Data API key (``TimeSeriesDownload.UsgsApiKey``).
|
|
607
|
+
|
|
608
|
+
Parameters
|
|
609
|
+
----------
|
|
610
|
+
key : str or None
|
|
611
|
+
A free key from https://api.waterdata.usgs.gov/signup/, or None for anonymous use. It raises the
|
|
612
|
+
hourly request limit and is never written to the raw text.
|
|
613
|
+
"""
|
|
614
|
+
_download().UsgsApiKey = key
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
__all__ = [
|
|
618
|
+
"DepthUnit", "DischargeUnit", "DownloadedSeries", "HeightUnit", "TimeSeriesType", "UsgsStatistic",
|
|
619
|
+
"from_abom", "from_chmn", "from_ghcn", "from_usgs", "get_usgs_api_key", "is_connected_to_internet",
|
|
620
|
+
"parse_abom", "parse_chmn", "parse_ghcn", "parse_usgs", "set_usgs_api_key",
|
|
621
|
+
]
|
|
622
|
+
|
|
623
|
+
translate_public(globals())
|