NASIS.java [tools/WepsSoilsIfcCreator/src/usda/weru/soil] Revision:   Date:
package usda.weru.soil;




import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.StringTokenizer;
import javax.measure.Measure;
import javax.measure.quantity.Length;
import javax.measure.unit.NonSI;
import javax.measure.unit.SI;
import javax.measure.unit.Unit;
import usda.weru.util.*;


public class NASIS//extends Record
{
    //Logs for errors and warnings.
    WepsMessageLog c_log = new WepsMessageLog();
    String	errIncFile = "";
    String	state = "";
    String	county = "";
    String	ssaname = "";
    String	ssaid = "";
    String	musym = "";
    String	compname = "";
    String	comppct = "";
    String	taxorder = "";
    String	localphase = "";
    double	losstolerance = 0.0;
    double	slope = 0.0;
    double	albedodry = 0.0;
//    double	fragmentcover = 0.0;
    int		numlay = 0;
    int		adjlay = 0;
    double	hzdept[];
    double	hzdepb[];
    double	hzthk[];
    String	texture[];
    boolean     stratified[];
    boolean     rv[];
    double	claytotal[];
    double	sandtotal[];
    double	silttotal[];
    double	sandvco[];
    double	sandco[];
    double	sandmed[];
    double	sandfine[];
    double	sandvf[];
    double	dbthirdbar[];
//	double	dbovendry[];
    double	wtenthbar[];
    double	wthirdbar[];
    double	w15thbar[];
    double	ksat[];
    double	cec7[];
    double	ecec[];
    double	om[];
    double	caco3[];
    double	ph1to1h2o[];
    double	ph01mcacl2[];
    double	fragvol[];
    double	lep[];
    
    //Not exactly sure where these variable should be placed in the list 1143.2
    double bedrockDepth ;
    double impermiableDepth;
    
        /*
         *The following soil layers and any below with the following textures are ignored by the interface.
        "by" - Boulders
        "cem" - Cemented
        "ce" - Coprogeneous earth
        "dur" - Duripan
        "frag" - Fragmental material
        "gyp" - Gypsiferous material
        "hpm" - Highly decomposed plant material
        "ind" - Indurated
        "marl" - Marl
        "mat" - Material
        "mpm" - Moderately decomposed plant material
        "muck" - Muck
        "mpt" - Mucky peat
        "or" - Orstein
        "opwd" - Oxide protected weathered bedrock
        "peat" - Peat
        "pc" - Petrocalcic
        "pf" - Petroferric
        "pgp" - Petrogypsic
        "pum" - Pumiceous
        "spm" - Slightly decomposed plant material
        "st" - Stones
        "udom" - Undecomposed organic matter
        "u" - Unknown texture
        "uwb" - Unweathered bedrock
        "var" - Variable
        "w" - Water
        "wb" - weathered bedrock
         */
    private String textureChk[] = {"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-"};
    
    public	NASIS() {
    }
    
    /**
     *
     * @param inpnumlay
     */
    public	NASIS(int inpnumlay)
    
    {
        numlay = inpnumlay;
        if (numlay == 0) return;
        
        hzdept		= new double[numlay];
        hzdepb		= new double[numlay];
        hzthk		= new double[numlay];
        texture		= new String[numlay];
        stratified      = new boolean[numlay];
        rv              = new boolean[numlay];
        claytotal	= new double[numlay];
        sandtotal	= new double[numlay];
        silttotal	= new double[numlay];
        sandvco		= new double[numlay];
        sandco		= new double[numlay];
        sandmed		= new double[numlay];
        sandfine	= new double[numlay];
        sandvf		= new double[numlay];
        dbthirdbar	= new double[numlay];
//		dbovendry	= new double[numlay];
        wtenthbar	= new double[numlay];
        wthirdbar	= new double[numlay];
        w15thbar	= new double[numlay];
        ksat		= new double[numlay];
        cec7		= new double[numlay];
        ecec		= new double[numlay];
        om			= new double[numlay];
        caco3		= new double[numlay];
        ph1to1h2o	= new double[numlay];
        ph01mcacl2	= new double[numlay];
        fragvol		= new double[numlay];
        lep			= new double[numlay];
        
    }
    
    /**
     *
     * @param con
     * @param key
     */
    
    public NASIS(Connection con) {
        this(30);			// arbitrary max number of layers for NASIS SQL data
        c_con = con;
    }
    
//    public NASIS(Connection con, ProgressMonitor progress) {
//        this(con);			// arbitrary max number of layers for NASIS SQL data
//        c_progress = progress;
//    }
    
    protected Connection c_con;
    
    /****************************************************************************/
    
    private static final String STRATIFIED_LAYER_KEY = "SR";
    /**
     *
     * @param nasis
     */
    public void averageStratifiedLayers(){
        StratifiedTextures st = StratifiedTextures.getInstance();
        Layers:
        for(int i = 0; i < numlay; i++){
            String layerTexture = texture[i];
            if(stratified[i] && rv[i] && layerTexture.trim().toUpperCase().startsWith(STRATIFIED_LAYER_KEY)){
                //This layer is stratified
                String[] textures = layerTexture.split(" ", -1);

                boolean first = true;
                double sand = 0;
                double clay = 0;
                double vsf = 0;
                int count = 0;
                for(String subTexture : textures){
                    subTexture = subTexture.trim();
                    if(first){
                        //skip the sr marker
                        first = false;
                        continue;
                    }
                    if(st.isTextureValid(subTexture)){
                        count++;
                        sand += st.getSand(subTexture);
                        sand /= count;

                        clay += st.getClay(subTexture);
                        clay /= count;

                        vsf += st.getVeryFineSand(subTexture);
                        vsf/= count;
                    }
                    else{
                        //can't fix up this layer
                        continue Layers;
                    }
                }
                if(sand + clay > 1){
                    //TODO: log error
                    continue Layers;
                }
                double silt = 1 - sand - clay;
                sandtotal[i] = sand * 100;
                silttotal[i] = silt * 100;
                claytotal[i] = clay * 100;

                sandvf[i] = vsf * 100;

                logMessage(WepsMessage.MessageSeverity.INFORMATION, "Averaged stratified layer values on layer " + String.valueOf(i + 1) + ".");

            }
        }

    }

