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