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

transformations.mddocs/

Transformations

Functions for transforming raster images and vector data between different coordinate systems. Includes regridding, warping, mesh generation, and utilities for handling cyclic data.

Capabilities

Image Transformations

Transform raster images and arrays between different coordinate reference systems.

def warp_array(array, target_proj, source_proj=None, target_res=(400, 200),
               source_extent=None, target_extent=None, mask_extrapolated=False):
    """
    Warp numpy array to target projection.
    
    Parameters:
    - array: numpy.ndarray, source data array
    - target_proj: cartopy.crs.CRS, target projection
    - source_proj: cartopy.crs.CRS, source projection (default PlateCarree)
    - target_res: tuple, target resolution (width, height)
    - source_extent: tuple, source extent (x0, x1, y0, y1)  
    - target_extent: tuple, target extent (x0, x1, y0, y1)
    - mask_extrapolated: bool, mask extrapolated values
    
    Returns:
    Tuple of (warped_array, target_extent)
    """

def warp_img(fname, target_proj, source_proj=None, target_res=(400, 200)):
    """
    Warp image file to target projection.
    
    Parameters:
    - fname: str, path to image file
    - target_proj: cartopy.crs.CRS, target projection
    - source_proj: cartopy.crs.CRS, source projection (default PlateCarree)
    - target_res: tuple, target resolution (width, height)
    
    Returns:
    Tuple of (warped_array, target_extent)
    """

def regrid(array, source_x_coords, source_y_coords, source_proj, target_proj,
           target_res, mask_extrapolated=False):
    """
    Regrid array between coordinate systems.
    
    Parameters:
    - array: numpy.ndarray, source data
    - source_x_coords: array-like, source x coordinates
    - source_y_coords: array-like, source y coordinates  
    - source_proj: cartopy.crs.CRS, source projection
    - target_proj: cartopy.crs.CRS, target projection
    - target_res: tuple, target resolution (width, height)
    - mask_extrapolated: bool, mask extrapolated values
    
    Returns:
    Tuple of (regridded_array, target_x_coords, target_y_coords)
    """

def mesh_projection(projection, nx, ny, x_extents=(None, None), y_extents=(None, None)):
    """
    Generate coordinate mesh for projection.
    
    Parameters:
    - projection: cartopy.crs.CRS, target projection
    - nx: int, number of x grid points
    - ny: int, number of y grid points
    - x_extents: tuple, x extent limits (min, max)
    - y_extents: tuple, y extent limits (min, max)
    
    Returns:
    Tuple of (x_mesh, y_mesh) coordinate arrays
    """

Vector Field Transformations

Transform vector fields and scalar data to regular grids.

def vector_scalar_to_grid(src_crs, target_proj, regrid_shape, x, y, u, v, *scalars, **kwargs):
    """
    Transform vector and scalar fields to regular grid.
    
    Parameters:
    - src_crs: cartopy.crs.CRS, source coordinate system
    - target_proj: cartopy.crs.CRS, target projection
    - regrid_shape: tuple, output grid shape (nx, ny)
    - x: array-like, source x coordinates
    - y: array-like, source y coordinates
    - u: array-like, x-component of vector field
    - v: array-like, y-component of vector field  
    - *scalars: additional scalar fields to transform
    - **kwargs: additional interpolation parameters
    
    Returns:
    Tuple of (target_x, target_y, target_u, target_v, *target_scalars)
    """

Cyclic Data Utilities

Handle periodic/cyclic data boundaries common in global datasets.

def add_cyclic_point(data, coord=None, axis=-1):
    """
    Add cyclic point to data array.
    
    Parameters:
    - data: array-like, input data array
    - coord: array-like, coordinate array (optional)
    - axis: int, axis along which to add cyclic point
    
    Returns:
    If coord provided: tuple of (extended_data, extended_coord)
    Otherwise: extended_data
    """

def add_cyclic(data, x=None, y=None, axis=-1, cyclic=360, precision=1e-4):
    """
    Add cyclic point with coordinate handling.
    
    Parameters:
    - data: array-like, input data
    - x: array-like, x coordinates (optional)
    - y: array-like, y coordinates (optional) 
    - axis: int, cyclic axis
    - cyclic: float, cyclic interval (default 360 for longitude)
    - precision: float, precision for cyclic detection
    
    Returns:
    Tuple of (extended_data, extended_x, extended_y) or subsets based on inputs
    """

