disdrodb 0.1.0__py3-none-any.whl → 0.1.2__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.
Files changed (77) hide show
  1. disdrodb/__init__.py +1 -1
  2. disdrodb/_version.py +2 -2
  3. disdrodb/api/io.py +12 -2
  4. disdrodb/data_transfer/download_data.py +145 -14
  5. disdrodb/l0/check_standards.py +15 -10
  6. disdrodb/l0/configs/LPM/bins_diameter.yml +3 -3
  7. disdrodb/l0/configs/LPM/l0a_encodings.yml +4 -4
  8. disdrodb/l0/configs/LPM/l0b_cf_attrs.yml +22 -6
  9. disdrodb/l0/configs/LPM/l0b_encodings.yml +41 -0
  10. disdrodb/l0/configs/LPM/raw_data_format.yml +40 -0
  11. disdrodb/l0/configs/PARSIVEL/l0b_cf_attrs.yml +1 -1
  12. disdrodb/l0/configs/PARSIVEL/raw_data_format.yml +1 -1
  13. disdrodb/l0/configs/PARSIVEL2/l0a_encodings.yml +4 -0
  14. disdrodb/l0/configs/PARSIVEL2/l0b_cf_attrs.yml +20 -4
  15. disdrodb/l0/configs/PARSIVEL2/l0b_encodings.yml +41 -0
  16. disdrodb/l0/configs/PARSIVEL2/raw_data_format.yml +50 -10
  17. disdrodb/l0/configs/PWS100/bins_diameter.yml +173 -0
  18. disdrodb/l0/configs/PWS100/bins_velocity.yml +173 -0
  19. disdrodb/l0/configs/PWS100/l0a_encodings.yml +19 -0
  20. disdrodb/l0/configs/PWS100/l0b_cf_attrs.yml +76 -0
  21. disdrodb/l0/configs/PWS100/l0b_encodings.yml +176 -0
  22. disdrodb/l0/configs/PWS100/raw_data_format.yml +182 -0
  23. disdrodb/l0/configs/RD80/raw_data_format.yml +2 -6
  24. disdrodb/l0/l0b_nc_processing.py +1 -1
  25. disdrodb/l0/l0b_processing.py +12 -10
  26. disdrodb/l0/manuals/SWS250.pdf +0 -0
  27. disdrodb/l0/manuals/VPF730.pdf +0 -0
  28. disdrodb/l0/manuals/VPF750.pdf +0 -0
  29. disdrodb/l0/readers/LPM/AUSTRALIA/MELBOURNE_2007_LPM.py +23 -13
  30. disdrodb/l0/readers/LPM/BRAZIL/CHUVA_LPM.py +3 -3
  31. disdrodb/l0/readers/LPM/BRAZIL/GOAMAZON_LPM.py +5 -3
  32. disdrodb/l0/readers/LPM/ITALY/GID_LPM.py +36 -20
  33. disdrodb/l0/readers/LPM/ITALY/GID_LPM_W.py +210 -0
  34. disdrodb/l0/readers/LPM/KIT/CHWALA.py +225 -0
  35. disdrodb/l0/readers/LPM/SLOVENIA/ARSO.py +197 -0
  36. disdrodb/l0/readers/LPM/SLOVENIA/CRNI_VRH.py +197 -0
  37. disdrodb/l0/readers/PARSIVEL/GPM/PIERS.py +107 -0
  38. disdrodb/l0/readers/PARSIVEL/JAPAN/JMA.py +125 -0
  39. disdrodb/l0/readers/PARSIVEL/NCAR/PECAN_MOBILE.py +1 -1
  40. disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2009.py +1 -1
  41. disdrodb/l0/readers/PARSIVEL/SLOVENIA/UL_FGG.py +121 -0
  42. disdrodb/l0/readers/PARSIVEL2/FRANCE/ENPC_PARSIVEL2.py +189 -0
  43. disdrodb/l0/readers/PARSIVEL2/KIT/BURKINA_FASO.py +133 -0
  44. disdrodb/l0/readers/PARSIVEL2/NCAR/FARM_PARSIVEL2.py +138 -0
  45. disdrodb/l0/readers/PARSIVEL2/NCAR/PECAN_FP3.py +1 -1
  46. disdrodb/l0/readers/PARSIVEL2/NCAR/VORTEX_SE_2016_P2.py +1 -1
  47. disdrodb/l0/readers/PARSIVEL2/NCAR/VORTEX_SE_2016_PIPS.py +9 -0
  48. disdrodb/l0/readers/PARSIVEL2/NETHERLANDS/DELFT_NC.py +67 -0
  49. disdrodb/l0/readers/PWS100/FRANCE/ENPC_PWS100.py +150 -0
  50. disdrodb/l0/readers/RD80/NOAA/PSL_RD80.py +291 -0
  51. disdrodb/l0/readers/template_reader_raw_netcdf_data.py +1 -1
  52. disdrodb/l0/standards.py +7 -4
  53. disdrodb/l0/template_tools.py +2 -2
  54. disdrodb/l1/encoding_attrs.py +30 -8
  55. disdrodb/l1/processing.py +6 -4
  56. disdrodb/l1/resampling.py +1 -1
  57. disdrodb/l1/routines.py +9 -7
  58. disdrodb/l2/empirical_dsd.py +100 -2
  59. disdrodb/l2/event.py +3 -3
  60. disdrodb/l2/processing.py +21 -12
  61. disdrodb/l2/processing_options.py +7 -7
  62. disdrodb/l2/routines.py +3 -3
  63. disdrodb/metadata/checks.py +15 -6
  64. disdrodb/metadata/manipulation.py +2 -2
  65. disdrodb/metadata/standards.py +83 -79
  66. disdrodb/metadata/writer.py +2 -2
  67. disdrodb/routines.py +246 -10
  68. disdrodb/scattering/routines.py +1 -1
  69. disdrodb/utils/dataframe.py +342 -0
  70. disdrodb/utils/directories.py +14 -2
  71. disdrodb/utils/xarray.py +83 -0
  72. {disdrodb-0.1.0.dist-info → disdrodb-0.1.2.dist-info}/METADATA +34 -61
  73. {disdrodb-0.1.0.dist-info → disdrodb-0.1.2.dist-info}/RECORD +77 -54
  74. {disdrodb-0.1.0.dist-info → disdrodb-0.1.2.dist-info}/WHEEL +1 -1
  75. {disdrodb-0.1.0.dist-info → disdrodb-0.1.2.dist-info}/entry_points.txt +3 -3
  76. {disdrodb-0.1.0.dist-info → disdrodb-0.1.2.dist-info}/licenses/LICENSE +0 -0
  77. {disdrodb-0.1.0.dist-info → disdrodb-0.1.2.dist-info}/top_level.txt +0 -0
