# Grid - sizing and spacing problems with spanning cells

I am trying to set up a Grid with cells that span two rows and are centered on those rows, but I am having troubles with the sizing and, therefore, spacing of the elements. For illustration I can get something that has at least part of the layout right by using TableForm, but this will not work in practice because the columns do not align, there is no Dividers option, Item is not fully supported, etc.

dot = Graphics[{Pink, Disk[]}, ImageSize -> 40];

dat = {{"Angel Falls", "17.7 m", "0.82", "9.2"},
{"Bridalveil Fall", "6.9 m", "0.94", "9.8"},
{"Cascata delle Marmore", "8.25 m", "0.87", "6.3"},
{"Cumberland Falls", "182.0 m", "0.87", "6.3"},
{"Dettifoss", "7.41 m", "0.88", "6.7"}};

table = {dot, {#, {##2}}} & @@@ dat;

TableForm[table, TableSpacing -> None]


Notice that the pink dot is roughly centered between the two lines, and there is no excess space around the text. If the dots are resized the alignment is retained:

Now compare the result of Grid:

{
{dot, Item[#, Alignment -> Left], \[SpanFromLeft], \[SpanFromLeft]},
{\[SpanFromAbove], Item[#2, Alignment -> "."], #3, #4 "cms"}
} & @@@ dat;

Grid[Join @@ %,
Alignment -> {Center, Center},
Spacings -> {Automatic, 0},
Dividers -> All
]


The spacing and alignment is all wrong because the second element on the first row of each pair (e.g. "Angel Falls") is given the vertical size of the dot. If the dots are a different size the alignment changes:

How can I get the vertical alignment shown in the TableForm example while retaining all the features of Grid?

To clarify, it is fine if the lines end up further apart when the size of the dot is increased, but they should remain centered. Also, I expect that when the size of the dot is twice as high as a line of text (or other objects) that there will be no extra space. To fully answer this question I need a way for the cell containing "Angel Falls" to have less height than the dot.

• BTW, shouldn't it be Bridalviel not Bridalveil? – m_goldberg Oct 20 '12 at 16:59
• @m_goldberg I just copied some random names as a sample. The name appears to be correct: yosemitehikes.com/yosemite-valley/bridalveil-fall/… – Mr.Wizard Oct 20 '12 at 17:01
• Isn't the underlying box of TableForm a nested GridBox like GridBox[{{GraphicsBox[...],GridBox[...]},{GraphicsBox[...],GridBox[...]}, ...}]? – Silvia Aug 17 '14 at 8:47
• @Silvia Nesting is a problem; if you nest the grids you no longer can align or span across them, etc. Please see my comment under m_goldberg's answer. – Mr.Wizard Aug 17 '14 at 16:03

This certainly doesn't provide an answer, but I thought it might prompt some more ideas.

dotSizes = {20, 40, 20, 60, 30};
dots = Graphics[{Pink, Disk[]}, ImageSize -> #] & /@ dotSizes;

dat = {{"Angel Falls", "17.7 m", "0.82", "9.2"}, {"Bridalveil Fall",
"6.9 m", "0.94", "9.8"}, {"Cascata delle Marmore", "8.25 m",
"0.87", "6.3"}, {"Cumberland Falls", "182.0 m", "0.87",
"6.3"}, {"Dettifoss", "7.41 m", "0.88", "6.7"}};

dat2 = Flatten[{
{Item[#[[1]],
Alignment -> {Left,
Bottom}], \[SpanFromLeft], \[SpanFromLeft]},
{Item[#[[2]], Alignment -> {".", Top}],
Item[#[[3]], Alignment -> {".", Top}],
Item[#[[4]] "cms", Alignment -> {".", Top}]}
} & /@ dat, 1];

Grid[
{{Column[dots, Alignment -> {Center, Center}, Dividers -> All,
ItemSize ->  {Automatic, 5}],
Grid[dat2, Spacings -> {Automatic, 1.25}, Dividers -> All,
ItemSize -> {Automatic, 1.75}]}
}, Alignment -> Bottom, Dividers -> All]


OK, I fudge things a bit in this (and I've got too many Dividers). Still, I think one might gain something by dividing up the column of dots and the rest of the data. Certainly not elegant, but it just gives you more control of the formatting.

Now, if one could find a scaling relationship between the vertical ItemSize (or maybe Spacings) and dotSizes I think one could get pretty close to what Mr. Wizard wants.

• This is distinctly more stable than the version I've been using so you get a +1. Unfortunately we now have spacing forced to the largest size, and should an element exceed that size the entire thing breaks, so I'm back to manually sizing to the largest. (Incidentally I'm pretty sure Alignment -> {Left, Bottom} doesn't do anything different from Alignment -> Left.) – Mr.Wizard Oct 21 '12 at 5:50
• @Mr.Wizard -- Thanks for the +1. I wonder if you could append or prepend a vertical line to each row of text. Size the line at 1/2 the height of the circle and color it the same as the background. That might automatically align everything without playing with spacing so much and solve the problem spacing forced to the largest size. What I tried to do with the Alignments {Left, Bottom} & {".",Top} was to hold the titles down and the stats up, to also help keep everything aligned. Still needs some thought. – Jagra Oct 21 '12 at 15:31

Maybe using sub-grids in the second column will more be like what you want. I tried it out and got something that was close to the TableForm layout.

color = RGBColor[0., .5, 1.];

dots = Graphics[{color, Disk[]}, ImageSize -> #] & /@ {40, 60, 50, 60, 40};

data = {{"Angel Falls", "17.7 m", "0.82", "9.2"},
{"Bridalveil Fall", "6.9 m", "0.94", "9.8"},
{"Cascata delle Marmore", "8.25 m", "0.87", "6.3"},
{"Cumberland Falls", "182.0 m", "0.87", "6.3"},
{"Dettifoss", "7.41 m", "0.88", "6.7"}};

combined = (Prepend[#2, #1] & @@ #) & /@ Transpose[{dots, data}];

gridStuff = {Item[#1,
Alignment -> {Center, Center}],
Item[Grid[{{Item[#2, Alignment -> Left],
\[SpanFromLeft],
\[SpanFromLeft]},
{#3, #4, #5 "cms"}}],
Alignment -> {Left, Center}]} & @@@ combined;

Grid[gridStuff, Dividers -> All]


It's true that the numbers are no longer decimal aligned, but you didn't get that with TableForm either.

• I really do appreciate your efforts, and I'm sorry that I can't be more positive. The problem with this is that the various columns of the sub-grids do not align with each other. I could use a fixed width and spacing in the sub-grids to get around this but then I lose a lot of the automatic function of Grid, and I might be better off constructing a Graphics object with Text etc., if I'm going to have to do it all manually anyway. – Mr.Wizard Oct 20 '12 at 16:56
• @Mr.Wizard, I spent a considerable amount of time looking at this. I considered the above solution and rejected posting it for exactly the reasons you point out...the decimal alignment cannot be preserved using this method. Some things I did discover--the vertical spanning seem to behave better if the Grid does not also have horizontal spanning. And the presence of a vertical alignment is playing a role in making this worse as well. But there are simply no control structures for how spanning gets distributed, so if the default behavior fails, there's not much you can do. – John Fultz Oct 20 '12 at 21:58
• @JohnFultz Do you know if any work has been done or progress made on this issue since you wrote the comment above? – Mr.Wizard Aug 17 '15 at 4:44

color = RGBColor[0., .5, 1.];

dots = Graphics[{color, Disk[]}, ImageSize -> #] & /@ {40, 60, 50, 60,
40}

data = {{"Angel Falls", "17.7 m", "0.82", "9.2"}, {"Bridalveil Fall",
"6.9 m", "0.94", "9.8"}, {"Cascata delle Marmore", "8.25 m",
"0.87", "6.3"}, {"Cumberland Falls", "182.0 m", "0.87",
"6.3"}, {"Dettifoss", "7.41 m", "0.88", "6.7"}};

GraphicsGrid[Partition[Flatten[{dots , data}\[Transpose]], 5]]


edit 2

Using insets for graphics may improve things...

dots = Graphics[{Inset[Graphics[{Blue, Disk[]}, ImageSize -> #]]},
ImageSize -> 70] & /@ {40, 60, 50, 60, 40};
data = {{"Angel Falls", "17.7 m", "0.82", "9.2"}, {"Bridalveil Fall",
"6.9 m", "0.94", "9.8"}, {"Cascata delle Marmore", "8.25 m",
"0.87", "6.3"}, {"Cumberland Falls", "182.0 m", "0.87",
"6.3"}, {"Dettifoss", "7.41 m", "0.88", "6.7"}};

merged = (Prepend[#2, #1] & @@ #) & /@ Transpose[{dots, data}];

grid = Flatten[{
{Item[#, Alignment -> Center], #2, SpanFromLeft, SpanFromLeft},
{SpanFromAbove, Item[#3, Alignment -> "."], #4, #5 "cms"}
} & @@@ merged , 1];

Grid[grid, Dividers -> All]


Edit 3 Using Text style, text alignment to format the text. And adding ItemSize -> {0, 5} to grid or Items and don't let the grid to use default text height or Calculate separately for each item...

grid = Flatten[{{Item[#, Alignment -> {Left, Center}],
Item[Text[Style[#2]], Alignment -> {Left, Bottom}],
SpanFromLeft, SpanFromLeft}, {SpanFromAbove,
Item[Text[Style[#3, TextAlignment -> {"."}]],
Alignment -> {Left, Top}],
Item[Text[Style[#4]], Alignment -> {Left, Top}],
Item[Text[Style[#5 "cms"]], Alignment -> {Left, Top}]}} & @@@
merged, 1];

Grid[grid, Dividers -> All, ItemSize -> {0, 5}]

• Unfortunately I cannot seem to get spanning to work with this, and that is the heart of the problem. Nevertheless I haven't considered this before so I'll keep trying. – Mr.Wizard Oct 21 '12 at 5:57
• Please see the edit 2. I hope using Inset improves a bit. – s.s.o Oct 22 '12 at 12:21
• Re: edit 2 I am presently using a fixed ImageSize in my own code. It still doesn't fix the problem of needed to re-space the Grid if you change that ImageSize (e.g. to accommodate a new, larger image). Further, it doesn't allow for rows to have different heights as shown here. Nevertheless this is a good method to document and I sincerely appreciate your effort on this problem: +1. – Mr.Wizard Oct 22 '12 at 12:36
• dots = Graphics[{Inset[Graphics[{Blue, Disk[]}, ImageSize -> #]]}, ImageSize -> 70] & /@ {40, 60, 50, 60, 40}; If you change the size of the graphics it changes the grid sizes as well. simply changing 70 to #. The whole grid resizes. – s.s.o Oct 22 '12 at 12:56
• Yes, but the rows are not centered around the dot, which requires readjustment. – Mr.Wizard Oct 22 '12 at 12:59

If you're OK with splitted Graphics, then here is my hack. It looks fine as long as you just concern about the appearance.

Example data:

dots = Graphics[{Pink, Disk[]}, ImageSize -> #] & /@
RandomSample[{10, 50, 100, 150, 200}];

data = {{"Angel Falls", "17.7 m", "0.82", "9.2"}, {"Bridalveil Fall",
"6.9 m", "0.94", "9.8"}, {"Cascata delle Marmore", "8.25 m",
"0.87", "6.3"}, {"Cumberland Falls", "182.0 m", "0.87",
"6.3"}, {"Dettifoss", "7.41 m", "0.88", "6.7"}};


Function splitting the Disk into up and down half parts:

Clear[superRowComposeFunc]
superRowComposeFunc[dot_Graphics, data_List] :=
Module[{dotUp, dotDown},
dotUp = Show[dot,
PlotRange -> {All, {0, All}},
ImagePadding -> {{1, 1}, {0, 1}},
BaselinePosition -> (Bottom -> Bottom)];
dotDown = Show[dot,
PlotRange -> {All, {All, 0}},
ImagePadding -> {{1, 1}, {1, 0}},
BaselinePosition -> (Top -> Top)];
{
{Item[dotUp, Alignment -> {Center, Baseline}],
data[[1]],
Sequence @@ ConstantArray[SpanFromLeft, Length[data] - 1]},
{Item[dotDown, Alignment -> {Center, Baseline}],
Sequence @@ data[[2 ;;]]}
}
]


Function composing the Grid:

Clear[superGridComposeFunc]
superGridComposeFunc[dots_, data_] := Grid[
MapThread[superRowComposeFunc, {dots, data}] // Flatten[#, 1] &,
Spacings -> {Automatic, {{.5, 0}}},
ItemSize -> Full,
Alignment -> Left,
Frame -> {None, None,
Flatten[{
{{#, # + 1}, {1, 1}} -> True,
{{#, # + 1}, {2, -1}} -> True
} & /@ Range[1, 2 Length[dots], 2]
]}
]


Try it on the example data:

superGridComposeFunc[dots, data]


• Although I don't know how generally one can apply this that's a pretty nice trick. +1 – Mr.Wizard Aug 18 '14 at 5:40
• @Mr.Wizard Thanks. I have tested some ImageSize from 1 to 200, it looks like when it's less than 6, this trick will suffer from this problem. Except that, I believe it'll work fine. – Silvia Aug 18 '14 at 5:56
• Have you tried extending this to more than two "data rows" per "graphic row"? Not that I expect you to implement that for me, merely tell me if you've tried it. – Mr.Wizard Aug 18 '14 at 5:59
• @Mr.Wizard No I haven't. But in that case I would try another approach, maybe an Overlay in which a Graphics align with a text Grid. There must be some (undocumented) functions who can measure the size of boxes or line-height in the FE. – Silvia Aug 18 '14 at 6:06