Esempio n. 1
0
    def plot(self,
             time,
             beam=None,
             maxground=2000,
             maxalt=500,
             step=1,
             showrefract=False,
             nr_cmap='jet_r',
             nr_lim=[0.8, 1.],
             raycolor='0.3',
             title=False,
             zorder=2,
             alpha=1,
             fig=None,
             rect=111,
             ax=None,
             aax=None):
        """Plot ray paths
        
        **Args**: 
            * **time** (datetime.datetime): time of rays
            * [**beam**]: beam number
            * [**maxground**]: maximum ground range [km]
            * [**maxalt**]: highest altitude limit [km]
            * [**step**]: step between each plotted ray (in number of ray steps)
            * [**showrefract**]: show refractive index along ray paths (supersedes raycolor)
            * [**nr_cmap**]: color map name for refractive index coloring
            * [**nr_lim**]: refractive index plotting limits
            * [**raycolor**]: color of ray paths
            * [**rect**]: subplot spcification
            * [**fig**]: A pylab.figure object (default to gcf)
            * [**title**]: Show default title
            * [**ax**]: Existing main axes
            * [**aax**]: Existing auxialary axes
        **Returns**:
            * **ax**: matplotlib.axes object containing formatting
            * **aax**: matplotlib.axes object containing data
            * **cbax**: matplotlib.axes object containing colorbar
        **Example**:
            ::

                # Show ray paths with colored refractive index along path
                import datetime as dt
                from davitpy.models import raydarn
                sTime = dt.datetime(2012, 11, 18, 5)
                rto = raydarn.RtRun(sTime, rCode='bks', beam=12, title=True)
                rto.readRays() # read rays into memory
                ax, aax, cbax = rto.rays.plot(sTime, step=10, showrefract=True, nr_lim=[.85,1])
                ax.grid()
                
        written by Sebastien, 2013-04
        """
        import datetime as dt
        from davitpy.utils import plotUtils
        from matplotlib.collections import LineCollection
        import matplotlib.pyplot as plt
        import numpy as np
        from types import MethodType

        # Set up axes
        if not ax and not aax:
            ax, aax = plotUtils.curvedEarthAxes(fig=fig,
                                                rect=rect,
                                                maxground=maxground,
                                                maxalt=maxalt)
        else:
            ax = ax
            aax = aax
            if hasattr(ax, 'time'):
                time = ax.time
            if hasattr(ax, 'beam'):
                beam = ax.beam

        # make sure that the required time and beam are present
        # Allow a 60 second difference between the requested time and the time
        # available.
        keys = np.array(self.paths.keys())
        diffs = np.abs(keys - time)
        if diffs.min() < dt.timedelta(minutes=1):
            time = keys[diffs.argmin()]

        assert (time in self.paths.keys()), 'Unkown time %s' % time
        if beam:
            assert (beam in self.paths[time].keys()), 'Unkown beam %s' % beam
        else:
            beam = self.paths[time].keys()[0]

        for ir, (el,
                 rays) in enumerate(sorted(self.paths[time][beam].items())):
            if not ir % step:
                if not showrefract:
                    aax.plot(rays['th'],
                             rays['r'] * 1e-3,
                             c=raycolor,
                             zorder=zorder,
                             alpha=alpha)
                else:
                    points = np.array([rays['th'],
                                       rays['r'] * 1e-3]).T.reshape(-1, 1, 2)
                    segments = np.concatenate([points[:-1], points[1:]],
                                              axis=1)
                    lcol = LineCollection(segments, zorder=zorder, alpha=alpha)
                    _ = lcol.set_cmap(nr_cmap)
                    _ = lcol.set_norm(plt.Normalize(*nr_lim))
                    _ = lcol.set_array(rays['nr'])
                    _ = aax.add_collection(lcol)

        # Plot title with date ut time and local time
        if title:
            stitle = _getTitle(time, beam, self.header, self.name)
            ax.set_title(stitle)

        # Add a colorbar when plotting refractive index
        if showrefract:
            cbax = plotUtils.addColorbar(lcol, ax)
            _ = cbax.set_ylabel("refractive index")
        else:
            cbax = None

        # Declare a new method to show range markers
        # This method is only available after rays have been plotted
        # This ensures that the markers match the plotted rays
        def showRange(self, markers=None, color='.8', s=2, zorder=3, **kwargs):
            """Plot ray paths
            
            **Args**: 
                * [**markers**]: range markers. Defaults to every 250 km
                * All other keywords are borrowed from :func:`matplotlib.pyplot.scatter`
            **Returns**:
                * **coll**: a collection of range markers
            **Example**:
                ::

                    # Add range markers to an existing ray plot
                    ax, aax, cbax = rto.rays.plot(sTime, step=10)
                    rto.rays.showRange()
                    
            written by Sebastien, 2013-04
            """

            if not markers:
                markers = np.arange(0, 5000, 250)

            x, y = [], []
            for el, rays in self.paths[time][beam].items():
                for rm in markers:
                    inds = (rays['gran'] * 1e-3 >= rm)
                    if inds.any():
                        x.append(rays['th'][inds][0])
                        y.append(rays['r'][inds][0] * 1e-3)
            coll = aax.scatter(x, y, color=color, s=s, zorder=zorder, **kwargs)

            return coll

        # End of new method

        # Assign new method
        self.showRange = MethodType(showRange, self)

        ax.beam = beam
        return ax, aax, cbax
