Interpolation.java [src/java/interpolation] Revision: default  Date:
package interpolation;

import csip.Config;
import csip.api.server.Executable;
import csip.api.server.ServiceException;
import csip.SessionLogger;
import static csip.utils.JSONUtils.getValueByKey;
import java.io.BufferedReader;
import java.io.EOFException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.logging.Level;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONObject;

/**
 *
 * @author Brad
 */
public class Interpolation {
    
    protected class StationItem {
        public String stationId;
        public String stationWeight;
        public StationItem(String id, String weight) {
            stationId = id;
            stationWeight = weight;
        }
    }

  Executable interpolateExe;
  Executable interp_wdb;
  File workspaceDir;
  SessionLogger LOG;
  private ArrayList<String> stationNames;

  public Interpolation(Executable interpolate_exe, Executable interp, File workspaceDir, SessionLogger log) {
    interpolateExe = interpolate_exe;
    interp_wdb = interp;
    this.workspaceDir = workspaceDir;
    LOG = log;
  }

  public File interpolate(double parmLat, double parmLon) throws ServiceException, IOException {
    return interpolate(parmLat, parmLon, null, null, null);
  }

  public File interpolate_US_PR(double parmLat, double parmLon, String suffix) throws ServiceException, IOException {
    String parmIdx = Config.getString("csip.dir") + "/data/wind_gen_his_upper_" + suffix + ".idx";

    return interpolate(parmLat, parmLon, parmIdx, null, null);
  }

  public File interpolate(double parmLat, double parmLon, String parmIdxFile, String parmPolygonFile, String parmOutputFile) throws ServiceException, IOException {
    File outputFile;
    ArrayList<String> weightsParms;

    if (interpolateExe == null) {
      throw new ServiceException("no interpolate exe found.");
    }

    if (parmIdxFile == null) {
      parmIdxFile = Config.getString("csip.dir") + "/data/wind_gen_his_upper_US_PR.idx";
    }

    if (parmPolygonFile == null) {
      parmPolygonFile = Config.getString("csip.dir") + "/data/interpolation_boundary.pol";
    }

    if (parmOutputFile == null) {
      parmOutputFile = "weights.txt";
    }

    // NOTE: this service .exe is very precise w/ input formats.
    //       Below is the only working format that I could find. Caveat Lector.
    interpolateExe.setArguments(
        "-f", parmIdxFile,
        "-p", parmPolygonFile,
        "-lat", parmLat,
        "-lon", parmLon,
        "-o", parmOutputFile
    );
    int ret = interpolateExe.exec();
    if (ret != 0) {
      throw new ServiceException("Error executing: " + interpolateExe);
    }

    outputFile = new File(workspaceDir + "/" + parmOutputFile);

    return outputFile;
  }

  public File createInterpolatedWdb(String parmWeightsInFile) throws Exception {
    return createInterpolatedWdb(parmWeightsInFile, null, null, null, null, null, null, null, null, null, null, null);
  }

  public File createInterpolatedWdb_US_PR(String parmWeightsInFile) throws Exception {
    String parmWdb = Config.getString("csip.dir") + "/data/wind_gen_his_upper_US_PR.wdb";
    return createInterpolatedWdb(parmWeightsInFile, parmWdb, null, null, null, null, null, null, null, null, null, null);
  }

