up follow livre
This commit is contained in:
parent
b4b4398bb0
commit
3a7a3849ae
12242 changed files with 2564461 additions and 6914 deletions
|
|
@ -0,0 +1,126 @@
|
|||
"""
|
||||
Non-separable transforms that map from data space to screen space.
|
||||
|
||||
Projections are defined as `~.axes.Axes` subclasses. They include the
|
||||
following elements:
|
||||
|
||||
- A transformation from data coordinates into display coordinates.
|
||||
|
||||
- An inverse of that transformation. This is used, for example, to convert
|
||||
mouse positions from screen space back into data space.
|
||||
|
||||
- Transformations for the gridlines, ticks and ticklabels. Custom projections
|
||||
will often need to place these elements in special locations, and Matplotlib
|
||||
has a facility to help with doing so.
|
||||
|
||||
- Setting up default values (overriding `~.axes.Axes.cla`), since the defaults
|
||||
for a rectilinear Axes may not be appropriate.
|
||||
|
||||
- Defining the shape of the Axes, for example, an elliptical Axes, that will be
|
||||
used to draw the background of the plot and for clipping any data elements.
|
||||
|
||||
- Defining custom locators and formatters for the projection. For example, in
|
||||
a geographic projection, it may be more convenient to display the grid in
|
||||
degrees, even if the data is in radians.
|
||||
|
||||
- Set up interactive panning and zooming. This is left as an "advanced"
|
||||
feature left to the reader, but there is an example of this for polar plots
|
||||
in `matplotlib.projections.polar`.
|
||||
|
||||
- Any additional methods for additional convenience or features.
|
||||
|
||||
Once the projection Axes is defined, it can be used in one of two ways:
|
||||
|
||||
- By defining the class attribute ``name``, the projection Axes can be
|
||||
registered with `matplotlib.projections.register_projection` and subsequently
|
||||
simply invoked by name::
|
||||
|
||||
fig.add_subplot(projection="my_proj_name")
|
||||
|
||||
- For more complex, parameterisable projections, a generic "projection" object
|
||||
may be defined which includes the method ``_as_mpl_axes``. ``_as_mpl_axes``
|
||||
should take no arguments and return the projection's Axes subclass and a
|
||||
dictionary of additional arguments to pass to the subclass' ``__init__``
|
||||
method. Subsequently a parameterised projection can be initialised with::
|
||||
|
||||
fig.add_subplot(projection=MyProjection(param1=param1_value))
|
||||
|
||||
where MyProjection is an object which implements a ``_as_mpl_axes`` method.
|
||||
|
||||
A full-fledged and heavily annotated example is in
|
||||
:doc:`/gallery/misc/custom_projection`. The polar plot functionality in
|
||||
`matplotlib.projections.polar` may also be of interest.
|
||||
"""
|
||||
|
||||
from .. import axes, _docstring
|
||||
from .geo import AitoffAxes, HammerAxes, LambertAxes, MollweideAxes
|
||||
from .polar import PolarAxes
|
||||
|
||||
try:
|
||||
from mpl_toolkits.mplot3d import Axes3D
|
||||
except Exception:
|
||||
import warnings
|
||||
warnings.warn("Unable to import Axes3D. This may be due to multiple versions of "
|
||||
"Matplotlib being installed (e.g. as a system package and as a pip "
|
||||
"package). As a result, the 3D projection is not available.")
|
||||
Axes3D = None
|
||||
|
||||
|
||||
class ProjectionRegistry:
|
||||
"""A mapping of registered projection names to projection classes."""
|
||||
|
||||
def __init__(self):
|
||||
self._all_projection_types = {}
|
||||
|
||||
def register(self, *projections):
|
||||
"""Register a new set of projections."""
|
||||
for projection in projections:
|
||||
name = projection.name
|
||||
self._all_projection_types[name] = projection
|
||||
|
||||
def get_projection_class(self, name):
|
||||
"""Get a projection class from its *name*."""
|
||||
return self._all_projection_types[name]
|
||||
|
||||
def get_projection_names(self):
|
||||
"""Return the names of all projections currently registered."""
|
||||
return sorted(self._all_projection_types)
|
||||
|
||||
|
||||
projection_registry = ProjectionRegistry()
|
||||
projection_registry.register(
|
||||
axes.Axes,
|
||||
PolarAxes,
|
||||
AitoffAxes,
|
||||
HammerAxes,
|
||||
LambertAxes,
|
||||
MollweideAxes,
|
||||
)
|
||||
if Axes3D is not None:
|
||||
projection_registry.register(Axes3D)
|
||||
else:
|
||||
# remove from namespace if not importable
|
||||
del Axes3D
|
||||
|
||||
|
||||
def register_projection(cls):
|
||||
projection_registry.register(cls)
|
||||
|
||||
|
||||
def get_projection_class(projection=None):
|
||||
"""
|
||||
Get a projection class from its name.
|
||||
|
||||
If *projection* is None, a standard rectilinear projection is returned.
|
||||
"""
|
||||
if projection is None:
|
||||
projection = 'rectilinear'
|
||||
|
||||
try:
|
||||
return projection_registry.get_projection_class(projection)
|
||||
except KeyError as err:
|
||||
raise ValueError("Unknown projection %r" % projection) from err
|
||||
|
||||
|
||||
get_projection_names = projection_registry.get_projection_names
|
||||
_docstring.interpd.register(projection_names=get_projection_names())
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
from .geo import (
|
||||
AitoffAxes as AitoffAxes,
|
||||
HammerAxes as HammerAxes,
|
||||
LambertAxes as LambertAxes,
|
||||
MollweideAxes as MollweideAxes,
|
||||
)
|
||||
from .polar import PolarAxes as PolarAxes
|
||||
from ..axes import Axes
|
||||
|
||||
class ProjectionRegistry:
|
||||
def __init__(self) -> None: ...
|
||||
def register(self, *projections: type[Axes]) -> None: ...
|
||||
def get_projection_class(self, name: str) -> type[Axes]: ...
|
||||
def get_projection_names(self) -> list[str]: ...
|
||||
|
||||
projection_registry: ProjectionRegistry
|
||||
|
||||
def register_projection(cls: type[Axes]) -> None: ...
|
||||
def get_projection_class(projection: str | None = ...) -> type[Axes]: ...
|
||||
def get_projection_names() -> list[str]: ...
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
511
venv/lib/python3.13/site-packages/matplotlib/projections/geo.py
Normal file
511
venv/lib/python3.13/site-packages/matplotlib/projections/geo.py
Normal file
|
|
@ -0,0 +1,511 @@
|
|||
import numpy as np
|
||||
|
||||
import matplotlib as mpl
|
||||
from matplotlib import _api
|
||||
from matplotlib.axes import Axes
|
||||
import matplotlib.axis as maxis
|
||||
from matplotlib.patches import Circle
|
||||
from matplotlib.path import Path
|
||||
import matplotlib.spines as mspines
|
||||
from matplotlib.ticker import (
|
||||
Formatter, NullLocator, FixedLocator, NullFormatter)
|
||||
from matplotlib.transforms import Affine2D, BboxTransformTo, Transform
|
||||
|
||||
|
||||
class GeoAxes(Axes):
|
||||
"""An abstract base class for geographic projections."""
|
||||
|
||||
class ThetaFormatter(Formatter):
|
||||
"""
|
||||
Used to format the theta tick labels. Converts the native
|
||||
unit of radians into degrees and adds a degree symbol.
|
||||
"""
|
||||
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, clear=False)
|
||||
self.yaxis = maxis.YAxis(self, clear=False)
|
||||
self.spines['geo'].register_axis(self.yaxis)
|
||||
|
||||
def clear(self):
|
||||
# docstring inherited
|
||||
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)
|
||||
# Why do we need to turn on yaxis tick labels, but
|
||||
# xaxis tick labels are already on?
|
||||
|
||||
self.grid(mpl.rcParams['axes.grid'])
|
||||
|
||||
Axes.set_xlim(self, -np.pi, np.pi)
|
||||
Axes.set_ylim(self, -np.pi / 2.0, np.pi / 2.0)
|
||||
|
||||
def _set_lim_and_transforms(self):
|
||||
# A (possibly non-linear) projection on the (already scaled) data
|
||||
self.transProjection = self._get_core_transform(self.RESOLUTION)
|
||||
|
||||
self.transAffine = self._get_affine_transform()
|
||||
|
||||
self.transAxes = BboxTransformTo(self.bbox)
|
||||
|
||||
# The complete data transformation stack -- from data all the
|
||||
# way to display coordinates
|
||||
self.transData = \
|
||||
self.transProjection + \
|
||||
self.transAffine + \
|
||||
self.transAxes
|
||||
|
||||
# This is the transform for longitude ticks.
|
||||
self._xaxis_pretransform = \
|
||||
Affine2D() \
|
||||
.scale(1, self._longitude_cap * 2) \
|
||||
.translate(0, -self._longitude_cap)
|
||||
self._xaxis_transform = \
|
||||
self._xaxis_pretransform + \
|
||||
self.transData
|
||||
self._xaxis_text1_transform = \
|
||||
Affine2D().scale(1, 0) + \
|
||||
self.transData + \
|
||||
Affine2D().translate(0, 4)
|
||||
self._xaxis_text2_transform = \
|
||||
Affine2D().scale(1, 0) + \
|
||||
self.transData + \
|
||||
Affine2D().translate(0, -4)
|
||||
|
||||
# This is the transform for latitude ticks.
|
||||
yaxis_stretch = Affine2D().scale(np.pi * 2, 1).translate(-np.pi, 0)
|
||||
yaxis_space = Affine2D().scale(1, 1.1)
|
||||
self._yaxis_transform = \
|
||||
yaxis_stretch + \
|
||||
self.transData
|
||||
yaxis_text_base = \
|
||||
yaxis_stretch + \
|
||||
self.transProjection + \
|
||||
(yaxis_space +
|
||||
self.transAffine +
|
||||
self.transAxes)
|
||||
self._yaxis_text1_transform = \
|
||||
yaxis_text_base + \
|
||||
Affine2D().translate(-8, 0)
|
||||
self._yaxis_text2_transform = \
|
||||
yaxis_text_base + \
|
||||
Affine2D().translate(8, 0)
|
||||
|
||||
def _get_affine_transform(self):
|
||||
transform = self._get_core_transform(1)
|
||||
xscale, _ = transform.transform((np.pi, 0))
|
||||
_, yscale = transform.transform((0, np.pi/2))
|
||||
return Affine2D() \
|
||||
.scale(0.5 / xscale, 0.5 / yscale) \
|
||||
.translate(0.5, 0.5)
|
||||
|
||||
def get_xaxis_transform(self, which='grid'):
|
||||
_api.check_in_list(['tick1', 'tick2', 'grid'], which=which)
|
||||
return self._xaxis_transform
|
||||
|
||||
def get_xaxis_text1_transform(self, pad):
|
||||
return self._xaxis_text1_transform, 'bottom', 'center'
|
||||
|
||||
def get_xaxis_text2_transform(self, pad):
|
||||
return self._xaxis_text2_transform, 'top', 'center'
|
||||
|
||||
def get_yaxis_transform(self, which='grid'):
|
||||
_api.check_in_list(['tick1', 'tick2', 'grid'], which=which)
|
||||
return self._yaxis_transform
|
||||
|
||||
def get_yaxis_text1_transform(self, pad):
|
||||
return self._yaxis_text1_transform, 'center', 'right'
|
||||
|
||||
def get_yaxis_text2_transform(self, pad):
|
||||
return self._yaxis_text2_transform, 'center', 'left'
|
||||
|
||||
def _gen_axes_patch(self):
|
||||
return Circle((0.5, 0.5), 0.5)
|
||||
|
||||
def _gen_axes_spines(self):
|
||||
return {'geo': mspines.Spine.circular_spine(self, (0.5, 0.5), 0.5)}
|
||||
|
||||
def set_yscale(self, *args, **kwargs):
|
||||
if args[0] != 'linear':
|
||||
raise NotImplementedError
|
||||
|
||||
set_xscale = set_yscale
|
||||
|
||||
def set_xlim(self, *args, **kwargs):
|
||||
"""Not supported. Please consider using Cartopy."""
|
||||
raise TypeError("Changing axes limits of a geographic projection is "
|
||||
"not supported. Please consider using Cartopy.")
|
||||
|
||||
set_ylim = set_xlim
|
||||
set_xbound = set_xlim
|
||||
set_ybound = set_ylim
|
||||
|
||||
def invert_xaxis(self):
|
||||
"""Not supported. Please consider using Cartopy."""
|
||||
raise TypeError("Changing axes limits of a geographic projection is "
|
||||
"not supported. Please consider using Cartopy.")
|
||||
|
||||
invert_yaxis = invert_xaxis
|
||||
|
||||
def format_coord(self, lon, lat):
|
||||
"""Return a format string formatting the coordinate."""
|
||||
lon, lat = np.rad2deg([lon, lat])
|
||||
ns = 'N' if lat >= 0.0 else 'S'
|
||||
ew = 'E' if lon >= 0.0 else 'W'
|
||||
return ('%f\N{DEGREE SIGN}%s, %f\N{DEGREE SIGN}%s'
|
||||
% (abs(lat), ns, abs(lon), ew))
|
||||
|
||||
def set_longitude_grid(self, degrees):
|
||||
"""
|
||||
Set the number of degrees between each longitude grid.
|
||||
"""
|
||||
# Skip -180 and 180, which are the fixed limits.
|
||||
grid = np.arange(-180 + degrees, 180, degrees)
|
||||
self.xaxis.set_major_locator(FixedLocator(np.deg2rad(grid)))
|
||||
self.xaxis.set_major_formatter(self.ThetaFormatter(degrees))
|
||||
|
||||
def set_latitude_grid(self, degrees):
|
||||
"""
|
||||
Set the number of degrees between each latitude grid.
|
||||
"""
|
||||
# Skip -90 and 90, which are the fixed limits.
|
||||
grid = np.arange(-90 + degrees, 90, degrees)
|
||||
self.yaxis.set_major_locator(FixedLocator(np.deg2rad(grid)))
|
||||
self.yaxis.set_major_formatter(self.ThetaFormatter(degrees))
|
||||
|
||||
def set_longitude_grid_ends(self, degrees):
|
||||
"""
|
||||
Set the latitude(s) at which to stop drawing the longitude grids.
|
||||
"""
|
||||
self._longitude_cap = np.deg2rad(degrees)
|
||||
self._xaxis_pretransform \
|
||||
.clear() \
|
||||
.scale(1.0, self._longitude_cap * 2.0) \
|
||||
.translate(0.0, -self._longitude_cap)
|
||||
|
||||
def get_data_ratio(self):
|
||||
"""Return the aspect ratio of the data itself."""
|
||||
return 1.0
|
||||
|
||||
### Interactive panning
|
||||
|
||||
def can_zoom(self):
|
||||
"""
|
||||
Return whether this Axes supports the zoom box button functionality.
|
||||
|
||||
This Axes object does not support interactive zoom box.
|
||||
"""
|
||||
return False
|
||||
|
||||
def can_pan(self):
|
||||
"""
|
||||
Return whether this Axes supports the pan/zoom button functionality.
|
||||
|
||||
This Axes object does not support interactive pan/zoom.
|
||||
"""
|
||||
return False
|
||||
|
||||
def start_pan(self, x, y, button):
|
||||
pass
|
||||
|
||||
def end_pan(self):
|
||||
pass
|
||||
|
||||
def drag_pan(self, button, key, x, y):
|
||||
pass
|
||||
|
||||
|
||||
class _GeoTransform(Transform):
|
||||
# Factoring out some common functionality.
|
||||
input_dims = output_dims = 2
|
||||
|
||||
def __init__(self, resolution):
|
||||
"""
|
||||
Create a new geographical transform.
|
||||
|
||||
Resolution is the number of steps to interpolate between each input
|
||||
line segment to approximate its path in curved space.
|
||||
"""
|
||||
super().__init__()
|
||||
self._resolution = resolution
|
||||
|
||||
def __str__(self):
|
||||
return f"{type(self).__name__}({self._resolution})"
|
||||
|
||||
def transform_path_non_affine(self, path):
|
||||
# docstring inherited
|
||||
ipath = path.interpolated(self._resolution)
|
||||
return Path(self.transform(ipath.vertices), ipath.codes)
|
||||
|
||||
|
||||
class AitoffAxes(GeoAxes):
|
||||
name = 'aitoff'
|
||||
|
||||
class AitoffTransform(_GeoTransform):
|
||||
"""The base Aitoff transform."""
|
||||
|
||||
def transform_non_affine(self, values):
|
||||
# docstring inherited
|
||||
longitude, latitude = values.T
|
||||
|
||||
# Pre-compute some values
|
||||
half_long = longitude / 2.0
|
||||
cos_latitude = np.cos(latitude)
|
||||
|
||||
alpha = np.arccos(cos_latitude * np.cos(half_long))
|
||||
sinc_alpha = np.sinc(alpha / np.pi) # np.sinc is sin(pi*x)/(pi*x).
|
||||
|
||||
x = (cos_latitude * np.sin(half_long)) / sinc_alpha
|
||||
y = np.sin(latitude) / sinc_alpha
|
||||
return np.column_stack([x, y])
|
||||
|
||||
def inverted(self):
|
||||
# docstring inherited
|
||||
return AitoffAxes.InvertedAitoffTransform(self._resolution)
|
||||
|
||||
class InvertedAitoffTransform(_GeoTransform):
|
||||
|
||||
def transform_non_affine(self, values):
|
||||
# docstring inherited
|
||||
# MGDTODO: Math is hard ;(
|
||||
return np.full_like(values, np.nan)
|
||||
|
||||
def inverted(self):
|
||||
# docstring inherited
|
||||
return AitoffAxes.AitoffTransform(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.AitoffTransform(resolution)
|
||||
|
||||
|
||||
class HammerAxes(GeoAxes):
|
||||
name = 'hammer'
|
||||
|
||||
class HammerTransform(_GeoTransform):
|
||||
"""The base Hammer transform."""
|
||||
|
||||
def transform_non_affine(self, values):
|
||||
# docstring inherited
|
||||
longitude, latitude = values.T
|
||||
half_long = longitude / 2.0
|
||||
cos_latitude = np.cos(latitude)
|
||||
sqrt2 = np.sqrt(2.0)
|
||||
alpha = np.sqrt(1.0 + cos_latitude * np.cos(half_long))
|
||||
x = (2.0 * sqrt2) * (cos_latitude * np.sin(half_long)) / alpha
|
||||
y = (sqrt2 * np.sin(latitude)) / alpha
|
||||
return np.column_stack([x, y])
|
||||
|
||||
def inverted(self):
|
||||
# docstring inherited
|
||||
return HammerAxes.InvertedHammerTransform(self._resolution)
|
||||
|
||||
class InvertedHammerTransform(_GeoTransform):
|
||||
|
||||
def transform_non_affine(self, values):
|
||||
# docstring inherited
|
||||
x, y = values.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):
|
||||
# docstring inherited
|
||||
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)
|
||||
|
||||
|
||||
class MollweideAxes(GeoAxes):
|
||||
name = 'mollweide'
|
||||
|
||||
class MollweideTransform(_GeoTransform):
|
||||
"""The base Mollweide transform."""
|
||||
|
||||
def transform_non_affine(self, values):
|
||||
# docstring inherited
|
||||
def d(theta):
|
||||
delta = (-(theta + np.sin(theta) - pi_sin_l)
|
||||
/ (1 + np.cos(theta)))
|
||||
return delta, np.abs(delta) > 0.001
|
||||
|
||||
longitude, latitude = values.T
|
||||
|
||||
clat = np.pi/2 - np.abs(latitude)
|
||||
ihigh = clat < 0.087 # within 5 degrees of the poles
|
||||
ilow = ~ihigh
|
||||
aux = np.empty(latitude.shape, dtype=float)
|
||||
|
||||
if ilow.any(): # Newton-Raphson iteration
|
||||
pi_sin_l = np.pi * np.sin(latitude[ilow])
|
||||
theta = 2.0 * latitude[ilow]
|
||||
delta, large_delta = d(theta)
|
||||
while np.any(large_delta):
|
||||
theta[large_delta] += delta[large_delta]
|
||||
delta, large_delta = d(theta)
|
||||
aux[ilow] = theta / 2
|
||||
|
||||
if ihigh.any(): # Taylor series-based approx. solution
|
||||
e = clat[ihigh]
|
||||
d = 0.5 * (3 * np.pi * e**2) ** (1.0/3)
|
||||
aux[ihigh] = (np.pi/2 - d) * np.sign(latitude[ihigh])
|
||||
|
||||
xy = np.empty(values.shape, dtype=float)
|
||||
xy[:, 0] = (2.0 * np.sqrt(2.0) / np.pi) * longitude * np.cos(aux)
|
||||
xy[:, 1] = np.sqrt(2.0) * np.sin(aux)
|
||||
|
||||
return xy
|
||||
|
||||
def inverted(self):
|
||||
# docstring inherited
|
||||
return MollweideAxes.InvertedMollweideTransform(self._resolution)
|
||||
|
||||
class InvertedMollweideTransform(_GeoTransform):
|
||||
|
||||
def transform_non_affine(self, values):
|
||||
# docstring inherited
|
||||
x, y = values.T
|
||||
# from Equations (7, 8) of
|
||||
# https://mathworld.wolfram.com/MollweideProjection.html
|
||||
theta = np.arcsin(y / np.sqrt(2))
|
||||
longitude = (np.pi / (2 * np.sqrt(2))) * x / np.cos(theta)
|
||||
latitude = np.arcsin((2 * theta + np.sin(2 * theta)) / np.pi)
|
||||
return np.column_stack([longitude, latitude])
|
||||
|
||||
def inverted(self):
|
||||
# docstring inherited
|
||||
return MollweideAxes.MollweideTransform(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.MollweideTransform(resolution)
|
||||
|
||||
|
||||
class LambertAxes(GeoAxes):
|
||||
name = 'lambert'
|
||||
|
||||
class LambertTransform(_GeoTransform):
|
||||
"""The base Lambert transform."""
|
||||
|
||||
def __init__(self, center_longitude, center_latitude, resolution):
|
||||
"""
|
||||
Create a new Lambert transform. Resolution is the number of steps
|
||||
to interpolate between each input line segment to approximate its
|
||||
path in curved Lambert space.
|
||||
"""
|
||||
_GeoTransform.__init__(self, resolution)
|
||||
self._center_longitude = center_longitude
|
||||
self._center_latitude = center_latitude
|
||||
|
||||
def transform_non_affine(self, values):
|
||||
# docstring inherited
|
||||
longitude, latitude = values.T
|
||||
clong = self._center_longitude
|
||||
clat = self._center_latitude
|
||||
cos_lat = np.cos(latitude)
|
||||
sin_lat = np.sin(latitude)
|
||||
diff_long = longitude - clong
|
||||
cos_diff_long = np.cos(diff_long)
|
||||
|
||||
inner_k = np.maximum( # Prevent divide-by-zero problems
|
||||
1 + np.sin(clat)*sin_lat + np.cos(clat)*cos_lat*cos_diff_long,
|
||||
1e-15)
|
||||
k = np.sqrt(2 / inner_k)
|
||||
x = k * cos_lat*np.sin(diff_long)
|
||||
y = k * (np.cos(clat)*sin_lat - np.sin(clat)*cos_lat*cos_diff_long)
|
||||
|
||||
return np.column_stack([x, y])
|
||||
|
||||
def inverted(self):
|
||||
# docstring inherited
|
||||
return LambertAxes.InvertedLambertTransform(
|
||||
self._center_longitude,
|
||||
self._center_latitude,
|
||||
self._resolution)
|
||||
|
||||
class InvertedLambertTransform(_GeoTransform):
|
||||
|
||||
def __init__(self, center_longitude, center_latitude, resolution):
|
||||
_GeoTransform.__init__(self, resolution)
|
||||
self._center_longitude = center_longitude
|
||||
self._center_latitude = center_latitude
|
||||
|
||||
def transform_non_affine(self, values):
|
||||
# docstring inherited
|
||||
x, y = values.T
|
||||
clong = self._center_longitude
|
||||
clat = self._center_latitude
|
||||
p = np.maximum(np.hypot(x, y), 1e-9)
|
||||
c = 2 * np.arcsin(0.5 * p)
|
||||
sin_c = np.sin(c)
|
||||
cos_c = np.cos(c)
|
||||
|
||||
latitude = np.arcsin(cos_c*np.sin(clat) +
|
||||
((y*sin_c*np.cos(clat)) / p))
|
||||
longitude = clong + np.arctan(
|
||||
(x*sin_c) / (p*np.cos(clat)*cos_c - y*np.sin(clat)*sin_c))
|
||||
|
||||
return np.column_stack([longitude, latitude])
|
||||
|
||||
def inverted(self):
|
||||
# docstring inherited
|
||||
return LambertAxes.LambertTransform(
|
||||
self._center_longitude,
|
||||
self._center_latitude,
|
||||
self._resolution)
|
||||
|
||||
def __init__(self, *args, center_longitude=0, center_latitude=0, **kwargs):
|
||||
self._longitude_cap = np.pi / 2
|
||||
self._center_longitude = center_longitude
|
||||
self._center_latitude = center_latitude
|
||||
super().__init__(*args, **kwargs)
|
||||
self.set_aspect('equal', adjustable='box', anchor='C')
|
||||
self.clear()
|
||||
|
||||
def clear(self):
|
||||
# docstring inherited
|
||||
super().clear()
|
||||
self.yaxis.set_major_formatter(NullFormatter())
|
||||
|
||||
def _get_core_transform(self, resolution):
|
||||
return self.LambertTransform(
|
||||
self._center_longitude,
|
||||
self._center_latitude,
|
||||
resolution)
|
||||
|
||||
def _get_affine_transform(self):
|
||||
return Affine2D() \
|
||||
.scale(0.25) \
|
||||
.translate(0.5, 0.5)
|
||||
112
venv/lib/python3.13/site-packages/matplotlib/projections/geo.pyi
Normal file
112
venv/lib/python3.13/site-packages/matplotlib/projections/geo.pyi
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
from matplotlib.axes import Axes
|
||||
from matplotlib.ticker import Formatter
|
||||
from matplotlib.transforms import Transform
|
||||
|
||||
from typing import Any, Literal
|
||||
|
||||
class GeoAxes(Axes):
|
||||
class ThetaFormatter(Formatter):
|
||||
def __init__(self, round_to: float = ...) -> None: ...
|
||||
def __call__(self, x: float, pos: Any | None = ...): ...
|
||||
RESOLUTION: float
|
||||
def get_xaxis_transform(
|
||||
self, which: Literal["tick1", "tick2", "grid"] = ...
|
||||
) -> Transform: ...
|
||||
def get_xaxis_text1_transform(
|
||||
self, pad: float
|
||||
) -> tuple[
|
||||
Transform,
|
||||
Literal["center", "top", "bottom", "baseline", "center_baseline"],
|
||||
Literal["center", "left", "right"],
|
||||
]: ...
|
||||
def get_xaxis_text2_transform(
|
||||
self, pad: float
|
||||
) -> tuple[
|
||||
Transform,
|
||||
Literal["center", "top", "bottom", "baseline", "center_baseline"],
|
||||
Literal["center", "left", "right"],
|
||||
]: ...
|
||||
def get_yaxis_transform(
|
||||
self, which: Literal["tick1", "tick2", "grid"] = ...
|
||||
) -> Transform: ...
|
||||
def get_yaxis_text1_transform(
|
||||
self, pad: float
|
||||
) -> tuple[
|
||||
Transform,
|
||||
Literal["center", "top", "bottom", "baseline", "center_baseline"],
|
||||
Literal["center", "left", "right"],
|
||||
]: ...
|
||||
def get_yaxis_text2_transform(
|
||||
self, pad: float
|
||||
) -> tuple[
|
||||
Transform,
|
||||
Literal["center", "top", "bottom", "baseline", "center_baseline"],
|
||||
Literal["center", "left", "right"],
|
||||
]: ...
|
||||
def set_xlim(self, *args, **kwargs) -> tuple[float, float]: ...
|
||||
def set_ylim(self, *args, **kwargs) -> tuple[float, float]: ...
|
||||
def format_coord(self, lon: float, lat: float) -> str: ...
|
||||
def set_longitude_grid(self, degrees: float) -> None: ...
|
||||
def set_latitude_grid(self, degrees: float) -> None: ...
|
||||
def set_longitude_grid_ends(self, degrees: float) -> None: ...
|
||||
def get_data_ratio(self) -> float: ...
|
||||
def can_zoom(self) -> bool: ...
|
||||
def can_pan(self) -> bool: ...
|
||||
def start_pan(self, x, y, button) -> None: ...
|
||||
def end_pan(self) -> None: ...
|
||||
def drag_pan(self, button, key, x, y) -> None: ...
|
||||
|
||||
class _GeoTransform(Transform):
|
||||
input_dims: int
|
||||
output_dims: int
|
||||
def __init__(self, resolution: int) -> None: ...
|
||||
|
||||
class AitoffAxes(GeoAxes):
|
||||
name: str
|
||||
|
||||
class AitoffTransform(_GeoTransform):
|
||||
def inverted(self) -> AitoffAxes.InvertedAitoffTransform: ...
|
||||
|
||||
class InvertedAitoffTransform(_GeoTransform):
|
||||
def inverted(self) -> AitoffAxes.AitoffTransform: ...
|
||||
|
||||
class HammerAxes(GeoAxes):
|
||||
name: str
|
||||
|
||||
class HammerTransform(_GeoTransform):
|
||||
def inverted(self) -> HammerAxes.InvertedHammerTransform: ...
|
||||
|
||||
class InvertedHammerTransform(_GeoTransform):
|
||||
def inverted(self) -> HammerAxes.HammerTransform: ...
|
||||
|
||||
class MollweideAxes(GeoAxes):
|
||||
name: str
|
||||
|
||||
class MollweideTransform(_GeoTransform):
|
||||
def inverted(self) -> MollweideAxes.InvertedMollweideTransform: ...
|
||||
|
||||
class InvertedMollweideTransform(_GeoTransform):
|
||||
def inverted(self) -> MollweideAxes.MollweideTransform: ...
|
||||
|
||||
class LambertAxes(GeoAxes):
|
||||
name: str
|
||||
|
||||
class LambertTransform(_GeoTransform):
|
||||
def __init__(
|
||||
self, center_longitude: float, center_latitude: float, resolution: int
|
||||
) -> None: ...
|
||||
def inverted(self) -> LambertAxes.InvertedLambertTransform: ...
|
||||
|
||||
class InvertedLambertTransform(_GeoTransform):
|
||||
def __init__(
|
||||
self, center_longitude: float, center_latitude: float, resolution: int
|
||||
) -> None: ...
|
||||
def inverted(self) -> LambertAxes.LambertTransform: ...
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*args,
|
||||
center_longitude: float = ...,
|
||||
center_latitude: float = ...,
|
||||
**kwargs
|
||||
) -> None: ...
|
||||
1563
venv/lib/python3.13/site-packages/matplotlib/projections/polar.py
Normal file
1563
venv/lib/python3.13/site-packages/matplotlib/projections/polar.py
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,197 @@
|
|||
import matplotlib.axis as maxis
|
||||
import matplotlib.ticker as mticker
|
||||
import matplotlib.transforms as mtransforms
|
||||
from matplotlib.axes import Axes
|
||||
from matplotlib.lines import Line2D
|
||||
from matplotlib.text import Text
|
||||
|
||||
import numpy as np
|
||||
from numpy.typing import ArrayLike
|
||||
from collections.abc import Sequence
|
||||
from typing import Any, ClassVar, Literal, overload
|
||||
|
||||
class PolarTransform(mtransforms.Transform):
|
||||
input_dims: int
|
||||
output_dims: int
|
||||
def __init__(
|
||||
self,
|
||||
axis: PolarAxes | None = ...,
|
||||
use_rmin: bool = ...,
|
||||
*,
|
||||
apply_theta_transforms: bool = ...,
|
||||
scale_transform: mtransforms.Transform | None = ...,
|
||||
) -> None: ...
|
||||
def inverted(self) -> InvertedPolarTransform: ...
|
||||
|
||||
class PolarAffine(mtransforms.Affine2DBase):
|
||||
def __init__(
|
||||
self, scale_transform: mtransforms.Transform, limits: mtransforms.BboxBase
|
||||
) -> None: ...
|
||||
|
||||
class InvertedPolarTransform(mtransforms.Transform):
|
||||
input_dims: int
|
||||
output_dims: int
|
||||
def __init__(
|
||||
self,
|
||||
axis: PolarAxes | None = ...,
|
||||
use_rmin: bool = ...,
|
||||
*,
|
||||
apply_theta_transforms: bool = ...,
|
||||
) -> None: ...
|
||||
def inverted(self) -> PolarTransform: ...
|
||||
|
||||
class ThetaFormatter(mticker.Formatter): ...
|
||||
|
||||
class _AxisWrapper:
|
||||
def __init__(self, axis: maxis.Axis) -> None: ...
|
||||
def get_view_interval(self) -> np.ndarray: ...
|
||||
def set_view_interval(self, vmin: float, vmax: float) -> None: ...
|
||||
def get_minpos(self) -> float: ...
|
||||
def get_data_interval(self) -> np.ndarray: ...
|
||||
def set_data_interval(self, vmin: float, vmax: float) -> None: ...
|
||||
def get_tick_space(self) -> int: ...
|
||||
|
||||
class ThetaLocator(mticker.Locator):
|
||||
base: mticker.Locator
|
||||
axis: _AxisWrapper | None
|
||||
def __init__(self, base: mticker.Locator) -> None: ...
|
||||
|
||||
class ThetaTick(maxis.XTick):
|
||||
def __init__(self, axes: PolarAxes, *args, **kwargs) -> None: ...
|
||||
|
||||
class ThetaAxis(maxis.XAxis):
|
||||
axis_name: str
|
||||
|
||||
class RadialLocator(mticker.Locator):
|
||||
base: mticker.Locator
|
||||
def __init__(self, base, axes: PolarAxes | None = ...) -> None: ...
|
||||
|
||||
class RadialTick(maxis.YTick): ...
|
||||
|
||||
class RadialAxis(maxis.YAxis):
|
||||
axis_name: str
|
||||
|
||||
class _WedgeBbox(mtransforms.Bbox):
|
||||
def __init__(
|
||||
self,
|
||||
center: tuple[float, float],
|
||||
viewLim: mtransforms.Bbox,
|
||||
originLim: mtransforms.Bbox,
|
||||
**kwargs,
|
||||
) -> None: ...
|
||||
|
||||
class PolarAxes(Axes):
|
||||
|
||||
PolarTransform: ClassVar[type] = PolarTransform
|
||||
PolarAffine: ClassVar[type] = PolarAffine
|
||||
InvertedPolarTransform: ClassVar[type] = InvertedPolarTransform
|
||||
ThetaFormatter: ClassVar[type] = ThetaFormatter
|
||||
RadialLocator: ClassVar[type] = RadialLocator
|
||||
ThetaLocator: ClassVar[type] = ThetaLocator
|
||||
|
||||
name: str
|
||||
use_sticky_edges: bool
|
||||
def __init__(
|
||||
self,
|
||||
*args,
|
||||
theta_offset: float = ...,
|
||||
theta_direction: float = ...,
|
||||
rlabel_position: float = ...,
|
||||
**kwargs,
|
||||
) -> None: ...
|
||||
def get_xaxis_transform(
|
||||
self, which: Literal["tick1", "tick2", "grid"] = ...
|
||||
) -> mtransforms.Transform: ...
|
||||
def get_xaxis_text1_transform(
|
||||
self, pad: float
|
||||
) -> tuple[
|
||||
mtransforms.Transform,
|
||||
Literal["center", "top", "bottom", "baseline", "center_baseline"],
|
||||
Literal["center", "left", "right"],
|
||||
]: ...
|
||||
def get_xaxis_text2_transform(
|
||||
self, pad: float
|
||||
) -> tuple[
|
||||
mtransforms.Transform,
|
||||
Literal["center", "top", "bottom", "baseline", "center_baseline"],
|
||||
Literal["center", "left", "right"],
|
||||
]: ...
|
||||
def get_yaxis_transform(
|
||||
self, which: Literal["tick1", "tick2", "grid"] = ...
|
||||
) -> mtransforms.Transform: ...
|
||||
def get_yaxis_text1_transform(
|
||||
self, pad: float
|
||||
) -> tuple[
|
||||
mtransforms.Transform,
|
||||
Literal["center", "top", "bottom", "baseline", "center_baseline"],
|
||||
Literal["center", "left", "right"],
|
||||
]: ...
|
||||
def get_yaxis_text2_transform(
|
||||
self, pad: float
|
||||
) -> tuple[
|
||||
mtransforms.Transform,
|
||||
Literal["center", "top", "bottom", "baseline", "center_baseline"],
|
||||
Literal["center", "left", "right"],
|
||||
]: ...
|
||||
def set_thetamax(self, thetamax: float) -> None: ...
|
||||
def get_thetamax(self) -> float: ...
|
||||
def set_thetamin(self, thetamin: float) -> None: ...
|
||||
def get_thetamin(self) -> float: ...
|
||||
@overload
|
||||
def set_thetalim(self, minval: float, maxval: float, /) -> tuple[float, float]: ...
|
||||
@overload
|
||||
def set_thetalim(self, *, thetamin: float, thetamax: float) -> tuple[float, float]: ...
|
||||
def set_theta_offset(self, offset: float) -> None: ...
|
||||
def get_theta_offset(self) -> float: ...
|
||||
def set_theta_zero_location(
|
||||
self,
|
||||
loc: Literal["N", "NW", "W", "SW", "S", "SE", "E", "NE"],
|
||||
offset: float = ...,
|
||||
) -> None: ...
|
||||
def set_theta_direction(
|
||||
self,
|
||||
direction: Literal[-1, 1, "clockwise", "counterclockwise", "anticlockwise"],
|
||||
) -> None: ...
|
||||
def get_theta_direction(self) -> Literal[-1, 1]: ...
|
||||
def set_rmax(self, rmax: float) -> None: ...
|
||||
def get_rmax(self) -> float: ...
|
||||
def set_rmin(self, rmin: float) -> None: ...
|
||||
def get_rmin(self) -> float: ...
|
||||
def set_rorigin(self, rorigin: float | None) -> None: ...
|
||||
def get_rorigin(self) -> float: ...
|
||||
def get_rsign(self) -> float: ...
|
||||
def set_rlim(
|
||||
self,
|
||||
bottom: float | tuple[float, float] | None = ...,
|
||||
top: float | None = ...,
|
||||
*,
|
||||
emit: bool = ...,
|
||||
auto: bool = ...,
|
||||
**kwargs,
|
||||
) -> tuple[float, float]: ...
|
||||
def get_rlabel_position(self) -> float: ...
|
||||
def set_rlabel_position(self, value: float) -> None: ...
|
||||
def set_rscale(self, *args, **kwargs) -> None: ...
|
||||
def set_rticks(self, *args, **kwargs) -> None: ...
|
||||
def set_thetagrids(
|
||||
self,
|
||||
angles: ArrayLike,
|
||||
labels: Sequence[str | Text] | None = ...,
|
||||
fmt: str | None = ...,
|
||||
**kwargs,
|
||||
) -> tuple[list[Line2D], list[Text]]: ...
|
||||
def set_rgrids(
|
||||
self,
|
||||
radii: ArrayLike,
|
||||
labels: Sequence[str | Text] | None = ...,
|
||||
angle: float | None = ...,
|
||||
fmt: str | None = ...,
|
||||
**kwargs,
|
||||
) -> tuple[list[Line2D], list[Text]]: ...
|
||||
def format_coord(self, theta: float, r: float) -> str: ...
|
||||
def get_data_ratio(self) -> float: ...
|
||||
def can_zoom(self) -> bool: ...
|
||||
def can_pan(self) -> bool: ...
|
||||
def start_pan(self, x: float, y: float, button: int) -> None: ...
|
||||
def end_pan(self) -> None: ...
|
||||
def drag_pan(self, button: Any, key: Any, x: float, y: float) -> None: ...
|
||||
Loading…
Add table
Add a link
Reference in a new issue