JFreeChart relative scaling - java

I want to create a Linegraph with JFreechart. My Data is like :
Number1,Number2,Timestamp
So I want two Lines Where the Y-Axis is Number1 and Number2 and the X-Axis it the Timestamp.
I save the Chart as a png Image and the X-Axis looks just to long I think it is because of the timestamp beeing long apart.
I Searched google and Stackoverflow but answers like this dont work for me. How can i Fix this?
Here is my code:
TimeSeries ts = new TimeSeries("Systole");
ts.add(new Minute(new Date(1356999660000L)), 141);
ts.add(new Minute(new Date(1356999840000L)), 129);
ts.add(new Minute(new Date(1433074800000L)), 146);
ts.add(new Minute(new Date(1433075700000L)), 136);
TimeSeries diast = new TimeSeries("Diastole");
diast.add(new Minute(new Date(1356999660000L)), 95);
diast.add(new Minute(new Date(1356999840000L)), 86);
diast.add(new Minute(new Date(1433074800000L)), 98);
diast.add(new Minute(new Date(1433075700000L)), 91);
final XYSeriesCollection dataset = new XYSeriesCollection();
dataset.addSeries(systole);
dataset.addSeries(diastole);
XYDataset data = dataset;
final TimeSeriesCollection dataset = new TimeSeriesCollection();
dataset.addSeries(ts);
dataset.addSeries(diast);
XYDataset data = dataset;
//String title, String timeAxisLabel, String valueAxisLabel, XYDataset dataset
JFreeChart chart = ChartFactory.createTimeSeriesChart("Blutdruck", "", "", data);
SaveImage(chart.createBufferedImage(500,500),"Test1.png");

The "vertical lines" effect happens because you are plotting a series that fluctuates on very different scales (minutes vs. years). Obviously, the variations at the scale of a minute are completely invisible when plotting them over a period of two years. It is like trying to draw the streets of a city on the map of a country.
There is no out-of-the-box solution for that problem, but you could find inspiration from the typical share price plots (from Google or Yahoo), where they let the user choose the scale they want to use.
Variation over five days:
Variation over three months:
Note that the variation over three months is smoother than the variation over five days. The reason is that they do not try to plot the value at every minute. Instead, they "subsample" their data.
In other words, they take the average over one day and plot only the values for each day.

Related

JFreeChart Candlestick zoom into last n candles on creation

I have an app and I somehow successfully managed to get a candlestick chart visible. However, once the chart is displayed, it shows all the candles (can be several hundred) which causes the chart to be very tiny and zoomed out. What I actually want is, the chart to be zoomed into the range of the last N candles so it looks more visible to the eye.
Here is how my charts look once they get created
I tried working with setRange, the Range on the Date Axis returns values in the format of [09.02.2023, 23:21:02 --> 10.02.2023, 08:29:56]
I dont seem to get how to make the chart like zoom in to a range of for example range.length-100 -> range.length. Whatever I tried, I always end up somewhere randomly without any data really shown.
Is there a way to define the last n entries on the range and set the dateAxis to start showing this range?
What I actually want to see after creation is something like this
private void displayChart(String title, String quoteMarket, BarSeries series) {
OHLCDataset dataSet = createOHLCDataset(title,series);
JFreeChart chart = ChartFactory.createCandlestickChart(
title,
"Time",
quoteMarket,
dataSet,
false);
chart.setBackgroundPaint(new Color(28, 39, 57));
chart.setBorderVisible(false);
chart.getTitle().setPaint(Color.WHITE);
// Candlestick rendering
CandlestickRenderer renderer = new CandlestickRenderer();
renderer.setAutoWidthMethod(CandlestickRenderer.WIDTHMETHOD_INTERVALDATA);
renderer.setUpPaint(new Color(0, 214, 110));
renderer.setDownPaint(new Color(196, 19, 30));
renderer.setSeriesPaint(0,new Color(211, 211, 211));
renderer.setCandleWidth(3.0);
XYPlot plot = chart.getXYPlot();
plot.setRenderer(renderer);
plot.setRangeGridlinePaint(Color.lightGray);
plot.setBackgroundPaint(new Color(28, 39, 57));
plot.setDomainGridlinesVisible( false );
plot.setRangeGridlinesVisible( false );
NumberAxis numberAxis = (NumberAxis) plot.getRangeAxis();
numberAxis.setAutoRangeIncludesZero(false);
numberAxis.setLabelPaint(Color.WHITE);
numberAxis.setTickLabelPaint(Color.WHITE);
numberAxis.setAutoRangeIncludesZero(false);
DateAxis dateAxis = (DateAxis) plot.getDomainAxis();
dateAxis.setAutoRange(true);
dateAxis.setLabelPaint(Color.WHITE);
dateAxis.setTickLabelPaint(Color.WHITE);
dateAxis.setTickMarkPosition(DateTickMarkPosition.MIDDLE);
plot.setDatasetRenderingOrder(DatasetRenderingOrder.FORWARD);
ChartPanel panel = new ChartPanel(chart);
panel.setMouseWheelEnabled(true);
panel.setMouseZoomable(true);
panel.setRangeZoomable(true);
panel.setDomainZoomable(true);
this.add(panel);
System.out.println(dateAxis.getRange());
}

