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
 
183 werner 20
#include "dynamicstandout.h"
21
 
808 werner 22
#include "debugtimer.h"
23
#include "statdata.h"
184 werner 24
#include "model.h"
189 iland 25
#include "resourceunit.h"
184 werner 26
#include "species.h"
27
#include "expressionwrapper.h"
28
 
183 werner 29
DynamicStandOut::DynamicStandOut()
30
{
184 werner 31
    setName("dynamic stand output by species/RU", "dynamicstand");
802 werner 32
    setDescription("Userdefined outputs for tree aggregates for each stand or species.\n"\
261 werner 33
                   "Technically, each field is calculated 'live', i.e. it is looped over all trees, and eventually the statistics (percentiles) "\
1157 werner 34
                   "are calculated. The aggregated values are not scaled to any area unit.\n" \
802 werner 35
                   "!!!Specifying the aggregation\n" \
36
                   "The ''by_species'' and ''by_ru'' option allow to define the aggregation level. When ''by_species'' is set to ''true'', " \
37
                   "a row for each species will be created, otherwise all trees of all species are aggregated to one row. " \
1157 werner 38
                   "Similarly, ''by_ru''=''true'' means outputs for each resource unit, while a value of ''false'' aggregates over the full project area.\n" \
802 werner 39
                   "!!!Specifying filters\n" \
521 werner 40
                   "You can use the 'rufilter' and 'treefilter' XML settings to reduce the limit the output to a subset of resource units / trees. " \
41
                   "Both filters are valid expressions (for resource unit level and tree level, respectively). For example, a ''treefilter'' of 'speciesindex=0' reduces the output to just one species.\n" \
802 werner 42
                   "The ''condition'' filter is (when present) evaluated and the output is only executed when ''condition'' is true (variable='year') This can be used to constrain the output to specific years (e.g. 'in(year,100,200,300)' produces output only for the given year.\n" \
43
                   "!!!Specifying data columns\n"
521 werner 44
                   "Each field is defined as: ''field.aggregatio''n (separated by a dot). A ''field'' is a valid [Expression]. ''Aggregation'' is one of the following:  " \
598 werner 45
                   "mean, sum, min, max, p25, p50, p75, p5, 10, p90, p95 (pXX=XXth percentile), sd (std.dev.).\n" \
46
                   "Complex expression are allowed, e.g: if(dbh>50,1,0).sum (-> counts trees with dbh>50)");
570 werner 47
    columns() << OutputColumn::year() << OutputColumn::ru()  << OutputColumn::id() << OutputColumn::species();
184 werner 48
    // other colums are added during setup...
183 werner 49
}
184 werner 50
 
598 werner 51
const QStringList aggList = QStringList() << "mean" << "sum" << "min" << "max" << "p25" << "p50" << "p75" << "p5"<< "p10" << "p90" << "p95" << "sd";
184 werner 52
 
53
void DynamicStandOut::setup()
54
{
55
    QString filter = settings().value(".rufilter","");
520 werner 56
    QString tree_filter = settings().value(".treefilter","");
184 werner 57
    QString fieldList = settings().value(".columns", "");
802 werner 58
    QString condition = settings().value(".condition", "");
184 werner 59
    if (fieldList.isEmpty())
60
        return;
61
    mRUFilter.setExpression(filter);
520 werner 62
    mTreeFilter.setExpression(tree_filter);
802 werner 63
    mCondition.setExpression(condition);
184 werner 64
    // clear columns
570 werner 65
    columns().erase(columns().begin()+4, columns().end());
184 werner 66
    mFieldList.clear();
67
 
68
    // setup fields
69
   if (!fieldList.isEmpty()) {
353 werner 70
       QRegExp rx("([^\\.]+).(\\w+)[,\\s]*"); // two parts: before dot and after dot, and , + whitespace at the end
184 werner 71
        int pos=0;
353 werner 72
        QString field, aggregation;
184 werner 73
        TreeWrapper tw;
74
        while ((pos = rx.indexIn(fieldList, pos)) != -1) {
75
            pos += rx.matchedLength();
353 werner 76
 
77
            field = rx.cap(1); // field / expresssion
78
            aggregation = rx.cap(2);
184 werner 79
            mFieldList.append(SDynamicField());
80
            // parse field
353 werner 81
            if (field.count()>0 && !field.contains('(')) {
184 werner 82
                // simple expression
353 werner 83
                mFieldList.back().var_index = tw.variableIndex(field);
184 werner 84
            } else {
85
                // complex expression
86
                mFieldList.back().var_index=-1;
353 werner 87
                mFieldList.back().expression = field;
184 werner 88
            }
89
 
353 werner 90
            mFieldList.back().agg_index = aggList.indexOf(aggregation);
91
            if (mFieldList.back().agg_index==-1)
184 werner 92
                 throw IException(QString("Invalid aggregate expression for dynamic output: %1\nallowed:%2")
353 werner 93
                                          .arg(aggregation).arg(aggList.join(" ")));
184 werner 94
 
353 werner 95
             QString stripped_field=QString("%1_%2").arg(field, aggregation);
184 werner 96
             stripped_field.replace(QRegExp("[\\[\\]\\,\\(\\)<>=!\\s]"), "_");
97
             stripped_field.replace("__", "_");
98
             columns() << OutputColumn(stripped_field, field, OutDouble);
99
        }
802 werner 100
   }
101
}
102
 
