Repository: sunpy/sunpy

Issue:
Add support to download the header information from helioviewer
Currently our HelioViewer Client (https://github.com/sunpy/sunpy/blob/master/sunpy/net/helioviewer.py) allows us to download jpeg files from the service. They (Helioviewer) have an a better API now (v2 which is linked in the original post) that allows us to also download the metadata of the jpeg files with a seperate function. We do not have that seperate function, which is what this issue is tracking. 

The API returns XML data so we want to convert it to a nicer dictionary format.


Discussion:
also related to #2744 
I would like to work on this. Can you explain it a bit?
Currently our HelioViewer Client (https://github.com/sunpy/sunpy/blob/master/sunpy/net/helioviewer.py) allows us to download jpeg files from the service. They (Helioviewer) have an a better API now (v2 which is linked in the original post) that allows us to also download the metadata of the jpeg files with a seperate function. We do not have that seperate function, which is what this issue is tracking. 

This is also related to https://github.com/sunpy/sunpy/issues/2280 since we currently use v1 of the API and need to move to V2. 
Ok got it
Is this issue still open? If yes, I would like to work on it.
Yes this issue is still open but before working on this we need to shift our API to version 2 and we are working on that. There are a lot of changes in the helioviewer code and that code still needs to be reviewed. We can only add that support after our API exclusive support the version 2 because downloading header features is supported on by the version 2 of the API
@ishanisri It is possible to do if you base your work off @Naman9639 code. 
But that code still needs review right? @nabobalis
Yes but the main part which is the url change would allow someone to write that function without touching your code.

Any changes made to your code would mean a rebase for any pull request based on that work. 
Ahh yeah.
I have a doubt. In what format do we want the data to be downloaded because the API returns XML data. So do want to download the .xml file or we want it some other format?
Probably convert it to a dictionary.  
ok
Okay, so as I am new to this, will I have to clone @Naman9639's fork of this repo to work on this issue?
Yeah it is a bit more involved. 

You probably want to fork this repo first.
Then add Naman's fork as a remote doing (i think this is correct) `git remote add naman https://github.com/Naman9639/sunpy`
and then you should be able to `git checkout -b <his work>` locally and work on it. 
Hi ! I am sorry I could not put in much work at the end of the year , but any updates on this that I should know ?
That's no problem. The only update is that the API changes occurred. 
Um, is this issue still open and may I work on this?
Yes.
We want the header information to be in XML format or do we want to print it on the terminal in dictionary format?
We probably want it to returned in a dictionary format. 
Ok I will do that.

Resolving patch:
diff --git a/sunpy/net/helioviewer.py b/sunpy/net/helioviewer.py
--- a/sunpy/net/helioviewer.py
+++ b/sunpy/net/helioviewer.py
@@ -11,9 +11,11 @@
 from astropy.utils.decorators import lazyproperty
 
 import sunpy
+from sunpy.util.xml import xml_to_dict
 from sunpy.time import parse_time
 from sunpy.util.net import download_fileobj
 
+
 __all__ = ['HelioviewerClient']
 
 
@@ -189,6 +191,65 @@ def download_jp2(self, date, observatory=None, instrument=None, detector=None,
 
         return self._get_file(params, directory=directory, overwrite=overwrite)
 
+    def get_jp2_header(self, date, observatory=None, instrument=None, detector=None, measurement=None, jp2_id=None):
+        """
+        Get the XML header embedded in a JPEG2000 image. Includes the FITS header as well as a section 
+        of Helioviewer-specific metadata.
+
+        This uses `getJP2Header <https://api.helioviewer.org/docs/v2/#JPEG2000>`_ from the Helioviewer API.
+
+        Parameters
+        ----------
+        date : `astropy.time.Time`, `str`
+            A `~sunpy.time.parse_time` parsable string or `~astropy.time.Time`
+            object for the desired date of the image
+        observatory : `str`
+            Observatory name
+        instrument : `str`
+            Instrument name
+        measurement : `str`
+            Measurement name
+        detector : `str`
+            Detector name
+        jp2_id : `int`
+            Unique JP2 image identifier.
+            This can be used directly instead of using the previous parameters.
+
+        Returns
+        -------
+        out : `dict`
+            Returns a dictionary containing the header information of JPEG 2000 image.
+            The returned dictionary may have either one or two keys: *fits* and *helioviewer*.
+
+        Examples
+        --------
+        >>> from sunpy.net import helioviewer
+        >>> hv = helioviewer.HelioviewerClient()  # doctest: +REMOTE_DATA
+        >>> header = hv.get_jp2_header('2012/07/03', observatory='SDO',
+        ...                            instrument='HMI', detector=None, measurement='continuum')  # doctest: +REMOTE_DATA
+        >>> # The key 'fits' can be used to get the fits header information
+        >>> fits_header = header['fits']  # doctest: +REMOTE_DATA
+        >>> # The keys 'helioviewer' can be used to extract the helioviewer specific metadata.
+        >>> helioviewer_meta_data = header['helioviewer']  # doctest: +REMOTE_DATA
+        """
+        if jp2_id is None:
+            try:
+                jp2_id = self.get_closest_image(date, observatory, instrument, detector, measurement)['id']
+            except KeyError:
+                raise KeyError("The values used for observatory, instrument, detector, measurement "
+                               "do not correspond to a source_id. Please check the list using "
+                               "HelioviewerClient.data_sources.")
+
+        params = {
+            "action": "getJP2Header",
+            "id" : jp2_id,
+        }
+
+        responses = self._request(params)
+        # Reads the output from HTTPResponse object and decodes it.
+        responses = responses.read().decode('utf-8')
+        return xml_to_dict(responses)['meta']
+
     def download_png(self, date, image_scale, layers,
                      directory=None, overwrite=False, watermark=False,
                      events="", event_labels=False,
@@ -384,7 +445,6 @@ def _request(self, params):
         """
         response = urllib.request.urlopen(
             self._api, urllib.parse.urlencode(params).encode('utf-8'))
-
         return response
 
     def _format_date(self, date):


Test patch:
diff --git a/sunpy/net/tests/test_helioviewer.py b/sunpy/net/tests/test_helioviewer.py
--- a/sunpy/net/tests/test_helioviewer.py
+++ b/sunpy/net/tests/test_helioviewer.py
@@ -74,6 +74,17 @@ def test_download_jp2(self, client):
         assert "2011_01_11__22_39_00_000__SOHO_MDI_MDI_continuum.jp2" in filepath
         os.remove(filepath)
 
+    def test_get_jp2_header(self, client):
+        """
+        Tests getJP2Header API method
+        """
+        header1 = client.get_jp2_header('1994/01/01', observatory='SOHO',
+                                              instrument='EIT', measurement='304')
+        header2 = client.get_jp2_header('1994/01/01', jp2_id = 1795504)
+        assert header1 == header2
+        assert len(header1) == len(header2) == 1
+        assert ('fits' in header1.keys()) and ('fits' in header2.keys())
+
     @skip_glymur
     def test_download_jp2_map(self, client):
         """