Set item shape size in jFreechart - java

I am plotting a line chart using jFreeChart, which is a series of (x, y) value connected by a straight line.
But the problem is the shape, a circle or a rect that represents a data point is too big, since I have a lot of values in one series. You can see how it looks like in this screenshot:
Also, I learned the shapes that represent data point do not scale as the rest of the plot does when I resize the chart panel.
Question:
How to make the shape(the circles and rects that represent data point) smaller so that they won't clog together?
Here is my code to generate the chart, and I used a customized renderer to draw different color for different part of the line in one data series.
private JFreeChart createChart(XYDataset dataCollection) {
// create the chart...
JFreeChart chart = ChartFactory.createXYLineChart(
fileName, // chart title
"Count", // x axis label
"Price", // y axis label
dataCollection, // data
PlotOrientation.VERTICAL,
true, // include legend
true, // tooltips
false // urls
);
// customize chart
XYPlot plot = (XYPlot) chart.getPlot();
// find out the max and min value for price series
XYSeriesCollection collection = (XYSeriesCollection)plot.getDataset();
XYSeries priceSeries = collection.getSeries(0);
double maxY = priceSeries.getMaxY();
double minY = priceSeries.getMinY();
// set max and min for range axis (y axis)
NumberAxis rangeAxis = (NumberAxis)plot.getRangeAxis();
rangeAxis.setRange(minY, maxY);
XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer() {
public Color getItemColor(int series, int item) {
// modify code here to change color for different part of the line in one serie line
if(series == 1) {
System.out.println("getting color at: " + item);
int isBuy = 0;
if(item < kFilter.buySellSignal.size()) {
isBuy = kFilter.buySellSignal.get(item);
}
if(isBuy == 1) {
return Color.red;
} else {
return Color.green;
}
} else {
return Color.yellow;
}
}
#Override
protected void drawFirstPassShape(Graphics2D g2, int pass, int series, int item, Shape shape) {
super.drawFirstPassShape(g2, pass, series, item, shape);
//g2.setStroke(getItemStroke(series, item));
Color c1 = getItemColor(series, item - 1);
Color c2 = getItemColor(series, item);
GradientPaint linePaint = new GradientPaint(0, 0, c1, 0, 300, c2);
g2.setPaint(linePaint);
g2.draw(shape);
}
};
plot.setRenderer(renderer);
return chart;
}

You can override getItemShape() in your XYLineAndShapeRenderer to return any desired Shape, as shown here. Alternatively, invoke one of the abstract parent's methods, setSeriesShape(), setBaseShape() etc.
#Nicolas S.Xu reports using code similar to the following:
Rectangle rect = new Rectangle(2, 2);
renderer.setSeriesShape(1, rect);
See also ShapeUtilities.

Related

MPAndroid : How to place the text inside the circle (without y-offset)

I am developing a small chart app to monitor the blood sugar with the MPAndroidChart library.
Is there a way to draw the text inside the circle without offset?
As suggested in another post, i put in a slight y-offset (different y-data for circle and text) and achieved a result which is ok.
But if there are only two values the text and circle do not align.
I am not that familiar with modifying the yAxis Renderer, do you know how to find the formula behind the offset for the labels? (would be great to just recalculate it..)
Similar problem: How to place the text values inside in MPAndroidChart circle?
With manual offset between text and circle y-data
Due to zooming in on y-axis (less data points and range on y-axis) the manuel offset is not working well here..
Otherwise awesome library PhilJay !!
EDIT: I have found a easy and sufficient solution:
Override the drawValues method from LineChartRenderer**
In LineChartRenderer.java -> drawValues the text is vertically shifted by this line:
drawValue(c, formatter.getPointLabel(entry), x, y - valOffset, dataSet.getValueTextColor(j / 2));
So to get rid of the "- valOffset":
1.Override the drawValues method
Create a new java file "CenteredTextLineChartRenderer.java" and override method drawValues from LineChartRenderer
2.Modify the y-valOffset to y+textHeight*0.35f
Add float textHeight = dataSet.getValueTextSize();
public class CenteredTextLineChartRenderer extends LineChartRenderer {
public CenteredTextLineChartRenderer(LineDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) {
super(chart, animator, viewPortHandler);
}
//Modified drawValues Method
// Center label on coordinate instead of applying a valOffset
#Override
public void drawValues(Canvas c) {
if (isDrawingValuesAllowed(mChart)) {
List<ILineDataSet> dataSets = mChart.getLineData().getDataSets();
for (int i = 0; i < dataSets.size(); i++) {
ILineDataSet dataSet = dataSets.get(i);
float textHeight = dataSet.getValueTextSize();
if (!shouldDrawValues(dataSet) || dataSet.getEntryCount() < 1)
continue;
// apply the text-styling defined by the DataSet
applyValueTextStyle(dataSet);
Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());
// make sure the values do not interfear with the circles
int valOffset = (int) (dataSet.getCircleRadius() * 1.75f);
if (!dataSet.isDrawCirclesEnabled())
valOffset = valOffset / 2;
mXBounds.set(mChart, dataSet);
float[] positions = trans.generateTransformedValuesLine(dataSet, mAnimator.getPhaseX(), mAnimator
.getPhaseY(), mXBounds.min, mXBounds.max);
ValueFormatter formatter = dataSet.getValueFormatter();
MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset());
iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x);
iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y);
for (int j = 0; j < positions.length; j += 2) {
float x = positions[j];
float y = positions[j + 1];
if (!mViewPortHandler.isInBoundsRight(x))
break;
if (!mViewPortHandler.isInBoundsLeft(x) || !mViewPortHandler.isInBoundsY(y))
continue;
Entry entry = dataSet.getEntryForIndex(j / 2 + mXBounds.min);
if (dataSet.isDrawValuesEnabled()) {
//drawValue(c, formatter.getPointLabel(entry), x, y - valOffset, dataSet.getValueTextColor(j / 2));
drawValue(c, formatter.getPointLabel(entry), x, y+textHeight*0.35f, dataSet.getValueTextColor(j / 2));
}
if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) {
Drawable icon = entry.getIcon();
Utils.drawImage(
c,
icon,
(int)(x + iconsOffset.x),
(int)(y + iconsOffset.y),
icon.getIntrinsicWidth(),
icon.getIntrinsicHeight());
}
}
MPPointF.recycleInstance(iconsOffset);
}
}
}
}
3.Set your own LineChart renderer to your modified drawValues class
LineChart mChart = (LineChart) mainActivity.findViewById(R.id.LineChart);
mChart.setRenderer(new CenteredTextLineChartRenderer(mChart,mChart.getAnimator(),mChart.getViewPortHandler()));
Run your code and manually adapt the 0.35f offset in your CenteredTextLineChartRenderer class
Now your text is always vertically centered!
IMPORTANT: With deleting the valOffset your label is not vertically centered as the text anchor is not in the center of your text label. So you have to insert a manual offset "textHeight*0.35f" (just try it out).

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

