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 25338bca021f..60eb5a2920f3 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 8734131dddc9..dcf1b7558b9f 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
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: