46

I like to label my plots directly, i.e. without using a legend, but instead using a pin node. So far, I have added the labels with manually specified coordinates, which means trial-and-error for data plots. A better way would be to let TikZ handle the positioning, but I don't know how to access the paths of the plots. Is there a way of adding a node to a plot in pgfplots, similar to the [pos=0.5] option for normal paths? Just adding node [pos=0.5] {x} to the \addplot command does not position the node on the line, but instead at some location in the lower left of the plot area, regardless of the pos value.

MWE:

\documentclass{article}
\usepackage{pgfplots}
\begin{document}
\begin{tikzpicture}
\begin{axis}
\addplot {x^2} node [pos=0.75,pin={-10:$x^2$},inner sep=0pt] {}; % This does not position the node on the line
\addplot {1.5*x^2};
\node at (axis cs:4,16) [pin={-10:$x^2$},inner sep=0pt] {}; % This is what I want, but without calculating the coordinates by hand
\node at (axis cs:4,24) [pin={170:$1.5 x^2$},inner sep=0pt] {};
\end{axis}
\end{tikzpicture}
\end{document}

pgfplots plot with nodes annotating the lines

2 Answers 2

46

The current version of pgfplots (>= 1.5.1) supports this as you expected it in the first place (i.e. using node[pos=0.75]):

enter image description here

\documentclass{article}
\usepackage{pgfplots}
\begin{document}
\thispagestyle{empty}
\begin{tikzpicture}
\begin{axis}
\addplot {x^2} node [pos=0.75,pin={-10:$x^2$},inner sep=0pt] {};
\addplot {1.5*x^2} node [pos=0.75,pin={170:$1.5 x^2$},inner sep=0pt] {};
\end{axis}
\end{tikzpicture}


\begin{tikzpicture}
 \begin{axis}[
        axis y line=center,
        axis x line=middle, 
        axis on top=true,
        xmin=-7,
        xmax=7,
        ymin=-4,
        ymax=4,
        clip=false
] 

\addplot[
    mark=none,
    domain=-4:6,
    samples=80,
    red,
    thick,
] {(x<-2)*-2 + (!(x<-2) && (x<3))*x + (!(x<3)) * 3}
    node[pos=0.1,pin=135:{\color{purple}$f(x)=-2$}] {}
    node[pos=0.6,pin=135:{\color{blue}$f(x)=x$}] {}
    node[pos=0.9,pin=135:{\color{green!70!black}$f(x)=3$}] {}
;
\end{axis}
\end{tikzpicture}

\end{document}

It is available for download on http://pgfplots.sourceforge.net/

3
  • Just discovered: pos=... does not work correctly if coordinates are read from an unsorted table, e.g. if x = {3 4 5 1 2} (sort of...). You'll have to sort the table first.
    – Christoph
    Feb 9, 2012 at 12:04
  • @Christoph from what I understand, a plot with the provided coordinates is completely different from one with sorted coords. I would say it is expected behavior that they look different... if you think it is an error, I would be interested in a complete bug report. Feb 9, 2012 at 17:40
  • @Christian "does not work correctly" was probably a bad choice of words. The pos option can simply not be used for unsorted data in order to place something along the path without some trial and error. It's not a bug.
    – Christoph
    Feb 9, 2012 at 19:56
18

I'm very keen on seeing other people's approaches to this, but in the meantime I'll post what I came up with:

The following code defines a new tikzstyle that creates a vertical path at a specified position in the plot, intersects that line with the plot, and adds something like a glorified pin (actually a line with a label at the end, which gives finer control over the text placement than just a pin) at the intersection.

It takes three arguments:

  • The position of the vertical line, specified as a fraction of the plot width (0 is the left edge, 1 the right)
  • The position of the label relative to the intersection of the line and plot. It can be given as polar coordinates ({45:1cm}) or a relative coordinate ({0.4cm,2cm}) and has to be enclosed in curly braces.
  • The label text, or, more accurately the label code: Either just the text, or something like {[red]left:<text>} to position the label to the left of the end of the label line and colour it red.

