Subversion Repositories public iLand

Rev

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