# Using foreach loop in TikZ to plot at non-uniform locations

As a Teaching Assistant for a class, I am trying to draw a seating chart for the professor. The seating chart will have a photo of each student, the student's name, and where the student is sitting in the lecture room. I need to produce two seating charts:

1. A blank seating chart where a rectangle represents each chair, for students to fill in their names where they are seated.
2. A seating chart with students' names and photos.

Before attempting to draw a seating chart for the 80+ seats in the lecture hall, I am working on a smaller example with 11 seats. Currently, the code I use to draw the blank seating chart and the output looks like the following:

\documentclass[10pt]{article}

\usepackage[margin=0in,paperwidth=17in, paperheight=11in]{geometry}

\usepackage{tikz}

% Usage: \student{x coord}{y coord}{name}{picture file}
%\newcommand{\student}[4]{%
%  \node[anchor=base,inner sep=0,
%      label={[text width=20mm,align=center]below:#3}] at (#1,#2)
%      {\includegraphics[width=17mm]{#4}};%
%}

% Draw an empty box for each chair
\newcommand{\student}[4]{
\node[anchor=base,shape=rectangle,draw,
minimum width=17mm,minimum height=22mm]
at (#1,#2) {};
}

\begin{document}

\begin{center}
\begin{tikzpicture}
\draw[white] (0,0) -- (43.1,0) -- (43.1,27.9) -- (0,27.9) -- (0,0);

\draw (5,5) -- (10,15);
\draw (12,18) -- (31,18);
\draw (38,5) -- (33,15);

\student{4}{7}{George Washington}{empty-face.png};
\student{7}{13}{Thomas Jefferson}{empty-face.png};

\student{17}{20}{James Monroe}{empty-face.png};
\student{26}{20}{Andrew Jackson}{empty-face.png};
\student{30.5}{20}{Martin Van Buren}{empty-face.png};

\student{36}{13}{William Henry Harrison}{empty-face.png};
\student{37.5}{10}{John Tyler}{empty-face.png};
\student{39}{7}{James K Polk}{empty-face.png};

\end{tikzpicture}
\end{center}

\end{document}


And the code for the seating chart with people's names and photos is very similar:

\documentclass[10pt]{article}

\usepackage[margin=0in,paperwidth=17in, paperheight=11in]{geometry}

\usepackage{tikz}

% Usage: \student{x coord}{y coord}{name}{picture file}
\newcommand{\student}[4]{%
\node[anchor=base,inner sep=0,
label={[text width=20mm,align=center]below:#3}] at (#1,#2)
{\includegraphics[width=17mm]{#4}};%
}

\begin{document}

\begin{center}
\begin{tikzpicture}
\draw[white] (0,0) -- (43.1,0) -- (43.1,27.9) -- (0,27.9) -- (0,0);

\draw[line width=5mm] (5,5) -- (10,15);
\draw[line width=5mm] (12,18) -- (31,18);
\draw[line width=5mm] (38,5) -- (33,15);

\student{4}{7}{George Washington}{empty-face.png};
\student{7}{13}{Thomas Jefferson}{empty-face.png};

\student{17}{20}{James Monroe}{empty-face.png};
\student{26}{20}{Andrew Jackson}{empty-face.png};
\student{30.5}{20}{Martin Van Buren}{empty-face.png};

\student{36}{13}{William Henry Harrison}{empty-face.png};
\student{37.5}{10}{John Tyler}{empty-face.png};
\student{39}{7}{James K Polk}{empty-face.png};

\end{tikzpicture}
\end{center}

\end{document}


In my prior post: Aligning image and text in new command in TikZ, Peter Grill recommended using a \foreach loop to plot multiple students. This is a great idea! Unfortunately, the seating in the lecture room is not regular. In fact, there are ~80 seats behind the desks which are shown in the image below:

My question is the following.

What I would like to do is to define a list of locations:

\newcommand{\ListOfSeats}{
(4,7) (5.5,10) (7,13) (12.5,20) (17,20)
(21.5,20) (26,20) (30.5,20) (36,13) (37.5,10) (39,7)
}


and a list of students' names and photos

\newcommand{\ListOfStudents}{
(George Washington, george-washington.jpg)
(Thomas Jefferson, thomas-jefferson.jpg)
...
}


Is there a way to then use a single \foreach loop to draw all the 80 seats, and all the 80 students with their names and photos?

Thanks for reading to the end, this was a long question but I hope you found it interesting!

-
Are you open for small changes to the syntax ({} instead of (), / instead of ,)? It would also be easier to use one list with entries like: George Washington/george-washington.jpg/4/7 – Qrrbrbirlbel Feb 17 '13 at 3:03
Definitely! I was just the syntax which seemed most natural to me, but would be happy to adopt whatever syntax works. – I Like to Code Feb 17 '13 at 3:07
What about introducing seat numbering? I think this would make it easier to enter the data from the (fill-out) blank chart. – Sašo Živanović Feb 17 '13 at 3:23
That's a great idea! – I Like to Code Feb 17 '13 at 3:28
Is the syntax George Washington, george-washington.jpg common to all the students and their images? If yes, then we don't need the 2 comma-separated elements: 1 will suffice. – Ahmed Musa Feb 17 '13 at 6:55

# Easy To Code

As described in my comment, the easiest solution to code would be one list, one foreach and four variables separated by /.

Do not introduce any spaces in the second part (the one containing the file name).

If name or file name contains a , enclose the whole variable in { } braces, e.g.

{Sir, St. Comma}/{sir,-st.-comma.jpg}/0/0


## Code

\documentclass[10pt]{article}
\usepackage[margin=0in,paperwidth=17in, paperheight=11in]{geometry}

\usepackage{tikz}

% Usage: \student{x coord}{y coord}{name}{picture file}
\newcommand{\student}[4]{%
\node[anchor=base,inner sep=0,
label={[text width=20mm,align=center]below:#3}] at (#1,#2)
{\includegraphics[width=17mm]{#4}};%
}

\newcommand*{\ListOfStudents}{%
George Washington     /example-image-10x16.jpg/ 4  / 7,
Thomas Jefferson      /example-image-10x16.jpg/ 7  /13,
James Monroe          /example-image-10x16.jpg/17  /20,
Andrew Jackson        /example-image-10x16.jpg/26  /20,
Martin Van Buren      /example-image-10x16.jpg/30.5/20,
William Henry Harrison/example-image-10x16.jpg/36  /13,
John Tyler            /example-image-10x16.jpg/37.5/10,
James K Polk          /example-image-10x16.jpg/39  / 7}

\begin{document}
\noindent
\begin{tikzpicture}
\useasboundingbox (0,0) rectangle (17in,11in);% see http://tex.stackexchange.com/a/98532/16595

\draw[line width=5mm] (5,5) -- (10,15);
\draw[line width=5mm] (12,18) -- (31,18);
\draw[line width=5mm] (38,5) -- (33,15);

\foreach \sName/\sFile/\sX/\sY in \ListOfStudents {\student{\sX}{\sY}{\sName}{\sFile}}
\end{tikzpicture}
\end{document}


## Output

If you want to have separate lists we can find the right pairs by using nested \foreach loops and two counters we can compare (if they are equal we have the right pair of (file) name and coordinates).

If no file name is given it is constructed via the lowercase name and spaces substituted with dashes (-). The xstring package is used for the substituting part.

I have also added the option that if a file name is given the extension .jpg is added automatically (although TeX/graphicx has its own rules how to deal with file names without an extension).

Note that the following code throws errors and warnings as the files are not available (but we have an easy way to check the outcoming file name).

## Code

\documentclass[10pt]{article}
\usepackage[margin=0in,paperwidth=17in, paperheight=11in]{geometry}
\usepackage{xstring}
\usepackage{tikz}

% Usage: \student{x coord}{y coord}{name}{picture file}
\newcommand{\student}[4]{%
\node[anchor=base,inner sep=0,
label={[text width=20mm,align=center]below:#3}] at (#1,#2)
{\includegraphics[width=17mm]{#4}};%
}

\newcommand{\ListOfSeats}{%
4/7, 5.5/10, 7/13, 12.5/20, 17/20, 21.5/20, 26/20, 30.5/20, 36/13, 37.5/10, 39/7}

\newcommand*{\ListOfStudents}{%
George Washington,
Thomas Jefferson,
James Monroe/different-filename,
Andrew Jackson,
Martin Van Buren,
William Henry Harrison/wh-harrison,
John Tyler,
James K Polk}

\begin{document}
\noindent
\begin{tikzpicture}
\useasboundingbox (0,0) rectangle (17in,11in);% see http://tex.stackexchange.com/a/98532/16595

\draw[line width=5mm] (5,5) -- (10,15);
\draw[line width=5mm] (12,18) -- (31,18);
\draw[line width=5mm] (38,5) -- (33,15);
\foreach \sName/\sFile[count=\iOuter from 0] in \ListOfStudents {
\foreach \sX/\sY[count=\iInner from 0] in \ListOfSeats {
\ifnum\iOuter=\iInner
\ifx\sName\sFile
\expandafter\lowercase\expandafter{\expandafter\def\expandafter\sFile\expandafter{\sName.jpg}}
\StrSubstitute{\sFile}{ }{-}[\sFile]
\else \edef\sFile{\sFile.jpg}% includes automatically the extension .jpg (comment line out if not needed)
\fi
\student{\sX}{\sY}{\sName}{\sFile}
\breakforeach
\fi
}
}
\end{tikzpicture}
\end{document}


## Output

-
The file-name-constructing code can also be implemented in the first solution … – Qrrbrbirlbel Feb 17 '13 at 8:22
Thank you for your help! This is a great solution and I learned quite a few LaTeX commands that I did not know about before. – I Like to Code Feb 17 '13 at 14:07
@ILiketoCode The macros \ifx, \ifnum, \else and \fi are technically TeX macros (as are \expandafter, \def and \lowercase), I have used them for simplicity. If you rather want a LaTeX approach (better input, better conditionals), the packages xstring and xifthen both offer a few macros that makes it easier to build conditionals (for example string testing). – Qrrbrbirlbel Feb 17 '13 at 15:45

At the risk of irritating Werner [:)], I would say that, with \newforeach, all elements (including file names, e.g., example-image-10x16.jpg) can be surrounded by spaces.

\documentclass[10pt]{article}
\usepackage[margin=0in,paperwidth=17in, paperheight=11in]{geometry}
\usepackage{tikz,loops}
\newcommand*{\ListOfStudents}{%
George Washington      / example-image-10x16.jpg / 4      / 7,
John Adams             / example-image-10x16.jpg / 5.5    / 10,
Thomas Jefferson       / example-image-10x16.jpg / 7      / 13,
James Madison          / example-image-10x16.jpg / 12.5   / 20,
James Monroe           / example-image-10x16.jpg / 17     / 20,
John Quincy Adams      / example-image-10x16.jpg / 21.5   / 20,
Andrew Jackson         / example-image-10x16.jpg / 26     / 20,
Martin Van Buren       / example-image-10x16.jpg / 30.5   / 20,
William Henry Harrison / example-image-10x16.jpg / 36     / 13,
John Tyler             / example-image-10x16.jpg / 37.5   / 10,
James K Polk           / example-image-10x16.jpg / 39     / 7
}

\begin{document}
\noindent
\begin{tikzpicture}
\useasboundingbox (0,0) rectangle (17in,11in);
\draw[line width=5mm] (5,5) -- (10,15);
\draw[line width=5mm] (12,18) -- (31,18);
\draw[line width=5mm] (38,5) -- (33,15);
\newforeach [expand list once=true] \name/\file/\x/\y in \ListOfStudents{%
\node[anchor=base,inner sep=0,
label={[text width=20mm,align=center]below:\name}] at (\x,\y)
{\includegraphics[width=17mm]{\file}};
}
\end{tikzpicture}
\end{document}


We could also have used

\foreachfox [expand list once=true, arg=#1/#2/#3/#4]\ListOfStudents{%
\edef\tempa{\skvtrimspace{#2}}%
\node[anchor=base,inner sep=0,
label={[text width=20mm,align=center]below:#1}] at (#3,#4)
{\includegraphics[width=17mm]{\tempa}};
}

-