Rev 779 | Rev 1104 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | |||
671 | werner | 2 | /******************************************************************************************** |
3 | ** iLand - an individual based forest landscape and disturbance model |
||
4 | ** http://iland.boku.ac.at |
||
5 | ** Copyright (C) 2009- Werner Rammer, Rupert Seidl |
||
6 | ** |
||
7 | ** This program is free software: you can redistribute it and/or modify |
||
8 | ** it under the terms of the GNU General Public License as published by |
||
9 | ** the Free Software Foundation, either version 3 of the License, or |
||
10 | ** (at your option) any later version. |
||
11 | ** |
||
12 | ** This program is distributed in the hope that it will be useful, |
||
13 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
14 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
15 | ** GNU General Public License for more details. |
||
16 | ** |
||
17 | ** You should have received a copy of the GNU General Public License |
||
18 | ** along with this program. If not, see <http://www.gnu.org/licenses/>. |
||
19 | ********************************************************************************************/ |
||
20 | |||
173 | werner | 21 | #include "global.h" |
22 | #include "output.h" |
||
23 | #include <QtCore> |
||
24 | #include <QtSql> |
||
25 | |||
26 | |||
27 | /** @class Output |
||
28 | The Output class abstracts output data (database, textbased, ...). |
||
177 | werner | 29 | To create a new output, create a class derived from Output and perform the following steps: |
30 | - Overwrite constructor: |
||
31 | Create columns and set fixed properties (e.g. table name) |
||
32 | - overwrite setup() |
||
33 | this function is called after the project file is read. You can access a XmlHelper calling settings() |
||
257 | werner | 34 | which is set to the top-node of the output (defined by tableName() which is set in the constructor). Access settings |
177 | werner | 35 | using relative xml-pathes (see example). |
36 | - overwrite exec() |
||
37 | add data using the stream operators or add() function of Output. Call writeRow() after each row. Each invokation |
||
38 | of exec() is a database transaction. |
||
39 | - Add the output to the constructor of @c OutputManager |
||
40 | |||
41 | @par Example |
||
173 | werner | 42 | @code |
177 | werner | 43 | // (1) Overwrite constructor and set name, description and columns |
44 | TreeOut::TreeOut() |
||
45 | { |
||
46 | setName("Tree Output", "tree"); |
||
47 | setDescription("Output of indivdual trees."); |
||
48 | columns() << OutputColumn("id", "id of the tree", OutInteger) |
||
49 | << OutputColumn("name", "tree species name", OutString) |
||
50 | << OutputColumn("v1", "a double value", OutDouble); |
||
51 | } |
||
52 | // (2) optionally: some special settings (here: filter) |
||
53 | void TreeOut::setup() |
||
54 | { |
||
55 | QString filter = settings().value(".filter",""); |
||
56 | if (filter!="") |
||
57 | mFilter = QSharedPointer<Expression>(new Expression(filter)); |
||
58 | } |
||
173 | werner | 59 | |
177 | werner | 60 | // (3) the execution |
61 | void TreeOut::exec() |
||
62 | { |
||
63 | AllTreeIterator at(GlobalSettings::instance()->model()); |
||
64 | while (Tree *t=at.next()) { |
||
65 | if (mFilter && !mFilter->execute()) // skip if filter present |
||
66 | continue; |
||
67 | *this << t->id() << t->species()->id() << t->dbh(); // stream operators |
||
68 | writeRow(); // executes DB insert |
||
69 | } |
||
70 | } |
||
71 | // in outputmanager.cpp: |
||
72 | OutputManager::OutputManager() |
||
73 | { |
||
74 | ... |
||
75 | mOutputs.append(new TreeOut); // add the output |
||
76 | ... |
||
77 | } |
||
78 | @endcode |
||
173 | werner | 79 | |
80 | */ |
||
257 | werner | 81 | const GlobalSettings *Output::gl = GlobalSettings::instance(); |
82 | |||
83 | |||
173 | werner | 84 | void Output::exec() |
85 | { |
||
86 | qDebug() << "Output::exec() called! (should be overrided!)"; |
||
87 | } |
||
88 | |||
89 | void Output::setup() |
||
90 | { |
||
91 | } |
||
92 | |||
93 | Output::~Output() |
||
94 | { |
||
265 | werner | 95 | mInserter.clear(); |
173 | werner | 96 | } |
97 | |||
98 | Output::Output() |
||
99 | { |
||
100 | mCount=0; |
||
101 | mMode = OutDatabase; |
||
102 | mOpen = false; |
||
181 | werner | 103 | mEnabled = false; |
176 | werner | 104 | newRow(); |
173 | werner | 105 | } |
106 | |||
107 | |||
108 | /** create the database table and opens up the output. |
||
109 | */ |
||
110 | void Output::openDatabase() |
||
111 | { |
||
112 | QSqlDatabase db = GlobalSettings::instance()->dbout(); |
||
113 | // create the "create table" statement |
||
176 | werner | 114 | QString sql = "create table " +mTableName + "("; |
115 | QString insert="insert into " + mTableName + " ("; |
||
173 | werner | 116 | QString values; |
117 | |||
118 | foreach(const OutputColumn &col, columns()) { |
||
119 | switch(col.mDatatype) { |
||
176 | werner | 120 | case OutInteger: sql+=col.mName + " integer"; break; |
121 | case OutDouble: sql+=col.mName + " real"; break; |
||
122 | case OutString: sql+=col.mName + " text"; break; |
||
173 | werner | 123 | } |
176 | werner | 124 | insert+=col.mName+","; |
125 | values+=QString(":")+col.mName+","; |
||
173 | werner | 126 | |
127 | sql+=","; |
||
128 | } |
||
129 | sql[sql.length()-1]=')'; // replace last "," with ) |
||
230 | werner | 130 | //qDebug()<< sql; |
407 | werner | 131 | if (mInserter.isValid()) |
132 | mInserter.clear(); |
||
176 | werner | 133 | QSqlQuery creator(db); |
134 | QString drop=QString("drop table if exists %1").arg(tableName()); |
||
184 | werner | 135 | creator.exec(drop); // drop table (if exists) |
176 | werner | 136 | creator.exec(sql); // (re-)create table |
539 | werner | 137 | //creator.exec("delete from " + tableName()); // clear table??? necessary? |
176 | werner | 138 | |
139 | if (creator.lastError().isValid()){ |
||
140 | throw IException(QString("Error creating output: %1").arg( creator.lastError().text()) ); |
||
173 | werner | 141 | } |
142 | insert[insert.length()-1]=')'; |
||
143 | values[values.length()-1]=')'; |
||
144 | insert += QString(" values (") + values; |
||
230 | werner | 145 | //qDebug() << insert; |
176 | werner | 146 | mInserter = QSqlQuery(db); |
173 | werner | 147 | mInserter.prepare(insert); |
176 | werner | 148 | if (mInserter.lastError().isValid()){ |
149 | throw IException(QString("Error creating output: %1").arg( mInserter.lastError().text()) ); |
||
150 | } |
||
173 | werner | 151 | for (int i=0;i<columns().count();i++) |
152 | mInserter.bindValue(i,mRow[i]); |
||
153 | |||
154 | mOpen = true; |
||
155 | } |
||
156 | |||
157 | void Output::newRow() |
||
158 | { |
||
159 | mIndex = 0; |
||
160 | } |
||
161 | |||
162 | |||
163 | |||
176 | werner | 164 | void Output::writeRow() |
173 | werner | 165 | { |
166 | DBG_IF(mIndex!=mCount, "Output::save()", "received invalid number of values!"); |
||
167 | if (!isOpen()) |
||
168 | open(); |
||
169 | switch(mMode) { |
||
170 | case OutDatabase: |
||
171 | saveDatabase(); break; |
||
172 | default: throw IException("Invalid output mode"); |
||
173 | } |
||
174 | |||
175 | } |
||
176 | void Output::open() |
||
177 | { |
||
178 | if (isOpen()) |
||
179 | return; |
||
176 | werner | 180 | // setup columns |
181 | mCount = columns().count(); |
||
182 | mRow.resize(mCount); |
||
539 | werner | 183 | mOpen = true; |
176 | werner | 184 | newRow(); |
185 | // setup output |
||
173 | werner | 186 | switch(mMode) { |
187 | case OutDatabase: |
||
188 | openDatabase(); break; |
||
189 | default: throw IException("Invalid output mode"); |
||
190 | } |
||
191 | } |
||
192 | |||
176 | werner | 193 | void Output::close() |
173 | werner | 194 | { |
232 | werner | 195 | if (!isOpen()) |
196 | return; |
||
197 | mOpen = false; |
||
198 | switch (mMode) { |
||
199 | case OutDatabase: |
||
200 | // calling finish() ensures, that the query and all locks are freed. |
||
201 | // having (old) locks on database connections, degrades insert performance. |
||
202 | if (mInserter.isValid()) |
||
203 | mInserter.finish(); |
||
407 | werner | 204 | mInserter = QSqlQuery(); // clear inserter |
232 | werner | 205 | break; |
206 | default: |
||
207 | qWarning() << "Output::close with invalid mode"; |
||
208 | } |
||
209 | |||
173 | werner | 210 | } |
211 | |||
212 | |||
176 | werner | 213 | void Output::saveDatabase() |
173 | werner | 214 | { |
176 | werner | 215 | for (int i=0;i<mCount;i++) |
216 | mInserter.bindValue(i,mRow[i]); |
||
217 | mInserter.exec(); |
||
218 | if (mInserter.lastError().isValid()){ |
||
1023 | werner | 219 | throw IException(QString("Error during saving of output tables: '%1'' (native code: '%2', driver: '%3')") |
220 | .arg( mInserter.lastError().text()) |
||
221 | .arg(mInserter.lastError().nativeErrorCode()) |
||
222 | .arg(mInserter.lastError().driverText()) ); |
||
176 | werner | 223 | } |
173 | werner | 224 | |
176 | werner | 225 | newRow(); |
226 | } |
||
173 | werner | 227 | |
254 | werner | 228 | QString Output::wikiFormat() const |
253 | werner | 229 | { |
254 | werner | 230 | QString result=QString("!!%1\nTable Name: %2\n%3\n\n").arg(name(), tableName(), description()); |
253 | werner | 231 | // loop over columns... |
254 | werner | 232 | result += "||__caption__|__datatype__|__description__\n"; // table begin |
253 | werner | 233 | foreach(const OutputColumn &col, mColumns) |
254 | werner | 234 | result+=QString("%1|%2|%3\n").arg(col.name(), col.datatype(), col.description()); |
253 | werner | 235 | result[result.length()-1]=' '; // clear last newline |
236 | result+="||\n"; |
||
237 | return result; |
||
238 | } |