Coverage for astrocalc/coords/unit_conversion.py : 90%

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*
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
19class unit_conversion(object):
20 """
21 *The worker class for the unit_conversion module*
23 **Key Arguments**
25 - ``log`` -- logger
26 - ``settings`` -- the settings dictionary (prob not required)
29 **Usage**
31 .. todo::
33 - add usage info
34 - create a sublime snippet for usage
35 - add ra_sexegesimal_to_decimal
37 ```python
38 usage code
39 ```
42 .. todo::
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__
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
62 # 2. @flagged: what are the default attrributes each object could have? Add them to variable attribute set here
63 # Variable Data Atrributes
65 # 3. @flagged: what variable attrributes need overriden in any baseclass(es) used
66 # Override Variable Data Atrributes
68 # Initial Actions
70 return None
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*
78 **Return**
80 - ``unit_conversion``
83 .. todo::
85 - @review: when complete, clean get method
86 - @review: when complete add logging
87 """
88 self.log.debug('starting the ``get`` method')
90 unit_conversion = None
92 self.log.debug('completed the ``get`` method')
93 return unit_conversion
95 def dec_sexegesimal_to_decimal(
96 self,
97 dec):
98 """
99 *Convert a declination from sexegesimal format to decimal degrees.*
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).
103 The code will attempt to read the sexegesimal value in whatever form it is passed. Any of the following should be handled correctly:
105 - ``+1:58:05.45341``
106 - ``01:5:05``
107 - ``+1 58 05.45341``
108 - ``-23h53m05s``
110 **Key Arguments**
112 - ``dec`` - DEC in sexegesimal format.
115 **Return**
117 - ``decDeg`` -- declination converted to decimal degrees
120 **Usage**
122 .. todo::
124 - replace dryxPython declination_sexegesimal_to_decimal with this version in all my code
125 - replace coords_sex_to_dec in all code
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)
137 # OUTPUT: -23.7558978667
138 ```
140 """
141 self.log.debug(
142 'completed the ````dec_sexegesimal_to_decimal`` method')
144 import re
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
156 # REMOVE SURROUNDING WHITESPACE
157 dec = str(dec).strip()
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)
164 if decMatch:
165 degrees = decMatch.group(1)
166 minutes = decMatch.group(3)
167 seconds = decMatch.group(4)
169 if degrees[0] == '-':
170 sgn = -1
171 else:
172 sgn = 1
174 degrees = abs(float(degrees))
175 minutes = float(minutes)
176 seconds = float(seconds)
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
184 decDeg = (degrees + (minutes / 60.0)
185 + (seconds / 3600.0)) * sgn
187 decDeg = "%0.*f" % (precision, decDeg)
189 else:
190 raise IOError(
191 "could not convert dec to decimal degrees, could not parse sexegesimal input. Original value was `%(dec)s`" % locals())
193 decDeg = float(decDeg)
194 self.log.debug('decDeg: %(decDeg)s' % locals())
195 self.log.debug(
196 'completed the ``dec_sexegesimal_to_decimal`` method')
198 return float(decDeg)
200 def ra_sexegesimal_to_decimal(
201 self,
202 ra
203 ):
204 """
205 *Convert a right-ascension from sexegesimal format to decimal degrees.*
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).
209 The code will attempt to read the sexegesimal value in whatever form it is passed. Any of the following should be handled correctly
211 - ``23:45:21.23232``
212 - ``23h45m21.23232s``
213 - ``23 45 21.23232``
214 - ``2 04 21.23232``
215 - ``04:45 21``
217 **Key Arguments**
219 - ``ra`` -- ra in sexegesimal units
222 **Return**
224 - ``decimalDegrees``
227 **Usage**
229 ```python
230 - replace dryxPython ra_sexegesimal_to_decimal with this version in all my code
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)
241 # OUTPUT: 71.3375
242 ```
244 """
245 import re
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
257 # REMOVE SURROUNDING WHITESPACE
258 ra = str(ra).strip()
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)
264 if raMatch:
265 degrees = raMatch.group(1)
266 minutes = raMatch.group(3)
267 seconds = raMatch.group(4)
269 degrees = abs(float(degrees)) * 15.0
270 minutes = float(minutes) * 15.0
271 seconds = float(seconds) * 15.0
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
279 decimalDegrees = (degrees + (minutes / 60.0)
280 + (seconds / 3600.0))
282 decimalDegrees = "%0.*f" % (precision, decimalDegrees)
284 else:
285 raise IOError(
286 "could not convert ra to decimal degrees, could not parse sexegesimal input. Original value was `%(ra)s`" % locals())
288 raDeg = decimalDegrees
289 self.log.debug('raDeg: %(decimalDegrees)s' % locals())
290 self.log.debug(
291 'completed the ``ra_sexegesimal_to_decimal`` method')
293 return float(raDeg)
295 def ra_decimal_to_sexegesimal(
296 self,
297 ra,
298 delimiter=":"):
299 """
300 *Convert a right-ascension between decimal degrees and sexegesimal.*
302 Precision should be respected.
304 **Key Arguments**
306 - ``ra`` -- RA in decimal degrees. Will try and convert to float before performing calculation.
307 - ``delimiter`` -- how to delimit the RA units. Default *:*
310 **Return**
312 - ``sexegesimal`` -- ra in sexegesimal units
315 **Usage**
317 .. todo::
319 - replace ra_to_sex from dryxPython in all code
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)
332 # OUT: 22:26:10.87
333 ```
335 """
336 self.log.debug('starting the ``ra_decimal_to_sexegesimal`` method')
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
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
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
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
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 = ""
378 sexegesimal = '%02d' % ra_hh + delimiter + '%02d' % ra_mm + \
379 delimiter + '%02d' % ra_ss + ra_ff
381 self.log.debug('completed the ``ra_decimal_to_sexegesimal`` method')
382 return sexegesimal
384 def dec_decimal_to_sexegesimal(
385 self,
386 dec,
387 delimiter=":"):
388 """
389 *Convert a declination between decimal degrees and sexegesimal.*
391 Precision should be respected.
393 **Key Arguments**
395 - ``dec`` -- DEC in decimal degrees. Will try and convert to float before performing calculation.
396 - ``delimiter`` -- how to delimit the RA units. Default *:*
399 **Return**
401 - ``sexegesimal`` -- ra in sexegesimal units
404 **Usage**
406 .. todo::
408 - replace dec_to_sex in dryxPython in all code
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)
421 # OUT: -03:27:16.8
422 ```
424 """
425 self.log.debug('starting the ``dec_decimal_to_sexegesimal`` method')
427 import math
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
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
446 if (dec >= 0):
447 hemisphere = '+'
448 else:
449 hemisphere = '-'
450 dec *= -1
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
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
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 = ""
471 sexegesimal = hemisphere + '%02d' % dec_deg + delimiter + \
472 '%02d' % dec_mm + delimiter + '%02d' % dec_ss + dec_f
474 self.log.debug('completed the ``dec_decimal_to_sexegesimal`` method')
475 return sexegesimal
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*
484 **Key Arguments**
486 - ``ra`` -- right ascension in sexegesimal or decimal degress.
487 - ``dec`` -- declination in sexegesimal or decimal degress.
490 **Return**
492 - ``cartesians`` -- tuple of (x, y, z) coordinates
495 .. todo::
497 - replace calculate_cartesians in all code
499 **Usage**
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)
512 # OUTPUT: 0.9973699780687104, -0.06382462462791459, 0.034344492110465606
513 ```
515 """
516 self.log.debug('starting the ``ra_dec_to_cartesian`` method')
518 ra = self.ra_sexegesimal_to_decimal(
519 ra=ra
520 )
521 dec = self.dec_sexegesimal_to_decimal(
522 dec=dec
523 )
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)
532 cartesians = (cx, cy, cz)
534 self.log.debug('completed the ``ra_dec_to_cartesian`` method')
535 return cartesians
537 # use the tab-trigger below for new method
538 # xt-class-method