Subversion Repositories public iLand

Rev

Rev 604 | Rev 639 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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