from math import floor, ceil, atan2, asin, sin, cos, tan
import doctest
from .mechanics import Mechanics
from .angles import Angle
from .home import Home
# ========================================================
# ========================================================
# === HORIZON
# ========================================================
# ========================================================
[docs]class Horizon(Mechanics):
""" Class to describe an Horizon
"""
# ========================================================
# === attributs
# ========================================================
_home = Home()
_TYPE_HORIZON_ALTAZ = 0
_TYPE_HORIZON_HADEC = 1
_horizon_type = _TYPE_HORIZON_ALTAZ
_amers_raws = [] ; # raw values
_amers_altaz = [] ; # raw values in degrees
_computed_interpolation_altaz = 0
_horizon_az = [] ; # resampled values
_horizon_elev = [] ; # resampled values
_computed_interpolation_hadec = 0
_horizon_dec = [] ; # resampled values
_horizon_harise = [] ; # resampled values
_horizon_haset = [] ; # resampled values
# ========================================================
# === internal methods : Generals
# ========================================================
def _init_horizon(self, home, *args):
""" Object initialization
"""
# --- home
if isinstance(home, Home) == True:
self._home = home
else:
self._home = Home(home)
#print("self._home={}".format(self._home.gps))
# --- *args
#print("args={}".format(args))
self._amers_raws = []
self._computed_interpolation_altaz = 0
self._computed_interpolation_hadec = 0
self._horizon_type = self._TYPE_HORIZON_ALTAZ
if (len(args)==0):
#h = (0, 20), (10,25), (67, 23)
h = (0,0)
self._horizon_args2amers(h)
else:
self._horizon_args2amers(*args)
# ========================================================
# === internal methods :
# ========================================================
def _horizon_args2amers(self, *args):
""" Record the horizon definition as amers
"""
# --- case of void args
#print("len={}".format(len(args)))
if (len(args)==0):
amers = (0,0) (180,0)
# --- first element is the tuple of amers
amers = args[0]
#print("amers={}".format(amers))
# --- verify that the first element of amers is also a tuple
if isinstance(amers[0], tuple) == False:
if (len(amers)>=2):
amers = [amers, ]
else:
raise Exception
return ""
else:
amers = list(amers)
#print("amers={}".format(amers))
self._amers_raws = amers
def _amers_decode(self):
""" Decodes the amers
_TYPE_HORIZON_ALTAZ : (az1,elev1) (az2,elev2) etc.
_TYPE_HORIZON_HADEC : (dec1,harise1,haset1) (dec2,harise2,haset2) etc.
"""
amers = (self._amers_raws).copy()
# --- identify the type of coordinates.
# --- how many coordinates in each element ? (2=ALTAZ 3=HADEC)
n2 = 0
n3 = 0
#print("amers={}".format(amers))
for amer in amers:
#print("amer={}".format(amer))
n = len(amer)
if (n==2):
n2 += 1
if (n==3):
n3 += 1
#print("n2={} n3={}".format(n2,n3))
if (n2>0) and (n3==0):
self._horizon_type = self._TYPE_HORIZON_ALTAZ
elif (n2==0) and (n3>0):
self._horizon_type = self._TYPE_HORIZON_HADEC
else:
raise Exception
#print("self._horizon_type={}".format(self._horizon_type))
# --- convert coordinates into degrees
angle = Angle()
deg_amers = []
for amer in amers:
degs = []
for angle_raw in amer:
angle.angle(angle_raw)
angle_deg = (angle%360).deg()
degs.append(angle_deg)
deg_amers.append(degs)
# --- if horizontype = HADEC, convert each amer into altaz
if self._horizon_type == self._TYPE_HORIZON_HADEC:
latitude = (self._home).latitude * self._DR
deg_amers = []
for amer in amers:
dec = amer[0] * self._DR
ha1 = amer[1] * self._DR
ha2 = amer[2] * self._DR
az1, elev1 = self._mc_hd2ah(ha1, dec, latitude)
degs = [ az1/self._DR, elev1/self._DR]
deg_amers.append(degs)
az2, elev2 = self._mc_hd2ah(ha2, dec, latitude)
degs = [ az2/self._DR, elev2/self._DR]
deg_amers.append(degs)
# --- sort in azimuts before interpolation
amers = list(sorted(deg_amers, key=lambda item: item[0]))
#print("amers={}".format(amers))
# --- add two extra items to be able to interpolate between [0-360]
first = (amers[0]).copy()
last = (amers[-1]).copy()
last[0] -= 360
amers.insert(0,last)
first[0] += 360
amers.append(first)
#print("amers={}".format(amers))
self._amers_altaz = amers
self._computed_interpolation_altaz = 0
# --- we can make linear interpolations for a resampling
def _amers_interpolation_altaz(self,az_sampling_deg=1):
"""
"""
amers = self._amers_altaz
if amers == [] or self._computed_interpolation_altaz == 1:
return
self._horizon_az = []
self._horizon_elev = []
az = 0
k1 = -1
az2 = -1000 ; # < -360 to oblige the first if az>az2
while (az<=360):
if az > az2:
k1 += 1
az1, elev1 = amers[k1]
az2, elev2 = amers[k1+1]
daz = az2-az1
delev = elev2-elev1
continue
# --- we can interpolate
elev = (az-az1)/daz*delev+elev1
self._horizon_az.append(az)
self._horizon_elev.append(elev)
az += az_sampling_deg
self._computed_interpolation_altaz = 1
def _amers_interpolation_hadec(self,dec_sampling_deg=1):
"""
"""
amers = self._amers_altaz
if amers == [] or self._computed_interpolation_hadec == 1:
return
if self._computed_interpolation_altaz == 0:
self._amers_interpolation_altaz(1)
# ---
az_sampling = self._horizon_az[1] - self._horizon_az[0]
# --- declination limits for visibility
latitude_deg = (self._home).latitude
latitude_rad = latitude_deg * self._DR
coslatitude = cos(latitude_rad)
sinlatitude = sin(latitude_rad)
declim_inf = -90
declim_sup = 90
if (latitude_deg>=0):
declim_inf = latitude_deg - 90
else:
declim_sup = latitude_deg + 90
dec1 = ceil(declim_inf)
dec2 = floor(declim_sup)
# ---
self._horizon_dec = []
self._horizon_harise = []
self._horizon_haset = []
dec_deg = -90
while (dec_deg<=90):
if (dec_deg <= dec1) or (dec_deg >= dec2):
# --- never visible
ha_set = 0
ha_rise = 0
continue
else:
ha_rise_computed = 0
ha_set_computed = 0
ha_rise = -180
ha_set = 180
dec = dec_deg * self._DR
cosdec = cos(dec)
sindec = sin(dec)
tandec = tan(dec)
sinlatitude_sindec = sinlatitude*sindec
coslatitude_cosdec = coslatitude*cosdec
tandec_coslatitude = tandec*coslatitude
ha_deg = -180
while (ha_deg <= 180):
ha = ha_deg * self._PI
sinha = sin(ha)
cosha = cos(ha)
az=atan2(sinha,cosha*sinlatitude-tandec_coslatitude)
el=asin(sinlatitude_sindec+coslatitude_cosdec*cosha)
az /= self._DR
el /= self._DR
if (az<0):
az += 360
kaltaz = int ((az - self._horizon_az[0])/360.*az_sampling)
horizon_elev = self._horizon_elev[kaltaz]
if (ha_rise_computed == 0):
if (el >= horizon_elev):
ha_rise = ha_deg
ha_rise_computed = 1
else:
if (ha_set_computed == 0) and (el <= horizon_elev):
ha_set = ha_deg
ha_set_computed = 1
break
self._horizon_dec.append(dec_deg)
self._horizon_harise.append(ha_rise)
self._horizon_haset.append(ha_set)
self._computed_interpolation_hadec = 1
# ========================================================
# === Horizon methods
# ========================================================
[docs] def horizon(self, home, *args):
""" Object initialization
"""
self._init_horizon(home, *args)
return args
# ========================================================
# === get/set methods
# ========================================================
def _get_horizon_altaz(self):
if (self._computed_interpolation_altaz == 0):
self._amers_decode()
az_sampling_deg=1
self._amers_interpolation_altaz(az_sampling_deg)
return self._horizon_az , self._horizon_elev
def _set_horizon_altaz(self, *args):
self._horizon_args2amers(*args)
def _get_horizon_hadec(self):
if (self._computed_interpolation_hadec == 0):
self._amers_decode()
hadec_sampling_deg=1
self._amers_interpolation_hadec(hadec_sampling_deg)
return self._horizon_dec , self._horizon_harise , self._horizon_haset
def _set_horizon_hadec(self, *args):
self._horizon_args2amers(*args)
def get_horizon_raw_amers(self):
return self._amers_raws
horizon_hadec = property(_get_horizon_hadec, _set_horizon_hadec)
horizon_altaz = property(_get_horizon_altaz, _set_horizon_altaz)
# ========================================================
# === 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:
Horizon().infos("doctest")
Horizon().infos("doc_methods")
Horizon().infos("internal_attributes")
Horizon().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 = "Horizon()."+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, home, *args):
""" Object initialization
"""
self._init_horizon(home, *args)
# super().__init__()