Subversion Repositories public iLand

Rev

Rev 767 | Rev 793 | 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
 
185 werner 21
#include "global.h"
22
#include "management.h"
23
#include "helper.h"
186 werner 24
#include "model.h"
189 iland 25
#include "resourceunit.h"
186 werner 26
#include "tree.h"
216 werner 27
#include "expressionwrapper.h"
564 werner 28
#include "sapling.h"
566 werner 29
#include "soil.h"
186 werner 30
 
767 werner 31
//#include "climateconverter.h"
32
//#include "csvfile.h"
247 werner 33
#include "scriptglobal.h"
552 werner 34
#include "mapgrid.h"
767 werner 35
//#include "modules.h"
186 werner 36
 
185 werner 37
#include <QtScript>
38
 
697 werner 39
/** @class Management Management executes management routines.
40
  @ingroup core
41
  The actual iLand management is based on Javascript functions. This class provides
42
  the frame for executing the javascript as well as the functions that are called by scripts and
43
  that really do the work.
44
  See http://iland.boku.ac.at/iLand+scripting, http://iland.boku.ac.at/Object+Management for management Javascript API.
45
  */
46
 
186 werner 47
 
216 werner 48
// global output function
49
QString Management::executeScript(QString cmd)
50
{
767 werner 51
    return ScriptGlobal::executeScript(cmd);
216 werner 52
}
53
 
185 werner 54
Management::Management()
55
{
564 werner 56
    // setup the scripting engine
767 werner 57
    mEngine = GlobalSettings::instance()->scriptEngine();
185 werner 58
    QScriptValue objectValue = mEngine->newQObject(this);
59
    mEngine->globalObject().setProperty("management", objectValue);
247 werner 60
 
564 werner 61
    // default values for removal fractions
62
    // 100% of the stem, 0% of foliage and branches
63
    mRemoveFoliage = 0.;
64
    mRemoveBranch = 0.;
65
    mRemoveStem = 1.;
247 werner 66
 
185 werner 67
}
68
 
69
Management::~Management()
70
{
71
}
72
 
73
 
566 werner 74
int Management::remain(int number)
185 werner 75
{
76
    qDebug() << "remain called (number): " << number;
186 werner 77
    Model *m = GlobalSettings::instance()->model();
78
    AllTreeIterator at(m);
79
    QList<Tree*> trees;
80
    while (Tree *t=at.next())
81
        trees.push_back(t);
82
    int to_kill = trees.count() - number;
83
    qDebug() << trees.count() << " standing, targetsize" << number << ", hence " << to_kill << "trees to remove";
84
    for (int i=0;i<to_kill;i++) {
349 werner 85
        int index = irandom(0, trees.count()-1);
278 werner 86
        trees[index]->remove();
186 werner 87
        trees.removeAt(index);
88
    }
89
    mRemoved += to_kill;
566 werner 90
    return to_kill;
185 werner 91
}
92
 
93
 
564 werner 94
int Management::kill()
252 werner 95
{
564 werner 96
    int c = mTrees.count();
252 werner 97
    for (int i=0;i<mTrees.count();i++)
278 werner 98
        mTrees[i].first->remove();
252 werner 99
    mTrees.clear();
564 werner 100
    return c;
252 werner 101
}
102
 
579 werner 103
int Management::kill(QString filter, double fraction)
104
{
105
   return remove_trees(filter, fraction, false);
106
}
107
int Management::manage(QString filter, double fraction)
108
{
109
    return remove_trees(filter, fraction, true);
110
}
111
 