    private	boolean	chkTexture(String texture)
    
    {
        String tmp = texture.toLowerCase();
        for (int idx = 0; idx < textureChk.length; idx++) {
            if (tmp.indexOf(textureChk[idx]) >= 0) {
                int tdx = tmp.indexOf(textureChk[idx]);		// check if token
                if (tdx > 0) {
                    if (!Character.isWhitespace(tmp.charAt(tdx-1))) continue;
                    if (tmp.charAt(tdx-1) != '-') continue;
                }
                if (tdx + textureChk[idx].length() < tmp.length()) {
                    if (!Character.isWhitespace(tmp.charAt(tdx+textureChk[idx].length())))
                        continue;
                }
                logMessage(WepsMessage.MessageSeverity.WARNING, "Layer dropped for texture " + texture + ".");
                return true;
            }
        }
        return false;
    }
    /**
     * Returns the message log of the object.
     */
    public WepsMessageLog getLog(){
        return c_log;
    }
    
    /****************************************************************************/
    /* adjust number of layers down to eliminate rock or highly organic soil */
    
    
    public	void	fixUpData()
    
    {
        for (adjlay = 1; adjlay < numlay; adjlay++) {
            
            // stop if layer is identified as rock, peat, etc.
            if (chkTexture(texture[adjlay])) {
                break;
            }
            
            // stop if both sand and clay are missing or zero
            if ((Double.isNaN(claytotal[adjlay]) || claytotal[adjlay] == 0.0) &&
                    (Double.isNaN(sandtotal[adjlay]) || sandtotal[adjlay] == 0.0)) {
                break;
            }
            // stop if layer does not have wet bulk density
            if (Double.isNaN(dbthirdbar[adjlay]) || dbthirdbar[adjlay] == 0.0) {
                break;
            }
            
        }
        
        
    }
    private void logMessage(WepsMessage.MessageSeverity severity, String message){
        logMessage(new WepsMessage(severity, message));
    }
    private void logMessage(WepsMessage message){
        c_log.logMessage(message);
    }
    
    private void logSubstitutionMessage(WepsMessage.MessageSeverity severity, String dataElement, String header, Object newValue){
        logSubstitutionMessage(severity, dataElement, header, newValue,-1);
    }
    
    private void logSubstitutionMessage(WepsMessage.MessageSeverity severity, String dataElement, String header, Object newValue, int layer){
        String layerTag = "";
        if (layer > -1){
            layerTag = " from layer " + (layer + 1);
        }
        WepsMessage tempMessage = new WepsMessage(severity, dataElement + " (" + header + ") is missing" + layerTag + ".  Null value was substituted with '" + newValue.toString() + "'.");
        logMessage(tempMessage);
        
    }
    
    private void logEstimatedMessage(WepsMessage.MessageSeverity severity, String dataElement, String header, Object newValue){
        logEstimatedMessage(severity, dataElement, header, newValue, -1);
    }
    
    private void logEstimatedMessage(WepsMessage.MessageSeverity severity, String dataElement, String header, Object newValue, int layer){
        String layerTag = "";
        if (layer > -1){
            layerTag = " from layer " + (layer + 1);
        }
        WepsMessage tempMessage = new WepsMessage(severity, dataElement + " (" + header + ") is missing" + layerTag + ".  Value was estimated to '" + newValue.toString() + "'.");
        logMessage(tempMessage);
    }
    private enum SoilElementTestAction{IGNORE, ERROR, ESTIMATE}
    
    private String testStringSoilElement(String value, String name, String header, SoilElementTestAction action, String actionValue){
        WepsMessage.MessageSeverity warning = WepsMessage.MessageSeverity.WARNING;
        WepsMessage.MessageSeverity error = WepsMessage.MessageSeverity.ERROR;
//1.  Test is not null
        if (value == null || value.length() == 0){
            switch(action){
                case IGNORE:
                    logSubstitutionMessage(warning, name, header, actionValue);
                    return actionValue;
                case ESTIMATE:
                    logEstimatedMessage(warning, name, header, actionValue);
                    return actionValue;
                case ERROR:
                    logMessage(error, name + " (" + header + ") is missing.");
                    return actionValue;
            }
        }
        return value;
    }
    private double testNumericSoilElement(double value, String units, double minimum, double maximum, String name, String header, SoilElementTestAction action, double actionValue){
        return testNumericSoilElement(value, units, minimum, maximum, name, header, action, actionValue, -1);
    }
    private double testNumericSoilElement(double value, String units, double minimum, double maximum, String name, String header, SoilElementTestAction action, double actionValue, int layer){
        WepsMessage.MessageSeverity warning = WepsMessage.MessageSeverity.WARNING;
        WepsMessage.MessageSeverity error = WepsMessage.MessageSeverity.ERROR;
        String layerTag = "";
        if (layer > -1){
            layerTag = " from layer " + (layer + 1);
        }
//1.  Test is not null
        if (Double.isNaN(value)){
            switch(action){
                case IGNORE:
                    logSubstitutionMessage(warning, name, header, actionValue, layer);
                    return actionValue;
                case ESTIMATE:
                    logEstimatedMessage(warning, name, header, actionValue, layer);
                    return actionValue;
                case ERROR:
                    logMessage(error, name + " (" + header + ") is missing" + layerTag + ".");
                    return actionValue;
            }
        }
        //2. Test in range if needed
        boolean outOfRange = false;
        if (!Double.isNaN(minimum) && value < minimum){
            outOfRange = true;
        }
        if (!Double.isNaN(maximum) && value > maximum){
            outOfRange = true;
        }
        if (outOfRange){
            switch(action){
                case IGNORE:
                    logSubstitutionMessage(warning, name, header, Double.toString(actionValue) + " " + units, layer);
                    return actionValue;
                case ESTIMATE:
                    logEstimatedMessage(warning, name, header, Double.toString(actionValue) + " " + units, layer);
                    return actionValue;
                case ERROR:
                    logMessage(error, name + " (" + header + ")" + layerTag + " is outside the acceptable range (" + Double.toString(minimum) + " to " + Double.toString(maximum) + ").");
                    return actionValue;
            }
        }
        return value;
    }
    
