Module pymap3d.lox

isometric latitude, meridian distance

Source code
""" isometric latitude, meridian distance """
try:
    from numpy import radians, degrees, cos, arctan2 as atan2, tan, pi, ndarray, vectorize
except ImportError:
    from math import radians, degrees, cos, atan2, tan, pi

    vectorize = None
import typing
from .ellipsoid import Ellipsoid
from .rcurve import rcurve_parallel
from .rsphere import rsphere_rectifying
from .latitude import geodetic2rectifying, rectifying2geodetic, geodetic2isometric, geodetic2authalic, authalic2geodetic
from .utils import sph2cart, cart2sph

__all__ = ["loxodrome_inverse", "loxodrome_direct", "meridian_arc", "meridian_dist", "departure", "meanm"]


def meridian_dist(lat: float, ell: Ellipsoid = None, deg: bool = True) -> float:
    """
    Computes the ground distance on an ellipsoid from the equator to the input latitude.

    Parameters
    ----------
    lat : float
        geodetic latitude
    ell : Ellipsoid, optional
         reference ellipsoid (default WGS84)
    deg : bool, optional
         degrees input/output  (False: radians in/out)

    Results
    -------
    dist : float
         distance (meters)
    """
    return meridian_arc(0, lat, ell, deg)


def meridian_arc(lat1: float, lat2: float, ell: Ellipsoid = None, deg: bool = True) -> float:
    """
    Computes the ground distance on an ellipsoid between two latitudes.

    Parameters
    ----------
    lat1, lat2 : float
        geodetic latitudes
    ell : Ellipsoid, optional
         reference ellipsoid (default WGS84)
    deg : bool, optional
         degrees input/output  (False: radians in/out)

    Results
    -------
    dist : float
         distance (meters)
    """

    if deg:
        lat1, lat2 = radians(lat1), radians(lat2)

    rlat1 = geodetic2rectifying(lat1, ell, deg=False)
    rlat2 = geodetic2rectifying(lat2, ell, deg=False)

    return rsphere_rectifying(ell) * abs(rlat2 - rlat1)


def loxodrome_inverse(
    lat1: float, lon1: float, lat2: float, lon2: float, ell: Ellipsoid = None, deg: bool = True
) -> typing.Tuple[float, float]:
    """
    computes the arc length and azimuth of the loxodrome
    between two points on the surface of the reference ellipsoid

    like Matlab distance('rh',...) and azimuth('rh',...)

    Parameters
    ----------

    lat1 : float
        geodetic latitude of first point
    lon1 : float
        geodetic longitude of first point
    lat2 : float
        geodetic latitude of second point
    lon2 : float
        geodetic longitude of second point
    ell : Ellipsoid, optional
         reference ellipsoid (default WGS84)
    deg : bool, optional
         degrees input/output  (False: radians in/out)

    Results
    -------

    lox_s : float
        distance along loxodrome
    az12 : float
        azimuth of loxodrome (degrees/radians)

    Based on Deakin, R.E., 2010, 'The Loxodrome on an Ellipsoid', Lecture Notes,
    School of Mathematical and Geospatial Sciences, RMIT University, January 2010

    [1] Bowring, B.R., 1985, 'The geometry of the loxodrome on the
    ellipsoid', The Canadian Surveyor, Vol. 39, No. 3, Autumn 1985,
    pp.223-230.
    [2] Snyder, J.P., 1987, Map Projections-A Working Manual. U.S.
    Geological Survey Professional Paper 1395. Washington, DC: U.S.
    Government Printing Office, pp.15-16 and pp. 44-45.
    [3] Thomas, P.D., 1952, Conformal Projections in Geodesy and
    Cartography, Special Publication No. 251, Coast and Geodetic
    Survey, U.S. Department of Commerce, Washington, DC: U.S.
    Government Printing Office, p. 66.
    """
    if vectorize is not None:
        fun = vectorize(loxodrome_inverse_point)
        return fun(lat1, lon1, lat2, lon2, ell, deg)
    else:
        return loxodrome_inverse_point(lat1, lon1, lat2, lon2, ell, deg)


def loxodrome_inverse_point(
    lat1: float, lon1: float, lat2: float, lon2: float, ell: Ellipsoid = None, deg: bool = True
) -> typing.Tuple[float, float]:
    if deg:
        lat1, lon1, lat2, lon2 = radians(lat1), radians(lon1), radians(lat2), radians(lon2)

    # compute changes in isometric latitude and longitude between points
    disolat = geodetic2isometric(lat2, deg=False, ell=ell) - geodetic2isometric(lat1, deg=False, ell=ell)
    dlon = lon2 - lon1

    # compute azimuth
    az12 = atan2(dlon, disolat)
    cosaz12 = cos(az12)

    # compute distance along loxodromic curve
    if abs(cosaz12) < 1e-9:  # straight east or west
        dist = departure(lon2, lon1, lat1, ell, deg=False)
    else:
        dist = meridian_arc(lat2, lat1, deg=False, ell=ell) / abs(cos(az12))

    if deg:
        az12 = degrees(az12) % 360.0

    return dist, az12


