.. doctest-skip-all .. _vo-samp-example-table-image: Sending and Receiving Tables and Images over SAMP ************************************************* In the following examples, we make use of: * `TOPCAT `_, which is a tool to explore tabular data. * `SAO DS9 `_, which is an image visualization tool that can overplot catalogs. * `Aladin Desktop `_, which is another tool that can visualize images and catalogs. TOPCAT and Aladin will run a SAMP Hub if none is found, so for the following examples you can either start up one of these applications first, or you can start up the `astropy.samp` hub. You can start this using the following command:: $ samp_hub Sending a Table to TOPCAT and DS9 ================================= The easiest way to send a VO table to TOPCAT is to make use of the |SAMPIntegratedClient| class. Once TOPCAT is open, first instantiate a |SAMPIntegratedClient| instance and then connect to the hub:: >>> from astropy.samp import SAMPIntegratedClient >>> client = SAMPIntegratedClient() >>> client.connect() Next, we have to set up a dictionary that contains details about the table to send. This should include ``url``, which is the URL to the file, and ``name``, which is a human-readable name for the table. The URL can be a local URL (starting with ``file:///``):: >>> params = {} >>> params["url"] = 'file:///Users/tom/Desktop/aj285677t3_votable.xml' >>> params["name"] = "Robitaille et al. (2008), Table 3" .. note:: To construct a local URL, you can also make use of ``urlparse`` as follows:: >>> import urlparse >>> params["url"] = urlparse.urljoin('file:', os.path.abspath("aj285677t3_votable.xml")) Now we can set up the message itself. This includes the type of message (here we use ``table.load.votable``, which indicates that a VO table should be loaded and the details of the table that we set above):: >>> message = {} >>> message["samp.mtype"] = "table.load.votable" >>> message["samp.params"] = params Finally, we can broadcast this to all clients that are listening for ``table.load.votable`` messages using :meth:`~astropy.samp.integrated_client.SAMPIntegratedClient.notify_all`:: >>> client.notify_all(message) The above message will actually be broadcast to all applications connected via SAMP. For example, if we open `SAO DS9 `_ in addition to TOPCAT, and we run the above command, both applications will load the table. We can use the :meth:`~astropy.samp.integrated_client.SAMPIntegratedClient.get_registered_clients` method to find all of the clients connected to the hub:: >>> client.get_registered_clients() ['hub', 'c1', 'c2'] These IDs do not mean much, but we can find out more using:: >>> client.get_metadata('c1') {'author.affiliation': 'Astrophysics Group, Bristol University', 'author.email': 'm.b.taylor@bristol.ac.uk', 'author.name': 'Mark Taylor', 'home.page': 'http://www.starlink.ac.uk/topcat/', 'samp.description.text': 'Tool for OPerations on Catalogues And Tables', 'samp.documentation.url': 'http://127.0.0.1:2525/doc/sun253/index.html', 'samp.icon.url': 'http://127.0.0.1:2525/doc/images/tc_sok.gif', 'samp.name': 'topcat', 'topcat.version': '4.0-1'} We can see that ``c1`` is the TOPCAT client. We can now resend the data, but this time only to TOPCAT, using the :meth:`~astropy.samp.integrated_client.SAMPIntegratedClient.notify` method:: >>> client.notify('c1', message) Once finished, we should make sure we disconnect from the hub:: >>> client.disconnect() Receiving a Table from TOPCAT ============================= To receive a table from TOPCAT, we have to set up a client that listens for messages from the hub. As before, we instantiate a |SAMPIntegratedClient| instance and connect to the hub:: >>> from astropy.samp import SAMPIntegratedClient >>> client = SAMPIntegratedClient() >>> client.connect() We now set up a receiver class which will handle any received messages. We need to take care to write handlers for both notifications and calls (the difference between the two being that calls expect a reply):: >>> class Receiver(object): ... def __init__(self, client): ... self.client = client ... self.received = False ... def receive_call(self, private_key, sender_id, msg_id, mtype, params, extra): ... self.params = params ... self.received = True ... self.client.reply(msg_id, {"samp.status": "samp.ok", "samp.result": {}}) ... def receive_notification(self, private_key, sender_id, mtype, params, extra): ... self.params = params ... self.received = True And we instantiate it: >>> r = Receiver(client) We can now use the :meth:`~astropy.samp.integrated_client.SAMPIntegratedClient.bind_receive_call` and :meth:`~astropy.samp.integrated_client.SAMPIntegratedClient.bind_receive_notification` methods to tell our receiver to listen to all ``table.load.votable`` messages:: >>> client.bind_receive_call("table.load.votable", r.receive_call) >>> client.bind_receive_notification("table.load.votable", r.receive_notification) We can now check that the message has not been received yet:: >>> r.received False We can now broadcast the table from TOPCAT. After a few seconds, we can check again if the message has been received:: >>> r.received True Success! The table URL should now be available in ``r.params['url']``, so we can do:: >>> from astropy.table import Table >>> t = Table.read(r.params['url']) Downloading http://127.0.0.1:2525/dynamic/4/t12.vot [Done] >>> t col1 col2 col3 col4 col5 col6 col7 col8 col9 col10 ------------------------- -------- ------- -------- -------- ----- ---- ----- ---- ----- SSTGLMC G000.0046+01.1431 0.0046 1.1432 265.2992 -28.3321 6.67 5.04 6.89 5.22 N SSTGLMC G000.0106-00.7315 0.0106 -0.7314 267.1274 -29.3063 7.18 6.07 nan 5.17 Y SSTGLMC G000.0110-01.0237 0.0110 -1.0236 267.4151 -29.4564 8.32 6.30 8.34 6.32 N ... As before, we should remember to disconnect from the hub once we are done:: >>> client.disconnect() Example ======= .. EXAMPLE START Receiving and Reading a Table over SAMP The following is a full example of a script that can be used to receive and read a table. It includes a loop that waits until the message is received, and reads the table once it has:: import time from astropy.samp import SAMPIntegratedClient from astropy.table import Table # Instantiate the client and connect to the hub client=SAMPIntegratedClient() client.connect() # Set up a receiver class class Receiver(object): def __init__(self, client): self.client = client self.received = False def receive_call(self, private_key, sender_id, msg_id, mtype, params, extra): self.params = params self.received = True self.client.reply(msg_id, {"samp.status": "samp.ok", "samp.result": {}}) def receive_notification(self, private_key, sender_id, mtype, params, extra): self.params = params self.received = True # Instantiate the receiver r = Receiver(client) # Listen for any instructions to load a table client.bind_receive_call("table.load.votable", r.receive_call) client.bind_receive_notification("table.load.votable", r.receive_notification) # We now run the loop to wait for the message in a try/finally block so that if # the program is interrupted e.g. by control-C, the client terminates # gracefully. try: # We test every 0.1s to see if the hub has sent a message while True: time.sleep(0.1) if r.received: t = Table.read(r.params['url']) break finally: client.disconnect() # Print out table print t .. EXAMPLE END Sending an Image to DS9 and Aladin ================================== As for tables, the most convenient way to send a FITS image over SAMP is to make use of the |SAMPIntegratedClient| class. Once Aladin or DS9 are open, first instantiate a |SAMPIntegratedClient| instance and then connect to the hub as before:: >>> from astropy.samp import SAMPIntegratedClient >>> client = SAMPIntegratedClient() >>> client.connect() Next, we have to set up a dictionary that contains details about the image to send. This should include ``url``, which is the URL to the file, and ``name``, which is a human-readable name for the table. The URL can be a local URL (starting with ``file:///``):: >>> params = {} >>> params["url"] = 'file:///Users/tom/Desktop/MSX_E.fits' >>> params["name"] = "MSX Band E Image of the Galactic Center" See `Sending a Table to TOPCAT and DS9`_ for an example of a recommended way to construct local URLs. Now we can set up the message itself. This includes the type of message (here we use ``image.load.fits`` which indicates that a FITS image should be loaded, and the details of the table that we set above):: >>> message = {} >>> message["samp.mtype"] = "image.load.fits" >>> message["samp.params"] = params Finally, we can broadcast this to all clients that are listening for ``table.load.votable`` messages:: >>> client.notify_all(message) As for `Sending a Table to TOPCAT and DS9`_, the :meth:`~astropy.samp.integrated_client.SAMPIntegratedClient.notify_all` method will broadcast the image to all listening clients, and for tables it is possible to instead use the :meth:`~astropy.samp.integrated_client.SAMPIntegratedClient.notify` method to send it to a specific client. Once finished, we should make sure we disconnect from the hub:: >>> client.disconnect() Receiving a Table from DS9 or Aladin ==================================== Receiving images over SAMP is identical to `Receiving a Table from TOPCAT`_, with the exception that the message type should be ``image.load.fits`` instead of ``table.load.votable``. Once the URL has been received, the FITS image can be opened with:: >>> from astropy.io import fits >>> fits.open(r.params['url'])