LCOV - code coverage report
Current view: top level - jdx/api - JdxConverter.cpp (source / functions) Hit Total Coverage
Test: coverage_filtered.info Lines: 294 308 95.5 %
Date: 2025-06-15 16:18:27 Functions: 28 28 100.0 %

          Line data    Source code
       1             : /* Copyright (C) 2025 Robert Schiwon
       2             :  *
       3             :  * This file is part of libjdx.
       4             :  *
       5             :  * libjdx is free software: you can redistribute it and/or modify it under the
       6             :  * terms of the GNU Lesser General Public License as published by the Free
       7             :  * Software Foundation, either version 3 of the License, or (at your option) any
       8             :  * later version.
       9             :  *
      10             :  * libjdx is distributed in the hope that it will be useful, but WITHOUT ANY
      11             :  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      12             :  * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
      13             :  * details.
      14             :  *
      15             :  * You should have received a copy of the GNU Lesser General Public License
      16             :  * along with libjdx. If not, see <https://www.gnu.org/licenses/>.
      17             :  */
      18             : #include "jdx/api/JdxConverter.hpp"
      19             : #include "api/Point2D.hpp"
      20             : #include "jdx/JdxParser.hpp"
      21             : #include "util/StringUtils.hpp"
      22             : 
      23             : #include <fstream>
      24             : #include <iostream>
      25             : #include <sstream>
      26             : #include <stdexcept>
      27             : #include <string>
      28             : 
      29             : #ifdef __EMSCRIPTEN__
      30             : #include <emscripten/bind.h>
      31             : #endif
      32             : 
      33          22 : libjdx::jdx::api::JdxConverter::JdxConverter(const std::string& path)
      34             :     : m_rootBlock{std::make_unique<libjdx::jdx::Block>(
      35          22 :         libjdx::jdx::JdxParser::parse(path))}
      36             : {
      37          18 : }
      38             : 
      39          35 : libjdx::api::Node libjdx::jdx::api::JdxConverter::read(const std::string& path)
      40             : {
      41             :     // std::cout << "C++: JdxConverter.read(): " << path << '\n';
      42          70 :     std::vector<size_t> nodeIndices = convertPathToNodeIndices(path);
      43          35 :     auto node = retrieveNode(nodeIndices);
      44          64 :     return node;
      45             : }
      46             : 
      47          35 : std::vector<size_t> libjdx::jdx::api::JdxConverter::convertPathToNodeIndices(
      48             :     const std::string& path)
      49             : {
      50         105 :     auto pathSegments = util::split(path, "/", true);
      51          35 :     if (!pathSegments.at(0).empty() || pathSegments.size() < 2)
      52             :     {
      53           0 :         throw std::invalid_argument("Illegal path for reading node: " + path);
      54             :     }
      55             : 
      56          35 :     std::vector<size_t> nodeIndices{};
      57          35 :     if (pathSegments.size() != 2 || !pathSegments.at(1).empty())
      58             :     {
      59             :         // path not only "/"
      60          49 :         for (size_t i = 1; i < pathSegments.size(); i++)
      61             :         {
      62          30 :             auto pathIndex = std::stoul(pathSegments.at(i));
      63          30 :             nodeIndices.push_back(pathIndex);
      64             :         }
      65             :     }
      66             : 
      67          70 :     return nodeIndices;
      68             : }
      69             : 
      70             : // NOLINTBEGIN(readability-function-cognitive-complexity)
      71          35 : libjdx::api::Node libjdx::jdx::api::JdxConverter::retrieveNode(
      72             :     const std::vector<size_t>& nodeIndices)
      73             : {
      74           2 :     auto raiseIllegalPathError = [](size_t nodeIndex, const Block* block) {
      75             :         throw std::invalid_argument(
      76             :             "Illegal path for reading node. Block: "
      77           4 :             + block->getLdr("TITLE").value().getLabel()
      78           6 :             + ", child index: " + std::to_string(nodeIndex));
      79             :     };
      80             : 
      81          35 :     const Block* block = &(*m_rootBlock);
      82          35 :     size_t iterationIndex = 0;
      83          48 :     for (const auto nodeIndex : nodeIndices)
      84             :     {
      85          25 :         const size_t brukerRelaxStartIndex = 0;
      86             :         const size_t brukerRelaxEndIndexExclusive
      87          25 :             = brukerRelaxStartIndex + block->getBrukerRelaxSections().size();
      88          25 :         const size_t brukerParamsStartIndex = brukerRelaxEndIndexExclusive;
      89             :         const size_t brukerParamsEndIndexExclusive
      90             :             = brukerParamsStartIndex
      91          25 :               + block->getBrukerSpecificParameters().size();
      92          25 :         const size_t nTuplesIndex = brukerParamsEndIndexExclusive;
      93             :         const size_t auditTrailIndex
      94          25 :             = block->getNTuples().has_value() ? nTuplesIndex + 1 : nTuplesIndex;
      95          25 :         const size_t childBlocksStartIndex = block->getAuditTrail().has_value()
      96          25 :                                                  ? auditTrailIndex + 1
      97          25 :                                                  : auditTrailIndex;
      98             : 
      99             :         // nodeIndex >= brukerRelaxStartIndex, always true
     100          25 :         if (nodeIndex < brukerRelaxEndIndexExclusive
     101          25 :             && !block->getBrukerRelaxSections().empty())
     102             :         {
     103             :             // Bruker ##$RELAX section
     104           3 :             if (iterationIndex < nodeIndices.size() - 1)
     105             :             {
     106             :                 // not a leaf node
     107           1 :                 raiseIllegalPathError(nodeIndex, block);
     108             :             }
     109           2 :             return mapBrukerRelaxSection(block->getBrukerRelaxSections().at(
     110          11 :                 nodeIndex - brukerRelaxStartIndex));
     111             :         }
     112          22 :         if (nodeIndex >= brukerParamsStartIndex
     113          22 :             && nodeIndex < brukerParamsEndIndexExclusive
     114          44 :             && !block->getBrukerSpecificParameters().empty())
     115             :         {
     116             :             // $$ Bruker specific parameters section
     117           3 :             if (iterationIndex < nodeIndices.size() - 1)
     118             :             {
     119             :                 // not a leaf node
     120           1 :                 raiseIllegalPathError(nodeIndex, block);
     121             :             }
     122             :             return mapBrukerSpecificParameters(
     123           2 :                 block->getBrukerSpecificParameters().at(
     124           2 :                     nodeIndex - brukerParamsStartIndex));
     125             :         }
     126          19 :         if (nodeIndex == nTuplesIndex && block->getNTuples().has_value())
     127             :         {
     128             :             // consider NTUPLES LDR as child node
     129             :             // NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
     130           4 :             auto startIt = nodeIndices.cbegin() + iterationIndex;
     131             :             auto nTuplesIndices
     132           8 :                 = std::vector<size_t>{++startIt, nodeIndices.cend()};
     133           8 :             return mapNTuples(block->getNTuples().value(), nTuplesIndices,
     134           4 :                 isPeakData(*block));
     135             :         }
     136          15 :         if (nodeIndex == auditTrailIndex && block->getAuditTrail().has_value())
     137             :         {
     138             :             // consider AUDIT TRAIL LDR as child node
     139           1 :             if (iterationIndex < nodeIndices.size() - 1)
     140             :             {
     141             :                 // not a leaf node
     142           0 :                 raiseIllegalPathError(nodeIndex, block);
     143             :             }
     144           1 :             return mapAuditTrail(block->getAuditTrail().value());
     145             :         }
     146             :         const auto& childBlock
     147          14 :             = block->getBlocks().at(nodeIndex - childBlocksStartIndex);
     148          13 :         block = &childBlock;
     149          13 :         ++iterationIndex;
     150             :     }
     151          23 :     return mapBlock(*block, isPeakData(*block));
     152             : }
     153             : // NOLINTEND(readability-function-cognitive-complexity)
     154             : 
     155          23 : libjdx::api::Node libjdx::jdx::api::JdxConverter::mapBlock(
     156             :     const Block& block, bool isPeakData)
     157             : {
     158          69 :     auto name = block.getLdr("TITLE").value().getValue();
     159             : 
     160          46 :     std::vector<libjdx::api::KeyValueParam> parameters{};
     161         160 :     for (auto const& ldr : block.getLdrs())
     162             :     {
     163         137 :         parameters.push_back({ldr.getLabel(), ldr.getValue()});
     164             :     }
     165             : 
     166          46 :     auto data = mapData(block);
     167             : 
     168          46 :     auto peakTable = libjdx::api::Table{};
     169          23 :     if (block.getPeakAssignments().has_value())
     170             :     {
     171           1 :         peakTable = mapPeakAssignments(block.getPeakAssignments().value());
     172             :     }
     173          22 :     else if (block.getPeakTable().has_value())
     174             :     {
     175           2 :         peakTable = mapPeakTable(block.getPeakTable().value());
     176             : 
     177           2 :         if (isPeakData && data.empty())
     178             :         {
     179             :             // for MS, map peak table as data if no other data is present
     180           1 :             data = mapPeakTableAsData(block.getPeakTable().value());
     181             :         }
     182             :     }
     183             : 
     184          46 :     std::vector<std::string> childNodeNames{};
     185          35 :     for (auto const& brukerRelaxSection : block.getBrukerRelaxSections())
     186             :     {
     187          24 :         auto childNodeName = brukerRelaxSection.getName();
     188          12 :         childNodeNames.push_back(childNodeName);
     189             :     }
     190          12 :     for (auto const& brukerSpecificSection :
     191          47 :         block.getBrukerSpecificParameters())
     192             :     {
     193          24 :         auto childNodeName = brukerSpecificSection.getName();
     194          12 :         childNodeNames.push_back(childNodeName);
     195             :     }
     196          23 :     if (block.getNTuples().has_value())
     197             :     {
     198             :         // consider NTUPLES LDR as child node
     199          14 :         auto nTuplesName = block.getNTuples().value().getDataForm();
     200           7 :         childNodeNames.push_back(nTuplesName);
     201             :     }
     202          23 :     if (block.getAuditTrail().has_value())
     203             :     {
     204             :         // consider AUDIT TRAIL LDR as child node
     205           0 :         auto auditTrailLabel = block.getAuditTrail().value().getLabel();
     206           0 :         childNodeNames.push_back(auditTrailLabel);
     207             :     }
     208         113 :     for (auto const& childBlock : block.getBlocks())
     209             :     {
     210         270 :         auto childNodeName = childBlock.getLdr("TITLE").value().getValue();
     211          90 :         childNodeNames.push_back(childNodeName);
     212             :     }
     213             : 
     214          46 :     auto metadata = mapMetadata(block);
     215             : 
     216          46 :     return {name, parameters, data, metadata, peakTable, childNodeNames};
     217             : }
     218             : 
     219          50 : bool libjdx::jdx::api::JdxConverter::isPeakData(const Block& block)
     220             : {
     221             :     auto dataType
     222         150 :         = block.getLdr("DATATYPE").value_or(StringLdr{"", ""}).getValue();
     223          50 :     util::toLower(dataType);
     224         100 :     return dataType == "mass spectrum";
     225             : }
     226             : 
     227           2 : libjdx::api::Node libjdx::jdx::api::JdxConverter::mapBrukerRelaxSection(
     228             :     const BrukerRelaxSection& section)
     229             : {
     230           4 :     std::vector<libjdx::api::KeyValueParam> parameters{};
     231           2 :     parameters.push_back({"", section.getContent()});
     232             :     return {
     233             :         section.getName(),
     234             :         parameters,
     235             :         std::vector<libjdx::api::Point2D>{},
     236             :         std::map<std::string, std::string>{},
     237             :         libjdx::api::Table{},
     238             :         std::vector<std::string>{},
     239           4 :     };
     240             : }
     241             : 
     242           2 : libjdx::api::Node libjdx::jdx::api::JdxConverter::mapBrukerSpecificParameters(
     243             :     const BrukerSpecificParameters& section)
     244             : {
     245           4 :     std::vector<libjdx::api::KeyValueParam> parameters{};
     246           9 :     for (const auto& ldr : section.getContent())
     247             :     {
     248           7 :         parameters.push_back({ldr.getLabel(), ldr.getValue()});
     249             :     }
     250             :     return {
     251             :         section.getName(),
     252             :         parameters,
     253             :         std::vector<libjdx::api::Point2D>{},
     254             :         std::map<std::string, std::string>{},
     255             :         libjdx::api::Table{},
     256             :         std::vector<std::string>{},
     257           4 :     };
     258             : }
     259             : 
     260           4 : libjdx::api::Node libjdx::jdx::api::JdxConverter::mapNTuples(
     261             :     const NTuples& nTuples, const std::vector<size_t>& nodeIndices,
     262             :     bool isPeakData)
     263             : {
     264           4 :     if (nodeIndices.empty())
     265             :     {
     266             :         // map NTUPLES record
     267           2 :         auto name = nTuples.getDataForm();
     268             : 
     269           2 :         std::vector<libjdx::api::KeyValueParam> parameters{};
     270          14 :         for (const auto& ldr : nTuples.getLdrs())
     271             :         {
     272          13 :             parameters.push_back({ldr.getLabel(), ldr.getValue()});
     273             :         }
     274             : 
     275           2 :         std::vector<libjdx::api::Point2D> data{};
     276             : 
     277           2 :         auto peakTable = libjdx::api::Table{};
     278             : 
     279           2 :         std::vector<std::string> childNodeNames{};
     280           3 :         for (size_t pageIndex = 0; pageIndex < nTuples.getNumPages();
     281             :              ++pageIndex)
     282             :         {
     283           2 :             const auto& page = nTuples.getPage(pageIndex);
     284           2 :             childNodeNames.push_back(mapNTuplesPageName(page));
     285             :         }
     286             : 
     287           2 :         auto metadata = std::map<std::string, std::string>{};
     288             : 
     289           1 :         return {name, parameters, data, metadata, peakTable, childNodeNames};
     290             :     }
     291             : 
     292           3 :     if (nodeIndices.size() > 1 || nodeIndices.at(0) > nTuples.getNumPages())
     293             :     {
     294           0 :         std::string indices{};
     295           0 :         for (auto nodeIndex : nodeIndices)
     296             :         {
     297           0 :             indices += " " + std::to_string(nodeIndex);
     298             :         }
     299             :         throw std::invalid_argument(
     300           0 :             "Illegal indices for reading NTUPLES node: " + indices);
     301             :     }
     302             : 
     303             :     // map PAGE
     304           3 :     return mapNTuplesPage(nTuples.getPage(nodeIndices.at(0)), isPeakData);
     305             : }
     306             : 
     307           3 : libjdx::api::Node libjdx::jdx::api::JdxConverter::mapNTuplesPage(
     308             :     const Page& page, bool isPeakData)
     309             : {
     310           6 :     auto name = mapNTuplesPageName(page);
     311             : 
     312           6 :     std::vector<libjdx::api::KeyValueParam> parameters{};
     313           4 :     for (auto&& ldr : page.getPageLdrs())
     314             :     {
     315           1 :         parameters.push_back({ldr.getLabel(), ldr.getValue()});
     316             :     }
     317             : 
     318           6 :     std::vector<libjdx::api::Point2D> data{};
     319           6 :     libjdx::api::Table peakTable{};
     320           3 :     if (page.getDataTable())
     321             :     {
     322           6 :         auto dataTable = page.getDataTable().value();
     323           3 :         if (dataTable.getPlotDescriptor().has_value())
     324             :         {
     325           6 :             parameters.push_back(
     326           3 :                 {"Plot Descriptor", dataTable.getPlotDescriptor().value()});
     327             :         }
     328           3 :         data = mapXyData(dataTable.getData());
     329             : 
     330           3 :         if (isPeakData)
     331             :         {
     332           1 :             peakTable = mapDataAsPeakTable(dataTable.getData());
     333             :         }
     334             :     }
     335             : 
     336           6 :     std::vector<std::string> childNodeNames{};
     337             : 
     338           6 :     auto metadata = mapMetadata(page, isPeakData);
     339             : 
     340           6 :     return {name, parameters, data, metadata, peakTable, childNodeNames};
     341             : }
     342             : 
     343           5 : std::string libjdx::jdx::api::JdxConverter::mapNTuplesPageName(const Page& page)
     344             : {
     345           5 :     auto name = page.getPageVariables();
     346           5 :     if (page.getDataTable())
     347             :     {
     348           5 :         const auto& dataTable = page.getDataTable().value();
     349           5 :         name += " - " + dataTable.getAttributes().yAttributes.varName;
     350             :     }
     351           5 :     return name;
     352             : }
     353             : 
     354          23 : std::vector<libjdx::api::Point2D> libjdx::jdx::api::JdxConverter::mapData(
     355             :     const Block& block)
     356             : {
     357          23 :     std::optional<std::vector<std::pair<double, double>>> rawData{};
     358          23 :     if (block.getXyData())
     359             :     {
     360             :         // TODO: fix const definitions that prevent rawData =
     361             :         // block.getXyData().value().getData();
     362           1 :         auto data = block.getXyData().value();
     363           1 :         rawData = data.getData();
     364             :     }
     365          23 :     if (block.getRaData())
     366             :     {
     367           1 :         auto data = block.getRaData().value();
     368           1 :         rawData = data.getData();
     369             :     }
     370          23 :     if (block.getXyPoints())
     371             :     {
     372           1 :         auto data = block.getXyPoints().value();
     373           1 :         rawData = data.getData();
     374             :     }
     375             : 
     376           3 :     return rawData ? mapXyData(rawData.value())
     377          49 :                    : std::vector<libjdx::api::Point2D>{};
     378             : }
     379             : 
     380           6 : std::vector<libjdx::api::Point2D> libjdx::jdx::api::JdxConverter::mapXyData(
     381             :     const std::vector<std::pair<double, double>>& xyData)
     382             : {
     383           6 :     std::vector<libjdx::api::Point2D> output{};
     384           6 :     output.reserve(xyData.size());
     385          27 :     for (const auto& pair : xyData)
     386             :     {
     387          21 :         output.push_back({pair.first, pair.second});
     388             :     }
     389             : 
     390           6 :     return output;
     391             : }
     392             : 
     393          23 : std::map<std::string, std::string> libjdx::jdx::api::JdxConverter::mapMetadata(
     394             :     const Block& block)
     395             : {
     396          23 :     std::map<std::string, std::string> metadata{};
     397             : 
     398          69 :     if (auto xUnits = block.getLdr("XUNITS"))
     399             :     {
     400           5 :         metadata.emplace("x.unit", xUnits.value().getValue());
     401             :     }
     402          37 :     else if (auto rUnits = block.getLdr("RUNITS"))
     403             :     {
     404           1 :         metadata.emplace("x.unit", rUnits.value().getValue());
     405             :     }
     406             : 
     407          23 :     if (metadata.count("x.unit") > 0)
     408             :     {
     409          18 :         auto xUnit = metadata.at("x.unit");
     410           6 :         util::toLower(xUnit);
     411           9 :         if (xUnit == "1/cm" || xUnit == "1 / cm" || xUnit == "cm-1"
     412           9 :             || xUnit == "cm^-1")
     413             :         {
     414           3 :             metadata.emplace("x.label", "Wavenumber");
     415             : 
     416           3 :             if (block.getLdr("DATATYPE"))
     417             :             {
     418           9 :                 auto dataType = block.getLdr("DATATYPE").value().getValue();
     419           3 :                 util::toLower(dataType);
     420             : 
     421           3 :                 if (dataType.find("infrared") != std::string::npos
     422           3 :                     || dataType.find("raman") != std::string::npos)
     423             :                 {
     424           3 :                     metadata.emplace("x.reverse", "true");
     425             :                 }
     426             :             }
     427             :         }
     428           6 :         else if (xUnit == "nanometers" || xUnit == "micrometers"
     429           6 :                  || xUnit == "nm" || xUnit == "um")
     430             :         {
     431           1 :             metadata.emplace("x.label", "Wavelength");
     432             :         }
     433             :     }
     434             : 
     435          69 :     if (auto yUnits = block.getLdr("YUNITS"))
     436             :     {
     437           5 :         metadata.emplace("y.unit", yUnits.value().getValue());
     438             :     }
     439          37 :     else if (auto aUnits = block.getLdr("AUNITS"))
     440             :     {
     441           1 :         metadata.emplace("y.unit", aUnits.value().getValue());
     442             :     }
     443             : 
     444          23 :     if (isPeakData(block))
     445             :     {
     446           1 :         metadata.emplace("plot.style", "sticks");
     447             :     }
     448             : 
     449          23 :     return metadata;
     450             : }
     451             : 
     452           3 : std::map<std::string, std::string> libjdx::jdx::api::JdxConverter::mapMetadata(
     453             :     const Page& page, bool isPeakData)
     454             : {
     455           3 :     std::map<std::string, std::string> metadata{};
     456             : 
     457           3 :     if (page.getDataTable().has_value())
     458             :     {
     459             :         const auto& pageAttributes
     460           3 :             = page.getDataTable().value().getAttributes();
     461           3 :         if (pageAttributes.xAttributes.units.has_value())
     462             :         {
     463             :             metadata.emplace(
     464           3 :                 "x.unit", pageAttributes.xAttributes.units.value());
     465             :         }
     466           3 :         if (pageAttributes.yAttributes.units.has_value())
     467             :         {
     468             :             metadata.emplace(
     469           3 :                 "y.unit", pageAttributes.yAttributes.units.value());
     470             :         }
     471           3 :         if (isPeakData)
     472             :         {
     473           1 :             metadata.emplace("plot.style", "sticks");
     474             :         }
     475             :         else
     476             :         {
     477             :             // don't use VAR_NAMEs for labels for MS
     478           2 :             metadata.emplace("x.label", pageAttributes.xAttributes.symbol);
     479           2 :             metadata.emplace("y.label", pageAttributes.yAttributes.symbol);
     480             :         }
     481             :     }
     482             : 
     483           3 :     return metadata;
     484             : }
     485             : 
     486           2 : libjdx::api::Table libjdx::jdx::api::JdxConverter::mapPeakTable(
     487             :     const libjdx::jdx::PeakTable& peakTable)
     488             : {
     489           4 :     auto peakTableData = peakTable.getData();
     490           2 :     const auto has_w = std::any_of(peakTableData.cbegin(), peakTableData.cend(),
     491           8 :         [](const Peak& peak) { return peak.w.has_value(); });
     492           2 :     const auto has_m = std::any_of(peakTableData.cbegin(), peakTableData.cend(),
     493           8 :         [](const Peak& peak) { return peak.m.has_value(); });
     494             : 
     495           2 :     auto resultPeakTable = libjdx::api::Table{};
     496           2 :     resultPeakTable.columnNames.emplace_back("x", "Peak Position");
     497           2 :     resultPeakTable.columnNames.emplace_back("y", "Intensity");
     498           2 :     if (has_w)
     499             :     {
     500           0 :         resultPeakTable.columnNames.emplace_back("w", "Width");
     501             :     }
     502           2 :     if (has_m)
     503             :     {
     504           0 :         resultPeakTable.columnNames.emplace_back("m", "Multiplicity");
     505             :     }
     506             : 
     507          10 :     for (const auto& peak : peakTable.getData())
     508             :     {
     509          16 :         auto resultPeak = std::map<std::string, std::string>{};
     510           8 :         resultPeak.emplace("x", std::to_string(peak.x));
     511           8 :         resultPeak.emplace("y", std::to_string(peak.y));
     512           8 :         if (peak.w.has_value())
     513             :         {
     514           0 :             resultPeak.emplace("w", std::to_string(peak.w.value()));
     515             :         }
     516           8 :         if (peak.m.has_value())
     517             :         {
     518           0 :             resultPeak.emplace("m", peak.m.value());
     519             :         }
     520           8 :         resultPeakTable.rows.push_back(resultPeak);
     521             :     }
     522           4 :     return resultPeakTable;
     523             : }
     524             : 
     525             : std::vector<libjdx::api::Point2D>
     526           1 : libjdx::jdx::api::JdxConverter::mapPeakTableAsData(
     527             :     const libjdx::jdx::PeakTable& peakTable)
     528             : {
     529           1 :     std::vector<libjdx::api::Point2D> data{};
     530             : 
     531           5 :     for (const auto& peak : peakTable.getData())
     532             :     {
     533           4 :         data.push_back({peak.x, peak.y});
     534             :     }
     535             : 
     536           1 :     return data;
     537             : }
     538             : 
     539           1 : libjdx::api::Table libjdx::jdx::api::JdxConverter::mapDataAsPeakTable(
     540             :     const std::vector<std::pair<double, double>>& xyData)
     541             : {
     542           1 :     auto resultPeakTable = libjdx::api::Table{};
     543             : 
     544           1 :     resultPeakTable.columnNames.emplace_back("x", "Peak Position");
     545           1 :     resultPeakTable.columnNames.emplace_back("y", "Intensity");
     546             : 
     547           5 :     for (const auto& xyPair : xyData)
     548             :     {
     549           8 :         auto resultPeak = std::map<std::string, std::string>{};
     550           4 :         resultPeak.emplace("x", std::to_string(xyPair.first));
     551           4 :         resultPeak.emplace("y", std::to_string(xyPair.second));
     552           4 :         resultPeakTable.rows.push_back(resultPeak);
     553             :     }
     554             : 
     555           1 :     return resultPeakTable;
     556             : }
     557             : 
     558           1 : libjdx::api::Table libjdx::jdx::api::JdxConverter::mapPeakAssignments(
     559             :     const libjdx::jdx::PeakAssignments& peakAssignments)
     560             : {
     561           2 :     auto peakAssignmentsData = peakAssignments.getData();
     562           1 :     const auto has_y = std::any_of(peakAssignmentsData.cbegin(),
     563           1 :         peakAssignmentsData.cend(), [](const PeakAssignment& peakAssignment) {
     564           1 :             return peakAssignment.y.has_value();
     565             :         });
     566           1 :     const auto has_m = std::any_of(peakAssignmentsData.cbegin(),
     567           1 :         peakAssignmentsData.cend(), [](const PeakAssignment& peakAssignment) {
     568           1 :             return peakAssignment.m.has_value();
     569             :         });
     570           1 :     const auto has_w = std::any_of(peakAssignmentsData.cbegin(),
     571           4 :         peakAssignmentsData.cend(), [](const PeakAssignment& peakAssignment) {
     572           4 :             return peakAssignment.w.has_value();
     573             :         });
     574             : 
     575           1 :     auto resultPeakTable = libjdx::api::Table{};
     576           1 :     resultPeakTable.columnNames.emplace_back("x", "Peak Position");
     577           1 :     if (has_y)
     578             :     {
     579           1 :         resultPeakTable.columnNames.emplace_back("y", "Intensity");
     580             :     }
     581           1 :     if (has_m)
     582             :     {
     583           1 :         resultPeakTable.columnNames.emplace_back("m", "Multiplicity");
     584             :     }
     585           1 :     if (has_w)
     586             :     {
     587           0 :         resultPeakTable.columnNames.emplace_back("w", "Width");
     588             :     }
     589           1 :     resultPeakTable.columnNames.emplace_back("a", "Assignment");
     590             : 
     591           5 :     for (const auto& peakAssignment : peakAssignments.getData())
     592             :     {
     593           8 :         auto resultPeak = std::map<std::string, std::string>{};
     594           4 :         resultPeak.emplace("x", std::to_string(peakAssignment.x));
     595           4 :         if (peakAssignment.y.has_value())
     596             :         {
     597           4 :             resultPeak.emplace("y", std::to_string(peakAssignment.y.value()));
     598             :         }
     599           4 :         if (peakAssignment.w.has_value())
     600             :         {
     601           0 :             resultPeak.emplace("w", std::to_string(peakAssignment.w.value()));
     602             :         }
     603           4 :         if (peakAssignment.m.has_value())
     604             :         {
     605           4 :             resultPeak.emplace("m", peakAssignment.m.value());
     606             :         }
     607           4 :         resultPeak.emplace("a", peakAssignment.a);
     608           4 :         resultPeakTable.rows.push_back(resultPeak);
     609             :     }
     610           2 :     return resultPeakTable;
     611             : }
     612             : 
     613           1 : libjdx::api::Node libjdx::jdx::api::JdxConverter::mapAuditTrail(
     614             :     const libjdx::jdx::AuditTrail& auditTrail)
     615             : {
     616           2 :     auto auditEntries = auditTrail.getData();
     617             : 
     618           1 :     const auto has_process = std::any_of(auditEntries.cbegin(),
     619             :         auditEntries.cend(),
     620           1 :         [](const AuditTrailEntry& entry) { return entry.process.has_value(); });
     621           1 :     const auto has_version = std::any_of(auditEntries.cbegin(),
     622             :         auditEntries.cend(),
     623           1 :         [](const AuditTrailEntry& entry) { return entry.version.has_value(); });
     624             : 
     625           2 :     libjdx::api::Table table{};
     626             :     // columns
     627           1 :     table.columnNames.emplace_back("number", "NUMBER");
     628           1 :     table.columnNames.emplace_back("when", "WHEN");
     629           1 :     table.columnNames.emplace_back("who", "WHO");
     630           1 :     table.columnNames.emplace_back("where", "WHERE");
     631           1 :     if (has_process)
     632             :     {
     633           1 :         table.columnNames.emplace_back("process", "PROCESS");
     634             :     }
     635           1 :     if (has_version)
     636             :     {
     637           1 :         table.columnNames.emplace_back("version", "VERSION");
     638             :     }
     639           1 :     table.columnNames.emplace_back("what", "WHAT");
     640             : 
     641             :     // rows
     642           4 :     for (const auto& entry : auditEntries)
     643             :     {
     644             :         std::map<std::string, std::string> row{
     645           3 :             {"number", std::to_string(entry.number)},
     646           3 :             {"when", entry.when},
     647           3 :             {"who", entry.who},
     648           3 :             {"where", entry.where},
     649           3 :             {"what", entry.what},
     650          27 :         };
     651           3 :         if (has_process && entry.process.has_value())
     652             :         {
     653           3 :             row.emplace("process", entry.process.value());
     654             :         }
     655           3 :         if (has_version && entry.version.has_value())
     656             :         {
     657           3 :             row.emplace("version", entry.version.value());
     658             :         }
     659             : 
     660           3 :         table.rows.push_back(row);
     661             :     }
     662             : 
     663           1 :     const auto& auditTrailLabel = auditTrail.getLabel();
     664             : 
     665             :     return {
     666             :         auditTrailLabel,
     667             :         std::vector<libjdx::api::KeyValueParam>{},
     668             :         std::vector<libjdx::api::Point2D>{},
     669             :         std::map<std::string, std::string>{},
     670             :         table,
     671             :         std::vector<std::string>{},
     672           2 :     };
     673             : }
     674             : 
     675             : #ifdef __EMSCRIPTEN__
     676             : EMSCRIPTEN_BINDINGS(JdxConverter)
     677             : {
     678             :     using namespace libjdx::api;
     679             :     using namespace libjdx::jdx::api;
     680             :     using namespace emscripten;
     681             :     class_<JdxConverter, base<Converter>>("JdxConverter")
     682             :         .smart_ptr_constructor(
     683             :             "JdxConverter", &std::make_shared<JdxConverter, const std::string&>)
     684             :         .function("read", &JdxConverter::read);
     685             : }
     686             : #endif

Generated by: LCOV version 1.14