Revision: 5230
          http://matplotlib.svn.sourceforge.net/matplotlib/?rev=5230&view=rev
Author:   mdboom
Date:     2008-05-23 10:41:36 -0700 (Fri, 23 May 2008)

Log Message:
-----------
Move examples from pylab to api.

Added Paths:
-----------
    trunk/matplotlib/examples/api/custom_projection_example.py
    trunk/matplotlib/examples/api/custom_scale_example.py

Removed Paths:
-------------
    trunk/matplotlib/examples/pylab/custom_projection_example.py
    trunk/matplotlib/examples/pylab/custom_scale_example.py

Copied: trunk/matplotlib/examples/api/custom_projection_example.py (from rev 
5226, trunk/matplotlib/examples/pylab/custom_projection_example.py)
===================================================================
--- trunk/matplotlib/examples/api/custom_projection_example.py                  
        (rev 0)
+++ trunk/matplotlib/examples/api/custom_projection_example.py  2008-05-23 
17:41:36 UTC (rev 5230)
@@ -0,0 +1,475 @@
+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
+from matplotlib.projections import register_projection
+
+import numpy as np
+
+# This example projection class is rather long, but it is designed to
+# illustrate many features, not all of which will be used every time.
+# It is also common to factor out a lot of these methods into common
+# code used by a number of projections with similar characteristics
+# (see geo.py).
+
+class HammerAxes(Axes):
+    """
+    A custom class for the Aitoff-Hammer projection, an equal-area map
+    projection.
+
+    http://en.wikipedia.org/wiki/Hammer_projection
+    """
+    # The projection must specify a name.  This will be used be the
+    # user to select the projection, i.e. ``subplot(111,
+    # projection='hammer')``.
+    name = 'hammer'
+
+    # The number of interpolation steps when converting from straight
+    # lines to curves.  (See ``transform_path``).
+    RESOLUTION = 75
+
+    def __init__(self, *args, **kwargs):
+        Axes.__init__(self, *args, **kwargs)
+        self.set_aspect(0.5, adjustable='box', anchor='C')
+        self.cla()
+
+    def cla(self):
+        """
+        Override to set up some reasonable defaults.
+        """
+        # Don't forget to call the base class
+        Axes.cla(self)
+
+        # Set up a default grid spacing
+        self.set_longitude_grid(30)
+        self.set_latitude_grid(15)
+        self.set_longitude_grid_ends(75)
+
+        # Turn off minor ticking altogether
+        self.xaxis.set_minor_locator(NullLocator())
+        self.yaxis.set_minor_locator(NullLocator())
+
+        # Do not display ticks -- we only want gridlines and text
+        self.xaxis.set_ticks_position('none')
+        self.yaxis.set_ticks_position('none')
+
+        # The limits on this projection are fixed -- they are not to
+        # be changed by the user.  This makes the math in the
+        # transformation itself easier, and since this is a toy
+        # example, the easier, the better.
+        Axes.set_xlim(self, -np.pi, np.pi)
+        Axes.set_ylim(self, -np.pi / 2.0, np.pi / 2.0)
+
+    def cla(self):
+        """
+        Initialize the Axes object to reasonable defaults.
+        """
+        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, -np.pi, np.pi)
+        Axes.set_ylim(self, -np.pi / 2.0, np.pi / 2.0)
+
+    def _set_lim_and_transforms(self):
+        """
+        This is called once when the plot is created to set up all the
+        transforms for the data, text and grids.
+        """
+        # There are three important coordinate spaces going on here:
+        #
+        #    1. Data space: The space of the data itself
+        #
+        #    2. Axes space: The unit rectangle (0, 0) to (1, 1)
+        #       covering the entire plot area.
+        #
+        #    3. Display space: The coordinates of the resulting image,
+        #       often in pixels or dpi/inch.
+
+        # This function makes heavy use of the Transform classes in
+        # ``lib/matplotlib/transforms.py.`` For more information, see
+        # the inline documentation there.
+
+        # The goal of the first two transformations is to get from the
+        # data space (in this case longitude and latitude) to axes
+        # space.  It is separated into a non-affine and affine part so
+        # that the non-affine part does not have to be recomputed when
+        # a simple affine change to the figure has been made (such as
+        # resizing the window or changing the dpi).
+
+        # 1) The core transformation from data space into
+        # rectilinear space defined in the HammerTransform class.
+        self.transProjection = self.HammerTransform(self.RESOLUTION)
+
+        # 2) The above has an output range that is not in the unit
+        # rectangle, so scale and translate it so it fits correctly
+        # within the axes.  The peculiar calculations of xscale and
+        # yscale are specific to a Aitoff-Hammer projection, so don't
+        # worry about them too much.
+        xscale = 2.0 * np.sqrt(2.0) * np.sin(0.5 * np.pi)
+        yscale = np.sqrt(2.0) * np.sin(0.5 * np.pi)
+        self.transAffine = Affine2D() \
+            .scale(0.5 / xscale, 0.5 / yscale) \
+            .translate(0.5, 0.5)
+
+        # 3) This is the transformation from axes space to display
+        # space.
+        self.transAxes = BboxTransformTo(self.bbox)
+
+        # Now put these 3 transforms together -- from data all the way
+        # to display coordinates.  Using the '+' operator, these
+        # transforms will be applied "in order".  The transforms are
+        # automatically simplified, if possible, by the underlying
+        # transformation framework.
+        self.transData = \
+            self.transProjection + \
+            self.transAffine + \
+            self.transAxes
+
+        # The main data transformation is set up.  Now deal with
+        # gridlines and tick labels.
+
+        # Longitude gridlines and ticklabels.  The input to these
+        # transforms are in display space in x and axes space in y.
+        # Therefore, the input values will be in range (-xmin, 0),
+        # (xmax, 1).  The goal of these transforms is to go from that
+        # space to display space.  The tick labels will be offset 4
+        # pixels from the equator.
+        self._xaxis_pretransform = \
+            Affine2D() \
+            .scale(1.0, np.pi) \
+            .translate(0.0, -np.pi)
+        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)
+
+        # Now set up the transforms for the latitude ticks.  The input to
+        # these transforms are in axes space in x and display space in
+        # y.  Therefore, the input values will be in range (0, -ymin),
+        # (1, ymax).  The goal of these transforms is to go from that
+        # space to display space.  The tick labels will be offset 4
+        # pixels from the edge of the axes ellipse.
+        yaxis_stretch = Affine2D().scale(np.pi * 2.0, 1.0).translate(-np.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_xaxis_transform(self):
+        """
+        Override this method to provide a transformation for the
+        x-axis grid and ticks.
+        """
+        return self._xaxis_transform
+
+    def get_xaxis_text1_transform(self, pixelPad):
+        """
+        Override this method to provide a transformation for the
+        x-axis tick labels.
+
+        Returns a tuple of the form (transform, valign, halign)
+        """
+        return self._xaxis_text1_transform, 'bottom', 'center'
+
+    def get_xaxis_text2_transform(self, pixelPad):
+        """
+        Override this method to provide a transformation for the
+        secondary x-axis tick labels.
+
+        Returns a tuple of the form (transform, valign, halign)
+        """
+        return self._xaxis_text2_transform, 'top', 'center'
+
+    def get_yaxis_transform(self):
+        """
+        Override this method to provide a transformation for the
+        y-axis grid and ticks.
+        """
+        return self._yaxis_transform
+
+    def get_yaxis_text1_transform(self, pixelPad):
+        """
+        Override this method to provide a transformation for the
+        y-axis tick labels.
+
+        Returns a tuple of the form (transform, valign, halign)
+        """
+        return self._yaxis_text1_transform, 'center', 'right'
+
+    def get_yaxis_text2_transform(self, pixelPad):
+        """
+        Override this method to provide a transformation for the
+        secondary y-axis tick labels.
+
+        Returns a tuple of the form (transform, valign, halign)
+        """
+        return self._yaxis_text2_transform, 'center', 'left'
+
+    def get_axes_patch(self):
+        """
+        Override this method to define the shape that is used for the
+        background of the plot.  It should be a subclass of Patch.
+
+        In this case, it is a Circle (that may be warped by the axes
+        transform into an ellipse).  Any data and gridlines will be
+        clipped to this shape.
+        """
+        return Circle((0.5, 0.5), 0.5)
+
+    # Prevent the user from applying scales to one or both of the
+    # axes.  In this particular case, scaling the axes wouldn't make
+    # sense, so we don't allow it.
+    def set_xscale(self, *args, **kwargs):
+        if args[0] != 'linear':
+            raise NotImplementedError
+        Axes.set_xscale(self, *args, **kwargs)
+
+    def set_yscale(self, *args, **kwargs):
+        if args[0] != 'linear':
+            raise NotImplementedError
+        Axes.set_yscale(self, *args, **kwargs)
+
+    # Prevent the user from changing the axes limits.  In our case, we
+    # want to display the whole sphere all the time, so we override
+    # set_xlim and set_ylim to ignore any input.  This also applies to
+    # interactive panning and zooming in the GUI interfaces.
+    def set_xlim(self, *args, **kwargs):
+        Axes.set_xlim(self, -np.pi, np.pi)
+        Axes.set_ylim(self, -np.pi / 2.0, np.pi / 2.0)
+    set_ylim = set_xlim
+
+    def format_coord(self, long, lat):
+        """
+        Override this method to change how the values are displayed in
+        the status bar.
+
+        In this case, we want them to be displayed in degrees N/S/E/W.
+        """
+        long = long * (180.0 / np.pi)
+        lat = lat * (180.0 / np.pi)
+        if lat >= 0.0:
+            ns = 'N'
+        else:
+            ns = 'S'
+        if long >= 0.0:
+            ew = 'E'
+        else:
+            ew = 'W'
+        # \u00b0 : degree symbol
+        return u'%f\u00b0%s, %f\u00b0%s' % (abs(lat), ns, abs(long), ew)
+
+    class DegreeFormatter(Formatter):
+        """
+        This is a custom formatter that converts the native unit of
+        radians into (truncated) 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 / np.pi) * 180.0
+            degrees = round(degrees / self._round_to) * self._round_to
+            # \u00b0 : degree symbol
+            return u"%d\u00b0" % degrees
+
+    def set_longitude_grid(self, degrees):
+        """
+        Set the number of degrees between each longitude grid.
+
+        This is an example method that is specific to this projection
+        class -- it provides a more convenient interface to set the
+        ticking than set_xticks would.
+        """
+        # Set up a FixedLocator at each of the points, evenly spaced
+        # by degrees.
+        number = (360.0 / degrees) + 1
+        self.xaxis.set_major_locator(
+            FixedLocator(
+                np.linspace(-np.pi, np.pi, number, True)[1:-1]))
+        # Set the formatter to display the tick labels in degrees,
+        # rather than radians.
+        self.xaxis.set_major_formatter(self.DegreeFormatter(degrees))
+
+    def set_latitude_grid(self, degrees):
+        """
+        Set the number of degrees between each longitude grid.
+
+        This is an example method that is specific to this projection
+        class -- it provides a more convenient interface than
+        set_yticks would.
+        """
+        # Set up a FixedLocator at each of the points, evenly spaced
+        # by degrees.
+        number = (180.0 / degrees) + 1
+        self.yaxis.set_major_locator(
+            FixedLocator(
+                np.linspace(-np.pi / 2.0, np.pi / 2.0, number, True)[1:-1]))
+        # Set the formatter to display the tick labels in degrees,
+        # rather than radians.
+        self.yaxis.set_major_formatter(self.DegreeFormatter(degrees))
+
+    def set_longitude_grid_ends(self, degrees):
+        """
+        Set the latitude(s) at which to stop drawing the longitude grids.
+
+        Often, in geographic projections, you wouldn't want to draw
+        longitude gridlines near the poles.  This allows the user to
+        specify the degree at which to stop drawing longitude grids.
+
+        This is an example method that is specific to this projection
+        class -- it provides an interface to something that has no
+        analogy in the base Axes class.
+        """
+        longitude_cap = degrees * (np.pi / 180.0)
+        # Change the xaxis gridlines transform so that it draws from
+        # -degrees to degrees, rather than -pi to pi.
+        self._xaxis_pretransform \
+            .clear() \
+            .scale(1.0, longitude_cap * 2.0) \
+            .translate(0.0, -longitude_cap)
+
+    def get_data_ratio(self):
+        """
+        Return the aspect ratio of the data itself.
+
+        This method should be overridden by any Axes that have a
+        fixed data ratio.
+        """
+        return 1.0
+
+    # Interactive panning and zooming is not supported with this projection,
+    # so we override all of the following methods to disable it.
+    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
+
+    # Now, the transforms themselves.
+
+    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):
+            """
+            Override the transform method to implement the custom transform.
+
+            The input and output are Nx2 numpy arrays.
+            """
+            longitude = ll[:, 0:1]
+            latitude  = ll[:, 1:2]
+
+            # Pre-compute some values
+            half_long = longitude / 2.0
+            cos_latitude = np.cos(latitude)
+            sqrt2 = np.sqrt(2.0)
+
+            alpha = 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.concatenate((x, y), 1)
+
+        # This is where things get interesting.  With this projection,
+        # straight lines in data space become curves in display space.
+        # This is done by interpolating new values between the input
+        # values of the data.  Since ``transform`` must not return a
+        # differently-sized array, any transform that requires
+        # changing the length of the data array must happen within
+        # ``transform_path``.
+        def transform_path(self, path):
+            vertices = path.vertices
+            ipath = path.interpolated(self._resolution)
+            return Path(self.transform(ipath.vertices), ipath.codes)
+
+        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 = np.sqrt(1.0 - quarter_x*quarter_x - half_y*half_y)
+            longitude = 2 * np.arctan((z*x) / (2.0 * (2.0*z*z - 1.0)))
+            latitude = np.arcsin(y*z)
+            return np.concatenate((longitude, latitude), 1)
+        transform.__doc__ = Transform.transform.__doc__
+
+        def inverted(self):
+            # The inverse of the inverse is the original transform... ;)
+            return HammerAxes.HammerTransform(self._resolution)
+        inverted.__doc__ = Transform.inverted.__doc__
+
+# Now register the projection with matplotlib so the user can select
+# it.
+register_projection(HammerAxes)
+
+# Now make a simple example using the custom projection.
+from pylab import *
+
+subplot(111, projection="hammer")
+grid(True)
+
+show()

Copied: trunk/matplotlib/examples/api/custom_scale_example.py (from rev 5226, 
trunk/matplotlib/examples/pylab/custom_scale_example.py)
===================================================================
--- trunk/matplotlib/examples/api/custom_scale_example.py                       
        (rev 0)
+++ trunk/matplotlib/examples/api/custom_scale_example.py       2008-05-23 
17:41:36 UTC (rev 5230)
@@ -0,0 +1,165 @@
+from matplotlib import scale as mscale
+from matplotlib import transforms as mtransforms
+
+class MercatorLatitudeScale(mscale.ScaleBase):
+    """
+    Scales data in range -pi/2 to pi/2 (-90 to 90 degrees) using
+    the system used to scale latitudes in a Mercator projection.
+
+    The scale function:
+      ln(tan(y) + sec(y))
+
+    The inverse scale function:
+      atan(sinh(y))
+
+    Since the Mercator scale tends to infinity at +/- 90 degrees,
+    there is user-defined threshold, above and below which nothing
+    will be plotted.  This defaults to +/- 85 degrees.
+
+    source:
+    http://en.wikipedia.org/wiki/Mercator_projection
+    """
+
+    # The scale class must have a member ``name`` that defines the
+    # string used to select the scale.  For example,
+    # ``gca().set_yscale("mercator")`` would be used to select this
+    # scale.
+    name = 'mercator'
+
+
+    def __init__(self, axis, **kwargs):
+        """
+        Any keyword arguments passed to ``set_xscale`` and
+        ``set_yscale`` will be passed along to the scale's
+        constructor.
+
+        thresh: The degree above which to crop the data.
+        """
+        mscale.ScaleBase.__init__(self)
+        thresh = kwargs.pop("thresh", (85 / 180.0) * np.pi)
+        if thresh >= np.pi / 2.0:
+            raise ValueError("thresh must be less than pi/2")
+        self.thresh = thresh
+
+    def get_transform(self):
+        """
+        Override this method to return a new instance that does the
+        actual transformation of the data.
+
+        The MercatorLatitudeTransform class is defined below as a
+        nested class of this one.
+        """
+        return self.MercatorLatitudeTransform(self.thresh)
+
+    def set_default_locators_and_formatters(self, axis):
+        """
+        Override to set up the locators and formatters to use with the
+        scale.  This is only required if the scale requires custom
+        locators and formatters.  Writing custom locators and
+        formatters is rather outside the scope of this example, but
+        there are many helpful examples in ``ticker.py``.
+
+        In our case, the Mercator example uses a fixed locator from
+        -90 to 90 degrees and a custom formatter class to put convert
+        the radians to degrees and put a degree symbol after the
+        value::
+        """
+        class DegreeFormatter(Formatter):
+            def __call__(self, x, pos=None):
+                # \u00b0 : degree symbol
+                return u"%d\u00b0" % ((x / np.pi) * 180.0)
+
+        deg2rad = np.pi / 180.0
+        axis.set_major_locator(FixedLocator(
+                np.arange(-90, 90, 10) * deg2rad))
+        axis.set_major_formatter(DegreeFormatter())
+        axis.set_minor_formatter(DegreeFormatter())
+
+    def limit_range_for_scale(self, vmin, vmax, minpos):
+        """
+        Override to limit the bounds of the axis to the domain of the
+        transform.  In the case of Mercator, the bounds should be
+        limited to the threshold that was passed in.  Unlike the
+        autoscaling provided by the tick locators, this range limiting
+        will always be adhered to, whether the axis range is set
+        manually, determined automatically or changed through panning
+        and zooming.
+        """
+        return max(vmin, -self.thresh), min(vmax, self.thresh)
+
+    class MercatorLatitudeTransform(mtransforms.Transform):
+        # There are two value members that must be defined.
+        # ``input_dims`` and ``output_dims`` specify number of input
+        # dimensions and output dimensions to the transformation.
+        # These are used by the transformation framework to do some
+        # error checking and prevent incompatible transformations from
+        # being connected together.  When defining transforms for a
+        # scale, which are, by definition, separable and have only one
+        # dimension, these members should always be set to 1.
+        input_dims = 1
+        output_dims = 1
+        is_separable = True
+
+        def __init__(self, thresh):
+            mtransforms.Transform.__init__(self)
+            self.thresh = thresh
+
+        def transform(self, a):
+            """
+            This transform takes an Nx1 ``numpy`` array and returns a
+            transformed copy.  Since the range of the Mercator scale
+            is limited by the user-specified threshold, the input
+            array must be masked to contain only valid values.
+            ``matplotlib`` will handle masked arrays and remove the
+            out-of-range data from the plot.  Importantly, the
+            ``transform`` method *must* return an array that is the
+            same shape as the input array, since these values need to
+            remain synchronized with values in the other dimension.
+            """
+            masked = ma.masked_where((a < -self.thresh) | (a > self.thresh), a)
+            if masked.mask.any():
+                return ma.log(np.abs(ma.tan(masked) + 1.0 / ma.cos(masked)))
+            else:
+                return np.log(np.abs(np.tan(a) + 1.0 / np.cos(a)))
+
+        def inverted(self):
+            """
+            Override this method so matplotlib knows how to get the
+            inverse transform for this transform.
+            """
+            return 
MercatorLatitudeScale.InvertedMercatorLatitudeTransform(self.thresh)
+
+    class InvertedMercatorLatitudeTransform(mtransforms.Transform):
+        input_dims = 1
+        output_dims = 1
+        is_separable = True
+
+        def __init__(self, thresh):
+            mtransforms.Transform.__init__(self)
+            self.thresh = thresh
+
+        def transform(self, a):
+            return np.arctan(np.sinh(a))
+
+        def inverted(self):
+            return MercatorLatitudeScale.MercatorLatitudeTransform(self.thresh)
+
+# Now that the Scale class has been defined, it must be registered so
+# that ``matplotlib`` can find it.
+mscale.register_scale(MercatorLatitudeScale)
+
+from pylab import *
+import numpy as np
+
+t = arange(-180.0, 180.0, 0.1)
+s = t / 360.0 * np.pi
+
+plot(t, s, '-', lw=2)
+gca().set_yscale('mercator')
+
+xlabel('Longitude')
+ylabel('Latitude')
+title('Mercator: Projection of the Oppressor')
+grid(True)
+
+show()

Deleted: trunk/matplotlib/examples/pylab/custom_projection_example.py
===================================================================
--- trunk/matplotlib/examples/pylab/custom_projection_example.py        
2008-05-23 17:41:07 UTC (rev 5229)
+++ trunk/matplotlib/examples/pylab/custom_projection_example.py        
2008-05-23 17:41:36 UTC (rev 5230)
@@ -1,475 +0,0 @@
-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
-from matplotlib.projections import register_projection
-
-import numpy as np
-
-# This example projection class is rather long, but it is designed to
-# illustrate many features, not all of which will be used every time.
-# It is also common to factor out a lot of these methods into common
-# code used by a number of projections with similar characteristics
-# (see geo.py).
-
-class HammerAxes(Axes):
-    """
-    A custom class for the Aitoff-Hammer projection, an equal-area map
-    projection.
-
-    http://en.wikipedia.org/wiki/Hammer_projection
-    """
-    # The projection must specify a name.  This will be used be the
-    # user to select the projection, i.e. ``subplot(111,
-    # projection='hammer')``.
-    name = 'hammer'
-
-    # The number of interpolation steps when converting from straight
-    # lines to curves.  (See ``transform_path``).
-    RESOLUTION = 75
-
-    def __init__(self, *args, **kwargs):
-        Axes.__init__(self, *args, **kwargs)
-        self.set_aspect(0.5, adjustable='box', anchor='C')
-        self.cla()
-
-    def cla(self):
-        """
-        Override to set up some reasonable defaults.
-        """
-        # Don't forget to call the base class
-        Axes.cla(self)
-
-        # Set up a default grid spacing
-        self.set_longitude_grid(30)
-        self.set_latitude_grid(15)
-        self.set_longitude_grid_ends(75)
-
-        # Turn off minor ticking altogether
-        self.xaxis.set_minor_locator(NullLocator())
-        self.yaxis.set_minor_locator(NullLocator())
-
-        # Do not display ticks -- we only want gridlines and text
-        self.xaxis.set_ticks_position('none')
-        self.yaxis.set_ticks_position('none')
-
-        # The limits on this projection are fixed -- they are not to
-        # be changed by the user.  This makes the math in the
-        # transformation itself easier, and since this is a toy
-        # example, the easier, the better.
-        Axes.set_xlim(self, -np.pi, np.pi)
-        Axes.set_ylim(self, -np.pi / 2.0, np.pi / 2.0)
-
-    def cla(self):
-        """
-        Initialize the Axes object to reasonable defaults.
-        """
-        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, -np.pi, np.pi)
-        Axes.set_ylim(self, -np.pi / 2.0, np.pi / 2.0)
-
-    def _set_lim_and_transforms(self):
-        """
-        This is called once when the plot is created to set up all the
-        transforms for the data, text and grids.
-        """
-        # There are three important coordinate spaces going on here:
-        #
-        #    1. Data space: The space of the data itself
-        #
-        #    2. Axes space: The unit rectangle (0, 0) to (1, 1)
-        #       covering the entire plot area.
-        #
-        #    3. Display space: The coordinates of the resulting image,
-        #       often in pixels or dpi/inch.
-
-        # This function makes heavy use of the Transform classes in
-        # ``lib/matplotlib/transforms.py.`` For more information, see
-        # the inline documentation there.
-
-        # The goal of the first two transformations is to get from the
-        # data space (in this case longitude and latitude) to axes
-        # space.  It is separated into a non-affine and affine part so
-        # that the non-affine part does not have to be recomputed when
-        # a simple affine change to the figure has been made (such as
-        # resizing the window or changing the dpi).
-
-        # 1) The core transformation from data space into
-        # rectilinear space defined in the HammerTransform class.
-        self.transProjection = self.HammerTransform(self.RESOLUTION)
-
-        # 2) The above has an output range that is not in the unit
-        # rectangle, so scale and translate it so it fits correctly
-        # within the axes.  The peculiar calculations of xscale and
-        # yscale are specific to a Aitoff-Hammer projection, so don't
-        # worry about them too much.
-        xscale = 2.0 * np.sqrt(2.0) * np.sin(0.5 * np.pi)
-        yscale = np.sqrt(2.0) * np.sin(0.5 * np.pi)
-        self.transAffine = Affine2D() \
-            .scale(0.5 / xscale, 0.5 / yscale) \
-            .translate(0.5, 0.5)
-
-        # 3) This is the transformation from axes space to display
-        # space.
-        self.transAxes = BboxTransformTo(self.bbox)
-
-        # Now put these 3 transforms together -- from data all the way
-        # to display coordinates.  Using the '+' operator, these
-        # transforms will be applied "in order".  The transforms are
-        # automatically simplified, if possible, by the underlying
-        # transformation framework.
-        self.transData = \
-            self.transProjection + \
-            self.transAffine + \
-            self.transAxes
-
-        # The main data transformation is set up.  Now deal with
-        # gridlines and tick labels.
-
-        # Longitude gridlines and ticklabels.  The input to these
-        # transforms are in display space in x and axes space in y.
-        # Therefore, the input values will be in range (-xmin, 0),
-        # (xmax, 1).  The goal of these transforms is to go from that
-        # space to display space.  The tick labels will be offset 4
-        # pixels from the equator.
-        self._xaxis_pretransform = \
-            Affine2D() \
-            .scale(1.0, np.pi) \
-            .translate(0.0, -np.pi)
-        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)
-
-        # Now set up the transforms for the latitude ticks.  The input to
-        # these transforms are in axes space in x and display space in
-        # y.  Therefore, the input values will be in range (0, -ymin),
-        # (1, ymax).  The goal of these transforms is to go from that
-        # space to display space.  The tick labels will be offset 4
-        # pixels from the edge of the axes ellipse.
-        yaxis_stretch = Affine2D().scale(np.pi * 2.0, 1.0).translate(-np.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_xaxis_transform(self):
-        """
-        Override this method to provide a transformation for the
-        x-axis grid and ticks.
-        """
-        return self._xaxis_transform
-
-    def get_xaxis_text1_transform(self, pixelPad):
-        """
-        Override this method to provide a transformation for the
-        x-axis tick labels.
-
-        Returns a tuple of the form (transform, valign, halign)
-        """
-        return self._xaxis_text1_transform, 'bottom', 'center'
-
-    def get_xaxis_text2_transform(self, pixelPad):
-        """
-        Override this method to provide a transformation for the
-        secondary x-axis tick labels.
-
-        Returns a tuple of the form (transform, valign, halign)
-        """
-        return self._xaxis_text2_transform, 'top', 'center'
-
-    def get_yaxis_transform(self):
-        """
-        Override this method to provide a transformation for the
-        y-axis grid and ticks.
-        """
-        return self._yaxis_transform
-
-    def get_yaxis_text1_transform(self, pixelPad):
-        """
-        Override this method to provide a transformation for the
-        y-axis tick labels.
-
-        Returns a tuple of the form (transform, valign, halign)
-        """
-        return self._yaxis_text1_transform, 'center', 'right'
-
-    def get_yaxis_text2_transform(self, pixelPad):
-        """
-        Override this method to provide a transformation for the
-        secondary y-axis tick labels.
-
-        Returns a tuple of the form (transform, valign, halign)
-        """
-        return self._yaxis_text2_transform, 'center', 'left'
-
-    def get_axes_patch(self):
-        """
-        Override this method to define the shape that is used for the
-        background of the plot.  It should be a subclass of Patch.
-
-        In this case, it is a Circle (that may be warped by the axes
-        transform into an ellipse).  Any data and gridlines will be
-        clipped to this shape.
-        """
-        return Circle((0.5, 0.5), 0.5)
-
-    # Prevent the user from applying scales to one or both of the
-    # axes.  In this particular case, scaling the axes wouldn't make
-    # sense, so we don't allow it.
-    def set_xscale(self, *args, **kwargs):
-        if args[0] != 'linear':
-            raise NotImplementedError
-        Axes.set_xscale(self, *args, **kwargs)
-
-    def set_yscale(self, *args, **kwargs):
-        if args[0] != 'linear':
-            raise NotImplementedError
-        Axes.set_yscale(self, *args, **kwargs)
-
-    # Prevent the user from changing the axes limits.  In our case, we
-    # want to display the whole sphere all the time, so we override
-    # set_xlim and set_ylim to ignore any input.  This also applies to
-    # interactive panning and zooming in the GUI interfaces.
-    def set_xlim(self, *args, **kwargs):
-        Axes.set_xlim(self, -np.pi, np.pi)
-        Axes.set_ylim(self, -np.pi / 2.0, np.pi / 2.0)
-    set_ylim = set_xlim
-
-    def format_coord(self, long, lat):
-        """
-        Override this method to change how the values are displayed in
-        the status bar.
-
-        In this case, we want them to be displayed in degrees N/S/E/W.
-        """
-        long = long * (180.0 / np.pi)
-        lat = lat * (180.0 / np.pi)
-        if lat >= 0.0:
-            ns = 'N'
-        else:
-            ns = 'S'
-        if long >= 0.0:
-            ew = 'E'
-        else:
-            ew = 'W'
-        # \u00b0 : degree symbol
-        return u'%f\u00b0%s, %f\u00b0%s' % (abs(lat), ns, abs(long), ew)
-
-    class DegreeFormatter(Formatter):
-        """
-        This is a custom formatter that converts the native unit of
-        radians into (truncated) 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 / np.pi) * 180.0
-            degrees = round(degrees / self._round_to) * self._round_to
-            # \u00b0 : degree symbol
-            return u"%d\u00b0" % degrees
-
-    def set_longitude_grid(self, degrees):
-        """
-        Set the number of degrees between each longitude grid.
-
-        This is an example method that is specific to this projection
-        class -- it provides a more convenient interface to set the
-        ticking than set_xticks would.
-        """
-        # Set up a FixedLocator at each of the points, evenly spaced
-        # by degrees.
-        number = (360.0 / degrees) + 1
-        self.xaxis.set_major_locator(
-            FixedLocator(
-                np.linspace(-np.pi, np.pi, number, True)[1:-1]))
-        # Set the formatter to display the tick labels in degrees,
-        # rather than radians.
-        self.xaxis.set_major_formatter(self.DegreeFormatter(degrees))
-
-    def set_latitude_grid(self, degrees):
-        """
-        Set the number of degrees between each longitude grid.
-
-        This is an example method that is specific to this projection
-        class -- it provides a more convenient interface than
-        set_yticks would.
-        """
-        # Set up a FixedLocator at each of the points, evenly spaced
-        # by degrees.
-        number = (180.0 / degrees) + 1
-        self.yaxis.set_major_locator(
-            FixedLocator(
-                np.linspace(-np.pi / 2.0, np.pi / 2.0, number, True)[1:-1]))
-        # Set the formatter to display the tick labels in degrees,
-        # rather than radians.
-        self.yaxis.set_major_formatter(self.DegreeFormatter(degrees))
-
-    def set_longitude_grid_ends(self, degrees):
-        """
-        Set the latitude(s) at which to stop drawing the longitude grids.
-
-        Often, in geographic projections, you wouldn't want to draw
-        longitude gridlines near the poles.  This allows the user to
-        specify the degree at which to stop drawing longitude grids.
-
-        This is an example method that is specific to this projection
-        class -- it provides an interface to something that has no
-        analogy in the base Axes class.
-        """
-        longitude_cap = degrees * (np.pi / 180.0)
-        # Change the xaxis gridlines transform so that it draws from
-        # -degrees to degrees, rather than -pi to pi.
-        self._xaxis_pretransform \
-            .clear() \
-            .scale(1.0, longitude_cap * 2.0) \
-            .translate(0.0, -longitude_cap)
-
-    def get_data_ratio(self):
-        """
-        Return the aspect ratio of the data itself.
-
-        This method should be overridden by any Axes that have a
-        fixed data ratio.
-        """
-        return 1.0
-
-    # Interactive panning and zooming is not supported with this projection,
-    # so we override all of the following methods to disable it.
-    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
-
-    # Now, the transforms themselves.
-
-    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):
-            """
-            Override the transform method to implement the custom transform.
-
-            The input and output are Nx2 numpy arrays.
-            """
-            longitude = ll[:, 0:1]
-            latitude  = ll[:, 1:2]
-
-            # Pre-compute some values
-            half_long = longitude / 2.0
-            cos_latitude = np.cos(latitude)
-            sqrt2 = np.sqrt(2.0)
-
-            alpha = 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.concatenate((x, y), 1)
-
-        # This is where things get interesting.  With this projection,
-        # straight lines in data space become curves in display space.
-        # This is done by interpolating new values between the input
-        # values of the data.  Since ``transform`` must not return a
-        # differently-sized array, any transform that requires
-        # changing the length of the data array must happen within
-        # ``transform_path``.
-        def transform_path(self, path):
-            vertices = path.vertices
-            ipath = path.interpolated(self._resolution)
-            return Path(self.transform(ipath.vertices), ipath.codes)
-
-        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 = np.sqrt(1.0 - quarter_x*quarter_x - half_y*half_y)
-            longitude = 2 * np.arctan((z*x) / (2.0 * (2.0*z*z - 1.0)))
-            latitude = np.arcsin(y*z)
-            return np.concatenate((longitude, latitude), 1)
-        transform.__doc__ = Transform.transform.__doc__
-
-        def inverted(self):
-            # The inverse of the inverse is the original transform... ;)
-            return HammerAxes.HammerTransform(self._resolution)
-        inverted.__doc__ = Transform.inverted.__doc__
-
-# Now register the projection with matplotlib so the user can select
-# it.
-register_projection(HammerAxes)
-
-# Now make a simple example using the custom projection.
-from pylab import *
-
-subplot(111, projection="hammer")
-grid(True)
-
-show()