doing a line graph with jfreechart in excel file

I am doing a lineal graph in java with JFreeChart, i was checking in internet and this is that i have at the moment:
DefaultKeyedValues data = new DefaultKeyedValues();
data.addValue("01/04/2012",7);
data.addValue("01/05/2012",5);
data.addValue("01/06/2012",6);
data.addValue("01/07/2012",2);
CategoryDataset dataset = DatasetUtilities.createCategoryDataset("Population", data);
JFreeChart chart = ChartFactory.createBarChart("Population","Date","Population",dataset,PlotOrientation.VERTICAL,true,true,false);
LineAndShapeRenderer renderer = new LineAndShapeRenderer();
renderer.setBaseLinesVisible(true);
CategoryPlot plot = (CategoryPlot) chart.getPlot();
plot.setRenderer(0, renderer);
NumberAxis numberAxis = (NumberAxis)plot.getRangeAxis();
numberAxis.setRange(new Range(0,10));
CategoryAxis axis = plot.getDomainAxis();
axis.setCategoryLabelPositions(CategoryLabelPositions.UP_45);
The problem comes when I try to insert more categories in the linear graph, I don't know how to do it. I need to do 3 categories more.
actual graph
I need use DefaultKeyedValues or some series collection that let me use String param with double/int param. I want to make a generalized method that passes different formats of dates, and the method always draw a graph. In the photo of the example I am passing a date range 'dd-MM-yyyy' but later I will pass a format 'MM-yyyy'.
Edit: Okey i find this example in internet that solves my problem.
enter link description here

Adding DateAxis to gantt Chart Javafx

I was able to make a Gantt Chart in JavaFX using this answer- Gantt chart from scratch.
Also i was able to add a DateAxis by using this-http://myjavafx.blogspot.com.by/2013/09/javafx-charts-display-date-values-on.html
But right now it is unusable, because current Gantt chart does not handle "length" as a date. So it draws the beginning of the the chart perfectly accurately, but the end of the chart can be anywhere, and if you resize the window with the chart, the end will be even more random.
I am adding new chart with
.add(new XYChart.Data(job.getTime(), machine, new ExtraData( timeLength, "status-red"))
where "timeLength" i set as number of milliseconds. But basicly that does not work, and it can only receive long.Also i cannot use JfreeChart, because i cannot add it FXML which i use.
So how can i get accurate both beginning and the end of each chart?
Thank you.
Add the following function to DateAxis class to calculate the scale factor from milliseconds to visual units.
/**
* #return The scale factor from milliseconds to visual units
*/
public double getScale(){
final double length = getSide().isHorizontal() ? getWidth() : getHeight();
// Get the difference between the max and min date.
double diff = currentUpperBound.get() - currentLowerBound.get();
// Get the actual range of the visible area.
// The minimal date should start at the zero position, that's why we subtract it.
double range = length - getZeroPosition();
return length/diff;
}
Test results
Date startDate=new Date();
long duration = 1000*60*1;//1 hour in milliseconds
series1.getData().add(new XYChart.Data(startDate, machine, new ExtraData(duration, "status-green")));
startDate = new Date(startDate.getTime()+duration);
duration = 1000*60*1;//2 hours in milliseconds
series1.getData().add(new XYChart.Data(startDate, machine, new ExtraData(duration, "status-red")));
screenshot 1

Creating an area graph below a XYDifference(Renderer) graph

I have been trying for the last week to find a way to make JFreeChart display something similar to the image below. Basically you are looking at three series (upper, middle, lower) with a fill inbetween. And underneath there is a (light green) fill color, or an area chart as some would perhaps call it - no meaning, just for looks.
The only thing really missing from what I have come up with is the last part: the fill underneath / area chart:
I even tried to subclass XYDifferenceRenderer and combine it with the renderer for Areachart, but I could not control the height of the areachart, basically filling up the plot to the top. So that was a no-go. Having created as simple rendererer to create rounded bar charts earlier, I thought that I might be able to change the code for XYDifferenceRenderer. But the code for XYDifferenceRenderer is quite a handful of geometry and inner workings of JFree chart, and the task was a bit overwhelming. So any tips on how to achieve this effect in any "normal" way (that does not involve hacking JFreeChart's inner workings)?
Found an old post describing how to use two renderers in the same plot, which was just the thing in this case.
To get a fill underneath you need to
create two new series
one is the lower bound of the difference plot
the other is the values at the bottom of the plot - often just zero. Easily got by calling plot.getRangeAxis().getLowerBound()
add them to a new dataset and add this to the plot
I was unaware that a plot could have several datasets. Turns out one can just use an index to access them.
create a new renderer for the "fill" dataset
create a new renderer
set the right fill paint
set the rendererer for the new dataset to be the new renderer
The code is something akin to the following, where the fill Paint obviously is up to you:
static void addFill(Plot plot) {
XYSeries lowerLimitSeries = ((XYSeriesCollection) (plot.getDataset())).getSeries(1);
XYSeriesCollection fillSet = new XYSeriesCollection();
double lowerBound = plot.getRangeAxis().getLowerBound();
fillSet.addSeries(lowerLimitSeries);
fillSet.addSeries(createLowerFillSeries(lowerLimitSeries, lowerBound));
plot.setDataset(1, fillSet);
Paint fillPaint = Color.GREEN;
XYDifferenceRenderer fillRenderer = new XYDifferenceRenderer(fillPaint, fillPaint, false);
fillRenderer.setSeriesStroke(0, new BasicStroke(0)); //do not show
fillRenderer.setSeriesStroke(1, new BasicStroke(0)); //do not show
plot.setRenderer(1, fillRenderer);
...
}
static XYSeries createLowerFillSeries(XYSeries lowerLimitSeries, double lowerLimit) {
int size = lowerLimitSeries.getItems().size();
XYSeries res = new XYSeries("lowerFillSeries");
for (int i = 0; i < size; i++) res.add(new XYDataItem(lowerLimitSeries.getX(i), lowerLimit));
return res;
}

How to construct and use TimeSeriesCollections

I want to display some dates in the X axis of a chart, and here it is said that i have to use a TimeSeriesCollections object
It seems that i have to add a TimeSeries to the TimeSeriesCollections, and that the TimeSeries has to be constructed using a RegularTimePeriod...
I am a bit confused...
Can you please explain me what i have to do?
If possible can you provide some example code?
Thanks
TimeSeriesCollections are made up of TimeSeries objects
Use this method to add series to the dataset: addSeries(TimeSeries series)
When creating TimeSeries objects. Fill them with the time and values. Here is a rough example:
TimeSeries ts= new TimeSeries("Name of Series");
ts.addOrUpdate(new Year(2008), 42);
ts.addOrUpdate(new Year(2009), 51);
ts.addOrUpdate(new Year(2010), 97);
ts.addOrUpdate(new Year(2011), 45);
For getting the Axis to display the dates nicely, you will have to do something like this:
XYPlot plot = chart.getXYPlot();
DateAxis axis = new DateAxis();
plot.setDomainAxis(axis);
axis.setDateFormatOverride(new SimpleDateFormat("yyyy"));

Categories

Resources