pycsamt.site.profile#

Functions

infer_line_orientation(sites)

Infer the survey line azimuth from a collection of sites.

Classes

Profile(origin, azimuth[, chainages, ...])

Profile(origin, azimuth, chainages=None, spacing_stats=None,

pycsamt.site.profile.infer_line_orientation(sites)[source]#

Infer the survey line azimuth from a collection of sites.

This estimates the dominant line axis that best explains the site distribution. The estimate uses PCA on local Cartesian offsets derived from geographic coordinates. The result is an azimuth in degrees where 0 deg is north and 90 deg is east.

Parameters:

sites (iterable of objects) – Items may be EDI-like objects or wrappers exposing an .edi attribute. Site names and coordinates are resolved using pycsamt.site.utils.station_name() and pycsamt.site.utils.get_coords().

Returns:

Azimuth in degrees, 0 deg north and 90 deg east. Because eigenvectors are sign-ambiguous, the orientation is defined modulo 180 deg (e.g., 45 deg and 225 deg are the same axis).

Return type:

float

Notes

Local offsets \((x, y)\) are built with a small-extent flat-Earth approximation about the mean latitude \(lat_0\) and longitude \(lon_0\):

\[ \begin{align}\begin{aligned}x = (lon - lon_0) * M\_PER\_DEG * cos(lat\_0)\\y = (lat - lat_0) * M\_PER\_DEG\end{aligned}\end{align} \]

The principal component with the largest variance gives the line axis in the local frame \((x=east, y=north)\). It is then converted to an azimuth (0 deg north, 90 deg east).

Examples

>>> from pycsamt.site.profile import infer_line_orientation
>>> class Head:
...     def __init__(self, lat, lon, name):
...         self.lat, self.lon, self.dataid = lat, lon, name
...
>>> class EDI:
...     def __init__(self, name, lat, lon):
...         self._h = Head(lat, lon, name)
...     def get_section(self, key):
...         return self._h if key == "head" else None
...
>>> east = [EDI(f"S{i}", 0.0, i*0.01) for i in range(5)]
>>> az = infer_line_orientation(east)
>>> 80.0 <= az <= 100.0
True

References

class pycsamt.site.profile.Profile(origin, azimuth, chainages=<factory>, spacing_stats=<factory>, gaps=<factory>)[source]#

Bases: object

Profile(origin, azimuth, chainages=None, spacing_stats=None,

gaps=None)

Describe a 1-D survey line (profile) built from site locations. Stores the origin, line azimuth, per-site chainages, spacing statistics, and detected large gaps.

The azimuth follows the convention 0 deg north, 90 deg east. Chainages are in meters and are computed consistently with pycsamt.site.location.chainage_along().

Parameters:
  • origin (pycsamt.site.location.Coord) – Origin coordinate used for chainage computations.

  • azimuth (float) – Line azimuth in degrees, 0 deg north, 90 deg east.

  • chainages (dict, optional) – Mapping {station_name: chainage_m}. Usually filled via Profile.from_sites().

  • spacing_stats (dict, optional) – Precomputed spacing metrics. Filled automatically when chainages are set or updated.

  • gaps (list of tuple, optional) – Large spacing gaps as [(s_left, s_right), ...] in meters.

Variables:
  • origin (Coord) – Profile origin coordinate.

  • azimuth (float) – Profile azimuth (deg).

  • chainages (dict) – Per-site chainages in meters.

  • spacing_stats (dict) – Keys include spacing_mean, spacing_med, spacing_min, spacing_max (meters).

  • gaps (list of tuple) – Detected large gaps as chainage intervals (meters).

Notes

Chainage for a site with local offsets \((x,y)\) relative to the origin and profile azimuth \(A\) is

\[s = x * cos(A) + y * sin(A)\]

Examples

>>> from pycsamt.site.profile import Profile
>>> from pycsamt.site.location import Coord
>>> class Head:
...     def __init__(self, lat, lon, name):
...         self.lat, self.lon, self.dataid = lat, lon, name
...
>>> class EDI:
...     def __init__(self, name, lat, lon):
...         self._h = Head(lat, lon, name)
...     def get_section(self, key):
...         return self._h if key == "head" else None
...
>>> sites = [EDI("A", 0.0, 0.00),
...          EDI("B", 0.0, 0.01),
...          EDI("C", 0.0, 0.02)]
>>> prof = Profile.from_sites(sites)
>>> round(prof.azimuth) in (89, 90, 91)
True
origin: Coord#
azimuth: float#
chainages: dict[str, float]#
spacing_stats: dict[str, float]#
gaps: list[tuple[float, float]]#
classmethod from_sites(sites, *, origin=None, azimuth=None)[source]#

Build a profile from an iterable of sites. If origin is omitted, the first site with finite coordinates is used. If azimuth is omitted, it is inferred with infer_line_orientation().

Parameters:
  • sites (iterable) – EDI-like objects or wrappers with an .edi attribute.

  • origin (Coord, optional) – Origin coordinate. If omitted, inferred from the first finite site.

  • azimuth (float, optional) – Profile azimuth in degrees. If omitted, inferred from site positions.

Returns:

Profile with per-site chainages and spacing statistics computed.

Return type:

Profile

Notes

Coordinates and names are obtained via pycsamt.site.utils.get_coords() and pycsamt.site.utils.station_name(). Chainages are computed with pycsamt.site.location.chainage_along().

Examples

>>> from pycsamt.site.profile import Profile
>>> prof = Profile.from_sites([])
>>> isinstance(prof, Profile)
True
sort_sites(sites)[source]#

Return the input sites ordered by chainage along the profile. Sites without finite chainage are dropped.

Parameters:

sites (iterable) – Same accepted types as Profile.from_sites().

Returns:

The subset of input sites sorted by increasing chainage.

Return type:

list

Examples

>>> sorted_sites = prof.sort_sites(sites)
>>> isinstance(sorted_sites, list)
True
slice(s_min, s_max)[source]#

Return chainages within the window s_min <= s <= s_max as a dict ordered by chainage.

Parameters:
  • s_min (float) – Lower bound in meters.

  • s_max (float) – Upper bound in meters.

Returns:

Mapping {station_name: chainage_m}, ordered by chainage.

Return type:

dict

Examples

>>> win = prof.slice(500.0, 1500.0)
>>> isinstance(win, dict)
True
resample(step)[source]#

Build a regular chainage grid between the current minimum and maximum chainage (inclusive of the minimum). If step <= 0, an empty array is returned.

Parameters:

step (float) – Grid spacing in meters.

Returns:

1-D array of chainage locations in meters.

Return type:

numpy.ndarray

Examples

>>> grid = prof.resample(250.0)
>>> (grid.ndim, grid.dtype.kind) == (1, 'f')
True
summary()[source]#

Return a compact summary of the profile and its spacing statistics.

Returns:

Keys include n_sites, s_min, s_max, n_gaps, and entries from spacing_stats (spacing_mean, spacing_med, spacing_min, spacing_max).

Return type:

dict

Examples

>>> info = prof.summary()
>>> set(["n_sites", "n_gaps"]).issubset(info.keys())
True