I'm looking at the custom projection example in the matplotlib gallery -- I'm trying to modify it to plot only the southern hemisphere. I have adjusted the necessary [-pi/2,pi/2] limits to [-pi/2,0]. Now I've been looking at:
def _gen_axes_patch(self):
"""
Override this method to define the shape that is used for the
background of the plot. It should be a subclass of Patch.
In this case, it is a Circle (that may be warped by the axes
transform into an ellipse). Any data and gridlines will be
clipped to this shape.
"""
#return Circle((0.5, 0.5), 0.5)
return Wedge((0.5,0.5), 0.5, 180, 360)
def _gen_axes_spines(self):
return {'custom_hammer':mspines.Spine.circular_spine(self,
(0.5, 0.5), 0.25)}
As you can see, I've replaced the Circle patch with a Wedge. This is what the projection plot currently looks like:
The spine still follows the circle/ellipse -- how can I specify that I want the spine to follow the boundary of the wedge?
I am not sure how best to modify the spine, so any help would be very much appreciated!
Thanks,
Alex
Just for the record, you're definitely jumping straight into the deep end of the pool if you're still new to python. (And kudos to you for going right in!)
What you're doing requires a reasonably detailed knowledge of the inner workings of matplotlib, which is a rather complex library.
That having been said, it's a good way to learn quickly!
For something like this, you need to understand the internal architecture of how things are structured instead of just the "public" api.
For most of this, you have to dig in and "use the source". For any project, the documentation of the inner workings is the code itself.
That having been said, for a simple case, it's pretty straight-forward.
import numpy as np
from matplotlib.projections.geo import HammerAxes
import matplotlib.projections as mprojections
from matplotlib.axes import Axes
from matplotlib.patches import Wedge
import matplotlib.spines as mspines
class LowerHammerAxes(HammerAxes):
name = 'lower_hammer'
def cla(self):
HammerAxes.cla(self)
Axes.set_xlim(self, -np.pi, np.pi)
Axes.set_ylim(self, -np.pi / 2.0, 0)
def _gen_axes_patch(self):
return Wedge((0.5, 0.5), 0.5, 180, 360)
def _gen_axes_spines(self):
path = Wedge((0, 0), 1.0, 180, 360).get_path()
spine = mspines.Spine(self, 'circle', path)
spine.set_patch_circle((0.5, 0.5), 0.5)
return {'wedge':spine}
mprojections.register_projection(LowerHammerAxes)
if __name__ == '__main__':
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111, projection='lower_hammer')
ax.grid(True)
plt.show()
Let's dig into the _get_axes_spines
method a bit:
def _gen_axes_spines(self):
"""Return the spines for the axes."""
# Make the path for the spines
# We need the path, rather than the patch, thus the "get_path()"
# The path is expected to be centered at 0,0, with radius of 1
# It will be transformed by `Spine` when we initialize it
path = Wedge((0, 0), 1.0, 180, 360).get_path()
# We can fake a "wedge" spine without subclassing `Spine` by initializing
# it as a circular spine with the wedge path.
spine = mspines.Spine(self, 'circle', path)
# This sets some attributes of the patch object. In this particular
# case, what it sets happens to be approriate for our "wedge spine"
spine.set_patch_circle((0.5, 0.5), 0.5)
# Spines in matplotlib are handled in a dict (normally, you'd have top,
# left, right, and bottom, instead of just wedge). The name is arbitrary
return {'wedge':spine}
Now there are a few problems with this:
- Things aren't centered within the axis properly
- The axes patch could be scaled a bit larger to properly take up the room within the axes.
- We're drawing the grid lines for the full globe and then clipping them. It would be more efficient to only draw them inside our "lower" wedge.
However, when we take a look at how HammerAxes
is structured, you'll notice that a lot these things (especially the centering of the axis patch) are effectively hard-coded into the transforms. (As they mention in the comments, it's meant to be a "toy" example, and making the assumption that you're always dealing with the full globe makes the math in the transformations much simpler.)
If you want to fix these, you'll need to tweak several of the various transforms in HammerAxes._set_lim_and_transforms
.
However, it works reasonably well as-is, so I'll leave that as an exercise to the reader. :) (Be warned, that part's a bit harder, as it requires a detailed knowledge of matplotlib's transformations.)