Loading Map Data#
The mapping package uses one normalized object,
pycsamt.map.MapData, for every station map, profile
pseudosection, and 3-D view. You rarely need to build
MapData by hand. Most public plotting functions accept the
same inputs directly and call the normalizer for you.
Use this page when you want explicit control over loading, line grouping, or pre-flight checks before drawing a map.
Loading Philosophy#
The loader follows the science API rather than the web app internals.
It delegates EDI parsing to
pycsamt.emtools._core.ensure_sites(), then extracts the small
mapping contract needed by the renderers:
station identifier;
latitude, longitude, and elevation when available;
profile or survey-line name;
the original EDI-like object, including its impedance
Zobject.
This means a code workflow and the web workflow can start from the same survey files, but code users can inspect, group, transform, and export each intermediate object.
Input Types#
All loading helpers accept flexible EDI sources:
strorpathlib.PathA single EDI file or a directory containing EDI files.
- sequence of paths
A list or tuple of EDI files. A sequence of directories is treated as one line per directory by
pycsamt.map.load_lines().- EDI-like iterable
Objects with station metadata and a
Zattribute, including objects already loaded elsewhere in pyCSAMT.Sites-like containerAny object accepted by
ensure_sites. Containers exposingas_list()are unpacked into EDI-like objects.MapDataReturned unchanged, so it is safe to pass normalized data through higher-level builders.
Single-Line Loading#
For one profile line, call pycsamt.map.ensure_map_data()
directly or pass the source to a plotting helper.
from pycsamt.map import ensure_map_data, plot_station_map
source = "data/AMT/WILLY_DATA/L18PLT"
data = ensure_map_data(source, recursive=True)
print(data.station_ids)
print(data.lines)
print(data.has_geo)
fig = plot_station_map(data, frequency=100.0)
ensure_map_data does not force every station to have geographic
coordinates. data.has_geo tells you whether all normalized
stations have finite latitude and longitude. If coordinates are
missing, profile views can still use station order or distance, while
geographic station maps may need CRS or coordinate preprocessing.
Multiple-Line Loading#
Use pycsamt.map.load_lines() when a survey is split across
several folders or when you want one combined object for 3-D fence,
block, or depth-slice views.
from pycsamt.map import load_lines
data = load_lines(
"data/AMT/WILLY_DATA",
detect="folder",
recursive=True,
)
print(data.lines)
print(data.metadata["n_lines"])
print(data.station_ids[:5])
Each station is re-indexed across the combined survey and tagged with
its resolved line name. The renderers then use data.profiles to
draw separate profiles or offset lines in 3-D.
Grouping Modes#
When load_lines receives one directory, the detect option
decides how files become lines.
detect="folder"Group by immediate parent folder. This is the best option for layouts such as
WILLY_DATA/L18PLT/*.ediandWILLY_DATA/L22PLT/*.edi. A flat folder becomes one line named after the folder.detect="flat"Treat every discovered EDI file under the directory as one line. Use this for a single profile stored in a nested directory tree.
detect="auto"Group by station-ID prefix. Numeric prefixes are normalized with an
Lprefix. For example, station files beginning with22are grouped underL22. Use this when line folders are not reliable but station naming is consistent.
You can inspect the grouping without loading impedance data by calling
pycsamt.map.resolve_line_groups().
from pycsamt.map import resolve_line_groups
groups = resolve_line_groups(
"data/AMT/WILLY_DATA",
detect="folder",
)
for line, source in groups.items():
print(line, len(source))
Explicit Line Mapping#
The most reproducible multi-line workflow is an explicit mapping:
from pycsamt.map import load_lines
data = load_lines({
"L18": "data/AMT/WILLY_DATA/L18PLT",
"L22": "data/AMT/WILLY_DATA/L22PLT",
"L26": "data/AMT/WILLY_DATA/L26PLT",
})
Mapping values may be directories, file lists, or EDI-like iterables. This is useful in notebooks and scripts where line names should be stable even if folder names later change.
If your EDI-like objects do not carry line metadata, but you already
know station membership, pass line_map to
pycsamt.map.ensure_map_data().
from pycsamt.map import ensure_map_data
data = ensure_map_data(
edis,
line_map={
"L18": ["S001", "S002", "S003"],
"L22": ["S101", "S102", "S103"],
},
)
Line metadata embedded on the EDI object takes priority. The fallback order is:
line;profile;survey_line;Line;Profile;station lookup in
line_map;default line name
"line".
Normalized Contract#
MapData exposes the surface that all map renderers share:
data.station_ids # tuple[str, ...]
data.lines # tuple[str, ...]
data.has_geo # bool
data.iter_edis() # tuple of original EDI-like objects
data.stations # tuple[StationRecord, ...]
data.profiles # tuple[ProfileLine, ...]
data.metadata # counts and loader metadata
Each pycsamt.map.StationRecord contains:
station.id
station.latitude
station.longitude
station.elevation
station.line
station.index
station.source
station.source is the original EDI-like object. Keep it when you
need to inspect impedance arrays, frequencies, or file-level metadata
after loading.
Pre-Flight Checks#
A good loading workflow checks the normalized survey before plotting:
from pycsamt.map import (
ensure_map_data,
select_frequency,
value_at_frequency_details,
)
data = ensure_map_data("data/AMT/WILLY_DATA/L18PLT")
first_edi = data.iter_edis()[0]
freq = first_edi.Z.freq
selection = select_frequency(freq, requested=100.0)
if selection is None:
raise RuntimeError("No finite positive frequency was found.")
print("requested:", selection.requested)
print("actual:", selection.actual)
print("relative delta:", selection.relative_delta)
values = value_at_frequency_details(
data,
frequency=selection.actual,
quantity="rho",
component="xy",
)
print("stations with values:", len(values))
This is especially helpful when different stations have slightly different frequency grids. Map extraction chooses the nearest finite positive frequency per station and can enforce a tolerance when you need a strict match.
Handling Missing or Partial Data#
The loader is designed for field surveys, where not every file is perfect.
Files without valid impedance are skipped by value-extraction helpers instead of breaking the whole map.
Non-finite coordinates become
NoneonStationRecord.If no line name is available, stations are grouped into
"line".Empty groups are ignored by
load_lines.A
MapDatawith no stations is still a valid object; renderers can return empty figures rather than crashing.
For strict workflows, validate the counts yourself:
data = load_lines("data/AMT/WILLY_DATA", detect="folder")
if not data.stations:
raise RuntimeError("No stations were loaded.")
if not data.has_geo:
raise RuntimeError("Station map requires geographic coordinates.")
if len(data.lines) < 2:
raise RuntimeError("Expected a multi-line survey.")
Using Loaded Data#
Once loaded, pass the same MapData object to station, profile, and
3-D builders. This avoids re-reading files and keeps grouping
consistent across views.
from pycsamt.map import (
load_lines,
plot_station_map,
plot_pseudosection,
plot_volume_map,
)
data = load_lines("data/AMT/WILLY_DATA", detect="folder")
station_fig = plot_station_map(data, frequency=100.0)
section_fig = plot_pseudosection(data, component="xy")
volume_fig = plot_volume_map(data, mode="fence")
Use the export helpers to save any resulting figure:
from pycsamt.map import write_html
write_html(station_fig, "outputs/stations.html")
Troubleshooting#
FileNotFoundErrorThe path passed to
load_linesorensure_map_datadoes not exist. Resolve project-relative paths before loading.ValueError: Unknown detect modedetectmust be one of"folder","flat", or"auto".- No stations in the output
Check that EDI files were discovered and that
ensure_sitescan read them. Start withresolve_line_groupsto confirm grouping.- Station map is empty
Inspect
data.has_geo. Geographic maps require finite latitude and longitude unless you first reproject or provide coordinates.- Pseudosection has gaps
Some stations may be missing impedance values, or the requested frequency/period may not be present within tolerance. Inspect
value_at_frequency_detailsfor selected-frequency metadata.