pycsamt.zonge.survey#

Survey layout.

  • Station: robust line-geometry container that understands modern/legacy AVG frames, exposes unique station positions, IDs, and handy header ($Stn.*) derivations for round-trips.

  • Topography: robust container for .stn station location files.

Classes

Station([unit, normalize, allow_ragged, ...])

One-dimensional survey-line geometry container.

Topography([data, meta, utm_zone, epsg, ...])

A container for station topography and location data.

class pycsamt.zonge.survey.Station(unit='m', normalize=False, allow_ragged=True, values=<factory>, names=<factory>, index_by_value=<factory>, index_by_name=<factory>)[source]#

Bases: AVGComponentBase

One-dimensional survey-line geometry container.

This component reads the station coordinate from a tidy AVG frame (usually column 'station'), validates and stores unique positions, and (optionally) normalizes the origin to zero. It also derives a stable set of header keys ($Stn.*) for round-tripping modern headers.

Key features#

  • Accepts either a DataFrame (from AVG parsers) or raw arrays.

  • Handles ragged frequency coverage (no equal-count requirement).

  • Tracks units; internal helpers can work in metres when desired.

  • Derives $Stn.Beg / $Stn.Inc / $Stn.Left / $Stn.Right if grid-like.

  • Exposes dictionary mappings useful for downstream selection.

Notes

We do not modify the caller’s AVG frame; we keep our own view in self._frame that at minimum contains a numeric 'station' column and (if relevant) a 'station_m' column when the input length unit is feet (‘ft’) and conversion to metres is helpful.

unit: str#
normalize: bool#
allow_ragged: bool#
values: ndarray#
names: list[str]#
index_by_value: dict[float, ndarray]#
index_by_name: dict[str, ndarray]#
read(source, meta=None, *, unit=None, names=None, normalize=None, allow_ragged=None)[source]#

Populate the component from source.

Parameters:
  • source (DataFrame | Sequence[float] | ndarray) – DataFrame with a 'station' column or a 1-D array of station values (possibly repeated across frequencies).

  • meta (Mapping[str, Any] | None) – Optional file metadata. If provided and unit is None, we try meta['Unit.Length'] for (‘m’|’ft’).

  • unit (str | None) – Length unit of source values. Defaults to ‘m’ or to meta['Unit.Length'] when available.

  • names (Sequence[str] | None) – Optional labels for unique station positions. If omitted, we auto-generate: ‘S00’, ‘S01’, …

  • normalize (bool | None) – When True, shift the origin so the first station is 0.0.

  • allow_ragged (bool | None) – When False, all stations must have identical row counts (one per frequency). When True (default), ragged coverage is allowed.

Return type:

None

write()[source]#

Emit a small, human-friendly header preamble plus a CSV of the station coordinate. This is mainly useful for diagnostics and tests; full file writing should be orchestrated at a higher level.

Return type:

Sequence[str]

property n_unique: int[source]#

Number of unique station positions.

property span: tuple[float, float] | None[source]#

(min, max) of the station coordinate; None if empty.

property increment: float | None[source]#

Grid increment if evenly spaced, else None.

label_map()[source]#

Map station numeric value → generated label (e.g., ‘S03’).

Return type:

dict[float, str]

to_keywords()[source]#

Derive a stable set of $Stn.* keys from current geometry:

  • Stn.Beg : first station

  • Stn.Inc : increment (when grid-like)

  • Stn.Left : left edge (min)

  • Stn.Right : right edge (max)

For convenience, we also mirror GDP-station versions when increment is grid-like: Stn.GdpBeg / Stn.GdpInc.

Return type:

dict[str, Any]

Parameters:
class pycsamt.zonge.survey.Topography(data=None, meta=None, *, utm_zone=None, epsg=None, name=None, verbose=False)[source]#

Bases: AVGComponentBase

A container for station topography and location data.

This class is designed to read, manage, and process spatial information from Zonge .stn files, as described in the ASTATIC manual [1]. It handles both legacy (space-delimited) and modern (comma-delimited) formats and provides a suite of tools for coordinate conversion, regularization, and gridding.

Parameters:
  • data (pandas.DataFrame, optional) – A pre-loaded DataFrame containing station location data. If provided, it will be standardized upon initialization.

  • meta (mapping, optional) – An optional dictionary for metadata, consistent with the AVGComponentBase API.

  • verbose (bool, default False) – Controls the level of detail in logging output.

  • utm_zone (str)

  • epsg (int)

  • name (str | None)

Variables:
  • stations (numpy.ndarray) – An array of the station numbers or identifiers.

  • northings (eastings,) – Arrays of the station UTM coordinates.

  • elevations (numpy.ndarray) – An array of the station elevations.

read(source)[source]#

Reads and parses a .stn file or a DataFrame.

Parameters:
Return type:

Topography

generate(...)[source]#

A static method to create a synthetic survey line.

Parameters:
Return type:

Topography

correct_coords(...)[source]#

Regularizes station locations to a best-fit straight line.

Parameters:
Return type:

DataFrame | None

convert_coords(...)[source]#

Converts coordinates between UTM and geographic (lat/lon).

Parameters:
Return type:

DataFrame | None

to_grid(...)[source]#

Interpolates the scattered station data onto a regular 2D grid for contouring.

Parameters:
  • resolution (int)

  • method (Literal['linear', 'cubic', 'nearest'])

Return type:

tuple[ndarray, ndarray, ndarray]

get_step()[source]#

Calculates the distance between consecutive stations.

Return type:

Series

Notes

The read method is designed to be robust, automatically detecting the delimiter and header row of .stn files. Upon reading, it standardizes column names to a canonical schema (e.g., ‘easting’, ‘northing’, ‘elevation’), making the data consistent for all subsequent processing steps.

Examples

>>> from pycsamt.zonge.survey import Topography
>>> # Load topography from a .stn file
>>> topo = Topography().read('data/avg/K1.stn')
>>>
>>> # Generate a synthetic survey line
>>> synthetic_topo = Topography.generate(
...     start_coord=(500000, 4000000),
...     n_stations=20,
...     step=50,
...     azimuth=45
... )
>>> # Get the average station spacing
>>> avg_step = synthetic_topo.get_step().mean()

References

read(source, meta=None, **kws)[source]#

Read topography data from a file path or DataFrame.

This is the primary data ingestion method for the Topography class. It is designed to be robust, handling various .stn file formats and standardizing the data into a consistent internal structure.

Parameters:
  • source (str, pathlib.Path, or pandas.DataFrame) –

    The data source to load. This can be: - A string or pathlib.Path pointing to a Zonge .stn

    file.

    • A pandas.DataFrame containing station location data.

  • meta (mapping, optional) – An optional dictionary for metadata, consistent with the AVGComponentBase API. This is typically not used for .stn files.

  • kws (Any)

Returns:

self – The method returns the instance of the class, allowing for convenient method chaining.

Return type:

Topography

Raises:
  • TypeError – If the source is of an unsupported type.

  • StationError – If the .stn file is malformed or a valid header row cannot be found.

Notes

The file parser is designed to be flexible: - It automatically detects whether the file is comma-delimited

or space-delimited.

  • It intelligently searches for a header row by looking for common keywords (e.g., ‘station’, ‘easting’), rather than assuming a fixed position.

  • All column names are normalized to a canonical schema (e.g., ‘easting’, ‘northing’, ‘elevation’) after reading.

Examples

>>> from pycsamt.zonge.survey import Topography
>>> # Load topography from a .stn file
>>> topo = Topography().read('data/avg/K1.stn')
>>> print(topo.stations[:5])
[150. 200. 250. 300. 350.]
convert_coords(to='auto', *, inplace=True)[source]#

Convert between UTM and geographic (lat/lon) coordinates.

This method provides a high-level interface for coordinate system conversion, leveraging the underlying utilities in the utils module.

Parameters:
  • to ({'utm', 'll', 'auto'}, default 'auto') –

    The target coordinate system.

    • ’utm’: Convert to Universal Transverse Mercator.

    • ’ll’: Convert to latitude/longitude decimal degrees.

    • ’auto’: Automatically detect the current system and convert to the other.

  • update_inplace (bool, default True) – If True, the internal DataFrame of the Topography object is updated with the new coordinate columns. If False, a new DataFrame containing both the original and converted coordinates is returned.

  • inplace (bool)

Returns:

  • If update_inplace is True, returns None and modifies the object’s internal frame.

  • If update_inplace is False, returns a new DataFrame with the added coordinate columns.

Return type:

pandas.DataFrame or None

Notes

The function first uses assert_xy_coordinate_system() to determine the current coordinate system of the data. It then calls either to_utm() or to_ll() to perform the conversion.

See also

pycsamt.gis.utils.to_utm

The underlying UTM conversion utility.

pycsamt.gis.utils.to_ll

The underlying lat/lon conversion utility.

to_grid(resolution=100, method='cubic')[source]#

Interpolate scattered station data onto a regular 2D grid.

This method takes the scattered station locations (easting, northing) and their corresponding elevations and interpolates them onto a regular grid, which is essential for creating contour maps or surface plots.

Parameters:
  • resolution (int, default 100) – The number of points to create in each dimension (x and y) of the output grid. A higher number results in a finer grid.

  • method ({'linear', 'cubic', 'nearest'}, default 'cubic') –

    The interpolation method to be used by the underlying scipy.interpolate.griddata() function. - ‘linear’: Performs linear interpolation. - ‘cubic’: Performs cubic interpolation for a smoother

    surface.

    • ’nearest’: Uses the value of the nearest data point.

Returns:

  • grid_x (numpy.ndarray) – A 2D array of the X (easting) coordinates of the grid.

  • grid_y (numpy.ndarray) – A 2D array of the Y (northing) coordinates of the grid.

  • grid_z (numpy.ndarray) – A 2D array of the interpolated Z (elevation) values on the grid.

Raises:

ProcessingError – If the topography data has not been loaded first.

Return type:

tuple[ndarray, ndarray, ndarray]

See also

scipy.interpolate.griddata

The core interpolation function used by this method.

pycsamt.zonge.plot.Plot.plot_location_map

A method for visualizing the gridded data.

get_step()[source]#

Calculate the distance between consecutive stations.

This method computes the Euclidean distance between each station and the next one along the survey line, based on their easting and northing coordinates.

Returns:

A Series containing the calculated step distances in meters. The first value is always 0. The length of the Series matches the number of stations.

Return type:

pandas.Series

Notes

The calculation assumes a Cartesian coordinate system (like UTM) where the Pythagorean theorem can be applied to find the distance between points. The result is useful for assessing the regularity of station spacing and for providing a default step size for other methods like correct_coords().

Examples

>>> from pycsamt.zonge.survey import Topography
>>> topo = Topography().read('data/avg/K1.stn')
>>> steps = topo.get_step()
>>> print(f"Average station spacing: {steps.mean():.2f} m")
correct_coords(step=None, *, inplace=True)[source]#

Regularize station coordinates to a best-fit straight line.

This processing tool corrects for minor deviations in survey line geometry by projecting all station locations onto a best-fit straight line and re-spacing them at a uniform interval. This is a common step for preparing data for 2D inversion or gridding.

Parameters:
  • step (float, optional) – The desired uniform distance between stations along the corrected line. If None, the average step distance is calculated automatically using the get_step() method.

  • update_inplace (bool, default True) – If True, the internal DataFrame of the Topography object is updated in place with the new, corrected coordinates. If False, a new DataFrame with the corrected coordinates is returned.

  • inplace (bool)

Returns:

  • If update_inplace is True, returns None.

  • If update_inplace is False, returns a new DataFrame with the corrected ‘easting’ and ‘northing’ columns.

Return type:

pandas.DataFrame or None

Notes

The correction process involves two main steps: 1. A first-degree polynomial (a straight line) is fitted

to the original easting and northing coordinates using a least-squares regression.

  1. New station locations are generated along this ideal line at a constant spacing defined by the step parameter.

See also

get_step

The method used to automatically determine the average station spacing.

static generate(start_coord, n_stations, step, azimuth, *, initial_station_name=0.0, initial_elevation=0.0, elevation_gradient=0.0, coord_type='utm')[source]#

Generate a synthetic station topography dataset.

This static method acts as a factory for creating a new Topography object based on survey design parameters. It is useful for creating test data, planning surveys, or generating a regularized coordinate set for modeling.

Parameters:
  • start_coord (tuple[float, float]) –

    The starting coordinate of the survey line. The format depends on coord_type:

    • For ‘utm’: (easting, northing) in meters.

    • For ‘ll’: (latitude, longitude) in decimal degrees.

  • n_stations (int) – The total number of stations to generate along the line.

  • step (float) – The distance between consecutive stations, in meters.

  • azimuth (float) – The azimuth of the survey line in degrees clockwise from North (e.g., 0 for North, 90 for East).

  • initial_station_name (float, default 0.0) – The name or number of the first station. Subsequent station names are incremented by step.

  • initial_elevation (float, default 0.0) – The elevation of the first station, in meters.

  • elevation_gradient (float, default 0.0) – The change in elevation per meter along the survey line. A positive value creates an upward slope.

  • coord_type ({'utm', 'll'}, default 'utm') – The coordinate system of the start_coord.

