Skip to content

Adding New Image Sources

Daniel Garcia Briseno edited this page Apr 16, 2025 · 14 revisions

0. Learn about the Pipeline

In this section, I discuss how images go from an observatory to being available on Helioviewer. From this information, I hope that the following steps for adding new data will make sense, and will give you some context about the updates that need to be made.

Source Data

During an observatory's regular operation, the measurements made are typically processed into a Flexible Image Transport System (FITS) file. There are various kinds of FITS files (typically called level X) which have gone through various levels of processing. There are FITS files that consist of raw measurement data, these usually aren't suitable for visualization purposes. Then there are FITS files that have gone through some processing to make them presentable. These are the types of FITS files we prefer for Helioviewer.

JPEG 2000 Compression

Helioviewer makes use of the JPEG2000 (*.jp2) file format. This type of image format boasts excellent compression ratios, allows embedding arbitrary metadata in the files, allows reading partial regions of an image, and allows multiple images to be merged into a single file. In order for data to be used in Helioviewer, the FITS images have to be converted into a grayscale JPEG2000 files, with the FITS header containing World Coordinate System (WCS) information embedded as XML within the jpeg2000 file.

Helioviewer Preprocessing

Creation of FITS and JPEG2000 files are considered upstream processes. The Helioviewer Project doesn't manage creation of jpeg2000 files, we retrieve them from data providers.

