pyadps 0.2.1b0__py3-none-any.whl → 0.3.0__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 (39) hide show
  1. pyadps/Home_Page.py +11 -5
  2. pyadps/pages/01_Read_File.py +623 -215
  3. pyadps/pages/02_View_Raw_Data.py +97 -41
  4. pyadps/pages/03_Download_Raw_File.py +200 -67
  5. pyadps/pages/04_Sensor_Health.py +905 -0
  6. pyadps/pages/05_QC_Test.py +493 -0
  7. pyadps/pages/06_Profile_Test.py +971 -0
  8. pyadps/pages/07_Velocity_Test.py +600 -0
  9. pyadps/pages/08_Write_File.py +623 -0
  10. pyadps/pages/09_Add-Ons.py +168 -0
  11. pyadps/utils/__init__.py +5 -3
  12. pyadps/utils/autoprocess.py +371 -80
  13. pyadps/utils/logging_utils.py +269 -0
  14. pyadps/utils/metadata/config.ini +22 -4
  15. pyadps/utils/metadata/demo.000 +0 -0
  16. pyadps/utils/metadata/flmeta.json +420 -420
  17. pyadps/utils/metadata/vlmeta.json +611 -565
  18. pyadps/utils/multifile.py +292 -0
  19. pyadps/utils/plotgen.py +505 -3
  20. pyadps/utils/profile_test.py +720 -125
  21. pyadps/utils/pyreadrdi.py +164 -92
  22. pyadps/utils/readrdi.py +436 -186
  23. pyadps/utils/script.py +197 -147
  24. pyadps/utils/sensor_health.py +120 -0
  25. pyadps/utils/signal_quality.py +472 -68
  26. pyadps/utils/velocity_test.py +79 -31
  27. pyadps/utils/writenc.py +222 -39
  28. {pyadps-0.2.1b0.dist-info → pyadps-0.3.0.dist-info}/METADATA +13 -14
  29. pyadps-0.3.0.dist-info/RECORD +35 -0
  30. {pyadps-0.2.1b0.dist-info → pyadps-0.3.0.dist-info}/WHEEL +1 -1
  31. {pyadps-0.2.1b0.dist-info → pyadps-0.3.0.dist-info}/entry_points.txt +1 -0
  32. pyadps/pages/04_QC_Test.py +0 -334
  33. pyadps/pages/05_Profile_Test.py +0 -575
  34. pyadps/pages/06_Velocity_Test.py +0 -341
  35. pyadps/pages/07_Write_File.py +0 -452
  36. pyadps/utils/cutbin.py +0 -413
  37. pyadps/utils/regrid.py +0 -279
  38. pyadps-0.2.1b0.dist-info/RECORD +0 -31
  39. {pyadps-0.2.1b0.dist-info → pyadps-0.3.0.dist-info}/LICENSE +0 -0
