You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 6 Current »

package nl.wldelft.timeseriesparsers;

import nl.wldelft.util.TextUtils;
import nl.wldelft.util.TimeZoneUtils;
import nl.wldelft.util.io.XmlParser;
import nl.wldelft.util.timeseries.DefaultTimeSeriesHeader;
import nl.wldelft.util.timeseries.TimeSeriesContentHandler;

import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 *
 * @autor pelgrim
 * @author ekkelenk
 * Date: 8/9/13
 * Time: 9:35 AM
 * Import the warnings from the AifsML format
 */
public class AifsMLTimeSeriesParser implements XmlParser<TimeSeriesContentHandler> {

    public static final String HYFS_MARKER = "[HYFS]";
    private TimeSeriesContentHandler contentHandler = null;
    private XMLStreamReader reader = null;
    private DefaultTimeSeriesHeader header = null;

    //Use for all
    private String title = null;
    private String identifier = null;
    private String externalForecastTime = null;


    @Override
    public void parse(XMLStreamReader reader, String virtualFileName, TimeSeriesContentHandler contentHandler) throws Exception {
        this.reader = reader;
        this.contentHandler = contentHandler;


        header = new DefaultTimeSeriesHeader();
        reader.require(XMLStreamConstants.START_DOCUMENT, null, null);
        reader.nextTag();
        reader.require(XMLStreamConstants.START_ELEMENT, null, "product");
        parseIdentifier();

        if (!XmlStreamReaderUtils.goTo(reader, "warning")) {
            // There has to be a warning element.
            throw new IOException("Input file doesn't contain a warning element: " + virtualFileName);
        }
        reader.require(XMLStreamConstants.START_ELEMENT, null, "warning");
        reader.nextTag();
        reader.require(XMLStreamConstants.START_ELEMENT, null, "warning-info");

        parseWarningInfo();
        parseWarningAreas();
    }

    /**
     * Retrieve the identifier from the source element
     *
     * @throws XMLStreamException;
     */
    private void parseIdentifier() throws XMLStreamException {
        XmlStreamReaderUtils.goTo(reader, "identifier");
        if (TextUtils.equals(reader.getLocalName(), "identifier")) {
            this.identifier = reader.getElementText();
        }
    }

    /**
     * filter the warning sequence and warning title text attributes from the text elements.
     * they will be used for all areas.
     *
     * @throws XMLStreamException
     */
    private void parseWarningInfo() throws XMLStreamException {
        do {
            if (!XmlStreamReaderUtils.goTo(reader, "text", "warning-info")) {
                // no more text areas, return.
                return;
            }
            // we only need the the warning title.
            if (this.title == null) {
                String type = reader.getAttributeValue(null, "type");
                if (TextUtils.equals("warning_title", type)) {
                    this.title = reader.getElementText();
                }
            }
            if (this.title != null) {
                // we're finished with the text elements, skip to the areas
                return;
            }
        } while (reader.hasNext());
    }


    /**
     * Retrieve the locationId from the area element(s) and call separate method to parse forecast data
     *
     * @throws XMLStreamException;
     */
    private void parseWarningAreas() throws XMLStreamException {
        // parse alle areas binnen een warning element.
        do {
            if (!XmlStreamReaderUtils.goTo(reader, "area", "warning")) {
                // alle area's have been processed or an end tag warning has been found.
                return;
            }
            String locationId = reader.getAttributeValue(null, "aac");
            // an area can have more that one fore-cast periods.
            parseForecastData(locationId);
        } while (reader.hasNext());
    }

    /**
     * helper class to store forecast data.
     */
    private static class ForecastData {
        private String parameter = null;
        private String value = null;
        private String localTime = null;
        private String comment = null;
    }

    /**
     * Retrieve the variable, value, unit, time and comment from the forecast data element(s)
     * Retrieve comment text from forecast text element
     * return true if more forecast periods are available
     *
     * @throws XMLStreamException;
     */
    private void parseForecastData(String locationId) throws XMLStreamException {
        // not every area has a forecast-period. Try to find the next forecast-period,
        // but stop if not found, or an end-tag area has been found.
        List<ForecastData> forecastDataList = new ArrayList<>();
        while (XmlStreamReaderUtils.goTo(reader, "forecast-period", "area")) {
            this.externalForecastTime = reader.getAttributeValue(null, "start-time-utc");
            // inside the forecast-period we need to  process the text elements first.
            do {
                // process all elements until we reach the end tag forecast-period.
                if (!XmlStreamReaderUtils.goTo(reader, "text", "forecast-period")) {
                    // break so we can write this forecast as a timeseries.
                    break;
                }
                String sequence = reader.getAttributeValue(null, "type");
                if (!TextUtils.equals("river_level_prediction", sequence)) {
                    // we only want the river level prediction.
                    continue;
                }
                // we found a river_level_prediction.

                String value = null;
                String comment = null;
                String time = null;
                String riverLevelPredictionText = reader.getElementText();
                int index = riverLevelPredictionText.indexOf(HYFS_MARKER);
                if (index != -1) {
                    String valueComment = riverLevelPredictionText.substring(index + HYFS_MARKER.length());
                    int split = valueComment.indexOf(",");
                    if (split != -1) {
                        value = valueComment.substring(0, split);
                        comment = valueComment.substring(split + 1);
                        time = parseTime();
                    }
                }
                if (time != null) {
                    ForecastData forecastData = new ForecastData();
                    forecastData.comment = comment;
                    forecastData.localTime = time;
                    forecastData.value = value;
                    forecastData.parameter = "prediction";
                    forecastDataList.add(forecastData);
                }
            } while (reader.hasNext());
            // All events have been collected
            writeTimeSerie(locationId, forecastDataList);
        }
    }

    private String parseTime() throws XMLStreamException {
        // if we get here, a river_level_prediction was found. No we need to parse all elements until we find the end of the forecast period
        do {
            // process all elements until we reach the end tag forecast-period.
            if (!XmlStreamReaderUtils.goTo(reader, "element", "forecast-period")) {
                // no element found
                return null;
            }
            String typeAttribute = reader.getAttributeValue(null, "type");
            if (TextUtils.equals("time", typeAttribute)) {
                String timeString = reader.getElementText();
                int index = timeString.indexOf(HYFS_MARKER);
                if (index != -1) {
                    return timeString.substring(index + HYFS_MARKER.length());
                }
            }
        } while (reader.hasNext());
        return null;
    }

    /**
     * Write a time serie header and it's events based on the forecastData.
     * @param locationId location of the timeserie
     * @param externalForecastTime forecast time.
     * @param forecastCollection collection of forecast events.
     */
    private void writeTimeSerie(String locationId, Collection<ForecastData> forecastCollection) {
        //noinspection SpellCheckingInspection
        header.setForecastTime(TimeZoneUtils.GMT, "yyyy-MM-dd'T'HH:mm:ss'Z'", this.externalForecastTime);
        header.setLocationId(locationId);
        for (ForecastData forecastData : forecastCollection) {
            contentHandler.setComment(identifier + ": " + title + '\n' + forecastData.comment);
            header.setParameterId(forecastData.parameter);
            contentHandler.setValue('.', forecastData.value);
            // header.setUnit(forecastData.unit);
            long rawTimeZoneOffset = TimeZoneUtils.parseRawTimeZoneOffset(forecastData.localTime.substring(forecastData.localTime.length() - 6));
            contentHandler.setTime(TimeZoneUtils.getTimeZone(rawTimeZoneOffset), "yyyy-MM-dd'T'HH:mm:ss", forecastData.localTime.substring(0, forecastData.localTime.length() - 6));
            contentHandler.setTimeSeriesHeader(header);
            contentHandler.applyCurrentFields();
        }
    }
}
 
  • No labels