1212
1313The following parameters are available for customization in the matplotlibrc:
1414 - scalebar.length_fraction
15- - scalebar.height_fraction
15+ - scalebar.thickness
1616 - scalebar.location
1717 - scalebar.pad
1818 - scalebar.border_pad
3939
4040# Standard library modules.
4141import bisect
42- import warnings
4342import dataclasses
43+ import numbers
44+ import warnings
4445
4546# Third party modules.
4647import matplotlib
6162 AnchoredOffsetbox ,
6263)
6364from matplotlib .patches import Rectangle
65+ from matplotlib .transforms import (
66+ Affine2D ,
67+ IdentityTransform ,
68+ blended_transform_factory ,
69+ )
6470
6571# Local modules.
6672from matplotlib_scalebar .dimension import (
@@ -96,10 +102,19 @@ def _validate_legend_loc(loc):
96102 return loc
97103
98104
105+ def _validate_dim (dim ):
106+ if (len (dim ) == 2
107+ and isinstance (dim [0 ], numbers .Real )
108+ and dim [1 ] in ["saxis" , "laxis" , "lw" , "font" , "pt" ]):
109+ return dim
110+ else :
111+ raise ValueError ("Not a valid dimension" )
112+
113+
99114defaultParams .update (
100115 {
101116 "scalebar.length_fraction" : [0.2 , validate_float ],
102- "scalebar.width_fraction " : [0.01 , validate_float ],
117+ "scalebar.thickness " : [( 0.01 , "saxis" ), _validate_dim ],
103118 "scalebar.location" : ["upper right" , _validate_legend_loc ],
104119 "scalebar.pad" : [0.2 , validate_float ],
105120 "scalebar.border_pad" : [0.1 , validate_float ],
@@ -177,6 +192,7 @@ def __init__(
177192 dimension = "si-length" ,
178193 label = None ,
179194 length_fraction = None ,
195+ thickness = None ,
180196 height_fraction = None ,
181197 width_fraction = None ,
182198 location = None ,
@@ -242,9 +258,17 @@ def __init__(
242258 This argument is ignored if a *fixed_value* is specified.
243259 :type length_fraction: :class:`float`
244260
245- :arg width_fraction: width of the scale bar as a fraction of the
246- axes's height (default: rcParams['scalebar.width_fraction'] or ``0.01``)
247- :type width_fraction: :class:`float`
261+ :arg thickness: thickness of the scale bar, as a ``(value, unit)`` pair.
262+ Valid units are
263+ * "laxis": value is relative to the size of the parent axes in the
264+ "long" direction.
265+ * "saxis": value is relative to the size of the parent axes in the
266+ "short" direction.
267+ * "font": value is relative to the label fontsize.
268+ * "lw": value is relative to ``rcParams["lines.linewidth"]``.
269+ * "pt": value is in points.
270+ (default: rcParams['scalebar.thickness'] or ``(0.01, "saxis")``)
271+ :type thickness: ``tuple[float, str]``
248272
249273 :arg location: a location code (same as legend)
250274 (default: rcParams['scalebar.location'] or ``upper right``)
@@ -347,6 +371,12 @@ def __init__(
347371 )
348372 scale_formatter = scale_formatter or label_formatter
349373
374+ if width_fraction is not None :
375+ if thickness is not None :
376+ warnings .warn ("Ignoring 'width_fraction', as 'thickness' is also set" )
377+ else :
378+ thickness = (width_fraction , "saxis" )
379+
350380 if (
351381 loc is not None
352382 and location is not None
@@ -359,7 +389,7 @@ def __init__(
359389 self .units = units
360390 self .label = label
361391 self .length_fraction = length_fraction
362- self .width_fraction = width_fraction
392+ self .thickness = thickness
363393 self .location = location or loc
364394 self .pad = pad
365395 self .border_pad = border_pad
@@ -433,7 +463,7 @@ def _get_value(attr, default):
433463 return value
434464
435465 length_fraction = _get_value ("length_fraction" , 0.2 )
436- width_fraction = _get_value ("width_fraction " , 0.01 )
466+ thickness_value , thickness_unit = _get_value ("thickness " , ( 0.01 , "saxis" ) )
437467 location = _get_value ("location" , "upper right" )
438468 if isinstance (location , str ):
439469 location = self ._LOCATIONS [location .lower ()]
@@ -486,29 +516,53 @@ def _get_value(attr, default):
486516
487517 scale_text = self .scale_formatter (value , self .dimension .to_latex (units ))
488518
489- width_px = abs (ylim [1 ] - ylim [0 ]) * width_fraction
519+ axis_order = slice (None ) if rotation == "horizontal" else slice (None , None , - 1 )
520+ if thickness_unit == "saxis" :
521+ thickness = thickness_value
522+ transform = (ax .get_xaxis_transform ()
523+ if rotation == "horizontal" else
524+ ax .get_yaxis_transform ())
525+ elif thickness_unit == "laxis" :
526+ thickness = thickness_value
527+ flip = Affine2D ([[0 , 1 , 0 ], [1 , 0 , 0 ], [0 , 0 , 1 ]])
528+ transform = (
529+ flip
530+ + blended_transform_factory (
531+ * (ax .transAxes , IdentityTransform ())[axis_order ])
532+ + flip
533+ + blended_transform_factory (
534+ * (ax .transData , IdentityTransform ())[axis_order ])
535+ )
536+ elif thickness_unit in {"font" , "lw" , "pt" }:
537+ thickness = thickness_value * {
538+ "font" : font_properties .get_size (),
539+ "lw" : matplotlib .rcParams ["lines.linewidth" ],
540+ "pt" : 1 ,
541+ }[thickness_unit ] / 72
542+ transform = blended_transform_factory (
543+ * (ax .transData , ax .figure .dpi_scale_trans )[axis_order ])
490544
491545 # Create scale bar
492546 if rotation == "horizontal" :
493547 scale_rect = Rectangle (
494548 (0 , 0 ),
495549 length_px ,
496- width_px ,
550+ thickness ,
497551 fill = True ,
498552 facecolor = color ,
499553 edgecolor = "none" ,
500554 )
501555 else :
502556 scale_rect = Rectangle (
503557 (0 , 0 ),
504- width_px ,
558+ thickness ,
505559 length_px ,
506560 fill = True ,
507561 facecolor = color ,
508562 edgecolor = "none" ,
509563 )
510564
511- scale_bar_box = AuxTransformBox (ax . transData )
565+ scale_bar_box = AuxTransformBox (transform )
512566 scale_bar_box .add_artist (scale_rect )
513567
514568 # Create scale text
@@ -627,30 +681,45 @@ def set_length_fraction(self, fraction):
627681
628682 length_fraction = property (get_length_fraction , set_length_fraction )
629683
684+ def get_thickness (self ):
685+ return self ._thickness
686+
687+ def set_thickness (self , thickness ):
688+ if thickness is not None :
689+ _validate_dim (thickness )
690+ self ._thickness = thickness
691+
692+ thickness = property (get_thickness , set_thickness )
693+
630694 def get_width_fraction (self ):
631- return self ._width_fraction
695+ if self ._thickness is None :
696+ return None
697+ elif self ._thickness [1 ] == "saxis" :
698+ return self ._thickness [0 ]
699+ else :
700+ raise ValueError (f"thickness ({ self ._thickness } ) is not a width fraction" )
632701
633702 def set_width_fraction (self , fraction ):
634703 if fraction is not None :
635704 fraction = float (fraction )
636705 if fraction <= 0.0 or fraction > 1.0 :
637706 raise ValueError ("Width fraction must be between [0.0, 1.0]" )
638- self ._width_fraction = fraction
707+ self ._thickness = ( fraction , "saxis" )
639708
640709 width_fraction = property (get_width_fraction , set_width_fraction )
641710
642711 def get_height_fraction (self ):
643712 warnings .warn (
644713 "The get_height_fraction method is deprecated. "
645- "Use get_width_fraction instead." ,
714+ "Use get_thickness instead." ,
646715 DeprecationWarning ,
647716 )
648717 return self .width_fraction
649718
650719 def set_height_fraction (self , fraction ):
651720 warnings .warn (
652721 "The set_height_fraction method is deprecated. "
653- "Use set_width_fraction instead." ,
722+ "Use set_thickness instead." ,
654723 DeprecationWarning ,
655724 )
656725 self .width_fraction = fraction
0 commit comments