Subversion Repositories public iLand

Rev

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