VOEvent2Dict documentation

VOEvents are a standard for representing transient astronomical events in XML. They are used by many observatories and telescopes to communicate the detection of transient events. This package provides a simple way to convert these XML files into Python dictionaries.

There are existing packages that can parse VOEvents, such as voevent-parse or xmltodict, but the dictionaries they produce are not always easy to work with. This package aims to solve that problem by providing dictionaries that are easier to work with.

Long gripe which motivates the need for this package:

Here is an example of a VOEvent file:

Example VOEvent XML file
<?xml version = '1.0' encoding = 'UTF-8'?>
<voe:VOEvent
      ivorn="ivo://nasa.gsfc.gcn/Fermi#Point_Dir_2025-01-22T08:08:00.00_000000-0-581"
      role="utility" version="2.0"
      xmlns:voe="http://www.ivoa.net/xml/VOEvent/v2.0"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.ivoa.net/xml/VOEvent/v2.0  http://www.ivoa.net/xml/VOEvent/VOEvent-v2.0.xsd" >
  <Who>
    <AuthorIVORN>ivo://nasa.gsfc.tan/gcn</AuthorIVORN>
    <Author>
      <shortName>Fermi (via VO-GCN)</shortName>
      <contactName>Julie McEnery</contactName>
      <contactPhone>+1-301-286-1632</contactPhone>
      <contactEmail>Julie.E.McEnery@nasa.gov</contactEmail>
    </Author>
    <Date>2025-01-22T08:07:45</Date>
    <Description>This VOEvent message was created with GCN VOE version: 15.08 17jun22</Description>
  </Who>
  <What>
    <Param name="Packet_Type"   value="129" />
    <Param name="Pkt_Ser_Num"   value="113" />
    <Param name="Start_TJD"     value="20697" unit="days" ucd="time" />
    <Param name="Start_SOD"     value="29280.00" unit="sec" ucd="time" />
    <Param name="Start_RA"      value="316.2710" unit="deg" ucd="pos.eq.ra" />
    <Param name="Start_Dec"     value="-56.1458" unit="deg" ucd="pos.eq.dec" />
    <Param name="Delta_T"       value="120" unit="sec" ucd="time" />
    <Param name="RA_1dt"        value="323.0" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_1dt"       value="-59.4" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_1dt"       value="0"  />
    <Param name="RA_2dt"        value="330.5" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_2dt"       value="-62.7" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_2dt"       value="0"  />
    <Param name="RA_3dt"        value="339.1" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_3dt"       value="-65.8" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_3dt"       value="0"  />
    <Param name="RA_4dt"        value="349.1" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_4dt"       value="-68.9" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_4dt"       value="0"  />
    <Param name="RA_5dt"        value="1.1" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_5dt"       value="-71.6" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_5dt"       value="0"  />
    <Param name="RA_6dt"        value="15.9" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_6dt"       value="-73.8" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_6dt"       value="0"  />
    <Param name="RA_7dt"        value="33.4" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_7dt"       value="-75.2" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_7dt"       value="0"  />
    <Param name="RA_8dt"        value="52.8" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_8dt"       value="-75.6" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_8dt"       value="0"  />
    <Param name="RA_9dt"        value="72.0" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_9dt"       value="-74.9" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_9dt"       value="0"  />
    <Param name="RA_10dt"       value="83.2" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_10dt"      value="-68.8" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_10dt"      value="0"  />
    <Param name="RA_11dt"       value="81.9" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_11dt"      value="-44.4" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_11dt"      value="0"  />
    <Param name="RA_12dt"       value="84.3" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_12dt"      value="-21.6" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_12dt"      value="0"  />
    <Param name="RA_13dt"       value="90.2" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_13dt"      value="-13.5" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_13dt"      value="0"  />
    <Param name="RA_14dt"       value="95.5" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_14dt"      value="-5.1" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_14dt"      value="0"  />
    <Param name="RA_15dt"       value="100.3" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_15dt"      value="3.3" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_15dt"      value="0"  />
    <Param name="RA_16dt"       value="104.8" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_16dt"      value="10.9" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_16dt"      value="0"  />
    <Param name="RA_17dt"       value="108.9" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_17dt"      value="18.6" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_17dt"      value="0"  />
    <Param name="RA_18dt"       value="112.5" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_18dt"      value="26.3" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_18dt"      value="0"  />
    <Param name="RA_19dt"       value="116.5" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_19dt"      value="32.4" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_19dt"      value="0"  />
    <Param name="RA_20dt"       value="120.0" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_20dt"      value="38.6" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_20dt"      value="0"  />
    <Param name="RA_21dt"       value="123.1" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_21dt"      value="44.7" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_21dt"      value="0"  />
    <Param name="RA_22dt"       value="128.0" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_22dt"      value="48.9" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_22dt"      value="0"  />
    <Param name="RA_23dt"       value="132.9" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_23dt"      value="53.1" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_23dt"      value="0"  />
    <Param name="RA_24dt"       value="137.9" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_24dt"      value="57.3" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_24dt"      value="0"  />
    <Param name="RA_25dt"       value="146.9" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_25dt"      value="59.5" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_25dt"      value="0"  />
    <Param name="RA_26dt"       value="156.9" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_26dt"      value="61.6" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_26dt"      value="0"  />
    <Param name="RA_27dt"       value="168.1" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_27dt"      value="63.4" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_27dt"      value="0"  />
    <Param name="RA_28dt"       value="183.0" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_28dt"      value="62.7" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_28dt"      value="0"  />
    <Param name="RA_29dt"       value="197.6" unit="deg" ucd="pos.eq.ra" />
    <Param name="Dec_29dt"      value="61.2" unit="deg" ucd="pos.eq.dec" />
    <Param name="SAA_29dt"      value="0"  />
    <Param name="Coords_Type"   value="2" unit="dn" />
    <Param name="Coords_String" value="pointing_direction" />
    <Group name="Obs_Support_Info" >
      <Description>The Sun and Moon values are valid at the time the VOEvent XML message was created.</Description>
      <Param name="Sun_RA"        value="304.83" unit="deg" ucd="pos.eq.ra" />
      <Param name="Sun_Dec"       value="-19.59" unit="deg" ucd="pos.eq.dec" />
      <Param name="Sun_Distance"  value="37.54" unit="deg" ucd="pos.angDistance" />
      <Param name="Sun_Hr_Angle"  value="-0.79" unit="hr" />
      <Param name="Moon_RA"       value="214.25" unit="deg" ucd="pos.eq.ra" />
      <Param name="Moon_Dec"      value="-17.10" unit="deg" ucd="pos.eq.dec" />
      <Param name="MOON_Distance" value="82.62" unit="deg" ucd="pos.angDistance" />
      <Param name="Moon_Illum"    value="45.52" unit="%" ucd="arith.ratio" />
      <Param name="Galactic_Long" value="340.74" unit="deg" ucd="pos.galactic.lon" />
      <Param name="Galactic_Lat"  value="-40.67" unit="deg" ucd="pos.galactic.lat" />
      <Param name="Ecliptic_Long" value="300.49" unit="deg" ucd="pos.ecliptic.lon" />
      <Param name="Ecliptic_Lat"  value="-37.50" unit="deg" ucd="pos.ecliptic.lat" />
    </Group>
    <Description>The current and soon-to-be pointing directions for the Fermi spacecraft.</Description>
  </What>
  <WhereWhen>
    <ObsDataLocation>
      <ObservatoryLocation id="GEOLUN" />
      <ObservationLocation>
        <AstroCoordSystem id="UTC-FK5-GEO" />
        <AstroCoords coord_system_id="UTC-FK5-GEO">
          <Time unit="s">
            <TimeInstant>
              <ISOTime>2025-01-22T08:08:00.00</ISOTime>
            </TimeInstant>
          </Time>
          <Position2D unit="deg">
            <Name1>RA</Name1>
            <Name2>Dec</Name2>
            <Value2>
              <C1>316.2710</C1>
              <C2>-56.1458</C2>
            </Value2>
            <Error2Radius>0.0000</Error2Radius>
          </Position2D>
        </AstroCoords>
      </ObservationLocation>
    </ObsDataLocation>
  <Description>The RA,Dec coordinates are of the type: pointing_direction.</Description>
  </WhereWhen>
  <How>
    <Description>Fermi Satellite, Spacecraft</Description>
    <Reference uri="http://gcn.gsfc.nasa.gov/fermi.html" type="url" />
  </How>
  <Why>
    <Inference probability="1.00">
      <Concept>Fermi spacecraft is currently looking in this direction.</Concept>
    </Inference>
  </Why>
  <Description>
  </Description>
