pyadps 0.3.3b0__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,970 @@
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_PT = False
24
+ st.session_state.isCutBinSideLobeCheck_PT = False
25
+ st.session_state.isCutBinManualCheck_PT = False
26
+ st.session_state.isRegridCheck_PT = 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_ST:
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_PT = False
70
+ st.session_state.isCutBinSideLobeCheck_PT = False
71
+ st.session_state.isCutBinManualCheck_PT = False
72
+ st.session_state.isRegridCheck_PT = 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_ST:
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.4,
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.start_ens_PT > 0:
286
+ mask[:, : st.session_state.start_ens_PT] = 1
287
+
288
+ if st.session_state.end_ens_PT <= x[-1]:
289
+ mask[:, st.session_state.end_ens_PT :] = 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_PT = True
294
+
295
+
296
+ def reset_button_trimends():
297
+ # Trim ends only modifies Mask
298
+ st.session_state.isTrimEndsCheck_PT = 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.water_depth_PT
308
+ extra_cells = st.session_state.extra_cell_PT
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_PT = 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_PT = False
331
+ if st.session_state.isTrimEndsCheck_PT:
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_PT = True
355
+
356
+
357
+ def set_button_mask_region():
358
+ st.session_state.isCutBinManualCheck_PT = 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_PT = 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_PT = True
379
+
380
+
381
+ def reset_button_mask_manual():
382
+ st.session_state.isCutBinManualCheck_PT = False
383
+ # st.session_state.isCutBinManualOn = False
384
+
385
+ if st.session_state.isCutBinSideLobeCheck_PT:
386
+ st.session_state.profile_mask_temp = st.session_state.profile_mask_sidelobe
387
+ elif st.session_state.isTrimEndsCheck_PT:
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_PT = False
399
+
400
+ # Reset to previous state
401
+ if st.session_state.isCutBinManualCheck_PT:
402
+ st.session_state.profile_mask_temp = st.session_state.profile_mask_manual
403
+ elif st.session_state.isCutBinSideLobeCheck_PT:
404
+ st.session_state.profile_mask_temp = st.session_state.profile_mask_sidelobe
405
+ elif st.session_state.isTrimEndsCheck_PT:
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_ST:
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_PT:
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
+ x1 = np.arange(0, ensembles, 1)
485
+ st.session_state.ens_range = st.number_input("Change range", x[0], x[-1], 20)
486
+ st.session_state.start_ens = st.slider(
487
+ "Deployment Ensembles", 0, st.session_state.ens_range, 0
488
+ )
489
+ st.session_state.end_ens = st.slider(
490
+ "Recovery Ensembles",
491
+ x1[-1] - st.session_state.ens_range,
492
+ x1[-1] + 1,
493
+ x1[-1] + 1,
494
+ )
495
+
496
+ if st.session_state.start_ens or st.session_state.end_ens:
497
+ trim_ends(
498
+ start_ens=int(st.session_state.start_ens),
499
+ end_ens=int(st.session_state.end_ens),
500
+ ens_range=int(st.session_state.ens_range),
501
+ )
502
+
503
+ st.session_state.start_ens_PT = st.session_state.start_ens
504
+ st.session_state.end_ens_PT = st.session_state.end_ens
505
+ st.session_state.trimends_endpoints = np.array(
506
+ [st.session_state.start_ens_PT, st.session_state.end_ens_PT]
507
+ )
508
+
509
+ left_te, right_te = st.columns([1, 1])
510
+ with left_te:
511
+ trimends_mask_button = st.button("Trim Ends", on_click=set_button_trimends)
512
+
513
+ # Display output
514
+ if trimends_mask_button:
515
+ st.success("Mask data updated")
516
+ st.write("Trim End Points", st.session_state.trimends_endpoints)
517
+ else:
518
+ st.write(":red[mask data not updated]")
519
+
520
+ with right_te:
521
+ trimends_reset_button = st.button(
522
+ "Reset Trim Ends", on_click=reset_button_trimends
523
+ )
524
+ if trimends_reset_button:
525
+ st.success("Mask data reset to default.")
526
+
527
+
528
+ ############ CUT BINS (SIDE LOBE) ############################
529
+ with tab2:
530
+ st.header("Cut Bins: Side Lobe Contamination", divider="blue")
531
+ st.write(
532
+ """
533
+ The side lobe echos from hard surface such as sea surface or bottom of the ocean can contaminate
534
+ data closer to this region. The data closer to the surface or bottom can be removed using
535
+ the relation between beam angle and the thickness of the contaminated layer.
536
+ """
537
+ )
538
+
539
+ left1, right1 = st.columns([1, 1])
540
+
541
+ with left1:
542
+ orientation = st.session_state.beam_direction_QCT
543
+ st.write(f"The orientation is `{orientation}`.")
544
+ beam = st.radio("Select beam", (1, 2, 3, 4), horizontal=True)
545
+ beam = beam - 1
546
+ st.session_state.beam = beam
547
+
548
+ with right1:
549
+ st.session_state.water_depth_PT = 0
550
+
551
+ st.session_state.extra_cell_PT = st.number_input(
552
+ "Additional Cells to Delete", 0, 10, 0
553
+ )
554
+ if orientation.lower() == "down":
555
+ water_column_depth = st.number_input(
556
+ "Enter water column depth (m): ", 0, 15000, 0
557
+ )
558
+ side_lobe_button = st.button(
559
+ label="Cut bins", on_click=set_button_apply_sidelobe
560
+ )
561
+
562
+ left2, right2 = st.columns([1, 1])
563
+
564
+ with left2:
565
+ # if st.session_state.isCutBinSideLobeCheck_PT:
566
+ # fillplot_plotly(
567
+ # echo[beam, :, :],
568
+ # title="Echo Intensity (Masked)",
569
+ # maskdata=st.session_state.profile_mask_temp,
570
+ # )
571
+ # else:
572
+ fillplot_plotly(
573
+ echo[beam, :, :],
574
+ maskdata=st.session_state.sidelobe_displaymask,
575
+ title="Echo Intensity",
576
+ )
577
+
578
+ with right2:
579
+ fillplot_plotly(
580
+ st.session_state.sidelobe_displaymask,
581
+ colorscale="greys",
582
+ title="Mask Data ",
583
+ )
584
+
585
+ left3sl, right3sl = st.columns([1, 1])
586
+ with left3sl:
587
+ update_mask_cutbin_button = st.button(
588
+ "Update Mask (Side Lobe)", on_click=set_button_sidelobe
589
+ )
590
+ if update_mask_cutbin_button:
591
+ st.success("Mask file updated")
592
+
593
+ with right3sl:
594
+ reset_mask_cutbin_button = st.button(
595
+ "Reset Mask (Side lobe)", on_click=reset_button_sidelobe
596
+ )
597
+ if reset_mask_cutbin_button:
598
+ st.info("Mask File Reset (Side Lobe)")
599
+
600
+ if not update_mask_cutbin_button:
601
+ st.write(":red[mask file not updated]")
602
+
603
+
604
+ ########### CUT BINS: Manual #################
605
+ with tab3:
606
+ st.header("Cut Bins: Manual", divider="blue")
607
+
608
+ left3, right3 = st.columns([1, 2])
609
+ with left3:
610
+ # User selects beam (1-4)
611
+ beam = st.radio(
612
+ "Select beam", (1, 2, 3, 4), horizontal=True, key="beam_selection"
613
+ )
614
+ beam_index = beam - 1
615
+ st.subheader("Mask Selected Regions")
616
+ # with st.form(key="manual_cutbin_form"):
617
+ st.write("Select the specific range of cells and ensembles to delete")
618
+
619
+ # Input for selecting minimum and maximum cells
620
+ st.session_state.profile_min_cell = st.number_input(
621
+ "Min Cell", 0, int(flobj.field()["Cells"]), 0
622
+ )
623
+ st.session_state.profile_max_cell = st.number_input(
624
+ "Max Cell", 0, int(flobj.field()["Cells"]), int(flobj.field()["Cells"])
625
+ )
626
+
627
+ st.write(st.session_state.profile_max_cell)
628
+ # Input for selecting minimum and maximum ensembles
629
+ st.session_state.profile_min_ensemble = st.number_input(
630
+ "Min Ensemble", 0, int(flobj.ensembles), 0
631
+ )
632
+ st.session_state.profile_max_ensemble = st.number_input(
633
+ "Max Ensemble", 0, int(flobj.ensembles), int(flobj.ensembles)
634
+ )
635
+
636
+ # Submit button to apply the mask
637
+ cut_bins_mask_manual = st.button(label="Apply Manual Cut Bins")
638
+ if cut_bins_mask_manual:
639
+ set_button_apply_mask_region()
640
+
641
+ # Adding the new feature: Delete Single Cell or Ensemble
642
+ st.subheader("Delete Specific Cell or Ensemble")
643
+
644
+ # Step 1: User chooses between deleting a cell or an ensemble
645
+ delete_option = st.radio(
646
+ "Select option to delete", ("Cell", "Ensemble"), horizontal=True
647
+ )
648
+
649
+ # Step 2: Display options based on user's choice
650
+ if delete_option == "Cell":
651
+ # Option to delete a specific cell across all ensembles
652
+ st.write("Select a specific cell to delete across all ensembles")
653
+
654
+ # Input for selecting a single cell
655
+ st.session_state.profile_delete_cell = st.number_input(
656
+ "Cell", 0, int(flobj.field()["Cells"]), 0, key="single_cell"
657
+ )
658
+
659
+ # Submit button to apply the mask for cell deletion
660
+ delete_cell_button = st.button(
661
+ label="Delete Cell", on_click=set_button_delete_cell
662
+ )
663
+
664
+ if delete_cell_button:
665
+ st.write("Deleted cell: ", st.session_state.profile_delete_cell)
666
+
667
+ if delete_option == "Ensemble":
668
+ # Option to delete a specific ensemble across all cells
669
+ st.write("Select a specific ensemble to delete across all cells")
670
+
671
+ # Input for selecting a specific ensemble
672
+ st.session_state.profile_delete_ensemble = st.number_input(
673
+ "Ensemble", 0, int(flobj.ensembles), 0, key="single_ensemble"
674
+ )
675
+
676
+ # Submit button to apply the mask for ensemble deletion
677
+ delete_ensemble_button = st.button(
678
+ label="Delete Ensemble", on_click=set_button_delete_ensemble
679
+ )
680
+
681
+ if delete_ensemble_button:
682
+ st.write("Deleted Ensemble: ", st.session_state.profile_delete_ensemble)
683
+
684
+ velocity = st.session_state.velocity_temp
685
+ # Map variable selection to corresponding data
686
+ data_dict = {
687
+ "Velocity": velocity,
688
+ "Echo Intensity": echo,
689
+ "Correlation": correlation,
690
+ "Percentage Good": pgood,
691
+ }
692
+
693
+ with right3:
694
+ # Selection of variable (Velocity, Echo Intensity, etc.)
695
+ variable = st.selectbox(
696
+ "Select Variable to Display",
697
+ ("Velocity", "Echo Intensity", "Correlation", "Percentage Good"),
698
+ )
699
+ # Display the selected variable and beam
700
+ selected_data = data_dict[variable][beam_index, :, :]
701
+ fillplot_plotly(
702
+ selected_data,
703
+ title=variable + "(Masked Manually)",
704
+ maskdata=st.session_state.profile_mask_temp,
705
+ )
706
+ # else:
707
+ # fillplot_plotly(selected_data, title=f"{variable}")
708
+ fillplot_plotly(
709
+ st.session_state.profile_mask_temp,
710
+ colorscale="greys",
711
+ title="Mask Data",
712
+ )
713
+
714
+ # Layout with two columns
715
+ col1, col2 = st.columns([1, 3])
716
+
717
+ # Button to reset the mask data, with unique key
718
+ reset_mask_button = st.button(
719
+ "Reset Cut Bins Manual",
720
+ key="reset_mask_button",
721
+ on_click=reset_button_mask_manual,
722
+ )
723
+ if reset_mask_button:
724
+ st.info("Cut Bins Manual Reset. Mask data is changed to previous state.")
725
+
726
+ ############ REGRID ###########################################
727
+ with tab4:
728
+ st.header("Regrid Depth Cells", divider="blue")
729
+
730
+ st.write(
731
+ """
732
+ When the ADCP buoy has vertical oscillations (greater than depth cell size),
733
+ the depth bins has to be regridded based on the pressure sensor data. The data
734
+ can be regrided either till the surface or till the last bin.
735
+ If the `Cell` option is selected, ensure that the end data are trimmed.
736
+ Manual option permits choosing the end cell depth.
737
+ """
738
+ )
739
+
740
+ left4, right4 = st.columns([1, 3])
741
+
742
+ with left4:
743
+ if st.session_state.beam_direction_QCT.lower() == "up":
744
+ end_cell_option = st.radio(
745
+ "Select the depth of last bin for regridding",
746
+ ("Cell", "Surface", "Manual"),
747
+ horizontal=True,
748
+ )
749
+ else:
750
+ end_cell_option = st.radio(
751
+ "Select the depth of last bin for regridding",
752
+ ("Cell", "Manual"),
753
+ horizontal=True,
754
+ )
755
+
756
+ st.session_state.end_cell_option_PT = end_cell_option
757
+ st.write(f"You have selected: `{end_cell_option}`")
758
+
759
+ if end_cell_option == "Manual":
760
+ mean_depth = (
761
+ np.mean(st.session_state.vlead.vleader["Depth of Transducer"]) / 10
762
+ )
763
+ mean_depth = round(mean_depth, 2)
764
+
765
+ st.write(
766
+ f"The transducer depth is {mean_depth} m. The value should not exceed the transducer depth"
767
+ )
768
+ if st.session_state.beam_direction_QCT.lower() == "up":
769
+ boundary = st.number_input(
770
+ "Enter the depth (m):", max_value=int(mean_depth), min_value=0
771
+ )
772
+ else:
773
+ boundary = st.number_input(
774
+ "Enter the depth (m):", min_value=int(mean_depth)
775
+ )
776
+ else:
777
+ boundary = 0
778
+
779
+ st.session_state.interpolate_PT = st.radio(
780
+ "Choose interpolation method:", ("nearest", "linear", "cubic")
781
+ )
782
+ st.session_state.manualdepth_PT = boundary
783
+
784
+ progress_text = "Regridding in progress. Please wait."
785
+ grid_bar = st.progress(0, text=progress_text)
786
+
787
+ regrid_button = st.button(label="Regrid Data")
788
+ if regrid_button:
789
+ transdepth = st.session_state.depth
790
+ z, st.session_state.velocity_regrid = regrid3d(
791
+ transdepth,
792
+ st.session_state.velocity_temp,
793
+ -32768,
794
+ trimends=st.session_state.trimends_endpoints,
795
+ end_cell_option=st.session_state.end_cell_option_PT,
796
+ orientation=st.session_state.beam_direction_QCT,
797
+ method=st.session_state.interpolate_PT,
798
+ boundary_limit=boundary,
799
+ cells=cells,
800
+ cell_size=cell_size,
801
+ bin1dist=bin1dist,
802
+ beams=beams,
803
+ )
804
+ grid_bar.progress(20, text=progress_text)
805
+ st.write(":grey[Regrided velocity ...]")
806
+ z, st.session_state.echo_regrid = regrid3d(
807
+ transdepth,
808
+ echo,
809
+ -32768,
810
+ trimends=st.session_state.trimends_endpoints,
811
+ end_cell_option=st.session_state.end_cell_option_PT,
812
+ orientation=st.session_state.beam_direction_QCT,
813
+ method=st.session_state.interpolate_PT,
814
+ boundary_limit=boundary,
815
+ cells=cells,
816
+ cell_size=cell_size,
817
+ bin1dist=bin1dist,
818
+ beams=beams,
819
+ )
820
+ grid_bar.progress(40, text=progress_text)
821
+ st.write(":grey[Regrided echo intensity ...]")
822
+ z, st.session_state.correlation_regrid = regrid3d(
823
+ transdepth,
824
+ correlation,
825
+ -32768,
826
+ trimends=st.session_state.trimends_endpoints,
827
+ end_cell_option=st.session_state.end_cell_option_PT,
828
+ orientation=st.session_state.beam_direction_QCT,
829
+ method=st.session_state.interpolate_PT,
830
+ boundary_limit=boundary,
831
+ cells=cells,
832
+ cell_size=cell_size,
833
+ bin1dist=bin1dist,
834
+ beams=beams,
835
+ )
836
+ grid_bar.progress(60, text=progress_text)
837
+ st.write(":grey[Regrided correlation...]")
838
+ z, st.session_state.pgood_regrid = regrid3d(
839
+ transdepth,
840
+ pgood,
841
+ -32768,
842
+ trimends=st.session_state.trimends_endpoints,
843
+ end_cell_option=st.session_state.end_cell_option_PT,
844
+ orientation=st.session_state.beam_direction_QCT,
845
+ method=st.session_state.interpolate_PT,
846
+ boundary_limit=boundary,
847
+ cells=cells,
848
+ cell_size=cell_size,
849
+ bin1dist=bin1dist,
850
+ beams=beams,
851
+ )
852
+ grid_bar.progress(80, text=progress_text)
853
+ st.write(":grey[Regrided percent good...]")
854
+
855
+ z, st.session_state.profile_mask_regrid = regrid2d(
856
+ transdepth,
857
+ st.session_state.profile_mask_temp,
858
+ 1,
859
+ trimends=st.session_state.trimends_endpoints,
860
+ end_cell_option=st.session_state.end_cell_option_PT,
861
+ orientation=st.session_state.beam_direction_QCT,
862
+ method="nearest",
863
+ boundary_limit=boundary,
864
+ cells=cells,
865
+ cell_size=cell_size,
866
+ bin1dist=bin1dist,
867
+ )
868
+
869
+ grid_bar.progress(99, text=progress_text)
870
+ st.write(":grey[Regrided mask...]")
871
+
872
+ st.session_state.depth_axis = z
873
+ st.write(":grey[New depth axis created...]")
874
+
875
+ grid_bar.progress(100, text="Completed")
876
+ st.write(":green[All data regrided!]")
877
+
878
+ st.write(
879
+ "No. of grid depth bins before regridding: ", np.shape(velocity)[1]
880
+ )
881
+ st.write(
882
+ "No. of grid depth bins after regridding: ",
883
+ np.shape(st.session_state.velocity_regrid)[1],
884
+ )
885
+ st.session_state.isRegridCheck_PT = True
886
+
887
+ regrid_reset_button = st.button(
888
+ "Reset Regrid Test", on_click=reset_button_regrid
889
+ )
890
+
891
+ if regrid_reset_button:
892
+ st.info("Data Reset")
893
+
894
+ with right4:
895
+ if st.session_state.isRegridCheck_PT:
896
+ fillplot_plotly(
897
+ st.session_state.velocity_regrid[0, :, :],
898
+ title="Regridded Velocity File",
899
+ maskdata=st.session_state.profile_mask_regrid,
900
+ )
901
+ fillplot_plotly(
902
+ st.session_state.profile_mask_regrid,
903
+ colorscale="greys",
904
+ title="Regridded Mask File",
905
+ )
906
+ else:
907
+ fillplot_plotly(
908
+ velocity[0, :, :],
909
+ maskdata=st.session_state.profile_mask_temp,
910
+ title="Original File",
911
+ )
912
+
913
+
914
+ ########### Save and Reset Mask ##############
915
+ with tab5:
916
+ st.header("Save & Reset Mask Data", divider="blue")
917
+
918
+ col1, col2 = st.columns([1, 1])
919
+ with col1:
920
+ save_mask_button = st.button(label="Save Mask Data", on_click=save_profiletest)
921
+ if save_mask_button:
922
+ st.success("Mask data saved")
923
+ # Table summarizing changes
924
+ changes_summary = pd.DataFrame(
925
+ [
926
+ [
927
+ "Trim Ends",
928
+ "True" if st.session_state.isTrimEndsCheck_PT else "False",
929
+ ],
930
+ [
931
+ "Cut Bins: Side Lobe Contamination",
932
+ "True"
933
+ if st.session_state.isCutBinSideLobeCheck_PT
934
+ else "False",
935
+ ],
936
+ [
937
+ "Cut Bins: Manual",
938
+ "True" if st.session_state.isCutBinManualCheck_PT else "False",
939
+ ],
940
+ [
941
+ "Regrid Depth Cells",
942
+ "True" if st.session_state.isRegridCheck_PT else "False",
943
+ ],
944
+ ],
945
+ columns=["Parameter", "Status"],
946
+ )
947
+
948
+ # Define a mapping function for styling
949
+ def status_color_map(value):
950
+ if value == "True":
951
+ return "background-color: green; color: white"
952
+ elif value == "False":
953
+ return "background-color: red; color: white"
954
+ else:
955
+ return ""
956
+
957
+ # Apply styles using Styler.apply
958
+ styled_table = changes_summary.style.set_properties(
959
+ **{"text-align": "center"}
960
+ )
961
+ styled_table = styled_table.map(status_color_map, subset=["Status"])
962
+
963
+ # Display the styled table
964
+ st.write(styled_table.to_html(), unsafe_allow_html=True)
965
+ else:
966
+ st.write(":red[Mask data not saved]")
967
+ with col2:
968
+ reset_mask_button = st.button("Reset mask data", on_click=reset_profiletest)
969
+ if reset_mask_button:
970
+ st.info("Mask data is reset to default")