Geoelectric Strike#

Geoelectric strike is the preferred 2-D structural direction inferred from electromagnetic data. It is one of the checks you make before rotating impedances, preparing 2-D inversion inputs, or interpreting transverse electric and transverse magnetic modes. In pyCSAMT, the strike tools estimate the direction from impedance rotation, phase tensor azimuth, or a consensus of both, then visualize the result as tables, profiles, ribbons, map-sticks, and rose diagrams.

Strike angles are axial. A strike of 0 degrees and a strike of 180 degrees are the same geological direction. For that reason, pyCSAMT reports station-level strike in the compact [-90, 90] range for estimator tables, while rose diagrams fold angles into 0 to 180 and mirror the histogram around the full polar circle.

Use this page when you need to answer concrete processing questions:

Question

Main tools

Output

What strike does each station prefer?

estimate_strike_sweep, estimate_strike_phase_tensor, estimate_strike_consensus

One row per station with angle, stability, period band, and sample count.

Does strike change with period?

strike_curve_sweep, plot_strike_ribbon

One angle per station and frequency, plus a ribbon image.

What strike should I rotate to?

rotate_to_strike

A corrected Sites object, with impedance tensors rotated by the selected station-level angles.

How do lines or bands compare?

plot_strike_rose, plot_strike_rose_by_line

Axial rose diagrams with weighted mean directions.

Is the strike spatially coherent?

plot_strike_profile, plot_strike_mapsticks

Along-line and geographic views.

How does Z strike compare with phase tensor and tipper direction?

plot_strike_analysis

Three-panel strike, PT azimuth, and tipper rose diagram.

The examples use the public two-level import style: from pycsamt.emtools import ....

Load Data#

Load the survey with ensure_sites first. This gives every strike function the same clean input and avoids repeating EDI parsing options in every call.

1from pathlib import Path
2
3from pycsamt.emtools import ensure_sites
4
5edi_dir = Path("data/AMT/WILLY_DATA/L18PLT")
6sites = ensure_sites(edi_dir, recursive=True, verbose=0)

For map and profile plots, coordinates matter. plot_strike_profile can order stations by "lon", "lat", "name", or "auto". Choose the ordering that matches the survey line, not merely the default. For a north-south line, sort_by="lat" is often the clearer choice. For an east-west line, sort_by="lon" is usually better.

Station-Level Estimators#

pyCSAMT provides three station-level strike estimators. They all return a pandas.DataFrame with the same practical columns:

Column

Meaning

station

Station identifier.

ang

Estimated strike angle in degrees, wrapped into [-90, 90].

iqr

Interquartile range of the frequency-level estimates used to summarize the station. Smaller values mean a more stable strike.

lo and hi

Period-band limits in seconds used by the estimate.

n

Number of frequency samples used.

The impedance sweep rotates each tensor through a grid of trial angles and chooses the angle that optimizes a metric.

 1import numpy as np
 2
 3from pycsamt.emtools import estimate_strike_sweep
 4
 5sweep = estimate_strike_sweep(
 6    sites,
 7    angles=np.arange(-90.0, 91.0, 1.0),
 8    metric="diag_ratio",
 9    band=(0.001, 10.0),
10)
11
12print(sweep[["station", "ang", "iqr", "n"]])
    station   ang    iqr   n
0   18-001A  34.0  175.5  39
1   18-002U  32.0  248.5  39
2   18-003A -21.0  305.5  39
3   18-004A -67.0  199.0  39
4   18-005U -36.0  181.0  39
5   18-006A -33.0  185.5  39
6   18-007U -41.0  165.0  39
7   18-008U -34.0  169.5  39
8   18-009A -75.0  177.0  39
9   18-010U -17.0   35.5  39
10  18-011A  76.0  185.0  39
11  18-012A -49.0   49.0  39
12  18-013U -38.0   49.5  39
13  18-014A -47.0  165.5  39
14  18-015U -13.0   85.0  39
15  18-016A  -8.0   84.0  39
16  18-017U -42.0   77.5  39
17  18-018A -14.0   55.0  39
18  18-019U -22.0  322.5  39
19  18-020A -37.0   96.5  39
20  18-021B -25.0  104.0  39
21  18-021U -29.0   24.5  39
22  18-022U -19.0   56.0  39
23  18-022V -53.0  146.5  39
24  18-023A -23.0   82.0  39
25  18-023V -46.0   45.0  39
26  18-024U -75.0   44.0  39
27  18-025A -66.0  319.0  39