    public void testData(){
        
        SoilElementTestAction ignore = SoilElementTestAction.IGNORE;
        SoilElementTestAction ignore2 = SoilElementTestAction.IGNORE;
        SoilElementTestAction estimate = SoilElementTestAction.ESTIMATE;
        SoilElementTestAction error = SoilElementTestAction.ERROR;
        
        if (!estimateNullValues){
            //We don't want to estimate
            estimate = error;
            ignore2 = error;
        
            //Test values that must be present in surgo
            state = testStringSoilElement(state, "State", "state", SoilElementTestAction.IGNORE, "unknown");

        }
        
        //Errors
        if (numlay <= 0){
            logMessage(WepsMessage.MessageSeverity.ERROR, "No layers found in soil record.");
        }
        
        //Soil Components
        compname = testStringSoilElement(compname, "Component Name", "compname", ignore, "unknown");
        comppct = testStringSoilElement(comppct, "Component Percent", "comppct_r", ignore, "unknown");
        taxorder = testStringSoilElement(taxorder, "Taxonomic Order", "taxorder", ignore, "unknown");
        localphase = testStringSoilElement(localphase, "Local Phase", "localphase", ignore, "unknown");
        losstolerance = testNumericSoilElement(losstolerance, "t/ac/yr", 1, 5, "Soil Loss Tolerance", "losstolerance", ignore, -1);
        slope = testNumericSoilElement(slope, "fraction", 0, 999, "Slope Gradient", "slope", ignore, -1);
        albedodry = testNumericSoilElement(albedodry, "fraction", 0, 1, "Dry Soil Albedo", "albedodry", estimate, 0.6/Math.exp(0.4*om[0]));
        //End Soil Components
        
        //Map Unit
        musym = testStringSoilElement(musym, "Map Symbol", "musym", ignore2, "no map symbol");
        //End Map Unit
        
        //MuAggatt
        bedrockDepth = testNumericSoilElement(bedrockDepth, "cm", 0, 9999, "Bedrock Depth", "bedrockDepth", ignore, 9999);
        //End MuAggatt
        
        //CoRestrictions
        impermiableDepth = testNumericSoilElement(impermiableDepth, "", 0, 9999, "Impermiable/Restrictive Depth", "resdept_r_min", ignore, 9999);
        //End CoRestrictions
        
        //Legend
        ssaname = testStringSoilElement(ssaname, "Area Name/Area State", "areaname", ignore2, "no area name, no area state");
        ssaid = testStringSoilElement(ssaid, "Area Symbol", "areasymbol", ignore2, "no area symbol");
        //End Legend
        
        //Test Soil Layers, only tests layers that were not dropped for texture reasons.
        if (adjlay <= numlay){
            
            for(int layer = 0; layer < adjlay; layer++){
                hzdept[layer] = testNumericSoilElement(hzdept[layer], "cm", 0, 999, "Layer Depth Top", "hzdept", error, Double.NaN, layer);
                hzdepb[layer] = testNumericSoilElement(hzdepb[layer], "cm", 0, 999, "Layer Depth Bottom", "hzdepb", error, Double.NaN, layer);
                hzthk[layer] = hzdepb[layer] - hzdept[layer];
                hzthk[layer] = testNumericSoilElement(hzthk[layer], "cm", 0.01, 1000, "Layer Thickness", "hzthk", estimate,hzdept[layer] - hzdepb[layer], layer);
                claytotal[layer] = testNumericSoilElement(claytotal[layer], "weight %", 0, 100, "Total Clay Content", "claytotal", error, Double.NaN, layer);
                sandtotal[layer] = testNumericSoilElement(sandtotal[layer], "weight %", 0, 100, "Total Sand Content", "sandtotal", error, Double.NaN, layer);
                
                double calculatedSiltTotal = 100 - (claytotal[layer] + sandtotal[layer]);                
                silttotal[layer] = testNumericSoilElement(silttotal[layer], "weight %", calculatedSiltTotal * 0.999, calculatedSiltTotal * 1.001,"Total Silt Content", "silttotal", SoilElementTestAction.ESTIMATE, calculatedSiltTotal, layer);
                
                sandvf[layer] = testNumericSoilElement(sandvf[layer], "weight %", 0, 100, "Very Fine Sand", "sandvf", error, Double.NaN, layer);
                dbthirdbar[layer] = testNumericSoilElement(dbthirdbar[layer], "g/cm3", .02, 2.60, "1/3 Bar Bulk Density", "dbthirdbar", error, Double.NaN, layer);

                //the value will be estimated by ifc
                wthirdbar[layer] = testNumericSoilElement(wthirdbar[layer], "volume %", 0, 80, "1/3 bar water ", "wthirdbar", ignore, Double.NaN, layer);
                
                om[layer] = testNumericSoilElement(om[layer], "weight %", 0, 100, "Organic Matter Content", "om", error, Double.NaN, layer);
                caco3[layer] = testNumericSoilElement(caco3[layer], "weight %", 0, 110, "CaCO3 Equivalent", "caco3", error, Double.NaN, layer);

//                cec7[layer] = testNumericSoilElement(cec7[layer], "weight %", 0, 110, "CEC7 or ECEC", "cec7", error, Double.NaN, layer);
                if (Double.isNaN(cec7[layer]) && Double.isNaN(ecec[layer])){
                    logMessage(WepsMessage.errorMessage("CEC7 and ECEC values are missing from layer " + (layer + 1) + "."));
                }
                //total sand and total clay can not be greater than 100% combined.
                if (claytotal[layer] + sandtotal[layer] > 100){
                    logMessage(WepsMessage.errorMessage("Total Clay and Sand content for layer " + (layer + 1 ) + " exceeds 100%." ));
                }
                
//                fractionRock = (double[]) nasinp.fragvol.clone();				// rock %
//        veryCoarseSandFraction = (double[]) nasinp.sandvco.clone();				// coarseSandFraction
//        coarseSandFraction = (double[]) nasinp.sandco.clone();				// coarseSandFraction
//        mediumSandFraction = (double[]) nasinp.sandmed.clone();				// mediumSandFraction
//        fineSandFraction = (double[]) nasinp.sandfine.clone();				// fineSandFraction
//        veryFineSandFraction = (double[]) nasinp.sandvf.clone();

                if(!estimateNullValues){
                    //Test values per layer
                    if (Double.isNaN(ph1to1h2o[layer]) && Double.isNaN(ph01mcacl2[layer])){
                        logMessage(WepsMessage.errorMessage("pH 1:1 H2O and 0.01 MCaCl2 values are missing from layer " + (layer + 1) + "."));
                    }
                    ksat[layer] = testNumericSoilElement(ksat[layer], "um/s", 0, 705, "Ksat", "ksat", error, Double.NaN, layer);
                    w15thbar[layer] = testNumericSoilElement(w15thbar[layer], "volume %", 0,70, "15 bar water (wp)", "wfifteenbar", ignore, Double.NaN, layer);
                }
                //Special case for LEP
                if (Double.isNaN(lep[layer])) {
                    if (estimateNullValues){
                        //Estimate a value
                        
                        double bsd = SoilUtil.estimateSettledBulkDensity(claytotal[layer], sandtotal[layer], om[layer]);
                        if (wthirdbar[layer] > bsd){
                            bsd = wthirdbar[layer];
                            String msgText =  "Settled bulk density adjusted: Set to wet bulk density.";                            
                            c_log.logMessage(WepsMessage.warningMessage(msgText));
                        }
                        else{
                            String msgText =  "Settled bulk density estimated: " + Util.formatDouble(bsd, 4);
                            c_log.logMessage(WepsMessage.warningMessage(msgText));
                        }
                        
                        double lepc = SoilUtil.estimateLinearExtensibility(bsd, wthirdbar[layer]);
                        lep[layer] = testNumericSoilElement(lep[layer], "%", 0, 30, "Linear Extensibility", "lep", estimate, lepc, layer);                                                                    
                    }
                    else{
                        if (om[layer] > omFractionThreshold){
                            lep[layer] = testNumericSoilElement(lep[layer], "%", 0, 30, "Linear Extensibility", "lep", estimate, 0.0d, layer);                                                                    
                        }
                        else{
                            lep[layer] = testNumericSoilElement(lep[layer], "%", 0, 30, "Linear Extensibility", "lep", error, Double.NaN, layer);                                                                    
                        }                     
                    }                    
                }
                else{
                       lep[layer] = testNumericSoilElement(lep[layer], "%", 0, 30, "Linear Extensibility", "lep", error, Double.NaN, layer);                                                                    
                }
                
                
            }
        }
        
        //Append to the error variable
        for (WepsMessage message : c_log.getMessages()){
            errIncFile += "# " + message.toString() + "\n";
        }
        
        
        
    }
    protected void copyComponent(ResultSet rs) {
        compname = getValueFromRS(rs, "compname", null);
        comppct = getValueFromRS(rs, "comppct_r", null);
        taxorder = getValueFromRS(rs, "taxorder", null);
        localphase = getValueFromRS(rs, "localphase", "");
        String temp = getValueFromRS(rs, "tfact", "0");
        if (temp.trim().length() == 0) temp = "0";
        losstolerance = Integer.parseInt(temp);
//            losstolerance = getValueFromRS(rs, "tfact");
        slope = getValueFromRS(rs, "slope");
        albedodry = getValueFromRS(rs, "albedodry");
    }
    
    protected void copyMapUnit(ResultSet rs) {
        musym = getValueFromRS(rs, "musym", null);
    }
    
    protected void copyMuAggatt(ResultSet rs) {
        bedrockDepth = getValueFromRS(rs, "brockdepmin");
    }
    
    protected void copyCoRestrictions(ResultSet rs) {
        impermiableDepth = getValueFromRS(rs, "resdeptmin");
    }
    
    
    
    /********************************************************************** wjr */
    
    protected void copyLegend(ResultSet rs) {
        ssaname = getValueFromRS(rs, "areaname", null);
        ssaid = getValueFromRS(rs, "areasymbol", null);
        Date cordate = getDateValueFromRS(rs, "cordate");
        
        Date archiveddate = getDateValueFromRS(rs, "ssurgoarchived");
        String tabularVersion = getStringValueFromRS(rs, "tabularversion");

        DateFormat format = new SimpleDateFormat("yyyy-MM-dd");

        if(cordate != null){
            logMessage(WepsMessage.MessageSeverity.INFORMATION, "Correlation Date: " + format.format(cordate));
        }
        if(archiveddate != null){
            logMessage(WepsMessage.MessageSeverity.INFORMATION, "SSURGO Archived Date: " + format.format(archiveddate));
        }
        
        if(tabularVersion != null && tabularVersion.trim().length() > 0){
            logMessage(WepsMessage.MessageSeverity.INFORMATION, "Tabular Version: " + tabularVersion);
        }

        String[] parts = ssaname.split(",", -1);
        if(parts.length == 0){
            state = null;
            county = "";
        }
        else if (parts.length ==1){
            state = parts[0].trim();
            county = "";
        }
        else{
            county = parts[0].trim();
            state = parts[1].trim();
        }
    }
    
