def test_write_with_metadict_header_astropy(tmpdir): fits_file = fits.open(AIA_171_IMAGE) data, header = fits_file[0].data, fits_file[0].header meta_header = MetaDict(OrderedDict(header)) temp_file = tmpdir / "temp.fits" sunpy.io.fits.write(str(temp_file), data, meta_header) assert temp_file.exists()
def test_write_with_metadict_header_astropy(tmpdir): with fits.open(AIA_171_IMAGE) as fits_file: data, header = fits_file[0].data, fits_file[0].header meta_header = MetaDict(OrderedDict(header)) temp_file = tmpdir / "temp.fits" with pytest.warns(SunpyUserWarning, match='The meta key comment is not valid ascii'): sunpy.io.fits.write(str(temp_file), data, meta_header) assert temp_file.exists()
def test_fitsheader(): """Test that all test data can be converted back to a FITS header.""" extensions = ('fts', 'fits') for ext in extensions: for ffile in Path(testpath).glob(f"*.{ext}*"): fits_file = fits.open(ffile) fits_file.verify("fix") meta_header = MetaDict(OrderedDict(fits_file[0].header)) sunpy.io.fits.header_to_fits(meta_header) fits_file.close()
def test_fitsheader(): """Test that all test data can be converted back to a FITS header.""" extensions = ('.fts', '.fits') for ext in extensions: test_files = [f for f in test_data_filenames() if f.suffix == ext] for ffile in test_files: fits_file = fits.open(ffile) fits_file.verify("fix") meta_header = MetaDict(OrderedDict(fits_file[0].header)) _fits.header_to_fits(meta_header) fits_file.close()
def make_fitswcs_header(data, coordinate, reference_pixel: u.pix = None, scale: u.arcsec / u.pix = None, rotation_angle: u.deg = None, rotation_matrix=None, instrument=None, telescope=None, observatory=None, wavelength: u.angstrom = None, exposure: u.s = None, projection_code="TAN"): """ Function to create a FITS-WCS header from a coordinate object (`~astropy.coordinates.SkyCoord`) that is required to create a `~sunpy.map.GenericMap`. Parameters ---------- data : `~numpy.ndarray` or `tuple` Array data of Map for which a header is required, or the shape of the data array (in numpy order, i.e. ``(y_size, x_size)``). coordinates : `~astropy.coordinates.SkyCoord` or `~astropy.coordinates.BaseFrame` Coordinate object to get meta information for map header. reference_pixel :`~astropy.units.Quantity` of size 2, optional Reference pixel along each axis. These are expected to be Cartestian ordered, i.e the first index is the x axis, second index is the y axis. Defaults to the center of data array, ``(data.shape[1] - 1)/2., (data.shape[0] - 1)/2.)``, this argument is zero indexed (Python convention) not 1 indexed (FITS convention). scale : `~astropy.units.Quantity` of size 2, optional Pixel scaling along x and y axis (i.e. the spatial scale of the pixels (dx, dy)). These are expected to be Cartestian ordered, i.e [dx, dy]. Defaults to ``([1., 1.] arcsec/pixel)``. rotation_angle : `~astropy.unit.Quantity`, optional Coordinate system rotation angle, will be converted to a rotation matrix and stored in the ``PCi_j`` matrix. Can not be specified with ``rotation_matrix``. rotation_matrix : `~numpy.ndarray` of dimensions 2x2, optional Matrix describing the rotation required to align solar North with the top of the image in FITS ``PCi_j`` convention. Can not be specified with ``rotation_angle``. instrument : `~str`, optional Name of the instrument of the observation. telescope : `~str`, optional Name of the telescope of the observation. observatory : `~str`, optional Name of the observatory of the observation. wavelength : `~u.Quantity`, optional Wavelength of the observation as an astropy quanitity, e.g. 171*u.angstrom. From this keyword, the meta keywords ``wavelnth`` and ``waveunit`` will be populated. exposure : `~u.Quantity`, optional Exposure time of the observation projection_code : `str`, optional The FITS standard projection code for the new header. Returns ------- `~sunpy.util.MetaDict` The header information required for making a `sunpy.map.GenericMap`. Examples -------- >>> import sunpy.map >>> from sunpy.coordinates import frames >>> from astropy.coordinates import SkyCoord >>> import astropy.units as u >>> import numpy as np >>> data = np.random.rand(1024, 1024) >>> my_coord = SkyCoord(0*u.arcsec, 0*u.arcsec, obstime="2017-08-01", ... observer = 'earth', frame=frames.Helioprojective) >>> my_header = sunpy.map.make_fitswcs_header(data, my_coord) >>> my_map = sunpy.map.Map(data, my_header) """ if not isinstance(coordinate, (SkyCoord, frames.BaseCoordinateFrame)): raise ValueError( "coordinate needs to be a coordinate frame or an SkyCoord instance." ) if isinstance(coordinate, SkyCoord): coordinate = coordinate.frame if coordinate.obstime is None: raise ValueError( "The coordinate needs an observation time, `obstime`.") if isinstance(coordinate, frames.Heliocentric): raise ValueError( "This function does not currently support heliocentric coordinates." ) if hasattr(data, "shape"): shape = data.shape else: shape = data meta_wcs = _get_wcs_meta(coordinate, projection_code) if hasattr(coordinate, "observer") and isinstance( coordinate.observer, frames.BaseCoordinateFrame): meta_observer = get_observer_meta(coordinate.observer, getattr(coordinate, 'rsun', None)) meta_wcs.update(meta_observer) meta_instrument = _get_instrument_meta(instrument, telescope, observatory, wavelength, exposure) meta_wcs.update(meta_instrument) if reference_pixel is None: reference_pixel = u.Quantity([(shape[1] + 1) / 2. * u.pixel, (shape[0] + 1) / 2. * u.pixel]) if scale is None: scale = [1., 1.] * (u.arcsec / u.pixel) meta_wcs['crval1'], meta_wcs['crval2'] = ( coordinate.spherical.lon.to_value(meta_wcs['cunit1']), coordinate.spherical.lat.to_value(meta_wcs['cunit2'])) meta_wcs['crpix1'], meta_wcs['crpix2'] = (reference_pixel[0].to_value( u.pixel), reference_pixel[1].to_value(u.pixel)) meta_wcs['cdelt1'], meta_wcs['cdelt2'] = (scale[0].to_value( meta_wcs['cunit1'] / u.pixel), scale[1].to_value(meta_wcs['cunit2'] / u.pixel)) if rotation_angle is not None and rotation_matrix is not None: raise ValueError( "Can not specify both rotation angle and rotation matrix.") if rotation_angle is not None: lam = meta_wcs['cdelt1'] / meta_wcs['cdelt2'] p = np.deg2rad(rotation_angle) rotation_matrix = np.array([[np.cos(p), -1 * lam * np.sin(p)], [1 / lam * np.sin(p), np.cos(p)]]) if rotation_matrix is not None: (meta_wcs['PC1_1'], meta_wcs['PC1_2'], meta_wcs['PC2_1'], meta_wcs['PC2_2']) = (rotation_matrix[0, 0], rotation_matrix[0, 1], rotation_matrix[1, 0], rotation_matrix[1, 1]) meta_dict = MetaDict(meta_wcs) return meta_dict
def build_meta(wcs, exif_data): time = get_image_time(exif_data) wcs.wcs.dateobs = time.isoformat() header = MetaDict(dict(wcs.to_header())) header.update(get_meta_from_exif(exif_data)) dsun = sunpy.coordinates.sun.earth_distance(time.isoformat()) lat = header.get('LAT') * u.deg lon = header.get('LON') * u.deg solar_rotation_angle = get_solar_rotation_angle(lat, lon, time) header.update({'crota2': solar_rotation_angle.to('deg').value}) header.update({'dsun_obs': dsun.to('m').value}) hgln_obs = 0 * u.deg hglt_obs = sunpy.coordinates.sun.B0(time) header.update({'hgln_obs': hgln_obs.to('deg').value}) header.update({'hglt_obs': hglt_obs.to('deg').value}) header.update({'ctype1': 'HPLN-TAN'}) header.update({'ctype2': 'HPLT-TAN'}) header.update({'rsun': dsun.to('m').value}) header.update({'rsun_obs': np.arctan(sunpy.sun.constants.radius / dsun).to( 'arcsec').value}) return header
def make_fitswcs_header(data, coordinate, reference_pixel: u.pix = None, scale: u.arcsec / u.pix = None, rotation_angle: u.deg = None, rotation_matrix=None, instrument=None, telescope=None, observatory=None, wavelength: u.angstrom = None, exposure: u.s = None, projection_code="TAN"): """ Function to create a FITS-WCS header from a coordinate object (`~astropy.coordinates.SkyCoord`) that is required to create a `~sunpy.map.GenericMap`. Parameters ---------- data : `~numpy.ndarray` or `tuple` Array data of Map for which a header is required, or the shape of the data array (in numpy order, i.e. ``(y_size, x_size)``). coordinate : `~astropy.coordinates.SkyCoord` or `~astropy.coordinates.BaseCoordinateFrame` The coordinate of the reference pixel. reference_pixel :`~astropy.units.Quantity` of size 2, optional Reference pixel along each axis. These are expected to be Cartestian ordered, i.e the first index is the x axis, second index is the y axis. Defaults to the center of data array, ``(data.shape[1] - 1)/2., (data.shape[0] - 1)/2.)``, this argument is zero indexed (Python convention) not 1 indexed (FITS convention). scale : `~astropy.units.Quantity` of size 2, optional Pixel scaling along x and y axis (i.e. the spatial scale of the pixels (dx, dy)). These are expected to be Cartestian ordered, i.e [dx, dy]. Defaults to ``([1., 1.] arcsec/pixel)``. rotation_angle : `~astropy.units.Quantity`, optional Coordinate system rotation angle, will be converted to a rotation matrix and stored in the ``PCi_j`` matrix. Can not be specified with ``rotation_matrix``. rotation_matrix : `~numpy.ndarray` of dimensions 2x2, optional Matrix describing the rotation required to align solar North with the top of the image in FITS ``PCi_j`` convention. Can not be specified with ``rotation_angle``. instrument : `~str`, optional Name of the instrument of the observation. telescope : `~str`, optional Name of the telescope of the observation. observatory : `~str`, optional Name of the observatory of the observation. wavelength : `~astropy.units.Quantity`, optional Wavelength of the observation as an astropy quanitity, e.g. 171*u.angstrom. From this keyword, the meta keywords ``wavelnth`` and ``waveunit`` will be populated. exposure : `~astropy.units.Quantity`, optional Exposure time of the observation projection_code : `str`, optional The FITS standard projection code for the new header. Returns ------- `~sunpy.util.MetaDict` The header information required for making a `sunpy.map.GenericMap`. Notes ----- The observer coordinate is taken from the observer property of the ``reference_pixel`` argument. Examples -------- >>> import sunpy.map >>> from sunpy.coordinates import frames >>> from astropy.coordinates import SkyCoord >>> import astropy.units as u >>> import numpy as np >>> data = np.random.rand(1024, 1024) >>> my_coord = SkyCoord(0*u.arcsec, 0*u.arcsec, obstime="2017-08-01", ... observer = 'earth', frame=frames.Helioprojective) >>> my_header = sunpy.map.make_fitswcs_header(data, my_coord) >>> my_map = sunpy.map.Map(data, my_header) """ coordinate = _validate_coordinate(coordinate) if hasattr(data, "shape"): shape = data.shape else: shape = data meta_wcs = _get_wcs_meta(coordinate, projection_code) meta_wcs = _set_instrument_meta(meta_wcs, instrument, telescope, observatory, wavelength, exposure) meta_wcs = _set_transform_params(meta_wcs, coordinate, reference_pixel, scale, shape) meta_wcs = _set_rotation_params(meta_wcs, rotation_angle, rotation_matrix) if getattr(coordinate, 'observer', None) is not None: # Have to check for str, as doing == on a SkyCoord and str raises an error if isinstance(coordinate.observer, str) and coordinate.observer == 'self': dsun_obs = coordinate.radius else: dsun_obs = coordinate.observer.radius meta_wcs['rsun_obs'] = sun._angular_radius(coordinate.rsun, dsun_obs).to_value(u.arcsec) meta_dict = MetaDict(meta_wcs) return meta_dict
def make_fitswcs_header(data, coordinate, reference_pixel: u.pix = None, scale: u.arcsec / u.pix = None, **kwargs): """ Function to create a FITS-WCS header from a coordinate object (`~astropy.coordinates.SkyCoord`) that is required to create a `~sunpy.map.GenericMap`. Parameters ---------- data : `~numpy.ndarray` Array data of Map for which a header is required. coordinates : `~astropy.coordinates.SkyCoord` or `~astropy.coordinates.BaseFrame` Coordinate object to get meta information for map header. reference_pixel :`~astropy.units.Quantity` of size 2, optional Reference pixel along each axis. These are expected to be Cartestian ordered, i.e the first index is the x axis, second index is the y axis. Defaults to the center of data array, ``(data.shape[1] + 1)/2., (data.shape[0] + 1)/2.)``. scale : `~astropy.units.Quantity` of size 2, optional Pixel scaling along x and y axis (i.e. the spatial scale of the pixels (dx, dy)). These are expected to be Cartestian ordered, i.e [dx, dy]. Defaults to ``([1., 1.] arcsec/pixel)``. **kwargs: Additional arguments that will be put into the metadict header if they are in the list returned by `~sunpy.map.meta_keywords`. Additional keyword arguments for the instrument meta can also be given in the following forms which will be translated to fits standard: | ``instrument`` | ``telescope`` | ``observatory`` | ``wavelength`` | ``exposure`` Returns ------- `~sunpy.util.MetaDict` The header information required for making a `sunpy.map.GenericMap`. Examples -------- >>> import sunpy.map >>> from sunpy.coordinates import frames >>> from astropy.coordinates import SkyCoord >>> from astropy import units as u >>> import numpy as np >>> data = np.random.rand(1024, 1024) >>> my_coord = SkyCoord(0*u.arcsec, 0*u.arcsec, obstime="2017-08-01", ... observer = 'earth', frame=frames.Helioprojective) >>> my_header = sunpy.map.make_fitswcs_header(data, my_coord) >>> my_map = sunpy.map.Map(data, my_header) """ if not isinstance(coordinate, (SkyCoord, frames.BaseCoordinateFrame)): raise ValueError( "coordinate needs to be a coordinate frame or an SkyCoord instance." ) if isinstance(coordinate, SkyCoord): coordinate = coordinate.frame if coordinate.obstime is None: raise ValueError( "The coordinate needs an observation time, `obstime`.") if isinstance(coordinate, frames.Heliocentric): raise ValueError( "This function does not currently support heliocentric coordinates." ) meta_wcs = _get_wcs_meta(coordinate) if hasattr(coordinate, "observer") and isinstance( coordinate.observer, frames.BaseCoordinateFrame): meta_observer = _get_observer_meta(coordinate) meta_wcs.update(meta_observer) meta_instrument = _get_instrument_meta(**kwargs) meta_wcs.update(meta_instrument) if reference_pixel is None: reference_pixel = u.Quantity([(data.shape[1] + 1) / 2. * u.pixel, (data.shape[0] + 1) / 2. * u.pixel]) if scale is None: scale = u.Quantity([1.0 * u.arcsec, 1.0 * u.arcsec]) meta_wcs['crval1'], meta_wcs['crval2'] = ( coordinate.spherical.lat.to_value(meta_wcs['cunit1']), coordinate.spherical.lon.to_value(meta_wcs['cunit2'])) meta_wcs['crpix1'], meta_wcs['crpix2'] = (reference_pixel[0].to_value( u.pixel), reference_pixel[1].to_value(u.pixel)) meta_wcs['cdelt1'], meta_wcs['cdelt2'] = (scale[0] * meta_wcs['cunit1'] / u.pixel, scale[1] * meta_wcs['cunit2'] / u.pixel) meta_dict = MetaDict(meta_wcs) for key in kwargs: if key in _map_meta_keywords: meta_dict[key] = kwargs[key] return meta_dict