diff --git a/README.md b/README.md index 3d2389d..298dbe5 100644 --- a/README.md +++ b/README.md @@ -311,7 +311,16 @@ Default: `False` ### rotation Whether to create a scale bar based on the x-axis (default) or y-axis. -*rotation* can either be `horizontal` or `vertical`. +*rotation* can either be `horizontal`, `vertical`, `horizontal-only`, or +`vertical-only`. + +By default, matplotlib_scalebar checks whether the axes have equal aspect ratio +(so that the scale bar applies both for the x and the y directions), and emits +a warning if this is not the case. This warning can be suppressed by setting +*rotation* to `horizontal-only` ("the colorbar only applies to the horizontal +direction") or `vertical-only` ("the colorbar only applies to the vertical +direction"). + Note you might have to adjust *scale_loc* and *label_loc* to achieve desired layout. Default: `None`, value from matplotlibrc or `horizontal`. diff --git a/matplotlib_scalebar/scalebar.py b/matplotlib_scalebar/scalebar.py index 92382b1..17f17fb 100644 --- a/matplotlib_scalebar/scalebar.py +++ b/matplotlib_scalebar/scalebar.py @@ -83,7 +83,7 @@ "label_loc", _VALID_LABEL_LOCATIONS, ignorecase=True ) -_VALID_ROTATIONS = ["horizontal", "vertical"] +_VALID_ROTATIONS = ["horizontal", "horizontal-only", "vertical", "vertical-only"] _validate_rotation = ValidateInStrings("rotation", _VALID_ROTATIONS, ignorecase=True) @@ -298,8 +298,11 @@ def __init__( :arg animated: animation state (default: ``False``) :type animated: :class`bool` - :arg rotation: either ``horizontal`` or ``vertical`` - (default: rcParams['scalebar.rotation'] or ``horizontal``) + :arg rotation: ``horizontal``, ``vertical``, ``horizontal-only``, or ``vertical-only`` + (default: rcParams['scalebar.rotation'] or ``horizontal``). + By default, ScaleBar checks that it is getting drawn on an axes + with equal aspect ratio and emits a warning if this is not the case. + The -only variants suppress that check. :type rotation: :class:`str` :arg bbox_to_anchor: box that is used to position the scalebar @@ -426,6 +429,14 @@ def _get_value(attr, default): fixed_value = self.fixed_value fixed_units = self.fixed_units or self.units rotation = _get_value("rotation", "horizontal").lower() + if rotation.endswith("-only"): + rotation = rotation[:-5] + else: # Check aspect ratio. + if self.axes.get_aspect() != 1: + warnings.warn( + f"Drawing scalebar on axes with unequal aspect ratio; " + f"either call ax.set_aspect(1) or suppress the warning with " + f"rotation='{rotation}-only'.") label = self.label # Create text properties diff --git a/matplotlib_scalebar/test_scalebar.py b/matplotlib_scalebar/test_scalebar.py index 0abb906..0a93791 100644 --- a/matplotlib_scalebar/test_scalebar.py +++ b/matplotlib_scalebar/test_scalebar.py @@ -2,6 +2,8 @@ # Standard library modules. +import warnings + # Third party modules. import matplotlib @@ -33,7 +35,7 @@ def scalebar(): yield scalebar - plt.draw() + plt.close(fig) def test_mpl_rcParams_update(): @@ -298,7 +300,8 @@ def test_label_formatter(scalebar): assert scalebar.label_formatter(value, units) == "m 5" -@pytest.mark.parametrize("rotation", ["horizontal", "vertical"]) +@pytest.mark.parametrize("rotation", [ + "horizontal", "vertical", "horizontal-only", "vertical-only"]) def test_rotation(scalebar, rotation): assert scalebar.get_rotation() is None assert scalebar.rotation is None @@ -311,6 +314,33 @@ def test_rotation(scalebar, rotation): scalebar.set_rotation("h") +def test_rotation_checks_aspect(): + fig, ax = plt.subplots() + sb = ScaleBar(0.5) + ax.add_artist(sb) + + with warnings.catch_warnings(): + warnings.simplefilter("error") + + for aspect in ["auto", 2]: + ax.set_aspect(aspect) # Warns if not using the -only variants. + for rotation in ["horizontal", "vertical"]: + sb.rotation = rotation + with pytest.warns(): + fig.canvas.draw() + sb.rotation = rotation + "-only" + fig.canvas.draw() + + ax.set_aspect("equal") # Never warn. + for rotation in ["horizontal", "vertical"]: + sb.rotation = rotation + fig.canvas.draw() + sb.rotation = rotation + "-only" + fig.canvas.draw() + + plt.close(fig) + + def test_bbox_to_anchor(scalebar): assert scalebar.get_bbox_to_anchor() is None assert scalebar.bbox_to_anchor is None