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.
Files changed (46) hide show
  1. pynumerics/__init__.py +73 -0
  2. pynumerics/_bin/Microsoft.CodeAnalysis.CSharp.dll +0 -0
  3. pynumerics/_bin/Microsoft.CodeAnalysis.LICENSE.txt +27 -0
  4. pynumerics/_bin/Microsoft.CodeAnalysis.dll +0 -0
  5. pynumerics/_bin/Numerics.dll +0 -0
  6. pynumerics/_bin/pynumerics.runtimeconfig.json +15 -0
  7. pynumerics/_coverage.py +3825 -0
  8. pynumerics/_util.py +237 -0
  9. pynumerics/_version.py +40 -0
  10. pynumerics/bootstrap.py +593 -0
  11. pynumerics/callbacks.py +791 -0
  12. pynumerics/copulas.py +910 -0
  13. pynumerics/data.py +2233 -0
  14. pynumerics/differentiation.py +348 -0
  15. pynumerics/distributions.py +4043 -0
  16. pynumerics/download.py +623 -0
  17. pynumerics/enums.py +119 -0
  18. pynumerics/errors.py +245 -0
  19. pynumerics/functions.py +1142 -0
  20. pynumerics/goodness_of_fit.py +699 -0
  21. pynumerics/graphs.py +556 -0
  22. pynumerics/hypothesis_tests.py +445 -0
  23. pynumerics/identity.py +144 -0
  24. pynumerics/integration.py +677 -0
  25. pynumerics/linalg.py +1408 -0
  26. pynumerics/machine_learning.py +1335 -0
  27. pynumerics/marshal.py +733 -0
  28. pynumerics/mathematics.py +1053 -0
  29. pynumerics/mcmc.py +1406 -0
  30. pynumerics/multivariate.py +1539 -0
  31. pynumerics/ode.py +156 -0
  32. pynumerics/optimization.py +1736 -0
  33. pynumerics/probability.py +966 -0
  34. pynumerics/py.typed +0 -0
  35. pynumerics/regional.py +522 -0
  36. pynumerics/roots.py +274 -0
  37. pynumerics/runtime.py +384 -0
  38. pynumerics/sampling.py +1758 -0
  39. pynumerics/special.py +1079 -0
  40. pynumerics/statistics.py +1958 -0
  41. pynumerics/timeseries.py +1326 -0
  42. pynumerics-2.3.0.dist-info/METADATA +142 -0
  43. pynumerics-2.3.0.dist-info/RECORD +46 -0
  44. pynumerics-2.3.0.dist-info/WHEEL +5 -0
  45. pynumerics-2.3.0.dist-info/licenses/LICENSE +10 -0
  46. 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())