23

I am working on a pgfgantt document where all timelines are governed by real dates and where the titlelist shows week days based on the TikZ calendar library. I have used and modified the code from Custom calendar using TikZ to achieve this. The dates are converted to running numbers by using the pgfcalendardatetojulian.

What I am now looking to do is to colour the background canvas of the Gantt plot so that weekdays are white and Sundays (or weekends, Sat-Sun and holidays) are highlighted by vertical (light) red stripes. In the picture from the MWE this is equal to the area between the two red dashed lines under each red S (for Sunday). This means using the calendar library to determine what parts of the background canvas should be coloured reddish. The question is how this can be done?

MWE:

\documentclass{article}
\usepackage[a4paper]{geometry}
\usepackage[utf8]{inputenc} \usepackage[T1]{fontenc}
\usepackage{pgfgantt}       \usepackage{pgfcalendar}

\newcount\startdate
\newcount\daynum
\pgfcalendardatetojulian{2012-01-09}{\startdate}
%Redefinition from pgfcalendar.code.tex
\def\pgfcalendarweekdayveryshortname#1{%
  \translate{\ifcase#1M\or T\or W\or T\or F\or S\or S\fi}%
}
\protected\def\zzz{% Mod from https://tex.stackexchange.com/q/87550/19384
\pgfcalendarjuliantodate{\numexpr\startdate\relax}{\year}{\month}{\day}%
\pgfcalendarjuliantoweekday\startdate\daynum
{\ifnum\daynum=6\color{red}%
\tiny\pgfcalendarweekdayveryshortname{\daynum}%
\else
\tiny\pgfcalendarweekdayveryshortname{\daynum}%
\fi}%
\global\advance\startdate1}

\begin{document}
\noindent\resizebox{\textwidth}{!}{
  \begin{tikzpicture}
    \begin{ganttchart}[vgrid={*5{black,dotted},*2{red,dashed}},hgrid,%
                       y unit chart=5mm,x unit=3mm]{20}
      \gantttitle{Plan}{20} \\
      \gantttitlelist[
         title list options={var=\y,evaluate=\y  as \x using
         "{\zzz}"}
          ]{1,...,20}{1} \\
      \ganttbar{Task 1}{2}{5} \\
      \ganttlinkedbar{Task 2}{6}{8} \\
      \ganttlinkedmilestone{Goal}{9}\\
    \end{ganttchart}
  \end{tikzpicture}
}
\end{document}

Resulting image but with a reddish shaded area added to show what I would like to achieve for all 'red days':

enter image description here

  • @PeterJansson: Still not solved? Or is an answer acceptable? – Bobyandbob Aug 18 '18 at 22:04
  • @Bobyandbob, thanks for asking, two good answers, just my response time that is in years rather than shorter – Peter Jansson Aug 20 '18 at 8:02
6
+100

And so, after three years...

enter image description here

My approach is to draw TikZ rectangles on the background layer once the Gantt chart has been drawn.

In the example below, I've highlighted the units used in the calendar, so that you can easily redefine the rectangles as you need. (For example, this approach could also be used to define horizontal stripes that would correspond to task groups.)

Note that the coloring of weekdays (Saturday in light red, and Sundays in bold red) is implemented here in a much lighter way that in your original code (i.e. directly in the definition of \pgfcalendarweekdayletter -- cf. line 27)

What remains to do:

  • automatically compute the number of title lines (to be implemented on line 75)
  • automatically compute the number of chart lines (to be implemented on line 83)

enter image description here

Edit: Now automatically computes the number of weeks, when does the first week-end starts, and handles correctly if the first day of the calendar is a Sunday.

enter image description here

\documentclass[margin=10pt]{standalone}
    \usepackage[T1]{fontenc}
    \usepackage[utf8]{inputenc}

    \usepackage{tikz}
        \usetikzlibrary{backgrounds,calc,calendar}
    \usepackage{pgfgantt}
    \usepackage{pgfcalendar}

    \usepackage{calc}
    \usepackage{ifthen}

