# TikZ: How to draw a pattern at the border of a tikz path

I want to draw the shapes of airspaces according to the german VFR map style and I have problems with the restricted areas, ED-R44 in this picture:
(source: flightplanner.de)

I already found out how to do the airspace A and B,C,D but the fill mechanism doesn't transpose to the hatch pattern I am looking for:

\documentclass{minimal}
\usepackage{tikz}
\usetikzlibrary{patterns}

\tikzstyle{EDR}=[draw=red,line width=1pt,preaction={clip, postaction={pattern=north west lines, pattern color=red}}]
\tikzstyle{D}=[draw=blue,line width=1pt,preaction={clip, postaction={draw=blue,opacity=0.5,line width=12pt}}]

\begin{document}
\begin{tikzpicture}
\draw[EDR] (1,0) -- (4.5,1.5) -- (4,4) -- (1,3.5) -- (2.5,2) -- cycle;

\node at (6,2) {$\rightarrow$};

\draw[EDR] (8,0) rectangle (12,4);
\draw[fill=white,draw=none] (8.5,0.5) rectangle (11.5,3.5);

\draw[D] (13,0) rectangle (17,4);
\end{tikzpicture}
\end{document}


The problem is, that the white fill doesn't work for arbitraray polygons, but I am unable to think of a way for preaction, postaction or decoration, to make it work.

• please, extend your code snippet to complete small document beginning with \documentclass{...} and ending with \end{document}. this is needed for test your code (it is not fun to write from scratch what you already done. help us to help you. Mar 10 '18 at 20:40
• Sorry @Zarko, I am using TikZEdit, hence the old code was my complete example. I now expanded it to a complete document which should compile. And please don't get me wrong, I don't want to replicate the whole legend, it is only about the restricted areas with the hatch pattern. Mar 10 '18 at 21:55
• reproducing complete shoved image is to broad for asking, so in such case your question be closed :-). from the very beginning, I understood that you are only interested in the part, that you interested only for part produced by your mwe. there is not clear, what you like to obtain? the most left image with patterns only band at shape border? Mar 10 '18 at 22:00
• Sorry, just wanted to make clear, because the image might be confusing without the text above. And exactly, I need to have the hatch pattern only at the border, like in the two rectangles (one with the red hatch, one with the blue solid), but without the trick of drawing white over the hatch, like in the MWE. Basically the result in tex.stackexchange.com/questions/282012/… but I have to do it on arbitrary polygons, so I cannot manually define the fill area. Mar 10 '18 at 22:05
• I am really curious about what your project was. An ICAO500-like map done in tikz? Do you have some website/image, what the overall outcome was?
– n4pK
Feb 7 '20 at 9:22

NEW ANSWER BASED ON YOUR OWN ANSWER: This avoids the white filling. UPDATE: One single style does the job. (I also did the blue contour.)

\documentclass{minimal}
\usepackage{tikz}
\usetikzlibrary{patterns}
\usetikzlibrary{decorations,backgrounds}

\newcounter{tmp}

%\tikzstyle{D}=[draw=blue,line width=1pt,preaction={clip, postaction={draw=blue,opacity=0.5,line width=12pt}}]
%<- note that tikzstyle is deprecated

\tikzset{D/.style={
preaction={draw=blue,line width=1pt},
preaction={decoration={contour lineto closed, contour distance=6pt},
decorate,
},
postaction={
insert path={%
\pgfextra{%
\pgfinterruptpath
\begin{scope}[opacity=0.5, transparency group]
\path[fill=blue,even odd rule]
\mySecondList \myList
;
\end{scope}
\endpgfinterruptpath}
}},
}}

\tikzset{EDR/.style={
preaction={draw=red,line width=1pt},
preaction={decoration={contour lineto closed, contour distance=6pt},
decorate,
},
postaction={
insert path={%
\pgfextra{%
\pgfinterruptpath
\path[pattern=north west lines, pattern color=red,even odd rule]
\mySecondList \myList
;
\endpgfinterruptpath}
}},
}}

