8

This question is related to Fill between two curves in pgfplots. which started me of.

However, I need to have just the area above the reference line filled.

\begin{tikzpicture}

\pgfplotstableread{data.dat}\data
\pgfplotstablesort[sort cmp={float >}]\coordinates{
0 0.9
33 0.9}

\pgfplotstablevertcat{\filledcurve}{\coordinates}
\pgfplotstablevertcat{\filledcurve}{\data}
\pgfplotstabletypeset{\filledcurve}

\begin{axis}[xlabel=Time of day, ylabel=Volume,xtick=\empty,ytick=\empty, axis x line=bottom, axis y line=left,enlargelimits=true]
\addplot[fill=gray!40,draw=none] table {\filledcurve}; 
\addplot[blue,mark=*,line width=2pt] table {\data};
\addplot[orange,mark=*,line width=2pt] table {\coordinates};
\end{axis}
\end{tikzpicture}

The datafile looks like:

0 0.2
1 0.217
2 0.255
3 0.288
6 0.58
7 0.9
8 1.02

In the code above I would like that just the area of \data above \coordinates is filled grey. Does anyone got a hint?

  • Welcome to Tex.SX. Please provide a full MWE including \documentclass. You could try to use the solution you liked to and fill the area between the curve and the line y=<y axis max> and use draw=none for that line so it does not get drawn. – Peter Grill Aug 15 '11 at 20:13
9

Here's a macro that generates a new table \interpolated that places points on your original data at every intersection with a certain y-value. You call it using \findintersections{<table macro}{<value>}.

To plot the area above the line, you would then use \addplot[fill,gray!20!white,no markers,line width=2pt] table [y=above line] {\interpolated};, or ...table [y=below line] for the area below the line.

In order to close the area properly in case your plot stops or begins above the cutoff line, you should add |- (current plot begin) at the end of the plot command.

\documentclass{article}

\usepackage{pgfplots}
\usepackage{pgfplotstable}
\usepackage{filecontents}
\usetikzlibrary{calc}
\begin{filecontents}{data.dat}
0 0.2
1 0.217
2 0.255
3 0.288
6 0.58
7 0.91
8 1.02
10 1.05
12 0.92
13 0.78
15 0.56
17 1.1
\end{filecontents}

\pgfplotstableread{data.dat}\data

\newcommand\findintersections[2]{
    \def\prevcell{#1}
    \pgfplotstableforeachcolumnelement{1}\of#2\as\cell{%
        \pgfmathparse{!or(
            and(
                \prevcell>#1,\cell>#1
            ),
            and(
                \prevcell<#1,\cell<#1
            )
        )}

        \ifnum\pgfmathresult=1
            \pgfplotstablegetelem{\pgfplotstablerow}{0}\of{\data} \let\xb=\pgfplotsretval
            \pgfplotstablegetelem{\pgfplotstablerow}{1}\of{\data} \let\yb=\pgfplotsretval
            \pgfmathtruncatemacro\previousrow{ifthenelse(\pgfplotstablerow>0,\pgfplotstablerow-1,0)}
            \pgfplotstablegetelem{\previousrow}{0}\of{\data} \let\xa=\pgfplotsretval
            \pgfplotstablegetelem{\previousrow}{1}\of{\data} \let\ya=\pgfplotsretval
            \pgfmathsetmacro\newx{
                \xa+(\ya-#1)/(ifthenelse(\yb==\ya,1,\ya-\yb) )*(\xb-\xa)    }

            \edef\test{\noexpand\pgfplotstableread[col sep=comma,row sep=crcr,header=has colnames]{
                0,1\noexpand\\
                \newx,#1\noexpand\\
            }\noexpand\newrow}
            \test
            \pgfplotstablevertcat\interpolated{\newrow}
        \fi
        \let\prevcell=\cell
    }
    \pgfplotstablevertcat\interpolated{#2}
    \pgfplotstablesort[sort cmp={float <}]\interpolated{\interpolated}
    \pgfplotstableset{
        create on use/above line/.style={
            create col/expr={max(\thisrow{1},#1)}
        },
        create on use/below line/.style={
            create col/expr={min(\thisrow{1},#1)}
        },
    }
}


\begin{document}
\pgfplotsset{compat=newest} % For nicer label placement

\findintersections{0.9}{\data}

\begin{tikzpicture}
\begin{axis}[
    xlabel=Time of day,
    ylabel=Volume,
    ytick=\empty,
    axis x line=bottom,
    axis y line=left,
    enlargelimits=true
    ]
\addplot[fill,gray!20!white,no markers,line width=2pt] table [y=above line] {\interpolated} |- (current plot begin);
\addplot[fill,yellow!20!white,no markers,line width=2pt] table [y=below line] {\interpolated} |- (current plot begin);
\addplot[orange,no markers,line width=2pt,domain=-1:20] {0.9};
\addplot[blue,line width=2pt,mark=*] table  {\data};
\end{axis}
\end{tikzpicture}

\end{document}
  • This works great, unless there is a file named data.dat.tex in the same directory. Took my quite some time to figure this out so thought I'd put that info here in case others encounter problems with this. – Peter Grill Aug 16 '11 at 16:06
  • Note though that this happens because I used the filecontents environment in my example to keep everything in one file. In a real application, the filecontents package and environment would not be used. – Jake Aug 16 '11 at 22:42
  • Hi Jake! I liked your answer a lot. I'm facing a similar problem as ThijsMuis. The main difference is that he is working with a curve and a constant and I'm working with two curves. Could you check out my question here and see if you can come up with a good solution? I have referenced this post in there already, as I definitely think that your findintersectons command could be a winner once it is set up to work with two curves: tex.stackexchange.com/questions/380323/… – Raphael Spaans Jul 12 '17 at 10:39
6

With the recently released pgfplots 1.10 and its fillbetween library (I learned it from Elke here) it's much easier.

  • Load the library: \usepgfplotslibrary{fillbetween}
  • Give the plots a name: \addplot[name path=curve,orange, ...
  • Add a fill plot:

      \addplot fill between[ 
        of = curve and line, 
        split, % calculate segments
        every even segment/.style = {yellow!20!white},
        every odd segment/.style ={gray!20!white}
      ]; 
    

Complete example:

Filled area between curve and line

\documentclass{article}
\usepackage{pgfplots}
\pgfplotsset{compat=newest}
\usepackage{pgfplotstable}
\usepgfplotslibrary{fillbetween}
\usetikzlibrary{calc}
\usepackage{filecontents}
\begin{filecontents}{data.dat}
0 0.2
1 0.217
2 0.255
3 0.288
6 0.58
7 0.91
8 1.02
10 1.05
12 0.92
13 0.78
15 0.56
17 1.1
\end{filecontents}

\pgfplotstableread{data.dat}\data

\begin{document}
\begin{tikzpicture}
\begin{axis}[
    xlabel=Time of day,
    ylabel=Volume,
    ytick=\empty,
    axis x line=bottom,
    axis y line=left,
    enlargelimits=true
    ]
  \addplot[name path=curve,orange,no markers,line width=2pt,domain=0:17] {0.9};
  \addplot[name path=line,blue,line width=2pt,mark=*] table  {\data};
  \addplot fill between[ 
    of = curve and line, 
    split, % calculate segments
    every even segment/.style = {yellow!20!white},
    every odd segment/.style ={gray!20!white}
  ]; 
\end{axis}
\end{tikzpicture}
\end{document}

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.