Source code for tracktable.core.geomath

#
# Copyright (c) 2014-2021 National Technology and Engineering
# Solutions of Sandia, LLC. Under the terms of Contract DE-NA0003525
# with National Technology and Engineering Solutions of Sandia, LLC,
# the U.S. Government retains certain rights in this software.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

"""
This file contains useful math functions. Some of them will be
related to geography such as 'find the distance between these two
points on the globe'.
"""

from __future__ import division, absolute_import

from six.moves import range
import copy
import math

import tracktable.core.log

from tracktable.lib._domain_algorithm_overloads import distance as _distance
from tracktable.lib._domain_algorithm_overloads import bearing as _bearing
from tracktable.lib._domain_algorithm_overloads import interpolate as _interpolate
from tracktable.lib._domain_algorithm_overloads import extrapolate as _extrapolate
from tracktable.lib._domain_algorithm_overloads import signed_turn_angle as _signed_turn_angle
from tracktable.lib._domain_algorithm_overloads import unsigned_turn_angle as _unsigned_turn_angle
from tracktable.lib._domain_algorithm_overloads import speed_between as _speed_between
from tracktable.lib._domain_algorithm_overloads import point_at_time_fraction as _point_at_time_fraction
from tracktable.lib._domain_algorithm_overloads import point_at_length_fraction as _point_at_length_fraction
from tracktable.lib._domain_algorithm_overloads import point_at_time as _point_at_time
from tracktable.lib._domain_algorithm_overloads import time_at_fraction as _time_at_fraction
from tracktable.lib._domain_algorithm_overloads import subset_during_interval as _subset_during_interval
from tracktable.lib._domain_algorithm_overloads import length as _length
from tracktable.lib._domain_algorithm_overloads import end_to_end_distance as _end_to_end_distance
from tracktable.lib._domain_algorithm_overloads import intersects as _intersects
from tracktable.lib._domain_algorithm_overloads import geometric_median as _geometric_median
from tracktable.lib._domain_algorithm_overloads import geometric_mean as _geometric_mean
from tracktable.lib._domain_algorithm_overloads import simplify as _simplify
from tracktable.lib._domain_algorithm_overloads import convex_hull_perimeter as _convex_hull_perimeter
from tracktable.lib._domain_algorithm_overloads import convex_hull_area as _convex_hull_area
from tracktable.lib._domain_algorithm_overloads import convex_hull_aspect_ratio as _convex_hull_aspect_ratio
from tracktable.lib._domain_algorithm_overloads import convex_hull_centroid as _convex_hull_centroid
from tracktable.lib._domain_algorithm_overloads import radius_of_gyration as _radius_of_gyration
from tracktable.lib._domain_algorithm_overloads import current_length as _current_length
from tracktable.lib._domain_algorithm_overloads import current_length_fraction as _current_length_fraction
from tracktable.lib._domain_algorithm_overloads import current_time_fraction as _current_time_fraction

import logging
LOGGER = logging.getLogger(__name__)
DOMAIN_MODULE = None


