Introduction
Dans ce laboratoire, nous allons apprendre à créer une projection personnalisée à l'aide de Matplotlib. Nous allons présenter la projection Hammer en mettant en évidence de nombreuses fonctionnalités de Matplotlib. Nous utiliserons Python comme langage de programmation.
Conseils sur la machine virtuelle
Une fois le démarrage de la machine virtuelle terminé, cliquez dans le coin supérieur gauche pour basculer vers l'onglet Carnet de notes pour accéder au carnet Jupyter Notebook pour pratiquer.
Parfois, vous devrez peut-être attendre quelques secondes pour que le carnet Jupyter Notebook ait fini de charger. La validation des opérations ne peut pas être automatisée en raison des limitations du carnet Jupyter Notebook.
Si vous rencontrez des problèmes pendant l'apprentissage, n'hésitez pas à demander à Labby. Donnez votre feedback après la session, et nous résoudrons rapidement le problème pour vous.
Importation des bibliothèques
Tout d'abord, nous allons importer les bibliothèques nécessaires pour créer une projection personnalisée.
import numpy as np
import matplotlib
from matplotlib.axes import Axes
import matplotlib.axis as maxis
from matplotlib.patches import Circle
from matplotlib.path import Path
from matplotlib.projections import register_projection
import matplotlib.spines as mspines
from matplotlib.ticker import FixedLocator, Formatter, NullLocator
from matplotlib.transforms import Affine2D, BboxTransformTo, Transform
Création de la classe GeoAxes
Nous allons créer une classe de base abstraite pour les projections géographiques appelée GeoAxes.
class GeoAxes(Axes):
"""
Une classe de base abstraite pour les projections géographiques
"""
class ThetaFormatter(Formatter):
"""
Utilisée pour formater les étiquettes d'échelonnage en theta. Convertit
l'unité native en radians en degrés et ajoute un symbole de degré.
"""
def __init__(self, round_to=1.0):
self._round_to = round_to
def __call__(self, x, pos=None):
degrees = round(np.rad2deg(x) / self._round_to) * self._round_to
return f"{degrees:0.0f}\N{DEGREE SIGN}"
RESOLUTION = 75
def _init_axis(self):
self.xaxis = maxis.XAxis(self)
self.yaxis = maxis.YAxis(self)
## Ne pas enregistrer xaxis ou yaxis avec les épines - comme dans
## Axes._init_axis() - jusqu'à ce que GeoAxes.xaxis.clear() fonctionne.
## self.spines['geo'].register_axis(self.yaxis)
def clear(self):
## docstring héritée
super().clear()
self.set_longitude_grid(30)
self.set_latitude_grid(15)
self.set_longitude_grid_ends(75)
self.xaxis.set_minor_locator(NullLocator())
self.yaxis.set_minor_locator(NullLocator())
self.xaxis.set_ticks_position('none')
self.yaxis.set_ticks_position('none')
self.yaxis.set_tick_params(label1On=True)
## Pourquoi devons-nous activer les étiquettes d'échelonnage de l'axe y, mais
## les étiquettes d'échelonnage de l'axe x sont déjà activées?
self.grid(rcParams['axes.grid'])
Axes.set_xlim(self, -np.pi, np.pi)
Axes.set_ylim(self, -np.pi / 2.0, np.pi / 2.0)
Création de la classe HammerAxes
Nous allons créer une classe personnalisée pour la projection Aitoff-Hammer, une projection cartographique à aire égale appelée HammerAxes.
class HammerAxes(GeoAxes):
"""
Une classe personnalisée pour la projection Aitoff-Hammer, une projection cartographique
à aire égale.
https://en.wikipedia.org/wiki/Hammer_projection
"""
## La projection doit spécifier un nom. Celui-ci sera utilisé par
## l'utilisateur pour sélectionner la projection,
## c'est-à-dire ``subplot(projection='custom_hammer')``.
name = 'custom_hammer'
class HammerTransform(Transform):
"""La transformation de base de Hammer."""
input_dims = output_dims = 2
def __init__(self, resolution):
"""
Crée une nouvelle transformation de Hammer. La résolution est le nombre d'étapes
pour interpoler entre chaque segment de ligne d'entrée pour approximer son
trajet dans l'espace courbe de Hammer.
"""
Transform.__init__(self)
self._resolution = resolution
def transform_non_affine(self, ll):
longitude, latitude = ll.T
## Pré-calculer quelques valeurs
demi_longitude = longitude / 2
cos_latitude = np.cos(latitude)
racine_deux = np.sqrt(2)
alpha = np.sqrt(1 + cos_latitude * np.cos(demi_longitude))
x = (2 * racine_deux) * (cos_latitude * np.sin(demi_longitude)) / alpha
y = (racine_deux * np.sin(latitude)) / alpha
return np.column_stack([x, y])
def transform_path_non_affine(self, path):
## vertices = path.vertices
ipath = path.interpolé(self._resolution)
return Path(self.transform(ipath.vertices), ipath.codes)
def inverted(self):
return HammerAxes.InvertedHammerTransform(self._resolution)
class InvertedHammerTransform(Transform):
input_dims = output_dims = 2
def __init__(self, resolution):
Transform.__init__(self)
self._resolution = resolution
def transform_non_affine(self, xy):
x, y = xy.T
z = np.sqrt(1 - (x / 4) ** 2 - (y / 2) ** 2)
longitude = 2 * np.arctan((z * x) / (2 * (2 * z ** 2 - 1)))
latitude = np.arcsin(y*z)
return np.column_stack([longitude, latitude])
def inverted(self):
return HammerAxes.HammerTransform(self._resolution)
def __init__(self, *args, **kwargs):
self._longitude_cap = np.pi / 2.0
super().__init__(*args, **kwargs)
self.set_aspect(0.5, adjustable='box', anchor='C')
self.clear()
def _get_core_transform(self, resolution):
return self.HammerTransform(resolution)
Enregistrer la projection
Maintenant, nous allons enregistrer la projection avec Matplotlib afin que l'utilisateur puisse la sélectionner.
register_projection(HammerAxes)
Créer un exemple
Enfin, nous allons créer un exemple utilisant la projection personnalisée.
if __name__ == '__main__':
import matplotlib.pyplot as plt
## Maintenant, créons un exemple simple utilisant la projection personnalisée.
fig, ax = plt.subplots(subplot_kw={'projection': 'custom_hammer'})
ax.plot([-1, 1, 1], [-1, -1, 1], "o-")
ax.grid()
plt.show()
Sommaire
Dans ce laboratoire, nous avons appris à créer une projection personnalisée à l'aide de Matplotlib. Nous avons créé une projection personnalisée appelée projection de Hammer en exploitant de nombreuses fonctionnalités de Matplotlib. Nous avons appris à créer la classe GeoAxes, la classe HammerAxes, à enregistrer la projection et à créer un exemple utilisant la projection personnalisée.