Skip to content

Commit

Permalink
Merge pull request #7262 from nabobalis/doc_map
Browse files Browse the repository at this point in the history
Fix map metadata how to
  • Loading branch information
nabobalis committed Jan 25, 2024
2 parents 9b754af + 9c4c953 commit 98e6a23
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 104 deletions.
1 change: 1 addition & 0 deletions changelog/7262.doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Created a how to guide on fixing metadata that is either missing or incorrect before passing the header into the `~sunpy.map.Map` class.
27 changes: 27 additions & 0 deletions docs/how_to/fix_map_metadata.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
.. _sunpy-how-to-fix-map-metadata:

*************************
Fixing incorrect metadata
*************************

There will be times where you will come across a FITS files with either incorrect, missing or unparsable metadata and reading these files into `~sunpy.map.Map` will cause an error.
Therefore, to load these files into a `~sunpy.map.Map`, you will need to correct the metadata beforehand.

In the example below, the units in the FITS header, as controlled by the ``CUNIT1`` and ``CUNIT2`` keywords, are incorrect.
Before loading the file into a `~sunpy.map.Map`, we will correct these keywords to have the correct units.

.. code-block:: python
>>> from astropy.io import fits
>>> from sunpy.map import Map
>>> import sunpy.data.sample
>>> filepath = sunpy.data.sample.AIA_171_IMAGE # doctest: +REMOTE_DATA
>>> data, header = fits.getdata(filepath, header=True) # doctest: +REMOTE_DATA
>>> # Note that it is case insensitive for the keys
>>> header['cunit1'] = 'arcsec' # doctest: +REMOTE_DATA
>>> header['cunit2'] = 'arcsec' # doctest: +REMOTE_DATA
>>> updated_map = Map(data, header) # doctest: +REMOTE_DATA
This applies for any keyword in the `FITS standard <https://fits.gsfc.nasa.gov/fits_standard.html>`__.
1 change: 1 addition & 0 deletions docs/how_to/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ If you're starting fresh you might want to check out the :ref:`sunpy-tutorial-in
create_coords
create_custom_map
create_custom_timeseries
fix_map_metadata
parse_time
remote_data_manager
search_multiple_wavelengths
Expand Down
112 changes: 9 additions & 103 deletions docs/reference/map.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,66 +5,22 @@ Maps (`sunpy.map`)

.. module:: sunpy.map

.. testsetup::

import sunpy

.. currentmodule:: sunpy.map

Overview
========
One of core classes in sunpy is a Map. A sunpy Map object is simply a
spatially-aware data array, often an image. In order to make it easy to work
with image data in sunpy, the Map object provides a number of methods for
commonly performed operations.

2D map objects are subclasses of `~sunpy.map.GenericMap` and these objects are
created using the Map factory `~sunpy.map.Map`.

A number of instrument are supported by subclassing this base object. See
:ref:`map-sources` to see a list of all of them. More complex subclasses are also
available. See :ref:`map-classes`.

.. todo :
1. Map factory and registration
2. MapBase and Generic Map
3. MapMeta and the separation from the file io
Creating Map Objects
====================
sunpy Map objects are constructed using the special factory
class `~sunpy.map.Map`: ::

>>> x = sunpy.map.Map('file.fits') # doctest: +SKIP

The result of a call to `~sunpy.map.Map` will be either a `~sunpy.map.mapbase.GenericMap` object,
or a subclass of `~sunpy.map.mapbase.GenericMap` which either deals with a specific type of data,
e.g. `~sunpy.map.sources.sdo.AIAMap` or `~sunpy.map.sources.soho.LASCOMap`
(see :ref:`map-classes` to see a list of all of them), or if no
instrument matches, a 2D map `~sunpy.map.mapbase.GenericMap`.

Fixing map metadata
-------------------

If you need to fix the metadata of a fits file before it is handed to `Map`, this can be done as
follows:

>>> data, header = astropy.io.fits.read(filepath)[0] # doctest: +SKIP
>>> header['cunit1'] = 'arcsec' # doctest: +SKIP
>>> header['cunit2'] = 'arcsec' # doctest: +SKIP
>>> map = sunpy.map.Map(data, header) # doctest: +SKIP

sunpy Map objects are constructed using the special factory class:

.. autoclass:: sunpy.map.map_factory.MapFactory

.. _map-classes:

.. currentmodule:: sunpy.map

sunpy.map Package
=================

Map objects are constructed using the special factory class: `~sunpy.map.Map`.
All sunpy Maps are derived from `sunpy.map.GenericMap`, all the methods and attributes are documented in that class.

The result of a call to `~sunpy.map.Map` will be either a `~sunpy.map.mapbase.GenericMap` object if no instrument matches, or a subclass of `~sunpy.map.mapbase.GenericMap` which deals with a specific source of data, e.g., `~sunpy.map.sources.sdo.AIAMap` or `~sunpy.map.sources.soho.LASCOMap` (see :ref:`map-classes` to see a list of all of them).

.. automodapi:: sunpy.map
:no-main-docstr:
:inherited-members:
Expand All @@ -87,60 +43,10 @@ The header_helper sub-module contains helper functions for generating FITS-WCS h

Instrument Map Classes
======================
Defined in ``sunpy.map.sources`` are a set of `~sunpy.map.GenericMap` subclasses
which convert the specific metadata and other differences in each instruments
data to the standard `~sunpy.map.GenericMap` interface.
These 'sources' also define things like the colormap and default
normalisation for each instrument.
These subclasses also provide a method, which describes to the `~sunpy.map.Map` factory
which data and metadata pairs match its instrument.
Defined in ``sunpy.map.sources`` are a set of `~sunpy.map.GenericMap` subclasses which convert the specific metadata and other differences in each instruments data to the standard `~sunpy.map.GenericMap` interface.
These 'sources' also define things like the colormap and default normalization for each instrument.
These subclasses also provide a method, which describes to the `~sunpy.map.Map` factory which data and metadata pairs match its instrument.

.. automodapi:: sunpy.map.sources
:no-main-docstr:
:inherited-members:


Writing a new Instrument Map Class
==================================

Any subclass of `~sunpy.map.GenericMap` which defines a method named
``is_datasource_for`` will automatically be registered with
the `~sunpy.map.Map` factory. The ``is_datasource_for`` method describes the form of the
data and metadata for which the `~sunpy.map.GenericMap` subclass is valid. For
example it might check the value of the ``INSTRUMENT`` key in the metadata
dictionary.
This makes it straightforward to define your own
`~sunpy.map.GenericMap` subclass for a new instrument or a custom data source
like simulated data. These classes only have to be imported for this to work, as
demonstrated by the following example.

.. code-block:: python
import sunpy.map
class FutureMap(sunpy.map.GenericMap):
def __init__(self, data, header, **kwargs):
super(FutureMap, self).__init__(data, header, **kwargs)
# Any Future Instrument specific keyword manipulation
# Specify a classmethod that determines if the data-header pair matches
# the new instrument
@classmethod
def is_datasource_for(cls, data, header, **kwargs):
"""Determines if header corresponds to an AIA image"""
return str(header.get('instrume', '')).startswith('FUTURESCOPE')
This class will now be available through the `~sunpy.map.Map` factory as long as this
class has been defined, i.e. imported into the current session.

If you do not want to create a method named ``is_datasource_for`` you can
manually register your class and matching method using the following method

.. code-block:: python
import sunpy.map
sunpy.map.Map.register(FutureMap, FutureMap.some_matching_method)
2 changes: 1 addition & 1 deletion sunpy/map/mapbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
PixelPair = namedtuple('PixelPair', 'x y')
SpatialPair = namedtuple('SpatialPair', 'axis1 axis2')

_META_FIX_URL = 'https://docs.sunpy.org/en/stable/code_ref/map.html#fixing-map-metadata'
_META_FIX_URL = 'https://docs.sunpy.org/en/stable/how_to/fix_map_metadata.html'

# Manually specify the ``.meta`` docstring. This is assigned to the .meta
# class attribute in GenericMap.__init__()
Expand Down

0 comments on commit 98e6a23

Please sign in to comment.