XYDataset with a single data point : nothing gets plotted - jfreechart

This is a corner case I encountered. Below's a SSCCE:
import java.util.*;
import java.io.*;
import java.awt.Color;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import org.jfree.chart.*;
import org.jfree.chart.plot.*;
import org.jfree.chart.axis.*;
import org.jfree.data.time.*;
import org.jfree.data.xy.XYDataset;
public class FooMain {
public static void main(String args[]) throws Exception {
BufferedImage img = timeAxisSSEProcessionsChart();
ImageIO.write(img, "png", new File("img.png"));
System.exit(0);
}
private static XYDataset createTimeSeriesDataset() {
TimeSeries timeSeries = new TimeSeries("series-a");
timeSeries.add(RegularTimePeriod.createInstance(Millisecond.class, new java.util.Date(0) , TimeZone.getTimeZone("Zulu")), 100);
// if below line is commented out, nothing is plotted:
timeSeries.add(RegularTimePeriod.createInstance(Millisecond.class, new java.util.Date(1000), TimeZone.getTimeZone("Zulu")), 100);
TimeSeriesCollection rv = new TimeSeriesCollection();
rv.addSeries(timeSeries);
return rv;
}
public static BufferedImage timeAxisSSEProcessionsChart() throws Exception {
XYDataset dataset = createTimeSeriesDataset();
JFreeChart chart = ChartFactory.createTimeSeriesChart("title", "date", "series-a", dataset, true, true, false);
InputStream in = new ByteArrayInputStream(imageBytesFromChart(chart, 600, 400));
return ImageIO.read(in);
}
private static byte[] imageBytesFromChart(JFreeChart chart, int width, int height) {
BufferedImage objBufferedImage = chart.createBufferedImage(width, height);
ByteArrayOutputStream bas = new ByteArrayOutputStream();
try {
ImageIO.write(objBufferedImage, "png", bas);
} catch (IOException e) {
e.printStackTrace();
}
byte[] byteArray=bas.toByteArray();
return byteArray;
}
}
The above code produces a plot as expected.
If however we comment out the line indicated in the createTimeSeriesDataset method, then nothing gets plotted:
So the question is: how can I ensure that at least a dot (or some other mark) gets printed in the corner case where the XYDataset contains only one data point?

The time series chart primarily consists of lines connecting the individual data points. When there is only one data element, there are no data points to connect. So far, so obvious.
One possible solution would be to enable the "tick shapes" for the chart when there is only one entry. I'm not sure whether this is an appropriate solution for your case. But this could be done with a method like
private static void showTickMarksForSingleElements(
XYDataset dataset, JFreeChart chart)
{
TimeSeriesCollection timeSeriesCollection =
(TimeSeriesCollection)dataset;
List<?> series = timeSeriesCollection.getSeries();
TimeSeries timeSeries = (TimeSeries) series.get(0);
if (timeSeries.getItemCount() == 1)
{
XYPlot plot = (XYPlot) chart.getPlot();
XYLineAndShapeRenderer renderer
= (XYLineAndShapeRenderer) plot.getRenderer();
renderer.setSeriesShapesVisible(0, true);
}
}
(Caution, there are obviously some assumptions about the types of the given objects involved - in doubt, check the types before doing the casts!)
The result would then be a single tick mark for time series that only contain a single element:
Here as another MVCE:
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.TimeZone;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.time.Millisecond;
import org.jfree.data.time.RegularTimePeriod;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.XYDataset;
public class SingleElementChart {
public static void main(String args[]) throws Exception {
BufferedImage img = timeAxisSSEProcessionsChart();
show(img);
}
private static void show(final BufferedImage img)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new JLabel(new ImageIcon(img)));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
});
}
private static TimeSeriesCollection createTimeSeriesDataset() {
TimeSeries timeSeries = new TimeSeries("series-a");
timeSeries.add(RegularTimePeriod.createInstance(
Millisecond.class, new java.util.Date(0),
TimeZone.getTimeZone("Zulu")), 100);
// if below line is commented out, nothing is plotted:
//timeSeries.add(RegularTimePeriod.createInstance(
// Millisecond.class, new java.util.Date(1000),
// TimeZone.getTimeZone("Zulu")), 100);
TimeSeriesCollection rv = new TimeSeriesCollection();
rv.addSeries(timeSeries);
return rv;
}
public static BufferedImage timeAxisSSEProcessionsChart() throws Exception {
XYDataset dataset = createTimeSeriesDataset();
JFreeChart chart = ChartFactory.createTimeSeriesChart(
"title", "date", "series-a", dataset, true, true, false);
showTickMarksForSingleElements(dataset, chart);
InputStream in = new ByteArrayInputStream(
imageBytesFromChart(chart, 600, 400));
return ImageIO.read(in);
}
private static void showTickMarksForSingleElements(
XYDataset dataset, JFreeChart chart)
{
TimeSeriesCollection timeSeriesCollection =
(TimeSeriesCollection)dataset;
List<?> series = timeSeriesCollection.getSeries();
TimeSeries timeSeries = (TimeSeries) series.get(0);
if (timeSeries.getItemCount() == 1)
{
XYPlot plot = (XYPlot) chart.getPlot();
XYLineAndShapeRenderer renderer
= (XYLineAndShapeRenderer) plot.getRenderer();
renderer.setSeriesShapesVisible(0, true);
}
}
private static byte[] imageBytesFromChart(JFreeChart chart, int width, int height) {
BufferedImage objBufferedImage =
chart.createBufferedImage(width, height);
ByteArrayOutputStream bas = new ByteArrayOutputStream();
try {
ImageIO.write(objBufferedImage, "png", bas);
} catch (IOException e) {
e.printStackTrace();
}
byte[] byteArray=bas.toByteArray();
return byteArray;
}