%\newcommand{\datenumber}[1]{% to compute the number of days between \mystartdate and a given date. Unused here
%   \pgfcalendardatetojulian{#1}{\dtnmbr}%
%   \advance\dtnmbr by -\mystrtdt%
%   \the\dtnmbr%
%}

%TO BE UPDATED ACCORDING TO YOUR NEEDS
\def\mystartdate{2016-08-29}%starting date of the calendar
\def\myenddate{2016-10-31}%ending date of the calendar
\def\myxunit{.5cm}%width of 1 day
\def\myyunittitle{.5cm}%height of 1 title line
\def\myyunitchart{1cm}%height of 1 chart line

\def\pgfcalendarweekdayletter#1{% define the name of weekdays + formatting
    \ifcase#1M\or T\or W\or T\or F\or \textcolor{red!50!white}{S}\or \textcolor{red}{\textbf{S}}\fi
}   

%Some calculation for plotting week-ends area
    \newcount\myenddatecount
        \pgfcalendardatetojulian{\myenddate}{\myenddatecount}

    \newcount\mystartdatecount
        \pgfcalendardatetojulian{\mystartdate}{\mystartdatecount}

    \newcount\mynumberofdays
        \mynumberofdays \myenddatecount\relax
        \advance \mynumberofdays by -\mystartdatecount\relax% so \mynumberofdays is now the number of days in the calendar

    \newcount\mynumberofweeks
        \mynumberofweeks\mynumberofdays\relax
        \advance \mynumberofweeks by -1\relax
        \divide \mynumberofweeks by 7\relax% so we have the number of full weeks

    \newcount\myfirstweekday
        \pgfcalendarjuliantoweekday{\mystartdatecount}{\myfirstweekday}

    \newcount\myfirstweekendshift
        \myfirstweekendshift 5\relax
        \advance\myfirstweekendshift by -\myfirstweekday\relax
        \ifnum \myfirstweekendshift=-1%if first day = sunday
            \advance \myfirstweekendshift by 7\relax% the first full weekend will thus begin one week after
        \fi

