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@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@1077: import org.jfree.data.xy.XYSeriesCollection;
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:
felix@1028: import de.intevation.flys.model.Annotation;
felix@1040: import de.intevation.flys.jfree.StickyAxisAnnotation;
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. */
felix@1028: protected List annotations;
ingo@364:
felix@1041: /** Pseudo-Dataseries to have a legend for the annotations. */
felix@1041: protected XYSeriesCollection pseudoAnnotationData = null;
felix@1041:
felix@1041:
ingo@359: public LongitudinalSectionGenerator() {
ingo@359: super();
felix@1028: 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@408: return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT);
ingo@369: }
ingo@369:
ingo@369:
ingo@369: protected String getYAxisLabel() {
ingo@408: return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT);
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.
felix@1034: ValueAxis yAxis = plot.getRangeAxis();
felix@1034: float posY = 140.f;
felix@1034: if (yAxis != null) {
felix@1034: posY = (float) yAxis.getRange().getLowerBound();
felix@1037: // Add some (2%) space between Text and axis.
felix@1037: posY += 0.02f * (yAxis.getRange().getUpperBound()
felix@1034: - yAxis.getRange().getLowerBound());
felix@1034: }
felix@1034:
felix@1036: // Add all annotations.
felix@1034: for (Annotation a: annotations) {
felix@1034: float posX = (float) a.getRange().getA().doubleValue();
felix@1034: String text = a.getPosition().getValue();
felix@1034:
felix@1038: StickyAxisAnnotation ta = new StickyAxisAnnotation(text, posX, posY);
felix@1034: double rotation = 270.0f * (Math.PI / 180.0f);
felix@1034: ta.setRotationAngle(rotation);
felix@1034: ta.setRotationAnchor(TextAnchor.CENTER_LEFT);
felix@1034: ta.setTextAnchor(TextAnchor.CENTER_LEFT);
felix@1034: plot.getRenderer().addAnnotation(ta);
felix@1034: }
felix@1034: }
felix@1034:
felix@1034:
felix@1034: /**
ingo@720: * This method overrides the XYChartGenerators zoomY method to include the 0
ingo@720: * value on the Q axis.
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) {
ingo@734: // we want the Q axis to start at 0 if no zooming has been done
ingo@734: range = new Range(0d, range.getUpperBound());
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@1028: this.annotations = (List) o;
felix@1041:
felix@1077: // Add (empty) pseudo series to have legend entry for annotations.
felix@1041: if (pseudoAnnotationData == null) {
felix@1041: pseudoAnnotationData = new XYSeriesCollection();
felix@1041: }
felix@1041: // Localized legend.
felix@1041: Object[] args = new Object[] {getRiverName()};
felix@1041: String label = msg(I18N_ANNOTATIONS_LABEL, "", args);
felix@1041: pseudoAnnotationData.addSeries(new StyledXYSeries(label, theme));
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: * Add datasets to plot.
felix@1041: * @param plot plot to add datasets to.
felix@1041: */
felix@1041: @Override
felix@1041: protected void addDatasets(XYPlot plot) {
felix@1041: super.addDatasets(plot);
felix@1041: if (pseudoAnnotationData != null) {
felix@1041: plot.setDataset(2, pseudoAnnotationData);
felix@1041: }
felix@1041: }
felix@1041:
felix@1041:
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 :