19
$\begingroup$

Bug introduced in 10.1 or earlier and persisting through 11.0.1 or later


This answer explains how to change various undocumented options for BarLegend with Method. In particular, I want to change the style of the ticks (marks and labels) in BarLegend. For example,

BarLegend[{"SunsetColors", {0, 1}}, LabelStyle -> {FontSize -> 12},
 Method -> {Frame -> False, TicksStyle -> Directive[Red, AbsoluteThickness[2]]}]

However, in Mathematica 10.2 on Linux, BarLegend refuses to change the thickness of the tick marks. Also, if I do not add the LabelStyle option then the labels don't change color? In Plot this command works,

Plot[Sin[x], {x, 0, 2 Pi},TicksStyle -> Directive[Red, AbsoluteThickness[2]]]

so I would expect that it works for BarLegend as well.

What is going on and is there a workaround to change the thickness of the tick marks? Thanks.

$\endgroup$
  • 2
    $\begingroup$ It seems appropriate to tag this as a bug. $\endgroup$ – J. M. is away Jul 30 '15 at 3:44
  • $\begingroup$ same issue in version 9.0.1.0. $\endgroup$ – kglr May 19 '16 at 10:58
13
$\begingroup$

Somehow the AbsolutThickness you specified gets replaced by a default value of AbsoluteThickness[0.2].

This misbehavior can be corrected by replacing the incorrect value with your specification.

PlotLegends; (*preload definitions*)

Cell[BoxData[
   MakeBoxes@
      BarLegend[{"SunsetColors", {0, 1}}, LabelStyle -> {FontSize -> 12}, 
       Method -> {Frame -> False, TicksStyle -> Directive[Red, AbsoluteThickness[2]]}] /. 
     Directive[RGBColor[1, 0, 0], AbsoluteThickness[_]] -> 
      Directive[RGBColor[1, 0, 0], AbsoluteThickness[2]] // #[[1, 1]] &
   ], "Output"] // CellPrint

AbsoluteThickness[2]

For opaque ticks:

Cell[BoxData[
   MakeBoxes@
      BarLegend[{"SunsetColors", {0, 1}}, LabelStyle -> {FontSize -> 12}, 
       Method -> {Frame -> False, TicksStyle -> Directive[Red, AbsoluteThickness[2]]}] /. 
     Directive[RGBColor[1, 0, 0], AbsoluteThickness[_]] -> 
      Directive[RGBColor[1, 0, 0], AbsoluteThickness[2], Opacity[1]] // #[[1, 1]] &
   ], "Output"] // CellPrint

opaque


Correcting the BarLegend of a DensityPlot, using the syntax provided in the answer by Praan :

DensityPlot[Sin[x y], {x, 0, 1}, {y, 0, 1}, 
  PlotLegends -> 
   BarLegend[Automatic, LabelStyle -> {FontSize -> 12}, 
    Method -> {Frame -> False, TicksStyle -> Directive[Red, AbsoluteThickness[2]]}]] /. 
 Placed[barLegend_BarLegend, args__] :> 
  Placed[ToExpression[
    FrameBox @@ MakeBoxes[barLegend] /. 
      Directive[Red, AbsoluteThickness[_]] -> 
       Directive[Red, AbsoluteThickness[2], Opacity[1]]], args]

DensityPlot

The same output can be achieved by using the following LegendFunction

DensityPlot[Sin[x y], {x, 0, 1}, {y, 0, 1}, 
 PlotLegends -> 
  BarLegend[Automatic, LabelStyle -> {FontSize -> 12}, 
   Method -> {Frame -> False, 
     TicksStyle -> Directive[Red, AbsoluteThickness[2]]}, 
   LegendFunction -> (# /. 
       Directive[Red, AbsoluteThickness[_]] -> 
        Directive[Red, AbsoluteThickness[2], Opacity[1]] &)]]

With the answer by Praan and our discussion in the comments it became clear, that a wrong InterpretationFunction inside the TemplateBox created by BarLegend can cause additional problems.
Compare

