5
$\begingroup$

This is a follow-up to the question I have left earlier today. Now, thanks to @kglr I made some progress on my mini project. This said, there are still a few things that I want to do with it, and I can't seem to find a solution myself, here on stackexchange.

Here is the updated code...

(* work-in-progress *)
data3 = Outer[{#1, #2, #1*#2} &, Subdivide[.02, 1, 4], 
   Subdivide[10000, 10^7, 4]] // Flatten[#, 1] &
grid = Reverse @ {
    {Green, Green, Green, Yellow, Yellow},
    {Green, Green, Yellow, Yellow, Orange},
    {Green, Yellow, Yellow, Orange, Orange},
    {Yellow, Yellow, Orange, Orange, Red},
    {Yellow, Orange, Orange, Red, Red}
    };

Overlay[{
  ArrayPlot[
   grid
   , Mesh -> All
   ],
  ListContourPlot[
   data3
   , Frame -> True
   , Contours -> {300000, 600000, 900000, 1600000, 2200000}
   , ContourStyle -> {{Thick, Dashed, Black}}
   , ContourLabels -> (Text[Framed[#3], {#1, #2}, 
       Background -> White] &)
   , ContourShading -> None
   , ScalingFunctions -> {None, "Log10"}
   ]
  }]

It produces the following plot...

enter image description here

Now, I would like to overlay the ListContourPlot over the ArrayPlot. However, it seems like I am getting some misalignment which I can't seem to fix. I would also like to move the ContourLabels at the right side of the frame near their designated contours. I have tried a few things but also failed to achieve the desired effect. I would appreciate your help, any help would be great!

EDIT 0x

Following is the desired label placement

enter image description here

$\endgroup$
4
$\begingroup$

It is quite difficult to get the alignments right using Overlay. You can use one of the following approaches instead:

Inset

You can use a combination of Prolog and Inset:

ap = ArrayPlot[grid, Mesh -> All];

ListContourPlot[data3, Frame -> True, ImageSize -> Large, 
 Contours -> {300000, 600000, 900000, 1600000, 2200000}, 
 PlotRange -> {{0, 1}, All},
 ContourStyle -> {{Thick, Dashed, Black}}, 
 ContourLabels -> (Text[Framed[#3], {#1, #2}, Background -> White] &),
 ContourShading -> None, ScalingFunctions -> {None, "Log10"}, 
 Prolog -> Inset[ap, Scaled[{.5, .5}], Scaled[{.5, .5}], Scaled[1]]]

enter image description here

RescalingTransform

An alternative approach: Rescale the graphics primitives of ap using the PlotRanges of ap and lcp:

lcp = ListContourPlot[data3, Frame -> True, ImageSize -> Large, 
   Contours -> {300000, 600000, 900000, 1600000, 2200000}, 
   ContourStyle -> {{Thick, Dashed, Black}}, 
   PlotRange -> {{0, 1}, All},
   ContourLabels -> (Text[Framed[#3], {#1, #2}, 
       Background -> White] &), ContourShading -> None, 
   ScalingFunctions -> {None, "Log10"}];

rT = RescalingTransform[PlotRange[ap], PlotRange[lcp]];

rescaledap = MapAt[GeometricTransformation[#, rT] &, ap, {1}];

Show[lcp, Prolog -> First @ rescaledap]

![enter image description here

Raster

A much simpler way is to use Raster and use GridLines instead of Mesh:

Show[lcp, Prolog -> 
  Raster[Reverse@grid /. RGBColor -> List, Transpose@PlotRange[lcp]], 
 GridLines -> (Subdivide[##, 5] & @@@ PlotRange[lcp])]

enter image description here

Modifying contour labels

Using a slight modification of the function modifyLabels from this answer we can place the contour labels nest to the right frame:

Needs["GraphUtilities`"]

ClearAll[modifyLabels]
modifyLabels[styles__: 14] := Quiet[Normal[#] /. {Text[__] :> Sequence[], 
     t : Tooltip[a_, b_] :> 
       {t, Text[Framed[Style[b, {a[[-2]], styles}], Background -> White], 
        LineScaledCoordinate[SortBy[First]@a[[-1, 1]], 1], Right]}}] &

modifyLabels[] @ ListContourPlot[data3, Frame -> True, ImageSize -> Large, 
  Contours -> {300000, 600000, 900000, 1600000, 2200000}, 
  PlotRange -> {{0,1}, All},
  ContourStyle -> {{Thick, Dashed, Black}}, ContourLabels -> All, 
  ContourShading -> None, ScalingFunctions -> {None, "Log10"}, 
  Prolog -> Inset[ap, Scaled[{.5, .5}], Scaled[{.5, .5}], Scaled[1]]]

enter image description here

With further tweaking:

ClearAll[modifyLabels2]
modifyLabels2[styles__: 14] := 
 Quiet[Normal[#] /. {Text[__] :> Sequence[], 
     t : Tooltip[a_, b_] :> {t, 
       Text[Pane[Framed[Style[b, {a[[-2]], styles}], Background -> White], 
         ImageMargins -> {{12, 0}, {0, 0}}], 
        LineScaledCoordinate[SortBy[First]@a[[-1, 1]], 1], Left]}}] &

modifyLabels2[]@
 ListContourPlot[data3, Frame -> True, ImageSize -> Large, 
  Contours -> {300000, 600000, 900000, 1600000, 2200000}, 
  ContourStyle -> {{Thick, Dashed, Black}}, ContourLabels -> All, 
  ContourShading -> None, ScalingFunctions -> {None, "Log10"}, 
  Prolog -> Inset[ap, Scaled[{.5, .5}], Scaled[{.5, .5}], Scaled[1]], 
  PlotRangeClipping -> False, PlotRange -> {{0, 1}, All},
  ImagePadding -> {{Automatic, 90}, {Automatic, Automatic}}]

enter image description here

| improve this answer | |
$\endgroup$
  • $\begingroup$ Is there a reason why Overlay is so picky? :D I spent an hour tinkering with it. So, I am impressied with the above. Also, any ideas how I could align the CountourLabels at the right side of the Frame? $\endgroup$ – e.doroskevic Jun 21 at 21:49
  • $\begingroup$ @e.doroskevic, maybe the price we pay for being able to overlay arbitrary objects:) By the raight side og the Frame, do you mean the label frames or the plot frame (i.e. place labels at the right end of the associated contour line)? $\endgroup$ – kglr Jun 21 at 22:18
  • $\begingroup$ It does make sense. I mean, I have learned a lot from today alone, and it has been a pleasent experience getting back to Mathematica. I do appreciate all the help. I have dropped a screenshot with a reference to the label placement. I should have formulated it better :D $\endgroup$ – e.doroskevic Jun 21 at 22:33
  • $\begingroup$ @e.doroskevic, please see the update re placement of labels. $\endgroup$ – kglr Jun 21 at 22:38
  • $\begingroup$ This is exactly what I wanted, thank you! :D I spent the whole day on this thing $\endgroup$ – e.doroskevic Jun 21 at 22:40
5
$\begingroup$

I thought it would be interesting to see how one could use DataRange to align the ArrayPlot with the contour plot using Show.

It could have been straight forward. Let lcp be the contour plot, then:

{xrange, yrange} = PlotRange /. Options[lcp];

ap = ArrayPlot[
      grid
      , Mesh -> All
      , DataRange -> {xrange, yrange}
      ];
Show[lcp, ap, lcp]

Output

Alas, the squares are centered on their values, so half of the outer squares are outside of the view. (Actually a bit less than half, considering the plot range padding.)

{xrange, yrange}

{{0.02, 1.}, {4., 7.}}

The DataRange option is currently saying that we should place, on the x-axis, the middle of the leftmost square at 0.02 and the middle of the rightmost square at 1, and correspondingly for the y-axis.

We can fix this by adjusting the data range. Currently, we have a total of four squares in view, covering $1 - 0.02 = 0.98$ units of length along the x-axis.

We need to offset the middle of the square along the x-axis precisely the amount it takes to move the entire square into the view. We can set up an equation to figure out what the amount shoud be:

$$ (1 - x) - (0.02 +x) = \frac{4}{5} (1 - 0.02)\implies x = 0.098 $$

and similarly for the y-axis:

$$ (7 - x) - (4 + x) = \frac{4}{5} (7 - 4)\implies x = 0.3 $$

Putting these offsets into the code, we get

ap = ArrayPlot[
      grid
      , Mesh -> All
      , DataRange -> {xrange + {0.098, -0.098}, yrange + {0.3, -0.3}}
      ];
Show[lcp, ap, lcp]

Output

| improve this answer | |
$\endgroup$
  • $\begingroup$ I like it! It's actually a clever way to use DataRange :O I have learned something new! $\endgroup$ – e.doroskevic Jun 21 at 22:42

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.