Revision: 4820
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=4820&view=rev
Author: mdboom
Date: 2008-01-08 13:55:36 -0800 (Tue, 08 Jan 2008)
Log Message:
-----------
Adding projections directory
Added Paths:
-----------
trunk/matplotlib/lib/matplotlib/projections/
trunk/matplotlib/lib/matplotlib/projections/__init__.py
trunk/matplotlib/lib/matplotlib/projections/geo.py
trunk/matplotlib/lib/matplotlib/projections/polar.py
Added: trunk/matplotlib/lib/matplotlib/projections/__init__.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/projections/__init__.py
(rev 0)
+++ trunk/matplotlib/lib/matplotlib/projections/__init__.py 2008-01-08
21:55:36 UTC (rev 4820)
@@ -0,0 +1,47 @@
+from geo import AitoffAxes, HammerAxes, LambertAxes
+from polar import PolarAxes
+from matplotlib import axes
+
+class ProjectionRegistry(object):
+ def __init__(self):
+ self._all_projection_types = {}
+
+ def register(self, *projections):
+ for projection in projections:
+ name = projection.name
+ self._all_projection_types[name] = projection
+
+ def get_projection_class(self, name):
+ return self._all_projection_types[name]
+
+ def get_projection_names(self):
+ names = self._all_projection_types.keys()
+ names.sort()
+ return names
+projection_registry = ProjectionRegistry()
+
+projection_registry.register(
+ axes.Axes,
+ PolarAxes,
+ AitoffAxes,
+ HammerAxes,
+ LambertAxes)
+
+
+def register_projection(cls):
+ projection_registry.register(cls)
+
+def get_projection_class(projection):
+ if projection is None:
+ projection = 'rectilinear'
+
+ try:
+ return projection_registry.get_projection_class(projection)
+ except KeyError:
+ raise ValueError("Unknown projection '%s'" % projection)
+
+def projection_factory(projection, figure, rect, **kwargs):
+ return get_projection_class(projection)(figure, rect, **kwargs)
+
+def get_projection_names():
+ return projection_registry.get_projection_names()
Added: trunk/matplotlib/lib/matplotlib/projections/geo.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/projections/geo.py
(rev 0)
+++ trunk/matplotlib/lib/matplotlib/projections/geo.py 2008-01-08 21:55:36 UTC
(rev 4820)
@@ -0,0 +1,591 @@
+import math
+
+import numpy as npy
+from matplotlib.numerix import npyma as ma
+
+import matplotlib
+rcParams = matplotlib.rcParams
+from matplotlib.artist import kwdocd
+from matplotlib.axes import Axes
+from matplotlib import cbook
+from matplotlib.patches import Circle
+from matplotlib.path import Path
+from matplotlib.ticker import Formatter, Locator, NullLocator, FixedLocator,
NullFormatter
+from matplotlib.transforms import Affine2D, Affine2DBase, Bbox, \
+ BboxTransformTo, IdentityTransform, Transform, TransformWrapper
+
+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 = (x / npy.pi) * 180.0
+ degrees = round(degrees / self._round_to) * self._round_to
+ # \u00b0 : degree symbol
+ return u"%d\u00b0" % degrees
+
+ RESOLUTION = 75
+
+ def cla(self):
+ Axes.cla(self)
+
+ 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.grid(rcParams['axes.grid'])
+
+ Axes.set_xlim(self, -npy.pi, npy.pi)
+ Axes.set_ylim(self, -npy.pi / 2.0, npy.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.0, self._longitude_cap * 2.0) \
+ .translate(0.0, -self._longitude_cap)
+ self._xaxis_transform = \
+ self._xaxis_pretransform + \
+ self.transData
+ self._xaxis_text1_transform = \
+ Affine2D().scale(1.0, 0.0) + \
+ self.transData + \
+ Affine2D().translate(0.0, 4.0)
+ self._xaxis_text2_transform = \
+ Affine2D().scale(1.0, 0.0) + \
+ self.transData + \
+ Affine2D().translate(0.0, -4.0)
+
+ # This is the transform for latitude ticks.
+ yaxis_stretch = Affine2D().scale(npy.pi * 2.0, 1.0).translate(-npy.pi,
0.0)
+ yaxis_space = Affine2D().scale(1.0, 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, 0.0)
+ self._yaxis_text2_transform = \
+ yaxis_text_base + \
+ Affine2D().translate(8.0, 0.0)
+
+ def _get_affine_transform(self):
+ transform = self._get_core_transform(1)
+ xscale, _ = transform.transform_point((npy.pi, 0))
+ _, yscale = transform.transform_point((0, npy.pi / 2.0))
+ return Affine2D() \
+ .scale(0.5 / xscale, 0.5 / yscale) \
+ .translate(0.5, 0.5)
+
+ def update_layout(self, renderer):
+ t_text, b_text = self.xaxis.get_text_heights(renderer)
+ l_text, r_text = self.yaxis.get_text_widths(renderer)
+ originalPosition = self.get_position(True)
+ title_offset = (b_text - originalPosition.transformed(
+ self.figure.transFigure).height) / 2.0
+ self.titleOffsetTrans.clear().translate(0, title_offset)
+
+ def get_xaxis_transform(self):
+ return self._xaxis_transform
+
+ def get_xaxis_text1_transform(self, pixelPad):
+ return self._xaxis_text1_transform, 'bottom', 'center'
+
+ def get_xaxis_text2_transform(self, pixelPad):
+ return self._xaxis_text2_transform, 'top', 'center'
+
+ def get_yaxis_transform(self):
+ return self._yaxis_transform
+
+ def get_yaxis_text1_transform(self, pixelPad):
+ return self._yaxis_text1_transform, 'center', 'right'
+
+ def get_yaxis_text2_transform(self, pixelPad):
+ return self._yaxis_text2_transform, 'center', 'left'
+
+ def get_axes_patch(self):
+ return Circle((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):
+ Axes.set_xlim(self, -npy.pi, npy.pi)
+ Axes.set_ylim(self, -npy.pi / 2.0, npy.pi / 2.0)
+
+ set_ylim = set_xlim
+
+ def format_coord(self, long, lat):
+ 'return a format string formatting the coordinate'
+ long = long * (180.0 / npy.pi)
+ lat = lat * (180.0 / npy.pi)
+ if lat >= 0.0:
+ ns = 'N'
+ else:
+ ns = 'S'
+ if long >= 0.0:
+ ew = 'E'
+ else:
+ ew = 'W'
+ return u'%f\u00b0%s, %f\u00b0%s' % (abs(lat), ns, abs(long), ew)
+
+ def set_longitude_grid(self, degrees):
+ """
+ Set the number of degrees between each longitude grid.
+ """
+ number = (360.0 / degrees) + 1
+ self.xaxis.set_major_locator(
+ FixedLocator(
+ npy.linspace(-npy.pi, npy.pi, number, True)[1:-1]))
+ self._logitude_degrees = degrees
+ self.xaxis.set_major_formatter(self.ThetaFormatter(degrees))
+
+ def set_latitude_grid(self, degrees):
+ """
+ Set the number of degrees between each longitude grid.
+ """
+ number = (180.0 / degrees) + 1
+ self.yaxis.set_major_locator(
+ FixedLocator(
+ npy.linspace(-npy.pi / 2.0, npy.pi / 2.0, number, True)[1:-1]))
+ self._latitude_degrees = degrees
+ 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 = degrees * (npy.pi / 180.0)
+ 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 True if this axes support the zoom box
+ """
+ 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 AitoffAxes(GeoAxes):
+ name = 'aitoff'
+
+ class AitoffTransform(Transform):
+ """
+ The base Aitoff transform.
+ """
+ input_dims = 2
+ output_dims = 2
+ is_separable = False
+
+ def __init__(self, resolution):
+ """
+ Create a new Aitoff transform. Resolution is the number of steps
+ to interpolate between each input line segment to approximate its
+ path in curved Aitoff space.
+ """
+ Transform.__init__(self)
+ self._resolution = resolution
+
+ def transform(self, ll):
+ longitude = ll[:, 0:1]
+ latitude = ll[:, 1:2]
+
+ # Pre-compute some values
+ half_long = longitude / 2.0
+ cos_latitude = npy.cos(latitude)
+
+ alpha = npy.arccos(cos_latitude * npy.cos(half_long))
+ # Mask this array, or we'll get divide-by-zero errors
+ alpha = ma.masked_where(alpha == 0.0, alpha)
+ # We want unnormalized sinc. numpy.sinc gives us normalized
+ sinc_alpha = ma.sin(alpha) / alpha
+
+ x = (cos_latitude * npy.sin(half_long)) / sinc_alpha
+ y = (npy.sin(latitude) / sinc_alpha)
+ x.set_fill_value(0.0)
+ y.set_fill_value(0.0)
+ return npy.concatenate((x.filled(), y.filled()), 1)
+ transform.__doc__ = Transform.transform.__doc__
+
+ transform_non_affine = transform
+ transform_non_affine.__doc__ = Transform.transform_non_affine.__doc__
+
+ def transform_path(self, path):
+ vertices = path.vertices
+ ipath = path.interpolated(self._resolution)
+ return Path(self.transform(ipath.vertices), ipath.codes)
+ transform_path.__doc__ = Transform.transform_path.__doc__
+
+ transform_path_non_affine = transform_path
+ transform_path_non_affine.__doc__ =
Transform.transform_path_non_affine.__doc__
+
+ def inverted(self):
+ return AitoffAxes.InvertedAitoffTransform(self._resolution)
+ inverted.__doc__ = Transform.inverted.__doc__
+
+ class InvertedAitoffTransform(Transform):
+ input_dims = 2
+ output_dims = 2
+ is_separable = False
+
+ def __init__(self, resolution):
+ Transform.__init__(self)
+ self._resolution = resolution
+
+ def transform(self, xy):
+ # MGDTODO: Math is hard ;(
+ return xy
+ transform.__doc__ = Transform.transform.__doc__
+
+ def inverted(self):
+ return AitoffAxes.AitoffTransform(self._resolution)
+ inverted.__doc__ = Transform.inverted.__doc__
+
+ def __init__(self, *args, **kwargs):
+ self._longitude_cap = npy.pi / 2.0
+ GeoAxes.__init__(self, *args, **kwargs)
+ self.set_aspect(0.5, adjustable='box', anchor='C')
+ self.cla()
+
+ def _get_core_transform(self, resolution):
+ return self.AitoffTransform(resolution)
+
+
+class HammerAxes(GeoAxes):
+ name = 'hammer'
+
+ class HammerTransform(Transform):
+ """
+ The base Hammer transform.
+ """
+ input_dims = 2
+ output_dims = 2
+ is_separable = False
+
+ def __init__(self, resolution):
+ """
+ Create a new Hammer transform. Resolution is the number of steps
+ to interpolate between each input line segment to approximate its
+ path in curved Hammer space.
+ """
+ Transform.__init__(self)
+ self._resolution = resolution
+
+ def transform(self, ll):
+ longitude = ll[:, 0:1]
+ latitude = ll[:, 1:2]
+
+ # Pre-compute some values
+ half_long = longitude / 2.0
+ cos_latitude = npy.cos(latitude)
+ sqrt2 = npy.sqrt(2.0)
+
+ alpha = 1.0 + cos_latitude * npy.cos(half_long)
+ x = (2.0 * sqrt2) * (cos_latitude * npy.sin(half_long)) / alpha
+ y = (sqrt2 * npy.sin(latitude)) / alpha
+ return npy.concatenate((x, y), 1)
+ transform.__doc__ = Transform.transform.__doc__
+
+ transform_non_affine = transform
+ transform_non_affine.__doc__ = Transform.transform_non_affine.__doc__
+
+ def transform_path(self, path):
+ vertices = path.vertices
+ ipath = path.interpolated(self._resolution)
+ return Path(self.transform(ipath.vertices), ipath.codes)
+ transform_path.__doc__ = Transform.transform_path.__doc__
+
+ transform_path_non_affine = transform_path
+ transform_path_non_affine.__doc__ =
Transform.transform_path_non_affine.__doc__
+
+ def inverted(self):
+ return HammerAxes.InvertedHammerTransform(self._resolution)
+ inverted.__doc__ = Transform.inverted.__doc__
+
+ class InvertedHammerTransform(Transform):
+ input_dims = 2
+ output_dims = 2
+ is_separable = False
+
+ def __init__(self, resolution):
+ Transform.__init__(self)
+ self._resolution = resolution
+
+ def transform(self, xy):
+ x = xy[:, 0:1]
+ y = xy[:, 1:2]
+
+ quarter_x = 0.25 * x
+ half_y = 0.5 * y
+ z = npy.sqrt(1.0 - quarter_x*quarter_x - half_y*half_y)
+ longitude = 2 * npy.arctan((z*x) / (2.0 * (2.0*z*z - 1.0)))
+ latitude = npy.arcsin(y*z)
+ return npy.concatenate((longitude, latitude), 1)
+ transform.__doc__ = Transform.transform.__doc__
+
+ def inverted(self):
+ return HammerAxes.HammerTransform(self._resolution)
+ inverted.__doc__ = Transform.inverted.__doc__
+
+ def __init__(self, *args, **kwargs):
+ self._longitude_cap = npy.pi / 2.0
+ GeoAxes.__init__(self, *args, **kwargs)
+ self.set_aspect(0.5, adjustable='box', anchor='C')
+ self.cla()
+
+ def _get_core_transform(self, resolution):
+ return self.HammerTransform(resolution)
+
+
+class MollweideAxes(GeoAxes):
+ name = 'mollweide'
+
+ class MollweideTransform(Transform):
+ """
+ The base Mollweide transform.
+ """
+ input_dims = 2
+ output_dims = 2
+ is_separable = False
+
+ def __init__(self, resolution):
+ """
+ Create a new Mollweide transform. Resolution is the number of
steps
+ to interpolate between each input line segment to approximate its
+ path in curved Mollweide space.
+ """
+ Transform.__init__(self)
+ self._resolution = resolution
+
+ def transform(self, ll):
+ longitude = ll[:, 0:1]
+ latitude = ll[:, 1:2]
+
+ aux = 2.0 * npy.arcsin((2.0 * latitude) / npy.pi)
+ x = (2.0 * npy.sqrt(2.0) * longitude * npy.cos(aux)) / npy.pi
+ y = (npy.sqrt(2.0) * npy.sin(aux))
+
+ return npy.concatenate((x, y), 1)
+ transform.__doc__ = Transform.transform.__doc__
+
+ transform_non_affine = transform
+ transform_non_affine.__doc__ = Transform.transform_non_affine.__doc__
+
+ def transform_path(self, path):
+ vertices = path.vertices
+ ipath = path.interpolated(self._resolution)
+ return Path(self.transform(ipath.vertices), ipath.codes)
+ transform_path.__doc__ = Transform.transform_path.__doc__
+
+ transform_path_non_affine = transform_path
+ transform_path_non_affine.__doc__ =
Transform.transform_path_non_affine.__doc__
+
+ def inverted(self):
+ return MollweideAxes.InvertedMollweideTransform(self._resolution)
+ inverted.__doc__ = Transform.inverted.__doc__
+
+ class InvertedMollweideTransform(Transform):
+ input_dims = 2
+ output_dims = 2
+ is_separable = False
+
+ def __init__(self, resolution):
+ Transform.__init__(self)
+ self._resolution = resolution
+
+ def transform(self, xy):
+ # MGDTODO: Math is hard ;(
+ return xy
+ transform.__doc__ = Transform.transform.__doc__
+
+ def inverted(self):
+ return MollweideAxes.MollweideTransform(self._resolution)
+ inverted.__doc__ = Transform.inverted.__doc__
+
+ def __init__(self, *args, **kwargs):
+ self._longitude_cap = npy.pi / 2.0
+ GeoAxes.__init__(self, *args, **kwargs)
+ self.set_aspect(0.5, adjustable='box', anchor='C')
+ self.cla()
+
+ def _get_core_transform(self, resolution):
+ return self.MollweideTransform(resolution)
+
+
+class LambertAxes(GeoAxes):
+ name = 'lambert'
+
+ class LambertTransform(Transform):
+ """
+ The base Lambert transform.
+ """
+ input_dims = 2
+ output_dims = 2
+ is_separable = False
+
+ 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.
+ """
+ Transform.__init__(self)
+ self._resolution = resolution
+ self._center_longitude = center_longitude
+ self._center_latitude = center_latitude
+
+ def transform(self, ll):
+ longitude = ll[:, 0:1]
+ latitude = ll[:, 1:2]
+ clong = self._center_longitude
+ clat = self._center_latitude
+ cos_lat = npy.cos(latitude)
+ sin_lat = npy.sin(latitude)
+ diff_long = longitude - clong
+ cos_diff_long = npy.cos(diff_long)
+
+ inner_k = (1.0 +
+ npy.sin(clat)*sin_lat +
+ npy.cos(clat)*cos_lat*cos_diff_long)
+ # Prevent divide-by-zero problems
+ inner_k = npy.where(inner_k == 0.0, 1e-15, inner_k)
+ k = npy.sqrt(2.0 / inner_k)
+ x = k*cos_lat*npy.sin(diff_long)
+ y = k*(npy.cos(clat)*sin_lat -
+ npy.sin(clat)*cos_lat*cos_diff_long)
+
+ return npy.concatenate((x, y), 1)
+ transform.__doc__ = Transform.transform.__doc__
+
+ transform_non_affine = transform
+ transform_non_affine.__doc__ = Transform.transform_non_affine.__doc__
+
+ def transform_path(self, path):
+ vertices = path.vertices
+ ipath = path.interpolated(self._resolution)
+ return Path(self.transform(ipath.vertices), ipath.codes)
+ transform_path.__doc__ = Transform.transform_path.__doc__
+
+ transform_path_non_affine = transform_path
+ transform_path_non_affine.__doc__ =
Transform.transform_path_non_affine.__doc__
+
+ def inverted(self):
+ return LambertAxes.InvertedLambertTransform(
+ self._center_longitude,
+ self._center_latitude,
+ self._resolution)
+ inverted.__doc__ = Transform.inverted.__doc__
+
+ class InvertedLambertTransform(Transform):
+ input_dims = 2
+ output_dims = 2
+ is_separable = False
+
+ def __init__(self, center_longitude, center_latitude, resolution):
+ Transform.__init__(self)
+ self._resolution = resolution
+ self._center_longitude = center_longitude
+ self._center_latitude = center_latitude
+
+ def transform(self, xy):
+ x = xy[:, 0:1]
+ y = xy[:, 1:2]
+ clong = self._center_longitude
+ clat = self._center_latitude
+ p = npy.sqrt(x*x + y*y)
+ p = npy.where(p == 0.0, 1e-9, p)
+ c = 2.0 * npy.arcsin(0.5 * p)
+ sin_c = npy.sin(c)
+ cos_c = npy.cos(c)
+
+ lat = npy.arcsin(cos_c*npy.sin(clat) +
+ ((y*sin_c*npy.cos(clat)) / p))
+ long = clong + npy.arctan(
+ (x*sin_c) / (p*npy.cos(clat)*cos_c - y*npy.sin(clat)*sin_c))
+
+ return npy.concatenate((long, lat), 1)
+ transform.__doc__ = Transform.transform.__doc__
+
+ def inverted(self):
+ return LambertAxes.LambertTransform(
+ self._center_longitude,
+ self._center_latitude,
+ self._resolution)
+ inverted.__doc__ = Transform.inverted.__doc__
+
+ def __init__(self, *args, **kwargs):
+ self._longitude_cap = npy.pi / 2.0
+ self._center_longitude = kwargs.pop("center_longitude", 0.0)
+ self._center_latitude = kwargs.pop("center_latitude", 0.0)
+ GeoAxes.__init__(self, *args, **kwargs)
+ self.set_aspect('equal', adjustable='box', anchor='C')
+ self.cla()
+
+ def cla(self):
+ GeoAxes.cla(self)
+ 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)
Added: trunk/matplotlib/lib/matplotlib/projections/polar.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/projections/polar.py
(rev 0)
+++ trunk/matplotlib/lib/matplotlib/projections/polar.py 2008-01-08
21:55:36 UTC (rev 4820)
@@ -0,0 +1,569 @@
+import math
+
+import numpy as npy
+
+import matplotlib
+rcParams = matplotlib.rcParams
+from matplotlib.artist import kwdocd
+from matplotlib.axes import Axes
+from matplotlib import cbook
+from matplotlib.patches import Circle
+from matplotlib.path import Path
+from matplotlib.ticker import Formatter, Locator
+from matplotlib.transforms import Affine2D, Affine2DBase, Bbox, \
+ BboxTransformTo, IdentityTransform, Transform, TransformWrapper
+
+class PolarAxes(Axes):
+ """
+ A polar graph projection, where the input dimensions are theta, r.
+
+ Theta starts pointing east and goes anti-clockwise.
+ """
+ name = 'polar'
+
+ class PolarTransform(Transform):
+ """
+ The base polar transform. This handles projection theta and r into
+ Cartesian coordinate space, but does not perform the ultimate affine
+ transformation into the correct position.
+ """
+ input_dims = 2
+ output_dims = 2
+ is_separable = False
+
+ def __init__(self, resolution):
+ """
+ Create a new polar transform. Resolution is the number of steps
+ to interpolate between each input line segment to approximate its
+ path in curved polar space.
+ """
+ Transform.__init__(self)
+ self._resolution = resolution
+
+ def transform(self, tr):
+ xy = npy.zeros(tr.shape, npy.float_)
+ t = tr[:, 0:1]
+ r = tr[:, 1:2]
+ x = xy[:, 0:1]
+ y = xy[:, 1:2]
+ x[:] = r * npy.cos(t)
+ y[:] = r * npy.sin(t)
+ return xy
+ transform.__doc__ = Transform.transform.__doc__
+
+ transform_non_affine = transform
+ transform_non_affine.__doc__ = Transform.transform_non_affine.__doc__
+
+ def transform_path(self, path):
+ vertices = path.vertices
+ if len(vertices) == 2 and vertices[0, 0] == vertices[1, 0]:
+ return Path(self.transform(vertices), path.codes)
+ ipath = path.interpolated(self._resolution)
+ return Path(self.transform(ipath.vertices), ipath.codes)
+ transform_path.__doc__ = Transform.transform_path.__doc__
+
+ transform_path_non_affine = transform_path
+ transform_path_non_affine.__doc__ =
Transform.transform_path_non_affine.__doc__
+
+ def inverted(self):
+ return PolarAxes.InvertedPolarTransform(self._resolution)
+ inverted.__doc__ = Transform.inverted.__doc__
+
+ class PolarAffine(Affine2DBase):
+ """
+ The affine part of the polar projection. Scales the output so
+ that maximum radius rests on the edge of the axes circle.
+ """
+ def __init__(self, scale_transform, limits):
+ """
+ limits is the view limit of the data. The only part of
+ its bounds that is used is ymax (for the radius maximum).
+ """
+ Affine2DBase.__init__(self)
+ self._scale_transform = scale_transform
+ self._limits = limits
+ self.set_children(scale_transform, limits)
+ self._mtx = None
+
+ def get_matrix(self):
+ if self._invalid:
+ limits_scaled = self._limits.transformed(self._scale_transform)
+ ymax = limits_scaled.ymax
+ affine = Affine2D() \
+ .scale(0.5 / ymax) \
+ .translate(0.5, 0.5)
+ self._mtx = affine.get_matrix()
+ self._inverted = None
+ self._invalid = 0
+ return self._mtx
+ get_matrix.__doc__ = Affine2DBase.get_matrix.__doc__
+
+ class InvertedPolarTransform(Transform):
+ """
+ The inverse of the polar transform, mapping Cartesian
+ coordinate space back to t and r.
+ """
+ input_dims = 2
+ output_dims = 2
+ is_separable = False
+
+ def __init__(self, resolution):
+ Transform.__init__(self)
+ self._resolution = resolution
+
+ def transform(self, xy):
+ x = xy[:, 0:1]
+ y = xy[:, 1:]
+ r = npy.sqrt(x*x + y*y)
+ theta = npy.arccos(x / r)
+ theta = npy.where(y < 0, 2 * npy.pi - theta, theta)
+ return npy.concatenate((theta, r), 1)
+ transform.__doc__ = Transform.transform.__doc__
+
+ def inverted(self):
+ return PolarAxes.PolarTransform(self._resolution)
+ inverted.__doc__ = Transform.inverted.__doc__
+
+ class ThetaFormatter(Formatter):
+ """
+ Used to format the theta tick labels. Converts the native
+ unit of radians into degrees and adds a degree symbol.
+ """
+ def __call__(self, x, pos=None):
+ # \u00b0 : degree symbol
+ return u"%d\u00b0" % ((x / npy.pi) * 180.0)
+
+ class RadialLocator(Locator):
+ """
+ Used to locate radius ticks.
+
+ Ensures that all ticks are strictly positive. For all other
+ tasks, it delegates to the base Locator (which may be
+ different depending on the scale of the r-axis.
+ """
+ def __init__(self, base):
+ self.base = base
+
+ def __call__(self):
+ ticks = self.base()
+ return [x for x in ticks if x > 0]
+
+ def autoscale(self):
+ return self.base.autoscale()
+
+ def pan(self, numsteps):
+ return self.base.pan(numsteps)
+
+ def zoom(self, direction):
+ return self.base.zoom(direction)
+
+ def refresh(self):
+ return self.base.refresh()
+
+ RESOLUTION = 75
+
+ def __init__(self, *args, **kwargs):
+ """
+ Create a new Polar Axes for a polar plot.
+ """
+
+ self._rpad = 0.05
+ Axes.__init__(self, *args, **kwargs)
+ self.set_aspect('equal', adjustable='box', anchor='C')
+ self.cla()
+ __init__.__doc__ = Axes.__init__.__doc__
+
+ def cla(self):
+ Axes.cla(self)
+
+ self.xaxis.set_major_formatter(self.ThetaFormatter())
+ angles = npy.arange(0.0, 360.0, 45.0)
+ self.set_thetagrids(angles)
+
self.yaxis.set_major_locator(self.RadialLocator(self.yaxis.get_major_locator()))
+
+ self.grid(rcParams['polaraxes.grid'])
+ self.xaxis.set_ticks_position('none')
+ self.yaxis.set_ticks_position('none')
+
+ def _set_lim_and_transforms(self):
+ self.transAxes = BboxTransformTo(self.bbox)
+
+ # Transforms the x and y axis separately by a scale factor
+ # It is assumed that this part will have non-linear components
+ self.transScale = TransformWrapper(IdentityTransform())
+
+ # A (possibly non-linear) projection on the (already scaled) data
+ self.transProjection = self.PolarTransform(self.RESOLUTION)
+
+ # An affine transformation on the data, generally to limit the
+ # range of the axes
+ self.transProjectionAffine = self.PolarAffine(self.transScale,
self.viewLim)
+
+ # The complete data transformation stack -- from data all the
+ # way to display coordinates
+ self.transData = self.transScale + self.transProjection + \
+ (self.transProjectionAffine + self.transAxes)
+
+ # This is the transform for theta-axis ticks. It is
+ # equivalent to transData, except it always puts r == 1.0 at
+ # the edge of the axis circle.
+ self._xaxis_transform = (
+ self.transProjection +
+ self.PolarAffine(IdentityTransform(), Bbox.unit()) +
+ self.transAxes)
+ # The theta labels are moved from radius == 0.0 to radius == 1.1
+ self._theta_label1_position = Affine2D().translate(0.0, 1.1)
+ self._xaxis_text1_transform = (
+ self._theta_label1_position +
+ self._xaxis_transform)
+ self._theta_label2_position = Affine2D().translate(0.0, 1.0 / 1.1)
+ self._xaxis_text2_transform = (
+ self._theta_label2_position +
+ self._xaxis_transform)
+
+ # This is the transform for r-axis ticks. It scales the theta
+ # axis so the gridlines from 0.0 to 1.0, now go from 0.0 to
+ # 2pi.
+ self._yaxis_transform = (
+ Affine2D().scale(npy.pi * 2.0, 1.0) +
+ self.transData)
+ # The r-axis labels are put at an angle and padded in the r-direction
+ self._r_label1_position = Affine2D().translate(22.5, self._rpad)
+ self._yaxis_text1_transform = (
+ self._r_label1_position +
+ Affine2D().scale(1.0 / 360.0, 1.0) +
+ self._yaxis_transform
+ )
+ self._r_label2_position = Affine2D().translate(22.5, self._rpad)
+ self._yaxis_text2_transform = (
+ self._r_label2_position +
+ Affine2D().scale(1.0 / 360.0, 1.0) +
+ self._yaxis_transform
+ )
+
+ def update_layout(self, renderer):
+ t_text, b_text = self.xaxis.get_text_heights(renderer)
+ l_text, r_text = self.yaxis.get_text_widths(renderer)
+ originalPosition = self.get_position(True)
+ title_offset = (b_text - originalPosition.transformed(
+ self.figure.transFigure).height) / 2.0
+ self.titleOffsetTrans.clear().translate(0, title_offset)
+
+ def get_xaxis_transform(self):
+ return self._xaxis_transform
+
+ def get_xaxis_text1_transform(self, pixelPad):
+ return self._xaxis_text1_transform, 'center', 'center'
+
+ def get_xaxis_text2_transform(self, pixelPad):
+ return self._xaxis_text2_transform, 'center', 'center'
+
+ def get_yaxis_transform(self):
+ return self._yaxis_transform
+
+ def get_yaxis_text1_transform(self, pixelPad):
+ return self._yaxis_text1_transform, 'center', 'center'
+
+ def get_yaxis_text2_transform(self, pixelPad):
+ return self._yaxis_text2_transform, 'center', 'center'
+
+ def get_axes_patch(self):
+ return Circle((0.5, 0.5), 0.5)
+
+ def set_rmax(self, rmax):
+ self.viewLim.y1 = rmax
+ angle = self._r_label1_position.to_values()[4]
+ self._r_label1_position.clear().translate(
+ angle, rmax * self._rpad)
+ self._r_label2_position.clear().translate(
+ angle, -rmax * self._rpad)
+
+ def get_rmax(self):
+ return self.viewLim.ymax
+
+ def set_yscale(self, *args, **kwargs):
+ Axes.set_yscale(self, *args, **kwargs)
+ self.yaxis.set_major_locator(
+ self.RadialLocator(self.yaxis.get_major_locator()))
+
+ set_rscale = Axes.set_yscale
+ set_rticks = Axes.set_yticks
+
+ def set_thetagrids(self, angles, labels=None, frac=None,
+ **kwargs):
+ """
+ Set the angles at which to place the theta grids (these
+ gridlines are equal along the theta dimension). angles is in
+ degrees
+
+ labels, if not None, is a len(angles) list of strings of the
+ labels to use at each angle.
+
+ if labels is None, the labels with be fmt%%angle
+
+ frac is the fraction of the polar axes radius at which to
+ place the label (1 is the edge).Eg 1.05 isd outside the axes
+ and 0.95 is inside the axes
+
+ Return value is a list of lines, labels where the lines are
+ lines.Line2D instances and the labels are Text
+ instances:
+
+ kwargs are optional text properties for the labels
+ %(Text)s
+ ACCEPTS: sequence of floats
+ """
+ angles = npy.asarray(angles, npy.float_)
+ self.set_xticks(angles * (npy.pi / 180.0))
+ if labels is not None:
+ self.set_xticklabels(labels)
+ if frac is not None:
+ self._theta_label1_position.clear().translate(0.0, frac)
+ self._theta_label2_position.clear().translate(0.0, 1.0 / frac)
+ for t in self.xaxis.get_ticklabels():
+ t.update(kwargs)
+ set_thetagrids.__doc__ = cbook.dedent(set_thetagrids.__doc__) % kwdocd
+
+ def set_rgrids(self, radii, labels=None, angle=None, rpad=None, **kwargs):
+ """
+ set the radial locations and labels of the r grids
+
+ The labels will appear at radial distances radii at angle
+
+ labels, if not None, is a len(radii) list of strings of the
+ labels to use at each angle.
+
+ if labels is None, the self.rformatter will be used
+
+ rpad is a fraction of the max of radii which will pad each of
+ the radial labels in the radial direction.
+
+ Return value is a list of lines, labels where the lines are
+ lines.Line2D instances and the labels are text.Text
+ instances
+
+ kwargs control the rgrid Text label properties:
+ %(Text)s
+
+ ACCEPTS: sequence of floats
+ """
+ radii = npy.asarray(radii)
+ rmin = radii.min()
+ if rmin <= 0:
+ raise ValueError('radial grids must be strictly positive')
+
+ self.set_yticks(radii)
+ if labels is not None:
+ self.set_yticklabels(labels)
+ if angle is None:
+ angle = self._r_label1_position.to_values()[4]
+ if rpad is not None:
+ self._rpad = rpad
+ rmax = self.get_rmax()
+ self._r_label1_position.clear().translate(angle, self._rpad * rmax)
+ self._r_label2_position.clear().translate(angle, -self._rpad * rmax)
+ for t in self.yaxis.get_ticklabels():
+ t.update(kwargs)
+
+ set_rgrids.__doc__ = cbook.dedent(set_rgrids.__doc__) % kwdocd
+
+ def set_xscale(self, scale, *args, **kwargs):
+ if scale != 'linear':
+ raise NotImplementedError("You can not set the xscale on a polar
plot.")
+
+ def set_xlim(self, *args, **kargs):
+ # The xlim is fixed, no matter what you do
+ self.viewLim.intervalx = (0.0, npy.pi * 2.0)
+
+ def format_coord(self, theta, r):
+ 'return a format string formatting the coordinate'
+ theta /= math.pi
+ # \u03b8: lower-case theta
+ # \u03c0: lower-case pi
+ # \u00b0: degree symbol
+ return u'\u03b8=%0.3f\u03c0 (%0.3f\u00b0), r=%0.3f' % (theta, theta *
180.0, r)
+
+ def get_data_ratio(self):
+ '''
+ Return the aspect ratio of the data itself. For a polar plot,
+ this should always be 1.0
+ '''
+ return 1.0
+
+ ### Interactive panning
+
+ def can_zoom(self):
+ """
+ Return True if this axes support the zoom box
+ """
+ return False
+
+ def start_pan(self, x, y, button):
+ angle = self._r_label1_position.to_values()[4] / 180.0 * npy.pi
+ mode = ''
+ if button == 1:
+ epsilon = npy.pi / 45.0
+ t, r = self.transData.inverted().transform_point((x, y))
+ if t >= angle - epsilon and t <= angle + epsilon:
+ mode = 'drag_r_labels'
+ elif button == 3:
+ mode = 'zoom'
+
+ self._pan_start = cbook.Bunch(
+ rmax = self.get_rmax(),
+ trans = self.transData.frozen(),
+ trans_inverse = self.transData.inverted().frozen(),
+ r_label_angle = self._r_label1_position.to_values()[4],
+ x = x,
+ y = y,
+ mode = mode
+ )
+
+ def end_pan(self):
+ del self._pan_start
+
+ def drag_pan(self, button, key, x, y):
+ p = self._pan_start
+
+ if p.mode == 'drag_r_labels':
+ startt, startr = p.trans_inverse.transform_point((p.x, p.y))
+ t, r = p.trans_inverse.transform_point((x, y))
+
+ # Deal with theta
+ dt0 = t - startt
+ dt1 = startt - t
+ if abs(dt1) < abs(dt0):
+ dt = abs(dt1) * sign(dt0) * -1.0
+ else:
+ dt = dt0 * -1.0
+ dt = (dt / npy.pi) * 180.0
+
+ rpad = self._r_label1_position.to_values()[5]
+ self._r_label1_position.clear().translate(
+ p.r_label_angle - dt, rpad)
+ self._r_label2_position.clear().translate(
+ p.r_label_angle - dt, -rpad)
+
+ elif p.mode == 'zoom':
+ startt, startr = p.trans_inverse.transform_point((p.x, p.y))
+ t, r = p.trans_inverse.transform_point((x, y))
+
+ dr = r - startr
+
+ # Deal with r
+ scale = r / startr
+ self.set_rmax(p.rmax / scale)
+
+# These are a couple of aborted attempts to project a polar plot using
+# cubic bezier curves.
+
+# def transform_path(self, path):
+# twopi = 2.0 * npy.pi
+# halfpi = 0.5 * npy.pi
+
+# vertices = path.vertices
+# t0 = vertices[0:-1, 0]
+# t1 = vertices[1: , 0]
+# td = npy.where(t1 > t0, t1 - t0, twopi - (t0 - t1))
+# maxtd = td.max()
+# interpolate = npy.ceil(maxtd / halfpi)
+# if interpolate > 1.0:
+# vertices = self.interpolate(vertices, interpolate)
+
+# vertices = self.transform(vertices)
+
+# result = npy.zeros((len(vertices) * 3 - 2, 2), npy.float_)
+# codes = mpath.Path.CURVE4 * npy.ones((len(vertices) * 3 - 2, ),
mpath.Path.code_type)
+# result[0] = vertices[0]
+# codes[0] = mpath.Path.MOVETO
+
+# kappa = 4.0 * ((npy.sqrt(2.0) - 1.0) / 3.0)
+# kappa = 0.5
+
+# p0 = vertices[0:-1]
+# p1 = vertices[1: ]
+
+# x0 = p0[:, 0:1]
+# y0 = p0[:, 1: ]
+# b0 = ((y0 - x0) - y0) / ((x0 + y0) - x0)
+# a0 = y0 - b0*x0
+
+# x1 = p1[:, 0:1]
+# y1 = p1[:, 1: ]
+# b1 = ((y1 - x1) - y1) / ((x1 + y1) - x1)
+# a1 = y1 - b1*x1
+
+# x = -(a0-a1) / (b0-b1)
+# y = a0 + b0*x
+
+# xk = (x - x0) * kappa + x0
+# yk = (y - y0) * kappa + y0
+
+# result[1::3, 0:1] = xk
+# result[1::3, 1: ] = yk
+
+# xk = (x - x1) * kappa + x1
+# yk = (y - y1) * kappa + y1
+
+# result[2::3, 0:1] = xk
+# result[2::3, 1: ] = yk
+
+# result[3::3] = p1
+
+# print vertices[-2:]
+# print result[-2:]
+
+# return mpath.Path(result, codes)
+
+# twopi = 2.0 * npy.pi
+# halfpi = 0.5 * npy.pi
+
+# vertices = path.vertices
+# t0 = vertices[0:-1, 0]
+# t1 = vertices[1: , 0]
+# td = npy.where(t1 > t0, t1 - t0, twopi - (t0 - t1))
+# maxtd = td.max()
+# interpolate = npy.ceil(maxtd / halfpi)
+
+# print "interpolate", interpolate
+# if interpolate > 1.0:
+# vertices = self.interpolate(vertices, interpolate)
+
+# result = npy.zeros((len(vertices) * 3 - 2, 2), npy.float_)
+# codes = mpath.Path.CURVE4 * npy.ones((len(vertices) * 3 - 2, ),
mpath.Path.code_type)
+# result[0] = vertices[0]
+# codes[0] = mpath.Path.MOVETO
+
+# kappa = 4.0 * ((npy.sqrt(2.0) - 1.0) / 3.0)
+# tkappa = npy.arctan(kappa)
+# hyp_kappa = npy.sqrt(kappa*kappa + 1.0)
+
+# t0 = vertices[0:-1, 0]
+# t1 = vertices[1: , 0]
+# r0 = vertices[0:-1, 1]
+# r1 = vertices[1: , 1]
+
+# td = npy.where(t1 > t0, t1 - t0, twopi - (t0 - t1))
+# td_scaled = td / (npy.pi * 0.5)
+# rd = r1 - r0
+# r0kappa = r0 * kappa * td_scaled
+# r1kappa = r1 * kappa * td_scaled
+# ravg_kappa = ((r1 + r0) / 2.0) * kappa * td_scaled
+
+# result[1::3, 0] = t0 + (tkappa * td_scaled)
+# result[1::3, 1] = r0*hyp_kappa
+# # result[1::3, 1] = r0 / npy.cos(tkappa * td_scaled) #
npy.sqrt(r0*r0 + ravg_kappa*ravg_kappa)
+
+# result[2::3, 0] = t1 - (tkappa * td_scaled)
+# result[2::3, 1] = r1*hyp_kappa
+# # result[2::3, 1] = r1 / npy.cos(tkappa * td_scaled) #
npy.sqrt(r1*r1 + ravg_kappa*ravg_kappa)
+
+# result[3::3, 0] = t1
+# result[3::3, 1] = r1
+
+# print vertices[:6], result[:6], t0[:6], t1[:6], td[:6],
td_scaled[:6], tkappa
+# result = self.transform(result)
+# return mpath.Path(result, codes)
+# transform_path_non_affine = transform_path
+
+
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
-------------------------------------------------------------------------
Check out the new SourceForge.net Marketplace.
It's the best place to buy or sell services for
just about anything Open Source.
http://ad.doubleclick.net/clk;164216239;13503038;w?http://sf.net/marketplace
_______________________________________________
Matplotlib-checkins mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins