hami
hami

Reputation: 443

Create a marker / crosshair in a CombinedDomainXYPlot

I want to show in a CombinedDomainXYPlot a marker. This is not working. I can add the marker for each subplot. But I want to add it for the CombinedDomainXYPlot. Anybody who can tell me something about this issue. I think with the crosshair the behaviour is the same.

This is working example which creates a CombinedDomainXYPlot and tries to add a marker. The marker adding is in createCombinedChart()

import java.awt.Font;

import javax.swing.JPanel;

import org.jfree.chart.ChartPanel;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.annotations.XYTextAnnotation;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.IntervalMarker;
import org.jfree.chart.plot.Marker;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RefineryUtilities;

/**
 * A demonstration application showing how to create a combined chart.
 */
public class MarkerDemo2 extends ApplicationFrame {

/**
 * Constructs a new demonstration application.
 *
 * @param title  the frame title.
 */
public MarkerDemo2(String title) {

    super(title);
    JFreeChart chart = createCombinedChart();
    ChartPanel panel = (ChartPanel) createDemoPanel();
    panel.setPreferredSize(new java.awt.Dimension(500, 270));
    setContentPane(panel);

}

/**
 * Creates a combined chart.
 *
 * @return The combined chart.
 */
private static JFreeChart createCombinedChart() {

    // create subplot 1...
    XYDataset data1 = createDataset1();
    XYItemRenderer renderer1 = new StandardXYItemRenderer();
    NumberAxis rangeAxis1 = new NumberAxis("Range 1");
    XYPlot subplot1 = new XYPlot(data1, null, rangeAxis1, renderer1);
    subplot1.setRangeAxisLocation(AxisLocation.BOTTOM_OR_LEFT);

    // add secondary axis
    subplot1.setDataset(1, createDataset2());
    NumberAxis axis2 = new NumberAxis("Range Axis 2");
    axis2.setAutoRangeIncludesZero(false);
    subplot1.setRangeAxis(1, axis2);
    subplot1.setRangeAxisLocation(1, AxisLocation.BOTTOM_OR_RIGHT);
    subplot1.setRenderer(1, new StandardXYItemRenderer());
    subplot1.mapDatasetToRangeAxis(1, 1);

    XYTextAnnotation annotation = new XYTextAnnotation("Hello!", 50.0, 10000.0);
    annotation.setFont(new Font("SansSerif", Font.PLAIN, 9));
    annotation.setRotationAngle(Math.PI / 4.0);
    subplot1.addAnnotation(annotation);

    // create subplot 2...
    XYDataset data2 = createDataset2();
    XYItemRenderer renderer2 = new StandardXYItemRenderer();
    NumberAxis rangeAxis2 = new NumberAxis("Range 2");
    rangeAxis2.setAutoRangeIncludesZero(false);
    XYPlot subplot2 = new XYPlot(data2, null, rangeAxis2, renderer2);
    subplot2.setRangeAxisLocation(AxisLocation.TOP_OR_LEFT);

    // parent plot...
    CombinedDomainXYPlot plot = new CombinedDomainXYPlot(new NumberAxis("Domain"));
    plot.setGap(10.0);

    // add the subplots...
    plot.add(subplot1, 1);
    plot.add(subplot2, 1);
    plot.setOrientation(PlotOrientation.VERTICAL);

    // Add marker
    Marker marker = new IntervalMarker(30, 40);
    plot.addDomainMarker(marker);
    // Working
    // subplot1.addDomainMarker(marker);

    // return a new chart containing the overlaid plot...
    JFreeChart chart = new JFreeChart("CombinedDomainXYPlot Demo",
            JFreeChart.DEFAULT_TITLE_FONT, plot, true);
    ChartUtilities.applyCurrentTheme(chart);
    return chart;

}

/**
 * Creates a sample dataset.
 *
 * @return Series 1.
 */
private static XYDataset createDataset1() {

    // create dataset 1...
    XYSeries series1 = new XYSeries("Series 1a");
    series1.add(10.0, 12353.3);
    series1.add(20.0, 13734.4);
    series1.add(30.0, 14525.3);
    series1.add(40.0, 13984.3);
    series1.add(50.0, 12999.4);
    series1.add(60.0, 14274.3);
    series1.add(70.0, 15943.5);
    series1.add(80.0, 14845.3);
    series1.add(90.0, 14645.4);
    series1.add(100.0, 16234.6);
    series1.add(110.0, 17232.3);
    series1.add(120.0, 14232.2);
    series1.add(130.0, 13102.2);
    series1.add(140.0, 14230.2);
    series1.add(150.0, 11235.2);

    XYSeries series1b = new XYSeries("Series 1b");
    series1b.add(10.0, 15000.3);
    series1b.add(20.0, 11000.4);
    series1b.add(30.0, 17000.3);
    series1b.add(40.0, 15000.3);
    series1b.add(50.0, 14000.4);
    series1b.add(60.0, 12000.3);
    series1b.add(70.0, 11000.5);
    series1b.add(80.0, 12000.3);
    series1b.add(90.0, 13000.4);
    series1b.add(100.0, 12000.6);
    series1b.add(110.0, 13000.3);
    series1b.add(120.0, 17000.2);
    series1b.add(130.0, 18000.2);
    series1b.add(140.0, 16000.2);
    series1b.add(150.0, 17000.2);

    XYSeriesCollection collection = new XYSeriesCollection();
    collection.addSeries(series1);
    collection.addSeries(series1b);
    return collection;

}

/**
 * Creates a sample dataset.
 *
 * @return A sample dataset.
 */
private static XYDataset createDataset2() {

    // create dataset 2...
    XYSeries series2 = new XYSeries("Series 2");

    series2.add(10.0, 6853.2);
    series2.add(20.0, 9642.3);
    series2.add(30.0, 8253.5);
    series2.add(40.0, 5352.3);
    series2.add(50.0, 3532.0);
    series2.add(60.0, 2635.3);
    series2.add(70.0, 3998.2);
    series2.add(80.0, 1943.2);
    series2.add(90.0, 6943.9);
    series2.add(100.0, 7843.2);
    series2.add(105.0, 6495.3);
    series2.add(110.0, 7943.6);
    series2.add(115.0, 8500.7);
    series2.add(120.0, 9595.9);

    return new XYSeriesCollection(series2);

}

/**
 * Creates a panel for the demo (used by SuperDemo.java).
 *
 * @return A panel.
 */
public static JPanel createDemoPanel() {
    JFreeChart chart = createCombinedChart();
    ChartPanel panel = new ChartPanel(chart);
    panel.setMouseWheelEnabled(true);
    return new ChartPanel(chart);
}

/**
 * Starting point for the demonstration application.
 *
 * @param args  ignored.
 */
public static void main(String[] args) {

    MarkerDemo2 demo = new MarkerDemo2(
            "JFreeChart: CombinedDomainXYPlotDemo4.java");
    demo.pack();
    RefineryUtilities.centerFrameOnScreen(demo);
    demo.setVisible(true);

}

}

