Subversion Repositories public iLand

Rev

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

Rev Author Line No. Line
1
 
106 Werner 2
#include "global.h"
3
#include "standloader.h"
4
 
290 werner 5
 
106 Werner 6
#include "grid.h"
7
#include "model.h"
189 iland 8
#include "resourceunit.h"
106 Werner 9
#include "speciesset.h"
10
 
11
#include "helper.h"
290 werner 12
#include "random.h"
137 Werner 13
#include "expression.h"
140 Werner 14
#include "expressionwrapper.h"
284 werner 15
#include "environment.h"
287 werner 16
#include "csvfile.h"
106 Werner 17
 
18
 
155 werner 19
QStringList picusSpeciesIds = QStringList() << "0" << "1" << "17";
20
QStringList iLandSpeciesIds = QStringList() << "piab" << "piab" << "fasy";
290 werner 21
StandLoader::~StandLoader()
22
{
23
    if (mRandom)
24
        delete mRandom;
25
}
106 Werner 26
 
281 werner 27
void StandLoader::loadForUnit()
28
{
106 Werner 29
 
281 werner 30
}
106 Werner 31
 
281 werner 32
void StandLoader::copyTrees()
33
{
34
    // we assume that all stands are equal, so wie simply COPY the trees and modify them afterwards
35
    const Grid<ResourceUnit*> &ruGrid=mModel->RUgrid();
36
    ResourceUnit **p = ruGrid.begin();
37
    if (!p)
38
        throw IException("Standloader: invalid resource unit pointer!");
39
    ++p; // skip the first...
40
    const QVector<Tree> &tocopy = mModel->ru()->trees();
41
    for (; p!=ruGrid.end(); ++p) {
42
        QRectF rect = (*p)->boundingBox();
43
        foreach(const Tree& tree, tocopy) {
44
            Tree &newtree = (*p)->newTree();
45
            newtree = tree; // copy tree data...
46
            newtree.setPosition(tree.position()+(*p)->boundingBox().topLeft());
47
            newtree.setRU(*p);
48
            newtree.setNewId();
49
        }
50
    }
51
    qDebug() << Tree::statCreated() << "trees loaded / copied.";
52
}
53
 
284 werner 54
/** main routine of the stand setup.
55
*/
106 Werner 56
void StandLoader::processInit()
57
{
58
    GlobalSettings *g = GlobalSettings::instance();
192 werner 59
    XmlHelper xml(g->settings().node("model.initialization"));
191 werner 60
 
281 werner 61
    QString copy_mode = xml.value("mode", "copy");
62
    QString type = xml.value("type", "");
192 werner 63
    QString  fileName = xml.value("file", "");
106 Werner 64
 
65
    Tree::resetStatistics();
281 werner 66
 
67
    // one global init-file for the whole area:
284 werner 68
    if (copy_mode=="single") {
281 werner 69
        loadInitFile(fileName, type);
299 werner 70
        evaluateDebugTrees();
281 werner 71
        return;
284 werner 72
    }
281 werner 73
 
74
    // copy trees from first unit to all other units:
75
    if (copy_mode=="copy") {
76
        loadInitFile(fileName, type);
77
        copyTrees();
299 werner 78
        evaluateDebugTrees();
281 werner 79
        return;
106 Werner 80
    }
137 Werner 81
 
281 werner 82
    // call a single tree init for each resource unit
83
    if (copy_mode=="unit") {
284 werner 84
        foreach( const ResourceUnit *const_ru, g->model()->ruList()) {
85
            ResourceUnit *ru = const_cast<ResourceUnit*>(const_ru);
86
            // set environment
87
            g->model()->environment()->setPosition(ru->boundingBox().center());
88
            type = xml.value("type", "");
89
            fileName = xml.value("file", "");
319 werner 90
            if (fileName.isEmpty())
91
                continue;
284 werner 92
            loadInitFile(fileName, type, ru);
93
            qDebug() << "loaded" << fileName << "on" << ru->boundingBox() << "," << ru->trees().count() << "trees.";
94
        }
299 werner 95
        evaluateDebugTrees();
284 werner 96
        return;
281 werner 97
    }
299 werner 98
 
281 werner 99
    throw IException("StandLoader::processInit: invalid initalization.mode!");
100
}
101
 
