Module losanalyst.classes.los
Expand source code
import math
from osgeo import ogr
from gdalhelpers.helpers import math_helpers
from typing import List
class LoS:
"""
Basic class representing LoS. From this class the specific types of LoS (local, global, without target) are derived.
Attributes
----------
points : list of list of float
List of points that represents LoS. The structure is [[X1, Y1, Z1], [X2, Y2, Z2] ... [Xn, Yn, Zn]].
is_global : bool
Is the LoS global?
is_without_target: bool
Is the LoS without target?
observer_offset, target_offset : float
Values representing offset of observer and target.
target_x, target_y : float
Coordinates of the target point. Necessary for global LoS.
use_curvature_corrections : bool, optional
Calculate Earth curvature corrections while analyzing LoS. Default value is `True`.
refraction_coefficient : float, optional
Refraction coefficient. Default value is `0.13`.
target_index: int
Index of target point in `points`. Important for global LoS, for local LoS the value is `len(points)-1`.
previous_max_angle : list of floats
Maximal view angle before the given point (in `points`).
visible : list of bool
Is the given point (from `points`) visible?
horizon : list of bool
Is the given point (from `points`) horizon?
"""
def __init__(self,
points: List[List[float]],
is_global: bool = False,
is_without_target: bool = False,
observer_offset: float = 0,
target_offset: float = 0,
target_x: float = 0,
target_y: float = 0,
sampling_distance: float = None,
use_curvature_corrections: bool = True,
refraction_coefficient: float = 0.13):
"""
Constructor of Los.
Parameters
----------
points : list of list of float
List of points that represents LoS. The structure is [[X1, Y1, Z1], [X2, Y2, Z2] ... [Xn, Yn, Zn]].
is_global : bool
Is the LoS global?
is_without_target: bool
Is the LoS without target?
observer_offset, target_offset : float
Values representing offset of observer and target.
target_x, target_y : float
Coordinates of the target point. Necessary for global LoS.
sampling_distance : float, optional
Sampling distance on Los if it known. Otherwise it is estimated from `points`.
use_curvature_corrections : bool, optional
Calculate Earth curvature corrections while analyzing LoS. Default value is `True`.
refraction_coefficient : float, optional
Refraction coefficient. Default value is `0.13`.
"""
self.is_global: bool = is_global
self.is_without_target: bool = is_without_target
self.use_curvature_corrections: bool = use_curvature_corrections
self.refraction_coefficient: float = refraction_coefficient
self.observer_offset: float = observer_offset
self.target_offset: float = target_offset
self.target_x: float = target_x
self.target_y: float = target_y
self.target_index: int = None
if sampling_distance is None:
sampling_distance = math_helpers.distance(points[0][0], points[0][1], points[1][0], points[1][1])
first_point_x = points[0][0]
first_point_y = points[0][1]
first_point_z = points[0][2] + observer_offset
target_distance = math_helpers.distance(first_point_x, first_point_y, target_x, target_y)
self.points: List = []
self.previous_max_angle: List = []
self.visible: List = []
self.horizon: List = []
max_angle_temp = -180
for i in range(0, len(points)):
point_x = points[i][0]
point_y = points[i][1]
point_z = points[i][2]
distance = math_helpers.distance(first_point_x, first_point_y, point_x, point_y)
if self.use_curvature_corrections:
point_z = self._curvature_corrections(point_z, distance, self.refraction_coefficient)
target_offset = self._curvature_corrections(target_offset, distance, self.refraction_coefficient)
if i == 0:
self.points.append([point_x, point_y, 0, first_point_z, -90])
elif self.is_global and math.fabs(target_distance - distance) < sampling_distance / 2:
self.points.append(
[point_x, point_y, distance, point_z + target_offset,
self._angle_vertical(distance, point_z + target_offset - first_point_z)])
self.target_index = i
else:
self.points.append([point_x, point_y, distance, point_z,
self._angle_vertical(distance, point_z - first_point_z)])
# first store max angle before this point and then add new max angle
self.previous_max_angle.append(max_angle_temp)
if max_angle_temp < self.points[-1][4]:
if self.is_global:
if i != self.target_index:
max_angle_temp = self.points[-1][4]
else:
max_angle_temp = self.points[-1][4]
# is visible is only valid if previous_max_angle is smaller then current angle
if i == 0:
self.visible.append(True)
else:
self.visible.append(self.previous_max_angle[i] < self.points[-1][4])
for i in range(0, len(self.points)):
if i == len(self.points) - 1:
self.horizon.append(False)
else:
self.horizon.append((self.visible[i] is True) and (self.visible[i+1] is False))
if self.is_global:
self.limit_angle = self.points[self.target_index][4]
self.is_visible = True
def __str__(self):
"""
String representation of LoS.
Returns
-------
str
"""
string = ""
for i in range(0, len(self.points)):
string += ("{} - {} {} {} (prev. {}) - vis. {} hor. {} \n".format(
i,
self.points[i][2],
self.points[i][3],
self.points[i][4],
self.previous_max_angle[i],
self.visible[i],
self.horizon[i]
))
return string
@staticmethod
def _angle_vertical(distance: float, elev_diff: float) -> float:
"""
Calculate vertical angle based on distance and elevation difference.
Parameters
----------
distance : float
elev_diff : float
Returns
-------
float
Angle.
"""
if distance == 0:
return 90
else:
return math.degrees(math.atan(elev_diff / distance))
@staticmethod
def _curvature_corrections(elev: float, dist: float,
ref_coeff: float, earth_diameter: float = 12740000) -> float:
"""
Calculate Earth's curvature correction based on elevation and distance with given refraction coefficient.
Returns updated value of elevation.
Parameters
----------
elev : float
dist : float
ref_coeff : float
earth_diameter : float
Returns
-------
float
Elevation including curvature corrections.
"""
return elev - (math.pow(dist, 2) / earth_diameter) + ref_coeff * (math.pow(dist, 2) / earth_diameter)
def _get_geom_at_index(self, index: int) -> ogr.Geometry:
"""
Return geometry (point) from `points` at specific index.
Parameters
----------
index : int
Returns
-------
ogr.Geometry
Returns geometry with specification `ogr.wkbPoint25D`.
"""
point = ogr.Geometry(ogr.wkbPoint25D)
point.AddPoint(self.points[index][0],
self.points[index][1],
self.points[index][3])
return point
def get_horizons(self) -> List[ogr.Geometry]:
"""
Return all horizons from `horizon` list as list of geometries.
Returns
-------
list of ogr.Geometry
Returns list of geometries with specification `ogr.wkbPoint25D`.
"""
points: List[ogr.Geometry] = []
for i in range(0, len(self.horizon)):
if self.horizon[i]:
points.append(self._get_geom_at_index(i))
return points
Classes
class LoS (points, is_global=False, is_without_target=False, observer_offset=0, target_offset=0, target_x=0, target_y=0, sampling_distance=None, use_curvature_corrections=True, refraction_coefficient=0.13)
-
Basic class representing LoS. From this class the specific types of LoS (local, global, without target) are derived.
Attributes
points
:list
oflist
offloat
- List of points that represents LoS. The structure is [[X1, Y1, Z1], [X2, Y2, Z2] … [Xn, Yn, Zn]].
is_global
:bool
- Is the LoS global?
is_without_target
:bool
- Is the LoS without target?
observer_offset
,target_offset
:float
- Values representing offset of observer and target.
target_x
,target_y
:float
- Coordinates of the target point. Necessary for global LoS.
use_curvature_corrections
:bool
, optional- Calculate Earth curvature corrections while analyzing LoS. Default value is
True
. refraction_coefficient
:float
, optional- Refraction coefficient. Default value is
0.13
. target_index
:int
- Index of target point in
points
. Important for global LoS, for local LoS the value islen(points)-1
. previous_max_angle
:list
offloats
- Maximal view angle before the given point (in
points
). visible
:list
ofbool
- Is the given point (from
points
) visible? horizon
:list
ofbool
- Is the given point (from
points
) horizon?
Constructor of Los.
Parameters
points
:list
oflist
offloat
- List of points that represents LoS. The structure is [[X1, Y1, Z1], [X2, Y2, Z2] … [Xn, Yn, Zn]].
is_global
:bool
- Is the LoS global?
is_without_target
:bool
- Is the LoS without target?
observer_offset
,target_offset
:float
- Values representing offset of observer and target.
target_x
,target_y
:float
- Coordinates of the target point. Necessary for global LoS.
sampling_distance
:float
, optional- Sampling distance on Los if it known. Otherwise it is estimated from
points
. use_curvature_corrections
:bool
, optional- Calculate Earth curvature corrections while analyzing LoS. Default value is
True
. refraction_coefficient
:float
, optional- Refraction coefficient. Default value is
0.13
.
Expand source code
class LoS: """ Basic class representing LoS. From this class the specific types of LoS (local, global, without target) are derived. Attributes ---------- points : list of list of float List of points that represents LoS. The structure is [[X1, Y1, Z1], [X2, Y2, Z2] ... [Xn, Yn, Zn]]. is_global : bool Is the LoS global? is_without_target: bool Is the LoS without target? observer_offset, target_offset : float Values representing offset of observer and target. target_x, target_y : float Coordinates of the target point. Necessary for global LoS. use_curvature_corrections : bool, optional Calculate Earth curvature corrections while analyzing LoS. Default value is `True`. refraction_coefficient : float, optional Refraction coefficient. Default value is `0.13`. target_index: int Index of target point in `points`. Important for global LoS, for local LoS the value is `len(points)-1`. previous_max_angle : list of floats Maximal view angle before the given point (in `points`). visible : list of bool Is the given point (from `points`) visible? horizon : list of bool Is the given point (from `points`) horizon? """ def __init__(self, points: List[List[float]], is_global: bool = False, is_without_target: bool = False, observer_offset: float = 0, target_offset: float = 0, target_x: float = 0, target_y: float = 0, sampling_distance: float = None, use_curvature_corrections: bool = True, refraction_coefficient: float = 0.13): """ Constructor of Los. Parameters ---------- points : list of list of float List of points that represents LoS. The structure is [[X1, Y1, Z1], [X2, Y2, Z2] ... [Xn, Yn, Zn]]. is_global : bool Is the LoS global? is_without_target: bool Is the LoS without target? observer_offset, target_offset : float Values representing offset of observer and target. target_x, target_y : float Coordinates of the target point. Necessary for global LoS. sampling_distance : float, optional Sampling distance on Los if it known. Otherwise it is estimated from `points`. use_curvature_corrections : bool, optional Calculate Earth curvature corrections while analyzing LoS. Default value is `True`. refraction_coefficient : float, optional Refraction coefficient. Default value is `0.13`. """ self.is_global: bool = is_global self.is_without_target: bool = is_without_target self.use_curvature_corrections: bool = use_curvature_corrections self.refraction_coefficient: float = refraction_coefficient self.observer_offset: float = observer_offset self.target_offset: float = target_offset self.target_x: float = target_x self.target_y: float = target_y self.target_index: int = None if sampling_distance is None: sampling_distance = math_helpers.distance(points[0][0], points[0][1], points[1][0], points[1][1]) first_point_x = points[0][0] first_point_y = points[0][1] first_point_z = points[0][2] + observer_offset target_distance = math_helpers.distance(first_point_x, first_point_y, target_x, target_y) self.points: List = [] self.previous_max_angle: List = [] self.visible: List = [] self.horizon: List = [] max_angle_temp = -180 for i in range(0, len(points)): point_x = points[i][0] point_y = points[i][1] point_z = points[i][2] distance = math_helpers.distance(first_point_x, first_point_y, point_x, point_y) if self.use_curvature_corrections: point_z = self._curvature_corrections(point_z, distance, self.refraction_coefficient) target_offset = self._curvature_corrections(target_offset, distance, self.refraction_coefficient) if i == 0: self.points.append([point_x, point_y, 0, first_point_z, -90]) elif self.is_global and math.fabs(target_distance - distance) < sampling_distance / 2: self.points.append( [point_x, point_y, distance, point_z + target_offset, self._angle_vertical(distance, point_z + target_offset - first_point_z)]) self.target_index = i else: self.points.append([point_x, point_y, distance, point_z, self._angle_vertical(distance, point_z - first_point_z)]) # first store max angle before this point and then add new max angle self.previous_max_angle.append(max_angle_temp) if max_angle_temp < self.points[-1][4]: if self.is_global: if i != self.target_index: max_angle_temp = self.points[-1][4] else: max_angle_temp = self.points[-1][4] # is visible is only valid if previous_max_angle is smaller then current angle if i == 0: self.visible.append(True) else: self.visible.append(self.previous_max_angle[i] < self.points[-1][4]) for i in range(0, len(self.points)): if i == len(self.points) - 1: self.horizon.append(False) else: self.horizon.append((self.visible[i] is True) and (self.visible[i+1] is False)) if self.is_global: self.limit_angle = self.points[self.target_index][4] self.is_visible = True def __str__(self): """ String representation of LoS. Returns ------- str """ string = "" for i in range(0, len(self.points)): string += ("{} - {} {} {} (prev. {}) - vis. {} hor. {} \n".format( i, self.points[i][2], self.points[i][3], self.points[i][4], self.previous_max_angle[i], self.visible[i], self.horizon[i] )) return string @staticmethod def _angle_vertical(distance: float, elev_diff: float) -> float: """ Calculate vertical angle based on distance and elevation difference. Parameters ---------- distance : float elev_diff : float Returns ------- float Angle. """ if distance == 0: return 90 else: return math.degrees(math.atan(elev_diff / distance)) @staticmethod def _curvature_corrections(elev: float, dist: float, ref_coeff: float, earth_diameter: float = 12740000) -> float: """ Calculate Earth's curvature correction based on elevation and distance with given refraction coefficient. Returns updated value of elevation. Parameters ---------- elev : float dist : float ref_coeff : float earth_diameter : float Returns ------- float Elevation including curvature corrections. """ return elev - (math.pow(dist, 2) / earth_diameter) + ref_coeff * (math.pow(dist, 2) / earth_diameter) def _get_geom_at_index(self, index: int) -> ogr.Geometry: """ Return geometry (point) from `points` at specific index. Parameters ---------- index : int Returns ------- ogr.Geometry Returns geometry with specification `ogr.wkbPoint25D`. """ point = ogr.Geometry(ogr.wkbPoint25D) point.AddPoint(self.points[index][0], self.points[index][1], self.points[index][3]) return point def get_horizons(self) -> List[ogr.Geometry]: """ Return all horizons from `horizon` list as list of geometries. Returns ------- list of ogr.Geometry Returns list of geometries with specification `ogr.wkbPoint25D`. """ points: List[ogr.Geometry] = [] for i in range(0, len(self.horizon)): if self.horizon[i]: points.append(self._get_geom_at_index(i)) return points
Subclasses
Methods
def get_horizons(self)
-
Return all horizons from
horizon
list as list of geometries.Returns
list
ofogr.Geometry
- Returns list of geometries with specification
ogr.wkbPoint25D
.
Expand source code
def get_horizons(self) -> List[ogr.Geometry]: """ Return all horizons from `horizon` list as list of geometries. Returns ------- list of ogr.Geometry Returns list of geometries with specification `ogr.wkbPoint25D`. """ points: List[ogr.Geometry] = [] for i in range(0, len(self.horizon)): if self.horizon[i]: points.append(self._get_geom_at_index(i)) return points