How to set boundary line of small pixel in JHeatChart heat map chart?

I use JHeatChart to generate a heat map chart as below; but we can see that if the value is same of neighbor pixel, they will combine to a big pixel because small pixel has not a boundary line. How can I set a boundary line for each pixel, so that they can separate clear when they have same value. And if I want to set several color not gradually color, what should i do?
In your JHeatChart, override the drawHeatMap() method and specify a different boundary color prior to invoking draw(), (untested):
private void drawHeatMap(Graphics2D chartGraphics, double[][] data) {
…
Graphics2D heatMapGraphics = heatMapImage.createGraphics();
for (int x=0; x<noXCells; x++) {
for (int y=0; y<noYCells; y++) {
// Set colour depending on zValues.
heatMapGraphics.setColor(getCellColour(data[y][x], lowValue, highValue));
int cellX = x*cellSize.width;
int cellY = y*cellSize.height;
heatMapGraphics.fillRect(cellX, cellY, cellSize.width, cellSize.height);
// Draw boundary
Rectangle2D block = new Rectangle2D.Double(
cellX, cellY, cellSize.width, cellSize.height));
heatMapGraphics.setPaint(boundaryColor);
heatMapGraphics.draw(block);
}
}
…
}

JFreeChart: how to set gradient paint for series in spider chart

