Coordinates

class geodetic_engine.geodesy.Coordinates[source]

Bases: tuple[tuple[float, …], …]

Transformed coordinate values, one tuple per point.

A plain tuple of tuples in every way that matters for existing code: indexing (coordinates[0]), iteration, len(), and equality against a plain tuple all behave exactly as they would for the tuple it wraps. The only addition is knowing the target CRS well enough to export itself correctly – in particular, to name a pandas DataFrame’s columns.

Example

>>> from geodetic_engine.geodesy import transform
>>> coordinates = transform(
...     "EPSG:4230", "EPSG:4326", (2.5, 63.5), operation="EPSG:1612"
... ).coordinates
>>> coordinates[0]
(2.49818..., 63.49961...)
>>> coordinates == (coordinates[0],)
True
static __new__(cls, rows, target_crs)[source]
Return type:

Coordinates

to_list()[source]

Coordinates as plain nested Python lists, one list per point.

No dependency beyond the standard library; prefer this over the tuples themselves only when a caller specifically needs lists, for example to hand to a JSON encoder that does not accept tuples.

Return type:

list[list[float]]

Returns:

One list of values per point, in the same order, units and axis count as the wrapped tuples.

Example

>>> from geodetic_engine.geodesy import transform
>>> transform(
...     "EPSG:4230", "EPSG:4326", (2.5, 63.5), operation="EPSG:1612"
... ).coordinates.to_list()
[[2.49818..., 63.49961...]]
to_numpy()[source]

Coordinates as a 2D NumPy array, one row per point.

Return type:

ndarray

Returns:

A float64 array of shape (n_points, n_axes).

Example

>>> from geodetic_engine.geodesy import transform
>>> transform(
...     "EPSG:4230", "EPSG:4326", (2.5, 63.5), operation="EPSG:1612"
... ).coordinates.to_numpy()
array([[ 2.49818..., 63.49961...]])
to_dataframe()[source]

Coordinates as a pandas DataFrame, one row per point.

Columns are named after the target CRS’s axes in coordinate value order, not in EPSG-declared order, so that each column label names the axis whose value the column actually holds: ["Lon", "Lat"] for EPSG:4326, whose declared order is ("Lat", "Lon"). Where the abbreviations do not tell the axes apart – EPSG:3388 abbreviates both of its axes none – the axis names are used instead, so that no two columns share a label. A row carrying one value more than the target CRS declares – a height passed through unchanged alongside a 2D horizontal target – gets one extra column, named "h" for a geographic target or "Z" for a Cartesian one (projected, geocentric, engineering), or "Z (carried)" / "h (carried)" where an axis of the target already goes by that label, numbered further ("Z (carried 2)") if that is taken too.

Return type:

DataFrame

Returns:

A DataFrame with one row per point and one column per value.

Example

>>> from geodetic_engine.geodesy import transform
>>> transform(
...     "EPSG:4230", "EPSG:4326", (2.5, 63.5), operation="EPSG:1612"
... ).coordinates.to_dataframe()
        Lon        Lat
0  2.49818...  63.49961...