Subversion Repositories public iLand

Rev

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

Rev 1162 Rev 1164
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
#include "species.h"
29
#include "species.h"
30
30
31
#include "helper.h"
31
#include "helper.h"
32
#include "random.h"
32
#include "random.h"
33
#include "expression.h"
33
#include "expression.h"
34
#include "expressionwrapper.h"
34
#include "expressionwrapper.h"
35
#include "environment.h"
35
#include "environment.h"
36
#include "csvfile.h"
36
#include "csvfile.h"
37
#include "mapgrid.h"
37
#include "mapgrid.h"
38
#include "snapshot.h"
38
#include "snapshot.h"
39
#include "grasscover.h"
39
#include "grasscover.h"
40
40
41
/** @class StandLoader
41
/** @class StandLoader
42
    @ingroup tools
42
    @ingroup tools
43
    loads (initializes) trees for a "stand" from various sources.
43
    loads (initializes) trees for a "stand" from various sources.
44
    StandLoader initializes trees on the landscape. It reads (usually) from text files, creates the
44
    StandLoader initializes trees on the landscape. It reads (usually) from text files, creates the
45
    trees and distributes the trees on the landscape (on the ResoureceUnit or on a stand defined by a grid).
45
    trees and distributes the trees on the landscape (on the ResoureceUnit or on a stand defined by a grid).
46

46

47
    See http://iland.boku.ac.at/initialize+trees
47
    See http://iland.boku.ac.at/initialize+trees
48
  */
48
  */
49
// provide a mapping between "Picus"-style and "iLand"-style species Ids
49
// provide a mapping between "Picus"-style and "iLand"-style species Ids
50
static QVector<int> picusSpeciesIds = QVector<int>() << 0 << 1 << 17;
50
static QVector<int> picusSpeciesIds = QVector<int>() << 0 << 1 << 17;
51
static QStringList iLandSpeciesIds = QStringList() << "piab" << "piab" << "fasy";
51
static QStringList iLandSpeciesIds = QStringList() << "piab" << "piab" << "fasy";
52
52
53
StandLoader::~StandLoader()
53
StandLoader::~StandLoader()
54
{
54
{
55
    if (mRandom)
55
    if (mRandom)
56
        delete mRandom;
56
        delete mRandom;
57
    if (mHeightGridResponse)
57
    if (mHeightGridResponse)
58
        delete mHeightGridResponse;
58
        delete mHeightGridResponse;
59
}
59
}
60
60
61
61
62
void StandLoader::copyTrees()
62
void StandLoader::copyTrees()
63
{
63
{
64
    // we assume that all stands are equal, so wie simply COPY the trees and modify them afterwards
64
    // we assume that all stands are equal, so wie simply COPY the trees and modify them afterwards
65
    const Grid<ResourceUnit*> &ruGrid=mModel->RUgrid();
65
    const Grid<ResourceUnit*> &ruGrid=mModel->RUgrid();
66
    ResourceUnit **p = ruGrid.begin();
66
    ResourceUnit **p = ruGrid.begin();
67
    if (!p)
67
    if (!p)
68
        throw IException("Standloader: invalid resource unit pointer!");
68
        throw IException("Standloader: invalid resource unit pointer!");
69
    ++p; // skip the first...
69
    ++p; // skip the first...
70
    const QVector<Tree> &tocopy = mModel->ru()->trees();
70
    const QVector<Tree> &tocopy = mModel->ru()->trees();
71
    for (; p!=ruGrid.end(); ++p) {
71
    for (; p!=ruGrid.end(); ++p) {
72
        QRectF rect = (*p)->boundingBox();
72
        QRectF rect = (*p)->boundingBox();
73
        foreach(const Tree& tree, tocopy) {
73
        foreach(const Tree& tree, tocopy) {
74
            Tree &newtree = (*p)->newTree();
74
            Tree &newtree = (*p)->newTree();
75
            newtree = tree; // copy tree data...
75
            newtree = tree; // copy tree data...
76
            newtree.setPosition(tree.position()+rect.topLeft());
76
            newtree.setPosition(tree.position()+rect.topLeft());
77
            newtree.setRU(*p);
77
            newtree.setRU(*p);
78
            newtree.setNewId();
78
            newtree.setNewId();
79
        }
79
        }
80
    }
80
    }
81
    if (logLevelInfo()) qDebug() << Tree::statCreated() << "trees loaded / copied.";
81
    if (logLevelInfo()) qDebug() << Tree::statCreated() << "trees loaded / copied.";
82
}
82
}
83
83
84
/** main routine of the stand setup.
84
/** main routine of the stand setup.
85
*/
85
*/
86
void StandLoader::processInit()
86
void StandLoader::processInit()
87
{
87
{
88
    GlobalSettings *g = GlobalSettings::instance();
88
    GlobalSettings *g = GlobalSettings::instance();
89
    XmlHelper xml(g->settings().node("model.initialization"));
89
    XmlHelper xml(g->settings().node("model.initialization"));
90
90
91
    QString copy_mode = xml.value("mode", "copy");
91
    QString copy_mode = xml.value("mode", "copy");
92
    QString type = xml.value("type", "");
92
    QString type = xml.value("type", "");
93
    QString  fileName = xml.value("file", "");
93
    QString  fileName = xml.value("file", "");
94
94
95
    bool height_grid_enabled = xml.valueBool("heightGrid.enabled", false);
95
    bool height_grid_enabled = xml.valueBool("heightGrid.enabled", false);
96
    mHeightGridTries = xml.valueInt("heightGrid.maxTries", 10);
96
    mHeightGridTries = xml.valueInt("heightGrid.maxTries", 10);
97
    QScopedPointer<const MapGrid> height_grid; // use a QScopedPointer to guarantee that the resource is freed at the end of the processInit() function
97
    QScopedPointer<const MapGrid> height_grid; // use a QScopedPointer to guarantee that the resource is freed at the end of the processInit() function
98
    if (height_grid_enabled) {
98
    if (height_grid_enabled) {
99
        QString init_height_grid_file = GlobalSettings::instance()->path(xml.value("heightGrid.fileName"), "init");
99
        QString init_height_grid_file = GlobalSettings::instance()->path(xml.value("heightGrid.fileName"), "init");
100
        qDebug() << "initialization: using predefined tree heights map" << init_height_grid_file;
100
        qDebug() << "initialization: using predefined tree heights map" << init_height_grid_file;
101
101
102
        QScopedPointer<const MapGrid> p(new MapGrid(init_height_grid_file, false));
102
        QScopedPointer<const MapGrid> p(new MapGrid(init_height_grid_file, false));
103
        if (!p->isValid()) {
103
        if (!p->isValid()) {
104
            throw IException(QString("Error when loading grid with tree heights for stand initalization: file %1 not found or not valid.").arg(init_height_grid_file));
104
            throw IException(QString("Error when loading grid with tree heights for stand initalization: file %1 not found or not valid.").arg(init_height_grid_file));
105
        }
105
        }
106
        height_grid.swap(p);
106
        height_grid.swap(p);
107
        mInitHeightGrid = height_grid.data();
107
        mInitHeightGrid = height_grid.data();
108
108
109
        QString expr=xml.value("heightGrid.fitFormula", "polygon(x, 0,0, 0.8,1, 1.1, 1, 1.25,0)");
109
        QString expr=xml.value("heightGrid.fitFormula", "polygon(x, 0,0, 0.8,1, 1.1, 1, 1.25,0)");
110
        mHeightGridResponse = new Expression(expr);
110
        mHeightGridResponse = new Expression(expr);
111
        mHeightGridResponse->linearize(0., 2.);
111
        mHeightGridResponse->linearize(0., 2.);
112
    }
112
    }
113
113
114
    Tree::resetStatistics();
114
    Tree::resetStatistics();
115
115
116
    // one global init-file for the whole area:
116
    // one global init-file for the whole area:
117
    if (copy_mode=="single") {
117
    if (copy_mode=="single") {
118
        // useful for 1ha simulations only...
118
        // useful for 1ha simulations only...
119
        if (GlobalSettings::instance()->model()->ruList().size()>1)
119
        if (GlobalSettings::instance()->model()->ruList().size()>1)
120
            throw IException("Error initialization: 'mode' is 'single' but more than one resource unit is simulated (consider using another 'mode').");
120
            throw IException("Error initialization: 'mode' is 'single' but more than one resource unit is simulated (consider using another 'mode').");
121
121
122
        loadInitFile(fileName, type, 0, GlobalSettings::instance()->model()->ru()); // this is the first resource unit
122
        loadInitFile(fileName, type, 0, GlobalSettings::instance()->model()->ru()); // this is the first resource unit
123
        evaluateDebugTrees();
123
        evaluateDebugTrees();
124
        return;
124
        return;
125
    }
125
    }
126
126
127
127
128
    // call a single tree init for each resource unit
128
    // call a single tree init for each resource unit
129
    if (copy_mode=="unit") {
129
    if (copy_mode=="unit") {
130
        foreach( const ResourceUnit *const_ru, g->model()->ruList()) {
130
        foreach( const ResourceUnit *const_ru, g->model()->ruList()) {
131
            ResourceUnit *ru = const_cast<ResourceUnit*>(const_ru);
131
            ResourceUnit *ru = const_cast<ResourceUnit*>(const_ru);
132
            // set environment
132
            // set environment
133
            g->model()->environment()->setPosition(ru->boundingBox().center());
133
            g->model()->environment()->setPosition(ru->boundingBox().center());
134
            type = xml.value("type", "");
134
            type = xml.value("type", "");
135
            fileName = xml.value("file", "");
135
            fileName = xml.value("file", "");
136
            if (fileName.isEmpty())
136
            if (fileName.isEmpty())
137
                continue;
137
                continue;
138
            loadInitFile(fileName, type, 0, ru);
138
            loadInitFile(fileName, type, 0, ru);
139
            if (logLevelInfo()) qDebug() << "loaded" << fileName << "on" << ru->boundingBox() << "," << ru->trees().count() << "trees.";
139
            if (logLevelInfo()) qDebug() << "loaded" << fileName << "on" << ru->boundingBox() << "," << ru->trees().count() << "trees.";
140
        }
140
        }
141
        evaluateDebugTrees();
141
        evaluateDebugTrees();
142
        return;
142
        return;
143
    }
143
    }
144
144
145
    // map-modus: load a init file for each "polygon" in the standgrid
145
    // map-modus: load a init file for each "polygon" in the standgrid
146
    if (copy_mode=="map") {
146
    if (copy_mode=="map") {
147
        if (!g->model()->standGrid() || !g->model()->standGrid()->isValid())
147
        if (!g->model()->standGrid() || !g->model()->standGrid()->isValid())
148
            throw IException(QString("Stand-Initialization: model.initialization.mode is 'map' but there is no valid stand grid defined (model.world.standGrid)"));
148
            throw IException(QString("Stand-Initialization: model.initialization.mode is 'map' but there is no valid stand grid defined (model.world.standGrid)"));
149
        QString map_file_name = GlobalSettings::instance()->path(xml.value("mapFileName"), "init");
149
        QString map_file_name = GlobalSettings::instance()->path(xml.value("mapFileName"), "init");
150
150
151
        CSVFile map_file(map_file_name);
151
        CSVFile map_file(map_file_name);
152
        if (map_file.rowCount()==0)
152
        if (map_file.rowCount()==0)
153
            throw IException(QString("Stand-Initialization: the map file %1 is empty or missing!").arg(map_file_name));
153
            throw IException(QString("Stand-Initialization: the map file %1 is empty or missing!").arg(map_file_name));
154
        int ikey = map_file.columnIndex("id");
154
        int ikey = map_file.columnIndex("id");
155
        int ivalue = map_file.columnIndex("filename");
155
        int ivalue = map_file.columnIndex("filename");
156
        if (ikey<0 || ivalue<0)
156
        if (ikey<0 || ivalue<0)
157
            throw IException(QString("Stand-Initialization: the map file %1 does not contain the mandatory columns 'id' and 'filename'!").arg(map_file_name));
157
            throw IException(QString("Stand-Initialization: the map file %1 does not contain the mandatory columns 'id' and 'filename'!").arg(map_file_name));
158
        QString file_name;
158
        QString file_name;
159
        for (int i=0;i<map_file.rowCount();i++) {
159
        for (int i=0;i<map_file.rowCount();i++) {
160
            int key = map_file.value(i, ikey).toInt();
160
            int key = map_file.value(i, ikey).toInt();
161
            if (key>0) {
161
            if (key>0) {
162
                file_name = map_file.value(i, ivalue).toString();
162
                file_name = map_file.value(i, ivalue).toString();
163
                if (logLevelInfo()) qDebug() << "loading" << file_name << "for grid id" << key;
163
                if (logLevelInfo()) qDebug() << "loading" << file_name << "for grid id" << key;
164
                if (!file_name.isEmpty())
164
                if (!file_name.isEmpty())
165
                    loadInitFile(file_name, type, key, NULL);
165
                    loadInitFile(file_name, type, key, NULL);
166
            }
166
            }
167
        }
167
        }
168
        mInitHeightGrid = 0;
168
        mInitHeightGrid = 0;
169
        evaluateDebugTrees();
169
        evaluateDebugTrees();
170
        return;
170
        return;
171
    }
171
    }
172
172
173
    // standgrid mode: load one large init file
173
    // standgrid mode: load one large init file
174
    if (copy_mode=="standgrid") {
174
    if (copy_mode=="standgrid") {
175
        fileName = GlobalSettings::instance()->path(fileName, "init");
175
        fileName = GlobalSettings::instance()->path(fileName, "init");
176
        if (!QFile::exists(fileName))
176
        if (!QFile::exists(fileName))
177
            throw IException(QString("load-ini-file: file '%1' does not exist.").arg(fileName));
177
            throw IException(QString("load-ini-file: file '%1' does not exist.").arg(fileName));
178
        QString content = Helper::loadTextFile(fileName);
178
        QString content = Helper::loadTextFile(fileName);
179
        // this processes the init file (also does the checking) and
179
        // this processes the init file (also does the checking) and
180
        // stores in a QHash datastrucutre
180
        // stores in a QHash datastrucutre
181
        parseInitFile(content, fileName);
181
        parseInitFile(content, fileName);
182
182
183
        // setup the random distribution
183
        // setup the random distribution
184
        QString density_func = xml.value("model.initialization.randomFunction", "1-x^2");
184
        QString density_func = xml.value("model.initialization.randomFunction", "1-x^2");
185
        if (logLevelInfo())  qDebug() << "density function:" << density_func;
185
        if (logLevelInfo())  qDebug() << "density function:" << density_func;
186
        if (!mRandom || (mRandom->densityFunction()!= density_func)) {
186
        if (!mRandom || (mRandom->densityFunction()!= density_func)) {
187
            if (mRandom)
187
            if (mRandom)
188
                delete mRandom;
188
                delete mRandom;
189
            mRandom=new RandomCustomPDF(density_func);
189
            mRandom=new RandomCustomPDF(density_func);
190
            if (logLevelInfo()) qDebug() << "new probabilty density function:" << density_func;
190
            if (logLevelInfo()) qDebug() << "new probabilty density function:" << density_func;
191
        }
191
        }
192
192
193
        if (mStandInitItems.isEmpty())
193
        if (mStandInitItems.isEmpty())
194
            throw IException("StandLoader::processInit: 'mode' is 'standgrid' but the init file is either empty or contains no 'stand_id'-column.");
194
            throw IException("StandLoader::processInit: 'mode' is 'standgrid' but the init file is either empty or contains no 'stand_id'-column.");
195
        QHash<int, QVector<InitFileItem> >::const_iterator it = mStandInitItems.constBegin();
195
        QHash<int, QVector<InitFileItem> >::const_iterator it = mStandInitItems.constBegin();
196
        while (it!=mStandInitItems.constEnd()) {
196
        while (it!=mStandInitItems.constEnd()) {
197
            mInitItems = it.value(); // copy the items...
197
            mInitItems = it.value(); // copy the items...
198
            executeiLandInitStand(it.key());
198
            executeiLandInitStand(it.key());
199
            ++it;
199
            ++it;
200
        }
200
        }
201
        qDebug() << "finished setup of trees.";
201
        qDebug() << "finished setup of trees.";
202
        evaluateDebugTrees();
202
        evaluateDebugTrees();
203
        return;
203
        return;
204
204
205
    }
205
    }
206
    if (copy_mode=="snapshot") {
206
    if (copy_mode=="snapshot") {
207
        // load a snapshot from a file
207
        // load a snapshot from a file
208
        Snapshot shot;
208
        Snapshot shot;
209
209
210
        QString input_db = GlobalSettings::instance()->path(fileName);
210
        QString input_db = GlobalSettings::instance()->path(fileName);
211
        shot.loadSnapshot(input_db);
211
        shot.loadSnapshot(input_db);
212
        return;
212
        return;
213
    }
213
    }
214
    throw IException("StandLoader::processInit: invalid initalization.mode!");
214
    throw IException("StandLoader::processInit: invalid initalization.mode!");
215
}
215
}
216
216
217
void StandLoader::processAfterInit()
217
void StandLoader::processAfterInit()
218
{
218
{
219
    XmlHelper xml(GlobalSettings::instance()->settings().node("model.initialization"));
219
    XmlHelper xml(GlobalSettings::instance()->settings().node("model.initialization"));
220
220
221
    QString mode = xml.value("mode", "copy");
221
    QString mode = xml.value("mode", "copy");
222
    if (mode=="standgrid") {
222
    if (mode=="standgrid") {
223
        // load a file with saplings per polygon
223
        // load a file with saplings per polygon
224
        QString  filename = xml.value("saplingFile", "");
224
        QString  filename = xml.value("saplingFile", "");
225
        if (filename.isEmpty())
225
        if (filename.isEmpty())
226
            return;
226
            return;
227
        filename = GlobalSettings::instance()->path(filename, "init");
227
        filename = GlobalSettings::instance()->path(filename, "init");
228
        if (!QFile::exists(filename))
228
        if (!QFile::exists(filename))
229
            throw IException(QString("load-sapling-ini-file: file '%1' does not exist.").arg(filename));
229
            throw IException(QString("load-sapling-ini-file: file '%1' does not exist.").arg(filename));
230
        CSVFile init_file(filename);
230
        CSVFile init_file(filename);
231
        int istandid = init_file.columnIndex("stand_id");
231
        int istandid = init_file.columnIndex("stand_id");
232
        if (istandid==-1)
232
        if (istandid==-1)
233
            throw IException("Sapling-Init: the init file contains no 'stand_id' column (required in 'standgrid' mode).");
233
            throw IException("Sapling-Init: the init file contains no 'stand_id' column (required in 'standgrid' mode).");
234
234
235
        int stand_id = -99999;
235
        int stand_id = -99999;
236
        int ilow = -1, ihigh = 0;
236
        int ilow = -1, ihigh = 0;
237
        int total = 0;
237
        int total = 0;
238
        for (int i=0;i<init_file.rowCount();++i) {
238
        for (int i=0;i<init_file.rowCount();++i) {
239
            int row_stand = init_file.value(i, istandid).toInt();
239
            int row_stand = init_file.value(i, istandid).toInt();
240
            if (row_stand != stand_id) {
240
            if (row_stand != stand_id) {
241
                if (stand_id>=0) {
241
                if (stand_id>=0) {
242
                    // process stand
242
                    // process stand
243
                    ihigh = i-1; // up to the last
243
                    ihigh = i-1; // up to the last
244
                    total += loadSaplingsLIF(stand_id, init_file, ilow, ihigh);
244
                    total += loadSaplingsLIF(stand_id, init_file, ilow, ihigh);
245
                }
245
                }
246
                ilow = i; // mark beginning of new stand
246
                ilow = i; // mark beginning of new stand
247
                stand_id = row_stand;
247
                stand_id = row_stand;
248
            }
248
            }
249
        }
249
        }
250
        if (stand_id>=0)
250
        if (stand_id>=0)
251
            total += loadSaplingsLIF(stand_id, init_file, ilow, init_file.rowCount()-1); // the last stand
251
            total += loadSaplingsLIF(stand_id, init_file, ilow, init_file.rowCount()-1); // the last stand
252
        qDebug() << "initialization of sapling: total created:" << total;
252
        qDebug() << "initialization of sapling: total created:" << total;
253
253
254
    }
254
    }
255
255
256
}
256
}
257
257
258
void StandLoader::evaluateDebugTrees()
258
void StandLoader::evaluateDebugTrees()
259
{
259
{
260
    // evaluate debugging
260
    // evaluate debugging
261
    QString dbg_str = GlobalSettings::instance()->settings().paramValueString("debug_tree");
261
    QString dbg_str = GlobalSettings::instance()->settings().paramValueString("debug_tree");
262
    int counter=0;
262
    int counter=0;
263
    if (!dbg_str.isEmpty()) {
263
    if (!dbg_str.isEmpty()) {
264
        if (dbg_str == "debugstamp") {
264
        if (dbg_str == "debugstamp") {
265
            qDebug() << "debug_tree = debugstamp: try touching all trees...";
265
            qDebug() << "debug_tree = debugstamp: try touching all trees...";
266
            // try to force an error if a stamp is invalid
266
            // try to force an error if a stamp is invalid
267
            AllTreeIterator at(GlobalSettings::instance()->model());
267
            AllTreeIterator at(GlobalSettings::instance()->model());
268
            double total_offset=0.;
268
            double total_offset=0.;
269
            while (Tree *t=at.next()) {
269
            while (Tree *t=at.next()) {
270
                total_offset += t->stamp()->offset();
270
                total_offset += t->stamp()->offset();
271
                if (!GlobalSettings::instance()->model()->grid()->isIndexValid(t->positionIndex()))
271
                if (!GlobalSettings::instance()->model()->grid()->isIndexValid(t->positionIndex()))
272
                    qDebug() << "evaluateDebugTrees: debugstamp: invalid position found!";
272
                    qDebug() << "evaluateDebugTrees: debugstamp: invalid position found!";
273
            }
273
            }
274
            qDebug() << "debug_tree = debugstamp: try touching all trees finished...";
274
            qDebug() << "debug_tree = debugstamp: try touching all trees finished...";
275
            return;
275
            return;
276
        }
276
        }
277
        TreeWrapper tw;
277
        TreeWrapper tw;
278
        Expression dexp(dbg_str, &tw); // load expression dbg_str and enable external model variables
278
        Expression dexp(dbg_str, &tw); // load expression dbg_str and enable external model variables
279
        AllTreeIterator at(GlobalSettings::instance()->model());
279
        AllTreeIterator at(GlobalSettings::instance()->model());
280
        double result;
280
        double result;
281
        while (Tree *t = at.next()) {
281
        while (Tree *t = at.next()) {
282
            tw.setTree(t);
282
            tw.setTree(t);
283
            result = dexp.execute();
283
            result = dexp.execute();
284
            if (result) {
284
            if (result) {
285
                t->enableDebugging();
285
                t->enableDebugging();
286
                counter++;
286
                counter++;
287
            }
287
            }
288
        }
288
        }
289
        qDebug() << "evaluateDebugTrees: enabled debugging for" << counter << "trees.";
289
        qDebug() << "evaluateDebugTrees: enabled debugging for" << counter << "trees.";
290
    }
290
    }
291
}
291
}
292
292
293
293
294
/// load a single init file. Calls loadPicusFile() or loadiLandFile()
294
/// load a single init file. Calls loadPicusFile() or loadiLandFile()
295
/// @param fileName file to load
295
/// @param fileName file to load
296
/// @param type init mode. allowed: "picus"/"single" or "iland"/"distribution"
296
/// @param type init mode. allowed: "picus"/"single" or "iland"/"distribution"
297
int StandLoader::loadInitFile(const QString &fileName, const QString &type, int stand_id, ResourceUnit *ru)
297
int StandLoader::loadInitFile(const QString &fileName, const QString &type, int stand_id, ResourceUnit *ru)
298
{
298
{
299
    QString pathFileName = GlobalSettings::instance()->path(fileName, "init");
299
    QString pathFileName = GlobalSettings::instance()->path(fileName, "init");
300
    if (!QFile::exists(pathFileName))
300
    if (!QFile::exists(pathFileName))
301
        throw IException(QString("StandLoader::loadInitFile: File %1 does not exist!").arg(pathFileName));
301
        throw IException(QString("StandLoader::loadInitFile: File %1 does not exist!").arg(pathFileName));
302
302
303
    if (type=="picus" || type=="single") {
303
    if (type=="picus" || type=="single") {
304
        if (stand_id>0)
304
        if (stand_id>0)
305
            throw IException(QLatin1String("StandLoader::loadInitFile: initialization type %1 currently not supported for stand initilization mode!")+type);
305
            throw IException(QLatin1String("StandLoader::loadInitFile: initialization type %1 currently not supported for stand initilization mode!")+type);
306
        return loadPicusFile(pathFileName, ru);
306
        return loadPicusFile(pathFileName, ru);
307
    }
307
    }
308
    if (type=="iland" || type=="distribution")
308
    if (type=="iland" || type=="distribution")
309
        return loadiLandFile(pathFileName, ru, stand_id);
309
        return loadiLandFile(pathFileName, ru, stand_id);
310
310
311
    throw IException(QLatin1String("StandLoader::loadInitFile: unknown initalization.type:")+type);
311
    throw IException(QLatin1String("StandLoader::loadInitFile: unknown initalization.type:")+type);
312
}
312
}
313
313
314
int StandLoader::loadPicusFile(const QString &fileName, ResourceUnit *ru)
314
int StandLoader::loadPicusFile(const QString &fileName, ResourceUnit *ru)
315
{
315
{
316
    QString content = Helper::loadTextFile(fileName);
316
    QString content = Helper::loadTextFile(fileName);
317
    if (content.isEmpty()) {
317
    if (content.isEmpty()) {
318
        qDebug() << "file not found: " + fileName;
318
        qDebug() << "file not found: " + fileName;
319
        return 0;
319
        return 0;
320
    }
320
    }
321
    return loadSingleTreeList(content, ru, fileName);
321
    return loadSingleTreeList(content, ru, fileName);
322
}
322
}
323
323
324
/** load a list of trees (given by content) to a resource unit. Param fileName is just for error reporting.
324
/** load a list of trees (given by content) to a resource unit. Param fileName is just for error reporting.
325
  returns the number of loaded trees.
325
  returns the number of loaded trees.
326
  */