Deleted: trunk/matplotlib/examples/pylab/custom_scale_example.py
===================================================================
--- trunk/matplotlib/examples/pylab/custom_scale_example.py     2008-05-23 
17:41:07 UTC (rev 5229)
+++ trunk/matplotlib/examples/pylab/custom_scale_example.py     2008-05-23 
17:41:36 UTC (rev 5230)
@@ -1,165 +0,0 @@
-from matplotlib import scale as mscale
-from matplotlib import transforms as mtransforms
-
-class MercatorLatitudeScale(mscale.ScaleBase):
-    """
-    Scales data in range -pi/2 to pi/2 (-90 to 90 degrees) using
-    the system used to scale latitudes in a Mercator projection.
-
-    The scale function:
-      ln(tan(y) + sec(y))
-
-    The inverse scale function:
-      atan(sinh(y))
-
-    Since the Mercator scale tends to infinity at +/- 90 degrees,
-    there is user-defined threshold, above and below which nothing
-    will be plotted.  This defaults to +/- 85 degrees.
-
-    source:
-    http://en.wikipedia.org/wiki/Mercator_projection
-    """
-
-    # The scale class must have a member ``name`` that defines the
-    # string used to select the scale.  For example,
-    # ``gca().set_yscale("mercator")`` would be used to select this
-    # scale.
-    name = 'mercator'
-
-
-    def __init__(self, axis, **kwargs):
-        """
-        Any keyword arguments passed to ``set_xscale`` and
-        ``set_yscale`` will be passed along to the scale's
-        constructor.
-
-        thresh: The degree above which to crop the data.
-        """
-        mscale.ScaleBase.__init__(self)
-        thresh = kwargs.pop("thresh", (85 / 180.0) * np.pi)
-        if thresh >= np.pi / 2.0:
-            raise ValueError("thresh must be less than pi/2")
-        self.thresh = thresh
-
-    def get_transform(self):
-        """
-        Override this method to return a new instance that does the
-        actual transformation of the data.
-
-        The MercatorLatitudeTransform class is defined below as a
-        nested class of this one.
-        """
-        return self.MercatorLatitudeTransform(self.thresh)
-
-    def set_default_locators_and_formatters(self, axis):
-        """
-        Override to set up the locators and formatters to use with the
-        scale.  This is only required if the scale requires custom
-        locators and formatters.  Writing custom locators and
-        formatters is rather outside the scope of this example, but
-        there are many helpful examples in ``ticker.py``.
-
-        In our case, the Mercator example uses a fixed locator from
-        -90 to 90 degrees and a custom formatter class to put convert
-        the radians to degrees and put a degree symbol after the
-        value::
-        """
-        class DegreeFormatter(Formatter):
-            def __call__(self, x, pos=None):
-                # \u00b0 : degree symbol
-                return u"%d\u00b0" % ((x / np.pi) * 180.0)
-
-        deg2rad = np.pi / 180.0
-        axis.set_major_locator(FixedLocator(
-                np.arange(-90, 90, 10) * deg2rad))
-        axis.set_major_formatter(DegreeFormatter())
-        axis.set_minor_formatter(DegreeFormatter())
-
-    def limit_range_for_scale(self, vmin, vmax, minpos):
-        """
-        Override to limit the bounds of the axis to the domain of the
-        transform.  In the case of Mercator, the bounds should be
-        limited to the threshold that was passed in.  Unlike the
-        autoscaling provided by the tick locators, this range limiting
-        will always be adhered to, whether the axis range is set
-        manually, determined automatically or changed through panning
-        and zooming.
-        """
-        return max(vmin, -self.thresh), min(vmax, self.thresh)
-
-    class MercatorLatitudeTransform(mtransforms.Transform):
-        # There are two value members that must be defined.
-        # ``input_dims`` and ``output_dims`` specify number of input
-        # dimensions and output dimensions to the transformation.
-        # These are used by the transformation framework to do some
-        # error checking and prevent incompatible transformations from
-        # being connected together.  When defining transforms for a
-        # scale, which are, by definition, separable and have only one
-        # dimension, these members should always be set to 1.
-        input_dims = 1
-        output_dims = 1
-        is_separable = True
-
-        def __init__(self, thresh):
-            mtransforms.Transform.__init__(self)
-            self.thresh = thresh
-
-        def transform(self, a):
-            """
-            This transform takes an Nx1 ``numpy`` array and returns a
-            transformed copy.  Since the range of the Mercator scale
-            is limited by the user-specified threshold, the input
-            array must be masked to contain only valid values.
-            ``matplotlib`` will handle masked arrays and remove the
-            out-of-range data from the plot.  Importantly, the
-            ``transform`` method *must* return an array that is the
-            same shape as the input array, since these values need to
-            remain synchronized with values in the other dimension.
-            """
-            masked = ma.masked_where((a < -self.thresh) | (a > self.thresh), a)
-            if masked.mask.any():
-                return ma.log(np.abs(ma.tan(masked) + 1.0 / ma.cos(masked)))
-            else:
-                return np.log(np.abs(np.tan(a) + 1.0 / np.cos(a)))
-
-        def inverted(self):
-            """
-            Override this method so matplotlib knows how to get the
-            inverse transform for this transform.
-            """
-            return 
MercatorLatitudeScale.InvertedMercatorLatitudeTransform(self.thresh)
-
-    class InvertedMercatorLatitudeTransform(mtransforms.Transform):
-        input_dims = 1
-        output_dims = 1
-        is_separable = True
-
-        def __init__(self, thresh):
-            mtransforms.Transform.__init__(self)
-            self.thresh = thresh
-
-        def transform(self, a):
-            return np.arctan(np.sinh(a))
-
-        def inverted(self):
-            return MercatorLatitudeScale.MercatorLatitudeTransform(self.thresh)
-
-# Now that the Scale class has been defined, it must be registered so
-# that ``matplotlib`` can find it.
-mscale.register_scale(MercatorLatitudeScale)
-
-from pylab import *
-import numpy as np
-
-t = arange(-180.0, 180.0, 0.1)
-s = t / 360.0 * np.pi
-
-plot(t, s, '-', lw=2)
-gca().set_yscale('mercator')
-
-xlabel('Longitude')
-ylabel('Latitude')
-title('Mercator: Projection of the Oppressor')
-grid(True)
-
-show()


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
Matplotlib-checkins mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins

Reply via email to