JavaFX : Barchart with multiple (2) y axis - javafx

I would like to create a JavaFX barchart with takes 2 y-axis, since the series-values are too wide apart, resulting in second series not showing off properly in the chart.
I have dummed-down the code to its very basics, for the purpose of this question:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
public class MultiAxisTryout extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
stage.setTitle("Bar Chart Multi Example");
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
final BarChart bc =
new BarChart(xAxis,yAxis);
bc.setTitle("Cost & Delta by JV");
xAxis.setLabel("JV");
XYChart.Series series1 = new XYChart.Series();
series1.setName("Cost");
XYChart.Series series2 = new XYChart.Series();
series2.setName("Delta");
series1.getData().add(new XYChart.Data("1000000", 25000));
series1.getData().add(new XYChart.Data("1200000", 30000));
series2.getData().add(new XYChart.Data("1000000", 22));
series2.getData().add(new XYChart.Data("1200000", 86));
Scene scene = new Scene(bc ,1800,1000);
scene.setFill(null);
bc.setAnimated(false);
bc.getData().addAll(series1,series2);
stage.setScene(scene);
stage.show();
}
}
The result looks like this :
As you can see, the second series is not showing off very well in the barchart, so it would be nice to have a second y-axis with its own scale, showing off better in the barchart.
Is this possible in JavaFx ?

Related

JavaFX 8 scale node for printing without changing displayed node

In JavaFX 8, I am printing a node (e.g., ScatterChart) with printerJob.printPage(). Without scaling, the printed node is cropped. If I scale for printing, then the printed node is correctly fit to the page, but the displayed node is scaled. A simple solution would be to make a copy/clone of the node, but it appears that isn't supported. Is there a better solution than scaling the node and then removing the scaling (which causes the displayed node to briefly rescale, which is unsightly)? It would seem that printing a graph would be a basic operation for JavaFX.
You can play with this app. It creates a PNG of the chart. It then prints the chart. I didn't scale the image. The actual image is located in your source folder. You can also open it using Paint and print from there. You can also code the printer settings so that the printer dialog shows up before printing.
import java.awt.Graphics;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import static java.awt.print.Printable.NO_SUCH_PAGE;
import static java.awt.print.Printable.PAGE_EXISTS;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.File;
import java.io.IOException;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
/**
*
* #author blj0011
*/
public class JavaFXApplication145 extends Application
{
final static String itemA = "A";
final static String itemB = "B";
final static String itemC = "F";
#Override
public void start(Stage stage) {
final NumberAxis xAxis = new NumberAxis();
final CategoryAxis yAxis = new CategoryAxis();
final BarChart<Number, String> bc = new BarChart<Number, String>(xAxis, yAxis);
bc.setTitle("Summary");
xAxis.setLabel("Value");
xAxis.setTickLabelRotation(90);
yAxis.setLabel("Item");
XYChart.Series series1 = new XYChart.Series();
series1.setName("2003");
series1.getData().add(new XYChart.Data(2, itemA));
series1.getData().add(new XYChart.Data(20, itemB));
series1.getData().add(new XYChart.Data(10, itemC));
XYChart.Series series2 = new XYChart.Series();
series2.setName("2004");
series2.getData().add(new XYChart.Data(50, itemA));
series2.getData().add(new XYChart.Data(41, itemB));
series2.getData().add(new XYChart.Data(45, itemC));
XYChart.Series series3 = new XYChart.Series();
series3.setName("2005");
series3.getData().add(new XYChart.Data(45, itemA));
series3.getData().add(new XYChart.Data(44, itemB));
series3.getData().add(new XYChart.Data(18, itemC));
Button button = new Button("Print Chart");
button.setOnAction((event)->{printImage(saveAsPng(bc));});//Create the image and print it.
VBox vbox = new VBox();
vbox.getChildren().add(bc);
StackPane stackPane = new StackPane();
stackPane.getChildren().add(button);
vbox.getChildren().add(stackPane);
Scene scene = new Scene(vbox, 800, 600);
bc.getData().addAll(series1, series2, series3);
stage.setScene(scene);
stage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
public File saveAsPng(BarChart barChart) {
WritableImage image = barChart.snapshot(new SnapshotParameters(), null);
// TODO: probably use a file chooser here
File file = new File("chart.png");
try {
ImageIO.write(SwingFXUtils.fromFXImage(image, null), "png", file);
} catch (IOException e) {
// TODO: handle exception here
}
return file;
}
private void printImage(File file) {
Image image = new Image(file.toURI().toString());
java.awt.image.BufferedImage bufferedImage = SwingFXUtils.fromFXImage(image, null);
PrinterJob printJob = PrinterJob.getPrinterJob();
printJob.setPrintable(new Printable() {
#Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
// Get the upper left corner that it printable
int x = (int) Math.ceil(pageFormat.getImageableX());
int y = (int) Math.ceil(pageFormat.getImageableY());
if (pageIndex != 0) {
return NO_SUCH_PAGE;
}
graphics.drawImage(bufferedImage, x, y, bufferedImage.getWidth(), bufferedImage.getHeight(), null);
return PAGE_EXISTS;
}
});
try {
printJob.print();
} catch (PrinterException e1) {
e1.printStackTrace();
}
}
}

javafx charts not working

I am using javafx charts for my project and after running my program I am getting this chart. what I want it to snap 3rd point first and then on to 4th point. Is there any way to do that. Thanks
I'm pretty sure that the 'AreaChart' that you're using plots data monotonically. So no matter what order the points get added to your series, the chart will plot them as monotonic.
note: Monotonic means that for every x value, there is a single y value.
If you want to break the 'monotonicness' of your plot and you're willing to give up the shaded area, you could construct it as a lineChart and then you would need to change your sorting policy. I've attached an example below to show you how to fix it.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Pane root = new Pane();
NumberAxis xAxis = new NumberAxis();
NumberAxis yAxis = new NumberAxis();
LineChart chart = new LineChart(xAxis,yAxis);
XYChart.Series series = new XYChart.Series();
chart.getData().add(series);
chart.setAxisSortingPolicy(LineChart.SortingPolicy.NONE);
series.getData().add(new XYChart.Data(0,1));
series.getData().add(new XYChart.Data(1,2));
series.getData().add(new XYChart.Data(2,1));
series.getData().add(new XYChart.Data(1,0));
root.getChildren().add(chart);
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Unfortunately, if you're looking for both, it's going to come down to having to build your own chart class by extending from XYChart or maybe even AreaChart and defining your own custom AreaChart.

Scatter chart with connected dots javafx

I am learning javafx for creating GUI. I want to implement a simple line chart of X-Y NumberAxis. As per the property of line chart, the lines connecting the data points never intersect.
For example - Suppose for data points
series.getData().add(new XYChart.Data(1.5,1.5));
series.getData().add(new XYChart.Data(2.5,4.5));
series.getData().add(new XYChart.Data(4.5,2.5));
series.getData().add(new XYChart.Data(1.0,2.0));
series.getData().add(new XYChart.Data(3.0,3.0));`
The output for these points in line chart is -
]1)
where as it should be as the following edited image of scatter chart output of same points -
Is there a way to change this behavior of line chart? OR connect the dots of scatter chart?
Note that I am going to add points dynamically and lower bound, upper bound of both the axis are going to vary continuously.
Switch the sorting policy off for the chart.
lineChart.setAxisSortingPolicy(LineChart.SortingPolicy.NONE);
When I use this, I get this beautiful child's scribble.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
public class LineChartSample extends Application {
#Override public void start(Stage stage) {
final NumberAxis xAxis = new NumberAxis();
final NumberAxis yAxis = new NumberAxis();
final LineChart<Number,Number> lineChart =
new LineChart<>(xAxis, yAxis);
XYChart.Series<Number, Number> series = new XYChart.Series<>();
series.getData().addAll(
new XYChart.Data<>(4, 24),
new XYChart.Data<>(1, 23),
new XYChart.Data<>(6, 36),
new XYChart.Data<>(8, 45),
new XYChart.Data<>(2, 14),
new XYChart.Data<>(9, 43),
new XYChart.Data<>(7, 22),
new XYChart.Data<>(12, 25),
new XYChart.Data<>(10, 17),
new XYChart.Data<>(5, 34),
new XYChart.Data<>(3, 15),
new XYChart.Data<>(11, 29)
);
lineChart.setAxisSortingPolicy(LineChart.SortingPolicy.NONE);
Scene scene = new Scene(lineChart,800,600);
lineChart.getData().add(series);
lineChart.setLegendVisible(false);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}

JavaFX - How to position the axis label to avoid conflicting with the tick marks

The below code is a simple line chart where the yAxis tick labels are wide in terms of their character length/width. The yAxis label positioned by default is placed too close to the yAxis, meaning the text conflicts with the tick marks. Is it possible to avoid this by increasing the gap between the label and the axis?
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
public class Example extends Application {
#Override
public void start(Stage stage) {
final NumberAxis xAxis = new NumberAxis(0, 10, 2);
final NumberAxis yAxis = new NumberAxis(1.3378, 1.3390, 0.0001);
xAxis.setLabel("xAxis");
yAxis.setLabel("yAxis");
yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis){
#Override public String toString(Number object){
return String.format("%1.4f", object); }
});
final LineChart<Number, Number> lineChart = new LineChart<Number, Number>(xAxis, yAxis);
XYChart.Series series = new XYChart.Series();
final XYChart.Data d1 = new XYChart.Data(0.0,1.3379);
final XYChart.Data d2 = new XYChart.Data(2.0,1.3387);
final XYChart.Data d3 = new XYChart.Data(2.5,1.3385);
final XYChart.Data d4 = new XYChart.Data(3.5,1.3387);
final XYChart.Data d5 = new XYChart.Data(8.0,1.3378);
final XYChart.Data d6 = new XYChart.Data(9.5,1.3388);
series.getData().addAll(d1, d2, d3, d4, d5, d6);
lineChart.getData().add(series);
lineChart.setLegendVisible(false);
final Scene scene = new Scene(lineChart, 400, 300);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I don't think it is possible to relocate "yAxis" label or increase the gap, but you may want to use the following workaround to avoid the text conficts:
lineChart.getYAxis().setTickLabelRotation(-90);
And you will get the following chart:
It is possible to locate the label of an axis.
For Example to move the Label 10 Pixel to the left, just use:
lineChart.getYAxis()
.lookup(".axis-label")
.setStyle("-fx-label-padding: -10 0 0 0;");

How to create BarChar that contain in each series one element (Bar) in javafx

I created a Bar char but there is a big gap between the bars (I think it's because I use one element in each series). I tried setBarGap() but it doesn't change anything. here is what i do : http://i.imgur.com/k3ZEYCT.png? and here is an example of what i want : http://i.imgur.com/fb7tMx5.png
ObservableList<XYChart.Series<String, Number>> answer = FXCollections.observableArrayList();
for(int i=1;i<9;i++){
....
...
...
XYChart.Series<String, Number> series6 = new XYChart.Series<String, Number>();
series6.getData().add(new XYChart.Data(String.valueOf(i), 2*i));
answer.add(series6);
}
XYChart.Series<String, Number> series1 = new XYChart.Series<String, Number>();
series1.getData().add(new Data<String, Number>(">8",rs2.getInt(1)));
answer.add(series1);
return answer;
The problem is you add more than one category for all data series. If one data series is named "1", and the other is named "2" and these are added to different series then the layout needs to save room in each category's group of bars for the missing category. Like there's a space in "2" group for "1" bars, but you haven't added any.
Make sure you never add more than one category - they all have to have the same value. Since it seems you're using different series for the color and legend, then just don't name the categories.
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.geometry.Side;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
public class BarChartSample extends Application {
#Override public void start(Stage stage) {
stage.setTitle("Bar Chart Sample");
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
final BarChart<String,Number> bc = new BarChart(xAxis,yAxis);
bc.setTitle("Frequency of visit");
bc.setLegendSide(Side.RIGHT);
//if you want axis labels
xAxis.setLabel("");
yAxis.setLabel("");
bc.getData().addAll(makeSerie("daily",20),
makeSerie("twice a week",30),
makeSerie("weekly",40),
makeSerie("monthly",50));
xAxis.setVisible(false);
xAxis.setTickMarkVisible(false);
xAxis.setTickLabelsVisible(false);
bc.setBarGap(20);
Scene scene = new Scene(bc);
stage.setScene(scene);
stage.show();
}
//just to save typing
private XYChart.Series<String, Number> makeSerie(String name, Number value){
return new XYChart.Series(name,
FXCollections.observableArrayList(new XYChart.Data("", value)));
}
public static void main(String[] args) {
launch(args);
}
}

Resources