I have a time history for arrays describing the pressure along a pipe. So I have an array of pressure values along the length of the pipe for each delta t. I want to plot the pressures along the length of the pipe with JFreeChart and chose which delta t to plot with a slider, so that whenever the user moves the slider the graphic is updates with values from a different delta t. I'm also resetting the tile to be the pressure at the last portion of the pipe. What happens is that the title is updates, meaning the data is being properly updates, but the curve remains the same. I have read every possible topic on forums and tried everything I could think of but it's not working! Here's the code of my class that extends JPanel, in which the method jSlider1StateChanged hears the change in the slider position, createChart creaters a new chart when the program is started, and dataSetGen(int ndt) generates the graph's new dataset based on the slider position:
public class MyMainPanel extends JPanel {
private JFreeChart jc;
private OutputPipe op;
private DefaultXYDataset ds;
private javax.swing.JFrame jFrame1;
private javax.swing.JSlider jSlider1;
private pipevisualizer.MyChartPanel pnlChartPanel;
private void jSlider1StateChanged(javax.swing.event.ChangeEvent evt) {
int ndt = ((JSlider) evt.
getSource()).
getValue();
System.out.println("Slider1: " + ((JSlider) evt.
getSource()).
getValue());
dataSetGen(ndt);
int a = 0;
jc.fireChartChanged();
}
private void dataSetGen(int ndt) {
ArrayList<OutputPipeDt> opDtArray = op.getOpLit();
OutputPipeDt opDt = opDtArray.get(ndt);
double[] H = opDt.getH();
double[] l = new double[H.length];
double[] p = new double[H.length];
double dX = op.getPipeLength() / H.length;
double slope = op.getPipeSlope();
double el = op.getPipeUSElev();
for (int i = 0; i < H.length; i++) {
l[i] = dX * i;
p[i] = el - dX * slope * i;
}
double[][] dataH = new double[2][H.length];
dataH[0] = l;
dataH[1] = H;
double[][] dataP = new double[2][H.length];
dataP[0] = l;
dataP[1] = p;
ds = new DefaultXYDataset();
ds.addSeries("pipe head", dataH);
ds.addSeries("pipe profile", dataP);
jc.setTitle("H[end] = " + Double.toString(dataH[1][l.length - 1]));
jc.fireChartChanged();
}
private JFreeChart createChart(OutputPipe op, int ndt) {
ArrayList<OutputPipeDt> opDtArray = op.getOpLit();
OutputPipeDt opDt = opDtArray.get(ndt);
double[] H = opDt.getH();
double[] l = new double[H.length];
double[] p = new double[H.length];
double dX = op.getPipeLength() / H.length;
double slope = op.getPipeSlope();
double el = op.getPipeUSElev();
for (int i = 0; i < H.length; i++) {
l[i] = dX * i;
p[i] = el - dX * slope * i;
}
double[][] dataH = new double[2][H.length];
dataH[0] = l;
dataH[1] = H;
double[][] dataP = new double[2][H.length];
dataP[0] = l;
dataP[1] = p;
DefaultXYDataset ds = new DefaultXYDataset();
ds.addSeries("pipe head", dataH);
ds.addSeries("pipe profile", dataP);
JFreeChart chart = ChartFactory.createXYLineChart(
"t = " + Double.toString(op.getOpLit().get(ndt).getT()),
// chart title
"X",
// x axis label
"Y",
// y axis label
ds,
// data
PlotOrientation.VERTICAL,
true,
// include legend
true,
// tooltips
false // urls
);
return chart;
}
}
I thought that any changes to the datasets would make the graph redraw itself.
Sorry if the code might be to big to be on the post, but I don't know exactly which parts should I paste to be more or less clear.
Absent a complete example, you may be able to use the approach shown here; it uses plot.setDataset() to replace the dataset on receiving each event.
Addendum: This example that shows temperature versus length over time may get you started.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
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;
/**
* #see https://stackoverflow.com/a/15207445/230513
*/
public class ChartSliderTest {
private static final int N = 25;
private static final double K = 273.15;
private static final Random random = new Random();
private static XYDataset getDataset(int n) {
final XYSeries series = new XYSeries("Temp (K°)");
double temperature;
for (int length = 0; length < N; length++) {
temperature = K + n * random.nextGaussian();
series.add(length + 1, temperature);
}
return new XYSeriesCollection(series);
}
private static JFreeChart createChart(final XYDataset dataset) {
JFreeChart chart = ChartFactory.createXYLineChart(
"ChartSliderTest", "Length (m)", "Temp (K°)", dataset,
PlotOrientation.VERTICAL, false, false, false);
return chart;
}
private static void display() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final List<XYDataset> list = new ArrayList<XYDataset>();
for (int i = 0; i <= 10; i++) {
list.add(getDataset(i));
}
JFreeChart chart = createChart(list.get(5));
final XYPlot plot = (XYPlot) chart.getPlot();
plot.getRangeAxis().setRangeAboutValue(K, K / 5);
ChartPanel chartPanel = new ChartPanel(chart) {
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 400);
}
};
f.add(chartPanel);
final JSlider slider = new JSlider(0, 10);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
plot.setDataset(list.get(slider.getValue()));
}
});
Box p = new Box(BoxLayout.X_AXIS);
p.add(new JLabel("Time:"));
p.add(slider);
f.add(p, BorderLayout.SOUTH);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
display();
}
});
}
}
Related
I have used JFreeChart to represent my array of x and y. These arrays get plotted just fine, however theregression line is broken and never gets drawn. All the functions work such as plotting values except drawinputpoint and drawregressionline function. Somehow these two never work. I don't mind drawwinginputpoint a lot, but I like to be able to drawregressionline. My array has correct data, so not sure what is the issue. I am importing my arrays data into dataset in createDateSetFromFile function. My Home_JFrame has targetx and targety array. These have arraylist and has Double data type.
package gradleproject2;
import java.awt.Color;
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
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.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.ui.ApplicationFrame;
import org.jfree.data.function.LineFunction2D;
import org.jfree.data.general.DatasetUtils;
import org.jfree.data.statistics.Regression;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import gradleproject2.Home_JFrame;
import org.jfree.ui.RefineryUtilities;
public class PriceEstimator extends ApplicationFrame{
private static final long serialVersionUID = 1L;
XYDataset inputData;
JFreeChart chart;
public static void main(String[] args) throws IOException {
PriceEstimator demo = new PriceEstimator();
demo.pack();
RefineryUtilities.centerFrameOnScreen(demo);
demo.setVisible(true);
demo.drawRegressionLine();
if (args.length >= 1 && args[0] != null) {
// Estimate the linear function given the input data
double regressionParameters[] = Regression.getOLSRegression(
demo.inputData, 0);
double x = Double.parseDouble(args[0]);
// Prepare a line function using the found parameters
LineFunction2D linefunction2d = new LineFunction2D(
regressionParameters[0], regressionParameters[1]);
// This is the estimated price
double y = linefunction2d.getValue(x);
demo.drawInputPoint(x, y);
}
}
public PriceEstimator() throws IOException {
super("Linear Regression");
// Read sample data from prices.txt file
inputData = createDatasetFromFile();
// Create the chart using the sample data
chart = createChart(inputData);
ChartPanel chartPanel = new ChartPanel(chart);
chartPanel.setPreferredSize(new java.awt.Dimension(500, 270));
setContentPane(chartPanel);
}
public XYDataset createDatasetFromFile() throws IOException {
ClassLoader classLoader = getClass().getClassLoader();
XYSeriesCollection dataset = new XYSeriesCollection();
XYSeries series = new XYSeries("Stock Item");
// Read the price and the date
for (int row = 0; row < Home_JFrame.targetx.size(); row++) {
series.add(Home_JFrame.targetx.get(row), Home_JFrame.targety.get(row));
}
dataset.addSeries(series);
Home_JFrame.targetx.clear();
Home_JFrame.targety.clear();
return dataset;
}
private JFreeChart createChart(XYDataset inputData) throws IOException {
// Create the chart using the data read from the prices.txt file
JFreeChart chart = ChartFactory.createScatterPlot(
"Stock Price", "Stock Date", "Stock Opening Price", inputData,
PlotOrientation.VERTICAL, true, true, false);
XYPlot plot = chart.getXYPlot();
plot.getRenderer().setSeriesPaint(0, Color.blue);
return chart;
}
private void drawRegressionLine() {
// Get the parameters 'a' and 'b' for an equation y = a + b * x,
// fitted to the inputData using ordinary least squares regression.
// a - regressionParameters[0], b - regressionParameters[1]
double regressionParameters[] = Regression.getOLSRegression(inputData,
0);
// Prepare a line function using the found parameters
LineFunction2D linefunction2d = new LineFunction2D(
regressionParameters[0], regressionParameters[1]);
// Creates a dataset by taking sample values from the line function
XYDataset dataset = DatasetUtils.sampleFunction2D(linefunction2d,
0D, 300, 100, "Fitted Regression Line");
// Draw the line dataset
XYPlot xyplot = chart.getXYPlot();
xyplot.setDataset(1, dataset);
XYLineAndShapeRenderer xylineandshaperenderer = new XYLineAndShapeRenderer(
true, false);
xylineandshaperenderer.setSeriesPaint(0, Color.YELLOW);
xyplot.setRenderer(1, xylineandshaperenderer);
}
private void drawInputPoint(double x, double y) {
// Create a new dataset with only one row
XYSeriesCollection dataset = new XYSeriesCollection();
String title = "Stock Date Distance: " + x + ", Stock Opening Price: " + y;
XYSeries series = new XYSeries(title);
series.add(x, y);
dataset.addSeries(series);
XYPlot plot = (XYPlot) chart.getPlot();
plot.setDataset(2, dataset);
XYItemRenderer renderer = new XYLineAndShapeRenderer(false, true);
plot.setRenderer(2, renderer);
}
}
It looks like you want a trend line though a scatter plot, but you may be creating unnecessary renderers in addition to the one instantiated by your chosen ChartFactory. To study the problem in isolation, modify this complete example to create a scatter plot and change the existing renderer to condition the trend line's display as desired.
JFreeChart chart = ChartFactory.createScatterPlot(…);
XYPlot plot = chart.getXYPlot();
XYLineAndShapeRenderer r = (XYLineAndShapeRenderer) plot.getRenderer();
r.setSeriesLinesVisible(1, Boolean.TRUE);
r.setSeriesShapesVisible(1, Boolean.FALSE);
Code:
import java.awt.Dimension;
import java.awt.EventQueue;
import java.util.Random;
import javax.swing.JFrame;
import org.jfree.chart.*;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.statistics.Regression;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
/**
* #see https://stackoverflow.com/a/37716411/230513
* #see http://stackoverflow.com/a/37716411/230513
*/
public class RegressionTest {
private static final int N = 16;
private static final Random R = new Random();
private static XYDataset createDataset() {
XYSeries series = new XYSeries("Data");
for (int i = 0; i < N; i++) {
series.add(i, R.nextGaussian() + i);
}
XYSeriesCollection xyData = new XYSeriesCollection(series);
double[] coefficients = Regression.getOLSRegression(xyData, 0);
double b = coefficients[0]; // intercept
double m = coefficients[1]; // slope
XYSeries trend = new XYSeries("Trend");
double x = series.getDataItem(0).getXValue();
trend.add(x, m * x + b);
x = series.getDataItem(series.getItemCount() - 1).getXValue();
trend.add(x, m * x + b);
xyData.addSeries(trend);
return xyData;
}
private static JFreeChart createChart(final XYDataset dataset) {
JFreeChart chart = ChartFactory.createScatterPlot("Test", "X", "Y",
dataset, PlotOrientation.VERTICAL, true, false, false);
XYPlot plot = chart.getXYPlot();
XYLineAndShapeRenderer r = (XYLineAndShapeRenderer) plot.getRenderer();
r.setSeriesLinesVisible(1, Boolean.TRUE);
r.setSeriesShapesVisible(1, Boolean.FALSE);
return chart;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
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(640, 480);
}
};
f.add(chartPanel);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
});
}
}
I have a 3D dataset XYZDataset that I want to plot as a 2D plot, by keeping (x,y) coordinates and by representing the z axis using a spectrum of colors.
Based on this example, here is my ploting class along with the spectrum color class.
package com.ingilab.algo.comparator.tools.plot;
import java.awt.Color;
import java.awt.Paint;
import java.awt.Shape;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.PaintScale;
import org.jfree.chart.renderer.xy.XYBlockRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.title.PaintScaleLegend;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.data.xy.XYZDataset;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.RectangleInsets;
import org.jfree.util.ShapeUtilities;
public class Plot2D extends ApplicationFrame {
private static final int N = 100;
/**
* A demonstration application showing an XY series containing a null value.
*
* #param title the frame title.
*/
final XYSeries series;
public Plot2D(final String title, String X, String Y, XYSeries series) {
super(title);
this.series = series;
final XYSeriesCollection data = new XYSeriesCollection(series);
final JFreeChart chart = ChartFactory.createScatterPlot(
title,
X,
Y,
data,
PlotOrientation.VERTICAL,
true,
true,
false
);
final ChartPanel chartPanel = new ChartPanel(chart);
chartPanel.setPreferredSize(new java.awt.Dimension(500, 270));
setContentPane(chartPanel);
}
public Plot2D (final String title, JFreeChart chart) {
super(title);
series = null;
final ChartPanel chartPanel = new ChartPanel(chart);
chartPanel.setPreferredSize(new java.awt.Dimension(500, 270));
setContentPane(chartPanel);
}
/**
* Creates a sample chart.
*
* #param dataset the dataset.
* #param max
*
* #return A sample chart.
*/
public static JFreeChart createChart(XYZDataset dataset,
String title, String x, String y, String z, double max) {
NumberAxis xAxis = new NumberAxis(x);
xAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
xAxis.setLowerMargin(0.0);
xAxis.setUpperMargin(0.0);
NumberAxis yAxis = new NumberAxis(y);
yAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
yAxis.setLowerMargin(0.0);
yAxis.setUpperMargin(0.0);
XYBlockRenderer renderer = new XYBlockRenderer();
SpectrumPaintScale scale = new SpectrumPaintScale(0, max);
//PaintScale scale = new GrayPaintScale(-2.0, 1.0);
renderer.setPaintScale(scale);
//Z axis
NumberAxis scaleAxis = new NumberAxis(z);
scaleAxis.setAxisLinePaint(Color.white);
scaleAxis.setTickMarkPaint(Color.white);
PaintScaleLegend legend = new PaintScaleLegend(scale, scaleAxis);
legend.setSubdivisionCount(128);
legend.setAxisLocation(AxisLocation.TOP_OR_RIGHT);
legend.setPadding(new RectangleInsets(10, 10, 10, 10));
legend.setStripWidth(20);
legend.setPosition(RectangleEdge.RIGHT);
legend.setBackgroundPaint(Color.WHITE);
XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer);
plot.setBackgroundPaint(Color.lightGray);
plot.setDomainGridlinesVisible(false);
plot.setRangeGridlinePaint(Color.white);
plot.setRenderer(new XYLineAndShapeRenderer(false, true) {
#Override
public Shape getItemShape(int row, int col) {
return ShapeUtilities.createDiagonalCross(5, 2);
}
});
JFreeChart chart = new JFreeChart(title, plot);
chart.addSubtitle(legend);
chart.removeLegend();
chart.setBackgroundPaint(Color.white);
return chart;
}
////////////////////////////////////
// //
// PaintScaleColor //
// //
////////////////////////////////////
private static class SpectrumPaintScale implements PaintScale {
private static final float H1 = 0f;
private static final float H2 = 1f;
private final double lowerBound;
private final double upperBound;
public SpectrumPaintScale(double lowerBound, double upperBound) {
this.lowerBound = lowerBound;
this.upperBound = upperBound;
}
#Override
public double getLowerBound() {
return lowerBound;
}
#Override
public double getUpperBound() {
return upperBound;
}
#Override
public Paint getPaint(double value) {
float scaledValue = (float) (value / (getUpperBound() - getLowerBound()));
float scaledH = H1 + scaledValue * (H2 - H1);
return Color.getHSBColor(scaledH, 1f, 1f);
}
}
public static void main(String[] args)
{
final DefaultXYZDataset timePerSizePerChrno = new
DefaultXYZDataset();
ydb [0][1] = 1;
ydb [1][1] = 78.0;
ydb [2][1] = 1341.0;
ydb [0][2] = 2;
ydb [1][2] = 100.0;
ydb [2][2] = 475.0;
ydb [0][1] = 3;
ydb [1][1] = 9215.0;
ydb [2][1] = 684.0;
ydb [0][1] = 4;
ydb [1][1] = 90.0;
ydb [2][1] = 251.0;
ydb [0][1] = 5;
ydb [1][1] = 75.0;
ydb [2][1] = 7022.0;
double maxZ = 7022;
timePerSizePerChrno.addSeries("Series", ydb);
//////////////////////////////////////////
// PLOTING RESUlTS //
//////////////////////////////////////////
final Plot2D plot3 = new Plot2D("Loading Performance Color Map", Plot2D.createChart (timePerSizePerChrno,
"Loading Performance Color Map", "Order Call", "Time in Ms", "Size in Ko", maxZ));
plot3.pack();
RefineryUtilities.centerFrameOnScreen(plot3);
plot3.setVisible(true);
}
}
The problem I am having is that the spectrum of colors is applied on the XYZDataset series and not on the z values (I have one unique serie in my dataset).
For instance on the above image. You can see that all points are in red and I want them to be mapped to the spectrum of color on the right based on their values. I also want to remove the red at the end of the spectrum since it can be confusing (the spectrum starts and finishes with the red color).
Any guess on how for a given series, plot the different point (x,y) using a spectrum of color knowing that z values are between [0, maxZ].
Your updated example creates an XYBlockRenderer, as shown here, and applies a custom PaintScale to the renderer; the scale is also used to create a matching PaintScaleLegend. After using the XYBlockRenderer to create an XYPlot, the original XYBlockRenderer is discarded and replaced with an XYLineAndShapeRenderer, which overrides getItemShape(). The new XYLineAndShapeRenderer knows nothing about the PaintScale.
Instead, override getItemFillPaint() in your XYLineAndShapeRenderer, as shown here. Instead of the List<Color> shown, use the getPaint() method of your custom PaintScale to interpolate the desired Shape color for each data point based on its corresponding z value.
XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(false, true) {
#Override
public Paint getItemFillPaint(int row, int col) {
return scale.getPaint(dataset.getZValue(row, col));
}
…
};
In addition,
To get a different spectrum, specify the desired boundary hues in the PaintScale.
private static final float H1 = 0f;
private static final float H2 = (float) (Math.PI / 8);
Use DatasetUtils.findZBounds() to determine the dataset range.
Range r = DatasetUtils.findZBounds(dataset);
Construct and manipulate Swing GUI objects only on the event dispatch thread.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Paint;
import java.awt.Shape;
import javax.swing.JFrame;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.PaintScale;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.title.PaintScaleLegend;
import org.jfree.data.Range;
import org.jfree.data.xy.DefaultXYZDataset;
import org.jfree.data.xy.XYZDataset;
import org.jfree.chart.ui.RectangleEdge;
import org.jfree.chart.ui.RectangleInsets;
import org.jfree.chart.util.ShapeUtils;
import org.jfree.data.general.DatasetUtils;
/**
* #see https://stackoverflow.com/a/54180207/230513
* #see https://stackoverflow.com/a/37235165/230513
*/
public class Plot2D {
public static JFreeChart createChart(XYZDataset dataset,
String title, String x, String y, String z, Range r) {
NumberAxis xAxis = new NumberAxis(x);
xAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
NumberAxis yAxis = new NumberAxis(y);
yAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
SpectrumPaintScale scale = new SpectrumPaintScale(r);
NumberAxis scaleAxis = new NumberAxis(z);
scaleAxis.setAxisLinePaint(Color.white);
scaleAxis.setTickMarkPaint(Color.white);
PaintScaleLegend legend = new PaintScaleLegend(scale, scaleAxis);
legend.setSubdivisionCount(128);
legend.setAxisLocation(AxisLocation.TOP_OR_RIGHT);
legend.setPadding(new RectangleInsets(10, 10, 10, 10));
legend.setStripWidth(20);
legend.setPosition(RectangleEdge.RIGHT);
legend.setBackgroundPaint(Color.WHITE);
XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(false, true) {
#Override
public Paint getItemFillPaint(int row, int col) {
return scale.getPaint(dataset.getZValue(row, col));
}
#Override
public Shape getItemShape(int row, int col) {
return ShapeUtils.createDiagonalCross(5, 2);
}
};
renderer.setUseFillPaint(true);
renderer.setSeriesShapesFilled(0, true);
renderer.setSeriesShapesVisible(0, true);
XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer);
plot.setBackgroundPaint(Color.lightGray);
plot.setDomainGridlinesVisible(false);
plot.setRangeGridlinePaint(Color.white);
plot.setRenderer((renderer));
JFreeChart chart = new JFreeChart(title, plot);
chart.addSubtitle(legend);
chart.removeLegend();
chart.setBackgroundPaint(Color.white);
return chart;
}
private static class SpectrumPaintScale implements PaintScale {
private static final float H1 = 0f;
private static final float H2 = (float) (Math.PI / 8);
private final Range range;
public SpectrumPaintScale(Range r) {
this.range = r;
}
#Override
public double getLowerBound() {
return range.getLowerBound();
}
#Override
public double getUpperBound() {
return range.getUpperBound();
}
#Override
public Paint getPaint(double value) {
float scaledValue = (float) (value / (getUpperBound() - getLowerBound()));
float scaledH = H1 + scaledValue * (H2 - H1);
return Color.getHSBColor(scaledH, 1f, 1f);
}
}
public static void main(String[] args) {
double xyz[][] = {
{ 1, 2, 3, 4, 5 }, // x
{ 1000, 3000, 9215, 4000, 1000 }, // y
{ 1341, 500, 3125, 1000, 7022 } // z
};
final DefaultXYZDataset dataset = new DefaultXYZDataset();
dataset.addSeries("Series", xyz);
Range r = DatasetUtils.findZBounds(dataset);
EventQueue.invokeLater(() -> {
JFrame f = new JFrame("Color Map");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JFreeChart chart = Plot2D.createChart(dataset, "Color Map",
"Order Call", "Time in Ms", "Size in Ko", r);
f.add(new ChartPanel(chart) {
#Override
public Dimension getPreferredSize() {
return new Dimension(600, 300);
}
});
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
}
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);
This program is supposed to graph and animate a sine wave by using a thread to "move over" the points in the XYSeries that draws the wave. After a few seconds, the chart starts to flash and zooms in and out randomly. I don't know if this is a problem with my code or with my computer (probably the code).
import java.awt.geom.Point2D;
import static java.lang.Math.PI;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
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.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
public class WaveGraph extends JFrame
{
double numOfWaves = 2;
double waveAmp = 2;
double waveLength = 10;
double waveRes = 20;
double median = ((waveLength*numOfWaves)/2);
int numOfPoints = (int)(numOfWaves*waveRes);
boolean going = true;
boolean left = true;
XYSeriesCollection dataset = new XYSeriesCollection();
Point2D.Double[] points;
WaveGraph()
{
XYSeries buoys = new XYSeries("Buoys");
XYSeries series = new XYSeries("Wave");
points = new Point2D.Double[numOfPoints];
for(int i=0; i<numOfPoints; i++)
{
Point2D.Double temp = new Point2D.Double();
temp.x = i*(waveLength/waveRes);
temp.y = waveAmp*Math.sin((2*PI)/waveLength*temp.x);
points[i] = temp;
series.add(points[i].x, points[i].y);
if(i==(numOfPoints/2)-1)
buoys.add(median, points[i].y);
}
buoys.add(median, 0);
dataset.addSeries(series);
dataset.addSeries(buoys);
JFreeChart chart = ChartFactory.createXYLineChart(
"Sine Wave", // chart title
"Wavelength(meters)", // x axis label
"Amplitude (meters)", dataset, // data
PlotOrientation.VERTICAL,
false, // include legend
false, // tooltips
false // urls
);
XYPlot plot = (XYPlot)chart.getPlot();
XYLineAndShapeRenderer renderer
= (XYLineAndShapeRenderer) plot.getRenderer();
renderer.setSeriesShapesFilled(1, true);
renderer.setSeriesShapesVisible(1, true);
NumberAxis domain = (NumberAxis)plot.getDomainAxis();
domain.setRange(0.00, waveLength*numOfWaves);
NumberAxis range = (NumberAxis)plot.getRangeAxis();
range.setRange(-waveAmp*1.3, waveAmp*1.3);
ChartPanel cp = new ChartPanel(chart);
cp.setPreferredSize(new java.awt.Dimension(500, 270));
setContentPane(cp);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
WaveRunner run = new WaveRunner();
}
class WaveRunner extends Thread
{
WaveRunner()
{
this.start();
}
public void run()
{
double[] temp = new double[numOfPoints];
XYSeries temp2, temp3;
while(going)
{
temp2 = dataset.getSeries(0);
temp3 = dataset.getSeries(1);
temp2.delete(0, numOfPoints-1);
temp3.delete(0, 1);
for(int i=0; i<numOfPoints; i++)
{
if(left){
try{
temp[i] = points[i+1].y;
}
catch(java.lang.ArrayIndexOutOfBoundsException exc){
temp[numOfPoints-1] = points[0].y;
}}
else{
try{
temp[i] = points[i-1].y;
}
catch(java.lang.ArrayIndexOutOfBoundsException exc){
temp[0] = points[numOfPoints-1].y;
}}
}
for(int i=0; i<numOfPoints; i++)
{
points[i].y = temp[i];
temp2.add(points[i].x, points[i].y);
if(i==(numOfPoints/2))
{
temp3.add(median, points[i].y);
temp3.add(median, 0);
}
}
try
{
Thread.sleep(50);
}
catch(InterruptedException exc)
{}
}
}
}
public static void main(String[] args)
{
WaveGraph test = new WaveGraph();
}
}
Your example is incorrectly synchronized in that it updates the chart's dataset from WaveRunner. The chart's model is displayed in a ChartPanel, so it should be updated only on the event dispatch thread. Instead, pace the animation using Swing Timer, as shown here and here, or SwingWorker, as shown here.
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);
}
}