102
void StandLoader::evaluateDebugTrees()
103
{
137 Werner 104
    // evaluate debugging
105
    QString dbg_str = GlobalSettings::instance()->settings().paramValueString("debug_tree");
299 werner 106
    int counter=0;
137 Werner 107
    if (!dbg_str.isEmpty()) {
140 Werner 108
       TreeWrapper tw;
109
       Expression dexp(dbg_str, &tw); // load expression dbg_str and enable external model variables
137 Werner 110
        AllTreeIterator at(GlobalSettings::instance()->model());
111
        double result;
112
        while (Tree *t = at.next()) {
140 Werner 113
            tw.setTree(t);
137 Werner 114
            result = dexp.execute();
299 werner 115
            if (result) {
137 Werner 116
                t->enableDebugging();
299 werner 117
                counter++;
118
            }
137 Werner 119
        }
120
    }
299 werner 121
    qDebug() << "evaluateDebugTrees: enabled debugging for" << counter << "trees.";
106 Werner 122
}
123
 
284 werner 124
void StandLoader::loadInitFile(const QString &fileName, const QString &type, ResourceUnit *ru)
281 werner 125
{
284 werner 126
    QString pathFileName = GlobalSettings::instance()->path(fileName, "init");
127
    if (!QFile::exists(pathFileName))
128
        throw IException(QString("StandLoader::loadInitFile: File %1 does not exist!").arg(pathFileName));
281 werner 129
 
130
    if (type=="picus")
294 werner 131
        return loadPicusFile(pathFileName, ru);
281 werner 132
    if (type=="iland")
287 werner 133
        return loadiLandFile(pathFileName, ru);
281 werner 134
 
135
    throw IException("StandLoader::loadInitFile: unknown initalization.type!");
136
}
137
 
294 werner 138
void StandLoader::loadPicusFile(const QString &fileName, ResourceUnit *ru)
106 Werner 139
{
294 werner 140
    QString content = Helper::loadTextFile(fileName);
141
    if (content.isEmpty()) {
142
        qDebug() << "file not found: " + fileName;
143
        return;
144
    }
145
    loadSingleTreeList(content, ru, fileName);
146
}
147
 
148
void StandLoader::loadSingleTreeList(const QString &content, ResourceUnit*ru, const QString &fileName)
149
{
106 Werner 150
    if (!ru)
151
        ru = mModel->ru();
284 werner 152
    Q_ASSERT(ru!=0);
106 Werner 153
 
284 werner 154
    QPointF offset = ru->boundingBox().topLeft();
155
    SpeciesSet *speciesSet = ru->speciesSet(); // of default RU
156
 
106 Werner 157
    // cut out the <trees> </trees> part....
158
    QRegExp rx(".*<trees>(.*)</trees>.*");
294 werner 159
    rx.indexIn(content, 0);
106 Werner 160
    if (rx.capturedTexts().count()<1)
161
        return;
294 werner 162
    QString my_content = rx.cap(1).trimmed();
163
    QStringList lines=my_content.split('\n');
106 Werner 164
    if (lines.count()<2)
165
        return;
166
    char sep='\t';
167
    if (!lines[0].contains(sep))
168
        sep=';';
155 werner 169
    QStringList headers = lines[0].trimmed().split(sep);
138 Werner 170
 
247 werner 171
    int iID = headers.indexOf("id");
106 Werner 172
    int iX = headers.indexOf("x");
173
    int iY = headers.indexOf("y");
174
    int iBhd = headers.indexOf("bhdfrom");
175
    int iHeight = headers.indexOf("treeheight");
176
    int iSpecies = headers.indexOf("species");
170 werner 177
    int iAge = headers.indexOf("age");
204 werner 178
    if (iX==-1 || iY==-1 || iBhd==-1 || iSpecies==-1 || iHeight==-1)
138 Werner 179
        throw IException(QString("Initfile %1 is not valid!").arg(fileName));
106 Werner 180
 
138 Werner 181
    double dbh;
106 Werner 182
    for (int i=1;i<lines.count();i++) {
183
        QString &line = lines[i];
138 Werner 184
        dbh = line.section(sep, iBhd, iBhd).toDouble();
185
        if (dbh<5.)
186
            continue;
285 werner 187
 
106 Werner 188
        QPointF f;
189
        if (iX>=0 && iY>=0) {
190
           f.setX( line.section(sep, iX, iX).toDouble() );
191
           f.setY( line.section(sep, iY, iY).toDouble() );
192
           f+=offset;
285 werner 193
 
106 Werner 194
        }
285 werner 195
        // position valid?
196
        if (!mModel->heightGrid()->valueAt(f).isValid())
197
            continue;
198
        Tree &tree = ru->newTree();
199
        tree.setPosition(f);
247 werner 200
        if (iID>=0)
201
            tree.setId(line.section(sep, iID, iID).toInt() );
106 Werner 202
 
138 Werner 203
        tree.setDbh(dbh);
204
        tree.setHeight(line.section(sep, iHeight, iHeight).toDouble()/100.); // convert from Picus-cm to m.
204 werner 205
        if (iAge>=0)
206
           tree.setAge(line.section(sep, iAge, iAge).toInt());
207
        else
208
            tree.setAge(10);
270 werner 209
        QString speciesid = line.section(sep, iSpecies, iSpecies);
210
        bool ok;
211
        int picusid = speciesid.toInt(&ok);
212
        if (ok) {
213
            int idx = picusSpeciesIds.indexOf(line.section(sep, iSpecies, iSpecies));
214
            if (idx==-1)
215
                throw IException(QString("Loading init-file: invalid Picus-species-id. Species: %1").arg(picusid));
138 Werner 216
            speciesid = iLandSpeciesIds[idx];
270 werner 217
        }
138 Werner 218
        Species *s = speciesSet->species(speciesid);
219
        if (!ru || !s)
270 werner 220
            throw IException(QString("Loading init-file: either ressource unit or species invalid. Species: %1").arg(speciesid));
107 Werner 221
 
138 Werner 222
        tree.setRU(ru);
223
        tree.setSpecies(s);
106 Werner 224
        tree.setup();
225
    }
226
    //qDebug() << "loaded init-file contained" << lines.count() <<"lines.";
227
    //qDebug() << "lines: " << lines;
228
}
287 werner 229
 
