I want to draw a figure similar to the attached imageenter image description here which is an example one from the SSN ontology.

However, the closest example I found yet is using auto colours and provides only one ring:

% A circular diagram of a TeX workflow
% Author: Stefan Kottwitz
% https://www.packtpub.com/hardware-and-creative/latex-cookbook
\smartdiagram[circular diagram:clockwise]{Edit,
  pdf\LaTeX, Bib\TeX/ biber, make\-index, pdf\LaTeX}

enter image description here

What is the best way to create such an image with tikz?

Edit: Thanks everybody! There are now multiple well working awesome versions in this question thread. It's beautyful what you can achieve with tikz!


5 Answers 5


Just for fun. I was curious to see whether one can get these shapes with the nonlinear transformation \polartransform from section 103.4.2 of the pgfmanual. The answer is more yes than no.

  • The node shapes and even the shadows do get transformed!

  • Even positioning works (except that left and right may lose their meaning)!

  • Bending the texts is surprisingly simple once one understands that the text along the path decoration of a "line" running from east to west (!) does the job. Yet it is not automatic.

  • The drawback: the anchors of the nodes go berserk if one wants to refer to them later. I was unable to figure out how to refer to them outside the transformed scope. Yet one can draw the arrows between the node at a given radius simply as arrow-shaped nodes. Fixed by @Symbol 1 here.

I think that with some amount of patience one could recreate the diagram you show. I didn't do that because I do not enjoy typing in text from a screen shot.

UPDATE: In order to mitigate the second point, I have created a macro that draws the nodes for you:

\BentNode[<options passed to the node>]{<name>}{<text>}{<options for text decoration>}{<options for the text>}

The options for the text decoration are things like textcolor=white whereas the options for the text are commands like \sffamily or \small. I plan to further improve these macros, but I plan to wait for some feedback before that.

2ND UPDATE: Built in the nice answers by @cfr here and @Symbol 1 here.