564 werner 112
int Management::remove_percentiles(int pctfrom, int pctto, int number, bool management)
216 werner 113
{
114
    if (mTrees.isEmpty())
115
        return 0;
116
    int index_from = limit(int(pctfrom/100. * mTrees.count()), 0, mTrees.count());
346 werner 117
    int index_to = limit(int(pctto/100. * mTrees.count()), 0, mTrees.count()-1);
216 werner 118
    if (index_from>=index_to)
119
        return 0;
217 werner 120
    qDebug() << "attempting to remove" << number << "trees between indices" << index_from << "and" << index_to;
216 werner 121
    int i;
122
    int count = number;
217 werner 123
    if (index_to-index_from <= number)  {
216 werner 124
        // kill all
564 werner 125
        if (management) {
126
            // management
127
            for (i=index_from; i<index_to; i++)
128
                mTrees.at(i).first->remove(removeFoliage(), removeBranch(), removeStem());
129
        } else {
130
            // just kill...
131
            for (i=index_from; i<index_to; i++)
132
                mTrees.at(i).first->remove();
133
        }
216 werner 134
        count = index_to - index_from;
135
    } else {
136
        // kill randomly the provided number
137
        int cancel = 1000;
138
        while(number>=0) {
139
            int rnd_index = irandom(index_from, index_to);
140
            if (mTrees[rnd_index].first->isDead()) {
141
                if (--cancel<0) {
142
                    qDebug() << "Management::kill: canceling search." << number << "trees left.";
217 werner 143
                    count-=number; // not all trees were killed
216 werner 144
                    break;
145
                }
146
                continue;
147
            }
148
            cancel = 1000;
149
            number--;
564 werner 150
            if (management) {
151
                mTrees[rnd_index].first->remove( removeFoliage(), removeBranch(), removeStem() );
152
            } else {
153
                mTrees[rnd_index].first->remove();
154
            }
216 werner 155
        }
156
    }
217 werner 157
    qDebug() << count << "removed.";
564 werner 158
    // clean up the tree list...
159
    for (int i=mTrees.count()-1; i>=0; --i) {
160
        if (mTrees[i].first->isDead())
161
            mTrees.removeAt(i);
162
    }
163
    return count; // killed or manages
216 werner 164
}
165
 
579 werner 166
/** remove trees from a list and reduce the list.
167
 
168
  */
169
int Management::remove_trees(QString expression, double fraction, bool management)
170
{
171
    TreeWrapper tw;
172
    Expression expr(expression,&tw);
173
    expr.enableIncSum();
174
    int n = 0;
175
    QList<QPair<Tree*, double> >::iterator tp=mTrees.begin();
176
    try {
177
        while (tp!=mTrees.end()) {
178
            tw.setTree(tp->first);
179
            // if expression evaluates to true and if random number below threshold...
180
            if (expr.calculate(tw) && drandom() <=fraction) {
181
                // remove from system
182
                if (management)
183
                    tp->first->remove(removeFoliage(), removeBranch(), removeStem()); // management with removal fractions
184
                else
185
                    tp->first->remove(); // kill
186
                // remove from tree list
187
                tp = mTrees.erase(tp);
188
                n++;
189
            } else {
753 werner 190
                ++tp;
579 werner 191
            }
192
        }
193
    } catch(const IException &e) {
194
        context()->throwError(e.message());
195
    }
196
    return n;
197
}
198
 
199
// calculate aggregates for all trees in the internal list
200
double Management::aggregate_function(QString expression, QString filter, QString type)
201
{
202
    QList<QPair<Tree*, double> >::iterator tp=mTrees.begin();
203
    TreeWrapper tw;
204
    Expression expr(expression,&tw);
205
 
206
    double sum = 0.;
207
    int n=0;
208
    try {
209
 
210
        if (filter.isEmpty()) {
211
            // without filtering
212
            while (tp!=mTrees.end()) {
213
                tw.setTree(tp->first);
214
                sum += expr.calculate();
215
                ++n;
216
                ++tp;
217
            }
218
        } else {
219
            // with filtering
220
            Expression filter_expr(filter,&tw);
221
            filter_expr.enableIncSum();
222
            while (tp!=mTrees.end()) {
223
                tw.setTree(tp->first);
224
                if (filter_expr.calculate()) {
225
                    sum += expr.calculate();
226
                    ++n;
227
                }
228
                ++tp;
229
            }
230
        }
231
 
232
    } catch(const IException &e) {
233
        context()->throwError(e.message());
234
    }
235
    if (type=="sum")
236
        return sum;
237
    if (type=="mean")
238
        return n>0?sum/double(n):0.;
239
    return 0.;
240
}
241
 
242
 
