Subversion Repositories public iLand

Rev

Rev 445 | Rev 472 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1
 
373 werner 2
#include "seeddispersal.h"
3
 
4
#include "globalsettings.h"
5
#include "model.h"
6
#include "helper.h"
391 werner 7
#include "species.h"
373 werner 8
 
9
#include <QtGui/QImage>
10
 
11
/** @class SeedDispersal
12
    The class encapsulates the dispersal of seeds of one species over the whole landscape.
13
 
14
  */
15
SeedDispersal::~SeedDispersal()
16
{
17
    if (isSetup()) {
18
 
19
    }
20
}
21
 
391 werner 22
// ************ Setup **************
23
 
24
/** setup of the seedmaps.
25
  This sets the size of the seed map and creates the seed kernel (species specific)
26
  */
373 werner 27
void SeedDispersal::setup()
28
{
391 werner 29
    if (!GlobalSettings::instance()->model()
30
        || !GlobalSettings::instance()->model()->heightGrid()
31
        || !mSpecies)
373 werner 32
        return;
391 werner 33
 
34
    const float seedmap_size = 20.f;
373 werner 35
    // setup of seed map
36
    mSeedMap.clear();
391 werner 37
    mSeedMap.setup(GlobalSettings::instance()->model()->heightGrid()->metricRect(), seedmap_size );
373 werner 38
    mSeedMap.initialize(0.);
391 werner 39
    mIndexFactor = int(seedmap_size) / cPxSize; // ratio seed grid / lip-grid:
459 werner 40
    qDebug() << "Seed map setup. Species:"<< mSpecies->id() << "kernel-size: " << mSeedMap.sizeX() << "x" << mSeedMap.sizeY() << "pixels.";
373 werner 41
 
445 werner 42
    if (mSpecies==0)
43
        throw IException("Setup of SeedDispersal: Species not defined.");
44
 
415 werner 45
    // settings
445 werner 46
    mTM_occupancy = 1.; // is currently constant
47
    mSpecies->treeMigKernel(mTM_as1, mTM_as2, mTM_ks); // set values
48
    mTM_fecundity_cell = mSpecies->fecundity_m2() * seedmap_size*seedmap_size * mTM_occupancy; // scale to production for the whole cell
49
    mNonSeedYearFraction = mSpecies->nonSeedYearFraction();
415 werner 50
 
445 werner 51
    createKernel(mKernelSeedYear, mTM_fecundity_cell);
415 werner 52
 
53
    // the kernel for non seed years looks similar, but is simply linearly scaled down
54
    // using the species parameter NonSeedYearFraction.
55
    // the central pixel still gets the value of 1 (i.e. 100% probability)
445 werner 56
    createKernel(mKernelNonSeedYear, mTM_fecundity_cell*mNonSeedYearFraction);
415 werner 57
 
417 werner 58
    if (QFile::exists("c:\\temp\\seedmaps")) {
445 werner 59
        Helper::saveToTextFile(QString("c:\\temp\\seedmaps\\seedkernelYes_%1.csv").arg(mSpecies->id()),gridToString(mKernelSeedYear));
60
        Helper::saveToTextFile(QString("c:\\temp\\seedmaps\\seedkernelNo_%1.csv").arg(mSpecies->id()),gridToString(mKernelNonSeedYear));
417 werner 61
    }
415 werner 62
 
63
 
373 werner 64
    // setup of seed kernel
391 werner 65
//    const int max_radius = 15; // pixels
66
//
67
//    mSeedKernel.clear();
68
//    mSeedKernel.setup(mSeedMap.cellsize(), 2*max_radius + 1 , 2*max_radius + 1);
69
//    mKernelOffset = max_radius;
70
//    // filling of the kernel.... for simplicity: a linear kernel
71
//    QPoint center = QPoint(mKernelOffset, mKernelOffset);
72
//    const double max_dist = max_radius * seedmap_size;
73
//    for (float *p=mSeedKernel.begin(); p!=mSeedKernel.end();++p) {
74
//        double d = mSeedKernel.distance(center, mSeedKernel.indexOf(p));
75
//        *p = qMax( 1. - d / max_dist, 0.);
76
//    }
373 werner 77
 
78
 
79
    // randomize seed map.... set 1/3 to "filled"
375 werner 80
    //for (int i=0;i<mSeedMap.count(); i++)
81
    //    mSeedMap.valueAtIndex(mSeedMap.randomPosition()) = 1.;
373 werner 82
 
83
 
375 werner 84
//    QImage img = gridToImage(mSeedMap, true, -1., 1.);
85
//    img.save("seedmap.png");
86
 
87
//    img = gridToImage(mSeedMap, true, -1., 1.);
88
//    img.save("seedmap_e.png");
373 werner 89
}
90
 
391 werner 91
// ************ Kernel **************
415 werner 92
void SeedDispersal::createKernel(Grid<float> &kernel, const float max_seed)
391 werner 93
{
415 werner 94
 
391 werner 95
    double max_dist = treemig_distanceTo(0.0001);
96
    double cell_size = mSeedMap.cellsize();
415 werner 97
    int max_radius = int(max_dist / cell_size);
391 werner 98
    // e.g.: cell_size: regeneration grid (e.g. 400qm), px-size: light-grid (4qm)
445 werner 99
    double occupation = cell_size*cell_size / (cPxSize*cPxSize * mTM_occupancy);
391 werner 100
 
415 werner 101
    kernel.clear();
391 werner 102
 
415 werner 103
    kernel.setup(mSeedMap.cellsize(), 2*max_radius + 1 , 2*max_radius + 1);
104
    int kernel_offset = max_radius;
105
 
391 werner 106
    // filling of the kernel.... use the treemig
415 werner 107
    QPoint center = QPoint(kernel_offset, kernel_offset);
108
    const float *sk_end = kernel.end();
109
    for (float *p=kernel.begin(); p!=sk_end;++p) {
110
        double d = kernel.distance(center, kernel.indexOf(p));
391 werner 111
        *p = d<=max_dist?treemig(d):0.f;
112
    }
113
 
114
    // normalize
415 werner 115
    double sum = kernel.sum();
391 werner 116
    if (sum==0. || occupation==0.)
117
        throw IException("create seed kernel: sum of probabilities = 0!");
118
 
119
    // the sum of all kernel cells has to equal 1
415 werner 120
    kernel.multiply(1./sum);
391 werner 121
    // probabilities are derived in multiplying by seed number, and dividing by occupancy criterion
415 werner 122
    kernel.multiply( max_seed / occupation);
391 werner 123
    // all cells that get more seeds than the occupancy criterion are considered to have no seed limitation for regeneration
415 werner 124
    for (float *p=kernel.begin(); p!=sk_end;++p) {
391 werner 125
        *p = qMin(*p, 1.f);
126
    }
127
    // set the parent cell to 1
415 werner 128
    kernel.valueAtIndex(kernel_offset, kernel_offset)=1.f;
129
 
130
 
391 werner 131
    // some final statistics....
415 werner 132
    qDebug() << "kernel setup.Species:"<< mSpecies->id() << "kernel-size: " << kernel.sizeX() << "x" << kernel.sizeY() << "pixels, sum: " << kernel.sum();
391 werner 133
}
134
 
135
/* R-Code:
136
treemig=function(as1,as2,ks,d) # two-part exponential function, cf. Lischke & Löffler (2006), Annex
137
        {
138
        p1=(1-ks)*exp(-d/as1)/as1
139
        if(as2>0){p2=ks*exp(-d/as2)/as2}else{p2=0}
140
        p1+p2
141
        }
142
*/
143
double SeedDispersal::treemig(const double &distance)
144
{
145
    double p1 = (1.-mTM_ks)*exp(-distance/mTM_as1)/mTM_as1;
146
    double p2 = 0.;
147
    if (mTM_as2>0.)
148
       p2 = mTM_ks*exp(-distance/mTM_as2)/mTM_as2;
149
    return p1 + p2;
150
}
151
 
152
/// calculate the distance where the probability falls below 'value'
153
double SeedDispersal::treemig_distanceTo(const double value)
154
{
155
    double dist = 0.;
156
    while (treemig(dist)>value && dist<10000.)
157
        dist+=10;
158
    return dist;
159
}
160
 
161
 
162
// ************ Dispersal **************
163
 
164
 
375 werner 165
/// debug function: loads a image of arbirtrary size...
166
void SeedDispersal::loadFromImage(const QString &fileName)
167
{
168
    mSeedMap.clear();
169
    loadGridFromImage(fileName, mSeedMap);
170
    for (float* p=mSeedMap.begin();p!=mSeedMap.end();++p)
171
        *p = *p>0.8?1.f:0.f;
172
 
173
}
174
 
175
void SeedDispersal::clear()
176
{
177
    mSeedMap.initialize(0.f);
178
}
179
 
180
void SeedDispersal::execute()
181
{
445 werner 182
    int year = GlobalSettings::instance()->currentYear();
417 werner 183
    if (QFile::exists("c:\\temp\\seedmaps"))
445 werner 184
        gridToImage(seedMap(), true, 0., 1.).save(QString("c:\\temp\\seedmaps\\seed_before_%1_%2.png").arg(mSpecies->id()).arg(year));
391 werner 185
    {
375 werner 186
    DebugTimer t("seed dispersal");
187
    // (1) detect edges
188
    edgeDetection();
189
    // (2) distribute seed probabilites from edges
190
    distribute();
391 werner 191
    }
417 werner 192
    if (QFile::exists("c:\\temp\\seedmaps"))
445 werner 193
        gridToImage(seedMap(), true, 0., 1.).save(QString("c:\\temp\\seedmaps\\seed_after_%1_%2.png").arg(mSpecies->id()).arg(year));
375 werner 194
}
195
 
373 werner 196
/** scans the seed image and detects "edges".
197
    edges are then subsequently marked (set to -1). This is pass 1 of the seed distribution process.
198
*/
199
void SeedDispersal::edgeDetection()
200
{
201
    float *p_above, *p, *p_below;
202
    int dy = mSeedMap.sizeY();
203
    int dx = mSeedMap.sizeX();
204
    int x,y;
205
    for (y=1;y<dy-1;++y){
206
        p = mSeedMap.ptr(1,y);
207
        p_above = p - dx; // one line above
208
        p_below = p + dx; // one line below
209
        for (x=1;x<dx-1;++x,++p,++p_below, ++p_above) {
210
            if (*p == 1.) {
211
                if (*(p_above-1)==0.f || *p_above==0.f || *(p_above+1)==0.f ||
212
                    *(p-1)==0.f || *(p+1)==0.f ||
213
                    *(p_below-1)==0.f || *p_below==0.f || *(p_below+1)==0.f )
214
                    *p=-1; // if any surrounding pixel is 0: -> mark as edge
215
            }
216
 
217
        }
218
    }
219
}
220
 
221
/** do the seed probability distribution.
222
    This is phase 2. Apply the seed kernel for each "edge" point identified in phase 1.
223
*/
224
void SeedDispersal::distribute()
225
{
226
    int x,y;
227
 
228
    float *end = mSeedMap.end();
229
    float *p = mSeedMap.begin();
415 werner 230
    // choose the kernel depending whether there is a seed year for the current species or not
231
    Grid<float> &kernel = species()->isSeedYear()?mKernelSeedYear:mKernelNonSeedYear;
232
    int offset = kernel.sizeX() / 2; // offset is the index of the center pixel
373 werner 233
    for(;p!=end;++p) {
375 werner 234
        if (*p==-1.f) {
373 werner 235
            // edge pixel found. Now apply the kernel....
236
            QPoint pt=mSeedMap.indexOf(p);
415 werner 237
            for (y=-offset;y<=offset;++y)
238
                for (x=-offset;x<=offset;++x)
373 werner 239
                    if (mSeedMap.isIndexValid(pt.x()+x, pt.y()+y)) {
240
                        float &val = mSeedMap.valueAtIndex(pt.x()+x, pt.y()+y);
375 werner 241
                        if (val!=-1)
415 werner 242
                            val = qMin(1.f - (1.f - val)*(1.f-kernel.valueAtIndex(x+offset, y+offset)),1.f );
373 werner 243
                    }
375 werner 244
            *p=1.f; // mark as processed
373 werner 245
        } // *p==1
246
    } // for()
247
}