103
void DynamicStandOut::exec()
104
{
105
    if (mFieldList.count()==0)
106
        return;
107
    if (!mCondition.isEmpty())
108
        if (!mCondition.calculate(GlobalSettings::instance()->currentYear()))
109
            return;
110
 
111
    DebugTimer t("dynamic stand output");
112
 
113
    bool per_species = GlobalSettings::instance()->settings().valueBool("output.dynamicstand.by_species", true);
114
    bool per_ru = GlobalSettings::instance()->settings().valueBool("output.dynamicstand.by_ru", true);
115
 
116
    if (per_ru) {
117
        // when looping over resource units, do it differently (old way)
855 werner 118
        extractByResourceUnit(per_species);
802 werner 119
        return;
184 werner 120
    }
802 werner 121
 
122
    Model *m = GlobalSettings::instance()->model();
123
    QVector<double> data; //statistics data
124
    TreeWrapper tw;
125
    Expression custom_expr;
126
 
127
    StatData stat; // statistcs helper class
128
    // grouping
129
    QVector<const Tree*> trees;
130
    for (QList<Species*>::const_iterator species = m->speciesSet()->activeSpecies().constBegin();species!=m->speciesSet()->activeSpecies().constEnd();++species) {
131
        trees.clear();
132
        AllTreeIterator all_trees(m);
855 werner 133
        while (const Tree *t = all_trees.nextLiving()) {
802 werner 134
            if (per_species && t->species() != *species)
135
                continue;
136
            trees.push_back(t);
137
        }
138
        if (trees.size()==0)
139
            continue;
140
        // dynamic calculations
141
        foreach (const SDynamicField &field, mFieldList) {
142
 
143
            if (!field.expression.isEmpty()) {
144
                // setup dynamic dynamic expression if present
145
                custom_expr.setExpression(field.expression);
146
                custom_expr.setModelObject(&tw);
147
            }
148
 
149
            // fetch data values from the trees
150
            data.clear();
151
            foreach(const Tree *t, trees) {
152
                tw.setTree(t);
153
                if (field.var_index>=0)
154
                    data.push_back(tw.value(field.var_index));
155
                else
156
                    data.push_back(custom_expr.execute());
157
            }
158
            // constant values (if not already present)
159
            if (isRowEmpty()) {
160
                *this << currentYear() << -1 << -1;
161
                if (per_species)
162
                    *this << (*species)->id();
163
                else
164
                    *this << "";
165
            }
166
 
167
            // calculate statistics
168
            stat.setData(data);
169
            // aggregate
170
            double value;
171
            switch (field.agg_index) {
172
            case 0: value = stat.mean(); break;
173
            case 1: value = stat.sum(); break;
174
            case 2: value = stat.min(); break;
175
            case 3: value = stat.max(); break;
176
            case 4: value = stat.percentile25(); break;
177
            case 5: value = stat.median(); break;
178
            case 6: value = stat.percentile75(); break;
179
            case 7: value = stat.percentile(5); break;
180
            case 8: value = stat.percentile(10); break;
181
            case 9: value = stat.percentile(90); break;
182
            case 10: value = stat.percentile(95); break;
183
            case 11: value = stat.standardDev(); break;
184
 
185
            default: value = 0.; break;
186
            }
187
            // add current value to output
188
            *this << value;
189
        }
190
        if (!isRowEmpty())
191
            writeRow();
192
 
193
        if (!per_species)
194
            break;
195
    }
196
 
184 werner 197
}
198
 
199
 
855 werner 200
void DynamicStandOut::extractByResourceUnit(const bool by_species)
184 werner 201
{
202
 
203
    if (mFieldList.count()==0)
204
        return;
205
 
206
    Model *m = GlobalSettings::instance()->model();
207
    QVector<double> data; //statistics data
208
    StatData stat; // statistcs helper class
209
    TreeWrapper tw;
210
    RUWrapper ruwrapper;
211
    mRUFilter.setModelObject(&ruwrapper);
212
 
213
    Expression custom_expr;
214
 
802 werner 215
 
189 iland 216
    foreach(ResourceUnit *ru, m->ruList()) {
574 werner 217
        if (ru->id()==-1)
218
            continue; // do not include if out of project area
219
 
184 werner 220
        // test filter
221
        if (!mRUFilter.isEmpty()) {
189 iland 222
            ruwrapper.setResourceUnit(ru);
184 werner 223
            if (!mRUFilter.execute())
224
                continue;
225
        }
455 werner 226
        foreach(const ResourceUnitSpecies *rus, ru->ruSpecies()) {
855 werner 227
            if (by_species && rus->constStatistics().count()==0)
184 werner 228
                continue;
229
 
520 werner 230
 
184 werner 231
            // dynamic calculations
232
            foreach (const SDynamicField &field, mFieldList) {
233
 
234
                if (!field.expression.isEmpty()) {
235
                    // setup dynamic dynamic expression if present
236
                    custom_expr.setExpression(field.expression);
237
                    custom_expr.setModelObject(&tw);
238
                }
239
                data.clear();
520 werner 240
                bool has_trees = false;
184 werner 241
                foreach(const Tree &tree, ru->trees()) {
855 werner 242
                    if (by_species && tree.species()->index()!=rus->species()->index())
184 werner 243
                        continue;
855 werner 244
                    if (tree.isDead())
245
                        continue;
184 werner 246
                    tw.setTree(&tree);
520 werner 247
 
248
                    // apply treefilter
249
                    if (!mTreeFilter.isEmpty()) {
250
                        mTreeFilter.setModelObject(&tw);
251
                        if (!mTreeFilter.execute())
252
                            continue;
253
                    }
254
                    has_trees = true;
255
 
184 werner 256
                    if (field.var_index>=0)
257
                        data.push_back(tw.value(field.var_index));
258
                    else
259
                        data.push_back(custom_expr.execute());
260
                }
520 werner 261
 
262
                // do nothing if no trees are avaiable
263
                if (!has_trees)
264
                    continue;
265
 
266
 
855 werner 267
                if (isRowEmpty()) {
268
                    *this << currentYear()  << ru->index() << ru->id();
269
                    if (by_species)
270
                        *this << rus->species()->id();
271
                    else
272
                        *this << "";
273
                 }
274
 
184 werner 275
                // calculate statistics
276
                stat.setData(data);
277
                // aggregate
278
                double value;
279
                switch (field.agg_index) {
218 werner 280
                case 0: value = stat.mean(); break;
281
                case 1: value = stat.sum(); break;
282
                case 2: value = stat.min(); break;
283
                case 3: value = stat.max(); break;
284
                case 4: value = stat.percentile25(); break;
285
                case 5: value = stat.median(); break;
286
                case 6: value = stat.percentile75(); break;
287
                case 7: value = stat.percentile(5); break;
288
                case 8: value = stat.percentile(10); break;
289
                case 9: value = stat.percentile(90); break;
290
                case 10: value = stat.percentile(95); break;
598 werner 291
                case 11: value = stat.standardDev(); break;
218 werner 292
 
293
                default: value = 0.; break;
184 werner 294
                }
295
                // add current value to output
296
                *this << value;
297
 
298
            } // foreach (field)
520 werner 299
            if (!isRowEmpty())
300
                writeRow();
855 werner 301
            if (!by_species)
302
                break;
184 werner 303
        } //foreach species
304
    } // foreach ressource unit
305
 
306
}