bulk and batch processing for multiple imzml and multiple m/z is now available, folder organization for imzml slices in public folder, JSON tracking of processed datasets to save memory time, multiple performance optimizations and UI bug fixes, metadata viewing for all processed files, comparative view between multiple datasets
This commit is contained in:
parent
39fe2bf52c
commit
39c92ba850
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,9 +1,6 @@
|
||||
public/*
|
||||
public/css/imgOver.png
|
||||
!public/css/
|
||||
!public/masks/
|
||||
public/masks/*
|
||||
!public/masks/static.txt
|
||||
log/*
|
||||
R_original_scripts/
|
||||
test/results/
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
julia_version = "1.11.7"
|
||||
manifest_format = "2.0"
|
||||
project_hash = "d4aecbadf6a54893101b3b89b57e8ae0ab391000"
|
||||
project_hash = "b3d73d786a430f468e0f2f1b3b05da6a3addc7fe"
|
||||
|
||||
[[deps.ATK_jll]]
|
||||
deps = ["Artifacts", "Glib_jll", "JLLWrappers", "Libdl"]
|
||||
|
||||
@ -13,6 +13,7 @@ Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
|
||||
Genie = "c43c736e-a2d1-11e8-161f-af95117fbd1e"
|
||||
GenieFramework = "a59fdf5c-6bf0-4f5d-949c-a137c9e2f353"
|
||||
Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
|
||||
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
|
||||
Libz = "2ec943e9-cfe8-584d-b93d-64dcb6d567b7"
|
||||
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
|
||||
Mmap = "a63ad114-7e13-5084-954f-fe012c677804"
|
||||
|
||||
@ -22,7 +22,7 @@ https://codeberg.org/LabABI/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):
|
||||
```
|
||||
julia --threads auto --project=. start_MSI_GUI.jl
|
||||
julia--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.
|
||||
|
||||
|
||||
223
app.jl.html
223
app.jl.html
@ -17,19 +17,42 @@
|
||||
<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 image and the plot, please be patient.
|
||||
<br>To generate the contour or surface plots, you have to select the desired image first using the interface.</p>
|
||||
<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>
|
||||
<q-icon name="search" v:onclick="btnSearch=true" class="cursor-pointer" />
|
||||
</template>
|
||||
</q-input>
|
||||
<!-- Variable Manipulation -->
|
||||
<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 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" step="0.001" v-model="Nmass"
|
||||
label="Mass-to-charge ratio of interest" type="number"
|
||||
:rules="[ val => !!val || '* Required', val => val >= 0.0 || 'Need positive mass values']"></q-input>
|
||||
<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"
|
||||
@ -104,20 +127,24 @@
|
||||
</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" :disable="btnMetadataDisable"
|
||||
v-on:click="showMetadataBtn=true" label="Show Metadata"></q-btn>
|
||||
</div>
|
||||
<p>{{msg}}</p>
|
||||
|
||||
<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" :disable="btnMetadataDisable"
|
||||
v-on:click="showMetadataBtn=true" label="Show Metadata"></q-btn>-->
|
||||
<q-btn class="q-ma-sm btn-style" 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">
|
||||
@ -177,13 +204,13 @@
|
||||
|
||||
<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:onclick="btnSearchMzml=true" class="cursor-pointer" />
|
||||
<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:onclick="btnSearchSync=true" class="cursor-pointer" />
|
||||
<q-icon name="search" v-on:click="btnSearchSync=true" class="cursor-pointer" />
|
||||
</template>
|
||||
</q-input>
|
||||
|
||||
@ -205,9 +232,10 @@
|
||||
<q-tab-panels v-model="selectedTab">
|
||||
<q-tab-panel name="tab0">
|
||||
<!-- Content for Tab 0 -->
|
||||
<!-- Btn image changer -->
|
||||
<h6>Image visualizer</h6>
|
||||
<div>
|
||||
<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;"></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>
|
||||
@ -221,14 +249,15 @@
|
||||
<q-img id="colorbar-normal" class="q-ma-none q-pa-none" :src="colorbar"></q-img>
|
||||
</div>
|
||||
</div>
|
||||
<p>{{msgimg}}</p>
|
||||
<p v-html="msgimg"></p>
|
||||
</q-tab-panel>
|
||||
|
||||
<q-tab-panel name="tab1">
|
||||
<!-- Content for Tab 1 -->
|
||||
<!-- Triq Btn image changer -->
|
||||
<h6>TrIQ visualizer</h6>
|
||||
<div>
|
||||
<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;"></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>
|
||||
@ -242,11 +271,38 @@
|
||||
<q-img id="colorbar-triq" class="q-ma-none q-pa-none" :src="colorbarT"></q-img>
|
||||
</div>
|
||||
</div>
|
||||
<p>{{msgtriq}}</p>
|
||||
<p v-html="msgtriq"></p>
|
||||
</q-tab-panel>
|
||||
|
||||
<q-tab-panel name="tab2">
|
||||
<!-- Content for Tab 2 -->
|
||||
<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;"></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>
|
||||
|
||||
@ -281,8 +337,8 @@
|
||||
|
||||
<q-dialog v-model="CompareDialog" full-width>
|
||||
<q-card>
|
||||
<q-card-section class="row">
|
||||
<div class="text-h6 q-ma-sm">Compare two diferent images or plots</div>
|
||||
<q-card-section class="row items-center">
|
||||
<div class="text-h6 q-ma-sm">Compare two different images or plots</div>
|
||||
<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>
|
||||
@ -294,46 +350,50 @@
|
||||
<q-card-section class="q-pt-none col-12">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<st-tabs id="tabHeaderCompareLeft" :ids="tabIDs" :labels="tabLabels" v-model="selectedTab"></st-tabs>
|
||||
<q-tab-panels v-model="selectedTab">
|
||||
<div class="row items-center">
|
||||
<q-select v-model="selected_folder_compare_left" :options="image_available_folders" label="Select Left Dataset" class="q-ma-sm" style="min-width: 200px;"></q-select>
|
||||
<q-space></q-space>
|
||||
<st-tabs id="tabHeaderCompareLeft" :ids="CompTabIDsLeft" :labels="CompTabLabelsLeft" v-model="CompSelectedTabLeft"></st-tabs>
|
||||
</div>
|
||||
<q-tab-panels v-model="CompSelectedTabLeft">
|
||||
<q-tab-panel name="tab0">
|
||||
<!-- Content for Tab 0 -->
|
||||
<!-- Btn image changer -->
|
||||
<div>
|
||||
<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>
|
||||
<q-btn icon="arrow_back" class="q-my-sm btn-style" v-on:click="imgMinusCompLeft=true"></q-btn>
|
||||
<q-btn icon="arrow_forward" class="q-my-sm on-right btn-style" v-on:click="imgPlusCompLeft=true"></q-btn>
|
||||
</div>
|
||||
<!-- Image manager -->
|
||||
<div id="image-container-compare-left-normal" class="row st-col col-12">
|
||||
<div class="col-10 q-pa-none q-ma-none ">
|
||||
<plotly id="plotImgCompareLeft" :data="plotdataImg" :layout="plotlayoutImg" class="q-pa-none q-ma-none">
|
||||
<plotly id="plotImgCompareLeft" :data="plotdataImgCompLeft" :layout="plotlayoutImgCompLeft" class="q-pa-none q-ma-none">
|
||||
</plotly>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<q-img id="colorbarCompareLeft" class="q-ma-none q-pa-none" :src="colorbar"></q-img>
|
||||
<q-img id="colorbarCompareLeft" class="q-ma-none q-pa-none" :src="colorbarCompLeft"></q-img>
|
||||
</div>
|
||||
</div>
|
||||
<p>{{msgimg}}</p>
|
||||
<p v-html="msgimgCompLeft"></p>
|
||||
</q-tab-panel>
|
||||
|
||||
<q-tab-panel name="tab1">
|
||||
<!-- Content for Tab 1 -->
|
||||
<!-- Triq Btn image changer -->
|
||||
<div>
|
||||
<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>
|
||||
<q-btn icon="arrow_back" class="q-my-sm btn-style" v-on:click="imgMinusTCompLeft=true"></q-btn>
|
||||
<q-btn icon="arrow_forward" class="q-my-sm on-right btn-style" v-on:click="imgPlusTCompLeft=true"></q-btn>
|
||||
</div>
|
||||
<!-- Triq Image manager -->
|
||||
<div id="image-container-compare-left-triq" class="row st-col col-12">
|
||||
<div class="col-10 q-pa-none q-ma-none ">
|
||||
<plotly id="plotImgTCompareLeft" :data="plotdataImgT" :layout="plotlayoutImgT" class="q-pa-none q-ma-none">
|
||||
<plotly id="plotImgTCompareLeft" :data="plotdataImgTCompLeft" :layout="plotlayoutImgTCompLeft" class="q-pa-none q-ma-none">
|
||||
</plotly>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<q-img id="colorbarTCompareLeft" class="q-ma-none q-pa-none" :src="colorbarT"></q-img>
|
||||
<q-img id="colorbarTCompareLeft" class="q-ma-none q-pa-none" :src="colorbarTCompLeft"></q-img>
|
||||
</div>
|
||||
</div>
|
||||
<p>{{msgtriq}}</p>
|
||||
<p v-html="msgtriqCompLeft"></p>
|
||||
</q-tab-panel>
|
||||
<q-tab-panel name="tab2">
|
||||
<!-- Content for Tab 2 -->
|
||||
@ -350,51 +410,54 @@
|
||||
</q-tab-panels>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<st-tabs id="tabHeaderCompareRight" :ids="CompTabIDs" :labels="CompTabLabels" v-model="CompSelectedTab" outside-arrows
|
||||
mobile-arrows></st-tabs>
|
||||
<q-tab-panels v-model="CompSelectedTab">
|
||||
<div class="row items-center">
|
||||
<q-select v-model="selected_folder_compare_right" :options="image_available_folders" label="Select Right Dataset" class="q-ma-sm" style="min-width: 200px;"></q-select>
|
||||
<q-space></q-space>
|
||||
<st-tabs id="tabHeaderCompareRight" :ids="CompTabIDsRight" :labels="CompTabLabelsRight" v-model="CompSelectedTabRight"></st-tabs>
|
||||
</div>
|
||||
<q-tab-panels v-model="CompSelectedTabRight">
|
||||
<q-tab-panel name="tab0">
|
||||
<!-- Content for Tab 0 -->
|
||||
<!-- Btn image changer -->
|
||||
<div>
|
||||
<q-btn icon="arrow_back" class="q-my-sm btn-style" v-on:click="imgMinusComp=true"></q-btn>
|
||||
<q-btn icon="arrow_back" class="q-my-sm btn-style" v-on:click="imgMinusCompRight=true"></q-btn>
|
||||
<q-btn icon="arrow_forward" class="q-my-sm on-right btn-style"
|
||||
v-on:click="imgPlusComp=true"></q-btn>
|
||||
v-on:click="imgPlusCompRight=true"></q-btn>
|
||||
</div>
|
||||
<!-- Image manager -->
|
||||
<div id="image-container-compare-right-normal" class="row st-col col-12">
|
||||
<div class="col-10 q-pa-none q-ma-none ">
|
||||
<plotly id="plotImgCompareRight" :data="plotdataImgComp" :layout="plotlayoutImgComp"
|
||||
<plotly id="plotImgCompareRight" :data="plotdataImgCompRight" :layout="plotlayoutImgCompRight"
|
||||
class="q-pa-none q-ma-none">
|
||||
</plotly>
|
||||
</div>
|
||||
<div class="col-2 q-pa-none q-ma-none ">
|
||||
<q-img id="colorbarComp" class="q-ma-none q-pa-none" :src="colorbarComp"></q-img>
|
||||
<q-img id="colorbarCompRight" class="q-ma-none q-pa-none" :src="colorbarCompRight"></q-img>
|
||||
</div>
|
||||
</div>
|
||||
<p>{{msgimgComp}}</p>
|
||||
<p v-html="msgimgCompRight"></p>
|
||||
</q-tab-panel>
|
||||
|
||||
<q-tab-panel name="tab1">
|
||||
<!-- Content for Tab 1 -->
|
||||
<!-- Triq Btn image changer -->
|
||||
<div>
|
||||
<q-btn icon="arrow_back" class="q-my-sm btn-style" v-on:click="imgMinusTComp=true"></q-btn>
|
||||
<q-btn icon="arrow_back" class="q-my-sm btn-style" v-on:click="imgMinusTCompRight=true"></q-btn>
|
||||
<q-btn icon="arrow_forward" class="q-my-sm on-right btn-style"
|
||||
v-on:click="imgPlusTComp=true"></q-btn>
|
||||
v-on:click="imgPlusTCompRight=true"></q-btn>
|
||||
</div>
|
||||
<!-- Triq Image manager -->
|
||||
<div id="image-container-compare-right-triq" class="row st-col col-12">
|
||||
<div class="col-10 q-pa-none q-ma-none ">
|
||||
<plotly id="plotImgTCompareRight" :data="plotdataImgTComp" :layout="plotlayoutImgTComp"
|
||||
<plotly id="plotImgTCompareRight" :data="plotdataImgTCompRight" :layout="plotlayoutImgTCompRight"
|
||||
class="q-pa-none q-ma-none">
|
||||
</plotly>
|
||||
</div>
|
||||
<div class="col-2 q-pa-none q-ma-none ">
|
||||
<q-img id="colorbarTComp" class="q-ma-none q-pa-none" :src="colorbarTComp"></q-img>
|
||||
<q-img id="colorbarTCompRight" class="q-ma-none q-pa-none" :src="colorbarTCompRight"></q-img>
|
||||
</div>
|
||||
</div>
|
||||
<p>{{msgtriqComp}}</p>
|
||||
<p v-html="msgtriqCompRight"></p>
|
||||
</q-tab-panel>
|
||||
<q-tab-panel name="tab2">
|
||||
<!-- Content for Tab 2 -->
|
||||
@ -419,23 +482,53 @@
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
|
||||
<q-dialog v-model="showBatchSummary">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<div class="text-h6">Batch Process Summary</div>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section class="q-pt-none">
|
||||
<pre>{{ batch_summary }}</pre>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-actions align="right">
|
||||
<q-btn flat label="Ok" style="color:#009f90" v-close-popup />
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
|
||||
<q-dialog v-model="showMetadataDialog" full-width full-height>
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<q-card-section class="row items-center q-pb-none">
|
||||
<div class="text-h6">Dataset Summary</div>
|
||||
<q-space />
|
||||
<q-select
|
||||
v-model="selected_folder_metadata"
|
||||
:options="available_folders"
|
||||
label="Select Dataset"
|
||||
class="q-ma-sm"
|
||||
style="min-width: 250px;"
|
||||
standout="custom-standout"
|
||||
></q-select>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section class="q-pt-none">
|
||||
<q-list bordered separator>
|
||||
<q-list bordered separator v-if="metadata_rows.length > 0">
|
||||
<q-item v-for="row in metadata_rows" :key="row.parameter">
|
||||
<q-item-section>
|
||||
<q-item-label>{{ row.parameter }}</q-item-label>
|
||||
<q-item-label class="text-body1">{{ row.parameter }}</q-item-label>
|
||||
</q-item-section>
|
||||
<q-item-section side>
|
||||
<q-item-label caption>{{ row.value }}</q-item-label>
|
||||
<q-item-label caption class="text-body1">{{ row.value }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
<div v-else class="text-center q-pa-md text-grey-7">
|
||||
<q-icon name="info" size="3em" />
|
||||
<p class="q-mt-md">No metadata found in registry for this dataset.</p>
|
||||
<p class="text-caption">You may need to process the file first.</p>
|
||||
</div>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-actions align="right">
|
||||
|
||||
@ -452,10 +452,11 @@ end
|
||||
# returns the layout and data for the surface plotly plot
|
||||
# this function loads the spectra data and makes a mean to display
|
||||
# its values in the spectrum plot
|
||||
function meanSpectrumPlot(data::MSIData)
|
||||
function meanSpectrumPlot(data::MSIData, dataset_name::String="")
|
||||
title_text = isempty(dataset_name) ? "Average Spectrum Plot" : "Average Spectrum for: $dataset_name"
|
||||
layout = PlotlyBase.Layout(
|
||||
title=PlotlyBase.attr(
|
||||
text="Average Spectrum Plot",
|
||||
text=title_text,
|
||||
font=PlotlyBase.attr(
|
||||
family="Roboto, Lato, sans-serif",
|
||||
size=18,
|
||||
@ -480,9 +481,9 @@ function meanSpectrumPlot(data::MSIData)
|
||||
|
||||
if isempty(xSpectraMz)
|
||||
@warn "Average spectrum is empty."
|
||||
trace = PlotlyBase.stem(x=Float64[], y=Float64[])
|
||||
trace = PlotlyBase.scatter(x=Float64[], y=Float64[])
|
||||
else
|
||||
trace = PlotlyBase.stem(x=xSpectraMz, y=ySpectraMz, marker=attr(size=1, color="blue", opacity=0.5), name="Average", hoverinfo="x",hovertemplate="<b>m/z</b>: %{x:.4f}<extra></extra>")
|
||||
trace = PlotlyBase.scatter(x=xSpectraMz, y=ySpectraMz, marker=attr(size=1, color="blue", opacity=0.5), name="Average", hoverinfo="x",hovertemplate="<b>m/z</b>: %{x:.4f}<extra></extra>")
|
||||
end
|
||||
|
||||
|
||||
@ -491,7 +492,7 @@ function meanSpectrumPlot(data::MSIData)
|
||||
return plotdata, plotlayout, xSpectraMz, ySpectraMz
|
||||
end
|
||||
|
||||
function xySpectrumPlot(data::MSIData, xCoord::Int, yCoord::Int, imgWidth::Int, imgHeight::Int)
|
||||
function xySpectrumPlot(data::MSIData, xCoord::Int, yCoord::Int, imgWidth::Int, imgHeight::Int, dataset_name::String="")
|
||||
local mz::AbstractVector, intensity::AbstractVector
|
||||
local plot_title::String
|
||||
|
||||
@ -502,24 +503,24 @@ function xySpectrumPlot(data::MSIData, xCoord::Int, yCoord::Int, imgWidth::Int,
|
||||
x = clamp(xCoord, 1, imgWidth)
|
||||
y = clamp(yCoord, 1, imgHeight)
|
||||
|
||||
# mz, intensity = GetSpectrum(data, Int(x), Int(y))
|
||||
process_spectrum(data, Int(x), Int(y)) do recieved_mz, recieved_intensity
|
||||
mz = recieved_mz
|
||||
intensity = recieved_intensity
|
||||
end
|
||||
plot_title = "Spectrum at ($x, $y)"
|
||||
base_title = "Spectrum at ($x, $y)"
|
||||
else
|
||||
# For non-imaging data, treat xCoord as the spectrum index
|
||||
index = clamp(xCoord, 1, length(data.spectra_metadata))
|
||||
|
||||
# mz, intensity = GetSpectrum(data, index)
|
||||
process_spectrum(data, index) do recieved_mz, recieved_intensity
|
||||
mz = recieved_mz
|
||||
intensity = recieved_intensity
|
||||
end
|
||||
plot_title = "Spectrum #$index"
|
||||
base_title = "Spectrum #$index"
|
||||
end
|
||||
|
||||
plot_title = isempty(dataset_name) ? base_title : "$base_title for: $dataset_name"
|
||||
|
||||
layout = PlotlyBase.Layout(
|
||||
title=PlotlyBase.attr(
|
||||
text=plot_title,
|
||||
@ -545,7 +546,7 @@ function xySpectrumPlot(data::MSIData, xCoord::Int, yCoord::Int, imgWidth::Int,
|
||||
# Downsample for plotting performance
|
||||
mz_down, int_down = MSI_src.downsample_spectrum(mz, intensity)
|
||||
|
||||
trace = 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>")
|
||||
trace = PlotlyBase.scatter(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>")
|
||||
|
||||
plotdata = [trace]
|
||||
plotlayout = layout
|
||||
@ -553,10 +554,11 @@ function xySpectrumPlot(data::MSIData, xCoord::Int, yCoord::Int, imgWidth::Int,
|
||||
return plotdata, plotlayout, mz, intensity
|
||||
end
|
||||
|
||||
function sumSpectrumPlot(data::MSIData)
|
||||
function sumSpectrumPlot(data::MSIData, dataset_name::String="")
|
||||
title_text = isempty(dataset_name) ? "Total Spectrum Plot" : "Total Spectrum for: $dataset_name"
|
||||
layout = PlotlyBase.Layout(
|
||||
title=PlotlyBase.attr(
|
||||
text="Total Spectrum Plot",
|
||||
text=title_text,
|
||||
font=PlotlyBase.attr(
|
||||
family="Roboto, Lato, sans-serif",
|
||||
size=18,
|
||||
@ -581,12 +583,47 @@ function sumSpectrumPlot(data::MSIData)
|
||||
|
||||
if isempty(xSpectraMz)
|
||||
@warn "Total spectrum is empty."
|
||||
trace = PlotlyBase.stem(x=Float64[], y=Float64[])
|
||||
trace = PlotlyBase.scatter(x=Float64[], y=Float64[])
|
||||
else
|
||||
trace = PlotlyBase.stem(x=xSpectraMz, y=ySpectraMz, marker=attr(size=1, color="blue", opacity=0.5), name="Total", hoverinfo="x",hovertemplate="<b>m/z</b>: %{x:.4f}<extra></extra>")
|
||||
trace = PlotlyBase.scatter(x=xSpectraMz, y=ySpectraMz, marker=attr(size=1, color="blue", opacity=0.5), name="Total", hoverinfo="x",hovertemplate="<b>m/z</b>: %{x:.4f}<extra></extra>")
|
||||
end
|
||||
|
||||
plotdata = [trace]
|
||||
plotlayout = layout
|
||||
return plotdata, plotlayout, xSpectraMz, ySpectraMz
|
||||
end
|
||||
|
||||
function warmup_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
|
||||
@ -82,7 +82,7 @@
|
||||
}
|
||||
|
||||
#intDivStyle-left .q-tab-panels {
|
||||
height: 670px; /* Set this to accommodate your tallest content */
|
||||
height: 700px; /* Set this to accommodate your tallest content */
|
||||
}
|
||||
|
||||
#intDivStyle-left .q-tab-panel {
|
||||
|
||||
@ -497,7 +497,7 @@ function ConvertMzmlToImzml(source_file::String, target_ibd_file::String, timing
|
||||
open(target_ibd_file, "w") do ibd_stream
|
||||
write(ibd_stream, zeros(UInt8, 16)) # UUID placeholder
|
||||
end
|
||||
return BinaryMetadata[], Tuple{Int, Int}[], (0, 0)
|
||||
return BinaryMetadata[], Tuple{Int, Int}[], (0, 0), UNKNOWN
|
||||
end
|
||||
|
||||
width = maximum(timing_matrix[:, 1])
|
||||
@ -505,6 +505,11 @@ function ConvertMzmlToImzml(source_file::String, target_ibd_file::String, timing
|
||||
|
||||
msi_data = OpenMSIData(source_file)
|
||||
|
||||
source_mode = UNKNOWN
|
||||
if !isempty(msi_data.spectra_metadata)
|
||||
source_mode = msi_data.spectra_metadata[1].mode
|
||||
end
|
||||
|
||||
scan_time_deltas = zeros(Int64, size(scans, 1))
|
||||
if size(scans, 1) > 1
|
||||
for i in 1:(size(scans, 1) - 1)
|
||||
@ -553,7 +558,7 @@ function ConvertMzmlToImzml(source_file::String, target_ibd_file::String, timing
|
||||
# Write m/z array
|
||||
mz_offset = position(ibd_stream)
|
||||
for val in mz
|
||||
write(ibd_stream, htol(Float64(val)))
|
||||
write(ibd_stream, htol(Float32(val)))
|
||||
end
|
||||
mz_length = position(ibd_stream) - mz_offset
|
||||
|
||||
@ -569,7 +574,7 @@ function ConvertMzmlToImzml(source_file::String, target_ibd_file::String, timing
|
||||
end
|
||||
|
||||
@info "Found and processed $empty_pixel_count empty pixels out of $(size(timing_matrix, 1)) total."
|
||||
return binary_meta_vec, coords_vec, (width, height)
|
||||
return binary_meta_vec, coords_vec, (width, height), source_mode
|
||||
end
|
||||
|
||||
"""
|
||||
@ -589,7 +594,7 @@ experiment and data format.
|
||||
# Returns
|
||||
- `true` on success, `false` on failure.
|
||||
"""
|
||||
function ExportImzml(target_file::String, binary_meta::Vector{BinaryMetadata}, coords::Vector{Tuple{Int, Int}}, dims::Tuple{Int, Int})
|
||||
function ExportImzml(target_file::String, binary_meta::Vector{BinaryMetadata}, coords::Vector{Tuple{Int, Int}}, dims::Tuple{Int, Int}, mode::SpectrumMode)
|
||||
ibd_file = replace(target_file, r"\.imzML$"i => ".ibd")
|
||||
|
||||
if isempty(binary_meta)
|
||||
@ -604,8 +609,7 @@ function ExportImzml(target_file::String, binary_meta::Vector{BinaryMetadata}, c
|
||||
open(target_file, "w") do imzml_stream
|
||||
# XML Header
|
||||
write(imzml_stream, """<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<indexedmzML xmlns="http://psi.hupo.org/ms/mzml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://psi.hupo.org/ms/mzml http://psidev.info/files/ms/mzML/xsd/mzML1.1.0_idx.xsd">
|
||||
<mzML version="1.1" id="$(splitext(basename(target_file))[1])">
|
||||
<mzML xmlns="http://psi.hupo.org/ms/mzml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://psi.hupo.org/ms/mzml http://psidev.info/files/ms/mzML/xsd/mzML1.1.0.xsd" version="1.1" id="$(splitext(basename(target_file))[1])">
|
||||
""")
|
||||
|
||||
# CV List, File Description, etc. (static parts)
|
||||
@ -627,13 +631,11 @@ function ExportImzml(target_file::String, binary_meta::Vector{BinaryMetadata}, c
|
||||
<referenceableParamGroup id="mzArray">
|
||||
<cvParam cvRef="MS" accession="MS:1000576" name="no compression"/>
|
||||
<cvParam cvRef="MS" accession="MS:1000514" name="m/z array" unitCvRef="MS" unitAccession="MS:1000040" unitName="m/z"/>
|
||||
<cvParam cvRef="IMS" accession="IMS:1000101" name="external data" value="true"/>
|
||||
<cvParam cvRef="MS" accession="MS:1000523" name="64-bit float"/>
|
||||
<cvParam cvRef="MS" accession="MS:1000521" name="32-bit float"/>
|
||||
</referenceableParamGroup>
|
||||
<referenceableParamGroup id="intensityArray">
|
||||
<cvParam cvRef="MS" accession="MS:1000576" name="no compression"/>
|
||||
<cvParam cvRef="MS" accession="MS:1000515" name="intensity array" unitCvRef="MS" unitAccession="MS:1000131" unitName="number of counts"/>
|
||||
<cvParam cvRef="IMS" accession="IMS:1000101" name="external data" value="true"/>
|
||||
<cvParam cvRef="MS" accession="MS:1000521" name="32-bit float"/>
|
||||
</referenceableParamGroup>
|
||||
</referenceableParamGroupList>
|
||||
@ -699,13 +701,20 @@ function ExportImzml(target_file::String, binary_meta::Vector{BinaryMetadata}, c
|
||||
push!(spectrum_offsets, spectrum_start)
|
||||
|
||||
# Calculate number of points from byte length
|
||||
mz_points = meta.mz_length ÷ sizeof(Float64)
|
||||
mz_points = meta.mz_length ÷ sizeof(Float32)
|
||||
int_points = meta.int_length ÷ sizeof(Float32)
|
||||
|
||||
write(imzml_stream, """ <spectrum id="Scan=$(i)" defaultArrayLength="$(mz_points)" index="$(i-1)">
|
||||
<cvParam cvRef="MS" accession="MS:1000511" name="ms level" value="1"/>
|
||||
<cvParam cvRef="MS" accession="MS:1000128" name="profile spectrum"/>
|
||||
<scanList count="1">
|
||||
""")
|
||||
if mode == CENTROID
|
||||
write(imzml_stream, """ <cvParam cvRef="MS" accession="MS:1000127" name="centroid spectrum"/>
|
||||
""")
|
||||
else
|
||||
write(imzml_stream, """ <cvParam cvRef="MS" accession="MS:1000128" name="profile spectrum"/>
|
||||
""")
|
||||
end
|
||||
write(imzml_stream, """ <scanList count="1">
|
||||
<scan instrumentConfigurationRef="instrument1">
|
||||
<cvParam cvRef="IMS" accession="IMS:1000050" name="position x" value="$(x)"/>
|
||||
<cvParam cvRef="IMS" accession="IMS:1000051" name="position y" value="$(y)"/>
|
||||
@ -714,16 +723,14 @@ function ExportImzml(target_file::String, binary_meta::Vector{BinaryMetadata}, c
|
||||
<binaryDataArrayList count="2">
|
||||
<binaryDataArray encodedLength="0">
|
||||
<referenceableParamGroupRef ref="mzArray"/>
|
||||
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="$(meta.mz_offset)"/>
|
||||
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="$(mz_points)"/>
|
||||
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="$(meta.mz_length)"/>
|
||||
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="$(meta.mz_offset)"/>
|
||||
<binary/>
|
||||
</binaryDataArray>
|
||||
<binaryDataArray encodedLength="0">
|
||||
<referenceableParamGroupRef ref="intensityArray"/>
|
||||
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="$(meta.int_offset)"/>
|
||||
<cvParam cvRef="IMS" accession="IMS:1000103" name="external array length" value="$(int_points)"/>
|
||||
<cvParam cvRef="IMS" accession="IMS:1000104" name="external encoded length" value="$(meta.int_length)"/>
|
||||
<cvParam cvRef="IMS" accession="IMS:1000102" name="external offset" value="$(meta.int_offset)"/>
|
||||
<binary/>
|
||||
</binaryDataArray>
|
||||
</binaryDataArrayList>
|
||||
@ -734,19 +741,6 @@ function ExportImzml(target_file::String, binary_meta::Vector{BinaryMetadata}, c
|
||||
write(imzml_stream, """ </spectrumList>
|
||||
</run>
|
||||
</mzML>
|
||||
""")
|
||||
# Index List
|
||||
index_list_start = position(imzml_stream)
|
||||
write(imzml_stream, """ <indexList count="1">
|
||||
<index name="spectrum">
|
||||
""")
|
||||
for (i, offset) in enumerate(spectrum_offsets)
|
||||
write(imzml_stream, " <offset idRef=\"Scan=$(i)\">$offset</offset>\n")
|
||||
end
|
||||
write(imzml_stream, """ </index>
|
||||
</indexList>
|
||||
<indexListOffset>$index_list_start</indexListOffset>
|
||||
</indexedmzML>
|
||||
""")
|
||||
end
|
||||
|
||||
@ -784,13 +778,13 @@ function ImportMzmlFile(source_file::String, sync_file::String, target_file::Str
|
||||
|
||||
println("Step 3: Converting spectra and writing .ibd file...")
|
||||
ibd_file = replace(target_file, r"\.imzML$"i => ".ibd")
|
||||
binary_meta, coords, (width, height) = ConvertMzmlToImzml(source_file, ibd_file, timing_matrix, scans)
|
||||
binary_meta, coords, (width, height), source_mode = ConvertMzmlToImzml(source_file, ibd_file, timing_matrix, scans)
|
||||
|
||||
# Flip image vertically to match R script output
|
||||
flipped_coords = [(x, height - y + 1) for (x, y) in coords]
|
||||
|
||||
println("Step 4: Exporting .imzML metadata file...")
|
||||
success = ExportImzml(target_file, binary_meta, flipped_coords, (width, height))
|
||||
success = ExportImzml(target_file, binary_meta, flipped_coords, (width, height), source_mode)
|
||||
|
||||
if success
|
||||
println("Conversion successful: $target_file")
|
||||
|
||||
78
src/imzML.jl
78
src/imzML.jl
@ -793,6 +793,84 @@ function get_mz_slice(data::MSIData, mass::Real, tolerance::Real)
|
||||
end
|
||||
|
||||
|
||||
"""
|
||||
get_multiple_mz_slices(data::MSIData, masses::AbstractVector{<:Real}, tolerance::Real)
|
||||
|
||||
Extracts multiple image slices for a given list of m/z values in a single pass.
|
||||
This is a highly performant function that iterates through the full dataset only once.
|
||||
|
||||
# Returns
|
||||
- A `Dict{Real, Matrix{Float64}}` mapping each mass to its intensity slice matrix.
|
||||
"""
|
||||
function get_multiple_mz_slices(data::MSIData, masses::AbstractVector{<:Real}, tolerance::Real)
|
||||
width, height = data.image_dims
|
||||
|
||||
# 1. Initialize a dictionary to hold the output slice matrices
|
||||
slice_dict = Dict{Real, Matrix{Float64}}()
|
||||
for mass in masses
|
||||
slice_dict[mass] = zeros(Float64, height, width)
|
||||
end
|
||||
|
||||
# 2. Ensure analytics are computed for filtering.
|
||||
if data.spectrum_stats_df === nothing || !hasproperty(data.spectrum_stats_df, :MinMZ)
|
||||
println("Per-spectrum metadata not found. Running one-time analytics computation...")
|
||||
precompute_analytics(data)
|
||||
end
|
||||
|
||||
println("Filtering candidate spectra for $(length(masses)) m/z values...")
|
||||
stats_df = data.spectrum_stats_df
|
||||
candidate_indices = Set{Int}()
|
||||
|
||||
# 3. Find all spectra that could contain *any* of the requested masses.
|
||||
for mass in masses
|
||||
target_min = mass - tolerance
|
||||
target_max = mass + tolerance
|
||||
for i in 1:length(data.spectra_metadata)
|
||||
# If already a candidate, no need to check again
|
||||
if i in candidate_indices
|
||||
continue
|
||||
end
|
||||
spec_min_mz = stats_df.MinMZ[i]
|
||||
spec_max_mz = stats_df.MaxMZ[i]
|
||||
if target_max >= spec_min_mz && target_min <= spec_max_mz
|
||||
push!(candidate_indices, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
println("Found $(length(candidate_indices)) total candidate spectra.")
|
||||
|
||||
# 4. Iterate through the data a single time using the optimized iterator.
|
||||
_iterate_spectra_fast(data) do idx, mz_array, intensity_array
|
||||
# Process only the spectra that are candidates
|
||||
if idx in candidate_indices
|
||||
meta = data.spectra_metadata[idx]
|
||||
# For this single spectrum, check all masses of interest
|
||||
for mass in masses
|
||||
# Check if this spectrum's range actually covers the current mass
|
||||
# This is a finer-grained check than the initial filtering
|
||||
if !isempty(mz_array) && (mass + tolerance) >= first(mz_array) && (mass - tolerance) <= last(mz_array)
|
||||
intensity = find_mass(mz_array, intensity_array, mass, tolerance)
|
||||
if intensity > 0.0
|
||||
if 1 <= meta.x <= width && 1 <= meta.y <= height
|
||||
slice_dict[mass][meta.y, meta.x] = intensity
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# 5. Clean up and return
|
||||
for mass in masses
|
||||
replace!(slice_dict[mass], NaN => 0.0)
|
||||
end
|
||||
|
||||
println("Finished generating $(length(masses)) slices in a single pass.")
|
||||
return slice_dict
|
||||
end
|
||||
|
||||
|
||||
"""
|
||||
plot_slice(msi_data::MSIData, mass::Float64, tolerance::Float64, output_dir::String; stage_name="slice", bins=256)
|
||||
|
||||
|
||||
32
src/mzML.jl
32
src/mzML.jl
@ -103,9 +103,37 @@ the m/z and intensity array metadata.
|
||||
function parse_spectrum_metadata(stream::IO, offset::Int64)
|
||||
seek(stream, offset)
|
||||
|
||||
id_match = find_tag(stream, r"<spectrum\s+index=\"\d+\"\s+id=\"([^\"]+)" )
|
||||
# Read the whole spectrum block to parse mode
|
||||
spectrum_start_pos = position(stream)
|
||||
line = ""
|
||||
spectrum_buffer = IOBuffer()
|
||||
while !eof(stream)
|
||||
line = readline(stream)
|
||||
write(spectrum_buffer, line)
|
||||
if occursin("</spectrum>", line)
|
||||
break
|
||||
end
|
||||
end
|
||||
spectrum_xml = String(take!(spectrum_buffer))
|
||||
seek(stream, spectrum_start_pos) # Reset for other parsing
|
||||
|
||||
id_match = match(r"<spectrum\s+index=\"\d+\"\s+id=\"([^\"]+)", spectrum_xml)
|
||||
id = id_match === nothing ? "" : id_match.captures[1]
|
||||
|
||||
# Determine mode from the XML block
|
||||
mode = UNKNOWN
|
||||
if occursin("MS:1000127", spectrum_xml)
|
||||
mode = CENTROID
|
||||
elseif occursin("MS:1000128", spectrum_xml)
|
||||
mode = PROFILE
|
||||
end
|
||||
|
||||
# Find where the binary data list starts to parse assets
|
||||
binary_list_match = findfirst("<binaryDataArrayList", spectrum_xml)
|
||||
if binary_list_match !== nothing
|
||||
seek(stream, spectrum_start_pos + binary_list_match.start - 1)
|
||||
end
|
||||
|
||||
asset1 = get_spectrum_asset_metadata(stream)
|
||||
asset2 = get_spectrum_asset_metadata(stream)
|
||||
|
||||
@ -118,7 +146,7 @@ function parse_spectrum_metadata(stream::IO, offset::Int64)
|
||||
|
||||
# Create the new unified metadata object
|
||||
# For mzML, x and y coordinates are not applicable, so we use 0.
|
||||
return SpectrumMetadata(Int32(0), Int32(0), id, UNKNOWN, mz_asset, int_asset)
|
||||
return SpectrumMetadata(Int32(0), Int32(0), id, mode, mz_asset, int_asset)
|
||||
end
|
||||
|
||||
"""
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user