I want plot a line chart using Trinidad 1.2. I have a class called AcidenteTO which consists in int indicating the year and a array of int indicating the values for each month. In ChartModel used to populate my chart, I passed a List of AcidenteTO's.
public class AcidenteTO implements Serializable {
// -------------[ Atributos ]----------------------
private int ano; // year
private int[] quantidades = new int[12]; // quantities for month
// Getters & Setters as usual
}
public class AcidentesChartModel extends ChartModel {
private final List<AcidenteTO> acidentes;
// Indicate the labels for each year
private final List<String> anosLabel;
private List<List<Double>> yValues;
private final Double maxYValue;
// Month labels
private final List<String> mesesLabels =
Arrays.asList("Janeiro", "Fevereiro", "Março", "Abril", "Maio",
"Junho", "Julho", "Agosto", "Setembro", "Outubro",
"Novembro", "Dezembro");
AcidentesChartModel(List<AcidenteTO> acidentes) {
this.acidentes = acidentes;
yValues = new ArrayList<List<Double>>(this.acidentes.size());
List<String> anosLabel = new ArrayList<String>(this.acidentes.size());
this.anosLabel = new ArrayList<String>(this.acidentes.size());
double maxY = 0D;
for (AcidenteTO a : this.acidentes) {
maxY = preencherDados(a, maxY);
}
this.maxYValue = maxY;
}
private double preencherDados(AcidenteTO a, double maxY) {
List<Double> mesesValues = new ArrayList<Double>(12); // Monthly values for each year
for (int i = 0; i < 12; i++) {
mesesValues.add((double)a.getQuantidades()[i]);
if (a.getQuantidades()[i] > maxY) {
maxY = a.getQuantidades()[i];
}
}
this.anosLabel.add(String.valueOf(a.getAno()));
this.yValues.add(mesesValues);
return maxY;
}
public List<String> getSeriesLabels() {
return this.anosLabel;
}
public List<String> getGroupLabels() {
return this.mesesLabels;
}
public List<List<Double>> getYValues() {
return this.yValues;
}
public Double getMaxYValue() {
return this.maxYValue;
}
}
What I was expecting is that as for each Year I have 12 data to plot, it would be plot all 12 values for each year. However what actually happened was the following: If I pass a List of 3 AcidenteTO's, the generated plot shows data until 3rd month. If I pass 4 AcidenteTO's List, the plot shows data until 4th month. 7 AcidenteTO's List, until 7th month (see picture below), and so on.
I could not found no example in the net showing how to use a ChartModel to create a Trinidad line chart. Just this one for pie charts. Therefore I was not able to discover where is my mistake.
Thanks,
Rafael Afonso.
Ever considered using Highcharts to generate graphs in your Trinidad project? We do and it works like a charm! Highcharts has way more possibilities to generate graphs and add interactivity then <tr:chart>. See these examples for pie charts and other graphs in Highcharts.
The downside is that you don't have a JSF component to work with. Creating a static graph will be easy, but you might want to write a few lines of code to pass your data to the Highcharts Javascript to get things a bit more interesting. Trinidad's ExtendedRenderKitService comes in handy there:
ExtendedRenderKitService service =
Service.getRenderKitService(facesContext, ExtendedRenderKitService.class);
service.addScript(facesContext, "alert('foo');");
Edit
I just found this, haven't tried it yet, but I will test it: Java API for generating highchart JSON.
Related
I have been searching for how to format the text (remove decimals) of point labels on Bar graph.
This is my code:
BarFormatter bf = new BarFormatter(Color.CYAN,Color.CYAN);
PointLabelFormatter plf = new PointLabelFormatter();
plf.getTextPaint().setColor(Color.BLUE);
bf.setPointLabelFormatter(plf);
i can only set the color here. The label is displayed with decimals. How to remove it or set a decimal format in this?
This comment https://stackoverflow.com/a/39482297/9969285 here describes pointlabeler under lineandpointformatter. How to implement this for a bar graph?
Should be the same as what you linked - BarFormatter extends LineAndPointFormatter:
bf.setPointLabeler(new PointLabeler<XYSeries>() {
final DecimalFormat df = new DecimalFormat("#");
#Override
public String getLabel(XYSeries series, int index) {
return df.format(series.getY(index));
}
});
I have been using this example for my project, and it works really nice.
My question: Is it possible to offset the hovered node such that it does not overlay the underlying data point. The example centers the hovered node right over the "normal" node. It kind of gets in the way on a chart with a lot of data points.
A simple solution is to set a custom translation to the displayed Label. The following code is extracted from the example.
private Label createDataThresholdLabel(int priorValue, int value)
{
final Label label = new Label(value + "");
label.setTranslateY(-25); //Move label 25 pixels up
label.getStyleClass().addAll("default-color0", "chart-line-symbol", "chart-series-line");
label.setStyle("-fx-font-size: 20; -fx-font-weight: bold;");
if (priorValue == 0)
{
label.setTextFill(Color.DARKGRAY);
}
else if (value > priorValue)
{
label.setTextFill(Color.FORESTGREEN);
}
else
{
label.setTextFill(Color.FIREBRICK);
}
label.setMinSize(Label.USE_PREF_SIZE, Label.USE_PREF_SIZE);
return label;
}
I have a JavaFX app which contains a line chart. I want users to be able to select the color of each series in the chart. Since the selection is dynamic I can't use static CSS to set the colors. I also have other controls that I need to set to the same color as the associated series. It's possible to set the line color of a series dynamically using code like this:
series.getNode().setStyle("-fx-stroke: " + color + ";");
That works well and I can use the user-specified color on the associated controls.
My problem is that I also need to set the color of the symbols for each series to the same color. I can't find any way to do that dynamically. All of the tutorials, documentation, and posts that I've read on the topic point to the static CSS approach.
Most charting widgets make this sort of thing very easy to do, but I've found no clues here or on the Oracle forums. Any guidance would be greatly appreciated.
-- Update --
I've found no way to do this other than to enumerate every data point in every series, grab the associated symbol node and set the style individually. Not what I was hoping for. In the process I realized that the default Node allocated for a symbol is a StackPane. I didn't need that flexibility so I replaced it with a Rectangle. This made rendering faster.
I'm late to the game, but maybe someone can use my solution. What worked for me, was iterating through every item in the data series and setting the CSS style for each one.
for (int index = 0; index < series.getData().size(); index++) {
XYChart.Data dataPoint = series.getData().get(index);
Node lineSymbol = dataPoint.getNode().lookup(".chart-line-symbol");
lineSymbol.setStyle("-fx-background-color: #00ff00, #000000; -fx-background-insets: 0, 2;\n" +
" -fx-background-radius: 3px;\n" +
" -fx-padding: 3px;");
}
I was stuck with a similar problem. I don't know upfront which data is going to be added to the graph, so I can't make use of a fixed stylesheet.
I came up with this solution. This code listens for new series added to graph. For every added series, it will create a new listener for data added to the series.
This listener will look up which series this is, the 0th, 1st, etc and then find the two nodes for the coloring of the line and of the legend/symbol.
As soon as it has set both, it can unsubscribe.
Problem can be that the legend/symbol node is not available yet when you receive the callback on the first added datapoint.
I'm aware it's very convoluted and I'm open to hear improvements.
At least it will give you the option to dynamically set the color to anything you want.
final LineChart<Number, Number> chart = new LineChart<>(new NumberAxis(), new NumberAxis());
final ObservableList<Series<Number, Number>> series = chart.getData();
series.addListener(new ListChangeListener<Series<Number, Number>>() {
#Override
public void onChanged(Change<? extends Series<Number, Number>> change) {
ObservableList<? extends Series<Number, Number>> list = change.getList();
for (final Series<Number, Number> serie : list) {
serie.getData().addListener(new ListChangeListener<Data<Number, Number>>() {
#Override
public void onChanged(Change<? extends Data<Number, Number>> ignore) {
int index = series.indexOf(serie);
Set<Node> nodes = chart.lookupAll(".series" + index);
boolean isStyleSet = false;
for (Node n : nodes) {
if (StringUtils.isEmpty(n.getStyle())) {
String css = "-fx-stroke: %s; -fx-background-color: %s, white; ";
String color = //assign dynamically here, for instance based on the name of the series
n.setStyle(String.format(css, color, color));
isStyleSet = true;
}
}
if (!isStyleSet & nodes.size() > 1) {
serie.getData().removeListener(this);
}
}
});
}
}
});
I had a problem which might be slightly different (possibly more complex); I needed to style some nodes of a series one color, others within the same series another color (I also needed to be able to change the allocation of color dynamically). I am working in JavaFx 2.2; I have css-styling, but of course that does not help here. I could not find my issue addressed anywhere; this was the closest I've found.
I just want to say that I could not get "series.getNode().setStyle("-fx-stroke: " + color + ";")" to work. However, using "-fx-background" instead does work. I hope this helps someone.
I have a list of color representing a color sequence. I want to apply the new color sequence to the piechart data.
private final int CASPIAN_COLOR_COUNTS = 8;
public void setPieChartColor(PieChart chart, List<String> colors) {
chart.getData().get(i); // for debug to get the node name (.data)
/**
* Set Pie color
*/
int i = 0;
for (String color : colors) {
final Node node = chart.lookup(".data" + i);
node.getStyleClass().remove("default-color" + (i % CASPIAN_COLOR_COUNTS));
node.getStyleClass().add(color);
i++;
}
but all chart data take Only one color from Caspian color.
You can achieve custom pie colors in code using a method such as:
private void applyCustomColorSequence(
ObservableList<PieChart.Data> pieChartData,
String... pieColors) {
int i = 0;
for (PieChart.Data data : pieChartData) {
data.getNode().setStyle(
"-fx-pie-color: " + pieColors[i % pieColors.length] + ";"
);
i++;
}
}
Note that the method must be applied after the chart has been shown on an active scene (otherwise the data.getNode() call will return null).
Here is some sample code which uses it.
You can accomplish the same effect using css stylesheets.
For example a css stylesheet containing the following style definitions will change the default colors of a pie chart when the stylesheet is applied against a given chart.
.default-color0.chart-pie { -fx-pie-color: #ffd700; }
.default-color1.chart-pie { -fx-pie-color: #ffa500; }
.default-color2.chart-pie { -fx-pie-color: #860061; }
.default-color3.chart-pie { -fx-pie-color: #adff2f; }
.default-color4.chart-pie { -fx-pie-color: #ff5700; }
For an example of the stylesheet based approach: see the "Setting Colors of a Pie Chart" section of the Styling Charts with CSS tutorial.
The stylesheet approach has an advantage that the styles are separated from the code. It has the disadvantage that the colors are must be set the time the stylesheet are created rather than at runtime and the color sequence is restricted to a fixed number of colors (8).
In general, the stylesheet approach is recommended for most applications.
The css styles might not work if your values are negative. This might be a bug but I had to convert my values to positive values for the styles to work. When the values were negative all pies were white in color.
I was using dynamic time chart.
Timeseries chart when the mouse is clicked I want to get the time value.
But the method I used was able to get the y-axis value.
How can I get the x-axis time?
Please help me.
code
chartComposite.addChartMouseListener(new ChartMouseListener() {
#Override
public void chartMouseMoved(ChartMouseEvent arg0) {
}
#Override
public void chartMouseClicked(ChartMouseEvent event) {
XYItemEntity entity = (XYItemEntity)event.getEntity();
XYDataset dataset = entity.getDataset();
int series = entity.getSeriesIndex();
int item = entity.getItem();
Comparable seriesKey = dataset.getSeriesKey(series);
System.out.println(series + " " + item + " " + seriesKey);
}
});
have you tried this?
Date date = new Date(dataset.getX(entity.getSeriesIndex(),entity.getItem()).longValue());