Horizon.java [src/soils] Revision: default  Date:
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package soils;

import csip.api.server.ServiceException;
import csip.utils.JSONUtils;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import static soils.Fragments.FRAGMENTS_LIST;
import static soils.TextureGroup.TEXTURE_GROUP_LIST;
import soils.db.tables.TableHorizon;
import soils.db.tables.TableHorizonCalculations;
import soils.exceptions.SDMException;
import soils.exceptions.WEPPException;
import soils.utils.EvalResult;
import soils.utils.SoilUtils;

/**
 *
 * @author <a href="mailto:shaun.case@colostate.edu">Shaun Case</a>
 */
public class Horizon {

  protected static ArrayList<String> horizonRequiredInputs = new ArrayList<>();
  protected static ArrayList<String> horizonUsedColumns = new ArrayList<>();
  protected static ArrayList<String> organicTextures = new ArrayList<>(Arrays.asList(
      "hpm",
      "mpm",
      "spm",
      //"mat", //?? This is an ignorable "cemented" layer isn't it??
      "mk-cl",
      "muck",
      "mpt",
      "peat",
      "pdom",
      "udom",
      "ce",
      "de"
  ));

  protected static ArrayList<String> restrictingTextures = new ArrayList<>(Arrays.asList(
      "cem-mat",
      "cem",
      "cem-s",
      "br",
      "dur",
      "ind",
      "or",
      "opwd",
      "pc",
      "pf",
      "pgp",
      "uwb",
      "wb"
  ));

  protected static ArrayList<String> restrictingTexturesPartialMatches = new ArrayList<>(Arrays.asList(
      "cem-",
      "cem ",
      " cem ",
      " cem",
      "pf-" //Perma-frost
  ));

  //These will get removed from their component before WEPP or WEPS runs...
  protected static ArrayList<String> nonCementedRestrictingLayers = new ArrayList<>(Arrays.asList(
      "cind",
      "frag",
      "gyp",
      "marl",
      "pum",
      "u",
      "var",
      "w",
      "mat",
      "sg",
      "art",
      "by",
      "cb",
      "cn",
      "fl",
      "g",
      "st"
  ));

  protected static ArrayList<String> ignoredTextureIfNotSurface = new ArrayList<>(Arrays.asList(
      "fine gypsum material"
  ));

  protected static final String[] TEXTURE_CHK = {"by", "cem", "ce", "dur", "frag", "gyp", "hpm", "ind", "marl", "mat",
    "mpm", "muck", "mpt", "or", "opwd",
    "peat", "pc", "pf", "pgp", "pum", "spm", "st", "udom",
    "u", "uwb", "var", "w", "wb", "cem-", "pf-"};

  public synchronized static void setRequiredInputs(List<String> horizonInputs) {
    horizonRequiredInputs.clear();
    if (null != horizonInputs) {
      horizonRequiredInputs.addAll(horizonInputs);
      horizonUsedColumns.addAll(horizonInputs);
    }
  }

  public synchronized static void setDefaultUsedColumns(List<String> usedColumns) {
    horizonUsedColumns.clear();
    if (null != usedColumns) {
      horizonUsedColumns.addAll(usedColumns);
    }
  }

  public synchronized static ArrayList<String> getDefaultUsedColumns() {
    return ((null != horizonUsedColumns) ? ((ArrayList<String>) horizonUsedColumns.clone()) : null);
  }

  public synchronized static ArrayList<String> getRequiedInputs() {
    return ((null != horizonRequiredInputs) ? ((ArrayList<String>) horizonRequiredInputs.clone()) : null);
  }

  private boolean selected = false;
  private ArrayList<String> selectedReason = new ArrayList<>();
  protected TableHorizon tableHorizon = new TableHorizon();
  protected TableHorizonCalculations tableHorizonCalculations = new TableHorizonCalculations();

  public LinkedHashMap<String, TextureGroup> textureGroups;
  public LinkedHashMap<String, Fragments> fragments;

  private double conductivity = Double.NaN;
  private double interrill = Double.NaN;
  private double rill = Double.NaN;
  private double shear = Double.NaN;
  private boolean surface = false;
  private boolean restricting = false;
  private boolean permiable = false;
  private Boolean mineral = null;
  private boolean useGenericHz1Values = false;
  private boolean useGenericHz2Values = false;
  private double fieldCapacitySWC;
  private double wiltingPointSWC;
  private double soilPh;

  /*
    public String chkey;
    public double om_r;
    public double hzthk_r;
    public double hzdept_r;
    public double hzdepb_r;
    public double kwfact;
    public double kffact;
    public double lsfact;
    public double sandtotal_r;  //AKA sand_pct
   */
  public Horizon() {
    fragments = new LinkedHashMap<>();
    textureGroups = new LinkedHashMap<>();
    tableHorizon.setUsedColumns(horizonUsedColumns);
    tableHorizon.setRequiredColumns(horizonRequiredInputs);
    tableHorizonCalculations.setRequiredColumns(horizonRequiredInputs);
    tableHorizonCalculations.setUsedColumns(horizonUsedColumns);
    selectedReason.add("ANY");
  }

  public Horizon(JSONArray dataJSON) throws ServiceException, JSONException {
    Map<String, JSONObject> horizonsDataArray = JSONUtils.preprocess(dataJSON);

    if ((null != dataJSON) && (dataJSON.length() > 0)) {
      tableHorizon.setUsedColumns(horizonUsedColumns);
      tableHorizon.setRequiredColumns(horizonRequiredInputs);
      tableHorizonCalculations.setRequiredColumns(horizonRequiredInputs);
      tableHorizonCalculations.setUsedColumns(horizonUsedColumns);

      tableHorizon.readValuesFromJSON(dataJSON);
      tableHorizonCalculations.readValuesFromJSON(dataJSON);
      selectedReason.add("ANY");

      selected = true;

      fragments = new LinkedHashMap<>();//We dont' currently use this, yet.
      textureGroups = new LinkedHashMap<>();
      setTextureGroups(JSONUtils.getJSONArrayParam(horizonsDataArray, TEXTURE_GROUP_LIST));

    } else {
      throw new ServiceException("No mapunit JSON specified.  A NULL or empty value was passed to SOILS_DATA.MapUnit()");
    }
  }

  public Horizon(ResultSet results) throws ServiceException {
    tableHorizon.setRequiredColumns(horizonRequiredInputs);
    tableHorizon.setUsedColumns(horizonUsedColumns);
    tableHorizonCalculations.setRequiredColumns(horizonRequiredInputs);
    tableHorizonCalculations.setUsedColumns(horizonUsedColumns);
    selectedReason.add("ANY");
    tableHorizon.readValuesFromSQL(results);
    tableHorizonCalculations.readValuesFromSQL(results);

    fragments = new LinkedHashMap<>();//We dont' currently use this, yet.  
    textureGroups = new LinkedHashMap<>();
  }

