NOAA Web Site Link Tides and Currents Home Page Transparent placeholder image
CO-OPS         IOOS Data Portal         Take Our Survey
banner graphic

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class WaterLevelPredictionsGetter {

    public static void main(String[] args) {
        //CO-OPS Sensor Observation Service (SOS) URL
        String sosUrl = "https://opendap.co-ops.nos.noaa.gov/ioos-dif-sos/SOS";
        String baseUrl = sosUrl + "?service=SOS&request=GetObservation&version=1.0.0&observedProperty=sea_surface_height_amplitude_due_to_equilibrium_ocean_tide";

        /*
         * The user can modify the local directory where the downloaded data files can be stored, the water level predictions data type, 
         * the stations IDs, the date range, the datum, the unit and the preferred format of those data files.
         *
         */
        //Local directory that data files will be stored to
        String saveToYourLocalDir = "C:\\temp\\sosdata\\";
        //Water level predictions data type options: SixMinuteTidePredictions, HourlyTidePredictions, HighLowTidePredictions
        String predictionsDataType = "SixMinuteTidePredictions";
        //Station Ids
        String stationsIds[] = {"8454000", "8454049"};
        //Begin date and end date for requested data        
        String dates[] = {"2012-06-01", "2012-08-20"};
        //Datum options: MHHW, MHW, MLLW, MLW, MSL, MTL, NAVD, STND, also can be null
        String datum = "MSL";
        //Data unit options: Meters, Feet, also can be null
        String unit = "Feet";
        //Response format options: csv, tsv, xml, kml
        String responseFormat = "csv";

        String dateTimeStr;
        String url;
        URL dataUrl;
        String fileName;

        try {
            List dtList = getDateTimeList(dates);
            String responseMIMEType = getResponseMIMEType(responseFormat);

            for (int i = 0; i < stationsIds.length; i++) {
                if (!isValidNwlonStationId(stationsIds[i])) {
                    throw new IOException(" Wrong Station ID: " + stationsIds[i] + ", please enter a valid station ID.");
                }

                for (int j = 0, size = dtList.size(); j < size; j++) {
                    dateTimeStr = (String) dtList.get(j);
                    url = baseUrl + "&offering=urn:ioos:station:NOAA.NOS.CO-OPS:"
                            + stationsIds[i] + "&responseFormat=" + urlEncode(responseMIMEType) + "&eventTime=" + dateTimeStr;

                    if (datum != null && datum.length() != 0) {
                        if (isValidVerticalDatum(datum)) {
                            url += "&result=VerticalDatum%3D%3D" + getDatumURN(datum);
                        } else {
                            throw new IOException(" Wrong Datum: please enter a valid datum string.");
                        }
                    }

                    if (unit != null && unit.length() != 0) {
                        if (unit.equalsIgnoreCase("Meters") || unit.equalsIgnoreCase("Feet")) {
                            url += "&unit=" + unit;
                        } else {
                            throw new IOException(" Wrong Unit: please enter a valid unit string.");
                        }
                    }

                    if (predictionsDataType != null && predictionsDataType.length() != 0) {
                        if (isValidWaterLevelPredsDataType(predictionsDataType)) {
                            url += "&dataType=" + predictionsDataType;
                        } else {
                            throw new IOException(" Wrong Water Level Predictions Data Type: please enter a valid data type string.");
                        }
                    }

                    /*
                     * The end result of that URL should look like this; Request
                     * URL:
                     * https://opendap.co-ops.nos.noaa.gov/ioos-dif-sos/SOS?service=SOS&request=GetObservation&version=1.0.0
                     * &observedProperty=sea_surface_height_amplitude_due_to_equilibrium_ocean_tide&offering=urn:ioos:station:NOAA.NOS.CO-OPS:8454000
                     * &responseFormat=text%2Fcsv&eventTime=2012-03-01T00:00:00Z/2012-03-31T23:59:00Z
                     * &result=VerticalDatum%3D%3Durn:ioos:def:datum:noaa::MLLW&unit=Meters&dataType=SixMinuteTidePredictions
                     */

                    dataUrl = new URL(url);
                    fileName = getFileName(stationsIds[i], dateTimeStr, responseFormat, predictionsDataType, saveToYourLocalDir);
                    getResource(dataUrl, fileName);
                }
            }
        } catch (Exception e) {
            System.err.println("ERROR: ******* " + e.getMessage());
        }
    }

    /**
     * Get the list of date/timestamp in IOOS-DIF format yyyy-mm-ddThh:mm:ssZ
     * and in GMT.
     *
     * @param dates[] date to fetch data
     * @return a list of date/timestamp in yyyy-mm-ddThh:mm:ssZ format, e.g.
     * 2005-01-01T00:00:00Z/2005-02-01T00:00:00Z
     */
    public static List getDateTimeList(String dates[]) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
        String startTime = "00:00:00";
        String endTime = "23:59:00";
        int MAX_RETRIEVAL_DAYS = 31; // This maximum allowed time period is enforced by the server and can not be changed
        long difference;
        double diffDays;
        Date start;
        Date end;
        Date tempStart;
        Date tempEnd;
        String dateTimeStr;
        List dateTimeList = new ArrayList();

        try {
            start = sdf.parse(dates[0] + " " + startTime);
            end = sdf.parse(dates[1] + " " + endTime);
            if (end.before(start)) {
                throw new java.text.ParseException(" Wrong dates order, end date must be later than begin date.", -1);
            }
            difference = end.getTime() - start.getTime();
            diffDays = difference / (1000 * 60 * 60 * 24);

            if (diffDays > MAX_RETRIEVAL_DAYS) {
                tempStart = start;
                for (int d = 0; d <= diffDays; d += MAX_RETRIEVAL_DAYS) {
                    tempEnd = sdf.parse(addDays(MAX_RETRIEVAL_DAYS - 1, tempStart) + " " + endTime);
                    if (tempEnd.after(end)) {
                        tempEnd = end;
                    }
                    dateTimeStr = sdf.format(tempStart).replace(" ", "T") + "Z" + "/" + sdf.format(tempEnd).replace(" ", "T") + "Z";
                    dateTimeList.add(dateTimeStr);
                    tempStart = sdf.parse(addDays(1, tempEnd) + " " + startTime);
                }
            } else {
                dateTimeStr = sdf.format(start).replace(" ", "T") + "Z" + "/" + sdf.format(end).replace(" ", "T") + "Z";
                dateTimeList.add(dateTimeStr);
            }
        } catch (ParseException ex) {
            System.err.println("ERROR: ******* " + ex.toString());
        }
        return dateTimeList;
    }

    /**
     * Add days to a Date.
     *
     * @param dayOffset number of days to add
     * @param dateTime a date
     * @return a date as string in yyyy-MM-dd format
     */
    public static String addDays(int dayOffset, Date dateTime) {
        Calendar calender = new GregorianCalendar();
        calender.setTime(dateTime);
        calender.add(Calendar.DATE, dayOffset);

        int month = calender.get(Calendar.MONTH) + 1;
        int day = calender.get(Calendar.DAY_OF_MONTH);
        int year = calender.get(Calendar.YEAR);

        StringBuilder buffer = new StringBuilder(15);
        buffer.append(year);
        buffer.append('-');

        if (month < 10) {
            buffer.append('0');
        }
        buffer.append(month);
        buffer.append('-');

        if (day < 10) {
            buffer.append('0');
        }
        buffer.append(day);

        return buffer.toString();
    }

    /**
     * Get the MIME type of response format.
     *
     * @param formatOption response format
     * @return MIME Type of response format
     */
    private static String getResponseMIMEType(String formatOption) throws IOException {
        String responseMIMEType = "text/csv";
        if (formatOption.equalsIgnoreCase("csv")) {
            responseMIMEType = "text/csv";
        } else if (formatOption.equalsIgnoreCase("tsv")) {
            responseMIMEType = "text/tab-separated-values";
        } else if (formatOption.equalsIgnoreCase("kml")) {
            responseMIMEType = "application/vnd.google-earth.kml+xml";
        } else if (formatOption.equalsIgnoreCase("xml")) {
            responseMIMEType = "text/xml;schema=\"ioos/0.6.1\"";
        } else {
            throw new IOException(" Wrong Response Format: please enter a valid response format string.");
        }
        return responseMIMEType;
    }

    /**
     * Check if string is a valid NWLON station ID (consist of 7 digits).
     *
     * @param stationId string to check
     * @return boolean if consists only of numbers
     */
    public static boolean isValidNwlonStationId(String stationId) {
        Pattern regexWL = Pattern.compile("^[1-9][0-9]{6}$");

        if (stationId == null) {
            return false;
        } else {
            stationId = stationId.trim();
            Matcher mWL = regexWL.matcher(stationId);

            if (mWL.matches()) {
                return true;
            } else {
                return false;
            }
        }
    }

    /**
     * Check if string is a valid vertical datum.
     *
     * @param datum string to check
     * @return boolean if string is a valid datum
     */
    public static boolean isValidVerticalDatum(String datum) {
        boolean isValidVDatum = false;
        List acceptVDatums = new ArrayList();
        acceptVDatums.add("MHHW");
        acceptVDatums.add("MHW");
        acceptVDatums.add("MLLW");
        acceptVDatums.add("MLW");
        acceptVDatums.add("MSL");
        acceptVDatums.add("MTL");
        acceptVDatums.add("NAVD");
        acceptVDatums.add("STND");

        if (datum != null) {
            if (datum.length() == 0) {
                isValidVDatum = false;
            } else {
                String s = datum.toUpperCase();
                for (int i = 0, size = acceptVDatums.size(); i < size; i++) {
                    if (acceptVDatums.get(i).toString().equals(s)) {
                        isValidVDatum = true;
                        break;
                    }
                }
            }
        }
        return isValidVDatum;
    }

    /**
     * Check if string is a valid water level predictions data type.
     *
     * @param stationId string to check
     * @return boolean if string is a valid water level predictions data type
     */
    public static boolean isValidWaterLevelPredsDataType(String dataType) {
        boolean isValidDataType = false;
        List acceptWLPredsDataType = new ArrayList();
        acceptWLPredsDataType.add("SixMinuteTidePredictions");
        acceptWLPredsDataType.add("HourlyTidePredictions");
        acceptWLPredsDataType.add("HighLowTidePredictions");
        if (dataType != null) {
            for (int i = 0, size = acceptWLPredsDataType.size(); i < size; i++) {
                int matches = dataType.compareToIgnoreCase(acceptWLPredsDataType.get(i).toString());
                if (matches == 0) {
                    isValidDataType = true;
                    break;
                }
            }
        }
        return isValidDataType;
    }

    /**
     * Encode a URL string.
     *
     * @param inString string to encode
     * @return encoded string
     */
    private static String urlEncode(String inString) {
        if (inString != null) {
            inString = inString.replace("/", "%2F");
            inString = inString.replace("+", "%2B");
        }
        return inString;
    }

    /**
     * Get the name of data file that will be saved to local hard disk.
     *
     * @param stationId station ID
     * @param dateTime time event
     * @param responseFormat response format
     * @param dataType data type
     * @param saveToYourLocalDir local directory that data files will be stored
     * to
     * @return new file name to be created on local hard disk
     */
    public static String getFileName(String stationId, String dateTime, String responseFormat, String dataType, String saveToYourLocalDir) {
        String dateTimeStr = dateTime.replace(":", "-").replace("/", "_");
        if (dataType == null && dataType.length() == 0) {
            dataType = "SixMinuteTidePredictions";
        }

        String fileName = saveToYourLocalDir + dataType + "_" + stationId + "_" + dateTimeStr + "." + responseFormat.toLowerCase();
        return fileName;
    }

    /**
     * Fetch the files from the http connection and write it to a local file.
     *
     * @param url URL to fetch data
     * @param fileName new file to be created on local hard disk
     */
    public static void getResource(URL url, String fileName) {
        HttpURLConnection huc = null;
        BufferedInputStream in = null;
        FileOutputStream file;
        BufferedOutputStream out = null;

        /*
         * Note that both the input and the output streams are buffered - this
         * greatly speeds the download time and is far more efficient that
         * reading and writing a single byte at a time, especially over the
         * network.
         */
        try {
            huc = (HttpURLConnection) url.openConnection();
            huc.setAllowUserInteraction(false);
            huc.setDoInput(true);
            huc.setDoOutput(false);
            huc.setUseCaches(false);
            huc.setReadTimeout(30000);
            huc.connect();

            in = new BufferedInputStream(huc.getInputStream());
            file = new FileOutputStream(fileName);
            out = new BufferedOutputStream(file);

            int len;
            byte[] data = new byte[1024];
            while ((len = in.read(data, 0, 1024)) >= 0) {
                out.write(data, 0, len);
            }

            out.flush();
            out.close();
            in.close();
            huc.disconnect();
            Thread.sleep(4000);
        } catch (InterruptedException iex) {
            System.err.println("ERROR: ******* " + iex.toString());
        } catch (IOException ex) {
            System.err.println("ERROR: ******* " + ex.toString());
        } finally {
            try {
                in.close();
                out.close();
                huc.disconnect();
            } catch (IOException ex) {
                System.err.println("ERROR: ******* " + ex.toString());
            }
        }
    }

    /**
     * Obtain datum URN.
     *
     * @param datum vertical datum
     * @return the datum URN
     */
    private static String getDatumURN(String datum) {
        String IOOS_VDATUM_PREFIX = "urn:ioos:def:datum:noaa::";
        String datumUrn;
        if (datum.equalsIgnoreCase("NAVD")) {
            datumUrn = "urn:ogc:def:datum:epsg::5103";
        } else {
            datumUrn = IOOS_VDATUM_PREFIX + datum;
        }
        return datumUrn;
    }
}

 
Web site owner: Center for Operational Oceanographic Products and Services (CO-OPS)          Privacy Policy         Take Our Survey