Search code examples
javajfreechart

Add GeneralPath shape in XYShapeAnnotation of JFreechart


I am trying to adding a general shape to highlight an area where Y is greater than X in JFreechart.

for that i have defined a general path.

int x3Points[] = {0, (int) Math.max(maxX, maxY), (int) Math.max(maxX, maxY), 0};
int y3Points[] = {0, (int) Math.max(maxX, maxY), (int) maxY, (int) maxY};
GeneralPath filledPolygon = new GeneralPath(GeneralPath.WIND_EVEN_ODD, x3Points.length);
filledPolygon.moveTo(x3Points[0], y3Points[0]);
    for (int index = 1; index < x3Points.length; index++) {
         filledPolygon.lineTo(x3Points[index], y3Points[index]);
     }
        filledPolygon.lineTo(x3Points[0], y3Points[0]);
        filledPolygon.closePath();

Graphics graphics = getGraphics(); // i am not sure if calling getGraphics() method of JFrame is a good idea. but if not how to get Graphics() to draw on chart 

   Graphics2D g2d = (Graphics2D) graphics;
   g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);

   g2d.setRenderingHint(RenderingHints.KEY_RENDERING,
                RenderingHints.VALUE_RENDER_QUALITY);
   g2d.setPaint(Color.gray);
   g2d.fill(filledPolygon);
   g2d.dispose();
   XYShapeAnnotation xyShapeAnnotation = new XYShapeAnnotation(filledPolygon, new BasicStroke(2.f), Color.black);

 renderer.addAnnotation(xyShapeAnnotation, Layer.BACKGROUND);

the shape is added to the chart but it is not filled with black color. i guess the getGraphics() method that i am using is not correct. but how to get graphics of JFreechart.


Solution

  • Don't use getGraphics(); the returned graphics context will become invalid after subsequent updates. Instead, specify the desired fillPaint in your XYShapeAnnotation constructor. Later calls to draw() will fill() the Shape accordingly, as shown in the example below. Note a few common pitfalls:

    • The desired Layer should be specified, as shown here.

    • The shape coordinates must be specified in data space, as shown here.

    • The winding rule defines the interior for fill(), as discussed here.

    image

    import java.awt.BasicStroke;
    import java.awt.Color;
    import java.awt.EventQueue;
    import java.awt.geom.GeneralPath;
    import org.jfree.chart.ChartFactory;
    import org.jfree.chart.ChartFrame;
    import org.jfree.chart.JFreeChart;
    import org.jfree.chart.annotations.XYShapeAnnotation;
    import org.jfree.chart.plot.PlotOrientation;
    import org.jfree.chart.plot.XYPlot;
    import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
    import org.jfree.chart.ui.Layer;
    import org.jfree.data.xy.XYDataset;
    import org.jfree.data.xy.XYSeries;
    import org.jfree.data.xy.XYSeriesCollection;
    
    /**
     * @see https://stackoverflow.com/q/59588078/230513
     * @see http://stackoverflow.com/a/35236100/261156
     */
    public class AnnotationTest {
    
        private static final BasicStroke STROKE = new BasicStroke(2.0f);
        private static final int N = 16;
        private static final int W = 1;
        private static final int H = W;
    
        public static void main(String[] args) {
            EventQueue.invokeLater(new AnnotationTest()::display);
        }
    
        private void display() {
            XYDataset data = createDataset();
            JFreeChart chart = ChartFactory.createXYLineChart("Annotation Test", "X", "Y",
                data, PlotOrientation.VERTICAL, true, true, false);
            XYPlot plot = chart.getXYPlot();
            XYLineAndShapeRenderer renderer
                = (XYLineAndShapeRenderer) plot.getRenderer();
            renderer.addAnnotation(new XYShapeAnnotation(initPath(4, 4),
                STROKE, Color.gray, Color.red), Layer.FOREGROUND);
            renderer.addAnnotation(new XYShapeAnnotation(initPath(8, 8),
                STROKE, Color.gray, Color.blue), Layer.FOREGROUND);
            renderer.addAnnotation(new XYShapeAnnotation(initPath(12, 12),
                STROKE, Color.gray, Color.green), Layer.FOREGROUND);
            ChartFrame frame = new ChartFrame("Annotation Test", chart);
            frame.pack();
            frame.setSize(640, 480);
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    
        private GeneralPath initPath(int x, int y) {
            GeneralPath path = new GeneralPath();
            path.moveTo(x, y);
            path.lineTo(x - W, y - H);
            path.lineTo(x + W, y - H);
            path.lineTo(x - W, y + H);
            path.lineTo(x + W, y + H);
            path.lineTo(x, y);
            return path;
        }
    
        private static XYDataset createDataset() {
            XYSeriesCollection result = new XYSeriesCollection();
            XYSeries series = new XYSeries("Test");
            for (int i = 0; i < N; i++) {
                series.add(i, i);
            }
            result.addSeries(series);
            return result;
        }
    }