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.exports; ingo@385: gernotbelger@9312: import java.awt.Font; gernotbelger@9312: import java.awt.geom.Point2D; gernotbelger@9312: gernotbelger@9312: import org.apache.log4j.Logger; teichmann@5831: import org.dive4elements.artifactdatabase.state.ArtifactAndFacet; gernotbelger@9123: import org.dive4elements.artifacts.CallContext; teichmann@5831: import org.dive4elements.river.artifacts.model.FacetTypes; teichmann@5831: import org.dive4elements.river.artifacts.model.WQDay; teichmann@5831: import org.dive4elements.river.jfree.Bounds; teichmann@5864: import org.dive4elements.river.jfree.RiverAnnotation; teichmann@5831: import org.dive4elements.river.jfree.StyledXYSeries; teichmann@6905: import org.dive4elements.river.themes.ThemeDocument; ingo@385: import org.jfree.chart.axis.NumberAxis; ingo@733: import org.jfree.chart.axis.ValueAxis; ingo@385: import org.jfree.chart.plot.XYPlot; ingo@733: import org.jfree.data.Range; ingo@385: import org.jfree.data.xy.XYSeries; felix@1850: ingo@385: /** ingo@385: * An OutGenerator that generates duration curves. ingo@385: * ingo@385: * @author Ingo Weinzierl ingo@385: */ gernotbelger@9312: public class DurationCurveGenerator extends XYChartGenerator implements FacetTypes { felix@1933: public static enum YAXIS { gernotbelger@9312: W(0), Q(1); felix@2163: public int idx; gernotbelger@9312: gernotbelger@9312: private YAXIS(final int c) { gernotbelger@9312: this.idx = c; felix@1933: } felix@1933: } felix@1933: teichmann@8202: /** Local log. */ gernotbelger@9312: private static Logger log = Logger.getLogger(DurationCurveGenerator.class); ingo@408: gernotbelger@9312: public static final String I18N_CHART_TITLE = "chart.duration.curve.title"; ingo@408: gernotbelger@9312: public static final String I18N_CHART_SUBTITLE = "chart.duration.curve.subtitle"; ingo@408: gernotbelger@9312: public static final String I18N_XAXIS_LABEL = "chart.duration.curve.xaxis.label"; ingo@408: gernotbelger@9312: public static final String I18N_YAXIS_LABEL_W = "chart.duration.curve.yaxis.label.w"; gernotbelger@9312: gernotbelger@9312: public static final String I18N_YAXIS_LABEL_Q = "common.export.csv.header.q"; gernotbelger@9312: gernotbelger@9312: public static final String I18N_CHART_TITLE_DEFAULT = "Dauerlinie"; gernotbelger@9312: gernotbelger@9312: public static final String I18N_XAXIS_LABEL_DEFAULT = "Unterschreitungsdauer [Tage]"; ingo@385: ingo@385: public DurationCurveGenerator() { ingo@385: super(); ingo@385: } ingo@385: felix@1933: /** felix@1933: * Create Axis for given index. gernotbelger@9312: * felix@1933: * @return axis with according internationalized label. felix@1933: */ felix@1933: @Override gernotbelger@9312: protected NumberAxis createYAxis(final int index) { gernotbelger@9312: final Font labelFont = new Font("Tahoma", Font.BOLD, 14); gernotbelger@9312: final String label = getYAxisLabel(index); ingo@2000: gernotbelger@9312: final NumberAxis axis = createNumberAxis(index, label); felix@1951: if (index == YAXIS.W.idx) { felix@1951: axis.setAutoRangeIncludesZero(false); felix@1951: } felix@1933: axis.setLabelFont(labelFont); felix@1933: return axis; felix@1933: } felix@1933: ingo@2048: @Override gernotbelger@9123: protected String getDefaultChartTitle(final CallContext context) { ingo@408: return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT); ingo@385: } ingo@385: ingo@414: @Override gernotbelger@9123: protected String getDefaultChartSubtitle(final CallContext context) { gernotbelger@9312: final double[] dist = getRange(); ingo@414: gernotbelger@9312: final Object[] args = new Object[] { getRiverName(), dist[0] }; ingo@414: ingo@1989: return msg(I18N_CHART_SUBTITLE, "", args); ingo@1989: } ingo@1989: ingo@1989: @Override gernotbelger@9123: protected String getDefaultXAxisLabel(final CallContext context) { ingo@408: return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT); ingo@385: } ingo@385: ingo@2000: @Override gernotbelger@9312: protected String getDefaultYAxisLabel(final int index) { ingo@2000: String label = "default"; ingo@2000: if (index == YAXIS.W.idx) { gernotbelger@9123: label = msg(I18N_YAXIS_LABEL_W, I18N_YAXIS_LABEL_W, new Object[] { getRiverUnit() }); gernotbelger@9312: } else if (index == YAXIS.Q.idx) { tom@8248: label = msg(I18N_YAXIS_LABEL_Q); ingo@2000: } ingo@2000: ingo@2000: return label; ingo@2000: } ingo@2000: ingo@2000: @Override gernotbelger@9312: protected boolean zoomX(final XYPlot plot, final ValueAxis axis, final Bounds bounds, final Range x) { gernotbelger@9312: final boolean zoomin = super.zoom(plot, axis, bounds, x); ingo@733: ingo@733: if (!zoomin) { ingo@733: axis.setLowerBound(0d); ingo@733: } ingo@733: ingo@1713: axis.setUpperBound(364); ingo@1713: ingo@733: return zoomin; ingo@733: } ingo@733: ingo@2421: /** ingo@2421: * This method overrides the method in the parent class to set the lower ingo@2421: * bounds of the Q axis to 0. This axis should never display negative ingo@2421: * values on its own. ingo@2421: */ ingo@2421: @Override gernotbelger@9312: protected boolean zoomY(final XYPlot plot, final ValueAxis axis, final Bounds bounds, final Range x) { gernotbelger@9312: final boolean zoomin = super.zoom(plot, axis, bounds, x); ingo@2421: ingo@2421: if (!zoomin && axis instanceof IdentifiableNumberAxis) { gernotbelger@9312: final String id = ((IdentifiableNumberAxis) axis).getId(); ingo@2421: ingo@2421: if (YAXIS.Q.toString().equals(id)) { ingo@2421: axis.setLowerBound(0d); ingo@2421: } ingo@2421: } ingo@2421: ingo@2421: return zoomin; ingo@2421: } ingo@2421: ingo@695: @Override gernotbelger@9312: public void doOut(final ArtifactAndFacet artifactFacet, final ThemeDocument attr, final boolean visible) { gernotbelger@9312: final String name = artifactFacet.getFacetName(); ingo@385: teichmann@8202: log.debug("DurationCurveGenerator.doOut: " + name); ingo@695: ingo@695: if (name == null || name.length() == 0) { teichmann@8202: log.error("No facet given. Cannot create dataset."); ingo@385: return; ingo@385: } gernotbelger@9312: gernotbelger@9123: final CallContext context = getContext(); ingo@385: ingo@696: if (name.equals(DURATION_W)) { gernotbelger@9312: doWOut((WQDay) artifactFacet.getData(context), artifactFacet, attr, visible); gernotbelger@9312: } else if (name.equals(DURATION_Q)) { gernotbelger@9312: doQOut((WQDay) artifactFacet.getData(context), artifactFacet, attr, visible); gernotbelger@9312: } else if (name.equals(MAINVALUES_Q) || name.equals(MAINVALUES_W)) { gernotbelger@9312: doAnnotations((RiverAnnotation) artifactFacet.getData(context), artifactFacet, attr, visible); gernotbelger@9312: } else if (name.equals(RELATIVE_POINT)) { gernotbelger@9312: doPointOut((Point2D) artifactFacet.getData(context), artifactFacet, attr, visible); gernotbelger@9312: } else if (FacetTypes.IS.MANUALPOINTS(name)) { gernotbelger@9312: doPoints(artifactFacet.getData(context), artifactFacet, attr, visible, YAXIS.W.idx); gernotbelger@9312: } else { teichmann@8202: log.warn("Unknown facet name: " + name); ingo@385: return; ingo@385: } ingo@385: } ingo@385: ingo@385: /** ingo@385: * Creates the series for a duration curve's W facet. ingo@385: * gernotbelger@9312: * @param wqdays gernotbelger@9312: * The WQDay store that contains the Ws. ingo@924: * @param theme ingo@385: */ gernotbelger@9312: protected void doWOut(final WQDay wqdays, final ArtifactAndFacet aaf, final ThemeDocument theme, final boolean visible) { teichmann@8202: log.debug("DurationCurveGenerator.doWOut"); ingo@385: gernotbelger@9556: final XYSeries series = new StyledXYSeries(aaf.getFacetName(), aaf.getFacetDescription(), theme); ingo@385: gernotbelger@9312: final int size = wqdays.size(); ingo@385: for (int i = 0; i < size; i++) { gernotbelger@9312: final int day = wqdays.getDay(i); gernotbelger@9312: final double w = wqdays.getW(i); ingo@385: christian@3409: series.add(day, w); ingo@385: } ingo@385: felix@1933: addAxisSeries(series, YAXIS.W.idx, visible); ingo@385: } ingo@385: gernotbelger@9312: protected void doPointOut(final Point2D point, final ArtifactAndFacet aandf, final ThemeDocument theme, final boolean visible) { teichmann@8202: log.debug("DurationCurveGenerator.doPointOut"); felix@2750: gernotbelger@9556: final XYSeries series = new StyledXYSeries(aandf.getFacetName(), aandf.getFacetDescription(), theme); felix@2750: felix@2750: series.add(point.getX(), point.getY()); felix@2750: felix@2750: addAxisSeries(series, YAXIS.W.idx, visible); felix@2750: } felix@2750: ingo@385: /** ingo@385: * Creates the series for a duration curve's Q facet. ingo@385: * gernotbelger@9312: * @param wqdays gernotbelger@9312: * The WQDay store that contains the Qs. ingo@924: * @param theme ingo@385: */ gernotbelger@9312: protected void doQOut(final WQDay wqdays, final ArtifactAndFacet aaf, final ThemeDocument theme, final boolean visible) { teichmann@8202: log.debug("DurationCurveGenerator.doQOut"); ingo@385: gernotbelger@9556: final XYSeries series = new StyledXYSeries(aaf.getFacetName(), aaf.getFacetDescription(), theme); ingo@385: gernotbelger@9312: final int size = wqdays.size(); ingo@385: for (int i = 0; i < size; i++) { gernotbelger@9312: final int day = wqdays.getDay(i); gernotbelger@9312: final double q = wqdays.getQ(i); ingo@385: christian@3409: series.add(day, q); ingo@385: } ingo@385: felix@1933: addAxisSeries(series, YAXIS.Q.idx, visible); ingo@385: } ingo@385: ingo@2000: @Override ingo@2000: protected YAxisWalker getYAxisWalker() { ingo@2000: return new YAxisWalker() { ingo@2000: @Override ingo@2000: public int length() { ingo@2000: return YAXIS.values().length; ingo@2000: } ingo@2000: ingo@2000: @Override gernotbelger@9312: public String getId(final int idx) { gernotbelger@9312: final YAXIS[] yaxes = YAXIS.values(); ingo@2000: return yaxes[idx].toString(); ingo@2000: } ingo@2000: }; ingo@2000: } ingo@2000: tom@8856: // MainValue-Annotations should be visualized by tom@8856: // a line that goes to the curve itself. ingo@385: } ingo@385: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :