Rev 1099 | Rev 1104 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | |||
1062 | werner | 2 | /******************************************************************************************** |
3 | ** iLand - an individual based forest landscape and disturbance model |
||
4 | ** http://iland.boku.ac.at |
||
5 | ** Copyright (C) 2009- Werner Rammer, Rupert Seidl |
||
6 | ** |
||
7 | ** This program is free software: you can redistribute it and/or modify |
||
8 | ** it under the terms of the GNU General Public License as published by |
||
9 | ** the Free Software Foundation, either version 3 of the License, or |
||
10 | ** (at your option) any later version. |
||
11 | ** |
||
12 | ** This program is distributed in the hope that it will be useful, |
||
13 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
14 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
15 | ** GNU General Public License for more details. |
||
16 | ** |
||
17 | ** You should have received a copy of the GNU General Public License |
||
18 | ** along with this program. If not, see <http://www.gnu.org/licenses/>. |
||
19 | ********************************************************************************************/ |
||
20 | #include "grasscover.h" |
||
21 | |||
22 | #include "globalsettings.h" |
||
1079 | werner | 23 | #include "debugtimer.h" |
1062 | werner | 24 | #include "xmlhelper.h" |
25 | #include "model.h" |
||
26 | #include "modelcontroller.h" |
||
27 | |||
1100 | werner | 28 | const int GrassCover::GRASSCOVERSTEPS; |
29 | |||
1062 | werner | 30 | GrassCover::GrassCover() |
31 | { |
||
32 | mLayers = new GrassCoverLayers(); |
||
33 | mLayers->setGrid(mGrid, this); |
||
34 | mEnabled = false; |
||
1068 | werner | 35 | mType = Invalid; |
1062 | werner | 36 | } |
37 | |||
38 | GrassCover::~GrassCover() |
||
39 | { |
||
40 | delete mLayers; |
||
41 | } |
||
42 | |||
43 | void GrassCover::setup() |
||
44 | { |
||
45 | XmlHelper xml=GlobalSettings::instance()->settings(); |
||
46 | if (!xml.valueBool("model.settings.grass.enabled")) { |
||
47 | // clear grid |
||
48 | mGrid.clear(); |
||
49 | GlobalSettings::instance()->controller()->removeLayers(mLayers); |
||
50 | mEnabled=false; |
||
51 | qDebug() << "grass module not enabled"; |
||
52 | return; |
||
53 | } |
||
54 | // create the grid |
||
55 | mGrid.setup(GlobalSettings::instance()->model()->grid()->metricRect(), GlobalSettings::instance()->model()->grid()->cellsize()); |
||
56 | mGrid.wipe(); |
||
1082 | werner | 57 | // mask out out-of-project areas |
58 | HeightGrid *hg = GlobalSettings::instance()->model()->heightGrid(); |
||
59 | for (int i=0;i<mGrid.count();++i) |
||
60 | if (!hg->valueAtIndex(mGrid.index5(i)).isValid()) |
||
61 | mGrid[i] = -1; |
||
1062 | werner | 62 | |
1068 | werner | 63 | mType = Invalid; |
64 | QString type = xml.value("model.settings.grass.type"); |
||
65 | if (type == QStringLiteral("pixel")) |
||
66 | mType = Pixel; |
||
1062 | werner | 67 | |
1068 | werner | 68 | if (type==QStringLiteral("continuous")) |
69 | mType = Continuous; |
||
70 | |||
71 | if (mType == Invalid) |
||
72 | throw IException("GrassCover::setup: invalid 'grass.type'. Allowed: 'continous' and 'pixel'."); |
||
73 | |||
74 | if (mType == Pixel) { |
||
75 | // setup of pixel based / discrete approach |
||
76 | QString formula = xml.value("model.settings.grass.grassDuration"); |
||
77 | if (formula.isEmpty()) |
||
78 | throw IException("GrassCover::setup(): missing equation for 'grassDuration'."); |
||
1096 | werner | 79 | mPDF.setup(formula, 0., 100.); |
80 | //mGrassEffect.setExpression(formula); |
||
1079 | werner | 81 | |
1096 | werner | 82 | mGrassLIFThreshold = static_cast<float>( xml.valueDouble("model.settings.grass.LIFThreshold", 0.2) ); |
1068 | werner | 83 | |
84 | // clear array |
||
1099 | werner | 85 | for (int i=0;i<GRASSCOVERSTEPS;++i) { |
1068 | werner | 86 | mEffect[i] = 0.; |
87 | } |
||
88 | |||
89 | } else { |
||
90 | |||
91 | // setup of continuous grass concept |
||
92 | |||
93 | QString formula = xml.value("model.settings.grass.grassPotential"); |
||
94 | if (formula.isEmpty()) |
||
95 | throw IException("setup of 'grass': required expression 'grassPotential' is missing."); |
||
96 | mGrassPotential.setExpression(formula); |
||
1099 | werner | 97 | mGrassPotential.linearize(0.,1., qMin(GRASSCOVERSTEPS, 1000)); |
1068 | werner | 98 | |
99 | formula = xml.value("model.settings.grass.grassEffect"); |
||
100 | if (formula.isEmpty()) |
||
101 | throw IException("setup of 'grass': required expression 'grassEffect' is missing."); |
||
102 | mGrassEffect.setExpression(formula); |
||
1096 | werner | 103 | mMaxTimeLag = static_cast<int>( xml.valueDouble("model.settings.grass.maxTimeLag") ); |
1068 | werner | 104 | if (mMaxTimeLag==0) |
105 | throw IException("setup of 'grass': value of 'maxTimeLag' is invalid or missing."); |
||
1099 | werner | 106 | mGrowthRate = GRASSCOVERSTEPS / mMaxTimeLag; |
1068 | werner | 107 | |
108 | // set up the effect on regeneration in NSTEPS steps |
||
1099 | werner | 109 | for (int i=0;i<GRASSCOVERSTEPS;++i) { |
110 | double effect = mGrassEffect.calculate(i/double(GRASSCOVERSTEPS-1)); |
||
1068 | werner | 111 | mEffect[i] = limit(effect, 0., 1.); |
112 | } |
||
113 | |||
1099 | werner | 114 | mMaxState = static_cast<qint16>( limit(mGrassPotential.calculate(1.f), 0., 1.)*(GRASSCOVERSTEPS-1) ); // the max value of the potential function |
1062 | werner | 115 | } |
116 | |||
117 | GlobalSettings::instance()->controller()->addLayers(mLayers, QStringLiteral("grass cover")); |
||
118 | mEnabled = true; |
||
119 | qDebug() << "setup of grass cover complete."; |
||
120 | |||
121 | } |
||
122 | |||
123 | void GrassCover::setInitialValues(const QVector<float *> &LIFpixels, const int percent) |
||
124 | { |
||
125 | if (!enabled()) |
||
126 | return; |
||
1068 | werner | 127 | if (mType == Continuous) { |
1099 | werner | 128 | grass_grid_type cval = static_cast<grass_grid_type>( limit(percent / 100., 0., 1.)*(GRASSCOVERSTEPS-1) ); |
1068 | werner | 129 | if (cval > mMaxState) |
130 | cval = mMaxState; |
||
1064 | werner | 131 | |
1068 | werner | 132 | Grid<float> *lif_grid = GlobalSettings::instance()->model()->grid(); |
133 | for (QVector<float *>::const_iterator it = LIFpixels.constBegin(); it!=LIFpixels.constEnd(); ++it) |
||
134 | mGrid.valueAtIndex(lif_grid->indexOf(*it)) = cval; |
||
135 | } else { |
||
136 | // mType == Pixel |
||
137 | Grid<float> *lif_grid = GlobalSettings::instance()->model()->grid(); |
||
138 | for (QVector<float *>::const_iterator it = LIFpixels.constBegin(); it!=LIFpixels.constEnd(); ++it) { |
||
139 | if (percent > irandom(0,100)) |
||
1096 | werner | 140 | mGrid.valueAtIndex(lif_grid->indexOf(*it)) = static_cast<qint16>( mPDF.get() ); |
1068 | werner | 141 | else |
142 | mGrid.valueAtIndex(lif_grid->indexOf(*it)) = 0; |
||
143 | } |
||
144 | |||
145 | } |
||
1062 | werner | 146 | } |
147 | |||
148 | void GrassCover::execute() |
||
149 | { |
||
150 | if (!enabled()) |
||
151 | return; |
||
152 | |||
1079 | werner | 153 | DebugTimer t("GrassCover"); |
154 | |||
1062 | werner | 155 | // Main function of the grass submodule |
156 | float *lif = GlobalSettings::instance()->model()->grid()->begin(); |
||
157 | float *end_lif = GlobalSettings::instance()->model()->grid()->end(); |
||
1068 | werner | 158 | grass_grid_type *gr = mGrid.begin(); |
1062 | werner | 159 | |
1068 | werner | 160 | if (mType == Continuous) { |
161 | // loop over every LIF pixel |
||
162 | int skipped=0; |
||
163 | for (; lif!=end_lif;++lif, ++gr) { |
||
164 | // calculate potential grass vegetation cover |
||
165 | if (*lif == 1.f && *gr==mMaxState) { |
||
166 | ++skipped; |
||
167 | continue; |
||
168 | } |
||
169 | |||
1099 | werner | 170 | int potential = static_cast<int>( limit(mGrassPotential.calculate(*lif), 0., 1.)*(GRASSCOVERSTEPS-1) ); |
1096 | werner | 171 | *gr = static_cast<qint16>( qMin( int(*gr) + mGrowthRate, potential) ); |
1068 | werner | 172 | |
1064 | werner | 173 | } |
1068 | werner | 174 | //qDebug() << "skipped" << skipped; |
175 | } else { |
||
176 | // type = Pixel |
||
177 | for (; lif!=end_lif;++lif, ++gr) { |
||
1082 | werner | 178 | if (*gr<0) |
179 | continue; |
||
1096 | werner | 180 | if (*gr>1) |
181 | (*gr)--; // count down the years (until gr=1) |
||
1062 | werner | 182 | |
1068 | werner | 183 | if (*gr==0 && *lif>mGrassLIFThreshold) { |
184 | // enable grass cover |
||
1096 | werner | 185 | qint16 v = static_cast<qint16>( qMax(mPDF.get(), 0.) ); |
186 | *gr = v + 1; // switch on... |
||
1068 | werner | 187 | } |
188 | if (*gr==1 && *lif<mGrassLIFThreshold) { |
||
1096 | werner | 189 | // now LIF is below the threshold - this enables the pixel get grassy again |
1068 | werner | 190 | *gr = 0; |
191 | } |
||
192 | } |
||
1062 | werner | 193 | } |
194 | |||
195 | } |
||
196 | |||
197 | |||
198 | |||
1068 | werner | 199 | double GrassCoverLayers::value(const grass_grid_type &data, const int index) const |
1062 | werner | 200 | { |
201 | if (!mGrassCover->enabled()) return 0.; |
||
202 | switch(index){ |
||
203 | case 0: return mGrassCover->effect(data); //effect |
||
1068 | werner | 204 | case 1: return mGrassCover->cover(data); // cover |
1062 | werner | 205 | default: throw IException(QString("invalid variable index for a GrassCoverLayers: %1").arg(index)); |
206 | } |
||
207 | } |
||
208 | |||
209 | const QVector<LayeredGridBase::LayerElement> &GrassCoverLayers::names() |
||
210 | { |
||
211 | if (mNames.isEmpty()) |
||
212 | mNames = QVector<LayeredGridBase::LayerElement>() |
||
213 | << LayeredGridBase::LayerElement(QLatin1Literal("effect"), QLatin1Literal("prohibiting effect on regeneration [0..1]"), GridViewGreens) |
||
1073 | werner | 214 | << LayeredGridBase::LayerElement(QLatin1Literal("cover"), QLatin1Literal("current grass cover on pixels [0..1 for continuous, or #(years+2) for pixel mode]"), GridViewGreens); |
1062 | werner | 215 | return mNames; |
216 | |||
217 | } |