Skip to content

Commit f13f8d1

Browse files
committed
refactor: Enhance LCMS data handling by adding xUnit extraction and formatting functions; update ViewerLineRect and LineFocus components to utilize new utilities
1 parent 58ad6ab commit f13f8d1

4 files changed

Lines changed: 67 additions & 20 deletions

File tree

src/components/d3_line_rect/index.js

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ import PeakGroup from '../cmd_bar/08_peak_group';
3636
import Threshold from '../cmd_bar/r03_threshold';
3737
import Integration from '../cmd_bar/04_integration';
3838
import Peak from '../cmd_bar/03_peak';
39-
import { getLcMsInfo } from '../../helpers/extractEntityLCMS';
39+
import {
40+
getLcMsInfo, getLcMsXUnit, formatLcmsTimeXLabel,
41+
} from '../../helpers/extractEntityLCMS';
4042

4143
const W = Math.round(window.innerWidth * 0.90 * 9 / 12); // ROI
4244
const H = Math.round(window.innerHeight * 0.90 * 0.8 / 3); // ROI
@@ -259,7 +261,7 @@ class ViewerLineRect extends React.Component {
259261

260262
componentDidMount() {
261263
const {
262-
curveSt, feature, ticEntities, hplcMsSt,
264+
curveSt, feature, ticEntities, uvvisEntities, hplcMsSt,
263265
tTrEndPts, layoutSt,
264266
isUiAddIntgSt, isUiNoBrushSt,
265267
integrationSt,
@@ -282,6 +284,8 @@ class ViewerLineRect extends React.Component {
282284
const { x, y } = currentData;
283285
uvvisSeed = toSeed(x, y);
284286
}
287+
const rawXUnit = getLcMsXUnit(uvvisEntities?.[0]) || getLcMsXUnit(ticEntities?.[0]);
288+
const timeXLabel = formatLcmsTimeXLabel(rawXUnit);
285289
drawMain(this.rootKlassLine, W, H, LIST_BRUSH_SVG_GRAPH.LINE);
286290
this.lineFocus.create({
287291
filterSeed: uvvisSeed,
@@ -294,8 +298,9 @@ class ViewerLineRect extends React.Component {
294298
isUiAddIntgSt,
295299
editPeakSt,
296300
hplcMsSt,
301+
uvvisXUnit: rawXUnit,
297302
});
298-
drawLabel(this.rootKlassLine, null, 'Minutes', 'Intensity');
303+
drawLabel(this.rootKlassLine, null, timeXLabel, 'Intensity');
299304
drawDisplay(this.rootKlassLine, false);
300305

301306
drawMain(this.rootKlassMulti, W, H, LIST_BRUSH_SVG_GRAPH.MULTI);
@@ -309,7 +314,7 @@ class ViewerLineRect extends React.Component {
309314
isUiAddIntgSt,
310315
isUiNoBrushSt,
311316
});
312-
drawLabel(this.rootKlassMulti, null, 'Minutes', 'Intensity');
317+
drawLabel(this.rootKlassMulti, null, timeXLabel, 'Intensity');
313318
drawDisplay(this.rootKlassMulti, isHidden);
314319

315320
drawMain(this.rootKlassRect, W, H, LIST_BRUSH_SVG_GRAPH.RECT);
@@ -327,7 +332,7 @@ class ViewerLineRect extends React.Component {
327332

328333
componentDidUpdate(prevProps) {
329334
const {
330-
ticEntities, curveSt,
335+
ticEntities, uvvisEntities, curveSt,
331336
tTrEndPts, layoutSt,
332337
isUiAddIntgSt, isUiNoBrushSt,
333338
isHidden, uiSt, hplcMsSt, integrationSt,
@@ -350,6 +355,7 @@ class ViewerLineRect extends React.Component {
350355
const currentData = data[0];
351356
const { x, y } = currentData;
352357
const uvvisSeed = toSeed(x, y);
358+
const uvvisXUnit = getLcMsXUnit(uvvisEntities?.[0]) || getLcMsXUnit(ticEntities?.[0]);
353359
if (this.lineFocus) {
354360
this.lineFocus.update({
355361
filterSeed: uvvisSeed,
@@ -363,9 +369,13 @@ class ViewerLineRect extends React.Component {
363369
integrationSt,
364370
hplcMsSt,
365371
editPeakSt,
372+
uvvisXUnit,
366373
});
367374
}
368-
drawLabel(this.rootKlassLine, null, 'Minutes', 'Intensity');
375+
const timeXLabelLine = formatLcmsTimeXLabel(
376+
getLcMsXUnit(uvvisEntities?.[0]) || getLcMsXUnit(ticEntities?.[0]),
377+
);
378+
drawLabel(this.rootKlassLine, null, timeXLabelLine, 'Intensity');
369379
drawDisplay(this.rootKlassLine, false);
370380
}
371381

@@ -396,7 +406,10 @@ class ViewerLineRect extends React.Component {
396406
} else if (polarity === 'positive') {
397407
ticLabel = 'PLUS';
398408
}
399-
drawLabel(this.rootKlassMulti, ticLabel, 'Minutes', 'Intensity');
409+
const timeXLabelMulti = formatLcmsTimeXLabel(
410+
getLcMsXUnit(uvvisEntities?.[0]) || getLcMsXUnit(ticEntities?.[0]),
411+
);
412+
drawLabel(this.rootKlassMulti, ticLabel, timeXLabelMulti, 'Intensity');
400413
drawDisplay(this.rootKlassMulti, isHidden);
401414

402415
const subViewFeature = this.extractSubView();
@@ -428,9 +441,13 @@ class ViewerLineRect extends React.Component {
428441
uiSt,
429442
});
430443
}
444+
const timeXLabelRect = formatLcmsTimeXLabel(
445+
getLcMsXUnit(uvvisEntities?.[0]) || getLcMsXUnit(ticEntities?.[0]),
446+
);
447+
const timeSuffix = timeXLabelRect === 'Seconds' ? ' s' : ' min';
431448
drawLabel(
432449
this.rootKlassRect,
433-
labelValue != null ? `${labelValue} min` : null,
450+
labelValue != null ? `${labelValue}${timeSuffix}` : null,
434451
'm/z',
435452
'Intensity',
436453
);

src/components/d3_line_rect/line_focus.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,11 @@ class LineFocus {
137137
this.root.call(this.tip);
138138
}
139139

140-
setDataParams(data, tTrEndPts, layout, editPeakSt) {
140+
setDataParams(data, tTrEndPts, layout, editPeakSt, uvvisXUnit = '') {
141141
this.data = [...data];
142-
this.data = data.map((d) => ({ x: d.x / 60, y: d.y }));
142+
const u = String(uvvisXUnit || '').toUpperCase();
143+
const xIsMinutes = u.includes('MINUTE');
144+
this.data = data.map((d) => ({ x: xIsMinutes ? d.x : d.x / 60, y: d.y }));
143145
this.tTrEndPts = tTrEndPts;
144146
if (layout) {
145147
this.layout = layout;
@@ -549,7 +551,7 @@ class LineFocus {
549551
create({
550552
filterSeed, tTrEndPts,
551553
layoutSt,
552-
sweepExtentSt, isUiAddIntgSt, isUiNoBrushSt, hplcMsSt, editPeakSt,
554+
sweepExtentSt, isUiAddIntgSt, isUiNoBrushSt, hplcMsSt, editPeakSt, uvvisXUnit,
553555
}) {
554556
this.svg = d3.select('.d3Svg');
555557
MountMainFrame(this, 'focus');
@@ -560,7 +562,7 @@ class LineFocus {
560562

561563
this.scales = InitScale(this, false);
562564
this.setTip();
563-
this.setDataParams(filterSeed, tTrEndPts, layoutSt, editPeakSt);
565+
this.setDataParams(filterSeed, tTrEndPts, layoutSt, editPeakSt, uvvisXUnit);
564566
MountCompass(this);
565567

566568
this.axis = MountAxis(this);
@@ -593,11 +595,11 @@ class LineFocus {
593595
update({
594596
filterSeed, tTrEndPts,
595597
layoutSt,
596-
sweepExtentSt, isUiAddIntgSt, isUiNoBrushSt, uiSt, hplcMsSt, editPeakSt,
598+
sweepExtentSt, isUiAddIntgSt, isUiNoBrushSt, uiSt, hplcMsSt, editPeakSt, uvvisXUnit,
597599
}) {
598600
this.root = d3.select(this.rootKlass).selectAll('.focus-main');
599601
this.scales = InitScale(this, false);
600-
this.setDataParams(filterSeed, tTrEndPts, layoutSt, editPeakSt);
602+
this.setDataParams(filterSeed, tTrEndPts, layoutSt, editPeakSt, uvvisXUnit);
601603
this.uiSt = uiSt;
602604
const isInitialized = !!(
603605
this.root && !this.root.empty() && this.path && this.grid && this.tags

src/helpers/chem.js

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -505,8 +505,9 @@ const extrSpectraMs = (jcamp, layout) => {
505505
const container = jcamp?.info?.$OBSERVEDINTEGRALS ?? null;
506506

507507
uvvisSpectra.forEach(({ spectrum }, pairIdx) => {
508-
const isTimeAxis = String(spectrum?.xUnit || '').toLowerCase().includes('time');
509-
const scaleX = (value) => (isTimeAxis ? value / 60 : value);
508+
const xUnit = String(spectrum?.xUnit || '').toUpperCase();
509+
const xIsMinutes = xUnit.includes('MINUTE');
510+
const scaleX = (value) => (xIsMinutes ? value : value / 60);
510511
const pageKey = spectrum.pageValue ?? spectrum.page;
511512
const peakTable = peakTablesByPage.get(pageKey);
512513
let selectedPeakTable = null;
@@ -1036,6 +1037,9 @@ const ensureSpectrumData = (spectrum, source) => {
10361037
return result;
10371038
};
10381039

1040+
const reHeaderXunits = /##XUNITS\s*=\s*([^\r\n]+)/i;
1041+
const reHeaderYunits = /##YUNITS\s*=\s*([^\r\n]+)/i;
1042+
10391043
const parseChemstationPages = (source, jcamp) => {
10401044
if (typeof source !== 'string') return [];
10411045
const parts = source.split(/##PAGE=/);
@@ -1044,8 +1048,12 @@ const parseChemstationPages = (source, jcamp) => {
10441048
const info = jcamp?.info || {};
10451049
const baseSpectrum = Array.isArray(jcamp?.spectra) ? jcamp.spectra[0] : null;
10461050
const dataType = baseSpectrum?.dataType || jcamp?.dataType || 'LC/MS';
1047-
const xUnit = info.XUNITS || baseSpectrum?.xUnit || jcamp?.xUnit || '';
1048-
const yUnit = info.YUNITS || baseSpectrum?.yUnit || jcamp?.yUnit || '';
1051+
const headerBlock = parts[0] || '';
1052+
const fromHeaderX = reHeaderXunits.exec(headerBlock);
1053+
const fromHeaderY = reHeaderYunits.exec(headerBlock);
1054+
const xUnitFromHeader = fromHeaderX ? fromHeaderX[1].trim() : '';
1055+
const xUnit = info.XUNITS || xUnitFromHeader || baseSpectrum?.xUnit || jcamp?.xUnit || '';
1056+
const yUnit = info.YUNITS || (fromHeaderY ? fromHeaderY[1].trim() : '') || baseSpectrum?.yUnit || jcamp?.yUnit || '';
10491057

10501058
const spectra = [];
10511059
for (let i = 1; i < parts.length; i += 1) {
@@ -1155,8 +1163,7 @@ const ExtractJcamp = (source) => {
11551163
);
11561164
const isChemstation = isChemstationLcms(source, jcamp);
11571165
const parsedPages = parseChemstationPages(source, jcamp);
1158-
const spectraCount = Array.isArray(jcamp.spectra) ? jcamp.spectra.length : 0;
1159-
if (parsedPages.length > 1 && spectraCount < parsedPages.length) {
1166+
if (isChemstation && parsedPages.length >= 1) {
11601167
jcamp.spectra = parsedPages;
11611168
}
11621169

src/helpers/extractEntityLCMS.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,27 @@ export function getLcMsInfo(entity = {}) {
9292
return { kind: kind || 'unknown', polarity };
9393
}
9494

95+
export function getLcMsXUnit(entity) {
96+
if (!entity) return '';
97+
return (
98+
entity.xUnit
99+
|| getEntityValue(entity, 'spectra.0.xUnit')
100+
|| getEntityValue(entity, 'feature.xUnit')
101+
|| getEntityValue(entity, 'features.0.xUnit')
102+
);
103+
}
104+
105+
export function formatLcmsTimeXLabel(xUnit) {
106+
if (xUnit == null || typeof xUnit !== 'string') return 'Minutes';
107+
const u = String(xUnit).toUpperCase().trim().replace(/\s+/g, ' ');
108+
if (u.includes('SECOND')) return 'Seconds';
109+
if (u.includes('MINUTE')) return 'Minutes';
110+
if (u.includes('RETENTION') && u.includes('TIME')) return 'Retention time';
111+
if (u.includes('TIME')) return 'Time';
112+
const trimmed = String(xUnit).trim();
113+
return trimmed || 'Minutes';
114+
}
115+
95116
export function classify(entity) {
96117
const { kind, polarity } = getLcMsInfo(entity);
97118
if (kind === 'unknown') return 'unknown';

0 commit comments

Comments
 (0)