Ejemplo n.º 1
0
    def __init__(self, fig, files, load_func=None, robust=False,
                       sps=None, time_markers=None,
                       **kwargs) :
        """
        Create an animation object for viewing radar reflectivities.

        *fig*           matplotlib Figure object

        *files*         list of filenames containing the radar data

        *load_func*     The function to use to load the data from a file.
                        Must return a dictionary of 'vals' which contains
                        the 3D numpy array (T by Y by X), 'lats' and 'lons'.

                        It is also optional that the loading function also
                        provides a 'scan_time', either as a
                        :class:`datetime.datetime` object or as an integer
                        or a float representing the number of seconds since
                        UNIX Epoch.

        *robust*        Boolean (default: False) indicating whether or not
                        we can assume all the data will be for the same domain.
                        If you can't assume a consistant domain, then set
                        *robust* to True.  This often happens for PAR data.
                        Note that a robust rendering is slower.

        *sps*           The rate of data time for each second displayed.
                        Default: None (a data frame per display frame).

        *time_markers*  A list of time offsets (in seconds) for each frame.
                        If None, then autogenerate from the event_source
                        and data (unless *sps* is None).

        All other kwargs for :class:`FuncAnimation` are also allowed.

        To use, specify the axes to display the image on using
        :meth:`add_axes`. If no axes are added by draw time, then this class
        will use the current axes by default with no extra keywords.
        """
        #self._rd = files
        #self._loadfunc = load_func if load_func is not None else LoadRastRadar
        self._rd = RadarCache(files, cachewidth=3, load_func=load_func,
                              cyclable=True)

        self.startTime = self.curr_time
        self.endTime = self.prev_time

        self._ims = []
        self._im_kwargs = []
        self._new_axes = []
        #self._curr_time = None
        self._robust = robust
        frames = kwargs.pop('frames', None)
        #if len(files) < frames :
        #    raise ValueError("Not enough data files for the number of frames")
        self.time_markers = None
        FuncAnimation.__init__(self, fig, self.nextframe, #frames=len(self._rd),
                                     init_func=self.firstframe,
                                     **kwargs)

        self._sps = sps

        if time_markers is None :
            # Convert milliseconds per frame to frames per second
            self._fps = 1000.0 / self.event_source.interval
            if self._sps is not None :
                #timelen = (self.endTime - self.startTime)
                timestep = timedelta(0, self._sps / self._fps)

                currTime = self.startTime
                self.time_markers = [currTime]
                while currTime < self.endTime :
                    currTime += timestep
                    self.time_markers.append(currTime)
            else :
                # Don't even bother trying to make playback uniform.
                self.time_markers = None

        else :
            self.time_markers = time_markers
            self._fps = ((len(time_markers) - 1) /
                         ((self.time_markers[-1] -
                           self.time_markers[0]).total_seconds() / self._sps))
            self.event_source.interval = 1000.0 / self._fps
        self.save_count = (len(self.time_markers) if 
                           self.time_markers is not None else
                           len(self._rd))