It's not exactly what I asked for, in that it does not position the label at a given distance along the graph, but rather at a fixed horizontal distance along the plot canvas. I find this to be more of a feature than a shortcoming, though, because it allows for nice alignments of the labels. Using other paths for defining the intersections, like horizontal lines or circles instead of the vertical line, is easily possible.

\documentclass{article}
\usepackage{pgfplots}
\usetikzlibrary{intersections}

\pgfkeys{/pgfplots/linelabel/.style args={#1:#2:#3}{name path global=labelpath,execute at end plot={
\path [name path global = labelpositionline]
(rel axis cs:#1,0) --
(rel axis cs:#1,1);
\draw [help lines,text=black,inner sep=0pt,name intersections={of=labelpath and labelpositionline}] (intersection-1) -- +(#2) node [label={#3}] {};},
}}

\begin{document}
\begin{tikzpicture}
\begin{axis}

\addplot +[linelabel=0.8:{-45:0.75cm}:{[blue]right:$x^2$}] {x^2};
\addplot +[linelabel=0.8:{-.6cm,.6cm}:$1.5x^2$] {1.5*x^2};

\end{axis}
\end{tikzpicture}
\end{document}

enter image description here


A way of placing labels at a specified position along the plot path, based on the answer to How determine slope of straight line (with auto scale of axis) in PGF plots

Alternatively, a decoration can be used to place label nodes. The decorations.markings library allows to place a node somewhere along a path. To use the decoration in \addplot, it has to be applied it through an every path/.style, which leads to a problem with the decoration being applied to itself. Thankfully, this has been solved already: Applying a postaction to every path in TikZ.

Also, PGFplots seems to have a problem with using TikZ nodes in the decorations. This can be avoided by using \pgfnode instead, which does exactly the same as \node, but looks a bit unusual.

Here's an example where a plotlabel style is defined (based on Altermundus' idea in How to label a path drawn using tikz with \draw plot?, and adapted to work around the described problems). You specify the label with plotlabel={<rel pos along path>}{<label text>}. To change the color of the label, \color{<color name>}<label text> can be used. The plotlabel option can be used multiple times to add different labels to the same plot.

\documentclass{article}
\usepackage{pgfplots}
\usetikzlibrary{decorations.markings}
\makeatletter
\tikzset{
    nomorepostaction/.code={\let\tikz@postactions\pgfutil@empty},
    plotlabel/.style 2 args={
        every path/.append style={
            postaction={
                nomorepostaction,
                decorate,
                decoration={
                    markings,
                    mark=at position #1 with {
                        \pgftransformresetnontranslations
           \pgfsetlinewidth{0.4pt}
           \pgfsetstrokecolor{black}
           \pgfpathmoveto{\pgfpointorigin}
           \pgfpathlineto{\pgfpoint{-0.3cm}{0.6cm}}
           \pgfusepath{stroke}
           \pgftransformshift{\pgfpoint{-0.3cm}{0.6cm}}
                        \pgfnode{rectangle}{east}{#2}{}{}
                    }
                }
            }
        }
    }
}
\makeatother

\begin{document}
\begin{tikzpicture}

 \begin{axis}[
        axis y line=center,
        axis x line=middle, 
        axis on top=true,
        xmin=-7,
        xmax=7,
        ymin=-4,
        ymax=4,
        clip=false
] 

\addplot[
    mark=none,
    domain=-4:6,
    samples=80,
    red,
    thick,
    plotlabel={0.1}{\color{purple}$f(x)=-2$},
    plotlabel={0.6}{\color{blue}$f(x)=x$},
    plotlabel={0.9}{\color{green!70!black}$f(x)=3$}
] {(x<-2)*-2 + (!(x<-2) && (x<3))*x + (!(x<3)) * 3};%
\end{axis}
\end{tikzpicture}
\end{document}

plot with label

0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .