Reputation: 107
I'm writing a javafx
program that is supposed to show a BubbleChart
for analysis of companies. On the y axis last year revenues are displayed and on the x axis the number of market segments that the company operates in is displayed. The radius of the bubble depends on what kind of market segments the company operates in.
I know that I can make a "bubble" by writing:
new XYChart.Data<Number,Number>(nbrOfMarketSegments, latestYearRevenue, radiusOfBubble);
The problem is that the y axis maximum is 100 (million EUR) and the x axis maximum is 20, which makes the "bubbles" very flat like in the picture (since radiusOfBubble
number is big compared to the maximum number of market segments and small compared to the maximum revenue. My question is, is there any way to make the bubbles more circle like even if the x axis and y axis have such different spans?
Upvotes: 2
Views: 658
Reputation: 24444
JavaFX charts are an evil piece of code. Adaption is only possible if you exactly know the code base behind, which is somehow against the good principle of information hiding. Getting round bubbles in a BubbleChart
is one of those adaptions.
The easiest thing you can do is to subclass BubbleChart
and create your own round bubbles.
Things that don't work:
The place where the data nodes are created (method createBubble
) is private, so you can't override it to create Circle
s instead. (Apart from that - even if you had a custom data node, it wouldn't work as the layout code in BubbleChart
ignores all data nodes that are not of type Ellipse
, so you'd end up having round bubbles but all at position (0/0)...).
The next thing that comes into mind is to override the place where the axes of the ellipsis are set. But no, all the layouting code is one single method - there is no layoutNode(Node)
-method that is called for every node and that you could override to add your custom layouting code.
Solution:
So you have to override the method layoutPlotChildren
, call the base implementation and then change the Y-radius of all nodes afterwards:
public class CircularBubbleChart<X, Y> extends BubbleChart<X, Y> {
public CircularBubbleChart(Axis<X> xAxis, Axis<Y> yAxis) {
super(xAxis, yAxis);
}
public CircularBubbleChart(Axis<X> xAxis, Axis<Y> yAxis, ObservableList<Series<X, Y>> data) {
super(xAxis, yAxis, data);
}
@Override
protected void layoutPlotChildren() {
super.layoutPlotChildren();
getData().stream().flatMap(series -> series.getData().stream())
.map(Data::getNode)
.map(StackPane.class::cast)
.map(StackPane::getShape)
.map(Ellipse.class::cast)
.forEach(ellipse -> ellipse.setRadiusY(ellipse.getRadiusX()));
}
}
Usage is simple: Just change the chart creation code line from ... = new BubbleChart()
to ... = new CircularBubbleChart()
. Everything else stays the same.
Note: This code changes the Y-Radius to be equal to the X-Radius, so the bubble radius will be in units of the X-Axis. Of course, you can also to the opposite to get a bubble radius in units of the Y-Axis.
Upvotes: 6