564 werner 243
// from the range percentile range pctfrom to pctto (each 1..100)
244
int Management::kill(int pctfrom, int pctto, int number)
245
{
246
    return remove_percentiles(pctfrom, pctto, number, false);
247
}
248
 
249
// from the range percentile range pctfrom to pctto (each 1..100)
250
int Management::manage(int pctfrom, int pctto, int number)
251
{
252
    return remove_percentiles(pctfrom, pctto, number, true);
253
}
254
 
255
int Management::manage()
256
{
257
    int c = mTrees.count();
258
    for (int i=0;i<mTrees.count();i++)
259
        mTrees[i].first->remove(removeFoliage(),
260
                                removeBranch(),
261
                                removeStem()); // remove with current removal fractions
262
    mTrees.clear();
263
    return c;
264
}
265
 
266
 
267
 
185 werner 268
void Management::run()
269
{
216 werner 270
    mTrees.clear();
186 werner 271
    mRemoved=0;
185 werner 272
    qDebug() << "Management::run() called";
273
    QScriptValue mgmt = mEngine->globalObject().property("manage");
274
    int year = GlobalSettings::instance()->currentYear();
275
    mgmt.call(QScriptValue(), QScriptValueList()<<year);
276
    if (mEngine->hasUncaughtException())
590 werner 277
        qDebug() << "Script Error occured: " << mEngine->uncaughtException().toString() << "\n" << mEngine->uncaughtExceptionBacktrace();
185 werner 278
 
186 werner 279
    if (mRemoved>0) {
187 iland 280
        foreach(ResourceUnit *ru, GlobalSettings::instance()->model()->ruList())
186 werner 281
           ru->cleanTreeList();
767 werner 282
    }
185 werner 283
}
216 werner 284
 
767 werner 285
void Management::loadScript(const QString &fileName)
286
{
287
    mScriptFile = fileName;
288
    ScriptGlobal::loadScript(fileName);
289
}
290
 
250 werner 291
int Management::filter(QVariantList idList)
292
{
389 werner 293
    QVector<int> ids;
250 werner 294
    foreach(const QVariant &v, idList)
389 werner 295
        if (!v.isNull())
296
            ids << v.toInt();
297
//    QHash<int, int> ids;
298
//    foreach(const QVariant &v, idList)
299
//        ids[v.toInt()] = 1;
216 werner 300
 
250 werner 301
    QList<QPair<Tree*, double> >::iterator tp=mTrees.begin();
302
    while (tp!=mTrees.end()) {
303
        if (!ids.contains(tp->first->id()) )
304
            tp = mTrees.erase(tp);
305
        else
753 werner 306
            ++tp;
250 werner 307
    }
389 werner 308
    qDebug() << "Management::filter by id-list:" << mTrees.count();
250 werner 309
    return mTrees.count();
310
}
311
 
312
int Management::filter(QString filter)
313
{
314
    TreeWrapper tw;
315
    Expression expr(filter,&tw);
579 werner 316
    expr.enableIncSum();
575 werner 317
    int n_before = mTrees.count();
250 werner 318
    QList<QPair<Tree*, double> >::iterator tp=mTrees.begin();
575 werner 319
    try {
320
        while (tp!=mTrees.end()) {
321
            tw.setTree(tp->first);
579 werner 322
            if (expr.calculate(tw))
575 werner 323
                tp = mTrees.erase(tp);
324
            else
753 werner 325
                ++tp;
575 werner 326
        }
327
    } catch(const IException &e) {
328
        context()->throwError(e.message());
250 werner 329
    }
575 werner 330
 
331
    qDebug() << "filtering with" << filter << "N=" << n_before << "/" << mTrees.count()  << "trees (before/after filtering).";
250 werner 332
    return mTrees.count();
333
}
334
 
294 werner 335
int Management::load(int ruindex)
336
{
337
    Model *m = GlobalSettings::instance()->model();
338
    ResourceUnit *ru = m->ru(ruindex);
339
    if (!ru)
340
        return -1;
341
    mTrees.clear();
342
    for (int i=0;i<ru->trees().count();i++)
579 werner 343
        if (!ru->tree(i)->isDead())
344
            mTrees.push_back(QPair<Tree*,double>(ru->tree(i), 0.));
294 werner 345
    return mTrees.count();
346
}
347
 
216 werner 348
int Management::load(QString filter)
349
{
350
    TreeWrapper tw;
351
    Model *m = GlobalSettings::instance()->model();
352
    mTrees.clear();
353
    AllTreeIterator at(m);
354
    if (filter.isEmpty()) {
579 werner 355
        while (Tree *t=at.nextLiving())
216 werner 356
            if (!t->isDead())
357
                mTrees.push_back(QPair<Tree*, double>(t, 0.));
358
    } else {
359
        Expression expr(filter,&tw);
579 werner 360
        expr.enableIncSum();
216 werner 361
        qDebug() << "filtering with" << filter;
579 werner 362
        while (Tree *t=at.nextLiving()) {
216 werner 363
            tw.setTree(t);
364
            if (!t->isDead() && expr.execute())
365
                mTrees.push_back(QPair<Tree*, double>(t, 0.));
366
        }
367
    }
368
    return mTrees.count();
369
}
370
 
555 werner 371
/**
372
*/
544 werner 373
void Management::loadFromTreeList(QList<Tree*>tree_list)
374
{
375
    mTrees.clear();
376
    for (int i=0;i<tree_list.count();++i)
377
        mTrees.append(QPair<Tree*, double>(tree_list[i], 0.));
378
}
379
 
555 werner 380
// loadFromMap: script access
604 werner 381
void Management::loadFromMap(MapGridWrapper *wrap, int key)
552 werner 382
{
555 werner 383
    if (!wrap) {
384
        context()->throwError("loadFromMap called with invalid map object!");
385
        return;
386
    }
387
    loadFromMap(wrap->map(), key);
388
}
389
 
604 werner 390
void Management::killSaplings(MapGridWrapper *wrap, int key)
564 werner 391
{
604 werner 392
    //MapGridWrapper *wrap = qobject_cast<MapGridWrapper*>(map_grid_object.toQObject());
393
    //if (!wrap) {
394
    //    context()->throwError("loadFromMap called with invalid map object!");
395
    //    return;
396
    //}
564 werner 397
    //loadFromMap(wrap->map(), key);
398
    // retrieve all sapling trees on the stand:
399
    QList<QPair<ResourceUnitSpecies *, SaplingTree *> > list = wrap->map()->saplingTrees(key);
400
    // for now, just kill em all...
662 werner 401
    for (QList<QPair<ResourceUnitSpecies *, SaplingTree *> >::iterator it = list.begin(); it!=list.end(); ++it) {
402
        // (*it).second->pixel = 0;
403
        (*it).first->changeSapling().clearSapling( *(*it).second, false); // kill and move biomass to soil
404
    }
405
 
564 werner 406
    // the storage for unused/invalid saplingtrees is released lazily (once a year, after growth)
407
}
408
 
604 werner 409
/// specify removal fractions
410
/// @param SWDFrac 0: no change, 1: remove all of standing woody debris
411
/// @param DWDfrac 0: no change, 1: remove all of downled woody debris
412
/// @param litterFrac 0: no change, 1: remove all of soil litter
413
/// @param soilFrac 0: no change, 1: remove all of soil organic matter
414
void Management::removeSoilCarbon(MapGridWrapper *wrap, int key, double SWDfrac, double DWDfrac, double litterFrac, double soilFrac)
565 werner 415
{
590 werner 416
    if (!(SWDfrac>=0. && SWDfrac<=1. && DWDfrac>=0. && DWDfrac<=1. && soilFrac>=0. && soilFrac<=1. && litterFrac>=0. && litterFrac<=1.)) {
417
        context()->throwError(QString("removeSoilCarbon called with invalid parameters!!\nArgs: %1").arg(context()->argumentsObject().toString()));
418
        return;
419
    }
565 werner 420
    QList<QPair<ResourceUnit*, double> > ru_areas = wrap->map()->resourceUnitAreas(key);
421
    double total_area = 0.;
422
    for (int i=0;i<ru_areas.size();++i) {
423
        ResourceUnit *ru = ru_areas[i].first;
424
        double area_factor = ru_areas[i].second; // 0..1
425
        total_area += area_factor;
566 werner 426
        // swd
604 werner 427
        if (SWDfrac>0.)
428
            ru->snag()->removeCarbon(SWDfrac*area_factor);
566 werner 429
        // soil pools
430
        ru->soil()->disturbance(DWDfrac*area_factor, litterFrac*area_factor, soilFrac*area_factor);
585 werner 431
        // qDebug() << ru->index() << area_factor;
565 werner 432
    }
433
    qDebug() << "total area" << total_area << "of" << wrap->map()->area(key);
434
}
435
 