    /********************************************************************** wjr */
    
    protected String getStringValueFromRS(ResultSet rs, String fieldName){
        try{
            return rs.getString(fieldName);
        }
        catch(Exception e){
            return null;
        }
    }

    protected int getIntergerValueFromRS(ResultSet rs, String fieldName){
        try{
            return rs.getInt(fieldName);
        }
        catch(Exception e){
            return Integer.MIN_VALUE;
        }
    }

    protected Date getDateValueFromRS(ResultSet rs, String fieldName){
        try{
            return rs.getDate(fieldName);
        }
        catch(Exception e){
            return null;
        }
    }

    protected double getValueFromRS(ResultSet rs, String fieldName){
        return getValueFromRS(rs, fieldName, Double.NaN);
    }
    
    protected double getValueFromRS(ResultSet rs, String fieldName, double defaultValue) 	{
        try {
            String rtnStr = rs.getString(fieldName + "_r");
            if (rtnStr == null) throw new SQLException();
            try {
                return Double.parseDouble(rtnStr);
            } catch (NumberFormatException g) {
                throw new SQLException();
            }
        } catch (SQLException e) {
            try {
                String rtn_l = rs.getString(fieldName + "_l");
                String rtn_h = rs.getString(fieldName + "_h");
                if (rtn_l == null || rtn_h == null) throw new SQLException();
                try {
                    return (Double.parseDouble(rtn_l) + Double.parseDouble(rtn_h)) / 2;
                } catch (NumberFormatException h) {
                    throw new SQLException();
                }
            } catch (SQLException f) {
                return defaultValue;
            }
        } catch (NullPointerException npe){
            return defaultValue;
        }
    }
    
    protected String getValueFromRS(ResultSet resultSet, String fieldName, String defaultValue ){
        try{
            String temp = resultSet.getString(fieldName);
            return temp != null ? temp : defaultValue;
        } catch(Exception e){
            return defaultValue;
        }
    }
    
    /********************************************************************** wjr */
    
    protected void copyHorizon(ResultSet rs, int idx) {
        this.hzdept[idx] = getValueFromRS(rs, "hzdept");
        this.hzdepb[idx] = getValueFromRS(rs, "hzdepb");
        this.claytotal[idx] = getValueFromRS(rs, "claytotal");
        this.sandtotal[idx] = getValueFromRS(rs, "sandtotal");
        this.silttotal[idx] = getValueFromRS(rs, "silttotal");
        sandvco[idx] = getValueFromRS(rs, "sandvc", 0);
        sandco[idx] = getValueFromRS(rs, "sandco", 0);
        this.sandmed[idx] = getValueFromRS(rs, "sandmed", 0);
        this.sandfine[idx] = getValueFromRS(rs, "sandfine", 0);
        this.sandvf[idx] = getValueFromRS(rs, "sandvf");
        this.dbthirdbar[idx] = getValueFromRS(rs, "dbthirdbar");
//		this.dbovendry[idx] = getValueFromRS(rs, "dbovendry");
//		this.wtenthbar[idx] = getValueFromRS(rs, "wtenthbar");
        this.wtenthbar[idx] = -9.9;
        this.wthirdbar[idx] = getValueFromRS(rs, "wthirdbar");
        this.w15thbar[idx] = getValueFromRS(rs, "wfifteenbar");
        this.ksat[idx] = getValueFromRS(rs, "ksat");
        
        this.cec7[idx] = getValueFromRS(rs, "cec7");
        this.ecec[idx] = getValueFromRS(rs, "ecec");
        // I think my error checking will work - it compiles :-) However, my test case error is not because both fields are null, but that one is null for one layer and the other is null for another and the NASIS(SSURGO) to IFC code can't handle that correctly (yet) - LEW
//                if ((this.cec7[idx] < 0.0) && (this.ecec[idx] < 0.0)) //System.err.println("NASIS_copyHorizon: cec7 or ecec fields not populated ");
//                if ((this.cec7[idx] < 0.0) && (this.ecec[idx] < 0.0)) throw new FieldNotPopulated("cec7 and ecec");
        
        this.om[idx] = getValueFromRS(rs, "om");
        this.caco3[idx] = getValueFromRS(rs, "caco3");
        this.ph1to1h2o[idx] = getValueFromRS(rs, "ph1to1h2o");
        this.ph01mcacl2[idx] = getValueFromRS(rs, "ph01mcacl2");
        this.lep[idx] = getValueFromRS(rs, "lep");
        
    }
    
