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);
});
}
}
Related
I have the following chart:
The chart is dynamic and has the capability of make zoom in and zoom out.
I want to put an icon that is created as:
BufferedImage triangleIcon = null;
try {
triangleIcon = ImageIO.read(getClass().getClassLoader().getResource("Resources/Imagenes/triangulo.png"));
} catch (IOException e) {
}
And can be placed in any point as:
xyannotation = new XYImageAnnotation(XValue, YValue, triangleIcon);
this.xyplot.addAnnotation(xyannotation);
I want to put this icon at any Y value but always next to the left side of the chart, without taking into account the zoom. Something like:
Is it possible?
I looks like you want to annotate "one side of the…chart, ignoring the value of the X axis". Ordinarily, such annotations specify both coordinates in data space. One approach is a custom annotation that extends the abstract parent, AbstractXYAnnotation. Override draw() to transform the vertical data coordinate to the corresponding screen coordinate, while pinning the horizontal screen coordinate to the dataArea of the plot. Pan and zoom to see the effect.
AffineTransform at = new AffineTransform();
…
#Override
public void draw(…) {
RectangleEdge rangeEdge = plot.getRangeAxisEdge();
double y = rangeAxis.valueToJava2D(yValue, dataArea, rangeEdge);
at.setToIdentity();
at.translate(dataArea.getX() + 1, y);
…
}
Example code:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.annotations.AbstractXYAnnotation;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.PlotRenderingInfo;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.ui.RectangleEdge;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
/**
* #see https://stackoverflow.com/q/72311226/230513
* #see https://stackoverflow.com/q/71780485/230513
*/
public class XYShapeTest {
private static final int N = 18_000;
private static final int SIZE = 16;
private final Shape pointer = createPolygon(SIZE);
private static class AxisAnnotation extends AbstractXYAnnotation {
private final AffineTransform at = new AffineTransform();
private final Stroke stroke = new BasicStroke(4f);
private final Shape shape;
private final double yValue;
public AxisAnnotation(Shape shape, double y) {
this.shape = shape;
this.yValue = y;
}
#Override
public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea,
ValueAxis domainAxis, ValueAxis rangeAxis,
int rendererIndex, PlotRenderingInfo info) {
RectangleEdge rangeEdge = plot.getRangeAxisEdge();
double y = rangeAxis.valueToJava2D(yValue, dataArea, rangeEdge);
at.setToIdentity();
at.translate(dataArea.getX() + 1, y);
g2.setStroke(stroke);
g2.setPaint(Color.BLACK);
g2.draw(at.createTransformedShape(shape));
}
}
private Shape createPolygon(int size) {
Polygon p = new Polygon();
p.addPoint(0, 0);
p.addPoint(0, 1);
p.addPoint(2, 0);
p.addPoint(0, -1);
AffineTransform at = new AffineTransform();
at.scale(size, size);
return at.createTransformedShape(p);
}
private XYDataset createDataset() {
XYSeries series = new XYSeries("Series");
for (int i = 0; i <= N; i += 1_000) {
series.add(i / 1_000, i);
}
return new XYSeriesCollection(series);
}
private JFreeChart createChart(final XYDataset dataset) {
JFreeChart chart = ChartFactory.createXYLineChart("Test", "X", "Y", dataset);
XYPlot plot = (XYPlot) chart.getPlot();
plot.setDomainPannable(true);
plot.setRangePannable(true);
plot.addAnnotation(new AxisAnnotation(pointer, 9_000));
return chart;
}
private void display() {
JFrame f = new JFrame("XYShapeTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new ChartPanel(createChart(createDataset())) {
#Override
public Dimension getPreferredSize() {
return new Dimension(600, 300);
}
});
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new XYShapeTest()::display);
}
}
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 m using JFreeChart to draw Running or Moving Sine Wave in java with netbeans. I write code for it , but it does not like moving or running sine wave graph. So if you have any idea regarding that then suggest me.
My code is below
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JPanel;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.ApplicationFrame;
public class DrawChart extends ApplicationFrame implements ActionListener {
public XYSeries series;
public DrawChart(final String title) {
super(title);
series = new XYSeries("Sine", true, true);
XYSeriesCollection dataset = new XYSeriesCollection(series);
final JFreeChart chart = createChart(dataset);
final ChartPanel chartPanel = new ChartPanel(chart);
final JButton button = new JButton("Add New Data Item");
button.setActionCommand("ADD_DATA");
button.addActionListener(this);
final JPanel content = new JPanel(new BorderLayout());
content.add(chartPanel);
content.add(button, BorderLayout.SOUTH);
chartPanel.setPreferredSize(new java.awt.Dimension(500, 270));
setContentPane(content);
}
private JFreeChart createChart(final XYDataset dataset) {
JFreeChart jfreechart = ChartFactory.createXYLineChart("Sin Curve", "Angle (Deg)", "Value", dataset, PlotOrientation.VERTICAL, true, true, true);
jfreechart.setBackgroundPaint(Color.white);
XYPlot xyplot = (XYPlot) jfreechart.getPlot();
xyplot.setBackgroundPaint(Color.lightGray);
xyplot.setDomainGridlinePaint(Color.white);
xyplot.setRangeGridlinePaint(Color.white);
return jfreechart;
}
public void actionPerformed(final ActionEvent e) {
if (e.getActionCommand().equals("ADD_DATA")) {
for (int i = 0; i < 100; i++) {
final double x = (i)/10.0 ;
final double y = Math.sin(x);
this.series.addOrUpdate(x, y);
}
}
}
public static void main(final String[] args) {
final DrawChart demo = new DrawChart("Dynamic Data view");
demo.pack();
RefineryUtilities.centerFrameOnScreen(demo);
demo.setVisible(true);
}
}
Starting from this example, substituting Math.sin() for nextGaussian() produced the illustration below. A javax.swing.Timer paces the animation at 1 Hz.
private ChartPanel createPane() {
final XYSeries series = new XYSeries("Data");
for (int i = 0; i < random.nextInt(N) + N / 2; i++) {
series.add(i, Math.sin(i));
}
XYSeriesCollection dataset = new XYSeriesCollection(series);
new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
series.add(series.getItemCount(), Math.sin(series.getItemCount()));
}
}).start();
…
}
Try this JavaFX. It's more flexible.
This is what I gathered and came up with using JavaFX
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javafx.animation.AnimationTimer;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Series;
import javafx.stage.Stage;
public class ACS extends Application
{
private static final int MAX_DATA_POINTS = 200;
private Series series;
private float xSeriesData = 0;
private final ConcurrentLinkedQueue<Number> dataQ = new ConcurrentLinkedQueue<Number>();
private ExecutorService executor;
private AddToQueue addToQueue;
private Timeline timeline2;
private NumberAxis xAxis;
private void init(final Stage primaryStage)
{
xAxis = new NumberAxis(0, MAX_DATA_POINTS, 1);
xAxis.setForceZeroInRange(false);
xAxis.setAutoRanging(false);
final NumberAxis yAxis = new NumberAxis(0, 200, 1);
yAxis.setAutoRanging(false);
// -- Chart
final LineChart<Number, Number> sc = new LineChart<Number, Number>(xAxis, yAxis) {
// Override to remove symbols on each data point
#Override
protected void dataItemAdded(final Series<Number, Number> series, final int itemIndex, final Data<Number, Number> item)
{}
};
sc.setAnimated(false);
sc.setId("liveAreaChart");
sc.setTitle("Animated Area Chart");
// -- Chart Series
series = new LineChart.Series<Number, Number>();
series.setName("Area Chart Series");
sc.getData().add(series);
final Scene scene = new Scene(sc, 800, 800);
scene.getStylesheets().add("site.css");
sc.getStyleClass().add("thick-chart");
primaryStage.setScene(scene);
}
#Override
public void start(final Stage primaryStage) throws Exception
{
init(primaryStage);
primaryStage.show();
// -- Prepare Executor Services
executor = Executors.newCachedThreadPool();
addToQueue = new AddToQueue();
executor.execute(addToQueue);
// -- Prepare Timeline
prepareTimeline();
}
public static void main(final String[] args)
{
launch(args);
}
private class AddToQueue implements Runnable
{
double PERIOD = 20;
double SCALE = 20;
int pos = 0;
#Override
public void run()
{
try {
final double Min = 20;
final double Max = 55;
// add a item of random data to queue
//uncomment the line below to generate a normal graph
// dataQ.add(Min + (Math.random() * ((Max - Min))));
dataQ.add(((Math.sin((++pos * 2 * Math.PI) / PERIOD) * (SCALE / 2)) + (SCALE / 2)));
Thread.sleep(1000);
executor.execute(this);
}
catch (final InterruptedException ex) {
// Logger.getLogger(ACS.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
// -- Timeline gets called in the JavaFX Main thread
private void prepareTimeline()
{
// Every frame to take any data from queue and add to chart
new AnimationTimer() {
#Override
public void handle(final long now)
{
addDataToSeries();
}
}.start();
}
private void addDataToSeries()
{
for (int i = 0; i < 20; i++) { // -- add 20 numbers to the plot+
if (dataQ.isEmpty()) {
break;
}
// series.getData().add(new LineChart.Data(xSeriesData++, dataQ.remove()));
final Number datapoint = dataQ.remove();
xSeriesData = xSeriesData + 1;
System.out.println(xSeriesData + " " + datapoint);
series.getData().add(new LineChart.Data(xSeriesData, datapoint));
}
// remove points to keep us at no more than MAX_DATA_POINTS
if (series.getData().size() > (MAX_DATA_POINTS * 10)) {
series.getData().remove(0, series.getData().size() - (MAX_DATA_POINTS * 10));
}
// update
xAxis.setLowerBound(xSeriesData - (MAX_DATA_POINTS));
xAxis.setUpperBound(xSeriesData - 1);
}
}
I 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();
}
});
}
}
Any suggestions over how to set Range for X-Axis and Y-Axis.
My "X-Axis" Range is from "0.00 to 1.00" with difference of "0.05". I mean 0.00 0.05 0.10 0.15.....0.90 0.95 1.00
My "Y-Axis" Range is from "0.0 to 1.0" with difference of "0.1". I mean 0.0 0.1 0.2 0.3 0.4.........0.9 1.0
I tried doing this Way, but it's not reflecting on the Graph; I don't know how to apply it to ChartFactory.createScatterPlot().
final NumberAxis domainAxis = new NumberAxis("X-Axis");
domainAxis.setRange(0.00,1.00);
domainAxis.setTickUnit(new NumberTickUnit(0.1));
final NumberAxis rangeAxis = new NumberAxis("Y-Axis");
rangeAxis.setRange(0.0,1.0);
rangeAxis.setTickUnit(new NumberTickUnit(0.05));
public JPanel createDemoPanel() {
XYDataset dataset1 = samplexydataset2();
JFreeChart jfreechart = ChartFactory.createScatterPlot("Scatter Plot Demo",
"X", "Y",dataset1, PlotOrientation.VERTICAL, true, true, false);
}
Any help regarding this would be great.
I'm guessing your new NumberAxis instances aren't being used by the plot; it may be easier to use the existing ones from the factory.
import java.awt.BorderLayout;
import java.awt.Color;
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.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
/**
* #see http://stackoverflow.com/questions/7231824
* #see http://stackoverflow.com/questions/7205742
* #see http://stackoverflow.com/questions/7208657
* #see http://stackoverflow.com/questions/7071057
*/
public class ScatterAdd extends JFrame {
private static final int N = 8;
private static final String title = "Scatter Add Demo";
private static final Random rand = new Random();
private XYSeries added = new XYSeries("Added");
public ScatterAdd(String s) {
super(s);
final ChartPanel chartPanel = createDemoPanel();
this.add(chartPanel, BorderLayout.CENTER);
JPanel control = new JPanel();
control.add(new JButton(new AbstractAction("Add") {
#Override
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < N; i++) {
added.add(rand.nextDouble(), rand.nextDouble());
}
}
}));
this.add(control, BorderLayout.SOUTH);
}
private ChartPanel createDemoPanel() {
JFreeChart jfreechart = ChartFactory.createScatterPlot(
title, "X", "Y", createSampleData(),
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);
NumberAxis domain = (NumberAxis) xyPlot.getDomainAxis();
domain.setRange(0.00, 1.00);
domain.setTickUnit(new NumberTickUnit(0.1));
domain.setVerticalTickLabels(true);
NumberAxis range = (NumberAxis) xyPlot.getRangeAxis();
range.setRange(0.0, 1.0);
range.setTickUnit(new NumberTickUnit(0.1));
return new ChartPanel(jfreechart);
}
private XYDataset createSampleData() {
XYSeriesCollection xySeriesCollection = new XYSeriesCollection();
XYSeries series = new XYSeries("Random");
for (int i = 0; i < N * N; i++) {
double x = rand.nextDouble();
double y = rand.nextDouble();
series.add(x, y);
}
xySeriesCollection.addSeries(series);
xySeriesCollection.addSeries(added);
return xySeriesCollection;
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
ScatterAdd demo = new ScatterAdd(title);
demo.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
demo.pack();
demo.setLocationRelativeTo(null);
demo.setVisible(true);
}
});
}
}
Unless the code that you pasted is incomplete, it looks like your problem is that you didn't associate the NumberAxis objects that you created with your plot.
Instead of creating new NumberAxis objects manually, try getting the default ValueAxis objects that are associated with your plot, then modifying their properties:
XYPlot xyPlot = jfreechart.getXYPlot();
ValueAxis domainAxis = xyPlot.getDomainAxis();
ValueAxis rangeAxis = xyPlot.getRangeAxis();
domainAxis.setRange(0.0, 1.0);
domainAxis.setTickUnit(new NumberTickUnit(0.1));
rangeAxis.setRange(0.0, 1.0);
rangeAxis.setTickUnit(new NumberTickUnit(0.05));