Esempio n. 2
0
    def plot(self,
             time,
             beam=None,
             maxground=2000,
             maxalt=500,
             iscat=True,
             gscat=True,
             title=False,
             weighted=False,
             cmap='hot_r',
             fig=None,
             rect=111,
             ax=None,
             aax=None,
             zorder=4):
        """Plot scatter on ground/altitude profile
        
        **Args**: 
            * **time** (datetime.datetime): time of profile
            * [**beam**]: beam number
            * [**iscat**] (bool): show ionospheric scatter
            * [**gscat**] (bool): show ground scatter
            * [**maxground**]: maximum ground range [km]
            * [**maxalt**]: highest altitude limit [km]
            * [**rect**]: subplot spcification
            * [**fig**]: A pylab.figure object (default to gcf)
            * [**ax**]: Existing main axes
            * [**aax**]: Existing auxialary axes
            * [**title**]: Show default title
            * [**weighted**] (bool): plot ionospheric scatter relative strength (based on background density and range)
            * [**cmap**]: colormap used for weighted ionospheric scatter
        **Returns**:
            * **ax**: matplotlib.axes object containing formatting
            * **aax**: matplotlib.axes object containing data
            * **cbax**: matplotlib.axes object containing colorbar
        **Example**:
            ::

                # Show ionospheric scatter
                import datetime as dt
                from models import raydarn
                sTime = dt.datetime(2012, 11, 18, 5)
                rto = raydarn.RtRun(sTime, rCode='bks', beam=12)
                rto.readRays() # read rays into memory
                ax, aax, cbax = rto.rays.plot(sTime, title=True)
                rto.readScatter() # read scatter into memory
                rto.scatter.plot(sTime, ax=ax, aax=aax)
                ax.grid()
                
        written by Sebastien, 2013-04
        """
        from davitpy.utils import plotUtils
        from matplotlib.collections import LineCollection
        import matplotlib.pyplot as plt
        import numpy as np

        # Set up axes
        if not ax and not aax:
            ax, aax = plotUtils.curvedEarthAxes(fig=fig,
                                                rect=rect,
                                                maxground=maxground,
                                                maxalt=maxalt)
        else:
            ax = ax
            aax = aax
            if hasattr(ax, 'beam'):
                beam = ax.beam

        # make sure that the required time and beam are present
        assert (time in self.isc.keys()
                or time in self.gsc.keys()), 'Unkown time %s' % time
        if beam:
            assert (beam in self.isc[time].keys()), 'Unkown beam %s' % beam
        else:
            beam = self.isc[time].keys()[0]

        if gscat and time in self.gsc.keys():
            for ir, (el,
                     rays) in enumerate(sorted(self.gsc[time][beam].items())):
                if len(rays['r']) == 0: continue
                _ = aax.scatter(rays['th'],
                                ax.Re * np.ones(rays['th'].shape),
                                color='0',
                                zorder=zorder)

        if iscat and time in self.isc.keys():
            if weighted:
                wmin = np.min([
                    r['w'].min() for r in self.isc[time][beam].values()
                    if r['nstp'] > 0
                ])
                wmax = np.max([
                    r['w'].max() for r in self.isc[time][beam].values()
                    if r['nstp'] > 0
                ])

            for ir, (el,
                     rays) in enumerate(sorted(self.isc[time][beam].items())):
                if rays['nstp'] == 0: continue
                t = rays['th']
                r = rays['r'] * 1e-3
                spts = np.array([t, r]).T.reshape(-1, 1, 2)
                h = rays['h'] * 1e-3
                rel = np.radians(rays['rel'])
                r = np.sqrt(r**2 + h**2 + 2 * r * h * np.sin(rel))
                t = t + np.arcsin(h / r * np.cos(rel))
                epts = np.array([t, r]).T.reshape(-1, 1, 2)
                segments = np.concatenate([spts, epts], axis=1)
                lcol = LineCollection(segments, zorder=zorder)
                if weighted:
                    _ = lcol.set_cmap(cmap)
                    _ = lcol.set_norm(plt.Normalize(0, 1))
                    _ = lcol.set_array((rays['w'] - wmin) / wmax)
                else:
                    _ = lcol.set_color('0')
                _ = aax.add_collection(lcol)

            # Plot title with date ut time and local time
            if title:
                stitle = _getTitle(time, beam, self.header, None)
                ax.set_title(stitle)

            # If weighted, plot ionospheric scatter with colormap
            if weighted:
                # Add a colorbar
                cbax = plotUtils.addColorbar(lcol, ax)
                _ = cbax.set_ylabel("Ionospheric Scatter")
            else:
                cbax = None

        ax.beam = beam
        return ax, aax, cbax
Esempio n. 3
0
    def plot(self,
             time,
             beam=None,
             maxground=2000,
             maxalt=500,
             nel_cmap='jet',
             nel_lim=[10, 12],
             title=False,
             fig=None,
             rect=111,
             ax=None,
             aax=None,
             plot_colorbar=True,
             nel_rasterize=False):
        """Plot electron density profile
        
        **Args**: 
            * **time** (datetime.datetime): time of profile
            * [**beam**]: beam number
            * [**maxground**]: maximum ground range [km]
            * [**maxalt**]: highest altitude limit [km]
            * [**nel_cmap**]: color map name for electron density index coloring
            * [**nel_lim**]: electron density index plotting limits
            * [**rect**]: subplot spcification
            * [**fig**]: A pylab.figure object (default to gcf)
            * [**ax**]: Existing main axes
            * [**aax**]: Existing auxialary axes
            * [**title**]: Show default title
            * [**plot_colorbar**]: Plot a colorbar
            * [**nel_rasterize**]: Rasterize the electron density plot
                (make your pdf files more managable)
        **Returns**:
            * **ax**: matplotlib.axes object containing formatting
            * **aax**: matplotlib.axes object containing data
            * **cbax**: matplotlib.axes object containing colorbar
        **Example**:
            ::

                # Show electron density profile
                import datetime as dt
                from models import raydarn
                sTime = dt.datetime(2012, 11, 18, 5)
                rto = raydarn.RtRun(sTime, rCode='bks', beam=12)
                rto.readEdens() # read electron density into memory
                ax, aax, cbax = rto.ionos.plot(sTime, title=True)
                ax.grid()
                
        written by Sebastien, 2013-04
        """
        import datetime as dt
        from davitpy.utils import plotUtils
        from matplotlib.collections import LineCollection
        import matplotlib.pyplot as plt
        import numpy as np

        # Set up axes
        if not ax and not aax:
            ax, aax = plotUtils.curvedEarthAxes(fig=fig,
                                                rect=rect,
                                                maxground=maxground,
                                                maxalt=maxalt)
        else:
            ax = ax
            aax = aax
            if hasattr(ax, 'time'):
                time = ax.time
            if hasattr(ax, 'beam'):
                beam = ax.beam

        # make sure that the required time and beam are present

        # Allow a 60 second difference between the requested time and the time
        # available.
        keys = np.array(self.edens.keys())
        diffs = np.abs(keys - time)
        if diffs.min() < dt.timedelta(minutes=1):
            time = keys[diffs.argmin()]

        assert (time in self.edens.keys()), 'Unkown time %s' % time
        if beam:
            assert (beam in self.edens[time].keys()), 'Unkown beam %s' % beam
        else:
            beam = self.edens[time].keys()[0]

        X, Y = np.meshgrid(self.edens[time][beam]['th'],
                           ax.Re + np.linspace(60, 560, 250))
        im = aax.pcolormesh(X,
                            Y,
                            np.log10(self.edens[time][beam]['nel']),
                            vmin=nel_lim[0],
                            vmax=nel_lim[1],
                            cmap=nel_cmap,
                            rasterized=nel_rasterize)

        # Plot title with date ut time and local time
        if title:
            stitle = _getTitle(time, beam, self.header, None)
            ax.set_title(stitle)

        # Add a colorbar
        cbax = None
        if plot_colorbar:
            cbax = plotUtils.addColorbar(im, ax)
            _ = cbax.set_ylabel(r"N$_{el}$ [$\log_{10}(m^{-3})$]")

        ax.beam = beam
        return ax, aax, cbax
