Source code for data_manipulation.geopandas_
from typing import TypeVar
import numpy as np
from numpy.typing import NDArray
# Type variable for numeric types (float or numpy array)
NumericType = TypeVar("NumericType", float, NDArray[np.float64])
# Earth's radius in kilometers
EARTH_RADIUS_KM = 6371.0
[docs]
def haversine_np(
longitude1: NumericType,
latitude1: NumericType,
longitude2: NumericType,
latitude2: NumericType,
) -> NumericType:
"""Calculates the great circle distance between two points on Earth.
Args:
longitude1 (float | ndarray): Longitude of the first point(s) in decimal degrees.
latitude1 (float | ndarray): Latitude of the first point(s) in decimal degrees.
longitude2 (float | ndarray): Longitude of the second point(s) in decimal degrees.
latitude2 (float | ndarray): Latitude of the second point(s) in decimal degrees.
Returns:
float | ndarray: Distance between the points in kilometers.
Raises:
ValueError: If latitude values are outside [-90, 90] or longitude values outside [-180, 180].
Examples:
>>> haversine_np(-0.127758, 51.507351, 103.819836, 1.352083) # London to Singapore
10880.39...
>>> haversine_np(np.array([-0.127758]), np.array([51.507351]),
... np.array([103.819836]), np.array([1.352083]))
array([10880.39...])
Note:
Uses the Haversine formula to calculate great circle distances.
Earth radius is assumed to be 6371 km.
Supports both scalar and numpy array inputs for vectorized calculations.
"""
# Input validation
for name, val in [("latitude1", latitude1), ("latitude2", latitude2)]:
if np.any(np.abs(val) > 90):
raise ValueError(f"{name} must be between -90 and 90 degrees")
for name, val in [("longitude1", longitude1), ("longitude2", longitude2)]:
if np.any(np.abs(val) > 180):
raise ValueError(f"{name} must be between -180 and 180 degrees")
# Convert to radians
lon1, lat1, lon2, lat2 = map(
np.radians, [longitude1, latitude1, longitude2, latitude2]
)
# Haversine formula
dlon = lon2 - lon1
dlat = lat2 - lat1
# Optimize by pre-calculating trigonometric functions
sin_dlat_2 = np.sin(dlat / 2.0)
sin_dlon_2 = np.sin(dlon / 2.0)
cos_lat1 = np.cos(lat1)
cos_lat2 = np.cos(lat2)
a = sin_dlat_2**2 + cos_lat1 * cos_lat2 * sin_dlon_2**2
c = 2 * np.arcsin(np.sqrt(a))
return EARTH_RADIUS_KM * c
if __name__ == "__main__":
import doctest
doctest.testmod()