Rev 566 | Rev 579 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | |||
185 | werner | 2 | #include "global.h" |
3 | #include "management.h" |
||
4 | #include "helper.h" |
||
186 | werner | 5 | #include "model.h" |
189 | iland | 6 | #include "resourceunit.h" |
186 | werner | 7 | #include "tree.h" |
216 | werner | 8 | #include "expressionwrapper.h" |
564 | werner | 9 | #include "sapling.h" |
566 | werner | 10 | #include "soil.h" |
186 | werner | 11 | |
242 | werner | 12 | #include "climateconverter.h" |
245 | werner | 13 | #include "csvfile.h" |
247 | werner | 14 | #include "scriptglobal.h" |
552 | werner | 15 | #include "mapgrid.h" |
186 | werner | 16 | |
185 | werner | 17 | #include <QtScript> |
216 | werner | 18 | #include <QTextEdit> |
19 | QObject *Management::scriptOutput = 0; |
||
185 | werner | 20 | |
216 | werner | 21 | QScriptValue script_debug(QScriptContext *ctx, QScriptEngine *eng) |
294 | werner | 22 | { |
23 | QString value; |
||
24 | for (int i = 0; i < ctx->argumentCount(); ++i) { |
||
25 | if (i > 0) |
||
26 | value.append(" "); |
||
27 | value.append(ctx->argument(i).toString()); |
||
28 | } |
||
29 | if (Management::scriptOutput) { |
||
30 | QTextEdit *e = qobject_cast<QTextEdit*>(Management::scriptOutput); |
||
31 | if (e) |
||
32 | e->append(value); |
||
33 | } else { |
||
34 | qDebug() << "Script:" << value; |
||
35 | } |
||
36 | return eng->undefinedValue(); |
||
37 | } |
||
186 | werner | 38 | |
294 | werner | 39 | QScriptValue script_include(QScriptContext *ctx, QScriptEngine *eng) |
40 | { |
||
552 | werner | 41 | |
294 | werner | 42 | QString fileName = ctx->argument(0).toString(); |
43 | QString path =GlobalSettings::instance()->path(fileName, "script") ; |
||
44 | QString includeFile=Helper::loadTextFile(path); |
||
552 | werner | 45 | |
46 | ctx->setActivationObject(ctx->parentContext()->activationObject()); |
||
47 | ctx->setThisObject(ctx->parentContext()->thisObject()); |
||
48 | |||
49 | QScriptValue ret = eng->evaluate(includeFile, fileName); |
||
294 | werner | 50 | if (eng->hasUncaughtException()) |
51 | qDebug() << "Error in include:" << eng->uncaughtException().toString(); |
||
552 | werner | 52 | return ret; |
294 | werner | 53 | } |
54 | |||
55 | QScriptValue script_alert(QScriptContext *ctx, QScriptEngine *eng) |
||
56 | { |
||
57 | QString value = ctx->argument(0).toString(); |
||
58 | Helper::msg(value); |
||
59 | return eng->undefinedValue(); |
||
60 | } |
||
216 | werner | 61 | // global output function |
62 | QString Management::executeScript(QString cmd) |
||
63 | { |
||
64 | DebugTimer t("execute javascript"); |
||
65 | if (mEngine) |
||
66 | mEngine->evaluate(cmd); |
||
295 | werner | 67 | if (mEngine->hasUncaughtException()) { |
321 | werner | 68 | //int line = mEngine->uncaughtExceptionLineNumber(); |
295 | werner | 69 | QString msg = QString( "Script Error occured: %1\n").arg( mEngine->uncaughtException().toString()); |
70 | msg+=mEngine->uncaughtExceptionBacktrace().join("\n"); |
||
71 | return msg; |
||
72 | } else { |
||
216 | werner | 73 | return QString(); |
295 | werner | 74 | } |
216 | werner | 75 | } |
76 | |||
185 | werner | 77 | Management::Management() |
78 | { |
||
564 | werner | 79 | // setup the scripting engine |
185 | werner | 80 | mEngine = new QScriptEngine(); |
81 | QScriptValue objectValue = mEngine->newQObject(this); |
||
216 | werner | 82 | QScriptValue dbgprint = mEngine->newFunction(script_debug); |
294 | werner | 83 | QScriptValue sinclude = mEngine->newFunction(script_include); |
84 | QScriptValue alert = mEngine->newFunction(script_alert); |
||
185 | werner | 85 | mEngine->globalObject().setProperty("management", objectValue); |
216 | werner | 86 | mEngine->globalObject().setProperty("print",dbgprint); |
294 | werner | 87 | mEngine->globalObject().setProperty("include",sinclude); |
88 | mEngine->globalObject().setProperty("alert", alert); |
||
247 | werner | 89 | |
90 | // globals object: instatiate here, but ownership goes to script engine |
||
91 | ScriptGlobal *global = new ScriptGlobal(); |
||
92 | QScriptValue glb = mEngine->newQObject(global,QScriptEngine::ScriptOwnership); |
||
93 | mEngine->globalObject().setProperty("Globals", glb); |
||
242 | werner | 94 | // other object types |
95 | ClimateConverter::addToScriptEngine(*mEngine); |
||
245 | werner | 96 | CSVFile::addToScriptEngine(*mEngine); |
552 | werner | 97 | MapGridWrapper::addToScriptEngine(*mEngine); |
216 | werner | 98 | |
564 | werner | 99 | // default values for removal fractions |
100 | // 100% of the stem, 0% of foliage and branches |
||
101 | mRemoveFoliage = 0.; |
||
102 | mRemoveBranch = 0.; |
||
103 | mRemoveStem = 1.; |
||
247 | werner | 104 | |
564 | werner | 105 | |
185 | werner | 106 | } |
107 | |||
108 | Management::~Management() |
||
109 | { |
||
110 | delete mEngine; |
||
111 | } |
||
112 | |||
113 | void Management::loadScript(const QString &fileName) |
||
114 | { |
||
216 | werner | 115 | mScriptFile = fileName; |
185 | werner | 116 | QString program = Helper::loadTextFile(fileName); |
117 | if (program.isEmpty()) |
||
118 | return; |
||
119 | |||
120 | mEngine->evaluate(program); |
||
121 | qDebug() << "management script loaded"; |
||
122 | if (mEngine->hasUncaughtException()) |
||
123 | qDebug() << "Script Error occured: " << mEngine->uncaughtExceptionBacktrace(); |
||
124 | |||
125 | } |
||
126 | |||
566 | werner | 127 | int Management::remain(int number) |
185 | werner | 128 | { |
129 | qDebug() << "remain called (number): " << number; |
||
186 | werner | 130 | Model *m = GlobalSettings::instance()->model(); |
131 | AllTreeIterator at(m); |
||
132 | QList<Tree*> trees; |
||
133 | while (Tree *t=at.next()) |
||
134 | trees.push_back(t); |
||
135 | int to_kill = trees.count() - number; |
||
136 | qDebug() << trees.count() << " standing, targetsize" << number << ", hence " << to_kill << "trees to remove"; |
||
137 | for (int i=0;i<to_kill;i++) { |
||
349 | werner | 138 | int index = irandom(0, trees.count()-1); |
278 | werner | 139 | trees[index]->remove(); |
186 | werner | 140 | trees.removeAt(index); |
141 | } |
||
142 | mRemoved += to_kill; |
||
566 | werner | 143 | return to_kill; |
185 | werner | 144 | } |
145 | |||
146 | |||
564 | werner | 147 | int Management::kill() |
252 | werner | 148 | { |
564 | werner | 149 | int c = mTrees.count(); |
252 | werner | 150 | for (int i=0;i<mTrees.count();i++) |
278 | werner | 151 | mTrees[i].first->remove(); |
252 | werner | 152 | mTrees.clear(); |
564 | werner | 153 | return c; |
252 | werner | 154 | } |
155 | |||
564 | werner | 156 | int Management::remove_percentiles(int pctfrom, int pctto, int number, bool management) |
216 | werner | 157 | { |
158 | if (mTrees.isEmpty()) |
||
159 | return 0; |
||
160 | int index_from = limit(int(pctfrom/100. * mTrees.count()), 0, mTrees.count()); |
||
346 | werner | 161 | int index_to = limit(int(pctto/100. * mTrees.count()), 0, mTrees.count()-1); |
216 | werner | 162 | if (index_from>=index_to) |
163 | return 0; |
||
217 | werner | 164 | qDebug() << "attempting to remove" << number << "trees between indices" << index_from << "and" << index_to; |
216 | werner | 165 | int i; |
166 | int count = number; |
||
217 | werner | 167 | if (index_to-index_from <= number) { |
216 | werner | 168 | // kill all |
564 | werner | 169 | if (management) { |
170 | // management |
||
171 | for (i=index_from; i<index_to; i++) |
||
172 | mTrees.at(i).first->remove(removeFoliage(), removeBranch(), removeStem()); |
||
173 | } else { |
||
174 | // just kill... |
||
175 | for (i=index_from; i<index_to; i++) |
||
176 | mTrees.at(i).first->remove(); |
||
177 | } |
||
216 | werner | 178 | count = index_to - index_from; |
179 | } else { |
||
180 | // kill randomly the provided number |
||
181 | int cancel = 1000; |
||
182 | while(number>=0) { |
||
183 | int rnd_index = irandom(index_from, index_to); |
||
184 | if (mTrees[rnd_index].first->isDead()) { |
||
185 | if (--cancel<0) { |
||
186 | qDebug() << "Management::kill: canceling search." << number << "trees left."; |
||
217 | werner | 187 | count-=number; // not all trees were killed |
216 | werner | 188 | break; |
189 | } |
||
190 | continue; |
||
191 | } |
||
192 | cancel = 1000; |
||
193 | number--; |
||
564 | werner | 194 | if (management) { |
195 | mTrees[rnd_index].first->remove( removeFoliage(), removeBranch(), removeStem() ); |
||
196 | } else { |
||
197 | mTrees[rnd_index].first->remove(); |
||
198 | } |
||
216 | werner | 199 | } |
200 | } |
||
217 | werner | 201 | qDebug() << count << "removed."; |
564 | werner | 202 | // clean up the tree list... |
203 | for (int i=mTrees.count()-1; i>=0; --i) { |
||
204 | if (mTrees[i].first->isDead()) |
||
205 | mTrees.removeAt(i); |
||
206 | } |
||
207 | return count; // killed or manages |
||
216 | werner | 208 | } |
209 | |||
564 | werner | 210 | // from the range percentile range pctfrom to pctto (each 1..100) |
211 | int Management::kill(int pctfrom, int pctto, int number) |
||
212 | { |
||
213 | return remove_percentiles(pctfrom, pctto, number, false); |
||
214 | } |
||
215 | |||
216 | // from the range percentile range pctfrom to pctto (each 1..100) |
||
217 | int Management::manage(int pctfrom, int pctto, int number) |
||
218 | { |
||
219 | return remove_percentiles(pctfrom, pctto, number, true); |
||
220 | } |
||
221 | |||
222 | int Management::manage() |
||
223 | { |
||
224 | int c = mTrees.count(); |
||
225 | for (int i=0;i<mTrees.count();i++) |
||
226 | mTrees[i].first->remove(removeFoliage(), |
||
227 | removeBranch(), |
||
228 | removeStem()); // remove with current removal fractions |
||
229 | mTrees.clear(); |
||
230 | return c; |
||
231 | } |
||
232 | |||
233 | |||
234 | |||
185 | werner | 235 | void Management::run() |
236 | { |
||
216 | werner | 237 | mTrees.clear(); |
186 | werner | 238 | mRemoved=0; |
185 | werner | 239 | qDebug() << "Management::run() called"; |
240 | QScriptValue mgmt = mEngine->globalObject().property("manage"); |
||
241 | int year = GlobalSettings::instance()->currentYear(); |
||
242 | mgmt.call(QScriptValue(), QScriptValueList()<<year); |
||
243 | if (mEngine->hasUncaughtException()) |
||
244 | qDebug() << "Script Error occured: " << mEngine->uncaughtExceptionBacktrace(); |
||
245 | |||
186 | werner | 246 | if (mRemoved>0) { |
187 | iland | 247 | foreach(ResourceUnit *ru, GlobalSettings::instance()->model()->ruList()) |
186 | werner | 248 | ru->cleanTreeList(); |
249 | } |
||
185 | werner | 250 | } |
216 | werner | 251 | |
250 | werner | 252 | int Management::filter(QVariantList idList) |
253 | { |
||
389 | werner | 254 | QVector<int> ids; |
250 | werner | 255 | foreach(const QVariant &v, idList) |
389 | werner | 256 | if (!v.isNull()) |
257 | ids << v.toInt(); |
||
258 | // QHash<int, int> ids; |
||
259 | // foreach(const QVariant &v, idList) |
||
260 | // ids[v.toInt()] = 1; |
||
216 | werner | 261 | |
250 | werner | 262 | QList<QPair<Tree*, double> >::iterator tp=mTrees.begin(); |
263 | while (tp!=mTrees.end()) { |
||
264 | if (!ids.contains(tp->first->id()) ) |
||
265 | tp = mTrees.erase(tp); |
||
266 | else |
||
267 | tp++; |
||
268 | } |
||
389 | werner | 269 | qDebug() << "Management::filter by id-list:" << mTrees.count(); |
250 | werner | 270 | return mTrees.count(); |
271 | } |
||
272 | |||
273 | int Management::filter(QString filter) |
||
274 | { |
||
275 | TreeWrapper tw; |
||
276 | Expression expr(filter,&tw); |
||
575 | werner | 277 | int n_before = mTrees.count(); |
250 | werner | 278 | QList<QPair<Tree*, double> >::iterator tp=mTrees.begin(); |
575 | werner | 279 | try { |
280 | while (tp!=mTrees.end()) { |
||
281 | tw.setTree(tp->first); |
||
282 | if (!expr.calculate(tw)) |
||
283 | tp = mTrees.erase(tp); |
||
284 | else |
||
285 | tp++; |
||
286 | } |
||
287 | } catch(const IException &e) { |
||
288 | context()->throwError(e.message()); |
||
250 | werner | 289 | } |
575 | werner | 290 | |
291 | qDebug() << "filtering with" << filter << "N=" << n_before << "/" << mTrees.count() << "trees (before/after filtering)."; |
||
250 | werner | 292 | return mTrees.count(); |
293 | } |
||
294 | |||
294 | werner | 295 | int Management::load(int ruindex) |
296 | { |
||
297 | Model *m = GlobalSettings::instance()->model(); |
||
298 | ResourceUnit *ru = m->ru(ruindex); |
||
299 | if (!ru) |
||
300 | return -1; |
||
301 | mTrees.clear(); |
||
302 | for (int i=0;i<ru->trees().count();i++) |
||
303 | mTrees.push_back(QPair<Tree*,double>(ru->tree(i), 0.)); |
||
304 | return mTrees.count(); |
||
305 | } |
||
306 | |||
216 | werner | 307 | int Management::load(QString filter) |
308 | { |
||
309 | TreeWrapper tw; |
||
310 | Model *m = GlobalSettings::instance()->model(); |
||
311 | mTrees.clear(); |
||
312 | AllTreeIterator at(m); |
||
313 | if (filter.isEmpty()) { |
||
314 | while (Tree *t=at.next()) |
||
315 | if (!t->isDead()) |
||
316 | mTrees.push_back(QPair<Tree*, double>(t, 0.)); |
||
317 | } else { |
||
318 | Expression expr(filter,&tw); |
||
319 | qDebug() << "filtering with" << filter; |
||
320 | while (Tree *t=at.next()) { |
||
321 | tw.setTree(t); |
||
322 | if (!t->isDead() && expr.execute()) |
||
323 | mTrees.push_back(QPair<Tree*, double>(t, 0.)); |
||
324 | } |
||
325 | } |
||
326 | return mTrees.count(); |
||
327 | } |
||
328 | |||
555 | werner | 329 | /** |
330 | */ |
||
544 | werner | 331 | void Management::loadFromTreeList(QList<Tree*>tree_list) |
332 | { |
||
333 | mTrees.clear(); |
||
334 | for (int i=0;i<tree_list.count();++i) |
||
335 | mTrees.append(QPair<Tree*, double>(tree_list[i], 0.)); |
||
336 | } |
||
337 | |||
555 | werner | 338 | // loadFromMap: script access |
339 | void Management::loadFromMap(QScriptValue map_grid_object, int key) |
||
552 | werner | 340 | { |
555 | werner | 341 | MapGridWrapper *wrap = qobject_cast<MapGridWrapper*>(map_grid_object.toQObject()); |
342 | if (!wrap) { |
||
343 | context()->throwError("loadFromMap called with invalid map object!"); |
||
344 | return; |
||
345 | } |
||
346 | loadFromMap(wrap->map(), key); |
||
347 | } |
||
348 | |||
564 | werner | 349 | void Management::killSaplings(QScriptValue map_grid_object, int key) |
350 | { |
||
351 | MapGridWrapper *wrap = qobject_cast<MapGridWrapper*>(map_grid_object.toQObject()); |
||
352 | if (!wrap) { |
||
353 | context()->throwError("loadFromMap called with invalid map object!"); |
||
354 | return; |
||
355 | } |
||
356 | //loadFromMap(wrap->map(), key); |
||
357 | // retrieve all sapling trees on the stand: |
||
358 | QList<QPair<ResourceUnitSpecies *, SaplingTree *> > list = wrap->map()->saplingTrees(key); |
||
359 | // for now, just kill em all... |
||
360 | for (QList<QPair<ResourceUnitSpecies *, SaplingTree *> >::iterator it = list.begin(); it!=list.end(); ++it) |
||
361 | (*it).second->pixel = 0; |
||
362 | // the storage for unused/invalid saplingtrees is released lazily (once a year, after growth) |
||
363 | } |
||
364 | |||
566 | werner | 365 | void Management::removeSoilCarbon(QScriptValue map_grid_object, int key, double SWDfrac, double DWDfrac, double litterFrac, double soilFrac) |
565 | werner | 366 | { |
367 | MapGridWrapper *wrap = qobject_cast<MapGridWrapper*>(map_grid_object.toQObject()); |
||
368 | if (!wrap) { |
||
369 | context()->throwError("loadFromMap called with invalid map object!"); |
||
370 | return; |
||
371 | } |
||
372 | QList<QPair<ResourceUnit*, double> > ru_areas = wrap->map()->resourceUnitAreas(key); |
||
373 | double total_area = 0.; |
||
374 | for (int i=0;i<ru_areas.size();++i) { |
||
375 | ResourceUnit *ru = ru_areas[i].first; |
||
376 | double area_factor = ru_areas[i].second; // 0..1 |
||
377 | total_area += area_factor; |
||
566 | werner | 378 | // swd |
379 | ru->snag()->removeCarbon(1. - SWDfrac*area_factor); |
||
380 | // soil pools |
||
381 | ru->soil()->disturbance(DWDfrac*area_factor, litterFrac*area_factor, soilFrac*area_factor); |
||
565 | werner | 382 | qDebug() << ru->index() << area_factor; |
383 | } |
||
384 | qDebug() << "total area" << total_area << "of" << wrap->map()->area(key); |
||
385 | } |
||
386 | |||
555 | werner | 387 | /** loadFromMap selects trees located on pixels with value 'key' within the grid 'map_grid'. |
388 | */ |
||
389 | void Management::loadFromMap(const MapGrid *map_grid, int key) |
||
390 | { |
||
391 | if (!map_grid) { |
||
552 | werner | 392 | qDebug() << "invalid parameter for Management::loadFromMap: Map expected!"; |
393 | return; |
||
394 | } |
||
555 | werner | 395 | if (map_grid->isValid()) { |
396 | QList<Tree*> tree_list = map_grid->trees(key); |
||
552 | werner | 397 | loadFromTreeList( tree_list ); |
398 | } else { |
||
399 | qDebug() << "Management::loadFromMap: grid is not valid - no trees loaded"; |
||
400 | } |
||
401 | |||
402 | } |
||
403 | |||
216 | werner | 404 | bool treePairValue(const QPair<Tree*, double> &p1, const QPair<Tree*, double> &p2) |
405 | { |
||
406 | return p1.second < p2.second; |
||
407 | } |
||
408 | |||
409 | void Management::sort(QString statement) |
||
410 | { |
||
411 | TreeWrapper tw; |
||
412 | Expression sorter(statement, &tw); |
||
413 | // fill the "value" part of the tree storage with a value for each tree |
||
414 | for (int i=0;i<mTrees.count(); ++i) { |
||
415 | tw.setTree(mTrees.at(i).first); |
||
416 | mTrees[i].second = sorter.execute(); |
||
417 | } |
||
418 | // now sort the list.... |
||
419 | qSort(mTrees.begin(), mTrees.end(), treePairValue); |
||
420 | } |
||
421 | |||
422 | double Management::percentile(int pct) |
||
423 | { |
||
424 | if (mTrees.count()==0) |
||
425 | return -1.; |
||
426 | int idx = int( (pct/100.) * mTrees.count()); |
||
427 | if (idx>=0 && idx<mTrees.count()) |
||
428 | return mTrees.at(idx).second; |
||
429 | else |
||
430 | return -1; |
||
431 | } |
||
564 | werner | 432 |