I'm currently trying to use JFreeChart to represent 3D data in a 2D graph.
Essentially, I have a 2d array called data[i][j]. The i and j represent the y and x coordinates where I want to plot. The value of data[i][j] represents a frequency value, which I want to represent in the graph as a colour.
I'm not entirely sure what something like this is called, but it would look something like this:
Now I have been trying to do this using XYBlockRenderer, however I am having issues with defining the dataset. I am trying to use DefaultXYZDataset, but I'm really confused at how to even define the data here.
Can someone explain how to use the DefaultXYZDataset to accomplish such a task?
DefaultXYZDataset dataset = new DefaultXYZDataset();
Concentration.dataoutHeight = Concentration.dataout[0].length;
System.out.println(Concentration.dataoutHeight);
System.out.println(ImageProcessor.MAXCBVINT);
double[][] data = new double[3][ImageProcessor.MAXCBVINT];
for (int i = 0; i < Concentration.dataoutHeight; i++) {
for (int j = 0; j < ImageProcessor.MAXCBVINT; j++) {
data[0][j] = j;//x value
data[1][j] = i;//y value
data[2][j] = Concentration.dataout[j][i][0];//Colour
}
dataset.addSeries(i, data);
}
NumberAxis xAxis = new NumberAxis("Intensity");
xAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
xAxis.setLowerMargin(0.0);
xAxis.setUpperMargin(0.0);
NumberAxis yAxis = new NumberAxis("Distance to Closest Blood Vessel (um)");
yAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
yAxis.setLowerMargin(0.0);
yAxis.setUpperMargin(0.0);
XYBlockRenderer renderer = new XYBlockRenderer();
PaintScale scale = new GrayPaintScale(0, 10000.0);
renderer.setPaintScale(scale);
renderer.setBlockHeight(1);
renderer.setBlockWidth(1);
XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer);
plot.setBackgroundPaint(Color.lightGray);
plot.setDomainGridlinesVisible(false);
plot.setRangeGridlinePaint(Color.white);
JFreeChart chart = new JFreeChart("Surface Plot", plot);
chart.removeLegend();
chart.setBackgroundPaint(Color.white);
ChartFrame frame = new ChartFrame("Surface Map - "
+ (Concentration.testing ? "TESTING using "
+ Concentration.testfile : currentFile.getName()), chart);
frame.pack();
frame.setVisible(true);
You have two options:
Represent them as 3d
3D Lib for JFreeChart
You need to use the class : XYBlockRenderer which does exactly what you are asking. You can download the JFreeChart demo collection where the code for this is given.
(source code of class here)
There is also this full code example with 4D very similar.
Related
I have a plot that I've created with GRAL:
GRAL plot
The problems I'm having are with the axis. Basically I'd like the Y axis to run from 0 .. 4 in every graph, and I can't find a way to force this behaviour. If I zoom out there is extra increments, but I'd like 0 .. 4 show up in the unzoomed plots.
I've also tried using setCustomTicks but the default ticks remain, is there a way to remove them and only use custom ones?
Help would be greatly appreciated!
The code for my plot is:
int[][] seq_data_200 = chunkArray(seq_data, 200);
DataTable[] listData = new DataTable[seq_data_200.length];
for (int i = 0; i < seq_data_200.length; i++){
DataTable data = new DataTable(Integer.class, Integer.class);
for (int j = 0; j < seq_data_200[i].length; j++) {
data.add(j, seq_data_200[i][j]);
}
listData[i] = data;
}
panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
panel.setLayout(new GridLayout(seq_data_200.length,1,20,20));
for (int i = 0; i < listData.length; i++) {
XYPlot plot = new XYPlot(listData[i]);
LineRenderer lines = new DefaultLineRenderer2D();
plot.setLineRenderer(listData[i], lines);
Color color = new Color(0.0f, 0.3f, 1.0f);
plot.getPointRenderer(listData[i]).setColor(color);
plot.getLineRenderer(listData[i]).setColor(color);
double insetsTop = 20.0, insetsLeft = 60.0, insetsBottom = 60.0, insetsRight = 40.0;
plot.setInsets(new Insets2D.Double(insetsTop, insetsLeft, insetsBottom, insetsRight));
plot.getTitle().setText(getTitle(i, sequence));
plot.getAxisRenderer(XYPlot.AXIS_X).setLabel("Bases");
plot.getAxisRenderer(XYPlot.AXIS_X).setCustomTicks(getTicks(i, sequence));
plot.getAxisRenderer(XYPlot.AXIS_Y).setLabel("Number of " + nucleo + "'s");
plot.getAxisRenderer(XYPlot.AXIS_Y).setMinorTicksVisible(false);
plot.getAxisRenderer(XYPlot.AXIS_Y).setTickSpacing(1);
panel.add(new InteractivePanel(plot), BorderLayout.CENTER);
}
I found a somewhat hackish fix that works in the interim:
GRAL decides on axis height using the DataTable. It uses the max and min values of the table to define the viewport.
So if you add two pieces of Data:
DataTable.add(x,minY)
DataTable.add(x,maxY)
They'll be some spurious data on the graph, but the viewport will resize itself to your desired axis. Since these points are added at the end, they tend to be obscured somewhat, and are easy to ignore.
The following method worked for me:
plot.getAxis(XYPlot.AXIS_X).setRange(0.0, 4.0);
plot.getAxis(XYPlot.AXIS_Y).setRange(0.0, 20.0);
i have two sets of data
int[] x1 = {1,2,3,4,5,6,7,8,9,10};
int[] y1 = {1,2,3,5,6,8,9,10,14,11};
int[] x2 = {1,2,3,4,5,6,7,8,9,10};
int[] y2 = {0,2,3,5,0,8,9,8,14,11};
int[] z2 = {1,2,3,1,2,3,1,2,3,1};
I want to plot the x1,y1 as an XYLineChart and then plot x2,y2 as a scatter on the same plot without a line.
I also need each scatter point of xy,y2 to be a different color depending on the value of z2 (1=Color.red, 2=Color.green, 3=Color.blue)
How can i do this?
So far i have:
JPanel panel_1 = new JPanel();
panel_1.setLayout(new BorderLayout(0, 0));
XYSeriesCollection dataset = new XYSeriesCollection();
XYSeries series1 = new XYSeries("series1");
for(int i=0; i<x1.length; i++){
series1.add(x1[i],y1[i]);
}
dataset.add(series1);
JFreeChart chart = ChartFactory.createXYLineChart("Title", "x", "y", dataset, PlotOrientation.VERTICAL, false, false, false);
ChartPanel cp = new ChartPanel(chart);
panel_1.add(cp, BorderLayout.CENTER);
This gets the line graph sorted. I now need to code the scatter plot for x2,y2 (with colors described above) which is where im stuck.
The createXYLineChart() method will create a chart that uses an XYLineAndShapeRenderer. So fetch the renderer from the plot and cast it to XYLineAndShapeRenderer. Then you can call the methods setSeriesLinesVisible() and setSeriesShapesVisible() to control, for each series, whether shapes and/or lines are drawn for the data items. That way you can use a single renderer and dataset, which makes things simpler.
Your requirement to change the colors depending on another data value requires a little more work. You should subclass the XYLineAndShapeRenderer class and override the getItemPaint(int, int) method. Here you can return any color you want for a data item. The default implementation looks at the series index and returns the color for the series. You need to look at the item index as well, then do a lookup in your table of z-values and decide what color to return.
i'm trying to make an overlaid plot,
And my problem is that i can't bring the second plot to fit the second one.
Here is my first plot :
And here the second one :
And when i try to fit both, here is what i get :
So basically, i would like to fit the whole second plot between 0 and 30, how can i do this without losing any data?
First I tried using plot.mapDatasetToRangeAxis()
Then i tried with :
domain.setRange(0.00, 30.0);
domain.setTickUnit(new NumberTickUnit(1));
But i couldn't bring neither the first, nor the second one to work as i wish.
Do you have any other ideas? (except buying this - which i can't afford right now as a student).
Any help will be greatly appreciated :)
Oh and by the way the x-axis is a speed (forgot to draw it on the plot).
So here a very ugly photomontage of the kind of result i wish to have (with fitting units on the x and y axis) :
Sorry for my Gimp skills, which are beyond bad.
Here is what i did :
private JFreeChart createOverlaidChart()
{
final NumberAxis domainAxis = new NumberAxis("Speed (m / s)");
final ValueAxis rangeAxis = new NumberAxis("Power (kw)");
// create plot ...
final IntervalXYDataset data0 = createDataset0();
final XYItemRenderer renderer0 = new XYBarRenderer(0.20);
// change "new XYBarRenderer(0.20)" to "StandardXYItemRenderer()" if you want to change type of graph
final XYPlot plot = new XYPlot(data0, domainAxis, rangeAxis, renderer0);
// add a second dataset and renderer...
final IntervalXYDataset data1 = createDataset1();
final XYLineAndShapeRenderer renderer1 = new XYLineAndShapeRenderer(false, true);
// arguments of new XYLineAndShapeRenderer are to activate or deactivate the display of points or line. Set first argument to true if you want to draw lines between the points for e.g.
plot.setDataset(1, data1);
plot.setRenderer(1, renderer1);
// add a third dataset and renderer...
final IntervalXYDataset data2 = createDataset2();
final XYLineAndShapeRenderer renderer2 = new XYLineAndShapeRenderer(true, true);
// arguments of new XYLineAndShapeRenderer are to activate or deactivate the display of points or line. Set first argument to true if you want to draw lines between the points for e.g.
plot.setDataset(2, data2);
plot.setRenderer(2, renderer2);
plot.setDatasetRenderingOrder(DatasetRenderingOrder.FORWARD);
// return a new chart containing the overlaid plot...
return new JFreeChart("Test", JFreeChart.DEFAULT_TITLE_FONT, plot, true);
}
The Range Axis is the Vertical/Y Axis, you need to add a second Domain Axis (Horizontal/X Axis) to your chart.
I did something similar -- although using same X axis vales but differing Y axis. For this, I use single Domain (you'd want single Range). I convert for you (there may be typos due to my edits):
priceXYPlot.setRangeAxis( new new NumberAxis( "Y" ) );
priceXYPlot.setDomainAxis( 0, new NumberAxis( "X1" ) );
priceXYPlot.setDomainAxis( 1, new NumberAxis("X2") );
and mapDatasetToRangeAxis so that you had two diff X axis along top & bottom, something like:
priceXYPlot.setDataset( 0, data0);
priceXYPlot.mapDatasetToDomainAxis( 0, 0 ); //1st dataset to 1st x-axis
priceXYPlot.setDataset( 1, data1 );
priceXYPlot.mapDatasetToDomainAxis( 1, 1 ); //2nd dataset to 2nd x-axis
I create and pass a TimeTableXYDataset to display a StackedBarChart, quite similar to the StackedXYBarChartDemo2 from the demo package. In addition to the values displayed as ItemLabels, I would like to display the sum of all values above every bar. Does anyone know if this is doable, and how ?
You can define a custom XYItemLabelGenerator which will sum the two series.
For example :
DateAxis domainAxis = new DateAxis("Date");
domainAxis.setTickMarkPosition(DateTickMarkPosition.MIDDLE);
NumberAxis rangeAxis = new NumberAxis("Y");
// Set a margin so that the label above the bar has a place to display
rangeAxis.setUpperMargin(0.15);
StackedXYBarRenderer renderer = new StackedXYBarRenderer(0.10);
renderer.setDrawBarOutline(false);
XYPlot plot = new XYPlot(dataset, domainAxis, rangeAxis, renderer);
// Custom LabelGenerator, which displays the sum of the two series.
XYItemLabelGenerator generator = new XYItemLabelGenerator() {
#Override
public String generateLabel(XYDataset dataset, int series, int item) {
// Sum values for the two series of data
double sum = dataset.getYValue(0, item) + dataset.getYValue(1, item);
return "" + sum;
}
};
// The LabelGenerator is linked to series 1 (top part of the bar)
renderer.setSeriesItemLabelGenerator(1, generator);
renderer.setSeriesItemLabelsVisible(1, true);
renderer.setSeriesPositiveItemLabelPosition(1, new ItemLabelPosition(ItemLabelAnchor.OUTSIDE12,
TextAnchor.BASELINE_CENTER));
renderer.setItemLabelAnchorOffset(10);
JFreeChart chart = new JFreeChart("Stacked XY Bar Chart Demo 2", plot);
return chart;
I have a requirement to show time-series data as layered bar chart. Is it possible with JFreeChart? Any pointers would be really helpful.
The data would be a list of: (TS, X1, X2), where I've to plot X1 for a given Timestamp (TS) and X2 would basically serve as the label for the given value of X1.
Edit: Also, for the same TS, there might exist different X1 values. The idea is to denote all these X1 values as layered bars against the same TS.
Here's somewhat of an example of what I want:
.
(so instead of category, I'll have TS in X-axis)
It sounds like you want a BarChart (with x-axis determined by time) with the bars labelled with their values. You don't need to add a new data series for the labels, but modify the rendering of the plot.
Here's a simple example:
public class LabelledBarChartTest {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
dataset.addValue(10.0, "Series", new Integer(2010));
dataset.addValue(20.0, "Series", new Integer(2011));
dataset.addValue(30.0, "Series", new Integer(2012));
JFreeChart chart = ChartFactory.createBarChart(null,null,null,dataset,
PlotOrientation.VERTICAL,true,true,false);
CategoryPlot plot = (CategoryPlot) chart.getPlot();
CategoryItemRenderer renderer = plot.getRenderer();
// label the points
NumberFormat format = NumberFormat.getNumberInstance();
format.setMaximumFractionDigits(2);
CategoryItemLabelGenerator generator = new StandardCategoryItemLabelGenerator(
StandardXYItemLabelGenerator.DEFAULT_ITEM_LABEL_FORMAT, format, format);
renderer.setBaseItemLabelGenerator(generator);
renderer.setBaseItemLabelsVisible(true);
frame.setContentPane(new ChartPanel(chart));
frame.pack();
frame.setVisible(true);
}
}
Credit where credit is due - I got the labelling example from this example.