Gantt Chart subtask color

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
ggilbert
Posts: 1
Joined: Thu Nov 13, 2008 1:12 pm

Gantt Chart subtask color

Post by ggilbert » Fri Nov 14, 2008 4:10 pm

Hi All,

I've searched the forums and have noticed this topic popping up quite often but haven't been able to find an example on how to achieve it.

I would like to be able to give each subtask in a taskseries a separate color. At the moment all tasks and subtasks in a series inherit the color from the series.

Is it possible to do this without having to extend the gantt renderer? If so does anyone have a working example they would like to share?

Sincerely

Gary Gilbert

david.gilbert
JFreeChart Project Leader
Posts: 11734
Joined: Fri Mar 14, 2003 10:29 am
antibot: No, of course not.
Contact:

Post by david.gilbert » Mon Nov 17, 2008 10:44 am

The subtasks use the series paint. You'd need to modify the GanttRenderer.drawTasks() method to make it behave differently.

A completely different approach you could try is to use the XYTaskDataset (which is more flexible about the number of tasks in a series) then override the getItemPaint() method in the renderer to change the task colours. See XYTaskDatasetDemo1 and XYTaskDatasetDemo2 in the JFreeChart Demo Collection to see how this approach (using an XYPlot and SymbolAxis) is used (you'd still have to write the code for overriding getItemPaint()).
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Support JFree via the Github sponsorship program

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

Post by marcinop » Wed Dec 10, 2008 10:55 am

Hi
Using XYTaskDataset is very simple and flexible.
Unfortunately, I have to use XYBarRenderer, and I can't draw percent of complete for each task.

How can I do to fix this?

By the way, I mentioned in another post about grouping mechanism.
Is it possible, or what should I do, to implement sth like categoryAxis for XYPlot. Especially the method

Code: Select all

setCategoryItemMargin(double a);
Best
Martin

I think I have found simple solution:
with this "//" I have marked my changes

Code: Select all

 @Override
    public void drawItem(Graphics2D g2, XYItemRendererState state,
            Rectangle2D dataArea, PlotRenderingInfo info,
            XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis,
            XYDataset dataset, int series, int item,
            CrosshairState crosshairState, int pass) {


        if (!getItemVisible(series, item)) {
            return;
        }
        IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset;

//
        XYTaskDataset xYTaskDataset = (XYTaskDataset) intervalDataset;
        TaskSeriesCollection tsc = xYTaskDataset.getTasks();
        TaskSeries ts = tsc.getSeries(series);
        Task t = ts.get(item);
//
        double value0;
        double value1;
        if (getUseYInterval()) {
            value0 = intervalDataset.getStartYValue(series, item);
            value1 = intervalDataset.getEndYValue(series, item);
        } else {
            value0 = getBase();
            value1 = intervalDataset.getYValue(series, item);
        }
        if (Double.isNaN(value0) || Double.isNaN(value1)) {
            return;
        }
        if (value0 <= value1) {
            if (!rangeAxis.getRange().intersects(value0, value1)) {
                return;
            }
        } else {
            if (!rangeAxis.getRange().intersects(value1, value0)) {
                return;
            }
        }

        double translatedValue0 = rangeAxis.valueToJava2D(value0, dataArea,
                plot.getRangeAxisEdge());
        double translatedValue1 = rangeAxis.valueToJava2D(value1, dataArea,
                plot.getRangeAxisEdge());
        double bottom = Math.min(translatedValue0, translatedValue1);
        double top = Math.max(translatedValue0, translatedValue1);

        double startX = intervalDataset.getStartXValue(series, item);
        if (Double.isNaN(startX)) {
            return;
        }
        double endX = intervalDataset.getEndXValue(series, item);
        if (Double.isNaN(endX)) {
            return;
        }
        if (startX <= endX) {
            if (!domainAxis.getRange().intersects(startX, endX)) {
                return;
            }
        } else {
            if (!domainAxis.getRange().intersects(endX, startX)) {
                return;
            }
        }

        RectangleEdge location = plot.getDomainAxisEdge();
        double translatedStartX = domainAxis.valueToJava2D(startX, dataArea,
                location);
        double translatedEndX = domainAxis.valueToJava2D(endX, dataArea,
                location);

        double translatedWidth = Math.max(1, Math.abs(translatedEndX - translatedStartX));

        double left = Math.min(translatedStartX, translatedEndX);
        if (getMargin() > 0.0) {
            double cut = translatedWidth * getMargin();
            translatedWidth = translatedWidth - cut;
            left = left + cut / 2;
        }

        Rectangle2D bar = null;
       //
        Rectangle2D barComplete = null;
//
        PlotOrientation orientation = plot.getOrientation();
        if (orientation == PlotOrientation.HORIZONTAL) {
            // clip left and right bounds to data area
            bottom = Math.max(bottom, dataArea.getMinX());
            top = Math.min(top, dataArea.getMaxX());
            bar = new Rectangle2D.Double(
                    bottom, left, top - bottom, translatedWidth);
//
            barComplete = new Rectangle2D.Double(
                    bottom, left + translatedWidth / 4, 
                    t.getPercentComplete() * (top - bottom), 
                    translatedWidth / 2);
//
        } else if (orientation == PlotOrientation.VERTICAL) {
            // clip top and bottom bounds to data area
            bottom = Math.max(bottom, dataArea.getMinY());
            top = Math.min(top, dataArea.getMaxY());
            bar = new Rectangle2D.Double(left, bottom, translatedWidth,
                    top - bottom);
        }

        boolean positive = (value1 > 0.0);
        boolean inverted = rangeAxis.isInverted();
        RectangleEdge barBase;

        if (orientation == PlotOrientation.HORIZONTAL) {
            if (positive && inverted || !positive && !inverted) {
                barBase = RectangleEdge.RIGHT;
            } else {
                barBase = RectangleEdge.LEFT;
            }
        } else {
            if (positive && !inverted || !positive && inverted) {
                barBase = RectangleEdge.BOTTOM;
            } else {
                barBase = RectangleEdge.TOP;
            }
        }
        /**/

        if (getShadowsVisible()) {
            getBarPainter().paintBarShadow(g2, this, series, item, bar, barBase,
                    !getUseYInterval());
        }
        getBarPainter().paintBar(g2, this, series, item, bar, barBase);
//
        g2.setPaint(Color.green);
        g2.fill(barComplete);
//
        if (isItemLabelVisible(series, item)) {
            XYItemLabelGenerator generator = getItemLabelGenerator(series,
                    item);
            drawItemLabel(g2, dataset, series, item, plot, generator, bar,
                    value1 < 0.0);
        }
        /**/

        // update the crosshair point
        double x1 = (startX + endX) / 2.0;
        double y1 = dataset.getYValue(series, item);
        double transX1 = domainAxis.valueToJava2D(x1, dataArea, location);
        double transY1 = rangeAxis.valueToJava2D(y1, dataArea,
                plot.getRangeAxisEdge());
        int domainAxisIndex = plot.getDomainAxisIndex(domainAxis);
        int rangeAxisIndex = plot.getRangeAxisIndex(rangeAxis);
        updateCrosshairValues(crosshairState, x1, y1, domainAxisIndex,
                rangeAxisIndex, transX1, transY1, plot.getOrientation());

        EntityCollection entities = state.getEntityCollection();
        if (entities != null) {
            addEntity(entities, bar, dataset, series, item, 0.0, 0.0);
        }

    }

pradeepbm
Posts: 1
Joined: Wed Aug 12, 2009 7:20 am
antibot: No, of course not.

Re: Gantt Chart subtask color

Post by pradeepbm » Wed Aug 12, 2009 7:30 am

I have similar requirement. How do we use XYTaskDataset when createGanttChart expects IntervalCategoryDataset. Can you please help me. :)

