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
19using namespace std;
20
21namespace 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
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16
int toInt(const QString &string)
Global function to convert from a string to an integer.
Definition IString.cpp:93
TNT::Array2D< double > HiMatrix
2-D buffer
Definition HiCalTypes.h:28
const double Null
Value for an Isis Null pixel.
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
TNT::Array1D< double > HiVector
1-D Buffer
Definition HiCalTypes.h:27
QString ToString(const T &value)
Helper function to convert values to strings.
Definition HiCalUtil.h:246
double ToDouble(const T &value)
Helper function to convert values to doubles.
Definition HiCalUtil.h:234
int ToInteger(const T &value)
Helper function to convert values to Integers.
Definition HiCalUtil.h:222
bool IsEqual(const QString &v1, const QString &v2="TRUE")
Shortened string equality test.
Definition HiCalUtil.h:258
Namespace for the standard library.