326
  */
327
int StandLoader::loadSingleTreeList(const QString &content, ResourceUnit *ru, const QString &fileName)
327
int StandLoader::loadSingleTreeList(const QString &content, ResourceUnit *ru, const QString &fileName)
328
{
328
{
329
    if (!ru)
329
    if (!ru)
330
        ru = mModel->ru();
330
        ru = mModel->ru();
331
    Q_ASSERT(ru!=0);
331
    Q_ASSERT(ru!=0);
332
332
333
    QPointF offset = ru->boundingBox().topLeft();
333
    QPointF offset = ru->boundingBox().topLeft();
334
    SpeciesSet *speciesSet = ru->speciesSet(); // of default RU
334
    SpeciesSet *speciesSet = ru->speciesSet(); // of default RU
335
335
336
    QString my_content(content);
336
    QString my_content(content);
337
    // cut out the <trees> </trees> part if present
337
    // cut out the <trees> </trees> part if present
338
    if (content.contains("<trees>")) {
338
    if (content.contains("<trees>")) {
339
        QRegExp rx(".*<trees>(.*)</trees>.*");
339
        QRegExp rx(".*<trees>(.*)</trees>.*");
340
        rx.indexIn(content, 0);
340
        rx.indexIn(content, 0);
341
        if (rx.capturedTexts().count()<1)
341
        if (rx.capturedTexts().count()<1)
342
            return 0;
342
            return 0;
343
        my_content = rx.cap(1).trimmed();
343
        my_content = rx.cap(1).trimmed();
344
    }
344
    }
345
345
346
    CSVFile infile;
346
    CSVFile infile;
347
    infile.loadFromString(my_content);
347
    infile.loadFromString(my_content);
348
348
349
349
350
    int iID =  infile.columnIndex("id");
350
    int iID =  infile.columnIndex("id");
351
    int iX = infile.columnIndex("x");
351
    int iX = infile.columnIndex("x");
352
    int iY = infile.columnIndex("y");
352
    int iY = infile.columnIndex("y");
353
    int iBhd = infile.columnIndex("bhdfrom");
353
    int iBhd = infile.columnIndex("bhdfrom");
354
    if (iBhd<0)
354
    if (iBhd<0)
355
        iBhd = infile.columnIndex("dbh");
355
        iBhd = infile.columnIndex("dbh");
356
    double height_conversion = 100.;
356
    double height_conversion = 100.;
357
    int iHeight =infile.columnIndex("treeheight");
357
    int iHeight =infile.columnIndex("treeheight");
358
    if (iHeight<0) {
358
    if (iHeight<0) {
359
        iHeight = infile.columnIndex("height");
359
        iHeight = infile.columnIndex("height");
360
        height_conversion = 1.; // in meter
360
        height_conversion = 1.; // in meter
361
    }
361
    }
362
    int iSpecies = infile.columnIndex("species");
362
    int iSpecies = infile.columnIndex("species");
363
    int iAge = infile.columnIndex("age");
363
    int iAge = infile.columnIndex("age");
364
    if (iX==-1 || iY==-1 || iBhd==-1 || iSpecies==-1 || iHeight==-1)
364
    if (iX==-1 || iY==-1 || iBhd==-1 || iSpecies==-1 || iHeight==-1)
365
        throw IException(QString("Initfile %1 is not valid!\nRequired columns are: x,y, bhdfrom or dbh, species, treeheight or height.").arg(fileName));
365
        throw IException(QString("Initfile %1 is not valid!\nRequired columns are: x,y, bhdfrom or dbh, species, treeheight or height.").arg(fileName));
366
366
367
    double dbh;
367
    double dbh;
368
    bool ok;
368
    bool ok;
369
    int cnt=0;
369
    int cnt=0;
370
    QString speciesid;
370
    QString speciesid;
371
    for (int i=1;i<infile.rowCount();i++) {
371
    for (int i=1;i<infile.rowCount();i++) {
372
        dbh = infile.value(i, iBhd).toDouble();
372
        dbh = infile.value(i, iBhd).toDouble();
373
373
374
        //if (dbh<5.)
374
        //if (dbh<5.)
375
        //    continue;
375
        //    continue;
376
376
377
        QPointF f;
377
        QPointF f;
378
        if (iX>=0 && iY>=0) {
378
        if (iX>=0 && iY>=0) {
379
           f.setX( infile.value(i, iX).toDouble() );
379
           f.setX( infile.value(i, iX).toDouble() );
380
           f.setY( infile.value(i, iY).toDouble() );
380
           f.setY( infile.value(i, iY).toDouble() );
381
           f+=offset;
381
           f+=offset;
382
382
383
        }
383
        }
384
        // position valid?
384
        // position valid?
385
        if (!mModel->heightGrid()->valueAt(f).isValid())
385
        if (!mModel->heightGrid()->valueAt(f).isValid())
386
            continue;
386
            continue;
387
        Tree &tree = ru->newTree();
387
        Tree &tree = ru->newTree();
388
        tree.setPosition(f);
388
        tree.setPosition(f);
389
        if (iID>=0)
389
        if (iID>=0)
390
            tree.setId(infile.value(i, iID).toInt());
390
            tree.setId(infile.value(i, iID).toInt());
391
391
392
        tree.setDbh(dbh);
392
        tree.setDbh(dbh);
393
        tree.setHeight(infile.value(i,iHeight).toDouble()/height_conversion); // convert from Picus-cm to m if necessary
393
        tree.setHeight(infile.value(i,iHeight).toDouble()/height_conversion); // convert from Picus-cm to m if necessary
394
394
395
        speciesid = infile.value(i,iSpecies).toString();
395
        speciesid = infile.value(i,iSpecies).toString();
396
        int picusid = speciesid.toInt(&ok);
396
        int picusid = speciesid.toInt(&ok);
397
        if (ok) {
397
        if (ok) {
398
            int idx = picusSpeciesIds.indexOf(picusid);
398
            int idx = picusSpeciesIds.indexOf(picusid);
399
            if (idx==-1)
399
            if (idx==-1)
400
                throw IException(QString("Loading init-file: invalid Picus-species-id. Species: %1").arg(picusid));
400
                throw IException(QString("Loading init-file: invalid Picus-species-id. Species: %1").arg(picusid));
401
            speciesid = iLandSpeciesIds[idx];
401
            speciesid = iLandSpeciesIds[idx];
402
        }
402
        }
403
        Species *s = speciesSet->species(speciesid);
403
        Species *s = speciesSet->species(speciesid);
404
        if (!ru || !s)
404
        if (!ru || !s)
405
            throw IException(QString("Loading init-file: either resource unit or species invalid. Species: %1").arg(speciesid));
405
            throw IException(QString("Loading init-file: either resource unit or species invalid. Species: %1").arg(speciesid));
406
        tree.setSpecies(s);
406
        tree.setSpecies(s);
407
407
408
        ok = true;
408
        ok = true;
409
        if (iAge>=0)
409
        if (iAge>=0)
410
           tree.setAge(infile.value(i, iAge).toInt(&ok), tree.height()); // this is a *real* age
410
           tree.setAge(infile.value(i, iAge).toInt(&ok), tree.height()); // this is a *real* age
411
        if (iAge<0 || !ok || tree.age()==0)
411
        if (iAge<0 || !ok || tree.age()==0)
412
           tree.setAge(0, tree.height()); // no real tree age available
412
           tree.setAge(0, tree.height()); // no real tree age available
413
413
414
        tree.setRU(ru);
414
        tree.setRU(ru);
415
        tree.setup();
415
        tree.setup();
416
        cnt++;
416
        cnt++;
417
    }
417
    }
418
    return cnt;
418
    return cnt;
419
    //qDebug() << "loaded init-file contained" << lines.count() <<"lines.";
419
    //qDebug() << "loaded init-file contained" << lines.count() <<"lines.";
420
    //qDebug() << "lines: " << lines;
420
    //qDebug() << "lines: " << lines;
421
}
421
}
422
422
423
/** initialize trees on a resource unit based on dbh distributions.
423
/** initialize trees on a resource unit based on dbh distributions.
424
  use a fairly clever algorithm to determine tree positions.
424
  use a fairly clever algorithm to determine tree positions.
425
  see http://iland.boku.ac.at/initialize+trees
425
  see http://iland.boku.ac.at/initialize+trees
426
  @param content tree init file (including headers) in a string
426
  @param content tree init file (including headers) in a string
427
  @param ru resource unit
427
  @param ru resource unit
428
  @param fileName source file name (for error reporting)
428
  @param fileName source file name (for error reporting)
429
  @return number of trees added
429
  @return number of trees added
430
  */
