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
 
33 Werner 20
#include "stampcontainer.h"
102 Werner 21
#include "globalsettings.h"
103 Werner 22
#include "exception.h"
33 Werner 23
 
24
//constants
25
const int StampContainer::cBHDclassWidth=4;
632 werner 26
const int StampContainer::cBHDclassLow = 4; ///< bhd classes start with 4cm
357 werner 27
const int StampContainer::cBHDclassCount = 70; ///< class count, see getKey(): for lower dbhs classes are smaller
33 Werner 28
const int StampContainer::cHDclassWidth=10;
61 Werner 29
const int StampContainer::cHDclassLow = 35; ///< hd classes offset is 35: class 0 = 35-45, class 1 = 45-55
362 werner 30
const int StampContainer::cHDclassCount = 16; ///< class count. highest class:  185-195
33 Werner 31
 
401 werner 32
// static values
33
Grid<float> StampContainer::m_distance;
33 Werner 34
 
35
StampContainer::StampContainer()
36
{
37
    //
38
    m_lookup.setup(1., // cellsize
39
                   cBHDclassCount, // count x
40
                   cHDclassCount); // count y
41
    m_lookup.initialize(NULL);
37 Werner 42
    //qDebug() << "grid after init" << gridToString(m_lookup);
33 Werner 43
    m_maxBhd = -1;
44
    m_useLookup = true;
45
}
46
 
47
StampContainer::~StampContainer()
48
{
49
    // delete stamps.
50
    while (!m_stamps.isEmpty())
51
        delete m_stamps.takeLast().stamp;
52
 
53
}
64 Werner 54
/// getKey: decodes a floating point piar of dbh and hd-ratio to indices for the
55
/// lookup table containing pointers to the actual stamps.
632 werner 56
inline void StampContainer::getKey(const float dbh, const float hd_value, int &dbh_class, int &hd_class) const
33 Werner 57
{
64 Werner 58
    hd_class = int(hd_value - cHDclassLow) / cHDclassWidth;
632 werner 59
    // dbh_class = int(dbh - cBHDclassLow) / cBHDclassWidth;
64 Werner 60
    // fixed scheme: smallest classification scheme for tree-diameters:
61
    // 1cm width from 4 up to 9cm,
62
    // 2cm bins from 10 to 18cm
1102 werner 63
    // 4cm bins starting from 20cm, max DBH=255 (with 70 classes)
64 Werner 64
    if (dbh < 10.f) {
65
        dbh_class = qMax(0, int(dbh-4.)); // classes from 0..5
66
    } else if (dbh<20.f) {
67
        dbh_class = 6 + int((dbh-10.f) / 2.f); // 10-12cm has index 6
68
    } else {
69
        dbh_class = 11 + int((dbh-20.f) / 4.f); // 20-24cm has index 11
70
    }
33 Werner 71
}
72
 
