Subversion Repositories public iLand

Rev

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