Thanks to visit codestin.com
Credit goes to github.com

Skip to content

polar plots do not properly handle units #4905

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 29 additions & 5 deletions lib/matplotlib/projections/polar.py
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,8 @@ def set_theta_offset(self, offset):
"""
Set the offset for the location of 0 in radians.
"""
# Make sure to strip away units
offset = self.convert_xunits(offset)
self._theta_offset = offset

def get_theta_offset(self):
Expand Down Expand Up @@ -521,9 +523,17 @@ def set_thetagrids(self, angles, labels=None, frac=None, fmt=None,
ACCEPTS: sequence of floats
"""
# Make sure we take into account unitized data
angles = self.convert_yunits(angles)
angles = np.asarray(angles, np.float_)
self.set_xticks(angles * (np.pi / 180.0))
if isinstance( angles[0], float ):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this if-branch? Is convert_yunits() not returning radians? If not, is that a regression?

# If the data is floats it is assumed to be degrees (as per the
# docsting for this method)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docstring

angles = np.asarray(angles, np.float_)
self.set_xticks(angles * (np.pi / 180.0))
else:
# The unit converters are defined to return radians
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Silly question, where is this documented/implemented?

angles = self.convert_xunits(angles)
angles = np.asarray(angles, np.float_)
self.set_xticks(angles)

if labels is not None:
self.set_xticklabels(labels)
elif fmt is not None:
Expand Down Expand Up @@ -560,12 +570,16 @@ def set_rgrids(self, radii, labels=None, angle=None, fmt=None,
ACCEPTS: sequence of floats
"""
# Make sure we take into account unitized data
radii = self.convert_xunits(radii)
radii = self.convert_yunits(radii)
radii = np.asarray(radii)
rmin = radii.min()
if rmin <= 0:
raise ValueError('radial grids must be strictly positive')

# Handle unitized data
if angle is not None:
angle = self.convert_xunits(angle)

self.set_yticks(radii)
if labels is not None:
self.set_yticklabels(labels)
Expand All @@ -591,11 +605,21 @@ def format_coord(self, theta, r):
Return a format string formatting the coordinate using Unicode
characters.
"""
# Strip away any units
r = self.convert_yunits(r)
if not isinstance( theta, float ):
theta = self.convert_xunits(theta)
# Stripping away units makes theta be in radians.
# convert to degrees
theta = theta * (180.0 / np.pi)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I am wrong, but this seems incorrect? The subsequent code already divides theta by pi and later multiplies by 180. In fact, the formatting string says that it is displaying radians first, then degrees second.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're right. Even though this function calls the arguments theta/r, it's called by matplotlib, so it's (sorta) x/y, meaning "theta" is in radians.


theta /= math.pi
# \u03b8: lower-case theta
# \u03c0: lower-case pi
# \u00b0: degree symbol
return '\u03b8=%0.3f\u03c0 (%0.3f\u00b0), r=%0.3f' % (theta, theta * 180.0, r)
s = '\u03b8=%0.3f\u03c0 (%0.3f\u00b0), r=%0.3f' % (theta, theta * 180.0, r)
s = s.encode( 'utf-8' )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be necessary and probably indicates a bug somewhere else. Returning unicode from this function is actually what we want. Can you describe which backend/platform you were using when you can across this problem?

return s

def get_data_ratio(self):
'''
Expand Down
101 changes: 101 additions & 0 deletions lib/matplotlib/tests/test_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,107 @@ def test_polar_units():
assert_true(isinstance(plt.gca().get_xaxis().get_major_formatter(), units.UnitDblFormatter))


def test_polar_format_coord():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs a @cleanup decorator so global state does not leak.

import matplotlib.testing.jpl_units as units
units.register()

deg = units.UnitDbl(1.0, "deg")
km = units.UnitDbl(1.0, "km")

fig = plt.figure()
ax = plt.subplot(111, polar=True)

# Make sure this doesn't throw any exceptions
ax.format_coord( 30.0*deg, 4.0*km )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you PEP8 the whitespace here?



@image_comparison(baseline_images=['polar_rgrids'])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As these tests add new image comparison tests, you'll also need to commit the new baseline images.

def test_polar_rgrids():
import matplotlib.testing.jpl_units as units
units.register()

pi = np.pi
deg = units.UnitDbl(1.0, "deg")
rad = units.UnitDbl(1.0, "rad")
km = units.UnitDbl(1.0, "km")

fig = plt.figure()

ax1 = plt.subplot(221, polar=True)
ax1.set_rgrids([45*deg, 90*deg], angle=60.0*deg)

ax2 = plt.subplot(222, polar=True)
ax2.set_rgrids([pi/4*rad, pi/2*rad], angle=60.0*deg)

ax3 = plt.subplot(223, polar=True)
ax3.set_rgrids([45*deg, 90*deg], angle=pi/3*rad)

ax4 = plt.subplot(224, polar=True)
ax4.set_rgrids([pi/4*rad, pi/2*rad], angle=pi/3*rad)


@image_comparison(baseline_images=['polar_rticks'])
def test_polar_rticks():
import matplotlib.testing.jpl_units as units
units.register()

pi = np.pi
deg = units.UnitDbl(1.0, "deg")
rad = units.UnitDbl(1.0, "rad")
km = units.UnitDbl(1.0, "km")

fig = plt.figure()

ax = plt.subplot(111, polar=True)
ax.set_rticks([1.5*km, 3.0*km, 4.5*km])

x1 = [30.0*deg, 45.0*deg, 60.0*deg, 90.0*deg]
y1 = [1.0*km, 2.0*km, 3.0*km, 4.0*km]

ax.plot( x1, y1 )


@image_comparison(baseline_images=['polar_teta_offset'])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

teta -> theta

def test_polar_theta_offset():
import matplotlib.testing.jpl_units as units
units.register()

pi = np.pi
deg = units.UnitDbl(1.0, "deg")
rad = units.UnitDbl(1.0, "rad")
km = units.UnitDbl(1.0, "km")

fig = plt.figure()

ax1 = plt.subplot(211, polar=True)
ax1.set_theta_offset(45*deg)

ax2 = plt.subplot(212, polar=True)
ax2.set_theta_offset(pi/4*rad)


@image_comparison(baseline_images=['polar_tetagrids'])
def test_polar_thetagrids():
import matplotlib.testing.jpl_units as units
units.register()

pi = np.pi
deg = units.UnitDbl(1.0, "deg")
rad = units.UnitDbl(1.0, "rad")
km = units.UnitDbl(1.0, "km")

fig = plt.figure()

ax1 = plt.subplot(311, polar=True)
ax1.set_thetagrids([90*deg, 180*deg])

ax2 = plt.subplot(312, polar=True)
ax2.set_thetagrids([pi/2*rad, pi*rad])

ax3 = plt.subplot(313, polar=True)
ax3.set_thetagrids([90.0, 180.0])


@image_comparison(baseline_images=['polar_rmin'])
def test_polar_rmin():
r = np.arange(0, 3.0, 0.01)
Expand Down