430
  */
431
int StandLoader::loadDistributionList(const QString &content, ResourceUnit *ru, int stand_id, const QString &fileName)
431
int StandLoader::loadDistributionList(const QString &content, ResourceUnit *ru, int stand_id, const QString &fileName)
432
{
432
{
433
    int total_count = parseInitFile(content, fileName, ru);
433
    int total_count = parseInitFile(content, fileName, ru);
434
    if (total_count==0)
434
    if (total_count==0)
435
        return 0;
435
        return 0;
436
436
437
437
438
    // setup the random distribution
438
    // setup the random distribution
439
    QString density_func = GlobalSettings::instance()->settings().value("model.initialization.randomFunction", "1-x^2");
439
    QString density_func = GlobalSettings::instance()->settings().value("model.initialization.randomFunction", "1-x^2");
440
    if (logLevelInfo())  qDebug() << "density function:" << density_func;
440
    if (logLevelInfo())  qDebug() << "density function:" << density_func;
441
    if (!mRandom || (mRandom->densityFunction()!= density_func)) {
441
    if (!mRandom || (mRandom->densityFunction()!= density_func)) {
442
        if (mRandom)
442
        if (mRandom)
443
            delete mRandom;
443
            delete mRandom;
444
        mRandom=new RandomCustomPDF(density_func);
444
        mRandom=new RandomCustomPDF(density_func);
445
        if (logLevelInfo()) qDebug() << "new probabilty density function:" << density_func;
445
        if (logLevelInfo()) qDebug() << "new probabilty density function:" << density_func;
446
    }
446
    }
447
    if (stand_id>0) {
447
    if (stand_id>0) {
448
        // execute stand based initialization
448
        // execute stand based initialization
449
        executeiLandInitStand(stand_id);
449
        executeiLandInitStand(stand_id);
450
    } else {
450
    } else {
451
        // exeucte the initialization based on single resource units
451
        // exeucte the initialization based on single resource units
452
        executeiLandInit(ru);
452
        executeiLandInit(ru);
453
        ru->cleanTreeList();
453
        ru->cleanTreeList();
454
    }
454
    }
455
    return total_count;
455
    return total_count;
456
456
457
}
457
}
458
458
459
int StandLoader::parseInitFile(const QString &content, const QString &fileName, ResourceUnit* ru)
459
int StandLoader::parseInitFile(const QString &content, const QString &fileName, ResourceUnit* ru)
460
{
460
{
461
    if (!ru)
461
    if (!ru)
462
        ru = mModel->ru();
462
        ru = mModel->ru();
463
    Q_ASSERT(ru!=0);
463
    Q_ASSERT(ru!=0);
464
    SpeciesSet *speciesSet = ru->speciesSet(); // of default RU
464
    SpeciesSet *speciesSet = ru->speciesSet(); // of default RU
465
    Q_ASSERT(speciesSet!=0);
465
    Q_ASSERT(speciesSet!=0);
466
466
467
    //DebugTimer t("StandLoader::loadiLandFile");
467
    //DebugTimer t("StandLoader::loadiLandFile");
468
    CSVFile infile;
468
    CSVFile infile;
469
    infile.loadFromString(content);
469
    infile.loadFromString(content);
470
470
471
    int icount = infile.columnIndex("count");
471
    int icount = infile.columnIndex("count");
472
    int ispecies = infile.columnIndex("species");
472
    int ispecies = infile.columnIndex("species");
473
    int idbh_from = infile.columnIndex("dbh_from");
473
    int idbh_from = infile.columnIndex("dbh_from");
474
    int idbh_to = infile.columnIndex("dbh_to");
474
    int idbh_to = infile.columnIndex("dbh_to");
475
    int ihd = infile.columnIndex("hd");
475
    int ihd = infile.columnIndex("hd");
476
    int iage = infile.columnIndex("age");
476
    int iage = infile.columnIndex("age");
477
    int idensity = infile.columnIndex("density");
477
    int idensity = infile.columnIndex("density");
478
    if (icount<0 || ispecies<0 || idbh_from<0 || idbh_to<0 || ihd<0 || iage<0)
478
    if (icount<0 || ispecies<0 || idbh_from<0 || idbh_to<0 || ihd<0 || iage<0)
479
        throw IException(QString("load-ini-file: file '%1' containts not all required fields (count, species, dbh_from, dbh_to, hd, age).").arg(fileName));
479
        throw IException(QString("load-ini-file: file '%1' containts not all required fields (count, species, dbh_from, dbh_to, hd, age).").arg(fileName));
480
480
481
    int istandid = infile.columnIndex("stand_id");
481
    int istandid = infile.columnIndex("stand_id");
482
    mInitItems.clear();
482
    mInitItems.clear();
483
    mStandInitItems.clear();
483
    mStandInitItems.clear();
484
484
485
    InitFileItem item;
485
    InitFileItem item;
486
    bool ok;
486
    bool ok;
487
    int total_count = 0;
487
    int total_count = 0;
488
    for (int row=0;row<infile.rowCount();row++) {
488
    for (int row=0;row<infile.rowCount();row++) {
489
        item.count = infile.value(row, icount).toDouble();
489
        item.count = infile.value(row, icount).toDouble();
490
        total_count += item.count;
490
        total_count += item.count;
491
        item.dbh_from = infile.value(row, idbh_from).toDouble();
491
        item.dbh_from = infile.value(row, idbh_from).toDouble();
492
        item.dbh_to = infile.value(row, idbh_to).toDouble();
492
        item.dbh_to = infile.value(row, idbh_to).toDouble();
493
        item.hd = infile.value(row, ihd).toDouble();
493
        item.hd = infile.value(row, ihd).toDouble();
494
        if (item.hd==0. || item.dbh_from / 100. * item.hd < 4.)
494
        if (item.hd==0. || item.dbh_from / 100. * item.hd < 4.)
495
            qWarning() << QString("load init file: file '%1' tries to init trees below 4m height. hd=%2, dbh=%3.").arg(fileName).arg(item.hd).arg(item.dbh_from) ;
495
            qWarning() << QString("load init file: file '%1' tries to init trees below 4m height. hd=%2, dbh=%3.").arg(fileName).arg(item.hd).arg(item.dbh_from) ;
496
            //throw IException(QString("load init file: file '%1' tries to init trees below 4m height. hd=%2, dbh=%3.").arg(fileName).arg(item.hd).arg(item.dbh_from) );
496
            //throw IException(QString("load init file: file '%1' tries to init trees below 4m height. hd=%2, dbh=%3.").arg(fileName).arg(item.hd).arg(item.dbh_from) );
497
        ok = true;
497
        ok = true;
498
        if (iage>=0)
498
        if (iage>=0)
499
            item.age = infile.value(row, iage).toInt(&ok);
499
            item.age = infile.value(row, iage).toInt(&ok);
500
        if (iage<0 || !ok)
500
        if (iage<0 || !ok)
501
            item.age = 0;
501
            item.age = 0;
502
502
503
        item.species = speciesSet->species(infile.value(row, ispecies).toString());
503
        item.species = speciesSet->species(infile.value(row, ispecies).toString());
504
        if (idensity>=0)
504
        if (idensity>=0)
505
            item.density = infile.value(row, idensity).toDouble();
505
            item.density = infile.value(row, idensity).toDouble();
506
        else
506
        else
507
            item.density = 0.;
507
            item.density = 0.;
508
        if (item.density<-1)
508
        if (item.density<-1)
509
            throw IException(QString("load-ini-file: invalid value for density. Allowed range is -1..1: '%1' in file '%2', line %3.")
509
            throw IException(QString("load-ini-file: invalid value for density. Allowed range is -1..1: '%1' in file '%2', line %3.")
510
                             .arg(item.density)
510
                             .arg(item.density)
511
                             .arg(fileName)
511
                             .arg(fileName)
512
                             .arg(row));
512
                             .arg(row));
513
        if (!item.species) {
513
        if (!item.species) {
514
            throw IException(QString("load-ini-file: unknown speices '%1' in file '%2', line %3.")
514
            throw IException(QString("load-ini-file: unknown speices '%1' in file '%2', line %3.")
515
                             .arg(infile.value(row, ispecies).toString())
515
                             .arg(infile.value(row, ispecies).toString())
516
                             .arg(fileName)
516
                             .arg(fileName)
517
                             .arg(row));
517
                             .arg(row));
518
        }
518
        }
519
        if (istandid>=0) {
519
        if (istandid>=0) {
520
            int standid = infile.value(row,istandid).toInt();
520
            int standid = infile.value(row,istandid).toInt();
521
            mStandInitItems[standid].push_back(item);
521
            mStandInitItems[standid].push_back(item);
522
        } else {
522
        } else {
523
            mInitItems.push_back(item);
523
            mInitItems.push_back(item);
524
        }
524
        }
525
    }
525
    }
526
    return total_count;
526
    return total_count;
527
527
528
}
528
}
529
529
530
530
531
int StandLoader::loadiLandFile(const QString &fileName, ResourceUnit *ru, int stand_id)
531
int StandLoader::loadiLandFile(const QString &fileName, ResourceUnit *ru, int stand_id)
532
{
532
{
533
    if (!QFile::exists(fileName))
533
    if (!QFile::exists(fileName))
534
        throw IException(QString("load-ini-file: file '%1' does not exist.").arg(fileName));
534
        throw IException(QString("load-ini-file: file '%1' does not exist.").arg(fileName));
535
    QString content = Helper::loadTextFile(fileName);
535
    QString content = Helper::loadTextFile(fileName);
536
    return loadDistributionList(content, ru, stand_id, fileName);
536
    return loadDistributionList(content, ru, stand_id, fileName);
537
}
537
}
538
538
539
// evenlist: tentative order of pixel-indices (within a 5x5 grid) used as tree positions.
539
// evenlist: tentative order of pixel-indices (within a 5x5 grid) used as tree positions.
540
// e.g. 12 = centerpixel, 0: upper left corner, ...
540
// e.g. 12 = centerpixel, 0: upper left corner, ...
541
int evenlist[25] = { 12, 6, 18, 16, 8, 22, 2, 10, 14, 0, 24, 20, 4,
541
int evenlist[25] = { 12, 6, 18, 16, 8, 22, 2, 10, 14, 0, 24, 20, 4,
542
                     1, 13, 15, 19, 21, 3, 7, 11, 17, 23, 5, 9};
542
                     1, 13, 15, 19, 21, 3, 7, 11, 17, 23, 5, 9};
543
int unevenlist[25] = { 11,13,7,17, 1,19,5,21, 9,23,3,15,
543
int unevenlist[25] = { 11,13,7,17, 1,19,5,21, 9,23,3,15,
544
                       6,18,2,10,4,24,12,0,8,14,20,22};
544
                       6,18,2,10,4,24,12,0,8,14,20,22};
