I'm using an XYBoxAnnotation to demarcate a rectangular area on a JFreeChart. I would like one side of the box to be "open", i.e go out to infinity. I tried setting the value to Double.POSITIVE_INFINITY but this did not seem to work. I also tried setting it to Double.MAX_VALUE, with no luck either. In these cases, the annotation doesn't even show up on the plot at all. And there are no exceptions thrown.
Below is a very simple version of my code in which I generate the XYBoxAnnotation and add it to the plot.
XYBoxAnnotation _axisMarker = new XYBoxAnnotation(xLow, yLow, Double.POSITIVE_INFINITY, yHigh, new BasicStroke(0.5F), Color.WHITE, Color.WHITE);
_plot.getRenderer().addAnnotation(_axisMarker, Layer.BACKGROUND);
EDIT:
I figured out that the reason the annotation wasn't showing up was because the x value for the annotation was much much larger than the axis scale. For some reason, this causes the annotation to not be visible until you zoom out enough.
Thanks to #trashgod's answer below, I came up with a solution. His answer didn't quite work for me since my plot allows zooming and you could see the edge of the box when you zoomed out.
First, I added a PlotChangeListener to listen for when the plot is zoomed:
// define PlotChangeListener to update the annotation when the plot is zoomed
private PlotChangeListener _zoomListener = new PlotChangeListener() {
#Override
public void plotChanged(PlotChangeEvent plotChangeEvent) {
if (_basisIsotope != null) {
updateAxisMarkers();
}
}
};
Then I created a function to re-draw the annotation based on the new plot bounds:
// function to re-draw the annotation
private void updateAxisMarkers() {
_plot.removeChangeListener(_zoomListener); // remove to prevent triggering infinite loop
// define xLow, yLow and yHigh...
double xHigh = _plot.getDomainAxis().getUpperBound() * 1.1;
XYBoxAnnotation _axisMarker = new = new XYBoxAnnotation(xLow, yLow, xHigh, yHigh, new BasicStroke(0.5F), Color.WHITE, Color.WHITE);
_plot.getRenderer().addAnnotation(annotation);
_plot.addChangeListener(_zoomListener); // add back
}
Double.MAX_VALUE is too large to scale to the relevant axis, but Double.MAX_VALUE / 2 works as well as any value larger than the upper bound of the axis. A better choice might be a value that exceeds the maximum value of the domain by some margin. The fragment below shades a plot of some Gaussian data with an XYBoxAnnotation that has domain bounds extending from 42 to the maximum domain value + 10%; the range bounds are ±1σ.
XYSeriesCollection dataset = createDataset();
JFreeChart chart = createChart(dataset);
Color color = new Color(0, 0, 255, 63);
double max = dataset.getSeries(0).getMaxX() * 1.1;
XYBoxAnnotation annotation = new XYBoxAnnotation(
42, -1, max, 1, new BasicStroke(1f), color, color);
chart.getXYPlot().getRenderer().addAnnotation(annotation);
Related
I am plotting the bar and line chart using the jfree chart .
One of the value axis is having the large label which doesn't fit in.
The label is suppose to be Greenhouse gas emissions (Tonnes).
Unable to find a way to control the width of the lable or wrap it.
final JFreeChart chart = ChartFactory.createBarChart(chartDetails.getTitle(),
chartDetails.getCategoryAxisLabel(), chartDetails.getValueAxisLabelLabelOne(),
dataSets.get(0),
PlotOrientation.VERTICAL, false,
true,
false
);
final ValueAxis rangeAxis = new NumberAxis(chartDetails.getValueAxisLabelLabelTwo());
plot.setRangeAxis(1, axis2);
final Font yaxisFont = getFont(yaxisFontAttibutes, xfactor);
rangeAxis.setLabelFont(yaxisFont);
rangeAxis.setLabelPaint(Color.decode(yaxisFontAttibutes.getColor()));
rangeAxis.setTickLabelFont(yaxisFont);
rangeAxis.setTickLabelPaint(Color.decode(yaxisFontAttibutes.getColor()));
rangeAxis.setLabelFont(yaxisFont);
rangeAxis.setAxisLineVisible(false);
rangeAxis.setLabelInsets(new RectangleInsets(2, 2, 2, 2));
return chart.createBufferedImage((int) (chartAttributes.getWidth() * xfactor),
(int) chartAttributes.getHeight() * xfactor);
Tried using LabelInsets but no use.
Note: I dont want to increase the heigth
Any sample code will be of great help.
Thanks
I have set the label at high end and rotated it .
But results are not desirable.
rangeAxis.setLabelLocation(AxisLabelLocation.HIGH_END);
rangeAxis.setLabelAngle(135);
I want to have x axis labels on my linechart (using MPAndroidChart) but no matter what I do, I can't get them to display. I have a method called setupChart which handles everything for that chart and this is what it looks like:
private void setupChart(LineChart chart, LineData data, int color) {
((LineDataSet) data.getDataSetByIndex(0)).setCircleColorHole(Color.WHITE);
((LineDataSet) data.getDataSetByIndex(0)).setCircleColor(color);
((LineDataSet) data.getDataSetByIndex(0)).setColors(dataColors);
data.getDataSetByIndex(0).setAxisDependency(YAxis.AxisDependency.LEFT);
((LineDataSet) data.getDataSetByIndex(0)).setDrawCircles(false);
// no description text
chart.getDescription().setEnabled(false);
// mChart.setDrawHorizontalGrid(false);
//
// enable / disable grid background
chart.setDrawGridBackground(false);
// enable touch gestures
chart.setTouchEnabled(true);
// disable scaling and dragging
chart.setDragEnabled(false);
chart.setScaleEnabled(false);
chart.setAutoScaleMinMaxEnabled(false);
// if disabled, scaling can be done on x- and y-axis separately
chart.setPinchZoom(false);
chart.setBackgroundColor(Color.parseColor("#dddddd"));
// set custom chart offsets (automatic offset calculation is hereby disabled)
chart.setViewPortOffsets(10, 0, 10, 0);
// add data
chart.setData(data);
// get the legend (only possible after setting data)
Legend l = chart.getLegend();
l.setEnabled(false);
chart.getAxisLeft().setEnabled(false);
chart.getAxisRight().setEnabled(false);
chart.getXAxis().setPosition(XAxis.XAxisPosition.BOTTOM);
chart.getXAxis().setEnabled(false);
chart.getXAxis().setDrawLabels(true);
chart.getXAxis().setTextColor(context.getResources().getColor(R.color.colorPrimary));
chart.getXAxis().setTypeface(Typeface.SANS_SERIF);
YAxis yAxis = chart.getAxisLeft();
yAxis.setAxisMinimum(0f);
yAxis.setAxisMaximum(100f);
final String[] weeks = new String[52];
for(int i = 0; i < weeks.length; i++) weeks[i] = "Week " + (i+1);
IAxisValueFormatter formatter = new IAxisValueFormatter() {
#Override
public String getFormattedValue(float value, AxisBase axis) {
return weeks[(int) value];
}
};
XAxis xAxis = chart.getXAxis();
xAxis.setGranularity(1f);
xAxis.setValueFormatter(formatter);
// animate calls invalidate()...
chart.animateXY(2000,2500);
}
I am not sure what I am doing wrong.
I solved this by increasing my offsets:
chart.setViewPortOffsets(60, 0, 50, 60);
linechart.setExtraBottomOffset(ff)
Modifying the offsets will help you to solve this problem as well.
Adjust the (ff) float values to get desired space.
My chart was not showing x-axis and y-axis. I remove this line the x-axis and y-axis appeared.
when this line exist lineChart.setViewPortOffsets(0,0,0,0) the chart look like this
when I remove this line lineChart.setViewPortOffsets(0,0,0,0) the chart look like this
I am using JFreeChart with Java to evaluate experimental results using the boxplot chart. I want to change the color and shape of the outliers and the farout entries.
This is how my plots currently look like when I use the normal BoxAndWhiskerRenderer:
I set up the renderer like this:
BoxAndWhiskerRenderer renderer = new BoxAndWhiskerRenderer();
renderer.setFillBox(true);
renderer.setSeriesPaint(0, Color.DARK_GRAY);
renderer.setSeriesPaint(1, Color.LIGHT_GRAY);
renderer.setSeriesOutlinePaint(0, Color.BLACK);
renderer.setSeriesOutlinePaint(1, Color.BLACK);
renderer.setUseOutlinePaintForWhiskers(true);
Font legendFont = new Font("SansSerif", Font.PLAIN, 15);
renderer.setLegendTextFont(0, legendFont);
renderer.setLegendTextFont(1, legendFont);
renderer.setMeanVisible(false);
Here, I cannot change the color and shape of the outliers. I would want them in black, not in the color of their series. And I would want them to look like small crosses rather than these big empty circles.
Also no farout values are shown at all and it seems like one of the outliers is cut off.
Then I found the ExtendedBoxAndWhiskerRenderer which allows to edit the color and shape of both outliers and farouts. This is what that looks like:
I set up the renderer like before, but I added two lines to set the color for the outliers and the farout entries:
renderer.setOutlierPaint(Color.BLACK); renderer.setFaroutPaint(Color.LIGHT_GRAY);
I also experimented with the shape of the outliers by reducing the cirle raduis in the extended renderer's implementation to 1.0 instead of 2.0:
private Shape createEllipse(Point2D point, double oRadius) {
Ellipse2D dot = new Ellipse2D.Double(point.getX(), point.getY(), oRadius*1.0, oRadius*1.0);
return dot;
}
However, I don't like these plots too much either. The Whiskers/Outlines of my plots aren't black anymore even though I set them to black. The mean is visible again even though I set it to invisible. And the huge number of outliers looks kind of ridiculous and makes me wonder why there are no farouts at the plots with the normal renderer at all.
If anyone could help me with these smaller appearance problems, that would be very nice. Otherwise, I will just take the current plots with the weird looking outliers and missing farouts...
While ExtendedBoxAndWhiskerRenderer is exemplary, it is somewhat dated, and much of its functionality has been incorporated into the mainline version. Your experiment suggests that the old renderer and new dataset are incompatible.
Because the outlier rendering methods are private, an alternative approach is to override the relevant draw*Item() method and let it invoke your own variations. You'll need to recapitulate the existing code, using the public accessors as required. In outline, the following variations demonstrate using Color.black, illustrated below.
plot.setRenderer(new BoxAndWhiskerRenderer() {
#Override
public void drawVerticalItem(Graphics2D g2, …) {
// existing code that calls the methods below
}
private void drawEllipse(Point2D point, double oRadius, Graphics2D g2) {
Paint temp = g2.getPaint();
g2.setColor(Color.black);
Ellipse2D dot = new Ellipse2D.Double(point.getX() + oRadius / 2,
point.getY(), oRadius, oRadius);
g2.draw(dot);
g2.setPaint(temp);
}
private void drawHighFarOut(double aRadius, Graphics2D g2, double xx,
double m) {
Paint temp = g2.getPaint();
g2.setColor(Color.black);
double side = aRadius * 2;
g2.draw(new Line2D.Double(xx - side, m + side, xx + side, m + side));
g2.draw(new Line2D.Double(xx - side, m + side, xx, m));
g2.draw(new Line2D.Double(xx + side, m + side, xx, m));
g2.setPaint(temp);
}
}
I am using JasperReports to create a line chart for my webapps.
I have successfully passed the dataset to the compiled report (created in iReport) and can see the data correctly.
However, I want to do some customization on the margin.
The value shown on the line chart is trimming for the highest value as there is no margin.
The X-Axis label is coming after few empty space from Y-Axis 0 value. I want to remove that margin and start the X-Axis from very close to the meeting point of X & Y.
Please see the picture:
I am using customized class which is defined in my webspps. I am able to change the font size and rotation of the label but don't know how to adjust margin.
public class LineChartCustomizer implements JRChartCustomizer {
#Override
public void customize(JFreeChart jFreeChart, JRChart jrChart) {
CategoryPlot plot = jFreeChart.getCategoryPlot();
DecimalFormat dfKey = new DecimalFormat("###,###");
StandardCategoryItemLabelGenerator labelGenerator = new StandardCategoryItemLabelGenerator("{2}", dfKey);
LineAndShapeRenderer renderer = new LineAndShapeRenderer();
renderer.setBaseItemLabelsVisible(true);
renderer.setBaseItemLabelGenerator(labelGenerator);
renderer.setBaseItemLabelFont(new java.awt.Font("SansSerif", java.awt.Font.PLAIN, 4));
renderer.setSeriesShape(0, ShapeUtilities.createDiamond(1F));
plot.setRenderer(renderer);
}
}
I think* you're looking for ValueAxis#setUpperMargin(double) and CategoryAxis#setLowerMargin(double). You can get the CategoryAxis and ValueAxis from plot.getDomainAxis() and plot.getRangeAxis(). Note that the margins are a percentage of the axis length and not pixel values.
* I'm not familiar with JasperReports, but it seems a little strange that you have a CategoryPlot in hand as opposed to an XYPlot. I would have expected the chart in your picture to have used an xy time series. I have only ever tested this with an XYPlot, so I'm not entirely sure how it will behave with a CategoryPlot.
I have been trying for the last week to find a way to make JFreeChart display something similar to the image below. Basically you are looking at three series (upper, middle, lower) with a fill inbetween. And underneath there is a (light green) fill color, or an area chart as some would perhaps call it - no meaning, just for looks.
The only thing really missing from what I have come up with is the last part: the fill underneath / area chart:
I even tried to subclass XYDifferenceRenderer and combine it with the renderer for Areachart, but I could not control the height of the areachart, basically filling up the plot to the top. So that was a no-go. Having created as simple rendererer to create rounded bar charts earlier, I thought that I might be able to change the code for XYDifferenceRenderer. But the code for XYDifferenceRenderer is quite a handful of geometry and inner workings of JFree chart, and the task was a bit overwhelming. So any tips on how to achieve this effect in any "normal" way (that does not involve hacking JFreeChart's inner workings)?
Found an old post describing how to use two renderers in the same plot, which was just the thing in this case.
To get a fill underneath you need to
create two new series
one is the lower bound of the difference plot
the other is the values at the bottom of the plot - often just zero. Easily got by calling plot.getRangeAxis().getLowerBound()
add them to a new dataset and add this to the plot
I was unaware that a plot could have several datasets. Turns out one can just use an index to access them.
create a new renderer for the "fill" dataset
create a new renderer
set the right fill paint
set the rendererer for the new dataset to be the new renderer
The code is something akin to the following, where the fill Paint obviously is up to you:
static void addFill(Plot plot) {
XYSeries lowerLimitSeries = ((XYSeriesCollection) (plot.getDataset())).getSeries(1);
XYSeriesCollection fillSet = new XYSeriesCollection();
double lowerBound = plot.getRangeAxis().getLowerBound();
fillSet.addSeries(lowerLimitSeries);
fillSet.addSeries(createLowerFillSeries(lowerLimitSeries, lowerBound));
plot.setDataset(1, fillSet);
Paint fillPaint = Color.GREEN;
XYDifferenceRenderer fillRenderer = new XYDifferenceRenderer(fillPaint, fillPaint, false);
fillRenderer.setSeriesStroke(0, new BasicStroke(0)); //do not show
fillRenderer.setSeriesStroke(1, new BasicStroke(0)); //do not show
plot.setRenderer(1, fillRenderer);
...
}
static XYSeries createLowerFillSeries(XYSeries lowerLimitSeries, double lowerLimit) {
int size = lowerLimitSeries.getItems().size();
XYSeries res = new XYSeries("lowerFillSeries");
for (int i = 0; i < size; i++) res.add(new XYDataItem(lowerLimitSeries.getX(i), lowerLimit));
return res;
}