Subversion Repositories public iLand

Rev

Rev 671 | Rev 699 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 671 Rev 697
1
Redirecting to URL 'https://iland.boku.ac.at/svn/iland/tags/release_1.0/src/core/standloader.cpp':
1
Redirecting to URL 'https://iland.boku.ac.at/svn/iland/tags/release_1.0/src/core/standloader.cpp':
2
/********************************************************************************************
2
/********************************************************************************************
3
**    iLand - an individual based forest landscape and disturbance model
3
**    iLand - an individual based forest landscape and disturbance model
4
**    http://iland.boku.ac.at
4
**    http://iland.boku.ac.at
5
**    Copyright (C) 2009-  Werner Rammer, Rupert Seidl
5
**    Copyright (C) 2009-  Werner Rammer, Rupert Seidl
6
**
6
**
7
**    This program is free software: you can redistribute it and/or modify
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
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
9
**    the Free Software Foundation, either version 3 of the License, or
10
**    (at your option) any later version.
10
**    (at your option) any later version.
11
**
11
**
12
**    This program is distributed in the hope that it will be useful,
12
**    This program is distributed in the hope that it will be useful,
13
**    but WITHOUT ANY WARRANTY; without even the implied warranty of
13
**    but WITHOUT ANY WARRANTY; without even the implied warranty of
14
**    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
**    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
**    GNU General Public License for more details.
15
**    GNU General Public License for more details.
16
**
16
**
17
**    You should have received a copy of the GNU General Public License
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/>.
18
**    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
********************************************************************************************/
19
********************************************************************************************/
20
20
21
#include "global.h"
21
#include "global.h"
22
#include "standloader.h"
22
#include "standloader.h"
23
23
24
24
25
#include "grid.h"
25
#include "grid.h"
26
#include "model.h"
26
#include "model.h"
27
#include "resourceunit.h"
27
#include "resourceunit.h"
28
#include "speciesset.h"
28
#include "speciesset.h"
29
29
30
#include "helper.h"
30
#include "helper.h"
31
#include "random.h"
31
#include "random.h"
32
#include "expression.h"
32
#include "expression.h"
33
#include "expressionwrapper.h"
33
#include "expressionwrapper.h"
34
#include "environment.h"
34
#include "environment.h"
35
#include "csvfile.h"
35
#include "csvfile.h"
36
#include "mapgrid.h"
36
#include "mapgrid.h"
37
37
-
 
38
/** @class StandLoader
-
 
39
    @ingroup tools
-
 
40
    loads (initializes) trees for a "stand" from various sources.
-
 
41
    StandLoader initializes trees on the landscape. It reads (usually) from text files, creates the
-
 
42
    trees and distributes the trees on the landscape (on the ResoureceUnit or on a stand defined by a grid).
-
 
43

-
 
44
    See http://iland.boku.ac.at/initialize+trees
-
 
45
  */
