I would like to create a simple method that plots a line chart given an object that contains the points to be displayed and some other chart attributes (like the chart title) and saves it as an image.
Here is a code example:
public class MyChartApp {
public static void main(String[] args) throws IOException {
ChartData chartData = generateChartData();
WritableImage image = constructChartImage(chartData);
save(image, "chart.png");
}
public static WritableImage constructChartImage(ChartData data) {
// code to be written
}
private static ChartData generateChartData() {
ChartData chartData = new ChartData();
chartData.setValuesX(new double[]{1,2,3,4,5,6,7,8,9});
chartData.setValuesY(new double[]{9,8,7,6,5,4,3,2,1});
chartData.setChartTitle("My Chart");
return chartData;
}
public static void save(WritableImage image, String path) throws IOException {
BufferedImage bImage = SwingFXUtils.fromFXImage(image, null);
ImageIO.write(bImage, "png", new File(path));
}
/**
* JavaFX application used to construct the chart.
*/
public static class MyChart extends Application {
@Override
public void start(Stage stage) throws Exception {
// Somehow retrieve chartData passed to constructChartImage()
ChartData chartData = ...
//defining the axes
final NumberAxis xAxis = new NumberAxis();
final NumberAxis yAxis = new NumberAxis();
//creating the chart
final LineChart<Number,Number> lineChart =
new LineChart<>(xAxis,yAxis);
lineChart.setAnimated(false);
lineChart.setTitle(chartData.getChartTitle());
//defining a series
XYChart.Series series = new XYChart.Series();
//populating the series with data
addAll(series, chartData);
Scene scene = new Scene(lineChart,800,600);
lineChart.getData().add(series);
lineChart.applyCss();
lineChart.layout();
stage.setScene(scene);
WritableImage image = scene.snapshot(null);
// somehow pass the image object to constructChartImage().
}
// utility methods for adding data to the chart //
private static void addAll(XYChart.Series series, ChartData chartData) {
double[] valuesX = chartData.getValuesX();
double[] valuesY = chartData.getValuesY();
for (int i = 0; i < valuesX.length; i++) {
add(series, valuesX[i], valuesY[i]);
}
}
private static void add(XYChart.Series series, double x, double y) {
series.getData().add(new XYChart.Data(x,y));
}
}
public static class ChartData {
private double[] valuesX;
private double[] valuesY;
private String chartTitle;
public String getChartTitle() {
return chartTitle;
}
public void setChartTitle(String chartTitle) {
this.chartTitle = chartTitle;
}
public double[] getValuesX() {
return valuesX;
}
public void setValuesX(double[] valuesX) {
this.valuesX = valuesX;
}
public double[] getValuesY() {
return valuesY;
}
public void setValuesY(double[] valuesY) {
this.valuesY = valuesY;
}
}
}
Although the idea might have seemed simple, the implementation is not. I do not know how to pass the chart data from the main application to the JavaFX application. Generating the data inside the JavaFX thread (for example by using the generateChartData()
method) is not a preferred solution.
There is a similar question here but the suggested solution did not worked for me. It also uses static shared fields which I think is not a good practice. In addition, my case is simpler because I do not want to update the data over time.
Any ideas ?