  public File createInterpolatedWdb(String parmWeightsInFile,
      String parmDBFileName,
      String getStaDataIdx1, String parmIn1Weight,
      String getStaDataIdx2, String parmIn2Weight,
      String getStaDataIdx3, String parmIn3Weight,
      String parmIn1File,
      String parmIn2File,
      String parmIn3File,
      String parmWdbFile) throws ServiceException, Exception {

    File wdbFile;

    boolean getStaDataFlag = false;

    if (interp_wdb == null) {
      throw new ServiceException("no interp_wdb exe found.");
    }

    if (parmWdbFile == null) {
      parmWdbFile = "test.wdb";
    }

    // The weights and indices to be combined can optionally specified in this input file
    // which the output file from the interpolate endpoint
    if (parmWeightsInFile == null) {
      parmWeightsInFile = "";
    }

    if (parmWeightsInFile.length() > 0) {

      File weightsFile = new File(workspaceDir + "/" + parmWeightsInFile);
      //checkFileExists(weightsFile);
      if (!weightsFile.exists()) {
        throw new ServiceException("File does not exist, filename=" + weightsFile.getName());
      }
      ArrayList<String> weightsParms = parseWeightFile(weightsFile);

      if (weightsParms.size() < 6) {
        if (weightsParms.size() > 0) {
          getStaDataIdx1 = weightsParms.get(0);
          parmIn1Weight = weightsParms.get(1);
          getStaDataIdx2 = weightsParms.get(0);
          parmIn2Weight = weightsParms.get(1);
          getStaDataIdx3 = weightsParms.get(0);
          parmIn3Weight = ((Double) (1.0 - (Double.parseDouble(parmIn1Weight) + Double.parseDouble(parmIn2Weight)))).toString();

        } else {
          throw new ServiceException("Invalid weights file was generated by the interpolation executable.  Expecting 5 entires, but found " + weightsParms.size());
        }
      } else {
        getStaDataIdx1 = weightsParms.get(0);
        parmIn1Weight = weightsParms.get(1);
        getStaDataIdx2 = weightsParms.get(2);
        parmIn2Weight = weightsParms.get(3);
        getStaDataIdx3 = weightsParms.get(4);
        parmIn3Weight = weightsParms.get(5);
      }
      getStaDataFlag = true;

    } else {
      // If no weights file, then weights must be specified
      // If no weights file, indices have 2 choices:
      // 1. specified as parms : stationIdx1
      // 2. implied by the inputFile1 parm, which is the supplied data record for the index

      if (getStaDataIdx1 == null) {
        getStaDataIdx1 = "";
      }

      //checkParmExists("weight1");
      if (parmIn1Weight == null) {
        throw new ServiceException("File does not exist, filename=" + "weight1");
      }

      if (getStaDataIdx2 == null) {
        getStaDataIdx2 = "";
      }

      //checkParmExists("weight2");
      if (parmIn2Weight == null) {
        throw new ServiceException("File does not exist, filename=" + "weight2");
      }

      if (getStaDataIdx3 == null) {
        getStaDataIdx3 = "";
      }

      //checkParmExists("weight3");
      if (parmIn3Weight == null) {
        throw new ServiceException("File does not exist, filename=" + "weight3");
      }
    }

    // If an index is specified, either directly or from the weights file, use it.
    // If an index is specified, then that record must be extracted from the datafile.
    // At this point, if there is no index, then there MUST be an inputFile parm.
    if (getStaDataIdx1.length() > 0) {
      parmIn1File = getStaDataIdx1;
      getStaDataFlag = true;
    } else {
      //checkParmExists("inputFile1", "Must specify either inputFile1 or stationIdx1");
      if (parmIn1File == null) {
        throw new ServiceException("Must specify either inputFile1 or stationIdx1");
      }
    }

    if (getStaDataIdx2.length() > 0) {
      parmIn2File = getStaDataIdx2;
      getStaDataFlag = true;
    } else {
      //checkParmExists("inputFile2", "Must specify either inputFile2 or stationIdx2");
      if (parmIn2File == null) {
        throw new ServiceException("Must specify either inputFile2 or stationIdx2");
      }
    }

    if (getStaDataIdx3.length() > 0) {
      parmIn3File = getStaDataIdx3;
      getStaDataFlag = true;
    } else {
      //checkParmExists("inputFile3", "Must specify either inputFile3 or stationIdx3");
      if (parmIn3File == null) {
        throw new ServiceException("Must specify either inputFile3 or stationIdx3");
      }
    }

    //
    // Enter here if we need to get station data.
    // I.e. all 3 station data records (inputFile1,2,3) have not been specified.
    //
    if (getStaDataFlag) {

      // The data can come from one of 3 sources:
      // 1. The internal database include with this endpoint
      // 2. A supplied data file included with this request
      // 3. Specified as individual file records with the inputFile1.2.3 parms
      //if ( hasParam(PARMITEMDATAFILE) ) {
      if (parmDBFileName == null) {
        parmDBFileName = Config.getString("csip.dir") + "/data/wind_gen_his_upper_US.wdb";
      }

      getStationData(getStaDataIdx1, getStaDataIdx2, getStaDataIdx3, parmDBFileName);
    }

    //checkFileExists(workspace().getDir() + "/" + parmIn1File);
    if (!(new File(workspaceDir + "/" + parmIn1File).exists())) {
      throw new ServiceException("File does not exist, filename=" + parmIn1File);
    }
    //checkFileExists(workspace().getDir() + "/" + parmIn2File);
    if (!(new File(workspaceDir + "/" + parmIn2File).exists())) {
      throw new ServiceException("File does not exist, filename=" + parmIn2File);
    }
    //checkFileExists(workspace().getDir() + "/" + parmIn3File);
    if (!(new File(workspaceDir + "/" + parmIn3File).exists())) {
      throw new ServiceException("File does not exist, filename=" + parmIn3File);
    }

    interp_wdb.setArguments(parmWdbFile,
        parmIn1File, parmIn1Weight,
        parmIn2File, parmIn2Weight,
        parmIn3File, parmIn3Weight
    );
    int ret = interp_wdb.exec();
    if (ret != 0) {
      throw new ServiceException("Error executing " + interp_wdb.toString());
    }

    wdbFile = new File(workspaceDir + "/" + parmWdbFile);
    return wdbFile;
  }

  
    public File createInterpolatedWdbNew(String parmDBFileName,
                                         JSONArray parmWeightsData,
                                         String parmWdbFile
                                        ) throws ServiceException, Exception {

        File wdbFile;

        ArrayList<StationItem> stationData = getStationDataNew(parmWeightsData, parmDBFileName);

        interp_wdb.setArguments(parmWdbFile);
        for (StationItem dataItem : stationData) {
            interp_wdb.addArguments(dataItem.stationId, dataItem.stationWeight);
        }
        int ret = interp_wdb.exec();
        if (ret != 0) {
            throw new ServiceException("Error executing " + interp_wdb.toString());
        }

        wdbFile = new File(workspaceDir + "/" + parmWdbFile);
        return wdbFile;
    }

        
  //
  // Source for this routine is : usda.weru.util.windgen.MakeInterpolatedStation.run() (line:288)
  // note: possiblie Linux / Windows issues?
  //
  public ArrayList<String> parseWeightFile(File weightsInputFile) throws ServiceException {

    BufferedReader br = null;
    String inpLin = null;
    ArrayList<String> stationsList;

    stationsList = new ArrayList();

    try {
      br = new BufferedReader(new FileReader(weightsInputFile));
    } catch (FileNotFoundException ex) {
      if (LOG.isLoggable(Level.INFO)) {
        LOG.info("parseWeightFile : " + "IO Error (on new Reader): " + weightsInputFile.getAbsolutePath());
      }
      throw new ServiceException(ex);
    }

    try {
      do {
        try {
          inpLin = br.readLine();
          if (inpLin != null) {
            inpLin = inpLin.trim();
            if (!inpLin.startsWith("#")) {
              String[] inpArr = inpLin.split(" +");
              if (inpArr.length == 2) {
                stationsList.add(inpArr[0]);
                stationsList.add(String.valueOf(inpArr[1]));
              }
            }
          }
        } catch (IOException ex) {
          if (LOG.isLoggable(Level.INFO)) {
            LOG.info("parseWeightFile : " + "IO Error (on readLine): " + weightsInputFile.getAbsolutePath());
          }
          inpLin = null;
        }
      } while (inpLin != null);
    } finally {
      try {
        br.close();
      } catch (IOException e) {
        if (LOG.isLoggable(Level.INFO)) {
          LOG.info("parseWeightFile : " + "Unable to close stream: " + weightsInputFile.getAbsolutePath());
        }
      }
      if (stationsList.size() != 6) {
        if (LOG.isLoggable(Level.INFO)) {
          LOG.info("parseWeightFile : " + "Incorrect result size, should be 6: " + stationsList.size());
        }
      }
    }
    return stationsList;
  }

  protected void getStationData(String stationName1, String stationName2, String stationName3, String parmDBFileName) throws Exception {
    //String parmDBFileName = csipDirName + "/d/windgenData/" + "wind_gen_his_upper_US.wdb";

    if (parmDBFileName.length() > 0) {
      File parmDBFile = new File(parmDBFileName);
      //checkFileExists (parmDBFile);
      if (!parmDBFile.exists()) {
        throw new ServiceException("File does not exist, filename=" + parmDBFile.getName());
      }

      try {
        extractStationFromWDB(parmDBFile, stationName1, stationName2, stationName3);
      } catch (IOException ex) {
        LOG.log(Level.SEVERE, null, ex);
      }
    } else {
      if (LOG.isLoggable(Level.INFO)) {
        LOG.info("getStationData : " + "Need to get Station data and no file specified");
      }
      throw new ServiceException("Station database name is null in \"CSIP windgen interp_wdb");
    }
  }

    protected ArrayList<StationItem> getStationDataNew(JSONArray stationData, String parmDBFileName) throws Exception {
        JSONArray stationDataItem;

        ArrayList<StationItem> stationArray = new ArrayList<>();
        ArrayList<String> stationIdArray = new ArrayList<>();

        if (parmDBFileName.length() > 0) {
            File parmDBFile = new File(parmDBFileName);

            if (!parmDBFile.exists()) {
                throw new ServiceException("Interpolation data file does not exist, filename=" + parmDBFile.getName());
            }
            
            for (int i = 0; i < stationData.length(); i++) {
                JSONObject o;
                stationDataItem = stationData.getJSONArray(i);

                String id = getValueByKey(stationDataItem, "stationId");
                stationArray.add(new StationItem(id, getValueByKey(stationDataItem, "stationWeight")));
                if (!stationIdArray.contains(id)) {
                    // this is for the extract function.
                    // It only needs to extract each record once.
                    stationIdArray.add(id);
                }
            }
            
            try {
                extractStationFromWDBNew(parmDBFile, stationIdArray);
            } catch (IOException ex) {
                LOG.log(Level.SEVERE, null, ex);
                throw new ServiceException("Station data extractionl in \"CSIP windgen interp_wdb", ex);
            }
        }
        return stationArray;
    }

  //
  // copied verbatum from usda.weru.uti.windgen.ExtractStationFromWdb.java
  // (one change: TFile to File).
  // (next change: copied the 
  //                 if (stat.equals(inpArr[1])) { 
  //                 block 3 times so that we can search for all 3 records with one pass through the data file)
  // (next change: added the
  //                 if (stat2.length() == 0 && stat3.length() == 0) {
  //                 check so that we only break after all 3 stations are found.
  //
    
    // The 2 extractStationFromWDB routines extract the station records out of the
    // specified wdb file and store them each in files in the CSIP working directory
    // with the name of the file being the record id value.
    // Those files, then, are read in by the interp_wdb function.
    
  //
  // one less than actual since 1st is already in inpLin
  //
  static int linesToCopy = 212;

  protected void extractStationFromWDB(File wdb, String stat1, String stat2, String stat3) throws FileNotFoundException, IOException {
    BufferedReader bfr = new BufferedReader(new FileReader(wdb), 32000);  // each record is aprox. 20k
    while (true) {
      String inpLin = bfr.readLine();
      if (inpLin == null) {
        bfr.close();
        throw new EOFException("station not found");
      }
      if (inpLin.length() > 10) {
        String[] inpArr = inpLin.substring(0, 10).split(" ");
        if ("#".equals(inpArr[0])) {
//                    System.out.println("inplin " + inpLin);
          if (stat1.equals(inpArr[1])) {
            try (PrintWriter pw = new PrintWriter(new File(workspaceDir, stat1))) {
              pw.println(inpLin);
              for (int idx = 0; idx < linesToCopy; idx++) {
                inpLin = bfr.readLine();
                pw.println(inpLin);
              }
            }
            stat1 = "";
            if (stat2.length() == 0 && stat3.length() == 0) {
              bfr.close();
              break;
            }
          } else if (stat2.equals(inpArr[1])) {
            try (PrintWriter pw = new PrintWriter(new File(workspaceDir, stat2))) {
              pw.println(inpLin);
              for (int idx = 0; idx < linesToCopy; idx++) {
                inpLin = bfr.readLine();
                pw.println(inpLin);
              }
            }
            stat2 = "";
            if (stat1.length() == 0 && stat3.length() == 0) {
              bfr.close();
              break;
            }
          } else if (stat3.equals(inpArr[1])) {
            try (PrintWriter pw = new PrintWriter(new File(workspaceDir, stat3))) {
              pw.println(inpLin);
              for (int idx = 0; idx < linesToCopy; idx++) {
                inpLin = bfr.readLine();
                pw.println(inpLin);
              }
            }
            stat3 = "";
            if (stat1.length() == 0 && stat2.length() == 0) {
              bfr.close();
              break;
            }
          }
        }
      }
    }
  }

    protected void extractStationFromWDBNew(File wdb, ArrayList<String> stationIds) throws FileNotFoundException, IOException {
 
        int stationCnt = stationIds.size();

        BufferedReader bfr = new BufferedReader(new FileReader(wdb), 32000);  // each record is aprox. 20k

        while (true) {
            String inpLin = bfr.readLine();
            if (inpLin == null) {
                bfr.close();
                throw new EOFException("station not found");
            }
            if (inpLin.length() > 10) {
                String[] inpArr = inpLin.substring(0, 10).split(" ");
                if ("#".equals(inpArr[0])) {
                    if (stationIds.contains(inpArr[1])) {
                        try (PrintWriter pw = new PrintWriter(new File(workspaceDir, inpArr[1]))) {
                            pw.println(inpLin);
                            for (int idx = 0; idx < linesToCopy; idx++) {
                                inpLin = bfr.readLine();
                                pw.println(inpLin);
                            }
                            pw.close();
                        }
                        if (--stationCnt == 0) {
                            bfr.close();
                            break;
                        }
                    } else {
                        // Don't need to check for # on every line.
                        for (int idx = 0; idx < linesToCopy; idx++) {
                            inpLin = bfr.readLine();
                        }
                        
                    }
                }
            }
        }
    }  
  
}