I have a JFreeChart instance that displays process memory status, initialized as follows:
m_data = new TimeSeriesCollection();
TimeSeries vmsize = new TimeSeries("VMSize");
TimeSeries resident = new TimeSeries("Resisdent");
TimeSeries shared = new TimeSeries("Shared memory");
TimeSeries code = new TimeSeries("Code");
TimeSeries data = new TimeSeries("Data");
m_data.addSeries(vmsize);
m_data.addSeries(resident);
m_data.addSeries(shared);
m_data.addSeries(code);
m_data.addSeries(data);
JFreeChart chart = ChartFactory.createTimeSeriesChart("Memory usage", "Time", "Size", m_data, true, true, false);
m_chart = new ChartPanel(chart);
Later I add values to each TimeSeries in the TimeSeriesCollection. I would like to somehow know - when the user clicks on the Chart - either what time associated with that columm, or even better - what is the index of the value.
I looked at the JFreeChart and ChartMouseListener classes, but I could not figure out how to do that (also the documentation of JFreeChart is annoyingly scarce, I guess they are trying to get people to buy their developer's guide).
if you click dead on the item, the event.getEntity() function returns XYItem and then from there onwards
XYItemEntity xyitem=(XYItemEntity) event.getEntity(); // get clicked entity
XYDataset dataset = (XYDataset)xyitem.getDataset(); // get data set
System.out.println(xyitem.getItem()+" item of "+xyitem.getSeriesIndex()+"series");
System.out.println(dataset.getXValue(xyitem.getSeriesIndex(), xyitem.getItem()));
System.out.println(dataset.getYValue(xyitem.getSeriesIndex(), xyitem.getItem()));
Comparable comparable=dataset.getSeriesKey(0);
XYPlot xyplot = (XYPlot) event.getChart().getPlot();
System.out.println(xyplot.getRangeCrosshairValue());
however incase you do not click on the item itself but your crosshair is set to auto lock on data, in such case the crosshair will move to nearest item but since the item has not been clicked, you will not be able to get the XYItem and hence you cannot know the series and item index, to solve this problem there is this code below, it should be put in the catch clause while the above mentioned code should be in try clause
first define a function which will take crosshair value at domain and range and also Xydataset, this functions returns an inner class object that groups item index and series index
public static SeriesAndItemIndex getItemIndex(double domainVal,double rangeVal,XYDataset xydataset){
Comparable comparable;
int indexOf;
for(int i=0;i<xydataset.getSeriesCount();i++){
comparable = xydataset.getSeriesKey(i);
indexOf=xydataset.indexOf(comparable);
for(int j=0 ; j<xydataset.getItemCount(indexOf);j++){
double x=xydataset.getXValue(indexOf, j);
double y=xydataset.getYValue(indexOf, j);
if(x == domainVal && y==rangeVal){
return new SeriesAndItemIndex(j,indexOf);//return item index and series index
}
}
}
return null;
}
private static class SeriesAndItemIndex{ ///inner CLASS to group series and item clicked index
public int itemIndex;
public int seriesIndex;
public SeriesAndItemIndex(int i,int s){
itemIndex=i;
seriesIndex=s;
}
#Override
public String toString(){
return "itemIndex="+itemIndex+",seriesIndex="+seriesIndex;
}
}
how to use it?
try{......code block from the top
}catch(Exception e){
Object source=event.getSource();
JFreeChart chartpanel=(JFreeChart)source;
XYPlot xyplot = (XYPlot) chartpanel.getPlot();
XYDataset xydataset= xyplot.getDataset();
double d=xyplot.getDomainCrosshairValue(); //get crosshair X value
double r =xyplot.getRangeCrosshairValue(); //get crosshair y value
SeriesAndItemIndex index=getItemIndex(d,r,xydataset);
if(index != null){
System.out.println(index.toString());
}
}
hmm should work, if you replace the last two lines by something like this:
ChartPanel panel=new ChartPanel(ChartFactory.createTimeSeriesChart("Memory usage", "Time", "Size", m_data, true, true, false)));
panel.addChartMouseListener(new ChartMouseListener(){
void chartMouseClicked(ChartMouseEvent e){
[...do something on click...]
}
void chartMouseMoved(ChartMouseEvent e){
[...do something on move...]
}
});
return panel;
Related
I'm using JFreeChart to graph some data, and I've set it up so that the graph shows my standard error for each point as such:
The label shows the Y value for each point, but I'd like to be able to show the Y value for the standard errors as well. Furthermore, is there a way to make it so this data only shows up if hovered over with the mouse?
This is the code I use to add both the error and the labels:
XYErrorRenderer renderer = new XYErrorRenderer();
renderer.setBaseLinesVisible(true);
renderer.setBaseShapesVisible(true);
renderer.setBaseItemLabelGenerator(new StandardXYItemLabelGenerator("{2}",
NumberFormat.getNumberInstance(),NumberFormat.getNumberInstance()));
renderer.setBaseItemLabelsVisible(true);
chart.getXYPlot().setRenderer(renderer);
Thanks in advance.
XYErrorRenderer inherits its implementation of drawItemLabel() from the abstract parent, which knows nothing about the error bars. You'll need to override drawItem() in a custom renderer subclass to draw the extra labels. The source for drawItemLabel() may serve as a guide.
Addendum: A less ambitious alternative would be to display the error range in a tooltip. The custom StandardXYToolTipGenerator below specifies a custom format string and overrides createItemArray() to supply the relevant y values from the dataset. As your XYDataset is a YIntervalSeriesCollection, you can cast it as shown below.
renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator(
"{0}: {1}…{2}", NumberFormat.getInstance(), NumberFormat.getInstance()) {
#Override
protected Object[] createItemArray(XYDataset data, int series, int item) {
YIntervalSeriesCollection d = (YIntervalSeriesCollection) data;
Object[] result = new Object[3];
double y = d.getYValue(series, item);
result[0] = getYFormat().format(y);
double min = d.getStartYValue(series, item);
result[1] = getYFormat().format(min);
double max = d.getEndYValue(series, item);
result[2] = getYFormat().format(max);
return result;
}
});
I have to implement an histogram using JFreeChart API. This histogram has to represent the datas of this JTable:
So I have a JTable with three columns: "thea", "type", "Number of occurrences". My histogram has two targets: the first is to count the number of occurrences of each thea field; the second is to mark with different colors the bars corresponding to JTable records with different types.
To implement my histogram I used a DefaultCategoryDataset:
private DefaultCategoryDataset createDataset(ArrayList<String>fieldsOccs) {
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
for(int i = 0; i<this.fieldsOccs.size() && i<end; i++) {
String thea = fieldsOccs.get(i).getFieldName();
String type = fieldsOccs.get(i).getType();
int occurrences = fieldsOccs.get(i).getOccurrences();
dataset.setValue(occurrences, type, thea);
}
return dataset;
}
Anf then I create my chart using a createChart method:
private JFreeChart createChart(DefaultCategoryDataset dataset) {
JFreeChart chart = ChartFactory.createBarChart(
"",
"", //X-axis title
"", //Y-axis title
dataset, //dataset
PlotOrientation.HORIZONTAL, //plot orientation
true, //show legends
true, //use tooltips
false //generate URLs
);
return chart;
}
This is what I get:
As you can see in the picture it is not nice to see. The values on x axes are not formatted correctly.
How can I solve this rendering problem?
--edit
I have this problem just in case of more types in the JTable. For example if my JTable is:
and there is just String, the correspondig histogram is nice:
--edit1
What dou you think about StackedBarChart3D? I get this output:
My histogram has two targets:
You may get a more appealing result with ChartFactory.createHistogram() and a SimpleHistogramDataset, seen here.
To get diverse colors, override the getItemPaint() method in a custom XYBarRenderer, as suggested here.
Is there a way to get plot list added to CombinedDomainXYPlot if I don't keep any references to them?
I'd like to get there plots, work with them and possibly remove them from the compined plot.
This is example code for adding plots to CombinedDomainXYPlot:
// axis
DateAxis domainAxis = new DateAxis("Date");
// plot container
CombinedDomainXYPlot plotCombined = new CombinedDomainXYPlot(domainAxis);
// plot 1
XYPlot plot1 = new XYPlot();
plot1.setDomainAxis(domainAxis);
plotCombined.add(plot1);
// plot 2
XYPlot plot2 = new XYPlot();
plot2.setDomainAxis(domainAxis);
plotCombined.add(plot2);
Update 1:
I've just tried this code but it doesn't return all the plots. It's not reliable.
for (Object sp : plotCombined.getSubplots()) {
plotCombined.remove((XYPlot)sp);
}
It this method of removing the plots correct?
Full example code:
import javax.swing.JFrame;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.XYPlot;
public class sample27 extends JFrame {
public sample27() {
super("sample27");
// axis
DateAxis domainAxis = new DateAxis("Date");
// plot container
CombinedDomainXYPlot plotCombined = new CombinedDomainXYPlot(domainAxis);
// plot 1
XYPlot plot1 = new XYPlot();
plot1.setDomainAxis(domainAxis);
plotCombined.add(plot1);
// plot 2
XYPlot plot2 = new XYPlot();
plot2.setDomainAxis(domainAxis);
plotCombined.add(plot2);
System.out.println("plot count before: " + plotCombined.getSubplots().size());
for (Object sp : plotCombined.getSubplots()) {
System.out.println("removing subplot: " + sp);
plotCombined.remove((XYPlot)sp);
}
System.out.println("plot count after: " + plotCombined.getSubplots().size());
}
public static void main(String[] args) {
new sample27();
}
}
Output:
plot count before: 2
removing subplot: org.jfree.chart.plot.XYPlot#15615099
plot count after: 1
getSubplots returns a List containing all the items - this List is copy from the standpoint that it uses Collections.unmodifiableList, which returns a new List backed by the original. As you iterate over the List, items are in fact being removed from the underlying List, affecting the iteration over the Collection.
Rather than rely on iteration (eg for (Object sp : plotCombined.getSubplots())), loop over the array backwards and use the index to remove the item.
for ( int i = plotCombined.getSubplots().size() - 1; i >= 0; i-- ){
plotCombined.remove((XYPlot)plotCombined.getSubplots().get(i));
}
As an alternative to the approach shown here, iterate over a modifiable list constructed from the unmodifiable list returned by getSubplots().
Code:
List<XYPlot> list = new ArrayList<>(plotCombined.getSubplots());
for (XYPlot plot : list) {
plotCombined.remove(plot);
}
Console:
plot count before: 2
plot count after: 0
I am working with java, I want to draw a line chart which includes at least two lines at the same time. I can only pass one DefaultCategoryDataset each time as a parameter, and this DefaultCategoryDataset represent one line. so how can I do that?
ChartFactory.createLineChart("String", "String", "String", "DefaultCategoryDataset", "PlotOrientation.HORIZONTAL", "boolean", "boolean", "boolean");
You have to "feed" data with more than one series.
Here it is how i handle this with XYLineChart:
1) I have a private object
private XYSeriesCollection data =null;
2) It is initialised into a method and then another method that "feeds" data is called.
data = new XYSeriesCollection();
fillXYSeries();
3) Here is this method:
private void fillXYSeries(){
data.removeAllSeries();
for(int i=0;i<tables.size();i++){
final XYSeries series = new XYSeries(tables.get(i).getName());
for(int j=0;j<mostIntensiveTables.get(i).getChangesForChart().size();j++){
series.add(j,mostIntensiveTables.get(i).getChangesForChart().get(j));
}
int found=0;
for(int k=0;k<data.getSeriesCount();k++){
if(data.getSeries(k)==series){
found=1;
break;
}
}
if(found==0){
data.addSeries(series);
}
}
}
and finally i construct my chart with data object as parameter:
final JFreeChart chart = ChartFactory.createXYLineChart(
"Most Updated Tables",
"Version ID",
"Number of Changes",
data,
PlotOrientation.VERTICAL,
true,
true,
false
);
i'm using jfreechart, and i need to add labels to my series data. There are bold dots on graphic and they need labels... Following code does not work.
XYSeries series = new XYSeries("Average Size");
series.add(.60, .70);
XYDataset xyDataset = new XYSeriesCollection(series);
XYItemRenderer rend = new XYShapeRenderer();
XYItemLabelGenerator generator = new XYItemLabelGenerator() {
#Override
public String generateLabel(XYDataset xyd, int i, int i1) {
return "Some label?";
}
};
//SeriesItemLabelGenerator
rend.setBaseItemLabelGenerator(generator);
rend.setBaseItemLabelsVisible(true);
ItemLabelPosition pos = new ItemLabelPosition(ItemLabelAnchor.CENTER, TextAnchor.TOP_LEFT);
rend.setBasePositiveItemLabelPosition(pos);
I think the problem is that XYShapeRenderer(http://www.jfree.org/jfreechart/api/javadoc/src-html/org/jfree/chart/renderer/xy/XYShapeRenderer.html), which extends AbstractXYItemRenderer, does not implement ItemLabelGenerator logic yet.
So, or you will need to use another Renderer.
For example, XYLineAndShapeRenderer(http://www.jfree.org/jfreechart/api/javadoc/src-html/org/jfree/chart/renderer/xy/XYLineAndShapeRenderer.html) implements it:
// draw the item label if there is one...
if (isItemLabelVisible(series, item)) {
drawItemLabel(g2, orientation, dataset, series, item, xx, yy,(y1 < 0.0));
}
Or you will need to extend XYShapeRenderer yourself and add the label drawing logic, using any of the other Renderer's source code as an example.