CliWindSoilFile.java [tools/GetLatLonCokey/src/getlatloncokey] Revision: 69ab6eb5379799363ea8548530ac0cb331f66446  Date: Fri Jan 10 11:03:01 MST 2020
/*
 * 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 getlatloncokey;

import csip.ServiceException;
import csip.utils.JSONUtils;
import data.interpretors.CligenData;
import static data.interpretors.CligenData.OBS_MONTHLY_PRCP;
import static data.interpretors.CligenData.PRCP_INDEX;
import data.interpretors.WindGenData;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import soils.AoA;
import soils.Component;
import soils.MapUnit;
import soils.SoilsData;
import static soils.SoilsData.MAPUNIT_COMPONENT_LIST_NAME;
import soils.db.tables.TableComponent;
import soils.db.tables.TableComponentCalculations;
import soils.db.tables.TableHorizon;
import soils.db.tables.TableHorizonCalculations;
import soils.db.tables.TableMapUnit;
import soils.db.tables.TableMapUnitCalculations;

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

  protected static final String SOILS_RETURN = "soils.json";
  protected static final String SOILS_REQUEST = "request.json";
  protected static final String CLIMATE_FILE = ".cli";
  protected static final String WIND_FILE = ".win";
  protected static final String LATITUDE = "latitude";
  protected static final String LONGITUDE = "longitude";
  protected static final String COKEY = TableComponent.COKEY;
  protected static final String SLOPE_R = TableComponent.SLOPE_R_NAME;
  protected static final String LENGTH_R = TableComponentCalculations.LENGTH_R_NAME;
  protected static final String WATER_CLAY_TOTAL = "water_" + TableHorizon.CLAYTOTAL_R;
  protected static final String WATER_SAND_TOTAL = "water_" + TableHorizon.SANDTOTAL_R_NAME;
  protected static final String WATER_OM_R = "water_" + TableHorizon.OM_R_NAME;
  protected static final String WIND_CLAY_TOTAL = "wind_" + TableHorizon.CLAYTOTAL_R;
  protected static final String WIND_SAND_TOTAL = "wind_" + TableHorizon.SANDTOTAL_R_NAME;
  protected static final String WIND_OM_R = "wind_" + TableHorizon.OM_R_NAME;
  protected static final String KFFACT = TableComponentCalculations.KFFACT_NAME;
  protected static final String TILLAGE_LAYER_CLAY_TOTAL = TableComponentCalculations.TILLAGE_LAYER_CLAY_TOTAL;

  protected static final String SIM_AVG_PRECIP = "100yr_avg_precip";
  protected static final String SIM_OBS_AVG_PRECIP = "100yr_obs_avg_precip";
  protected static final String SIM_AVG_WIND_ENERGY = "wind_energy_100yr_avg";
  protected static final String ANNUAL_AVG_PRECIP = "annual_avg_precip";
  protected static final String ANNUAL_OBS_AVG_PRECIP = "annual_obs_avg_precip";
  protected static final String ANNUAL_WIND_ENERGY_AVG = "annual_wind_energy_avg";
  protected static final String ANNUAL_WIND_ENEGERY_AVG_NON_ZERO = "annual_wind_energy_avg_non_zero";
  protected static final String ANNUAL_WIND_ENERGY_AVG_PEAK = "annual_wind_energy_avg_peak";
  protected static final String MONTHLY_AVG_PRECIP = "monthly_avg_precip";
  protected static final String MONTLY_OBS_AVG_PRECIP = "monthly_obs_avg_precip";
  protected static final String MONTHLY_WIND_ENGERY_AVG = "monthly_wind_energy_avg";
  protected static final String MONTHLY_WIND_ENERGY_AVG_NON_ZERO = "monthly_wind_energy_avg_non_zero";
  protected static final String MONTHLY_WIND_ENERGY_AVG_PEAK = "monthly_wind_energy_avg_peak";

  public static final ArrayList<String> soilFields = new ArrayList<>(Arrays.asList(
      LATITUDE,
      LONGITUDE,
      COKEY,
      KFFACT,
      SLOPE_R,
      LENGTH_R,
      WATER_CLAY_TOTAL,
      WATER_SAND_TOTAL,
      WATER_OM_R,
      WIND_CLAY_TOTAL,
      WIND_SAND_TOTAL,
      WIND_OM_R,
      TILLAGE_LAYER_CLAY_TOTAL
  ));

  public static final ArrayList<String> simTotalFields = new ArrayList<>(Arrays.asList(
      LATITUDE,
      LONGITUDE,
      COKEY,
      KFFACT,
      SLOPE_R,
      LENGTH_R,
      WATER_CLAY_TOTAL,
      WATER_SAND_TOTAL,
      WATER_OM_R,
      WIND_CLAY_TOTAL,
      WIND_SAND_TOTAL,
      WIND_OM_R,
      TILLAGE_LAYER_CLAY_TOTAL,
      SIM_AVG_PRECIP,
      SIM_OBS_AVG_PRECIP,
      SIM_AVG_WIND_ENERGY
  ));

  public static final ArrayList<String> annualAverageFields = new ArrayList<>(Arrays.asList(
      LATITUDE,
      LONGITUDE,
      COKEY,
      ANNUAL_AVG_PRECIP,
      ANNUAL_OBS_AVG_PRECIP,
      ANNUAL_WIND_ENERGY_AVG,
      ANNUAL_WIND_ENEGERY_AVG_NON_ZERO,
      ANNUAL_WIND_ENERGY_AVG_PEAK
  ));

  public static final ArrayList<String> monthlyAverageFields = new ArrayList<>(Arrays.asList(
      LATITUDE,
      LONGITUDE,
      COKEY,
      MONTHLY_AVG_PRECIP,
      MONTLY_OBS_AVG_PRECIP,
      MONTHLY_WIND_ENGERY_AVG,
      MONTHLY_WIND_ENERGY_AVG_NON_ZERO,
      MONTHLY_WIND_ENERGY_AVG_PEAK
  ));

  protected JSONArray soilsReturn;
  protected JSONArray returnVal;
  protected JSONObject request;
  protected Double latitude = Double.NaN, longitude = Double.NaN;
  protected String cokey = "";
  protected soils.AoA aoa;
  protected ArrayList<String> simFields;
  protected ArrayList<String> annualFields;
  protected ArrayList<String> monthlyFields;
  protected Component maxComponent = null;
  protected MapUnit maxMapUnit = null;
  protected HashMap<String, Object> fieldData = new HashMap<>();
  protected CligenData climateData;
  protected WindGenData windData;
  protected TableHorizon waterHorizon;
  protected TableHorizon windHorizon;
  protected boolean wroteHeader = false;
  protected ArrayList<String> fields;

  public CliWindSoilFile(File filename, ArrayList<String> _fields, boolean _wroteHeader) throws IOException, JSONException, ServiceException {
    boolean gotSoilsResponse = false;
    boolean gotRequest = false;
    boolean gotWind = false;
    boolean gotClimate = false;
    boolean gotAllFields = true;

    wroteHeader = _wroteHeader;

    if (null == _fields) {
      throw new ServiceException("No field names specified for extraction.");
    } else {
      fields = _fields;
    }

    //simTotalFields.
    InputStream zipFile = new FileInputStream(filename);

    try (ZipInputStream zin = new ZipInputStream(zipFile)) {
      ZipEntry entry;

      while ((entry = zin.getNextEntry()) != null) {
        if (entry.getName().contains(SOILS_RETURN) && !gotSoilsResponse) {
          returnVal = new JSONArray(readInputFile(zin));
          //soilsReturn = returnVal.getJSONArray(KEY_RESULT).getJSONArray(0);

          Map<String, JSONObject> inputMap = JSONUtils.preprocess(returnVal);
          soils.AoA.setRequiredInputs(new ArrayList<>(Arrays.asList(AoA.MAP_UNIT_LIST)),
              null, null, null);

          MapUnit.setDefaultUsedColumns(new ArrayList<>(Arrays.asList(TableMapUnit.MUKEY, TableMapUnitCalculations.AREA_NAME, SoilsData.MAPUNIT_COMPONENT_LIST_NAME)));
          Component.setDefaultUsedColumns(new ArrayList<>(Arrays.asList(TableComponent.COKEY, TableComponent.SLOPE_R_NAME,
              TableComponent.HYDGRP_NAME, TableComponent.COMPPCT_R_NAME,
              TableComponentCalculations.COMP_AREA_NAME, TableComponentCalculations.LENGTH_R_NAME, TableComponentCalculations.AREA_PCT_NAME,
              TableComponentCalculations.TILLAGE_LAYER_CLAY_TOTAL, TableComponentCalculations.KFFACT_NAME)));

          aoa = new soils.AoA(inputMap);

          if (aoa.getMapUnits().size() <= 0) {
            throw new ServiceException("Mapunits returned for this AoA did not match expected number of 1.  Mapunit size is: " + aoa.getMapUnits().size());
          }

          maxMapUnit = null;
          for (String mukey : aoa.getMapUnits().keySet()) {
            MapUnit mapunit = aoa.getMapUnits().get(mukey);
            if (null == maxMapUnit) {
              maxMapUnit = mapunit;
            } else {
              if (mapunit.area() > maxMapUnit.area()) {
                maxMapUnit = mapunit;
              }
            }
          }

          maxComponent = null;
          if (null != maxMapUnit) {
            for (String cokey : maxMapUnit.components().keySet()) {
              Component component = maxMapUnit.components().get(cokey);
              if (null == maxComponent) {
                maxComponent = component;
              } else {
                if (component.area_pct() > maxComponent.area_pct()) {
                  maxComponent = component;
                  maxComponent.mukey(maxMapUnit.mukey());
                }
              }
            }
          } else {
            throw new ServiceException("No max mapunit was found.");
          }
          if (null != maxComponent) {
            cokey = maxComponent.cokey();
            JSONArray map_units = JSONUtils.getJSONArrayParam(inputMap, AoA.MAP_UNIT_LIST);
            for (int mapUnitIndex = 0; mapUnitIndex < map_units.length(); mapUnitIndex++) {
              JSONArray map_unit = map_units.getJSONArray(mapUnitIndex);

              Map<String, JSONObject> tmap_unit = JSONUtils.preprocess(map_unit);

              String mukey = JSONUtils.getStringParam(tmap_unit, TableMapUnit.MUKEY, "");
              if (!mukey.isEmpty()) {
                if (mukey.equalsIgnoreCase(maxMapUnit.mukey())) {
                  JSONArray components = JSONUtils.getJSONArrayParam(tmap_unit, MAPUNIT_COMPONENT_LIST_NAME);

                  for (int componentIndex = 0; componentIndex < components.length(); componentIndex++) {
                    JSONArray component = components.getJSONArray(componentIndex);

                    Map<String, JSONObject> tcomponent = JSONUtils.preprocess(component);

                    String cokey = JSONUtils.getStringParam(tcomponent, TableComponent.COKEY, "");
                    if (!cokey.isEmpty()) {
                      if (cokey.equalsIgnoreCase(maxComponent.cokey())) {
                        JSONArray waterHorizons = JSONUtils.getJSONArrayParam(tcomponent, "water horizons");
                        JSONArray windHorizons = JSONUtils.getJSONArrayParam(tcomponent, "wind horizons");
                        if (waterHorizons.length() >= 1) {
                          JSONArray horizon = waterHorizons.getJSONArray(0);

                          waterHorizon = new TableHorizon();
                          waterHorizon.setUsedColumns(new ArrayList<>(Arrays.asList(
                              TableHorizon.CHKEY_NAME, TableHorizon.SANDTOTAL_R_NAME,
                              TableHorizon.SILTTOTAL_R, TableHorizon.CLAYTOTAL_R, TableHorizon.OM_R_NAME,
                              TableHorizon.HZTHK_R_NAME, TableHorizon.HZDEPB_R_NAME, TableHorizon.HZDEPT_R_NAME,
                              TableHorizonCalculations.FRAGVOL_R_NAME
                          )));
                          Map<String, JSONObject> horizonMap = JSONUtils.preprocess(horizon);

                          waterHorizon.readValuesFromJSON(horizonMap);
                          waterHorizon.cokey(cokey);
                        }

                        if (windHorizons.length() >= 1) {
                          JSONArray horizon = windHorizons.getJSONArray(0);

                          windHorizon = new TableHorizon();
                          windHorizon.setUsedColumns(new ArrayList<>(Arrays.asList(
                              TableHorizon.CHKEY_NAME, TableHorizon.SANDTOTAL_R_NAME,
                              TableHorizon.SILTTOTAL_R, TableHorizon.CLAYTOTAL_R, TableHorizon.OM_R_NAME,
                              TableHorizon.HZTHK_R_NAME, TableHorizon.HZDEPB_R_NAME, TableHorizon.HZDEPT_R_NAME,
                              TableHorizonCalculations.FRAGVOL_R_NAME
                          )));
                          Map<String, JSONObject> horizonMap = JSONUtils.preprocess(horizon);

                          windHorizon.readValuesFromJSON(horizonMap);
                          windHorizon.cokey(cokey);
                        }

                        if ((null != windHorizon) && (null != waterHorizon)) {
                          gotSoilsResponse = true;
                        }

                      }
                    }
                  }
                }
              }
            }
          }
        }

        if (entry.getName().contains(SOILS_REQUEST) && !gotRequest) {
          request = new JSONObject(readInputFile(zin));
          String coordinates = request.getString("coordinates");
          String type = request.getString("type");

          if (type.equalsIgnoreCase("point")) {
            coordinates = coordinates.replace("[", " ");
            coordinates = coordinates.replace("]", " ");
            String[] coord = coordinates.split(",");
            if (coord.length == 2) {
              longitude = Double.parseDouble(coord[0]);
              latitude = Double.parseDouble(coord[1]);
              gotRequest = true;
            }
          }
        }

        if (entry.getName().contains(CLIMATE_FILE) && !gotClimate) {
          climateData = new CligenData(readInputFile(zin));
          if (!climateData.badClimateData()) {
            gotClimate = true;
          }
        }

        if (entry.getName().contains(WIND_FILE) && !gotWind) {
          windData = new WindGenData(readInputFile(zin));
          if (!windData.badWindData()) {
            gotWind = true;
          }
        }

        if (gotRequest && gotSoilsResponse && gotClimate && gotWind) {
          break;
        }
      }

      if (gotRequest && gotSoilsResponse && gotClimate && gotWind) {
        for (String field : fields) {
          switch (field) {
            case LONGITUDE:
              fieldData.put(LONGITUDE, longitude);
              break;

            case LATITUDE:
              fieldData.put(LATITUDE, latitude);
              break;

            case COKEY:
              fieldData.put(COKEY, cokey);
              break;

            case WATER_CLAY_TOTAL:
              fieldData.put(WATER_CLAY_TOTAL, waterHorizon.claytotal_r());
              break;

            case WIND_CLAY_TOTAL:
              fieldData.put(WIND_CLAY_TOTAL, windHorizon.claytotal_r());
              break;

            case WATER_SAND_TOTAL:
              fieldData.put(WATER_SAND_TOTAL, waterHorizon.sandtotal_r());
              break;

            case WIND_SAND_TOTAL:
              fieldData.put(WIND_SAND_TOTAL, windHorizon.sandtotal_r());
              break;

            case KFFACT:
              fieldData.put(KFFACT, maxComponent.calculated_kffact());
              break;

            case WATER_OM_R:
              fieldData.put(WATER_OM_R, waterHorizon.om_r());
              break;

            case WIND_OM_R:
              fieldData.put(WIND_OM_R, windHorizon.om_r());
              break;

            case SLOPE_R:
              fieldData.put(SLOPE_R, maxComponent.slope_r());
              break;

            case LENGTH_R:
              fieldData.put(LENGTH_R, maxComponent.length_r());
              break;

            case TILLAGE_LAYER_CLAY_TOTAL:
              fieldData.put(TILLAGE_LAYER_CLAY_TOTAL, maxComponent.calculated_tl_claytotal());
              break;

            case ANNUAL_AVG_PRECIP:
              for (int i = 0; i < climateData.yearsInFile(); i++) {
                fieldData.put(ANNUAL_AVG_PRECIP + "_" + (i + 1), climateData.yearlySummary(i, CligenData.PRCP_INDEX));
              }
              break;

            case SIM_OBS_AVG_PRECIP:
              fieldData.put(SIM_OBS_AVG_PRECIP, climateData.annualAvgPrecip());
              break;

            case ANNUAL_WIND_ENERGY_AVG:
              for (int i = 0; i < windData.yearsInFile(); i++) {
                fieldData.put(ANNUAL_WIND_ENERGY_AVG + "_" + (i + 1), windData.yearlyAverage(i));
              }
              break;

            case SIM_AVG_WIND_ENERGY:
              fieldData.put(SIM_AVG_WIND_ENERGY, windData.simulationAverage());
              break;

            case SIM_AVG_PRECIP:
              fieldData.put(SIM_AVG_PRECIP, climateData.simulationAverage(PRCP_INDEX));
              break;

            case ANNUAL_OBS_AVG_PRECIP:
              fieldData.put(ANNUAL_OBS_AVG_PRECIP, climateData.observedAnnualAverage(OBS_MONTHLY_PRCP));
              break;

            case MONTHLY_AVG_PRECIP:
              for (int i = 0; i < 12; i++) {
                fieldData.put(MONTHLY_AVG_PRECIP + "_" + (i + 1), climateData.monthlyAverages(i, PRCP_INDEX));
              }
              break;

            case MONTLY_OBS_AVG_PRECIP:
              for (int i = 0; i < 12; i++) {
                fieldData.put(MONTLY_OBS_AVG_PRECIP + "_" + (i + 1), climateData.observedMonthlyAverages(i, OBS_MONTHLY_PRCP));
              }
              break;

            case ANNUAL_WIND_ENEGERY_AVG_NON_ZERO:
              break;

            case ANNUAL_WIND_ENERGY_AVG_PEAK:
              break;

            case MONTHLY_WIND_ENGERY_AVG:
              break;

            case MONTHLY_WIND_ENERGY_AVG_NON_ZERO:
              break;

            case MONTHLY_WIND_ENERGY_AVG_PEAK:
              break;

            default:
              gotAllFields = false;
          }
        }
      }

      if (!gotRequest || !gotSoilsResponse || !gotAllFields || !gotWind || !gotClimate) {
        ArrayList<String> missingList = new ArrayList<>();

        if (!gotRequest) {
          missingList.add(" Request JSON");
        }

        if (!gotSoilsResponse) {
          missingList.add(" Response JSON");
        }

        if (!gotAllFields) {
          missingList.add(" Soils file and matching requested field names");
        }

        if (!gotWind) {
          missingList.add(" Wind file" + ((null != windData) ? (":  " + windData.windDataMessages()) : ""));
        }
        if (!gotClimate) {
          missingList.add(" Climate file" + ((null != climateData) ? (":  " + climateData.cligenDataMessages()) : ""));
        }

        throw new ServiceException("Could not parse all of the service zip file properly: " + filename + ".  " + "Parse isses with: " + missingList);
      }
    }

  }

  protected String readInputFile(ZipInputStream zin) throws IOException {
    String ret_val = "";

    BufferedReader bReader = new BufferedReader(new InputStreamReader(zin));
    StringBuilder fileContent = new StringBuilder();
    String inputStr;

    while ((inputStr = bReader.readLine()) != null) {
      fileContent.append(inputStr + System.lineSeparator());
    }

    ret_val = fileContent.toString();

    return ret_val;
  }

  public double latitude() {
    return latitude;
  }

  public double longitude() {
    return longitude;
  }

  public String cokey() {
    return cokey;
  }

  public void writeCSV(BufferedWriter simAvg, BufferedWriter annualAvg, BufferedWriter monthlyAvg) throws IOException {
    if (null != simAvg) {
      if (!wroteHeader) {
        // Write header
        String header;

        header = buildSimTotalHeader();
        if ((null != header) && (!header.isEmpty())) {
          simAvg.write(header + System.lineSeparator());
          simAvg.flush();
        }

        header = buildAnnualTotalHeader();
        if ((null != header) && (!header.isEmpty())) {
          annualAvg.write(header + System.lineSeparator());
          annualAvg.flush();
        }

        header = buildMonthlyTotalHeader();
        if ((null != header) && (!header.isEmpty())) {
          monthlyAvg.write(header + System.lineSeparator());
          monthlyAvg.flush();
        }

        wroteHeader = true;
      }

      String outString;

      outString = buildSimTotalsLine();
      if ((null != outString) && (!outString.isEmpty())) {
        simAvg.write(outString + System.lineSeparator());
        simAvg.flush();
      }

      outString = buildAnnualTotalsLine();
      if ((null != outString) && (!outString.isEmpty())) {
        annualAvg.write(outString + System.lineSeparator());
        annualAvg.flush();
      }

      outString = buildMonthlyTotalsLine();
      if ((null != outString) && (!outString.isEmpty())) {
        monthlyAvg.write(outString + System.lineSeparator());
        monthlyAvg.flush();
      }
    }
  }

  protected String buildSimTotalHeader() {
    String ret_val = "";

    for (String key : simTotalFields) {
      if (fields.contains(key)) {
        ret_val += ((!ret_val.isEmpty()) ? ", " : "") + key;
      }
    }

    return ret_val;
  }

  protected String buildAnnualTotalHeader() {
    String ret_val = "";

    for (String key : annualAverageFields) {
      if (fields.contains(key)) {
        switch (key) {

          case ANNUAL_AVG_PRECIP:
            for (int i = 0; i < climateData.yearsSimulated(); i++) {
              ret_val += ((!ret_val.isEmpty()) ? ", " : "") + key + "_" + (i + 1);
            }
            break;

          case ANNUAL_WIND_ENERGY_AVG:
          case ANNUAL_WIND_ENEGERY_AVG_NON_ZERO:
          case ANNUAL_WIND_ENERGY_AVG_PEAK:
            for (int i = 0; i < windData.yearsSimulated(); i++) {
              ret_val += ((!ret_val.isEmpty()) ? ", " : "") + key + "_" + (i + 1);
            }
            break;

          default:
            ret_val += ((!ret_val.isEmpty()) ? ", " : "") + key;
        }
      }
    }

    return ret_val;
  }

  protected String buildMonthlyTotalHeader() {
    String ret_val = "";

    for (String key : monthlyAverageFields) {
      if (fields.contains(key)) {
        switch (key) {

          case MONTHLY_AVG_PRECIP:
          case MONTLY_OBS_AVG_PRECIP:
          case MONTHLY_WIND_ENGERY_AVG:
          case MONTHLY_WIND_ENERGY_AVG_NON_ZERO:
          case MONTHLY_WIND_ENERGY_AVG_PEAK:
            for (int i = 0; i < 12; i++) {
              ret_val += ((!ret_val.isEmpty()) ? ", " : "") + key + "_" + (i + 1);
            }
            break;

          default:
            ret_val += ((!ret_val.isEmpty()) ? ", " : "") + key;
        }
      }
    }

    return ret_val;
  }

  protected String buildSimTotalsLine() {
    String ret_val = "";

    for (String key : simTotalFields) {
      if (fields.contains(key)) {
        if (fieldData.containsKey(key)) {
          ret_val += ((!ret_val.isEmpty()) ? ", " : "") + fieldData.get(key);
        }
      }
    }

    return ret_val;
  }

  protected String buildAnnualTotalsLine() {
    String ret_val = "";

    for (String key : annualAverageFields) {
      if (fields.contains(key)) {
        switch (key) {
          case ANNUAL_AVG_PRECIP:
            for (int i = 0; i < climateData.yearsSimulated(); i++) {
              ret_val += ((!ret_val.isEmpty()) ? ", " : "") + fieldData.get(key + "_" + (i + 1));
            }
            break;

          case ANNUAL_WIND_ENERGY_AVG:
          case ANNUAL_WIND_ENEGERY_AVG_NON_ZERO:
          case ANNUAL_WIND_ENERGY_AVG_PEAK:
            for (int i = 0; i < windData.yearsSimulated(); i++) {
              ret_val += ((!ret_val.isEmpty()) ? ", " : "") + key + "_" + (i + 1);
            }
            break;

          default:
            ret_val += ((!ret_val.isEmpty()) ? ", " : "") + fieldData.get(key);
        }
      }
    }

    return ret_val;
  }

  protected String buildMonthlyTotalsLine() {
    String ret_val = "";

    for (String key : monthlyAverageFields) {
      if (fields.contains(key)) {
        switch (key) {
          case MONTHLY_AVG_PRECIP:
          case MONTLY_OBS_AVG_PRECIP:
          case MONTHLY_WIND_ENGERY_AVG:
          case MONTHLY_WIND_ENERGY_AVG_NON_ZERO:
          case MONTHLY_WIND_ENERGY_AVG_PEAK:
            for (int i = 0; i < 12; i++) {
              ret_val += ((!ret_val.isEmpty()) ? ", " : "") + fieldData.get(key + "_" + (i + 1));
            }
            break;

          default:
            ret_val += ((!ret_val.isEmpty()) ? ", " : "") + fieldData.get(key);
        }
      }
    }

    return ret_val;
  }
}