  private void setTextureGroups(JSONArray dataJSON) throws ServiceException, JSONException {

    if ((null != dataJSON) && (dataJSON.length() > 0)) {
      for (int i = 0; i < dataJSON.length(); i++) {
        TextureGroup textureGroup = new TextureGroup(dataJSON.getJSONArray(i));

        if (!textureGroups.containsKey(textureGroup.chtgkey())) {
          textureGroups.put(textureGroup.chtgkey(), textureGroup);
        } else {
          throw new ServiceException("Duplicate component passed to Component.setTextureGroups() from input JSON.  Texture group keys listed by horizon should be unique.");
        }
      }
    }
  }

  @Deprecated
  public void setTextureGroups(ResultSet results) throws SQLException, ServiceException {
    if (null != results) {
      TextureGroup textureGroup = new TextureGroup();
      textureGroup.readFromSQL(results);
      textureGroups.put(textureGroup.chtgkey(), textureGroup);
    }
  }

  public void setTextureGroups(ResultSet results, ArrayList<String> columnsUsed, ArrayList<String> textureColumns) throws SQLException, ServiceException {
    if (null != results) {
      if ((null != columnsUsed) && (!columnsUsed.isEmpty())) {
        TextureGroup textureGroup = new TextureGroup();
        textureGroup.setUsedColumns(columnsUsed);
        textureGroup.readFromSQL(results, textureColumns);
        textureGroups.put(textureGroup.chtgkey(), textureGroup);
      }
    } else {
      throw new ServiceException("NULL ResultSet passed to setTextureGropus.  Cannot continue.");
    }
  }

//    public boolean readAllTextureData(ResultSet results) throws SQLException, ServiceException {
//        boolean ret_val = false;
//        if (null != results) {
//            while (results.next()) {
//
//            }
//        }
//
//        return ret_val;
//    }
  @Deprecated
  public void setFragments(ResultSet results) throws SQLException, ServiceException {
    while (results.next()) {
      boolean matchFound = false;
      Fragments tFragment = new Fragments();
      tFragment.readFromSQL(results);
      for (Fragments fragment : fragments.values()) {
        if (fragment.isEqual(tFragment)) {
          matchFound = true;
          break;
        }
      }
      if (!matchFound) {
        fragments.put(tFragment.chfragskey(), tFragment);
      }
    }
  }

  public void setFragments(ResultSet results, List<String> usedColumns) throws SQLException, ServiceException {
    if (null != results) {
      if (null != usedColumns) {
        while (results.next()) {
          boolean matchFound = false;
          Fragments tFragment = new Fragments();
          tFragment.setUsedColumns(usedColumns);
          tFragment.readFromSQL(results);
          for (Fragments fragment : fragments.values()) {
            if (fragment.isEqual(tFragment)) {
              matchFound = true;
              break;
            }
          }
          if (!matchFound) {
            fragments.put(tFragment.chfragskey(), tFragment);
          }
        }
      } else {
        throw new ServiceException("No used column list was passed to setFragments function.");

      }
    } else {
      throw new ServiceException("NULL ResultSet was passed to setFragments funciton.");
    }
  }

  public LinkedHashMap<String, TextureGroup> textureGroups() {
    return this.textureGroups;
  }

  public void readFromSQL(ResultSet results) throws ServiceException {
    tableHorizon.readValuesFromSQL(results);
    tableHorizonCalculations.readValuesFromSQL(results);

    //Update depth inches conversions in calculations table.
    hzdepb_r_in();
    hzdept_r_in();
  }

  public final void fieldCapacitySWC(double value) {
    fieldCapacitySWC = value;
  }

  public final void wiltingPointSWC(double value) {
    wiltingPointSWC = value;
  }

  public final void soilPh(double value) {
    soilPh = value;
  }

  public final double fieldCapacitySWC() {
    return fieldCapacitySWC;
  }

  public final double wiltingPointSWC() {
    return wiltingPointSWC;
  }

  public final double soilPh() {
    return soilPh;
  }

  public final void sar_r(double value) {
    tableHorizon.sar_r(value);
  }

  public final double sar_r() {
    return tableHorizon.sar_r();
  }

  public final void ec_r(double value) {
    tableHorizon.ec_r(value);
  }

  public final double ec_r() {
    return tableHorizon.ec_r();
  }

  public final void hzdept_l(double value) {
    tableHorizon.hzdept_l(value);
  }

  public final double hzdept_l() {
    return tableHorizon.hzdept_l();
  }

  public final void hzdept_h(double value) {
    tableHorizon.hzdept_h(value);
  }

  public final double hzdept_h() {
    return tableHorizon.hzdept_h();
  }

  public final void hzdepb_l(double value) {
    tableHorizon.hzdepb_l(value);
  }

  public final double hzdepb_l() {
    return tableHorizon.hzdepb_l();
  }

  public final void hzdepb_h(double value) {
    tableHorizon.hzdepb_h(value);
  }

  public final double hzdepb_h() {
    return tableHorizon.hzdepb_h();
  }

  public final void sandvc_l(double value) {
    tableHorizon.sandvc_l(value);
  }

  public final double sandvc_l() {
    return tableHorizon.sandvc_l();
  }

  public final void sandvc_r(double value) {
    tableHorizon.sandvc_r(value);
  }

  public final double sandvc_r() {
    return tableHorizon.sandvc_r();
  }

  public final void sandvc_h(double value) {
    tableHorizon.sandvc_h(value);
  }

  public final double sandvc_h() {
    return tableHorizon.sandvc_h();
  }

  public final void sandco_l(double value) {
    tableHorizon.sandco_l(value);
  }

  public final double sandco_l() {
    return tableHorizon.sandco_l();
  }

  public final void sandco_r(double value) {
    tableHorizon.sandco_r(value);
  }

  public final double sandco_r() {
    return tableHorizon.sandco_r();
  }

  public final void sandco_h(double value) {
    tableHorizon.sandco_h(value);
  }

  public final double sandco_h() {
    return tableHorizon.sandco_h();
  }

  public final void sandmed_l(double value) {
    tableHorizon.sandmed_l(value);
  }

  public final double sandmed_l() {
    return tableHorizon.sandmed_l();
  }

  public final void sandmed_r(double value) {
    tableHorizon.sandmed_r(value);
  }

  public final double sandmed_r() {
    return tableHorizon.sandmed_r();
  }

  public final void sandmed_h(double value) {
    tableHorizon.sandmed_h(value);
  }

  public final double sandmed_h() {
    return tableHorizon.sandmed_h();
  }