[docs]def xcoord(thing): """Return what we think is the X-coordinate for an object. If the supplied thing has a property named 'x' then we return that. Otherwise we try to return its first element. Args: thing (Tracktable object): Object with an 'x' property or tuple of numbers Returns: Number corresponding to x coordinate Raises: AttributeError: if attempt at access fails """ try: return thing.x except AttributeError: return thing[0]
[docs]def ycoord(thing): """Return what we think is the Y-coordinate for an object. If the supplied thing has a property named 'y' then we return that. Otherwise we try to return its second element. Args: thing (Tracktable object): Object with an 'y' property or tuple of numbers Returns: Number corresponding to y coordinate Raises: AttributeError: if attempt at access fails """ try: return thing.y except AttributeError: return thing[1]
[docs]def longitude(thing): """Return the longitude from a point or tuple It is often convenient to specify a point as a (lon, lat) tuple instead of a fullfledged TrajectoryPoint. By using this function to look up longitude we can cope gracefully with both. Args: thing (TrajectoryPoint or (lon, lat) tuple): Object with an 'longitude' property or tuple of numbers Returns: Longitude as float Raises: AttributeError: if attempt at access fails """ if hasattr(thing, 'longitude'): return thing.longitude else: return thing[0]
[docs]def latitude(thing): """Return the latitude from a point or tuple It is often convenient to specify a point as a (lon, lat) tuple instead of a fullfledged TrajectoryPoint. By using this function to look up latitude we can cope gracefully with both. Args: thing (TrajectoryPoint or (lon, lat) tuple): Object with an 'latitude' property or tuple of numbers Returns: Latitude as float Raises: AttributeError: if attempt at access fails """ if hasattr(thing, 'latitude'): return thing.latitude else: return thing[1]
[docs]def longitude_or_x(thing): """Return the longitude or X-coordinate from a point or tuple Args: thing (TrajectoryPoint, (lon, lat) tuple or (x, y) point): Object with an 'longitude' or 'x' property or tuple of numbers Returns: Longitude/X as float Raises: AttributeError: if attempt at access fails """ if hasattr(thing, 'longitude'): return thing.longitude elif hasattr(thing, 'x'): return thing.x else: return thing[0]
[docs]def latitude_or_y(thing): """Return the latitude or Y-coordinate from a point or tuple Args: thing (TrajectoryPoint, (lon, lat) tuple or (x, y) point): Object with an 'latitude' or 'y' property or tuple of numbers Returns: Latitude/Y as float Raises: AttributeError: if attempt at access fails """ if hasattr(thing, 'latitude'): return thing.latitude elif hasattr(thing, 'y'): return thing.y else: return thing[1]
[docs]def altitude(thing): """Return the altitude from a point or tuple It is often convenient to specify a point as a (lon, lat, altitude) tuple instead of a full-fledged TrajectoryPoint. By using this function to look up altitude we can cope gracefully with both. Args: thing (TrajectoryPoint or (lon, lat, altitude) tuple): Object with an 'altitude' property or tuple of numbers Returns: Altitude as float Raises: AttributeError: if attempt at access fails """ if hasattr(thing, 'altitude'): return thing.altitude else: try: return thing.properties['altitude'] except IndexError: return 0
[docs]def almost_equal(a, b, relative_tolerance=1e-6): """Check two numbers for equality within a tolerance Arguments: a (float): First number b (float): Second number Keyword Arguments: relative_tolerance (float): Numbers must be close to within this fraction of their average to be considered equal (Default: 1e-6) Returns: True/false depending on whether or not they're equal-ish """ return ( math.fabs(a - b) < relative_tolerance * 0.5 * ( math.fabs(a) + math.fabs(b) ) )
# ----------------------------------------------------------------------
[docs]def bearing(origin, destination): """Compute angular bearing between two points Source: http://gagravarr.livejournal.com/109998.html with modifications. Domain Information: Terrestrial: Returned in degrees. 0 is due north, 90 is due east. Cartesian2D: Returned in radians. 0 is positive X, pi/2 is positive Y. Cartesian3D: Not defined. Args: origin (BasePoint or TrajectoryPoint): start point destination (BasePoint or TrajectoryPoint): end point Returns: Bearing from origin to destination. """ return _bearing(origin, destination)
# ----------------------------------------------------------------------
[docs]def speed_between(point1, point2): """Return speed in km/hr between two timestamped points. Domain Information: Terrestrial: Speed is in km/hr Cartesian2D: Speed is in units/sec Cartesian3D: Speed is in units/sec Args: point1 (TrajectoryPoint): Start point point2 (TrajectoryPoint): End point Returns: Speed measured in domain-specific units as float """ return _speed_between(point1, point2)
# ----------------------------------------------------------------------
[docs]def signed_turn_angle(a, b, c): """Return signed turn angle between (a, b) and (b, c). The magnitude of the angle tells you how far you turned. The sign of the angle tells you whether you turned right or left. Which one depends on the domain. Domain Information: Terrestrial: angle in degrees, positive angles are clockwise Cartesian2D: angle in radians, positive angles are counterclockwise Cartesian3D: not defined Args: a (BasePoint): first point b (BasePoint): second point c (BasePoint): third point Returns: Signed angle in domain-dependent units """ return _signed_turn_angle(a, b, c)
# ----------------------------------------------------------------------
[docs]def unsigned_turn_angle(a, b, c): """Return unsigned turn angle between (a, b) and (b, c). The magnitude of the angle tells you how far you turned. This function will not tell you whether you turned right or left - for that you need signed_turn_angle. Domain Information: Terrestrial: angle in degrees between 0 and 180 Cartesian2D: angle in radians between 0 and pi Cartesian3D: angle in radians between 0 and pi Args: a (BasePoint): first point b (BasePoint): second point c (BasePoint): third point Returns: Angle in domain-dependent units """ return _unsigned_turn_angle(a, b, c)
# ----------------------------------------------------------------------
[docs]def intersects(thing1, thing2): """Check to see whether two geometries intersect The geometries in question must be from the same domain. They can be points, trajectories, linestrings or bounding boxes. Args: thing1 (points, trajectories, linestrings or bounding boxes): Geometry object used for intersection calculation thing2 (points, trajectories, linestrings or bounding boxes): Geometry object used for intersection calculation Returns: True or False """ return _intersects(thing1, thing2)
# ----------------------------------------------------------------------
[docs]def length(trajectory): """Return the length of a path in domain-dependent units This is the total length of all segments in the trajectory. Domain Information: Terrestrial: distance in km Cartesian2D: distance in units Cartesian3D: distance in units Args: trajectory (Trajectory): Path whose length we want Returns: Length in domain-dependent units """ return _length(trajectory)
# ----------------------------------------------------------------------
[docs]def current_length(point): """Return the current length of a path in domain-dependent units This is the length up to the given point in a trajectory. Domain Information: Terrestrial: distance in km Cartesian2D: distance in units Cartesian3D: distance in units Args: Point (TrajectoryPoint): Point to which we want the length Returns: Length in domain-dependent units """ return _current_length(point)
# ----------------------------------------------------------------------
[docs]def end_to_end_distance(trajectory): """Return the distance between a path's endpoints This is just the crow-flight distance between start and end points rather than the total distance traveled. Domain Information: Terrestrial: distance in km Cartesian2D: distance in units Cartesian3D: distance in units Args: trajectory (Trajectory): Path whose length we want Returns: Length in domain-dependent units """ return _end_to_end_distance(trajectory)
# ----------------------------------------------------------------------
[docs]def point_at_fraction(trajectory, time_fraction): """WARNING: This function is deprecated. Use point_at_time_fraction instead. Return a point from a trajectory at a specific fraction of its duration This function will estimate a point at a trajectory at some specific fraction of its total duration. If the supplied fraction does not fall exactly on a vertex of the trajectory we will interpolate between the nearest two points. Fractions before the beginning or after the end of the trajectory will return the start and end points, respectively. Args: trajectory (Trajectory): Path to sample time_fraction (float): Value between 0 and 1. 0 is the beginning and 1 is the end. Returns: TrajectoryPoint at specified fraction """ tracktable.core.log.warn_deprecated(( "tracktable.core.geomath.point_at_fraction is " "deprecated and will be removed in a future " "release. Use tracktable.core.geomath." "point_at_time_fraction or tracktable.core." "geomath.point_at_length_fraction instead.")) return _point_at_time_fraction(trajectory, time_fraction)
# ----------------------------------------------------------------------
[docs]def point_at_time_fraction(trajectory, time_fraction): """Return a point from a trajectory at a specific fraction of its duration This function will estimate a point at a trajectory at some specific fraction of its total duration. If the supplied fraction does not fall exactly on a vertex of the trajectory we will interpolate between the nearest two points. Fractions before the beginning or after the end of the trajectory will return the start and end points, respectively. Args: trajectory (Trajectory): Path to sample time_fraction (float): Value between 0 and 1. 0 is the beginning and 1 is the end. Returns: TrajectoryPoint at specified fraction """ return _point_at_time_fraction(trajectory, time_fraction)
# ----------------------------------------------------------------------
[docs]def point_at_length_fraction(trajectory, length_fraction): """Return a point from a trajectory at a specific fraction of its distance This function will estimate a point at a trajectory at some specific fraction of its total travel distance. If the supplied fraction does not fall exactly on a vertex of the trajectory we will interpolate between the nearest two points. Fractions before the beginning or after the end of the trajectory will return the start and end points, respectively. Args: trajectory (Trajectory): Path to sample length_fraction (float): Value between 0 and 1. 0 is the beginning and 1 is the end. Returns: TrajectoryPoint at specified fraction """ return _point_at_length_fraction(trajectory, length_fraction)
# ----------------------------------------------------------------------
[docs]def point_at_time(trajectory, when): """Return a point from a trajectory at a specific time This function will estimate a point at a trajectory at some specific time. If the supplied timestamp does not fall exactly on a vertex of the trajectory we will interpolate between the nearest two points. Times before the beginning or after the end of the trajectory will return the start and end points, respectively. Args: trajectory (Trajectory): Path to sample when (datetime): Timestamp for which we want the point Returns: TrajectoryPoint at specified time """ return _point_at_time(trajectory, when)
# ----------------------------------------------------------------------
[docs]def time_at_fraction(trajectory, fraction): """Return a time from a trajectory at a specific fraction of its duration This function will estimate a time in a trajectory at some specific fraction of its total travel duration. If the supplied fraction does not fall exactly on a vertex of the trajectory we will interpolate between the nearest two points. Fractions before the beginning or after the end of the trajectory will return the start and end times, respectively. Args: trajectory (Trajectory): Path to sample fraction (float): Value between 0 and 1. 0 is the beginning and 1 is the end. Returns: Timestamp (datetime) at specified fraction """ return _time_at_fraction(trajectory, fraction)
# ----------------------------------------------------------------------
[docs]def subset_during_interval(trajectory, start_time, end_time): """Return a subset of a trajectory between two times This function will extract some (possibly empty) subset of a trajectory between two timestamps. If the time interval is entirely outside the trajectory, the result will be an empty trajectory. Otherwise we will use point_at_time to find the two endpoints and build a new trajectory from the endpoints and all trajectory points between them. Args: trajectory (Trajectory): Path to sample start_time (datetime): Timestamp for beginning of subset end_time (datetime): Timestamp for end of subset Returns: Trajectory for desired interval """ return _subset_during_interval(trajectory, start_time, end_time)
# ----------------------------------------------------------------------
[docs]def distance(hither, yon): """Return the distance between two points This function will compute the distance between two points in domain-specific units. The points being measured must be from the same domain. Domain Information: Terrestrial domain returns distance in km. Cartesian domains return distance in native units. Args: hither (BasePoint): point 1 yon (BasePoint): point 2 Returns: Distance between hither and yon """ return _distance(hither, yon)
# ----------------------------------------------------------------------
[docs]def interpolate(start, end, t): """Interpolate between two points This function will interpolate linearly between two points. It is aware of the underlying coordinate system: interpolation on the globe will be done along great circles and interpolation in Cartesian space will be done along a straight line. The points being measured must be from the same domain. Args: start (BasePoint or TrajectoryPoint): point 1 end (BasePoint or TrajectoryPoint): point 2 t (float in [0, 1]): interpolant Returns: New point interpolated between start and end """ return _interpolate(start, end, t)
# ----------------------------------------------------------------------
[docs]def extrapolate(start, end, t): """Extrapolate between two points This function will extrapolate linearly between two points. It is aware of the underlying coordinate system: interpolation on the globe will be done along great circles and interpolation in Cartesian space will be done along a straight line. The points being measured must be from the same domain. Args: start (BasePoint or TrajectoryPoint): point 1 end (BasePoint or TrajectoryPoint): point 2 t (float): interpolant Returns: New point interpolated between start and end """ return _extrapolate(start, end, t)
# ----------------------------------------------------------------------
[docs]def sanity_check_distance_less_than(max_distance): """Check and verify distance is less than max allowable distance Args: max_distance (float): Max allowable distance between points Returns: True/False indicating if distance between two points is less than max distance """ def sanity_check(point1, point2): return ( distance(point1, point2) < max_distance ) return sanity_check
# ----------------------------------------------------------------------
[docs]def compute_bounding_box(point_sequence, buffer=()): """Compute a bounding box for a sequence of points. This function will construct a domain-specific bounding box over an arbitrary sequence of points. Those points must all have the same type. It can also produce a buffer of space that extends the bounding box some percentage beyond the min and max points. The implementation is fairly naive and can cause issues if the values extend past max values for the point/map type. Domain Information: Each domain returns a separate bounding box type. Args: point_sequence (BasePoint or TrajectoryPoint iterable): Iterable of points Keyword Arguments: buffer (tuple): Ratios to extend the bounding box. This defaults to an empty tuple which means no padding is added. (Default: ()) Returns: Bounding box with min_corner, max_corner attributes """ # We need some machinery from tracktable.domain in order to find # the correct class for the bounding box. In order to avoid a # load-time circular import dependency, we grab it on demand # here. global DOMAIN_MODULE if DOMAIN_MODULE is None: import importlib DOMAIN_MODULE = importlib.import_module('tracktable.domain') min_corner = None max_corner = None bbox_type = None num_points = 0 for point in point_sequence: num_points += 1 if bbox_type is None: bbox_type = DOMAIN_MODULE.domain_class_for_object( point, 'BoundingBox' ) if min_corner is None: min_corner = copy.deepcopy(point) max_corner = copy.deepcopy(point) else: for i in range(len(point)): min_corner[i] = min(min_corner[i], point[i]) max_corner[i] = max(max_corner[i], point[i]) if len(buffer) == 2: horiz_buff = (max_corner[0] - min_corner[0]) * buffer[0] vert_buff = (max_corner[1] - min_corner[1]) * buffer[1] min_corner[0] = min_corner[0] - horiz_buff min_corner[1] = min_corner[1] - vert_buff max_corner[0] = max_corner[0] + horiz_buff max_corner[1] = max_corner[1] + vert_buff elif len(buffer) != 0: raise ValueError("Buffer must contain exactly 0 or 2 values.") if num_points == 0: raise ValueError("Cannot compute bounding box. No points provided.") else: global LOGGER LOGGER.debug("Bounding box points: {}, {}".format( min_corner, max_corner)) result = bbox_type(min_corner, max_corner) LOGGER.debug("Final bounding box: {}".format(result)) return result
# ----------------------------------------------------------------------
[docs]def recompute_speed(trajectory, target_attribute_name="speed"): """Use points and timestamps to compute speed The speed data in trajectories is often suspect. This method goes through and recomputes it based on the distance between neighboring points and the time elapsed between those points. The speed at point N is computed using the distance and time since point N-1. The speed at point 0 is copied from point 1. Args: trajectory (Trajectory): Any Tracktable trajectory Keyword Arguments: target_attribute_name (str): Speed will be stored in this property at each point. Defaults to 'speed'. (Default: "speed") The trajectory will be modified in place instead of returning a new copy. """ if len(trajectory) == 0: return elif len(trajectory) == 1: trajectory[0].properties[target_attribute_name] = 0 else: for point_index in range(1, len(trajectory)): trajectory[point_index].properties[target_attribute_name] = speed_between( trajectory[point_index - 1], trajectory[point_index] ) trajectory[0].properties[target_attribute_name] = trajectory[1].properties[target_attribute_name]
# ----------------------------------------------------------------------
[docs]def geometric_median(points): """Compute L1 multivariate median of points The multivariate median generalizes the standard 1D median to two or more dimensions. Args: points (BasePoint or TrajectoryPoint iterable): Points for which you want a median Returns: Single point of the same type that came in """ my_points = list(points) if len(my_points) == 0: return None return _geometric_median(my_points[0], my_points)
# ----------------------------------------------------------------------
[docs]def geometric_mean(points): """Compute mean of input points This is the regular mean: just the component-wise average of the coordinates. Note: This does not yet do the right thing with terrestrial points. It should normalize their coordinates to make sure they do not fall across the limb of the map before taking the average. Args: points (BasePoint or TrajectoryPoint iterable): Points for which you want a mean Returns: Single point of the same type that came in """ my_points = list(points) if len(my_points) == 0: return None return _geometric_mean(my_points[0], my_points)
# ----------------------------------------------------------------------
[docs]def simplify(trajectory, tolerance): """Geometric simplification for trajectory This function reduces the number of points in a trajectory without introducing positional error greater than the supplied tolerance. Under the hood it uses Douglas-Peucker simplification. Note: The points in the output are copies of the points in the input. Changing the input after a call to simplify() will have no effect on previous results. Note: This function only cares about geometric error in the trajectory. It does not account for error in the attributes attached to each point. Args: trajectory (Trajectory): Trajectory to simplify tolerance (float): Error tolerance measured in the trajectory's native distance Returns: Simplified version of trajectory """ return _simplify(trajectory, tolerance)
# ----------------------------------------------------------------------
[docs]def convex_hull_perimeter(trajectory): """Compute the perimeter of the convex hull of a trajectory Perimeter length will be returned in the native distance units of the domain. This is kilometers for the terrestrial domain and untyped units for Cartesian. Args: trajectory (Trajectory): Trajectory whose hull you want to measure Returns: Perimeter of the trajectory's convex hull """ return _convex_hull_perimeter(trajectory)
# ----------------------------------------------------------------------
[docs]def convex_hull_area(trajectory): """Compute the area of the convex hull of a trajectory Area will be returned in the native area units of the domain. This is square kilometers for the terrestrial domain and untyped squared units for Cartesian. Args: trajectory (Trajectory): Trajectory whose hull you want to measure Returns: Area of the trajectory's convex hull """ return _convex_hull_area(trajectory)
# ----------------------------------------------------------------------
[docs]def convex_hull_aspect_ratio(trajectory): """Compute the aspect ratio of the convex hull of a trajectory Aspect ratio is a dimensionless number. It refers to the ratio of the shortest axis of the polygon over the longest. Note that we compute an approximation using the vertices of the convex hull. This will get better in a future release. Args: trajectory (Trajectory): Trajectory whose shape you want to measure Returns: Aspect ratio of the trajectory's convex hull """ return _convex_hull_aspect_ratio(trajectory)
# ----------------------------------------------------------------------
[docs]def radius_of_gyration(trajectory): """Compute the radius of gyration of a trajectory Radius of gyration is an indication of the compactness of a trajectory. Technically the result is in radians from the center of mass of the trajectory. The units of the radius is dependent on the type of trajectory being measured. Terrestrial will return km, while Cartesian2D returns radians. Args: trajectory (Trajectory): Trajectory whose shape you want to measure Returns: Radius of gyration of the trajectory """ return _radius_of_gyration(trajectory)
# ----------------------------------------------------------------------
[docs]def convex_hull_centroid(trajectory): """Compute the centroid of the convex hull of a trajectory Centroid will be returned in the native units of the domain. This is: latitude, longitude (altitude) for the terrestrial domain; and x, y (z) for Cartesian. Args: trajectory (Trajectory): Trajectory whose shape you want to measure Returns: Centroid ratio of the trajectory's convex hull """ return _convex_hull_centroid(trajectory)
# ----------------------------------------------------------------------
[docs]def latitude_degree_size(latitude): """ latitude_degree_size(latitude: float between -90 and 90) -> float (in km) Compute the distance between adjacent degrees of latitude centered on a given parallel. This measurement is 111.694km at the equator and 110.574km at the poles. This is a small enough variation that we'll just use linear interpolation. Args: latitude (float): Latitude Returns: The distance between adjacent degrees of latitude """ return (math.fabs(latitude) / 90) * (110.574 - 111.694) + 111.594
# ----------------------------------------------------------------------
[docs]def longitude_degree_size(latitude): """ longitude_degree_size(latitude: float between -90 and 90) -> float (in km) Compute the distance between adjacent degrees of longitude at a given latitude. This varies from 111.32km at the equator to 0 at the poles and decreases as the cosine of increasing latitude. Args: latitude (float): Latitude Returns: The distance between adjacent degrees of longitude """ def d2r(d): return math.pi * d / 180 return 111.32 * math.cos(d2r(math.fabs(latitude)))
# ----------------------------------------------------------------------
[docs]def kms_to_lon(kms, latitude): """ kms_to_lon(kms: float, latitude: float between -90 and 90) -> float (in longitude) Compute the degrees-longitude conversion for a distance in km, at a given latitude. This is because as you move towards the poles, the km/longitude ratio decreases Args: kms (float): Kilometers latitude (float): Latitude Returns: degrees-longitude conversion for a distance in km, at a given latitude """ return kms / longitude_degree_size(latitude)
# ----------------------------------------------------------------------
[docs]def kms_to_lat(kms, latitude): """ kms_to_lat(kms: float, latitude: float between -90 and 90) -> float (in latitude) Compute the degrees-latitude conversion for a distance in km, at a given latitude. Args: kms (float): Kilometers latitude (float): Latitude Returns: degrees-latitude conversion for a distance in km, at a given latitude """ return kms / latitude_degree_size(latitude)
# ----------------------------------------------------------------------
[docs]def km_to_radians(distance): """Convert distance from km to radians given that we are working on the surface of the earth Arguments: distance: distance in km Returns: distance in radians """ # divide the distance by the radius of the sphere (in this case the earth) return float(distance / 6371)
# ----------------------------------------------------------------------
[docs]def current_length_fraction(point): """Return the fraction length of a point in a trajectory This is the fraction length of all segments in the trajectory up to the given point. Args: point (Point): Point whose current length fraction we want Returns: Fraction of total length up to this point. """ return _current_length_fraction(point)
# ----------------------------------------------------------------------
[docs]def current_time_fraction(point): """Return the fraction duration of a point in trajectory This is the fraction duration of all segments in the trajectory up to the given point. Args: point (Point): Point whose current duration fraction we want Returns: Fraction of total duration up to this point. """ return _current_time_fraction(point)
# ----------------------------------------------------------------------
[docs]def ECEF(point, ratio = 1.0, altitudeString = ""): """Convert point to ECEF. Altitude is in km. Converts Terrestrial points to cartesian coordinates. Args: points: A point to convert ratio: conversion ratio from km to result altitudeString: String label of altitude property Returns: Single Cartesian 3D point """ return point.ECEF(ratio, altitudeString)
# ----------------------------------------------------------------------
[docs]def ECEF_from_feet(point, altitudeString = "altitude"): """Convert point to ECEF using altitude in feet Converts Terrestrial points to cartesian coordinates. Args: points: A point to convert altitudeString: String label of altitude property Returns: Single Cartesian 3D point """ return point.ECEF_from_feet(altitudeString)
# ----------------------------------------------------------------------
[docs]def ECEF_from_meters(point, altitudeString = "altitude"): """Convert point to ECEF using altitude in meters Converts Terrestrial points to cartesian coordinates. Args: points: A point to convert altitudeString: String label of altitude property Returns: Single Cartesian 3D point """ return point.ECEF_from_meters(altitudeString)
# ----------------------------------------------------------------------