diff --git a/doc/api/next_api_changes/deprecations/24834-DS.rst b/doc/api/next_api_changes/deprecations/24834-DS.rst new file mode 100644 index 000000000000..3761daaf1275 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/24834-DS.rst @@ -0,0 +1,17 @@ +Applying theta transforms in ``PolarTransform`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Applying theta transforms in `~matplotlib.projections.polar.PolarTransform` +and `~matplotlib.projections.polar.InvertedPolarTransform` +is deprecated, and will be removed in a future version of Matplotlib. This +is currently the default behaviour when these transforms are used externally, +but only takes affect when: + +- An axis is associated with the transform. +- The axis has a non-zero theta offset or has theta values increasing in + a clockwise direction. + +To silence this warning and adopt future behaviour, +set ``apply_theta_transforms=False``. If you need to retain the behaviour +where theta values are transformed, chain the ``PolarTransform`` with +a `~matplotlib.transforms.Affine2D` transform that performs the theta shift +and/or sign shift. diff --git a/galleries/examples/axisartist/demo_axis_direction.py b/galleries/examples/axisartist/demo_axis_direction.py index 00ba40004a59..8c57b6c5a351 100644 --- a/galleries/examples/axisartist/demo_axis_direction.py +++ b/galleries/examples/axisartist/demo_axis_direction.py @@ -20,7 +20,10 @@ def setup_axes(fig, rect): """Polar projection, but in a rectangular box.""" # see demo_curvelinear_grid.py for details grid_helper = GridHelperCurveLinear( - Affine2D().scale(np.pi/180., 1.) + PolarAxes.PolarTransform(), + ( + Affine2D().scale(np.pi/180., 1.) + + PolarAxes.PolarTransform(apply_theta_transforms=False) + ), extreme_finder=angle_helper.ExtremeFinderCycle( 20, 20, lon_cycle=360, lat_cycle=None, diff --git a/galleries/examples/axisartist/demo_curvelinear_grid.py b/galleries/examples/axisartist/demo_curvelinear_grid.py index fb1fbdd011ce..40853dee12cb 100644 --- a/galleries/examples/axisartist/demo_curvelinear_grid.py +++ b/galleries/examples/axisartist/demo_curvelinear_grid.py @@ -54,7 +54,8 @@ def curvelinear_test2(fig): # PolarAxes.PolarTransform takes radian. However, we want our coordinate # system in degree - tr = Affine2D().scale(np.pi/180, 1) + PolarAxes.PolarTransform() + tr = Affine2D().scale(np.pi/180, 1) + PolarAxes.PolarTransform( + apply_theta_transforms=False) # Polar projection, which involves cycle, and also has limits in # its coordinates, needs a special method to find the extremes # (min, max of the coordinate within the view). diff --git a/galleries/examples/axisartist/demo_floating_axes.py b/galleries/examples/axisartist/demo_floating_axes.py index add03e266d3e..632f6d237aa6 100644 --- a/galleries/examples/axisartist/demo_floating_axes.py +++ b/galleries/examples/axisartist/demo_floating_axes.py @@ -54,7 +54,7 @@ def setup_axes2(fig, rect): With custom locator and formatter. Note that the extreme values are swapped. """ - tr = PolarAxes.PolarTransform() + tr = PolarAxes.PolarTransform(apply_theta_transforms=False) pi = np.pi angle_ticks = [(0, r"$0$"), @@ -99,7 +99,8 @@ def setup_axes3(fig, rect): # scale degree to radians tr_scale = Affine2D().scale(np.pi/180., 1.) - tr = tr_rotate + tr_scale + PolarAxes.PolarTransform() + tr = tr_rotate + tr_scale + PolarAxes.PolarTransform( + apply_theta_transforms=False) grid_locator1 = angle_helper.LocatorHMS(4) tick_formatter1 = angle_helper.FormatterHMS() diff --git a/galleries/examples/axisartist/demo_floating_axis.py b/galleries/examples/axisartist/demo_floating_axis.py index 0894bf8f4ce1..5296b682367b 100644 --- a/galleries/examples/axisartist/demo_floating_axis.py +++ b/galleries/examples/axisartist/demo_floating_axis.py @@ -22,7 +22,8 @@ def curvelinear_test2(fig): """Polar projection, but in a rectangular box.""" # see demo_curvelinear_grid.py for details - tr = Affine2D().scale(np.pi / 180., 1.) + PolarAxes.PolarTransform() + tr = Affine2D().scale(np.pi / 180., 1.) + PolarAxes.PolarTransform( + apply_theta_transforms=False) extreme_finder = angle_helper.ExtremeFinderCycle(20, 20, diff --git a/galleries/examples/axisartist/simple_axis_pad.py b/galleries/examples/axisartist/simple_axis_pad.py index 7027a88d3549..9c613c820b2b 100644 --- a/galleries/examples/axisartist/simple_axis_pad.py +++ b/galleries/examples/axisartist/simple_axis_pad.py @@ -21,7 +21,8 @@ def setup_axes(fig, rect): """Polar projection, but in a rectangular box.""" # see demo_curvelinear_grid.py for details - tr = Affine2D().scale(np.pi/180., 1.) + PolarAxes.PolarTransform() + tr = Affine2D().scale(np.pi/180., 1.) + PolarAxes.PolarTransform( + apply_theta_transforms=False) extreme_finder = angle_helper.ExtremeFinderCycle(20, 20, lon_cycle=360, diff --git a/lib/matplotlib/projections/polar.py b/lib/matplotlib/projections/polar.py index 7b7b9dac21ca..6bd72d2e35e0 100644 --- a/lib/matplotlib/projections/polar.py +++ b/lib/matplotlib/projections/polar.py @@ -15,6 +15,20 @@ from matplotlib.spines import Spine +def _apply_theta_transforms_warn(): + _api.warn_deprecated( + "3.9", + message=( + "Passing `apply_theta_transforms=True` (the default) " + "is deprecated since Matplotlib %(since)s. " + "Support for this will be removed in Matplotlib %(removal)s. " + "To prevent this warning, set `apply_theta_transforms=False`, " + "and make sure to shift theta values before being passed to " + "this transform." + ) + ) + + class PolarTransform(mtransforms.Transform): r""" The base polar transform. @@ -34,8 +48,8 @@ class PolarTransform(mtransforms.Transform): input_dims = output_dims = 2 - def __init__(self, axis=None, use_rmin=True, - _apply_theta_transforms=True, *, scale_transform=None): + def __init__(self, axis=None, use_rmin=True, *, + apply_theta_transforms=True, scale_transform=None): """ Parameters ---------- @@ -50,13 +64,15 @@ def __init__(self, axis=None, use_rmin=True, super().__init__() self._axis = axis self._use_rmin = use_rmin - self._apply_theta_transforms = _apply_theta_transforms + self._apply_theta_transforms = apply_theta_transforms self._scale_transform = scale_transform + if apply_theta_transforms: + _apply_theta_transforms_warn() __str__ = mtransforms._make_str_method( "_axis", use_rmin="_use_rmin", - _apply_theta_transforms="_apply_theta_transforms") + apply_theta_transforms="_apply_theta_transforms") def _get_rorigin(self): # Get lower r limit after being scaled by the radial scale transform @@ -133,8 +149,10 @@ def transform_path_non_affine(self, path): def inverted(self): # docstring inherited - return PolarAxes.InvertedPolarTransform(self._axis, self._use_rmin, - self._apply_theta_transforms) + return PolarAxes.InvertedPolarTransform( + self._axis, self._use_rmin, + apply_theta_transforms=self._apply_theta_transforms + ) class PolarAffine(mtransforms.Affine2DBase): @@ -193,7 +211,7 @@ class InvertedPolarTransform(mtransforms.Transform): input_dims = output_dims = 2 def __init__(self, axis=None, use_rmin=True, - _apply_theta_transforms=True): + *, apply_theta_transforms=True): """ Parameters ---------- @@ -208,12 +226,14 @@ def __init__(self, axis=None, use_rmin=True, super().__init__() self._axis = axis self._use_rmin = use_rmin - self._apply_theta_transforms = _apply_theta_transforms + self._apply_theta_transforms = apply_theta_transforms + if apply_theta_transforms: + _apply_theta_transforms_warn() __str__ = mtransforms._make_str_method( "_axis", use_rmin="_use_rmin", - _apply_theta_transforms="_apply_theta_transforms") + apply_theta_transforms="_apply_theta_transforms") @_api.rename_parameter("3.8", "xy", "values") def transform_non_affine(self, values): @@ -234,8 +254,10 @@ def transform_non_affine(self, values): def inverted(self): # docstring inherited - return PolarAxes.PolarTransform(self._axis, self._use_rmin, - self._apply_theta_transforms) + return PolarAxes.PolarTransform( + self._axis, self._use_rmin, + apply_theta_transforms=self._apply_theta_transforms + ) class ThetaFormatter(mticker.Formatter): @@ -879,7 +901,7 @@ def _set_lim_and_transforms(self): # data. This one is aware of rmin self.transProjection = self.PolarTransform( self, - _apply_theta_transforms=False, + apply_theta_transforms=False, scale_transform=self.transScale ) # Add dependency on rorigin. diff --git a/lib/matplotlib/projections/polar.pyi b/lib/matplotlib/projections/polar.pyi index 2592d4947184..de1cbc293900 100644 --- a/lib/matplotlib/projections/polar.pyi +++ b/lib/matplotlib/projections/polar.pyi @@ -17,8 +17,8 @@ class PolarTransform(mtransforms.Transform): self, axis: PolarAxes | None = ..., use_rmin: bool = ..., - _apply_theta_transforms: bool = ..., *, + apply_theta_transforms: bool = ..., scale_transform: mtransforms.Transform | None = ..., ) -> None: ... def inverted(self) -> InvertedPolarTransform: ... @@ -35,7 +35,8 @@ class InvertedPolarTransform(mtransforms.Transform): self, axis: PolarAxes | None = ..., use_rmin: bool = ..., - _apply_theta_transforms: bool = ..., + *, + apply_theta_transforms: bool = ..., ) -> None: ... def inverted(self) -> PolarTransform: ... diff --git a/lib/matplotlib/tests/test_transforms.py b/lib/matplotlib/tests/test_transforms.py index 95e76ad95c4f..959814de82db 100644 --- a/lib/matplotlib/tests/test_transforms.py +++ b/lib/matplotlib/tests/test_transforms.py @@ -859,7 +859,7 @@ def test_str_transform(): PolarTransform( PolarAxes(0.125,0.1;0.775x0.8), use_rmin=True, - _apply_theta_transforms=False)), + apply_theta_transforms=False)), CompositeGenericTransform( CompositeGenericTransform( PolarAffine( diff --git a/lib/matplotlib/text.py b/lib/matplotlib/text.py index 572ec76e223a..da11194c6427 100644 --- a/lib/matplotlib/text.py +++ b/lib/matplotlib/text.py @@ -1497,7 +1497,7 @@ def _get_xy_transform(self, renderer, coords): return self.axes.transData elif coords == 'polar': from matplotlib.projections import PolarAxes - tr = PolarAxes.PolarTransform() + tr = PolarAxes.PolarTransform(apply_theta_transforms=False) trans = tr + self.axes.transData return trans diff --git a/lib/mpl_toolkits/axisartist/tests/test_floating_axes.py b/lib/mpl_toolkits/axisartist/tests/test_floating_axes.py index 31dcf24bb22d..7644fea16965 100644 --- a/lib/mpl_toolkits/axisartist/tests/test_floating_axes.py +++ b/lib/mpl_toolkits/axisartist/tests/test_floating_axes.py @@ -24,7 +24,7 @@ def test_curvelinear3(): fig = plt.figure(figsize=(5, 5)) tr = (mtransforms.Affine2D().scale(np.pi / 180, 1) + - mprojections.PolarAxes.PolarTransform()) + mprojections.PolarAxes.PolarTransform(apply_theta_transforms=False)) grid_helper = GridHelperCurveLinear( tr, extremes=(0, 360, 10, 3), @@ -73,7 +73,7 @@ def test_curvelinear4(): fig = plt.figure(figsize=(5, 5)) tr = (mtransforms.Affine2D().scale(np.pi / 180, 1) + - mprojections.PolarAxes.PolarTransform()) + mprojections.PolarAxes.PolarTransform(apply_theta_transforms=False)) grid_helper = GridHelperCurveLinear( tr, extremes=(120, 30, 10, 0), diff --git a/lib/mpl_toolkits/axisartist/tests/test_grid_helper_curvelinear.py b/lib/mpl_toolkits/axisartist/tests/test_grid_helper_curvelinear.py index eb7673fa1fa7..8e6aded047fe 100644 --- a/lib/mpl_toolkits/axisartist/tests/test_grid_helper_curvelinear.py +++ b/lib/mpl_toolkits/axisartist/tests/test_grid_helper_curvelinear.py @@ -82,7 +82,8 @@ def test_polar_box(): # PolarAxes.PolarTransform takes radian. However, we want our coordinate # system in degree - tr = Affine2D().scale(np.pi / 180., 1.) + PolarAxes.PolarTransform() + tr = (Affine2D().scale(np.pi / 180., 1.) + + PolarAxes.PolarTransform(apply_theta_transforms=False)) # polar projection, which involves cycle, and also has limits in # its coordinates, needs a special method to find the extremes @@ -144,7 +145,8 @@ def test_axis_direction(): # PolarAxes.PolarTransform takes radian. However, we want our coordinate # system in degree - tr = Affine2D().scale(np.pi / 180., 1.) + PolarAxes.PolarTransform() + tr = (Affine2D().scale(np.pi / 180., 1.) + + PolarAxes.PolarTransform(apply_theta_transforms=False)) # polar projection, which involves cycle, and also has limits in # its coordinates, needs a special method to find the extremes