</voe:VOEvent>

When this is parsed by voevent-parse, the resulting structure is not a dict, but a custom object that requires users to understand how xml files are built. For example any time there is a Group or Param in the xml file the user has to manually navigate the structure to get the data they want. See this tutorial

When the above file is parsed by xmltodict, the resulting dictionary looks like this:

{
'voe:VOEvent': {'@ivorn': 'ivo://nasa.gsfc.gcn/Fermi#Point_Dir_2025-01-22T08:08:00.00_000000-0-581',
'@role': 'utility',
'@version': '2.0',
'@xmlns:voe': 'http://www.ivoa.net/xml/VOEvent/v2.0',
'@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
'@xsi:schemaLocation': 'http://www.ivoa.net/xml/VOEvent/v2.0  http://www.ivoa.net/xml/VOEvent/VOEvent-v2.0.xsd',
'Who': {'AuthorIVORN': 'ivo://nasa.gsfc.tan/gcn',
   'Author': {'shortName': 'Fermi (via VO-GCN)',
   'contactName': 'Julie McEnery',
   'contactPhone': '+1-301-286-1632',
   'contactEmail': 'Julie.E.McEnery@nasa.gov'},
   'Date': '2025-01-22T08:07:45',
   'Description': 'This VOEvent message was created with GCN VOE version: 15.08 17jun22'},
'What': {'Param': [{'@name': 'Packet_Type', '@value': '129'},
   {'@name': 'Pkt_Ser_Num', '@value': '113'},
   {'@name': 'Start_TJD', '@value': '20697', '@unit': 'days', '@ucd': 'time'},
   {'@name': 'Start_SOD',
   '@value': '29280.00',
   '@unit': 'sec',
   '@ucd': 'time'},
   {'@name': 'Start_RA',
   '@value': '316.2710',
   '@unit': 'deg',
   '@ucd': 'pos.eq.ra'},
   {'@name': 'Start_Dec',
   '@value': '-56.1458',
   '@unit': 'deg',
   '@ucd': 'pos.eq.dec'},
   {'@name': 'Delta_T', '@value': '120', '@unit': 'sec', '@ucd': 'time'},
   {'@name': 'RA_1dt',
   '@value': '323.0',
   '@unit': 'deg',
   '@ucd': 'pos.eq.ra'},
   {'@name': 'Dec_1dt',
   '@value': '-59.4',
   '@unit': 'deg',
   '@ucd': 'pos.eq.dec'},
   ...
   {'@name': 'SAA_29dt', '@value': '0'},
   {'@name': 'Coords_Type', '@value': '2', '@unit': 'dn'},
   {'@name': 'Coords_String', '@value': 'pointing_direction'}],
   'Group': {'@name': 'Obs_Support_Info',
   'Description': 'The Sun and Moon values are valid at the time the VOEvent XML message was created.',
   'Param': [{'@name': 'Sun_RA',
      '@value': '304.83',
      '@unit': 'deg',
      '@ucd': 'pos.eq.ra'},
   {'@name': 'Sun_Dec',
      '@value': '-19.59',
      '@unit': 'deg',
      '@ucd': 'pos.eq.dec'},
   {'@name': 'Sun_Distance',
      '@value': '37.54',
      '@unit': 'deg',
      '@ucd': 'pos.angDistance'},
   {'@name': 'Sun_Hr_Angle', '@value': '-0.79', '@unit': 'hr'},
   ...
   ]},
   'Description': 'The current and soon-to-be pointing directions for the Fermi spacecraft.'},
'WhereWhen': {'ObsDataLocation': {'ObservatoryLocation': {'@id': 'GEOLUN'},
   'ObservationLocation': {'AstroCoordSystem': {'@id': 'UTC-FK5-GEO'},
   'AstroCoords': {'@coord_system_id': 'UTC-FK5-GEO',
      'Time': {'@unit': 's',
      'TimeInstant': {'ISOTime': '2025-01-22T08:08:00.00'}},
      'Position2D': {'@unit': 'deg',
      'Name1': 'RA',
      'Name2': 'Dec',
      'Value2': {'C1': '316.2710', 'C2': '-56.1458'},
      'Error2Radius': '0.0000'}}}},
   'Description': 'The RA,Dec coordinates are of the type: pointing_direction.'},
'How': {'Description': 'Fermi Satellite, Spacecraft',
   'Reference': {'@uri': 'http://gcn.gsfc.nasa.gov/fermi.html',
   '@type': 'url'}},
'Why': {'Inference': {'@probability': '1.00',
   'Concept': 'Fermi spacecraft is currently looking in this direction.'}},
'Description': None}
}

If a user wants to access the Packet_Type, they would have to do something like this:

packet_type = voevent['voe:VOEvent']['What']['Param'][0]['@value']

Which is very annoying because it’s unclear that [‘Param’] is a list and that the [0] element is the Packet_Type. Instead I want users to be able to do this:

packet_type = voevent['What']['Packet_Type']