CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-cartopy

A Python package designed to make drawing maps for data analysis and visualisation easy

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

geodesic.mddocs/

Geodesic Calculations

Great circle computations, distance calculations, and geodesic operations on ellipsoidal Earth models. Essential for navigation, surveying, and geographic analysis applications that require accurate distance and bearing calculations.

Capabilities

Geodesic Class

Core class for geodesic calculations on ellipsoidal Earth models using Proj geodesic functions.

class Geodesic:
    """
    Define an ellipsoid on which to solve geodesic problems.
    
    Parameters:
    - radius: float, equatorial radius in metres (default: WGS84 semimajor axis 6378137.0)
    - flattening: float, flattening of ellipsoid (default: WGS84 flattening 1/298.257223563)
    """
    def __init__(self, radius=6378137.0, flattening=1/298.257223563): ...
    
    def direct(self, points, azimuths, distances):
        """
        Solve direct geodesic problem (distance and azimuth to endpoint).
        
        Parameters:
        - points: array_like, shape=(n or 1, 2), starting longitude-latitude points
        - azimuths: float or array_like, azimuth values in degrees
        - distances: float or array_like, distance values in metres
        
        Returns:
        numpy.ndarray, shape=(n, 3), longitudes, latitudes, and forward azimuths of endpoints
        """
    
    def inverse(self, points, endpoints):
        """
        Solve inverse geodesic problem (distance and azimuth between points).
        
        Parameters:
        - points: array_like, shape=(n or 1, 2), starting longitude-latitude points
        - endpoints: array_like, shape=(n or 1, 2), ending longitude-latitude points
        
        Returns:
        numpy.ndarray, shape=(n, 3), distances and forward azimuths of start/end points
        """
    
    def circle(self, lon, lat, radius, n_samples=180, endpoint=False):
        """
        Create geodesic circle of given radius at specified point.
        
        Parameters:
        - lon: float, longitude coordinate of center
        - lat: float, latitude coordinate of center  
        - radius: float, radius of circle in metres
        - n_samples: int, number of sample points on circle (default 180)
        - endpoint: bool, whether to repeat endpoint (default False)
        
        Returns:
        numpy.ndarray, shape=(n_samples, 2), longitude-latitude points on circle
        """
    
    def geometry_length(self, geometry):
        """
        Calculate physical length of Shapely geometry on ellipsoid.
        
        Parameters:
        - geometry: shapely.geometry.BaseGeometry, geometry in spherical coordinates
        
        Returns:
        float, length in metres
        """

Usage Examples

Basic Distance and Bearing Calculations

from cartopy.geodesic import Geodesic
import numpy as np

# Create geodesic calculator with WGS84 ellipsoid
geod = Geodesic()

# Calculate distance between two cities
# New York to London
ny_coords = [-74.0060, 40.7128]  # [lon, lat]
london_coords = [-0.1278, 51.5074]

# Inverse problem: find distance and bearing
result = geod.inverse(ny_coords, london_coords)
distance_m = result[0, 0]
initial_bearing = result[0, 1]
final_bearing = result[0, 2]

print(f"Distance: {distance_m/1000:.0f} km")
print(f"Initial bearing: {initial_bearing:.1f}°")
print(f"Final bearing: {final_bearing:.1f}°")

Direct Geodesic Problem

from cartopy.geodesic import Geodesic

geod = Geodesic()

# Start from a point and travel a specific distance and bearing
start_point = [0.0, 51.5]  # London [lon, lat]
bearing = 45.0  # Northeast
distance = 100000  # 100 km

# Find endpoint
endpoint = geod.direct(start_point, bearing, distance)
end_lon = endpoint[0, 0]
end_lat = endpoint[0, 1]
final_bearing = endpoint[0, 2]

print(f"Endpoint: {end_lon:.4f}°, {end_lat:.4f}°")
print(f"Final bearing: {final_bearing:.1f}°")

Creating Geodesic Circles

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
from cartopy.geodesic import Geodesic

# Create geodesic calculator
geod = Geodesic()

# Create circles around major cities
cities = {
    'London': [0.0, 51.5],
    'New York': [-74.0, 40.7],
    'Tokyo': [139.7, 35.7]
}

fig = plt.figure(figsize=(15, 10))
ax = plt.axes(projection=ccrs.PlateCarree())

for city_name, coords in cities.items():
    # Create 1000 km radius circle
    circle_points = geod.circle(coords[0], coords[1], 1000000, n_samples=100)
    
    # Plot circle
    ax.plot(circle_points[:, 0], circle_points[:, 1], 
            transform=ccrs.PlateCarree(), label=f'{city_name} (1000km)')
    
    # Mark city center
    ax.plot(coords[0], coords[1], 'ro', transform=ccrs.PlateCarree(), 
            markersize=8)

ax.coastlines()
ax.set_global()
ax.legend()
plt.title('Geodesic Circles Around Major Cities')
plt.show()

Multiple Point Calculations with Broadcasting

from cartopy.geodesic import Geodesic
import numpy as np

geod = Geodesic()

# Calculate distances from London to multiple cities
london = [0.0, 51.5]
cities = np.array([
    [-74.0, 40.7],    # New York  
    [139.7, 35.7],    # Tokyo
    [151.2, -33.9],   # Sydney
    [-43.2, -22.9]    # Rio de Janeiro
])

# Use broadcasting: single start point, multiple endpoints
results = geod.inverse(london, cities)
distances_km = results[:, 0] / 1000
bearings = results[:, 1]

city_names = ['New York', 'Tokyo', 'Sydney', 'Rio de Janeiro']
for i, city in enumerate(city_names):
    print(f"London to {city}: {distances_km[i]:.0f} km, bearing {bearings[i]:.1f}°")

Geometry Length Calculations

from cartopy.geodesic import Geodesic
from shapely.geometry import LineString, Polygon
import numpy as np

geod = Geodesic()

# Calculate length of a route
route_coords = [
    [0.0, 51.5],      # London
    [2.3, 48.9],      # Paris
    [13.4, 52.5],     # Berlin
    [16.4, 48.2],     # Vienna
    [14.5, 46.0]      # Ljubljana
]

# Create LineString geometry
route = LineString(route_coords)

# Calculate total route length
route_length_km = geod.geometry_length(route) / 1000
print(f"Total route length: {route_length_km:.0f} km")

# Calculate polygon perimeter
# Create a triangle between three cities
triangle_coords = [
    [0.0, 51.5],      # London
    [2.3, 48.9],      # Paris  
    [4.9, 52.4],      # Amsterdam
    [0.0, 51.5]       # Back to London
]

triangle = Polygon(triangle_coords)
perimeter_km = geod.geometry_length(triangle) / 1000
print(f"Triangle perimeter: {perimeter_km:.0f} km")

Custom Ellipsoid Calculations

from cartopy.geodesic import Geodesic

# Create geodesic calculator for different ellipsoids

# Sphere (flattening = 0)
sphere_geod = Geodesic(radius=6371000, flattening=0.0)

# GRS80 ellipsoid
grs80_geod = Geodesic(radius=6378137.0, flattening=1/298.257222101)

# Calculate same distance on different ellipsoids
start = [0.0, 0.0]
end = [1.0, 1.0]

wgs84_dist = Geodesic().inverse(start, end)[0, 0]
sphere_dist = sphere_geod.inverse(start, end)[0, 0]
grs80_dist = grs80_geod.inverse(start, end)[0, 0]

print(f"WGS84 distance: {wgs84_dist:.2f} m")
print(f"Sphere distance: {sphere_dist:.2f} m") 
print(f"GRS80 distance: {grs80_dist:.2f} m")

Great Circle Paths for Plotting

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
from cartopy.geodesic import Geodesic
import numpy as np

geod = Geodesic()

# Create great circle path between distant points
start_point = [-120.0, 35.0]  # California
end_point = [140.0, 35.0]     # Japan

# Calculate intermediate points along great circle
# Use multiple direct calculations
result = geod.inverse(start_point, end_point)
total_distance = result[0, 0]
initial_bearing = result[0, 1]

# Create points every 500 km along the great circle
distances = np.arange(0, total_distance, 500000)
path_points = geod.direct(start_point, initial_bearing, distances)

# Plot the great circle path
fig = plt.figure(figsize=(15, 8))
ax = plt.axes(projection=ccrs.PlateCarree())

# Plot path points
ax.plot(path_points[:, 0], path_points[:, 1], 'r-', 
        transform=ccrs.Geodetic(), linewidth=2, label='Great Circle')

# Mark start and end points
ax.plot(start_point[0], start_point[1], 'go', 
        transform=ccrs.PlateCarree(), markersize=10, label='Start')
ax.plot(end_point[0], end_point[1], 'ro', 
        transform=ccrs.PlateCarree(), markersize=10, label='End')

ax.coastlines()
ax.set_global()
ax.legend()
plt.title('Great Circle Path: California to Japan')
plt.show()

Mathematical Background

The geodesic calculations in Cartopy use the algorithms developed by Charles Karney, which provide accurate solutions to geodesic problems on ellipsoids. These methods are implemented via the Proj library's geodesic functions.

Key Concepts:

  • Direct Problem: Given a starting point, initial bearing, and distance, find the endpoint
  • Inverse Problem: Given two points, find the distance and bearings between them
  • Geodesic: The shortest path between two points on an ellipsoid (generalization of great circles)
  • Azimuth: The angle measured clockwise from north to the geodesic direction

Accuracy:

The algorithms provide accuracy better than 15 nanometers for distances up to 20,000 km on the WGS84 ellipsoid.

Constants

# Default WGS84 parameters
WGS84_RADIUS: float = 6378137.0  # metres
WGS84_FLATTENING: float = 1/298.257223563

Install with Tessl CLI

npx tessl i tessl/pypi-cartopy

docs

coordinate-systems.md

data-io.md

geodesic.md

geographic-features.md

index.md

matplotlib-integration.md

transformations.md

tile.json