Repository: nilearn/nilearn

Issue:
Adding a vmin option to plot_img_on_surf
I notice that the function "plotting.plot_img_on_surf" still does not support a "vmin" argument (nilearn version 0.10.0). If I enter a vmin parameter, it will raise an error saying "ValueError: this function does not accept a "vmin" argument, as it uses a symmetrical range defined via the vmax argument. To threshold the plotted map, use the "threshold" argument".
But unfortunately, the "threshold" argument still brings out a symetrical colormap. The colorbar always contains symetrical positive and negative sides. How could I set the vmin of colorbar when plotting images on surface, e.g. showing only the positive values?



Discussion:
The inconsistency between the docstring and the actual behavior is a bug. I think that the function should accept the `vmin` parameter.
If you simply want not to plot negative values, you can always use something like `values[values < 0] = 0`, but the colorbar will still be symmetric.  
OK.
Thanks！
Linking issue #3084
I can try working on this issue
@michellewang awesome go for it!

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
@@ -60,42 +60,63 @@ def show():
 # Core, usage-agnostic functions
 
 
-def _get_colorbar_and_data_ranges(stat_map_data, vmax, symmetric_cbar, kwargs,
-                                  force_min_stat_map_value=None):
+def _get_colorbar_and_data_ranges(
+    stat_map_data, vmin=None, vmax=None, symmetric_cbar=True,
+    force_min_stat_map_value=None, symmetric_data_range=True,
+):
     """Set colormap and colorbar limits.
 
-    Used by for plot_stat_map and plot_glass_brain.
-
-    The limits for the colormap will always be set to range from -vmax to vmax.
-    The limits for the colorbar depend on the symmetric_cbar argument, please
-    refer to docstring of plot_stat_map.
+    Used by plot_stat_map, plot_glass_brain and plot_img_on_surf.
 
+    If symmetric_data_range is True, the limits for the colormap will
+    always be set to range from -vmax to vmax. The limits for the colorbar
+    depend on the symmetric_cbar argument, please refer to docstring of
+    plot_stat_map.
     """
-    if 'vmin' in kwargs:
+    if symmetric_data_range and (vmin is not None):
         raise ValueError('this function does not accept a "vmin" '
                          'argument, as it uses a symmetrical range '
                          'defined via the vmax argument. To threshold '
                          'the plotted map, use the "threshold" argument')
 
-    # make sure that the color range is symmetrical
-    if vmax is None or symmetric_cbar in ['auto', False]:
-        # Avoid dealing with masked_array:
-        if hasattr(stat_map_data, '_mask'):
-            stat_map_data = np.asarray(
-                stat_map_data[np.logical_not(stat_map_data._mask)])
-        stat_map_max = np.nanmax(stat_map_data)
-        if force_min_stat_map_value is None:
-            stat_map_min = np.nanmin(stat_map_data)
-        else:
-            stat_map_min = force_min_stat_map_value
+    # avoid dealing with masked_array:
+    if hasattr(stat_map_data, '_mask'):
+        stat_map_data = np.asarray(
+            stat_map_data[np.logical_not(stat_map_data._mask)])
 
-    if symmetric_cbar == 'auto':
-        symmetric_cbar = stat_map_min < 0 and stat_map_max > 0
+    if force_min_stat_map_value is None:
+        stat_map_min = np.nanmin(stat_map_data)
+    else:
+        stat_map_min = force_min_stat_map_value
+    stat_map_max = np.nanmax(stat_map_data)
 
+    if symmetric_cbar == "auto":
+        if symmetric_data_range or (vmin is None) or (vmax is None):
+            symmetric_cbar = stat_map_min < 0 and stat_map_max > 0
+        else:
+            symmetric_cbar = np.isclose(vmin, -vmax)
+
+    # check compatibility between vmin, vmax and symmetric_cbar
+    if symmetric_cbar or symmetric_data_range:
+        if vmin is None and vmax is None:
+            vmax = max(-stat_map_min, stat_map_max)
+            vmin = -vmax
+        elif vmin is None:
+            vmin = -vmax
+        elif vmax is None:
+            vmax = -vmin
+        elif not np.isclose(vmin, -vmax):
+            raise ValueError(
+                "vmin must be equal to -vmax unless symmetric_cbar is False."
+            )
+
+    # set vmin/vmax based on data if they are not already set
+    if vmin is None:
+        vmin = stat_map_min
     if vmax is None:
-        vmax = max(-stat_map_min, stat_map_max)
-    vmin = -vmax
+        vmax = stat_map_max
 
+    # set colorbar limits
     if not symmetric_cbar:
         negative_range = stat_map_max <= 0
         positive_range = stat_map_min >= 0
@@ -110,6 +131,7 @@ def _get_colorbar_and_data_ranges(stat_map_data, vmax, symmetric_cbar, kwargs,
             cbar_vmax = stat_map_max
     else:
         cbar_vmin, cbar_vmax = None, None
+
     return cbar_vmin, cbar_vmax, vmin, vmax
 
 
@@ -883,8 +905,8 @@ def plot_stat_map(stat_map_img, bg_img=MNI152TEMPLATE, cut_coords=None,
                   cbar_tick_format="%.2g", figure=None, axes=None,
                   title=None, threshold=1e-6, annotate=True, draw_cross=True,
                   black_bg='auto', cmap=cm.cold_hot, symmetric_cbar="auto",
-                  dim='auto', vmax=None, resampling_interpolation='continuous',
-                  radiological=False, **kwargs):
+                  dim='auto', vmin=None, vmax=None, radiological=False,
+                  resampling_interpolation='continuous', **kwargs):
     """Plot cuts of an ROI/mask image.
 
     By default 3 cuts: Frontal, Axial, and Lateral.
@@ -926,6 +948,7 @@ def plot_stat_map(stat_map_img, bg_img=MNI152TEMPLATE, cut_coords=None,
         Default='auto'.
     %(dim)s
         Default='auto'.
+    %(vmin)s
     %(vmax)s
     %(resampling_interpolation)s
         Default='continuous'.
@@ -953,9 +976,9 @@ def plot_stat_map(stat_map_img, bg_img=MNI152TEMPLATE, cut_coords=None,
 
     cbar_vmin, cbar_vmax, vmin, vmax = _get_colorbar_and_data_ranges(
         _safe_get_data(stat_map_img, ensure_finite=True),
-        vmax,
-        symmetric_cbar,
-        kwargs)
+        vmin=vmin,
+        vmax=vmax,
+        symmetric_cbar=symmetric_cbar)
 
     display = _plot_img_with_bg(
         img=stat_map_img, bg_img=bg_img, cut_coords=cut_coords,
@@ -1056,16 +1079,16 @@ def plot_glass_brain(stat_map_img,
         if plot_abs:
             cbar_vmin, cbar_vmax, vmin, vmax = _get_colorbar_and_data_ranges(
                 _safe_get_data(stat_map_img, ensure_finite=True),
-                vmax,
-                symmetric_cbar,
-                kwargs,
-                0)
+                vmin=vmin,
+                vmax=vmax,
+                symmetric_cbar=symmetric_cbar,
+                force_min_stat_map_value=0)
         else:
             cbar_vmin, cbar_vmax, vmin, vmax = _get_colorbar_and_data_ranges(
                 _safe_get_data(stat_map_img, ensure_finite=True),
-                vmax,
-                symmetric_cbar,
-                kwargs)
+                vmin=vmin,
+                vmax=vmax,
+                symmetric_cbar=symmetric_cbar)
     else:
         cbar_vmin, cbar_vmax = None, None
 
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
@@ -486,14 +486,21 @@ def _threshold_and_rescale(data, threshold, vmin, vmax):
     This function thresholds and rescales the provided data.
     """
     data_copy, vmin, vmax = _rescale(data, vmin, vmax)