def has_cyclic(x, axis=-1, cyclic=360, precision=1e-4):
    """
    Check if coordinates already have cyclic point.
    
    Parameters:
    - x: array-like, coordinate array
    - axis: int, axis to check
    - cyclic: float, expected cyclic interval
    - precision: float, precision for comparison
    
    Returns:
    bool, True if cyclic point exists
    """

def add_cyclic(data, x=None, y=None, axis=-1, cyclic=360, precision=1e-4):
    """
    Add cyclic point with coordinate handling (newer version of add_cyclic_point).
    
    Parameters:
    - data: array-like, input data
    - x: array-like, x coordinates (optional)
    - y: array-like, y coordinates (optional) 
    - axis: int, cyclic axis
    - cyclic: float, cyclic interval (default 360 for longitude)
    - precision: float, precision for cyclic detection
    
    Returns:
    Tuple of (extended_data, extended_x, extended_y) or subsets based on inputs
    """

Geodesic Calculations

Geodesic computations on ellipsoidal Earth models.

class Geodesic:
    """Geodesic calculations on ellipsoid."""
    def __init__(self, a, f):
        """
        Parameters:
        - a: float, semi-major axis
        - f: float, flattening
        """
    
    def inverse(self, lat1, lon1, lat2, lon2):
        """
        Compute inverse geodesic problem.
        
        Parameters:
        - lat1, lon1: float, first point coordinates
        - lat2, lon2: float, second point coordinates
        
        Returns:
        Dict with distance, azimuth angles
        """
    
    def direct(self, lat1, lon1, azi1, s12):
        """
        Compute direct geodesic problem.
        
        Parameters:
        - lat1, lon1: float, starting point
        - azi1: float, initial azimuth
        - s12: float, distance
        
        Returns:
        Dict with end point coordinates and final azimuth
        """

Usage Examples

Warping Images Between Projections

import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
from cartopy.img_transform import warp_array

# Create sample data on regular lat/lon grid
lons = np.linspace(-180, 180, 360)
lats = np.linspace(-90, 90, 180)
lon_grid, lat_grid = np.meshgrid(lons, lats)
data = np.sin(np.radians(lat_grid)) * np.cos(np.radians(lon_grid))

# Warp to different projections
source_proj = ccrs.PlateCarree()
target_proj = ccrs.Orthographic(central_longitude=-90, central_latitude=45)

warped_data, target_extent = warp_array(
    data, 
    target_proj, 
    source_proj=source_proj,
    target_res=(400, 400)
)

# Plot results
fig, axes = plt.subplots(1, 2, figsize=(15, 6))

# Original data
ax1 = plt.subplot(1, 2, 1, projection=source_proj)
ax1.contourf(lon_grid, lat_grid, data, transform=source_proj)
ax1.coastlines()
ax1.set_global()
ax1.set_title('Original Data (PlateCarree)')

# Warped data  
ax2 = plt.subplot(1, 2, 2, projection=target_proj)
ax2.imshow(warped_data, extent=target_extent, transform=target_proj)
ax2.coastlines()
ax2.set_global()
ax2.set_title('Warped Data (Orthographic)')

plt.tight_layout()
plt.show()

Handling Cyclic Data

import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
from cartopy.util import add_cyclic_point

# Create longitude data that doesn't include 360°
lons = np.arange(0, 360, 5)  # 0 to 355
lats = np.arange(-90, 91, 5)
lon_grid, lat_grid = np.meshgrid(lons, lats)

# Sample data
data = np.sin(np.radians(lat_grid)) * np.cos(np.radians(lon_grid))

# Add cyclic point to avoid gap at 180°/-180°
cyclic_data, cyclic_lons = add_cyclic_point(data, coord=lons)

# Plot comparison
fig, axes = plt.subplots(1, 2, figsize=(15, 6),
                        subplot_kw={'projection': ccrs.PlateCarree()})

# Without cyclic point
lon_mesh, lat_mesh = np.meshgrid(lons, lats)
axes[0].pcolormesh(lon_mesh, lat_mesh, data, transform=ccrs.PlateCarree())
axes[0].coastlines()
axes[0].set_global()
axes[0].set_title('Without Cyclic Point (Gap at Dateline)')

# With cyclic point
cyclic_lon_mesh, cyclic_lat_mesh = np.meshgrid(cyclic_lons, lats)
axes[1].pcolormesh(cyclic_lon_mesh, cyclic_lat_mesh, cyclic_data, 
                   transform=ccrs.PlateCarree())
axes[1].coastlines()
axes[1].set_global()
axes[1].set_title('With Cyclic Point (No Gap)')

plt.tight_layout()
plt.show()

Vector Field Transformation

