Heatmap over country like Google Map

Here is a command, called \heatmark, to produce, in a tikzpicture, a marker similar to those on the map you link to; the code could probably be improved (edit: I've spruced the original code a bit).

The \heatmark command takes 4 arguments:

  1. the position, in the form of x,y,
  2. the radius of the inner disk,
  3. the color of the inner disk,
  4. the text to be written at the centre of the disk.

For more details on how to draw on top of an external picture (e.g. a map) in TikZ, please refer to Drawing on an image with TikZ.

bare map, markers alone

map + markers

\documentclass{article}

\usepackage[usenames,dvipsnames]{xcolor}
\usepackage{tikz}

\newcommand\heatmark[4]{
    \def\r{#2}                                  % radius of disk
    \def\d{.1*\r}                               % radius gap between arc segments
    \def\angleoffset{10}                        % half angular gap between arc segments
    \def\e{.3*\r}                               % radial width of arc segments
    \def\mycolor{#3}                            % color of disk
    \pgfmathtruncatemacro\n{3}                  % number of arc segments outside disk

    \begin{scope}[shift={(#1)}]                 % shift to desired position
    \fill[\mycolor] circle (\r);                % draw inner disk
    \foreach \onethirdangle in {0,120,240}{%    % rotational symmetry
      \begin{scope}[rotate=\onethirdangle]
        \foreach \i in {1,...,\n}{%    

          \pgfmathsetmacro\opac{1.1-\i/\n}
          \def\Aone{30+\angleoffset}
          \def\Atwo{150-\angleoffset}
          \def\Rone{\r+\i*\d+(\i-1)*\e}
          \def\Rtwo{\Rone+\e}

          \fill[\mycolor,opacity=\opac]                     % draw one arc segment
            ({(\Rone)*cos(\Aone)},{(\Rone)*sin(\Atwo)})
                arc ({\Aone}:{\Atwo}:{\Rone})               % arc 1 
             -- ++({\e*cos(\Atwo)},{\e*sin(\Atwo)})         % straight line 1
             -- ({(\Rtwo)*cos(\Atwo)},{(\Rtwo)*sin(\Aone)})
                arc ({\Atwo}:{\Aone}:{\Rtwo} )              % arc 2
             -- cycle;                                      % straight line 2
        }
      \end{scope}
    }
    \node[white] {\small #4};
    \end{scope}
}

\begin{document}

Original map:\\
\includegraphics[width=0.9\textwidth]{western_europe.png}

Some markers on their own:\\
\begin{tikzpicture}
    \heatmark{0,0}{.5}{RubineRed}{300}
    \heatmark{2,0}{.3}{YellowOrange!80}{50}
    \heatmark{4,0}{.3}{Cerulean}{50}
\end{tikzpicture}

\newpage
Original map with some markers on it:\\
\begin{tikzpicture}
    \node[anchor=south west,inner sep=0] (image) at (0,0)
        {\includegraphics[width=0.9\textwidth]{western_europe.png}};
    \begin{scope}[x={(image.south east)},y={(image.north west)}]
        \def\Paris{.45,.5}
        \def\Madrid{.25,.2}
        \def\Rome{.8,.25}
        \heatmark{\Rome}{.5cm}{RubineRed}{300}
        \heatmark{\Madrid}{.25cm}{YellowOrange!80}{35}
        \heatmark{\Paris}{.3cm}{Cerulean}{50}
    \end{scope}
\end{tikzpicture}

\end{document}

Code

\documentclass[tikz,convert=false]{standalone}
\makeatletter
\tikzset{
    heat arc width/.initial=+4pt,
    heat arc sep/.initial=+1pt,
    heat arc rings/.initial=3,
    heat arcs/.initial=3,
    heat arc sep angle/.initial=20,
    heat opacity high/.initial=.8,
    heat opacity low/.initial=.2,
    heat rotate/.initial=90,
}
\pgfdeclareshape{heat}
%
% Draws a circle around the text
%
{
  \savedanchor\centerpoint{%
    \pgf@x=.5\wd\pgfnodeparttextbox%
    \pgf@y=.5\ht\pgfnodeparttextbox%
    \advance\pgf@y by-.5\dp\pgfnodeparttextbox%
  }

  \saveddimen\innerradius{%
    % 
    % Caculate ``height radius''
    % 
    \pgf@ya=.5\ht\pgfnodeparttextbox%
    \advance\pgf@ya by.5\dp\pgfnodeparttextbox%
    \pgfmathsetlength\pgf@yb{\pgfkeysvalueof{/pgf/inner ysep}}%
    \advance\pgf@ya by\pgf@yb%
    % 
    % Caculate ``width radius''
    % 
    \pgf@xa=.5\wd\pgfnodeparttextbox%
    \pgfmathsetlength\pgf@xb{\pgfkeysvalueof{/pgf/inner xsep}}%
    \advance\pgf@xa by\pgf@xb%
    % 
    % Calculate length of radius vector:
    % 
    \pgf@process{\pgfpointnormalised{\pgfqpoint{\pgf@xa}{\pgf@ya}}}%
    \ifdim\pgf@x>\pgf@y%
        \c@pgf@counta=\pgf@x%
        \ifnum\c@pgf@counta=0\relax%
        \else%
          \divide\c@pgf@counta by 255\relax%
          \pgf@xa=16\pgf@xa\relax%
          \divide\pgf@xa by\c@pgf@counta%
          \pgf@xa=16\pgf@xa\relax%
        \fi%
      \else%
        \c@pgf@counta=\pgf@y%
        \ifnum\c@pgf@counta=0\relax%
        \else%
          \divide\c@pgf@counta by 255\relax%
          \pgf@ya=16\pgf@ya\relax%
          \divide\pgf@ya by\c@pgf@counta%
          \pgf@xa=16\pgf@ya\relax%
        \fi%
    \fi%
    \pgf@x=\pgf@xa%
    % 
    % If necessary, adjust radius so that the size requirements are
    % met: 
    % 
    \pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/minimum width}}%  
    \pgfmathsetlength{\pgf@yb}{\pgfkeysvalueof{/pgf/minimum height}}%  
    \ifdim\pgf@x<.5\pgf@xb%
        \pgf@x=.5\pgf@xb%
    \fi%
    \ifdim\pgf@x<.5\pgf@yb%
        \pgf@x=.5\pgf@yb%
    \fi%
  }%

  \saveddimen\radius{%
    \pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/outer xsep}}%  
    \pgfmathsetlength{\pgf@yb}{\pgfkeysvalueof{/pgf/outer ysep}}%  
    \ifdim\pgf@xb<\pgf@yb%
      \advance\pgf@x by\pgf@yb%
    \else%
      \advance\pgf@x by\pgf@xb%
    \fi%
    \pgfmathsetcount\c@pgf@counta{\pgfkeysvalueof{/tikz/heat arc rings}}%
    \pgfmathsetlength\pgf@xa{\pgfkeysvalueof{/tikz/heat arc width}+\pgfkeysvalueof{/tikz/heat arc sep}}%
    \multiply\pgf@xa\c@pgf@counta
    \advance\pgf@x\pgf@xa
  }%
  %
  % Anchors
  % 
  \inheritanchor[from=circle]{north}
  \inheritanchor[from=circle]{north west}
  \inheritanchor[from=circle]{north east}
  \inheritanchor[from=circle]{center}
  \inheritanchor[from=circle]{west}
  \inheritanchor[from=circle]{east}
  \inheritanchor[from=circle]{mid}
  \inheritanchor[from=circle]{mid west}
  \inheritanchor[from=circle]{mid east}
  \inheritanchor[from=circle]{base}
  \inheritanchor[from=circle]{base west}
  \inheritanchor[from=circle]{base east}
  \inheritanchor[from=circle]{south}
  \inheritanchor[from=circle]{south west}
  \inheritanchor[from=circle]{south east}
  \inheritanchorborder[from=circle]
  %
  % Background path
  %
  \backgroundpath{
    \pgfutil@tempdima=\innerradius%
    \pgfpathcircle{\centerpoint}{\pgfutil@tempdima}%
  }%
  %
  %
  %
  \behindbackgroundpath{%
    \pgfmathsetcount\c@pgf@counta{\pgfkeysvalueof{/tikz/heat arc rings}}%     heat arc rings
    \pgfmathsetcount\c@pgf@countb{\pgfkeysvalueof{/tikz/heat arcs}}%          heat arcs
    \pgfmathsetlength\pgf@xa{\pgfkeysvalueof{/tikz/heat arc width}}%          heat arc width
    \pgfmathsetlength\pgf@xb{\pgfkeysvalueof{/tikz/heat arc sep}}%            heat arc sep
    \pgfmathsetmacro\pgf@tempa{\pgfkeysvalueof{/tikz/heat arc sep angle}}%    heat arc sep angle
    \pgfmathsetmacro\pgf@tempb{(360-\c@pgf@countb*\pgf@tempa)/\c@pgf@countb}% heat arc
    \pgfmathsetmacro\pgf@tempc{\pgfkeysvalueof{/tikz/heat rotate}}%           heat rotate
    %
    \pgfmathsetmacro\pgf@tempd{\pgfkeysvalueof{/tikz/heat opacity high}}%     heat opacity high
    \ifnum\c@pgf@counta=1
      \def\qrr@heat@opacity@seps{0}%
    \else
      \pgfmathsetmacro\qrr@heat@opacity@seps
      {(\pgf@tempd-\pgfkeysvalueof{/tikz/heat opacity low})%
         /(\c@pgf@counta-1)}%
    \fi
    \pgfutil@tempcnta\z@
    \pgfutil@tempcntb\z@
    \pgf@ya\innerradius\relax
    \pgftransformshift{\centerpoint}
    \pgfutil@loop % ring loop
      \advance\pgf@ya\pgf@xb % current radius + heat arc sep
      \pgf@yb\pgf@ya         
      \advance\pgf@yb\pgf@xa % current radius + heat arc sep + heat arc width
      \pgfsetfillopacity{\pgf@tempd}%
      \begingroup
        \edef\pgf@marshal{\noexpand\tikzset{heat ring \number\numexpr\pgfutil@tempcnta+1\relax/.try}}%
        \pgf@marshal
        \tikz@options
        {%
        \pgfutil@loop
          \pgfmathsetmacro\qrr@start@angle{\[email protected]*\pgf@tempb+\pgfutil@tempcntb*(360/\c@pgf@countb)}%
          % path
          \begingroup
            \edef\pgf@marshal{\noexpand\tikzset{heat arc \number\numexpr\pgfutil@tempcntb+1\relax/.try}%
                              \noexpand\tikzset{heat arc \number\numexpr\pgfutil@tempcnta+1\relax-\number\numexpr\pgfutil@tempcntb+1\relax/.try}}%
            \pgf@marshal
            \tikz@options
            \pgfpathmoveto{\pgfpointpolar{\qrr@start@angle}{\pgf@ya}}%
            \pgfpatharc{\qrr@start@angle}{\qrr@start@angle+\pgf@tempb}{\pgf@ya}%
            \pgfpathlineto{\pgfpointpolar{\qrr@start@angle+\pgf@tempb}{\pgf@yb}}%
            \pgfpatharc{\qrr@start@angle+\pgf@tempb}{\qrr@start@angle}{\pgf@yb}%
            \pgfpathclose
            \pgfusepathqfill
          \endgroup
          \advance\pgfutil@tempcntb\@ne
          \ifnum\pgfutil@tempcntb<\c@pgf@countb
        \pgfutil@repeat
        }%
      \endgroup
      \advance\pgfutil@tempcnta\@ne
      \ifnum\pgfutil@tempcnta<\c@pgf@counta
        \pgf@ya\pgf@yb
        \pgfmathsetmacro\pgf@tempd{\pgf@tempd-\qrr@heat@opacity@seps}%
    \pgfutil@repeat
  }
}
\tikzset{every heat node/.append style={
  outer sep={+\z@},
  draw=none
  }}
\makeatother
\tikzset{radius=.5pt}
\begin{document}
    \tikz[
      heat ring 1/.append style={green},
      heat arc 1/.append style={blue},
      heat arc 2-2/.append style={yellow!70!black}
    ] {
        \node[heat, fill=red] (n) {100};
        \fill (n.west) circle[]
              (n.east) circle[]
              (n.north) circle[]
              (n.south) circle[];
            \useasboundingbox ([yshift=-2mm]n.south) ([xshift=-2mm]n.west) ([yshift=2mm]n.north) ([xshift=2mm]n.east);
    }

    \foreach \sep in {0,10,...,120}{%
        \tikz{
            \node[heat arc sep angle=\sep, heat, fill=red, text=white, minimum size=1cm] (n) {\sep};
            \fill (n.west) circle[] 
                  (n.east) circle[] 
                  (n.north) circle[]
                  (n.south) circle[];
            \useasboundingbox ([yshift=-2mm]n.south) ([xshift=-2mm]n.west) ([yshift=2mm]n.north) ([xshift=2mm]n.east);
        }%
    }%

    \foreach \rot in {0,10,...,359}{%
        \tikz{
            \node[heat rotate=\rot, heat, fill=green, minimum size=1cm] (n) {\rot};
            \fill (n.west) circle[] 
                  (n.east) circle[] 
                  (n.north) circle[]
                  (n.south) circle[];
            \useasboundingbox ([yshift=-2mm]n.south) ([xshift=-2mm]n.west) ([yshift=2mm]n.north) ([xshift=2mm]n.east);
        }%
    }%

    \foreach \arcs in {1,...,10}{%
        \tikz{
            \node[heat arcs=\arcs, heat, fill=blue, text=white, minimum size=1cm] (n) {\arcs};
            \fill (n.west) circle[] 
                  (n.east) circle[] 
                  (n.north) circle[]
                  (n.south) circle[];
            \useasboundingbox ([yshift=-2mm]n.south) ([xshift=-2mm]n.west) ([yshift=2mm]n.north) ([xshift=2mm]n.east);
        }%
    }%

    \foreach \arcs in {1,...,10,9,8,...,2}{%
        \tikz{
            \node[heat arc rings=\arcs, heat, fill=yellow!50!black, minimum size=1cm] (n) {\arcs};
            \fill (n.west) circle[] 
                  (n.east) circle[] 
                  (n.north) circle[]
                  (n.south) circle[];
            \useasboundingbox ([yshift=-2mm]n.south) ([xshift=-2mm]n.west) ([yshift=2mm]n.north) ([xshift=2mm]n.east);
        }%
    }%
\end{document}

Output

(The last one does change its overall size.)

enter image description here

enter image description here enter image description here

enter image description here enter image description here

Tags:

Tikz Pgf