def loxodrome_direct(
    lat1: float, lon1: float, rng: float, a12: float, ell: Ellipsoid = None, deg: bool = True
) -> typing.Tuple[float, float]:
    """
    Given starting lat, lon with arclength and azimuth, compute final lat, lon

    like Matlab reckon('rh', ...)

    Parameters
    ----------
    lat1 : float
        inital geodetic latitude (degrees)
    lon1 : float
        initial geodetic longitude (degrees)
    rng : float
        ground distance (meters)
    a12 : float
        azimuth (degrees) clockwide from north.
    ell : Ellipsoid, optional
          reference ellipsoid
    deg : bool, optional
         degrees input/output  (False: radians in/out)

    Results
    -------
    lat2 : float
        final geodetic latitude (degrees)
    lon2 : float
        final geodetic longitude (degrees)
    """
    if vectorize is not None:
        fun = vectorize(loxodrome_direct_point)
        return fun(lat1, lon1, rng, a12, ell, deg)
    else:
        return loxodrome_direct_point(lat1, lon1, rng, a12, ell, deg)


def loxodrome_direct_point(
    lat1: float, lon1: float, rng: float, a12: float, ell: Ellipsoid = None, deg: bool = True
) -> typing.Tuple[float, float]:

    if deg:
        lat1, lon1, a12 = radians(lat1), radians(lon1), radians(a12)

    if abs(lat1) > pi / 2:
        raise ValueError("-90 <= latitude <= 90")
    if rng < 0:
        raise ValueError("ground distance must be >= 0")

    #   compute rectifying sphere latitude and radius
    reclat = geodetic2rectifying(lat1, ell, deg=False)

    # compute the new points
    cosaz = cos(a12)
    lat2 = reclat + (rng / rsphere_rectifying(ell)) * cosaz  # compute rectifying latitude
    lat2 = rectifying2geodetic(lat2, ell, deg=False)  # transform to geodetic latitude

    newiso = geodetic2isometric(lat2, ell, deg=False)
    iso = geodetic2isometric(lat1, ell, deg=False)

    dlon = tan(a12) * (newiso - iso)
    lon2 = lon1 + dlon

    if deg:
        lat2, lon2 = degrees(lat2), degrees(lon2)

    return lat2, lon2


def departure(lon1: float, lon2: float, lat: float, ell: Ellipsoid = None, deg: bool = True) -> float:
    """
    Computes the distance along a specific parallel between two meridians.

    like Matlab departure()

    Parameters
    ----------
    lon1, lon2 : float
        geodetic longitudes (degrees)
    lat : float
        geodetic latitude (degrees)
    ell : Ellipsoid, optional
          reference ellipsoid
    deg : bool, optional
         degrees input/output  (False: radians in/out)

    Returns
    -------
    dist: float
        ground distance (meters)
    """
    if deg:
        lon1, lon2, lat = radians(lon1), radians(lon2), radians(lat)

    return rcurve_parallel(lat, ell, deg=False) * ((lon2 - lon1) % pi)


def meanm(lat: "ndarray", lon: "ndarray", ell: Ellipsoid = None, deg: bool = True) -> typing.Tuple[float, float]:
    """
    Computes geographic mean for geographic points on an ellipsoid

    like Matlab meanm()

    Parameters
    ----------
    lat : sequence of float
        geodetic latitude (degrees)
    lon : sequence of float
        geodetic longitude (degrees)
    ell : Ellipsoid, optional
          reference ellipsoid
    deg : bool, optional
         degrees input/output  (False: radians in/out)

    Returns
    -------
    latbar, lonbar: float
        geographic mean latitude, longitude
    """
    if deg:
        lat, lon = radians(lat), radians(lon)

    lat = geodetic2authalic(lat, ell, deg=False)
    x, y, z = sph2cart(lon, lat, 1)
    lonbar, latbar, _ = cart2sph(sum(x), sum(y), sum(z))  # type: ignore
    latbar = authalic2geodetic(latbar, ell, deg=False)

    if deg:
        latbar, lonbar = degrees(latbar), degrees(lonbar)
    return latbar, lonbar

Functions

def departure(lon1, lon2, lat, ell=None, deg=True)

Computes the distance along a specific parallel between two meridians.

like Matlab departure()

Parameters

lon1, lon2 : float
geodetic longitudes (degrees)
lat : float
geodetic latitude (degrees)
ell : Ellipsoid, optional
reference ellipsoid
deg : bool, optional
degrees input/output (False: radians in/out)

Returns

