teichmann@5863: /* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde teichmann@5863: * Software engineering by Intevation GmbH teichmann@5863: * teichmann@5863: * This file is Free Software under the GNU AGPL (>=v3) teichmann@5863: * and comes with ABSOLUTELY NO WARRANTY! Check out the teichmann@5863: * 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@5831: import org.dive4elements.river.jfree.FLYSAnnotation; teichmann@5831: import org.dive4elements.river.jfree.StyledXYSeries; 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; christian@3409: import org.w3c.dom.Document; 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: felix@3442: /** Local logger. */ ingo@385: private static Logger logger = 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: ingo@408: public static final String I18N_YAXIS_LABEL = ingo@408: "chart.duration.curve.yaxis.label"; 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@408: public static final String I18N_YAXIS_LABEL_DEFAULT = ingo@408: "W [NN + m]"; 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) { ingo@2051: label = msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT); ingo@2000: } ingo@2000: else if (index == YAXIS.Q.idx) { ingo@2000: // TODO i18n for this label ingo@2000: label = "Q [m\u00b3/s]"; ingo@2000: //label = msg(get2YAxisLabelKey(), get2YAxisDefaultLabel()); ingo@2000: } ingo@2000: ingo@2000: return label; ingo@2000: } ingo@2000: ingo@2000: ingo@2000: @Override ingo@2587: protected boolean zoomX(XYPlot plot, ValueAxis axis, Bounds bounds, Range x) { 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 ingo@2587: protected boolean zoomY(XYPlot plot, ValueAxis axis, Bounds bounds, Range x) { 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, felix@1944: Document attr, felix@1944: boolean visible ingo@1684: ) { felix@1944: String name = artifactFacet.getFacetName(); ingo@385: ingo@695: logger.debug("DurationCurveGenerator.doOut: " + name); ingo@695: ingo@695: if (name == null || name.length() == 0) { ingo@385: logger.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: } felix@2163: else if (name.equals(DURATION_MAINVALUES_Q) felix@1850: || name.equals(MAINVALUES_Q) felix@1850: || name.equals(COMPUTED_DISCHARGE_MAINVALUES_W) felix@1850: || name.equals(MAINVALUES_W) felix@1850: ) { felix@1850: doAnnotations( felix@1944: (FLYSAnnotation) 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 { ingo@695: logger.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, ingo@2605: Document theme, ingo@2605: boolean visible ingo@2605: ) { ingo@385: logger.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( felix@2750: Point2D point, felix@2750: ArtifactAndFacet aandf, felix@2750: Document theme, felix@2750: boolean visible felix@2750: ){ felix@2750: logger.debug("DurationCurveGenerator.doPointOut"); felix@2750: felix@2750: XYSeries series = 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, ingo@2605: Document theme, ingo@2605: boolean visible ingo@2605: ) { ingo@385: logger.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: felix@1931: // MainValue-Annotations should be visualized by 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 :