pyadps 0.2.1b0__py3-none-any.whl → 0.3.1b0__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.
@@ -0,0 +1,958 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+
4
+ # import plotly.express as px
5
+ import plotly.graph_objects as go
6
+ import streamlit as st
7
+ from plotly.subplots import make_subplots
8
+ from plotly_resampler import FigureResampler
9
+ from utils.profile_test import side_lobe_beam_angle, manual_cut_bins
10
+ from utils.profile_test import regrid2d, regrid3d
11
+ from utils.signal_quality import default_mask
12
+
13
+ # If no file is uploaded, then give a warning
14
+ if "flead" not in st.session_state:
15
+ st.write(":red[Please Select Data!]")
16
+ st.stop()
17
+
18
+
19
+ def reset_profiletest():
20
+ # Reset Global Test
21
+ st.session_state.isProfileTest = False
22
+ # Reset Local Tests
23
+ st.session_state.isTrimEndsCheck = False
24
+ st.session_state.isCutBinSideLobeCheck = False
25
+ st.session_state.isCutBinManualCheck = False
26
+ st.session_state.isRegridCheck = False
27
+
28
+ # Reset Page Return
29
+ st.session_state.isQCPageReturn = False
30
+ if not st.session_state.isQCTest:
31
+ st.session_state.isSensorPageReturn = False
32
+
33
+ # Reset Data
34
+ st.session_state.echo_regrid = np.copy(st.session_state.echo)
35
+ st.session_state.correlation_regrid = np.copy(st.session_state.correlation)
36
+ st.session_state.pgood_regrid = np.copy(st.session_state.pgood)
37
+ if st.session_state.isVelocityModifiedSound:
38
+ st.session_state.velocity_regrid = np.copy(st.session_state.velocity_sensor)
39
+ st.session_state.velocity_temp = np.copy(st.session_state.velocity_sensor)
40
+ else:
41
+ st.session_state.velocity_regrid = np.copy(st.session_state.velocity)
42
+ st.session_state.velocity_temp = np.copy(st.session_state.velocity)
43
+
44
+ # Reset Mask Data
45
+ if st.session_state.isQCTest:
46
+ st.session_state.profile_mask_default = np.copy(st.session_state.qc_mask)
47
+ elif st.session_state.isSensorTest:
48
+ st.session_state.profile_mask_default = np.copy(st.session_state.sensor_mask)
49
+ else:
50
+ st.session_state.profile_mask_default = np.copy(st.session_state.orig_mask)
51
+
52
+ mask = st.session_state.profile_mask_default
53
+ st.session_state.profile_mask_temp = np.copy(mask)
54
+ st.session_state.profile_mask = np.copy(mask)
55
+ st.session_state.profile_mask_trimends = np.copy(mask)
56
+ st.session_state.profile_mask_sidelobe = np.copy(mask)
57
+ st.session_state.profile_mask_manual = np.copy(mask)
58
+ st.session_state.profile_mask_regrid = np.copy(mask)
59
+
60
+ st.session_state.sidelobe_displaymask = np.copy(mask)
61
+ st.session_state.manual_displaymask = np.copy(mask)
62
+
63
+
64
+ def hard_reset(option):
65
+ # Reset Global Test
66
+ st.session_state.isProfileTest = False
67
+
68
+ # Reset Local Tests
69
+ st.session_state.isTrimEndsCheck = False
70
+ st.session_state.isCutBinSideLobeCheck = False
71
+ st.session_state.isCutBinManualCheck = False
72
+ st.session_state.isRegridCheck = False
73
+
74
+ # Reset Page Return
75
+ st.session_state.isQCPageReturn = False
76
+ if not st.session_state.isQCTest:
77
+ st.session_state.isSensorPageReturn = False
78
+
79
+ # Reset Data
80
+ st.session_state.echo_regrid = np.copy(st.session_state.echo)
81
+ st.session_state.correlation_regrid = np.copy(st.session_state.correlation)
82
+ st.session_state.pgood_regrid = np.copy(st.session_state.pgood)
83
+ if st.session_state.isVelocityModifiedSound:
84
+ st.session_state.velocity_regrid = np.copy(st.session_state.velocity_sensor)
85
+ st.session_state.velocity_temp = np.copy(st.session_state.velocity_sensor)
86
+ else:
87
+ st.session_state.velocity_regrid = np.copy(st.session_state.velocity)
88
+ st.session_state.velocity_temp = np.copy(st.session_state.velocity)
89
+
90
+ # Reset Mask Data based on user options
91
+ if option == "Sensor Test":
92
+ st.session_state.profile_mask_default = np.copy(st.session_state.sensor_mask)
93
+ if option == "QC Test":
94
+ st.session_state.profile_mask_default = np.copy(st.session_state.qc_mask)
95
+ elif option == "Default":
96
+ st.session_state.profile_mask_default = np.copy(st.session_state.orig_mask)
97
+
98
+ st.session_state.profile_mask = np.copy(st.session_state.profile_mask_default)
99
+ st.session_state.profile_mask_temp = np.copy(st.session_state.profile_mask_default)
100
+ st.session_state.profile_mask_trimends = np.copy(
101
+ st.session_state.profile_mask_default
102
+ )
103
+ st.session_state.profile_mask_sidelobe = np.copy(
104
+ st.session_state.profile_mask_default
105
+ )
106
+ st.session_state.profile_mask_manual = np.copy(
107
+ st.session_state.profile_mask_default
108
+ )
109
+ st.session_state.profile_mask_regrid = np.copy(
110
+ st.session_state.profile_mask_default
111
+ )
112
+ st.session_state.profile_mask_displaymask = np.copy(
113
+ st.session_state.profile_mask_default
114
+ )
115
+
116
+
117
+ # Load data
118
+ ds = st.session_state.ds
119
+ flobj = st.session_state.flead
120
+ vlobj = st.session_state.vlead
121
+ echo = st.session_state.echo
122
+ correlation = st.session_state.correlation
123
+ pgood = st.session_state.pgood
124
+ fdata = flobj.fleader
125
+ vdata = vlobj.vleader
126
+
127
+ # Setting up parameters for plotting graphs.
128
+ ensembles = st.session_state.head.ensembles
129
+ cells = flobj.field()["Cells"]
130
+ beams = flobj.field()["Beams"]
131
+ cell_size = flobj.field()["Depth Cell Len"]
132
+ bin1dist = flobj.field()["Bin 1 Dist"]
133
+ beam_angle = int(flobj.system_configuration()["Beam Angle"])
134
+
135
+ x = np.arange(0, ensembles, 1)
136
+ y = np.arange(0, cells, 1)
137
+
138
+ # Regrided data
139
+ # if "velocity_regrid" not in st.session_state:
140
+ # st.session_state.echo_regrid = np.copy(echo)
141
+ # st.session_state.velocity_regrid = np.copy(velocity)
142
+ # st.session_state.correlation_regrid = np.copy(correlation)
143
+ # st.session_state.pgood_regrid = np.copy(pgood)
144
+ # st.session_state.mask_regrid = np.copy(mask)
145
+
146
+
147
+ # @st.cache_data
148
+ def fillplot_plotly(
149
+ data, title="data", maskdata=None, missing=-32768, colorscale="balance"
150
+ ):
151
+ fig = FigureResampler(go.Figure())
152
+ data = np.int32(data)
153
+ data1 = np.where(data == missing, np.nan, data)
154
+ fig.add_trace(
155
+ go.Heatmap(
156
+ z=data1,
157
+ x=x,
158
+ y=y,
159
+ colorscale=colorscale,
160
+ hoverongaps=False,
161
+ )
162
+ )
163
+ if maskdata is not None:
164
+ fig.add_trace(
165
+ go.Heatmap(
166
+ z=maskdata,
167
+ x=x,
168
+ y=y,
169
+ colorscale="gray",
170
+ hoverongaps=False,
171
+ showscale=False,
172
+ opacity=0.7,
173
+ )
174
+ )
175
+ fig.update_layout(
176
+ xaxis=dict(showline=True, mirror=True),
177
+ yaxis=dict(showline=True, mirror=True),
178
+ title_text=title,
179
+ )
180
+ fig.update_xaxes(title="Ensembles")
181
+ fig.update_yaxes(title="Depth Cells")
182
+ st.plotly_chart(fig)
183
+
184
+
185
+ def fillselect_plotly(data, title="data", colorscale="balance"):
186
+ fig = FigureResampler(go.Figure())
187
+ data = np.int32(data)
188
+ data1 = np.where(data == -32768, None, data)
189
+ fig.add_trace(
190
+ go.Heatmap(
191
+ z=data1,
192
+ x=x,
193
+ y=y,
194
+ colorscale=colorscale,
195
+ hoverongaps=False,
196
+ )
197
+ )
198
+ # fig.add_trace(
199
+ # go.Scatter(x=X, y=Y, marker=dict(color="black", size=16), mode="lines+markers")
200
+ # )
201
+ fig.update_layout(
202
+ xaxis=dict(showline=True, mirror=True),
203
+ yaxis=dict(showline=True, mirror=True),
204
+ title_text=title,
205
+ )
206
+ fig.update_xaxes(title="Ensembles")
207
+ fig.update_yaxes(title="Depth Cells")
208
+ fig.update_layout(clickmode="event+select")
209
+ event = st.plotly_chart(fig, key="1", on_select="rerun", selection_mode="box")
210
+
211
+ return event
212
+
213
+
214
+ @st.cache_data
215
+ def trim_ends(start_ens=0, end_ens=0, ens_range=20):
216
+ depth = vdata["Depth of Transducer"] / 10
217
+ fig = make_subplots(
218
+ rows=1,
219
+ cols=2,
220
+ subplot_titles=[
221
+ "Deployment Ensemble",
222
+ "Recovery Ensemble",
223
+ ],
224
+ )
225
+ fig.add_trace(
226
+ go.Scatter(
227
+ x=x[0:ens_range],
228
+ y=depth[0:ens_range],
229
+ name="Deployment",
230
+ mode="markers",
231
+ marker=dict(color="#1f77b4"),
232
+ ),
233
+ row=1,
234
+ col=1,
235
+ )
236
+
237
+ fig.add_trace(
238
+ go.Scatter(
239
+ x=x[-1 * ens_range :],
240
+ y=depth[-1 * ens_range :],
241
+ name="Recovery",
242
+ mode="markers",
243
+ marker=dict(color="#17becf"),
244
+ ),
245
+ row=1,
246
+ col=2,
247
+ )
248
+
249
+ if start_ens > x[0]:
250
+ fig.add_trace(
251
+ go.Scatter(
252
+ x=x[0:start_ens],
253
+ y=depth[0:start_ens],
254
+ name="Selected Points (D)",
255
+ mode="markers",
256
+ marker=dict(color="red"),
257
+ ),
258
+ row=1,
259
+ col=1,
260
+ )
261
+
262
+ if end_ens < x[-1] + 1:
263
+ fig.add_trace(
264
+ go.Scatter(
265
+ x=x[end_ens : x[-1] + 1],
266
+ y=depth[end_ens : x[-1] + 1],
267
+ name="Selected Points (R)",
268
+ mode="markers",
269
+ marker=dict(color="orange"),
270
+ ),
271
+ row=1,
272
+ col=2,
273
+ )
274
+
275
+ fig.update_layout(height=600, width=800, title_text="Transducer depth")
276
+ fig.update_xaxes(title="Ensembles")
277
+ fig.update_yaxes(title="Depth (m)")
278
+ st.plotly_chart(fig)
279
+
280
+
281
+ # Trim-end functions
282
+ def set_button_trimends():
283
+ # Trim ends only modifies Mask
284
+ mask = st.session_state.profile_mask_default
285
+ if st.session_state.trimends_start_ens > 0:
286
+ mask[:, : st.session_state.trimends_start_ens] = 1
287
+
288
+ if st.session_state.trimends_end_ens <= x[-1]:
289
+ mask[:, st.session_state.trimends_end_ens :] = 1
290
+
291
+ st.session_state.profile_mask_trimends = np.copy(mask)
292
+ st.session_state.profile_mask_temp = np.copy(mask)
293
+ st.session_state.isTrimEndsCheck = True
294
+
295
+
296
+ def reset_button_trimends():
297
+ # Trim ends only modifies Mask
298
+ st.session_state.isTrimEndsCheck = False
299
+ st.session_state.profile_mask_trimends = st.session_state.profile_mask_default
300
+ st.session_state.profile_mask_temp = st.session_state.profile_mask_default
301
+
302
+
303
+ # Side-lobe functions
304
+ def set_button_apply_sidelobe():
305
+ inmask = np.copy(st.session_state.profile_mask_temp)
306
+ transdepth = st.session_state.depth
307
+ water_column_depth = st.session_state.profile_water_column_depth
308
+ extra_cells = st.session_state.profile_extra_cells
309
+ mask = side_lobe_beam_angle(
310
+ transdepth,
311
+ inmask,
312
+ orientation=orientation,
313
+ water_column_depth=water_column_depth,
314
+ extra_cells=extra_cells,
315
+ cells=cells,
316
+ cell_size=cell_size,
317
+ bin1dist=bin1dist,
318
+ beam_angle=beam_angle,
319
+ )
320
+ st.session_state.sidelobe_displaymask = np.copy(mask)
321
+
322
+
323
+ def set_button_sidelobe():
324
+ st.session_state.isCutBinSideLobeCheck = True
325
+ st.session_state.profile_mask_temp = np.copy(st.session_state.sidelobe_displaymask)
326
+ st.session_state.profile_mask_sidelobe = np.copy(st.session_state.profile_mask_temp)
327
+
328
+
329
+ def reset_button_sidelobe():
330
+ st.session_state.isCutBinSideLobeCheck = False
331
+ if st.session_state.isTrimEndsCheck:
332
+ st.session_state.profile_mask_temp = st.session_state.profile_mask_trimends
333
+ else:
334
+ st.session_state.profile_mask_temp = st.session_state.profile_mask_default
335
+
336
+ st.session_state.profile_mask_sidelobe = np.copy(st.session_state.profile_mask_temp)
337
+ st.session_state.sidelobe_displaymask = np.copy(st.session_state.profile_mask_temp)
338
+
339
+
340
+ # Cutbins Manual Functions
341
+ def set_button_apply_mask_region():
342
+ mask = np.copy(st.session_state.profile_mask_temp)
343
+
344
+ min_cell = st.session_state.profile_min_cell
345
+ max_cell = st.session_state.profile_max_cell
346
+ min_ensemble = st.session_state.profile_min_ensemble
347
+ max_ensemble = st.session_state.profile_max_ensemble
348
+
349
+ mask = manual_cut_bins(mask, min_cell, max_cell, min_ensemble, max_ensemble)
350
+ st.session_state.manual_displaymask = np.copy(mask)
351
+ st.session_state.profile_mask_temp = np.copy(mask)
352
+ st.session_state.profile_mask_manual = np.copy(mask)
353
+
354
+ st.session_state.isCutBinManualCheck = True
355
+
356
+
357
+ def set_button_mask_region():
358
+ st.session_state.isCutBinManualCheck = True
359
+ st.session_state.profile_mask_manual = np.copy(st.session_state.profile_mask_temp)
360
+
361
+
362
+ def set_button_delete_cell():
363
+ cell = st.session_state.profile_delete_cell
364
+ mask = st.session_state.profile_mask_temp
365
+ mask[cell, :] = 1 # Mask the entire row for the selected cell
366
+ st.session_state.profile_mask_temp = np.copy(mask)
367
+ st.session_state.profile_mask_manual = np.copy(mask)
368
+
369
+ st.session_state.isCutBinManualCheck = True
370
+
371
+
372
+ def set_button_delete_ensemble():
373
+ ensemble = st.session_state.profile_delete_ensemble
374
+ mask = st.session_state.profile_mask_temp
375
+ mask[:, ensemble - 1] = 1 # Mask the entire column for the selected ensemble
376
+ st.session_state.profile_mask_temp = np.copy(mask)
377
+ st.session_state.profile_mask_manual = np.copy(mask)
378
+ st.session_state.isCutBinManualCheck = True
379
+
380
+
381
+ def reset_button_mask_manual():
382
+ st.session_state.isCutBinManualCheck = False
383
+ # st.session_state.isCutBinManualOn = False
384
+
385
+ if st.session_state.isCutBinSideLobeCheck:
386
+ st.session_state.profile_mask_temp = st.session_state.profile_mask_sidelobe
387
+ elif st.session_state.isTrimEndsCheck:
388
+ st.session_state.profile_mask_temp = st.session_state.profile_mask_trimends
389
+ else:
390
+ st.session_state.profile_mask_temp = st.session_state.profile_mask_default
391
+
392
+ # Copy to local mask
393
+ st.session_state.profile_mask_manual = np.copy(st.session_state.profile_mask_temp)
394
+
395
+
396
+ # Regrid functions
397
+ def reset_button_regrid():
398
+ st.session_state.isRegridCheck = False
399
+
400
+ # Reset to previous state
401
+ if st.session_state.isCutBinManualCheck:
402
+ st.session_state.profile_mask_temp = st.session_state.profile_mask_manual
403
+ elif st.session_state.isCutBinSideLobeCheck:
404
+ st.session_state.profile_mask_temp = st.session_state.profile_mask_sidelobe
405
+ elif st.session_state.isTrimEndsCheck:
406
+ st.session_state.profile_mask_temp = st.session_state.profile_mask_trimends
407
+ else:
408
+ st.session_state.profile_mask_temp = st.session_state.profile_mask_default
409
+
410
+ # Copy to local mask
411
+ st.session_state.profile_mask_regrid = np.copy(st.session_state.profile_mask_temp)
412
+
413
+ # Reset Data
414
+ if st.session_state.isVelocityModifiedSound:
415
+ st.session_state.velocity_regrid = np.copy(st.session_state.velocity_sensor)
416
+ else:
417
+ st.session_state.velocity_regrid = np.copy(st.session_state.velocity)
418
+ st.session_state.echo_regrid = np.copy(st.session_state.echo)
419
+ st.session_state.velocity_regrid = np.copy(st.session_state.velocity)
420
+ st.session_state.correlation_regrid = np.copy(st.session_state.correlation)
421
+ st.session_state.pgood_regrid = np.copy(st.session_state.pgood)
422
+
423
+
424
+ def save_profiletest():
425
+ if st.session_state.isRegridCheck:
426
+ st.session_state.profile_mask = st.session_state.profile_mask_regrid
427
+ else:
428
+ st.session_state.profile_mask = st.session_state.profile_mask_temp
429
+ # st.session_state.velocity_regrid = np.copy(st.session_state.velocity_temp)
430
+ st.session_state.isProfileTest = True
431
+ st.session_state.isVelocityTest = False
432
+
433
+ st.session_state.isSensorPageReturn = True
434
+ st.session_state.isQCPageReturn = True
435
+
436
+
437
+ # Giving infromations and warnings according to the state of masks.
438
+ if st.session_state.isQCTest or st.session_state.isSensorTest:
439
+ st.write(":grey[Working on a saved mask file ...]")
440
+ if st.session_state.isProfilePageReturn:
441
+ st.write(
442
+ ":orange[Warning: Profile test already completed. Reset to change settings.]"
443
+ )
444
+ reset_selectbox = st.selectbox(
445
+ "Choose reset option",
446
+ ("Sensor Test", "QC Test", "Default"),
447
+ index=None,
448
+ placeholder="Reset mask to ...",
449
+ )
450
+ # Selecting the original mask file.
451
+ if reset_selectbox == "Default":
452
+ st.write("Default mask file selected")
453
+ elif reset_selectbox == "Sensor Test":
454
+ st.write("Sensor Test mask file selected")
455
+ elif reset_selectbox == "QC Test":
456
+ st.write("QC Test mask file selected")
457
+ if reset_selectbox is not None:
458
+ hard_reset(reset_selectbox)
459
+ elif st.session_state.isFirstProfileVisit:
460
+ reset_profiletest()
461
+ st.session_state.isFirstProfileVisit = False
462
+ else:
463
+ if st.session_state.isFirstProfileVisit:
464
+ reset_profiletest()
465
+ st.session_state.isFirstProfileVisit = False
466
+ st.write(":orange[Creating a new mask file ...]")
467
+
468
+ st.header("Profile Test")
469
+ # Creating tabs for each actions
470
+ tab1, tab2, tab3, tab4, tab5 = st.tabs(
471
+ [
472
+ "Trim Ends",
473
+ "Cut Bins - Sidelobe",
474
+ "Cut Bins - Manual",
475
+ "Regriding",
476
+ "Save & Reset",
477
+ ]
478
+ )
479
+
480
+
481
+ ############## TRIM ENDS #################
482
+ with tab1:
483
+ st.header("Trim Ends", divider="blue")
484
+ ens_range = st.number_input("Change range", x[0], x[-1], 20)
485
+ start_ens = st.slider("Deployment Ensembles", 0, ens_range, 0)
486
+ end_ens = st.slider("Recovery Ensembles", x[-1] - ens_range, x[-1] + 1, x[-1] + 1)
487
+
488
+ n = int(ens_range)
489
+ if start_ens or end_ens:
490
+ trim_ends(start_ens=int(start_ens), end_ens=int(end_ens), ens_range=n)
491
+ # st.session_state.update_mask = False
492
+
493
+ st.session_state.trimends_ens_range = int(ens_range)
494
+ st.session_state.trimends_start_ens = start_ens
495
+ st.session_state.trimends_end_ens = end_ens
496
+ st.session_state.trimends_endpoints = np.array(
497
+ [st.session_state.trimends_start_ens, st.session_state.trimends_end_ens]
498
+ )
499
+
500
+ left_te, right_te = st.columns([1, 1])
501
+ with left_te:
502
+ trimends_mask_button = st.button("Trim Ends", on_click=set_button_trimends)
503
+
504
+ # Display output
505
+ if trimends_mask_button:
506
+ st.success("Mask data updated")
507
+ st.write("Trim End Points", st.session_state.trimends_endpoints)
508
+ else:
509
+ st.write(":red[mask data not updated]")
510
+
511
+ with right_te:
512
+ trimends_reset_button = st.button(
513
+ "Reset Trim Ends", on_click=reset_button_trimends
514
+ )
515
+ if trimends_reset_button:
516
+ st.success("Mask data reset to default.")
517
+
518
+
519
+ ############ CUT BINS (SIDE LOBE) ############################
520
+ with tab2:
521
+ st.header("Cut Bins: Side Lobe Contamination", divider="blue")
522
+ st.write(
523
+ """
524
+ The side lobe echos from hard surface such as sea surface or bottom of the ocean can contaminate
525
+ data closer to this region. The data closer to the surface or bottom can be removed using
526
+ the relation between beam angle and the thickness of the contaminated layer.
527
+ """
528
+ )
529
+
530
+ left1, right1 = st.columns([1, 1])
531
+
532
+ with left1:
533
+ orientation = st.session_state.beam_direction
534
+ st.write(f"The orientation is `{orientation}`.")
535
+ beam = st.radio("Select beam", (1, 2, 3, 4), horizontal=True)
536
+ beam = beam - 1
537
+ st.session_state.beam = beam
538
+
539
+ with right1:
540
+ st.session_state.profile_water_column_depth = 0
541
+
542
+ st.session_state.profile_extra_cells = st.number_input(
543
+ "Additional Cells to Delete", 0, 10, 0
544
+ )
545
+ if orientation.lower() == "down":
546
+ water_column_depth = st.number_input(
547
+ "Enter water column depth (m): ", 0, 15000, 0
548
+ )
549
+ side_lobe_button = st.button(
550
+ label="Cut bins", on_click=set_button_apply_sidelobe
551
+ )
552
+
553
+ left2, right2 = st.columns([1, 1])
554
+
555
+ with left2:
556
+ # if st.session_state.isCutBinSideLobeCheck:
557
+ # fillplot_plotly(
558
+ # echo[beam, :, :],
559
+ # title="Echo Intensity (Masked)",
560
+ # maskdata=st.session_state.profile_mask_temp,
561
+ # )
562
+ # else:
563
+ fillplot_plotly(
564
+ echo[beam, :, :],
565
+ maskdata=st.session_state.sidelobe_displaymask,
566
+ title="Echo Intensity",
567
+ )
568
+
569
+ with right2:
570
+ fillplot_plotly(
571
+ st.session_state.sidelobe_displaymask,
572
+ colorscale="greys",
573
+ title="Mask Data ",
574
+ )
575
+
576
+ left3sl, right3sl = st.columns([1, 1])
577
+ with left3sl:
578
+ update_mask_cutbin_button = st.button(
579
+ "Update Mask (Side Lobe)", on_click=set_button_sidelobe
580
+ )
581
+ if update_mask_cutbin_button:
582
+ st.success("Mask file updated")
583
+
584
+ with right3sl:
585
+ reset_mask_cutbin_button = st.button(
586
+ "Reset Mask (Side lobe)", on_click=reset_button_sidelobe
587
+ )
588
+ if reset_mask_cutbin_button:
589
+ st.info("Mask File Reset (Side Lobe)")
590
+
591
+ if not update_mask_cutbin_button:
592
+ st.write(":red[mask file not updated]")
593
+
594
+
595
+ ########### CUT BINS: Manual #################
596
+ with tab3:
597
+ st.header("Cut Bins: Manual", divider="blue")
598
+
599
+ left3, right3 = st.columns([1, 2])
600
+ with left3:
601
+ # User selects beam (1-4)
602
+ beam = st.radio(
603
+ "Select beam", (1, 2, 3, 4), horizontal=True, key="beam_selection"
604
+ )
605
+ beam_index = beam - 1
606
+ st.subheader("Mask Selected Regions")
607
+ # with st.form(key="manual_cutbin_form"):
608
+ st.write("Select the specific range of cells and ensembles to delete")
609
+
610
+ # Input for selecting minimum and maximum cells
611
+ st.session_state.profile_min_cell = st.number_input(
612
+ "Min Cell", 0, int(flobj.field()["Cells"]), 0
613
+ )
614
+ st.session_state.profile_max_cell = st.number_input(
615
+ "Max Cell", 0, int(flobj.field()["Cells"]), int(flobj.field()["Cells"])
616
+ )
617
+
618
+ # Input for selecting minimum and maximum ensembles
619
+ st.session_state.profile_min_ensemble = st.number_input(
620
+ "Min Ensemble", 0, int(flobj.ensembles), 0
621
+ )
622
+ st.session_state.profile_max_ensemble = st.number_input(
623
+ "Max Ensemble", 0, int(flobj.ensembles), int(flobj.ensembles)
624
+ )
625
+
626
+ # Submit button to apply the mask
627
+ cut_bins_mask_manual = st.button(
628
+ label="Apply Manual Cut Bins", on_click=set_button_apply_mask_region
629
+ )
630
+
631
+ # Adding the new feature: Delete Single Cell or Ensemble
632
+ st.subheader("Delete Specific Cell or Ensemble")
633
+
634
+ # Step 1: User chooses between deleting a cell or an ensemble
635
+ delete_option = st.radio(
636
+ "Select option to delete", ("Cell", "Ensemble"), horizontal=True
637
+ )
638
+
639
+ # Step 2: Display options based on user's choice
640
+ if delete_option == "Cell":
641
+ # Option to delete a specific cell across all ensembles
642
+ st.write("Select a specific cell to delete across all ensembles")
643
+
644
+ # Input for selecting a single cell
645
+ st.session_state.profile_delete_cell = st.number_input(
646
+ "Cell", 0, int(flobj.field()["Cells"]), 0, key="single_cell"
647
+ )
648
+
649
+ # Submit button to apply the mask for cell deletion
650
+ delete_cell_button = st.button(
651
+ label="Delete Cell", on_click=set_button_delete_cell
652
+ )
653
+
654
+ if delete_cell_button:
655
+ st.write("Deleted cell: ", st.session_state.profile_delete_cell)
656
+
657
+ if delete_option == "Ensemble":
658
+ # Option to delete a specific ensemble across all cells
659
+ st.write("Select a specific ensemble to delete across all cells")
660
+
661
+ # Input for selecting a specific ensemble
662
+ st.session_state.profile_delete_ensemble = st.number_input(
663
+ "Ensemble", 0, int(flobj.ensembles), 0, key="single_ensemble"
664
+ )
665
+
666
+ # Submit button to apply the mask for ensemble deletion
667
+ delete_ensemble_button = st.button(
668
+ label="Delete Ensemble", on_click=set_button_delete_ensemble
669
+ )
670
+
671
+ if delete_ensemble_button:
672
+ st.write("Deleted Ensemble: ", st.session_state.profile_delete_ensemble)
673
+
674
+ velocity = st.session_state.velocity_temp
675
+ # Map variable selection to corresponding data
676
+ data_dict = {
677
+ "Velocity": velocity,
678
+ "Echo Intensity": echo,
679
+ "Correlation": correlation,
680
+ "Percentage Good": pgood,
681
+ }
682
+
683
+ with right3:
684
+ # Selection of variable (Velocity, Echo Intensity, etc.)
685
+ variable = st.selectbox(
686
+ "Select Variable to Display",
687
+ ("Velocity", "Echo Intensity", "Correlation", "Percentage Good"),
688
+ )
689
+ # Display the selected variable and beam
690
+ selected_data = data_dict[variable][beam_index, :, :]
691
+ fillplot_plotly(
692
+ selected_data,
693
+ title=variable + "(Masked Manually)",
694
+ maskdata=st.session_state.profile_mask_temp,
695
+ )
696
+ # else:
697
+ # fillplot_plotly(selected_data, title=f"{variable}")
698
+ fillplot_plotly(
699
+ st.session_state.profile_mask_temp,
700
+ colorscale="greys",
701
+ title="Mask Data",
702
+ )
703
+
704
+ # Layout with two columns
705
+ col1, col2 = st.columns([1, 3])
706
+
707
+ # Button to reset the mask data, with unique key
708
+ reset_mask_button = st.button(
709
+ "Reset Cut Bins Manual",
710
+ key="reset_mask_button",
711
+ on_click=reset_button_mask_manual,
712
+ )
713
+ if reset_mask_button:
714
+ st.info("Cut Bins Manual Reset. Mask data is changed to previous state.")
715
+
716
+ ############ REGRID ###########################################
717
+ with tab4:
718
+ st.header("Regrid Depth Cells", divider="blue")
719
+
720
+ st.write(
721
+ """
722
+ When the ADCP buoy has vertical oscillations (greater than depth cell size),
723
+ the depth bins has to be regridded based on the pressure sensor data. The data
724
+ can be regrided either till the surface or till the last bin.
725
+ If the `Cell` option is selected, ensure that the end data are trimmed.
726
+ Manual option permits choosing the end cell depth.
727
+ """
728
+ )
729
+
730
+ left4, right4 = st.columns([1, 3])
731
+
732
+ with left4:
733
+ if st.session_state.beam_direction.lower() == "up":
734
+ end_bin_option = st.radio(
735
+ "Select the depth of last bin for regridding",
736
+ ("Cell", "Surface", "Manual"),
737
+ horizontal=True,
738
+ )
739
+ else:
740
+ end_bin_option = st.radio(
741
+ "Select the depth of last bin for regridding",
742
+ ("Cell", "Manual"),
743
+ horizontal=True,
744
+ )
745
+
746
+ st.session_state.end_bin_option = end_bin_option
747
+ st.write(f"You have selected: `{end_bin_option}`")
748
+
749
+ if end_bin_option == "Manual":
750
+ mean_depth = (
751
+ np.mean(st.session_state.vlead.vleader["Depth of Transducer"]) / 10
752
+ )
753
+ mean_depth = round(mean_depth, 2)
754
+
755
+ st.write(
756
+ f"The transducer depth is {mean_depth} m. The value should not exceed the transducer depth"
757
+ )
758
+ if st.session_state.beam_direction.lower() == "up":
759
+ boundary = st.number_input(
760
+ "Enter the depth (m):", max_value=int(mean_depth), min_value=0
761
+ )
762
+ else:
763
+ boundary = st.number_input(
764
+ "Enter the depth (m):", min_value=int(mean_depth)
765
+ )
766
+ else:
767
+ boundary = 0
768
+
769
+ interpolate = st.radio(
770
+ "Choose interpolation method:", ("nearest", "linear", "cubic")
771
+ )
772
+
773
+ progress_text = "Regridding in progress. Please wait."
774
+ grid_bar = st.progress(0, text=progress_text)
775
+
776
+ regrid_button = st.button(label="Regrid Data")
777
+ if regrid_button:
778
+ transdepth = st.session_state.depth
779
+ z, st.session_state.velocity_regrid = regrid3d(
780
+ transdepth,
781
+ st.session_state.velocity_temp,
782
+ -32768,
783
+ trimends=st.session_state.trimends_endpoints,
784
+ end_bin_option=st.session_state.end_bin_option,
785
+ orientation=st.session_state.beam_direction,
786
+ method=interpolate,
787
+ boundary_limit=boundary,
788
+ cells=cells,
789
+ cell_size=cell_size,
790
+ bin1dist=bin1dist,
791
+ beams=beams,
792
+ )
793
+ grid_bar.progress(20, text=progress_text)
794
+ st.write(":grey[Regrided velocity ...]")
795
+ z, st.session_state.echo_regrid = regrid3d(
796
+ transdepth,
797
+ echo,
798
+ -32768,
799
+ trimends=st.session_state.trimends_endpoints,
800
+ end_bin_option=st.session_state.end_bin_option,
801
+ orientation=st.session_state.beam_direction,
802
+ method=interpolate,
803
+ boundary_limit=boundary,
804
+ cells=cells,
805
+ cell_size=cell_size,
806
+ bin1dist=bin1dist,
807
+ beams=beams,
808
+ )
809
+ grid_bar.progress(40, text=progress_text)
810
+ st.write(":grey[Regrided echo intensity ...]")
811
+ z, st.session_state.correlation_regrid = regrid3d(
812
+ transdepth,
813
+ correlation,
814
+ -32768,
815
+ trimends=st.session_state.trimends_endpoints,
816
+ end_bin_option=st.session_state.end_bin_option,
817
+ orientation=st.session_state.beam_direction,
818
+ method=interpolate,
819
+ boundary_limit=boundary,
820
+ cells=cells,
821
+ cell_size=cell_size,
822
+ bin1dist=bin1dist,
823
+ beams=beams,
824
+ )
825
+ grid_bar.progress(60, text=progress_text)
826
+ st.write(":grey[Regrided correlation...]")
827
+ z, st.session_state.pgood_regrid = regrid3d(
828
+ transdepth,
829
+ pgood,
830
+ -32768,
831
+ trimends=st.session_state.trimends_endpoints,
832
+ end_bin_option=st.session_state.end_bin_option,
833
+ orientation=st.session_state.beam_direction,
834
+ method=interpolate,
835
+ boundary_limit=boundary,
836
+ cells=cells,
837
+ cell_size=cell_size,
838
+ bin1dist=bin1dist,
839
+ beams=beams,
840
+ )
841
+ grid_bar.progress(80, text=progress_text)
842
+ st.write(":grey[Regrided percent good...]")
843
+
844
+ z, st.session_state.profile_mask_regrid = regrid2d(
845
+ transdepth,
846
+ st.session_state.profile_mask_temp,
847
+ 1,
848
+ trimends=st.session_state.trimends_endpoints,
849
+ end_bin_option=st.session_state.end_bin_option,
850
+ orientation=st.session_state.beam_direction,
851
+ method="nearest",
852
+ boundary_limit=boundary,
853
+ cells=cells,
854
+ cell_size=cell_size,
855
+ bin1dist=bin1dist,
856
+ )
857
+
858
+ grid_bar.progress(99, text=progress_text)
859
+ st.write(":grey[Regrided mask...]")
860
+
861
+ st.session_state.depth_axis = z
862
+ st.write(":grey[New depth axis created...]")
863
+
864
+ grid_bar.progress(100, text="Completed")
865
+ st.write(":green[All data regrided!]")
866
+
867
+ st.write(
868
+ "No. of grid depth bins before regridding: ", np.shape(velocity)[1]
869
+ )
870
+ st.write(
871
+ "No. of grid depth bins after regridding: ",
872
+ np.shape(st.session_state.velocity_regrid)[1],
873
+ )
874
+ st.session_state.isGrid = True
875
+ st.session_state.isRegridCheck = True
876
+
877
+ regrid_reset_button = st.button(
878
+ "Reset Regrid Test", on_click=reset_button_regrid
879
+ )
880
+
881
+ if regrid_reset_button:
882
+ st.info("Data Reset")
883
+
884
+ with right4:
885
+ if st.session_state.isRegridCheck:
886
+ fillplot_plotly(
887
+ st.session_state.velocity_regrid[0, :, :],
888
+ title="Regridded Velocity File",
889
+ maskdata=st.session_state.profile_mask_regrid,
890
+ )
891
+ fillplot_plotly(
892
+ st.session_state.profile_mask_regrid,
893
+ colorscale="greys",
894
+ title="Regridded Mask File",
895
+ )
896
+ else:
897
+ fillplot_plotly(
898
+ velocity[0, :, :],
899
+ maskdata=st.session_state.profile_mask_temp,
900
+ title="Original File",
901
+ )
902
+
903
+
904
+ ########### Save and Reset Mask ##############
905
+ with tab5:
906
+ st.header("Save & Reset Mask Data", divider="blue")
907
+
908
+ col1, col2 = st.columns([1, 1])
909
+ with col1:
910
+ save_mask_button = st.button(label="Save Mask Data", on_click=save_profiletest)
911
+ if save_mask_button:
912
+ st.success("Mask data saved")
913
+ # Table summarizing changes
914
+ changes_summary = pd.DataFrame(
915
+ [
916
+ [
917
+ "Trim Ends",
918
+ "True" if st.session_state.isTrimEndsCheck else "False",
919
+ ],
920
+ [
921
+ "Cut Bins: Side Lobe Contamination",
922
+ "True" if st.session_state.isCutBinSideLobeCheck else "False",
923
+ ],
924
+ [
925
+ "Cut Bins: Manual",
926
+ "True" if st.session_state.isCutBinManualCheck else "False",
927
+ ],
928
+ [
929
+ "Regrid Depth Cells",
930
+ "True" if st.session_state.isRegridCheck else "False",
931
+ ],
932
+ ],
933
+ columns=["Parameter", "Status"],
934
+ )
935
+
936
+ # Define a mapping function for styling
937
+ def status_color_map(value):
938
+ if value == "True":
939
+ return "background-color: green; color: white"
940
+ elif value == "False":
941
+ return "background-color: red; color: white"
942
+ else:
943
+ return ""
944
+
945
+ # Apply styles using Styler.apply
946
+ styled_table = changes_summary.style.set_properties(
947
+ **{"text-align": "center"}
948
+ )
949
+ styled_table = styled_table.map(status_color_map, subset=["Status"])
950
+
951
+ # Display the styled table
952
+ st.write(styled_table.to_html(), unsafe_allow_html=True)
953
+ else:
954
+ st.write(":red[Mask data not saved]")
955
+ with col2:
956
+ reset_mask_button = st.button("Reset mask data", on_click=reset_profiletest)
957
+ if reset_mask_button:
958
+ st.info("Mask data is reset to default")