Repository: mne-tools/mne-python

Issue:
Add function to plot montage (channel locations)
As mentioned in #4273, it is not straightforward to plot channel locations of a given montage. Here's a function that might be convenient when a user just wants to visualize channel locations without having to load any data:

```Python
import mne
from mne.channels.montage import _set_montage


def plot_montage(montage):
    if isinstance(montage, str):
        montage = mne.channels.read_montage(montage)
    info = mne.create_info(montage.ch_names, sfreq=256, ch_types="eeg")
    _set_montage(info, montage)
    mne.viz.plot_sensors(info, show_names=True)


plot_montage("biosemi32")
```

In which module should this function go? Right now, it only plots EEG channels - are there montages for other channel types available and should these be taken into account as well? Does this function need other arguments (such as an option to show only a subset of channels, an option to hide names, etc)?


Discussion:
I think it would be appropriate to change the ``plot`` method to reuse the ``plot_sensors`` code. Note that ``create_info`` takes a ``montage`` param you can use instead of calling ``_set_montage``.
Alright, so this becomes:

```Python
import mne


def plot_montage(montage):
    if isinstance(montage, str):
        montage = mne.channels.read_montage(montage)
    info = mne.create_info(montage.ch_names, sfreq=256, ch_types="eeg",
                           montage=montage)
    mne.viz.plot_sensors(info, show_names=True)


plot_montage("biosemi32")
```

Which `plot` method do you mean?
``montage.plot``
Yes, this would make sense as currently only a 3D plot can be created.

Resolving patch:
diff --git a/mne/channels/montage.py b/mne/channels/montage.py
--- a/mne/channels/montage.py
+++ b/mne/channels/montage.py
@@ -73,9 +73,9 @@ def __repr__(self):
         return s
 
     @copy_function_doc_to_method_doc(plot_montage)
-    def plot(self, scale_factor=20, show_names=False, show=True):
+    def plot(self, scale_factor=20, show_names=False, kind='3d', show=True):
         return plot_montage(self, scale_factor=scale_factor,
-                            show_names=show_names, show=True)
+                            show_names=show_names, kind=kind, show=show)
 
 
 def read_montage(kind, ch_names=None, path=None, unit='m', transform=False):
@@ -433,9 +433,9 @@ def __repr__(self):
         return s
 
     @copy_function_doc_to_method_doc(plot_montage)
-    def plot(self, scale_factor=20, show_names=False, show=True):
+    def plot(self, scale_factor=20, show_names=False, kind='3d', show=True):
         return plot_montage(self, scale_factor=scale_factor,
-                            show_names=show_names)
+                            show_names=show_names, kind=kind, show=show)
 
     def transform_to_head(self):
         """Transform digitizer points to Neuromag head coordinates."""
diff --git a/mne/viz/montage.py b/mne/viz/montage.py
--- a/mne/viz/montage.py
+++ b/mne/viz/montage.py
@@ -1,20 +1,22 @@
 """Functions to plot EEG sensor montages or digitizer montages."""
-import numpy as np
+from ..utils import check_version
+from . import plot_sensors
 
-from .utils import plt_show
 
-
-def plot_montage(montage, scale_factor=20, show_names=False, show=True):
+def plot_montage(montage, scale_factor=20, show_names=True, kind='topomap',
+                 show=True):
     """Plot a montage.
 
     Parameters
     ----------
-    montage : instance of Montage
+    montage : instance of Montage or DigMontage
         The montage to visualize.
     scale_factor : float
-        Determines the size of the points. Defaults to 20.
+        Determines the size of the points.
     show_names : bool
-        Whether to show the channel names. Defaults to False.
+        Whether to show the channel names.
+    kind : str
+        Whether to plot the montage as '3d' or 'topomap' (default).
     show : bool
         Show figure if True.
 
@@ -23,35 +25,26 @@ def plot_montage(montage, scale_factor=20, show_names=False, show=True):
     fig : Instance of matplotlib.figure.Figure
         The figure object.
     """
-    from ..channels.montage import Montage, DigMontage
-
-    import matplotlib.pyplot as plt
-    from mpl_toolkits.mplot3d import Axes3D  # noqa: F401
-    fig = plt.figure()
-    ax = fig.add_subplot(111, projection='3d')
-
+    from ..channels import Montage, DigMontage
+    from .. import create_info
     if isinstance(montage, Montage):
-        pos = montage.pos
-        ax.scatter(pos[:, 0], pos[:, 1], pos[:, 2], s=scale_factor)
-        if show_names:
-            ch_names = montage.ch_names
-            for ch_name, x, y, z in zip(ch_names, pos[:, 0],
-                                        pos[:, 1], pos[:, 2]):
-                ax.text(x, y, z, ch_name)
+        ch_names = montage.ch_names
+        title = montage.kind
     elif isinstance(montage, DigMontage):
-        pos = np.vstack((montage.hsp, montage.elp))
-        ax.scatter(pos[:, 0], pos[:, 1], pos[:, 2], s=scale_factor)
-        if show_names:
-            if montage.point_names:
-                hpi_names = montage.point_names
-                for hpi_name, x, y, z in zip(hpi_names, montage.elp[:, 0],
-                                             montage.elp[:, 1],
-                                             montage.elp[:, 2]):
-                    ax.text(x, y, z, hpi_name)
-
-    ax.set_xlabel('x')
-    ax.set_ylabel('y')
-    ax.set_zlabel('z')
-
-    plt_show(show)
+        ch_names = montage.point_names
+        title = None
+    else:
+        raise TypeError("montage must be an instance of "
+                        "mne.channels.montage.Montage or"
+                        "mne.channels.montage.DigMontage")
+    if kind not in ['topomap', '3d']:
+        raise ValueError("kind must be 'topomap' or '3d'")
+    info = create_info(ch_names, sfreq=256, ch_types="eeg", montage=montage)
+    fig = plot_sensors(info, kind=kind, show_names=show_names, show=show,
+                       title=title)
+    collection = fig.axes[0].collections[0]
+    if check_version("matplotlib", "1.4"):
+        collection.set_sizes([scale_factor])
+    else:
+        collection._sizes = [scale_factor]
     return fig


Test patch:
diff --git a/mne/viz/tests/test_montage.py b/mne/viz/tests/test_montage.py
--- a/mne/viz/tests/test_montage.py
+++ b/mne/viz/tests/test_montage.py
@@ -26,7 +26,11 @@ def test_plot_montage():
     """
     m = read_montage('easycap-M1')
     m.plot()
-    m.plot(show_names=True)
+    m.plot(kind='3d')
+    m.plot(kind='3d', show_names=True)
+    m.plot(kind='topomap')
+    m.plot(kind='topomap', show_names=True)
     d = read_dig_montage(hsp, hpi, elp, point_names)
     d.plot()
-    d.plot(show_names=True)
+    d.plot(kind='3d')
+    d.plot(kind='3d', show_names=True)