"""
This module contains the class to manage bounding box
"""
# For type annotation
from __future__ import annotations
from typing import Union
import sys
import copy
import matplotlib.pyplot as plt
from tracklib.algo.Geometrics import Polygon
from tracklib.core import ObsCoords as Coords
[docs]class Bbox:
"""Class to represent a boundary box"""
[docs] def __init__(self, ll: Union[Coords.ENUCoords, Coords.ENUCoords, Coords.GeoCoords],
ur: Union[Coords.ENUCoords, Coords.ENUCoords, Coords.GeoCoords]):
"""Constructor of :class:`Bbox`
:param ll: lower left point
:param ur: upper right point
"""
self.ll = ll
self.ur = ur
[docs] def __str__(self) -> str:
"""String representation of :class:`Bbox`
:return: String representation of bbox
"""
output = "Bounding box: \n"
output += " Lower left corner : " + str(self.ll) + "\n"
output += " Upper right corner: " + str(self.ur)
return output
[docs] def copy(self) -> Bbox:
"""Copy the current object
:return: Copy of bbox
"""
return copy.deepcopy(self)
[docs] def getLowerLeft(
self,
) -> Union[Coords.ENUCoords, Coords.ENUCoords, Coords.GeoCoords]:
"""Return the lower-left coordinates of :class:`Bbox`
:return: Lower-left coordinates
"""
return self.ll
[docs] def getUpperRight(
self,
) -> Union[Coords.ENUCoords, Coords.ENUCoords, Coords.GeoCoords]:
"""Return the upper-right coordinates of :class:`Bbox`
:return: Upper-right coordinates
"""
return self.ur
[docs] def getXmin(self) -> float:
"""Return the min X coordinate"""
return self.ll.getX()
[docs] def getYmin(self) -> float:
"""Return the min Y coordinate"""
return self.ll.getY()
[docs] def getXmax(self) -> float:
"""Return the max X coordinate"""
return self.ur.getX()
[docs] def getYmax(self) -> float:
"""Return the max Y coordinate"""
return self.ur.getY()
[docs] def getDx(self) -> float:
"""Return the difference of X coordinates"""
return self.getXmax() - self.getXmin()
[docs] def getDy(self) -> float:
"""Return the difference of Y coordinates"""
return self.getYmax() - self.getYmin()
[docs] def getDimensions(self) -> tuple[float, float]:
"""Return Dx and Dy
:return: Tuple with structure : (Dx, Dy)
"""
return (self.getDx(), self.getDy())
[docs] def setXmin(self, xmin: float):
"""Set Xmin coordinate
:param xmin: Xmin coordinate
"""
self.ll.setX(xmin)
[docs] def setYmin(self, ymin: float):
"""Set Ymin coordinate
:param ymin: Ymin coordinate
"""
return self.ll.setY(ymin)
[docs] def setXmax(self, xmax: float):
"""Set Xmax coordinate
:param xmax: Xmax coordinate
"""
return self.ur.setX(xmax)
[docs] def setYmax(self, ymax: float):
"""Set Ymax coordinate
:param ymax: Ymax coordinate
"""
return self.ur.setY(ymax)
[docs] def plot(self, sym="b-"):
"""TODO"""
X = [
self.getXmin(),
self.getXmax(),
self.getXmax(),
self.getXmin(),
self.getXmin(),
]
Y = [
self.getYmin(),
self.getYmin(),
self.getYmax(),
self.getYmax(),
self.getYmin(),
]
plt.plot(X, Y, sym)
def __add__(self, bbox: Bbox) -> Bbox:
"""Bounding boxes combination
:param bbox: Bbox 2
"""
ll = self.ll.copy()
ur = self.ur.copy()
xmin = min(self.getXmin(), bbox.getXmin())
ymin = min(self.getYmin(), bbox.getYmin())
xmax = max(self.getXmax(), bbox.getXmax())
ymax = max(self.getYmax(), bbox.getYmax())
ll.setX(xmin)
ll.setY(ymin)
ur.setX(xmax)
ur.setY(ymax)
return Bbox(ll, ur)
def __and__(self, bbox):
"""TODO"""
return None # TO DO
[docs] def contains(self, point) -> bool:
"""Check if a point is in the bbox"""
return self.geom().contains(point)
[docs] def translate(self, dx: float, dy: float):
"""Translation (2D) of shape
:param dx: dx in ground units
:param dy: dy in ground units
"""
self.ll.translate(dx, dy)
self.ur.translate(dx, dy)
[docs] def rotate(self, theta: float):
"""Rotation (2D) of shape
:param theta: angle in radians
"""
self.ll.rotate(theta)
self.ur.rotate(theta)
[docs] def scale(self, h: float):
"""Homothetic transformation (2D) of shape
:param h: factor
"""
self.ll.scale(h)
self.ur.scale(h)
[docs] def geom(self) -> Polygon:
"""Convert to Geometrics (Polygon)
:return: Polygon
"""
X = [
self.getXmin(),
self.getXmax(),
self.getXmax(),
self.getXmin(),
self.getXmin(),
]
Y = [
self.getYmin(),
self.getYmin(),
self.getYmax(),
self.getYmax(),
self.getYmin(),
]
return Polygon(X, Y)
[docs] def toECEFCoords(self, base: Union[Coords.ENUCoords, Coords.ENUCoords, Coords.GeoCoords] = None):
"""Coordinate transformation to :class:`core.Coords.ECEFCoords`
:param base: base coordinates, defaults to None
"""
self.ll.toECEFCoords(base)
self.ur.toECEFCoords(base)
[docs] def toGeoCoords(self, base: Union[Coords.ENUCoords, Coords.ENUCoords, Coords.GeoCoords] = None):
"""Coordinate transformation to :class:`core.Coords.GeoCoords`
:param base: base coordinates, defaults to None
"""
self.ll.toGeoCoords(base)
self.ur.toGeoCoords(base)
[docs] def toENUCoords(self, base: Union[Coords.ENUCoords, Coords.ENUCoords, Coords.GeoCoords] = None):
"""Coordinate transformation to :class:`core.Coords.ENUCoords`
:param base: base coordinates, defaults to None
"""
self.ll.toENUCoords(base)
self.ur.toENUCoords(base)
[docs] def addMargin(self, margin: float = 0.05):
"""Adding margin (relative float) to bounding box
:param margin: margin, defaults to 0.05
"""
dx, dy = self.getDimensions()
self.setXmin(self.getXmin() - margin * dx)
self.setXmax(self.getXmax() + margin * dx)
self.setYmin(self.getYmin() - margin * dy)
self.setYmax(self.getYmax() + margin * dy)
def __getitem__(self, index: int) -> float:
"""Get value by index
- 0: xmin
- 1: xmax
- 2: ymin
- 3: ymax
:param index: index of coordinates
:return: coordinate
"""
if (index == 0) or (index == "xmin"):
return self.getXmin()
if (index == 1) or (index == "xmax"):
return self.getXmax()
if (index == 2) or (index == "ymin"):
return self.getYmin()
if (index == 3) or (index == "ymax"):
return self.getYmax()
def __setitem__(self, index: int, value: float):
"""Set value by index
:param index: index of value
:pram value: value to set
"""
if (index == 0) or (index == "xmin"):
self.setXmin(value)
if (index == 1) or (index == "xmax"):
self.setXmax(value)
if (index == 2) or (index == "ymin"):
self.setYmin(value)
if (index == 3) or (index == "ymax"):
self.setYmax(value)
[docs] def asTuple(self) -> tuple[float, float, float, float]:
"""Transform the Bbox object in a tuple of coordinates
:return: Tuple of coordinates with this structure (x min, x max, y min, y max)
"""
return (self.getXmin(), self.getXmax(), self.getYmin(), self.getYmax())