Isis 3 Programmer Reference
LoadCSV.cpp
1 
7 /* SPDX-License-Identifier: CC0-1.0 */
8 
9 #include <QString>
10 #include <vector>
11 #include <numeric>
12 #include <iostream>
13 #include <sstream>
14 
15 #include "LoadCSV.h"
16 #include "FileName.h"
17 #include "IException.h"
18 
19 using namespace std;
20 
21 namespace Isis {
22 
23 
24  LoadCSV::LoadCSV() : _base(), _csvSpecs("LoadCSV"), _data(0,0), _history() { }
25 
26  LoadCSV::LoadCSV(const QString &base, const HiCalConf &conf,
27  const DbProfile &profile) : _base(), _csvSpecs("LoadCSV"),
28  _data(0,0), _history() {
29  load(base, conf, profile);
30  }
31 
32  void LoadCSV::load(const QString &base, const HiCalConf &conf,
33  const DbProfile &profile) {
34 
35  // Initialize the object with necessary info
36  init(base, conf, profile);
37 
38  // Set up access to the CSV table. Note that HiCalConf.getMatrixSource()
39  // method is typically used, but the parsing has been broken up here for
40  // implementation purposes.
41  QString csvfile(conf.filepath(getValue()));
42  addHistory("File", csvfile);
43  CSVReader csv;
44 
45  // Retrieve information regarding the format within the CSV
46  bool colHeader(IsEqual(ConfKey(_csvSpecs,makeKey("Header"), QString("FALSE")), "TRUE"));
47  bool rowHeader(colHeader); // Both default to state of the {BASE}Header
48  if (IsEqual(getValue("ColumnHeader"), "TRUE")) colHeader = true;
49  if (IsEqual(getValue("RowHeader"), "TRUE")) rowHeader = true;
50  if (_csvSpecs.exists(makeKey("ColumnName"))) colHeader = true;
51  if (_csvSpecs.exists(makeKey("RowName") )) rowHeader = true;
52 
53  // Skip lines, comment headers and separator
54  int skip = toInt(ConfKey(_csvSpecs, makeKey("SkipLines"), QString("0")));
55  addHistory("SkipLines", ToString(skip));
56  bool comments = IsEqual(ConfKey(_csvSpecs, makeKey("IgnoreComments"), QString("TRUE")));
57  QString separator = ConfKey(_csvSpecs, makeKey("Separator"), QString(","));
58  if (separator.isEmpty()) separator = ","; // Guarantees content
59 
60  // Apply conditions
61  csv.setComment(comments);
62  csv.setSkip(skip);
63  csv.setHeader(colHeader);
64  csv.setDelimiter(separator[0].toLatin1());
65  if (separator[0] == ' ') csv.setSkipEmptyParts();
66 
67  // Now read the file
68  FileName csvF(csvfile);
69  csvfile = csvF.expanded();
70  try {
71  csv.read(csvfile);
72  } catch (IException &ie) {
73  QString mess = "Could not read CSV file \'" + csvfile + "\'";
74  throw IException(ie, IException::User, mess, _FILEINFO_);
75  }
76 
77  // Now get the data from the CSV table
78  int ncols = csv.columns();
79  int nrows = csv.rows();
80 
81  // Initial conditions selects all rows and columns
82  int startColumn((rowHeader) ? 1 : 0), endColumn(ncols-1);
83  int startRow(0), endRow(nrows-1);
84 
85  // Update columns
86  QString colName(getValue("ColumnName"));
87  if (!colName.isEmpty()) {
88  addHistory("ColumnName", colName);
89  CSVReader::CSVAxis chead = csv.getHeader();
90  startColumn = getAxisIndex(colName, chead);
91  if (startColumn < 0) {
92  QString mess = "Column name " + colName +
93  " not found in CSV file " + csvfile;
94  throw IException(IException::User, mess, _FILEINFO_);
95  }
96  endColumn = startColumn;
97  addHistory("ColumnIndex", ToString(startColumn));
98  }
99  else if (!(getValue("ColumnIndex").isEmpty())) {
100  startColumn = ToInteger(getValue("ColumnIndex")) +
101  ((rowHeader) ? 1 : 0);
102  endColumn = startColumn;
103  addHistory("ColumnStart", ToString(startColumn));
104  addHistory("ColumnEnd", ToString(endColumn));
105  }
106 
107  // Update row indicies
108  QString rowName(getValue("RowName"));
109  if (!rowName.isEmpty()) {
110  addHistory("RowName", rowName);
111  if (!rowHeader) {
112  QString mess = "Row name given but config does not specify presence of row header!";
113  throw IException(IException::User, mess, _FILEINFO_);
114  }
115  CSVReader::CSVAxis rhead = csv.getColumn(0);
116  startRow = getAxisIndex(rowName, rhead);
117  if (startRow < 0) {
118  QString mess = "Row name " + rowName + " not found in CSV file " + csvfile;
119  throw IException(IException::User, mess, _FILEINFO_);
120  }
121  endRow = startRow;
122  addHistory("RowIndex", ToString(startRow));
123  }
124  else if (!(getValue("RowIndex").isEmpty())) {
125  startRow = ToInteger(getValue("RowIndex"));
126  if (rowHeader) startRow++;
127  endRow = startRow;
128  addHistory("RowStart", ToString(startRow));
129  addHistory("RowEnd", ToString(endRow));
130  }
131 
132  // Now ready to read all row/columns and convert to matrix
133  int drows(endRow-startRow+1);
134  int dcols(endColumn-startColumn+1);
135  HiMatrix d(drows, dcols);
136  vector<QString> errors;
137  for (int r = startRow, hr = 0 ; r <= endRow ; r++, hr++) {
138  CSVReader::CSVAxis row = csv.getRow(r);
139  for (int c = startColumn, hc = 0 ; c <= endColumn ; c++, hc++) {
140  try {
141  d[hr][hc] = ToDouble(row[c]);
142  }
143  catch (...) {
144  std::ostringstream mess;
145  mess << "Invalid real value (" << row[c] << ") in row index " << r;
146  if (!rowName.isEmpty()) mess << " (Name:" << rowName << ")";
147  mess << ", column index " << c;
148  if (!colName.isEmpty()) mess << " (Name:" << colName << ")";
149  errors.push_back(mess.str().c_str());
150  d[hr][hc] = Null;
151  }
152  }
153  }
154 
155  // Save data anyway
156  _data = d;
157 
158  // Check for errors
159  if (errors.size() > 0) {
160  //iException::Clear(); Not sure how this could ever do anything
161  std::ostringstream mess;
162  mess << "Conversion errors in CSV file " + csvfile + ": Errors: ";
163 
164  std::vector<QString>::const_iterator it = errors.begin();
165 
166  while (it != errors.end()) {
167  mess << *it << "; ";
168  it++;
169  }
170  throw IException(IException::User, mess.str().c_str(), _FILEINFO_);
171  }
172  return;
173  }
174 
175 
176  QString LoadCSV::filename() const {
177  return (getValue());
178  }
179 
180  int LoadCSV::size() const {
181  return (_data.dim1() * _data.dim2());
182  }
183 
184  bool LoadCSV::validateSize(const int &expected, const bool &throw_on_error)
185  const {
186  if (expected != size()) {
187  if (!throw_on_error) return (false);
188  ostringstream mess;
189  mess << "Invalid count (Expected: " << expected << ", Received: "
190  << size() << ") in CSV file " << getValue();
191  throw IException(IException::User, mess.str(), _FILEINFO_);
192  }
193  return (true);
194  }
195 
196  HiVector LoadCSV::getVector() const {
197  HiVector v(size(), const_cast<double *> (_data[0]));
198  return (v.copy());
199  }
200 
201  HiMatrix LoadCSV::getMatrix() const {
202  return (_data.copy());
203  }
204 
205 
206  void LoadCSV::History(HiHistory &history) const {
207  std::ostringstream mess;
208  mess << "LoadCSV(";
209  QString comma("");
210  for (unsigned int i = 0 ; i < _history.size() ; i++) {
211  mess << comma << _history[i];
212  comma = ",";
213  }
214  mess << ")";
215  history.add(mess.str().c_str());
216  return;
217  }
218 
219  void LoadCSV::init(const QString &base, const HiCalConf &conf,
220  const DbProfile &profile) {
221  _base = base;
222  _csvSpecs = ResolveKeys(base, conf, profile);
223  _history.clear();
224  return;
225  }
226 
227  void LoadCSV::addHistory(const QString &element,
228  const QString &desc) {
229  std::ostringstream mess;
230  mess << element << "[" << desc << "]";
231  _history.push_back(mess.str().c_str());
232  }
233 
234  void LoadCSV::getKeyList(const QString &base,
235  std::vector<QString> &keys) const {
236  keys.clear();
237  keys.push_back(base);
238  keys.push_back(base+"IgnoreComments");
239  keys.push_back(base+"ColumnHeader");
240  keys.push_back(base+"ColumnName");
241  keys.push_back(base+"ColumnIndex");
242  keys.push_back(base+"RowHeader");
243  keys.push_back(base+"RowName");
244  keys.push_back(base+"RowIndex");
245  keys.push_back(base+"SkipLines");
246  keys.push_back(base+"Header");
247  keys.push_back(base+"Separator");
248  return;
249  }
250 
251  DbProfile LoadCSV::ResolveKeys(const QString &base, const HiCalConf &conf,
252  const DbProfile &prof) const {
253  vector<QString> keys;
254  getKeyList(base, keys);
255  DbProfile keyprof("LoadCSV");
256  for (unsigned int i = 0 ; i < keys.size() ; i++) {
257  QString kvalue = ParsedKey(keys[i], conf, prof);
258  if (!kvalue.isEmpty()) keyprof.add(keys[i], kvalue);
259  }
260  return (keyprof);
261  }
262 
263  QString LoadCSV::ParsedKey(const QString &key, const HiCalConf &conf,
264  const DbProfile &prof) const {
265  QString value("");
266  if (prof.exists(key)) {
267  value = conf.resolve(prof(key), prof);
268  }
269  return (value);
270  }
271 
272  QString LoadCSV::makeKey(const QString &suffix) const {
273  QString key = _base + suffix;
274  return (key);
275  }
276 
277  QString LoadCSV::getValue(const QString &suffix) const {
278  QString key = makeKey(suffix);
279  QString value("");
280  if (_csvSpecs.exists(key)) value = _csvSpecs(key);
281  return (value);
282  }
283 
284  int LoadCSV::getAxisIndex(const QString &name,
285  const CSVReader::CSVAxis &header) const {
286  QString head = name.trimmed();
287  for (int i = 0 ; i < header.dim() ; i++) {
288  if (head.toLower() == header[i].toLower().trimmed()) {
289  return (i);
290  }
291  }
292  return (-1);
293  }
294 
295 } // namespace ISIS
Isis::IsEqual
bool IsEqual(const QString &v1, const QString &v2="TRUE")
Shortened string equality test.
Definition: HiCalUtil.h:258
Isis::ToString
QString ToString(const T &value)
Helper function to convert values to strings.
Definition: HiCalUtil.h:246
Isis::HiMatrix
TNT::Array2D< double > HiMatrix
2-D buffer
Definition: HiCalTypes.h:28
Isis::ConfKey
T ConfKey(const DbProfile &conf, const QString &keyname, const T &defval, int index=0)
Find a keyword in a profile using default for non-existant keywords.
Definition: HiCalUtil.h:205
Isis::toInt
int toInt(const QString &string)
Global function to convert from a string to an integer.
Definition: IString.cpp:93
Isis::ToInteger
int ToInteger(const T &value)
Helper function to convert values to Integers.
Definition: HiCalUtil.h:222
Isis::HiVector
TNT::Array1D< double > HiVector
1-D Buffer
Definition: HiCalTypes.h:27
Isis::Null
const double Null
Value for an Isis Null pixel.
Definition: SpecialPixel.h:95
Isis::ToDouble
double ToDouble(const T &value)
Helper function to convert values to doubles.
Definition: HiCalUtil.h:234
std
Namespace for the standard library.
Isis
This is free and unencumbered software released into the public domain.
Definition: Apollo.h:16