Pesticides.java [src/java/wqm/utils] Revision:   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 wqm.utils;

import static csip.ModelDataServiceConstants.KEY_DESC;
import static csip.ModelDataServiceConstants.KEY_METAINFO;
import static csip.ModelDataServiceConstants.KEY_NAME;
import static csip.ModelDataServiceConstants.KEY_PARAMETER;
import static csip.ModelDataServiceConstants.KEY_VALUE;
import csip.api.server.PayloadResults;
import csip.api.server.ServiceException;
import csip.utils.JSONUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import static soils.AoA.AOA_DRAINED;
import static soils.AoA.AOA_ID;
import static soils.AoA.CORRECTED_GEOMETRY;
import static soils.AoA.EXCLUDED_LIST;
import soils.Component;
import soils.MapUnit;
import soils.SoilsData;
import soils.db.tables.TableComponent;
import soils.db.tables.TableComponentCalculations;
import soils.db.tables.TableLegend;
import soils.db.tables.TableMapUnit;
import soils.db.tables.TableMapUnitCalculations;
import soils.db.tables.TableMuaggatt;
import soils.db.tables.TableSaCatalog;
import soils.db.tables.TableSaSpatialVer;
import soils.db.tables.TableSaTabularVer;
import utils.EvalResult;
import static wqm.utils.Pesticides.AoA.AOA_FILTER_PCT;

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

  private static final String COMP_PSLP_STRING[] = {"", "VERY LOW", "LOW", "INTERMEDIATE", "HIGH"};
  private static final String COMP_SARP[] = {"", "LOW", "INTERMEDIATE", "HIGH"};
  private static final String COMP_SSRP[] = {"", "LOW", "INTERMEDIATE", "HIGH"};
  private static final int HIGH = 4;
  private static final int INTERMEDIATE = 3;
  private static final int LOW = 2;
  private static final String PHR[] = {"", "LOW", "INTERMEDIATE", "HIGH", "EXTRA HIGH"};
  private static final String PHR_LEACH[] = {"", "VERY LOW", "LOW", "INTERMEDIATE", "HIGH", "EXTRA HIGH"};
  private static final int VERY_LOW = 1;
  private static final String WPLP_BANDED = "Banded";
  private static final String WPLP_FOLIARAPPLICATION = "Foliar Application";
  private static final String WPLP_HIGH = "HIGH";
  private static final String WPLP_INTERMEDIATE = "INTERMEDIATE";
  private static final String WPLP_LOW = "LOW";
  private static final String WPLP_SOILINCORPORATED = "Soil Incorporated";
  private static final String WPLP_SPOTTREATMENT = "Spot Treatment";
  private static final String WPLP_ULTRALOW = "ULTRA LOW";
  private static final String WPLP_VERYLOW = "VERY LOW";
  public static final String AI_LIST = "active_ingredient_list";
  public static final String PRODUCTS_LIST = "pesticide_list";
  public static String WQM_02_Service_URI;
  private JSONObject aoaGeometry;
  private boolean aoa_drained;
  private String aoa_id;
  private JSONObject badGeometryNote;
  private JSONObject badSoils;
  private Connection conn;
  private double minimumPercentage;
  protected Pesticides.AoA aoa;
  protected ConcurrentHashMap<Integer, Pesticide> pesticides = new ConcurrentHashMap<>();
  protected String rainProbability = "HIGH";
  protected PayloadResults results = null;

  int comp_pslp_number;
  int comp_sarp_number;
  int comp_ssrp_number;
  JSONObject metaInfo;

  TablePesticides tablePesticides = new TablePesticides();

  public Pesticides(Connection conn) {
    this.conn = conn;
  }

  public Pesticides(Connection conn, Map<String, JSONObject> inputJSON) throws ServiceException, JSONException, SQLException {
    this.conn = conn;
    if (JSONUtils.checkKeyExistsB(inputJSON, soils.AoA.AOA_GEOMETRY)) {
      minimumPercentage = JSONUtils.getDoubleParam(inputJSON, Pesticides.AoA.AOA_FILTER_PCT, 0.0);
      aoa_drained = JSONUtils.getBooleanParam(inputJSON, AOA_DRAINED, false);
      aoa_id = JSONUtils.getStringParam(inputJSON, AOA_ID, "0");
      //Get the entire aoa_geometry group as it matches the input payload exactly for WQM-2
      aoaGeometry = inputJSON.get(soils.AoA.AOA_GEOMETRY);
      if (inputJSON.containsKey("aoa_rain_probability")) {
        rainProbability = inputJSON.get("aoa_rain_probability").optString("value", "HIGH");
        if (!rainProbability.equals("HIGH") && !rainProbability.equals("INTERMEDIATE") && !rainProbability.equals("LOW") && !rainProbability.equals("VERY LOW")) {
          throw new ServiceException("Invalid value passed for aoa_rain_probability.  Acceptable values are: HIGH, INTERMEDIATE, LOW, VERY LOW");
        }
      } else {
        rainProbability = "HIGH";
      }

      readPesticides(inputJSON);
    } else {
      throw new ServiceException("No aoa geometry shape specified.  Need input parameter, [" + soils.AoA.AOA_GEOMETRY + "] .");
    }

  }

  public Pesticides(Connection conn, Map<String, JSONObject> inputJSON, PayloadResults _results) throws ServiceException, JSONException, SQLException {
    this.conn = conn;
    results = _results;

    if (JSONUtils.checkKeyExistsB(inputJSON, soils.AoA.AOA_GEOMETRY)) {
      minimumPercentage = JSONUtils.getDoubleParam(inputJSON, Pesticides.AoA.AOA_FILTER_PCT, 0.0);
      aoa_drained = JSONUtils.getBooleanParam(inputJSON, AOA_DRAINED, false);
      aoa_id = JSONUtils.getStringParam(inputJSON, AOA_ID, "0");
      //Get the entire aoa_geometry group as it matches the input payload exactly for WQM-2
      aoaGeometry = inputJSON.get(soils.AoA.AOA_GEOMETRY);
      if (inputJSON.containsKey("aoa_rain_probability")) {
        rainProbability = inputJSON.get("aoa_rain_probability").optString("value", "HIGH");
        if (!rainProbability.equals("HIGH") && !rainProbability.equals("INTERMEDIATE") && !rainProbability.equals("LOW") && !rainProbability.equals("VERY LOW")) {
          throw new ServiceException("Invalid value passed for aoa_rain_probability.  Acceptable values are: HIGH, INTERMEDIATE, LOW, VERY LOW");
        }
      } else {
        rainProbability = "HIGH";
      }

      readPesticides(inputJSON);
    } else {
      throw new ServiceException("No aoa geometry shape specified.  Need input parameter, [" + soils.AoA.AOA_GEOMETRY + "] .");
    }

  }

  private void generalizePestHazRating() throws ServiceException {
    int leach_human = 0;
    int leach_matcfish = 0;
    int sorun_human = 0;
    int sorun_matcfish = 0;
    int adrun_human = 0;
    int adrun_stvfish = 0;

    for (Pesticide pesticide : pesticides.values()) {
      if (pesticide.p_op_phr_leach_human() > leach_human) {
        leach_human = pesticide.p_op_phr_leach_human();
      }
      if (pesticide.p_op_phr_leach_matcfish() > leach_matcfish) {
        leach_matcfish = pesticide.p_op_phr_leach_matcfish();
      }
      if (pesticide.p_op_phr_sorun_human() > sorun_human) {
        sorun_human = pesticide.p_op_phr_sorun_human();
      }
      if (pesticide.p_op_phr_sorun_matcfish() > sorun_matcfish) {
        sorun_matcfish = pesticide.p_op_phr_sorun_matcfish();
      }
      if (pesticide.p_op_phr_adrun_human() > adrun_human) {
        adrun_human = pesticide.p_op_phr_adrun_human();
      }
      if (pesticide.p_op_phr_adrun_stvfish() > adrun_stvfish) {
        adrun_stvfish = pesticide.p_op_phr_adrun_stvfish();
      }
    }

    tablePesticides.phr_leach_human(translatePHRLeach(leach_human));
    tablePesticides.phr_leach_matcfish(translatePHRLeach(leach_matcfish));
    tablePesticides.phr_sorun_human(translatePHRLeach(sorun_human));
    tablePesticides.phr_sorun_matcfish(translatePHRLeach(sorun_matcfish));
    tablePesticides.phr_adrun_human(translatePHRLeach(adrun_human));
    tablePesticides.phr_adrun_stvfish(translatePHRLeach(adrun_stvfish));

  }

  public void setResults(PayloadResults _results) {
    results = _results;
  }

  public void readPesticides(Map<String, JSONObject> inputJSON) throws ServiceException, JSONException, SQLException {
    if (JSONUtils.checkKeyExistsB(inputJSON, PRODUCTS_LIST)) {
      JSONArray products = JSONUtils.getJSONArrayParam(inputJSON, PRODUCTS_LIST);
      for (int i = 0; i < products.length(); i++) {
        Pesticide pesticide = new Pesticide(products.getJSONArray(i));
        if (!pesticides.contains(pesticide.pest_id())) {
          pesticides.put(pesticide.pest_id(), pesticide);
        } else {
          throw new ServiceException("Duplicate input records for the pesticide product database row id, [" + pesticide.pest_id() + "], have been found in the input JSON. Item should be unique.");
        }
      }
    } else {
      throw new ServiceException("No pesticide products were listed in the input.  Need input parameter: [" + PRODUCTS_LIST + "] .");
    }
  }

  /**
   *
   * @param inputJSON
   * @throws ServiceException
   * @throws JSONException
   * @throws SQLException
   *
   * //Used to read the Component data for WQM-07, 08, 09
   */
  public void readAoAInput(Map<String, JSONObject> inputJSON) throws ServiceException, JSONException, SQLException {
    aoa = new Pesticides.AoA(inputJSON);
    aoa_drained = JSONUtils.getBooleanParam(inputJSON, AOA_DRAINED, false);
  }

  public void readWQM10Input(Map<String, JSONObject> inputJSON) throws ServiceException, JSONException, SQLException {
    aoa = new Pesticides.AoA();
    aoa.readWQM10Input(inputJSON);
    if (JSONUtils.checkKeyExistsB(inputJSON, PRODUCTS_LIST)) {
      JSONArray products = JSONUtils.getJSONArrayParam(inputJSON, PRODUCTS_LIST);
      for (int i = 0; i < products.length(); i++) {
        Pesticide pesticide = new Pesticide();
        pesticide.readWQM10Input(products.getJSONArray(i));
        pesticides.put(pesticide.pest_id(), pesticide);
      }
    }
  }

  public void readWQM11Input(Map<String, JSONObject> inputJSON) throws ServiceException, JSONException, SQLException {
    if (JSONUtils.checkKeyExistsB(inputJSON, PRODUCTS_LIST)) {
      JSONArray products = JSONUtils.getJSONArrayParam(inputJSON, PRODUCTS_LIST);
      for (int i = 0; i < products.length(); i++) {
        Pesticide pesticide = new Pesticide();
        pesticide.readWQM11Input(products.getJSONArray(i));
        pesticides.put(pesticide.pest_id(), pesticide);
      }
    }
  }

  /**
   * For WQM-11
   *
   * @throws ServiceException
   */
  public void calculatePestHazRating() throws ServiceException, SQLException {
    for (Pesticide pesticide : this.pesticides.values()) {
      pesticide.calculatePestHazRating();
    }
    generalizePestHazRating();
  }

  /**
   * For WQM-04
   *
   * @throws ServiceException
   */
  public void calculatePesticideLossPotentials() throws ServiceException {

    for (Pesticide pesticide : pesticides.values()) {
      pesticide.getLossPotentials(conn);
    }
  }

  /**
   * For WQM-09
   *
   * @throws ServiceException
   */
  public void calculatePesticideSARP() throws ServiceException {
    if (null != aoa) {
      aoa.computePestSARP();
    } else {
      throw new ServiceException("Cannot calculate Pesticide SARP; Missing AoA soils data.  Soils data was never calculated.");
    }
  }

  /**
   * For WQM-07
   *
   * @throws ServiceException
   */
  public void calculatePesticideSLP() throws ServiceException {
    if (null != aoa) {
      aoa.computePestSLP();
    } else {
      throw new ServiceException("Cannot calculate Pesticide SLP; Missing AoA soils data.  Soils data was never calculated.");
    }

  }

  /**
   * For WQM-08
   *
   * @throws ServiceException
   */
  public void calculatePesticideSSRP() throws ServiceException {
    if (null != aoa) {
      aoa.computePestSSRP();
    } else {
      throw new ServiceException("Cannot calculate Pesticide SSRP; Missing AoA soils data.  Soils data was never calculated.");
    }
  }

  /**
   * For WQM-10
   *
   * @throws ServiceException
   */
  public void calculateSoilPestLossPot() throws ServiceException {
    for (Pesticide pesticide : pesticides.values()) {
      pesticide.getILP(rainProbability, aoa.pslp(), aoa.pH());
      pesticide.getISRP(rainProbability, aoa.pssrp(), aoa.pH());
      pesticide.getIARP(rainProbability, aoa.psarp(), aoa.pH());

    }
  }

  public void getSoilData() throws ServiceException, JSONException {
    if (null != aoaGeometry) {
      JSONArray soilComponents;

      WQMSoilParams wqm02 = new WQMSoilParams(aoa_id, minimumPercentage, aoa_drained, aoaGeometry, WQM_02_Service_URI);
      wqm02.call();

      soilComponents = wqm02.getResultSection();
      Map<String, JSONObject> wqm02Map = JSONUtils.preprocess(soilComponents);

      if (wqm02Map.containsKey(CORRECTED_GEOMETRY)) {
        badGeometryNote = wqm02Map.get(CORRECTED_GEOMETRY);
      }

      if (wqm02Map.containsKey(EXCLUDED_LIST)) {
        badSoils = wqm02Map.get(EXCLUDED_LIST);
      }

      soilComponents.put(JSONUtils.dataDesc(AOA_DRAINED, aoa_drained, "Is AoA drained?"));

      metaInfo = wqm02.getReturnMetainfo();
      if (JSONUtils.getJSONArrayParam(wqm02Map, SoilsData.MAP_UNIT_LIST).length() <= 0) {
        throw new ServiceException("No soil components were found for that shape.  Cannot continue");
      }

      ArrayList<String> mapUnitColumns = new ArrayList<>();
      ArrayList<String> componentColumns = new ArrayList<>();

      mapUnitColumns.addAll(new TableMapUnit().getColumnList());
      mapUnitColumns.addAll(new TableMapUnitCalculations().getColumnList());
      mapUnitColumns.addAll(new TableMuaggatt().getColumnList());
      mapUnitColumns.addAll(new TableLegend().getColumnList());
      mapUnitColumns.addAll(new TableSaCatalog().getColumnList());
      mapUnitColumns.addAll(new TableSaSpatialVer().getColumnList());
      mapUnitColumns.addAll(new TableSaTabularVer().getColumnList());

      componentColumns.addAll(new TableComponent().getColumnList());
      componentColumns.addAll(new TableComponentCalculations().getColumnList());

      MapUnit.setDefaultUsedColumns(mapUnitColumns);
      Component.setDefaultUsedColumns(componentColumns);
      aoa = new Pesticides.AoA(JSONUtils.preprocess(soilComponents));
      if (!wqm02Map.containsKey("AoA Area")) {
        throw new ServiceException("No AoA area value was returned from WQM-02.  Cannot continue");
      } else {
        aoa.setArea(JSONUtils.getDoubleParam(wqm02Map, "AoA Area", 0.0));
      }

    } else {
      throw new ServiceException("Cannot build soils data for pesticides without an AoA Geometry Shape.");
    }
  }

  //TODO:  Verify this will read the input for WQM-07 correctly
  public void setSoilData(JSONArray inputData) throws JSONException, ServiceException {
    Map<String, JSONObject> wqm02Map = JSONUtils.preprocess(inputData);
      ArrayList<String> mapUnitColumns = new ArrayList<>();
      ArrayList<String> componentColumns = new ArrayList<>();

      mapUnitColumns.addAll(new TableMapUnit().getColumnList());
      mapUnitColumns.addAll(new TableMapUnitCalculations().getColumnList());
      mapUnitColumns.addAll(new TableMuaggatt().getColumnList());
      mapUnitColumns.addAll(new TableLegend().getColumnList());
      mapUnitColumns.addAll(new TableSaCatalog().getColumnList());
      mapUnitColumns.addAll(new TableSaSpatialVer().getColumnList());
      mapUnitColumns.addAll(new TableSaTabularVer().getColumnList());

      componentColumns.addAll(new TableComponent().getColumnList());
      componentColumns.addAll(new TableComponentCalculations().getColumnList());

      MapUnit.setDefaultUsedColumns(mapUnitColumns);
      Component.setDefaultUsedColumns(componentColumns);    
    aoa = new Pesticides.AoA(JSONUtils.preprocess(inputData));
    if (!wqm02Map.containsKey("AoA Area")) {
      throw new ServiceException("No AoA area value was returned from WQM-02.  Cannot continue");
    } else {
      aoa.setArea(JSONUtils.getDoubleParam(wqm02Map, "AoA Area", 0.0));
    }
  }

  public JSONArray toWQM27Result() throws JSONException, ServiceException {
    JSONArray ret_val = aoa.toJSON();
    tablePesticides.setOutputColumnOrdering(new ArrayList<>(Arrays.asList(
        TablePesticides.HUM_PHR_LEACH, TablePesticides.HUM_PHR_SORUN,
        TablePesticides.FISH_PHR_LEACH, TablePesticides.FISH_PHR_SORUN, TablePesticides.FISH_PHR_ADRUN)));  //Sets all as output columns
    tablePesticides.toJSON(ret_val);

    JSONArray operationArray = new JSONArray();
    // operationArray.put(JSONUtils.dataDesc("operation_id", "1", "Operation Identifier"));
    //JSONArray pesticidesArray = new JSONArray();

    for (Pesticide pesticide : pesticides.values()) {
      JSONArray pestData = new JSONArray();
      pesticide.toJSON(pestData);
      operationArray.put(pestData);
    }

    if (null != results) {
      results.put("pesticide summary", operationArray);
    } else {
      // operationArray.put(JSONUtils.data("pesticide summary", pesticidesArray));
      ret_val.put(JSONUtils.data("pesticide summary", operationArray));
    }

    //For now there is only one operation assumed for RS v3.0, but CDSI may require multiple operations
    //JSONArray operationsArray = new JSONArray();
    //operationsArray.put(operationArray);
    //ret_val.put(JSONUtils.data("operations", operationsArray));
    return ret_val;
  }

  protected void putResults(JSONArray outArray) throws JSONException {
    if (null != results) {
      for (int i = 0; i < outArray.length(); i++) {
        JSONObject outObject = outArray.getJSONObject(i);
        if (outObject.optString(KEY_NAME, "").equalsIgnoreCase(soils.AoA.MAP_UNIT_LIST)) {
          JSONArray outArray2 = outObject.getJSONArray(KEY_VALUE);
          results.put(soils.AoA.MAP_UNIT_LIST, outArray2);
        } else {
          try {
            writeResults(outObject);
          } catch (Exception ex) {
            throw new JSONException("Cannot create output results: " + ex.getMessage());
          }
        }
      }
    }
  }

  protected void writeResults(JSONObject outObject) throws Exception {
    results.put(outObject.getString(KEY_NAME), outObject.getString(KEY_VALUE), outObject.getString(KEY_DESC));
  }

  public JSONArray toWQM04Result() throws JSONException, ServiceException {
    JSONArray ret_val = new JSONArray();
    JSONArray pesticidesArray = new JSONArray();
    for (Pesticide pesticide : pesticides.values()) {
      JSONArray pestData = new JSONArray();
      pesticide.toWQM04JSON(pestData);
      pesticidesArray.put(pestData);
    }
    if (null != results) {
      results.put("pesticide summary", pesticidesArray);
    } else {
      ret_val.put(JSONUtils.data("pesticide summary", pesticidesArray));
    }
    return ret_val;
  }

  public JSONArray toWQM07Result() throws JSONException {
    JSONArray ret_val = aoa.toWQM07JSON();
    if (null != results) {
      putResults(ret_val);
    }
    return ret_val;
  }

  public JSONArray toWQM08Result() throws JSONException {
    JSONArray ret_val = aoa.toWQM08JSON();
    if (null != results) {
      putResults(ret_val);
    }
    return ret_val;
  }

  public JSONArray toWQM09Result() throws JSONException {
    JSONArray ret_val = aoa.toWQM09JSON();
    if (null != results) {
      putResults(ret_val);
    }
    return ret_val;
  }

  public JSONArray toWQM10JSON() throws JSONException, ServiceException {
    JSONArray ret_val = aoa.toJSON();

    JSONArray pesticidesArray = new JSONArray();
    for (Pesticide pesticide : pesticides.values()) {
      JSONArray pestData = new JSONArray();
      pesticide.toWQM10JSON(pestData);
      pesticidesArray.put(pestData);
    }

    if (null != results) {
      results.put("pesticide summary", pesticidesArray);
    } else {
      ret_val.put(JSONUtils.data("pesticide summary", pesticidesArray));
    }
    return ret_val;
  }

  public JSONArray toWQM11JSON() throws JSONException, ServiceException {
    JSONArray ret_val = new JSONArray();
    tablePesticides.setNonOutputColumns(null);  //Sets all as output columns
    tablePesticides.toJSON(ret_val);

    JSONArray operationArray = new JSONArray();

    for (Pesticide pesticide : pesticides.values()) {
      JSONArray pestData = new JSONArray();
      pesticide.toJSON(pestData);
      operationArray.put(pestData);
    }

    if (null != results) {
      results.put("pesticide summary", operationArray);
    } else {
      ret_val.put(JSONUtils.data("pesticide summary", operationArray));
    }

    return ret_val;
  }

  public int translatePHR(String phrString) throws ServiceException {
    int ret_val = -1;
    for (int i = 0; i < PHR.length; i++) {
      if (PHR[i].equalsIgnoreCase(phrString)) {
        ret_val = i;
        break;
      }
    }

    if (-1 == ret_val) {
      throw new ServiceException("Invalid PHR String Found, [" + phrString + "].");
    }

    return ret_val;
  }

  public String translatePHR(int phr) throws ServiceException {
    String ret_val = "";

    if ((phr >= 0) && (phr < PHR.length)) {
      ret_val = PHR[phr];
    }

    return ret_val;
  }

  public int translatePHRLeach(String phrLeachString) throws ServiceException {
    int ret_val = -1;
    for (int i = 0; i < PHR_LEACH.length; i++) {
      if (PHR_LEACH[i].equalsIgnoreCase(phrLeachString)) {
        ret_val = i;
        break;
      }
    }

    if (-1 == ret_val) {
      throw new ServiceException("Invalid PHR Leach String Found, [" + phrLeachString + "].");
    }

    return ret_val;
  }

  public String translatePHRLeach(int phrLeach) throws ServiceException {
    String ret_val = "";

    if ((phrLeach >= 0) && (phrLeach < PHR_LEACH.length)) {
      ret_val = PHR_LEACH[phrLeach];
    }

    return ret_val;
  }

  private class WQMSoilParams extends ServiceCall {

    private final String AoAId;
    private final double minimumPercentage;
    private final JSONObject aoaGeometry;

    WQMSoilParams(String AoAId, double minimumPercentage, boolean comp_drained, JSONObject aoaGeometry, String URI) {
      super(URI);
      this.AoAId = AoAId;
      this.minimumPercentage = minimumPercentage;
      this.aoaGeometry = aoaGeometry;
      errorPrefix = "WQMSoilParams";
    }

    @Override
    protected void createRequest() throws ServiceException {
      JSONArray dataArray;

      requestMetainfoObject = new JSONObject();
      request = new JSONObject();

      dataArray = new JSONArray();
      try {
        requestMetainfoObject.put("MultipartRequest", "Bundled Service Request WQM-27 combinedPesticideCalc");
        request.put(KEY_METAINFO, requestMetainfoObject);

        dataArray.put(JSONUtils.dataDesc(soils.AoA.AOA_ID, AoAId, "Area of Analysis Identifier"));
        dataArray.put(JSONUtils.dataDesc(AOA_FILTER_PCT, minimumPercentage, "Percent AoA Threshold for Including Soil Components in Result Payload; default is no filter value.  Example 0.10 for 10%."));
        dataArray.put(aoaGeometry);
        request.put(KEY_PARAMETER, dataArray);
      } catch (JSONException ex) {
        throwServiceCallException("Cannot create the JSON request.", ex);
      }
    }
  }

  /**
   * This class is used by the WQM-07 functionality, calculatePesticideSLP,
   * which requires soils data to find pH values
   */
  protected class AoA extends soils.AoA {

    protected static final String AOA_COMP_DRAINED = "aoa_comp_drained";
    protected static final String AOA_FILTER_PCT = "aoa_filter_pct";

    private JSONObject aoaGeometry = null;
    private double minimumPercentage = 0.0;
    private JSONObject badGeometryNote = null;
    private JSONObject badSoils = null;

    /**
     *
     * @param params
     * @throws ServiceException
     * @throws JSONException
     */
    public AoA(Map<String, JSONObject> params) throws ServiceException, JSONException {
      super(params);
    }

    public AoA() throws ServiceException, JSONException {
    }

    public void readWQM10Input(Map<String, JSONObject> params) throws ServiceException, JSONException {
      tableAoA.setRequiredColumns(new ArrayList<>(Arrays.asList(AoA.AOA_ID, AoA.PSLP, AoA.PSSRP, AoA.PSARP)));
      tableAoA.readValuesFromJSON(params);
    }

    public double minPercentage() {
      return minimumPercentage;
    }

    public double pH() {
      double pH = 0.0;
      double area_total = 0.0;
      int counter = 0;
      for (MapUnit mapUnit : map_units.values()) {
        for (Component component : mapUnit.components().values()) {
          pH += component.calculated_pH() * component.calculated_area();
          area_total += component.calculated_area();
          counter++;
        }
      }

      if (counter > 0) {
        pH = pH / area_total;
      } else {
        pH = 7.0;
      }

      return pH;
    }

    public JSONArray toJSON() throws JSONException {
      JSONArray ret_val = new JSONArray();
      tableAoA.setOutputColumnOrdering(new ArrayList(Arrays.asList(AoA.AOA_ID, AoA.PSLP, AoA.PSSRP, AoA.PSARP)));
      tableAoA.setOutputColumns(new ArrayList<>(Arrays.asList(AoA.AOA_ID, AoA.PSLP, AoA.PSSRP, AoA.PSARP)));
      tableAoA.toJSON(ret_val);
      return ret_val;
    }

    public JSONArray toWQM07JSON() throws JSONException {
      JSONArray ret_val = new JSONArray();
      tableAoA.setOutputColumns(new ArrayList<>(Arrays.asList(AoA.AOA_ID, AoA.PSLP)));
      try {
        setMapUnitOutputColumns(new ArrayList<>(Arrays.asList(TableMapUnit.MUKEY)),
            new ArrayList<>(Arrays.asList(TableComponent.COKEY,
                TableComponent.COMPNAME,
                TableComponentCalculations.PSLP)));
      } catch (ServiceException ex) {
        throw new JSONException("Cannot format JSON Results: " + ex.getMessage());
      }
      toJSON(true, ret_val);
      return ret_val;
    }

    public JSONArray toWQM08JSON() throws JSONException {
      JSONArray ret_val = new JSONArray();
      tableAoA.setOutputColumns(new ArrayList<>(Arrays.asList(AoA.AOA_ID)));
      try {
        setMapUnitOutputColumns(new ArrayList<>(Arrays.asList(TableMapUnit.MUKEY)),
            new ArrayList<>(Arrays.asList(TableComponent.COKEY,
                TableComponentCalculations.COMP_AREA_NAME,
                TableComponentCalculations.PSSRP,
                TableComponentCalculations.PSSRP_NUMBER))); //ssrp
      } catch (ServiceException ex) {
        throw new JSONException("Cannot format JSON Results: " + ex.getMessage());
      }
      toJSON(true, ret_val);
      return ret_val;
    }

    public JSONArray toWQM09JSON() throws JSONException {
      JSONArray ret_val = new JSONArray();
      tableAoA.setOutputColumns(new ArrayList<>(Arrays.asList(AoA.AOA_ID)));
      try {
        setMapUnitOutputColumns(new ArrayList<>(Arrays.asList(TableMapUnit.MUKEY)),
            new ArrayList<>(Arrays.asList(TableComponent.COKEY,
                TableComponentCalculations.COMP_AREA_NAME,
                TableComponentCalculations.PSARP,
                TableComponentCalculations.PSARP_NUMBER))); //sarp
      } catch (ServiceException ex) {
        throw new JSONException("Cannot format JSON Results: " + ex.getMessage());
      }
      toJSON(true, ret_val);
      return ret_val;
    }
  }

  protected class Pesticide {

    //private int pest_id; //Product Id, row Identifier, in d_pesticides_products.  Not PC_CODE
    private String reg_no;
    private String app_rate;
    private String app_area;
    private String app_method;
    private int app_rate_id;
    private int app_area_id;
    private int app_method_id;

//        private int p_op_phr_leach_human;
//        private int p_op_phr_leach_matcfish;
//        private int p_op_phr_sorun_human;
//        private int p_op_phr_sorun_matcfish;
//        private int p_op_phr_adrun_human;
//        private int p_op_phr_adrun_stvfish;
    ConcurrentHashMap<Integer, ActiveIngredient> activeIngredients = new ConcurrentHashMap<>();
    TablePesticide tablePesticide = new TablePesticide();

    Pesticide(int pest_id, int app_area_id, int app_method_id) throws SQLException, ServiceException {
      tablePesticide.pest_id(pest_id);
      this.app_area_id = app_area_id;
      this.app_method_id = app_method_id;

      try (Statement statement = conn.createStatement()) {
        String query = "SELECT area, method from wqm.d_pesticides_app_area, wqm.d_pesticides_app_method "
            + " where d_pesticides_app_area.id=" + app_area_id + " AND d_pesticides_app_method.id=" + app_method_id + ";";
        try (ResultSet results = statement.executeQuery(query)) {
          if (results.next()) {
            app_area = results.getString("area");
            app_method = results.getString("method");
          } else {
            throw new ServiceException("Cannot look up the application area or application method for this pesticide, " + pest_id);
          }
        }
      }
      initVariables();
    }

    Pesticide(JSONArray inputJSON) throws JSONException, ServiceException, SQLException {
      Map<String, JSONObject> productJSON = JSONUtils.preprocess(inputJSON);
      if (JSONUtils.checkKeyExistsB(productJSON, "pesticide_product_id")) {
        tablePesticide.pest_id(JSONUtils.getIntParam(productJSON, "pesticide_product_id", 0));
      } else {
        throw new ServiceException("Input parameter, [pesticide_product_id], not found in pesticide list.");
      }

      if (JSONUtils.checkKeyExistsB(productJSON, "app_area_id")) {
        app_area_id = JSONUtils.getIntParam(productJSON, "app_area_id", 0);
      } else {
        throw new ServiceException("Input parameter, [app_area_id], not found in pesticide list.");
      }

      if (JSONUtils.checkKeyExistsB(productJSON, "app_method_id")) {
        app_method_id = JSONUtils.getIntParam(productJSON, "app_method_id", 0);
      } else {
        throw new ServiceException("Input parameter, [app_method_id], not found in pesticide list.");
      }
      try (Statement statement = conn.createStatement()) {
        String query = "SELECT area, method from wqm.d_pesticides_app_area, wqm.d_pesticides_app_method "
            + " where d_pesticides_app_area.id=" + app_area_id + " AND d_pesticides_app_method.id=" + app_method_id + ";";
        try (ResultSet results = statement.executeQuery(query)) {
          if (results.next()) {
            app_area = results.getString("area");
            app_method = results.getString("method");
          } else {
            throw new ServiceException("Cannot look up the application area or application method for this pesticide, " + pest_id());
          }
        }
      }
      try (Statement statement = conn.createStatement()) {
        String query = "SELECT prod_name from wqm.d_pesticides_products where id=" + tablePesticide.pest_id() + ";";
        try (ResultSet results = statement.executeQuery(query)) {
          if (results.next()) {
            tablePesticide.pest_name(results.getString("prod_name"));
          } else {
            throw new ServiceException("Cannot look up the application area or application method for this pesticide, " + pest_id());
          }
        }
      }
      if (JSONUtils.checkKeyExistsB(productJSON, "active_ingredient_rates")) {
        JSONArray ingredientArray = JSONUtils.getJSONArrayParam(productJSON, "active_ingredient_rates");
        for (int i = 0; i < ingredientArray.length(); i++) {
          ActiveIngredient ingredient = new ActiveIngredient(ingredientArray.getJSONArray(i));
          if (!activeIngredients.containsKey(ingredient.ai_id())) {
            activeIngredients.put(ingredient.ai_id(), ingredient);
          } else {
            throw new ServiceException("Duplicate input records for the pesticide product active ingredient database row id, [" + pest_id() + ":" + ingredient.ai_id() + "], have been found in the input JSON. Item should be unique.");
          }
        }
      } else {
        throw new ServiceException("Input parameter, [active_ingredient_rates], not found in this pesticide, [" + pest_id() + "].");
      }

      initVariables();
    }

    public Pesticide() {
    }

    public void readWQM10Input(JSONArray inputJSON) throws JSONException, ServiceException, SQLException {
      Map<String, JSONObject> productJSON = JSONUtils.preprocess(inputJSON);
      if (JSONUtils.checkKeyExistsB(productJSON, "pesticide_product_id")) {
        tablePesticide.pest_id(JSONUtils.getIntParam(productJSON, "pesticide_product_id", 0));
      } else {
        throw new ServiceException("Input parameter, [pesticide_product_id], not found in pesticide list.");
      }

      if (JSONUtils.checkKeyExistsB(productJSON, "active_ingredient_rates")) {
        JSONArray ingredientArray = JSONUtils.getJSONArrayParam(productJSON, "active_ingredient_rates");
        for (int i = 0; i < ingredientArray.length(); i++) {
          Map<String, JSONObject> ai = JSONUtils.preprocess(ingredientArray.getJSONArray(i));
          int id = JSONUtils.getIntParam(ai, "ai_id", 0);
          String plp = JSONUtils.getStringParam(ai, "ai_plp", "0");
          String psrp = JSONUtils.getStringParam(ai, "ai_psrp", "0");
          String parp = JSONUtils.getStringParam(ai, "ai_parp", "0");
          ActiveIngredient ingredient = new ActiveIngredient(id, plp, psrp, parp);
          if (!activeIngredients.containsKey(ingredient.ai_id())) {
            activeIngredients.put(ingredient.ai_id(), ingredient);
          } else {
            throw new ServiceException("Duplicate input records for the pesticide product active ingredient database row id, [" + pest_id() + ":" + ingredient.ai_id() + "], have been found in the input JSON. Item should be unique.");
          }
        }
      }
    }

    public void readWQM11Input(JSONArray inputJSON) throws JSONException, ServiceException, SQLException {
      Map<String, JSONObject> productJSON = JSONUtils.preprocess(inputJSON);
      if (JSONUtils.checkKeyExistsB(productJSON, "pesticide_product_id")) {
        tablePesticide.pest_id(JSONUtils.getIntParam(productJSON, "pesticide_product_id", 0));
      } else {
        throw new ServiceException("Input parameter, [pesticide_product_id], not found in pesticide list.");
      }

      if (JSONUtils.checkKeyExistsB(productJSON, "active_ingredient_rates")) {
        JSONArray ingredientArray = JSONUtils.getJSONArrayParam(productJSON, "active_ingredient_rates");
        for (int i = 0; i < ingredientArray.length(); i++) {
          Map<String, JSONObject> ai = JSONUtils.preprocess(ingredientArray.getJSONArray(i));
          int id = JSONUtils.getIntParam(ai, "ai_id", 0);
          String op_pest_ilp = JSONUtils.getStringParam(ai, "op_pest_ilp", "0");
          String op_pest_isrp = JSONUtils.getStringParam(ai, "op_pest_isrp", "0");
          String op_pest_iarp = JSONUtils.getStringParam(ai, "op_pest_iarp", "0");
          double ai_humtox = JSONUtils.getDoubleParam(ai, "ai_humtox", 0);
          double ai_fishtox = JSONUtils.getDoubleParam(ai, "ai_fishtox", 0);
          double ai_koc = JSONUtils.getDoubleParam(ai, "ai_koc", 0);

          ActiveIngredient ingredient = new ActiveIngredient(id, op_pest_ilp, op_pest_isrp, op_pest_iarp, ai_humtox, ai_fishtox, ai_koc);
          if (!activeIngredients.containsKey(ingredient.ai_id())) {
            activeIngredients.put(ingredient.ai_id(), ingredient);
          } else {
            throw new ServiceException("Duplicate input records for the pesticide product active ingredient database row id, [" + pest_id() + ":" + ingredient.ai_id() + "], have been found in the input JSON. Item should be unique.");
          }

        }
      }
      initVariables();
    }

    private void initVariables() throws ServiceException {
      p_op_phr_leach_human("");
      p_op_phr_leach_matcfish("");
      p_op_phr_sorun_human("");
      p_op_phr_sorun_matcfish("");
      p_op_phr_adrun_human("");
      p_op_phr_adrun_stvfish("");
    }

    public int pest_id() {
      return tablePesticide.pest_id();
    }

    public void pest_id(int value) {
      tablePesticide.pest_id(value);
    }

    public int p_op_phr_leach_human() throws ServiceException {
      return tablePesticide.phr_leach_human_number();
    }

    public int p_op_phr_leach_matcfish() throws ServiceException {
      return tablePesticide.phr_leach_matcfish_number();
    }

    public int p_op_phr_sorun_human() throws ServiceException {
      return tablePesticide.phr_sorun_human_number();
    }

    public int p_op_phr_sorun_matcfish() throws ServiceException {
      return tablePesticide.phr_sorun_matcfish_number();
    }

    public int p_op_phr_adrun_human() throws ServiceException {
      return tablePesticide.phr_adrun_human_number();
    }

    public int p_op_phr_adrun_stvfish() throws ServiceException {
      return tablePesticide.phr_adrun_stvfish_number();
    }

    public void p_op_phr_leach_human(String value) throws ServiceException {
      tablePesticide.phr_leach_human(value);
      tablePesticide.phr_leach_human_number(translatePHRLeach(value));
    }

    public void p_op_phr_leach_matcfish(String value) throws ServiceException {
      tablePesticide.phr_leach_matcfish(value);
      tablePesticide.phr_leach_matcfish_number(translatePHRLeach(value));
    }

    public void p_op_phr_sorun_human(String value) throws ServiceException {
      tablePesticide.phr_sorun_human(value);
      tablePesticide.phr_sorun_human_number(translatePHRLeach(value));
    }

    public void p_op_phr_sorun_matcfish(String value) throws ServiceException {
      tablePesticide.phr_sorun_matcfish(value);
      tablePesticide.phr_sorun_matcfish_number(translatePHRLeach(value));
    }

    public void p_op_phr_adrun_human(String value) throws ServiceException {
      tablePesticide.phr_adrun_human(value);
      tablePesticide.phr_adrun_human_number(translatePHRLeach(value));
    }

    public void p_op_phr_adrun_stvfish(String value) throws ServiceException {
      tablePesticide.phr_adrun_stvfish(value);
      tablePesticide.phr_adrun_stvfish_number(translatePHRLeach(value));
    }

    public void toJSON(JSONArray outArray) throws JSONException, ServiceException {
      //tablePesticide.setOutputColumn(TablePesticide.PEST_ID);
      tablePesticide.setOutputColumnOrdering(new ArrayList<>(Arrays.asList(TablePesticide.PEST_ID,
          TablePesticide.PEST_NAME, TablePesticide.HUM_PHR_LEACH, TablePesticide.HUM_PHR_SORUN,
          TablePesticide.FISH_PHR_LEACH, TablePesticide.FISH_PHR_SORUN, TablePesticide.FISH_PHR_ADRUN)));

      tablePesticide.toJSON(outArray);

      JSONArray aiArray = new JSONArray();

      for (ActiveIngredient ingredient : this.activeIngredients.values()) {
        JSONArray ingredientArray = new JSONArray();
        ingredient.toJSON(ingredientArray);
        aiArray.put(ingredientArray);
      }

      outArray.put(JSONUtils.data("active ingredient list", aiArray));
    }

    public void toWQM04JSON(JSONArray outArray) throws JSONException, ServiceException {
      tablePesticide.setOutputColumns(new ArrayList<>(Arrays.asList(TablePesticide.PEST_ID)));
      tablePesticide.toJSON(outArray);

      outArray.put(JSONUtils.data("app_area_id", app_area_id, "Pesticide application operation area database Id"));
      outArray.put(JSONUtils.data("app_area", app_area, "Pesticide application operation area"));
      outArray.put(JSONUtils.data("app_method_id", app_method_id, "Pesticide application operation method database Id"));
      outArray.put(JSONUtils.data("app_method", app_method, "Pesticide application operation method"));

      JSONArray aiArray = new JSONArray();
      for (ActiveIngredient ingredient : this.activeIngredients.values()) {
        JSONArray ingredientArray = new JSONArray();
        ingredient.toWQM04JSON(ingredientArray);
        aiArray.put(ingredientArray);

      }

      outArray.put(JSONUtils.data("active ingredient list", aiArray));
    }

    public void toWQM10JSON(JSONArray outArray) throws JSONException, ServiceException {
      tablePesticide.setOutputColumns(new ArrayList<>(Arrays.asList(TablePesticide.PEST_ID, TablePesticide.HUM_PHR_LEACH, TablePesticide.FISH_PHR_LEACH,
          TablePesticide.HUM_PHR_SORUN, TablePesticide.FISH_PHR_SORUN, TablePesticide.HUM_PHR_ADRUN, TablePesticide.FISH_PHR_ADRUN)));
      tablePesticide.toJSON(outArray);

      JSONArray aiArray = new JSONArray();
      for (ActiveIngredient ingredient : this.activeIngredients.values()) {
        JSONArray ingredientArray = new JSONArray();
        ingredient.toWQM10JSON(ingredientArray);
        aiArray.put(ingredientArray);
      }

      outArray.put(JSONUtils.data("active ingredient list", aiArray));
    }

    public void toWQM11JSON(JSONArray outArray) throws JSONException, ServiceException {
      tablePesticide.setOutputColumns(new ArrayList<>(Arrays.asList(TablePesticide.PEST_ID)));
      tablePesticide.toJSON(outArray);

      JSONArray aiArray = new JSONArray();
      for (ActiveIngredient ingredient : this.activeIngredients.values()) {
        JSONArray ingredientArray = new JSONArray();
        ingredient.toWQM11JSON(ingredientArray);
        aiArray.put(ingredientArray);
      }

      outArray.put(JSONUtils.data("active ingredient list", aiArray));
    }

    private class ActiveIngredient {

      private double ai_fishtox = 0.0;
      private String ai_fishtoxtype = "";
      private double ai_humtox = 0.0;
      private String ai_humtoxtype = "";
      private int app_rate_id;
      private String app_rate;
      private String pc_code;
//            private String op_pest_ilp;
//            private String op_pest_isrp;
//            private String op_pest_iarp;
      private String hum_tox_rating;
      private String fish_tox_rating;
      private boolean hadToxRecord = false;

      ConcurrentHashMap<Double, ValBypH> pHValues = new ConcurrentHashMap<>();
      TableActiveIngredient tableActiveIngredient = new TableActiveIngredient();
      TableActiveIngredientDebug tableActiveIngredientDebug = new TableActiveIngredientDebug();

      ActiveIngredient(int ai_id, int app_rate_id) throws SQLException, ServiceException {
        tableActiveIngredient.ai_id(ai_id);
        this.app_rate_id = app_rate_id;
        initVariables();
        getDbStrings();
      }

      ActiveIngredient(JSONArray inputJSON) throws SQLException, ServiceException, JSONException {
        Map<String, JSONObject> ingredientJSON = JSONUtils.preprocess(inputJSON);
        if (JSONUtils.checkKeyExistsB(ingredientJSON, "ai_id")) {
          tableActiveIngredient.ai_id(JSONUtils.getIntParam(ingredientJSON, "ai_id", 0));
        } else {
          throw new ServiceException("Input parameter, [ai_id], not found in pesticide, [" + pest_id() + "], active ingredient list.");
        }

        if (JSONUtils.checkKeyExistsB(ingredientJSON, "app_rate_id")) {
          app_rate_id = JSONUtils.getIntParam(ingredientJSON, "app_rate_id", 0);
        } else {
          throw new ServiceException("Input parameter, [app_rate_id], not found in pesticide, [" + pest_id() + "], active ingredient list.");
        }

        if (JSONUtils.checkKeyExistsB(ingredientJSON, "ai_name")) {
          tableActiveIngredient.ai_name(JSONUtils.getStringParam(ingredientJSON, "ai_name", EvalResult.getDefaultString()));
        }

        initVariables();
        getDbStrings();
      }

      ActiveIngredient(int ai_id, String ai_plp, String ai_psrp, String ai_parp) {
        tableActiveIngredient.ai_id(ai_id);
        pHValues.put(7.0, new ValBypH(ai_plp, ai_psrp, ai_parp));
      }

      ActiveIngredient(int ai_id, String op_pest_ilp, String op_pest_isrp, String op_pest_iarp, double ai_humtox, double ai_fishtox, double koc) {
        tableActiveIngredient.ai_id(ai_id);
        tableActiveIngredientDebug.op_pest_ilp(op_pest_ilp);
        tableActiveIngredientDebug.op_pest_isrp(op_pest_isrp);
        tableActiveIngredientDebug.op_pest_iarp(op_pest_iarp);
        this.ai_humtox = ai_humtox;
        this.ai_fishtox = ai_fishtox;
        pHValues.put(7.0, new ValBypH(koc));
        getValBypH(7.0);
      }

      private void initVariables() throws ServiceException {
        op_phr_leach_human("");
        op_phr_leach_matcfish("");
        op_phr_sorun_human("");
        op_phr_sorun_matcfish("");
        op_phr_adrun_human("");
        op_phr_adrun_stvfish("");
      }

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

      public boolean hadToxRecord() {
        return hadToxRecord;
      }

      public int ai_id() {
        return tableActiveIngredient.ai_id();
      }

      public void ai_id(int value) {
        tableActiveIngredient.ai_id(value);
      }

      public String op_phr_leach_human_string() {
        return tableActiveIngredient.phr_leach_human();
      }

      public String op_phr_leach_matcfish_string() {
        return tableActiveIngredient.phr_leach_matcfish();
      }

      public String op_phr_sorun_human_string() {
        return tableActiveIngredient.phr_sorun_human();
      }

      public String op_phr_sorun_matcfish_string() {
        return tableActiveIngredient.phr_sorun_matcfish();
      }

      public String op_phr_adrun_human_string() {
        return tableActiveIngredient.phr_adrun_human();
      }

      public String op_phr_adrun_stvfish_string() {
        return tableActiveIngredient.phr_adrun_stvfish();
      }

      public int op_phr_leach_human() throws ServiceException {
        return tableActiveIngredient.phr_leach_human_number();
      }

      public int op_phr_leach_matcfish() throws ServiceException {
        return tableActiveIngredient.phr_leach_matcfish_number();
      }

      public int op_phr_sorun_human() throws ServiceException {
        return tableActiveIngredient.phr_sorun_human_number();
      }

      public int op_phr_sorun_matcfish() throws ServiceException {
        return tableActiveIngredient.phr_sorun_matcfish_number();
      }

      public int op_phr_adrun_human() throws ServiceException {
        return tableActiveIngredient.phr_adrun_human_number();
      }

      public int op_phr_adrun_stvfish() throws ServiceException {
        return tableActiveIngredient.phr_adrun_stvfish_number();
      }

      public void op_phr_leach_human(String value) throws ServiceException {
        tableActiveIngredient.phr_leach_human(value);
        tableActiveIngredient.phr_leach_human_number(translatePHRLeach(value));
      }

      public void op_phr_leach_matcfish(String value) throws ServiceException {
        tableActiveIngredient.phr_leach_matcfish(value);
        tableActiveIngredient.phr_leach_matcfish_number(translatePHRLeach(value));
      }

      public void op_phr_sorun_human(String value) throws ServiceException {
        tableActiveIngredient.phr_sorun_human(value);
        tableActiveIngredient.phr_sorun_human_number(translatePHRLeach(value));
      }

      public void op_phr_sorun_matcfish(String value) throws ServiceException {
        tableActiveIngredient.phr_sorun_matcfish(value);
        tableActiveIngredient.phr_sorun_matcfish_number(translatePHRLeach(value));
      }

      public void op_phr_adrun_human(String value) throws ServiceException {
        tableActiveIngredient.phr_adrun_human(value);
        tableActiveIngredient.phr_adrun_human_number(translatePHRLeach(value));
      }

      public void op_phr_adrun_stvfish(String value) throws ServiceException {
        tableActiveIngredient.phr_adrun_stvfish(value);
        tableActiveIngredient.phr_adrun_stvfish_number(translatePHRLeach(value));
      }

      private void getDbStrings() throws SQLException, ServiceException {
        try (Statement statement = conn.createStatement()) {
          String query = "SELECT rate from wqm.d_pesticides_app_rate "
              + " where d_pesticides_app_rate.id=" + app_rate_id + ";";
          try (ResultSet results = statement.executeQuery(query)) {
            if (results.next()) {
              app_rate = results.getString("rate");
            } else {
              throw new ServiceException("Cannot look up the application rate this active ingredient, " + ai_id());
            }
          }
        }
      }

      public void toJSON(JSONArray outputArray) throws JSONException {
        tableActiveIngredient.setOutputColumnOrdering(new ArrayList<>(Arrays.asList(TableActiveIngredient.AI_ID, TableActiveIngredient.AI_NAME)));
        tableActiveIngredient.toJSON(outputArray);

        tableActiveIngredientDebug.setOutputColumnOrdering(new ArrayList<>(Arrays.asList(TableActiveIngredientDebug.PLP,
            TableActiveIngredientDebug.PSRP, TableActiveIngredientDebug.PARP, TableActiveIngredientDebug.OP_PEST_ILP,
            TableActiveIngredientDebug.OP_PEST_ISRP, TableActiveIngredientDebug.OP_PEST_IARP)));
        tableActiveIngredientDebug.toJSON(outputArray);

        tableActiveIngredient.setOutputColumnOrdering(new ArrayList<>(Arrays.asList(TableActiveIngredient.HUM_PHR_LEACH,
            TableActiveIngredient.HUM_PHR_SORUN, TableActiveIngredient.FISH_PHR_LEACH, TableActiveIngredient.FISH_PHR_SORUN,
            TableActiveIngredient.FISH_PHR_ADRUN)));
        tableActiveIngredient.toJSON(outputArray);

      }

      public void toWQM04JSON(JSONArray outputArray) throws JSONException {
        tableActiveIngredient.setOutputColumns(new ArrayList<>(Arrays.asList(TableActiveIngredient.AI_ID)));
        tableActiveIngredient.toJSON(outputArray);

        outputArray.put(JSONUtils.data("ai_name", tableActiveIngredient.ai_name(), "Active Ingredient Name"));
        outputArray.put(JSONUtils.data("app_rate_id", app_rate_id, "Pesticide application rate database Id"));
        outputArray.put(JSONUtils.data("app_rate", app_rate, "Pesticide application rate"));
        outputArray.put(JSONUtils.data("ai_humtox", ai_humtox, "Active ingredient human toxicity value"));
        outputArray.put(JSONUtils.data("ai_humtoxtype", ai_humtoxtype, "Active ingredient human toxicity type"));
        outputArray.put(JSONUtils.data("ai_fishtox", ai_fishtox, "Active ingredient maximum acceptable toxicant concentration-fish"));
        outputArray.put(JSONUtils.data("ai_fishtoxtype", ai_fishtoxtype, "Active ingredient fish toxicity type"));

        if (!pHValues.isEmpty()) {
          for (double key : pHValues.keySet()) {
            outputArray.put(JSONUtils.data("ai_ph", key, "Active ingredient pH of associate properties"));
            pHValues.get(key).toJSON(outputArray);
          }
        }
      }

      public void toWQM10JSON(JSONArray outputArray) throws JSONException {
        tableActiveIngredient.setOutputColumns(new ArrayList<>(Arrays.asList(TableActiveIngredient.AI_ID)));
        tableActiveIngredient.toJSON(outputArray);

        tableActiveIngredientDebug.setOutputColumns(new ArrayList<>(Arrays.asList(TableActiveIngredientDebug.OP_PEST_ILP, TableActiveIngredientDebug.OP_PEST_ISRP, TableActiveIngredientDebug.OP_PEST_IARP)));
        tableActiveIngredientDebug.toJSON(outputArray);
      }

      public void toWQM11JSON(JSONArray outputArray) throws JSONException, ServiceException {
        tableActiveIngredient.setOutputColumns(new ArrayList<>(Arrays.asList(TableActiveIngredient.AI_ID)));
        tableActiveIngredient.toJSON(outputArray);
        outputArray.put(JSONUtils.data("op_phr_leach_human", op_phr_leach_human_string()));
        outputArray.put(JSONUtils.data("op_phr_leach_matcfish", op_phr_leach_matcfish_string()));
        outputArray.put(JSONUtils.data("op_phr_sorun_human", op_phr_sorun_human_string()));
        outputArray.put(JSONUtils.data("op_phr_sorun_matcfish", op_phr_sorun_matcfish_string()));
        outputArray.put(JSONUtils.data("op_phr_adrun_human", op_phr_adrun_human_string()));
        outputArray.put(JSONUtils.data("op_phr_adrun_stvfish", op_phr_adrun_stvfish_string()));
      }

      public class ValBypH {

        double hl;
        double koc;
        double sol;
        String ai_plp;
        String ai_psrp;
        String ai_parp;
        ;
                boolean selected;

        public ValBypH(double ai_hl, double ai_koc, double ai_sol, String app_area, String app_method, String app_rate) {
          hl = ai_hl;
          koc = ai_koc;
          sol = ai_sol;

          // PLP:   New technique.
          double log_val = (Math.log10(ai_hl)) * (4 - Math.log10(ai_koc));
          ai_plp = WPLP_INTERMEDIATE;
//                    if (log_val >= 2.8) {
//                        ai_plp = WPLP_HIGH;
//                    } else {
//                        if ((log_val < 0.0) || ((ai_sol < 1) && (ai_hl <= 1))) {
//                            ai_plp = WPLP_VERYLOW;
//                        } else {
//                            if (log_val <= 1.8) {
//                                ai_plp = WPLP_LOW;
//                            }
//                        }
//                    }
//
//                    if (!ai_plp.equalsIgnoreCase(WPLP_VERYLOW)) {
//                        if ((app_area.equalsIgnoreCase(WPLP_BANDED)) || (app_method.equalsIgnoreCase(WPLP_FOLIARAPPLICATION))
//                                || (app_rate.equalsIgnoreCase(WPLP_LOW)) || (app_rate.equalsIgnoreCase(WPLP_ULTRALOW)) ) {
//                            if (ai_plp.equalsIgnoreCase(WPLP_HIGH)) {
//                                ai_plp = WPLP_INTERMEDIATE;
//                            } else {
//                                if (ai_plp.equalsIgnoreCase(WPLP_INTERMEDIATE)) {
//                                    ai_plp = WPLP_LOW;
//                                } else {
//                                    if (ai_plp.equalsIgnoreCase(WPLP_LOW)) {
//                                        ai_plp = WPLP_VERYLOW;
//                                    }
//                                }
//                            }
//                        } else {
//                            if ((app_area.equalsIgnoreCase(WPLP_SPOTTREATMENT)) || (app_rate.equalsIgnoreCase(WPLP_ULTRALOW))) {
//                                if (ai_plp.equalsIgnoreCase(WPLP_HIGH)) {
//                                    ai_plp = WPLP_LOW;
//                                } else {
//                                    if (ai_plp.equalsIgnoreCase(WPLP_INTERMEDIATE)) {
//                                        ai_plp = WPLP_VERYLOW;
//                                    } else {
//                                        if (ai_plp.equalsIgnoreCase(WPLP_LOW)) {
//                                            ai_plp = WPLP_VERYLOW;
//                                        }
//                                    }
//                                }
//                            }
//                        }
//                    }

          if (log_val >= 2.8) {
            if ((app_area.equalsIgnoreCase(WPLP_SPOTTREATMENT)) || (app_rate.equalsIgnoreCase(WPLP_ULTRALOW))) {
              ai_plp = WPLP_LOW;
            } else if ((app_area.equalsIgnoreCase(WPLP_BANDED)) || (app_method.equalsIgnoreCase(WPLP_FOLIARAPPLICATION)) || (app_rate.equalsIgnoreCase(WPLP_LOW))) {
              ai_plp = WPLP_INTERMEDIATE;
            } else {
              ai_plp = WPLP_HIGH;
            }
          } else if ((log_val < 0.0) || ((ai_sol < 1) && (ai_hl <= 1))) {
            ai_plp = WPLP_VERYLOW;
          } else if (log_val <= 1.8) {
            if ((app_area.equalsIgnoreCase(WPLP_BANDED)) || (app_area.equalsIgnoreCase(WPLP_SPOTTREATMENT)) || (app_method.equalsIgnoreCase(WPLP_FOLIARAPPLICATION)) || (app_rate.equalsIgnoreCase(WPLP_LOW)) || (app_rate.equalsIgnoreCase(WPLP_ULTRALOW))) {
              ai_plp = WPLP_VERYLOW;
            } else {
              ai_plp = WPLP_LOW;
            }
          } else if ((app_area.equalsIgnoreCase(WPLP_SPOTTREATMENT)) || (app_rate.equalsIgnoreCase(WPLP_ULTRALOW))) {
            ai_plp = WPLP_VERYLOW;
          } else if ((app_area.equalsIgnoreCase(WPLP_BANDED)) || (app_method.equalsIgnoreCase(WPLP_FOLIARAPPLICATION)) || (app_rate.equalsIgnoreCase(WPLP_LOW))) {
            ai_plp = WPLP_LOW;
          } else {
            ai_plp = WPLP_INTERMEDIATE;
          }

          // PSRP:  New technique to avoid going below a "LOW" rating.
          ai_psrp = WPLP_INTERMEDIATE;
          if (((ai_sol >= 1) && (ai_hl > 35) && (ai_koc < 100_000)) || (((ai_sol >= 10) && (ai_sol < 100) && (ai_koc <= 700)))) {
            ai_psrp = WPLP_HIGH;
          } else {
            if ((ai_koc >= 100_000) || ((ai_koc >= 1_000) && (ai_hl <= 1)) || ((ai_sol < 0.5) && (ai_hl < 35))) {
              ai_psrp = WPLP_LOW;
            }
          }

          if (!ai_psrp.equalsIgnoreCase(WPLP_LOW)) {
            if ((app_area.equalsIgnoreCase(WPLP_BANDED)) || (app_method.equalsIgnoreCase(WPLP_FOLIARAPPLICATION))
                || (app_method.equalsIgnoreCase(WPLP_SOILINCORPORATED)) || (app_rate.equalsIgnoreCase(WPLP_LOW))) {
              if (ai_psrp.equalsIgnoreCase(WPLP_HIGH)) {
                ai_psrp = WPLP_INTERMEDIATE;
              } else {
                if (ai_psrp.equalsIgnoreCase(WPLP_INTERMEDIATE)) {
                  ai_psrp = WPLP_LOW;
                }
              }
            } else {
              if ((app_method.equalsIgnoreCase(WPLP_SOILINCORPORATED)) || (app_rate.equalsIgnoreCase(WPLP_ULTRALOW))) {
                if (ai_psrp.equalsIgnoreCase(WPLP_HIGH)) {
                  ai_psrp = WPLP_LOW;
                } else {
                  if (ai_psrp.equalsIgnoreCase(WPLP_INTERMEDIATE)) {
                    ai_psrp = WPLP_LOW;
                  }
                }
              }
            }
          }

          //PARP:   New technique to avoid going below a "LOW" rating.
          ai_parp = WPLP_INTERMEDIATE;
          if (((ai_hl >= 40) && (ai_koc >= 1_000)) || ((ai_hl >= 40) && (ai_koc >= 500) && (ai_sol <= 0.5))) {
            ai_parp = WPLP_HIGH;
          } else {
            if ((ai_hl <= 1) || ((ai_hl <= 2) && (ai_koc <= 500)) || ((ai_hl <= 4) && (ai_koc <= 900) && (ai_sol >= 0.5))
                || ((ai_hl <= 40) && (ai_koc <= 500) && (ai_sol >= 0.5)) || ((ai_hl <= 40) && (ai_koc <= 900) && (ai_sol >= 2))) {
              ai_parp = WPLP_LOW;
            }
          }

          if (!ai_parp.equalsIgnoreCase(WPLP_LOW)) {
            if ((app_area.equalsIgnoreCase(WPLP_BANDED)) || (app_method.equalsIgnoreCase(WPLP_FOLIARAPPLICATION))
                || (app_method.equalsIgnoreCase(WPLP_SOILINCORPORATED)) || (app_rate.equalsIgnoreCase(WPLP_LOW))) {
              if (ai_parp.equalsIgnoreCase(WPLP_HIGH)) {
                ai_parp = WPLP_INTERMEDIATE;
              } else {
                if (ai_parp.equalsIgnoreCase(WPLP_INTERMEDIATE)) {
                  ai_parp = WPLP_LOW;
                }
              }
            } else {
              if ((app_method.equalsIgnoreCase(WPLP_SOILINCORPORATED)) || (app_rate.equalsIgnoreCase(WPLP_ULTRALOW))) {
                if (ai_parp.equalsIgnoreCase(WPLP_HIGH)) {
                  ai_parp = WPLP_LOW;
                } else {
                  if (ai_parp.equalsIgnoreCase(WPLP_INTERMEDIATE)) {
                    ai_parp = WPLP_LOW;
                  }
                }
              }
            }
          }
        }

        public ValBypH(String ai_plp, String ai_psrp, String ai_parp) {
          this.ai_plp = ai_plp;
          this.ai_psrp = ai_psrp;
          this.ai_parp = ai_parp;
        }

        public ValBypH(double koc) {
          this.koc = koc;
        }

        public void toJSON(JSONArray outputArray) throws JSONException {
          outputArray.put(JSONUtils.data("ai_hl", hl, "Active ingredient field half life"));
          outputArray.put(JSONUtils.data("ai_koc", koc, "Active ingredient soil organic carbon sorption coefficient"));
          outputArray.put(JSONUtils.data("ai_sol", sol, "Active ingredient solubility in water"));
          outputArray.put(JSONUtils.data("ai_plp", ai_plp, "Active ingredient pesticide leaching potential"));
          outputArray.put(JSONUtils.data("ai_psrp", ai_psrp, "Active ingredient pesticide solution runoff potential"));
          outputArray.put(JSONUtils.data("ai_parp", ai_parp, "Active ingredient pesticide adsorbed runoff potential"));
        }
      }

      private ValBypH getValBypH(double pH) {
        double diff = -9999;
        ValBypH selectedVal = null;
        for (double val : pHValues.keySet()) {
          if (Math.abs(val - pH) < Math.abs(diff)) {
            selectedVal = pHValues.get(val);
          }
        }

        if (null != selectedVal) {
          selectedVal.selected = true;
        }

        return selectedVal;
      }

      public void calculatePestHazRating() throws SQLException, ServiceException {
        try (Statement statement = conn.createStatement()) {
          for (ValBypH pHValue : pHValues.values()) {
            if (pHValue.selected) {
              if (ai_humtox < 1) {
                hum_tox_rating = "EXTRA HIGH";
              } else if (ai_humtox >= 1 && ai_humtox < 10) {
                hum_tox_rating = "HIGH";
              } else if (ai_humtox >= 10 && ai_humtox < 50) {
                hum_tox_rating = "INTERMEDIATE";
              } else if (ai_humtox >= 50 && ai_humtox < 100) {
                hum_tox_rating = "LOW";
              } else if (ai_humtox >= 100) {
                hum_tox_rating = "VERY LOW";
              }

              String eat_rating_human_temp = "'" + hum_tox_rating + "'";
              String op_pest_ilp_temp = "'" + tableActiveIngredientDebug.op_pest_ilp() + "'";

              ResultSet results = statement.executeQuery(DBQueries.WQM11Query01(op_pest_ilp_temp, eat_rating_human_temp));

              while (results.next()) {
                op_phr_leach_human(results.getString("wqm_phr"));
              }
              String op_pest_isrp_temp = "'" + tableActiveIngredientDebug.op_pest_isrp() + "'";

              results = statement.executeQuery(DBQueries.WQM11Query02(op_pest_isrp_temp, eat_rating_human_temp));

              while (results.next()) {
                op_phr_sorun_human(results.getString("wqm_phr"));
              }
              String op_pest_iarp_temp = "'" + tableActiveIngredientDebug.op_pest_iarp() + "'";

              results = statement.executeQuery(DBQueries.WQM11Query03(op_pest_iarp_temp, eat_rating_human_temp));

              while (results.next()) {
                op_phr_adrun_human(results.getString("wqm_phr"));
              }
              // #Compute pesticide hazard rating for adsorbed runoff adjusted for toxicity to fish (STV)

              double ai_eatstv = ai_fishtox * pHValue.koc;

              if (ai_eatstv < 10) {
                fish_tox_rating = "EXTRA HIGH";
              } else if (ai_eatstv >= 10 && ai_eatstv < 100) {
                fish_tox_rating = "HIGH";
              } else if (ai_eatstv >= 100 && ai_eatstv < 1500) {
                fish_tox_rating = "INTERMEDIATE";
              } else if (ai_eatstv >= 1500 && ai_eatstv < 20000) {
                fish_tox_rating = "LOW";
              } else if (ai_eatstv >= 20000) {
                fish_tox_rating = "VERY LOW";
              }
              String eat_rating_stvfish_temp = "'" + fish_tox_rating + "'";

              results = statement.executeQuery(DBQueries.WQM11Query04(op_pest_iarp_temp, eat_rating_stvfish_temp));

              while (results.next()) {
                op_phr_adrun_stvfish(results.getString("wqm_phr"));
              }

              // Compute pesticide hazard rating for leaching, solution runoff, and adsorbed runoff adjusted for toxicity to fish (MATC)
              String eat_rating_matcfish = null;
//                            if (ai_fishtox < 10) {
//                                eat_rating_matcfish = "EXTRA HIGH";
//                            } else if (ai_fishtox >= 10 && ai_fishtox < 100) {
//                                eat_rating_matcfish = "HIGH";
//                            } else if (ai_fishtox >= 100 && ai_fishtox < 1500) {
//                                eat_rating_matcfish = "INTERMEDIATE";
//                            } else if (ai_fishtox >= 1500 && ai_fishtox < 20000) {
//                                eat_rating_matcfish = "LOW";
//                            } else if (ai_fishtox >= 20000) {
//                                eat_rating_matcfish = "VERY LOW";
//                            }

              if (ai_fishtox < 1) {
                eat_rating_matcfish = "EXTRA HIGH";
              } else if (ai_fishtox >= 1 && ai_fishtox < 10) {
                eat_rating_matcfish = "HIGH";
              } else if (ai_fishtox >= 10 && ai_fishtox < 100) {
                eat_rating_matcfish = "INTERMEDIATE";
              } else if (ai_fishtox >= 100 && ai_fishtox < 500) {
                eat_rating_matcfish = "LOW";
              } else if (ai_fishtox >= 500) {
                eat_rating_matcfish = "VERY LOW";
              }
              String eat_rating_matcfish_temp = "'" + eat_rating_matcfish + "'";

              results = statement.executeQuery(DBQueries.WQM11Query05(op_pest_ilp_temp, eat_rating_matcfish_temp));

              while (results.next()) {
                op_phr_leach_matcfish(results.getString("wqm_phr"));
              }

              results = statement.executeQuery(DBQueries.WQM11Query06(op_pest_isrp_temp, eat_rating_matcfish_temp));

              while (results.next()) {
                op_phr_sorun_matcfish(results.getString("wqm_phr"));
              }
            }
          }
        }
      }

      public void getILP(String rain_probability, String aoa_pslp, double pH) throws ServiceException {
        ValBypH ingredient = getValBypH(pH);

        if (null != ingredient) {
          try (Statement statement = conn.createStatement()) {
            try (ResultSet resultSet = statement.executeQuery(DBQueries.WQM10Query01(ingredient.ai_plp, aoa_pslp))) {
              if (resultSet.next()) {
                tableActiveIngredientDebug.op_pest_ilp(resultSet.getString("wqm_ilp"));
              } else {
                throw new ServiceException("For active ingredient: " + ai_id() + ", Cannot find that ai_plp and aoa_pslp combination in the database.");
              }

              if ("LOW".equals(rain_probability)) {
                switch (tableActiveIngredientDebug.op_pest_ilp()) {
                  case "HIGH":
                    tableActiveIngredientDebug.op_pest_ilp("INTERMEDIATE");
                    break;
                  case "INTERMEDIATE":
                    tableActiveIngredientDebug.op_pest_ilp("LOW");
                    break;
                  case "LOW":
                    tableActiveIngredientDebug.op_pest_ilp("VERY LOW");
                    break;
                }
              }
              tableActiveIngredientDebug.plp(ingredient.ai_plp);
            }
          } catch (SQLException ex) {
            throw new ServiceException("SQL Error: " + ex.getMessage(), ex);
          }
        } else {
          throw new ServiceException("No active ingredient data was found for this soil.  Ingredients pH values may be empty.");
        }
      }

      public void getISRP(String rain_probability, String aoa_ssrp, double pH) throws ServiceException {
        ValBypH ingredient = getValBypH(pH);
        try (Statement statement = conn.createStatement()) {
          try (ResultSet resultSet = statement.executeQuery(DBQueries.WQM10Query02(ingredient.ai_psrp, aoa_ssrp))) {
            tableActiveIngredientDebug.op_pest_isrp("err");
            if (resultSet.next()) {
              tableActiveIngredientDebug.op_pest_isrp(resultSet.getString("wqm_isrp"));
            } else {
              throw new ServiceException("For active ingredient: " + ai_id() + ", Cannot find that ai_psrp and aoa_ssrp combination in the database.");
            }

            if ("LOW".equals(rain_probability)) {
              switch (tableActiveIngredientDebug.op_pest_isrp()) {
                case "HIGH":
                  tableActiveIngredientDebug.op_pest_isrp("INTERMEDIATE");
                  break;
                case "INTERMEDIATE":
                  tableActiveIngredientDebug.op_pest_isrp("LOW");
                  break;

                /*case "LOW":
                                    op_pest_isrp = "LOW";
                                    break;\
                 */
              }
            }
            tableActiveIngredientDebug.psrp(ingredient.ai_psrp);
          }
        } catch (SQLException ex) {
          throw new ServiceException("SQL Error: " + ex.getMessage(), ex);
        }
      }

      public void getIARP(String rain_probability, String aoa_sarp, double pH) throws ServiceException {
        ValBypH ingredient = getValBypH(pH);

        try (Statement statement = conn.createStatement()) {
          try (ResultSet resultSet = statement.executeQuery(DBQueries.WQM10Query03(ingredient.ai_parp, aoa_sarp))) {
            tableActiveIngredientDebug.op_pest_iarp("err");
            if (resultSet.next()) {
              tableActiveIngredientDebug.op_pest_iarp(resultSet.getString("wqm_iarp"));
            } else {
              throw new ServiceException("For active ingredient: " + ai_id() + ", Cannot find that ai_parp and aoa_sarp combination in the database.");
            }

            if ("LOW".equals(rain_probability)) {
              switch (tableActiveIngredientDebug.op_pest_iarp()) {
                case "HIGH":
                  tableActiveIngredientDebug.op_pest_iarp("INTERMEDIATE");
                  break;
                case "INTERMEDIATE":
                  tableActiveIngredientDebug.op_pest_iarp("LOW");
                  break;

                /*case "LOW":
                                    op_pest_iarp = "VERY LOW";
                                    break;
                 */
              }
            }
            tableActiveIngredientDebug.parp(ingredient.ai_parp);
          }
        } catch (SQLException ex) {
          throw new ServiceException("SQL Error: " + ex.getMessage(), ex);
        }

      }

      public void setLossPotentials(String app_area, String app_method, ResultSet results) throws ServiceException, SQLException {
        if (null != results) {
          double ai_ph;
          String chem_id = results.getString("chem_id");

          if (EvalResult.testDefaultString(tableActiveIngredient.ai_name()) || (tableActiveIngredient.ai_name().isEmpty())) {
            tableActiveIngredient.ai_name(results.getString("ai_name"));
          }

          if ((null == ai_humtoxtype) || (ai_humtoxtype.isEmpty())) {
            ai_humtox = results.getDouble("HUM_TOX_PPB");
            ai_humtoxtype = results.getString("HUM_TOX_TYPE");
          }
          if ((null == ai_fishtoxtype) || (ai_fishtoxtype.isEmpty())) {
            ai_fishtox = results.getDouble("FISH_TOX_PPB");
            ai_fishtoxtype = results.getString("FISH_TOX_TYPE");
          }

          ai_ph = results.getDouble("pH");

          if (!pHValues.contains(ai_ph)) {
            double ai_hl = results.getDouble("SOIL_HL_RV");
            double ai_koc = results.getDouble("KOC_RV");
            double ai_sol = results.getDouble("SOL_RV");

            pHValues.put(ai_ph, new ValBypH(ai_hl, ai_koc, ai_sol, app_area, app_method, app_rate));
          } else {
            throw new ServiceException("Duplicate pH record found in ChembyPh table.  Please notify the SME or data steward to update the table for chem_id: " + chem_id);
          }

        } else {
          throw new ServiceException("Invalid ResultSet passed to ActiveIngredient::setLossPotentials");
        }
      }
    }

    public void calculatePestHazRating() throws ServiceException, SQLException {
      for (ActiveIngredient ingredient : activeIngredients.values()) {
        ingredient.calculatePestHazRating();
        if (ingredient.op_phr_leach_human() > p_op_phr_leach_human()) {
          p_op_phr_leach_human(ingredient.op_phr_leach_human_string());
        }
        if (ingredient.op_phr_leach_matcfish() > p_op_phr_leach_matcfish()) {
          p_op_phr_leach_matcfish(ingredient.op_phr_leach_matcfish_string());
        }
        if (ingredient.op_phr_sorun_human() > p_op_phr_sorun_human()) {
          p_op_phr_sorun_human(ingredient.op_phr_sorun_human_string());
        }
        if (ingredient.op_phr_sorun_matcfish() > p_op_phr_sorun_matcfish()) {
          p_op_phr_sorun_matcfish(ingredient.op_phr_sorun_matcfish_string());
        }
        if (ingredient.op_phr_adrun_human() > p_op_phr_adrun_human()) {
          p_op_phr_adrun_human(ingredient.op_phr_adrun_human_string());
        }
        if (ingredient.op_phr_adrun_stvfish() > p_op_phr_adrun_stvfish()) {
          p_op_phr_adrun_stvfish(ingredient.op_phr_adrun_stvfish_string());
        }
      }
    }

    public void getLossPotentials(Connection conn) throws ServiceException {
      String ais = "";
      int counter = 0;
      for (int key : activeIngredients.keySet()) {
        if (counter > 0) {
          ais += ", ";
        }
        ais += key;
        counter++;
      }

      try (Statement statement = conn.createStatement()) {
        String query = " select d_pesticides_ais.Id as ai_id, d_pesticides_ais.pc_code, ai_name, ph, SOIL_HL_RV, KOC_RV, SOL_RV, "
            + " d_pesticides_humtox.tox_type as hum_tox_type, d_pesticides_humtox.tox_ppb as hum_tox_ppb, "
            + " d_pesticides_fishtox.tox_type as fish_tox_type, d_pesticides_fishtox.tox_ppb as fish_tox_ppb, d_pesticides_chembyph.CHEM_ID"
            + "	from wqm.d_pesticides_ais "
            + "		inner join wqm.d_pesticides_fishtox on wqm.d_pesticides_ais.pc_code = wqm.d_pesticides_fishtox.pc_code "
            + "		inner join wqm.d_pesticides_humtox on wqm.d_pesticides_ais.pc_code = wqm.d_pesticides_humtox.pc_code  "
            + "		inner join wqm.d_pesticides_chembyph on wqm.d_pesticides_fishtox.Chem_id = wqm.d_pesticides_chembyph.CHEM_ID"
            + " WHERE  d_pesticides_ais.Id IN ( " + ais + ")  "
            + " AND d_pesticides_chembyph.pc_code= d_pesticides_ais.pc_code "
            + " ORDER BY  d_pesticides_ais.Id;";

        try (ResultSet results = statement.executeQuery(query)) {

          counter = 0;
          while (results.next()) {
            int ai_id = results.getInt("ai_id");
            ActiveIngredient ingredient = activeIngredients.get(ai_id);
            if (null != ingredient) {
              ingredient.setLossPotentials(app_area, app_method, results);
              ingredient.hadToxRecord(true);
            } else {
              throw new ServiceException("Bad data returned in the SQL results.  Extra Active Ingredient Id found that was not specified in the input.  Contact the data steward to correct the dataabse.");
            }
            counter++;
          }

          for (ActiveIngredient ingredient : activeIngredients.values()) {
            if (!ingredient.hadToxRecord()) {
              throw new ServiceException("No toxicity results were found for an active ingredient, [" + ingredient.ai_id() + "].  Contact the data steward to update the database.");
            }
          }

          if (counter <= 0) {
            throw new ServiceException("No toxicity results were found for that active ingredient list, [" + ais + "].");
          }
        }
      } catch (SQLException ex) {
        throw new ServiceException("SQL Error: " + ex.getMessage(), ex);
      }
    }

    public void getILP(String rain_probability, String aoa_pslp, double pH) throws ServiceException {
      for (ActiveIngredient ingredient : activeIngredients.values()) {
        ingredient.getILP(rain_probability, aoa_pslp, pH);
      }
    }

    public void getISRP(String rain_probability, String aoa_ssrp, double pH) throws ServiceException {
      for (ActiveIngredient ingredient : activeIngredients.values()) {
        ingredient.getISRP(rain_probability, aoa_ssrp, pH);
      }
    }

    public void getIARP(String rain_probability, String aoa_sarp, double pH) throws ServiceException {
      for (ActiveIngredient ingredient : activeIngredients.values()) {
        ingredient.getIARP(rain_probability, aoa_sarp, pH);
      }
    }

  }

}