V2_0.java [src/java/d/soils/wwe02_wepssoilinput] Revision: default Date:
/*
* $Id$
*
* This file is part of the Cloud Services Integration Platform (CSIP),
* a Model-as-a-Service framework, API, and application suite.
*
* 2012-2017, OMSLab, Colorado State University.
*
* OMSLab licenses this file to you under the MIT license.
* See the LICENSE file in the project root for more information.
*/
package d.soils.wwe02_wepssoilinput;
import static adb.DBResources.R2GIS_SQLSVR;
import csip.Config;
import csip.ModelDataService;
import csip.api.server.ServiceException;
import csip.annotations.Resource;
import csip.utils.Validation;
import d.util.IFCWriter;
import d.util.OrganicSoilException;
import d.util.SoilCalc;
import d.util.Utils;
import java.io.File;
import java.util.Iterator;
import java.util.logging.Level;
import javax.ws.rs.Path;
import csip.annotations.Description;
import csip.annotations.Name;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.commons.io.FileUtils;
import soils.Component;
import soils.Horizon;
import soils.MapUnit;
import static soils.db.DBResources.SDM;
import soils.db.SOILS_DATA;
import soils.db.SOILS_DB_Factory;
import soils.utils.EvalResult;
/**
*
* @author brad
* @author <a href="mailto:shaun.case@colostate.edu">Shaun Case</a>
*/
@Name("WWE-02: WEPS Soil Input IFC (wepssoilinput)")
@Description("Gets data from the NRCS Soil Data Mart and generates a soil IFC input file for the WEPS model")
@Path("d/wepssoilinput/2.0")
@Resource(from = soils.db.DBResources.class)
public class V2_0 extends ModelDataService {
protected static final String KEY_COKEY = "cokey";
protected static final String STREAM_FILE = "stream_file";
protected static final String DEFAULT_SDM_SOURCE = "SDM_REST";
protected boolean streamFile = false;
protected Component comp = null;
protected MapUnit mapUnit = null;
protected File ifcFile = null;
protected String fileOut = "";
protected static final StratifiedTextures STRATIFIED_TEXTURES = StratifiedTextures.getInstance();
protected static final String KEY_AVG_STRATIFIED = "avg_stratified_layers";
protected static final String KEY_MAX_ORGANIC_DEPTH = "max_organic_depth";
public static final String KEY_ORGANIC_LAYER_THRESHOLD = "organic_layer_threshold";
protected static final String KEY_CHECK_ORGANIC_SURFACE = "check_organic_surface";
protected static final String[] TEXTURE_CHK = {"by", "cem", "ce", "dur", "frag", "gyp", "hpm", "ind", "marl", "mat",
"mpm", "muck", "mpt", "or", "opwd",
"peat", "pc", "pf", "pgp", "pum", "spm", "st", "udom",
"u", "uwb", "var", "w", "wb", "cem-", "pf-"};
protected final String KEY_ESTIMATE_NULLS = "estimate_nulls";
@Override
protected void preProcess() throws Exception {
String cokey = parameter().getString(KEY_COKEY);
streamFile = parameter().getBoolean(STREAM_FILE, false);
Validation.checkCokey(cokey);
comp = new Component();
comp.cokey(cokey);
mapUnit = new MapUnit(comp);
}
@Override
protected Map<String, Object> getConfigInfo() {
return new LinkedHashMap<String, Object>() {
{
put("soils.gis.database.source", resources().getResolved("soils.gis.database.source"));
put(SDM, resources().getResolved(SDM));
put(R2GIS_SQLSVR, resources().getResolved(R2GIS_SQLSVR));
put("fpp.version", "wwe-02 2.0");
}
};
}
@Override
protected void doProcess() throws Exception, ServiceException {
try (SOILS_DATA soilsDb = SOILS_DB_Factory.createEngine(getClass(), LOG, Config.getString("soils.gis.database.source", DEFAULT_SDM_SOURCE))) {
if (soilsDb.validateComponent(Integer.parseInt(comp.cokey()))) {
if (soilsDb.findWEPSHorizonsForCokey(mapUnit, comp)) {
averageMissingValues(comp); //In the WEPS code this is done as the values come out so I am doing it as soon as possible.
} else {
throw new ServiceException("Error executing SoilsDb::findWEPSHorizonsForCokey() function. Check logfile for more details.");
}
} else {
throw new ServiceException("The cokey value provided does not exist in the SDM database. Cannot continue.");
}
}
if (mapUnit.isExcluded()) {
throw new ServiceException("MapUnit: " + mapUnit.muname() + " has been excluded. Reason: " + comp.getExcludedReason());
}
if (comp.isExcluded()) {
throw new ServiceException("Component: " + comp.compname() + " has been excluded. Reason: " + comp.getExcludedReason());
}
ifcFile = ((streamFile) ? null : (workspace().getFile(buildFileName(comp))));
IFCWriter writer = new IFCWriter();
if (organicCheck(comp)) {
fileOut = writer.getDefaultOrganicFileData();
} else {
if (Config.getBoolean(KEY_AVG_STRATIFIED, true)) {
SoilCalc.averageStratifiedLayers(comp, STRATIFIED_TEXTURES);
}
testData(mapUnit, comp); // have to do this before converting units, I think it should be after but this is how WEPS does it
SoilCalc.convertUnits(mapUnit, comp);
if (Config.getBoolean(KEY_CHECK_ORGANIC_SURFACE, false)) {
checkOrganicSurface(comp);
}
removeBadLayers(comp);
fileOut = writer.getIFCFileData(mapUnit, comp);
}
if (!streamFile && (null != ifcFile)) {
FileUtils.writeStringToFile(ifcFile, fileOut, "UTF-8");
}
}
@Override
protected void postProcess() throws Exception {
if (!streamFile && ifcFile.exists()) {
results().put(ifcFile);
} else {
if (!streamFile) {
throw new ServiceException("File " + ifcFile.getName() + " could not be created.");
} else {
if ((null != fileOut) && (!fileOut.isEmpty()) && (fileOut.length() > 0)) {
results().put(comp.compname(), fileOut);
} else {
throw new ServiceException("Resulting ifc file was empty, cannot proceed");
}
}
}
}
protected String buildFileName(Component comp) {
return comp.compname() + ".ifc";
}
protected boolean organicCheck(Component comp) {
if (comp.taxorder().equalsIgnoreCase("Histosols")) {
return true;
}
// assuming the first horizon is the top because the sql orders by deptht_r
if (Utils.getSurfaceHorizon(comp).hzdept_r() == 0
&& Utils.getSurfaceHorizon(comp).hzdepb_r() > 10 && Utils.getSurfaceHorizon(comp).om_r() > 15) {
return true;
}
return false;
}
protected void checkOrganicSurface(Component comp) {
double maxOrganicDepth = Config.getDouble(KEY_MAX_ORGANIC_DEPTH, 10) * 10; //convert to mm
int i = 0;
Iterator<Horizon> it = comp.horizons.values().iterator();
while (it.hasNext()) {
Horizon h = it.next();
if (Utils.isLayerOrganic(h.hzname(), h.desgnmaster(), SoilCalc.getSurfaceTexture(comp), h.om_r())) {
if (h.hzdepb_r() <= maxOrganicDepth) {
it.remove(); // save remove
} else if (i == 0) {
throw new OrganicSoilException("The organic surface layer depth exceeded the maximum ignorable depth.\n"
+ "hzdepb_r=" + h.hzdepb_r() + " max organic depth set to " + maxOrganicDepth);
} else {
throw new OrganicSoilException("The soil layer below the ignored organic layer is still organic.");
}
i++;
} else {
break; // We found a mineral layer
}
}
}
protected void averageMissingValues(Component comp) {
comp.slope_r(Utils.averageMissingValue(comp.slope_h(), comp.slope_l(), comp.slope_r()));
comp.albedodry_r(Utils.averageMissingValue(comp.albedodry_h(), comp.albedodry_l(), comp.albedodry_r()));
for (Horizon h : comp.horizons.values()) {
h.hzdept_r(Utils.averageMissingValue(h.hzdept_h(), h.hzdept_l(), h.hzdept_r()));
h.hzdepb_r(Utils.averageMissingValue(h.hzdepb_h(), h.hzdepb_l(), h.hzdepb_r()));
h.claytotal_r(Utils.averageMissingValue(h.claytotal_h(), h.claytotal_l(), h.claytotal_r()));
h.sandtotal_r(Utils.averageMissingValue(h.sandtotal_h(), h.sandtotal_l(), h.sandtotal_r())); // may be useless because the query removes null sandtotals
h.silttotal_r(Utils.averageMissingValue(h.silttotal_h(), h.silttotal_l(), h.silttotal_r()));
h.sandvc_r(Utils.averageMissingValue(h.sandvc_h(), h.sandvc_l(), h.sandvc_r(), 0));
h.sandco_r(Utils.averageMissingValue(h.sandco_h(), h.sandco_l(), h.sandco_r(), 0));
h.sandmed_r(Utils.averageMissingValue(h.sandmed_h(), h.sandmed_l(), h.sandmed_r(), 0));
h.sandfine_r(Utils.averageMissingValue(h.sandfine_h(), h.sandfine_l(), h.sandfine_r(), 0));
h.sandvf_r(Utils.averageMissingValue(h.sandvf_h(), h.sandvf_l(), h.sandvf_r()));
h.dbthirdbar_r(Utils.averageMissingValue(h.dbthirdbar_h(), h.dbthirdbar_l(), h.dbthirdbar_r()));
h.wthirdbar_r(Utils.averageMissingValue(h.wthirdbar_h(), h.wthirdbar_l(), h.wthirdbar_r()));
h.wfifteenbar_r(Utils.averageMissingValue(h.wfifteenbar_h(), h.wfifteenbar_l(), h.wfifteenbar_r()));
h.ksat_r(Utils.averageMissingValue(h.ksat_h(), h.ksat_l(), h.ksat_r()));
h.cec7_r(Utils.averageMissingValue(h.cec7_h(), h.cec7_l(), h.cec7_r()));
h.ecec_r(Utils.averageMissingValue(h.ecec_h(), h.ecec_l(), h.ecec_r()));
h.om_r(Utils.averageMissingValue(h.om_h(), h.om_l(), h.om_r()));
h.caco3_r(Utils.averageMissingValue(h.caco3_h(), h.caco3_l(), h.caco3_r()));
h.ph1to1h2o_r(Utils.averageMissingValue(h.ph1to1h2o_h(), h.ph1to1h2o_l(), h.ph1to1h2o_r()));
h.ph01mcacl2_r(Utils.averageMissingValue(h.ph01mcacl2_h(), h.ph01mcacl2_l(), h.ph01mcacl2_r()));
h.lep_r(Utils.averageMissingValue(h.lep_h(), h.lep_l(), h.lep_r()));
}
}
protected void removeBadLayers(Component comp) {
boolean bad = false; // delete everything after bad found
Iterator<Horizon> it = comp.horizons.values().iterator();
it.next(); // skip the first horizon but I don't think the WEPS code is right
while (it.hasNext()) {
Horizon h = it.next();
if (bad) {
it.remove();
continue;
}
// stop if layer is identified as rock, peat, etc.
if (chkTexture(SoilCalc.getTexture(h))) {
it.remove();
bad = true;
continue;
}
// stop if both sand and clay are missing or zero
// Should already be removed because of the query
if ((EvalResult.testDefaultDouble(h.claytotal_r()) || h.claytotal_r() == 0.0)
&& (EvalResult.testDefaultDouble(h.sandtotal_r()) || h.sandtotal_r() == 0.0)) {
it.remove();
bad = true;
continue;
}
// stop if layer does not have wet bulk density
if (EvalResult.testDefaultDouble(h.dbthirdbar_r()) || h.dbthirdbar_r() == 0.0) {
it.remove();
bad = true;
}
}
}
protected boolean chkTexture(String texture) {
String tmp = texture.toLowerCase();
for (String textureChk1 : TEXTURE_CHK) {
if (tmp.contains(textureChk1)) {
int tdx = tmp.indexOf(textureChk1); // check if token
if (tdx > 0) {
if (!Character.isWhitespace(tmp.charAt(tdx - 1))) {
continue;
}
if (tmp.charAt(tdx - 1) != '-') {
continue;
}
}
if (tdx + textureChk1.length() < tmp.length()) {
if (!Character.isWhitespace(tmp.charAt(tdx + textureChk1.length()))) {
continue;
}
}
LOG.log(Level.WARNING, "Layer dropped for texture " + texture + ".");
return true;
}
}
return false;
}
public void testData(MapUnit mapUnit, Component comp) throws ServiceException {
double calculatedSiltTotal;
if (comp.compname() == null || comp.compname().length() == 0) {
comp.compname("unknown");
}
// WEPS check comp_pct here but as a string? wait until it becomes a problem
if (comp.taxorder() == null || comp.taxorder().length() == 0) {
comp.taxorder("unkown");
}
if (comp.localphase() == null || comp.localphase().length() == 0) {
comp.localphase("unkown");
}
comp.tfact(SoilCalc.testNumericSoilElement(5, 1, comp.tfact(), -1));
comp.slope_r(SoilCalc.testNumericSoilElement(999, 0, comp.slope_r(), -1));
// if(mapUnit.musym() == null || mapUnit.musym().length() == 0) // If this begins to fail we will have to get a setter
// mapUnit.musym("no map symbol");
mapUnit.brockdepmin(SoilCalc.testNumericSoilElement(9999, 0, mapUnit.brockdepmin(), 9999));
comp.calculated_resdeptmin_r(SoilCalc.testNumericSoilElement(9999, 0, comp.calculated_resdeptmin_r(), 9999));
// if(mapUnit.areaname() == null || mapUnit.areaname().length() == 0) // same
// mapUnit.areaname("no area name, no area state");
// if(mapUnit.areasymbol() == null || mapUnit.areaname().length() == 0) // same
// mapUnit.areasymbol("no area symbol");
for (Horizon h : comp.horizons.values()) {
h.hzdept_r(SoilCalc.testNumericSoilElement(999, 0, h.hzdept_r(), Double.NaN));
h.hzdepb_r(SoilCalc.testNumericSoilElement(999, 0, h.hzdepb_r(), Double.NaN));
h.claytotal_r(SoilCalc.testNumericSoilElement(100, 0, h.claytotal_r(), Double.NaN));
h.sandtotal_r(SoilCalc.testNumericSoilElement(100, 0, h.sandtotal_r(), Double.NaN));
calculatedSiltTotal = 100 - (h.claytotal_r() + h.sandtotal_r());
h.silttotal_r(SoilCalc.testNumericSoilElement(calculatedSiltTotal * 1.001, calculatedSiltTotal * .999, h.silttotal_r(), calculatedSiltTotal));
h.sandvf_r(SoilCalc.testNumericSoilElement(100, 0, h.sandvf_r(), Double.NaN));
h.dbthirdbar_r(SoilCalc.testNumericSoilElement(2.60, .02, h.dbthirdbar_r(), Double.NaN));
h.wthirdbar_r(SoilCalc.testNumericSoilElement(80, 0, h.wthirdbar_r(), Double.NaN));
h.om_r(SoilCalc.testNumericSoilElement(100, 0, h.om_r(), Double.NaN));
h.caco3_r(SoilCalc.testNumericSoilElement(110, 0, h.caco3_r(), Double.NaN));
if (Config.getBoolean(KEY_ESTIMATE_NULLS, true)) {
h.ksat_r(SoilCalc.testNumericSoilElement(705, 0, h.ksat_r(), Double.NaN));
h.wfifteenbar_r(SoilCalc.testNumericSoilElement(70, 0, h.wfifteenbar_r(), Double.NaN));
}
}
}
}