Source code for gradeit.grade
from math import asin, cos, radians, sin, sqrt
import numpy as np
from gradeit.coordinate import Coordinate
[docs]
def get_grade(
elevation_profile: list[float],
distances: list[float],
min_distance_ft: float = 1.0,
) -> list[float]:
"""Compute decimal road grade (rise/run) for an elevation profile.
Grade is ``elevation change / distance`` between points. Segments shorter
than ``min_distance_ft`` use the previous valid grade.
Parameters
----------
elevation_profile : List[float]
Elevation at each point (n > 1).
distances : List[float]
Horizontal distance of each segment, length len(elevation_profile) - 1.
min_distance_ft : float, optional
Segments shorter than this use the previous grade. Default: 1.0.
"""
# Grade needs at least two points.
if len(elevation_profile) < 2:
raise ValueError(
"Determining grade requires at least 2 coordinates\n\t\ti.e. Input size of n > 1"
)
d_elev = np.diff(np.asarray(elevation_profile, dtype=float))
dist_arr = np.asarray(distances, dtype=float)
# Divide only segments that are long enough to measure.
grade = np.full(d_elev.shape, np.nan)
measurable = dist_arr >= min_distance_ft
grade[measurable] = d_elev[measurable] / dist_arr[measurable]
grade = np.insert(grade, 0, 0.0)
grade = np.round(grade, decimals=4)
for a in range(len(grade) - 1):
if np.isinf(grade[a + 1]) or np.isnan(grade[a + 1]):
grade[a + 1] = grade[a]
return list(grade)
[docs]
def get_distances(coordinates: list[Coordinate]) -> list[float]:
"""Return the distance in feet between each pair of nearby coordinates."""
FT_PER_KM = 3280.84
distances = []
i = 1
while i < len(coordinates):
dist_ft = haversine(coordinates[i - 1], coordinates[i]) * FT_PER_KM
distances += [dist_ft]
i += 1
return distances
[docs]
def haversine(coord1: Coordinate, coord2: Coordinate, get_bearing: bool = False) -> float:
"""Return the great-circle distance in kilometers between two coordinates."""
# Convert degrees to radians.
lat1 = radians(coord1.latitude)
lon1 = radians(coord1.longitude)
lat2 = radians(coord2.latitude)
lon2 = radians(coord2.longitude)
# Calculate the great-circle distance.
dlat = lat2 - lat1
dlon = lon2 - lon1
a = sin(dlat / 2) ** 2 + cos(lat1) * cos(lat2) * sin(dlon / 2) ** 2
c = 2 * asin(sqrt(a))
R = 6371 # Earth radius in kilometers.
distance = c * R
# Round to centimeter precision.
distance = round(distance, 5)
return distance