607 werner 436
/** slash snags (SWD and otherWood-Pools) of polygon \p key on the map \p wrap.
437
  The factor is scaled to the overlapping area of \p key on the resource unit.
438
  @param wrap MapGrid to use together with \p key
439
  @param key ID of the polygon.
440
  @param slash_fraction 0: no change, 1: 100%
441
   */
442
void Management::slashSnags(MapGridWrapper *wrap, int key, double slash_fraction)
443
{
444
    if (slash_fraction<0 || slash_fraction>1) {
445
        context()->throwError(QString("slashSnags called with invalid parameters!!\nArgs: %1").arg(context()->argumentsObject().toString()));
446
        return;
447
    }
448
    QList<QPair<ResourceUnit*, double> > ru_areas = wrap->map()->resourceUnitAreas(key);
449
    double total_area = 0.;
450
    for (int i=0;i<ru_areas.size();++i) {
451
        ResourceUnit *ru = ru_areas[i].first;
452
        double area_factor = ru_areas[i].second; // 0..1
453
        total_area += area_factor;
454
        ru->snag()->management(slash_fraction * area_factor);
455
        // qDebug() << ru->index() << area_factor;
456
    }
457
    qDebug() << "total area" << total_area << "of" << wrap->map()->area(key);
458
 
459
}
460
 
555 werner 461
/** loadFromMap selects trees located on pixels with value 'key' within the grid 'map_grid'.
462
*/
463
void Management::loadFromMap(const MapGrid *map_grid, int key)
464
{
465
    if (!map_grid) {
552 werner 466
        qDebug() << "invalid parameter for Management::loadFromMap: Map expected!";
467
        return;
468
    }
555 werner 469
    if (map_grid->isValid()) {
470
        QList<Tree*> tree_list = map_grid->trees(key);
552 werner 471
        loadFromTreeList( tree_list );
472
    } else {
473
        qDebug() << "Management::loadFromMap: grid is not valid - no trees loaded";
474
    }
475
 
476
}
477
 
216 werner 478
bool treePairValue(const QPair<Tree*, double> &p1, const QPair<Tree*, double> &p2)
479
{
480
    return p1.second < p2.second;
481
}
482
 
483
void Management::sort(QString statement)
484
{
485
    TreeWrapper tw;
486
    Expression sorter(statement, &tw);
487
    // fill the "value" part of the tree storage with a value for each tree
488
    for (int i=0;i<mTrees.count(); ++i) {
489
        tw.setTree(mTrees.at(i).first);
490
        mTrees[i].second = sorter.execute();
491
   }
492
   // now sort the list....
493
   qSort(mTrees.begin(), mTrees.end(), treePairValue);
494
}
495
 
496
double Management::percentile(int pct)
497
{
498
    if (mTrees.count()==0)
499
        return -1.;
500
    int idx = int( (pct/100.) * mTrees.count());
501
    if (idx>=0 && idx<mTrees.count())
502
        return mTrees.at(idx).second;
503
    else
504
        return -1;
505
}
564 werner 506
 
579 werner 507
/// random shuffle of all trees in the list
508
void Management::randomize()
509
{
510
    // fill the "value" part of the tree storage with a random value for each tree
511
    for (int i=0;i<mTrees.count(); ++i) {
512
        mTrees[i].second = drandom();
513
    }
514
    // now sort the list....
515
    qSort(mTrees.begin(), mTrees.end(), treePairValue);
516
 
517
}
518
 
519
 
520
 
607 werner 521
 
522