58 Werner 73
/** fill up the NULLs in the lookup map */
74
void StampContainer::finalizeSetup()
75
{
76
    if (!m_useLookup)
77
        return;
78
    Stamp *s;
79
    int h;
401 werner 80
    int max_size=0;
58 Werner 81
    for (int b=0;b<cBHDclassCount;b++) {
82
        // find lowest value...
83
        for (h=0;h<cHDclassCount;h++) {
84
            s=m_lookup.valueAtIndex(b,h);
85
            if (s) {
86
                // fill up values left from this value
87
                for (int hfill=0;hfill<h;hfill++)
361 werner 88
                    m_lookup.valueAtIndex(b,hfill) = s;
58 Werner 89
                break;
90
            }
91
        }
92
        // go to last filled cell...
93
        for (;h<cHDclassCount;h++) {
94
            if (m_lookup.valueAtIndex(b,h)==0)
95
                break;
96
            s=m_lookup.valueAtIndex(b,h);
97
        }
98
        // fill up the rest...
99
        for (;h<cHDclassCount;h++) {
100
            m_lookup.valueAtIndex(b,h)=s;
101
        }
401 werner 102
        if(s)
103
            max_size = std::max(max_size, s->dataSize());
498 werner 104
 
105
        // if no stamps in this dbh-class, copy values (from last row)
106
        if (!s && b>0) {
107
           for (h=0;h<cHDclassCount;h++)
108
               m_lookup.valueAtIndex(b,h) = m_lookup(b-1, h);
109
        }
58 Werner 110
    }
505 werner 111
    if (!m_lookup.valueAtIndex(0,0)) {
112
        // first values are missing
113
        int b=0;
114
        while (b<cBHDclassCount && m_lookup.valueAtIndex(b,0)==NULL)
115
            b++;
116
        for (int fill=0;fill<b;fill++)
117
            for (h=0;h<cHDclassCount;h++)
118
                m_lookup.valueAtIndex(fill, h) = m_lookup.valueAtIndex(b,h);
119
    }
401 werner 120
    // distance grid
121
    if (m_distance.sizeX()<max_size) {
122
        setupDistanceGrid(max_size);
123
    }
58 Werner 124
}
125
 
401 werner 126
void StampContainer::setupDistanceGrid(const int size)
127
{
128
    const float px_size = cPxSize;
129
    m_distance.setup(px_size, size, size);
130
    float *p=m_distance.begin();
131
    QPoint idx;
132
    for (;p!=m_distance.end();++p) {
133
        idx = m_distance.indexOf(p);
134
        *p = sqrt(double(idx.x()*idx.x()) + double(idx.y()*idx.y()))*px_size;
135
    }
136
}
64 Werner 137
 
138
 void StampContainer::addStamp(Stamp* stamp, const int cls_dbh, const int cls_hd, const float crown_radius_m, const float dbh, const float hd_value)
139
 {
33 Werner 140
    if (m_useLookup) {
357 werner 141
        if (cls_dbh<0 || cls_dbh>=cBHDclassCount || cls_hd<0 || cls_hd>=cHDclassCount)
142
            throw IException(QString("StampContainer::addStamp: Stamp out of range. dbh=%1 hd=%2.").arg(dbh).arg(hd_value));
64 Werner 143
        m_lookup.valueAtIndex(cls_dbh, cls_hd) = stamp; // save address in look up table
33 Werner 144
    } // if (useLookup)
430 werner 145
    stamp->setCrownRadius(crown_radius_m);
33 Werner 146
    StampItem si;
64 Werner 147
    si.dbh = dbh;
33 Werner 148
    si.hd = hd_value;
47 Werner 149
    si.crown_radius = crown_radius_m;
33 Werner 150
    si.stamp = stamp;
151
    m_stamps.append(si); // store entry in list of stamps
152
 
64 Werner 153
 }
154
 
155
 
156
/** add a stamp to the internal storage.
157
    After loading the function finalizeSetup() must be called to ensure that gaps in the matrix get filled. */
65 Werner 158
void  StampContainer::addStamp(Stamp* stamp, const float dbh, const float hd_value, const float crown_radius)
64 Werner 159
{
160
    int cls_dbh, cls_hd;
161
    getKey(dbh, hd_value, cls_dbh, cls_hd); // decode dbh/hd-value
65 Werner 162
    addStamp(stamp, cls_dbh, cls_hd, crown_radius, dbh, hd_value); // dont set crownradius
33 Werner 163
}
164
 
64 Werner 165
void StampContainer::addReaderStamp(Stamp *stamp, const float crown_radius_m)
40 Werner 166
{
781 werner 167
    double rest = fmod(crown_radius_m, 1.f)+0.0001;
397 werner 168
    int cls_hd = int( rest * 10 ); // 0 .. 9.99999999
40 Werner 169
    if (cls_hd>=cHDclassCount)
170
        cls_hd=cHDclassCount-1;
64 Werner 171
    int cls_dbh = int(crown_radius_m);
102 Werner 172
    //qDebug() << "Readerstamp r="<< crown_radius_m<<" index dbh hd:" << cls_dbh << cls_hd;
397 werner 173
    stamp->setCrownRadius(crown_radius_m);
174
 
64 Werner 175
    // prepare special keys for reader stamps
176
    addStamp(stamp,cls_dbh, cls_hd, crown_radius_m, 0., 0.); // set crownradius, but not dbh/hd
177
}
40 Werner 178
 
