JFreeChart how to draw dashed line with closely spaced points - java

I am attempting to plot smooth curves where the points are close together (-4.0, 0.01), (-4.1, .02), and so on. The plot will have multiple lines and I would like to use dashed lines for each curve. I am using JFreeChart and have written a custom XYLineAndShapeRenderer to create dashed lines. However, the dashes do not always appear. It seems that the closer together the points in the dataset the fewer dashes I get. Does anyone know how I can use points that are close together and still get dashed lines? The code below demonstrates the problem. All of these lines should have dashes. I think the problem is related to the way Java draws lines with BasicStroke, but I am not sure.Thanks for your help!
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.ApplicationFrame;
import javax.swing.*;
import java.awt.*;
public class IccGraph extends ApplicationFrame {
public IccGraph(String title){
super(title);
JPanel chartPanel = createPanel();
chartPanel.setPreferredSize(new Dimension(450, 400));
setContentPane(chartPanel);
}
private double probability(double x, double b){
double top = Math.exp(x-b);
double prob = top/(1+top);
return prob;
}
public XYSeriesCollection createData(){
XYSeriesCollection dataset = new XYSeriesCollection();
XYSeries series1 = new XYSeries("Wide Points");
XYSeries series2 = new XYSeries("Close Points");
XYSeries series3 = new XYSeries("Very Close Points");
double maxValue = 4.0;
double value = -4.0;
double prob = 0.0;
//create series where x-axis values use increments of .5
//will produce graph with dashes as expected
while(value<maxValue){
prob = probability(value, -1.0);
series1.add(value, prob);
value += .5;
}
//create series where x-axis values use increments of .1
//will produce line with some dashes
value = -4.0;
while(value<maxValue){
prob = probability(value, 0.0);
series2.add(value, prob);
value += .1;
}
//create series where x-axis values use increments of .01
//will produce straight lines in graph
value = -4.0;
while(value<maxValue){
prob = probability(value, 1.0);
series3.add(value, prob);
value += .01;
}
dataset.addSeries(series1);
dataset.addSeries(series2);
dataset.addSeries(series3);
return dataset;
}
public JPanel createPanel(){
JFreeChart chart = createChart(createData());
ChartPanel panel = new ChartPanel(chart);
return panel;
}
public JFreeChart createChart(XYSeriesCollection dataset){
JFreeChart chart = ChartFactory.createXYLineChart(
"", // chart title
"Theta", // x axis label
"Probability", // y axis label
dataset, // data
PlotOrientation.VERTICAL,
true, // include legend
true, // tooltips
false // urls
);
XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(){
#Override
public Stroke getItemStroke(int row, int col){
Stroke dash1 = new BasicStroke(1.0f,
BasicStroke.CAP_SQUARE,
BasicStroke.JOIN_MITER,
10.0f,
new float[] {5.0f,5.0f},
0.0f);
return dash1;
}
};
renderer.setBaseShapesFilled(false);
renderer.setDrawOutlines(false);
XYPlot plot = (XYPlot)chart.getPlot();
plot.setRenderer(renderer);
return chart;
}
public static void main(String[] args) {
IccGraph graph = new IccGraph("Line Dash Problem");
graph.pack();
graph.setVisible(true);
}

OK, I found the answer. The solution is to use renderer.setDrawSeriesLineAsPath(true). The revised code listed below, plots three dashed lines as desired.
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.ApplicationFrame;
import javax.swing.*;
import java.awt.*;
public class IccGraph extends ApplicationFrame {
public IccGraph(String title){
super(title);
JPanel chartPanel = createPanel();
chartPanel.setPreferredSize(new Dimension(450, 400));
setContentPane(chartPanel);
}
private double probability(double x, double b){
double top = Math.exp(x-b);
double prob = top/(1+top);
return prob;
}
public XYSeriesCollection createData(){
XYSeriesCollection dataset = new XYSeriesCollection();
XYSeries series1 = new XYSeries("Wide Points");
XYSeries series2 = new XYSeries("Close Points");
XYSeries series3 = new XYSeries("Very Close Points");
double maxValue = 4.0;
double value = -4.0;
double prob = 0.0;
//create series where x-axis values use increments of .5
//will produce graph with dashes as expected
while(value<maxValue){
prob = probability(value, -1.0);
series1.add(value, prob);
value += .5;
}
//create series where x-axis values use increments of .1
//will produce line with some dashes
value = -4.0;
while(value<maxValue){
prob = probability(value, 0.0);
series2.add(value, prob);
value += .1;
}
//create series where x-axis values use increments of .01
//will produce straight lines in graph
value = -4.0;
while(value<maxValue){
prob = probability(value, 1.0);
series3.add(value, prob);
value += .01;
}
dataset.addSeries(series1);
dataset.addSeries(series2);
dataset.addSeries(series3);
return dataset;
}
public JPanel createPanel(){
JFreeChart chart = createChart(createData());
ChartPanel panel = new ChartPanel(chart);
return panel;
}
public JFreeChart createChart(XYSeriesCollection dataset){
JFreeChart chart = ChartFactory.createXYLineChart(
"", // chart title
"Theta", // x axis label
"Probability", // y axis label
dataset, // data
PlotOrientation.VERTICAL,
true, // include legend
true, // tooltips
false // urls
);
XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(){
#Override
public Stroke getItemStroke(int row, int col){
Stroke dash1 = new BasicStroke(1.0f,
BasicStroke.CAP_SQUARE,
BasicStroke.JOIN_MITER,
10.0f,
new float[] {10.0f,5.0f},
0.0f);
return dash1;
}
};
renderer.setBaseShapesFilled(false);
renderer.setDrawOutlines(false);
renderer.setDrawSeriesLineAsPath(true);//this line is the solution
XYPlot plot = (XYPlot)chart.getPlot();
plot.setRenderer(renderer);
return chart;
}
public static void main(String[] args) {
IccGraph graph = new IccGraph("Line Dash Problem");
graph.pack();
graph.setVisible(true);
}
}

Related

JFreechart - Polar plot with a colormap

I am trying to obtain the equivalent of a matlab Pcolor added on a polar() function but in java.
I am pretty new to the language, but managed to obtain a polar plot already, with the following code:
public class Polar extends JFrame {
public Polar(double[][] vec){
XYDataset dataset = getXYDataset(vec);
JFreeChart chart = ChartFactory.createPolarChart("test", dataset, true, true, true);
PolarPlot plot = (PolarPlot) chart.getPlot();
DefaultPolarItemRenderer render = (DefaultPolarItemRenderer)
plot.getRenderer();
render.setFillComposite(...);
render.setSeriesFilled(0,true);
ChartPanel panel = new ChartPanel(chart);
panel.setMouseZoomable(false);
setContentPane(panel);
}
private XYDataset getXYDataset(double[][] vec){
XYSeriesCollection dataset = new XYSeriesCollection();
XYSeries faultDP =new XYSeries("Serie1");
for(int i = 0; i<vec.length; i++){
faultDP.add(vec[i][1],vec[i][0]);
}
dataset.addSeries(faultDP);
return dataset;
}
}
The array vec contains the speed and angle of my variable, and should be plotted on the polar plot. this works fine.
The next step would be to pass a new variable, a double[][] vector of dimension 90x360. Each cell should be plotted on the polar plot with background color value, a bit like in the picture below.
Any idea on how to do so ?
Cheers,
Flo
So, I've looked into what trashgod offered me and came with this code:
public class PolarTest extends JFrame {
public PolarTest(double[][] dipaz, double[][] val){
JFrame f = new JFrame("Title");
ChartPanel panel = new ChartPanel(createChart(val, dipaz));
panel.setPreferredSize(new Dimension(500,500));
f.add(panel);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private JFreeChart createChart(double[][] val,double[][] dipaz){
XYSeriesCollection dataset = new XYSeriesCollection ();
double step, min, max;
min = val[0][2];
max = val[val.length-1][2];
step = (max-min)/10; //creation of 10 areas of similar heat range
dataset = (XYSeriesCollection) createDataset(val,step);
JFreeChart chart = ChartFactory.createPolarChart("test", dataset, true, true, true);
PolarPlot plot = (PolarPlot) chart.getPlot();
DefaultPolarItemRenderer r = (DefaultPolarItemRenderer) plot.getRenderer();
r.setFillComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.5f));
for(int i =0; i<dataset.getSeriesCount(); i++){
r.setSeriesFilled(i, true);
r.setShapesVisible(false);
r.setDrawOutlineWhenFilled(false);
}
NumberAxis rangeAxis = (NumberAxis) plot.getAxis();
rangeAxis.setTickLabelsVisible(false);
return chart;
}
private XYDataset createDataset(double[][] val, double step){
XYSeriesCollection result = new XYSeriesCollection();
double dpTmp, breakPoint;
int cpt=1;
boolean t;
t= true;
dpTmp = val[0][2];
breakPoint = dpTmp + step;
XYSeries series = null ;
for(int i = 0; i< val.length; i++){
if(val[i][2] < breakPoint){
if(t){
series = new XYSeries(String.valueOf(cpt));
t = false;
}
series.add(val[i][1],val[i][0]);
cpt++;
}else{
result.addSeries(series);
i--;
t=true;
breakPoint = val[i][2] + step;
}
}
return result;
}
The array dipaz contains my angle and dip (of X,Y dots I'd like to later plot on the polar representation), while val a 3 by N array:
val[][0] = dip
val[][1] = angle
val[][2] = value of "heat" , if we see that problem as a heatmap.
val is ascending sorted by the third column. My idea was to split the whole array in 10 (or more) areas of similar heat values.
if I run this code, I obtain the following output (cf pic). We can see that it's getting there, but not quite. I am not sure that this code is able to map correctly my heat on the polar plot>
How could I get closer to the picture I posted in my first post?
Thank you for your help.
Flo

Plot a quadratic curve as a function of y on a scatterplot

I am using JFreeCharts to plot a scatterplot in Java. I am using PolynomialFunction2D to plot a quadratic regression line, over the scatterplot.
PolynomialFunction2D can be used to plot quadratic curves, that are defined as a function of x, ie, y = a0 + a1 * x + a2 * x^2 + ...
I want to plot a quadratic curve as a function of y, ie, x = a0 + a1 * y + a2 * y^2 + ... on the scatterplot, made in JFreeCharts, when the coefficients are given.
Is this possible? And, if yes, what approach can be used for the same.
You can create an XYSeries from the function, by manually sampling the function over y and computing the corresponding x-value. The resulting data set can be added to the XYPlot, together with the XYDataset of the scatter plot:
As an example:
import java.awt.Dimension;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.function.Function2D;
import org.jfree.data.function.PolynomialFunction2D;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.ApplicationFrame;
public class FunctionOfYOverScatter
{
public static void main(String[] args)
{
new FunctionOfYOverScatter();
}
public FunctionOfYOverScatter()
{
XYPlot plot = new XYPlot();
XYDataset scatterPlotDataset = getScatterPlotDataset();
plot.setDataset(0, scatterPlotDataset);
plot.setRenderer(0, new XYLineAndShapeRenderer(false, true));
plot.setDomainAxis(0, new NumberAxis("Scatterplot domain"));
plot.setRangeAxis(0, new NumberAxis("Scatterplot range"));
plot.mapDatasetToDomainAxis(0, 0);
plot.mapDatasetToRangeAxis(0, 0);
double minY = -2.0;
double maxY = 2.0;
XYDataset functionDataset =
getFunctionDataset(0.8, 0.5, 1.2, minY, maxY);
plot.setDataset(1, functionDataset);
plot.setRenderer(1, new XYLineAndShapeRenderer(true, false));
JFreeChart chart = new JFreeChart("Function of Y over scatter plot",
JFreeChart.DEFAULT_TITLE_FONT, plot, true);
ChartPanel chartPanel = new ChartPanel(chart);
chartPanel.setPreferredSize(new Dimension(500, 270));
ApplicationFrame frame = new ApplicationFrame("Example");
frame.setContentPane(chartPanel);
frame.pack();
frame.setVisible(true);
}
private XYDataset getFunctionDataset(
double a0, double a1, double a2,
double minY, double maxY)
{
double[] a = { a0, a1, a2 };
Function2D p = new PolynomialFunction2D(a);
XYSeries series = sampleFunctionOverY(p, minY, maxY, 100, "Function");
XYSeriesCollection dataset = new XYSeriesCollection();
dataset.addSeries(series);
return dataset;
}
public static XYSeries sampleFunctionOverY(Function2D f, double start,
double end, int samples, Comparable<?> seriesKey)
{
XYSeries series = new XYSeries(seriesKey, false);
double step = (end - start) / (samples - 1);
for (int i = 0; i < samples; i++)
{
double y = start + step * i;
series.add(f.getValue(y), y);
}
return series;
}
private XYDataset getScatterPlotDataset()
{
XYSeriesCollection dataset = new XYSeriesCollection();
XYSeries data = new XYSeries("Scatterplot");
data.add(3.0, 2.0);
data.add(1.7, 1.0);
data.add(2.0, -1.0);
data.add(1.0, 1.8);
dataset.addSeries(data);
return dataset;
}
}

To change the X-axis starting value of graph in Jfreechart

I am calculating histogram of red component of the image and stored it in redhisto[]. The index of the array represent the intensity(0 to 255)
and the value represent the number of pixel with that intensity. Then plotting those values with JFreeChart.
My question is:
How to make X-axis value start from 0. Now its starting from negative number.
Can we change the color of the bars in the graph
code is :
public class Histogram extends ApplicationFrame {
public Histogram(final String title) throws IOException {
super(title);
IntervalXYDataset dataset = createDataset();
JFreeChart chart = createChart(dataset);
final ChartPanel chartPanel = new ChartPanel(chart);
chartPanel.setPreferredSize(new java.awt.Dimension(500, 270));
setContentPane(chartPanel);
}
private IntervalXYDataset createDataset() throws IOException {
BufferedImage imageA = ImageIO.read(new File("XYZ.bmp"));
int[] red = new int[imageA.getHeight()*imageA.getWidth()];
int[] redhisto = new int[256];
int[] pixel;
int k= 0;
for (int y = 0; y < imageA.getHeight(); y++) {
for (int x = 0; x < imageA.getWidth(); x++) {
pixel = imageA.getRaster().getPixel(x, y, new int[3]);
red[k] = pixel[0];
k++;
}
}
for(int x=0;x<red.length;x++){
int y = red[x];
redhisto[y]++;
}
final XYSeries series = new XYSeries("No of pixels");
for(int i=0; i<redhisto.length;i++)
series.add(i,redhisto[i]);
final XYSeriesCollection dataset = new XYSeriesCollection(series);
return dataset;
}
private JFreeChart createChart(IntervalXYDataset dataset) {
final JFreeChart chart = ChartFactory.createXYBarChart("Color Intensity Histogram","X",false,"Y",dataset,PlotOrientation.VERTICAL,true,true,false);
XYPlot plot = (XYPlot) chart.getPlot();
return chart;
}
public static void main(final String[] args) throws IOException {
final Histogram demo = new Histogram("Image Histogram");
demo.pack();
RefineryUtilities.centerFrameOnScreen(demo);
demo.setVisible(true);
}
}
You can change the lower bound of the domain axis and set the series paint as shown below. The default XYBarPainter has a gradient color highlight, so I used a StandardXYBarPainter.
XYPlot plot = (XYPlot) chart.getPlot();
ValueAxis axis = plot.getDomainAxis();
axis.setLowerBound(0);
XYBarRenderer r = (XYBarRenderer) plot.getRenderer();
r.setBarPainter(new StandardXYBarPainter());
r.setSeriesPaint(0, Color.blue);
XYPlot plot = (XYPlot) chart.getPlot();
//To change the lower bound of X-axis
NumberAxis xAxis = (NumberAxis) plot.getDomainAxis();
xAxis.setLowerBound(0);
//To change the lower bound of Y-axis
NumberAxis yAxis = (NumberAxis) plot.getRangeAxis();
yAxis.setLowerBound(0);
// To change the color
XYItemRenderer renderer = plot.getRenderer();
renderer.setSeriesPaint(0, Color.green);

HeatMaps with JFreeChart

I have a dataset of points (x,y) and I would like to create an heatmap of this dataset. More specifically, I would like an image in which I have lighter colors in the areas where I have a bigger concentration of points and darker where there are less points.
I am using JFreeChart libraries and I found some classes for example DefaultHeatMapDataset but I am not sure how to use them in the proper way.
Does anybody has a clue on how to do it?
Thanks in advance!
Giovanni
Use an XYBlockRenderer with a suitable implementation of XYZDataset and a corresponding implementation of PaintScale.
Creating a heatmap in JFreeChart is a bit tricky, as there is still (as of version 1.0.19/1.5.0) no chart-type resp. plot for this usecase. (The available HeatMapDataset was written to be used with HeatMapUtilities as David Gilbert wrote in the forum.)
So best way for creating a heatmap is using an XYPlot with an XYBlockRenderer and an XYZDataset, as trashgod already wrote.
Here is a complete, minimalistic example that you can run to get the chart shown below:
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.*;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.LookupPaintScale;
import org.jfree.chart.renderer.xy.XYBlockRenderer;
import org.jfree.chart.title.PaintScaleLegend;
import org.jfree.data.xy.DefaultXYZDataset;
import org.jfree.data.xy.XYZDataset;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RectangleEdge;
import java.awt.*;
import java.util.Random;
public class HeatMapDemo extends ApplicationFrame
{
public HeatMapDemo()
{
super("JFreeChart Heatmap Demo");
final JFreeChart chart = createChart(createDataset());
setContentPane(new ChartPanel(chart));
}
private static JFreeChart createChart(XYZDataset dataset)
{
// x-axis for time
DateAxis xAxis = new DateAxis("Time");
xAxis.setStandardTickUnits(DateAxis.createStandardDateTickUnits());
xAxis.setLowerMargin(0);
xAxis.setUpperMargin(0);
// visible y-axis with symbols
String labels[] = new String[500];
for (int i = 0; i < 500; i++)
labels[i] = "ca. " + i + "nm";
SymbolAxis yAxis = new SymbolAxis(null, labels);
yAxis.setTickUnit(new NumberTickUnit(50));
// another invisible y-axis for scaling
// (this is not necessary if your y-values are suitable)
NumberAxis valueAxis1 = new NumberAxis("Marker");
valueAxis1.setLowerMargin(0);
valueAxis1.setUpperMargin(0);
valueAxis1.setVisible(false);
// create a paint-scale and a legend showing it
LookupPaintScale paintScale = new LookupPaintScale(0, 300, Color.black);
Color c = Color.green;
paintScale.add(0.0, c);
paintScale.add(33.0, c = c.darker());
paintScale.add(66.0, c.darker());
paintScale.add(100.0, c = Color.blue);
paintScale.add(133.0, c = c.darker());
paintScale.add(166.0, c.darker());
paintScale.add(200.0, c = Color.red.darker().darker());
paintScale.add(233.0, c = c.brighter());
paintScale.add(266.0, c.brighter());
PaintScaleLegend psl = new PaintScaleLegend(paintScale, new NumberAxis());
psl.setPosition(RectangleEdge.RIGHT);
psl.setAxisLocation(AxisLocation.TOP_OR_RIGHT);
psl.setMargin(50.0, 20.0, 80.0, 0.0);
// finally a renderer and a plot
XYPlot plot = new XYPlot(dataset, xAxis, yAxis, new XYBlockRenderer());
((XYBlockRenderer)plot.getRenderer()).setPaintScale(paintScale);
// 2 optional lines, depending on your y-values
plot.setRangeAxis(1, valueAxis1);
plot.mapDatasetToRangeAxis(0, 1);
JFreeChart chart = new JFreeChart(null, null, plot, false);
chart.addSubtitle(psl);
return chart;
}
public XYZDataset createDataset()
{
double[] xvalues = new double[1000*100]; // date
double[] yvalues = new double[1000*100]; // numeric (1-100)
double[] zvalues = new double[1000*100]; // numeric (the actual data)
// create some random data
final Random rand = new Random();
long l = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
l -= 600000;
for (int j = 0; j < 100; j++) {
final int idx = i * 100 + j;
xvalues[idx] = l;
yvalues[idx] = j;
double delta = rand.nextInt(15) * (rand.nextInt(4) == 0 ? -1 : 1);
zvalues[idx] = Math.max(0, Math.min(300,
(idx < 1000 ? 0 : zvalues[idx - 1000]) + delta));
}
}
DefaultXYZDataset dataset = new DefaultXYZDataset();
dataset.addSeries("Just one Series", new double[][] { xvalues, yvalues, zvalues });
return dataset;
}
public static void main(String args[])
{
final HeatMapDemo demo = new HeatMapDemo();
demo.pack();
demo.setVisible(true);
}
}
The produced heatmap looks like this:
If you need a more linear PaintScale, you could have a look a the SpectrumPaintScale implementation in this answer.

Making a Scatter Plot Using 2d array and JfreeChart

This is my First Month with Java, so I apologize for my stupid question in advance. I'm trying to make a simple program using Jfreechart. I want to display my 2D array on the scatter plot.
here is the code:
package myappthatusesjfreechart;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartFrame;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.general.DefaultPieDataset;
public class MyAppThatUsesJFreeChart {
public static void main(String[] args) {
// create a dataset...
int[][] a2 = new int[10][5];
// print array in rectangular form
for (int r = 0; r < a2.length; r++) {
for (int c = 0; c < a2[r].length; c++) {
System.out.print(" " + a2[r][c]);
}
System.out.println("");
}
// create a chart...
JFreeChart chart = ChartFactory.createScatterPlot(
"Scatter Plot", // chart title
"X", // x axis label
"Y", // y axis label
a2, // data ***-----PROBLEM------***
PlotOrientation.VERTICAL,
true, // include legend
true, // tooltips
false // urls
);
// create and display a frame...
ChartFrame frame = new ChartFrame("First", chart);
frame.pack();
frame.setVisible(true);
}
}
The ;ChartFactory.createScatterPlot; is not allowing me to pass the 2d array, I want to ask is there any way that i can do it.
The createScatterPlot() method expects an XYDataset, such as XYSeriesCollection. There are examples using XYSeriesCollection here and here.
Addendum: Here's an example more suited to a scatter plot; just replace a2 with createDataset() in the factory call.
private static final Random r = new Random();
private static XYDataset createDataset() {
XYSeriesCollection result = new XYSeriesCollection();
XYSeries series = new XYSeries("Random");
for (int i = 0; i <= 100; i++) {
double x = r.nextDouble();
double y = r.nextDouble();
series.add(x, y);
}
result.addSeries(series);
return result;
}

Categories

Resources