Пример #1
0
    def plot_wavelength_slice(self, offset, axes=None, style='imshow', **kwargs):
        """
        Plots an x-y graph at a certain specified wavelength onto the current
        axes. Keyword arguments are passed on to matplotlib.

        Parameters
        ----------
        offset: `int` or `float`
            The offset from the primary wavelength to plot. If it's an int it
            will plot the nth wavelength from the primary; if it's a float then
            it will plot the closest wavelength. If the offset is out of range,
            it will plot the primary wavelength (offset 0)

        axes: `astropy.visualization.wcsaxes.core.WCSAxes` or `None`:
            The axes to plot onto. If None the current axes will be used.

        style: 'imshow' or 'pcolormesh'
            The style of plot to be used. Default is 'imshow'
        """
        if axes is None:
            axes = wcsaxes_compat.gca_wcs(self.axes_wcs, slices=("x", "y", offset))

        data = self._choose_wavelength_slice(offset)
        if data is None:
            data = self._choose_wavelength_slice(0)

        if style == 'imshow':
            plot = axes.imshow(data, **kwargs)
        elif style == 'pcolormesh':
            plot = axes.pcolormesh(data, **kwargs)

        return plot
Пример #2
0
    def peek(self, draw_limb=False, draw_grid=False, gamma=None,
                   colorbar=True, basic_plot=False, **matplot_args):
        """Displays the map in a new figure

        Parameters
        ----------
        draw_limb : bool
            Whether the solar limb should be plotted.

        draw_grid : bool or `~astropy.units.Quantity`
            Whether solar meridians and parallels are plotted.
            If `~astropy.units.Quantity` then sets degree difference between
            parallels and meridians.
        gamma : float
            Gamma value to use for the color map
        colorbar : bool
            Whether to display a colorbar next to the plot
        basic_plot : bool
            If true, the data is plotted by itself at it's natural scale; no
            title, labels, or axes are shown.
        **matplot_args : dict
            Matplotlib Any additional imshow arguments that should be used
            when plotting the image.
        """

        # Create a figure and add title and axes
        figure = plt.figure(frameon=not basic_plot)

        # Basic plot
        if basic_plot:
            axes = plt.Axes(figure, [0., 0., 1., 1.])
            axes.set_axis_off()
            figure.add_axes(axes)
            matplot_args.update({'annotate':False})

        # Normal plot
        else:
            axes = wcsaxes_compat.gca_wcs(self.wcs)

        im = self.plot(axes=axes, **matplot_args)

        if colorbar and not basic_plot:
            figure.colorbar(im)

        if draw_limb:
            self.draw_limb(axes=axes)

        if isinstance(draw_grid, bool):
            if draw_grid:
                self.draw_grid(axes=axes)
        elif isinstance(draw_grid, (int, long, float)):
            self.draw_grid(axes=axes, grid_spacing=draw_grid)
        else:
            raise TypeError("draw_grid should be bool, int, long or float")

        figure.show()

        return figure
Пример #3
0
    def draw_limb(self, axes=None, **kwargs):
        """Draws a circle representing the solar limb

            Parameters
            ----------
            axes: matplotlib.axes object or None
                Axes to plot limb on or None to use current axes.

            Returns
            -------
            circ: list
                A list containing the `matplotlib.patches.Circle` object that
                has been added to the axes.

            Notes
            -----
            keyword arguments are passed onto the Circle Patch, see:
            http://matplotlib.org/api/artist_api.html#matplotlib.patches.Patch
            http://matplotlib.org/api/artist_api.html#matplotlib.patches.Circle
        """

        if not axes:
            axes = wcsaxes_compat.gca_wcs(self.wcs)

        transform = wcsaxes_compat.get_world_transform(axes)
        if wcsaxes_compat.is_wcsaxes(axes):
            radius = self.rsun_obs.to(u.deg).value
        else:
            radius = self.rsun_obs.value
        c_kw = {'radius':radius,
                'fill':False,
                'color':'white',
                'zorder':100,
                'transform': transform
                }
        c_kw.update(kwargs)

        circ = patches.Circle([0, 0], **c_kw)
        axes.add_artist(circ)

        return [circ]
Пример #4
0
    def plot(self, gamma=None, annotate=True, axes=None, **imshow_args):
        """ Plots the map object using matplotlib, in a method equivalent
        to plt.imshow() using nearest neighbour interpolation.

        Parameters
        ----------
        gamma : float
            Gamma value to use for the color map

        annotate : bool
            If true, the data is plotted at it's natural scale; with
            title and axis labels.

        axes: matplotlib.axes object or None
            If provided the image will be plotted on the given axes. Else the
            current matplotlib axes will be used.

        **imshow_args : dict
            Any additional imshow arguments that should be used
            when plotting the image.

        Examples
        --------
        #Simple Plot with color bar
        >>> aiamap.plot()
        >>> plt.colorbar()

        #Add a limb line and grid
        >>> aia.plot()
        >>> aia.draw_limb()
        >>> aia.draw_grid()

        """

        #Get current axes
        if not axes:
            axes = wcsaxes_compat.gca_wcs(self.wcs)

        # Check that the image is properly oriented
        if (not wcsaxes_compat.is_wcsaxes(axes) and
            not np.array_equal(self.rotation_matrix, np.matrix(np.identity(2)))):
            warnings.warn("This map is not properly oriented. Plot axes may be incorrect",
                          Warning)

        # Normal plot
        if annotate:
            axes.set_title("{name} {date:{tmf}}".format(name=self.name,
                                                        date=parse_time(self.date),
                                                        tmf=TIME_FORMAT))

            # x-axis label
            if self.coordinate_system.x == 'HG':
                xlabel = 'Longitude [{lon}]'.format(lon=self.units.x)
            else:
                xlabel = 'X-position [{xpos}]'.format(xpos=self.units.x)

            # y-axis label
            if self.coordinate_system.y == 'HG':
                ylabel = 'Latitude [{lat}]'.format(lat=self.units.y)
            else:
                ylabel = 'Y-position [{ypos}]'.format(ypos=self.units.y)

            axes.set_xlabel(xlabel)
            axes.set_ylabel(ylabel)


        cmap = deepcopy(self.cmap)
        if gamma is not None:
            cmap.set_gamma(gamma)

        kwargs = self._mpl_imshow_kwargs(axes, cmap)
        kwargs.update(imshow_args)

        if self.mask is None:
            ret = axes.imshow(self.data, **kwargs)
        else:
            ret = axes.imshow(np.ma.array(np.asarray(self.data), mask=self.mask), **kwargs)

        if wcsaxes_compat.is_wcsaxes(axes):
            wcsaxes_compat.default_wcs_grid(axes)

        #Set current image (makes colorbar work)
        plt.sca(axes)
        plt.sci(ret)
        return ret
Пример #5
0
    def plot(self, axes=None, resample=None, annotate=True,
             interval=200, plot_function=None, **kwargs):
        """
        A animation plotting routine that animates each element in the
        MapSequence

        Parameters
        ----------
        axes: mpl axes
            axes to plot the animation on, if none uses current axes

        resample: list or False
            Draws the map at a lower resolution to increase the speed of
            animation. Specify a list as a fraction i.e. [0.25, 0.25] to
            plot at 1/4 resolution.
            [Note: this will only work where the map arrays are the same size]

        annotate: bool
            Annotate the figure with scale and titles

        interval: int
            Animation interval in ms

        plot_function : function
            A function to be called as each map is plotted. Any variables
            returned from the function will have their ``remove()`` method called
            at the start of the next frame so that they are removed from the plot.

        Examples
        --------
        >>> import matplotlib.pyplot as plt
        >>> import matplotlib.animation as animation
        >>> from sunpy.map import Map

        >>> sequence = Map(files, sequence=True)   # doctest: +SKIP
        >>> ani = sequence.plot(colorbar=True)   # doctest: +SKIP
        >>> plt.show()   # doctest: +SKIP

        Plot the map at 1/2 original resolution

        >>> sequence = Map(files, sequence=True)   # doctest: +SKIP
        >>> ani = sequence.plot(resample=[0.5, 0.5], colorbar=True)   # doctest: +SKIP
        >>> plt.show()   # doctest: +SKIP

        Save an animation of the MapSequence

        >>> sequence = Map(res, sequence=True)   # doctest: +SKIP

        >>> ani = sequence.plot()   # doctest: +SKIP

        >>> Writer = animation.writers['ffmpeg']   # doctest: +SKIP
        >>> writer = Writer(fps=10, metadata=dict(artist='SunPy'), bitrate=1800)   # doctest: +SKIP

        >>> ani.save('mapsequence_animation.mp4', writer=writer)   # doctest: +SKIP

        Save an animation with the limb at each time step

        >>> def myplot(fig, ax, sunpy_map):
        ...    p = sunpy_map.draw_limb()
        ...    return p
        >>> sequence = Map(files, sequence=True)   # doctest: +SKIP
        >>> ani = sequence.peek(plot_function=myplot)   # doctest: +SKIP
        >>> plt.show()   # doctest: +SKIP

        """
        if not axes:
            axes = wcsaxes_compat.gca_wcs(self.maps[0].wcs)
        fig = axes.get_figure()

        if not plot_function:
            plot_function = lambda fig, ax, smap: []
        removes = []

        # Normal plot
        def annotate_frame(i):
            axes.set_title("{s.name}".format(s=self[i]))
            axes.set_xlabel(axis_labels_from_ctype(self[i].coordinate_system[0],
                                                   self[i].spatial_units[0]))
            axes.set_ylabel(axis_labels_from_ctype(self[i].coordinate_system[1],
                                                   self[i].spatial_units[1]))

        if resample:
            if self.all_maps_same_shape():
                resample = u.Quantity(self.maps[0].dimensions) * np.array(resample)
                ani_data = [amap.resample(resample) for amap in self.maps]
            else:
                raise ValueError('Maps in mapsequence do not all have the same shape.')
        else:
            ani_data = self.maps

        im = ani_data[0].plot(axes=axes, **kwargs)

        def updatefig(i, im, annotate, ani_data, removes):
            while removes:
                removes.pop(0).remove()

            im.set_array(ani_data[i].data)
            im.set_cmap(ani_data[i].plot_settings['cmap'])

            norm = deepcopy(ani_data[i].plot_settings['norm'])
            # The following explicit call is for bugged versions of Astropy's
            # ImageNormalize
            norm.autoscale_None(ani_data[i].data)
            im.set_norm(norm)

            if wcsaxes_compat.is_wcsaxes(axes):
                im.axes.reset_wcs(ani_data[i].wcs)
                wcsaxes_compat.default_wcs_grid(axes)
            else:
                bl = ani_data[i]._get_lon_lat(ani_data[i].bottom_left_coord)
                tr = ani_data[i]._get_lon_lat(ani_data[i].top_right_coord)
                x_range = list(u.Quantity([bl[0], tr[0]]).to(ani_data[i].spatial_units[0]).value)
                y_range = list(u.Quantity([bl[1], tr[1]]).to(ani_data[i].spatial_units[1]).value)

                im.set_extent(np.concatenate((x_range.value, y_range.value)))

            if annotate:
                annotate_frame(i)
            removes += list(plot_function(fig, axes, ani_data[i]))

        ani = matplotlib.animation.FuncAnimation(fig, updatefig,
                                                 frames=list(range(0, len(ani_data))),
                                                 fargs=[im, annotate, ani_data, removes],
                                                 interval=interval,
                                                 blit=False)

        return ani
Пример #6
0
    def draw_grid(self, axes=None, grid_spacing=15*u.deg, **kwargs):
        """Draws a grid over the surface of the Sun

        Parameters
        ----------
        axes: matplotlib.axes object or None
        Axes to plot limb on or None to use current axes.

        grid_spacing: float
            Spacing (in degrees) for longitude and latitude grid.

        Returns
        -------
        lines: list
            A list of `matplotlib.lines.Line2D` objects that have been plotted.

        Notes
        -----
        keyword arguments are passed onto matplotlib.pyplot.plot
        """

        if not axes:
            axes = wcsaxes_compat.gca_wcs(self.wcs)

        lines = []

        # Do not automatically rescale axes when plotting the overlay
        axes.set_autoscale_on(False)

        transform = wcsaxes_compat.get_world_transform(axes)

        XX, YY = np.meshgrid(np.arange(self.data.shape[0]),
                             np.arange(self.data.shape[1]))
        x, y = self.pixel_to_data(XX*u.pix, YY*u.pix)
        dsun = self.dsun

        b0 = self.heliographic_latitude.to(u.deg).value
        l0 = self.heliographic_longitude.to(u.deg).value
        units = self.units

        #Prep the plot kwargs
        plot_kw = {'color':'white',
                   'linestyle':'dotted',
                   'zorder':100,
                   'transform':transform}
        plot_kw.update(kwargs)

        hg_longitude_deg = np.linspace(-180, 180, num=361) + l0
        hg_latitude_deg = np.arange(-90, 90, grid_spacing.to(u.deg).value)

        # draw the latitude lines
        for lat in hg_latitude_deg:
            x, y = wcs.convert_hg_hpc(hg_longitude_deg, lat * np.ones(361),
                                      b0_deg=b0, l0_deg=l0, dsun_meters=dsun,
                                      angle_units=units.x, occultation=True)
            valid = np.logical_and(np.isfinite(x), np.isfinite(y))
            x = x[valid]
            y = y[valid]
            if wcsaxes_compat.is_wcsaxes(axes):
                x = (x*u.arcsec).to(u.deg).value
                y = (y*u.arcsec).to(u.deg).value
            lines += axes.plot(x, y, **plot_kw)

        hg_longitude_deg = np.arange(-180, 180, grid_spacing.to(u.deg).value) + l0
        hg_latitude_deg = np.linspace(-90, 90, num=181)

        # draw the longitude lines
        for lon in hg_longitude_deg:
            x, y = wcs.convert_hg_hpc(lon * np.ones(181), hg_latitude_deg,
                                      b0_deg=b0, l0_deg=l0, dsun_meters=dsun,
                                      angle_units=units[0], occultation=True)
            valid = np.logical_and(np.isfinite(x), np.isfinite(y))
            x = x[valid]
            y = y[valid]
            if wcsaxes_compat.is_wcsaxes(axes):
                x = (x*u.arcsec).to(u.deg).value
                y = (y*u.arcsec).to(u.deg).value
            lines += axes.plot(x, y, **plot_kw)

        # Turn autoscaling back on.
        axes.set_autoscale_on(True)
        return lines
Пример #7
0
    def plot(self,
             axes=None,
             resample=None,
             annotate=True,
             interval=200,
             plot_function=None,
             **kwargs):
        """
        A animation plotting routine that animates each element in the
        MapSequence

        Parameters
        ----------
        axes : matplotlib.axes.Axes
            axes to plot the animation on, if none uses current axes
        resample : list
            Draws the map at a lower resolution to increase the speed of
            animation. Specify a list as a fraction i.e. [0.25, 0.25] to
            plot at 1/4 resolution.
            [Note: this will only work where the map arrays are the same size]
        annotate : bool
            Annotate the figure with scale and titles
        interval : int
            Animation interval in ms
        plot_function : function
            A function to be called as each map is plotted.
            For more information see `sunpy.visualization.animator.MapSequenceAnimator`.

        Returns
        -------
        `matplotlib.animation.FuncAnimation`
            A FuncAnimation instance.

        See Also
        --------
        `sunpy.visualization.animator.MapSequenceAnimator`

        Examples
        --------
        >>> import matplotlib.pyplot as plt
        >>> import matplotlib.animation as animation
        >>> from sunpy.map import Map

        >>> sequence = Map(files, sequence=True)   # doctest: +SKIP
        >>> ani = sequence.plot(colorbar=True)   # doctest: +SKIP
        >>> plt.show()   # doctest: +SKIP

        Plot the map at 1/2 original resolution

        >>> sequence = Map(files, sequence=True)   # doctest: +SKIP
        >>> ani = sequence.plot(resample=[0.5, 0.5], colorbar=True)   # doctest: +SKIP
        >>> plt.show()   # doctest: +SKIP

        Save an animation of the MapSequence

        >>> sequence = Map(res, sequence=True)   # doctest: +SKIP

        >>> ani = sequence.plot()   # doctest: +SKIP

        >>> Writer = animation.writers['ffmpeg']   # doctest: +SKIP
        >>> writer = Writer(fps=10, metadata=dict(artist='SunPy'), bitrate=1800)   # doctest: +SKIP

        >>> ani.save('mapsequence_animation.mp4', writer=writer)   # doctest: +SKIP

        Save an animation with the limb at each time step

        >>> def myplot(fig, ax, sunpy_map):
        ...    p = sunpy_map.draw_limb()
        ...    return p
        >>> sequence = Map(files, sequence=True)   # doctest: +SKIP
        >>> ani = sequence.peek(plot_function=myplot)   # doctest: +SKIP
        >>> plt.show()   # doctest: +SKIP

        """
        if not axes:
            axes = wcsaxes_compat.gca_wcs(self.maps[0].wcs)
        fig = axes.get_figure()

        if not plot_function:

            def plot_function(fig, ax, smap):
                return []

        removes = []

        # Normal plot
        def annotate_frame(i):
            axes.set_title("{s.name}".format(s=self[i]))
            axes.set_xlabel(
                axis_labels_from_ctype(self[i].coordinate_system[0],
                                       self[i].spatial_units[0]))
            axes.set_ylabel(
                axis_labels_from_ctype(self[i].coordinate_system[1],
                                       self[i].spatial_units[1]))

        if resample:
            if self.all_maps_same_shape():
                resample = u.Quantity(
                    self.maps[0].dimensions) * np.array(resample)
                ani_data = [amap.resample(resample) for amap in self.maps]
            else:
                raise ValueError(
                    'Maps in mapsequence do not all have the same shape.')
        else:
            ani_data = self.maps

        im = ani_data[0].plot(axes=axes, **kwargs)

        def updatefig(i, im, annotate, ani_data, removes):
            while removes:
                removes.pop(0).remove()

            im.set_array(ani_data[i].data)
            im.set_cmap(ani_data[i].plot_settings['cmap'])

            norm = deepcopy(ani_data[i].plot_settings['norm'])
            # The following explicit call is for bugged versions of Astropy's
            # ImageNormalize
            norm.autoscale_None(ani_data[i].data)
            im.set_norm(norm)

            if wcsaxes_compat.is_wcsaxes(axes):
                im.axes.reset_wcs(ani_data[i].wcs)
                wcsaxes_compat.default_wcs_grid(axes)
            else:
                bl = ani_data[i]._get_lon_lat(ani_data[i].bottom_left_coord)
                tr = ani_data[i]._get_lon_lat(ani_data[i].top_right_coord)
                x_range = list(
                    u.Quantity([bl[0],
                                tr[0]]).to(ani_data[i].spatial_units[0]).value)
                y_range = list(
                    u.Quantity([bl[1],
                                tr[1]]).to(ani_data[i].spatial_units[1]).value)

                im.set_extent(np.concatenate((x_range.value, y_range.value)))

            if annotate:
                annotate_frame(i)
            removes += list(plot_function(fig, axes, ani_data[i]))

        ani = matplotlib.animation.FuncAnimation(
            fig,
            updatefig,
            frames=list(range(0, len(ani_data))),
            fargs=[im, annotate, ani_data, removes],
            interval=interval,
            blit=False)

        return ani
Пример #8
0
    def plot(self,
             axes=None,
             resample=None,
             annotate=True,
             interval=200,
             plot_function=None,
             **kwargs):
        """
        A animation plotting routine that animates each element in the
        MapCube

        Parameters
        ----------
        gamma: float
            Gamma value to use for the color map

        axes: mpl axes
            axes to plot the animation on, if none uses current axes

        resample: list or False
            Draws the map at a lower resolution to increase the speed of
            animation. Specify a list as a fraction i.e. [0.25, 0.25] to
            plot at 1/4 resolution.
            [Note: this will only work where the map arrays are the same size]

        annotate: bool
            Annotate the figure with scale and titles

        interval: int
            Animation interval in ms

        plot_function : function
            A function to be called as each map is plotted. Any variables
            returned from the function will have their ``remove()`` method called
            at the start of the next frame so that they are removed from the plot.

        Examples
        --------
        >>> import matplotlib.pyplot as plt
        >>> import matplotlib.animation as animation
        >>> from sunpy.map import Map

        >>> cube = Map(files, cube=True)   # doctest: +SKIP
        >>> ani = cube.plot(colorbar=True)   # doctest: +SKIP
        >>> plt.show()   # doctest: +SKIP

        Plot the map at 1/2 original resolution

        >>> cube = Map(files, cube=True)   # doctest: +SKIP
        >>> ani = cube.plot(resample=[0.5, 0.5], colorbar=True)   # doctest: +SKIP
        >>> plt.show()   # doctest: +SKIP

        Save an animation of the MapCube

        >>> cube = Map(res, cube=True)   # doctest: +SKIP

        >>> ani = cube.plot()   # doctest: +SKIP

        >>> Writer = animation.writers['ffmpeg']   # doctest: +SKIP
        >>> writer = Writer(fps=10, metadata=dict(artist='SunPy'), bitrate=1800)   # doctest: +SKIP

        >>> ani.save('mapcube_animation.mp4', writer=writer)   # doctest: +SKIP

        Save an animation with the limb at each time step

        >>> def myplot(fig, ax, sunpy_map):
        ...    p = sunpy_map.draw_limb()
        ...    return p
        >>> cube = Map(files, cube=True)   # doctest: +SKIP
        >>> ani = cube.peek(plot_function=myplot)   # doctest: +SKIP
        >>> plt.show()   # doctest: +SKIP
        """
        if not axes:
            axes = wcsaxes_compat.gca_wcs(self._get_map(self.ref_index).wcs)
        fig = axes.get_figure()

        if not plot_function:
            plot_function = lambda fig, ax, smap: []
        removes = []

        # Normal plot
        def annotate_frame(i):
            axes.set_title("{s.name}".format(s=self[i]))

            # x-axis label
            if self[0].coordinate_system.x == 'HG':
                xlabel = 'Longitude [{lon}'.format(lon=self[i].units.x)
            else:
                xlabel = 'X-position [{xpos}]'.format(xpos=self[i].units.x)

            # y-axis label
            if self[0].coordinate_system.y == 'HG':
                ylabel = 'Latitude [{lat}]'.format(lat=self[i].units.y)
            else:
                ylabel = 'Y-position [{ypos}]'.format(ypos=self[i].units.y)

            axes.set_xlabel(xlabel)
            axes.set_ylabel(ylabel)

        if resample:
            # This assumes that the maps are homogeneous!
            # TODO: Update this!
            resample = np.array(len(self) - 1) * np.array(resample)
            ani_data = [
                self._get_map(j).resample(resample)
                for j in range(0, len(self))
            ]
        else:
            ani_data = [self._get_map(j) for j in range(0, len(self))]

        im = ani_data[0].plot(axes=axes, **kwargs)

        def updatefig(i, im, annotate, ani_data, removes):
            while removes:
                removes.pop(0).remove()

            im.set_array(ani_data[i].data)
            im.set_cmap(self._maps[i].plot_settings['cmap'])

            norm = deepcopy(self._maps[i].plot_settings['norm'])
            # The following explicit call is for bugged versions of Astropy's ImageNormalize
            norm.autoscale_None(ani_data[i].data)
            im.set_norm(norm)

            if wcsaxes_compat.is_wcsaxes(axes):
                im.axes.reset_wcs(self._maps[i].wcs)
                wcsaxes_compat.default_wcs_grid(axes)
            else:
                im.set_extent(
                    np.concatenate((self._maps[i].xrange.value,
                                    self._maps[i].yrange.value)))

            if annotate:
                annotate_frame(i)
            removes += list(plot_function(fig, axes, self._maps[i]))

        ani = matplotlib.animation.FuncAnimation(
            fig,
            updatefig,
            frames=list(range(0, len(self))),
            fargs=[im, annotate, ani_data, removes],
            interval=interval,
            blit=False)

        return ani
Пример #9
0
    def plot(self, axes=None, annotate=True,
             title="SunPy Composite Plot", **matplot_args):
        """Plots the composite map object by calling :meth:`~sunpy.map.GenericMap.plot`
        or :meth:`~sunpy.map.GenericMap.draw_contours`.

        By default, each map is plotted as an image.  If a given map has levels
        defined (via :meth:`~sunpy.map.CompositeMap.set_levels`), that map will instead
        be plotted as contours.

        Parameters
        ----------

        axes: `~matplotlib.axes.Axes` or None
            If provided the image will be plotted on the given axes. Else the
            current matplotlib axes will be used.

        annotate : `bool`
            If true, the data is plotted at it's natural scale; with
            title and axis labels.

        title : `str`
            Title of the composite map.

        **matplot_args : `dict`
            Any additional Matplotlib arguments that should be used
            when plotting.

        Returns
        -------
        ret : `list`
            List of axes image or quad contour sets that have been plotted.

        Notes
        -----
        Images are plotted using either `~matplotlib.axes.Axes.imshow` or
        `~matplotlib.axes.Axes.pcolormesh`, and contours are plotted using
        `~matplotlib.axes.Axes.contour`.
        The Matplotlib arguments accepted by the plotting method are passed to it.
        (For compatability reasons, we enforce a more restrictive set of
        accepted `~matplotlib.axes.Axes.pcolormesh` arguments.)
        If any Matplotlib arguments are not used by any plotting method,
        a ``TypeError`` will be raised.
        The ``sunpy.map.compositemap`` module includes variables which list the
        full set of arguments passed to each plotting method. These are:

        >>> import sunpy.map.compositemap
        >>> sorted(sunpy.map.compositemap.ACCEPTED_IMSHOW_KWARGS)
        {ACCEPTED_IMSHOW_KWARGS}
        >>> sorted(sunpy.map.compositemap.ACCEPTED_PCOLORMESH_KWARGS)
        {ACCEPTED_PCOLORMESH_KWARGS}
        >>> sorted(sunpy.map.compositemap.ACCEPTED_CONTOUR_KWARGS)
        {ACCEPTED_CONTOUR_KWARGS}

        If a transformation is required to overlay the maps with the correct
        alignment, the plot limits may need to be manually set because
        Matplotlib autoscaling may not work as intended.
        """

        # If axes are not provided, create a WCSAxes based on the first map
        if not axes:
            axes = wcsaxes_compat.gca_wcs(self._maps[0].wcs)

        if annotate:
            axes.set_xlabel(axis_labels_from_ctype(self._maps[0].coordinate_system[0],
                                                   self._maps[0].spatial_units[0]))
            axes.set_ylabel(axis_labels_from_ctype(self._maps[0].coordinate_system[1],
                                                   self._maps[0].spatial_units[1]))
            axes.set_title(title)

        # Checklist to determine unused keywords in `matplot_args`
        unused_kwargs = set(matplot_args.keys())

        # Define a list of plotted objects
        ret = []
        # Plot layers of composite map
        for m in self._maps:
            # Parameters for plotting
            params = {
                "alpha": m.alpha,
                "zorder": m.zorder,
            }
            params.update(matplot_args)

            # The request to show a map layer rendered as a contour is indicated by a
            # non False levels property.
            if m.levels is False:
                # We tell GenericMap.plot() that we need to autoalign the map
                if wcsaxes_compat.is_wcsaxes(axes):
                    params['autoalign'] = True

                # Filter `matplot_args`
                if params.get('autoalign', None) in (True, 'pcolormesh'):
                    accepted_kwargs = ACCEPTED_PCOLORMESH_KWARGS
                else:
                    accepted_kwargs = ACCEPTED_IMSHOW_KWARGS
                for item in matplot_args.keys():
                    if item not in accepted_kwargs:
                        del params[item]
                    else:  # mark as used
                        unused_kwargs -= {item}

                params['annotate'] = False
                ret.append(m.plot(**params))
            else:
                # Filter `matplot_args`
                for item in matplot_args.keys():
                    if item not in ACCEPTED_CONTOUR_KWARGS:
                        del params[item]
                    else:  # mark as used
                        unused_kwargs -= {item}

                ret.append(m.draw_contours(m.levels, **params))

                # Set the label of the first line so a legend can be created
                ret[-1].collections[0].set_label(m.name)

        if len(unused_kwargs) > 0:
            raise TypeError(f'plot() got unexpected keyword arguments {unused_kwargs}')

        # Adjust axes extents to include all data
        axes.axis('image')

        # Set current image (makes colorbar work)
        plt.sci(ret[0])
        return ret
Пример #10
0
    def plot(self, axes=None, resample=None, annotate=True,
             interval=200, plot_function=None, **kwargs):
        """
        A animation plotting routine that animates each element in the
        MapCube

        Parameters
        ----------
        gamma: float
            Gamma value to use for the color map

        axes: mpl axes
            axes to plot the animation on, if none uses current axes

        resample: list or False
            Draws the map at a lower resolution to increase the speed of
            animation. Specify a list as a fraction i.e. [0.25, 0.25] to
            plot at 1/4 resolution.
            [Note: this will only work where the map arrays are the same size]

        annotate: bool
            Annotate the figure with scale and titles

        interval: int
            Animation interval in ms

        plot_function : function
            A function to be called as each map is plotted. Any variables
            returned from the function will have their ``remove()`` method called
            at the start of the next frame so that they are removed from the plot.

        Examples
        --------
        >>> import matplotlib.pyplot as plt
        >>> import matplotlib.animation as animation
        >>> from sunpy.map import Map

        >>> cube = Map(files, cube=True)   # doctest: +SKIP
        >>> ani = cube.plot(colorbar=True)   # doctest: +SKIP
        >>> plt.show()   # doctest: +SKIP

        Plot the map at 1/2 original resolution

        >>> cube = Map(files, cube=True)   # doctest: +SKIP
        >>> ani = cube.plot(resample=[0.5, 0.5], colorbar=True)   # doctest: +SKIP
        >>> plt.show()   # doctest: +SKIP

        Save an animation of the MapCube

        >>> cube = Map(res, cube=True)   # doctest: +SKIP

        >>> ani = cube.plot()   # doctest: +SKIP

        >>> Writer = animation.writers['ffmpeg']   # doctest: +SKIP
        >>> writer = Writer(fps=10, metadata=dict(artist='SunPy'), bitrate=1800)   # doctest: +SKIP

        >>> ani.save('mapcube_animation.mp4', writer=writer)   # doctest: +SKIP

        Save an animation with the limb at each time step

        >>> def myplot(fig, ax, sunpy_map):
        ...    p = sunpy_map.draw_limb()
        ...    return p
        >>> cube = Map(files, cube=True)   # doctest: +SKIP
        >>> ani = cube.peek(plot_function=myplot)   # doctest: +SKIP
        >>> plt.show()   # doctest: +SKIP

        """
        if not axes:
            axes = wcsaxes_compat.gca_wcs(self.maps[0].wcs)
        fig = axes.get_figure()

        if not plot_function:
            plot_function = lambda fig, ax, smap: []
        removes = []

        # Normal plot
        def annotate_frame(i):
            axes.set_title("{s.name}".format(s=self[i]))

            # x-axis label
            if self[0].coordinate_system.x == 'HG':
                xlabel = 'Longitude [{lon}'.format(lon=self[i].units.x)
            else:
                xlabel = 'X-position [{xpos}]'.format(xpos=self[i].units.x)

            # y-axis label
            if self[0].coordinate_system.y == 'HG':
                ylabel = 'Latitude [{lat}]'.format(lat=self[i].units.y)
            else:
                ylabel = 'Y-position [{ypos}]'.format(ypos=self[i].units.y)

            axes.set_xlabel(xlabel)
            axes.set_ylabel(ylabel)

        if resample:
            # This assumes that the maps are homogeneous!
            # TODO: Update this!
            resample = np.array(len(self.maps)-1) * np.array(resample)
            ani_data = [x.resample(resample) for x in self.maps]
        else:
            ani_data = self.maps

        im = ani_data[0].plot(axes=axes, **kwargs)

        def updatefig(i, im, annotate, ani_data, removes):
            while removes:
                removes.pop(0).remove()

            im.set_array(ani_data[i].data)
            im.set_cmap(self.maps[i].plot_settings['cmap'])

            norm = deepcopy(self.maps[i].plot_settings['norm'])
            # The following explicit call is for bugged versions of Astropy's ImageNormalize
            norm.autoscale_None(ani_data[i].data)
            im.set_norm(norm)

            if wcsaxes_compat.is_wcsaxes(axes):
                im.axes.reset_wcs(self.maps[i].wcs)
                wcsaxes_compat.default_wcs_grid(axes)
            else:
                im.set_extent(np.concatenate((self.maps[i].xrange.value,
                                              self.maps[i].yrange.value)))

            if annotate:
                annotate_frame(i)
            removes += list(plot_function(fig, axes, self.maps[i]))

        ani = matplotlib.animation.FuncAnimation(fig, updatefig,
                                                frames=list(range(0, len(self.maps))),
                                                fargs=[im, annotate, ani_data, removes],
                                                interval=interval,
                                                blit=False)

        return ani
