Example #1
0
def get_header(filepath):
    """Returns the header for a given file"""
    hdulist = pyfits.open(filepath)
    hdulist.verify('silentfix')
    
    comment = "".join(hdulist[0].header.get_comment()).strip()
    header = MapHeader(hdulist[0].header)
    header['comment'] = comment
            
    return header
Example #2
0
def get_header(filepath):
    """Reads the header in and saves it as a dictionary"""
    xmlstring = read_xmlbox(filepath, "fits")
    pydict = xml_to_dict(xmlstring)["fits"]
    
    #Fix types
    for k, v in pydict.items():
        if v.isdigit():
            pydict[k] = int(v)
        elif is_float(v):
            pydict[k] = float(v)
            
    # Remove newlines from comment
    pydict['comment'] = pydict['comment'].replace("\n", "")
            
    return MapHeader(pydict)
Example #3
0
    def __init__(self, data, header):
        self._original_header = MapHeader(header)
        
        # Set naxis1 and naxis2 if not specified
        if header.get('naxis1') is None:
            header['naxis1'] = self.shape[0]
        if header.get('naxis2') is None:
            header['naxis2'] = self.shape[1]

        # Parse header and set map attributes
        for attr, value in list(self.get_properties(header).items()):
            setattr(self, attr, value)

        # Addition properties based on data
        self.byte_scaled = self.dtype == np.uint8
        
        # Validate properties
        self._validate()
Example #4
0
def read(filepath):
    """Reads in the file at the specified location"""
    hdulist = pyfits.open(filepath)
    hdulist.verify('silentfix')
    
    fits_comment = hdulist[0].header.get_comment()
    
    # PyFITS 2.x
    if len(fits_comment) > 0 and isinstance(fits_comment[0], basestring):
        comments = [val for val in fits_comment]       
    else:
        # PyFITS 3.x
        comments = [card.value for card in fits_comment]
        
    comment = "".join(comments).strip()
    header = MapHeader(hdulist[0].header)
    header['comment'] = comment

    return hdulist[0].data, header
Example #5
0
class BaseMap(np.ndarray):
    """
    BaseMap(data, header)

    A spatially-aware data array based on the SolarSoft Map object

    Parameters
    ----------
    data : numpy.ndarray, list
        A 2d list or ndarray containing the map data
    header : dict
        A dictionary of the original image header tags

    Attributes
    ----------
    fits_header : dict
        Dictionary representation of the original FITS header
    carrington_longitude : str
        Carrington longitude (crln_obs)
    center : dict
        X and Y coordinate of the center of the map in units.
        Usually represents the offset between the center of the Sun and the
        center of the map.
    cmap : matplotlib.colors.Colormap
        A Matplotlib colormap to be applied to the data
    coordinate_system : dict
        Coordinate system used for x and y axes (ctype1/2)
    date : datetime
        Image observation time
    detector : str
        Detector name
    dsun : float
        The observer distance from the Sun.
    exptime : float
        Exposure time of the image in seconds.
    heliographic_latitude : float
        Heliographic latitude in degrees
    heliographic_longitude : float
        Heliographic longitude in degrees
    instrument : str
        Instrument name
    measurement : str, int
        Measurement name. In some instances this is the wavelength of image.
    name: str
        Human-readable description of map-type
    nickname: str
        An abbreviated human-readable description of the map-type; part of
        the Helioviewer data model
    observatory : str
        Observatory name
    reference_coordinate : float
        Reference point WCS axes in data units (crval1/2) 
    reference_pixel : float
        Reference point axes in pixels (crpix1/2)
    rsun_arcseconds : float
        Radius of the sun in arcseconds
    rsun_meters : float
        Radius of the sun in meters
    scale : dict
        Image scale along the x and y axes in units/pixel (cdelt1/2).
    units : dict
        Image coordinate units along the x and y axes (cunit1/2).

    Examples
    --------
    >>> aia = sunpy.Map(sunpy.AIA_171_IMAGE)
    >>> aia.T
    AIAMap([[ 0.3125,  1.    , -1.1875, ..., -0.625 ,  0.5625,  0.5   ],
    [-0.0625,  0.1875,  0.375 , ...,  0.0625,  0.0625, -0.125 ],
    [-0.125 , -0.8125, -0.5   , ..., -0.3125,  0.5625,  0.4375],
    ...,
    [ 0.625 ,  0.625 , -0.125 , ...,  0.125 , -0.0625,  0.6875],
    [-0.625 , -0.625 , -0.625 , ...,  0.125 , -0.0625,  0.6875],
    [ 0.    ,  0.    , -1.1875, ...,  0.125 ,  0.    ,  0.6875]])
    >>> aia.units['x']
    'arcsec'
    >>> aia.show()
    >>> import matplotlib.cm as cm
    >>> import matplotlib.colors as colors
    >>> aia.show(cmap=cm.hot, norm=colors.Normalize(1, 2048))

    See Also
    --------
    numpy.ndarray Parent class for the Map object

    References
    ----------
    | http://docs.scipy.org/doc/numpy/reference/arrays.classes.html
    | http://docs.scipy.org/doc/numpy/user/basics.subclassing.html
    | http://docs.scipy.org/doc/numpy/reference/ufuncs.html
    | http://www.scipy.org/Subclasses

    """
    def __new__(cls, data, header):
        """Creates a new BaseMap instance"""
        if isinstance(data, np.ndarray):
            obj = data.view(cls)
        elif isinstance(data, list):
            obj = np.asarray(data).view(cls)
        else:
            raise TypeError('Invalid input')

        return obj

    def __init__(self, data, header):
        self._original_header = MapHeader(header)
        
        # Set naxis1 and naxis2 if not specified
        if header.get('naxis1') is None:
            header['naxis1'] = self.shape[0]
        if header.get('naxis2') is None:
            header['naxis2'] = self.shape[1]

        # Parse header and set map attributes
        for attr, value in list(self.get_properties(header).items()):
            setattr(self, attr, value)

        # Addition properties based on data
        self.byte_scaled = self.dtype == np.uint8
        
        # Validate properties
        self._validate()
        
    @classmethod
    def get_properties(cls, header):
        """Parses a map header and determines default properties."""
        return {
            "cmap": cm.gray,  # @UndefinedVariable
            "date": parse_time(header.get('date-obs', None)),
            "detector": header.get('detector', ''),
            "dsun": header.get('dsun_obs', constants.au),
            "exposure_time": header.get('exptime', 0.),
            "instrument": header.get('instrume', ''),
            "measurement": header.get('wavelnth', ''),
            "observatory": header.get('telescop', ''),
            "name": header.get('telescop', '') + " " + 
                    str(header.get('wavelnth', '')),
            "nickname": header.get('detector', ''),
            "rsun_meters": header.get('rsun_ref', constants.radius),
            "rsun_arcseconds": header.get('rsun_obs', header.get('solar_r',
                               header.get('radius',
                               constants.average_angular_size))),
            "coordinate_system": {
                'x': header.get('ctype1', 'HPLN-TAN'),
                'y': header.get('ctype2', 'HPLT-TAN')
            },
            "carrington_longitude": header.get('crln_obs', 0.),
            "heliographic_latitude": header.get('hglt_obs', 
                                     header.get('crlt_obs',
                                     header.get('solar_b0', 0.))),
            "heliographic_longitude": header.get('hgln_obs', 0.),
            "reference_coordinate": {
                'x': header.get('crval1', 0.),
                'y': header.get('crval2', 0.),
            },
            "reference_pixel": {
                'x': header.get('crpix1', (header.get('naxis1') + 1) / 2.),
                'y': header.get('crpix2', (header.get('naxis2') + 1) / 2.)
            },
            "scale": {
                'x': header.get('cdelt1', 1.),
                'y': header.get('cdelt2', 1.),
            },
            "units": {
                'x': header.get('cunit1', 'arcsec'),
                'y': header.get('cunit2', 'arcsec')
            }
        }

    def __array_finalize__(self, obj):
        """Finishes instantiation of the new map object"""
        if obj is None:
            return

        if hasattr(obj, '_original_header'):
            properties = ['_original_header', 'cmap', 'date', 'detector', 'dsun',
                          'exposure_time', 'instrument', 'measurement', 'name',
                          'observatory', 'rsun_arcseconds', 'rsun_meters',
                          'scale', 'units', 'reference_coordinate',
                          'reference_pixel', 'coordinate_system',
                          'heliographic_latitude', 'heliographic_longitude',
                          'carrington_longitude']

            for attr in properties:
                setattr(self, attr, getattr(obj, attr))

    def __array_wrap__(self, out_arr, context=None):
        """Returns a wrapped instance of a Map object"""
        return np.ndarray.__array_wrap__(self, out_arr, context)

    def __getitem__(self, key):
        """Overiding indexing operation to ensure that header is updated"""
        if isinstance(key, tuple) and type(key[0]) is slice:
            x_range = [key[1].start, key[1].stop]
            y_range = [key[0].start, key[0].stop]

            return self.submap(y_range, x_range, units="pixels")
        else:
            return np.ndarray.__getitem__(self, key)

    def __add__(self, other):
        """Add two maps. Currently does not take into account the alignment
        between the two maps."""
        result = np.ndarray.__add__(self, other)

        return result

    def __repr__(self):
        if not hasattr(self, 'observatory'):
            return np.ndarray.__repr__(self)

        return (
"""SunPy Map
---------
Observatory:\t %s
Instrument:\t %s
Detector:\t %s
Measurement:\t %s
Obs Date:\t %s
dt:\t\t %f
Dimension:\t [%d, %d] 
[dx, dy] =\t [%f, %f]
 
""" % (self.observatory, self.instrument, self.detector, self.measurement,
       self.date.strftime("%Y-%m-%d %H:%M:%S"), self.exposure_time,
       self.shape[0], self.shape[1], self.scale['x'], self.scale['y']) 
     + np.ndarray.__repr__(self))

    def __sub__(self, other):
        """Subtract two maps. Currently does not take into account the
        alignment between the two maps.

        numpy dtype nums:
            1    int8
            2    uint8
            3    int16
            4    uint16
        """
        # if data is stored as unsigned, cast up (e.g. uint8 => int16)
        if self.dtype.kind == "u":
            dtype = "int%d" % (int(self.dtype.name[4:]) * 2)
            self = self.astype(np.dtype(dtype))
        if other.dtype.kind == "u":
            dtype = "int%d" % (int(other.dtype.name[4:]) * 2)
            other = other.astype(np.dtype(dtype))

        result = np.ndarray.__sub__(self, other)

        def norm():
            mean = result.mean()
            std = result.std()
            vmin = max(result.min(), mean - 6 * std)
            vmax = min(result.max(), mean + 6 * std)

            return colors.Normalize(vmin, vmax)

        result.norm = norm
        result.cmap = cm.gray  # @UndefinedVariable

        return result

    @property
    def xrange(self):
        """Return the X range of the image in arcsec from edge to edge."""
        xmin = self.center['x'] - self.shape[1] / 2 * self.scale['x']
        xmax = self.center['x'] + self.shape[1] / 2 * self.scale['x']
        return [xmin, xmax]

    @property
    def yrange(self):
        """Return the Y range of the image in arcsec from edge to edge."""
        ymin = self.center['y'] - self.shape[0] / 2 * self.scale['y']
        ymax = self.center['y'] + self.shape[0] / 2 * self.scale['y']
        return [ymin, ymax]
    
    @property
    def center(self):
        """Returns the offset between the center of the Sun and the center of 
        the map."""
        return {
            'x': wcs.get_center(self.shape[0], self.scale['x'], 
                                self.reference_pixel['x'], 
                                self.reference_coordinate['x']),
            'y': wcs.get_center(self.shape[0], self.scale['y'], 
                                self.reference_pixel['y'], 
                                self.reference_coordinate['y'])
        }

    def _draw_limb(self, fig, axes):
        """Draws a circle representing the solar limb"""
        circ = patches.Circle([0, 0], radius=self.rsun_arcseconds, fill=False,
                              color='white')
        axes.add_artist(circ)
        return fig, axes

    def _draw_grid(self, fig, axes, grid_spacing=20):
        """Draws a grid over the surface of the Sun"""
        # define the number of points for each latitude or longitude line
        num_points = 20
        hg_longitude_deg = np.linspace(-90, 90, num=num_points)
        hg_latitude_deg = np.arange(-90, 90, grid_spacing)

        # draw the latitude lines
        for lat in hg_latitude_deg:
            hg_latitude_deg_mesh, hg_longitude_deg_mesh = np.meshgrid(
                lat * np.ones(num_points), hg_longitude_deg)
            x, y = wcs.convert_hg_hpc(self.rsun_meters,
                                      self.dsun, self.heliographic_latitude,
                                      self.heliographic_longitude,
                                      hg_longitude_deg_mesh,
                                      hg_latitude_deg_mesh, units='arcsec')
            axes.plot(x, y, color='white', linestyle='dotted')

        hg_longitude_deg = np.arange(-90, 90, grid_spacing)
        hg_latitude_deg = np.linspace(-90, 90, num=num_points)

        # draw the longitude lines
        for lon in hg_longitude_deg:
            hg_longitude_deg_mesh, hg_latitude_deg_mesh = np.meshgrid(
                lon * np.ones(num_points), hg_latitude_deg)
            x, y = wcs.convert_hg_hpc(self.rsun_meters,
                                      self.dsun, self.heliographic_latitude,
                                      self.heliographic_longitude,
                                      hg_longitude_deg_mesh,
                                      hg_latitude_deg_mesh, units='arcsec')
            axes.plot(x, y, color='white', linestyle='dotted')

        return fig, axes

    def _validate(self):
        """Validates the meta-information associated with a Map.

        This function includes very basic validation checks which apply to
        all of the kinds of files that SunPy can read. Datasource-specific
        validation should be handled in the relevant file in the
        sunpy.map.sources package."""
        if (self.dsun <= 0 or self.dsun >= 40 * constants.au):
            raise InvalidHeaderInformation("Invalid value for DSUN")

    def std(self, *args, **kwargs):
        """overide np.ndarray.std()"""
        return np.array(self, copy=False, subok=False).std(*args, **kwargs)
    
    def mean(self, *args, **kwargs):
        """overide np.ndarray.mean()"""
        return np.array(self, copy=False, subok=False).mean(*args, **kwargs)
    
    def min(self, *args, **kwargs):
            """overide np.ndarray.min()"""
            return np.array(self, copy=False, subok=False).min(*args, **kwargs)
        
    def max(self, *args, **kwargs):
            """overide np.ndarray.max()"""
            return np.array(self, copy=False, subok=False).max(*args, **kwargs)

    def data_to_pixel(self, value, dim):
        """Convert pixel-center data coordinates to pixel values"""
        if dim not in ['x', 'y']:
            raise ValueError("Invalid dimension. Must be one of 'x' or 'y'.")

        size = self.shape[dim == 'x']  # 1 if dim == 'x', 0 if dim == 'y'.

        return (value - self.center[dim]) / self.scale[dim] + ((size - 1) / 2.)
    
    def get_header(self):
        """Returns an updated MapHeader instance"""
        header = self._original_header.copy()
        
        # Bit-depth
        #
        #   8    Character or unsigned binary integer
        #  16    16-bit twos-complement binary integer
        #  32    32-bit twos-complement binary integer
        # -32    IEEE single precision floating point
        # -64    IEEE double precision floating point
        #
        if not header.has_key('bitpix'):
            bitdepth = 8 * self.dtype.itemsize
            
            if self.dtype.kind == "f":
                bitdepth = - bitdepth
                
            header['bitpix'] = bitdepth

        # naxis
        header['naxis'] = self.ndim
        header['naxis1'] = self.shape[0]
        header['naxis2'] = self.shape[1]
        
        # dsun
        if header.has_key('dsun_obs'):
            header['dsun_obs'] = self.dsun

        # rsun_obs
        if header.has_key('rsun_obs'):
            header['rsun_obs'] = self.rsun_arcseconds
        elif header.has_key('solar_r'):
            header['solar_r'] = self.rsun_arcseconds
        elif header.has_key('radius'):
            header['radius'] = self.rsun_arcseconds
            
        # cdelt
        header['cdelt1'] = self.scale['x']
        header['cdelt2'] = self.scale['y']

        # crpix
        header['crval1'] = self.reference_coordinate['x']
        header['crval2'] = self.reference_coordinate['y']
        
        # crval
        header['crpix1'] = self.reference_pixel['x']
        header['crpix2'] = self.reference_pixel['y']
        
        return header               

    def resample(self, dimensions, method='linear'):
        """Returns a new Map that has been resampled up or down

        Arbitrary resampling of the Map to new dimension sizes.

        Uses the same parameters and creates the same co-ordinate lookup points
        as IDL''s congrid routine, which apparently originally came from a
        VAX/VMS routine of the same name.

        Parameters
        ----------
        dimensions : tuple
            Dimensions that new Map should have.
            Note: the first argument corresponds to the 'x' axis and the second
            argument corresponds to the 'y' axis.
        method : {'neighbor' | 'nearest' | 'linear' | 'spline'}
            Method to use for resampling interpolation.
                * neighbor - Closest value from original data
                * nearest and linear - Uses n x 1-D interpolations using
                  scipy.interpolate.interp1d
                * spline - Uses ndimage.map_coordinates

        Returns
        -------
        out : Map
            A new Map which has been resampled to the desired dimensions.

        References
        ----------
        | http://www.scipy.org/Cookbook/Rebinning (Original source, 2011/11/19)
        """
        from sunpy.image import resample

        # Note: because the underlying ndarray is transposed in sense when
        #   compared to the Map, the ndarray is transposed, resampled, then
        #   transposed back
        # Note: "center" defaults to True in this function because data
        #   coordinates in a Map are at pixel centers

        # Make a copy of the original data and perform resample
        data = resample(np.asarray(self).copy().T, dimensions,
                        method, center=True)

        # Update image scale and number of pixels
        header = self._original_header.copy()

        # Note that 'x' and 'y' correspond to 1 and 0 in self.shape,
        # respectively
        scale_factor_x = (float(self.shape[1]) / dimensions[0])
        scale_factor_y = (float(self.shape[0]) / dimensions[1])

        # Create new map instance
        new_map = self.__class__(data.T, header)

        # Update metadata
        new_map.scale['x'] *= scale_factor_x
        new_map.scale['y'] *= scale_factor_y
        new_map.reference_pixel['x'] = (dimensions[0] + 1) / 2.
        new_map.reference_pixel['y'] = (dimensions[1] + 1) / 2.
        new_map.reference_coordinate['x'] = self.center['x']
        new_map.reference_coordinate['y'] = self.center['x']

        return new_map
    
    def save(self, filepath):
        """Saves the SunPy Map object to a file.
        
        Currently SunPy can only save files in the FITS format. In the future
        support will be added for saving to other formats.
        
        Parameters
        ----------
        filepath : string
            Location to save file to.
        """
        pyfits_header = self.get_header().as_pyfits_header()
        hdu = pyfits.PrimaryHDU(self, header=pyfits_header)
        hdulist = pyfits.HDUList([hdu])
        hdulist.writeto(os.path.expanduser(filepath))        

    def submap(self, range_a, range_b, units="data"):
        """Returns a submap of the map with the specified range

        Parameters
        ----------
        range_a : list
            The range of the Map to select across either the x axis.
        range_b : list
            The range of the Map to select across either the y axis.
        units : {'data' | 'pixels'}, optional
            The units for the supplied ranges.

        Returns
        -------
        out : Map
            A new map instance is returned representing to specified sub-region

        Examples
        --------
        >>> aia.submap([-5,5],[-5,5])
        AIAMap([[ 341.3125,  266.5   ,  329.375 ,  330.5625,  298.875 ],
        [ 347.1875,  273.4375,  247.4375,  303.5   ,  305.3125],
        [ 322.8125,  302.3125,  298.125 ,  299.    ,  261.5   ],
        [ 334.875 ,  289.75  ,  269.25  ,  256.375 ,  242.3125],
        [ 273.125 ,  241.75  ,  248.8125,  263.0625,  249.0625]])

        >>> aia.submap([0,5],[0,5], units='pixels')
        AIAMap([[ 0.3125, -0.0625, -0.125 ,  0.    , -0.375 ],
        [ 1.    ,  0.1875, -0.8125,  0.125 ,  0.3125],
        [-1.1875,  0.375 , -0.5   ,  0.25  , -0.4375],
        [-0.6875, -0.3125,  0.8125,  0.0625,  0.1875],
        [-0.875 ,  0.25  ,  0.1875,  0.    , -0.6875]])
        """
        if units is "data":
            # Check edges (e.g. [:512,..] or [:,...])
            if range_a[0] is None:
                range_a[0] = self.xrange[0]
            if range_a[1] is None:
                range_a[1] = self.xrange[1]
            if range_b[0] is None:
                range_b[0] = self.yrange[0]
            if range_b[1] is None:
                range_b[1] = self.yrange[1]

            #x_pixels = [self.data_to_pixel(elem, 'x') for elem in range_a]
            x_pixels = [np.ceil(self.data_to_pixel(range_a[0], 'x')),
                        np.floor(self.data_to_pixel(range_a[1], 'x')) + 1]
            #y_pixels = [self.data_to_pixel(elem, 'y') for elem in range_b]
            y_pixels = [np.ceil(self.data_to_pixel(range_b[0], 'y')),
                        np.floor(self.data_to_pixel(range_b[1], 'y')) + 1]
        elif units is "pixels":
            # Check edges
            if range_a[0] is None:
                range_a[0] = 0
            if range_a[1] is None:
                range_a[1] = self.shape[0]
            if range_b[0] is None:
                range_b[0] = 0
            if range_b[1] is None:
                range_b[1] = self.shape[0]
                
            x_pixels = range_a
            y_pixels = range_b
        else:
            raise ValueError(
                "Invalid unit. Must be one of 'data' or 'pixels'")

        # Make a copy of the header with updated centering information
        header = self._original_header.copy()
        
        # Get ndarray representation of submap
        data = np.asarray(self)[y_pixels[0]:y_pixels[1],
                                x_pixels[0]:x_pixels[1]]
        
        # Instantiate new instance and update metadata
        new_map = self.__class__(data.copy(), header)
        new_map.reference_pixel['x'] = self.reference_pixel['x'] - x_pixels[0]
        new_map.reference_pixel['y'] = self.reference_pixel['y'] - y_pixels[0]

        return new_map

    @toggle_pylab
    def plot(self, figure=None, overlays=None, draw_limb=True, gamma=None,
             draw_grid=False, colorbar=True, basic_plot=False, **matplot_args):
        """Plots the map object using matplotlib

        Parameters
        ----------
        overlays : list
            List of overlays to include in the plot
        draw_limb : bool
            Whether the solar limb should be plotted.
        draw_grid : bool
            Whether solar meridians and parallels
        grid_spacing : float
            Set the spacing between meridians and parallels for the grid
        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.
        """
        if overlays is None:
            overlays = []
        if draw_limb:
            overlays = overlays + [self._draw_limb]
        # TODO: need to be able to pass the grid spacing to _draw_grid from the
        # plot command.
        if draw_grid:
            overlays = overlays + [self._draw_grid]

        # Create a figure and add title and axes
        if figure is None:
            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)
            
        # Normal plot
        else:
            axes = figure.add_subplot(111)
            axes.set_title("%s %s" % (self.name, self.date))
            
            # x-axis label
            if self.coordinate_system['x'] == 'HG':
                xlabel = 'Longitude [%s]' % self.units['x']
            else:
                xlabel = 'X-position [%s]' % self.units['x']

            # y-axis label
            if self.coordinate_system['y'] == 'HG':
                ylabel = 'Latitude [%s]' % self.units['y']
            else:
                ylabel = 'Y-position [%s]' % self.units['y']
                
            axes.set_xlabel(xlabel)
            axes.set_ylabel(ylabel)

        # Determine extent
        extent = self.xrange + self.yrange

        # Matplotlib arguments
        params = {
            "cmap": self.cmap,
            "norm": self.norm()
        }
        params.update(matplot_args)

        if gamma is not None:
            params['cmap'] = copy(params['cmap'])
            params['cmap'].set_gamma(gamma)

        im = axes.imshow(self, origin='lower', extent=extent, **params)
        
        if colorbar and not basic_plot:
            figure.colorbar(im)

        for overlay in overlays:
            figure, axes = overlay(figure, axes)
        return figure

    def show(self, figure=None, overlays=None, draw_limb=False, gamma=1.0,
             draw_grid=False, colorbar=True, basic_plot=False, **matplot_args):
        """Displays map on screen. Arguments are same as plot()."""
        self.plot(figure, overlays, draw_limb, gamma, draw_grid, colorbar, 
                  basic_plot, **matplot_args).show()
    
    def norm(self):
        """Default normalization method"""
        return None

    @classmethod
    def parse_file(cls, filepath):
        """Reads in a map file and returns a header and data array"""
        data, dict_header = read_file(filepath)

        header = MapHeader(dict_header)

        return header, data

    @classmethod
    def read(cls, filepath):
        """Map class factory

        Attempts to determine the type of data associated with input and
        returns an instance of either the generic BaseMap class or a subclass
        of BaseMap such as AIAMap, EUVIMap, etc.

        Parameters
        ----------
        filepath : string
            Path to a valid FITS or JPEG 2000 file of a type supported by SunPy

        Returns
        -------
        out : Map
            Returns a Map instance for the particular type of data loaded.
        """
        header, data = cls.parse_file(filepath)

        if cls.__name__ is not "BaseMap":
            return cls(data, header)

        for cls in BaseMap.__subclasses__():
            if cls.is_datasource_for(header):
                return cls(data, header)
        
        return BaseMap(data, header)

    @classmethod
    def read_header(cls, filepath):
        """Attempts to detect the datasource type and returns meta-information
        for that particular datasource."""
        dict_header = read_file_header(filepath)

        header = MapHeader(dict_header)

        for cls in BaseMap.__subclasses__():
            if cls.is_datasource_for(header):
                properties = cls.get_properties(header)
                properties['header'] = header
                
                return properties