1
2 """module for coordinate manipulation (conversions, calculations etc.)
3
4 (c) 2007-2011 Matt Hilton
5
6 U{http://astlib.sourceforge.net}
7
8 """
9
10 import sys
11 import math
12 import numpy
13 from PyWCSTools import wcscon
14
15
17 """Converts a delimited string of Hours:Minutes:Seconds format into decimal degrees.
18
19 @type RAString: string
20 @param RAString: coordinate string in H:M:S format
21 @type delimiter: string
22 @param delimiter: delimiter character in RAString
23 @rtype: float
24 @return: coordinate in decimal degrees
25
26 """
27
28 if delimiter=="":
29 RABits=str(RAString).split()
30 else:
31 RABits=str(RAString).split(delimiter)
32 if len(RABits)>1:
33 RAHDecimal=float(RABits[0])
34 if len(RABits)>1:
35 RAHDecimal=RAHDecimal+(float(RABits[1])/60.0)
36 if len(RABits)>2:
37 RAHDecimal=RAHDecimal+(float(RABits[2])/3600.0)
38 RADeg=(RAHDecimal/24.0)*360.0
39 else:
40 RADeg=float(RAString)
41
42 return RADeg
43
44
46 """Converts a delimited string of Degrees:Minutes:Seconds format into decimal degrees.
47
48 @type decString: string
49 @param decString: coordinate string in D:M:S format
50 @type delimiter: string
51 @param delimiter: delimiter character in decString
52 @rtype: float
53 @return: coordinate in decimal degrees
54
55 """
56
57 if delimiter=="":
58 decBits=str(decString).split()
59 else:
60 decBits=str(decString).split(delimiter)
61 if len(decBits)>1:
62 decDeg=float(decBits[0])
63 if decBits[0].find("-")!=-1:
64 if len(decBits)>1:
65 decDeg=decDeg-(float(decBits[1])/60.0)
66 if len(decBits)>2:
67 decDeg=decDeg-(float(decBits[2])/3600.0)
68 else:
69 if len(decBits)>1:
70 decDeg=decDeg+(float(decBits[1])/60.0)
71 if len(decBits)>2:
72 decDeg=decDeg+(float(decBits[2])/3600.0)
73 else:
74 decDeg=float(decString)
75
76 return decDeg
77
78
80 """Converts decimal degrees to string in Hours:Minutes:Seconds format with user specified
81 delimiter.
82
83 @type RADeg: float
84 @param RADeg: coordinate in decimal degrees
85 @type delimiter: string
86 @param delimiter: delimiter character in returned string
87 @rtype: string
88 @return: coordinate string in H:M:S format
89
90
91 """
92 hours=(RADeg/360.0)*24
93 if hours<10 and hours>=1:
94 sHours="0"+str(hours)[0]
95 elif hours>=10:
96 sHours=str(hours)[:2]
97 elif hours<1:
98 sHours="00"
99
100 if str(hours).find(".")==-1:
101 mins=float(hours)*60.0
102 else:
103 mins=float(str(hours)[str(hours).index("."):])*60.0
104 if mins<10 and mins>=1:
105 sMins="0"+str(mins)[:1]
106 elif mins>=10:
107 sMins=str(mins)[:2]
108 elif mins<1:
109 sMins="00"
110
111 secs=(hours-(float(sHours)+float(sMins)/60.0))*3600.0
112 if secs<10 and secs>0.001:
113 sSecs="0"+str(secs)[:str(secs).find(".")+4]
114 elif secs<0.0001:
115 sSecs="00.001"
116 else:
117 sSecs=str(secs)[:str(secs).find(".")+4]
118 if len(sSecs)<5:
119 sSecs=sSecs+"00"
120
121 if float(sSecs) == 60.000:
122 sSecs="00.00"
123 sMins=str(int(sMins)+1)
124 if int(sMins) == 60:
125 sMins="00"
126 sDeg=str(int(sDeg)+1)
127
128 return sHours+delimiter+sMins+delimiter+sSecs
129
130
132 """Converts decimal degrees to string in Degrees:Minutes:Seconds format with user specified
133 delimiter.
134
135 @type decDeg: float
136 @param decDeg: coordinate in decimal degrees
137 @type delimiter: string
138 @param delimiter: delimiter character in returned string
139 @rtype: string
140 @return: coordinate string in D:M:S format
141
142 """
143
144 if decDeg>0:
145 if decDeg<10 and decDeg>=1:
146 sDeg="0"+str(decDeg)[0]
147 elif decDeg>=10:
148 sDeg=str(decDeg)[:2]
149 elif decDeg<1:
150 sDeg="00"
151
152 if str(decDeg).find(".")==-1:
153 mins=float(decDeg)*60.0
154 else:
155 mins=float(str(decDeg)[str(decDeg).index("."):])*60
156 if mins<10 and mins>=1:
157 sMins="0"+str(mins)[:1]
158 elif mins>=10:
159 sMins=str(mins)[:2]
160 elif mins<1:
161 sMins="00"
162
163 secs=(decDeg-(float(sDeg)+float(sMins)/60.0))*3600.0
164 if secs<10 and secs>0:
165 sSecs="0"+str(secs)[:str(secs).find(".")+3]
166 elif secs<0.001:
167 sSecs="00.00"
168 else:
169 sSecs=str(secs)[:str(secs).find(".")+3]
170 if len(sSecs)<5:
171 sSecs=sSecs+"0"
172
173 if float(sSecs) == 60.00:
174 sSecs="00.00"
175 sMins=str(int(sMins)+1)
176 if int(sMins) == 60:
177 sMins="00"
178 sDeg=str(int(sDeg)+1)
179
180 return "+"+sDeg+delimiter+sMins+delimiter+sSecs
181
182 else:
183 if decDeg>-10 and decDeg<=-1:
184 sDeg="-0"+str(decDeg)[1]
185 elif decDeg<=-10:
186 sDeg=str(decDeg)[:3]
187 elif decDeg>-1:
188 sDeg="-00"
189
190 if str(decDeg).find(".")==-1:
191 mins=float(decDeg)*-60.0
192 else:
193 mins=float(str(decDeg)[str(decDeg).index("."):])*60
194 if mins<10 and mins>=1:
195 sMins="0"+str(mins)[:1]
196 elif mins>=10:
197 sMins=str(mins)[:2]
198 elif mins<1:
199 sMins="00"
200
201 secs=(decDeg-(float(sDeg)-float(sMins)/60.0))*3600.0
202 if secs>-10 and secs<0:
203 sSecs="0"+str(secs)[1:str(secs).find(".")+3]
204 elif secs>-0.001:
205 sSecs="00.00"
206 else:
207 sSecs=str(secs)[1:str(secs).find(".")+3]
208 if len(sSecs)<5:
209 sSecs=sSecs+"0"
210
211 if float(sSecs) == 60.00:
212 sSecs="00.00"
213 sMins=str(int(sMins)+1)
214 if int(sMins) == 60:
215 sMins="00"
216 sDeg=str(int(sDeg)-1)
217
218 return sDeg+delimiter+sMins+delimiter+sSecs
219
220
222 """Calculates the angular separation of two positions on the sky (specified in decimal
223 degrees) in decimal degrees, assuming a tangent plane projection (so separation has to be
224 <90 deg). Note that RADeg2, decDeg2 can be numpy arrays.
225
226 @type RADeg1: float
227 @param RADeg1: R.A. in decimal degrees for position 1
228 @type decDeg1: float
229 @param decDeg1: dec. in decimal degrees for position 1
230 @type RADeg2: float or numpy array
231 @param RADeg2: R.A. in decimal degrees for position 2
232 @type decDeg2: float or numpy array
233 @param decDeg2: dec. in decimal degrees for position 2
234 @rtype: float or numpy array, depending upon type of RADeg2, decDeg2
235 @return: angular separation in decimal degrees
236
237 """
238 cRA=numpy.radians(RADeg1)
239 cDec=numpy.radians(decDeg1)
240
241 gRA=numpy.radians(RADeg2)
242 gDec=numpy.radians(decDeg2)
243
244 dRA=cRA-gRA
245 dDec=gDec-cDec
246 cosC=(numpy.sin(gDec)*numpy.sin(cDec))+(numpy.cos(gDec)*numpy.cos(cDec)*numpy.cos(gRA-cRA))
247 x=(numpy.cos(cDec)*numpy.sin(gRA-cRA))/cosC
248 y=((numpy.cos(gDec)*numpy.sin(cDec))-(numpy.sin(gDec)*numpy.cos(cDec)*numpy.cos(gRA-cRA)))/cosC
249 r=numpy.degrees(numpy.sqrt(x*x+y*y))
250
251 return r
252
253
254 -def convertCoords(inputSystem, outputSystem, coordX, coordY, epoch):
255 """Converts specified coordinates (given in decimal degrees) between J2000, B1950, and
256 Galactic.
257
258 @type inputSystem: string
259 @param inputSystem: system of the input coordinates (either "J2000", "B1950" or "GALACTIC")
260 @type outputSystem: string
261 @param outputSystem: system of the returned coordinates (either "J2000", "B1950" or
262 "GALACTIC")
263 @type coordX: float
264 @param coordX: longitude coordinate in decimal degrees, e.g. R. A.
265 @type coordY: float
266 @param coordY: latitude coordinate in decimal degrees, e.g. dec.
267 @type epoch: float
268 @param epoch: epoch of the input coordinates
269 @rtype: list
270 @return: coordinates in decimal degrees in requested output system
271
272 """
273
274 if inputSystem=="J2000" or inputSystem=="B1950" or inputSystem=="GALACTIC":
275 if outputSystem=="J2000" or outputSystem=="B1950" or outputSystem=="GALACTIC":
276
277 outCoords=wcscon.wcscon(wcscon.wcscsys(inputSystem),
278 wcscon.wcscsys(outputSystem), 0, 0, coordX, coordY, epoch)
279
280 return outCoords
281
282 raise Exception, "inputSystem and outputSystem must be 'J2000', 'B1950' or 'GALACTIC'"
283
284
286 """Calculates minimum and maximum RA, dec coords needed to define a box enclosing a circle of
287 radius radiusSkyDeg around the given RADeg, decDeg coordinates. Useful for freeform queries
288 of e.g. SDSS, UKIDSS etc.. Uses L{calcAngSepDeg}, so has the same limitations.
289
290 @type RADeg: float
291 @param RADeg: RA coordinate of centre of search region
292 @type decDeg: float
293 @param decDeg: dec coordinate of centre of search region
294 @type radiusSkyDeg: float
295 @param radiusSkyDeg: radius in degrees on the sky used to define search region
296 @rtype: list
297 @return: [RAMin, RAMax, decMin, decMax] - coordinates in decimal degrees defining search box
298
299 """
300
301 tolerance=1e-5
302 targetHalfSizeSkyDeg=radiusSkyDeg
303 funcCalls=["calcAngSepDeg(RADeg, decDeg, guess, decDeg)",
304 "calcAngSepDeg(RADeg, decDeg, guess, decDeg)",
305 "calcAngSepDeg(RADeg, decDeg, RADeg, guess)",
306 "calcAngSepDeg(RADeg, decDeg, RADeg, guess)"]
307 coords=[RADeg, RADeg, decDeg, decDeg]
308 signs=[1.0, -1.0, 1.0, -1.0]
309 results=[]
310 for f, c, sign in zip(funcCalls, coords, signs):
311
312 maxGuess=sign*targetHalfSizeSkyDeg*2.0
313 minGuess=sign*targetHalfSizeSkyDeg/10.0
314 guessStep=(maxGuess-minGuess)/10.0
315 guesses=numpy.arange(minGuess+c, maxGuess+c, guessStep)
316 for i in range(20):
317 minSizeDiff=1e6
318 bestGuess=None
319 for guess in guesses:
320 sizeDiff=abs(eval(f)-targetHalfSizeSkyDeg)
321 if sizeDiff < minSizeDiff:
322 minSizeDiff=sizeDiff
323 bestGuess=guess
324 if minSizeDiff < tolerance:
325 break
326 else:
327 guessRange=abs((maxGuess-minGuess))
328 maxGuess=bestGuess+guessRange/4.0
329 minGuess=bestGuess-guessRange/4.0
330 guessStep=(maxGuess-minGuess)/10.0
331 guesses=numpy.arange(minGuess, maxGuess, guessStep)
332 results.append(bestGuess)
333 RAMax=results[0]
334 RAMin=results[1]
335 decMax=results[2]
336 decMin=results[3]
337
338 return [RAMin, RAMax, decMin, decMax]
339