# Label plots in pgfplots without entering coordinates manually

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
\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}


-

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]):

\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
]

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}


-
+1 keeps getting better! –  percusse Sep 23 '11 at 23:26
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 '12 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. –  Christian Feuersänger Feb 9 '12 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 '12 at 19:56

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}


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
]

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}


-