Skip to content

Commit b14aa60

Browse files
committed
Fix bug in polar plots: if rmax - rmin was too small, the old rpad padding heuristic broke down and sent the rlabels way off of the plot axes. The new approach is to not have rpad, but to change the alignment of the rlabels depending on the quadrant of the plot. This seems to work quite well, but is perhaps slightly backward incompatible.
1 parent 7f3bac3 commit b14aa60

File tree

2 files changed

+49
-42
lines changed

2 files changed

+49
-42
lines changed

CHANGELOG

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
2012-01-23 The radius labels in polar plots no longer use a fixed
2+
padding, but use a different alignment depending on the
3+
quadrant they are in. This fixes numerical problems when
4+
(rmax - rmin) gets too small. - MGD
5+
16
2011-12-27 Work around an EINTR bug in some versions of subprocess. - JKS
27

38
2011-08-18 Change api of Axes.get_tightbbox and add an optional

lib/matplotlib/projections/polar.py

+44-42
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def transform(self, tr):
6262

6363
t *= theta_direction
6464
t += theta_offset
65-
65+
6666
if rmin != 0:
6767
r = r - rmin
6868
mask = r < 0
@@ -149,7 +149,7 @@ def transform(self, xy):
149149
rmin = 0
150150
theta_offset = 0
151151
theta_direction = 1
152-
152+
153153
x = xy[:, 0:1]
154154
y = xy[:, 1:]
155155
r = np.sqrt(x*x + y*y)
@@ -160,7 +160,7 @@ def transform(self, xy):
160160
theta *= theta_direction
161161

162162
r += rmin
163-
163+
164164
return np.concatenate((theta, r), 1)
165165
transform.__doc__ = Transform.transform.__doc__
166166

