@@ -50,13 +50,18 @@ |
private double aoa_cfactor; |
private String aoa_dom_comp; |
private String aoa_dom_compname; |
+ private String aoa_dom_wind_comp; |
+ private String aoa_dom_wind_compname; |
private double aoa_ifactor; |
private double aoa_tfactor; |
+ private double aoa_wind_tfactor; |
private double aoa_lsfactor; |
private double aoa_kfactor; |
private double aoa_wind_ep; |
private double aoa_water_ep; |
|
+ private boolean isPalouse; |
+ |
double cfactorraster = -1; |
|
@Override |
@@ -71,10 +76,14 @@ |
this.aoa_dom_compname = ""; |
this.aoa_ifactor = Const.UNKNOWN_CFACTOR; |
this.aoa_tfactor = Const.UNKNOWN_CFACTOR; |
+ this.aoa_dom_wind_comp = ""; |
+ this.aoa_dom_wind_compname = ""; |
+ this.aoa_wind_tfactor = Const.UNKNOWN_CFACTOR; |
this.aoa_lsfactor = Const.UNKNOWN_CFACTOR; |
this.aoa_kfactor = Const.UNKNOWN_CFACTOR; |
this.aoa_wind_ep = Const.UNKNOWN_CFACTOR; |
this.aoa_water_ep = Const.UNKNOWN_CFACTOR; |
+ this.isPalouse = false; |
|
try { |
//These functions will throw a usuable user message if they cannot find these values. |
@@ -88,15 +97,18 @@ |
|
@Override |
protected String process() { |
+ ArrayList<V1_1.ssurgoAttributes> intersectionAttributes = null; |
try { |
|
if (this.error_msg.isEmpty()) { |
JSONArray features = this.aoaGeometry.optJSONArray("features"); |
if (this.buildPolygon(features)) { |
+ |
+ this.polygonData.makeESRIRotation(PGTools.PolygonLatLon.ESRIContourType.outer); |
+ |
//Got a valid Polygon now. |
//HINT: You can get the WKT from the PolygonLatLon object to build an SQL query via the "toWKT" funciton, |
// and then encasing that WKT in the typical "geometry::STPolyFromText(<KWT goes here>,4326)", etc. |
- |
LOG.info("THE POLYGON AS WE KNOW IT IS=" + this.polygonData.toWKT()); |
String cfactor_polygon = "geometry::STPolyFromText(" + this.polygonData.toWKT() + ",4326)"; |
LOG.info("getting centroid for polygon now"); |
@@ -104,12 +116,21 @@ |
LOG.info("About to get C Factor Raster value for lat=" + centroid.lat + " long=" + centroid.lon + " "); |
this.aoa_cfactor = PGTools.getCFactorRaster(centroid.lat, centroid.lon, getWorkspaceDir()) / 100.0; |
|
- //Call SSURGO gemoetry intersect code here ( ssurgo.soilmu_a table ) |
- //Call SSURGO component lookups here ( ssurgo.component table ) |
- if (this.componentLookup(this.ssurgoIntersect(this.polygonData.toWKT()), this.isPalouseArea(this.polygonData.toWKT()))) { |
- //Calc wind and water ep here... |
- this.aoa_wind_ep = this.aoa_cfactor * this.aoa_ifactor / this.aoa_tfactor; |
- this.aoa_water_ep = this.aoa_kfactor * this.aoa_lsfactor / this.aoa_tfactor; |
+ intersectionAttributes = this.ssurgoIntersect(this.polygonData.toWKT()); |
+ this.isPalouse = this.isPalouseArea(this.polygonData.toWKT()); |
+ |
+ if (null != intersectionAttributes) { |
+ if (this.windCriticalComponentLookup(intersectionAttributes)) { |
+ this.aoa_wind_ep = this.aoa_cfactor * this.aoa_ifactor / this.aoa_wind_tfactor; |
+ |
+ if (this.waterCriticalComponentLookup(intersectionAttributes)) { |
+ this.aoa_water_ep = this.aoa_kfactor * this.aoa_lsfactor / this.aoa_tfactor; |
+ } else { |
+ this.error_msg += " Could not calculate the water erodibility potenial. Missing component information. "; |
+ } |
+ } else { |
+ this.error_msg += " Could not calculate the wind erodibility potenial. Missing component information. "; |
+ } |
} |
} |
} |
@@ -119,8 +140,7 @@ |
this.error_msg += "SQL exception getting cfactor: " + se.getMessage(); |
} catch (ServiceException sx) { |
this.error_msg += "Service exception getting cfactor: " + sx.getMessage(); |
- } |
- catch (IOException ioe) { |
+ } catch (IOException ioe) { |
this.error_msg += "IO Exception getting cFactor value from raster geotiff layer: " + ioe.getMessage(); |
} |
|
@@ -131,11 +151,14 @@ |
protected void postProcess() throws Exception { |
if (this.error_msg.isEmpty()) { |
putResult("aoa_id", this.AoAId); |
- putResult("aoa_dom_comp", this.aoa_dom_comp); |
- putResult("aoa_dom_compname", this.aoa_dom_compname); |
+ putResult("aoa_dom_water_comp", this.aoa_dom_comp); |
+ putResult("aoa_dom_water_compname", this.aoa_dom_compname); |
+ putResult("aoa_dom_wind_comp", this.aoa_dom_wind_comp); |
+ putResult("aoa_dom_wind_compname", this.aoa_dom_wind_compname); |
putResult("aoa_cfactor", this.aoa_cfactor); |
putResult("aoa_ifactor", this.aoa_ifactor); |
putResult("aoa_tfactor", this.aoa_tfactor); |
+ putResult("aoa_wind_tfactor", this.aoa_wind_tfactor); |
putResult("aoa_lsfactor", this.aoa_lsfactor); |
putResult("aoa_kfactor", this.aoa_kfactor); |
putResult("aoa_wind_ep", this.aoa_wind_ep); |
@@ -258,37 +281,21 @@ |
return ret_val; |
} |
|
- // Returns the intersected mukey with the largest area. |
- private String ssurgoIntersect(String WKTPolygon) { |
+ // Returns the intersected mukeys |
+ private ArrayList<V1_1.ssurgoAttributes> ssurgoIntersect(String WKTPolygon) { |
HashMap<String, V1_1.ssurgoAttributes> mukeyAttributes; |
ArrayList<V1_1.ssurgoAttributes> attributeList; |
- //String polygonText = " ST_PolygonFromText(" + WKTPolygon + ") "; |
|
- String ret_val = null; |
+ ArrayList<V1_1.ssurgoAttributes> ret_val = null; |
|
if (null != WKTPolygon) { |
String query; |
mukeyAttributes = new HashMap<>(); |
attributeList = new ArrayList<>(); |
|
-// postgis sql |
-// query = "SELECT m.areasymbol, m.musym, m.mukey," |
-// + "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 " |
-// + " WHERE ST_Intersects(" + polygonText + ", m.the_geom ) " |
-// + " AND st_isvalid( m.the_geom) AND st_isvalid(" + polygonText + ");"; |
-// sql server spatial sql |
query |
- = /* "SELECT areasymbol, musym, mukey, geography::STPolyFromText(" + WKTPolygon + ",4326).MakeValid().ReorientObject().STIntersection(soilpoly).STArea() / 4046.86 as sizeIntersectionAcres " |
- + "FROM " |
- + "(SELECT m.areasymbol, m.musym, m.mukey, geography::STPolyFromText(m.the_geom.STAsText(),4326).MakeValid().ReorientObject() as soilpoly " |
- + "FROM ssurgo.ssurgo.soilmu_a as m " |
- + "WITH (index(geom_sidx)) " |
- + "WHERE m.the_geom.STIntersects(geometry::STPolyFromText(" + WKTPolygon + ",0)) = 1 " |
- + "AND m.the_geom.STIsValid()=1 " |
- + "AND (geometry::STPolyFromText(" + WKTPolygon + ",0).STIsValid())=1) " |
- + "as a;"; |
- */ "SELECT areasymbol, musym, mukey, geography::STGeomFromText(intersectPoly.STAsText(), 4326).MakeValid().STArea() / 4046.86 as sizeIntersectionAcres" |
+ = "SELECT areasymbol, musym, mukey, geography::STGeomFromText(intersectPoly.STAsText(), 4326).MakeValid().STArea() / 4046.86 as sizeIntersectionAcres, " |
+ + " geography::STGeomFromText(" + WKTPolygon + ", 4326).MakeValid().STArea() / 4046.86 as aoaArea" |
+ " FROM" |
+ " (SELECT m.areasymbol, m.musym, m.mukey, " |
+ " m.the_geom.STIntersection(geometry:: STGeomFromText (" + WKTPolygon + ",0)).MakeValid() as intersectPoly" |
@@ -323,20 +330,14 @@ |
tAttributes.AoA_Id = results.getString("areasymbol"); |
tAttributes.mukey = results.getString("mukey"); |
tAttributes.gid_area = results.getDouble("sizeIntersectionAcres"); |
+ tAttributes.aoa_area = results.getDouble("aoaArea"); |
|
mukeyAttributes.put(tKey, tAttributes); |
attributeList.add(tAttributes); |
} |
} |
- // Have a unique set of mukeys now, let's find the largest |
- double largestArea = 0.0; |
|
- for (V1_1.ssurgoAttributes tAttribute : attributeList) { |
- if (largestArea < tAttribute.gid_area) { |
- largestArea = tAttribute.gid_area; |
- ret_val = tAttribute.mukey; |
- } |
- } |
+ ret_val = attributeList; |
} else { |
this.error_msg += "No results from the intersect query for this geometry. "; |
} |
@@ -354,103 +355,233 @@ |
return ret_val; |
} |
|
- private Boolean componentLookup(String mukey, boolean isPalouseRegion) { |
+ private Boolean windCriticalComponentLookup(ArrayList<V1_1.ssurgoAttributes> intersectionAttributeList) { |
+ boolean ret_val = true; |
+ String dom_comp = ""; |
+ String dom_compname = ""; |
+ double ifact = 0.0; |
+ double tfact = 0.0; |
+ double sandtotal = 0.0; |
+ double area = 0.0; |
+ |
+ for (V1_1.ssurgoAttributes attributes : intersectionAttributeList) { |
+ if (attributes.gid_area >= (attributes.aoa_area * 0.10)) { |
+ this.componentLookup(attributes, false); |
+ if (attributes.horizons.size() > 0) { |
+ for (V1_1.componentAttributes tComponent : attributes.horizons) { |
+ if ((tComponent.sand_pct >= sandtotal) && (tComponent.sand_pct > 0)) { |
+ if (sandtotal == tComponent.sand_pct) { |
+ if (tComponent.area_pct > area) { |
+ sandtotal = tComponent.sand_pct; |
+ ifact = tComponent.ifact; |
+ tfact = tComponent.tfact; |
+ dom_comp = tComponent.cokey; |
+ dom_compname = tComponent.compname; |
+ area = tComponent.area_pct; |
+ } |
+ } else { |
+ sandtotal = tComponent.sand_pct; |
+ ifact = tComponent.ifact; |
+ tfact = tComponent.tfact; |
+ dom_comp = tComponent.cokey; |
+ dom_compname = tComponent.compname; |
+ area = tComponent.area_pct; |
+ } |
+ } |
+ } |
+ } |
+ } |
+ } |
+ |
+ this.aoa_dom_wind_comp = dom_comp; |
+ this.aoa_dom_wind_compname = dom_compname; |
+ this.aoa_ifactor = ifact; |
+ this.aoa_wind_tfactor = tfact; |
+ |
+ if (dom_comp.isEmpty()) { |
+ this.error_msg += " Could not find any wind critical components in the database. No sand found or no components were greater than 10% of the AOI. "; |
+ ret_val = false; |
+ } |
+ |
+ return ret_val; |
+ } |
+ |
+ private Boolean waterCriticalComponentLookup(ArrayList<V1_1.ssurgoAttributes> intersectionAttributeList) { |
+ boolean ret_val = true; |
+ String dom_comp = ""; |
+ String dom_compname = ""; |
+ double ifact = 0.0; |
+ double lsfactor = 0.0; |
+ double tfactor = 0.0; |
+ double kffact = 0.0; |
+ double area = 0.0; |
+ |
+ for (V1_1.ssurgoAttributes attributes : intersectionAttributeList) { |
+ if (attributes.gid_area >= (attributes.aoa_area * 0.10)) { |
+ this.componentLookup(attributes, true); |
+ if (attributes.horizons.size() > 0) { |
+ for (V1_1.componentAttributes tComponent : attributes.horizons) { |
+ if ((tComponent.kffact >= kffact) && (tComponent.kffact > 0)) { |
+ if (kffact == tComponent.kffact) { |
+ if (tComponent.area_pct > area) { |
+ kffact = tComponent.kffact; |
+ lsfactor = tComponent.lsfact; |
+ tfactor = tComponent.tfact; |
+ ifact = tComponent.ifact; |
+ dom_comp = tComponent.cokey; |
+ dom_compname = tComponent.compname; |
+ area = tComponent.area_pct; |
+ } |
+ } else { |
+ kffact = tComponent.kffact; |
+ lsfactor = tComponent.lsfact; |
+ tfactor = tComponent.tfact; |
+ ifact = tComponent.ifact; |
+ dom_comp = tComponent.cokey; |
+ dom_compname = tComponent.compname; |
+ area = tComponent.area_pct; |
+ } |
+ } |
+ } |
+ } |
+ } |
+ } |
+ |
+ this.aoa_dom_comp = dom_comp; |
+ this.aoa_dom_compname = dom_compname; |
+ this.aoa_lsfactor = lsfactor; |
+ this.aoa_tfactor = tfactor; |
+ this.aoa_ifactor = ifact; |
+ this.aoa_kfactor = kffact; |
+ |
+ if (dom_comp.isEmpty()) { |
+ this.error_msg += " Could not find any water critical components in the database. No kffact found or no components were greater than 10% of the AOI. "; |
+ ret_val = false; |
+ } |
+ |
+ return ret_val; |
+ } |
+ |
+ private Boolean componentLookup(V1_1.ssurgoAttributes attribute, boolean useKFact) { |
String query; |
ResultSet results; |
- Boolean ret_val = false; |
+ Boolean ret_val = true; |
double slope_r = 0.0; |
double theta = 0.0; |
double lambda = 0.0; |
double length_m = 0.0; |
- double[] slopeDistance = {200.0, 300.0, 200.0, 180.0, 160.0, 150.0, 140.0, 130.0, 125.0, 120.0, 110.0, 100.0, 90.0, 80.0, 70.0}; |
- double[] palouseSlopeDistance = {350.0, 275.0, 225.0, 175.0, 150.0, 125.0, 100.0}; |
- boolean palouseRegion = isPalouseRegion; |
+ // Look up tables for slope length |
+ double[][] LightleWeesiesSlopeLength = {{0, .75, 100}, |
+ {.75, 1.5, 200}, |
+ {1.5, 2.5, 300}, |
+ {2.5, 3.5, 200}, |
+ {3.5, 4.5, 180}, |
+ {4.5, 5.5, 160}, |
+ {5.5, 6.5, 150}, |
+ {6.5, 7.5, 140}, |
+ {7.5, 8.5, 130}, |
+ {8.5, 9.5, 125}, |
+ {9.5, 10.5, 120}, |
+ {10.5, 11.5, 110}, |
+ {11.5, 12.5, 100}, |
+ {12.5, 13.5, 90}, |
+ {13.5, 14.5, 80}, |
+ {14.5, 15.5, 70}, |
+ {15.5, 17.5, 60}, |
+ {17.5, -1, 50}}; |
|
- if ((null != mukey) && !mukey.isEmpty()) { |
+ double[][] PalouseSlopeLength = {{0, 5.5, 350}, |
+ {5.5, 10.5, 275}, |
+ {10.5, 15.5, 225}, |
+ {15.5, 20.5, 175}, |
+ {20.5, 25.5, 150}, |
+ {25.5, 35.5, 125}, |
+ {35.5, -1, 100}}; |
+ double[][] slopeLength; |
+ |
+ if ((null != attribute.mukey) && !attribute.mukey.isEmpty()) { |
try ( |
java.sql.Connection conn = PGTools.getConnection("cfactor", LOG); |
java.sql.Statement statement = conn.createStatement();) { |
|
- query = "SELECT component.cokey, component.compname, component.wei as ifact, component.slope_r, component.slopelenusle_r as lsfact, component.tfact, component.comppct_r " |
+ query = "SELECT component.cokey, component.compname, component.wei as ifact, component.slope_r, component.slopelenusle_r as lsfact, component.tfact, component.comppct_r, " |
+ + " chorizon.cokey, chorizon.kffact, chorizon.kwfact, chorizon.sandtotal_r " |
+ " FROM ssurgo.component " |
- + " WHERE component.mukey='" + mukey + "' " |
+ + " INNER JOIN ssurgo.chorizon on component.cokey=chorizon.cokey " |
+ + " WHERE component.mukey='" + attribute.mukey + "' " |
+ " AND component.slope_r is not null" |
- + " ORDER BY component.comppct_r DESC;"; |
+ + " AND UPPER(component.majcompflag)='YES' " |
+ + " AND chorizon.hzdept_r=0 " |
+ + ((useKFact) ? " AND (chorizon.kffact is not null OR chorizon.kwfact is not null )" : " AND chorizon.sandtotal_r is not null ") + ";"; |
+ |
LOG.info(" ssurgo query=" + query); |
|
results = statement.executeQuery(query); |
LOG.info(" past the query execute"); |
|
- if (null != results) { |
- results.next(); //Only need the first result row, since it will contain the larges comppct_r value, due to the order by clause. |
+ attribute.horizons = new ArrayList<>(); |
|
- this.aoa_dom_comp = results.getString("cokey"); |
- this.aoa_dom_compname = results.getString("compname"); |
- this.aoa_ifactor = results.getDouble("ifact"); |
+ while (results.next()) { |
+ V1_1.componentAttributes tHorizon = new V1_1.componentAttributes(); |
+ |
+ tHorizon.cokey = results.getString("cokey"); |
+ tHorizon.compname = results.getString("compname"); |
+ tHorizon.ifact = results.getDouble("ifact"); |
+ tHorizon.tfact = results.getDouble("tfact"); |
slope_r = results.getDouble("slope_r"); |
+ tHorizon.slope_r = slope_r; |
+ tHorizon.area_pct = (results.getDouble("comppct_r") / 100.0) * (attribute.gid_area / attribute.aoa_area); |
|
- if (!palouseRegion) { |
- if (slope_r >= 18) { |
- lambda = 50; |
- } else if (slope_r >= 16) { |
- lambda = 60; |
- } else if (slope_r >= 0 && slope_r < 1.0) { //maybe should just be <1.0 |
- lambda = 100; |
+ if (useKFact) { // Water Erodibility needed. |
+ if (this.isPalouse) { |
+ slopeLength = PalouseSlopeLength; |
} else { |
- lambda = slopeDistance[((int) slope_r) - 1]; // Maybe should be ((int) round(slope_r)) - 1 |
+ slopeLength = LightleWeesiesSlopeLength; |
} |
- } else if (slope_r < 6) { |
- lambda = palouseSlopeDistance[0]; |
- } else if (slope_r < 11) { |
- lambda = palouseSlopeDistance[1]; |
- } else if (slope_r < 16) { |
- lambda = palouseSlopeDistance[2]; |
- } else if (slope_r < 21) { |
- lambda = palouseSlopeDistance[3]; |
- } else if (slope_r < 26) { |
- lambda = palouseSlopeDistance[4]; |
- } else if (slope_r < 36) { |
- lambda = palouseSlopeDistance[5]; |
- } else if (slope_r < 46) { |
- lambda = palouseSlopeDistance[6]; |
- } |
|
- length_m = ((slope_r >= 5.0) ? 0.5 : (slope_r >= 3.5) ? 0.4 : (slope_r >= 1.0) ? 0.3 : 0.2); |
- theta = Math.atan(slope_r / 100.0); |
- |
- this.aoa_lsfactor = Math.pow((lambda / 72.6), length_m) * (65.41 * Math.pow(Math.sin(theta), 2.0) + 4.56 * Math.sin(theta) + 0.065); //results.getDouble("lsfact"); |
- |
- this.aoa_tfactor = results.getDouble("tfact"); |
- |
- results.close(); |
- |
- query = "SELECT chorizon.cokey, chorizon.kffact, chorizon.kwfact " |
- + "FROM ssurgo.chorizon " |
- + "WHERE ssurgo.chorizon.cokey='" + this.aoa_dom_comp + "'"; |
- LOG.info(" ssurgo query=" + query); |
- |
- results = statement.executeQuery(query); |
- LOG.info(" past the query execute"); |
- |
- if (null != results) { |
- results.next(); |
- |
- this.aoa_kfactor = results.getDouble("kffact"); |
- if (results.wasNull()) { |
- this.aoa_kfactor = results.getDouble("kwfact"); |
- if (results.wasNull()) { |
- this.aoa_kfactor = 0.02; |
+ for (int i = 0; i < slopeLength.length; i++) { |
+ if (slopeLength[i][1] != -1) { |
+ if ((slope_r >= slopeLength[i][0]) && (slope_r < slopeLength[i][1])) { |
+ lambda = slopeLength[i][2]; |
+ break; |
+ } else if (slope_r >= slopeLength[i][0]) { |
+ lambda = slopeLength[i][2]; |
+ break; |
+ } |
} |
} |
|
+ length_m = ((slope_r >= 5.0) ? 0.5 : (slope_r >= 3.5) ? 0.4 : (slope_r >= 1.0) ? 0.3 : 0.2); |
+ theta = Math.atan(slope_r / 100.0); |
+ |
+ tHorizon.lsfact = Math.pow((lambda / 72.6), length_m) * (65.41 * Math.pow(Math.sin(theta), 2.0) + 4.56 * Math.sin(theta) + 0.065); //results.getDouble("lsfact"); |
+ tHorizon.kffact = results.getDouble("kffact"); |
+ if (results.wasNull()) { |
+ tHorizon.kffact = results.getDouble("kwfact"); |
+ if (results.wasNull()) { |
+ this.error_msg = "Invalid kffact selected, empty columns in both kffact and kwfact"; |
+ ret_val = false; |
+ } else { |
+ attribute.horizons.add(tHorizon); |
+ ret_val = true; |
+ } |
+ } else { |
+ attribute.horizons.add(tHorizon); |
+ ret_val = true; |
+ } |
+ } else { |
+ tHorizon.lsfact = 0.0; |
+ tHorizon.sand_pct = results.getDouble("sandtotal_r"); |
+ attribute.horizons.add(tHorizon); |
ret_val = true; |
- } else { |
- this.error_msg += "Could not find a matching cokey for this intersected mapunit in the cHorizon table."; |
} |
+ } |
|
- } else { |
- this.error_msg += "Could not find a matching cokey for this intersected mapunit."; |
+ if (attribute.horizons.size() <= 0) { |
+ ret_val = false; |
+ this.error_msg += "No soil components and/or horizon data found for this mukey: " + attribute.mukey + " ."; |
} |
+ |
} catch (SQLException | csip.ServiceException ex) { |
this.error_msg += "Could not continue processing the component lookups for this mapunit intersect: " + ex.getMessage(); |
} |
@@ -469,6 +600,44 @@ |
public String AoA_Id; |
public String mukey; |
public double gid_area; |
+ public double aoa_area; |
+ public ArrayList<V1_1.componentAttributes> horizons; |
+ |
+ public ssurgoAttributes() { |
+ this.gid = ""; |
+ this.AoA_Id = ""; |
+ this.mukey = ""; |
+ this.gid_area = 0.0; |
+ this.aoa_area = 1.0; |
+ this.horizons = null; |
+ } |
+ } |
+ |
+ public static class componentAttributes { |
+ |
+ public String cokey; |
+ public String compname; |
+ public double ifact; |
+ public double slope_r; |
+ public double tfact; |
+ public double kffact; |
+ public double kwfact; |
+ public double area_pct; |
+ public double lsfact; |
+ public double sand_pct; |
+ |
+ public componentAttributes() { |
+ this.cokey = ""; |
+ this.compname = ""; |
+ this.ifact = 0.0; |
+ this.slope_r = 0.0; |
+ this.tfact = 0.0; |
+ this.kffact = 0.0; |
+ this.kwfact = 0.0; |
+ this.area_pct = 0.0; |
+ this.lsfact = 0.0; |
+ this.sand_pct = 0.0; |
+ } |
} |
|
} |