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

Source Code for Module gavo.protocols.scs

  1  """ 
  2  IVOA cone search: Helper functions, a core, and misc. 
  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 gavo import base 
 12  from gavo import svcs 
 13  from gavo.protocols import simbadinterface  #noflake: for registration 
 14  from gavo.svcs import outputdef 
 15   
 16   
17 -def findNClosest(alpha, delta, tableDef, n, fields, searchRadius=5):
18 """returns the n objects closest around alpha, delta in table. 19 20 n is the number of items returned, with the closest ones at the 21 top, fields is a sequence of desired field names, searchRadius 22 is a radius for the initial q3c search and will need to be 23 lowered for dense catalogues and possibly raised for sparse ones. 24 25 The last item of each row is the distance of the object from 26 the query center in degrees. 27 28 The query depends on postgastro extension (and should be changed to 29 use pgsphere). It also requires the q3c extension. 30 """ 31 with base.AdhocQuerier(base.getTableConn) as q: 32 raField = tableDef.getColumnByUCDs("pos.eq.ra;meta.main", 33 "POS_EQ_RA_MAIN").name 34 decField = tableDef.getColumnByUCDs("pos.eq.dec;meta.main", 35 "POS_EQ_RA_MAIN").name 36 res = list(q.query("SELECT %s," 37 " celDistDD(%s, %s, %%(alpha)s, %%(delta)s) as dist_" 38 " FROM %s WHERE" 39 " q3c_radial_query(%s, %s, %%(alpha)s, %%(delta)s," 40 " %%(searchRadius)s)" 41 " ORDER BY dist_ LIMIT %%(n)s"% 42 (",".join(fields), raField, decField, tableDef.getQName(), 43 raField, decField), 44 locals())) 45 return res
46 47
48 -def parseHumanSpoint(cooSpec, colName=None):
49 """tries to interpret cooSpec as some sort of cone center. 50 51 Attempted interpretations include various forms of coordinate pairs 52 and simbad objects; hence, this will in general cause network traffic. 53 54 If no sense can be made, a ValidationError on colName is raised. 55 """ 56 try: 57 cooPair = base.parseCooPair(cooSpec) 58 except ValueError: 59 simbadData = base.caches.getSesame("web").query(cooSpec) 60 if not simbadData: 61 raise base.ValidationError("%s is neither a RA,DEC" 62 " pair nor a simbad resolvable object."%cooSpec, colName) 63 cooPair = simbadData["RA"], simbadData["dec"] 64 return cooPair
65 66
67 -def getConeColumns(td):
68 """returns the columns the cone search will use as positions in a 69 tableDef. 70 71 This will raise an error if these are not present or not unique. 72 Both new-style and old-style UCDs are accepted. 73 """ 74 raColumn = td.getColumnByUCDs( 75 "pos.eq.ra;meta.main", "POS_EQ_RA_MAIN") 76 decColumn = td.getColumnByUCDs( 77 "pos.eq.dec;meta.main", "POS_EQ_DEC_MAIN") 78 return raColumn, decColumn
79 80
81 -class SCSCore(svcs.DBCore):
82 """A core performing cone searches. 83 84 This will, if it finds input parameters it can make out a position from, 85 add a _r column giving the distance between the match center and 86 the columns that a cone search will match against. 87 88 If any of the conditions for adding _r aren't met, this will silently 89 degrade to a plain DBCore. 90 91 You will almost certainly want a:: 92 93 <FEED source="//scs#coreDescs"/> 94 95 in the body of this (in addition to whatever other custom conditions 96 you may have). 97 """ 98 name_ = "scsCore" 99
100 - def onElementComplete(self):
101 self._onElementCompleteNext(SCSCore) 102 # raColumn and decColumn must be from the queriedTable (rather than 103 # the outputTable, as it would be preferable), since we're using 104 # them to build database queries. 105 self.raColumn, self.decColumn = getConeColumns(self.queriedTable) 106 try: 107 self.idColumn = self.outputTable.getColumnByUCDs( 108 "meta.id;meta.main", "ID_MAIN") 109 except ValueError: 110 base.ui.notifyWarning("SCS core at %s: Output table has no" 111 " meta.id;meta.main column. This service will be invalid."% 112 self.getSourcePosition()) 113 114 self.distCol = base.resolveCrossId("//scs#distCol") 115 self.outputTable = self.outputTable.change( 116 columns=[self.distCol]+self.outputTable.columns) 117 118 if not self.hasProperty("defaultSortKey"): 119 self.setProperty("defaultSortKey", self.distCol.name)
120
121 - def _guessDestPos(self, inputTable):
122 """returns RA and Dec for a cone search possibly contained in inputTable. 123 124 If no positional query is discernable, this returns None. 125 """ 126 pars = inputTable.getParamDict() 127 if pars.get("RA") is not None and pars.get("DEC") is not None: 128 return pars["RA"], pars["DEC"] 129 elif pars.get("hscs_pos") is not None: 130 try: 131 return parseHumanSpoint(pars["hscs_pos"]) 132 except ValueError: 133 # We do not want to fail for this fairly unimportant thing. 134 # If the core actually needs the position, it should fail itself. 135 return None 136 else: 137 return None
138
139 - def _getDistColumn(self, destPos):
140 """returns an outputField selecting the distance of the match 141 object to the cone center. 142 """ 143 if destPos is None: 144 select = "NULL" 145 else: 146 select = "degrees(spoint(radians(%s), radians(%s)) <-> %s)"%( 147 self.raColumn.name, self.decColumn.name, 148 "spoint '(%fd,%fd)'"%destPos) 149 150 return self.distCol.change(select=select)
151
152 - def _fixupQueryColumns(self, destPos, baseColumns):
153 """returns the output columns from baseColumns for a query 154 centered at destPos. 155 156 In particular, the _r column is primed so it yields the right result 157 if destPos is given. 158 """ 159 res = [] 160 for col in baseColumns: 161 if col.name=="_r": 162 res.append(self._getDistColumn(destPos)) 163 else: 164 res.append(col) 165 return res
166
167 - def _makeResultTableDef(self, service, inputTable, queryMeta):
168 destPos = self._guessDestPos(inputTable) 169 170 outCols = self._fixupQueryColumns(destPos, 171 self.getQueryCols(service, queryMeta)) 172 173 return base.makeStruct(outputdef.OutputTableDef, 174 parent_=self.queriedTable.parent, 175 id="result", 176 onDisk=False, 177 columns=outCols, 178 params=self.queriedTable.params)
179