MakeBoxes[
    BarLegend[{"SunsetColors", {0, 1}}, LegendMarkerSize -> 300, 
     LabelStyle -> {FontSize -> 12}, 
     Method -> {FrameStyle -> Black, AxesStyle -> None, 
       TicksStyle -> Black}]] /. 
   AbsoluteThickness[_] -> 
    AbsoluteThickness[2] /. (InterpretationFunction :> 
     f_) -> (InterpretationFunction :> (# &)) // ToExpression

corr

with

MakeBoxes[
   BarLegend[{"SunsetColors", {0, 1}}, LegendMarkerSize -> 300, 
    LabelStyle -> {FontSize -> 12}, 
    Method -> {FrameStyle -> Black, AxesStyle -> None, 
      TicksStyle -> Black}]] /. 
  AbsoluteThickness[_] -> AbsoluteThickness[2] // ToExpression

false

or just the InterpretationFunction

MakeBoxes[
    BarLegend[{"SunsetColors", {0, 1}}, LegendMarkerSize -> 300, 
     LabelStyle -> {FontSize -> 12}, 
     Method -> {FrameStyle -> Black, AxesStyle -> None, 
       TicksStyle -> Directive[Black, AbsoluteThickness[2]]}]] /. 
   AbsoluteThickness[_] -> 
    AbsoluteThickness[2] // #[[-1, 2, 1]] & // ToExpression

and the first code block in the answer by Praan.

$\endgroup$
  • $\begingroup$ Thanks. If I would like to use the BarLegend in a plot, how do I incorporate your solution with PlotLegends? Can the output be converted to a Graphics expression? $\endgroup$ – Praan Jul 30 '15 at 10:01
  • $\begingroup$ Never mind, I managed to solve it somehow. See my answer below. $\endgroup$ – Praan Jul 30 '15 at 13:04
9
$\begingroup$

Update: You can also use the forms given in Options[Charting`iBarLegend] listed below, i.e., we can use Charting`TicksStyle for "TicksStyle", Charting`Frame for "Frame", etc.

BarLegend[{"SunsetColors", {0, 1}}, LabelStyle -> {FontSize -> 12}, 
 Charting`TicksStyle -> Directive[Opacity[1], Red, FontColor -> Black, AbsoluteThickness[5]],
 Charting`Frame -> False,
 Charting`TickLengths -> {20, 10}]

enter image description here

Note: This works for versions 9 thru 11 (Windows 10).

Original answer:

Use the (undocumented, afaik) options "TicksStyle" and "FrameStyle":

BarLegend[{"SunsetColors", {0, 1}}, LabelStyle -> {FontSize -> 12}, 
 "TicksStyle" -> Directive[Red, AbsoluteThickness[5]], 
 "FrameStyle" -> Opacity[0]]

Mathematica graphics

In version 10, we need to add Opacity[1] in Directive[...]:

BarLegend[{"SunsetColors", {0, 1}}, LabelStyle -> {FontSize -> 12}, 
 "TicksStyle" -> Directive[Opacity[1], Red, FontColor->Black, AbsoluteThickness[5]], 
 "Frame" -> False]

Mathematica graphics

A few more such options:

BarLegend[{"SunsetColors", {0, 1}}, 
 "LabelStyle" -> Directive[Green, Bold, FontSize -> Scaled[.4]],
  "Ticks" -> Range[0, 1, .1],
 "TicksStyle" -> Directive[Red, AbsoluteThickness[5]], 
 "FrameStyle" -> Directive[Dashed, Green], "TickSide" -> Left, 
 "LabelSide" -> Right, "Frame" -> True, "ImageSize" -> {100, 300}, 
 LegendMarkerSize -> {100, 500}]

Mathematica graphics

Update: Full list of options

Options[Charting`iBarLegend]

Version 9:

Mathematica graphics

Version 10:

Mathematica graphics

$\endgroup$
  • $\begingroup$ Can one of these options also solve this issue? $\endgroup$ – Karsten 7. May 19 '16 at 11:18
  • $\begingroup$ @Karsten7., good question. It seems that Verbeia already tried some of the options. I haven't tried it, but maybe one can change the threshold number 11 which is set in the function Legending`LegendDump`iColorBandLegend as Legending`LegendDump`cthreshold = 11 $\endgroup$ – kglr May 19 '16 at 12:29
4
$\begingroup$

Analysis

(Observations in 10.1.0 under Windows.)

Curiously it seems that AbsoluteThickness[0.2`] is hard-coded within the internal definitions. Formatting of BarLegend calls Charting`iBarLegend which calls Legending`LegendDump`iColorGradientLegend or Legending`LegendDump`iColorBandLegend. In the definition of iColorGradientLegend we find:

ticksstyle = Switch[ticksstyle,
  Automatic, {Directive[AbsoluteThickness[0.2`], GrayLevel[0.4`]], 
   Directive[AbsoluteThickness[0.2`], GrayLevel[0.4`], Opacity[0.3`]]},
  _?Charting`generalDirective | {_?
      Charting`generalDirective ..} | {}, (Charting`ConstructDirective[#1, 
      AbsoluteThickness[0.2`]] &) /@ Charting`padList[ticksstyle, 2],
  None | False, None,
  _, (Directive[AbsoluteThickness[0.2`], #1] &) /@ {GrayLevel[0.4`], 
    Directive[GrayLevel[0.4`], Opacity[0.5`]]}]

