I am using jfreechart for displaying line graph.Now, on X axis it shows value for every (x,y) pair on chart.As a result the X axis has huge amount of values getting overlapped.I want to display few values eg after every 5 units or something like that.How is this possible using Jfreechart.
Before the NumberAxis of the chart's plot is drawn, its tick marks are refreshed. The result is a List that includes a NumberTick object for each tick mark of the axis.
By overriding the function NumberAxis.refreshTicks you can control how and if the marks will be shown.
For example, in the following code I get all tick marks and iterate through them looking for TickType.MAJOR. If the value of a major tick mark is not dividable by 5, it gets replaced by a minor tick mark.
As a result, only values dividable by 5 will be shown with their text label.
XYPlot plot = (XYPlot) chart.getPlot();
NumberAxis myAxis = new NumberAxis(plot.getDomainAxis().getLabel()) {
#Override
public List refreshTicks(Graphics2D g2, AxisState state,
Rectangle2D dataArea, RectangleEdge edge) {
List allTicks = super.refreshTicks(g2, state, dataArea, edge);
List myTicks = new ArrayList();
for (Object tick : allTicks) {
NumberTick numberTick = (NumberTick) tick;
if (TickType.MAJOR.equals(numberTick.getTickType()) &&
(numberTick.getValue() % 5 != 0)) {
myTicks.add(new NumberTick(TickType.MINOR, numberTick.getValue(), "",
numberTick.getTextAnchor(), numberTick.getRotationAnchor(),
numberTick.getAngle()));
continue;
}
myTicks.add(tick);
}
return myTicks;
}
};
plot.setDomainAxis(myAxis);
Related
I have such like a live chart realized with JFreeChart. The x-axis shows the time and there are several TimeSeriesCollections in the chart. If the a value is added to the TimeSeries the charts updates and shows the value only if it is not zoomed. Is there a possibility in the zoomed mode to show automatically new values (means to keep the zoom range and jump to the right).
Thanks
Oli
I found a possible solution:
this.chart.addChangeListener((ChartChangeEvent cce) -> {
if (scrollToNewest) {
Number max = DatasetUtilities.findMaximumDomainValue(this.plot.getDataset(0));
ValueAxis va = this.plot.getDomainAxis();
Range r = va.getRange();
double curUpperBound = r.getUpperBound();
double diff = max.doubleValue() - curUpperBound;
Range newR = new Range(r.getLowerBound() + diff, r.getUpperBound() + diff);
va.setRange(newR);
}
});
I'm having a Problem constructing a combined Candlestick Chart and Line Chart (both on the same plot). For the Candlesticks I use OHLCDataset and for the "moving average" linechart I use TimeSeries. However the Linecharts get drawn at the wrong timepoint along the axis. I have printed all DateTime Elements to make shure I did not set the wrong time or date but when printed they show exactly the date times they are supposed to. In the Chart however they start 6 hours too early. I first thought this would be a Timezone Issue but I'm setting the timezone on both to EST.
Here are the code snippets that create the dataset and assign it to the XYPlot
The OHLCDataset retrieving method (time[i] is a Date Object):
public OHLCDataset getOHLCDataset(){
OHLCSeries ohlcSeries = new OHLCSeries("Candlesticks");
for(int i=0; i<close.length; i++){
ohlcSeries.add(RegularTimePeriod.createInstance(Minute.class, time[i], TimeZone.getTimeZone("EST")), open[i], max[i], min[i], close[i]);
}
OHLCSeriesCollection ohlcCollection = new OHLCSeriesCollection();
ohlcCollection.addSeries(ohlcSeries);
return ohlcCollection;
}
The TimeSeries retrieving method (time[i] is a Date Object - the same as above):
public XYDataset getAverageXYDataset(int periods, int frame){
TimeSeries x = new TimeSeries("moving average " + periods + " periods");
if(frame>60){
for(int i=periods-1; i<close.length; i++){
double sum = 0;
for(int j=i; j>i-periods; j--){
sum += close[j];
}
x.add(RegularTimePeriod.createInstance(Hour.class, time[i], TimeZone.getTimeZone("EST")), sum/periods);
}
}else{
for(int i=periods-1; i<close.length; i++){
double sum = 0;
for(int j=i; j>i-periods; j--){
sum += close[j];
}
x.add(RegularTimePeriod.createInstance(Minute.class, time[i], TimeZone.getTimeZone("EST")), sum/periods);
}
}
return new TimeSeriesCollection(x);
}
The code that adds the datasets to the plot:
OHLCDataset dataset1 = dataset.getOHLCDataset();
XYDataset smallAverageDataset = dataset.getAverageXYDataset(20, period);
XYDataset bigAverageDataset = dataset.getAverageXYDataset(50, period);
// create the jfreechart - add candlestickdataset first
String title2 = dataset.getTime()[0] + " - " + dataset.getTime()[dataset.getTime().length-1];
JFreeChart chart = createChart(dataset1, title2);
// get the xyplot and set other datasets
chart.getXYPlot().setDataset(1, smallAverageDataset);
chart.getXYPlot().setDataset(2, bigAverageDataset);
Here is the method createChart:
private static JFreeChart createChart(final OHLCDataset dataset, String title) {
DateAxis xAxis = createXAxis();
NumberAxis yAxis = createYAxis();
MyCandlestickRenderer candlestickRenderer = createCandlestickRenderer();
plot = new XYPlot(dataset, xAxis, yAxis, candlestickRenderer);
JFreeChart chart = new JFreeChart(
title,
new Font("SansSerif", Font.BOLD, 24),
plot,
false
);
return chart;
}
And here is the createXAxis method:
private static DateAxis createXAxis(){
DateAxis domainAxis = new DateAxis();
domainAxis.setAutoRange(true);
domainAxis.setTickLabelsVisible(true);
domainAxis.setAutoTickUnitSelection(true);
return domainAxis;
}
I cannot figure out why there is such an offset on the linecharts but as you see I set the same Timezones for all Datasets.
Thanks in advance for the Help.
In JFreeChart's TimeSeries class, the x-values are time periods rather than specific points in time. The TimeSeriesCollection class presents this data via the XYDataset interface and has to choose a specific x-value for each data item. The setXPosition method (in TimeSeriesCollection) sets a flag that determines which point in the time period is used (start, middle or end).
I am using Jfreechart API 1.0.8 to generate the TimeSeriesChart(line chart).
when I am generating the chart, i am facing the problem of overlapping.
Here I am trying to display the rendered points(graph rendered points), by using XYLineAndShapeRenderer with StandardXYItemLabelGenerator.
When the points are displayed, the data-point is overlapping with the generated line chart (graph).
I am taking X-Axis as time, and y-Axis as revenue of organization, and i am using line chart here.
I'm displaying the points as discussed below.
By using "renderer.setBasePositiveItemLabelPosition" method, globally i am setting the position of graph points(data points) inside the xyplot rendered chart while considering the rendered "ItemLabelAnchor".
I am sending my sample code here:
chart = ChartFactory.createTimeSeriesChart("", "", "", newxyseries, false, true, false);
renderer = new XYLineAndShapeRenderer();
renderer = (XYLineAndShapeRenderer) chart.getXYPlot().getRenderer();
renderer.setBaseItemLabelGenerator(new StandardXYItemLabelGenerator("{2}", monthDate, formatSymbol));
renderer.setBaseItemLabelsVisible(true);
renderer.setBasePositiveItemLabelPosition(new ItemLabelPosition(ItemLabelAnchor.OUTSIDE3, TextAnchor.TOP_RIGHT));
chart.getXYPlot().setRenderer(renderer);
But when I am generating the graph chart using Ms-office of Excel tools, there is no problem of overlapping of labels, the points are displayed in an effective manner without any overlapping.
JFreeChart doesn't do any overlap detection for item labels. It would be a great feature to have, but nobody has written the code to do it.
you have to refrain the labels to get overlap by define your own algo or logic because as the graph keep growing sooner the labels will start to get overlap.
the key is to make some or alternative labels transparent so that no overlapping occur
For example in the following P.O.C, only 35 labels will be drawn but the tick of graphs will remai same just the labels will reduce
CategoryAxis domainAxis = plot.getDomainAxis();
CategoryLabelPositions pos = domainAxis.getCategoryLabelPositions();
double size = plot.getCategories().size();
double occurence = Math.ceil(plot.getCategories().size() / 20);
double interval = Math.ceil(plot.getCategories().size() / 20);
for (int i = 1; i <= plot.getCategories().size(); i++) {
if (plot.getCategories().size() > 35) {
if(plot.getCategories().size()>35 && plot.getCategories().size()<250) {
if(i%8==0){
String cat_Name = (String) plot.getCategories().get(i-1);
} else{
String cat_Names = (String) plot.getCategories().get(i-1);
domainAxis.setTickLabelPaint(cat_Names, new Color(0,0,0,0));
}
}else if(plot.getCategories().size()>250){
[![enter image description here][1]][1]
if (i == occurence) {
String cat_Name = (String) plot.getCategories().get(i - 1);
if (occurence + interval >= size) {
// occurence=size;
} else {
occurence = occurence + interval;
}
} else {
String cat_Names = (String) plot.getCategories().get(i - 1);
domainAxis.setTickLabelPaint(cat_Names, new Color(0, 0, 0, 0));
}
}
}
Below line will make label transparent
domainAxis.setTickLabelPaint(cat_Names, new Color(0,0,0,0));
By using StandardCategoryItemLabelGenerator() Iam able to display the exact range axis value on a bar, Now my problem is I dont want to display the range axis value instead I want to display some other value of that bar How I can achieve that?
for (String IDS : idMap.keySet()) {
List<String> listValues = idMap.get(IDS);
if(listValues != null && listValues.get(1) != null) {
dataSet.setValue(Double.valueOf(listValues.get(1)), "", IDS);
}
}
JFreeChart chart = ChartFactory.createBarChart3D("", "", "Time taken ", dataSet,PlotOrientation.VERTICAL, true, true, false);
chart.setTitle(new org.jfree.chart.title.TextTitle("Time Duration",new java.awt.Font("SansSerif", java.awt.Font.BOLD, 10)));
chart.getLegend().setItemFont(new java.awt.Font("SansSerif",0,7));
chart.removeLegend();
final CategoryPlot plot = chart.getCategoryPlot();
plot.setNoDataMessage("No data available");
final CategoryItemRenderer renderer = plot.getRenderer();
renderer.setItemLabelsVisible(true);
final BarRenderer r = (BarRenderer) renderer;
r.setMaximumBarWidth(0.05);
r.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator());
r.setBaseItemLabelsVisible(true);
r.setSeriesItemLabelFont(0, new java.awt.Font("Times New Roman",Font.NORMAL,7));
r.setSeriesPositiveItemLabelPosition(0,
new ItemLabelPosition(ItemLabelAnchor.OUTSIDE3, TextAnchor.BOTTOM_LEFT, TextAnchor.BOTTOM_LEFT, -Math.PI/2));
final ValueAxis rangeAxis = plot.getRangeAxis();
rangeAxis.setLabelFont(new java.awt.Font("SansSerif", java.awt.Font.BOLD, 8));
final CategoryAxis axis = plot.getDomainAxis();
axis.setLabel("ids");
axis.setLabelFont(new java.awt.Font("SansSerif", java.awt.Font.BOLD, 8));
axis.setCategoryLabelPositions(CategoryLabelPositions.createUpRotationLabelPositions(Math.PI / 8.0));
axis.setTickLabelFont(new java.awt.Font("SansSerif", java.awt.Font.BOLD, 6));
plot.setDomainGridlinesVisible(true);
plot.setRangeGridlinesVisible(true);
By using the above code iam able to display exact range axis value on a bar, but I dont want the range axis value I need some other value listValues.get(2) to be displayed on a bar How I can achieve that?
StandardCategoryItemLabelGenerator uses an instance of MessgeFormat for this, defaulting to ArgumentIndex {2}. The other ArgumentIndex values are defined in the abstract parent. For example,
renderer.setBaseItemLabelGenerator(
new StandardCategoryItemLabelGenerator(
"{0} {1} {2} {3}", NumberFormat.getInstance()));
renderer.setBaseItemLabelsVisible(true);
You can also override generateLabel() to return any value you want.
renderer.setBaseItemLabelGenerator(
new StandardCategoryItemLabelGenerator()
#Override
public String generateLabel(CategoryDataset dataset, int row, int column) {
return "Your Text" +row+","+column;
}
);
I have been trying to get current Mouse Co-ordinates in a JfreeChart and found that the following solution was working partially
JFreeChart get mouse coordinates
I have been using OHLC Dataset to draw the chart and while I could get the RangeAxis properly (meaning in the subplot values), I couldn't make anything out of the value recieved for the X-Axis from the above example.
I am sure that I am receiving the values in some other format (not the displayed date format), anyone could point out what I am doing wrong?
Got it solved after few hours of experiment. Here is the code for a complete MouseMotionListener. Just added this to the chartPanel and voila! - it works!
The chartY returns proper value of Y-Axis and the dateString returns complete date. Tried it in an OHLC Chart and seems proper.
MouseMotionListener mouselisten = new MouseMotionListener() {
public void mouseDragged(MouseEvent e) {
//
}
public void mouseMoved(MouseEvent e) {
Point2D p = e.getPoint();
Rectangle2D plotArea = chartPanel.getScreenDataArea();
XYPlot plot = (XYPlot) chart.getPlot(); // your plot
double chartX = plot.getDomainAxis().java2DToValue(p.getX(), plotArea, plot.getDomainAxisEdge());
double chartY = plot.getRangeAxis().java2DToValue(p.getY(), plotArea, plot.getRangeAxisEdge());
DecimalFormat dfT = new DecimalFormat("00");
GregorianCalendar gc = new GregorianCalendar();
long lDte = (long)chartX;
Date dtXX = new Date(lDte);
gc.setTime(dtXX);
String sDD = dfT.format(Double.valueOf(String.valueOf(gc.get(GregorianCalendar.DAY_OF_MONTH))));
String sMM = dfT.format(Double.valueOf(String.valueOf(gc.get(GregorianCalendar.MONTH)+1)));
String sYY = dfT.format(Double.valueOf(String.valueOf(gc.get(GregorianCalendar.YEAR))));
String dateString = sDD +"/"+ sMM +"/"+ sYY;
}
};