Upvotes: 1

Views: 3497

Answers (2)

hami
hami

Reputation: 443

Thanks to Graham I developed an adjusted version of CombinedDomainXYPlot, which works for me. The result looks like this.

CombinedDomainXYPlot with Marker

I merged Code from XYPlot and XYLineAndShapeRenderer to achieve this. Here is the source code.

import java.awt.AlphaComposite;
import java.awt.Composite;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.jfree.chart.annotations.XYAnnotation;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.IntervalMarker;
import org.jfree.chart.plot.Marker;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.PlotRenderingInfo;
import org.jfree.chart.plot.PlotState;
import org.jfree.chart.plot.ValueMarker;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.Range;
import org.jfree.text.TextUtilities;
import org.jfree.ui.GradientPaintTransformer;
import org.jfree.ui.Layer;
import org.jfree.ui.LengthAdjustmentType;
import org.jfree.ui.RectangleAnchor;
import org.jfree.ui.RectangleInsets;

public class MyCombinedDomainXYPlot extends CombinedDomainXYPlot {

public MyCombinedDomainXYPlot() {
    super();
    // TODO Auto-generated constructor stub
}

public MyCombinedDomainXYPlot(ValueAxis domainAxis) {
    super(domainAxis);
    // TODO Auto-generated constructor stub
}

@Override
public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor,
        PlotState parentState, PlotRenderingInfo info) {
    super.draw(g2, area, anchor, parentState, info);
    drawDomainMarkers(g2, area, 0, Layer.FOREGROUND);
}

/**
 * Draws the domain markers (if any) for an axis and layer.  This method is
 * typically called from within the draw() method.
 *
 * @param g2  the graphics device.
 * @param dataArea  the data area.
 * @param index  the renderer index.
 * @param layer  the layer (foreground or background).
 */
@Override
protected void drawDomainMarkers(Graphics2D g2, Rectangle2D dataArea,
                                 int index, Layer layer) {

    // check that the renderer has a corresponding dataset (it doesn't
    // matter if the dataset is null)
    if (index >= getDatasetCount()) {
        return;
    }
    Collection markers = getDomainMarkers(index, layer);
    ValueAxis axis = getDomainAxisForDataset(index);
    if (markers != null && axis != null) {
        Iterator iterator = markers.iterator();
        while (iterator.hasNext()) {
            Marker marker = (Marker) iterator.next();
            drawDomainMarker(g2, marker, dataArea);
        }
    }

}


/**
 * Draws a vertical line on the chart to represent a 'range marker'.
 * 
 * @param g2
 *            the graphics device.
 * @param plot
 *            the plot.
 * @param domainAxis
 *            the domain axis.
 * @param marker
 *            the marker line.
 * @param dataArea
 *            the axis data area.
 */
public void drawDomainMarker(Graphics2D g2, Marker marker, Rectangle2D dataArea) {

        ValueAxis domainAxis = getDomainAxis();
        if (marker instanceof ValueMarker) {
            ValueMarker vm = (ValueMarker) marker;
            double value = vm.getValue();
            Range range = domainAxis.getRange();
            if (!range.contains(value)) {
                return;
            }

            double v = domainAxis.valueToJava2D(value, dataArea,
                    getDomainAxisEdge());

            PlotOrientation orientation = getOrientation();
            Line2D line = null;
            if (orientation == PlotOrientation.HORIZONTAL) {
                line = new Line2D.Double(dataArea.getMinX(), v,
                        dataArea.getMaxX(), v);
            } else if (orientation == PlotOrientation.VERTICAL) {
                line = new Line2D.Double(v, dataArea.getMinY(), v,
                        dataArea.getMaxY());
            }

            final Composite originalComposite = g2.getComposite();
            g2.setComposite(AlphaComposite.getInstance(
                    AlphaComposite.SRC_OVER, marker.getAlpha()));
            g2.setPaint(marker.getPaint());
            g2.setStroke(marker.getStroke());
            g2.draw(line);

            String label = marker.getLabel();
            RectangleAnchor anchor = marker.getLabelAnchor();
            if (label != null) {
                Font labelFont = marker.getLabelFont();
                g2.setFont(labelFont);
                g2.setPaint(marker.getLabelPaint());
                Point2D coordinates = calculateDomainMarkerTextAnchorPoint(
                        g2, orientation, dataArea, line.getBounds2D(),
                        marker.getLabelOffset(),
                        LengthAdjustmentType.EXPAND, anchor);
                TextUtilities.drawAlignedString(label, g2,
                        (float) coordinates.getX(),
                        (float) coordinates.getY(),
                        marker.getLabelTextAnchor());
            }
            g2.setComposite(originalComposite);
        } else if (marker instanceof IntervalMarker) {
            IntervalMarker im = (IntervalMarker) marker;
            double start = im.getStartValue();
            double end = im.getEndValue();
            Range range = domainAxis.getRange();
            if (!(range.intersects(start, end))) {
                return;
            }

            double start2d = domainAxis.valueToJava2D(start, dataArea,
                    getDomainAxisEdge());
            double end2d = domainAxis.valueToJava2D(end, dataArea,
                    getDomainAxisEdge());
            double low = Math.min(start2d, end2d);
            double high = Math.max(start2d, end2d);

            PlotOrientation orientation = getOrientation();
            Rectangle2D rect = null;
            if (orientation == PlotOrientation.HORIZONTAL) {
                // clip top and bottom bounds to data area
                low = Math.max(low, dataArea.getMinY());
                high = Math.min(high, dataArea.getMaxY());
                rect = new Rectangle2D.Double(dataArea.getMinX(), low,
                        dataArea.getWidth(), high - low);
            } else if (orientation == PlotOrientation.VERTICAL) {
                // clip left and right bounds to data area
                low = Math.max(low, dataArea.getMinX());
                high = Math.min(high, dataArea.getMaxX());
                rect = new Rectangle2D.Double(low, dataArea.getMinY(), high
                        - low, dataArea.getHeight());
            }

            final Composite originalComposite = g2.getComposite();
            g2.setComposite(AlphaComposite.getInstance(
                    AlphaComposite.SRC_OVER, marker.getAlpha()));
            Paint p = marker.getPaint();
            if (p instanceof GradientPaint) {
                GradientPaint gp = (GradientPaint) p;
                GradientPaintTransformer t = im
                        .getGradientPaintTransformer();
                if (t != null) {
                    gp = t.transform(gp, rect);
                }
                g2.setPaint(gp);
            } else {
                g2.setPaint(p);
            }
            g2.fill(rect);

            // now draw the outlines, if visible...
            if (im.getOutlinePaint() != null
                    && im.getOutlineStroke() != null) {
                if (orientation == PlotOrientation.VERTICAL) {
                    Line2D line = new Line2D.Double();
                    double y0 = dataArea.getMinY();
                    double y1 = dataArea.getMaxY();
                    g2.setPaint(im.getOutlinePaint());
                    g2.setStroke(im.getOutlineStroke());
                    if (range.contains(start)) {
                        line.setLine(start2d, y0, start2d, y1);
                        g2.draw(line);
                    }
                    if (range.contains(end)) {
                        line.setLine(end2d, y0, end2d, y1);
                        g2.draw(line);
                    }
                } else { // PlotOrientation.HORIZONTAL
                    Line2D line = new Line2D.Double();
                    double x0 = dataArea.getMinX();
                    double x1 = dataArea.getMaxX();
                    g2.setPaint(im.getOutlinePaint());
                    g2.setStroke(im.getOutlineStroke());
                    if (range.contains(start)) {
                        line.setLine(x0, start2d, x1, start2d);
                        g2.draw(line);
                    }
                    if (range.contains(end)) {
                        line.setLine(x0, end2d, x1, end2d);
                        g2.draw(line);
                    }
                }
            }

            String label = marker.getLabel();
            RectangleAnchor anchor = marker.getLabelAnchor();
            if (label != null) {
                Font labelFont = marker.getLabelFont();
                g2.setFont(labelFont);
                g2.setPaint(marker.getLabelPaint());
                Point2D coordinates = calculateDomainMarkerTextAnchorPoint(
                        g2, orientation, dataArea, rect,
                        marker.getLabelOffset(),
                        marker.getLabelOffsetType(), anchor);
                TextUtilities.drawAlignedString(label, g2,
                        (float) coordinates.getX(),
                        (float) coordinates.getY(),
                        marker.getLabelTextAnchor());
            }
            g2.setComposite(originalComposite);

        }
    }