@@ -101,6 +101,101 @@ def count_bins_with_drops(ds):
101
101
  return da
102
102
 
103
103
 
104
+ def _compute_qc_bins_metrics(arr):
105
+ # Find indices of non-zero elements
106
+ arr = arr.copy()
107
+ arr[np.isnan(arr)] = 0
108
+ non_zero_indices = np.nonzero(arr)[0]
109
+ if non_zero_indices.size == 0:
110
+ return np.array([0, len(arr), 1, len(arr)])
111
+
112
+ # Define bins interval with drops
113
+ start_idx, end_idx = non_zero_indices[0], non_zero_indices[-1]
114
+ segment = arr[start_idx : end_idx + 1]
115
+
116
+ # Compute number of bins with drops
117
+ total_bins = segment.size
118
+
119
+ # Compute number of missing bins (zeros)
120
+ n_missing_bins = int(np.sum(segment == 0))
121
+
122
+ # Compute fraction of bins with missing drops
123
+ fraction_missing = n_missing_bins / total_bins
124
+
125
+ # Identify longest with with consecutive zeros
126
+ zero_mask = (segment == 0).astype(int)
127
+ # - Pad with zeros at both ends to detect edges
128
+ padded = np.pad(zero_mask, (1, 1), "constant", constant_values=0)
129
+ diffs = np.diff(padded)
130
+ # - Start and end indices of runs
131
+ run_starts = np.where(diffs == 1)[0]
132
+ run_ends = np.where(diffs == -1)[0]
133
+ run_lengths = run_ends - run_starts
134
+ max_consecutive_missing = run_lengths.max() if run_lengths.size > 0 else 0
135
+
136
+ # Define output
137
+ output = np.array([total_bins, n_missing_bins, fraction_missing, max_consecutive_missing])
138
+ return output
139
+
140
+
141
+ def compute_qc_bins_metrics(ds):
142
+ """
143
+ Compute quality-control metrics for drop-count bins along the diameter dimension.
144
+
145
+ This function selects the first available drop-related variable from the dataset,
146
+ optionally collapses over velocity methods and the velocity dimension, then
147
+ computes four metrics per time step:
148
+
149
+ 1. Nbins: total number of diameter bins between the first and last non-zero count
150
+ 2. Nbins_missing: number of bins with zero or NaN counts in that interval
151
+ 3. Nbins_missing_fraction: fraction of missing bins (zeros) in the interval
152
+ 4. Nbins_missing_consecutive: maximum length of consecutive missing bins
153
+
154
+ Parameters
155
+ ----------
156
+ ds : xr.Dataset
157
+ Input dataset containing one of the following variables:
158
+ 'drop_counts', 'drop_number_concentration', or 'drop_number'.
159
+ If a 'velocity_method' dimension exists, only the first method is used.
160
+ If a velocity dimension (specified by VELOCITY_DIMENSION) exists, it is summed over.
161
+
162
+ Returns
163
+ -------
164
+ xr.Dataset
165
+ Dataset with a new 'metric' dimension of size 4 and coordinates:
166
+ ['Nbins', 'Nbins_missing', 'Nbins_missing_fraction', 'Nbins_missing_consecutive'],
167
+ indexed by 'time'.
168
+ """
169
+ # Select useful variable
170
+ candidate_variables = ["drop_counts", "drop_number_concentration", "drop_number"]
171
+ available_variables = [var for var in candidate_variables if var in ds]
172
+ if len(available_variables) == 0:
173
+ raise ValueError(f"One of these variables is required: {candidate_variables}")
174
+ da = ds[available_variables[0]]
175
+ if "velocity_method" in da.dims:
176
+ da = da.isel(velocity_method=0)
177
+ da = da.drop_vars("velocity_method")
178
+ if VELOCITY_DIMENSION in da.dims:
179
+ da = da.sum(dim=VELOCITY_DIMENSION)
180
+
181
+ # Compute QC metrics
182
+ da_qc_bins = xr.apply_ufunc(
183
+ _compute_qc_bins_metrics,
184
+ da,
185
+ input_core_dims=[[DIAMETER_DIMENSION]],
186
+ output_core_dims=[["metric"]],
187
+ vectorize=True,
188
+ dask="parallelized",
189
+ output_dtypes=[float],
190
+ dask_gufunc_kwargs={"output_sizes": {"metric": 4}},
191
+ )
192
+
193
+ # Assign meaningful labels to the qc 'metric' dimension
194
+ variables = ["Nbins", "Nbins_missing", "Nbins_missing_fraction", "Nbins_missing_consecutive"]
195
+ ds_qc_bins = da_qc_bins.assign_coords(metric=variables).to_dataset(dim="metric")
196
+ return ds_qc_bins
197
+
198
+
104
199
  ####-------------------------------------------------------------------------------------------------------------------.
105
200
  #### DSD Spectrum, Concentration, Moments
106
201
 
@@ -117,13 +212,16 @@ def get_effective_sampling_area(sensor_name, diameter):
117
212
  B = 30 / 1000 # Width of the Parsivel beam in m (30mm)
118
213
  sampling_area = L * (B - diameter / 2)
119
214
  return sampling_area
120
- if sensor_name in "LPM":
215
+ if sensor_name == "LPM":
121
216
  # Calculate sampling area for each diameter bin (S_i)
122
217
  L = 228 / 1000 # Length of the Parsivel beam in m (228 mm)
123
218
  B = 20 / 1000 # Width of the Parsivel beam in m (20 mm)
124
219
  sampling_area = L * (B - diameter / 2)
125
220
  return sampling_area
126
- if sensor_name in "RD80":
221
+ if sensor_name == "PWS100":
222
+ sampling_area = 0.004 # m2 # TODO: L * (B - diameter / 2) ?
223
+ return sampling_area
224
+ if sensor_name == "RD80":
127
225
  sampling_area = 0.005 # m2
128
226
  return sampling_area
129
227
  raise NotImplementedError(f"Effective sampling area for {sensor_name} must yet to be specified in the software.")
disdrodb/l2/event.py CHANGED
@@ -43,7 +43,7 @@ def identify_events(
43
43
  ):