\makeatletter
\def\pgfdecoratedcontourdistance{0pt}
\pgfset{
decoration/contour distance/.code=%
\pgfmathsetlengthmacro\pgfdecoratedcontourdistance{#1}}
\pgfdeclaredecoration{contour lineto closed}{start}{%
\state{start}[
next state=draw,
width=0pt,
persistent precomputation=\let\pgf@decorate@firstsegmentangle\pgfdecoratedangle]{%
\pgfextra{\xdef\myList{}\xdef\mySecondList{}}
\pgfextra{\setcounter{tmp}{0}}
\pgfpathmoveto{\pgfpointlineattime{.5}
{\pgfqpoint{0pt}{\pgfdecoratedcontourdistance}}
{\pgfqpoint{\pgfdecoratedinputsegmentlength}{\pgfdecoratedcontourdistance}}}%
}%
\state{draw}[next state=draw, width=\pgfdecoratedinputsegmentlength]{%
\ifpgf@decorate@is@closepath@%
\pgfmathsetmacro\pgfdecoratedangletonextinputsegment{%
-\pgfdecoratedangle+\pgf@decorate@firstsegmentangle}%
\fi
\pgfmathsetlengthmacro\pgf@decoration@contour@shorten{%
-\pgfdecoratedcontourdistance*cot(-\pgfdecoratedangletonextinputsegment/2+90)}%
\pgfpathlineto
{\pgfpoint{\pgfdecoratedinputsegmentlength+\pgf@decoration@contour@shorten}
{\pgfdecoratedcontourdistance}}%
\stepcounter{tmp}
\pgfcoordinate{muemmel\thetmp}{\pgfpoint{\pgfdecoratedinputsegmentlength+\pgf@decoration@contour@shorten}
{\pgfdecoratedcontourdistance}}
\pgfcoordinate{feep\thetmp}{\pgfpoint{\pgfdecoratedinputsegmentlength}{0pt}}
\pgfextra{\xdef\myList{\myList (muemmel\thetmp) -- }%
\xdef\mySecondList{\mySecondList (feep\thetmp) -- }}
\ifpgf@decorate@is@closepath@%
\pgfpathclose
\pgfextra{\xdef\myList{\myList cycle}%
\xdef\mySecondList{\mySecondList cycle}}
\fi
}%
\state{final}{\pgfextra{%\typeout{\myList,\mySecondList}
}}%
}
\makeatother
\tikzset{
contour/.style={
decoration={
name=contour lineto closed,
contour distance=#1
},
decorate}}
\begin{document}

\begin{tikzpicture}
\draw(0,0) -- ({sqrt(8)},4) node[midway,sloped,above]{That's just a test!};

\path[EDR]
(1,0) -- (4.5,1.5) -- (4,4) -- (1,3.5) -- (3.5,2) -- cycle;

\path[D] (7,0) -- (9.5,2.5) -- (11,4) -- (9,3.5) -- (8,2) -- cycle;

\end{tikzpicture}

\end{document}


ORIGINAL ANSWER: Some very similar question has been answered here. Using the code written there allowed me to write a command \DrawBorder, which I believe does what you want. Note, however, that the present version works for polygons only. (EDIT: Added the BCD style, cleaned up the code and added explanations.)

\documentclass[tikz,border=3pt]{standalone}
\usetikzlibrary{patterns,decorations,calc}
%
\def\contourwidth{12pt}
% Notice that this width enters at two places
% first it defines the widths of the nodes created by \tikzsegment
% but it also defines the overshoot, required when an angle is larger than
% 180 degrees
\newcommand{\tikzsegment}[3][]{ % from https://tex.stackexchange.com/a/192824/121799
\path let
\p1=($#3-#2$),
\n1={veclen(\p1)+1.75*\contourwidth}
in #2 -- #3
node[minimum width=\n1,
inner sep=0pt,
pos=0.5,sloped,rectangle,
fill=white]{}
node[minimum width=\n1,
inner sep=0pt,
pos=0.5,sloped,rectangle,
#1]
(line){};
}
\newcommand{\DrawBorderA}[2][]{
\begin{scope}
\foreach \point [count=\n] in {#2} {
\ifnum\n=1
\xdef\ClipList{\point --}
\else
\xdef\ClipList{\ClipList \point --}
\fi
\node (prev) at \point {};
}
\xdef\ClipList{\ClipList cycle;}%\typeout{\ClipList}
\clip \ClipList
\foreach \point in {#2} {
\node (new) at \point {};%\typeout{\point}
\tikzsegment[pattern=north west lines, pattern color=red,
minimum height={2*\contourwidth}]
{(prev.center)}{(new.center)}
\node (prev) at \point {};
}
\draw[red,line width=2pt] \ClipList
\end{scope}
}

\newcommand{\DrawBorderBCD}[2][]{
\begin{scope}[opacity=0.5, transparency group]
\foreach \point [count=\n] in {#2} {
\ifnum\n=1
\xdef\ClipList{\point --}
\else
\xdef\ClipList{\ClipList \point --}
\fi
\node (prev) at \point {};
}
\xdef\ClipList{\ClipList cycle;}%\typeout{\ClipList}
\clip \ClipList
\foreach \point in {#2} {
\node (new) at \point {};%\typeout{\point}
\tikzsegment[fill=blue,
minimum height={2*\contourwidth}]
{(prev.center)}{(new.center)}
\node (prev) at \point {};
}
\end{scope}
\draw[blue,opacity=1,line width=1pt] \ClipList
s}

% \tikzstyle{EDR}=[draw=red,line width=1pt,preaction={clip, postaction={pattern=north west lines, pattern color=red}}]
% \tikzstyle{D}=[draw=blue,line width=1pt,preaction={clip, postaction={draw=blue,opacity=0.5,line width=12pt}}]
\begin{document}

\begin{tikzpicture}

\DrawBorderA{(1,0),(4.5,1.5),(4,4),(1,3.5),(2.5,2)}

\begin{scope}[xshift=5cm]
\DrawBorderBCD{(1,0),(4.5,1.5),(4,4),(1,3.5),(2.6,2)}
\end{scope}

\end{tikzpicture}
\end{document}


DISCLAIMER: It does not yet work with arbitrarily crazy angles (much larger than 270 degrees). Dealing with those will either require brute force, i.e. some fair amount of work, or some clever idea. I plan to revisit this task once I know that this is the way to go.

• In general the results looks like my expectation, but this solution creates problems in my actual file, which is scaled and hence creates nasty effects on some edges. Mar 11 '18 at 14:08
• That is a nice solution, but shouldn't it be possible to avoid to have the path twice? I know one could save it or one could again build a macro around it, but I think we are very close to a solution which can be completely embedded in a style. Mar 11 '18 at 17:20
• @TobiBS I am working on that, my attempts use append after command and insert path. As you probably know, TikZ can be tricky ... ;-)
– user121799
Mar 11 '18 at 17:22
• Regarding your comment about tikzstyle: Is it really deprecated now, I was still under the impression of tex.stackexchange.com/questions/52372/… Mar 11 '18 at 17:23
• Here is a version that does not use \pgfextra.
– user121799
Dec 15 '18 at 23:08

I found another solution for my problem which is based on Draw additional parallel paths in TikZ and furthermore Polygon drawn with an offset

\tikzstyle{EDR}=[draw=red,line width=1pt,pattern=north west lines, pattern color=red,postaction={decoration={contour lineto closed, contour distance=6pt}, fill=white, decorate}]

\makeatletter
\usetikzlibrary{decorations,backgrounds}
\def\pgfdecoratedcontourdistance{0pt}
\pgfset{
decoration/contour distance/.code=%
\pgfmathsetlengthmacro\pgfdecoratedcontourdistance{#1}}
\pgfdeclaredecoration{contour lineto closed}{start}{%
\state{start}[
next state=draw,
width=0pt,
persistent precomputation=\let\pgf@decorate@firstsegmentangle\pgfdecoratedangle]{%
\pgfpathmoveto{\pgfpointlineattime{.5}
{\pgfqpoint{0pt}{\pgfdecoratedcontourdistance}}
{\pgfqpoint{\pgfdecoratedinputsegmentlength}{\pgfdecoratedcontourdistance}}}%
}%
\state{draw}[next state=draw, width=\pgfdecoratedinputsegmentlength]{%
\ifpgf@decorate@is@closepath@%
\pgfmathsetmacro\pgfdecoratedangletonextinputsegment{%
-\pgfdecoratedangle+\pgf@decorate@firstsegmentangle}%
\fi
\pgfmathsetlengthmacro\pgf@decoration@contour@shorten{%
-\pgfdecoratedcontourdistance*cot(-\pgfdecoratedangletonextinputsegment/2+90)}%
\pgfpathlineto
{\pgfpoint{\pgfdecoratedinputsegmentlength+\pgf@decoration@contour@shorten}
{\pgfdecoratedcontourdistance}}%
\ifpgf@decorate@is@closepath@%
\pgfpathclose
\fi
}%
\state{final}{}%
}
\makeatother
\tikzset{
contour/.style={
decoration={
name=contour lineto closed,
contour distance=#1
},
decorate}}

\begin{tikzpicture}
\draw[EDR] (1,0) -- (4.5,1.5) -- (4,4) -- (1,3.5) -- (2.5,2) -- cycle;
\end{tikzpicture}


• Is anybody able to propose a good way to create the white area via clipping, instead of my poor mans filling? It works in my current project, but might create issues for others. Mar 11 '18 at 14:09
• I have added a slightly modified version of your code to my answer which avoids the fill.
– user121799
Mar 11 '18 at 15:29
• Is it possible to use this pattern also with a not closed path? When I remove the cycle I get the error: Package tikz Error: Cannot parse this coordinate.`. Nov 28 '19 at 13:15