@@ -229,7 +229,6 @@ def __init__(self, *args, **kwargs):
229229
interpolation.
230230
"""
231231

232-
self._rpad = 0.05
233232
self.resolution = kwargs.pop('resolution', None)
234233
if self.resolution not in (None, 1):
235234
warnings.warn(
@@ -261,7 +260,7 @@ def cla(self):
261260

262261
self.set_theta_offset(0)
263262
self.set_theta_direction(1)
264-
263+
265264
def _init_axis(self):
266265
"move this out of __init__ because non-separable axes don't use it"
267266
self.xaxis = maxis.XAxis(self)
@@ -319,21 +318,10 @@ def _set_lim_and_transforms(self):
319318
Affine2D().scale(np.pi * 2.0, 1.0) +
320319
self.transData)
321320
# The r-axis labels are put at an angle and padded in the r-direction
322-
self._r_label1_position = ScaledTranslation(
323-
22.5, self._rpad,
324-
blended_transform_factory(
325-
Affine2D(), BboxTransformToMaxOnly(self.viewLim)))
326-
self._yaxis_text1_transform = (
327-
self._r_label1_position +
328-
Affine2D().scale(1.0 / 360.0, 1.0) +
329-
self._yaxis_transform
330-
)
331-
self._r_label2_position = ScaledTranslation(
332-
22.5, -self._rpad,
333-
blended_transform_factory(
334-
Affine2D(), BboxTransformToMaxOnly(self.viewLim)))
335-
self._yaxis_text2_transform = (
336-
self._r_label2_position +
321+
self._r_label_position = ScaledTranslation(
322+
22.5, 0.0, Affine2D())
323+
self._yaxis_text_transform = (
324+
self._r_label_position +
337325
Affine2D().scale(1.0 / 360.0, 1.0) +
338326
self._yaxis_transform
339327
)
@@ -353,10 +341,26 @@ def get_yaxis_transform(self,which='grid'):
353341
return self._yaxis_transform
354342

355343
def get_yaxis_text1_transform(self, pad):
356-
return self._yaxis_text1_transform, 'center', 'center'
344+
angle = self._r_label_position.to_values()[4]
345+
if angle < 90.:
346+
return self._yaxis_text_transform, 'bottom', 'left'
347+
elif angle < 180.:
348+
return self._yaxis_text_transform, 'bottom', 'right'
349+
elif angle < 270.:
350+
return self._yaxis_text_transform, 'top', 'right'
351+
else:
352+
return self._yaxis_text_transform, 'top', 'left'
357353

358354
def get_yaxis_text2_transform(self, pad):
359-
return self._yaxis_text2_transform, 'center', 'center'
355+
angle = self._r_label_position.to_values()[4]
356+
if angle < 90.:
357+
return self._yaxis_text_transform, 'top', 'right'
358+
elif angle < 180.:
359+
return self._yaxis_text_transform, 'top', 'left'
360+
elif angle < 270.:
361+
return self._yaxis_text_transform, 'bottom', 'left'
362+
else:
363+
return self._yaxis_text_transform, 'bottom', 'right'
360364

361365
def _gen_axes_patch(self):
362366
return Circle((0.5, 0.5), 0.5)
@@ -406,7 +410,7 @@ def set_theta_zero_location(self, loc):
406410
'E': 0,
407411
'NE': np.pi * 0.25 }
408412
return self.set_theta_offset(mapping[loc])
409-
413+
410414
def set_theta_direction(self, direction):
411415
"""
412416
Set the direction in which theta increases.
@@ -437,7 +441,7 @@ def get_theta_direction(self):
437441
Theta increases in the counterclockwise direction
438442
"""
439443
return self._direction
440-
444+
441445
def set_rlim(self, *args, **kwargs):
442446
if 'rmin' in kwargs:
443447
kwargs['ymin'] = kwargs.pop('rmin')
@@ -494,7 +498,7 @@ def set_thetagrids(self, angles, labels=None, frac=None, fmt=None,
494498
return self.xaxis.get_ticklines(), self.xaxis.get_ticklabels()
495499

496500
@docstring.dedent_interpd
497-
def set_rgrids(self, radii, labels=None, angle=None, rpad=None, fmt=None,
501+
def set_rgrids(self, radii, labels=None, angle=None, fmt=None,
498502
**kwargs):
499503
"""
500504
Set the radial locations and labels of the *r* grids.
@@ -507,9 +511,6 @@ def set_rgrids(self, radii, labels=None, angle=None, rpad=None, fmt=None,
507511
508512
If *labels* is None, the built-in formatter will be used.
509513
510-
*rpad* is a fraction of the max of *radii* which will pad each of
511-
the radial labels in the radial direction.
512-
513514
Return value is a list of tuples (*line*, *label*), where
514515
*line* is :class:`~matplotlib.lines.Line2D` instances and the
515516
*label* is :class:`~matplotlib.text.Text` instances.
@@ -531,13 +532,9 @@ def set_rgrids(self, radii, labels=None, angle=None, rpad=None, fmt=None,
531532
elif fmt is not None:
532533
self.yaxis.set_major_formatter(FormatStrFormatter(fmt))
533534
if angle is None:
534-
angle = self._r_label1_position.to_values()[4]
535-
if rpad is not None:
536-
self._rpad = rpad
537-
self._r_label1_position._t = (angle, self._rpad)
538-
self._r_label1_position.invalidate()
539-
self._r_label2_position._t = (angle, -self._rpad)
540-
self._r_label2_position.invalidate()
535+
angle = self._r_label_position.to_values()[4]
536+
self._r_label_position._t = (angle, 0.0)
537+
self._r_label_position.invalidate()
541538
for t in self.yaxis.get_ticklabels():
542539
t.update(kwargs)
543540
return self.yaxis.get_gridlines(), self.yaxis.get_ticklabels()
@@ -589,7 +586,7 @@ def can_pan(self) :
589586
return True
590587

591588
def start_pan(self, x, y, button):
592-
angle = self._r_label1_position.to_values()[4] / 180.0 * np.pi
589+
angle = np.deg2rad(self._r_label_position.to_values()[4])
593590
mode = ''
594591
if button == 1:
595592
epsilon = np.pi / 45.0
@@ -603,7 +600,7 @@ def start_pan(self, x, y, button):
603600
rmax = self.get_rmax(),
604601
trans = self.transData.frozen(),
605602
trans_inverse = self.transData.inverted().frozen(),
606-
r_label_angle = self._r_label1_position.to_values()[4],
603+
r_label_angle = self._r_label_position.to_values()[4],
607604
x = x,
608605
y = y,
609606
mode = mode
@@ -628,11 +625,16 @@ def drag_pan(self, button, key, x, y):
628625
dt = dt0 * -1.0
629626
dt = (dt / np.pi) * 180.0
630627

631-
rpad = self._rpad
632-
self._r_label1_position._t = (p.r_label_angle - dt, rpad)
633-
self._r_label1_position.invalidate()
634-
self._r_label2_position._t = (p.r_label_angle - dt, -rpad)
635-
self._r_label2_position.invalidate()
628+
self._r_label_position._t = (p.r_label_angle - dt, 0.0)
629+
self._r_label_position.invalidate()
630+
631+
trans, vert1, horiz1 = self.get_yaxis_text1_transform(0.0)
632+
trans, vert2, horiz2 = self.get_yaxis_text2_transform(0.0)
633+
for t in self.yaxis.majorTicks + self.yaxis.minorTicks:
634+
t.label1.set_va(vert1)
635+
t.label1.set_ha(horiz1)
636+
t.label2.set_va(vert2)
637+
t.label2.set_ha(horiz2)
636638

637639
elif p.mode == 'zoom':
638640
startt, startr = p.trans_inverse.transform_point((p.x, p.y))

0 commit comments

Comments
 (0)