4
$\begingroup$

It's easy to present a terrain using BarChart3D using ChartLayout -> "Grid"with the following code:

terrain = {{10, 10, 10, 10, 10, 10, 10, 10}, {10, 10, 6, 6, 4, 7, 10, 
    10}, {10, 10, 7, 10, 5, 10, 10, 10}, {10, 10, 7, 6, 5, 10, 2, 
    1}, {10, 10, 8, 10, 10, 10, 10, 10}, {10, 9, 8, 6, 4, 5, 2, 
    1}, {10, 10, 10, 5, 10, 10, 10, 10}, {10, 6, 1, 4, 1, 1, 1, 
    1}, {10, 6, 6, 3, 2, 1, 1, 1}, {10, 10, 10, 10, 10, 10, 10, 10}};
BarChart3D[terrain, ChartLayout -> "Grid", 
 Method -> {"Canvas" -> None}, ColorFunction -> "TemperatureMap", 
 BoxRatios -> {7, 10, 3}, ChartStyle -> Opacity@.7, 
 ViewPoint -> {1.3, 1.2, 4.}, ImageSize -> 300, PlotRange -> {0, 10}]

It's also easy to present the height of the water flowing on this terrain by a similar code:

water = {{0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 3, 3, 5, 2, 0, 0}, {0, 
    0, 2, 0, 4, 0, 0, 0}, {0, 0, 2, 3, 4, 0, 0, 0}, {0, 0, 1, 0, 0, 0,
     0, 0}, {0, 1, 1, 1, 2, 1, 1, 1}, {0, 0, 0, 1, 0, 0, 0, 0}, {0, 0,
     4, 1, 1, 1, 1, 1}, {0, 0, 0, 1, 1, 1, 1, 1}, {0, 0, 0, 0, 0, 0, 
    0, 0}};
BarChart3D[water, ChartLayout -> "Grid", 
 Method -> {"Canvas" -> None}, ColorFunction -> "TemperatureMap", 
 BoxRatios -> {7, 10, 3}, ChartStyle -> Opacity@.7, 
 ViewPoint -> {1.3, 1.2, 4.}, ImageSize -> 300, PlotRange -> {0, 10}]

But I would love to have a graph that shows both information: The terrain shall lay below the waterflow with 100% opacity. They should be colored just like the first code do. However, the water shall have a 30% opacity and colored blue. In this way, we can get the information of terrain as well as water elegantly in one chart with one glimpse.

However, ChartLayout->"Stacked" can put different layers together, but only in 2-D format, while ChartLayout->"Grid" cannot stack different layers. Their simple combination won't work too.

So, put it simple, my question is: how can we "Stack" a "Grid" using BarChart3D?

Thanks!


Edit

Actually I would love to put the water "on" the terrain, so it should look like the following picture, but terrain and water shall have different color: Terrain+Water

I gave out an solution in my answer, but I would still love to have one using mainly BarChart3D cause it is a more direct form I think.


Update2

Thanks everyone for providing so many elegant solution, so there's only one question left:

As you can see, Using "Grid" layout we can get a 2D+height.graph. Meanwhile, using "Stacked" layout we can get stacked result. But why there're no easy way to use them together? It seems that this will not be a hard work for Wolfram as both functionality already exist......

$\endgroup$
  • 1
    $\begingroup$ btw, why there're more answers than upvotes? $\endgroup$ – Wjx Jun 9 '16 at 2:10
  • 1
    $\begingroup$ My understanding is becase the question is not very well defined, and I was still not sure I understood what you wanted. As more answers came in, I finally got it, but this is mostly due to the answerers' efforts. No worries, it's all right, but if you want to have more upvotes, next time try to be more specific from the beginning. $\endgroup$ – István Zachar Jun 9 '16 at 7:18
7
$\begingroup$

Update

Using @kglr suggestions to overcome Histogram3D's ColorFunction limitation.