    /********************************************************************** wjr */
    
    protected void copyFrags(ResultSet rs, int idx) {
        this.fragvol[idx] = 0.0;
        try {
            while (rs.next()) {
                double tempFragVol = getValueFromRS(rs, "fragvol");
                this.fragvol[idx] += (Double.isNaN(tempFragVol)) ? 0.0 : tempFragVol;
            }
        } catch (SQLException e) {
        }
    }
    
    /********************************************************************** wjr */
    
    protected void copyFragCover(ResultSet rs, int idx) {
//        this.fragmentcover = 0.0;
        try {
            while (rs.next()) {
                double tempFragVol = getValueFromRS(rs, "sfragcov");
                this.fragvol[idx] += (Double.isNaN(tempFragVol)) ? 0.0 : tempFragVol;
            }
        } catch (SQLException e) {
        }
    }
    
    /********************************************************************** wjr */
    
    protected void copyTexture(ResultSet rs, int idx) {
        this.texture[idx] = "";
        try {
            rs.next();
            String temp = rs.getString("texture");
            temp = (temp == null) ? "" : temp;
            temp = temp.replace("-", "_");
            this.texture[idx] = temp;

            String stratifiedString = rs.getString("stratextsflag");
            stratified[idx] = stratifiedString != null ? (stratifiedString.trim().equalsIgnoreCase("yes")) : false;
            String rvString = rs.getString("rvindicator");
            rv[idx] = rvString != null ? (rvString.trim().equalsIgnoreCase("yes")) : false;

        } catch (SQLException e) {
        }
    }
    
    /********************************************************************** wjr */
    
    public boolean loadSQL(String cokey){
        return loadSQL(c_con, cokey);
    }
    
    
    public boolean loadSQL(Connection con, String cokey)
    
    {
        try {
            //boolean updateProgress = c_progress != null;
//            boolean updateProgress = false;
//            
//            if (updateProgress){
////                c_progress.setMinimum(0);
////                c_progress.setMaximum(10);
//            }
            String mukey = null;
            String lkey = null;
            String chkey = null;
            
//            if (updateProgress){
//                c_progress.setProgress(1);
//            }
            Statement stmt = con.createStatement();
//			String query = new String("SELECT * FROM component WHERE cokey = '"+cokey+"'");
            String query = "SELECT compname,comppct_r,taxorder,slope_r,albedodry_r,mukey,localphase,tfact FROM ssurgo.component WHERE cokey = '"+cokey+"'";


            
            ResultSet rs = stmt.executeQuery(query);
            boolean tmp = rs.next();
            if(!tmp) {
//                if (updateProgress){
//                    c_progress.setProgress(10);
//                }
                return false;
            }
            copyComponent(rs);

            //can test taxorder now to see if organic
            if(ConfigData.getDefault().getSoilTestOrganic()){
                if("histosols".equalsIgnoreCase(taxorder)){
                    throw new OrganicSoilException();
                }
            }
            
            mukey = rs.getString("mukey");
            rs.close();
            
//            if (updateProgress){
//                c_progress.setProgress(2);
//            }
            query = "SELECT musym,lkey FROM ssurgo.mapunit WHERE mukey = '"+mukey+"'";
            
            rs = stmt.executeQuery(query);
            tmp = rs.next();
            copyMapUnit(rs);
            
            lkey = rs.getString("lkey");
            rs.close();
            
//1143.2
//            if (updateProgress){
//                c_progress.setProgress(3);
//            }
            //Bedrock Depth
            query = "SELECT brockdepmin AS brockdepmin_r FROM ssurgo.muaggatt WHERE mukey = '"+mukey+"'";
            rs = stmt.executeQuery(query);
            tmp = rs.next();
            copyMuAggatt(rs);
            rs.close();
            
//1143.2
//            if (updateProgress){
//                c_progress.setProgress(4);
//            }
            //Restritive Depth
            query = "SELECT Min(resdept_r) AS resdeptmin_r FROM  ssurgo.corestrictions WHERE cokey='"+cokey+"' GROUP BY cokey";
            rs = stmt.executeQuery(query);
            tmp = rs.next();
            copyCoRestrictions(rs);
            
            rs.close();
            
            
//            if (updateProgress){
//                c_progress.setProgress(5);
//            }
            try{
                query = "SELECT areaname,areasymbol,cordate,ssurgoarchived,tabularversion FROM ssurgo.legend WHERE lkey = '"+lkey+"'";
                rs = stmt.executeQuery(query);
            }
            catch(SQLException sqle){
                query = "SELECT areaname,areasymbol,cordate,ssurgoarchived FROM ssurgo.legend WHERE lkey = '"+lkey+"'";
                rs = stmt.executeQuery(query);
            }
            tmp = rs.next();
            copyLegend(rs);
            
            rs.close();
            
//            if (updateProgress){
//                c_progress.setProgress(6);
//            }
            Statement stmtFrags = con.createStatement();
            Statement stmtTexture = con.createStatement();
//			query = new String("SELECT * FROM chorizon WHERE cokey = '"+cokey+"'");
            
            String[] hrzFlds = {"hzdept", "hzdepb", "claytotal", "sandtotal","silttotal",
            "sandvc", "sandco", "sandmed", "sandfine",
            "sandvf", "dbthirdbar", "wtenthbar",
            "wthirdbar", "wfifteenbar", "cec7", "ecec", "om",
            "caco3", "ph1to1h2o", "ph01mcacl2", "lep", "ksat"};
            StringBuffer sb = new StringBuffer("SELECT ");
            for (int idx = 0; idx < hrzFlds.length; idx++) {
                sb.append(hrzFlds[idx] + "_r,");
                sb.append(hrzFlds[idx] + "_l,");
                sb.append(hrzFlds[idx] + "_h,");
            }
            //hzdept_r, (hzdept_l + hzdept_h) / 2
            sb.append("chkey, desgnmaster, hzname FROM ssurgo.chorizon WHERE cokey = '"+cokey+"' ORDER BY hzdept_r ASC");   //Order by depth to make sure layers are in the correct order.
            
            String chorizonQuery = sb.toString();
            rs = stmt.executeQuery(chorizonQuery);
            int layerNum = -1;
            int i = 0;

            boolean foundAMineralLayer = false;

            //up to this amount is allowed to be ignored
            double maxOrganicDepth = ConfigData.getDefault().getSoilMaxOrganicDepth().doubleValue(SI.MILLIMETER);

            //as we loop over the layers, we add up how much organic layer has been ignored
            double usedOrganicDepth = 0;
            while (rs.next()) {
                chkey = rs.getString("chkey");
                
                String chtexturegrpQuery = "SELECT texture, stratextsflag, rvindicator FROM ssurgo.chtexturegrp WHERE chkey = '"+chkey+"'";
                ResultSet rsTexture = stmtTexture.executeQuery(chtexturegrpQuery);
                try{
                    layerNum++;
                    //System.err.println("incrementing layer number count to=" + layerNum);                    

                    //do we ignore an organic surface?
                    if(ConfigData.getDefault().isSkipOrganicSoilSurfaceLayers() && !foundAMineralLayer && usedOrganicDepth <= maxOrganicDepth){

                        String hzname = getValueFromRS(rs, "hzname", null);
                        String desgnmaster = getValueFromRS(rs, "desgnmaster", null);
                        double om = getValueFromRS(rs, "om");
                        //convert from percent to fraction
                        om = om / 100d;
                        String text = getValueFromRS(rsTexture, "texture", null);

                        boolean organic = isLayerOrganic(hzname, desgnmaster, text, om);

                        if(organic){
                            //data mart is in cm not mm
                            double thicknessMiliMeter = getValueFromRS(rs, "hzthk", getValueFromRS(rs, "hzdepb", Double.NaN)) * 10 ;

                            usedOrganicDepth += thicknessMiliMeter;
                            if(usedOrganicDepth <= maxOrganicDepth){
                                //still allowed to ignore
                                
                                logMessage(WepsMessage.MessageSeverity.INFORMATION, "Organic layer ignored.");
                                continue;
                            }
                            else{   
                                Measure<Double, Length> max = Measure.valueOf(maxOrganicDepth, SI.MILLIMETER);
                                Measure<Double, Length> used = Measure.valueOf(usedOrganicDepth, SI.MILLIMETER);

                                NumberFormat format = DecimalFormat.getNumberInstance();

                                boolean isSIUnits = Util.SIUnits.equals(Util.SIUnits);

                                Unit<Length> units = isSIUnits ? SI.MILLIMETER : NonSI.INCH;

                                if(i == 0){
                                    throw new OrganicSoilException("The organic surface layer depth exceeded the maximum ignorable depth.\n" + format.format(used.doubleValue(units)) + units + " > " + format.format(max.doubleValue(units)) + units);
                                }
                                else{
                                    throw new OrganicSoilException("The soil layer below the ignored organic layer is still organic.");
                                }
                                                                
                            }

                        }
                        else{
                            //not organic, so we're good to go
                            foundAMineralLayer = true;
                        }
                                                            
                    }

                    copyHorizon(rs, layerNum);

                    String chfragsQuery = "SELECT fragvol_r,fragvol_h,fragvol_l FROM ssurgo.chfrags WHERE chkey = '"+chkey+"'";
                    ResultSet rsFrags = stmtFrags.executeQuery(chfragsQuery);
                    copyFrags(rsFrags, layerNum);
                    rsFrags.close();

                    //already executed the query for the organic tests
                    copyTexture(rsTexture, layerNum);                    
                    // why is the number of layers incremented here, highly suspect...
//                    layerNum++;
//                    System.err.println("incrementing layer number count to=" + layerNum);                    
                }
                finally{
                    //make sure we close
                    rsTexture.close();
                    //System.err.println("Closed the rsTexture result set. Incrementing i to=" + i+1);
                    i++;
                }
            }
            //System.err.println("closing the master result set and assigning number of layers to=" + layerNum);
            rs.close();
            this.numlay = layerNum;

            //If the config option is set, average the stratified layers.
            if(ConfigData.getDefault().isAverageStratifiedSoilLayers()){
                averageStratifiedLayers();
            }
            
//            if (updateProgress){
//                c_progress.setProgress(7);
//            }
            fixUpData();
            
//            if (updateProgress){
//                c_progress.setProgress(8);
//            }
            testData();
//            if (updateProgress){
//                c_progress.setProgress(10);
//            }
            
            
            for (i=0;i<numlay;i++)
            {
                // stop check soil layers for valid data if we encounter a 
                // special texture such as rock, peat, etc. that identifies that
                // we should stop drilling down further into layers...
                if (chkTexture(texture[i])) {
                    break;
                }
                
                if (Double.isNaN(om[i]))
                {
                    // commented out code to check for indexing error
//                    String text = "number of layers is claimed to be=" + numlay + "\n";
//                    for (int jj=0;jj<i;jj++)
//                        text += "Organic material index=" + jj + ":" + om[jj] + "\n";
                    //throw new InvalidSoilException(text + "organic material for layer " + i + " is missing.");
                    //throw new InvalidSoilException("organic material for layer " + i + " is missing.");
                }
                
                if (!checkMinMax(om[i], 0.0, 100.0))
                {
                    //throw new InvalidSoilException("organic material for layer " + i + " has an invalid value=" + om[i]);
                }
                
                if (Double.isNaN(sandtotal[i])) 
                {
                    //throw new InvalidSoilException("sand total for layer " + i + " is missing.");
                }
                
                if (Double.isNaN(claytotal[i]))
                {
                    //throw new InvalidSoilException("clay total for layer " + i + " is missing.");
                }
                
                if ((sandtotal[i] + claytotal[i]) > 100.0) 
                {
                   //throw new InvalidSoilException("sand + clay total for layer " + i + " is too large.");
                }
                
                if (!checkMinMax(sandtotal[i], 0.0, 100.0))
                {
                    //throw new InvalidSoilException("sand total for layer " + i + " has an invalid value.");
                }
                    
                if (!checkMinMax(claytotal[i], 0.0, 100.0))
                {
                    //throw new InvalidSoilException("clay total for layer " + i + " has an invalid value.");
                }
                
                if (Double.isNaN(sandvf[i]))
                {
                    //throw new InvalidSoilException("sand fractions very fine for layer " + i + " is missing.");
                }
                
                if (!checkMinMax(sandvf[i], 0.0, 100.0))
                {
                    //throw new InvalidSoilException("sand fractions very fine for layer " + i + " has an invalid value.");
                }
                
                if (Double.isNaN(dbthirdbar[i])) // && (Double.isNaN(wtenthbar[i]))) 
                {
                    //throw new InvalidSoilException("bulk density for layer " + i + " is missing.");
                }

                if (!checkMinMax(dbthirdbar[i], 0.02, 2.60))
                {
                    //throw new InvalidSoilException("bulk density 3rd bar for layer " + i + " has an invalid value.");
                }
                
//                if (!checkMinMax(wtenthbar[i], 0.02, 2.60))
//                {
//                    throw new InvalidSoilException("bulk density 10th bar for layer " + i + " has an invalid value.");
//                }
                
                if (Double.isNaN(caco3[i]))
                {
                    //throw new InvalidSoilException("caco3 for layer " + i + " is missing.");
                }
                
                if (!checkMinMax(caco3[i],0.0,110.0))
                {
                    //throw new InvalidSoilException("caco3 for layer " + i + " has an invalid value.");
                }
                
                if (Double.isNaN(hzdept[i]))
                {
                    //throw new InvalidSoilException("layer depth top for layer " + i + " is missing.");
                }
                
                if (!checkMinMax(hzdept[i], 0.0, 999.0))
                {
                    //throw new InvalidSoilException("layer depth top for layer " + i + " has an invalid value.");
                }
                
                if (Double.isNaN(hzdepb[i]))
                {
                    //throw new InvalidSoilException("layer depth bottom for layer " + i + " is missing.");
                }
                
                if (!checkMinMax(hzdepb[i], 0.0, 999.0))
                {
                    //throw new InvalidSoilException("layer depth bottom for layer " + i + " has an invalid value.");
                }

                if (Double.isNaN(cec7[i]))  // && (Double.isNaN(ecec[i])))
                {
                    //throw new InvalidSoilException("cec7 for layer " + i + " is missing.");
                }
                
                if (!checkMinMax(cec7[i], 0.0, 400.0))
                {
                    //throw new InvalidSoilException("cec7 for layer " + i + " has an invalid value.");
                }
                
//                if (!checkMinMax(ecec[i], 0.0, 400.0))
//                {
//                    throw new InvalidSoilException("ecec for layer " + i + " has an invalid value.");
//                }
            }
            
        } catch (SQLException e) {
            e.printStackTrace();
//			for (; e != null; e = e.getNextException()) {
//				//System.err.println("\n" + e);
//				e.printStackTrace();
//			}
        }
        return true;
    }

