ingo@359: package de.intevation.flys.exports; ingo@359: felix@1028: import java.util.ArrayList; felix@1028: import java.util.List; felix@1028: ingo@359: import org.apache.log4j.Logger; ingo@359: ingo@375: import org.jfree.chart.JFreeChart; ingo@1677: import org.jfree.chart.LegendItem; ingo@1677: import org.jfree.chart.LegendItemCollection; ingo@375: import org.jfree.chart.axis.NumberAxis; ingo@422: import org.jfree.chart.axis.ValueAxis; ingo@375: import org.jfree.chart.plot.XYPlot; ingo@414: import org.jfree.chart.title.TextTitle; ingo@720: import org.jfree.data.Range; ingo@369: import org.jfree.data.xy.XYSeries; felix@1034: import org.jfree.ui.TextAnchor; ingo@364: ingo@359: import org.w3c.dom.Document; ingo@359: ingo@359: import de.intevation.artifacts.Artifact; ingo@359: ingo@695: import de.intevation.artifactdatabase.state.Facet; ingo@695: ingo@422: import de.intevation.flys.artifacts.FLYSArtifact; sascha@706: ingo@696: import de.intevation.flys.artifacts.model.FacetTypes; ingo@364: import de.intevation.flys.artifacts.model.WQKms; ingo@364: ingo@1677: import de.intevation.flys.jfree.FLYSAnnotation; ingo@1667: import de.intevation.flys.jfree.StickyAxisAnnotation; felix@1028: import de.intevation.flys.model.Annotation; ingo@1667: import de.intevation.flys.utils.FLYSUtils; ingo@359: felix@1035: felix@1035: /** ingo@359: * An OutGenerator that generates discharge curves. ingo@359: * ingo@359: * @author Ingo Weinzierl ingo@359: */ ingo@696: public class LongitudinalSectionGenerator ingo@696: extends XYChartGenerator ingo@696: implements FacetTypes ingo@696: { felix@1037: /** The logger that is used in this generator. */ ingo@359: private static Logger logger = ingo@359: Logger.getLogger(LongitudinalSectionGenerator.class); ingo@359: ingo@408: public static final String I18N_CHART_TITLE = ingo@408: "chart.longitudinal.section.title"; ingo@408: felix@1041: public static final String I18N_ANNOTATIONS_LABEL = felix@1041: "chart.longitudinal.annotations.label"; felix@1041: ingo@414: public static final String I18N_CHART_SUBTITLE = ingo@414: "chart.longitudinal.section.subtitle"; ingo@414: ingo@408: public static final String I18N_XAXIS_LABEL = ingo@408: "chart.longitudinal.section.xaxis.label"; ingo@408: ingo@408: public static final String I18N_YAXIS_LABEL = ingo@408: "chart.longitudinal.section.yaxis.label"; ingo@408: ingo@408: public static final String I18N_2YAXIS_LABEL = ingo@408: "chart.longitudinal.section.yaxis.second.label"; ingo@408: sascha@664: public static final String I18N_CHART_TITLE_DEFAULT = "W-L\u00e4ngsschnitt"; ingo@408: public static final String I18N_XAXIS_LABEL_DEFAULT = "km"; ingo@408: public static final String I18N_YAXIS_LABEL_DEFAULT = "W [NN + m]"; sascha@664: public static final String I18N_2YAXIS_LABEL_DEFAULT = "Q [m\u00b3/s]"; ingo@369: ingo@364: sascha@745: protected boolean inverted; sascha@745: felix@1041: /** List of annotations to insert in plot. */ ingo@1677: protected List annotations; felix@1041: felix@1041: ingo@359: public LongitudinalSectionGenerator() { ingo@359: super(); ingo@1677: annotations = new ArrayList(); ingo@369: } ingo@369: ingo@369: ingo@369: protected String getChartTitle() { ingo@408: return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT); ingo@369: } ingo@369: sascha@745: public boolean isInverted() { sascha@745: return inverted; sascha@745: } sascha@745: sascha@745: public void setInverted(boolean inverted) { sascha@745: this.inverted = inverted; sascha@745: } ingo@369: ingo@414: @Override ingo@414: protected void addSubtitles(JFreeChart chart) { ingo@414: double[] dist = getRange(); ingo@414: ingo@414: Object[] args = new Object[] { ingo@414: getRiverName(), ingo@414: dist[0], ingo@414: dist[1] ingo@414: }; ingo@414: ingo@414: String subtitle = msg(I18N_CHART_SUBTITLE, "", args); ingo@414: chart.addSubtitle(new TextTitle(subtitle)); ingo@414: } ingo@414: felix@1028: @Override felix@1028: public JFreeChart generateChart() { felix@1028: JFreeChart c = super.generateChart(); felix@1028: XYPlot p = (XYPlot) c.getPlot(); felix@1028: felix@1034: redoAnnotations(p, p.getDomainAxis()); felix@1034: felix@1028: return c; felix@1028: } felix@1028: ingo@414: ingo@369: protected String getXAxisLabel() { ingo@1667: FLYSArtifact flys = (FLYSArtifact) master; ingo@1667: ingo@1667: return msg( ingo@1667: I18N_XAXIS_LABEL, ingo@1667: I18N_XAXIS_LABEL_DEFAULT, ingo@1667: new Object[] { FLYSUtils.getRiver(flys).getName() }); ingo@369: } ingo@369: ingo@369: ingo@369: protected String getYAxisLabel() { ingo@1667: FLYSArtifact flys = (FLYSArtifact) master; ingo@1667: ingo@1673: String unit = FLYSUtils.getRiver(flys).getWstUnit().getName(); ingo@1667: ingo@1667: return msg( ingo@1667: I18N_YAXIS_LABEL, ingo@1667: I18N_YAXIS_LABEL_DEFAULT, ingo@1667: new Object[] { unit }); ingo@369: } ingo@369: ingo@369: ingo@375: protected void adjustAxes(XYPlot plot) { ingo@375: super.adjustAxes(plot); ingo@375: ingo@408: NumberAxis qAxis = new NumberAxis( ingo@408: msg(I18N_2YAXIS_LABEL, I18N_2YAXIS_LABEL_DEFAULT)); ingo@375: ingo@666: plot.setRangeAxis(1, qAxis); ingo@422: ingo@422: invertXAxis(plot.getDomainAxis()); ingo@422: } ingo@422: ingo@422: ingo@422: /** felix@1034: * Remove all annotations from plot and re-insert them at an approximately felix@1034: * okay position. The followed approach is naive but side-effect free. felix@1037: * felix@1037: * @param plot the plot. felix@1037: * @param axis the value axis. felix@1034: */ felix@1034: protected void redoAnnotations(XYPlot plot, ValueAxis axis) { felix@1034: plot.clearAnnotations(); felix@1037: // TODO Position calculation could/should be done in felix@1038: // the StickyAxisAnnotation-Implementation itself. ingo@1677: ingo@1677: int idx = 0; ingo@1677: ValueAxis yAxis = plot.getRangeAxis(idx); ingo@1677: ingo@1677: if (yAxis == null) { ingo@1677: if (plot.getRangeAxisCount() >= 2) { ingo@1677: yAxis = plot.getRangeAxis(++idx); ingo@1677: } ingo@1677: } ingo@1677: ingo@1677: if (yAxis == null) { ingo@1677: // XXX There is no y-axis that might be used to add annotations. If ingo@1677: // we absolutely want to display annotations, we need to create a ingo@1677: // virtual dataset for an axis. ingo@1677: return; ingo@1677: } ingo@1677: felix@1034: float posY = 140.f; ingo@1677: posY = (float) yAxis.getRange().getLowerBound(); ingo@1677: // Add some (2%) space between Text and axis. ingo@1677: posY += 0.02f * (yAxis.getRange().getUpperBound() ingo@1677: - yAxis.getRange().getLowerBound()); ingo@1677: ingo@1677: LegendItemCollection lic = plot.getLegendItems(); felix@1034: felix@1036: // Add all annotations. ingo@1677: for (FLYSAnnotation fa: annotations) { ingo@1677: lic.add(new LegendItem(fa.getLabel())); felix@1034: ingo@1677: for (Annotation a: fa.getAnnotations()) { ingo@1677: float posX = (float) a.getRange().getA().doubleValue(); ingo@1677: String text = a.getPosition().getValue(); ingo@1677: ingo@1677: StickyAxisAnnotation ta = new StickyAxisAnnotation( ingo@1677: text, ingo@1677: posX, ingo@1677: posY); ingo@1677: ingo@1677: double rotation = 270.0f * (Math.PI / 180.0f); ingo@1677: ta.setRotationAngle(rotation); ingo@1677: ta.setRotationAnchor(TextAnchor.CENTER_LEFT); ingo@1677: ta.setTextAnchor(TextAnchor.CENTER_LEFT); ingo@1677: plot.getRenderer(idx).addAnnotation(ta); ingo@1677: } felix@1034: } ingo@1677: ingo@1677: plot.setFixedLegendItems(lic); felix@1034: } felix@1034: felix@1034: felix@1034: /** felix@1653: * Create a range that includes 0 (for the Q axis). felix@1653: * @param range range with which to look up upper bound. felix@1653: * @return range with 0 included. felix@1653: */ felix@1653: protected Range createSecondAxisRange(Range range) { felix@1653: return new Range(0d, range.getUpperBound()); felix@1653: } felix@1653: felix@1653: felix@1653: /** felix@1653: * This method overrides the XYChartGenerators zoomY method to be able to felix@1653: * modify the range of the Q axis (here, it shall include 0). ingo@720: */ ingo@720: @Override ingo@720: protected boolean zoomY(XYPlot plot, ValueAxis axis, Range range, Range x) { ingo@734: if (plot.getRangeAxisIndex(axis) == 1) { felix@1653: // We want the Q axis to start at 0 if no zooming has been done. felix@1653: range = createSecondAxisRange(range); ingo@720: } ingo@720: ingo@734: return super.zoomY(plot, axis, range, x); ingo@720: } ingo@720: ingo@720: ingo@720: /** ingo@422: * This method inverts the x-axis based on the kilometer information of the ingo@422: * selected river. If the head of the river is at kilometer 0, the axis is ingo@422: * not inverted, otherwise it is. ingo@422: * ingo@422: * @param xaxis The domain axis. ingo@422: */ ingo@422: protected void invertXAxis(ValueAxis xaxis) { ingo@422: sascha@745: if (inverted) { sascha@745: logger.debug("Invert X-Axis."); ingo@422: xaxis.setInverted(true); ingo@422: } ingo@359: } ingo@359: ingo@359: ingo@695: public void doOut(Artifact artifact, Facet facet, Document attr) { ingo@695: String name = facet.getName(); ingo@359: ingo@695: logger.debug("LongitudinalSectionGenerator.doOut: " + name); ingo@695: ingo@695: if (name == null) { ingo@369: logger.error("No facet name for doOut(). No output generated!"); ingo@369: return; ingo@369: } ingo@369: ingo@696: FLYSArtifact flys = (FLYSArtifact) artifact; ingo@696: Facet f = flys.getNativeFacet(facet); ingo@696: ingo@696: if (f == null) { ingo@696: return; ingo@369: } ingo@696: ingo@696: if (name.equals(LONGITUDINAL_W)) { ingo@924: doWOut((WQKms) f.getData(artifact, context), attr); ingo@696: } ingo@696: else if (name.equals(LONGITUDINAL_Q)) { ingo@924: doQOut((WQKms) f.getData(artifact, context), attr); ingo@369: } felix@1028: else if (name.equals(LONGITUDINAL_ANNOTATION)) { felix@1028: doAnnotationsOut(f.getData(artifact, context), attr); felix@1028: } ingo@369: else { ingo@695: logger.warn("Unknown facet name: " + name); ingo@369: return; ingo@369: } ingo@369: } ingo@369: ingo@369: felix@1037: /** felix@1037: * Register annotations available for the diagram. felix@1037: * felix@1037: * @param o list of annotations (data of facet). felix@1041: * @param theme yet ignored. felix@1037: */ felix@1028: protected void doAnnotationsOut(Object o, Document theme) { felix@1028: logger.debug("LongitudinalSectionGenerator.doAnnotationsOut"); felix@1041: ingo@1677: // Add all annotations in list o to our annotation pool. ingo@1677: FLYSAnnotation fa = (FLYSAnnotation) o; ingo@1677: annotations.add(fa); felix@1028: } felix@1028: felix@1028: ingo@369: /** ingo@369: * Process the output for W facets in a longitudinal section curve. ingo@369: * ingo@369: * @param wqkms An array of WQKms values. ingo@924: * @param theme The theme that contains styling information. ingo@369: */ ingo@924: protected void doWOut(WQKms wqkms, Document theme) { ingo@369: logger.debug("LongitudinalSectionGenerator.doWOut"); ingo@359: ingo@924: XYSeries series = new StyledXYSeries(getSeriesName(wqkms, "W"), theme); ingo@369: sascha@719: int size = wqkms.size(); ingo@696: ingo@696: if (logger.isDebugEnabled()) { ingo@696: if (wqkms.size() > 0) { ingo@696: logger.debug("Generate series: " + series.getKey()); sascha@925: logger.debug("Start km: " + wqkms.getKm(0)); sascha@925: logger.debug("End km: " + wqkms.getKm(size-1)); ingo@696: logger.debug("Values : " + size); ingo@369: } ingo@696: } ingo@369: ingo@696: for (int i = 0; i < size; i++) { sascha@925: series.add(wqkms.getKm(i), wqkms.getW(i)); ingo@369: } ingo@696: ingo@923: addFirstAxisSeries(series); sascha@745: sascha@745: if (wqkms.guessWaterIncreasing()) { sascha@745: setInverted(true); sascha@745: } ingo@369: } ingo@369: ingo@369: ingo@369: /** ingo@369: * Process the output for Q facets in a longitudinal section curve. ingo@369: * ingo@369: * @param wqkms An array of WQKms values. ingo@924: * @param theme The theme that contains styling information. ingo@369: */ ingo@924: protected void doQOut(WQKms wqkms, Document theme) { ingo@369: logger.debug("LongitudinalSectionGenerator.doQOut"); ingo@369: ingo@924: XYSeries series = new StyledXYSeries(getSeriesName(wqkms, "Q"), theme); ingo@369: sascha@719: int size = wqkms.size(); ingo@369: ingo@696: if (logger.isDebugEnabled()) { ingo@696: if (wqkms.size() > 0) { ingo@696: logger.debug("Generate series: " + series.getKey()); sascha@925: logger.debug("Start km: " + wqkms.getKm(0)); sascha@925: logger.debug("End km: " + wqkms.getKm(size-1)); ingo@696: logger.debug("Values : " + size); ingo@696: } ingo@696: } ingo@369: ingo@696: for (int i = 0; i < size; i++) { sascha@925: series.add(wqkms.getKm(i), wqkms.getQ(i)); ingo@369: } ingo@696: ingo@923: addSecondAxisSeries(series); sascha@745: sascha@745: if (wqkms.guessWaterIncreasing()) { sascha@745: setInverted(true); sascha@745: } ingo@369: } ingo@369: ingo@369: felix@1041: /** felix@1041: * Get name of series (displayed in legend). felix@1041: * @return name of the series. felix@1041: */ ingo@448: protected String getSeriesName(WQKms wqkms, String mode) { ingo@448: String name = wqkms.getName(); ingo@448: String prefix = name != null && name.indexOf(mode) >= 0 ? null : mode; ingo@448: ingo@448: return prefix != null && prefix.length() > 0 ingo@448: ? prefix + "(" + name +")" ingo@448: : name; ingo@359: } ingo@359: } ingo@359: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :