cloudnetpy 1.56.5__py3-none-any.whl → 1.56.6__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/exceptions.py +7 -0
- cloudnetpy/plotting/plotting.py +53 -44
- cloudnetpy/version.py +1 -1
- {cloudnetpy-1.56.5.dist-info → cloudnetpy-1.56.6.dist-info}/METADATA +1 -1
- {cloudnetpy-1.56.5.dist-info → cloudnetpy-1.56.6.dist-info}/RECORD +8 -8
- {cloudnetpy-1.56.5.dist-info → cloudnetpy-1.56.6.dist-info}/LICENSE +0 -0
- {cloudnetpy-1.56.5.dist-info → cloudnetpy-1.56.6.dist-info}/WHEEL +0 -0
- {cloudnetpy-1.56.5.dist-info → cloudnetpy-1.56.6.dist-info}/top_level.txt +0 -0
cloudnetpy/exceptions.py
CHANGED
@@ -23,6 +23,13 @@ class RadarDataError(CloudnetException):
|
|
23
23
|
super().__init__(msg)
|
24
24
|
|
25
25
|
|
26
|
+
class PlottingError(CloudnetException):
|
27
|
+
"""Internal exception class."""
|
28
|
+
|
29
|
+
def __init__(self, msg: str):
|
30
|
+
super().__init__(msg)
|
31
|
+
|
32
|
+
|
26
33
|
class WeatherStationDataError(CloudnetException):
|
27
34
|
"""Internal exception class."""
|
28
35
|
|
cloudnetpy/plotting/plotting.py
CHANGED
@@ -16,6 +16,7 @@ from matplotlib.transforms import Affine2D, Bbox
|
|
16
16
|
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
17
17
|
from numpy import ma, ndarray
|
18
18
|
|
19
|
+
from cloudnetpy.exceptions import PlottingError
|
19
20
|
from cloudnetpy.plotting.plot_meta import ATTRIBUTES, PlotMeta
|
20
21
|
|
21
22
|
|
@@ -129,7 +130,7 @@ class FigureData:
|
|
129
130
|
variable_indices.append(extracted_ind)
|
130
131
|
if not valid_variables:
|
131
132
|
msg = f"None of the variables {requested_variables} found in the file."
|
132
|
-
raise
|
133
|
+
raise PlottingError(msg)
|
133
134
|
return valid_variables, variable_indices
|
134
135
|
|
135
136
|
def _get_height(self) -> np.ndarray | None:
|
@@ -270,8 +271,10 @@ class Plot:
|
|
270
271
|
)
|
271
272
|
if conversion_method == multiply:
|
272
273
|
self._data *= conversion
|
274
|
+
self._data_orig *= conversion
|
273
275
|
elif conversion_method == add:
|
274
276
|
self._data += conversion
|
277
|
+
self._data_orig += conversion
|
275
278
|
if units is not None:
|
276
279
|
return units
|
277
280
|
units = getattr(self.sub_plot.variable, "units", "")
|
@@ -309,7 +312,7 @@ class Plot:
|
|
309
312
|
max_gap_fraction_hour = _get_max_gap_in_minutes(figure_data) / 60
|
310
313
|
|
311
314
|
if figure_data.file.cloudnet_file_type == "model":
|
312
|
-
time, data =
|
315
|
+
time, data = screen_completely_masked_profiles(time, data)
|
313
316
|
|
314
317
|
gap_indices = np.where(np.diff(time) > max_gap_fraction_hour)[0]
|
315
318
|
if not ma.is_masked(data):
|
@@ -348,21 +351,16 @@ class Plot:
|
|
348
351
|
self._data = data_new
|
349
352
|
figure_data.time_including_gaps = time_new
|
350
353
|
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
def _read_flags(self, figure_data: FigureData) -> np.ndarray:
|
362
|
-
flag_name = f"{self.sub_plot.variable.name}_quality_flag"
|
363
|
-
if flag_name not in figure_data.variables:
|
364
|
-
flag_name = "temperature_quality_flag"
|
365
|
-
return figure_data.file.variables[flag_name][:] > 0
|
354
|
+
def _read_flagged_data(self, figure_data: FigureData) -> np.ndarray:
|
355
|
+
flag_names = [
|
356
|
+
f"{self.sub_plot.variable.name}_quality_flag",
|
357
|
+
"temperature_quality_flag",
|
358
|
+
"quality_flag",
|
359
|
+
]
|
360
|
+
for flag_name in flag_names:
|
361
|
+
if flag_name in figure_data.file.variables:
|
362
|
+
return figure_data.file.variables[flag_name][:] > 0
|
363
|
+
return np.array([])
|
366
364
|
|
367
365
|
|
368
366
|
class Plot2D(Plot):
|
@@ -385,7 +383,7 @@ class Plot2D(Plot):
|
|
385
383
|
self._fill_flagged_data(figure_data)
|
386
384
|
|
387
385
|
def _fill_flagged_data(self, figure_data: FigureData) -> None:
|
388
|
-
flags = self.
|
386
|
+
flags = self._read_flagged_data(figure_data)
|
389
387
|
batches = find_batches_of_ones(flags)
|
390
388
|
for batch in batches:
|
391
389
|
if batch[0] == batch[1]:
|
@@ -394,10 +392,9 @@ class Plot2D(Plot):
|
|
394
392
|
self._ax.fill_between(
|
395
393
|
time_batch,
|
396
394
|
*self._get_y_limits(),
|
397
|
-
facecolor="
|
398
|
-
alpha=
|
399
|
-
|
400
|
-
edgecolor="lightsalmon",
|
395
|
+
facecolor="whitesmoke",
|
396
|
+
alpha=0.7,
|
397
|
+
edgecolor="grey",
|
401
398
|
label="_nolegend_",
|
402
399
|
)
|
403
400
|
|
@@ -469,12 +466,18 @@ class Plot2D(Plot):
|
|
469
466
|
|
470
467
|
|
471
468
|
class Plot1D(Plot):
|
472
|
-
def plot_tb(self, figure_data: FigureData,
|
473
|
-
|
474
|
-
self.
|
475
|
-
|
476
|
-
|
469
|
+
def plot_tb(self, figure_data: FigureData, freq_ind: int):
|
470
|
+
self._data = self._data[:, freq_ind]
|
471
|
+
self._data_orig = self._data_orig[:, freq_ind]
|
472
|
+
is_bad_zenith = self._get_bad_zenith_profiles(figure_data)
|
473
|
+
self._data[is_bad_zenith] = ma.masked
|
474
|
+
self._data_orig[is_bad_zenith] = ma.masked
|
475
|
+
flags = self._read_flagged_data(figure_data)[:, freq_ind]
|
476
|
+
flags[is_bad_zenith] = False
|
477
|
+
if np.any(flags):
|
478
|
+
self.plot_flag_data(figure_data.time[flags], self._data_orig[flags])
|
477
479
|
self.add_legend()
|
480
|
+
self.plot(figure_data)
|
478
481
|
|
479
482
|
def plot_flag_data(self, time: np.ndarray, values: np.ndarray) -> None:
|
480
483
|
self._ax.plot(
|
@@ -511,7 +514,7 @@ class Plot1D(Plot):
|
|
511
514
|
pos = self._ax.get_position()
|
512
515
|
self._ax.set_position((pos.x0, pos.y0, pos.width * 0.965, pos.height))
|
513
516
|
if figure_data.is_mwrpy_product():
|
514
|
-
flags = self.
|
517
|
+
flags = self._read_flagged_data(figure_data)
|
515
518
|
if np.any(flags):
|
516
519
|
self.plot_flag_data(figure_data.time[flags], self._data_orig[flags])
|
517
520
|
self.add_legend()
|
@@ -556,8 +559,9 @@ class Plot1D(Plot):
|
|
556
559
|
return min(max(line_width, 0.25), 0.9)
|
557
560
|
|
558
561
|
def _plot_moving_average(self, figure_data: FigureData):
|
559
|
-
time = figure_data.
|
560
|
-
data
|
562
|
+
time = figure_data.time.copy()
|
563
|
+
data = self._data_orig.copy()
|
564
|
+
data, time = self._get_unmasked_values(data, time)
|
561
565
|
sma = self._calculate_moving_average(data, time, window=5)
|
562
566
|
gap_time = _get_max_gap_in_minutes(figure_data)
|
563
567
|
gaps = self._find_time_gap_indices(time, max_gap_min=gap_time)
|
@@ -574,24 +578,17 @@ class Plot1D(Plot):
|
|
574
578
|
good_values = ~data.mask
|
575
579
|
return data[good_values], time[good_values]
|
576
580
|
|
577
|
-
|
581
|
+
@staticmethod
|
582
|
+
def _get_bad_zenith_profiles(figure_data: FigureData) -> np.ndarray:
|
578
583
|
zenith_limit = 5
|
579
|
-
|
580
|
-
self._data = self._data[:, ind]
|
581
|
-
flagged_data = ma.masked_all_like(figure_data.time)
|
584
|
+
valid_pointing_status = 0
|
582
585
|
if "pointing_flag" in figure_data.file.variables:
|
583
586
|
pointing_flag = figure_data.file.variables["pointing_flag"][:]
|
584
587
|
zenith_angle = figure_data.file.variables["zenith_angle"][:]
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
# Store flagged data points for visualization
|
590
|
-
valid_ind = np.where(quality_flag != status)[0]
|
591
|
-
if len(valid_ind) > 0:
|
592
|
-
flagged_data[valid_ind] = self._data[valid_ind]
|
593
|
-
self._data[quality_flag != status] = ma.masked
|
594
|
-
return flagged_data
|
588
|
+
is_bad_zenith = np.abs(zenith_angle) > zenith_limit
|
589
|
+
is_bad_pointing = pointing_flag != valid_pointing_status
|
590
|
+
return is_bad_zenith | is_bad_pointing
|
591
|
+
return np.zeros_like(figure_data.time, dtype=bool)
|
595
592
|
|
596
593
|
@staticmethod
|
597
594
|
def _find_time_gap_indices(time: ndarray, max_gap_min: float) -> ndarray:
|
@@ -750,3 +747,15 @@ def find_batches_of_ones(array: np.ndarray) -> list[tuple[int, int]]:
|
|
750
747
|
starts = np.where(np.diff(np.hstack(([0], array))) == 1)[0]
|
751
748
|
stops = np.where(np.diff(np.hstack((array, [0]))) == -1)[0]
|
752
749
|
return list(zip(starts, stops, strict=True))
|
750
|
+
|
751
|
+
|
752
|
+
def screen_completely_masked_profiles(time: np.ndarray, data: ma.MaskedArray) -> tuple:
|
753
|
+
if not ma.is_masked(data):
|
754
|
+
return time, data
|
755
|
+
good_ind = np.where(np.any(~data.mask, axis=1))[0]
|
756
|
+
if len(good_ind) == 0:
|
757
|
+
msg = "All values masked in the file."
|
758
|
+
raise PlottingError(msg)
|
759
|
+
good_ind = np.append(good_ind, good_ind[-1] + 1)
|
760
|
+
good_ind = np.clip(good_ind, 0, len(time) - 1)
|
761
|
+
return time[good_ind], data[good_ind, :]
|
cloudnetpy/version.py
CHANGED
@@ -3,12 +3,12 @@ cloudnetpy/cloudnetarray.py,sha256=vSI6hqozhS1ntNIgY2kqTt7N6BF_xJtUii_vsuyxTno,6
|
|
3
3
|
cloudnetpy/concat_lib.py,sha256=YK5ho5msqwNxpPtPT8f2OewIJ8hTrbhCkaxHaBKLTI0,9809
|
4
4
|
cloudnetpy/constants.py,sha256=OMp3pKHCZmdKyRvfO35E7vE3FrS_DHIs_GJuexJLCQk,600
|
5
5
|
cloudnetpy/datasource.py,sha256=-6oLC5bsn9sIoaN0glV88owFyTeGRsW1ZVJSV8rM5rE,7813
|
6
|
-
cloudnetpy/exceptions.py,sha256=
|
6
|
+
cloudnetpy/exceptions.py,sha256=nA6YieylwKSp5KQOh3DhhcTmUBsd0tBZnedgmlWck-w,1415
|
7
7
|
cloudnetpy/metadata.py,sha256=Bcu1a9UyUq61jomuZ0_6hYIOzf61e5qCXeiwLm46ikw,5040
|
8
8
|
cloudnetpy/output.py,sha256=jD1pfBb4OQhVOrlhPEk-8FAi4bUW7zjAL468r6BPkJg,14586
|
9
9
|
cloudnetpy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
10
|
cloudnetpy/utils.py,sha256=yY5a5HLuAks2uzA4XbbqsGFEmXoyqECn_TjD3sMa0lI,27193
|
11
|
-
cloudnetpy/version.py,sha256=
|
11
|
+
cloudnetpy/version.py,sha256=KcLO5w70O8aXSbgK5nqMVq2YM5wAvubh8Uf9D1Q4FNc,72
|
12
12
|
cloudnetpy/categorize/__init__.py,sha256=gP5q3Vis1y9u9OWgA_idlbjfWXYN_S0IBSWdwBhL_uU,69
|
13
13
|
cloudnetpy/categorize/atmos.py,sha256=cax3iRmvr7S-VkUZqz0JCfAN3WEsUVbGfH4zSHy1APo,12384
|
14
14
|
cloudnetpy/categorize/atmos_utils.py,sha256=wndpwJxc2-QnNTkV8tc8I11Vs_WkNz9sVMX1fuGgUC4,3777
|
@@ -92,7 +92,7 @@ cloudnetpy/model_evaluation/tests/unit/test_statistical_methods.py,sha256=Ra3r4V
|
|
92
92
|
cloudnetpy/model_evaluation/tests/unit/test_tools.py,sha256=Ia_VrLdV2NstX5gbx_3AZTOAlrgLAy_xFZ8fHYVX0xI,3817
|
93
93
|
cloudnetpy/plotting/__init__.py,sha256=2-8x8d1AfAhfU15RwWhusD0Wot_g6Ob_jJoywbrTC7A,95
|
94
94
|
cloudnetpy/plotting/plot_meta.py,sha256=xbiEU9Okx5L_WG7KtVnljxjpCYcgqyOLywBc49gibSQ,13033
|
95
|
-
cloudnetpy/plotting/plotting.py,sha256=
|
95
|
+
cloudnetpy/plotting/plotting.py,sha256=NjeWF5u7fSvNVCwvOu8RyxQ7bApm7K74CF4I2sDCR40,26868
|
96
96
|
cloudnetpy/products/__init__.py,sha256=2hRb5HG9hNrxH1if5laJkLeFeaZCd5W1q3hh4ewsX0E,273
|
97
97
|
cloudnetpy/products/classification.py,sha256=J_FOMUSyxvFaT-hvdKVVcKPtuQ0u3V9PsV5xaIKzMjg,7843
|
98
98
|
cloudnetpy/products/der.py,sha256=HAdPvbJySEqkIwDrdZDPnli_wnN2qwm72_D1a82ZWIs,12398
|
@@ -106,8 +106,8 @@ cloudnetpy/products/mie_lu_tables.nc,sha256=It4fYpqJXlqOgL8jeZ-PxGzP08PMrELIDVe5
|
|
106
106
|
cloudnetpy/products/mwr_tools.py,sha256=TsVEqNwzoDv90TgzUSnJjMuc3C1KQ-hwsIZ8t0IdDJ4,4407
|
107
107
|
cloudnetpy/products/product_tools.py,sha256=E8CSijBY8cr70BH2JFa0lGQ-RzI9EcHQ0Fzt8CQ8rY4,10442
|
108
108
|
docs/source/conf.py,sha256=IKiFWw6xhUd8NrCg0q7l596Ck1d61XWeVjIFHVSG9Og,1490
|
109
|
-
cloudnetpy-1.56.
|
110
|
-
cloudnetpy-1.56.
|
111
|
-
cloudnetpy-1.56.
|
112
|
-
cloudnetpy-1.56.
|
113
|
-
cloudnetpy-1.56.
|
109
|
+
cloudnetpy-1.56.6.dist-info/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
|
110
|
+
cloudnetpy-1.56.6.dist-info/METADATA,sha256=kr-kst-d8xZSZA6N5IpUhsoRdab_rPEEXAmWlPS1pXo,5733
|
111
|
+
cloudnetpy-1.56.6.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
112
|
+
cloudnetpy-1.56.6.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
|
113
|
+
cloudnetpy-1.56.6.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|