I'm trying to have two axes on the same data.
The data is a couple of DefaultTableXYDatasets. The plot is a XYPlot, and I have two XYLineAndShapeRenderers and one StackedXYAreaRenderer2.
All data is in meters for the y-values, and I want to have one axis displaying it in meters and one axis displaying it in feet. Now this feels like a common thing to do, but I can't decide on the most obvious way to do it. One way that works would be to duplicate the data and have the y-values in feet, then add another NumberAxis and be done with it.
But I thought it would be wiser to subclass NumberAxis, or inject some functionality into NumberAxis to scale the values. Or should I go with the first approach?
What do you think?
To avoid duplicating data, you can use the XYPlot method mapDatasetToRangeAxes() to map a dataset index to a list of axis indices. In the example below, meters is the principle axis, and the range of the corresponding feet axis is scaled accordingly, as shown here. Note that invokeLater() is required to ensure that the feet axis is scaled after any change in the meters axis.
import java.awt.EventQueue;
import java.util.Arrays;
import java.util.List;
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.axis.ValueAxis;
import org.jfree.chart.event.AxisChangeEvent;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
/**
* #see https://stackoverflow.com/q/13358758/230513
*/
public class AxisTest {
private static final int N = 5;
private static final double FEET_PER_METER = 3.28084;
private static XYDataset createDataset() {
XYSeriesCollection data = new XYSeriesCollection();
final XYSeries series = new XYSeries("Data");
for (int i = -N; i < N * N; i++) {
series.add(i, i);
}
data.addSeries(series);
return data;
}
private JFreeChart createChart(XYDataset dataset) {
NumberAxis meters = new NumberAxis("Meters");
NumberAxis feet = new NumberAxis("Feet");
ValueAxis domain = new NumberAxis();
XYItemRenderer renderer = new XYLineAndShapeRenderer();
XYPlot plot = new XYPlot(dataset, domain, meters, renderer);
plot.setRangeAxis(1, feet);
plot.setRangeAxisLocation(1, AxisLocation.BOTTOM_OR_LEFT);
List<Integer> axes = Arrays.asList(0, 1);
plot.mapDatasetToRangeAxes(0, axes);
scaleRange(feet, meters);
meters.addChangeListener((AxisChangeEvent event) -> {
EventQueue.invokeLater(() -> {
scaleRange(feet, meters);
});
});
JFreeChart chart = new JFreeChart("Axis Test",
JFreeChart.DEFAULT_TITLE_FONT, plot, true);
return chart;
}
private void scaleRange(NumberAxis feet, NumberAxis meters) {
feet.setRange(meters.getLowerBound() * FEET_PER_METER,
meters.getUpperBound() * FEET_PER_METER);
}
private void display() {
JFrame f = new JFrame("AxisTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new ChartPanel(createChart(createDataset())));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
new AxisTest().display();
});
}
}
Alternatively, you can use a JCheckBox to flip between the two series, meters & feet, as shown in this related example. Using the methods available to XYLineAndShapeRenderer, you can hide a second series' lines, shapes and legend. The series itself must be visible to establish the axis range.
Eventually i settled on this solution, it might not be the most elegant but it worked. I have a second axis feetAxis, and added a AxisChangeListener on the first axis called meterAxis. When the meterAxis changes set the range on feetAxis.
I used SwingUtilities.invokeLater, otherwise the range would be incorrect when zooming out of the chart, then the feetAxis would only go from 0 to 1. Didn't check why though.
feetAxis = new NumberAxis("Height [ft]");
metersAxis = new NumberAxis("Height [m]");
pathPlot.setRangeAxis(0, metersAxis);
pathPlot.setRangeAxis(1, feetAxis);
metersAxis.addChangeListener(new AxisChangeListener() {
#Override
public void axisChanged(AxisChangeEvent event) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
feetAxis.setRange(metersAxis.getLowerBound() * MetersToFeet, metersAxis.getUpperBound() * MetersToFeet);
}
});
}
});
Related
I managed to create a Candlestick Chart using JFreeChart-FX and display it using the fxgraphics2d API. But I am pretty much confused on how to enable any interaction with my chart and need some help to this.
I'd be very grateful for any help in the right direction.
I started with this example to get up my initial Chart and altered it so that it uses my data. Then I use a custom Canvas, which utilizes fxgraphics2d to make the JPanel component accessable as a node for my JavaFX applicaiton.
So I know there is for example a specific PanHandlerFX class, but I am lost to utilize this. As far as I was able to research (e.g. here), I need to add the PanHandlerFX class to the list of availableMouseHandlers of my ChartCanvas. But my canvas does not offer anything like availableMouseHandlers. I feel lost now, since there is so few tutorials and information to find regarding the JFree-FX charts and the documentation does not help me either.
Here is my custom canvas class:
import javafx.scene.canvas.Canvas;
import org.jfree.chart.JFreeChart;
import org.jfree.fx.FXGraphics2D;
import java.awt.geom.Rectangle2D;
public class ChartCanvas extends Canvas {
JFreeChart chart;
private FXGraphics2D graphics2D;
public ChartCanvas(JFreeChart chart) {
this.chart = chart;
this.graphics2D = new FXGraphics2D(getGraphicsContext2D());
// Redraw canvas when size changes.
widthProperty().addListener(e -> draw());
heightProperty().addListener(e -> draw());
}
private void draw() {
double width = getWidth();
double height = getHeight();
getGraphicsContext2D().clearRect(0, 0, width, height);
this.chart.draw(this.graphics2D, new Rectangle2D.Double(0, 0, width, height));
//(this.graphics2D,, new Rectangle2D.Double(0, 0, width, height));
}
}
Here is my custom JFreeChart:
import javafx.collections.ObservableList;
import org.ezstrats.model.chartData.Candlestick;
import org.ezstrats.model.chartData.Exchange;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.labels.HighLowItemLabelGenerator;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.CandlestickRenderer;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.data.time.FixedMillisecond;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.time.ohlc.OHLCSeries;
import org.jfree.data.time.ohlc.OHLCSeriesCollection;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
public class JFreeCandlestickChart extends JPanel {
private static final DateFormat READABLE_TIME_FORMAT = new SimpleDateFormat("kk:mm:ss");
private OHLCSeries ohlcSeries;
private TimeSeries volumeSeries;
private JFreeChart candlestickChart;
public JFreeCandlestickChart(String title) {
ObservableList<Candlestick> candlesticks = Exchange.getCandlesticks();
// Create new chart
candlestickChart = createChart(title, candlesticks);
// Create new chart panel
final ChartPanel chartPanel = new ChartPanel(candlestickChart);
chartPanel.setPreferredSize(new Dimension(832, 468));
chartPanel.getChart().getXYPlot().getDomainAxis().setAutoRange(false);
chartPanel.getChart().getXYPlot().getDomainAxis().setLowerBound(candlesticks.get(candlesticks.size() - 300).getTimestampOpen());
chartPanel.getChart().getXYPlot().getDomainAxis().setUpperBound(candlesticks.get(candlesticks.size() - 1).getTimestampOpen());
// Enable zooming - not workign?! ...
chartPanel.setMouseZoomable(true);
chartPanel.setMouseWheelEnabled(true);
chartPanel.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
// process before
super.mouseDragged(e);
chartPanel.getChart().getXYPlot().getDomainAxis().configure();
// process after
}
});
add(chartPanel, BorderLayout.CENTER);
}
public JFreeChart createChart(String title, ObservableList<Candlestick> candlesticks){
/**
* 1st:
* Creating candlestick subplot
*/
// Create OHLCSeriesCollection as a price dataset for candlestick chart
OHLCSeriesCollection candlestickDataset = new OHLCSeriesCollection();
ohlcSeries = new OHLCSeries("Price");
candlestickDataset.addSeries(ohlcSeries);
// Create candlestick chart priceAxis
NumberAxis priceAxis = new NumberAxis("Price");
priceAxis.setAutoRangeIncludesZero(false);
// Create candlestick chart renderer
CandlestickRenderer candlestickRenderer = new CandlestickRenderer(CandlestickRenderer.WIDTHMETHOD_AVERAGE,
false,
new HighLowItemLabelGenerator(new SimpleDateFormat("kk:mm"), new DecimalFormat("0.00000000")));
// Create candlestickSubplot
XYPlot candlestickSubplot = new XYPlot(candlestickDataset, null, priceAxis, candlestickRenderer);
candlestickSubplot.setBackgroundPaint(Color.white);
/**
* 2nd:
* Creating volume subplot
*/
// creates TimeSeriesCollection as a volume dataset for volume chart
TimeSeriesCollection volumeDataset = new TimeSeriesCollection();
volumeSeries = new TimeSeries("Volume");
volumeDataset.addSeries(volumeSeries);
// Create volume chart volumeAxis
NumberAxis volumeAxis = new NumberAxis("Volume");
volumeAxis.setAutoRangeIncludesZero(true);
// Set to no decimal
volumeAxis.setNumberFormatOverride(new DecimalFormat("0"));
// Create volume chart renderer
XYBarRenderer timeRenderer = new XYBarRenderer();
timeRenderer.setShadowVisible(false);
timeRenderer.setDefaultToolTipGenerator(new StandardXYToolTipGenerator("Volume--> Time={1} Size={2}",
new SimpleDateFormat("kk:mm"), new DecimalFormat("0")));
// Create volumeSubplot
XYPlot volumeSubplot = new XYPlot(volumeDataset, null, volumeAxis, timeRenderer);
volumeSubplot.setBackgroundPaint(Color.white);
/**
* 3rd:
* Adding Candles to this chart
**/
for (Candlestick candle: candlesticks){
addCandleToChart(candle.getTimestampOpen(),
candle.getPriceOpen(),
candle.getPriceHigh(),
candle.getPriceLow(),
candle.getPriceClose(),
candle.getVolumeQuote());
}
/**
* 4th:
* Create chart main plot with two subplots (candlestickSubplot,
* volumeSubplot) and one common dateAxis
*/
// Creating charts common dateAxis
DateAxis dateAxis = new DateAxis("Time");
dateAxis.setDateFormatOverride(new SimpleDateFormat("dd.mm.yy kk:mm"));
//dateAxis.setRange();
// reduce the default left/right margin from 0.05 to 0.02
dateAxis.setLowerMargin(0.02);
dateAxis.setUpperMargin(0.02);
dateAxis.setLabelAngle(0);
// Create mainPlot
CombinedDomainXYPlot mainPlot = new CombinedDomainXYPlot(dateAxis);
mainPlot.setGap(10.0);
mainPlot.add(candlestickSubplot, 4);
mainPlot.add(volumeSubplot, 1);
mainPlot.setOrientation(PlotOrientation.VERTICAL);
mainPlot.setDomainPannable(true);
JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, mainPlot, false);
//chart.removeLegend();
// Einbetten in JScrollPaenl??? um Scrollen zu ermöglichen...
// ChartPanel chartPanel = new ChartPanel(chart);
return chart;
}
/**
* Fill series with data.
*
* #param c opentime
* #param o openprice
* #param h highprice
* #param l lowprice
* #param c closeprice
* #param v volume
*/
private void addCandleToChart(long time, double o, double h, double l, double c, double v) {
// Add bar to the data. Let's repeat the same bar
FixedMillisecond t = new FixedMillisecond(time);
//READABLE_TIME_FORMAT.parse(String.valueOf(time)));
ohlcSeries.add(t, o, h, l, c);
volumeSeries.add(t, v);
}
public void setOhlcSeries(OHLCSeries ohlcSeries) {
this.ohlcSeries = ohlcSeries;
}
public void setVolumeSeries(TimeSeries volumeSeries) {
this.volumeSeries = volumeSeries;
}
public OHLCSeries getOhlcSeries() {
return ohlcSeries;
}
public TimeSeries getVolumeSeries() {
return volumeSeries;
}
public JFreeChart getCandlestickChart() {
return candlestickChart;
}
}
And this is how I print the chart (main.class):
// Switching Views
public void drawNewChart(JFreeChart newChart){
centerChart.getChildren().removeAll();
ChartCanvas chartCanvas = new ChartCanvas(newChart);
centerChart.getChildren().add(chartCanvas);
chartCanvas.widthProperty().bind(centerChart.widthProperty());
chartCanvas.heightProperty().bind(centerChart.heightProperty());
}
As shown here, construct a ChartViewer with your JFreeChart to create an interactive chart. The viewer's enclosed ChartCanvas will manage the PanHandlerFX for you. As a concrete example, add the following line to the example, and drag as described here:
plot.setDomainPannable(true);
Original:
After drag right:
As an aside, you may find the JavaFX Demos helpful in this context.
thanks for reading my question.
I'm currently working with JavaFX-8, SceneBuilder and Eclipse.
I want to do a scatter chart with four quadrants, that has two fixed number axis (the data position is not relevant, I only need to display the dots on each quadrant... only matters in which quadrant a dot is). Each quadrant must have a background with a specific color.
I found this question, so I tried to extend ScatterChart with the aim of overriding the method layoutPlotChildren(). I tried a minimum implementation to see if it will run with my FXML (I did import the new component to the FXML). This was my minimum implementation:
public class ScatterQuadrantChart<X,Y> extends ScatterChart<X,Y> {
public ScatterQuadrantChart(Axis<X> xAxis, Axis<Y> yAxis) {
super(xAxis, yAxis);
} }
And then, I get the NotSuchMethodError init error. I found a similar error but from someone extending LineChart here, but I'm not quite sure of what I need to do on my own class.
I tried adding a no-parameters constructor, but I need to call super and cant because I can't call the "getXAxis()" method either. What should I do here?
Plus, the other issue that remains is, once I solve this, what should the layoutPlotChildren() method do?
Thanks for reading.
The problem you are seeing is arising because the default mechanism for the FXMLLoader to instantiate a class is to call the no-argument constructor. Your ScatterQuadrantChart has no no-argument constructor, hence the NoSuchMethodError.
Prior to Java 8, the only way to fix this was to create a builder class for your class, as in the post you linked. JavaFX 8 introduced (but failed to document) a mechanism to specify values for constructor parameters that would be recognized by the FXMLLoader, using the #NamedArg annotation).
So, in Java 8, you can modify your ScatterQuadrantChart:
public class ScatterQuadrantChart<X,Y> extends ScatterChart<X,Y> {
public ScatterQuadrantChart(#NamedArg("xAxis")Axis<X> xAxis,
#NamedArg("yAxis)Axis<Y> yAxis) {
super(xAxis, yAxis);
}
}
and then your FXML will look like
<ScatterQuadrantChart>
<xAxis>
<NumberAxis ... />
</xAxis>
<yAxis>
<NumberAxis ... />
</yAxis>
</ScatterQuadrantChart>
I have no idea if or how SceneBuilder will interact with this, but the FXML will work.
As for the implementation, you will need to add some nodes to the plot to represent your quadrants. I would probably just use plain regions for these. Create them in the constructor and call getPlotChildren().add(...) to add them. Then in the layoutPlotChildren() method, first call the superclass method (which will lay out the scatter chart nodes), and then resize and reposition the quadrants. You can use getXAxis().getDisplayPosition(...) to figure out the location from the actual divider value.
In real life, you should add style classes to the quadrants so you can style them externally with css, etc, but a very basic implementation might look like
import javafx.beans.NamedArg;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.scene.chart.Axis;
import javafx.scene.chart.ScatterChart;
import javafx.scene.layout.Region;
public class ScatterQuadrantChart<X,Y> extends ScatterChart<X,Y> {
private final Property<X> xQuadrantDivider = new SimpleObjectProperty<>();
private final Property<Y> yQuadrantDivider = new SimpleObjectProperty<>();
private final Region nwQuad ;
private final Region neQuad ;
private final Region swQuad ;
private final Region seQuad ;
public ScatterQuadrantChart(#NamedArg("xAxis") Axis<X> xAxis,
#NamedArg("yAxis") Axis<Y> yAxis) {
super(xAxis, yAxis);
nwQuad = new Region();
neQuad = new Region();
swQuad = new Region();
seQuad = new Region();
nwQuad.setStyle("-fx-background-color: lightsalmon ;");
neQuad.setStyle("-fx-background-color: antiquewhite ;");
swQuad.setStyle("-fx-background-color: aqua ;");
seQuad.setStyle("-fx-background-color: lightskyblue ;");
getPlotChildren().addAll(nwQuad, neQuad, swQuad, seQuad);
ChangeListener<Object> quadListener = (obs, oldValue, newValue) -> layoutPlotChildren();
xQuadrantDivider.addListener(quadListener);
yQuadrantDivider.addListener(quadListener);
}
#Override
public void layoutPlotChildren() {
super.layoutPlotChildren();
X x = xQuadrantDivider.getValue();
Y y = yQuadrantDivider.getValue();
if (x != null && y != null) {
Axis<X> xAxis = getXAxis();
Axis<Y> yAxis = getYAxis();
double xPixels = xAxis.getDisplayPosition(x);
double yPixels = yAxis.getDisplayPosition(y);
double totalWidth = xAxis.getWidth();
double totalHeight = yAxis.getHeight();
nwQuad.resizeRelocate(0, 0, xPixels, yPixels);
swQuad.resizeRelocate(0, yPixels, xPixels, totalHeight - yPixels);
neQuad.resizeRelocate(xPixels, 0, totalWidth - xPixels, yPixels);
seQuad.resizeRelocate(xPixels, yPixels, totalWidth - xPixels, totalHeight - yPixels);
}
}
public final Property<X> xQuadrantDividerProperty() {
return this.xQuadrantDivider;
}
public final X getXQuadrantDivider() {
return this.xQuadrantDividerProperty().getValue();
}
public final void setXQuadrantDivider(final X xQuadrantDivider) {
this.xQuadrantDividerProperty().setValue(xQuadrantDivider);
}
public final Property<Y> yQuadrantDividerProperty() {
return this.yQuadrantDivider;
}
public final Y getYQuadrantDivider() {
return this.yQuadrantDividerProperty().getValue();
}
public final void setYQuadrantDivider(final Y yQuadrantDivider) {
this.yQuadrantDividerProperty().setValue(yQuadrantDivider);
}
}
Test code:
import java.util.Random;
import java.util.stream.Stream;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class ScatterQuadrantChartTest extends Application {
#Override
public void start(Stage primaryStage) {
final Random rng = new Random();
ScatterQuadrantChart<Number, Number> chart = new ScatterQuadrantChart<>(new NumberAxis(), new NumberAxis());
Series<Number, Number> series = new Series<>();
for (int i=0; i<20; i++) {
series.getData().add(new Data<>(rng.nextDouble() * 100, rng.nextDouble() * 100));
}
chart.getData().add(series);
chart.setXQuadrantDivider(50);
chart.setYQuadrantDivider(50);
BorderPane root = new BorderPane(chart);
Scene scene = new Scene(root, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I am trying to read in a .csv file but I am stuck on how once I've read each line on the csv file and how to add it to the XYDataset.
My algorithm is as follows: read in .csv file -> read each line -> add to dataset -> create chart -> output as frame
data.csv has 4 columns: Time, X, Y, Z
How do I add each point (time,x), (time,y), (time,z) to the dataset?
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RefineryUtilities;
import com.opencsv.CSVReader;
public class Test extends ApplicationFrame
{
XYDataset dataset;
JFreeChart chart;
final ChartPanel chartPanel;
final int chartWidth = 560;
final int chartHeight = 367;
CSVReader reader;
String[] readNextLine;
XYSeries series;
public Test(String applicationTitle) throws IOException
{
super(applicationTitle);
dataset = createDataset();
chart = createChart(dataset);
chartPanel = new ChartPanel(chart);
chartPanel.setPreferredSize(new java.awt.Dimension(chartHeight, chartWidth));
}
public XYDataset createDataset() throws NumberFormatException, IOException
{
dataset = new XYSeriesCollection();
try
{
reader = new CSVReader(new FileReader("data.csv"));
while((readNextLine = reader.readNext()) != null)
{
if (readNextLine != null)
{
//add values to dataset
}
}
}
catch (FileNotFoundException e)
{
System.out.println("File not found!");
}
return dataset;
}
public JFreeChart createChart(XYDataset dataset) throws NumberFormatException, IOException
{
chart = ChartFactory.createXYLineChart(
"Acceleration vs Time", //chart title
"Time", //domain axis label
"Acceleration", //range axis label
createDataset(), //data
PlotOrientation.VERTICAL, //the plot orientation
true, //legend
true, //tooltips
false); //urls
return chart;
}
public static void main(String[] args) throws IOException
{
final Test demo = new Test("Test XY Line chart");
demo.pack();
RefineryUtilities.centerFrameOnScreen(demo);
demo.setVisible(true);
}
}
The first 10 lines of my dataset look like this:
time x y z
0.003 -0.13 0.83 0.6
0.009 -0.12 0.83 0.61
0.012 -0.12 0.82 0.6
0.018 -0.13 0.81 0.61
0.021 -0.13 0.8 0.61
0.025 -0.12 0.8 0.61
0.033 -0.12 0.79 0.6
0.034 -0.11 0.79 0.6
0.039 -0.11 0.79 0.58
0.044 -0.11 0.77 0.57
Where your comment is - you need to first go through the elements of readNextLine where each element of the String[] will contain the value from your file for a given row (assuming your file is correctly formatted as you said). So - you need to turn these into variables. You haven't specified what format these are in, or what data type Time,X, Y and Z are.
Given that you're looking at an XY chart and your axes are labelled Time and acceleration - I'm going to guess that they're all Floating Point numbers, with Time in decimal seconds and the other variables as accelerations in the various axes. NOTE - if this assumption is wrong - you might need to use a different converter such as Integer.valueOf(), but the principle is the same.
before your while loop, set up the series that you want to add to. You'll add to them row by row within the while loop
final XYSeries seriesX = new XYSeries("X");
final XYSeries seriesY = new XYSeries("Y");
final XYSeries seriesZ = new XYSeries("Z");
within the while loop where your comment is
//add values to dataset
double Time = Double.valueOf(readNextLine[0]);
double X = Double.valueOf(readNextLine[1]);
double Y = Double.valueOf(readNextLine[2]);
double Z = Double.valueOf(readNextLine[3]);
seriesX.add(Time, X);
seriesY.add(Time, Y);
seriesZ.add(Time, Z);
and after the while loop add the 3 series to the collection before you return it:
dataset.addSeries(seriesX);
dataset.addSeries(seriesY);
dataset.addSeries(seriesZ);
Lastly - two comments
Your logic is a little weird around dataset creation. In your constructor, you create the dataset and pass it to createChart(). This is quite standard. But you then call createDataset() inline within the call to ChartFactory.createXYLineChart(, thus rendering the first creation of data pointless and doing it all again. It will work, but is wasteful and might be masking something else that you're intending the code to do.
The if check in createDataset() is redundant - you're already in a while loop based on a condition which means it will always be true.
if (readNextLine != null)
{
EDIT - added full working version to clear confusion in several comments
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RefineryUtilities;
import au.com.bytecode.opencsv.CSVReader;
public class Test extends ApplicationFrame {
/**
*
*/
private static final long serialVersionUID = 1L;
XYSeriesCollection dataset;
JFreeChart chart;
final ChartPanel chartPanel;
final int chartWidth = 560;
final int chartHeight = 367;
CSVReader reader;
String[] readNextLine;
XYSeries series;
public Test(String applicationTitle) throws IOException {
super(applicationTitle);
dataset = createDataset();
chart = createChart(dataset);
chartPanel = new ChartPanel(chart);
chartPanel.setPreferredSize(new java.awt.Dimension(chartHeight,
chartWidth));
this.add(chartPanel);
}
public XYSeriesCollection createDataset() throws NumberFormatException,
IOException {
dataset = new XYSeriesCollection();
try {
reader = new CSVReader(new FileReader("res\\data.csv"),'\t');
// Read the header and chuck it away
readNextLine = reader.readNext();
// Set up series
final XYSeries seriesX = new XYSeries("X");
final XYSeries seriesY = new XYSeries("Y");
final XYSeries seriesZ = new XYSeries("Z");
while ((readNextLine = reader.readNext()) != null) {
// add values to dataset
double Time = Double.valueOf(readNextLine[0]);
double X = Double.valueOf(readNextLine[1]);
double Y = Double.valueOf(readNextLine[2]);
double Z = Double.valueOf(readNextLine[3]);
seriesX.add(Time, X);
seriesY.add(Time, Y);
seriesZ.add(Time, Z);
}
System.out.println(seriesX.getMaxX() + "; " + seriesX.getMaxY());
dataset.addSeries(seriesX);
dataset.addSeries(seriesY);
dataset.addSeries(seriesZ);
} catch (FileNotFoundException e) {
System.out.println("File not found!");
}
return dataset;
}
public JFreeChart createChart(XYDataset dataset)
throws NumberFormatException, IOException {
chart = ChartFactory.createXYLineChart("Acceleration vs Time", // chart
// title
"Time", // domain axis label
"Acceleration", // range axis label
dataset, // data
PlotOrientation.VERTICAL, // the plot orientation
true, // legend
true, // tooltips
false); // urls
return chart;
}
public static void main(String[] args) throws IOException {
System.out.println("In here, to create a Test");
final Test demo = new Test("Test XY Line chart");
System.out.println("Created, pakcking");
demo.pack();
RefineryUtilities.centerFrameOnScreen(demo);
demo.setVisible(true);
}
}
Output with OP supplied first 10 data points
I'm working with jFreeChart on Eclipse (in Windows) and I want to draw gantt diagram for processors.
I'm doing a XY Chart
But my programme draw me a line with only P0. I want something like
draw from 0 to 10 --> P0
then from 10 to 20 --> draw H
then from 20 to 30 ---->draw wait
from 30 to 40---> draw P0
from 35 to 40 -->draw H
Code:
import javax.swing.JFrame;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
public class JFreeChartLineChartExample extends JFrame {
private static final long serialVersionUID = 1L;
public JFreeChartLineChartExample(String applicationTitle, String chartTitle) {
super(applicationTitle);
// based on the dataset we create the chart
JFreeChart pieChart = ChartFactory.createXYLineChart(chartTitle, "Time", "Processors",
createDataset(),PlotOrientation.VERTICAL, true, true, false);
// Adding chart into a chart panel
ChartPanel chartPanel = new ChartPanel(pieChart);
// settind default size
chartPanel.setPreferredSize(new java.awt.Dimension(500, 270));
// add to contentPane
setContentPane(chartPanel);
}
private XYDataset createDataset() {
final XYSeries P0 = new XYSeries("P0");
P0.add(0, 1);
P0.add(10, 1);
P0.add(35, 1);
P0.add(50, 1);
P0.add(85, 1);
P0.add(110, 1);
final XYSeries P2 = new XYSeries("P2");
final XYSeries P1 = new XYSeries("P1");
final XYSeries H = new XYSeries("H");
H.add(10, 1);
H.add(20, 1);
H.add(45, 1);
H.add(100, 1);
final XYSeries wait = new XYSeries("wait");
wait.add(80, 1);
wait.add(90, 1);
wait.add(105, 1);
final XYSeriesCollection dataset = new XYSeriesCollection();
dataset.addSeries(P0);
dataset.addSeries(P2);
dataset.addSeries(P1);
dataset.addSeries(H);
dataset.addSeries(wait);
return dataset;
}
public static void main(String[] args) {
JFreeChartLineChartExample chart = new JFreeChartLineChartExample(" GANTT ", " GANTT");
chart.pack();
chart.setVisible(true);
}
}
As far as I have understood you want to have gaps in your datasets. Your code paints a straight line from time 0 to 110 for dataset P0 because JFreeChart does not know that you want to have gaps.
To add gaps you need to add null values to your datasets.
For example P0 should be created as follows:
final XYSeries P0 = new XYSeries("P0");
P0.add(0, 1);
P0.add(10, 1);
P0.add(11, null);
P0.add(35, 1);
P0.add(50, 1);
P0.add(51, null);
P0.add(85, 1);
P0.add(110, 1);
You might also have a look at the special Gantt classes provided by JFreeChart. There are some examples in the JFreeChart demo.
Hello i have to make a program to display power curves, and therefore i need to display three different plots on one window.
The different kind of plots are XY (just points), bar, and XY with lines.
My problem(s) : somehow i can get only two of the charts to get drawn AND i can't change the colors of the single chart correctly.
EDIT : When i put as comment the declaration of the third chart, the second one finally gets drawn. Is it impossible to draw three charts ?
Any help will be greatly appreciated, thanks ;)
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.DatasetRenderingOrder;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.IntervalXYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.ApplicationFrame;
public class OverlaidPlot extends ApplicationFrame
{
final XYSeries series0 = new XYSeries("Graph0");
final XYSeries series1 = new XYSeries("Graph1");
final XYSeries series2 = new XYSeries("Graph2");
public OverlaidXYPlotDemo(final String title)
{
super(title);
final JFreeChart chart = createOverlaidChart();
final ChartPanel panel = new ChartPanel(chart, true, true, true, true, true);
panel.setPreferredSize(new java.awt.Dimension(800, 600));
setContentPane(panel);
}
public void addElem0(double x, double y)
{
this.series0.add(x, y);
}
public void addElem1(double x, double y)
{
this.series1.add(x, y);
}
public void addElem2(double x, double y)
{
this.series2.add(x, y);
}
private JFreeChart createOverlaidChart()
{
final NumberAxis domainAxis = new NumberAxis("Speed (m/s)");
final ValueAxis rangeAxis = new NumberAxis("Power (kw)");
// create plot ...
final IntervalXYDataset data0 = createDataset0();
final XYItemRenderer renderer0 = new XYBarRenderer(0.20);
// change "new XYBarRenderer(0.20)" to "StandardXYItemRenderer()" if you want to change type of graph
final XYPlot plot = new XYPlot(data0, domainAxis, rangeAxis, renderer0);
// add a second dataset and renderer...
final IntervalXYDataset data1 = createDataset1();
final XYLineAndShapeRenderer renderer1 = new XYLineAndShapeRenderer(false, true);
// arguments of new XYLineAndShapeRenderer are to activate or deactivate the display of points or line. Set first argument to true if you want to draw lines between the points for e.g.
plot.setDataset(1, data1);
plot.setRenderer(1, renderer1);
// add a third dataset and renderer...
final IntervalXYDataset data2 = createDataset2();
final XYLineAndShapeRenderer renderer2 = new XYLineAndShapeRenderer(true, true);
// arguments of new XYLineAndShapeRenderer are to activate or deactivate the display of points or line. Set first argument to true if you want to draw lines between the points for e.g.
plot.setDataset(1, data2);
plot.setRenderer(1, renderer2);
plot.setDatasetRenderingOrder(DatasetRenderingOrder.FORWARD);
NumberAxis domain = (NumberAxis) plot.getDomainAxis();/*
domain.setRange(0.00, 30);*/
domain.setTickUnit(new NumberTickUnit(0.5));
domain.setVerticalTickLabels(true);
// return a new chart containing the overlaid plot...
return new JFreeChart("Test", JFreeChart.DEFAULT_TITLE_FONT, plot, true);
}
private IntervalXYDataset createDataset0()
{
// create dataset 0...
final XYSeriesCollection coll0 = new XYSeriesCollection(series0);
return coll0;
}
private IntervalXYDataset createDataset1()
{
// create dataset 1...
final XYSeriesCollection coll1 = new XYSeriesCollection(series1);
return coll1;
}
private IntervalXYDataset createDataset2()
{
// create dataset 2...
final XYSeriesCollection coll2 = new XYSeriesCollection(series2);
return coll2;
}
}
You have two datasets at the same index within the plot - make sure you're setting each dataset to a unique index:
plot.setDataset(2, data2);
plot.setRenderer(2, renderer2);
After changing this, I ran your example with some test data and was able to see all three data sets plotted.