Code: Select all

JFreeChart chart = ChartFactory.createGanttChart(
            "Gantt Chart Demo",  // chart title
            "Task",              // domain axis label
            "Date",              // range axis label
            dataset,             // data
            true,                // include legend
            true,                // tooltips
            false                // urls
        );    

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

Re: Gantt Chart subtask color

Post by marcinop » Thu Aug 13, 2009 12:35 pm

Hi
See my code:

Code: Select all

import java.awt.Color;
import java.awt.Font;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import javax.swing.JPopupMenu;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.LegendItem;
import org.jfree.chart.LegendItemCollection;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.DateTickMarkPosition;
import org.jfree.chart.axis.SymbolAxis;
import org.jfree.chart.labels.ItemLabelAnchor;
import org.jfree.chart.labels.ItemLabelPosition;
import org.jfree.chart.plot.Plot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.ValueMarker;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.gantt.TaskSeries;
import org.jfree.data.gantt.TaskSeriesCollection;
import org.jfree.data.gantt.XYTaskDataset;
import org.jfree.data.xy.IntervalXYDataset;
import org.jfree.ui.GradientPaintTransformType;
import org.jfree.ui.Layer;
import org.jfree.ui.StandardGradientPaintTransformer;
import org.jfree.ui.TextAnchor;

