usdata 0.22.0__tar.gz → 0.24.0__tar.gz
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.
- {usdata-0.22.0 → usdata-0.24.0}/PKG-INFO +22 -3
- {usdata-0.22.0 → usdata-0.24.0}/README.md +21 -2
- {usdata-0.22.0 → usdata-0.24.0}/pyproject.toml +1 -1
- {usdata-0.22.0 → usdata-0.24.0}/pyproject.toml.orig +1 -1
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/data/registry.yaml +52 -7
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/noaa/coops.py +66 -3
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/noaa/normals.py +7 -4
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/__init__.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/__main__.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/_fetch.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/_files.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/_grib.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/_hurdat2.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/_netcdf.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/_progress.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/_radar.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/cache.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/cache_ops.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/cite.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/cli/__init__.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/cli/app.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/cli/cache.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/cli/cite.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/cli/doctor.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/cli/inspect.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/cli/progress.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/data/nexrad_sites.csv +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/data/places.csv +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/data/places.sources.json +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/doctor.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/inspect.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/manifest.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/mirror.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/models.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/protocols/__init__.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/protocols/erddap.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/protocols/http.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/protocols/listing.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/protocols/s3.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/provenance.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/__init__.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/base.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/fema/__init__.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/fema/declarations.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/http.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/noaa/__init__.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/noaa/coastwatch.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/noaa/gfs.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/noaa/ghcnd.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/noaa/glm.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/noaa/goes.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/noaa/grib_index.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/noaa/gsom.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/noaa/gsoy.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/noaa/hrrr.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/noaa/hurdat2.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/noaa/ibtracs.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/noaa/lcd.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/noaa/mrms.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/noaa/nbm.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/noaa/nexrad.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/noaa/nexrad_level3.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/noaa/nws_vtec.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/noaa/rap.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/noaa/sites.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/noaa/spc.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/noaa/storm_events.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/params.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/usgs/__init__.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/usgs/daily.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/providers/usgs/earthquakes.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/pull.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/py.typed +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/query.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/readers.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/registry.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/selection.py +0 -0
- {usdata-0.22.0 → usdata-0.24.0}/src/usdata/testing.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: usdata
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.24.0
|
|
4
4
|
Summary: Unified Python SDK and CLI for discovering, fetching, and tracking provenance of U.S. public scientific data
|
|
5
5
|
Keywords: noaa,usgs,open-data,scientific-data,provenance,reproducible-research,weather,climate,meteorology
|
|
6
6
|
Author: Jake Van Slyke
|
|
@@ -66,10 +66,29 @@ for pinning inputs across sources and proving later that they have not changed.
|
|
|
66
66
|
pip install "usdata[pandas]"
|
|
67
67
|
usdata search precipitation --location Oklahoma
|
|
68
68
|
usdata fetch noaa:ghcn-daily -p stations=USW00013967 --start 2024-05-06 --end 2024-05-07
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Save the same station query as `dataset.yaml` in your working directory:
|
|
72
|
+
|
|
73
|
+
```yaml
|
|
74
|
+
name: first-station
|
|
75
|
+
sources:
|
|
76
|
+
- dataset: noaa:ghcn-daily
|
|
77
|
+
start: 2024-05-06
|
|
78
|
+
end: 2024-05-07
|
|
79
|
+
params:
|
|
80
|
+
stations: USW00013967
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Then fetch and pin its inputs, and print their citation:
|
|
84
|
+
|
|
85
|
+
```sh
|
|
69
86
|
usdata pull dataset.yaml && usdata cite dataset.yaml
|
|
70
87
|
```
|
|
71
88
|
|
|
72
|
-
The
|
|
89
|
+
The first pull writes `dataset.lock.json`; later pulls restore its pinned assets.
|
|
90
|
+
|
|
91
|
+
From Python, using the same `dataset.yaml`:
|
|
73
92
|
|
|
74
93
|
```python
|
|
75
94
|
from usdata import build_query, fetch, get, pull
|
|
@@ -93,7 +112,7 @@ breaking changes.
|
|
|
93
112
|
| [docs.usdata.dev](https://docs.usdata.dev/) | How to use it: [install](https://docs.usdata.dev/install/), [getting started](https://docs.usdata.dev/getting-started/), guides, dataset notes, and reference |
|
|
94
113
|
| [Severe-weather case study](https://usdata.dev/examples/severe-weather-case-study/) | One tornado, six sources, one manifest and lockfile, ending in a citation |
|
|
95
114
|
|
|
96
|
-
Twenty-
|
|
115
|
+
Twenty-six datasets are available today and twenty more are planned, grouped
|
|
97
116
|
by agency and product family in the [catalog](docs/providers/README.md).
|
|
98
117
|
|
|
99
118
|
## How this compares
|
|
@@ -21,10 +21,29 @@ for pinning inputs across sources and proving later that they have not changed.
|
|
|
21
21
|
pip install "usdata[pandas]"
|
|
22
22
|
usdata search precipitation --location Oklahoma
|
|
23
23
|
usdata fetch noaa:ghcn-daily -p stations=USW00013967 --start 2024-05-06 --end 2024-05-07
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Save the same station query as `dataset.yaml` in your working directory:
|
|
27
|
+
|
|
28
|
+
```yaml
|
|
29
|
+
name: first-station
|
|
30
|
+
sources:
|
|
31
|
+
- dataset: noaa:ghcn-daily
|
|
32
|
+
start: 2024-05-06
|
|
33
|
+
end: 2024-05-07
|
|
34
|
+
params:
|
|
35
|
+
stations: USW00013967
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Then fetch and pin its inputs, and print their citation:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
24
41
|
usdata pull dataset.yaml && usdata cite dataset.yaml
|
|
25
42
|
```
|
|
26
43
|
|
|
27
|
-
The
|
|
44
|
+
The first pull writes `dataset.lock.json`; later pulls restore its pinned assets.
|
|
45
|
+
|
|
46
|
+
From Python, using the same `dataset.yaml`:
|
|
28
47
|
|
|
29
48
|
```python
|
|
30
49
|
from usdata import build_query, fetch, get, pull
|
|
@@ -48,7 +67,7 @@ breaking changes.
|
|
|
48
67
|
| [docs.usdata.dev](https://docs.usdata.dev/) | How to use it: [install](https://docs.usdata.dev/install/), [getting started](https://docs.usdata.dev/getting-started/), guides, dataset notes, and reference |
|
|
49
68
|
| [Severe-weather case study](https://usdata.dev/examples/severe-weather-case-study/) | One tornado, six sources, one manifest and lockfile, ending in a citation |
|
|
50
69
|
|
|
51
|
-
Twenty-
|
|
70
|
+
Twenty-six datasets are available today and twenty more are planned, grouped
|
|
52
71
|
by agency and product family in the [catalog](docs/providers/README.md).
|
|
53
72
|
|
|
54
73
|
## How this compares
|
|
@@ -641,6 +641,47 @@ datasets:
|
|
|
641
641
|
limits: { max_window: P366D }
|
|
642
642
|
adapter: usdata.providers.noaa.coops:CoopsTidePredictions
|
|
643
643
|
|
|
644
|
+
- id: noaa:coops-currents
|
|
645
|
+
provider: noaa
|
|
646
|
+
status: available
|
|
647
|
+
system: noaa:coops
|
|
648
|
+
domain: ocean-physics
|
|
649
|
+
since: "0.24"
|
|
650
|
+
title: CO-OPS Observed Currents
|
|
651
|
+
description: >-
|
|
652
|
+
Native six-minute current speed and direction from the anonymous CO-OPS
|
|
653
|
+
Data API. Select one alphanumeric station, a positive bin, units, and a
|
|
654
|
+
UTC interval of at most 28 days. Raw CSV preserves gaps and blank values.
|
|
655
|
+
Bin depths can change between deployments; the CSV carries bin number,
|
|
656
|
+
not depth or a verification-quality flag. Predictions, all-bin queries,
|
|
657
|
+
station discovery, and resampling are not included.
|
|
658
|
+
keywords: [currents, current speed, current direction, coastal, ocean, coops, ports, stations, bin]
|
|
659
|
+
protocol: http
|
|
660
|
+
homepage: https://api.tidesandcurrents.noaa.gov/api/prod/
|
|
661
|
+
license: US Government Work (public domain)
|
|
662
|
+
capabilities: { spatial_subset: false, temporal_subset: true, variable_subset: false }
|
|
663
|
+
summary: Coastal current speed and direction
|
|
664
|
+
formats: [CSV]
|
|
665
|
+
selection: Native six-minute observations for one station and explicit bin; at most 28 days
|
|
666
|
+
inputs: Alphanumeric station, positive bin, and both minute-aligned timestamps
|
|
667
|
+
reader: pandas
|
|
668
|
+
guide: docs/providers/noaa-coops-currents.md
|
|
669
|
+
examples:
|
|
670
|
+
- examples/coastal-currents/example.ipynb
|
|
671
|
+
resolution:
|
|
672
|
+
spatial: One current-meter station and bin per request; bin depths depend on deployment
|
|
673
|
+
temporal: Six-minute observations
|
|
674
|
+
citation: >-
|
|
675
|
+
NOAA National Ocean Service, CO-OPS observed currents, accessed via usdata
|
|
676
|
+
terms: https://tidesandcurrents.noaa.gov/disclaimers.html
|
|
677
|
+
variables:
|
|
678
|
+
- { name: "Date Time", units: "UTC", description: "Observation time, requested with time_zone=gmt" }
|
|
679
|
+
- { name: "Speed", units: "cm/s", description: "Observed current speed in metric units; knots with english units" }
|
|
680
|
+
- { name: "Direction", units: "degrees", description: "Observed current direction" }
|
|
681
|
+
- { name: "Bin", description: "Requested bin number, not a depth measurement" }
|
|
682
|
+
limits: { max_window: P28D }
|
|
683
|
+
adapter: usdata.providers.noaa.coops:CoopsCurrents
|
|
684
|
+
|
|
644
685
|
- id: noaa:gsom
|
|
645
686
|
provider: noaa
|
|
646
687
|
status: available
|
|
@@ -731,14 +772,15 @@ datasets:
|
|
|
731
772
|
since: "0.11"
|
|
732
773
|
title: U.S. Climate Normals 1991-2020
|
|
733
774
|
description: >-
|
|
734
|
-
|
|
735
|
-
Service datasets normals-daily-1991-2020, normals-monthly-1991-2020,
|
|
736
|
-
normals-annualseasonal-1991-2020, chosen with a period parameter. Dates are optional;
|
|
737
|
-
daily and monthly normals accept a month-day window inside a placeholder year.
|
|
775
|
+
Hourly, daily, monthly, and annual/seasonal 30-year station normals via the NCEI Access Data
|
|
776
|
+
Service datasets normals-hourly-1991-2020, normals-daily-1991-2020, normals-monthly-1991-2020,
|
|
777
|
+
and normals-annualseasonal-1991-2020, chosen with a period parameter. Dates are optional;
|
|
778
|
+
hourly, daily, and monthly normals accept a month-day window inside a placeholder year.
|
|
779
|
+
Hourly labels use local standard time, with no February 29 values. Explicit
|
|
738
780
|
stations or station discovery through the companion search service. Variables are NCEI
|
|
739
781
|
data-type codes and the catalog lists the probed ones; the delivered set is the open NCEI
|
|
740
782
|
normals list.
|
|
741
|
-
keywords: [climate, normals, averages, stations, temperature, precipitation, ncei]
|
|
783
|
+
keywords: [climate, normals, averages, stations, temperature, precipitation, hourly, ncei]
|
|
742
784
|
protocol: http
|
|
743
785
|
homepage: https://www.ncei.noaa.gov/products/land-based-station/us-climate-normals
|
|
744
786
|
license: US Government Work (public domain)
|
|
@@ -747,17 +789,18 @@ datasets:
|
|
|
747
789
|
capabilities: { spatial_subset: false, temporal_subset: true, variable_subset: true }
|
|
748
790
|
summary: 30-year station climate normals
|
|
749
791
|
formats: [CSV]
|
|
750
|
-
selection:
|
|
792
|
+
selection: Hourly, daily, monthly, or annual/seasonal normals per station; optional month-day window except annual/seasonal; hourly returns whole days
|
|
751
793
|
inputs: Station IDs or a geographic query; dates optional
|
|
752
794
|
reader: pandas
|
|
753
795
|
guide: docs/providers/noaa-normals.md
|
|
754
796
|
examples:
|
|
755
797
|
- examples/climate-normals/README.md
|
|
756
798
|
- examples/climate-anomalies/example.ipynb
|
|
799
|
+
- examples/hourly-anomalies/example.ipynb
|
|
757
800
|
resolution:
|
|
758
801
|
spatial: "U.S. land surface stations with 1991-2020 normals coverage"
|
|
759
802
|
temporal: >-
|
|
760
|
-
|
|
803
|
+
Hourly, daily, monthly, or annual and seasonal normals, chosen with the period parameter
|
|
761
804
|
update_frequency: "As needed; the normals are republished about once a decade"
|
|
762
805
|
citation: >-
|
|
763
806
|
Palecki, Michael; Durre, Imke; Applequist, Scott; Arguez, Anthony; Lawrimore, Jay (2021).
|
|
@@ -765,6 +808,7 @@ datasets:
|
|
|
765
808
|
Information; cite the record for the period used
|
|
766
809
|
terms: https://www.ncei.noaa.gov/products/land-based-station/us-climate-normals
|
|
767
810
|
variables:
|
|
811
|
+
- { name: "HLY-TEMP-NORMAL", units: "degrees Celsius", description: "Hourly normal mean temperature (metric units); local standard time" }
|
|
768
812
|
- { name: "MLY-TMAX-NORMAL", units: "degrees Celsius", description: "Monthly normal maximum temperature" }
|
|
769
813
|
- { name: "MLY-TMIN-NORMAL", units: "degrees Celsius", description: "Monthly normal minimum temperature" }
|
|
770
814
|
- { name: "MLY-PRCP-NORMAL", units: "mm", description: "Monthly normal precipitation total" }
|
|
@@ -1144,6 +1188,7 @@ datasets:
|
|
|
1144
1188
|
guide: docs/providers/noaa-lcd.md
|
|
1145
1189
|
examples:
|
|
1146
1190
|
- examples/hourly-observations/README.md
|
|
1191
|
+
- examples/hourly-anomalies/example.ipynb
|
|
1147
1192
|
resolution:
|
|
1148
1193
|
spatial: "Airport and first-order stations, addressed by eleven-digit station id"
|
|
1149
1194
|
temporal: "Hourly, special, and synoptic reports, plus daily and monthly summaries"
|
|
@@ -1,17 +1,21 @@
|
|
|
1
|
-
"""CO-OPS observed water levels and tide predictions as raw CSV.
|
|
1
|
+
"""CO-OPS observed water levels, currents, and tide predictions as raw CSV.
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Water levels and tide predictions share one station model: one seven-digit ``station``, an explicit
|
|
4
4
|
``datum``, optional ``units`` (metric or english), and both timestamps at minute
|
|
5
5
|
precision, requested in UTC; a bare end date means 23:59 on that day.
|
|
6
6
|
Observations span at most 28 days; predictions
|
|
7
7
|
span at most a year on any ``interval`` (six-minute by default, another minute
|
|
8
8
|
step, hourly, or high/low), within NOAA's own limits.
|
|
9
|
+
Currents instead name an alphanumeric station and explicit positive bin, with
|
|
10
|
+
no datum or interval selector. Their native six-minute observations share the
|
|
11
|
+
28-day window and use cm/s (metric) or knots (english).
|
|
9
12
|
"""
|
|
10
13
|
|
|
11
14
|
from __future__ import annotations
|
|
12
15
|
|
|
13
16
|
import csv
|
|
14
17
|
import hashlib
|
|
18
|
+
import math
|
|
15
19
|
from collections.abc import Iterator
|
|
16
20
|
from datetime import UTC, datetime, timedelta
|
|
17
21
|
from pathlib import Path
|
|
@@ -25,7 +29,7 @@ from usdata.models import Asset, Protocol, Query, TimeRange
|
|
|
25
29
|
from usdata.protocols import http
|
|
26
30
|
from usdata.providers.base import QueryError
|
|
27
31
|
from usdata.providers.http import HttpProvider
|
|
28
|
-
from usdata.providers.params import choice
|
|
32
|
+
from usdata.providers.params import choice, positive_int
|
|
29
33
|
from usdata.query import LAST_INSTANT
|
|
30
34
|
|
|
31
35
|
DATA_URL = "https://api.tidesandcurrents.noaa.gov/api/prod/datagetter"
|
|
@@ -44,6 +48,7 @@ OBSERVATION_COLUMNS = {
|
|
|
44
48
|
"Quality",
|
|
45
49
|
}
|
|
46
50
|
PREDICTION_COLUMNS = {"Date Time", "Prediction"}
|
|
51
|
+
CURRENTS_COLUMNS = {"Date Time", "Speed", "Direction", "Bin"}
|
|
47
52
|
|
|
48
53
|
|
|
49
54
|
def _rows(path: Path, asset: Asset, required: set[str]) -> Iterator[dict[str, str]]:
|
|
@@ -150,6 +155,29 @@ class CoopsPredictionParams(CoopsParams):
|
|
|
150
155
|
return token
|
|
151
156
|
|
|
152
157
|
|
|
158
|
+
class CoopsCurrentsParams(BaseModel):
|
|
159
|
+
"""One current-meter station and bin; a bin is not a permanent depth."""
|
|
160
|
+
|
|
161
|
+
model_config = ConfigDict(extra="forbid")
|
|
162
|
+
|
|
163
|
+
station: str = Field(
|
|
164
|
+
description="Required alphanumeric CO-OPS currents station id, for example 'cb0102'."
|
|
165
|
+
)
|
|
166
|
+
bin: Annotated[int, positive_int()] = Field(
|
|
167
|
+
description="Required positive bin number; 0 (all bins) is not supported."
|
|
168
|
+
)
|
|
169
|
+
units: Annotated[str, choice("metric", "english")] = Field(
|
|
170
|
+
default="metric", description="metric (default, cm/s) or english (knots)."
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
@field_validator("station", mode="before")
|
|
174
|
+
@classmethod
|
|
175
|
+
def _one_station_id(cls, value: object) -> object:
|
|
176
|
+
if not isinstance(value, str) or not value.isascii() or not value.isalnum():
|
|
177
|
+
raise ValueError("must be an alphanumeric string, for example 'cb0102'")
|
|
178
|
+
return value
|
|
179
|
+
|
|
180
|
+
|
|
153
181
|
class _CoopsStation(HttpProvider):
|
|
154
182
|
"""Shared window, request, and validation rules for CO-OPS station products."""
|
|
155
183
|
|
|
@@ -255,3 +283,38 @@ class CoopsTidePredictions(_CoopsStation):
|
|
|
255
283
|
def _validate(self, path: Path, asset: Asset) -> None:
|
|
256
284
|
interval = httpx.URL(asset.href).params.get("interval", "6")
|
|
257
285
|
_validate_predictions(path, asset, interval)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
class CoopsCurrents(_CoopsStation):
|
|
289
|
+
"""One station's observed current speed and direction at an explicit bin."""
|
|
290
|
+
|
|
291
|
+
product = "currents"
|
|
292
|
+
label = "current"
|
|
293
|
+
params_model = CoopsCurrentsParams
|
|
294
|
+
|
|
295
|
+
def list_assets(self, query: Query) -> list[Asset]:
|
|
296
|
+
"""Describe one native-interval CSV request; check availability on fetch."""
|
|
297
|
+
params = self.parse_params(query, CoopsCurrentsParams)
|
|
298
|
+
self.reject(query, "bbox", "text", "variables", hint="CO-OPS requires an explicit station")
|
|
299
|
+
start, end = self._window(query, MAX_INTERVAL)
|
|
300
|
+
request = {"bin": str(params.bin), "units": params.units}
|
|
301
|
+
return [self._asset(params.station, start, end, request)]
|
|
302
|
+
|
|
303
|
+
def _validate(self, path: Path, asset: Asset) -> None:
|
|
304
|
+
expected_bin = int(httpx.URL(asset.href).params["bin"])
|
|
305
|
+
count = 0
|
|
306
|
+
for row in _rows(path, asset, CURRENTS_COLUMNS):
|
|
307
|
+
token = row["Bin"]
|
|
308
|
+
if not token.isascii() or not token.isdigit() or int(token) != expected_bin:
|
|
309
|
+
raise ValueError("record does not match the requested bin")
|
|
310
|
+
# Preserve blank measurements and missing timestamps, not invented values.
|
|
311
|
+
for field in ("Speed", "Direction"):
|
|
312
|
+
if row[field]:
|
|
313
|
+
value = float(row[field])
|
|
314
|
+
if not math.isfinite(value) or value < 0:
|
|
315
|
+
raise ValueError(f"invalid current {field.lower()}")
|
|
316
|
+
if field == "Direction" and value > 360:
|
|
317
|
+
raise ValueError("current direction must be between 0 and 360 degrees")
|
|
318
|
+
count += 1
|
|
319
|
+
if not count:
|
|
320
|
+
raise ValueError("no current observations returned")
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
"""U.S. Climate Normals 1991-2020 through the NCEI Access Data Service.
|
|
2
2
|
|
|
3
|
-
Params: ``period`` (``monthly`` by default, ``daily``, or ``
|
|
3
|
+
Params: ``period`` (``monthly`` by default, ``daily``, ``annualseasonal``, or ``hourly``),
|
|
4
4
|
``stations`` (list or comma string), and ``units`` (metric or standard).
|
|
5
5
|
|
|
6
|
-
Normals are 30-year averages, not observations, so dates are optional. For
|
|
7
|
-
and monthly normals an optional ``start``/``end`` pair selects a calendar window
|
|
6
|
+
Normals are 30-year averages, not observations, so dates are optional. For hourly,
|
|
7
|
+
daily, and monthly normals an optional ``start``/``end`` pair selects a calendar window
|
|
8
8
|
by month and day; the year is ignored and sent as the placeholder 2020, a leap
|
|
9
9
|
year so February 29 is valid. Windows cannot cross the new year. Without dates
|
|
10
10
|
the whole year is requested. Annual/seasonal normals accept no dates. Assets
|
|
11
11
|
carry the 1991-2020 normals period as their time bounds.
|
|
12
|
+
Hourly normals return all hours of each selected day, labeled in local standard
|
|
13
|
+
time as ``MM-DDTHH:MM:SS``. They have no February 29 values.
|
|
12
14
|
"""
|
|
13
15
|
|
|
14
16
|
from __future__ import annotations
|
|
@@ -29,6 +31,7 @@ PERIODS = {
|
|
|
29
31
|
"monthly": "normals-monthly-1991-2020",
|
|
30
32
|
"daily": "normals-daily-1991-2020",
|
|
31
33
|
"annualseasonal": "normals-annualseasonal-1991-2020",
|
|
34
|
+
"hourly": "normals-hourly-1991-2020",
|
|
32
35
|
}
|
|
33
36
|
PLACEHOLDER_YEAR = 2020
|
|
34
37
|
NORMALS_PERIOD = TimeRange(
|
|
@@ -41,7 +44,7 @@ class ClimateNormalsParams(GhcnDailyParams):
|
|
|
41
44
|
"""A normals query also names which averaging period it wants."""
|
|
42
45
|
|
|
43
46
|
period: Annotated[str, choice(*PERIODS)] = Field(
|
|
44
|
-
default="monthly", description="monthly (default), daily, or
|
|
47
|
+
default="monthly", description="monthly (default), daily, annualseasonal, or hourly."
|
|
45
48
|
)
|
|
46
49
|
|
|
47
50
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|