CDWR_Data.java [src/java/cfa] Revision: 6ce728f6fd4170655c0b164feee5263a81ef1fde  Date: Mon Sep 15 10:58:21 MDT 2014
package cfa;

import java.util.ArrayList;
import java.util.Iterator;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.MimeHeaders;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPBodyElement;
import javax.xml.soap.SOAPConnection;
import javax.xml.soap.SOAPConnectionFactory;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;

/**
 * Last Updated: 11-July-2014
 * @author Tyler Wible
 * @since 7-July-2014
 * For more information on all of CDWR's water services, go to:
 * http://www.dwr.state.co.us/SurfaceWater/ColoradoWaterSMS.pdf
 */
public class CDWR_Data {
    public static final String serverURI = "http://www.dwr.state.co.us/";
    public static final String waterSMSURI = "http://www.dwr.state.co.us/SMS_WebService/ColoradoWaterSMS.asmx";
    /**
     * Finds discharge data for the specified station and start/end dates out of the Colorado Division of Water Resource Database
     * @param stationID  the abbreviation of the name for the CDWR station
     * @param beginDate  the user specified begin date for the station (yyyy-MM-dd format)
     * @param endDate  the user specified end date for the station (yyyy-MM-dd format)
     * @param dataType  a flag for extraction of either "hourly" or "daily" flow data from CDWR
     * @return  a String[][] containing column1 = date(if dataType = "daily": yyyy-MM-dd format, if dataType = "hourly": yyyy-MM-dd HH:mm format), column2 = flowValue
     * @throws Exception 
     */
    public Object[] getCDWRflowData(String stationID, String beginDate, String endDate, String dataType) throws Exception {
        // Create SOAP Connection
        SOAPConnectionFactory soapConnectionFactory = SOAPConnectionFactory.newInstance();
        SOAPConnection soapConnection = soapConnectionFactory.createConnection();

        // Send SOAP Message to SOAP Server
        SOAPMessage dataMessage = GetSMSProvisionalData(stationID, "DISCHRG", beginDate, endDate, dataType);
        SOAPMessage dataResponse = soapConnection.call(dataMessage, waterSMSURI);
        //System.out.print("Station Data:");
        //dataResponse.writeTo(System.out);

        SOAPBody replyBody = dataResponse.getSOAPBody();
        Iterator<?> iter1 = replyBody.getChildElements();
        ArrayList<String> stationData = new ArrayList<String>();
        while (iter1.hasNext()) {
            //Open <GetSMSTransmittingStationsResponse>
            SOAPBodyElement bodyElement = (SOAPBodyElement)iter1.next();
            Iterator<?> iter2 = bodyElement.getChildElements();
            while (iter2.hasNext()) {
                //Open <GetSMSTransmittingStationsResult>
                SOAPElement child2 = (SOAPElement)iter2.next();
                Iterator<?> iter3 =  child2.getChildElements();
                while (iter3.hasNext()) {
                    //Open <Station>
                    SOAPElement child3 = (SOAPElement)iter3.next();
                    Iterator<?> iter4 = child3.getChildElements();
                    //Hopefully ever station has 7 attributes (div, wd, abbrev, variable, transDateTime, resultCount, amount)
                    SOAPElement child4_0 = (SOAPElement)iter4.next();
                    SOAPElement child4_1 = (SOAPElement)iter4.next();
                    SOAPElement child4_2 = (SOAPElement)iter4.next();
                    SOAPElement child4_3 = (SOAPElement)iter4.next();
                    SOAPElement child4_4 = (SOAPElement)iter4.next();
                    SOAPElement child4_5 = (SOAPElement)iter4.next();
                    SOAPElement child4_6 = (SOAPElement)iter4.next();
                    
                    //if the result is not null or -999, keep it
                    if(!child4_5.getValue().equalsIgnoreCase(null) && !child4_5.getValue().equalsIgnoreCase("-999")){
                        stationData.add(child4_0.getValue() + "\t" +
                                        child4_1.getValue() + "\t" +
                                        child4_2.getValue() + "\t" +
                                        child4_3.getValue() + "\t" +
                                        child4_4.getValue() + "\t" +
                                        child4_5.getValue() + "\t" +
                                        child4_6.getValue() + "\t");
                    }
//                    while (iter4.hasNext()) {
//                        SOAPElement child4 = (SOAPElement)iter4.next();
//                        String content = child4.getValue();
//                        System.out.println(content);
//                    }
                }
            }
        }
        soapConnection.close();
        
        //Reformat data
        String[][] stringArray = new String[stationData.size()][2];
        for(int i=0; i<stationData.size(); i++){
            String[] currentStationData = stationData.get(i).split("\t");
            //Pull out only the data needed to pass between sub-functions
            stringArray[i][0] = reformatDate(currentStationData[4], dataType);//date
            stringArray[i][1] = currentStationData[5];//value
        }
        
        //Add header to stationData and shift all other data in the list down one index (+1)
        stationData.add(0, "div\twd\tStationNameAbbrev\tvariable\tDateTime\tvalue\tNumberOfTransmissionsAggregatedTo" + dataType + "Data");
        
        //Save analysis results
        String start = "-1";
        String end = "-1";
        if(stringArray.length > 0){
            start = stringArray[0][0];
            end = stringArray[stringArray.length - 1][0];
        }
        
        Object[] returnArray = {stationData, stringArray, start, end};
        return returnArray;
    }
    /**
     * Creates a SOAP Call to CDWR for "GetSMSProvisionalData" for the specified inputs
     * @param station  the SEO abbreviation for the CDWR station
     * @param variable  the variable desired from this station (ex. "DISCHRG", "GAGE_HT", "AIR_TEMP")
     * @param beginDate  the start date of data desired (yyyy-mm-dd)
     * @param endDate  the end date of date desired (yyyy-mm-dd)
     * @param aggregation  the aggregation of data desired (ex. "Hourly", "Daily", "15-min")
     * @return the response SOAP message from CDWR
     * @throws Exception 
     */
    public static SOAPMessage GetSMSProvisionalData(String station, String variable, String beginDate, String endDate, String aggregation) throws Exception {
        MessageFactory messageFactory = MessageFactory.newInstance();
        SOAPMessage soapMessage = messageFactory.createMessage();
        SOAPPart soapPart = soapMessage.getSOAPPart();

        // SOAP Envelope
        SOAPEnvelope envelope = soapPart.getEnvelope();
        envelope.addNamespaceDeclaration("dwr", serverURI);

        /*
        Constructed SOAP Request Message:
        <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:dwr="http://www.dwr.state.co.us/">
		   <soapenv:Header/>
		   <soapenv:Body>
		      <dwr:GetSMSProvisionalData>
		         <!--Optional:-->
		         <dwr:Abbrev>CLAGRECO</dwr:Abbrev>
		         <!--Optional:-->
		         <dwr:Variable>DISCHRG</dwr:Variable>
		         <!--Optional:-->
		         <dwr:StartDate>2013-06-05</dwr:StartDate>
		         <!--Optional:-->
		         <dwr:EndDate>2013-06-06</dwr:EndDate>
		         <!--Optional:-->
		         <dwr:Aggregation>Hourly</dwr:Aggregation>
		      </dwr:GetSMSProvisionalData>
		   </soapenv:Body>
		</soapenv:Envelope>
         */

        // SOAP Body
        SOAPBody soapBody = envelope.getBody();
        SOAPElement soapBodyMethod = soapBody.addChildElement("GetSMSProvisionalData", "dwr");
        SOAPElement abbrev = soapBodyMethod.addChildElement("Abbrev", "dwr");
        abbrev.addTextNode(station);
        SOAPElement soapVariable = soapBodyMethod.addChildElement("Variable", "dwr");
        soapVariable.addTextNode(variable);
        SOAPElement startDate_soap = soapBodyMethod.addChildElement("StartDate", "dwr");
        startDate_soap.addTextNode(beginDate);
        SOAPElement endDate_soap = soapBodyMethod.addChildElement("EndDate", "dwr");
        endDate_soap.addTextNode(endDate);
        if(!aggregation.equalsIgnoreCase("15-min")){
            SOAPElement soapAggregation = soapBodyMethod.addChildElement("Aggregation", "dwr");
            soapAggregation.addTextNode(aggregation);
        }

        MimeHeaders headers = soapMessage.getMimeHeaders();
        headers.addHeader("SOAPAction", serverURI  + "GetSMSProvisionalData");

        soapMessage.saveChanges();

        // Print the request message
        //System.out.print("Request SOAP Message:");
        //soapMessage.writeTo(System.out);
        //System.out.println();

        return soapMessage;
    }
    /**
     * Reformats the date from CDWR's "m/d/yyyy hh:mm am/pm" format to yyyy-MM-dd or yyyy-MM-dd HH:mm depending on the value of dataType
     * @param date the current date 
     * if dataType = "daily" the format is "m/d/yyyy hh:mm am/pm" 
     * if datatype = "hourly" the format is "m/d/yyyy HH:mm"
     * @param dataType  the type of date format desired either "daily" or "hourly"
     * @return the date parsed into one of the following date formats:
     * if dataType = "daily" the format is "yyyy-MM-dd" 
     * if datatype = "hourly" the format is "yyyy-MM-dd HH:mm"
     */
    private String reformatDate(String date, String dataType){
        String newDate = "1900-01-01";
        
        //Get portions of date
        int index1 = date.indexOf("/");
        int index2 = date.indexOf("/", index1 + 1);
        int index3 = date.indexOf(" ");
        String month = date.substring(0, index1);
        String day = date.substring(index1 + 1, index2);
        String year = date.substring(index2 + 1, index3);
        //check month size
            if(month.length() < 2){
                month = "0" + month;
            }
            //check day size
            if(day.length() < 2){
                day = "0" + day;
            }
        //Create new date format
        if(dataType.equalsIgnoreCase("Daily")){
            //Assemble new date format
            newDate = year + "-" + month + "-" + day;
            
        }else if(dataType.equalsIgnoreCase("Hourly") || dataType.equalsIgnoreCase("15-min")){
            //Hourly data returns as m/d/yyyy HH:mm which matches
            //USGS 15-minute data is zero-based military time (24hr day from 00:00 to 23:00)
            int index4 = date.indexOf(":");
            int index5 = date.indexOf(":", index4 + 1);
            int index6 = date.indexOf(" ", index3 + 1);
            String hour = date.substring(index3 + 1, index4);
            String minute = date.substring(index4 + 1, index5);
            String amPM = date.substring(index6 + 1);
            
            //Convert AM/PM
            int hour_int = (int) Double.parseDouble(hour);
            if(amPM.equalsIgnoreCase("PM")){
                hour = String.valueOf(hour_int-1 + 12);
            }else{
                hour = String.valueOf(hour_int-1);
            }
            //check hour size
            if(hour.length() < 2){
                hour = "0" + hour;
            }
            //check minute size
            if(minute.length() < 2){
                minute = "0" + minute;
            }
            newDate = year + "-" + month + "-" + day + " " + hour + ":" + minute;
        }
        return newDate;
    }
    /**
     * Finds rating curve data for the specified station from the Colorado Division of Water Resource Database
     * @param stationID  the abbreviation of the name for the CDWR station
     * @return  a double[][] containing column1 = discharge(ft3/s), column2 = depth(ft)
     * @throws Exception 
     */
    public Object[] getCDWRratingCurve(String stationID) throws Exception {
        // Create SOAP Connection
        SOAPConnectionFactory soapConnectionFactory = SOAPConnectionFactory.newInstance();
        SOAPConnection soapConnection = soapConnectionFactory.createConnection();

        // Send SOAP Message to SOAP Server
        SOAPMessage dataMessage = GetSMSCurrentRatingTable(stationID);
        SOAPMessage dataResponse = soapConnection.call(dataMessage, waterSMSURI);
        //System.out.print("Rating Curve Data:");
        //dataResponse.writeTo(System.out);
        
        SOAPBody replyBody = dataResponse.getSOAPBody();
        Iterator<?> iter1 = replyBody.getChildElements();
        ArrayList<String> stationData = new ArrayList<String>();
        while(iter1.hasNext()){
            //Open <GetSMSCurrentRatingTableResponse >
            SOAPBodyElement bodyElement = (SOAPBodyElement)iter1.next();
            //System.out.println(bodyElement.getValue());
            Iterator<?> iter2 = bodyElement.getChildElements();
            while(iter2.hasNext()){
                //Open <GetSMSCurrentRatingTableResult>
                SOAPElement child2 = (SOAPElement)iter2.next();
                //System.out.println(child2.getValue());
                Iterator<?> iter3 =  child2.getChildElements();
                while(iter3.hasNext()){
                    //Open <tableName>
                    SOAPElement child3 = (SOAPElement)iter3.next();
                    //System.out.println(child3.getValue());
                    //Open <tablePoints>
                    SOAPElement child3b = (SOAPElement)iter3.next();
                    //System.out.println(child3b.getValue());
                    Iterator<?> iter4 = child3b.getChildElements();
                    while(iter4.hasNext()){
                        //Open <RatingTablePoints>
                        SOAPElement child4 = (SOAPElement)iter4.next();
                        //System.out.println(child4.getValue());
                        Iterator<?> iter5 = child4.getChildElements();
                        //Hopefully RatingTablePoint only has 2 attributes (x, y)
                        SOAPElement child5x = (SOAPElement)iter5.next();
                        SOAPElement child5y = (SOAPElement)iter5.next();
                        //System.out.println(child5x.getValue());
                        //System.out.println(child5y.getValue());
                        stationData.add(child5y.getValue() + "\t" + child5x.getValue());//the discharge value followed by the stage value
                    }
                }
            }
        }
        soapConnection.close();
        
        //Reformat data
        double[][] stringArray = new double[stationData.size()][2];
        for(int i=0; i<stationData.size(); i++){
            String[] currentStationData = stationData.get(i).split("\t");
            //currentStationData[0] = discharge
            //currentStationData[1] = stage
            stringArray[i][0] = Double.parseDouble(currentStationData[0]);
            stringArray[i][1] = Double.parseDouble(currentStationData[1]);
        }
        
        //Add header to stationData and shift all other data in the list down one index (+1)
        stationData.add(0, "x\ty");
        
        Object[] returnArray = {stationData, stringArray};
        return returnArray;
    }
    /**
     * Creates a SOAP Call to CDWR for "GetSMSCurrentRatingTable" for the specified inputs
     * @param station  the SEO abbreviation for the CDWR station
     * @return the response SOAP message from CDWR
     * @throws Exception 
     */
    private SOAPMessage GetSMSCurrentRatingTable(String station) throws Exception {
        MessageFactory messageFactory = MessageFactory.newInstance();
        SOAPMessage soapMessage = messageFactory.createMessage();
        SOAPPart soapPart = soapMessage.getSOAPPart();

        // SOAP Envelope
        SOAPEnvelope envelope = soapPart.getEnvelope();
        envelope.addNamespaceDeclaration("dwr", serverURI);

        /*
        Constructed SOAP Request Message:
        <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:dwr="http://www.dwr.state.co.us/">
            <soapenv:Header/>
            <soapenv:Body>
                <dwr:GetSMSCurrentRatingTable>
                    <dwr:Abbrev>CLAGRECO</dwr:Abbrev>
                    <dwr:Variable>DISCHRG</dwr:Variable>
                </dwr:GetSMSCurrentRatingTable>
            </soapenv:Body>
        </soapenv:Envelope>
         */

        // SOAP Body
        SOAPBody soapBody = envelope.getBody();
        SOAPElement soapBodyMethod = soapBody.addChildElement("GetSMSCurrentRatingTable", "dwr");
        SOAPElement abbrev = soapBodyMethod.addChildElement("Abbrev", "dwr");
        abbrev.addTextNode(station);
        SOAPElement soapVariable = soapBodyMethod.addChildElement("Variable", "dwr");
        soapVariable.addTextNode("DISCHRG");

        MimeHeaders headers = soapMessage.getMimeHeaders();
        headers.addHeader("SOAPAction", serverURI  + "GetSMSCurrentRatingTable");

        soapMessage.saveChanges();

        //Print the request message
        //System.out.print("Request SOAP Message:");
        //soapMessage.writeTo(System.out);

        return soapMessage;
    }
}