Spatial Calculations
The spatial module provides comprehensive geospatial calculations for KML elements, including distance calculations, bearing computations, and coordinate operations.
Overview
The spatial module implements a Protocol-based design that allows any object with coordinates to participate in spatial calculations. This provides clean separation of concerns and extensibility.
Key Features
Multiple distance calculation strategies - Haversine (default), Vincenty (high precision), and Euclidean (fast approximation)
Support for multiple distance units - Kilometers, meters, miles, nautical miles, feet, and yards
Protocol-based design - Any object implementing
HasCoordinatescan use spatial operationsLRU caching - Automatic caching of repeated calculations for performance
Bulk operations - Efficient batch distance calculations
Quick Example
from kmlorm.models import Placemark
from kmlorm.spatial import DistanceUnit
# Create two placemarks
nyc = Placemark(name="NYC", coordinates=(-74.006, 40.7128))
london = Placemark(name="London", coordinates=(-0.1276, 51.5074))
# Calculate distance (default: kilometers)
distance_km = nyc.distance_to(london)
print(f"Distance: {distance_km:.1f} km") # Distance: 5570.2 km
# Calculate in different units
distance_miles = nyc.distance_to(london, unit=DistanceUnit.MILES)
print(f"Distance: {distance_miles:.1f} miles") # Distance: 3461.0 miles
# Calculate bearing
bearing = nyc.bearing_to(london)
print(f"Bearing: {bearing:.1f}°") # Bearing: 51.2°
# Find midpoint
midpoint = nyc.midpoint_to(london)
print(f"Midpoint: ({midpoint.longitude:.2f}, {midpoint.latitude:.2f})")
# Midpoint: (-41.29, 52.37)
Core Components
HasCoordinates Protocol
The HasCoordinates protocol defines the interface for objects that can provide coordinates:
from typing import Protocol, Optional
from kmlorm.models.point import Coordinate
class HasCoordinates(Protocol):
"""Protocol for objects that can provide coordinates."""
def get_coordinates(self) -> Optional[Coordinate]:
"""Return the coordinate representation of this object."""
...
Any object implementing this protocol can participate in spatial calculations. The following
classes implement HasCoordinates:
CoordinatePointPlacemark
Distance Units
- class kmlorm.spatial.DistanceUnit(Enum)[source]
Enumeration of available distance units with conversion factors from kilometers.
- KILOMETERS
Kilometers (base unit, factor = 1.0)
- METERS
Meters (factor = 1000.0)
- MILES
Statute miles (factor = 0.621371)
- NAUTICAL_MILES
Nautical miles (factor = 0.539957)
- FEET
Feet (factor = 3280.84)
- YARDS
Yards (factor = 1093.61)
Example usage:
from kmlorm.spatial import DistanceUnit # Calculate distance in different units distance_km = point1.distance_to(point2) distance_m = point1.distance_to(point2, unit=DistanceUnit.METERS) distance_mi = point1.distance_to(point2, unit=DistanceUnit.MILES)
Spatial Calculations
- class kmlorm.spatial.SpatialCalculations[source]
Core class providing static methods for spatial calculations between geographic coordinates. All calculations use the WGS84 ellipsoid model and assume coordinates in decimal degrees.
- classmethod distance_between(from_obj: HasCoordinates, to_obj: HasCoordinates, unit: DistanceUnit = DistanceUnit.KILOMETERS) float | None[source]
Calculate the distance between two objects with coordinates.
- Parameters:
from_obj – Source object with coordinates
to_obj – Destination object with coordinates
unit – Unit for distance measurement (default: kilometers)
- Returns:
Distance in specified units, or None if coordinates unavailable
Example:
from kmlorm.spatial.calculations import SpatialCalculations from kmlorm.models.point import Coordinate coord1 = Coordinate(longitude=-74.006, latitude=40.7128) coord2 = Coordinate(longitude=-0.1276, latitude=51.5074) distance = SpatialCalculations.distance_between(coord1, coord2) print(f"Distance: {distance:.1f} km")
- classmethod bearing_between(from_obj: HasCoordinates, to_obj: HasCoordinates) float | None[source]
Calculate the initial bearing (azimuth) between two objects.
- Parameters:
from_obj – Source object with coordinates
to_obj – Destination object with coordinates
- Returns:
Initial bearing in degrees (0-360), where 0° = North, 90° = East, etc.
Example:
bearing = SpatialCalculations.bearing_between(coord1, coord2) print(f"Bearing: {bearing:.1f}°")
- classmethod midpoint(obj1: HasCoordinates, obj2: HasCoordinates) Coordinate | None[source]
Find the geographic midpoint between two objects.
- Parameters:
obj1 – First object with coordinates
obj2 – Second object with coordinates
- Returns:
Coordinate representing the midpoint, or None if calculation fails
- classmethod distances_to_many(from_obj: HasCoordinates, to_objects: List[HasCoordinates], unit: DistanceUnit = DistanceUnit.KILOMETERS) List[float | None][source]
Calculate distances from one object to many others efficiently.
- Parameters:
from_obj – Source object with coordinates
to_objects – List of destination objects
unit – Unit for distance measurements
- Returns:
List of distances (None for objects without coordinates)
Example:
center = Coordinate(longitude=0, latitude=0) targets = [coord1, coord2, coord3, coord4] distances = SpatialCalculations.distances_to_many(center, targets) for i, dist in enumerate(distances): if dist is not None: print(f"Distance to target {i}: {dist:.1f} km")
Distance Calculation Strategies
The spatial module provides multiple strategies for distance calculation, each with different trade-offs between accuracy and performance.
- class kmlorm.spatial.HaversineStrategy
Great circle distance using the Haversine formula. This is the default strategy, providing a good balance of speed and accuracy.
Accuracy: ±0.5% for most distances
Performance: Fast (O(1) with simple trigonometric operations)
Best for: General purpose distance calculations
- class kmlorm.spatial.VincentyStrategy
Vincenty’s formulae for accurate distance on an oblate spheroid (WGS84 ellipsoid). More accurate than Haversine but significantly slower.
Accuracy: ±0.5mm for distances up to ~20,000 km
Performance: Slower (iterative algorithm)
Best for: High-precision applications requiring maximum accuracy
- class kmlorm.spatial.EuclideanApproximation
Fast Euclidean approximation using equirectangular projection. Very fast but only accurate for small distances.
Accuracy: Good for distances <100km, decreases with distance and latitude
Performance: Very fast (O(1) with minimal operations)
Best for: Quick approximations when speed is critical and distances are small
- class kmlorm.spatial.AdaptiveStrategy
Adaptive strategy that automatically selects the best algorithm based on distance and requirements. Uses Euclidean for very small distances (<50km), Haversine for medium distances, and optionally Vincenty for long distances when high accuracy is requested.
Usage Examples
Basic Distance Calculations
from kmlorm.models import Placemark, Point
from kmlorm.models.point import Coordinate
# Distance between Coordinate objects
coord1 = Coordinate(longitude=-74.006, latitude=40.7128) # NYC
coord2 = Coordinate(longitude=-0.1276, latitude=51.5074) # London
distance = coord1.distance_to(coord2)
# Distance between Points
point1 = Point(coordinates=(-74.006, 40.7128))
point2 = Point(coordinates=(-0.1276, 51.5074))
distance = point1.distance_to(point2)
# Distance between Placemarks
place1 = Placemark(name="NYC", coordinates=(-74.006, 40.7128))
place2 = Placemark(name="London", coordinates=(-0.1276, 51.5074))
distance = place1.distance_to(place2)
# Mixed types - all work together
distance = coord1.distance_to(place2)
distance = point1.distance_to(coord2)
Working with Different Units
from kmlorm.spatial import DistanceUnit
# Default is kilometers
km = place1.distance_to(place2)
# Other units
meters = place1.distance_to(place2, unit=DistanceUnit.METERS)
miles = place1.distance_to(place2, unit=DistanceUnit.MILES)
nautical = place1.distance_to(place2, unit=DistanceUnit.NAUTICAL_MILES)
feet = place1.distance_to(place2, unit=DistanceUnit.FEET)
yards = place1.distance_to(place2, unit=DistanceUnit.YARDS)
print(f"Distance: {km:.1f} km = {miles:.1f} mi = {nautical:.1f} nm")
Finding Midpoints
# Find geographic midpoint
midpoint = place1.midpoint_to(place2)
print(f"Midpoint between NYC and London:")
print(f" Longitude: {midpoint.longitude:.4f}")
print(f" Latitude: {midpoint.latitude:.4f}")
# Create a new placemark at the midpoint
midpoint_place = Placemark(
name="Atlantic Midpoint",
coordinates=(midpoint.longitude, midpoint.latitude)
)
Bulk Distance Operations
from kmlorm.spatial.calculations import SpatialCalculations
# Efficient bulk distance calculations
center = Coordinate(longitude=0, latitude=0)
# Many target locations
targets = [
Placemark(name="North", coordinates=(0, 10)),
Placemark(name="South", coordinates=(0, -10)),
Placemark(name="East", coordinates=(10, 0)),
Placemark(name="West", coordinates=(-10, 0)),
Placemark(name="Northeast", coordinates=(10, 10)),
]
# Calculate all distances at once
distances = SpatialCalculations.distances_to_many(center, targets)
for target, distance in zip(targets, distances):
if distance is not None:
print(f"{target.name}: {distance:.1f} km")
Integration with QuerySets
The QuerySet’s near() method uses the spatial calculations internally:
from kmlorm.parsers import KMLFile
# Load KML file
kml = KMLFile.from_file("stores.kml")
# Find all stores within 50km of a location
center_lat, center_lon = 40.7128, -74.006 # NYC
nearby_stores = kml.placemarks.near(
longitude=center_lon,
latitude=center_lat,
radius_km=50
)
for store in nearby_stores:
# Each store has distance methods available
distance = store.distance_to((center_lon, center_lat))
bearing = store.bearing_to((center_lon, center_lat))
print(f"{store.name}: {distance:.1f} km at {bearing:.0f}°")
Edge Cases and Limitations
Date Line Crossing
The Haversine and Vincenty strategies correctly handle date line crossing:
# Points on opposite sides of the International Date Line
west_of_date_line = Coordinate(longitude=179.5, latitude=0)
east_of_date_line = Coordinate(longitude=-179.5, latitude=0)
# Correctly calculates ~111 km, not ~39,000 km
distance = west_of_date_line.distance_to(east_of_date_line)
Polar Regions
All strategies handle polar calculations correctly:
north_pole = Coordinate(longitude=0, latitude=90)
south_pole = Coordinate(longitude=0, latitude=-90)
# Pole to pole distance
distance = north_pole.distance_to(south_pole) # ~20,004 km
Coordinate Validation
Invalid coordinates are handled gracefully:
from kmlorm.models import Placemark
# Placemark without coordinates
place_no_coords = Placemark(name="Unknown Location")
# Returns None for objects without valid coordinates
distance = place_no_coords.distance_to(north_pole) # None
Performance Considerations
Caching
The spatial module uses LRU caching for repeated calculations:
# These repeated calculations are cached automatically
for i in range(1000):
distance = place1.distance_to(place2) # Cached after first calculation
Strategy Selection
Choose the appropriate strategy based on your needs:
from kmlorm.spatial.strategies import HaversineStrategy, VincentyStrategy
# For general use (default)
strategy = HaversineStrategy()
# For high precision requirements
strategy = VincentyStrategy()
# Use with SpatialCalculations
from kmlorm.spatial.calculations import SpatialCalculations
# The strategy can be selected internally based on requirements
Typical Use Cases
For typical KML files with hundreds of placemarks, the default Haversine strategy provides excellent performance and accuracy. The spatial calculations are optimized for this common use case.
API Reference
Spatial calculations package for KML ORM.
This package provides spatial calculations and utilities for geometric operations on KML elements including distance, bearing, and coordinate transformations.
- Key Components:
calculations: Core spatial calculation functions
exceptions: Spatial-specific exceptions
strategies: Distance calculation strategies
constants: WGS84 and other spatial constants
Examples
>>> from kmlorm.spatial.calculations import SpatialCalculations, DistanceUnit
>>> from kmlorm.models.point import Coordinate
>>>
>>> # Calculate distance between two coordinates
>>> coord1 = Coordinate(longitude=-74.006, latitude=40.7128) # NYC
>>> coord2 = Coordinate(longitude=-0.1276, latitude=51.5074) # London
>>> distance = SpatialCalculations.distance_between(coord1, coord2)
>>> print(f"Distance: {distance:.1f} km")
- class kmlorm.spatial.SpatialCalculations[source]
Bases:
objectSpatial calculation utilities following WGS84 datum.
All calculations assume: - WGS84 ellipsoid (a=6378137.0m, f=1/298.257223563) - Coordinates in decimal degrees - Longitude: -180 to 180 - Latitude: -90 to 90
Mathematical Accuracy: - Haversine formula: ±0.5% for most distances - Good for distances up to ~20,000 km - Assumes spherical Earth (mean radius 6371.0088 km)
- classmethod bearing_between(cls, from_obj, to_obj)[source]
Calculate the initial bearing from one object to another.
- Parameters:
- Return type:
- Returns:
Initial bearing in degrees (0-360), or None if coordinates unavailable 0° = North, 90° = East, 180° = South, 270° = West
- Raises:
SpatialCalculationError – If calculation fails
Examples
>>> coord1 = Coordinate(longitude=0, latitude=0) >>> coord2 = Coordinate(longitude=1, latitude=0) # Due east >>> bearing = SpatialCalculations.bearing_between(coord1, coord2) >>> print(f"Bearing: {bearing:.1f}°") # Should be ~90°
- classmethod bounding_box(cls, objects)[source]
Calculate minimum bounding rectangle for a set of objects.
- Parameters:
objects (
List[Union[HasCoordinates,Tuple[float,float],List[float]]]) – List of objects with coordinates- Return type:
- Returns:
Tuple of (min_lon, min_lat, max_lon, max_lat) or None if no valid coordinates
Examples
>>> points = [ ... Coordinate(longitude=-1, latitude=-1), ... Coordinate(longitude=1, latitude=1), ... Coordinate(longitude=0, latitude=2), ... ] >>> bbox = SpatialCalculations.bounding_box(points) >>> print(f"Bounding box: {bbox}") # (-1, -1, 1, 2)
- classmethod distance_between(cls, from_obj, to_obj, unit=DistanceUnit.KILOMETERS)[source]
Calculate distance between two objects with coordinates.
- Parameters:
- Return type:
- Returns:
Distance in specified units, or None if coordinates unavailable
- Raises:
SpatialCalculationError – If calculation fails
InvalidCoordinateError – If coordinates are invalid
Examples
>>> from kmlorm.models.point import Coordinate >>> coord1 = Coordinate(longitude=0, latitude=0) >>> coord2 = Coordinate(longitude=1, latitude=1) >>> distance = SpatialCalculations.distance_between(coord1, coord2) >>> print(f"Distance: {distance:.2f} km")
>>> # Using tuples >>> distance = SpatialCalculations.distance_between((0, 0), (1, 1))
>>> # Different units >>> distance_miles = SpatialCalculations.distance_between( ... coord1, coord2, unit=DistanceUnit.MILES ... )
- classmethod distances_to_many(cls, from_obj, to_objects, unit=DistanceUnit.KILOMETERS)[source]
Calculate distances from one object to many others efficiently.
This is more efficient than calling distance_between() repeatedly because it extracts the source coordinates once and reuses the calculation.
- Parameters:
- Return type:
- Returns:
List of distances in specified units (None for objects without coordinates)
Time Complexity: O(n) where n = len(to_objects) Space Complexity: O(n) for result list
Examples
>>> center = Coordinate(longitude=0, latitude=0) >>> points = [ ... Coordinate(longitude=1, latitude=0), ... Coordinate(longitude=0, latitude=1), ... Coordinate(longitude=-1, latitude=0), ... ] >>> distances = SpatialCalculations.distances_to_many(center, points)
- classmethod interpolate(cls, start, end, fraction)[source]
Find a point along the great circle path between two coordinates.
- Parameters:
- Return type:
Optional[Coordinate]- Returns:
Coordinate at the specified fraction along the path, or None if coordinates unavailable
- Raises:
ValueError – If fraction is not in range [0, 1]
Examples
>>> start = Coordinate(longitude=0, latitude=0) >>> end = Coordinate(longitude=10, latitude=10) >>> quarter_point = SpatialCalculations.interpolate(start, end, 0.25) >>> midpoint = SpatialCalculations.interpolate(start, end, 0.5)
- classmethod midpoint(cls, obj1, obj2)[source]
Find the geographic midpoint between two objects.
- Parameters:
- Return type:
Optional[Coordinate]- Returns:
Coordinate at the midpoint, or None if coordinates unavailable
Examples
>>> coord1 = Coordinate(longitude=0, latitude=0) >>> coord2 = Coordinate(longitude=2, latitude=2) >>> midpoint = SpatialCalculations.midpoint(coord1, coord2) >>> print(f"Midpoint: {midpoint.longitude}, {midpoint.latitude}")
- class kmlorm.spatial.DistanceUnit(value)[source]
Bases:
EnumUnits for distance measurements with conversion factors relative to kilometers.
The values represent the number of units per kilometer. For example, METERS = 1000 means 1 km = 1000 meters.
- METERS = 1000.0
- KILOMETERS = 1.0
- MILES = 0.621371
- NAUTICAL_MILES = 0.539957
- FEET = 3280.84
- YARDS = 1093.61
- class kmlorm.spatial.HasCoordinates(*args, **kwargs)[source]
Bases:
ProtocolProtocol for objects that can provide coordinates.
This protocol enables duck typing for any object that can return a Coordinate representation of itself.
- __init__(*args, **kwargs)
- exception kmlorm.spatial.SpatialCalculationError[source]
Bases:
ExceptionBase exception for spatial calculations.
Raised when spatial calculations encounter errors that cannot be handled gracefully, such as mathematical errors or invalid geometric configurations.
- exception kmlorm.spatial.InvalidCoordinateError[source]
Bases:
SpatialCalculationErrorRaised when coordinates are invalid or out of bounds.
This includes: - Longitude not in range [-180, 180] - Latitude not in range [-90, 90] - Non-numeric coordinate values - NaN or infinite coordinate values
Examples
>>> from kmlorm.models.point import Coordinate >>> try: ... coord = Coordinate(longitude=200, latitude=45) # Invalid longitude ... except InvalidCoordinateError as e: ... print(f"Invalid coordinate: {e}")
- exception kmlorm.spatial.InsufficientDataError[source]
Bases:
SpatialCalculationErrorRaised when not enough data is available for spatial calculation.
This occurs when: - Objects don’t have coordinate information - Required coordinate components are missing - Input data is incomplete for the requested calculation
Examples
>>> from kmlorm.spatial.calculations import SpatialCalculations >>> from kmlorm.models.placemark import Placemark >>> placemark_no_coords = Placemark(name="No coordinates") >>> coord = Coordinate(longitude=0, latitude=0) >>> try: ... distance = SpatialCalculations.distance_between(placemark_no_coords, coord) ... except InsufficientDataError as e: ... print(f"Cannot calculate: {e}")
Core spatial calculations for KML ORM.
This module provides spatial calculation utilities following WGS84 datum standards. All calculations assume coordinates in decimal degrees with longitude in range [-180, 180] and latitude in range [-90, 90].
- Key Features:
Protocol-based design for type safety
Multiple distance calculation strategies
Unit conversion support
Comprehensive error handling
Performance optimizations with caching
Examples
>>> from kmlorm.models.point import Coordinate
>>> coord1 = Coordinate(longitude=-74.006, latitude=40.7128) # NYC
>>> coord2 = Coordinate(longitude=-0.1276, latitude=51.5074) # London
>>>
>>> distance = SpatialCalculations.distance_between(coord1, coord2)
>>> print(f"Distance: {distance:.1f} km")
>>>
>>> bearing = SpatialCalculations.bearing_between(coord1, coord2)
>>> print(f"Bearing: {bearing:.1f} degrees")
- class kmlorm.spatial.calculations.DistanceUnit(value)[source]
Bases:
EnumUnits for distance measurements with conversion factors relative to kilometers.
The values represent the number of units per kilometer. For example, METERS = 1000 means 1 km = 1000 meters.
- METERS = 1000.0
- KILOMETERS = 1.0
- MILES = 0.621371
- NAUTICAL_MILES = 0.539957
- FEET = 3280.84
- YARDS = 1093.61
- class kmlorm.spatial.calculations.HasCoordinates(*args, **kwargs)[source]
Bases:
ProtocolProtocol for objects that can provide coordinates.
This protocol enables duck typing for any object that can return a Coordinate representation of itself.
- get_coordinates()[source]
Return the coordinate representation of this object.
- Return type:
Optional[Coordinate]- Returns:
Coordinate object if available, None if no coordinates exist
- __init__(*args, **kwargs)
- kmlorm.spatial.calculations.log_spatial_operation(func)[source]
Decorator to log spatial operations for monitoring and debugging.
Logs slow operations (>0.1s) and operations that return None.
- Return type:
- class kmlorm.spatial.calculations.SpatialCalculations[source]
Bases:
objectSpatial calculation utilities following WGS84 datum.
All calculations assume: - WGS84 ellipsoid (a=6378137.0m, f=1/298.257223563) - Coordinates in decimal degrees - Longitude: -180 to 180 - Latitude: -90 to 90
Mathematical Accuracy: - Haversine formula: ±0.5% for most distances - Good for distances up to ~20,000 km - Assumes spherical Earth (mean radius 6371.0088 km)
- classmethod distance_between(cls, from_obj, to_obj, unit=DistanceUnit.KILOMETERS)[source]
Calculate distance between two objects with coordinates.
- Parameters:
- Return type:
- Returns:
Distance in specified units, or None if coordinates unavailable
- Raises:
SpatialCalculationError – If calculation fails
InvalidCoordinateError – If coordinates are invalid
Examples
>>> from kmlorm.models.point import Coordinate >>> coord1 = Coordinate(longitude=0, latitude=0) >>> coord2 = Coordinate(longitude=1, latitude=1) >>> distance = SpatialCalculations.distance_between(coord1, coord2) >>> print(f"Distance: {distance:.2f} km")
>>> # Using tuples >>> distance = SpatialCalculations.distance_between((0, 0), (1, 1))
>>> # Different units >>> distance_miles = SpatialCalculations.distance_between( ... coord1, coord2, unit=DistanceUnit.MILES ... )
- classmethod bearing_between(cls, from_obj, to_obj)[source]
Calculate the initial bearing from one object to another.
- Parameters:
- Return type:
- Returns:
Initial bearing in degrees (0-360), or None if coordinates unavailable 0° = North, 90° = East, 180° = South, 270° = West
- Raises:
SpatialCalculationError – If calculation fails
Examples
>>> coord1 = Coordinate(longitude=0, latitude=0) >>> coord2 = Coordinate(longitude=1, latitude=0) # Due east >>> bearing = SpatialCalculations.bearing_between(coord1, coord2) >>> print(f"Bearing: {bearing:.1f}°") # Should be ~90°
- classmethod midpoint(cls, obj1, obj2)[source]
Find the geographic midpoint between two objects.
- Parameters:
- Return type:
Optional[Coordinate]- Returns:
Coordinate at the midpoint, or None if coordinates unavailable
Examples
>>> coord1 = Coordinate(longitude=0, latitude=0) >>> coord2 = Coordinate(longitude=2, latitude=2) >>> midpoint = SpatialCalculations.midpoint(coord1, coord2) >>> print(f"Midpoint: {midpoint.longitude}, {midpoint.latitude}")
- classmethod distances_to_many(cls, from_obj, to_objects, unit=DistanceUnit.KILOMETERS)[source]
Calculate distances from one object to many others efficiently.
This is more efficient than calling distance_between() repeatedly because it extracts the source coordinates once and reuses the calculation.
- Parameters:
- Return type:
- Returns:
List of distances in specified units (None for objects without coordinates)
Time Complexity: O(n) where n = len(to_objects) Space Complexity: O(n) for result list
Examples
>>> center = Coordinate(longitude=0, latitude=0) >>> points = [ ... Coordinate(longitude=1, latitude=0), ... Coordinate(longitude=0, latitude=1), ... Coordinate(longitude=-1, latitude=0), ... ] >>> distances = SpatialCalculations.distances_to_many(center, points)
- classmethod bounding_box(cls, objects)[source]
Calculate minimum bounding rectangle for a set of objects.
- Parameters:
objects (
List[Union[HasCoordinates,Tuple[float,float],List[float]]]) – List of objects with coordinates- Return type:
- Returns:
Tuple of (min_lon, min_lat, max_lon, max_lat) or None if no valid coordinates
Examples
>>> points = [ ... Coordinate(longitude=-1, latitude=-1), ... Coordinate(longitude=1, latitude=1), ... Coordinate(longitude=0, latitude=2), ... ] >>> bbox = SpatialCalculations.bounding_box(points) >>> print(f"Bounding box: {bbox}") # (-1, -1, 1, 2)
- classmethod interpolate(cls, start, end, fraction)[source]
Find a point along the great circle path between two coordinates.
- Parameters:
- Return type:
Optional[Coordinate]- Returns:
Coordinate at the specified fraction along the path, or None if coordinates unavailable
- Raises:
ValueError – If fraction is not in range [0, 1]
Examples
>>> start = Coordinate(longitude=0, latitude=0) >>> end = Coordinate(longitude=10, latitude=10) >>> quarter_point = SpatialCalculations.interpolate(start, end, 0.25) >>> midpoint = SpatialCalculations.interpolate(start, end, 0.5)
Distance calculation strategies for spatial operations.
This module provides different strategies for calculating distances between coordinates, each with different trade-offs between accuracy and performance.
- Available Strategies:
HaversineStrategy: Good balance of speed and accuracy (default)
VincentyStrategy: High accuracy for oblate spheroid (slower)
EuclideanApproximation: Fast approximation for small distances
- Usage:
>>> from kmlorm.spatial.strategies import HaversineStrategy, VincentyStrategy >>> strategy = HaversineStrategy() >>> distance = strategy.calculate(40.7128, -74.006, 51.5074, -0.1276)
- class kmlorm.spatial.strategies.DistanceStrategy[source]
Bases:
ABCAbstract base class for distance calculation strategies.
All strategies should implement the calculate method to compute distance between two points in decimal degrees.
- abstractmethod calculate(lat1, lon1, lat2, lon2)[source]
Calculate distance between two points.
- Parameters:
- Return type:
- Returns:
Distance in kilometers
- Raises:
ValueError – If coordinates are invalid
- class kmlorm.spatial.strategies.HaversineStrategy[source]
Bases:
DistanceStrategyGreat circle distance using Haversine formula.
This is a good balance of speed and accuracy for most applications. Assumes a spherical Earth with mean radius of 6371.0088 km.
Accuracy: ±0.5% for most distances Performance: Fast (O(1) with simple trigonometric operations) Best for: General purpose distance calculations
- Formula:
a = sin²(Δφ/2) + cos(φ1) * cos(φ2) * sin²(Δλ/2) c = 2 * atan2(√a, √(1−a)) d = R * c
Where φ is latitude, λ is longitude, R is Earth’s radius.
- class kmlorm.spatial.strategies.VincentyStrategy(max_iterations=100, tolerance=1e-12)[source]
Bases:
DistanceStrategyVincenty’s formulae for accurate distance on oblate spheroid.
This strategy uses Vincenty’s inverse formula for calculating distances on an oblate spheroid (WGS84 ellipsoid). More accurate than Haversine but significantly slower.
Accuracy: ±0.5mm for distances up to ~20,000 km Performance: Slower (iterative algorithm) Best for: High-precision applications requiring maximum accuracy
- Reference: T. Vincenty, “Direct and Inverse Solutions of Geodesics on the
Ellipsoid with application of nested equations”, Survey Review, vol XXIII no 176, 1975
- calculate(lat1, lon1, lat2, lon2)[source]
Calculate distance using Vincenty’s inverse formula.
- Parameters:
- Return type:
- Returns:
Distance in kilometers
- Raises:
ValueError – If coordinates are invalid or calculation doesn’t converge
- class kmlorm.spatial.strategies.EuclideanApproximation[source]
Bases:
DistanceStrategyFast Euclidean approximation for small distances.
Uses equirectangular projection (plate carrée) to approximate distances. Very fast but only accurate for small distances (typically <100km).
Accuracy: Good for distances <100km, decreases with distance and latitude Performance: Very fast (O(1) with minimal operations) Best for: Quick approximations, filtering, when speed is critical
- Formula:
x = Δλ * cos(φm) y = Δφ d = R * √(x² + y²)
Where φm is the mean latitude.
- calculate(lat1, lon1, lat2, lon2)[source]
Calculate distance using Euclidean approximation.
- Parameters:
- Return type:
- Returns:
Distance in kilometers
Note
This is an approximation and becomes less accurate for: - Large distances (>100km) - High latitudes (near poles) - Distances crossing large longitude differences
- class kmlorm.spatial.strategies.AdaptiveStrategy(high_accuracy=False)[source]
Bases:
DistanceStrategyAdaptive strategy that selects the best algorithm based on distance and requirements.
This strategy automatically chooses between different algorithms based on the characteristics of the calculation: - Euclidean for very small distances (<50km) - Haversine for medium distances (50km - 10,000km) - Vincenty for long distances (>10,000km) when high accuracy is needed
This provides a good balance of performance and accuracy for mixed workloads.
- __init__(high_accuracy=False)[source]
Initialize adaptive strategy.
- Parameters:
high_accuracy (
bool) – Whether to prefer accuracy over speed for long distances
Spatial calculation constants.
This module defines constants used in spatial calculations, including Earth parameters, coordinate system definitions, and conversion factors.
Spatial calculation exceptions.
This module defines exceptions specific to spatial calculations and operations. These exceptions provide detailed context for spatial calculation failures.
- exception kmlorm.spatial.exceptions.SpatialCalculationError[source]
Bases:
ExceptionBase exception for spatial calculations.
Raised when spatial calculations encounter errors that cannot be handled gracefully, such as mathematical errors or invalid geometric configurations.
- exception kmlorm.spatial.exceptions.InvalidCoordinateError[source]
Bases:
SpatialCalculationErrorRaised when coordinates are invalid or out of bounds.
This includes: - Longitude not in range [-180, 180] - Latitude not in range [-90, 90] - Non-numeric coordinate values - NaN or infinite coordinate values
Examples
>>> from kmlorm.models.point import Coordinate >>> try: ... coord = Coordinate(longitude=200, latitude=45) # Invalid longitude ... except InvalidCoordinateError as e: ... print(f"Invalid coordinate: {e}")
- exception kmlorm.spatial.exceptions.InsufficientDataError[source]
Bases:
SpatialCalculationErrorRaised when not enough data is available for spatial calculation.
This occurs when: - Objects don’t have coordinate information - Required coordinate components are missing - Input data is incomplete for the requested calculation
Examples
>>> from kmlorm.spatial.calculations import SpatialCalculations >>> from kmlorm.models.placemark import Placemark >>> placemark_no_coords = Placemark(name="No coordinates") >>> coord = Coordinate(longitude=0, latitude=0) >>> try: ... distance = SpatialCalculations.distance_between(placemark_no_coords, coord) ... except InsufficientDataError as e: ... print(f"Cannot calculate: {e}")