Rev 1221 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
671 | werner | 1 | /******************************************************************************************** |
2 | ** iLand - an individual based forest landscape and disturbance model |
||
3 | ** http://iland.boku.ac.at |
||
4 | ** Copyright (C) 2009- Werner Rammer, Rupert Seidl |
||
5 | ** |
||
6 | ** This program is free software: you can redistribute it and/or modify |
||
7 | ** it under the terms of the GNU General Public License as published by |
||
8 | ** the Free Software Foundation, either version 3 of the License, or |
||
9 | ** (at your option) any later version. |
||
10 | ** |
||
11 | ** This program is distributed in the hope that it will be useful, |
||
12 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
13 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
14 | ** GNU General Public License for more details. |
||
15 | ** |
||
16 | ** You should have received a copy of the GNU General Public License |
||
17 | ** along with this program. If not, see <http://www.gnu.org/licenses/>. |
||
18 | ********************************************************************************************/ |
||
19 | |||
543 | werner | 20 | #include "mapgrid.h" |
21 | #include "globalsettings.h" |
||
22 | #include "model.h" |
||
544 | werner | 23 | #include "resourceunit.h" |
24 | #include "tree.h" |
||
549 | werner | 25 | #include "grid.h" |
564 | werner | 26 | #include "resourceunit.h" |
884 | werner | 27 | #include "expressionwrapper.h" |
544 | werner | 28 | /** MapGrid encapsulates maps that classify the area in 10m resolution (e.g. for stand-types, management-plans, ...) |
732 | werner | 29 | @ingroup tools |
544 | werner | 30 | The grid is (currently) loaded from disk in a ESRI style text file format. See also the "location" keys and GisTransformation classes for |
31 | details on how the grid is mapped to the local coordinate system of the project area. From the source grid a 10m grid |
||
32 | using the extent and position of the "HeightGrid" and spatial indices for faster access are generated. |
||
550 | werner | 33 | The grid is clipped to the extent of the simulation area and -1 is used for no_data_values. |
544 | werner | 34 | Use boundingBox(), resourceUnits(), trees() to retrieve information for specific 'ids'. gridValue() retrieves the 'id' for a given |
35 | location (in LIF-coordinates). |
||
543 | werner | 36 | |
37 | */ |
||
932 | werner | 38 | |
39 | /// MapGridRULock is a custom class to serialize (write) access to (the trees of a) resource units. |
||
40 | /// |
||
41 | class MapGridRULock { |
||
42 | public: |
||
43 | void lock(const int id, QList<ResourceUnit*> &elements); |
||
44 | void unlock(const int id); |
||
45 | private: |
||
46 | QHash<ResourceUnit*, int> mLockedElements; |
||
47 | QMutex mLock; |
||
48 | QWaitCondition mWC; |
||
49 | }; |
||
50 | static MapGridRULock mapGridLock; |
||
51 | |||
52 | void MapGridRULock::lock(const int id, QList<ResourceUnit *> &elements) |
||
53 | { |
||
54 | // check if one of the elements is already in the LockedElements-list |
||
55 | bool ok; |
||
56 | do { |
||
57 | ok = true; |
||
58 | mLock.lock(); |
||
59 | for (int i=0;i<elements.size();++i) |
||
1032 | werner | 60 | if (mLockedElements.contains(elements[i])) { |
932 | werner | 61 | if (mLockedElements[elements[i]] != id){ |
62 | qDebug() << "MapGridRULock: must wait (" << QThread::currentThread() << id << "). stand with lock: " << mLockedElements[elements[i]] << ".Lock list length" << mLockedElements.size(); |
||
63 | |||
64 | // we have to wait until |
||
65 | mWC.wait(&mLock); |
||
66 | ok = false; |
||
67 | } else { |
||
68 | // this resource unit is already locked for the same stand-id, therefore do nothing |
||
69 | // qDebug() << "MapGridRULock: already locked for (" << QThread::currentThread() << ", stand "<< id <<"). Lock list length" << mLockedElements.size(); |
||
70 | mLock.unlock(); |
||
71 | return; |
||
72 | } |
||
1032 | werner | 73 | } |
932 | werner | 74 | mLock.unlock(); |
75 | } while (!ok); |
||
76 | |||
77 | // now add the elements |
||
78 | mLock.lock(); |
||
79 | for (int i=0;i<elements.size();++i) |
||
80 | mLockedElements[elements[i]] = id; |
||
81 | //qDebug() << "MapGridRULock: created lock " << QThread::currentThread() << " for stand" << id << ". lock list length" << mLockedElements.size(); |
||
82 | |||
83 | mLock.unlock(); |
||
84 | } |
||
85 | |||
86 | void MapGridRULock::unlock(const int id) |
||
87 | { |
||
88 | QMutexLocker locker(&mLock); // protect changing the list |
||
89 | QMutableHashIterator<ResourceUnit*, int> i(mLockedElements); |
||
90 | bool found = false; |
||
91 | while (i.hasNext()) { |
||
92 | i.next(); |
||
93 | if (i.value() == id) { |
||
94 | i.remove(); |
||
95 | found = true; |
||
96 | } |
||
97 | } |
||
98 | |||
99 | // notify all waiting threads that now something changed.... |
||
100 | if (found) { |
||
101 | //qDebug() << "MapGridRULock: free" << QThread::currentThread() << "for stand " << id << "lock list length" << mLockedElements.size(); |
||
102 | mWC.wakeAll(); |
||
103 | } |
||
104 | |||
105 | } |
||
106 | |||
107 | |||
108 | |||
543 | werner | 109 | MapGrid::MapGrid() |
110 | { |
||
111 | } |
||
112 | |||
732 | werner | 113 | bool MapGrid::loadFromGrid(const GisGrid &source_grid, const bool create_index) |
543 | werner | 114 | { |
115 | if (!GlobalSettings::instance()->model()) |
||
116 | throw IException("GisGrid::create10mGrid: no valid model to retrieve height grid."); |
||
117 | |||
118 | HeightGrid *h_grid = GlobalSettings::instance()->model()->heightGrid(); |
||
119 | if (!h_grid || h_grid->isEmpty()) |
||
120 | throw IException("GisGrid::create10mGrid: no valid height grid to copy grid size."); |
||
121 | // create a grid with the same size as the height grid |
||
122 | // (height-grid: 10m size, covering the full extent) |
||
123 | mGrid.clear(); |
||
124 | mGrid.setup(h_grid->metricRect(),h_grid->cellsize()); |
||
125 | |||
550 | werner | 126 | const QRectF &world = GlobalSettings::instance()->model()->extent(); |
127 | QPointF p; |
||
128 | for (int i=0;i<mGrid.count();i++) { |
||
129 | p = mGrid.cellCenterPoint(mGrid.indexOf(i)); |
||
130 | if (source_grid.value(p) != source_grid.noDataValue() && world.contains(p) ) |
||
131 | mGrid.valueAtIndex(i) = source_grid.value(p); |
||
132 | else |
||
133 | mGrid.valueAtIndex(i) = -1; |
||
134 | } |
||
543 | werner | 135 | |
136 | // create spatial index |
||
137 | mRectIndex.clear(); |
||
138 | mRUIndex.clear(); |
||
139 | |||
848 | werner | 140 | if (create_index) |
141 | createIndex(); |
||
565 | werner | 142 | |
848 | werner | 143 | return true; |
565 | werner | 144 | |
848 | werner | 145 | } |
732 | werner | 146 | |
848 | werner | 147 | |
148 | void MapGrid::createEmptyGrid() |
||
149 | { |
||
150 | HeightGrid *h_grid = GlobalSettings::instance()->model()->heightGrid(); |
||
151 | if (!h_grid || h_grid->isEmpty()) |
||
152 | throw IException("GisGrid::createEmptyGrid: 10mGrid: no valid height grid to copy grid size."); |
||
153 | // create a grid with the same size as the height grid |
||
154 | // (height-grid: 10m size, covering the full extent) |
||
155 | mGrid.clear(); |
||
156 | mGrid.setup(h_grid->metricRect(),h_grid->cellsize()); |
||
157 | |||
158 | QPointF p; |
||
159 | for (int i=0;i<mGrid.count();i++) { |
||
160 | p = mGrid.cellCenterPoint(mGrid.indexOf(i)); |
||
161 | mGrid.valueAtIndex(i) = 0; |
||
162 | } |
||
163 | |||
164 | // reset spatial index |
||
165 | mRectIndex.clear(); |
||
166 | mRUIndex.clear(); |
||
167 | } |
||
168 | |||
169 | void MapGrid::createIndex() |
||
170 | { |
||
171 | // reset spatial index |
||
172 | mRectIndex.clear(); |
||
173 | mRUIndex.clear(); |
||
174 | // create new |
||
175 | for (int *p = mGrid.begin(); p!=mGrid.end(); ++p) { |
||
176 | if (*p==-1) |
||
177 | continue; |
||
178 | QPair<QRectF,double> &data = mRectIndex[*p]; |
||
179 | data.first = data.first.united(mGrid.cellRect(mGrid.indexOf(p))); |
||
180 | data.second += cPxSize*cPxPerHeight*cPxSize*cPxPerHeight; // 100m2 |
||
181 | |||
182 | ResourceUnit *ru = GlobalSettings::instance()->model()->ru(mGrid.cellCenterPoint(mGrid.indexOf(p))); |
||
882 | werner | 183 | if (!ru) |
184 | continue; |
||
848 | werner | 185 | // find all entries for the current grid id |
186 | QMultiHash<int, QPair<ResourceUnit*, double> >::iterator pos = mRUIndex.find(*p); |
||
187 | |||
188 | // look for the resource unit 'ru' |
||
189 | bool found = false; |
||
190 | while (pos!=mRUIndex.end() && pos.key() == *p) { |
||
191 | if (pos.value().first == ru) { |
||
192 | pos.value().second+= 0.01; // 1 pixel = 1% of the area |
||
193 | found=true; |
||
194 | break; |
||
565 | werner | 195 | } |
848 | werner | 196 | ++pos; |
565 | werner | 197 | } |
848 | werner | 198 | if (!found) |
199 | mRUIndex.insertMulti(*p, QPair<ResourceUnit*, double>(ru, 0.01)); |
||
543 | werner | 200 | } |
201 | |||
202 | } |
||
203 | |||
732 | werner | 204 | bool MapGrid::loadFromFile(const QString &fileName, const bool create_index) |
543 | werner | 205 | { |
206 | GisGrid gis_grid; |
||
575 | werner | 207 | mName = "invalid"; |
543 | werner | 208 | if (gis_grid.loadFromFile(fileName)) { |
575 | werner | 209 | mName = fileName; |
732 | werner | 210 | return loadFromGrid(gis_grid, create_index); |
543 | werner | 211 | } |
212 | return false; |
||
213 | } |
||
544 | werner | 214 | |
848 | werner | 215 | /// returns the list of resource units with at least one pixel within the area designated by 'id' |
565 | werner | 216 | QList<ResourceUnit *> MapGrid::resourceUnits(const int id) const |
217 | { |
||
218 | QList<ResourceUnit *> result; |
||
848 | werner | 219 | QList<QPair<ResourceUnit*, double> > list = mRUIndex.values(id); |
565 | werner | 220 | for (int i=0;i<list.count();++i) |
221 | result.append( list[i].first); |
||
222 | return result; |
||
223 | } |
||
224 | |||
578 | werner | 225 | /// return a list of all living trees on the area denoted by 'id' |
549 | werner | 226 | QList<Tree *> MapGrid::trees(const int id) const |
544 | werner | 227 | { |
228 | QList<Tree*> tree_list; |
||
229 | QList<ResourceUnit*> resource_units = resourceUnits(id); |
||
230 | foreach(ResourceUnit *ru, resource_units) { |
||
231 | foreach(const Tree &tree, ru->constTrees()) |
||
1203 | werner | 232 | if (standIDFromLIFCoord(tree.positionIndex()) == id && !tree.isDead()) { |
544 | werner | 233 | tree_list.append( & const_cast<Tree&>(tree) ); |
585 | werner | 234 | } |
544 | werner | 235 | } |
585 | werner | 236 | // qDebug() << "MapGrid::trees: found" << c << "/" << tree_list.size(); |
544 | werner | 237 | return tree_list; |
238 | |||
239 | } |
||
549 | werner | 240 | |
885 | werner | 241 | int MapGrid::loadTrees(const int id, QVector<QPair<Tree *, double> > &rList, const QString filter, int n_estimate) const |
884 | werner | 242 | { |
243 | rList.clear(); |
||
885 | werner | 244 | if (n_estimate>0) |
245 | rList.reserve(n_estimate); |
||
884 | werner | 246 | Expression *expression = 0; |
247 | TreeWrapper tw; |
||
248 | if (!filter.isEmpty()) { |
||
249 | expression = new Expression(filter, &tw); |
||
885 | werner | 250 | expression ->enableIncSum(); |
884 | werner | 251 | } |
252 | QList<ResourceUnit*> resource_units = resourceUnits(id); |
||
933 | werner | 253 | // lock the resource units: removed again, WR20140821 |
254 | // mapGridLock.lock(id, resource_units); |
||
932 | werner | 255 | |
884 | werner | 256 | foreach(ResourceUnit *ru, resource_units) { |
257 | foreach(const Tree &tree, ru->constTrees()) |
||
1203 | werner | 258 | if (standIDFromLIFCoord(tree.positionIndex()) == id && !tree.isDead()) { |
884 | werner | 259 | Tree *t = & const_cast<Tree&>(tree); |
260 | tw.setTree(t); |
||
261 | if (expression) { |
||
262 | double value = expression->calculate(tw); |
||
263 | // keep if expression returns true (1) |
||
264 | bool keep = value==1.; |
||
265 | // if value is >0 (i.e. not "false"), then draw a random number |
||
266 | if (!keep && value>0.) |
||
267 | keep = drandom() < value; |
||
268 | |||
269 | if (!keep) |
||
270 | continue; |
||
271 | } |
||
885 | werner | 272 | rList.push_back(QPair<Tree*, double>(t,0.)); |
884 | werner | 273 | } |
274 | } |
||
275 | if (expression) |
||
276 | delete expression; |
||
277 | return rList.size(); |
||
278 | |||
279 | } |
||
280 | |||
932 | werner | 281 | void MapGrid::freeLocksForStand(const int id) |
282 | { |
||
283 | if (id>-1) |
||
284 | mapGridLock.unlock(id); |
||
285 | } |
||
286 | |||
600 | werner | 287 | /// return a list of grid-indices of a given stand-id (a grid-index |
288 | /// is the index of 10m x 10m pixels within the internal storage) |
||
549 | werner | 289 | /// The selection is limited to pixels within the world's extent |
290 | QList<int> MapGrid::gridIndices(const int id) const |
||
291 | { |
||
292 | QList<int> result; |
||
293 | QRectF rect = mRectIndex[id].first; |
||
294 | GridRunner<int> run(mGrid, rect); |
||
295 | while (int *cell = run.next()) { |
||
550 | werner | 296 | if (*cell == id) |
549 | werner | 297 | result.push_back(cell - mGrid.begin()); |
298 | } |
||
299 | return result; |
||
300 | } |
||
564 | werner | 301 | |
912 | werner | 302 | /// retrieve a list of saplings on a given stand polygon. |
1162 | werner | 303 | //QList<QPair<ResourceUnitSpecies *, SaplingTreeOld *> > MapGrid::saplingTrees(const int id) const |
304 | //{ |
||
305 | // QList<QPair<ResourceUnitSpecies *, SaplingTreeOld *> > result; |
||
306 | // QList<ResourceUnit*> resource_units = resourceUnits(id); |
||
307 | // foreach(ResourceUnit *ru, resource_units) { |
||
308 | // foreach(ResourceUnitSpecies *rus, ru->ruSpecies()) { |
||
309 | // foreach(const SaplingTreeOld &tree, rus->sapling().saplings()) { |
||
310 | // if (LIFgridValue( tree.coords() ) == id) |
||
311 | // result.push_back( QPair<ResourceUnitSpecies *, SaplingTreeOld *>(rus, &const_cast<SaplingTreeOld&>(tree)) ); |
||
312 | // } |
||
313 | // } |
||
314 | // } |
||
315 | // qDebug() << "loaded" << result.count() << "sapling trees"; |
||
316 | // return result; |
||
564 | werner | 317 | |
1162 | werner | 318 | //} |
565 | werner | 319 | |
911 | werner | 320 | |
797 | werner | 321 | /// retrieve a list of all stands that are neighbors of the stand with ID "index". |
322 | QList<int> MapGrid::neighborsOf(const int index) const |
||
323 | { |
||
324 | if (mNeighborList.isEmpty()) |
||
914 | werner | 325 | const_cast<MapGrid*>(this)->updateNeighborList(); // fill the list |
797 | werner | 326 | return mNeighborList.values(index); |
327 | } |
||
565 | werner | 328 | |
903 | werner | 329 | |
797 | werner | 330 | /// scan the map and add neighborhood-relations to the mNeighborList |
331 | /// the 4-neighborhood is used to identify neighbors. |
||
914 | werner | 332 | void MapGrid::updateNeighborList() |
797 | werner | 333 | { |
334 | mNeighborList.clear(); |
||
803 | werner | 335 | GridRunner<int> gr(mGrid, mGrid.rectangle()); // the full grid |
797 | werner | 336 | int *n4[4]; |
337 | QHash<int,int>::iterator it_hash; |
||
338 | while (gr.next()) { |
||
803 | werner | 339 | gr.neighbors4(n4); // get the four-neighborhood (0-pointers possible) |
797 | werner | 340 | for (int i=0;i<4;++i) |
803 | werner | 341 | if (n4[i] && *gr.current() != *n4[i]) { |
797 | werner | 342 | // look if we already have the pair |
343 | it_hash = mNeighborList.find(*gr.current(), *n4[i]); |
||
344 | if (it_hash == mNeighborList.end()) { |
||
345 | // add the "edge" two times in the hash |
||
346 | mNeighborList.insertMulti(*gr.current(), *n4[i]); |
||
347 | mNeighborList.insertMulti(*n4[i], *gr.current()); |
||
348 | } |
||
349 | } |
||
350 | } |
||
351 | |||
352 | } |
||
353 | |||
354 | |||
848 | werner | 355 | |
356 | |||
932 | werner | 357 | |
358 |