  public final void sandfine_l(double value) {
    tableHorizon.sandfine_l(value);
  }

  public final double sandfine_l() {
    return tableHorizon.sandfine_l();
  }

  public final void sandfine_r(double value) {
    tableHorizon.sandfine_r(value);
  }

  public final double sandfine_r() {
    return tableHorizon.sandfine_r();
  }

  public final void sandfine_h(double value) {
    tableHorizon.sandfine_h(value);
  }

  public final double sandfine_h() {
    return tableHorizon.sandfine_h();
  }

  public final void sandvf_l(double value) {
    tableHorizon.sandvf_l(value);
  }

  public final double sandvf_l() {
    return tableHorizon.sandvf_l();
  }

  public final void sandvf_r(double value) {
    tableHorizon.sandvf_r(value);
  }

  public final double sandvf_r() {
    return tableHorizon.sandvf_r();
  }

  public final void sandvf_h(double value) {
    tableHorizon.sandvf_h(value);
  }

  public final double sandvf_h() {
    return tableHorizon.sandvf_h();
  }

  public void useGenericSolParams(boolean value) {
    useGenericHz1Values = value;
  }

  public boolean useGenericHz1Values() {
    return useGenericHz1Values;
  }

  public boolean useGenericHz2Values() {
    return useGenericHz2Values;
  }

  public final void wtenthbar_l(double value) {
    tableHorizon.wtenthbar_l(value);
  }

  public final double wtenthbar_l() {
    return tableHorizon.wtenthbar_l();
  }

  public final void wtenthbar_r(double value) {
    tableHorizon.wtenthbar_r(value);
  }

  public final double wtenthbar_r() {
    return tableHorizon.wtenthbar_r();
  }

  public final void wtenthbar_h(double value) {
    tableHorizon.wtenthbar_h(value);
  }

  public final double wtenthbar_h() {
    return tableHorizon.wtenthbar_h();
  }

  public final void ecec_l(double value) {
    tableHorizon.ecec_l(value);
  }

  public final double ecec_l() {
    return tableHorizon.ecec_l();
  }

  public final void ecec_r(double value) {
    tableHorizon.ecec_r(value);
  }

  public final double ecec_r() {
    return tableHorizon.ecec_r();
  }

  public final void ecec_h(double value) {
    tableHorizon.ecec_h(value);
  }

  public final double ecec_h() {
    return tableHorizon.ecec_h();
  }

  public final void caco3_l(double value) {
    tableHorizon.caco3_l(value);
  }

  public final double caco3_l() {
    return tableHorizon.caco3_l();
  }

  public final void caco3_r(double value) {
    tableHorizon.caco3_r(value);
  }

  public final double caco3_r() {
    return tableHorizon.caco3_r();
  }

  public final void caco3_h(double value) {
    tableHorizon.caco3_h(value);
  }

  public final double caco3_h() {
    return tableHorizon.caco3_h();
  }

  public final void ph01mcacl2_l(double value) {
    tableHorizon.ph01mcacl2_l(value);
  }

  public final double ph01mcacl2_l() {
    return tableHorizon.ph01mcacl2_l();
  }

  public final void ph01mcacl2_r(double value) {
    tableHorizon.ph01mcacl2_r(value);
  }

  public final double ph01mcacl2_r() {
    return tableHorizon.ph01mcacl2_r();
  }

  public final void ph01mcacl2_h(double value) {
    tableHorizon.ph01mcacl2_h(value);
  }

  public final double ph01mcacl2_h() {
    return tableHorizon.ph01mcacl2_h();
  }

  public final void lep_l(double value) {
    tableHorizon.lep_l(value);
  }

  public final double lep_l() {
    return tableHorizon.lep_l();
  }

  public final void lep_r(double value) {
    tableHorizon.lep_r(value);
  }

  public final double lep_r() {
    return tableHorizon.lep_r();
  }

  public final void lep_h(double value) {
    tableHorizon.lep_h(value);
  }

  public final double lep_h() {
    return tableHorizon.lep_h();
  }

  public final void ksat_l(double value) {
    tableHorizon.ksat_l(value);
  }

  public final double ksat_l() {
    return tableHorizon.ksat_l();
  }

  public final void ksat_r(double value) {
    tableHorizon.ksat_r(value);
  }

  public final double ksat_r() {
    return tableHorizon.ksat_r();
  }

  public final void ksat_h(double value) {
    tableHorizon.ksat_h(value);
  }

  public final double ksat_h() {
    return tableHorizon.ksat_h();
  }

  public final void hzname(String value) {
    tableHorizon.hzname(value);
  }

  public final String hzname() {
    return tableHorizon.hzname();
  }

  public final void desgnmaster(String value) {
    tableHorizon.desgnmaster(value);
  }

  public final String desgnmaster() {
    return tableHorizon.desgnmaster();
  }

  public Horizon deepCopy() {
    Horizon newHorizon = new Horizon();

    deepCopy(newHorizon);

    return newHorizon;

  }

  public void deepCopy(Horizon newHorizon) {

    if (null != newHorizon) {
      tableHorizon.deepCopy(newHorizon.tableHorizon);
      tableHorizonCalculations.deepCopy(newHorizon.tableHorizonCalculations);

      LinkedHashMap<String, Fragments> tFragments = new LinkedHashMap<>();
      for (Fragments tValue : fragments.values()) {
        Fragments newValue = tValue.deepCopy();
        if (null != newValue) {
          tFragments.put(newValue.chfragskey(), newValue);
        }
      }

      if (tFragments.size() > 0) {
        newHorizon.setFragments(tFragments);
      }

      LinkedHashMap<String, TextureGroup> tTGroups = new LinkedHashMap<>();
      for (TextureGroup tValue : textureGroups.values()) {
        TextureGroup newValue = tValue.deepCopy();
        if (null != newValue) {
          tTGroups.put(newValue.chtgkey(), newValue);
        }
      }

      if (tTGroups.size() > 0) {
        newHorizon.setTextureGroups(tTGroups);
      }

      newHorizon.conductivity(conductivity);
      newHorizon.interrill(interrill);
      newHorizon.rill(rill);
      newHorizon.shear(shear);
      newHorizon.surface(surface);
      newHorizon.restricting(restricting);
      newHorizon.permiable(permiable);
      if (null != mineral) {
        newHorizon.mineral(mineral);
      } else {
        try {
          newHorizon.evaluateMineral();
        } catch (SDMException ex) {
          Logger.getLogger(Horizon.class.getName()).log(Level.SEVERE, null, ex);
        }
      }
      newHorizon.useGenericHz1Values(useGenericHz1Values);
      newHorizon.useGenericHz2Values(useGenericHz2Values);

      newHorizon.selected(selected());
      for (String reason : selectedReason) {
        newHorizon.selectedReason(reason);
      }

    }
  }

  public void setFragments(Map<String, Fragments> tFragments) {
    if ((null != tFragments) && (tFragments.size() > 0)) {
      fragments.clear();

      for (Fragments tFragment : tFragments.values()) {
        if (!fragments.containsKey(tFragment.chfragskey())) {
          fragments.put(tFragment.chfragskey(), tFragment);
        }
      }
    }
  }

  public void setTextureGroups(Map<String, TextureGroup> tTGroups) {
    if ((null != tTGroups) && (tTGroups.size() > 0)) {
      textureGroups.clear();

      for (TextureGroup tTGroup : tTGroups.values()) {
        if (!textureGroups.containsKey(tTGroup.chtgkey())) {
          textureGroups.put(tTGroup.chtgkey(), tTGroup);
        }
      }
    }
  }

  public void conductivity(double value) {
    conductivity = value;
  }

  public void interrill(double value) {
    interrill = value;
  }

  public void rill(double value) {
    rill = value;
  }

  public void shear(double value) {
    shear = value;
  }

  public void surface(boolean value) {
    surface = value;
  }

  public void restricting(boolean value) {
    restricting = value;
  }

  public void permiable(boolean value) {
    permiable = value;
  }

  public void mineral(boolean value) {
    mineral = value;
  }

  public String hasWeppWepsParams(String testModel) {
    String errorMessage = "";

    switch (testModel.toLowerCase()) {
      case "wepp":
        errorMessage += testWEPP();
        break;
      case "weps":
        errorMessage += testWEPS();
        break;
      default:
        errorMessage += testWEPP();
        errorMessage += testWEPS();
    }

    return errorMessage;
  }

  protected String testWEPS() {
    String errorMessage = "";

    //First average all potentially missing values if possible.
    hzdept_r(SoilUtils.averageMissingValue(hzdept_h(), hzdept_l(), hzdept_r()));
    hzdepb_r(SoilUtils.averageMissingValue(hzdepb_h(), hzdepb_l(), hzdepb_r()));
    claytotal_r(SoilUtils.averageMissingValue(claytotal_h(), claytotal_l(), claytotal_r()));
    sandtotal_r(SoilUtils.averageMissingValue(sandtotal_h(), sandtotal_l(), sandtotal_r())); // may be useless because the query removes null sandtotals
    silttotal_r(SoilUtils.averageMissingValue(silttotal_h(), silttotal_l(), silttotal_r()));
    sandvc_r(SoilUtils.averageMissingValue(sandvc_h(), sandvc_l(), sandvc_r(), 0));
    sandco_r(SoilUtils.averageMissingValue(sandco_h(), sandco_l(), sandco_r(), 0));
    sandmed_r(SoilUtils.averageMissingValue(sandmed_h(), sandmed_l(), sandmed_r(), 0));
    sandfine_r(SoilUtils.averageMissingValue(sandfine_h(), sandfine_l(), sandfine_r(), 0));
    sandvf_r(SoilUtils.averageMissingValue(sandvf_h(), sandvf_l(), sandvf_r()));
    dbthirdbar_r(SoilUtils.averageMissingValue(dbthirdbar_h(), dbthirdbar_l(), dbthirdbar_r()));
    wthirdbar_r(SoilUtils.averageMissingValue(wthirdbar_h(), wthirdbar_l(), wthirdbar_r()));
    wfifteenbar_r(SoilUtils.averageMissingValue(wfifteenbar_h(), wfifteenbar_l(), wfifteenbar_r()));
    ksat_r(SoilUtils.averageMissingValue(ksat_h(), ksat_l(), ksat_r()));
    cec7_r(SoilUtils.averageMissingValue(cec7_h(), cec7_l(), cec7_r()));
    ecec_r(SoilUtils.averageMissingValue(ecec_h(), ecec_l(), ecec_r()));
    om_r(SoilUtils.averageMissingValue(om_h(), om_l(), om_r()));
    caco3_r(SoilUtils.averageMissingValue(caco3_h(), caco3_l(), caco3_r()));
    ph1to1h2o_r(SoilUtils.averageMissingValue(ph1to1h2o_h(), ph1to1h2o_l(), ph1to1h2o_r()));
    ph01mcacl2_r(SoilUtils.averageMissingValue(ph01mcacl2_h(), ph01mcacl2_l(), ph01mcacl2_r()));
    lep_r(SoilUtils.averageMissingValue(lep_h(), lep_l(), lep_r()));

    if (EvalResult.testDefaultDouble(hzdept_r())) {
      errorMessage += " hzdept_r is empty";
    }
    if (EvalResult.testDefaultDouble(hzdepb_r())) {
      errorMessage += " hzdepb_r is empty";
    }
    if (EvalResult.testDefaultDouble(claytotal_r())) {
      errorMessage += " claytotal_r is empty";
    }
    if (EvalResult.testDefaultDouble(sandtotal_r())) {
      errorMessage += " sandtotal_r is empty";
    }
    if (EvalResult.testDefaultDouble(sandvf_r())) {
      errorMessage += " sandvf_r is empty";
    }
    if (EvalResult.testDefaultDouble(dbthirdbar_r())) {
      errorMessage += " dbthirdbar_r is empty";
    }

//        if (EvalResult.testDefaultDouble(silttotal_r())) {
//            errorMessage += " silttotal_r is empty";
//        }
//        if (EvalResult.testDefaultDouble(sandvc_r())) {
//            errorMessage += " sandvc_r is empty";
//        }
//        if (EvalResult.testDefaultDouble(sandco_r())) {
//            errorMessage += " sandco_r is empty";
//        }
//        if (EvalResult.testDefaultDouble(sandmed_r())) {
//            errorMessage += " sandmed_r is empty";
//        }
//        if (EvalResult.testDefaultDouble(sandfine_r())) {
//            errorMessage += " sandfine_r is empty";
//        }
//        if (EvalResult.testDefaultDouble(wthirdbar_r())) {
//            errorMessage += " wthirdbar_r is empty";
//        }
//        if (EvalResult.testDefaultDouble(wfifteenbar_r())) {
//            errorMessage += " wfifteenbar_r is empty";
//        }
//        if (EvalResult.testDefaultDouble(ksat_r())) {
//            errorMessage += " hzksat_rdept_r is empty";
//        }
    if (EvalResult.testDefaultDouble(cec7_r())) {
      if (EvalResult.testDefaultDouble(ecec_r())) {
        errorMessage += " cec7_r is empty";
        errorMessage += " ecec_r is empty";
      } else {
        cec7_r(ecec_r());
      }
    }

    if (EvalResult.testDefaultDouble(ph1to1h2o_r())) {
      if (EvalResult.testDefaultDouble(ph01mcacl2_r())) {
        errorMessage += " ph1to1h2o_r is empty";
        errorMessage += " ph01mcacl2_r is empty";
      } else {
        ph1to1h2o_r(ph01mcacl2_r());
      }

    }

    if (EvalResult.testDefaultDouble(om_r())) {
      errorMessage += " om_r is empty";
    }
    if (EvalResult.testDefaultDouble(caco3_r())) {
      errorMessage += " caco3_r is empty";
    }

    return errorMessage;
  }

  protected String testWEPP() {
    String errorMessage = "";
    try {
      setWEPPDefaults();
    } catch (WEPPException | SDMException ex) {
      errorMessage += ex.getMessage();
    }

    if (EvalResult.testDefaultDouble(sandtotal_r())
        || EvalResult.testDefaultDouble(claytotal_r())
        || EvalResult.testDefaultDouble(cec7_r())
        || EvalResult.testDefaultDouble(sandvf_r())
        || EvalResult.testDefaultDouble(om_r())) {

      if (EvalResult.testDefaultDouble(sandtotal_r())) {
        errorMessage += " sandtotal_r";
      }

      if (EvalResult.testDefaultDouble(claytotal_r())) {
        errorMessage += " claytotal_r";
      }
      if (EvalResult.testDefaultDouble(cec7_r())) {
        errorMessage += " cec7_r";
      }
      if (EvalResult.testDefaultDouble(sandvf_r())) {
        errorMessage += " sandvf_r";
      }
      if (EvalResult.testDefaultDouble(om_r())) {
        errorMessage += " om_r";
      }
    }
    return errorMessage;
  }

  public boolean isRestrictingPermiable() throws SDMException {
    return (evaluateForRestrictingHorizon() && permiable);
  }

  public boolean isRestrictingNonPermiable() throws SDMException {
    return (evaluateForRestrictingHorizon() && !permiable);
  }

  public void useGenericHz1Values(boolean value) {
    useGenericHz1Values = value;
    if (value) {
      useGenericHz2Values = false;
    }
  }

  public void useGenericHz2Values(boolean value) {
    useGenericHz2Values = value;
    if (value) {
      useGenericHz1Values = false;
    }
  }

  public final void silttotal_l(double value) {
    tableHorizon.silttotal_l(value);
  }

  public final double silttotal_l() {
    return tableHorizon.silttotal_l();
  }

  public final void claytotal_l(double value) {
    tableHorizon.claytotal_l(value);
  }

  public final double claytotal_l() {
    return tableHorizon.claytotal_l();
  }

  public final void silttotal_h(double value) {
    tableHorizon.silttotal_h(value);
  }

  public final double silttotal_h() {
    return tableHorizon.silttotal_h();
  }

  public final void claytotal_h(double value) {
    tableHorizon.claytotal_h(value);
  }

  public final double claytotal_h() {
    return tableHorizon.claytotal_h();
  }

  public final void sandtotal_l(double value) {
    tableHorizon.sandtotal_l(value);
  }

  public final double sandtotal_l() {
    return tableHorizon.sandtotal_l();
  }

  public final void sandtotal_h(double value) {
    tableHorizon.sandtotal_h(value);
  }

  public final double sandtotal_h() {
    return tableHorizon.sandtotal_h();
  }

  public final void om_l(double value) {
    tableHorizon.om_l(value);
  }

  public final double om_l() {
    return tableHorizon.om_l();
  }

  public final void om_h(double value) {
    tableHorizon.om_h(value);
  }

  public final double om_h() {
    return tableHorizon.om_h();
  }

  public final void cokey(String value) {
    tableHorizon.cokey(value);
  }

  public final String cokey() {
    return tableHorizon.cokey();
  }

  public boolean chkWEPSStopTexture() throws SDMException {
    String tmp = getRepresentativeTextureName().toLowerCase();
    if (null != tmp) {
      for (String textureChk1 : TEXTURE_CHK) {
        if (tmp.contains(textureChk1)) {
          int tdx = tmp.indexOf(textureChk1); // check if token
          if (tdx > 0) {
            if (!Character.isWhitespace(tmp.charAt(tdx - 1))) {
              continue;
            }
            if (tmp.charAt(tdx - 1) != '-') {
              continue;
            }
          }
          if (tdx + textureChk1.length() < tmp.length()) {
            if (!Character.isWhitespace(tmp.charAt(tdx + textureChk1.length()))) {
              continue;
            }
          }
          return true;
        }
      }
    }
    return false;
  }

  public void setWEPSDefaults(boolean estimateNulls) {
    double calculatedSiltTotal;
    hzdept_r(SoilUtils.testNumericSoilElement(999, 0, hzdept_r(), Double.NaN));
    hzdepb_r(SoilUtils.testNumericSoilElement(999, 0, hzdepb_r(), Double.NaN));
    claytotal_r(SoilUtils.testNumericSoilElement(100, 0, claytotal_r(), Double.NaN));
    sandtotal_r(SoilUtils.testNumericSoilElement(100, 0, sandtotal_r(), Double.NaN));
    calculatedSiltTotal = 100 - (claytotal_r() + sandtotal_r());
    silttotal_r(SoilUtils.testNumericSoilElement(calculatedSiltTotal * 1.001, calculatedSiltTotal * .999, silttotal_r(), calculatedSiltTotal));
    sandvf_r(SoilUtils.testNumericSoilElement(100, 0, sandvf_r(), Double.NaN));
    dbthirdbar_r(SoilUtils.testNumericSoilElement(2.60, .02, dbthirdbar_r(), Double.NaN));
    wthirdbar_r(SoilUtils.testNumericSoilElement(80, 0, wthirdbar_r(), Double.NaN));
    om_r(SoilUtils.testNumericSoilElement(100, 0, om_r(), Double.NaN));
    caco3_r(SoilUtils.testNumericSoilElement(110, 0, caco3_r(), Double.NaN));

    if (estimateNulls) {
      ksat_r(SoilUtils.testNumericSoilElement(705, 0, ksat_r(), Double.NaN));
      wfifteenbar_r(SoilUtils.testNumericSoilElement(70, 0, wfifteenbar_r(), Double.NaN));
    }
  }

  public void setWEPPDefaults() throws WEPPException, SDMException {
    if (EvalResult.testDefaultDouble(cec7_r())) {
      if (!EvalResult.testDefaultDouble(ecec_r())) {
        cec7_r(ecec_r());
      } else {
        if (surface) {
          throw new WEPPException("WEPP Defaults:  No data for CEC nor ECEC available for this horizon, " + chkey() + ".  Please contact your SDM data steward to have this corrected.");
        } else {
          cec7_r(0.0);
        }
      }
    }

    if (0.0 == cec7_r()) {
      if (surface) {
        throw new WEPPException("WEPP Defaults:  No valid CEC nor ECEC value greater than 0 available for this surface horizon, " + chkey() + ".  Please contact your SDM data steward to have this corrected.");
      }
    }

    if (EvalResult.testDefaultDouble(claytotal_r())) {
      claytotal_r(0.0);
    }

    if (isOrganic()) {
      if (this.hzdept_r() <= 40) {  //  Use Hz1 line from default organic .sol file.
        sandtotal_r(50);
        claytotal_r(10);
        om_r(77);
        cec7_r(40);

      } else {  //  Use Hz2 line from default organic .sol file.
        sandtotal_r(13);
        claytotal_r(5);
        om_r(81);
        cec7_r(45);
      }
      fragvol_r(0);
      fragments.clear();  //  Remove the fragment records, this will set the getWEPPRockFragments() value to be 0.
    }
  }

  public void computeErodibility() throws WEPPException {
    if (!EvalResult.testDefaultDouble(sandtotal_r())
        && !EvalResult.testDefaultDouble(claytotal_r())
        && !EvalResult.testDefaultDouble(cec7_r())
        && !EvalResult.testDefaultDouble(sandvf_r())
        && !EvalResult.testDefaultDouble(om_r())) {

      if ((sandtotal_r() > 0) && (claytotal_r() > 0) && (cec7_r() > 0)) {
        if (claytotal_r() <= 40) {
          if (cec7_r() > 1) {
            conductivity = -0.265 + 0.0086 * Math.pow(sandtotal_r(), 1.8) + 11.46 * Math.pow(cec7_r(), -0.75);
          } else {
            conductivity = 11.195 + 0.0086 * Math.pow(sandtotal_r(), 1.8); // from WEPP code, not in documentation
          }
        } else {
          conductivity = 0.0066 * Math.exp(244.0 / claytotal_r());
        }
      } else {
        conductivity = 0; // let model calc
      }
      if ((sandtotal_r() > 0) && (sandvf_r() > 0) && (om_r() > 0) && (claytotal_r() > 0)) {
        if (sandtotal_r() >= 30) {
          if (sandvf_r() > 40) {
            sandvf_r(40);
          }
          if (om_r() < 0.35) {
            om_r(0.35);
          }
          if (claytotal_r() > 40) {
            claytotal_r(40);
          }
          interrill = 2728000 + 192100 * sandvf_r();
          rill = 0.00197 + 0.00030 * sandvf_r() + 0.03863 * Math.exp(-1.84 * om_r());
          shear = 2.67 + 0.065 * claytotal_r() - 0.058 * sandvf_r();
        } else {
          if (claytotal_r() < 10) {
            claytotal_r(10);
          }
          interrill = 6054000 - 55130 * claytotal_r();
          rill = 0.0069 + 0.134 * Math.exp(-0.20 * claytotal_r());
          shear = 3.5;
        }
      } else {
        interrill = 0;
        rill = 0;
        shear = 0;
      }
    } else {
      String missingColumn = "";
      if (EvalResult.testDefaultDouble(sandtotal_r())) {
        missingColumn += " sandtotal_r";
      }

      if (EvalResult.testDefaultDouble(claytotal_r())) {
        missingColumn += " claytotal_r";
      }
      if (EvalResult.testDefaultDouble(cec7_r())) {
        missingColumn += " cec7_r";
      }
      if (EvalResult.testDefaultDouble(sandvf_r())) {
        missingColumn += " sandvf_r";
      }
      if (EvalResult.testDefaultDouble(om_r())) {
        missingColumn += " om_r";
      }
      throw new WEPPException("WEPP Computing Erodability:  Missing data in column(s): " + missingColumn + ", in the database for this horizon.  Cannot compute erodabilty.  Contact the SMD data steward to have this corrected for horizon: " + this.chkey());
    }
  }

  public final void cec7_l(double value) {
    tableHorizon.cec7_l(value);
  }

  public final double cec7_l() {
    return tableHorizon.cec7_l();
  }

  public final void cec7_r(double value) {
    tableHorizon.cec7_r(value);
  }

  public final double cec7_r() {
    return tableHorizon.cec7_r();
  }

  public final void cec7_h(double value) {
    tableHorizon.cec7_h(value);
  }

  public final double cec7_h() {
    return tableHorizon.cec7_h();
  }

  public final void wthirdbar_l(double value) {
    tableHorizon.wthirdbar_l(value);
  }

  public final double wthirdbar_l() {
    return tableHorizon.wthirdbar_l();
  }

  public final void wthirdbar_r(double value) {
    tableHorizon.wthirdbar_r(value);
  }

  public final double wthirdbar_r() {
    return tableHorizon.wthirdbar_r();
  }

  public final void wthirdbar_h(double value) {
    tableHorizon.wthirdbar_h(value);
  }

  public final double wthirdbar_h() {
    return tableHorizon.wthirdbar_h();
  }

  public final void wfifteenbar_l(double value) {
    tableHorizon.wfifteenbar_l(value);
  }

  public final double wfifteenbar_l() {
    return tableHorizon.wfifteenbar_l();
  }

  public final void wfifteenbar_r(double value) {
    tableHorizon.wfifteenbar_r(value);
  }

  public final double wfifteenbar_r() {
    return tableHorizon.wfifteenbar_r();
  }

  public final void wfifteenbar_h(double value) {
    tableHorizon.wfifteenbar_h(value);
  }

  public final double wfifteenbar_h() {
    return tableHorizon.wfifteenbar_h();
  }

  public final void dbthirdbar_l(double value) {
    tableHorizon.dbthirdbar_l(value);
  }

  public final double dbthirdbar_l() {
    return tableHorizon.dbthirdbar_l();
  }

  public final void dbthirdbar_r(double value) {
    tableHorizon.dbthirdbar_r(value);
  }

  public final double dbthirdbar_r() {
    return tableHorizon.dbthirdbar_r();
  }

  public final void dbthirdbar_h(double value) {
    tableHorizon.dbthirdbar_h(value);
  }

  public final double dbthirdbar_h() {
    return tableHorizon.dbthirdbar_h();
  }

  public final void silttotal_r(double value) {
    tableHorizon.silttotal_r(value);
  }

  public final double silttotal_r() {
    return tableHorizon.silttotal_r();
  }

  public final void claytotal_r(double value) {
    tableHorizon.claytotal_r(value);
  }

  public final double claytotal_r() {
    return tableHorizon.claytotal_r();
  }

  public final void ph1to1h2o_l(double value) {
    tableHorizon.ph1to1h2o_l(value);
  }

  public final double ph1to1h2o_l() {
    return tableHorizon.ph1to1h2o_l();
  }

  public final void ph1to1h2o_r(double value) {
    tableHorizon.ph1to1h2o_r(value);
  }

  public final double ph1to1h2o_r() {
    return tableHorizon.ph1to1h2o_r();
  }

  public final void ph1to1h2o_h(double value) {
    tableHorizon.ph1to1h2o_h(value);
  }

  public final double ph1to1h2o_h() {
    return tableHorizon.ph1to1h2o_h();
  }

  public String chkey() {
    return tableHorizon.chkey();
  }

  public void chkey(String chkey) {
    tableHorizon.chkey(chkey);
  }

  public void fragvol_r(double value) {
    tableHorizonCalculations.fragvol_r(value);
  }

  public double fragvol_r() {
    if (EvalResult.testDefaultDouble(tableHorizonCalculations.fragvol_r())) {
      double totalFragvol = 0.0;

      for (Fragments tFragment : fragments.values()) {
        totalFragvol += tFragment.fragvol_r();
      }

      fragvol_r((totalFragvol > 100) ? 100.0 : totalFragvol);
    }

    return tableHorizonCalculations.fragvol_r();
  }

  public void setOutputColumns(List<String> usedList) {
    tableHorizon.setOutputColumns(usedList);
    tableHorizonCalculations.setOutputColumns(usedList);
  }

  public void setOutputColumnOrdering(List<String> usedList) {
    tableHorizon.setOutputColumnOrdering(usedList);
    tableHorizonCalculations.setOutputColumnOrdering(usedList);
  }

  public String getRepresentativeTextureName() throws SDMException {
    if (null != textureGroups) {
      for (TextureGroup t : textureGroups.values()) {
        if (t.rvindicator()) {
          return t.texture();
        }
      }
    }
    throw new SDMException("No representative texture record was found for this horizon, " + chkey() + ".  Please notify your SDM data steward of the missing or incorrect data.");
  }

  public TableHorizon getTableHorizon() {
    return tableHorizon;
  }

  public double getConductivity() {
    return conductivity;
  }

  public double getInterrill() {
    return interrill;
  }

  public double getRill() {
    return rill;
  }

  public double getShear() {
    return shear;
  }

  public TableHorizonCalculations getTableHorizonCalculations() {
    return tableHorizonCalculations;
  }

  public double getWEPPRockFragments() {
    double rockFrag = 0.0;

    for (Fragments f : fragments.values()) {
      rockFrag += SoilUtils.averageMissingValue(f.fragvol_h(), f.fragvol_l(), f.fragvol_r());
    }

    rockFrag = rockFrag / 100.0;

    return rockFrag;
  }

  public void setUsedColumns(List<String> usedList) {
    tableHorizon.setUsedColumns(usedList);
    tableHorizonCalculations.setUsedColumns(usedList);
  }

  /**
   *
   * @param usedList
   */
  public void setTextureGroupOutputColumns(List<String> usedList) {
    for (String key : textureGroups.keySet()) {
      textureGroups.get(key).setOutputColumns(usedList);
    }
  }

  /**
   *
   * @param usedList
   */
  public void setTextureGroupUsedColumns(List<String> usedList) {
    for (String key : textureGroups.keySet()) {
      textureGroups.get(key).setUsedColumns(usedList);
    }
  }

  public boolean hasSelectedReason(String reason) {
    return selectedReason.contains(reason);
  }

  public double hzdepb_r() {
    return tableHorizon.hzdepb_r();
  }

  public void hzdepb_r(double value) {
    tableHorizon.hzdepb_r(value);
    hzdepb_r_in(value * 0.3937);
  }

  public double hzdept_r() {
    return tableHorizon.hzdept_r();
  }

  public void hzdept_r(double value) {
    tableHorizon.hzdept_r(value);
    hzdept_r_in(value * 0.3937);
  }

  public void hzdepb_r_in(double value) {
    tableHorizonCalculations.hzdepb_r_in(value);
  }

  public double hzdepb_r_in() {
    if (EvalResult.testDefaultDouble(tableHorizonCalculations.hzdepb_r_in())) {
      hzdepb_r_in(hzdepb_r() * 0.3937);
    }
    return tableHorizonCalculations.hzdepb_r_in();
  }

  public void hzdept_r_in(double value) {
    tableHorizonCalculations.hzdept_r_in(value);
  }

  public double hzdept_r_in() {
    if (EvalResult.testDefaultDouble(tableHorizonCalculations.hzdept_r_in())) {
      hzdept_r_in(hzdept_r() * 0.3937);
    }
    return tableHorizonCalculations.hzdept_r_in();
  }

  public double hzthk_r() {
    if (EvalResult.testDefaultDouble(tableHorizon.hzthk_r())) {
      if (!EvalResult.testDefaultDouble(tableHorizon.hzdept_r()) && !EvalResult.testDefaultDouble(tableHorizon.hzdepb_r())) {
        hzthk_r(tableHorizon.hzdepb_r() - tableHorizon.hzdept_r());
      }
    }

    return tableHorizon.hzthk_r();
  }

  public void hzthk_r(double value) {
    tableHorizon.hzthk_r(value);
  }

  public boolean isSurface() {
    return surface;
  }

  protected Boolean evaluateMineral() throws SDMException {
    boolean ret_val = false;

    if (!EvalResult.testDefaultDouble(om_r())) {
      if (om_r() < 15) {
        ret_val = true;
      } else {
        ret_val = false;
      }
    } else {
      ret_val = !isOrganic();
    }

    return ret_val;
  }

  public boolean isMineral() throws SDMException {
    if (null == mineral) {
      mineral = evaluateMineral();
    }

    return mineral;
  }

  public void setSurface(boolean value) {
    surface = value;
  }

  public boolean isIgnored() throws SDMException {
    String texStr = getRepresentativeTextureName();
    for (int i = 0; i < nonCementedRestrictingLayers.size(); i++) {
      if (texStr.equalsIgnoreCase(nonCementedRestrictingLayers.get(i))) {
        return true;
      }
    }

    for (int i = 0; i < ignoredTextureIfNotSurface.size(); i++) {
      if (texStr.equalsIgnoreCase(ignoredTextureIfNotSurface.get(i))) {
        if (hzdept_r() > 0) {
          return true;
        }
      }
    }
    return false;
  }

  public boolean isPermiable() {
    return permiable;
  }

  public boolean isOrganic() throws SDMException {
    if ((om_r() >= 15) || (this.hzname().startsWith("O"))) {//  TODO:  Review the "Seybold document" to find other tests to run for this.
      return true;
    }

    String texStr = getRepresentativeTextureName().toLowerCase();
    if ((null != texStr) && !texStr.isEmpty()) {
      for (int i = 0; i < organicTextures.size(); i++) {
        if (organicTextures.get(i).equalsIgnoreCase(texStr)) {
          return true;
        }
      }
    }

    return false;
  }

