Rev 671 | Rev 697 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 671 | Rev 674 | ||
---|---|---|---|
1 | Redirecting to URL 'https://iland.boku.ac.at/svn/iland/tags/release_1.0/src/core/management.cpp': |
1 | Redirecting to URL 'https://iland.boku.ac.at/svn/iland/tags/release_1.0/src/core/management.cpp': |
2 | /********************************************************************************************
|
2 | /********************************************************************************************
|
3 | ** iLand - an individual based forest landscape and disturbance model
|
3 | ** iLand - an individual based forest landscape and disturbance model
|
4 | ** http://iland.boku.ac.at
|
4 | ** http://iland.boku.ac.at
|
5 | ** Copyright (C) 2009- Werner Rammer, Rupert Seidl
|
5 | ** Copyright (C) 2009- Werner Rammer, Rupert Seidl
|
6 | **
|
6 | **
|
7 | ** This program is free software: you can redistribute it and/or modify
|
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
|
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
|
9 | ** the Free Software Foundation, either version 3 of the License, or
|
10 | ** (at your option) any later version.
|
10 | ** (at your option) any later version.
|
11 | **
|
11 | **
|
12 | ** This program is distributed in the hope that it will be useful,
|
12 | ** This program is distributed in the hope that it will be useful,
|
13 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15 | ** GNU General Public License for more details.
|
15 | ** GNU General Public License for more details.
|
16 | **
|
16 | **
|
17 | ** You should have received a copy of the GNU General Public License
|
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/>.
|
18 | ** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
19 | ********************************************************************************************/
|
19 | ********************************************************************************************/
|
20 | 20 | ||
21 | #include "global.h"
|
21 | #include "global.h"
|
22 | #include "management.h"
|
22 | #include "management.h"
|
23 | #include "helper.h"
|
23 | #include "helper.h"
|
24 | #include "model.h"
|
24 | #include "model.h"
|
25 | #include "resourceunit.h"
|
25 | #include "resourceunit.h"
|
26 | #include "tree.h"
|
26 | #include "tree.h"
|
27 | #include "expressionwrapper.h"
|
27 | #include "expressionwrapper.h"
|
28 | #include "sapling.h"
|
28 | #include "sapling.h"
|
29 | #include "soil.h"
|
29 | #include "soil.h"
|
30 | 30 | ||
31 | #include "climateconverter.h"
|
31 | #include "climateconverter.h"
|
32 | #include "csvfile.h"
|
32 | #include "csvfile.h"
|
33 | #include "scriptglobal.h"
|
33 | #include "scriptglobal.h"
|
34 | #include "mapgrid.h"
|
34 | #include "mapgrid.h"
|
- | 35 | #include "modules.h"
|
|
35 | 36 | ||
36 | #include <QtScript>
|
37 | #include <QtScript>
|
37 | #include <QTextEdit>
|
38 | #include <QTextEdit>
|
38 | QObject *Management::scriptOutput = 0; |
39 | QObject *Management::scriptOutput = 0; |
39 | 40 | ||
40 | QScriptValue script_debug(QScriptContext *ctx, QScriptEngine *eng) |
41 | QScriptValue script_debug(QScriptContext *ctx, QScriptEngine *eng) |
41 | {
|
42 | {
|
42 | QString value;
|
43 | QString value;
|
43 | for (int i = 0; i < ctx->argumentCount(); ++i) { |
44 | for (int i = 0; i < ctx->argumentCount(); ++i) { |
44 | if (i > 0) |
45 | if (i > 0) |
45 | value.append(" "); |
46 | value.append(" "); |
46 | value.append(ctx->argument(i).toString()); |
47 | value.append(ctx->argument(i).toString()); |
47 | }
|
48 | }
|
48 | if (Management::scriptOutput) { |
49 | if (Management::scriptOutput) { |
49 | QTextEdit *e = qobject_cast<QTextEdit*>(Management::scriptOutput); |
50 | QTextEdit *e = qobject_cast<QTextEdit*>(Management::scriptOutput); |
50 | if (e) |
51 | if (e) |
51 | e->append(value); |
52 | e->append(value); |
52 | } else { |
53 | } else { |
53 | qDebug() << "Script:" << value; |
54 | qDebug() << "Script:" << value; |
54 | }
|
55 | }
|
55 | return eng->undefinedValue(); |
56 | return eng->undefinedValue(); |
56 | }
|
57 | }
|
57 | 58 | ||
58 | QScriptValue script_include(QScriptContext *ctx, QScriptEngine *eng) |
59 | QScriptValue script_include(QScriptContext *ctx, QScriptEngine *eng) |
59 | {
|
60 | {
|
60 | 61 | ||
61 | QString fileName = ctx->argument(0).toString(); |
62 | QString fileName = ctx->argument(0).toString(); |
62 | QString path =GlobalSettings::instance()->path(fileName, "script") ; |
63 | QString path =GlobalSettings::instance()->path(fileName, "script") ; |
63 | QString includeFile=Helper::loadTextFile(path); |
64 | QString includeFile=Helper::loadTextFile(path); |
64 | 65 | ||
65 | ctx->setActivationObject(ctx->parentContext()->activationObject()); |
66 | ctx->setActivationObject(ctx->parentContext()->activationObject()); |
66 | ctx->setThisObject(ctx->parentContext()->thisObject()); |
67 | ctx->setThisObject(ctx->parentContext()->thisObject()); |
67 | 68 | ||
68 | QScriptValue ret = eng->evaluate(includeFile, fileName); |
69 | QScriptValue ret = eng->evaluate(includeFile, fileName); |
69 | if (eng->hasUncaughtException()) |
70 | if (eng->hasUncaughtException()) |
70 | qDebug() << "Error in include:" << eng->uncaughtException().toString(); |
71 | qDebug() << "Error in include:" << eng->uncaughtException().toString(); |
71 | return ret; |
72 | return ret; |
72 | }
|
73 | }
|
73 | 74 | ||
74 | QScriptValue script_alert(QScriptContext *ctx, QScriptEngine *eng) |
75 | QScriptValue script_alert(QScriptContext *ctx, QScriptEngine *eng) |
75 | {
|
76 | {
|
76 | QString value = ctx->argument(0).toString(); |
77 | QString value = ctx->argument(0).toString(); |
77 | Helper::msg(value); |
78 | Helper::msg(value); |
78 | return eng->undefinedValue(); |
79 | return eng->undefinedValue(); |
79 | }
|
80 | }
|
80 | // global output function
|
81 | // global output function
|
81 | QString Management::executeScript(QString cmd) |
82 | QString Management::executeScript(QString cmd) |
82 | {
|
83 | {
|
83 | DebugTimer t("execute javascript"); |
84 | DebugTimer t("execute javascript"); |
84 | if (mEngine) |
85 | if (mEngine) |
85 | mEngine->evaluate(cmd); |
86 | mEngine->evaluate(cmd); |
86 | if (mEngine->hasUncaughtException()) { |
87 | if (mEngine->hasUncaughtException()) { |
87 | //int line = mEngine->uncaughtExceptionLineNumber();
|
88 | //int line = mEngine->uncaughtExceptionLineNumber();
|
88 | QString msg = QString( "Script Error occured: %1\n").arg( mEngine->uncaughtException().toString()); |
89 | QString msg = QString( "Script Error occured: %1\n").arg( mEngine->uncaughtException().toString()); |
89 | msg+=mEngine->uncaughtExceptionBacktrace().join("\n"); |
90 | msg+=mEngine->uncaughtExceptionBacktrace().join("\n"); |
90 | return msg; |
91 | return msg; |
91 | } else { |
92 | } else { |
92 | return QString(); |
93 | return QString(); |
93 | }
|
94 | }
|
94 | }
|
95 | }
|
95 | 96 | ||
96 | Management::Management() |
97 | Management::Management() |
97 | {
|
98 | {
|
98 | // setup the scripting engine
|
99 | // setup the scripting engine
|
99 | mEngine = new QScriptEngine(); |
100 | mEngine = new QScriptEngine(); |
100 | QScriptValue objectValue = mEngine->newQObject(this); |
101 | QScriptValue objectValue = mEngine->newQObject(this); |
101 | QScriptValue dbgprint = mEngine->newFunction(script_debug); |
102 | QScriptValue dbgprint = mEngine->newFunction(script_debug); |
102 | QScriptValue sinclude = mEngine->newFunction(script_include); |
103 | QScriptValue sinclude = mEngine->newFunction(script_include); |
103 | QScriptValue alert = mEngine->newFunction(script_alert); |
104 | QScriptValue alert = mEngine->newFunction(script_alert); |
104 | mEngine->globalObject().setProperty("management", objectValue); |
105 | mEngine->globalObject().setProperty("management", objectValue); |
105 | mEngine->globalObject().setProperty("print",dbgprint); |
106 | mEngine->globalObject().setProperty("print",dbgprint); |
106 | mEngine->globalObject().setProperty("include",sinclude); |
107 | mEngine->globalObject().setProperty("include",sinclude); |
107 | mEngine->globalObject().setProperty("alert", alert); |
108 | mEngine->globalObject().setProperty("alert", alert); |
108 | 109 | ||
109 | // globals object: instatiate here, but ownership goes to script engine
|
110 | // globals object: instatiate here, but ownership goes to script engine
|
110 | ScriptGlobal *global = new ScriptGlobal(); |
111 | ScriptGlobal *global = new ScriptGlobal(); |
111 | QScriptValue glb = mEngine->newQObject(global,QScriptEngine::ScriptOwnership); |
112 | QScriptValue glb = mEngine->newQObject(global,QScriptEngine::ScriptOwnership); |
112 | mEngine->globalObject().setProperty("Globals", glb); |
113 | mEngine->globalObject().setProperty("Globals", glb); |
113 | // other object types
|
114 | // other object types
|
114 | ClimateConverter::addToScriptEngine(*mEngine); |
115 | ClimateConverter::addToScriptEngine(*mEngine); |
115 | CSVFile::addToScriptEngine(*mEngine); |
116 | CSVFile::addToScriptEngine(*mEngine); |
116 | MapGridWrapper::addToScriptEngine(*mEngine); |
117 | MapGridWrapper::addToScriptEngine(*mEngine); |
- | 118 | // setup scripting for modules
|
|
- | 119 | GlobalSettings::instance()->model()->modules()->setupScripting(mEngine); |
|
117 | 120 | ||
118 | // default values for removal fractions
|
121 | // default values for removal fractions
|
119 | // 100% of the stem, 0% of foliage and branches
|
122 | // 100% of the stem, 0% of foliage and branches
|
120 | mRemoveFoliage = 0.; |
123 | mRemoveFoliage = 0.; |
121 | mRemoveBranch = 0.; |
124 | mRemoveBranch = 0.; |
122 | mRemoveStem = 1.; |
125 | mRemoveStem = 1.; |
123 | 126 | ||
124 | 127 | ||
125 | }
|
128 | }
|
126 | 129 | ||
127 | Management::~Management() |
130 | Management::~Management() |
128 | {
|
131 | {
|
129 | delete mEngine; |
132 | delete mEngine; |
130 | }
|
133 | }
|
131 | 134 | ||
132 | void Management::loadScript(const QString &fileName) |
135 | void Management::loadScript(const QString &fileName) |
133 | {
|
136 | {
|
134 | mScriptFile = fileName; |
137 | mScriptFile = fileName; |
135 | QString program = Helper::loadTextFile(fileName); |
138 | QString program = Helper::loadTextFile(fileName); |
136 | if (program.isEmpty()) |
139 | if (program.isEmpty()) |
137 | return; |
140 | return; |
138 | 141 | ||
139 | mEngine->evaluate(program); |
142 | mEngine->evaluate(program); |
140 | qDebug() << "management script loaded"; |
143 | qDebug() << "management script loaded"; |
141 | if (mEngine->hasUncaughtException()) |
144 | if (mEngine->hasUncaughtException()) |
142 | qDebug() << "Script Error occured: " << mEngine->uncaughtException().toString() << "\n" << mEngine->uncaughtExceptionBacktrace(); |
145 | qDebug() << "Script Error occured: " << mEngine->uncaughtException().toString() << "\n" << mEngine->uncaughtExceptionBacktrace(); |
143 | 146 | ||
144 | }
|
147 | }
|
145 | 148 | ||
146 | int Management::remain(int number) |
149 | int Management::remain(int number) |
147 | {
|
150 | {
|
148 | qDebug() << "remain called (number): " << number; |
151 | qDebug() << "remain called (number): " << number; |
149 | Model *m = GlobalSettings::instance()->model(); |
152 | Model *m = GlobalSettings::instance()->model(); |
150 | AllTreeIterator at(m); |
153 | AllTreeIterator at(m); |
151 | QList<Tree*> trees; |
154 | QList<Tree*> trees; |
152 | while (Tree *t=at.next()) |
155 | while (Tree *t=at.next()) |
153 | trees.push_back(t); |
156 | trees.push_back(t); |
154 | int to_kill = trees.count() - number; |
157 | int to_kill = trees.count() - number; |
155 | qDebug() << trees.count() << " standing, targetsize" << number << ", hence " << to_kill << "trees to remove"; |
158 | qDebug() << trees.count() << " standing, targetsize" << number << ", hence " << to_kill << "trees to remove"; |
156 | for (int i=0;i<to_kill;i++) { |
159 | for (int i=0;i<to_kill;i++) { |
157 | int index = irandom(0, trees.count()-1); |
160 | int index = irandom(0, trees.count()-1); |
158 | trees[index]->remove(); |
161 | trees[index]->remove(); |
159 | trees.removeAt(index); |
162 | trees.removeAt(index); |
160 | }
|
163 | }
|
161 | mRemoved += to_kill; |
164 | mRemoved += to_kill; |
162 | return to_kill; |
165 | return to_kill; |
163 | }
|
166 | }
|
164 | 167 | ||
165 | 168 | ||
166 | int Management::kill() |
169 | int Management::kill() |
167 | {
|
170 | {
|
168 | int c = mTrees.count(); |
171 | int c = mTrees.count(); |
169 | for (int i=0;i<mTrees.count();i++) |
172 | for (int i=0;i<mTrees.count();i++) |
170 | mTrees[i].first->remove(); |
173 | mTrees[i].first->remove(); |
171 | mTrees.clear(); |
174 | mTrees.clear(); |
172 | return c; |
175 | return c; |
173 | }
|
176 | }
|
174 | 177 | ||
175 | int Management::kill(QString filter, double fraction) |
178 | int Management::kill(QString filter, double fraction) |
176 | {
|
179 | {
|
177 | return remove_trees(filter, fraction, false); |
180 | return remove_trees(filter, fraction, false); |
178 | }
|
181 | }
|
179 | int Management::manage(QString filter, double fraction) |
182 | int Management::manage(QString filter, double fraction) |
180 | {
|
183 | {
|
181 | return remove_trees(filter, fraction, true); |
184 | return remove_trees(filter, fraction, true); |
182 | }
|
185 | }
|
183 | 186 | ||
184 | int Management::remove_percentiles(int pctfrom, int pctto, int number, bool management) |
187 | int Management::remove_percentiles(int pctfrom, int pctto, int number, bool management) |
185 | {
|
188 | {
|
186 | if (mTrees.isEmpty()) |
189 | if (mTrees.isEmpty()) |
187 | return 0; |
190 | return 0; |
188 | int index_from = limit(int(pctfrom/100. * mTrees.count()), 0, mTrees.count()); |
191 | int index_from = limit(int(pctfrom/100. * mTrees.count()), 0, mTrees.count()); |
189 | int index_to = limit(int(pctto/100. * mTrees.count()), 0, mTrees.count()-1); |
192 | int index_to = limit(int(pctto/100. * mTrees.count()), 0, mTrees.count()-1); |
190 | if (index_from>=index_to) |
193 | if (index_from>=index_to) |
191 | return 0; |
194 | return 0; |
192 | qDebug() << "attempting to remove" << number << "trees between indices" << index_from << "and" << index_to; |
195 | qDebug() << "attempting to remove" << number << "trees between indices" << index_from << "and" << index_to; |
193 | int i; |
196 | int i; |
194 | int count = number; |
197 | int count = number; |
195 | if (index_to-index_from <= number) { |
198 | if (index_to-index_from <= number) { |
196 | // kill all
|
199 | // kill all
|
197 | if (management) { |
200 | if (management) { |
198 | // management
|
201 | // management
|
199 | for (i=index_from; i<index_to; i++) |
202 | for (i=index_from; i<index_to; i++) |
200 | mTrees.at(i).first->remove(removeFoliage(), removeBranch(), removeStem()); |
203 | mTrees.at(i).first->remove(removeFoliage(), removeBranch(), removeStem()); |
201 | } else { |
204 | } else { |
202 | // just kill...
|
205 | // just kill...
|
203 | for (i=index_from; i<index_to; i++) |
206 | for (i=index_from; i<index_to; i++) |
204 | mTrees.at(i).first->remove(); |
207 | mTrees.at(i).first->remove(); |
205 | }
|
208 | }
|
206 | count = index_to - index_from; |
209 | count = index_to - index_from; |
207 | } else { |
210 | } else { |
208 | // kill randomly the provided number
|
211 | // kill randomly the provided number
|
209 | int cancel = 1000; |
212 | int cancel = 1000; |
210 | while(number>=0) { |
213 | while(number>=0) { |
211 | int rnd_index = irandom(index_from, index_to); |
214 | int rnd_index = irandom(index_from, index_to); |
212 | if (mTrees[rnd_index].first->isDead()) { |
215 | if (mTrees[rnd_index].first->isDead()) { |
213 | if (--cancel<0) { |
216 | if (--cancel<0) { |
214 | qDebug() << "Management::kill: canceling search." << number << "trees left."; |
217 | qDebug() << "Management::kill: canceling search." << number << "trees left."; |
215 | count-=number; // not all trees were killed |
218 | count-=number; // not all trees were killed |
216 | break; |
219 | break; |
217 | }
|
220 | }
|
218 | continue; |
221 | continue; |
219 | }
|
222 | }
|
220 | cancel = 1000; |
223 | cancel = 1000; |
221 | number--; |
224 | number--; |
222 | if (management) { |
225 | if (management) { |
223 | mTrees[rnd_index].first->remove( removeFoliage(), removeBranch(), removeStem() ); |
226 | mTrees[rnd_index].first->remove( removeFoliage(), removeBranch(), removeStem() ); |
224 | } else { |
227 | } else { |
225 | mTrees[rnd_index].first->remove(); |
228 | mTrees[rnd_index].first->remove(); |
226 | }
|
229 | }
|
227 | }
|
230 | }
|
228 | }
|
231 | }
|
229 | qDebug() << count << "removed."; |
232 | qDebug() << count << "removed."; |
230 | // clean up the tree list...
|
233 | // clean up the tree list...
|
231 | for (int i=mTrees.count()-1; i>=0; --i) { |
234 | for (int i=mTrees.count()-1; i>=0; --i) { |
232 | if (mTrees[i].first->isDead()) |
235 | if (mTrees[i].first->isDead()) |
233 | mTrees.removeAt(i); |
236 | mTrees.removeAt(i); |
234 | }
|
237 | }
|
235 | return count; // killed or manages |
238 | return count; // killed or manages |
236 | }
|
239 | }
|
237 | 240 | ||
238 | /** remove trees from a list and reduce the list.
|
241 | /** remove trees from a list and reduce the list.
|
239 | 242 | ||
240 | */
|
243 | */
|
241 | int Management::remove_trees(QString expression, double fraction, bool management) |
244 | int Management::remove_trees(QString expression, double fraction, bool management) |
242 | {
|
245 | {
|
243 | TreeWrapper tw;
|
246 | TreeWrapper tw;
|
244 | Expression expr(expression,&tw); |
247 | Expression expr(expression,&tw); |
245 | expr.enableIncSum(); |
248 | expr.enableIncSum(); |
246 | int n = 0; |
249 | int n = 0; |
247 | QList<QPair<Tree*, double> >::iterator tp=mTrees.begin(); |
250 | QList<QPair<Tree*, double> >::iterator tp=mTrees.begin(); |
248 | try { |
251 | try { |
249 | while (tp!=mTrees.end()) { |
252 | while (tp!=mTrees.end()) { |
250 | tw.setTree(tp->first); |
253 | tw.setTree(tp->first); |
251 | // if expression evaluates to true and if random number below threshold...
|
254 | // if expression evaluates to true and if random number below threshold...
|
252 | if (expr.calculate(tw) && drandom() <=fraction) { |
255 | if (expr.calculate(tw) && drandom() <=fraction) { |
253 | // remove from system
|
256 | // remove from system
|
254 | if (management) |
257 | if (management) |
255 | tp->first->remove(removeFoliage(), removeBranch(), removeStem()); // management with removal fractions |
258 | tp->first->remove(removeFoliage(), removeBranch(), removeStem()); // management with removal fractions |
256 | else
|
259 | else
|
257 | tp->first->remove(); // kill |
260 | tp->first->remove(); // kill |
258 | // remove from tree list
|
261 | // remove from tree list
|
259 | tp = mTrees.erase(tp); |
262 | tp = mTrees.erase(tp); |
260 | n++; |
263 | n++; |
261 | } else { |
264 | } else { |
262 | tp++; |
265 | tp++; |
263 | }
|
266 | }
|
264 | }
|
267 | }
|
265 | } catch(const IException &e) { |
268 | } catch(const IException &e) { |
266 | context()->throwError(e.message()); |
269 | context()->throwError(e.message()); |
267 | }
|
270 | }
|
268 | return n; |
271 | return n; |
269 | }
|
272 | }
|
270 | 273 | ||
271 | // calculate aggregates for all trees in the internal list
|
274 | // calculate aggregates for all trees in the internal list
|
272 | double Management::aggregate_function(QString expression, QString filter, QString type) |
275 | double Management::aggregate_function(QString expression, QString filter, QString type) |
273 | {
|
276 | {
|
274 | QList<QPair<Tree*, double> >::iterator tp=mTrees.begin(); |
277 | QList<QPair<Tree*, double> >::iterator tp=mTrees.begin(); |
275 | TreeWrapper tw;
|
278 | TreeWrapper tw;
|
276 | Expression expr(expression,&tw); |
279 | Expression expr(expression,&tw); |
277 | 280 | ||
278 | double sum = 0.; |
281 | double sum = 0.; |
279 | int n=0; |
282 | int n=0; |
280 | try { |
283 | try { |
281 | 284 | ||
282 | if (filter.isEmpty()) { |
285 | if (filter.isEmpty()) { |
283 | // without filtering
|
286 | // without filtering
|
284 | while (tp!=mTrees.end()) { |
287 | while (tp!=mTrees.end()) { |
285 | tw.setTree(tp->first); |
288 | tw.setTree(tp->first); |
286 | sum += expr.calculate(); |
289 | sum += expr.calculate(); |
287 | ++n; |
290 | ++n; |
288 | ++tp; |
291 | ++tp; |
289 | }
|
292 | }
|
290 | } else { |
293 | } else { |
291 | // with filtering
|
294 | // with filtering
|
292 | Expression filter_expr(filter,&tw); |
295 | Expression filter_expr(filter,&tw); |
293 | filter_expr.enableIncSum(); |
296 | filter_expr.enableIncSum(); |
294 | while (tp!=mTrees.end()) { |
297 | while (tp!=mTrees.end()) { |
295 | tw.setTree(tp->first); |
298 | tw.setTree(tp->first); |
296 | if (filter_expr.calculate()) { |
299 | if (filter_expr.calculate()) { |
297 | sum += expr.calculate(); |
300 | sum += expr.calculate(); |
298 | ++n; |
301 | ++n; |
299 | }
|
302 | }
|
300 | ++tp; |
303 | ++tp; |
301 | }
|
304 | }
|
302 | }
|
305 | }
|
303 | 306 | ||
304 | } catch(const IException &e) { |
307 | } catch(const IException &e) { |
305 | context()->throwError(e.message()); |
308 | context()->throwError(e.message()); |
306 | }
|
309 | }
|
307 | if (type=="sum") |
310 | if (type=="sum") |
308 | return sum; |
311 | return sum; |
309 | if (type=="mean") |
312 | if (type=="mean") |
310 | return n>0?sum/double(n):0.; |
313 | return n>0?sum/double(n):0.; |
311 | return 0.; |
314 | return 0.; |
312 | }
|
315 | }
|
313 | 316 | ||
314 | 317 | ||
315 | // from the range percentile range pctfrom to pctto (each 1..100)
|
318 | // from the range percentile range pctfrom to pctto (each 1..100)
|
316 | int Management::kill(int pctfrom, int pctto, int number) |
319 | int Management::kill(int pctfrom, int pctto, int number) |
317 | {
|
320 | {
|
318 | return remove_percentiles(pctfrom, pctto, number, false); |
321 | return remove_percentiles(pctfrom, pctto, number, false); |
319 | }
|
322 | }
|
320 | 323 | ||
321 | // from the range percentile range pctfrom to pctto (each 1..100)
|
324 | // from the range percentile range pctfrom to pctto (each 1..100)
|
322 | int Management::manage(int pctfrom, int pctto, int number) |
325 | int Management::manage(int pctfrom, int pctto, int number) |
323 | {
|
326 | {
|
324 | return remove_percentiles(pctfrom, pctto, number, true); |
327 | return remove_percentiles(pctfrom, pctto, number, true); |
325 | }
|
328 | }
|
326 | 329 | ||
327 | int Management::manage() |
330 | int Management::manage() |
328 | {
|
331 | {
|
329 | int c = mTrees.count(); |
332 | int c = mTrees.count(); |
330 | for (int i=0;i<mTrees.count();i++) |
333 | for (int i=0;i<mTrees.count();i++) |
331 | mTrees[i].first->remove(removeFoliage(), |
334 | mTrees[i].first->remove(removeFoliage(), |
332 | removeBranch(), |
335 | removeBranch(), |
333 | removeStem()); // remove with current removal fractions |
336 | removeStem()); // remove with current removal fractions |
334 | mTrees.clear(); |
337 | mTrees.clear(); |
335 | return c; |
338 | return c; |
336 | }
|
339 | }
|
337 | 340 | ||
338 | 341 | ||
339 | 342 | ||
340 | void Management::run() |
343 | void Management::run() |
341 | {
|
344 | {
|
342 | mTrees.clear(); |
345 | mTrees.clear(); |
343 | mRemoved=0; |
346 | mRemoved=0; |
344 | qDebug() << "Management::run() called"; |
347 | qDebug() << "Management::run() called"; |
345 | QScriptValue mgmt = mEngine->globalObject().property("manage"); |
348 | QScriptValue mgmt = mEngine->globalObject().property("manage"); |
346 | int year = GlobalSettings::instance()->currentYear(); |
349 | int year = GlobalSettings::instance()->currentYear(); |
347 | mgmt.call(QScriptValue(), QScriptValueList()<<year); |
350 | mgmt.call(QScriptValue(), QScriptValueList()<<year); |
348 | if (mEngine->hasUncaughtException()) |
351 | if (mEngine->hasUncaughtException()) |
349 | qDebug() << "Script Error occured: " << mEngine->uncaughtException().toString() << "\n" << mEngine->uncaughtExceptionBacktrace(); |
352 | qDebug() << "Script Error occured: " << mEngine->uncaughtException().toString() << "\n" << mEngine->uncaughtExceptionBacktrace(); |
350 | 353 | ||
351 | if (mRemoved>0) { |
354 | if (mRemoved>0) { |
352 | foreach(ResourceUnit *ru, GlobalSettings::instance()->model()->ruList()) |
355 | foreach(ResourceUnit *ru, GlobalSettings::instance()->model()->ruList()) |
353 | ru->cleanTreeList(); |
356 | ru->cleanTreeList(); |
354 | }
|
357 | }
|
355 | }
|
358 | }
|
356 | 359 | ||
357 | int Management::filter(QVariantList idList) |
360 | int Management::filter(QVariantList idList) |
358 | {
|
361 | {
|
359 | QVector<int> ids; |
362 | QVector<int> ids; |
360 | foreach(const QVariant &v, idList) |
363 | foreach(const QVariant &v, idList) |
361 | if (!v.isNull()) |
364 | if (!v.isNull()) |
362 | ids << v.toInt(); |
365 | ids << v.toInt(); |
363 | // QHash<int, int> ids;
|
366 | // QHash<int, int> ids;
|
364 | // foreach(const QVariant &v, idList)
|
367 | // foreach(const QVariant &v, idList)
|
365 | // ids[v.toInt()] = 1;
|
368 | // ids[v.toInt()] = 1;
|
366 | 369 | ||
367 | QList<QPair<Tree*, double> >::iterator tp=mTrees.begin(); |
370 | QList<QPair<Tree*, double> >::iterator tp=mTrees.begin(); |
368 | while (tp!=mTrees.end()) { |
371 | while (tp!=mTrees.end()) { |
369 | if (!ids.contains(tp->first->id()) ) |
372 | if (!ids.contains(tp->first->id()) ) |
370 | tp = mTrees.erase(tp); |
373 | tp = mTrees.erase(tp); |
371 | else
|
374 | else
|
372 | tp++; |
375 | tp++; |
373 | }
|
376 | }
|
374 | qDebug() << "Management::filter by id-list:" << mTrees.count(); |
377 | qDebug() << "Management::filter by id-list:" << mTrees.count(); |
375 | return mTrees.count(); |
378 | return mTrees.count(); |
376 | }
|
379 | }
|
377 | 380 | ||
378 | int Management::filter(QString filter) |
381 | int Management::filter(QString filter) |
379 | {
|
382 | {
|
380 | TreeWrapper tw;
|
383 | TreeWrapper tw;
|
381 | Expression expr(filter,&tw); |
384 | Expression expr(filter,&tw); |
382 | expr.enableIncSum(); |
385 | expr.enableIncSum(); |
383 | int n_before = mTrees.count(); |
386 | int n_before = mTrees.count(); |
384 | QList<QPair<Tree*, double> >::iterator tp=mTrees.begin(); |
387 | QList<QPair<Tree*, double> >::iterator tp=mTrees.begin(); |
385 | try { |
388 | try { |
386 | while (tp!=mTrees.end()) { |
389 | while (tp!=mTrees.end()) { |
387 | tw.setTree(tp->first); |
390 | tw.setTree(tp->first); |
388 | if (expr.calculate(tw)) |
391 | if (expr.calculate(tw)) |
389 | tp = mTrees.erase(tp); |
392 | tp = mTrees.erase(tp); |
390 | else
|
393 | else
|
391 | tp++; |
394 | tp++; |
392 | }
|
395 | }
|
393 | } catch(const IException &e) { |
396 | } catch(const IException &e) { |
394 | context()->throwError(e.message()); |
397 | context()->throwError(e.message()); |
395 | }
|
398 | }
|
396 | 399 | ||
397 | qDebug() << "filtering with" << filter << "N=" << n_before << "/" << mTrees.count() << "trees (before/after filtering)."; |
400 | qDebug() << "filtering with" << filter << "N=" << n_before << "/" << mTrees.count() << "trees (before/after filtering)."; |
398 | return mTrees.count(); |
401 | return mTrees.count(); |
399 | }
|
402 | }
|
400 | 403 | ||
401 | int Management::load(int ruindex) |
404 | int Management::load(int ruindex) |
402 | {
|
405 | {
|
403 | Model *m = GlobalSettings::instance()->model(); |
406 | Model *m = GlobalSettings::instance()->model(); |
404 | ResourceUnit *ru = m->ru(ruindex); |
407 | ResourceUnit *ru = m->ru(ruindex); |
405 | if (!ru) |
408 | if (!ru) |
406 | return -1; |
409 | return -1; |
407 | mTrees.clear(); |
410 | mTrees.clear(); |
408 | for (int i=0;i<ru->trees().count();i++) |
411 | for (int i=0;i<ru->trees().count();i++) |
409 | if (!ru->tree(i)->isDead()) |
412 | if (!ru->tree(i)->isDead()) |
410 | mTrees.push_back(QPair<Tree*,double>(ru->tree(i), 0.)); |
413 | mTrees.push_back(QPair<Tree*,double>(ru->tree(i), 0.)); |
411 | return mTrees.count(); |
414 | return mTrees.count(); |
412 | }
|
415 | }
|
413 | 416 | ||
414 | int Management::load(QString filter) |
417 | int Management::load(QString filter) |
415 | {
|
418 | {
|
416 | TreeWrapper tw;
|
419 | TreeWrapper tw;
|
417 | Model *m = GlobalSettings::instance()->model(); |
420 | Model *m = GlobalSettings::instance()->model(); |
418 | mTrees.clear(); |
421 | mTrees.clear(); |
419 | AllTreeIterator at(m); |
422 | AllTreeIterator at(m); |
420 | if (filter.isEmpty()) { |
423 | if (filter.isEmpty()) { |
421 | while (Tree *t=at.nextLiving()) |
424 | while (Tree *t=at.nextLiving()) |
422 | if (!t->isDead()) |
425 | if (!t->isDead()) |
423 | mTrees.push_back(QPair<Tree*, double>(t, 0.)); |
426 | mTrees.push_back(QPair<Tree*, double>(t, 0.)); |
424 | } else { |
427 | } else { |
425 | Expression expr(filter,&tw); |
428 | Expression expr(filter,&tw); |
426 | expr.enableIncSum(); |
429 | expr.enableIncSum(); |
427 | qDebug() << "filtering with" << filter; |
430 | qDebug() << "filtering with" << filter; |
428 | while (Tree *t=at.nextLiving()) { |
431 | while (Tree *t=at.nextLiving()) { |
429 | tw.setTree(t); |
432 | tw.setTree(t); |
430 | if (!t->isDead() && expr.execute()) |
433 | if (!t->isDead() && expr.execute()) |
431 | mTrees.push_back(QPair<Tree*, double>(t, 0.)); |
434 | mTrees.push_back(QPair<Tree*, double>(t, 0.)); |
432 | }
|
435 | }
|
433 | }
|
436 | }
|
434 | return mTrees.count(); |
437 | return mTrees.count(); |
435 | }
|
438 | }
|
436 | 439 | ||
437 | /**
|
440 | /**
|
438 | */
|
441 | */
|
439 | void Management::loadFromTreeList(QList<Tree*>tree_list) |
442 | void Management::loadFromTreeList(QList<Tree*>tree_list) |
440 | {
|
443 | {
|
441 | mTrees.clear(); |
444 | mTrees.clear(); |
442 | for (int i=0;i<tree_list.count();++i) |
445 | for (int i=0;i<tree_list.count();++i) |
443 | mTrees.append(QPair<Tree*, double>(tree_list[i], 0.)); |
446 | mTrees.append(QPair<Tree*, double>(tree_list[i], 0.)); |
444 | }
|
447 | }
|
445 | 448 | ||
446 | // loadFromMap: script access
|
449 | // loadFromMap: script access
|
447 | void Management::loadFromMap(MapGridWrapper *wrap, int key) |
450 | void Management::loadFromMap(MapGridWrapper *wrap, int key) |
448 | {
|
451 | {
|
449 | if (!wrap) { |
452 | if (!wrap) { |
450 | context()->throwError("loadFromMap called with invalid map object!"); |
453 | context()->throwError("loadFromMap called with invalid map object!"); |
451 | return; |
454 | return; |
452 | }
|
455 | }
|
453 | loadFromMap(wrap->map(), key); |
456 | loadFromMap(wrap->map(), key); |
454 | }
|
457 | }
|
455 | 458 | ||
456 | void Management::killSaplings(MapGridWrapper *wrap, int key) |
459 | void Management::killSaplings(MapGridWrapper *wrap, int key) |
457 | {
|
460 | {
|
458 | //MapGridWrapper *wrap = qobject_cast<MapGridWrapper*>(map_grid_object.toQObject());
|
461 | //MapGridWrapper *wrap = qobject_cast<MapGridWrapper*>(map_grid_object.toQObject());
|
459 | //if (!wrap) {
|
462 | //if (!wrap) {
|
460 | // context()->throwError("loadFromMap called with invalid map object!");
|
463 | // context()->throwError("loadFromMap called with invalid map object!");
|
461 | // return;
|
464 | // return;
|
462 | //}
|
465 | //}
|
463 | //loadFromMap(wrap->map(), key);
|
466 | //loadFromMap(wrap->map(), key);
|
464 | // retrieve all sapling trees on the stand:
|
467 | // retrieve all sapling trees on the stand:
|
465 | QList<QPair<ResourceUnitSpecies *, SaplingTree *> > list = wrap->map()->saplingTrees(key); |
468 | QList<QPair<ResourceUnitSpecies *, SaplingTree *> > list = wrap->map()->saplingTrees(key); |
466 | // for now, just kill em all...
|
469 | // for now, just kill em all...
|
467 | for (QList<QPair<ResourceUnitSpecies *, SaplingTree *> >::iterator it = list.begin(); it!=list.end(); ++it) { |
470 | for (QList<QPair<ResourceUnitSpecies *, SaplingTree *> >::iterator it = list.begin(); it!=list.end(); ++it) { |
468 | // (*it).second->pixel = 0;
|
471 | // (*it).second->pixel = 0;
|
469 | (*it).first->changeSapling().clearSapling( *(*it).second, false); // kill and move biomass to soil |
472 | (*it).first->changeSapling().clearSapling( *(*it).second, false); // kill and move biomass to soil |
470 | }
|
473 | }
|
471 | 474 | ||
472 | // the storage for unused/invalid saplingtrees is released lazily (once a year, after growth)
|
475 | // the storage for unused/invalid saplingtrees is released lazily (once a year, after growth)
|
473 | }
|
476 | }
|
474 | 477 | ||
475 | /// specify removal fractions
|
478 | /// specify removal fractions
|
476 | /// @param SWDFrac 0: no change, 1: remove all of standing woody debris
|
479 | /// @param SWDFrac 0: no change, 1: remove all of standing woody debris
|
477 | /// @param DWDfrac 0: no change, 1: remove all of downled woody debris
|
480 | /// @param DWDfrac 0: no change, 1: remove all of downled woody debris
|
478 | /// @param litterFrac 0: no change, 1: remove all of soil litter
|
481 | /// @param litterFrac 0: no change, 1: remove all of soil litter
|
479 | /// @param soilFrac 0: no change, 1: remove all of soil organic matter
|
482 | /// @param soilFrac 0: no change, 1: remove all of soil organic matter
|
480 | void Management::removeSoilCarbon(MapGridWrapper *wrap, int key, double SWDfrac, double DWDfrac, double litterFrac, double soilFrac) |
483 | void Management::removeSoilCarbon(MapGridWrapper *wrap, int key, double SWDfrac, double DWDfrac, double litterFrac, double soilFrac) |
481 | {
|
484 | {
|
482 | if (!(SWDfrac>=0. && SWDfrac<=1. && DWDfrac>=0. && DWDfrac<=1. && soilFrac>=0. && soilFrac<=1. && litterFrac>=0. && litterFrac<=1.)) { |
485 | if (!(SWDfrac>=0. && SWDfrac<=1. && DWDfrac>=0. && DWDfrac<=1. && soilFrac>=0. && soilFrac<=1. && litterFrac>=0. && litterFrac<=1.)) { |
483 | context()->throwError(QString("removeSoilCarbon called with invalid parameters!!\nArgs: %1").arg(context()->argumentsObject().toString())); |
486 | context()->throwError(QString("removeSoilCarbon called with invalid parameters!!\nArgs: %1").arg(context()->argumentsObject().toString())); |
484 | return; |
487 | return; |
485 | }
|
488 | }
|
486 | QList<QPair<ResourceUnit*, double> > ru_areas = wrap->map()->resourceUnitAreas(key); |
489 | QList<QPair<ResourceUnit*, double> > ru_areas = wrap->map()->resourceUnitAreas(key); |
487 | double total_area = 0.; |
490 | double total_area = 0.; |
488 | for (int i=0;i<ru_areas.size();++i) { |
491 | for (int i=0;i<ru_areas.size();++i) { |
489 | ResourceUnit *ru = ru_areas[i].first; |
492 | ResourceUnit *ru = ru_areas[i].first; |
490 | double area_factor = ru_areas[i].second; // 0..1 |
493 | double area_factor = ru_areas[i].second; // 0..1 |
491 | total_area += area_factor; |
494 | total_area += area_factor; |
492 | // swd
|
495 | // swd
|
493 | if (SWDfrac>0.) |
496 | if (SWDfrac>0.) |
494 | ru->snag()->removeCarbon(SWDfrac*area_factor); |
497 | ru->snag()->removeCarbon(SWDfrac*area_factor); |
495 | // soil pools
|
498 | // soil pools
|
496 | ru->soil()->disturbance(DWDfrac*area_factor, litterFrac*area_factor, soilFrac*area_factor); |
499 | ru->soil()->disturbance(DWDfrac*area_factor, litterFrac*area_factor, soilFrac*area_factor); |
497 | // qDebug() << ru->index() << area_factor;
|
500 | // qDebug() << ru->index() << area_factor;
|
498 | }
|
501 | }
|
499 | qDebug() << "total area" << total_area << "of" << wrap->map()->area(key); |
502 | qDebug() << "total area" << total_area << "of" << wrap->map()->area(key); |
500 | }
|
503 | }
|
501 | 504 | ||
502 | /** slash snags (SWD and otherWood-Pools) of polygon \p key on the map \p wrap.
|
505 | /** slash snags (SWD and otherWood-Pools) of polygon \p key on the map \p wrap.
|
503 | The factor is scaled to the overlapping area of \p key on the resource unit.
|
506 | The factor is scaled to the overlapping area of \p key on the resource unit.
|
504 | @param wrap MapGrid to use together with \p key
|
507 | @param wrap MapGrid to use together with \p key
|
505 | @param key ID of the polygon.
|
508 | @param key ID of the polygon.
|
506 | @param slash_fraction 0: no change, 1: 100%
|
509 | @param slash_fraction 0: no change, 1: 100%
|
507 | */
|
510 | */
|
508 | void Management::slashSnags(MapGridWrapper *wrap, int key, double slash_fraction) |
511 | void Management::slashSnags(MapGridWrapper *wrap, int key, double slash_fraction) |
509 | {
|
512 | {
|
510 | if (slash_fraction<0 || slash_fraction>1) { |
513 | if (slash_fraction<0 || slash_fraction>1) { |
511 | context()->throwError(QString("slashSnags called with invalid parameters!!\nArgs: %1").arg(context()->argumentsObject().toString())); |
514 | context()->throwError(QString("slashSnags called with invalid parameters!!\nArgs: %1").arg(context()->argumentsObject().toString())); |
512 | return; |
515 | return; |
513 | }
|
516 | }
|
514 | QList<QPair<ResourceUnit*, double> > ru_areas = wrap->map()->resourceUnitAreas(key); |
517 | QList<QPair<ResourceUnit*, double> > ru_areas = wrap->map()->resourceUnitAreas(key); |
515 | double total_area = 0.; |
518 | double total_area = 0.; |
516 | for (int i=0;i<ru_areas.size();++i) { |
519 | for (int i=0;i<ru_areas.size();++i) { |
517 | ResourceUnit *ru = ru_areas[i].first; |
520 | ResourceUnit *ru = ru_areas[i].first; |
518 | double area_factor = ru_areas[i].second; // 0..1 |
521 | double area_factor = ru_areas[i].second; // 0..1 |
519 | total_area += area_factor; |
522 | total_area += area_factor; |
520 | ru->snag()->management(slash_fraction * area_factor); |
523 | ru->snag()->management(slash_fraction * area_factor); |
521 | // qDebug() << ru->index() << area_factor;
|
524 | // qDebug() << ru->index() << area_factor;
|
522 | }
|
525 | }
|
523 | qDebug() << "total area" << total_area << "of" << wrap->map()->area(key); |
526 | qDebug() << "total area" << total_area << "of" << wrap->map()->area(key); |
524 | 527 | ||
525 | }
|
528 | }
|
526 | 529 | ||
527 | /** loadFromMap selects trees located on pixels with value 'key' within the grid 'map_grid'.
|
530 | /** loadFromMap selects trees located on pixels with value 'key' within the grid 'map_grid'.
|
528 | */
|
531 | */
|
529 | void Management::loadFromMap(const MapGrid *map_grid, int key) |
532 | void Management::loadFromMap(const MapGrid *map_grid, int key) |
530 | {
|
533 | {
|
531 | if (!map_grid) { |
534 | if (!map_grid) { |
532 | qDebug() << "invalid parameter for Management::loadFromMap: Map expected!"; |
535 | qDebug() << "invalid parameter for Management::loadFromMap: Map expected!"; |
533 | return; |
536 | return; |
534 | }
|
537 | }
|
535 | if (map_grid->isValid()) { |
538 | if (map_grid->isValid()) { |
536 | QList<Tree*> tree_list = map_grid->trees(key); |
539 | QList<Tree*> tree_list = map_grid->trees(key); |
537 | loadFromTreeList( tree_list ); |
540 | loadFromTreeList( tree_list ); |
538 | } else { |
541 | } else { |
539 | qDebug() << "Management::loadFromMap: grid is not valid - no trees loaded"; |
542 | qDebug() << "Management::loadFromMap: grid is not valid - no trees loaded"; |
540 | }
|
543 | }
|
541 | 544 | ||
542 | }
|
545 | }
|
543 | 546 | ||
544 | bool treePairValue(const QPair<Tree*, double> &p1, const QPair<Tree*, double> &p2) |
547 | bool treePairValue(const QPair<Tree*, double> &p1, const QPair<Tree*, double> &p2) |
545 | {
|
548 | {
|
546 | return p1.second < p2.second; |
549 | return p1.second < p2.second; |
547 | }
|
550 | }
|
548 | 551 | ||
549 | void Management::sort(QString statement) |
552 | void Management::sort(QString statement) |
550 | {
|
553 | {
|
551 | TreeWrapper tw;
|
554 | TreeWrapper tw;
|
552 | Expression sorter(statement, &tw); |
555 | Expression sorter(statement, &tw); |
553 | // fill the "value" part of the tree storage with a value for each tree
|
556 | // fill the "value" part of the tree storage with a value for each tree
|
554 | for (int i=0;i<mTrees.count(); ++i) { |
557 | for (int i=0;i<mTrees.count(); ++i) { |
555 | tw.setTree(mTrees.at(i).first); |
558 | tw.setTree(mTrees.at(i).first); |
556 | mTrees[i].second = sorter.execute(); |
559 | mTrees[i].second = sorter.execute(); |
557 | }
|
560 | }
|
558 | // now sort the list....
|
561 | // now sort the list....
|
559 | qSort(mTrees.begin(), mTrees.end(), treePairValue); |
562 | qSort(mTrees.begin(), mTrees.end(), treePairValue); |
560 | }
|
563 | }
|
561 | 564 | ||
562 | double Management::percentile(int pct) |
565 | double Management::percentile(int pct) |
563 | {
|
566 | {
|
564 | if (mTrees.count()==0) |
567 | if (mTrees.count()==0) |
565 | return -1.; |
568 | return -1.; |
566 | int idx = int( (pct/100.) * mTrees.count()); |
569 | int idx = int( (pct/100.) * mTrees.count()); |
567 | if (idx>=0 && idx<mTrees.count()) |
570 | if (idx>=0 && idx<mTrees.count()) |
568 | return mTrees.at(idx).second; |
571 | return mTrees.at(idx).second; |
569 | else
|
572 | else
|
570 | return -1; |
573 | return -1; |
571 | }
|
574 | }
|
572 | 575 | ||
573 | /// random shuffle of all trees in the list
|
576 | /// random shuffle of all trees in the list
|
574 | void Management::randomize() |
577 | void Management::randomize() |
575 | {
|
578 | {
|
576 | // fill the "value" part of the tree storage with a random value for each tree
|
579 | // fill the "value" part of the tree storage with a random value for each tree
|
577 | for (int i=0;i<mTrees.count(); ++i) { |
580 | for (int i=0;i<mTrees.count(); ++i) { |
578 | mTrees[i].second = drandom(); |
581 | mTrees[i].second = drandom(); |
579 | }
|
582 | }
|
580 | // now sort the list....
|
583 | // now sort the list....
|
581 | qSort(mTrees.begin(), mTrees.end(), treePairValue); |
584 | qSort(mTrees.begin(), mTrees.end(), treePairValue); |
582 | 585 | ||
583 | }
|
586 | }
|
584 | 587 | ||
585 | 588 | ||
586 | 589 | ||
587 | 590 | ||
588 | 591 | ||
589 | 592 |