Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1#!/usr/local/bin/python 

2# encoding: utf-8 

3""" 

4*Convert coordinates from decimal to sexagesimal units and vice-versa* 

5 

6:Author: 

7 David Young 

8""" 

9from __future__ import division 

10from builtins import str 

11from builtins import object 

12from past.utils import old_div 

13import sys 

14import os 

15import math 

16os.environ['TERM'] = 'vt100' 

17from fundamentals import tools 

18 

19class unit_conversion(object): 

20 """ 

21 *The worker class for the unit_conversion module* 

22 

23 **Key Arguments** 

24 

25 - ``log`` -- logger 

26 - ``settings`` -- the settings dictionary (prob not required) 

27  

28 

29 **Usage** 

30 

31 .. todo:: 

32 

33 - add usage info 

34 - create a sublime snippet for usage 

35 - add ra_sexegesimal_to_decimal 

36 

37 ```python 

38 usage code 

39 ``` 

40  

41 

42 .. todo:: 

43 

44 - @review: when complete, clean unit_conversion class 

45 - @review: when complete add logging 

46 - @review: when complete, decide whether to abstract class to another module 

47 """ 

48 # Initialisation 

49 # 1. @flagged: what are the unique attrributes for each object? Add them 

50 # to __init__ 

51 

52 def __init__( 

53 self, 

54 log, 

55 settings=False 

56 ): 

57 self.log = log 

58 log.debug("instansiating a new 'unit_conversion' object") 

59 self.settings = settings 

60 # xt-self-arg-tmpx 

61 

62 # 2. @flagged: what are the default attrributes each object could have? Add them to variable attribute set here 

63 # Variable Data Atrributes 

64 

65 # 3. @flagged: what variable attrributes need overriden in any baseclass(es) used 

66 # Override Variable Data Atrributes 

67 

68 # Initial Actions 

69 

70 return None 

71 

72 # 4. @flagged: what actions does each object have to be able to perform? Add them here 

73 # Method Attributes 

74 def get(self): 

75 """ 

76 *get the unit_conversion object* 

77 

78 **Return** 

79 

80 - ``unit_conversion`` 

81  

82 

83 .. todo:: 

84 

85 - @review: when complete, clean get method 

86 - @review: when complete add logging 

87 """ 

88 self.log.debug('starting the ``get`` method') 

89 

90 unit_conversion = None 

91 

92 self.log.debug('completed the ``get`` method') 

93 return unit_conversion 

94 

95 def dec_sexegesimal_to_decimal( 

96 self, 

97 dec): 

98 """ 

99 *Convert a declination from sexegesimal format to decimal degrees.* 

100 

101 Precision should be respected. If a float is passed to this method, the same float will be returned (useful if unclear which format coordinates are in). 

102 

103 The code will attempt to read the sexegesimal value in whatever form it is passed. Any of the following should be handled correctly: 

104 

105 - ``+1:58:05.45341`` 

106 - ``01:5:05`` 

107 - ``+1 58 05.45341`` 

108 - ``-23h53m05s`` 

109 

110 **Key Arguments** 

111 

112 - ``dec`` - DEC in sexegesimal format. 

113  

114 

115 **Return** 

116 

117 - ``decDeg`` -- declination converted to decimal degrees 

118  

119 

120 **Usage** 

121 

122 .. todo:: 

123 

124 - replace dryxPython declination_sexegesimal_to_decimal with this version in all my code 

125 - replace coords_sex_to_dec in all code 

126 

127 ```python 

128 from astrocalc.coords import unit_conversion 

129 converter = unit_conversion( 

130 log=log 

131 ) 

132 dec = converter.dec_sexegesimal_to_decimal( 

133 dec="-23:45:21.23232" 

134 ) 

135 print(dec) 

136 

137 # OUTPUT: -23.7558978667 

138 ``` 

139  

140 """ 

141 self.log.debug( 

142 'completed the ````dec_sexegesimal_to_decimal`` method') 

143 

144 import re 

145 

146 # TEST TO SEE IF DECIMAL DEGREES PASSED 

147 try: 

148 dec = float(dec) 

149 if dec > -90. and dec < 90.: 

150 self.log.debug( 

151 'declination seems to already be in decimal degrees, returning original value' % locals()) 

152 return float(dec) 

153 except: 

154 pass 

155 

156 # REMOVE SURROUNDING WHITESPACE 

157 dec = str(dec).strip() 

158 