-    return data_copy, _threshold(data, threshold), vmin, vmax
+    return data_copy, _threshold(data, threshold, vmin, vmax), vmin, vmax
 
 
-def _threshold(data, threshold):
+def _threshold(data, threshold, vmin, vmax):
     """Thresholds the data."""
     # If no thresholding and nans, filter them out
-    return (np.logical_not(np.isnan(data)) if threshold is None
-            else np.abs(data) >= threshold)
+    if threshold is None:
+        mask = np.logical_not(np.isnan(data))
+    else:
+        mask = np.abs(data) >= threshold
+        if vmin > -threshold:
+            mask = np.logical_and(mask, data >= vmin)
+        if vmax < threshold:
+            mask = np.logical_and(mask, data <= vmax)
+    return mask
 
 
 def _rescale(data, vmin=None, vmax=None):
@@ -625,8 +632,7 @@ def plot_surf(surf_mesh, surf_map=None, bg_map=None,
               hemi='left', view='lateral', engine='matplotlib',
               cmap=None, symmetric_cmap=False, colorbar=False,
               avg_method='mean', threshold=None, alpha='auto',
-              bg_on_data=False, darkness=.7,
-              vmin=None, vmax=None,
+              bg_on_data=False, darkness=.7, vmin=None, vmax=None,
               cbar_vmin=None, cbar_vmax=None, cbar_tick_format="auto",
               title=None, title_font_size=18, output_file=None, axes=None,
               figure=None, **kwargs):