545
545
546
546
547
547
548
// sort function
548
// sort function
549
bool sortPairLessThan(const QPair<int, double> &s1, const QPair<int, double> &s2)
549
bool sortPairLessThan(const QPair<int, double> &s1, const QPair<int, double> &s2)
550
{
550
{
551
    return s1.second < s2.second;
551
    return s1.second < s2.second;
552
}
552
}
553
553
554
struct SInitPixel {
554
struct SInitPixel {
555
    double basal_area; // accumulated basal area
555
    double basal_area; // accumulated basal area
556
    QPoint pixelOffset; // location of the pixel
556
    QPoint pixelOffset; // location of the pixel
557
    ResourceUnit *resource_unit; // pointer to the resource unit the pixel belongs to
557
    ResourceUnit *resource_unit; // pointer to the resource unit the pixel belongs to
558
    double h_max; // predefined maximum height at given pixel (if available from LIDAR or so)
558
    double h_max; // predefined maximum height at given pixel (if available from LIDAR or so)
559
    bool locked; // pixel is dedicated to a single species
559
    bool locked; // pixel is dedicated to a single species
560
    SInitPixel(): basal_area(0.), resource_unit(0), h_max(-1.), locked(false) {}
560
    SInitPixel(): basal_area(0.), resource_unit(0), h_max(-1.), locked(false) {}
561
};
561
};
562
562
563
bool sortInitPixelLessThan(const SInitPixel &s1, const SInitPixel &s2)
563
bool sortInitPixelLessThan(const SInitPixel &s1, const SInitPixel &s2)
564
{
564
{
565
    return s1.basal_area < s2.basal_area;
565
    return s1.basal_area < s2.basal_area;
566
}
566
}
567
567
568
bool sortInitPixelUnlocked(const SInitPixel &s1, const SInitPixel &s2)
568
bool sortInitPixelUnlocked(const SInitPixel &s1, const SInitPixel &s2)
569
{
569
{
570
    return !s1.locked && s2.locked;
570
    return !s1.locked && s2.locked;
571
}
571
}
572
572
573
/**
573
/**
574
*/
574
*/
575
575
576
void StandLoader::executeiLandInit(ResourceUnit *ru)
576
void StandLoader::executeiLandInit(ResourceUnit *ru)
577
{
577
{
578
578
579
    QPointF offset = ru->boundingBox().topLeft();
579
    QPointF offset = ru->boundingBox().topLeft();
580
    QPoint offsetIdx = GlobalSettings::instance()->model()->grid()->indexAt(offset);
580
    QPoint offsetIdx = GlobalSettings::instance()->model()->grid()->indexAt(offset);
581
581
582
    // a multimap holds a list for all trees.
582
    // a multimap holds a list for all trees.
583
    // key is the index of a 10x10m pixel within the resource unit
583
    // key is the index of a 10x10m pixel within the resource unit
584
    QMultiMap<int, int> tree_map;
584
    QMultiMap<int, int> tree_map;
585
    //QHash<int,SInitPixel> tcount;
585
    //QHash<int,SInitPixel> tcount;
586
586
587
    QVector<QPair<int, double> > tcount; // counts
587
    QVector<QPair<int, double> > tcount; // counts
588
    for (int i=0;i<100;i++)
588
    for (int i=0;i<100;i++)
589
        tcount.push_back(QPair<int,double>(i,0.));
589
        tcount.push_back(QPair<int,double>(i,0.));
590
590
591
    int key;
591
    int key;
592
    double rand_val, rand_fraction;
592
    double rand_val, rand_fraction;
593
    int total_count = 0;
593
    int total_count = 0;
594
    foreach(const InitFileItem &item, mInitItems) {
594
    foreach(const InitFileItem &item, mInitItems) {
595
        rand_fraction = fabs(double(item.density));
595
        rand_fraction = fabs(double(item.density));
596
        for (int i=0;i<item.count;i++) {
596
        for (int i=0;i<item.count;i++) {
597
            // create trees
597
            // create trees
598
            int tree_idx = ru->newTreeIndex();
598
            int tree_idx = ru->newTreeIndex();
599
            Tree &tree = ru->trees()[tree_idx]; // get reference to modify tree
599
            Tree &tree = ru->trees()[tree_idx]; // get reference to modify tree
600
            tree.setDbh(nrandom(item.dbh_from, item.dbh_to));
600
            tree.setDbh(nrandom(item.dbh_from, item.dbh_to));
601
            tree.setHeight(tree.dbh()/100. * item.hd); // dbh from cm->m, *hd-ratio -> meter height
601
            tree.setHeight(tree.dbh()/100. * item.hd); // dbh from cm->m, *hd-ratio -> meter height
602
            tree.setSpecies(item.species);
602
            tree.setSpecies(item.species);
603
            if (item.age<=0)
603
            if (item.age<=0)
604
                tree.setAge(0,tree.height());
604
                tree.setAge(0,tree.height());
605
            else
605
            else
606
                tree.setAge(item.age, tree.height());
606
                tree.setAge(item.age, tree.height());
607
            tree.setRU(ru);
607
            tree.setRU(ru);
608
            tree.setup();
608
            tree.setup();
609
            total_count++;
609
            total_count++;
610
610
611
            // calculate random value. "density" is from 1..-1.
611
            // calculate random value. "density" is from 1..-1.
612
            rand_val = mRandom->get();
612
            rand_val = mRandom->get();
613
            if (item.density<0)
613
            if (item.density<0)
614
                rand_val = 1. - rand_val;
614
                rand_val = 1. - rand_val;
615
            rand_val = rand_val * rand_fraction + drandom()*(1.-rand_fraction);
615
            rand_val = rand_val * rand_fraction + drandom()*(1.-rand_fraction);
616
616
617
            // key: rank of target pixel
617
            // key: rank of target pixel
618
            // first: index of target pixel
618
            // first: index of target pixel
619
            // second: sum of target pixel
619
            // second: sum of target pixel
620
            key = limit(int(100*rand_val), 0, 99); // get from random number generator
620
            key = limit(int(100*rand_val), 0, 99); // get from random number generator
621
            tree_map.insert(tcount[key].first, tree_idx); // store tree in map
621
            tree_map.insert(tcount[key].first, tree_idx); // store tree in map
622
            tcount[key].second+=tree.basalArea(); // aggregate the basal area for each 10m pixel
622
            tcount[key].second+=tree.basalArea(); // aggregate the basal area for each 10m pixel
623
            if ( (total_count < 20 && i%2==0)
623
            if ( (total_count < 20 && i%2==0)
624
                || (total_count<100 && i%10==0 )
624
                || (total_count<100 && i%10==0 )
625
                || (i%30==0) ) {
625
                || (i%30==0) ) {
626
                qSort(tcount.begin(), tcount.end(), sortPairLessThan);
626
                qSort(tcount.begin(), tcount.end(), sortPairLessThan);
627
            }
627
            }
628
        }
628
        }
629
        qSort(tcount.begin(), tcount.end(), sortPairLessThan);
629
        qSort(tcount.begin(), tcount.end(), sortPairLessThan);
630
    }
630
    }
631
631
632
    int bits, index, pos;
632
    int bits, index, pos;
633
    int c;
633
    int c;
634
    QList<int> trees;
634
    QList<int> trees;
635
    QPoint tree_pos;
635
    QPoint tree_pos;
636
636
637
    for (int i=0;i<100;i++) {
637
    for (int i=0;i<100;i++) {
638
        trees = tree_map.values(i);
638
        trees = tree_map.values(i);
639
        c = trees.count();
639
        c = trees.count();
640
        QPointF pixel_center = ru->boundingBox().topLeft() + QPointF((i/10)*10. + 5., (i%10)*10. + 5.);
640
        QPointF pixel_center = ru->boundingBox().topLeft() + QPointF((i/10)*10. + 5., (i%10)*10. + 5.);
641
        if (!mModel->heightGrid()->valueAt(pixel_center).isValid()) {
641
        if (!mModel->heightGrid()->valueAt(pixel_center).isValid()) {
642
            // no trees on that pixel: let trees die
642
            // no trees on that pixel: let trees die
643
            foreach(int tree_idx, trees) {
643
            foreach(int tree_idx, trees) {
644
                ru->trees()[tree_idx].die();
644
                ru->trees()[tree_idx].die();
645
            }
645
            }
646
            continue;
646
            continue;
647
        }
647
        }
648
648
649
        bits = 0;
649
        bits = 0;
650
        index = -1;
650
        index = -1;
651
        double r;
651
        double r;
652
        foreach(int tree_idx, trees) {
652
        foreach(int tree_idx, trees) {
653
            if (c>18) {
653
            if (c>18) {
654
                index = (index + 1)%25;
654
                index = (index + 1)%25;
655
            } else {
655
            } else {
656
                int stop=1000;
656
                int stop=1000;
657
                index = 0;
657
                index = 0;
658
                do {
658
                do {
659
                    //r = drandom();
659
                    //r = drandom();
660
                    //if (r<0.5)  // skip position with a prob. of 50% -> adds a little "noise"
660
                    //if (r<0.5)  // skip position with a prob. of 50% -> adds a little "noise"
661
                    //    index++;
661
                    //    index++;
662
                    //index = (index + 1)%25; // increase and roll over
662
                    //index = (index + 1)%25; // increase and roll over
663
663
664
                    // search a random position
664
                    // search a random position
665
                    r = drandom();
665
                    r = drandom();
666
                    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)
666
                    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)
667
                } while (isBitSet(bits, index)==true && stop--);
667
                } while (isBitSet(bits, index)==true && stop--);
668
                if (!stop)
668
                if (!stop)
669
                    qDebug() << "executeiLandInit: found no free bit.";
669
                    qDebug() << "executeiLandInit: found no free bit.";
670
                setBit(bits, index, true); // mark position as used
670
                setBit(bits, index, true); // mark position as used
671
            }
671
            }
672
            // get position from fixed lists (one for even, one for uneven resource units)
672
            // get position from fixed lists (one for even, one for uneven resource units)
673
            pos = ru->index()%2?evenlist[index]:unevenlist[index];
673
            pos = ru->index()%2?evenlist[index]:unevenlist[index];
674
            tree_pos = offsetIdx  // position of resource unit
674
            tree_pos = offsetIdx  // position of resource unit
675
                       + QPoint(5*(i/10), 5*(i%10)) // relative position of 10x10m pixel
675
                       + QPoint(5*(i/10), 5*(i%10)) // relative position of 10x10m pixel
676
                       + QPoint(pos/5, pos%5); // relative position within 10x10m pixel
676
                       + QPoint(pos/5, pos%5); // relative position within 10x10m pixel
677
            //qDebug() << tree_no++ << "to" << index;
677
            //qDebug() << tree_no++ << "to" << index;
678
            ru->trees()[tree_idx].setPosition(tree_pos);
678
            ru->trees()[tree_idx].setPosition(tree_pos);
679
        }
679
        }
680
    }
680
    }
