# HG changeset patch # User Andre Heinecke # Date 1379517133 -7200 # Node ID 06a9a241faac9c2ca3b1bfba60d654c357692598 # Parent 599d3c48474c837dcbe4dcef117267caad27a98c Factor out annotation handling code diff -r 599d3c48474c -r 06a9a241faac artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator2.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator2.java Wed Sep 18 16:26:12 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator2.java Wed Sep 18 17:12:13 2013 +0200 @@ -21,12 +21,10 @@ import org.dive4elements.river.artifacts.resources.Resources; import org.dive4elements.river.collections.D4EArtifactCollection; import org.dive4elements.river.jfree.Bounds; -import org.dive4elements.river.jfree.CollisionFreeXYTextAnnotation; import org.dive4elements.river.jfree.DoubleBounds; import org.dive4elements.river.jfree.EnhancedLineAndShapeRenderer; import org.dive4elements.river.jfree.RiverAnnotation; import org.dive4elements.river.jfree.StableXYDifferenceRenderer; -import org.dive4elements.river.jfree.StickyAxisAnnotation; import org.dive4elements.river.jfree.Style; import org.dive4elements.river.jfree.StyledAreaSeriesCollection; import org.dive4elements.river.jfree.StyledSeries; @@ -60,8 +58,6 @@ import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItem; import org.jfree.chart.LegendItemCollection; -import org.jfree.chart.annotations.XYLineAnnotation; -import org.jfree.chart.annotations.XYTextAnnotation; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; @@ -77,15 +73,9 @@ import org.dive4elements.river.utils.Formatter; /** - * The base class for chart creation. It should provide some basic things that - * equal in all chart types. + * Implementation of the OutGenerator interface for charts. + * It should provide some basic things that equal in all chart types. * - * Annotations are added as RiverAnnotations and come in mutliple basic forms: - * TextAnnotations are labels somewhere in data space, StickyAnnotations are - * labels of a slice or line in one data dimension (i.e. visualized as label - * on a single axis). - * - * @author Ingo Weinzierl */ public abstract class ChartGenerator2 implements OutGenerator { @@ -99,7 +89,6 @@ public static final int DEFAULT_FONT_SIZE = 12; public static final String DEFAULT_FONT_NAME = "Tahoma"; - protected static float ANNOTATIONS_AXIS_OFFSET = 0.02f; public static final String XPATH_CHART_SIZE = "/art:action/art:attributes/art:size"; @@ -155,7 +144,6 @@ datasets = new TreeMap(); } - /** * Adds annotations to list. The given annotation will be visible. */ @@ -164,313 +152,6 @@ } /** - * Add a text and a line annotation. - * @param area convenience to determine positions in plot. - * @param theme (optional) theme document - */ - protected void addStickyAnnotation( - StickyAxisAnnotation annotation, - XYPlot plot, - ChartArea area, - LineStyle lineStyle, - TextStyle textStyle, - ThemeDocument theme - ) { - // OPTIMIZE pre-calculate area-related values - final float TEXT_OFF = 0.03f; - - XYLineAnnotation lineAnnotation = null; - XYTextAnnotation textAnnotation = null; - - int rendererIndex = 0; - - if (annotation.atX()) { - textAnnotation = new CollisionFreeXYTextAnnotation( - annotation.getText(), annotation.getPos(), area.ofGround(TEXT_OFF)); - // OPTIMIZE externalize the calculation involving PI. - //textAnnotation.setRotationAngle(270f*Math.PI/180f); - lineAnnotation = createGroundStickAnnotation( - area, annotation.getPos(), lineStyle); - textAnnotation.setRotationAnchor(TextAnchor.CENTER_LEFT); - textAnnotation.setTextAnchor(TextAnchor.CENTER_LEFT); - } - else { - // Do the more complicated case where we stick to the Y-Axis. - // There is one nasty case (duration curves, where annotations - // might stick to the second y-axis). - // FIXME: Remove dependency to XYChartGenerator2 here - AxisDataset dataset = getAxisDataset( - new Integer(annotation.getAxisSymbol())); - if (dataset == null) { - logger.warn("Annotation should stick to unfindable y-axis: " - + annotation.getAxisSymbol()); - rendererIndex = 0; - } - else { - rendererIndex = dataset.getPlotAxisIndex(); - } - - // Stick to the "right" (opposed to left) Y-Axis. - if (rendererIndex != 0) { - // OPTIMIZE: Pass a different area to this function, - // do the adding to renderer outside (let this - // function return the annotations). - // Note that this path is travelled rarely. - ChartArea area2 = new ChartArea(plot.getDomainAxis(), plot.getRangeAxis(rendererIndex)); - textAnnotation = new CollisionFreeXYTextAnnotation( - annotation.getText(), area2.ofRight(TEXT_OFF), annotation.getPos()); - textAnnotation.setRotationAnchor(TextAnchor.CENTER_RIGHT); - textAnnotation.setTextAnchor(TextAnchor.CENTER_RIGHT); - lineAnnotation = createRightStickAnnotation( - area2, annotation.getPos(), lineStyle); - if (!Float.isNaN(annotation.getHitPoint()) && theme != null) { - // New line annotation to hit curve. - if (theme.parseShowVerticalLine()) { - XYLineAnnotation hitLineAnnotation = - createStickyLineAnnotation( - StickyAxisAnnotation.SimpleAxis.X_AXIS, - annotation.getHitPoint(), annotation.getPos(),// annotation.getHitPoint(), - area2, lineStyle); - plot.getRenderer(rendererIndex).addAnnotation(hitLineAnnotation, - org.jfree.ui.Layer.BACKGROUND); - } - if (theme.parseShowHorizontalLine()) { - XYLineAnnotation lineBackAnnotation = - createStickyLineAnnotation( - StickyAxisAnnotation.SimpleAxis.Y_AXIS2, - annotation.getPos(), annotation.getHitPoint(), - area2, lineStyle); - plot.getRenderer(rendererIndex).addAnnotation(lineBackAnnotation, - org.jfree.ui.Layer.BACKGROUND); - } - } - } - else { // Stick to the left y-axis. - textAnnotation = new CollisionFreeXYTextAnnotation( - annotation.getText(), area.ofLeft(TEXT_OFF), annotation.getPos()); - textAnnotation.setRotationAnchor(TextAnchor.CENTER_LEFT); - textAnnotation.setTextAnchor(TextAnchor.CENTER_LEFT); - lineAnnotation = createLeftStickAnnotation(area, annotation.getPos(), lineStyle); - if (!Float.isNaN(annotation.getHitPoint()) && theme != null) { - // New line annotation to hit curve. - if (theme.parseShowHorizontalLine()) { - XYLineAnnotation hitLineAnnotation = - createStickyLineAnnotation( - StickyAxisAnnotation.SimpleAxis.Y_AXIS, - annotation.getPos(), annotation.getHitPoint(), - area, lineStyle); - plot.getRenderer(rendererIndex).addAnnotation(hitLineAnnotation, - org.jfree.ui.Layer.BACKGROUND); - } - if (theme.parseShowVerticalLine()) { - XYLineAnnotation lineBackAnnotation = - createStickyLineAnnotation( - StickyAxisAnnotation.SimpleAxis.X_AXIS, - annotation.getHitPoint(), annotation.getPos(), - area, lineStyle); - plot.getRenderer(rendererIndex).addAnnotation(lineBackAnnotation, - org.jfree.ui.Layer.BACKGROUND); - } - } - } - } - - // Style the text. - if (textStyle != null) { - textStyle.apply(textAnnotation); - } - - // Add the Annotations to renderer. - plot.getRenderer(rendererIndex).addAnnotation(textAnnotation, - org.jfree.ui.Layer.FOREGROUND); - plot.getRenderer(rendererIndex).addAnnotation(lineAnnotation, - org.jfree.ui.Layer.FOREGROUND); - } - - /** - * Create annotation that sticks to "ground" (X) axis. - * @param area helper to calculate coordinates - * @param pos one-dimensional position (distance from axis) - * @param lineStyle the line style to use for the line. - */ - protected static XYLineAnnotation createGroundStickAnnotation( - ChartArea area, float pos, LineStyle lineStyle - ) { - // Style the line. - if (lineStyle != null) { - return new XYLineAnnotation( - pos, area.atGround(), - pos, area.ofGround(ANNOTATIONS_AXIS_OFFSET), - new BasicStroke(lineStyle.getWidth()),lineStyle.getColor()); - } - else { - return new XYLineAnnotation( - pos, area.atGround(), - pos, area.ofGround(ANNOTATIONS_AXIS_OFFSET)); - } - } - - - /** - * Create annotation that sticks to the second Y axis ("right"). - * @param area helper to calculate coordinates - * @param pos one-dimensional position (distance from axis) - * @param lineStyle the line style to use for the line. - */ - protected static XYLineAnnotation createRightStickAnnotation( - ChartArea area, float pos, LineStyle lineStyle - ) { - // Style the line. - if (lineStyle != null) { - return new XYLineAnnotation( - area.ofRight(ANNOTATIONS_AXIS_OFFSET), pos, - area.atRight(), pos, - new BasicStroke(lineStyle.getWidth()), lineStyle.getColor()); - } - else { - return new XYLineAnnotation( - area.atRight(), pos, - area.ofRight(ANNOTATIONS_AXIS_OFFSET), pos); - } - } - - - /** - * Create annotation that sticks to the first Y axis ("left"). - * @param area helper to calculate coordinates - * @param pos one-dimensional position (distance from axis) - * @param lineStyle the line style to use for the line. - */ - protected static XYLineAnnotation createLeftStickAnnotation( - ChartArea area, float pos, LineStyle lineStyle - ) { - // Style the line. - if (lineStyle != null) { - return new XYLineAnnotation( - area.atLeft(), pos, - area.ofLeft(ANNOTATIONS_AXIS_OFFSET), pos, - new BasicStroke(lineStyle.getWidth()), lineStyle.getColor()); - } - else { - return new XYLineAnnotation( - area.atLeft(), pos, - area.ofLeft(ANNOTATIONS_AXIS_OFFSET), pos); - } - } - - - /** - * Create a line from a axis to a given point. - * @param axis The "simple" axis. - * @param fromD1 from-location in first dimension. - * @param toD2 to-location in second dimension. - * @param area helper to calculate offsets. - * @param lineStyle optional line style. - */ - protected static XYLineAnnotation createStickyLineAnnotation( - StickyAxisAnnotation.SimpleAxis axis, float fromD1, float toD2, - ChartArea area, LineStyle lineStyle - ) { - double anchorX1 = 0d, anchorX2 = 0d, anchorY1 = 0d, anchorY2 = 0d; - switch(axis) { - case X_AXIS: - anchorX1 = fromD1; - anchorX2 = fromD1; - anchorY1 = area.atGround(); - anchorY2 = toD2; - break; - case Y_AXIS: - anchorX1 = area.atLeft(); - anchorX2 = toD2; - anchorY1 = fromD1; - anchorY2 = fromD1; - break; - case Y_AXIS2: - anchorX1 = area.atRight(); - anchorX2 = toD2; - anchorY1 = fromD1; - anchorY2 = fromD1; - break; - } - // Style the line. - if (lineStyle != null) { - return new XYLineAnnotation( - anchorX1, anchorY1, - anchorX2, anchorY2, - new BasicStroke(lineStyle.getWidth()), lineStyle.getColor()); - } - else { - return new XYLineAnnotation( - anchorX1, anchorY1, - anchorX2, anchorY2); - } - } - - /** - * Add the annotations (Sticky, Text and hyk zones) stored - * in the annotations field. - * @param plot Plot to add annotations to. - */ - protected void addAnnotationsToRenderer(XYPlot plot) { - logger.debug("addAnnotationsToRenderer"); - - if (annotations == null || annotations.isEmpty()) { - logger.debug("addAnnotationsToRenderer: no annotations."); - return; - } - - // OPTMIMIZE: Pre-calculate positions - ChartArea area = new ChartArea( - plot.getDomainAxis(0).getRange(), - plot.getRangeAxis().getRange()); - - // Walk over all Annotation sets. - for (RiverAnnotation fa: annotations) { - - // Access text styling, if any. - ThemeDocument theme = fa.getTheme(); - TextStyle textStyle = null; - LineStyle lineStyle = null; - - // Get Themeing information and add legend item. - if (theme != null) { - textStyle = theme.parseComplexTextStyle(); - lineStyle = theme.parseComplexLineStyle(); - if (fa.getLabel() != null) { - LegendItemCollection lic = new LegendItemCollection(); - LegendItemCollection old = plot.getFixedLegendItems(); - lic.add(createLegendItem(theme, fa.getLabel())); - // (Re-)Add prior legend entries. - if (old != null) { - old.addAll(lic); - } - else { - old = lic; - } - plot.setFixedLegendItems(old); - } - } - - // The 'Sticky' Annotations (at axis, with line and text). - for (StickyAxisAnnotation sta: fa.getAxisTextAnnotations()) { - addStickyAnnotation( - sta, plot, area, lineStyle, textStyle, theme); - } - - // Other Text Annotations (e.g. labels of (manual) points). - for (XYTextAnnotation ta: fa.getTextAnnotations()) { - // Style the text. - if (textStyle != null) { - textStyle.apply(ta); - } - ta.setY(area.above(0.05d, ta.getY())); - plot.getRenderer().addAnnotation(ta, org.jfree.ui.Layer.FOREGROUND); - } - } - } - - - /** * This method needs to be implemented by concrete subclasses to create new * instances of JFreeChart. * @@ -585,40 +266,6 @@ } } - - /** - * Register annotations like MainValues for later plotting - * - * @param annotations list of annotations (data of facet). - * @param aandf Artifact and the facet. - * @param theme Theme document for given annotations. - * @param visible The visibility of the annotations. - */ - public void doAnnotations( - RiverAnnotation annotations, - ArtifactAndFacet aandf, - ThemeDocument theme, - boolean visible - ){ - logger.debug("doAnnotations"); - - // Add all annotations to our annotation pool. - annotations.setTheme(theme); - if (aandf != null) { - annotations.setLabel(aandf.getFacetDescription()); - } - else { - logger.error( - "Art/Facet for Annotations is null. " + - "This should never happen!"); - } - - if (visible) { - addAnnotations(annotations); - } - } - - /** * Generate chart. */ diff -r 599d3c48474c -r 06a9a241faac artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionGenerator.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionGenerator.java Wed Sep 18 16:26:12 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionGenerator.java Wed Sep 18 17:12:13 2013 +0200 @@ -23,6 +23,7 @@ import org.dive4elements.river.exports.process.BedheightProcessor; import org.dive4elements.river.exports.process.QOutProcessor; import org.dive4elements.river.exports.process.WOutProcessor; +import org.dive4elements.river.exports.process.AnnotationProcessor; import org.dive4elements.river.jfree.RiverAnnotation; import org.dive4elements.river.jfree.StyledAreaSeriesCollection; @@ -340,11 +341,12 @@ return; } - WOutProcessor wProcessor = new WOutProcessor(); - QOutProcessor qProcessor = new QOutProcessor(); + Processor wProcessor = new WOutProcessor(); + Processor qProcessor = new QOutProcessor(); Processor bedp = new BedheightProcessor(); Processor bdyProcessor = new BedDiffYearProcessor(); Processor bdhyProcessor = new BedDiffHeightYearProcessor(); + Processor annotationProcessor = new AnnotationProcessor(); if (wProcessor.canHandle(name)) { wProcessor.doOut(this, artifactAndFacet, attr, visible, YAXIS.W.idx); @@ -361,12 +363,8 @@ else if (bdhyProcessor.canHandle(name)) { bdhyProcessor.doOut(this, artifactAndFacet, attr, visible, YAXIS.W.idx); } - else if (name.equals(LONGITUDINAL_ANNOTATION)) { - doAnnotations( - (RiverAnnotation) artifactAndFacet.getData(context), - artifactAndFacet, - attr, - visible); + else if (annotationProcessor.canHandle(name)) { + annotationProcessor.doOut(this, artifactAndFacet, attr, visible, 0); } else if (name.equals(W_DIFFERENCES)) { doWDifferencesOut( diff -r 599d3c48474c -r 06a9a241faac artifacts/src/main/java/org/dive4elements/river/exports/XYChartGenerator.java --- a/artifacts/src/main/java/org/dive4elements/river/exports/XYChartGenerator.java Wed Sep 18 16:26:12 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/exports/XYChartGenerator.java Wed Sep 18 17:12:13 2013 +0200 @@ -47,6 +47,7 @@ import org.dive4elements.river.jfree.StyledAreaSeriesCollection; import org.dive4elements.river.jfree.StyledXYSeries; import org.dive4elements.river.jfree.AxisDataset; +import org.dive4elements.river.jfree.AnnotationHelper; import org.dive4elements.river.themes.ThemeDocument; @@ -150,7 +151,8 @@ //debugAxis(plot); // These have to go after the autozoom. - addAnnotationsToRenderer(plot); + AnnotationHelper.addAnnotationsToRenderer(annotations, plot, + getChartSettings(), datasets); // Add a logo (maybe). addLogo(plot); diff -r 599d3c48474c -r 06a9a241faac artifacts/src/main/java/org/dive4elements/river/jfree/AnnotationHelper.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/jfree/AnnotationHelper.java Wed Sep 18 17:12:13 2013 +0200 @@ -0,0 +1,366 @@ +/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde + * Software engineering by Intevation GmbH + * + * This file is Free Software under the GNU AGPL (>=v3) + * and comes with ABSOLUTELY NO WARRANTY! Check out the + * documentation coming with Dive4Elements River for details. + */ +package org.dive4elements.river.jfree; + +import org.dive4elements.river.artifacts.model.HYKFactory; +import org.dive4elements.river.themes.ThemeDocument; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; + +import org.jfree.ui.TextAnchor; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.LegendItem; +import org.jfree.chart.LegendItemCollection; +import org.jfree.chart.annotations.XYTextAnnotation; +import org.jfree.chart.annotations.XYLineAnnotation; + +import org.dive4elements.river.themes.LineStyle; +import org.dive4elements.river.themes.TextStyle; +import org.dive4elements.river.exports.ChartSettings; +import org.dive4elements.river.exports.LegendSection; +import org.dive4elements.river.exports.ChartArea; + +import org.apache.log4j.Logger; + +/** Annotation helper class, handles plotting of annotations. */ +public class AnnotationHelper { + private static final Logger logger = Logger.getLogger(AnnotationHelper.class); + + protected static float ANNOTATIONS_AXIS_OFFSET = 0.02f; + + /* arr this would be better in chartsettings */ + public static final int DEFAULT_FONT_SIZE = 12; + public static final String DEFAULT_FONT_NAME = "Tahoma"; + + /** + * Add annotations (Sticky, Text and hyk zones) to a plot. + * @param annotations Annotations to add + * @param plot Plot to add annotations to. + * @param settings ChartSettings object for settings. + */ + public static void addAnnotationsToRenderer(List annotations, + XYPlot plot, ChartSettings settings, Map datasets) { + logger.debug("addAnnotationsToRenderer"); + + if (annotations == null || annotations.isEmpty()) { + logger.debug("addAnnotationsToRenderer: no annotations."); + return; + } + + // OPTMIMIZE: Pre-calculate positions + ChartArea area = new ChartArea( + plot.getDomainAxis(0).getRange(), + plot.getRangeAxis().getRange()); + + // Walk over all Annotation sets. + for (RiverAnnotation fa: annotations) { + + // Access text styling, if any. + ThemeDocument theme = fa.getTheme(); + TextStyle textStyle = null; + LineStyle lineStyle = null; + + // Get Themeing information and add legend item. + if (theme != null) { + textStyle = theme.parseComplexTextStyle(); + lineStyle = theme.parseComplexLineStyle(); + if (fa.getLabel() != null) { + // Legend handling, maybe misplaced? + LegendItemCollection lic = new LegendItemCollection(); + LegendItemCollection old = plot.getFixedLegendItems(); + + Color color = theme.parseLineColorField(); + if (color == null) { + color = Color.BLACK; + } + + LegendItem newItem = new LegendItem(fa.getLabel(), color); + LegendSection ls = settings.getLegendSection(); + newItem.setLabelFont (new Font( + DEFAULT_FONT_NAME, + Font.PLAIN, + ls != null ? ls.getFontSize() : null) + ); + + lic.add(newItem); + // (Re-)Add prior legend entries. + if (old != null) { + old.addAll(lic); + } + else { + old = lic; + } + plot.setFixedLegendItems(old); + } + } + + // The 'Sticky' Annotations (at axis, with line and text). + for (StickyAxisAnnotation sta: fa.getAxisTextAnnotations()) { + addStickyAnnotation( + sta, plot, area, lineStyle, textStyle, theme, + datasets.get(new Integer(sta.getAxisSymbol()))); + } + + // Other Text Annotations (e.g. labels of (manual) points). + for (XYTextAnnotation ta: fa.getTextAnnotations()) { + // Style the text. + if (textStyle != null) { + textStyle.apply(ta); + } + ta.setY(area.above(0.05d, ta.getY())); + plot.getRenderer().addAnnotation(ta, org.jfree.ui.Layer.FOREGROUND); + } + } + } + + /** + * Add a text and a line annotation. + * @param area convenience to determine positions in plot. + * @param theme (optional) theme document + */ + public static void addStickyAnnotation( + StickyAxisAnnotation annotation, + XYPlot plot, + ChartArea area, + LineStyle lineStyle, + TextStyle textStyle, + ThemeDocument theme, + AxisDataset dataset + ) { + // OPTIMIZE pre-calculate area-related values + final float TEXT_OFF = 0.03f; + + XYLineAnnotation lineAnnotation = null; + XYTextAnnotation textAnnotation = null; + + int rendererIndex = 0; + + if (annotation.atX()) { + textAnnotation = new CollisionFreeXYTextAnnotation( + annotation.getText(), annotation.getPos(), area.ofGround(TEXT_OFF)); + // OPTIMIZE externalize the calculation involving PI. + //textAnnotation.setRotationAngle(270f*Math.PI/180f); + lineAnnotation = createGroundStickAnnotation( + area, annotation.getPos(), lineStyle); + textAnnotation.setRotationAnchor(TextAnchor.CENTER_LEFT); + textAnnotation.setTextAnchor(TextAnchor.CENTER_LEFT); + } + else { + // Do the more complicated case where we stick to the Y-Axis. + // There is one nasty case (duration curves, where annotations + // might stick to the second y-axis). + if (dataset == null) { + logger.warn("Annotation should stick to unfindable y-axis: " + + annotation.getAxisSymbol()); + rendererIndex = 0; + } + else { + rendererIndex = dataset.getPlotAxisIndex(); + } + + // Stick to the "right" (opposed to left) Y-Axis. + if (rendererIndex != 0) { + // OPTIMIZE: Pass a different area to this function, + // do the adding to renderer outside (let this + // function return the annotations). + // Note that this path is travelled rarely. + ChartArea area2 = new ChartArea(plot.getDomainAxis(), plot.getRangeAxis(rendererIndex)); + textAnnotation = new CollisionFreeXYTextAnnotation( + annotation.getText(), area2.ofRight(TEXT_OFF), annotation.getPos()); + textAnnotation.setRotationAnchor(TextAnchor.CENTER_RIGHT); + textAnnotation.setTextAnchor(TextAnchor.CENTER_RIGHT); + lineAnnotation = createRightStickAnnotation( + area2, annotation.getPos(), lineStyle); + if (!Float.isNaN(annotation.getHitPoint()) && theme != null) { + // New line annotation to hit curve. + if (theme.parseShowVerticalLine()) { + XYLineAnnotation hitLineAnnotation = + createStickyLineAnnotation( + StickyAxisAnnotation.SimpleAxis.X_AXIS, + annotation.getHitPoint(), annotation.getPos(),// annotation.getHitPoint(), + area2, lineStyle); + plot.getRenderer(rendererIndex).addAnnotation(hitLineAnnotation, + org.jfree.ui.Layer.BACKGROUND); + } + if (theme.parseShowHorizontalLine()) { + XYLineAnnotation lineBackAnnotation = + createStickyLineAnnotation( + StickyAxisAnnotation.SimpleAxis.Y_AXIS2, + annotation.getPos(), annotation.getHitPoint(), + area2, lineStyle); + plot.getRenderer(rendererIndex).addAnnotation(lineBackAnnotation, + org.jfree.ui.Layer.BACKGROUND); + } + } + } + else { // Stick to the left y-axis. + textAnnotation = new CollisionFreeXYTextAnnotation( + annotation.getText(), area.ofLeft(TEXT_OFF), annotation.getPos()); + textAnnotation.setRotationAnchor(TextAnchor.CENTER_LEFT); + textAnnotation.setTextAnchor(TextAnchor.CENTER_LEFT); + lineAnnotation = createLeftStickAnnotation(area, annotation.getPos(), lineStyle); + if (!Float.isNaN(annotation.getHitPoint()) && theme != null) { + // New line annotation to hit curve. + if (theme.parseShowHorizontalLine()) { + XYLineAnnotation hitLineAnnotation = + createStickyLineAnnotation( + StickyAxisAnnotation.SimpleAxis.Y_AXIS, + annotation.getPos(), annotation.getHitPoint(), + area, lineStyle); + plot.getRenderer(rendererIndex).addAnnotation(hitLineAnnotation, + org.jfree.ui.Layer.BACKGROUND); + } + if (theme.parseShowVerticalLine()) { + XYLineAnnotation lineBackAnnotation = + createStickyLineAnnotation( + StickyAxisAnnotation.SimpleAxis.X_AXIS, + annotation.getHitPoint(), annotation.getPos(), + area, lineStyle); + plot.getRenderer(rendererIndex).addAnnotation(lineBackAnnotation, + org.jfree.ui.Layer.BACKGROUND); + } + } + } + } + + // Style the text. + if (textStyle != null) { + textStyle.apply(textAnnotation); + } + + // Add the Annotations to renderer. + plot.getRenderer(rendererIndex).addAnnotation(textAnnotation, + org.jfree.ui.Layer.FOREGROUND); + plot.getRenderer(rendererIndex).addAnnotation(lineAnnotation, + org.jfree.ui.Layer.FOREGROUND); + } + + /** + * Create annotation that sticks to "ground" (X) axis. + * @param area helper to calculate coordinates + * @param pos one-dimensional position (distance from axis) + * @param lineStyle the line style to use for the line. + */ + public static XYLineAnnotation createGroundStickAnnotation( + ChartArea area, float pos, LineStyle lineStyle + ) { + // Style the line. + if (lineStyle != null) { + return new XYLineAnnotation( + pos, area.atGround(), + pos, area.ofGround(ANNOTATIONS_AXIS_OFFSET), + new BasicStroke(lineStyle.getWidth()),lineStyle.getColor()); + } + else { + return new XYLineAnnotation( + pos, area.atGround(), + pos, area.ofGround(ANNOTATIONS_AXIS_OFFSET)); + } + } + + + /** + * Create annotation that sticks to the second Y axis ("right"). + * @param area helper to calculate coordinates + * @param pos one-dimensional position (distance from axis) + * @param lineStyle the line style to use for the line. + */ + public static XYLineAnnotation createRightStickAnnotation( + ChartArea area, float pos, LineStyle lineStyle + ) { + // Style the line. + if (lineStyle != null) { + return new XYLineAnnotation( + area.ofRight(ANNOTATIONS_AXIS_OFFSET), pos, + area.atRight(), pos, + new BasicStroke(lineStyle.getWidth()), lineStyle.getColor()); + } + else { + return new XYLineAnnotation( + area.atRight(), pos, + area.ofRight(ANNOTATIONS_AXIS_OFFSET), pos); + } + } + /** + * Create annotation that sticks to the first Y axis ("left"). + * @param area helper to calculate coordinates + * @param pos one-dimensional position (distance from axis) + * @param lineStyle the line style to use for the line. + */ + public static XYLineAnnotation createLeftStickAnnotation( + ChartArea area, float pos, LineStyle lineStyle + ) { + // Style the line. + if (lineStyle != null) { + return new XYLineAnnotation( + area.atLeft(), pos, + area.ofLeft(ANNOTATIONS_AXIS_OFFSET), pos, + new BasicStroke(lineStyle.getWidth()), lineStyle.getColor()); + } + else { + return new XYLineAnnotation( + area.atLeft(), pos, + area.ofLeft(ANNOTATIONS_AXIS_OFFSET), pos); + } + } + + + /** + * Create a line from a axis to a given point. + * @param axis The "simple" axis. + * @param fromD1 from-location in first dimension. + * @param toD2 to-location in second dimension. + * @param area helper to calculate offsets. + * @param lineStyle optional line style. + */ + public static XYLineAnnotation createStickyLineAnnotation( + StickyAxisAnnotation.SimpleAxis axis, float fromD1, float toD2, + ChartArea area, LineStyle lineStyle + ) { + double anchorX1 = 0d, anchorX2 = 0d, anchorY1 = 0d, anchorY2 = 0d; + switch(axis) { + case X_AXIS: + anchorX1 = fromD1; + anchorX2 = fromD1; + anchorY1 = area.atGround(); + anchorY2 = toD2; + break; + case Y_AXIS: + anchorX1 = area.atLeft(); + anchorX2 = toD2; + anchorY1 = fromD1; + anchorY2 = fromD1; + break; + case Y_AXIS2: + anchorX1 = area.atRight(); + anchorX2 = toD2; + anchorY1 = fromD1; + anchorY2 = fromD1; + break; + } + // Style the line. + if (lineStyle != null) { + return new XYLineAnnotation( + anchorX1, anchorY1, + anchorX2, anchorY2, + new BasicStroke(lineStyle.getWidth()), lineStyle.getColor()); + } + else { + return new XYLineAnnotation( + anchorX1, anchorY1, + anchorX2, anchorY2); + } + } + +};