JavaFX real-time LineChart with time axis - java

I'm trying to plot real-time graph, with time axis, but I have found the LineChart constructor only has the signature.
LineChart(Axis<X> xAxis, Axis<Y> yAxis)
I think embedding jfree chart in javafx is not a proper solution.
I want a few of the jfree features in a javafx LineChart, is this possible?

Download Ensemble sample from http://www.oracle.com/technetwork/java/javafx/samples/index.html
There are several examples in it for dynamic charts, e.g. "Advanced Stock Line Chart". You can take a look at their source code directly in the application.
To show time on axis you can use string and DateFormatter:
BarChart<String, Number> chart = new BarChart<>(new CategoryAxis(), new NumberAxis());
final XYChart.Series<String, Number> series1 = new XYChart.Series<>();
chart.getData().addAll(series1);
SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
Date date = new Date();
for (int i = 0; i <= 10; i += 1) {
date.setTime(date.getTime() + i * 11111);
series1.getData().add(new XYChart.Data(dateFormat.format(date), Math.random() * 500));
}

The class org.jfree.chart.demo.TimeSeriesChartDemo1 is included with the distribution. It is pictured in the demo, and its source illustrates the use of the factory method ChartFactory.createTimeSeriesChart(). There's a related example here.

Related

Continuous linear line not showing in MPAndroidChart linechart?

I am making a linechart to show the number of cases of coronavirus in the world.
private void setLinearChart() {
lineChart.setDragEnabled(true);
lineChart.setScaleEnabled(false);
lineChart.getAxisRight().setEnabled(false);
lineChart.getXAxis().setTextSize(1f);
lineChart.getXAxis().setPosition(XAxis.XAxisPosition.BOTTOM);
lineChart.setVisibleXRange(100, 100);
lineChart.getAxisLeft().setDrawGridLines(false);
lineChart.getXAxis().setDrawGridLines(false);
lineChart.setVisibleXRangeMinimum(100);
lineChart.setVisibleYRangeMinimum(100,
lineChart.getAxisLeft().getAxisDependency());
caseslist = getCasesList(casesMap);
LineDataSet casesSet = new LineDataSet(caseslist, "Cases");
casesSet.setFillAlpha(110);
LineData casesLineData = new LineData(casesSet);
XAxis xAxis = lineChart.getXAxis();
xAxis.setValueFormatter(new DateValueFormatter());
lineChart.setData(casesLineData);
lineChart.invalidate();
}
and I am getting a line char like this.
I this line to be a continuous curve like in below pic
For smoother curve use
casesSet.setMode(LineDataSet.Mode.CUBIC_BEZIER);

How can I change charts generated by apache poi to not use smoothed lines and show empty cells as gaps?

