2

I want to automatically build a PGF/TikZ based drawing based on some external data that is read using csvsimple. My main issue at the moment is that most of the nodes are part of a matrix, mainly to setup a consistent spacing. Asssembling the contents of \matrix{csv-stuff-goes-here} didn't get me very far, so I tried to use \NewEnviron to define a custom matrix environment. The first examples looked promising:

\documentclass{article}
\usepackage{environ}
\usepackage{tikz}

\begin{document}

% working example
\begin{tikzpicture}[every matrix/.style = {column sep = 0.8 cm, row sep = 0.2 cm}]
  \matrix{
    \draw (0,0) circle (4mm); & \node (n1) {Hello}; \\
    \draw (0,0) circle (2mm); & \node (n2) {world}; \\
  };
  \draw[thick, draw=black] (n1) -- (n2);
\end{tikzpicture}

% this works as well
\NewEnviron{TheMatrix}{\matrix[ampersand replacement=\&]{\BODY};}
\begin{tikzpicture}[every matrix/.style = {column sep = 0.8 cm, row sep = 0.2 cm}]
  \begin{TheMatrix}
    \draw (0,0) circle (4mm); \& \node (n1) {Hello}; \\        
    \draw (0,0) circle (2mm); \& \node (n2) {world}; \\        
  \end{TheMatrix} 
  \draw[thick, draw=black] (n1) -- (n2);
\end{tikzpicture}

\end{document}

Now I wanted to introduce the csvsimple package - and failed miserably. MNWE:

\documentclass{article}
\usepackage{environ}
\usepackage{filecontents}
\usepackage{csvsimple}
\usepackage{tikz}

\begin{filecontents*}{mystuff.csv}
mynode;mytext
n1;Hello
n2;World
\end{filecontents*}

\begin{document}

% working example
\begin{tikzpicture}[every matrix/.style = {column sep = 0.8 cm, row sep = 0.2 cm}]
  \matrix{
    \draw (0,0) circle (4mm); & \node (n1) {Hello}; \\
    \draw (0,0) circle (2mm); & \node (n2) {world}; \\
  };
  \draw[thick, draw=black] (n1) -- (n2);
\end{tikzpicture}

% this works as well
\NewEnviron{TheMatrix}{\matrix[ampersand replacement=\&]{\BODY};}
\begin{tikzpicture}[every matrix/.style = {column sep = 0.8 cm, row sep = 0.2 cm}]
  \begin{TheMatrix}
    \draw (0,0) circle (4mm); \& \node (n1) {Hello}; \\        
    \draw (0,0) circle (2mm); \& \node (n2) {world}; \\        
  \end{TheMatrix} 
  \draw[thick, draw=black] (n1) -- (n2);
\end{tikzpicture}

% now let's try it from a CSV file
\begin{tikzpicture}[every matrix/.style = {column sep = 0.8 cm, row sep = 0.2 cm}]
  \begin{TheMatrix}
    \csvreader[separator = semicolon, head to column names]{mystuff.csv}{}{
      \draw (0,0) circle (4mm); \& \node (\mynode) {\mytext}; \\        
    }
  \end{TheMatrix} 
  \draw[thick, draw=black] (n1) -- (n2);
\end{tikzpicture}

\end{document}

The reported error is

pdflatex.exe> ! Missing } inserted.
pdflatex.exe> <inserted text> 
pdflatex.exe>                 }
pdflatex.exe> l.40   \end{TheMatrix}

I have no idea why the system assumes there's abracket missing and how to find out what actually went wrong. I've tried to turn on some additional tracing with

\tracinggroups=1
\tracingnesting=2

but I have no idea how to get any meaning from the output. What is going on here and how do I debug things like this?

  • There could be some trickery to build up a \BODY-like macro from the CSV contents and to parse that afterwards inside a \matrix, but maybe another approach instead of \matrix is preferrable? – Thomas F. Sturm Jan 22 '15 at 16:18
  • I don't know why but adding double backslash (I don't know how to write it here) before \end{TheMatrix} worked for me. – Ignasi Jan 22 '15 at 16:22
  • @ThomasF.Sturm I've converted the comment in answer to better show what I did. – Ignasi Jan 22 '15 at 16:34
1

Here is a CSV approach with some trickery. The CSV file is read and its content put inside a macro \BODY. This \BODY is used inside the matrix later.

