##// END OF EJS Templates
Fixes compilation
Alexandre Leroux -
r943:e9b5130d9068
parent child
Show More
@@ -1,385 +1,387
1 1 #include "AmdaResultParserHelper.h"
2 2
3 3 #include <Common/DateUtils.h>
4 4 #include <Common/SortUtils.h>
5 5
6 6 #include <Data/ScalarSeries.h>
7 7 #include <Data/SpectrogramSeries.h>
8 8 #include <Data/Unit.h>
9 9 #include <Data/VectorSeries.h>
10 10
11 11 #include <QtCore/QDateTime>
12 12 #include <QtCore/QRegularExpression>
13 13
14 #include <functional>
15
14 16 Q_LOGGING_CATEGORY(LOG_AmdaResultParserHelper, "AmdaResultParserHelper")
15 17
16 18 namespace {
17 19
18 20 // ///////// //
19 21 // Constants //
20 22 // ///////// //
21 23
22 24 /// Separator between values in a result line
23 25 const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")};
24 26
25 27 /// Format for dates in result files
26 28 const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
27 29
28 30 // /////// //
29 31 // Methods //
30 32 // /////// //
31 33
32 34 /**
33 35 * Checks that the properties contain a specific unit and that this unit is valid
34 36 * @param properties the properties map in which to search unit
35 37 * @param key the key to search for the unit in the properties
36 38 * @param errorMessage the error message to log in case the unit is invalid
37 39 * @return true if the unit is valid, false it it's invalid or was not found in the properties
38 40 */
39 41 bool checkUnit(const Properties &properties, const QString &key, const QString &errorMessage)
40 42 {
41 43 auto unit = properties.value(key).value<Unit>();
42 44 if (unit.m_Name.isEmpty()) {
43 45 qCWarning(LOG_AmdaResultParserHelper()) << errorMessage;
44 46 return false;
45 47 }
46 48
47 49 return true;
48 50 }
49 51
50 52 QDateTime dateTimeFromString(const QString &stringDate) noexcept
51 53 {
52 54 #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
53 55 return QDateTime::fromString(stringDate, Qt::ISODateWithMs);
54 56 #else
55 57 return QDateTime::fromString(stringDate, DATE_FORMAT);
56 58 #endif
57 59 }
58 60
59 61 /// Converts a string date to a double date
60 62 /// @return a double that represents the date in seconds, NaN if the string date can't be converted
61 63 double doubleDate(const QString &stringDate) noexcept
62 64 {
63 65 // Format: yyyy-MM-ddThh:mm:ss.zzz
64 66 auto dateTime = dateTimeFromString(stringDate);
65 67 dateTime.setTimeSpec(Qt::UTC);
66 68 return dateTime.isValid() ? DateUtils::secondsSinceEpoch(dateTime)
67 69 : std::numeric_limits<double>::quiet_NaN();
68 70 }
69 71
70 72 /**
71 73 * Reads a line from the AMDA file and tries to extract a x-axis data and value data from it
72 74 * @param xAxisData the vector in which to store the x-axis data extracted
73 75 * @param valuesData the vector in which to store the value extracted
74 76 * @param line the line to read to extract the property
75 77 * @param valuesIndexes indexes of insertion of read values. For example, if the line contains three
76 78 * columns of values, and valuesIndexes are {2, 0, 1}, the value of the third column will be read
77 79 * and inserted first, then the value of the first column, and finally the value of the second
78 80 * column.
79 81 * @param fillValue value that tags an invalid data. For example, if fillValue is -1 and a read
80 82 * value is -1, then this value is considered as invalid and converted to NaN
81 83 */
82 84 void tryReadResult(std::vector<double> &xAxisData, std::vector<double> &valuesData,
83 85 const QString &line, const std::vector<int> &valuesIndexes,
84 86 double fillValue = std::numeric_limits<double>::quiet_NaN())
85 87 {
86 88 auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
87 89
88 90 // Checks that the line contains expected number of values + x-axis value
89 91 if (lineData.size() == valuesIndexes.size() + 1) {
90 92 // X : the data is converted from date to double (in secs)
91 93 auto x = doubleDate(lineData.at(0));
92 94
93 95 // Adds result only if x is valid. Then, if value is invalid, it is set to NaN
94 96 if (!std::isnan(x)) {
95 97 xAxisData.push_back(x);
96 98
97 99 // Values
98 100 for (auto valueIndex : valuesIndexes) {
99 101 bool valueOk;
100 102 // we use valueIndex + 1 to skip column 0 (x-axis value)
101 103 auto value = lineData.at(valueIndex + 1).toDouble(&valueOk);
102 104
103 105 if (!valueOk) {
104 106 qCWarning(LOG_AmdaResultParserHelper())
105 107 << QObject::tr(
106 108 "Value from (line %1, column %2) is invalid and will be "
107 109 "converted to NaN")
108 110 .arg(line, valueIndex);
109 111 value = std::numeric_limits<double>::quiet_NaN();
110 112 }
111 113
112 114 // Handles fill value
113 115 if (!std::isnan(fillValue) && !std::isnan(value) && fillValue == value) {
114 116 value = std::numeric_limits<double>::quiet_NaN();
115 117 }
116 118
117 119 valuesData.push_back(value);
118 120 }
119 121 }
120 122 else {
121 123 qCWarning(LOG_AmdaResultParserHelper())
122 124 << QObject::tr("Can't retrieve results from line %1: x is invalid").arg(line);
123 125 }
124 126 }
125 127 else {
126 128 qCWarning(LOG_AmdaResultParserHelper())
127 129 << QObject::tr("Can't retrieve results from line %1: invalid line").arg(line);
128 130 }
129 131 }
130 132
131 133 /**
132 134 * Reads a line from the AMDA file and tries to extract a property from it
133 135 * @param properties the properties map in which to put the property extracted from the line
134 136 * @param key the key to which the property is added in the properties map
135 137 * @param line the line to read to extract the property
136 138 * @param regex the expected regex to extract the property. If the line matches this regex, the
137 139 * property is generated
138 140 * @param fun the function used to generate the property
139 141 * @return true if the property could be generated, false if the line does not match the regex, or
140 142 * if a property has already been generated for the key
141 143 */
142 144 template <typename GeneratePropertyFun>
143 145 bool tryReadProperty(Properties &properties, const QString &key, const QString &line,
144 146 const QRegularExpression &regex, GeneratePropertyFun fun)
145 147 {
146 148 if (properties.contains(key)) {
147 149 return false;
148 150 }
149 151
150 152 auto match = regex.match(line);
151 153 if (match.hasMatch()) {
152 154 properties.insert(key, fun(match));
153 155 }
154 156
155 157 return match.hasMatch();
156 158 }
157 159
158 160 /**
159 161 * Reads a line from the AMDA file and tries to extract a double from it
160 162 * @sa tryReadProperty()
161 163 */
162 164 bool tryReadDouble(Properties &properties, const QString &key, const QString &line,
163 165 const QRegularExpression &regex)
164 166 {
165 167 return tryReadProperty(properties, key, line, regex, [](const auto &match) {
166 168 bool ok;
167 169
168 170 // If the value can't be converted to double, it is set to NaN
169 171 auto doubleValue = match.captured(1).toDouble(&ok);
170 172 if (!ok) {
171 173 doubleValue = std::numeric_limits<double>::quiet_NaN();
172 174 }
173 175
174 176 return QVariant::fromValue(doubleValue);
175 177 });
176 178 }
177 179
178 180 /**
179 181 * Reads a line from the AMDA file and tries to extract a vector of doubles from it
180 182 * @param sep the separator of double values in the line
181 183 * @sa tryReadProperty()
182 184 */
183 185 bool tryReadDoubles(Properties &properties, const QString &key, const QString &line,
184 186 const QRegularExpression &regex, const QString &sep = QStringLiteral(","))
185 187 {
186 188 return tryReadProperty(properties, key, line, regex, [sep](const auto &match) {
187 189 std::vector<double> doubleValues{};
188 190
189 191 // If the value can't be converted to double, it is set to NaN
190 192 auto values = match.captured(1).split(sep);
191 193 for (auto value : values) {
192 194 bool ok;
193 195
194 196 auto doubleValue = value.toDouble(&ok);
195 197 if (!ok) {
196 198 doubleValue = std::numeric_limits<double>::quiet_NaN();
197 199 }
198 200
199 201 doubleValues.push_back(doubleValue);
200 202 }
201 203
202 204 return QVariant::fromValue(doubleValues);
203 205 });
204 206 }
205 207
206 208 /**
207 209 * Reads a line from the AMDA file and tries to extract a unit from it
208 210 * @sa tryReadProperty()
209 211 */
210 212 bool tryReadUnit(Properties &properties, const QString &key, const QString &line,
211 213 const QRegularExpression &regex, bool timeUnit = false)
212 214 {
213 215 return tryReadProperty(properties, key, line, regex, [timeUnit](const auto &match) {
214 216 return QVariant::fromValue(Unit{match.captured(1), timeUnit});
215 217 });
216 218 }
217 219
218 220 } // namespace
219 221
220 222 // ////////////////// //
221 223 // ScalarParserHelper //
222 224 // ////////////////// //
223 225
224 226 bool ScalarParserHelper::checkProperties()
225 227 {
226 228 return checkUnit(m_Properties, X_AXIS_UNIT_PROPERTY,
227 229 QObject::tr("The x-axis unit could not be found in the file"));
228 230 }
229 231
230 232 std::shared_ptr<IDataSeries> ScalarParserHelper::createSeries()
231 233 {
232 234 return std::make_shared<ScalarSeries>(std::move(m_XAxisData), std::move(m_ValuesData),
233 235 m_Properties.value(X_AXIS_UNIT_PROPERTY).value<Unit>(),
234 236 m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>());
235 237 }
236 238
237 239 void ScalarParserHelper::readPropertyLine(const QString &line)
238 240 {
239 241 tryReadUnit(m_Properties, X_AXIS_UNIT_PROPERTY, line, DEFAULT_X_AXIS_UNIT_REGEX, true);
240 242 }
241 243
242 244 void ScalarParserHelper::readResultLine(const QString &line)
243 245 {
244 246 tryReadResult(m_XAxisData, m_ValuesData, line, valuesIndexes());
245 247 }
246 248
247 249 std::vector<int> ScalarParserHelper::valuesIndexes() const
248 250 {
249 251 // Only one value to read
250 252 static auto result = std::vector<int>{0};
251 253 return result;
252 254 }
253 255
254 256 // /////////////////////// //
255 257 // SpectrogramParserHelper //
256 258 // /////////////////////// //
257 259
258 260 bool SpectrogramParserHelper::checkProperties()
259 261 {
260 262 // Generates y-axis data from bands extracted (take the middle of the intervals)
261 263 auto minBands = m_Properties.value(MIN_BANDS_PROPERTY).value<std::vector<double> >();
262 264 auto maxBands = m_Properties.value(MAX_BANDS_PROPERTY).value<std::vector<double> >();
263 265
264 266 if (minBands.size() != maxBands.size()) {
265 267 qCWarning(LOG_AmdaResultParserHelper()) << QObject::tr(
266 268 "Can't generate y-axis data from bands extracted: bands intervals are invalid");
267 269 return false;
268 270 }
269 271
270 272 std::transform(
271 273 minBands.begin(), minBands.end(), maxBands.begin(), std::back_inserter(m_YAxisData),
272 274 [](const auto &minValue, const auto &maxValue) { return (minValue + maxValue) / 2.; });
273 275
274 276 // Generates values indexes, i.e. the order in which each value will be retrieved (in ascending
275 277 // order of the associated bands)
276 278 m_ValuesIndexes = SortUtils::sortPermutation(m_YAxisData, std::less<double>());
277 279
278 280 // Sorts y-axis data accoding to the ascending order
279 281 m_YAxisData = SortUtils::sort(m_YAxisData, 1, m_ValuesIndexes);
280 282
281 283 // Sets fill value
282 284 m_FillValue = m_Properties.value(FILL_VALUE_PROPERTY).value<double>();
283 285
284 286 /// @todo: handle min/max samplings?
285 287
286 288 return true;
287 289 }
288 290
289 291 std::shared_ptr<IDataSeries> SpectrogramParserHelper::createSeries()
290 292 {
291 293 return std::make_shared<SpectrogramSeries>(
292 294 std::move(m_XAxisData), std::move(m_YAxisData), std::move(m_ValuesData),
293 295 Unit{"t", true}, // x-axis unit is always a time unit
294 296 m_Properties.value(Y_AXIS_UNIT_PROPERTY).value<Unit>(),
295 297 m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>());
296 298 }
297 299
298 300 void SpectrogramParserHelper::readPropertyLine(const QString &line)
299 301 {
300 302 // Set of functions to test on the line to generate a property. If a function is valid (i.e. a
301 303 // property has been generated for the line), the line is treated as processed and the other
302 304 // functions are not called
303 305 std::vector<std::function<bool()> > functions{
304 306 // values unit
305 307 [&] {
306 308 return tryReadUnit(m_Properties, VALUES_UNIT_PROPERTY, line,
307 309 SPECTROGRAM_VALUES_UNIT_REGEX);
308 310 },
309 311 // y-axis unit
310 312 [&] {
311 313 return tryReadUnit(m_Properties, Y_AXIS_UNIT_PROPERTY, line,
312 314 SPECTROGRAM_Y_AXIS_UNIT_REGEX);
313 315 },
314 316 // min sampling
315 317 [&] {
316 318 return tryReadDouble(m_Properties, MIN_SAMPLING_PROPERTY, line,
317 319 SPECTROGRAM_MIN_SAMPLING_REGEX);
318 320 },
319 321 // max sampling
320 322 [&] {
321 323 return tryReadDouble(m_Properties, MAX_SAMPLING_PROPERTY, line,
322 324 SPECTROGRAM_MAX_SAMPLING_REGEX);
323 325 },
324 326 // fill value
325 327 [&] {
326 328 return tryReadDouble(m_Properties, FILL_VALUE_PROPERTY, line,
327 329 SPECTROGRAM_FILL_VALUE_REGEX);
328 330 },
329 331 // min bounds of each band
330 332 [&] {
331 333 return tryReadDoubles(m_Properties, MIN_BANDS_PROPERTY, line,
332 334 SPECTROGRAM_MIN_BANDS_REGEX);
333 335 },
334 336 // max bounds of each band
335 337 [&] {
336 338 return tryReadDoubles(m_Properties, MAX_BANDS_PROPERTY, line,
337 339 SPECTROGRAM_MAX_BANDS_REGEX);
338 340 }};
339 341
340 342 for (auto function : functions) {
341 343 // Stops at the first function that is valid
342 344 if (function()) {
343 345 return;
344 346 }
345 347 }
346 348 }
347 349
348 350 void SpectrogramParserHelper::readResultLine(const QString &line)
349 351 {
350 352 tryReadResult(m_XAxisData, m_ValuesData, line, m_ValuesIndexes, m_FillValue);
351 353 }
352 354
353 355 // ////////////////// //
354 356 // VectorParserHelper //
355 357 // ////////////////// //
356 358
357 359 bool VectorParserHelper::checkProperties()
358 360 {
359 361 return checkUnit(m_Properties, X_AXIS_UNIT_PROPERTY,
360 362 QObject::tr("The x-axis unit could not be found in the file"));
361 363 }
362 364
363 365 std::shared_ptr<IDataSeries> VectorParserHelper::createSeries()
364 366 {
365 367 return std::make_shared<VectorSeries>(std::move(m_XAxisData), std::move(m_ValuesData),
366 368 m_Properties.value(X_AXIS_UNIT_PROPERTY).value<Unit>(),
367 369 m_Properties.value(VALUES_UNIT_PROPERTY).value<Unit>());
368 370 }
369 371
370 372 void VectorParserHelper::readPropertyLine(const QString &line)
371 373 {
372 374 tryReadUnit(m_Properties, X_AXIS_UNIT_PROPERTY, line, DEFAULT_X_AXIS_UNIT_REGEX, true);
373 375 }
374 376
375 377 void VectorParserHelper::readResultLine(const QString &line)
376 378 {
377 379 tryReadResult(m_XAxisData, m_ValuesData, line, valuesIndexes());
378 380 }
379 381
380 382 std::vector<int> VectorParserHelper::valuesIndexes() const
381 383 {
382 384 // 3 values to read, in order in the file (x, y, z)
383 385 static auto result = std::vector<int>{0, 1, 2};
384 386 return result;
385 387 }
General Comments 0
You need to be logged in to leave comments. Login now