Histogram3D[
 Flatten[MapIndexed[ConstantArray[#2, #1] &, #, {2}], 2] & /@ {terrain, water},
 Automatic, "Count",
 ChartLayout -> "Stacked", ChartStyle -> {Opacity[1], Blue}, 
 ChartElementFunction -> {ChartElementDataFunction[
    "GradientScaleCube", "ColorScheme" -> "Rainbow"], Automatic}]

Mathematica graphics


Unfortunately Histogram3D will not take a list of colour functions for its ColorFunction option. Therefore the most I can do is make the ground Brown and the water Blue.

Histogram3D[
 Flatten[MapIndexed[ConstantArray[#2, #1] &, #, {2}], 2] & /@ {terrain, water},
 Automatic, "Count",
 ChartLayout -> "Stacked",
 ChartStyle -> {Opacity[1, Brown], Blue}]

Mathematica graphics

Hope this helps.

$\endgroup$
  • 1
    $\begingroup$ (+1) maybe we can use the options ChartStyle -> {Opacity[1], Blue}, ChartElementFunction -> {ChartElementDataFunction[ "GradientScaleCube", "ColorScheme" -> "Rainbow"], Automatic}] to color terrain. $\endgroup$ – kglr Jun 8 '16 at 23:36
  • $\begingroup$ @kglr That is a very nice addition. I'll update. $\endgroup$ – Edmund Jun 8 '16 at 23:42
  • $\begingroup$ This post is the closest to what I want. $\endgroup$ – Wjx Jun 9 '16 at 1:29
4
$\begingroup$

You can easily add the water matrix to the terrain matrix (and zero it where there is no water):

t = ListPlot3D[terrain, InterpolationOrder -> 0, 
   ColorFunction -> "TemperatureMap", Mesh -> None, Filling -> 0, 
   FillingStyle -> Gray, PlotRange -> All, BoundaryStyle -> None];
w = ListPlot3D[(terrain + water)*Sign[water], InterpolationOrder -> 0, 
   PlotStyle -> {Opacity@.3, Blue}, PlotRange -> All, Mesh -> None, 
   Filling -> 0, FillingStyle -> {Opacity@.3, Blue}, BoundaryStyle -> None];
Show[t, w]

Mathematica graphics

The same with BarChart3D:

t = BarChart3D[terrain, PlotRange -> All, ChartLayout -> "Grid", 
   Method -> {"Canvas" -> None}, BarSpacing -> {0, 0}, 
   ColorFunction -> "TemperatureMap", BoxRatios -> {7, 10, 3}, 
   ChartStyle -> {EdgeForm@"TemperatureMap"}, 
   ImageSize -> 300, PlotRange -> {0, 10}];
w = BarChart3D[(terrain + water)*Sign@water, PlotRange -> All, 
   ColorFunction -> (Lighter@Blue &), ChartLayout -> "Grid", 
   Method -> {"Canvas" -> None}, BarSpacing -> {0, 0}, 
   BoxRatios -> {7, 10, 3}, ChartStyle -> {Opacity@.7}, 
   ImageSize -> 300, PlotRange -> {0, 10}];
Show[t, w]

Mathematica graphics

An alternative, if the water has an even surface level:

Show[g1, Graphics3D[{Opacity@.2, EdgeForm@None, Blue, 
  Cuboid[{1, 1, 0}, {8, 10, 15}]}], ImageSize -> 500, Boxed -> False]

Mathematica graphics

$\endgroup$
  • 1
    $\begingroup$ Minecraft, anyone? $\endgroup$ – Edmund Jun 8 '16 at 13:43
  • $\begingroup$ Great solution! $\endgroup$ – Anton Antonov Jun 8 '16 at 14:08
  • $\begingroup$ Actually I would like the water to be "over" or "cover" the terrain~ $\endgroup$ – Wjx Jun 8 '16 at 14:26
  • $\begingroup$ @Wjx But the water z-values are 1) below the terrain z-values most of the time and 2) the water surface is not even. How do you expect it to look? $\endgroup$ – István Zachar Jun 8 '16 at 15:22
  • $\begingroup$ check my answer $\endgroup$ – Wjx Jun 9 '16 at 6:57
3
$\begingroup$

It seems the plot is more informative if (1) the bars are thinner and more transparent, and (2) there is a tooltip showing a 2D BarChart with stacked layout.

{n, m} = Dimensions[terrain];
{cx, cy} = {1/4, 1/4};
Graphics3D[{
  Red, Opacity[0.1], Cuboid[{1/2, 1/2, 0}, {n + 1/2, m + 1/2, 0}],
  Table[Tooltip[{
     Red, Opacity[0.1], 
     Cuboid[{i, j, 0}, {i + cx, j + cy, terrain[[i, j]]}],
     Blue, Opacity[0.3], 
     Cuboid[{i, j, terrain[[i, j]]}, {i + cx, j + cy, 
       terrain[[i, j]] + water[[i, j]]}]},
    BarChart[Transpose[{terrain[[i]], water[[i]]}], 
     PlotLabel -> Row[{"row:", i}], ChartLayout -> "Stacked"]
    ], {i, n}, {j, m}]}, Axes -> True, Boxed -> False, 
 BoxRatios -> {n/Max[n, m], m/Max[n, m], 1/3}, ImageSize -> Large]

enter image description here

XKCD update

The answer of Simon Woods made me think that we should look and the XKCD versions of the plots.

enter image description here

enter image description here

$\endgroup$
  • $\begingroup$ quite beautiful! $\endgroup$ – Wjx Jun 9 '16 at 1:28
  • $\begingroup$ @Wjx Glad you like it! $\endgroup$ – Anton Antonov Jun 9 '16 at 2:24
2
$\begingroup$

You can do it with Cuboid

Graphics3D[{Opacity[0.5], {l, w} = Dimensions[water]; 
 Table[{Blue, Cuboid[{i, j, 0}, {i + 1, j + 1, water[[i, j]]}]}, {i,l}, {j, w}],
 wmax = Max[water];
 Opacity[0.2], {l, w} = Dimensions[terrain];
  tscale = 1/3; (*scale terrain height for better look*)
 Table[{Red, Cuboid[{i, j, wmax}, {i + 1, j + 1, 
        wmax + terrain[[i, j]]tscale}]}, {i, l}, {j, w}]
}]

enter image description here

You can control the distance between water and terrain by changing wmax manually.

Using ColorSchemes

Graphics3D[{Opacity[0.8], {l, w} = Dimensions[water];
 wmin = Min[water]; wmax = Max[water];
 Table[{ColorData["Rainbow"][(water[[i, j]] - wmin)/(wmax - wmin)], 
 Cuboid[{i, j, 0}, {i + 1, j + 1, water[[i, j]]}]}, {i, l}, {j, w}],
 base = wmax; (*gap between water and terrain*)
 Opacity[0.2], {l, w} = Dimensions[terrain];
 tscale = 1/3;(*scale terrain height for better look*)
 tmin = Min[terrain]; tmax = Max[terrain];
 Table[{ColorData["DarkRainbow"][(terrain[[i, j]] - tmin)/(tmax - tmin)], 
  Cuboid[{i, j, base}, {i + 1, j + 1, 
  base + terrain[[i, j]] tscale}]}, {i, l}, {j, w}]}]

enter image description here

Changing Opacity with height

For terrain

Graphics3D[{
{l, w} = Dimensions[terrain];
tscale = 1/3;(*scale terrain height for better look*)
tmin = Min[terrain]; tmax = Max[terrain];
Table[{fx = (terrain[[i, j]] - tmin)/(tmax - tmin); 
ColorData["DarkRainbow"][fx], Opacity[1 - fx + 0.1], 
EdgeForm[None], 
Cuboid[{i, j, base}, {i + 1, j + 1, 
  base + terrain[[i, j]] tscale}]}, {i, l}, {j, w}]}]

enter image description here

$\endgroup$
  • $\begingroup$ I know Cuboid will do the job, but can we modify BarChart3D to do it? $\endgroup$ – Wjx Jun 8 '16 at 14:33
  • $\begingroup$ I don't think there is a straightforward way to shift the base of BarChart3D. Is there any special property of BarChart3D you are interested in to use? $\endgroup$ – Sumit Jun 8 '16 at 15:37
  • $\begingroup$ I like the update, but I up-voted already... $\endgroup$ – Anton Antonov Jun 8 '16 at 15:58
  • $\begingroup$ Yes, I would love to use "Stacked" and "Grid" in ChartLayout. $\endgroup$ – Wjx Jun 9 '16 at 1:50
1
$\begingroup$

Here are alternatives (which OP did not ask for). But OP requests graphs with which

[...] we can get the information of terrain as well as water elegantly in one chart with one glimpse.

  • The mosaic plot shows the conditional probabilities of the terrain-vs-water values, and that is easy to grasp information. Easier, actually, than looking into stacked 3D bar plots.

  • The Chernoff faces, are mostly for fun, but they do show outliers in the data, which, are not easy to see in the plots of the rest of the answers, or the plot in OP's question.

Mosaic plot

This is over flattened data, has the advantage of showing the conditional probabilities of the terrain-water values:

Import["https://github.com/antononcube/MathematicaForPrediction/blob/master/MosaicPlot.m"]

MosaicPlot[Transpose[{Flatten@terrain, Flatten@water}], 
 "ColumnNames" -> {"terrain", "water"}, 
 ColorRules -> {1 -> Reverse[ColorData[12, "ColorList"]]}, ImageSize -> Large]

enter image description here

Note that the coloring corresponds to terrain values.

Chernoff faces

The data has to be (column) normalized for this plot. The face length depends on the terrain. The vertical position of the eyes and eye size depend on the water. The face color depends on the normalized terrain-water pairs. The grid has the same structure as the original 2D arrays for terrain and water.

Import["https://raw.githubusercontent.com/antononcube/\
MathematicaForPrediction/master/ChernoffFaces.m"]

data = N@Transpose[{Flatten@terrain, Flatten@water}];
Grid[Partition[
  MapThread[
   ChernoffFace[<|"FaceLength" -> #[[1]], 
      "EyesVerticalPosition" -> #[[2]], "EyeSize" -> #[[2]], 
      "FaceColor" -> 
       Blend[{Lighter@Brown, Lighter@Blue}, #2[[2]]/Total[#2]]|>, 
     ImageSize -> 60] &, {VariablesRescale@data, data}],
  Length@First@terrain], Dividers -> All]

enter image description here

$\endgroup$
  • $\begingroup$ what happened? It seems that it's too far away from my need. Quite interesting though.~ $\endgroup$ – Wjx Jun 8 '16 at 14:50
  • $\begingroup$ I know :) I am preparing a follow-up of some posts I made about Chernoff faces so I could not resist. And yes, I do think there is some additional information presented by those plots not easily seen from bar charts. $\endgroup$ – Anton Antonov Jun 8 '16 at 14:59
  • $\begingroup$ And to whoever down voted: (1) Did you see the first paragraph of my post? (2) Did you see OP's comment that he finds the graphs in my answer interesting? (2) Do you realize that the graphics in my answer do provide additional information about the data in the spirit of OP's question, and (4) that information is easy to grasp. $\endgroup$ – Anton Antonov Jun 8 '16 at 15:25
  • $\begingroup$ Anton, even reading your comment I fail to see the relevance of your post, especially with a wall of faces occupying a lot of space. Could you make it more obvious for the simple minded? Btw, the downvote is not mine. $\endgroup$ – István Zachar Jun 8 '16 at 15:31
  • $\begingroup$ @IstvánZachar The mosaic plot shows the conditional probabilities of the terrain-vs-water values, and think that is easy to grasp information. Easier, actually, than looking into stacked 3D bar plots. As for the Chernoff faces, they are mostly for fun, but they do show outliers in the data, which, for example, is not easy to see in your and Sumit's solutions, or the plot in OP's question. $\endgroup$ – Anton Antonov Jun 8 '16 at 15:42
0
$\begingroup$
Graphics3D[{Opacity[1], {l, w} = Dimensions[water];
  MapIndexed[{Hue[#/10], 
     Cuboid[{#2[[1]], #2[[2]], 0}, {#2[[1]] + 1, #2[[2]] + 1, #}]} &, 
   terrain, {2}],
  Opacity[0.2], Blue, 
  MapIndexed[
   If[#[[2]] != 0, 
     Cuboid[{#2[[1]], #2[[2]], #[[1]]}, {#2[[1]] + 1, #2[[2]] + 
        1, #[[1]] + #[[2]]}], Nothing] &, 
   Thread /@ Thread[{terrain, water}], {2}]}, 
 BoxRatios -> {1, 1, .3}]

This code will generate a proper result.(Despite its color is not in rainbow form) But I would still love to have an answer using BarChart3D.

$\endgroup$

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.