Subversion Repositories public iLand

Rev

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