I would like put text over each point I plotted in a line chart.
This is what I can do:
And this is what I need (names of point are in green):
The StandardXYItemLabelGenerator should work; there's an example here.
Addendum: The labels you can see in the picture are in a separate string array.
Such labels may be incorporated in the XYDataset, as shown in LabeledXYDataset below. Because none of the features of StandardXYItemLabelGenerator are used, a custom implementation of XYItemLabelGenerator is sufficient. The XYItemRenderer controls the color, size and relative position of the labels.
Addendum: How can I add tooltips?
Guided by ChartFactory.createXYLineChart(), simply specify a XYToolTipGenerator to the renderer. The default format, seen here, is Series: (x, y).
renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator());
import java.awt.Color;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import org.jfree.chart.*;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.labels.ItemLabelAnchor;
import org.jfree.chart.labels.ItemLabelPosition;
import org.jfree.chart.labels.XYItemLabelGenerator;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.AbstractXYDataset;
import org.jfree.data.xy.XYDataset;
import org.jfree.ui.TextAnchor;
/** #see https://stackoverflow.com/a/14459322/230513 */
public class UnitPrice {
private static XYDataset createDataset() {
LabeledXYDataset ds = new LabeledXYDataset();
ds.add(11, 0, "");
ds.add(12, 68, "A");
ds.add(13, 65, "B");
ds.add(14, 67, "C");
ds.add(15, 69, "D");
ds.add(16, 60, "F");
ds.add(17, 83, "G");
ds.add(18, 86, "H");
ds.add(19, 70, "I");
ds.add(20, 60, "J");
ds.add(21, 55, "K");
ds.add(22, 54, "L");
ds.add(23, 50, "M");
return ds;
}
private static class LabeledXYDataset extends AbstractXYDataset {
private static final int N = 26;
private List<Number> x = new ArrayList<Number>(N);
private List<Number> y = new ArrayList<Number>(N);
private List<String> label = new ArrayList<String>(N);
public void add(double x, double y, String label){
this.x.add(x);
this.y.add(y);
this.label.add(label);
}
public String getLabel(int series, int item) {
return label.get(item);
}
#Override
public int getSeriesCount() {
return 1;
}
#Override
public Comparable getSeriesKey(int series) {
return "Unit";
}
#Override
public int getItemCount(int series) {
return label.size();
}
#Override
public Number getX(int series, int item) {
return x.get(item);
}
#Override
public Number getY(int series, int item) {
return y.get(item);
}
}
private static class LabelGenerator implements XYItemLabelGenerator {
#Override
public String generateLabel(XYDataset dataset, int series, int item) {
LabeledXYDataset labelSource = (LabeledXYDataset) dataset;
return labelSource.getLabel(series, item);
}
}
private static JFreeChart createChart(final XYDataset dataset) {
NumberAxis domain = new NumberAxis("Unit");
NumberAxis range = new NumberAxis("Price");
domain.setAutoRangeIncludesZero(false);
XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false);
renderer.setBaseItemLabelGenerator(new LabelGenerator());
renderer.setBaseItemLabelPaint(Color.green.darker());
renderer.setBasePositiveItemLabelPosition(
new ItemLabelPosition(ItemLabelAnchor.CENTER, TextAnchor.CENTER));
renderer.setBaseItemLabelFont(
renderer.getBaseItemLabelFont().deriveFont(14f));
renderer.setBaseItemLabelsVisible(true);
renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator());
XYPlot plot = new XYPlot(dataset, domain, range, renderer);
JFreeChart chart = new JFreeChart(
"Unit Price", JFreeChart.DEFAULT_TITLE_FONT, plot, false);
return chart;
}
public static void main(String[] args) {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
XYDataset dataset = createDataset();
JFreeChart chart = createChart(dataset);
ChartPanel chartPanel = new ChartPanel(chart) {
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 320);
}
};
f.add(chartPanel);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
Use XYTextAnnotation:
for(int i = 0; i < data.length; i++){
XYTextAnnotation textAnnotaion = new XYTextAnnotation(
"" + data[i][anotation], data[i][X], data[i][Y]);
plot.addAnnotation(textAnnotaion);
}
Related
I am trying to create a sky plot in java using the jfreechart polar plot.
Everything is working fine. However i was not able to change the various series legend markers and their size.
Here is an example for my code:
public class Skyplot {
private JFrame plotFrame = null;
public static void main (String[] args){
Skyplot sp = new Skyplot();
sp.display();
}
public Skyplot(){
}
public void display(){
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
plot();
}
});
}
private void plot(){
// create and set up the window
plotFrame = new JFrame("Visualizer");
plotFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
//Display the window.
plotFrame.pack();
plotFrame.setVisible(true);
plotFrame.setLocation(500, 500);
plotFrame.setSize(1200, 900);
// set up the content pane
Container C = plotFrame.getContentPane();
Plotter pl = new Plotter();
pl.setBorder(BorderFactory.createRaisedBevelBorder());
pl.setBackground(Color.WHITE);
C.setLayout(new GridLayout(1, 1));
C.add(pl);
}
private class Plotter extends JPanel {
private static final long serialVersionUID = 1L;
public Plotter(){
XYDataset dataset = getXYDataset();
final ChartPanel chartPanel = createChartPanel(dataset);
this.add(chartPanel, BorderLayout.CENTER);
}
private ChartPanel createChartPanel(XYDataset dataset) {
// Create chart
JFreeChart chart = ChartFactory.createPolarChart("Skyplot", dataset, true, true, false);
PolarPlot polPlot = (PolarPlot) chart.getPlot();
polPlot.setRadiusMinorGridlinesVisible(false);
polPlot.setBackgroundPaint(Color.WHITE);
polPlot.setRadiusGridlinePaint(Color.DARK_GRAY);
polPlot.setAngleGridlinePaint(Color.BLACK);
DefaultPolarItemRenderer renderer = (DefaultPolarItemRenderer) polPlot.getRenderer();
renderer.setBaseLegendShape(new Rectangle(15,15));
Font legend_font = new Font("Verdana", Font.PLAIN, 24);
renderer.setBaseLegendTextFont(legend_font);
NumberAxis rangeAxis = (NumberAxis) polPlot.getAxis();
rangeAxis.setTickUnit(new NumberTickUnit(10.0));
rangeAxis.setMinorTickMarksVisible(false);
rangeAxis.setRange(0.0, 90.0);
rangeAxis.setInverted(true);
return new ChartPanel(chart){
#Override
public Dimension getPreferredSize() {
double H = plotFrame.getHeight()*0.9;
double W = plotFrame.getWidth()*0.9;
return new Dimension((int)W, (int)H);
}
};
}
private XYDataset getXYDataset() {
XYSeriesCollection dataset = new XYSeriesCollection();
XYSeries g01= new XYSeries("G01");
series.add(35, 45);
dataset.addSeries(g01);
XYSeries g02= new XYSeries("G02");
series.add(105, 73);
dataset.addSeries(g01);
XYSeries g03= new XYSeries("G03");
series.add(264, 15);
dataset.addSeries(g03);
return dataset;
}
}
}
Any idea why would it not work?
Am i doing something wrong with the Renderer?
Any help would be appreciated. thank you.
The missing piece appears to be telling the parent AbstractRenderer to setShapesVisible().
renderer.setShapesVisible(true);
I want to make all shapes rectangle and the text inside the legend larger.
To coordinate colors and shapes in the chart and legend, use a custom DrawingSupplier, as suggested here. The exact details will depend on your use case, but the basic approach would be to override the DefaultDrawingSupplier methods getNextPaint() and getNextShape() so they return your chosen colors and shapes.
To make the legend text larger, update the chart's LegendTitle, as illustrated below.
LegendTitle title = chart.getLegend();
title.setItemFont(new Font(Font.SERIF, Font.BOLD, 24));
As updated:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Rectangle;
import javax.swing.JFrame;
import javax.swing.JPanel;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.plot.PolarPlot;
import org.jfree.chart.renderer.DefaultPolarItemRenderer;
import org.jfree.chart.title.LegendTitle;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
/** #see https://stackoverflow.com/a/46931762/230513 */
public class Skyplot {
private JFrame plotFrame = null;
public static void main(String[] args) {
EventQueue.invokeLater(new Skyplot()::plot);
}
private void plot() {
plotFrame = new JFrame("Visualizer");
plotFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
plotFrame.add(new Plotter());
plotFrame.pack();
plotFrame.setLocationRelativeTo(null);
plotFrame.setVisible(true);
}
private static final class Plotter extends JPanel {
public Plotter() {
super(new GridLayout());
this.add(createChartPanel(getXYDataset()));
}
private ChartPanel createChartPanel(XYDataset dataset) {
JFreeChart chart = ChartFactory.createPolarChart("Skyplot", dataset, true, true, false);
LegendTitle title = chart.getLegend();
title.setItemFont(new Font(Font.SERIF, Font.BOLD, 24));
PolarPlot polPlot = (PolarPlot) chart.getPlot();
polPlot.setRadiusMinorGridlinesVisible(false);
polPlot.setBackgroundPaint(Color.WHITE);
polPlot.setRadiusGridlinePaint(Color.DARK_GRAY);
polPlot.setAngleGridlinePaint(Color.BLACK);
DefaultPolarItemRenderer renderer = (DefaultPolarItemRenderer) polPlot.getRenderer();
renderer.setShapesVisible(true);
NumberAxis rangeAxis = (NumberAxis) polPlot.getAxis();
rangeAxis.setTickUnit(new NumberTickUnit(10.0));
rangeAxis.setMinorTickMarksVisible(false);
rangeAxis.setRange(0.0, 90.0);
rangeAxis.setInverted(true);
return new ChartPanel(chart) {
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 600);
}
};
}
private XYDataset getXYDataset() {
XYSeriesCollection dataset = new XYSeriesCollection();
XYSeries g01 = new XYSeries("G01");
g01.add(35, 45);
dataset.addSeries(g01);
XYSeries g02 = new XYSeries("G02");
g02.add(105, 73);
dataset.addSeries(g02);
XYSeries g03 = new XYSeries("G03");
g03.add(264, 15);
dataset.addSeries(g03);
return dataset;
}
}
}
I have used the advise from above.
The method to create the chart looks like this now:
private ChartPanel createChartPanel(XYDataset dataset) {
// Create chart
JFreeChart chart = ChartFactory.createPolarChart("Skyplot", dataset, true, true, false);
PolarPlot polPlot = (PolarPlot) chart.getPlot();
polPlot.setRadiusMinorGridlinesVisible(false);
polPlot.setBackgroundPaint(Color.WHITE);
polPlot.setRadiusGridlinePaint(Color.DARK_GRAY);
polPlot.setAngleGridlinePaint(Color.BLACK);
polPlot.setDrawingSupplier(new DrawingSupplier() {
#Override
public Paint getNextFillPaint() {
// TODO Auto-generated method stub
return null;
}
#Override
public Paint getNextOutlinePaint() {
// TODO Auto-generated method stub
return null;
}
#Override
public Stroke getNextOutlineStroke() {
// TODO Auto-generated method stub
return null;
}
#Override
public Paint getNextPaint() {
Random rand = new Random();
int r = rand.nextInt(255);
int g = rand.nextInt(255);
int b = rand.nextInt(255);
return new Color(r,g,b);
}
#Override
public Shape getNextShape() {
return ShapeUtilities.createDiamond(5f);
}
#Override
public Stroke getNextStroke() {
// TODO Auto-generated method stub
return null;
}
});
NumberAxis rangeAxis = (NumberAxis) polPlot.getAxis();
rangeAxis.setTickUnit(new NumberTickUnit(10.0));
rangeAxis.setMinorTickMarksVisible(false);
rangeAxis.setRange(0.0, 90.0);
rangeAxis.setInverted(true);
LegendTitle legend = chart.getLegend();
Font legend_font = new Font("Verdana", Font.BOLD, 18);
legend.setItemFont(legend_font);
return new ChartPanel(chart){
#Override
public Dimension getPreferredSize() {
double H = plotFrame.getHeight()*0.9;
double W = plotFrame.getWidth()*0.9;
return new Dimension((int)W, (int)H);
}
};
}
The result of using the DrawingSupplier is as follows:
resulting skyplot
How can I use JFreeChart to display just the most recent data in a continually updated time series?
Addenda: A complete, working example that incorporates the accepted answer is shown here. See also this variation having two series. See also this Q&A regarding setTimeBase().
The JFreeChart class DynamicTimeSeriesCollection is a good choice.
Addendum: As noted by #Bahadır, the last point of the series was persistently zero. #Don helpfully suggests advancing the time and then appending the data.
dataset.advanceTime();
dataset.appendData(newData);
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JPanel;
import javax.swing.Timer;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.time.DynamicTimeSeriesCollection;
import org.jfree.data.time.Second;
import org.jfree.data.xy.XYDataset;
import org.jfree.chart.ui.ApplicationFrame;
import org.jfree.chart.ui.UIUtils;
/**
* #see http://stackoverflow.com/a/15521956/230513
* #see http://stackoverflow.com/questions/5048852
*/
public class DTSCTest extends ApplicationFrame {
private static final String TITLE = "Dynamic Series";
private static final String START = "Start";
private static final String STOP = "Stop";
private static final float MINMAX = 100;
private static final int COUNT = 2 * 60;
private static final int FAST = 100;
private static final int SLOW = FAST * 5;
private static final Random random = new Random();
private Timer timer;
public DTSCTest(final String title) {
super(title);
final DynamicTimeSeriesCollection dataset =
new DynamicTimeSeriesCollection(1, COUNT, new Second());
dataset.setTimeBase(new Second(0, 0, 0, 1, 1, 2011));
dataset.addSeries(gaussianData(), 0, "Gaussian data");
JFreeChart chart = createChart(dataset);
final JButton run = new JButton(STOP);
run.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if (STOP.equals(cmd)) {
timer.stop();
run.setText(START);
} else {
timer.start();
run.setText(STOP);
}
}
});
final JComboBox combo = new JComboBox();
combo.addItem("Fast");
combo.addItem("Slow");
combo.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if ("Fast".equals(combo.getSelectedItem())) {
timer.setDelay(FAST);
} else {
timer.setDelay(SLOW);
}
}
});
this.add(new ChartPanel(chart) {
#Override
public Dimension getPreferredSize() {
return new Dimension(640, 480);
}
}, BorderLayout.CENTER);
JPanel btnPanel = new JPanel(new FlowLayout());
btnPanel.add(run);
btnPanel.add(combo);
this.add(btnPanel, BorderLayout.SOUTH);
timer = new Timer(FAST, new ActionListener() {
float[] newData = new float[1];
#Override
public void actionPerformed(ActionEvent e) {
newData[0] = randomValue();
dataset.advanceTime();
dataset.appendData(newData);
}
});
}
private float randomValue() {
return (float) (random.nextGaussian() * MINMAX / 3);
}
private float[] gaussianData() {
float[] a = new float[COUNT];
for (int i = 0; i < a.length; i++) {
a[i] = randomValue();
}
return a;
}
private JFreeChart createChart(final XYDataset dataset) {
final JFreeChart result = ChartFactory.createTimeSeriesChart(
TITLE, "hh:mm:ss", "milliVolts", dataset, true, true, false);
final XYPlot plot = result.getXYPlot();
ValueAxis domain = plot.getDomainAxis();
domain.setAutoRange(true);
ValueAxis range = plot.getRangeAxis();
range.setRange(-MINMAX, MINMAX);
return result;
}
public void start() {
timer.start();
}
public static void main(final String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
DTSCTest demo = new DTSCTest(TITLE);
demo.pack();
UIUtils.centerFrameOnScreen(demo);
demo.setVisible(true);
demo.start();
}
});
}
}
You can also eliminate the zero by first advanceTime(), then appendData. (swap the way they are doing it in the example).
One alternative approach to #thrashgod's answer would be to use TimeSeriesCollection and setting item age on the TimeSeries. Below code can setup a graph to show last 1 hour of data with 1 minute intervals.
private TimeSeriesCollection dataset;
private TimeSeries sensorSeries;
sensorSeries = new TimeSeries("name", Minute.class);
sensorSeries.setMaximumItemAge(60);
dataset = new TimeSeriesCollection();
dataset.addSeries(sensorSeries);
..and you will add the data as it comes with:
sensorSeries.add(new Minute(new Date()), newData);
I m using JFreeChart to draw Running or Moving Sine Wave in java with netbeans. I write code for it , but it does not like moving or running sine wave graph. So if you have any idea regarding that then suggest me.
My code is below
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JPanel;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.ApplicationFrame;
public class DrawChart extends ApplicationFrame implements ActionListener {
public XYSeries series;
public DrawChart(final String title) {
super(title);
series = new XYSeries("Sine", true, true);
XYSeriesCollection dataset = new XYSeriesCollection(series);
final JFreeChart chart = createChart(dataset);
final ChartPanel chartPanel = new ChartPanel(chart);
final JButton button = new JButton("Add New Data Item");
button.setActionCommand("ADD_DATA");
button.addActionListener(this);
final JPanel content = new JPanel(new BorderLayout());
content.add(chartPanel);
content.add(button, BorderLayout.SOUTH);
chartPanel.setPreferredSize(new java.awt.Dimension(500, 270));
setContentPane(content);
}
private JFreeChart createChart(final XYDataset dataset) {
JFreeChart jfreechart = ChartFactory.createXYLineChart("Sin Curve", "Angle (Deg)", "Value", dataset, PlotOrientation.VERTICAL, true, true, true);
jfreechart.setBackgroundPaint(Color.white);
XYPlot xyplot = (XYPlot) jfreechart.getPlot();
xyplot.setBackgroundPaint(Color.lightGray);
xyplot.setDomainGridlinePaint(Color.white);
xyplot.setRangeGridlinePaint(Color.white);
return jfreechart;
}
public void actionPerformed(final ActionEvent e) {
if (e.getActionCommand().equals("ADD_DATA")) {
for (int i = 0; i < 100; i++) {
final double x = (i)/10.0 ;
final double y = Math.sin(x);
this.series.addOrUpdate(x, y);
}
}
}
public static void main(final String[] args) {
final DrawChart demo = new DrawChart("Dynamic Data view");
demo.pack();
RefineryUtilities.centerFrameOnScreen(demo);
demo.setVisible(true);
}
}
Starting from this example, substituting Math.sin() for nextGaussian() produced the illustration below. A javax.swing.Timer paces the animation at 1 Hz.
private ChartPanel createPane() {
final XYSeries series = new XYSeries("Data");
for (int i = 0; i < random.nextInt(N) + N / 2; i++) {
series.add(i, Math.sin(i));
}
XYSeriesCollection dataset = new XYSeriesCollection(series);
new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
series.add(series.getItemCount(), Math.sin(series.getItemCount()));
}
}).start();
…
}
Try this JavaFX. It's more flexible.
This is what I gathered and came up with using JavaFX
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javafx.animation.AnimationTimer;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Series;
import javafx.stage.Stage;
public class ACS extends Application
{
private static final int MAX_DATA_POINTS = 200;
private Series series;
private float xSeriesData = 0;
private final ConcurrentLinkedQueue<Number> dataQ = new ConcurrentLinkedQueue<Number>();
private ExecutorService executor;
private AddToQueue addToQueue;
private Timeline timeline2;
private NumberAxis xAxis;
private void init(final Stage primaryStage)
{
xAxis = new NumberAxis(0, MAX_DATA_POINTS, 1);
xAxis.setForceZeroInRange(false);
xAxis.setAutoRanging(false);
final NumberAxis yAxis = new NumberAxis(0, 200, 1);
yAxis.setAutoRanging(false);
// -- Chart
final LineChart<Number, Number> sc = new LineChart<Number, Number>(xAxis, yAxis) {
// Override to remove symbols on each data point
#Override
protected void dataItemAdded(final Series<Number, Number> series, final int itemIndex, final Data<Number, Number> item)
{}
};
sc.setAnimated(false);
sc.setId("liveAreaChart");
sc.setTitle("Animated Area Chart");
// -- Chart Series
series = new LineChart.Series<Number, Number>();
series.setName("Area Chart Series");
sc.getData().add(series);
final Scene scene = new Scene(sc, 800, 800);
scene.getStylesheets().add("site.css");
sc.getStyleClass().add("thick-chart");
primaryStage.setScene(scene);
}
#Override
public void start(final Stage primaryStage) throws Exception
{
init(primaryStage);
primaryStage.show();
// -- Prepare Executor Services
executor = Executors.newCachedThreadPool();
addToQueue = new AddToQueue();
executor.execute(addToQueue);
// -- Prepare Timeline
prepareTimeline();
}
public static void main(final String[] args)
{
launch(args);
}
private class AddToQueue implements Runnable
{
double PERIOD = 20;
double SCALE = 20;
int pos = 0;
#Override
public void run()
{
try {
final double Min = 20;
final double Max = 55;
// add a item of random data to queue
//uncomment the line below to generate a normal graph
// dataQ.add(Min + (Math.random() * ((Max - Min))));
dataQ.add(((Math.sin((++pos * 2 * Math.PI) / PERIOD) * (SCALE / 2)) + (SCALE / 2)));
Thread.sleep(1000);
executor.execute(this);
}
catch (final InterruptedException ex) {
// Logger.getLogger(ACS.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
// -- Timeline gets called in the JavaFX Main thread
private void prepareTimeline()
{
// Every frame to take any data from queue and add to chart
new AnimationTimer() {
#Override
public void handle(final long now)
{
addDataToSeries();
}
}.start();
}
private void addDataToSeries()
{
for (int i = 0; i < 20; i++) { // -- add 20 numbers to the plot+
if (dataQ.isEmpty()) {
break;
}
// series.getData().add(new LineChart.Data(xSeriesData++, dataQ.remove()));
final Number datapoint = dataQ.remove();
xSeriesData = xSeriesData + 1;
System.out.println(xSeriesData + " " + datapoint);
series.getData().add(new LineChart.Data(xSeriesData, datapoint));
}
// remove points to keep us at no more than MAX_DATA_POINTS
if (series.getData().size() > (MAX_DATA_POINTS * 10)) {
series.getData().remove(0, series.getData().size() - (MAX_DATA_POINTS * 10));
}
// update
xAxis.setLowerBound(xSeriesData - (MAX_DATA_POINTS));
xAxis.setUpperBound(xSeriesData - 1);
}
}
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 .
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)
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);
}
}
});
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 http://stackoverflow.com/a/20359200/230513
* #see http://stackoverflow.com/a/6669529/230513
*/
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);
}
});
}
}
I have a scatterplot which plots positions of agents. These positions change. I was wondering how can I repaint/redraw the scatterplot with the new positions
my drawing method. I need to redraw in the updatePositions function. Is there any way to implement any listener for ScatterPlot?
private ChartPanel createPanel() {
JFreeChart jfreechart = ChartFactory.createScatterPlot(
title, "", "", initPositions(),PlotOrientation.VERTICAL, true, true, false);
XYPlot xyPlot = (XYPlot) jfreechart.getPlot();
xyPlot.setDomainCrosshairVisible(true);
xyPlot.setRangeCrosshairVisible(true);
XYItemRenderer renderer = xyPlot.getRenderer();
renderer.setSeriesPaint(0, Color.blue);
adjustAxis((NumberAxis) xyPlot.getDomainAxis(), true);
adjustAxis((NumberAxis) xyPlot.getRangeAxis(), false);
xyPlot.setBackgroundPaint(Color.white);
return new ChartPanel(jfreechart);
}
private void adjustAxis(NumberAxis axis, boolean vertical) {
axis.setRange(-1, lattice+1);
axis.setTickUnit(new NumberTickUnit(1));
axis.setVerticalTickLabels(vertical);
}
private XYDataset initPositions() {
XYSeriesCollection xySeriesCollection = new XYSeriesCollection();
for (int i = 0; i < populationSize; i++) {
if(population.get(i).status==1){
healthy.add(population.get(i).position[0], population.get(i).position[1]);
}else if(population.get(i).status==2){
infected.add(population.get(i).position[0], population.get(i).position[1]);
}else if(population.get(i).status==3){
recovered.add(population.get(i).position[0], population.get(i).position[1]);
}
}
xySeriesCollection.addSeries(healthy);
xySeriesCollection.addSeries(infected);
xySeriesCollection.addSeries(recovered);
return xySeriesCollection;
}
public void clear(){
healthy.clear();
infected.clear();
recovered.clear();
}
public void updatePositions(ArrayList<Person> pop ){
population = pop;
for (int i = 0; i < populationSize; i++) {
if(population.get(i).status==1){
healthy.addOrUpdate(population.get(i).position[0], population.get(i).position[1]);
}else if(population.get(i).status==2){
infected.addOrUpdate(population.get(i).position[0], population.get(i).position[1]);
}else if(population.get(i).status==3){
recovered.addOrUpdate(population.get(i).position[0], population.get(i).position[1]);
}
}
}
this is the method in the main class. The update of the positions is done at the "move" function
public static void main(String [] args){
createPopulation(populationSize);
initInfection(infectRatio);
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
DrawArea demo = new DrawArea("Demo", lattice, populationSize,population);
demo.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
demo.pack();
demo.setLocationRelativeTo(null);
demo.setVisible(true);
for(int i =0;i<1000;i++){
for(int j=0; j<populationSize; j++){
population.get(j).move(0.8);
}
demo.clear();
demo.updatePositions(population);
}
}
});
}
As shown below, the chart (view) listens to its dataset (model) and updates itself accordingly. When the Move button is pressed, each XYDataItem in the series in modified in update() to reflect its new position.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.util.*;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
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.XYItemRenderer;
import org.jfree.data.xy.XYDataItem;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
/**
* #see http://stackoverflow.com/a/19749344/230513
* #see http://stackoverflow.com/a/7208723/230513
*/
public class ScatterMove extends JFrame {
private static final int N = 16;
private static final String title = "Scatter Move Demo";
private static final Random rand = new Random();
private XYSeries moved = new XYSeries("Population");
public ScatterMove(String s) {
super(s);
update();
final ChartPanel chartPanel = createDemoPanel();
this.add(chartPanel, BorderLayout.CENTER);
JPanel control = new JPanel();
control.add(new JButton(new AbstractAction("Move") {
#Override
public void actionPerformed(ActionEvent e) {
moved.clear();
update();
}
}));
this.add(control, BorderLayout.SOUTH);
}
private void update() {
for (int i = 0; i < N; i++) {
moved.add(new XYDataItem(rand.nextGaussian(), rand.nextGaussian()));
}
}
private ChartPanel createDemoPanel() {
JFreeChart jfreechart = ChartFactory.createScatterPlot(
title, "X", "Y", createSampleData(),
PlotOrientation.VERTICAL, true, true, false);
XYPlot xyPlot = (XYPlot) jfreechart.getPlot();
XYItemRenderer renderer = xyPlot.getRenderer();
NumberAxis domain = (NumberAxis) xyPlot.getDomainAxis();
domain.setRange(-3.0, 3.0);
domain.setTickUnit(new NumberTickUnit(1));
NumberAxis range = (NumberAxis) xyPlot.getRangeAxis();
range.setRange(-3.0, 3.0);
range.setTickUnit(new NumberTickUnit(1));
return new ChartPanel(jfreechart){
#Override
public Dimension getPreferredSize() {
return new Dimension(640, 480);
}
};
}
private XYDataset createSampleData() {
XYSeriesCollection xySeriesCollection = new XYSeriesCollection();
xySeriesCollection.addSeries(moved);
return xySeriesCollection;
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
ScatterMove demo = new ScatterMove(title);
demo.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
demo.pack();
demo.setLocationRelativeTo(null);
demo.setVisible(true);
}
});
}
}