xy plot of renderer label points(data points) are overlapping with line chart of TimeSeriesChart of jfreechart - jfreechart

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));

Related

Show newest inserted values in chart with TimeSeriesCollection when zoomed

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);
}
});

DynamicReport - How to plot too many data points

I am trying to create LineChartReport using more than 1000 data points. Problem is that the X Axis should show the time stamp, and since there are too many data points, the data gets overlapped and no comprehensible data is shown. So I need help on following 2 points:
1. Limit the data points on X-Axis (only) to say 25. The number of data points for the graph/chart still remains at 1000
2. Rotate the Timestamp data by 90 degrees so that the Timestamp data is recorded correctly and not truncated.
Have tried to get the domain axis and manipulate it, like this, but the library does not allow that:
CategoryAxis domainAxis = chart.getCategoryPlot().getDomainAxis();
domainAxis.setMinorTickMarksVisible(false);
domainAxis.clearCategoryLabelToolTips();
chart.getCategoryPlot().getDataset().getColumnKeys()
CategoryDataset ds = chart.getCategoryPlot().getDataset();
List ls = ds.getColumnKeys();
List ls2 = new ArrayList();
int i = 0;
for (Iterator it = ls.iterator(); it.hasNext(); ) {
it.next();
if (i % 2 != 0) {
ls2.add(ls.get(i));
}
i++;
}
chart.getCategoryPlot().setDataset(ds);
Sample image with 10 data points appear here: https://drive.google.com/drive/u/0/folders/0B-m6SCJULOTRdHZ6cUwxX041SHM
Any suggestions ??
The below codes are based on DynamicReport 4.0.2. I didn't test them in other versions.
Regarding your first question, you want 1000 points of data, and just want the few data in line chart. In this case, you need to use the different data source for you data table and line chart.
Firstly, create the subreport for the data table and set up.
SubreportBuilder subreport = cmp.subreport(
report().setTemplate(Templates.reportTemplate)
.addColumn(
col.column("Name", "name", type.stringType()),
col.column("Counts", "value", type.integerType())
)
);
JasperReportBuilder reportContent = report();
subreport.setDataSource(allDatasource);
reportContent.summary(subreport, cmp.verticalGap(20));
Secondly, prepare another data source for line chart and set up.
reportContent.setTemplate(Templates.reportTemplate)
/* add title */
.title(title, subtitle,
/* add chart in the head of title */
cmp.verticalList(LINE_CHART)
/* set style */
.setStyle(stl.style().setBottomPadding(30).setTopPadding(30)))
/* set data source for line chart*/
.setDataSource(dataSource);
About your second question, you need to create customizer at first.
public class DynamicLineCustomizer implements DRIChartCustomizer, Serializable {
private static final long serialVersionUID = -8493880774698206000L;
#Override
public void customize(JFreeChart jFreeChart, ReportParameters reportParameters) {
CategoryPlot plot = jFreeChart.getCategoryPlot();
CategoryAxis domainAxis = plot.getDomainAxis();
domainAxis.setCategoryLabelPositions(CategoryLabelPositions
.createUpRotationLabelPositions(Math.PI / 6.0));
}
}
Then use this customizer in line chard builder.
LineChartBuilder lineChart = cht.lineChart()
.customizers(new DynamicLineCustomizer())
.setCategory(columns[0])
.series(createSeries(columns))
.setCategoryAxisFormat(cht.axisFormat().setLabel("TimeStamp"))
.seriesColors(seriesColors);
The line chart and data table will be like below:
This finally worked for me (hope it helps somebody) :
Ref: http://www.dynamicreports.org/forum/viewtopic.php?f=1&t=1046
private void build(String startDate, String endDate) {
TextColumnBuilder<Integer> i = col.column("I", "I", type.integerType());
TextColumnBuilder<Integer> b = col.column("B", "B", type.integerType());
TextColumnBuilder<Integer> t = col.column("T", "T", type.integerType());
TextColumnBuilder<Date> timeColumn = col.column("TimeStamp", "TimeStamp", type.dateType());
createDataSource(startDate, endDate);
try {
TimeSeriesChartBuilder timeSeriesChartBuilder1 = cht.timeSeriesChart();
timeSeriesChartBuilder1.series(cht.serie(b), cht.serie(t), cht.serie(i));
timeSeriesChartBuilder1.setShowShapes(false);
timeSeriesChartBuilder1.setDataSource(dataSource);
timeSeriesChartBuilder1.setTimePeriod(timeColumn);
timeSeriesChartBuilder1.setTimePeriodType(TimePeriod.SECOND);
timeSeriesChartBuilder1.setTitle("ABC Information");
JasperReportBuilder builder = report()
.summary(cht.multiAxisChart(timeSeriesChartBuilder1))
.setTemplate(Templates.reportTemplate)
.title(Templates.createTitleComponent("ABC Complete Info"))
;
builder.show();
} catch (Exception e) {
e.printStackTrace();
}
}

Exporting TeeChart as Image

We are using TeeChart for .net (Version 4.1.2013.7302) in our forms application.
One of the charts in our product has Y Axis scrolling enabled. This makes some portion of chart visible at a given instance. To see other part of chart, user needs to use the scrollbar. A separate scrollbar is used instead of axis scrollbar, as there will be a adjoining grid control and; both chart & grid are expected to be scrolled using common scrollbar. Following is the sample form image depicting the scenario:
We are using TeeChart's export functionality to export this chart as an image. But since chart has scrolling enabled (i.e. minimum of chart is not visible by default); TeeChart is exporting only visible portion of Chart, and not the entire chart. Following is the image of chart exported:
Please suggest if there exists any way to export the entire chart as an image, and not just the visible portion of it?
Thanks in Advance.
Here's a fuller version of Yeray's code that fills out the exported image to the size of the full, mainly non-visible chart:
private void button11_Click(object sender, EventArgs e)
{
//get zoomed axis min maxes
double xtmpMin = tChart1.Axes.Bottom.Minimum;
double xtmpMax = tChart1.Axes.Bottom.Maximum;
double ytmpMin = tChart1.Axes.Left.Minimum;
double ytmpMax = tChart1.Axes.Left.Maximum;
//how many pixels are plotted for the axes' ranges
int yPixelRange = tChart1.Axes.Left.CalcPosValue(tChart1.Axes.Left.Minimum)-tChart1.Axes.Left.CalcPosValue(tChart1.Axes.Left.Maximum);
int xPixelRange = tChart1.Axes.Bottom.CalcPosValue(tChart1.Axes.Bottom.Maximum) - tChart1.Axes.Bottom.CalcPosValue(tChart1.Axes.Bottom.Minimum);
//get the chart header/footer space to re-apply to chart
int yMargins = tChart1.Bounds.Height - yPixelRange;
int xMargins = tChart1.Bounds.Width - xPixelRange;
//how many pixels are we getting per axis scale
double pixelsPerYAxisInt = yPixelRange / (ytmpMax - ytmpMin);
double pixelsPerXAxisInt = xPixelRange / (xtmpMax - xtmpMin);
//what increment are we at. Note. To get this back we may need to mod font size, min separation
double yInc = tChart1.Axes.Left.CalcIncrement;
double xInc = tChart1.Axes.Bottom.CalcIncrement;
//now reset auto axes before plotting full chart. Could use other criteria here
tChart1.Axes.Left.Automatic = true;
tChart1.Axes.Bottom.Automatic = true;
//Repaint full Chart (necessary for positioning calcs)
tChart1.Draw();
//set increments on full scales (note Chart will try to set them,
//but if it can't you have the last word with label separation, font size, etc)
tChart1.Axes.Left.Increment = yInc;
tChart1.Axes.Bottom.Increment = xInc;
//dimension chart for export
double fullYRange = tChart1.Axes.Left.Maximum - tChart1.Axes.Left.Minimum;
double fullXRange = tChart1.Axes.Bottom.Maximum - tChart1.Axes.Bottom.Minimum;
int fullYSize = (int)((pixelsPerYAxisInt * fullYRange) + yMargins);
int fullXSize = (int)((pixelsPerXAxisInt * fullXRange) + xMargins);
//setup and export image
tChart1.Export.Image.PNG.Width = fullXSize;
tChart1.Export.Image.PNG.Height = fullYSize;
tChart1.Export.Image.PNG.Save(#"c:\mypath\chart.png");
//reset screen chart to where it was
tChart1.Axes.Bottom.SetMinMax(xtmpMin, xtmpMax);
tChart1.Axes.Left.SetMinMax(ytmpMin, ytmpMax);
}
There are many ways to optimise that code, Axis does have an iRange that I haven't tried, and some of the steps can be brought together but I hope they are clear and useful and give you something of what you're looking for.
You can manually adjust your axis scale, export your chart and then restore your axis. Ie (if 0 - 4.25 in the bottom axis is the "entire chart"):
double tmpMin = tChart1.Axes.Bottom.Minimum;
double tmpMax = tChart1.Axes.Bottom.Maximum;
tChart1.Axes.Bottom.SetMinMax(0, 4.25);
tChart1.Export.Image.JPEG.Save(myFileName);
tChart1.Axes.Bottom.SetMinMax(tmpMin, tmpMax);

how to display customized values on a bar in bar chart using jfree chart?

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;
}
);

JFreechart: Displaying X axis with values after specific units

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);

Resources