Repository: nilearn/nilearn

Issue:
add_sphere for slicers
This came up in a conversation with @GaelVaroquaux at Brainhack Paris, 2016.

Would it be possible to add an add_sphere functionality to slicers? This could be used e.g. to overlay a seed sphere on top of a stats map (e.g., for seed-based correlation).



Discussion:
Hi fliem,

This is already in the pipeline (even though I can't find the related issue right now). We will add an `add_markers` functions that will displays markers of any shape (round, squares...) and of any color/size.

Would that suit your needs? It's just 5 lines of code away, maybe we can hack it now.

Sounds good. I am in the middle module.

> Am 26.02.2016 um 11:14 schrieb Alexandre Abraham notifications@github.com:
> 
> Hi fliem,
> 
> This is already in the pipeline (even though I can't find the related issue right now). We will add an add_markers functions that will displays markers of any shape (round, squares...) and of any color/size.
> 
> Would that suit your needs? It's just 5 lines of code away, maybe we can hack it now.
> 
> —
> Reply to this email directly or view it on GitHub https://github.com/nilearn/nilearn/issues/1019#issuecomment-189204772.

@KamalakerDadi do you have time for that?


Resolving patch:
diff --git a/nilearn/plotting/displays.py b/nilearn/plotting/displays.py
--- a/nilearn/plotting/displays.py
+++ b/nilearn/plotting/displays.py
@@ -184,21 +184,31 @@ def draw_position(self, size, bg_color, **kwargs):
                 **kwargs)
 
 
-def _coords_3d_to_2d(coords_3d, direction):
-    """Project 3d coordinates into 2d ones given the direction of a cut
+def _get_index_from_direction(direction):
+    """Returns numerical index from direction
     """
-    direction_to_index = {'x': [1, 2],
-                          'y': [0, 2],
-                          'z': [0, 1]}
-    index = direction_to_index.get(direction)
-
-    if index is None:
+    directions = ['x', 'y', 'z']
+    try:
+        index = directions.index(direction)
+    except ValueError:
         message = (
             '{0} is not a valid direction. '
             "Allowed values are 'x', 'y' and 'z'").format(direction)
         raise ValueError(message)
+    return index
+
+
+def _coords_3d_to_2d(coords_3d, direction, return_direction=False):
+    """Project 3d coordinates into 2d ones given the direction of a cut
+    """
+    index = _get_index_from_direction(direction)
+    dimensions = [0, 1, 2]
+    dimensions.pop(index)
+
+    if return_direction:
+        return coords_3d[:, dimensions], coords_3d[:, index]
 
-    return coords_3d[:, index]
+    return coords_3d[:, dimensions]
 
 
 class GlassBrainAxes(BaseAxes):
@@ -703,6 +713,40 @@ def add_edges(self, img, color='r'):
 
         plt.draw_if_interactive()
 
+    def add_markers(self, marker_coords, marker_color='r', marker_size=30,
+                    **kwargs):
+        """Add markers to the plot.
+
+        Parameters
+        ----------
+        markers_coords: array of size (n_markers, 3)
+            Coordinates of the markers to plot. For each slice, only markers
+            that are 2 millimeters away from the slice are plotted.
+        marker_color: pyplot compatible color or list of shape (n_markers,)
+            List of colors for each marker that can be string or matplotlib
+            colors
+        marker_size: single float or list of shape (n_markers,)
+            Size in pixel for each marker
+        """
+        defaults = {'marker': 'o',
+                    'zorder': 1000}
+        for k, v in defaults.items():
+            kwargs.setdefault(k, v)
+
+        for display_ax in self.axes.values():
+            direction = display_ax.direction
+            coord = display_ax.coord
+            marker_coords_2d, third_d = _coords_3d_to_2d(
+                marker_coords, direction, return_direction=True)
+            # Heuristic that plots only markers that are 2mm away from the
+            # current slice.
+            # XXX: should we keep this heuristic?
+            mask = np.abs(third_d - coord) <= 2.
+            xdata, ydata = marker_coords_2d.T
+            display_ax.ax.scatter(xdata[mask], ydata[mask],
+                                  s=marker_size,
+                                  c=marker_color, **kwargs)
+
     def annotate(self, left_right=True, positions=True, size=12, **kwargs):
         """ Add annotations to the plot.
 


Test patch: