Location And Profiles#
The site location tools handle station coordinates, topography assignment,
coordinate projection, distance and bearing calculations, and survey-line
chainage. The profile tools in pycsamt.site.profile build a 1-D line
model from station locations so that a survey can be sorted, sliced, checked
for gaps, and passed cleanly into processing or inversion preparation.
Use this page when you need to:
parse latitude, longitude, or elevation values from field tables;
normalize EDI
HEADcoordinate fields;apply corrected GPS or topography tables to EDI sites;
project lon/lat coordinates into another CRS;
compute station spacing, bearing, or chainage along a survey line;
infer the dominant line orientation from station coordinates;
build a
pycsamt.site.profile.Profilefor line-ordered workflows.
Location Tool Map#
Object or function |
Scope |
Main purpose |
|---|---|---|
One coordinate. |
Store latitude, longitude, and elevation as a small dataclass. |
|
Field values. |
Parse decimal or DMS-like coordinate text into numeric values. |
|
One EDI-like object. |
Ensure the EDI |
|
One or many sites. |
Update site coordinates from a table matched by station identifier. |
|
Coordinate arrays. |
Transform points between coordinate reference systems. |
|
Two points. |
Compute geodetic, flat, or projected distance in meters. |
|
Two points. |
Compute azimuth from one point to another. |
|
One profile axis. |
Project station positions onto a survey-line axis. |
Profile Tool Map#
Object or function |
Main purpose |
|---|---|
Store profile origin, azimuth, per-station chainage, spacing statistics, and detected large gaps. |
|
Build a profile from EDI-like sites or |
|
Return sites ordered by increasing chainage. |
|
Return stations whose chainage lies inside a distance window. |
|
Build a regular chainage grid between minimum and maximum station chainage. |
|
Return spacing and gap summary statistics. |
|
Estimate survey-line azimuth using PCA on local coordinate offsets. |
Coordinate Containers#
Use pycsamt.site.location.Coord when a small explicit coordinate
object is clearer than passing loose tuples.
1from pycsamt.site.location import Coord
2
3station = Coord(lat=10.25, lon=20.75, elev=640.0)
4
5print(station.lat)
6print(station.lon)
7print(station.elev)
Coord does not validate values at construction time. Validation happens in
helpers such as ensure_head_coords(), distance(),
bearing(), and chainage_along().
Parsing Coordinates#
Field coordinate tables are not always consistent. The parser accepts numeric values and common degree/minute/second style strings.
1from pycsamt.site.location import parse_elev, parse_lat, parse_lon
2
3lat1 = parse_lat("45N")
4lat2 = parse_lat("45 30 0 S")
5lon1 = parse_lon("123W")
6lon2 = parse_lon("123 15 30 E")
7elev = parse_elev("1200")
8
9print(lat1, lat2)
10print(lon1, lon2)
11print(elev)
Parsing conventions:
north and east are positive;
south and west are negative;
signed decimal values are accepted;
unparseable values return
NaNinstead of raising;elevation is interpreted in meters.
Accepted examples include "10", "-3.2", "12 30 00 N",
"12:30:00W", "3.5W", and numeric floats.
Normalizing EDI Head Coordinates#
ensure_head_coords() creates or updates coordinate fields on an EDI-like
object. It guarantees that the HEAD section carries numeric lat,
lon, long, and elev values.
1from pycsamt.seg.edi import EDIFile
2from pycsamt.site.location import ensure_head_coords
3
4edi = EDIFile("data/edi/S01.edi")
5
6head = ensure_head_coords(
7 edi,
8 lat="35 07 30 N",
9 lon="12 45 00 E",
10 elev="1234",
11)
12
13print(head.lat, head.lon, head.long, head.elev)
If a coordinate is missing or invalid, the function uses empty as the
fallback sentinel. By default empty is 0.0.
1head = ensure_head_coords(edi, empty=-9999.0)
Use this helper early when downstream code expects numeric coordinates.
Applying Topography Tables#
apply_topography() updates one site, a list of sites, or a container with
._items from a table matched by station identifier.
1import pandas as pd
2
3from pycsamt.seg.collection import EDICollection
4from pycsamt.site.location import apply_topography
5
6collection = EDICollection.from_sources("data/edi")
7
8topo = pd.DataFrame(
9 {
10 "station": ["S01", "S02", "S03"],
11 "latitude": [35.125, 35.200, 35.275],
12 "longitude": [12.750, 12.900, 13.050],
13 "elevation": [1234.0, 1180.0, 1215.0],
14 }
15)
16
17updated = apply_topography(collection, topo, inplace=False)
Station identifiers are matched case-insensitively against common columns:
station, site, dataid, id, and name. Coordinate columns are
searched among:
Coordinate |
Accepted columns |
|---|---|
Latitude |
|
Longitude |
|
Elevation |
|
Use inplace=False when you want a copied structure and inplace=True
when the input objects should be updated directly.
Coordinate Projection#
project() transforms coordinate pairs between coordinate reference
systems. When pyproj is available, pyCSAMT uses it with
always_xy=True. GDAL is used as a fallback when available.
1from pycsamt.site.location import project
2
3x, y = project(
4 [(12.5, 35.25), (12.6, 35.30)],
5 crs_from="EPSG:4326",
6 crs_to="EPSG:32633",
7)
8
9print(x)
10print(y)
Important convention: projected coordinate pairs are interpreted as (x, y).
For geographic CRS values, that means (lon, lat) when using
EPSG:4326 with this function.
If neither pyproj nor GDAL is available, project() raises
RuntimeError.
Distance And Bearing#
distance() computes the distance between two coordinates in meters.
Three modes are available:
mode="geodetic"Haversine approximation on a spherical Earth. This is the default.
mode="flat"Local small-extent planar approximation using latitude-scaled longitude distances.
mode="utm"Project both points to UTM, or to
crs_towhen supplied, and compute Euclidean distance.
1from pycsamt.site.location import Coord, bearing, distance
2
3a = Coord(0.0, 0.0, 0.0)
4b = Coord(0.0, 1.0, 0.0)
5
6d = distance(a, b, mode="geodetic")
7az = bearing(a, b)
8
9print(d) # about 111 km at the equator
10print(az) # about 90 degrees, east
bearing() uses the same mode names. It returns azimuth in degrees with
0 degrees pointing north and 90 degrees pointing east.
The geodetic distance is based on:
The geodetic bearing uses the spherical forward azimuth:
Chainage Along A Line#
chainage_along() projects station coordinates onto a survey-line axis.
The axis is defined by an origin and azimuth. Azimuth follows the geophysical
line convention used in pyCSAMT: 0 degrees is north and 90 degrees is east.
1from pycsamt.site.location import chainage_along
2
3origin = (0.0, 0.0)
4station = (0.0, 1.0 / 111.0) # about 1 km east near the equator
5
6s = chainage_along(origin, azimuth=90.0, pts=station)
7print(s)
For local offsets \(dx\) east and \(dy\) north, chainage is:
where \(A\) is the profile azimuth in radians.
The function returns a scalar for one point and a NumPy array for a sequence of points.
1points = [
2 (0.0, 0.0),
3 (0.0, 1.0 / 111.0),
4 (0.0, 2.0 / 111.0),
5]
6
7chainages = chainage_along(origin, 90.0, points)
8print(chainages)
Inferring Line Orientation#
infer_line_orientation() estimates the dominant survey-line axis from a
set of site coordinates. It builds local Cartesian offsets and applies PCA.
The principal component with the largest variance defines the line direction.
1from pycsamt.seg.collection import EDICollection
2from pycsamt.site.profile import infer_line_orientation
3
4collection = EDICollection.from_sources("data/edi")
5
6azimuth = infer_line_orientation(collection)
7print(azimuth)
The returned azimuth is in degrees, with 0 degrees north and 90 degrees east. Because a line axis has no intrinsic direction, the result should be interpreted modulo 180 degrees. For example, 45 degrees and 225 degrees describe the same line axis.
Building Profiles#
Profile stores a survey-line representation:
originas apycsamt.site.location.Coord;azimuthin degrees;chainagesas{station_name: chainage_m};spacing_statsfor line spacing;gapsfor large station-spacing intervals.
Build one from sites:
1from pycsamt.seg.collection import EDICollection
2from pycsamt.site.profile import Profile
3
4collection = EDICollection.from_sources("data/edi")
5
6profile = Profile.from_sites(collection)
7
8print(profile.azimuth)
9print(profile.chainages)
10print(profile.spacing_stats)
11print(profile.gaps)
If no origin is supplied, the first finite site coordinate is used. If no
azimuth is supplied, infer_line_orientation() is used.
You can also force both:
1from pycsamt.site.location import Coord
2
3profile = Profile.from_sites(
4 collection,
5 origin=Coord(35.0, 12.0, 0.0),
6 azimuth=90.0,
7)
Sorting And Slicing Profiles#
Use Profile.sort_sites() to return sites ordered by increasing chainage.
Sites without finite coordinates are dropped.
1ordered = profile.sort_sites(collection)
2print([site.name for site in ordered if hasattr(site, "name")])
Use Profile.slice() to select a chainage window:
1window = profile.slice(500.0, 2500.0)
2print(window)
The returned mapping is ordered by chainage.
Resampling And Summary#
Profile.resample() builds a regular chainage grid between the minimum and
maximum station chainage.
1grid = profile.resample(step=250.0)
2print(grid)
Profile.summary() returns spacing and gap information:
1summary = profile.summary()
2
3print(summary["n_sites"])
4print(summary.get("spacing_mean"))
5print(summary.get("spacing_med"))
6print(summary["n_gaps"])
Spacing statistics include:
Key |
Meaning |
|---|---|
|
Mean station spacing in meters. |
|
Median station spacing in meters. |
|
Minimum station spacing in meters. |
|
Maximum station spacing in meters. |
|
Minimum and maximum finite chainage. |
|
Number of stations with finite chainage. |
|
Number of large spacing gaps. |
Gap Detection#
After chainages are sorted, Profile computes station spacings with
numpy.diff. A large gap is recorded when spacing exceeds
1.5 * median_spacing. Gaps are stored as (s_left, s_right) chainage
intervals.
1if profile.gaps:
2 for left, right in profile.gaps:
3 print(f"Gap from {left:.1f} m to {right:.1f} m")
This is a simple QC rule, not a geological interpretation. Use it to find survey-line acquisition holes, missing stations, or irregular spacing before building inversions or pseudo-sections.
End-To-End Example#
The following example applies a topography table, builds a profile, sorts the survey, and checks spacing.
1import pandas as pd
2
3from pycsamt.seg.collection import EDICollection
4from pycsamt.site.location import apply_topography
5from pycsamt.site.profile import Profile
6
7collection = EDICollection.from_sources("data/edi")
8
9topo = pd.read_csv("data/station_coordinates.csv")
10collection = apply_topography(collection, topo, inplace=False)
11
12profile = Profile.from_sites(collection)
13ordered = profile.sort_sites(collection)
14summary = profile.summary()
15
16print(profile.azimuth)
17print(summary)
18print([site.name for site in ordered if hasattr(site, "name")])
Common Mistakes#
- Swapping latitude and longitude in projection
project()usesalways_xy=Truewhen pyproj is available. ForEPSG:4326, pass(lon, lat)pairs, not(lat, lon)pairs.- Treating local flat chainage as high-precision geodesy
chainage_along()uses a local flat approximation. It is suitable for survey-line ordering and spacing checks, not cadastral-grade positioning.- Forgetting the 180-degree ambiguity of line orientation
A line azimuth of 45 degrees and 225 degrees describes the same physical axis. Choose the direction that matches acquisition convention when reporting chainage.
- Using uncorrected EDI header coordinates
Raw EDI coordinates can be missing, rounded, or copied from acquisition templates. Apply the authoritative station table before building profiles.
- Ignoring missing coordinates
Profile methods drop stations without finite coordinates. Check
profile.summary()and compare the number of profiled stations with the number of loaded sites.
Next Pages#
Continue with:
Site Containers for the site and collection objects that carry locations;
Site Editing for assigning coordinates from tables and projected values;
Site Selection for selecting stations by chainage, bounding box, or frequency coverage;
Export And Reporting for writing cleaned sites and reporting profile-ready station sets.