Related

How to create hollow shapes in JFreeChart Scatter Plot

JFreeChart version used: 1.5.0
I tried the following to obtain hollow shapes in a scatter plot:
PlotFrame.java file content:
package javaapplication1;
import javax.swing.JFrame;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
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;
/**
*
* #author tarik
*/
public class PlotFrame extends javax.swing.JFrame {
public static void main(String[] argv) {
PlotFrame plotFrame = new PlotFrame();
plotFrame.setVisible(true);
}
/**
* Creates new form PlotFrame
*/
public PlotFrame() {
this.setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JFreeChart chart = createChart();
ChartPanel panel = new ChartPanel(chart);
this.add(panel);
pack();
}
private JFreeChart createChart() {
// Create dataset
XYDataset dataset = createDataset();
// Create chart
JFreeChart chart = ChartFactory.createScatterPlot(
"Boys VS Girls weight comparison chart",
"X-Axis", "Y-Axis", dataset);
XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) chart.getXYPlot().getRenderer();
//renderer.setBaseShapesFilled(false);
renderer.setUseFillPaint(false);
return chart;
}
private XYDataset createDataset() {
XYSeriesCollection dataset = new XYSeriesCollection();
//Boys (Age,weight) series
XYSeries series1 = new XYSeries("Boys");
series1.add(1, 72.9);
series1.add(2, 81.6);
series1.add(3, 88.9);
series1.add(4, 96);
series1.add(5, 102.1);
series1.add(6, 108.5);
series1.add(7, 113.9);
series1.add(8, 119.3);
series1.add(9, 123.8);
series1.add(10, 124.4);
dataset.addSeries(series1);
//Girls (Age,weight) series
XYSeries series2 = new XYSeries("Girls");
series2.add(1, 72.5);
series2.add(2, 80.1);
series2.add(3, 87.2);
series2.add(4, 94.5);
series2.add(5, 101.4);
series2.add(6, 107.4);
series2.add(7, 112.8);
series2.add(8, 118.2);
series2.add(9, 122.9);
series2.add(10, 123.4);
dataset.addSeries(series2);
return dataset;
}
}
It did not work, the shapes are still filled.
Recall that Graphics2D can draw() the outline of a Shape as well as fill() its interior. By limiting rendering to draw(), #micro's approach is sound in this regard, but it assumes version 1.0.19. When migrating to version 1.5 and later, note that "many methods getBaseXXX()/setBaseXXX() have been renamed setDefaultXXX()/getDefaultXXX()." The correct formulation is illustrated below:
renderer.setDefaultShapesFilled(false);
renderer.setUseFillPaint(false);
Other changes:
Construct and manipulate Swing GUI objects only on the event dispatch thread.
Override getPreferredSize() to set the chart's initial preferred size.
As tested:
import java.awt.Dimension;
import java.awt.EventQueue;
import javax.swing.JFrame;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
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;
/**
* #see https://stackoverflow.com/q/58812592/230513
* #author trashgod
* #author tarik
*/
public class PlotFrame extends javax.swing.JFrame {
public static void main(String[] argv) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
PlotFrame plotFrame = new PlotFrame();
plotFrame.setLocationRelativeTo(null);
plotFrame.setVisible(true);
}
});
}
/**
* Creates new form PlotFrame
*/
public PlotFrame() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JFreeChart chart = createChart();
ChartPanel panel = new ChartPanel(chart){
#Override
public Dimension getPreferredSize() {
return new Dimension(640, 480);
}
};
this.add(panel);
this.pack();
}
private JFreeChart createChart() {
// Create dataset
XYDataset dataset = createDataset();
// Create chart
JFreeChart chart = ChartFactory.createScatterPlot(
"Boys VS Girls weight comparison chart",
"X-Axis", "Y-Axis", dataset);
XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) chart.getXYPlot().getRenderer();
renderer.setDefaultShapesFilled(false);
renderer.setUseFillPaint(false);
return chart;
}
private XYDataset createDataset() {
XYSeriesCollection dataset = new XYSeriesCollection();
//Boys (Age,weight) series
XYSeries series1 = new XYSeries("Boys");
series1.add(1, 72.9);
series1.add(2, 81.6);
series1.add(3, 88.9);
series1.add(4, 96);
series1.add(5, 102.1);
series1.add(6, 108.5);
series1.add(7, 113.9);
series1.add(8, 119.3);
series1.add(9, 123.8);
series1.add(10, 124.4);
dataset.addSeries(series1);
//Girls (Age,weight) series
XYSeries series2 = new XYSeries("Girls");
series2.add(1, 72.5);
series2.add(2, 80.1);
series2.add(3, 87.2);
series2.add(4, 94.5);
series2.add(5, 101.4);
series2.add(6, 107.4);
series2.add(7, 112.8);
series2.add(8, 118.2);
series2.add(9, 122.9);
series2.add(10, 123.4);
dataset.addSeries(series2);
return dataset;
}
}
From source code:
if (getItemShapeFilled(series, item)) {
if (this.useFillPaint) {
g2.setPaint(getItemFillPaint(series, item));
}
else {
g2.setPaint(getItemPaint(series, item));
}
g2.fill(shape);
}
For the first if there is no else, so we need the getItemShapeFilled to return false. It looks like this:
public boolean getItemShapeFilled(int series, int item) {
Boolean flag = getSeriesShapesFilled(series);
if (flag != null) {
return flag;
}
return this.baseShapesFilled;
}
Easiest way is to use this method:
public void setBaseShapesFilled(boolean flag) {
this.baseShapesFilled = flag;
fireChangeEvent();
}
So the actual code is:
import javax.swing.JFrame;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
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;
/**
*
* #author tarik
*/
public class PlotFrame extends javax.swing.JFrame {
public static void main(String[] argv) {
PlotFrame plotFrame = new PlotFrame();
plotFrame.setVisible(true);
}
/**
* Creates new form PlotFrame
*/
public PlotFrame() {
this.setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JFreeChart chart = createChart();
ChartPanel panel = new ChartPanel(chart);
this.add(panel);
pack();
}
private JFreeChart createChart() {
// Create dataset
XYDataset dataset = createDataset();
// Create chart
JFreeChart chart = ChartFactory.createScatterPlot(
"Boys VS Girls weight comparison chart",
"X-Axis", "Y-Axis", dataset);
XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) chart.getXYPlot().getRenderer();
renderer.setBaseShapesFilled(false);
renderer.setUseFillPaint(false);
return chart;
}
private XYDataset createDataset() {
XYSeriesCollection dataset = new XYSeriesCollection();
//Boys (Age,weight) series
XYSeries series1 = new XYSeries("Boys");
series1.add(1, 72.9);
series1.add(2, 81.6);
series1.add(3, 88.9);
series1.add(4, 96);
series1.add(5, 102.1);
series1.add(6, 108.5);
series1.add(7, 113.9);
series1.add(8, 119.3);
series1.add(9, 123.8);
series1.add(10, 124.4);
dataset.addSeries(series1);
//Girls (Age,weight) series
XYSeries series2 = new XYSeries("Girls");
series2.add(1, 72.5);
series2.add(2, 80.1);
series2.add(3, 87.2);
series2.add(4, 94.5);
series2.add(5, 101.4);
series2.add(6, 107.4);
series2.add(7, 112.8);
series2.add(8, 118.2);
series2.add(9, 122.9);
series2.add(10, 123.4);
dataset.addSeries(series2);
return dataset;
}
}
My result is:
Isn't that what you meant?

JFreeChart get getWidth() returns 0.0

I' m using the ChartPanel class from JFreeChart and want to know the width of the plot's data area.
The returned value from the method chartPanel.getChartRenderingInfo().getPlotInfo().getDataArea().getWidth() is 0.0d.
I'm using the JFreeChart version 1.5.0.
My suspicion was a thread-problem, which means that the plotting isn't finished, when I call the .getWidth()-method. So I tried to use ChartChange events, but the result is always a width of 0.0 pixels.
Here is a short example class I created to Show the Problem:
The println in the main() gives "xAxisLengthInPixels = 0.0":
package jFreeChartTest;
import java.awt.BorderLayout;
import javax.swing.JFrame;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.event.ChartChangeEvent;
import org.jfree.chart.event.ChartChangeListener;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
public class Panel_Chart extends JFrame implements ChartChangeListener {
private ChartPanel chartPanel = null;
private int width = 1024;
private int height = 820;
public Panel_Chart() {
this.setTitle("XY Plot");
setBounds(0, 0, width, height);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
XYSeriesCollection xySeriesCollection = new XYSeriesCollection();
XYSeries testXYSeries = new XYSeries("Test-Series", true);
testXYSeries.add(4,10);
testXYSeries.add(5,20);
testXYSeries.add(6,30);
xySeriesCollection.addSeries(testXYSeries);
boolean includeLegend = true;
boolean useTooltips = true;
boolean useUrls = true;
JFreeChart chart = ChartFactory.createScatterPlot(//createXYLineChart(
"",
"X-Axis",
"Y-Axis",
xySeriesCollection,
PlotOrientation.VERTICAL,
includeLegend,
useTooltips,
useUrls);
this.chartPanel = new ChartPanel(chart);
getContentPane().add(chartPanel, BorderLayout.PAGE_START);
}
public Double getXAxisLengthInPixels(){
return chartPanel.getChartRenderingInfo().getPlotInfo().getDataArea().getWidth();
}
#Override
public void chartChanged(ChartChangeEvent arg0) {
// TODO Auto-generated method stub
System.out.println("ChartChangeEvent:" + arg0);
}
public static void main(String[] args){
Panel_Chart panelChart = new Panel_Chart();
panelChart.setVisible(true);
System.out.println("xAxisLengthInPixels = " + panelChart.getXAxisLengthInPixels());
}
}

CodenameOne Charts not rendering well when sharing or simulator screen capture

The recent updates draw the lines on the charts when sharing, but the chart seems to get a bit mangled both when shared and when doing a screen capture on the simulator. The legend gets rotated and moved into the grid area, the title is missing, and there is a yellow outline that runs through the grid area.
Different chart renderings. The far right image got mirrored somehow when I pasted it, it isn't actually a mirror-image when shared.
Here is a test case:
package com.fastlaneinnovations.chartexample;
import com.codename1.charts.ChartComponent;
import com.codename1.charts.models.XYMultipleSeriesDataset;
import com.codename1.charts.models.XYSeries;
import com.codename1.charts.renderers.XYMultipleSeriesRenderer;
import com.codename1.charts.renderers.XYSeriesRenderer;
import com.codename1.charts.util.ColorUtil;
import com.codename1.charts.views.LineChart;
import com.codename1.components.ShareButton;
import com.codename1.io.FileSystemStorage;
import com.codename1.io.Log;
import com.codename1.ui.Container;
import com.codename1.ui.Display;
import com.codename1.ui.Form;
import com.codename1.ui.Image;
import com.codename1.ui.Label;
import com.codename1.ui.events.ActionEvent;
import com.codename1.ui.events.ActionListener;
import com.codename1.ui.layouts.BorderLayout;
import com.codename1.ui.plaf.UIManager;
import com.codename1.ui.util.ImageIO;
import com.codename1.ui.util.Resources;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Hashtable;
import java.util.Set;
public class ChartExample {
private Form current;
private Resources theme;
private XYMultipleSeriesRenderer chartRenderer;
private Container rpmChartContainer;
private XYSeries series;
private LineChart lineChart;
private ChartComponent chart;
private Hashtable<Long, Integer> chartData;
private String chartName = "Chart";
private String path;
private Object os;
private int chartHeight;
private int chartWidth;
private ShareButton share;
private Set<Long> keys;
public void init(Object context) {
theme = UIManager.initFirstTheme("/theme");
// Pro only feature, uncomment if you have a pro subscription
// Log.bindCrashProtection(true);
}
public void start() {
if (current != null) {
current.show();
return;
}
Form form = new Form();
form.setLayout(new BorderLayout());
chartData = new Hashtable<Long, Integer>();
for (int i = 0; i < 8000; i += 100) {
chartData.put(Long.valueOf(i), Integer.valueOf(i));
}
rpmChartContainer = new Container();
rpmChartContainer.setLayout(new BorderLayout());
series = new XYSeries("RPM");
chartRenderer = createChartRenderer();
setChartSettings(chartRenderer, "RPM vs Time", "Time (s)", "RPM", 0.0,
10.0, 0.0, 8000.0, ColorUtil.YELLOW, ColorUtil.WHITE);
lineChart = new LineChart(buildDataSet(series), chartRenderer);
chart = new ChartComponent(lineChart);
chart.setUIID("ChartComponent");
rpmChartContainer.addComponent(BorderLayout.CENTER, chart);
updateChart();
share = new ShareButton();
share.setName(chartName);
share.setText("Share this chart");
share.setTextToShare("RPM chart created by R-P-M Control Center app");
rpmChartContainer.add(BorderLayout.SOUTH, share);
form.add(BorderLayout.CENTER, rpmChartContainer);
form.show();
createShareButton();
}
public void stop() {
current = Display.getInstance().getCurrent();
}
public void destroy() {
}
public void updateChart() {
keys = chartData.keySet();
series.clear();
for (Long key : keys) {
series.add((key / 1000.0), (chartData.get(key) / 1.0));
}
}
private XYMultipleSeriesRenderer createChartRenderer() {
XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer();
renderer.setPointSize(3f);
XYSeriesRenderer seriesRenderer = new XYSeriesRenderer();
seriesRenderer.setColor(ColorUtil.GREEN);
// seriesRenderer.setPointStyle(PointStyle.CIRCLE);
// seriesRenderer.setFillPoints(true);
seriesRenderer.setLineWidth(5f);
seriesRenderer.setShowLegendItem(true);
renderer.addSeriesRenderer(seriesRenderer);
return renderer;
}
private XYMultipleSeriesDataset buildDataSet(XYSeries dataSeries) {
XYMultipleSeriesDataset multiDataSet = new XYMultipleSeriesDataset();
multiDataSet.addSeries(dataSeries);
return multiDataSet;
}
private void setChartSettings(XYMultipleSeriesRenderer renderer,
String title, String xTitle, String yTitle, double xMin,
double xMax, double yMin, double yMax, int axesColor,
int labelsColor) {
renderer.setChartTitle(title);
renderer.setXTitle(xTitle);
renderer.setYTitle(yTitle);
renderer.setXAxisMin(xMin);
// renderer.setXAxisMax(xMax);
renderer.setYAxisMin(yMin);
renderer.setYAxisMax(yMax);
renderer.setAxesColor(axesColor);
renderer.setLabelsColor(labelsColor);
renderer.setMarginsColor(ColorUtil.BLACK);
renderer.setGridColor(ColorUtil.BLACK);
}
public void createShareButton() {
rpmChartContainer.revalidate();
chartWidth = rpmChartContainer.getWidth();
chartHeight = rpmChartContainer.getHeight();
Image chartAsImage = Image.createImage(chartWidth, chartHeight);
rpmChartContainer.paint(chartAsImage.getGraphics());
setShareImage(share, chartAsImage);
}
public void setShareImage(ShareButton shareButton, Image shareImage) {
OutputStream os = null;
final String shareImagePath = FileSystemStorage.getInstance()
.getAppHomePath() + shareButton.getName();
FileSystemStorage.getInstance().delete(shareImagePath);
try {
os = FileSystemStorage.getInstance().openOutputStream(
shareImagePath);
ImageIO.getImageIO().save(shareImage, os, ImageIO.FORMAT_JPEG,
0.95f);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
os.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
shareButton.setImageToShare(shareImagePath, "image/jpeg");
}
}
In implementing the 2D transformations on iOS mutable images, I fixed some bugs related to transforms on mutable images in the simulator. You may not have these until the next plugin update.
As for the third image on the right in your screenshot. What device was that from? Can you share a test case with your chart so I can test it out?

JfreeChart XYAreaChart draw shape at each point [duplicate]

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

JFreeChart Horizontal Charts

I'm using CombinedDomainXYPlot to plot the charts. I have another requirement where, I need to show the two charts horizontally.
Currently i'm having only one chart. what i need is, i need two charts in the first row.
like Chart1 Chart2
Code:
CombinedDomainXYPlot plot = new CombinedDomainXYPlot();
plot.add(getChart1(), 2);
plot.add(getChart2(), 2);
It is giving only one chart in the first row. and second chart2 in the another row.
Is there any way I can make these two charts into single row?
Edit: Actually I wanted it like your ThermometerDemo example. For that you have used JPanel, but here I'm using JFrame.
I wanted it like your ThermometerDemo example.
Based on this example, the code below adds two panels to a GridLayout(1, 0). Each panel includes it's own chart and control panel.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.Random;
import javax.swing.*;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.CombinedDomainXYPlot;
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.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
/**
* #see https://stackoverflow.com/a/20243624/230513
* #see https://stackoverflow.com/q/11870416/230513
*/
public class CombinedPlot {
private static final int MAX = 3;
private static final Random rand = new Random();
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
init();
}
});
}
private static void init() {
JFrame frame = new JFrame("Combined Plot Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridLayout(1, 0));
frame.add(createPanel());
frame.add(createPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static JPanel createPanel() {
JPanel p = new JPanel(new BorderLayout());
XYItemRenderer renderer = new StandardXYItemRenderer();
XYPlot plot1 = new XYPlot(
generateData(), null, new NumberAxis("Range 1"), renderer);
XYPlot plot2 = new XYPlot(
generateData(), null, new NumberAxis("Range 2"), renderer);
final CombinedDomainXYPlot plot
= new CombinedDomainXYPlot(new NumberAxis("Domain"));
plot.add(plot1);
plot.add(plot2);
plot.setOrientation(PlotOrientation.VERTICAL);
JFreeChart chart = new JFreeChart(
"Combined Plots", JFreeChart.DEFAULT_TITLE_FONT, plot, false);
ChartPanel chartPanel = new ChartPanel(chart) {
#Override
public Dimension getPreferredSize() {
return new Dimension(320, 320);
}
};
JPanel controlPanel = new JPanel();
controlPanel.add(new JButton(new UpdateAction(plot, 0)));
controlPanel.add(new JButton(new UpdateAction(plot, 1)));
p.add(chartPanel, BorderLayout.CENTER);
p.add(controlPanel, BorderLayout.SOUTH);
return p;
}
private static class UpdateAction extends AbstractAction {
private final XYPlot plot;
public UpdateAction(CombinedDomainXYPlot plot, int i) {
super("Update plot " + (i + 1));
this.plot = (XYPlot) plot.getSubplots().get(i);
}
#Override
public void actionPerformed(ActionEvent e) {
plot.setDataset(CombinedPlot.generateData());
}
}
private static XYSeriesCollection generateData() {
XYSeriesCollection data = new XYSeriesCollection();
for (int i = 0; i < MAX; i++) {
data.addSeries(generateSeries("Series " + (i + 1)));
}
return data;
}
private static XYSeries generateSeries(String key) {
XYSeries series = new XYSeries(key);
for (int i = 0; i < 16; i++) {
series.add(rand.nextGaussian(), rand.nextGaussian());
}
return series;
}
}

Resources