8

I'm working with plots generated from matlab2tikz and I have several plot lines close to each other. Labeling them is proving difficult, but I believe a good looking solution could be to list the labels, and draw vertical lines to the appropriate plot.

Consider the following MWE:

\documentclass{standalone}
\usepackage{tikz, pgfplots}
\begin{document}
\begin{tikzpicture}[scale = 3.5]
    \begin{axis}[xmin=0,xmax=3,ymin=0,ymax=5]
        \addplot[domain=0:3,solid]{(x-4)^2}    node[pos=0.8,pin=-180:{Plot 1}] {} ;
        \addplot[domain=0:3,solid]{(x-3.5)^2}  node[pos=0.8,pin=-180:{Plot 2}] {} ;
        \addplot[domain=0:3,solid]{(x-3)^2}    node[pos=0.8,pin=-180:{Plot 3}] {} ;
    \end{axis}
\end{tikzpicture}
\end{document}

Which produces

enter image description here

Given I don't know where the addplot plots will show up (i.e around x = 3 or x = 300), is there a way to align the labels/nodes with equal height spacing, and have the pins extend until they reach the plot?

Torbjørn T.
  • 206,688
Holene
  • 6,920

1 Answers1

5

Mixing TikZ and pgfplots can become tricky. The scope apparently executes the given options directly.

But here you go. The following code doesn’t use the pin/pos combo. I hope that at least the output is something you wanted. (You actually can overwrite the at part of the every name plot node style with the /.append style handler.)

I’m open for suggestions.

There exist a few more keys in the name plot namespace:

The keys relative (relative x/relative y) turns on the rel axis cs which “uses the complete axis vectors as units. That means 'x = 0' denotes the point on the lower x axis range and 'x = 1' the point on the upper x axis range […].”

I recommend the relative [x|y] option for the placement of the nodes (as you have said, you don’t know whether x is around 3 or 300, which is probably also true for y, unless you specify xmin and so on).
You can mix absolute and relative coordinate. The last example uses absolute y values but relative x values.

The backwards key finds the intersection from the highest to the lowest y value.

The numberth intersection will be used (default: 1).