When Helioviewer gets a new source of jpeg2000 files, a patch has to be applied to Helioviewer's source code to be able to serve the images. The patch has to do the following:

  • Define where to get the jp2 image files.
  • Add new database identifiers for the new data source.
  • Define a selection hierarchy for the Helioviewer UI (i.e. user will select SOHO > LASCO > C2, or SDO > AIA > 304.
  • Define how to read the jp2 files if non-standard fields are used.
  • Define image processing steps such as adding color or making portions of the image transparent.
  • Define watermark information, the text that appears for the data source Helioviewer movies & screenshots.
  • Add attribution to the helioviewer.org About Dialog.

After these updates are made, Helioviewer will be able to read a list of jpeg2000 files, add them to its internal database of images, and enable loading the images on Helioviewer.org and JHelioviewer.

1. Gather Information

In general, you will need to know the following things about your new datasource:

  • Where to get jp2 images. This can be a remote web directory with jp2 files, or a local folder with jp2 files.
  • All the different instruments and measurements that are provided by your new datasource, and an idea of the selection hierarchy
  • Machine readable information available from your jp2 files. This comes from FITS metadata embedded as XML in the jp2 file.

1.a. Getting machine readable information

This is the name that the data ingestion scripts will see when importing the jp2 image. To get the exact name, download a jp2 for each new measurement, and execute <api>/management/data/get_jp2_info.py <jp2 file>

NOTE: This script requires a connection to the Helioviewer database, so it should be executed within a Helioviewer server environment. If you're using the docker compose environment, then execute this within the helioviewer-cli container. Inside the container, source the python virtual environment via source ~/venv/bin/activate. This script lives in ~/api/management/data.

This will print the information found in the FITS header which is needed for data ingestion. For example, for EUI HRI 1216, it prints the following

observatory: Solar_Orbiter
instrument: EUI
detector: HRI_LYA
measurement: 1216

These are the values that must be set in the datasource_property table for data ingestion to work.

1.b Custom Labels

The default observatory, instrument, detector, measurement covers the hierarchy for many data sources, but it doesn't cover all of them. For example, the RHESSI instrument uses observatory, energy band, reconstruction method instead.

To enable custom fields, you must enable the pipeline to extract your desired labels from the jp2 metadata by updating jp2parser.py:JP2parser:getData. For reference, RHESSI is handled by adding extra processing. pseudocode below:

def getData():
  ...
    if image["observatory"] == "RHESSI":
      execute rhessi specific processing
  ...

def _process_rhessi_extras(...):
  add energy band to the data dictionary.
  add reconstruction method to the data dictionary.

Then in jp2.py:insert_images, update the processing to read your labels. This function reads the fields returned by jp2parser's getData().

if img["observatory"] == "RHESSI":
  leafs = ["observatory", "energy_band", "reconstruction_method"]

2. Create Datasource IDs

The first step is to create new ids for each measurement. Update <api>/install/helioviewer/db.py:create_datasource_table with the new data source.

If you already have a database instance running, then also insert that new data source into the existing datasources table. Create a sql file with the insert statement in <api>/install/database to help mirrors easily update their database.

TODO: Still need to understand layeringOrder, sourceIdGroup, and displayOrder. For now just set:

  • layeringOrder to 1
  • sourceIdGroup to ''
  • displayOrder to 0
  • Set enabled to 0

3. Create datasource properties

Each new data source created in step 2 need fields added to the datasource_property table. This is the text that will show up in the UI. Typically this will use uiOrder 1, 2, 3, 4 in the form Observatory, Instrument, Detector, Measurement. All of this information can be gathered with information in section 1.a. If you're using custom fields, the name field can be used to set the label, and the fitsName field should match the value read from a jp2 file for your custom label.

Add these property insertions to the sql file created in section 2.

name Is the display name that will be shown in the web application fitsName Is the value in the image metadata used for data ingestion description should be the expanded name of the acronym. uiOrder is the order the dropdowns should appear.

4. Add a Server Description

The server description points Helioviewer to the URL used for downloading images for the new datasource. It lives in <api>/helioviewer/hvpull/servers. Refer to other server definitions in that folder for implementation details.

After defining the server definition, you must update daemon.py with the new server in the get_servers() function. This simply returns a dictionary in the form {filename: server_class}, add your new filename, classname pair to the dictionary.

The filename mentioned above is the name that will be used for the -d server parameter in downloader.py.

5 Add Image to API

The API backend needs to know how to handle the new image type. Therefore a new class must be defined in <api>/src/Image/ImageType. The new image should have a class name of Image_ImageType_xxxImage. Use EUIVImage.php as your starting point as its the simplest form of image source.

In the constructor you must set a color table (see 5.b) and you must define the getWaterMarkName() function.

In this context, $uiLabels is an array of Observatory, Instrument, Detector, Measurement in the form:

{
    {
        'label': 'Observatory',
        'name': Your Observatory name
    },
    {
        'label': 'Instrument',
        'name': Your Instrument Name
    },
    {
        'label': 'Detector',
        'name': Your Detector Name
    },
    {
        'label': 'Measurement',
        'name': Your measurement
    }
}

These were defined in section 3.

5.b.

You will also need to create color tables for the image. For this you will need to work with Jack and/or Bogden to gather information about the desired colors. There may be some manual effort here. For EUI I was given a text file describing the RGB colors that I needed to convert into the png usable for helioviewer. If you have a text file with the RGB values available, you may use <api>/scripts/gen_color_tables/gen_color_table.php to create the png.

When you have the appropriate color pngs in <api>/docroot/resources/images/color-tables, then program your ***Image.php class to return the appropriate color table.

6 Test

That's all that needs to be done. Test downloader.py and make sure that the downloader is able to parse files from the new server. Then check Helioviewer to verify how the images are displayed.

Appendix A - Troubleshooting

This section details problems that have been observed when trying to add new image sources.

Bad Date Format

Seen when attempting to add Solar Orbiter data to Helioviewer.

ValueError: time data 'solo_L3_eui-fsi174-i' does not match format '%Y_%m_%d__%H_%M_%S'

At the time of writing daemon.py assumed the first 20 characters of the filename contain the date. That is not the case for Solar Orbiter images. To work around this, the server definitions have been updated with a function get_datetime_from_file which can be used to override this behavior. If this function is not defined, then the old behavior will be used. Otherwise, you must implement your own time parser in the server definition by implementing a function named get_datetime_from_file for this. See solar_orbiter.py for details.

Missing Database Values

If you get a KeyError in jp2.py:insert_images it is most likely because some field is missing from the database.

The error looks like this:

    source = source[str(img[leaf])]
KeyError: '304'

If you see this, check the file that it's failing on, and refer to section 1.a. for details on what to add to the database.

Appendix B - FITS Fields

For the best compatibility and user experience, the FITS header should be transformed into XML and embedded as an XMLBox in the JPEG2000 header. It looks like this (as an example):

Sample Header
XML Box (xml ) @ (6500, 6423)
    <meta>
    <fits>
    <SIMPLE>1</SIMPLE>
    <BITPIX>16</BITPIX>
    <NAXIS>2</NAXIS>
    <NAXIS1>4096</NAXIS1>
    <NAXIS2>4096</NAXIS2>
    <EXTEND>1</EXTEND>
    <ORIGIN>SDO</ORIGIN>
    <DATE>2021-06-01T00:09:33.000</DATE>
    <TELESCOP>SDO</TELESCOP>
    <INSTRUME>AIA_4</INSTRUME>
    <DATE-OBS>2021-06-01T00:01:29.132</DATE-OBS>
    <T_OBS>2021-06-01T00:01:30.583</T_OBS>
    <TOBSSTEP>1.00000</TOBSSTEP>
    <TOBSEPOC>1977.01.01_00:00:00.000_TAI</TOBSEPOC>
    <CAMERA>4</CAMERA>
    <IMG_TYPE>LIGHT</IMG_TYPE>
    <EXPTIME>2.9020560</EXPTIME>
    <EXPSDEV>0.00020240554</EXPSDEV>
    <INT_TIME>3.1484375</INT_TIME>
    <WAVELNTH>304</WAVELNTH>
    <WAVEUNIT>angstrom</WAVEUNIT>
    <WAVE_STR>304_THIN</WAVE_STR>
    <FSN>235893077</FSN>
    <FID>0</FID>
    <LVL_NUM>1.50000</LVL_NUM>
    <QUALLEV0>0</QUALLEV0>
    <QUALITY>1073741824</QUALITY>
    <TOTVALS>16777216</TOTVALS>
    <DATAVALS>16777216</DATAVALS>
    <MISSVALS>0</MISSVALS>
    <PERCENTD>100.000</PERCENTD>
    <DATAMIN>-7</DATAMIN>
    <DATAMAX>988</DATAMAX>
    <DATAMEDN>3</DATAMEDN>
    <DATAMEAN>4.7359357</DATAMEAN>
    <DATARMS>6.4962573</DATARMS>
    <DATASKEW>7.4229717</DATASKEW>
    <DATAKURT>284.68384</DATAKURT>
    <OSCNMEAN>0</OSCNMEAN>
    <OSCNRMS>0</OSCNRMS>
    <FLAT_REC>aia.flatfield[:#655]</FLAT_REC>
    <CTYPE1>HPLN-TAN</CTYPE1>
    <CUNIT1>arcsec</CUNIT1>
    <CRVAL1>0.00000</CRVAL1>
    <CDELT1>0.60000002</CDELT1>
    <CRPIX1>2048.50</CRPIX1>
    <CTYPE2>HPLT-TAN</CTYPE2>
    <CUNIT2>arcsec</CUNIT2>
    <CRVAL2>0.00000</CRVAL2>
    <CDELT2>0.60000002</CDELT2>
    <CRPIX2>2048.50</CRPIX2>
    <CROTA2>0.00000</CROTA2>
    <R_SUN>1577.4635</R_SUN>
    <MPO_REC>sdo.master_pointing[:#1458]</MPO_REC>
    <INST_ROT>-0.13163900</INST_ROT>
    <IMSCL_MP>0.60016501</IMSCL_MP>
    <X0_MP>2068.8899</X0_MP>
    <Y0_MP>2009.7500</Y0_MP>
    <RSUN_LF>0</RSUN_LF>
    <X0_LF>0</X0_LF>
    <Y0_LF>0</Y0_LF>
    <ASD_REC>sdo.lev0_asd_0004[:#89405241]</ASD_REC>
    <SAT_Y0>-5.8003411</SAT_Y0>
    <SAT_Z0>9.1231413</SAT_Z0>
    <SAT_ROT>3.1858184e-05</SAT_ROT>
    <ACS_MODE>SCIENCE</ACS_MODE>
    <ACS_ECLP>NO</ACS_ECLP>
    <ACS_SUNP>YES</ACS_SUNP>
    <ACS_SAFE>NO</ACS_SAFE>
    <ACS_CGT>GT3</ACS_CGT>
    <ORB_REC>sdo.fds_orbit_vectors[2021.06.01_00:01:00_UTC]</ORB_REC>
    <DSUN_REF>1.4959787e+11</DSUN_REF>
    <DSUN_OBS>1.5167896e+11</DSUN_OBS>
    <RSUN_REF>6.9600000e+08</RSUN_REF>
    <RSUN_OBS>946.47807</RSUN_OBS>
    <GCIEC_X>0</GCIEC_X>
    <GCIEC_Y>0</GCIEC_Y>
    <GCIEC_Z>0</GCIEC_Z>
    <HCIEC_X>0</HCIEC_X>
    <HCIEC_Y>0</HCIEC_Y>
    <HCIEC_Z>0</HCIEC_Z>
    <OBS_VR>2327.1037</OBS_VR>
    <OBS_VW>28223.582</OBS_VW>
    <OBS_VN>5788.5049</OBS_VN>
    <CRLN_OBS>88.404388</CRLN_OBS>
    <CRLT_OBS>-0.66814470</CRLT_OBS>
    <CAR_ROT>2244</CAR_ROT>
    <ROI_NWIN>268435456</ROI_NWIN>
    <ROI_SUM>0</ROI_SUM>
    <ROI_NAX1>0</ROI_NAX1>
    <ROI_NAY1>0</ROI_NAY1>
    <ROI_LLX1>0</ROI_LLX1>
    <ROI_LLY1>0</ROI_LLY1>
    <ROI_NAX2>0</ROI_NAX2>
    <ROI_NAY2>0</ROI_NAY2>
    <ROI_LLX2>0</ROI_LLX2>
    <ROI_LLY2>0</ROI_LLY2>
    <ISPSNAME>aia.lev0_isp_0011</ISPSNAME>
    <ISPPKTIM>2021-06-01T00:01:27.507</ISPPKTIM>
    <ISPPKTVN>001.197</ISPPKTVN>
    <AIVNMST>453</AIVNMST>
    <AIMGOTS>2001196927</AIMGOTS>
    <ASQHDR>3457118549</ASQHDR>
    <ASQTNUM>3</ASQTNUM>
    <ASQFSN>235893077</ASQFSN>
    <AIAHFSN>235893069</AIAHFSN>
    <AECDELAY>1534</AECDELAY>
    <AIAECTI>0</AIAECTI>
    <AIASEN>0</AIASEN>
    <AIFDBID>241</AIFDBID>
    <AIMGOTSS>13697</AIMGOTSS>
    <AIFCPS>4</AIFCPS>
    <AIFTSWTH>0</AIFTSWTH>
    <AIFRMLID>3337</AIFRMLID>
    <AIFTSID>40960</AIFTSID>
    <AIHISMXB>7</AIHISMXB>
    <AIHIS192>0</AIHIS192>
    <AIHIS348>8252211</AIHIS348>
    <AIHIS604>8388011</AIHIS604>
    <AIHIS860>8388608</AIHIS860>
    <AIFWEN>204</AIFWEN>
    <AIMGSHCE>2900</AIMGSHCE>
    <AECTYPE>0</AECTYPE>
    <AECMODE>ON</AECMODE>
    <AISTATE>CLOSED</AISTATE>
    <AIAECENF>1</AIAECENF>
    <AIFILTYP>0</AIFILTYP>
    <AIMSHOBC>53.891998</AIMSHOBC>
    <AIMSHOBE>67.351997</AIMSHOBE>
    <AIMSHOTC>39.855999</AIMSHOTC>
    <AIMSHOTE>24.927999</AIMSHOTE>
    <AIMSHCBC>2955.7241</AIMSHCBC>
    <AIMSHCBE>2969.4719</AIMSHCBE>
    <AIMSHCTC>2941.7720</AIMSHCTC>
    <AIMSHCTE>2927.2839</AIMSHCTE>
    <AICFGDL1>0</AICFGDL1>
    <AICFGDL2>25</AICFGDL2>
    <AICFGDL3>89</AICFGDL3>
    <AICFGDL4>236</AICFGDL4>
    <AIFOENFL>1</AIFOENFL>
    <AIMGFSN>3</AIMGFSN>
    <AIMGTYP>0</AIMGTYP>
    <AIAWVLEN>8</AIAWVLEN>
    <AIAGP1>0</AIAGP1>
    <AIAGP2>0</AIAGP2>
    <AIAGP3>0</AIAGP3>
    <AIAGP4>0</AIAGP4>
    <AIAGP5>0</AIAGP5>
    <AIAGP6>0</AIAGP6>
    <AIAGP7>0</AIAGP7>
    <AIAGP8>281</AIAGP8>
    <AIAGP9>345</AIAGP9>
    <AIAGP10>748</AIAGP10>
    <AGT1SVY>7</AGT1SVY>
    <AGT1SVZ>-20</AGT1SVZ>
    <AGT2SVY>-4</AGT2SVY>
    <AGT2SVZ>-16</AGT2SVZ>
    <AGT3SVY>-2</AGT3SVY>
    <AGT3SVZ>0</AGT3SVZ>
    <AGT4SVY>57</AGT4SVY>
    <AGT4SVZ>123</AGT4SVZ>
    <AIMGSHEN>12</AIMGSHEN>
    <RECNUM>230361239</RECNUM>
    <DRMS_ID>aia_test.lev1p5:230361239:image_lev1p5</DRMS_ID>
    <PRIMARYK>T_OBS, WAVELNTH</PRIMARYK>
    <HEADSUM>HfdQKZaNHdaNHZaN</HEADSUM>
    <LONGSTRN>OGIP 1.0</LONGSTRN>
    <BLANK>-32768</BLANK>
    <CHECKSUM>ZMjebJhdZJhdbJhd</CHECKSUM>
    <DATASUM>3404142404</DATASUM>
    <DATE_OBS>2021-06-01T00:01:29.132</DATE_OBS>
    <XCEN>0.0000000</XCEN>
    <YCEN>0.0000000</YCEN>
    <history>
    FITSHEAD2STRUCT run at: Tue Jun  1 14:07:45 2021
    </history>
    <comment>
    FITS (Flexible Image Transport System) format is defined in 'Astronomy
    and Astrophysics', volume 376, page 359; bibcode: 2001A&amp;A...376..359H
    This FITS file may contain long string keyword values that are
    continued over multiple keywords.  The HEASARC convention uses the &amp;
    character at the end of each substring which is then continued
    on the next keyword which has the name CONTINUE.
    FITSHEAD2STRUCT
    </comment>
    </fits>
    <helioviewer>
    <HV_ROTATION>0.00000</HV_ROTATION>
    <HV_JP2GEN_VERSION>0.8</HV_JP2GEN_VERSION>
    <HV_JP2GEN_BRANCH_REVISION>No valid revision number found. Bazaar not installed? Using HV_WRITTENBY manually included revision number: 84 [2011/01/10, https://launchpad.net/jp2gen] : % SPAWN: Error managing child process.:  No such file or directory</HV_JP2GEN_BRANCH_REVISION>
    <HV_HVS_DETAILS_FILENAME>hvs_version5.pro</HV_HVS_DETAILS_FILENAME>
    <HV_HVS_DETAILS_FILENAME_VERSION>5.0</HV_HVS_DETAILS_FILENAME_VERSION>
    <HV_COMMENT>JP2 file created locally at Lockheed LMSAL using hv_aia_list2jp2_gs2 at Tue Jun  1 14:07:45 2021.
    Contact Helioviewer LMSAL Franchise ([email protected]) for more details/questions/comments regarding this JP2 file.
    HVS (Helioviewer setup) file used to create this JP2 file: hvs_version5.pro (version 5.0).
    FITS to JP2 source code provided by ESA/NASA Helioviewer Project [contact the Helioviewer Project at [email protected]][NASA-GSFC] and is available for download at https://launchpad.net/jp2gen.
    Please contact the source code providers if you suspect an error in the source code.
    Full source code for the entire Helioviewer Project can be found at https://launchpad.net/helioviewer.</HV_COMMENT>
    <HV_SUPPORTED>TRUE</HV_SUPPORTED>
    </helioviewer>
    </meta>

Helioviewer depends on information in the header for position images. Helioviewer.org also allows viewing the full FITS metadata in case users are interested in more specific details. Having the full header means if there is an update to take advantage of other features present in the header, the information will already be present in the header.

In case the header is missing some fields required by Helioviewer, this is a list of required values:

FITS Key Description
DSUN_OBS or DSUN Distance between the observer and the sun, in meters
SOLAR_R or RADIUS Width of the sun's radius in the image, in pixels.
NAXIS1 Image width in pixels
NAXIS2 Image height in pixels
CRPIX1 / CRPIX2 x,y pixel coordinate of the reference pixel measured from the bottom left of the image. Can be outside of the image
CRVAL1 / CRVAL2 HPC coordinate of the reference pixel in arcseconds
CDELT1 / CDELT2 Area represented by the pixel in arcseconds per pixel. Often called Image Scale. Helioviewer requires CDELT1 == CDELT2
TELESCOP Name of the observatory i.e. SDO, SOHO, Hinode
DETECTOR Detector used to create the image i.e C2/C3 for LASCO or COR1, COR2 for STEREO.
INSTRUME Instrument of the observatory used to create the image i.e. EIT, MDI for SOHO or AIA, HMI for SDO.
DATE-OBS Date and time the image was taken