Matplotlib:半球/楔形的自定义投影

3

我在浏览matplotlib画廊中的自定义投影示例——我试图修改它,只绘制南半球。我已经调整了必要的[-pi/2,pi/2]限制为[-pi/2,0]。现在我一直在看:

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)}

正如你所看到的,我已经用楔形替换了圆形补丁。目前投影图看起来是这样的:projection plot 脊柱仍然遵循圆/椭圆形 - 我该如何指定我想要脊柱遵循楔形的边界?
我不确定如何最好地修改脊柱,因此非常感谢任何帮助!
谢谢,
Alex

哦,只是想指出——我还是 Python 的新手,因此任何方法的解释都将非常有用!谢谢! - aim
1个回答

5
只是为了记录,如果你还不熟悉Python,那么你肯定会直接跳入深水区。(并且赞扬你的勇气!)你所做的需要相当详细的matplotlib内部工作知识,这是一个相当复杂的库。
话虽如此,这是快速学习的好方法!
对于这样的事情,你需要理解事物结构的内部架构而不仅仅是“公共”API。
对于大多数情况,你必须深入挖掘并使用源代码。对于任何项目,内部工作文档就是代码本身。
话虽如此,对于简单情况来说,它是非常直截了当的。
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()

enter image description here

让我们深入了解一下_get_axes_spines方法:

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}

现在存在一些问题:
  1. 轴内的物体未正确居中
  2. 轴路径应该缩放得更大,以便在轴内占用足够的空间。
  3. 我们正在为整个地球绘制网格线,然后对其进行剪切。 更有效的方法是仅在我们的“下”楔形图内部绘制它们。
但是,当我们查看 HammerAxes 的结构时,您会注意到很多这些事情(特别是轴补丁的居中)实际上已经硬编码到转换中了。(正如他们在注释中提到的那样,它是一个“玩具”示例,假定您总是处理完整的地球,可以使转换中的数学计算更简单。)
如果您想解决这些问题,您需要调整 HammerAxes._set_lim_and_transforms 中的各种变换。
但是,它的工作效果还算不错,所以我将把它留给读者去练习。 :)(请注意,这部分有点难度,因为它需要详细了解matplotlib的转换。)

谢谢Joe - 这是一个非常有用的答案!(我承认到目前为止,我更多地坚持使用公共API,直到我需要更多定制化的东西)!感谢您对get_axes_spines的详细解释。因此,通过使用“path”,理论上我可以定义一个沿任意形状的脊柱?(虽然变换会更复杂)。我将更详细地阅读matplotlib文档的必要页面,但我同意,玩弄代码可以很有启发性!再次感谢! - aim
您可以为脊柱定义一个更或者少的任意路径。对于真正的任意路径,您需要子类化 Spine ,但在这种情况下,它足以被视为圆形脊柱(就居中等方面而言)。与子类化 Axes 的详细指南不同,没有关于子类化 Spine 的详细指南,因此您需要深入挖掘和探索。无论如何,祝您好运! - Joe Kington

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接