  public boolean evaluateForRestrictingHorizon() throws SDMException {
    restricting = false;
    permiable = false;

    String texStr = getRepresentativeTextureName().toLowerCase();

    if (texStr.startsWith("Cr")) {
      restricting = true;
      permiable = true;
    }

    if (texStr.startsWith("R")) {
      restricting = true;
      permiable = false;
    }

    for (int i = 0; i < nonCementedRestrictingLayers.size(); i++) {
      if (texStr.equalsIgnoreCase(nonCementedRestrictingLayers.get(i))) {
        restricting = true;
        permiable = true;
        break;
      }
    }

    for (int i = 0; i < restrictingTextures.size(); i++) {
      if (texStr.equalsIgnoreCase(restrictingTextures.get(i))) {
        restricting = true;
        permiable = false;
        break;
      }
    }

    for (int i = 0; i < restrictingTexturesPartialMatches.size(); i++) {
      String testStr = restrictingTexturesPartialMatches.get(i);
      if (texStr.contains(testStr.toUpperCase()) || texStr.contains(testStr.toLowerCase())) {
        restricting = true;
        permiable = false;
        break;
      }
    }

    return restricting;
  }

  public boolean isRestricting() {
    return restricting;
  }

  public void isRestricting(boolean value) {
    restricting = value;
  }

  public void setRestricting(boolean value) {
    restricting = value;
  }

  public double kffact() {
    return tableHorizon.kffact();
  }

  public void kffact(double value) {
    tableHorizon.kffact(value);
  }

  public double kwfact() {
    return tableHorizon.kwfact();
  }

  public void kwfact(double value) {
    tableHorizon.kwfact(value);
  }

  public void lsfact(double value) {
    tableHorizonCalculations.lsfact(value);
  }

  public double lsfact() {
    return tableHorizonCalculations.lsfact();
  }

  public void merge(Horizon mergeThisUnit) throws ServiceException {
    this.tableHorizon.merge(mergeThisUnit.getTableHorizon());
    this.tableHorizonCalculations.merge(mergeThisUnit.getTableHorizonCalculations());

    for (String key : mergeThisUnit.textureGroups.keySet()) {
      if (textureGroups.containsKey(key)) {
        textureGroups.get(key).merge(mergeThisUnit.textureGroups().get(key));
      }
    }
  }

  public double om_r() {
    return tableHorizon.om_r();
  }

  public void om_r(double value) {
    tableHorizon.om_r(value);
  }

  public double sandtotal_r() {
    return tableHorizon.sandtotal_r();
  }

  public void sandtotal_r(double value) {
    tableHorizon.sandtotal_r(value);
  }

  public boolean selected() {
    return selected;
  }

  public void selected(boolean value) {
    selected = value;
  }

  public void selectedReason(String reason) {
    selectedReason.add(reason);
  }

  /*  This is a component level value
    public double slopelenusle_r() {
        return tableHorizon.slopelenusle_r();
    }

    public void slopelenusle_r(double value) {
        tableHorizon.slopelenusle_r(value);
    }
   */
  /**
   *
   * @return @throws JSONException
   */
  public JSONArray toJSON() throws JSONException {
    JSONArray ret_val = new JSONArray();
    JSONArray texturesArray = new JSONArray();
    JSONArray fragmentArray = new JSONArray();

    tableHorizon.toJSON(ret_val);
    tableHorizonCalculations.toJSON(ret_val);

    for (Fragments tFragments : this.fragments.values()) {
      fragmentArray.put(tFragments.toJSON());
    }

    for (TextureGroup textureGroup : this.textureGroups.values()) {
      if (textureGroup.rvindicator()) {
        texturesArray.put(textureGroup.toJSON());
      }
    }

    if (((fragmentArray.length() > 0) && (fragmentArray.getJSONArray(0).length() > 0)) && (ret_val.length() > 0)) {
      ret_val.put(JSONUtils.data(FRAGMENTS_LIST, fragmentArray));
    }

    if (((texturesArray.length() > 0) && (texturesArray.getJSONArray(0).length() > 0)) && (ret_val.length() > 0)) {
      ret_val.put(JSONUtils.data(TEXTURE_GROUP_LIST, texturesArray));
    }

    return ret_val;
  }

  /**
   *
   * @return @throws JSONException
   */
  public JSONObject toBasicJSON() throws JSONException {
    JSONObject ret_val = new JSONObject();
    JSONArray texturesArray = new JSONArray();
    JSONArray fragmentArray = new JSONArray();

    tableHorizon.toBasicJSON(ret_val);
    tableHorizonCalculations.toBasicJSON(ret_val);

    for (Fragments tFragments : this.fragments.values()) {
      fragmentArray.put(tFragments.toBasicJSON());
    }

    for (TextureGroup textureGroup : this.textureGroups.values()) {
      if (textureGroup.rvindicator()) {
        texturesArray.put(textureGroup.toBasicJSON());
      }
    }

    if ((fragmentArray.length() > 0) && (ret_val.length() > 0)) {
      ret_val.put(FRAGMENTS_LIST, fragmentArray);
    }

    if ((texturesArray.length() > 0) && (ret_val.length() > 0)) {
      ret_val.put(TEXTURE_GROUP_LIST, texturesArray);
    }

    return ret_val;
  }

  public boolean isEqual(Horizon thorizon) throws ServiceException {
    boolean ret_val = (textureGroups.size() == thorizon.textureGroups().size()
        && fragments.size() == thorizon.fragments.size());

    if (ret_val && (tableHorizon.isEqual(thorizon.tableHorizon) && tableHorizonCalculations.isEqual(thorizon.tableHorizonCalculations))) {
      for (Fragments tfragment : fragments.values()) {
        if (ret_val && thorizon.fragments.containsKey(tfragment.chfragskey())) {
          ret_val &= tfragment.isEqual(thorizon.fragments.get(tfragment.chfragskey()));
        }
      }

      for (TextureGroup tgroup : textureGroups.values()) {
        if (ret_val && thorizon.textureGroups.containsKey(tgroup.chtgkey())) {
          ret_val &= tgroup.isEqual(thorizon.textureGroups.get(tgroup.chtgkey()));
        }
      }
    }
    return ret_val;
  }

  /**
   *
   */
  public static class HFrags {

    public String chfragskey;
    public double fragvol_r;
    public double fragvol_l;
    public double fragvol_h;
    public String fragkind;
    public double fragsize_l;
    public double fragsize_r;
    public double fragsize_h;
    public String fragshp;
    public String fraground;
    public String fraghard;

    HFrags() {

    }
  }
}