Repository: nilearn/nilearn

Issue:
Bug in _uncompress_file
Hi!
(...still nilearn-sprinting in the train back to Marseille ;) )

I found a bug when trying to download .tar.gz archive files from osf.

I got an error when using _fetch_files:

```
from nilearn.datasets.utils import _fetch_files
opts = {'uncompress': True}
data_dir = '~/nilearn_data'
url = 'https://osf.io/vym9k/download'
_fetch_files(data_dir, [('sphere_right.gii', url, opts)])
```

After digging, I realized the problem is in _uncompress_file: the temporary downloaded file is actually called 'download' on disk (because of the url that's given by osf), and  _uncompress_file does not extract the files if the archive was a .tar.gz one (it does not return an error, and it erases the 'download' file)

Note that:
- if the archive is a zip file, everything works... (as in the fetch_icbm152_2009 function); the temporary file is also called 'download' on disk, but _uncompress_file handles it properly
- if the file on disk is actually called 'whatever.tar.gz' and you run _uncompress_file on it, it also handles it properly

Soooooooooo, to reproduce the problem, the easiest is that:
1. you type https://osf.io/vym9k/download in your browser to get the file
2. you rename the file manually in your unix shell
```
> mv sphere_mesh.tar.gz download
```
3. you try to use _uncompress_file on it
```
from nilearn.datasets.utils import _uncompress_file
_uncompress_file('download')
```
It will: 1. not extract the files in the archive, 2. erase the 'download' file, 3. not return an error.

I hope this is clear enough...

Sylvain




Discussion:

Resolving patch:
diff --git a/examples/01_plotting/plot_atlas.py b/examples/01_plotting/plot_atlas.py
--- a/examples/01_plotting/plot_atlas.py
+++ b/examples/01_plotting/plot_atlas.py
@@ -23,4 +23,10 @@
 from nilearn import plotting
 
 plotting.plot_roi(atlas_filename, title="Harvard Oxford atlas")
+
+###########################################################################
+# Visualizing the Harvard-Oxford atlas with contours
+# --------------------------------------------------
+plotting.plot_roi(atlas_filename, view_type='contours',
+                  title="Harvard Oxford atlas in contours")
 plotting.show()
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
@@ -642,12 +642,58 @@ def plot_epi(epi_img=None, cut_coords=None, output_file=None,
     return display
 
 
+def _plot_roi_contours(display, roi_img, cmap, alpha, linewidths):
+    """Helper function for plotting regions of interest ROIs in contours.
+
+    Parameters
+    ----------
+    display : nilearn.plotting.displays.OrthoSlicer, object
+        An object with background image on which contours are shown.
+
+    roi_img : Niimg-like object
+        See http://nilearn.github.io/manipulating_images/input_output.html
+        The ROI/mask image, it could be binary mask or an atlas or ROIs
+        with integer values.
+
+    cmap : matplotlib colormap
+        The colormap for the atlas maps
+
+    alpha : float between 0 and 1
+        Alpha sets the transparency of the color inside the filled
+        contours.
+
+    linewidths : float
+        This option can be used to set the boundary thickness of the
+        contours.
+
+    Returns
+    -------
+    display : nilearn.plotting.displays.OrthoSlicer, object
+        Contours displayed on the background image.
+    """
+    roi_img = _utils.check_niimg_3d(roi_img)
+    roi_data = roi_img.get_data()
+    labels = np.unique(roi_data)
+    cmap = plt.cm.get_cmap(cmap)
+    color_list = cmap(np.linspace(0, 1, len(labels)))
+    for idx, label in enumerate(labels):
+        if label == 0:
+            continue
+        data = (roi_data == label)
+        data = data.astype(np.int)
+        img = new_img_like(roi_img, data, affine=roi_img.affine)
+        display.add_contours(img, levels=[0.5], colors=[color_list[idx - 1]],
+                             alpha=alpha, linewidths=linewidths,
+                             linestyles='solid')
+    return display
+
+
 def plot_roi(roi_img, bg_img=MNI152TEMPLATE, cut_coords=None,
              output_file=None, display_mode='ortho', figure=None, axes=None,
              title=None, annotate=True, draw_cross=True, black_bg='auto',
              threshold=0.5, alpha=0.7, cmap=plt.cm.gist_ncar, dim='auto',
              vmin=None, vmax=None, resampling_interpolation='nearest',
-             **kwargs):
+             view_type='continuous', linewidths=2.5, **kwargs):
     """ Plot cuts of an ROI/mask image (by default 3 cuts: Frontal, Axial, and
         Lateral)
 
@@ -705,6 +751,11 @@ def plot_roi(roi_img, bg_img=MNI152TEMPLATE, cut_coords=None,
             values below the threshold (in absolute value) are plotted
             as transparent. If auto is given, the threshold is determined
             magically by analysis of the image.
+        alpha : float between 0 and 1, default=0.7
+            Alpha sets the transparency of the color inside the filled
+            contours.
+        cmap : matplotlib colormap, optional
+            The colormap for the atlas maps
         dim : float, 'auto' (by default), optional
             Dimming factor applied to background image. By default, automatic
             heuristics are applied based upon the background image intensity.
@@ -720,6 +771,15 @@ def plot_roi(roi_img, bg_img=MNI152TEMPLATE, cut_coords=None,
             space. Can be "continuous" to use 3rd-order spline interpolation,
             or "nearest" (default) to use nearest-neighbor mapping.
             "nearest" is faster but can be noisier in some cases.
+        view_type : {'continuous', 'contours'}, optional
+            By default view_type == 'continuous', rois are shown as continuous
+            colors.
+            If view_type == 'contours', maps are shown as contours. For this
+            type, label denoted as 0 is considered as background and not
+            shown.
+        linewidths : float, default=2.5
+            This option can be used to set the boundary thickness of the
+            contours. Only reflects when view_type='contours'.
 
         Notes
         -----
@@ -734,6 +794,16 @@ def plot_roi(roi_img, bg_img=MNI152TEMPLATE, cut_coords=None,
         nilearn.plotting.plot_prob_atlas : To simply plot probabilistic atlases
             (4D images)
     """  # noqa: E501
+    valid_view_types = ['continuous', 'contours']
+    if view_type not in valid_view_types:
+        raise ValueError(
+            'Unknown view type: %s. Valid view types are %s' %
+            (str(view_type), str(valid_view_types))
+        )
+    elif view_type == 'contours':
+        img = roi_img
+        roi_img = None
+
     bg_img, black_bg, bg_vmin, bg_vmax = _load_anat(bg_img, dim=dim,
                                                     black_bg=black_bg)
 
@@ -745,6 +815,11 @@ def plot_roi(roi_img, bg_img=MNI152TEMPLATE, cut_coords=None,
         threshold=threshold, bg_vmin=bg_vmin, bg_vmax=bg_vmax,
         resampling_interpolation=resampling_interpolation,
         alpha=alpha, cmap=cmap, vmin=vmin, vmax=vmax, **kwargs)
+
+    if view_type == 'contours':
+        display = _plot_roi_contours(display, img, cmap=cmap, alpha=alpha,
+                                     linewidths=linewidths)
+
     return display
 
 


Test patch:
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
@@ -86,6 +86,34 @@ def demo_plot_roi(**kwargs):
     return plot_roi(img, title="Broca's area", **kwargs)
 
 
+def test_plot_roi_view_types():
+    # This is only a smoke test contours rois
+    demo_plot_roi(view_type='contours')
+    # This is only a smoke test contours rois
+    demo_plot_roi(view_type='continuous')
+
+    # Test error message for invalid view_type
+    with pytest.raises(ValueError,
+                       match='Unknown view type:'
+                       ):
+        demo_plot_roi(view_type='flled')
+    plt.close()
+
+
+def test_plot_roi_contours():
+    display = plot_roi(None)
+    data = np.zeros((91, 109, 91))
+    x, y, z = -52, 10, 22
+    x_map, y_map, z_map = coord_transform(x, y, z,
+                                          np.linalg.inv(mni_affine))
+    data[int(x_map) - 5:int(x_map) + 5, int(y_map) - 3:int(y_map) + 3,
+         int(z_map) - 10:int(z_map) + 10] = 1
+    img = nibabel.Nifti1Image(data, mni_affine)
+    plot_roi(img, cmap='RdBu', alpha=0.1, view_type='contours',
+             linewidths=2.)
+    plt.close()
+
+
 def test_demo_plot_roi():
     # This is only a smoke test
     demo_plot_roi()