38
// provide a mapping between "Picus"-style and "iLand"-style species Ids
46
// provide a mapping between "Picus"-style and "iLand"-style species Ids
39
QVector<int> picusSpeciesIds = QVector<int>() << 0 << 1 << 17;
47
QVector<int> picusSpeciesIds = QVector<int>() << 0 << 1 << 17;
40
QStringList iLandSpeciesIds = QStringList() << "piab" << "piab" << "fasy";
48
QStringList iLandSpeciesIds = QStringList() << "piab" << "piab" << "fasy";
41
StandLoader::~StandLoader()
49
StandLoader::~StandLoader()
42
{
50
{
43
    if (mRandom)
51
    if (mRandom)
44
        delete mRandom;
52
        delete mRandom;
45
}
53
}
46
54
47
55
48
void StandLoader::copyTrees()
56
void StandLoader::copyTrees()
49
{
57
{
50
    // we assume that all stands are equal, so wie simply COPY the trees and modify them afterwards
58
    // we assume that all stands are equal, so wie simply COPY the trees and modify them afterwards
51
    const Grid<ResourceUnit*> &ruGrid=mModel->RUgrid();
59
    const Grid<ResourceUnit*> &ruGrid=mModel->RUgrid();
52
    ResourceUnit **p = ruGrid.begin();
60
    ResourceUnit **p = ruGrid.begin();
53
    if (!p)
61
    if (!p)
54
        throw IException("Standloader: invalid resource unit pointer!");
62
        throw IException("Standloader: invalid resource unit pointer!");
55
    ++p; // skip the first...
63
    ++p; // skip the first...
56
    const QVector<Tree> &tocopy = mModel->ru()->trees();
64
    const QVector<Tree> &tocopy = mModel->ru()->trees();
57
    for (; p!=ruGrid.end(); ++p) {
65
    for (; p!=ruGrid.end(); ++p) {
58
        QRectF rect = (*p)->boundingBox();
66
        QRectF rect = (*p)->boundingBox();
59
        foreach(const Tree& tree, tocopy) {
67
        foreach(const Tree& tree, tocopy) {
60
            Tree &newtree = (*p)->newTree();
68
            Tree &newtree = (*p)->newTree();
61
            newtree = tree; // copy tree data...
69
            newtree = tree; // copy tree data...
62
            newtree.setPosition(tree.position()+(*p)->boundingBox().topLeft());
70
            newtree.setPosition(tree.position()+(*p)->boundingBox().topLeft());
63
            newtree.setRU(*p);
71
            newtree.setRU(*p);
64
            newtree.setNewId();
72
            newtree.setNewId();
65
        }
73
        }
66
    }
74
    }
67
    if (logLevelInfo()) qDebug() << Tree::statCreated() << "trees loaded / copied.";
75
    if (logLevelInfo()) qDebug() << Tree::statCreated() << "trees loaded / copied.";
68
}
76
}
69
77
70
/** main routine of the stand setup.
78
/** main routine of the stand setup.
71
*/
79
*/
72
void StandLoader::processInit()
80
void StandLoader::processInit()
73
{
81
{
74
    GlobalSettings *g = GlobalSettings::instance();
82
    GlobalSettings *g = GlobalSettings::instance();
75
    XmlHelper xml(g->settings().node("model.initialization"));
83
    XmlHelper xml(g->settings().node("model.initialization"));
76
84
77
    QString copy_mode = xml.value("mode", "copy");
85
    QString copy_mode = xml.value("mode", "copy");
78
    QString type = xml.value("type", "");
86
    QString type = xml.value("type", "");
79
    QString  fileName = xml.value("file", "");
87
    QString  fileName = xml.value("file", "");
80
88
81
    Tree::resetStatistics();
89
    Tree::resetStatistics();
82
90
83
    // one global init-file for the whole area:
91
    // one global init-file for the whole area:
84
    if (copy_mode=="single") {
92
    if (copy_mode=="single") {
85
        loadInitFile(fileName, type);
93
        loadInitFile(fileName, type);
86
        evaluateDebugTrees();
94
        evaluateDebugTrees();
87
        return;
95
        return;
88
    }
96
    }
89
97
90
    // copy trees from first unit to all other units:
98
    // copy trees from first unit to all other units:
91
    if (copy_mode=="copy") {
99
    if (copy_mode=="copy") {
92
        loadInitFile(fileName, type);
100
        loadInitFile(fileName, type);
93
        copyTrees();
101
        copyTrees();
94
        evaluateDebugTrees();
102
        evaluateDebugTrees();
95
        return;
103
        return;
96
    }
104
    }
97
105
98
    // call a single tree init for each resource unit
106
    // call a single tree init for each resource unit
99
    if (copy_mode=="unit") {
107
    if (copy_mode=="unit") {
100
        foreach( const ResourceUnit *const_ru, g->model()->ruList()) {
108
        foreach( const ResourceUnit *const_ru, g->model()->ruList()) {
101
            ResourceUnit *ru = const_cast<ResourceUnit*>(const_ru);
109
            ResourceUnit *ru = const_cast<ResourceUnit*>(const_ru);
102
            ru->setRandomGenerator(); //
110
            ru->setRandomGenerator(); //
103
            // set environment
111
            // set environment
104
            g->model()->environment()->setPosition(ru->boundingBox().center());
112
            g->model()->environment()->setPosition(ru->boundingBox().center());
105
            type = xml.value("type", "");
113
            type = xml.value("type", "");
106
            fileName = xml.value("file", "");
114
            fileName = xml.value("file", "");
107
            if (fileName.isEmpty())
115
            if (fileName.isEmpty())
108
                continue;
116
                continue;
109
            loadInitFile(fileName, type, 0, ru);
117
            loadInitFile(fileName, type, 0, ru);
110
            if (logLevelInfo()) qDebug() << "loaded" << fileName << "on" << ru->boundingBox() << "," << ru->trees().count() << "trees.";
118
            if (logLevelInfo()) qDebug() << "loaded" << fileName << "on" << ru->boundingBox() << "," << ru->trees().count() << "trees.";
111
        }
119
        }
112
        evaluateDebugTrees();
120
        evaluateDebugTrees();
113
        return;
121
        return;
114
    }
122
    }
115
123
116
    // map-modus
124
    // map-modus
117
    if (copy_mode=="map") {
125
    if (copy_mode=="map") {
118
        if (!g->model()->standGrid() || !g->model()->standGrid()->isValid())
126
        if (!g->model()->standGrid() || !g->model()->standGrid()->isValid())
119
            throw IException(QString("Stand-Initialization: model.initialization.mode is 'map' but there is no valid stand grid defined (model.world.standGrid)"));
127
            throw IException(QString("Stand-Initialization: model.initialization.mode is 'map' but there is no valid stand grid defined (model.world.standGrid)"));
120
        QString map_file_name = GlobalSettings::instance()->path(xml.value("mapFileName"), "init");
128
        QString map_file_name = GlobalSettings::instance()->path(xml.value("mapFileName"), "init");
121
129
122
        CSVFile map_file(map_file_name);
130
        CSVFile map_file(map_file_name);
123
        if (map_file.rowCount()==0)
131
        if (map_file.rowCount()==0)
124
            throw IException(QString("Stand-Initialization: the map file %1 is empty or missing!").arg(map_file_name));
132
            throw IException(QString("Stand-Initialization: the map file %1 is empty or missing!").arg(map_file_name));
125
        int ikey = map_file.columnIndex("id");
133
        int ikey = map_file.columnIndex("id");
126
        int ivalue = map_file.columnIndex("filename");
134
        int ivalue = map_file.columnIndex("filename");
127
        if (ikey<0 || ivalue<0)
135
        if (ikey<0 || ivalue<0)
128
            throw IException(QString("Stand-Initialization: the map file %1 does not contain the mandatory columns 'id' and 'filename'!").arg(map_file_name));
136
            throw IException(QString("Stand-Initialization: the map file %1 does not contain the mandatory columns 'id' and 'filename'!").arg(map_file_name));
129
        QString file_name;
137
        QString file_name;
130
        for (int i=0;i<map_file.rowCount();i++) {
138
        for (int i=0;i<map_file.rowCount();i++) {
131
            int key = map_file.value(i, ikey).toInt();
139
            int key = map_file.value(i, ikey).toInt();
132
            if (key>0) {
140
            if (key>0) {
133
                file_name = map_file.value(i, ivalue).toString();
141
                file_name = map_file.value(i, ivalue).toString();
134
                if (logLevelInfo()) qDebug() << "loading" << file_name << "for grid id" << key;
142
                if (logLevelInfo()) qDebug() << "loading" << file_name << "for grid id" << key;
135
                loadInitFile(file_name, type, key, NULL);
143
                loadInitFile(file_name, type, key, NULL);
136
            }
144
            }
137
        }
145
        }
138
        evaluateDebugTrees();
146
        evaluateDebugTrees();
139
        return;
147
        return;
140
    }
148
    }
141
149
142
    throw IException("StandLoader::processInit: invalid initalization.mode!");
150
    throw IException("StandLoader::processInit: invalid initalization.mode!");
143
}
151
}
144
152
145
void StandLoader::evaluateDebugTrees()
153
void StandLoader::evaluateDebugTrees()
146
{
154
{
147
    // evaluate debugging
155
    // evaluate debugging
148
    QString dbg_str = GlobalSettings::instance()->settings().paramValueString("debug_tree");
156
    QString dbg_str = GlobalSettings::instance()->settings().paramValueString("debug_tree");
149
    int counter=0;
157
    int counter=0;
150
    if (!dbg_str.isEmpty()) {
158
    if (!dbg_str.isEmpty()) {
151
       TreeWrapper tw;
159
       TreeWrapper tw;
152
       Expression dexp(dbg_str, &tw); // load expression dbg_str and enable external model variables
160
       Expression dexp(dbg_str, &tw); // load expression dbg_str and enable external model variables
153
        AllTreeIterator at(GlobalSettings::instance()->model());
161
        AllTreeIterator at(GlobalSettings::instance()->model());
154
        double result;
162
        double result;
155
        while (Tree *t = at.next()) {
163
        while (Tree *t = at.next()) {
156
            tw.setTree(t);
164
            tw.setTree(t);
157
            result = dexp.execute();
165
            result = dexp.execute();
158
            if (result) {
166
            if (result) {
159
                t->enableDebugging();
167
                t->enableDebugging();
160
                counter++;
168
                counter++;
161
            }
169
            }
162
        }
170
        }
163
    }
171
    }
164
    qDebug() << "evaluateDebugTrees: enabled debugging for" << counter << "trees.";
172
    qDebug() << "evaluateDebugTrees: enabled debugging for" << counter << "trees.";
165
}
173
}
166
174
167
int StandLoader::loadInitFile(const QString &fileName, const QString &type, int stand_id, ResourceUnit *ru)
175
int StandLoader::loadInitFile(const QString &fileName, const QString &type, int stand_id, ResourceUnit *ru)
168
{
176
{
169
    QString pathFileName = GlobalSettings::instance()->path(fileName, "init");
177
    QString pathFileName = GlobalSettings::instance()->path(fileName, "init");
170
    if (!QFile::exists(pathFileName))
178
    if (!QFile::exists(pathFileName))
171
        throw IException(QString("StandLoader::loadInitFile: File %1 does not exist!").arg(pathFileName));
179
        throw IException(QString("StandLoader::loadInitFile: File %1 does not exist!").arg(pathFileName));
172
180
173
    if (type=="picus" || type=="single") {
181
    if (type=="picus" || type=="single") {
174
        if (stand_id>0)
182
        if (stand_id>0)
175
            throw IException(QLatin1String("StandLoader::loadInitFile: initialization type %1 currently not supported for stand initilization mode!")+type);
183
            throw IException(QLatin1String("StandLoader::loadInitFile: initialization type %1 currently not supported for stand initilization mode!")+type);
176
        return loadPicusFile(pathFileName, ru);
184
        return loadPicusFile(pathFileName, ru);
177
    }
185
    }
178
    if (type=="iland" || type=="distribution")
186
    if (type=="iland" || type=="distribution")
179
        return loadiLandFile(pathFileName, ru, stand_id);
187
        return loadiLandFile(pathFileName, ru, stand_id);
180
188
181
    throw IException(QLatin1String("StandLoader::loadInitFile: unknown initalization.type:")+type);
189
    throw IException(QLatin1String("StandLoader::loadInitFile: unknown initalization.type:")+type);
182
}
190
}
183
191
184
int StandLoader::loadPicusFile(const QString &fileName, ResourceUnit *ru)
192
int StandLoader::loadPicusFile(const QString &fileName, ResourceUnit *ru)
185
{
193
{
186
    QString content = Helper::loadTextFile(fileName);
194
    QString content = Helper::loadTextFile(fileName);
187
    if (content.isEmpty()) {
195
    if (content.isEmpty()) {
188
        qDebug() << "file not found: " + fileName;
196
        qDebug() << "file not found: " + fileName;
189
        return 0;
197
        return 0;
190
    }
198
    }
191
    return loadSingleTreeList(content, ru, fileName);
199
    return loadSingleTreeList(content, ru, fileName);
192
}
200
}
193
201
194
/** load a list of trees (given by content) to a resource unit. Param fileName is just for error reporting.
202
/** load a list of trees (given by content) to a resource unit. Param fileName is just for error reporting.
195
  returns the number of loaded trees.
203
  returns the number of loaded trees.
196
  */
