Changing shape of single point in JFreeChart XYPLot
Asked Answered
T

1

6

I am using JFreeChart XYPLot for plotting a XYData set with different labels . I have created different XYSeries objects for different labels so that I can have different colors for different labels . Now I need to require the change the shapes of specific points(test data) in each XYDataSeries as below enter image description here. In the above plotting , there are two different XYSeries with blue and red color . Out of these two I need to change the shapes of some points(test data) to X instead of circle . Is it possible in JFreeChart. This post explained on how to do it for whole data set , but I want to change only specific points

Below is the code I have written so far

  public static Map<String, XYSeries> createXYSeries(Data[] dataSet){       
    Map<String,XYSeries> xySeries = new HashMap<String, XYSeries>();        
    for(Data data : dataSet){
        if(xySeries.get(data.actualLabel) == null){
            xySeries.put(data.actualLabel, new XYSeries(data.actualLabel));
        }
        xySeries.get(data.actualLabel).add(data.dimensionValues[0],data.dimensionValues[1]);
    }   

    return xySeries;
}

   public XYDataset createXYSeriesCollection(Map<String, XYSeries> plottingDataSet) {
    XYSeriesCollection xySeriesCollection = new XYSeriesCollection();
    for (String key : plottingDataSet.keySet()) {
        xySeriesCollection.addSeries(plottingDataSet.get(key));
    }
    return xySeriesCollection;
}

   private ChartPanel createPlottingPanel(String title,
        Map<String, XYSeries> plottingDataSet) {
    JFreeChart jfreechart = ChartFactory.createScatterPlot(title, "X", "Y",
            createSampleData(plottingDataSet), PlotOrientation.VERTICAL,
            true, true, false);
    XYPlot xyPlot = (XYPlot) jfreechart.getPlot();
    xyPlot.setDomainCrosshairVisible(true);
    xyPlot.setRangeCrosshairVisible(true);
    xyPlot.setBackgroundPaint(Color.white);
    return new ChartPanel(jfreechart);
}

Note : I am trying to plot the KNearestNeighbors results .(Circles for train data and X for test data)

Tymothy answered 3/12, 2013 at 17:32 Comment(0)
C
10

ChartFactory.createScatterPlot() instantiates an XYLineAndShapeRenderer. You can replace the renderer with one that lets you selectively replace the Shape returned by getItemShape(), as shown below.

    xyPlot.setRenderer(new XYLineAndShapeRenderer(false, true) {

        @Override
        public Shape getItemShape(int row, int col) {
            if (row == 0 & col == N) {
                return ShapeUtilities.createDiagonalCross(5, 2);
            } else {
                return super.getItemShape(row, col);
            }
        }
    });

image

Complete example, as run:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Shape;
import java.util.*;
import javax.swing.JFrame;
import org.jfree.chart.*;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.util.ShapeUtilities;

/**
 * @see https://mcmap.net/q/904425/-changing-shape-of-single-point-in-jfreechart-xyplot
 * @see https://mcmap.net/q/855078/-changing-the-shapes-of-points-in-scatter-plot
 */
public class ScatterShape extends JFrame {

    private static final int N = 8;
    private static final int SIZE = 345;
    private static final String title = "Scatter Shape Demo";
    private static final Random rand = new Random();
    private final XYSeries series = new XYSeries("Data");

    public ScatterShape(String s) {
        super(s);
        final ChartPanel chartPanel = createDemoPanel();
        this.add(chartPanel, BorderLayout.CENTER);
    }

    private ChartPanel createDemoPanel() {
        JFreeChart chart = ChartFactory.createScatterPlot(
            title, "X", "Y", createSampleData(),
            PlotOrientation.VERTICAL, true, true, false);
        XYPlot xyPlot = (XYPlot) chart.getPlot();
        xyPlot.setDomainCrosshairVisible(true);
        xyPlot.setRangeCrosshairVisible(true);
        xyPlot.setRenderer(new XYLineAndShapeRenderer(false, true) {

            @Override
            public Shape getItemShape(int row, int col) {
                if (row == 0 & col == N) {
                    return ShapeUtilities.createDiagonalCross(5, 2);
                } else {
                    return super.getItemShape(row, col);
                }
            }
        });
        adjustAxis((NumberAxis) xyPlot.getDomainAxis(), true);
        adjustAxis((NumberAxis) xyPlot.getRangeAxis(), false);
        xyPlot.setBackgroundPaint(Color.white);
        return new ChartPanel(chart) {

            @Override
            public Dimension getPreferredSize() {
                return new Dimension(SIZE, SIZE);
            }
        };
    }

    private void adjustAxis(NumberAxis axis, boolean vertical) {
        axis.setRange(-3.0, 3.0);
        axis.setTickUnit(new NumberTickUnit(0.5));
        axis.setVerticalTickLabels(vertical);
    }

    private XYDataset createSampleData() {
        XYSeriesCollection xySeriesCollection = new XYSeriesCollection();
        for (int i = 0; i < N * N; i++) {
            series.add(rand.nextGaussian(), rand.nextGaussian());
        }
        xySeriesCollection.addSeries(series);
        return xySeriesCollection;
    }

    public static void main(String args[]) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                ScatterShape demo = new ScatterShape(title);
                demo.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                demo.pack();
                demo.setLocationRelativeTo(null);
                demo.setVisible(true);
            }
        });
    }
}
Claypan answered 3/12, 2013 at 18:47 Comment(5)
Thank you very much for the answer . I would like to know is there any way I can change the shape of the XYSeries itself i.e. to set different shape for each XYSeries ,instead of checking each point to which XYSeries it belongs then changing the shape .Tymothy
The XYSeries in your dataset comprise the model; the renderer is the view. The view should hold a reference to the model with which to query the model for the information it needs to decide which shape to render.Claypan
That Intersected dotted lines drawn on the graph using these two lines of code xyPlot.setDomainCrosshairVisible(true); xyPlot.setRangeCrosshairVisible(true); is changing, i mean how can i lock it at 0,0 co-ordinate independent of mouse click?Petropavlovsk
I'd look at a Marker.Claypan
hi trashgod, have you seen my question in the comment section #41712118 any clue on how to do ?Petropavlovsk

© 2022 - 2024 — McMap. All rights reserved.