dist : float
ground distance (meters)
Source code
def departure(lon1: float, lon2: float, lat: float, ell: Ellipsoid = None, deg: bool = True) -> float:
    """
    Computes the distance along a specific parallel between two meridians.

    like Matlab departure()

    Parameters
    ----------
    lon1, lon2 : float
        geodetic longitudes (degrees)
    lat : float
        geodetic latitude (degrees)
    ell : Ellipsoid, optional
          reference ellipsoid
    deg : bool, optional
         degrees input/output  (False: radians in/out)

    Returns
    -------
    dist: float
        ground distance (meters)
    """
    if deg:
        lon1, lon2, lat = radians(lon1), radians(lon2), radians(lat)

    return rcurve_parallel(lat, ell, deg=False) * ((lon2 - lon1) % pi)
def loxodrome_direct(lat1, lon1, rng, a12, ell=None, deg=True)

Given starting lat, lon with arclength and azimuth, compute final lat, lon

like Matlab reckon('rh', …)

Parameters

lat1 : float
inital geodetic latitude (degrees)
lon1 : float
initial geodetic longitude (degrees)
rng : float
ground distance (meters)
a12 : float
azimuth (degrees) clockwide from north.
ell : Ellipsoid, optional
reference ellipsoid
deg : bool, optional
degrees input/output (False: radians in/out)

Results

lat2 : float
final geodetic latitude (degrees)
lon2 : float
final geodetic longitude (degrees)
Source code
def loxodrome_direct(
    lat1: float, lon1: float, rng: float, a12: float, ell: Ellipsoid = None, deg: bool = True
) -> typing.Tuple[float, float]:
    """
    Given starting lat, lon with arclength and azimuth, compute final lat, lon

    like Matlab reckon('rh', ...)

    Parameters
    ----------
    lat1 : float
        inital geodetic latitude (degrees)
    lon1 : float
        initial geodetic longitude (degrees)
    rng : float
        ground distance (meters)
    a12 : float
        azimuth (degrees) clockwide from north.
    ell : Ellipsoid, optional
          reference ellipsoid
    deg : bool, optional
         degrees input/output  (False: radians in/out)

    Results
    -------
    lat2 : float
        final geodetic latitude (degrees)
    lon2 : float
        final geodetic longitude (degrees)
    """
    if vectorize is not None:
        fun = vectorize(loxodrome_direct_point)
        return fun(lat1, lon1, rng, a12, ell, deg)
    else:
        return loxodrome_direct_point(lat1, lon1, rng, a12, ell, deg)
def loxodrome_inverse(lat1, lon1, lat2, lon2, ell=None, deg=True)

computes the arc length and azimuth of the loxodrome between two points on the surface of the reference ellipsoid

like Matlab distance('rh',…) and azimuth('rh',…)

Parameters

lat1 : float
geodetic latitude of first point
lon1 : float
geodetic longitude of first point
lat2 : float
geodetic latitude of second point
lon2 : float
geodetic longitude of second point
ell : Ellipsoid, optional
reference ellipsoid (default WGS84)
deg : bool, optional
degrees input/output (False: radians in/out)

Results

lox_s : float
distance along loxodrome
az12 : float
azimuth of loxodrome (degrees/radians)

Based on Deakin, R.E., 2010, 'The Loxodrome on an Ellipsoid', Lecture Notes, School of Mathematical and Geospatial Sciences, RMIT University, January 2010

[1] Bowring, B.R., 1985, 'The geometry of the loxodrome on the ellipsoid', The Canadian Surveyor, Vol. 39, No. 3, Autumn 1985, pp.223-230. [2] Snyder, J.P., 1987, Map Projections-A Working Manual. U.S. Geological Survey Professional Paper 1395. Washington, DC: U.S. Government Printing Office, pp.15-16 and pp. 44-45. [3] Thomas, P.D., 1952, Conformal Projections in Geodesy and Cartography, Special Publication No. 251, Coast and Geodetic Survey, U.S. Department of Commerce, Washington, DC: U.S. Government Printing Office, p. 66.

