OGS
CsvInterface.cpp
Go to the documentation of this file.
1
14#include "CsvInterface.h"
15
16#include <algorithm>
17#include <numeric>
18
19#include "GeoLib/Point.h"
20
21namespace FileIO
22{
24
25std::vector<std::string> CsvInterface::getColumnNames(std::string const& fname,
26 char const delim)
27{
28 std::ifstream in(fname.c_str());
29
30 if (!in.is_open())
31 {
32 ERR("CsvInterface::getColumnNames(): Could not open file {:s}.", fname);
33 return std::vector<std::string>();
34 }
35 std::string line;
36 if (!std::getline(in, line))
37 {
38 ERR("CsvInterface::getColumnNames(): Could not read line from file "
39 "{:s}. Is it empty?",
40 fname);
41 return {};
42 }
43
44 if (delim == '\n')
45 {
46 return {};
47 }
48
49 std::list<std::string> fields = BaseLib::splitString(line, delim);
50 if (fields.size() < 2)
51 {
52 for (char const d : {'\t', ';', ','})
53 {
54 fields = BaseLib::splitString(line, d);
55 if (fields.size() > 1)
56 {
57 break;
58 }
59 }
60 }
61 return {begin(fields), end(fields)};
62}
63
64int CsvInterface::readPoints(std::string const& fname, char delim,
65 std::vector<GeoLib::Point*>& points)
66{
67 std::ifstream in(fname.c_str());
68
69 if (!in.is_open())
70 {
71 ERR("CsvInterface::readPoints(): Could not open file {:s}.", fname);
72 return -1;
73 }
74
75 std::string line;
76 std::getline(in, line);
77
78 std::size_t line_count(0);
79 std::size_t error_count(0);
80 std::list<std::string>::const_iterator it;
81 while (std::getline(in, line))
82 {
83 line_count++;
84 std::list<std::string> const fields = BaseLib::splitString(line, delim);
85
86 if (fields.size() < 3)
87 {
88 ERR("Line {:d} contains not enough columns of data. Skipping "
89 "line...",
90 line_count);
91 error_count++;
92 continue;
93 }
94 it = fields.begin();
95 try
96 {
97 std::array<double, 3> point{};
98 point[0] = std::stod(*it);
99 point[1] = std::stod(*(++it));
100 point[2] = std::stod(*(++it));
101 points.push_back(new GeoLib::Point(point[0], point[1], point[2]));
102 }
103 catch (const std::invalid_argument&)
104 {
105 ERR("Error converting data to coordinates in line {:d}.",
106 line_count);
107 }
108 }
109 return error_count;
110}
111
112int CsvInterface::readPoints(std::string const& fname, char delim,
113 std::vector<GeoLib::Point*>& points,
114 std::string const& x_column_name,
115 std::string const& y_column_name,
116 std::string const& z_column_name)
117{
118 std::ifstream in(fname.c_str());
119 std::array<std::string, 3> const column_names = {
120 {x_column_name, y_column_name, z_column_name}};
121
122 if (!in.is_open())
123 {
124 ERR("CsvInterface::readPoints(): Could not open file {:s}.", fname);
125 return -1;
126 }
127
128 std::string line;
129 std::getline(in, line);
130 std::array<std::size_t, 3> const column_idx = {
131 {CsvInterface::findColumn(line, delim, x_column_name),
132 CsvInterface::findColumn(line, delim, y_column_name),
133 (z_column_name.empty())
134 ? CsvInterface::findColumn(line, delim, y_column_name)
135 : CsvInterface::findColumn(line, delim, z_column_name)}};
136
137 for (std::size_t i = 0; i < 3; ++i)
138 {
139 if (column_idx[i] == std::numeric_limits<std::size_t>::max())
140 {
141 ERR("Column '{:s}' not found in file header.", column_names[i]);
142 return -1;
143 }
144 }
145
146 return readPoints(in, delim, points, column_idx);
147}
148
149int CsvInterface::readPoints(std::string const& fname, char delim,
150 std::vector<GeoLib::Point*>& points,
151 std::size_t x_column_idx, std::size_t y_column_idx,
152 std::size_t z_column_idx)
153{
154 std::ifstream in(fname.c_str());
155
156 if (!in.is_open())
157 {
158 ERR("CsvInterface::readPoints(): Could not open file {:s}.", fname);
159 return -1;
160 }
161
162 if (z_column_idx == std::numeric_limits<std::size_t>::max())
163 {
164 z_column_idx = y_column_idx;
165 }
166 std::array<std::size_t, 3> const column_idx = {
167 {x_column_idx, y_column_idx, z_column_idx}};
168
169 return readPoints(in, delim, points, column_idx);
170}
171
172int CsvInterface::readPoints(std::ifstream& in, char delim,
173 std::vector<GeoLib::Point*>& points,
174 std::array<std::size_t, 3> const& column_idx)
175{
176 std::array<std::size_t, 3> order = {{0, 1, 2}};
177 std::sort(order.begin(), order.end(),
178 [&column_idx](std::size_t idx1, std::size_t idx2)
179 { return column_idx[idx1] < column_idx[idx2]; });
180 std::array<std::size_t, 3> const column_advance = {
181 {column_idx[order[0]], column_idx[order[1]] - column_idx[order[0]],
182 column_idx[order[2]] - column_idx[order[1]]}};
183
184 std::string line;
185 std::size_t line_count(0);
186 std::size_t error_count(0);
187 std::list<std::string>::const_iterator it;
188
189 while (std::getline(in, line))
190 {
191 line_count++;
192 std::list<std::string> const fields = BaseLib::splitString(line, delim);
193
194 if (fields.size() < column_idx[order[2]] + 1)
195 {
196 ERR("Line {:d} contains not enough columns of data. Skipping "
197 "line...",
198 line_count);
199 error_count++;
200 continue;
201 }
202
203 it = fields.begin();
204 try
205 {
206 std::advance(it, column_advance[0]);
207 std::array<double, 3> point{};
208 point[order[0]] = std::stod(*it);
209 std::advance(it, column_advance[1]);
210 point[order[1]] = std::stod(*it);
211 std::advance(it, column_advance[2]);
212 point[order[2]] =
213 (column_idx[1] == column_idx[2]) ? 0 : std::stod(*it);
214 points.push_back(new GeoLib::Point(point[0], point[1], point[2]));
215 }
216 catch (const std::invalid_argument&)
217 {
218 ERR("Error converting data to coordinates in line {:d}.",
219 line_count);
220 error_count++;
221 }
222 }
223 return error_count;
224}
225
226std::size_t CsvInterface::findColumn(std::string const& line, char delim,
227 std::string const& column_name)
228{
229 std::list<std::string> const fields = BaseLib::splitString(line, delim);
230 if (fields.empty())
231 {
232 return std::numeric_limits<std::size_t>::max();
233 }
234
235 std::size_t count(0);
236 for (const auto& field : fields)
237 {
238 if (field == column_name)
239 {
240 break;
241 }
242
243 count++;
244 }
245
246 if (count == fields.size())
247 {
248 return std::numeric_limits<std::size_t>::max();
249 }
250
251 return count;
252}
253
255{
256 std::vector<int> idx_vec(s);
257 std::iota(idx_vec.begin(), idx_vec.end(), 0);
258 addVectorForWriting("Index", idx_vec);
259}
260
262{
263 if (_data.empty())
264 {
265 ERR("CsvInterface::write() - No data to write.");
266 return false;
267 }
268
269 std::size_t const n_vecs(_data.size());
270 std::size_t const vec_size(getVectorSize(0));
271
272 if (_writeCsvHeader)
273 {
274 out << _vec_names[0];
275 for (std::size_t i = 1; i < n_vecs; ++i)
276 {
277 out << "\t" << _vec_names[i];
278 }
279 out << "\n";
280 }
281
282 for (std::size_t j = 0; j < vec_size; ++j)
283 {
284 writeValue(0, j);
285 for (std::size_t i = 1; i < n_vecs; ++i)
286 {
287 out << "\t";
288 writeValue(i, j);
289 }
290 out << "\n";
291 }
292 return true;
293}
294
295std::size_t CsvInterface::getVectorSize(std::size_t idx) const
296{
297 if (_data[idx].type() == typeid(std::vector<std::string>))
298 {
299 return std::any_cast<std::vector<std::string>>(_data[idx]).size();
300 }
301 if (_data[idx].type() == typeid(std::vector<double>))
302 {
303 return std::any_cast<std::vector<double>>(_data[idx]).size();
304 }
305 if (_data[idx].type() == typeid(std::vector<int>))
306 {
307 return std::any_cast<std::vector<int>>(_data[idx]).size();
308 }
309 return 0;
310}
311
312void CsvInterface::writeValue(std::size_t vec_idx, std::size_t in_vec_idx)
313{
314 if (_data[vec_idx].type() == typeid(std::vector<std::string>))
315 {
317 _data[vec_idx])[in_vec_idx];
318 }
319 else if (_data[vec_idx].type() == typeid(std::vector<double>))
320 {
321 out << std::any_cast<std::vector<double>>(_data[vec_idx])[in_vec_idx];
322 }
323 else if (_data[vec_idx].type() == typeid(std::vector<int>))
324 {
325 out << std::any_cast<std::vector<int>>(_data[vec_idx])[in_vec_idx];
326 }
327}
328
329} // end namespace FileIO
Definition of the CsvInterface class.
Definition of the Point class.
void ERR(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:45
std::ostringstream out
The stream to write to.
Definition Writer.h:47
std::vector< std::any > _data
void writeValue(std::size_t vec_idx, std::size_t in_vec_idx)
std::vector< std::string > _vec_names
static std::vector< std::string > getColumnNames(std::string const &fname, char delim)
bool addVectorForWriting(std::string const &vec_name, std::vector< T > const &vec)
static std::size_t findColumn(std::string const &line, char delim, std::string const &column_name)
void addIndexVectorForWriting(std::size_t s)
Adds an index vector of size s to the CSV file.
std::size_t getVectorSize(std::size_t idx) const
Returns the size of the vector with the given index.
CsvInterface()
Constructor (only needed for writing files)
bool write() override
Writes the CSV file.
static int readPoints(std::string const &fname, char delim, std::vector< GeoLib::Point * > &points)
std::vector< std::string > splitString(std::string const &str)