From 1ac794b362d143ac6a634b70993d950d8fe4adeb Mon Sep 17 00:00:00 2001
From: Friedrich Romstedt <friedrichromstedt@gmail.com>
Date: Mon, 18 Oct 2010 14:40:49 +0200
Subject: [PATCH] Fixing bug in mpl.cm.get_cmap()

Bug description:
    LinearSegmentedColormap constructor was called with *spec*
    directly also if *spec* was of tuple format.

Reported by:
    LittleBigBrain <braingateway@gmail.com>

Fix:
    Overhaul of the initialisation of the colormaps.
    Providing mpl.cm.generate_cmap(), which takes care of the format.
    Using that in get_cmap().

Details:
    New functions reverse_cmap_spec() (for both formats) and
    generate_cmap().  Initialisation of precached cmaps in two steps now:

    1.  Generate the reversed specs and store them in *datad* under
        appropriate key.

    2.  Generate cmaps with rc lut size from all specs now in *datad*.

Advantages:
    In the author's opinion better readable init, less indentation :-)
    Bug seems fixed, reporter's reproduction code passes well.

TODO:
    Test suite run, awaiting LittleBigBrain's confirmation of test suite
    pass.

    Generally, LinearSegmentedColormap shouldn't be instantiated via a
    static method .from_list(), but rather by specifying appropriate
    arguments to .__init__() directly.
---
 lib/matplotlib/cm.py |   56 +++++++++++++++++++++++++++++++++----------------
 1 files changed, 38 insertions(+), 18 deletions(-)

diff --git a/lib/matplotlib/cm.py b/lib/matplotlib/cm.py
index a35ceb8..989bc84 100644
--- a/lib/matplotlib/cm.py
+++ b/lib/matplotlib/cm.py
@@ -25,6 +25,7 @@ def _reverser(f):
     return freversed
 
 def revcmap(data):
+    """Can only handle specification *data* in dictionary format."""
     data_r = {}
     for key, val in data.iteritems():
         if callable(val):
@@ -39,32 +40,51 @@ def revcmap(data):
         data_r[key] = valnew
     return data_r
 
+def reverse_cmap_spec(spec):
+    """Reverses cmap specification *spec*, can handle both dict and tuple
+    type specs."""
+    
+    if 'red' in spec:
+        return revcmap(spec)
+    else:
+        revspec = list(reversed(spec))
+        if len(revspec[0]) == 2:    # e.g., (1, (1.0, 0.0, 1.0))
+            revspec = [(1.0 - a, b) for a, b in revspec]
+        return revspec
+
+def generate_cmap(name, lutsize):
+    """Generates the requested cmap from it's name *name*.  The lut size is 
+    *lutsize*."""
+    
+    spec = datad[name]
+
+    # Generate the colormap object.
+    if 'red' in spec:
+        return colors.LinearSegmentedColormap(name, spec, lutsize)
+    else:
+        return colors.LinearSegmentedColormap.from_list(spec, spec, lutsize)
+
 LUTSIZE = mpl.rcParams['image.lut']
 
 _cmapnames = datad.keys()  # need this list because datad is changed in loop
 
+# Generate the reversed specifications ...
+
 for cmapname in _cmapnames:
-    cmapname_r = cmapname+'_r'
-    cmapspec = datad[cmapname]
-    if 'red' in cmapspec:
-        datad[cmapname_r] = revcmap(cmapspec)
-        cmap_d[cmapname] = colors.LinearSegmentedColormap(
-                                cmapname, cmapspec, LUTSIZE)
-        cmap_d[cmapname_r] = colors.LinearSegmentedColormap(
-                                cmapname_r, datad[cmapname_r], LUTSIZE)
-    else:
-        revspec = list(reversed(cmapspec))
-        if len(revspec[0]) == 2:    # e.g., (1, (1.0, 0.0, 1.0))
-            revspec = [(1.0 - a, b) for a, b in revspec]
-        datad[cmapname_r] = revspec
+    spec = datad[cmapname]
+    spec_reversed = reverse_cmap_spec(spec)
+    datad[cmapname + '_r'] = spec_reversed
+
+# Precache the cmaps with ``lutsize = LUTSIZE`` ...
 
-        cmap_d[cmapname] = colors.LinearSegmentedColormap.from_list(
-                                cmapname, cmapspec, LUTSIZE)
-        cmap_d[cmapname_r] = colors.LinearSegmentedColormap.from_list(
-                                cmapname_r, revspec, LUTSIZE)
+# Use datad.keys() to also add the reversed ones added in the section above:
+for cmapname in datad.keys():
+    cmap_d[cmapname] = generate_cmap(cmapname, LUTSIZE)
 
 locals().update(cmap_d)
 
+# Continue with definitions ...
+
 def register_cmap(name=None, cmap=None, data=None, lut=None):
     """
     Add a colormap to the set recognized by :func:`get_cmap`.
@@ -128,7 +148,7 @@ def get_cmap(name=None, lut=None):
         if lut is None:
             return cmap_d[name]
         elif name in datad:
-            return colors.LinearSegmentedColormap(name,  datad[name], lut)
+            return generate_cmap(name, lut)
         else:
             raise ValueError("Colormap %s is not recognized" % name)
 
-- 
1.7.1