\begin{document}
    \begin{ganttchart}[%
            hgrid,
            vgrid,
            x unit = \myxunit,
            y unit title = \myyunittitle,
            title height = .75,
            y unit chart = \myyunitchart,
            time slot format=isodate,
            canvas/.append style={fill opacity=.1},
        ]%
        {\mystartdate}%
        {\myenddate}

            \gantttitlecalendar{year}\\
            \gantttitlecalendar{month=name}\\
            \gantttitlecalendar{day}\\
            \gantttitlecalendar{weekday=letter}\\
            %So we have 4 title lines
                \def\numbttitlelines{4} 

            \ganttgroup{Group 1}{\mystartdate}{2016-10-14} \\
            \ganttbar{Bar 1}{\mystartdate}{2016-09-05}\\
            \ganttbar{Bar 2}{2016-09-06}{2016-09-15}\\
            \ganttbar{Bar 3}{2016-09-15}{2016-10-12}\\
            \ganttmilestone{Milestone}{2016-10-14}%Note that whe didn't add \\ here! (so that last line is not blanck)
            %So we have 5 chart lines
                \def\numbtchartlines{5}

        \begin{scope}[|<->| ,thick] %Display units
            \fill [red] (0,0) circle [radius = 2.5pt] node [above left] {origin \texttt{(0,0)}};
            \draw [blue] (-1ex,0) --(-1ex,-\myyunittitle) node [midway, right] {\verb| 1*\myyunittitle|};
            \draw [blue!33!white] (-1em,0) --(-1em,-\numbttitlelines*\myyunittitle) node [midway, left] {\verb| <# of title lines>*\myyunittitle|};
            \draw [green!75!black] (-1ex,-\numbttitlelines*\myyunittitle) --(-1ex,-\numbttitlelines*\myyunittitle-1*\myyunitchart) node [midway, right, anchor=south west] {\verb| 1*\myyunitchart|};
            \draw [green] (-1em,-\numbttitlelines*\myyunittitle)--(-1em,-\numbttitlelines*\myyunittitle -\numbtchartlines*\myyunitchart) node [midway, left, anchor=south east] {\verb| <# of chart lines>*\myyunitchart|};
            \draw [yellow!50!orange] (0, 4pt-2*\myyunittitle) -- (\myxunit,4pt-2*\myyunittitle) node [right, anchor=base west] {\verb| 1*\myxunit|};
        \end{scope}

        \begin{scope}[on background layer]%display week-ends
            \foreach \i in {0,...,\the\mynumberofweeks}
            \fill [red!10]
                (\myfirstweekendshift*\myxunit+\i*7*\myxunit,-\numbttitlelines*\myyunittitle)% on the x-absis: shift the number of days before the first Saturday + 7days*\i; on the y-absis: shift down the number of title lines * their height
                rectangle 
                (\myfirstweekendshift*\myxunit+\i*7*\myxunit+2*\myxunit,-\numbttitlelines*\myyunittitle -\numbtchartlines*\myyunitchart);% on the x-absis: add two days (the week-end); on the y-absis: add the number of chartlines*their height
            \ifthenelse{\myfirstweekendshift=6}{% if the first day is a sunday, it is not grayed. So draw a rectangle for the first day of the chart.
                \fill [red!10]
                    (0,-\numbttitlelines*\myyunittitle)
                    rectangle
                    (1*\myxunit,-\numbttitlelines*\myyunittitle -\numbtchartlines*\myyunitchart);
            }{}
        \end{scope}
    \end{ganttchart}
\end{document}
5

And so, after two years...

Filling the canvas with the correct pattern is easy, but aligning it is not. I tried.

Drawing the highlights finding the correct anchors is possible but not simple at all, I believe.

I think the less obtrusive way to solve the problem is a clever vgrid pattern:

\documentclass{standalone}
%\usepackage[a4paper]{geometry}
\usepackage[utf8]{inputenc} \usepackage[T1]{fontenc}
\usepackage{pgfgantt}       \usepackage{pgfcalendar}

\newcount\startdate
\newcount\daynum
\pgfcalendardatetojulian{2012-01-09}{\startdate}
%Redefinition from pgfcalendar.code.tex
\def\pgfcalendarweekdayveryshortname#1{%
  \translate{\ifcase#1M\or T\or W\or T\or F\or S\or S\fi}%
}
\protected\def\zzz{% Mod from http://tex.stackexchange.com/q/87550/19384
\pgfcalendarjuliantodate{\numexpr\startdate\relax}{\year}{\month}{\day}%
\pgfcalendarjuliantoweekday\startdate\daynum
{\ifnum\daynum=6\color{red}%
\tiny\pgfcalendarweekdayveryshortname{\daynum}%
\else
\tiny\pgfcalendarweekdayveryshortname{\daynum}%
\fi}%
\global\advance\startdate1}

\begin{document}
\begin{tikzpicture}
\begin{ganttchart}[ vgrid = { *5{ black, dotted }%
                            , *1{ red, dashed
                                , double distance between line centers = 3mm
                                , transform canvas = { xshift =  1.5mm } }%
                            , *1{ red!50!white
                                , line width={3mm-0.4pt} % 0.4pt = thin
                                , transform canvas = { xshift = -1.5mm } }%
                            }
                  , hgrid
                  , y unit chart = 5mm
                  , x unit = 3mm
                  ]{1}{20}
  \gantttitle{Plan}{20}\\
  \gantttitlelist[ title list options = { var = \y
                                        , evaluate = \y as \x using "{\zzz}" }
                 ]{1,...,20}{1}\\
  \ganttbar{Task 1}{2}{5}\\
  \ganttlinkedbar{Task 2}{6}{8}\\
  \ganttlinkedmilestone{Goal}{9}\\
\end{ganttchart}
\end{tikzpicture}
\end{document}

We use the line on the left of sundays to draw a double dashed line, and the one on the right to highlight the column. Order is important, as double lines don't behave particularly well when dashed and don't allow transparency. We just cover up their middle stroke with the third line in the pattern, aptly thinned by 0.4pt.

Here is the result:

Final result

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.