/**
 *
 * @author Marcin
 */
public class JFCTotalMachineLoadTemplate extends PanningChartPanel {

    private SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss");
    private SimpleDateFormat axisDateFormat = new SimpleDateFormat("yy/MM/dd HH:mm:ss");
    private Font font1 = new Font("Helvetica", Font.PLAIN, 22);
    private Font font2 = new Font("TimesRoman", Font.PLAIN, 12);
    private Font font3 = new Font("Courier", Font.PLAIN, 4);
    private Font fontC12 = new Font("Courier", Font.TRUETYPE_FONT | Font.ITALIC, 12);

    public JFCTotalMachineLoadTemplate() {
//        super(ChartFactory.createXYBarChart(null,
//                null, true, null, null,
//                PlotOrientation.HORIZONTAL,
//                true, false, false));
        super(new JFreeChart(new PannableXYPlot()));
        PannableXYPlot xYPlot = (PannableXYPlot) getChart().getXYPlot();
        xYPlot.setOrientation(PlotOrientation.HORIZONTAL);
        setMouseMode(PanningChartPanel.MOUSE_PAN);
        prePlotConfig();
    }

    private void prePlotConfig() {
        getChart().removeLegend();
        final PannableXYPlot xyPlot = (PannableXYPlot) getChart().getXYPlot();
        xyPlot.setRangeAxisLocation(AxisLocation.BOTTOM_OR_RIGHT);
        xyPlot.setRangeGridlinesVisible(true);
        //
        EOperationsXYBarRenderer renderer = new EOperationsXYBarRenderer();
        EGradientXYBarPainter barPainter = new EGradientXYBarPainter();
        renderer.setBarPainter(barPainter);
        renderer.setDrawBarOutline(true);
        renderer.setAutoPopulateSeriesOutlinePaint(false);
        renderer.setBaseOutlinePaint(Color.black, true);
//        renderer.setBaseOutlineStroke(new BasicStroke(2, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND), false);
        renderer.setUseYInterval(true);
        renderer.setGradientPaintTransformer(
                new StandardGradientPaintTransformer(GradientPaintTransformType.CENTER_HORIZONTAL));
        renderer.setCompletePaint(ColorDefinition.getStateColor(CommonDefinitions.ACTUAL_AMOUNT));
        renderer.setIncompletePaint(ColorDefinition.getStateColor(CommonDefinitions.DEFICIT));

        xyPlot.setBackgroundAlpha(0f);
        xyPlot.setRenderer(renderer);
//
        xyPlot.setBackgroundPaint(new Color(0, 0, 0, 0f));
        getChart().setBackgroundImageAlpha(0f);
        getChart().setBackgroundPaint(new Color(0xFF, 0xFF, 0xFF, 0x00));
//        setBackground(new Color(0,0,0,0f));

        DateAxis dateAxis = /*(DateAxis) xyPlot.getRangeAxis();//*/ new DateAxis();
        dateAxis.setDateFormatOverride(axisDateFormat);
        dateAxis.setTickMarkPosition(DateTickMarkPosition.MIDDLE);
        xyPlot.setRangeAxes(new DateAxis[]{dateAxis});
        //
        setInitialDelay(100);
        setReshowDelay(100);
        setDisplayToolTips(true);
        setDismissDelay(20000);
        setDoubleBuffered(true);
        setAutoscrolls(false);
        getChart().setAntiAlias(true);

        //domain Yaxis
        SymbolAxis yAxis = new SymbolAxis("", new String[]{"Maszyna"});
//        yAxis.setCategoryMargin(0.1);
        yAxis.setLowerMargin(0.01);
        yAxis.setUpperMargin(0.01);
        yAxis.setLabel(null);
        yAxis.setVisible(true);
        //range Xaxis
        xyPlot.getRangeAxis().setLabel(null);
        xyPlot.setDomainAxis(yAxis);
        createLabels();
////        createLegends();
        createToolTips();
        addsOn();
        configPopUpMenu();
    /*listerner do zaznaczenia*/
//        addChartMouseListener(new ChartMouseListener() {
//
//            public void chartMouseClicked(ChartMouseEvent event) {
////                throw new UnsupportedOperationException("Not supported yet.");
//            }
//
//            public void chartMouseMoved(ChartMouseEvent event) {
////                throw new UnsupportedOperationException("Not supported yet.");
//            }
//        });
    /**/

    }

    private void configPopUpMenu() {
        JPopupMenu menu = getPopupMenu();
        menu.add(new ResetViewAction(this));
    }

    private void postPlotConfig() {
        Collection markers = getChart().getXYPlot().getRangeMarkers(Layer.FOREGROUND);
        for (Iterator valueMarker = markers.iterator(); valueMarker.hasNext();) {
            ValueMarker vm = (ValueMarker) valueMarker.next();
            vm.setValue(new java.util.Date().getTime());
        }
        List<String> symbols = new ArrayList<String>();
//        SymbolAxis yAxis = (SymbolAxis) getChart().getXYPlot().getDomainAxis();
        IntervalXYDataset dataset = (IntervalXYDataset) getChart().getXYPlot().getDataset();
        if (dataset != null) {
            int seriesCount = dataset.getSeriesCount();
            if (seriesCount > 0) {
                for (int i = 0; i < seriesCount; i++) {
                    String seria = (String) dataset.getSeriesKey(i);
                    symbols.add(i, seria);
                }

                String sva[] = new String[symbols.size()];
                symbols.toArray(sva);

                SymbolAxis yAxis = new SymbolAxis("", sva);
                getChart().getXYPlot().setDomainAxis(null);
                getChart().getXYPlot().setDomainAxis(yAxis);
            }
        }
    }

    /**
     * Jeżeli parametr <code>ISelectedTaskProperties</code> będzie nullem to załadują się domyślne ustawienia zaznaczenia.
     * Przy wartościach innych niż null, nalezy pamiętać o ustawieniu:
     * slected -- IllegalArgumentException, InnerPaint i OutLine Paint - NullPointerException!!
     * np.<code> List tmp = new ArrayList<Pair<IOperation, ISelectedTaskProperties>>();
    <br>tmp.add(new Pair(new Operations(161), null)); </br>
    <br>setSelectedOperation(tmp);</br></code>
     * @param iOperation
     */
    public void setSelectedOperation(List<Pair<AOperation, ISelectedTaskProperties>> iOperation) {
        getChart().setNotify(false);
        clearSelection();
        XYPlot plot = getChart().getXYPlot();
        XYTaskDataset dataset = (XYTaskDataset) plot.getDataset();
        TaskSeriesCollection timetable = dataset.getTasks();
        for (int i = 0; i < timetable.getSeriesCount(); i++) {
            TaskSeries taskSeries = timetable.getSeries(i);

            for (ETask<IOperation> obj : (List<ETask<IOperation>>) taskSeries.getTasks()) {
                if (obj.getObj() != null) {
                    for (Pair<AOperation, ISelectedTaskProperties> pair : iOperation) {
                        if (pair.getFirst().getId() == obj.getObj().getId()) {
                            if (pair.getLast() == null) {
                                obj.getSelectedTaskProperties().setSelected(true);
                                obj.getSelectedTaskProperties().setInnerTaskPaint(pair.getFirst().getStatusColor());
                                obj.getSelectedTaskProperties().setOutlinePaint(SelectedTaskProperties.DEFAULT_OUTLINE_PAINT);
                                obj.getSelectedTaskProperties().setOutlineStroke(SelectedTaskProperties.DEFAULT_OUTLINE_STROKE);
                            } else {
                                obj.getSelectedTaskProperties().setSelected(pair.getLast().isSelected());
                                obj.getSelectedTaskProperties().setInnerTaskPaint(pair.getLast().getInnerTaskPaint());
                                obj.getSelectedTaskProperties().setOutlinePaint(pair.getLast().getOutlinePaint());
                                obj.getSelectedTaskProperties().setOutlineStroke(pair.getLast().getOutlineStroke());
                            }
                        }
                    }
                }
            }
        }
        getChart().setNotify(true);
    }

    public void clearSelection() {
        getChart().setNotify(false);
        XYPlot plot = getChart().getXYPlot();
        XYTaskDataset dataset = (XYTaskDataset) plot.getDataset();
        TaskSeriesCollection timetable = dataset.getTasks();
        for (int i = 0; i < timetable.getSeriesCount(); i++) {
            TaskSeries taskSeries = timetable.getSeries(i);

            for (ETask<IOperation> obj : (List<ETask<IOperation>>) taskSeries.getTasks()) {
                if (obj.getObj() != null) {
                    obj.getSelectedTaskProperties().setSelected(false);
                }
            }
        }
        getChart().setNotify(true);
    }

    /**
     * Creates a chart.
     * 
     * @param timetable the timetable to display.
     */
    public void updateDataset(IntervalXYDataset timetable) {
        getChart().setNotify(false);
        XYPlot plot = getChart().getXYPlot();
        plot.setDataset(timetable);
        postPlotConfig();
        getChart().setNotify(true);
    }

    private void addsOn() {
        ValueMarker valueMarker = new ValueMarker(new java.util.Date().getTime());
        getChart().getXYPlot().addRangeMarker(valueMarker);
    }

    private LegendItemCollection createLegends() {
        LegendItemCollection legenditemcollection = new LegendItemCollection();
        XYPlot xyPlot = getChart().getXYPlot();
        LegendItem legenditem = new LegendItem("Czas do utylizacji.", "-",
                "Jest to ilość zmian pomniejszona o czasy przezbrojeń i montażu krązków.", "", Plot.DEFAULT_LEGEND_ITEM_BOX,
                ColorDefinition.getStateColor(CommonDefinitions.NOMINAL));//Color.blue);
        legenditemcollection.add(legenditem);
        xyPlot.setFixedLegendItems(legenditemcollection);
        return legenditemcollection;
    }

    private void createToolTips() {
        EOperationsXYBarRenderer renderer = (EOperationsXYBarRenderer) (getChart().getXYPlot()).getRenderer();
        renderer.setBaseToolTipGenerator(new EStandardXYToolTipGenerator(
                "<html>{3}</html>", dateFormat, dateFormat));
        renderer.setBaseItemLabelFont(fontC12, true);
    }

    private void createLabels() {
        EOperationsXYBarRenderer renderer = (EOperationsXYBarRenderer) (getChart().getXYPlot()).getRenderer();
        renderer.setBaseItemLabelGenerator(new EStandardXYItemLabelGenerator(
                "{1} - {2}", dateFormat, dateFormat));
        renderer.setBaseItemLabelsVisible(true);
        renderer.setBasePositiveItemLabelPosition(new ItemLabelPosition(ItemLabelAnchor.CENTER, TextAnchor.CENTER));
        renderer.setBaseItemLabelFont(fontC12, true);

    }
}
Best
Martin

John Matthews
Posts: 513
Joined: Wed Sep 12, 2007 3:18 pm

Re: Gantt Chart subtask color

Post by John Matthews » Tue Apr 28, 2015 3:41 pm

A complete example that overrides the renderer's getItemPaint() method, as suggested by @david.gilbert above, is examined here.

Locked