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