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