Skip to content

Commit

Permalink
feat: cdf loader for gcms added (#290)
Browse files Browse the repository at this point in the history
* feat: cdf loader for gcms added

* feat: add cdfLoader in the app

* test: add direct link to the test files HPLC and GC/MS

* chore: specify the return value of loaders

* chore: fixing missing importt

* chore: suggestions implemented

* fix: style issues fixed

* fix: return full experiment as measurement

* chore: fix the code

Co-authored-by: Luc Patiny <luc@patiny.com>
  • Loading branch information
josoriom and lpatiny committed Oct 30, 2022
1 parent 43adbe8 commit 79f9147
Show file tree
Hide file tree
Showing 10 changed files with 155 additions and 8 deletions.
10 changes: 9 additions & 1 deletion src/app/context/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { loadMeasurements } from '../data/append';
import { getIRAutoPeakPickingEnhancer } from '../data/enhancers/irAutoPeakPickingEnhancer';
import { irMeasurementEnhancer } from '../data/enhancers/irMeasurementEnhancer';
import { biologicLoader } from '../data/loaders/biologicLoader';
import { cdfLoader } from '../data/loaders/cdfLoader';
import { jcampLoader } from '../data/loaders/jcampLoader';
import { cary500Loader } from '../data/loaders/proprietary/agilent/cary500Loader';
import { spcLoader } from '../data/loaders/spcLoader';
Expand All @@ -12,7 +13,14 @@ import { wdfLoader } from '../data/loaders/wdfLoader';
import type { AppDispatch } from './appState';

const options = {
loaders: [jcampLoader, spcLoader, wdfLoader, biologicLoader, cary500Loader],
loaders: [
jcampLoader,
spcLoader,
wdfLoader,
biologicLoader,
cary500Loader,
cdfLoader,
],
enhancers: {
ir: [
irMeasurementEnhancer,
Expand Down
8 changes: 8 additions & 0 deletions src/app/data/__tests__/getGCMSMeasurement.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { expect, test } from 'vitest';

import { getGCMSMeasurement } from './getGCMSMeasurement';

test('getGCMSMeasurement', async () => {
const result = await getGCMSMeasurement();
expect(result).toHaveLength(1);
});
20 changes: 20 additions & 0 deletions src/app/data/__tests__/getGCMSMeasurement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { join } from 'path';

import { fileCollectionFromPath } from 'filelist-utils';

import { getEmptyDataState } from '../DataState';
import { append } from '../append';
import { cdfLoader } from '../loaders/cdfLoader';

export async function getGCMSMeasurement() {
const dataState = getEmptyDataState();

const filteredFileCollection = (
await fileCollectionFromPath(join(__dirname, 'data/cdf/'))
).filter((file) => file.name === 'agilent-gcms.cdf');

const gcmsEntry = (
await append(filteredFileCollection, dataState, { loaders: [cdfLoader] })
).dataState.measurements.gclcms.entries;
return gcmsEntry;
}
4 changes: 2 additions & 2 deletions src/app/data/loaders/biologicLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { v4 } from '@lukeed/uuid';
import { convert } from 'biologic-converter';
import type { FileCollection } from 'filelist-utils';

import { getEmptyMeasurements, Loader } from '../DataState';
import { getEmptyMeasurements, Loader, Measurements } from '../DataState';
import type { MeasurementBase } from '../MeasurementBase';

export const biologicLoader: Loader = async function biologicLoader(
fileCollection: FileCollection,
) {
): Promise<Measurements> {
let measurements = getEmptyMeasurements();
const results = await convert(fileCollection);
for (let { dir, mpr, mps, mpt } of results) {
Expand Down
96 changes: 96 additions & 0 deletions src/app/data/loaders/cdfLoader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { v4 } from '@lukeed/uuid';
import type { FileCollection } from 'filelist-utils';
import { NetCDFReader } from 'netcdfjs';

import {
MeasurementKind,
Loader,
Measurements,
getEmptyMeasurements,
} from '../DataState';

export const cdfLoader: Loader = async function cdfLoader(
fileCollection: FileCollection,
): Promise<Measurements> {
const newMeasurements: Measurements = getEmptyMeasurements();
for (const file of fileCollection) {
if (file.name.match(/(?:\.cdf)$/i)) {
const reader = new NetCDFReader(await file.arrayBuffer(), { meta: true });
if (
!reader.dataVariableExists('mass_values') ||
!reader.dataVariableExists('time_values')
) {
return newMeasurements;
}

const kind: MeasurementKind = 'gclcms';

const times = reader.getDataVariable('scan_acquisition_time');
const tics = reader.getDataVariable('total_intensity');
const pointCount = reader.getDataVariable('point_count');
const massValues = reader.getDataVariable('mass_values');
const intensityValues = reader.getDataVariable('intensity_values');
addMeta(reader, reader.globalAttributes);

let index = 0;
const allMasses: Float64Array[] = [];
const allIntensities: Float64Array[] = [];
for (let size of pointCount) {
// Taken from: https://github.com/cheminfo/netcdf-gcms
const masses = new Float64Array(size);
const intensities = new Float64Array(size);
for (let j = 0; j < size; j++) {
masses[j] = massValues[index];
intensities[j] = intensityValues[index++];
}
allIntensities.push(intensities);
allMasses.push(masses);
}
newMeasurements[kind].entries.push({
id: v4(),
meta: reader.header.meta,
filename: file.name,
path: file.relativePath || '',
info: {},
title: reader.getAttribute('experiment_title'),
data: normalizeChromatogram(times, allMasses, allIntensities, tics),
});
}
}
return newMeasurements;
};

function normalizeChromatogram(time, masses, intensities, tics) {
let data: any = [];
for (let i = 0; i < time.length; i++) {
data.push({
meta: {},
info: {
time: { value: time[i], units: 's' },
tic: tics[i],
},
variables: {
x: {
symbol: 'X',
label: 'm/z',
units: '',
data: masses[i] || [],
},
y: {
symbol: 'Y',
label: 'relative intensity',
units: '',
data: intensities[i] || [],
},
},
});
}
return data;
}

function addMeta(reader, globalAttributes) {
reader.header.meta = {};
for (const item of globalAttributes) {
reader.header.meta[item.name] = item.value;
}
}
2 changes: 1 addition & 1 deletion src/app/data/loaders/jcampLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {

export const jcampLoader: Loader = async function jcampLoader(
fileCollection: FileCollection,
) {
): Promise<Measurements> {
const newMeasurements: Measurements = getEmptyMeasurements();

for (const file of fileCollection) {
Expand Down
2 changes: 1 addition & 1 deletion src/app/data/loaders/proprietary/agilent/cary500Loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Loader, Measurements, getEmptyMeasurements } from '../../../DataState';

export const cary500Loader: Loader = async function cary500Loader(
fileCollection: FileCollection,
) {
): Promise<Measurements> {
const newMeasurements: Measurements = getEmptyMeasurements();

for (const file of fileCollection) {
Expand Down
4 changes: 2 additions & 2 deletions src/app/data/loaders/spcLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { v4 } from '@lukeed/uuid';
import type { FileCollection } from 'filelist-utils';
import { parse, guessSpectraType } from 'spc-parser';

import { getEmptyMeasurements, Loader } from '../DataState';
import { getEmptyMeasurements, Loader, Measurements } from '../DataState';
import type { MeasurementBase } from '../MeasurementBase';

export const spcLoader: Loader = async function spcLoader(
fileCollection: FileCollection,
) {
): Promise<Measurements> {
let measurements = getEmptyMeasurements();
for (const file of fileCollection) {
if (file.name.match(/\.spc$/i)) {
Expand Down
2 changes: 1 addition & 1 deletion src/app/data/loaders/wdfLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { getEmptyMeasurements, Loader, Measurements } from '../DataState';

export const wdfLoader: Loader = async function wdfLoader(
fileCollection: FileCollection,
) {
): Promise<Measurements> {
const measurements: Measurements = getEmptyMeasurements();

for (const file of fileCollection) {
Expand Down
15 changes: 15 additions & 0 deletions src/app/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ createRoot(document.getElementById('root') as HTMLDivElement).render(
paddingBlock: 5,
}}
>
<a href={import.meta.env.BASE_URL}>Blank</a>
<a
href={`${
import.meta.env.BASE_URL
Expand All @@ -40,6 +41,20 @@ createRoot(document.getElementById('root') as HTMLDivElement).render(
>
UV-vis
</a>
<a
href={`${
import.meta.env.BASE_URL
}?filelist=https://zakodium-oss.github.io/analysis-dataset/hplc.json`}
>
HPLC
</a>
<a
href={`${
import.meta.env.BASE_URL
}?filelist=https://zakodium-oss.github.io/analysis-dataset/gcms.json`}
>
GC/MS
</a>
<a
href={`${
import.meta.env.BASE_URL
Expand Down

0 comments on commit 79f9147

Please sign in to comment.