Source code
def loxodrome_inverse(
    lat1: float, lon1: float, lat2: float, lon2: float, ell: Ellipsoid = None, deg: bool = True
) -> typing.Tuple[float, float]:
    """
    computes the arc length and azimuth of the loxodrome
    between two points on the surface of the reference ellipsoid

    like Matlab distance('rh',...) and azimuth('rh',...)

    Parameters
    ----------

    lat1 : float
        geodetic latitude of first point
    lon1 : float
        geodetic longitude of first point
    lat2 : float
        geodetic latitude of second point
    lon2 : float
        geodetic longitude of second point
    ell : Ellipsoid, optional
         reference ellipsoid (default WGS84)
    deg : bool, optional
         degrees input/output  (False: radians in/out)

    Results
    -------

    lox_s : float
        distance along loxodrome
    az12 : float
        azimuth of loxodrome (degrees/radians)

    Based on Deakin, R.E., 2010, 'The Loxodrome on an Ellipsoid', Lecture Notes,
    School of Mathematical and Geospatial Sciences, RMIT University, January 2010

    [1] Bowring, B.R., 1985, 'The geometry of the loxodrome on the
    ellipsoid', The Canadian Surveyor, Vol. 39, No. 3, Autumn 1985,
    pp.223-230.
    [2] Snyder, J.P., 1987, Map Projections-A Working Manual. U.S.
    Geological Survey Professional Paper 1395. Washington, DC: U.S.
    Government Printing Office, pp.15-16 and pp. 44-45.
    [3] Thomas, P.D., 1952, Conformal Projections in Geodesy and
    Cartography, Special Publication No. 251, Coast and Geodetic
    Survey, U.S. Department of Commerce, Washington, DC: U.S.
    Government Printing Office, p. 66.
    """
    if vectorize is not None:
        fun = vectorize(loxodrome_inverse_point)
        return fun(lat1, lon1, lat2, lon2, ell, deg)
    else:
        return loxodrome_inverse_point(lat1, lon1, lat2, lon2, ell, deg)
def meanm(lat, lon, ell=None, deg=True)

Computes geographic mean for geographic points on an ellipsoid

like Matlab meanm()

Parameters

lat : sequence of float
geodetic latitude (degrees)
lon : sequence of float
geodetic longitude (degrees)
ell : Ellipsoid, optional
reference ellipsoid
deg : bool, optional
degrees input/output (False: radians in/out)

Returns

latbar, lonbar : float
geographic mean latitude, longitude
Source code
def meanm(lat: "ndarray", lon: "ndarray", ell: Ellipsoid = None, deg: bool = True) -> typing.Tuple[float, float]:
    """
    Computes geographic mean for geographic points on an ellipsoid

    like Matlab meanm()

    Parameters
    ----------
    lat : sequence of float
        geodetic latitude (degrees)
    lon : sequence of float
        geodetic longitude (degrees)
    ell : Ellipsoid, optional
          reference ellipsoid
    deg : bool, optional
         degrees input/output  (False: radians in/out)

    Returns
    -------
    latbar, lonbar: float
        geographic mean latitude, longitude
    """
    if deg:
        lat, lon = radians(lat), radians(lon)

    lat = geodetic2authalic(lat, ell, deg=False)
    x, y, z = sph2cart(lon, lat, 1)
    lonbar, latbar, _ = cart2sph(sum(x), sum(y), sum(z))  # type: ignore
    latbar = authalic2geodetic(latbar, ell, deg=False)

    if deg:
        latbar, lonbar = degrees(latbar), degrees(lonbar)
    return latbar, lonbar
def meridian_arc(lat1, lat2, ell=None, deg=True)

Computes the ground distance on an ellipsoid between two latitudes.

Parameters

lat1, lat2 : float
geodetic latitudes
ell : Ellipsoid, optional
reference ellipsoid (default WGS84)
deg : bool, optional
degrees input/output (False: radians in/out)

Results

dist : float
distance (meters)
Source code
def meridian_arc(lat1: float, lat2: float, ell: Ellipsoid = None, deg: bool = True) -> float:
    """
    Computes the ground distance on an ellipsoid between two latitudes.

    Parameters
    ----------
    lat1, lat2 : float
        geodetic latitudes
    ell : Ellipsoid, optional
         reference ellipsoid (default WGS84)
    deg : bool, optional
         degrees input/output  (False: radians in/out)

    Results
    -------
    dist : float
         distance (meters)
    """

    if deg:
        lat1, lat2 = radians(lat1), radians(lat2)

    rlat1 = geodetic2rectifying(lat1, ell, deg=False)
    rlat2 = geodetic2rectifying(lat2, ell, deg=False)

    return rsphere_rectifying(ell) * abs(rlat2 - rlat1)
def meridian_dist(lat, ell=None, deg=True)

Computes the ground distance on an ellipsoid from the equator to the input latitude.

Parameters

lat : float
geodetic latitude
ell : Ellipsoid, optional
reference ellipsoid (default WGS84)
deg : bool, optional
degrees input/output (False: radians in/out)

Results

dist : float
distance (meters)
Source code
def meridian_dist(lat: float, ell: Ellipsoid = None, deg: bool = True) -> float:
    """
    Computes the ground distance on an ellipsoid from the equator to the input latitude.

    Parameters
    ----------
    lat : float
        geodetic latitude
    ell : Ellipsoid, optional
         reference ellipsoid (default WGS84)
    deg : bool, optional
         degrees input/output  (False: radians in/out)

    Results
    -------
    dist : float
         distance (meters)
    """
    return meridian_arc(0, lat, ell, deg)