44
44
  """Return a list of rainy events.
45
45
 
46
- Rainy timesteps are defined when n_drops_selected > min_n_drops.
46
+ Rainy timesteps are defined when N > min_n_drops.
47
47
  Any rainy isolated timesteps (based on neighborhood criteria) is removed.
48
48
  Then, consecutive rainy timesteps are grouped into the same event if the time gap between them does not
49
49
  exceed `intra_event_max_time_gap`. Finally, events that do not meet minimum size or duration
@@ -90,7 +90,7 @@ def identify_events(
90
90
  else:
91
91
  list_ds = [xr.open_dataset(filepath, chunks={}, cache=False, decode_timedelta=False) for filepath in filepaths]
92
92
  # Filter dataset for requested variables
93
- variables = ["time", "n_drops_selected"]
93
+ variables = ["time", "N"]
94
94
  list_ds = [ds[variables] for ds in list_ds]
95
95
  # Concat datasets
96
96
  ds = xr.concat(list_ds, dim="time", compat="no_conflicts", combine_attrs="override")
@@ -102,7 +102,7 @@ def identify_events(
102
102
  # Sort dataset by time
103
103
  ds = ensure_sorted_by_time(ds)
104
104
  # Define candidate timesteps to group into events
105
- idx_valid = ds["n_drops_selected"].data > min_n_drops
105
+ idx_valid = ds["N"].data > min_n_drops
106
106
  timesteps = ds["time"].data[idx_valid]
107
107
  # Define event list
108
108
  event_list = group_timesteps_into_event(
disdrodb/l2/processing.py CHANGED
@@ -24,8 +24,8 @@ from disdrodb.l1.fall_velocity import get_raindrop_fall_velocity
24
24
  from disdrodb.l1_env.routines import load_env_dataset
25
25
  from disdrodb.l2.empirical_dsd import (
26
26
  compute_integral_parameters,
27
+ compute_qc_bins_metrics,
27
28
  compute_spectrum_parameters,
28
- count_bins_with_drops,
29
29
  get_drop_average_velocity,
30
30
  get_drop_number_concentration,
31
31
  get_effective_sampling_area,
@@ -140,11 +140,12 @@ def generate_l2_empirical(ds, ds_env=None, compute_spectra=False):
140
140
  # Discard all timesteps without measured drops
141
141
  # - This allow to speed up processing
142
142
  # - Regularization can be done at the end
143
- ds = ds.isel(time=ds["n_drops_selected"] > 0)
143
+ ds = ds.isel(time=ds["N"] > 0)
144
144
 
145
145
  # Count number of diameter bins with data
146
- if "n_bins_with_drops" not in ds:
147
- ds["n_bins_with_drops"] = count_bins_with_drops(ds)
146
+ if "Nbins" not in ds:
147
+ # Add bins statistics
148
+ ds.update(compute_qc_bins_metrics(ds))
148
149
 
149
150
  # Retrieve ENV dataset or take defaults
150
151
  # --> Used for fall velocity and water density estimates
@@ -174,8 +175,8 @@ def generate_l2_empirical(ds, ds_env=None, compute_spectra=False):
174
175
  "drop_number", # 2D V x D
175
176
  "drop_counts", # 1D D
176
177
  "sample_interval",
177
- "n_drops_selected",
178
- "n_drops_discarded",
178
+ "N",
179
+ "Nremoved",
179
180
  "Dmin",
180
181
  "Dmax",
181
182
  "fall_velocity",
@@ -291,14 +292,14 @@ def generate_l2_model(
291
292
  fall_velocity_method="Beard1976",
292
293
  # PSD discretization
293
294
  diameter_min=0,
294
- diameter_max=8,
295
+ diameter_max=10,
295
296
  diameter_spacing=0.05,
296
297
  # Fitting options
297
298
  psd_model=None,
298
299
  optimization=None,
299
300
  optimization_kwargs=None,
300
301
  # Filtering options
301
- min_bins_with_drops=4,
302
+ min_nbins=4,
302
303
  remove_timesteps_with_few_bins=False,
303
304
  mask_timesteps_with_few_bins=False,
304
305
  # GOF metrics options
@@ -357,11 +358,12 @@ def generate_l2_model(
357
358
  ####------------------------------------------------------.
358
359
  #### Preprocessing
359
360
  # Count number of diameter bins with data
360
- if "n_bins_with_drops" not in ds:
361
- ds["n_bins_with_drops"] = count_bins_with_drops(ds)
361
+ if "Nbins" not in ds:
362
+ # Add bins statistics
363
+ ds.update(compute_qc_bins_metrics(ds))
362
364
 
363
365
  # Identify timesteps with enough diameter bins with counted trops
364
- valid_timesteps = ds["n_bins_with_drops"] >= min_bins_with_drops
366
+ valid_timesteps = ds["Nbins"] >= min_nbins
365
367
 
366
368
  # Drop such timesteps if asked
367
369
  if remove_timesteps_with_few_bins:
@@ -466,7 +468,14 @@ def generate_l2_model(
466
468
 
467
469
 
468
470
  @check_pytmatrix_availability
469
- def generate_l2_radar(ds, radar_band=None, canting_angle_std=7, diameter_max=8, axis_ratio="Thurai2007", parallel=True):
471
+ def generate_l2_radar(
472
+ ds,
473
+ radar_band=None,
474
+ canting_angle_std=7,
475
+ diameter_max=10,
476
+ axis_ratio="Thurai2007",
477
+ parallel=True,
478
+ ):
470
479
  """Simulate polarimetric radar variables from empirical drop number concentration or the estimated PSD.
