simplified imzML parsing, finished interactivity for preprocessing in frontend / backend, and processing spectrums is now possible. added n Spectrum plotting
This commit is contained in:
parent
11c181b3b3
commit
8b1c2d2722
767
app.jl
767
app.jl
@ -22,7 +22,7 @@ using Dates
|
|||||||
using Base.Threads
|
using Base.Threads
|
||||||
|
|
||||||
# Bring MSIData into App module's scope
|
# Bring MSIData into App module's scope
|
||||||
using .MSI_src: MSIData, OpenMSIData, process_spectrum, IterateSpectra, ImzMLSource, _iterate_spectra_fast, MzMLSource, find_mass, ViridisPalette, get_mz_slice, get_multiple_mz_slices, quantize_intensity, save_bitmap, median_filter, save_bitmap, downsample_spectrum, TrIQ, precompute_analytics, ImportMzmlFile, generate_colorbar_image, load_and_prepare_mask, set_global_mz_range!, main_precalculation, MutableSpectrum
|
using .MSI_src: MSIData, OpenMSIData, process_spectrum, IterateSpectra, ImzMLSource, _iterate_spectra_fast, MzMLSource, find_mass, ViridisPalette, get_mz_slice, get_multiple_mz_slices, quantize_intensity, save_bitmap, median_filter, save_bitmap, downsample_spectrum, TrIQ, precompute_analytics, ImportMzmlFile, generate_colorbar_image, load_and_prepare_mask, set_global_mz_range!, main_precalculation, MutableSpectrum, execute_full_preprocessing
|
||||||
|
|
||||||
if !@isdefined(increment_image)
|
if !@isdefined(increment_image)
|
||||||
include("./julia_imzML_visual.jl")
|
include("./julia_imzML_visual.jl")
|
||||||
@ -67,6 +67,30 @@ else
|
|||||||
log_memory_usage(context::String, msi_data_val) = nothing # No-op for production
|
log_memory_usage(context::String, msi_data_val) = nothing # No-op for production
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function validate_parse(validation_errors::Vector{String}, param_str::String, param_name::String, target_type::Type, step_name::String)
|
||||||
|
println("DEBUG: Validating ($step_name) Parameter '$param_name'. Received value: '$param_str'")
|
||||||
|
if isempty(param_str)
|
||||||
|
push!(validation_errors, "($step_name) Parameter '$param_name' is empty.")
|
||||||
|
return nothing
|
||||||
|
end
|
||||||
|
val = tryparse(target_type, param_str)
|
||||||
|
if val === nothing
|
||||||
|
push!(validation_errors, "($step_name) Parameter '$param_name' ('$param_str') is not a valid $(target_type).")
|
||||||
|
return nothing
|
||||||
|
end
|
||||||
|
return val
|
||||||
|
end
|
||||||
|
|
||||||
|
# Helper function to check if a pipeline step is enabled
|
||||||
|
function is_step_enabled(step_name::String, pipeline_order::Vector{Dict{String, Any}})
|
||||||
|
for step in pipeline_order
|
||||||
|
if get(step, "name", "") == step_name
|
||||||
|
return get(step, "enabled", false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false # Default to disabled if step not found
|
||||||
|
end
|
||||||
|
|
||||||
@genietools
|
@genietools
|
||||||
|
|
||||||
# == Reactive code ==
|
# == Reactive code ==
|
||||||
@ -116,6 +140,7 @@ end
|
|||||||
@in compareBtn=false # To open dialog
|
@in compareBtn=false # To open dialog
|
||||||
@in createMeanPlot=false # To generate mean spectrum plot
|
@in createMeanPlot=false # To generate mean spectrum plot
|
||||||
@in createXYPlot=false # To generate an spectrum plot according to the xy values inputed
|
@in createXYPlot=false # To generate an spectrum plot according to the xy values inputed
|
||||||
|
@in createNSpectrumPlot=false # To generate an spectrum plot according to spectrum order
|
||||||
@in createSumPlot=false # To generate a sum of all the spectrum plots
|
@in createSumPlot=false # To generate a sum of all the spectrum plots
|
||||||
@in image3dPlot=false # To generate 3d plot based on current image
|
@in image3dPlot=false # To generate 3d plot based on current image
|
||||||
@in triq3dPlot=false # To generate 3d plot based on current triq image
|
@in triq3dPlot=false # To generate 3d plot based on current triq image
|
||||||
@ -264,7 +289,6 @@ end
|
|||||||
Dict("name" => "normalization", "label" => "Normalization", "enabled" => true),
|
Dict("name" => "normalization", "label" => "Normalization", "enabled" => true),
|
||||||
Dict("name" => "peak_binning", "label" => "Peak Binning", "enabled" => true)
|
Dict("name" => "peak_binning", "label" => "Peak Binning", "enabled" => true)
|
||||||
]
|
]
|
||||||
@in preprocessing_mask_route = ""
|
|
||||||
@in selected_spectrum_id_for_plot = 1
|
@in selected_spectrum_id_for_plot = 1
|
||||||
@in feature_matrix_result = nothing
|
@in feature_matrix_result = nothing
|
||||||
@in bin_info_result = nothing
|
@in bin_info_result = nothing
|
||||||
@ -274,41 +298,21 @@ end
|
|||||||
Dict("mz" => 155.0349, "label" => "DHB_M+H"),
|
Dict("mz" => 155.0349, "label" => "DHB_M+H"),
|
||||||
]
|
]
|
||||||
|
|
||||||
# --- Methods for Reference Peaks List ---
|
|
||||||
function addReferencePeak()
|
|
||||||
push!(reference_peaks_list, Dict("mz" => 0.0, "label" => ""))
|
|
||||||
reference_peaks_list = deepcopy(reference_peaks_list) # Force reactivity
|
|
||||||
end
|
|
||||||
|
|
||||||
function removeReferencePeak(index::Int)
|
|
||||||
deleteat!(reference_peaks_list, index)
|
|
||||||
reference_peaks_list = deepcopy(reference_peaks_list) # Force reactivity
|
|
||||||
end
|
|
||||||
|
|
||||||
# Step reordering functions
|
|
||||||
function moveStepUp(index::Int)
|
|
||||||
if index > 1
|
|
||||||
pipeline_step_order = deepcopy(pipeline_step_order)
|
|
||||||
temp = pipeline_step_order[index]
|
|
||||||
pipeline_step_order[index] = pipeline_step_order[index-1]
|
|
||||||
pipeline_step_order[index-1] = temp
|
|
||||||
pipeline_step_order = pipeline_step_order # Force reactivity
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function moveStepDown(index::Int)
|
|
||||||
if index < length(pipeline_step_order)
|
|
||||||
pipeline_step_order = deepcopy(pipeline_step_order)
|
|
||||||
temp = pipeline_step_order[index]
|
|
||||||
pipeline_step_order[index] = pipeline_step_order[index+1]
|
|
||||||
pipeline_step_order[index+1] = temp
|
|
||||||
pipeline_step_order = pipeline_step_order # Force reactivity
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@in save_feature_matrix_btn = false
|
@in save_feature_matrix_btn = false
|
||||||
|
@in recalculate_suggestions_btn = false
|
||||||
|
@in addReferencePeak = false
|
||||||
|
@in removeReferencePeak = false
|
||||||
|
@in moveStepUp = false
|
||||||
|
@in moveStepDown = false
|
||||||
|
|
||||||
# Trigger for running the full pipeline
|
# Trigger for running the full pipeline
|
||||||
@in run_full_pipeline = false
|
@in run_full_pipeline = false
|
||||||
|
@in enable_standards = true
|
||||||
|
@in action_index = -1
|
||||||
|
@in remove_peak_trigger = false
|
||||||
|
@in move_step_up_trigger = false
|
||||||
|
@in move_step_down_trigger = false
|
||||||
|
@in toggle_step_trigger = false
|
||||||
@out current_pipeline_step = "" # To indicate which step is currently running in the full pipeline
|
@out current_pipeline_step = "" # To indicate which step is currently running in the full pipeline
|
||||||
|
|
||||||
@in export_params_btn = false
|
@in export_params_btn = false
|
||||||
@ -453,7 +457,13 @@ end
|
|||||||
showgrid=true,
|
showgrid=true,
|
||||||
tickformat = ".3g"
|
tickformat = ".3g"
|
||||||
),
|
),
|
||||||
margin=attr(l=0,r=0,t=120,b=0,pad=0)
|
margin=attr(l=0,r=0,t=120,b=0,pad=0),
|
||||||
|
legend=attr(
|
||||||
|
x=1.0,
|
||||||
|
y=1.0,
|
||||||
|
xanchor="right",
|
||||||
|
yanchor="top"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
# Dummy 2D scatter plot
|
# Dummy 2D scatter plot
|
||||||
traceSpectra=PlotlyBase.scatter(x=Vector{Float64}(), y=Vector{Float64}(), mode="lines", marker=attr(size=1, color="blue", opacity=0.1))
|
traceSpectra=PlotlyBase.scatter(x=Vector{Float64}(), y=Vector{Float64}(), mode="lines", marker=attr(size=1, color="blue", opacity=0.1))
|
||||||
@ -558,7 +568,7 @@ end
|
|||||||
msg = "Opening file: $(basename(picked_route))..."
|
msg = "Opening file: $(basename(picked_route))..."
|
||||||
|
|
||||||
try
|
try
|
||||||
dataset_name = replace(basename(picked_route), r"(\.(imzML|imzml|mzML|mzml))$ "i => "")
|
dataset_name = replace(basename(picked_route), r"(\.(imzML|imzml|mzML|mzml))$"i => "")
|
||||||
registry = load_registry(registry_path)
|
registry = load_registry(registry_path)
|
||||||
existing_entry = get(registry, dataset_name, nothing)
|
existing_entry = get(registry, dataset_name, nothing)
|
||||||
|
|
||||||
@ -938,119 +948,339 @@ end
|
|||||||
@onbutton run_full_pipeline begin
|
@onbutton run_full_pipeline begin
|
||||||
progressPrep = true
|
progressPrep = true
|
||||||
current_pipeline_step = "Initializing..."
|
current_pipeline_step = "Initializing..."
|
||||||
|
println("DEBUG: run_full_pipeline started.")
|
||||||
|
# println("DEBUG: Current pipeline_step_order configuration: $pipeline_step_order")
|
||||||
|
|
||||||
try
|
try
|
||||||
# 1. Data Preparation
|
# --- 1. Initial Checks and Data Loading ---
|
||||||
if isempty(selected_folder_main)
|
println("DEBUG: Performing initial checks and data loading...")
|
||||||
msg = "No dataset selected. Please process a file first."
|
if msi_data === nothing || isempty(selected_folder_main)
|
||||||
|
msg = "No dataset loaded. Please load a file using 'Select an imzMl / mzML file'."
|
||||||
warning_msg = true
|
warning_msg = true
|
||||||
|
println("DEBUG: $msg")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
registry = load_registry(registry_path)
|
registry = load_registry(registry_path)
|
||||||
entry = registry[selected_folder_main]
|
entry = get(registry, selected_folder_main, nothing)
|
||||||
|
if entry === nothing
|
||||||
|
msg = "Selected dataset '$(selected_folder_main)' not found in registry. Please reload the file."
|
||||||
|
warning_msg = true
|
||||||
|
println("DEBUG: $msg")
|
||||||
|
return
|
||||||
|
end
|
||||||
target_path = entry["source_path"]
|
target_path = entry["source_path"]
|
||||||
|
|
||||||
if msi_data === nothing || full_route != target_path
|
# Ensure msi_data is for the currently selected file
|
||||||
if msi_data !== nothing
|
if full_route != target_path
|
||||||
close(msi_data)
|
println("DEBUG: Active file path changed. Reloading MSI data: $(basename(target_path))")
|
||||||
end
|
if msi_data !== nothing; close(msi_data); end
|
||||||
|
msg = "Reloading $(basename(target_path)) for analysis..."
|
||||||
full_route = target_path
|
full_route = target_path
|
||||||
msi_data = OpenMSIData(target_path)
|
msi_data = OpenMSIData(target_path)
|
||||||
|
else
|
||||||
|
println("DEBUG: Using already loaded MSI data for $(basename(target_path)).")
|
||||||
end
|
end
|
||||||
|
|
||||||
# Apply mask if enabled
|
# Mask path retrieval from registry
|
||||||
|
local mask_path_for_pipeline::Union{String, Nothing} = nothing
|
||||||
|
if maskEnabled
|
||||||
|
println("DEBUG: Masking is ENABLED.")
|
||||||
|
if get(entry, "has_mask", false)
|
||||||
|
mask_path_candidate = get(entry, "mask_path", "")
|
||||||
|
if isfile(mask_path_candidate)
|
||||||
|
mask_path_for_pipeline = mask_path_candidate
|
||||||
|
println("DEBUG: Using mask for pipeline: $(mask_path_for_pipeline)")
|
||||||
|
else
|
||||||
|
msg = "Mask enabled but file not found: $(mask_path_candidate). Aborting pipeline."
|
||||||
|
warning_msg = true
|
||||||
|
@warn msg
|
||||||
|
println("DEBUG: $msg")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
else
|
||||||
|
msg = "Mask enabled but no valid mask entry found for: $(selected_folder_main). Aborting pipeline."
|
||||||
|
warning_msg = true
|
||||||
|
@warn msg
|
||||||
|
println("DEBUG: $msg")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
else
|
||||||
|
println("DEBUG: Masking is DISABLED. No mask will be applied.")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Apply mask if enabled to get indices to process
|
||||||
spectrum_indices_to_process = collect(1:length(msi_data.spectra_metadata))
|
spectrum_indices_to_process = collect(1:length(msi_data.spectra_metadata))
|
||||||
if maskEnabled && !isempty(preprocessing_mask_route) && isfile(preprocessing_mask_route)
|
if mask_path_for_pipeline !== nothing
|
||||||
current_pipeline_step = "Loading mask..."
|
current_pipeline_step = "Applying mask..."
|
||||||
mask_matrix = load_and_prepare_mask(preprocessing_mask_route, msi_data.image_dims)
|
println("DEBUG: Applying mask matrix to filter spectra...")
|
||||||
|
mask_matrix = load_and_prepare_mask(mask_path_for_pipeline, msi_data.image_dims)
|
||||||
masked_indices_set = get_masked_spectrum_indices(msi_data, mask_matrix)
|
masked_indices_set = get_masked_spectrum_indices(msi_data, mask_matrix)
|
||||||
spectrum_indices_to_process = collect(masked_indices_set)
|
spectrum_indices_to_process = collect(masked_indices_set)
|
||||||
|
if isempty(spectrum_indices_to_process)
|
||||||
|
msg = "No spectra remaining after applying mask. Aborting pipeline."
|
||||||
|
warning_msg = true
|
||||||
|
println("DEBUG: $msg")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
println("DEBUG: $(length(spectrum_indices_to_process)) spectra remaining after mask application.")
|
||||||
|
else
|
||||||
|
println("DEBUG: No mask applied. Processing all $(length(msi_data.spectra_metadata)) spectra.")
|
||||||
end
|
end
|
||||||
|
|
||||||
# Initialize spectra data structure
|
# Initialize spectra data structure
|
||||||
current_pipeline_step = "Loading spectra..."
|
current_pipeline_step = "Loading spectra..."
|
||||||
|
println("DEBUG: Loading $(length(spectrum_indices_to_process)) spectra into MutableSpectrum objects...")
|
||||||
current_spectra = Vector{MutableSpectrum}(undef, length(spectrum_indices_to_process))
|
current_spectra = Vector{MutableSpectrum}(undef, length(spectrum_indices_to_process))
|
||||||
|
|
||||||
Threads. @threads for i in 1:length(spectrum_indices_to_process)
|
Threads.@threads for i in 1:length(spectrum_indices_to_process)
|
||||||
original_idx = spectrum_indices_to_process[i]
|
original_idx = spectrum_indices_to_process[i]
|
||||||
mz, intensity = GetSpectrum(msi_data, original_idx)
|
mz, intensity = GetSpectrum(msi_data, original_idx) # Fetch mz and intensity for the current spectrum
|
||||||
current_spectra[i] = MutableSpectrum(original_idx, mz, intensity, [])
|
current_spectra[i] = MutableSpectrum(original_idx, Float64.(mz), Float64.(intensity), NamedTuple{(:mz, :intensity, :fwhm, :shape_r2, :snr, :prominence), NTuple{6, Float64}}[])
|
||||||
|
end
|
||||||
|
println("DEBUG: All spectra loaded into temporary structure for processing.")
|
||||||
|
|
||||||
|
|
||||||
|
# --- 2. Parameter Assembly with Validation ---
|
||||||
|
current_pipeline_step = "Configuring parameters..."
|
||||||
|
println("DEBUG: Configuring parameters and validating enabled steps...")
|
||||||
|
ref_peaks = Dict{Float64, String}(p["mz"] => p["label"] for p in reference_peaks_list)
|
||||||
|
|
||||||
|
final_params = Dict{Symbol, Dict{Symbol, Any}}()
|
||||||
|
validation_errors = String[]
|
||||||
|
|
||||||
|
# --- Stabilization ---
|
||||||
|
println("DEBUG: Checking Stabilization step (name: stabilization)")
|
||||||
|
if is_step_enabled("stabilization", pipeline_step_order)
|
||||||
|
println("DEBUG: Stabilization step is ENABLED. Setting method: $(stabilization_method).")
|
||||||
|
final_params[:Stabilization] = Dict{Symbol, Any}(:method => Symbol(stabilization_method))
|
||||||
|
else
|
||||||
|
println("DEBUG: Stabilization step is DISABLED. Skipping.")
|
||||||
end
|
end
|
||||||
|
|
||||||
# 2. Parameter Assembly
|
# --- Smoothing ---
|
||||||
current_pipeline_step = "Configuring parameters..."
|
println("DEBUG: Checking Smoothing step (name: smoothing)")
|
||||||
final_params = Dict(
|
if is_step_enabled("smoothing", pipeline_step_order)
|
||||||
:Stabilization => Dict(:method => Symbol(stabilization_method)),
|
println("DEBUG: Smoothing step is ENABLED. Validating parameters.")
|
||||||
:Smoothing => Dict(
|
window_val = validate_parse(validation_errors, smoothing_window, "Window", Int, "Smoothing")
|
||||||
|
order_val = validate_parse(validation_errors, smoothing_order, "Order", Int, "Smoothing")
|
||||||
|
final_params[:Smoothing] = Dict{Symbol, Any}(
|
||||||
:method => Symbol(smoothing_method),
|
:method => Symbol(smoothing_method),
|
||||||
:window => parse(Int, smoothing_window),
|
:window => something(window_val, 9),
|
||||||
:order => parse(Int, smoothing_order)
|
:order => something(order_val, 2)
|
||||||
),
|
|
||||||
:BaselineCorrection => Dict(
|
|
||||||
:method => Symbol(baseline_method),
|
|
||||||
:iterations => parse(Int, baseline_iterations),
|
|
||||||
:window => parse(Int, baseline_window)
|
|
||||||
),
|
|
||||||
:Normalization => Dict(:method => Symbol(normalization_method)),
|
|
||||||
:PeakPicking => Dict(
|
|
||||||
:method => Symbol(peak_picking_method),
|
|
||||||
:snr_threshold => parse(Float64, peak_picking_snr_threshold),
|
|
||||||
:half_window => parse(Int, peak_picking_half_window),
|
|
||||||
:min_peak_prominence => parse(Float64, peak_picking_min_peak_prominence),
|
|
||||||
:merge_peaks_tolerance => parse(Float64, peak_picking_merge_peaks_tolerance)
|
|
||||||
),
|
|
||||||
:PeakSelection => Dict(
|
|
||||||
:min_snr => parse(Float64, peak_selection_min_snr),
|
|
||||||
:min_fwhm_ppm => parse(Float64, peak_selection_min_fwhm_ppm),
|
|
||||||
:max_fwhm_ppm => parse(Float64, peak_selection_max_fwhm_ppm),
|
|
||||||
:min_shape_r2 => parse(Float64, peak_selection_min_shape_r2)
|
|
||||||
),
|
|
||||||
:Calibration => Dict(
|
|
||||||
:method => :internal_standards,
|
|
||||||
:ppm_tolerance => parse(Float64, calibration_ppm_tolerance),
|
|
||||||
:fit_order => parse(Int, calibration_fit_order)
|
|
||||||
),
|
|
||||||
:PeakAlignment => Dict(
|
|
||||||
:method => Symbol(alignment_method),
|
|
||||||
:tolerance => parse(Float64, alignment_tolerance),
|
|
||||||
:tolerance_unit => Symbol(alignment_tolerance_unit)
|
|
||||||
),
|
|
||||||
:PeakBinning => Dict(
|
|
||||||
:method => Symbol(binning_method),
|
|
||||||
:tolerance => parse(Float64, binning_tolerance),
|
|
||||||
:tolerance_unit => Symbol(binning_tolerance_unit),
|
|
||||||
:min_peak_per_bin => parse(Int, binning_min_peak_per_bin)
|
|
||||||
)
|
)
|
||||||
)
|
if window_val !== nothing && window_val < 1
|
||||||
|
push!(validation_errors, "(Smoothing) Window must be positive.")
|
||||||
|
end
|
||||||
|
if order_val !== nothing && order_val < 0
|
||||||
|
push!(validation_errors, "(Smoothing) Order must be non-negative.")
|
||||||
|
end
|
||||||
|
println("DEBUG: Smoothing parameters set: method=$(smoothing_method), window=$(something(window_val, 9)), order=$(something(order_val, 2)).")
|
||||||
|
else
|
||||||
|
println("DEBUG: Smoothing step is DISABLED. Skipping parameter validation.")
|
||||||
|
end
|
||||||
|
|
||||||
|
# --- Baseline Correction ---
|
||||||
|
println("DEBUG: Checking Baseline Correction step (name: baseline_correction)")
|
||||||
|
if is_step_enabled("baseline_correction", pipeline_step_order)
|
||||||
|
println("DEBUG: Baseline Correction step is ENABLED. Validating parameters.")
|
||||||
|
iterations_val = validate_parse(validation_errors, baseline_iterations, "Iterations", Int, "Baseline Correction")
|
||||||
|
baseline_window_val = validate_parse(validation_errors, baseline_window, "Window", Int, "Baseline Correction")
|
||||||
|
final_params[:BaselineCorrection] = Dict{Symbol, Any}(
|
||||||
|
:method => Symbol(baseline_method),
|
||||||
|
:iterations => something(iterations_val, 100),
|
||||||
|
:window => something(baseline_window_val, 20)
|
||||||
|
)
|
||||||
|
if iterations_val !== nothing && iterations_val < 0
|
||||||
|
push!(validation_errors, "(Baseline Correction) Iterations must be non-negative.")
|
||||||
|
end
|
||||||
|
if baseline_window_val !== nothing && baseline_window_val < 1
|
||||||
|
push!(validation_errors, "(Baseline Correction) Window must be positive.")
|
||||||
|
end
|
||||||
|
println("DEBUG: Baseline Correction parameters set: method=$(baseline_method), iterations=$(something(iterations_val, 100)), window=$(something(baseline_window_val, 20)).")
|
||||||
|
else
|
||||||
|
println("DEBUG: Baseline Correction step is DISABLED. Skipping parameter validation.")
|
||||||
|
end
|
||||||
|
|
||||||
|
# --- Normalization ---
|
||||||
|
println("DEBUG: Checking Normalization step (name: normalization)")
|
||||||
|
if is_step_enabled("normalization", pipeline_step_order)
|
||||||
|
println("DEBUG: Normalization step is ENABLED. Setting method: $(normalization_method).")
|
||||||
|
final_params[:Normalization] = Dict{Symbol, Any}(:method => Symbol(normalization_method))
|
||||||
|
else
|
||||||
|
println("DEBUG: Normalization step is DISABLED. Skipping.")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# --- Peak Picking ---
|
||||||
|
println("DEBUG: Checking Peak Picking step (name: peak_picking)")
|
||||||
|
if is_step_enabled("peak_picking", pipeline_step_order)
|
||||||
|
println("DEBUG: Peak Picking step is ENABLED. Validating parameters.")
|
||||||
|
snr_threshold_val = validate_parse(validation_errors, peak_picking_snr_threshold, "SNR Threshold", Float64, "Peak Picking")
|
||||||
|
half_window_val = validate_parse(validation_errors, peak_picking_half_window, "Half Window", Int, "Peak Picking")
|
||||||
|
min_peak_prominence_val = validate_parse(validation_errors, peak_picking_min_peak_prominence, "Min Prominence", Float64, "Peak Picking")
|
||||||
|
merge_peaks_tolerance_val = validate_parse(validation_errors, peak_picking_merge_peaks_tolerance, "Merge Tolerance", Float64, "Peak Picking")
|
||||||
|
final_params[:PeakPicking] = Dict{Symbol, Any}(
|
||||||
|
:method => Symbol(peak_picking_method),
|
||||||
|
:snr_threshold => something(snr_threshold_val, 3.0),
|
||||||
|
:half_window => something(half_window_val, 10),
|
||||||
|
:min_peak_prominence => something(min_peak_prominence_val, 0.1),
|
||||||
|
:merge_peaks_tolerance => something(merge_peaks_tolerance_val, 0.002)
|
||||||
|
)
|
||||||
|
if snr_threshold_val !== nothing && snr_threshold_val < 0
|
||||||
|
push!(validation_errors, "(Peak Picking) SNR Threshold must be non-negative.")
|
||||||
|
end
|
||||||
|
if half_window_val !== nothing && half_window_val < 1
|
||||||
|
push!(validation_errors, "(Peak Picking) Half Window must be positive.")
|
||||||
|
end
|
||||||
|
if min_peak_prominence_val !== nothing && (min_peak_prominence_val < 0 || min_peak_prominence_val > 1)
|
||||||
|
push!(validation_errors, "(Peak Picking) Min Prominence must be between 0 and 1.")
|
||||||
|
end
|
||||||
|
if merge_peaks_tolerance_val !== nothing && merge_peaks_tolerance_val < 0
|
||||||
|
push!(validation_errors, "(Peak Picking) Merge Tolerance must be non-negative.")
|
||||||
|
end
|
||||||
|
println("DEBUG: Peak Picking parameters set: method=$(peak_picking_method), snr_threshold=$(something(snr_threshold_val, 3.0)), half_window=$(something(half_window_val, 10))...")
|
||||||
|
else
|
||||||
|
println("DEBUG: Peak Picking step is DISABLED. Skipping parameter validation.")
|
||||||
|
end
|
||||||
|
|
||||||
|
# --- Peak Selection ---
|
||||||
|
println("DEBUG: Checking Peak Selection step (name: peak_selection)")
|
||||||
|
if is_step_enabled("peak_selection", pipeline_step_order)
|
||||||
|
println("DEBUG: Peak Selection step is ENABLED. Validating parameters.")
|
||||||
|
min_snr_val = validate_parse(validation_errors, peak_selection_min_snr, "Min SNR", Float64, "Peak Selection")
|
||||||
|
min_fwhm_ppm_val = validate_parse(validation_errors, peak_selection_min_fwhm_ppm, "Min FWHM", Float64, "Peak Selection")
|
||||||
|
max_fwhm_ppm_val = validate_parse(validation_errors, peak_selection_max_fwhm_ppm, "Max FWHM", Float64, "Peak Selection")
|
||||||
|
min_shape_r2_val = validate_parse(validation_errors, peak_selection_min_shape_r2, "Min Shape R2", Float64, "Peak Selection")
|
||||||
|
final_params[:PeakSelection] = Dict{Symbol, Any}(
|
||||||
|
:min_snr => something(min_snr_val, 0.0),
|
||||||
|
:min_fwhm_ppm => something(min_fwhm_ppm_val, 0.0),
|
||||||
|
:max_fwhm_ppm => something(max_fwhm_ppm_val, Inf),
|
||||||
|
:min_shape_r2 => something(min_shape_r2_val, 0.0)
|
||||||
|
)
|
||||||
|
if min_snr_val !== nothing && min_snr_val < 0
|
||||||
|
push!(validation_errors, "(Peak Selection) Min SNR must be non-negative.")
|
||||||
|
end
|
||||||
|
if min_fwhm_ppm_val !== nothing && min_fwhm_ppm_val < 0
|
||||||
|
push!(validation_errors, "(Peak Selection) Min FWHM must be non-negative.")
|
||||||
|
end
|
||||||
|
if max_fwhm_ppm_val !== nothing && max_fwhm_ppm_val < 0
|
||||||
|
push!(validation_errors, "(Peak Selection) Max FWHM must be non-negative.")
|
||||||
|
end
|
||||||
|
if min_shape_r2_val !== nothing && (min_shape_r2_val < 0 || min_shape_r2_val > 1)
|
||||||
|
push!(validation_errors, "(Peak Selection) Min Shape R2 must be between 0 and 1.")
|
||||||
|
end
|
||||||
|
println("DEBUG: Peak Selection parameters set: min_snr=$(something(min_snr_val, 0.0)), min_fwhm_ppm=$(something(min_fwhm_ppm_val, 0.0))...")
|
||||||
|
else
|
||||||
|
println("DEBUG: Peak Selection step is DISABLED. Skipping parameter validation.")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# --- Calibration ---
|
||||||
|
println("DEBUG: Checking Calibration step (name: calibration)")
|
||||||
|
if is_step_enabled("calibration", pipeline_step_order)
|
||||||
|
println("DEBUG: Calibration step is ENABLED. Validating parameters.")
|
||||||
|
ppm_tolerance_cal_val = validate_parse(validation_errors, calibration_ppm_tolerance, "PPM Tolerance", Float64, "Calibration")
|
||||||
|
fit_order_val = validate_parse(validation_errors, calibration_fit_order, "Fit Order", Int, "Calibration")
|
||||||
|
final_params[:Calibration] = Dict{Symbol, Any}(
|
||||||
|
:method => :internal_standards, # Fixed method
|
||||||
|
:ppm_tolerance => something(ppm_tolerance_cal_val, 20.0),
|
||||||
|
:fit_order => something(fit_order_val, 1) # Default to linear
|
||||||
|
)
|
||||||
|
if ppm_tolerance_cal_val !== nothing && ppm_tolerance_cal_val < 0
|
||||||
|
push!(validation_errors, "(Calibration) PPM Tolerance must be non-negative.")
|
||||||
|
end
|
||||||
|
if fit_order_val !== nothing && (fit_order_val < 0 || fit_order_val > 2)
|
||||||
|
push!(validation_errors, "(Calibration) Fit Order must be 0, 1, or 2.")
|
||||||
|
end
|
||||||
|
if enable_standards && isempty(ref_peaks)
|
||||||
|
push!(validation_errors, "(Calibration) Internal Standards are enabled, but no reference peaks are defined.")
|
||||||
|
end
|
||||||
|
println("DEBUG: Calibration parameters set: ppm_tolerance=$(something(ppm_tolerance_cal_val, 20.0)), fit_order=$(something(fit_order_val, 1)).")
|
||||||
|
else
|
||||||
|
println("DEBUG: Calibration step is DISABLED. Skipping parameter validation.")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# --- Peak Alignment ---
|
||||||
|
println("DEBUG: Checking Peak Alignment step (name: peak_alignment)")
|
||||||
|
if is_step_enabled("peak_alignment", pipeline_step_order)
|
||||||
|
println("DEBUG: Peak Alignment step is ENABLED. Validating parameters.")
|
||||||
|
alignment_tolerance_val = validate_parse(validation_errors, alignment_tolerance, "Tolerance", Float64, "Peak Alignment")
|
||||||
|
final_params[:PeakAlignment] = Dict{Symbol, Any}(
|
||||||
|
:method => Symbol(alignment_method),
|
||||||
|
:tolerance => something(alignment_tolerance_val, 0.002),
|
||||||
|
:tolerance_unit => Symbol(alignment_tolerance_unit)
|
||||||
|
)
|
||||||
|
if alignment_tolerance_val !== nothing && alignment_tolerance_val < 0
|
||||||
|
push!(validation_errors, "(Peak Alignment) Tolerance must be non-negative.")
|
||||||
|
end
|
||||||
|
println("DEBUG: Peak Alignment parameters set: method=$(alignment_method), tolerance=$(something(alignment_tolerance_val, 0.002)), tolerance_unit=$(alignment_tolerance_unit).")
|
||||||
|
else
|
||||||
|
println("DEBUG: Peak Alignment step is DISABLED. Skipping parameter validation.")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# --- Peak Binning ---
|
||||||
|
println("DEBUG: Checking Peak Binning step (name: peak_binning)")
|
||||||
|
if is_step_enabled("peak_binning", pipeline_step_order)
|
||||||
|
println("DEBUG: Peak Binning step is ENABLED. Validating parameters.")
|
||||||
|
binning_tolerance_val = validate_parse(validation_errors, binning_tolerance, "Tolerance", Float64, "Peak Binning")
|
||||||
|
min_peak_per_bin_val = validate_parse(validation_errors, binning_min_peak_per_bin, "Min Peaks Per Bin", Int, "Peak Binning")
|
||||||
|
final_params[:PeakBinning] = Dict{Symbol, Any}(
|
||||||
|
:method => Symbol(binning_method),
|
||||||
|
:tolerance => something(binning_tolerance_val, 20.0),
|
||||||
|
:tolerance_unit => Symbol(binning_tolerance_unit),
|
||||||
|
:min_peak_per_bin => something(min_peak_per_bin_val, 3)
|
||||||
|
)
|
||||||
|
if binning_tolerance_val !== nothing && binning_tolerance_val < 0
|
||||||
|
push!(validation_errors, "(Peak Binning) Tolerance must be non-negative.")
|
||||||
|
end
|
||||||
|
if min_peak_per_bin_val !== nothing && min_peak_per_bin_val < 1
|
||||||
|
push!(validation_errors, "(Peak Binning) Min Peaks Per Bin must be positive.")
|
||||||
|
end
|
||||||
|
println("DEBUG: Peak Binning parameters set: method=$(binning_method), tolerance=$(something(binning_tolerance_val, 20.0)), min_peak_per_bin=$(something(min_peak_per_bin_val, 3))...")
|
||||||
|
else
|
||||||
|
println("DEBUG: Peak Binning step is DISABLED. Skipping parameter validation.")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
if !isempty(validation_errors)
|
||||||
|
msg = "Pipeline setup errors:\n" * join(validation_errors, "\n")
|
||||||
|
warning_msg = true
|
||||||
|
println("DEBUG: Validation errors encountered: $validation_errors")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
# Build pipeline steps from enabled steps in order
|
# Build pipeline steps from enabled steps in order
|
||||||
pipeline_stp = [step["name"] for step in pipeline_step_order if step["enabled"]]
|
pipeline_stp = [step["name"] for step in pipeline_step_order if step["enabled"]]
|
||||||
|
println("DEBUG: Final enabled pipeline steps to execute: $pipeline_stp")
|
||||||
# Convert reference peaks
|
|
||||||
ref_peaks = Dict(p["mz"] => p["label"] for p in reference_peaks_list)
|
|
||||||
|
|
||||||
# 3. Execute Pipeline
|
# 3. Execute Pipeline
|
||||||
current_pipeline_step = "Running preprocessing pipeline..."
|
current_pipeline_step = "Running preprocessing pipeline..."
|
||||||
|
println("DEBUG: Starting pipeline execution with $(length(pipeline_stp)) enabled steps.")
|
||||||
feature_matrix_result, bin_info_result = execute_full_preprocessing(
|
feature_matrix_result, bin_info_result = execute_full_preprocessing(
|
||||||
current_spectra,
|
current_spectra,
|
||||||
final_params,
|
final_params,
|
||||||
pipeline_stp,
|
pipeline_stp,
|
||||||
ref_peaks,
|
ref_peaks,
|
||||||
maskEnabled ? preprocessing_mask_route : nothing
|
mask_path_for_pipeline
|
||||||
) do step
|
) do step
|
||||||
current_pipeline_step = "Processing: $step"
|
current_pipeline_step = "Processing: $step"
|
||||||
|
println("DEBUG: Processing step: $step")
|
||||||
end
|
end
|
||||||
|
println("DEBUG: Pipeline execution finished.")
|
||||||
|
|
||||||
# 4. Update Results Display
|
# 4. Update Results Display
|
||||||
current_pipeline_step = "Updating results..."
|
current_pipeline_step = "Updating results..."
|
||||||
|
println("DEBUG: Updating results display after pipeline completion.")
|
||||||
|
|
||||||
# Find the spectrum to display in "after" plot
|
# Find the spectrum to display in "after" plot
|
||||||
display_spectrum_idx = findfirst(s -> s.id == selected_spectrum_id_for_plot, current_spectra)
|
display_spectrum_idx = findfirst(s -> s.id == selected_spectrum_id_for_plot, current_spectra)
|
||||||
if display_spectrum_idx !== nothing
|
if display_spectrum_idx !== nothing
|
||||||
processed_spectrum = current_spectra[display_spectrum_idx]
|
processed_spectrum = current_spectra[display_spectrum_idx]
|
||||||
|
println("DEBUG: Displaying spectrum $(selected_spectrum_id_for_plot) after processing.")
|
||||||
|
|
||||||
# Create "after" plot data
|
|
||||||
after_trace = PlotlyBase.scatter(
|
after_trace = PlotlyBase.scatter(
|
||||||
x=processed_spectrum.mz,
|
x=processed_spectrum.mz,
|
||||||
y=processed_spectrum.intensity,
|
y=processed_spectrum.intensity,
|
||||||
@ -1060,10 +1290,9 @@ end
|
|||||||
|
|
||||||
traces_after = [after_trace]
|
traces_after = [after_trace]
|
||||||
|
|
||||||
# Add peaks if they exist
|
|
||||||
if !isempty(processed_spectrum.peaks)
|
if !isempty(processed_spectrum.peaks)
|
||||||
peak_mzs = [p.mz for p in processed_spectrum.peaks]
|
peak_mzs = [p.mz for p in processed_spectrum.peaks]
|
||||||
peak_intensities = [p.intensity for p in processed_spectrum.peaks] # Fixed: Should be peak.intensity
|
peak_intensities = [p.intensity for p in processed_spectrum.peaks]
|
||||||
peak_trace = PlotlyBase.scatter(
|
peak_trace = PlotlyBase.scatter(
|
||||||
x=peak_mzs,
|
x=peak_mzs,
|
||||||
y=peak_intensities,
|
y=peak_intensities,
|
||||||
@ -1072,14 +1301,19 @@ end
|
|||||||
marker=attr(color="red", size=8)
|
marker=attr(color="red", size=8)
|
||||||
)
|
)
|
||||||
push!(traces_after, peak_trace)
|
push!(traces_after, peak_trace)
|
||||||
|
println("DEBUG: $(length(processed_spectrum.peaks)) peaks picked for spectrum $(selected_spectrum_id_for_plot).")
|
||||||
|
else
|
||||||
|
println("DEBUG: No peaks picked for spectrum $(selected_spectrum_id_for_plot).")
|
||||||
end
|
end
|
||||||
|
|
||||||
plotdata_after = traces_after
|
plotdata_after = traces_after
|
||||||
plotlayout_after = PlotlyBase.Layout(
|
plotlayout_after = PlotlyBase.Layout(
|
||||||
title="After Preprocessing (Spectrum $selected_spectrum_id_for_plot)",
|
title="After Preprocessing (Spectrum $(selected_spectrum_id_for_plot))",
|
||||||
xaxis_title="m/z",
|
xaxis_title="m/z",
|
||||||
yaxis_title="Intensity"
|
yaxis_title="Intensity"
|
||||||
)
|
)
|
||||||
|
else
|
||||||
|
println("DEBUG: Selected spectrum for display ($(selected_spectrum_id_for_plot)) not found in processed spectra.")
|
||||||
end
|
end
|
||||||
|
|
||||||
# Save feature matrix if binning was performed
|
# Save feature matrix if binning was performed
|
||||||
@ -1088,18 +1322,254 @@ end
|
|||||||
mkpath(output_dir)
|
mkpath(output_dir)
|
||||||
save_feature_matrix(feature_matrix_result, bin_info_result, output_dir)
|
save_feature_matrix(feature_matrix_result, bin_info_result, output_dir)
|
||||||
msg = "Pipeline completed successfully. Feature matrix saved."
|
msg = "Pipeline completed successfully. Feature matrix saved."
|
||||||
|
println("DEBUG: Feature matrix saved to $output_dir")
|
||||||
else
|
else
|
||||||
msg = "Pipeline completed successfully."
|
msg = "Pipeline completed successfully. No feature matrix generated (binning step not enabled)."
|
||||||
|
println("DEBUG: $msg")
|
||||||
end
|
end
|
||||||
|
|
||||||
catch e
|
catch e
|
||||||
msg = "Error during pipeline execution: $e"
|
msg = "Error during pipeline execution: $e"
|
||||||
warning_msg = true
|
warning_msg = true
|
||||||
@error "Pipeline failed" exception=(e, catch_backtrace())
|
@error "Pipeline failed" exception=(e, catch_backtrace())
|
||||||
|
println("DEBUG: Pipeline caught an exception: $e")
|
||||||
finally
|
finally
|
||||||
progressPrep = false
|
progressPrep = false
|
||||||
current_pipeline_step = ""
|
current_pipeline_step = ""
|
||||||
GC.gc()
|
println("DEBUG: run_full_pipeline finished (finally block).")
|
||||||
|
GC.gc() # Trigger garbage collection
|
||||||
|
if Sys.islinux()
|
||||||
|
ccall(:malloc_trim, Int32, (Int32,), 0) # Ensure Julia returns the freed memory to OS
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@onbutton recalculate_suggestions_btn begin
|
||||||
|
if msi_data === nothing
|
||||||
|
msg = "Please load a file first."
|
||||||
|
warning_msg = true
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
try
|
||||||
|
msg = "Recalculating suggestions..."
|
||||||
|
progressPrep = true
|
||||||
|
|
||||||
|
ref_peaks = Dict(p["mz"] => p["label"] for p in reference_peaks_list)
|
||||||
|
recommended_params = main_precalculation(msi_data, reference_peaks=ref_peaks)
|
||||||
|
|
||||||
|
for (step_name, params) in recommended_params
|
||||||
|
for (param_key, value) in params
|
||||||
|
# Convert value to appropriate type before assignment
|
||||||
|
processed_value = if value === nothing
|
||||||
|
""
|
||||||
|
elseif value isa Tuple
|
||||||
|
@warn "Skipping invalid parameter suggestion (tuple): $value for $param_key"
|
||||||
|
"" # Set to empty string for safety
|
||||||
|
elseif value isa Number
|
||||||
|
string(value)
|
||||||
|
else
|
||||||
|
string(value)
|
||||||
|
end
|
||||||
|
|
||||||
|
if isempty(processed_value) && !(processed_value isa Number)
|
||||||
|
continue # Skip if processed_value is an empty string and not a number type
|
||||||
|
end
|
||||||
|
|
||||||
|
# Map recommended parameters to suggested_* reactive variables
|
||||||
|
if step_name == :Smoothing
|
||||||
|
if param_key == :window
|
||||||
|
suggested_smoothing_window = processed_value
|
||||||
|
smoothing_window = processed_value
|
||||||
|
elseif param_key == :order
|
||||||
|
suggested_smoothing_order = processed_value
|
||||||
|
smoothing_order = processed_value
|
||||||
|
end
|
||||||
|
elseif step_name == :BaselineCorrection
|
||||||
|
if param_key == :iterations
|
||||||
|
suggested_baseline_iterations = processed_value
|
||||||
|
baseline_iterations = processed_value
|
||||||
|
elseif param_key == :window
|
||||||
|
suggested_baseline_window = processed_value
|
||||||
|
baseline_window = processed_value
|
||||||
|
end
|
||||||
|
elseif step_name == :PeakAlignment
|
||||||
|
if param_key == :span
|
||||||
|
suggested_alignment_span = processed_value
|
||||||
|
alignment_span = processed_value
|
||||||
|
elseif param_key == :tolerance
|
||||||
|
suggested_alignment_tolerance = processed_value
|
||||||
|
alignment_tolerance = processed_value
|
||||||
|
elseif param_key == :max_shift_ppm
|
||||||
|
suggested_alignment_max_shift_ppm = processed_value
|
||||||
|
alignment_max_shift_ppm = processed_value
|
||||||
|
elseif param_key == :min_matched_peaks
|
||||||
|
suggested_alignment_min_matched_peaks = processed_value
|
||||||
|
alignment_min_matched_peaks = processed_value
|
||||||
|
end
|
||||||
|
elseif step_name == :Calibration
|
||||||
|
if param_key == :fit_order
|
||||||
|
suggested_calibration_fit_order = processed_value
|
||||||
|
calibration_fit_order = processed_value
|
||||||
|
elseif param_key == :ppm_tolerance
|
||||||
|
suggested_calibration_ppm_tolerance = processed_value
|
||||||
|
calibration_ppm_tolerance = processed_value
|
||||||
|
end
|
||||||
|
elseif step_name == :PeakPicking
|
||||||
|
if param_key == :snr_threshold
|
||||||
|
suggested_peak_picking_snr_threshold = processed_value
|
||||||
|
peak_picking_snr_threshold = processed_value
|
||||||
|
elseif param_key == :half_window
|
||||||
|
suggested_peak_picking_half_window = processed_value
|
||||||
|
peak_picking_half_window = processed_value
|
||||||
|
elseif param_key == :min_peak_prominence
|
||||||
|
suggested_peak_picking_min_peak_prominence = processed_value
|
||||||
|
peak_picking_min_peak_prominence = processed_value
|
||||||
|
elseif param_key == :merge_peaks_tolerance
|
||||||
|
suggested_peak_picking_merge_peaks_tolerance = processed_value
|
||||||
|
peak_picking_merge_peaks_tolerance = processed_value
|
||||||
|
elseif param_key == :min_peak_width_ppm
|
||||||
|
suggested_peak_picking_min_peak_width_ppm = processed_value
|
||||||
|
peak_picking_min_peak_width_ppm = processed_value
|
||||||
|
elseif param_key == :max_peak_width_ppm
|
||||||
|
suggested_peak_picking_max_peak_width_ppm = processed_value
|
||||||
|
peak_picking_max_peak_width_ppm = processed_value
|
||||||
|
elseif param_key == :min_peak_shape_r2
|
||||||
|
suggested_peak_picking_min_peak_shape_r2 = processed_value
|
||||||
|
peak_picking_min_peak_shape_r2 = processed_value
|
||||||
|
end
|
||||||
|
elseif step_name == :PeakSelection
|
||||||
|
if param_key == :min_snr
|
||||||
|
suggested_peak_selection_min_snr = processed_value
|
||||||
|
peak_selection_min_snr = processed_value
|
||||||
|
elseif param_key == :min_fwhm_ppm
|
||||||
|
suggested_peak_selection_min_fwhm_ppm = processed_value
|
||||||
|
peak_selection_min_fwhm_ppm = processed_value
|
||||||
|
elseif param_key == :max_fwhm_ppm
|
||||||
|
suggested_peak_selection_max_fwhm_ppm = processed_value
|
||||||
|
peak_selection_max_fwhm_ppm = processed_value
|
||||||
|
elseif param_key == :min_shape_r2
|
||||||
|
suggested_peak_selection_min_shape_r2 = processed_value
|
||||||
|
peak_selection_min_shape_r2 = processed_value
|
||||||
|
elseif param_key == :frequency_threshold
|
||||||
|
suggested_peak_selection_frequency_threshold = processed_value
|
||||||
|
peak_selection_frequency_threshold = processed_value
|
||||||
|
elseif param_key == :correlation_threshold
|
||||||
|
suggested_peak_selection_correlation_threshold = processed_value
|
||||||
|
peak_selection_correlation_threshold = processed_value
|
||||||
|
end
|
||||||
|
elseif step_name == :PeakBinning
|
||||||
|
if param_key == :tolerance
|
||||||
|
suggested_binning_tolerance = processed_value
|
||||||
|
binning_tolerance = processed_value
|
||||||
|
elseif param_key == :frequency_threshold
|
||||||
|
suggested_binning_frequency_threshold = processed_value
|
||||||
|
binning_frequency_threshold = processed_value
|
||||||
|
elseif param_key == :min_peak_per_bin
|
||||||
|
suggested_binning_min_peak_per_bin = processed_value
|
||||||
|
binning_min_peak_per_bin = processed_value
|
||||||
|
elseif param_key == :max_bin_width_ppm
|
||||||
|
suggested_binning_max_bin_width_ppm = processed_value
|
||||||
|
binning_max_bin_width_ppm = processed_value
|
||||||
|
elseif param_key == :num_uniform_bins
|
||||||
|
suggested_binning_num_uniform_bins = processed_value
|
||||||
|
binning_num_uniform_bins = processed_value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Also set method types for steps
|
||||||
|
if haskey(recommended_params, :Smoothing) && haskey(recommended_params[:Smoothing], :method)
|
||||||
|
smoothing_method = string(recommended_params[:Smoothing][:method])
|
||||||
|
end
|
||||||
|
if haskey(recommended_params, :BaselineCorrection) && haskey(recommended_params[:BaselineCorrection], :method)
|
||||||
|
baseline_method = string(recommended_params[:BaselineCorrection][:method])
|
||||||
|
end
|
||||||
|
if haskey(recommended_params, :Normalization) && haskey(recommended_params[:Normalization], :method)
|
||||||
|
normalization_method = string(recommended_params[:Normalization][:method])
|
||||||
|
end
|
||||||
|
if haskey(recommended_params, :PeakAlignment) && haskey(recommended_params[:PeakAlignment], :method)
|
||||||
|
alignment_method = string(recommended_params[:PeakAlignment][:method])
|
||||||
|
end
|
||||||
|
if haskey(recommended_params, :PeakPicking) && haskey(recommended_params[:PeakPicking], :method)
|
||||||
|
peak_picking_method = string(recommended_params[:PeakPicking][:method])
|
||||||
|
end
|
||||||
|
if haskey(recommended_params, :PeakBinning) && haskey(recommended_params[:PeakBinning], :method)
|
||||||
|
binning_method = string(recommended_params[:PeakBinning][:method])
|
||||||
|
end
|
||||||
|
if haskey(recommended_params, :PeakAlignment) && haskey(recommended_params[:PeakAlignment], :tolerance_unit)
|
||||||
|
alignment_tolerance_unit = string(recommended_params[:PeakAlignment][:tolerance_unit])
|
||||||
|
end
|
||||||
|
if haskey(recommended_params, :PeakBinning) && haskey(recommended_params[:PeakBinning], :tolerance_unit)
|
||||||
|
binning_tolerance_unit = string(recommended_params[:PeakBinning][:tolerance_unit])
|
||||||
|
end
|
||||||
|
|
||||||
|
msg = "Suggestions have been recalculated."
|
||||||
|
catch e
|
||||||
|
msg = "Failed to recalculate suggestions: $e"
|
||||||
|
warning_msg = true
|
||||||
|
@error "Recalculation failed" exception=(e, catch_backtrace())
|
||||||
|
finally
|
||||||
|
progressPrep = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@onbutton addReferencePeak begin
|
||||||
|
new_list = deepcopy(reference_peaks_list)
|
||||||
|
push!(new_list, Dict("mz" => 0.0, "label" => ""))
|
||||||
|
reference_peaks_list = new_list # Assign new list to trigger reactivity
|
||||||
|
end
|
||||||
|
|
||||||
|
@onbutton remove_peak_trigger begin
|
||||||
|
if action_index > -1
|
||||||
|
julia_index = action_index + 1
|
||||||
|
new_list = deepcopy(reference_peaks_list)
|
||||||
|
if 1 <= julia_index <= length(new_list)
|
||||||
|
deleteat!(new_list, julia_index)
|
||||||
|
reference_peaks_list = new_list
|
||||||
|
end
|
||||||
|
action_index = -1 # Reset
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@onbutton move_step_up_trigger begin
|
||||||
|
if action_index > -1
|
||||||
|
julia_index = action_index + 1
|
||||||
|
if julia_index > 1
|
||||||
|
new_order = deepcopy(pipeline_step_order)
|
||||||
|
temp = new_order[julia_index]
|
||||||
|
new_order[julia_index] = new_order[julia_index - 1]
|
||||||
|
new_order[julia_index - 1] = temp
|
||||||
|
pipeline_step_order = new_order
|
||||||
|
end
|
||||||
|
action_index = -1 # Reset
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@onbutton move_step_down_trigger begin
|
||||||
|
if action_index > -1
|
||||||
|
julia_index = action_index + 1
|
||||||
|
if julia_index < length(pipeline_step_order)
|
||||||
|
new_order = deepcopy(pipeline_step_order)
|
||||||
|
temp = new_order[julia_index]
|
||||||
|
new_order[julia_index] = new_order[julia_index + 1]
|
||||||
|
new_order[julia_index + 1] = temp
|
||||||
|
pipeline_step_order = new_order
|
||||||
|
end
|
||||||
|
action_index = -1 # Reset
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@onbutton toggle_step_trigger begin
|
||||||
|
if action_index > -1
|
||||||
|
julia_index = action_index + 1
|
||||||
|
if 1 <= julia_index <= length(pipeline_step_order)
|
||||||
|
new_order = deepcopy(pipeline_step_order)
|
||||||
|
new_order[julia_index]["enabled"] = !new_order[julia_index]["enabled"]
|
||||||
|
pipeline_step_order = new_order
|
||||||
|
end
|
||||||
|
action_index = -1 # Reset
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1633,9 +2103,11 @@ end
|
|||||||
|
|
||||||
# Convert to positive coordinates for processing
|
# Convert to positive coordinates for processing
|
||||||
y_positive = yCoord < 0 ? abs(yCoord) : yCoord
|
y_positive = yCoord < 0 ? abs(yCoord) : yCoord
|
||||||
plotdata, plotlayout, xSpectraMz, ySpectraMz = xySpectrumPlot(msi_data, xCoord, y_positive, imgWidth, imgHeight, selected_folder_main, mask_path=mask_path_for_plot)
|
plotdata, plotlayout, xSpectraMz, ySpectraMz, spectrum_id = xySpectrumPlot(msi_data, xCoord, y_positive, imgWidth, imgHeight, selected_folder_main, mask_path=mask_path_for_plot)
|
||||||
plotdata_before = plotdata
|
plotdata_before = plotdata
|
||||||
plotlayout_before = plotlayout
|
plotlayout_before = plotlayout
|
||||||
|
selected_spectrum_id_for_plot = spectrum_id
|
||||||
|
idSpectrum = spectrum_id # we set the same obtained spectrum id to the UI
|
||||||
|
|
||||||
# Update coordinates based on actual plot title
|
# Update coordinates based on actual plot title
|
||||||
# Extract title text from the Dict safely
|
# Extract title text from the Dict safely
|
||||||
@ -1688,6 +2160,93 @@ end
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@onbutton createNSpectrumPlot @time begin
|
||||||
|
if isempty(selected_folder_main)
|
||||||
|
msg = "No dataset selected. Please process a file and select a folder first."
|
||||||
|
warning_msg = true
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
progressSpectraPlot = true
|
||||||
|
btnStartDisable = true
|
||||||
|
btnPlotDisable = true
|
||||||
|
btnSpectraDisable = true
|
||||||
|
msg = "Loading plot for $(selected_folder_main)..."
|
||||||
|
|
||||||
|
try
|
||||||
|
sTime = time()
|
||||||
|
registry = load_registry(registry_path)
|
||||||
|
|
||||||
|
# Add error handling for registry access
|
||||||
|
if !haskey(registry, selected_folder_main)
|
||||||
|
msg = "Dataset '$(selected_folder_main)' not found in registry."
|
||||||
|
warning_msg = true
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
entry = registry[selected_folder_main]
|
||||||
|
target_path = entry["source_path"]
|
||||||
|
|
||||||
|
if target_path == "unknown (manually added)"
|
||||||
|
msg = "Dataset selected contained no route."
|
||||||
|
warning_msg = true
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if msi_data === nothing || full_route != target_path
|
||||||
|
if msi_data !== nothing
|
||||||
|
close(msi_data)
|
||||||
|
end
|
||||||
|
msg = "Reloading $(basename(target_path)) for analysis..."
|
||||||
|
full_route = target_path
|
||||||
|
msi_data = OpenMSIData(target_path)
|
||||||
|
if haskey(get(entry, "metadata", Dict()), "global_min_mz") && entry["metadata"]["global_min_mz"] !== nothing
|
||||||
|
raw_min = entry["metadata"]["global_min_mz"]
|
||||||
|
raw_max = entry["metadata"]["global_max_mz"]
|
||||||
|
min_val = isa(raw_min, Dict) ? get(raw_min, "value", raw_min) : raw_min
|
||||||
|
max_val = isa(raw_max, Dict) ? get(raw_max, "value", raw_max) : raw_max
|
||||||
|
set_global_mz_range!(msi_data, convert(Float64, min_val), convert(Float64, max_val))
|
||||||
|
else
|
||||||
|
precompute_analytics(msi_data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local mask_path_for_plot::Union{String, Nothing} = nothing
|
||||||
|
if maskEnabled && get(entry, "has_mask", false)
|
||||||
|
mask_path_for_plot = get(entry, "mask_path", "")
|
||||||
|
if !isfile(mask_path_for_plot)
|
||||||
|
@warn "Mask not found for plotting: $(mask_path_for_plot). Plotting without mask."
|
||||||
|
mask_path_for_plot = nothing
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Call the new nSpectrumPlot function
|
||||||
|
plotdata, plotlayout, xSpectraMz, ySpectraMz, spectrum_id = nSpectrumPlot(msi_data, idSpectrum, selected_folder_main, mask_path=mask_path_for_plot)
|
||||||
|
plotdata_before = plotdata
|
||||||
|
plotlayout_before = plotlayout
|
||||||
|
selected_spectrum_id_for_plot = spectrum_id
|
||||||
|
|
||||||
|
selectedTab = "tab2"
|
||||||
|
fTime = time()
|
||||||
|
eTime = round(fTime - sTime, digits=3)
|
||||||
|
msg = "Plot loaded in $(eTime) seconds"
|
||||||
|
log_memory_usage("nSpectrum Plot Generated", msi_data)
|
||||||
|
catch e
|
||||||
|
msg = "Could not retrieve spectrum: $e"
|
||||||
|
warning_msg = true
|
||||||
|
@error "nSpectrum plotting failed" exception=(e, catch_backtrace())
|
||||||
|
finally
|
||||||
|
progressSpectraPlot = false
|
||||||
|
btnPlotDisable = false
|
||||||
|
btnSpectraDisable = false
|
||||||
|
btnStartDisable = false
|
||||||
|
GC.gc() # Trigger garbage collection
|
||||||
|
if Sys.islinux()
|
||||||
|
ccall(:malloc_trim, Int32, (Int32,), 0) # Ensure Julia returns the freed memory to OS
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# --- Main View Handlers ---
|
# --- Main View Handlers ---
|
||||||
@onbutton imgMinus begin
|
@onbutton imgMinus begin
|
||||||
if isempty(selected_folder_main) return end
|
if isempty(selected_folder_main) return end
|
||||||
|
|||||||
110
app.jl.html
110
app.jl.html
@ -62,6 +62,11 @@
|
|||||||
<q-item clickable v-close-popup v-on:click="createXYPlot=true">
|
<q-item clickable v-close-popup v-on:click="createXYPlot=true">
|
||||||
<q-item-label>Spectrum plot (X,Y)</q-item-label>
|
<q-item-label>Spectrum plot (X,Y)</q-item-label>
|
||||||
</q-item>
|
</q-item>
|
||||||
|
<q-item clickable v-close-popup v-on:click="createNSpectrumPlot=true">
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label>Spectrum plot (ID)</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
</q-btn-dropdown>
|
</q-btn-dropdown>
|
||||||
<div class="row col-6">
|
<div class="row col-6">
|
||||||
@ -86,20 +91,21 @@
|
|||||||
<q-card class="q-mb-md">
|
<q-card class="q-mb-md">
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<div class="text-h6">Internal Standards</div>
|
<div class="text-h6">Internal Standards</div>
|
||||||
<div class="text-caption">Reference peaks for calibration and alignment</div>
|
<div class="text-caption">Reference peaks for calibration, alignment and precise suggestion calculations</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
|
<q-toggle v-model="enable_standards" v-on:click="enable_standards" label="Use Internal Standards" color="primary" class="q-mb-md" />
|
||||||
<!-- Keep your existing reference_peaks_list implementation -->
|
<!-- Keep your existing reference_peaks_list implementation -->
|
||||||
<q-list bordered separator class="q-mt-md">
|
<q-list bordered separator class="q-mt-md">
|
||||||
<q-item v-for="(peak, index) in reference_peaks_list" :key="index">
|
<q-item v-for="(peak, index) in reference_peaks_list" :key="index">
|
||||||
<q-item-section avatar>
|
<q-item-section avatar>
|
||||||
<q-btn flat round icon="delete" color="negative" @click="removeReferencePeak(index)"></q-btn>
|
<q-btn flat round icon="delete" color="negative" v-on:click="action_index = index; remove_peak_trigger = true"></q-btn>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<div class="row q-col-gutter-sm">
|
<div class="row q-col-gutter-sm">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="m/z" type="number" step="0.0001"
|
<q-input standout="custom-standout" label="m/z" type="number" step="0.0001"
|
||||||
v-model.number="peak.mz" :rules="[val => !!val || 'Required', val => val > 0 || 'Must be positive']"></q-input>
|
v-model="peak.mz" :rules="[val => !!val || 'Required', val => val > 0 || 'Must be positive']"></q-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="Label (optional)" v-model="peak.label"></q-input>
|
<q-input standout="custom-standout" label="Label (optional)" v-model="peak.label"></q-input>
|
||||||
@ -109,13 +115,15 @@
|
|||||||
</q-item>
|
</q-item>
|
||||||
<q-item>
|
<q-item>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-btn class="q-ma-sm btn-style" icon="add" label="Add Reference Peak" @click="addReferencePeak"></q-btn>
|
<q-btn class="q-ma-sm btn-style" icon="add" label="Add Reference Peak" v-on:click="addReferencePeak=true"></q-btn>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
</q-card-section>
|
<div class="row justify-end q-mt-sm">
|
||||||
</q-card>
|
<q-btn class="q-ma-sm" icon="functions" v-on:click="recalculate_suggestions_btn=true" label="Recalculate Suggestions" outline hint="Re-run automatic parameter suggestion using the current list of internal standards."/>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
<!-- Reorderable Preprocessing Steps -->
|
<!-- Reorderable Preprocessing Steps -->
|
||||||
<div class="text-h6 q-mb-md">Preprocessing Pipeline</div>
|
<div class="text-h6 q-mb-md">Preprocessing Pipeline</div>
|
||||||
<q-list bordered>
|
<q-list bordered>
|
||||||
@ -128,9 +136,9 @@
|
|||||||
<q-item-section avatar>
|
<q-item-section avatar>
|
||||||
<div class="row no-wrap">
|
<div class="row no-wrap">
|
||||||
<q-btn flat round icon="arrow_upward" size="sm"
|
<q-btn flat round icon="arrow_upward" size="sm"
|
||||||
:disable="index === 0" @click.stop="moveStepUp(index)"></q-btn>
|
:disable="index === 0" v-on:click.stop="action_index = index; move_step_up_trigger = true"></q-btn>
|
||||||
<q-btn flat round icon="arrow_downward" size="sm"
|
<q-btn flat round icon="arrow_downward" size="sm"
|
||||||
:disable="index === pipeline_step_order.length - 1" @click.stop="moveStepDown(index)"></q-btn>
|
:disable="index === pipeline_step_order.length - 1" v-on:click.stop="action_index = index; move_step_down_trigger = true"></q-btn>
|
||||||
</div>
|
</div>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
|
|
||||||
@ -139,7 +147,7 @@
|
|||||||
</q-item-section>
|
</q-item-section>
|
||||||
|
|
||||||
<q-item-section side>
|
<q-item-section side>
|
||||||
<q-toggle v-model="step.enabled" color="green" @click.stop />
|
<q-toggle v-model="step.enabled" color="green" v-on:click.stop="action_index = index; toggle_step_trigger = true" />
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -160,10 +168,10 @@
|
|||||||
<q-radio v-model="smoothing_method" val="ma" label="Moving Average" />
|
<q-radio v-model="smoothing_method" val="ma" label="Moving Average" />
|
||||||
<div class="row q-col-gutter-sm q-mt-md">
|
<div class="row q-col-gutter-sm q-mt-md">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="Window Size" v-model.number="smoothing_window" type="number" />
|
<q-input standout="custom-standout" label="Window Size" v-model="smoothing_window" type="number" />
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="Order (Savitzky-Golay)" v-model.number="smoothing_order" type="number" />
|
<q-input standout="custom-standout" label="Order (Savitzky-Golay)" v-model="smoothing_order" type="number" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
@ -186,11 +194,11 @@
|
|||||||
<div class="row q-col-gutter-sm">
|
<div class="row q-col-gutter-sm">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="Iterations (for SNIP)" type="number"
|
<q-input standout="custom-standout" label="Iterations (for SNIP)" type="number"
|
||||||
:placeholder="suggested_baseline_iterations" v-model.number="baseline_iterations" hint="The number of iterations for the SNIP algorithm. A higher number results in a more aggressive baseline."></q-input>
|
:placeholder="suggested_baseline_iterations" v-model="baseline_iterations" hint="The number of iterations for the SNIP algorithm. A higher number results in a more aggressive baseline."></q-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="Window (for Median)" type="number"
|
<q-input standout="custom-standout" label="Window (for Median)" type="number"
|
||||||
:placeholder="suggested_baseline_window" v-model.number="baseline_window" hint="The window size for the median method, determining the local region for median calculation."></q-input>
|
:placeholder="suggested_baseline_window" v-model="baseline_window" hint="The window size for the median method, determining the local region for median calculation."></q-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
@ -226,11 +234,11 @@
|
|||||||
<div class="row q-col-gutter-sm">
|
<div class="row q-col-gutter-sm">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="Span (for LOWESS)" type="number" step="0.01"
|
<q-input standout="custom-standout" label="Span (for LOWESS)" type="number" step="0.01"
|
||||||
:placeholder="suggested_alignment_span" v-model.number="alignment_span" :rules="[val => val >= 0.0 && val <= 1.0 || 'Needs to be between 0 and 1']" hint="The span parameter for LOWESS regression, controlling smoothness (0.0 to 1.0)."></q-input>
|
:placeholder="suggested_alignment_span" v-model="alignment_span" :rules="[val => val >= 0.0 && val <= 1.0 || 'Needs to be between 0 and 1']" hint="The span parameter for LOWESS regression, controlling smoothness (0.0 to 1.0)."></q-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="Tolerance" type="number" step="0.001"
|
<q-input standout="custom-standout" label="Tolerance" type="number" step="0.001"
|
||||||
:placeholder="suggested_alignment_tolerance" v-model.number="alignment_tolerance" hint="The tolerance for matching peaks between the target and reference spectrum."></q-input>
|
:placeholder="suggested_alignment_tolerance" v-model="alignment_tolerance" hint="The tolerance for matching peaks between the target and reference spectrum."></q-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<q-select standout="custom-standout" label="Tolerance Unit" v-model="alignment_tolerance_unit"
|
<q-select standout="custom-standout" label="Tolerance Unit" v-model="alignment_tolerance_unit"
|
||||||
@ -238,11 +246,11 @@
|
|||||||
<div class="row q-col-gutter-sm q-mt-md">
|
<div class="row q-col-gutter-sm q-mt-md">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="Max Shift PPM" type="number"
|
<q-input standout="custom-standout" label="Max Shift PPM" type="number"
|
||||||
:placeholder="suggested_alignment_max_shift_ppm" v-model.number="alignment_max_shift_ppm" hint="The maximum allowed m/z shift in ppm to prevent spurious peak matches."></q-input>
|
:placeholder="suggested_alignment_max_shift_ppm" v-model="alignment_max_shift_ppm" hint="The maximum allowed m/z shift in ppm to prevent spurious peak matches."></q-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="Min Matched Peaks" type="number"
|
<q-input standout="custom-standout" label="Min Matched Peaks" type="number"
|
||||||
:placeholder="suggested_alignment_min_matched_peaks" v-model.number="alignment_min_matched_peaks" hint="The minimum number of matching peaks required to perform the alignment."></q-input>
|
:placeholder="suggested_alignment_min_matched_peaks" v-model="alignment_min_matched_peaks" hint="The minimum number of matching peaks required to perform the alignment."></q-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
@ -256,12 +264,12 @@
|
|||||||
<div class="row q-col-gutter-sm">
|
<div class="row q-col-gutter-sm">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="Fit Order" type="number"
|
<q-input standout="custom-standout" label="Fit Order" type="number"
|
||||||
:placeholder="suggested_calibration_fit_order" v-model.number="calibration_fit_order"
|
:placeholder="suggested_calibration_fit_order" v-model="calibration_fit_order"
|
||||||
hint="Polynomial order for the calibration curve (e.g., 1 or 2)."></q-input>
|
hint="Polynomial order for the calibration curve (e.g., 1 or 2)."></q-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="PPM Tolerance" type="number"
|
<q-input standout="custom-standout" label="PPM Tolerance" type="number"
|
||||||
:placeholder="suggested_calibration_ppm_tolerance" v-model.number="calibration_ppm_tolerance"
|
:placeholder="suggested_calibration_ppm_tolerance" v-model="calibration_ppm_tolerance"
|
||||||
hint="PPM tolerance for matching reference peaks to internal standards."></q-input>
|
hint="PPM tolerance for matching reference peaks to internal standards."></q-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -285,35 +293,35 @@
|
|||||||
<div class="row q-col-gutter-sm">
|
<div class="row q-col-gutter-sm">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="Signal to Noise Threshold" type="number" step="0.1"
|
<q-input standout="custom-standout" label="Signal to Noise Threshold" type="number" step="0.1"
|
||||||
:placeholder="suggested_peak_picking_snr_threshold" v-model.number="peak_picking_snr_threshold" hint="Signal-to-Noise Ratio threshold. Peaks with SNR below this value are discarded."></q-input>
|
:placeholder="suggested_peak_picking_snr_threshold" v-model="peak_picking_snr_threshold" hint="Signal-to-Noise Ratio threshold. Peaks with SNR below this value are discarded."></q-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="Half Window Size" type="number"
|
<q-input standout="custom-standout" label="Half Window Size" type="number"
|
||||||
:placeholder="suggested_peak_picking_half_window" v-model.number="peak_picking_half_window" hint="Number of data points to the left and right of a potential peak to consider for local maximum detection (for Profile method)."></q-input>
|
:placeholder="suggested_peak_picking_half_window" v-model="peak_picking_half_window" hint="Number of data points to the left and right of a potential peak to consider for local maximum detection (for Profile method)."></q-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row q-col-gutter-sm q-mt-md">
|
<div class="row q-col-gutter-sm q-mt-md">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="Min Peak Prominence" type="number" step="0.01"
|
<q-input standout="custom-standout" label="Min Peak Prominence" type="number" step="0.01"
|
||||||
:placeholder="suggested_peak_picking_min_peak_prominence" v-model.number="peak_picking_min_peak_prominence" hint="Minimum required prominence of a peak, expressed as a fraction of its height."></q-input>
|
:placeholder="suggested_peak_picking_min_peak_prominence" v-model="peak_picking_min_peak_prominence" hint="Minimum required prominence of a peak, expressed as a fraction of its height."></q-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="Merge Peaks Tolerance (m/z)" type="number" step="0.001"
|
<q-input standout="custom-standout" label="Merge Peaks Tolerance (m/z)" type="number" step="0.001"
|
||||||
:placeholder="suggested_peak_picking_merge_peaks_tolerance" v-model.number="peak_picking_merge_peaks_tolerance" hint="The m/z tolerance within which to merge adjacent peaks, keeping the more intense one."></q-input>
|
:placeholder="suggested_peak_picking_merge_peaks_tolerance" v-model="peak_picking_merge_peaks_tolerance" hint="The m/z tolerance within which to merge adjacent peaks, keeping the more intense one."></q-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row q-col-gutter-sm q-mt-md">
|
<div class="row q-col-gutter-sm q-mt-md">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="Min Peak Width (PPM)" type="number"
|
<q-input standout="custom-standout" label="Min Peak Width (PPM)" type="number"
|
||||||
:placeholder="suggested_peak_picking_min_peak_width_ppm" v-model.number="peak_picking_min_peak_width_ppm" hint="Minimum acceptable peak width (FWHM) in ppm."></q-input>
|
:placeholder="suggested_peak_picking_min_peak_width_ppm" v-model="peak_picking_min_peak_width_ppm" hint="Minimum acceptable peak width (FWHM) in ppm."></q-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="Max Peak Width (PPM)" type="number"
|
<q-input standout="custom-standout" label="Max Peak Width (PPM)" type="number"
|
||||||
:placeholder="suggested_peak_picking_max_peak_width_ppm" v-model.number="peak_picking_max_peak_width_ppm" hint="Maximum acceptable peak width (FWHM) in ppm."></q-input>
|
:placeholder="suggested_peak_picking_max_peak_width_ppm" v-model="peak_picking_max_peak_width_ppm" hint="Maximum acceptable peak width (FWHM) in ppm."></q-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<q-input standout="custom-standout" label="Min Peak Shape R2" type="number" step="0.01" class="q-mt-md"
|
<q-input standout="custom-standout" label="Min Peak Shape R2" type="number" step="0.01" class="q-mt-md"
|
||||||
:placeholder="suggested_peak_picking_min_peak_shape_r2" v-model.number="peak_picking_min_peak_shape_r2" hint="Minimum R-squared value from a Gaussian fit to the peak, used as a quality measure for peak shape."></q-input>
|
:placeholder="suggested_peak_picking_min_peak_shape_r2" v-model="peak_picking_min_peak_shape_r2" hint="Minimum R-squared value from a Gaussian fit to the peak, used as a quality measure for peak shape."></q-input>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
|
|
||||||
@ -325,32 +333,32 @@
|
|||||||
<div class="row q-col-gutter-sm">
|
<div class="row q-col-gutter-sm">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="Min SNR" type="number" step="0.1"
|
<q-input standout="custom-standout" label="Min SNR" type="number" step="0.1"
|
||||||
:placeholder="suggested_peak_selection_min_snr" v-model.number="peak_selection_min_snr" hint="Minimum Signal-to-Noise Ratio for a peak to be kept."></q-input>
|
:placeholder="suggested_peak_selection_min_snr" v-model="peak_selection_min_snr" hint="Minimum Signal-to-Noise Ratio for a peak to be kept."></q-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="Min FWHM (PPM)" type="number"
|
<q-input standout="custom-standout" label="Min FWHM (PPM)" type="number"
|
||||||
:placeholder="suggested_peak_selection_min_fwhm_ppm" v-model.number="peak_selection_min_fwhm_ppm" hint="Minimum Full Width at Half Maximum (FWHM) in ppm for a peak to be kept."></q-input>
|
:placeholder="suggested_peak_selection_min_fwhm_ppm" v-model="peak_selection_min_fwhm_ppm" hint="Minimum Full Width at Half Maximum (FWHM) in ppm for a peak to be kept."></q-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row q-col-gutter-sm q-mt-md">
|
<div class="row q-col-gutter-sm q-mt-md">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="Max FWHM (PPM)" type="number"
|
<q-input standout="custom-standout" label="Max FWHM (PPM)" type="number"
|
||||||
:placeholder="suggested_peak_selection_max_fwhm_ppm" v-model.number="peak_selection_max_fwhm_ppm" hint="Maximum Full Width at Half Maximum (FWHM) in ppm for a peak to be kept."></q-input>
|
:placeholder="suggested_peak_selection_max_fwhm_ppm" v-model="peak_selection_max_fwhm_ppm" hint="Maximum Full Width at Half Maximum (FWHM) in ppm for a peak to be kept."></q-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="Min Peak Shape R2" type="number" step="0.01"
|
<q-input standout="custom-standout" label="Min Peak Shape R2" type="number" step="0.01"
|
||||||
:placeholder="suggested_peak_selection_min_shape_r2" v-model.number="peak_selection_min_shape_r2" hint="Minimum R-squared value from a Gaussian fit, filtering for good peak shape."></q-input>
|
:placeholder="suggested_peak_selection_min_shape_r2" v-model="peak_selection_min_shape_r2" hint="Minimum R-squared value from a Gaussian fit, filtering for good peak shape."></q-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row q-col-gutter-sm q-mt-md">
|
<div class="row q-col-gutter-sm q-mt-md">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="Frequency Threshold" type="number" step="0.01"
|
<q-input standout="custom-standout" label="Frequency Threshold" type="number" step="0.01"
|
||||||
:placeholder="suggested_peak_selection_frequency_threshold" v-model.number="peak_selection_frequency_threshold"
|
:placeholder="suggested_peak_selection_frequency_threshold" v-model="peak_selection_frequency_threshold"
|
||||||
hint="The minimum fraction of spectra a peak must be present in to be kept (0.0 to 1.0)."></q-input>
|
hint="The minimum fraction of spectra a peak must be present in to be kept (0.0 to 1.0)."></q-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="Correlation Threshold" type="number" step="0.01"
|
<q-input standout="custom-standout" label="Correlation Threshold" type="number" step="0.01"
|
||||||
:placeholder="suggested_peak_selection_correlation_threshold" v-model.number="peak_selection_correlation_threshold"
|
:placeholder="suggested_peak_selection_correlation_threshold" v-model="peak_selection_correlation_threshold"
|
||||||
hint="Minimum correlation with neighboring peaks (not yet implemented)."></q-input>
|
hint="Minimum correlation with neighboring peaks (not yet implemented)."></q-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -373,7 +381,7 @@
|
|||||||
<div class="row q-col-gutter-sm">
|
<div class="row q-col-gutter-sm">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="Tolerance (for Adaptive)" type="number" step="0.001"
|
<q-input standout="custom-standout" label="Tolerance (for Adaptive)" type="number" step="0.001"
|
||||||
:placeholder="suggested_binning_tolerance" v-model.number="binning_tolerance" hint="Tolerance for grouping peaks into a bin in adaptive mode."></q-input>
|
:placeholder="suggested_binning_tolerance" v-model="binning_tolerance" hint="Tolerance for grouping peaks into a bin in adaptive mode."></q-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-select standout="custom-standout" label="Tolerance Unit" v-model="binning_tolerance_unit"
|
<q-select standout="custom-standout" label="Tolerance Unit" v-model="binning_tolerance_unit"
|
||||||
@ -383,24 +391,24 @@
|
|||||||
<div class="row q-col-gutter-sm q-mt-md">
|
<div class="row q-col-gutter-sm q-mt-md">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="Frequency Threshold" type="number" step="0.01"
|
<q-input standout="custom-standout" label="Frequency Threshold" type="number" step="0.01"
|
||||||
:placeholder="suggested_binning_frequency_threshold" v-model.number="binning_frequency_threshold" hint="The minimum fraction of spectra a bin must contain a peak in to be kept (0.0 to 1.0)."></q-input>
|
:placeholder="suggested_binning_frequency_threshold" v-model="binning_frequency_threshold" hint="The minimum fraction of spectra a bin must contain a peak in to be kept (0.0 to 1.0)."></q-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="Min Peaks Per Bin" type="number"
|
<q-input standout="custom-standout" label="Min Peaks Per Bin" type="number"
|
||||||
:placeholder="suggested_binning_min_peak_per_bin" v-model.number="binning_min_peak_per_bin" hint="The minimum number of individual peaks required to form a bin in adaptive mode."></q-input>
|
:placeholder="suggested_binning_min_peak_per_bin" v-model="binning_min_peak_per_bin" hint="The minimum number of individual peaks required to form a bin in adaptive mode."></q-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row q-col-gutter-sm q-mt-md">
|
<div class="row q-col-gutter-sm q-mt-md">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="Max Bin Width (PPM)" type="number"
|
<q-input standout="custom-standout" label="Max Bin Width (PPM)" type="number"
|
||||||
:placeholder="suggested_binning_max_bin_width_ppm" v-model.number="binning_max_bin_width_ppm" hint="Maximum width of a bin in ppm for adaptive mode."></q-input>
|
:placeholder="suggested_binning_max_bin_width_ppm" v-model="binning_max_bin_width_ppm" hint="Maximum width of a bin in ppm for adaptive mode."></q-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<q-input standout="custom-standout" label="Number of Uniform Bins" type="number"
|
<q-input standout="custom-standout" label="Number of Uniform Bins" type="number"
|
||||||
:placeholder="suggested_binning_num_uniform_bins" v-model.number="binning_num_uniform_bins" hint="The number of bins to create for the uniform method."></q-input>
|
:placeholder="suggested_binning_num_uniform_bins" v-model="binning_num_uniform_bins" hint="The number of bins to create for the uniform method."></q-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<q-toggle v-model="binning_intensity_weighted_centers" label="Intensity Weighted Centers"
|
<q-toggle v-model="binning_intensity_weighted_centers" v-on:click="binning_intensity_weighted_centers" label="Intensity Weighted Centers"
|
||||||
class="q-mt-md" hint="If enabled, calculates bin centers as an intensity-weighted average of the peaks within it."></q-toggle>
|
class="q-mt-md" hint="If enabled, calculates bin centers as an intensity-weighted average of the peaks within it."></q-toggle>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
@ -410,8 +418,8 @@
|
|||||||
|
|
||||||
<!-- Pipeline Controls -->
|
<!-- Pipeline Controls -->
|
||||||
<div class="row justify-end items-center q-mt-md">
|
<div class="row justify-end items-center q-mt-md">
|
||||||
<q-btn class="q-ma-sm" icon="save" @click="export_params_btn=true" label="Export Params" outline />
|
<q-btn class="q-ma-sm" icon="save" v-on:click="export_params_btn=true" label="Export Params" outline />
|
||||||
<q-btn class="q-ma-sm" icon="upload_file" @click="import_params_btn=true" label="Import Params" outline />
|
<q-btn class="q-ma-sm" icon="upload_file" v-on:click="import_params_btn=true" label="Import Params" outline />
|
||||||
<q-btn :loading="progressPrep" class="q-ma-sm btn-style" icon="play_arrow"
|
<q-btn :loading="progressPrep" class="q-ma-sm btn-style" icon="play_arrow"
|
||||||
v-on:click="run_full_pipeline=true" padding="lg" label="Run Pipeline" />
|
v-on:click="run_full_pipeline=true" padding="lg" label="Run Pipeline" />
|
||||||
</div>
|
</div>
|
||||||
@ -422,8 +430,8 @@
|
|||||||
|
|
||||||
<!-- Pipeline Controls -->
|
<!-- Pipeline Controls -->
|
||||||
<div class="row justify-end items-center q-mt-md">
|
<div class="row justify-end items-center q-mt-md">
|
||||||
<q-btn class="q-ma-sm" icon="save" @click="export_params_btn=true" label="Export Params" outline />
|
<q-btn class="q-ma-sm" icon="save" v-on:click="export_params_btn=true" label="Export Params" outline />
|
||||||
<q-btn class="q-ma-sm" icon="upload_file" @click="import_params_btn=true" label="Import Params" outline />
|
<q-btn class="q-ma-sm" icon="upload_file" v-on:click="import_params_btn=true" label="Import Params" outline />
|
||||||
<q-btn :loading="progressPrep" class="q-ma-sm btn-style" icon="play_arrow"
|
<q-btn :loading="progressPrep" class="q-ma-sm btn-style" icon="play_arrow"
|
||||||
v-on:click="run_full_pipeline=true" padding="lg" label="Run Pipeline" />
|
v-on:click="run_full_pipeline=true" padding="lg" label="Run Pipeline" />
|
||||||
</div>
|
</div>
|
||||||
@ -525,6 +533,11 @@
|
|||||||
<q-item-label>Spectrum plot (X,Y)</q-item-label>
|
<q-item-label>Spectrum plot (X,Y)</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
|
<q-item clickable v-close-popup v-on:click="createNSpectrumPlot=true">
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label>Spectrum plot (ID)</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
</q-btn-dropdown>
|
</q-btn-dropdown>
|
||||||
</div>
|
</div>
|
||||||
@ -688,7 +701,7 @@
|
|||||||
<div id="image-container-normal" class="row st-col col-12">
|
<div id="image-container-normal" class="row st-col col-12">
|
||||||
<div class="col-10 q-pa-none q-ma-none">
|
<div class="col-10 q-pa-none q-ma-none">
|
||||||
<plotly id="plotImg" :data="plotdataImg" :layout="plotlayoutImg" class="q-pa-none q-ma-none sync_data"
|
<plotly id="plotImg" :data="plotdataImg" :layout="plotlayoutImg" class="q-pa-none q-ma-none sync_data"
|
||||||
@click="data_click"></plotly>
|
v-on:click="data_click"></plotly>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-2 q-pa-none q-ma-none">
|
<div class="col-2 q-pa-none q-ma-none">
|
||||||
<q-img id="colorbar-normal" class="q-ma-none q-pa-none" :src="colorbar"></q-img>
|
<q-img id="colorbar-normal" class="q-ma-none q-pa-none" :src="colorbar"></q-img>
|
||||||
@ -711,7 +724,7 @@
|
|||||||
<div id="image-container-triq" class="row st-col col-12">
|
<div id="image-container-triq" class="row st-col col-12">
|
||||||
<div class="col-10 q-pa-none q-ma-none ">
|
<div class="col-10 q-pa-none q-ma-none ">
|
||||||
<plotly id="plotImgT" :data="plotdataImgT" :layout="plotlayoutImgT"
|
<plotly id="plotImgT" :data="plotdataImgT" :layout="plotlayoutImgT"
|
||||||
class="q-pa-none q-ma-none sync_data" @click="data_click"></plotly>
|
class="q-pa-none q-ma-none sync_data" v-on:click="data_click"></plotly>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-2 q-pa-none q-ma-none ">
|
<div class="col-2 q-pa-none q-ma-none ">
|
||||||
<q-img id="colorbar-triq" class="q-ma-none q-pa-none" :src="colorbarT"></q-img>
|
<q-img id="colorbar-triq" class="q-ma-none q-pa-none" :src="colorbarT"></q-img>
|
||||||
@ -747,6 +760,11 @@
|
|||||||
<q-item-label>Spectrum plot (X,Y)</q-item-label>
|
<q-item-label>Spectrum plot (X,Y)</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
|
<q-item clickable v-close-popup v-on:click="createNSpectrumPlot=true">
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label>Spectrum plot (ID)</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
</q-btn-dropdown>
|
</q-btn-dropdown>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
665
app_snippet.html
665
app_snippet.html
@ -1,665 +0,0 @@
|
|||||||
<header id="header">
|
|
||||||
<img src="/css/LABI_logo.png" alt="Labi Logo Icon" id="imgLogo">
|
|
||||||
<div>
|
|
||||||
<h4>JuliaMSI </h4>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
<div v-if="is_initializing" class="loading-overlay">
|
|
||||||
<div class="loading-content">
|
|
||||||
<q-spinner-hourglass color="white" size="4em" />
|
|
||||||
<div class="q-mt-md text-white text-h6">{{ initialization_message }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<div id="extDivStyle" class="row col-12 q-pa-xl">
|
|
||||||
<div class="row col-6">
|
|
||||||
<!-- Left DIV -->
|
|
||||||
<div id="intDivStyle-left" class="st-col col-12 st-module">
|
|
||||||
<q-tabs v-model="left_tab" dense class="text-grey" indicator-color="primary" align="justify">
|
|
||||||
<q-tab name="pre_treatment" label="Pre-Treatment"></q-tab>
|
|
||||||
<q-tab name="generator" label="Slice Generator"></q-tab>
|
|
||||||
<q-tab name="converter" label="Converter"></q-tab>
|
|
||||||
</q-tabs>
|
|
||||||
<q-separator></q-separator>
|
|
||||||
<q-tab-panels v-model="left_tab" animated>
|
|
||||||
<q-tab-panel name="pre_treatment">
|
|
||||||
<div class="text-h6">imzML & mzML Data Pre-Treatment</div>
|
|
||||||
<div class="row items-center">
|
|
||||||
<q-input standout="custom-standout" class="q-ma-sm cursor-pointer col" v-model="full_route" readonly
|
|
||||||
:label="batch_file_count > 0 ? batch_file_count + ' file(s) in batch' : 'Select an imzMl / mzML file'"
|
|
||||||
v-on:click="btnSearch=true">
|
|
||||||
<template v-slot:append>
|
|
||||||
<q-icon name="search" v-on:click="btnSearch=true" class="cursor-pointer" />
|
|
||||||
</template>
|
|
||||||
</q-input>
|
|
||||||
<q-btn class="q-ma-sm" icon="add" v-on:click="btnAddBatch=true" label="Add"></q-btn>
|
|
||||||
<q-btn class="q-ma-sm" icon="clear" v-on:click="clear_batch_btn=true" :disable="batch_file_count === 0"
|
|
||||||
label="Clear"></q-btn>
|
|
||||||
</div>
|
|
||||||
<q-list bordered separator v-if="selected_files.length > 0">
|
|
||||||
<q-item v-for="(file, index) in selected_files" :key="index">
|
|
||||||
<q-item-section>
|
|
||||||
{{ file }}
|
|
||||||
</q-item-section>
|
|
||||||
<q-item-section side>
|
|
||||||
<q-btn flat round icon="delete" size="sm" v-on:click="selected_files.splice(index, 1)"></q-btn>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</q-list>
|
|
||||||
<!--<q-input standout="custom-standout" class="q-ma-sm cursor-pointer col" v-model="full_route_cal" readonly
|
|
||||||
label="Select a calibration imzMl / mzML file" v-on:click="btnSearchCal=true">
|
|
||||||
<template v-slot:append>
|
|
||||||
<q-icon name="search" v-on:click="btnSearchCal=true" class="cursor-pointer" />
|
|
||||||
</template>
|
|
||||||
</q-input>-->
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
<q-tabs v-model="pre_tab" dense class="text-grey" indicator-color="primary" align="justify">
|
|
||||||
<q-tab name="stabilization" label="Stabilization"></q-tab>
|
|
||||||
<q-tab name="smoothing" label="Smoothing"></q-tab>
|
|
||||||
<q-tab name="baseline" label="Baseline"></q-tab>
|
|
||||||
<q-tab name="normalization" label="Normalization"></q-tab>
|
|
||||||
<q-tab name="alignment" label="Alignment"></q-tab>
|
|
||||||
<q-tab name="standards" label="Internal Standards"></q-tab>
|
|
||||||
<q-tab name="calibration" label="Calibration"></q-tab>
|
|
||||||
<q-tab name="peak_picking" label="Peak Picking"></q-tab>
|
|
||||||
<q-tab name="peak_selection" label="Peak Selection"></q-tab>
|
|
||||||
<q-tab name="binning" label="Binning"></q-tab>
|
|
||||||
</q-tabs>
|
|
||||||
<div class="row justify-end items-center q-mt-md">
|
|
||||||
<q-btn class="q-ma-sm" icon="save" @click="export_params_btn=true" label="Export Params" outline />
|
|
||||||
<q-btn class="q-ma-sm" icon="upload_file" @click="import_params_btn=true" label="Import Params" outline />
|
|
||||||
<q-btn :loading="progressPrep" class="q-ma-sm btn-style" icon="play_arrow" v-on:click="run_full_pipeline=true"
|
|
||||||
padding="lg" label="Run Pipeline" />
|
|
||||||
</div>
|
|
||||||
<q-separator />
|
|
||||||
<q-tab-panels v-model="pre_tab" animated>
|
|
||||||
<q-tab-panel name="stabilization" class="q-pa-md">
|
|
||||||
<q-toggle v-model="enable_stabilization" label="Enable Stabilization" color="green" class="q-mb-md" hint="Enables variance-stabilizing transformation for intensities." />
|
|
||||||
<q-card class="q-mb-md">
|
|
||||||
<q-card-section>
|
|
||||||
<div class="text-h6">Method</div>
|
|
||||||
<div class="text-caption">Applies a variance-stabilizing transformation to the intensity vector.</div>
|
|
||||||
</q-card-section>
|
|
||||||
<q-card-section>
|
|
||||||
<q-radio v-model="stabilization_method" val="sqrt" label="SQRT" hint="Square root transformation." /><br>
|
|
||||||
<q-radio v-model="stabilization_method" val="log" label="LOG" hint="Natural log transformation." /><br>
|
|
||||||
<q-radio v-model="stabilization_method" val="log2" label="LOG 2" hint="Base-2 log transformation." /><br>
|
|
||||||
<q-radio v-model="stabilization_method" val="log10" label="LOG 10" hint="Base-10 log transformation." /><br>
|
|
||||||
<q-radio v-model="stabilization_method" val="log1p" label="LOG 1P" hint="Natural log of `1 + x`, useful for data with zeros." /><br>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</q-tab-panel>
|
|
||||||
<q-tab-panel name="smoothing">
|
|
||||||
<q-toggle v-model="enable_smoothing" label="Enable Smoothing" color="green" class="q-mb-md" hint="Enables spectral smoothing to reduce high-frequency noise." />
|
|
||||||
<q-card class="q-mb-md">
|
|
||||||
<q-card-section>
|
|
||||||
<div class="text-h6">Method</div>
|
|
||||||
<div class="text-caption">The smoothing algorithm.</div>
|
|
||||||
</q-card-section>
|
|
||||||
<q-card-section>
|
|
||||||
<q-radio v-model="smoothing_method" val="sg" label="Savitzky-Golay" hint="Savitzky-Golay filtering." /><br>
|
|
||||||
<q-radio v-model="smoothing_method" val="ma" label="Moving Average" hint="Moving Average filtering." /><br>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
|
||||||
<div class="text-h6">Parameters</div>
|
|
||||||
</q-card-section>
|
|
||||||
<q-card-section>
|
|
||||||
<q-input standout="custom-standout" label="Half Window Size" type="number"
|
|
||||||
:placeholder="suggested_smoothing_window" v-model.number="smoothing_window" hint="The half size of the smoothing window. For Savitzky-Golay, the full window (2*half_window + 1) must be an odd integer."></q-input>
|
|
||||||
<q-input standout="custom-standout" label="Order (for Savitzky-Golay)" type="number"
|
|
||||||
:placeholder="suggested_smoothing_order" v-model.number="smoothing_order" class="q-mt-md" hint="The polynomial order for the Savitzky-Golay filter. Must be less than the full window size."></q-input>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</q-tab-panel>
|
|
||||||
<q-tab-panel name="baseline">
|
|
||||||
<q-toggle v-model="enable_baseline" label="Enable Baseline Correction" color="green" class="q-mb-md" hint="Estimates and subtracts the background noise (baseline) from the spectral intensities." />
|
|
||||||
<q-card class="q-mb-md">
|
|
||||||
<q-card-section>
|
|
||||||
<div class="text-h6">Method</div>
|
|
||||||
<div class="text-caption">The algorithm to use for baseline correction.</div>
|
|
||||||
</q-card-section>
|
|
||||||
<q-card-section>
|
|
||||||
<q-radio v-model="baseline_method" val="snip" label="SNIP" hint="Sensitive Nonlinear Iterative Peak clipping." /><br>
|
|
||||||
<q-radio v-model="baseline_method" val="convex_hull" label="CONVEX HULL" hint="Finds the lower convex hull of the spectrum." /><br>
|
|
||||||
<q-radio v-model="baseline_method" val="median" label="MEDIAN" hint="Moving median filter." /><br>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
|
||||||
<div class="text-h6">Parameters</div>
|
|
||||||
</q-card-section>
|
|
||||||
<q-card-section>
|
|
||||||
<q-input standout="custom-standout" label="Iterations (for SNIP)" type="number"
|
|
||||||
:placeholder="suggested_baseline_iterations" v-model.number="baseline_iterations" hint="The number of iterations for the SNIP algorithm. A higher number results in a more aggressive baseline."></q-input>
|
|
||||||
<q-input standout="custom-standout" label="Window (for Median)" type="number"
|
|
||||||
:placeholder="suggested_baseline_window" v-model.number="baseline_window" class="q-mt-md" hint="The window size for the median method, determining the local region for median calculation."></q-input>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</q-tab-panel>
|
|
||||||
<q-tab-panel name="normalization">
|
|
||||||
<q-toggle v-model="enable_normalization" label="Enable Normalization" color="green" class="q-mb-md" hint="Corrects for variations in total ion current between different spectra, making them more comparable." />
|
|
||||||
<q-card class="q-mb-md">
|
|
||||||
<q-card-section>
|
|
||||||
<div class="text-h6">Method</div>
|
|
||||||
<div class="text-caption">The normalization method to apply.</div>
|
|
||||||
</q-card-section>
|
|
||||||
<q-card-section>
|
|
||||||
<q-radio v-model="normalization_method" val="tic" label="TIC" hint="Total Ion Current normalization (divides by the sum of intensities)." /><br>
|
|
||||||
<q-radio v-model="normalization_method" val="median" label="MEDIAN" hint="Divides by the median intensity." /><br>
|
|
||||||
<q-radio v-model="normalization_method" val="rms" label="RMS" hint="Root Mean Square normalization." /><br>
|
|
||||||
<q-radio v-model="normalization_method" val="none" label="NONE" hint="No normalization is applied." /><br>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</q-tab-panel>
|
|
||||||
<q-tab-panel name="alignment">
|
|
||||||
<q-toggle v-model="enable_alignment" label="Enable Peak Alignment" color="green" class="q-mb-md" hint="Corrects for m/z shifts between spectra, ensuring that the same analyte peak appears at the same m/z across all samples." />
|
|
||||||
<q-card class="q-mb-md">
|
|
||||||
<q-card-section>
|
|
||||||
<div class="text-h6">Method</div>
|
|
||||||
<div class="text-caption">The alignment algorithm.</div>
|
|
||||||
</q-card-section>
|
|
||||||
<q-card-section>
|
|
||||||
<q-radio v-model="alignment_method" val="lowess" label="LOWESS" hint="Locally Weighted Scatterplot Smoothing regression." /><br>
|
|
||||||
<q-radio v-model="alignment_method" val="linear" label="LINEAR" hint="Linear regression." /><br>
|
|
||||||
<q-radio v-model="alignment_method" val="ransac" label="RANSAC" hint="Random Sample Consensus algorithm for robust fitting." /><br>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
|
||||||
<div class="text-h6">Parameters</div>
|
|
||||||
</q-card-section>
|
|
||||||
<q-card-section>
|
|
||||||
<q-input standout="custom-standout" label="Span (for LOWESS)" type="number" step="0.01"
|
|
||||||
:placeholder="suggested_alignment_span" v-model.number="alignment_span" :rules="[val => val >= 0.0 && val <= 1.0 || 'Needs to be between 0 and 1']" hint="The span parameter for LOWESS regression, controlling smoothness (0.0 to 1.0)."></q-input>
|
|
||||||
<q-input standout="custom-standout" label="Tolerance" type="number" step="0.001"
|
|
||||||
:placeholder="suggested_alignment_tolerance" v-model.number="alignment_tolerance" class="q-mt-md" hint="The tolerance for matching peaks between the target and reference spectrum."></q-input>
|
|
||||||
<q-select standout="custom-standout" label="Tolerance Unit" v-model="alignment_tolerance_unit"
|
|
||||||
:options="['mz', 'ppm']" class="q-mt-md" hint="The unit for tolerance, either 'mz' (absolute) or 'ppm' (relative)."></q-select>
|
|
||||||
<q-input standout="custom-standout" label="Max Shift PPM" type="number"
|
|
||||||
:placeholder="suggested_alignment_max_shift_ppm" v-model.number="alignment_max_shift_ppm" class="q-mt-md" hint="The maximum allowed m/z shift in ppm to prevent spurious peak matches."></q-input>
|
|
||||||
<q-input standout="custom-standout" label="Min Matched Peaks" type="number"
|
|
||||||
:placeholder="suggested_alignment_min_matched_peaks" v-model.number="alignment_min_matched_peaks" class="q-mt-md" hint="The minimum number of matching peaks required to perform the alignment."></q-input>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</q-tab-panel>
|
|
||||||
<q-tab-panel name="standards">
|
|
||||||
<q-toggle v-model="enable_standards" label="Enable Internal Standards for Calibration" color="green" class="q-mb-md" hint="Enables the use of internal standards for mass calibration." />
|
|
||||||
<div class="text-h6">Define Internal Standards / Reference Peaks</div>
|
|
||||||
<p>Provide m/z values and optional labels for internal standards or reference peaks. These are used for mass calibration and alignment.</p>
|
|
||||||
<q-list bordered separator class="q-mt-md">
|
|
||||||
<q-item v-for="(peak, index) in reference_peaks_list" :key="index">
|
|
||||||
<q-item-section avatar>
|
|
||||||
<q-btn flat round icon="delete" color="negative" @click="removeReferencePeak(index)"></q-btn>
|
|
||||||
</q-item-section>
|
|
||||||
<q-item-section>
|
|
||||||
<div class="row q-col-gutter-sm">
|
|
||||||
<div class="col-6">
|
|
||||||
<q-input standout="custom-standout" label="m/z" type="number" step="0.0001"
|
|
||||||
v-model.number="peak.mz" :rules="[val => !!val || 'Required', val => val > 0 || 'Must be positive']" hint="Theoretical m/z value of the internal standard."></q-input>
|
|
||||||
</div>
|
|
||||||
<div class="col-6">
|
|
||||||
<q-input standout="custom-standout" label="Label (optional)" v-model="peak.label" hint="Optional label for the internal standard."></q-input>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
<q-item>
|
|
||||||
<q-item-section>
|
|
||||||
<q-btn class="q-ma-sm btn-style" icon="add" label="Add Reference Peak" @click="addReferencePeak"></q-btn>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</q-list>
|
|
||||||
</q-tab-panel>
|
|
||||||
<q-tab-panel name="calibration">
|
|
||||||
<q-toggle v-model="enable_calibration" label="Enable Mass Calibration" color="green" class="q-mb-md" hint="Enables mass calibration using internal standards." />
|
|
||||||
<p>This step uses the peaks defined in the 'Internal Standards' tab to correct the m/z axis.</p>
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
|
||||||
<div class="text-h6">Parameters</div>
|
|
||||||
</q-card-section>
|
|
||||||
<q-card-section>
|
|
||||||
<q-input standout="custom-standout" label="Fit Order" type="number"
|
|
||||||
:placeholder="suggested_calibration_fit_order" v-model.number="calibration_fit_order"
|
|
||||||
hint="Polynomial order for the calibration curve (e.g., 1 or 2)."></q-input>
|
|
||||||
<q-input standout="custom-standout" label="PPM Tolerance" type="number"
|
|
||||||
:placeholder="suggested_calibration_ppm_tolerance" v-model.number="calibration_ppm_tolerance" class="q-mt-md"
|
|
||||||
hint="PPM tolerance for matching reference peaks to internal standards."></q-input>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</q-tab-panel>
|
|
||||||
<q-tab-panel name="peak_picking">
|
|
||||||
<q-toggle v-model="enable_peak_picking" label="Enable Peak Picking" color="green" class="q-mb-md" hint="Identifies peaks (signals of interest) in the profile or centroided spectra." />
|
|
||||||
<p>Select the appropriate peak picking method and set its parameters.</p>
|
|
||||||
<q-card class="q-mb-md">
|
|
||||||
<q-card-section>
|
|
||||||
<div class="text-h6">Method</div>
|
|
||||||
<div class="text-caption">The peak detection algorithm.</div>
|
|
||||||
</q-card-section>
|
|
||||||
<q-card-section>
|
|
||||||
<q-radio v-model="peak_picking_method" val="profile" label="PROFILE" hint="For profile-mode data, using local maxima and quality filters." /><br>
|
|
||||||
<q-radio v-model="peak_picking_method" val="wavelet" label="WAVELET" hint="Continuous Wavelet Transform (CWT) based peak detection." /><br>
|
|
||||||
<q-radio v-model="peak_picking_method" val="centroid" label="CENTROID" hint="For centroid-mode data, essentially a filtering step." /><br>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
|
||||||
<div class="text-h6">Parameters</div>
|
|
||||||
</q-card-section>
|
|
||||||
<q-card-section>
|
|
||||||
<q-input standout="custom-standout" label="Signal to Noise Threshold" type="number" step="0.1"
|
|
||||||
:placeholder="suggested_peak_picking_snr_threshold" v-model.number="peak_picking_snr_threshold" hint="Signal-to-Noise Ratio threshold. Peaks with SNR below this value are discarded."></q-input>
|
|
||||||
<q-input standout="custom-standout" label="Half Window Size" type="number" class="q-mt-md"
|
|
||||||
:placeholder="suggested_peak_picking_half_window" v-model.number="peak_picking_half_window" hint="Number of data points to the left and right of a potential peak to consider for local maximum detection (for Profile method)."></q-input>
|
|
||||||
<q-input standout="custom-standout" label="Min Peak Prominence" type="number" step="0.01"
|
|
||||||
:placeholder="suggested_peak_picking_min_peak_prominence" v-model.number="peak_picking_min_peak_prominence" class="q-mt-md" hint="Minimum required prominence of a peak, expressed as a fraction of its height."></q-input>
|
|
||||||
<q-input standout="custom-standout" label="Merge Peaks Tolerance (m/z)" type="number" step="0.001"
|
|
||||||
:placeholder="suggested_peak_picking_merge_peaks_tolerance" v-model.number="peak_picking_merge_peaks_tolerance" class="q-mt-md" hint="The m/z tolerance within which to merge adjacent peaks, keeping the more intense one."></q-input>
|
|
||||||
<q-input standout="custom-standout" label="Min Peak Width (PPM)" type="number"
|
|
||||||
:placeholder="suggested_peak_picking_min_peak_width_ppm" v-model.number="peak_picking_min_peak_width_ppm" class="q-mt-md" hint="Minimum acceptable peak width (FWHM) in ppm."></q-input>
|
|
||||||
<q-input standout="custom-standout" label="Max Peak Width (PPM)" type="number"
|
|
||||||
:placeholder="suggested_peak_picking_max_peak_width_ppm" v-model.number="peak_picking_max_peak_width_ppm" class="q-mt-md" hint="Maximum acceptable peak width (FWHM) in ppm."></q-input>
|
|
||||||
<q-input standout="custom-standout" label="Min Peak Shape R2" type="number" step="0.01"
|
|
||||||
:placeholder="suggested_peak_picking_min_peak_shape_r2" v-model.number="peak_picking_min_peak_shape_r2" class="q-mt-md" hint="Minimum R-squared value from a Gaussian fit to the peak, used as a quality measure for peak shape."></q-input>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</q-tab-panel>
|
|
||||||
<q-tab-panel name="peak_selection">
|
|
||||||
<q-toggle v-model="enable_peak_selection" label="Enable Peak Selection" color="green" class="q-mb-md" hint="Filters detected peaks based on various quality criteria to remove noise and irrelevant signals." />
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
|
||||||
<div class="text-h6">Peak Quality Filters</div>
|
|
||||||
</q-card-section>
|
|
||||||
<q-card-section>
|
|
||||||
<q-input standout="custom-standout" label="Min SNR" type="number" step="0.1"
|
|
||||||
:placeholder="suggested_peak_selection_min_snr" v-model.number="peak_selection_min_snr" hint="Minimum Signal-to-Noise Ratio for a peak to be kept."></q-input>
|
|
||||||
<q-input standout="custom-standout" label="Min FWHM (PPM)" type="number"
|
|
||||||
:placeholder="suggested_peak_selection_min_fwhm_ppm" v-model.number="peak_selection_min_fwhm_ppm" class="q-mt-md" hint="Minimum Full Width at Half Maximum (FWHM) in ppm for a peak to be kept."></q-input>
|
|
||||||
<q-input standout="custom-standout" label="Max FWHM (PPM)" type="number"
|
|
||||||
:placeholder="suggested_peak_selection_max_fwhm_ppm" v-model.number="peak_selection_max_fwhm_ppm" class="q-mt-md" hint="Maximum Full Width at Half Maximum (FWHM) in ppm for a peak to be kept."></q-input>
|
|
||||||
<q-input standout="custom-standout" label="Min Peak Shape R2" type="number" step="0.01"
|
|
||||||
:placeholder="suggested_peak_selection_min_shape_r2" v-model.number="peak_selection_min_shape_r2" class="q-mt-md" hint="Minimum R-squared value from a Gaussian fit, filtering for good peak shape."></q-input>
|
|
||||||
<q-input standout="custom-standout" label="Frequency Threshold" type="number" step="0.01"
|
|
||||||
:placeholder="suggested_peak_selection_frequency_threshold" v-model.number="peak_selection_frequency_threshold" class="q-mt-md"
|
|
||||||
hint="The minimum fraction of spectra a peak must be present in to be kept (0.0 to 1.0)."></q-input>
|
|
||||||
<q-input standout="custom-standout" label="Correlation Threshold" type="number" step="0.01"
|
|
||||||
:placeholder="suggested_peak_selection_correlation_threshold" v-model.number="peak_selection_correlation_threshold" class="q-mt-md"
|
|
||||||
hint="Minimum correlation with neighboring peaks (not yet implemented)."></q-input>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</q-tab-panel>
|
|
||||||
<q-tab-panel name="binning">
|
|
||||||
<q-toggle v-model="enable_binning" label="Enable Peak Binning" color="green" class="q-mb-md" hint="Groups peaks from all spectra into common m/z bins to generate a feature matrix." />
|
|
||||||
<q-card class="q-mb-md">
|
|
||||||
<q-card-section>
|
|
||||||
<div class="text-h6">Method</div>
|
|
||||||
<div class="text-caption">The binning strategy.</div>
|
|
||||||
</q-card-section>
|
|
||||||
<q-card-section>
|
|
||||||
<q-radio v-model="binning_method" val="adaptive" label="ADAPTIVE" hint="Creates bins based on the density of detected peaks." /><br>
|
|
||||||
<q-radio v-model="binning_method" val="uniform" label="UNIFORM" hint="Creates a fixed number of equally spaced bins over the m/z range." /><br>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
|
||||||
<div class="text-h6">Parameters</div>
|
|
||||||
</q-card-section>
|
|
||||||
<q-card-section>
|
|
||||||
<q-input standout="custom-standout" label="Tolerance (for Adaptive)" type="number" step="0.001"
|
|
||||||
:placeholder="suggested_binning_tolerance" v-model.number="binning_tolerance" hint="Tolerance for grouping peaks into a bin in adaptive mode."></q-input>
|
|
||||||
<q-select standout="custom-standout" label="Tolerance Unit" v-model="binning_tolerance_unit"
|
|
||||||
:options="['mz', 'ppm']" class="q-mt-md" hint="The unit for tolerance, either 'mz' (absolute) or 'ppm' (relative)."></q-select>
|
|
||||||
<q-input standout="custom-standout" label="Frequency Threshold" type="number" step="0.01"
|
|
||||||
:placeholder="suggested_binning_frequency_threshold" v-model.number="binning_frequency_threshold" class="q-mt-md" hint="The minimum fraction of spectra a bin must contain a peak in to be kept (0.0 to 1.0)."></q-input>
|
|
||||||
<q-input standout="custom-standout" label="Min Peaks Per Bin" type="number"
|
|
||||||
:placeholder="suggested_binning_min_peak_per_bin" v-model.number="binning_min_peak_per_bin" class="q-mt-md" hint="The minimum number of individual peaks required to form a bin in adaptive mode."></q-input>
|
|
||||||
<q-input standout="custom-standout" label="Max Bin Width (PPM)" type="number"
|
|
||||||
:placeholder="suggested_binning_max_bin_width_ppm" v-model.number="binning_max_bin_width_ppm" class="q-mt-md" hint="Maximum width of a bin in ppm for adaptive mode."></q-input>
|
|
||||||
<q-toggle v-model="binning_intensity_weighted_centers" label="Intensity Weighted Centers"
|
|
||||||
class="q-mt-md" hint="If enabled, calculates bin centers as an intensity-weighted average of the peaks within it."></q-toggle>
|
|
||||||
<q-input standout="custom-standout" label="Number of Uniform Bins" type="number"
|
|
||||||
:placeholder="suggested_binning_num_uniform_bins" v-model.number="binning_num_uniform_bins" class="q-mt-md" hint="The number of bins to create for the uniform method."></q-input>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</q-tab-panel>
|
|
||||||
</q-tab-panels>
|
|
||||||
</q-tab-panel>
|
|
||||||
<q-tab-panel name="generator">
|
|
||||||
<div class="text-h6">imzML & mzML Data Processor</div>
|
|
||||||
<p>Please make sure the ibd and imzML file are located in the same directory and have the same name.
|
|
||||||
<br>It may take a while to generate the slice / spectrum, please be patient.
|
|
||||||
<br>To generate the contour or surface plots, you have to select the desired slice first using the
|
|
||||||
interface.
|
|
||||||
</p>
|
|
||||||
<div class="row items-center">
|
|
||||||
<q-input standout="custom-standout" class="q-ma-sm cursor-pointer col" v-model="full_route" readonly
|
|
||||||
:label="batch_file_count > 0 ? batch_file_count + ' file(s) in batch' : 'Select an imzMl / mzML file'"
|
|
||||||
v-on:click="btnSearch=true">
|
|
||||||
<template v-slot:append>
|
|
||||||
<q-icon name="search" v-on:click="btnSearch=true" class="cursor-pointer" />
|
|
||||||
</template>
|
|
||||||
</q-input>
|
|
||||||
<q-btn class="q-ma-sm" icon="add" v-on:click="btnAddBatch=true" label="Add"></q-btn>
|
|
||||||
<q-btn class="q-ma-sm" icon="clear" v-on:click="clear_batch_btn=true" :disable="batch_file_count === 0"
|
|
||||||
label="Clear"></q-btn>
|
|
||||||
</div>
|
|
||||||
<q-list bordered separator v-if="selected_files.length > 0">
|
|
||||||
<q-item v-for="(file, index) in selected_files" :key="index">
|
|
||||||
<q-item-section>
|
|
||||||
{{ file }}
|
|
||||||
</q-item-section>
|
|
||||||
<q-item-section side>
|
|
||||||
<q-btn flat round icon="delete" size="sm" v-on:click="selected_files.splice(index, 1)"></q-btn>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</q-list>
|
|
||||||
|
|
||||||
<!-- Variable Manipulation -->
|
|
||||||
<div class="row">
|
|
||||||
<div class="st-col col-4 col-sm q-ma-sm">
|
|
||||||
<q-input standout="custom-standout" id="textNmass" v-model="Nmass"
|
|
||||||
label="Mass-to-charge ratio(s) of interest" type="text" :rules="[
|
|
||||||
val => !!val || '* Required',
|
|
||||||
val => val.split(',').every(m => !isNaN(parseFloat(m.trim())) && parseFloat(m.trim()) > 0) || 'Need comma-separated positive numbers'
|
|
||||||
]">
|
|
||||||
</q-input>
|
|
||||||
</div>
|
|
||||||
<div class="st-col col-4 col-sm q-ma-sm">
|
|
||||||
<q-input standout="custom-standout" id="textTol" step="0.005" v-model="Tol"
|
|
||||||
label="Mass-to-charge ratio tolerance" type="number"
|
|
||||||
:rules="[val => !!val || '* Required', val => val >= 0.0 && val <= 1.0 || 'Needs to be in range between 0 and 1']"></q-input>
|
|
||||||
</div>
|
|
||||||
<div class="st-col col-4 col-sm q-ma-sm">
|
|
||||||
<q-input standout="custom-standout" id="textcolorLevel" step="1" v-model="colorLevel" label="Color levels"
|
|
||||||
type="number"
|
|
||||||
:rules="[ val => !!val || '* Required', val => val >= 2 && val <= 256 || 'Needs to be in range between 2 and 256']"></q-input>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<!-- Triq Variable Manipulation and filters-->
|
|
||||||
<div class="col-6">
|
|
||||||
<div class="st-col col-6 col-sm q-ma-sm">
|
|
||||||
<q-toggle id="btnEnableMFilter" v-on:click="MFilterEnabled" v-model="MFilterEnabled" color="green"
|
|
||||||
label="Add Median Filter"></q-toggle>
|
|
||||||
<q-toggle id="btnEnableTriq" v-on:click="triqEnabled" v-model="triqEnabled" color="blue"
|
|
||||||
label="Add Threshold Intensity Quantization (TrIQ)"></q-toggle>
|
|
||||||
<q-toggle id="btnEnableMask" v-on:click="maskEnabled" v-model="maskEnabled" color="black"
|
|
||||||
label="Use Mask To Filter Data"></q-toggle>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="st-col col-4 col-sm-4 q-ma-sm">
|
|
||||||
<q-input standout="custom-standout" id="textTriqProb" step="0.01" v-model="triqProb"
|
|
||||||
label="TrIQ probability" type="number" :rules="[
|
|
||||||
val => triqEnabled ? ( '* Required', val >= 0.8 && val <= 1 || 'Needs to be in range between 0.8 and 1') : true
|
|
||||||
]" :readonly="!triqEnabled" :disable="!triqEnabled"></q-input>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- Spectra Plot Manipulation -->
|
|
||||||
<div class="col-6">
|
|
||||||
<div class="st-col col-6 col-sm">
|
|
||||||
<q-btn-dropdown class="q-ma-sm btn-style" :loading="progressSpectraPlot" :disable="btnSpectraDisable"
|
|
||||||
label="Generate Spectra" icon="play_arrow">
|
|
||||||
<template v-slot:loading>
|
|
||||||
<q-spinner-hourglass class="on-left" />
|
|
||||||
Loading plot
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<q-list>
|
|
||||||
<q-item clickable v-close-popup v-on:click="createMeanPlot=true">
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label>Mean spectrum plot</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
<q-item clickable v-close-popup v-on:click="createSumPlot=true">
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label>Sum Spectrum plot</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
<q-item clickable v-close-popup v-on:click="createXYPlot=true">
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label>Spectrum plot (X,Y)</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</q-list>
|
|
||||||
</q-btn-dropdown>
|
|
||||||
</div>
|
|
||||||
<div class="row col-6">
|
|
||||||
<div class="st-col col-4 col-sm-4 q-ma-sm">
|
|
||||||
<q-input standout="custom-standout" step="1" v-model="xCoord" label="X coord" type="number" :rules="[
|
|
||||||
val => SpectraEnabled ? ( '* Required', val >= 0|| 'Needs to be bigger than 0') : true
|
|
||||||
]"></q-input>
|
|
||||||
</div>
|
|
||||||
<div class="st-col col-4 col-sm-4 q-ma-sm">
|
|
||||||
<q-input standout="custom-standout" step="1" v-model="yCoord" label="Y coord" type="number" :rules="[
|
|
||||||
val => SpectraEnabled ? ( '* Required', val <= 0|| 'Needs to be lower than 0') : true
|
|
||||||
]"></q-input>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<q-btn :loading="progress" class="q-ma-sm btn-style" :disabled="btnStartDisable" icon="play_arrow"
|
|
||||||
v-on:click="mainProcess=true" padding="lg" label="Generate Slice">
|
|
||||||
<template v-slot:loading>
|
|
||||||
<q-spinner-hourglass class="on-left" />
|
|
||||||
Loading...
|
|
||||||
</template>
|
|
||||||
</q-btn>
|
|
||||||
<q-btn icon="zoom_out_map" class="q-ma-sm on-right btn-style" v-on:click="compareBtn=true" padding="sm"
|
|
||||||
label="Compare"></q-btn>
|
|
||||||
<q-btn class="q-ma-sm btn-style" icon="edit" label="Mask Editor" href="/mask"></q-btn>
|
|
||||||
<q-btn class="q-ma-sm btn-style" icon="dashboard" v-on:click="showMetadataBtn=true"
|
|
||||||
label="Show Metadata"></q-btn>
|
|
||||||
<div class="q-pa-md row items-center" v-show="progress">
|
|
||||||
<q-spinner color="primary" size="2em" class="q-mr-sm"></q-spinner>
|
|
||||||
<div class="text-caption">{{ progress_message }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p>{{msg}}</p>
|
|
||||||
<div class="row st-col col-12">
|
|
||||||
<q-btn-dropdown class="q-ma-sm btn-style" :loading="progressPlot" :disable="btnPlotDisable"
|
|
||||||
label="Generate Plots" icon="play_arrow">
|
|
||||||
<template v-slot:loading>
|
|
||||||
<q-spinner-hourglass class="on-left" />
|
|
||||||
Loading plot
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<q-list>
|
|
||||||
<q-item clickable v-close-popup v-on:click="imageCPlot=true">
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label>Image topography Plot</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
|
|
||||||
<q-item clickable v-close-popup v-on:click="triqCPlot=true">
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label>TrIQ topography Plot</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
|
|
||||||
<q-item clickable v-close-popup v-on:click="image3dPlot=true">
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label>Image surface Plot</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
|
|
||||||
<q-item clickable v-close-popup v-on:click="triq3dPlot=true">
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label>TrIQ Surface Plot</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</q-list>
|
|
||||||
</q-btn-dropdown>
|
|
||||||
<q-btn-dropdown icon="search" class="q-ma-sm btn-style" :disable="btnOpticalDisable"
|
|
||||||
label="Load your optical image">
|
|
||||||
<q-item clickable v-close-popup v-on:click="btnOptical=true">
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label>Over normal image</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
<q-item clickable v-close-popup v-on:click="btnOpticalT=true">
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label>Over TrIQ image</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</q-btn-dropdown>
|
|
||||||
<div class="q-mx-sm">
|
|
||||||
<q-slider color="black" v-model="imgTrans" :min="0.0" :max="1" :step="0.1" :disable="btnOpticalDisable" />
|
|
||||||
<q-badge style="background-color: #009f90;"> Transparency: {{ imgTrans }}</q-badge>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</q-tab-panel>
|
|
||||||
<q-tab-panel name="converter">
|
|
||||||
<div class="text-h6">mzML to imzML Converter</div>
|
|
||||||
<p>Select the .mzML file and the corresponding .txt synchronization file to convert them into an .imzML/.ibd
|
|
||||||
pair.</p>
|
|
||||||
<q-input standout="custom-standout" class="q-ma-sm cursor-pointer" v-model="mzml_full_route" readonly
|
|
||||||
label="Select your .mzML file" v-on:click="btnSearchMzml=true">
|
|
||||||
<template v-slot:append>
|
|
||||||
<q-icon name="search" v-on:click="btnSearchMzml=true" class="cursor-pointer" />
|
|
||||||
</template>
|
|
||||||
</q-input>
|
|
||||||
|
|
||||||
<q-input standout="custom-standout" class="q-ma-sm cursor-pointer" v-model="sync_full_route" readonly
|
|
||||||
label="Select your .txt sync file" v-on:click="btnSearchSync=true">
|
|
||||||
<template v-slot:append>
|
|
||||||
<q-icon name="search" v-on:click="btnSearchSync=true" class="cursor-pointer" />
|
|
||||||
</template>
|
|
||||||
</q-input>
|
|
||||||
|
|
||||||
<q-btn :loading="progress_conversion" class="q-ma-sm btn-style" :disabled="btnConvertDisable"
|
|
||||||
icon="swap_horiz" v-on:click="convert_process=true" padding="lg" label="Convert File">
|
|
||||||
<template v-slot:loading>
|
|
||||||
<q-spinner-hourglass class="on-left" />
|
|
||||||
Converting...
|
|
||||||
</template>
|
|
||||||
</q-btn>
|
|
||||||
<p>{{msg_conversion}}</p>
|
|
||||||
</q-tab-panel>
|
|
||||||
</q-tab-panels>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row col-6">
|
|
||||||
<!-- Right DIV -->
|
|
||||||
<div id="intDivStyle-right" class="st-col col-12 col-sm st-module">
|
|
||||||
<div v-if="left_tab === 'pre_treatment'">
|
|
||||||
<div class="text-h6 q-mb-md">Spectrum View</div>
|
|
||||||
<q-card class="q-mb-md">
|
|
||||||
<q-card-section>
|
|
||||||
<div class="text-subtitle1">Before Preprocessing</div>
|
|
||||||
</q-card-section>
|
|
||||||
<q-card-section>
|
|
||||||
<plotly id="plotSpectraBefore" :data="plotdata_before" :layout="plotlayout_before" class="q-pa-none q-ma-none"></plotly>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
<q-card>
|
|
||||||
<q-card-section>
|
|
||||||
<div class="text-subtitle1">After Preprocessing</div>
|
|
||||||
</q-card-section>
|
|
||||||
<q-card-section>
|
|
||||||
<plotly id="plotSpectraAfter" :data="plotdata_after" :layout="plotlayout_after" class="q-pa-none q-ma-none">
|
|
||||||
</plotly>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</div>
|
|
||||||
<div v-else>
|
|
||||||
<st-tabs id="tabHeader-main" :ids="tabIDs" :labels="tabLabels" v-model="selectedTab" no-arrows></st-tabs>
|
|
||||||
<q-tab-panels v-model="selectedTab">
|
|
||||||
<q-tab-panel name="tab0">
|
|
||||||
<!-- Content for Tab 0 -->
|
|
||||||
<h6>Image visualizer</h6>
|
|
||||||
<div class="row items-center">
|
|
||||||
<q-select v-model="selected_folder_main" :options="image_available_folders" label="Select Dataset"
|
|
||||||
class="q-ma-sm" style="min-width: 200px;" v-on:focus="refetch_folders = true"></q-select>
|
|
||||||
<q-space></q-space>
|
|
||||||
<q-btn icon="arrow_back" class="q-my-sm btn-style" v-on:click="imgMinus=true"></q-btn>
|
|
||||||
<q-btn icon="arrow_forward" class="q-my-sm on-right btn-style" v-on:click="imgPlus=true"></q-btn>
|
|
||||||
</div>
|
|
||||||
<!-- Image manager -->
|
|
||||||
<div id="image-container-normal" class="row st-col col-12">
|
|
||||||
<div class="col-10 q-pa-none q-ma-none">
|
|
||||||
<plotly id="plotImg" :data="plotdataImg" :layout="plotlayoutImg" class="q-pa-none q-ma-none sync_data"
|
|
||||||
@click="data_click"></plotly>
|
|
||||||
</div>
|
|
||||||
<div class="col-2 q-pa-none q-ma-none">
|
|
||||||
<q-img id="colorbar-normal" class="q-ma-none q-pa-none" :src="colorbar"></q-img>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p v-html="msgimg"></p>
|
|
||||||
</q-tab-panel>
|
|
||||||
|
|
||||||
<q-tab-panel name="tab1">
|
|
||||||
<!-- Content for Tab 1 -->
|
|
||||||
<h6>TrIQ visualizer</h6>
|
|
||||||
<div class="row items-center">
|
|
||||||
<q-select v-model="selected_folder_main" :options="image_available_folders" label="Select Dataset"
|
|
||||||
class="q-ma-sm" style="min-width: 200px;" v-on:focus="refetch_folders = true"></q-select>
|
|
||||||
<q-space></q-space>
|
|
||||||
<q-btn icon="arrow_back" class="q-my-sm btn-style" v-on:click="imgMinusT=true"></q-btn>
|
|
||||||
<q-btn icon="arrow_forward" class="q-my-sm on-right btn-style" v-on:click="imgPlusT=true"></q-btn>
|
|
||||||
</div>
|
|
||||||
<!-- Triq Image manager -->
|
|
||||||
<div id="image-container-triq" class="row st-col col-12">
|
|
||||||
<div class="col-10 q-pa-none q-ma-none ">
|
|
||||||
<plotly id="plotImgT" :data="plotdataImgT" :layout="plotlayoutImgT"
|
|
||||||
class="q-pa-none q-ma-none sync_data" @click="data_click"></plotly>
|
|
||||||
</div>
|
|
||||||
<div class="col-2 q-pa-none q-ma-none ">
|
|
||||||
<q-img id="colorbar-triq" class="q-ma-none q-pa-none" :src="colorbarT"></q-img>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p v-html="msgtriq"></p>
|
|
||||||
</q-tab-panel>
|
|
||||||
|
|
||||||
<q-tab-panel name="tab2">
|
|
||||||
<div class="row items-center">
|
|
||||||
<q-select v-model="selected_folder_main" :options="available_folders" label="Select Dataset"
|
|
||||||
class="q-ma-sm" style="min-width: 200px;" v-on:focus="refetch_folders = true"></q-select>
|
|
||||||
<q-btn-dropdown class="q-ma-sm btn-style" :loading="progressSpectraPlot" :disable="btnSpectraDisable"
|
|
||||||
label="Generate Spectra" icon="play_arrow">
|
|
||||||
<template v-slot:loading>
|
|
||||||
<q-spinner-hourglass class="on-left" />
|
|
||||||
Loading plot
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<q-list>
|
|
||||||
<q-item clickable v-close-popup v-on:click="createMeanPlot=true">
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label>Mean spectrum plot</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
<q-item clickable v-close-popup v-on:click="createSumPlot=true">
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label>Sum Spectrum plot</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
<q-item clickable v-close-popup v-on:click="createXYPlot=true">
|
|
||||||
<q-item-section>
|
|
||||||
<q-item-label>Spectrum plot (X,Y)</q-item-label>
|
|
||||||
</q-item-section>
|
|
||||||
</q-item>
|
|
||||||
</q-list>
|
|
||||||
</q-btn-dropdown>
|
|
||||||
</div>
|
|
||||||
<plotly id="plotSpectra" :data="plotdata" :layout="plotlayout" class="q-pa-none q-ma-none"></plotly>
|
|
||||||
</q-tab-panel>
|
|
||||||
|
|
||||||
<q-tab-panel name="tab3">
|
|
||||||
<!-- Content for Tab 3 -->
|
|
||||||
<plotly id="plotTopo" :data="plotdataC" :layout="plotlayoutC" class="q-pa-none q-ma-none"></plotly>
|
|
||||||
</q-tab-panel>
|
|
||||||
<q-tab-panel name="tab4">
|
|
||||||
<!-- Content for Tab 4 -->
|
|
||||||
<plotly id="plot3d" :data="plotdata3d" :layout="plotlayout3d" class="q-pa-none q-ma-none"></plotly>
|
|
||||||
</q-tab-panel>
|
|
||||||
</q-tab-panels>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
1524
app_snippet.jl
1524
app_snippet.jl
File diff suppressed because it is too large
Load Diff
@ -531,7 +531,13 @@ function meanSpectrumPlot(data::MSIData, dataset_name::String=""; mask_path::Uni
|
|||||||
showgrid=true,
|
showgrid=true,
|
||||||
tickformat=".3g"
|
tickformat=".3g"
|
||||||
),
|
),
|
||||||
margin=attr(l=0, r=0, t=120, b=0, pad=0)
|
margin=attr(l=0, r=0, t=120, b=0, pad=0),
|
||||||
|
legend=attr(
|
||||||
|
x=1.0,
|
||||||
|
y=1.0,
|
||||||
|
xanchor="right",
|
||||||
|
yanchor="top"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Use the new, efficient function from the backend
|
# Use the new, efficient function from the backend
|
||||||
@ -582,22 +588,25 @@ Generates a plot for the spectrum at a specific coordinate (for imaging data) or
|
|||||||
"""
|
"""
|
||||||
function xySpectrumPlot(data::MSIData, xCoord::Int, yCoord::Int, imgWidth::Int, imgHeight::Int, dataset_name::String=""; mask_path::Union{String, Nothing}=nothing)
|
function xySpectrumPlot(data::MSIData, xCoord::Int, yCoord::Int, imgWidth::Int, imgHeight::Int, dataset_name::String=""; mask_path::Union{String, Nothing}=nothing)
|
||||||
local mz::AbstractVector, intensity::AbstractVector
|
local mz::AbstractVector, intensity::AbstractVector
|
||||||
local plot_title::String
|
local spectrum_id::Int = -1 # Initialize spectrum_id
|
||||||
local spectrum_mode = MSI_src.CENTROID # Default to centroid
|
mz, intensity = Float64[], Float64[]
|
||||||
|
base_title = ""
|
||||||
|
spectrum_mode = MSI_src.PROFILE
|
||||||
|
|
||||||
is_imaging = data.source isa ImzMLSource
|
if data.source isa ImzMLSource
|
||||||
|
w, h = data.image_dims
|
||||||
|
x = clamp(xCoord, 1, w)
|
||||||
|
y = clamp(yCoord, 1, h)
|
||||||
|
spectrum_id = (y - 1) * w + x # Calculate spectrum_id for imaging data
|
||||||
|
|
||||||
if is_imaging
|
# Check if spectrum stats are available and retrieve the mode
|
||||||
x = clamp(xCoord, 1, imgWidth)
|
|
||||||
y = clamp(yCoord, 1, imgHeight)
|
|
||||||
|
|
||||||
# Get spectrum mode
|
|
||||||
if data.spectrum_stats_df !== nothing && hasproperty(data.spectrum_stats_df, :Mode)
|
if data.spectrum_stats_df !== nothing && hasproperty(data.spectrum_stats_df, :Mode)
|
||||||
w, h = data.image_dims
|
if spectrum_id <= length(data.spectrum_stats_df.Mode)
|
||||||
if y > 0 && x > 0 && y <= h && x <= w
|
try
|
||||||
idx = (y - 1) * w + x
|
spectrum_mode = data.spectrum_stats_df.Mode[spectrum_id]
|
||||||
if idx <= length(data.spectrum_stats_df.Mode)
|
catch e
|
||||||
spectrum_mode = data.spectrum_stats_df.Mode[idx]
|
@warn "Could not retrieve spectrum mode for index $spectrum_id. Defaulting to PROFILE."
|
||||||
|
spectrum_mode = MSI_src.PROFILE
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -625,6 +634,7 @@ function xySpectrumPlot(data::MSIData, xCoord::Int, yCoord::Int, imgWidth::Int,
|
|||||||
else
|
else
|
||||||
# For non-imaging data, treat xCoord as the spectrum index
|
# For non-imaging data, treat xCoord as the spectrum index
|
||||||
index = clamp(xCoord, 1, length(data.spectra_metadata))
|
index = clamp(xCoord, 1, length(data.spectra_metadata))
|
||||||
|
spectrum_id = index # Assign index to spectrum_id for non-imaging data
|
||||||
|
|
||||||
if data.spectrum_stats_df !== nothing && hasproperty(data.spectrum_stats_df, :Mode)
|
if data.spectrum_stats_df !== nothing && hasproperty(data.spectrum_stats_df, :Mode)
|
||||||
if index <= length(data.spectrum_stats_df.Mode)
|
if index <= length(data.spectrum_stats_df.Mode)
|
||||||
@ -660,7 +670,13 @@ function xySpectrumPlot(data::MSIData, xCoord::Int, yCoord::Int, imgWidth::Int,
|
|||||||
showgrid=true,
|
showgrid=true,
|
||||||
tickformat=".3g"
|
tickformat=".3g"
|
||||||
),
|
),
|
||||||
margin=attr(l=0, r=0, t=120, b=0, pad=0)
|
margin=attr(l=0, r=0, t=120, b=0, pad=0),
|
||||||
|
legend=attr(
|
||||||
|
x=1.0,
|
||||||
|
y=1.0,
|
||||||
|
xanchor="right",
|
||||||
|
yanchor="top"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Downsample for plotting performance
|
# Downsample for plotting performance
|
||||||
@ -675,7 +691,98 @@ function xySpectrumPlot(data::MSIData, xCoord::Int, yCoord::Int, imgWidth::Int,
|
|||||||
plotdata = [trace]
|
plotdata = [trace]
|
||||||
plotlayout = layout
|
plotlayout = layout
|
||||||
|
|
||||||
return plotdata, plotlayout, mz, intensity
|
return plotdata, plotlayout, mz, intensity, spectrum_id
|
||||||
|
end
|
||||||
|
|
||||||
|
"""
|
||||||
|
nSpectrumPlot(data::MSIData, id::Int, dataset_name::String=""; mask_path::Union{String, Nothing}=nothing)
|
||||||
|
|
||||||
|
Generates a plot for a spectrum specified by its linear `id` for any type of MSIData.
|
||||||
|
|
||||||
|
# Arguments
|
||||||
|
- `data`: The `MSIData` object.
|
||||||
|
- `id`: The linear index (ID) of the spectrum to plot.
|
||||||
|
- `dataset_name`: Optional name of the dataset for the plot title.
|
||||||
|
- `mask_path`: Optional path to a mask file (currently not used for plotting by ID, but kept for signature consistency).
|
||||||
|
|
||||||
|
# Returns
|
||||||
|
- `plotdata`: A vector containing the Plotly trace.
|
||||||
|
- `plotlayout`: The Plotly layout for the plot.
|
||||||
|
- `mz`: The m/z values of the spectrum.
|
||||||
|
- `intensity`: The intensity values of the spectrum.
|
||||||
|
- `spectrum_id`: The linear index of the spectrum (same as input `id`).
|
||||||
|
"""
|
||||||
|
function nSpectrumPlot(data::MSIData, id::Int, dataset_name::String=""; mask_path::Union{String, Nothing}=nothing)
|
||||||
|
local mz::AbstractVector, intensity::AbstractVector
|
||||||
|
local plot_title::String
|
||||||
|
local spectrum_mode = MSI_src.CENTROID # Default to centroid
|
||||||
|
local spectrum_id::Int = id # Spectrum ID is the input id
|
||||||
|
|
||||||
|
# Validate ID
|
||||||
|
if id < 1 || id > length(data.spectra_metadata)
|
||||||
|
@warn "Spectrum ID $id is out of bounds."
|
||||||
|
mz, intensity = Float64[], Float64[]
|
||||||
|
base_title = "Spectrum ID $id (Out of Bounds)"
|
||||||
|
spectrum_id = 0 # Indicate invalid spectrum ID
|
||||||
|
else
|
||||||
|
# Get spectrum mode
|
||||||
|
if data.spectrum_stats_df !== nothing && hasproperty(data.spectrum_stats_df, :Mode)
|
||||||
|
if id <= length(data.spectrum_stats_df.Mode)
|
||||||
|
spectrum_mode = data.spectrum_stats_df.Mode[id]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Retrieve spectrum data
|
||||||
|
process_spectrum(data, id) do recieved_mz, recieved_intensity
|
||||||
|
mz = recieved_mz
|
||||||
|
intensity = recieved_intensity
|
||||||
|
end
|
||||||
|
base_title = "Spectrum #$id"
|
||||||
|
end
|
||||||
|
|
||||||
|
plot_title = isempty(dataset_name) ? base_title : "$base_title for: $dataset_name"
|
||||||
|
|
||||||
|
layout = PlotlyBase.Layout(
|
||||||
|
title=PlotlyBase.attr(
|
||||||
|
text=plot_title,
|
||||||
|
font=PlotlyBase.attr(
|
||||||
|
family="Roboto, Lato, sans-serif",
|
||||||
|
size=18,
|
||||||
|
color="black"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
hovermode="closest",
|
||||||
|
xaxis=PlotlyBase.attr(
|
||||||
|
title="<i>m/z</i>",
|
||||||
|
showgrid=true
|
||||||
|
),
|
||||||
|
yaxis=PlotlyBase.attr(
|
||||||
|
title="Intensity",
|
||||||
|
showgrid=true,
|
||||||
|
tickformat=".3g"
|
||||||
|
),
|
||||||
|
margin=attr(l=0, r=0, t=120, b=0, pad=0),
|
||||||
|
legend=attr(
|
||||||
|
x=1.0,
|
||||||
|
y=1.0,
|
||||||
|
xanchor="right",
|
||||||
|
yanchor="top"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Downsample for plotting performance
|
||||||
|
mz_down, int_down = MSI_src.downsample_spectrum(mz, intensity)
|
||||||
|
|
||||||
|
trace = if spectrum_mode == MSI_src.CENTROID
|
||||||
|
PlotlyBase.stem(x=mz_down, y=int_down, marker=attr(size=1, color="blue", opacity=0.5), name="Spectrum", hoverinfo="x", hovertemplate="<b>m/z</b>: %{x:.4f}<extra></extra>")
|
||||||
|
else
|
||||||
|
PlotlyBase.scatter(x=mz_down, y=int_down, mode="lines", marker=attr(size=1, color="blue", opacity=0.5), name="Spectrum", hoverinfo="x", hovertemplate="<b>m/z</b>: %{x:.4f}<extra></extra>")
|
||||||
|
end
|
||||||
|
|
||||||
|
plotdata = [trace]
|
||||||
|
plotlayout = layout
|
||||||
|
|
||||||
|
return plotdata, plotlayout, mz, intensity, spectrum_id
|
||||||
end
|
end
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@ -722,7 +829,13 @@ function sumSpectrumPlot(data::MSIData, dataset_name::String=""; mask_path::Unio
|
|||||||
showgrid=true,
|
showgrid=true,
|
||||||
tickformat=".3g"
|
tickformat=".3g"
|
||||||
),
|
),
|
||||||
margin=attr(l=0, r=0, t=120, b=0, pad=0)
|
margin=attr(l=0, r=0, t=120, b=0, pad=0),
|
||||||
|
legend=attr(
|
||||||
|
x=1.0,
|
||||||
|
y=1.0,
|
||||||
|
xanchor="right",
|
||||||
|
yanchor="top"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Use the get_total_spectrum function from the backend
|
# Use the get_total_spectrum function from the backend
|
||||||
|
|||||||
@ -389,10 +389,11 @@ function save_feature_matrix(feature_matrix::Matrix{Float64}, bin_info, output_d
|
|||||||
return csv_path, csv_path_standard
|
return csv_path, csv_path_standard
|
||||||
end
|
end
|
||||||
|
|
||||||
function execute_full_preprocessing(spectra::Vector{MutableSpectrum}, params::Dict,
|
function execute_full_preprocessing(progress_callback::Function, # Now first positional
|
||||||
|
spectra::Vector{MutableSpectrum}, params::Dict,
|
||||||
pipeline_steps::Vector{String}, reference_peaks::Dict,
|
pipeline_steps::Vector{String}, reference_peaks::Dict,
|
||||||
mask_path::Union{String, Nothing}=nothing;
|
mask_path::Union{String, Nothing} # Positional argument
|
||||||
progress_callback::Function=(step -> nothing))
|
)
|
||||||
|
|
||||||
println("Starting preprocessing pipeline with $(length(spectra)) spectra")
|
println("Starting preprocessing pipeline with $(length(spectra)) spectra")
|
||||||
println("Steps: $(join(pipeline_steps, " -> "))")
|
println("Steps: $(join(pipeline_steps, " -> "))")
|
||||||
|
|||||||
371
src/imzML.jl
371
src/imzML.jl
@ -229,53 +229,189 @@ function get_spectrum_attributes(stream::IO, hIbd::IO)
|
|||||||
return [x_skip, y_skip, mz_first, array_len_skip, spectrum_end_skip]
|
return [x_skip, y_skip, mz_first, array_len_skip, spectrum_end_skip]
|
||||||
end
|
end
|
||||||
|
|
||||||
function determine_parser(stream::IO, mz_is_compressed::Bool, int_is_compressed::Bool)
|
|
||||||
start_pos = position(stream)
|
|
||||||
spectrum_xml = ""
|
|
||||||
|
|
||||||
try
|
# Helper function to read an entire spectrum block from the stream
|
||||||
# Find the start of the first spectrum tag
|
function read_spectrum_block(stream::IO)
|
||||||
while !eof(stream)
|
lines = String[]
|
||||||
line = readline(stream)
|
in_spectrum = false
|
||||||
if occursin("<spectrum ", line)
|
|
||||||
spectrum_buffer = IOBuffer()
|
# Store the stream's position before starting to read this block.
|
||||||
write(spectrum_buffer, line)
|
# This is important for determining if a block was successfully read.
|
||||||
# Read until the end of the spectrum tag
|
start_pos = position(stream)
|
||||||
while !eof(stream)
|
|
||||||
line = readline(stream)
|
while !eof(stream)
|
||||||
write(spectrum_buffer, line)
|
line = readline(stream)
|
||||||
if occursin("</spectrum>", line)
|
|
||||||
break
|
if occursin("<spectrum ", line)
|
||||||
end
|
in_spectrum = true
|
||||||
end
|
push!(lines, line)
|
||||||
spectrum_xml = String(take!(spectrum_buffer))
|
elseif in_spectrum
|
||||||
break # Found the first spectrum, so we can stop
|
push!(lines, line)
|
||||||
|
if occursin("</spectrum>", line)
|
||||||
|
return join(lines, "\n")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
finally
|
|
||||||
seek(stream, start_pos) # Always reset stream position
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if isempty(spectrum_xml)
|
# If we reach EOF without finding </spectrum> for a started block,
|
||||||
# Fallback based on compression flags if no spectrum tag found
|
# or if no <spectrum> was found at all, return empty.
|
||||||
return (mz_is_compressed || int_is_compressed) ? :compressed : :uncompressed
|
if isempty(lines)
|
||||||
|
seek(stream, start_pos) # Reset stream position if no block was found
|
||||||
|
return ""
|
||||||
|
else
|
||||||
|
# Should not happen if XML is well-formed, but just in case
|
||||||
|
@warn "Reached EOF while parsing spectrum block without finding </spectrum> tag. Returning partial block."
|
||||||
|
return join(lines, "\n")
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Inspect the XML content
|
# Helper function to parse coordinates from a spectrum block
|
||||||
has_neofx_markers = occursin("encodedLength=\"0\"", spectrum_xml) &&
|
function parse_coordinates(spectrum_block::String)
|
||||||
occursin("external encoded length", spectrum_xml)
|
x = Int32(0)
|
||||||
|
y = Int32(0)
|
||||||
|
|
||||||
has_external_data_markers = occursin("IMS:1000101", spectrum_xml) &&
|
x_match = match(r"IMS:1000050[^>]*value=\"(\d+)\"", spectrum_block)
|
||||||
occursin("IMS:1000102", spectrum_xml) &&
|
y_match = match(r"IMS:1000051[^>]*value=\"(\d+)\"", spectrum_block)
|
||||||
occursin("IMS:1000103", spectrum_xml)
|
|
||||||
|
|
||||||
if has_neofx_markers
|
x = x_match !== nothing ? parse(Int32, x_match.captures[1]) : x
|
||||||
return :neofx
|
y = y_match !== nothing ? parse(Int32, y_match.captures[1]) : y
|
||||||
end
|
|
||||||
|
|
||||||
# If not neofx, it must be compressed (as uncompressed path is no longer supported)
|
return x, y
|
||||||
return :compressed
|
end
|
||||||
|
|
||||||
|
# Helper function to parse spectrum mode from a spectrum block
|
||||||
|
function parse_spectrum_mode(spectrum_block::String, global_mode::SpectrumMode)
|
||||||
|
if occursin("MS:1000127", spectrum_block)
|
||||||
|
return CENTROID
|
||||||
|
elseif occursin("MS:1000128", spectrum_block)
|
||||||
|
return PROFILE
|
||||||
|
else
|
||||||
|
return global_mode
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Helper function to parse binary data arrays
|
||||||
|
function parse_binary_data_arrays(spectrum_block::String, spectrum_start_line::String,
|
||||||
|
default_mz_format::DataType, default_intensity_format::DataType,
|
||||||
|
mz_is_compressed::Bool, int_is_compressed::Bool)
|
||||||
|
|
||||||
|
array_data_type = @NamedTuple{is_mz::Bool, array_length::Int32, encoded_length::Int64, offset::Int64}
|
||||||
|
current_array_data = array_data_type[]
|
||||||
|
|
||||||
|
for bda_match in eachmatch(r"<binaryDataArray.*?>(.*?)</binaryDataArray>"sm, spectrum_block)
|
||||||
|
bda_content = bda_match.match # This will be the full <binaryDataArray> block
|
||||||
|
|
||||||
|
is_mz = occursin("MS:1000514", bda_content) || occursin("mzArray", bda_content)
|
||||||
|
|
||||||
|
array_len_cv_match = match(r"IMS:1000103.*?value=\"(\d+)\"", bda_content)
|
||||||
|
array_length = Int32(0)
|
||||||
|
if array_len_cv_match !== nothing
|
||||||
|
array_length = parse(Int32, array_len_cv_match.captures[1])
|
||||||
|
end
|
||||||
|
|
||||||
|
# Fallback for defaultArrayLength from the spectrum_start_line
|
||||||
|
if array_length == 0
|
||||||
|
default_match = match(r"defaultArrayLength=\"(\d+)\"", spectrum_start_line)
|
||||||
|
if default_match !== nothing
|
||||||
|
array_length = parse(Int32, default_match.captures[1])
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
encoded_len_cv_match = match(r"IMS:1000104.*?value=\"(\d+)\"", bda_content)
|
||||||
|
encoded_length = Int64(0)
|
||||||
|
if encoded_len_cv_match !== nothing
|
||||||
|
encoded_length = parse(Int64, encoded_len_cv_match.captures[1])
|
||||||
|
else
|
||||||
|
encoded_len_attr_match = match(r"encodedLength=\"(\d+)\"", bda_content)
|
||||||
|
if encoded_len_attr_match !== nothing
|
||||||
|
encoded_length = parse(Int64, encoded_len_attr_match.captures[1])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
offset_match = match(r"IMS:1000102.*?value=\"(\d+)\"", bda_content)
|
||||||
|
offset = Int64(0)
|
||||||
|
if offset_match !== nothing
|
||||||
|
offset = parse(Int64, offset_match.captures[1])
|
||||||
|
end
|
||||||
|
|
||||||
|
if array_length > 0 && offset > 0
|
||||||
|
push!(current_array_data, (is_mz=is_mz, array_length=array_length,
|
||||||
|
encoded_length=encoded_length, offset=offset))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return current_array_data
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Unified function to parse spectrum metadata
|
||||||
|
function parse_imzml_spectrum_block(stream::IO, hIbd::Union{IO, ThreadSafeFileHandle}, param_groups::Dict{String, SpecDim},
|
||||||
|
width::Int32, height::Int32, num_spectra::Int32,
|
||||||
|
default_mz_format::DataType, default_intensity_format::DataType,
|
||||||
|
mz_is_compressed::Bool, int_is_compressed::Bool, global_mode::SpectrumMode)
|
||||||
|
|
||||||
|
spectra_metadata = Vector{SpectrumMetadata}(undef, num_spectra)
|
||||||
|
|
||||||
|
# Store the initial position of the stream, in case read_spectrum_block fails to find a spectrum.
|
||||||
|
# This helps reset the stream for subsequent debugging if needed, though typically it should find a spectrum.
|
||||||
|
initial_stream_pos = position(stream)
|
||||||
|
|
||||||
|
for k in 1:num_spectra
|
||||||
|
spectrum_block_content = read_spectrum_block(stream)
|
||||||
|
if isempty(spectrum_block_content)
|
||||||
|
@warn "Expected spectrum block $k but found none or reached EOF prematurely. Stopping parsing."
|
||||||
|
# Fill remaining spectra_metadata with placeholder or error.
|
||||||
|
for j in k:num_spectra
|
||||||
|
mz_asset = SpectrumAsset(default_mz_format, mz_is_compressed, Int64(0), 0, :mz)
|
||||||
|
int_asset = SpectrumAsset(default_intensity_format, int_is_compressed, Int64(0), 0, :intensity)
|
||||||
|
spectra_metadata[j] = SpectrumMetadata(Int32(0), Int32(0), "", :sample, global_mode, mz_asset, int_asset)
|
||||||
|
end
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
x, y = parse_coordinates(spectrum_block_content)
|
||||||
|
spectrum_mode = parse_spectrum_mode(spectrum_block_content, global_mode)
|
||||||
|
|
||||||
|
# The spectrum_start_line is needed for defaultArrayLength fallback in parse_binary_data_arrays
|
||||||
|
# We need to extract it from spectrum_block_content or pass the first line of the spectrum_block_content
|
||||||
|
# Let's assume the first line of spectrum_block_content is the spectrum_start_line for this purpose
|
||||||
|
spectrum_start_line_from_block = String(split(spectrum_block_content, '\n')[1])
|
||||||
|
|
||||||
|
current_array_data = parse_binary_data_arrays(spectrum_block_content, spectrum_start_line_from_block,
|
||||||
|
default_mz_format, default_intensity_format,
|
||||||
|
mz_is_compressed, int_is_compressed)
|
||||||
|
|
||||||
|
# Separate m/z and intensity arrays
|
||||||
|
mz_data = filter(d -> d.is_mz, current_array_data)
|
||||||
|
int_data = filter(d -> !d.is_mz, current_array_data)
|
||||||
|
|
||||||
|
if length(mz_data) != 1 || length(int_data) != 1
|
||||||
|
println("DEBUG: Spectrum $k is empty or invalid - creating placeholder metadata")
|
||||||
|
mz_asset = SpectrumAsset(default_mz_format, mz_is_compressed, Int64(0), 0, :mz)
|
||||||
|
int_asset = SpectrumAsset(default_intensity_format, int_is_compressed, Int64(0), 0, :intensity)
|
||||||
|
else
|
||||||
|
mz_info = mz_data[1]
|
||||||
|
int_info = int_data[1]
|
||||||
|
|
||||||
|
if k == 1 # Only print debug for the first spectrum
|
||||||
|
println("DEBUG First spectrum parsed (unified):")
|
||||||
|
println(" Coordinates: x=$x, y=$y")
|
||||||
|
println(" Mode: $spectrum_mode")
|
||||||
|
println(" m/z array: array_length=$(mz_info.array_length), encoded_length=$(mz_info.encoded_length), offset=$(mz_info.offset)")
|
||||||
|
println(" intensity array: array_length=$(int_info.array_length), encoded_length=$(int_info.encoded_length), offset=$(int_info.offset)")
|
||||||
|
end
|
||||||
|
|
||||||
|
mz_asset = SpectrumAsset(default_mz_format, mz_is_compressed, mz_info.offset,
|
||||||
|
mz_is_compressed ? mz_info.encoded_length : mz_info.array_length, :mz)
|
||||||
|
int_asset = SpectrumAsset(default_intensity_format, int_is_compressed, int_info.offset,
|
||||||
|
int_is_compressed ? int_info.encoded_length : int_info.array_length, :intensity)
|
||||||
|
end
|
||||||
|
|
||||||
|
spectra_metadata[k] = SpectrumMetadata(x, y, "", :sample, spectrum_mode, mz_asset, int_asset)
|
||||||
|
end
|
||||||
|
|
||||||
|
return spectra_metadata
|
||||||
|
end
|
||||||
|
|
||||||
function load_imzml_lazy(file_path::String; cache_size::Int=100)
|
function load_imzml_lazy(file_path::String; cache_size::Int=100)
|
||||||
println("DEBUG: Checking for .imzML file at $file_path")
|
println("DEBUG: Checking for .imzML file at $file_path")
|
||||||
if !isfile(file_path)
|
if !isfile(file_path)
|
||||||
@ -342,22 +478,9 @@ function load_imzml_lazy(file_path::String; cache_size::Int=100)
|
|||||||
println("DEBUG: m/z compressed: $mz_is_compressed, Intensity compressed: $int_is_compressed")
|
println("DEBUG: m/z compressed: $mz_is_compressed, Intensity compressed: $int_is_compressed")
|
||||||
println("DEBUG: Global mode: $global_mode")
|
println("DEBUG: Global mode: $global_mode")
|
||||||
|
|
||||||
# --- Parser Selection ---
|
local spectra_metadata = parse_imzml_spectrum_block(stream, ts_hIbd, param_groups, width, height, num_spectra,
|
||||||
parser_type = determine_parser(stream, mz_is_compressed, int_is_compressed)
|
default_mz_format, default_intensity_format,
|
||||||
println("DEBUG: Selected parser: $parser_type")
|
mz_is_compressed, int_is_compressed, global_mode)
|
||||||
|
|
||||||
local spectra_metadata
|
|
||||||
if parser_type == :neofx
|
|
||||||
println("DEBUG: Using neofx parser.")
|
|
||||||
spectra_metadata = parse_neofx(stream, ts_hIbd, param_groups, width, height, num_spectra,
|
|
||||||
default_mz_format, default_intensity_format,
|
|
||||||
mz_is_compressed, int_is_compressed, global_mode)
|
|
||||||
else
|
|
||||||
println("DEBUG: Using compressed parser.")
|
|
||||||
spectra_metadata = parse_compressed(stream, ts_hIbd, param_groups, width, height, num_spectra,
|
|
||||||
default_mz_format, default_intensity_format,
|
|
||||||
mz_is_compressed, int_is_compressed, global_mode)
|
|
||||||
end
|
|
||||||
|
|
||||||
println("DEBUG: Metadata parsing complete.")
|
println("DEBUG: Metadata parsing complete.")
|
||||||
|
|
||||||
@ -554,150 +677,6 @@ function parse_compressed(stream::IO, hIbd::Union{IO, ThreadSafeFileHandle}, par
|
|||||||
return spectra_metadata
|
return spectra_metadata
|
||||||
end
|
end
|
||||||
|
|
||||||
function parse_neofx(stream::IO, hIbd::Union{IO, ThreadSafeFileHandle}, param_groups::Dict{String, SpecDim},
|
|
||||||
width::Int32, height::Int32, num_spectra::Int32,
|
|
||||||
default_mz_format::DataType, default_intensity_format::DataType,
|
|
||||||
mz_is_compressed::Bool, int_is_compressed::Bool, global_mode::SpectrumMode)
|
|
||||||
spectra_metadata = Vector{SpectrumMetadata}(undef, num_spectra)
|
|
||||||
|
|
||||||
array_data_type = @NamedTuple{is_mz::Bool, array_length::Int32, encoded_length::Int64, offset::Int64}
|
|
||||||
|
|
||||||
for k in 1:num_spectra
|
|
||||||
# Initialize variables for this spectrum
|
|
||||||
x = Int32(0)
|
|
||||||
y = Int32(0)
|
|
||||||
spectrum_mode = global_mode
|
|
||||||
current_array_data = array_data_type[]
|
|
||||||
spectrum_start_line = ""
|
|
||||||
|
|
||||||
# Find the start of the spectrum tag
|
|
||||||
line = ""
|
|
||||||
while !eof(stream)
|
|
||||||
line = readline(stream)
|
|
||||||
if occursin("<spectrum ", line)
|
|
||||||
spectrum_start_line = line
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Parse lines within the spectrum block
|
|
||||||
while !eof(stream)
|
|
||||||
if !occursin("<spectrum ", line) # Avoid re-reading the first line
|
|
||||||
line = readline(stream)
|
|
||||||
end
|
|
||||||
|
|
||||||
if occursin("</spectrum>", line)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
# Parse coordinates
|
|
||||||
x_match = match(r"IMS:1000050.*?value=\"(\d+)\"", line)
|
|
||||||
if x_match !== nothing
|
|
||||||
x = parse(Int32, x_match.captures[1])
|
|
||||||
end
|
|
||||||
y_match = match(r"IMS:1000051.*?value=\"(\d+)\"", line)
|
|
||||||
if y_match !== nothing
|
|
||||||
y = parse(Int32, y_match.captures[1])
|
|
||||||
end
|
|
||||||
|
|
||||||
# Parse mode
|
|
||||||
if occursin("MS:1000127", line)
|
|
||||||
spectrum_mode = CENTROID
|
|
||||||
elseif occursin("MS:1000128", line)
|
|
||||||
spectrum_mode = PROFILE
|
|
||||||
end
|
|
||||||
|
|
||||||
# More robust binaryDataArray detection
|
|
||||||
if occursin("<binaryDataArray", line)
|
|
||||||
bda_lines = [line]
|
|
||||||
if !occursin("</binaryDataArray>", line)
|
|
||||||
while !eof(stream)
|
|
||||||
bda_line = readline(stream)
|
|
||||||
push!(bda_lines, bda_line)
|
|
||||||
if occursin("</binaryDataArray>", bda_line)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
bda_content = join(bda_lines, "\n")
|
|
||||||
|
|
||||||
# Parse from bda_content
|
|
||||||
is_mz = occursin("MS:1000514", bda_content) || occursin("mzArray", bda_content)
|
|
||||||
|
|
||||||
array_len_cv_match = match(r"IMS:1000103.*?value=\"(\d+)\"", bda_content)
|
|
||||||
array_length = Int32(0)
|
|
||||||
if array_len_cv_match !== nothing
|
|
||||||
array_length = parse(Int32, array_len_cv_match.captures[1])
|
|
||||||
end
|
|
||||||
|
|
||||||
# Fallback for defaultArrayLength
|
|
||||||
if array_length == 0
|
|
||||||
default_match = match(r"defaultArrayLength=\"(\d+)\"", spectrum_start_line)
|
|
||||||
if default_match !== nothing
|
|
||||||
array_length = parse(Int32, default_match.captures[1])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
encoded_len_cv_match = match(r"IMS:1000104.*?value=\"(\d+)\"", bda_content)
|
|
||||||
encoded_length = Int64(0)
|
|
||||||
if encoded_len_cv_match !== nothing
|
|
||||||
encoded_length = parse(Int64, encoded_len_cv_match.captures[1])
|
|
||||||
else
|
|
||||||
encoded_len_attr_match = match(r"encodedLength=\"(\d+)\"", bda_content)
|
|
||||||
if encoded_len_attr_match !== nothing
|
|
||||||
encoded_length = parse(Int64, encoded_len_attr_match.captures[1])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
offset_match = match(r"IMS:1000102.*?value=\"(\d+)\"", bda_content)
|
|
||||||
offset = Int64(0)
|
|
||||||
if offset_match !== nothing
|
|
||||||
offset = parse(Int64, offset_match.captures[1])
|
|
||||||
end
|
|
||||||
|
|
||||||
if array_length > 0 && offset > 0
|
|
||||||
push!(current_array_data, (is_mz=is_mz, array_length=array_length,
|
|
||||||
encoded_length=encoded_length, offset=offset))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Reset line to continue loop
|
|
||||||
line = ""
|
|
||||||
end
|
|
||||||
|
|
||||||
# Separate m/z and intensity arrays
|
|
||||||
mz_data = filter(d -> d.is_mz, current_array_data)
|
|
||||||
int_data = filter(d -> !d.is_mz, current_array_data)
|
|
||||||
|
|
||||||
if length(mz_data) != 1 || length(int_data) != 1
|
|
||||||
println("DEBUG: Spectrum $k is empty or invalid - creating placeholder metadata")
|
|
||||||
mz_asset = SpectrumAsset(default_mz_format, mz_is_compressed, Int64(0), 0, :mz)
|
|
||||||
int_asset = SpectrumAsset(default_intensity_format, int_is_compressed, Int64(0), 0, :intensity)
|
|
||||||
else
|
|
||||||
mz_info = mz_data[1]
|
|
||||||
int_info = int_data[1]
|
|
||||||
|
|
||||||
if k == 1
|
|
||||||
println("DEBUG First spectrum parsed:")
|
|
||||||
println(" Coordinates: x=$x, y=$y")
|
|
||||||
println(" Mode: $spectrum_mode")
|
|
||||||
println(" m/z array: array_length=$(mz_info.array_length), encoded_length=$(mz_info.encoded_length), offset=$(mz_info.offset)")
|
|
||||||
println(" intensity array: array_length=$(int_info.array_length), encoded_length=$(int_info.encoded_length), offset=$(int_info.offset)")
|
|
||||||
end
|
|
||||||
|
|
||||||
mz_asset = SpectrumAsset(default_mz_format, mz_is_compressed, mz_info.offset,
|
|
||||||
mz_is_compressed ? mz_info.encoded_length : mz_info.array_length, :mz)
|
|
||||||
int_asset = SpectrumAsset(default_intensity_format, int_is_compressed, int_info.offset,
|
|
||||||
int_is_compressed ? int_info.encoded_length : int_info.array_length, :intensity)
|
|
||||||
end
|
|
||||||
|
|
||||||
spectra_metadata[k] = SpectrumMetadata(x, y, "", :sample, spectrum_mode, mz_asset, int_asset)
|
|
||||||
end
|
|
||||||
|
|
||||||
return spectra_metadata
|
|
||||||
end
|
|
||||||
|
|
||||||
# --- End of content from imzML.jl ---
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
#
|
#
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user