/**
* @class harvest
* @memberof abe-lib
*/
/**
* Disturbance response and salvaging
* @method salvage
* @param {object} options
* @param {string} options.id A unique identifier for the salvage activity (default: 'Salvage').
* @param {object} options.schedule default is simply repeating operation, but can be overwritten.
* @param {expression|undefined} options.salvageCondition expression that evaluates for each disturbed tree whether to salvage or not.
* If empty, no salvaging of trees takes place. Default: undefined.
* @param {number} options.thresholdReset Value between 0 and 1 indicating a disturbance severity threshold. If disturbance is above
* the threhold, the stand is reset (see other options). What happens depends
* on the options `clearAll`, `onClear` and `sendSignal`. Default: 0.5
* @param {boolean|undefined} options.clearAll If a stand is reset, a simple clearcut is executed when `clearAll` is `true`. Default: false
* @param {function|undefined} options.onClear If a stand is reset, this custom function is executed (if provided). Default: undefined
* @param {number} options.thresholdIgnoreDamage threshold m3 volume/ha. if disturbance below, no further tests are executed. Default: 50
* @param {string|undefined} options.sendSignal If a stand is reset, then this signal is sent if a value is provided. Default: undefined
* @return {object} - An object describing the salvage activity
* @example
* lib.harvest.salvage({ salvageCondition: 'dbh>10',
* thresholdReset: 0.5,
* clearAll: true });
*
*/
lib.harvest.salvage = function(options) {
// 1. Default Options
const defaultOptions = {
id: 'Salvage',
schedule: { repeat: true },
salvageCondition: undefined,
thresholdReset: 0.5,
clearAll: undefined,
onClear: undefined,
thresholdIgnoreDamage: 50, // threshold m3 volume/ha. if disturbance below, no further tests are executed
sendSignal: undefined,
// ... add other default parameters
};
const opts = lib.mergeOptions(defaultOptions, options || {});
// use the onExecuteHandler if provided:
var onExecuteHandler = opts.onClear;
var description = `Salvage operation. `;
if (onExecuteHandler === undefined) {
if (opts.clearAll === true) {
description += "When cleared: simple clearcut ";
if (opts.sendSignal !== undefined) {
// clearcut and signal
description += `and signal '${opts.sendSignal} '`;
onExecuteHandler = function() {
// simple clear-cut: remove all trees > 4m
const n_trees = stand.trees.loadAll();
stand.trees.harvest();
stand.stp.signal(opts.sendSignal);
lib.log(`clearcut after disturbance. Harvested ${n_trees} trees. Sent signal ${opts.sendSignal}`);
//lib.activityLog('clearcut and signal after disturbance');
};
} else {
// only clearcut
onExecuteHandler = function() {
// simple clear-cut: remove all trees > 4m
const n_trees = stand.trees.loadAll();
stand.trees.harvest();
lib.log(`clearcut after disturbance. Harvested ${n_trees} trees.`);
//lib.activityLog('clearcut after disturbance');
};
}
} else if (opts.sendSignal !== undefined) {
// only signal
description += `When cleared: signal '${opts.sendSignal} '`;
onExecuteHandler = function() {
// send a specific signal instead of specifying code to reset the stand
lib.log(`Sent signal ${opts.sendSignal} after disturbance.`);
stand.stp.signal(opts.sendSignal);
//lib.activityLog('signal after disturbance');
}
}
} else {
description += "When cleared: custom function";
}
if (onExecuteHandler === undefined && opts.thresholdReset < 1) {
throw new Error('harvest.salvage: You need to specify what to do in case the stand should be cleared!');
}
if (opts.onClear !== undefined && opts.clearAll !== undefined) {
throw new Error('harvest.salvage: You specified onClear and clearALL! Please choose only one way.');
};
return {
id: opts.id,
type: 'salvage',
schedule: opts.schedule,
onExecute: onExecuteHandler,
disturbanceCondition: opts.disturbanceCondition,
thresholdSplitStand: 10, // never split stands
thresholdIgnoreDamage: opts.thresholdIgnoreDamage,
thresholdClearStand: opts.thresholdReset,
debugSplit: false,
maxPrepone: 0,
description: description
};
} // end lib.harvest.salvage