Subversion Repositories public iLand

Rev

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

Rev Author Line No. Line
1033 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
********************************************************************************************/
884 werner 19
#include "global.h"
908 werner 20
#include "abe_global.h"
884 werner 21
#include "fmtreelist.h"
22
 
23
#include "forestmanagementengine.h"
24
// iLand stuff
25
#include "tree.h"
26
#include "expression.h"
27
#include "mapgrid.h"
885 werner 28
#include "expressionwrapper.h"
29
#include "model.h"
913 werner 30
#include "helper.h"
885 werner 31
#include "fmstand.h"
887 werner 32
#include "fomescript.h"
884 werner 33
 
907 werner 34
namespace ABE {
884 werner 35
 
1095 werner 36
/** @class FMTreeList
37
    @ingroup abe
38
    The FMTreeList class implements low-level functionality for selecting and harvesting of trees.
39
    The functions of the class are usually accessed via Javascript.
40
 
41
  */
42
 
43
 
885 werner 44
// TODO: fix: removal fractions need to be moved to agent/units/ whatever....
45
double removeFoliage()  {return 0.;}
46
double removeStem()  {return 1.;}
47
double removeBranch()  {return 0.;}
48
 
884 werner 49
FMTreeList::FMTreeList(QObject *parent) :
50
    QObject(parent)
51
{
932 werner 52
    mStand = 0;
885 werner 53
    setStand(0); // clear stand link
932 werner 54
    mResourceUnitsLocked = false;
885 werner 55
 
884 werner 56
}
57
 
891 werner 58
FMTreeList::FMTreeList(FMStand *stand, QObject *parent):
59
    QObject(parent)
60
{
932 werner 61
    mStand = 0;
891 werner 62
    setStand(stand);
932 werner 63
    mResourceUnitsLocked = false;
891 werner 64
}
65
 
932 werner 66
FMTreeList::~FMTreeList()
67
{
68
    check_locks();
69
}
70
 
889 werner 71
void FMTreeList::setStand(FMStand *stand)
885 werner 72
{
932 werner 73
    check_locks();
885 werner 74
    mStand = stand;
75
    if (stand) {
76
        mStandId = stand->id();
77
        mNumberOfStems = stand->stems() * stand->area();
891 werner 78
        mOnlySimulate = stand->currentActivity()?stand->currentFlags().isScheduled() : false;
912 werner 79
        mStandRect=QRectF();
885 werner 80
    } else {
81
        mStandId = -1;
82
        mNumberOfStems = 1000;
891 werner 83
        mOnlySimulate = false;
885 werner 84
    }
932 werner 85
 
885 werner 86
}
87
 
88
 
912 werner 89
 
884 werner 90
int FMTreeList::load(const QString &filter)
91
{
92
    if (standId()>-1) {
93
        // load all trees of the current stand
94
        const MapGrid *map = ForestManagementEngine::instance()->standGrid();
95
        if (map->isValid()) {
885 werner 96
            map->loadTrees(mStandId, mTrees, filter, mNumberOfStems);
932 werner 97
            mResourceUnitsLocked = true;
884 werner 98
        } else {
909 werner 99
            qCDebug(abe) << "FMTreeList::load: grid is not valid - no trees loaded";
884 werner 100
        }
101
        return mTrees.count();
102
 
103
    } else {
909 werner 104
        qCDebug(abe) << "FMTreeList::load: loading *all* trees, because stand id is -1";
884 werner 105
        TreeWrapper tw;
106
        Model *m = GlobalSettings::instance()->model();
107
        mTrees.clear();
108
        AllTreeIterator at(m);
109
        if (filter.isEmpty()) {
110
            while (Tree *t=at.nextLiving())
111
                if (!t->isDead())
112
                    mTrees.push_back(QPair<Tree*, double>(t, 0.));
113
        } else {
114
            Expression expr(filter,&tw);
115
            expr.enableIncSum();
116
            qDebug() << "filtering with" << filter;
117
            while (Tree *t=at.nextLiving()) {
118
                tw.setTree(t);
119
                if (!t->isDead() && expr.execute())
120
                    mTrees.push_back(QPair<Tree*, double>(t, 0.));
121
            }
122
        }
123
        return mTrees.count();
124
    }
125
}
126
 
885 werner 127
int FMTreeList::removeMarkedTrees()
128
{
129
    loadAll();
130
    int n_removed = 0;
131
    for (QVector<QPair<Tree*, double> >::const_iterator it = mTrees.constBegin(); it!=mTrees.constEnd(); ++it) {
132
        Tree *t = const_cast<Tree*>((*it).first);
133
        if (t->isMarkedForCut()) {
134
            t->remove();
135
            n_removed++;
136
        } else if (t->isMarkedForHarvest()) {
137
            t->remove(removeFoliage(), removeBranch(), removeStem());
138
            n_removed++;
139
        }
140
    }
888 werner 141
    if (mStand->trace())
909 werner 142
        qCDebug(abe) << mStand->context() << "removeMarkedTrees: n=" << n_removed;
1062 werner 143
 
144
    return n_removed;
885 werner 145
}
884 werner 146
 
1070 werner 147
int FMTreeList::kill(QString filter)
148
{
149
    return remove_trees(filter, 1., false);
150
}
151
 
885 werner 152
int FMTreeList::harvest(QString filter, double fraction)
884 werner 153
{
885 werner 154
    return remove_trees(filter, fraction, true);
155
 
884 werner 156
}
157
 
887 werner 158
bool FMTreeList::trace() const
159
{
160
    return FomeScript::bridge()->standObj()->trace();
161
}
884 werner 162
 
887 werner 163
 
885 werner 164
int FMTreeList::remove_percentiles(int pctfrom, int pctto, int number, bool management)
165
{
166
    if (mTrees.isEmpty())
167
        return 0;
168
    int index_from = limit(int(pctfrom/100. * mTrees.count()), 0, mTrees.count());
169
    int index_to = limit(int(pctto/100. * mTrees.count()), 0, mTrees.count()-1);
170
    if (index_from>=index_to)
171
        return 0;
923 werner 172
    //qDebug() << "attempting to remove" << number << "trees between indices" << index_from << "and" << index_to;
885 werner 173
    int i;
174
    int count = number;
175
    if (index_to-index_from <= number)  {
176
        // kill all
177
        if (management) {
178
            // management
179
            for (i=index_from; i<index_to; i++)
889 werner 180
                if (simulate()) {
885 werner 181
                    mTrees.at(i).first->markForHarvest(true);
889 werner 182
                    mStand->addScheduledHarvest(mTrees.at(i).first->volume());
183
                } else {
885 werner 184
                    mTrees.at(i).first->remove(removeFoliage(), removeBranch(), removeStem());
889 werner 185
                }
885 werner 186
        } else {
187
            // just kill...
188
            for (i=index_from; i<index_to; i++)
889 werner 189
                if (simulate()) {
885 werner 190
                    mTrees.at(i).first->markForCut(true);
889 werner 191
                    mStand->addScheduledHarvest(mTrees.at(i).first->volume());
192
                } else
885 werner 193
                    mTrees.at(i).first->remove();
194
        }
195
        count = index_to - index_from;
196
    } else {
197
        // kill randomly the provided number
198
        int cancel = 1000;
199
        while(number>=0) {
200
            int rnd_index = irandom(index_from, index_to);
923 werner 201
            Tree *tree = mTrees[rnd_index].first;
202
            if (tree->isDead() || tree->isMarkedForHarvest() || tree->isMarkedForCut()) {
885 werner 203
                if (--cancel<0) {
204
                    qDebug() << "Management::kill: canceling search." << number << "trees left.";
205
                    count-=number; // not all trees were killed
206
                    break;
207
                }
208
                continue;
209
            }
210
            cancel = 1000;
211
            number--;
212
            if (management) {
889 werner 213
                if (simulate()) {
923 werner 214
                    tree->markForHarvest(true);
215
                    mStand->addScheduledHarvest( tree->volume());
889 werner 216
                } else
923 werner 217
                    tree->remove( removeFoliage(), removeBranch(), removeStem() );
885 werner 218
            } else {
889 werner 219
                if (simulate()) {
923 werner 220
                    tree->markForCut(true);
221
                    mStand->addScheduledHarvest( tree->volume());
889 werner 222
                } else
923 werner 223
                    tree->remove();
885 werner 224
            }
225
        }
226
    }
923 werner 227
    if (mStand && mStand->trace())
228
        qCDebug(abe) << "FMTreeList::remove_percentiles:" << count << "removed.";
885 werner 229
    // clean up the tree list...
230
    for (int i=mTrees.count()-1; i>=0; --i) {
231
        if (mTrees[i].first->isDead())
232
            mTrees.removeAt(i);
233
    }
234
    return count; // killed or manages
235
 
236
}
237
 
238
/** remove trees from a list and reduce the list.
239
 
240
  */
241
int FMTreeList::remove_trees(QString expression, double fraction, bool management)
242
{
243
    TreeWrapper tw;
244
    if (expression.isEmpty())
245
        expression="true";
246
    Expression expr(expression,&tw);
247
    expr.enableIncSum();
248
    int n = 0;
249
    QVector<QPair<Tree*, double> >::iterator tp=mTrees.begin();
250
    try {
251
        while (tp!=mTrees.end()) {
252
            tw.setTree(tp->first);
253
            // if expression evaluates to true and if random number below threshold...
254
            if (expr.calculate(tw) && drandom() <=fraction) {
255
                // remove from system
256
                if (management) {
889 werner 257
                    if (simulate()) {
885 werner 258
                        tp->first->markForHarvest(true);
889 werner 259
                        mStand->addScheduledHarvest(tp->first->volume());
1070 werner 260
                    } else {
261
                        tp->first->markForHarvest(true);
885 werner 262
                        tp->first->remove(removeFoliage(), removeBranch(), removeStem()); // management with removal fractions
1070 werner 263
                    }
885 werner 264
                } else {
889 werner 265
                    if (simulate()) {
885 werner 266
                        tp->first->markForCut(true);
1070 werner 267
                        tp->first->setDeathCutdown();
889 werner 268
                        mStand->addScheduledHarvest(tp->first->volume());
1070 werner 269
                    } else {
270
                        tp->first->markForCut(true);
271
                        tp->first->setDeathCutdown();
885 werner 272
                        tp->first->remove(); // kill
1070 werner 273
                    }
885 werner 274
                }
275
                // remove from tree list
276
                tp = mTrees.erase(tp);
277
                n++;
278
            } else {
279
                ++tp;
280
            }
281
        }
282
    } catch(const IException &e) {
909 werner 283
        qCWarning(abe) << "treelist: remove_trees: expression:" << expression << ", msg:" << e.message();
885 werner 284
    }
285
    return n;
286
 
287
}
288
 
289
double FMTreeList::aggregate_function(QString expression, QString filter, QString type)
290
{
291
    QVector<QPair<Tree*, double> >::iterator tp=mTrees.begin();
292
    TreeWrapper tw;
293
    Expression expr(expression,&tw);
294
 
295
    double sum = 0.;
296
    int n=0;
297
    try {
298
 
299
        if (filter.isEmpty()) {
300
            // without filtering
301
            while (tp!=mTrees.end()) {
302
                tw.setTree(tp->first);
303
                sum += expr.calculate();
304
                ++n;
305
                ++tp;
306
            }
307
        } else {
308
            // with filtering
309
            Expression filter_expr(filter,&tw);
310
            filter_expr.enableIncSum();
311
            while (tp!=mTrees.end()) {
312
                tw.setTree(tp->first);
313
                if (filter_expr.calculate()) {
314
                    sum += expr.calculate();
315
                    ++n;
316
                }
317
                ++tp;
318
            }
319
        }
320
 
321
    } catch(const IException &e) {
909 werner 322
        qCWarning(abe) << "Treelist: aggregate function: expression:" << expression << ", filter:" << filter << ", msg:" <<e.message();
885 werner 323
        //throwError(e.message());
324
    }
325
    if (type=="sum")
326
        return sum;
327
    if (type=="mean")
328
        return n>0?sum/double(n):0.;
329
    return 0.;
330
 
331
}
332
 
930 werner 333
bool FMTreeList::remove_single_tree(int index, bool harvest)
334
{
335
    if (!mStand || index<0 || index>=mTrees.size())
336
        return false;
337
    Tree *tree = mTrees.at(index).first;
338
    if (harvest) {
339
        if (simulate()) {
340
            tree->markForHarvest(true);
341
            mStand->addScheduledHarvest( tree->volume());
342
        } else
343
            tree->remove( removeFoliage(), removeBranch(), removeStem() );
344
    } else {
345
        if (simulate()) {
346
            tree->markForCut(true);
347
            mStand->addScheduledHarvest(  tree->volume());
348
        } else
349
            tree->remove();
350
    }
351
    return true;
352
}
923 werner 353
 
930 werner 354
 
923 werner 355
bool treePairValue(const QPair<Tree*, double> &p1, const QPair<Tree*, double> &p2)
356
{
357
    return p1.second < p2.second;
358
}
359
 
360
void FMTreeList::sort(QString statement)
361
{
362
    TreeWrapper tw;
363
    Expression sorter(statement, &tw);
364
    // fill the "value" part of the tree storage with a value for each tree
365
    for (int i=0;i<mTrees.count(); ++i) {
366
        tw.setTree(mTrees.at(i).first);
367
        mTrees[i].second = sorter.execute();
368
   }
369
   // now sort the list....
370
   qSort(mTrees.begin(), mTrees.end(), treePairValue);
371
}
372
 
373
double FMTreeList::percentile(int pct)
374
{
375
    if (mTrees.count()==0)
376
        return -1.;
377
    int idx = int( (pct/100.) * mTrees.count());
378
    if (idx>=0 && idx<mTrees.count())
379
        return mTrees.at(idx).second;
380
    else
381
        return -1;
382
}
383
 
384
/// random shuffle of all trees in the list
385
void FMTreeList::randomize()
386
{
387
    // fill the "value" part of the tree storage with a random value for each tree
388
    for (int i=0;i<mTrees.count(); ++i) {
389
        mTrees[i].second = drandom();
390
    }
391
    // now sort the list....
392
    qSort(mTrees.begin(), mTrees.end(), treePairValue);
393
 
394
}
395
 
396
 
912 werner 397
void FMTreeList::prepareGrids()
398
{
399
    QRectF box = ForestManagementEngine::instance()->standGrid()->boundingBox(mStand->id());
400
    if (mStandRect==box)
401
        return;
402
    mStandRect = box;
403
    // the memory of the grids is only reallocated if the current box is larger then the previous...
404
    mStandGrid.setup(box, cHeightSize);
405
    mTreeCountGrid.setup(box, cHeightSize);
951 werner 406
    mLocalGrid.setup(box, cPxSize);
913 werner 407
    // mark areas outside of the grid...
408
    GridRunner<int> runner(ForestManagementEngine::instance()->standGrid()->grid(), box);
409
    float *p=mStandGrid.begin();
410
    while (runner.next()) {
411
        if (*runner.current()!=mStand->id())
914 werner 412
            *p=-1.f;
913 werner 413
        ++p;
414
    }
951 werner 415
    // copy stand limits to the grid
416
    for (int iy=0;iy<mLocalGrid.sizeY();++iy)
417
        for (int ix=0;ix<mLocalGrid.sizeX();++ix)
418
            mLocalGrid.valueAtIndex(ix,iy) = mStandGrid.valueAtIndex(ix/cPxPerHeight, iy/cPxPerHeight)==-1.f ? -1.f: 0.f;
912 werner 419
}
885 werner 420
 
912 werner 421
void FMTreeList::runGrid(void (*func)(float &, int &, const Tree *, const FMTreeList *))
422
{
423
    if (mStandRect.isNull())
424
        prepareGrids();
885 werner 425
 
1157 werner 426
    // set all values to 0 (within the limits of the stand grid)
427
    for (float *p=mStandGrid.begin(); p!=mStandGrid.end(); ++p)
428
        if (*p!=-1.f)
429
            *p=0.f;
912 werner 430
    mTreeCountGrid.initialize(0);
1070 werner 431
    int invalid_index = 0;
912 werner 432
    for (QVector<QPair<Tree*, double> >::const_iterator it=mTrees.constBegin(); it!=mTrees.constEnd(); ++it) {
433
        const Tree* tree = it->first;
434
        QPoint p = mStandGrid.indexAt(tree->position());
1070 werner 435
        if (mStandGrid.isIndexValid(p))
436
            (*func)(mStandGrid.valueAtIndex(p), mTreeCountGrid.valueAtIndex(p), tree, this);
437
        else
438
            ++invalid_index;
912 werner 439
    }
1070 werner 440
    if (invalid_index)
441
        qDebug() << "FMTreeList::runGrid: invalid index: n=" << invalid_index;
885 werner 442
 
912 werner 443
    // finalization: call again for each *cell*
444
    for (int i=0;i<mStandGrid.count();++i)
445
        (*func)(mStandGrid.valueAtIndex(i), mTreeCountGrid.valueAtIndex(i), 0, this);
446
 
447
}
448
 
449
void rungrid_heightmax(float &cell, int &n, const Tree *tree, const FMTreeList *list)
450
{
451
    Q_UNUSED(n); Q_UNUSED(list);
452
    if (tree)
453
        cell = qMax(cell, tree->height());
454
}
455
void rungrid_basalarea(float &cell, int &n, const Tree *tree, const FMTreeList *list)
456
{
457
    Q_UNUSED(list);
458
    if (tree) {
459
        cell += tree->basalArea();
460
        ++n;
461
    } else {
462
        if (n>0)
463
            cell /= float(n);
464
    }
465
}
466
void rungrid_volume(float &cell, int &n, const Tree *tree, const FMTreeList *list)
467
{
468
    Q_UNUSED(list);
469
    if (tree) {
470
        cell += tree->volume();
471
        ++n;
472
    } else {
473
        if (n>0)
474
            cell /= float(n);
475
    }
476
}
477
 
478
void rungrid_custom(float &cell, int &n, const Tree *tree, const FMTreeList *list)
479
{
480
    if (tree) {
481
        *list->mRunGridCustomCell = cell;
482
        TreeWrapper tw(tree);
1157 werner 483
        cell = static_cast<float>(list->mRunGridCustom->calculate(tw));
912 werner 484
        ++n;
485
    }
486
}
487
void FMTreeList::prepareStandGrid(QString type, QString custom_expression)
488
{
489
    if(!mStand){
490
        qCDebug(abe) << "Error: FMTreeList: no current stand defined.";
491
        return;
492
    }
493
 
494
    if (type==QStringLiteral("height")) {
495
        return runGrid(&rungrid_heightmax);
496
    }
497
 
498
    if (type==QStringLiteral("basalArea"))
499
        return runGrid(&rungrid_basalarea);
500
 
501
    if (type==QStringLiteral("volume"))
502
        return runGrid(&rungrid_volume);
503
 
504
    if (type==QStringLiteral("custom")) {
505
        mRunGridCustom = new Expression(custom_expression);
506
        mRunGridCustomCell = mRunGridCustom->addVar("cell");
507
        runGrid(&rungrid_custom);
508
        delete mRunGridCustom;
509
        mRunGridCustom = 0;
510
        return;
511
    }
512
    qCDebug(abe) << "FMTreeList: invalid type for prepareStandGrid: " << type;
513
}
913 werner 514
 
515
void FMTreeList::exportStandGrid(QString file_name)
516
{
517
    file_name = GlobalSettings::instance()->path(file_name);
518
    Helper::saveToTextFile(file_name, gridToESRIRaster(mStandGrid) );
519
    qCDebug(abe) << "saved grid to file" << file_name;
520
}
932 werner 521
 
522
void FMTreeList::check_locks()
523
{
933 werner 524
    // removed the locking code again, WR20140821
525
//    if (mStand && mResourceUnitsLocked) {
526
//        const MapGrid *map = ForestManagementEngine::instance()->standGrid();
527
//        if (map->isValid()) {
528
//            map->freeLocksForStand(mStandId);
529
//            mResourceUnitsLocked = false;
530
//        }
531
//    }
932 werner 532
}
533
 
884 werner 534
} // namespace