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
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
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]
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
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
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
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
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
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
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
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')
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
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
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
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.)