179
 
180
/** retrieve a read-out-stamp. Readers depend solely on a crown radius.
181
Internally, readers are stored in the same lookup-table, but using a encoding/decoding trick.*/
182
const Stamp* StampContainer::readerStamp(const float crown_radius_m) const
183
{
184
    // Readers: from 0..10m in 50 steps???
781 werner 185
    int cls_hd = int( (fmod(crown_radius_m, 1.f)+0.0001) * 10 ); // 0 .. 9.99999999
40 Werner 186
    if (cls_hd>=cHDclassCount)
187
        cls_hd=cHDclassCount-1;
188
    int cls_bhd = int(crown_radius_m);
189
    const Stamp* stamp = m_lookup(cls_bhd, cls_hd);
58 Werner 190
    if (!stamp)
191
        qDebug() << "Stamp::readerStamp(): no stamp found for radius" << crown_radius_m;
40 Werner 192
    return stamp;
193
}
194
 
33 Werner 195
/** fast access for an individual stamp using a lookup table.
196
    the dimensions of the lookup table are defined by class-constants.
197
    If stamp is not found there, the more complete list of stamps is searched. */
39 Werner 198
const Stamp* StampContainer::stamp(const float bhd_cm, const float height_m) const
33 Werner 199
{
200
 
40 Werner 201
    float hd_value = 100.f * height_m / bhd_cm;
35 Werner 202
 
498 werner 203
    int cls_dbh, cls_hd;
204
    getKey(bhd_cm, hd_value, cls_dbh, cls_hd);
35 Werner 205
 
33 Werner 206
    // check loopup table
498 werner 207
    if (cls_dbh<cBHDclassCount && cls_dbh>=0 && cls_hd < cHDclassCount && cls_hd>=0) {
208
        const Stamp* stamp = m_lookup(cls_dbh, cls_hd);
40 Werner 209
        if (stamp)
210
            return stamp;
498 werner 211
        if (logLevelDebug())
212
            qDebug() << "StampContainer::stamp(): not in list: dbh height:" << bhd_cm << height_m << "in" << m_fileName;
40 Werner 213
    }
33 Werner 214
 
215
    // extra work: search in list...
498 werner 216
    // look for a stamp if the HD-ratio is out of range
217
    if (cls_dbh<cBHDclassCount && cls_dbh>=0) {
218
        if (logLevelDebug())
219
            qDebug() << "HD for stamp out of range dbh " << bhd_cm << "and h="<< height_m << "(using smallest/largeset HD)";
143 Werner 220
        if (cls_hd>=cHDclassCount)
498 werner 221
            return m_lookup(cls_dbh, cHDclassCount-1); // highest
222
        return m_lookup(cls_dbh, 0); // smallest
143 Werner 223
    }
498 werner 224
    // look for a stamp if the DBH is out of range.
225
    if (cls_hd<cHDclassCount && cls_hd>=0) {
226
        if (logLevelDebug())
227
            qDebug() << "DBH for stamp out of range dbh " << bhd_cm << "and h="<< height_m << "-> using largest available DBH.";
228
        if (cls_dbh>=cBHDclassCount)
229
            return m_lookup(cBHDclassCount-1, cls_hd); // highest
230
        return m_lookup(0, cls_hd); // smallest
33 Werner 231
 
498 werner 232
    }
632 werner 233
    // handle the case DBH and HD are out of range
234
    if (cls_dbh>=cBHDclassCount && cls_hd<0) {
235
        if (logLevelDebug())
236
            qDebug() << "DBH AND HD for stamp out of range dbh " << bhd_cm << "and h="<< height_m << "-> using largest available DBH/smallest HD.";
237
        return m_lookup(cBHDclassCount-1, 0);
238
    }
239
    // handle the case that DBH is too high and HD is too high (not very likely)
240
    if (cls_dbh>=cBHDclassCount && cls_hd>=cHDclassCount) {
241
        if (logLevelDebug())
242
            qDebug() << "DBH AND HD for stamp out of range dbh " << bhd_cm << "and h="<< height_m << "-> using largest available DBH.";
243
        return m_lookup(cBHDclassCount-1, cHDclassCount-1);
244
 
245
    }
498 werner 246
    qDebug() << "ERROR: No stamp defined for dbh " << bhd_cm << "and h="<< height_m;
247
    throw IException("StampContainer:: did not find a valid stamp.");
33 Werner 248
}
249
 
