Geometry

Geospatial utilities: coordinate wrapping, haversine distances, UTM transforms, magnetic declination, and coordinate format conversions.

Geodesic geometry utilities.

Coordinate conversions, distance calculations, and geometric operations on the WGS84 ellipsoid. All distance and bearing calculations use Vincenty’s inverse and direct formulae via the pymap3d library.

Geometry references

Vincenty, T. (1975). Direct and inverse solutions of geodesics on the ellipsoid with application of nested equations. Survey Review, 23(176), 88-93. doi:10.1179/sre.1975.23.176.88

Hirsch, M. (2018). PyMap3D: 3-D coordinate conversions for terrestrial and geospace environments. Journal of Open Source Software, 3(23), 580. doi:10.21105/joss.00580

wrap_to_180(lon)[source]

Wrap angle(s) to the range [-180, 180) degrees.

Parameters:

lon (float or array-like) – Angle(s) in degrees.

Returns:

Angle(s) wrapped to [-180, 180).

Return type:

numpy.ndarray or float

wrap_to_360(angle)[source]

Wrap angle(s) to the range [0, 360) degrees.

Parameters:

angle (float or array-like) – Angle(s) in degrees.

Returns:

Angle(s) wrapped to [0, 360).

Return type:

numpy.ndarray

get_timezone(latitude, longitude)[source]

Return the IANA timezone name for a lat/lon (e.g. 'America/Los_Angeles').

Parameters:
  • latitude (float) – Latitude in decimal degrees, in [-90, 90].

  • longitude (float) – Longitude in decimal degrees, in [-180, 180].

Return type:

str

Returns:

IANA timezone name string. Coordinates over open ocean may return an 'Etc/GMT±N' zone.

Raises:
  • HyPlanValueError – If coordinates are out of range or no timezone can be determined for the location.

  • HyPlanRuntimeError – If the optional timezonefinder package is not installed.

calculate_geographic_mean(geometry)[source]

Calculate the geographic mean of coordinates from a Shapely geometry or a list of Shapely geometries using pymap3d.lox.meanm.

Parameters:

geometry (LineString, Polygon, Point, or list) – A single Shapely geometry or a list of Shapely geometries.

Returns:

Geographic mean as a Shapely Point.

Return type:

Point

get_utm_crs(lon, lat)[source]

Determine the UTM CRS for a given WGS84 coordinate.

UTM zones are determined arithmetically from longitude (6° wide) and hemisphere from latitude, so we compute the EPSG code directly instead of querying pyproj’s CRS database — this is both faster and exact.

Parameters:
  • lon (float) – Longitude in decimal degrees (WGS84).

  • lat (float) – Latitude in decimal degrees (WGS84).

Returns:

The appropriate UTM CRS for the coordinate. Latitudes outside

UTM’s defined validity range (84°N to 80°S) still map to the nearest UTM zone, with a warning logged — polar work should use a polar stereographic CRS instead.

Return type:

CRS

get_utm_transforms(geometry)[source]

Get the UTM CRS and transformation functions to/from WGS84 for a Shapely geometry or a list of geometries.

Parameters:

geometry (BaseGeometry or list of BaseGeometry) – A single Shapely geometry object or a list of geometries.

Returns:

Transformation functions:
  • wgs84_to_utm: Function to transform coordinates from WGS84 to UTM.

  • utm_to_wgs84: Function to transform coordinates from UTM to WGS84.

Return type:

Tuple[Callable[…, Any], Callable]

Raises:

ValueError – If the geometry is invalid, empty, or has no valid centroid.

haversine(lat1, lon1, lat2, lon2, radius=6371000.0)[source]

Calculate the haversine (great-circle) distance between two points.

Accepts scalars or numpy arrays for any of the coordinate arguments, so it works both as a one-off distance and as a vectorized calculation over many candidate points (e.g. an array of airport latitudes against a single query point).

Parameters:
Return type:

float | ndarray[Any, dtype[Any]]

Returns:

Distance(s) in the same unit as radius. Returns a scalar if all inputs are scalars, otherwise a numpy array broadcast to the inputs.

geodesic_midpoint(lat1, lon1, lat2, lon2)[source]

Geodesic midpoint of two points on the WGS-84 ellipsoid.

Computes the point at half the geodesic distance from (lat1, lon1) along the initial bearing toward (lat2, lon2), via Vincenty. This is the correct midpoint for any pair of points — unlike the naïve ((lat1+lat2)/2, (lon1+lon2)/2) arithmetic mean, which fails across the antimeridian, near the poles, and accumulates error on long legs at high latitude.

Parameters:
  • lat1 (float) – First endpoint in decimal degrees.

  • lon1 (float) – First endpoint in decimal degrees.

  • lat2 (float) – Second endpoint in decimal degrees.

  • lon2 (float) – Second endpoint in decimal degrees.

Return type:

tuple[float, float]

Returns:

(mid_lat, mid_lon) in decimal degrees; longitude in [-180, 180].

random_points_in_polygon(polygon, k)[source]

Generate k points chosen uniformly at random inside a polygon.

Uses Delaunay triangulation with area-weighted sampling to ensure uniform distribution across the polygon’s interior.

Parameters:
  • polygon (Polygon) – A Shapely Polygon to sample points from.

  • k (int) – Number of random points to generate.

Returns:

List of k Shapely Point objects inside the polygon.

Return type:

list[Point]

minimum_rotated_rectangle(polygon)[source]

Calculate the minimum rotated rectangle of a polygon in WGS84 coordinates.

Parameters:

polygon (Polygon) – Input polygon in WGS84 coordinates. Must be valid.

Returns:

The minimum rotated rectangle in WGS84 coordinates.

Use rectangle_dimensions() to extract the centroid, orientation, and side lengths from the returned rectangle.

Return type:

Polygon

Raises:

ValueError – If the input polygon is invalid or processing fails.

Notes

  • The input polygon is transformed to UTM for accurate geometry calculations.

rotated_rectangle(polygon, azimuth)[source]

Compute a rotated bounding rectangle around a Shapely polygon in WGS84 coordinates at a specified azimuth.

Parameters:
  • polygon (Polygon) – Input polygon in WGS84 coordinates. Must be valid.

  • azimuth (float) – Desired azimuth for the bounding rectangle in degrees. Will be wrapped to [-180, 180].

Returns:

The rotated bounding rectangle in WGS84 coordinates.

Return type:

Polygon

Raises:

ValueError – If the input polygon is invalid or if an error occurs during processing.

Notes

  • The input polygon is transformed to UTM for accurate geometry calculations.

  • The bounding rectangle is rotated to align with the specified azimuth.

  • The result is returned in WGS84 coordinates.

rectangle_dimensions(rectangle, azimuth=None)[source]

Extract centroid, length, width and orientation from a rotated rectangle.

Given a 4-sided polygon (e.g. the output of minimum_rotated_rectangle() or rotated_rectangle()), this returns the centroid, the orientation in degrees, and the lengths of the long and short edges in meters.

If azimuth is None, the rectangle’s orientation is taken from the longer edge. If azimuth is supplied, length_m is the side most aligned with that azimuth and width_m is the perpendicular side; the returned orientation is still wrapped to [-180, 180].

Parameters:
  • rectangle (Polygon) – A Shapely Polygon assumed to be a (rotated) rectangle in WGS84 coordinates.

  • azimuth (float | None) – Optional preferred orientation in degrees from true north.

Return type:

tuple[float, float, float, float, float]

Returns:

Tuple (lat0, lon0, azimuth, length_m, width_m).

translate_polygon(polygon, distance, azimuth)[source]

Translate a Shapely polygon by distance along the given compass azimuth.

Operates in the polygon’s native coordinate frame — the offset is applied as (dx, dy) = (distance·sin(az), distance·cos(az)) where +y is interpreted as grid-north and +x as grid-east. This is only meaningful for polygons in a projected CRS (e.g. UTM) where coordinate axes are nominally aligned with north/east and units match distance. The caller is responsible for projection; passing a WGS84 polygon will translate it by distance degrees of lat/lon, not metres, which is almost never what you want.

Parameters:
  • polygon (Polygon) – The input Shapely polygon, in a projected CRS.

  • distance (float) – Translation distance in the polygon’s coordinate units (typically metres).

  • azimuth (float) – Translation direction in degrees, measured clockwise from grid-north.

Returns:

The translated Shapely polygon in the same CRS as the input.

Return type:

Polygon

buffer_polygon_along_azimuth(polygon, along_track_distance, across_track_distance, azimuth)[source]

Expand a polygon along and across a flight azimuth.

Translates the polygon by along_track_distance in both the azimuth direction and its opposite, unions the results with the original, then repeats across-track (azimuth ± 90°) with across_track_distance.

Parameters:
  • polygon (Polygon) – The input Shapely polygon to be buffered in WGS84 coordinates. Must be valid.

  • along_track_distance (Union[float, ureg.Quantity]) – Distance to expand along the azimuth direction. Plain numbers are interpreted as meters; Quantities must have length units. Must be positive.

  • across_track_distance (Union[float, ureg.Quantity]) – Distance to expand perpendicular to the azimuth. Plain numbers are interpreted as meters; Quantities must have length units. Must be positive.

  • azimuth (float) – Translation direction in degrees, measured clockwise from north. Will be wrapped to [-180, 180].

Returns:

The union of the translated polygons in WGS84 coordinates.

Return type:

Polygon

Raises:

ValueError – If the input polygon is invalid or if a distance is not a valid positive length.

Notes

  • The input polygon is transformed to UTM for accurate geometry calculations.

process_linestring(linestring)[source]

Process a LineString containing WGS84 coordinates to compute latitudes, longitudes, azimuths, and cumulative along-track distances.

Parameters:

linestring (LineString) – A shapely LineString containing WGS84 coordinates.

Returns:

A tuple[Any, …] containing:
  • numpy.ndarray: Latitudes of the track points.

  • numpy.ndarray: Longitudes of the track points.

  • numpy.ndarray: Azimuths between consecutive points.

  • numpy.ndarray: Cumulative along-track distances in meters.

Return type:

tuple

magnetic_declination(lat, lon, alt_m=0, date=None)[source]

Return magnetic declination in degrees (positive = east).

Uses the optional geomag library (WMM model). geomag is not on conda-forge, so it is an optional dependency — install it via pip install hyplan[mag] (or pip install geomag) when magnetic headings are required.

Parameters:
  • lat (float) – Latitude in decimal degrees.

  • lon (float) – Longitude in decimal degrees.

  • alt_m (float) – Altitude in meters above WGS-84 ellipsoid (default 0).

  • date (date | None) – Date for the calculation (default today).

Return type:

float

Returns:

Declination in degrees, positive east. Note that magnetic = true declination; prefer true_to_magnetic() over manual arithmetic.

true_to_magnetic(heading, declination)[source]

Convert true heading to magnetic heading.

magnetic = (360 + true - declination) % 360

Parameters:
  • heading (float) – True heading in degrees.

  • declination (float) – Magnetic declination in degrees (positive = east).

Return type:

float

Returns:

Magnetic heading in degrees [0, 360).

dd_to_ddm(lat, lon)[source]

Decimal degrees → 'DD MM.MM' (e.g. '37 24.21', '-122 03.45').

This is the MovingLines 'DD MM' / pilot_format style.

Return type:

tuple[str, str]

Parameters:
dd_to_ddms(lat, lon)[source]

Decimal degrees → 'DD MM SS.S' (e.g. '37 24 12.5').

Return type:

tuple[str, str]

Parameters:
dd_to_nddmm(lat, lon)[source]

Decimal degrees → 'N37 24.21' / 'W122 03.45' (Honeywell FMS style).

Return type:

tuple[str, str]

Parameters:
dd_to_foreflight_oneline(lat, lon)[source]

Decimal degrees → 'N3724.210/W12203.450' (ForeFlight one-liner).

Return type:

str

Parameters: