%@ page import="java.io.*" %> <%@ page import="java.awt.*" %><%@ page import="java.awt.image.*" %><%@ page import="java.io.*" %><%@ page import="javax.swing.*" %><%@ page import="javax.imageio.*" %><%@ page import="com.sun.image.codec.jpeg.*" %><%! // A simple chart class, allowing drawing in a more convenient coordinate system private static class Chart { public Graphics2D g; int left, top, right, bottom, width, height; float scale_x, scale_y; public Chart(Graphics2D g, int left, int top, int right, int bottom, float scale_x, float scale_y) { this.g = g; this.left = left; this.top = top; this.right = right; this.bottom = bottom; this.scale_x = scale_x; this.scale_y = scale_y; this.width = this.right - this.left + 1; this.height = this.bottom - this.top + 1; } public void drawLine(int x1, int y1, int x2, int y2) { g.drawLine( left + (int)(x1 * scale_x), bottom - (int)(y1 * scale_y), left + (int)(x2 * scale_x), bottom - (int)(y2 * scale_y) ); } } %><% try { String[] days = request.getParameter("days").split(","); String[] workStrings = request.getParameterValues("work")[0].split(","); int[] work = new int[workStrings.length]; for (int i=0; i < workStrings.length; i++) { work[i] = Integer.parseInt(workStrings[i]); } int[] work2 = new int[0]; if (request.getParameterValues("work").length > 1) { workStrings = request.getParameterValues("work")[1].split(","); work2 = new int[workStrings.length]; for (int i=0; i < workStrings.length; i++) { work2[i] = Integer.parseInt(workStrings[i]); } } int width = 480; int height = 320; int border = 30; int chart_width = width - 2*border; int chart_height = height - 2*border; int chart_top = border; int chart_left = border; int chart_right = width - border; int chart_bottom = height - border; float day_width = (float)chart_width / days.length; int max_work = work[0]; for (int i=1; i < work.length; i++) { if (work[i] > max_work) { max_work = work[i]; } } float hour_height = (float)chart_height / max_work; Color background = Color.WHITE; // prepare some output BufferedImage buffer = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB); Graphics2D g = buffer.createGraphics(); g.setColor(background); g.fillRect(0,0,width,height); Chart c = new Chart(g, chart_left, chart_top, chart_right, chart_bottom, day_width, hour_height); // draw day numbers, ticks and grid lines for (int i=0; i < days.length; i++) { int x = chart_left + Math.round(i*day_width); g.setColor(Color.BLACK); //g.drawLine(x, chart_bottom, x, chart_bottom+5); c.drawLine(i, 0, i, -3); g.drawString(""+days[i], x + Math.round(day_width/8), chart_bottom + 15); g.setColor(Color.LIGHT_GRAY); if (i > 0) { //g.drawLine(x, chart_top, x, chart_bottom-1); c.drawLine(i, 0, i, max_work); } } // draw hour numbers, ticks and grid lines for (int i=0; i <= max_work; i += (hour_height >= 1 ? 10 : 25)) { int y = chart_bottom - Math.round(i*hour_height); g.setColor(Color.BLACK); g.drawLine(chart_left-5, y, chart_left, y); g.drawString(""+i, 0, y + 5); g.setColor(Color.LIGHT_GRAY); if (i > 0) { g.drawLine(chart_left+1, y, chart_right, y); } } // draw axes g.setColor(Color.BLACK); //g.drawPolyline(new int[] {chart_left, chart_left, chart_right}, new int[] {chart_top, chart_bottom, chart_bottom}, 3); c.drawLine(0, max_work, 0, 0); c.drawLine(0, 0, days.length, 0); // draw ideal burndown g.setColor(Color.BLACK); //g.drawLine(chart_left, chart_bottom - max_work * hour_height, chart_left + days.length * day_width, chart_bottom); c.drawLine(0, work[0], days.length, 0); // draw the secondary actual burndown g.setColor(new Color(0.0f,0.9f,0.9f,0.8f)); g.setStroke(new BasicStroke(2)); for (int i=1; i < work2.length; i++) { c.drawLine(i-1, work2[i-1], i, work2[i]); } // draw the actual burndown g.setColor(new Color(0,1.0f,0,0.8f)); g.setStroke(new BasicStroke(3)); for (int i=1; i < work.length; i++) { c.drawLine(i-1, work[i-1], i, work[i]); } // set the content type and get the output stream response.setContentType("image/png"); OutputStream os = response.getOutputStream(); // output the image as pnt ImageIO.write(buffer, "png", os); // output the image as a jpeg os.close(); } catch (Exception e) { %>
To get a burndown chart, call this page like this, this or this.
To get the resulting chart embedded into a MediaWiki page, make sure the URL ends in .png and that embedded display of external images are enabled on your MediaWiki installation. For example like this. Note that the generator currently ignores the format parameter (it always generates a PNG). It is just needed to make sure the URL ends with .png.
You can read more about this generator here.
| Date | Change |
|---|---|
| 20070926 | You can now specify a second work parameter with its own set of values. This is useful for doing burndown comparisions, like in a release planning. |
| 20070902 | Now correctly calculates the maximum work, instead of assuming the first work value is the highest. |
| 20070830 | The line is now drawn smoother and wider, making the chart look nicer. |