63 Werner 250
 
47 Werner 251
void StampContainer::attachReaderStamps(const StampContainer &source)
252
{
253
    int found=0, total=0;
802 werner 254
    foreach (const StampItem &si, m_stamps) {
47 Werner 255
        const Stamp *s = source.readerStamp(si.crown_radius);
256
        si.stamp->setReader(const_cast<Stamp*>(s));
257
        if (s) found++;
258
        total++;
259
        //si.crown_radius
260
    }
550 werner 261
    if (logLevelInfo()) qDebug() << "attachReaderStamps: found" << found << "stamps of" << total;
47 Werner 262
}
263
 
51 Werner 264
void StampContainer::invert()
265
{
266
    StampItem  si;
267
    foreach(si, m_stamps) {
268
        Stamp *s =si.stamp;
269
        float *p = s->data();
270
        while (p!=s->end()) {
271
            *p = 1. - *p;
272
            p++;
273
        }
274
    }
275
}
99 Werner 276
/// convenience function that loads stamps directly from a single file.
277
void StampContainer::load(const QString &fileName)
278
{
279
    QFile readerfile(fileName);
102 Werner 280
    if (!readerfile.exists())
281
        throw IException(QString("The LIP stampfile %1 cannot be found!").arg(fileName));
429 werner 282
    m_fileName = fileName;
99 Werner 283
    readerfile.open(QIODevice::ReadOnly);
284
    QDataStream rin(&readerfile);
361 werner 285
    qDebug() << "loading stamp file" << fileName;
99 Werner 286
    load(rin);
287
    readerfile.close();
288
}
51 Werner 289
 