294 werner 230
void StandLoader::loadDistributionList(const QString &content, ResourceUnit *ru, const QString &fileName)
287 werner 231
{
232
    if (!ru)
233
        ru = mModel->ru();
234
    Q_ASSERT(ru!=0);
235
    SpeciesSet *speciesSet = ru->speciesSet(); // of default RU
236
    Q_ASSERT(speciesSet!=0);
237
 
238
    DebugTimer t("StandLoader::loadiLandFile");
294 werner 239
    CSVFile infile;
240
    infile.loadFromString(content);
241
 
287 werner 242
    int icount = infile.columnIndex("count");
243
    int ispecies = infile.columnIndex("species");
244
    int idbh_from = infile.columnIndex("dbh_from");
245
    int idbh_to = infile.columnIndex("dbh_to");
246
    int ihd = infile.columnIndex("hd");
247
    int iage = infile.columnIndex("age");
311 werner 248
    int idensity = infile.columnIndex("density");
287 werner 249
    if (icount<0 || ispecies<0 || idbh_from<0 || idbh_to<0 || ihd<0 || iage<0)
250
        throw IException(QString("load-ini-file: file '%1' containts not all required fields (count, species, dbh_from, dbh_to, hd, age).").arg(fileName));
251
 
294 werner 252
    mInitItems.clear();
287 werner 253
    InitFileItem item;
254
    for (int row=0;row<infile.rowCount();row++) {
255
         item.count = infile.value(row, icount).toInt();
256
         item.dbh_from = infile.value(row, idbh_from).toDouble();
257
         item.dbh_to = infile.value(row, idbh_to).toDouble();
258
         item.hd = infile.value(row, ihd).toDouble();
259
         item.age = infile.value(row, iage).toInt();
260
         item.species = speciesSet->species(infile.value(row, ispecies).toString());
311 werner 261
         if (idensity>=0)
262
             item.density = infile.value(row, idensity).toDouble();
263
         else
264
             item.density = 0.;
265
         if (item.density<-1 || item.density>1)
266
             throw IException(QString("load-ini-file: invalid value for density. Allowed range is -1..1: '%1' in file '%2', line %3.")
267
                              .arg(item.density)
268
                              .arg(fileName)
269
                              .arg(row));
287 werner 270
         if (!item.species) {
271
             throw IException(QString("load-ini-file: unknown speices '%1' in file '%2', line %3.")
272
                              .arg(infile.value(row, ispecies).toString())
273
                              .arg(fileName)
274
                              .arg(row));
275
         }
290 werner 276
         mInitItems.push_back(item);
287 werner 277
    }
290 werner 278
    // setup the random distribution
279
    QString density_func = GlobalSettings::instance()->settings().value("model.initialization.randomFunction", "1-x^2");
280
    qDebug() << "density function:" << density_func;
281
    if (!mRandom || (mRandom->densityFunction()!= density_func)) {
282
        if (mRandom)
283
            delete mRandom;
284
        mRandom=new RandomCustomPDF(density_func);
285
        qDebug() << "new probabilty density function:" << density_func;
286
    }
287
 
287 werner 288
    // exeucte the
289
    executeiLandInit(ru);
290 werner 290
    ru->cleanTreeList();
287 werner 291
 
292
}
293
 
294 werner 294
void StandLoader::loadiLandFile(const QString &fileName, ResourceUnit *ru)
295
{
296
    if (!QFile::exists(fileName))
297
        throw IException(QString("load-ini-file: file '%1' does not exist.").arg(fileName));
298
    QString content = Helper::loadTextFile(fileName);
299
    loadDistributionList(content, ru, fileName);
300
}
301
 
287 werner 302
// evenlist: tentative order of pixel-indices (within a 5x5 grid) used as tree positions.
303
// e.g. 12 = centerpixel, 0: upper left corner, ...
304
int evenlist[25] = { 12, 6, 18, 16, 8, 22, 2, 10, 14, 0, 24, 20, 4,
305
                     1, 13, 15, 19, 21, 3, 7, 11, 17, 23, 5, 9};
290 werner 306
int unevenlist[25] = { 11,13,7,17, 1,19,5,21, 9,23,3,15,
307
                       6,18,2,10,4,24,12,0,8,14,20,22};
308
 
309
 
287 werner 310
// sort function
311
bool sortPairLessThan(const QPair<int, double> &s1, const QPair<int, double> &s2)
311 werner 312
{
313
    return s1.second < s2.second;
314
}
315
 
316
/**
317
*/
290 werner 318
void StandLoader::executeiLandInit(ResourceUnit *ru)
287 werner 319
{
290 werner 320
 
287 werner 321
    QPointF offset = ru->boundingBox().topLeft();
322
    QPoint offsetIdx = GlobalSettings::instance()->model()->grid()->indexAt(offset);
323
 
324
    // a multimap holds a list for all trees.
325
    // key is the index of a 10x10m pixel within the resource unit
326
    QMultiMap<int, int> tree_map;
327
    QVector<QPair<int, double> > tcount; // counts
328
    for (int i=0;i<100;i++)
329
        tcount.push_back(QPair<int,double>(i,0.));
330
 
331
    int key;
311 werner 332
    double rand_val, rand_fraction;
312 werner 333
    int total_count = 0;
290 werner 334
    foreach(const InitFileItem &item, mInitItems) {
311 werner 335
        rand_fraction = fabs(double(item.density));
287 werner 336
        for (int i=0;i<item.count;i++) {
337
            // create trees
338
            int tree_idx = ru->newTreeIndex();
339
            Tree &tree = ru->trees()[tree_idx]; // get reference to modify tree
340
            tree.setDbh(nrandom(item.dbh_from, item.dbh_to));
341
            tree.setHeight(tree.dbh()/100. * item.hd); // dbh from cm->m, *hd-ratio -> meter height
342
            tree.setSpecies(item.species);
343
            tree.setAge(item.age);
344
            tree.setRU(ru);
345
            tree.setup();
312 werner 346
            total_count++;
311 werner 347
 
348
            // calculate random value. "density" is from 1..-1.
349
            rand_val = mRandom->get();
350
            if (item.density<0)
351
                rand_val = 1. - rand_val;
312 werner 352
            rand_val = rand_val * rand_fraction + drandom()*(1.-rand_fraction);
311 werner 353
 
287 werner 354
            // key: rank of target pixel
355
            // first: index of target pixel
356
            // second: sum of target pixel
311 werner 357
            key = limit(int(100*rand_val), 0, 99); // get from random number generator
287 werner 358
            tree_map.insert(tcount[key].first, tree_idx); // store tree in map
359
            tcount[key].second+=tree.basalArea(); // aggregate the basal area for each 10m pixel
312 werner 360
            if ( (total_count < 20 && i%2==0)
361
                || (total_count<100 && i%10==0 )
362
                || (i%30==0) ) {
287 werner 363
                qSort(tcount.begin(), tcount.end(), sortPairLessThan);
312 werner 364
            }
287 werner 365
        }
366
        qSort(tcount.begin(), tcount.end(), sortPairLessThan);
367
    }
368
 
369
    int bits, index, pos;
370
    int c;
371
    QList<int> trees;
372
    QPoint tree_pos;
289 werner 373
 
287 werner 374
    for (int i=0;i<100;i++) {
375
        trees = tree_map.values(i);
376
        c = trees.count();
290 werner 377
        QPointF pixel_center = ru->boundingBox().topLeft() + QPointF((i/10)*10. + 5., (i%10)*10. + 5.);
378
        if (!mModel->heightGrid()->valueAt(pixel_center).isValid()) {
379
            // no trees on that pixel: let trees die
380
            foreach(int tree_idx, trees) {
381
                ru->trees()[tree_idx].die();
382
            }
383
            continue;
384
        }
385
 
287 werner 386
        bits = 0;
387
        index = -1;
290 werner 388
        double r;
287 werner 389
        foreach(int tree_idx, trees) {
390
            if (c>18) {
391
                index = (index + 1)%25;
392
            } else {
393
                int stop=1000;
290 werner 394
                index = 0;
287 werner 395
                do {
290 werner 396
                    //r = drandom();
397
                    //if (r<0.5)  // skip position with a prob. of 50% -> adds a little "noise"
398
                    //    index++;
399
                    //index = (index + 1)%25; // increase and roll over
400
 
401
                    // search a random position
402
                    r = drandom();
403
                    index = limit(int(25 *  r*r), 0, 24); // use rnd()^2 to search for locations -> higher number of low indices (i.e. 50% of lookups in first 25% of locations)
287 werner 404
                } while (isBitSet(bits, index)==true && stop--);
405
                if (!stop)
406
                    qDebug() << "executeiLandInit: found no free bit.";
407
                setBit(bits, index, true); // mark position as used
408
            }
290 werner 409
            // get position from fixed lists (one for even, one for uneven resource units)
410
            pos = ru->index()%2?evenlist[index]:unevenlist[index];
287 werner 411
            tree_pos = offsetIdx  // position of resource unit
412
                       + QPoint(5*(i/10), 5*(i%10)) // relative position of 10x10m pixel
413
                       + QPoint(pos/5, pos%5); // relative position within 10x10m pixel
414
            //qDebug() << tree_no++ << "to" << index;
415
            ru->trees()[tree_idx].setPosition(tree_pos);
416
        }
417
    }
418
}