The anchor key can be used to correct the edge (if the anchor key of the node is neither west nor center nor east the line will not be perfectly horizontal. The anchor key can be used to fix this. The preceding . must be included.


A possible improvement could be that the \namePlot finds the nearest point/intersection on its own (with the option to look only to the left or the right).

Code

\documentclass[tikz]{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=1.8}
\usetikzlibrary{intersections}
\makeatletter
\newif\ifqrr@nameplot@relative@x
\newif\ifqrr@nameplot@relative@y
\newif\ifqrr@nameplot@backwards
\tikzset{
  every name plot node/.style={
    anchor=east,
    name=\pgfkeysvalueof{/name plot/name}
  },
  name plot/.code={\pgfkeys{/name plot/.cd,#1}}}
\pgfqkeys{/name plot}{
  at x/.initial=0,
  at y/.initial=0,
  at/.style args={#1,#2}{/name plot/at x={#1},/name plot/at y={#2}},
  path name/.initial=,
  name/.initial=,
  anchor/.initial=,
  number/.initial=1,
  relative x/.is if=qrr@nameplot@relative@x,
  relative y/.is if=qrr@nameplot@relative@y,
  relative/.style={
    /name plot/relative x=#1,
    /name plot/relative y=#1
  },
  relative/.default=true,
  backwards/.is if=qrr@nameplot@backwards
}
\newcommand*{\namePlot}[2][]{
  \scope[/name plot/.cd,#1]
    \ifqrr@nameplot@relative@y
      \path (rel axis cs:0,\pgfkeysvalueof{/name plot/at y}) coordinate (qrr@name plot@a)
            (rel axis cs:1,\pgfkeysvalueof{/name plot/at y}) coordinate (qrr@name plot@b);
    \else
      \path (axis cs:\pgfkeysvalueof{/pgfplots/xmin},\pgfkeysvalueof{/name plot/at y}) coordinate (qrr@name plot@a)
            (axis cs:\pgfkeysvalueof{/pgfplots/xmax},\pgfkeysvalueof{/name plot/at y}) coordinate (qrr@name plot@b);
    \fi
    \ifqrr@nameplot@backwards
      \path[name path=qrr@nameplot@horizontal line] (qrr@name plot@b) -- (qrr@name plot@a);
    \else
      \path[name path=qrr@nameplot@horizontal line] (qrr@name plot@a) -- (qrr@name plot@b);
    \fi
    \path[
      name intersections={%
        of=\pgfkeysvalueof{/name plot/path name} and qrr@nameplot@horizontal line,
        name=qrr@nameplot@intersections,
        sort by=qrr@nameplot@horizontal line}
    ];
    \node[
      at={(perpendicular cs: horizontal line through={(qrr@nameplot@intersections-\pgfkeysvalueof{/name plot/number})},
                            vertical line through={(\ifqrr@nameplot@relative@x rel \fi axis cs:\pgfkeysvalueof{/name plot/at x},0)})},
      every name plot node/.try,
      alias=qrr@nameplot@node
    ] {#2} (qrr@nameplot@node\pgfkeysvalueof{/name plot/anchor}) edge[every name plot edge/.try] (qrr@nameplot@intersections-\pgfkeysvalueof{/name plot/number});
  \endscope
}
\makeatother
\tikzset{every name plot edge/.append style={help lines, shorten >=2pt, -latex}}
\begin{document}
\begin{tikzpicture}[
  scale = 3.5,
  /name plot/at x=.7
  ]
    \begin{axis}[xmin=0,xmax=3,ymin=0,ymax=5]
        \addplot[domain=0:3, solid, name path global=plot 1]{(x-4)^2}  ;
        \addplot[domain=0:3, solid, name path global=plot 2]{(x-3.5)^2};
        \addplot[domain=0:3, solid, name path global=plot 3]{(x-3)^2}  ;
        \namePlot[path name=plot 1, at y=2, name=p1] {Plot 1}
        \namePlot[path name=plot 2, at y=3, name=p2] {Plot 2}
        \namePlot[path name=plot 3, at y=4, name=p3] {Plot 3}
        \path [<->] (p1) edge (p2) (p2) edge (p3);% just for fun
    \end{axis}
\end{tikzpicture}
\begin{tikzpicture}[
  scale = 3.5,
  name plot={
    at x=.15,
    relative x},
  every name plot edge/.append style={preaction={draw=white,ultra thick,-}},
  every name plot node/.append style={anchor=west}
  ]
    \begin{axis}[xmin=-1,xmax=10,ymin=-1.5,ymax=1.5]
        \addplot[domain=-1:10, solid, smooth, name path global=sinus]{sin(x/pi*180)}  ;
        \tikzset{/name plot/path name=sinus}
        \namePlot[at y=-.99 ] {$y=-1$}
        \namePlot[at y=-.5, number=2, backwards] {$y=-0.5$}
        \namePlot[at y=0] {$y=0$}
        \namePlot[at y=0, number=2, /tikz/every name plot node/.append style={text opacity=0}] {$y=0$}
    \end{axis}
\end{tikzpicture}
\end{document}

Output

enter image description here

enter image description here

Qrrbrbirlbel
  • 119,821
  • The execution order in pgfplots is different than TikZ, paths are not added consecutively but collected. We had some head scratching with Jake on this one too http://tex.stackexchange.com/questions/57582/change-linestyle-within-a-plot-to-add-dashed-trendline – percusse May 05 '13 at 17:35
  • @percusse Thanks for the comment/pointer. – Qrrbrbirlbel May 05 '13 at 18:22
  • Well not much of help though :) Is it OK to edit your answer if I can think of something else tomorrow? – percusse May 06 '13 at 18:45
  • @percusse Well the scope tip in one of your comments helped. The contents of \namePlot had to be grouped anyway … But sure, go ahead, if you find a more appropriate/stable way. – Qrrbrbirlbel May 07 '13 at 00:48
  • 1
    Nice solution! But should I experience difficulties when using the table option for providing points to \addplot? – Holene May 12 '13 at 11:04
  • @Holene Where there’s a path, there’s a way. I don’t know how only marks is processed internally, but otherwise there shouldn’t be any problems. – Qrrbrbirlbel May 12 '13 at 13:18
  • @Qrrbrbirlbel You're right, there's a way. Got some weird errors, but they originated from the \namePlot being positioned too high for the plot. It works now, thanks! – Holene May 12 '13 at 13:27
  • @Qrrbrbirlbel Could you please document your code? I think these are interesting plots so I really would like to understand how you got it going. thx – Lorzen Jul 16 '14 at 09:38