Overlays#
The overlay helpers are reusable building blocks for custom maps and application views. They are useful when you want to add CRS transforms, contours, labels, profile lines, or topography to figures that you control yourself.
Use the high-level map builders first when you can:
pycsamt.map.StationMap, pycsamt.map.ProfileMap, and
pycsamt.map.VolumeMap already add the common survey overlays.
Use this page when you need to assemble traces manually, add your own
computed values, or keep the same overlay logic in scripts, notebooks,
and application callbacks.
Overlay Workflow#
Most overlay workflows follow the same pattern:
Load and normalize survey data.
Extract coordinates and values.
Reproject coordinates if needed.
Build one or more Plotly traces or layout settings.
Add the traces to a figure you own.
import plotly.graph_objects as go
from pycsamt.map import (
build_contour_overlay,
build_profile_line_overlay,
build_station_label_overlay,
ensure_map_data,
)
data = ensure_map_data("data/AMT/WILLY_DATA/L18PLT")
lon = [station.longitude for station in data.stations]
lat = [station.latitude for station in data.stations]
labels = data.station_ids
values = [station.index for station in data.stations]
fig = go.Figure()
fig.add_trace(build_contour_overlay(lon, lat, values))
fig.add_trace(build_profile_line_overlay(lon, lat, geo=False))
fig.add_trace(build_station_label_overlay(lon, lat, labels))
Coordinate Reference Systems#
Many CSAMT surveys store station positions in projected coordinates, while web maps usually expect WGS84 longitude and latitude. The CRS helpers keep that conversion explicit.
CRSConfigDescribes a source CRS, target CRS, and coordinate-axis convention.
normalize_epsgConverts integers or strings such as
4326into"EPSG:4326".transform_xyTransforms coordinate arrays between arbitrary CRS definitions.
reproject_xy_to_lonlatConvenience wrapper for projected coordinates to WGS84 lon/lat.
CRS Helpers#
import numpy as np
from pycsamt.map import CRSConfig, transform_xy
lon, lat = transform_xy(
np.array([500000.0, 500250.0]),
np.array([850000.0, 850200.0]),
crs=CRSConfig(source=32630, target=4326),
)
The returned arrays are NumPy arrays. With the default
always_xy=True, the input order is always x, y and the output
for EPSG:4326 is lon, lat. This avoids the common latitude/longitude
axis-order surprise in modern PROJ/GDAL stacks.
For display text or logs, use pycsamt.map.resolve_crs_info().
from pycsamt.map import resolve_crs_info
print(resolve_crs_info("geo"))
print(resolve_crs_info("utm", zone=30, hemisphere="N"))
print(resolve_crs_info("custom", epsg=32630))
Basemaps#
build_basemap_layout creates the geographic map settings shared by
station maps and application views. It does not return a full Plotly
layout dictionary; it returns a pycsamt.map.BasemapConfig
dataclass so callers can apply it to either new Plotly map APIs or
legacy Mapbox-backed figures.
from pycsamt.map import build_basemap_layout
basemap = build_basemap_layout(
lon,
lat,
dark=False,
bearing=0.0,
)
print(basemap.style)
print(basemap.center)
print(basemap.zoom)
Default styles are public and token-free:
dark=FalseUses
"open-street-map".dark=TrueUses
"carto-darkmatter".
Pass style=... when your application has its own tile policy:
basemap = build_basemap_layout(
lon,
lat,
style="carto-positron",
)
If all coordinates are missing or non-finite, the helper returns a
world-scale center at {"lat": 0.0, "lon": 0.0} and zoom 1.
Contours#
Contours interpolate scattered values to a regular grid. SciPy is used when available; otherwise the helper falls back to a nearest neighbour grid. This makes contour overlays useful in lightweight environments where SciPy is not installed, while still giving smoother linear interpolation when SciPy is available.
import numpy as np
from pycsamt.map import build_contour_overlay
contour = build_contour_overlay(
np.array([2.0, 2.1, 2.2]),
np.array([1.0, 1.1, 1.0]),
np.array([100.0, 120.0, 80.0]),
levels=8,
mode="both",
)
The coordinate arrays and value array must contain at least three finite points. Non-finite points are ignored before interpolation.
Important options:
levelsNumber of contour bands. Values below two are promoted to two.
cmapPlotly colorscale name, such as
"Viridis","Cividis", or"Turbo".opacityTrace opacity from
0to1.grid_sizeNumber of grid nodes along each axis. Larger grids look smoother but cost more memory and rendering time.
modeOne of
"lines","fill","filled","heatmap", or"both"."lines"draws contour lines only;"both"uses heatmap coloring with contour lines.
When you need the interpolated grid rather than a Plotly trace, call
pycsamt.map.interpolate_overlay_grid().
from pycsamt.map import interpolate_overlay_grid
xi, yi, grid = interpolate_overlay_grid(
lon,
lat,
values,
grid_size=120,
)
Labels And Profile Lines#
Station labels and profile lines are simple Plotly traces. They can be added on top of a station map, a custom contour figure, or a geographic scatter map.
from pycsamt.map import (
build_profile_line_overlay,
build_station_label_overlay,
)
line = build_profile_line_overlay(lon, lat, geo=True)
labels = build_station_label_overlay(
lon,
lat,
["S00", "S01"],
geo=True,
)
Use geo=True when x and y are longitude and latitude for a
Plotly map trace. Use geo=False for ordinary Cartesian figures,
including profile plots and local projected-coordinate maps.
labels = build_station_label_overlay(
lon,
lat,
labels=data.station_ids,
geo=True,
color="#0f172a",
)
profile = build_profile_line_overlay(
lon,
lat,
geo=True,
name="L18",
color="#2563eb",
width=2.5,
)
The helpers use Plotly’s modern Scattermap trace when available and
fall back to Scattermapbox for older Plotly versions.
Topography#
build_topography_overlay returns Mesh3d for scattered
elevations and Surface for 2-D elevation grids.
Scattered station elevations:
from pycsamt.map import build_topography_overlay
topo = build_topography_overlay(
x,
y,
elevation,
opacity=0.55,
colorscale="Earth",
)
Regular elevation grid:
topo = build_topography_overlay(
grid_x,
grid_y,
elevation_grid,
opacity=0.40,
)
Use topography overlays in 3-D figures where the vertical dimension is meaningful. For station maps, use elevation as a station overlay or a contour value instead of adding a 3-D terrain trace.
Adding Overlays To Figures#
The helpers return traces, so they can be added directly to a Plotly figure:
import plotly.graph_objects as go
fig = go.Figure()
fig.add_trace(contour)
fig.add_trace(line)
fig.add_trace(labels)
For geographic station maps, apply the basemap settings to the figure layout:
fig.update_layout(
map=dict(
style=basemap.style,
center=basemap.center,
zoom=basemap.zoom,
bearing=basemap.bearing,
)
)
For older Plotly versions that use Mapbox layout keys, applications may
need to map the same dataclass to mapbox instead:
fig.update_layout(
mapbox=dict(
style=basemap.style,
center=basemap.center,
zoom=basemap.zoom,
bearing=basemap.bearing,
)
)
Practical Examples#
Contour station resistivity at one frequency:
from pycsamt.map import (
build_contour_overlay,
ensure_map_data,
value_at_frequency_details,
)
data = ensure_map_data("data/AMT/WILLY_DATA/L18PLT")
values_by_station = value_at_frequency_details(
data,
frequency=100.0,
quantity="rho",
component="xy",
)
lon = []
lat = []
values = []
for station in data.stations:
if station.id not in values_by_station:
continue
lon.append(station.longitude)
lat.append(station.latitude)
values.append(values_by_station[station.id].value)
contour = build_contour_overlay(
lon,
lat,
values,
levels=12,
cmap="Viridis",
mode="both",
)
Build labels for every profile in a multi-line survey:
from pycsamt.map import (
build_profile_line_overlay,
build_station_label_overlay,
load_lines,
)
data = load_lines("data/AMT/WILLY_DATA", detect="folder")
traces = []
for profile in data.profiles:
lon = [station.longitude for station in profile.stations]
lat = [station.latitude for station in profile.stations]
labels = [station.id for station in profile.stations]
traces.append(
build_profile_line_overlay(
lon,
lat,
geo=True,
name=profile.name,
)
)
traces.append(
build_station_label_overlay(
lon,
lat,
labels,
geo=True,
)
)
Troubleshooting#
ImportErrorfrom CRS transformstransform_xyrequirespyproj. Install thegeoorfullextra before using CRS conversion helpers.ValueError: At least three finite points are required.Contour interpolation needs at least three finite coordinate/value triples. Drop incomplete stations or use a scatter trace instead.
- Empty or world-scale basemap
The input longitude/latitude arrays have no finite coordinate pairs. Inspect
data.has_geobefore building geographic overlays.- Contour is blocky
SciPy may be unavailable, so interpolation falls back to nearest neighbour. Install SciPy or increase
grid_sizeif appropriate.- Labels do not appear on a map
Check the
geoflag. Geographic Plotly maps needgeo=True; Cartesian figures needgeo=False.