Subversion Repositories public iLand

Rev

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