159 # LOOK FOR A MINUS SIGN. NOTE THAT -00 IS THE SAME AS 00. 

160 regex = re.compile( 

161 r'^([\+\-]?(\d|[0-8]\d))\D+([0-5]\d)\D+([0-6]?\d(\.\d+)?)$') 

162 decMatch = regex.match(dec) 

163 

164 if decMatch: 

165 degrees = decMatch.group(1) 

166 minutes = decMatch.group(3) 

167 seconds = decMatch.group(4) 

168 

169 if degrees[0] == '-': 

170 sgn = -1 

171 else: 

172 sgn = 1 

173 

174 degrees = abs(float(degrees)) 

175 minutes = float(minutes) 

176 seconds = float(seconds) 

177 

178 # PRECISION TEST 

179 # 1s = .000277778 DEGREE 

180 # THEREFORE REPORT SECONDS TO A PRECISION = INPUT PRECISION + 4 

181 decimalLen = len(repr(seconds).split(".")[-1]) 

182 precision = decimalLen + 4 

183 

184 decDeg = (degrees + (minutes / 60.0) 

185 + (seconds / 3600.0)) * sgn 

186 

187 decDeg = "%0.*f" % (precision, decDeg) 

188 

189 else: 

190 raise IOError( 

191 "could not convert dec to decimal degrees, could not parse sexegesimal input. Original value was `%(dec)s`" % locals()) 

192 

193 decDeg = float(decDeg) 

194 self.log.debug('decDeg: %(decDeg)s' % locals()) 

195 self.log.debug( 

196 'completed the ``dec_sexegesimal_to_decimal`` method') 

197 

198 return float(decDeg) 

199 

200 def ra_sexegesimal_to_decimal( 

201 self, 

202 ra 

203 ): 

204 """ 

205 *Convert a right-ascension from sexegesimal format to decimal degrees.* 

206 

207 Precision should be respected. If a float is passed to this method, the same float will be returned (useful if unclear which format coordinates are in). 

208 

209 The code will attempt to read the sexegesimal value in whatever form it is passed. Any of the following should be handled correctly 

210 

211 - ``23:45:21.23232`` 

212 - ``23h45m21.23232s`` 

213 - ``23 45 21.23232`` 

214 - ``2 04 21.23232`` 

215 - ``04:45 21`` 

216 

217 **Key Arguments** 

218 

219 - ``ra`` -- ra in sexegesimal units 

220  

221 

222 **Return** 

223 

224 - ``decimalDegrees`` 

225  

226 

227 **Usage** 

228 

229 ```python 

230 - replace dryxPython ra_sexegesimal_to_decimal with this version in all my code 

231 

232 from astrocalc.coords import unit_conversion 

233 converter = unit_conversion( 

234 log=log 

235 ) 

236 ra = converter.ra_sexegesimal_to_decimal( 

237 ra="04:45 21" 

238 ) 

239 print(ra) 

240 

241 # OUTPUT: 71.3375 

242 ``` 

243  

244 """ 

245 import re 

246 

247 # TEST TO SEE IF DECIMAL DEGREES PASSED 

248 try: 

249 ra = float(ra) 

250 if ra >= 0. and ra <= 360.: 

251 self.log.debug( 

252 'RA seems to already be in decimal degrees, returning original value' % locals()) 

253 return float(ra) 

254 except: 

255 pass 

256 

257 # REMOVE SURROUNDING WHITESPACE 

258 ra = str(ra).strip() 

259 

260 regex = re.compile( 

261 r'^(\+?(\d|[0-1]\d|2[0-3]))\D+([0-5]\d)\D+([0-6]?\d(\.\d*?)?)(s)?\s*?$') 

262 raMatch = regex.match(ra) 

263 

264 if raMatch: 

265 degrees = raMatch.group(1) 

266 minutes = raMatch.group(3) 

267 seconds = raMatch.group(4) 

268 

269 degrees = abs(float(degrees)) * 15.0 

270 minutes = float(minutes) * 15.0 

271 seconds = float(seconds) * 15.0 

272 

273 # PRECISION TEST 

274 # 1s ARCSEC = .000018519 DEGREE 

275 # THEREFORE REPORT SECONDS TO A PRECISION = INPUT PRECISION + 5 

276 decimalLen = len(repr(seconds).split(".")[-1]) 

277 precision = decimalLen + 5 

278 

279 decimalDegrees = (degrees + (minutes / 60.0) 

280 + (seconds / 3600.0)) 

281 

282 decimalDegrees = "%0.*f" % (precision, decimalDegrees) 

283 

284 else: 

285 raise IOError( 

286 "could not convert ra to decimal degrees, could not parse sexegesimal input. Original value was `%(ra)s`" % locals()) 

287 

288 raDeg = decimalDegrees 

289 self.log.debug('raDeg: %(decimalDegrees)s' % locals()) 

290 self.log.debug( 

291 'completed the ``ra_sexegesimal_to_decimal`` method') 

292 

293 return float(raDeg) 

294 

295 def ra_decimal_to_sexegesimal( 

296 self, 

297 ra, 

298 delimiter=":"): 

299 """ 

300 *Convert a right-ascension between decimal degrees and sexegesimal.* 

301 

302 Precision should be respected. 

303 

304 **Key Arguments** 

305 

306 - ``ra`` -- RA in decimal degrees. Will try and convert to float before performing calculation. 

307 - ``delimiter`` -- how to delimit the RA units. Default *:* 

308  

309 

310 **Return** 

311 

312 - ``sexegesimal`` -- ra in sexegesimal units 

313  

314 

315 **Usage** 

316 

317 .. todo:: 

318 

319 - replace ra_to_sex from dryxPython in all code 

320 

321 ```python 

322 from astrocalc.coords import unit_conversion 

323 converter = unit_conversion( 

324 log=log 

325 ) 

326 ra = converter.ra_decimal_to_sexegesimal( 

327 ra="-23.454676456", 

328 delimiter=":" 

329 ) 

330 print(ra) 

331 

332 # OUT: 22:26:10.87 

333 ``` 

334  

335 """ 

336 self.log.debug('starting the ``ra_decimal_to_sexegesimal`` method') 

337 

338 # CONVERT RA TO FLOAT 

339 try: 

340 self.log.debug("attempting to convert RA to float") 

341 ra = float(ra) 

342 except Exception as e: 

343 self.log.error( 

344 "could not convert RA to float - failed with this error: %s " % (str(e),)) 

345 return -1 

346 

347 # COMPLAIN IF RA NOT BETWEEN -360 - 360 

348 if ra > 0. and ra < 360.: 

349 pass 

350 elif ra < 0 and ra > -360.: 

351 ra = 360. + ra 

352 else: 

353 self.log.error( 

354 "RA must be between 0 - 360 degrees") 

355 return -1 

356 

357 # PRECISION TEST 

358 # 1s ARCSEC = .000018519 DEGREE 

359 # THEREFORE REPORT SECONDS TO A PRECISION = INPUT PRECISION - 5 

360 decimalLen = len(repr(ra).split(".")[-1]) 

361 precision = decimalLen - 5 

362 

363 # CALCULATION FROM DECIMAL DEGREES 

364 import math 

365 ra_hh = int(old_div(ra, 15)) 

366 ra_mm = int((old_div(ra, 15) - ra_hh) * 60) 

367 ra_ss = int(((old_div(ra, 15) - ra_hh) * 60 - ra_mm) * 60) 

368 ra_ff = ((old_div(ra, 15) - ra_hh) * 60 - ra_mm) * 60 - ra_ss 

369 

370 # SET PRECISION 

371 ra_ff = repr(ra_ff)[2:] 

372 ra_ff = ra_ff[:precision] 

373 if len(ra_ff): 

374 ra_ff = "." + ra_ff 

375 if precision < 0: 

376 ra_ff = "" 

377 

378 sexegesimal = '%02d' % ra_hh + delimiter + '%02d' % ra_mm + \ 

379 delimiter + '%02d' % ra_ss + ra_ff 

380 

381 self.log.debug('completed the ``ra_decimal_to_sexegesimal`` method') 

382 return sexegesimal 

383 

384 def dec_decimal_to_sexegesimal( 

385 self, 

386 dec, 

387 delimiter=":"): 

388 """ 

389 *Convert a declination between decimal degrees and sexegesimal.* 

390 

391 Precision should be respected. 

392 

393 **Key Arguments** 

394 

395 - ``dec`` -- DEC in decimal degrees. Will try and convert to float before performing calculation. 

396 - ``delimiter`` -- how to delimit the RA units. Default *:* 

397  

398 

399 **Return** 

400 

401 - ``sexegesimal`` -- ra in sexegesimal units 

402  

403 

404 **Usage** 

405 

406 .. todo:: 

407 

408 - replace dec_to_sex in dryxPython in all code 

409 

410 ```python 

411 from astrocalc.coords import unit_conversion 

412 converter = unit_conversion( 

413 log=log 

414 ) 

415 dec = converter.dec_decimal_to_sexegesimal( 

416 dec="-3.454676456", 

417 delimiter=":" 

418 ) 

419 print(dec) 

420 

421 # OUT: -03:27:16.8 

422 ``` 

423  

424 """ 

