5

How to draw a Technology Radar?

Situation and Problem

A friend of mine, who is not really into LaTeX has to create a diagram which they call "techonology radar". Apparently there are no best practices or available tools to create such a radial plot (apart from this js-implementation that I found). So instead of doing it all by hand in Powerpoint, I suggested to see what LaTeX has to offer. To my surprise, there does not seem to be a defined style or example for that. While I can see myself creating an appropriate diagram from this and this (which nicely loads data from an unavailable csv-file), I fear to get entagled in problems because I took the wrong attempt at it. Hence I ask here for a good starting point.

What is a Technology-Radar ?

From my understanding a technology radar is a diagram with concentric circles and discrete points, depicting technologies as blips on a radar. However, it can be modified to show only one half or one quarter of a circle. The radius describes time and the angle is used for categorization. While the radial axis can either display a concrete time-horizon with e.g. yearly (or five-year) periods or a priority (like "adopt, trial, assess and hold"), the angular axis is used more freely. Inside categories points are sometimes randomly distributed.

These types of diagrams are used in corporate business for predicting the future necessity of adoption of said technologies. Its origins and "how to build your own" are described here and there.

build your own tech radar

Question

How can I draw a technology radar diagram in LaTeX ?

Some requirements would be:

  • adjustable: e.g. to color, scale or shape nodes (blips); TikZ is preferable
  • easy to set up: use a tabular value scheme (like this or database-ish one comparable to this javascript with JSON?); or at least comprehensible node-code

Research and Further Ideas

Radar-like diagrams: one, two, three. An interactive radar with four categorizing areas and further information. Another opensource JavaScript example: here with the code on github.


Edits

Clarification

  • The amount of radial, angular segments is variable.
  • The exact angular position of all points (in a segment) might be random or known.
  • The shape might be a half-/quarter-circle.

An optimal solution shouldn't depend on the number of classes, nor should it always expect exact angular information.

Use-Cases

Two examples, as asked for, whose solutions should cover everything that might come up. My input-csv might be wrong, feel free to correct it. Names or attributes can be numbers too.

Yellow-marked parts are only for comprehension and shouldn't be drawn; I am not trusting my drawing skills.

Example 1 should allow for "stretched circles" (compare a) and sizes. The angular_position value should yield the angle in one class-arc like e.g.: $\theta = |\theta_\text{max} - \theta_\text{min}| \cdot (ap/2)$, where $ap$ is the angular_position.

\usepackage{filecontents}
\begin{filecontents*}{radar.csv}
name, class, radius_a, radius_b, angular_position, size
a, 1, 3, 12, 0.5, 1
b, 1, 14, 14, 0.8, 1
c, 1, 5, 5, -1, 1
d, 3, 10, 10, 1, 1
e, 2, 8, 8, 0.1, 2
\end{filecontents*}

Example 2 should allow for various shapes (compare g) and randomly assign the blips a angular position.

\usepackage{filecontents}
\begin{filecontents*}{radar.csv}
name, class, radius, shape
a, 1, 2.7, circle
b, 1, 1.5, circle
c, 2, 1.8, circle
d, 2, 1.8, circle
e, 3, 2.8, circle
f, 3, 0.1, circle
g, 4, 0.7, box
\end{filecontents*}
  • 1
    I think a polar plot with pgfplots is the easiest approach – Cuniye Datacu Jun 5 at 12:18
  • @CuniyeDatacu : I also thought that this direction would be best. But how would I use a csv-file or something similar like in three in order to make it easily reproducible/ adjustable ? – gr4nt3d Jun 5 at 12:21
  • Easy in lualatex. – JPi Jun 5 at 13:21
  • 1
    Just a reference: tex.stackexchange.com/a/158757/87876 – Money Oriented Programmer Jun 5 at 13:27
  • 1
    Here are some posts that allow you to do plots with only marks and some backgrounds. However, you would make things much easier for those who may write an answer if you provided some data file. – user121799 Jun 5 at 14:06
8

Here is a version in which you can load csv files into \addplots. I do not have data, so for the moment these are random plots, but if you provide data the plots can be fed with it.

