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 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
 
@@ -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 ValueError(msg)
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 = self._get_unmasked_model_values(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
- @staticmethod
352
- def _get_unmasked_model_values(time: np.ndarray, data: ma.MaskedArray) -> tuple:
353
- good_ind = np.where(np.any(~data.mask, axis=1))[0]
354
- if len(good_ind) == 0:
355
- msg = "No unmasked values in the file."
356
- raise ValueError(msg)
357
- good_ind = np.append(good_ind, good_ind[-1] + 1)
358
- good_ind = np.clip(good_ind, 0, len(time) - 1)
359
- return time[good_ind], data[good_ind, :]
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._read_flags(figure_data)
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="mistyrose",
398
- alpha=1,
399
- hatch=r"\\",
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, ind: int):
473
- flagged_data = self._pointing_filter(figure_data, ind)
474
- self.plot(figure_data)
475
- if ma.count(flagged_data) > 0:
476
- self.plot_flag_data(figure_data.time, flagged_data)
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._read_flags(figure_data)
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.time_including_gaps.copy()
560
- data, time = self._get_unmasked_values(self._data, time)
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
- def _pointing_filter(self, figure_data: FigureData, ind: int) -> ndarray:
581
+ @staticmethod
582
+ def _get_bad_zenith_profiles(figure_data: FigureData) -> np.ndarray:
578
583
  zenith_limit = 5
579
- status = 0
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
- quality_flag = figure_data.file.variables["quality_flag"][:, ind]
586
- # First mask bad zenith angle points
587
- self._data[np.abs(zenith_angle) > zenith_limit] = ma.masked
588
- self._data[pointing_flag != status] = ma.masked
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
@@ -1,4 +1,4 @@
1
1
  MAJOR = 1
2
2
  MINOR = 56
3
- PATCH = 5
3
+ PATCH = 6
4
4
  __version__ = f"{MAJOR}.{MINOR}.{PATCH}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cloudnetpy
3
- Version: 1.56.5
3
+ Version: 1.56.6
4
4
  Summary: Python package for Cloudnet processing
5
5
  Author: Simo Tukiainen
6
6
  License: MIT License
@@ -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=NsuB5gTA8NvpmxykIJjHOWObTPpAerzpofs8xrFA1ow,1272
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=6cv25AS5sFm-DzOvNw4RB7-2flKWgFkjLMkIKHBnLD0,72
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=jUn64USNApkL_c6FXAr30hW2Xc_M8WZthqVgjuCgN0M,26667
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.5.dist-info/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
110
- cloudnetpy-1.56.5.dist-info/METADATA,sha256=x8u7BhZ4EiuxFV80zTN-LaHuqkAU8TKlOMLR0FAzCHo,5733
111
- cloudnetpy-1.56.5.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
112
- cloudnetpy-1.56.5.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
113
- cloudnetpy-1.56.5.dist-info/RECORD,,
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,,