""" Provides conversions from x,y,z to lon,lat,alt. """ import logging from typing import Tuple import pyproj from pyproj import Transformer from core.emulator.enumerations import RegisterTlvs logger = logging.getLogger(__name__) SCALE_FACTOR: float = 100.0 CRS_WGS84: int = 4326 CRS_PROJ: int = 3857 class GeoLocation: """ Provides logic to convert x,y,z coordinates to lon,lat,alt using defined projections. """ name: str = "location" config_type: RegisterTlvs = RegisterTlvs.UTILITY def __init__(self) -> None: """ Creates a GeoLocation instance. """ self.to_pixels: Transformer = pyproj.Transformer.from_crs( CRS_WGS84, CRS_PROJ, always_xy=True ) self.to_geo: Transformer = pyproj.Transformer.from_crs( CRS_PROJ, CRS_WGS84, always_xy=True ) self.refproj: Tuple[float, float, float] = (0.0, 0.0, 0.0) self.refgeo: Tuple[float, float, float] = (0.0, 0.0, 0.0) self.refxyz: Tuple[float, float, float] = (0.0, 0.0, 0.0) self.refscale: float = 1.0 def setrefgeo(self, lat: float, lon: float, alt: float) -> None: """ Set the geospatial reference point. :param lat: latitude reference :param lon: longitude reference :param alt: altitude reference :return: nothing """ self.refgeo = (lat, lon, alt) px, py = self.to_pixels.transform(lon, lat) self.refproj = (px, py, alt) def reset(self) -> None: """ Reset reference data to default values. :return: nothing """ self.refxyz = (0.0, 0.0, 0.0) self.refgeo = (0.0, 0.0, 0.0) self.refscale = 1.0 self.refproj = self.to_pixels.transform(*self.refgeo) def pixels2meters(self, value: float) -> float: """ Provides conversion from pixels to meters. :param value: pixels value :return: pixels value in meters """ return (value / SCALE_FACTOR) * self.refscale def meters2pixels(self, value: float) -> float: """ Provides conversion from meters to pixels. :param value: meters value :return: meters value in pixels """ if self.refscale == 0.0: return 0.0 return SCALE_FACTOR * (value / self.refscale) def getxyz(self, lat: float, lon: float, alt: float) -> Tuple[float, float, float]: """ Convert provided lon,lat,alt to x,y,z. :param lat: latitude value :param lon: longitude value :param alt: altitude value :return: x,y,z representation of provided values """ logger.debug("input lon,lat,alt(%s, %s, %s)", lon, lat, alt) px, py = self.to_pixels.transform(lon, lat) px -= self.refproj[0] py -= self.refproj[1] pz = alt - self.refproj[2] x = self.meters2pixels(px) + self.refxyz[0] y = -(self.meters2pixels(py) + self.refxyz[1]) z = self.meters2pixels(pz) + self.refxyz[2] logger.debug("result x,y,z(%s, %s, %s)", x, y, z) return x, y, z def getgeo(self, x: float, y: float, z: float) -> Tuple[float, float, float]: """ Convert provided x,y,z to lon,lat,alt. :param x: x value :param y: y value :param z: z value :return: lat,lon,alt representation of provided values """ logger.debug("input x,y(%s, %s)", x, y) x -= self.refxyz[0] y = -(y - self.refxyz[1]) if z is None: z = self.refxyz[2] else: z -= self.refxyz[2] px = self.refproj[0] + self.pixels2meters(x) py = self.refproj[1] + self.pixels2meters(y) lon, lat = self.to_geo.transform(px, py) alt = self.refgeo[2] + self.pixels2meters(z) logger.debug("result lon,lat,alt(%s, %s, %s)", lon, lat, alt) return lat, lon, alt