MapUnit.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 static csip.ModelDataService.KEY_NAME;
import csip.api.server.ServiceException;
import csip.utils.JSONUtils;
import gisobjects.GISObject;
import gisobjects.GISObjectException;
import gisobjects.GISObjectFactory;
import java.io.IOException;
import static java.lang.Double.NaN;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import soils.db.tables.Table;
import soils.db.tables.TableColumn;
import soils.db.tables.TableExcluded;
import soils.db.tables.TableLegend;
import soils.db.tables.TableMapUnit;
import soils.db.tables.TableMapUnitCalculations;
import soils.db.tables.TableMuaggatt;
import soils.db.tables.TableMucropyld;
import soils.db.tables.TableSaCatalog;
import soils.db.tables.TableSaSpatialVer;
import soils.db.tables.TableSaTabularVer;
import soils.exceptions.SDMException;
import soils.exceptions.WEPSException;
import soils.utils.EvalResult;
import soils.utils.GenericIFCData;
import soils.utils.SoilUtils;

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

  protected static ArrayList<String> mapUnitRequiredInputs = new ArrayList<>();
  protected static ArrayList<String> mapUnitUsedColumns = new ArrayList<>();

  //public static ArrayList<String> unusedColumns = new ArrayList<>();
  public static ArrayList<String> getDefaultUsedColumns() {
    return ((null != mapUnitUsedColumns) ? (ArrayList<String>) mapUnitUsedColumns.clone() : null);
  }

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

  public synchronized static void setRequiredInputs(List<String> mapUnitInputs) {
    mapUnitRequiredInputs.clear();
    if (null != mapUnitInputs) {
      mapUnitRequiredInputs.addAll(mapUnitInputs);
      mapUnitUsedColumns.addAll(mapUnitInputs);
    }
  }

  private double aoaArea = NaN;
  private LinkedHashMap<String, Component> components;
  private LinkedHashMap<String, Mucropyld> cropYlds = new LinkedHashMap<>();

  private boolean isPalouse;
  private TableExcluded tableExcluded = new TableExcluded();
  private TableMapUnitCalculations tableMapUnitCalculations = new TableMapUnitCalculations();
  private TableMapUnit table_columns = new TableMapUnit();
  private TableMuaggatt table_muaggatt_columns = new TableMuaggatt();
  private TableLegend table_legend_columns = new TableLegend();
  private TableSaCatalog table_sacatalog_columns = new TableSaCatalog();
  private TableSaSpatialVer table_saspatialver_columns = new TableSaSpatialVer();
  private TableSaTabularVer table_satabularver_columns = new TableSaTabularVer();

  private HashMap<String, Table> linkedTables = new HashMap<>();

  //  Could be more than one intersection shape for this map unit.
  private ArrayList<GISObject> intersectionShape = new ArrayList<>();
  private boolean outputShapes = false;

  private void addLinkedTables() {
    linkedTables.put("excluded", tableExcluded);
    linkedTables.put(tableMapUnitCalculations.getTableName(), tableMapUnitCalculations);
    linkedTables.put(table_columns.getTableName(), table_columns);
    linkedTables.put(table_muaggatt_columns.getTableName(), table_muaggatt_columns);
    linkedTables.put(table_legend_columns.getTableName(), table_legend_columns);
    linkedTables.put(table_sacatalog_columns.getTableName(), table_sacatalog_columns);
    linkedTables.put(table_saspatialver_columns.getTableName(), table_saspatialver_columns);
    linkedTables.put(table_satabularver_columns.getTableName(), table_satabularver_columns);
  }

  private void setTablesRequiredColumns(List<String> requiredList) {
    for (Table table : linkedTables.values()) {
      table.setRequiredColumns(requiredList);
    }
  }

  private void setTablesUsedColumns(List<String> used) {
    for (Table table : linkedTables.values()) {
      table.setUsedColumns(used);
    }
  }

  private void setTablesUsedColumns() {
    for (Table table : linkedTables.values()) {
      table.setUsedColumns(mapUnitUsedColumns);
    }
  }

  private void setTablesUnUsedColumns(List<String> unUsed) {
    for (Table table : linkedTables.values()) {
      table.setUnusedColumns(unUsed);
    }
  }

  private void readTablesValuesFromSQL(ResultSet results) throws ServiceException {
    for (Table table : linkedTables.values()) {
      table.readValuesFromSQL(results);
    }
  }

  private void readTablesValuesFromJSON(JSONArray inputJSON) throws ServiceException, JSONException {
    for (Table table : linkedTables.values()) {
      table.readValuesFromJSON(inputJSON);
    }
  }

  private void readTablesValuesFromJSON(Map<String, JSONObject> inputMap) throws ServiceException, JSONException {
    for (Table table : linkedTables.values()) {
      table.readValuesFromJSON(inputMap);
    }
  }

  public MapUnit() {
    components = new LinkedHashMap<>();

    addLinkedTables();
    setTablesUsedColumns();
  }

  public MapUnit(String mukey, String muname, String musym, String areasymbol, double area) throws ServiceException {
    components = new LinkedHashMap<>();
    addLinkedTables();
    setTablesUsedColumns();

    table_columns.mukey(mukey);
    table_columns.muname(muname);
    table_columns.musym(musym);
    setAreaSymbol(areasymbol);

    area(area);
    isPalouse = SoilUtils.isPalouse(areasymbol());
  }

  public MapUnit(Component component) throws ServiceException {
    components = new LinkedHashMap<>();

    addLinkedTables();
    setTablesUsedColumns();

    components.put(component.cokey(), component);
  }

  public MapUnit(String mukey, ResultSet results, GISObject intersectionShape) throws ServiceException {

    addLinkedTables();
    setTablesUsedColumns();

    readTablesValuesFromSQL(results);

    if (null != intersectionShape) {
      this.intersectionShape.add(intersectionShape);
    }

    isPalouse = SoilUtils.isPalouse(areasymbol());
    components = new LinkedHashMap<>();
  }

  public MapUnit(String mukey, String muname, String musym, String areasymbol, GISObject intersectionShape, double area) throws ServiceException {
    addLinkedTables();
    setTablesUnUsedColumns(null);

    table_columns.mukey(mukey);
    table_columns.muname(muname);
    table_columns.musym(musym);
    setAreaSymbol(areasymbol);

    if (null != intersectionShape) {
      this.intersectionShape.add(intersectionShape);
    }

    area(area);
    isPalouse = SoilUtils.isPalouse(areasymbol());

    components = new LinkedHashMap<>();
  }

  public MapUnit(JSONArray mapUnitJSON) throws JSONException, ServiceException {
    Map<String, JSONObject> mapUnitArray = JSONUtils.preprocess(mapUnitJSON);
    addLinkedTables();
    setTablesUnUsedColumns(null);

    table_columns.setRequiredColumns(mapUnitRequiredInputs);
    tableMapUnitCalculations.setRequiredColumns(mapUnitRequiredInputs);

    if ((null != mapUnitJSON) && (mapUnitJSON.length() > 0)) {
      JSONArray componentsArray;
      readTablesValuesFromJSON(mapUnitArray);

      intersectionShape = null;

      isPalouse = SoilUtils.isPalouse(areasymbol());

      components = new LinkedHashMap<>();

      setComponents(JSONUtils.getJSONArrayParam(mapUnitArray, SoilsData.MAPUNIT_COMPONENT_LIST_NAME));

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

  private void setComponents(JSONArray componentJSON) throws ServiceException, JSONException {
    if ((null != componentJSON) && (componentJSON.length() > 0)) {
      for (int i = 0; i < componentJSON.length(); i++) {
        Component component = new Component(componentJSON.getJSONArray(i));

        if (!components.containsKey(component.cokey())) {
          components.put(component.cokey(), component);
        } else {
          throw new ServiceException("Duplicate component passed to SOILS_DATA.MapUnit.setComponents() from input JSON.  Component keys listed by map unit should be unique.");
        }
      }
    } else {
      if ((null != mapUnitRequiredInputs) && (mapUnitRequiredInputs.contains(AoA.MAPUNIT_COMPONENT_LIST_NAME))) {
        throw new ServiceException("Map Unit, " + table_columns.mukey() + ", is missing the " + AoA.MAPUNIT_COMPONENT_LIST_NAME + " parameter.");
      }
    }
  }

  public boolean hasTableColumn(String columnName) {
    return table_columns.getColumnList().contains(columnName);
  }

  public Object getTableColumnValue(String columnName) {
    if (hasTableColumn(columnName)) {
      return table_columns.getColumns().get(columnName).getValue();
    }
    return null;
  }

  public TableColumn getTableColumn(String columnName) {
    if (hasTableColumn(columnName)) {
      return table_columns.getColumns().get(columnName);
    }
    return null;
  }

  public TableMapUnit getTableColumns() {
    return table_columns;
  }

  public boolean isEqual(MapUnit tMap) throws ServiceException {
    boolean ret_val = (tMap.components().size() == components.size());;

    for (Table lTable : linkedTables.values()) {
      if (ret_val && (null != tMap.getLinkedTable(lTable.getTableName()))) {
        ret_val &= lTable.isEqual(tMap.getLinkedTable(lTable.getTableName()));
      }
    }

    for (Component component : components.values()) {
      if (ret_val && tMap.components().containsKey(component.cokey())) {
        ret_val &= component.isEqual(tMap.components().get(component.cokey()));
      } else {
        return false;
      }
    }

    return ret_val;
  }

  public ArrayList<GISObject> getIntersectionShapes() {
    return intersectionShape;
  }

  public void clearIntersectionPolygonList() {
    intersectionShape.clear();
  }

  public ArrayList<GISObject> getIntersectionPolygons() {
    return intersectionShape;
  }

  public void setIntersectionPolygons(List<GISObject> shapes) {
    if (!intersectionShape.isEmpty()) {
      intersectionShape.clear();
    }

    for (GISObject shape : shapes) {
      intersectionShape.add(shape);
    }
  }

  public boolean outputShapes() {
    return outputShapes;
  }

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

  public double aoaArea() {
    return aoaArea;
  }

  /**
   *
   * @param aoaArea
   */
  public void aoaArea(double aoaArea) {
    this.aoaArea = aoaArea;
    if (!EvalResult.testDefaultDouble(area()) && (aoaArea > 0)) {
      tableMapUnitCalculations.area_pct((area() / aoaArea) * 100.0);
    }
  }

  /**
   *
   */
  public double area() {
    return tableMapUnitCalculations.area();
  }

  /**
   *
   * @param area
   */
  public final void area(double area) {
    tableMapUnitCalculations.area(area);
  }

  /**
   *
   * @param area
   */
  public void areaAdd(double area) {
    double total = tableMapUnitCalculations.area();
    total += area;
    tableMapUnitCalculations.area(total);

    if (!EvalResult.testDefaultDouble(total) && (aoaArea > 0)) {
      tableMapUnitCalculations.area_pct((total / aoaArea) * 100.0);
    }
  }

  public double area_pct() {
    return tableMapUnitCalculations.area_pct();
  }

  public void area_pct(double value) {
    tableMapUnitCalculations.area_pct(value);
  }

  public String areaname() {
    return table_columns.areaname();
  }

  /**
   *
   */
  public final String areasymbol() {
    return table_columns.areasymbol();
  }

  public int brockdepmin() {
    return table_muaggatt_columns.brockdepmin();
  }

  public void brockdepmin(int value) {
    table_muaggatt_columns.brockdepmin(value);
  }

  public void computeTillageLayerClayTotals() {
    for (Component tComponent : components.values()) {
      tComponent.computeTillageLayerClayTotal();
    }
  }

  /**
   *
   * @return
   */
  public LinkedHashMap<String, Component> components() {
    return components;
  }

  public void putComponent(Component component) {
    if (null != components) {
      components.put(component.cokey(), component);
    }
  }

  public LinkedHashMap<String, Mucropyld> cropYlds() {
    return cropYlds;
  }

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

  public void addShape(GISObject tShape) throws GISObjectException {
    GISObject newShape = GISObjectFactory.createGISObject(tShape.getGeometry(), tShape.getEngine());

    intersectionShape.add(tShape);
  }

  public MapUnit deepCopy() throws ServiceException {
    MapUnit mapUnit = new MapUnit();

    deepCopy(mapUnit);

    return mapUnit;
  }

  public void deepCopy(MapUnit newMapUnit) throws ServiceException {
    if (null != newMapUnit) {
      table_columns.deepCopy(newMapUnit.table_columns);
      tableMapUnitCalculations.deepCopy(newMapUnit.tableMapUnitCalculations);
      tableExcluded.deepCopy(newMapUnit.tableExcluded);
      table_legend_columns.deepCopy(newMapUnit.table_legend_columns);
      table_muaggatt_columns.deepCopy(newMapUnit.table_muaggatt_columns);
      table_sacatalog_columns.deepCopy(newMapUnit.table_sacatalog_columns);
      table_saspatialver_columns.deepCopy(newMapUnit.table_saspatialver_columns);
      table_satabularver_columns.deepCopy(newMapUnit.table_satabularver_columns);

      newMapUnit.aoaArea(aoaArea);
      newMapUnit.isPalouse(isPalouse);
      newMapUnit.outputShapes(outputShapes);

      for (GISObject tShape : intersectionShape) {
        try {
          newMapUnit.addShape(tShape);
        } catch (GISObjectException ex) {
          throw new ServiceException("Cannot copy this mapUnit, error copying intersection shapes: " + ex.getMessage(), ex);
        }
      }

      LinkedHashMap<String, Component> tComponents = new LinkedHashMap<>();
      for (Component tComponent : components.values()) {
        Component newComponent = tComponent.deepCopy();
        if (null != newComponent) {
          tComponents.put(newComponent.cokey(), newComponent);
        }
      }

      if (tComponents.size() > 0) {
        newMapUnit.setComponents(tComponents);
      }
    }
  }

  public void setComponents(LinkedHashMap<String, Component> tComponents) {
    if ((null != tComponents) && (tComponents.size() > 0)) {
      this.components.clear();

      for (Component tComponent : tComponents.values()) {
        if (!components.containsKey(tComponent.cokey())) {
          components.put(tComponent.cokey(), tComponent);
        }
      }
    }
  }

  public void exclude() {
    tableExcluded.exclude();
  }

  public void setAreaSymbol(String symbol) {
    table_columns.areasymbol(symbol);
    table_legend_columns.areasymbol(symbol);
    table_sacatalog_columns.areasymbol(symbol);
    table_saspatialver_columns.areasymbol(symbol);
    table_satabularver_columns.areasymbol(symbol);
  }

  /**
   *
   * @param usedList
   */
  public void setComponentOutputColumns(List<String> usedList) {
    for (String key : components.keySet()) {
      Component tComponent = components.get(key);
      tComponent.setOutputColumns(usedList);
    }
  }

  /**
   *
   * @param usedList
   */
  public void setComponentOutputColumnOrdering(List<String> usedList) {
    for (String key : components.keySet()) {
      Component tComponent = components.get(key);
      tComponent.setOutputColumnOrdering(usedList);
    }
  }

  /**
   *
   * @param usedList
   */
  public void setComponentUsedColumns(List<String> usedList) {
    for (String key : components.keySet()) {
      Component tComponent = components.get(key);
      tComponent.setUsedColumns(usedList);
    }
  }

  public void setExcludedReason(String reason) {
    tableExcluded.setExcludedReason(reason);
  }

  public String getExcludedReason() {
    return tableExcluded.getExcludedReason();
  }

  /**
   *
   * @param usedList
   */
  public void setFragmentOutputColumnOrdering(List<String> usedList) {
    for (String key : components.keySet()) {
      Component tComponent = components.get(key);
      tComponent.setFragmentOutputColumnOrdering(usedList);
    }
  }

  /**
   *
   * @param usedList
   */
  public void setHorizonOutputColumns(List<String> usedList) {
    for (String key : components.keySet()) {
      Component tComponent = components.get(key);
      tComponent.setHorizonOutputColumns(usedList);
    }
  }

  /**
   *
   * @param usedList
   */
  public void setTextureGroupOutputColumns(List<String> usedList) {
    for (String key : components.keySet()) {
      Component tComponent = components.get(key);
      tComponent.setTextureGroupOutputColumns(usedList);
    }
  }

  /**
   *
   * @param usedList
   */
  public void setTextureOutputColumns(List<String> usedList) {
    for (String key : components.keySet()) {
      Component tComponent = components.get(key);
      tComponent.setTextureOutputColumns(usedList);
    }
  }

  /**
   *
   * @param usedList
   */
  public void setHorizonOutputColumnOrdering(List<String> usedList) {
    for (String key : components.keySet()) {
      Component tComponent = components.get(key);
      tComponent.setHorizonOutputColumnOrdering(usedList);
    }
  }

  /**
   *
   * @param usedList
   */
  public void setTextureGroupOutputColumnOrdering(List<String> usedList) {
    for (String key : components.keySet()) {
      Component tComponent = components.get(key);
      tComponent.setTextureGroupOutputColumnOrdering(usedList);
    }
  }

  /**
   *
   * @param usedList
   */
  public void setTextureOutputColumnOrdering(List<String> usedList) {
    for (String key : components.keySet()) {
      Component tComponent = components.get(key);
      tComponent.setTextureOutputColumnOrdering(usedList);
    }
  }

  /**
   *
   * @param usedList
   */
  public void setHorizonUsedColumns(List<String> usedList) {
    for (String key : components.keySet()) {
      Component tComponent = components.get(key);
      tComponent.setHorizonUsedColumns(usedList);
    }
  }

  /**
   *
   * @param usedList
   */
  public void setOutputColumns(List<String> usedList) throws ServiceException {
    for (Table table : linkedTables.values()) {
      setLinkedTableOutputColumns(table.getTableName(), usedList);
    }
  }

  /**
   *
   * @param usedList
   */
  public void setOutputColumnOrdering(List<String> usedList) throws ServiceException {
    table_columns.setOutputColumnOrdering(usedList);
    tableMapUnitCalculations.setOutputColumnOrdering(usedList);
//        for (Table table : linkedTables.values()) {
//            setLinkedTableOutputColumnOrdering(table.getTableName(), usedList);
//        }
  }

  public TableMapUnit getTableMapUnit() {
    return table_columns;
  }

  public TableMapUnitCalculations getTableMapUnitCalculations() {
    return tableMapUnitCalculations;
  }

  public Table getLinkedTable(String name) {
    Table table = linkedTables.get(name);

    return table;
  }

  public TableMuaggatt getTableMuaggatt() {
    return table_muaggatt_columns;
  }

  public TableLegend getTableLegend() {
    return table_legend_columns;
  }

  public TableSaCatalog getTableSaCatalog() {
    return table_sacatalog_columns;
  }

  /**
   *
   * @param usedList
   */
  public void setUsedColumns(List<String> usedList) {
    setTablesUsedColumns(usedList);
  }

  public void setLinkedTableUsedColumns(String tableName, List<String> usedColumns) throws ServiceException {
    Table table = linkedTables.get(tableName);

    if (null != table) {
      table.setUsedColumns(usedColumns);
    } else {
      throw new ServiceException("That table does not exist, " + tableName + " .");
    }
  }

  public void setLinkedTableUnUsedColumns(String tableName, List<String> unUsedColumns) throws ServiceException {
    Table table = linkedTables.get(tableName);

    if (null != table) {
      table.setUnusedColumns(unUsedColumns);
    } else {
      throw new ServiceException("That table does not exist, " + tableName + " .");
    }
  }

  public void setLinkedTableRequiredColumns(String tableName, List<String> requiredColumns) throws ServiceException {
    Table table = linkedTables.get(tableName);

    if (null != table) {
      table.setUsedColumns(requiredColumns);
    } else {
      throw new ServiceException("That table does not exist, " + tableName + " .");
    }
  }

  public void setLinkedTableOutputColumnOrdering(String tableName, List<String> outputColumns) throws ServiceException {
    Table table = linkedTables.get(tableName);

    if (null != table) {
      table.setOutputColumnOrdering(outputColumns);
    } else {
      throw new ServiceException("That table does not exist, " + tableName + " .");
    }
  }

  public void setLinkedTableOutputColumns(String tableName, List<String> usedColumns) throws ServiceException {
    Table table = linkedTables.get(tableName);

    if (null != table) {
      table.setOutputColumns(usedColumns);
    } else {
      throw new ServiceException("That table does not exist, " + tableName + " .");
    }
  }

  public boolean hasExcludedComponents() {
    boolean ret_val = false;
    for (Component tComponent : components.values()) {
      if (tComponent.isExcluded()) {
        ret_val = true;
        break;
      }
    }
    return ret_val;
  }

  public boolean isExcluded() {
    return tableExcluded.isExcluded();
  }

  /**
   *
   * @return
   */
  public boolean isPalouse() {
    return isPalouse;
  }

  /**
   *
   * @param dataArray
   * @return
   * @throws JSONException
   */
  public JSONArray mapUnitJSON(JSONArray dataArray) throws JSONException {
    JSONArray ret_val = dataArray;
    table_sacatalog_columns.setOutputColumnOrdering(new ArrayList<>(Arrays.asList(TableSaCatalog.SA_VERSION, TableSaCatalog.SA_VER_EST)));
    table_saspatialver_columns.setOutputColumnOrdering(new ArrayList<>(Arrays.asList(TableSaSpatialVer.SPATIAL_VERSION, TableSaSpatialVer.SPATIAL_VERSION_EST)));
    table_satabularver_columns.setOutputColumnOrdering(new ArrayList<>(Arrays.asList(TableSaTabularVer.TABULAR_VERSION, TableSaTabularVer.TABULAR_VERSION_EST)));

    tableExcluded.toJSON(ret_val);
    if (outputShapes) {
      JSONArray shapeArray = new JSONArray();
      for (GISObject shape : intersectionShape) {
        try {
          shapeArray.put(shape.toJSON());
        } catch (IOException ex) {
          throw new JSONException("Cannot create JSON object for shape: " + ex.getMessage());
        }
      }
      JSONObject polys = new JSONObject();
      ret_val.put(JSONUtils.data("intersected_polygons", shapeArray));
    }
    table_columns.toJSON(ret_val);
    tableMapUnitCalculations.toJSON(ret_val);
    table_muaggatt_columns.toJSON(ret_val);
    table_legend_columns.toJSON(ret_val);
    table_sacatalog_columns.toJSON(ret_val);
    table_saspatialver_columns.toJSON(ret_val);
    table_satabularver_columns.toJSON(ret_val);

    return ret_val;
  }

  /**
   *
   * @param dataArray
   * @return
   * @throws JSONException
   */
  public JSONObject mapUnitBasicJSON(JSONObject dataArray) throws JSONException {
    JSONObject ret_val = dataArray;
    table_sacatalog_columns.setOutputColumnOrdering(new ArrayList<>(Arrays.asList(TableSaCatalog.SA_VERSION, TableSaCatalog.SA_VER_EST)));
    table_saspatialver_columns.setOutputColumnOrdering(new ArrayList<>(Arrays.asList(TableSaSpatialVer.SPATIAL_VERSION, TableSaSpatialVer.SPATIAL_VERSION_EST)));
    table_satabularver_columns.setOutputColumnOrdering(new ArrayList<>(Arrays.asList(TableSaTabularVer.TABULAR_VERSION, TableSaTabularVer.TABULAR_VERSION_EST)));

    tableExcluded.toBasicJSON(ret_val);
    if (outputShapes) {
      JSONArray shapeArray = new JSONArray();
      int i = 0;
      for (GISObject shape : intersectionShape) {
        try {
          JSONObject shapeObject = shape.toJSON();
          try {
            shapeObject.put(KEY_NAME, mukey() + ": Intersection_" + i);
          } catch (ServiceException ex) {
            throw new JSONException("Error forming polygon intersection name from mukey value: " + ex.getMessage());
          }
          shapeArray.put(shapeObject);
          i++;
        } catch (IOException ex) {
          throw new JSONException("Cannot create JSON object for shape: " + ex.getMessage());
        }
      }
      JSONObject polys = new JSONObject();
      ret_val.put("intersected_polygons", shapeArray);
    }

    table_columns.toBasicJSON(ret_val);
    tableMapUnitCalculations.toBasicJSON(ret_val);
    table_muaggatt_columns.toBasicJSON(ret_val);
    table_legend_columns.toBasicJSON(ret_val);
    table_sacatalog_columns.toBasicJSON(ret_val);
    table_saspatialver_columns.toBasicJSON(ret_val);
    table_satabularver_columns.toBasicJSON(ret_val);

    return ret_val;
  }

  public void merge(MapUnit mergeThisUnit) throws ServiceException {
    table_columns.merge(mergeThisUnit.getTableMapUnit());
    tableMapUnitCalculations.merge(mergeThisUnit.tableMapUnitCalculations);
    table_muaggatt_columns.merge(mergeThisUnit.getTableMuaggatt());
    table_legend_columns.merge(mergeThisUnit.getTableLegend());
    table_sacatalog_columns.merge(mergeThisUnit.getLinkedTable(table_sacatalog_columns.getTableName()));
    table_saspatialver_columns.merge(mergeThisUnit.getLinkedTable(table_saspatialver_columns.getTableName()));
    table_satabularver_columns.merge(mergeThisUnit.getLinkedTable(table_satabularver_columns.getTableName()));

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

  public double muacres() {
    return table_columns.muacres();
  }

  public void muacres(double value) {
    table_columns.muacres(value);
  }

  /**
   *
   * @return @throws ServiceException
   */
  public String mukey() throws ServiceException {
    return table_columns.mukey();
  }

  public void mukey(String value) {
    table_columns.mukey(value);
  }

  /**
   *
   * @return @throws ServiceException
   */
  public String muname() throws ServiceException {
    return table_columns.muname();
  }

  public String musym() {
    return table_columns.musym();
  }

  public synchronized void readCropYldFromSQL(ResultSet results) throws ServiceException {
    String cropYldKey;
    try {
      cropYldKey = results.getString(TableMucropyld.MUCRPYLDKEY);
    } catch (SQLException ex) {
      throw new ServiceException("Cannot find the mucropyldkey value in the returned database resultset in readCropYldFromSQL(): " + ex.getMessage(), ex);
    }

    if (null != cropYldKey) {
      Mucropyld tCropYld = cropYlds.get(cropYldKey);
      if (null == tCropYld) {
        tCropYld = new Mucropyld(results);
        cropYlds.put(cropYldKey, tCropYld);
      } else {
        throw new ServiceException("Duplicate cocropyldkey found in results returned from database querey in readCropYldFromSQL().  Results should have unidque mucropyldkey.");
      }

    }
  }

  public synchronized void readFromSQL(ResultSet results) throws ServiceException {
    table_columns.readValuesFromSQL(results);
    tableMapUnitCalculations.readValuesFromSQL(results);
  }

  public void setExclude(boolean value, String reason) {
    tableExcluded.setExcluded(value, reason);
  }

  public int tabularVersion() {
    return table_legend_columns.tabularversion();
  }

  public int saVersion() {
    return table_sacatalog_columns.saversion();
  }

  public String saVersionEst() {
    return table_sacatalog_columns.saversionest();
  }

  public int saTabularVersion() {
    return table_satabularver_columns.tabularversion();
  }

  public String saTabularVersionEst() {
    return table_satabularver_columns.tabularverest();
  }

  public int saSpatialVersion() {
    return table_saspatialver_columns.spatialversion();
  }

  public String saSpatialVersionEst() {
    return table_saspatialver_columns.spatialverest();
  }

  public void tabularVersion(int version) {
    table_legend_columns.tabularversion(version);
  }

  public void saVersion(int version) {
    table_sacatalog_columns.saversion(version);
  }

  public void saVersionEst(String versionEst) {
    table_sacatalog_columns.saversionest(versionEst);
  }

  public void saTabularVersion(int version) {
    table_satabularver_columns.tabularversion(version);
  }

  public void saTabularVersionEst(String versionEst) {
    table_satabularver_columns.tabularverest(versionEst);
  }

  public void saSpatialVersion(int version) {
    table_saspatialver_columns.spatialversion(version);
  }

  public void saSpatialVersionEst(String versionEst) {
    table_saspatialver_columns.spatialverest(versionEst);
  }

  public String toIfc(String cokey) throws ServiceException, SDMException, WEPSException {
    String ret_val = "";
    Component comp;

    if (components.size() > 0) {
      comp = (Component) components.get(cokey);

      if (null == comp) {
        throw new ServiceException("Cannot generate IFC file:  Component, " + cokey + ", was not found in the resulting mapunit's data.");
      }

//            if (comp.isOrganicSoil()) {
//                //  No need to add this mapunit's data to this generic file, just return.
//                return comp.toIfc(0, this);
//            }
      String state, county;
      String[] parts = areaname().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();
      }

      ret_val += soils.utils.SoilUtils.println(GenericIFCData.version);
      ret_val += soils.utils.SoilUtils.println("#");
      ret_val += soils.utils.SoilUtils.println("# Soil ID");
      ret_val += soils.utils.SoilUtils.println(areasymbol() + "-" + musym() + "-" + comp.compname()
          + "-" + comp.comppct_r() + "-" + comp.getWEPPSurfaceHorizon().getRepresentativeTextureName()
          + "-" + state + "-" + county + "-" + areaname());
      ret_val += soils.utils.SoilUtils.println("#");
      ret_val += comp.toIfc(brockdepmin());
    } else {
      throw new ServiceException("No components were found in this mapunit.  Cannot generate IFC file.");
    }
    return ret_val;
  }

  /**
   *
   * @param onlySelectedHorizons
   * @param selectedReasons
   * @return
   * @throws JSONException
   */
  public synchronized JSONArray toJSON(boolean onlySelectedHorizons, ArrayList<String> selectedReasons) throws JSONException {
    JSONArray ret_val = mapUnitJSON(new JSONArray());

    if (components.size() > 0) {
      JSONArray componentArray = new JSONArray();

      for (String cokey : components.keySet()) {
        Component tComponent = components.get(cokey);
        if (!tComponent.isExcluded()) {
          componentArray.put(tComponent.toJSON(onlySelectedHorizons, selectedReasons));
        }
      }
      if (componentArray.length() > 0) {
        ret_val.put(JSONUtils.data(SoilsData.MAPUNIT_COMPONENT_LIST_NAME, componentArray));
      }
    }

    return ret_val;
  }

  /**
   *
   * @param onlySelectedHorizons
   * @param selectedReasons
   * @return
   * @throws JSONException
   */
  public synchronized JSONObject toBasicJSON(boolean onlySelectedHorizons, List<String> selectedReasons) throws JSONException {
    JSONObject ret_val = mapUnitBasicJSON(new JSONObject());

    if (components.size() > 0) {
      JSONArray componentArray = new JSONArray();

      for (String cokey : components.keySet()) {
        Component tComponent = components.get(cokey);
        if (!tComponent.isExcluded()) {
          componentArray.put(tComponent.toBasicJSON(onlySelectedHorizons, selectedReasons));
        }
      }
      if (componentArray.length() > 0) {
        ret_val.put(SoilsData.MAPUNIT_COMPONENT_LIST_NAME, componentArray);
      }
    }

    return ret_val;
  }

  public synchronized JSONArray toJSON(boolean onlySelectedHorizons, boolean useExcluded) throws JSONException {
    JSONArray ret_val = mapUnitJSON(new JSONArray());

    if (components.size() > 0) {
      JSONArray componentArray = new JSONArray();

      for (String cokey : components.keySet()) {
        Component tComponent = components.get(cokey);
        if ((tComponent.isExcluded() && useExcluded) || (!tComponent.isExcluded() && !useExcluded)) {
          componentArray.put(tComponent.toJSON(onlySelectedHorizons, null));
        }
      }
      if (componentArray.length() > 0) {
        ret_val.put(JSONUtils.data(SoilsData.MAPUNIT_COMPONENT_LIST_NAME, componentArray));
      }
    }

    return ret_val;
  }

  /**
   *
   * @param onlySelectedHorizons
   * @return
   * @throws JSONException
   */
  public JSONArray toJSON(boolean onlySelectedHorizons) throws JSONException {
    return toJSON(onlySelectedHorizons, null);
  }
}