\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{backgrounds,calc}
\usepackage{pgfplots}
\pgfplotsset{compat=1.16}
\usepgfplotslibrary{polar}
\begin{document}
\begin{tikzpicture}
\begin{polaraxis}[width=12cm,height=12cm,hide axis,xticklabels=\empty,yticklabels=\empty]
 \addplot[only marks,mark=*,red,samples=12] (5+80*rnd,10+90*rnd);
 \addplot[only marks,mark=*,blue,samples=12] (95+80*rnd,10+90*rnd);
 \addplot[only marks,mark=*,purple,samples=12] (185+80*rnd,10+90*rnd);
 \addplot[only marks,mark=*,orange,samples=12] (275+80*rnd,10+90*rnd);
 \path (0,0) coordinate (aux0) (0,30) coordinate (aux1) 
  (0,60) coordinate (aux2) (0,80) coordinate (aux3) (0,90) coordinate (aux4); 
\end{polaraxis}
\begin{scope}[on background layer]
\foreach \X [evaluate=\X as \GrayLevel using {int(5+8*(4-\X))}]in {4,3,2,1}
 \path let \p1=($(aux\X)-(aux0)$),\n1={veclen(\x1,\y1)} in 
  [fill=gray!\GrayLevel] (aux0) circle[radius=\n1];
 \draw[white,line width=4pt] let \p1=($(aux4)-(aux0)$),\n1={veclen(\x1,\y1)} in
  ($(aux0)+(0:\n1)$) -- ($(aux0)+(180:\n1)$)  
  ($(aux0)+(90:\n1)$) -- ($(aux0)+(270:\n1)$);
\end{scope}
\end{tikzpicture}
\end{document}

enter image description here

\documentclass[tikz,border=3.14mm]{standalone}
\usepackage{filecontents}
\begin{filecontents*}{radar.csv}
angle,radius,class
270.378,94.8494,1
348.654,33.3956,1
262.655,67.4501,2
0.283019,67.4716,4
192.991,86.0843,3
287.466,58.6273,3
56.841,22.3808,2
20.0212,88.9344,1
2.1422,97.1612,2
222.892,78.5474,2
302.461,24.0801,2
64.9934,47.812,4
102.387,72.817,4
23.9928,44.8714,3
58.0144,70.8405,1
237.81,16.9276,2
99.7234,64.6314,4
43.7404,59.2716,3
154.042,97.1341,1
105.706,46.9238,4
8.538,32.7798,2
223.455,88.5721,4
193.885,86.7844,1
255.534,68.7281,1
142.8,71.204,2
287.631,37.2925,3
95.7389,31.695,3
146.019,62.2968,2
96.9872,19.9715,4
342.846,55.9929,4
217.888,83.0623,4
105.241,79.9873,2
353.252,76.9388,1
33.0193,32.6544,2
150.789,69.5382,1
120.266,78.7951,3
255.166,35.7227,4
57.3896,10.8303,4
27.6518,75.0756,3
282.238,75.4462,2
17.1386,84.2318,1
148.593,35.1021,2
295.303,31.174,3
342.586,55.4607,1
143.964,44.5899,1
14.5737,84.3482,2
153.079,71.1151,3
271.775,44.0174,4
268.151,15.8369,2
58.6009,80.1182,1
\end{filecontents*}
\usetikzlibrary{backgrounds,calc}
\usepackage{pgfplots}
\pgfplotsset{compat=1.16}
\usepgfplotslibrary{polar}
\begin{document}
\begin{tikzpicture}
\begin{polaraxis}[width=12cm,height=12cm,hide axis,xticklabels=\empty,yticklabels=\empty]
 \addplot[scatter,only marks, point meta=explicit symbolic,
 scatter/classes={1={mark=square*,blue},
            2={mark=triangle*,red},3={mark=o,draw=black},4={mark=*,draw=orange}}]
 table[x=angle,y=radius,col sep=comma,meta=class] {radar.csv};
 \path (0,0) coordinate (aux0) (0,30) coordinate (aux1) 
  (0,60) coordinate (aux2) (0,80) coordinate (aux3) (0,100) coordinate (aux4); 
   \legend{Class 1,Class 2,Class 3,Class 4}
\end{polaraxis}
\begin{scope}[on background layer]
\foreach \X [evaluate=\X as \GrayLevel using {int(5+8*(4-\X))}]in {4,3,2,1}
 \path let \p1=($(aux\X)-(aux0)$),\n1={veclen(\x1,\y1)} in 
  [fill=gray!\GrayLevel] (aux0) circle[radius=\n1];
 \draw[white,line width=4pt] let \p1=($(aux4)-(aux0)$),\n1={veclen(\x1,\y1)} in
  ($(aux0)+(0:\n1)$) -- ($(aux0)+(180:\n1)$)  
  ($(aux0)+(90:\n1)$) -- ($(aux0)+(270:\n1)$);
\end{scope}
\end{tikzpicture}
\end{document}

enter image description here

  • Thank you very much! This looks excellent! Could you maybe include the texts for the different radial and angular categories ( angular ones just outside )? The thing is I am unsure how the csv-file would have to look. There are a few problems for me to be able to specify (not knowing what can be done with TikZ): (1) The amount of categories is flexible; (2) in one case one would have the random pattern, while in the other one might want to specify the angle (maybe indirectly by some other value). – gr4nt3d Jun 5 at 15:11
  • @gr4nt3d The "texts for the different radial and angular categories" are known as a legend in the pgfplots world and easy to add. But this will make much more sense if there is data. One possible way to set up the data would be in three columns: angle, radius, and scatter class, where the latter determines the shape and color (pgfplots supports this, too). And yes, you can let pgfplots let compute the angle if you have a prescription/formula. Why don't you just add a small data file to your question? – user121799 Jun 5 at 15:16
  • Concerning the "legend", I was referring to rather axis ticks. Zoom in on the picture above. However, this detailed style-problem is not important right now. I will think of a data-format, where I place dummy-numbers, if the placement should be randomized. I suppose such a dummy (lets say -1) could be caught and a random number could be inserted then? – gr4nt3d Jun 5 at 15:31
  • @gr4nt3d I added quickly something and will be back online in a few hours. – user121799 Jun 5 at 15:34
  • 2
    @gr4nt3d Sorry to be so frank, but IMHO you do not understand the purpose of this site correctly. It is meant to help you when you are stuck somewhere with your own attempts, not to find someone who translates your directions in code (even though this wouldn't be difficult). You have received two answers to your original question. The way to go is to accept one of these, try something on your own and then come back if you are stuck. – user121799 Jun 5 at 18:29
7

I am sorry, I do not read the whole text, but I read

position each entry randomly in its segment

So, if you want a circle with some randomly shapes in it, I can help you.

Maybe you can use this as a workaround:

enter image description here

\documentclass[demo]{standalone}
\usepackage[dvipsnames]{xcolor}
\usepackage{tikz}
\usetikzlibrary{plotmarks}

\begin{document}
\begin{tikzpicture}[]
\foreach \s in  {100,85,50,30}{
\draw[fill=lightgray!\s, draw=white] circle[radius=3*\s/100];
}
% CoSy
\draw[white, thick] (-3,0) -- (3,0); 
\draw[white, thick] (0,-3) -- (0,3); 

% I 
\foreach \No in {0,...,33}{
\pgfmathsetmacro{\RandAngle}{random(0,900)/10} 
\pgfmathsetmacro{\RandRadius}{random(0,3000)/1000} 
\fill[blue] (\RandAngle:\RandRadius) circle (2pt);
}

% II
\foreach \No in {0,...,55}{
\pgfmathsetmacro{\RandAngle}{random(900,1800)/10} 
\pgfmathsetmacro{\RandRadius}{random(0,3000)/1000} 
\draw[green!70!black] plot[mark=triangle*,mark size=2.75pt, mark options={fill=green}] coordinates{(\RandAngle:\RandRadius)};
}

% III
\foreach \No in {0,...,55}{
\pgfmathsetmacro{\RandAngle}{random(1800,2700)/10} 
\pgfmathsetmacro{\RandRadius}{random(0,3000)/1000} 
\draw[orange!85!black] plot[mark=triangle*,mark size=2.75pt, mark options={fill=orange!50!yellow}] coordinates{(\RandAngle:\RandRadius)};
}

\end{tikzpicture}
\end{document}
  • Thank you very much, this looks very good! I am unsure though, how I would add points at certain completely or partially (just the radius) known positions. – gr4nt3d Jun 5 at 15:15
2

I wanted to list my test-file such that everybody can profit from my recherche. This contains different approaches from the above and other answers (see this and that) to achieve the various blip-forms.

results in: result

\documentclass[border=3mm]{standalone}

\usepackage{
    tikz,
    pgfplots
    }
\usetikzlibrary{
    backgrounds,
    calc,
    shapes.geometric,       % regular polygon shape
    decorations.markings,   % halo
    decorations.text        % text along path
}
\pgfplotsset{compat=1.15} 
\usepgfplotslibrary{polar}

% https://tex.stackexchange.com/questions/495067/dashed-trajectory-encircling-two-segments/495140comment1252594_495140
\newcounter{halo}

\tikzset{
    record path/.style={
        /utils/exec=\tikzset{halo pars/.cd,#1},
        decorate,
        decoration={
            markings,
            mark=at position 0 with
            {
                \setcounter{halo}{1}%\typeout{\pgfdecoratedpathlength}
                \path 
                    (0pt,{\pgfkeysvalueof{/tikz/halo pars/dist}})
                    coordinate (halo-L-\number\value{halo})
                    (0pt,{-1*\pgfkeysvalueof{/tikz/halo pars/dist}})
                    coordinate (halo-R-\number\value{halo})
                    ({-\pgfkeysvalueof{/tikz/halo pars/dist}/sqrt(2)},{-\pgfkeysvalueof{/tikz/halo pars/dist}/sqrt(2)})
                    coordinate (halo-A-1)
                    ({-\pgfkeysvalueof{/tikz/halo pars/dist}},{0pt})
                    coordinate (halo-A-2)
                    ({-\pgfkeysvalueof{/tikz/halo pars/dist}/sqrt(2)},{\pgfkeysvalueof{/tikz/halo pars/dist}/sqrt(2)})
                    coordinate (halo-A-3);
                    %
                    \pgfmathsetmacro{%
                        \mystep%
                    }{%
                        (\pgfdecoratedpathlength-2*\pgfkeysvalueof{/tikz/halo pars/step})/int(1+(\pgfdecoratedpathlength-2*\pgfkeysvalueof{/tikz/halo pars/step})/\pgfkeysvalueof{/tikz/halo pars/step})%
                    }
                    %
                    \xdef\mystep{\mystep}
                },
            mark=between positions 
                \pgfkeysvalueof{/tikz/halo pars/step} 
                and 
                {\pgfdecoratedpathlength-\pgfkeysvalueof{/tikz/halo pars/step}} 
                step 
                \mystep pt 
                with {
                    \stepcounter{halo}
                    \path
                        (0pt,{\pgfkeysvalueof{/tikz/halo pars/dist}})
                        coordinate (halo-L-\number\value{halo})
                        (0pt,{-1*\pgfkeysvalueof{/tikz/halo pars/dist}})
                        coordinate (halo-R-\number\value{halo});
            },
            mark=at position 1 with {
                \stepcounter{halo}
                %
                \path 
                    (0pt,{\pgfkeysvalueof{/tikz/halo pars/dist}}) 
                    coordinate (halo-L-\number\value{halo})
                    (0pt,{-1*\pgfkeysvalueof{/tikz/halo pars/dist}}) 
                    coordinate (halo-R-\number\value{halo})
                    ({\pgfkeysvalueof{/tikz/halo pars/dist}/sqrt(2)},{\pgfkeysvalueof{/tikz/halo pars/dist}/sqrt(2)}) 
                    coordinate (halo-B-1)
                    ({\pgfkeysvalueof{/tikz/halo pars/dist}},{0pt}) 
                    coordinate (halo-B-2)
                    ({\pgfkeysvalueof{/tikz/halo pars/dist}/sqrt(2)},{-\pgfkeysvalueof{/tikz/halo pars/dist}/sqrt(2)}) 
                    coordinate (halo-B-3);
                %
                \xdef\LstHaloCoords{(halo-A-1) (halo-A-2) (halo-A-3)}
                %
                \foreach \XX in {1,...,\number\value{halo}}
                {
                    \xdef\LstHaloCoords{\LstHaloCoords\space (halo-L-\XX)}    
                }
                %
                \xdef\LstHaloCoords{\LstHaloCoords\space (halo-B-1) (halo-B-2) (halo-B-3)}
                %
                \foreach \XX in {\number\value{halo},\the\numexpr\number\value{halo}-1,...,1}
                {
                    \xdef\LstHaloCoords{\LstHaloCoords\space (halo-R-\XX)}
                }
            }
        }
    },
    halo/.style={
        insert path={
            plot[smooth,samples at={1,...,\number\value{bracep}},variable=\x] (bracep-\x)
        }
    },
    halo/.style={
        insert path={
            plot[smooth cycle] coordinates {\LstHaloCoords} 
        }
    },
    halo pars/.cd,
    dist/.initial = 4pt,
    step/.initial = 2pt
}


% https://tex.stackexchange.com/questions/66216/draw-arc-in-tikz-when-center-of-circle-is-specified/66220#66220
\def\centerarc[#1](#2)(#3:#4:#5)% Syntax: [draw options] (center) (initial angle:final angle:radius)
{%
    \draw[#1] ($(#2)+({#5*cos(#3)},{#5*sin(#3)})$) arc (#3:#4:#5);%
}

\def\centerarcpolar[#1](#2,#3)(#4:#5)% Syntax: [draw options] (center, radiushelper) (initial angle:final angle:radius)
{%
    \draw[#1]%
        let \p1=($(#3)-(#2)$),\n1={veclen(\x1,\y1)} in %
        ($(#2)+({\n1*cos(#4)},{\n1*sin(#4)})$) arc (#4:#5:\n1);%
}

\def\centerarcpolarpath[#1](#2,#3)(#4:#5)% Syntax: [draw options] (center, radiushelper) (initial angle:final angle:radius)
{%
    \path[#1]%
        let \p1=($(#3)-(#2)$),\n1={veclen(\x1,\y1)} in %
        ($(#2)+({\n1*cos(#4)},{\n1*sin(#4)})$) arc (#4:#5:\n1);%
}


\usepackage{filecontents}
\begin{filecontents*}{test_radar.csv}
    angle,radius,scale,class,name,color
    270,    9,      3/5 * 3/2 + 1/2,     1,      a,      orange
    338,    3,      3/5 * 3/2 + 1/2,     2,      b,      lightgray
    262,    6,      3/5 * 3/2 + 1/2,     3,      c,      green
    0,      10,     3/5 * 3/2 + 1/2,     2,      d,      lightgray
    136,    5,      3/5 * 3/2 + 1/2,     4,      e,      red
    78,     7.5,    3/5 * 3/2 + 1/2,     1,      f,      blue
\end{filecontents*}

\begin{document}
\begin{tikzpicture}
    \begin{polaraxis}[
        width   = 8cm,
        height  = 8cm,
        xmin    = 0,
        xmax    = 360, 
        ymin    = 0,
        ymax    = 10,
        ytick   = {0,3,...,10},
        xticklabels=\empty,
        rotate=-30,
        visualization depends on={value \thisrow{name}  \as \labelname},
        visualization depends on={value \thisrow{scale} \as \labelscale},
        visualization depends on={value \thisrow{color} \as \labelcolor}
    ]
        \addplot[
            scatter/classes={
                1={
                    mark                = text,
                    text mark as node   = true,
                    text mark           = \labelname,
                    text mark style={
                        circle,
                        fill opacity = 0.5,
                        draw  = black,
                        fill  = \labelcolor,
                        scale = \labelscale,
                        inner sep   = 2pt,
                        draw
                    }
                },
                2={
                    mark                = text,
                    text mark as node   = true,
                    text mark           = \labelname,
                    text mark style={
                        circle,
                        draw  = black,
                        fill  = \labelcolor,
                        scale = \labelscale,
                        inner sep   = 0.6pt,
                        line width  = 1.4pt,
                        font        = \tiny\bfseries,
                        draw
                    }
                },
                3={
                    mark                = text,
                    text mark as node   = true,
                    text mark           = \labelname,
                    text mark style     = {
                        rectangle,
                        draw  = black,
                        fill  = \labelcolor,
                        scale = \labelscale,
                        inner sep = 1pt,
                        outer sep = 2pt,
                        draw
                    }
                },
                4={
                    mark                = text,
                    text mark as node   = true,
                    text mark           = \labelname,
                    text mark style     = {
                        regular polygon,
                        regular polygon sides=3,
                        draw  = black,
                        fill  = \labelcolor,
                        scale = \labelscale,
                        inner sep   = 1pt,
                        outer sep = 2pt,
                        draw
                    }
                }
            },
            scatter,
            draw=none,
            scatter src=explicit symbolic
        ]
        table[
            x       = angle,
            y       = radius,
            meta    = class,
            col sep = comma
        ]{test_radar.csv};
        \path % segment radii
            (0,0)  coordinate (aux0)
            (0,3)  coordinate (aux1) 
            (0,6)  coordinate (aux2)
            (0,8)  coordinate (aux3)
            (0,10) coordinate (aux4);

        \path
            (0,0)   coordinate (start)
            (0,7)   coordinate (strechedBradius)
            (0,8.5) coordinate (strechedC)
            (0,3)   coordinate (strechedD)
            ;

        % streched blips
        % https://tex.stackexchange.com/questions/495067/dashed-trajectory-encircling-two-segments/495140 #comment1252594_495140
        \newcommand{\strechedA}{(50,3) -- (50,8)}
        \draw[line cap=round, line width=3mm]               \strechedA;
        \draw[line cap=round, lightgray, line width=2.5mm]  \strechedA;

        % halo stuff
        \path[thick,postaction={record path={step=10pt}}] (300,2) -- (300,6) ;
        \draw[red, halo]
            node[
                xshift=3ex,
                yshift=-4ex,
                draw, 
                black, 
                fill=white, 
                inner sep=1pt
            ] {\tiny test1};

    \end{polaraxis}

    \begin{scope}[
        on background layer
        ]
        \foreach \X [evaluate=\X as \GrayLevel using {int(10+8*(4-\X))}]in {4,3,2,1}
        \path
            let \p1=($(aux\X)-(aux0)$),\n1={veclen(\x1,\y1)} in 
            [fill=gray!\GrayLevel] (aux0) circle[radius=\n1];
        \draw[white,line width=4pt]
            let \p1=($(aux4)-(aux0)$),\n1={veclen(\x1,\y1)} in
            (aux0) -- ($(aux0)+(0:\n1)$)        coordinate  (auy0)
            (aux0) -- ($(aux0)+(120:\n1)$)      coordinate  (auy1)
            (aux0) -- ($(aux0)+(240:\n1)$)      coordinate  (auy2)
        ;

        \def\mymoveup#1{\raisebox{2.5ex}}
        \centerarcpolarpath[
            postaction={
                decorate,
                decoration={
                    text along path,
                    text align = center,
                    reverse path, % for flipping
                    text = {
                        |\mymoveup|
                        sector one
                    }
                }
            }
        ](aux0,auy1)(0:60);
    \end{scope}

    \node[purple, ultra thick]  at (aux0) {x};
    \node[red, ultra thick]     at (aux1) {x};

    \centerarcpolar[very thick, green, dashed](aux0,aux1)(45:140);

    \newcommand{\strechedBa}{160};
    \newcommand{\strechedBb}{215};
    \centerarcpolar[line cap=round, line width=3mm](aux0,strechedBradius)(\strechedBa:\strechedBb);
    \centerarcpolar[line cap=round, lightgray, line width=2.5mm](aux0,strechedBradius)(\strechedBa:\strechedBb);

    % halo stuff
    \centerarcpolar[
    ](aux0,strechedC)(90:120);
    \centerarcpolar[
        very thick, green, dashed,
        draw=none,
        postaction={%
            record path={step=10pt}
        }
    ](aux0,strechedC)(90:120);
    \draw[red, halo] 
        node[
            xshift=-2ex,
            yshift=-1ex,
            draw, 
            black, 
            fill=white, 
            inner sep=1pt
        ] {\tiny test2};

    \centerarcpolarpath[
        postaction={record path={step=10pt}}
    ](aux0,strechedD)(200:270);
    \draw[
        halo,
        red
    ]
    node[
        xshift=3ex,
        yshift=-2.5ex,
        draw, 
        black, 
        fill=white, 
        inner sep=1pt
    ] {\tiny test3};

\end{tikzpicture}
\end{document}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.