altered imzML parser to be compatible with timsconverter, small improvements on the UI, other small bug fixes done
This commit is contained in:
parent
f35acd1d0f
commit
2fd2b90513
@ -22,7 +22,7 @@ https://codeberg.org/LabABI/JuliaMSI
|
|||||||
~/Downloads/JuliaMSI-main/juliamsi
|
~/Downloads/JuliaMSI-main/juliamsi
|
||||||
2. Without entering the Julia environment, launch the project in your terminal with the following command (which works for all operating systems):
|
2. Without entering the Julia environment, launch the project in your terminal with the following command (which works for all operating systems):
|
||||||
```
|
```
|
||||||
julia --project=. start_MSI_GUI.jl
|
julia --threads auto --project=. start_MSI_GUI.jl
|
||||||
```
|
```
|
||||||
3. After the script has finished loading, you can open a [page](http://127.0.0.1:1481/) in your browser with the web app running.
|
3. After the script has finished loading, you can open a [page](http://127.0.0.1:1481/) in your browser with the web app running.
|
||||||
|
|
||||||
|
|||||||
141
app.jl
141
app.jl
@ -159,6 +159,14 @@ include("./julia_imzML_visual.jl")
|
|||||||
## Plots
|
## Plots
|
||||||
# Local image to plot
|
# Local image to plot
|
||||||
layoutImg=PlotlyBase.Layout(
|
layoutImg=PlotlyBase.Layout(
|
||||||
|
title=PlotlyBase.attr(
|
||||||
|
text="",
|
||||||
|
font=PlotlyBase.attr(
|
||||||
|
family="Roboto, Lato, sans-serif",
|
||||||
|
size=14,
|
||||||
|
color="black"
|
||||||
|
)
|
||||||
|
),
|
||||||
xaxis=PlotlyBase.attr(
|
xaxis=PlotlyBase.attr(
|
||||||
visible=false,
|
visible=false,
|
||||||
scaleanchor="y",
|
scaleanchor="y",
|
||||||
@ -185,7 +193,14 @@ include("./julia_imzML_visual.jl")
|
|||||||
@out plotlayoutImgTComp=layoutImg
|
@out plotlayoutImgTComp=layoutImg
|
||||||
# Interface Plot Spectrum
|
# Interface Plot Spectrum
|
||||||
layoutSpectra=PlotlyBase.Layout(
|
layoutSpectra=PlotlyBase.Layout(
|
||||||
title="Spectrum plot",
|
title=PlotlyBase.attr(
|
||||||
|
text="Spectrum plot",
|
||||||
|
font=PlotlyBase.attr(
|
||||||
|
family="Roboto, Lato, sans-serif",
|
||||||
|
size=18,
|
||||||
|
color="black"
|
||||||
|
)
|
||||||
|
),
|
||||||
hovermode="closest",
|
hovermode="closest",
|
||||||
xaxis=PlotlyBase.attr(
|
xaxis=PlotlyBase.attr(
|
||||||
title="<i>m/z</i>",
|
title="<i>m/z</i>",
|
||||||
@ -215,7 +230,14 @@ include("./julia_imzML_visual.jl")
|
|||||||
|
|
||||||
# Interface Plot Surface
|
# Interface Plot Surface
|
||||||
layoutContour=PlotlyBase.Layout(
|
layoutContour=PlotlyBase.Layout(
|
||||||
title="2D Topographic map",
|
title=PlotlyBase.attr(
|
||||||
|
text="2D Topographic map",
|
||||||
|
font=PlotlyBase.attr(
|
||||||
|
family="Roboto, Lato, sans-serif",
|
||||||
|
size=18,
|
||||||
|
color="black"
|
||||||
|
)
|
||||||
|
),
|
||||||
xaxis=PlotlyBase.attr(
|
xaxis=PlotlyBase.attr(
|
||||||
visible=false,
|
visible=false,
|
||||||
scaleanchor="y"
|
scaleanchor="y"
|
||||||
@ -226,7 +248,7 @@ include("./julia_imzML_visual.jl")
|
|||||||
margin=attr(l=0,r=0,t=100,b=0,pad=0)
|
margin=attr(l=0,r=0,t=100,b=0,pad=0)
|
||||||
)
|
)
|
||||||
# Dummy 2D surface plot
|
# Dummy 2D surface plot
|
||||||
traceContour=PlotlyBase.scatter(x=Vector{Float64}(), y=Vector{Float64}(), mode="lines")
|
traceContour=PlotlyBase.contour(x=Vector{Float64}(), y=Vector{Float64}(), mode="lines")
|
||||||
# Create conection to frontend
|
# Create conection to frontend
|
||||||
@out plotdataC=[traceContour]
|
@out plotdataC=[traceContour]
|
||||||
@out plotlayoutC=layoutContour
|
@out plotlayoutC=layoutContour
|
||||||
@ -234,7 +256,14 @@ include("./julia_imzML_visual.jl")
|
|||||||
# Interface Plot 3d
|
# Interface Plot 3d
|
||||||
# Define the layout for the 3D plot
|
# Define the layout for the 3D plot
|
||||||
layout3D=PlotlyBase.Layout(
|
layout3D=PlotlyBase.Layout(
|
||||||
title="3D Surface plot",
|
title=PlotlyBase.attr(
|
||||||
|
text="3D Surface plot",
|
||||||
|
font=PlotlyBase.attr(
|
||||||
|
family="Roboto, Lato, sans-serif",
|
||||||
|
size=18,
|
||||||
|
color="black"
|
||||||
|
)
|
||||||
|
),
|
||||||
scene=attr(
|
scene=attr(
|
||||||
xaxis_title="X",
|
xaxis_title="X",
|
||||||
yaxis_title="Y",
|
yaxis_title="Y",
|
||||||
@ -386,6 +415,43 @@ include("./julia_imzML_visual.jl")
|
|||||||
# Use the new get_mz_slice with the centralized MSIData object
|
# Use the new get_mz_slice with the centralized MSIData object
|
||||||
println("get_mz_slice time:")
|
println("get_mz_slice time:")
|
||||||
slice = @time get_mz_slice(msi_data, Nmass, Tol)
|
slice = @time get_mz_slice(msi_data, Nmass, Tol)
|
||||||
|
|
||||||
|
# Failsafe check for empty slice
|
||||||
|
if all(iszero, slice)
|
||||||
|
msg = "No intensity data found for m/z = $Nmass with tolerance = $Tol. The resulting image is black. Please consider using a larger tolerance."
|
||||||
|
warning_msg = true
|
||||||
|
|
||||||
|
# Generate a black image but skip the colorbar that would crash
|
||||||
|
timestamp = string(time_ns())
|
||||||
|
text_nmass = replace(string(Nmass), "." => "_")
|
||||||
|
sliceQuant = zeros(UInt8, size(slice))
|
||||||
|
|
||||||
|
if triqEnabled
|
||||||
|
@time save_bitmap(joinpath("public", "TrIQ_$(text_nmass).bmp"), sliceQuant, ViridisPalette)
|
||||||
|
imgIntT = "/TrIQ_$(text_nmass).bmp?t=$(timestamp)"
|
||||||
|
plotdataImgT, plotlayoutImgT, imgWidth, imgHeight = loadImgPlot(imgIntT)
|
||||||
|
current_triq = "TrIQ_$(text_nmass).bmp"
|
||||||
|
msgtriq = "TrIQ image with the Nmass of $(replace(text_nmass, "_" => ".")) (No data)"
|
||||||
|
colorbarT = "" # Clear colorbar
|
||||||
|
current_col_triq = ""
|
||||||
|
triq_bmp = sort(filter(filename -> startswith(filename, "TrIQ_") && endswith(filename, ".bmp"), readdir("public")), lt=natural)
|
||||||
|
selectedTab = "tab1"
|
||||||
|
else
|
||||||
|
@time save_bitmap(joinpath("public", "MSI_$(text_nmass).bmp"), sliceQuant, ViridisPalette)
|
||||||
|
imgInt = "/MSI_$(text_nmass).bmp?t=$(timestamp)"
|
||||||
|
plotdataImg, plotlayoutImg, imgWidth, imgHeight = loadImgPlot(imgInt)
|
||||||
|
current_msi = "MSI_$(text_nmass).bmp"
|
||||||
|
msgimg = "Image with the Nmass of $(replace(text_nmass, "_" => ".")) (No data)"
|
||||||
|
colorbar = "" # Clear colorbar
|
||||||
|
current_col_msi = ""
|
||||||
|
msi_bmp = sort(filter(filename -> startswith(filename, "MSI_") && endswith(filename, ".bmp"), readdir("public")), lt=natural)
|
||||||
|
selectedTab = "tab0"
|
||||||
|
end
|
||||||
|
fTime = time()
|
||||||
|
eTime = round(fTime - sTime, digits=3)
|
||||||
|
# The warning message is already set above
|
||||||
|
else
|
||||||
|
# Original path for when data is found
|
||||||
fig = CairoMakie.Figure(size=(150, 250)) # Container
|
fig = CairoMakie.Figure(size=(150, 250)) # Container
|
||||||
timestamp = string(time_ns())
|
timestamp = string(time_ns())
|
||||||
|
|
||||||
@ -399,7 +465,7 @@ include("./julia_imzML_visual.jl")
|
|||||||
if MFilterEnabled
|
if MFilterEnabled
|
||||||
sliceTriq = round.(UInt8, median_filter(sliceTriq))
|
sliceTriq = round.(UInt8, median_filter(sliceTriq))
|
||||||
end
|
end
|
||||||
sliceTriq = reverse(sliceTriq, dims=2)
|
|
||||||
println("save_bitmap time:")
|
println("save_bitmap time:")
|
||||||
@time save_bitmap(joinpath("public", "TrIQ_$(text_nmass).bmp"), sliceTriq, ViridisPalette)
|
@time save_bitmap(joinpath("public", "TrIQ_$(text_nmass).bmp"), sliceTriq, ViridisPalette)
|
||||||
|
|
||||||
@ -428,7 +494,7 @@ include("./julia_imzML_visual.jl")
|
|||||||
if MFilterEnabled
|
if MFilterEnabled
|
||||||
sliceQuant = round.(UInt8, median_filter(sliceQuant))
|
sliceQuant = round.(UInt8, median_filter(sliceQuant))
|
||||||
end
|
end
|
||||||
sliceQuant = reverse(sliceQuant, dims=2)
|
|
||||||
println("save_bitmap time:")
|
println("save_bitmap time:")
|
||||||
@time save_bitmap(joinpath("public", "MSI_$(text_nmass).bmp"), sliceQuant, ViridisPalette)
|
@time save_bitmap(joinpath("public", "MSI_$(text_nmass).bmp"), sliceQuant, ViridisPalette)
|
||||||
|
|
||||||
@ -450,6 +516,7 @@ include("./julia_imzML_visual.jl")
|
|||||||
eTime = round(fTime - sTime, digits=3)
|
eTime = round(fTime - sTime, digits=3)
|
||||||
msg = "The image has been created in $(eTime) seconds successfully inside the 'public' folder of the app"
|
msg = "The image has been created in $(eTime) seconds successfully inside the 'public' folder of the app"
|
||||||
end
|
end
|
||||||
|
end
|
||||||
catch e
|
catch e
|
||||||
msg = "There was an error creating the image: $e"
|
msg = "There was an error creating the image: $e"
|
||||||
warning_msg = true
|
warning_msg = true
|
||||||
@ -624,6 +691,7 @@ include("./julia_imzML_visual.jl")
|
|||||||
else
|
else
|
||||||
traceImg=PlotlyBase.heatmap(x=Vector{Float64}(), y=Vector{Float64}())
|
traceImg=PlotlyBase.heatmap(x=Vector{Float64}(), y=Vector{Float64}())
|
||||||
plotdataImg=[traceImg]
|
plotdataImg=[traceImg]
|
||||||
|
plotlayoutImg=layoutImg
|
||||||
msgimg=""
|
msgimg=""
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -651,6 +719,7 @@ include("./julia_imzML_visual.jl")
|
|||||||
else
|
else
|
||||||
traceImg=PlotlyBase.heatmap(x=Vector{Float64}(), y=Vector{Float64}())
|
traceImg=PlotlyBase.heatmap(x=Vector{Float64}(), y=Vector{Float64}())
|
||||||
plotdataImg=[traceImg]
|
plotdataImg=[traceImg]
|
||||||
|
plotlayoutImg=layoutImg
|
||||||
msgimg=""
|
msgimg=""
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -679,6 +748,7 @@ include("./julia_imzML_visual.jl")
|
|||||||
else
|
else
|
||||||
traceImg=PlotlyBase.heatmap(x=Vector{Float64}(), y=Vector{Float64}())
|
traceImg=PlotlyBase.heatmap(x=Vector{Float64}(), y=Vector{Float64}())
|
||||||
plotdataImgT=[traceImg]
|
plotdataImgT=[traceImg]
|
||||||
|
plotlayoutImgT=layoutImg
|
||||||
msgtriq=""
|
msgtriq=""
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -706,6 +776,7 @@ include("./julia_imzML_visual.jl")
|
|||||||
else
|
else
|
||||||
traceImg=PlotlyBase.heatmap(x=Vector{Float64}(), y=Vector{Float64}())
|
traceImg=PlotlyBase.heatmap(x=Vector{Float64}(), y=Vector{Float64}())
|
||||||
plotdataImgT=[traceImg]
|
plotdataImgT=[traceImg]
|
||||||
|
plotlayoutImgT=layoutImg
|
||||||
msgtriq=""
|
msgtriq=""
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -735,6 +806,7 @@ include("./julia_imzML_visual.jl")
|
|||||||
else
|
else
|
||||||
traceImg=PlotlyBase.heatmap(x=Vector{Float64}(), y=Vector{Float64}())
|
traceImg=PlotlyBase.heatmap(x=Vector{Float64}(), y=Vector{Float64}())
|
||||||
plotdataImgComp=[traceImg]
|
plotdataImgComp=[traceImg]
|
||||||
|
plotlayoutImgComp=layoutImg
|
||||||
msgimgComp=""
|
msgimgComp=""
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -763,6 +835,7 @@ include("./julia_imzML_visual.jl")
|
|||||||
else
|
else
|
||||||
traceImg=PlotlyBase.heatmap(x=Vector{Float64}(), y=Vector{Float64}())
|
traceImg=PlotlyBase.heatmap(x=Vector{Float64}(), y=Vector{Float64}())
|
||||||
plotdataImgComp=[traceImg]
|
plotdataImgComp=[traceImg]
|
||||||
|
plotlayoutImgComp=layoutImg
|
||||||
msgimgComp=""
|
msgimgComp=""
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -791,6 +864,7 @@ include("./julia_imzML_visual.jl")
|
|||||||
else
|
else
|
||||||
traceImg=PlotlyBase.heatmap(x=Vector{Float64}(), y=Vector{Float64}())
|
traceImg=PlotlyBase.heatmap(x=Vector{Float64}(), y=Vector{Float64}())
|
||||||
plotdataImgTComp=[traceImg]
|
plotdataImgTComp=[traceImg]
|
||||||
|
plotlayoutImgTComp=layoutImg
|
||||||
msgtriqComp=""
|
msgtriqComp=""
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -819,6 +893,7 @@ include("./julia_imzML_visual.jl")
|
|||||||
else
|
else
|
||||||
traceImg=PlotlyBase.heatmap(x=Vector{Float64}(), y=Vector{Float64}())
|
traceImg=PlotlyBase.heatmap(x=Vector{Float64}(), y=Vector{Float64}())
|
||||||
plotdataImgTComp=[traceImg]
|
plotdataImgTComp=[traceImg]
|
||||||
|
plotlayoutImgTComp=layoutImg
|
||||||
msgtriqComp=""
|
msgtriqComp=""
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -1026,9 +1101,20 @@ include("./julia_imzML_visual.jl")
|
|||||||
@onchange data_click begin
|
@onchange data_click begin
|
||||||
if selectedTab == "tab1" || selectedTab == "tab0"
|
if selectedTab == "tab1" || selectedTab == "tab0"
|
||||||
# This is for the image heatmaps
|
# This is for the image heatmaps
|
||||||
cursor_data = data_click["cursor"]
|
cursor_data = get(data_click, "cursor", nothing)
|
||||||
x = Int32(round(cursor_data["x"]))
|
if cursor_data === nothing
|
||||||
y = Int32(round(cursor_data["y"])) # y is negative in the UI
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
x_val = get(cursor_data, "x", nothing)
|
||||||
|
y_val = get(cursor_data, "y", nothing)
|
||||||
|
|
||||||
|
if x_val === nothing || y_val === nothing
|
||||||
|
return # Do nothing if coordinates are not provided by the event
|
||||||
|
end
|
||||||
|
|
||||||
|
x = Int32(round(x_val))
|
||||||
|
y = Int32(round(y_val)) # y is negative in the UI
|
||||||
|
|
||||||
# Update the reactive coordinates, which will trigger the crosshair update
|
# Update the reactive coordinates, which will trigger the crosshair update
|
||||||
xCoord = clamp(x, 1, imgWidth)
|
xCoord = clamp(x, 1, imgWidth)
|
||||||
@ -1107,3 +1193,40 @@ end
|
|||||||
# Register a new route and the page that will be loaded on access
|
# Register a new route and the page that will be loaded on access
|
||||||
@page("/", "app.jl.html")
|
@page("/", "app.jl.html")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#=
|
||||||
|
function __init__()
|
||||||
|
@async begin
|
||||||
|
println("Pre-compiling functions at startup...")
|
||||||
|
|
||||||
|
# Create a dummy MSIData object to be used for pre-compilation
|
||||||
|
dummy_source = ImzMLSource("dummy.ibd", Float32, Float32)
|
||||||
|
dummy_meta = MSI_src.SpectrumMetadata(0,0,"",MSI_src.UNKNOWN, MSI_src.SpectrumAsset(Float32,false,0,0,:mz), MSI_src.SpectrumAsset(Float32,false,0,0,:intensity))
|
||||||
|
dummy_msi_data = MSIData(dummy_source, [dummy_meta], (1,1), zeros(Int,1,1), 0)
|
||||||
|
|
||||||
|
# Pre-compile functions from btnSearch
|
||||||
|
try OpenMSIData("dummy.imzML") catch end
|
||||||
|
try precompute_analytics(dummy_msi_data) catch end
|
||||||
|
|
||||||
|
# Pre-compile functions from mainProcess
|
||||||
|
try get_mz_slice(dummy_msi_data, 1.0, 1.0) catch end
|
||||||
|
try TrIQ(zeros(10,10), 256, 0.98) catch end
|
||||||
|
try quantize_intensity(zeros(10,10), 256) catch end
|
||||||
|
|
||||||
|
dummy_bmp_path = joinpath("public", "dummy.bmp")
|
||||||
|
dummy_png_path = joinpath("public", "dummy.png")
|
||||||
|
try
|
||||||
|
save_bitmap(dummy_bmp_path, zeros(UInt8, 10, 10), ViridisPalette)
|
||||||
|
loadImgPlot("/dummy.bmp")
|
||||||
|
generate_colorbar_image(zeros(10,10), 256, dummy_png_path)
|
||||||
|
catch e
|
||||||
|
@warn "Pre-compilation step failed (this is expected if dummy files can't be created/read)"
|
||||||
|
finally
|
||||||
|
rm(dummy_bmp_path, force=true)
|
||||||
|
rm(dummy_png_path, force=true)
|
||||||
|
end
|
||||||
|
|
||||||
|
println("Pre-compilation finished.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
=#
|
||||||
|
|||||||
105
app.jl.html
105
app.jl.html
@ -1,4 +1,3 @@
|
|||||||
<template>
|
|
||||||
<header id="header">
|
<header id="header">
|
||||||
<img src="/css/LABI_logo.png" alt="Labi Logo Icon" id="imgLogo">
|
<img src="/css/LABI_logo.png" alt="Labi Logo Icon" id="imgLogo">
|
||||||
<div>
|
<div>
|
||||||
@ -12,7 +11,7 @@
|
|||||||
<div id="extDivStyle" class="row col-12 q-pa-xl">
|
<div id="extDivStyle" class="row col-12 q-pa-xl">
|
||||||
<div class="row col-6">
|
<div class="row col-6">
|
||||||
<!-- Left DIV -->
|
<!-- Left DIV -->
|
||||||
<div id="intDivStyle" class="st-col col-12 st-module">
|
<div id="intDivStyle-left" class="st-col col-12 st-module">
|
||||||
<h6>Search for the imzML or mzML file in your system</h6>
|
<h6>Search for the imzML or mzML file in your system</h6>
|
||||||
<q-input standout="custom-standout" class="q-ma-sm cursor-pointer" v-model="full_route" readonly label="Select your imzML or mzML file" v-on:click="btnSearch=true">
|
<q-input standout="custom-standout" class="q-ma-sm cursor-pointer" v-model="full_route" readonly label="Select your imzML or mzML file" v-on:click="btnSearch=true">
|
||||||
<template v-slot:append>
|
<template v-slot:append>
|
||||||
@ -58,7 +57,7 @@
|
|||||||
<!-- Spectra Plot Manipulation -->
|
<!-- Spectra Plot Manipulation -->
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<div class="st-col col-6 col-sm">
|
<div class="st-col col-6 col-sm">
|
||||||
<q-btn-dropdown id="btnStyle" class="q-ma-sm" :loading="progressSpectraPlot" :disable="btnSpectraDisable"
|
<q-btn-dropdown class="q-ma-sm btn-style" :loading="progressSpectraPlot" :disable="btnSpectraDisable"
|
||||||
label="Generate Spectra" icon="play_arrow">
|
label="Generate Spectra" icon="play_arrow">
|
||||||
<template v-slot:loading>
|
<template v-slot:loading>
|
||||||
<q-spinner-hourglass class="on-left" />
|
<q-spinner-hourglass class="on-left" />
|
||||||
@ -99,22 +98,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<q-btn id="btnStyle" :loading="progress" class="q-ma-sm" :disabled="btnStartDisable" icon="play_arrow"
|
<q-btn :loading="progress" class="q-ma-sm btn-style" :disabled="btnStartDisable" icon="play_arrow"
|
||||||
v-on:click="mainProcess=true" padding="lg" label="Main process">
|
v-on:click="mainProcess=true" padding="lg" label="Generate Spectra">
|
||||||
<template v-slot:loading>
|
<template v-slot:loading>
|
||||||
<q-spinner-hourglass class="on-left" />
|
<q-spinner-hourglass class="on-left" />
|
||||||
Loading...
|
Loading...
|
||||||
</template>
|
</template>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
<q-btn id="btnStyle" icon="zoom_out_map" class="q-ma-sm on-right" v-on:click="compareBtn=true" padding="sm"
|
<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>
|
label="Compare"></q-btn>
|
||||||
<q-btn id="btnStyle" class="q-ma-sm" :disable="btnMetadataDisable"
|
<q-btn class="q-ma-sm btn-style" :disable="btnMetadataDisable"
|
||||||
v-on:click="showMetadataBtn=true" label="Show Metadata"></q-btn>
|
v-on:click="showMetadataBtn=true" label="Show Metadata"></q-btn>
|
||||||
</div>
|
</div>
|
||||||
<p>{{msg}}</p>
|
<p>{{msg}}</p>
|
||||||
|
|
||||||
<div class="row st-col col-12">
|
<div class="row st-col col-12">
|
||||||
<q-btn-dropdown id="btnStyle" class="q-ma-sm" :loading="progressPlot" :disable="btnPlotDisable"
|
<q-btn-dropdown class="q-ma-sm btn-style" :loading="progressPlot" :disable="btnPlotDisable"
|
||||||
label="Generate Plots" icon="play_arrow">
|
label="Generate Plots" icon="play_arrow">
|
||||||
<template v-slot:loading>
|
<template v-slot:loading>
|
||||||
<q-spinner-hourglass class="on-left" />
|
<q-spinner-hourglass class="on-left" />
|
||||||
@ -147,7 +146,7 @@
|
|||||||
</q-item>
|
</q-item>
|
||||||
</q-list>
|
</q-list>
|
||||||
</q-btn-dropdown>
|
</q-btn-dropdown>
|
||||||
<q-btn-dropdown id="btnStyle" icon="search" class="q-ma-sm" :disable="btnOpticalDisable"
|
<q-btn-dropdown icon="search" class="q-ma-sm btn-style" :disable="btnOpticalDisable"
|
||||||
label="Load your optical image">
|
label="Load your optical image">
|
||||||
<q-item clickable v-close-popup v-on:click="btnOptical=true">
|
<q-item clickable v-close-popup v-on:click="btnOptical=true">
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
@ -169,25 +168,25 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row col-6">
|
<div class="row col-6">
|
||||||
<!-- Right DIV -->
|
<!-- Right DIV -->
|
||||||
<div id="intDivStyle" class="st-col col-12 col-sm st-module">
|
<div id="intDivStyle-right" class="st-col col-12 col-sm st-module">
|
||||||
<st-tabs id="tabHeader" :ids="tabIDs" :labels="tabLabels" v-model="selectedTab" no-arrows></st-tabs>
|
<st-tabs id="tabHeader-main" :ids="tabIDs" :labels="tabLabels" v-model="selectedTab" no-arrows></st-tabs>
|
||||||
<q-tab-panels v-model="selectedTab">
|
<q-tab-panels v-model="selectedTab">
|
||||||
<q-tab-panel name="tab0">
|
<q-tab-panel name="tab0">
|
||||||
<!-- Content for Tab 0 -->
|
<!-- Content for Tab 0 -->
|
||||||
<!-- Btn image changer -->
|
<!-- Btn image changer -->
|
||||||
<h6>Image visualizer</h6>
|
<h6>Image visualizer</h6>
|
||||||
<div>
|
<div>
|
||||||
<q-btn id="btnStyle" icon="arrow_back" class="q-my-sm" v-on:click="imgMinus=true"></q-btn>
|
<q-btn icon="arrow_back" class="q-my-sm btn-style" v-on:click="imgMinus=true"></q-btn>
|
||||||
<q-btn id="btnStyle" icon="arrow_forward" class="q-my-sm on-right" v-on:click="imgPlus=true"></q-btn>
|
<q-btn icon="arrow_forward" class="q-my-sm on-right btn-style" v-on:click="imgPlus=true"></q-btn>
|
||||||
</div>
|
</div>
|
||||||
<!-- Image manager -->
|
<!-- Image manager -->
|
||||||
<div id="image-container" 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="plotStyle" :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>
|
@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" 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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>{{msgimg}}</p>
|
<p>{{msgimg}}</p>
|
||||||
@ -198,17 +197,17 @@
|
|||||||
<!-- Triq Btn image changer -->
|
<!-- Triq Btn image changer -->
|
||||||
<h6>TrIQ visualizer</h6>
|
<h6>TrIQ visualizer</h6>
|
||||||
<div>
|
<div>
|
||||||
<q-btn id="btnStyle" icon="arrow_back" class="q-my-sm" v-on:click="imgMinusT=true"></q-btn>
|
<q-btn icon="arrow_back" class="q-my-sm btn-style" v-on:click="imgMinusT=true"></q-btn>
|
||||||
<q-btn id="btnStyle" icon="arrow_forward" class="q-my-sm on-right" v-on:click="imgPlusT=true"></q-btn>
|
<q-btn icon="arrow_forward" class="q-my-sm on-right btn-style" v-on:click="imgPlusT=true"></q-btn>
|
||||||
</div>
|
</div>
|
||||||
<!-- Triq Image manager -->
|
<!-- Triq Image manager -->
|
||||||
<div id="image-container" 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="plotStyle" :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" @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" 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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>{{msgtriq}}</p>
|
<p>{{msgtriq}}</p>
|
||||||
@ -216,16 +215,16 @@
|
|||||||
|
|
||||||
<q-tab-panel name="tab2">
|
<q-tab-panel name="tab2">
|
||||||
<!-- Content for Tab 2 -->
|
<!-- Content for Tab 2 -->
|
||||||
<plotly id="plotStyle" :data="plotdata" :layout="plotlayout" class="q-pa-none q-ma-none"></plotly>
|
<plotly id="plotSpectra" :data="plotdata" :layout="plotlayout" class="q-pa-none q-ma-none"></plotly>
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
|
|
||||||
<q-tab-panel name="tab3">
|
<q-tab-panel name="tab3">
|
||||||
<!-- Content for Tab 3 -->
|
<!-- Content for Tab 3 -->
|
||||||
<plotly id="plotStyle" :data="plotdataC" :layout="plotlayoutC" class="q-pa-none q-ma-none"></plotly>
|
<plotly id="plotTopo" :data="plotdataC" :layout="plotlayoutC" class="q-pa-none q-ma-none"></plotly>
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
<q-tab-panel name="tab4">
|
<q-tab-panel name="tab4">
|
||||||
<!-- Content for Tab 4 -->
|
<!-- Content for Tab 4 -->
|
||||||
<plotly id="plotStyle3d" :data="plotdata3d" :layout="plotlayout3d" class="q-pa-none q-ma-none"></plotly>
|
<plotly id="plot3d" :data="plotdata3d" :layout="plotlayout3d" class="q-pa-none q-ma-none"></plotly>
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
</q-tab-panels>
|
</q-tab-panels>
|
||||||
</div>
|
</div>
|
||||||
@ -263,23 +262,23 @@
|
|||||||
<q-card-section class="q-pt-none col-12">
|
<q-card-section class="q-pt-none col-12">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<st-tabs id="tabHeader" :ids="tabIDs" :labels="tabLabels" v-model="selectedTab"></st-tabs>
|
<st-tabs id="tabHeaderCompareLeft" :ids="tabIDs" :labels="tabLabels" v-model="selectedTab"></st-tabs>
|
||||||
<q-tab-panels v-model="selectedTab">
|
<q-tab-panels v-model="selectedTab">
|
||||||
<q-tab-panel name="tab0">
|
<q-tab-panel name="tab0">
|
||||||
<!-- Content for Tab 0 -->
|
<!-- Content for Tab 0 -->
|
||||||
<!-- Btn image changer -->
|
<!-- Btn image changer -->
|
||||||
<div>
|
<div>
|
||||||
<q-btn id="btnStyle" icon="arrow_back" class="q-my-sm" v-on:click="imgMinus=true"></q-btn>
|
<q-btn icon="arrow_back" class="q-my-sm btn-style" v-on:click="imgMinus=true"></q-btn>
|
||||||
<q-btn id="btnStyle" icon="arrow_forward" class="q-my-sm on-right" v-on:click="imgPlus=true"></q-btn>
|
<q-btn icon="arrow_forward" class="q-my-sm on-right btn-style" v-on:click="imgPlus=true"></q-btn>
|
||||||
</div>
|
</div>
|
||||||
<!-- Image manager -->
|
<!-- Image manager -->
|
||||||
<div id="image-container" class="row st-col col-12">
|
<div id="image-container-compare-left-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="plotStyle" :data="plotdataImg" :layout="plotlayoutImg" class="q-pa-none q-ma-none">
|
<plotly id="plotImgCompareLeft" :data="plotdataImg" :layout="plotlayoutImg" class="q-pa-none q-ma-none">
|
||||||
</plotly>
|
</plotly>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-2">
|
<div class="col-2">
|
||||||
<q-img id="colorbar" class="q-ma-none q-pa-none" :src="colorbar"></q-img>
|
<q-img id="colorbarCompareLeft" class="q-ma-none q-pa-none" :src="colorbar"></q-img>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>{{msgimg}}</p>
|
<p>{{msgimg}}</p>
|
||||||
@ -289,56 +288,56 @@
|
|||||||
<!-- Content for Tab 1 -->
|
<!-- Content for Tab 1 -->
|
||||||
<!-- Triq Btn image changer -->
|
<!-- Triq Btn image changer -->
|
||||||
<div>
|
<div>
|
||||||
<q-btn id="btnStyle" icon="arrow_back" class="q-my-sm" v-on:click="imgMinusT=true"></q-btn>
|
<q-btn icon="arrow_back" class="q-my-sm btn-style" v-on:click="imgMinusT=true"></q-btn>
|
||||||
<q-btn id="btnStyle" icon="arrow_forward" class="q-my-sm on-right" v-on:click="imgPlusT=true"></q-btn>
|
<q-btn icon="arrow_forward" class="q-my-sm on-right btn-style" v-on:click="imgPlusT=true"></q-btn>
|
||||||
</div>
|
</div>
|
||||||
<!-- Triq Image manager -->
|
<!-- Triq Image manager -->
|
||||||
<div id="image-container" class="row st-col col-12">
|
<div id="image-container-compare-left-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="plotStyle" :data="plotdataImgT" :layout="plotlayoutImgT" class="q-pa-none q-ma-none">
|
<plotly id="plotImgTCompareLeft" :data="plotdataImgT" :layout="plotlayoutImgT" class="q-pa-none q-ma-none">
|
||||||
</plotly>
|
</plotly>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-2">
|
<div class="col-2">
|
||||||
<q-img id="colorbar" class="q-ma-none q-pa-none" :src="colorbarT"></q-img>
|
<q-img id="colorbarTCompareLeft" class="q-ma-none q-pa-none" :src="colorbarT"></q-img>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>{{msgtriq}}</p>
|
<p>{{msgtriq}}</p>
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
<q-tab-panel name="tab2">
|
<q-tab-panel name="tab2">
|
||||||
<!-- Content for Tab 2 -->
|
<!-- Content for Tab 2 -->
|
||||||
<plotly id="plotStyle" :data="plotdata" :layout="plotlayout" class="q-pa-none q-ma-none"></plotly>
|
<plotly id="plotSpectraCompareLeft" :data="plotdata" :layout="plotlayout" class="q-pa-none q-ma-none"></plotly>
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
<q-tab-panel name="tab3">
|
<q-tab-panel name="tab3">
|
||||||
<!-- Content for Tab 3 -->
|
<!-- Content for Tab 3 -->
|
||||||
<plotly id="plotStyle" :data="plotdataC" :layout="plotlayoutC" class="q-pa-none q-ma-none"></plotly>
|
<plotly id="plotTopoCompareLeft" :data="plotdataC" :layout="plotlayoutC" class="q-pa-none q-ma-none"></plotly>
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
<q-tab-panel name="tab4">
|
<q-tab-panel name="tab4">
|
||||||
<!-- Content for Tab 4 -->
|
<!-- Content for Tab 4 -->
|
||||||
<plotly id="plotStyle3d" :data="plotdata3d" :layout="plotlayout3d" class="q-pa-none q-ma-none"></plotly>
|
<plotly id="plot3dCompareLeft" :data="plotdata3d" :layout="plotlayout3d" class="q-pa-none q-ma-none"></plotly>
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
</q-tab-panels>
|
</q-tab-panels>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<st-tabs id="tabHeader" :ids="CompTabIDs" :labels="CompTabLabels" v-model="CompSelectedTab" outside-arrows
|
<st-tabs id="tabHeaderCompareRight" :ids="CompTabIDs" :labels="CompTabLabels" v-model="CompSelectedTab" outside-arrows
|
||||||
mobile-arrows></st-tabs>
|
mobile-arrows></st-tabs>
|
||||||
<q-tab-panels v-model="CompSelectedTab">
|
<q-tab-panels v-model="CompSelectedTab">
|
||||||
<q-tab-panel name="tab0">
|
<q-tab-panel name="tab0">
|
||||||
<!-- Content for Tab 0 -->
|
<!-- Content for Tab 0 -->
|
||||||
<!-- Btn image changer -->
|
<!-- Btn image changer -->
|
||||||
<div>
|
<div>
|
||||||
<q-btn id="btnStyle" icon="arrow_back" class="q-my-sm" v-on:click="imgMinusComp=true"></q-btn>
|
<q-btn icon="arrow_back" class="q-my-sm btn-style" v-on:click="imgMinusComp=true"></q-btn>
|
||||||
<q-btn id="btnStyle" icon="arrow_forward" class="q-my-sm on-right"
|
<q-btn icon="arrow_forward" class="q-my-sm on-right btn-style"
|
||||||
v-on:click="imgPlusComp=true"></q-btn>
|
v-on:click="imgPlusComp=true"></q-btn>
|
||||||
</div>
|
</div>
|
||||||
<!-- Image manager -->
|
<!-- Image manager -->
|
||||||
<div id="image-container" class="row st-col col-12">
|
<div id="image-container-compare-right-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="plotStyle" :data="plotdataImgComp" :layout="plotlayoutImgComp"
|
<plotly id="plotImgCompareRight" :data="plotdataImgComp" :layout="plotlayoutImgComp"
|
||||||
class="q-pa-none q-ma-none">
|
class="q-pa-none q-ma-none">
|
||||||
</plotly>
|
</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" class="q-ma-none q-pa-none" :src="colorbarComp"></q-img>
|
<q-img id="colorbarComp" class="q-ma-none q-pa-none" :src="colorbarComp"></q-img>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>{{msgimgComp}}</p>
|
<p>{{msgimgComp}}</p>
|
||||||
@ -348,34 +347,34 @@
|
|||||||
<!-- Content for Tab 1 -->
|
<!-- Content for Tab 1 -->
|
||||||
<!-- Triq Btn image changer -->
|
<!-- Triq Btn image changer -->
|
||||||
<div>
|
<div>
|
||||||
<q-btn id="btnStyle" icon="arrow_back" class="q-my-sm" v-on:click="imgMinusTComp=true"></q-btn>
|
<q-btn icon="arrow_back" class="q-my-sm btn-style" v-on:click="imgMinusTComp=true"></q-btn>
|
||||||
<q-btn id="btnStyle" icon="arrow_forward" class="q-my-sm on-right"
|
<q-btn icon="arrow_forward" class="q-my-sm on-right btn-style"
|
||||||
v-on:click="imgPlusTComp=true"></q-btn>
|
v-on:click="imgPlusTComp=true"></q-btn>
|
||||||
</div>
|
</div>
|
||||||
<!-- Triq Image manager -->
|
<!-- Triq Image manager -->
|
||||||
<div id="image-container" class="row st-col col-12">
|
<div id="image-container-compare-right-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="plotStyle" :data="plotdataImgTComp" :layout="plotlayoutImgTComp"
|
<plotly id="plotImgTCompareRight" :data="plotdataImgTComp" :layout="plotlayoutImgTComp"
|
||||||
class="q-pa-none q-ma-none">
|
class="q-pa-none q-ma-none">
|
||||||
</plotly>
|
</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" class="q-ma-none q-pa-none" :src="colorbarTComp"></q-img>
|
<q-img id="colorbarTComp" class="q-ma-none q-pa-none" :src="colorbarTComp"></q-img>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>{{msgtriqComp}}</p>
|
<p>{{msgtriqComp}}</p>
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
<q-tab-panel name="tab2">
|
<q-tab-panel name="tab2">
|
||||||
<!-- Content for Tab 2 -->
|
<!-- Content for Tab 2 -->
|
||||||
<plotly id="plotStyle" :data="plotdata" :layout="plotlayout" class="q-pa-none q-ma-none"></plotly>
|
<plotly id="plotSpectraCompareRight" :data="plotdata" :layout="plotlayout" class="q-pa-none q-ma-none"></plotly>
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
<q-tab-panel name="tab3">
|
<q-tab-panel name="tab3">
|
||||||
<!-- Content for Tab 3 -->
|
<!-- Content for Tab 3 -->
|
||||||
<plotly id="plotStyle" :data="plotdataC" :layout="plotlayoutC" class="q-pa-none q-ma-none"></plotly>
|
<plotly id="plotTopoCompareRight" :data="plotdataC" :layout="plotlayoutC" class="q-pa-none q-ma-none"></plotly>
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
<q-tab-panel name="tab4">
|
<q-tab-panel name="tab4">
|
||||||
<!-- Content for Tab 4 -->
|
<!-- Content for Tab 4 -->
|
||||||
<plotly id="plotStyle3d" :data="plotdata3d" :layout="plotlayout3d" class="q-pa-none q-ma-none"></plotly>
|
<plotly id="plot3dCompareRight" :data="plotdata3d" :layout="plotlayout3d" class="q-pa-none q-ma-none"></plotly>
|
||||||
</q-tab-panel>
|
</q-tab-panel>
|
||||||
</q-tab-panels>
|
</q-tab-panels>
|
||||||
</div>
|
</div>
|
||||||
@ -412,5 +411,3 @@
|
|||||||
</q-card-actions>
|
</q-card-actions>
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-dialog>
|
</q-dialog>
|
||||||
|
|
||||||
</template>
|
|
||||||
@ -26,6 +26,26 @@ function decrement_image(current_image, image_list)
|
|||||||
end
|
end
|
||||||
|
|
||||||
## Plot Image functions
|
## Plot Image functions
|
||||||
|
# Downsample an image matrix to a maximum dimension while preserving aspect ratio
|
||||||
|
function downsample_image(img_matrix, max_dim::Int)
|
||||||
|
h, w = size(img_matrix)
|
||||||
|
if h <= max_dim && w <= max_dim
|
||||||
|
return img_matrix # No downsampling needed
|
||||||
|
end
|
||||||
|
|
||||||
|
aspect_ratio = w / h
|
||||||
|
if w > h
|
||||||
|
new_w = max_dim
|
||||||
|
new_h = round(Int, max_dim / aspect_ratio)
|
||||||
|
else
|
||||||
|
new_h = max_dim
|
||||||
|
new_w = round(Int, max_dim * aspect_ratio)
|
||||||
|
end
|
||||||
|
|
||||||
|
# imresize from Images.jl is perfect for this
|
||||||
|
return imresize(img_matrix, (new_h, new_w))
|
||||||
|
end
|
||||||
|
|
||||||
# loadImgPlot recieves the local directory of the image as a string,
|
# loadImgPlot recieves the local directory of the image as a string,
|
||||||
# returns the layout and data for the heatmap plotly plot
|
# returns the layout and data for the heatmap plotly plot
|
||||||
# this function loads the image into a plot
|
# this function loads the image into a plot
|
||||||
@ -46,6 +66,14 @@ function loadImgPlot(interfaceImg::String)
|
|||||||
|
|
||||||
# Create the layout
|
# Create the layout
|
||||||
layout=PlotlyBase.Layout(
|
layout=PlotlyBase.Layout(
|
||||||
|
title=PlotlyBase.attr(
|
||||||
|
text="",
|
||||||
|
font=PlotlyBase.attr(
|
||||||
|
family="Roboto, Lato, sans-serif",
|
||||||
|
size=14,
|
||||||
|
color="black"
|
||||||
|
)
|
||||||
|
),
|
||||||
xaxis=PlotlyBase.attr(
|
xaxis=PlotlyBase.attr(
|
||||||
visible=false,
|
visible=false,
|
||||||
scaleanchor="y",
|
scaleanchor="y",
|
||||||
@ -64,6 +92,7 @@ function loadImgPlot(interfaceImg::String)
|
|||||||
x=X,
|
x=X,
|
||||||
y=-Y,
|
y=-Y,
|
||||||
name="",
|
name="",
|
||||||
|
hoverinfo="x+y",
|
||||||
showlegend=false,
|
showlegend=false,
|
||||||
colorscale="Viridis",
|
colorscale="Viridis",
|
||||||
showscale=false,
|
showscale=false,
|
||||||
@ -110,6 +139,14 @@ function loadImgPlot(interfaceImg::String, overlayImg::String, imgTrans::Float64
|
|||||||
|
|
||||||
# Create the layout with overlay image
|
# Create the layout with overlay image
|
||||||
layoutImg = PlotlyBase.Layout(
|
layoutImg = PlotlyBase.Layout(
|
||||||
|
title=PlotlyBase.attr(
|
||||||
|
text="",
|
||||||
|
font=PlotlyBase.attr(
|
||||||
|
family="Roboto, Lato, sans-serif",
|
||||||
|
size=14,
|
||||||
|
color="black"
|
||||||
|
)
|
||||||
|
),
|
||||||
images = [attr(
|
images = [attr(
|
||||||
source = "$(overlayImg)?t=$(timestamp)",
|
source = "$(overlayImg)?t=$(timestamp)",
|
||||||
xref = "x",
|
xref = "x",
|
||||||
@ -140,6 +177,7 @@ function loadImgPlot(interfaceImg::String, overlayImg::String, imgTrans::Float64
|
|||||||
x = X,
|
x = X,
|
||||||
y = -Y,
|
y = -Y,
|
||||||
name = "",
|
name = "",
|
||||||
|
hoverinfo = "x+y",
|
||||||
showlegend = false,
|
showlegend = false,
|
||||||
colorscale = "Viridis",
|
colorscale = "Viridis",
|
||||||
showscale = false
|
showscale = false
|
||||||
@ -169,6 +207,10 @@ function loadContourPlot(interfaceImg::String)
|
|||||||
kernel=Kernel.gaussian(sigma)
|
kernel=Kernel.gaussian(sigma)
|
||||||
elevation_smoothed=imfilter(elevation, kernel)
|
elevation_smoothed=imfilter(elevation, kernel)
|
||||||
|
|
||||||
|
# --- DOWNSAMPLING FOR PERFORMANCE ---
|
||||||
|
elevation_smoothed = downsample_image(elevation_smoothed, 512)
|
||||||
|
# ---
|
||||||
|
|
||||||
# Create the X, Y meshgrid coordinates
|
# Create the X, Y meshgrid coordinates
|
||||||
x=1:size(elevation_smoothed, 2)
|
x=1:size(elevation_smoothed, 2)
|
||||||
y=1:size(elevation_smoothed, 1)
|
y=1:size(elevation_smoothed, 1)
|
||||||
@ -182,7 +224,14 @@ function loadContourPlot(interfaceImg::String)
|
|||||||
tickT = log_tick_formatter(collect(tickV))
|
tickT = log_tick_formatter(collect(tickV))
|
||||||
|
|
||||||
layout=PlotlyBase.Layout(
|
layout=PlotlyBase.Layout(
|
||||||
title="2D topographic map of $cleaned_img",
|
title=PlotlyBase.attr(
|
||||||
|
text="2D topographic map of $cleaned_img (downsampled)",
|
||||||
|
font=PlotlyBase.attr(
|
||||||
|
family="Roboto, Lato, sans-serif",
|
||||||
|
size=18,
|
||||||
|
color="black"
|
||||||
|
)
|
||||||
|
),
|
||||||
xaxis=PlotlyBase.attr(
|
xaxis=PlotlyBase.attr(
|
||||||
visible=false,
|
visible=false,
|
||||||
scaleanchor="y"
|
scaleanchor="y"
|
||||||
@ -228,6 +277,10 @@ function loadSurfacePlot(interfaceImg::String)
|
|||||||
kernel=Kernel.gaussian(sigma)
|
kernel=Kernel.gaussian(sigma)
|
||||||
elevation_smoothed=imfilter(elevation, kernel)
|
elevation_smoothed=imfilter(elevation, kernel)
|
||||||
|
|
||||||
|
# --- DOWNSAMPLING FOR PERFORMANCE ---
|
||||||
|
elevation_smoothed = downsample_image(elevation_smoothed, 256)
|
||||||
|
# ---
|
||||||
|
|
||||||
# Create the X, Y meshgrid coordinates
|
# Create the X, Y meshgrid coordinates
|
||||||
x=1:size(elevation_smoothed, 2)
|
x=1:size(elevation_smoothed, 2)
|
||||||
y=1:size(elevation_smoothed, 1)
|
y=1:size(elevation_smoothed, 1)
|
||||||
@ -247,7 +300,14 @@ function loadSurfacePlot(interfaceImg::String)
|
|||||||
aspect_ratio=attr(x=1, y=length(y) / length(x), z=0.5)
|
aspect_ratio=attr(x=1, y=length(y) / length(x), z=0.5)
|
||||||
# Define the layout for the 3D plot
|
# Define the layout for the 3D plot
|
||||||
layout3D=PlotlyBase.Layout(
|
layout3D=PlotlyBase.Layout(
|
||||||
title="3D surface plot of $cleaned_img",
|
title=PlotlyBase.attr(
|
||||||
|
text="3D surface plot of $cleaned_img (downsampled)",
|
||||||
|
font=PlotlyBase.attr(
|
||||||
|
family="Roboto, Lato, sans-serif",
|
||||||
|
size=18,
|
||||||
|
color="black"
|
||||||
|
)
|
||||||
|
),
|
||||||
scene=attr(
|
scene=attr(
|
||||||
xaxis_nticks=x_nticks,
|
xaxis_nticks=x_nticks,
|
||||||
yaxis_nticks=y_nticks,
|
yaxis_nticks=y_nticks,
|
||||||
@ -377,8 +437,9 @@ function generate_colorbar_image(slice_data::AbstractMatrix, color_levels::Int,
|
|||||||
# 3. Create and save the colorbar image
|
# 3. Create and save the colorbar image
|
||||||
fig = Figure(size=(150, 250))
|
fig = Figure(size=(150, 250))
|
||||||
Colorbar(fig[1, 1],
|
Colorbar(fig[1, 1],
|
||||||
colormap=cgrad(:viridis, bins),
|
colormap=cgrad(:viridis, bins, categorical=true),
|
||||||
limits=(min_val, max_val),
|
# limits=(min_val, max_val),
|
||||||
|
limits=(levels[1], levels[end]),
|
||||||
label=(scale == 1 ? "Intensity" : "Intensity ×10^$(round(Int, 3 * exponent))"),
|
label=(scale == 1 ? "Intensity" : "Intensity ×10^$(round(Int, 3 * exponent))"),
|
||||||
ticks=(tick_positions, tick_labels),
|
ticks=(tick_positions, tick_labels),
|
||||||
labelsize=20,
|
labelsize=20,
|
||||||
@ -393,7 +454,14 @@ end
|
|||||||
# its values in the spectrum plot
|
# its values in the spectrum plot
|
||||||
function meanSpectrumPlot(data::MSIData)
|
function meanSpectrumPlot(data::MSIData)
|
||||||
layout = PlotlyBase.Layout(
|
layout = PlotlyBase.Layout(
|
||||||
title="Average Spectrum Plot",
|
title=PlotlyBase.attr(
|
||||||
|
text="Average Spectrum Plot",
|
||||||
|
font=PlotlyBase.attr(
|
||||||
|
family="Roboto, Lato, sans-serif",
|
||||||
|
size=18,
|
||||||
|
color="black"
|
||||||
|
)
|
||||||
|
),
|
||||||
hovermode="closest",
|
hovermode="closest",
|
||||||
xaxis=PlotlyBase.attr(
|
xaxis=PlotlyBase.attr(
|
||||||
title="<i>m/z</i>",
|
title="<i>m/z</i>",
|
||||||
@ -434,7 +502,7 @@ function xySpectrumPlot(data::MSIData, xCoord::Int, yCoord::Int, imgWidth::Int,
|
|||||||
x = clamp(xCoord, 1, imgWidth)
|
x = clamp(xCoord, 1, imgWidth)
|
||||||
y = clamp(yCoord, 1, imgHeight)
|
y = clamp(yCoord, 1, imgHeight)
|
||||||
|
|
||||||
mz, intensity = GetSpectrum(data, x, y)
|
mz, intensity = GetSpectrum(data, Int(x), Int(y))
|
||||||
plot_title = "Spectrum at ($x, $y)"
|
plot_title = "Spectrum at ($x, $y)"
|
||||||
else
|
else
|
||||||
# For non-imaging data, treat xCoord as the spectrum index
|
# For non-imaging data, treat xCoord as the spectrum index
|
||||||
@ -445,7 +513,14 @@ function xySpectrumPlot(data::MSIData, xCoord::Int, yCoord::Int, imgWidth::Int,
|
|||||||
end
|
end
|
||||||
|
|
||||||
layout = PlotlyBase.Layout(
|
layout = PlotlyBase.Layout(
|
||||||
title=plot_title,
|
title=PlotlyBase.attr(
|
||||||
|
text=plot_title,
|
||||||
|
font=PlotlyBase.attr(
|
||||||
|
family="Roboto, Lato, sans-serif",
|
||||||
|
size=18,
|
||||||
|
color="black"
|
||||||
|
)
|
||||||
|
),
|
||||||
hovermode="closest",
|
hovermode="closest",
|
||||||
xaxis=PlotlyBase.attr(
|
xaxis=PlotlyBase.attr(
|
||||||
title="<i>m/z</i>",
|
title="<i>m/z</i>",
|
||||||
@ -472,7 +547,14 @@ end
|
|||||||
|
|
||||||
function sumSpectrumPlot(data::MSIData)
|
function sumSpectrumPlot(data::MSIData)
|
||||||
layout = PlotlyBase.Layout(
|
layout = PlotlyBase.Layout(
|
||||||
title="Total Spectrum Plot",
|
title=PlotlyBase.attr(
|
||||||
|
text="Total Spectrum Plot",
|
||||||
|
font=PlotlyBase.attr(
|
||||||
|
family="Roboto, Lato, sans-serif",
|
||||||
|
size=18,
|
||||||
|
color="black"
|
||||||
|
)
|
||||||
|
),
|
||||||
hovermode="closest",
|
hovermode="closest",
|
||||||
xaxis=PlotlyBase.attr(
|
xaxis=PlotlyBase.attr(
|
||||||
title="<i>m/z</i>",
|
title="<i>m/z</i>",
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
#btnStyle {
|
.btn-style {
|
||||||
background: #009f90;
|
background: #009f90 !important;
|
||||||
color: rgb(229, 236, 246);
|
color: rgb(229, 236, 246) !important;
|
||||||
transition: background-color 0.3s ease;
|
transition: background-color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
#btnStyle:hover {
|
.btn-style:hover {
|
||||||
background-color: #007f72;
|
background-color: #007f72 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#imgInt {
|
#imgInt {
|
||||||
|
|||||||
@ -61,6 +61,8 @@ struct SpectrumAsset
|
|||||||
axis_type::Symbol
|
axis_type::Symbol
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@enum SpectrumMode CENTROID=1 PROFILE=2 UNKNOWN=3
|
||||||
|
|
||||||
"""
|
"""
|
||||||
SpectrumMetadata
|
SpectrumMetadata
|
||||||
|
|
||||||
@ -69,6 +71,7 @@ Contains all metadata for a single spectrum, common to both imzML and mzML forma
|
|||||||
# Fields
|
# Fields
|
||||||
- `x`, `y`: The spatial coordinates of the spectrum (for imzML only).
|
- `x`, `y`: The spatial coordinates of the spectrum (for imzML only).
|
||||||
- `id`: The unique identifier string for the spectrum (for mzML only).
|
- `id`: The unique identifier string for the spectrum (for mzML only).
|
||||||
|
- `mode`: The spectrum mode (`CENTROID` or `PROFILE`).
|
||||||
- `mz_asset`: A `SpectrumAsset` for the m/z array.
|
- `mz_asset`: A `SpectrumAsset` for the m/z array.
|
||||||
- `int_asset`: A `SpectrumAsset` for the intensity array.
|
- `int_asset`: A `SpectrumAsset` for the intensity array.
|
||||||
"""
|
"""
|
||||||
@ -80,6 +83,7 @@ struct SpectrumMetadata
|
|||||||
# For mzML
|
# For mzML
|
||||||
id::String
|
id::String
|
||||||
|
|
||||||
|
mode::SpectrumMode
|
||||||
# Common binary data info
|
# Common binary data info
|
||||||
mz_asset::SpectrumAsset
|
mz_asset::SpectrumAsset
|
||||||
int_asset::SpectrumAsset
|
int_asset::SpectrumAsset
|
||||||
@ -345,25 +349,28 @@ as they can use this cached data. This function modifies the `MSIData` object in
|
|||||||
and is idempotent.
|
and is idempotent.
|
||||||
"""
|
"""
|
||||||
function precompute_analytics(msi_data::MSIData)
|
function precompute_analytics(msi_data::MSIData)
|
||||||
# Idempotency check: If already computed, do nothing.
|
# Idempotency check
|
||||||
if msi_data.spectrum_stats_df !== nothing && hasproperty(msi_data.spectrum_stats_df, :MinMZ)
|
if msi_data.spectrum_stats_df !== nothing && hasproperty(msi_data.spectrum_stats_df, :MinMZ)
|
||||||
println("Analytics have already been pre-computed.")
|
println("Analytics have already been pre-computed.")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
"""
|
|
||||||
meta = msi_data.spectra_metadata[1]
|
|
||||||
println("First spectrum:")
|
|
||||||
println(" mz compressed: $(meta.mz_asset.is_compressed)")
|
|
||||||
println(" int compressed: $(meta.int_asset.is_compressed)")
|
|
||||||
println(" mz encoded_length: $(meta.mz_asset.encoded_length)")
|
|
||||||
println(" int encoded_length: $(meta.int_asset.encoded_length)")
|
|
||||||
|
|
||||||
println("Pre-computing analytics (single pass)...")
|
println("Pre-computing analytics (single pass)...")
|
||||||
"""
|
|
||||||
start_time = time_ns()
|
start_time = time_ns()
|
||||||
|
|
||||||
num_spectra = length(msi_data.spectra_metadata)
|
num_spectra = length(msi_data.spectra_metadata)
|
||||||
|
|
||||||
|
# DEBUG: Check the first spectrum's metadata
|
||||||
|
if num_spectra > 0
|
||||||
|
first_meta = msi_data.spectra_metadata[1]
|
||||||
|
println("DEBUG First spectrum metadata:")
|
||||||
|
println(" mz_asset: format=$(first_meta.mz_asset.format), compressed=$(first_meta.mz_asset.is_compressed)")
|
||||||
|
println(" mz_asset: offset=$(first_meta.mz_asset.offset), encoded_length=$(first_meta.mz_asset.encoded_length)")
|
||||||
|
println(" int_asset: format=$(first_meta.int_asset.format), compressed=$(first_meta.int_asset.is_compressed)")
|
||||||
|
println(" int_asset: offset=$(first_meta.int_asset.offset), encoded_length=$(first_meta.int_asset.encoded_length)")
|
||||||
|
println(" mode: $(first_meta.mode)")
|
||||||
|
end
|
||||||
|
|
||||||
# Initialize variables for global stats
|
# Initialize variables for global stats
|
||||||
g_min_mz = Inf
|
g_min_mz = Inf
|
||||||
g_max_mz = -Inf
|
g_max_mz = -Inf
|
||||||
@ -375,9 +382,21 @@ function precompute_analytics(msi_data::MSIData)
|
|||||||
num_points = Vector{Int}(undef, num_spectra)
|
num_points = Vector{Int}(undef, num_spectra)
|
||||||
min_mzs = Vector{Float64}(undef, num_spectra)
|
min_mzs = Vector{Float64}(undef, num_spectra)
|
||||||
max_mzs = Vector{Float64}(undef, num_spectra)
|
max_mzs = Vector{Float64}(undef, num_spectra)
|
||||||
|
modes = Vector{SpectrumMode}(undef, num_spectra)
|
||||||
|
is_compressed = Vector{Bool}(undef, num_spectra)
|
||||||
|
|
||||||
|
# DEBUG: Add counter to see how many spectra have data
|
||||||
|
spectra_with_data = 0
|
||||||
|
empty_spectra = 0
|
||||||
|
|
||||||
_iterate_spectra_fast(msi_data) do idx, mz, intensity
|
_iterate_spectra_fast(msi_data) do idx, mz, intensity
|
||||||
|
# Store metadata
|
||||||
|
modes[idx] = msi_data.spectra_metadata[idx].mode
|
||||||
|
is_compressed[idx] = msi_data.spectra_metadata[idx].mz_asset.is_compressed ||
|
||||||
|
msi_data.spectra_metadata[idx].int_asset.is_compressed
|
||||||
|
|
||||||
if isempty(mz)
|
if isempty(mz)
|
||||||
|
empty_spectra += 1
|
||||||
tics[idx] = 0.0
|
tics[idx] = 0.0
|
||||||
bpis[idx] = 0.0
|
bpis[idx] = 0.0
|
||||||
bp_mzs[idx] = 0.0
|
bp_mzs[idx] = 0.0
|
||||||
@ -385,6 +404,8 @@ function precompute_analytics(msi_data::MSIData)
|
|||||||
min_mzs[idx] = Inf
|
min_mzs[idx] = Inf
|
||||||
max_mzs[idx] = -Inf
|
max_mzs[idx] = -Inf
|
||||||
return
|
return
|
||||||
|
else
|
||||||
|
spectra_with_data += 1
|
||||||
end
|
end
|
||||||
|
|
||||||
# Update global m/z range
|
# Update global m/z range
|
||||||
@ -402,6 +423,27 @@ function precompute_analytics(msi_data::MSIData)
|
|||||||
num_points[idx] = length(mz)
|
num_points[idx] = length(mz)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Add mode statistics
|
||||||
|
centroid_count = count(==(CENTROID), modes)
|
||||||
|
profile_count = count(==(PROFILE), modes)
|
||||||
|
unknown_count = count(==(UNKNOWN), modes)
|
||||||
|
|
||||||
|
println("DEBUG Mode Statistics:")
|
||||||
|
println(" Centroid spectra: $centroid_count")
|
||||||
|
println(" Profile spectra: $profile_count")
|
||||||
|
println(" Unknown mode: $unknown_count")
|
||||||
|
|
||||||
|
# DEBUG: Print summary
|
||||||
|
println("DEBUG Analytics Summary:")
|
||||||
|
println(" Total spectra: $num_spectra")
|
||||||
|
println(" Spectra with data: $spectra_with_data")
|
||||||
|
println(" Empty spectra: $empty_spectra")
|
||||||
|
println(" Global m/z range: [$g_min_mz, $g_max_mz]")
|
||||||
|
println(" Centroid spectra: $(count(==(CENTROID), modes))")
|
||||||
|
println(" Profile spectra: $(count(==(PROFILE), modes))")
|
||||||
|
println(" Compressed spectra: $(sum(is_compressed))")
|
||||||
|
println(" Average points per spectrum: $(mean(num_points))")
|
||||||
|
|
||||||
# Populate the MSIData object
|
# Populate the MSIData object
|
||||||
msi_data.global_min_mz = g_min_mz
|
msi_data.global_min_mz = g_min_mz
|
||||||
msi_data.global_max_mz = g_max_mz
|
msi_data.global_max_mz = g_max_mz
|
||||||
@ -412,7 +454,9 @@ function precompute_analytics(msi_data::MSIData)
|
|||||||
BasePeakMZ = bp_mzs,
|
BasePeakMZ = bp_mzs,
|
||||||
NumPoints = num_points,
|
NumPoints = num_points,
|
||||||
MinMZ = min_mzs,
|
MinMZ = min_mzs,
|
||||||
MaxMZ = max_mzs
|
MaxMZ = max_mzs,
|
||||||
|
Mode = modes,
|
||||||
|
IsCompressed = is_compressed
|
||||||
)
|
)
|
||||||
|
|
||||||
duration = (time_ns() - start_time) / 1e9
|
duration = (time_ns() - start_time) / 1e9
|
||||||
@ -731,12 +775,15 @@ function read_compressed_array(io::IO, asset::SpectrumAsset, format::Type)
|
|||||||
seek(io, asset.offset)
|
seek(io, asset.offset)
|
||||||
|
|
||||||
if asset.is_compressed
|
if asset.is_compressed
|
||||||
# Read compressed bytes
|
# Read compressed bytes - use encoded_length as the number of compressed bytes
|
||||||
compressed_bytes = read(io, asset.encoded_length)
|
compressed_bytes = read(io, asset.encoded_length)
|
||||||
|
|
||||||
|
println("DEBUG: Decompressing data - offset=$(asset.offset), compressed_bytes=$(length(compressed_bytes))")
|
||||||
|
|
||||||
local decompressed_bytes
|
local decompressed_bytes
|
||||||
try
|
try
|
||||||
decompressed_bytes = Libz.inflate(compressed_bytes)
|
decompressed_bytes = Libz.inflate(compressed_bytes)
|
||||||
|
println("DEBUG: Decompression successful - decompressed_bytes=$(length(decompressed_bytes))")
|
||||||
catch e
|
catch e
|
||||||
@error "ZLIB DECOMPRESSION FAILED. This is likely due to an incorrect offset or corrupt data in the .ibd file."
|
@error "ZLIB DECOMPRESSION FAILED. This is likely due to an incorrect offset or corrupt data in the .ibd file."
|
||||||
@error "Asset offset: $(asset.offset), Encoded length: $(asset.encoded_length)"
|
@error "Asset offset: $(asset.offset), Encoded length: $(asset.encoded_length)"
|
||||||
@ -747,8 +794,7 @@ function read_compressed_array(io::IO, asset::SpectrumAsset, format::Type)
|
|||||||
rethrow(e)
|
rethrow(e)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Use an IOBuffer to safely read the data, avoiding reinterpret errors
|
# Use an IOBuffer to safely read the data
|
||||||
# if the decompressed size is not a perfect multiple of the element size.
|
|
||||||
bytes_io = IOBuffer(decompressed_bytes)
|
bytes_io = IOBuffer(decompressed_bytes)
|
||||||
n_elements = bytes_io.size ÷ sizeof(format)
|
n_elements = bytes_io.size ÷ sizeof(format)
|
||||||
array = Array{format}(undef, n_elements)
|
array = Array{format}(undef, n_elements)
|
||||||
|
|||||||
@ -20,6 +20,7 @@ mutable struct SpecDim
|
|||||||
Packed::Bool
|
Packed::Bool
|
||||||
Axis::Int
|
Axis::Int
|
||||||
Skip::Int
|
Skip::Int
|
||||||
|
Mode::SpectrumMode
|
||||||
end
|
end
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@ -51,7 +52,7 @@ Retrieves an attribute's value from an XML tag string.
|
|||||||
"""
|
"""
|
||||||
function get_attribute(source::AbstractString, tag::String = "([^=]+)")
|
function get_attribute(source::AbstractString, tag::String = "([^=]+)")
|
||||||
# Construct the regex pattern string
|
# Construct the regex pattern string
|
||||||
pattern_str = "\\s" * tag * "=\"([^\"]*)\""
|
pattern_str = "\\s*" * tag * "=\"([^\"]*)\""
|
||||||
regStr = Regex(pattern_str)
|
regStr = Regex(pattern_str)
|
||||||
return match(regStr, source)
|
return match(regStr, source)
|
||||||
end
|
end
|
||||||
@ -70,18 +71,19 @@ compression status (`zlib`), and axis type (m/z vs. intensity).
|
|||||||
- A `SpecDim` struct populated with the parsed configuration.
|
- A `SpecDim` struct populated with the parsed configuration.
|
||||||
"""
|
"""
|
||||||
function configure_spec_dim(stream)
|
function configure_spec_dim(stream)
|
||||||
axis = SpecDim(Float64, false, 1, 0)
|
axis = SpecDim(Float64, false, 1, 0, UNKNOWN) # Add UNKNOWN as default mode
|
||||||
offset = position(stream)
|
|
||||||
|
|
||||||
while !eof(stream)
|
while !eof(stream)
|
||||||
currLine = readline(stream)
|
currLine = readline(stream)
|
||||||
matchInfo = match(r"^\s*<(cvParam)", currLine)
|
matchInfo = match(r"^\s*<(cvParam)", currLine)
|
||||||
|
|
||||||
if matchInfo === nothing
|
if matchInfo === nothing
|
||||||
matchInfo = match(r"^\s*", currLine)
|
# Check for end of cvParam block
|
||||||
axis.Skip = position(stream) - offset - length(currLine) + length(matchInfo.match)
|
if match(r"^\s*</", currLine) !== nothing
|
||||||
return axis
|
return axis
|
||||||
end
|
end
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
index = length(matchInfo.captures[1])
|
index = length(matchInfo.captures[1])
|
||||||
attr_match = get_attribute(currLine[index:end], "accession")
|
attr_match = get_attribute(currLine[index:end], "accession")
|
||||||
@ -98,10 +100,14 @@ function configure_spec_dim(stream)
|
|||||||
axis.Format = Int64
|
axis.Format = Int64
|
||||||
elseif accession == "MS:1000574" # zlib compression
|
elseif accession == "MS:1000574" # zlib compression
|
||||||
axis.Packed = true
|
axis.Packed = true
|
||||||
|
elseif accession == "MS:1000127" # centroid spectrum
|
||||||
|
axis.Mode = CENTROID
|
||||||
|
elseif accession == "MS:1000128" # profile spectrum
|
||||||
|
axis.Mode = PROFILE
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return axis # Should be unreachable if file is well-formed
|
return axis
|
||||||
end
|
end
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|||||||
487
src/imzML.jl
487
src/imzML.jl
@ -26,16 +26,25 @@ Core Functions:
|
|||||||
Determines the storage order of the m/z and intensity arrays.
|
Determines the storage order of the m/z and intensity arrays.
|
||||||
"""
|
"""
|
||||||
function axes_config_img(stream)
|
function axes_config_img(stream)
|
||||||
tag = find_tag(stream, r"^\s*<(referenceableParamGroup )")
|
param_groups = Dict{String, SpecDim}()
|
||||||
value = get_attribute(tag.captures[1], "intensityArray")
|
find_tag(stream, r"<referenceableParamGroupList")
|
||||||
order = 1 + (value !== nothing)
|
|
||||||
|
|
||||||
axis = Array{SpecDim,1}(undef, 2)
|
while true
|
||||||
axis[order] = configure_spec_dim(stream)
|
pos = position(stream)
|
||||||
|
line = readline(stream)
|
||||||
|
|
||||||
find_tag(stream, r"^\s*<(referenceableParamGroup )")
|
if eof(stream) || occursin("</referenceableParamGroupList>", line)
|
||||||
axis[xor(order, 3)] = configure_spec_dim(stream)
|
break
|
||||||
return axis
|
end
|
||||||
|
|
||||||
|
id_match = match(r"<referenceableParamGroup id=\"([^\"]+)\"", line)
|
||||||
|
if id_match !== nothing
|
||||||
|
id = id_match.captures[1]
|
||||||
|
spec_dim = configure_spec_dim(stream)
|
||||||
|
param_groups[id] = spec_dim
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return param_groups
|
||||||
end
|
end
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@ -137,13 +146,57 @@ function get_spectrum_attributes(stream, hIbd)
|
|||||||
return skip
|
return skip
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function determine_parser(stream, mz_is_compressed, int_is_compressed)
|
||||||
|
start_pos = position(stream)
|
||||||
|
spectrum_xml = ""
|
||||||
|
|
||||||
"""
|
try
|
||||||
load_imzml_lazy(file_path::String; cache_size=100)
|
# Find the start of the first spectrum tag
|
||||||
|
while !eof(stream)
|
||||||
|
line = readline(stream)
|
||||||
|
if occursin("<spectrum ", line)
|
||||||
|
spectrum_buffer = IOBuffer()
|
||||||
|
write(spectrum_buffer, line)
|
||||||
|
# Read until the end of the spectrum tag
|
||||||
|
while !eof(stream)
|
||||||
|
line = readline(stream)
|
||||||
|
write(spectrum_buffer, line)
|
||||||
|
if occursin("</spectrum>", line)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
spectrum_xml = String(take!(spectrum_buffer))
|
||||||
|
break # Found the first spectrum, so we can stop
|
||||||
|
end
|
||||||
|
end
|
||||||
|
finally
|
||||||
|
seek(stream, start_pos) # Always reset stream position
|
||||||
|
end
|
||||||
|
|
||||||
|
if isempty(spectrum_xml)
|
||||||
|
# Fallback based on compression flags if no spectrum tag found
|
||||||
|
return (mz_is_compressed || int_is_compressed) ? :compressed : :uncompressed
|
||||||
|
end
|
||||||
|
|
||||||
|
# Inspect the XML content
|
||||||
|
has_neofx_markers = occursin("encodedLength=\"0\"", spectrum_xml) &&
|
||||||
|
occursin("external encoded length", spectrum_xml)
|
||||||
|
|
||||||
|
has_external_data_markers = occursin("IMS:1000101", spectrum_xml) &&
|
||||||
|
occursin("IMS:1000102", spectrum_xml) &&
|
||||||
|
occursin("IMS:1000103", spectrum_xml)
|
||||||
|
|
||||||
|
if has_neofx_markers
|
||||||
|
return :neofx
|
||||||
|
end
|
||||||
|
|
||||||
|
if mz_is_compressed || int_is_compressed || has_external_data_markers
|
||||||
|
return :compressed
|
||||||
|
end
|
||||||
|
|
||||||
|
return :uncompressed
|
||||||
|
end
|
||||||
|
|
||||||
Main function to parse an `.imzML`/.ibd file pair and prepare for lazy loading.
|
|
||||||
It now returns a unified MSIData object.
|
|
||||||
"""
|
|
||||||
function load_imzml_lazy(file_path::String; cache_size=100)
|
function load_imzml_lazy(file_path::String; cache_size=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)
|
||||||
@ -162,22 +215,96 @@ function load_imzml_lazy(file_path::String; cache_size=100)
|
|||||||
|
|
||||||
try
|
try
|
||||||
println("DEBUG: Configuring axes...")
|
println("DEBUG: Configuring axes...")
|
||||||
axis = axes_config_img(stream)
|
param_groups = axes_config_img(stream)
|
||||||
println("DEBUG: Getting image dimensions...")
|
println("DEBUG: Getting image dimensions...")
|
||||||
imgDim = get_img_dimensions(stream)
|
imgDim = get_img_dimensions(stream)
|
||||||
width, height, num_spectra = imgDim
|
width, height, num_spectra = imgDim
|
||||||
println("DEBUG: Image dimensions: $(width)x$(height), $num_spectra spectra.")
|
println("DEBUG: Image dimensions: $(width)x$(height), $num_spectra spectra.")
|
||||||
|
|
||||||
mz_config_idx = findfirst(a -> a.Axis == 1, axis)
|
# Extract default formats from the parsed param_groups
|
||||||
int_config_idx = findfirst(a -> a.Axis == 2, axis)
|
mz_group = nothing
|
||||||
mz_format = axis[mz_config_idx].Format
|
int_group = nothing
|
||||||
intensity_format = axis[int_config_idx].Format
|
|
||||||
mz_is_compressed = axis[mz_config_idx].Packed
|
|
||||||
int_is_compressed = axis[int_config_idx].Packed
|
|
||||||
println("DEBUG: m/z format: $mz_format, Intensity format: $intensity_format")
|
|
||||||
println("DEBUG: m/z compressed: $mz_is_compressed, Intensity compressed: $int_is_compressed")
|
|
||||||
|
|
||||||
# --- NEW PARSING LOGIC based on the old, working code ---
|
for group in values(param_groups)
|
||||||
|
if group.Axis == 1
|
||||||
|
mz_group = group
|
||||||
|
elseif group.Axis == 2
|
||||||
|
int_group = group
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if mz_group === nothing || int_group === nothing
|
||||||
|
@warn "Could not find global definitions for m/z and intensity arrays. Using hardcoded defaults (Float64)."
|
||||||
|
default_mz_format = Float64
|
||||||
|
default_intensity_format = Float64
|
||||||
|
mz_is_compressed = false
|
||||||
|
int_is_compressed = false
|
||||||
|
global_mode = UNKNOWN
|
||||||
|
else
|
||||||
|
default_mz_format = mz_group.Format
|
||||||
|
default_intensity_format = int_group.Format
|
||||||
|
mz_is_compressed = mz_group.Packed
|
||||||
|
int_is_compressed = int_group.Packed
|
||||||
|
global_mode = mz_group.Mode != UNKNOWN ? mz_group.Mode : int_group.Mode
|
||||||
|
end
|
||||||
|
|
||||||
|
println("DEBUG: m/z format: $default_mz_format, Intensity format: $default_intensity_format")
|
||||||
|
println("DEBUG: m/z compressed: $mz_is_compressed, Intensity compressed: $int_is_compressed")
|
||||||
|
println("DEBUG: Global mode: $global_mode")
|
||||||
|
|
||||||
|
# --- Parser Selection ---
|
||||||
|
parser_type = determine_parser(stream, mz_is_compressed, int_is_compressed)
|
||||||
|
println("DEBUG: Selected parser: $parser_type")
|
||||||
|
|
||||||
|
local spectra_metadata
|
||||||
|
if parser_type == :neofx
|
||||||
|
println("DEBUG: Using neofx parser.")
|
||||||
|
spectra_metadata = parse_neofx(stream, hIbd, param_groups, width, height, num_spectra,
|
||||||
|
default_mz_format, default_intensity_format,
|
||||||
|
mz_is_compressed, int_is_compressed, global_mode)
|
||||||
|
elseif parser_type == :compressed
|
||||||
|
println("DEBUG: Using compressed parser.")
|
||||||
|
spectra_metadata = parse_compressed(stream, hIbd, param_groups, width, height, num_spectra,
|
||||||
|
default_mz_format, default_intensity_format,
|
||||||
|
mz_is_compressed, int_is_compressed, global_mode)
|
||||||
|
else # :uncompressed
|
||||||
|
println("DEBUG: Using uncompressed parser.")
|
||||||
|
spectra_metadata = parse_uncompressed(stream, 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.")
|
||||||
|
|
||||||
|
# Build coordinate map for imzML files
|
||||||
|
println("DEBUG: Building coordinate map...")
|
||||||
|
coordinate_map = zeros(Int, width, height)
|
||||||
|
for (idx, meta) in enumerate(spectra_metadata)
|
||||||
|
if idx == 1
|
||||||
|
println("DIAGNOSTIC_WRITE: For index 1, attempting to write to coordinate_map[$(meta.x), $(meta.y)]")
|
||||||
|
end
|
||||||
|
if 1 <= meta.x <= width && 1 <= meta.y <= height
|
||||||
|
coordinate_map[meta.x, meta.y] = idx
|
||||||
|
end
|
||||||
|
end
|
||||||
|
println("DEBUG: Coordinate map built.")
|
||||||
|
|
||||||
|
close(stream)
|
||||||
|
|
||||||
|
source = ImzMLSource(hIbd, default_mz_format, default_intensity_format)
|
||||||
|
println("DEBUG: Creating MSIData object.")
|
||||||
|
return MSIData(source, spectra_metadata, (width, height), coordinate_map, cache_size)
|
||||||
|
|
||||||
|
catch e
|
||||||
|
close(stream)
|
||||||
|
close(hIbd)
|
||||||
|
rethrow(e)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function parse_uncompressed(stream, hIbd, param_groups, width, height, num_spectra,
|
||||||
|
mz_format, intensity_format, mz_is_compressed, int_is_compressed, global_mode)
|
||||||
|
# Your existing working skip-based parser
|
||||||
println("DEBUG: Learning file structure from first spectrum...")
|
println("DEBUG: Learning file structure from first spectrum...")
|
||||||
start_of_spectra_xml = position(stream)
|
start_of_spectra_xml = position(stream)
|
||||||
attr = get_spectrum_attributes(stream, hIbd)
|
attr = get_spectrum_attributes(stream, hIbd)
|
||||||
@ -188,9 +315,11 @@ function load_imzml_lazy(file_path::String; cache_size=100)
|
|||||||
spectra_metadata = Vector{SpectrumMetadata}(undef, num_spectra)
|
spectra_metadata = Vector{SpectrumMetadata}(undef, num_spectra)
|
||||||
mz_is_first = attr[3] == 3
|
mz_is_first = attr[3] == 3
|
||||||
|
|
||||||
println("DEBUG: Parsing metadata for $num_spectra spectra using skip-based method...")
|
|
||||||
for k in 1:num_spectra
|
for k in 1:num_spectra
|
||||||
# Use skip values learned from the first spectrum, assuming all are identical.
|
# Store the start position of this spectrum for mode detection
|
||||||
|
spectrum_start_pos = position(stream)
|
||||||
|
|
||||||
|
# Use skip values learned from the first spectrum
|
||||||
skip(stream, attr[5]) # Skip to X coordinate value
|
skip(stream, attr[5]) # Skip to X coordinate value
|
||||||
val_tag_x = find_tag(stream, r"value=\"(\d+)\"")
|
val_tag_x = find_tag(stream, r"value=\"(\d+)\"")
|
||||||
x = parse(Int32, val_tag_x.captures[1])
|
x = parse(Int32, val_tag_x.captures[1])
|
||||||
@ -203,6 +332,7 @@ function load_imzml_lazy(file_path::String; cache_size=100)
|
|||||||
val_tag_len = find_tag(stream, r"value=\"(\d+)\"")
|
val_tag_len = find_tag(stream, r"value=\"(\d+)\"")
|
||||||
nPoints = parse(Int32, val_tag_len.captures[1])
|
nPoints = parse(Int32, val_tag_len.captures[1])
|
||||||
|
|
||||||
|
# For uncompressed data, use simple calculation
|
||||||
mz_len_bytes = nPoints * sizeof(mz_format)
|
mz_len_bytes = nPoints * sizeof(mz_format)
|
||||||
int_len_bytes = nPoints * sizeof(intensity_format)
|
int_len_bytes = nPoints * sizeof(intensity_format)
|
||||||
|
|
||||||
@ -215,49 +345,299 @@ function load_imzml_lazy(file_path::String; cache_size=100)
|
|||||||
mz_offset = int_offset + int_len_bytes
|
mz_offset = int_offset + int_len_bytes
|
||||||
end
|
end
|
||||||
|
|
||||||
# Create modern SpectrumAsset objects
|
# Mode detection from spectrum XML
|
||||||
|
current_pos = position(stream)
|
||||||
|
seek(stream, spectrum_start_pos)
|
||||||
|
spectrum_buffer = IOBuffer()
|
||||||
|
line = ""
|
||||||
|
while !eof(stream)
|
||||||
|
line = readline(stream)
|
||||||
|
write(spectrum_buffer, line)
|
||||||
|
if occursin("</spectrum>", line)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
spectrum_xml = String(take!(spectrum_buffer))
|
||||||
|
|
||||||
|
spectrum_mode = global_mode
|
||||||
|
if occursin("MS:1000127", spectrum_xml)
|
||||||
|
spectrum_mode = CENTROID
|
||||||
|
elseif occursin("MS:1000128", spectrum_xml)
|
||||||
|
spectrum_mode = PROFILE
|
||||||
|
end
|
||||||
|
seek(stream, current_pos)
|
||||||
|
|
||||||
|
# Create SpectrumAsset objects
|
||||||
mz_asset = SpectrumAsset(mz_format, mz_is_compressed, mz_offset, nPoints, :mz)
|
mz_asset = SpectrumAsset(mz_format, mz_is_compressed, mz_offset, nPoints, :mz)
|
||||||
int_asset = SpectrumAsset(intensity_format, int_is_compressed, int_offset, nPoints, :intensity)
|
int_asset = SpectrumAsset(intensity_format, int_is_compressed, int_offset, nPoints, :intensity)
|
||||||
|
|
||||||
spectra_metadata[k] = SpectrumMetadata(x, y, "", mz_asset, int_asset)
|
spectra_metadata[k] = SpectrumMetadata(x, y, "", spectrum_mode, mz_asset, int_asset)
|
||||||
|
|
||||||
# Advance the offset for the next spectrum's data.
|
|
||||||
current_ibd_offset += mz_len_bytes + int_len_bytes
|
current_ibd_offset += mz_len_bytes + int_len_bytes
|
||||||
|
|
||||||
skip(stream, attr[8]) # Skip to the end of the spectrum tag
|
skip(stream, attr[8]) # Skip to the end of the spectrum tag
|
||||||
end
|
end
|
||||||
# --- END OF NEW PARSING LOGIC ---
|
|
||||||
|
|
||||||
println("DEBUG: Metadata parsing complete.")
|
return spectra_metadata
|
||||||
|
end
|
||||||
|
|
||||||
# Build coordinate map for imzML files
|
function parse_compressed(stream, hIbd, param_groups, width, height, num_spectra,
|
||||||
println("DEBUG: Building coordinate map...")
|
default_mz_format, default_intensity_format,
|
||||||
coordinate_map = zeros(Int, width, height)
|
mz_is_compressed, int_is_compressed, global_mode)
|
||||||
for (idx, meta) in enumerate(spectra_metadata)
|
# New parser for compressed data
|
||||||
if 1 <= meta.x <= width && 1 <= meta.y <= height
|
spectra_metadata = Vector{SpectrumMetadata}(undef, num_spectra)
|
||||||
coordinate_map[meta.x, meta.y] = idx
|
|
||||||
|
for k in 1:num_spectra
|
||||||
|
# Read the full spectrum XML block
|
||||||
|
spectrum_buffer = IOBuffer()
|
||||||
|
line = ""
|
||||||
|
while !eof(stream)
|
||||||
|
line = readline(stream)
|
||||||
|
if occursin("<spectrum ", line)
|
||||||
|
write(spectrum_buffer, line)
|
||||||
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
println("DEBUG: Coordinate map built.")
|
while !eof(stream)
|
||||||
|
line = readline(stream)
|
||||||
close(stream)
|
write(spectrum_buffer, line)
|
||||||
|
if occursin("</spectrum>", line)
|
||||||
source = ImzMLSource(hIbd, mz_format, intensity_format)
|
break
|
||||||
println("DEBUG: Creating MSIData object.")
|
|
||||||
return MSIData(source, spectra_metadata, (width, height), coordinate_map, cache_size)
|
|
||||||
|
|
||||||
catch e
|
|
||||||
close(stream)
|
|
||||||
close(hIbd)
|
|
||||||
rethrow(e)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
spectrum_xml = String(take!(spectrum_buffer))
|
||||||
|
|
||||||
|
# Parse coordinates
|
||||||
|
x_match = match(r"IMS:1000050.*?value=\"(\d+)\"", spectrum_xml)
|
||||||
|
y_match = match(r"IMS:1000051.*?value=\"(\d+)\"", spectrum_xml)
|
||||||
|
x = x_match !== nothing ? parse(Int32, x_match.captures[1]) : Int32(0)
|
||||||
|
y = y_match !== nothing ? parse(Int32, y_match.captures[1]) : Int32(0)
|
||||||
|
|
||||||
|
# Parse mode
|
||||||
|
spectrum_mode = global_mode
|
||||||
|
if occursin("MS:1000127", spectrum_xml)
|
||||||
|
spectrum_mode = CENTROID
|
||||||
|
elseif occursin("MS:1000128", spectrum_xml)
|
||||||
|
spectrum_mode = PROFILE
|
||||||
|
end
|
||||||
|
|
||||||
|
# Parse binary data arrays
|
||||||
|
array_data = []
|
||||||
|
|
||||||
|
# Find all binaryDataArray blocks
|
||||||
|
array_matches = eachmatch(r"<binaryDataArray.*?<\/binaryDataArray>"s, spectrum_xml)
|
||||||
|
for array_match in array_matches
|
||||||
|
array_xml = array_match.match
|
||||||
|
|
||||||
|
# Determine if this is m/z or intensity array
|
||||||
|
is_mz = occursin("MS:1000514", array_xml) || occursin("mzArray", array_xml)
|
||||||
|
|
||||||
|
# Parse external data parameters
|
||||||
|
# Get array_length (nPoints)
|
||||||
|
array_len_cv_match = match(r"IMS:1000103.*?value=\"(\d+)\"", array_xml)
|
||||||
|
array_length = 0
|
||||||
|
if array_len_cv_match !== nothing
|
||||||
|
array_length = parse(Int32, array_len_cv_match.captures[1])
|
||||||
|
end
|
||||||
|
if array_length == 0
|
||||||
|
nPoints_match = match(r"defaultArrayLength=\"(\d+)\"", spectrum_xml)
|
||||||
|
if nPoints_match !== nothing
|
||||||
|
array_length = parse(Int32, nPoints_match.captures[1])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get encoded_length
|
||||||
|
encoded_len_cv_match = match(r"IMS:1000104.*?value=\"(\d+)\"", array_xml)
|
||||||
|
encoded_length = 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+)\"", array_xml)
|
||||||
|
if encoded_len_attr_match !== nothing
|
||||||
|
encoded_length = parse(Int64, encoded_len_attr_match.captures[1])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get offset
|
||||||
|
offset_match = match(r"IMS:1000102.*?value=\"(\d+)\"", array_xml)
|
||||||
|
offset = 0
|
||||||
|
if offset_match !== nothing
|
||||||
|
offset = parse(Int64, offset_match.captures[1])
|
||||||
|
end
|
||||||
|
|
||||||
|
if array_length > 0 && offset > 0
|
||||||
|
push!(array_data, (
|
||||||
|
is_mz = is_mz,
|
||||||
|
array_length = array_length,
|
||||||
|
encoded_length = encoded_length,
|
||||||
|
offset = offset
|
||||||
|
))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Separate m/z and intensity arrays
|
||||||
|
mz_data = filter(d -> d.is_mz, array_data)
|
||||||
|
int_data = filter(d -> !d.is_mz, array_data)
|
||||||
|
|
||||||
|
if length(mz_data) != 1 || length(int_data) != 1
|
||||||
|
error("Spectrum $k: Expected exactly one m/z and one intensity array")
|
||||||
|
end
|
||||||
|
|
||||||
|
mz_info = mz_data[1]
|
||||||
|
int_info = int_data[1]
|
||||||
|
|
||||||
|
# DEBUG: Print first spectrum details
|
||||||
|
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)")
|
||||||
|
println(" Expected m/z bytes: $(mz_info.array_length * sizeof(default_mz_format))")
|
||||||
|
println(" Expected intensity bytes: $(int_info.array_length * sizeof(default_intensity_format))")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Create SpectrumAsset objects
|
||||||
|
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)
|
||||||
|
|
||||||
|
spectra_metadata[k] = SpectrumMetadata(x, y, "", spectrum_mode, mz_asset, int_asset)
|
||||||
|
end
|
||||||
|
|
||||||
|
return spectra_metadata
|
||||||
|
end
|
||||||
|
|
||||||
|
function parse_neofx(stream, hIbd, param_groups, width, height, num_spectra,
|
||||||
|
default_mz_format, default_intensity_format,
|
||||||
|
mz_is_compressed, int_is_compressed, global_mode)
|
||||||
|
# New parser for compressed data
|
||||||
|
spectra_metadata = Vector{SpectrumMetadata}(undef, num_spectra)
|
||||||
|
|
||||||
|
for k in 1:num_spectra
|
||||||
|
# Read the full spectrum XML block
|
||||||
|
spectrum_buffer = IOBuffer()
|
||||||
|
line = ""
|
||||||
|
while !eof(stream)
|
||||||
|
line = readline(stream)
|
||||||
|
if occursin("<spectrum ", line)
|
||||||
|
write(spectrum_buffer, line)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
while !eof(stream)
|
||||||
|
line = readline(stream)
|
||||||
|
write(spectrum_buffer, line)
|
||||||
|
if occursin("</spectrum>", line)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
spectrum_xml = String(take!(spectrum_buffer))
|
||||||
|
|
||||||
|
# Parse coordinates
|
||||||
|
x_match = match(r"IMS:1000050.*?value=\"(\d+)\"", spectrum_xml)
|
||||||
|
y_match = match(r"IMS:1000051.*?value=\"(\d+)\"", spectrum_xml)
|
||||||
|
x = x_match !== nothing ? parse(Int32, x_match.captures[1]) : Int32(0)
|
||||||
|
y = y_match !== nothing ? parse(Int32, y_match.captures[1]) : Int32(0)
|
||||||
|
|
||||||
|
# Parse mode
|
||||||
|
spectrum_mode = global_mode
|
||||||
|
if occursin("MS:1000127", spectrum_xml)
|
||||||
|
spectrum_mode = CENTROID
|
||||||
|
elseif occursin("MS:1000128", spectrum_xml)
|
||||||
|
spectrum_mode = PROFILE
|
||||||
|
end
|
||||||
|
|
||||||
|
# Parse binary data arrays
|
||||||
|
array_data = []
|
||||||
|
|
||||||
|
# Find all binaryDataArray blocks
|
||||||
|
array_matches = eachmatch(r"<binaryDataArray.*?<\/binaryDataArray>"s, spectrum_xml)
|
||||||
|
for array_match in array_matches
|
||||||
|
array_xml = array_match.match
|
||||||
|
|
||||||
|
# Determine if this is m/z or intensity array
|
||||||
|
is_mz = occursin("MS:1000514", array_xml) || occursin("mzArray", array_xml)
|
||||||
|
|
||||||
|
# Parse external data parameters
|
||||||
|
# Get array_length (nPoints)
|
||||||
|
array_len_cv_match = match(r"IMS:1000103.*?value=\"(\d+)\"", array_xml)
|
||||||
|
array_length = 0
|
||||||
|
if array_len_cv_match !== nothing
|
||||||
|
array_length = parse(Int32, array_len_cv_match.captures[1])
|
||||||
|
end
|
||||||
|
if array_length == 0
|
||||||
|
nPoints_match = match(r"defaultArrayLength=\"(\d+)\"", spectrum_xml)
|
||||||
|
if nPoints_match !== nothing
|
||||||
|
array_length = parse(Int32, nPoints_match.captures[1])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get encoded_length
|
||||||
|
encoded_len_cv_match = match(r"IMS:1000104.*?value=\"(\d+)\"", array_xml)
|
||||||
|
encoded_length = 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+)\"", array_xml)
|
||||||
|
if encoded_len_attr_match !== nothing
|
||||||
|
encoded_length = parse(Int64, encoded_len_attr_match.captures[1])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get offset
|
||||||
|
offset_match = match(r"IMS:1000102.*?value=\"(\d+)\"", array_xml)
|
||||||
|
offset = 0
|
||||||
|
if offset_match !== nothing
|
||||||
|
offset = parse(Int64, offset_match.captures[1])
|
||||||
|
end
|
||||||
|
|
||||||
|
if array_length > 0 && offset > 0
|
||||||
|
push!(array_data, (
|
||||||
|
is_mz = is_mz,
|
||||||
|
array_length = array_length,
|
||||||
|
encoded_length = encoded_length,
|
||||||
|
offset = offset
|
||||||
|
))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Separate m/z and intensity arrays
|
||||||
|
mz_data = filter(d -> d.is_mz, array_data)
|
||||||
|
int_data = filter(d -> !d.is_mz, array_data)
|
||||||
|
|
||||||
|
if length(mz_data) != 1 || length(int_data) != 1
|
||||||
|
error("Spectrum $k: Expected exactly one m/z and one intensity array")
|
||||||
|
end
|
||||||
|
|
||||||
|
mz_info = mz_data[1]
|
||||||
|
int_info = int_data[1]
|
||||||
|
|
||||||
|
# DEBUG: Print first spectrum details
|
||||||
|
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)")
|
||||||
|
println(" Expected m/z bytes: $(mz_info.array_length * sizeof(default_mz_format))")
|
||||||
|
println(" Expected intensity bytes: $(int_info.array_length * sizeof(default_intensity_format))")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Create SpectrumAsset objects
|
||||||
|
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)
|
||||||
|
|
||||||
|
spectra_metadata[k] = SpectrumMetadata(x, y, "", spectrum_mode, mz_asset, int_asset)
|
||||||
|
end
|
||||||
|
|
||||||
|
return spectra_metadata
|
||||||
|
end
|
||||||
|
|
||||||
# --- End of content from imzML.jl ---
|
# --- End of content from imzML.jl ---
|
||||||
|
|
||||||
|
|
||||||
# --- Start of content from Imaging_Normalization.jl ---
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
#
|
#
|
||||||
# Image Slice Extraction
|
# Image Slice Extraction
|
||||||
@ -446,7 +826,8 @@ function plot_slice(msi_data::MSIData, mass::Real, tolerance::Real, output_dir::
|
|||||||
fig = Figure(size = (600, 500))
|
fig = Figure(size = (600, 500))
|
||||||
ax = CairoMakie.Axis(fig[1, 1],
|
ax = CairoMakie.Axis(fig[1, 1],
|
||||||
aspect=DataAspect(),
|
aspect=DataAspect(),
|
||||||
title=@sprintf("Slice for m/z: %.2f", mass)
|
title=@sprintf("Slice for m/z: %.2f", mass),
|
||||||
|
yreversed=true
|
||||||
)
|
)
|
||||||
hidedecorations!(ax)
|
hidedecorations!(ax)
|
||||||
|
|
||||||
|
|||||||
@ -52,15 +52,19 @@ const CONVERSION_TARGET_IMZML = "test/results/converted_mzml.imzML"
|
|||||||
|
|
||||||
# --- Test Case 3: Standard .imzML file ---
|
# --- Test Case 3: Standard .imzML file ---
|
||||||
# An existing imzML file (can be the one generated from Case 2).
|
# An existing imzML file (can be the one generated from Case 2).
|
||||||
const TEST_IMZML_FILE = CONVERSION_TARGET_IMZML # The output from case 2
|
# const TEST_IMZML_FILE = CONVERSION_TARGET_IMZML # The output from case 2
|
||||||
# const TEST_IMZML_FILE = "/home/pixel/Documents/Cinvestav_2025/Analisis/imzML_AP_SMALDI/HR2MSImouseurinarybladderS096.imzML"
|
# const TEST_IMZML_FILE = "/home/pixel/Documents/Cinvestav_2025/Analisis/imzML_AP_SMALDI/HR2MSImouseurinarybladderS096.imzML"
|
||||||
# const TEST_IMZML_FILE = "/home/pixel/Documents/Cinvestav_2025/Analisis/Imaging_paper_spray/Imaging_paper_spray.imzML"
|
# const TEST_IMZML_FILE = "/home/pixel/Documents/Cinvestav_2025/Analisis/Imaging_paper_spray/Imaging_paper_spray.imzML" #profile
|
||||||
# const TEST_IMZML_FILE = "/home/pixel/Documents/Cinvestav_2025/Analisis/Imaging prueba Roya 1/royaimg.imzML"
|
# const TEST_IMZML_FILE = "/home/pixel/Documents/Cinvestav_2025/Analisis/Imaging prueba Roya 1/royaimg.imzML"
|
||||||
|
# const TEST_IMZML_FILE = "/home/pixel/Documents/Cinvestav_2025/Analisis/salida/ltpmsi-chilli.imzML" # centroid aparently?
|
||||||
|
# const TEST_IMZML_FILE = "/home/pixel/Documents/Cinvestav_2025/Analisis/salida/Stomach_DHB_compressed.imzML" # centroid compressed
|
||||||
|
const TEST_IMZML_FILE = "/home/pixel/Documents/Cinvestav_2025/Analisis/salida/Stomach_DHB_uncompressed.imzML" # centroid
|
||||||
# The m/z value to use for creating an image slice.
|
# The m/z value to use for creating an image slice.
|
||||||
# const MZ_VALUE_FOR_SLICE = 309.06 # BF
|
# const MZ_VALUE_FOR_SLICE = 309.06 # BF
|
||||||
# const MZ_VALUE_FOR_SLICE = 896.0 # HR2MSI
|
# const MZ_VALUE_FOR_SLICE = 896.0 # HR2MSI
|
||||||
# const MZ_VALUE_FOR_SLICE = 76.03 # I PS
|
# const MZ_VALUE_FOR_SLICE = 76.03 # I PS
|
||||||
const MZ_VALUE_FOR_SLICE = 313 # ROYA
|
# const MZ_VALUE_FOR_SLICE = 313 # ROYA
|
||||||
|
const MZ_VALUE_FOR_SLICE = 100 # advanced processing
|
||||||
# const MZ_TOLERANCE = 0.1
|
# const MZ_TOLERANCE = 0.1
|
||||||
# const MZ_TOLERANCE = 1
|
# const MZ_TOLERANCE = 1
|
||||||
const MZ_TOLERANCE = 0.1
|
const MZ_TOLERANCE = 0.1
|
||||||
@ -71,8 +75,8 @@ const COORDS_TO_PLOT = (50, 50) # Example coordinates (X, Y)
|
|||||||
# --- Output Directory ---
|
# --- Output Directory ---
|
||||||
const RESULTS_DIR = "test/results"
|
const RESULTS_DIR = "test/results"
|
||||||
|
|
||||||
test1 = true
|
test1 = false
|
||||||
test2 = true
|
test2 = false
|
||||||
test3 = true
|
test3 = true
|
||||||
|
|
||||||
# ===================================================================
|
# ===================================================================
|
||||||
@ -135,6 +139,30 @@ function validate_msi_data(filepath::String)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function debug_xml_parsing(file_path::String)
|
||||||
|
println("=== DEBUG XML PARSING ===")
|
||||||
|
stream = open(file_path, "r")
|
||||||
|
|
||||||
|
# Find and print the first spectrum
|
||||||
|
while !eof(stream)
|
||||||
|
line = readline(stream)
|
||||||
|
if occursin("<spectrum", line)
|
||||||
|
println("FOUND FIRST SPECTRUM:")
|
||||||
|
spectrum_xml = line
|
||||||
|
# Read until end of spectrum
|
||||||
|
while !eof(stream) && !occursin("</spectrum>", line)
|
||||||
|
line = readline(stream)
|
||||||
|
spectrum_xml *= line
|
||||||
|
end
|
||||||
|
println("SPECTRUM XML:")
|
||||||
|
println(spectrum_xml)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
close(stream)
|
||||||
|
println("=== END DEBUG ===")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
# ===================================================================
|
# ===================================================================
|
||||||
# TEST RUNNER
|
# TEST RUNNER
|
||||||
@ -229,16 +257,24 @@ function run_test()
|
|||||||
|
|
||||||
# Also run tests for plotting spectrum and image slice
|
# Also run tests for plotting spectrum and image slice
|
||||||
if isfile(TEST_IMZML_FILE)
|
if isfile(TEST_IMZML_FILE)
|
||||||
|
debug_xml_parsing(TEST_IMZML_FILE)
|
||||||
|
msi_data = @time OpenMSIData(TEST_IMZML_FILE)
|
||||||
|
precompute_analytics(msi_data)
|
||||||
# Add spectrum plotting for imzML to match Test Case 1
|
# Add spectrum plotting for imzML to match Test Case 1
|
||||||
try
|
try
|
||||||
|
|
||||||
# Get the msi data from the imzml
|
# Get the msi data from the imzml
|
||||||
println("Plotting a sample spectrum from $TEST_IMZML_FILE...")
|
println("Plotting a sample spectrum from $TEST_IMZML_FILE...")
|
||||||
msi_data = @time OpenMSIData(TEST_IMZML_FILE)
|
|
||||||
x_coord, y_coord = COORDS_TO_PLOT
|
# Use coordinates from the first spectrum in the metadata
|
||||||
|
first_spectrum_meta = msi_data.spectra_metadata[1]
|
||||||
|
x_coord = first_spectrum_meta.x
|
||||||
|
y_coord = first_spectrum_meta.y
|
||||||
|
|
||||||
|
println("DIAGNOSTIC_READ: Reading from coordinate_map[$x_coord, $y_coord]. Value is $(msi_data.coordinate_map[x_coord, y_coord]).")
|
||||||
|
|
||||||
# Get the x y coordinate spectrum data
|
# Get the x y coordinate spectrum data
|
||||||
mz, intensity = GetSpectrum(msi_data, x_coord, y_coord)
|
mz, intensity = GetSpectrum(msi_data, Int(x_coord), Int(y_coord))
|
||||||
|
|
||||||
# Plot the data
|
# Plot the data
|
||||||
fig = Figure(size = (800, 600))
|
fig = Figure(size = (800, 600))
|
||||||
ax = Axis(fig[1, 1], xlabel="m/z", ylabel="Intensity", title="Spectrum at ($x_coord, $y_coord) from $(basename(TEST_IMZML_FILE))")
|
ax = Axis(fig[1, 1], xlabel="m/z", ylabel="Intensity", title="Spectrum at ($x_coord, $y_coord) from $(basename(TEST_IMZML_FILE))")
|
||||||
@ -276,7 +312,7 @@ function run_test()
|
|||||||
output_path = joinpath(RESULTS_DIR, "test_imzml_average_spectrum.png")
|
output_path = joinpath(RESULTS_DIR, "test_imzml_average_spectrum.png")
|
||||||
save(output_path, fig)
|
save(output_path, fig)
|
||||||
println("SUCCESS: Total spectrum plot saved to $output_path")
|
println("SUCCESS: Total spectrum plot saved to $output_path")
|
||||||
println("No spectrums tested on this try.")
|
# println("No spectrums tested on this try.")
|
||||||
catch e
|
catch e
|
||||||
println("ERROR during spectrum plotting in Test Case 3: $e")
|
println("ERROR during spectrum plotting in Test Case 3: $e")
|
||||||
end
|
end
|
||||||
@ -284,8 +320,13 @@ function run_test()
|
|||||||
# Test the plot_slice function
|
# Test the plot_slice function
|
||||||
try
|
try
|
||||||
println("Testing plot_slice function on $TEST_IMZML_FILE...")
|
println("Testing plot_slice function on $TEST_IMZML_FILE...")
|
||||||
msi_data = @time OpenMSIData(TEST_IMZML_FILE)
|
|
||||||
@time plot_slice(msi_data, MZ_VALUE_FOR_SLICE, MZ_TOLERANCE, RESULTS_DIR, stage_name="test_imzml_single_slice")
|
# Use the base peak m/z from the first spectrum for the slice
|
||||||
|
# Or a more general approach: use the global max intensity m/z
|
||||||
|
# For now, let's use the base peak m/z of the first spectrum
|
||||||
|
slice_mz_value = msi_data.spectrum_stats_df.BasePeakMZ[1]
|
||||||
|
|
||||||
|
@time plot_slice(msi_data, slice_mz_value, MZ_TOLERANCE, RESULTS_DIR, stage_name="test_imzml_single_slice")
|
||||||
# The success message is now inside plot_slice
|
# The success message is now inside plot_slice
|
||||||
catch e
|
catch e
|
||||||
println("ERROR during plot_slice test in Test Case 3: $e")
|
println("ERROR during plot_slice test in Test Case 3: $e")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user