If the option value passed to this inner function is a "generalDirective" we specifically get:

(Charting`ConstructDirective[#1, 
  AbsoluteThickness[0.2`]] &) /@ Charting`padList[ticksstyle, 2]

So the program is appending AbsoluteThickness[0.2`] to anything we specify by way of Charting`ConstructDirective. This seems at best a poor design in my opinion.

Work-around

Seeing the cause of the problem inspires an unusual work-around; here is a chomp head that consumes the argument to its right and then vanishes:

chomp /: h_[a___, chomp[x__], b_, c___] := h[a, x, c]

Now when used on our AbsoluteThickness spec making sure it is the last:

BarLegend[{"SunsetColors", {0, 1}}, LabelStyle -> {FontSize -> 12}, 
 Method -> {Frame -> False, 
   TicksStyle -> Directive[Red, Opacity[1], chomp @ AbsoluteThickness[4]]}]

enter image description here

One could also enter Directive[chomp[ Red, Opacity[1], AbsoluteThickness[4] ] ].

$\endgroup$
3
$\begingroup$

I went back to the old legend functions that I used before BarLegend existed, and tried the following:

  • First, copy the definitions of trimPoint, colorLegend, display and at by selecting the large code block in the section Color bar legend.

Then do this:

display[{
  colorLegend[
    ColorData["SunsetColors"],
    {-.5, .5},
    LabelStyle -> 
     Directive[Yellow, FontSize -> 12, FontWeight -> Bold],
    "ColorBarFrameStyle" -> Directive[AbsoluteThickness[2], Cyan],
    Background -> Darker@Darker@Darker@Blue,
    "ColorSwathes" -> None,
    Contours -> 10,
    RoundingRadius -> 15,
    BoxFrame -> 3,
    "Digits" -> 2] // at[{0, 0}, {1, 1}]},
 ImageSize -> 80,
 AspectRatio -> 4]

bar legend

The thickness information can be put either into the option "ColorBarFrameStyle" or LabelStyle. To show how these options differ, I highlighted their targets in different colors (Yellow for LabelStyle and Cyan for the "ColorBarFrameStyle".

So this older legend functionality has the ability to customize thicknesses as you were asking. It doesn't require BarLegends, which makes it useable in Mathematica version 8, too. The workings of the positioning commands display and at are explained with more examples in the linked answer. Normally, you would use display to combine the legend bar with a figure. Then the AspectRatio and ImageSize options will have to be adjusted to fit both objects.

$\endgroup$
3
$\begingroup$

In the answer of Karsten 7. the BarLegend is shown as a Cell expression, and since I want to use the legend in an actual plot, it is not immediately useful. However, with the help of his/her answer, I managed to solve my problem.

First I make the legend:

barLegend = ToExpression[FrameBox@@MakeBoxes[
  BarLegend[{"SunsetColors", {0, 1}}, LegendMarkerSize -> 300,
    LabelStyle -> {FontSize -> 12},
    Method -> {FrameStyle -> Black, AxesStyle -> None, TicksStyle -> Black}]] /. 
AbsoluteThickness[_] -> AbsoluteThickness[2]];

I don't know how changing the head to FrameBox works, but without this, the thickness of the tick marks reverts back to the default value AbsoluteThickness[0.2] during ToExpression.

This legend can then be used in a plotting environment, for example DensityPlot:

DensityPlot[Sin[x+y^2],{x, 0, 3},{y, 0, 3},
  PlotLegends -> barLegend, LabelStyle -> {FontSize -> 12},
  ColorFunction -> ColorData["SunsetColors"], ImageSize -> 300]

Edit

See the update of Karsten 7's answer for a nice implementation; doing the replacement after the plot command has been evaluated.

$\endgroup$
  • $\begingroup$ Your ToExpression FrameBox@@MakeBoxes combination makes this think much more convenient. With FrameBox@@ you are replacing TemplateBox with FrameBox. $\endgroup$ – Karsten 7. Jul 30 '15 at 13:52
  • $\begingroup$ With a fresh kernel the first code block will produce an error message. One has to evaluate PlotLegends first to load some definitions. $\endgroup$ – Karsten 7. Jul 30 '15 at 14:27
  • $\begingroup$ @Karsten 7. Thanks! I know about the infix notation @@ of Apply. I just don't know why Framebox keeps the new thickness value while TemplateBox reverts it back to the default value. $\endgroup$ – Praan Jul 30 '15 at 14:37
  • 1
    $\begingroup$ By replacing TemplateBox with FrameBox you basically inactivate the InterpretationFunction and the other stuff part of the TemplateBox. $\endgroup$ – Karsten 7. Jul 30 '15 at 15:26

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.