Subversion Repositories public iLand

Rev

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