| 1 |
97d86a8f
|
Alessandro_N
|
"""module for coordinate manipulation (conversions, calculations etc.)
|
| 2 |
|
|
|
| 3 |
|
|
(c) 2007-2012 Matt Hilton
|
| 4 |
|
|
|
| 5 |
|
|
(c) 2013 Matt Hilton & Steven Boada
|
| 6 |
|
|
|
| 7 |
|
|
U{http://astlib.sourceforge.net}
|
| 8 |
|
|
|
| 9 |
|
|
"""
|
| 10 |
|
|
|
| 11 |
|
|
import sys
|
| 12 |
|
|
import math
|
| 13 |
|
|
import numpy
|
| 14 |
|
|
import string
|
| 15 |
|
|
from PyWCSTools import wcscon
|
| 16 |
|
|
|
| 17 |
|
|
|
| 18 |
|
|
def hms2decimal(RAString, delimiter):
|
| 19 |
|
|
"""Converts a delimited string of Hours:Minutes:Seconds format into decimal
|
| 20 |
|
|
degrees.
|
| 21 |
|
|
|
| 22 |
|
|
@type RAString: string
|
| 23 |
|
|
@param RAString: coordinate string in H:M:S format
|
| 24 |
|
|
@type delimiter: string
|
| 25 |
|
|
@param delimiter: delimiter character in RAString
|
| 26 |
|
|
@rtype: float
|
| 27 |
|
|
@return: coordinate in decimal degrees
|
| 28 |
|
|
|
| 29 |
|
|
"""
|
| 30 |
|
|
|
| 31 |
|
|
if delimiter == "":
|
| 32 |
|
|
RABits = str(RAString).split()
|
| 33 |
|
|
else:
|
| 34 |
|
|
RABits = str(RAString).split(delimiter)
|
| 35 |
|
|
if len(RABits) > 1:
|
| 36 |
|
|
RAHDecimal = float(RABits[0])
|
| 37 |
|
|
if len(RABits) > 1:
|
| 38 |
|
|
RAHDecimal = RAHDecimal+(float(RABits[1])/60.0)
|
| 39 |
|
|
if len(RABits) > 2:
|
| 40 |
|
|
RAHDecimal = RAHDecimal+(float(RABits[2])/3600.0)
|
| 41 |
|
|
RADeg = (RAHDecimal/24.0)*360.0
|
| 42 |
|
|
else:
|
| 43 |
|
|
RADeg = float(RAString)
|
| 44 |
|
|
|
| 45 |
|
|
return RADeg
|
| 46 |
|
|
|
| 47 |
|
|
|
| 48 |
|
|
def dms2decimal(decString, delimiter):
|
| 49 |
|
|
"""Converts a delimited string of Degrees:Minutes:Seconds format into
|
| 50 |
|
|
decimal degrees.
|
| 51 |
|
|
|
| 52 |
|
|
@type decString: string
|
| 53 |
|
|
@param decString: coordinate string in D:M:S format
|
| 54 |
|
|
@type delimiter: string
|
| 55 |
|
|
@param delimiter: delimiter character in decString
|
| 56 |
|
|
@rtype: float
|
| 57 |
|
|
@return: coordinate in decimal degrees
|
| 58 |
|
|
|
| 59 |
|
|
"""
|
| 60 |
|
|
|
| 61 |
|
|
if delimiter == "":
|
| 62 |
|
|
decBits = str(decString).split()
|
| 63 |
|
|
else:
|
| 64 |
|
|
decBits = str(decString).split(delimiter)
|
| 65 |
|
|
if len(decBits) > 1:
|
| 66 |
|
|
decDeg = float(decBits[0])
|
| 67 |
|
|
if decBits[0].find("-") != -1:
|
| 68 |
|
|
if len(decBits) > 1:
|
| 69 |
|
|
decDeg = decDeg-(float(decBits[1])/60.0)
|
| 70 |
|
|
if len(decBits) > 2:
|
| 71 |
|
|
decDeg = decDeg-(float(decBits[2])/3600.0)
|
| 72 |
|
|
else:
|
| 73 |
|
|
if len(decBits) > 1:
|
| 74 |
|
|
decDeg = decDeg+(float(decBits[1])/60.0)
|
| 75 |
|
|
if len(decBits) > 2:
|
| 76 |
|
|
decDeg = decDeg+(float(decBits[2])/3600.0)
|
| 77 |
|
|
else:
|
| 78 |
|
|
decDeg = float(decString)
|
| 79 |
|
|
|
| 80 |
|
|
return decDeg
|
| 81 |
|
|
|
| 82 |
|
|
|
| 83 |
|
|
def decimal2hms(RADeg, delimiter):
|
| 84 |
|
|
"""Converts decimal degrees to string in Hours:Minutes:Seconds format with
|
| 85 |
|
|
user specified delimiter.
|
| 86 |
|
|
|
| 87 |
|
|
@type RADeg: float
|
| 88 |
|
|
@param RADeg: coordinate in decimal degrees
|
| 89 |
|
|
@type delimiter: string
|
| 90 |
|
|
@param delimiter: delimiter character in returned string
|
| 91 |
|
|
@rtype: string
|
| 92 |
|
|
@return: coordinate string in H:M:S format
|
| 93 |
|
|
|
| 94 |
|
|
"""
|
| 95 |
|
|
hours = (RADeg/360.0)*24
|
| 96 |
|
|
|
| 97 |
|
|
if 1 <= hours < 10:
|
| 98 |
|
|
sHours = "0"+str(hours)[0]
|
| 99 |
|
|
elif hours >= 10:
|
| 100 |
|
|
sHours = str(hours)[:2]
|
| 101 |
|
|
elif hours < 1:
|
| 102 |
|
|
sHours = "00"
|
| 103 |
|
|
|
| 104 |
|
|
if str(hours).find(".") == -1:
|
| 105 |
|
|
mins = float(hours)*60.0
|
| 106 |
|
|
else:
|
| 107 |
|
|
mins = float(str(hours)[str(hours).index("."):])*60.0
|
| 108 |
|
|
|
| 109 |
|
|
if 1 <= mins<10:
|
| 110 |
|
|
sMins = "0"+str(mins)[:1]
|
| 111 |
|
|
elif mins >= 10:
|
| 112 |
|
|
sMins = str(mins)[:2]
|
| 113 |
|
|
elif mins < 1:
|
| 114 |
|
|
sMins = "00"
|
| 115 |
|
|
|
| 116 |
|
|
secs = (hours-(float(sHours)+float(sMins)/60.0))*3600.0
|
| 117 |
|
|
|
| 118 |
|
|
if 0.001 < secs < 10:
|
| 119 |
|
|
sSecs = "0"+str(secs)[:str(secs).find(".")+4]
|
| 120 |
|
|
elif secs < 0.0001:
|
| 121 |
|
|
sSecs = "00.001"
|
| 122 |
|
|
else:
|
| 123 |
|
|
sSecs = str(secs)[:str(secs).find(".")+4]
|
| 124 |
|
|
if len(sSecs) < 5:
|
| 125 |
|
|
sSecs = sSecs+"00"
|
| 126 |
|
|
|
| 127 |
|
|
if float(sSecs) == 60.000:
|
| 128 |
|
|
sSecs = "00.00"
|
| 129 |
|
|
sMins = str(int(sMins)+1)
|
| 130 |
|
|
if int(sMins) == 60:
|
| 131 |
|
|
sMins = "00"
|
| 132 |
|
|
sDeg = str(int(sDeg)+1)
|
| 133 |
|
|
|
| 134 |
|
|
return sHours+delimiter+sMins+delimiter+sSecs
|
| 135 |
|
|
|
| 136 |
|
|
|
| 137 |
|
|
def decimal2dms(decDeg, delimiter):
|
| 138 |
|
|
"""Converts decimal degrees to string in Degrees:Minutes:Seconds format
|
| 139 |
|
|
with user specified delimiter.
|
| 140 |
|
|
|
| 141 |
|
|
@type decDeg: float
|
| 142 |
|
|
@param decDeg: coordinate in decimal degrees
|
| 143 |
|
|
@type delimiter: string
|
| 144 |
|
|
@param delimiter: delimiter character in returned string
|
| 145 |
|
|
@rtype: string
|
| 146 |
|
|
@return: coordinate string in D:M:S format
|
| 147 |
|
|
|
| 148 |
|
|
"""
|
| 149 |
|
|
|
| 150 |
|
|
if decDeg > 0:
|
| 151 |
|
|
|
| 152 |
|
|
if 1 <= decDeg < 10:
|
| 153 |
|
|
sDeg = "0"+str(decDeg)[0]
|
| 154 |
|
|
elif decDeg >= 10:
|
| 155 |
|
|
sDeg = str(decDeg)[:2]
|
| 156 |
|
|
elif decDeg < 1:
|
| 157 |
|
|
sDeg = "00"
|
| 158 |
|
|
|
| 159 |
|
|
if str(decDeg).find(".") == -1:
|
| 160 |
|
|
mins = float(decDeg)*60.0
|
| 161 |
|
|
else:
|
| 162 |
|
|
mins = float(str(decDeg)[str(decDeg).index("."):])*60
|
| 163 |
|
|
|
| 164 |
|
|
if 1 <= mins < 10:
|
| 165 |
|
|
sMins = "0"+str(mins)[:1]
|
| 166 |
|
|
elif mins >= 10:
|
| 167 |
|
|
sMins = str(mins)[:2]
|
| 168 |
|
|
elif mins < 1:
|
| 169 |
|
|
sMins = "00"
|
| 170 |
|
|
|
| 171 |
|
|
secs = (decDeg-(float(sDeg)+float(sMins)/60.0))*3600.0
|
| 172 |
|
|
|
| 173 |
|
|
if 0 < secs < 10:
|
| 174 |
|
|
sSecs = "0"+str(secs)[:str(secs).find(".")+3]
|
| 175 |
|
|
elif secs < 0.001:
|
| 176 |
|
|
sSecs = "00.00"
|
| 177 |
|
|
else:
|
| 178 |
|
|
sSecs = str(secs)[:str(secs).find(".")+3]
|
| 179 |
|
|
if len(sSecs) < 5:
|
| 180 |
|
|
sSecs = sSecs+"0"
|
| 181 |
|
|
|
| 182 |
|
|
if float(sSecs) == 60.00:
|
| 183 |
|
|
sSecs = "00.00"
|
| 184 |
|
|
sMins = str(int(sMins)+1)
|
| 185 |
|
|
if int(sMins) == 60:
|
| 186 |
|
|
sMins = "00"
|
| 187 |
|
|
sDeg = str(int(sDeg)+1)
|
| 188 |
|
|
|
| 189 |
|
|
return "+"+sDeg+delimiter+sMins+delimiter+sSecs
|
| 190 |
|
|
|
| 191 |
|
|
else:
|
| 192 |
|
|
|
| 193 |
|
|
if -10 < decDeg <= -1:
|
| 194 |
|
|
sDeg = "-0"+str(decDeg)[1]
|
| 195 |
|
|
elif decDeg <= -10:
|
| 196 |
|
|
sDeg = str(decDeg)[:3]
|
| 197 |
|
|
elif decDeg > -1:
|
| 198 |
|
|
sDeg = "-00"
|
| 199 |
|
|
|
| 200 |
|
|
if str(decDeg).find(".") == -1:
|
| 201 |
|
|
mins = float(decDeg)*-60.0
|
| 202 |
|
|
else:
|
| 203 |
|
|
mins = float(str(decDeg)[str(decDeg).index("."):])*60
|
| 204 |
|
|
|
| 205 |
|
|
if 1 <= mins < 10:
|
| 206 |
|
|
sMins = "0"+str(mins)[:1]
|
| 207 |
|
|
elif mins >= 10:
|
| 208 |
|
|
sMins = str(mins)[:2]
|
| 209 |
|
|
elif mins < 1:
|
| 210 |
|
|
sMins = "00"
|
| 211 |
|
|
|
| 212 |
|
|
secs = (decDeg-(float(sDeg)-float(sMins)/60.0))*3600.0
|
| 213 |
|
|
|
| 214 |
|
|
|
| 215 |
|
|
if -10 < secs < 0:
|
| 216 |
|
|
sSecs = "0"+str(secs)[1:str(secs).find(".")+3]
|
| 217 |
|
|
elif secs > -0.001:
|
| 218 |
|
|
sSecs = "00.00"
|
| 219 |
|
|
else:
|
| 220 |
|
|
sSecs = str(secs)[1:str(secs).find(".")+3]
|
| 221 |
|
|
if len(sSecs) < 5:
|
| 222 |
|
|
sSecs = sSecs+"0"
|
| 223 |
|
|
|
| 224 |
|
|
if float(sSecs) == 60.00:
|
| 225 |
|
|
sSecs = "00.00"
|
| 226 |
|
|
sMins = str(int(sMins)+1)
|
| 227 |
|
|
if int(sMins) == 60:
|
| 228 |
|
|
sMins = "00"
|
| 229 |
|
|
sDeg = str(int(sDeg)-1)
|
| 230 |
|
|
|
| 231 |
|
|
return sDeg+delimiter+sMins+delimiter+sSecs
|
| 232 |
|
|
|
| 233 |
|
|
|
| 234 |
|
|
def calcAngSepDeg(RA1, dec1, RA2, dec2):
|
| 235 |
|
|
"""Calculates the angular separation of two positions on the sky (specified
|
| 236 |
|
|
in decimal degrees) in decimal degrees, assuming a tangent plane projection
|
| 237 |
|
|
(so separation has to be <90 deg). Note that RADeg2, decDeg2 can be numpy
|
| 238 |
|
|
arrays.
|
| 239 |
|
|
|
| 240 |
|
|
@type RADeg1: float
|
| 241 |
|
|
@param RADeg1: R.A. in decimal degrees for position 1
|
| 242 |
|
|
@type decDeg1: float
|
| 243 |
|
|
@param decDeg1: dec. in decimal degrees for position 1
|
| 244 |
|
|
@type RADeg2: float or numpy array
|
| 245 |
|
|
@param RADeg2: R.A. in decimal degrees for position 2
|
| 246 |
|
|
@type decDeg2: float or numpy array
|
| 247 |
|
|
@param decDeg2: dec. in decimal degrees for position 2
|
| 248 |
|
|
@rtype: float or numpy array, depending upon type of RADeg2, decDeg2
|
| 249 |
|
|
@return: angular separation in decimal degrees
|
| 250 |
|
|
|
| 251 |
|
|
***** Updates added by Alessandro Nastasi - 26/06/2014 *****
|
| 252 |
|
|
|
| 253 |
|
|
Now RA and DEC can be entered indifferently as decimal degrees or as
|
| 254 |
|
|
hh:mm:ss.s or hh mm ss.s (provided that they are passed as STRING
|
| 255 |
|
|
in the latter two cases). An internal routine recognizes the correct format
|
| 256 |
|
|
and converts them into decimal degrees.
|
| 257 |
|
|
|
| 258 |
|
|
In addition, the tangent plane approximation restriction (i.e., dist < 90 deg)
|
| 259 |
|
|
has been removed and the complete formula is now implemented.
|
| 260 |
|
|
Pythagoras approximation is applied only for dist < 1 arcsec.
|
| 261 |
|
|
"""
|
| 262 |
|
|
|
| 263 |
|
|
|
| 264 |
|
|
|
| 265 |
|
|
|
| 266 |
|
|
|
| 267 |
|
|
|
| 268 |
|
|
|
| 269 |
|
|
|
| 270 |
|
|
|
| 271 |
|
|
|
| 272 |
|
|
|
| 273 |
|
|
|
| 274 |
|
|
|
| 275 |
|
|
|
| 276 |
|
|
|
| 277 |
|
|
|
| 278 |
|
|
|
| 279 |
|
|
RA1 = str(RA1).strip()
|
| 280 |
|
|
dec1 = str(dec1).strip()
|
| 281 |
|
|
RA2 = str(RA2).strip()
|
| 282 |
|
|
dec2 = str(dec2).strip()
|
| 283 |
|
|
|
| 284 |
|
|
inp_param = [str(RA1), dec1, RA2, dec2]
|
| 285 |
|
|
newinp = []
|
| 286 |
|
|
for x in inp_param:
|
| 287 |
|
|
newinp.append(string.replace(x, ":", " "))
|
| 288 |
|
|
|
| 289 |
|
|
if str(newinp[0]).find(' ') >= 0:
|
| 290 |
|
|
RADeg1 = hms2decimal(newinp[0], ' ')
|
| 291 |
|
|
decDeg1 = dms2decimal(newinp[1], ' ')
|
| 292 |
|
|
else:
|
| 293 |
|
|
RADeg1 = float(newinp[0])
|
| 294 |
|
|
decDeg1 = float(newinp[1])
|
| 295 |
|
|
|
| 296 |
|
|
if str(newinp[2]).find(' ') >= 0:
|
| 297 |
|
|
RADeg2 = hms2decimal(newinp[2], ' ')
|
| 298 |
|
|
decDeg2 = dms2decimal(newinp[3], ' ')
|
| 299 |
|
|
else:
|
| 300 |
|
|
RADeg2 = float(newinp[2])
|
| 301 |
|
|
decDeg2 = float(newinp[3])
|
| 302 |
|
|
|
| 303 |
|
|
cRA = numpy.radians(RADeg1)
|
| 304 |
|
|
cDec = numpy.radians(decDeg1)
|
| 305 |
|
|
|
| 306 |
|
|
gRA = numpy.radians(RADeg2)
|
| 307 |
|
|
gDec = numpy.radians(decDeg2)
|
| 308 |
|
|
|
| 309 |
|
|
x=numpy.cos(cRA)*numpy.cos(cDec)*numpy.cos(gRA)*numpy.cos(gDec)
|
| 310 |
|
|
y=numpy.sin(cRA)*numpy.cos(cDec)*numpy.sin(gRA)*numpy.cos(gDec)
|
| 311 |
|
|
z=numpy.sin(cDec)*numpy.sin(gDec)
|
| 312 |
|
|
|
| 313 |
|
|
rad=numpy.degrees(numpy.arccos(round(x+y+z,10)))
|
| 314 |
|
|
|
| 315 |
|
|
dRA = cRA-gRA
|
| 316 |
|
|
dDec = gDec-cDec
|
| 317 |
|
|
|
| 318 |
|
|
|
| 319 |
|
|
|
| 320 |
|
|
|
| 321 |
|
|
|
| 322 |
|
|
|
| 323 |
|
|
|
| 324 |
|
|
|
| 325 |
|
|
if rad<0.000004848:
|
| 326 |
|
|
rad=numpy.degrees(numpy.sqrt((numpy.cos(cDec)*dRA)**2+dDec**2))
|
| 327 |
|
|
|
| 328 |
|
|
return rad
|
| 329 |
|
|
|
| 330 |
|
|
|
| 331 |
|
|
def calcAngSepDegPythagoras(RADeg1, decDeg1, RADeg2, decDeg2):
|
| 332 |
|
|
|
| 333 |
|
|
|
| 334 |
|
|
cRA = numpy.radians(RADeg1)
|
| 335 |
|
|
cDec = numpy.radians(decDeg1)
|
| 336 |
|
|
|
| 337 |
|
|
gRA = numpy.radians(RADeg2)
|
| 338 |
|
|
gDec = numpy.radians(decDeg2)
|
| 339 |
|
|
|
| 340 |
|
|
dRA = cRA-gRA
|
| 341 |
|
|
dDec = gDec-cDec
|
| 342 |
|
|
cosC = ((numpy.sin(gDec)*numpy.sin(cDec)) +
|
| 343 |
|
|
(numpy.cos(gDec)*numpy.cos(cDec) * numpy.cos(gRA-cRA)))
|
| 344 |
|
|
x = (numpy.cos(cDec)*numpy.sin(gRA-cRA))/cosC
|
| 345 |
|
|
y = (((numpy.cos(gDec)*numpy.sin(cDec)) - (numpy.sin(gDec) *
|
| 346 |
|
|
numpy.cos(cDec)*numpy.cos(gRA-cRA)))/cosC)
|
| 347 |
|
|
r = numpy.degrees(numpy.sqrt(x*x+y*y))
|
| 348 |
|
|
|
| 349 |
|
|
return r
|
| 350 |
|
|
|
| 351 |
|
|
|
| 352 |
|
|
def shiftRADec(ra1, dec1, deltaRA, deltaDec):
|
| 353 |
|
|
"""Computes new right ascension and declination shifted from the original
|
| 354 |
|
|
by some delta RA and delta DEC. Input position is decimal degrees. Shifts
|
| 355 |
|
|
(deltaRA, deltaDec) are arcseconds, and output is decimal degrees. Based on
|
| 356 |
|
|
an IDL routine of the same name.
|
| 357 |
|
|
|
| 358 |
|
|
@param ra1: float
|
| 359 |
|
|
@type ra1: R.A. in decimal degrees
|
| 360 |
|
|
@param dec1: float
|
| 361 |
|
|
@type dec1: dec. in decimal degrees
|
| 362 |
|
|
@param deltaRA: float
|
| 363 |
|
|
@type deltaRA: shift in R.A. in arcseconds
|
| 364 |
|
|
@param deltaDec: float
|
| 365 |
|
|
@type deltaDec: shift in dec. in arcseconds
|
| 366 |
|
|
@rtype: float [newRA, newDec]
|
| 367 |
|
|
@return: shifted R.A. and dec.
|
| 368 |
|
|
|
| 369 |
|
|
"""
|
| 370 |
|
|
|
| 371 |
|
|
d2r = math.pi/180.
|
| 372 |
|
|
as2r = math.pi/648000.
|
| 373 |
|
|
|
| 374 |
|
|
|
| 375 |
|
|
rara1 = ra1*d2r
|
| 376 |
|
|
dcrad1 = dec1*d2r
|
| 377 |
|
|
shiftRArad = deltaRA*as2r
|
| 378 |
|
|
shiftDCrad = deltaDec*as2r
|
| 379 |
|
|
|
| 380 |
|
|
|
| 381 |
|
|
deldec2 = 0.0
|
| 382 |
|
|
sindis = math.sin(shiftRArad / 2.0)
|
| 383 |
|
|
sindelRA = sindis / math.cos(dcrad1)
|
| 384 |
|
|
delra = 2.0*math.asin(sindelRA) / d2r
|
| 385 |
|
|
|
| 386 |
|
|
|
| 387 |
|
|
ra2 = ra1+delra
|
| 388 |
|
|
dec2 = dec1 +deltaDec / 3600.0
|
| 389 |
|
|
|
| 390 |
|
|
return ra2, dec2
|
| 391 |
|
|
|
| 392 |
|
|
|
| 393 |
|
|
def convertCoords(inputSystem, outputSystem, coordX, coordY, epoch):
|
| 394 |
|
|
"""Converts specified coordinates (given in decimal degrees) between J2000,
|
| 395 |
|
|
B1950, and Galactic.
|
| 396 |
|
|
|
| 397 |
|
|
@type inputSystem: string
|
| 398 |
|
|
@param inputSystem: system of the input coordinates (either "J2000",
|
| 399 |
|
|
"B1950" or "GALACTIC")
|
| 400 |
|
|
@type outputSystem: string
|
| 401 |
|
|
@param outputSystem: system of the returned coordinates (either "J2000",
|
| 402 |
|
|
"B1950" or "GALACTIC")
|
| 403 |
|
|
@type coordX: float
|
| 404 |
|
|
@param coordX: longitude coordinate in decimal degrees, e.g. R. A.
|
| 405 |
|
|
@type coordY: float
|
| 406 |
|
|
@param coordY: latitude coordinate in decimal degrees, e.g. dec.
|
| 407 |
|
|
@type epoch: float
|
| 408 |
|
|
@param epoch: epoch of the input coordinates
|
| 409 |
|
|
@rtype: list
|
| 410 |
|
|
@return: coordinates in decimal degrees in requested output system
|
| 411 |
|
|
|
| 412 |
|
|
"""
|
| 413 |
|
|
|
| 414 |
|
|
if inputSystem=="J2000" or inputSystem=="B1950" or inputSystem=="GALACTIC":
|
| 415 |
|
|
if outputSystem=="J2000" or outputSystem=="B1950" or \
|
| 416 |
|
|
outputSystem=="GALACTIC":
|
| 417 |
|
|
|
| 418 |
|
|
outCoords=wcscon.wcscon(wcscon.wcscsys(inputSystem),
|
| 419 |
|
|
wcscon.wcscsys(outputSystem), 0, 0, coordX, coordY, epoch)
|
| 420 |
|
|
|
| 421 |
|
|
return outCoords
|
| 422 |
|
|
|
| 423 |
|
|
raise Exception("inputSystem and outputSystem must be 'J2000', 'B1950'"
|
| 424 |
|
|
"or 'GALACTIC'")
|
| 425 |
|
|
|
| 426 |
|
|
|
| 427 |
|
|
def calcRADecSearchBox(RADeg, decDeg, radiusSkyDeg):
|
| 428 |
|
|
"""Calculates minimum and maximum RA, dec coords needed to define a box
|
| 429 |
|
|
enclosing a circle of radius radiusSkyDeg around the given RADeg, decDeg
|
| 430 |
|
|
coordinates. Useful for freeform queries of e.g. SDSS, UKIDSS etc.. Uses
|
| 431 |
|
|
L{calcAngSepDeg}, so has the same limitations.
|
| 432 |
|
|
|
| 433 |
|
|
@type RADeg: float
|
| 434 |
|
|
@param RADeg: RA coordinate of centre of search region
|
| 435 |
|
|
@type decDeg: float
|
| 436 |
|
|
@param decDeg: dec coordinate of centre of search region
|
| 437 |
|
|
@type radiusSkyDeg: float
|
| 438 |
|
|
@param radiusSkyDeg: radius in degrees on the sky used to define search
|
| 439 |
|
|
region
|
| 440 |
|
|
@rtype: list
|
| 441 |
|
|
@return: [RAMin, RAMax, decMin, decMax] - coordinates in decimal degrees
|
| 442 |
|
|
defining search box
|
| 443 |
|
|
|
| 444 |
|
|
"""
|
| 445 |
|
|
|
| 446 |
|
|
tolerance = 1e-5
|
| 447 |
|
|
targetHalfSizeSkyDeg = radiusSkyDeg
|
| 448 |
|
|
funcCalls = ["calcAngSepDeg(RADeg, decDeg, guess, decDeg)",
|
| 449 |
|
|
"calcAngSepDeg(RADeg, decDeg, guess, decDeg)",
|
| 450 |
|
|
"calcAngSepDeg(RADeg, decDeg, RADeg, guess)",
|
| 451 |
|
|
"calcAngSepDeg(RADeg, decDeg, RADeg, guess)"]
|
| 452 |
|
|
coords = [RADeg, RADeg, decDeg, decDeg]
|
| 453 |
|
|
signs = [1.0, -1.0, 1.0, -1.0]
|
| 454 |
|
|
results = []
|
| 455 |
|
|
for f, c, sign in zip(funcCalls, coords, signs):
|
| 456 |
|
|
|
| 457 |
|
|
maxGuess = sign*targetHalfSizeSkyDeg*10.0
|
| 458 |
|
|
minGuess = sign*targetHalfSizeSkyDeg/10.0
|
| 459 |
|
|
guessStep = (maxGuess-minGuess)/10.0
|
| 460 |
|
|
guesses = numpy.arange(minGuess+c, maxGuess+c, guessStep)
|
| 461 |
|
|
for i in range(50):
|
| 462 |
|
|
minSizeDiff = 1e6
|
| 463 |
|
|
bestGuess = None
|
| 464 |
|
|
for guess in guesses:
|
| 465 |
|
|
sizeDiff = abs(eval(f)-targetHalfSizeSkyDeg)
|
| 466 |
|
|
if sizeDiff < minSizeDiff:
|
| 467 |
|
|
minSizeDiff = sizeDiff
|
| 468 |
|
|
bestGuess = guess
|
| 469 |
|
|
if minSizeDiff < tolerance:
|
| 470 |
|
|
break
|
| 471 |
|
|
else:
|
| 472 |
|
|
guessRange = abs((maxGuess-minGuess))
|
| 473 |
|
|
maxGuess = bestGuess+guessRange/4.0
|
| 474 |
|
|
minGuess = bestGuess-guessRange/4.0
|
| 475 |
|
|
guessStep = (maxGuess-minGuess)/10.0
|
| 476 |
|
|
guesses = numpy.arange(minGuess, maxGuess, guessStep)
|
| 477 |
|
|
results.append(bestGuess)
|
| 478 |
|
|
|
| 479 |
|
|
RAMax = results[0]
|
| 480 |
|
|
RAMin = results[1]
|
| 481 |
|
|
decMax = results[2]
|
| 482 |
|
|
decMin = results[3]
|
| 483 |
|
|
|
| 484 |
|
|
return [RAMin, RAMax, decMin, decMax] |