@@ -798,8 +804,7 @@ def plot_surf(surf_mesh, surf_map=None, bg_map=None,
         fig = _plot_surf_matplotlib(
             coords, faces, surf_map=surf_map, bg_map=bg_map, hemi=hemi,
             view=view, cmap=cmap, colorbar=colorbar, avg_method=avg_method,
-            threshold=threshold, alpha=alpha,
-            bg_on_data=bg_on_data,
+            threshold=threshold, alpha=alpha, bg_on_data=bg_on_data,
             darkness=darkness, vmin=vmin, vmax=vmax, cbar_vmin=cbar_vmin,
             cbar_vmax=cbar_vmax, cbar_tick_format=cbar_tick_format,
             title=title, title_font_size=title_font_size,
@@ -979,10 +984,9 @@ def plot_surf_contours(surf_mesh, roi_map, axes=None, figure=None, levels=None,
 @fill_doc
 def plot_surf_stat_map(surf_mesh, stat_map, bg_map=None,
                        hemi='left', view='lateral', engine='matplotlib',
-                       threshold=None, alpha='auto', vmax=None,
-                       cmap='cold_hot', colorbar=True,
-                       symmetric_cbar="auto", cbar_tick_format="auto",
-                       bg_on_data=False, darkness=.7,
+                       threshold=None, alpha='auto', vmin=None, vmax=None,
+                       cmap='cold_hot', colorbar=True, symmetric_cbar="auto",
+                       cbar_tick_format="auto", bg_on_data=False, darkness=.7,
                        title=None, title_font_size=18, output_file=None,
                        axes=None, figure=None, **kwargs):
     """Plot a stats map on a surface mesh with optional background.
@@ -1069,6 +1073,7 @@ def plot_surf_stat_map(surf_mesh, stat_map, bg_map=None,
             This option is currently only implemented for the
             ``matplotlib`` engine.
 
+    %(vmin)s
     %(vmax)s
     %(symmetric_cbar)s
         Default='auto'.
@@ -1123,7 +1128,12 @@ def plot_surf_stat_map(surf_mesh, stat_map, bg_map=None,
     # Call _get_colorbar_and_data_ranges to derive symmetric vmin, vmax
     # And colorbar limits depending on symmetric_cbar settings
     cbar_vmin, cbar_vmax, vmin, vmax = _get_colorbar_and_data_ranges(
-        loaded_stat_map, vmax, symmetric_cbar, kwargs)
+        loaded_stat_map,
+        vmin=vmin,
+        vmax=vmax,
+        symmetric_cbar=symmetric_cbar,
+        symmetric_data_range=False,
+    )
 
     display = plot_surf(
         surf_mesh, surf_map=loaded_stat_map,
@@ -1211,7 +1221,7 @@ def _check_views(views) -> list:
     return views
 
 
-def _colorbar_from_array(array, vmax, threshold, kwargs,
+def _colorbar_from_array(array, vmin, vmax, threshold, symmetric_cbar=True,
                          cmap='cold_hot'):
     """Generate a custom colorbar for an array.
 
@@ -1220,6 +1230,9 @@ def _colorbar_from_array(array, vmax, threshold, kwargs,
     array : np.ndarray
         Any 3D array.
 
+    vmin : float
+        lower bound for plotting of stat_map values.
+
     vmax : float
         upper bound for plotting of stat_map values.
 
@@ -1236,8 +1249,12 @@ def _colorbar_from_array(array, vmax, threshold, kwargs,
         Default='cold_hot'.
 
     """
-    cbar_vmin, cbar_vmax, vmin, vmax = _get_colorbar_and_data_ranges(
-        array, vmax, True, kwargs
+    _, _, vmin, vmax = _get_colorbar_and_data_ranges(
+        array,
+        vmin=vmin,
+        vmax=vmax,
+        symmetric_cbar=symmetric_cbar,
+        symmetric_data_range=False,
     )
     norm = Normalize(vmin=vmin, vmax=vmax)
     cmaplist = [cmap(i) for i in range(cmap.N)]
@@ -1252,8 +1269,8 @@ def _colorbar_from_array(array, vmax, threshold, kwargs,
         cmaplist[i] = (0.5, 0.5, 0.5, 1.)
     our_cmap = LinearSegmentedColormap.from_list('Custom cmap',
                                                  cmaplist, cmap.N)
-    sm = plt.cm.ScalarMappable(cmap=our_cmap,
-                               norm=plt.Normalize(vmin=vmin, vmax=vmax))
+    sm = plt.cm.ScalarMappable(cmap=our_cmap, norm=norm)
+
     # fake up the array of the scalar mappable.
     sm._A = []
 
@@ -1262,12 +1279,11 @@ def _colorbar_from_array(array, vmax, threshold, kwargs,
 
 @fill_doc
 def plot_img_on_surf(stat_map, surf_mesh='fsaverage5', mask_img=None,
-                     hemispheres=['left', 'right'],
-                     bg_on_data=False, inflate=False,
-                     views=['lateral', 'medial'],
+                     hemispheres=['left', 'right'], bg_on_data=False,
+                     inflate=False, views=['lateral', 'medial'],
                      output_file=None, title=None, colorbar=True,
-                     vmax=None, threshold=None,
-                     cmap='cold_hot', **kwargs):
+                     vmin=None, vmax=None, threshold=None,
+                     symmetric_cbar='auto', cmap='cold_hot', **kwargs):
     """Plot multiple views of plot_surf_stat_map \
     in a single figure.
 
@@ -1321,8 +1337,14 @@ def plot_img_on_surf(stat_map, surf_mesh='fsaverage5', mask_img=None,
             This function uses a symmetric colorbar for the statistical map.
 
         Default=True.
+    %(vmin)s
     %(vmax)s
     %(threshold)s
+    symmetric_cbar : :obj:`bool`, or "auto", optional
+        Specifies whether the colorbar should range from `-vmax` to `vmax`
+        (or from `vmin` to `-vmin` if `-vmin` is greater than `vmax`) or
+        from `vmin` to `vmax`.
+        Default=True.
     %(cmap)s
         Default='cold_hot'.
     kwargs : dict, optional
@@ -1339,7 +1361,7 @@ def plot_img_on_surf(stat_map, surf_mesh='fsaverage5', mask_img=None,
         accepted by plot_img_on_surf.
 
     """
-    for arg in ('figure', 'axes'):
+    for arg in ("figure", "axes", "engine"):
         if arg in kwargs:
             raise ValueError(
                 'plot_img_on_surf does not accept '
@@ -1374,6 +1396,15 @@ def plot_img_on_surf(stat_map, surf_mesh='fsaverage5', mask_img=None,
         height_ratios=height_ratios, hspace=0.0, wspace=0.0)
     axes = []
 
+    # get vmin and vmax for entire data (all hemis)
+    _, _, vmin, vmax = _get_colorbar_and_data_ranges(
+        image.get_data(stat_map),
+        vmin=vmin,
+        vmax=vmax,
+        symmetric_cbar=symmetric_cbar,
+        symmetric_data_range=False,
+    )
+
     for i, (mode, hemi) in enumerate(itertools.product(modes, hemis)):
         bg_map = None
         # By default, add curv sign background map if mesh is inflated,
@@ -1390,25 +1421,37 @@ def plot_img_on_surf(stat_map, surf_mesh='fsaverage5', mask_img=None,
 
         ax = fig.add_subplot(grid[i + len(hemis)], projection="3d")
         axes.append(ax)
-        plot_surf_stat_map(surf[hemi], texture[hemi],
-                           view=mode, hemi=hemi,
-                           bg_map=bg_map,
-                           bg_on_data=bg_on_data,
-                           axes=ax,
-                           colorbar=False,  # Colorbar created externally.
-                           vmax=vmax,
-                           threshold=threshold,
-                           cmap=cmap,
-                           **kwargs)
-        # ax.set_facecolor("#e0e0e0")
+
+        plot_surf_stat_map(
+            surf[hemi],
+            texture[hemi],
+            view=mode,
+            hemi=hemi,
+            bg_map=bg_map,
+            bg_on_data=bg_on_data,
+            axes=ax,
+            colorbar=False,  # Colorbar created externally.
+            vmin=vmin,
+            vmax=vmax,
+            threshold=threshold,
+            cmap=cmap,
+            symmetric_cbar=symmetric_cbar,
+            **kwargs,
+        )
+
         # We increase this value to better position the camera of the
         # 3D projection plot. The default value makes meshes look too small.
         ax.set_box_aspect(None, zoom=1.3)
 
     if colorbar:
-        sm = _colorbar_from_array(image.get_data(stat_map),
-                                  vmax, threshold, kwargs,
-                                  cmap=plt.get_cmap(cmap))
+        sm = _colorbar_from_array(
+            image.get_data(stat_map),
+            vmin,
+            vmax,
+            threshold,
+            symmetric_cbar=symmetric_cbar,
+            cmap=plt.get_cmap(cmap),
+        )
 
         cbar_grid = gridspec.GridSpecFromSubplotSpec(3, 3, grid[-1, :])
         cbar_ax = fig.add_subplot(cbar_grid[1])


Test patch:
diff --git a/nilearn/plotting/tests/test_img_plotting/test_get_colorbar_and_data_ranges.py b/nilearn/plotting/tests/test_img_plotting/test_get_colorbar_and_data_ranges.py
--- a/nilearn/plotting/tests/test_img_plotting/test_get_colorbar_and_data_ranges.py
+++ b/nilearn/plotting/tests/test_img_plotting/test_get_colorbar_and_data_ranges.py
@@ -22,77 +22,103 @@
 data_masked = np.ma.masked_greater(data_pos_neg, 2.0)
 
 
-def test_get_colorbar_and_data_ranges_with_vmin():
-    """Tests for _get_colorbar_and_data_ranges.
-
-    Tests that a ValueError is raised when vmin and
-    symmetric_cbar are both provided.
-    """
-    with pytest.raises(ValueError, match='does not accept a "vmin" argument'):
-        _get_colorbar_and_data_ranges(
-            data_pos_neg, vmax=None, symmetric_cbar=True, kwargs={"vmin": 1.0}
-        )
-
-
-def _expected_results_pos_neg(symmetric_cbar, vmax, data):
+def _expected_results_pos_neg(
+    symmetric_cbar, vmin, vmax, symmetric_data_range, data
+):
     """Help for expected_results.
 
     Return the expected `cbar_vmin`, `cbar_vmax`, `vmin`,
-    and `vmax` for general case.
+    and `vmax` for general case (masked or unmasked).
     """
+    data_min = np.nanmin(data)
     data_max = np.nanmax(data)
     if symmetric_cbar:
-        if vmax is None:
+        if vmax is None and vmin is None:
             return (None, None, -data_max, data_max)
         else:
             return (None, None, -2, 2)
-    else:
+    elif symmetric_data_range:
         if vmax is None:
-            return (np.nanmin(data), data_max, -data_max, data_max)
+            return (data_min, data_max, -data_max, data_max)
         else:
-            return (np.nanmin(data), data_max, -2, 2)
+            return (data_min, data_max, -2, 2)
+    else:
+        if vmin is None and vmax is None:
+            return (data_min, data_max, data_min, data_max)
+        elif vmin is None:
+            return (data_min, data_max, data_min, vmax)
+        elif vmax is None:
+            return (data_min, data_max, vmin, data_max)
+        else:
+            return (data_min, data_max, vmin, vmax)
 
 
-def _expected_results_pos(symmetric_cbar, vmax, data):
+def _expected_results_pos(
+    symmetric_cbar, vmin, vmax, symmetric_data_range, data
+):
     """Help for expected_results.
 
     Return the expected `cbar_vmin`, `cbar_vmax`, `vmin`,
     and `vmax` for positive data.
     """
+    data_min = np.nanmin(data)
     data_max = np.nanmax(data)
     if symmetric_cbar is True:
-        if vmax is None:
+        if vmax is None and vmin is None:
             return (None, None, -data_max, data_max)
         else:
             return (None, None, -2, 2)
-    else:
+    elif symmetric_data_range:
         if vmax is None:
             return (0, None, -data_max, data_max)
         else:
             return (0, None, -2, 2)
+    else:
+        if vmin is None and vmax is None:
+            return (0, None, data_min, data_max)
+        elif vmin is None:
+            return (0, None, data_min, vmax)
+        elif vmax is None:
+            return (0, None, vmin, data_max)
+        else:
+            return (0, None, vmin, vmax)
 
 
-def _expected_results_neg(symmetric_cbar, vmax, data):
+def _expected_results_neg(
+    symmetric_cbar, vmin, vmax, symmetric_data_range, data
+):
     """Help for expected_results.
 
     Return the expected `cbar_vmin`, `cbar_vmax`, `vmin`,
     and `vmax` for negative data.
     """
     data_min = np.nanmin(data)
+    data_max = np.nanmax(data)
     if symmetric_cbar is True:
-        if vmax is None:
+        if vmax is None and vmin is None:
             return (None, None, data_min, -data_min)
         else:
             return (None, None, -2, 2)
-    else:
+    elif symmetric_data_range:
         if vmax is None:
             return (None, 0, data_min, -data_min)
         else:
             return (None, 0, -2, 2)
+    else:
+        if vmin is None and vmax is None:
+            return (None, 0, data_min, data_max)
+        elif vmin is None:
+            return (None, 0, data_min, vmax)
+        elif vmax is None:
+            return (None, 0, vmin, data_max)
+        else:
+            return (None, 0, vmin, vmax)
 
 
 @pytest.fixture
-def expected_results(case, data, symmetric_cbar, vmax):
+def expected_results(
+    case, data, symmetric_cbar, vmin, vmax, symmetric_data_range
+):
     """Fixture to retrieve expected results."""
     expected = {
         "pos_neg": _expected_results_pos_neg,
@@ -100,7 +126,31 @@ def expected_results(case, data, symmetric_cbar, vmax):
         "neg": _expected_results_neg,
         "masked": _expected_results_pos_neg,
     }
-    return expected[case](symmetric_cbar, vmax, data)
+    return expected[case](
+        symmetric_cbar, vmin, vmax, symmetric_data_range, data
+    )
+
+
+def test_get_colorbar_and_data_ranges_error():
+    """Tests for _get_colorbar_and_data_ranges."""
+    # vmin not accepted by default
+    with pytest.raises(ValueError, match='does not accept a "vmin" argument'):
+        _get_colorbar_and_data_ranges(
+            data_pos_neg,
+            vmax=None,
+            symmetric_cbar=True,
+            vmin=1.0,
+        )
+
+    # incompatible vmin and vmax if symmetric_cbar is True
+    with pytest.raises(ValueError, match="vmin must be equal to -vmax"):
+        _get_colorbar_and_data_ranges(
+            data_pos_neg,
+            vmin=0,
+            vmax=1.0,
+            symmetric_cbar=True,
+            symmetric_data_range=False,
+        )
 
 
 @pytest.mark.parametrize(
@@ -112,10 +162,25 @@ def expected_results(case, data, symmetric_cbar, vmax):
         ("masked", data_masked),
     ],
 )
-@pytest.mark.parametrize("symmetric_cbar", [True, False, "auto"])
+@pytest.mark.parametrize(
+    "symmetric_cbar,vmin,symmetric_data_range",
+    [
+        (True, None, True),
+        ("auto", None, True),
+        (False, None, False),
+        (False, -1, False),
+        (True, -2, False),
+    ],
+)
 @pytest.mark.parametrize("vmax", [None, 2])
 def test_get_colorbar_and_data_ranges(
-    case, data, symmetric_cbar, vmax, expected_results
+    case,
+    data,
+    symmetric_cbar,
+    vmin,
+    vmax,
+    symmetric_data_range,
+    expected_results,
 ):
     """Tests for _get_colorbar_and_data_ranges.
 
@@ -126,10 +191,13 @@ def test_get_colorbar_and_data_ranges(
         - data having only positive values.
         - masked data.
     """
-    kwargs = {"aspect": "auto", "alpha": 0.9}
     assert (
         _get_colorbar_and_data_ranges(
-            data, vmax=vmax, symmetric_cbar=symmetric_cbar, kwargs=kwargs
+            data,
+            vmin=vmin,
+            vmax=vmax,
+            symmetric_cbar=symmetric_cbar,
+            symmetric_data_range=symmetric_data_range,
         )
         == expected_results
     )
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
@@ -708,12 +708,6 @@ def test_plot_surf_stat_map_error():
     rng = np.random.RandomState(42)
     data = 10 * rng.standard_normal(size=mesh[0].shape[0])
 
-    # Try to input vmin
-    with pytest.raises(
-            ValueError,
-            match='this function does not accept a "vmin" argument'):
-        plot_surf_stat_map(mesh, stat_map=data, vmin=0)
-
     # Wrong size of stat map data
     with pytest.raises(
             ValueError,
@@ -838,7 +832,10 @@ def test_plot_img_on_surf_hemispheres_and_orientations(mni_3d_img):
 def test_plot_img_on_surf_colorbar(mni_3d_img):
     nii = mni_3d_img
     plot_img_on_surf(nii, hemispheres=['right'], views=['lateral'],
-                     colorbar=True, vmax=5, threshold=3)
+                     colorbar=True, vmin=-5, vmax=5, threshold=3)
+    plot_img_on_surf(nii, hemispheres=['right'], views=['lateral'],
+                     colorbar=True, vmin=-1, vmax=5, symmetric_cbar=False,
+                     threshold=3)
     plot_img_on_surf(nii, hemispheres=['right'], views=['lateral'],
                      colorbar=False)
     plot_img_on_surf(nii, hemispheres=['right'], views=['lateral'],
@@ -918,6 +915,18 @@ def test_plot_img_on_surf_with_axes_kwarg(mni_3d_img):
         )
 
 
+def test_plot_img_on_surf_with_engine_kwarg(mni_3d_img):
+    nii = mni_3d_img
+    with pytest.raises(ValueError):
+        plot_img_on_surf(
+            nii,
+            views=["anterior"],
+            hemispheres=["right"],
+            inflat=True,
+            engine="something",
+        )
+
+
 def test_plot_img_on_surf_title(mni_3d_img):
     nii = mni_3d_img
     title = "Title"