BarLegend: FrameLabel ruins alignment of BarLegend

$Version (* Wolfram Cloud FrontEnd *)

12.0.0 for Linux x86 (64-bit) (April 7, 2019)

We can add the frame labels using Labeled instead of using FrameLabel

ClearAll[addLabels]
addLabels[plot_, labels_, pos_, legpos_ : Right]:= Labeled[First @ plot, 
 Append[labels, plot[[2, 1]]], Append[pos, legpos]]

Examples:

dp1 = DensityPlot[Sin[2 x y], {x, -2, 2}, {y, -2, 2}, 
   ImageSize -> 320,  PlotLegends -> Placed[BarLegend[Automatic], After]];

Row[{dp1, addLabels[dp1, {"x", "y"}, {Bottom , Left}]},  Spacer[20]]

enter image description here

Row[{dp1, addLabels[dp1, Style[#, 32] & /@ {"x", "y", "z"}, {Bottom , Left, Top}]}, 
 Spacer[20]]

enter image description here

cp = ContourPlot[Sin[2 x y], {x, -2, 2}, {y, -2, 2}, ImageSize -> 320,  
  PlotLegends -> Placed[BarLegend[Automatic, LegendLayout -> "Row"], Top]];

Row[{cp, addLabels[cp, Style[#, 32] & /@ {"x", "y", "z"}, 
 {Bottom , Left, Right}, Top]}, Spacer[20]] 

enter image description here


The output of the DensityPlot call is a Legended object, that basically creates a Grid object that lines up the graphic and the legend. So, to get the size of the legend and the alignment of the legend to match the graphic, we first need to figure out how to do this when using Grid. Consider the following two graphics objects:

g = Graphics[{Circle[{1,1}]}, Frame->True, FrameLabel->{{None, None}, {"x", None}}];
b = Graphics[{Rectangle[{0,-100}, {10,100}]}, ImageSize->{Automatic, 360}];

which basically have the same shapes as the density plot and the bar legend. Let's see what happens when we put them side by side using Grid:

Grid[{{g, b}}]

enter image description here

Not very good. To get the sizes to match, I will use the syntax: ImageSize -> Automatic -> size instead of ImageSize -> size:

Grid[{{Show[g, ImageSize->Automatic->360], Show[b, ImageSize->Automatic->{Automatic, 360}]}}]

enter image description here

Much better. However, the alignment is not quite correct. The only way I know to align two graphics objects in a Grid when ImagePadding is involved, is to use BaselinePosition->Axis:

Grid[{{
    Show[g, ImageSize->Automatic->360, BaselinePosition->Axis],
    Show[b, ImageSize->Automatic->{Automatic, 360}, BaselinePosition->Axis]
}}]

enter image description here

One final issue is that the AxesOrigin of the two objects are not in the same scaled position. I think the BarLegend always uses a raster with the center at {0,0}. So, we need to make sure that the graphic axis is also at the center:

Grid[{{
    Show[g, ImageSize->Automatic->360, BaselinePosition->Axis, AxesOrigin->{0, 1}],
    Show[b, ImageSize->Automatic->{Automatic, 360}, BaselinePosition->Axis]
}}]

enter image description here

This is basically the way to size and align two graphics objects in a Grid.

Now, unfortunately, Legended does two things that ruin the above alignment procedure:

TracePrint[
    ToBoxes @ DensityPlot[
        Sin[2 x y],
        {x,-2,2},
        {y,-2,2},
        PlotLegends->Placed[BarLegend[Automatic],After],
        FrameLabel->{{None,None},{"x",None}}
    ],
    ToBoxes[_Grid,_] | Pane[_Graphics, __Rule],
    TraceInternal->True,
    TraceAction->Print@*OutputForm@*ReplaceAll[Grid->Inactive[Grid]]
];
Pane[-Graphics-, Alignment -> Left, AppearanceElements -> None, ImageMargins -> {{5, 5}, {5, 5}}, ImageSizeAction -> ResizeToFit]

Pane[-Graphics-, Alignment -> Left, AppearanceElements -> None, ImageMargins -> {{5, 5}, {5, 5}}, ImageSizeAction -> ResizeToFit]

ToBoxes[Inactive[Grid][Map[RawBoxes, BoxForm`grid$716644, {2}], Alignment -> {Center, Center}, WrappersDump`removeIfDefault[BaselinePosition -> WrappersDump`baselineposition$716644, Automatic], WrappersDump`removeIfDefault[Spacings -> OptionValue[Labeled, {}, Spacings], Automatic], StripOnInput -> True], StandardForm]

ToBoxes[Inactive[Grid][{{RawBoxes[TagBox[ItemBox[PaneBox[TagBox[#1, SkipImageSizeLevel], Alignment -> {Center, Baseline}, BaselinePosition -> Baseline], DefaultBaseStyle -> Labeled], SkipImageSizeLevel]], RawBoxes[ItemBox[#2, DefaultBaseStyle -> LabeledLabel]]}}, Alignment -> {Center, Center}, BaselinePosition -> {1, 1}, StripOnInput -> True], StandardForm]

Both this Pane wrapper and the Alignment -> {Center, Center} option in the Grid prevent the above alignment procedure from working. For example, using Alignment -> {Center, Center}:

Grid[
    {{
        Show[g, ImageSize->Automatic->360, BaselinePosition->Axis, AxesOrigin->{0, 1}],
        Show[b, ImageSize->Automatic->{Automatic, 360}, BaselinePosition->Axis]
    }},
    Alignment->{Center, Center}
]

enter image description here

and similarly for the Pane option.

So, to fix your issue, we need to do the following:

  1. Use the ImageSize -> Automatic -> size option setting.
  2. Use BaselinePosition -> Axis.
  3. Avoid using Alignment -> {Center, Center} when constructing the Grid.
  4. Avoid wrapping the graphic in Pane.
  5. Make sure that the AxesOrigin of the graphic is in the center of the graphic.

Here is one approach that does this. First define a LegendFunction that fixes the BarLegend Graphics options and removes the Pane wrapper:

fixLegend[size_][legend_] := ReplaceAll[
    legend,
    {
        g_Graphics :> Show[g, ImageSize->Automatic->{Automatic, size}, BaselinePosition->Axis],
        Pane -> Function@#
    }
]

Then, define a wrapper function that removes the unwanted Grid alignment during box generation:

MakeBoxes[stripGridAlignemnt[expr_], form_] ^:= ReplaceAll[
    MakeBoxes[expr, form],
    Rule[GridBoxAlignment, _] -> Sequence[]
]

Now, we are ready to create the desired output:

stripGridAlignemnt @ DensityPlot[
    Sin[2 x y],
    {x,-2,2},
    {y, 0, 4},
    BaselinePosition->Axis,
    AxesOrigin -> {0, 2},
    ImageSize->Automatic->360,
    PlotLegends->Placed[BarLegend[Automatic, LegendFunction -> fixLegend[360]], After],
    FrameLabel->{{None,None},{"x",None}}
]

enter image description here


Seems that I am the few people who can reproduce this "bug". (MMA 12.0 on Windows 10)

I suggest you manually control the height of the image and the size of barlegend. Here is what I used:

DensityPlot[Sin[2 x y], {x, -2, 2}, {y, -2, 2}, AspectRatio -> 1, 
 PlotLegends -> 
  Placed[BarLegend[Automatic, LegendMarkerSize -> 270], After], 
 FrameLabel -> {"x", None, "x", None}, ImageSize -> {Automatic, 300}]

Output:enter image description here

Adding y axis label:

DensityPlot[Sin[2 x y], {x, -2, 2}, {y, -2, 2}, AspectRatio -> 1, 
 PlotLegends -> 
  Placed[BarLegend[Automatic, LegendMarkerSize -> 270], After], 
 FrameLabel -> {"x", "y", "x", None}, ImageSize -> {Automatic, 300}]

enter image description here

It is not perfect in alignment, but at least, it will not shift uncontrollably, so that you can manually adjust the alignment.

Again, for publication-quality production of figures, I suggest trying SciDraw. This might be helpful to your question, as you can treat the bar legend as a side by side nested frame.