Repository: nilearn/nilearn

Issue:
Incorrect colorbar with thresholded statmap
I'm trying to plot a thresholded statistical map, with values ranging between 0.0 and 0.27.

I can't get the colorbar 

Running `plotting.plot_stat_map(svm_loc)` gives me the following:
<img width="540" alt="image" src="https://user-images.githubusercontent.com/54182/80816934-714a2680-8b9e-11ea-8e43-b240ead33bd5.png">

Notice the small colorbar box on the bottom right. I've tried playing around with `threshold`, `symmetric_cbar`, `vmax` without any success.

I'm taking a look at `plot_stat_map`, and specifically this line of `_crop_colorbar`:
https://github.com/nilearn/nilearn/blob/927976dad301ff729d3e695aef0aad7a9be53084/nilearn/plotting/img_plotting.py#L223

From what I can tell, it's being given cbar_vmin and cbar_vmax values of 0 and None, which are adjusted to 0 and 0.27, and _then_ normalized to 0.5 and 1.

If I replace `cbar.ax.set_ylim(cbar.norm(cbar_vmin), cbar.norm(cbar_vmax))` with `    cbar.ax.set_ylim(cbar_vmin, cbar_vmax)`, and disable the outline, I get the following:

<img width="540" alt="image" src="https://user-images.githubusercontent.com/54182/80817874-39dc7980-8ba0-11ea-82fd-7979f88d429f.png">


My guess is due to the following API change in matplotlib 3.x: https://matplotlib.org/3.2.1/api/api_changes.html#matplotlib-colorbar-colorbar-uses-un-normalized-axes-for-all-mappables

nilearn: 0.7.0a
matplotlib: 3.2.1


Discussion:
thanks for finding this! So I guess we need to do one or the other depending on the matplolib version. Since you already found the solution would you like to open a PR?

Resolving patch:
diff --git a/nilearn/plotting/img_plotting.py b/nilearn/plotting/img_plotting.py
--- a/nilearn/plotting/img_plotting.py
+++ b/nilearn/plotting/img_plotting.py
@@ -13,6 +13,7 @@
 import functools
 import numbers
 import warnings
+from distutils.version import LooseVersion
 
 # Standard scientific libraries imports (more specific imports are
 # delayed, so that the part module can be used without them).
@@ -220,12 +221,24 @@ def _crop_colorbar(cbar, cbar_vmin, cbar_vmax):
         cbar_vmin = cbar_tick_locs.min()
     new_tick_locs = np.linspace(cbar_vmin, cbar_vmax,
                                 len(cbar_tick_locs))
-    cbar.ax.set_ylim(cbar.norm(cbar_vmin), cbar.norm(cbar_vmax))
-    outline = cbar.outline.get_xy()
-    outline[:2, 1] += cbar.norm(cbar_vmin)
-    outline[2:6, 1] -= (1. - cbar.norm(cbar_vmax))
-    outline[6:, 1] += cbar.norm(cbar_vmin)
-    cbar.outline.set_xy(outline)
+
+    # matplotlib >= 3.2.0 no longer normalizes axes between 0 and 1
+    # See https://matplotlib.org/3.2.1/api/prev_api_changes/api_changes_3.2.0.html
+    if LooseVersion(matplotlib.__version__) >= LooseVersion("3.2.0"):
+        cbar.ax.set_ylim(cbar_vmin, cbar_vmax)
+        X, _ = cbar._mesh()
+        new_X = np.array([X[0], X[-1]])
+        new_Y = np.array([[cbar_vmin, cbar_vmin], [cbar_vmax, cbar_vmax]])
+        xy = cbar._outline(new_X, new_Y)
+        cbar.outline.set_xy(xy)
+    else:
+        cbar.ax.set_ylim(cbar.norm(cbar_vmin), cbar.norm(cbar_vmax))
+        outline = cbar.outline.get_xy()
+        outline[:2, 1] += cbar.norm(cbar_vmin)
+        outline[2:6, 1] -= (1. - cbar.norm(cbar_vmax))
+        outline[6:, 1] += cbar.norm(cbar_vmin)
+        cbar.outline.set_xy(outline)
+
     cbar.set_ticks(new_tick_locs, update_ticks=True)
 
 


Test patch: