Subversion Repositories public iLand

Rev

Rev 1221 | Details | Compare with Previous | Last modification | View Log | RSS feed

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