Coverage for nilearn/plotting/surface/_backend.py: 84%
46 statements
« prev ^ index » next coverage.py v7.8.2, created at 2025-06-04 12:51 +0200
« prev ^ index » next coverage.py v7.8.2, created at 2025-06-04 12:51 +0200
1"""Functions that are common to all possible backend implementations for
2surface visualization functions in
3:obj:`~nilearn.plotting.surface.surf_plotting`.
5"Matplotlib" is default engine for surface visualization functions. For some
6functions, there is also a "plotly" implementation.
8All dependencies and functions related to "matplotlib" implementation is in
9:obj:`~nilearn.plotting.surface._matplotlib_backend` module.
11All dependencies and functions related to "plotly" implementation is in
12:obj:`~nilearn.plotting.surface._plotly_backend` module.
14Each backend engine implementation should be a self contained module. Any
15imports on the engine package, or engine specific utility functions should not
16appear elsewhere.
17"""
19from collections.abc import Sequence
20from warnings import warn
22import numpy as np
24from nilearn._utils.param_validation import check_params
25from nilearn.plotting.surface._utils import check_surface_plotting_inputs
26from nilearn.surface import load_surf_data, load_surf_mesh
27from nilearn.surface.surface import (
28 FREESURFER_DATA_EXTENSIONS,
29 check_extensions,
30)
32# subset of data format extensions supported
33DATA_EXTENSIONS = (
34 "gii",
35 "gii.gz",
36 "mgz",
37)
39VALID_VIEWS = (
40 "anterior",
41 "posterior",
42 "medial",
43 "lateral",
44 "dorsal",
45 "ventral",
46 "left",
47 "right",
48)
50VALID_HEMISPHERES = "left", "right", "both"
53class BaseSurfaceBackend:
54 """A base class that behaves as an interface for Surface plotting
55 backend.
57 The methods of class should be implemented by each engine used as backend.
58 """
60 def _check_engine_params(self, params):
61 """Check default values of the parameters that are not implemented for
62 current engine and warn the user if the parameter has other value then
63 None.
65 Parameters
66 ----------
67 params: :obj:`dict`
68 A dictionary where keys are the unimplemented parameter names for a
69 specific engine and values are the assigned value for corresponding
70 parameter.
71 """
72 for parameter, value in params.items():
73 if value is not None:
74 warn(
75 f"'{parameter}' is not implemented "
76 f"for the {self.name} engine.\n"
77 f"Got '{parameter} = {value}'.\n"
78 f"Use '{parameter} = None' to silence this warning."
79 )
81 def plot_surf(
82 self,
83 surf_mesh=None,
84 surf_map=None,
85 bg_map=None,
86 hemi="left",
87 view=None,
88 cmap=None,
89 symmetric_cmap=None,
90 colorbar=True,
91 avg_method=None,
92 threshold=None,
93 alpha=None,
94 bg_on_data=False,
95 darkness=0.7,
96 vmin=None,
97 vmax=None,
98 cbar_vmin=None,
99 cbar_vmax=None,
100 cbar_tick_format="auto",
101 title=None,
102 title_font_size=None,
103 output_file=None,
104 axes=None,
105 figure=None,
106 ):
107 check_params(locals())
108 if view is None:
109 view = "dorsal" if hemi == "both" else "lateral"
111 surf_map, surf_mesh, bg_map = check_surface_plotting_inputs(
112 surf_map, surf_mesh, hemi, bg_map
113 )
115 check_extensions(surf_map, DATA_EXTENSIONS, FREESURFER_DATA_EXTENSIONS)
117 coords, faces = load_surf_mesh(surf_mesh)
119 return self._plot_surf(
120 coords,
121 faces,
122 surf_map=surf_map,
123 bg_map=bg_map,
124 hemi=hemi,
125 view=view,
126 cmap=cmap,
127 symmetric_cmap=symmetric_cmap,
128 colorbar=colorbar,
129 avg_method=avg_method,
130 threshold=threshold,
131 alpha=alpha,
132 bg_on_data=bg_on_data,
133 darkness=darkness,
134 vmin=vmin,
135 vmax=vmax,
136 cbar_vmin=cbar_vmin,
137 cbar_vmax=cbar_vmax,
138 cbar_tick_format=cbar_tick_format,
139 title=title,
140 title_font_size=title_font_size,
141 output_file=output_file,
142 axes=axes,
143 figure=figure,
144 )
147def _check_hemisphere_is_valid(hemi):
148 return hemi in VALID_HEMISPHERES
151def check_hemispheres(hemispheres):
152 """Check whether the hemispheres passed to in plot_img_on_surf are \
153 correct.
155 hemispheres : :obj:`list`
156 Any combination of 'left' and 'right'.
158 """
159 invalid_hemis = [
160 not _check_hemisphere_is_valid(hemi) for hemi in hemispheres
161 ]
162 if any(invalid_hemis): 162 ↛ 163line 162 didn't jump to line 163 because the condition on line 162 was never true
163 raise ValueError(
164 "Invalid hemispheres definition!\n"
165 f"Got: {np.array(hemispheres)[invalid_hemis]!s}\n"
166 f"Supported values are: {VALID_HEMISPHERES!s}"
167 )
168 return hemispheres
171def _check_view_is_valid(view) -> bool:
172 """Check whether a single view is one of two valid input types.
174 Parameters
175 ----------
176 view : :obj:`str` in {"anterior", "posterior", "medial", "lateral",
177 "dorsal", "ventral" or pair of floats (elev, azim).
179 Returns
180 -------
181 valid : True if view is valid, False otherwise.
182 """
183 if isinstance(view, str) and (view in VALID_VIEWS): 183 ↛ 185line 183 didn't jump to line 185 because the condition on line 183 was always true
184 return True
185 return (
186 isinstance(view, Sequence)
187 and len(view) == 2
188 and all(isinstance(x, (int, float)) for x in view)
189 )
192def check_views(views) -> list:
193 """Check whether the views passed to in plot_img_on_surf are correct.
195 Parameters
196 ----------
197 views : :obj:`list`
198 Any combination of strings in {"anterior", "posterior", "medial",
199 "lateral", "dorsal", "ventral"} and / or pair of floats (elev, azim).
201 Returns
202 -------
203 views : :obj:`list`
204 Views given as inputs.
205 """
206 invalid_views = [not _check_view_is_valid(view) for view in views]
208 if any(invalid_views): 208 ↛ 209line 208 didn't jump to line 209 because the condition on line 208 was never true
209 raise ValueError(
210 "Invalid view definition!\n"
211 f"Got: {np.array(views)[invalid_views]!s}\n"
212 f"Supported values are: {VALID_VIEWS!s}"
213 " or a sequence of length 2"
214 " setting the elevation and azimut of the camera."
215 )
217 return views
220def check_surf_map(surf_map, n_vertices):
221 """Help for plot_surf.
223 This function checks the dimensions of provided surf_map.
224 """
225 surf_map_data = load_surf_data(surf_map)
226 if surf_map_data.ndim != 1: 226 ↛ 227line 226 didn't jump to line 227 because the condition on line 226 was never true
227 raise ValueError(
228 "'surf_map' can only have one dimension "
229 f"but has '{surf_map_data.ndim}' dimensions"
230 )
231 if surf_map_data.shape[0] != n_vertices: 231 ↛ 232line 231 didn't jump to line 232 because the condition on line 231 was never true
232 raise ValueError(
233 "The surf_map does not have the same number "
234 "of vertices as the mesh."
235 )
236 return surf_map_data