lair.utils.geo#

Geo-spatial utilities.

Functions

add_extent_map(fig, main_extent, ...[, zorder])

Add an extent map to the figure.

add_lat_ticks(ax, ylims[, labelsize, more_ticks])

Add latitude ticks to the map.

add_latlon_ticks(ax, extent[, x_rotation, ...])

Add latitude and longitude ticks to the map.

add_lon_ticks(ax, xlims[, rotation, ...])

Add longitude ticks to the map.

bbox2extent(bbox)

Bounding box to extent.

bearing(lat1, lon1, lat2, lon2[, deg, final])

clip(data[, bbox, extent, geom, crs])

Clip the data to the given bounds.

cosine_weights(lats)

Calculate cosine weights from latitude.

dms2dd([d, m, s])

Degree-minute-second to decimal degree

earth_radius(lat)

Calculate radius of Earth assuming oblate spheroid defined by WGS84

extent2bbox(extent)

Extent to bounding box.

gridcell_area(grid[, R])

Calculate the area of each grid cell in a grid.

gridcell_area_from_latlon(lat, lon[, R])

Calculate the area of each grid cell in a lat-lon grid.

haversine(lat1, lon1, lat2, lon2[, R, deg])

wrap_lons(lons)

Wrap longitudes ranging from 0~360 to -180~180

write_rio_crs(data, crs)

Classes

CRS(crs)

Coordinate Reference System (CRS) class.

class lair.utils.geo.CRS(crs: Any)[source]#

Coordinate Reference System (CRS) class.

This class is a wrapper around the pyproj.CRS class, with additional methods for converting to other CRS classes.

See https://pyproj4.github.io/pyproj/stable/crs_compatibility.html#cartopy for more information.

Note

osgeo, fiona, and pycrs conversions have not been implemented.

Attributes

epsg

Get the EPSG code of the CRS.

proj4

Get the PROJ4 string of the CRS.

wkt

Get the WKT string of the CRS.

crs

(pyproj.CRS) Pyproj CRS object

__init__(crs: Any)[source]#
property epsg: int | None#

Get the EPSG code of the CRS.

property proj4: str#

Get the PROJ4 string of the CRS.

property units: str#

Get the units of the CRS.

property wkt: str#

Get the WKT string of the CRS.

to_cartopy() CRS[source]#

Convert to cartopy CRS.

to_rasterio() CRS[source]#

Convert to rasterio CRS.

to_pyproj() CRS[source]#

Convert to pyproj CRS.

lair.utils.geo.dms2dd(d: float = 0.0, m: float = 0.0, s: float = 0.0) float[source]#

Degree-minute-second to decimal degree

Parameters:
dfloat, optional

Degrees, by default 0.0

mfloat, optional

Minutes, by default 0.0

sfloat, optional

Seconds, by default 0.0

Returns:
float

Decimal degrees

Raises:
ValueError

If any of the inputs are not floats

lair.utils.geo.bbox2extent(bbox: list[float]) list[float][source]#

Bounding box to extent.

Parameters:
bboxlist[minx, miny, maxx, maxy]

Bounding box

Returns:
list[minx, maxx, miny, maxy]

Extent

lair.utils.geo.extent2bbox(extent: list[float] | tuple[float, float, float, float]) list[float][source]#

Extent to bounding box.

Parameters:
extentlist[minx, maxx, miny, maxy]

Extent

Returns:
list[minx, miny, maxx, maxy]

Bounding box

lair.utils.geo.clip(data: DataArray | Dataset, bbox: list[float] | tuple[float, float, float, float] | None = None, extent: list[float] | tuple[float, float, float, float] | None = None, geom: Polygon | list[Polygon] | None = None, crs: Any = 'EPSG:4326', **kwargs: Any) DataArray | Dataset[source]#

Clip the data to the given bounds.

Input bounds must be in the same CRS as the data.

Note

The result can be slightly different between supplying a geom and a bbox/extent. Clipping with a geom seems to be exclusive of the outer bounds, while clipping with a bbox/extent seems to be inclusive of the outer bounds.

Parameters:
dataxr.DataArray | xr.Dataset

The data to clip.

bboxtuple[minx, miny, maxx, maxy]

The bounding box to clip the data to.

extenttuple[minx, maxx, miny, maxy]

The extent to clip the data to.

geomshapely.Polygon

The geometry or geometries to clip the data to.

crsAny, optional

The CRS of the input geometries. Default is ‘EPSG:4326’.

