Unable to change legend shape and font in polar plot - java

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

Related

Sorting 2D Array ( String ) [duplicate]

I'm trying to create a custom dynamic histogram type bar graph in Java. I've searched a lot but coudn't find a way to achieve this.
I'm aware of the JFreeChart library but it doesn't meet my needs. This is what the JFreeChart histogram looks like :
But what I want is a dyanamic Histogram with just X-axis.
This photoshopped image will make it easier to understand.
The JFrame will have fixed boundaries. As you can see, there should be no Y-axis. The bars' height should adjust automatically based on the values.
Please help me build this! Thanks in advance.
A poor man's histogram:
import java.awt.*;
import java.util.List;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.border.*;
public class HistogramPanel extends JPanel
{
private int histogramHeight = 200;
private int barWidth = 50;
private int barGap = 10;
private JPanel barPanel;
private JPanel labelPanel;
private List<Bar> bars = new ArrayList<Bar>();
public HistogramPanel()
{
setBorder( new EmptyBorder(10, 10, 10, 10) );
setLayout( new BorderLayout() );
barPanel = new JPanel( new GridLayout(1, 0, barGap, 0) );
Border outer = new MatteBorder(1, 1, 1, 1, Color.BLACK);
Border inner = new EmptyBorder(10, 10, 0, 10);
Border compound = new CompoundBorder(outer, inner);
barPanel.setBorder( compound );
labelPanel = new JPanel( new GridLayout(1, 0, barGap, 0) );
labelPanel.setBorder( new EmptyBorder(5, 10, 0, 10) );
add(barPanel, BorderLayout.CENTER);
add(labelPanel, BorderLayout.PAGE_END);
}
public void addHistogramColumn(String label, int value, Color color)
{
Bar bar = new Bar(label, value, color);
bars.add( bar );
}
public void layoutHistogram()
{
barPanel.removeAll();
labelPanel.removeAll();
int maxValue = 0;
for (Bar bar: bars)
maxValue = Math.max(maxValue, bar.getValue());
for (Bar bar: bars)
{
JLabel label = new JLabel(bar.getValue() + "");
label.setHorizontalTextPosition(JLabel.CENTER);
label.setHorizontalAlignment(JLabel.CENTER);
label.setVerticalTextPosition(JLabel.TOP);
label.setVerticalAlignment(JLabel.BOTTOM);
int barHeight = (bar.getValue() * histogramHeight) / maxValue;
Icon icon = new ColorIcon(bar.getColor(), barWidth, barHeight);
label.setIcon( icon );
barPanel.add( label );
JLabel barLabel = new JLabel( bar.getLabel() );
barLabel.setHorizontalAlignment(JLabel.CENTER);
labelPanel.add( barLabel );
}
}
private class Bar
{
private String label;
private int value;
private Color color;
public Bar(String label, int value, Color color)
{
this.label = label;
this.value = value;
this.color = color;
}
public String getLabel()
{
return label;
}
public int getValue()
{
return value;
}
public Color getColor()
{
return color;
}
}
private class ColorIcon implements Icon
{
private int shadow = 3;
private Color color;
private int width;
private int height;
public ColorIcon(Color color, int width, int height)
{
this.color = color;
this.width = width;
this.height = height;
}
public int getIconWidth()
{
return width;
}
public int getIconHeight()
{
return height;
}
public void paintIcon(Component c, Graphics g, int x, int y)
{
g.setColor(color);
g.fillRect(x, y, width - shadow, height);
g.setColor(Color.GRAY);
g.fillRect(x + width - shadow, y + shadow, shadow, height - shadow);
}
}
private static void createAndShowGUI()
{
HistogramPanel panel = new HistogramPanel();
panel.addHistogramColumn("A", 350, Color.RED);
panel.addHistogramColumn("B", 690, Color.YELLOW);
panel.addHistogramColumn("C", 510, Color.BLUE);
panel.addHistogramColumn("D", 570, Color.ORANGE);
panel.addHistogramColumn("E", 180, Color.MAGENTA);
panel.addHistogramColumn("F", 504, Color.CYAN);
panel.layoutHistogram();
JFrame frame = new JFrame("Histogram Panel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( panel );
frame.setLocationByPlatform( true );
frame.pack();
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
}
I'd give JFreeChart a second look. You can make the range axis invisible and use item labels instead. Absent a legend, overriding getItemPaint() can supply arbitrary colors. An alternative approach is shown here.
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Paint;
import javax.swing.JFrame;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.labels.ItemLabelAnchor;
import org.jfree.chart.labels.ItemLabelPosition;
import org.jfree.chart.labels.StandardCategoryItemLabelGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.renderer.category.StandardBarPainter;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.ui.TextAnchor;
/**
* #see https://stackoverflow.com/a/29709153/230513
*/
public class BarChart {
private CategoryDataset createDataset() {
String row = "Row";
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
dataset.addValue(350, row, "A");
dataset.addValue(690, row, "B");
dataset.addValue(510, row, "C");
dataset.addValue(570, row, "D");
dataset.addValue(180, row, "E");
dataset.addValue(504, row, "F");
return dataset;
}
private JFreeChart createChart(CategoryDataset dataset) {
CategoryAxis categoryAxis = new CategoryAxis("");
ValueAxis valueAxis = new NumberAxis("");
valueAxis.setVisible(false);
BarRenderer renderer = new BarRenderer() {
#Override
public Paint getItemPaint(int row, int column) {
switch (column) {
case 0:
return Color.red;
case 1:
return Color.yellow;
case 2:
return Color.blue;
case 3:
return Color.orange;
case 4:
return Color.gray;
case 5:
return Color.green.darker();
default:
return Color.red;
}
}
};
renderer.setDrawBarOutline(false);
renderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator());
renderer.setBasePositiveItemLabelPosition(new ItemLabelPosition(
ItemLabelAnchor.OUTSIDE12, TextAnchor.BOTTOM_CENTER));
renderer.setBaseItemLabelsVisible(Boolean.TRUE);
renderer.setBarPainter(new StandardBarPainter());
CategoryPlot plot = new CategoryPlot(dataset, categoryAxis, valueAxis, renderer);
JFreeChart chart = new JFreeChart("", JFreeChart.DEFAULT_TITLE_FONT, plot, false);
chart.setBackgroundPaint(Color.white);
return chart;
}
private void display() {
JFrame f = new JFrame("BarChart");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new ChartPanel(createChart(createDataset())));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
new BarChart().display();
});
}
}

Jfreechart make a dynamic scrollable chart [duplicate]

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);

Changing shape of single point in JFreeChart XYPLot

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);
}
});
}
}

Dynamically repaint ScatterPlot in jfreechart

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);
}
});
}
}

JFreeChart line chart with text at each point

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);
}

Categories

Resources