77import pyopenms as poms
88from src .common .common import show_fig , display_large_dataframe
99from typing import Union
10+ from plotly .subplots import make_subplots
1011
1112
1213def get_df (file : Union [str , Path ]) -> pd .DataFrame :
@@ -170,6 +171,8 @@ def plot_ms_spectrum(df, title, bin_peaks, num_x_bins):
170171@st .fragment
171172def view_peak_map ():
172173 df = st .session_state .view_ms1
174+
175+ # ✅ Apply Box Selection Filtering
173176 if "view_peak_map_selection" in st .session_state :
174177 box = st .session_state .view_peak_map_selection .selection .box
175178 if box :
@@ -178,29 +181,111 @@ def view_peak_map():
178181 df = df [df ["mz" ] > box [0 ]["y" ][1 ]]
179182 df = df [df ["mz" ] < box [0 ]["y" ][0 ]]
180183 df = df [df ["RT" ] < box [0 ]["x" ][1 ]]
184+
185+ # ✅ Main 2D Peak Map Plot with Correct Y-Axis Label
181186 peak_map = df .plot (
182187 kind = "peakmap" ,
183188 x = "RT" ,
184189 y = "mz" ,
185190 z = "inty" ,
186- title = st .session_state .view_selected_file ,
191+ xlabel = "Retention Time (s)" , # ✅ X-axis label
192+ ylabel = "m/z" , # ✅ Y-axis label for main plot
187193 grid = False ,
188194 show_plot = False ,
189195 bin_peaks = True ,
190196 backend = "ms_plotly" ,
191197 aggregate_duplicates = True ,
192198 )
193- peak_map .update_layout (template = "simple_white" , dragmode = "select" )
199+
200+ # ✅ Marginal TIC Plot with Correct Y-Axis Label
201+ df_tic = df .groupby ("RT" ).sum ().reset_index ()
202+
203+ marginal_tic = go .Figure ()
204+ marginal_tic .add_trace (
205+ go .Scatter (
206+ x = df_tic ["RT" ],
207+ y = df_tic ["inty" ],
208+ mode = "lines" ,
209+ line = dict (color = "#f24c5c" , width = 2 ),
210+ name = "TIC" ,
211+ )
212+ )
213+
214+ marginal_tic .update_layout (
215+ height = 200 ,
216+ margin = dict (l = 0 , r = 0 , t = 0 , b = 0 ),
217+ plot_bgcolor = "rgb(255,255,255)" ,
218+ xaxis = dict (title = "Retention Time (s)" ), # ✅ X-axis label
219+ yaxis = dict (title = "TIC" ) # ✅ Y-axis label for TIC plot
220+ )
221+
222+ # ✅ Create subplots with peak map on top and TIC at the bottom
223+ combined_fig = make_subplots (
224+ rows = 2 ,
225+ cols = 1 ,
226+ shared_xaxes = True ,
227+ row_heights = [0.7 , 0.3 ], # Peak map gets 70%, TIC gets 30%
228+ vertical_spacing = 0.05
229+ )
230+
231+ # ✅ Add main peak map to subplot (first row)
232+ for trace in peak_map .data :
233+ combined_fig .add_trace (trace , row = 1 , col = 1 )
234+
235+ # ✅ Add marginal TIC plot (second row)
236+ for trace in marginal_tic .data :
237+ combined_fig .add_trace (trace , row = 2 , col = 1 )
238+
239+ # ✅ Update layout with range slider only on the bottom x-axis
240+ combined_fig .update_layout (
241+ template = "simple_white" ,
242+ dragmode = "zoom" ,
243+
244+ # ✅ Top x-axis (no range slider)
245+ xaxis = dict (
246+ showgrid = False ,
247+ domain = [0 , 1 ]
248+ ),
249+
250+ # ✅ Bottom x-axis with range slider
251+ xaxis2 = dict (
252+ title = "Retention Time (s)" ,
253+ rangeslider = dict (visible = True ),
254+ showgrid = False
255+ ),
256+
257+ # ✅ Configure Y-axes
258+ yaxis = dict (title = "m/z" ), # Y-axis for peak map
259+ yaxis2 = dict (title = "TIC" ), # Y-axis for TIC
260+
261+ # ✅ Styling
262+ height = 850 ,
263+ margin = dict (t = 100 , b = 100 ),
264+ title = dict (
265+ text = st .session_state .view_selected_file ,
266+ x = 0.5 ,
267+ y = 0.99 ,
268+ xanchor = "center" ,
269+ yanchor = "top" ,
270+ font = dict (size = 18 , family = "Arial, sans-serif" )
271+ )
272+ )
273+
274+ # ✅ Display the Combined Plot
194275 c1 , c2 = st .columns (2 )
276+
195277 with c1 :
196278 st .info (
197- "💡 Zoom in via rectangular selection for more details and 3D plot. Double click plot to zoom back out."
279+ "💡 Zoom in via rectangular selection for more details and 3D plot. "
280+ "Double click plot to zoom back out."
198281 )
199282 show_fig (
200- peak_map ,
283+ combined_fig ,
201284 f"peak_map_{ st .session_state .view_selected_file } " ,
202285 selection_session_state_key = "view_peak_map_selection" ,
203286 )
287+
288+ # ✅ 3D Peak Map (without range slider)
204289 with c2 :
205290 if df .shape [0 ] < 2500 :
206291 peak_map_3D = df .plot (
@@ -211,6 +296,8 @@ def view_peak_map():
211296 y = "mz" ,
212297 z = "inty" ,
213298 zlabel = "Intensity" ,
299+ xlabel = "Retention Time (s)" ,
300+ ylabel = "m/z" ,
214301 title = "" ,
215302 show_plot = False ,
216303 grid = False ,
@@ -220,9 +307,18 @@ def view_peak_map():
220307 width = 900 ,
221308 aggregate_duplicates = True ,
222309 )
223- st .plotly_chart (peak_map_3D , use_container_width = True )
224310
311+ # ✅ Update 3D plot layout (without range slider)
312+ peak_map_3D .update_layout (
313+ scene = dict (
314+ xaxis = dict (title = "Retention Time (s)" ),
315+ yaxis = dict (title = "m/z" ),
316+ zaxis = dict (title = "Intensity" ),
317+ dragmode = "orbit"
318+ )
319+ )
225320
321+ st .plotly_chart (peak_map_3D , use_container_width = True )
226322@st .fragment
227323def view_spectrum ():
228324 cols = st .columns ([0.34 , 0.66 ])
0 commit comments