import math
import doctest
from .angles import Angle
# ========================================================
# ========================================================
# === COORD
# ========================================================
# ========================================================
[docs]class Coords:
""" Class to convert coordinates for astronomy
Coordinate formats are:
* cart = tuple of x, xy, xyz
* sphe = tuple of r, rp, rpt
:Usage:
First, instanciate an object from the class:
::
coord = Coords()
Second, assign a coordinate in any format:
::
xyz = (x, y, z)
coord.coords(xyz)
or
::
phi = Angle(45)
theta = Angle(120)
rpt = (r, phi, theta)
coord.coords(rpt)
Third, get the converted angles:
::
r, phi, theta = coord.sphe()
x,y,y = coord.cart()
Fourth, some tools
::
coord.separation(coord2)
coord.pole(coord2)
coord.scalar_product(coord2)
coord.vectorial_product(coord2)
:Informations:
::
help(Coords)
Coords().infos("doctest")
Coords().infos("doc_methods")
"""
# ========================================================
# === attributs
# ========================================================
_TYPE_UNKN = 0
_TYPE_CART = 1
_TYPE_SPHE = 2
_X = 0
_Y = 1
_Z = 2
_R = 0
_PHI = 1
_THETA = 2
# ---
_x = 0
_y = 0
_z = 0
_r = 0
_phi = 0
_theta = 0
_cos_phi = 1
_sin_phi = 0
_cos_theta = 1
_sin_theta = 0
_computed_rpt = 0
_computed_xyz = 0
_init_cart_or_sphe = (0, 0, 0)
_init_type = _TYPE_UNKN
# ========================================================
# === internal methods
# ========================================================
def _coord_cart_or_sphe_decode(self,cart_or_sphe):
""" Decode the cart_or_sphe tuple into three floats and the type.
"""
dim = len(cart_or_sphe)
# --- Verify integrity of the input tuple
if (dim>=1):
p1 = cart_or_sphe[0]
if isinstance(p1, (int, float)) == False:
raise TypeError
return ""
if (dim>=2):
p2 = cart_or_sphe[1]
if isinstance(p2, (int, float, Angle)) == False:
raise TypeError
return ""
if (dim>=3):
p3 = cart_or_sphe[2]
if isinstance(p3, (int, float, Angle)) == False:
raise TypeError
return ""
else:
raise TypeError
return ""
# ---
x = 0
y = 0
z = 0
r = 0
phi = 0
theta = 0
if (dim==1):
init_type =self. _TYPE_CART
x = cart_or_sphe[self._X]
if (dim>=2):
p1 = cart_or_sphe[0]
p2 = cart_or_sphe[1]
if isinstance(p2, (int, float)) == True:
init_type = self._TYPE_CART
x = cart_or_sphe[self._X]
y = cart_or_sphe[self._Y]
else:
init_type = self._TYPE_SPHE
if isinstance(p2, Angle) == False:
p2 = Angle(p2)
r = cart_or_sphe[self._R]
phi = p2
if (dim==3):
p3 = cart_or_sphe[2]
if (isinstance(p2, (int, float)) == True) and (isinstance(p3, (int, float)) == True):
init_type = self._TYPE_CART
x = cart_or_sphe[self._X]
y = cart_or_sphe[self._Y]
z = cart_or_sphe[self._Z]
else:
init_type =self. _TYPE_SPHE
if isinstance(p2, Angle) == False:
p2 = Angle(p2)
if isinstance(p3, Angle) == False:
p3 = Angle(p3)
r = cart_or_sphe[self._R]
phi = p2
theta = p3
ppp = (0,0,0)
if init_type == self._TYPE_CART:
ppp = (x, y, z)
elif init_type == self._TYPE_SPHE:
ppp = (r, phi.rad(), theta.rad())
return init_type, ppp
def _init(self,cart_or_sphe=(0,0,0)):
""" Initialize internal attributes.
:param cart_or_sphe: Cartesian or spherical coordinates. If the tupple contains two elements it is spherical coordinates. If the tupple contains three elements it is cartesian coordinates.
:type cart_or_sphe: tuple
:Example:
objangle = Coords()
objangle._init()
"""
self._computed_rpt = 0
self._computed_xyz = 0
self._init_dim = 0
self._init_type = self._TYPE_UNKN
init_type, ppp = self._coord_cart_or_sphe_decode(cart_or_sphe)
# print("init_type={} ppp={} ".format(init_type, ppp ))
# ---
if init_type == self._TYPE_UNKN:
raise Exception
return ""
self._init_type = init_type
self._cos_phi = 1
self._sin_phi = 0
self._cos_theta = 1
self._sin_theta = 0
if self._init_type == self._TYPE_CART:
self._x = ppp[self._X]
self._y = ppp[self._Y]
self._z = ppp[self._Z]
xyz = (self._x, self._y, self._z)
res = self.coord_xyz2rpt(xyz)
self._r, self._phi, self._theta, self._cos_phi, self._sin_phi, self._cos_theta, self._sin_theta = res
else:
self._r = ppp[self._R]
self._phi = ppp[self._PHI]
self._theta = ppp[self._THETA]
rpt = (self._r, self._phi, self._theta)
res = self.coord_rpt2xyz(rpt)
self._x, self._y, self._z, self._cos_phi, self._sin_phi, self._cos_theta, self._sin_theta = res
self._computed_rpt = 1
self._computed_xyz = 1
self._init_cart_or_sphe = cart_or_sphe
def _coord_compare(self, coords, operator):
""" Comparaison of radius coords for various operators.
:param coords: An coords in any supported format (cf. help(Coords))
:type coords: Coords()
:param operator: Operator such as == != > >= < <=
:type operator: string
:returns: The logic result of the comparison.
:rtype: bool
:Example:
>>> objcoord1 = Coords()
>>> objcoord2 = Coords()
>>> objcoord1.coords((1,2,3)) ; objcoord2.coords((4,5,6)) ; objcoord1._coord_compare(objcoord2,">")
(1, 2, 3)
(4, 5, 6)
False
"""
if isinstance(coords, Coords) == False:
coords = Coords(coords)
res = False
if (self._computed_rpt == 1) and (coords._computed_rpt == 1):
toeval = str(self._r)+" "+operator+" "+str(coords._r)
res = eval(toeval)
return res
# ========================================================
# === coords methods
# ========================================================
def coord_xyz2rpt(self, xyz):
if (self._computed_rpt ==0):
x, y, z = xyz
r = math.sqrt(x*x + y*y + z*z)
phi = math.atan2(y,x)
if (r==0):
theta = 0
else:
theta = math.asin(z/r)
cos_theta = math.cos(theta)
sin_theta = math.sin(theta)
cos_phi = math.cos(phi)
sin_phi = math.sin(phi)
return r, phi, theta, cos_phi, sin_phi, cos_theta, sin_theta
else:
return self._r, self._phi, self._theta, self._cos_phi, self._sin_phi, self._cos_theta, self._sin_theta
def coord_rpt2xyz(self, rpt):
if (self._computed_xyz ==0):
r, phi, theta = rpt
cos_theta = math.cos(theta)
sin_theta = math.sin(theta)
cos_phi = math.cos(phi)
sin_phi = math.sin(phi)
d = r * cos_theta
x = d * cos_phi
y = d * sin_phi
z = r * sin_theta
return x, y, z, cos_phi, sin_phi, cos_theta, sin_theta
else:
return self._x, self._y, self._z, self._cos_phi, self._sin_phi, self._cos_theta, self._sin_theta
def great_circle_distance(self, coords):
if isinstance(coords, Coords) == False:
coords = Coords(coords)
dx = coords._cos_theta * coords._cos_phi - self._cos_theta * self._cos_phi
dy = coords._cos_theta * coords._sin_phi - self._cos_theta * self._sin_phi
dz = coords._sin_theta - self._sin_theta
#print("dd={} dz={} ct1={} ct2={}".format(math.sqrt(dx*dx + dy*dy), dz,coords._cos_theta,self._cos_theta))
c = math.sqrt(dx*dx + dy*dy + dz*dz)
d = 2*math.asin(c/2)
return d * Angle()._r2d
# ========================================================
# === get/set methods
# ========================================================
[docs] def coords(self, cart_or_sphe):
""" Set the input cart_or_sphe in any format
:param cart_or_sphe: Cartesian or spherical coordinates. If the tupple contains two elements it is spherical coordinates. If the tupple contains three elements it is cartesian coordinates.
:type cart_or_sphe: tuple
Angles are in any supported format (cf. help(Coords))
:Example:
>>> objcoord = Coords()
>>> objcoord.coords((1,3,6))
(1, 3, 6)
:Related topics:
After using objcoord.coords() get conversions with methods as objcoord.cart() or objcoord.sphe().
"""
if cart_or_sphe != "":
if (cart_or_sphe != self._init_cart_or_sphe):
self._init(cart_or_sphe)
self.init_cart_or_sphe = cart_or_sphe
return self.init_cart_or_sphe
[docs] def cart(self):
""" Get the cartesian coordinates
:returns: The tuple (x,y,z)
:rtype: tuple(float, float, float)
:Example:
>>> objcoord = Coords()
>>> objcoord.coords((1,3,6))
(1, 3, 6)
>>> objcoord.cart()
(1, 3, 6)
.. note:: Before use objcoord.cart() to set the input coords with objcoord.coords().
"""
if (self._computed_xyz == 0):
raise Exception
return ""
return self._x, self._y, self._z
[docs] def sphe(self,format_phi="deg",format_theta="deg"):
""" Get the spherical coordinates
:param format_phi: Angle format unit.
:type format_phi: str
:param format_theta: Angle format unit.
:type format_theta: str
:returns: The tuple (x,y,z)
:rtype: tuple(float, float, float)
:Example:
>>> objcoord = Coords()
>>> objcoord.coords((1,3,6))
(1, 3, 6)
>>> objcoord.sphe("H","d")
(6.782329983125268, '4h46m15.61s', '62d12m31.30s')
.. note:: Before use objcoord.sphe() to set the input coords with objcoord.coords().
"""
if (self._computed_rpt == 0):
raise Exception
return ""
if format_phi=="rad":
pout = self._phi
elif format_phi=="deg":
pout = self._phi * Angle()._r2d
else:
p = Angle(self._phi * Angle()._r2d)
pout = p.sexagesimal(format_phi)
if format_theta=="rad":
tout = self._theta
elif format_theta=="deg":
tout = self._theta * Angle()._r2d
else:
t = Angle(self._theta * Angle()._r2d)
tout = t.sexagesimal(format_theta)
return self._r, pout, tout
# ========================================================
# === debug methods
# ========================================================
[docs] def infos(self, action) -> None:
""" To get informations about this class
:param action: A command to run a debug action (see examples).
:type action: string
:Example:
Coords().infos("doctest")
Coords().infos("doc_methods")
Coords().infos("internal_attributes")
Coords().infos("public_methods")
"""
if (action == "doc_methods"):
publics = [x for x in dir(self) if x[0]!="_"]
for public in publics:
varname = "{}".format(public)
if (callable(getattr(self,varname))==True):
print("\n{:=^40}".format(" method "+varname+" "))
t = "Coords()."+varname+".__doc__"
tt =eval(t)
print(tt)
if (action == "doctest"):
if __name__ == "__main__":
print("\n{:~^40}".format("doctest"))
doctest.testmod(verbose=False)
if (action == "internal_attributes"):
internals = [x for x in dir(self) if x[0]=="_" and x[1]!="_"]
for internal in internals:
varname = "{}".format(internal)
#if (hasattr(self,varname)==True):
if (callable(getattr(self,varname))==False):
print(varname + "=" + str(getattr(self,varname)))
if (action == "public_methods"):
publics = [x for x in dir(self) if x[0]!="_"]
for public in publics:
varname = "{}".format(public)
if (callable(getattr(self,varname))==True):
print(varname)
# ========================================================
# === special methods
# ========================================================
def __init__(self, cart_or_sphe=(0,0,0)):
""" Object initialization where cart_or_sphe is the input tuple of cartesian or spheric coordinates.
:param cart_or_sphe: An angle in any supported format (cf. help(Angle))
:type cart_or_sphe: tuple
cartesian coords = tuple(float, float, float)
spheric coords = tuple(float, Angle, Angle)
"""
self._init(cart_or_sphe)