#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Plotting subroutines
"""
from nilearn import plotting
from nilearn.plotting.displays import BaseAxes
import matplotlib.pyplot as plt
from matplotlib import gridspec, cm
import numpy as np
import os
from neuromaps.datasets import fetch_atlas
from brainspace import mesh as me
from .utils import is_string_like
from .geometry import remesh
surfaces = ['fsaverage', 'fsLR']
[docs]
def vrange(scalars, pv=5.):
# check if nans
mask = np.isnan(scalars)
# get visual range
return np.percentile(scalars[~mask], pv), np.percentile(scalars[~mask], 100-pv)
[docs]
def csplot(scalars, surface='fsLR', hemisphere='left', orientation='landscape',
vrange=(-4,4), cmap='viridis'):
"""
Automated plotting of cortical surfaces. Generates a figure of
a scalar brainmap `y` and returns `fig` and `ax` handles for
further use (if needed). Suitable for use with data registered to
CIFTI 32k space only. Plots data on fs_LR_32k freesurfer-type
"midthickness" surface.
Parameters
----------
scalars : np.ndarray of shape=(32492,)
Brainmap to visualize registered to standard fs_LR_32k CIFTI space.
hemisphere : str, optional
Hemisphere to plot. The default is 'left'.
orientation : str, optional
Orientation of plot, 'landscape' or 'portrait'. Default is 'landscape'.
vrange : tuple, optional
Maximum range of values to plot. Values higher or lower are truncated.
Default is (-4, 4).
cmap : str, optional
matplotlib.cm color types. The default is 'viridis'.
Returns
-------
fig : matplotlib.figure handle
ax : matplotlib.ax handle
Notes
-----
If there are NaNs present in the brainmap `y`, these are masked out
of the plotting. See ``nilearn.plotting``
"""
if surface not in surfaces:
raise ValueError('chosen surface must be "fsLR" or "fsaverage5"')
if surface == 'fsaverage':
surf = 'inflated'
den = '10k'
else:
surf = 'midthickness'
den = '32k'
# make figure
if orientation == 'landscape':
fig = plt.figure(figsize=(9, 6), constrained_layout=False)
grid = gridspec.GridSpec(1, 2)
rot = 'vertical'
axes = [1.01, 0.28, 0.03, 0.4]
elif orientation == 'portrait':
fig = plt.figure(figsize=(6, 9), constrained_layout=False)
grid = gridspec.GridSpec(2, 1)
rot = 'horizontal'
axes = [1.01, 0.28, 0.03, 0.4]
if hemisphere == 'left':
mesh = fetch_atlas(atlas=f'{surface}', density=den)[surf][0]
bg_map = fetch_atlas(atlas=f'{surface}', density=den)['vaavg'][0]
elif hemisphere == 'right':
mesh = fetch_atlas(atlas=f'{surface}', density=den)[surf][1]
bg_map = fetch_atlas(atlas=f'{surface}', density=den)['vaavg'][1]
else:
raise ValueError('Hemisphere can only be "left" or "right"')
# get colormap
cmap = plt.get_cmap(cmap)
vmin, vmax = vrange
view = ['lateral', 'medial']
# plot surface
for idx in range(2):
ax = fig.add_subplot(grid[idx], projection='3d')
plotting.plot_surf(mesh, surf_map=scalars, hemi=hemisphere,
view=view[idx], vmin=vmin, vmax=vmax,
colorbar=False, cmap=cmap, axes=ax,
bg_map=bg_map)
ax.dist = 9
# colorbar
cax = plt.axes(axes)
cbar = fig.colorbar(cm.ScalarMappable(norm=None, cmap=cmap), cax=cax,
orientation=rot)
cbar.set_ticks([])
cbar.ax.set_title(f'{vmax:.2f}', fontdict={'fontsize':20}, pad=20)
cbar.ax.set_xlabel(f'{vmin:.2f}', fontdict={'fontsize':20}, labelpad=20)
return fig, ax
[docs]
def flatplot(scalars, hemisphere='left', vrange=(-4,4), cmap='viridis', colorbar=False):
"""
Automated plotting of cortical surfaces. Generates a figure of
a scalar brainmap `y` and returns `fig` and `ax` handles for
further use (if needed). Plots on the fs_LR_32k freesurfer-type "flat" surface.
Suitable for use with data registered to CIFTI 32k space only.
Parameters
----------
scalars : np.ndarray of shape=(32492,)
Brainmap to visualize registered to standard fs_LR_32k CIFTI space.
hemisphere : str, optional
Hemisphere to plot. The default is 'left'.
vrange : tuple, optional
Maximum range of values to plot. Values higher or lower are truncated.
Default is (-4, 4).
cmap : str, optional
matplotlib.cm color types. The default is 'viridis'.
colorbar : bool, optional
If True, plots the colorbar.
Returns
-------
fig : matplotlib.figure handle
ax : matplotlib.ax handle
Notes
-----
If there are NaNs present in the brainmap `y`, these are masked out
of the plotting. See ``nilearn.plotting``
"""
# make figure
fig = plt.figure(figsize=(6, 6), constrained_layout=False)
if hemisphere == 'left':
mesh = os.path.join(os.path.split(__file__)[0], 'datasets', 'surfaces', 'standard', 'fs_LR.32k.L.flat.surf.gii')
elif hemisphere == 'right':
mesh = os.path.join(os.path.split(__file__)[0], 'datasets', 'surfaces', 'standard', 'fs_LR.32k.R.flat.surf.gii')
else:
raise ValueError('Hemisphere can only be "left" or "right"')
# get colormap
cmap = plt.get_cmap(cmap)
vmin, vmax = vrange
# plot surface
ax = fig.add_subplot(projection='3d')
plotting.plot_surf(mesh, surf_map=scalars, hemi=hemisphere,
view='lateral', vmin=vmin, vmax=vmax,
colorbar=False, cmap=cmap, axes=ax)
ax.dist = 8
# colorbar
if colorbar:
cax = plt.axes([1.04, 0.2, 0.03, 0.6])
cbar = fig.colorbar(cm.ScalarMappable(norm=None, cmap=cmap), cax=cax)
cbar.set_ticks([])
cbar.ax.set_title(f'{vmax:.2f}', fontdict={'fontsize':15}, pad=20)
cbar.ax.set_xlabel(f'{vmin:.2f}', fontdict={'fontsize':15}, labelpad=20)
return fig, ax
[docs]
def meshplot(mesh, scalars=None, vrange=(-4, 4), cmap='viridis', colorbar=False, cam=[0, 0]):
"""
Automated plotting of surface in `mesh`. `mesh` must be a `*.vtk`-like file
or a list-like of arrays (`vertices`, `faces`). If given, `scalars` must be a 1D vector
and have the same number of entries as the longest axis of `vertices`. If
not given as default, function simply plots gray surface (`cmap`, `vrange` do
nothing)
Parameters
----------
mesh : file or list-like
Surface mesh to plot `scalars` on.
scalars : 1D ndarray, optional
1D vector on mesh. Default None (not given)
vrange : float or tuple, optional
Min and max range to visualize `scalars`. Anything higher or lower is truncated.
If one number is given (positive or negative) then assumed to be maximum
and sign of `vrange` is flipped to give minimum. Default is (-4, 4).
cmap : str, optional
Colormap of `scalars`. Accepts recognized ``matplotlib.cm`` types.
Default is 'viridis'.
colorbar : bool, optional
Add colorbar to plot. Default is False.
cam : list-like, optional
Elevation and azimuthal angle for camera. Default [30, 60] i.e., elev=30, azim=60.
Returns
-------
fig : matplotlib.figure handle
ax : matplotlib.ax handle
"""
# check mesh
if is_string_like(mesh):
m = me.mesh_io.read_surface(mesh)
# plot triangular surface
if m.GetCells2D().shape[-1] == 4:
mesh = remesh(mesh)
return meshplot(mesh, scalars=scalars, vrange=vrange, cmap=cmap, colorbar=colorbar, cam=cam)
else:
mesh = m.GetPoints(), m.GetCells2D()
fig = plt.figure(figsize=(5, 6), constrained_layout=False)
# get colormap
cmap = plt.get_cmap(cmap)
if isinstance(vrange, float):
vmin, vmax = -vrange, vrange
else:
vmin, vmax = vrange
ax = fig.add_subplot(111, projection='3d')
# Set the camera view
ax.view_init(elev=cam[0], azim=cam[1])
# Arrows for cardinal directions
# Lateral (X-axis)
max_scale = m.GetPoints().max()/4
origin = -5*max_scale
ax.quiver(origin, origin, origin, 1*max_scale, 0, 0, color='blue', arrow_length_ratio=0.1)
ax.text(1.1*max_scale+origin, origin, origin, "L", color='blue')
# Anterior (Y-axis)
ax.quiver(-5*max_scale, origin, origin, 0, 1*max_scale, 0, color='red', arrow_length_ratio=0.1)
ax.text(origin, 1.1*max_scale+origin, origin, "A", color='red')
# Superior (Z-axis)
ax.quiver(origin, origin, origin, 0, 0, 1*max_scale, color='green', arrow_length_ratio=0.1)
ax.text(origin, origin, 1.1*max_scale+origin, "S", color='green')
# plot surface
plotting.plot_surf(mesh, surf_map=scalars, view=cam,
cmap=cmap, vmin=vmin, vmax=vmax,
axes=ax, colorbar=False)
if colorbar:
cax = plt.axes([0.9, 0.4, 0.03, 0.3])
cbar = fig.colorbar(cm.ScalarMappable(norm=None, cmap=cmap), cax=cax)
cbar.set_ticks([])
cbar.ax.set_title(f'{vmax:.2f}', fontdict={'fontsize':15}, pad=20)
cbar.ax.set_xlabel(f'{vmin:.2f}', fontdict={'fontsize':15}, labelpad=20)
return fig, ax