V1_0.java [src/java/m/wqm/wqmsoilattributes] Revision: ee768ce6108fe7a422a140c93088e2a03a36f7f0 Date: Wed Nov 18 14:36:56 MST 2015
package m.wqm.wqmsoilattributes;
/**
*
* @author RUMPAL SIDHU
* @author Shaun Case
*/
import csip.Config;
import csip.ModelDataService;
import csip.ServiceException;
import csip.annotations.Polling;
import csip.utils.JSONUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Iterator;
import java.util.Set;
import java.util.logging.Level;
import javax.ws.rs.Path;
import oms3.annotations.Description;
import oms3.annotations.Name;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
/**
*
* @author Shaun Case
*/
@Name("WQM-02: Soil Component Attributes (WQMSoilAttributes)")
@Description("This service intersects area of analysis (AoA) geometry with SSURGO soil mapunit geometry, derives a list of distinct soil components for the AoA, and gets attributes from SSURGO tables required for computing nutrient and pesticide loss potentials.")
@Path("m/wqmsoilattributes/1.0")
@Polling(first = 5000, next = 2000)
public class V1_0 extends ModelDataService {
static final int JSON_LATITUDE = 1;
static final int JSON_LONGITUDE = 0;
private double minimumPercentage = 0.0;
//Request
private String aoaId;
//Response
private ArrayList<V1_0.Component> componentList;
private HashMap<String, V1_0.Component> componentMap;
private String error_msg = "";
private Connection conn = null;
private Statement statement = null;
private wqm.utils.WQMTools.PolygonLatLon polygonData;
private HashMap<String, V1_0.mukeyData> aoa_mukeyList;
private boolean useMSSQL;
@Override
protected void preProcess() {
this.error_msg = "";
this.polygonData = null;
try {
JSONArray request = getRequest().optJSONArray("parameter");
if (JSONUtils.checkKeyExistsB(JSONUtils.preprocess(request), "AoAId")) {
this.aoaId = getStringParam("AoAId");
this.minimumPercentage = getDoubleParam("aoa_filter_pct", 0.0);
//this.useMSSQL = getBooleanParam("use_mssql", false);
String confString = Config.getString("rse-db", "ssurgo");
if (confString.contains("rse-sql")) {
this.useMSSQL = true;
LOG.info("Using MS SQL database.");
} else {
LOG.info("Using PostgreSQL database.");
}
JSONArray features = getJSONParam("aoa_geometry").optJSONArray("features");
if (this.buildPolygon(features)) {
this.conn = wqm.utils.WQMTools.getConnection(confString, LOG);
this.statement = this.conn.createStatement();
}
} else {
// No valid input stream for this service
this.error_msg = "Request JSON is missing AoAId. ";
}
} catch (JSONException | ServiceException | SQLException se) {
LOG.log(Level.SEVERE, "Could not proceed with preprocessing of request JSON for WQM-02: {0}", se.getMessage());
this.error_msg = "Could not proceed with preprocessing of the request JSON. " + se.getMessage();
}
}
@Override
protected String process() throws Exception {
if ((null != this.polygonData) && (this.error_msg.isEmpty())) {
aoa_mukeyList = this.ssurgoIntersect(this.polygonData.toWKT());
try {
if ((this.error_msg.isEmpty()) && (null != aoa_mukeyList) && (aoa_mukeyList.size() > 0)) {
ResultSet resultSet;
String query;
this.componentList = new ArrayList<>();
this.componentMap = new HashMap<>();
int mapCount = 0;
double totalAreas = 0.0;
String lastQueryWhere = "(";
String lastQueryWhere2 = "(";
int num_mu = 0;
Set keys = aoa_mukeyList.keySet();
Iterator ite = keys.iterator();
query = "SELECT ssurgo.component.mukey, ssurgo.component.cokey, ssurgo.component.compname, ssurgo.component.comppct_r, ssurgo.component.hydgrp, "
+ " ssurgo.component.slope_r, ssurgo.component.taxorder, ssurgo.chorizon.chkey, ssurgo.chorizon.om_r, ssurgo.chorizon.hzthk_r, "
+ " ssurgo.chorizon.hzdept_r, ssurgo.chorizon.hzdepb_r, ssurgo.chorizon.kwfact, ssurgo.chorizon.kffact, ssurgo.chfrags.fragvol_r, ssurgo.chfrags.chfragskey FROM ssurgo.component "
+ " LEFT OUTER JOIN ssurgo.chorizon ON ssurgo.chorizon.cokey=ssurgo.component.cokey LEFT OUTER JOIN ssurgo.chfrags on ssurgo.chfrags.chkey=ssurgo.chorizon.chkey WHERE ssurgo.component.mukey in ( '";
while (ite.hasNext()) {
num_mu++;
if (mapCount > 0) {
query += ", '" + ite.next().toString() + "' ";
} else {
query += ite.next().toString() + "' ";
}
mapCount++;
}
query += ") AND ssurgo.component.comppct_r IS NOT NULL ORDER BY ssurgo.component.mukey, ssurgo.component.cokey, ssurgo.chorizon.hzdept_r DESC, ssurgo.chorizon.kffact;";
LOG.log(Level.INFO, "WQM-02-polygon intersect query(118):{0}", query);
resultSet = this.statement.executeQuery(query);
HashMap<String, Double> aoa_comp_area_list = new HashMap<>();
while (resultSet.next()) {
V1_0.Component tComponent;
V1_0.Component component;
String mukey = resultSet.getString("mukey");
String cokey = resultSet.getString("cokey");
String chkey = resultSet.getString("chkey");
double aoa_Area = aoa_mukeyList.get(mukey).area;
if (!this.componentMap.containsKey((cokey))) {
component = new V1_0.Component(cokey, resultSet.getString("mukey"), resultSet.getString("compname"), (aoa_Area * (resultSet.getDouble("comppct_r") / 100.0)),
resultSet.getString("hydgrp"), resultSet.getDouble("slope_r"), resultSet.getString("taxorder"));
totalAreas += component.getArea();
this.componentList.add(component);
this.componentMap.put(cokey, component);
tComponent = component;
double tArea = tComponent.getArea();
if (aoa_comp_area_list.containsKey(mukey)) {
tArea += aoa_comp_area_list.get(mukey);
}
aoa_comp_area_list.put(mukey, tArea);
} else {
tComponent = this.componentMap.get(cokey);
}
//For the rest of these operations, we need to use the temp component pointer..
// Add chkeys to cokey object, some will be duplicates because they are unique by cokey:chkey:chfragkey
double kwfact;
Boolean kwfact_b;
double kffact;
Boolean kffact_b;
double hzthk_r;
Boolean hzthk_r_b;
// Keep these pairs of resultSet calls together..."wasNULL()" depends on the call previous to it.
kwfact = resultSet.getDouble("kwfact");
kwfact_b = !resultSet.wasNull();
kffact = resultSet.getDouble("kffact");
kffact_b = !resultSet.wasNull();
hzthk_r = resultSet.getDouble("hzthk_r");
hzthk_r_b = !resultSet.wasNull();
tComponent.addHorizon(chkey, resultSet.getString("chfragskey"), kwfact, kwfact_b, kffact, kffact_b, resultSet.getDouble("om_r"), hzthk_r, hzthk_r_b, resultSet.getDouble("hzdept_r"), resultSet.getDouble("hzdepb_r"), resultSet.getDouble("fragvol_r"));
}
int componentsRemaining = 0;
//Remove components having less than minimumPercentage of total area "totalAreas" here.
for (V1_0.Component component : this.componentList) {
component.setAoaPct_r(component.getArea() / totalAreas);
if (component.getAoAPct_r() < this.minimumPercentage) {
component.setDeleted(true);
} else {
componentsRemaining++;
component.computeHorizonResults();
if (lastQueryWhere.length() > 1) {
lastQueryWhere += " OR component.cokey='" + component.getCokey() + "'";
lastQueryWhere2 += " OR WT1.cokey='" + component.getCokey() + "'";
} else {
lastQueryWhere += " component.cokey='" + component.getCokey() + "'";
lastQueryWhere2 += "WT1.cokey='" + component.getCokey() + "'";
}
}
}
if (componentsRemaining > 0) {
lastQueryWhere += " ) ";
lastQueryWhere2 += " ) ";
if (this.useMSSQL) {
query = "With WT1 As (Select component.cokey, component.compname, component.comppct_r, MIN(cosoilmoist.soimoistdept_r) As wtbl_top_min, MAX(cosoilmoist.soimoistdepb_r) As wtbl_bot_max From ssurgo.component Inner Join ssurgo.comonth On component.cokey=comonth.cokey Inner Join ssurgo.cosoilmoist On comonth.comonthkey=cosoilmoist.comonthkey "
+ "Where " + lastQueryWhere + "and cosoilmoist.soimoiststat='Wet' Group By component.cokey, component.compname, component.comppct_r), WT2 As (Select WT1.cokey, WT1.compname, WT1.comppct_r, WT1.wtbl_top_min, WT1.wtbl_bot_max, MAX(cosoilmoist.soimoistdept_r) As nonwet_top_max From WT1 Left Outer Join ssurgo.comonth On WT1.cokey=comonth.cokey Left Outer Join ssurgo.cosoilmoist On comonth.comonthkey=cosoilmoist.comonthkey "
+ "Where " + lastQueryWhere2 + " and (cosoilmoist.soimoiststat NOT IN ('Wet') OR cosoilmoist.soimoiststat IS NULL) Group By WT1.cokey, WT1.compname, WT1.comppct_r, WT1.wtbl_top_min, WT1.wtbl_bot_max) Select WT2.cokey, WT2.compname, WT2.comppct_r, WT2.wtbl_top_min, WT2.wtbl_bot_max, WT2.nonwet_top_max, case when (wtbl_bot_max < 183 or nonwet_top_max >= wtbl_bot_max) then 'Perched' else 'Apparent' end as wtkind from WT2";
} else {
query = "With WT1 As (Select component.cokey, component.compname, component.comppct_r, MIN(cosoilmoist.soimoistdept_r) As wtbl_top_min, MAX(cosoilmoist.soimoistdepb_r) As wtbl_bot_max From ssurgo.component Inner Join ssurgo.comonth On component.cokey=comonth.cokey Inner Join ssurgo.cosoilmoist On comonth.comonthkey=cosoilmoist.comonthkey "
+ "Where " + lastQueryWhere + "and cosoilmoist.soimoiststat='Wet' Group By component.cokey, component.compname, component.comppct_r ORDER BY component.cokey), WT2 As (Select WT1.cokey, WT1.compname, WT1.comppct_r, WT1.wtbl_top_min, WT1.wtbl_bot_max, MAX(cosoilmoist.soimoistdept_r) As nonwet_top_max From WT1 Left Outer Join ssurgo.comonth On WT1.cokey=comonth.cokey Left Outer Join ssurgo.cosoilmoist On comonth.comonthkey=cosoilmoist.comonthkey "
+ "Where " + lastQueryWhere2 + " and (cosoilmoist.soimoiststat NOT IN ('Wet') OR cosoilmoist.soimoiststat IS NULL) Group By WT1.cokey, WT1.compname, WT1.comppct_r, WT1.wtbl_top_min, WT1.wtbl_bot_max) Select WT2.cokey, WT2.compname, WT2.comppct_r, WT2.wtbl_top_min, WT2.wtbl_bot_max, WT2.nonwet_top_max, case when (wtbl_bot_max < 183 or nonwet_top_max >= wtbl_bot_max) then 'Perched' else 'Apparent' end as wtkind from WT2";
}
LOG.log(Level.INFO, "WQM-02-polygon intersect query(189):{0}", query);
resultSet = this.statement.executeQuery(query);
while (resultSet.next()) {
String tCokey = resultSet.getString("cokey");
V1_0.Component tcomponent = this.componentMap.get(tCokey);
tcomponent.setWTBL(resultSet.getString("wtkind")); // If this is null, the set funciton will make the appropriate adjustment to "None".
tcomponent.setWtblTopMin(resultSet.getDouble("wtbl_top_min"));
}
}
} // Actual Intersect Call failed.
else {
this.error_msg += " Could not intersect polygons. ";
}
} catch (SQLException se) {
this.error_msg += "Error executing SQL: " + se.getMessage();
}
}// Creation of Client objec to make call to intersect failed.
else {
this.error_msg += " Creation of intersect service call failed. ";
}
if (null != this.conn) {
if (null != this.statement) {
this.statement.close();
}
this.conn.close();
}
return (!this.error_msg.isEmpty() ? this.error_msg : EXEC_OK);
}
@Override
protected void postProcess() throws Exception {
putResult("AoaId", aoaId, "Area of Analysis Identifier");
JSONArray resultArray = new JSONArray();
for (Component component : componentList) {
if (!component.isDeleted()) {
JSONArray tmpArr = new JSONArray();
tmpArr.put(JSONUtils.dataDesc("cokey", component.getCokey(), "Soil Component Key"));
tmpArr.put(JSONUtils.dataDesc("compname", component.getName(), "Soil Component Name"));
tmpArr.put(JSONUtils.dataDesc("mukey", component.getMukey(), "Soil Mapunit Key"));
tmpArr.put(JSONUtils.dataDesc("muname", this.aoa_mukeyList.get(component.getMukey()).muname, "Soil Mapunit Name"));
tmpArr.put(JSONUtils.dataDesc("aoa_comp_area", component.getArea(), "Soil Component Area (Acres) in the Area of Analysis"));
tmpArr.put(JSONUtils.dataDesc("aoa_pct_r", component.getAoAPct_r(), "Percentage of the Area of Analysis Represented by Soil Component"));
tmpArr.put(JSONUtils.dataDesc("aoa_comp_hsg", component.getHsg(), "Hydrologic Soil Group of the Soil Component"));
tmpArr.put(JSONUtils.dataDesc("aoa_comp_taxorder", component.getTaxorder(), "Taxonomic Order of the Soil Component"));
tmpArr.put(JSONUtils.dataDesc("aoa_comp_kfact", component.getKfactor(), "K factor of the Surface Mineral Horizon of the Soil Component"));
tmpArr.put(JSONUtils.dataDesc("aoa_comp_slope", component.getSlope(), "Slope Percentage of the Soil Component"));
tmpArr.put(JSONUtils.dataDesc("aoa_comp_coarse_frag", component.getCoarseFrag(), "Weighted Average Coarse Rock Fragment Volume Percentage through the Profile of the Soil Component"));
tmpArr.put(JSONUtils.dataDesc("aoa_comp_om", component.getOrganicMatter(), "Organic Matter Percentage of the Surface Horizon of the Soil Component"));
tmpArr.put(JSONUtils.dataDesc("aoa_comp_hzdepth", component.getHzdepth(), "Depth (inches) of the Surface Horizon of the Soil Component"));
tmpArr.put(JSONUtils.dataDesc("aoa_comp_wtbl", component.getWTBL(), "Kind of Water Table of the Soil Component; values are None, Apparent, Perched"));
tmpArr.put(JSONUtils.dataDesc("aoa_comp_cracksgr24", component.getCracksgr24(), "Surface Connected Macropores (Cracks) at Least 24 Inches Deep"));
tmpArr.put(JSONUtils.dataDesc("aoa_comp_slopegr15", component.getSlopegr15(), "Field Slope is Greater Than 15%"));
tmpArr.put(JSONUtils.dataDesc("aoa_comp_hwt_lt_24", component.getHwt_lt_24(), "High Water is Less than 24 Inches Under the Surface"));
resultArray.put(JSONUtils.dataDesc("soil_component", tmpArr, "Entry for Soil Component"));
}
}
putResult("soil_component_list", resultArray);
}
private Boolean buildPolygon(JSONArray features) throws JSONException {
Boolean ret_val = false;
if (null != features) {
if (features.length() > 0) {
JSONObject feature = features.getJSONObject(0).optJSONObject("geometry");
if (this.isGeometryPolygonType(feature)) {
JSONArray polygon = feature.optJSONArray("coordinates");
if (null != polygon) {
if (polygon.length() > 0) {
this.polygonData = readPolygonCoordinates(polygon.getJSONArray(0));
if (null != this.polygonData) {
if (!this.polygonData.isValid()) {
this.error_msg += "Invalid latitude and/or longitude data contained in this polygon. Cannot proceed with processing of this request. ";
} else {
ret_val = true;
}
}//No else needed here, the error message will be built in readPolygonCoordinates()
} else {
this.error_msg = "No coordinates found associated with the polygon specified in feature collection number: 1 . ";
}
} else {
this.error_msg = "No coordinates found associated with the polygon specified in feature collection number: 1 . ";
}
}//No else needed here, the error message will be built in isGeometryPolygonType()
} else {
this.error_msg = "No geometry found associated with this feature. ";
}
} else {
this.error_msg = "Cannot process request JSON, missing features. ";
}
return ret_val;
}
private Boolean isGeometryPolygonType(JSONObject geometry) {
Boolean ret_val = false;
if (null != geometry) {
String geometryType;
geometryType = geometry.optString("type");
if (!geometryType.isEmpty()) {
if (geometryType.toLowerCase().equals("polygon")) {
ret_val = true;
} else {
this.error_msg = "No valid geometry type found in the feature collection number: 1 . Looking for 'Polygon'. ";
}
} else {
this.error_msg = "No geometry type specified in the feature collection number: 1 . ";
}
} else {
this.error_msg = "No geometry found in the feature collection number: 1 . ";
}
return ret_val;
}
private wqm.utils.WQMTools.PolygonLatLon readPolygonCoordinates(JSONArray shape) throws JSONException {
wqm.utils.WQMTools.PolygonLatLon tPolygon = new wqm.utils.WQMTools.PolygonLatLon();
if (null != shape) {
for (int k = 0; k < shape.length(); k++) {
JSONArray jPoint = shape.getJSONArray(k);
tPolygon.add(jPoint.getDouble(V1_0.JSON_LATITUDE), jPoint.getDouble(V1_0.JSON_LONGITUDE));
}
} else {
this.error_msg = "Cannot find the latitude and longitude values for this polygon. ";
tPolygon = null;
}
return tPolygon;
}
// Returns the intersected mukey with the largest area.
private HashMap<String, V1_0.mukeyData> ssurgoIntersect(String WKTPolygon) {
String polygonText = " ST_PolygonFromText(" + WKTPolygon + ") ";
String query;
HashMap<String, V1_0.mukeyData> ret_val = null;
try {
if (null != WKTPolygon) {
ret_val = new HashMap<>();
if (this.useMSSQL) {
query = "SELECT areasymbol, musym, mukey, muname, geography::STGeomFromText( intersectPoly.STAsText(), 4326).MakeValid().STArea() / 4046.86 as sizeIntersectionAcres "
//+ "geography::STPolyFromText(" + WKTPolygon + ",4326).MakeValid().ReorientObject().STIntersection(soilpoly).STArea() / 4046.86 as sizeIntersectionAcres "
+ " FROM "
+ " (SELECT m.areasymbol, m.musym, m.mukey, ssurgo.mapunit.muname, m.the_geom.STIntersection(geometry::STGeomFromText(" + WKTPolygon + ",0)).MakeValid() as intersectPoly"
//+ " geography::STPolyFromText(m.the_geom.STAsText(),4326).MakeValid().ReorientObject() as soilpoly "
+ " FROM ssurgo.ssurgo.soilmu_a as m "
+ " WITH (index(geom_sidx)) "
+ " INNER JOIN ssurgo.mapunit ON m.mukey=ssurgo.mapunit.mukey "
+ " WHERE m.the_geom.STIntersects(geometry::STGeomFromText(" + WKTPolygon + ",0)) = 1 "
+ " AND m.the_geom.STIsValid()=1 "
+ " AND (geometry::STGeomFromText(" + WKTPolygon + ",0).STIsValid())=1) "
+ " as a;";
} else {
query = "SELECT m.mukey, ssurgo.mapunit.muname, "
+ "st_area(st_transform(st_intersection(ST_PolygonFromText(" + WKTPolygon + ", 4326), m.the_geom::geography::geometry ),3541))/43560 as sizeIntersectionAcres "
+ " FROM ssurgo.soilmu_a as m "
+ " INNER JOIN ssurgo.mapunit ON m.mukey=ssurgo.mapunit.mukey "
+ " WHERE ST_Intersects(" + polygonText + ", m.the_geom ) "
+ " AND st_isvalid( m.the_geom) AND st_isvalid(" + polygonText + ") order by m.mukey;";
}
ResultSet results = this.statement.executeQuery(query);
if (null != results) {
while (results.next()) {
V1_0.mukeyData tMData = new V1_0.mukeyData();
String tKey = results.getString("mukey");
tMData.area = results.getDouble("sizeIntersectionAcres");
tMData.muname = results.getString("muname");
if (!ret_val.containsKey(tKey)) {
ret_val.put(tKey, tMData);
} else {
tMData.area += ret_val.get(tKey).area;
ret_val.put(tKey, tMData);
}
}
} else {
this.error_msg += "No results from the intersect query for this geometry. ";
}
}
} catch (SQLException ex) {
this.error_msg += "Cannot continue processing this request in the intersect procedure: " + ex.getMessage();
if (null != ret_val) {
ret_val.clear();
ret_val = null;
}
}
return ret_val;
}
//Inner Classes
/**
*
*/
public class Component {
private String cokey; //Soil Component Key
private String name; //Soil Component Name
private double area; //Soil Component Area (Acres) in the Area of Analysis
private String hsg; //Hydrologic Soil Group of the Soil Component
private String taxorder; //Taxonomic Order of the Soil Component
private double slope; //Slope Percentage of the Soil Component
private double kfactor; //K factor of the Surface Mineral Horizon of the Soil Component
private double coarseFrag; //Weighted Average Coarse Rock Fragment Volume Percentage through the Profile of the Soil Component
private double organicMatter; //Organic Matter Percentage of the Surface Horizon of the Soil Component
private double hzdepth; //Depth (inches) of the Surface Horizon of the Soil Component
private boolean cracksgr24; //Surface Connected Macropores (Cracks) at Least 24 Inches Deep; default is False
private boolean slopegr15; //Field Slope is Greater Than 15%; default is False
private boolean hwt_lt_24; //High Water is Less than 24 Inches Under the Surface; default is False
private double wtbl_top_min;
private String wtbl;
private double frag_vol_total;
private String mukey;
private double aoa_pct_r;
Boolean Deleted;
private ArrayList<V1_0.Component.horizon> chKeys;
private HashMap<String, V1_0.Component.horizon> horizonMap;
/**
*
* @param cokey
* @param mukey
* @param compname
* @param area
* @param hsg
* @param slope_r
* @param taxorder
*/
public Component(String cokey, String mukey, String compname, double area, String hsg, double slope_r, String taxorder) {
this.cracksgr24 = false;
this.slopegr15 = false;
this.hwt_lt_24 = false;
this.chKeys = new ArrayList<>();
this.horizonMap = new HashMap<>();
this.cokey = cokey;
this.mukey = mukey;
this.name = compname;
this.area = area;
this.hsg = hsg;
this.slope = slope_r;
this.taxorder = taxorder;
this.wtbl = "None";
this.wtbl_top_min = 0.0;
this.cracksgr24 = false; //Set to default false, and is not updated by the current spec.
this.Deleted = false;
this.frag_vol_total = 0.0;
this.aoa_pct_r = 0.0;
this.slopegr15 = (this.slope > 15.0);
}
//Debugging purposes...
@Override
public String toString() {
return this.mukey + "," + this.cokey + "," + this.name + "," + this.area + "," + this.aoa_pct_r + "," + (this.Deleted ? "Deleted" : "Not Deleted");
}
//Set Methods
/**
*
* @param deleted
*/
public void setDeleted(Boolean deleted) {
this.Deleted = deleted;
}
public void setAoaPct_r(double aoa_pct_r) {
this.aoa_pct_r = aoa_pct_r;
}
/**
*
* @param wtKind
*/
public void setWTBL(String wtKind) {
if (null == wtKind) {
this.wtbl = "None";
} else {
this.wtbl = wtKind;
}
}
/**
*
* @param wtbl_top_min
*/
public void setWtblTopMin(double wtbl_top_min) {
this.wtbl_top_min = wtbl_top_min;
this.hwt_lt_24 = (wtbl_top_min <= 61);
}
//Get Methods
public double getAoAPct_r() {
return this.aoa_pct_r;
}
/**
*
* @return
*/
public String getCokey() {
return this.cokey;
}
/**
*
* @return
*/
public Boolean isDeleted() {
return this.Deleted;
}
public String getMukey() {
return this.mukey;
}
/**
*
* @return
*/
public String getName() {
return this.name;
}
/**
*
* @return
*/
public double getArea() {
return this.area;
}
/**
*
* @return
*/
public String getHsg() {
return this.hsg;
}
/**
*
* @return
*/
public String getTaxorder() {
return this.taxorder;
}
/**
*
* @return
*/
public double getSlope() {
return this.slope;
}
/**
*
* @return
*/
public double getKfactor() {
return this.kfactor;
}
/**
*
* @return
*/
public double getCoarseFrag() {
return this.coarseFrag;
}
/**
*
* @return
*/
public double getOrganicMatter() {
return this.organicMatter;
}
/**
*
* @return
*/
public double getHzdepth() {
return this.hzdepth;
}
/**
*
* @return
*/
public boolean getCracksgr24() {
return this.cracksgr24;
}
/**
*
* @return
*/
public boolean getSlopegr15() {
return this.slopegr15;
}
/**
*
* @return
*/
public boolean getHwt_lt_24() {
return this.hwt_lt_24;
}
/**
*
* @return
*/
public String getWTBL() {
return this.wtbl;
}
/**
*
* @param chkey
* @param chfragskey
* @param kwfact
* @param kwfact_b
* @param kffact
* @param kffact_b
* @param om_r
* @param hzthk_r
* @param hzthk_r_b
* @param hzdept_r
* @param hzdepb_r
*/
public void addHorizon(String chkey, String chfragskey, double kwfact, boolean kwfact_b, double kffact, boolean kffact_b, double om_r, double hzthk_r, Boolean hzthk_r_b, double hzdept_r, double hzdepb_r, double fragvol_r) {
// Each component can have mulitiple horizons...each horizon can have multiple fragment volumes...
V1_0.Component.horizon tHorizon;
if (this.horizonMap.containsKey(chkey)) {
tHorizon = this.horizonMap.get(chkey);
tHorizon.addFragKey(chfragskey, fragvol_r);
} else {
tHorizon = new V1_0.Component.horizon(chkey, chfragskey, kwfact, kwfact_b, kffact, kffact_b, om_r, hzthk_r, hzthk_r_b, hzdept_r, hzdepb_r, fragvol_r);
this.chKeys.add(tHorizon);
this.horizonMap.put(chkey, tHorizon);
}
}
/**
*
*/
public void computeHorizonResults() {
double profile_thk = 0.0;
boolean haveKFactor = false;
boolean haveHzDepth = false;
double comp_product = 0.0;
//If we remove the "order by" which includes hzdept_r in the first SQL statement in process(), then we need to sort this list before continuing...
//For now this "order by...hzdept_r" is currently in the SQL statement, so no sort is done here. If we find that the SQL statement takes longer with
//The order by clause, and the sort is quicker here, then this code will change.
//#Get first horizon organic matter
this.organicMatter = this.chKeys.get(0).getOm_r();
for (V1_0.Component.horizon horizon : this.chKeys) {
double horizonThickness = 0.0;
double horizonProduct = 0.0;
if (!haveHzDepth) { //We only want to use the first thickness since these are presorted
if (!horizon.getHzthk_r_b()) {
this.hzdepth = horizon.getHzdepb_r() - horizon.getHzdept_r();
} else {
this.hzdepth = horizon.getHzdept_r();
}
haveHzDepth = true;
}
if ((null != this.taxorder) && this.taxorder.equals("Histosols") && !horizon.getKffact_b() && !horizon.getKwfact_b()) {
this.kfactor = 0.02;
} else {
if (!haveKFactor) {
if (horizon.getKffact_b()) {
this.kfactor = horizon.getKffact();
haveKFactor = true;
} else {
if (horizon.getKwfact_b() && !horizon.getKffact_b()) {
this.kfactor = horizon.getKwfact();
haveKFactor = true;
}
}
}
}
this.frag_vol_total += horizon.getFragVol();
if (!horizon.getHzthk_r_b()) {
horizonThickness = horizon.getHzdepb_r() - horizon.getHzdept_r();
} else {
horizonThickness = horizon.getHzthk_r();
}
profile_thk += horizonThickness;
horizonProduct = horizonThickness * horizon.getFragVol();
comp_product += horizonProduct;
}
if ( profile_thk > 0 ){
this.coarseFrag = comp_product / profile_thk;
}
}
class horizon implements Comparable<V1_0.Component.horizon> {
private final String chkey;
private final String chfragskey;
private final double kwfact;
private final Boolean kwfact_b;
private final double kffact;
private final Boolean kffact_b;
private final double om_r;
private final double hzthk_r;
private final double hzdept_r;
private final double hzdepb_r;
private final Boolean hzthk_r_b;
private double frag_vol_total;
private HashMap<String, Double> fragkeyMap;
horizon(String chkey, String chfragskey, double kwfact, boolean kwfact_b, double kffact, boolean kffact_b, double om_r, double hzthk_r, Boolean hzthk_r_b, double hzdept_r, double hzdepb_r, double fragvol_r) {
this.chkey = chkey;
this.chfragskey = chfragskey;
this.kwfact = kwfact;
this.kwfact_b = kwfact_b;
this.kffact = kffact;
this.kffact_b = kffact_b;
this.om_r = om_r;
this.hzthk_r = hzthk_r;
this.hzthk_r_b = hzthk_r_b;
this.hzdept_r = hzdept_r;
this.hzdepb_r = hzdepb_r;
this.fragkeyMap = new HashMap<>();
this.fragkeyMap.put(chfragskey, fragvol_r);
this.frag_vol_total = fragvol_r;
}
@Override
public int compareTo(V1_0.Component.horizon tHorizon) {
int ret_val = 0;
//TODO: Allow this to be sorted....by chkey:chfragskey:hzdept_r
// This might be needed if the "order by" clause of the SQL statement
// used to get this data runs faster without the "order by" that includes this hzdept_r value....
return ret_val;
}
public void addFragKey(String chfragskey, double fragvol_r) {
this.fragkeyMap.put(chfragskey, fragvol_r);
this.frag_vol_total += fragvol_r;
}
public double getFragVol() {
return this.frag_vol_total;
}
public double getKwfact() {
return this.kwfact;
}
public boolean getKwfact_b() {
return this.kwfact_b;
}
public double getKffact() {
return this.kffact;
}
public boolean getKffact_b() {
return this.kffact_b;
}
public double getOm_r() {
return this.om_r;
}
public double getHzthk_r() {
return this.hzthk_r;
}
public Boolean getHzthk_r_b() {
return this.hzthk_r_b;
}
public double getHzdept_r() {
return this.hzdept_r;
}
public double getHzdepb_r() {
return this.hzdepb_r;
}
}
}
protected class mukeyData {
String muname;
double area;
}
}