The default metric="diag_ratio" searches for the rotation that minimizes diagonal energy relative to off-diagonal energy. This is a useful impedance-based strike diagnostic, but it can be sensitive to noise, 3-D effects, and weak diagonal/off-diagonal contrast.

The phase-tensor estimator summarizes the phase tensor theta angle. It is often more stable than a raw impedance sweep because the phase tensor is less affected by static-shift amplitude distortion.

1from pycsamt.emtools import estimate_strike_phase_tensor
2
3pt = estimate_strike_phase_tensor(
4    sites,
5    band=(0.001, 10.0),
6    robust=True,
7)
8
9print(pt[["station", "ang", "iqr", "n"]])
    station        ang         iqr   n
0   18-001A -44.641194   33.507488  39
1   18-002U -43.757578   13.310763  39
2   18-003A -37.153258   12.725693  39
3   18-004A -40.889492   17.540830  39
4   18-005U -42.404158   14.929706  39
5   18-006A -42.558905   26.757145  39
6   18-007U -45.967488   19.748005  39
7   18-008U -45.374795   33.130660  39
8   18-009A -40.484848   13.871500  39
9   18-010U -43.432226   19.080351  39
10  18-011A -40.391078   18.967773  39
11  18-012A -39.419545   26.067880  39
12  18-013U -41.742580   31.608457  39
13  18-014A -22.821394   72.754984  39
14  18-015U -33.387647  169.387460  39
15  18-016A -20.658294   24.764908  39
16  18-017U -26.561809   27.729096  39
17  18-018A -33.728193   13.362541  39
18  18-019U -31.821680   25.741787  39
19  18-020A -30.044215    6.472585  39
20  18-021B -21.755190   97.461766  39
21  18-021U -28.191808    6.079438  39
22  18-022U -28.484481  152.548514  39
23  18-022V -22.375149   18.359603  39
24  18-023A -41.324136  155.368896  39
25  18-023V -50.745390   40.183828  39
26  18-024U -51.114376   11.877314  39
27  18-025A -40.069989   24.228480  39

The consensus estimator blends the sweep and phase-tensor estimates. Use it when neither method should dominate the processing decision.

 1from pycsamt.emtools import estimate_strike_consensus
 2
 3consensus = estimate_strike_consensus(
 4    sites,
 5    band=(0.001, 10.0),
 6    w_sweep=0.4,
 7    w_pt=0.6,
 8    metric="diag_ratio",
 9)
10
11print(consensus[["station", "ang", "iqr", "n"]])
    station        ang         iqr   n
0   18-001A -13.184716  104.503744  78
1   18-002U -13.454547  130.905381  78
2   18-003A -30.691955  159.112846  78
3   18-004A -51.333695  108.270415  78
4   18-005U -39.842495   97.964853  78
5   18-006A -38.735343  106.128573  78
6   18-007U -43.980493   92.374002  78
7   18-008U -40.824877  101.315330  78
8   18-009A -54.290909   95.435750  78
9   18-010U -32.859336   27.290176  78
10  18-011A   6.165353  101.983886  78
11  18-012A -43.251727   37.533940  78
12  18-013U -40.245548   40.554228  78
13  18-014A -32.492836  119.127492  78
14  18-015U -25.232588  127.193730  78
15  18-016A -15.594976   54.382454  78
16  18-017U -32.737085   52.614548  78
17  18-018A -25.836916   34.181271  78
18  18-019U -27.893008  174.120894  78
19  18-020A -32.826529   51.486293  78
20  18-021B -23.053114  100.730883  78
21  18-021U -28.515085   15.289719  78
22  18-022U -24.690689  104.274257  78
23  18-022V -34.625089   82.429802  78
24  18-023A -33.994482  118.684448  78
25  18-023V -48.847234   42.591914  78
26  18-024U -60.668626   27.938657  78
27  18-025A -50.441993  171.614240  78

For all three tables, treat iqr as a stability warning. A station with an angle near 30 degrees and an iqr near 80 degrees does not have a reliable single strike; it has a broad or frequency-dependent strike population.

Compare Axial Angles Correctly#