Esempio n. 4
0
    def plot(self, time, beam=None, maxground=2000, maxalt=500,
        iscat=True, gscat=True, title=False, weighted=False, cmap='hot_r', 
        fig=None, rect=111, ax=None, aax=None, zorder=4):
        """Plot scatter on ground/altitude profile
        
        Parameters
        ----------
        time : datetime.datetime
            time of profile
        beam : Optional[ ]
            beam number
        maxground : Optional[int]
            maximum ground range [km]
        maxalt : Optional[int]
            highest altitude limit [km]
        iscat : Optional[bool]
            show ionospheric scatter
        gscat : Optional[bool]
            show ground scatter
        title : Optional[bool]
            Show default title
        weighted : Optional[bool]
            plot ionospheric scatter relative strength (based on background density and range)
        cmap : Optional[str]
            colormap used for weighted ionospheric scatter
        fig : Optional[pylab.figure]
            object (default to gcf)
        rect : Optional[int]
            subplot spcification
        ax : Optional[ ]
            Existing main axes
        aax : Optional[ ]
            Existing auxialary axes
        zorder : Optional[int]


        Returns
        -------
        ax : matplotlib.axes
            object containing formatting
        aax : matplotlib.axes
            object containing data
        cbax : matplotlib.axes
            object containing colorbar

        Example
        -------
            # Show ionospheric scatter
            import datetime as dt
            from models import raydarn
            sTime = dt.datetime(2012, 11, 18, 5)
            rto = raydarn.RtRun(sTime, rCode='bks', beam=12)
            rto.readRays() # read rays into memory
            ax, aax, cbax = rto.rays.plot(sTime, title=True)
            rto.readScatter() # read scatter into memory
            rto.scatter.plot(sTime, ax=ax, aax=aax)
            ax.grid()

        written by Sebastien, 2013-04

        """
        from davitpy.utils import plotUtils
        from matplotlib.collections import LineCollection
        import matplotlib.pyplot as plt
        import numpy as np

        # Set up axes
        if not ax and not aax:
            ax, aax = plotUtils.curvedEarthAxes(fig=fig, rect=rect, 
                maxground=maxground, maxalt=maxalt)
        else:
            ax = ax
            aax = aax
            if hasattr(ax, 'beam'):
                beam = ax.beam

        # make sure that the required time and beam are present
        assert (time in self.isc.keys() or time in self.gsc.keys()), logging.error('Unkown time %s' % time)
        if beam:
            assert (beam in self.isc[time].keys()), logging.error('Unkown beam %s' % beam)
        else:
            beam = self.isc[time].keys()[0]

        if gscat and time in self.gsc.keys():
            for ir, (el, rays) in enumerate( sorted(self.gsc[time][beam].items()) ):
                if len(rays['r']) == 0: continue
                _ = aax.scatter(rays['th'], ax.Re*np.ones(rays['th'].shape), 
                    color='0', zorder=zorder)

        if iscat and time in self.isc.keys():
            if weighted:
                wmin = np.min( [ r['w'].min() for r in self.isc[time][beam].values() if r['nstp'] > 0] )
                wmax = np.max( [ r['w'].max() for r in self.isc[time][beam].values() if r['nstp'] > 0] )

            for ir, (el, rays) in enumerate( sorted(self.isc[time][beam].items()) ):
                if rays['nstp'] == 0: continue
                t = rays['th']
                r = rays['r']*1e-3
                spts = np.array([t, r]).T.reshape(-1, 1, 2)
                h = rays['h']*1e-3
                rel = np.radians( rays['rel'] )
                r = np.sqrt( r**2 + h**2 + 2*r*h*np.sin( rel ) )
                t = t + np.arcsin( h/r * np.cos( rel ) )
                epts = np.array([t, r]).T.reshape(-1, 1, 2)
                segments = np.concatenate([spts, epts], axis=1)
                lcol = LineCollection( segments, zorder=zorder )
                if weighted:
                    _ = lcol.set_cmap( cmap )
                    _ = lcol.set_norm( plt.Normalize(0, 1) )
                    _ = lcol.set_array( ( rays['w'] - wmin ) / wmax )
                else:
                    _ = lcol.set_color('0')
                _ = aax.add_collection( lcol )

            # Plot title with date ut time and local time
            if title:
                stitle = _getTitle(time, beam, self.header, None)
                ax.set_title( stitle )

            # If weighted, plot ionospheric scatter with colormap
            if weighted:
                # Add a colorbar
                cbax = plotUtils.addColorbar(lcol, ax)
                _ = cbax.set_ylabel("Ionospheric Scatter")
            else: cbax = None

        ax.beam = beam
        return ax, aax, cbax
Esempio n. 5
0
    def plot(self, time, beam=None, maxground=2000, maxalt=500,
        nel_cmap='jet', nel_lim=[10, 12], title=False, 
        fig=None, rect=111, ax=None, aax=None,plot_colorbar=True,
        nel_rasterize=False):
        """Plot electron density profile
        
        Parameters
        ----------
        time : datetime.datetime
            time of profile
        beam : Optional[ ]
            beam number
        maxground : Optional[int]
            maximum ground range [km]
        maxalt : Optional[int]
            highest altitude limit [km]
        nel_cmap : Optional[str]
            color map name for electron density index coloring
        nel_lim : Optional[list, int]
            electron density index plotting limits
        title : Optional[bool]
            Show default title
        fig : Optional[pylab.figure]
            object (default to gcf)
        rect : Optional[int]
            subplot spcification
        ax : Optional[ ]
            Existing main axes
        aax : Optional[ ]
            Existing auxialary axes
        plot_colorbar : Optional[bool]
            Plot a colorbar
        nel_rasterize : Optional[bool]
            Rasterize the electron density plot
            (make your pdf files more managable)

        Returns
        -------
        ax : matplotlib.axes
            object containing formatting
        aax : matplotlib.axes 
            object containing data
        cbax : matplotlib.axes
            object containing colorbar

        Example
        -------
            # Show electron density profile
            import datetime as dt
            from models import raydarn
            sTime = dt.datetime(2012, 11, 18, 5)
            rto = raydarn.RtRun(sTime, rCode='bks', beam=12)
            rto.readEdens() # read electron density into memory
            ax, aax, cbax = rto.ionos.plot(sTime, title=True)
            ax.grid()

        written by Sebastien, 2013-04

        """
        import datetime as dt
        from davitpy.utils import plotUtils
        from matplotlib.collections import LineCollection
        import matplotlib.pyplot as plt
        import numpy as np

        # Set up axes
        if not ax and not aax:
            ax, aax = plotUtils.curvedEarthAxes(fig=fig, rect=rect, 
                maxground=maxground, maxalt=maxalt)
        else:
            ax = ax
            aax = aax
            if hasattr(ax, 'time'):
                time = ax.time
            if hasattr(ax, 'beam'):
                beam = ax.beam

        # make sure that the required time and beam are present

        # Allow a 60 second difference between the requested time and the time
        # available.
        keys    = np.array(self.edens.keys())
        diffs   = np.abs(keys-time)
        if diffs.min() < dt.timedelta(minutes=1):
            time = keys[diffs.argmin()]

        assert (time in self.edens.keys()), logging.error('Unkown time %s' % time)
        if beam:
            assert (beam in self.edens[time].keys()), logging.error('Unkown beam %s' % beam)
        else:
            beam = self.edens[time].keys()[0]

        X, Y = np.meshgrid(self.edens[time][beam]['th'], ax.Re + np.linspace(60,560,250))
        im = aax.pcolormesh(X, Y, np.log10( self.edens[time][beam]['nel'] ), 
            vmin=nel_lim[0], vmax=nel_lim[1], cmap=nel_cmap,rasterized=nel_rasterize)

        # Plot title with date ut time and local time
        if title:
            stitle = _getTitle(time, beam, self.header, None)
            ax.set_title( stitle )

        # Add a colorbar
        cbax    = None
        if plot_colorbar:
            cbax = plotUtils.addColorbar(im, ax)
            _ = cbax.set_ylabel(r"N$_{el}$ [$\log_{10}(m^{-3})$]")

        ax.beam = beam
        return ax, aax, cbax