% from https://tex.stackexchange.com/a/434247/121799
% from the pgfmanual
\def\polartransformation{% from the pgfmanual section 103.4.2
} % note: the following should work with arbitrary (nonlinear) transformations
%from https://tex.stackexchange.com/a/433461/121799
\sbox\untransformedtip{\tikz{\draw [thick,-latex] (0,0) -- (0,1);}}
\tikzset{rednode/.style={draw,blur shadow,fill=red!50!black,align=center,
font=\sffamily,text opacity=0},
bluenode/.style={draw,blur shadow,fill=blue!10,align=center,
font=\sffamily,text opacity=0}
\newcommand{\BentNode}[5][]{% see https://tex.stackexchange.com/a/56405/121799
\path let \p1=(dummy.center) in 
\pgfextra{\settowidth{\mywidth}{\pgfinterruptpicture #5 #3\endpgfinterruptpicture}}
node[#1,minimum width={\mywidth*(2.06*28.4/\y1}](#2){};
\fill[decoration={text along path, text={|#5| #3},
  raise=-2.5pt,#4},decorate] (#2.east) -- (#2.west);
\node[circle,draw,fill=red!50!black,align=center,drop shadow,text=white,inner
sep=1pt,font=\small\sffamily] (sensor)
{Sensor,\\ Observation,\\ Sample, and\\ Actuator\\ (SOSA)};
\begin{scope}[transform shape nonlinear=true]
% within the scope, think of the first coordinate as angle in radians rescaled 
% by the radius, i.e. an arc length, and the second as radius
% positioning is rather convenient except that "left" and "right" lose their meaning
\BentNode[rednode,minimum height=12pt,at={(3.15,2.5)}]{semantic}{%
Semantic sensor network}{text color=white}{\sffamily}
\BentNode[rednode,minimum height=12pt,left=0.3cm of semantic]{system}{%
System Capabilities}{text color=white}{\sffamily}
\BentNode[bluenode,minimum height=12pt,right=0.3cm of semantic]{sample}{%
Sample relations}{text color=black}{\sffamily}
\draw[thick,-latex] (semantic) -- (system);
\draw[thick,-latex] (semantic) -- (sample);
\BentNode[bluenode,minimum height=11pt,at={(9.35,3.5)}]{PROV}{%
PROV alignment module}{text color=black}{\sffamily\small} %note: special characters need to be placed in curly brackets
\BentNode[rednode,minimum height=11pt,left=0.3cm of PROV]{OM}{%
O {\&} M alignment module}{text color=white}{\sffamily\small} %note: special characters need to be placed in curly brackets
\BentNode[bluenode,minimum height=11pt,right=0.3cm of PROV]{OBOE}{%
OBOE alignment module}{text color=black}{\sffamily\small} 
% this is the unfortunate part: I do not know how to properly refer to the nodes
% if I do it within the above scope, the arrow heads get messed up, if I do it
% outside, the coordinates get messed up, so I draw the arrows by hand
\draw[thick,-latex] (polar cs:semantic.south) -- (sensor);
\draw[thick,-latex] (polar cs:PROV.south) -- (sensor);
\draw[thick,-latex] (polar cs:OM.-20) -- (sensor);
\draw[thick,-latex] (polar cs:OBOE.-160) -- (sensor);

enter image description here

  • That looks amazing! When I put it into my larger document, some of the elements collapsed. Do you have any suggestion/feeling what could cause this? imgur.com/a/kknHDmi
    – Jannis
    Commented May 24, 2018 at 19:11
  • @Jannis Most likely there is an issue with the scale of the tikz picture. Do you have scale=... somewhere?
    – user121799
    Commented May 24, 2018 at 19:13
  • Two times, each time combined with figure \includegraphics[scale=0.2] and \includegraphics[scale=1.5] and I have font size 12 I can change the numbers to move them to suitable positions though that works fine. When I push the plates further out, and the text the same distance, the bending/rotation of the text doesnt match exactly anymore. Those I just tune by hand right? You didn't calculate them?
    – Jannis
    Commented May 24, 2018 at 19:16
  • 1
    @Andrew Thanks for looking into this! But so far I had never to do remember picture if I was accessing nodes inside the same picture, even when they were in different scopes. And overlay just stops the bounding box, it is equivalent to the pgfinterruptboundingbox environment according to percusse, and everything I've done so far with it is consistent with that. And I tried these options just now without success, but of course I may have made some mistake.
    – user121799
    Commented May 25, 2018 at 4:33
  • 1
    This is a crazy way to do this, but I guess you already know this :-).
    – cfr
    Commented May 26, 2018 at 2:56

Here is an approach using a multi-argument pic called "strip" to define the circular nodes (see section 18.2 of the tikz manual; version 3.0.1a). These are placed with commands like:

\pic at (0,0){strip={raduis, start angle, end angle, draw style, 
                     text colour, text}};

For example,

\pic at (0,0){strip={25mm, 16,-57,redshell, white, System Capabilities}};

To use this you need to explicitly work out appropriate angles but I think that this is probably unavoidable with a diagram like this. The end result is that you can produce a diagram like this:

enter image description here

using code like thus:

\usetikzlibrary{decorations.text, arrows.meta}

  myarrow/.style={thick, -latex},
  redcircle/.style={redshell,circle, align=center, font=\small\sffamily,
                    text=white, inner sep=1pt},
  % #1=radius, #2=start angle, #3=end angle, #4=draw style,
  % #5 text colour, #6=text
  pics/strip/.style args = {#1,#2,#3,#4,#5,#6}{
       code = {
        \draw[#4] (#2:#1-3mm) arc (#2:#3:#1-3mm)
             -- (#3:#1) -- (#3:#1+3mm) arc (#3:#2:#1+3mm)
             -- (#2:#1) -- cycle;
              decoration={text along path, text color=#5, text = {#6},
                          text align = {align = center}, raise = -0.5ex},
              decorate] (#2:#1) arc (#2:#3:#1);


    \node[redcircle](SOSA) at (0,0) {Sensor,\\Observation,\\Sample, and\\Actuator\\(SOSA)};
    \draw[myarrow] (156:42mm) -- (156:28mm);
    \draw[myarrow] (120:42mm) -- (120:28mm);
    \draw[myarrow] (120:22mm) -- (SOSA);
    \draw[myarrow] (219:32mm) -- (SOSA);
    \draw[myarrow] (260:32mm) -- (SOSA);
    \draw[myarrow] (301:32mm) -- (SOSA);
    \draw[myarrow] (145:45mm) arc [start angle=145, end angle=150, radius=45mm];
    \draw[myarrow] (142:25mm) arc [start angle=142, end angle=152, radius=25mm];
    \draw[myarrow]  (16:25mm) arc [start angle=16, end angle=24, radius=25mm];
    \pic at (0,0){strip={25mm, 16,-57,redshell, white,System Capabilities}};
    \pic at (0,0){strip={25mm,142, 24,redshell, white,Semantic Sensor Network (SSN)}};
    \pic at (0,0){strip={25mm,217,152,blueshell,black,Sample Relations}};
    \pic at (0,0){strip={35mm,224,159,redshell, white,O{\&}M Alignment module}};
    \pic at (0,0){strip={35mm,295,228,blueshell,black,PROV Alignment module}};
    \pic at (0,0){strip={35mm,370,299,blueshell,black,OBOE Alignment module}};
    \pic at (0,0){strip={45mm,145, 90,blueshell,black,SSNX Alignment module}};
    \pic at (0,0){strip={45mm,220,150,blueshell,black,Docle-UltraLite Alignment module}};

  • How could I change the font size in the \pic commands? just adding \small{text} causes my latex to crash? The text doesn't quite fit into the inner blue and red circles. Is there a way to push them out without manually retuning every position?
    – Jannis
    Commented May 25, 2018 at 8:13
  • Is it really necessary to manually try all coordinate combinations for arrows, or does something like a tikz editor exist for this?
    – Jannis
    Commented May 25, 2018 at 8:19
  • Thank you. I figured out the logic behind the coordinates and this is actually ok to tune. :-)
    – Jannis
    Commented May 25, 2018 at 8:30
  • @Jannis The font sizes are controlled by the two font=\small\sffamily lines in the tikzset{...} block. The way that I have done it everything is placed manually...as you say, the logic is not too hard but it is a little tedious. Marmot's automatic placement is certainly easier!
    – user30471
    Commented May 26, 2018 at 1:32

Only for sport, because I arrive late and with pirated code from excellent and powerful examples, only that altered to my way python type, by creating parametric objects, and structure the code to be readable, which I hope will be useful to another pirate xD.

RESULT: enter image description here


\usetikzlibrary{decorations.text, arrows.meta,calc,shadows.blur,shadings}
\renewcommand*\familydefault{\sfdefault} % Set font to serif family

% arctext from Andrew code with modifications:
%Variables: 1: ID, 2:Style 3:box height 4: Radious 5:start-angl 6:end-angl 7:text {format along path} 

\draw[#2] (#5:#4cm+#3) coordinate (above #1) arc (#5:#6:#4cm+#3)
             -- (#6:#4) coordinate (right #1) -- (#6:#4cm-#3) coordinate (below right #1) arc (#6:#5:#4cm-#3) coordinate (below #1)
             -- (#5:#4) coordinate (left #1) -- cycle;
        raise = -0.5ex, % Controls relavite text height position.
        text  along path,
        text = {#7},
        text align = center,        
    (#5:#4) arc (#5:#6:#4);

%arcarrow, this is mine, for beerware purpose...
%Function: Draw an arrow from arctex coordinate specific nodes to another 
%Arrow start at the start of arctext box and could be shifted to change the position
%to avoid go over another box.
%Var: 1:Start coordinate 2:End coordinate 3:angle to shift from acrtext box  
        let \p1 = (#1), \p2 = (#2), % To access cartesian coordinates x, and y.
            \n1 = {veclen(\x1,\y1)}, % Distance from the origin
            \n2 = {veclen(\x2,\y2)}, % Distance from the origin
            \n3 = {atan2(\y1,\x1)} % Angle where acrtext starts.
        in (\n3-#3: \n1) -- (\n3-#3: \n2); % Draw the arrow.

        % Environment Cfg
        font=\sf    \scriptsize,
        % Styles
        Center/.style ={
            font =\footnotesize\bf,
            inner sep=1pt,          
        RedArc/.style ={
            blur shadow, %Tikzedt not suport online view
        SkyArc/.style ={
            blur shadow, %Tikzedt not suport online view

    % Drawing the center
    \node[Center](SOSA) at (0,0) { Sensor \\ Observation, \\ Sample, and \\ Actuator \\(SOSA)};
    \coordinate (SOSA-R) at (0:1.2); % To make compatible with \arcarrow macro.

    % Drawing the Tex Arcs

    % \Arctext[ID][box-style][box-height](radious)(start-angl)(end-angl){|text-styles| Text}

    \arctext[SSN][RedArc][8pt](2.25)(180)(60){|\footnotesize\bf\color{white}| Semantic Sensor Network (SSN)};
    \arctext[SCap][RedArc][8pt](2.25)(50)(-20){|\footnotesize\bf\color{white}| System Capabilities};
    \arctext[SRel][SkyArc][8pt](2.25)(190)(255){|\footnotesize\color{black}| System Relation};
    \arctext[OMAM][RedArc][5pt](3.5)(205)(265){|\scriptsize\bf\color{white}| O{\&}M Alignment Module};
    \arctext[PROV][SkyArc][5pt](3.5)(270)(320){|\scriptsize| PROV Alignment Module};
    \arctext[OBOE][SkyArc][5pt](3.5)(-35)(20){|\scriptsize| OBOE Alignment Module};
    \arctext[DUAM][SkyArc][5pt](4.5)(215)(150){|\scriptsize| Dolce-UltraLite Alingment Module};
    \arctext[SSNX][SkyArc][5pt](4.5)(145)(80){|\scriptsize| SSNX Alingment Module};

        upper left=red,
        upper right=black!50,
        lower left=blue,
        lower right=blue!50,
        rounded corners = 8pt
        ][8pt](5.2)(100)(-20){|\footnotesize\bf\color{white}| You can create and use all the style options for shapes and text};

    %Drawing the Arrows
    %\arcarrow(above/below ID)(abobe/below ID)[shift]
    \arcarrow(below DUAM)(above SRel)[15];
    \arcarrow(below SSNX)(above SSN)[35];
    \arcarrow(below SSN)(SOSA-R)[60];
    \arcarrow(below right OMAM)(SOSA-R)[4];
    \arcarrow(below right PROV)(SOSA-R)[25];
    \arcarrow(below OBOE)(SOSA-R)[-5];

    %Same level Arrows
    \draw[myarrow] (left SSNX) -- (right DUAM);
    \draw[myarrow] (left SSN) -- (left SRel);
    \draw[myarrow] (left SCap) -- (right SSN);

     \draw[myarrow] (-5,-3.5) coordinate (legend) -- ++(.8,0) node[anchor=west] {owl: imports (extends)};
     \draw[RedArc] (legend)++(0,-0.4) rectangle ++(.8,-.3)++(0,.2) node[anchor=west] {normative};
     \draw[SkyArc] (legend)++(0,-1) rectangle ++(.8,-.3)++(0,.2) node[anchor=west, color=black] {non-normative};



Maybe the following code gets you started (did not look at the link of @samcarter until posting). Play around with the elements, copy and adjust. The ultimate compendium in all things TikZ is of course always worth searching, found here.


% Now we use the fading in another picture:
\begin{tikzpicture}[block/.style={rectangle, minimum size = 6mm, very thick, draw = red!50!black!50, rounded corners = 2mm}]
%\draw [help lines] grid (4,4);
\draw (0,0) node[block] (centre) {Centre text};
\path[postaction={decoration={text along path, text={text on an arc}}, decorate}] (30:2) arc(30:250:2);
\path[postaction={decoration={text along path, text={text on another arc}}, decorate}] (-30:3) arc(-30:30:3) node[midway, left] (second){};
\draw[->] (centre) -- (second);


enter image description here


The wheelchart package, which I wrote, can be used.

The text in the arcs is given to the key arc data. The direction of this text depends on the angle of the middle of the arc given by \WCmidangle which is used in the key arc data dir. This text is centered in the arc with arc data pos=0.5.

The gaps between the arcs are obtained with the keys gap polar=5 and gap radius=0.25.

The keys fill and draw for a slice are set in the key slices style. These are defined as none if the third variable for a slice (given by \WCvarC) is empty.

enter image description here

\fill[{\WCcolorsfill[1]}] (0,0) circle[radius=1.5]
  ] {%
    Sample, and\\
  arc data=\WCvarC,
  arc data dir={\WCmidangle<180?-1:1},
  arc data pos=0.5,
  arc data style={text color=\WCcolorstext[\WCvarB]},
  gap polar=5,
  gap radius=0.25,
  slices style={
  start angle=-30
  90/1/System Capabilities,
  140/1/Semantic Sensor Network (SSN),
  80/2/Sample Relations,
  start angle=190
  70/1/O{\&}M Alignment Module,
  70/2/PROV Alignment Module,
  70/2/OBOE Alignment Module,
  start angle=70
  80/2/SSNX Alignment Module,
  80/2/Dolce-UltraLite Alignment Module,

You must log in to answer this question.

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