Repository: nilearn/nilearn

Issue:
Title not handled properly in the `plotting.plot_surf_stat_map` method
<!--Provide a brief description of the bug.-->

In `plotting.plot_surf_stat_map`, passing a title and a given axis (e.g in a subplots case) to the method results in the title being displayed for the subplot only. When passing a title to the `plotting.plot_surf_stat_map` method, the title is being displayed as the title of the overall figure. This results in different subplots with no titles, and a general figure with the title of the last subplot.

<!--Please fill in the following information, to the best of your ability.-->
Nilearn version: main

### Expected behavior

one title per subplot

![image](https://user-images.githubusercontent.com/12402673/162764534-5cf45085-638e-4111-8424-2a9e7d563b1e.png)

### Actual behavior

last title is used as title of the overall figure

![image](https://user-images.githubusercontent.com/12402673/162764621-403f3699-4eff-4e89-9c96-a9c067ef7123.png)

### Solution

The solution is to replace the [figure.suptitle](https://github.com/nilearn/nilearn/blob/0eb1340212c6db5aac07ae31b3061bde5c9f7432/nilearn/plotting/surf_plotting.py#L522) by something similar to [plot_stat_map](https://github.com/nilearn/nilearn/blob/main/nilearn/plotting/img_plotting.py#L213) which is implemented [here](https://github.com/nilearn/nilearn/blob/main/nilearn/plotting/displays/_slicers.py#L162).

I've generated the actual behavior figure by hard coding the following code in the figure.suptitle emplacement:

```python
    if title is not None:
        # Actual version, leading to general title
        # figure.suptitle(title, x=.5, y=.95, fontsize=title_font_size)
        # New version leading to title for each subplot (with hardcoded values)
        x = 0.01
        y = 0.99
        text = title
        size=15
        color="w"
        bgcolor="k"
        axes.text2D(x, y, text,
                transform=axes.transAxes,
                horizontalalignment='left',
                verticalalignment='top',
                size=size, color=color,
                bbox=dict(boxstyle="square,pad=.3",
                          ec=bgcolor, fc=bgcolor, alpha=1),
                zorder=1000)
        axes.set_zorder(1000)
```

### Steps and code to reproduce bug

```python
import matplotlib.pyplot as plt
from nilearn import datasets, plotting, surface

# Fetch mask and motor images
motor_img = datasets.fetch_neurovault_motor_task().images[0]

# Compute a texture example
fsaverage = datasets.fetch_surf_fsaverage()
texture = surface.vol_to_surf(motor_img, fsaverage.pial_right)
texture = texture.clip(0, texture.max())

fig, axes = plt.subplots(nrows=1, ncols=2, subplot_kw={"projection": "3d"})
plotting.plot_surf_stat_map(
    fsaverage.infl_right,
    texture,
    hemi='right',
    title='a subplot',
    colorbar=True,
    threshold=1.,
    axes=axes[0],
    bg_map=fsaverage.sulc_right,
)
plotting.plot_surf_stat_map(
    fsaverage.infl_right,
    texture,
    hemi='right',
    title='an other subplot',
    colorbar=True,
    threshold=1.,
    axes=axes[1],
    bg_map=fsaverage.sulc_right,
)
plotting.show()
```



Discussion:
Thanks for reporting this! 
were you getting bad results with `Axes.set_title`? (for example used with 3d subplots [here](https://matplotlib.org/stable/gallery/mplot3d/wire3d_zero_stride.html) ).
If it gives good results, I slightly prefer using this more high-level method because there is a slightly smaller chance that we will need to tweak some parameters to keep the text for being too far or in front of the brain.
However, if using `set_title` does not look good (for example it results in a lot of wasted space because it is too far from the brain), then the `text` solution is great
The results look good, I only took the .text method as it were used somewhere else. Here is the set_title version, thanks for pointing it out!

![image](https://user-images.githubusercontent.com/12402673/162777053-47dd098e-49da-4c99-af3a-728790dff185.png)

If everyone is on board with this, I'll open a PR with the modification

> If everyone is on board with this, I'll open a PR with the modification

Awesome, thanks!


Resolving patch:
diff --git a/nilearn/plotting/surf_plotting.py b/nilearn/plotting/surf_plotting.py
--- a/nilearn/plotting/surf_plotting.py
+++ b/nilearn/plotting/surf_plotting.py
@@ -519,7 +519,7 @@ def _plot_surf_matplotlib(coords, faces, surf_map=None, bg_map=None,
         p3dcollec.set_facecolors(face_colors)
 
     if title is not None:
-        figure.suptitle(title, x=.5, y=.95, fontsize=title_font_size)
+        axes.set_title(title)
     # save figure if output file is given
     if output_file is not None:
         figure.savefig(output_file)
@@ -866,15 +866,13 @@ def plot_surf_contours(surf_mesh, roi_map, axes=None, figure=None, levels=None,
         if label and legend:
             patch_list.append(Patch(color=color, label=label))
     # plot legend only if indicated and labels provided
-    pos_title_x = .5
     if legend and np.any([lbl is not None for lbl in labels]):
         figure.legend(handles=patch_list)
         # if legends, then move title to the left
-        pos_title_x = .3
     if title is None and hasattr(figure._suptitle, "_text"):
         title = figure._suptitle._text
     if title:
-        figure.suptitle(title, x=pos_title_x, y=.95)
+        axes.set_title(title)
     # save figure if output file is given
     if output_file is not None:
         figure.savefig(output_file)


Test patch:
diff --git a/nilearn/plotting/tests/test_surf_plotting.py b/nilearn/plotting/tests/test_surf_plotting.py
--- a/nilearn/plotting/tests/test_surf_plotting.py
+++ b/nilearn/plotting/tests/test_surf_plotting.py
@@ -259,9 +259,8 @@ def test_plot_surf(engine, tmp_path):
     display = plot_surf(mesh, bg_map=bg, title='Test title',
                         engine=engine)
     if engine == 'matplotlib':
-        assert display._suptitle._text == 'Test title'
-        assert display._suptitle._x == .5
-        assert display._suptitle._y == .95
+        assert len(display.axes) == 1
+        assert display.axes[0].title._text == 'Test title'
 
 
 def test_plot_surf_avg_method():
@@ -438,9 +437,7 @@ def test_plot_surf_stat_map(engine):
     # Plot with title
     display = plot_surf_stat_map(mesh, stat_map=data, bg_map=bg,
                                  title="Stat map title")
-    assert display._suptitle._text == "Stat map title"
-    assert display._suptitle._x == .5
-    assert display._suptitle._y == .95
+    assert display.axes[0].title._text == "Stat map title"
 
     # Apply threshold
     plot_surf_stat_map(mesh, stat_map=data, bg_map=bg,
@@ -785,16 +782,16 @@ def test_plot_surf_contours():
                                  labels=['1', '2'], colors=['r', 'g'],
                                  legend=True, title='title',
                                  figure=fig)
-    assert display._suptitle._text == 'title'
-    assert display._suptitle._x == .3
-    assert display._suptitle._y == .95
+    # Non-regression assertion: we switched from _suptitle to axis title
+    assert display._suptitle is None
+    assert display.axes[0].get_title() == "title"
     fig = plot_surf(mesh, title='title 2')
     display = plot_surf_contours(mesh, parcellation, levels=[1, 2],
                                  labels=['1', '2'], colors=['r', 'g'],
                                  legend=True, figure=fig)
-    assert display._suptitle._text == 'title 2'
-    assert display._suptitle._x == .3
-    assert display._suptitle._y == .95
+    # Non-regression assertion: we switched from _suptitle to axis title
+    assert display._suptitle is None
+    assert display.axes[0].get_title() == "title 2"
     with tempfile.NamedTemporaryFile() as tmp_file:
         plot_surf_contours(mesh, parcellation, output_file=tmp_file.name)
     plt.close()