Subversion Repositories public iLand

Rev

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
}