I am using POI 3.12-beta1 and have code that creates a line chart with multiple datasets and named series in the legend. However, the default settings for line charts in poi generate a line that has been smoothed over the data points. Empty values are also being plotted as 0, but we want the lines to stop at the first column where there is an empty cell.
I can go into the chart properties once it is rendered in an xlsx file and change these settings, but we need to have the xlsx rendered with these settings. I can't find anything in the available API to change these settings.
I used this sample class as a starting point for my code below
http://svn.apache.org/repos/asf/poi/trunk/src/examples/src/org/apache/poi/xssf/usermodel/examples/LineChart.java
Drawing drawing = sheet.createDrawingPatriarch();
ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 17, 18, 30);
Chart chart = drawing.createChart(anchor);
ChartLegend legend = chart.getOrCreateLegend();
legend.setPosition(LegendPosition.RIGHT);
LineChartData data = chart.getChartDataFactory().createLineChartData();
ChartAxis bottomAxis = chart.getChartAxisFactory().createCategoryAxis(AxisPosition.BOTTOM);
ValueAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT);
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
int row = 2;
int startCol = 3;
int endCol = 17;
boolean abs = false;
ChartDataSource<Number> xs = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(row, row, startCol, endCol));
row = 10;
int seriesCol = 0;
ChartDataSource<Number> ys1 = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(row, row, startCol, endCol));
LineChartSerie ser1 = data.addSerie(xs, ys1);
ser1.setTitle(new CellReference(sheet.getSheetName(), row, seriesCol, abs, abs));
row = 11;
ChartDataSource<Number> ys2 = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(row, row, startCol, endCol));
LineChartSerie ser2 = data.addSerie(xs, ys2);
ser2.setTitle(new CellReference(sheet.getSheetName(), row, seriesCol, abs, abs));
row = 12;
ChartDataSource<Number> ys3 = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(row, row, startCol, endCol));
LineChartSerie ser3 = data.addSerie(xs, ys3);
ser3.setTitle(new CellReference(sheet.getSheetName(), row, seriesCol, abs, abs));
chart.plot(data, new ChartAxis[] { bottomAxis, leftAxis });
Thanks to Etienne for the code to set blanks as gaps. I got help from a POI developer and here is the solution that solves both issues mentioned in the original question.
XSSFChart chart = (XSSFChart)drawing.createChart(anchor);
// this will set blank values as gaps in the chart so you
// can accurately plot data series of different lengths
CTDispBlanksAs disp = CTDispBlanksAs.Factory.newInstance();
disp.setVal(STDispBlanksAs.GAP);
chart.getCTChart().setDispBlanksAs(disp);
// setup chart, axes, data series, etc
chart.plot(data, new ChartAxis[] { bottomAxis, leftAxis });
// this must occur after the call to chart.plot above
CTPlotArea plotArea = chart.getCTChart().getPlotArea();
for (CTLineChart ch : plotArea.getLineChartList()) {
for (CTLineSer ser : ch.getSerList()) {
CTBoolean ctBool = CTBoolean.Factory.newInstance();
ctBool.setVal(false);
ser.setSmooth(ctBool);
}
}
I couldn't find any solution to that same problem so I ended up trying to find some "creative workaround". It's not a clean approach at all, but I'll share it just in case.
It turns out that the default Chart POI interface doesn't allow to do a lot of operations (modify Axis and Legend, and that's about it).
I do work with XLSX files however, and the implementing XSSFChart class provides a bit more features, such as a setPlotOnlyVisibleCells method that does work in enabling/disabling that option in the "Hidden & Empty Cells" menu. However, that's not what we want!
If you look at the XLSX file format (which is basically XML), you'll notice there's a "dispBlanksAs" XML element as a child of the Chart element, and we'll want to set it to "gap". (POI won't set it, which is equivalent to set it to "zero").
Unfortunately, there's no such method in the POI API currently, so I had to resort to the following dirty code to get it working:
XSSFChart chart = (XSSFChart)drawing.createChart(anchor);
CTDispBlanksAs disp = CTDispBlanksAs.Factory.newInstance();
disp.setVal(STDispBlanksAs.GAP);
chart.getCTChart().setDispBlanksAs(disp);
This code is very likely reprehensible in many ways, but it works for me :)
An important note is that you'll need to have the ooxml-schemas 1.1 jar on the classpath for this to work. It won't work even with poi-ooxml-schemas 3.12, as it doesn't contain the definition for CTDispBlanksAs class.
Hopefully this feature will be supported in a future version of POI.

Too much data in DomainAxis, another way of setTickUnit

I am creating chart like this:
DefaultCategoryDataset cumulativeResultData = new DefaultCategoryDataset();
DefaultCategoryDataset indexResultData = new DefaultCategoryDataset();
then in for loop im adding data to each Dataset using cumulativeResultData.addValue
CategoryPlot plot = new CategoryPlot();
plot.setDomainAxis(new CategoryAxis("Days"));
plot.setRangeAxis(new NumberAxis("Result"));
plot.setOrientation(PlotOrientation.VERTICAL);
CategoryAxis domainAxis = plot.getDomainAxis();
domainAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_45);
LineAndShapeRenderer renderer2 = new LineAndShapeRenderer(true, false);
LineAndShapeRenderer renderer = new LineAndShapeRenderer(true, false);
plot.setRenderer(renderer);
plot.setDataset(cumulativeResultData);
ValueAxis rangeAxis2 = new NumberAxis("Index price");
rangeAxis2.setRange(minRange - 1, maxRange + 1);
plot.setRangeAxis(1, rangeAxis2);
plot.setRenderer(1, renderer2);
plot.setDataset(1, indexResultData);
plot.mapDatasetToRangeAxis(1, 1);
JFreeChart chart = new JFreeChart(plot);
But what i get is not what i expected to get, please take a look in the picture bellow.
The domainAxis has to much data and it is blurred. Is any way to repair this? That only every for example 20th element shows on the domainAxis?
http://imgur.com/PkxSZl8
You should only use the CategoryPlot class when your x-axis will show categorical data. It looks like you have time series data, and for that you can use the XYPlot class with a DateAxis for the x-axis. There is also the TimeSeriesCollection class that you can probably use for your dataset.

Java JFreeChart Category Step Chart horizontal (image to explane)

i need the following type of chart:
It should be a "steped" line chart with categories on the vertical axis, like this:
I found this example of an Category Step Chart, but it the orientation is not right for my purpose.
http://www.jfree.org/jfreechart/api/javadoc/org/jfree/chart/renderer/category/CategoryStepRenderer.html
All i have done so far is this, but as you can see the red line does not fit to the orientation of the chart(should be horizontal):
The corresponding code to this:
DefaultCategoryDataset ds = new DefaultCategoryDataset();
// create dataset
for (int k = 0; k < ffCount; k++) {
StateSignal ss1 = (StateSignal) this.ffDSet.getFframes().get(k).getSignals().get(i);
ds.setValue((double) k + 1, ss1.getName(), ss1.getStates().get(0).getStatus());
}
CategoryStepRenderer categorysteprenderer = new CategoryStepRenderer(false);
categorysteprenderer.setBaseToolTipGenerator(new StandardCategoryToolTipGenerator());
CategoryAxis categoryaxis = new CategoryAxis("Category");
NumberAxis numberaxis = new NumberAxis("Value");
CategoryPlot categoryplot = new CategoryPlot(ds, categoryaxis, numberaxis, categorysteprenderer);
categoryplot.setRangePannable(true);
categoryplot.setOrientation(PlotOrientation.HORIZONTAL);
chart = new JFreeChart("test", null, categoryplot, true);
I donĀ“t get it to work. Any ideas?
Thanks in advance!
It looks like you need to use a standard XYLineChart with a XYStepRenderer and a SymbolAxis to replace the default Range Axis rather than a CategoryStepRenderer and a horizontal plot orientation
If you associate Status A and B with a numerical value say 1 and 2 you can create a chart like this:
Using this a XYStepRenderer
XYStepRenderer renderer = new XYStepRenderer();
renderer.setBaseShapesVisible(true);
renderer.setSeriesStroke(0, new BasicStroke(2.0f));
renderer.setSeriesStroke(1, new BasicStroke(2.0f));
renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator());
renderer.setDefaultEntityRadius(6);
plot.setRenderer(renderer);
and a Symbol Axis
String[] grade = new String[3];
grade[0] = "";
grade[1] = "Status A";
grade[2] = "Status B";
SymbolAxis rangeAxis = new SymbolAxis("", grade);
rangeAxis.setTickUnit(new NumberTickUnit(1));
rangeAxis.setRange(0,3);
plot.setRangeAxis(rangeAxis);
In this example the SymbolAxis provides an alternative label for each value in the Axis

Well displaying the Y-axis on jFreechart

Please how can I change the Y-axis to have data displayed with the same number of digits.
This is what I have on my graph
and this is the code that produces that part of the graph
final CategoryPlot plot = (CategoryPlot)result.getPlot ();
plot.setBackgroundPaint(Color.BLACK);
plot.setDomainGridlinePaint(Color.white);
plot.setRangeGridlinePaint(Color.white);
NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
rangeAxis.setRange (1.2, 1.3);
rangeAxis.setStandardTickUnits(NumberAxis.createStandardTickUnits ());
rangeAxis.setTickUnit (new NumberTickUnit(0.005));
As you can see, I fix the begin at 1.2 and the end at 1.3 and I define the TickUnit at 0.005. So normaly (I think) I should have something gradualy like :
1.2 1.205 1.210 ... 1.305
This is what I would like to obtain
How can I modify the code to obtain the Y-axis with gradues values ?
Thanks
I think I have found this solution
DecimalFormat df = new DecimalFormat("0.000");
NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
rangeAxis.setRange (1.200, 1.300);
rangeAxis.setStandardTickUnits(NumberAxis.createStandardTickUnits ());
rangeAxis.setTickUnit (new NumberTickUnit(0.005, df, 0));
it seems to work fine.
Thanks
The chart axis will accept a java.text.NumberFormat instance, which it will use to format the labels. To do this, try adding these lines to your code:
DecimalFormat newFormat = new DecimalFormat("0.000");
rangeAxis.setNumberFormatOverride(newFormat);

Categories