204
  */
197
int StandLoader::loadSingleTreeList(const QString &content, ResourceUnit *ru, const QString &fileName)
205
int StandLoader::loadSingleTreeList(const QString &content, ResourceUnit *ru, const QString &fileName)
198
{
206
{
199
    if (!ru)
207
    if (!ru)
200
        ru = mModel->ru();
208
        ru = mModel->ru();
201
    Q_ASSERT(ru!=0);
209
    Q_ASSERT(ru!=0);
202
210
203
    QPointF offset = ru->boundingBox().topLeft();
211
    QPointF offset = ru->boundingBox().topLeft();
204
    SpeciesSet *speciesSet = ru->speciesSet(); // of default RU
212
    SpeciesSet *speciesSet = ru->speciesSet(); // of default RU
205
213
206
    QString my_content(content);
214
    QString my_content(content);
207
    // cut out the <trees> </trees> part if present
215
    // cut out the <trees> </trees> part if present
208
    if (content.contains("<trees>")) {
216
    if (content.contains("<trees>")) {
209
        QRegExp rx(".*<trees>(.*)</trees>.*");
217
        QRegExp rx(".*<trees>(.*)</trees>.*");
210
        rx.indexIn(content, 0);
218
        rx.indexIn(content, 0);
211
        if (rx.capturedTexts().count()<1)
219
        if (rx.capturedTexts().count()<1)
212
            return 0;
220
            return 0;
213
        my_content = rx.cap(1).trimmed();
221
        my_content = rx.cap(1).trimmed();
214
    }
222
    }
215
223
216
    QStringList lines=my_content.split('\n');
224
    QStringList lines=my_content.split('\n');
217
    if (lines.count()<2)
225
    if (lines.count()<2)
218
        return 0;
226
        return 0;
219
    // drop comments
227
    // drop comments
220
    while (!lines.isEmpty() && lines.front().startsWith('#') )
228
    while (!lines.isEmpty() && lines.front().startsWith('#') )
221
        lines.pop_front();
229
        lines.pop_front();
222
    while (!lines.isEmpty() && lines.last().isEmpty())
230
    while (!lines.isEmpty() && lines.last().isEmpty())
223
        lines.removeLast();
231
        lines.removeLast();
224
232
225
    char sep='\t';
233
    char sep='\t';
226
    if (!lines[0].contains(sep))
234
    if (!lines[0].contains(sep))
227
        sep=';';
235
        sep=';';
228
    QStringList headers = lines[0].trimmed().split(sep);
236
    QStringList headers = lines[0].trimmed().split(sep);
229
237
230
    int iID = headers.indexOf("id");
238
    int iID = headers.indexOf("id");
231
    int iX = headers.indexOf("x");
239
    int iX = headers.indexOf("x");
232
    int iY = headers.indexOf("y");
240
    int iY = headers.indexOf("y");
233
    int iBhd = headers.indexOf("bhdfrom");
241
    int iBhd = headers.indexOf("bhdfrom");
234
    if (iBhd<0)
242
    if (iBhd<0)
235
        iBhd = headers.indexOf("dbh");
243
        iBhd = headers.indexOf("dbh");
236
    double height_conversion = 100.;
244
    double height_conversion = 100.;
237
    int iHeight = headers.indexOf("treeheight");
245
    int iHeight = headers.indexOf("treeheight");
238
    if (iHeight<0) {
246
    if (iHeight<0) {
239
        iHeight = headers.indexOf("height");
247
        iHeight = headers.indexOf("height");
240
        height_conversion = 1.; // in meter
248
        height_conversion = 1.; // in meter
241
    }
249
    }
242
    int iSpecies = headers.indexOf("species");
250
    int iSpecies = headers.indexOf("species");
243
    int iAge = headers.indexOf("age");
251
    int iAge = headers.indexOf("age");
244
    if (iX==-1 || iY==-1 || iBhd==-1 || iSpecies==-1 || iHeight==-1)
252
    if (iX==-1 || iY==-1 || iBhd==-1 || iSpecies==-1 || iHeight==-1)
245
        throw IException(QString("Initfile %1 is not valid!\nObligatory columns are: x,y, bhdfrom or dbh, species, treeheight or height.").arg(fileName));
253
        throw IException(QString("Initfile %1 is not valid!\nObligatory columns are: x,y, bhdfrom or dbh, species, treeheight or height.").arg(fileName));
246
254
247
    double dbh;
255
    double dbh;
248
    bool ok;
256
    bool ok;
249
    int cnt=0;
257
    int cnt=0;
250
    QString speciesid;
258
    QString speciesid;
251
    for (int i=1;i<lines.count();i++) {
259
    for (int i=1;i<lines.count();i++) {
252
        QString &line = lines[i];
260
        QString &line = lines[i];
253
        dbh = line.section(sep, iBhd, iBhd).toDouble();
261
        dbh = line.section(sep, iBhd, iBhd).toDouble();
254
        if (dbh<5.)
262
        if (dbh<5.)
255
            continue;
263
            continue;
256
264
257
        QPointF f;
265
        QPointF f;
258
        if (iX>=0 && iY>=0) {
266
        if (iX>=0 && iY>=0) {
259
           f.setX( line.section(sep, iX, iX).toDouble() );
267
           f.setX( line.section(sep, iX, iX).toDouble() );
260
           f.setY( line.section(sep, iY, iY).toDouble() );
268
           f.setY( line.section(sep, iY, iY).toDouble() );
261
           f+=offset;
269
           f+=offset;
262
270
263
        }
271
        }
264
        // position valid?
272
        // position valid?
265
        if (!mModel->heightGrid()->valueAt(f).isValid())
273
        if (!mModel->heightGrid()->valueAt(f).isValid())
266
            continue;
274
            continue;
267
        Tree &tree = ru->newTree();
275
        Tree &tree = ru->newTree();
268
        tree.setPosition(f);
276
        tree.setPosition(f);
269
        if (iID>=0)
277
        if (iID>=0)
270
            tree.setId(line.section(sep, iID, iID).toInt() );
278
            tree.setId(line.section(sep, iID, iID).toInt() );
271
279
272
        tree.setDbh(dbh);
280
        tree.setDbh(dbh);
273
        tree.setHeight(line.section(sep, iHeight, iHeight).toDouble()/height_conversion); // convert from Picus-cm to m if necessary
281
        tree.setHeight(line.section(sep, iHeight, iHeight).toDouble()/height_conversion); // convert from Picus-cm to m if necessary
274
282
275
        speciesid = line.section(sep, iSpecies, iSpecies).trimmed();
283
        speciesid = line.section(sep, iSpecies, iSpecies).trimmed();
276
        int picusid = speciesid.toInt(&ok);
284
        int picusid = speciesid.toInt(&ok);
277
        if (ok) {
285
        if (ok) {
278
            int idx = picusSpeciesIds.indexOf(picusid);
286
            int idx = picusSpeciesIds.indexOf(picusid);
279
            if (idx==-1)
287
            if (idx==-1)
280
                throw IException(QString("Loading init-file: invalid Picus-species-id. Species: %1").arg(picusid));
288
                throw IException(QString("Loading init-file: invalid Picus-species-id. Species: %1").arg(picusid));
281
            speciesid = iLandSpeciesIds[idx];
289
            speciesid = iLandSpeciesIds[idx];
282
        }
290
        }
283
        Species *s = speciesSet->species(speciesid);
291
        Species *s = speciesSet->species(speciesid);
284
        if (!ru || !s)
292
        if (!ru || !s)
285
            throw IException(QString("Loading init-file: either resource unit or species invalid. Species: %1").arg(speciesid));
293
            throw IException(QString("Loading init-file: either resource unit or species invalid. Species: %1").arg(speciesid));
286
        tree.setSpecies(s);
294
        tree.setSpecies(s);
287
295
288
        ok = true;
296
        ok = true;
289
        if (iAge>=0)
297
        if (iAge>=0)
290
           tree.setAge(line.section(sep, iAge, iAge).toInt(&ok), tree.height()); // this is a *real* age
298
           tree.setAge(line.section(sep, iAge, iAge).toInt(&ok), tree.height()); // this is a *real* age
291
        if (iAge<0 || !ok || tree.age()==0)
299
        if (iAge<0 || !ok || tree.age()==0)
292
           tree.setAge(0, tree.height()); // no real tree age available
300
           tree.setAge(0, tree.height()); // no real tree age available
293
301
294
        tree.setRU(ru);
302
        tree.setRU(ru);
295
        tree.setup();
303
        tree.setup();
296
        cnt++;
304
        cnt++;
297
    }
305
    }
298
    return cnt;
306
    return cnt;
299
    //qDebug() << "loaded init-file contained" << lines.count() <<"lines.";
307
    //qDebug() << "loaded init-file contained" << lines.count() <<"lines.";
300
    //qDebug() << "lines: " << lines;
308
    //qDebug() << "lines: " << lines;
301
}
309
}
302
310
303
/** initialize trees on a resource unit based on dbh distributions.
311
/** initialize trees on a resource unit based on dbh distributions.
304
  use a fairly clever algorithm to determine tree positions.
312
  use a fairly clever algorithm to determine tree positions.
305
  see http://iland.boku.ac.at/initialize+trees
313
  see http://iland.boku.ac.at/initialize+trees
306
  @param content tree init file (including headers) in a string
314
  @param content tree init file (including headers) in a string
307
  @param ru resource unit
315
  @param ru resource unit
308
  @param fileName source file name (for error reporting)
316
  @param fileName source file name (for error reporting)
309
  @return number of trees added
317
  @return number of trees added
310
  */
318
  */
311
int StandLoader::loadDistributionList(const QString &content, ResourceUnit *ru, int stand_id, const QString &fileName)
319
int StandLoader::loadDistributionList(const QString &content, ResourceUnit *ru, int stand_id, const QString &fileName)
312
{
320
{
313
    if (!ru)
321
    if (!ru)
314
        ru = mModel->ru();
322
        ru = mModel->ru();
315
    Q_ASSERT(ru!=0);
323
    Q_ASSERT(ru!=0);
316
    SpeciesSet *speciesSet = ru->speciesSet(); // of default RU
324
    SpeciesSet *speciesSet = ru->speciesSet(); // of default RU
317
    Q_ASSERT(speciesSet!=0);
325
    Q_ASSERT(speciesSet!=0);
318
326
319
    //DebugTimer t("StandLoader::loadiLandFile");
327
    //DebugTimer t("StandLoader::loadiLandFile");
320
    CSVFile infile;
328
    CSVFile infile;
321
    infile.loadFromString(content);
329
    infile.loadFromString(content);
322
330
323
    int icount = infile.columnIndex("count");
331
    int icount = infile.columnIndex("count");
324
    int ispecies = infile.columnIndex("species");
332
    int ispecies = infile.columnIndex("species");
325
    int idbh_from = infile.columnIndex("dbh_from");
333
    int idbh_from = infile.columnIndex("dbh_from");
326
    int idbh_to = infile.columnIndex("dbh_to");
334
    int idbh_to = infile.columnIndex("dbh_to");
327
    int ihd = infile.columnIndex("hd");
335
    int ihd = infile.columnIndex("hd");
328
    int iage = infile.columnIndex("age");
336
    int iage = infile.columnIndex("age");
329
    int idensity = infile.columnIndex("density");
337
    int idensity = infile.columnIndex("density");
330
    if (icount<0 || ispecies<0 || idbh_from<0 || idbh_to<0 || ihd<0 || iage<0)
338
    if (icount<0 || ispecies<0 || idbh_from<0 || idbh_to<0 || ihd<0 || iage<0)
331
        throw IException(QString("load-ini-file: file '%1' containts not all required fields (count, species, dbh_from, dbh_to, hd, age).").arg(fileName));
339
        throw IException(QString("load-ini-file: file '%1' containts not all required fields (count, species, dbh_from, dbh_to, hd, age).").arg(fileName));
332
340
333
    mInitItems.clear();
341
    mInitItems.clear();
334
    InitFileItem item;
342
    InitFileItem item;
335
    bool ok;
343
    bool ok;
336
    int total_count = 0;
344
    int total_count = 0;
337
    for (int row=0;row<infile.rowCount();row++) {
345
    for (int row=0;row<infile.rowCount();row++) {
338
        item.count = infile.value(row, icount).toInt();
346
        item.count = infile.value(row, icount).toInt();
339
        total_count += item.count;
347
        total_count += item.count;
340
        item.dbh_from = infile.value(row, idbh_from).toDouble();
348
        item.dbh_from = infile.value(row, idbh_from).toDouble();
341
        item.dbh_to = infile.value(row, idbh_to).toDouble();
349
        item.dbh_to = infile.value(row, idbh_to).toDouble();
342
        item.hd = infile.value(row, ihd).toDouble();
350
        item.hd = infile.value(row, ihd).toDouble();
343
        ok = true;
351
        ok = true;
344
        if (iage>=0)
352
        if (iage>=0)
345
            item.age = infile.value(row, iage).toInt(&ok);
353
            item.age = infile.value(row, iage).toInt(&ok);
346
        if (iage<0 || !ok)
354
        if (iage<0 || !ok)
347
            item.age = 0;
355
            item.age = 0;
348
356
349
        item.species = speciesSet->species(infile.value(row, ispecies).toString());
357
        item.species = speciesSet->species(infile.value(row, ispecies).toString());
350
        if (idensity>=0)
358
        if (idensity>=0)
351
            item.density = infile.value(row, idensity).toDouble();
359
            item.density = infile.value(row, idensity).toDouble();
352
        else
360
        else
353
            item.density = 0.;
361
            item.density = 0.;
354
        if (item.density<-1 || item.density>1)
362
        if (item.density<-1 || item.density>1)
355
            throw IException(QString("load-ini-file: invalid value for density. Allowed range is -1..1: '%1' in file '%2', line %3.")
363
            throw IException(QString("load-ini-file: invalid value for density. Allowed range is -1..1: '%1' in file '%2', line %3.")
356
                             .arg(item.density)
364
                             .arg(item.density)
357
                             .arg(fileName)
365
                             .arg(fileName)
358
                             .arg(row));
366
                             .arg(row));
359
        if (!item.species) {
367
        if (!item.species) {
360
            throw IException(QString("load-ini-file: unknown speices '%1' in file '%2', line %3.")
368
            throw IException(QString("load-ini-file: unknown speices '%1' in file '%2', line %3.")
361
                             .arg(infile.value(row, ispecies).toString())
369
                             .arg(infile.value(row, ispecies).toString())
362
                             .arg(fileName)
370
                             .arg(fileName)
363
                             .arg(row));
371
                             .arg(row));
364
        }
372
        }
365
        mInitItems.push_back(item);
373
        mInitItems.push_back(item);
366
    }
374
    }
367
    // setup the random distribution
375
    // setup the random distribution
368
    QString density_func = GlobalSettings::instance()->settings().value("model.initialization.randomFunction", "1-x^2");
376
    QString density_func = GlobalSettings::instance()->settings().value("model.initialization.randomFunction", "1-x^2");
369
    if (logLevelInfo())  qDebug() << "density function:" << density_func;
377
    if (logLevelInfo())  qDebug() << "density function:" << density_func;
370
    if (!mRandom || (mRandom->densityFunction()!= density_func)) {
378
    if (!mRandom || (mRandom->densityFunction()!= density_func)) {
371
        if (mRandom)
379
        if (mRandom)
372
            delete mRandom;
380
            delete mRandom;
373
        mRandom=new RandomCustomPDF(density_func);
381
        mRandom=new RandomCustomPDF(density_func);
374
        if (logLevelInfo()) qDebug() << "new probabilty density function:" << density_func;
382
        if (logLevelInfo()) qDebug() << "new probabilty density function:" << density_func;
375
    }
383
    }
376
    if (stand_id>0) {
384
    if (stand_id>0) {
377
        // execute stand based initialization
385
        // execute stand based initialization
378
        executeiLandInitStand(stand_id);
386
        executeiLandInitStand(stand_id);
379
    } else {
387
    } else {
380
        // exeucte the initialization based on single resource units
388
        // exeucte the initialization based on single resource units
381
        executeiLandInit(ru);
389
        executeiLandInit(ru);
382
        ru->cleanTreeList();
390
        ru->cleanTreeList();
383
    }
391
    }
384
    return total_count;
392
    return total_count;
385
393
386
}
394
}
387
395
388
int StandLoader::loadiLandFile(const QString &fileName, ResourceUnit *ru, int stand_id)
396
int StandLoader::loadiLandFile(const QString &fileName, ResourceUnit *ru, int stand_id)
389
{
397
{
390
    if (!QFile::exists(fileName))
398
    if (!QFile::exists(fileName))
391
        throw IException(QString("load-ini-file: file '%1' does not exist.").arg(fileName));
399
        throw IException(QString("load-ini-file: file '%1' does not exist.").arg(fileName));
392
    QString content = Helper::loadTextFile(fileName);
400
    QString content = Helper::loadTextFile(fileName);
393
    return loadDistributionList(content, ru, stand_id, fileName);
401
    return loadDistributionList(content, ru, stand_id, fileName);
394
}
402
}
395
403
396
// evenlist: tentative order of pixel-indices (within a 5x5 grid) used as tree positions.
404
// evenlist: tentative order of pixel-indices (within a 5x5 grid) used as tree positions.
397
// e.g. 12 = centerpixel, 0: upper left corner, ...
405
// e.g. 12 = centerpixel, 0: upper left corner, ...
398
int evenlist[25] = { 12, 6, 18, 16, 8, 22, 2, 10, 14, 0, 24, 20, 4,
406
int evenlist[25] = { 12, 6, 18, 16, 8, 22, 2, 10, 14, 0, 24, 20, 4,
399
                     1, 13, 15, 19, 21, 3, 7, 11, 17, 23, 5, 9};
407
                     1, 13, 15, 19, 21, 3, 7, 11, 17, 23, 5, 9};