Ejemplo n.º 2
0
class RadarAnim(FuncAnimation) :
    def __init__(self, fig, files, load_func=None, robust=False,
                       sps=None, time_markers=None,
                       **kwargs) :
        """
        Create an animation object for viewing radar reflectivities.

        *fig*           matplotlib Figure object

        *files*         list of filenames containing the radar data

        *load_func*     The function to use to load the data from a file.
                        Must return a dictionary of 'vals' which contains
                        the 3D numpy array (T by Y by X), 'lats' and 'lons'.

                        It is also optional that the loading function also
                        provides a 'scan_time', either as a
                        :class:`datetime.datetime` object or as an integer
                        or a float representing the number of seconds since
                        UNIX Epoch.

        *robust*        Boolean (default: False) indicating whether or not
                        we can assume all the data will be for the same domain.
                        If you can't assume a consistant domain, then set
                        *robust* to True.  This often happens for PAR data.
                        Note that a robust rendering is slower.

        *sps*           The rate of data time for each second displayed.
                        Default: None (a data frame per display frame).

        *time_markers*  A list of time offsets (in seconds) for each frame.
                        If None, then autogenerate from the event_source
                        and data (unless *sps* is None).

        All other kwargs for :class:`FuncAnimation` are also allowed.

        To use, specify the axes to display the image on using
        :meth:`add_axes`. If no axes are added by draw time, then this class
        will use the current axes by default with no extra keywords.
        """
        #self._rd = files
        #self._loadfunc = load_func if load_func is not None else LoadRastRadar
        self._rd = RadarCache(files, cachewidth=3, load_func=load_func,
                              cyclable=True)

        self.startTime = self.curr_time
        self.endTime = self.prev_time

        self._ims = []
        self._im_kwargs = []
        self._new_axes = []
        #self._curr_time = None
        self._robust = robust
        frames = kwargs.pop('frames', None)
        #if len(files) < frames :
        #    raise ValueError("Not enough data files for the number of frames")
        self.time_markers = None
        FuncAnimation.__init__(self, fig, self.nextframe, #frames=len(self._rd),
                                     init_func=self.firstframe,
                                     **kwargs)

        self._sps = sps

        if time_markers is None :
            # Convert milliseconds per frame to frames per second
            self._fps = 1000.0 / self.event_source.interval
            if self._sps is not None :
                #timelen = (self.endTime - self.startTime)
                timestep = timedelta(0, self._sps / self._fps)

                currTime = self.startTime
                self.time_markers = [currTime]
                while currTime < self.endTime :
                    currTime += timestep
                    self.time_markers.append(currTime)
            else :
                # Don't even bother trying to make playback uniform.
                self.time_markers = None

        else :
            self.time_markers = time_markers
            self._fps = ((len(time_markers) - 1) /
                         ((self.time_markers[-1] -
                           self.time_markers[0]).total_seconds() / self._sps))
            self.event_source.interval = 1000.0 / self._fps
        self.save_count = (len(self.time_markers) if 
                           self.time_markers is not None else
                           len(self._rd))

    @property
    def curr_time(self) :
        """
        The :class:`datetime.datetime` object for the current frame's time.

        Could possibly be None.

        This is a read-only property.
        """
        return self._get_time(self._rd.curr())

    @property
    def prev_time(self) :
        """
        Similar to :meth:`curr_time`.
        """
        return self._get_time(self._rd.peek_prev())

    @property
    def next_time(self) :
        """
        Similar to :meth:`curr_time`.
        """
        return self._get_time(self._rd.peek_next())

    @staticmethod
    def _get_time(data) :
        currTime = data.get('scan_time', None)
        if (currTime is not None and
            not isinstance(currTime, datetime)) :
            currTime = datetime.utcfromtimestamp(currTime)

        return currTime

    def firstframe(self, *args) :
        #if len(self._ims) == 0 and len(self._new_axes) == 0 :
        #    self.add_axes(plt.gca())
        return self.nextframe(0)

    def _advance_anim(self) :
        data = next(self._rd)

        if not self._robust :
            for im in self._ims :
                im.set_array(data['vals'][0, :-1, :-1].flatten())
        else :
            for im, kwargs in zip(self._ims, self._im_kwargs) :
                # Add this im object's axes object to _new_axes so
                # that a completely new rendering is made.
                self._new_axes.append((im.axes, kwargs))

                # Remove the current rendering from the Axes
                im.remove()

            # Reset these arrays so that they can be refilled
            self._ims = []
            self._im_kwargs = []

        for ax, kwargs in self._new_axes :
            self._im_kwargs.append(kwargs)
            self._ims.append(MakeReflectPPI(data['vals'][0],
                                            data['lats'], data['lons'],
                                            meth='pcmesh', axis_labels=False,
                                            mask=False, ax=ax, **kwargs))

        # Reset the "stack"
        self._new_axes = []

    def add_axes(self, ax, **kwargs) :
        """
        Display the animation on Axes *ax*. Can also specify what *kwargs* to
        pass to the call of :func:`MakeReflectPPI` except 'meth', 'axis_labels',
        'ax', and 'mask'.  Others such as 'zorder' and 'alpha' can be passed.
        """
        self._new_axes.append((ax, kwargs))

    def nextframe(self, frameindex, *args) :
        if self.time_markers is None :
            self._advance_anim()
            print("CurrTime:", str(self.curr_time))
            return self._ims

        frametime = self.time_markers[frameindex % self.save_count]

        if frametime > self.endTime :
            # We have no additional data to display.
            # Just simply hold until frametime cycles
            return None

        if frameindex % self.save_count == 0 and frameindex > 0 :
            # Force a cycling of the data
            while self.startTime < self.curr_time <= self.endTime :
                self._rd.next()
                print("Skipping ahead:", str(self.curr_time))

        if frametime >= self.curr_time :
            while self.next_time < frametime < self.endTime :
                # Dropping frames
                self._rd.next()
                print("Dropped:", str(self.curr_time))

            print("CurrTime:", str(self.curr_time))
            self._advance_anim()
            return self._ims
        else :
            return None


    def add_axes(self, ax, **kwargs) :
        """
        Display the animation on Axes *ax*. Can also specify what *kwargs* to
        pass to the call of :func:`MakeReflectPPI` except 'meth', 'axis_labels',
        'ax', and 'mask'.  Others such as 'zorder' and 'alpha' can be passed.
        """
        self._new_axes.append((ax, kwargs))

    """