I'm trying to write a method to create a simple graph of a normal distribution in JFreeChart and save it as a file. Here's an example of an output image that's pretty much exactly what I want
Notice that there are exactly 9 tick marks on the x axis. The center one is the mean of the distribution, and the rest of the ticks indicate standard deviations. There is one tick for each standard deviation from the mean.
Here's an example of another chart showing a normal distribution with a mean of 7 and a standard deviation of 5 and no other code changes.
This is not what I want. Suddenly there are only 8 tick marks, and there is no tick in the center to mark the mean. It appears that JFreeChart wants to only use nice round numbers instead of the odd 7 as the center tick.
I've tried reading other StackOverflow questions on forcing axis labels, but it appears everyone else wants to do this with some form of dates. It would help if I could simply specify 9 exactly values to put on the axis instead of them being autogenerated, but I don't know how to do that.
There's also one other problem. If you look at the curve near the sides of the graph, it is clipping below the frame of the plot and running into the tick marks. I want to add padding between the curve and the tick marks. I tried using something like plot.getRangeAxis().setRange(-0.01, 0.09);
but I ran into a bizarre problem where it appears that the height of the normal distribution is impacted by its width. Large means and standard deviations cause this to break miserably. (That makes zero sense from a statistics standpoint and I'm starting to question this normal distribution method.)
Anyway I basically need a way to force the chart to (a) add padding around the curve and (b) use exactly nine tick marks corresponding to the mean and four standard deviations out.
Here's my current code, which was mostly stolen online and trimmed to what appeared to be actually necessary:
static double mean = 7.0, sd = 5.0;
static Color line = new Color(0x6AA2A3);
static Color grey = new Color(0x555555);
public static void main(String[] args) throws IOException {
// Create the normal distribution
double minX = mean - (4 * sd), maxX = mean + (4 * sd);
Function2D normal = new NormalDistributionFunction2D(mean, sd);
XYDataset dataset = DatasetUtils.sampleFunction2D(normal, minX, maxX, 100, "Normal");
JFreeChart chart = ChartFactory.createXYLineChart(null, null, null, dataset, PlotOrientation.VERTICAL, false, false, false);
chart.setBorderVisible(true);
// Create and format the Plot
XYPlot plot = chart.getXYPlot();
plot.setBackgroundPaint(Color.WHITE);
plot.getRangeAxis().setVisible(false);
plot.setOutlineVisible(false);
// Format the X axis to look pretty
NumberAxis domain = (NumberAxis) plot.getDomainAxis();
domain.setRange(minX, maxX);
domain.setAxisLineVisible(false);
domain.setAutoRangeStickyZero(false);
domain.setTickUnit(new NumberTickUnit(sd));
domain.setTickLabelFont(new Font("Roboto", Font.PLAIN, 20));
domain.setTickLabelPaint(grey);
domain.setTickMarkStroke(new BasicStroke(2, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
domain.setTickMarkInsideLength(8);
domain.setTickMarkPaint(grey);
// Create a renderer to turn the chart into an image
XYLineAndShapeRenderer render = (XYLineAndShapeRenderer) plot.getRenderer(0);
render.setSeriesStroke(0, new BasicStroke(4));
render.setSeriesPaint(0, line);
// Output the final image
chart.setPadding(new RectangleInsets(5, 20, 20, 20));
BufferedImage image = chart.createBufferedImage(600,400);
File outFile = new File("graph.png");
outFile.createNewFile();
ImageIO.write(image, "png", outFile);
}
for request a),
plot.setAxisOffset(new RectangleInsets(5,5,5,5));
should do the trick.
For request b), the general recommendation is to override
refreshTicks(Graphics2D g2, AxisState state,Rectangle2D dataArea,RectangleEdge edge)
of the
ValueAxis
class and return a suitable List of ticks. Though doing so may look a bit intimidating, it is not if your logic is simple. You could try to simply add a NumberTick for the mean to the auto-generated tick list.