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]))
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]))
def _drawFallback(self, model): self.figure.clear() try: s_map = model.map plot_preferences = model.plot_preferences ax = self.figure.add_subplot(111, projection=s_map) image = ax.imshow(model.data, cmap=model.cmap, norm=model.norm, interpolation=model.interpolation, origin=model.origin) if plot_preferences["show_colorbar"]: self.figure.colorbar(image) if plot_preferences["show_limb"]: s_map.draw_limb(axes=ax) if plot_preferences["contours"]: levels = sorted(plot_preferences["contours"]) s_map.draw_contours(levels * u.percent, axes=ax) if plot_preferences["draw_grid"]: s_map.draw_grid(grid_spacing=10 * u.deg, axes=ax) if plot_preferences["wcs_grid"] and wcsaxes_compat.is_wcsaxes(ax): wcsaxes_compat.default_wcs_grid(ax, units=s_map.spatial_units, ctypes=s_map.wcs.wcs.ctype) except Exception as ex: self.figure.clear() self.figure.text(0.5, 0.5, s="Error during rendering data: " + str(ex), ha="center", va="center")
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, ani_data[i].spatial_units, ani_data[i].coordinate_system) else: im.set_extent( np.concatenate( (ani_data[i].xrange.value, ani_data[i].yrange.value))) if annotate: annotate_frame(i) removes += list(plot_function(fig, axes, ani_data[i]))
def updatefig(self, val, im, slider): # Remove all the objects that need to be removed from the # plot while self.remove_obj: self.remove_obj.pop(0).remove() i = int(val) im.set_array(self.data[i].data) im.set_cmap(self.mapcube[i].plot_settings['cmap']) norm = deepcopy(self.mapcube[i].plot_settings['norm']) # The following explicit call is for bugged versions of Astropy's ImageNormalize norm.autoscale_None(self.data[i].data) im.set_norm(norm) if wcsaxes_compat.is_wcsaxes(im.axes): im.axes.reset_wcs(self.mapcube[i].wcs) wcsaxes_compat.default_wcs_grid(im.axes) # Having this line in means the plot will resize for non-homogenous # maps. However it also means that if you zoom in on the plot bad # things happen. # im.set_extent(self.mapcube[i].xrange + self.mapcube[i].yrange) if self.annotate: self._annotate_plot(i) self.remove_obj += list(self.user_plot_function(self.fig, self.axes, self.mapcube[i]))
def updatefig(self, val, im, slider): # Remove all the objects that need to be removed from the # plot while self.remove_obj: self.remove_obj.pop(0).remove() i = int(val) im.set_array(self.data[i].data) im.set_cmap(self.mapcube[i].plot_settings['cmap']) norm = deepcopy(self.mapcube[i].plot_settings['norm']) # The following explicit call is for bugged versions of Astropy's ImageNormalize norm.autoscale_None(self.data[i].data) im.set_norm(norm) if wcsaxes_compat.is_wcsaxes(im.axes): im.axes.reset_wcs(self.mapcube[i].wcs) wcsaxes_compat.default_wcs_ticks(im.axes, self.mapcube[i].spatial_units, self.mapcube[i].coordinate_system) # Having this line in means the plot will resize for non-homogenous # maps. However it also means that if you zoom in on the plot bad # things happen. # im.set_extent(self.mapcube[i].xrange + self.mapcube[i].yrange) if self.annotate: self._annotate_plot(i) self.remove_obj += list( self.user_plot_function(self.fig, self.axes, self.mapcube[i]))
def prime_meridian(axes, *, rsun: u.m = R_sun, resolution=500, **kwargs): """ Draws the solar prime meridian (zero Carrington longitude) as seen by the axes observer. Hidden parts are drawn as a dotted line. Parameters ---------- axes : `matplotlib.axes.Axes` The axes to plot the prime meridian on, or "None" to use current axes. rsun : `~astropy.units.Quantity` Solar radius (in physical length units) at which to draw the solar prime meridian. Defaults to the standard photospheric radius. resolution : `int` The number of points used to represent the prime meridian. Returns ------- visible : `~matplotlib.patches.Polygon` The patch added to the axes for the visible part of the solar equator. hidden : `~matplotlib.patches.Polygon` The patch added to the axes for the hidden part of the solar equator. """ if not wcsaxes_compat.is_wcsaxes(axes): raise ValueError('axes must be a WCSAxes') axes_frame = wcsapi_to_celestial_frame(axes.wcs) if not hasattr(axes_frame, 'observer'): raise ValueError( 'the coordinate frame of the WCSAxes does not have an observer, ' 'so zero Carrington longitude cannot be determined.') observer = axes_frame.observer lon = 0 * u.deg lon0 = SkyCoord(np.ones(resolution) * lon, np.linspace(-90, 90, resolution) * u.deg, radius=rsun, frame=HeliographicCarrington(observer=observer, obstime=axes_frame.obstime)) visible, hidden = _plot_vertices(lon0, axes, axes_frame, rsun, close_path=False, **kwargs) return visible, hidden
def _mpl_imshow_kwargs(self, axes, cmap): """ Return the keyword arguments for imshow to display this map """ if wcsaxes_compat.is_wcsaxes(axes): kwargs = {'cmap': cmap, 'origin': 'lower', 'norm': self.mpl_color_normalizer, 'interpolation': 'nearest'} else: # make imshow kwargs a dict kwargs = {'origin': 'lower', 'cmap': cmap, 'norm': self.mpl_color_normalizer, 'extent': list(self.xrange.value) + list(self.yrange.value), 'interpolation': 'nearest'} return kwargs
def equator(axes, *, rsun: u.m = R_sun, resolution=500, **kwargs): """ Draws the solar equator as seen by the axes observer. Hidden parts are drawn as a dotted line. Parameters ---------- axes : `matplotlib.axes.Axes` The axes to plot the equator on. rsun : `~astropy.units.Quantity` Solar radius (in physical length units) at which to draw the solar equator. Defaults to the standard photospheric radius. resolution : `int` The number of points used to represent the equator. Returns ------- visible : `~matplotlib.patches.Polygon` The patch added to the axes for the visible part of the solar equator. hidden : `~matplotlib.patches.Polygon` The patch added to the axes for the hidden part of the solar equator. Examples -------- >>> import sunpy.map >>> from sunpy.visualization import draw >>> import sunpy.data.sample # doctest: +REMOTE_DATA >>> aia = sunpy.map.Map(sunpy.data.sample.AIA_171_IMAGE) # doctest: +REMOTE_DATA >>> fig = plt.figure() # doctest: +SKIP >>> ax = fig.add_subplot(projection=aia) # doctest: +SKIP >>> aia.plot() # doctest: +SKIP >>> draw.equator(ax) # doctest: +SKIP """ if not wcsaxes_compat.is_wcsaxes(axes): raise ValueError('axes must be a WCSAxes') axes_frame = wcsapi_to_celestial_frame(axes.wcs) lat = 0 * u.deg lat0 = SkyCoord(np.linspace(-180, 179, resolution) * u.deg, np.ones(resolution) * lat, radius=rsun, frame=HeliographicStonyhurst(obstime=axes_frame.obstime)) visible, hidden = _plot_vertices(lat0, axes, axes_frame, rsun, **kwargs) return visible, hidden
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 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]))
def updatefig(self, val, im, slider): """ ? Parameters ---------- val : ? ? im : ? ? Returns ------- .. todo:: improve documentation """ # Remove all the objects that need to be removed from the # plot while self.remove_obj: self.remove_obj.pop(0).remove() i = int(val) im.set_array(self.data[i].data) im.set_cmap(self.mapcube[i].plot_settings['cmap']) im.set_norm(self.mapcube[i].plot_settings['norm']) if wcsaxes_compat.is_wcsaxes(im.axes): im.axes.reset_wcs(self.mapcube[i].wcs) wcsaxes_compat.default_wcs_grid(im.axes) # Having this line in means the plot will resize for non-homogenous # maps. However it also means that if you zoom in on the plot bad # things happen. # im.set_extent(self.mapcube[i].xrange + self.mapcube[i].yrange) if self.annotate: self._annotate_plot(i) self.remove_obj += list(self.user_plot_function(self.fig, self.axes, self.mapcube[i]))
def updatefig(self, val, im, slider): # Remove all the objects that need to be removed from the # plot while self.remove_obj: self.remove_obj.pop(0).remove() i = int(val) im.set_array(self.data[i].data) im.set_cmap(self.mapsequence[i].plot_settings['cmap']) norm = deepcopy(self.mapsequence[i].plot_settings['norm']) im.set_norm(norm) if wcsaxes_compat.is_wcsaxes(im.axes): im.axes.reset_wcs(self.mapsequence[i].wcs) # Having this line in means the plot will resize for non-homogenous # maps. However it also means that if you zoom in on the plot bad # things happen. # im.set_extent(self.mapsequence[i].xrange + self.mapsequence[i].yrange) if self.annotate: self._annotate_plot(i) self.remove_obj += list( self.user_plot_function(self.fig, self.axes, self.mapsequence[i]))
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 draw_limb(axes, observer, *, rsun: u.m = R_sun, resolution=1000, **kwargs): """ Draws the solar limb as seen by the specified observer. The limb is a circle for only the simplest plots. If the specified observer of the limb is different from the observer of the coordinate frame of the plot axes, not only may the limb not be a true circle, a portion of the limb may be hidden from the observer. In that case, the circle is divided into visible and hidden segments, represented by solid and dotted lines, respectively. Parameters ---------- axes : `~matplotlib.axes` or ``None`` Axes to plot limb on. observer : `astropy.coordinates.SkyCoord` Observer coordinate for which the limb is drawn. rsun : `~astropy.units.Quantity` Solar radius (in physical length units) at which to draw the limb. Defaults to the standard photospheric radius. resolution : `int` The number of points to use to represent the limb. Returns ------- visible : `~matplotlib.patches.Polygon` or `~matplotlib.patches.Circle` or None The patch added to the axes for the visible part of the limb (i.e., the "near" side of the Sun). hidden : `~matplotlib.patches.Polygon` or None The patch added to the axes for the hidden part of the limb (i.e., the "far" side of the Sun). Notes ----- Keyword arguments are passed onto the patches. If the limb is a true circle, ``visible`` will instead be `~matplotlib.patches.Circle` and ``hidden`` will be ``None``. If there are no hidden points (e.g., on a synoptic map any limb is fully visible) ``hidden`` will be ``None``. If there are no visible points (e.g., for an observer on the opposite side of the Sun to the map observer) ``visible`` will be ``None``. To avoid triggering Matplotlib auto-scaling, these patches are added as artists instead of patches. One consequence is that the plot legend is not populated automatically when the limb is specified with a text label. See :ref:`sphx_glr_gallery_text_labels_and_annotations_custom_legends.py` in the Matplotlib documentation for examples of creating a custom legend. """ if not wcsaxes_compat.is_wcsaxes(axes): raise ValueError('axes must be a WCSAxes') c_kw = {'fill': False, 'color': 'white', 'zorder': 100} c_kw.update(kwargs) transform = axes.get_transform('world') # transform is always passed on as a keyword argument c_kw.setdefault('transform', transform) # If the observer matches the axes's observer frame and is Helioprojective, use a Circle axes_frame = axes._transform_pixel2world.frame_out if isinstance(axes_frame, Helioprojective): axes_observer = SkyCoord(axes_frame.observer) if axes_observer.separation_3d(observer) < 1 * u.m: distance = observer.transform_to( HeliocentricInertial).spherical.distance # Obtain the solar radius and the world->pixel transform angular_radius = _angular_radius(rsun, distance) circ = patches.Circle([0, 0], radius=angular_radius.to_value(u.deg), **c_kw) axes.add_artist(circ) return circ, None # Otherwise, we use Polygon to be able to distort the limb # Create the limb coordinate array using Heliocentric Radial limb = get_limb_coordinates(observer, rsun, resolution) # Transform the limb to the axes frame and get the 2D vertices limb_in_axes = limb.transform_to(axes_frame) Tx = limb_in_axes.spherical.lon.to_value(u.deg) Ty = limb_in_axes.spherical.lat.to_value(u.deg) vertices = np.array([Tx, Ty]).T # Determine which points are visible if hasattr(axes_frame, 'observer'): # The reference distance is the distance to the limb for the axes # observer rsun = getattr(axes_frame, 'rsun', rsun) reference_distance = np.sqrt(axes_frame.observer.radius**2 - rsun**2) is_visible = limb_in_axes.spherical.distance <= reference_distance else: # If the axes has no observer, the entire limb is considered visible is_visible = np.ones_like(limb_in_axes.spherical.distance, bool, subok=False) # Identify discontinuities in the limb. Uses the same approach as # astropy.visualization.wcsaxes.grid_paths.get_lon_lat_path() step = np.sqrt((vertices[1:, 0] - vertices[:-1, 0])**2 + (vertices[1:, 1] - vertices[:-1, 1])**2) continuous = np.concatenate([[True, True], step[1:] < 100 * step[:-1]]) visible, hidden = None, None if np.sum(is_visible) > 0: # Create the Polygon for the near side of the Sun (using a solid line) if 'linestyle' not in kwargs: c_kw['linestyle'] = '-' visible = patches.Polygon(vertices, **c_kw) _modify_polygon_visibility(visible, is_visible & continuous) # Add patches as artists rather than patches to avoid triggering auto-scaling axes.add_artist(visible) if np.sum(~is_visible) > 0: # Create the Polygon for the far side of the Sun (using a dotted line) if 'linestyle' not in kwargs: c_kw['linestyle'] = ':' hidden = patches.Polygon(vertices, **c_kw) _modify_polygon_visibility(hidden, ~is_visible & continuous) axes.add_artist(hidden) return visible, hidden
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 ----- If a line-width Matplotlib argument (``linewidth``, ``linewidths``, or ``lw``) is provided, it will apply only to those maps that are plotted as contours. 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) # 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: # Check if any linewidth argument is provided, if so, then delete it from params. for item in ['linewidth', 'linewidths', 'lw']: if item in matplot_args: del params[item] # We tell GenericMap.plot() that we need to autoalign the map if wcsaxes_compat.is_wcsaxes(axes): params['autoalign'] = True params['annotate'] = False ret.append(m.plot(**params)) else: 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) # Adjust axes extents to include all data axes.axis('image') # Set current image (makes colorbar work) plt.sci(ret[0]) return ret
def draw_limb(axes, observer, *, rsun: u.m = R_sun, resolution=1000, **kwargs): """ Draws the solar limb as seen by the specified observer. The limb is a circle for only the simplest plots. If the specified observer of the limb is different from the observer of the coordinate frame of the plot axes, not only may the limb not be a true circle, a portion of the limb may be hidden from the observer. In that case, the circle is divided into visible and hidden segments, represented by solid and dotted lines, respectively. Parameters ---------- axes : `~matplotlib.axes` or ``None`` Axes to plot limb on. observer : `astropy.coordinates.SkyCoord` Observer coordinate for which the limb is drawn. rsun : `~astropy.units.Quantity` Solar radius (in physical length units) at which to draw the limb. Defaults to the standard photospheric radius. resolution : `int` The number of points to use to represent the limb. Returns ------- visible : `~matplotlib.patches.Polygon` or `~matplotlib.patches.Circle` or None The patch added to the axes for the visible part of the limb (i.e., the "near" side of the Sun). hidden : `~matplotlib.patches.Polygon` or None The patch added to the axes for the hidden part of the limb (i.e., the "far" side of the Sun). Notes ----- Keyword arguments are passed onto the patches. If the limb is a true circle, ``visible`` will instead be `~matplotlib.patches.Circle` and ``hidden`` will be ``None``. If there are no hidden points (e.g., on a synoptic map any limb is fully visible) ``hidden`` will be ``None``. If there are no visible points (e.g., for an observer on the opposite side of the Sun to the map observer) ``visible`` will be ``None``. To avoid triggering Matplotlib auto-scaling, these patches are added as artists instead of patches. One consequence is that the plot legend is not populated automatically when the limb is specified with a text label. See :ref:`sphx_glr_gallery_text_labels_and_annotations_custom_legends.py` in the Matplotlib documentation for examples of creating a custom legend. """ if not wcsaxes_compat.is_wcsaxes(axes): raise ValueError('axes must be a WCSAxes') c_kw = {'fill': False, 'color': 'white', 'zorder': 100} c_kw.update(kwargs) transform = axes.get_transform('world') # transform is always passed on as a keyword argument c_kw.setdefault('transform', transform) # If the observer matches the axes's observer frame and is Helioprojective, use a Circle axes_frame = axes._transform_pixel2world.frame_out if isinstance(axes_frame, Helioprojective): axes_observer = SkyCoord(axes_frame.observer) if axes_observer.separation_3d(observer) < 1 * u.m: distance = observer.transform_to( HeliocentricInertial).spherical.distance # Obtain the solar radius and the world->pixel transform angular_radius = _angular_radius(rsun, distance) circ = patches.Circle([0, 0], radius=angular_radius.to_value(u.deg), **c_kw) axes.add_artist(circ) return circ, None # Otherwise, we use Polygon to be able to distort the limb # Create the limb coordinate array using Heliocentric Radial limb = get_limb_coordinates(observer, rsun, resolution) # Transform the limb to the axes frame and get the 2D vertices visible, hidden = _plot_vertices(limb, axes, axes_frame, rsun, **kwargs) return visible, hidden
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, 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 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