400
int unevenlist[25] = { 11,13,7,17, 1,19,5,21, 9,23,3,15,
408
int unevenlist[25] = { 11,13,7,17, 1,19,5,21, 9,23,3,15,
401
                       6,18,2,10,4,24,12,0,8,14,20,22};
409
                       6,18,2,10,4,24,12,0,8,14,20,22};
402
410
403
411
404
412
405
// sort function
413
// sort function
406
bool sortPairLessThan(const QPair<int, double> &s1, const QPair<int, double> &s2)
414
bool sortPairLessThan(const QPair<int, double> &s1, const QPair<int, double> &s2)
407
{
415
{
408
    return s1.second < s2.second;
416
    return s1.second < s2.second;
409
}
417
}
410
418
411
struct SInitPixel {
419
struct SInitPixel {
412
    double basal_area;
420
    double basal_area;
413
    QPoint pixelOffset;
421
    QPoint pixelOffset;
414
    ResourceUnit *resource_unit;
422
    ResourceUnit *resource_unit;
415
    SInitPixel(): basal_area(0.), resource_unit(0) {}
423
    SInitPixel(): basal_area(0.), resource_unit(0) {}
416
};
424
};
417
425
418
bool sortInitPixelLessThan(const SInitPixel &s1, const SInitPixel &s2)
426
bool sortInitPixelLessThan(const SInitPixel &s1, const SInitPixel &s2)
419
{
427
{
420
    return s1.basal_area < s2.basal_area;
428
    return s1.basal_area < s2.basal_area;
421
}
429
}
422
430
423
431
424
/**
432
/**
425
*/
433
*/
426
434
427
void StandLoader::executeiLandInit(ResourceUnit *ru)
435
void StandLoader::executeiLandInit(ResourceUnit *ru)
428
{
436
{
429
437
430
    QPointF offset = ru->boundingBox().topLeft();
438
    QPointF offset = ru->boundingBox().topLeft();
431
    QPoint offsetIdx = GlobalSettings::instance()->model()->grid()->indexAt(offset);
439
    QPoint offsetIdx = GlobalSettings::instance()->model()->grid()->indexAt(offset);
432
440
433
    // a multimap holds a list for all trees.
441
    // a multimap holds a list for all trees.
434
    // key is the index of a 10x10m pixel within the resource unit
442
    // key is the index of a 10x10m pixel within the resource unit
435
    QMultiMap<int, int> tree_map;
443
    QMultiMap<int, int> tree_map;
436
    //QHash<int,SInitPixel> tcount;
444
    //QHash<int,SInitPixel> tcount;
437
445
438
    QVector<QPair<int, double> > tcount; // counts
446
    QVector<QPair<int, double> > tcount; // counts
439
    for (int i=0;i<100;i++)
447
    for (int i=0;i<100;i++)
440
        tcount.push_back(QPair<int,double>(i,0.));
448
        tcount.push_back(QPair<int,double>(i,0.));
441
449
442
    int key;
450
    int key;
443
    double rand_val, rand_fraction;
451
    double rand_val, rand_fraction;
444
    int total_count = 0;
452
    int total_count = 0;
445
    foreach(const InitFileItem &item, mInitItems) {
453
    foreach(const InitFileItem &item, mInitItems) {
446
        rand_fraction = fabs(double(item.density));
454
        rand_fraction = fabs(double(item.density));
447
        for (int i=0;i<item.count;i++) {
455
        for (int i=0;i<item.count;i++) {
448
            // create trees
456
            // create trees
449
            int tree_idx = ru->newTreeIndex();
457
            int tree_idx = ru->newTreeIndex();
450
            Tree &tree = ru->trees()[tree_idx]; // get reference to modify tree
458
            Tree &tree = ru->trees()[tree_idx]; // get reference to modify tree
451
            tree.setDbh(nrandom(ru->randomGenerator(), item.dbh_from, item.dbh_to));
459
            tree.setDbh(nrandom(ru->randomGenerator(), item.dbh_from, item.dbh_to));
452
            tree.setHeight(tree.dbh()/100. * item.hd); // dbh from cm->m, *hd-ratio -> meter height
460
            tree.setHeight(tree.dbh()/100. * item.hd); // dbh from cm->m, *hd-ratio -> meter height
453
            tree.setSpecies(item.species);
461
            tree.setSpecies(item.species);
454
            if (item.age<=0)
462
            if (item.age<=0)
455
                tree.setAge(0,tree.height());
463
                tree.setAge(0,tree.height());
456
            else
464
            else
457
                tree.setAge(item.age, tree.height());
465
                tree.setAge(item.age, tree.height());
458
            tree.setRU(ru);
466
            tree.setRU(ru);
459
            tree.setup();
467
            tree.setup();
460
            total_count++;
468
            total_count++;
461
469
462
            // calculate random value. "density" is from 1..-1.
470
            // calculate random value. "density" is from 1..-1.
463
            rand_val = mRandom->get();
471
            rand_val = mRandom->get();
464
            if (item.density<0)
472
            if (item.density<0)
465
                rand_val = 1. - rand_val;
473
                rand_val = 1. - rand_val;
466
            rand_val = rand_val * rand_fraction + drandom(ru->randomGenerator())*(1.-rand_fraction);
474
            rand_val = rand_val * rand_fraction + drandom(ru->randomGenerator())*(1.-rand_fraction);
467
475
468
            // key: rank of target pixel
476
            // key: rank of target pixel
469
            // first: index of target pixel
477
            // first: index of target pixel
470
            // second: sum of target pixel
478
            // second: sum of target pixel
471
            key = limit(int(100*rand_val), 0, 99); // get from random number generator
479
            key = limit(int(100*rand_val), 0, 99); // get from random number generator
472
            tree_map.insert(tcount[key].first, tree_idx); // store tree in map
480
            tree_map.insert(tcount[key].first, tree_idx); // store tree in map
473
            tcount[key].second+=tree.basalArea(); // aggregate the basal area for each 10m pixel
481
            tcount[key].second+=tree.basalArea(); // aggregate the basal area for each 10m pixel
474
            if ( (total_count < 20 && i%2==0)
482
            if ( (total_count < 20 && i%2==0)
475
                || (total_count<100 && i%10==0 )
483
                || (total_count<100 && i%10==0 )
476
                || (i%30==0) ) {
484
                || (i%30==0) ) {
477
                qSort(tcount.begin(), tcount.end(), sortPairLessThan);
485
                qSort(tcount.begin(), tcount.end(), sortPairLessThan);
478
            }
486
            }
479
        }
487
        }
480
        qSort(tcount.begin(), tcount.end(), sortPairLessThan);
488
        qSort(tcount.begin(), tcount.end(), sortPairLessThan);
481
    }
489
    }
482
490
483
    int bits, index, pos;
491
    int bits, index, pos;
484
    int c;
492
    int c;
485
    QList<int> trees;
493
    QList<int> trees;
486
    QPoint tree_pos;
494
    QPoint tree_pos;
487
495
488
    for (int i=0;i<100;i++) {
496
    for (int i=0;i<100;i++) {
489
        trees = tree_map.values(i);
497
        trees = tree_map.values(i);
490
        c = trees.count();
498
        c = trees.count();
491
        QPointF pixel_center = ru->boundingBox().topLeft() + QPointF((i/10)*10. + 5., (i%10)*10. + 5.);
499
        QPointF pixel_center = ru->boundingBox().topLeft() + QPointF((i/10)*10. + 5., (i%10)*10. + 5.);
492
        if (!mModel->heightGrid()->valueAt(pixel_center).isValid()) {
500
        if (!mModel->heightGrid()->valueAt(pixel_center).isValid()) {
493
            // no trees on that pixel: let trees die
501
            // no trees on that pixel: let trees die
494
            foreach(int tree_idx, trees) {
502
            foreach(int tree_idx, trees) {
495
                ru->trees()[tree_idx].die();
503
                ru->trees()[tree_idx].die();
496
            }
504
            }
497
            continue;
505
            continue;
498
        }
506
        }
499
507
500
        bits = 0;
508
        bits = 0;
501
        index = -1;
509
        index = -1;
502
        double r;
510
        double r;
503
        foreach(int tree_idx, trees) {
511
        foreach(int tree_idx, trees) {
504
            if (c>18) {
512
            if (c>18) {
505
                index = (index + 1)%25;
513
                index = (index + 1)%25;
506
            } else {
514
            } else {
507
                int stop=1000;
515
                int stop=1000;
508
                index = 0;
516
                index = 0;
509
                do {
517
                do {
510
                    //r = drandom();
518
                    //r = drandom();
511
                    //if (r<0.5)  // skip position with a prob. of 50% -> adds a little "noise"
519
                    //if (r<0.5)  // skip position with a prob. of 50% -> adds a little "noise"
512
                    //    index++;
520
                    //    index++;
513
                    //index = (index + 1)%25; // increase and roll over
521
                    //index = (index + 1)%25; // increase and roll over
514
522
515
                    // search a random position
523
                    // search a random position
516
                    r = drandom();
524
                    r = drandom();
517
                    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)
525
                    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)
518
                } while (isBitSet(bits, index)==true && stop--);
526
                } while (isBitSet(bits, index)==true && stop--);
519
                if (!stop)
527
                if (!stop)
520
                    qDebug() << "executeiLandInit: found no free bit.";
528
                    qDebug() << "executeiLandInit: found no free bit.";
521
                setBit(bits, index, true); // mark position as used
529
                setBit(bits, index, true); // mark position as used
522
            }
530
            }
523
            // get position from fixed lists (one for even, one for uneven resource units)
531
            // get position from fixed lists (one for even, one for uneven resource units)
524
            pos = ru->index()%2?evenlist[index]:unevenlist[index];
532
            pos = ru->index()%2?evenlist[index]:unevenlist[index];
525
            tree_pos = offsetIdx  // position of resource unit
533
            tree_pos = offsetIdx  // position of resource unit
526
                       + QPoint(5*(i/10), 5*(i%10)) // relative position of 10x10m pixel
534
                       + QPoint(5*(i/10), 5*(i%10)) // relative position of 10x10m pixel
527
                       + QPoint(pos/5, pos%5); // relative position within 10x10m pixel
535
                       + QPoint(pos/5, pos%5); // relative position within 10x10m pixel
528
            //qDebug() << tree_no++ << "to" << index;
536
            //qDebug() << tree_no++ << "to" << index;
529
            ru->trees()[tree_idx].setPosition(tree_pos);
537
            ru->trees()[tree_idx].setPosition(tree_pos);
530
        }
538
        }
531
    }
539
    }