import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
from cartopy.vector_transform import vector_scalar_to_grid

# Create irregular vector field data
np.random.seed(42)
x_pts = np.random.uniform(-180, 180, 100)
y_pts = np.random.uniform(-90, 90, 100)
u_pts = 10 * np.sin(np.radians(y_pts))
v_pts = 5 * np.cos(np.radians(x_pts))

# Transform to regular grid
source_crs = ccrs.PlateCarree()
target_proj = ccrs.PlateCarree()

# Regular grid output
x_reg, y_reg, u_reg, v_reg = vector_scalar_to_grid(
    source_crs, target_proj, 
    regrid_shape=(20, 15),  # 20x15 regular grid
    x=x_pts, y=y_pts, u=u_pts, v=v_pts
)

# Plot results
fig = plt.figure(figsize=(15, 10))

# Original irregular data
ax1 = plt.subplot(2, 1, 1, projection=ccrs.PlateCarree())
ax1.quiver(x_pts, y_pts, u_pts, v_pts, transform=ccrs.PlateCarree(),
           alpha=0.7, color='blue', scale=200)
ax1.coastlines()
ax1.set_global()
ax1.set_title('Original Irregular Vector Field')

# Interpolated regular grid
ax2 = plt.subplot(2, 1, 2, projection=ccrs.PlateCarree())
ax2.quiver(x_reg, y_reg, u_reg, v_reg, transform=ccrs.PlateCarree(),
           color='red', scale=200)
ax2.coastlines()
ax2.set_global()
ax2.set_title('Interpolated Regular Grid')

plt.tight_layout()
plt.show()

Creating Projection Meshes

import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
from cartopy.img_transform import mesh_projection

# Generate coordinate mesh for Lambert Conformal projection
proj = ccrs.LambertConformal(central_longitude=-95, central_latitude=39)

# Create mesh over continental US region
x_mesh, y_mesh = mesh_projection(proj, nx=50, ny=40,
                                x_extents=(-2.5e6, 2.5e6),
                                y_extents=(-1.5e6, 1.5e6))

# Plot the mesh
fig = plt.figure(figsize=(12, 8))
ax = plt.axes(projection=proj)

# Plot mesh points
ax.scatter(x_mesh.flatten(), y_mesh.flatten(), s=1, 
           transform=proj, alpha=0.5)

# Add geographic features
ax.coastlines()
ax.add_feature(cfeature.BORDERS)
ax.set_extent([-125, -65, 25, 55], ccrs.PlateCarree())

plt.title('Lambert Conformal Projection Mesh')
plt.show()

Geodesic Calculations

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

# WGS84 ellipsoid parameters
geod = Geodesic(6378137.0, 1/298.257223563)

# Calculate great circle between two cities
# New York to London
ny_lat, ny_lon = 40.7128, -74.0060
london_lat, london_lon = 51.5074, -0.1278

result = geod.inverse(ny_lat, ny_lon, london_lat, london_lon)
distance_km = result['distance'] / 1000
azimuth = result['azi1']

print(f"Distance: {distance_km:.0f} km")
print(f"Initial bearing: {azimuth:.1f}°")

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

# Plot cities
ax.scatter([ny_lon, london_lon], [ny_lat, london_lat], 
           s=100, c=['red', 'blue'], transform=ccrs.PlateCarree())

# Add great circle (simplified - would need more points for accuracy)
ax.plot([ny_lon, london_lon], [ny_lat, london_lat], 
        'r--', transform=ccrs.Geodetic(), linewidth=2)

ax.coastlines()
ax.set_global()
ax.gridlines(draw_labels=True)
plt.title(f'Great Circle: New York to London ({distance_km:.0f} km)')
plt.show()

Advanced Transformations

Custom Interpolation

from cartopy.vector_transform import vector_scalar_to_grid

# Custom interpolation with different methods
x_reg, y_reg, u_reg, v_reg, temp_reg = vector_scalar_to_grid(
    ccrs.PlateCarree(), ccrs.LambertConformal(),
    regrid_shape=(30, 25),
    x=lon_data, y=lat_data, u=u_wind, v=v_wind, temperature_data,
    method='linear',  # or 'nearest', 'cubic'
    fill_value=np.nan
)

Masking Extrapolated Values

# Warp with extrapolation masking
warped_data, extent = warp_array(
    original_data, target_projection,
    source_proj=ccrs.PlateCarree(),
    mask_extrapolated=True  # Mask values outside original domain
)

# Masked values will be np.nan
valid_mask = ~np.isnan(warped_data)

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