Пример #11
0
 top_right = SkyCoord(1000 * u.arcsec,
                      1000 * u.arcsec,
                      frame=magproc.coordinate_frame)
 submag = magproc.submap(bottom_left, top_right)
 subar = thisar.submap(bottom_left, top_right)
 subpsl = pslmap.submap(bottom_left, top_right)
 # limb nans
 limbmask[np.where(limbmask == 0.)] = np.nan
 limbmap = sunpy.map.Map(limbmask, magproc.meta)
 sublimb = limbmap.submap(bottom_left, top_right)
 submag = sunpy.map.Map(submag.data * sublimb.data, submag.meta)
 subar = sunpy.map.Map(subar.data * sublimb.data, subar.meta)
 subpsl = sunpy.map.Map(subpsl.data * sublimb.data, subpsl.meta)
 # Draw solar lat/lon grid
 figure = plt.figure()
 axes = wcsaxes_compat.gca_wcs(submag.wcs)
 image = submag.plot(vmin=-500, vmax=500, axes=axes)
 axes.coords.grid(False)
 overlay = grid_overlay(axes, grid_spacing=10 * u.deg)
 #    plt.colorbar(label='B [G]')
 # Overlay PILs and SMART detections
 #       plt.contour(subpsl.data, origin='lower',
 #               colors='yellow', linewidths=0.5,
 #               vmin=0., vmax=np.max(np.unique(thisar.data))+1)
 plt.contour(subar.data > 0.,
             origin='lower',
             colors='blue',
             linewidths=1.0,
             vmin=0.,
             vmax=np.max(np.unique(subar.data)) + 1)
 plt.savefig(data_dir + smartdate + '.eps')
Пример #12
0
    def plot(self, annotate=True, axes=None, title=True, **imshow_kwargs):
        """ Plots the map object using matplotlib, in a method equivalent
        to plt.imshow() using nearest neighbour interpolation.

        Parameters
        ----------
        annotate : bool
            If True, the data is plotted at it's natural scale; with
            title and axis labels.

        axes: `~matplotlib.axes` or None
            If provided the image will be plotted on the given axes. Else the
            current matplotlib axes will be used.

        **imshow_kwargs  : dict
            Any additional imshow arguments that should be used
            when plotting.

        Examples
        --------
        #Simple Plot with color bar
        >>> aiamap.plot()   # doctest: +SKIP
        >>> plt.colorbar()   # doctest: +SKIP

        #Add a limb line and grid
        >>> aia.plot()   # doctest: +SKIP
        >>> aia.draw_limb()   # doctest: +SKIP
        >>> aia.draw_grid()   # doctest: +SKIP

        """

        # Get current axes
        if not axes:
            axes = wcsaxes_compat.gca_wcs(self.wcs)

        # Check that the image is properly oriented
        if not wcsaxes_compat.is_wcsaxes(axes) and not np.array_equal(self.rotation_matrix, np.matrix(np.identity(2))):
            warnings.warn("This map is not properly oriented. Plot axes may be incorrect", Warning)

        # Normal plot
        imshow_args = deepcopy(self.plot_settings)
        if imshow_args.has_key("title"):
            plot_settings_title = imshow_args.pop("title")
        else:
            plot_settings_title = self.name

        if annotate:
            if title is True:
                title = plot_settings_title

            if title:
                axes.set_title(title)

            # x-axis label
            if self.coordinate_system.x == "HG":
                xlabel = "Longitude [{lon}]".format(lon=self.units.x)
            else:
                xlabel = "X-position [{xpos}]".format(xpos=self.units.x)

            # y-axis label
            if self.coordinate_system.y == "HG":
                ylabel = "Latitude [{lat}]".format(lat=self.units.y)
            else:
                ylabel = "Y-position [{ypos}]".format(ypos=self.units.y)

            axes.set_xlabel(xlabel)
            axes.set_ylabel(ylabel)

        if not wcsaxes_compat.is_wcsaxes(axes):
            imshow_args.update({"extent": list(self.xrange.value) + list(self.yrange.value)})
        imshow_args.update(imshow_kwargs)

        if self.mask is None:
            ret = axes.imshow(self.data, **imshow_args)
        else:
            ret = axes.imshow(np.ma.array(np.asarray(self.data), mask=self.mask), **imshow_args)

        if wcsaxes_compat.is_wcsaxes(axes):
            wcsaxes_compat.default_wcs_grid(axes)

        # Set current image (makes colorbar work)
        plt.sca(axes)
        plt.sci(ret)
        return ret