Returns:

A new, fully populated instance of the Topography class.

Return type:

Topography

Notes

When coord_type is ‘ll’, the function uses geodetic calculations to accurately generate new points along the great-circle path and then converts the final lat/lon coordinates to UTM for storage.

write()[source]#

Serialize the topography data to .stn format lines.

This method converts the internal DataFrame of topography data into a list of strings that conform to the standard Zonge .stn file format. This is useful for exporting processed or generated topography data.

Returns:

A list of strings, where the first string is the comma- separated header and subsequent strings are the data rows.

Return type:

list[str]

Notes

The output is designed to be a “modern” .stn file, using comma-separated values, which is compatible with the ASTATIC program and other Zonge software.

Examples

>>> from pycsamt.zonge.survey import Topography
>>> topo = Topography.generate(
...     start_coord=(500000, 4000000), n_stations=3,
...     step=100, azimuth=90
... )
>>> stn_lines = topo.write()
>>> for line in stn_lines:
...     print(line)
station,easting,northing,elevation
0.0,500000.0,4000000.0,0.0
100.0,500100.0,4000000.0,0.0
200.0,500200.0,4000000.0,0.0
get_elevation_from(from_='utm', zone=None, datum='WGS84')[source]#

Fetches station elevations from an external API.

This method retrieves elevation data for each station in the survey using an online service. It can operate using either the existing UTM coordinates or geographic (lat/lon) coordinates.

Parameters:
  • from ({'utm', 'api'}, default 'utm') –

    Specifies the coordinate system to use for the API query. - ‘utm’: Uses the easting and northing columns.

    The zone parameter is required.

    • ’api’: Uses latitude and longitude. If these are not present, they are automatically calculated from the UTM coordinates.

  • zone (str, optional) – The UTM zone designator (e.g., “11S”, “32N”). This is required when from_ is ‘utm’.

  • datum (str, default "WGS84") – The geodetic datum to assume for coordinate conversions.

  • from_ (Literal['utm', 'api'])

Returns:

An array of elevation values in meters corresponding to each station.

Return type:

numpy.ndarray

Raises:
  • ProcessingError – If the topography data has not been loaded first.

  • ValueError – If from_ is ‘utm’ but no zone is provided, or if an invalid from_ value is given.

See also

get_elevation_from_utm

Fetches elevation from UTM data.

get_elevation_from_api

Fetches elevation from lat/lon data.

convert_coords

Converts between coordinate systems.

get_azimuth(*, mode=None)[source]#

Returns the survey line azimuth(s).

This method can return the full array of segment azimuths or a single aggregated value (mean or median) representing the overall trend of the survey line.

Parameters:

mode ({'mean', 'median'}, optional) –

Determines the return format. - None (default): Returns the full NumPy array of

azimuths for each station segment.

  • ’mean’: Returns the circular mean of all segment azimuths, providing the average direction.

  • ’median’: Returns the median of all segment azimuths.

Returns:

  • If mode is None, returns the full array of azimuths.

  • If mode is ‘mean’ or ‘median’, returns a single float representing the aggregated value.

Return type:

float or numpy.ndarray

Notes

The ‘mean’ is calculated using circular statistics to correctly average angles (e.g., the mean of 350° and 10° is 0°, not 180°). The ‘median’ is calculated linearly and is a good approximation for relatively straight lines.

property azimuth: ndarray[source]#

Calculates and returns the forward azimuths.

This property computes the azimuth for each line segment (from one station to the next) along the survey line. The result is cached for efficiency.

Returns:

An array of azimuths in degrees (0-360), where the azimuth at index i corresponds to the direction from station i to i+1.

Return type:

numpy.ndarray

property bearing: ndarray[source]#

Alias for the azimuth property.

In this context, bearing is treated as synonymous with azimuth (0-360 degrees clockwise from North).

property stations: ndarray[source]#
property easting: ndarray[source]#
property northing: ndarray[source]#
property elevations: ndarray[source]#
property elevation[source]#
property latitude: ndarray[source]#

Exposes the latitude data as a NumPy array.

Returns:

An array of station latitudes, or an empty array if latitude data is not available in the frame.

Return type:

numpy.ndarray

property longitude: ndarray[source]#

Exposes the longitude data as a NumPy array.

Returns:

An array of station longitudes, or an empty array if longitude data is not available in the frame.

Return type:

numpy.ndarray