Gantt - Additional Features

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
jblancx
Posts: 24
Joined: Tue Sep 02, 2008 11:27 am
Location: Germany

Post by jblancx » Fri Sep 26, 2008 12:02 pm

Hallo takaya, guess you will have to extract the changes yourself from the pdfs and paste where necessary in the java files.

Try to see if you could implement both the Summary Tasks, milestone and the dependency lines.

I am expecting Silenus to give me a hint.. :)

Thanks

Silenius
Posts: 4
Joined: Mon Oct 02, 2006 6:32 pm
Contact:

Post by Silenius » Thu Dec 18, 2008 3:41 am

These changes were made in a project subject to a Non-Disclosure Agreement (NDA).

Sorry, but the following files are all the code I can share.

The modified JFreeChart source code files:
BarRenderer.java
GanttRenderer.java
GanttCategoryDataset.java
Task.java
TaskSeriesCollection.java
LineAndShapeGanttRenderer.java Image

Some code chunks on how to use them:
JFreeChartEngine.java
ActivityItemLabelGenerator.java

Maybe someone can post I nice working example afterwards, or maybe can JFree fix these issues on the JFreeChart.

Cheers,
Samuel

marcinop
Posts: 9
Joined: Wed Nov 12, 2008 1:27 pm

Post by marcinop » Thu Jan 08, 2009 8:59 am

Hi
Thanks for nice pice of code.
Could You post classes:

Milestone
Deliverable

Best
Martin

mrbcuda
Posts: 3
Joined: Sat Sep 19, 2009 10:16 pm
antibot: No, of course not.

Re: Gantt - Additional Features

Post by mrbcuda » Sat Sep 19, 2009 10:35 pm

Another way to draw dependency lines is to use the annotations feature. One can use the CategoryLineAnnotation class passing the end value of the first category and the start value of the second category. This way the programmer controls the mapping of dependency lines, which may not be trivially serial with the task sequence. This annotation class will draw the "dependency" lines as diagonal lines, which might not be what most expect in a Gantt chart. I created a subclass of CategoryLineAnnotation called GanttCategoryLineAnnotation that will draw a collection of line segments to square-off the connections. It isn't perfect because it will allow a line segment to cross a category shape, but overall it looks pretty good.

Code: Select all

package my.charts;

import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Stroke;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;

import org.jfree.chart.annotations.CategoryLineAnnotation;
import org.jfree.chart.axis.CategoryAnchor;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.Plot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.category.CategoryDataset;
import org.jfree.ui.RectangleEdge;


/**
 * A line annotation drawing class for Gantt charts.  Used to show dependencies among tasks
 * using the annotation framework.  Pass the end value of the first category and the
 * start value of the second category as the connection points in the constructor.
 * This is just like <code>CategoryLineAnnotation</code> except its drawing method
 * draws rectilinear line segments rather than diagonal lines.  Use the <code>minimumLength</code>
 * field to adjust the minimum length of a line segment emerging from a connection point.
 * @since Java 1.5 collections  
 */
public class GanttCategoryLineAnnotation extends CategoryLineAnnotation {

	/** generated serial version id */
	private static final long serialVersionUID = -2928265293236347594L;
	/** minimum line segment length at a category connection */
	private double minimumLength = 10.0;
	
	/**
	 * Sets the minimum segment length value.
	 * @param length
	 */
	public void setMinimumLength(double length) {
		minimumLength = length;
	}
	
	/**
	 * Gets the minimum segment length value.
	 * @return
	 */
	public double getMinimumLength() {
		return minimumLength;
	}
	
	/**
	 * Constructor merely calls the super class. 
	 * @param category1 first category to connect
	 * @param value1 end point of first category
	 * @param category2 second category to connect
	 * @param value2 start point of first category
	 * @param paint paint object, same as super class
	 * @param stroke stroke object, same as super class
	 */
	public GanttCategoryLineAnnotation(Comparable category1, double value1,
			Comparable category2, double value2, Paint paint, Stroke stroke) {
		super(category1, value1, category2, value2, paint, stroke);
	}

