I have a problem to get the pixel coordinate with the method "getDisplayPosition", each time it returns 0.0.
Here is my code :
public void start(Stage primaryStage) throws Exception
{
final NumberAxis xAxis = new NumberAxis(0.0, 200.0, 5.0);
final NumberAxis yAxis = new NumberAxis(0.0, 20000.0, 1000.0);
xAxis.setLabel("Disante (Nm)");
yAxis.setLabel("Altitude (ft)");
AreaChart<Number, Number> areaChart = new AreaChart<>(xAxis, yAxis);
Series<Number, Number> area = new XYChart.Series<>();
area.getData().add(new XYChart.Data<>(60, 4000));
area.getData().add(new XYChart.Data<>(65, 4500));
area.getData().add(new XYChart.Data<>(70, 3800));
area.getData().add(new XYChart.Data<>(75, 1000));
area.getData().add(new XYChart.Data<>(80, 1500));
areaChart.getData().add(area);
areaChart.setLegendVisible(false);
areaChart.setCreateSymbols(false);
LineChart<Number, Number> lineChart = new LineChart<>(xAxis, yAxis);
Series<Number, Number> line = new XYChart.Series<>();
line.getData().add(new XYChart.Data<>(0, 0));
line.getData().add(new XYChart.Data<>(15, 6000));
line.getData().add(new XYChart.Data<>(145, 6000));
line.getData().add(new XYChart.Data<>(155, 9000));
line.getData().add(new XYChart.Data<>(200, 9000));
lineChart.getData().add(line);
lineChart.setLegendVisible(false);
lineChart.setCreateSymbols(false);
lineChart.setAlternativeColumnFillVisible(false);
lineChart.setAlternativeRowFillVisible(false);
lineChart.setHorizontalGridLinesVisible(false);
lineChart.setVerticalGridLinesVisible(false);
lineChart.getXAxis().setVisible(false);
lineChart.getYAxis().setVisible(false);
lineChart.getStylesheets().add(this.getClass().getResource("/resources/chart/chart.css").toExternalForm());
polygon = new Polygon();
polygon.getPoints().addAll(new Double[] {
100.0, 3000.0,
100.0, 19000.0,
150.0, 19000.0,
150.0, 3000.0,
140.0, 5000.0,
130.0, 4500.0,
120.0, 5500.0,
110.0, 3500.0
});
// polygon.setFill(new ImagePattern(createHatch(Color.BLUE)));
Polygon polygonInChart = updateShape(xAxis, yAxis);
StackPane root = new StackPane();
root.getChildren().addAll(areaChart, polygonInChart, lineChart);
Scene scene = new Scene(root, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private Polygon updateShape(NumberAxis xAxis, NumberAxis yAxis)
{
List<Double> points = polygon.getPoints();
Polygon polygonInChart = new Polygon();
for (int i = 0; i < points.size(); i += 2)
{
double displayX = xAxis.getDisplayPosition(points.get(i).doubleValue());
double displayY = yAxis.getDisplayPosition(points.get(i + 1).doubleValue());
polygonInChart.getPoints().addAll(displayX, displayY);
}
return polygonInChart;
}
I want to add a polygon in the chart, so I try to find the pixel position in the screen of the polygon's coordinate and add them to the stackPane in order to have the polygon "in" the chart.
Related
I am using LineChart in JavaFX. I am using Arrays of same length as inputs to plot.
But when i include this in my application the plot is not autosize.
I have included the snapshot how it looks.
I would like to set the line width smaller and also to change the color.
Here is the code to plot this graph
public class ChartPlot extends Application {
static LineChart<Number, Number> linechart;
static double[] xArray, yArray;
public static LineChart linePlot(double[] x, double[] y) {
xArray = new double[x.length];
yArray = new double[y.length];
xArray = x;
yArray = y;
// Defining the x axis
final NumberAxis xAxis = new NumberAxis();
xAxis.setLabel("Wavelength");
// Defining the y axis
final NumberAxis yAxis = new NumberAxis();
yAxis.setLabel("Intensity");
// Creating the line chart
linechart = new LineChart<Number, Number>(xAxis, yAxis);
// Prepare XYChart.Series objects by setting data
XYChart.Series series = new XYChart.Series();
// series.setName("No of schools in an year");
// Setting the data to Line chart
for (int i = 0; i < xArray.length; i++) {
series.getData().add(new XYChart.Data(xArray[i], yArray[i]));
}
linechart.setCreateSymbols(false);
linechart.getData().add(series);
return linechart;
}
}
Please help me to resolve this .
Thanks in advance
NumerAxis need to be added with autoRange. Here is the code
public static LineChart linePlot(double[] x,double[] y)
{
xArray=new double[x.length];
yArray=new double[y.length];
xArray=x;
yArray=y;
//Defining the x axis
final NumberAxis xAxis = new NumberAxis();
xAxis.setLabel("Wavelength");
//Defining the y axis
final NumberAxis yAxis = new NumberAxis();
yAxis.setLabel("Intensity");
//Creating the line chart
linechart= new LineChart<Number,Number>(xAxis,yAxis);
linechart.getData().clear();
//Prepare XYChart.Series objects by setting data
XYChart.Series series = new XYChart.Series();
//series.setName("No of schools in an year");
//Setting the data to Line chart
for(int i = 0; i<xArray.length; i++)
{
series.getData().add(new XYChart.Data(xArray[i], yArray[i]));
}
linechart.setCreateSymbols(false);
linechart.getData().add(series);
//This is what I have Changed
//---
xAxis.setAutoRanging(true);
xAxis.setForceZeroInRange(false);
yAxis.setAutoRanging(true);
yAxis.setForceZeroInRange(false);
//---
linechart.autosize();
linechart.applyCss();
return linechart;
}
}
I'm trying to create vertical and horizontal lines on ScatterChart together with points. I cannot find a way to mix them up.
This is the code I generate points on chart.
vbox {
add(ScatterChart(NumberAxis(), NumberAxis()).apply {
val seriesMap: HashMap<String, XYChart.Series<Number, Number>> = HashMap()
pointsList
.map { it.decisionClass }
.distinct()
.forEach {
seriesMap.put(it, XYChart.Series())
}
for (point in pointsList) {
seriesMap.get(point.decisionClass)?.data(point.axisesValues[0], point.axisesValues[1])
}
seriesMap
.toSortedMap()
.forEach { key, value ->
value.name = key
data.add(value)
}
(xAxis as NumberAxis).setForceZeroInRange(false)
(yAxis as NumberAxis).setForceZeroInRange(false)
})
}
I don't know Kotlin, so this answer is in Java. I think you can probably translate it to Kotlin (and feel free to post another answer if so).
To add additional nodes to a chart, there are three things you need:
Call getPlotChildren() and add the new nodes to the chart's "plot children"
Override the layoutPlotChildren() method to update the positions of your nodes when the chart is laid out
Use getDisplayPosition(...), defined in Axis, to get the location in the coordinate system of the plot area of the chart from a value on the axis.
The following SSCCE creates a scatter chart somewhat similar to the one you posted in the screen shot, and adds lines gating a specified series (i.e. the line on the left extends the height of the chart and passes through the minimum x-value of the series; the line at the top extends the width of the chart, and passes through the maximum y-value of the series, etc). I added radio buttons so you can choose which series is "bounded" by the lines.
import java.util.Random;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.ScatterChart;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.control.RadioButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
public class ScatterChartWithLines extends Application {
private final class ScatterChartWithBoundary extends ScatterChart<Number, Number> {
private Series<Number, Number> boundedSeries ;
private final NumberAxis yAxis;
private final NumberAxis xAxis;
private final Line leftLine = new Line();
private final Line rightLine = new Line();
private final Line topLine = new Line();
private final Line bottomLine = new Line();
{
getPlotChildren().addAll(leftLine, rightLine, topLine, bottomLine);
}
private ScatterChartWithBoundary(NumberAxis xAxis, NumberAxis yAxis) {
super(xAxis, yAxis);
this.yAxis = yAxis;
this.xAxis = xAxis;
}
#Override
protected void layoutPlotChildren() {
super.layoutPlotChildren();
getPlotChildren().removeAll(leftLine, rightLine, topLine, bottomLine);
if (boundedSeries != null) {
getPlotChildren().addAll(leftLine, rightLine, topLine, bottomLine);
double minX = Double.MAX_VALUE ;
double minY = Double.MAX_VALUE ;
double maxX = Double.MIN_VALUE ;
double maxY = Double.MIN_VALUE ;
for (Data<Number, Number> d : boundedSeries.getData()) {
if (d.getXValue().doubleValue() < minX) minX = d.getXValue().doubleValue() ;
if (d.getXValue().doubleValue() > maxX) maxX = d.getXValue().doubleValue() ;
if (d.getYValue().doubleValue() < minY) minY = d.getYValue().doubleValue() ;
if (d.getYValue().doubleValue() > maxY) maxY = d.getYValue().doubleValue() ;
}
positionLineInAxisCoordinates(leftLine, minX, yAxis.getLowerBound(), minX, yAxis.getUpperBound());
positionLineInAxisCoordinates(rightLine, maxX, yAxis.getLowerBound(), maxX, yAxis.getUpperBound());
positionLineInAxisCoordinates(bottomLine, xAxis.getLowerBound(), minY, xAxis.getUpperBound(), minY);
positionLineInAxisCoordinates(topLine, xAxis.getLowerBound(), maxY, xAxis.getUpperBound(), maxY);
}
}
private void positionLineInAxisCoordinates(Line line, double startX, double startY, double endX, double endY) {
double x0 = xAxis.getDisplayPosition(startX);
double x1 = xAxis.getDisplayPosition(endX);
double y0 = yAxis.getDisplayPosition(startY);
double y1 = yAxis.getDisplayPosition(endY);
line.setStartX(x0);
line.setStartY(y0);
line.setEndX(x1);
line.setEndY(y1);
}
public void setBoundedSeries(Series<Number, Number> boundedSeries) {
if (! getData().contains(boundedSeries)) {
throw new IllegalArgumentException("Specified series is not displayed in this chart");
}
this.boundedSeries = boundedSeries ;
requestChartLayout();
}
}
private final Random rng = new Random();
#Override
public void start(Stage primaryStage) {
Series<Number, Number> series1 = new Series<>("Series 1", FXCollections.observableArrayList());
Series<Number, Number> series2 = new Series<>("Series 2", FXCollections.observableArrayList());
Series<Number, Number> series3 = new Series<>("Series 3", FXCollections.observableArrayList());
for (int i = 0 ; i < 40 ; i++) {
series1.getData().add(new Data<>(rng.nextDouble()*2 + 4, rng.nextDouble()*3 + 2));
series2.getData().add(new Data<>(rng.nextDouble()*2.5 + 4.75, rng.nextDouble()*1.5 + 2));
series3.getData().add(new Data<>(rng.nextDouble()*3 + 5, rng.nextDouble()*1.5 + 2.75));
}
NumberAxis xAxis = new NumberAxis();
NumberAxis yAxis = new NumberAxis();
xAxis.setForceZeroInRange(false);
yAxis.setForceZeroInRange(false);
ScatterChartWithBoundary chart = new ScatterChartWithBoundary(xAxis, yAxis);
chart.getData().addAll(series1, series2, series3);
VBox buttons = new VBox(2);
ToggleGroup toggleGroup = new ToggleGroup();
for (Series<Number, Number> series : chart.getData()) {
RadioButton rb = new RadioButton(series.getName());
rb.selectedProperty().addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
chart.setBoundedSeries(series);
}
});
rb.setToggleGroup(toggleGroup);
buttons.getChildren().add(rb);
}
BorderPane root = new BorderPane(chart);
root.setTop(buttons);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Here is a typical result:
I'm currently stacking two different charts (BarChart and LineChart) using JavaFX.
The LineChart has been divided into three areas to display where the points from the BarChart will fall into.
I am having a problem with my current code where the CategoryAxis from the BarChart and the NumberAxis from the LineChart is not aligning well on the xAxis.
This is what it currently looks like:
As you can see (click on the image to zoom in), some of the values along the xAxis does not align. I am unable to use the same NumberAxis for the LineChart and BarChart because BarChart only supports CategoryAxis. Is there any way to fix this so that the values along the xAxis aligns?
Code:
NumberAxis xAxis = new NumberAxis(0, 6, 1);
xAxis.setLabel("X");
NumberAxis yAxis = new NumberAxis(-5, 5, 0.5);
yAxis.setLabel("Y");
XYChart.Series<Number, Number> dataSeries1 = new XYChart.Series<>();
XYChart.Series<Number, Number> dataSeries2 = new XYChart.Series<>();
dataSeries1.setName("2SD");
dataSeries2.setName("3SD");
dataSeries1.getData().add(new XYChart.Data<>(0, 0.6));
dataSeries1.getData().add(new XYChart.Data<>(1, 0.9));
dataSeries1.getData().add(new XYChart.Data<>(2, 1.7));
dataSeries1.getData().add(new XYChart.Data<>(3, 1.8));
dataSeries1.getData().add(new XYChart.Data<>(4, 1.8));
dataSeries1.getData().add(new XYChart.Data<>(5, 2.1));
dataSeries1.getData().add(new XYChart.Data<>(6, 2.3));
dataSeries2.getData().add(new XYChart.Data<>(0, 1.0));
dataSeries2.getData().add(new XYChart.Data<>(1, 1.3));
dataSeries2.getData().add(new XYChart.Data<>(2, 2.4));
dataSeries2.getData().add(new XYChart.Data<>(3, 2.5));
dataSeries2.getData().add(new XYChart.Data<>(4, 2.7));
dataSeries2.getData().add(new XYChart.Data<>(5, 3.1));
dataSeries2.getData().add(new XYChart.Data<>(6, 3.3));
final LineChart<Number, Number> lineChart =
new LineChart<Number, Number>(xAxis, yAxis, FXCollections.observableArrayList(dataSeries2, dataSeries1)) {
#Override
protected void layoutPlotChildren() {
super.layoutPlotChildren();
XYChart.Series a = getData().get(0);
XYChart.Series b = getData().get(1);
ObservableList<XYChart.Data<Number, Number>> aData = a.getData();
ObservableList<XYChart.Data<Number, Number>> bData = b.getData();
for(int i = 0; i < aData.size()-1; i++){
// Color in the area between 2SD and 3SD
double x = getXAxis().getDisplayPosition(aData.get(i).getXValue());
double y = getYAxis().getDisplayPosition(bData.get(i).getYValue());
double x2 = getXAxis().getDisplayPosition(aData.get((i+1)).getXValue());
double y2 = getYAxis().getDisplayPosition(bData.get((i+1)).getYValue());
Polygon middlePolygon = new Polygon();
middlePolygon.getPoints().addAll(x,y,
x, getYAxis().getDisplayPosition(aData.get(i).getYValue()),
x2,getYAxis().getDisplayPosition(aData.get((i+1)).getYValue()),
x2,y2);
getPlotChildren().add(middlePolygon);
middlePolygon.toFront();
middlePolygon.setFill(Color.ORANGE);
// Color in the area above 3SD
double y3 = getXAxis().getDisplayPosition(0);
double y4 = getXAxis().getDisplayPosition(0);
Polygon topPolygon = new Polygon();
topPolygon.getPoints().addAll(x,y3,
x, getYAxis().getDisplayPosition(aData.get(i).getYValue()),
x2,getYAxis().getDisplayPosition(aData.get(i+1).getYValue()),
x2,y4);
getPlotChildren().add(topPolygon);
topPolygon.toFront();
topPolygon.setFill(Color.RED);
// Color in the area below 2SD
double xb = getXAxis().getDisplayPosition(bData.get(i).getXValue());
double yb = getYAxis().getDisplayPosition(-10);
double xb2 = getXAxis().getDisplayPosition(bData.get(i+1).getXValue());
double yb2 = getYAxis().getDisplayPosition(-10);
Polygon bottomPolygon = new Polygon();
bottomPolygon.getPoints().addAll(xb,yb,
xb, getYAxis().getDisplayPosition(bData.get(i).getYValue()),
xb2,getYAxis().getDisplayPosition(bData.get((i+1)).getYValue()),
xb2,yb2
);
getPlotChildren().add(bottomPolygon);
bottomPolygon.toFront();
bottomPolygon.setFill(Color.GREEN);
}
}
};
lineChart.setCreateSymbols(false);
CategoryAxis xAxis2 = new CategoryAxis();
xAxis2.setLabel("X");
BarChart<String, Number> barChart = new BarChart<>(xAxis2, yAxis);
barChart.setLegendVisible(false);
barChart.setAnimated(false);
//barChart.setAlternativeRowFillVisible(false);
//barChart.setAlternativeColumnFillVisible(false);
//barChart.setHorizontalGridLinesVisible(false);
//barChart.setVerticalGridLinesVisible(false);
barChart.getXAxis().setVisible(false);
barChart.getYAxis().setVisible(false);
XYChart.Series<String, Number> barSeries = new XYChart.Series<>();
barSeries.getData().add(new XYChart.Data<>("1", 2.4));
barSeries.getData().add(new XYChart.Data<>("2", 2.7));
barSeries.getData().add(new XYChart.Data<>("3", 0.8));
barSeries.getData().add(new XYChart.Data<>("4", -3.2));
barSeries.getData().add(new XYChart.Data<>("5", -0.1));
barChart.getData().addAll(barSeries);
barChart.getStylesheets().add("sample/BarChart.css");
barChart.setPadding(new Insets(0,0,35,3));
StackPane pane = new StackPane();
pane.getChildren().addAll(lineChart, barChart);
You can make your NumberAxis start at 0.5 and end at 5.5 then the tick marks will nearly overlap. Then you tell either the NumberAxis or the CategoryAxis not to show it's tick marks and it will be fitting.
If you don't want to change the range of the NumberAxis and you are ok with the numbers not being aligned to the values of the BarChart, you can simply tell the CategoryAxis not to show it's tick marks.
The function for that is setTickLabelsVisible(boolean) and probably you also should use setTickMarkVisible(boolean).
I didn't test this.
In JavaFX line chart, a type of two-axis chart that presents data as a series of points connected by straight lines. All points are connected with one line. But I want to connect them with my order . Like there are three points p1(x1,y1), p2(x2,y2), p3(x3,y3) . In line chart, they are all connected with a single line , but I want connect p1,p3 and p1,p2 separately . How can I do that ? X-axis and Y-axis must present there .
By default JavaFX will join all points in a series. Simply solution is to use multiple series. Note you cannot share Data instances between series because Data stores a back-reference to the Node - see javafx.scene.chart.XYChart.Data.getNode()
#Override
public void start(Stage primaryStage) throws Exception {
NumberAxis xAxis1 = new NumberAxis();
NumberAxis yAxis1 = new NumberAxis();
LineChart<Number, Number> chart = new LineChart<Number, Number>(xAxis1, yAxis1);
double p1x = 0;
double p1y = 0;
double p2x = 5;
double p2y = 5;
double p3x = 3;
double p3y = 5;
Series<Number, Number> series1 = new Series<>();
series1.getData().add(new Data<>(p1x, p1y));
series1.getData().add(new Data<>(p2x, p2y));
chart.getData().add(series1);
Series<Number, Number> series2 = new Series<>();
series2.getData().add(new Data<>(p2x, p2y));
series2.getData().add(new Data<>(p3x, p3y));
chart.getData().add(series2);
primaryStage.setScene(new Scene(chart));
primaryStage.show();
}
I improved on #Adam answer to address your new probem:
#Override
public void start(Stage primaryStage) throws Exception {
NumberAxis xAxis1 = new NumberAxis();
NumberAxis yAxis1 = new NumberAxis();
LineChart<Number, Number> chart = new LineChart<Number, Number>(xAxis1, yAxis1);
Random random = new Random();
ArrayList<Series<Number, Number>> seriesContainer = new ArrayList();
for(int i = 0; i < 10; i++)
{
Series<Number, Number> series1 = new Series<>();
series1.getData().add(new XYChart.Data<>(random.nextDouble() * 100, random.nextDouble() * 100));
series1.getData().add(new XYChart.Data<>(random.nextDouble() * 100, random.nextDouble() * 100));
seriesContainer.add(series1);
}
chart.getData().addAll(seriesContainer);
primaryStage.setScene(new Scene(chart));
primaryStage.show();
}
I have created a bar chart with multi series as you can see at the picture.
The List ent has 2 Lists.
I need to somehow write their xAxis value on top of their body diagonally (with a rotation of 45 degree). And set another value for xAxis later.
public class FunctionalRedundantController {
#FXML
private BarChart<String, Number> barchart;
#FXML
private CategoryAxis funcNameAxis;
#FXML
private NumberAxis yAxis;
private ObservableList<String> funcNames = FXCollections
.observableArrayList();
private DataConstructor dc = new DataConstructor();
#FXML
private void initialize() {
funcNameAxis.setLabel("Name of Functions");
funcNameAxis.tickLabelFontProperty().set(Font.font(10));
yAxis.setLabel("Redundant");
}
public void setfunctionalredundant() {
XYChart.Series<String, Number> series = new XYChart.Series<>();
XYChart.Series<String, Number> series2 = new XYChart.Series<>();
int green = 0;
int yellow = 0;
List<String> compGreen = new ArrayList<String>();
List<String> compyellow = new ArrayList<String>();
for (List<String> ent : dc.getFuncTypeOrg().values()) {
if (ent.size() > 10) {
for (int k = 0; k < ent.size(); k++) {
if (ent.get(k).equals("hasType")) {
if (ent.get(k+1).equals("Green")) {
series.getData().add(
new XYChart.Data<String, Number>(ent.get(k-2), 1));
}
else if(ent.get(k+1).equals("yellow")){
series2.getData().add(
new XYChart.Data<String, Number>(ent.get(k-2), 1));
}
}
}
}
}
barchart.getYAxis().setVisible(false);
barchart.getYAxis().setOpacity(0);
barchart.getData().addAll(series, series2);
barchart.setBarGap(1);
barchart.setCategoryGap(20);
}
}