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; felix@5344: import java.util.Map; 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; 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: ingo@4213: import de.intevation.artifacts.CallMeta; ingo@4213: import de.intevation.artifacts.GlobalContext; ingo@4213: import de.intevation.flys.artifacts.model.DischargeTables; felix@5344: import de.intevation.flys.artifacts.model.GaugeFinder; felix@5344: import de.intevation.flys.artifacts.model.GaugeFinderFactory; felix@5344: import de.intevation.flys.artifacts.model.GaugeRange; 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: 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: 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 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@5344: gauge.getRiver().getName(), felix@5344: gauge.getStation().doubleValue(), 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: 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: felix@5344: felix@5344: /** Add domain markers to plot that indicate (only some Q) mainvalues. */ felix@5344: protected static void applyMainValueMarkers( felix@5344: XYPlot plot, felix@5344: String river, felix@5344: double km, felix@5344: CallMeta meta felix@5344: ) { felix@5344: GaugeFinderFactory ggf = GaugeFinderFactory.getInstance(); felix@5344: GaugeFinder gf = ggf.getGaugeFinder(river); felix@5344: felix@5344: if (gf == null) { felix@5344: log.warn("No gauge finder found for river '" + river + "'"); felix@5344: return; felix@5344: } felix@5344: felix@5344: GaugeRange gr = gf.find(km); felix@5344: if (gr == null) { felix@5344: log.debug("No gauge range found for km " felix@5344: + km + " on river " + river + "."); felix@5344: return; felix@5344: } felix@5344: felix@5344: if (log.isDebugEnabled()) { felix@5344: log.debug(gr); felix@5344: } felix@5344: felix@5344: for (Map.Entry entry: gr.getMainValues().entrySet()) { felix@5344: Marker m = FixingsKMChartService.createQSectorMarker( felix@5344: entry.getValue(), felix@5344: entry.getKey()); felix@5344: felix@5344: if (m != null) { felix@5344: plot.addDomainMarker(m); felix@5344: } 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 :