Repository: nilearn/nilearn

Issue:
plot_stat_map (and probably plot_*) fails when display_mode axis has singleton dimension
Fuzzing the plotting code, I find the following testcase fails

```
    for axis, direction in enumerate("xyz"):
        shape = [5, 6, 7]
        shape[axis] = 1
        img = nibabel.Nifti1Image(np.ones(shape), np.eye(4))
        plot_stat_map(img, None, display_mode=direction)
```

`ValueError: Too many cuts requested for the data: n_cuts=7, data size=1`

The culprit is the find_cut_slices function.

A fix would be to return all the slices along the axis if there are at least as many requested n_slices as there are slices.



Discussion:

Resolving patch:
diff --git a/nilearn/plotting/find_cuts.py b/nilearn/plotting/find_cuts.py
--- a/nilearn/plotting/find_cuts.py
+++ b/nilearn/plotting/find_cuts.py
@@ -5,6 +5,7 @@
 # Author: Gael Varoquaux
 # License: BSD
 
+import warnings
 import numpy as np
 from scipy import ndimage
 
@@ -123,6 +124,39 @@ def _get_auto_mask_bounds(img):
     return (xmin, xmax), (ymin, ymax), (zmin, zmax)
 
 
+def _transform_cut_coords(cut_coords, direction, affine):
+    """Transforms cut_coords back in image space
+
+    Parameters
+    ----------
+    cut_coords: 1D array of length n_cuts
+        The coordinates to be transformed.
+
+    direction: string, optional (default "z")
+        sectional direction; possible values are "x", "y", or "z"
+
+    affine: 2D array of shape (4, 4)
+        The affine for the image.
+
+    Returns
+    -------
+    cut_coords: 1D array of length n_cuts
+       The original cut_coords transformed image space.
+    """
+    # make kwargs
+    axis = 'xyz'.index(direction)
+    kwargs = {}
+    for name in 'xyz':
+        kwargs[name] = np.zeros(len(cut_coords))
+    kwargs[direction] = cut_coords
+    kwargs['affine'] = affine
+
+    # We need atleast_1d to make sure that when n_cuts is 1 we do
+    # get an iterable
+    cut_coords = coord_transform(**kwargs)[axis]
+    return np.atleast_1d(cut_coords)
+
+
 def find_cut_slices(img, direction='z', n_cuts=12, spacing='auto'):
     """ Find 'good' cross-section slicing positions along a given axis.
 
@@ -151,16 +185,23 @@ def find_cut_slices(img, direction='z', n_cuts=12, spacing='auto'):
     less than 'spacing' will be returned.
     """
 
-    assert direction in 'xyz'
-
+    # misc
+    if not direction in 'xyz':
+        raise ValueError(
+            "'direction' must be one of 'x', 'y', or 'z'. Got '%s'" % (
+                direction))
     axis = 'xyz'.index(direction)
-
     affine = img.get_affine()
     orig_data = np.abs(img.get_data())
     this_shape = orig_data.shape[axis]
+
+    # BF issue #575: Return all the slices along and axis if this axis
+    # is the display mode and there are at least as many requested
+    # n_slices as there are slices.
     if n_cuts > this_shape:
-        raise ValueError('Too many cuts requested for the data: '
-                         'n_cuts=%i, data size=%i' % (n_cuts, this_shape))
+        warnings.warn('Too many cuts requested for the data: '
+                      'n_cuts=%i, data size=%i' % (n_cuts, this_shape))
+        return _transform_cut_coords(np.arange(this_shape), direction, affine)
 
     data = orig_data.copy()
     if data.dtype.kind == 'i':
@@ -227,14 +268,4 @@ def find_cut_slices(img, direction='z', n_cuts=12, spacing='auto'):
     cut_coords = np.array(cut_coords)
     cut_coords.sort()
 
-    # Transform this back in image space
-    kwargs = dict()
-    for name in 'xyz':
-        kwargs[name] = np.zeros(len(cut_coords))
-    kwargs[direction] = cut_coords
-    kwargs['affine'] = affine
-
-    cut_coords = coord_transform(**kwargs)[axis]
-    # We need to atleast_1d to make sure that when n_cuts is 1 we do
-    # get an iterable
-    return np.atleast_1d(cut_coords)
+    return _transform_cut_coords(cut_coords, direction, affine)


Test patch:
diff --git a/nilearn/plotting/tests/test_find_cuts.py b/nilearn/plotting/tests/test_find_cuts.py
--- a/nilearn/plotting/tests/test_find_cuts.py
+++ b/nilearn/plotting/tests/test_find_cuts.py
@@ -1,10 +1,8 @@
 import numpy as np
-
-from nose.tools import assert_equal
-
+from nose.tools import assert_equal, assert_true
 import nibabel
-
-from nilearn.plotting.find_cuts import find_xyz_cut_coords, find_cut_slices
+from nilearn.plotting.find_cuts import (find_xyz_cut_coords, find_cut_slices,
+                                        _transform_cut_coords)
 
 
 def test_find_cut_coords():
@@ -72,3 +70,27 @@ def test_find_cut_slices():
         # Only a smoke test
         cuts = find_cut_slices(img, direction=direction,
                                n_cuts=n_cuts, spacing=2)
+
+
+def test_singleton_ax_dim():
+    for axis, direction in enumerate("xyz"):
+        shape = [5, 6, 7]
+        shape[axis] = 1
+        img = nibabel.Nifti1Image(np.ones(shape), np.eye(4))
+        find_cut_slices(img, direction=direction)
+
+
+def test_tranform_cut_coords():
+    affine = np.eye(4)
+
+    # test that when n_cuts is 1 we do get an iterable
+    for direction in 'xyz':
+        assert_true(hasattr(_transform_cut_coords([4], direction, affine),
+                            "__iter__"))
+
+    # test that n_cuts after as before function call
+    n_cuts = 5
+    cut_coords = np.arange(n_cuts)
+    for direction in 'xyz':
+        assert_equal(len(_transform_cut_coords(cut_coords, direction, affine)),
+                     n_cuts)
diff --git a/nilearn/plotting/tests/test_img_plotting.py b/nilearn/plotting/tests/test_img_plotting.py
--- a/nilearn/plotting/tests/test_img_plotting.py
+++ b/nilearn/plotting/tests/test_img_plotting.py
@@ -379,3 +379,11 @@ def test_plot_connectome_exceptions():
                         adjacency_matrix, node_coords,
                         node_kwargs={'c': 'blue'},
                         **kwargs)
+
+
+def test_singleton_ax_dim():
+    for axis, direction in enumerate("xyz"):
+        shape = [5, 6, 7]
+        shape[axis] = 1
+        img = nibabel.Nifti1Image(np.ones(shape), np.eye(4))
+        plot_stat_map(img, None, display_mode=direction)