编辑

编辑

这个问题导致了一个新的方案的出现:
sankey

我想画一些类似的东西(不完全是):

使用 TikZ。理想情况下,我可以通过变量设置宽度并将两个流重新合并为一个。有什么好方法可以做到这一点吗?

由于这将是我的第一个 TikZ 图,我正在寻找如何解决这个问题的一般方法,而不是完整的解决方案,以便我可以有效地阅读文档。我如何分支矩形的(可变)部分?我如何将这些部分重新合并为一个?

部分解决方案:

\documentclass{article}
\usepackage{tikz}
\begin{document}
    \begin{tikzpicture}[scale=.5]
    \draw[very thick] (1,0) -- (4,-1) -- (7,0) -- (7,-4) to [out=-90,in=180] (8,-5) -- (23,-5) -- (24,-6.5) --  (23,-8) -- (20,-8) to [out=180,in=0] (15,-10) -- (15,-10) (14,-10) -- (10,-10) to [out=0, in=90] (14,-14) -- (14,-8) to [out=90,in=0] (13,-7) -- (11,-7);
    \draw[very thick] (15,-7) to [out=0,in=180] (20,-6) -- (11,-6) -- (13,-6) to [out=0,in=90] (15,-8) to (15,-20) -- (13.5,-21) -- (12,-20) to (12,-14) to [out=90,in=0] (11,-13) -- (5,-13) to [out=180,in=-90] (1,-9) to (1,0);
    \draw[very thick] (4,-4) to [out=-90, in=180] (8,-8) -- (14,-8) (15,-8) -- (16,-8) to [out=0,in=180] (20,-7) to [out=180, in=0] (15,-9) (14,-9) -- (6,-9) to [out=180,in=-90] (4,-7) -- (4,-4);
    \draw[very thick] (2,-6) to (2,-7) to [out=-90,in=180] (6,-11) -- (10,-11) to [out=0,in=90] (13,-14) -- (13,-14) to [out=90,in=0] (11,-12) -- (5,-12) to [out=180,in=-90] (2,-9) -- (2,-7);
    \end{tikzpicture}
\end{document}

给出:

在此处输入图片描述

但这些都是硬编码的。我理想情况下希望流量的宽度可变(无需手动重新计算所有点)

从语法角度来看,我可以想象这样的事情:

\stream[width=x,direction=down] (initial_amount)
\redirect[initialamount,x\%][from=rightedge][to=right] (choide_A)
\redirect[choiceA,y\%][from=middle,rejoinrest=true][to=down] (choice_C)
\redirect[initial_amount,g\%][from=right][to=right] (choice_D) %i.e. that what's left of it after redirecting choice_A
\redirect[choid_D,h\%][from=left][to=down] (choice_E) %left in the sense that it the left side turned by 90 degrees

\join[initial_amount][choice_D][choice_C]
\join[choice_A][choice_E]

我正在考虑的要点

  • 溪流有流向,但 tikz 应该被允许改变它(右转然后左转或反之亦然),例如在连接溪流时。
  • 分层应该是这样的,第一层 == 最低层,之后的任何层都应该堆积在其上
  • 直线部分或移位部分的长度/移位量可根据间距需要进行调整。

更新:

下面有一部分向右分支(\def'd by \choiceA)

\documentclass{article}
\usepackage{tikz}
\begin{document}
    \def\startstreamx{1}
    \def\startstreamy{1}
    \def\initialwidth{6}
    \def\initialheight{4}
    \def\choiceA{.9}
    \begin{tikzpicture}[scale=.5]
    \draw[very thick] (\startstreamx,-\startstreamy-\initialheight) -- (\startstreamx,-\startstreamy) --    (\startstreamx+\initialwidth,-\startstreamy) -- (\startstreamx+\initialwidth,-\startstreamy-\initialheight);
    \draw[very thick] (\startstreamx+\initialwidth,-\startstreamy-\initialheight) to [out=-90, in=180] (\startstreamx+\initialwidth+1,-\startstreamy-\initialheight-1);
    \draw[very thick] (\startstreamx+\initialwidth-\choiceA*\initialwidth,-\startstreamy-\initialheight) to [out=-90, in=180] (\startstreamx+\initialwidth+1,-\startstreamy-\initialheight-1-\choiceA*\initialwidth);
    \draw[very thick] (\startstreamx,-\startstreamy-\initialheight) to (\startstreamx,-\startstreamy-\initialheight-1-\choiceA*\initialwidth);
    \draw[very thick] (\startstreamx+\initialwidth-\choiceA*\initialwidth,-\startstreamy-\initialheight) to (\startstreamx+\initialwidth-\choiceA*\initialwidth,-\startstreamy-\initialheight-1-\choiceA*\initialwidth);
    \end{tikzpicture}
\end{document}

现在,我们可以将另一个这样的代码片段与初始 x 和 y 以及宽度值一起添加到剩余(向下)流的末尾,然后再次向右分支。

在此处输入图片描述

答案1

编辑

自 TikZ/PGF 3.0 版以来,的参数atan2已被交换。

新版本(TikZ/PGF 3.0)

这是我对新环境的第一次尝试sankeydiagram

它的可选参数对于修复一些全局参数很有用:

  • sankey tot quantity是一个数字,代表全球流量的总量(默认值:100(代表 100%))。

  • sankey tot length是全局流的宽度(默认值:100pt)。

  • sankey min radius是每次转弯的最小半径(默认值:30pt)。

  • sankey fill是用于填充流程的样式。

  • sankey draw是用于绘制流程的样式。

  • sankey debug(标志)对于在构造图期间调试图很有用(默认值: false)。

环境sankeydiagram定义了一些有用的命令来构建桑基图:

  • \sankeynode{prop}{angle}{name}{pos}创建一个桑基节点(流)支柱容量(以数量单位表示),名为name。其方向和位置由下式给出:角度位置

  • \sankeynodestart\sankeynodeend类似\sankeynode(参数相同)但分别使 和 流开始 和 流结束。

  • \sankeyadvance{name}{distance}向前移动名为姓名

  • \sankeyturn{name}{angle}将 sankey 节点命名为姓名

  • \sankeyfork{name}{list of forks}分叉名为姓名。分叉列表是成对的列表:数量/名称(数量总和必须等于要分叉的sankey节点数量)。

以下是结果(先不加 ,sankey debug然后加sankey debug)。

在此处输入图片描述

在此处输入图片描述

现在,代码如下:

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\usepackage{etoolbox}

\pgfdeclarelayer{background}
\pgfdeclarelayer{foreground}
\pgfdeclarelayer{sankeydebug}
\pgfsetlayers{background,main,foreground,sankeydebug}

\newif\ifsankeydebug

\newenvironment{sankeydiagram}[1][]{

  \def\sankeyflow##1##2{% sn, en
    \path[sankey fill]
    let
    \p1=(##1.north east),\p2=(##1.south east),
    \n1={atan2(\y1-\y2,\x1-\x2)-90},
    \p3=(##2.north west),\p4=(##2.south west),
    \n2={atan2(\y3-\y4,\x3-\x4)+90}
    in
    (\p1) to[out=\n1,in=\n2] (\p3) --
    (\p4) to[in=\n1,out=\n2] (\p2) -- cycle;
    \draw[sankey draw]
    let
    \p1=(##1.north east),\p2=(##1.south east),
    \n1={atan2(\y1-\y2,\x1-\x2)-90},
    \p3=(##2.north west),\p4=(##2.south west),
    \n2={atan2(\y3-\y4,\x3-\x4)+90}
    in
    (\p1) to[out=\n1,in=\n2] (\p3)
    (\p4) to[in=\n1,out=\n2] (\p2);
  }


  \tikzset{
    sankey tot length/.store in=\sankeytotallen,
    sankey tot quantity/.store in=\sankeytotalqty,
    sankey min radius/.store in=\sankeyminradius,
    sankey arrow length/.store in=\sankeyarrowlen,
    sankey debug/.is if=sankeydebug,
    sankey debug=false,
    sankey flow/.style={
      to path={
        \pgfextra{
          \pgfinterruptpath
          \edef\sankeystart{\tikztostart}
          \edef\sankeytarget{\tikztotarget}
          \sankeyflow{\sankeystart}{\sankeytarget}
          \endpgfinterruptpath
        }
      },
    },
    sankey node/.style={
      inner sep=0,minimum height={sankeyqtytolen(##1)},
      minimum width=0,draw=none,line width=0pt,
    },
    % sankey angle
    sankey angle/.store in=\sankeyangle,
    % sankey default styles
    sankey fill/.style={line width=0pt,fill,white},
    sankey draw/.style={draw=black,line width=.4pt},
  }

  \newcommand\sankeynode[4]{%prop,orientation,name,pos
    \node[sankey node=##1,rotate=##2] (##3) at (##4) {};
    \ifsankeydebug
    \begin{pgfonlayer}{sankeydebug}
      \draw[red,|-|] (##3.north west) -- (##3.south west);
      \pgfmathsetmacro{\len}{sankeyqtytolen(##1)/3}
      \draw[red] (##3.west)
      -- ($(##3.west)!\len pt!90:(##3.south west)$)
      node[font=\tiny,text=black] {##3};
    \end{pgfonlayer}
    \fi
  }

  \newcommand\sankeynodestart[4]{%prop,orientation,name,pos
    \sankeynode{##1}{##2}{##3}{##4}
    \begin{scope}[shift={(##3)},rotate=##2]
      \path[sankey fill]
      (##3.north west) -- ++(-\sankeyarrowlen,0)
      -- ([xshift=-\sankeyarrowlen/6]##3.west)
      -- ([xshift=-\sankeyarrowlen]##3.south west)
      -- (##3.south west) -- cycle;
      \path[sankey draw]
      (##3.north west) -- ++(-\sankeyarrowlen,0)
      -- ([xshift=-\sankeyarrowlen/6]##3.west)
      -- ([xshift=-\sankeyarrowlen]##3.south west)
      -- (##3.south west);
    \end{scope}
  }

  \newcommand\sankeynodeend[4]{%prop,orientation,name,pos
    \sankeynode{##1}{##2}{##3}{##4}
    \begin{scope}[shift={(##3)},rotate=##2]
      \path[sankey fill]
      (##3.north east)
      -- ([xshift=\sankeyarrowlen]##3.east)
      -- (##3.south west) -- cycle;
      \path[sankey draw]
      (##3.north east)
      -- ([xshift=\sankeyarrowlen]##3.east)
      -- (##3.south west);
    \end{scope}
  }

  \newcommand\sankeyadvance[3][]{%newname,name,distance
    \edef\name{##2}
    \ifstrempty{##1}{
      \def\newname{##2}
      \edef\name{##2-old}
      \path [late options={name=##2,alias=\name}];
    }{
      \def\newname{##1}
    }
    \path
    let
    % sankey node angle
    \p1=(##2.north east),
    \p2=(##2.south east),
    \n1={atan2(\y1-\y2,\x1-\x2)-90},
    % sankey prop
    \p3=($(\p1)-(\p2)$),
    \n2={sankeylentoqty(veclen(\x3,\y3))},
    % next position
    \p4=($(##2.east)!##3!-90:(##2.north east)$)
    in
    \pgfextra{
      \pgfmathsetmacro{\prop}{\n2}
      \pgfinterruptpath
      \sankeynode{\prop}{\n1}{\newname}{\p4}
      \path (\name) to[sankey flow] (\newname);
      \endpgfinterruptpath
    };
  }

  \newcommand\sankeyturn[3][]{%newname,name,angle
    \edef\name{##2}
    \ifstrempty{##1}{
      \def\newname{##2}
      \edef\name{##2-old}
      \path [late options={name=##2,alias=\name}];
    }{
      \def\newname{##1}
    }
    \ifnumgreater{##3}{0}{
      \typeout{turn acw: ##3}
      \path
      let
      % sankey node angle
      \p1=(##2.north east),
      \p2=(##2.south east),
      \p3=($(\p1)!-\sankeyminradius!(\p2)$),
      \n1={atan2(\y1-\y2,\x1-\x2)-90},
      % sankey prop
      \p4=($(\p1)-(\p2)$),
      \n2={sankeylentoqty(veclen(\x4,\y4))},
      \p5=(##2.east),
      \p6=($(\p3)!1!##3:(\p5)$)
      in
      \pgfextra{
        \pgfmathsetmacro{\prop}{\n2}
        \pgfinterruptpath
        % \fill[red] (\p3) circle (2pt);
        % \fill[blue](\p6) circle (2pt);
        \sankeynode{\prop}{\n1+##3}{\newname}{\p6}
        \path (\name) to[sankey flow] (\newname);
        \endpgfinterruptpath
      };
    }{
      \typeout{turn acw: ##3}
      \path
      let
      % sankey node angle
      \p1=(##2.south east),
      \p2=(##2.north east),
      \p3=($(\p1)!-\sankeyminradius!(\p2)$),
      \n1={atan2(\y1-\y2,\x1-\x2)+90},
      % sankey prop
      \p4=($(\p1)-(\p2)$),
      \n2={sankeylentoqty(veclen(\x4,\y4))},
      \p5=(##2.east),
      \p6=($(\p3)!1!##3:(\p5)$)
      in
      \pgfextra{
        \pgfmathsetmacro{\prop}{\n2}
        \pgfinterruptpath
        % \fill[red] (\p3) circle (2pt);
        % \fill[blue](\p6) circle (2pt);
        \sankeynode{\prop}{\n1+##3}{\newname}{\p6}
        \path (\name) to[sankey flow] (\newname);
        \endpgfinterruptpath
      };
    }
  }

  \newcommand\sankeyfork[2]{%name,list of forks
    \def\name{##1}
    \def\listofforks{##2}
    \xdef\sankeytot{0}
    \path 
    let
    % sankey node angle
    \p1=(\name.north east),
    \p2=(\name.south east),
    \n1={atan2(\y1-\y2,\x1-\x2)-90},
    % sankey prop
    \p4=($(\p1)-(\p2)$),
    \n2={sankeylentoqty(veclen(\x4,\y4))}
    in
    \pgfextra{
      \pgfmathsetmacro{\iprop}{\n2}
    }
    \foreach \prop/\name[count=\c] in \listofforks {
      let
      \p{start \name}=($(\p1)!\sankeytot/\iprop!(\p2)$),
      \n{nexttot}={\sankeytot+\prop},
      \p{end \name}=($(\p1)!\n{nexttot}/\iprop!(\p2)$),
      \p{mid \name}=($(\p{start \name})!.5!(\p{end \name})$)
      in
      \pgfextra{
        \xdef\sankeytot{\n{nexttot}}
        \pgfinterruptpath
        \sankeynode{\prop}{\n1}{\name}{\p{mid \name}}
        \endpgfinterruptpath
      }
    }
    \pgfextra{
      \pgfmathsetmacro{\diff}{abs(\iprop-\sankeytot)}
      \pgfmathtruncatemacro{\finish}{\diff<0.01?1:0}
      \ifnumequal{\finish}{1}{}{
        \message{*** Warning: bad sankey fork (maybe)...}
        \message{\iprop-\sankeytot}
      }
    };
  }

  \tikzset{
    % default values,
    declare function={
      sankeyqtytolen(\qty)=\qty/\sankeytotalqty*\sankeytotallen;
      sankeylentoqty(\len)=\len/\sankeytotallen*\sankeytotalqty;
    },
    sankey tot length=100pt,
    sankey tot quantity=100,
    sankey min radius=30pt,%
    sankey arrow length=10pt,%
    % user values
    #1}
}{
}



\begin{document}
\begin{tikzpicture}[x=1pt,y=1pt]

  \begin{sankeydiagram}[
    sankey tot length=90pt,%
    sankey tot quantity=6,%
    sankey min radius=15pt,%
    sankey fill/.style={
      draw,line width=0pt,
      fill,
      lime!50,
    },
    sankey draw/.style={
      draw=black,
      line width=1pt,
      line cap=round,
      line join=round,
    },
    sankey debug,
    ]
    \sankeynodestart{6}{-90}{p0}{0,100};
    \sankeyadvance{p0}{50pt}

    \sankeyfork{p0}{3/p1,3/p2}

    \sankeyturn{p1}{90}
    \sankeyadvance{p1}{20pt}

    \sankeyadvance{p2}{60pt}

    \sankeyfork{p2}{2/p3,1/p4}

    \sankeyturn{p3}{90}
    \sankeyadvance{p3}{50pt}

    \sankeyfork{p3}{1/p5,1/p6}

    \sankeyadvance{p5}{70pt}

    \sankeyfork{p1}{1/p7,1/p8,1/p9}
    \sankeyadvance{p7}{50pt}
    \sankeyadvance{p9}{50pt}

    \sankeyadvance{p4}{40pt}
    \sankeyturn{p4}{90}
    \sankeyadvance{p4}{65pt}

    \sankeyadvance{p7}{40pt}

    \sankeynode{3}{0}{p11}{[shift={(50pt,-15pt)}]p7}
    \sankeyfork{p11}{1/p7a,1/p9a,1/p5a}
    \path (p7) to[sankey flow] (p7a);
    \path (p9) to[sankey flow] (p9a);
    \path (p5) to[sankey flow] (p5a);
    \sankeyadvance{p11}{30pt}
    \sankeynodeend{3}{0}{p11}{p11}

    {
      \tikzset{
        sankey fill/.append style={
          line width=0pt,
          lime!50!green!50,
        }
      }
      \sankeyturn{p8}{-90}
      \sankeyadvance{p8}{40pt}

      \sankeyturn{p6}{-90}
      \sankeyturn{p4}{-90}

      \sankeynode{3}{-90}{p10}{[shift={(-15pt,-60pt)}]p8}
      \sankeyfork{p10}{1/p8a,1/p6a,1/p4a}
      \path (p4) to[sankey flow] (p4a);
      \path (p6) to[sankey flow] (p6a);
      \path (p8) to[sankey flow] (p8a);
      \sankeyadvance{p10}{30pt}
      \sankeynodeend{3}{-90}{p10}{p10}
    }



  \end{sankeydiagram}
\end{tikzpicture}
\end{document}

下面是另一个具有相同序言的例子(它与您的第一个例子是相同的桑基图,但具有调整后的值:工业=86.1。未经调整,总数不正确……):

在此处输入图片描述

然后是代码(无序言):

\usetikzlibrary{positioning}
\begin{document}
\begin{tikzpicture}

  \begin{sankeydiagram}[
    sankey tot length=5cm,%
    sankey tot quantity=524.3,%
    sankey min radius=3mm,%
    sankey fill/.style={
      draw,line width=0pt,
      fill,
      cyan!50!blue!50!black,
    },
    sankey draw/.style={
      draw=none,
      line width=1pt,
      line cap=round,
      line join=round,
    },
    %sankey debug,
    ]

    \sankeynodestart{7.2}{-90}{B}{-.5,0}
    \coordinate[below=1mm of B.center] (B label);
    \sankeyadvance{B}{5mm}
    \sankeynodestart{137.3}{-90}{GI}{1,0}
    \coordinate[below=1mm of GI.center] (GI label);
    \sankeyadvance{GI}{5mm}
    \sankeynodestart{397.8}{-90}{I}{4,0}
    \coordinate[below=1mm of I.center] (I label);
    \sankeyadvance{I}{5mm}

    \sankeynode{542.3}{-90}{EI}{2.86,-1}
    \sankeyfork{EI}{397.8/Ia,137.3/GIa,7.2/Ba}
    \path (I) to[sankey flow] (Ia);
    \path (GI) to[sankey flow] (GIa);
    \path (B) to[sankey flow] (Ba);
    \sankeyadvance{EI}{5mm}
    \coordinate (EI label) at (EI);
    \sankeyadvance{EI}{5mm}

    \sankeyfork{EI}{63.1/EB,479.2/P}

    \sankeyturn{EB}{90}
    \sankeyadvance{EB}{2cm}
    \coordinate (EB label) at (EB.center);
    \sankeyadvance{EB}{2cm}
    \sankeynodeend{63.1}{0}{EB}{EB}

    \sankeyadvance{P}{10mm}
    \coordinate (P label) at (P);
    \sankeyadvance{P}{5mm}

    \sankeyfork{P}{33.5/NV,445.7/P}

    {
      \tikzset{sankey fill/.append style={cyan!80!lime!50!gray}}
      \sankeyturn{NV}{90}
      \sankeyadvance{NV}{2cm}
      \coordinate (NV label) at (NV);
      \sankeyadvance{NV}{2cm}
      \sankeynodeend{33.5}{0}{NV}{NV}
    }

    \sankeyadvance{P}{10mm}

    \sankeyfork{P}{118.1/U,327.6/P}

    {
      \tikzset{sankey fill/.append style={orange!70!gray!50}}
      \sankeyturn{U}{90}
      \sankeyadvance{U}{2cm}
      \coordinate (U label) at (U);
      \sankeyadvance{U}{2cm}
      \sankeynodeend{118.1}{0}{U}{U}
    }

    \sankeyadvance{P}{10mm}

    \sankeyfork{P}{327.2/P,0.4/SD}

    {
      \sankeyturn{SD}{-90}
      \sankeyadvance{SD}{15mm}
      \coordinate (SD label) at (SD);
      \sankeyadvance{SD}{15mm}
      \sankeynodeend{0.4}{0}{SD}{SD}
    }

    \sankeyadvance{P}{8mm}

    \sankeyfork{P}{18.8/VE,308.4/E}

    {
      \tikzset{sankey fill/.append style={orange!70!gray!30}}
      \sankeyturn{VE}{90}
      \sankeyadvance{VE}{2cm}
      \coordinate (VE label) at (VE);
      \sankeyadvance{VE}{2cm}
      \sankeynodeend{18.8}{0}{VE}{VE}
    }

    \sankeyadvance{E}{8mm}
    \coordinate (E label) at (E);
    \sankeyadvance{E}{20mm}

    \sankeyfork{E}{135.1/H+GHD,87.2/V,86.1/In}

    \sankeyturn{In}{-90}
    \sankeyadvance{In}{10mm}
    \sankeyturn{In}{90}
    \sankeyadvance{In}{5mm}
    \coordinate (In label)  at (In);
    \sankeyadvance{In}{10mm}
    \sankeynodeend{86.7}{-90}{In}{In}

    \sankeyadvance{V}{19mm}
    \coordinate (V label) at (V);
    \sankeyadvance{V}{10mm}
    \sankeynodeend{87.2}{-90}{V}{V}

    \sankeyturn{H+GHD}{90}
    \sankeyadvance{H+GHD}{10mm}
    \sankeyfork{H+GHD}{47.0/GHD,88.1/H}

    \sankeyturn{H}{-90}
    \sankeyadvance{H}{.5mm}
    \coordinate (H label) at (H);
    \sankeyadvance{H}{10mm}
    \sankeynodeend{88.1}{-90}{H}{H}

    \sankeyadvance{GHD}{30mm}
    \sankeyturn{GHD}{-90}
    \sankeyadvance{GHD}{8.5mm}
    \coordinate (GHD label) at (GHD);
    \sankeyadvance{GHD}{10mm}
    \sankeynodeend{47}{-90}{GHD}{GHD}



    % labels
    \tikzset{
      label/.style={
        fill=white,inner sep=.5mm,text=cyan!50!blue!50!black,
        font=\sffamily\bfseries\small,inner sep=1mm,
        align=center,
      },
    }
    \node[label,anchor=north] (B label) at (B label) {7.1};
    \node[label,left=1mm of B label] {Bestands-\\entnahme};
    \node[label,anchor=north] at (GI label) {137.3};
    \node[label,above=5mm of GI label] {Gewinnung\\im Inland};
    \node[label,anchor=north] at (I label) {397.8};
    \node[label,above=5mm of I label] {Import};

    \node[label] at (EI label) {542.3\\Energieaufkommen im Inland};

    \node[label,anchor=center] (EB label) at (EB label) {63.1};
    \node[label,above=1mm of EB label] {Export und\\Bunkerung};

    \node[label] at (P label) {479.1\\Primärenergieverbrauch};

    \node[label,anchor=center] (NV label) at (NV label) {33.5};
    \node[label,above=0mm of NV label] {Nichtenergetischer Verbrauch};

    \node[label,anchor=center] (U label) at (U label) {118.1};
    \node[label,below=4mm of U label] {Umwandlungsverluste};

    \node[label,anchor=center] (SD label) at (SD label) {0.4};
    \node[label,above=0mm of SD label] {Statistische\\Differenzen};

    \node[label,anchor=center] (VE label) at (VE label) {18.8};
    \node[label,below=0mm of VE label] {Verbrauch in den\\Energiesktoren};

    \node[label,anchor=north] (E label) at (E label) {308.4\\Endenergieverbrauch};

    \node[label,anchor=north] (In label) at (In label) {86.1};
    \node[label,anchor=north,below=1cm of In label] {Industrie};

    \node[label,anchor=north] (V label) at (V label) {87.2};
    \node[label,anchor=north,below=1cm of V label] {Verkehr};

    \node[label,anchor=north] (H label) at (H label) {87.2};
    \node[label,anchor=north,below=1cm of H label] {Haushalte};

    \node[label,anchor=north] (GHD label) at (GHD label) {47.0};
    \node[label,anchor=north,below=1cm of GHD label] {Gewerbe, Handel\\Diensleistungen};
 \end{sankeydiagram}
\end{tikzpicture}
\end{document}

答案2

直接使用 tikz 的一个很好的替代方案是 Matplotlib 和 Sankey 模块。你可以在这里看到示例代码:Matplotlib Sankey 示例

我认为这是创建桑基图的更好的方法。

使用 XeLaTeX 和 matplotlib 1.2,您可以使用matplotlib-后端-pgf

使用桑基示例图并创建 pdf 图的简短代码示例:

import matplotlib as mpl
mpl.use("module://backend_pgf")
import matplotlib.pyplot as plt
from matplotlib.sankey import Sankey

Sankey(flows=[0.25, 0.15, 0.60, -0.20, -0.15, -0.05, -0.50, -0.10],
   labels=['', '', '', 'First', 'Second', 'Third', 'Fourth', 'Fifth'],
   orientations=[-1, 1, 0, 1, 1, 1, 0, -1]).finish()

# remove the frame in the sankey diagram:
a = plt.gca()
a.set_frame_on(False)

# Save it to pdf:
plt.savefig("sankey.pdf", bbox_inches='tight')

然后,您可以在 tex 文件中包括 pdf 图像:

% tex file:
\includegraphics[width=0.7\textwidth]{img/sankey.pdf}

答案3

我认为双倍距离的使用是此类绘图的关键。以下是一个小例子

\documentclass{standalone}

\usepackage{tikz}


\begin{document}
\begin{tikzpicture}
\draw[help lines] (-2,-4)grid(9,5);
\def\lI{4cm}
\def\rIfrazI{.4}
\pgfmathsetmacro\rIfrazII{1-\rIfrazI}

\def\rl{2}
\draw[line width      = 2pt,
      double distance = \lI-\pgflinewidth] (0,0)--(\rl,0);

%%%%%%%%%%%%
% BRANCH I %
%%%%%%%%%%%%
\def\rIl{5}
\draw[line width      = 2pt,
      double distance = \rIfrazI*\lI-\pgflinewidth] (\rl,{(1/2-\rIfrazI/2)*\lI})--++(\rIl,0);

% ... you continue

%%%%%%%%%%%%%
% BRANCH II %
%%%%%%%%%%%%%
\def\rIIl{2.7}
\draw[line width      = 2pt,
      double distance = \rIfrazII*\lI-\pgflinewidth]
                   (\rl,{(-1/2+\rIfrazII/2)*\lI})--++
                   (\rIIl,0)arc[radius      = {(\rIfrazII*\lI-\pgflinewidth)/2+1cm},
                                start angle = 90,
                                end angle   = 0]
                            coordinate(r);

% ... you continue
\end{tikzpicture}
\end{document}

显然,要发展和进步。

答案4

只是想让你知道:atan2 函数被改为pgf/tikz 3.0。 你需要每次调用 atan2 时切换 x 和 y 参数这么说吧,工作代码是:

\pgfdeclarelayer{background}
\pgfdeclarelayer{foreground}
\pgfdeclarelayer{sankeydebug}
\pgfsetlayers{background,main,foreground,sankeydebug}

\newif\ifsankeydebug

\newenvironment{sankeydiagram}[1][]{

  \def\sankeyflow##1##2{% sn, en
    \path[sankey fill]
    let
    \p1=(##1.north east),\p2=(##1.south east),
    \n1={atan2(\y1-\y2,\x1-\x2)-90},
    \p3=(##2.north west),\p4=(##2.south west),
    \n2={atan2(\y3-\y4,\x3-\x4)+90}
    in
    (\p1) to[out=\n1,in=\n2] (\p3) --
    (\p4) to[in=\n1,out=\n2] (\p2) -- cycle;
    \draw[sankey draw]
    let
    \p1=(##1.north east),\p2=(##1.south east),
    \n1={atan2(\y1-\y2,\x1-\x2)-90},
    \p3=(##2.north west),\p4=(##2.south west),
    \n2={atan2(\y3-\y4,\x3-\x4)+90}
    in
    (\p1) to[out=\n1,in=\n2] (\p3)
    (\p4) to[in=\n1,out=\n2] (\p2);
  }


  \tikzset{
    sankey tot length/.store in=\sankeytotallen,
    sankey tot quantity/.store in=\sankeytotalqty,
    sankey min radius/.store in=\sankeyminradius,
    sankey arrow length/.store in=\sankeyarrowlen,
    sankey debug/.is if=sankeydebug,
    sankey debug=false,
    sankey flow/.style={
      to path={
        \pgfextra{
          \pgfinterruptpath
          \edef\sankeystart{\tikztostart}
          \edef\sankeytarget{\tikztotarget}
          \sankeyflow{\sankeystart}{\sankeytarget}
          \endpgfinterruptpath
        }
      },
    },
    sankey node/.style={
      inner sep=0,minimum height={sankeyqtytolen(##1)},
      minimum width=0,draw=none,line width=0pt,
    },
    % sankey angle
    sankey angle/.store in=\sankeyangle,
    % sankey default styles
    sankey fill/.style={line width=0pt,fill,white},
    sankey draw/.style={draw=black,line width=.4pt},
  }

  \newcommand\sankeynode[4]{%prop,orientation,name,pos
    \node[sankey node=##1,rotate=##2] (##3) at (##4) {};
    \ifsankeydebug
    \begin{pgfonlayer}{sankeydebug}
      \draw[red,|-|] (##3.north west) -- (##3.south west);
      \pgfmathsetmacro{\len}{sankeyqtytolen(##1)/3}
      \draw[red] (##3.west)
      -- ($(##3.west)!\len pt!90:(##3.south west)$)
      node[font=\tiny,text=black] {##3};
    \end{pgfonlayer}
    \fi
  }

  \newcommand\sankeynodestart[4]{%prop,orientation,name,pos
    \sankeynode{##1}{##2}{##3}{##4}
    \begin{scope}[shift={(##3)},rotate=##2]
      \path[sankey fill]
      (##3.north west) -- ++(-\sankeyarrowlen,0)
      -- ([xshift=-\sankeyarrowlen/6]##3.west)
      -- ([xshift=-\sankeyarrowlen]##3.south west)
      -- (##3.south west) -- cycle;
      \path[sankey draw]
      (##3.north west) -- ++(-\sankeyarrowlen,0)
      -- ([xshift=-\sankeyarrowlen/6]##3.west)
      -- ([xshift=-\sankeyarrowlen]##3.south west)
      -- (##3.south west);
    \end{scope}
  }

  \newcommand\sankeynodeend[4]{%prop,orientation,name,pos
    \sankeynode{##1}{##2}{##3}{##4}
    \begin{scope}[shift={(##3)},rotate=##2]
      \path[sankey fill]
      (##3.north east)
      -- ([xshift=\sankeyarrowlen]##3.east)
      -- (##3.south west) -- cycle;
      \path[sankey draw]
      (##3.north east)
      -- ([xshift=\sankeyarrowlen]##3.east)
      -- (##3.south west);
    \end{scope}
  }

  \newcommand\sankeyadvance[3][]{%newname,name,distance
    \edef\name{##2}
    \ifstrempty{##1}{
      \def\newname{##2}
      \edef\name{##2-old}
      \path [late options={name=##2,alias=\name}];
    }{
      \def\newname{##1}
    }
    \path
    let
    % sankey node angle
    \p1=(##2.north east),
    \p2=(##2.south east),
    \n1={atan2(\y1-\y2,\x1-\x2)-90},
    % sankey prop
    \p3=($(\p1)-(\p2)$),
    \n2={sankeylentoqty(veclen(\x3,\y3))},
    % next position
    \p4=($(##2.east)!##3!-90:(##2.north east)$)
    in
    \pgfextra{
      \pgfmathsetmacro{\prop}{\n2}
      \pgfinterruptpath
      \sankeynode{\prop}{\n1}{\newname}{\p4}
      \path (\name) to[sankey flow] (\newname);
      \endpgfinterruptpath
    };
  }

  \newcommand\sankeyturn[3][]{%newname,name,angle
    \edef\name{##2}
    \ifstrempty{##1}{
      \def\newname{##2}
      \edef\name{##2-old}
      \path [late options={name=##2,alias=\name}];
    }{
      \def\newname{##1}
    }
    \ifnumgreater{##3}{0}{
      \typeout{turn acw: ##3}
      \path
      let
      % sankey node angle
      \p1=(##2.north east),
      \p2=(##2.south east),
      \p3=($(\p1)!-\sankeyminradius!(\p2)$),
      \n1={atan2(\y1-\y2,\x1-\x2)-90},
      % sankey prop
      \p4=($(\p1)-(\p2)$),
      \n2={sankeylentoqty(veclen(\x4,\y4))},
      \p5=(##2.east),
      \p6=($(\p3)!1!##3:(\p5)$)
      in
      \pgfextra{
        \pgfmathsetmacro{\prop}{\n2}
        \pgfinterruptpath
        % \fill[red] (\p3) circle (2pt);
        % \fill[blue](\p6) circle (2pt);
        \sankeynode{\prop}{\n1+##3}{\newname}{\p6}
        \path (\name) to[sankey flow] (\newname);
        \endpgfinterruptpath
      };
    }{
      \typeout{turn acw: ##3}
      \path
      let
      % sankey node angle
      \p1=(##2.south east),
      \p2=(##2.north east),
      \p3=($(\p1)!-\sankeyminradius!(\p2)$),
      \n1={atan2(\y1-\y2,\x1-\x2)+90},
      % sankey prop
      \p4=($(\p1)-(\p2)$),
      \n2={sankeylentoqty(veclen(\x4,\y4))},
      \p5=(##2.east),
      \p6=($(\p3)!1!##3:(\p5)$)
      in
      \pgfextra{
        \pgfmathsetmacro{\prop}{\n2}
        \pgfinterruptpath
        % \fill[red] (\p3) circle (2pt);
        % \fill[blue](\p6) circle (2pt);
        \sankeynode{\prop}{\n1+##3}{\newname}{\p6}
        \path (\name) to[sankey flow] (\newname);
        \endpgfinterruptpath
      };
    }
  }

  \newcommand\sankeyfork[2]{%name,list of forks
    \def\name{##1}
    \def\listofforks{##2}
    \xdef\sankeytot{0}
    \path 
    let
    % sankey node angle
    \p1=(\name.north east),
    \p2=(\name.south east),
    \n1={atan2(\y1-\y2,\x1-\x2)-90},
    % sankey prop
    \p4=($(\p1)-(\p2)$),
    \n2={sankeylentoqty(veclen(\x4,\y4))}
    in
    \pgfextra{
      \pgfmathsetmacro{\iprop}{\n2}
    }
    \foreach \prop/\name[count=\c] in \listofforks {
      let
      \p{start \name}=($(\p1)!\sankeytot/\iprop!(\p2)$),
      \n{nexttot}={\sankeytot+\prop},
      \p{end \name}=($(\p1)!\n{nexttot}/\iprop!(\p2)$),
      \p{mid \name}=($(\p{start \name})!.5!(\p{end \name})$)
      in
      \pgfextra{
        \xdef\sankeytot{\n{nexttot}}
        \pgfinterruptpath
        \sankeynode{\prop}{\n1}{\name}{\p{mid \name}}
        \endpgfinterruptpath
      }
    }
    \pgfextra{
      \pgfmathsetmacro{\diff}{abs(\iprop-\sankeytot)}
      \pgfmathtruncatemacro{\finish}{\diff<0.01?1:0}
      \ifnumequal{\finish}{1}{}{
        \message{*** Warning: bad sankey fork (maybe)...}
        \message{\iprop-\sankeytot}
      }
    };
  }

  \tikzset{
    % default values,
    declare function={
      sankeyqtytolen(\qty)=\qty/\sankeytotalqty*\sankeytotallen;
      sankeylentoqty(\len)=\len/\sankeytotallen*\sankeytotalqty;
    },
    sankey tot length=100pt,
    sankey tot quantity=100,
    sankey min radius=30pt,%
    sankey arrow length=10pt,%
    % user values
    #1}
}{
}

相关内容