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