pyadps/utils/cutbin.py DELETED
@@ -1,413 +0,0 @@
1
- import matplotlib as mpl
2
- import matplotlib.pyplot as plt
3
- import numpy as np
4
- from matplotlib.widgets import Button, RadioButtons, RectangleSelector, Slider
5
-
6
- from pyadps.utils import readrdi as rd
7
-
8
-
9
- class CutBins:
10
- def __init__(
11
- self, data, mask, newmask=False, t1=0, t2=200, tinc=500, z1=0, z2=-1, zinc=0
12
- ):
13
- # DATA SETUP
14
- self.orig_data = data
15
- self.orig_shape = np.shape(self.orig_data)
16
- self.fill = 999
17
- self.maskarray = mask
18
- if not newmask:
19
- self.orig_data[self.maskarray == 1] = self.fill
20
-
21
- self.t1, self.t2, self.tinc = t1, t2, tinc
22
- self.z1, self.z2, self.zinc = z1, z2, zinc
23
- if z2 == -1:
24
- self.z2 = self.orig_shape[0]
25
-
26
- self.data = self.orig_data[self.z1 : self.z2, self.t1 : self.t2]
27
- self.orig_subset = self.orig_data[self.z1 : self.z2, self.t1 : self.t2]
28
- self.datacopy = np.copy(self.orig_data)
29
- self.datamin = np.min(self.orig_data)
30
- self.datamax = np.max(self.orig_data)
31
- self.shape = np.shape(self.data)
32
-
33
- # PLOT SETUP
34
- self.t = np.arange(self.t1, self.t2)
35
- self.z = np.arange(self.z1, self.z2)
36
- self.tickinterval = int((self.t2 - self.t1) / 5)
37
- self.xticks = np.arange(self.t1, self.t2, self.tickinterval)
38
- self.X, self.Y = np.meshgrid(self.t, self.z)
39
- self.fig, self.axs = plt.subplot_mosaic(
40
- [["a", "b"], ["c", "b"]],
41
- figsize=(12, 10),
42
- width_ratios=[2, 1],
43
- height_ratios=[1.75, 1],
44
- )
45
- self.fig.set_facecolor("darkgrey")
46
- plt.subplots_adjust(top=0.82, right=0.95)
47
-
48
- # ADDING WIDGET AXES
49
- self.ax_clear_button = self.fig.add_axes(rect=(0.125, 0.90, 0.08, 0.025))
50
- self.ax_delete_button = self.fig.add_axes(rect=(0.225, 0.90, 0.08, 0.025))
51
- self.ax_refill_button = self.fig.add_axes(rect=(0.325, 0.90, 0.08, 0.025))
52
- self.ax_next_button = self.fig.add_axes(rect=(0.630, 0.65, 0.02, 0.050))
53
- self.ax_previous_button = self.fig.add_axes(rect=(0.075, 0.65, 0.02, 0.050))
54
- self.ax_radio_button = self.fig.add_axes(rect=(0.725, 0.87, 0.10, 0.10))
55
- self.ax_exit_button = self.fig.add_axes(rect=(0.825, 0.025, 0.08, 0.035))
56
- self.ax_hslider = self.fig.add_axes(rect=(0.125, 0.85, 0.50, 0.03))
57
- self.ax_vslider = self.fig.add_axes(rect=(0.04, 0.25, 0.03, 0.50))
58
-
59
- self.ax_delete_button.set_visible(False)
60
- self.ax_refill_button.set_visible(False)
61
-
62
- # --- Slider settings ---
63
- # Initial slider settings
64
- self.hevent = 0
65
- self.vevent = 0
66
-
67
- # Slider options
68
- self.hslider = Slider(
69
- ax=self.ax_hslider,
70
- label="Ensemble",
71
- valmin=self.t1,
72
- valmax=self.t2,
73
- valinit=self.hevent,
74
- valfmt="%i",
75
- valstep=1,
76
- )
77
-
78
- self.vslider = Slider(
79
- ax=self.ax_vslider,
80
- label="Bins",
81
- valmin=self.z1,
82
- valmax=self.z2,
83
- valinit=self.vevent,
84
- valfmt="%i",
85
- valstep=1,
86
- orientation="vertical",
87
- )
88
-
89
- # Button Labels
90
- self.clear_button = Button(self.ax_clear_button, "Clear")
91
- self.delete_button = Button(self.ax_delete_button, "Delete")
92
- self.refill_button = Button(self.ax_refill_button, "Refill")
93
- self.previous_button = Button(self.ax_previous_button, "<")
94
- self.next_button = Button(self.ax_next_button, ">")
95
- self.exit_button = Button(self.ax_exit_button, "Save & Exit")
96
- # self.cell_button = Button(self.ax_cell_button, "Cell")
97
- # self.ensemble_button = Button(self.ax_ensemble_button, "Ensemble")
98
- self.radio_button = RadioButtons(
99
- self.ax_radio_button, ("Bin", "Ensemble", "Cell", "Region")
100
- )
101
-
102
- # --------------PLOTS---------------------
103
-
104
- # Settings colorbar extreme to black
105
- cmap = mpl.cm.turbo.with_extremes(over="k")
106
- # FILL PLOT
107
- self.mesh = self.axs["a"].pcolormesh(
108
- self.X, self.Y, self.data, cmap=cmap, picker=True, vmin=0, vmax=255
109
- )
110
- plt.colorbar(self.mesh, orientation="horizontal")
111
- self.axs["a"].set_xlim([self.t1, self.t2])
112
- self.axs["a"].set_ylim([self.z1, self.z2])
113
- # Draw vertical and horizontal lines
114
- (self.vline,) = self.axs["a"].plot(
115
- [self.t1, self.t1], [self.z1, self.z2], color="r", linewidth=2.5
116
- )
117
- (self.hline,) = self.axs["a"].plot(
118
- [self.t1, self.t2], [self.z1, self.z1], color="r", linewidth=2.5
119
- )
120
-
121
- # PROFILE
122
- (self.profile,) = self.axs["b"].plot(
123
- self.data[self.z1 : self.z2, self.t1 + self.hevent], range(self.z1, self.z2)
124
- )
125
-
126
- self.axs["b"].set_xlim([self.datamin, self.datamax])
127
- self.profile_text = self.axs["b"].text(
128
- 0.95,
129
- 0.95,
130
- f"Ensemble No.: {self.t1 + self.hevent}",
131
- verticalalignment="bottom",
132
- horizontalalignment="right",
133
- transform=self.axs["b"].transAxes,
134
- color="k",
135
- fontsize=12,
136
- )
137
-
138
- # TIME SERIES
139
- (self.tseries,) = self.axs["c"].plot(
140
- range(self.t1, self.t2), self.data[self.z1 + self.vevent, self.t1 : self.t2]
141
- )
142
- self.axs["c"].set_ylim([self.datamin, self.datamax])
143
- self.tseries_text = self.axs["c"].text(
144
- 0.90,
145
- 0.90,
146
- f"Bin No.: {self.z1 + self.vevent}",
147
- verticalalignment="bottom",
148
- horizontalalignment="right",
149
- transform=self.axs["c"].transAxes,
150
- color="k",
151
- fontsize=12,
152
- )
153
- # --------------END PLOTS---------------------
154
-
155
- # EVENTS
156
- self.onclick = self.onclick_bin
157
- self.hslider.on_changed(self.hupdate)
158
- self.vslider.on_changed(self.vupdate)
159
- self.clear_button.on_clicked(self.clear)
160
- self.radio_button.on_clicked(self.radio)
161
- self.cid = self.fig.canvas.mpl_connect("pick_event", self.onclick)
162
-
163
- self.delete_button.on_clicked(self.boxdelete)
164
- self.refill_button.on_clicked(self.boxrefill)
165
- self.next_button.on_clicked(self.next)
166
- self.previous_button.on_clicked(self.previous)
167
- self.exit_button.on_clicked(self.exit)
168
-
169
- def next(self, event):
170
- if self.t2 <= self.orig_shape[1]:
171
- # Next works till the last subset. The if statement checks for last subset.
172
- self.t1 = self.t1 + self.tinc
173
- self.t2 = self.t2 + self.tinc
174
- if self.t2 > (self.orig_shape[1]):
175
- # If in last subset create a dummy data set with missing value.
176
- self.data = self.datacopy[
177
- self.z1 : self.z2, self.t1 : self.orig_shape[1]
178
- ]
179
- self.orig_subset = self.orig_data[
180
- self.z1 : self.z2, self.t1 : self.orig_shape[1]
181
- ]
182
- self.missing = (
183
- np.ones((self.z2 - self.z1, self.t2 - self.orig_shape[1]))
184
- * self.fill
185
- )
186
- # self.data consist of data along with flagged value
187
- self.data = np.append(self.data, self.missing, axis=1)
188
- # self.orig_subset contains only the subset of the original data
189
- # Useful for plotting time series and profiles
190
- self.orig_subset = np.append(self.orig_subset, self.missing, axis=1)
191
- else:
192
- self.data = self.datacopy[self.z1 : self.z2, self.t1 : self.t2]
193
- self.orig_subset = self.orig_data[self.z1 : self.z2, self.t1 : self.t2]
194
-
195
- self.mesh.set_array(self.data)
196
- self.tick = np.arange(self.t1, self.t2, self.tickinterval)
197
- self.axs["a"].set_xticks(self.xticks, self.tick)
198
-
199
- self.profile.set_xdata(self.orig_subset[:, self.hevent])
200
- self.profile_text.set_text(f"Ensemble No.: {self.t1 + self.hevent}")
201
- self.vline.set_xdata([self.hevent, self.hevent])
202
-
203
- self.tseries.set_ydata(self.orig_subset[self.vevent, :])
204
- self.tseries_text.set_text(f"Bin No.: {self.z1 + self.vevent}")
205
- self.hline.set_ydata([self.vevent, self.vevent])
206
-
207
- self.fig.canvas.draw()
208
-
209
- def previous(self, event):
210
- if self.t1 >= self.tinc:
211
- self.t1 = self.t1 - self.tinc
212
- self.t2 = self.t2 - self.tinc
213
- self.tick = np.arange(self.t1, self.t2, self.tickinterval)
214
- self.data = self.datacopy[self.z1 : self.z2, self.t1 : self.t2]
215
- self.axs["a"].set_xticks(self.xticks, self.tick)
216
- self.mesh.set_array(self.data)
217
-
218
- # Reset sliders
219
- self.profile.set_xdata(self.orig_data[self.z1 : self.z2, self.hevent])
220
- self.profile_text.set_text(f"Ensemble No.: {self.hevent}")
221
- self.vline.set_xdata([self.hevent, self.hevent])
222
-
223
- self.tseries.set_ydata(self.orig_data[self.vevent, self.t1 : self.t2])
224
- self.tseries_text.set_text(f"Bin No.: {self.z1 + self.vevent}")
225
- self.hline.set_ydata([self.vevent, self.vevent])
226
-
227
- self.fig.canvas.draw()
228
-
229
- def radio(self, event):
230
- self.fig.canvas.mpl_disconnect(self.cid)
231
- if event == "Bin":
232
- self.cid = self.fig.canvas.mpl_connect("pick_event", self.onclick_bin)
233
- elif event == "Ensemble":
234
- self.cid = self.fig.canvas.mpl_connect("pick_event", self.onclick_ens)
235
- elif event == "Cell":
236
- self.cid = self.fig.canvas.mpl_connect("pick_event", self.onclick_cell)
237
- else:
238
- self.rid = RectangleSelector(
239
- self.axs["a"],
240
- self.onclick_box,
241
- useblit=True,
242
- minspanx=2,
243
- minspany=2,
244
- interactive=True,
245
- )
246
-
247
- def clear(self, event):
248
- if event.button == 1:
249
- self.datacopy = np.copy(self.orig_data)
250
- if self.t2 >= (self.orig_shape[1]):
251
- test = self.datacopy[self.z1 : self.z2, self.t1 : self.t2]
252
- test = np.append(test, self.missing, axis=1)
253
- else:
254
- test = self.datacopy[self.z1 : self.z2, self.t1 : self.t2]
255
-
256
- # self.mesh.set_array(self.datacopy[self.z1 : self.z2, self.t1 : self.t2])
257
- self.mesh.set_array(test)
258
- self.fig.canvas.draw()
259
-
260
- def hupdate(self, event):
261
- self.hevent = event
262
- self.profile.set_xdata(self.orig_subset[:, self.hevent])
263
- self.profile_text.set_text(f"Ensemble No.: {self.t1 + self.hevent}")
264
- self.vline.set_xdata([self.hevent, self.hevent])
265
-
266
- def vupdate(self, event):
267
- self.vevent = event
268
- self.tseries.set_ydata(self.orig_subset[self.vevent, :])
269
- self.tseries_text.set_text(f"Bin No.: {self.z1 + self.vevent}")
270
- self.hline.set_ydata([self.vevent, self.vevent])
271
-
272
- def onclick_bin(self, event):
273
- ind = event.ind
274
- x = ind // (self.t[-1] + 1)
275
- # y = ind % (self.t[-1] + 1)
276
- xx = self.z1 + x
277
- # yy = self.t1 + y
278
- if np.all(self.datacopy[xx, :] == self.fill):
279
- self.datacopy[xx, :] = np.copy(self.orig_data[xx, :])
280
-
281
- else:
282
- self.datacopy[xx, :] = self.fill
283
-
284
- if self.t2 >= (self.orig_shape[1]):
285
- test = self.datacopy[self.z1 : self.z2, self.t1 : self.t2]
286
- test = np.append(test, self.missing, axis=1)
287
- else:
288
- test = self.datacopy[self.z1 : self.z2, self.t1 : self.t2]
289
-
290
- # self.mesh.set_array(self.datacopy[self.z1 : self.z2, self.t1 : self.t2])
291
- self.mesh.set_array(test)
292
- self.hline.set_ydata([x, x])
293
- self.vslider.set_val(x[0])
294
- self.fig.canvas.draw()
295
-
296
- def onclick_ens(self, event):
297
- ind = event.ind
298
- if np.size(ind) != 1:
299
- return
300
- # x = ind // (self.t[-1] + 1)
301
- y = ind % (self.t[-1] + 1)
302
- yy = self.t1 + y
303
-
304
- if yy < self.orig_shape[1]:
305
- if np.all(self.datacopy[:, yy] == self.fill):
306
- self.datacopy[:, yy] = np.copy(self.orig_data[:, yy])
307
- else:
308
- self.datacopy[:, yy] = self.fill
309
-
310
- if self.t2 >= (self.orig_shape[1]):
311
- test = self.datacopy[self.z1 : self.z2, self.t1 : self.t2]
312
- test = np.append(test, self.missing, axis=1)
313
- else:
314
- test = self.datacopy[self.z1 : self.z2, self.t1 : self.t2]
315
- # self.mesh.set_array(self.datacopy[self.z1 : self.z2, self.t1 : self.t2])
316
- self.mesh.set_array(test)
317
- self.hline.set_xdata([y, y])
318
- self.hslider.set_val(y[0])
319
- self.fig.canvas.draw()
320
-
321
- def onclick_cell(self, event):
322
- ind = event.ind
323
- if np.size(ind) != 1:
324
- return
325
- x = ind // (self.t[-1] + 1)
326
- y = ind % (self.t[-1] + 1)
327
- xx = self.z1 + x
328
- yy = self.t1 + y
329
-
330
- if yy < self.orig_shape[1]:
331
- if self.datacopy[xx, yy] == self.fill:
332
- self.datacopy[xx, yy] = np.copy(self.orig_data[x, y])
333
- else:
334
- self.datacopy[xx, yy] = self.fill
335
-
336
- if self.t2 > (self.orig_shape[1]):
337
- test = self.datacopy[self.z1 : self.z2, self.t1 : self.t2]
338
- test = np.append(test, self.missing, axis=1)
339
- else:
340
- test = self.datacopy[self.z1 : self.z2, self.t1 : self.t2]
341
-
342
- # self.mesh.set_array(self.datacopy[self.z1 : self.z2, self.t1 : self.t2])
343
- self.mesh.set_array(test)
344
- self.vline.set_xdata([y, y])
345
- self.hline.set_ydata([x, x])
346
- self.hslider.set_val(y[0])
347
- self.vslider.set_val(x[0])
348
- self.fig.canvas.draw()
349
-
350
- def onclick_box(self, eclick, erelease):
351
- self.ax_delete_button.set_visible(True)
352
- self.ax_refill_button.set_visible(True)
353
- plt.gcf().canvas.draw()
354
- self.x11, self.y11 = int(eclick.xdata), int(eclick.ydata)
355
- self.x22, self.y22 = int(erelease.xdata) + 1, int(erelease.ydata) + 1
356
-
357
- print(
358
- f"({self.x11:3.2f}, {self.y11:3.2f}) --> ({self.x22:3.2f}, {self.y22:3.2f})"
359
- )
360
- print(f" The buttons you used were: {eclick.button} {erelease.button}")
361
-
362
- def boxdelete(self, event):
363
- z1 = self.z1 + self.y11 + 1
364
- z2 = self.z1 + self.y22
365
- t1 = self.t1 + self.x11 + 1
366
- t2 = self.t1 + self.x22
367
- self.datacopy[z1:z2, t1:t2] = self.fill
368
-
369
- if self.t2 > (self.orig_shape[1]):
370
- test = self.datacopy[self.z1 : self.z2, self.t1 : self.t2]
371
- test = np.append(test, self.missing, axis=1)
372
- else:
373
- test = self.datacopy[self.z1 : self.z2, self.t1 : self.t2]
374
-
375
- # self.mesh.set_array(self.datacopy[self.z1 : self.z2, self.t1 : self.t2])
376
- self.mesh.set_array(test)
377
- self.fig.canvas.draw()
378
-
379
- def boxrefill(self, event):
380
- z1 = self.z1 + self.y11 + 1
381
- z2 = self.z1 + self.y22
382
- t1 = self.t1 + self.x11 + 1
383
- t2 = self.t1 + self.x22
384
- self.datacopy[z1:z2, t1:t2] = self.orig_data[z1:z2, t1:t2]
385
-
386
- if self.t2 > (self.orig_shape[1]):
387
- test = self.datacopy[self.z1 : self.z2, self.t1 : self.t2]
388
- test = np.append(test, self.missing, axis=1)
389
- else:
390
- test = self.datacopy[self.z1 : self.z2, self.t1 : self.t2]
391
- # self.mesh.set_array(self.datacopy[self.z1 : self.z2, self.t1 : self.t2])
392
- self.mesh.set_array(test)
393
- self.fig.canvas.draw()
394
-
395
- def exit(self, event):
396
- plt.close()
397
-
398
- def mask(self):
399
- self.maskarray[self.datacopy == self.fill] = 1
400
- return self.maskarray
401
-
402
-
403
- # filename = "BGS11000.000"
404
- # ds = rd.echo(filename, run="fortran")
405
- # echo = ds[0, :, :]
406
- # shape = echo.shape
407
- # mask = np.zeros(shape)
408
-
409
- # manual = CutBins(echo, mask)
410
- # plt.show()
411
- # mask = manual.mask()
412
- # plt.pcolormesh(mask)
413
- # plt.show()
pyadps/utils/regrid.py DELETED
@@ -1,279 +0,0 @@
1
- import numpy as np
2
- import scipy as sp
3
-
4
- # import readrdi as rd
5
-
6
-
7
- def regrid2d(
8
- flobj,
9
- vlobj,
10
- data,
11
- fill_value,
12
- end_bin_option="cell",
13
- trimends=None,
14
- method="nearest",
15
- orientation="default",
16
- boundary_limit=0,
17
- ):
18
- """
19
- Regrids 2D data onto a new grid based on specified parameters.
20
-
21
- Parameters:
22
- -----------
23
- flobj : object
24
- The fixed leader object that contains information
25
- about the fixed leader data.
26
-
27
- vlobj : object
28
- The variable leader object that contains information
29
- about the variable leader data.
30
-
31
- data : array-like
32
- The 2D data array to be regridded.
33
-
34
- fill_value : scalar
35
- The value used to fill missing or undefined grid points.
36
-
37
- end_bin_option : str or float, optional, default="cell"
38
- The depth of the last bin or boundary for the grid.
39
- Options include:
40
- - "cell" : Calculates the depth of the default last bin for the grid.
41
- Truncates to surface for upward ADCP.
42
- - "surface": The data is gridded till the surface
43
- - "manual": User-defined depth for the grid.
44
- Use boundary_limit option to provide the value.
45
- otherwise, a specific numerical depth value can be provided.
46
-
47
- trimends : tuple of floats, optional, default=None
48
- If provided, defines the ensemble range (start, end) for
49
- calculating the maximum/minimum transducer depth.
50
- Helps avoiding the deployment or retrieval data.
51
- E.g. (10, 3000)
52
-
53
- method : str, optional, default="nearest"
54
- The interpolation method to use for regridding based
55
- on scipy.interpolate.interp1d.
56
- Options include:
57
- - "nearest" : Nearest neighbor interpolation.
58
- - "linear" : Linear interpolation.
59
- - "cubic" : Cubic interpolation.
60
-
61
- orientation : str, optional, default="up"
62
- Defines the direction of the regridding for an upward/downward looking ADCP. Options include:
63
- - "up" : Regrid upwards (for upward-looking ADCP).
64
- - "down" : Regrid downwards (for downward-looking ADCP).
65
-
66
- boundary_limit : float, optional, default=0
67
- The limit for the boundary depth. This restricts the grid regridding to depths beyond the specified limit.
68
-
69
- Returns:
70
- --------
71
- z: regridded depth
72
- regridded_data : array-like
73
- The regridded 2D data array, based on the specified method,
74
- orientation, and other parameters.
75
-
76
- Notes:
77
- ------
78
- - If `end_bin_option == boundary`, then `boundary_limit` is used to regrid the data.
79
- - This function allows for flexible regridding of 2D data to fit a new grid, supporting different interpolation methods.
80
- - The `boundary_limit` parameter helps restrict regridding to depths above or below a certain threshold.
81
- """
82
-
83
- # Get values and convert to 'm'
84
- bin1dist = flobj.field()["Bin 1 Dist"] / 100
85
- transdepth = vlobj.vleader["Depth of Transducer"] / 10
86
- depth_interval = flobj.field()["Depth Cell Len"] / 100
87
- bins = flobj.field()["Cells"]
88
- ensembles = flobj.ensembles
89
-
90
- if orientation.lower() == "default":
91
- orientation = flobj.system_configuration()['Beam Direction']
92
-
93
- if orientation.lower() == "up":
94
- sgn = -1
95
- else:
96
- sgn = 1
97
-
98
- # Create a regular grid
99
-
100
- # Find depth of first cell
101
- depth = transdepth + sgn*bin1dist
102
-
103
- # Find the maximum and minimum depth for first cell for upward
104
- # looking ADCP (minimum and maximum for downward looking)
105
- if trimends is not None:
106
- max_depth = abs(np.min(sgn*depth[trimends[0] : trimends[1]]))
107
- min_depth = abs(np.max(sgn*depth[trimends[0] : trimends[1]]))
108
- else:
109
- max_depth = abs(np.min(sgn*depth))
110
- min_depth = abs(np.max(sgn*depth))
111
-
112
- # FIRST CELL
113
- # Convert the first cell depth to the first regular grid depth
114
- depthfirstcell = max_depth - max_depth % depth_interval
115
-
116
- # LAST CELL
117
- # Convert the last cell depth to last regular grid depth
118
- if end_bin_option.lower() == "surface":
119
- # Added one additional negative cell to accomodate 0 m.
120
- depthlastcell = sgn * depth_interval
121
- elif end_bin_option.lower() == "cell":
122
- min_depth_regrid = min_depth - sgn*min_depth % depth_interval
123
- depthlastcell = min_depth_regrid + sgn* (bins+1) * depth_interval
124
- # Check if this is required. Use 'surface' option
125
- if depthlastcell < 0:
126
- depthlastcell = sgn*depth_interval
127
- elif end_bin_option.lower() == "manual":
128
- if sgn < 0 and boundary_limit > depthfirstcell:
129
- print("ERROR: For upward looking ADCP, boundary limit should be less than transducer depth")
130
- return
131
- if sgn > 0 and boundary_limit < depthfirstcell:
132
- print("ERROR: For downward looking ADCP, boundary limit should be greater than transducer depth")
133
- return
134
- # Set the last grid cell depth
135
- depthlastcell = boundary_limit
136
- else:
137
- print("ERROR: `end_bin_option` not recognized.")
138
- return
139
-
140
- # Negative used for upward and positive for downward.
141
- z = np.arange(sgn * depthfirstcell, sgn * depthlastcell, depth_interval)
142
- regbins = len(z)
143
-
144
- regridded_data = np.zeros((regbins, ensembles))
145
-
146
- # Create original depth array
147
- for i, d in enumerate(depth):
148
- n = d + sgn*depth_interval * bins
149
- # np.arange may include unexpected elements due to floating-point
150
- # precision issues at the stopping point. Changed to np.linspace.
151
- #
152
- # depth_bins = np.arange(sgn*d, sgn*n, depth_interval)
153
- depth_bins = np.linspace(sgn*d, sgn*n, bins)
154
- f = sp.interpolate.interp1d(
155
- depth_bins,
156
- data[:, i],
157
- kind=method,
158
- fill_value=fill_value,
159
- bounds_error=False,
160
- )
161
- gridz = f(z)
162
-
163
- regridded_data[:, i] = gridz
164
-
165
- return abs(z), regridded_data
166
-
167
-
168
- def regrid3d(
169
- flobj,
170
- vlobj,
171
- data,
172
- fill_value,
173
- end_bin_option="cell",
174
- trimends=None,
175
- method="nearest",
176
- orientation="up",
177
- boundary_limit=0,
178
- ):
179
- """
180
- Regrids 3D data onto a new grid based on specified parameters.
181
-
182
- Parameters:
183
- -----------
184
- flobj : object
185
- The fixed leader object that contains information
186
- about the fixed leader data.
187
-
188
- vlobj : object
189
- The variable leader object that contains information
190
- about the variable leader data.
191
-
192
- data : array-like
193
- The 3D data array to be regridded, with dimensions
194
- typically representing time, depth, and another axis (e.g., ensembles).
195
-
196
- fill_value : scalar
197
- The value used to fill missing or undefined grid points.
198
-
199
- end_bin_option : str or float, optional, default="cell"
200
- The depth of the last bin or boundary for the grid.
201
- Options include:
202
- - "cell" : Calculates the depth of the default last bin for the grid.
203
- Truncates to surface for upward ADCP.
204
- - "surface" : The data is gridded till the surface.
205
- - "manual" : User-defined depth for the grid.
206
- Use boundary_limit option to provide the value.
207
- Otherwise, a specific numerical depth value can be provided.
208
-
209
- trimends : tuple of floats, optional, default=None
210
- If provided, defines the ensemble range (start, end) for
211
- calculating the maximum/minimum transducer depth.
212
- Helps avoiding the deployment or retrieval data.
213
- E.g., (10, 3000)
214
-
215
- method : str, optional, default="nearest"
216
- The interpolation method to use for regridding based
217
- on scipy.interpolate.interp1d.
218
- Options include:
219
- - "nearest" : Nearest neighbor interpolation.
220
- - "linear" : Linear interpolation.
221
- - "cubic" : Cubic interpolation.
222
-
223
- orientation : str, optional, default="up"
224
- Defines the direction of the regridding for an upward/downward looking ADCP. Options include:
225
- - "up" : Regrid upwards (for upward-looking ADCP).
226
- - "down" : Regrid downwards (for downward-looking ADCP).
227
-
228
- boundary_limit : float, optional, default=0
229
- The limit for the boundary depth. This restricts the grid regridding to depths beyond the specified limit.
230
-
231
- Returns:
232
- --------
233
- z : array-like
234
- The regridded depth array.
235
- regridded_data : array-like
236
- The regridded 3D data array, based on the specified method,
237
- orientation, and other parameters.
238
-
239
- Notes:
240
- ------
241
- - If `end_bin_option == boundary`, then `boundary_limit` is used to regrid the data.
242
- - This function allows for flexible regridding of 3D data to fit a new grid, supporting different interpolation methods.
243
- - The `boundary_limit` parameter helps restrict regridding to depths above or below a certain threshold.
244
- - This function is an extension of 2D regridding to handle the time dimension or other additional axes in the data.
245
- """
246
-
247
- beams = flobj.field()["Beams"]
248
- z, data_dummy = regrid2d(
249
- flobj,
250
- vlobj,
251
- data[0, :, :],
252
- fill_value,
253
- end_bin_option=end_bin_option,
254
- trimends=trimends,
255
- method=method,
256
- orientation=orientation,
257
- boundary_limit=boundary_limit,
258
- )
259
-
260
- newshape = np.shape(data_dummy)
261
- regridded_data = np.zeros((beams, newshape[0], newshape[1]))
262
- regridded_data[0, :, :] = data_dummy
263
-
264
- for i in range(beams - 1):
265
- z, data_dummy = regrid2d(
266
- flobj,
267
- vlobj,
268
- data[i + 1, :, :],
269
- fill_value,
270
- end_bin_option=end_bin_option,
271
- trimends=trimends,
272
- method=method,
273
- orientation=orientation,
274
- boundary_limit=boundary_limit,
275
- )
276
- regridded_data[i + 1, :, :] = data_dummy
277
-
278
- return z, regridded_data
279
-