Package gavo :: Package protocols :: Module simbadinterface
[frames] | no frames]

Source Code for Module gavo.protocols.simbadinterface

  1  """ 
  2  A caching proxy for CDS' Simbad object resolver. 
  3  """ 
  4   
  5  #c Copyright 2008-2019, the GAVO project 
  6  #c 
  7  #c This program is free software, covered by the GNU GPL.  See the 
  8  #c COPYING file in the source distribution. 
  9   
 10   
 11  from __future__ import print_function 
 12   
 13  import cPickle 
 14  import os 
 15  import socket 
 16  import tempfile 
 17  import urllib 
 18   
 19  from gavo import base 
 20  from gavo import utils 
 21  from gavo.utils import ElementTree 
22 23 24 # TODO: use http://vizier.cfa.harvard.edu/viz-bin/nph-sesame as fallback 25 26 -class ObjectCache(object):
27 - def __init__(self, id):
28 self.id = id 29 self._loadCache()
30
31 - def _getCacheName(self):
32 return os.path.join(base.getConfig("cacheDir"), "oc"+self.id)
33
34 - def _loadCache(self):
35 try: 36 self.cache = cPickle.load(open(self._getCacheName())) 37 except IOError: 38 self.cache = {}
39
40 - def _saveCache(self, silent=False):
41 try: 42 handle, name = tempfile.mkstemp(dir=base.getConfig("cacheDir")) 43 f = os.fdopen(handle, "w") 44 cPickle.dump(self.cache, f) 45 utils.safeclose(f) 46 os.rename(name, self._getCacheName()) 47 except (IOError, os.error): 48 if not silent: 49 raise
50
51 - def addItem(self, key, record, save, silent=False):
52 self.cache[key] = record 53 if save: 54 self._saveCache(silent)
55
56 - def sync(self):
57 self._saveCache(silent=True)
58
59 - def getItem(self, key):
60 return self.cache[key]
61
62 63 -class Sesame(object):
64 """is a simple interface to the simbad name resolver. 65 """ 66 SVC_URL = "http://cdsweb.u-strasbg.fr/cgi-bin/nph-sesame/-ox/SN?" 67
68 - def __init__(self, id="simbad", debug=False, saveNew=False):
69 self.saveNew = saveNew 70 self.debug = debug 71 self._getCache(id)
72
73 - def _getCache(self, id):
74 self.cache = ObjectCache(id)
75
76 - def _parseXML(self, simbadXML):
77 try: 78 et = ElementTree.fromstring(simbadXML) 79 except Exception as msg: # simbad returned weird XML 80 base.ui.notifyWarning("Bad XML from simbad (%s)"%str(msg)) 81 return None 82 83 res = {} 84 nameMatch = et.find("Target/name") 85 if nameMatch is None: 86 # no such object, return a negative 87 return None 88 89 res["oname"] = nameMatch.text 90 firstResponse = et.find("Target/Resolver") 91 if not firstResponse: 92 return None 93 94 res["otype"] = getattr(firstResponse.find("otype"), "text", None) 95 try: 96 res["RA"] = float(firstResponse.find("jradeg").text) 97 res["dec"] = float(firstResponse.find("jdedeg").text) 98 except (ValueError, AttributeError): 99 # presumably null position 100 return None 101 return res
102
103 - def query(self, ident):
104 try: 105 return self.cache.getItem(ident) 106 except KeyError: 107 try: 108 f = urllib.urlopen(self.SVC_URL+urllib.quote(ident)) 109 response = f.read() 110 f.close() 111 112 newOb = self._parseXML(response) 113 self.cache.addItem(ident, newOb, save=self.saveNew) 114 return newOb 115 except socket.error: # Simbad is offline 116 raise base.ui.logOldExc(base.ValidationError( 117 "Simbad is offline, cannot query.", 118 "hscs_pos", # really, this should be added by the widget 119 hint="If this problem persists, complain to us rather than simbad."))
120
121 - def getPositionFor(self, identifier):
122 data = self.query(identifier) 123 if not data: 124 raise KeyError(identifier) 125 return float(data["RA"]), float(data["dec"])
126
127 128 -def getSimbadPositions(identifier):
129 """returns ra and dec from Simbad for identifier. 130 131 It raises a KeyError if Simbad doesn't know identifier. 132 """ 133 return base.caches.getSesame("simbad").getPositionFor(identifier)
134 135 136 base.caches.makeCache("getSesame", lambda key: Sesame(key, saveNew=True)) 137 138 139 ############## ADQL ufunc 140 141 from gavo import adql 142 143 @adql.userFunction("gavo_simbadpoint", 144 "(identifier TEXT) -> POINT", 145 """ 146 gavo_simbadpoint queries simbad for an identifier and returns the 147 corresponding point. Note that identifier can only be a literal, 148 i.e., as simple string rather than a column name. This is because 149 our database cannot query simbad, and we probably wouldn't want 150 to fire off millions of simbad queries anyway; use simbad's own 151 TAP service for this kind of applications. 152 """, 153 "point", ucd="pos.eq;src")
154 -def _simbadpoint(args):
155 if len(args)!=1 or args[0].type!="characterStringLiteral": 156 raise adql.UfuncError( 157 "gavo_simbadpoint takes exactly one string literal as argument") 158 159 object = args[0].value 160 161 resolver = base.caches.getSesame("web") 162 try: 163 alpha, delta = resolver.getPositionFor(object) 164 except KeyError: 165 raise adql.UfuncError("No simbad position for '%s'"%object) 166 167 return "spoint(RADIANS(%f), RADIANS(%f))"%(alpha, delta)
168 169 170 if __name__=="__main__": 171 s = Sesame(debug=True) 172 print(s.query("M 33")) 173