ingo@4213: package de.intevation.flys.artifacts.services;
ingo@4213: 
ingo@4213: import java.awt.Color;
ingo@4213: import java.text.DateFormat;
ingo@4213: import java.text.ParseException;
ingo@4213: import java.util.ArrayList;
ingo@4213: import java.util.Collections;
ingo@4213: import java.util.Date;
ingo@4213: import java.util.List;
ingo@4213: import java.util.Locale;
ingo@4213: 
ingo@4213: import org.apache.log4j.Logger;
ingo@4213: import org.hibernate.Session;
ingo@4213: import org.jfree.chart.ChartFactory;
ingo@4213: import org.jfree.chart.JFreeChart;
ingo@4213: import org.jfree.chart.plot.PlotOrientation;
ingo@4213: import org.jfree.chart.plot.XYPlot;
ingo@4213: import org.jfree.data.xy.XYSeries;
ingo@4213: import org.jfree.data.xy.XYSeriesCollection;
ingo@4213: import org.w3c.dom.Document;
ingo@4213: import org.w3c.dom.Element;
ingo@4213: import org.w3c.dom.NodeList;
ingo@4213: 
ingo@4213: import de.intevation.artifacts.CallMeta;
ingo@4213: import de.intevation.artifacts.GlobalContext;
ingo@4213: import de.intevation.flys.artifacts.model.DischargeTables;
ingo@4213: import de.intevation.flys.artifacts.model.GaugesFactory;
ingo@4213: import de.intevation.flys.artifacts.resources.Resources;
ingo@4213: import de.intevation.flys.backend.SessionHolder;
ingo@4213: import de.intevation.flys.model.DischargeTable;
ingo@4213: import de.intevation.flys.model.Gauge;
ingo@4213: import de.intevation.flys.model.TimeInterval;
ingo@4213: 
ingo@4213: 
ingo@4213: public class DischargeTablesOverview extends AbstractChartService {
ingo@4213: 
ingo@4213:     private static final Logger log = Logger
ingo@4213:         .getLogger(DischargeTablesOverview.class);
ingo@4213: 
ingo@4213:     private static final long serialVersionUID = 1L;
ingo@4213: 
ingo@4213:     public static final String I18N_CHART_TITLE = "gauge.discharge.service.chart.title";
ingo@4213:     public static final String DEFAULT_CHART_TITLE = "Pegel: XXX";
ingo@4213: 
ingo@4213:     public static final String I18N_CHART_X_AXIS_TITLE = "gauge.discharge.service.chart.x.title";
ingo@4213:     public static final String DEFAULT_X_AXIS_TITLE = "Q [m^3/s]";
ingo@4213: 
ingo@4213:     public static final String I18N_CHART_Y_AXIS_TITLE = "gauge.discharge.service.chart.y.title";
ingo@4213:     public static final String DEFAULT_Y_AXIS_TITLE = "W [cm]";
ingo@4213: 
ingo@4213:     public static final String I18N_CHART_SERIES_TITLE = "gauge.discharge.service.chart.series.title";
ingo@4213:     public static final String DEFAULT_CHART_SERIES_TITLE = "Abflusskurve";
ingo@4213: 
ingo@4213:     public static final String I18N_CHART_SERIES_TITLE_MASTER = "gauge.discharge.service.chart.series.title.master";
ingo@4213:     public static final String DEFAULT_CHART_SERIES_TITLE_MASTER = "Aktuelle Abflusskurve";
ingo@4213: 
ingo@4213:     public static final DateFormat DATE_FORMAT = DateFormat.getDateInstance(
ingo@4213:         DateFormat.SHORT, Locale.GERMANY);
ingo@4213: 
ingo@4213:     private Session session;
ingo@4213: 
ingo@4213:     @Override
ingo@4213:     protected void init() {
ingo@4213:         session = SessionHolder.acquire();
ingo@4213:     }
ingo@4213: 
ingo@4213:     @Override
ingo@4213:     protected void finish() {
ingo@4213:         if (session != null) {
ingo@4213:             session.close();
ingo@4213:             SessionHolder.release();
ingo@4213:         }
ingo@4213:     }
ingo@4213: 
ingo@4213:     protected JFreeChart createChart(Document data,
ingo@4213:         GlobalContext globalContext, CallMeta callMeta) {
ingo@4213: 
ingo@4213:         Gauge gauge = extractGauge(data);
ingo@4213: 
ingo@4213:         if (gauge == null) {
ingo@4213:             log.warn("Could not determine Gauge from request!");
ingo@4213:             return null;
ingo@4213:         }
ingo@4213: 
ingo@4213:         log.info("create discharge chart for gauge '" + gauge.getName() + "'");
ingo@4213:         TimeInterval timerange = extractTimeInterval(data);
ingo@4213: 
ingo@4213:         List<DischargeTable> dts = getDischargeTables(gauge, timerange);
ingo@4213:         XYSeriesCollection dataset = new XYSeriesCollection();
ingo@4213: 
ingo@4213:         for (DischargeTable dt : dts) {
ingo@4213:             try {
ingo@4213:                 XYSeries series = createSeries(callMeta, dt);
ingo@4213:                 if (series != null) {
ingo@4213:                     dataset.addSeries(series);
ingo@4213:                 }
ingo@4213:             }
ingo@4213:             catch (IllegalArgumentException iae) {
ingo@4213:                 log.warn("unable to create discharge curve: "
ingo@4213:                     + iae.getMessage());
ingo@4213:             }
ingo@4213:         }
ingo@4213: 
ingo@4213:         String title = Resources.format(callMeta, I18N_CHART_TITLE,
ingo@4213:             DEFAULT_CHART_TITLE, gauge.getName());
ingo@4213: 
ingo@4213:         String xAxis = Resources.getMsg(callMeta, I18N_CHART_X_AXIS_TITLE,
ingo@4213:             DEFAULT_X_AXIS_TITLE);
ingo@4213: 
ingo@4213:         String yAxis = Resources.format(callMeta, I18N_CHART_Y_AXIS_TITLE,
ingo@4213:             DEFAULT_Y_AXIS_TITLE);
ingo@4213: 
ingo@4213:         JFreeChart chart = ChartFactory.createXYLineChart(title, xAxis, yAxis,
ingo@4213:             null, PlotOrientation.VERTICAL, true, true, false);
ingo@4213: 
ingo@4213:         chart.setBackgroundPaint(Color.white);
ingo@4213: 
ingo@4213:         XYPlot plot = (XYPlot) chart.getPlot();
ingo@4213:         plot.setDataset(0, dataset);
ingo@4213:         plot.setBackgroundPaint(Color.white);
ingo@4213:         plot.setDomainGridlinePaint(Color.gray);
ingo@4213:         plot.setRangeGridlinePaint(Color.gray);
ingo@4213:         plot.setDomainGridlinesVisible(true);
ingo@4213:         plot.setRangeGridlinesVisible(true);
ingo@4213: 
ingo@4213:         return chart;
ingo@4213:     }
ingo@4213: 
ingo@4213:     protected XYSeries createSeries(CallMeta callMeta, DischargeTable dt)
ingo@4213:         throws IllegalArgumentException {
ingo@4213: 
ingo@4213:         double[][] xy = null;
ingo@4213: 
ingo@4213:         if (dt.getKind() == DischargeTables.MASTER) {
ingo@4213:             xy = DischargeTables.loadDischargeTableValues(dt,
ingo@4213:                 DischargeTables.MASTER_SCALE);
ingo@4213:         }
ingo@4213:         else {
ingo@4213:             xy = DischargeTables.loadDischargeTableValues(dt,
ingo@4213:                 DischargeTables.HISTORICAL_SCALE);
ingo@4213:         }
ingo@4213: 
ingo@4213:         XYSeries series = new XYSeries(createSeriesTitle(callMeta, dt), false);
ingo@4213:         for (int i = 0, n = xy[0].length; i < n; i++) {
ingo@4213:             series.add(xy[0][i], xy[1][i]);
ingo@4213:         }
ingo@4213: 
ingo@4213:         return series;
ingo@4213:     }
ingo@4213: 
ingo@4213:     protected String createSeriesTitle(CallMeta callMeta, DischargeTable dt)
ingo@4213:         throws IllegalArgumentException {
ingo@4213:         TimeInterval timeInterval = dt.getTimeInterval();
ingo@4227: 
ingo@4227:         if (timeInterval == null) {
ingo@4227:             return Resources.format(callMeta, DEFAULT_CHART_SERIES_TITLE);
ingo@4227:         }
ingo@4227: 
ingo@4213:         Date start = timeInterval.getStartTime();
ingo@4213:         Date end = timeInterval.getStopTime();
ingo@4213: 
ingo@4213:         if (start != null && end != null) {
ingo@4213:             return Resources.format(callMeta, I18N_CHART_SERIES_TITLE,
ingo@4213:                 DEFAULT_CHART_SERIES_TITLE, start, end);
ingo@4213:         }
ingo@4213:         else if (start != null) {
ingo@4213:             return Resources.format(callMeta, I18N_CHART_SERIES_TITLE_MASTER,
ingo@4213:                 DEFAULT_CHART_SERIES_TITLE, start);
ingo@4213:         }
ingo@4213:         else {
ingo@4213:             throw new IllegalArgumentException(
ingo@4213:                 "Missing start date of DischargeTable " + dt.getId());
ingo@4213:         }
ingo@4213:     }
ingo@4213: 
ingo@4213:     protected Gauge extractGauge(Document data) {
ingo@4213:         NodeList gauges = data.getElementsByTagName("gauge");
ingo@4213: 
ingo@4213:         if (gauges.getLength() > 0) {
ingo@4213:             String name = ((Element) gauges.item(0)).getAttribute("name");
ingo@4217: 
ingo@4217:             try {
ingo@4217:                 long officialNumber = Long.valueOf(name);
ingo@4217:                 return Gauge.getGaugeByOfficialNumber(officialNumber);
ingo@4217:             }
ingo@4217:             catch (NumberFormatException nfe) {
ingo@4217:                 // it seems, that the client uses the name of the gauge instead
ingo@4217:                 // of its official number
ingo@4217:             }
ingo@4217: 
ingo@4213:             if (name != null && name.length() > 0) {
ingo@4213:                 return GaugesFactory.getGauge(name);
ingo@4213:             }
ingo@4213:         }
ingo@4213: 
ingo@4213:         return null;
ingo@4213:     }
ingo@4213: 
ingo@4213:     protected TimeInterval extractTimeInterval(Document data) {
ingo@4213:         NodeList timeranges = data.getElementsByTagName("timerange");
ingo@4213: 
ingo@4213:         if (timeranges != null && timeranges.getLength() > 0) {
ingo@4213:             Element timerange = (Element) timeranges.item(0);
ingo@4213: 
ingo@4213:             String lower = timerange.getAttribute("lower");
ingo@4213:             String upper = timerange.getAttribute("upper");
ingo@4213: 
ingo@4213:             if (lower != null && upper != null) {
ingo@4213:                 try {
ingo@4213:                     Date d1 = DATE_FORMAT.parse(lower);
ingo@4213:                     Date d2 = DATE_FORMAT.parse(upper);
ingo@4213: 
ingo@4213:                     return new TimeInterval(d1, d2);
ingo@4213:                 }
ingo@4213:                 catch (ParseException pe) {
ingo@4213:                     log.warn("Wrong time format: " + pe.getMessage());
ingo@4213:                 }
ingo@4213:             }
ingo@4213:         }
ingo@4213: 
ingo@4213:         return null;
ingo@4213:     }
ingo@4213: 
ingo@4213:     protected List<DischargeTable> getDischargeTables(Gauge gauge,
ingo@4213:         TimeInterval timerange) {
ingo@4213:         List<DischargeTable> all = gauge.getDischargeTables();
ingo@4213:         Collections.sort(all);
ingo@4213: 
ingo@4213:         if (timerange == null) {
ingo@4213:             return all;
ingo@4213:         }
ingo@4213: 
ingo@4213:         List<DischargeTable> dts = new ArrayList<DischargeTable>(all.size());
ingo@4213:         long startDate = timerange.getStartTime().getTime();
ingo@4213:         long stopDate = timerange.getStopTime().getTime();
ingo@4213: 
ingo@4213:         for (DischargeTable dt : all) {
ingo@4213:             TimeInterval tmp = dt.getTimeInterval();
ingo@4230:             if (tmp == null) {
ingo@4230:                 // this should never happen because all discharge tables should
ingo@4230:                 // have a time interval set!
ingo@4230:                 continue;
ingo@4230:             }
ingo@4230: 
ingo@4213:             Date start = tmp.getStartTime();
ingo@4213:             Date stop = tmp.getStartTime();
ingo@4213: 
ingo@4213:             if (start.getTime() > startDate && start.getTime() < stopDate) {
ingo@4213:                 dts.add(dt);
ingo@4213:                 continue;
ingo@4213:             }
ingo@4213:             else if (stop != null && stop.getTime() < stopDate
ingo@4213:                 && stop.getTime() > startDate) {
ingo@4213:                 dts.add(dt);
ingo@4213:                 continue;
ingo@4213:             }
ingo@4213:         }
ingo@4213: 
ingo@4213:         return dts;
ingo@4213:     }
ingo@4213: }