matplotlib:proyección personalizada para hemisferio/cuña
-
13-12-2019 - |
Pregunta
estoy mirando el proyección personalizada Ejemplo en la galería matplotlib. Estoy intentando modificarlo para trazar solo el hemisferio sur.He ajustado los límites necesarios [-pi/2,pi/2] a [-pi/2,0].Ahora he estado mirando:
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)}
Como puedes ver, reemplacé el parche Circle con un Wedge.Así es como se ve actualmente el gráfico de proyección:
El lomo sigue el círculo/elipse. ¿Cómo puedo especificar que quiero que el lomo siga el límite de la cuña?
No estoy seguro de cuál es la mejor manera de modificar la columna, por lo que cualquier ayuda sería muy apreciada.
Gracias,
Alex
Solución
Solo para que conste, definitivamente estás saltando directamente al fondo de la piscina si todavía eres nuevo en Python.(¡Y felicitaciones por entrar directamente!)
Lo que estás haciendo requiere un conocimiento razonablemente detallado del funcionamiento interno de matplotlib, que es una biblioteca bastante compleja.
Dicho esto, ¡es una buena manera de aprender rápidamente!
Para algo como esto, es necesario entender el interno arquitectura de cómo se estructuran las cosas en lugar de solo la API "pública".
Para la mayor parte de esto, debes profundizar y "usar la fuente".Para cualquier proyecto, la documentación del funcionamiento interno es el código mismo.
Dicho esto, para un caso simple, es bastante sencillo.
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()
Profundicemos en el _get_axes_spines
método un poco:
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}
Ahora hay algunos problemas con esto:
- Las cosas no están centradas correctamente dentro del eje.
- El parche de los ejes podría ampliarse un poco más para ocupar adecuadamente el espacio dentro de los ejes.
- Estamos dibujando las líneas de la cuadrícula para el globo completo y luego recortándolas.Sería más eficiente atraerlos únicamente dentro de nuestra cuña "inferior".
Sin embargo, cuando echamos un vistazo a cómo HammerAxes
está estructurado, notarás que muchas de estas cosas (especialmente el centrado del parche del eje) están efectivamente codificadas en las transformaciones.(Como mencionan en los comentarios, está destinado a ser un ejemplo de "juguete", y asumir que siempre estás tratando con el globo completo hace que las matemáticas en las transformaciones sean mucho más simples).
Si desea solucionar estos problemas, deberá modificar varias de las diversas transformaciones en HammerAxes._set_lim_and_transforms
.
Sin embargo, funciona razonablemente bien tal como está, así que lo dejaré como ejercicio para el lector.:) (Tenga en cuenta que esa parte es un poco más difícil, ya que requiere un conocimiento detallado de las transformaciones de matplotlib).