681
}
681
}
682
682
683
683
684
684
685
// Initialization routine based on a stand map.
685
// Initialization routine based on a stand map.
686
// Basically a list of 10m pixels for a given stand is retrieved
686
// Basically a list of 10m pixels for a given stand is retrieved
687
// and the filled with the same procedure as the resource unit based init
687
// and the filled with the same procedure as the resource unit based init
688
// see http://iland.boku.ac.at/initialize+trees
688
// see http://iland.boku.ac.at/initialize+trees
689
void StandLoader::executeiLandInitStand(int stand_id)
689
void StandLoader::executeiLandInitStand(int stand_id)
690
{
690
{
691
691
692
    const MapGrid *grid = GlobalSettings::instance()->model()->standGrid();
692
    const MapGrid *grid = GlobalSettings::instance()->model()->standGrid();
693
    if (mCurrentMap)
693
    if (mCurrentMap)
694
        grid = mCurrentMap;
694
        grid = mCurrentMap;
695
695
696
    // get a list of positions of all pixels that belong to our stand
696
    // get a list of positions of all pixels that belong to our stand
697
    QList<int> indices = grid->gridIndices(stand_id);
697
    QList<int> indices = grid->gridIndices(stand_id);
698
    if (indices.isEmpty()) {
698
    if (indices.isEmpty()) {
699
        qDebug() << "stand" << stand_id << "not in project area. No init performed.";
699
        qDebug() << "stand" << stand_id << "not in project area. No init performed.";
700
        return;
700
        return;
701
    }
701
    }
702
    // a multiHash holds a list for all trees.
702
    // a multiHash holds a list for all trees.
703
    // key is the location of the 10x10m pixel
703
    // key is the location of the 10x10m pixel
704
    QMultiHash<QPoint, int> tree_map;
704
    QMultiHash<QPoint, int> tree_map;
705
    QList<SInitPixel> pixel_list; // working list of all 10m pixels
705
    QList<SInitPixel> pixel_list; // working list of all 10m pixels
706
    pixel_list.reserve(indices.size());
706
    pixel_list.reserve(indices.size());
707
707
708
    foreach (int i, indices) {
708
    foreach (int i, indices) {
709
       SInitPixel p;
709
       SInitPixel p;
710
       p.pixelOffset = grid->grid().indexOf(i); // index in the 10m grid
710
       p.pixelOffset = grid->grid().indexOf(i); // index in the 10m grid
711
       p.resource_unit = GlobalSettings::instance()->model()->ru( grid->grid().cellCenterPoint(p.pixelOffset));
711
       p.resource_unit = GlobalSettings::instance()->model()->ru( grid->grid().cellCenterPoint(p.pixelOffset));
712
       if (mInitHeightGrid)
712
       if (mInitHeightGrid)
713
           p.h_max = mInitHeightGrid->grid().constValueAtIndex(p.pixelOffset);
713
           p.h_max = mInitHeightGrid->grid().constValueAtIndex(p.pixelOffset);
714
       pixel_list.append(p);
714
       pixel_list.append(p);
715
    }
715
    }
716
    double area_factor = grid->area(stand_id) / cRUArea;
716
    double area_factor = grid->area(stand_id) / cRUArea;
717
717
718
    int key=0;
718
    int key=0;
719
    double rand_val, rand_fraction;
719
    double rand_val, rand_fraction;
720
    int total_count = 0;
720
    int total_count = 0;
721
    int total_tries = 0;
721
    int total_tries = 0;
722
    int total_misses = 0;
722
    int total_misses = 0;
723
    if (mInitHeightGrid && !mHeightGridResponse)
723
    if (mInitHeightGrid && !mHeightGridResponse)
724
        throw IException("executeiLandInitStand: trying to initialize with height grid but without response function.");
724
        throw IException("executeiLandInitStand: trying to initialize with height grid but without response function.");
725
725
726
    Species *last_locked_species=0;
726
    Species *last_locked_species=0;
727
    foreach(const InitFileItem &item, mInitItems) {
727
    foreach(const InitFileItem &item, mInitItems) {
728
        if (item.density>1.) {
728
        if (item.density>1.) {
729
            // special case with single-species-area
729
            // special case with single-species-area
730
            if (total_count==0) {
730
            if (total_count==0) {
731
                // randomize the pixels
731
                // randomize the pixels
732
                for (QList<SInitPixel>::iterator it=pixel_list.begin();it!=pixel_list.end();++it)
732
                for (QList<SInitPixel>::iterator it=pixel_list.begin();it!=pixel_list.end();++it)
733
                    it->basal_area = drandom();
733
                    it->basal_area = drandom();
734
                qSort(pixel_list.begin(), pixel_list.end(), sortInitPixelLessThan);
734
                qSort(pixel_list.begin(), pixel_list.end(), sortInitPixelLessThan);
735
                for (QList<SInitPixel>::iterator it=pixel_list.begin();it!=pixel_list.end();++it)
735
                for (QList<SInitPixel>::iterator it=pixel_list.begin();it!=pixel_list.end();++it)
736
                    it->basal_area = 0.;
736
                    it->basal_area = 0.;
737
            }
737
            }
738
738
739
            if (item.species != last_locked_species) {
739
            if (item.species != last_locked_species) {
740
                last_locked_species=item.species;
740
                last_locked_species=item.species;
741
                qSort(pixel_list.begin(), pixel_list.end(), sortInitPixelUnlocked);
741
                qSort(pixel_list.begin(), pixel_list.end(), sortInitPixelUnlocked);
742
            }
742
            }
743
        } else {
743
        } else {
744
            qSort(pixel_list.begin(), pixel_list.end(), sortInitPixelLessThan);
744
            qSort(pixel_list.begin(), pixel_list.end(), sortInitPixelLessThan);
745
            last_locked_species=0;
745
            last_locked_species=0;
746
        }
746
        }
747
        rand_fraction = item.density;
747
        rand_fraction = item.density;
748
        int count = item.count * area_factor + 0.5; // round
748
        int count = item.count * area_factor + 0.5; // round
749
        double init_max_height = item.dbh_to/100. * item.hd;
749
        double init_max_height = item.dbh_to/100. * item.hd;
750
        for (int i=0;i<count;i++) {
750
        for (int i=0;i<count;i++) {
751
751
752
            bool found = false;
752
            bool found = false;
753
            int tries = mHeightGridTries;
753
            int tries = mHeightGridTries;
754
            while (!found &&--tries) {
754
            while (!found &&--tries) {
755
                // calculate random value. "density" is from 1..-1.
755
                // calculate random value. "density" is from 1..-1.
756
                if (item.density <= 1.) {
756
                if (item.density <= 1.) {
757
                    rand_val = mRandom->get();
757
                    rand_val = mRandom->get();
758
                    if (item.density<0)
758
                    if (item.density<0)
759
                        rand_val = 1. - rand_val;
759
                        rand_val = 1. - rand_val;
760
                    rand_val = rand_val * rand_fraction + drandom()*(1.-rand_fraction);
760
                    rand_val = rand_val * rand_fraction + drandom()*(1.-rand_fraction);
761
                } else {
761
                } else {
762
                    // limited area: limit potential area using the "density" input parameter
762
                    // limited area: limit potential area using the "density" input parameter
763
                    rand_val = drandom() * qMin(item.density/100., 1.);
763
                    rand_val = drandom() * qMin(item.density/100., 1.);
764
                }
764
                }
765
                ++total_tries;
765
                ++total_tries;
766
766
767
                // key: rank of target pixel
767
                // key: rank of target pixel
768
                key = limit(int(pixel_list.count()*rand_val), 0, pixel_list.count()-1); // get from random number generator
768
                key = limit(int(pixel_list.count()*rand_val), 0, pixel_list.count()-1); // get from random number generator
769
769
770
                if (mInitHeightGrid) {
770
                if (mInitHeightGrid) {
771
                    // calculate how good the selected pixel fits w.r.t. the predefined height
771
                    // calculate how good the selected pixel fits w.r.t. the predefined height
772
                    double p_value = pixel_list[key].h_max>0.?mHeightGridResponse->calculate(init_max_height/pixel_list[key].h_max):0.;
772
                    double p_value = pixel_list[key].h_max>0.?mHeightGridResponse->calculate(init_max_height/pixel_list[key].h_max):0.;
773
                    if (drandom() < p_value)
773
                    if (drandom() < p_value)
774
                        found = true;
774
                        found = true;
775
                } else {
775
                } else {
776
                    found = true;
776
                    found = true;
777
                }
777
                }
778
                if (!last_locked_species && pixel_list[key].locked)
778
                if (!last_locked_species && pixel_list[key].locked)
779
                    found = false;
779
                    found = false;
780
            }
780
            }
781
            if (tries<0) ++total_misses;
781
            if (tries<0) ++total_misses;
782
782
783
            // create a tree
783
            // create a tree
784
            ResourceUnit *ru = pixel_list[key].resource_unit;
784
            ResourceUnit *ru = pixel_list[key].resource_unit;
785
            int tree_idx = ru->newTreeIndex();
785
            int tree_idx = ru->newTreeIndex();
786
            Tree &tree = ru->trees()[tree_idx]; // get reference to modify tree
786
            Tree &tree = ru->trees()[tree_idx]; // get reference to modify tree
787
            tree.setDbh(nrandom(item.dbh_from, item.dbh_to));
787
            tree.setDbh(nrandom(item.dbh_from, item.dbh_to));
788
            tree.setHeight(tree.dbh()/100. * item.hd); // dbh from cm->m, *hd-ratio -> meter height
788
            tree.setHeight(tree.dbh()/100. * item.hd); // dbh from cm->m, *hd-ratio -> meter height
789
            tree.setSpecies(item.species);
789
            tree.setSpecies(item.species);
790
            if (item.age<=0)
790
            if (item.age<=0)
791
                tree.setAge(0,tree.height());
791
                tree.setAge(0,tree.height());
792
            else
792
            else
793
                tree.setAge(item.age, tree.height());
793
                tree.setAge(item.age, tree.height());
794
            tree.setRU(ru);
794
            tree.setRU(ru);
795
            tree.setup();
795
            tree.setup();
796
            total_count++;
796
            total_count++;
797
797
798
            // store in the multiHash the position of the pixel and the tree_idx in the resepctive resource unit
798
            // store in the multiHash the position of the pixel and the tree_idx in the resepctive resource unit
799
            tree_map.insert(pixel_list[key].pixelOffset, tree_idx);
799
            tree_map.insert(pixel_list[key].pixelOffset, tree_idx);
800
            pixel_list[key].basal_area+=tree.basalArea(); // aggregate the basal area for each 10m pixel
800
            pixel_list[key].basal_area+=tree.basalArea(); // aggregate the basal area for each 10m pixel
801
            if (last_locked_species)
801
            if (last_locked_species)
802
                pixel_list[key].locked = true;
802
                pixel_list[key].locked = true;
803
803
804
            // resort list
804
            // resort list
805
            if (last_locked_species==0 && ((total_count < 20 && i%2==0)
805
            if (last_locked_species==0 && ((total_count < 20 && i%2==0)
806
                || (total_count<100 && i%10==0 )
806
                || (total_count<100 && i%10==0 )
807
                || (i%30==0)) ) {
807
                || (i%30==0)) ) {
808
                qSort(pixel_list.begin(), pixel_list.end(), sortInitPixelLessThan);
808
                qSort(pixel_list.begin(), pixel_list.end(), sortInitPixelLessThan);
809
            }
809
            }
810
        }
810
        }
811
    }
811
    }
812
    if (total_misses>0 || total_tries > total_count) {
812
    if (total_misses>0 || total_tries > total_count) {
813
        if (logLevelInfo()) qDebug() << "init for stand" << stand_id << "treecount:" << total_count << ", tries:" << total_tries << ", misses:" << total_misses << ", %miss:" << qRound(total_misses*100 / (double)total_count);
813
        if (logLevelInfo()) qDebug() << "init for stand" << stand_id << "treecount:" << total_count << ", tries:" << total_tries << ", misses:" << total_misses << ", %miss:" << qRound(total_misses*100 / (double)total_count);
814
    }
814
    }
815
815
816
    int bits, index, pos;
816
    int bits, index, pos;
817
    int c;
817
    int c;
818
    QList<int> trees;
818
    QList<int> trees;
819
    QPoint tree_pos;
819
    QPoint tree_pos;
820
820
821
    foreach(const SInitPixel &p, pixel_list) {
821
    foreach(const SInitPixel &p, pixel_list) {
822
        trees = tree_map.values(p.pixelOffset);
822
        trees = tree_map.values(p.pixelOffset);
823
        c = trees.count();
823
        c = trees.count();
824
        bits = 0;
824
        bits = 0;
825
        index = -1;
825
        index = -1;
826
        double r;
826
        double r;
827
        foreach(int tree_idx, trees) {
827
        foreach(int tree_idx, trees) {
828
            if (c>18) {
828
            if (c>18) {
829
                index = (index + 1)%25;
829
                index = (index + 1)%25;
830
            } else {
830
            } else {
831
                int stop=1000;
831
                int stop=1000;
832
                index = 0;
832
                index = 0;
833
                do {
833
                do {
834
                    // search a random position
834
                    // search a random position
835
                    r = drandom();
835
                    r = drandom();
836
                    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)
836
                    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)
837
                } while (isBitSet(bits, index)==true && stop--);
837
                } while (isBitSet(bits, index)==true && stop--);
838
                if (!stop)
838
                if (!stop)
839
                    qDebug() << "executeiLandInit: found no free bit.";
839
                    qDebug() << "executeiLandInit: found no free bit.";
840
                setBit(bits, index, true); // mark position as used
840
                setBit(bits, index, true); // mark position as used
841
            }
841
            }
842
            // get position from fixed lists (one for even, one for uneven resource units)
842
            // get position from fixed lists (one for even, one for uneven resource units)
843
            pos = p.resource_unit->index()%2?evenlist[index]:unevenlist[index];
843
            pos = p.resource_unit->index()%2?evenlist[index]:unevenlist[index];
844
            tree_pos = p.pixelOffset * cPxPerHeight; // convert to LIF index
844
            tree_pos = p.pixelOffset * cPxPerHeight; // convert to LIF index
845
            tree_pos += QPoint(pos/cPxPerHeight, pos%cPxPerHeight);
845
            tree_pos += QPoint(pos/cPxPerHeight, pos%cPxPerHeight);
846
846
847
            p.resource_unit->trees()[tree_idx].setPosition(tree_pos);
847
            p.resource_unit->trees()[tree_idx].setPosition(tree_pos);
848
            // test if tree position is valid..
848
            // test if tree position is valid..
849
            if (!GlobalSettings::instance()->model()->grid()->isIndexValid(tree_pos))
849
            if (!GlobalSettings::instance()->model()->grid()->isIndexValid(tree_pos))
850
                qDebug() << "Standloader: invalid position!";
850
                qDebug() << "Standloader: invalid position!";
851
        }
851
        }
852
    }
852
    }
853
    if (logLevelInfo())
853
    if (logLevelInfo())
854
        qDebug() << "init for stand" << stand_id << "with area" << "area (m2)" << grid->area(stand_id) << "count of 10m pixels:"  << indices.count() << "initialized trees:" << total_count;
854
        qDebug() << "init for stand" << stand_id << "with area" << "area (m2)" << grid->area(stand_id) << "count of 10m pixels:"  << indices.count() << "initialized trees:" << total_count;
855
855
856
}
856
}
857
857
858
/// a (hacky) way of adding saplings of a certain age to a stand defined by 'stand_id'.
858
/// a (hacky) way of adding saplings of a certain age to a stand defined by 'stand_id'.
859
int StandLoader::loadSaplings(const QString &content, int stand_id, const QString &fileName)
859
int StandLoader::loadSaplings(const QString &content, int stand_id, const QString &fileName)
860
{
860
{
861
    Q_UNUSED(fileName);
861
    Q_UNUSED(fileName);
862
    const MapGrid *stand_grid;
862
    const MapGrid *stand_grid;
863
    if (mCurrentMap)
863
    if (mCurrentMap)
864
        stand_grid = mCurrentMap; // if set
864
        stand_grid = mCurrentMap; // if set
865
    else
865
    else
866
        stand_grid = GlobalSettings::instance()->model()->standGrid(); // default
866
        stand_grid = GlobalSettings::instance()->model()->standGrid(); // default
867
867
868
    QList<int> indices = stand_grid->gridIndices(stand_id); // list of 10x10m pixels
868
    QList<int> indices = stand_grid->gridIndices(stand_id); // list of 10x10m pixels
869
    if (indices.isEmpty()) {
869
    if (indices.isEmpty()) {
870
        qDebug() << "stand" << stand_id << "not in project area. No init performed.";
870
        qDebug() << "stand" << stand_id << "not in project area. No init performed.";
871
        return -1;
871
        return -1;
872
    }
872
    }
873
    double area_factor = stand_grid->area(stand_id) / cRUArea; // multiplier for grid (e.g. 2 if stand has area of 2 hectare)
873
    double area_factor = stand_grid->area(stand_id) / cRUArea; // multiplier for grid (e.g. 2 if stand has area of 2 hectare)
874
874
875
    // parse the content of the init-file
875
    // parse the content of the init-file
876
    // species
876
    // species
877
    CSVFile init;
877
    CSVFile init;
878
    init.loadFromString(content);
878
    init.loadFromString(content);
879
    int ispecies = init.columnIndex("species");
879
    int ispecies = init.columnIndex("species");
880
    int icount = init.columnIndex("count");
880
    int icount = init.columnIndex("count");
881
    int iheight = init.columnIndex("height");
881
    int iheight = init.columnIndex("height");
882
    int iage = init.columnIndex("age");
882
    int iage = init.columnIndex("age");
883
    if (ispecies==-1 || icount==-1)
883
    if (ispecies==-1 || icount==-1)
884
        throw IException("Error while loading saplings: columns 'species' or 'count' are missing!!");
884
        throw IException("Error while loading saplings: columns 'species' or 'count' are missing!!");
885
885
886
    const SpeciesSet *set = GlobalSettings::instance()->model()->ru()->speciesSet();
886
    const SpeciesSet *set = GlobalSettings::instance()->model()->ru()->speciesSet();
887
    double height, age;
887
    double height, age;
888
    int total = 0;
888
    int total = 0;
889
    for (int row=0;row<init.rowCount();++row) {
889
    for (int row=0;row<init.rowCount();++row) {
890
        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)
890
        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)
891
        const Species *species = set->species(init.value(row, ispecies).toString());
891
        const Species *species = set->species(init.value(row, ispecies).toString());
892
        if (!species)
892
        if (!species)
893
            throw IException(QString("Error while loading saplings: invalid species '%1'.").arg(init.value(row, ispecies).toString()));
893
            throw IException(QString("Error while loading saplings: invalid species '%1'.").arg(init.value(row, ispecies).toString()));
894
        height = iheight==-1?0.05: init.value(row, iheight).toDouble();
894
        height = iheight==-1?0.05: init.value(row, iheight).toDouble();
895
        age = iage==-1?1:init.value(row,iage).toDouble();
895
        age = iage==-1?1:init.value(row,iage).toDouble();
896
896
897
        int misses = 0;
897
        int misses = 0;
898
        int hits = 0;
898
        int hits = 0;
899
        while (hits < pxcount) {
899
        while (hits < pxcount) {
900
           int rnd_index = irandom(0, indices.count()-1);
-
 
-
 
900
           int rnd_index = irandom(0, indices.count());
901
           QPoint offset=stand_grid->grid().indexOf(indices[rnd_index]);
901
           QPoint offset=stand_grid->grid().indexOf(indices[rnd_index]);
902
           ResourceUnit *ru = GlobalSettings::instance()->model()->ru(stand_grid->grid().cellCenterPoint(offset));
902
           ResourceUnit *ru = GlobalSettings::instance()->model()->ru(stand_grid->grid().cellCenterPoint(offset));
903
           //
903
           //
904
           offset = offset * cPxPerHeight; // index of 10m patch -> to lif pixel coordinates
904
           offset = offset * cPxPerHeight; // index of 10m patch -> to lif pixel coordinates
905
           int in_p = irandom(0, cPxPerHeight*cPxPerHeight-1); // index of lif-pixel
-
 
-
 
905
           int in_p = irandom(0, cPxPerHeight*cPxPerHeight); // index of lif-pixel
906
           offset += QPoint(in_p / cPxPerHeight, in_p % cPxPerHeight);
906
           offset += QPoint(in_p / cPxPerHeight, in_p % cPxPerHeight);
907
           SaplingCell *sc = GlobalSettings::instance()->model()->saplings()->cell(offset);
907
           SaplingCell *sc = GlobalSettings::instance()->model()->saplings()->cell(offset);
908
           if (sc && sc->max_height()>height) {
908
           if (sc && sc->max_height()>height) {
909
           //if (!ru || ru->saplingHeightForInit(offset) > height) {
909
           //if (!ru || ru->saplingHeightForInit(offset) > height) {
910
               misses++;
910
               misses++;
911
           } else {
911
           } else {
912
               // ok
912
               // ok
913
               hits++;
913
               hits++;
914
               if (sc)
914
               if (sc)
915
                   sc->addSapling(height, age, species->index());
915
                   sc->addSapling(height, age, species->index());
916
               //ru->resourceUnitSpecies(species).changeSapling().addSapling(offset, height, age);
916
               //ru->resourceUnitSpecies(species).changeSapling().addSapling(offset, height, age);
917
           }
917
           }
918
           if (misses > 3*pxcount) {
918
           if (misses > 3*pxcount) {
919
               qDebug() << "tried to add" << pxcount << "saplings at stand" << stand_id << "but failed in finding enough free positions. Added" << hits << "and stopped.";
919
               qDebug() << "tried to add" << pxcount << "saplings at stand" << stand_id << "but failed in finding enough free positions. Added" << hits << "and stopped.";
920
               break;
920
               break;
921
           }
921
           }
922
        }
922
        }
923
        total += hits;
923
        total += hits;
924
924
925
    }
925
    }
926
    return total;
926
    return total;