    private boolean checkMinMax(double val, double min, double max) 
    {
        return ((val >= min) && (val <= max));
    }

    private boolean checkMinMax(int val, int min, int max) 
    {
        return ((val >= min) && (val <= max));
    }
    
    private static final String[] ORGANIC_TEXTURES = {"mpm", "mpt", "muck", "peat", "spm", "udom", "pdom", "hpm"};

    private static boolean isLayerOrganic(String hzname, String desgnmaster, String texture, double organicMatter){
        //horizon name
        if(hzname != null && hzname.startsWith("O")){
            return true;
        }

        if(desgnmaster != null && desgnmaster.startsWith("O")){
            return true;
        }

        for(String organicTexture : ORGANIC_TEXTURES){
            if(organicTexture.equals(texture)){
                return true;
            }
        }

        if(organicMatter > omFractionThreshold){
            return true;
        }

        //aka, a mineral layer
        return false;
    }
    
    private static boolean estimateNullValues = true;    
    
    public static void setEstimateNullValues(boolean flg) {
        estimateNullValues = flg;
    }
    
    public static boolean getEstimateNullValues() {
        return estimateNullValues;
    }
    
    private static double omFractionThreshold;
    
    public static void setOmFractionThreshold(double val) {
        omFractionThreshold = val;
    }
    
    public static double getOmFractionThreshold() {
        return omFractionThreshold;
    }
    
}