2626 from matplotlib .text import Text
2727
2828 from plotnine import theme
29+ from plotnine .guides import guides
2930 from plotnine .scales .scale import scale
3031 from plotnine .typing import Side
3132
@@ -48,7 +49,12 @@ class guide_colorbar(guide):
4849 """
4950
5051 display : Literal ["gradient" , "rectangles" , "raster" ] = "gradient"
51- """How to render the colorbar."""
52+ """
53+ How to render the colorbar
54+
55+ SVG figures will always use "rectangles" to create gradients. This has
56+ better support across applications that render svg images.
57+ """
5258
5359 alpha : Optional [float ] = None
5460 """
@@ -74,6 +80,12 @@ def __post_init__(self):
7480 if self .nbin is None :
7581 self .nbin = 300 # if self.display == "gradient" else 300
7682
83+ def setup (self , guides : guides ):
84+ super ().setup (guides )
85+ # See: add_segmented_colorbar
86+ if guides .plot ._build_objs .meta ["figure_format" ] == "svg" :
87+ self .display = "rectangles"
88+
7789 def train (self , scale : scale , aesthetic = None ):
7890 self .nbin = cast ("int" , self .nbin )
7991 self .title = cast ("str" , self .title )
@@ -171,6 +183,7 @@ def draw(self):
171183 nbars = len (self .bar )
172184 elements = self .elements
173185 raster = self .display == "raster"
186+ alpha = self .alpha
174187
175188 colors = self .bar ["color" ].tolist ()
176189 labels = self .key ["label" ].tolist ()
@@ -219,9 +232,9 @@ def draw(self):
219232
220233 # colorbar
221234 if self .display == "rectangles" :
222- add_segmented_colorbar (auxbox , colors , elements )
235+ add_segmented_colorbar (auxbox , colors , alpha , elements )
223236 else :
224- add_gradient_colorbar (auxbox , colors , elements , raster )
237+ add_gradient_colorbar (auxbox , colors , alpha , elements , raster )
225238
226239 # ticks
227240 visible = slice (
@@ -264,6 +277,7 @@ def draw(self):
264277def add_gradient_colorbar (
265278 auxbox : AuxTransformBox ,
266279 colors : Sequence [str ],
280+ alpha : float | None ,
267281 elements : GuideElementsColorbar ,
268282 raster : bool = False ,
269283):
@@ -319,6 +333,7 @@ def add_gradient_colorbar(
319333 shading = "gouraud" ,
320334 cmap = cmap ,
321335 array = Z .ravel (),
336+ alpha = alpha ,
322337 rasterized = raster ,
323338 )
324339 auxbox .add_artist (coll )
@@ -327,6 +342,7 @@ def add_gradient_colorbar(
327342def add_segmented_colorbar (
328343 auxbox : AuxTransformBox ,
329344 colors : Sequence [str ],
345+ alpha : float | None ,
330346 elements : GuideElementsColorbar ,
331347):
332348 """
@@ -335,6 +351,21 @@ def add_segmented_colorbar(
335351 from matplotlib .collections import PolyCollection
336352
337353 nbreak = len (colors )
354+ # Problem:
355+ # 1. Webbrowsers do not properly render SVG with QuadMesh
356+ # colorbars. Also when the QuadMesh is "rasterized",
357+ # the colorbar is misplaced within the SVG (and pdfs!).
358+ # So SVGs cannot use `add_gradient_colobar` at all.
359+ # 2. Webbrowsers do not properly render SVG with PolyCollection
360+ # colorbars when the adjacent rectangles that make up the
361+ # colorbar touch each other precisely. The "bars" appear to
362+ # be separated by lines.
363+ #
364+ # For a wayout, we overlap the bars. Overlapping creates artefacts
365+ # when alpha < 1, but having a gradient + alpha is rare. And, we can
366+ # minimise apparent artefacts by using a large overlap_factor.
367+ # A value of 2 gives the best results in the rare case should alpha < 1.
368+ overlap_factor = 2
338369 if elements .is_vertical :
339370 colorbar_height = elements .key_height
340371 colorbar_width = elements .key_width
@@ -345,6 +376,8 @@ def add_segmented_colorbar(
345376 for i in range (nbreak ):
346377 y1 = i * linewidth
347378 y2 = y1 + linewidth
379+ if i > 1 :
380+ y1 -= linewidth * overlap_factor
348381 verts .append (((x1 , y1 ), (x1 , y2 ), (x2 , y2 ), (x2 , y1 )))
349382 else :
350383 colorbar_width = elements .key_height
@@ -356,12 +389,15 @@ def add_segmented_colorbar(
356389 for i in range (nbreak ):
357390 x1 = i * linewidth
358391 x2 = x1 + linewidth
392+ if i > 1 :
393+ x1 -= linewidth * overlap_factor
359394 verts .append (((x1 , y1 ), (x1 , y2 ), (x2 , y2 ), (x2 , y1 )))
360395
361396 coll = PolyCollection (
362397 verts ,
363398 facecolors = colors ,
364399 linewidth = 0 ,
400+ alpha = alpha ,
365401 antialiased = False ,
366402 )
367403 auxbox .add_artist (coll )
0 commit comments