471
480
 
472
481
  Parameters
@@ -7,16 +7,16 @@ DEFAULT_CONFIG = {
7
7
  "global_settings": {
8
8
  "time_integration": [
9
9
  "1MIN",
10
+ "5MIN",
10
11
  "10MIN",
11
12
  "ROLL1MIN",
12
- "ROLL10MIN",
13
13
  ], # ["10S", "30S", "1MIN", "5MIN", "10MIN", "15MIN", "30MIN", "1H", "ROLL5MIN", "ROLL10MIN"],
14
14
  # Radar options
15
15
  "radar_simulation_enabled": False,
16
16
  "radar_simulation_options": {
17
17
  "radar_band": ["S", "C", "X", "Ku", "Ka", "W"],
18
18
  "canting_angle_std": 7,
19
- "diameter_max": 8,
19
+ "diameter_max": 10,
20
20
  "axis_ratio": "Thurai2007",
21
21
  },
22
22
  # L2E options
@@ -25,10 +25,10 @@ DEFAULT_CONFIG = {
25
25
  "l2m_options": {
26
26
  "fall_velocity_method": "Beard1976",
27
27
  "diameter_min": 0,
28
- "diameter_max": 8,
28
+ "diameter_max": 10,
29
29
  "diameter_spacing": 0.05,
30
30
  "gof_metrics": True,
31
- "min_bins_with_drops": 4,
31
+ "min_nbins": 4,
32
32
  "remove_timesteps_with_few_bins": False,
33
33
  "mask_timesteps_with_few_bins": False,
34
34
  "models": {
@@ -112,7 +112,7 @@ TEST_CONFIG = {
112
112
  "radar_simulation_options": {
113
113
  "radar_band": ["S", "C", "X", "Ku", "Ka", "W"],
114
114
  "canting_angle_std": 7,
115
- "diameter_max": 8,
115
+ "diameter_max": 10,
116
116
  "axis_ratio": "Thurai2007",
117
117
  },
118
118
  # L2E options
@@ -121,10 +121,10 @@ TEST_CONFIG = {
121
121
  "l2m_options": {
122
122
  "fall_velocity_method": "Beard1976",
123
123
  "diameter_min": 0,
124
- "diameter_max": 8,
124
+ "diameter_max": 10,
125
125
  "diameter_spacing": 0.05,
126
126
  "gof_metrics": True,
127
- "min_bins_with_drops": 4,
127
+ "min_nbins": 4,
128
128
  "remove_timesteps_with_few_bins": False,
129
129
  "mask_timesteps_with_few_bins": False,
130
130
  "models": {
disdrodb/l2/routines.py CHANGED
@@ -156,11 +156,11 @@ def _generate_l2e(
156
156
 
157
157
  ##------------------------------------------------------------------------.
158
158
  # Remove timesteps with no drops or NaN (from L2E computations)
159
- # timestep_zero_drops = ds["time"].data[ds["n_drops_selected"].data == 0]
160
- # timestep_nan = ds["time"].data[np.isnan(ds["n_drops_selected"].data)]
159
+ # timestep_zero_drops = ds["time"].data[ds["N"].data == 0]
160
+ # timestep_nan = ds["time"].data[np.isnan(ds["N"].data)]
161
161
  # TODO: Make it a choice !
162
162
  indices_valid_timesteps = np.where(
163
- ~np.logical_or(ds["n_drops_selected"].data == 0, np.isnan(ds["n_drops_selected"].data)),
163
+ ~np.logical_or(ds["N"].data == 0, np.isnan(ds["N"].data)),
164
164
  )[0]
165
165
  ds = ds.isel(time=indices_valid_timesteps)
166
166
 
@@ -30,7 +30,7 @@ from disdrodb.api.info import (
30
30
  from disdrodb.configs import get_metadata_archive_dir
31
31
  from disdrodb.metadata.reader import read_station_metadata
32
32
  from disdrodb.metadata.search import get_list_metadata
33
- from disdrodb.metadata.standards import get_valid_metadata_keys
33
+ from disdrodb.metadata.standards import METADATA_KEYS, METADATA_VALUES
34
34
  from disdrodb.utils.yaml import read_yaml
35
35
 
36
36
  #### --------------------------------------------------------------------------.
@@ -40,19 +40,17 @@ from disdrodb.utils.yaml import read_yaml
40
40
  def get_metadata_missing_keys(metadata):
41
41
  """Return the DISDRODB metadata keys which are missing."""
42
42
  keys = list(metadata.keys())
43
- valid_keys = get_valid_metadata_keys()
44
43
  # Identify missing keys
45
- idx_missing_keys = np.where(np.isin(valid_keys, keys, invert=True))[0]
46
- missing_keys = np.array(valid_keys)[idx_missing_keys].tolist()
44
+ idx_missing_keys = np.where(np.isin(METADATA_KEYS, keys, invert=True))[0]
45
+ missing_keys = np.array(METADATA_KEYS)[idx_missing_keys].tolist()
47
46
  return missing_keys
48
47
 
49
48
 
50
49
  def get_metadata_invalid_keys(metadata):
51
50
  """Return the DISDRODB metadata keys which are not valid."""
52
51
  keys = list(metadata.keys())
53
- valid_keys = get_valid_metadata_keys()
54
52
  # Identify invalid keys
55
- idx_invalid_keys = np.where(np.isin(keys, valid_keys, invert=True))[0]
53
+ idx_invalid_keys = np.where(np.isin(keys, METADATA_KEYS, invert=True))[0]
56
54
  invalid_keys = np.array(keys)[idx_invalid_keys].tolist()
57
55
  return invalid_keys
58
56
 
@@ -73,11 +71,22 @@ def _check_metadata_values(metadata):
73
71
  """Check validity of metadata values.
74
72
 
75
73
  If null is specified in the YAML files (or None in the dict) raise error.
74
+ For specific keys, check that values match one of the allowed options in METADATA_VALUES.
76
75
  """
77
76
  for key, value in metadata.items():
77
+ # Check for None/null values
78
78
  if isinstance(value, type(None)):
79
79
  raise ValueError(f"The metadata key {key} has None or null value. Use '' instead.")
80
80
 
81
+ # Check that values match allowed options for specific keys
82
+ if key in METADATA_VALUES:
83
+ allowed_values = METADATA_VALUES[key]
84
+ if value not in allowed_values:
85
+ allowed_str = ", ".join([f"'{v}'" for v in allowed_values])
86
+ raise ValueError(
87
+ f"Invalid value '{value}' for metadata key '{key}'. " f"Allowed values are: {allowed_str}.",
88
+ )
89
+
81
90
 
82
91
  def _check_metadata_campaign_name(metadata, expected_name):
83
92
  """Check metadata ``campaign_name``."""
@@ -41,8 +41,8 @@ def add_missing_metadata_keys(metadata):
41
41
 
42
42
  def sort_metadata_dictionary(metadata):
43
43
  """Sort the keys of the metadata dictionary by ``valid_metadata_keys`` list order."""
44
- from disdrodb.metadata.standards import get_valid_metadata_keys
44
+ from disdrodb.metadata.standards import METADATA_KEYS
45
45
 
46
- list_metadata_keys = get_valid_metadata_keys()
46
+ list_metadata_keys = METADATA_KEYS
47
47
  metadata = {k: metadata[k] for k in list_metadata_keys}
48
48
  return metadata
@@ -16,85 +16,89 @@
16
16
  # You should have received a copy of the GNU General Public License
17
17
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
18
  # -----------------------------------------------------------------------------.
19
- """Define DISDRODB Metadata Standards."""
19
+ """Define the DISDRODB Metadata standards.
20
20
 
21
+ When editing one of these variables, one need to update all YAML files in
22
+ the DISDRODB-METADATA repository
23
+ """
21
24
 
22
- def get_valid_metadata_keys() -> list:
23
- """Get DISDRODB valid metadata list.
25
+ # Define valid values for specific metadata keys
26
+ METADATA_VALUES = {
27
+ "deployment_status": ["ongoing", "terminated"],
28
+ "platform_type": ["fixed", "mobile"],
29
+ "deployment_mode": ["land", "ship", "truck", "cable"],
30
+ "platform_protection": ["shielded", "unshielded", ""],
31
+ }
24
32
 
25
- Returns
26
- -------
27
- list
28
- List of valid metadata keys
29
- """
30
- # NOTE: When updating one of these keys, one need to update the yaml files in/at:
31
- # - the DISDRODB-METADATA repository
32
- # - disdrodb/data/DISDRODB/RAW/DATA_SOURCE/CAMPAIGN_NAME/metadata/*.yml
33
- list_attrs = [
34
- ## Mandatory fields
35
- "data_source",
36
- "campaign_name",
37
- "station_name",
38
- "sensor_name",
39
- "measurement_interval", # sampling_interval ? [in seconds]
40
- # DISDRODB reader info
41
- "reader",
42
- "raw_data_glob_pattern",
43
- "raw_data_format", # 'txt', 'netcdf'
44
- "platform_type", # 'fixed', 'mobile'
45
- ## DISDRODB keys
46
- "disdrodb_data_url",
47
- ## Source
48
- "source",
49
- "source_convention",
50
- "source_processing_date",
51
- ## Description
52
- "title",
53
- "description",
54
- "project_name",
55
- "keywords",
56
- "summary",
57
- "history",
58
- "comment",
59
- "station_id",
60
- "location",
61
- "country",
62
- "continent",
63
- ## Deployment Info
64
- "latitude", # in degrees North
65
- "longitude", # in degrees East
66
- "altitude", # in meter above sea level
67
- "deployment_status", # 'ended', 'ongoing'
68
- "deployment mode", # 'land', 'ship', 'truck', 'cable'
69
- "platform_protection", # 'shielded', 'unshielded'
70
- "platform_orientation", # [0-360] from N (clockwise)
71
- ## Sensor info
72
- "sensor_long_name",
73
- "sensor_manufacturer",
74
- "sensor_wavelength",
75
- "sensor_serial_number",
76
- "firmware_iop",
77
- "firmware_dsp",
78
- "firmware_version",
79
- "sensor_beam_length",
80
- "sensor_beam_width",
81
- "sensor_nominal_width",
82
- "calibration_sensitivity",
83
- "calibration_certification_date",
84
- "calibration_certification_url",
85
- ## Attribution
86
- "contributors",
87
- "authors",
88
- "authors_url",
89
- "contact",
90
- "contact_information",
91
- "acknowledgement", # acknowledgements?
92
- "references",
93
- "documentation",
94
- "website",
95
- "institution",
96
- "source_repository",
97
- "license",
98
- "doi",
99
- ]
100
- return list_attrs
33
+
34
+ METADATA_KEYS = [
35
+ ## Mandatory fields
36
+ "data_source",
37
+ "campaign_name",
38
+ "station_name",
39
+ "sensor_name",
40
+ # DISDRODB reader info
41
+ "reader",
42
+ "raw_data_glob_pattern",
43
+ "raw_data_format", # 'txt', 'netcdf'
44
+ "measurement_interval", # sampling_interval ? [in seconds]
45
+ ## Deployment Info
46
+ "deployment_status", # 'terminated', 'ongoing'
47
+ "deployment_mode", # 'land', 'ship', 'truck', 'cable'
48
+ "platform_type", # 'fixed', 'mobile'
49
+ "latitude", # in degrees North
50
+ "longitude", # in degrees East
51
+ "altitude", # in meter above sea level
52
+ # Platform info
53
+ "platform_protection", # 'shielded', 'unshielded'
54
+ "platform_orientation", # [0-360] from N (clockwise)
55
+ ## Time info
56
+ "time_coverage_start", # YYYY-MM-DDTHH:MM:SS
57
+ "time_coverage_end", # YYYY-MM-DDTHH:MM:SS
58
+ ## DISDRODB data url
59
+ "disdrodb_data_url",
60
+ ## Source
61
+ "source",
62
+ "source_convention",
63
+ "source_processing_date",
64
+ ## Description
65
+ "title",
66
+ "description",
67
+ "project_name",
68
+ "keywords",
69
+ "summary",
70
+ "history",
71
+ "comment",
72
+ "station_id",
73
+ "location",
74
+ "country",
75
+ "continent",
76
+ ## Sensor info
77
+ "sensor_long_name",
78
+ "sensor_manufacturer",
79
+ "sensor_wavelength",
80
+ "sensor_serial_number",
81
+ "firmware_iop",
82
+ "firmware_dsp",
83
+ "firmware_version",
84
+ "sensor_beam_length",
85
+ "sensor_beam_width",
86
+ "sensor_nominal_width",
87
+ "calibration_sensitivity",
88
+ "calibration_certification_date",
89
+ "calibration_certification_url",
90
+ ## Attribution
91
+ "contributors",
92
+ "authors",
93
+ "authors_url",
94
+ "contact",
95
+ "contact_information",
96
+ "acknowledgement", # acknowledgements?
97
+ "references",
98
+ "documentation",
99
+ "website",
100
+ "institution",
101
+ "source_repository",
102
+ "license",
103
+ "doi",
104
+ ]
@@ -22,7 +22,7 @@ import os
22
22
 
23
23
  from disdrodb.api.path import define_metadata_filepath
24
24
  from disdrodb.metadata.manipulation import sort_metadata_dictionary
25
- from disdrodb.metadata.standards import get_valid_metadata_keys
25
+ from disdrodb.metadata.standards import METADATA_KEYS
26
26
  from disdrodb.utils.yaml import write_yaml
27
27
 
28
28
 
@@ -35,7 +35,7 @@ def get_default_metadata_dict() -> dict:
35
35
  Dictionary of attributes standard
36
36
  """
37
37
  # Get valid metadata keys
38
- list_attrs = get_valid_metadata_keys()
38
+ list_attrs = METADATA_KEYS
39
39
  attrs = dict.fromkeys(list_attrs, "")
40
40
 
41
41
  # Add default values for certain keys