<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">
package usda.weru.weps.wepsRunControl;

import csip.ServiceException;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.Charset;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.openide.util.Exceptions;

/**
 *
 * @author mhaas
 */

//
// These routines are all taken from CSIP-climate.
// This is the CSIP code that adds prism data to the cligen data record.
// It is copied here for consistency.
// It is only used in "off-line mode" when we desire to add in 
// locally downloaded Prism data and augment the local cligen model run with the Prism data.
//


//
// from CSIP-climate CligenWepsUtils.java
//
public class CligenWepsUtils {
    
    File workspaceDir;
    File ExtractedCliRecFile;
    
    public CligenWepsUtils (double lat, double lon, File workspaceDir ) {
        
        // MEH additions to original
        latitude = lat;
        longitude = lon;
        ppt = new double[13]; 
        tmax = new double[13];
        tmin = new double[13];
        // MEH end
        
        ExtractedCliRecFile = null;
        this.workspaceDir = workspaceDir;
    }
    
    
    public String extractClimateRecord (String parFileName, int stateId, int stationId) throws ServiceException {
        
        BufferedReader lineReader = null;
//        String idxFileName = Config.getString("csip.dir") + "/d/cligenData/" + "upd_US_cligen_stations.idx";
//        String idxFileName = Config.getString("csip.dir") + "/data/" + "upd_US_cligen_stations.idx";
        String idxFileName = parFileName.replace(".par", ".idx");
        File idxFile = new File (idxFileName);
        String lineStr;
        String[] lineParts;
        long parIdxStart;
        long parIdxEnd;
        
        try {
            lineReader = new BufferedReader(new FileReader(idxFile));
            
            String stateStr = String.format("%1$2d", stateId);
            String stationStr = String.format("%1$7d", stationId);
            
            // look in the idx file for the line that contains this state and station
            String srchStr = stateStr + stationStr;
            while ((lineStr = lineReader.readLine()) != null) {
                if (lineStr.contains(srchStr)) {
                    break;
                }
            }
            if (lineStr == null) {
                throw new ServiceException("Error reading index file, state and/or station not found," + 
                                           " filename=" + idxFileName +
                                           " state=" + stateId +
                                           " station=" + stationId);
            }
            
            // need to trim first so because single digits have a space before them. e.g. " 5"
            // get the index pointer from this line (the 3rd element on the line) which is an index into the par file
            lineParts = lineStr.replaceAll("\\s+", " ").trim().split(" ");
            parIdxStart = Long.decode(lineParts[2]);
            
            // get the next line in the idx file and get its index pointer as the pointer to the end of the desired record.
            if ((lineStr = lineReader.readLine()) != null) {
                lineParts = lineStr.replaceAll("\\s+", " ").trim().split(" ");
                parIdxEnd = Long.decode(lineParts[2]);
            } else {
                parIdxEnd = idxFile.length();
            }
            lineReader.close();
            
            // Using the pointers, extract the specified single record element out of the par file
            ExtractedCliRecFile = new File (workspaceDir, "cligenRecord.par");
            BufferedWriter outputWriter = new BufferedWriter(new FileWriter(ExtractedCliRecFile));
            lineReader = new BufferedReader(new FileReader(parFileName));
            lineReader.skip(parIdxStart);
            long count = parIdxStart;
            while ((lineStr = lineReader.readLine()) != null) {
                outputWriter.write(lineStr);
                outputWriter.newLine();
                count += lineStr.length() + 1;
                if (count &gt;= parIdxEnd) {
                    break;
                }
            }
            lineReader.close();
            outputWriter.close();
            
            if (count &lt; parIdxEnd) {
                throw new ServiceException("Error writing par record file, filename=" + ExtractedCliRecFile.getAbsolutePath());
            }
            
        } catch (FileNotFoundException ex) {
            throw new ServiceException("Index file does not exist, filename=" + idxFileName);
        } catch (IOException ex) {
            throw new ServiceException("Error reading index file, filename=" + idxFileName);
        }
       
        return ExtractedCliRecFile.getPath();
    }
    
    
    //
    // from CSIP-climate WeppClimate.java
    //
   
    
    double latitude, longitude;
    String outputCliFile;
    String cligenIndexFile;
    String workingDir;
    String baseParData;
    double ppt_annual;
    double ppt_annual_base;
    double[] ppt; 
    double[] tmax;
    double[] tmin;
    double MM_IN = 25.4;
    double C_F = 1.8;
    String stationName;
    //
    // adjustPARFile()
    //
    // Using the base CLIGEN PAR file and the PRISM values modify the preciptation,
    // tmax and tmin lines of the new PAR file.
    // If the location is not in the ppt, tmax and tmin grids then the adjustments
    // are not done. This is determined by the annual precip being &lt; 0 
    //
    public boolean adjustPARFile(String prismPar) throws Exception {
        
        double [] pww = new double[12];  // probabilty of wet day following wet day
        double [] pwd = new double[12];  // probabilty of wet day following dry day
        double [] nwd = new double[12];  // calculated number of wet days in month
        double [] p = new double[12];    // original precip by month
       
        ppt_annual_base = 0;
        
        String [] lines = baseParData.split("\n");
        // PAR lines are fixed format - need exact spacing.
        // see https://www.ars.usda.gov/ARSUserFiles/50201000/WEPP/cligen/cligenparms.pdf
        int [] spacing = new int[]{41,2,4};
        String [] cols = splitLine(lines[0],spacing);
        String name = cols[0];
        StringBuilder builder = new StringBuilder(name);
        
        // add a "+" sign in the name
        int index = StringUtils.indexOfAnyBut(name, " ");

        if (index &gt; 0) {
            builder.setCharAt(index-1,'+');
        }
        name = builder.toString();
        lines[0] = name.substring(0,41) + lines[0].substring(41);
        
        // change latitude and longitude
        spacing = new int[]{6,7,6,7,7,3,7,2};
        cols = splitLine(lines[1],spacing);
        cols[1] = String.format("%7.2f", latitude);
        cols[3] = String.format("%7.2f", longitude);
        lines[1] = makeString(cols);
  
        spacing = new int[]{8,6,6,6,6,6,6,6,6,6,6,6,6};
        // probability of wet day following a wet day
        cols = splitLine(lines[6],spacing);
        for (int i=1;i&lt;=12;i++) {
            pww[i-1] = Double.parseDouble(cols[i]);
        }
        
        // probabilty of a wet day following a dry day
        cols = splitLine(lines[7],spacing);
        for (int i=1;i&lt;=12;i++) {
            pwd[i-1] = Double.parseDouble(cols[i]);
        }
        
        // average precip on wet days
        cols = splitLine(lines[3],spacing);
        for (int i=1;i&lt;=12;i++) {
            p[i-1] = Double.parseDouble(cols[i]);
        }
        
        int month;
        int mdays;
        double pdly;
        for (int i=0;i&lt;12;i++) {
           month = i+1;
           if (month == 2) {
               mdays = 28;
           } else if ((month == 4) || (month == 6) || (month == 9) || (month == 11)) {
               mdays = 30;
           } else {
              mdays = 31;
           }
           // calc number of wet days based on probabilities
           nwd[i] = (mdays*(pwd[i]/(1.0-pww[i]+pwd[i])));
           // precip amount on wet days
           if (ppt_annual &gt; 0) {
                if (nwd[i] &gt; 0) {
                    pdly = ppt[i] / nwd[i];
                } else {
                    pdly = 0.01;
                }
                cols[i+1] = String.format("%6.2f", pdly);  // update new precip amount
           }
           ppt_annual_base = ppt_annual_base + (p[i] * nwd[i]); // original base station
        }
        lines[3] = makeString(cols);

        if (ppt_annual &gt; 0) {
            // replace tmax values
            cols = splitLine(lines[8],spacing);
            for (int i=1;i&lt;=12;i++) {
                cols[i] = String.format("%6.2f", tmax[i-1]);
            }
            lines[8] = makeString(cols);

            // replace tmin values
            cols = splitLine(lines[9],spacing);
            for (int i=1;i&lt;=12;i++) {
                cols[i] = String.format("%6.2f", tmin[i-1]);
            }
            lines[9] = makeString(cols);
        }
        
        StringBuilder builder2 = new StringBuilder();
        for(String s : lines) {
            builder2.append(s);
            builder2.append("\n");
        }

        FileUtils.writeStringToFile(new File(prismPar),builder2.toString(), (Charset)null, true);
        return true;
    }

   
    //
    // splitLine()
    //
    // Breaks a string into fixed format columns.
    //
    private String[] splitLine(String line, int[] columnLengths) {
        String[] columns = new String[columnLengths.length];
        int currentIndex = 0;
        for(int i = 0; i &lt; columnLengths.length; i++) {
                columns[i] = line.substring(currentIndex, currentIndex += columnLengths[i]);
        }
        return columns;
    }
    
    //
    // makeString()
    //
    // Puts an array of fixed format string back into a single line.
    //
    private String makeString(String []cols) {
        StringBuilder builder = new StringBuilder();
        for (int i=0;i&lt;cols.length;i++) {
            builder.append(cols[i]);
        }
       return builder.toString();
    }
    
    
    
    
    //
    // from CSIP-climate CligenPrism.java
    //

    
    String ExtractedCliRecPrismFileName;

    public String addPrismDataToRecord (File ExtractedCliRecFile, JSONArray periodData) throws ServiceException {
        ExtractedCliRecPrismFileName = workspaceDir + "/" + "cligenRecordPrism.par";
        
        getPRISMdata(periodData);
         
        File prismFile = null;
        try {
           prismFile = new File (workspaceDir + "/" + "prismData.txt");
           FileUtils.writeStringToFile(prismFile, periodData.toString(4), (Charset)null, true);
        } catch (JSONException ex) {
            throw new ServiceException("Error formatting prism data, data=" + (periodData == null ? "" : periodData));
        } catch (IOException ex) {
            throw new ServiceException("Error writing prism data file, filename=" + (prismFile == null ? "" : prismFile.getAbsolutePath()));
        }
        
        try {
            baseParData = FileUtils.readFileToString(ExtractedCliRecFile, (Charset)null);
            adjustPARFile (ExtractedCliRecPrismFileName);
        } catch (IOException ex) {
            throw new ServiceException("Error reading in par file, filename=" + ExtractedCliRecFile);
        } catch (Exception ex) {
            throw new ServiceException("Error adjusting par file, filename=" + ExtractedCliRecFile);
        }
        
        return ExtractedCliRecPrismFileName;
    }

    void getPRISMdata (JSONArray periodData) throws ServiceException {
        try {
            JSONObject periodObj;
            for (int i=0; i&lt;12; i++) {
                periodObj = periodData.getJSONObject(i);
                // check
                if (periodObj.getInt("period") != i+1) {
                    throw new ServiceException("Unexpected monthly record in prism data, index=" + i);
                }

                ppt[i] = periodObj.getDouble("ppt") / MM_IN;  // mm to in
                tmin[i] = periodObj.getDouble("tmin");
                tmin[i] = (tmin[i] * C_F) + 32.0;  // Convert from C to F 
                tmax[i] = periodObj.getDouble("tmax");
                tmax[i] = (tmax[i] * C_F) + 32.0;  // Convert from C to F 
            }
            
            periodObj = periodData.getJSONObject(12);
            // check
            if (periodObj.getInt("period") != 13) {
                throw new ServiceException("Unexpected monthly record in prism data, index=" + 12);
            }
            ppt_annual = periodObj.getDouble("ppt") / MM_IN;  // mm to in
        } catch (JSONException ex) {
            throw new ServiceException("Unexpected JSON error reading PRISM values in getPRISMdata");
        }
    }    
}
</pre></body></html>