927
}
927
}
928
928
929
bool LIFValueHigher(const float *a, const float *b)
929
bool LIFValueHigher(const float *a, const float *b)
930
{
930
{
931
    return *a > *b;
931
    return *a > *b;
932
}
932
}
933
933
934
int StandLoader::loadSaplingsLIF(int stand_id, const CSVFile &init, int low_index, int high_index)
934
int StandLoader::loadSaplingsLIF(int stand_id, const CSVFile &init, int low_index, int high_index)
935
{
935
{
936
    const MapGrid *stand_grid;
936
    const MapGrid *stand_grid;
937
    if (mCurrentMap)
937
    if (mCurrentMap)
938
        stand_grid = mCurrentMap; // if set
938
        stand_grid = mCurrentMap; // if set
939
    else
939
    else
940
        stand_grid = GlobalSettings::instance()->model()->standGrid(); // default
940
        stand_grid = GlobalSettings::instance()->model()->standGrid(); // default
941
941
942
    if (!stand_grid->isValid(stand_id))
942
    if (!stand_grid->isValid(stand_id))
943
        return 0;
943
        return 0;
944
944
945
    QList<int> indices = stand_grid->gridIndices(stand_id); // list of 10x10m pixels
945
    QList<int> indices = stand_grid->gridIndices(stand_id); // list of 10x10m pixels
946
    if (indices.isEmpty()) {
946
    if (indices.isEmpty()) {
947
        qDebug() << "stand" << stand_id << "not in project area. No init performed.";
947
        qDebug() << "stand" << stand_id << "not in project area. No init performed.";
948
        return 0;
948
        return 0;
949
    }
949
    }
950
    // prepare space for LIF-pointers (2m Pixel)
950
    // prepare space for LIF-pointers (2m Pixel)
951
    QVector<float*> lif_ptrs;
951
    QVector<float*> lif_ptrs;
952
    lif_ptrs.reserve(indices.size() * cPxPerHeight * cPxPerHeight);
952
    lif_ptrs.reserve(indices.size() * cPxPerHeight * cPxPerHeight);
953
    for (int l=0;l<indices.size();++l){
953
    for (int l=0;l<indices.size();++l){
954
        QPoint offset=stand_grid->grid().indexOf(indices[l]);
954
        QPoint offset=stand_grid->grid().indexOf(indices[l]);
955
        offset = offset * cPxPerHeight; // index of 10m patch -> to lif pixel coordinates
955
        offset = offset * cPxPerHeight; // index of 10m patch -> to lif pixel coordinates
956
        for (int y=0;y<cPxPerHeight;++y)
956
        for (int y=0;y<cPxPerHeight;++y)
957
            for(int x=0;x<cPxPerHeight;++x)
957
            for(int x=0;x<cPxPerHeight;++x)
958
                lif_ptrs.push_back( GlobalSettings::instance()->model()->grid()->ptr(offset.x()+x, offset.y()+y) );
958
                lif_ptrs.push_back( GlobalSettings::instance()->model()->grid()->ptr(offset.x()+x, offset.y()+y) );
959
    }
959
    }
960
    // sort based on LIF-Value
960
    // sort based on LIF-Value
961
    std::sort(lif_ptrs.begin(), lif_ptrs.end(), LIFValueHigher); // higher: highest values first
961
    std::sort(lif_ptrs.begin(), lif_ptrs.end(), LIFValueHigher); // higher: highest values first
962
962
963
963
964
    double area_factor = stand_grid->area(stand_id) / cRUArea; // multiplier for grid (e.g. 2 if stand has area of 2 hectare)
964
    double area_factor = stand_grid->area(stand_id) / cRUArea; // multiplier for grid (e.g. 2 if stand has area of 2 hectare)
965
965
966
    // parse the content of the init-file
966
    // parse the content of the init-file
967
    // species
967
    // species
968
    int ispecies = init.columnIndex("species");
968
    int ispecies = init.columnIndex("species");
969
    int icount = init.columnIndex("count");
969
    int icount = init.columnIndex("count");
970
    int iheight = init.columnIndex("height");
970
    int iheight = init.columnIndex("height");
971
    int iheightfrom = init.columnIndex("height_from");
971
    int iheightfrom = init.columnIndex("height_from");
972
    int iheightto = init.columnIndex("height_to");
972
    int iheightto = init.columnIndex("height_to");
973
    int iage = init.columnIndex("age");
973
    int iage = init.columnIndex("age");
974
    int itopage = init.columnIndex("age4m");
974
    int itopage = init.columnIndex("age4m");
975
    int iminlif = init.columnIndex("min_lif");
975
    int iminlif = init.columnIndex("min_lif");
976
    if ((iheightfrom==-1) ^ (iheightto==-1))
976
    if ((iheightfrom==-1) ^ (iheightto==-1))
977
        throw IException("Error while loading saplings: height not correctly provided. Use either 'height' or 'height_from' and 'height_to'.");
977
        throw IException("Error while loading saplings: height not correctly provided. Use either 'height' or 'height_from' and 'height_to'.");
978
    if (ispecies==-1 || icount==-1)
978
    if (ispecies==-1 || icount==-1)
979
        throw IException("Error while loading saplings: columns 'species' or 'count' are missing!!");
979
        throw IException("Error while loading saplings: columns 'species' or 'count' are missing!!");
980
980
981
    const SpeciesSet *set = GlobalSettings::instance()->model()->ru()->speciesSet();
981
    const SpeciesSet *set = GlobalSettings::instance()->model()->ru()->speciesSet();
982
    double height, age;
982
    double height, age;
983
    int total = 0;
983
    int total = 0;
984
    for (int row=low_index;row<=high_index;++row) {
984
    for (int row=low_index;row<=high_index;++row) {
985
        double pxcount = init.value(row, icount).toDouble() * area_factor; // no. of pixels that should be filled (sapling grid is the same resolution as the lif-grid)
985
        double pxcount = init.value(row, icount).toDouble() * area_factor; // no. of pixels that should be filled (sapling grid is the same resolution as the lif-grid)
986
        const Species *species = set->species(init.value(row, ispecies).toString());
986
        const Species *species = set->species(init.value(row, ispecies).toString());
987
        if (!species)
987
        if (!species)
988
            throw IException(QString("Error while loading saplings: invalid species '%1'.").arg(init.value(row, ispecies).toString()));
988
            throw IException(QString("Error while loading saplings: invalid species '%1'.").arg(init.value(row, ispecies).toString()));
989
        height = iheight==-1?0.05: init.value(row, iheight).toDouble();
989
        height = iheight==-1?0.05: init.value(row, iheight).toDouble();
990
        age = iage==-1?1:init.value(row,iage).toDouble();
990
        age = iage==-1?1:init.value(row,iage).toDouble();
991
        double age4m = itopage==-1?10:init.value(row, itopage).toDouble();
991
        double age4m = itopage==-1?10:init.value(row, itopage).toDouble();
992
        double height_from = iheightfrom==-1?-1.: init.value(row, iheightfrom).toDouble();
992
        double height_from = iheightfrom==-1?-1.: init.value(row, iheightfrom).toDouble();
993
        double height_to = iheightto==-1?-1.: init.value(row, iheightto).toDouble();
993
        double height_to = iheightto==-1?-1.: init.value(row, iheightto).toDouble();
994
        double min_lif = iminlif==-1?1.: init.value(row, iminlif).toDouble();
994
        double min_lif = iminlif==-1?1.: init.value(row, iminlif).toDouble();
995
        // find LIF-level in the pixels
995
        // find LIF-level in the pixels
996
        int min_lif_index = 0;
996
        int min_lif_index = 0;
997
        if (min_lif < 1.) {
997
        if (min_lif < 1.) {
998
            for (QVector<float*>::ConstIterator it=lif_ptrs.constBegin(); it!=lif_ptrs.constEnd(); ++it, ++min_lif_index)
998
            for (QVector<float*>::ConstIterator it=lif_ptrs.constBegin(); it!=lif_ptrs.constEnd(); ++it, ++min_lif_index)
999
                if (**it <= min_lif)
999
                if (**it <= min_lif)
1000
                    break;
1000
                    break;
1001
            if (pxcount < min_lif_index) {
1001
            if (pxcount < min_lif_index) {
1002
                // not enough LIF pixels available
1002
                // not enough LIF pixels available
1003
                min_lif_index = static_cast<int>(pxcount); // try the brightest pixels (ie with the largest value for the LIF)
1003
                min_lif_index = static_cast<int>(pxcount); // try the brightest pixels (ie with the largest value for the LIF)
1004
            }
1004
            }
1005
        } else {
1005
        } else {
1006
            // No LIF threshold: the full range of pixels is valid
1006
            // No LIF threshold: the full range of pixels is valid
1007
            min_lif_index = lif_ptrs.size();
1007
            min_lif_index = lif_ptrs.size();
1008
        }
1008
        }
1009
1009
1010
1010
1011
1011
1012
        double hits = 0.;
1012
        double hits = 0.;
1013
        while (hits < pxcount) {
1013
        while (hits < pxcount) {
1014
            int rnd_index = irandom(0, min_lif_index-1);
-
 
-
 
1014
            int rnd_index = irandom(0, min_lif_index);
1015
            if (iheightfrom!=-1) {
1015
            if (iheightfrom!=-1) {
1016
                height = limit(nrandom(height_from, height_to), 0.05,4.);
1016
                height = limit(nrandom(height_from, height_to), 0.05,4.);
1017
                if (age<=1.)
1017
                if (age<=1.)
1018
                    age = qMax(qRound(height/4. * age4m),1); // assume a linear relationship between height and age
1018
                    age = qMax(qRound(height/4. * age4m),1); // assume a linear relationship between height and age
1019
            }
1019
            }
1020
            QPoint offset = GlobalSettings::instance()->model()->grid()->indexOf(lif_ptrs[rnd_index]);
1020
            QPoint offset = GlobalSettings::instance()->model()->grid()->indexOf(lif_ptrs[rnd_index]);
1021
            ResourceUnit *ru;
1021
            ResourceUnit *ru;
1022
            SaplingCell *sc = GlobalSettings::instance()->model()->saplings()->cell(offset, true, &ru);
1022
            SaplingCell *sc = GlobalSettings::instance()->model()->saplings()->cell(offset, true, &ru);
1023
            if (sc) {
1023
            if (sc) {
1024
                if (SaplingTree *st=sc->addSapling(static_cast<float>(height), static_cast<int>(age), species->index()))
1024
                if (SaplingTree *st=sc->addSapling(static_cast<float>(height), static_cast<int>(age), species->index()))
1025
                    hits+=ru->resourceUnitSpecies(st->species_index)->species()->saplingGrowthParameters().representedStemNumberByHeight(st->height);
-
 
-
 
1025
                    hits+=std::max(1., ru->resourceUnitSpecies(st->species_index)->species()->saplingGrowthParameters().representedStemNumberByHeight(st->height));
-
 
1026
                else
-
 
1027
                    hits++;
1026
            } else {
1028
            } else {
1027
                hits++; // avoid an infinite loop
1029
                hits++; // avoid an infinite loop
1028
            }
1030
            }
1029
1031
1030
1032
1031
        }
1033
        }
1032
        total += pxcount;
1034
        total += pxcount;
1033
1035
1034
    }
1036
    }
1035
1037
1036
    // initialize grass cover
1038
    // initialize grass cover
1037
    if (init.columnIndex("grass_cover")>-1) {
1039
    if (init.columnIndex("grass_cover")>-1) {
1038
        int grass_cover_value = init.value(low_index, "grass_cover").toInt();
1040
        int grass_cover_value = init.value(low_index, "grass_cover").toInt();
1039
        if (grass_cover_value<0 || grass_cover_value>100)
1041
        if (grass_cover_value<0 || grass_cover_value>100)
1040
            throw IException(QString("The grass cover percentage (column 'grass_cover') for stand '%1' is '%2', which is invalid (expected: 0-100)").arg(stand_id).arg(grass_cover_value));
1042
            throw IException(QString("The grass cover percentage (column 'grass_cover') for stand '%1' is '%2', which is invalid (expected: 0-100)").arg(stand_id).arg(grass_cover_value));
1041
        GlobalSettings::instance()->model()->grassCover()->setInitialValues(lif_ptrs, grass_cover_value);
1043
        GlobalSettings::instance()->model()->grassCover()->setInitialValues(lif_ptrs, grass_cover_value);
1042
    }
1044
    }
1043
1045
1044
1046
1045
    return total;
1047
    return total;
1046
}
1048
}
1047
 
1049