Subversion Repositories public iLand

Rev

Rev 1104 | Rev 1217 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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