libpappsomspp
Library for mass spectrometry
Loading...
Searching...
No Matches
binarydataarray.cpp
Go to the documentation of this file.
1/**
2 * \file pappsomspp/core/processing/cbor/mzcbor/binarydataarray.cpp
3 * \date 25/11/2025
4 * \author Olivier Langella
5 * \brief PSI BinaryDataArray object for mzML/mzCBOR
6 */
7
8/*******************************************************************************
9 * Copyright (c) 2025 Olivier Langella <Olivier.Langella@universite-paris-saclay.fr>.
10 *
11 * This file is part of PAPPSOms-tools.
12 *
13 * PAPPSOms-tools is free software: you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation, either version 3 of the License, or
16 * (at your option) any later version.
17 *
18 * PAPPSOms-tools is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with PAPPSOms-tools. If not, see <http://www.gnu.org/licenses/>.
25 *
26 ******************************************************************************/
27
28
29#include "binarydataarray.h"
30#include "cvparam.h"
32#include <qjsonarray.h>
33#include <qlogging.h>
34#include <zlib.h>
35
36void
38{
39 QString txt_value;
40 reader.enterContainer();
41 // qDebug() << txt_value;
42 while(reader.hasNext() && (!reader.isInvalid()))
43 {
44 if(reader.isString())
45 {
46 if(reader.decodeString(txt_value))
47 {
48 // qDebug() << txt_value;
49 if(txt_value == "bits")
50 {
51 // cvParamMap = CvParam::getCvParamsMapFromCbor(reader);
52 bits = reader.toUnsignedInteger();
53 reader.next();
54 }
55 else if(txt_value == "isInt")
56 {
57 // cvParamMap = CvParam::getCvParamsMapFromCbor(reader);
58 isInt = reader.toBool();
59 reader.next();
60 }
61 else if(txt_value == "unit")
62 {
63 // cvParamMap = CvParam::getCvParamsMapFromCbor(reader);
64 reader.decodeString(txt_value);
65 unit = txt_value;
66 }
67 else if(txt_value == "compress")
68 {
69 // cvParamMap = CvParam::getCvParamsMapFromCbor(reader);
70 reader.decodeString(txt_value);
71 compress = txt_value;
72 }
73 else if(txt_value == "data")
74 {
75 // cvParamMap = CvParam::getCvParamsMapFromCbor(reader);
76 // reader.next();
77 // qDebug() << reader.type();
78 auto r = reader.readByteArray();
79 while(r.status == QCborStreamReader::Ok)
80 {
81 byteArray += r.data;
82 r = reader.readByteArray();
83 }
84
85 if(r.status == QCborStreamReader::Error)
86 {
87 // handle error condition
88 // qDebug() << "error";
89 byteArray.clear();
90 }
91 }
92 else
93 {
94 reader.next();
95 }
96 }
97 else
98 {
99 reader.next();
100 }
101 }
102 else
103 {
104 reader.next();
105 }
106 }
107 reader.leaveContainer();
108}
109
110void
112{
113 QString txt_value;
114 // qDebug() << txt_value;
115 for(auto it_binarr = cbor_map.cbegin(); it_binarr != cbor_map.cend(); it_binarr++)
116 {
117 // while(reader.hasNext() && (!reader.isInvalid()))
118 // {
119 //
120 txt_value = it_binarr.key().toString();
121 // qDebug() << txt_value;
122 if(txt_value == "bits")
123 {
124 // cvParamMap = CvParam::getCvParamsMapFromCbor(reader);
125 bits = it_binarr.value().toInteger();
126 }
127 else if(txt_value == "isInt")
128 {
129 // cvParamMap = CvParam::getCvParamsMapFromCbor(reader);
130 isInt = it_binarr.value().toBool();
131 }
132 else if(txt_value == "unit")
133 {
134 // cvParamMap = CvParam::getCvParamsMapFromCbor(reader);
135 unit = it_binarr.value().toString();
136 }
137 else if(txt_value == "compress")
138 {
139 // cvParamMap = CvParam::getCvParamsMapFromCbor(reader);
140 compress = it_binarr.value().toString();
141 }
142 else if(txt_value == "data")
143 {
144 // cvParamMap = CvParam::getCvParamsMapFromCbor(reader);
145 // reader.next();
146 // qDebug() << reader.type();
147 byteArray = it_binarr.value().toByteArray();
148 }
149 }
150}
151void
153{
154
155 writer.startMap();
156 writer.append("unit");
157 writer.append(unit);
158
159 writer.append("bits");
160 writer.append(bits);
161 writer.append("isInt");
162 writer.append(isInt);
163
164 writer.append("compress");
165 writer.append(compress);
166
167 writer.append("data");
168 writer.append(byteArray);
169 writer.endMap();
170}
171
172void
174{
175
176 qDebug();
177 //<binaryDataArray encodedLength="6380">
178 std::size_t encodedLength = reader.attributes().value("encodedLength").toULongLong();
179 qDebug() << "encodedLength=" << encodedLength;
180 while(reader.readNext() && !reader.isEndElement())
181 {
182 if(reader.isStartElement())
183 {
184 if(reader.name().toString() == "cvParam")
185 {
186 QString accession = reader.attributes().value("accession").toString();
187
188 qDebug() << "accession=" << accession;
189 //<cvParam cvRef="MS" accession="MS:1000523" value="" name="64-bit float" />
190 if(accession == "MS:1000523")
191 {
192 bits = 64;
193 isInt = false;
194 }
195 else if(accession == "MS:1000519")
196 {
197 /*
198 *
199[Term]
200id: MS:1000519
201name: 32-bit integer
202def: "Signed 32-bit little-endian integer." [PSI:MS]
203is_a: MS:1000518 ! binary data type
204*/
205 bits = 32;
206 isInt = true;
207 }
208 else if(accession == "MS:1000521")
209 {
210 /*
211 [Term]
212 id: MS:1000521
213 name: 32-bit float
214 def: "32-bit precision little-endian floating point conforming to IEEE-754." [PSI:MS]
215 is_a: MS:1000518 ! binary data type
216 */
217 bits = 32;
218 isInt = false;
219 }
220 else if(accession == "MS:1000522")
221 {
222
223 /*
224 [Term]
225 id: MS:1000522
226 name: 64-bit integer
227 def: "Signed 64-bit little-endian integer." [PSI:MS]
228 is_a: MS:1000518 ! binary data type*/
229 bits = 64;
230 isInt = true;
231 }
232
233 //<cvParam cvRef="MS" accession="MS:1000574" value="" name="zlib compression" />
234 else if(accession == "MS:1000574")
235 {
236 compress = "zlib";
237 }
238 else if(accession == "MS:1000576")
239 {
240 /*
241 [Term]
242 id: MS:1000576
243 name: no compression
244 def: "No Compression." [PSI:MS]
245 is_a: MS:1000572 ! binary data compression type
246 */
247 compress = "none";
248 }
249
250 else if(accession == "MS:1000515")
251 {
252 unit = accession;
253 }
254 else if(accession == "MS:1000514")
255 {
256 // <cvParam cvRef="MS" accession="MS:1000514" value="" name="m/z array"
257 // unitAccession="MS:1000040" unitName="m/z" unitCvRef="MS" />
258
259 unit = accession;
260 }
261 else if(accession == "MS:1000595")
262 {
263 // <cvParam cvRef="MS" accession="MS:1000595" name="time array" value=""
264 // unitCvRef="UO" unitAccession="UO:0000031" unitName="minute"/>
265
266 unit = accession;
267 }
268 else if(accession == "MS:1000786")
269 {
270 // <cvParam cvRef="MS" accession="MS:1000786" name="non-standard data
271 // array" value="ms level" unitCvRef="UO" unitAccession="UO:0000186"
272 // unitName="dimensionless unit"/>
273 //
274 unit = accession;
275 }
276 else
277 {
278 reader.raiseError(
279 QObject::tr("cvParam accession %1 is not known in binaryDataArray")
280 .arg(accession));
282 QObject::tr("cvParam accession %1 is not known in binaryDataArray")
283 .arg(accession));
284 }
285 reader.skipCurrentElement();
286 }
287 else if(reader.name().toString() == "binary")
288 {
289
290 while(reader.readNext() && !reader.isEndElement())
291 {
292 if(reader.isCharacters())
293 {
294 // clean content:
295 QStringView content = reader.text().trimmed();
296 if((reader.text().toString() == "\n") || (reader.text().toString() == "\n\t"))
297 {
298 }
299 else
300 {
301 // text node
302 if(!content.isEmpty())
303 {
304 // qDebug() << "text isCharacters" << content.mid(0, 10);
305
306 if((std::size_t)reader.text().size() != encodedLength)
307 {
308 qWarning() << "reader.text().size() != encodedLength"
309 << reader.text().size() << " " << encodedLength;
310 }
311
312 // mp_cborWriter->append("@text@");
313 // mp_cborWriter->append(content);
314 byteArray = byteArray.fromBase64(reader.text().trimmed().toLatin1());
315 }
316 }
317 }
318 }
319 }
320 else
321 {
322 reader.skipCurrentElement();
323 }
324 }
325 }
326 qDebug();
327}
328
329
330void
332{
333 //<binaryDataArray encodedLength="1152">
334 writer.writeStartElement("binaryDataArray");
335 auto base64 = byteArray.toBase64();
336 writer.writeAttribute("encodedLength", QString("%1").arg(base64.size()));
337 // <cvParam cvRef="MS" accession="MS:1000514" value="" name="m/z array"
338 // unitAccession="MS:1000040" unitName="m/z" unitCvRef="MS" />
339 CvParam cv_param;
340 cv_param.cvRef = "MS";
341
342 if(unit == "MS:1000514")
343 {
344 cv_param.accession = unit;
345 cv_param.name = "m/z array";
346 cv_param.unitCvRef = "MS";
347 cv_param.unitAccession = "MS:1000040";
348 cv_param.unitName = "m/z";
349 cv_param.setValue("");
350 cv_param.toMzml(writer);
351 }
352 else if(unit == "MS:1000515")
353 {
354 //<cvParam cvRef="MS" accession="MS:1000515" value="" name="intensity array"
355 // unitAccession="MS:1000131" unitName="number of counts" unitCvRef="MS" />
356
357 cv_param.accession = unit;
358 cv_param.name = "intensity array";
359 cv_param.unitCvRef = "MS";
360 cv_param.unitAccession = "MS:1000131";
361 cv_param.unitName = "number of counts";
362 cv_param.setValue("");
363 cv_param.toMzml(writer);
364 }
365 else if(unit == "MS:1000595")
366 {
367 // <cvParam cvRef="MS" accession="MS:1000595" name="time array" value=""
368 // unitCvRef="UO" unitAccession="UO:0000031" unitName="minute"/>
369
370 cv_param.accession = unit;
371 cv_param.name = "time array";
372 cv_param.unitCvRef = "UO";
373 cv_param.unitAccession = "UO:0000031";
374 cv_param.unitName = "minute";
375 cv_param.setValue("");
376 cv_param.toMzml(writer);
377 }
378 else if(unit == "MS:1000786")
379 {
380 // <cvParam cvRef="MS" accession="MS:1000786" name="non-standard data
381 // array" value="ms level" unitCvRef="UO" unitAccession="UO:0000186"
382 // unitName="dimensionless unit"/>
383 //
384 cv_param.accession = unit;
385 cv_param.name = "non-standard data array";
386 cv_param.unitCvRef = "UO";
387 cv_param.unitAccession = "UO:0000186";
388 cv_param.unitName = "dimensionless unit";
389 cv_param.setValue("ms level");
390 cv_param.toMzml(writer);
391 }
392 else
393 {
395 QObject::tr("unit accession %1 is not known in binaryDataArray").arg(unit));
396 }
397
398 // <cvParam cvRef="MS" accession="MS:1000523" value="" name="64-bit float" />
399
400 cv_param.unitCvRef.clear();
401 cv_param.unitAccession.clear();
402 cv_param.unitName.clear();
403 cv_param.setValue("");
404 if(isInt)
405 {
406 /*
407 id: MS:1000519
408 name: 32-bit integer
409 def: "Signed 32-bit little-endian integer." [PSI:MS]
410 is_a: MS:1000518 ! binary data type*/
411 if(bits == 32)
412 {
413 cv_param.accession = "MS:1000519";
414 cv_param.name = "32-bit integer";
415 cv_param.toMzml(writer);
416 }
417 else if(bits == 64)
418 {
419 /*
420 [Term]
421 id: MS:1000522
422 name: 64-bit integer
423 def: "Signed 64-bit little-endian integer." [PSI:MS]
424 is_a: MS:1000518 ! binary data type*/
425 cv_param.accession = "MS:1000522";
426 cv_param.name = "64-bit integer";
427 cv_param.toMzml(writer);
428 }
429 }
430 else
431 {
432 if(bits == 64)
433 {
434 cv_param.accession = "MS:1000523";
435 cv_param.name = "64-bit float";
436 cv_param.toMzml(writer);
437 }
438 else if(bits == 32)
439 {
440 /*
441 [Term]
442 id: MS:1000521
443 name: 32-bit float
444 def: "32-bit precision little-endian floating point conforming to IEEE-754." [PSI:MS]
445 is_a: MS:1000518 ! binary data type
446 */
447 cv_param.accession = "MS:1000521";
448 cv_param.name = "32-bit float";
449 cv_param.toMzml(writer);
450 }
451 }
452 // <cvParam cvRef="MS" accession="MS:1000574" value="" name="zlib compression" />
453 /*
454
455[Term]
456id: MS:1000520
457name: 16-bit float
458def: "OBSOLETE Signed 16-bit float." [PSI:MS]
459is_a: MS:1000518 ! binary data type
460is_obsolete: true
461
462
463[Term]
464id: MS:1000523
465name: 64-bit float
466def: "64-bit precision little-endian floating point conforming to IEEE-754." [PSI:MS]
467is_a: MS:1000518 ! binary data type
468*/
469
470 if(compress == "zlib")
471 {
472 cv_param.accession = "MS:1000574";
473 cv_param.name = "zlib compression";
474 cv_param.toMzml(writer);
475 }
476 else if(compress == "none")
477 { /*
478[Term]
479id: MS:1000576
480name: no compression
481def: "No Compression." [PSI:MS]
482is_a: MS:1000572 ! binary data compression type
483*/
484 cv_param.accession = "MS:1000576";
485 cv_param.name = "no compression";
486 cv_param.toMzml(writer);
487 }
488
489 // <binary>eJwl0W9oW1U
490 // writer.writeStartElement("binary");
491 writer.writeTextElement("binary", base64);
492 // </binary>
493 // writer.writeEndElement();
494 // </binaryDataArray>
495 writer.writeEndElement();
496}
497
498
499void
501 std::vector<double> &double_list) const
502{
503 qDebug();
504 int size_in_byte = 8;
505 if(bits == 32)
506 {
507 size_in_byte = 4;
508 }
509
510 qDebug();
511 // if(result.decodingStatus == QByteArray::Base64DecodingStatus::Ok)
512 // { // Allocate buffer for decompressed data
513 if(compress == "zlib")
514 {
515 qDebug();
516 std::vector<unsigned char> data_heap;
517 uLongf decompressedSize = estimated_length * size_in_byte; // Estimate size
518 data_heap.resize(decompressedSize);
519
520 // Decompress the data
521 int result_zlib = uncompress(
522 data_heap.data(), &decompressedSize, (Bytef *)byteArray.constData(), byteArray.size());
523
524 if(result_zlib != Z_OK)
525 {
526 throw pappso::PappsoException(QObject::tr("Decompression failed: %1").arg(result_zlib));
527 }
528
529 // Resize the vector to the actual decompressed size
530 data_heap.resize(decompressedSize);
531 double_list.resize(decompressedSize / size_in_byte);
532
533
534 // double *double_ptr = (double *)&decompressedData[0];
535 std::size_t j = 0;
536 for(std::size_t i = 0; i < data_heap.size(); i += size_in_byte)
537 {
538 if(bits == 32)
539 {
540 if(isInt)
541 {
542 double_list[j] = *(std::int32_t *)&data_heap[i];
543 }
544 else
545 {
546 double_list[j] = *(std::float_t *)&data_heap[i];
547 }
548 }
549 else
550 {
551 if(isInt)
552 {
553 double_list[j] = *(std::int64_t *)&data_heap[i];
554 }
555 else
556 {
557 double_list[j] = *(double *)&data_heap[i];
558 }
559 }
560 // double_ptr++;
561 j++;
562 }
563 }
564 else if(compress == "none")
565 {
566
567 qDebug() << size_in_byte << " " << byteArray.size();
568
569 double_list.resize(byteArray.size() / size_in_byte);
570 // double *double_ptr = (double *)&decompressedData[0];
571 std::size_t j = 0;
572 for(std::size_t i = 0; i < (std::size_t)byteArray.size(); i += size_in_byte)
573 {
574 qDebug() << " i=" << i;
575 if(bits == 32)
576 {
577 if(isInt)
578 {
579 double_list[j] = *(std::int32_t *)&byteArray.constData()[i];
580 }
581 else
582 {
583 double_list[j] = *(std::float_t *)&byteArray.constData()[i];
584 }
585 }
586 else
587 {
588 if(isInt)
589 {
590 double_list[j] = *(std::int64_t *)&byteArray.constData()[i];
591 }
592 else
593 {
594 qDebug();
595 double_list[j] = *(double *)&byteArray.constData()[i];
596 }
597 }
598 // double_ptr++;
599 j++;
600 }
601 }
602
603
604 qDebug();
605 // std::vector<double> v(decompressedData.cbegin(), decompressedData.cend());
606 // qDebug() << j << " " << double_list.size();
607}
608
609bool
611{
612 // <cvParam cvRef="MS" accession="MS:1000515" value="" name="intensity array"
613 // unitAccession="MS:1000131" unitName="number of counts" unitCvRef="MS" />
614
615 return unit == "MS:1000515";
616}
617
618bool
620{
621 // <cvParam cvRef="MS" accession="MS:1000514" value="" name="m/z array"
622 // unitAccession="MS:1000040" unitName="m/z" unitCvRef="MS" />
623
624 return unit == "MS:1000514";
625}
626
627QJsonObject
629{
630 QJsonObject binary_data;
631 binary_data.insert("unit", unit);
632 std::vector<double> double_list;
633 decodeVector(estimated_length, double_list);
634
635 QJsonArray double_array;
636
637 for(auto &value : double_list)
638 {
639 double_array.append(value);
640 }
641
642 binary_data.insert("array", double_array);
643 return binary_data;
644}
PSI BinaryDataArray object for mzML/mzCBOR.
simple override of the raw QCborStreamReader This adds convenient functions to put CBOR data into C++...
bool decodeString(QString &the_str)
decode the current cbor value as a string the point to the next value the current value is decoded as...
overrides QCborStreamWriter base class to provide convenient functions
PSI cvParam object for mzML/mzCBOR.
void fromCbor(CborStreamReader &reader)
void fromMzml(QXmlStreamReader &reader)
void fromQCborMap(const QCborMap &cbor_map)
void toMzml(QXmlStreamWriter &writer)
void toCbor(CborStreamWriter &writer)
void decodeVector(std::size_t estimated_length, std::vector< double > &double_list) const
QJsonObject toJsonObject(std::size_t estimated_length) const
write the structure to a JSON object needs to decode the binary array
void setValue(const QString &value_str)
Definition cvparam.cpp:280
void toMzml(QXmlStreamWriter &writer)
Definition cvparam.cpp:288