	/**
	 * Draws the annotation.
	 *
	 * @param g2  the graphics device.
	 * @param plot  the plot.
	 * @param dataArea  the data area.
	 * @param domainAxis  the domain axis.
	 * @param rangeAxis  the range axis.
	 */
	public void draw(Graphics2D g2, CategoryPlot plot, Rectangle2D dataArea,
					CategoryAxis domainAxis, ValueAxis rangeAxis) {

		CategoryDataset dataset = plot.getDataset();
		int catIndex1 = dataset.getColumnIndex(this.getCategory1());
		int catIndex2 = dataset.getColumnIndex(this.getCategory2());
		int catCount = dataset.getColumnCount();

		double pointX1 = 0.0f;
		double pointY1 = 0.0f;
		double pointX2 = 0.0f;
		double pointY2 = 0.0f;
		PlotOrientation orientation = plot.getOrientation();
		RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(
				plot.getDomainAxisLocation(), orientation);
		RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation(
				plot.getRangeAxisLocation(), orientation);

		// create a list of lines to draw
		List<Line2D> lines = new ArrayList<Line2D>();
		
		if (orientation == PlotOrientation.HORIZONTAL) {
			pointY1 = domainAxis.getCategoryJava2DCoordinate(
					CategoryAnchor.MIDDLE, catIndex1, catCount, dataArea,
					domainEdge);
			pointX1 = rangeAxis.valueToJava2D(this.getValue1(), dataArea, rangeEdge);
			pointY2 = domainAxis.getCategoryJava2DCoordinate(
					CategoryAnchor.MIDDLE, catIndex2, catCount, dataArea,
					domainEdge);
			pointX2 = rangeAxis.valueToJava2D(this.getValue2(), dataArea, rangeEdge);
			
			// work out the line segments
			if ( Math.abs( pointX2 - pointX1 ) < (minimumLength * 2.0) ) {
				// wrap-around case, add extra line segments
				double halfway = pointY1 + (pointY2-pointY1)/2.0;
				lines.add( new Line2D.Double(pointX1,pointY1,pointX1+minimumLength,pointY1));
				lines.add( new Line2D.Double(pointX1+minimumLength,pointY1,pointX1+minimumLength,halfway));
				lines.add( new Line2D.Double(pointX1+minimumLength,halfway,pointX2-minimumLength,halfway));
				lines.add( new Line2D.Double(pointX2-minimumLength,halfway,pointX2-minimumLength,pointY2));
				lines.add( new Line2D.Double(pointX2-minimumLength,pointY2,pointX2,pointY2));
			} else {
				// no wrap-around, use half the distance
				// could be fancier to avoid other shapes on the chart
				double halfway = pointX1 + (pointX2-pointX1)/2.0;
				lines.add( new Line2D.Double(pointX1,pointY1,halfway,pointY1) );
				lines.add( new Line2D.Double(halfway,pointY1,halfway,pointY2) );
				lines.add( new Line2D.Double(halfway,pointY2,pointX2,pointY2) );
			}
		}
		else if (orientation == PlotOrientation.VERTICAL) {
			pointX1 = domainAxis.getCategoryJava2DCoordinate(
					CategoryAnchor.MIDDLE, catIndex1, catCount, dataArea,
					domainEdge);
			pointY1 = rangeAxis.valueToJava2D(this.getValue1(), dataArea, rangeEdge);
			pointX2 = domainAxis.getCategoryJava2DCoordinate(
					CategoryAnchor.MIDDLE, catIndex2, catCount, dataArea,
					domainEdge);
			pointY2 = rangeAxis.valueToJava2D(this.getValue2(), dataArea, rangeEdge);
			
			// work out the line segments
			if ( Math.abs( pointX2 - pointX1 ) < (minimumLength * 2.0) ) {
				// wrap-around case, add extra line segments
				double halfway = pointX1 + (pointX2-pointX1)/2.0;
				lines.add( new Line2D.Double(pointX1,pointY1,pointX1,pointY1+minimumLength));
				lines.add( new Line2D.Double(pointX1,pointY1+minimumLength,halfway,pointY1+minimumLength));
				lines.add( new Line2D.Double(halfway,pointY1+minimumLength,halfway,pointY2-minimumLength));
				lines.add( new Line2D.Double(halfway,pointY2-minimumLength,pointX2,pointY2-minimumLength));
				lines.add( new Line2D.Double(pointX2,pointY2-minimumLength,pointX2,pointY2));
			} else {
				// no wrap-around, use half the distance
				// could be fancier to avoid other shapes on the chart
				double halfway = pointY1 + (pointY2-pointY1)/2.0;
				lines.add( new Line2D.Double(pointX1,pointY1,pointX1,halfway) );
				lines.add( new Line2D.Double(pointX1,halfway,pointX2,halfway) );
				lines.add( new Line2D.Double(pointX2,halfway,pointX2,pointY2) );
			}

		}

		g2.setPaint(this.getPaint());
		g2.setStroke(this.getStroke());

		// draw all the lines
		for ( Line2D line : lines ) {
			g2.drawLine( (int)line.getP1().getX(),
						 (int)line.getP1().getY(),
						 (int)line.getP2().getX(),
						 (int)line.getP2().getY());
		}
	}
}
To use it try something like this, where we have a plan containing a list of activities, and each activity can have some number of predecessors for which we want to show the dependency lines. The dataset here is a TaskSeriesCollection keyed by the activity name.

Code: Select all

int categoryRow = 0;

for ( Activity a : plan.getActivities() ) {
	int indexa = dataset.getColumnIndex( a.getName() );
	for ( Activity pa : a.getPredecessor() ) {
		int indexp = dataset.getColumnIndex( pa.getName() );
			GanttCategoryLineAnnotation annotation = 
			    new GanttCategoryLineAnnotation( pa.getName(),
										dataset.getEndValue(categoryRow, indexp).doubleValue(),
										a.getName(),
										dataset.getStartValue(categoryRow, indexa).doubleValue(),
										Color.DARK_GRAY,
										new BasicStroke(2.0f)
										);
				
		chart.getCategoryPlot().addAnnotation(annotation);
	}
}

davidFromSteria
Posts: 1
Joined: Fri Dec 04, 2009 12:00 pm
antibot: No, of course not.

Re: Gantt - Additional Features

Post by davidFromSteria » Fri Dec 04, 2009 12:32 pm

Hello,

I'm very interested by the work of Silenius. I managed to run examples with his modified sources.
But like everyone, I don't manage to see how can we add dependencies between Tasks. I think I don't have all necesary sources.
Silenius can you explain to us how you get your example works with task dependencies.

Thank you

David

Locked