Repository: nilearn/nilearn

Issue:
[BUG] Plot surf ROI with plotly engine cannot do symmetric colorbars
### Is there an existing issue for this?

- [X] I have searched the existing issues

### Operating system

- [ ] Linux
- [X] Mac
- [ ] Windows

### Operating system version

- Mac OS Version 13.4.1 "Ventura"


### Python version

- [ ] 3.11
- [ ] 3.10
- [X] 3.9
- [ ] 3.8
- [ ] 3.7

### nilearn version

0.10.0

### Expected behavior

The colorbar should be symmetric when using the plotly engine as per [the nilearn docs](https://nilearn.github.io/stable/modules/generated/nilearn.plotting.plot_surf.html#nilearn.plotting.plot_surf). Nilearn shouldn't throw out an error.

### Current behavior & error messages

This is what I got:



```python
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
[/Users/eurunuela/GitHub/papers_msPFM/validation_iteration.ipynb](https://file+.vscode-resource.vscode-cdn.net/Users/eurunuela/GitHub/papers_msPFM/validation_iteration.ipynb) Cell 23 in ()
      [7](vscode-notebook-cell:/Users/eurunuela/GitHub/papers_msPFM/validation_iteration.ipynb#Y144sZmlsZQ%3D%3D?line=6) corr = np.squeeze(corr)
     [10](vscode-notebook-cell:/Users/eurunuela/GitHub/papers_msPFM/validation_iteration.ipynb#Y144sZmlsZQ%3D%3D?line=9) texture = vol_to_surf(utils.color_rois(out_dir, mask, corr), fsaverage.pial_left,interpolation='nearest',radius =1, n_samples=1)
---> [11](vscode-notebook-cell:/Users/eurunuela/GitHub/papers_msPFM/validation_iteration.ipynb#Y144sZmlsZQ%3D%3D?line=10) surf_plot1=plotting.plot_surf_roi(fsaverage.infl_left, texture, hemi='left',cmap = cmap, colorbar=False, symmetric_cmap=True, engine='plotly',
     [12](vscode-notebook-cell:/Users/eurunuela/GitHub/papers_msPFM/validation_iteration.ipynb#Y144sZmlsZQ%3D%3D?line=11)                                 bg_map=fsaverage.sulc_left,vmax=vmax,vmin=vmin)
     [14](vscode-notebook-cell:/Users/eurunuela/GitHub/papers_msPFM/validation_iteration.ipynb#Y144sZmlsZQ%3D%3D?line=13) texture = vol_to_surf(utils.color_rois(out_dir, mask, corr), fsaverage.pial_right,interpolation='nearest',radius =1, n_samples=1)
     [15](vscode-notebook-cell:/Users/eurunuela/GitHub/papers_msPFM/validation_iteration.ipynb#Y144sZmlsZQ%3D%3D?line=14) surf_plot3=plotting.plot_surf_roi(fsaverage.infl_right, texture, hemi='right',cmap = cmap, colorbar=False,symmetric_cmap=True, engine='plotly',
     [16](vscode-notebook-cell:/Users/eurunuela/GitHub/papers_msPFM/validation_iteration.ipynb#Y144sZmlsZQ%3D%3D?line=15)                                 bg_map=fsaverage.sulc_right,vmax=vmax,vmin=vmin)#

File [~/conda_envs/msPFM/lib/python3.8/site-packages/nilearn/plotting/surf_plotting.py:1415](https://file+.vscode-resource.vscode-cdn.net/Users/eurunuela/GitHub/papers_msPFM/~/conda_envs/msPFM/lib/python3.8/site-packages/nilearn/plotting/surf_plotting.py:1415), in plot_surf_roi(surf_mesh, roi_map, bg_map, hemi, view, engine, threshold, alpha, vmin, vmax, cmap, cbar_tick_format, bg_on_data, darkness, title, title_font_size, output_file, axes, figure, **kwargs)
   1413 if cbar_tick_format == "auto":
   1414     cbar_tick_format = "." if engine == "plotly" else "%i"
-> 1415 display = plot_surf(mesh, surf_map=roi, bg_map=bg_map,
   1416                     hemi=hemi, view=view, engine=engine,
   1417                     avg_method='median', threshold=threshold,
   1418                     cmap=cmap, symmetric_cmap=False,
   1419                     cbar_tick_format=cbar_tick_format,
   1420                     alpha=alpha, bg_on_data=bg_on_data,
   1421                     darkness=darkness, vmin=vmin, vmax=vmax,
   1422                     title=title, title_font_size=title_font_size,
   1423                     output_file=output_file, axes=axes,
   1424                     figure=figure, **kwargs)
   1426 return display

TypeError: plot_surf() got multiple values for keyword argument 'symmetric_cmap'


```


### Steps and code to reproduce bug

```python
# Paste your code here
cmap = "RdYlBu_r"
# cmap = "viridis"
vmax = 0.4
# vmin = vmax/data.shape[1] * 60
vmin = -0.4
corr = masker.transform(os.path.join(out_dir, f"{img}_sbj_corr_only.nii.gz"))
corr = np.squeeze(corr)


texture = vol_to_surf(utils.color_rois(out_dir, mask, corr), fsaverage.pial_left,interpolation='nearest',radius =1, n_samples=1)
surf_plot1=plotting.plot_surf_roi(fsaverage.infl_left, texture, hemi='left',cmap = cmap, colorbar=False, symmetric_cmap=True, engine='plotly',
                                bg_map=fsaverage.sulc_left,vmax=vmax,vmin=vmin)
```

[BUG] Plot surf ROI with plotly engine cannot do symmetric colorbars
### Is there an existing issue for this?

- [X] I have searched the existing issues

### Operating system

- [ ] Linux
- [X] Mac
- [ ] Windows

### Operating system version

- Mac OS Version 13.4.1 "Ventura"


### Python version

- [ ] 3.11
- [ ] 3.10
- [X] 3.9
- [ ] 3.8
- [ ] 3.7

### nilearn version

0.10.0

### Expected behavior

The colorbar should be symmetric when using the plotly engine as per [the nilearn docs](https://nilearn.github.io/stable/modules/generated/nilearn.plotting.plot_surf.html#nilearn.plotting.plot_surf). Nilearn shouldn't throw out an error.

### Current behavior & error messages

This is what I got:



```python
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
[/Users/eurunuela/GitHub/papers_msPFM/validation_iteration.ipynb](https://file+.vscode-resource.vscode-cdn.net/Users/eurunuela/GitHub/papers_msPFM/validation_iteration.ipynb) Cell 23 in ()
      [7](vscode-notebook-cell:/Users/eurunuela/GitHub/papers_msPFM/validation_iteration.ipynb#Y144sZmlsZQ%3D%3D?line=6) corr = np.squeeze(corr)
     [10](vscode-notebook-cell:/Users/eurunuela/GitHub/papers_msPFM/validation_iteration.ipynb#Y144sZmlsZQ%3D%3D?line=9) texture = vol_to_surf(utils.color_rois(out_dir, mask, corr), fsaverage.pial_left,interpolation='nearest',radius =1, n_samples=1)
---> [11](vscode-notebook-cell:/Users/eurunuela/GitHub/papers_msPFM/validation_iteration.ipynb#Y144sZmlsZQ%3D%3D?line=10) surf_plot1=plotting.plot_surf_roi(fsaverage.infl_left, texture, hemi='left',cmap = cmap, colorbar=False, symmetric_cmap=True, engine='plotly',
     [12](vscode-notebook-cell:/Users/eurunuela/GitHub/papers_msPFM/validation_iteration.ipynb#Y144sZmlsZQ%3D%3D?line=11)                                 bg_map=fsaverage.sulc_left,vmax=vmax,vmin=vmin)
     [14](vscode-notebook-cell:/Users/eurunuela/GitHub/papers_msPFM/validation_iteration.ipynb#Y144sZmlsZQ%3D%3D?line=13) texture = vol_to_surf(utils.color_rois(out_dir, mask, corr), fsaverage.pial_right,interpolation='nearest',radius =1, n_samples=1)
     [15](vscode-notebook-cell:/Users/eurunuela/GitHub/papers_msPFM/validation_iteration.ipynb#Y144sZmlsZQ%3D%3D?line=14) surf_plot3=plotting.plot_surf_roi(fsaverage.infl_right, texture, hemi='right',cmap = cmap, colorbar=False,symmetric_cmap=True, engine='plotly',
     [16](vscode-notebook-cell:/Users/eurunuela/GitHub/papers_msPFM/validation_iteration.ipynb#Y144sZmlsZQ%3D%3D?line=15)                                 bg_map=fsaverage.sulc_right,vmax=vmax,vmin=vmin)#

File [~/conda_envs/msPFM/lib/python3.8/site-packages/nilearn/plotting/surf_plotting.py:1415](https://file+.vscode-resource.vscode-cdn.net/Users/eurunuela/GitHub/papers_msPFM/~/conda_envs/msPFM/lib/python3.8/site-packages/nilearn/plotting/surf_plotting.py:1415), in plot_surf_roi(surf_mesh, roi_map, bg_map, hemi, view, engine, threshold, alpha, vmin, vmax, cmap, cbar_tick_format, bg_on_data, darkness, title, title_font_size, output_file, axes, figure, **kwargs)
   1413 if cbar_tick_format == "auto":
   1414     cbar_tick_format = "." if engine == "plotly" else "%i"
-> 1415 display = plot_surf(mesh, surf_map=roi, bg_map=bg_map,
   1416                     hemi=hemi, view=view, engine=engine,
   1417                     avg_method='median', threshold=threshold,
   1418                     cmap=cmap, symmetric_cmap=False,
   1419                     cbar_tick_format=cbar_tick_format,
   1420                     alpha=alpha, bg_on_data=bg_on_data,
   1421                     darkness=darkness, vmin=vmin, vmax=vmax,
   1422                     title=title, title_font_size=title_font_size,
   1423                     output_file=output_file, axes=axes,
   1424                     figure=figure, **kwargs)
   1426 return display

TypeError: plot_surf() got multiple values for keyword argument 'symmetric_cmap'


```


### Steps and code to reproduce bug

```python
# Paste your code here
cmap = "RdYlBu_r"
# cmap = "viridis"
vmax = 0.4
# vmin = vmax/data.shape[1] * 60
vmin = -0.4
corr = masker.transform(os.path.join(out_dir, f"{img}_sbj_corr_only.nii.gz"))
corr = np.squeeze(corr)


texture = vol_to_surf(utils.color_rois(out_dir, mask, corr), fsaverage.pial_left,interpolation='nearest',radius =1, n_samples=1)
surf_plot1=plotting.plot_surf_roi(fsaverage.infl_left, texture, hemi='left',cmap = cmap, colorbar=False, symmetric_cmap=True, engine='plotly',
                                bg_map=fsaverage.sulc_left,vmax=vmax,vmin=vmin)
```



Discussion:
Can reproduce with the following



```python
from nilearn.plotting.surf_plotting import plot_surf_roi

from nilearn.surface.testing_utils import generate_surf

def _generate_data_test_surf_roi():
    mesh = generate_surf()
    rng = np.random.RandomState(42)
    roi_idx = rng.randint(0, mesh[0].shape[0], size=10)
    roi_map = np.zeros(mesh[0].shape[0])
    roi_map[roi_idx] = 1
    parcellation = rng.uniform(size=mesh[0].shape[0])
    return mesh, roi_map, parcellation

def test_plot_surf_roi_plotly_sym_map():
    if not PLOTLY_INSTALLED:
        pytest.skip('Plotly is not installed; required for this test.')
    mesh, roi_map, _ = _generate_data_test_surf_roi()
    # plot roi
    vmax = 0.4
    vmin = -0.4
    cmap = "RdYlBu_r"
    plot_surf_roi(mesh, roi_map=roi_map,  
                  hemi='left',
                  colorbar=False, 
                  engine="plotly", 
                  symmetric_cmap=True, 
                  cmap=cmap,
                  vmax=vmax,
                  vmin=vmin)
```
Need to check but it may make sense to fix as part of https://github.com/nilearn/nilearn/pull/2887
Can reproduce with the following



```python
from nilearn.plotting.surf_plotting import plot_surf_roi

from nilearn.surface.testing_utils import generate_surf

def _generate_data_test_surf_roi():
    mesh = generate_surf()
    rng = np.random.RandomState(42)
    roi_idx = rng.randint(0, mesh[0].shape[0], size=10)
    roi_map = np.zeros(mesh[0].shape[0])
    roi_map[roi_idx] = 1
    parcellation = rng.uniform(size=mesh[0].shape[0])
    return mesh, roi_map, parcellation

def test_plot_surf_roi_plotly_sym_map():
    if not PLOTLY_INSTALLED:
        pytest.skip('Plotly is not installed; required for this test.')
    mesh, roi_map, _ = _generate_data_test_surf_roi()
    # plot roi
    vmax = 0.4
    vmin = -0.4
    cmap = "RdYlBu_r"
    plot_surf_roi(mesh, roi_map=roi_map,  
                  hemi='left',
                  colorbar=False, 
                  engine="plotly", 
                  symmetric_cmap=True, 
                  cmap=cmap,
                  vmax=vmax,
                  vmin=vmin)
```
Need to check but it may make sense to fix as part of https://github.com/nilearn/nilearn/pull/2887

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
@@ -1469,14 +1469,27 @@ def plot_img_on_surf(stat_map, surf_mesh='fsaverage5', mask_img=None,
 
 
 @fill_doc
-def plot_surf_roi(surf_mesh, roi_map, bg_map=None,
-                  hemi='left', view='lateral', engine='matplotlib',
-                  threshold=1e-14, alpha='auto', vmin=None, vmax=None,
-                  cmap='gist_ncar', cbar_tick_format="auto",
-                  bg_on_data=False, darkness=.7,
-                  title=None, title_font_size=18,
-                  output_file=None, axes=None,
-                  figure=None, **kwargs):
+def plot_surf_roi(surf_mesh,
+                  roi_map,
+                  bg_map=None,
+                  hemi='left',
+                  view='lateral',
+                  engine='matplotlib',
+                  avg_method='median',
+                  threshold=1e-14,
+                  alpha='auto',
+                  vmin=None,
+                  vmax=None,
+                  cmap='gist_ncar',
+                  cbar_tick_format="auto",
+                  bg_on_data=False,
+                  darkness=0.7,
+                  title=None,
+                  title_font_size=18,
+                  output_file=None,
+                  axes=None,
+                  figure=None,
+                  **kwargs):
     """Plot ROI on a surface mesh with optional background.
 
     .. versionadded:: 0.3
@@ -1531,6 +1544,14 @@ def plot_surf_roi(surf_mesh, roi_map, bg_map=None,
 
         Default='matplotlib'.
 
+    %(avg_method)s
+
+        .. note::
+            This option is currently only implemented for the
+            ``matplotlib`` engine.
+
+        Default='median'.
+
     threshold : a number or None, optional
         Threshold regions that are labelled 0.
         If you want to use 0 as a label, set threshold to None.
@@ -1623,15 +1644,27 @@ def plot_surf_roi(surf_mesh, roi_map, bg_map=None,
 
     if cbar_tick_format == "auto":
         cbar_tick_format = "." if engine == "plotly" else "%i"
-    display = plot_surf(mesh, surf_map=roi, bg_map=bg_map,
-                        hemi=hemi, view=view, engine=engine,
-                        avg_method='median', threshold=threshold,
-                        cmap=cmap, symmetric_cmap=False,
+
+    display = plot_surf(mesh,
+                        surf_map=roi,
+                        bg_map=bg_map,
+                        hemi=hemi,
+                        view=view,
+                        engine=engine,
+                        avg_method=avg_method,
+                        threshold=threshold,
+                        cmap=cmap,
                         cbar_tick_format=cbar_tick_format,
-                        alpha=alpha, bg_on_data=bg_on_data,
-                        darkness=darkness, vmin=vmin, vmax=vmax,
-                        title=title, title_font_size=title_font_size,
-                        output_file=output_file, axes=axes,
-                        figure=figure, **kwargs)
+                        alpha=alpha,
+                        bg_on_data=bg_on_data,
+                        darkness=darkness,
+                        vmin=vmin,
+                        vmax=vmax,
+                        title=title,
+                        title_font_size=title_font_size,
+                        output_file=output_file,
+                        axes=axes,
+                        figure=figure,
+                        **kwargs)
 
     return display


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
@@ -1115,3 +1115,19 @@ def test_compute_facecolors_matplotlib():
             0.5,
             alpha,
         )
+
+
+@pytest.mark.parametrize("avg_method", ["mean", "median"])
+@pytest.mark.parametrize("symmetric_cmap", [True, False, None])
+@pytest.mark.parametrize("engine", ["matplotlib", "plotly"])
+def test_plot_surf_roi_default_arguments(engine, symmetric_cmap, avg_method):
+    """Regression test for https://github.com/nilearn/nilearn/issues/3941"""
+    if not PLOTLY_INSTALLED:
+        pytest.skip('Plotly is not installed; required for this test.')
+    mesh, roi_map, _ = _generate_data_test_surf_roi()
+    plot_surf_roi(mesh, roi_map=roi_map,
+                  engine=engine,
+                  symmetric_cmap=symmetric_cmap,
+                  darkness=None,  # to avoid deprecation warning
+                  cmap="RdYlBu_r",
+                  avg_method=avg_method)