How can I set the TikZ label anchor explicitly?

I want to add a label to a node, but "inside" the node. Something like this:

The manual seems to indicate it might be possible, although no actual key is named.

The ... anchor point for the label node ... is determined in such a way that the label node will “face away” from the border of the main node. The anchor that is chosen depends on the position of the border point that is chosen and its position relative to the center of the main node and on whether the transform shape option is set. In general, the choice should be what you would expect, but you may have to set the anchor yourself in difficult situations. (emph. added)

But when I try an example like this:

\documentclass{standalone}
\usepackage{tikz}
\begin{document}
\tikz \node [circle,draw,label={[red,anchor=north]above:X}] {my circle};
\end{document}


I don't get any change in the position of the label node. It's still "outside."

Any ideas?

-
@Andrew: Good link. My use cases don't involve rotated nodes so I think I'm in the clear. But it's good to keep in mind that labels and pins are designed to "snap to" certain places. – Matthew Leingang Apr 4 '13 at 15:20

The problem here is that TikZ applies some automatication (namely the \tikz@auto@anchor macro) that will overwrite your set anchor.

We can either hijack a .code key that is applied very last in the node that is used internally for the label (yes, even after our own settings red, anchor=north) and which calls \tikz@auto@anchor:

\tikzset{label anchor/.style={tikz@label@post/.append style={anchor=#1}}}


or we let the macro that does the calculations relax a little bit (no additional key needed):

\tikzset{anchor/.append code=\let\tikz@auto@anchor\relax}


If the new and improved anchor key is now used more globally (say \tikz[anchor=<dir>]) it will also affects the nodes (and there is currently now way to get it back).

I also provide a key inside (you can even use it twice to reverse the effect).

Code

\documentclass[tikz]{standalone}
\usepackage{etoolbox}
\makeatletter
\tikzset{anchor/.append code=\let\tikz@auto@anchor\relax}
\tikzset{inside/.code=\preto\tikz@auto@anchor{\pgf@x-\pgf@x\pgf@y-\pgf@y}}
\makeatother
\begin{document}
\tikz\node [anchor=west, circle,draw,label={[red, anchor=north]above:X}] {my circle};
\tikz\node [anchor=west, circle,draw,label={[red, inside]below:X}] {my circle};
\end{document}


Output

-
For having the same effect on labels of coordinates one must add \let\tikz@compute@direction\pgfutil@gobble to the (improved) anchor key. – Qrrbrbirlbel May 22 '13 at 19:51
I put both tikzset commands from above into my preamble, but I still get : File ended while scanning use of \@argdef. :( – DaveBall aka user750378 Jul 8 '13 at 19:18
@DaveBallakauser750378 Are you sure? Have you included the other three lines as well? The inside key uses etoolbox\preto and both use @ in macro names so @ needs to be a letter, thus the \makeatletter-\makeatother sequence. – Qrrbrbirlbel Jul 8 '13 at 19:23
oh, ok. I'm using the version without inside and etoolbox now, and it works fine :) Thanks again. – DaveBall aka user750378 Jul 8 '13 at 19:27

Here is another approach exploiting the append after command option. The point is that, by referring to the anchor \tikzlastnode.center one might add, relatively to that position, labels.

The new style able to accomplish this task is my label:

\tikzset{my label/.style args={#1:#2}{
append after command={
(\tikzlastnode.center) node [#1] {#2}
}
}
}


A mwe:

\documentclass[tikz,png,border=10pt]{standalone}
\usepackage{tikz}
\tikzset{my label/.style args={#1:#2}{
append after command={
(\tikzlastnode.center) node [#1] {#2}
}
}
}
\begin{document}
\tikz \node [circle,draw,my label={red,above=0.15cm:X}] {my circle};
\tikz \node [circle,draw,my label={red,below=0.15cm:X}] {my circle};
\end{document}


which gives as results:

The style accepts at first argument any TikZ option, so it is easy to customize colors and position: indeed, it is better to adjust a bit the offset to not overlap the text.

If, instead, one knows that the label always has to be placed below the "north border", well, the best thing to do is to change the my label style into:

\tikzset{my label/.style args={#1:#2}{
append after command={
(\tikzlastnode.north) node [#1] {#2}
}
}
}


In such a way, just setting:

\tikz\node [circle,draw,my label={red,below:X}] {my circle};


allows to achieve the target.

A mwe:

\documentclass[tikz,png,border=10pt]{standalone}
\usepackage{tikz}
\tikzset{my label/.style args={#1:#2}{
append after command={
(\tikzlastnode.north) node [#1] {#2}
}
}
}
\begin{document}
\begin{tikzpicture}
\node [circle,draw,my label={red,below:X}] {my circle};
\node [circle,draw,my label={red,below=0.15cm:X},xshift=2cm] {my circle};
\end{tikzpicture}
\end{document}


The result:

Revision

The previous method suffer, as said in the comments, if more than one label should be introduced. But this is perfectly doable if rather than introducing nodes, we introduce coordinates with labels.

The code:

\documentclass[tikz,png,border=10pt]{standalone}
\usepackage{tikz}
\tikzset{my label/.style args={[#1]#2:#3}{
append after command={
(\tikzlastnode.center) coordinate[label={[label distance=0.1cm,#1]#2:#3}]
}
},
}
\begin{document}
\begin{tikzpicture}
\node [circle,
draw,
my label={[red]above:X},
my label={[green!70!blue]below:Y},
my label={[blue,yshift=0.3cm]above:Z}] {my circle};
\node [circle,
draw,
my label={[blue,yshift=-0.15cm]below:X},
my label={[green!70!blue]70:Z},
my label={[orange!80!red]130:Y},
xshift=2cm] {my circle};
\end{tikzpicture}
\end{document}


The result:

Notice: by using the label distance within the option definition, allows to not have the label text overlapped when one just says:

\tikz\node [circle,draw,my label={[red]below:X}] {my circle};

-
I've tried this method before, too. I found it did not work well with multiple labels. Because once you place the first label node, \tikzlastnode is that label node rather than the main node. – Matthew Leingang Apr 4 '13 at 15:16
Actually, you're right, but I think we can supply this problem by not introducing nodes. I'm going to edit my answer. – Claudio Fiandrino Apr 4 '13 at 17:58

You can also shift the label:

\documentclass{standalone}
\usepackage{tikz}
\begin{document}
\tikz \node [circle,draw,label={[red,yshift=-0.5cm]above:X}] {my circle};

\tikz \node [circle,draw,label={[red,label distance=-0.5cm]above:X}] {my circle};
\end{document}

-
Funny, I tried a negative label distance before I posted the question and couldn't get it to work. Maybe I was doing it wrong. – Matthew Leingang Apr 4 '13 at 12:15