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: teichmann@5831: import org.dive4elements.artifactdatabase.state.ArtifactAndFacet; 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; christian@3409: felix@1933: import java.awt.Font; felix@2750: import java.awt.geom.Point2D; felix@1933: ingo@385: import org.apache.log4j.Logger; 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: /** ingo@385: * An OutGenerator that generates duration curves. ingo@385: * ingo@385: * @author Ingo Weinzierl ingo@385: */ ingo@696: public class DurationCurveGenerator ingo@696: extends XYChartGenerator ingo@696: implements FacetTypes ingo@696: { felix@1933: public static enum YAXIS { felix@1933: W(0), felix@1933: Q(1); felix@2163: public int idx; felix@1933: private YAXIS(int c) { felix@1933: idx = c; felix@1933: } felix@1933: } felix@1933: teichmann@8202: /** Local log. */ teichmann@8202: private static Logger log = ingo@385: Logger.getLogger(DurationCurveGenerator.class); ingo@385: ingo@408: public static final String I18N_CHART_TITLE = ingo@408: "chart.duration.curve.title"; ingo@408: ingo@414: public static final String I18N_CHART_SUBTITLE = ingo@414: "chart.duration.curve.subtitle"; ingo@414: ingo@408: public static final String I18N_XAXIS_LABEL = ingo@408: "chart.duration.curve.xaxis.label"; ingo@408: tom@8248: public static final String I18N_YAXIS_LABEL_W = tom@8248: "chart.duration.curve.yaxis.label.w"; tom@8248: tom@8248: public static final String I18N_YAXIS_LABEL_Q = tom@8248: "chart.duration.curve.yaxis.label.q"; ingo@408: ingo@408: public static final String I18N_CHART_TITLE_DEFAULT = ingo@408: "Dauerlinie"; ingo@408: ingo@408: public static final String I18N_XAXIS_LABEL_DEFAULT = ingo@408: "Unterschreitungsdauer [Tage]"; ingo@408: ingo@385: ingo@385: public DurationCurveGenerator() { ingo@385: super(); ingo@385: } ingo@385: ingo@385: felix@1933: /** felix@1933: * Create Axis for given index. felix@1933: * @return axis with according internationalized label. felix@1933: */ felix@1933: @Override felix@1933: protected NumberAxis createYAxis(int index) { felix@1933: Font labelFont = new Font("Tahoma", Font.BOLD, 14); ingo@2000: String label = getYAxisLabel(index); ingo@2000: ingo@2049: 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: ingo@2048: @Override ingo@2048: protected String getDefaultChartTitle() { ingo@408: return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT); ingo@385: } ingo@385: ingo@385: ingo@414: @Override ingo@2048: protected String getDefaultChartSubtitle() { ingo@414: double[] dist = getRange(); ingo@414: ingo@414: Object[] args = new Object[] { ingo@414: getRiverName(), ingo@414: dist[0] ingo@414: }; ingo@414: ingo@1989: return msg(I18N_CHART_SUBTITLE, "", args); ingo@1989: } ingo@1989: ingo@1989: ingo@1989: @Override ingo@2051: protected String getDefaultXAxisLabel() { ingo@408: return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT); ingo@385: } ingo@385: ingo@385: ingo@2000: @Override ingo@2051: protected String getDefaultYAxisLabel(int index) { ingo@2000: String label = "default"; ingo@2000: if (index == YAXIS.W.idx) { tom@8248: label = msg(I18N_YAXIS_LABEL_W, new Object[] { getRiverUnit() }); ingo@2000: } ingo@2000: 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: ingo@2000: @Override tom@8856: protected boolean zoomX( tom@8856: XYPlot plot, tom@8856: ValueAxis axis, tom@8856: Bounds bounds, tom@8856: Range x tom@8856: ) { ingo@2587: 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@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 tom@8856: protected boolean zoomY( tom@8856: XYPlot plot, tom@8856: ValueAxis axis, tom@8856: Bounds bounds, tom@8856: Range x tom@8856: ) { ingo@2587: boolean zoomin = super.zoom(plot, axis, bounds, x); ingo@2421: ingo@2421: if (!zoomin && axis instanceof IdentifiableNumberAxis) { ingo@2421: 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@2421: ingo@695: @Override ingo@1684: public void doOut( felix@1944: ArtifactAndFacet artifactFacet, teichmann@6905: ThemeDocument attr, felix@1944: boolean visible ingo@1684: ) { felix@1944: 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: } ingo@385: ingo@696: if (name.equals(DURATION_W)) { ingo@2605: doWOut( ingo@2605: (WQDay) artifactFacet.getData(context), ingo@2605: artifactFacet, ingo@2605: attr, ingo@2605: visible); ingo@385: } ingo@696: else if (name.equals(DURATION_Q)) { ingo@2605: doQOut( ingo@2605: (WQDay) artifactFacet.getData(context), ingo@2605: artifactFacet, ingo@2605: attr, ingo@2605: visible); ingo@385: } tom@8331: else if (name.equals(MAINVALUES_Q) || name.equals(MAINVALUES_W)) { felix@1850: doAnnotations( teichmann@5864: (RiverAnnotation) artifactFacet.getData(context), ingo@2325: artifactFacet, ingo@2325: attr, ingo@2325: visible); felix@1850: } felix@2750: else if (name.equals(RELATIVE_POINT)) { felix@2750: doPointOut((Point2D) artifactFacet.getData(context), felix@2750: artifactFacet, felix@2750: attr, felix@2750: visible); felix@2750: } felix@2206: else if (FacetTypes.IS.MANUALPOINTS(name)) { ingo@2325: doPoints( ingo@2325: artifactFacet.getData(context), ingo@2325: artifactFacet, felix@2206: attr, visible, YAXIS.W.idx); felix@2206: } ingo@385: else { teichmann@8202: log.warn("Unknown facet name: " + name); ingo@385: return; ingo@385: } ingo@385: } ingo@385: ingo@385: ingo@385: /** ingo@385: * Creates the series for a duration curve's W facet. ingo@385: * ingo@385: * @param wqdays The WQDay store that contains the Ws. ingo@924: * @param theme ingo@385: */ ingo@2605: protected void doWOut( ingo@2605: WQDay wqdays, ingo@2605: ArtifactAndFacet aaf, teichmann@6905: ThemeDocument theme, ingo@2605: boolean visible ingo@2605: ) { teichmann@8202: log.debug("DurationCurveGenerator.doWOut"); ingo@385: ingo@2605: XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), theme); ingo@385: ingo@385: int size = wqdays.size(); ingo@385: for (int i = 0; i < size; i++) { ingo@385: int day = wqdays.getDay(i); ingo@385: 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: felix@2750: protected void doPointOut( teichmann@6905: Point2D point, felix@2750: ArtifactAndFacet aandf, teichmann@6905: ThemeDocument theme, teichmann@6905: boolean visible felix@2750: ){ teichmann@8202: log.debug("DurationCurveGenerator.doPointOut"); felix@2750: tom@8856: XYSeries series = tom@8856: new StyledXYSeries(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: /** ingo@385: * Creates the series for a duration curve's Q facet. ingo@385: * ingo@385: * @param wqdays The WQDay store that contains the Qs. ingo@924: * @param theme ingo@385: */ ingo@2605: protected void doQOut( ingo@2605: WQDay wqdays, ingo@2605: ArtifactAndFacet aaf, teichmann@6905: ThemeDocument theme, ingo@2605: boolean visible ingo@2605: ) { teichmann@8202: log.debug("DurationCurveGenerator.doQOut"); ingo@385: ingo@2605: XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), theme); ingo@385: ingo@385: int size = wqdays.size(); ingo@385: for (int i = 0; i < size; i++) { ingo@385: int day = wqdays.getDay(i); ingo@385: 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@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 ingo@2000: public String getId(int idx) { ingo@2000: 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 :