Esempio n. 6
0
    def plot(self, time, beam=None, 
        maxground=2000, maxalt=500, step=1,
        showrefract=False, nr_cmap='jet_r', nr_lim=[0.8, 1.], 
        raycolor='0.3', title=False, zorder=2, alpha=1, 
        fig=None, rect=111, ax=None, aax=None):
        """Plot ray paths
        
        Parameters
        ----------
        time : datetime.datetime
            time of rays
        beam: Optional[ ]
            beam number
        maxground : Optional[int]
            maximum ground range [km]
        maxalt : Optional[int]
            highest altitude limit [km]
        step : Optional[int]
            step between each plotted ray (in number of ray steps)
        showrefract : Optional[bool]
            show refractive index along ray paths (supersedes raycolor)
        nr_cmap : Optional[str]
            color map name for refractive index coloring
        nr_lim : Optional[list, float]
            refractive index plotting limits
        raycolor : Optional[float]
            color of ray paths
        title : Optional[bool]
            Show default title
        zorder : Optional[int]

        alpha : Optional[int]

        fig : Optional[pylab.figure]
            object (default to gcf)
        rect : Optional[int]
            subplot spcification
        ax : Optional[ ]
            Existing main axes
        aax : Optional[ ]
            Existing auxialary axes

        Returns
        -------
        ax : matplotlib.axes
            object containing formatting
        aax : matplotlib.axes
            object containing data
        cbax : matplotlib.axes
            object containing colorbar

        Example
        -------
            # Show ray paths with colored refractive index along path
            import datetime as dt
            from davitpy.models import raydarn
            sTime = dt.datetime(2012, 11, 18, 5)
            rto = raydarn.RtRun(sTime, rCode='bks', beam=12, title=True)
            rto.readRays() # read rays into memory
            ax, aax, cbax = rto.rays.plot(sTime, step=10, showrefract=True, nr_lim=[.85,1])
            ax.grid()

        written by Sebastien, 2013-04

        """
        import datetime as dt
        from davitpy.utils import plotUtils
        from matplotlib.collections import LineCollection
        import matplotlib.pyplot as plt
        import numpy as np
        from types import MethodType

        # Set up axes
        if not ax and not aax:
            ax, aax = plotUtils.curvedEarthAxes(fig=fig, rect=rect, 
                maxground=maxground, maxalt=maxalt)
        else:
            ax = ax
            aax = aax
            if hasattr(ax, 'time'):
                time = ax.time
            if hasattr(ax, 'beam'):
                beam = ax.beam

        # make sure that the required time and beam are present
        # Allow a 60 second difference between the requested time and the time
        # available.
        keys    = np.array(self.paths.keys())
        diffs   = np.abs(keys-time)
        if diffs.min() < dt.timedelta(minutes=1):
            time = keys[diffs.argmin()]

        assert (time in self.paths.keys()), logging.error('Unkown time %s' % time)
        if beam:
            assert (beam in self.paths[time].keys()), logging.error('Unkown beam %s' % beam)
        else:
            beam = self.paths[time].keys()[0]
        
        for ir, (el, rays) in enumerate( sorted(self.paths[time][beam].items()) ):
            if not ir % step:
                if not showrefract:
                    aax.plot(rays['th'], rays['r']*1e-3, c=raycolor, 
                        zorder=zorder, alpha=alpha)
                else:
                    points = np.array([rays['th'], rays['r']*1e-3]).T.reshape(-1, 1, 2)
                    segments = np.concatenate([points[:-1], points[1:]], axis=1)
                    lcol = LineCollection( segments, zorder=zorder, alpha=alpha)
                    _ = lcol.set_cmap( nr_cmap )
                    _ = lcol.set_norm( plt.Normalize(*nr_lim) )
                    _ = lcol.set_array( rays['nr'] )
                    _ = aax.add_collection( lcol )

        # Plot title with date ut time and local time
        if title:
            stitle = _getTitle(time, beam, self.header, self.name)
            ax.set_title( stitle )

        # Add a colorbar when plotting refractive index
        if showrefract:
            cbax = plotUtils.addColorbar(lcol, ax)
            _ = cbax.set_ylabel("refractive index")
        else: cbax = None

        # Declare a new method to show range markers
        # This method is only available after rays have been plotted
        # This ensures that the markers match the plotted rays
        def showRange(self, markers=None, 
            color='.8', s=2, zorder=3, 
            **kwargs):
            """Plot ray paths
            
            Parameters
            ----------
            markers : Optional[ ]
                range markers. Defaults to every 250 km
            color : Optional[float]

            s : Optional[int]

            zorder : Optional[int]

            **kwargs :

            Returns
            -------
            coll :
                a collection of range markers

            Notes
            -----
            Parameters other than markers are borrowed from matplotlib.pyplot.scatter

            Example
            -------
                # Add range markers to an existing ray plot
                ax, aax, cbax = rto.rays.plot(sTime, step=10)
                rto.rays.showRange()

            written by Sebastien, 2013-04

            """

            if not markers:
                markers = np.arange(0, 5000, 250)
            
            x, y = [], []
            for el, rays in self.paths[time][beam].items():
                for rm in markers:
                    inds = (rays['gran']*1e-3 >= rm)
                    if inds.any():
                        x.append( rays['th'][inds][0] )
                        y.append( rays['r'][inds][0]*1e-3 )
            coll = aax.scatter(x, y, 
                color=color, s=s, zorder=zorder, **kwargs)

            return coll
        # End of new method

        # Assign new method
        self.showRange = MethodType(showRange, self)

        ax.beam = beam
        return ax, aax, cbax