I have a chart with this presentation:
But I require to do this:
How do I set correctly the gradient paint for series?. Here is what I have:
public class SpiderWebChartDemo1 extends ApplicationFrame {
public SpiderWebChartDemo1(String s) {
super(s);
JPanel jpanel = createDemoPanel();
jpanel.setPreferredSize(new Dimension(500, 270));
setContentPane(jpanel);
}
private static CategoryDataset createDataset() {
String s = "First";
String s3 = "Self leadership";
String s4 = "Organization leadership";
String s5 = "Team leadership";
DefaultCategoryDataset defaultcategorydataset = new DefaultCategoryDataset();
defaultcategorydataset.addValue(1.0D, s, s3);
defaultcategorydataset.addValue(4D, s, s4);
defaultcategorydataset.addValue(3D, s, s5);
return defaultcategorydataset;
}
private static JFreeChart createChart(CategoryDataset categorydataset) {
Color bckColor1 = Color.decode("#4282CE"); //Light blue
Color bckColor2 = Color.decode("#9BC1FF"); //Dark blue
Color axisColor = Color.decode("#DD0010"); //Red
SpiderWebPlot plot = new SpiderWebPlot(categorydataset);
Paint p = new GradientPaint(0,0,bckColor1,0,0,bckColor2);
plot.setSeriesPaint(p);
plot.setAxisLinePaint(axisColor);
JFreeChart chart = new JFreeChart("Spider Web Chart Demo 1"
, TextTitle.DEFAULT_FONT, plot, false);
LegendTitle legendtitle = new LegendTitle(plot);
legendtitle.setPosition(RectangleEdge.BOTTOM);
chart.addSubtitle(legendtitle);
return chart;
}
public static JPanel createDemoPanel() {
JFreeChart jfreechart = createChart(createDataset());
return new ChartPanel(jfreechart);
}
public static void main(String args[]) {
SpiderWebChartDemo1 spiderwebchartdemo1 = new SpiderWebChartDemo1("SpiderWebChartDemo1");
spiderwebchartdemo1.pack();
RefineryUtilities.centerFrameOnScreen(spiderwebchartdemo1);
spiderwebchartdemo1.setVisible(true);
}
}
I've seen gradient paint in bar charts, but not for spider charts. All I'm getting is transparent series.
Thanks.
You are setting the paint correctly however there are 2 things you should realize.
Gradient paints in java declare a start and end point. The first color will start at point 1 and transform into color 2 at point 2. If you use it to draw a polygon then the points are not relative to the polygons dimensions. Heres a picture to display, pt1 and pt2 in the picture are where your gradient start and end points are defined.
In an ideal world every setting is editable in a library but many times this just isnt the case. We can overcome that by overwriting methods in a subclass. You will need to override the SpiderWebPlot class and implement some of the painting methods. Heres a quick class I wrote up that does just that.
Take a look at the very end where it actually draws the polygon. I took this directly from the SpiderWebPlot source and altered the very end. To use this in your program call it like this
GradientSpiderWebPlot plot = new GradientSpiderWebPlot(categorydataset, Color.decode("#4282CE"), Color.decode("#9BC1FF"), .8f);
Here are the results
public class GradientSpiderWebPlot extends SpiderWebPlot {
private Color startColor, endColor;
private float alpha;
public GradientSpiderWebPlot(CategoryDataset data, Color startColor, Color endColor, float alpha) {
// TODO Auto-generated constructor stub
super(data);
this.startColor = startColor;
this.endColor = endColor;
this.alpha = alpha;
}
#Override
protected void drawRadarPoly(Graphics2D g2,
Rectangle2D plotArea,
Point2D centre,
PlotRenderingInfo info,
int series, int catCount,
double headH, double headW) {
Polygon polygon = new Polygon();
EntityCollection entities = null;
if (info != null) {
entities = info.getOwner().getEntityCollection();
}
// plot the data...
for (int cat = 0; cat < catCount; cat++) {
Number dataValue = getPlotValue(series, cat);
if (dataValue != null) {
double value = dataValue.doubleValue();
if (value >= 0) { // draw the polygon series...
// Finds our starting angle from the centre for this axis
double angle = getStartAngle()
+ (getDirection().getFactor() * cat * 360 / catCount);
// The following angle calc will ensure there isn't a top
// vertical axis - this may be useful if you don't want any
// given criteria to 'appear' move important than the
// others..
// + (getDirection().getFactor()
// * (cat + 0.5) * 360 / catCount);
// find the point at the appropriate distance end point
// along the axis/angle identified above and add it to the
// polygon
Point2D point = getWebPoint(plotArea, angle,
value / this.getMaxValue());
polygon.addPoint((int) point.getX(), (int) point.getY());
// put an elipse at the point being plotted..
Paint paint = getSeriesPaint(series);
Paint outlinePaint = getSeriesOutlinePaint(series);
Stroke outlineStroke = getSeriesOutlineStroke(series);
Ellipse2D head = new Ellipse2D.Double(point.getX()
- headW / 2, point.getY() - headH / 2, headW,
headH);
g2.setPaint(paint);
g2.fill(head);
g2.setStroke(outlineStroke);
g2.setPaint(outlinePaint);
g2.draw(head);
if (entities != null) {
int row = 0; int col = 0;
if (this.getDataExtractOrder() == TableOrder.BY_ROW) {
row = series;
col = cat;
}
else {
row = cat;
col = series;
}
String tip = null;
if (this.getToolTipGenerator() != null) {
tip = this.getToolTipGenerator().generateToolTip(
this.getDataset(), row, col);
}
String url = null;
if (this.getURLGenerator() != null) {
url = this.getURLGenerator().generateURL(this.getDataset(),
row, col);
}
Shape area = new Rectangle(
(int) (point.getX() - headW),
(int) (point.getY() - headH),
(int) (headW * 2), (int) (headH * 2));
CategoryItemEntity entity = new CategoryItemEntity(
area, tip, url, this.getDataset(),
this.getDataset().getRowKey(row),
this.getDataset().getColumnKey(col));
entities.add(entity);
}
}
}
}
// Plot the polygon
// Lastly, fill the web polygon if this is required
Rectangle2D rec = polygon.getBounds2D();
//Paint paint = getSeriesPaint(series);
// create linear vertical gradient based upon the bounds of the polygon.
Paint paint = new GradientPaint(new Point2D.Double(rec.getCenterX(),rec.getMinY()), startColor,
new Point2D.Double(rec.getCenterX(),rec.getMaxY()), endColor);
g2.setPaint(paint);
g2.setStroke(getSeriesOutlineStroke(series));
g2.draw(polygon);
if (this.isWebFilled()) {
// made this the variable alpha instead of the fixed .1f
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
alpha));
g2.fill(polygon);
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
getForegroundAlpha()));
}
}
}

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

Categories

Resources