33 Werner 290
void StampContainer::load(QDataStream &in)
291
{
292
    qint32 type;
293
    qint32 count;
802 werner 294
    float dbh, hdvalue, crownradius;
400 werner 295
    quint32 magic;
296
    in >> magic;
297
    if (magic!=0xFEED0001)
298
        throw IException("StampContainer: invalid file type!");
299
    quint16 version;
300
    in >> version;
301
    if (version != 100)
302
        throw IException(QString("StampContainer: invalid file version: %1").arg(version));
303
    in.setVersion(QDataStream::Qt_4_5);
304
 
33 Werner 305
    in >> count; // read count of stamps
550 werner 306
    if (logLevelInfo()) qDebug() << count << "stamps to read";
63 Werner 307
    QString desc;
308
    in >> desc; // read textual description of stamp
550 werner 309
    if (logLevelInfo()) qDebug() << "Stamp notes:" << desc;
63 Werner 310
    m_desc = desc;
33 Werner 311
    for (int i=0;i<count;i++) {
312
        in >> type; // read type
802 werner 313
        in >> dbh;
33 Werner 314
        in >> hdvalue;
47 Werner 315
        in >> crownradius;
309 werner 316
        //qDebug() << "stamp bhd hdvalue type readsum dominance type" << bhd << hdvalue << type << readsum << domvalue << type;
64 Werner 317
 
735 werner 318
        Stamp *stamp = new Stamp(type);
33 Werner 319
        stamp->load(in);
400 werner 320
 
802 werner 321
        if (dbh > 0.f)
322
            addStamp(stamp, dbh, hdvalue, crownradius);
65 Werner 323
        else
64 Werner 324
            addReaderStamp(stamp, crownradius);
33 Werner 325
    }
58 Werner 326
    finalizeSetup(); // fill up lookup grid
102 Werner 327
    if (count==0)
328
        throw IException("no stamps loaded!");
33 Werner 329
}
330
/** Saves all stamps of the container to a binary stream.
34 Werner 331
  Format: * count of stamps (int32)
63 Werner 332
          * a string containing a description (free text) (QString)
33 Werner 333
      for each stamp:
334
      - type (enum Stamp::StampType, 4, 8, 12, 16, ...)
335
      - bhd of the stamp (float)
42 Werner 336
      - hd-value of the tree (float)
47 Werner 337
      - crownradius of the stamp (float) in [m]
43 Werner 338
      - the sum of values in the center of the stamp (used for read out)
339
      - the dominance value of the stamp
33 Werner 340
      - individual data values (Stamp::save() / Stamp::load())
341
      -- offset (int) no. of pixels away from center
342
      -- list of data items (type*type items)
34 Werner 343
      see also stamp creation (FonStudio application, MainWindow.cpp).
33 Werner 344
*/
345
void StampContainer::save(QDataStream &out)
346
{
347
    qint32 type;
34 Werner 348
    qint32 size = m_stamps.count();
400 werner 349
    out << (quint32)0xFEED0001; // magic number
350
    out << (quint16)100; // version
351
    out.setVersion(QDataStream::Qt_4_5);
352
 
63 Werner 353
    out << size; // count of stamps...
354
    out << m_desc; // text...
33 Werner 355
    foreach(StampItem si, m_stamps) {
40 Werner 356
        type = si.stamp->dataSize();
35 Werner 357
        out << type;
64 Werner 358
        out << si.dbh;
35 Werner 359
        out << si.hd;
47 Werner 360
        out << si.crown_radius;
34 Werner 361
        si.stamp->save(out);
33 Werner 362
    }
34 Werner 363
 
33 Werner 364
}
35 Werner 365
 
366
QString StampContainer::dump()
367
{
368
    QString res;
369
    QString line;
370
    int x,y;
371
    int maxidx;
429 werner 372
    res = QString("****** Dump of StampContainer %1 **********\r\n").arg(m_fileName);
35 Werner 373
    foreach (StampItem si, m_stamps) {
374
        line = QString("%5 -> size: %1 offset: %2 dbh: %3 hd-ratio: %4\r\n")
781 werner 375
               .arg(sqrt((double)si.stamp->count())).arg(si.stamp->offset())
835 werner 376
               .arg(si.dbh).arg(si.hd).arg((ptrdiff_t)si.stamp, 0, 16);
35 Werner 377
        // add data....
378
        maxidx = 2*si.stamp->offset() + 1;
41 Werner 379
        for (y=0;y<maxidx;++y)  {
380
            for (x=0;x<maxidx;++x)  {
35 Werner 381
                line+= QString::number(*si.stamp->data(x,y)) + " ";
382
            }
383
            line+="\r\n";
384
        }
385
        line+="==============================================\r\n";
386
        res+=line;
387
    }
36 Werner 388
    res+= "Dump of lookup map\r\n=====================\r\n";
35 Werner 389
    for (Stamp **s = m_lookup.begin(); s!=m_lookup.end(); ++s) {
390
        if (*s)
835 werner 391
         res += QString("P: x/y: %1/%2 addr %3\r\n").arg( m_lookup.indexOf(s).x()).arg(m_lookup.indexOf(s).y()).arg((ptrdiff_t)*s, 0, 16);
35 Werner 392
    }
36 Werner 393
    res+="\r\n" + gridToString(m_lookup);
35 Werner 394
    return res;
395
}