org.powertac.hamweather.XmlOutputStructure.java Source code

Java tutorial

Introduction

Here is the source code for org.powertac.hamweather.XmlOutputStructure.java

Source

/*
 * Copyright (c) 2014 by the original author
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.powertac.hamweather;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * Builds batches of weather reports and corresponding forecasts.
 * Each batch is 24h of data and the corresponding 24h of 24h forecasts.
 * Incomplete batches are discarded.
 * @author John Collins
 */
public class XmlOutputStructure implements OutputStructure {
    static enum XmlState {
        WAITING, GATHERING
    }

    static final double KPH_MPS = 1000.0 / 3600.0;
    static final int HOUR = 3600 * 1000;
    static final int BLOCKSIZE = 24;

    private XmlState state = XmlState.WAITING;
    //private DateTime start = null;
    private Integer batchStartHour;
    private ArrayList<Observation> observations;
    private ArrayList<Forecast> forecasts;

    private String outputFile;
    private Document doc;
    private Element rootElement;

    private DateTimeFormatter iso;
    //private DateTime start;

    public XmlOutputStructure() {
        iso = ISODateTimeFormat.dateTimeNoMillis();
        observations = new ArrayList<Observation>();
        forecasts = new ArrayList<Forecast>();
        DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder docBuilder;
        try {
            docBuilder = docFactory.newDocumentBuilder();

            // Root element
            doc = docBuilder.newDocument();
            doc.setXmlStandalone(true);
            rootElement = doc.createElement("data");
            doc.appendChild(rootElement);
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        }
    }

    /* (non-Javadoc)
     * @see org.powertac.hamweather.OutputStructure#addObservation(org.joda.time.DateTime, long, long, long, long)
     */
    @Override
    public void addObservation(DateTime when, long temp, long dewpoint, long pressure, long windKPH) {
        startMaybe(when);
        if (state == XmlState.GATHERING) {
            if (observations.size() == BLOCKSIZE) {
                // write out and re-initialize
                // this is here because we have to check after gathering up the
                // forecasts from the previous observation.
                buildBlock();
                state = XmlState.WAITING;
                startMaybe(when);
            } else if (skipped(when)) {
                // we've lost data - start over
                state = XmlState.WAITING;
                startMaybe(when);
                if (state == XmlState.WAITING)
                    // discard and wait for start of next block
                    return;
            }
            observations.add(new Observation(when, temp, dewpoint, pressure, windKPH * KPH_MPS));
        }
    }

    /* (non-Javadoc)
     * @see org.powertac.hamweather.OutputStructure#addForecast(org.joda.time.DateTime, int, org.joda.time.DateTime, long, long, long, long)
     */
    @Override
    public void addForecast(DateTime when, int index, DateTime hour, long temp, long dewpoint, long sky,
            long windKPH) {
        if (state == XmlState.GATHERING) {
            forecasts.add(new Forecast(when, index, hour, temp, dewpoint, windKPH * KPH_MPS));
        }
    }

    /* (non-Javadoc)
     * @see org.powertac.hamweather.OutputStructure#forecastMissing()
     */
    @Override
    public void forecastMissing() {
        state = XmlState.WAITING;
    }

    @Override
    public void setOutputFile(String filename) {
        outputFile = filename;
    }

    @Override
    public void setBatchStartHour(Integer hour) {
        batchStartHour = hour;
    }

    /* (non-Javadoc)
     * @see org.powertac.hamweather.OutputStructure#write()
     */
    @Override
    public void write() {
        FileOutputStream out;
        try {
            out = new FileOutputStream(new File(outputFile));
            TransformerFactory tFactory = TransformerFactory.newInstance();
            Transformer transformer = tFactory.newTransformer();
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");

            DOMSource source = new DOMSource(doc);
            StreamResult result = new StreamResult(out);
            transformer.transform(source, result);
            out.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (TransformerConfigurationException e) {
            e.printStackTrace();
        } catch (TransformerException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // If it's time to start, then re-initialize
    private void startMaybe(DateTime current) {
        if (state == XmlState.GATHERING)
            return;
        if (state == XmlState.WAITING) {
            if (timeToStart(current)) {
                //start = current;
                state = XmlState.GATHERING;
                observations.clear();
                forecasts.clear();
            }
        } else {
            System.out.println("Bogus state " + state);
        }
    }

    private boolean timeToStart(DateTime current) {
        if (null == batchStartHour)
            return true;
        int hour = current.hourOfDay().get();
        if (current.minuteOfHour().get() >= 30)
            hour += 1;
        if (hour == batchStartHour)
            return true;
        return false;
    }

    private boolean skipped(DateTime when) {
        if (observations.size() == 0)
            return false;
        Observation last = observations.get(observations.size() - 1);
        DateTime fence = last.when.plus(HOUR + HOUR / 2);
        if (when.isAfter(fence)) {
            System.out.println("Observation skipped at " + iso.print(fence));
            return true;
        }
        return false;
    }

    private void buildBlock() {
        // weatherReports elements
        Element weatherReports = doc.createElement("weatherReports");
        rootElement.appendChild(weatherReports);

        for (Observation weather : observations) {
            Element weatherReport = doc.createElement("weatherReport");
            weatherReport.setAttribute("date", weather.when.toString(iso));
            weatherReport.setAttribute("windspeed", weather.windMPS.toString());
            weatherReports.appendChild(weatherReport);
        }

        // weatherForecasts elements
        Element weatherForecasts = doc.createElement("weatherForecasts");
        rootElement.appendChild(weatherForecasts);

        for (Forecast forecast : forecasts) {
            Element weatherForecast = doc.createElement("weatherForecast");
            weatherForecast.setAttribute("date", forecast.when.toString(iso));
            weatherForecast.setAttribute("id", forecast.id.toString());
            weatherForecast.setAttribute("origin", forecast.origin.toString(iso));
            weatherForecast.setAttribute("temp", forecast.temp.toString());
            weatherForecast.setAttribute("windspeed", forecast.windMPS.toString());
            weatherForecasts.appendChild(weatherForecast);
        }
    }

    // Data structures
    class Observation {
        DateTime when;
        Long temp;
        Long dewpoint;
        Long pressure;
        Double windMPS;

        Observation(DateTime when, Long temp, Long dewpoint, Long pressure, double windMPS) {
            this.when = when;
            this.temp = temp;
            this.dewpoint = dewpoint;
            this.pressure = pressure;
            this.windMPS = windMPS;
        }
    }

    class Forecast {
        DateTime when;
        Integer id;
        DateTime origin;
        Long temp;
        Long dewpoint;
        Double windMPS;

        Forecast(DateTime when, int id, DateTime origin, Long temp, Long dewpoint, Double windMPS) {
            this.when = when;
            this.id = id;
            this.origin = origin;
            this.temp = temp;
            this.dewpoint = dewpoint;
            this.windMPS = windMPS;
        }
    }
}