I have requirement to show a XyLineChart with adding data dynamically. I have used the chart Customizer to read data from db with some additional logic and adding that to chart. But I am not able to create tool tip on mouse over for each data points on chart. following is my code for Customizer.
What is the correct way to create Tool tip on Mouse over?
public class MyChartCustomizer extends JRAbstractChartCustomizer{
#Override
public void customize(JFreeChart chart, JRChart jrChart) {
XyPlot plot= chart.getXyPlot;
XYSeriesCollection ds = (XYSeriesCollection) plot.getDataset();
XYSeries x1 = new XYSeries("C 1", true, true);
x1.add(10,20);
XYBarRenderer ren = (XYBarRenderer) plot.getRenderer();
plot.setRenderer(ren);
ren.setSeriesToolTipGenerator(0, new XYToolTipGenerator() {
#Override
public String generateToolTip(XYDataset arg0, int arg1, int arg2) {
return "C 1";
}
});
ren.setToolTipGenerator(new XYToolTipGenerator() {
#Override
public String generateToolTip(XYDataset arg0, int arg1, int arg2) {
return "C 1";
}
});
chart.fireChartChanged();
}
}
}
Thank you, Petter and Trashgod helping me out to find the solution for this issue.
The actual issue is, if we add the new data using customizer then the new data point get display on the graph but respected tool tip does not get generated and the map used for tooltip will not be updated on html code. Since I have to use jasper server as per requirement I implemented following work around other way will be simply generate chart image with map using jfreechart API and display on the jsp page(no need for report design....)..
Following is the way I Implemented this.
I used the following code to generate the same chart which jasperserver/jasper studio create the chart. this gives me same chart which internally get created and I create the map for tooltips and passing it as parameter to browser and using javascript function inserting the new map html code with the chart image.
XYSeriesCollection xyDataSet = new XYSeriesCollection();
JFreeChart chart = ChartFactory.createXYLineChart(
cur_chart.getTitle(),
cur_chart.getxLabel(), cur_chart.getyLabel(),
xyDataSet,
PlotOrientation.VERTICAL,
true,
true,
false);
String chartId = null;
for ( Object tt : chart.getSubtitles()){
if (tt instanceof TextTitle){
chartId= ((TextTitle) tt).getText();
}
}
XYPlot plot = chart.getXYPlot();
//following code to set font size and color is required so that same chart with matching tooltip pixels can we generated.
LegendItemCollection legends = plot.getLegendItems();
List<JRSeriesColor> colors = new ArrayList<JRSeriesColor>();
System.out.println("Customizer: "+ chartId);
NumberAxis xAxis = (NumberAxis) plot.getDomainAxis();
NumberAxis yAxis = (NumberAxis) plot.getRangeAxis();
xAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
yAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
Color trans = new Color(0xFF, 0xFF, 0xFF, 0);
chart.setBackgroundPaint(trans);
plot .setBackgroundPaint(trans);
chart.getLegend().setBackgroundPaint(trans);
chart.setTitle(cur_chart.getTitle());
Font font3 = new Font("Dialog", Font.PLAIN, 10);
plot.getDomainAxis().setLabelFont(font3);
plot.getRangeAxis().setLabelFont(font3);
plot.getDomainAxis().setLabelPaint(Color.BLACK);
plot.getRangeAxis().setLabelPaint(Color.BLACK);
//some more code to add real time data to XyDataset,
ToolTipTagFragmentGenerator tooltipConstructor = new ToolTipTagFragmentGenerator() {
public String generateToolTipFragment(String arg0) {
String toolTip = " title = \"" + arg0.replace("\"", "") + "\"";
return (toolTip);
}
};
URLTagFragmentGenerator urlConstructor = new URLTagFragmentGenerator() {
public String generateURLFragment(String arg0) {
String address = " href=\"ControllerAddress\\methodName?"
+ arg0.replace("\"", "") + "\"";
return (address);
}
};
ChartRenderingInfo info = new ChartRenderingInfo(
new StandardEntityCollection());
// BufferedImage bi chart.createBufferedImage(272, 178, info);
TextTitle tt = new TextTitle("chart1");
tt.setFont(font3);
chart.addSubtitle(tt);
ChartUtilities.saveChartAsPNG(new File("/tmp/test.png"), chart, 500, 250, info);
String map = ChartUtilities.getImageMap(cur_chart.getName(), info, tooltipConstructor, urlConstructor);
I'm using JFreeChart to show a stacked line chart of two sets of data over time, in this example dogs and cats.
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.swing.JFrame;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StackedXYAreaRenderer;
import org.jfree.data.time.Minute;
import org.jfree.data.time.TimeTableXYDataset;
public class ChartTest {
public ChartTest() throws ParseException{
TimeTableXYDataset chartData = createChartData();
JFreeChart chart = createChart(chartData);
ChartPanel chartPanel = new ChartPanel(chart);
JFrame frame = new JFrame("Chart Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(chartPanel);
frame.setSize(500, 500);
frame.setVisible(true);
}
private TimeTableXYDataset createChartData() throws ParseException {
int[] dogs = {14, 81, 99, 89, 151, 263, 396, 548, 822, 1410, 2180, 3134, 4065, 5016, 6019, 7648, 9323, 11059, 12252, 13432, 15238, 17559, 19796, 21853, 23971, 26414, 28694, 31371, 34233, 37353, 40451, 44081, 47978, 52040, 56024, 60486, 64881, 69663, 74320, 79391, 84840, 91228, 96383, 102061, 107832, 114244, 119992, 126207, 132894, 139146, 144727, 150896, 156503, 161960, 167724, 174172, 180121, 185929, 191375, 196050, 200768, 205208, 208727, 212329, 216439, 221102, 224284, 226944, 230307, 233075, 234814, 236220, 237733, 239158, 240311, 241267};
int[] cats = {244, 360, 363, 644, 1075, 1516, 2241, 3160, 3591, 4661, 5633, 6990, 7889, 9059, 10510, 11743, 12506, 13540, 14557, 15705, 16969, 18350, 20197, 21659, 23160, 24840, 26394, 28109, 29742, 31428, 33021, 34514, 35822, 37339, 38784, 40258, 41568, 42921, 44180, 45454, 46710, 48084, 49418, 50712, 51920, 53014, 53923, 54830, 55756, 56573, 57554, 58352, 59064, 59874, 60933, 61948, 62762, 63299, 63772, 64243, 64789, 65206, 65693, 66016, 66391, 66859, 67432, 67919, 68400, 68677, 68944, 69211, 69511, 69786, 69990, 70279};
final TimeTableXYDataset chartData = new TimeTableXYDataset();
long start = new SimpleDateFormat("MM/dd/yyyy HH:mm").parse("11/08/2016 08:00").getTime();
for (int t = 0; t < dogs.length; t++) {
Minute m = new Minute(new Date(start + 15*t*60*1000));
chartData.add(m, dogs[t], "Dogs");
chartData.add(m, cats[t], "Cats");
}
return chartData;
}
private JFreeChart createChart(TimeTableXYDataset chartData) {
JFreeChart chart = ChartFactory.createStackedXYAreaChart("Dogs and Cats", "Time", "Count", chartData, PlotOrientation.VERTICAL, false, true, false);
StackedXYAreaRenderer chartRenderer = new StackedXYAreaRenderer();
XYPlot plot = (XYPlot)chart.getPlot();
plot.setRenderer(chartRenderer);
DateAxis dateAxis = new DateAxis();
dateAxis.setDateFormatOverride(new SimpleDateFormat("HH:mm"));
dateAxis.setTickLabelFont(dateAxis.getTickLabelFont().deriveFont(20f));
plot.setDomainAxis(dateAxis);
return chart;
}
public static void main(String... args) throws ParseException{
new ChartTest();
}
}
However, this results in a "crimp" in the cats section of the chart:
I've looked my data over, and it doesn't contain negative values or anything strange that might throw the chart off.
Through some shotgun debugging, I realized that if I remove this section of code from the createChart() function:
StackedXYAreaRenderer chartRenderer = new StackedXYAreaRenderer();
XYPlot plot = (XYPlot)chart.getPlot();
plot.setRenderer(chartRenderer);
DateAxis dateAxis = new DateAxis();
dateAxis.setDateFormatOverride(new SimpleDateFormat("HH:mm"));
dateAxis.setTickLabelFont(dateAxis.getTickLabelFont().deriveFont(20f));
plot.setDomainAxis(dateAxis);
Then I get a more reasonable stacked line chart:
But then I lose my nicely formatted dates.
My questions are:
What is causing the "crimp" in the cats section of the chart, and the space between the two stacks?
Is there a different way to format the dates that doesn't cause this behavior?
ChartFactory.createStackedXYAreaChart() instantiates StackedXYAreaRenderer2 to avoid this problem. Your example replaces it with an instance of StackedXYAreaRenderer. Either,
Use the factory's renderer and a custom DateAxis.
private JFreeChart createChart(TimeTableXYDataset chartData) {
JFreeChart chart = ChartFactory.createStackedXYAreaChart(
"Dogs and Cats", "Time", "Count", chartData,
PlotOrientation.VERTICAL, false, true, false);
DateAxis dateAxis = new DateAxis();
dateAxis.setDateFormatOverride(new SimpleDateFormat("HH:mm"));
dateAxis.setTickLabelFont(dateAxis.getTickLabelFont().deriveFont(20f));
XYPlot plot = (XYPlot) chart.getPlot();
plot.setDomainAxis(dateAxis);
return chart;
}
Recapitulate the factory, as shown here, in your createChart() method.
private JFreeChart createChart(TimeTableXYDataset chartData) {
DateAxis dateAxis = new DateAxis("Time");
dateAxis.setDateFormatOverride(new SimpleDateFormat("HH:mm"));
dateAxis.setTickLabelFont(dateAxis.getTickLabelFont().deriveFont(20f));
NumberAxis yAxis = new NumberAxis("Count");
XYToolTipGenerator toolTipGenerator = new StandardXYToolTipGenerator();
StackedXYAreaRenderer2 renderer = new StackedXYAreaRenderer2(
toolTipGenerator, null);
renderer.setOutline(true);
XYPlot plot = new XYPlot(chartData, dateAxis, yAxis, renderer);
plot.setOrientation(PlotOrientation.VERTICAL);
plot.setRangeAxis(yAxis); // forces recalculation of the axis range
JFreeChart chart = new JFreeChart("Dogs and Cats",
JFreeChart.DEFAULT_TITLE_FONT, plot, false);
new StandardChartTheme("JFree").apply(chart);
return chart;
}
Can you expand a little bit on why the StackedXYRenderer causes that crimp?
The author writes, "StackedXYAreaRenderer2 uses a different drawing approach, calculating a polygon for each data point and filling that." In contrast, StackedXYAreaRenderer appears to close a single Shape by connecting the endpoints with a straight line.
I have generated a few charts, as per an assignment, and for data analysis I would like the domain axis to scale to my specifications, rather than to automatically fit the data in each series.
I found a solution here, which seemed very simple to follow: JFReeChart x axis scale
However, after I create my plot, with the code below, it seems like the method does not exist.
Is the method setDomainAxis() one that is included in the libraries or do I have to create it myself? Or have I violated some fundamental rule of java that has nothing to do with JFreeChart? I am a beginner java programmer, so please inform your answer accordingly. Thanks!
Here is my XYPlot class:
public class XYPlot extends JFrame {
private String title;
private String xAxis;
private String yAxis;
private XYSeriesCollection dataset;
public XYPlot(String header, String graphTitle, String xax, String yax) {
super(header);
xAxis = xax;
yAxis = yax;
title = graphTitle;
XYPlot myPlot;
// dataset = dat;
}
public void setTit(String newTitle){
title = newTitle;
}
public void setXAxis(String X){
xAxis = X;
}
public void setYAxis(String Y){
xAxis = Y;
}
public void passData(XYSeriesCollection data){
dataset = data;
}
public void createChart(XYSeriesCollection dataIn){
final JFreeChart chart = ChartFactory.createXYLineChart(
title,
xAxis,
yAxis,
dataIn
);
final ChartPanel chartPanel = new ChartPanel(chart);
chartPanel.setPreferredSize(new java.awt.Dimension(1200, 800));
setContentPane(chartPanel);
}
And here is the implementation of the plot in my main program:
public static void seriesPlotter(XYSeriesCollection dataset, String title, String header, String xAxis, String yAxis) {
XYPlot myPlot = new XYPlot(header, title, xAxis, yAxis);
myPlot.createChart(dataset);
NumberAxis domain = new NumberAxis();
domain.setTickUnit(1);
myPlot.setDomainAxis(domain);
myPlot.pack();
myPlot.setVisible(true);
}
Your fragment's use of XYPlot is inconsistent with the API. In particular, there is no such constructor, and there is no method named createChart(). Verify that you are using the current version, 1.0.19, available here.
The methods of ChartFactory are an excellent guide to creating a chart from individual subcomponents. As a concrete example, ChartFactory.createXYLineChart() is recapitulated here. Note how the axes are passed to the XYPlot constructor, obviating the need to invoke setDomainAxis() explicitly. In outline,
// axes
NumberAxis domain = new NumberAxis(xAxis);
NumberAxis range = new NumberAxis(yAxis);
// renderer
XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false);
// plot
XYPlot plot = new XYPlot(dataset, domain, range, renderer);
// chart
JFreeChart chart = new JFreeChart(
title", JFreeChart.DEFAULT_TITLE_FONT, plot, false);
Maybe you could take a second look after I edit to add the additional code.
Your XYPlot is a JFrame which has no setDomainAxis() method; rename your class to avoid the conflict, e.g. MyXYPlot.
Let your renamed class have a member variable to hold a reference to the org.jfree.chart.plot.XYPlot and add a method to update the name.
XYPlot myPlot;
…
public void setDomainAxisName(String name){
myPlot.getDomainAxis().setLabel(name);
}
There's no reason to extend JFrame.
My technical vocabulary is nil.
Experimenting with JFreeChart is an excellent opportunity to learn.
I have two sections. In the left section I am using checkbox tableviewer for displaying list of file names. The right section is for showing graphs(I am using JFreechart). I have a handler which is used for dynamically adding tabs to the right side section. IF I am in first tab and made some checkbox selections in left side tableviewer ,the graph is displayed in right side. When I create a new tab(right side) , the left side tableviewer should reset.
When I select the first tab again and I want to see the previous selection in left side section.Can anyone please give some ideas how to save/restore the views based on the tab change?
Left side section code for file viewer:
#PostConstruct
public void createComposite(Composite parent) {
parent.setLayout(new GridLayout(1, false));
tableViewer = new CheckboxTableViewer(parent, SWT.BORDER);
tableViewer.getTable().setLayoutData(new GridData(GridData.FILL_BOTH));
}
public void setTableInput(File[] selectedFiles) {
tableViewer.setContentProvider(ArrayContentProvider.getInstance());
tableViewer.setLabelProvider(new FileLabelProvider());
prevSelectedFiles = selectedFiles;
tableViewer.setInput(selectedFiles);
tableViewer.addCheckStateListener(new ICheckStateListener() {
#Override
public void checkStateChanged(CheckStateChangedEvent event) {
filesSelected = tableViewer.getCheckedElements();
// some code to display graph
}}}
Right side code(Graph)
#PostConstruct
public void postConstruct(final Composite parent) {
final JFreeChart chart = createChart(dataset, title);
new ChartComposite(parent, SWT.NONE, chart, true);
}
private JFreeChart createChart(TimeSeriesCollection dataset, String string) {
final JFreeChart chart = ChartFactory.createTimeSeriesChart(
"REPORT GENERATION", "TimeStamp", "ms", dataset, true, true,
false);
chart.setBackgroundPaint(Color.WHITE);
final XYPlot plot = (XYPlot) chart.getPlot();
plot.setDataset(0, dataset);
plot.setBackgroundPaint(Color.WHITE);
plot.setDomainGridlinePaint(Color.BLACK);
plot.setRangeGridlinePaint(Color.BLACK);
Shape shape = new Ellipse2D.Double(-2.0, -2.0, 4.0, 4.0);
XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plot
.getRenderer();
renderer.setSeriesShape(0, shape);
renderer.setSeriesShape(1, shape);
renderer.setSeriesShape(2, shape);
renderer.setBaseShapesVisible(true);
renderer.setSeriesOutlinePaint(0, Color.GRAY);
renderer.setSeriesOutlinePaint(1, Color.GRAY);
renderer.setSeriesOutlinePaint(2, Color.GRAY);
renderer.setUseFillPaint(true);
renderer.setSeriesFillPaint(0, Color.red);
renderer.setSeriesFillPaint(1, Color.green);
renderer.setSeriesFillPaint(2, Color.blue);
renderer.setSeriesPaint(0, Color.red);
renderer.setSeriesPaint(1, Color.green);
renderer.setSeriesPaint(2, Color.blue);
NumberAxis yaxis = (NumberAxis) plot.getRangeAxis();
yaxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
yaxis.setAutoRangeIncludesZero(false);
plot.setRangeAxis(yaxis);
DateAxis axis = (DateAxis) plot.getDomainAxis();
axis.setAutoTickUnitSelection(true);
// axis.setTickUnit(new DateTickUnit(DateTickUnitType.SECOND,120));
axis.setDateFormatOverride(new SimpleDateFormat("HH:mm:ss"));
// DateAxis.createStandardDateTickUnits();
axis.setTickMarksVisible(true);
axis.setTickLabelsVisible(true);
return chart;
}
public void setValue(ArrayList<TreeMap<Timestamp, Long>> statisticalValues, String protocolName, String statistics) {
// //System.out
// .println("setting the value for timeseries-->" + i);
TimeSeries ts = null;
for (TreeMap<Timestamp, Long> entries : statisticalValues) {
ts = new TimeSeries(protocolName + "_" + statistics,
Second.class);
for (Entry<Timestamp, Long> seriesData : entries.entrySet()) {
ts.addOrUpdate(new Second(seriesData.getKey()),
seriesData.getValue());
}
}
dataset.addSeries(ts);
}
The handler responsible for dynamic creation of tab(Grapgh part):
public class DynamicPartsHandler {
#Execute
public void execute(EPartService partService, EModelService modelService,
MApplication application,Shell shell) {
String partName = "Graph";
MPart part = partService
.createPart("com.wincor.commtrace.project.partDescriptor.1");
MPartStack stack = (MPartStack) modelService.find(
"com.wincor.commtrace.project.partstack.2", application);
stack.getChildren().add(part);
part.setLabel(partName);
part.setVisible(true);
part.setCloseable(true);
partService.showPart(part, PartState.ACTIVATE);
}
}
Thanks in advance
I'm working with this exemple wich put rondom dynamic data into a TimeSeriesCollection chart.
My problem is that i can't find how to :
1- Make a track of the old data (of the last hour) when they pass the left boundary (because the data points move from the right to the left ) of the view area just by implementing a horizontal scroll bar.
2- Is XML a good choice to save my data into when i want to have all the history of the data?
public class DynamicDataDemo extends ApplicationFrame {
/** The time series data. */
private TimeSeries series;
/** The most recent value added. */
private double lastValue = 100.0;
public DynamicDataDemo(final String title) {
super(title);
this.series = new TimeSeries("Random Data", Millisecond.class);
final TimeSeriesCollection dataset = new TimeSeriesCollection(this.series);
final JFreeChart chart = createChart(dataset);
final ChartPanel chartPanel = new ChartPanel(chart);
final JPanel content = new JPanel(new BorderLayout());
content.add(chartPanel);
chartPanel.setPreferredSize(new java.awt.Dimension(500, 270));
setContentPane(content);
}
private JFreeChart createChart(final XYDataset dataset) {
final JFreeChart result = ChartFactory.createTimeSeriesChart(
"Dynamic Data Demo",
"Time",
"Value",
dataset,
true,
true,
false
);
final XYPlot plot = result.getXYPlot();
ValueAxis axis = plot.getDomainAxis();
axis.setAutoRange(true);
axis.setFixedAutoRange(60000.0); // 60 seconds
axis = plot.getRangeAxis();
axis.setRange(0.0, 200.0);
return result;
}
public void go() {
final double factor = 0.90 + 0.2 * Math.random();
this.lastValue = this.lastValue * factor;
final Millisecond now = new Millisecond();
System.out.println("Now = " + now.toString());
this.series.add(new Millisecond(), this.lastValue);
}
public static void main(final String[] args) throws InterruptedException {
final DynamicDataDemo demo = new DynamicDataDemo("Dynamic Data Demo");
demo.pack();
RefineryUtilities.centerFrameOnScreen(demo);
demo.setVisible(true);
while(true){
demo.go();
Thread.currentThread().sleep(1000);
}
}
}
The example uses the default values specified in TimeSeries for the maximum item age and count. You'll want to change them to suit your requirements.
XML is fine, but it's voluminous for high rates; plan accordingly.
See also this example that uses javax.swing.Timer to avoid blocking the event dispatch thread.