Do not compare strike estimates with ordinary subtraction unless you first account for the 180 degree ambiguity. The axial difference between 89 and -89 degrees is 2 degrees, not 178 degrees.

 1merged = sweep.merge(
 2    pt,
 3    on="station",
 4    suffixes=("_sweep", "_pt"),
 5)
 6
 7axial_diff = (
 8    (merged["ang_sweep"] - merged["ang_pt"] + 90.0) % 180.0
 9) - 90.0
10
11merged["abs_axial_diff"] = axial_diff.abs()
12
13print(
14    merged[
15        ["station", "ang_sweep", "ang_pt", "abs_axial_diff"]
16    ].sort_values("abs_axial_diff", ascending=False)
17)
    station  ang_sweep     ang_pt  abs_axial_diff
0   18-001A       34.0 -44.641194       78.641194
1   18-002U       32.0 -43.757578       75.757578
10  18-011A       76.0 -40.391078       63.608922
8   18-009A      -75.0 -40.484848       34.515152
23  18-022V      -53.0 -22.375149       30.624851
9   18-010U      -17.0 -43.432226       26.432226
3   18-004A      -67.0 -40.889492       26.110508
27  18-025A      -66.0 -40.069989       25.930011
13  18-014A      -47.0 -22.821394       24.178606
26  18-024U      -75.0 -51.114376       23.885624
14  18-015U      -13.0 -33.387647       20.387647
17  18-018A      -14.0 -33.728193       19.728193
24  18-023A      -23.0 -41.324136       18.324136
2   18-003A      -21.0 -37.153258       16.153258
16  18-017U      -42.0 -26.561809       15.438191
15  18-016A       -8.0 -20.658294       12.658294
7   18-008U      -34.0 -45.374795       11.374795
18  18-019U      -22.0 -31.821680        9.821680
11  18-012A      -49.0 -39.419545        9.580455
5   18-006A      -33.0 -42.558905        9.558905
22  18-022U      -19.0 -28.484481        9.484481
19  18-020A      -37.0 -30.044215        6.955785
4   18-005U      -36.0 -42.404158        6.404158
6   18-007U      -41.0 -45.967488        4.967488
25  18-023V      -46.0 -50.745390        4.745390
12  18-013U      -38.0 -41.742580        3.742580
20  18-021B      -25.0 -21.755190        3.244810
21  18-021U      -29.0 -28.191808        0.808192

Use this pattern when comparing sweep, phase tensor, consensus, tipper azimuth, or externally interpreted structural trends. A naive Pearson correlation of raw angles can be misleading because it treats the wrap boundary as a real discontinuity.

Choose A Period Band#

The band argument is a period band in seconds. It is available on the station-level estimators and on the high-level plots that summarize station-level strike. Use it to separate shallow, high-frequency behavior from deeper, long-period behavior.

 1short_period = estimate_strike_consensus(
 2    sites,
 3    band=(0.001, 0.1),
 4)
 5
 6long_period = estimate_strike_consensus(
 7    sites,
 8    band=(0.1, 10.0),
 9)
10
11band_compare = short_period[["station", "ang", "iqr"]].merge(
12    long_period[["station", "ang", "iqr"]],
13    on="station",
14    suffixes=("_short", "_long"),
15)
16
17band_compare["band_axial_diff"] = (
18    (band_compare["ang_short"] - band_compare["ang_long"] + 90.0)
19    % 180.0
20) - 90.0
21
22print(band_compare)
    station  ang_short   iqr_short   ang_long    iqr_long  band_axial_diff
0   18-001A -54.506201   41.369421 -63.900455  123.466880         9.394254
1   18-002U -48.985879  160.057160 -36.157439   19.623319       -12.828440
2   18-003A  19.394690   78.014960 -22.303922   91.734047        41.698612
3   18-004A  13.847161   90.942272 -27.625934   87.492859        41.473095
4   18-005U   6.699252  139.105342 -32.804464    8.183583        39.503716
5   18-006A -45.672788   25.902400 -27.695474   15.377996       -17.977315
6   18-007U -43.969709   97.304264 -40.122137    5.186640        -3.847572
7   18-008U  -9.158404  101.878737 -41.169270   27.580778        32.010866
8   18-009A -54.850045   44.971449 -25.514648   51.649186       -29.335397
9   18-010U -38.081566   29.303524 -22.194658   95.009109       -15.886908
10  18-011A -27.725972   74.750686 -23.482145    6.419555        -4.243828
11  18-012A -52.101381   30.124857 -24.691455   11.576741       -27.409926
12  18-013U -45.827104   71.741797 -30.024716   12.679180       -15.802388
13  18-014A -29.010735   47.579291  -1.990831   87.090185       -27.019904
14  18-015U -22.291812   94.794970   1.541876  138.925951       -23.833688
15  18-016A -44.272043   60.760395 -15.286250   92.857554       -28.985794
16  18-017U -60.409554   37.127807  31.430982   92.784065        88.159465
17  18-018A -23.871328   47.793334 -36.763786   97.172580        12.892458
18  18-019U  26.599036   81.719385 -34.186076   31.783625        60.785112
19  18-020A -60.182514   85.406263 -32.313086    3.341403       -27.869429
20  18-021B -17.977499   83.255421 -22.009438   56.404952         4.031939
21  18-021U -27.000027   18.589938 -29.192501    3.856444         2.192474
22  18-022U  -5.488390  103.595335 -24.809677   54.724645        19.321287
23  18-022V  -0.989376   80.416924 -30.666820    4.212487        29.677445
24  18-023A -32.013201  115.048817 -16.838399   49.906991       -15.174802
25  18-023V -49.434564   52.035662 -61.711376   86.588207        12.276812
26  18-024U -68.098458   50.928656 -45.399175   33.879698       -22.699283
27  18-025A -15.407326  101.090032 -39.573699   66.987043        24.166372

If short- and long-period strikes disagree strongly, do not force a single rotation across the entire band. Review dimensionality, static shift, near-surface diagnostics, and the inversion band before choosing a processing strike.

Rotate Data Onto Strike#

rotate_to_strike estimates one strike angle per station and rotates that station’s impedance tensor. Keep the original and rotated data separate until you have checked the result.

 1from pycsamt.emtools import rotate_to_strike
 2
 3rotated = rotate_to_strike(
 4    sites,
 5    method="consensus",
 6    band=(0.001, 10.0),
 7    metric="diag_ratio",
 8    inplace=False,
 9)
10
11before = estimate_strike_consensus(
12    sites,
13    band=(0.001, 10.0),
14)
15after = estimate_strike_consensus(
16    rotated,
17    band=(0.001, 10.0),
18)
19
20print("before mean abs strike:", before["ang"].abs().mean())
21print("after mean abs strike:", after["ang"].abs().mean())
before mean abs strike: 33.75926323036427
after mean abs strike: 23.699459229145212

Valid method names are "consensus", "sweep", and "pt". Use inplace=False while building a workflow. It returns a rotated copy and keeps the unrotated survey available for before/after checks.

Rotation does not make a survey 2-D by itself. If the selected band has high skew, unstable strike, or strong station-to-station disagreement, the rotated tensors may still be poor 2-D inversion input.

Per-Frequency Strike Curve#

strike_curve_sweep keeps the frequency dimension instead of reducing each station to one number. It is useful for finding unstable bands or stations whose strike flips with period.

 1from pycsamt.emtools import strike_curve_sweep
 2
 3curve = strike_curve_sweep(
 4    sites,
 5    angles=np.arange(-90.0, 91.0, 1.0),
 6    metric="diag_ratio",
 7    smooth=5,
 8)
 9
10print(curve.head())
11print(curve.groupby("station")["ang"].agg(["median", "std", "count"]))
    station     freq    period   ang
0  18-001A  10400.0  0.000096  59.0
1  18-001A   8707.0  0.000115 -78.2
2  18-001A   7289.0  0.000137 -36.2
3  18-001A   6102.0  0.000164  -2.2
4  18-001A   5108.0  0.000196  -3.2
         median        std  count
station
18-001A   -27.4  52.458417     53
18-002U   -45.8  60.834128     53
18-003A   -22.4  46.238784     53
18-004A   -21.6  50.020511     53
18-005U   -18.6  43.659775     53
18-006A   -37.8  34.259013     53
18-007U   -43.8  38.061708     53
18-008U   -47.4  45.431997     53
18-009A   -16.0  47.725733     53
18-010U   -18.6  44.842346     53
18-011A    -9.8  47.598151     53
18-012A   -40.8  39.072262     53
18-013U   -38.0  48.110960     53
18-014A   -24.2  32.911592     53
18-015U   -16.4  49.769125     53
18-016A   -46.2  33.376512     53
18-017U   -66.0  41.357284     53
18-018A   -19.6  37.349080     53
18-019U   -21.8  50.727413     53
18-020A   -31.8  31.427665     53
18-021B   -23.2  38.380236     53
18-021U   -33.2  36.604952     53
18-022U   -38.8  32.873812     53
18-022V   -37.2  38.037783     53
18-023A   -28.6  48.879846     53
18-023V   -40.6  33.763535     53
18-024U   -36.2  56.412705     53
18-025A   -29.0  54.837346     53

The table columns are station, freq, and ang. smooth applies a moving average to the frequency-level sweep angles before they are wrapped back into the axial range. Increase it only when you want a smoother visual trend; do not use smoothing to hide genuine strike changes.

Ribbon Plot#

plot_strike_ribbon converts the per-frequency strike curve to a station-by-period image. Hue encodes strike angle. Saturation encodes local stability: desaturated colors indicate high local variance.

 1import matplotlib.pyplot as plt
 2
 3from pycsamt.emtools import plot_strike_ribbon
 4
 5ax = plot_strike_ribbon(
 6    sites,
 7    method="sweep",
 8    win=5,
 9    show_colorbar=True,
10)
11ax.figure.savefig("strike_ribbon.png", dpi=200, bbox_inches="tight")
12plt.close(ax.figure)
../../_images/user-guide-emtools-strike-09.png

Use the ribbon before selecting a single strike for a broad period band. If the ribbon changes color systematically from short period to long period, the survey may need band-specific interpretation.

Rose Diagrams#

plot_strike_rose draws axial strike histograms. It mirrors the 0 to 180 degree histogram around the full circle, so both halves of the polar plot represent the same set of axes.

 1import matplotlib.pyplot as plt
 2
 3from pycsamt.emtools import plot_strike_rose
 4
 5fig = plot_strike_rose(
 6    sites,
 7    method="consensus",
 8    band=(0.001, 10.0),
 9    bins=36,
10    weight="inv_iqr",
11    suptitle="Consensus geoelectric strike",
12)
13fig.savefig("strike_rose_consensus.png", dpi=200, bbox_inches="tight")
14plt.close(fig)
../../_images/user-guide-emtools-strike-10.png

weight="inv_iqr" down-weights stations whose strike varies strongly with frequency. Use weight="uniform" when every station should contribute equally.

When groups is omitted, pyCSAMT attempts to group stations by a profile-like station-name prefix. This works for names such as E1S01 because the inferred group is E1. For station names that do not encode the line this way, pass an explicit mapping.

 1from pathlib import Path
 2
 3line18 = sorted(Path("data/AMT/WILLY_DATA/L18PLT").glob("*.edi"))
 4line22 = sorted(Path("data/AMT/WILLY_DATA/L22PLT").glob("*.edi"))
 5
 6groups = {
 7    "L18PLT": [path.stem for path in line18],
 8    "L22PLT": [path.stem for path in line22],
 9}
10
11fig = plot_strike_rose(
12    line18 + line22,
13    groups=groups,
14    method="consensus",
15    bins=36,
16    n_cols=2,
17    suptitle="Strike by profile line",
18)
19fig.savefig("strike_rose_profiles.png", dpi=200, bbox_inches="tight")
20plt.close(fig)
../../_images/user-guide-emtools-strike-11.png

plot_strike_rose_by_line is a simpler line-comparison helper. It requires at least two stations per group; if automatic grouping produces only singleton groups, pass the explicit groups dictionary yourself.

 1from pycsamt.emtools import plot_strike_rose_by_line
 2
 3fig = plot_strike_rose_by_line(
 4    line18 + line22,
 5    groups=groups,
 6    method="consensus",
 7    band=(0.001, 10.0),
 8    weight="inv_iqr",
 9)
10fig.savefig("strike_rose_by_line.png", dpi=200, bbox_inches="tight")
11plt.close(fig)
../../_images/user-guide-emtools-strike-12.png

Frequency-Band Roses#

Use bar_style="bands" when you want one rose diagram to show several period bands. Each band contributes its own stacked histogram.

 1fig = plot_strike_rose(
 2    sites,
 3    method="consensus",
 4    bar_style="bands",
 5    freq_bands=[
 6        (0.001, 0.01),
 7        (0.01, 0.1),
 8        (0.1, 10.0),
 9    ],
10    band_labels=[
11        "very short period",
12        "short period",
13        "long period",
14    ],
15    suptitle="Strike by period band",
16)
17fig.savefig("strike_rose_bands.png", dpi=200, bbox_inches="tight")
18plt.close(fig)
../../_images/user-guide-emtools-strike-13.png