425 self.log.debug('starting the ``dec_decimal_to_sexegesimal`` method') 

426 

427 import math 

428 

429 # CONVERT DEC TO FLOAT 

430 try: 

431 self.log.debug("attempting to convert RA to float") 

432 dec = float(dec) 

433 except Exception as e: 

434 self.log.error( 

435 "could not convert RA to float - failed with this error: %s " % (str(e),)) 

436 return -1 

437 

438 # COMPLAIN IF DEC NOT BETWEEN -90 - 90 

439 if dec > -90. and dec < 90.: 

440 pass 

441 else: 

442 self.log.error( 

443 "DEC must be between -90 - 90 degrees") 

444 return -1 

445 

446 if (dec >= 0): 

447 hemisphere = '+' 

448 else: 

449 hemisphere = '-' 

450 dec *= -1 

451 

452 # PRECISION TEST 

453 # 1s = .000277778 DEGREE 

454 # THEREFORE REPORT SECONDS TO A PRECISION = INPUT PRECISION - 4 

455 decimalLen = len(repr(dec).split(".")[-1]) 

456 precision = decimalLen - 4 

457 

458 dec_deg = int(dec) 

459 dec_mm = int((dec - dec_deg) * 60) 

460 dec_ss = int(((dec - dec_deg) * 60 - dec_mm) * 60) 

461 dec_f = (((dec - dec_deg) * 60 - dec_mm) * 60) - dec_ss 

462 

463 # SET PRECISION 

464 dec_f = repr(dec_f)[2:] 

465 dec_f = dec_f[:precision] 

466 if len(dec_f): 

467 dec_f = "." + dec_f 

468 if precision < 0: 

469 dec_f = "" 

470 

471 sexegesimal = hemisphere + '%02d' % dec_deg + delimiter + \ 

472 '%02d' % dec_mm + delimiter + '%02d' % dec_ss + dec_f 

473 

474 self.log.debug('completed the ``dec_decimal_to_sexegesimal`` method') 

475 return sexegesimal 

476 

477 # use the tab-trigger below for new method 

478 def ra_dec_to_cartesian( 

479 self, 

480 ra, 

481 dec): 

482 """*Convert an RA, DEC coordinate set to x, y, z cartesian coordinates* 

483 

484 **Key Arguments** 

485 

486 - ``ra`` -- right ascension in sexegesimal or decimal degress. 

487 - ``dec`` -- declination in sexegesimal or decimal degress. 

488  

489 

490 **Return** 

491 

492 - ``cartesians`` -- tuple of (x, y, z) coordinates 

493  

494 

495 .. todo:: 

496 

497 - replace calculate_cartesians in all code 

498 

499 **Usage** 

500 

501 ```python 

502 from astrocalc.coords import unit_conversion 

503 converter = unit_conversion( 

504 log=log 

505 ) 

506 x, y, z = converter.ra_dec_to_cartesian( 

507 ra="23 45 21.23232", 

508 dec="+01:58:5.45341" 

509 ) 

510 print(x, y, z) 

511 

512 # OUTPUT: 0.9973699780687104, -0.06382462462791459, 0.034344492110465606 

513 ``` 

514  

515 """ 

516 self.log.debug('starting the ``ra_dec_to_cartesian`` method') 

517 

518 ra = self.ra_sexegesimal_to_decimal( 

519 ra=ra 

520 ) 

521 dec = self.dec_sexegesimal_to_decimal( 

522 dec=dec 

523 ) 

524 

525 ra = math.radians(ra) 

526 dec = math.radians(dec) 

527 cos_dec = math.cos(dec) 

528 cx = math.cos(ra) * cos_dec 

529 cy = math.sin(ra) * cos_dec 

530 cz = math.sin(dec) 

531 

532 cartesians = (cx, cy, cz) 

533 

534 self.log.debug('completed the ``ra_dec_to_cartesian`` method') 

535 return cartesians 

536 

537 # use the tab-trigger below for new method 

538 # xt-class-method