Пример #13
0
    def _plot_2D_cube(self, axes=None, plot_axis_indices=None, axes_coordinates=None,
                      axes_units=None, data_unit=None, **kwargs):
        """
        Plots a 2D image onto the current
        axes. Keyword arguments are passed on to matplotlib.

        Parameters
        ----------
        axes: `astropy.visualization.wcsaxes.core.WCSAxes` or `None`:
            The axes to plot onto. If None the current axes will be used.

        plot_axis_indices: `list`.
            The first axis in WCS object will become the first axis of plot_axis_indices and
            second axis in WCS object will become the second axis of plot_axis_indices.
            Default: ['x', 'y']

        """
        # Set default values of kwargs if not set.
        if axes_coordinates is None:
            axes_coordinates = [None, None]
        if axes_units is None:
            axes_units = [None, None]
        # Set which cube dimensions are on the x an y axes.
        axis_data = ['x', 'x']
        axis_data[plot_axis_indices[1]] = 'y'
        axis_data = axis_data[::-1]
        # Determine data to be plotted
        if data_unit is None:
            data = self.data
        else:
            # If user set data_unit, convert dat to desired unit if self.unit set.
            if self.unit is None:
                raise TypeError("Can only set data_unit if NDCube.unit is set.")
            else:
                data = (self.data * self.unit).to(data_unit).value
        # Combine data with mask
        data = np.ma.masked_array(data, self.mask)
        if axes is None:
            try:
                axes_coord_check == [None, None]
            except:
                axes_coord_check = False
            if axes_coord_check:
                # Build slice list for WCS for initializing WCSAxes object.
                if self.wcs.naxis is not 2:
                    slice_list = []
                    index = 0
                    for i, bool_ in enumerate(self.missing_axes):
                        if not bool_:
                            slice_list.append(axis_data[index])
                            index += 1
                        else:
                            slice_list.append(1)
                    if index is not 2:
                        raise ValueError("Dimensions of WCS and data don't match")
                ax = wcsaxes_compat.gca_wcs(self.wcs, slices=slice_list)
                # Set axis labels
                x_wcs_axis = utils.cube.data_axis_to_wcs_axis(plot_axis_indices[0],
                                                              self.missing_axes)
                ax.set_xlabel("{0} [{1}]".format(
                    self.world_axis_physical_types[plot_axis_indices[0]],
                    self.wcs.wcs.cunit[x_wcs_axis]))
                y_wcs_axis = utils.cube.data_axis_to_wcs_axis(plot_axis_indices[1],
                                                              self.missing_axes)
                ax.set_ylabel("{0} [{1}]".format(
                    self.world_axis_physical_types[plot_axis_indices[1]],
                    self.wcs.wcs.cunit[y_wcs_axis]))
                # Plot data
                ax.imshow(data, **kwargs)
            else:
                # Else manually set axes x and y values based on user's input for axes_coordinates.
                new_axes_coordinates, new_axis_units, default_labels = \
                  self._derive_axes_coordinates(axes_coordinates, axes_units)
                # Initialize axes object and set values along axis.
                fig, ax = plt.subplots(1, 1)
                # Since we can't assume the x-axis will be uniform, create NonUniformImage
                # axes and add it to the axes object.
                if plot_axis_indices[0] < plot_axis_indices[1]:
                    data = data.transpose()
                im_ax = mpl.image.NonUniformImage(
                    ax, extent=(new_axes_coordinates[plot_axis_indices[0]][0],
                                new_axes_coordinates[plot_axis_indices[0]][-1],
                                new_axes_coordinates[plot_axis_indices[1]][0],
                                new_axes_coordinates[plot_axis_indices[1]][-1]), **kwargs)
                im_ax.set_data(new_axes_coordinates[plot_axis_indices[0]],
                               new_axes_coordinates[plot_axis_indices[1]], data)
                ax.add_image(im_ax)
                # Set the limits, labels, etc. of the axes.
                xlim = kwargs.pop("xlim", (new_axes_coordinates[plot_axis_indices[0]][0],
                                           new_axes_coordinates[plot_axis_indices[0]][-1]))
                ax.set_xlim(xlim)
                ylim = kwargs.pop("xlim", (new_axes_coordinates[plot_axis_indices[1]][0],
                                           new_axes_coordinates[plot_axis_indices[1]][-1]))
                ax.set_ylim(ylim)
                xlabel = kwargs.pop("xlabel", default_labels[plot_axis_indices[0]])
                ylabel = kwargs.pop("ylabel", default_labels[plot_axis_indices[1]])
                ax.set_xlabel(xlabel)
                ax.set_ylabel(ylabel)
        return ax
Пример #14
0
    def _plot_2D_cube(self,
                      axes=None,
                      plot_axis_indices=None,
                      axes_coordinates=None,
                      axes_units=None,
                      data_unit=None,
                      **kwargs):
        """
        Plots a 2D image onto the current axes. Keyword arguments are passed on
        to matplotlib.

        Parameters
        ----------
        axes: `astropy.visualization.wcsaxes.core.WCSAxes` or `None`:
            The axes to plot onto. If None the current axes will be used.

        plot_axis_indices: `list`.
            The first axis in WCS object will become the first axis of plot_axis_indices and
            second axis in WCS object will become the second axis of plot_axis_indices.
            Default: ['x', 'y']
        """
        # Set default values of kwargs if not set.
        if axes_coordinates is None:
            axes_coordinates = [None, None]
        if axes_units is None:
            axes_units = [None, None]
        # Set which cube dimensions are on the x an y axes.
        axis_data = ['x', 'x']
        axis_data[plot_axis_indices[1]] = 'y'
        axis_data = axis_data[::-1]
        # Determine data to be plotted
        if data_unit is None:
            data = self.data
        else:
            # If user set data_unit, convert dat to desired unit if self.unit set.
            if self.unit is None:
                raise TypeError(
                    "Can only set data_unit if NDCube.unit is set.")
            else:
                data = (self.data * self.unit).to(data_unit).value
        # Combine data with mask
        data = np.ma.masked_array(data, self.mask)
        try:
            axes_coord_check = axes_coordinates == [None, None]
        except Exception:
            axes_coord_check = False
        if axes_coord_check and (isinstance(axes, WCSAxes) or axes is None):
            if axes is None:
                # Build slice list for WCS for initializing WCSAxes object.
                if self.wcs.naxis != 2:
                    slice_list = []
                    index = 0
                    for bool_ in self.missing_axes:
                        if not bool_:
                            slice_list.append(axis_data[index])
                            index += 1
                        else:
                            slice_list.append(1)
                    if index != 2:
                        raise ValueError(
                            "Dimensions of WCS and data don't match")
                    axes = wcsaxes_compat.gca_wcs(self.wcs,
                                                  slices=tuple(slice_list))
                else:
                    axes = wcsaxes_compat.gca_wcs(self.wcs)

            # Plot data
            axes.imshow(data, **kwargs)

            # Set axis labels
            x_wcs_axis = utils.cube.data_axis_to_wcs_axis(
                plot_axis_indices[0], self.missing_axes)

            axes.coords[x_wcs_axis].set_axislabel("{} [{}]".format(
                self.world_axis_physical_types[plot_axis_indices[0]],
                self.wcs.wcs.cunit[x_wcs_axis]))

            y_wcs_axis = utils.cube.data_axis_to_wcs_axis(
                plot_axis_indices[1], self.missing_axes)

            axes.coords[y_wcs_axis].set_axislabel("{} [{}]".format(
                self.world_axis_physical_types[plot_axis_indices[1]],
                self.wcs.wcs.cunit[y_wcs_axis]))

        else:
            # Else manually set axes x and y values based on user's input for axes_coordinates.
            new_axes_coordinates, new_axis_units, default_labels = \
                self._derive_axes_coordinates(axes_coordinates, axes_units, data.shape)
            # Initialize axes object and set values along axis.
            if axes is None:
                axes = plt.gca()
            # Since we can't assume the x-axis will be uniform, create NonUniformImage
            # axes and add it to the axes object.
            if plot_axis_indices[0] < plot_axis_indices[1]:
                data = data.transpose()
            im_ax = mpl.image.NonUniformImage(
                axes,
                extent=(new_axes_coordinates[plot_axis_indices[0]][0],
                        new_axes_coordinates[plot_axis_indices[0]][-1],
                        new_axes_coordinates[plot_axis_indices[1]][0],
                        new_axes_coordinates[plot_axis_indices[1]][-1]),
                **kwargs)
            im_ax.set_data(new_axes_coordinates[plot_axis_indices[0]],
                           new_axes_coordinates[plot_axis_indices[1]], data)
            axes.add_image(im_ax)
            # Set the limits, labels, etc. of the axes.
            xlim = kwargs.pop("xlim",
                              (new_axes_coordinates[plot_axis_indices[0]][0],
                               new_axes_coordinates[plot_axis_indices[0]][-1]))
            axes.set_xlim(xlim)
            ylim = kwargs.pop("xlim",
                              (new_axes_coordinates[plot_axis_indices[1]][0],
                               new_axes_coordinates[plot_axis_indices[1]][-1]))
            axes.set_ylim(ylim)

            xlabel = kwargs.pop("xlabel", default_labels[plot_axis_indices[0]])
            ylabel = kwargs.pop("ylabel", default_labels[plot_axis_indices[1]])
            axes.set_xlabel(xlabel)
            axes.set_ylabel(ylabel)

        return axes
