cloudnetpy 1.61.8__py3-none-any.whl → 1.61.9__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.
- cloudnetpy/instruments/weather_station.py +103 -5
- cloudnetpy/version.py +1 -1
- {cloudnetpy-1.61.8.dist-info → cloudnetpy-1.61.9.dist-info}/METADATA +1 -1
- {cloudnetpy-1.61.8.dist-info → cloudnetpy-1.61.9.dist-info}/RECORD +7 -7
- {cloudnetpy-1.61.8.dist-info → cloudnetpy-1.61.9.dist-info}/LICENSE +0 -0
- {cloudnetpy-1.61.8.dist-info → cloudnetpy-1.61.9.dist-info}/WHEEL +0 -0
- {cloudnetpy-1.61.8.dist-info → cloudnetpy-1.61.9.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,6 @@
|
|
1
|
+
import csv
|
1
2
|
import datetime
|
3
|
+
import math
|
2
4
|
|
3
5
|
import numpy as np
|
4
6
|
from numpy import ma
|
@@ -15,7 +17,7 @@ from cloudnetpy.utils import datetime2decimal_hours
|
|
15
17
|
|
16
18
|
|
17
19
|
def ws2nc(
|
18
|
-
weather_station_file: str,
|
20
|
+
weather_station_file: str | list[str],
|
19
21
|
output_file: str,
|
20
22
|
site_meta: dict,
|
21
23
|
uuid: str | None = None,
|
@@ -38,12 +40,16 @@ def ws2nc(
|
|
38
40
|
WeatherStationDataError : Unable to read the file.
|
39
41
|
ValidTimeStampError: No valid timestamps found.
|
40
42
|
"""
|
43
|
+
if not isinstance(weather_station_file, list):
|
44
|
+
weather_station_file = [weather_station_file]
|
41
45
|
try:
|
42
46
|
ws: WS
|
43
47
|
if site_meta["name"] == "Palaiseau":
|
44
48
|
ws = PalaiseauWS(weather_station_file, site_meta)
|
45
49
|
elif site_meta["name"] == "Granada":
|
46
50
|
ws = GranadaWS(weather_station_file, site_meta)
|
51
|
+
elif site_meta["name"] == "Kenttärova":
|
52
|
+
ws = KenttarovaWS(weather_station_file, site_meta)
|
47
53
|
else:
|
48
54
|
msg = "Unsupported site"
|
49
55
|
raise ValueError(msg) # noqa: TRY301
|
@@ -81,9 +87,11 @@ class WS(CloudnetInstrument):
|
|
81
87
|
|
82
88
|
|
83
89
|
class PalaiseauWS(WS):
|
84
|
-
def __init__(self,
|
90
|
+
def __init__(self, filenames: list[str], site_meta: dict):
|
85
91
|
super().__init__()
|
86
|
-
|
92
|
+
if len(filenames) != 1:
|
93
|
+
raise ValueError
|
94
|
+
self.filename = filenames[0]
|
87
95
|
self.site_meta = site_meta
|
88
96
|
self.instrument = instruments.GENERIC_WEATHER_STATION
|
89
97
|
self._data = self._read_data()
|
@@ -176,9 +184,11 @@ class PalaiseauWS(WS):
|
|
176
184
|
|
177
185
|
|
178
186
|
class GranadaWS(WS):
|
179
|
-
def __init__(self,
|
187
|
+
def __init__(self, filenames: list[str], site_meta: dict):
|
188
|
+
if len(filenames) != 1:
|
189
|
+
raise ValueError
|
180
190
|
super().__init__()
|
181
|
-
self.filename =
|
191
|
+
self.filename = filenames[0]
|
182
192
|
self.site_meta = site_meta
|
183
193
|
self.instrument = instruments.GENERIC_WEATHER_STATION
|
184
194
|
self._data = self._read_data()
|
@@ -262,6 +272,94 @@ class GranadaWS(WS):
|
|
262
272
|
) # mm -> m
|
263
273
|
|
264
274
|
|
275
|
+
class KenttarovaWS(WS):
|
276
|
+
def __init__(self, filenames: list[str], site_meta: dict):
|
277
|
+
super().__init__()
|
278
|
+
self.filenames = filenames
|
279
|
+
self.site_meta = site_meta
|
280
|
+
self.instrument = instruments.GENERIC_WEATHER_STATION
|
281
|
+
self._data = self._read_data()
|
282
|
+
|
283
|
+
def _read_data(self) -> dict:
|
284
|
+
merged: dict = {}
|
285
|
+
for filename in self.filenames:
|
286
|
+
with open(filename, newline="") as f:
|
287
|
+
reader = csv.DictReader(f)
|
288
|
+
raw_data: dict = {key: [] for key in reader.fieldnames} # type: ignore[union-attr]
|
289
|
+
for row in reader:
|
290
|
+
for key, value in row.items():
|
291
|
+
parsed_value: float | datetime.datetime
|
292
|
+
if key == "Read time (UTC+2)":
|
293
|
+
parsed_value = datetime.datetime.strptime(
|
294
|
+
value, "%Y-%m-%d %H:%M:%S"
|
295
|
+
) - datetime.timedelta(hours=2)
|
296
|
+
else:
|
297
|
+
try:
|
298
|
+
parsed_value = float(value)
|
299
|
+
except ValueError:
|
300
|
+
parsed_value = math.nan
|
301
|
+
raw_data[key].append(parsed_value)
|
302
|
+
data = {
|
303
|
+
"time": raw_data["Read time (UTC+2)"],
|
304
|
+
"air_temperature": raw_data["Temp 2m (C)"],
|
305
|
+
"relative_humidity": raw_data["Humidity 2m (%)"],
|
306
|
+
"air_pressure": raw_data["Pressure (hPa)"],
|
307
|
+
"wind_speed": raw_data["Wind speed (m/s)"],
|
308
|
+
"wind_direction": raw_data["Wind dir (deg)"],
|
309
|
+
"rainfall_rate": raw_data["Precipitation (?)"],
|
310
|
+
}
|
311
|
+
if merged:
|
312
|
+
merged = {key: [*merged[key], *data[key]] for key in merged}
|
313
|
+
else:
|
314
|
+
merged = data
|
315
|
+
for key, value in merged.items():
|
316
|
+
new_value = np.array(value)
|
317
|
+
if key != "time":
|
318
|
+
new_value = ma.masked_where(np.isnan(new_value), new_value)
|
319
|
+
merged[key] = new_value
|
320
|
+
return merged
|
321
|
+
|
322
|
+
def convert_time(self) -> None:
|
323
|
+
pass
|
324
|
+
|
325
|
+
def screen_timestamps(self, date: str) -> None:
|
326
|
+
dates = [str(d.date()) for d in self._data["time"]]
|
327
|
+
valid_ind = [ind for ind, d in enumerate(dates) if d == date]
|
328
|
+
if not valid_ind:
|
329
|
+
raise ValidTimeStampError
|
330
|
+
for key in self._data:
|
331
|
+
self._data[key] = [
|
332
|
+
x for ind, x in enumerate(self._data[key]) if ind in valid_ind
|
333
|
+
]
|
334
|
+
|
335
|
+
def add_date(self) -> None:
|
336
|
+
first_date = self._data["time"][0].date()
|
337
|
+
self.date = [
|
338
|
+
str(first_date.year),
|
339
|
+
str(first_date.month).zfill(2),
|
340
|
+
str(first_date.day).zfill(2),
|
341
|
+
]
|
342
|
+
|
343
|
+
def add_data(self) -> None:
|
344
|
+
for key, value in self._data.items():
|
345
|
+
parsed = datetime2decimal_hours(value) if key == "time" else ma.array(value)
|
346
|
+
self.data[key] = CloudnetArray(parsed, key)
|
347
|
+
self.data["rainfall_amount"] = CloudnetArray(
|
348
|
+
ma.cumsum(self._data["rainfall_rate"]), "rainfall_amount"
|
349
|
+
)
|
350
|
+
|
351
|
+
def convert_units(self) -> None:
|
352
|
+
temperature_kelvins = atmos_utils.c2k(self.data["air_temperature"][:])
|
353
|
+
self.data["air_temperature"].data = temperature_kelvins
|
354
|
+
self.data["relative_humidity"].data = self.data["relative_humidity"][:] / 100
|
355
|
+
self.data["air_pressure"].data = self.data["air_pressure"][:] * 100 # hPa -> Pa
|
356
|
+
rainfall_rate = self.data["rainfall_rate"][:]
|
357
|
+
self.data["rainfall_rate"].data = rainfall_rate / 60 / 1000 # mm/min -> m/s
|
358
|
+
self.data["rainfall_amount"].data = (
|
359
|
+
self.data["rainfall_amount"][:] / 1000
|
360
|
+
) # mm -> m
|
361
|
+
|
362
|
+
|
265
363
|
ATTRIBUTES = {
|
266
364
|
"rainfall_amount": MetaData(
|
267
365
|
long_name="Rainfall amount",
|
cloudnetpy/version.py
CHANGED
@@ -8,7 +8,7 @@ cloudnetpy/metadata.py,sha256=v_VDo2vbdTxB0zIsfP69IcrwSKiRlLpsGdq6JPI4CoA,5306
|
|
8
8
|
cloudnetpy/output.py,sha256=WoVTNuxni0DUr163vZ-_mDr1brXhY15XSlGMrq9Aoqw,14700
|
9
9
|
cloudnetpy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
10
|
cloudnetpy/utils.py,sha256=-8x7LQ6WDHxf2lDZfhG50WYe2iSVLQObnVXZG46JzKI,28468
|
11
|
-
cloudnetpy/version.py,sha256=
|
11
|
+
cloudnetpy/version.py,sha256=jCiBK4tcMrQYXPVaBsEM_6y-SUJGsVWPfbgo81jYpFg,72
|
12
12
|
cloudnetpy/categorize/__init__.py,sha256=gP5q3Vis1y9u9OWgA_idlbjfWXYN_S0IBSWdwBhL_uU,69
|
13
13
|
cloudnetpy/categorize/atmos.py,sha256=fWW8ye_8HZASRAiYwURFKWzcGOYIA2RKeVxCq0lVOuM,12389
|
14
14
|
cloudnetpy/categorize/atmos_utils.py,sha256=wndpwJxc2-QnNTkV8tc8I11Vs_WkNz9sVMX1fuGgUC4,3777
|
@@ -47,7 +47,7 @@ cloudnetpy/instruments/rpg.py,sha256=yQpcKcgzRvVvkl6NhKvo4PUkv9nZ69_hzzPpS2Ei-Is
|
|
47
47
|
cloudnetpy/instruments/rpg_reader.py,sha256=LAdXL3TmD5QzQbqtPOcemZji_qkXwmw6a6F8NmF6Zg8,11355
|
48
48
|
cloudnetpy/instruments/toa5.py,sha256=1JnuYViD8c_tHJZ9lf4OU44iepEkXHsXOzDfVf_b0qc,1759
|
49
49
|
cloudnetpy/instruments/vaisala.py,sha256=ektdXoID2X_V9H5Zp1fgHTUBapFMSyPVEWW_aoR6DEY,14655
|
50
|
-
cloudnetpy/instruments/weather_station.py,sha256=
|
50
|
+
cloudnetpy/instruments/weather_station.py,sha256=76O3z1932Bidi_1tXHeVJmtoISOJFKB9vFVJnWJj4M0,13822
|
51
51
|
cloudnetpy/instruments/disdrometer/__init__.py,sha256=lyjwttWvFvuwYxEkusoAvgRcbBmglmOp5HJOpXUqLWo,93
|
52
52
|
cloudnetpy/instruments/disdrometer/common.py,sha256=g52iK2aNp3Z88kovUmGVpC54NZomPa9D871gzO0AmQ4,9267
|
53
53
|
cloudnetpy/instruments/disdrometer/parsivel.py,sha256=WiL-vCjw9Gmb5irvW3AXddsyprp8MGOfqcVAlfy0zpc,25521
|
@@ -108,8 +108,8 @@ cloudnetpy/products/mie_lu_tables.nc,sha256=It4fYpqJXlqOgL8jeZ-PxGzP08PMrELIDVe5
|
|
108
108
|
cloudnetpy/products/mwr_tools.py,sha256=RuzokxxqXlTGk7XAOrif_FDPUJdf0j_wJgNq-7a_nK8,4684
|
109
109
|
cloudnetpy/products/product_tools.py,sha256=rhx_Ru9FLlQqCNM-awoiHx18-Aq1eBwL9LiUaQoJs6k,10412
|
110
110
|
docs/source/conf.py,sha256=IKiFWw6xhUd8NrCg0q7l596Ck1d61XWeVjIFHVSG9Og,1490
|
111
|
-
cloudnetpy-1.61.
|
112
|
-
cloudnetpy-1.61.
|
113
|
-
cloudnetpy-1.61.
|
114
|
-
cloudnetpy-1.61.
|
115
|
-
cloudnetpy-1.61.
|
111
|
+
cloudnetpy-1.61.9.dist-info/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
|
112
|
+
cloudnetpy-1.61.9.dist-info/METADATA,sha256=-rbU3tJCXKfgY_2BcIqFh0Ryw9R7rHXhURpuPabJXLQ,5784
|
113
|
+
cloudnetpy-1.61.9.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
114
|
+
cloudnetpy-1.61.9.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
|
115
|
+
cloudnetpy-1.61.9.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|