Subversion Repositories public iLand

Rev

Rev 1221 | Details | Compare with Previous | Last modification | View Log | RSS feed

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