This may not work, if the CSV content contains macros or umlauts since everything is expanded before it is added to \BODY.

\documentclass{article}
\usepackage{environ}
\usepackage{filecontents}
\usepackage{csvsimple,etoolbox}
\usepackage{tikz}

\begin{filecontents*}{mystuff.csv}
mynode;mytext
n1;Hello
n2;World
\end{filecontents*}

\begin{document}

% working example
\begin{tikzpicture}[every matrix/.style = {column sep = 0.8 cm, row sep = 0.2 cm}]
  \matrix{
    \draw (0,0) circle (4mm); & \node (n1) {Hello}; \\
    \draw (0,0) circle (2mm); & \node (n2) {world}; \\
  };
  \draw[thick, draw=black] (n1) -- (n2);
\end{tikzpicture}

% this works as well
\NewEnviron{TheMatrix}{\matrix[ampersand replacement=\&]{\BODY};}
\begin{tikzpicture}[every matrix/.style = {column sep = 0.8 cm, row sep = 0.2 cm}]
  \begin{TheMatrix}
    \draw (0,0) circle (4mm); \& \node (n1) {Hello}; \\ 
    \draw (0,0) circle (2mm); \& \node (n2) {world}; \\ 
  \end{TheMatrix}
  \draw[thick, draw=black] (n1) -- (n2);
\end{tikzpicture}

\def\BODY{}
% now let's try it from a CSV file
\begin{tikzpicture}[every matrix/.style = {column sep = 0.8 cm, row sep = 0.2 cm}]
  \csvreader[separator = semicolon, head to column names]{mystuff.csv}{}{
    \eappto\BODY{\noexpand\draw (0,0) circle (4mm); \noexpand\& \noexpand\node (\mynode) {\mytext}; \noexpand\\}
  }
  \matrix[ampersand replacement=\&]{\BODY};
  \draw[thick, draw=black] (n1) -- (n2);
\end{tikzpicture}

\end{document}
2

This is not an answer, but I didn't know how to include \\ inside a comment. I've inserted \\ before \end{TheMatrix} in last example and worked. Why? I've no idea, just luck.

\documentclass{article}
\usepackage{environ}
\usepackage{filecontents}
\usepackage{csvsimple}
\usepackage{tikz}

\begin{filecontents*}{mystuff.csv}
mynode;mytext
n1;Hello
n2;World
\end{filecontents*}

\begin{document}

% working example
\begin{tikzpicture}[every matrix/.style = {column sep = 0.8 cm, row sep = 0.2 cm}]
  \matrix{
    \draw (0,0) circle (4mm); & \node (n1) {Hello}; \\
    \draw (0,0) circle (2mm); & \node (n2) {world}; \\
  };
  \draw[thick, draw=black] (n1) -- (n2);
\end{tikzpicture}

% this works as well
\NewEnviron{TheMatrix}{\matrix[ampersand replacement=\&]{\BODY};}
\begin{tikzpicture}[every matrix/.style = {column sep = 0.8 cm, row sep = 0.2 cm}]
  \begin{TheMatrix}
    \draw (0,0) circle (4mm); \& \node (n1) {Hello}; \\        
    \draw (0,0) circle (2mm); \& \node (n2) {world}; \\        
  \end{TheMatrix} 
  \draw[thick, draw=black] (n1) -- (n2);
\end{tikzpicture}

% now let's try it from a CSV file
\begin{tikzpicture}[every matrix/.style = {column sep = 0.8 cm, row sep = 0.2 cm}]
  \begin{TheMatrix}
    \csvreader[separator = semicolon, head to column names]{mystuff.csv}{}{
      \draw (0,0) circle (4mm); \& \node (\mynode) {\mytext}; \\        
    }\\  %<------------------- This is what I added
  \end{TheMatrix} 
  \draw[thick, draw=black] (n1) -- (n2);
\end{tikzpicture}

\end{document}

enter image description here

  • Seems, I have underestimated the combination of environ and the TikZ parser :-) – Thomas F. Sturm Jan 22 '15 at 16:34
  • Still, one double-backslash is too much. But, maybe, it is silently discarded. – Thomas F. Sturm Jan 22 '15 at 16:36
  • This approach works for the simplified example, but for some reason it fails in my real-life document. Thanks for finding another workaround though. – vwegert Jan 22 '15 at 22:38

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.