128 lines
3.9 KiB
Python
128 lines
3.9 KiB
Python
"""
|
|
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
|