kwargsAny

Additional keyword arguments to pass to the rioxarray clip method.

Returns:
xr.DataArray | xr.Dataset

The clipped data.

lair.utils.geo.wrap_lons(lons: ndarray) Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes][source]#

Wrap longitudes ranging from 0~360 to -180~180

Parameters:
lonsnp.ndarray

Longitudes

Returns:
array-like

Wrapped longitudes

lair.utils.geo.add_lat_ticks(ax: Axes, ylims: list[float], labelsize: int | None = None, more_ticks: int = 0) None[source]#

Add latitude ticks to the map.

Parameters:
axplt.Axes

Axes object

ylimslist[float]

Latitude limits

labelsizeint, optional

Font size of the labels, by default None

more_ticksint, optional

Number of additional ticks, by default 0

Returns:
None
lair.utils.geo.add_lon_ticks(ax: Axes, xlims: list[float], rotation: int = 0, labelsize: int | None = None, more_ticks: int = 0) None[source]#

Add longitude ticks to the map.

Parameters:
axplt.Axes

Axes object

xlimslist[float]

Longitude limits

rotationint, optional

Rotation of the labels, by default 0

labelsizeint, optional

Font size of the labels, by default None

more_ticksint, optional

Number of additional ticks, by default 0

Returns:
None
lair.utils.geo.add_latlon_ticks(ax: Axes, extent: list[float], x_rotation: int = 0, labelsize: int | None = None, more_lon_ticks: int = 0, more_lat_ticks: int = 0) None[source]#

Add latitude and longitude ticks to the map.

Parameters:
axplt.Axes

Axes object

extentlist[float]

Extent of the map. [minx, maxx, miny, maxy]

x_rotationint, optional

Rotation of the longitude labels, by default 0

labelsizeint, optional

Font size of the labels, by default None

more_lon_ticksint, optional

Number of additional longitude ticks, by default 0

more_lat_ticksint, optional

Number of additional latitude ticks, by default 0

Returns:
None
lair.utils.geo.cosine_weights(lats: ndarray) ndarray[source]#

Calculate cosine weights from latitude.

Parameters:
latsnp.ndarray

Latitude values

Returns:
np.ndarray

Cosine weighting

Examples

>>> ds: xr.Dataset
>>> weights = cosine_weighting(ds.lat)
>>> ds_weighted = ds.weighted(weights)
lair.utils.geo.gridcell_area(grid: DataArray | Dataset, R: float | Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes] | None = None) DataArray[source]#

Calculate the area of each grid cell in a grid.

Note

For lat-lon grids, xesmf.utils.grid_area is used to calculate the area, which requires the radius of the earth in kilometers. If the radius of the earth is not provided, it will be calculated based on the latitude.

Parameters:
gridxr.DataArray | xr.Dataset

Grid data. rioxarray coords must be set.

Rfloat, optional

Radius of earth in kilometers, by default calculated based on the latitude.

Returns:
np.ndarray | xr.DataArray

grid-cell area in square-kilometers

lair.utils.geo.gridcell_area_from_latlon(lat: Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], lon: Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], R: float | None = None) ndarray[source]#

Calculate the area of each grid cell in a lat-lon grid.

Parameters:
latArrayLike

Latitude array

lonArrayLike

Longitude array

Returns:
np.ndarray

Grid-cell area in square-kilometers

lair.utils.geo.earth_radius(lat: Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes]) Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes][source]#

Calculate radius of Earth assuming oblate spheroid defined by WGS84

Parameters:
latarray-like

latitudes in degrees

Returns:
array-like

vector of radius in kilometers

Notes

lair.utils.geo.add_extent_map(fig: matplotlib.figure.Figure, main_extent: list[float], main_extent_crs: CRS, extent_map_rect: list[float], extent_map_extent: list[float], extent_map_crs: CRS, color: str, linewidth: int, zorder: int | None = None) Axes[source]#

Add an extent map to the figure.

TODO This needs better naming and documentation.

Parameters:
figmatplotlib.figure.Figure

Figure object

main_extentlist[float]

Extent of the main map

main_extent_crsccrs.CRS

CRS of the main extent

extent_map_rectlist[float]

Rectangle of the extent map

extent_map_extentlist[float]

Extent of the extent map

extent_map_crsccrs.CRS

CRS of the extent map

colorstr

Color of the extent map

linewidthint

Line width of the extent map

zorderint, optional

Zorder of the extent map, by default None

Returns:
plt.Axes

Axes object of the extent map