532
}
540
}
533
541
534
// provide a hashing function for the QPoint type (needed from stand init function below)
542
// provide a hashing function for the QPoint type (needed from stand init function below)
535
inline uint qHash(const QPoint &key)
543
inline uint qHash(const QPoint &key)
536
 {
544
 {
537
     return qHash(key.x()) ^ qHash(key.y());
545
     return qHash(key.x()) ^ qHash(key.y());
538
 }
546
 }
539
547
540
548
541
// Initialization routine based on a stand map.
549
// Initialization routine based on a stand map.
542
// Basically a list of 10m pixels for a given stand is retrieved
550
// Basically a list of 10m pixels for a given stand is retrieved
543
// and the filled with the same procedure as the resource unit based init
551
// and the filled with the same procedure as the resource unit based init
544
// see http://iland.boku.ac.at/initialize+trees
552
// see http://iland.boku.ac.at/initialize+trees
545
void StandLoader::executeiLandInitStand(int stand_id)
553
void StandLoader::executeiLandInitStand(int stand_id)
546
{
554
{
547
555
548
    const MapGrid *grid = GlobalSettings::instance()->model()->standGrid();
556
    const MapGrid *grid = GlobalSettings::instance()->model()->standGrid();
549
557
550
    QList<int> indices = grid->gridIndices(stand_id);
558
    QList<int> indices = grid->gridIndices(stand_id);
551
    if (indices.isEmpty()) {
559
    if (indices.isEmpty()) {
552
        qDebug() << "stand" << stand_id << "not in project area. No init performed.";
560
        qDebug() << "stand" << stand_id << "not in project area. No init performed.";
553
        return;
561
        return;
554
    }
562
    }
555
    // a multiHash holds a list for all trees.
563
    // a multiHash holds a list for all trees.
556
    // key is the location of the 10x10m pixel
564
    // key is the location of the 10x10m pixel
557
    QMultiHash<QPoint, int> tree_map;
565
    QMultiHash<QPoint, int> tree_map;
558
    QList<SInitPixel> pixel_list; // working list of all 10m pixels
566
    QList<SInitPixel> pixel_list; // working list of all 10m pixels
559
567
560
    foreach (int i, indices) {
568
    foreach (int i, indices) {
561
       SInitPixel p;
569
       SInitPixel p;
562
       p.pixelOffset = grid->grid().indexOf(i); // index in the 10m grid
570
       p.pixelOffset = grid->grid().indexOf(i); // index in the 10m grid
563
       p.resource_unit = GlobalSettings::instance()->model()->ru( grid->grid().cellCenterPoint(grid->grid().indexOf(i)));
571
       p.resource_unit = GlobalSettings::instance()->model()->ru( grid->grid().cellCenterPoint(grid->grid().indexOf(i)));
564
       pixel_list.append(p);
572
       pixel_list.append(p);
565
    }
573
    }
566
    double area_factor = grid->area(stand_id) / 10000.;
574
    double area_factor = grid->area(stand_id) / 10000.;
567
575
568
    int key;
576
    int key;
569
    double rand_val, rand_fraction;
577
    double rand_val, rand_fraction;
570
    int total_count = 0;
578
    int total_count = 0;
571
    foreach(const InitFileItem &item, mInitItems) {
579
    foreach(const InitFileItem &item, mInitItems) {
572
        rand_fraction = fabs(double(item.density));
580
        rand_fraction = fabs(double(item.density));
573
        int count = item.count * area_factor + 0.5; // round
581
        int count = item.count * area_factor + 0.5; // round
574
        for (int i=0;i<count;i++) {
582
        for (int i=0;i<count;i++) {
575
583
576
584
577
            // calculate random value. "density" is from 1..-1.
585
            // calculate random value. "density" is from 1..-1.
578
            rand_val = mRandom->get();
586
            rand_val = mRandom->get();
579
            if (item.density<0)
587
            if (item.density<0)
580
                rand_val = 1. - rand_val;
588
                rand_val = 1. - rand_val;
581
            rand_val = rand_val * rand_fraction + drandom()*(1.-rand_fraction);
589
            rand_val = rand_val * rand_fraction + drandom()*(1.-rand_fraction);
582
590
583
            // key: rank of target pixel
591
            // key: rank of target pixel
584
            key = limit(int(pixel_list.count()*rand_val), 0, pixel_list.count()-1); // get from random number generator
592
            key = limit(int(pixel_list.count()*rand_val), 0, pixel_list.count()-1); // get from random number generator
585
593
586
            // create a tree
594
            // create a tree
587
            ResourceUnit *ru = pixel_list[key].resource_unit;
595
            ResourceUnit *ru = pixel_list[key].resource_unit;
588
            int tree_idx = ru->newTreeIndex();
596
            int tree_idx = ru->newTreeIndex();
589
            Tree &tree = ru->trees()[tree_idx]; // get reference to modify tree
597
            Tree &tree = ru->trees()[tree_idx]; // get reference to modify tree
590
            tree.setDbh(nrandom(item.dbh_from, item.dbh_to));
598
            tree.setDbh(nrandom(item.dbh_from, item.dbh_to));
591
            tree.setHeight(tree.dbh()/100. * item.hd); // dbh from cm->m, *hd-ratio -> meter height
599
            tree.setHeight(tree.dbh()/100. * item.hd); // dbh from cm->m, *hd-ratio -> meter height
592
            tree.setSpecies(item.species);
600
            tree.setSpecies(item.species);
593
            if (item.age<=0)
601
            if (item.age<=0)
594
                tree.setAge(0,tree.height());
602
                tree.setAge(0,tree.height());
595
            else
603
            else
596
                tree.setAge(item.age, tree.height());
604
                tree.setAge(item.age, tree.height());
597
            tree.setRU(ru);
605
            tree.setRU(ru);
598
            tree.setup();
606
            tree.setup();
599
            total_count++;
607
            total_count++;
600
608
601
            // store in the multiHash the position of the pixel and the tree_idx in the resepctive resource unit
609
            // store in the multiHash the position of the pixel and the tree_idx in the resepctive resource unit
602
            tree_map.insert(pixel_list[key].pixelOffset, tree_idx);
610
            tree_map.insert(pixel_list[key].pixelOffset, tree_idx);
603
            pixel_list[key].basal_area+=tree.basalArea(); // aggregate the basal area for each 10m pixel
611
            pixel_list[key].basal_area+=tree.basalArea(); // aggregate the basal area for each 10m pixel
604
612
605
            // resort list
613
            // resort list
606
            if ( (total_count < 20 && i%2==0)
614
            if ( (total_count < 20 && i%2==0)
607
                || (total_count<100 && i%10==0 )
615
                || (total_count<100 && i%10==0 )
608
                || (i%30==0) ) {
616
                || (i%30==0) ) {
609
                qSort(pixel_list.begin(), pixel_list.end(), sortInitPixelLessThan);
617
                qSort(pixel_list.begin(), pixel_list.end(), sortInitPixelLessThan);
610
            }
618
            }
611
        }
619
        }
612
        qSort(pixel_list.begin(), pixel_list.end(), sortInitPixelLessThan);
620
        qSort(pixel_list.begin(), pixel_list.end(), sortInitPixelLessThan);
613
    }
621
    }
614
622
615
    int bits, index, pos;
623
    int bits, index, pos;
616
    int c;
624
    int c;
617
    QList<int> trees;
625
    QList<int> trees;
618
    QPoint tree_pos;
626
    QPoint tree_pos;
619
627
620
    foreach(const SInitPixel &p, pixel_list) {
628
    foreach(const SInitPixel &p, pixel_list) {
621
        trees = tree_map.values(p.pixelOffset);
629
        trees = tree_map.values(p.pixelOffset);
622
        c = trees.count();
630
        c = trees.count();
623
        bits = 0;
631
        bits = 0;
624
        index = -1;
632
        index = -1;
625
        double r;
633
        double r;
626
        foreach(int tree_idx, trees) {
634
        foreach(int tree_idx, trees) {
627
            if (c>18) {
635
            if (c>18) {
628
                index = (index + 1)%25;
636
                index = (index + 1)%25;
629
            } else {
637
            } else {
630
                int stop=1000;
638
                int stop=1000;
631
                index = 0;
639
                index = 0;
632
                do {
640
                do {
633
                    // search a random position
641
                    // search a random position
634
                    r = drandom();
642
                    r = drandom();
635
                    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)
643
                    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)
636
                } while (isBitSet(bits, index)==true && stop--);
644
                } while (isBitSet(bits, index)==true && stop--);
637
                if (!stop)
645
                if (!stop)
638
                    qDebug() << "executeiLandInit: found no free bit.";
646
                    qDebug() << "executeiLandInit: found no free bit.";
639
                setBit(bits, index, true); // mark position as used
647
                setBit(bits, index, true); // mark position as used
640
            }
648
            }
641
            // get position from fixed lists (one for even, one for uneven resource units)
649
            // get position from fixed lists (one for even, one for uneven resource units)
642
            pos = p.resource_unit->index()%2?evenlist[index]:unevenlist[index];
650
            pos = p.resource_unit->index()%2?evenlist[index]:unevenlist[index];
643
            tree_pos = p.pixelOffset * cPxPerHeight; // convert to LIF index
651
            tree_pos = p.pixelOffset * cPxPerHeight; // convert to LIF index
644
            tree_pos += QPoint(pos/cPxPerHeight, pos%cPxPerHeight);
652
            tree_pos += QPoint(pos/cPxPerHeight, pos%cPxPerHeight);
645
653
646
            p.resource_unit->trees()[tree_idx].setPosition(tree_pos);
654
            p.resource_unit->trees()[tree_idx].setPosition(tree_pos);
647
        }
655
        }
648
    }
656
    }
