1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
// Qt-Security score:critical reason:data-parser
#include "rainfalldata.h"
#include <QtCore/qfile.h>
#include <QtCore/qrangemodel.h>
#include <QtCore/qlist.h>
#include <QtCore/qtextstream.h>
#include <QtGraphs/q3dscene.h>
#include <QtGraphs/qbar3dseries.h>
#include <QtGraphs/qcategory3daxis.h>
#include <QtGraphs/qgraphstheme.h>
#include <QtGraphs/qitemmodelbardataproxy.h>
#include <QtGraphs/qvalue3daxis.h>
#include <array>
using namespace Qt::StringLiterals;
//! [1]
using YearlyData = std::array<double, 12>;
using ModelData = QList<YearlyData>;
static ModelData readData(const QString &fileName, int *firstYear)
{
ModelData result;
*firstYear = -1;
// Read data from a data file into the data item list
QFile dataFile(fileName);
if (!dataFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
qWarning() << "Unable to open data file:" << dataFile.fileName() << dataFile.errorString();
return result;
}
QTextStream stream(&dataFile);
int lastYear = -1;
while (!stream.atEnd()) {
QString line = stream.readLine();
if (line.startsWith(u'#')) // Ignore comments
continue;
const auto strList = QStringView{line}.split(',', Qt::SkipEmptyParts);
// Each line has three data items: Year, month, and rainfall value
if (strList.size() < 3) {
qWarning() << "Invalid row read from data:" << line;
continue;
}
// Store year and month as int, and rainfall value as double
bool yearOk{};
bool monthOk{};
bool valueOk{};
const int year = strList.at(0).trimmed().toInt(&yearOk);
const int month = strList.at(1).trimmed().toInt(&monthOk);
const double value = strList.at(2).trimmed().toDouble(&valueOk);
if (!yearOk || !monthOk || month < 1 || month > 12 || !valueOk) {
qWarning() << "Invalid row values:" << line;
continue;
}
if (year != lastYear) {
if (lastYear == -1) {
*firstYear = year;
} else if (year != lastYear + 1) {
qWarning() << "Non-consecutive years" << year << lastYear;
return {};
}
lastYear = year;
result.emplace_back(YearlyData{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
}
result.back()[month - 1] = value;
}
return result;
}
//! [1]
RainfallData::RainfallData()
{
// Create proxy and series
//! [0]
int firstYear{};
auto data = readData(":/data/raindata.txt"_L1, &firstYear);
Q_ASSERT(!data.isEmpty());
updateYearsList(firstYear, firstYear + int(data.size()) - 1);
auto *model = new QRangeModel(data, this);
m_proxy = new QItemModelBarDataProxy(model);
m_proxy->setUseModelCategories(true);
m_series = new QBar3DSeries(m_proxy);
//! [0]
m_series->setItemLabelFormat(u"%.1f mm"_s);
// Create the axes
m_rowAxis = new QCategory3DAxis(this);
m_colAxis = new QCategory3DAxis(this);
m_valueAxis = new QValue3DAxis(this);
m_rowAxis->setAutoAdjustRange(true);
m_colAxis->setAutoAdjustRange(true);
m_valueAxis->setAutoAdjustRange(true);
// Set axis labels and titles
QStringList months{"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"};
m_rowAxis->setTitle("Year");
m_colAxis->setTitle("Month");
m_valueAxis->setTitle("rainfall (mm)");
m_valueAxis->setSegmentCount(5);
m_rowAxis->setLabels(m_years);
m_colAxis->setLabels(months);
m_rowAxis->setTitleVisible(true);
m_colAxis->setTitleVisible(true);
m_valueAxis->setTitleVisible(true);
}
RainfallData::~RainfallData() = default;
void RainfallData::updateYearsList(int start, int end)
{
m_years.clear();
for (int i = start; i <= end; ++i)
m_years << QString::number(i);
}
|