LCOV - code coverage report
Current view: top level - jdx - DataTable.cpp (source / functions) Hit Total Coverage
Test: coverage_filtered.info Lines: 138 141 97.9 %
Date: 2025-06-15 16:18:27 Functions: 14 14 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/DataTable.hpp"
      19             : #include "jdx/ParseException.hpp"
      20             : #include "util/DataParser.hpp"
      21             : #include "util/LdrUtils.hpp"
      22             : #include "util/PeakTableParser.hpp"
      23             : #include "util/StringUtils.hpp"
      24             : 
      25             : #include <limits>
      26             : #include <tuple>
      27             : 
      28          67 : libjdx::jdx::DataTable::DataTable(std::string label, std::string variableList,
      29             :     std::optional<std::string> plotDescriptor,
      30             :     const std::vector<StringLdr>& blockLdrs,
      31             :     const std::vector<NTuplesAttributes>& nTuplesAttributes,
      32             :     const std::vector<StringLdr>& pageLdrs, io::TextReader& reader,
      33          67 :     std::optional<std::string>& nextLine)
      34          67 :     : Data2D(std::move(label), std::move(variableList), reader)
      35         137 :     , m_plotDescriptor{std::move(plotDescriptor)}
      36             : {
      37             :     // extract permitted variable lists from mapping keys
      38         134 :     std::vector<std::string> permittedVarLists;
      39          67 :     permittedVarLists.reserve(s_varListMapping.size());
      40         670 :     for (const auto& keyValuePair : s_varListMapping)
      41             :     {
      42         603 :         permittedVarLists.emplace_back(keyValuePair.first);
      43             :     }
      44             :     // validate label and variable list
      45          69 :     validateInput(getLabel(), getVariableList(), s_label, permittedVarLists);
      46             :     // validate plot descriptor if present
      47          66 :     if (m_plotDescriptor)
      48             :     {
      49          64 :         determinePlotDescriptor(m_plotDescriptor.value());
      50             :     }
      51             :     // parse
      52          66 :     parse(blockLdrs, nTuplesAttributes, pageLdrs, nextLine);
      53          66 : }
      54             : 
      55             : const std::optional<std::string>&
      56          12 : libjdx::jdx::DataTable::getPlotDescriptor() const
      57             : {
      58          12 :     return m_plotDescriptor;
      59             : }
      60             : 
      61             : const libjdx::jdx::DataTable::Attributes&
      62          18 : libjdx::jdx::DataTable::getAttributes() const
      63             : {
      64          18 :     return m_mergedAttributes;
      65             : }
      66             : 
      67          11 : std::vector<std::pair<double, double>> libjdx::jdx::DataTable::getData() const
      68             : {
      69          11 :     auto variableList = determineVariableList(getVariableList());
      70          22 :     auto dataTableParams = m_mergedAttributes;
      71             : 
      72          11 :     if (variableList == VariableList::XYXY || variableList == VariableList::XRXR
      73           7 :         || variableList == VariableList::XIXI)
      74             :     {
      75           4 :         auto xFactor = dataTableParams.xAttributes.factor.value_or(1.0);
      76           4 :         auto yFactor = dataTableParams.yAttributes.factor.value_or(1.0);
      77           4 :         auto nPoints = dataTableParams.yAttributes.varDim;
      78             :         return Data2D::parseXyXyData(
      79           4 :             getLabel(), getReader(), xFactor, yFactor, nPoints);
      80             :     }
      81             : 
      82           7 :     auto firstX = dataTableParams.xAttributes.first.value();
      83           7 :     auto lastX = dataTableParams.xAttributes.last.value();
      84           7 :     auto nPoints = dataTableParams.yAttributes.varDim.value();
      85           7 :     auto yFactor = dataTableParams.yAttributes.factor.value_or(1.0);
      86             :     return Data2D::parseXppYYData(
      87           7 :         getLabel(), getReader(), firstX, lastX, yFactor, nPoints);
      88             : }
      89             : 
      90          66 : void libjdx::jdx::DataTable::parse(const std::vector<StringLdr>& blockLdrs,
      91             :     const std::vector<NTuplesAttributes>& nTuplesVars,
      92             :     const std::vector<StringLdr>& pageLdrs,
      93             :     std::optional<std::string>& nextLine)
      94             : {
      95          66 :     auto variableList = parseDataTableVars().first;
      96             : 
      97         584 :     auto findNTuplesIndex = [&nTuplesVars](const std::string& symbol) {
      98         226 :         for (size_t i = 0; i < nTuplesVars.size(); ++i)
      99             :         {
     100         226 :             if (nTuplesVars.at(i).symbol == symbol)
     101             :             {
     102         132 :                 return i;
     103             :             }
     104             :         }
     105             :         throw ParseException(
     106           0 :             "Could not find NTUPLES parameters for SYMBOL: " + symbol);
     107          66 :     };
     108             : 
     109          66 :     auto xColumnIndex = std::numeric_limits<size_t>::max();
     110          66 :     auto yColumnIndex = std::numeric_limits<size_t>::max();
     111          66 :     if (variableList == VariableList::XppYY
     112          64 :         || variableList == VariableList::XYXY)
     113             :     {
     114          21 :         xColumnIndex = findNTuplesIndex("X");
     115          21 :         yColumnIndex = findNTuplesIndex("Y");
     116             :     }
     117          45 :     else if (variableList == VariableList::XppRR)
     118             :     {
     119          21 :         xColumnIndex = findNTuplesIndex("X");
     120          21 :         yColumnIndex = findNTuplesIndex("R");
     121             :     }
     122          24 :     else if (variableList == VariableList::XppII)
     123             :     {
     124          21 :         xColumnIndex = findNTuplesIndex("X");
     125          21 :         yColumnIndex = findNTuplesIndex("I");
     126             :     }
     127           3 :     else if (variableList == VariableList::T2ppRR)
     128             :     {
     129           1 :         xColumnIndex = findNTuplesIndex("T2");
     130           1 :         yColumnIndex = findNTuplesIndex("R");
     131             :     }
     132           2 :     else if (variableList == VariableList::T2ppII)
     133             :     {
     134           1 :         xColumnIndex = findNTuplesIndex("T2");
     135           1 :         yColumnIndex = findNTuplesIndex("I");
     136             :     }
     137           1 :     else if (variableList == VariableList::F2ppYY)
     138             :     {
     139           1 :         xColumnIndex = findNTuplesIndex("F2");
     140           1 :         yColumnIndex = findNTuplesIndex("Y");
     141             :     }
     142             :     else
     143             :     {
     144             :         // should never happen
     145             :         throw ParseException(
     146           0 :             "Unsupported variabe list in DATA TABLE: " + getVariableList());
     147             :     }
     148             : 
     149         132 :     auto xNTuplesAttrs = nTuplesVars.at(xColumnIndex);
     150         132 :     auto yNTuplesAttrs = nTuplesVars.at(yColumnIndex);
     151             : 
     152         132 :     auto mergedXVars = mergeVars(blockLdrs, xNTuplesAttrs, pageLdrs);
     153         132 :     auto mergedYVars = mergeVars(blockLdrs, yNTuplesAttrs, pageLdrs);
     154             : 
     155             :     // special treatment for "FIRST" page LDR if present
     156             :     // this is described in the README for the JCAMP-DX nD-NMR test file round
     157             :     // robin
     158          66 :     mergePageFirstLdr(mergedXVars, pageLdrs, xColumnIndex);
     159          66 :     mergePageFirstLdr(mergedYVars, pageLdrs, yColumnIndex);
     160             : 
     161          66 :     m_mergedAttributes = {mergedXVars, mergedYVars};
     162             : 
     163          66 :     auto& reader = getReader();
     164          66 :     util::skipToNextLdr(reader, nextLine, true);
     165          66 : }
     166             : 
     167             : std::pair<libjdx::jdx::Data2D::VariableList,
     168             :     std::optional<libjdx::jdx::DataTable::PlotDescriptor>>
     169          66 : libjdx::jdx::DataTable::parseDataTableVars()
     170             : {
     171          66 :     auto varType = determineVariableList(getVariableList());
     172          66 :     if (!m_plotDescriptor.has_value() || m_plotDescriptor.value().empty())
     173             :     {
     174           2 :         return {varType, std::nullopt};
     175             :     }
     176          64 :     auto plotDesc = determinePlotDescriptor(m_plotDescriptor.value());
     177          64 :     return {varType, plotDesc};
     178             : }
     179             : 
     180          77 : libjdx::jdx::Data2D::VariableList libjdx::jdx::DataTable::determineVariableList(
     181             :     const std::string& varList)
     182             : {
     183          77 :     return findValue(s_varListMapping, varList, "variable list");
     184             : }
     185             : 
     186             : libjdx::jdx::DataTable::PlotDescriptor
     187         128 : libjdx::jdx::DataTable::determinePlotDescriptor(
     188             :     const std::string& plotDescriptor)
     189             : {
     190         256 :     return findValue(
     191         256 :         s_plotDescriptorMapping, plotDescriptor, "plot descriptor");
     192             : }
     193             : 
     194         132 : libjdx::jdx::NTuplesAttributes libjdx::jdx::DataTable::mergeVars(
     195             :     const std::vector<StringLdr>& blockLdrs,
     196             :     const NTuplesAttributes& nTuplesVars,
     197             :     const std::vector<StringLdr>& pageLdrs)
     198             : {
     199         132 :     auto outputVars = nTuplesVars;
     200         132 :     outputVars.applicationAttributes.clear();
     201             : 
     202         132 :     if (std::any_of(s_xSymbols.begin(), s_xSymbols.end(),
     203         268 :             [&nTuplesVars](
     204         268 :                 const std::string& s) { return s == nTuplesVars.symbol; }))
     205             :     {
     206             :         // use values from block relevant for abscissa
     207             :         std::map<std::string, std::optional<std::string>&> stringMapping{
     208          66 :             {"XUNITS", outputVars.units},
     209         264 :         };
     210             :         std::map<std::string, std::optional<double>&> doubleMapping{
     211          66 :             {"FIRSTX", outputVars.first},
     212          66 :             {"LASTX", outputVars.last},
     213          66 :             {"MINX", outputVars.min},
     214          66 :             {"MAXX", outputVars.max},
     215          66 :             {"XFACTOR", outputVars.factor},
     216         528 :         };
     217             :         std::map<std::string, std::optional<uint64_t>&> uint64Mapping{
     218          66 :             {"NPOINTS", outputVars.varDim},
     219         198 :         };
     220             : 
     221             :         // fill in block params for missing NTUPLE attributes
     222          66 :         mergeLdrs(
     223             :             blockLdrs, stringMapping, doubleMapping, uint64Mapping, false);
     224             : 
     225             :         // replace with page LDR values if available
     226          66 :         mergeLdrs(pageLdrs, stringMapping, doubleMapping, uint64Mapping, true);
     227             :     }
     228          66 :     else if (std::any_of(s_ySymbols.begin(), s_ySymbols.end(),
     229         132 :                  [&nTuplesVars](
     230         132 :                      const std::string& s) { return s == nTuplesVars.symbol; }))
     231             :     {
     232             :         // use values from block relevant for ordinate
     233             :         std::map<std::string, std::optional<std::string>&> stringMapping{
     234          66 :             {"YUNITS", outputVars.units},
     235         264 :         };
     236             :         std::map<std::string, std::optional<double>&> doubleMapping{
     237          66 :             {"FIRSTY", outputVars.first},
     238          66 :             {"LASTY", outputVars.last},
     239          66 :             {"MINY", outputVars.min},
     240          66 :             {"MAXY", outputVars.max},
     241          66 :             {"YFACTOR", outputVars.factor},
     242         528 :         };
     243             :         std::map<std::string, std::optional<uint64_t>&> uint64Mapping{
     244          66 :             {"NPOINTS", outputVars.varDim},
     245         198 :         };
     246             :         // Also check for other symbols but Y? Does not seem relevant for NMR
     247             :         // and MS.
     248             : 
     249             :         // fill in block params for missing NTUPLE attributes
     250          66 :         mergeLdrs(
     251             :             blockLdrs, stringMapping, doubleMapping, uint64Mapping, false);
     252             : 
     253             :         // replace with page LDR values if available
     254          66 :         mergeLdrs(pageLdrs, stringMapping, doubleMapping, uint64Mapping, true);
     255             :     }
     256             :     else
     257             :     {
     258             :         throw ParseException("Unexpected symbol found during parsing of PAGE: "
     259           0 :                              + nTuplesVars.symbol);
     260             :     }
     261             : 
     262         132 :     return outputVars;
     263             : }
     264             : 
     265         264 : void libjdx::jdx::DataTable::mergeLdrs(const std::vector<StringLdr>& ldrs,
     266             :     std::map<std::string, std::optional<std::string>&> stringMapping,
     267             :     std::map<std::string, std::optional<double>&> doubleMapping,
     268             :     std::map<std::string, std::optional<uint64_t>&> uint64Mapping, bool replace)
     269             : {
     270         778 :     for (const auto& ldr : ldrs)
     271             :     {
     272         514 :         if (stringMapping.count(ldr.getLabel()) > 0)
     273             :         {
     274           4 :             auto& field = stringMapping.at(ldr.getLabel());
     275           4 :             if (replace || !field || field.value().empty())
     276             :             {
     277           4 :                 field = ldr.getValue();
     278             :             }
     279             :         }
     280         510 :         else if (doubleMapping.count(ldr.getLabel()) > 0)
     281             :         {
     282          20 :             auto& field = doubleMapping.at(ldr.getLabel());
     283          20 :             if (replace || !field)
     284             :             {
     285          20 :                 field = std::stod(ldr.getValue());
     286             :             }
     287             :         }
     288         490 :         else if (uint64Mapping.count(ldr.getLabel()) > 0)
     289             :         {
     290          32 :             auto& field = uint64Mapping.at(ldr.getLabel());
     291          32 :             if (replace || !field)
     292             :             {
     293          32 :                 field = std::stol(ldr.getValue());
     294             :             }
     295             :         }
     296             :     }
     297         264 : }
     298             : 
     299         132 : void libjdx::jdx::DataTable::mergePageFirstLdr(
     300             :     libjdx::jdx::NTuplesAttributes& mergedVars,
     301             :     const std::vector<StringLdr>& pageLdrs, size_t columnIndex)
     302             : {
     303         184 :     for (const auto& ldr : pageLdrs)
     304             :     {
     305          58 :         if ("FIRST" == ldr.getLabel())
     306             :         {
     307          18 :             auto segments = util::split(ldr.getValue(), ",", true);
     308           6 :             if (segments.size() > columnIndex)
     309             :             {
     310           6 :                 auto value = std::stod(segments.at(columnIndex));
     311           6 :                 mergedVars.first = value;
     312             :             }
     313           6 :             break;
     314             :         }
     315             :     }
     316         132 : }

Generated by: LCOV version 1.14