1+ .. _mivot-examples :
2+
13************************************************************
24MIVOT (``pyvo.mivot ``): How to use annotated data - Examples
35************************************************************
46
5- Photometric properties readout
7+ Photometric Properties Readout
68==============================
79
810This example is based on VOTables provided by the ``XTapDB `` service.
@@ -22,32 +24,30 @@ to tell the server to annotate the queried data.
2224
2325(*Please read the comment inside the code snippet carefully to fully understand the process *)
2426
25- .. code-block :: python
26-
27- import pytest
28- from pyvo.utils import activate_features
29- from pyvo.dal import TAPService
30- from pyvo.mivot.utils.xml_utils import XmlUtils
31- from pyvo.mivot.utils.dict_utils import DictUtils
32- from pyvo.mivot.viewer.mivot_viewer import MivotViewer
33-
34- # Enable MIVOT-specific features in the pyvo library
35- activate_features(" MIVOT" )
36-
37- service = TAPService(' https://xcatdb.unistra.fr/xtapdb' )
38- result = service.run_sync(
39- """
40- SELECT TOP 5 * FROM "public".mergedentry
41- """ ,
42- format = " application/x-votable+xml;content=mivot"
43- )
44-
45- # The MIVOT viewer generates the model view of the data
46- m_viewer = MivotViewer(result, resolve_ref = True )
47-
48- # Print out the Mivot annotations read out of the VOtable
49- # This statement is just for a pedagogic purpose (access to a private attribute)
50- XmlUtils.pretty_print(m_viewer._mapping_block)
27+ .. doctest-skip ::
28+
29+ >>> from pyvo.utils import activate_features
30+ >>> from pyvo.dal import TAPService
31+ >>> from pyvo.mivot.utils.xml_utils import XmlUtils
32+ >>> from pyvo.mivot.viewer.mivot_viewer import MivotViewer
33+ >>>
34+ >>> # Enable MIVOT-specific features in the pyvo library
35+ >>> activate_features(" MIVOT" )
36+ >>>
37+ >>> service = TAPService(' https://xcatdb.unistra.fr/xtapdb' )
38+ >>> result = service.run_sync(
39+ ... """
40+ ... SELECT TOP 5 * FROM " public" .mergedentry
41+ ... """ ,
42+ ... format = " application/x-votable+xml;content=mivot"
43+ ... )
44+ >>>
45+ >>> # The MIVOT viewer generates the model view of the data
46+ >>> m_viewer = MivotViewer(result, resolve_ref = True )
47+ >>>
48+ >>> # Print out the Mivot annotations read out of the VOtable
49+ >>> # This statement is just for a pedagogic purpose (access to a private attribute)
50+ >>> XmlUtils.pretty_print(m_viewer._mapping_block)
5151
5252
5353In this first step we just queried the service and we built the object that will process the Mivot annotations.
@@ -63,48 +63,45 @@ The Mivot block printing output is too long to be listed here. However, the scre
6363 ``MangoObject `` instance which holds all the mapped properties.
6464
6565At instantiation time, the viewer reads the first data row, which must exist,
66- in order to construct a Python object that reflects the mapped model.
66+ in order to construct the Python objects that reflect the mapped models and
67+ to make the data available through them.
6768
68- .. code-block :: python
69+ .. doctest-skip ::
6970
70- # Build a Python object matching the TEMPLATES content and
71- # which leaves are set with the values of the first row
72- mango_object = m_viewer.dm_instance
73-
74- # Print out the content of the Python object
75- # This statement is just for a pedagogic purpose
76- DictUtils.print_pretty_json(mango_object.to_dict())
71+ >>> # Discover the Python objects matching the TEMPLATES content
72+ >>> for dm_instance in m_viewer.dm_instances;
73+ >>> print (dm_instance)
74+ <MivotInstance: dmtype="mango:MangoObject">
7775
7876The annotations are consumed by this dynamic Python object which leaves are set with the data of the current row.
79- You can explore the structure of this object by using the printed dictionary or standard object paths as shown below.
77+ You can explore the structure of this object by using standard object paths as shown below.
8078
8179Now, we can iterate through the table data and retrieve an updated Mivot instance for each row.
8280
83- .. code-block :: python
84-
85- while m_viewer.next_row_view():
86- if mango_object.dmtype == " mango:MangoObject" :
87- print (f " Read source { mango_object.identifier.value} { mango_object.dmtype} " )
88- for mango_property in mango_object.propertyDock:
89- if mango_property.dmtype == " mango:Brightness" :
90- if mango_property.value.value:
91- mag_value = mango_property.value.value
92- mag_error = mango_property.error.sigma.value
93- phot_cal = mango_property.photCal
94- spectral_location = phot_cal.photometryFilter.spectralLocation
95- mag_filter = phot_cal.identifier.value
96- spectral_location = phot_cal.photometryFilter.spectralLocation
97- mag_wl = spectral_location.value.value
98- sunit = spectral_location.unitexpression.value
99-
100- print (f " flux at { mag_wl} { sunit} (filter { mag_filter} ) is { mag_value:.2e } +/- { mag_error:.2e } " )
101-
102- Read source 4XMM J054329.3- 682106 mango:MangoObject
81+ .. doctest-skip ::
82+
83+ >>> mango_object = m_viewer.dm_instances[0 ]
84+ >>> while m_viewer.next_row_view():
85+ >>> if mango_object.dmtype == " mango:MangoObject" :
86+ >>> print (f " Read source { mango_object.identifier.value} { mango_object.dmtype} " )
87+ >>> for mango_property in mango_object.propertyDock:
88+ >>> if mango_property.dmtype == " mango:Brightness" :
89+ >>> if mango_property.value.value:
90+ >>> mag_value = mango_property.value.value
91+ >>> mag_error = mango_property.error.sigma.value
92+ >>> phot_cal = mango_property.photCal
93+ >>> spectral_location = phot_cal.photometryFilter.spectralLocation
94+ >>> mag_filter = phot_cal.identifier.value
95+ >>> spectral_location = phot_cal.photometryFilter.spectralLocation
96+ >>> mag_wl = spectral_location.value.value
97+ >>> sunit = spectral_location.unitexpression.value
98+ >>> print (f " flux at { mag_wl} { sunit} (filter { mag_filter} ) is { mag_value:.2e } +/- { mag_error:.2e } " )
99+ Read source 4XMM J054329.3-682106 mango:MangoObject
103100 flux at 0.35 keV (filter XMM/EPIC/EB1) is 8.35e-14 +/- 3.15e-14
104101 flux at 0.75 keV (filter XMM/EPIC/EB2) is 3.26e-15 +/- 5.45e-15
105102 flux at 6.1 keV (filter XMM/EPIC/EB8) is 8.68e-14 +/- 6.64e-14
106- ...
107- ...
103+ ...
104+ ...
108105
109106The same code can easily be connected with matplotlib to plot SEDs as shown below (code not provided).
110107
@@ -113,11 +110,11 @@ The same code can easily be connected with matplotlib to plot SEDs as shown belo
113110 :width: 500
114111 :alt: XMM SED
115112
116- It is to noted that the current table row keeps available through the Mivot viewer.
113+ It is to be noted that the current table row keeps available through the Mivot viewer.
117114
118- .. code-block :: python
115+ .. code-block :: python
119116
120- row = m_viewer.table_row
117+ row = m_viewer.table_row
121118
122119
123120 .. important ::
@@ -129,7 +126,7 @@ It is to noted that the current table row keeps available through the Mivot view
129126
130127 The same client code can be reused in many places with many datasets, provided they are annotated.
131128
132- EpochPosition property readout
129+ EpochPosition Property Readout
133130==============================
134131
135132This example is based on a VOtable resulting on a Vizier cone search.
@@ -138,58 +135,64 @@ which models a full source's astrometry at a given date.
138135
139136
140137.. warning ::
141- At the time of writing, Vizier only mapped positions and proper motions (when available),
138+ At the time of writing (Q1 2025) , Vizier only mapped positions and proper motions (when available),
142139 and the definitive epoch class had not been adopted.
143140 Therefore, this implementation may differ a little bit from the standard model.
144141
145142 Vizier does not wrap the source properties in a MANGO object,
146143 but rather lists them in the Mivot *TEMPLATES *.
147- The annotation reader must support both designs.
144+ The annotation reader supports both designs.
148145
149146In the first step below, we run a standard cone search query by using the standard PyVO API.
150-
151- .. code-block :: python
152-
153- import pytest
154- import astropy.units as u
155- from astropy.coordinates import SkyCoord
156- from pyvo.dal.scs import SCSService
157-
158- from pyvo.utils import activate_features
159- from pyvo.mivot.viewer.mivot_viewer import MivotViewer
160- from pyvo.mivot.features.sky_coord_builder import SkyCoordBuilder
161- from pyvo.mivot.utils.dict_utils import DictUtils
162-
163- # Enable MIVOT-specific features in the pyvo library
164- activate_features(" MIVOT" )
165-
166- scs_srv = SCSService(" https://vizier.cds.unistra.fr/viz-bin/conesearch/V1.5/I/239/hip_main" )
167-
168- query_result = scs_srv.search(
169- pos = SkyCoord(ra = 52.26708 * u.degree, dec = 59.94027 * u.degree, frame = ' icrs' ),
170- radius = 0.5 )
171-
172- # The MIVOt viewer generates the model view of the data
173- m_viewer = MivotViewer(query_result, resolve_ref = True )
174-
175147Once the query is finished, we can get a reference to the object that will process the Mivot annotations.
176148
177- .. code-block :: python
178-
179- # Build a Python object matching the TEMPLATES content and
180- # which leaves are set with the values of the first row
181- mango_property = m_viewer.dm_instance
182-
183- # Print out the content of the Python object
184- # This statement is just for a pedagogic purpose
185- DictUtils.print_pretty_json(mango_property.to_dict())
186-
187- The annotations are consumed by this dynamic Python object which leaves are set with the data of the current row.
188- You can explore the structure of this object by using standard object paths or by browsing the dictionary shown below.
189-
190- .. code-block :: json
191-
192- {
149+ .. doctest-skip ::
150+
151+ >>> import astropy.units as u
152+ >>> from astropy.coordinates import SkyCoord
153+ >>> from pyvo.dal.scs import SCSService
154+ >>> from pyvo.utils import activate_features
155+ >>> from pyvo.mivot.viewer.mivot_viewer import MivotViewer
156+ >>> from pyvo.mivot.features.sky_coord_builder import SkyCoordBuilder
157+ >>>
158+ >>> # Enable MIVOT-specific features in the pyvo library
159+ >>> activate_features(" MIVOT" )
160+ >>>
161+ >>> scs_srv = SCSService(" https://vizier.cds.unistra.fr/viz-bin/conesearch/V1.5/I/239/hip_main" )
162+ >>>
163+ >>> query_result = scs_srv.search(
164+ ... pos= SkyCoord(ra = 52.26708 * u.degree, dec = 59.94027 * u.degree, frame = ' icrs' ),
165+ ... radius= 0.5 )
166+ >>>
167+ >>> # The MIVOT viewer generates the model view of the data
168+ >>> m_viewer = MivotViewer(query_result, resolve_ref = True )
169+
170+ We can now discover which data model classes the data is mapped to.
171+
172+ .. doctest-skip ::
173+
174+ >>> # Get a set of Python objects matching the TEMPLATES content and
175+ >>> # which leaves are set with the values of the first row
176+ >>> for dm_instance in m_viewer.dm_instances;
177+ >>> print (dm_instance)
178+ <MivotInstance: dmtype="mango:EpochPosition">
179+
180+ The first instance can be accessed by the ``m_viewer.dm_instance `` getter.
181+ This is a simple shorcut aiming at simplifying the code.
182+
183+ .. doctest-skip ::
184+
185+ >>> dm_instance = m_viewer.dm_instance
186+ >>> print (dm_instance.dmtype)
187+ mango:EpochPosition
188+
189+ We can also provide a complete instance representation that includes all fields in the entire hierarchy.
190+
191+ .. doctest-skip ::
192+
193+ >>> # Print out the json serialization of the Python object
194+ >>> print (repr (dm_instance))
195+ {
193196 "dmtype": "mango:EpochPosition",
194197 "longitude": {
195198 "dmtype": "ivoa:RealQuantity",
@@ -231,22 +234,22 @@ You can explore the structure of this object by using standard object paths or b
231234 "spaceRefFrame": {
232235 "dmtype": "ivoa:string",
233236 "value": "ICRS"
234- }
235- }
236- }
237- }
238-
237+ }
238+ }
239+ }
240+ }
239241
240- The reader can transform ``EpochPosition `` instances into ``SkyCoord `` instances.
241- These can then be used for further scientific processing.
242+ The reader can transform ``EpochPosition `` instances into ``SkyCoord `` instances.
243+ These can then be used for further scientific processing.
242244
243- .. code-block :: python
245+ .. doctest-skip ::
244246
245- while m_viewer.next_row_view():
246- if mango_property.dmtype == " mango:EpochPosition" :
247- scb = SkyCoordBuilder(mango_property.to_dict())
248- # do whatever process with the SkyCoord object
249- print (scb.build_sky_coord())
247+ >>> while m_viewer.next_row_view():
248+ >>> mango_property = m_viewer.dm_instance
249+ >>> if mango_property.dmtype == " mango:EpochPosition" :
250+ >>> scb = SkyCoordBuilder(mango_property)
251+ >>> # do whatever process with the SkyCoord object
252+ >>> print (scb.build_sky_coord())
250253
251254.. important ::
252255 Similar to the previous example, this code can be used with any VOTable with data mapped to MANGO.
@@ -255,5 +258,31 @@ You can explore the structure of this object by using standard object paths or b
255258 It avoids the need for users to build SkyCoord objects by hand from VOTable fields,
256259 which is never an easy task.
257260
261+ Homework
262+ ========
263+
264+ Simbad has released (Q3 2025) an annotated version of its Cone Search.
265+ It's a good case to exercise this API.
266+
267+
268+ .. code-block :: python
269+
270+ SERVER = " https://simbad.cds.unistra.fr/cone?"
271+ VERB = 2
272+ RA = 269.452076 * u.degree
273+ DEC = 4.6933649 * u.degree
274+ SR = 0.1 * u.degree
275+ MAXREC = 100
276+ RESPONSEFORMAT = " mivot"
277+
278+ scs_srv = SCSService(SERVER )
279+
280+ query_result = scs_srv.search(
281+ pos = SkyCoord(ra = RA , dec = DEC , frame = ' icrs' ),
282+ radius = SR ,
283+ verbosity = VERB ,
284+ RESPONSEFORMAT = RESPONSEFORMAT ,
285+ MAXREC = MAXREC )
286+
258287
259- The next section provides some tips to use the API documented in the :ref: `annoter page <mivot-annoter >`.
288+ * The next section provides some tips to use the API documented in the * :ref: `annoter page <mivot-annoter >`.
0 commit comments