1 """
2 IVOA cone search: Helper functions, a core, and misc.
3 """
4
5
6
7
8
9
10
11 from gavo import base
12 from gavo import svcs
13 from gavo.protocols import simbadinterface
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
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
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
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
101 self._onElementCompleteNext(SCSCore)
102
103
104
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
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
134
135 return None
136 else:
137 return None
138
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
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
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