/**
 * Calculates the (x, y) coordinates for drawing a marker label.
 * 
 * @param g2
 *            the graphics device.
 * @param orientation
 *            the plot orientation.
 * @param dataArea
 *            the data area.
 * @param markerArea
 *            the rectangle surrounding the marker area.
 * @param markerOffset
 *            the marker label offset.
 * @param labelOffsetType
 *            the label offset type.
 * @param anchor
 *            the label anchor.
 * 
 * @return The coordinates for drawing the marker label.
 */
protected Point2D calculateDomainMarkerTextAnchorPoint(Graphics2D g2,
        PlotOrientation orientation, Rectangle2D dataArea,
        Rectangle2D markerArea, RectangleInsets markerOffset,
        LengthAdjustmentType labelOffsetType, RectangleAnchor anchor) {

    Rectangle2D anchorRect = null;
    if (orientation == PlotOrientation.HORIZONTAL) {
        anchorRect = markerOffset.createAdjustedRectangle(markerArea,
                LengthAdjustmentType.CONTRACT, labelOffsetType);
    } else if (orientation == PlotOrientation.VERTICAL) {
        anchorRect = markerOffset.createAdjustedRectangle(markerArea,
                labelOffsetType, LengthAdjustmentType.CONTRACT);
    }
    return RectangleAnchor.coordinates(anchorRect, anchor);

}
}

Upvotes: 2

GrahamA
GrahamA

Reputation: 5923

drawAnnotations is implemented in XYPlot, unfortunately CombinedDomainXYPlot subclasses XYPlot but doesn't call drawAnnotations in its overridden implementation of draw or forward the annotation on to the subplots.

You could possible provide your on implemntation of addAnnotation and removeAnnotation by subclassing CombinedDomainXYPlot.

Upvotes: 2

Related Questions