tim@64: /**
tim@64:  *
tim@64:  */
tim@64: package de.intevation.gnv.transition.timeseries;
tim@64: 
tim@68: import java.awt.Color;
tim@68: import java.awt.Dimension;
tim@68: import java.io.IOException;
tim@73: import java.io.OutputStream;
tim@90: import java.io.OutputStreamWriter;
tim@177: import java.io.UnsupportedEncodingException;
tim@217: import java.util.ArrayList;
tim@68: import java.util.Collection;
tim@68: import java.util.Iterator;
tim@217: import java.util.List;
tim@68: 
tim@95: import javax.xml.transform.Transformer;
tim@95: import javax.xml.transform.TransformerConfigurationException;
tim@95: import javax.xml.transform.TransformerException;
tim@95: import javax.xml.transform.TransformerFactory;
tim@95: import javax.xml.transform.TransformerFactoryConfigurationError;
tim@95: import javax.xml.transform.dom.DOMSource;
tim@95: import javax.xml.transform.stream.StreamResult;
tim@95: 
tim@68: import org.apache.log4j.Logger;
tim@95: import org.w3c.dom.Document;
tim@95: import org.w3c.dom.Element;
tim@95: import org.w3c.dom.Node;
tim@217: import org.w3c.dom.NodeList;
tim@68: 
tim@90: import au.com.bytecode.opencsv.CSVWriter;
tim@119: import de.intevation.artifactdatabase.Config;
tim@95: import de.intevation.artifactdatabase.XMLUtils;
tim@117: import de.intevation.artifacts.CallMeta;
tim@178: import de.intevation.artifacts.PreferredLocale;
tim@178: import de.intevation.gnv.artifacts.ressource.RessourceFactory;
tim@68: import de.intevation.gnv.chart.ChartFactory;
tim@68: import de.intevation.gnv.chart.ChartLabels;
tim@68: import de.intevation.gnv.chart.ChartStyle;
tim@68: import de.intevation.gnv.chart.exception.TechnicalChartException;
tim@68: import de.intevation.gnv.geobackend.base.Result;
tim@95: import de.intevation.gnv.statistics.Statistic;
tim@98: import de.intevation.gnv.statistics.Statistics;
tim@95: import de.intevation.gnv.statistics.TimeseriesStatistics;
tim@95: import de.intevation.gnv.statistics.exception.StatisticsException;
tim@217: import de.intevation.gnv.timeseries.gap.DefaultTimeGap;
tim@217: import de.intevation.gnv.timeseries.gap.TimeGap;
tim@91: import de.intevation.gnv.transition.InputData;
tim@64: import de.intevation.gnv.transition.OutputTransitionBase;
tim@68: import de.intevation.gnv.transition.describedata.KeyValueDescibeData;
tim@81: import de.intevation.gnv.transition.describedata.NamedCollection;
tim@68: import de.intevation.gnv.transition.exception.TransitionException;
tim@95: import de.intevation.gnv.utils.ArtifactXMLUtilities;
tim@64: 
tim@64: /**
tim@64:  * @author Tim Englich <tim.englich@intevation.de>
tim@117:  * 
tim@64:  */
tim@117: public class TimeSeriesOutputTransition extends OutputTransitionBase {
tim@86: 
tim@86:     /**
tim@86:      * The UID of this Class
tim@86:      */
tim@86:     private static final long serialVersionUID = 4178407570503098858L;
tim@117: 
tim@68:     /**
tim@68:      * the logger, used to log exceptions and additonaly information
tim@68:      */
tim@117:     private static Logger log = Logger
tim@117:             .getLogger(TimeSeriesOutputTransition.class);
tim@217:     
tim@217:     
tim@217:     private static List<TimeGap> timeGapDefinitions = null;
tim@117: 
tim@117:     protected String domainLable = "Zeit [UTC]";
tim@171: 
tim@119:     protected String featureValuesName = "featureid";
tim@119:     protected String parameterValuesName = "parameterid";
tim@119:     protected String measuremenValueName = "measurementid";
tim@179:     protected String dateValueName = "dateid";
tim@117: 
tim@64:     /**
tim@64:      * Constructor
tim@64:      */
tim@64:     public TimeSeriesOutputTransition() {
tim@64:         super();
tim@64:     }
tim@64: 
tim@64:     /**
tim@117:      * @see de.intevation.gnv.transition.OutputTransition#out(java.lang.String,
tim@117:      *      java.util.Collection, java.io.OutputStream, java.lang.String,
tim@117:      *      de.intevation.artifacts.CallMeta)
tim@117:      */
tim@117:     public void out(String outputMode, Collection<InputData> inputData,
tim@117:                     OutputStream outputStream, String uuid, CallMeta callMeta)
tim@117:                                                                               throws TransitionException {
tim@90:         log.debug("TimeSeriesOutputTransition.out");
tim@68:         try {
tim@117: 
tim@117:             this.advance(uuid, callMeta);
tim@117:             Collection<Result> chartResult = this.getChartResult(uuid);
tim@117:             if (outputMode.equalsIgnoreCase("chart")) {
tim@90:                 log.debug("Chart will be generated.");
tim@91:                 int chartWidth = 600;
tim@91:                 int chartHeight = 400;
tim@91:                 try {
tim@117:                     if (inputData != null) {
tim@91:                         Iterator<InputData> it = inputData.iterator();
tim@117:                         while (it.hasNext()) {
tim@91:                             InputData ip = it.next();
tim@117:                             if (ip.getName().equalsIgnoreCase("width")) {
tim@91:                                 chartWidth = Integer.parseInt(ip.getValue());
tim@117:                             } else if (ip.getName().equalsIgnoreCase("height")) {
tim@91:                                 chartHeight = Integer.parseInt(ip.getValue());
tim@91:                             }
tim@91:                         }
tim@91:                     }
tim@91:                 } catch (NumberFormatException e) {
tim@117:                     log.error(e, e);
tim@91:                     throw new TransitionException(e);
tim@91:                 }
tim@207:                 Collection<KeyValueDescibeData> parameters = this.getParameters(uuid);
tim@207:                 Collection<KeyValueDescibeData> measurements = this.getMeasurements(uuid);
tim@207:                 Collection<KeyValueDescibeData> dates = this.getDates(uuid);
tim@117:                 ChartStyle chartStyle = this
tim@117:                         .creatStyle(chartWidth, chartHeight);
tim@178:                 ChartLabels chartLables = new ChartLabels(this.getFisName(callMeta.getLanguages())+" "+this
tim@207:                         .getSelectedFeatureName(uuid), this.domainLable);
tim@179:                 this.createChart(outputStream, parameters, measurements,dates,
tim@117:                         chartStyle, chartLables, uuid);
tim@117:             } else if (outputMode.equalsIgnoreCase("csv")) {
tim@90:                 log.debug("CSV-File will be generated.");
tim@177:                 this.createCSV(outputStream, chartResult);
tim@117:             } else if (outputMode.equalsIgnoreCase("statistics")) {
tim@95:                 log.debug("Statistics will be generated.");
tim@98:                 Statistics s = getStatisticsGenerator();
tim@117:                 Collection<Statistic> statistics = s
tim@117:                         .calculateStatistics(chartResult);
tim@95:                 Document doc = this.writeStatistics2XML(statistics);
tim@95:                 this.writeDocument2OutputStream(doc, outputStream);
tim@90:             }
tim@68:         } catch (IOException e) {
tim@117:             log.error(e, e);
tim@68:             throw new TransitionException(e);
tim@68:         } catch (TechnicalChartException e) {
tim@117:             log.error(e, e);
tim@68:             throw new TransitionException(e);
tim@117:         } catch (StatisticsException e) {
tim@117:             log.error(e, e);
tim@95:             throw new TransitionException(e);
tim@68:         }
tim@68:     }
tim@98: 
tim@98:     /**
tim@177:      * @param outputStream
tim@177:      * @param chartResult
tim@177:      * @throws UnsupportedEncodingException
tim@177:      * @throws IOException
tim@177:      * @throws TransitionException
tim@177:      */
tim@177:     protected void createCSV(OutputStream outputStream,
tim@177:                             Collection<Result> chartResult)
tim@177:                                                           throws UnsupportedEncodingException,
tim@177:                                                           IOException,
tim@177:                                                           TransitionException {
tim@177:         if (chartResult != null) {
tim@177:             CSVWriter writer = new CSVWriter(new OutputStreamWriter(
tim@177:                     outputStream, "ISO-8859-1"), ','); 
tim@177:             // USE THIS ENCODING BECAUSE OF
tim@177:             // PROBLEMS WITH EXCEL AND UTF-8
tim@177:             Iterator<Result> it = chartResult.iterator();
tim@177:             while (it.hasNext()) {
tim@177:                 Result result = it.next();
tim@177:                 int i = 0;
tim@177:                 String[] entries = new String[5];
tim@177:                 entries[i++] = result.getString("XORDINATE");
tim@177:                 entries[i++] = result.getString("YORDINATE");
tim@177:                 entries[i++] = result.getString("GROUP1");
tim@177:                 entries[i++] = result.getString("GROUP2");
tim@177:                 entries[i++] = result.getString("GROUP3");
tim@177:                 writer.writeNext(entries);
tim@177:             }
tim@177:             writer.close();
tim@177:         } else {
tim@177:             log.error("No Data given for generation an CSV-File.");
tim@177:             throw new TransitionException(
tim@177:                     "No Data given for generation an CSV-File.");
tim@177:         }
tim@177:     }
tim@177: 
tim@177:     /**
tim@98:      * @return
tim@98:      */
tim@98:     protected Statistics getStatisticsGenerator() {
tim@98:         Statistics s = new TimeseriesStatistics();
tim@98:         return s;
tim@98:     }
tim@117: 
tim@117:     protected void writeDocument2OutputStream(Document document, OutputStream os) {
tim@117: 
tim@95:         try {
tim@117:             TransformerFactory transformerFactory = TransformerFactory
tim@117:                     .newInstance();
tim@95:             Transformer transformer = transformerFactory.newTransformer();
tim@95:             DOMSource source = new DOMSource(document);
tim@117:             StreamResult result = new StreamResult(os);
tim@95:             transformer.transform(source, result);
tim@95:         } catch (TransformerConfigurationException e) {
tim@117:             log.error(e, e);
tim@95:         } catch (TransformerFactoryConfigurationError e) {
tim@117:             log.error(e, e);
tim@117:         } catch (TransformerException e) {
tim@117:             log.error(e, e);
tim@95:         }
tim@95:     }
tim@117: 
tim@117:     protected Document writeStatistics2XML(Collection<Statistic> statistic) {
tim@95:         ArtifactXMLUtilities xmlUtilities = new ArtifactXMLUtilities();
tim@95:         Document doc = XMLUtils.newDocument();
tim@117:         if (statistic != null) {
tim@117:             Node statisticResults = xmlUtilities.createArtifactElement(doc,
tim@117:                     "statistic-values");
tim@95:             doc.appendChild(statisticResults);
tim@95:             Iterator<Statistic> it = statistic.iterator();
tim@117:             while (it.hasNext()) {
tim@95:                 Statistic s = it.next();
tim@117:                 Element result = xmlUtilities.createArtifactElement(doc,
tim@117:                         "statistic");
tim@95:                 result.setAttribute("name", s.getKey());
tim@95:                 result.setAttribute("value", s.getStringValue());
tim@95:                 statisticResults.appendChild(result);
tim@95:             }
tim@117: 
tim@95:         }
tim@95:         return doc;
tim@95:     }
tim@68: 
tim@207:     protected String getSelectedFeatureName(String uuid) {
tim@117:         Collection<KeyValueDescibeData> values = this
tim@207:                 .getCollection(featureValuesName, uuid);
tim@117:         if (values != null) {
tim@86:             Iterator<KeyValueDescibeData> it = values.iterator();
tim@117:             while (it.hasNext()) {
tim@86:                 KeyValueDescibeData data = it.next();
tim@117:                 if (data.isSelected()) {
tim@86:                     return data.getValue();
tim@86:                 }
tim@86:             }
tim@86:         }
tim@86:         return null;
tim@86:     }
tim@117: 
tim@86:     /**
tim@86:      * @param outputStream
tim@86:      * @param parameters
tim@86:      * @param measurements
tim@86:      * @param timeSeriesName
tim@86:      * @param chartStyle
tim@86:      * @param chartLables
tim@86:      * @throws IOException
tim@86:      * @throws TechnicalChartException
tim@86:      */
tim@86:     protected void createChart(OutputStream outputStream,
tim@117:                                Collection<KeyValueDescibeData> parameters,
tim@117:                                Collection<KeyValueDescibeData> measurements,
tim@179:                                Collection<KeyValueDescibeData> dates,
tim@117:                                ChartStyle chartStyle, ChartLabels chartLables,
tim@117:                                String uuid) throws IOException,
tim@117:                                            TechnicalChartException {
tim@86:         ChartFactory chartFactory = new ChartFactory();
tim@86:         chartFactory.createSimpleTimeSeriesChart(chartLables, chartStyle,
tim@117:                 parameters, measurements, outputStream, this
tim@217:                         .getChartResult(uuid),timeGapDefinitions);
tim@68:     }
tim@117: 
tim@117:     protected ChartStyle creatStyle(int witdh, int height) {
tim@68:         // TODO Konfigurierbar machen
tim@117:         de.intevation.gnv.chart.Insets lInsets = new de.intevation.gnv.chart.Insets(
tim@117:                 5d, 5d, 5d, 5d);
tim@68:         Dimension lChartSize = new Dimension(witdh, height);
tim@117:         return new ChartStyle(Color.white, new Color(230, 230, 230),
tim@117:                 Color.white, Color.white, true, true, lInsets, lChartSize);
tim@68:     }
tim@178:     
tim@178:     protected String getFisName(PreferredLocale[] preferredLocales){
tim@178:         String returnValue = "";
tim@178:         InputData inputData = this.inputData.get("fisname");
tim@178:         if (inputData != null){
tim@178:             returnValue = RessourceFactory.getInstance()
tim@178:                                           .getRessource(preferredLocales, 
tim@178:                                                         inputData.getValue(), 
tim@178:                                                         inputData.getValue());
tim@178:         }
tim@178:         return returnValue;
tim@178:     }
tim@117: 
tim@207:     protected Collection<KeyValueDescibeData> getParameters(String uuid) {
tim@207:         return this.getCollection(parameterValuesName, uuid);
tim@64:     }
tim@117: 
tim@207:     protected Collection<KeyValueDescibeData> getMeasurements(String uuid) {
tim@207:         return this.getCollection(measuremenValueName, uuid);
tim@119:     }
tim@207:     protected Collection<KeyValueDescibeData> getDates(String uuid) {
tim@207:         return this.getCollection(dateValueName,uuid);
tim@179:     }
tim@119: 
tim@119:     @Override
tim@119:     public void setup(Node configuration) {
tim@119:         super.setup(configuration);
tim@171:         String featureNameValue = Config.getStringXPath(configuration,
tim@171:                 "value-names/value-name[@name='feature']/@value");
tim@171:         if (featureNameValue != null) {
tim@119:             this.featureValuesName = featureNameValue;
tim@119:         }
tim@171:         String parameterNameValue = Config.getStringXPath(configuration,
tim@171:                 "value-names/value-name[@name='parameter']/@value");
tim@171:         if (parameterNameValue != null) {
tim@119:             this.parameterValuesName = parameterNameValue;
tim@119:         }
tim@171:         String measurementNameValue = Config.getStringXPath(configuration,
tim@171:                 "value-names/value-name[@name='measurement']/@value");
tim@171:         if (measurementNameValue != null) {
tim@119:             this.measuremenValueName = measurementNameValue;
tim@119:         }
tim@179:         
tim@179:         String dateNameValue = Config.getStringXPath(configuration,
tim@179:         "value-names/value-name[@name='date']/@value");
tim@179:         if (dateNameValue != null) {
tim@179:             this.dateValueName = dateNameValue;
tim@179:         }
tim@217:         if (timeGapDefinitions == null){
tim@217:             Element gapDefinition =  (Element)Config.getNodeXPath(configuration,
tim@217:                                                                  "time-gap-definition");
tim@217:             synchronized (this.getClass()) {
tim@217:                 if (gapDefinition != null){
tim@217:                     String link = gapDefinition.getAttribute("xlink:href");
tim@217:                     if (link != null ){
tim@217:                         String absolutFileName = Config.replaceConfigDir(link);
tim@217:                         gapDefinition = (Element)new ArtifactXMLUtilities().
tim@217:                                                      readConfiguration(absolutFileName);
tim@217:                     }
tim@217:                     
tim@217:                     NodeList gapDefinitions = Config.getNodeSetXPath(gapDefinition, 
tim@217:                                                                     "/time-gaps/time-gap");
tim@217:                     if (gapDefinition != null){
tim@217:                         timeGapDefinitions = new ArrayList<TimeGap>(gapDefinitions.
tim@217:                                                                     getLength());
tim@217:                         for (int i = 0; i < gapDefinitions.getLength(); i++){
tim@217:                             Element gapNode = (Element)gapDefinitions.item(i);
tim@217:                             String unit = gapNode.getAttribute("unit");
tim@217:                             int key = Integer.parseInt(gapNode.getAttribute("key"));
tim@217:                             int value = Integer.parseInt(gapNode.getAttribute("gap"));
tim@217:                             log.info("Add new Timegap: "+key+" "+value+" "+ unit);
tim@217:                             timeGapDefinitions.add(new DefaultTimeGap(unit, 
tim@217:                                                                       key, 
tim@217:                                                                       value));
tim@217:                         }
tim@217:                     }
tim@217:                     
tim@217:                 }
tim@217:             }
tim@217:         }
tim@82:     }
tim@217:     
tim@82:     /**
tim@82:      * @param collectionName
tim@82:      * @return
tim@82:      */
tim@82:     protected Collection<KeyValueDescibeData> getCollection(
tim@207:                                                             String collectionName, 
tim@207:                                                             String uuid) {
tim@207:         Iterator<Object> it = this.getDescibeData(uuid).iterator();
tim@117:         while (it.hasNext()) {
tim@117: 
tim@117:             Object o = it.next();
tim@117: 
tim@117:             if (o instanceof NamedCollection<?>) {
tim@117:                 NamedCollection<KeyValueDescibeData> nc = (NamedCollection<KeyValueDescibeData>) o;
tim@117:                 if (nc.getName().equals(collectionName)) {
tim@117:                     return nc;
tim@117:                 }
tim@117:             }
tim@68:         }
tim@68:         return null;
tim@68:     }
tim@64: }