649
    if (logLevelInfo()) qDebug() << "init for stand" << stand_id << "with area" << "area (m2)" << grid->area(stand_id) << "count of 10m pixels:"  << indices.count() << "initialized trees:" << total_count;
657
    if (logLevelInfo()) qDebug() << "init for stand" << stand_id << "with area" << "area (m2)" << grid->area(stand_id) << "count of 10m pixels:"  << indices.count() << "initialized trees:" << total_count;
650
658
651
}
659
}
652
660
653
/// a (hacky) way of adding saplings of a certain age to a stand defined by 'stand_id'.
661
/// a (hacky) way of adding saplings of a certain age to a stand defined by 'stand_id'.
654
int StandLoader::loadSaplings(const QString &content, int stand_id, const QString &fileName)
662
int StandLoader::loadSaplings(const QString &content, int stand_id, const QString &fileName)
655
{
663
{
656
    const MapGrid *stand_grid;
664
    const MapGrid *stand_grid;
657
    if (mCurrentMap)
665
    if (mCurrentMap)
658
        stand_grid = mCurrentMap; // if set
666
        stand_grid = mCurrentMap; // if set
659
    else
667
    else
660
        stand_grid = GlobalSettings::instance()->model()->standGrid(); // default
668
        stand_grid = GlobalSettings::instance()->model()->standGrid(); // default
661
669
662
    QList<int> indices = stand_grid->gridIndices(stand_id); // list of 10x10m pixels
670
    QList<int> indices = stand_grid->gridIndices(stand_id); // list of 10x10m pixels
663
    if (indices.isEmpty()) {
671
    if (indices.isEmpty()) {
664
        qDebug() << "stand" << stand_id << "not in project area. No init performed.";
672
        qDebug() << "stand" << stand_id << "not in project area. No init performed.";
665
        return -1;
673
        return -1;
666
    }
674
    }
667
    double area_factor = stand_grid->area(stand_id) / 10000.; // multiplier for grid (e.g. 2 if stand has area of 2 hectare)
675
    double area_factor = stand_grid->area(stand_id) / 10000.; // multiplier for grid (e.g. 2 if stand has area of 2 hectare)
668
676
669
    // parse the content of the init-file
677
    // parse the content of the init-file
670
    // species
678
    // species
671
    CSVFile init;
679
    CSVFile init;
672
    init.loadFromString(content);
680
    init.loadFromString(content);
673
    int ispecies = init.columnIndex("species");
681
    int ispecies = init.columnIndex("species");
674
    int icount = init.columnIndex("count");
682
    int icount = init.columnIndex("count");
675
    int iheight = init.columnIndex("height");
683
    int iheight = init.columnIndex("height");
676
    int iage = init.columnIndex("age");
684
    int iage = init.columnIndex("age");
677
    if (ispecies==-1 || icount==-1)
685
    if (ispecies==-1 || icount==-1)
678
        throw IException("Error while loading saplings: columns 'species' or 'count' are missing!!");
686
        throw IException("Error while loading saplings: columns 'species' or 'count' are missing!!");
679
687
680
    const SpeciesSet *set = GlobalSettings::instance()->model()->ru()->speciesSet();
688
    const SpeciesSet *set = GlobalSettings::instance()->model()->ru()->speciesSet();
681
    double height, age;
689
    double height, age;
682
    int total = 0;
690
    int total = 0;
683
    for (int row=0;row<init.rowCount();++row) {
691
    for (int row=0;row<init.rowCount();++row) {
684
        int pxcount = init.value(row, icount).toDouble() * area_factor + 0.5; // no. of pixels that should be filled (sapling grid is the same resolution as the lif-grid)
692
        int pxcount = init.value(row, icount).toDouble() * area_factor + 0.5; // no. of pixels that should be filled (sapling grid is the same resolution as the lif-grid)
685
        const Species *species = set->species(init.value(row, ispecies).toString());
693
        const Species *species = set->species(init.value(row, ispecies).toString());
686
        if (!species)
694
        if (!species)
687
            throw IException(QString("Error while loading saplings: invalid species '%1'.").arg(init.value(row, ispecies).toString()));
695
            throw IException(QString("Error while loading saplings: invalid species '%1'.").arg(init.value(row, ispecies).toString()));
688
        height = iheight==-1?0.05: init.value(row, iheight).toDouble();
696
        height = iheight==-1?0.05: init.value(row, iheight).toDouble();
689
        age = iage==-1?1:init.value(row,iage).toDouble();
697
        age = iage==-1?1:init.value(row,iage).toDouble();
690
698
691
        int misses = 0;
699
        int misses = 0;
692
        int hits = 0;
700
        int hits = 0;
693
        while (hits < pxcount) {
701
        while (hits < pxcount) {
694
           int rnd_index = irandom(0, indices.count()-1);
702
           int rnd_index = irandom(0, indices.count()-1);
695
           QPoint offset=stand_grid->grid().indexOf(indices[rnd_index]);
703
           QPoint offset=stand_grid->grid().indexOf(indices[rnd_index]);
696
           ResourceUnit *ru = GlobalSettings::instance()->model()->ru(stand_grid->grid().cellCenterPoint(offset));
704
           ResourceUnit *ru = GlobalSettings::instance()->model()->ru(stand_grid->grid().cellCenterPoint(offset));
697
           //
705
           //
698
           offset = offset * cPxPerHeight; // index of 10m patch -> to lif pixel coordinates
706
           offset = offset * cPxPerHeight; // index of 10m patch -> to lif pixel coordinates
699
           int in_p = irandom(0, cPxPerHeight*cPxPerHeight-1); // index of lif-pixel
707
           int in_p = irandom(0, cPxPerHeight*cPxPerHeight-1); // index of lif-pixel
700
           offset += QPoint(in_p / cPxPerHeight, in_p % cPxPerHeight);
708
           offset += QPoint(in_p / cPxPerHeight, in_p % cPxPerHeight);
701
           if (ru->saplingHeightForInit(offset) > height) {
709
           if (ru->saplingHeightForInit(offset) > height) {
702
               misses++;
710
               misses++;
703
           } else {
711
           } else {
704
               // ok
712
               // ok
705
               hits++;
713
               hits++;
706
               ru->resourceUnitSpecies(species).changeSapling().addSapling(offset);
714
               ru->resourceUnitSpecies(species).changeSapling().addSapling(offset);
707
           }
715
           }
708
           if (misses > 3*pxcount) {
716
           if (misses > 3*pxcount) {
709
               qDebug() << "tried to add" << pxcount << "saplings at stand" << stand_id << "but failed in finding enough free positions. Added" << hits << "and stopped.";
717
               qDebug() << "tried to add" << pxcount << "saplings at stand" << stand_id << "but failed in finding enough free positions. Added" << hits << "and stopped.";
710
               break;
718
               break;
711
           }
719
           }
712
        }
720
        }
713
        total += hits;
721
        total += hits;
714
722
715
    }
723
    }
716
    return total;
724
    return total;
717
}
725
}
718
 
726