teichmann@5863: /* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde teichmann@5863: * Software engineering by Intevation GmbH teichmann@5863: * teichmann@5994: * This file is Free Software under the GNU AGPL (>=v3) teichmann@5863: * and comes with ABSOLUTELY NO WARRANTY! Check out the teichmann@5994: * documentation coming with Dive4Elements River for details. teichmann@5863: */ teichmann@5863: teichmann@5831: package org.dive4elements.river.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.jfree.chart.ChartFactory; ingo@4213: import org.jfree.chart.JFreeChart; felix@5344: import org.jfree.chart.plot.Marker; 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: teichmann@5831: import org.dive4elements.artifacts.CallMeta; teichmann@5831: import org.dive4elements.artifacts.GlobalContext; teichmann@5831: import org.dive4elements.river.artifacts.model.DischargeTables; teichmann@5831: import org.dive4elements.river.artifacts.model.GaugesFactory; teichmann@5831: import org.dive4elements.river.artifacts.resources.Resources; teichmann@5831: import org.dive4elements.river.backend.SessionHolder; teichmann@5831: import org.dive4elements.river.model.DischargeTable; teichmann@5831: import org.dive4elements.river.model.Gauge; teichmann@5831: import org.dive4elements.river.model.MainValue; teichmann@5831: import org.dive4elements.river.model.TimeInterval; ingo@4213: ingo@4213: felix@5342: /** Generate Discharge Table chart. */ 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: tom@8856: public static final String I18N_CHART_TITLE = tom@8856: "gauge.discharge.service.chart.title"; ingo@4213: public static final String DEFAULT_CHART_TITLE = "Pegel: XXX"; ingo@4213: tom@8856: public static final String I18N_CHART_X_AXIS_TITLE = tom@8856: "gauge.discharge.service.chart.x.title"; ingo@4213: public static final String DEFAULT_X_AXIS_TITLE = "Q [m^3/s]"; ingo@4213: tom@8856: public static final String I18N_CHART_Y_AXIS_TITLE = tom@8856: "gauge.discharge.service.chart.y.title"; ingo@4213: public static final String DEFAULT_Y_AXIS_TITLE = "W [cm]"; ingo@4213: tom@8856: public static final String I18N_CHART_SERIES_TITLE = tom@8856: "gauge.discharge.service.chart.series.title"; ingo@4213: public static final String DEFAULT_CHART_SERIES_TITLE = "Abflusskurve"; ingo@4213: tom@8856: public static final String I18N_CHART_SERIES_TITLE_MASTER = tom@8856: "gauge.discharge.service.chart.series.title.master"; tom@8856: public static final String DEFAULT_CHART_SERIES_TITLE_MASTER = tom@8856: "Aktuelle Abflusskurve"; ingo@4213: ingo@4213: public static final DateFormat DATE_FORMAT = DateFormat.getDateInstance( ingo@4213: DateFormat.SHORT, Locale.GERMANY); ingo@4213: ingo@4213: ingo@4213: @Override ingo@4213: protected void init() { tom@8572: SessionHolder.acquire(); ingo@4213: } ingo@4213: ingo@4213: @Override ingo@4213: protected void finish() { andre@8566: SessionHolder.release(); 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 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: felix@5344: applyMainValueMarkers( felix@5344: plot, felix@5360: gauge, felix@5344: callMeta); felix@5344: 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: aheinecke@6301: xy = DischargeTables.loadDischargeTableValues(dt); 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: felix@5344: felix@5360: /** Add domain markers to plot that indicate mainvalues. */ felix@5344: protected static void applyMainValueMarkers( felix@5344: XYPlot plot, felix@5360: Gauge gauge, felix@5344: CallMeta meta felix@5344: ) { felix@5360: String river = gauge.getRiver().getName(); felix@5360: double km = gauge.getStation().doubleValue(); felix@5344: felix@5360: // Get Gauge s mainvalues. felix@5360: List mainValues = gauge.getMainValues(); felix@5360: for (MainValue mainValue : mainValues) { felix@5360: if (mainValue.getMainValue().getType().getName().equals("Q")) { felix@5360: // Its a Q main value. felix@5360: Marker m = FixingsKMChartService.createQSectorMarker( felix@5360: mainValue.getValue().doubleValue(), felix@5360: mainValue.getMainValue().getName()); felix@5344: plot.addDomainMarker(m); felix@5344: } tom@8856: else if ( tom@8856: mainValue.getMainValue().getType().getName().equals("W") tom@8856: ) { felix@5360: // Its a W main value. felix@5360: Marker m = FixingsKMChartService.createQSectorMarker( felix@5360: mainValue.getValue().doubleValue(), felix@5360: mainValue.getMainValue().getName()); felix@5360: plot.addRangeMarker(m); felix@5360: } felix@5344: } felix@5344: } felix@5344: 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 getDischargeTables(Gauge gauge, ingo@4213: TimeInterval timerange) { ingo@4213: List 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 dts = new ArrayList(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: } felix@5342: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :