ksmesopy 0.1.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.
@@ -0,0 +1,242 @@
1
+ Metadata-Version: 2.4
2
+ Name: ksmesopy
3
+ Version: 0.1.0
4
+ Summary: Python API for the Kansas Mesonet
5
+ License: MIT
6
+ Project-URL: Homepage, https://mesonet.k-state.edu
7
+ Project-URL: Repository, https://github.com/<your-username>/ksmesopy
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: numpy>=1.24
11
+ Requires-Dist: pandas>=2.0
12
+ Requires-Dist: matplotlib>=3.7
13
+ Provides-Extra: app
14
+ Requires-Dist: guile; extra == "app"
15
+
16
+ # ksmesopy
17
+
18
+ Python package for downloading and processing data from the [Kansas Mesonet](https://mesonet.k-state.edu), an environmental monitoring network operated by Kansas State University.
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ pip install git+https://github.com/<your-username>/ksmesopy.git
24
+ ```
25
+
26
+ Dependencies: `numpy`, `pandas`, `matplotlib`. The desktop app additionally requires `guile`.
27
+
28
+ ## Quick start
29
+
30
+ ```python
31
+ import ksmesopy as ms
32
+
33
+ # Download daily temperature and precipitation for one station
34
+ df = ms.request_data(
35
+ station="Manhattan",
36
+ start="2024-01-01",
37
+ end="2024-12-31",
38
+ interval="day",
39
+ variables=["TEMP2MAVG", "TEMP2MMIN", "TEMP2MMAX", "PRECIP"],
40
+ )
41
+
42
+ # Optional: rename columns to snake_case
43
+ df = ms.rename_columns(df)
44
+ # TIMESTAMP → timestamp, TEMP2MAVG → t2m, PRECIP → precip, …
45
+ ```
46
+
47
+ ## Desktop app
48
+
49
+ ```bash
50
+ python ksmesoapp.py
51
+ ```
52
+
53
+ A GUI for selecting stations, date ranges, variables, and intervals, with a tabular view and time-series chart. Exports to CSV and PNG.
54
+
55
+ ---
56
+
57
+ ## API reference
58
+
59
+ ### Stations
60
+
61
+ | Function | Returns | Description |
62
+ |---|---|---|
63
+ | `get_stations()` | `DataFrame` | Full station metadata table |
64
+ | `get_stations(names_only=True)` | `list[str]` | Sorted list of station names only |
65
+ | `get_stations_active()` | `DataFrame` | Availability table: `STATION`, `OBS_INTERVAL` (s), `START`, `END` |
66
+
67
+ ### Data retrieval
68
+
69
+ | Function | Returns | Description |
70
+ |---|---|---|
71
+ | `request_data(station, start, end, interval, variables, *, verbose, sleep)` | `DataFrame` | Download data for one station. `interval` is `"day"`, `"hour"`, or `"5min"`. Daily timestamps are corrected from the Mesonet's next-day convention. |
72
+ | `request_data_multi(stations, start, end, interval, variables, *, verbose, sleep)` | `dict[str, DataFrame]` | Same as above for a list of stations; returns one DataFrame per station. |
73
+ | `list_variables(interval=None)` | `list[dict]` | Variable catalogue filtered by interval, or all variables if `None`. Each entry has keys `api_name`, `snake_name`, `description`, `intervals`. |
74
+ | `rename_columns(df, preset="snake")` | `DataFrame` | Rename API column names to snake_case (e.g. `TEMP2MAVG` → `t2m`). Pass a `dict` for a custom mapping. |
75
+
76
+ ### Soil processing
77
+
78
+ | Function | Returns | Description |
79
+ |---|---|---|
80
+ | `calibrate_vwc(df, vwc_cols=None)` | `DataFrame` | Replace firmware VWC values with the KSU site-specific calibration. Fetch `SOILKA*CM` and `SOILEC*CM` alongside `VWC*CM` in the same `request_data` call. Works on any subset of depths. |
81
+ | `compute_soil_water_storage(df)` | `DataFrame` | Trapezoidal soil water storage in the top 50 cm (mm). Requires all four VWC depths; adds a `STORAGE_MM` column. Call `calibrate_vwc()` first for calibrated storage. |
82
+
83
+ ### Derived variables
84
+
85
+ | Function | Returns | Description |
86
+ |---|---|---|
87
+ | `growing_degree_days(tmin, tmax, base=10.0, ceiling=30.0)` | `ndarray` | Daily GDD. Both tmin/tmax clipped to `[base, ceiling]` before averaging. |
88
+ | `heat_index(temp, rh)` | `ndarray` | NOAA/NWS apparent temperature (°C). |
89
+ | `wind_chill(temp, wspd)` | `ndarray` | NWS wind chill (°C); valid for temp ≤ 10 °C, wind ≥ 1.3 m s⁻¹. |
90
+ | `temperature_humidity_index(temp, rh)` | `ndarray` | THI for livestock heat stress. Thresholds (dairy): <68 none · 68–72 mild · 72–80 moderate · 80–90 severe · >90 dangerous. |
91
+
92
+ ### Reference evapotranspiration
93
+
94
+ | Function | Returns | Description |
95
+ |---|---|---|
96
+ | `reference_et_penman_monteith(doy, lat, elev, tmin, tmax, srad, wspd, rhmin, rhmax, *, vpd, ea, wind_height)` | `(ETo, Ra)` | FAO-56 Penman-Monteith. Supply vapour pressure via `ea=`, `vpd=`, or `rhmin=`+`rhmax=`. `srad` in W m⁻², converted internally. |
97
+ | `reference_et_hargreaves(doy, lat, tmin, tmax, *, tmean)` | `(ETo, Ra)` | Hargreaves–Samani. Temperature only — no humidity, radiation, or wind needed. |
98
+
99
+ Both return `(ETo [mm day⁻¹], Ra [MJ m⁻² day⁻¹])`.
100
+
101
+ ### Atmospheric helpers
102
+
103
+ | Function | Output |
104
+ |---|---|
105
+ | `saturation_vapor_pressure(temp)` | es (kPa) |
106
+ | `actual_vapor_pressure(temp, rh)` | ea (kPa) |
107
+ | `vapor_pressure_deficit(temp, rh)` | VPD (kPa, ≥ 0) |
108
+ | `slope_saturation_vapor_pressure(temp)` | Δ (kPa °C⁻¹) |
109
+ | `atmospheric_pressure(elev)` | P (kPa) |
110
+ | `psychrometric_constant(elev)` | γ (kPa °C⁻¹) |
111
+ | `extraterrestrial_radiation(doy, lat)` | Ra (MJ m⁻² day⁻¹) |
112
+ | `net_radiation(srad_mj, tmin, tmax, ea, elev, doy, lat)` | Rn (MJ m⁻² day⁻¹) |
113
+ | `srad_to_mj(srad, period)` | energy (MJ m⁻²) |
114
+
115
+ All functions accept scalars or NumPy arrays.
116
+
117
+ ### Charts
118
+
119
+ Each function draws onto a Matplotlib `Axes` supplied by the caller, so panels compose freely inside any figure layout. All functions accept API column names (`TEMP2MAVG`) or snake_case names (`t2m`) interchangeably, and return the axes they drew on so they can be further customised.
120
+
121
+ ```python
122
+ import matplotlib.pyplot as plt
123
+ import ksmesopy as ms
124
+
125
+ fig, axes = plt.subplots(5, 1, sharex=True, figsize=(12, 14))
126
+
127
+ ms.plot_temperature(axes[0], df, ["TEMP2MAVG", "TEMP2MMIN", "TEMP2MMAX"])
128
+ ms.plot_precip(axes[1], df, "PRECIP")
129
+ ms.plot_humidity(axes[2], df, "RELHUM2MAVG")
130
+ ms.plot_solar_radiation(axes[3], df, ["SRAVG", "Ra"]) # Ra drawn dashed
131
+ ms.plot_vwc(axes[4], df) # auto-detects VWC columns
132
+
133
+ plt.tight_layout()
134
+ plt.savefig("meteogram.png", dpi=150)
135
+ ```
136
+
137
+ plt.tight_layout()
138
+ plt.savefig("meteogram.png", dpi=150)
139
+ ```
140
+
141
+ All functions accept API column names (`TEMP2MAVG`) or snake_case names (`t2m`) interchangeably, and return the axes they drew on so they can be further customised.
142
+
143
+ | Function | Key behaviour |
144
+ |---|---|
145
+ | `plot_temperature(ax, df, variables, *, band, ylabel, legend)` | Shaded band when min/avg/max triplet detected (`band=True`); plain lines otherwise. Works for air and soil temperature. |
146
+ | `plot_precip(ax, df, variable, *, ylabel, color)` | Bar chart, bar width inferred from timestamp spacing. |
147
+ | `plot_humidity(ax, df, variables, *, ylabel, legend)` | Lines, y-axis fixed 0–100 %. |
148
+ | `plot_vpd(ax, df, variables, *, ylabel, legend)` | Filled area + line, y-axis starts at 0. |
149
+ | `plot_solar_radiation(ax, df, variables, *, ylabel, legend)` | Filled area for observed; dashed line for Ra columns (detected by name). |
150
+ | `plot_wind(ax, df, speed, direction, *, ylabel, legend)` | Speed as line; direction overlaid as scatter on a twin y-axis with N/E/S/W ticks. |
151
+ | `plot_vwc(ax, df, variables, *, ylabel, legend)` | Sequential colormap shallow→deep; auto-detects VWC columns if `variables=None`. |
152
+ | `plot_et(ax, df, variables, *, bar, ylabel, legend)` | Line by default; `bar=True` for daily totals. |
153
+
154
+ ---
155
+
156
+ ## Variable catalogue
157
+
158
+ Intervals: **D** = daily only · **H** = hourly and daily · **A** = 5-min, hourly, and daily
159
+
160
+ ### Atmospheric
161
+
162
+ | API name | snake_case | Description | Unit | Intervals |
163
+ |---|---|---|---|---|
164
+ | `TEMP2MAVG` | `t2m` | Air temperature 2 m avg | °C | A |
165
+ | `TEMP2MMIN` | `t2m_min` | Air temperature 2 m min | °C | D |
166
+ | `TEMP2MMAX` | `t2m_max` | Air temperature 2 m max | °C | D |
167
+ | `TEMP10MAVG` | `t10m` | Air temperature 10 m avg | °C | A |
168
+ | `TEMP10MMIN` | `t10m_min` | Air temperature 10 m min | °C | D |
169
+ | `TEMP10MMAX` | `t10m_max` | Air temperature 10 m max | °C | D |
170
+ | `RELHUM2MAVG` | `rh` | Relative humidity 2 m avg | % | A |
171
+ | `RELHUM2MMIN` | `rh_min` | Relative humidity 2 m min | % | D |
172
+ | `RELHUM2MMAX` | `rh_max` | Relative humidity 2 m max | % | D |
173
+ | `VPDEFAVG` | `vpd` | Vapor pressure deficit avg | kPa | A |
174
+ | `PRESSUREAVG` | `pres` | Atmospheric pressure avg | kPa | A |
175
+ | `PRECIP` | `precip` | Precipitation gauge 1 | mm | A |
176
+ | `PRECIP2` | `precip2` | Precipitation gauge 2 | mm | A |
177
+ | `SRAVG` | `srad` | Solar radiation avg | W m⁻² | A |
178
+ | `WSPD2MAVG` | `wspd` | Wind speed 2 m avg | m s⁻¹ | A |
179
+ | `WSPD2MMAX` | `wspd_max` | Wind speed 2 m max | m s⁻¹ | H¹ |
180
+ | `WDIR2M` | `wdir` | Wind direction 2 m | ° | A |
181
+ | `WDIR2MSTD` | `wdir_std` | Wind direction 2 m std dev | ° | A |
182
+ | `WSPD10MAVG` | `wspd10m` | Wind speed 10 m avg | m s⁻¹ | A |
183
+ | `WSPD10MMAX` | `wspd10m_max` | Wind speed 10 m max | m s⁻¹ | H¹ |
184
+ | `WDIR10M` | `wdir10m` | Wind direction 10 m | ° | A |
185
+ | `WDIR10MSTD` | `wdir10m_std` | Wind direction 10 m std dev | ° | A |
186
+
187
+ ¹ Available at 5-min and daily only (not hourly).
188
+
189
+ ### Soil temperature — dedicated probes
190
+
191
+ | API name | snake_case | Description | Unit | Intervals |
192
+ |---|---|---|---|---|
193
+ | `SOILTMP5AVG` | `tsoil_5cm` | Soil temperature 5 cm avg | °C | A |
194
+ | `SOILTMP5MIN` | `tsoil_5cm_min` | Soil temperature 5 cm min | °C | D |
195
+ | `SOILTMP5MAX` | `tsoil_5cm_max` | Soil temperature 5 cm max | °C | D |
196
+ | `SOILTMP10AVG` | `tsoil_10cm` | Soil temperature 10 cm avg | °C | A |
197
+ | `SOILTMP10MIN` | `tsoil_10cm_min` | Soil temperature 10 cm min | °C | D |
198
+ | `SOILTMP10MAX` | `tsoil_10cm_max` | Soil temperature 10 cm max | °C | D |
199
+
200
+ ### Soil — CS655 sensors
201
+
202
+ | API name | snake_case | Description | Unit | Intervals |
203
+ |---|---|---|---|---|
204
+ | `SOILTMP5AVG655` | `tsoil_5cm_655` | Soil temperature 5 cm | °C | A |
205
+ | `SOILTMP10AVG655` | `tsoil_10cm_655` | Soil temperature 10 cm | °C | A |
206
+ | `SOILTMP20AVG655` | `tsoil_20cm_655` | Soil temperature 20 cm | °C | A |
207
+ | `SOILTMP50AVG655` | `tsoil_50cm_655` | Soil temperature 50 cm | °C | A |
208
+ | `SOILKA5CM` | `ka_5cm` | Dielectric constant 5 cm | — | A |
209
+ | `SOILKA10CM` | `ka_10cm` | Dielectric constant 10 cm | — | A |
210
+ | `SOILKA20CM` | `ka_20cm` | Dielectric constant 20 cm | — | A |
211
+ | `SOILKA50CM` | `ka_50cm` | Dielectric constant 50 cm | — | A |
212
+ | `SOILEC5CM` | `ec_5cm` | Electrical conductivity 5 cm | dS m⁻¹ | A |
213
+ | `SOILEC10CM` | `ec_10cm` | Electrical conductivity 10 cm | dS m⁻¹ | A |
214
+ | `SOILEC20CM` | `ec_20cm` | Electrical conductivity 20 cm | dS m⁻¹ | A |
215
+ | `SOILEC50CM` | `ec_50cm` | Electrical conductivity 50 cm | dS m⁻¹ | A |
216
+ | `VWC5CM` | `vwc_5cm` | Volumetric water content 5 cm | m³ m⁻³ | A |
217
+ | `VWC10CM` | `vwc_10cm` | Volumetric water content 10 cm | m³ m⁻³ | A |
218
+ | `VWC20CM` | `vwc_20cm` | Volumetric water content 20 cm | m³ m⁻³ | A |
219
+ | `VWC50CM` | `vwc_50cm` | Volumetric water content 50 cm | m³ m⁻³ | A |
220
+
221
+ > **VWC note:** the Mesonet API returns VWC computed by the CS655 firmware equation. Requesting any `VWC*CM` column will print a warning reminding you that a site-specific calibration is available. To apply it, fetch the corresponding `SOILKA*CM` and `SOILEC*CM` columns in the same `request_data` call and then pass the DataFrame to `calibrate_vwc()`. Works for any subset of depths independently.
222
+
223
+ ---
224
+
225
+ ## Notes
226
+
227
+ **Precipitation.** The Mesonet operates dual tipping-bucket rain gauges at most stations. `request_data()` returns both (`PRECIP`, `PRECIP2`); the desktop app merges them to the row-wise maximum automatically. For scripted use, merge manually:
228
+
229
+ ```python
230
+ df["PRECIP"] = df[["PRECIP", "PRECIP2"]].max(axis=1)
231
+ df.drop(columns="PRECIP2", inplace=True)
232
+ ```
233
+
234
+ **Daily timestamps.** The Mesonet API stores each day's aggregated values at 00:00 of the following calendar day. `request_data()` corrects for this automatically; the returned `TIMESTAMP` always reflects the observation date.
235
+
236
+ **Missing values.** The API encodes missing observations as `"M"`. These are converted to `NaN` on read. Periods before a station or sensor was installed are pre-filled with `NaN` rather than omitted.
237
+
238
+ ---
239
+
240
+ ## License
241
+
242
+ MIT
@@ -0,0 +1,227 @@
1
+ # ksmesopy
2
+
3
+ Python package for downloading and processing data from the [Kansas Mesonet](https://mesonet.k-state.edu), an environmental monitoring network operated by Kansas State University.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install git+https://github.com/<your-username>/ksmesopy.git
9
+ ```
10
+
11
+ Dependencies: `numpy`, `pandas`, `matplotlib`. The desktop app additionally requires `guile`.
12
+
13
+ ## Quick start
14
+
15
+ ```python
16
+ import ksmesopy as ms
17
+
18
+ # Download daily temperature and precipitation for one station
19
+ df = ms.request_data(
20
+ station="Manhattan",
21
+ start="2024-01-01",
22
+ end="2024-12-31",
23
+ interval="day",
24
+ variables=["TEMP2MAVG", "TEMP2MMIN", "TEMP2MMAX", "PRECIP"],
25
+ )
26
+
27
+ # Optional: rename columns to snake_case
28
+ df = ms.rename_columns(df)
29
+ # TIMESTAMP → timestamp, TEMP2MAVG → t2m, PRECIP → precip, …
30
+ ```
31
+
32
+ ## Desktop app
33
+
34
+ ```bash
35
+ python ksmesoapp.py
36
+ ```
37
+
38
+ A GUI for selecting stations, date ranges, variables, and intervals, with a tabular view and time-series chart. Exports to CSV and PNG.
39
+
40
+ ---
41
+
42
+ ## API reference
43
+
44
+ ### Stations
45
+
46
+ | Function | Returns | Description |
47
+ |---|---|---|
48
+ | `get_stations()` | `DataFrame` | Full station metadata table |
49
+ | `get_stations(names_only=True)` | `list[str]` | Sorted list of station names only |
50
+ | `get_stations_active()` | `DataFrame` | Availability table: `STATION`, `OBS_INTERVAL` (s), `START`, `END` |
51
+
52
+ ### Data retrieval
53
+
54
+ | Function | Returns | Description |
55
+ |---|---|---|
56
+ | `request_data(station, start, end, interval, variables, *, verbose, sleep)` | `DataFrame` | Download data for one station. `interval` is `"day"`, `"hour"`, or `"5min"`. Daily timestamps are corrected from the Mesonet's next-day convention. |
57
+ | `request_data_multi(stations, start, end, interval, variables, *, verbose, sleep)` | `dict[str, DataFrame]` | Same as above for a list of stations; returns one DataFrame per station. |
58
+ | `list_variables(interval=None)` | `list[dict]` | Variable catalogue filtered by interval, or all variables if `None`. Each entry has keys `api_name`, `snake_name`, `description`, `intervals`. |
59
+ | `rename_columns(df, preset="snake")` | `DataFrame` | Rename API column names to snake_case (e.g. `TEMP2MAVG` → `t2m`). Pass a `dict` for a custom mapping. |
60
+
61
+ ### Soil processing
62
+
63
+ | Function | Returns | Description |
64
+ |---|---|---|
65
+ | `calibrate_vwc(df, vwc_cols=None)` | `DataFrame` | Replace firmware VWC values with the KSU site-specific calibration. Fetch `SOILKA*CM` and `SOILEC*CM` alongside `VWC*CM` in the same `request_data` call. Works on any subset of depths. |
66
+ | `compute_soil_water_storage(df)` | `DataFrame` | Trapezoidal soil water storage in the top 50 cm (mm). Requires all four VWC depths; adds a `STORAGE_MM` column. Call `calibrate_vwc()` first for calibrated storage. |
67
+
68
+ ### Derived variables
69
+
70
+ | Function | Returns | Description |
71
+ |---|---|---|
72
+ | `growing_degree_days(tmin, tmax, base=10.0, ceiling=30.0)` | `ndarray` | Daily GDD. Both tmin/tmax clipped to `[base, ceiling]` before averaging. |
73
+ | `heat_index(temp, rh)` | `ndarray` | NOAA/NWS apparent temperature (°C). |
74
+ | `wind_chill(temp, wspd)` | `ndarray` | NWS wind chill (°C); valid for temp ≤ 10 °C, wind ≥ 1.3 m s⁻¹. |
75
+ | `temperature_humidity_index(temp, rh)` | `ndarray` | THI for livestock heat stress. Thresholds (dairy): <68 none · 68–72 mild · 72–80 moderate · 80–90 severe · >90 dangerous. |
76
+
77
+ ### Reference evapotranspiration
78
+
79
+ | Function | Returns | Description |
80
+ |---|---|---|
81
+ | `reference_et_penman_monteith(doy, lat, elev, tmin, tmax, srad, wspd, rhmin, rhmax, *, vpd, ea, wind_height)` | `(ETo, Ra)` | FAO-56 Penman-Monteith. Supply vapour pressure via `ea=`, `vpd=`, or `rhmin=`+`rhmax=`. `srad` in W m⁻², converted internally. |
82
+ | `reference_et_hargreaves(doy, lat, tmin, tmax, *, tmean)` | `(ETo, Ra)` | Hargreaves–Samani. Temperature only — no humidity, radiation, or wind needed. |
83
+
84
+ Both return `(ETo [mm day⁻¹], Ra [MJ m⁻² day⁻¹])`.
85
+
86
+ ### Atmospheric helpers
87
+
88
+ | Function | Output |
89
+ |---|---|
90
+ | `saturation_vapor_pressure(temp)` | es (kPa) |
91
+ | `actual_vapor_pressure(temp, rh)` | ea (kPa) |
92
+ | `vapor_pressure_deficit(temp, rh)` | VPD (kPa, ≥ 0) |
93
+ | `slope_saturation_vapor_pressure(temp)` | Δ (kPa °C⁻¹) |
94
+ | `atmospheric_pressure(elev)` | P (kPa) |
95
+ | `psychrometric_constant(elev)` | γ (kPa °C⁻¹) |
96
+ | `extraterrestrial_radiation(doy, lat)` | Ra (MJ m⁻² day⁻¹) |
97
+ | `net_radiation(srad_mj, tmin, tmax, ea, elev, doy, lat)` | Rn (MJ m⁻² day⁻¹) |
98
+ | `srad_to_mj(srad, period)` | energy (MJ m⁻²) |
99
+
100
+ All functions accept scalars or NumPy arrays.
101
+
102
+ ### Charts
103
+
104
+ Each function draws onto a Matplotlib `Axes` supplied by the caller, so panels compose freely inside any figure layout. All functions accept API column names (`TEMP2MAVG`) or snake_case names (`t2m`) interchangeably, and return the axes they drew on so they can be further customised.
105
+
106
+ ```python
107
+ import matplotlib.pyplot as plt
108
+ import ksmesopy as ms
109
+
110
+ fig, axes = plt.subplots(5, 1, sharex=True, figsize=(12, 14))
111
+
112
+ ms.plot_temperature(axes[0], df, ["TEMP2MAVG", "TEMP2MMIN", "TEMP2MMAX"])
113
+ ms.plot_precip(axes[1], df, "PRECIP")
114
+ ms.plot_humidity(axes[2], df, "RELHUM2MAVG")
115
+ ms.plot_solar_radiation(axes[3], df, ["SRAVG", "Ra"]) # Ra drawn dashed
116
+ ms.plot_vwc(axes[4], df) # auto-detects VWC columns
117
+
118
+ plt.tight_layout()
119
+ plt.savefig("meteogram.png", dpi=150)
120
+ ```
121
+
122
+ plt.tight_layout()
123
+ plt.savefig("meteogram.png", dpi=150)
124
+ ```
125
+
126
+ All functions accept API column names (`TEMP2MAVG`) or snake_case names (`t2m`) interchangeably, and return the axes they drew on so they can be further customised.
127
+
128
+ | Function | Key behaviour |
129
+ |---|---|
130
+ | `plot_temperature(ax, df, variables, *, band, ylabel, legend)` | Shaded band when min/avg/max triplet detected (`band=True`); plain lines otherwise. Works for air and soil temperature. |
131
+ | `plot_precip(ax, df, variable, *, ylabel, color)` | Bar chart, bar width inferred from timestamp spacing. |
132
+ | `plot_humidity(ax, df, variables, *, ylabel, legend)` | Lines, y-axis fixed 0–100 %. |
133
+ | `plot_vpd(ax, df, variables, *, ylabel, legend)` | Filled area + line, y-axis starts at 0. |
134
+ | `plot_solar_radiation(ax, df, variables, *, ylabel, legend)` | Filled area for observed; dashed line for Ra columns (detected by name). |
135
+ | `plot_wind(ax, df, speed, direction, *, ylabel, legend)` | Speed as line; direction overlaid as scatter on a twin y-axis with N/E/S/W ticks. |
136
+ | `plot_vwc(ax, df, variables, *, ylabel, legend)` | Sequential colormap shallow→deep; auto-detects VWC columns if `variables=None`. |
137
+ | `plot_et(ax, df, variables, *, bar, ylabel, legend)` | Line by default; `bar=True` for daily totals. |
138
+
139
+ ---
140
+
141
+ ## Variable catalogue
142
+
143
+ Intervals: **D** = daily only · **H** = hourly and daily · **A** = 5-min, hourly, and daily
144
+
145
+ ### Atmospheric
146
+
147
+ | API name | snake_case | Description | Unit | Intervals |
148
+ |---|---|---|---|---|
149
+ | `TEMP2MAVG` | `t2m` | Air temperature 2 m avg | °C | A |
150
+ | `TEMP2MMIN` | `t2m_min` | Air temperature 2 m min | °C | D |
151
+ | `TEMP2MMAX` | `t2m_max` | Air temperature 2 m max | °C | D |
152
+ | `TEMP10MAVG` | `t10m` | Air temperature 10 m avg | °C | A |
153
+ | `TEMP10MMIN` | `t10m_min` | Air temperature 10 m min | °C | D |
154
+ | `TEMP10MMAX` | `t10m_max` | Air temperature 10 m max | °C | D |
155
+ | `RELHUM2MAVG` | `rh` | Relative humidity 2 m avg | % | A |
156
+ | `RELHUM2MMIN` | `rh_min` | Relative humidity 2 m min | % | D |
157
+ | `RELHUM2MMAX` | `rh_max` | Relative humidity 2 m max | % | D |
158
+ | `VPDEFAVG` | `vpd` | Vapor pressure deficit avg | kPa | A |
159
+ | `PRESSUREAVG` | `pres` | Atmospheric pressure avg | kPa | A |
160
+ | `PRECIP` | `precip` | Precipitation gauge 1 | mm | A |
161
+ | `PRECIP2` | `precip2` | Precipitation gauge 2 | mm | A |
162
+ | `SRAVG` | `srad` | Solar radiation avg | W m⁻² | A |
163
+ | `WSPD2MAVG` | `wspd` | Wind speed 2 m avg | m s⁻¹ | A |
164
+ | `WSPD2MMAX` | `wspd_max` | Wind speed 2 m max | m s⁻¹ | H¹ |
165
+ | `WDIR2M` | `wdir` | Wind direction 2 m | ° | A |
166
+ | `WDIR2MSTD` | `wdir_std` | Wind direction 2 m std dev | ° | A |
167
+ | `WSPD10MAVG` | `wspd10m` | Wind speed 10 m avg | m s⁻¹ | A |
168
+ | `WSPD10MMAX` | `wspd10m_max` | Wind speed 10 m max | m s⁻¹ | H¹ |
169
+ | `WDIR10M` | `wdir10m` | Wind direction 10 m | ° | A |
170
+ | `WDIR10MSTD` | `wdir10m_std` | Wind direction 10 m std dev | ° | A |
171
+
172
+ ¹ Available at 5-min and daily only (not hourly).
173
+
174
+ ### Soil temperature — dedicated probes
175
+
176
+ | API name | snake_case | Description | Unit | Intervals |
177
+ |---|---|---|---|---|
178
+ | `SOILTMP5AVG` | `tsoil_5cm` | Soil temperature 5 cm avg | °C | A |
179
+ | `SOILTMP5MIN` | `tsoil_5cm_min` | Soil temperature 5 cm min | °C | D |
180
+ | `SOILTMP5MAX` | `tsoil_5cm_max` | Soil temperature 5 cm max | °C | D |
181
+ | `SOILTMP10AVG` | `tsoil_10cm` | Soil temperature 10 cm avg | °C | A |
182
+ | `SOILTMP10MIN` | `tsoil_10cm_min` | Soil temperature 10 cm min | °C | D |
183
+ | `SOILTMP10MAX` | `tsoil_10cm_max` | Soil temperature 10 cm max | °C | D |
184
+
185
+ ### Soil — CS655 sensors
186
+
187
+ | API name | snake_case | Description | Unit | Intervals |
188
+ |---|---|---|---|---|
189
+ | `SOILTMP5AVG655` | `tsoil_5cm_655` | Soil temperature 5 cm | °C | A |
190
+ | `SOILTMP10AVG655` | `tsoil_10cm_655` | Soil temperature 10 cm | °C | A |
191
+ | `SOILTMP20AVG655` | `tsoil_20cm_655` | Soil temperature 20 cm | °C | A |
192
+ | `SOILTMP50AVG655` | `tsoil_50cm_655` | Soil temperature 50 cm | °C | A |
193
+ | `SOILKA5CM` | `ka_5cm` | Dielectric constant 5 cm | — | A |
194
+ | `SOILKA10CM` | `ka_10cm` | Dielectric constant 10 cm | — | A |
195
+ | `SOILKA20CM` | `ka_20cm` | Dielectric constant 20 cm | — | A |
196
+ | `SOILKA50CM` | `ka_50cm` | Dielectric constant 50 cm | — | A |
197
+ | `SOILEC5CM` | `ec_5cm` | Electrical conductivity 5 cm | dS m⁻¹ | A |
198
+ | `SOILEC10CM` | `ec_10cm` | Electrical conductivity 10 cm | dS m⁻¹ | A |
199
+ | `SOILEC20CM` | `ec_20cm` | Electrical conductivity 20 cm | dS m⁻¹ | A |
200
+ | `SOILEC50CM` | `ec_50cm` | Electrical conductivity 50 cm | dS m⁻¹ | A |
201
+ | `VWC5CM` | `vwc_5cm` | Volumetric water content 5 cm | m³ m⁻³ | A |
202
+ | `VWC10CM` | `vwc_10cm` | Volumetric water content 10 cm | m³ m⁻³ | A |
203
+ | `VWC20CM` | `vwc_20cm` | Volumetric water content 20 cm | m³ m⁻³ | A |
204
+ | `VWC50CM` | `vwc_50cm` | Volumetric water content 50 cm | m³ m⁻³ | A |
205
+
206
+ > **VWC note:** the Mesonet API returns VWC computed by the CS655 firmware equation. Requesting any `VWC*CM` column will print a warning reminding you that a site-specific calibration is available. To apply it, fetch the corresponding `SOILKA*CM` and `SOILEC*CM` columns in the same `request_data` call and then pass the DataFrame to `calibrate_vwc()`. Works for any subset of depths independently.
207
+
208
+ ---
209
+
210
+ ## Notes
211
+
212
+ **Precipitation.** The Mesonet operates dual tipping-bucket rain gauges at most stations. `request_data()` returns both (`PRECIP`, `PRECIP2`); the desktop app merges them to the row-wise maximum automatically. For scripted use, merge manually:
213
+
214
+ ```python
215
+ df["PRECIP"] = df[["PRECIP", "PRECIP2"]].max(axis=1)
216
+ df.drop(columns="PRECIP2", inplace=True)
217
+ ```
218
+
219
+ **Daily timestamps.** The Mesonet API stores each day's aggregated values at 00:00 of the following calendar day. `request_data()` corrects for this automatically; the returned `TIMESTAMP` always reflects the observation date.
220
+
221
+ **Missing values.** The API encodes missing observations as `"M"`. These are converted to `NaN` on read. Periods before a station or sensor was installed are pre-filled with `NaN` rather than omitted.
222
+
223
+ ---
224
+
225
+ ## License
226
+
227
+ MIT
@@ -0,0 +1,98 @@
1
+ """
2
+ ksmesopy
3
+ =========
4
+ Python API for the Kansas Mesonet.
5
+ https://mesonet.k-state.edu
6
+ """
7
+
8
+ from ksmesopy.core import (
9
+ get_stations,
10
+ get_stations_active,
11
+ list_variables,
12
+ VARIABLES,
13
+ RENAME_PRESET,
14
+ _VWC_DEPS,
15
+ _ALL_VWC,
16
+ _VALID_FOR,
17
+ request_data,
18
+ request_data_multi,
19
+ rename_columns,
20
+ calibrate_vwc,
21
+ compute_soil_water_storage,
22
+ srad_to_mj,
23
+ atmospheric_pressure,
24
+ saturation_vapor_pressure,
25
+ actual_vapor_pressure,
26
+ vapor_pressure_deficit,
27
+ slope_saturation_vapor_pressure,
28
+ psychrometric_constant,
29
+ extraterrestrial_radiation,
30
+ net_radiation,
31
+ reference_et_penman_monteith,
32
+ reference_et_hargreaves,
33
+ )
34
+
35
+ from ksmesopy.utils import (
36
+ growing_degree_days,
37
+ heat_index,
38
+ wind_chill,
39
+ temperature_humidity_index,
40
+ )
41
+
42
+ from ksmesopy.charts import (
43
+ plot_temperature,
44
+ plot_precip,
45
+ plot_humidity,
46
+ plot_vpd,
47
+ plot_solar_radiation,
48
+ plot_wind,
49
+ plot_vwc,
50
+ plot_et,
51
+ )
52
+
53
+ __all__ = [
54
+ # Station metadata
55
+ "get_stations",
56
+ "get_stations_active",
57
+ # Variable catalogue
58
+ "list_variables",
59
+ "VARIABLES",
60
+ "RENAME_PRESET",
61
+ "_VWC_DEPS",
62
+ "_ALL_VWC",
63
+ "_VALID_FOR",
64
+ # Data retrieval
65
+ "request_data",
66
+ "request_data_multi",
67
+ "rename_columns",
68
+ # Soil processing
69
+ "calibrate_vwc",
70
+ "compute_soil_water_storage",
71
+ # Atmospheric helpers
72
+ "srad_to_mj",
73
+ "atmospheric_pressure",
74
+ "saturation_vapor_pressure",
75
+ "actual_vapor_pressure",
76
+ "vapor_pressure_deficit",
77
+ "slope_saturation_vapor_pressure",
78
+ "psychrometric_constant",
79
+ "extraterrestrial_radiation",
80
+ "net_radiation",
81
+ # Reference ET
82
+ "reference_et_penman_monteith",
83
+ "reference_et_hargreaves",
84
+ # Derived variables
85
+ "growing_degree_days",
86
+ "heat_index",
87
+ "wind_chill",
88
+ "temperature_humidity_index",
89
+ # Charts
90
+ "plot_temperature",
91
+ "plot_precip",
92
+ "plot_humidity",
93
+ "plot_vpd",
94
+ "plot_solar_radiation",
95
+ "plot_wind",
96
+ "plot_vwc",
97
+ "plot_et",
98
+ ]