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

Reply via email to