Subversion Repositories public iLand

Rev

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