Пример #15
0
def main(input_folder='/Users/sophie/data/smart/track_test/',
         group='magprop',
         property='totarea',
         *smart_folder,
         **output_folder):
    """
    Parameters
    ----------
    input_folder : Folder string location of .json files with true_id's
    group : Indicate what group in .json file the property you wish to plot is, e.g. 'magprop'
    property : Indicate what property you wish to plot, e.g. 'totarea'
    smart_folder : Optional folder location to load maps and detections from if not in same folder as json
    output_folder: Optional output folder location of images created with algorithm

    Returns
    -------
    """
    #    if not input_folder:
    #        input_folder = os.getcwd() + '/'
    if not smart_folder:
        smart_folder = input_folder
    if not output_folder:
        output_folder = input_folder

    # load json files
    filenames = sorted(os.listdir(input_folder))
    filenames_json = [x for x in filenames if ".json" in x]

    filename_dates = [datetime_from_file_string(x) for x in filenames_json]
    start_date, end_date = filename_dates[0], filename_dates[
        len(filename_dates) - 1]

    date_strings = []
    for index, value in enumerate(filename_dates):
        if start_date <= value <= end_date:
            date_strings.append([filenames_json[index][:13],
                                 value])  #todo get rid of hardcoded 13

    # get properties (time and value for each id)
    property_values = {}
    x_position, y_position = {}, {}
    for date_string in date_strings:
        json_filename = input_folder + date_string[0] + "_properties.json"
        json_data = json.load(open(json_filename))
        for key, value in json_data['posprop']['trueid'].items():
            if str(value) in property_values:
                property_values[str(value)][0].append(date_string[1])
                property_values[str(value)][1].append(
                    json_data[group][property][str(key)])
            else:
                property_values[str(value)] = [[
                    date_string[1]
                ], [json_data[group][property][str(key)]]]
            if str(value) in x_position:
                x_position[str(value)].append(
                    json_data['posprop']['xcenarea'][str(key)])
                y_position[str(value)].append(
                    json_data['posprop']['ycenarea'][str(key)])
            else:
                x_position[str(value)] = [
                    json_data['posprop']['xcenarea'][str(key)]
                ]
                y_position[str(value)] = [
                    json_data['posprop']['ycenarea'][str(key)]
                ]

    #----------------------------------------
    # get detection outlines as outline_edges

    count = 1
    for date_string in date_strings:
        detection_filename = smart_folder + date_string[0] + "_detections.fits"
        detection_map = sunpy.map.Map(detection_filename)

        #----------------------------------------
        # get actual image of sun
        magnetogram_filename = smart_folder + date_string[0] + "_map.fits"
        magnetogram_map = sunpy.map.Map(magnetogram_filename)

        #----------------------------------------
        # read in numbers and centroids from json
        json_data = json.load(
            open(input_folder + date_string[0] + "_properties.json"))
        # smart id
        number_json = list(json_data['posprop']['trueid'].keys())
        # the tracked ids in the data
        number_json_values = [
            json_data['posprop']['trueid'][i] for i in number_json
        ]

        json_centx, json_centy = [], []
        for i in number_json:
            json_centx.append(json_data['posprop']['xcenarea'][i])
            json_centy.append(json_data['posprop']['ycenarea'][i])

        #----------------------------------------
        # plot evolution of property
        fig = plt.figure(figsize=(10, 12))
        ax1 = fig.add_subplot(2, 1, 2)

        colors = itertools.cycle([
            "black", "grey", "brown", "orange", "red", "pink", "purple",
            "blue", "turquoise", "green"
        ])

        for key, value in property_values.items():
            ax1.plot(value[0], value[1], label=key, marker='o', markersize=3.0)
            plt.legend(loc='upper left')

        import matplotlib.dates as mdates
        ax1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d %H'))
        plt.gcf().autofmt_xdate()
        plt.xlabel('Date and time [UT]')

        plt.axvline(date_string[1], linestyle="dashed", color="black")
        plt.ylabel('Total area [m.s.h]')  #todo should be meta data

        # plot detections on sunpy map of magnetogram
        ax1 = fig.add_subplot(2, 1, 1, projection=magnetogram_map)
        plt.subplots_adjust(left=0.1,
                            bottom=0.1,
                            right=0.95,
                            top=0.95,
                            wspace=None,
                            hspace=None)

        # Get same axes
        bottom_left = SkyCoord(-1000 * u.arcsec,
                               -1000 * u.arcsec,
                               frame=magnetogram_map.coordinate_frame)
        top_right = SkyCoord(1000 * u.arcsec,
                             1000 * u.arcsec,
                             frame=magnetogram_map.coordinate_frame)
        submap = magnetogram_map.submap(bottom_left, top_right)
        axes = wcsaxes_compat.gca_wcs(magnetogram_map.wcs)

        image = magnetogram_map.plot(vmin=-500, vmax=500, axes=axes)

        # Draw solar lat/lon grid
        axes.coords.grid(False)
        overlay = grid_overlay(axes, grid_spacing=10 * u.deg)
        #        plt.colorbar(label='B [G]')

        plt.contour(detection_map.data,
                    origin='lower',
                    colors='lightblue',
                    linewidths=0.5)

        # add numbers
        plt.plot(json_centx, json_centy, 'or', color='yellow', markersize=2.0)
        for x, y, numb in zip(json_centx, json_centy, number_json_values):
            plt.text(x + 10, y + 10, str(numb), color='yellow')
        plt.title(date_string[1].strftime('%Y %B %d %H:%M'))

        plt.savefig(output_folder + date_string[0] + "_tracking.png", dpi=150)
        plt.close()

    # convert to gif
    images = []
    filenames = sorted(os.listdir(input_folder))
    filenames_images = [x for x in filenames if "_tracking.png" in x]
    filenames_images = [input_folder + x for x in filenames_images]
    for filename in filenames_images:
        images.append(imageio.imread(filename))
    imageio.mimwrite(input_folder + 'SMART_evolution.gif', images, fps=1.)