If the bands stack around different mean directions, report the band-specific behavior instead of collapsing it to one survey-wide number.

Profile And Map-Stick Views#

plot_strike_profile shows strike angle along station order with an IQR ribbon. It is the best quick check for station-to-station coherence.

 1import matplotlib.pyplot as plt
 2
 3from pycsamt.emtools import plot_strike_profile
 4
 5ax = plot_strike_profile(
 6    sites,
 7    method="consensus",
 8    band=(0.001, 10.0),
 9    sort_by="lat",
10)
11ax.figure.savefig("strike_profile.png", dpi=200, bbox_inches="tight")
12plt.close(ax.figure)
../../_images/user-guide-emtools-strike-14.png

The profile plot uses ang as the line and iqr as the uncertainty ribbon. A coherent 2-D line should not show random jumps from station to station unless there is a real geological or data-quality reason.

plot_strike_mapsticks draws a short line segment at each station coordinate, oriented along the estimated strike. It is useful for checking whether nearby stations point in a consistent direction.

 1import matplotlib.pyplot as plt
 2
 3from pycsamt.emtools import plot_strike_mapsticks
 4
 5ax = plot_strike_mapsticks(
 6    sites,
 7    method="consensus",
 8    band=(0.001, 10.0),
 9    len_deg=0.02,
10)
11lats = [site.coords[0] for site in sites if site.coords]
12lons = [site.coords[1] for site in sites if site.coords]
13lon_pad = (max(lons) - min(lons)) * 0.15
14lat_pad = (max(lats) - min(lats)) * 0.15
15ax.set_xlim(min(lons) - lon_pad, max(lons) + lon_pad)
16ax.set_ylim(min(lats) - lat_pad, max(lats) + lat_pad)
17ax.set_aspect("auto", adjustable="box")
18ax.ticklabel_format(axis="x", style="plain", useOffset=False)
19ax.figure.savefig("strike_mapsticks.png", dpi=200, bbox_inches="tight")
20plt.close(ax.figure)
../../_images/user-guide-emtools-strike-15.png

The len_deg value is a display length in coordinate degrees, not a geological length. Adjust it for readability when the survey extent is very small or very large.

Combined Strike Analysis#

plot_strike_analysis creates a three-panel rose figure: impedance strike, phase-tensor azimuth, and tipper strike. The tipper panel is empty when the data do not contain vertical magnetic transfer functions.

 1import matplotlib.pyplot as plt
 2
 3from pycsamt.emtools import plot_strike_analysis
 4
 5fig = plot_strike_analysis(
 6    sites,
 7    method="consensus",
 8    band=(0.001, 10.0),
 9    bins=36,
10    suptitle="Strike, phase tensor, and tipper azimuth",
11)
12fig.savefig("strike_analysis.png", dpi=200, bbox_inches="tight")
13plt.close(fig)
../../_images/user-guide-emtools-strike-16.png

Use this figure as a consistency check. If Z strike and phase-tensor azimuth cluster around the same axial direction, confidence increases. If tipper strike is available and points somewhere else, investigate regional 3-D structure, coast effects, cultural noise, or sign convention before choosing a rotation angle.

Common Pitfalls#

Strike has a 180 degree ambiguity. Always compare angles with an axial difference formula.

iqr is not decoration. A high-IQR strike is unstable across frequency and should not be used blindly as a rotation angle.

The period band is part of the result. A strike estimated over (0.001, 0.1) seconds may not match a strike estimated over (0.1, 10.0) seconds.

Automatic rose grouping depends on station names. If your station names do not encode line membership, pass groups explicitly.

rotate_to_strike rotates by station-level estimates. It does not guarantee a single regional strike, and it does not remove 3-D structure.

Map-stick plots require usable station coordinates. If no coordinates are available, use profile and rose diagrams instead.

Worked Example#

The gallery example uses L18PLT, adds L22PLT for a multi-line rose comparison, demonstrates estimator agreement, rotates data onto strike, and builds ribbon, rose, map-stick, profile, and combined strike-analysis figures.

Open the rendered gallery page here: Geoelectric strike estimation and visualization (pycsamt.emtools.strike).