Skip to content

skyweaver.viz.ground

Ground-track plotting utilities.

plot_ground_track

plot_ground_track(track, ax=None, *, observatories=None, show_labels=True)

Plot a simple longitude-latitude ground track.

Parameters:

Name Type Description Default
track GroundTrack

Ground track to plot.

required
ax Axes | None

Optional matplotlib axes. If not provided, a new figure and axes are created.

None
observatories Sequence[Observatory] | None

Optional sequence of observatories to mark on the map.

None
show_labels bool

If True, annotate observatory names next to their markers.

True

Returns:

Type Description
Axes

Matplotlib axes containing the plot.

Source code in src/skyweaver/viz/ground.py
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
def plot_ground_track(
    track: GroundTrack,
    ax: Axes | None = None,
    *,
    observatories: Sequence[Observatory] | None = None,
    show_labels: bool = True,
) -> Axes:
    """Plot a simple longitude-latitude ground track.

    Parameters
    ----------
    track
        Ground track to plot.
    ax
        Optional matplotlib axes. If not provided, a new figure and axes are
        created.
    observatories
        Optional sequence of observatories to mark on the map.
    show_labels
        If True, annotate observatory names next to their markers.

    Returns
    -------
    Axes
        Matplotlib axes containing the plot.
    """
    if ax is None:
        _, ax = plt.subplots(figsize=(10, 5))

    m = Basemap(
        projection="cyl",
        llcrnrlat=-90,
        urcrnrlat=90,
        llcrnrlon=-180,
        urcrnrlon=180,
        resolution="c",
        ax=ax,
    )

    land_color = to_hex(cmr.pride(0.73))
    water_color = to_hex(cmr.pride(0.3))

    m.drawcoastlines(color="#222222", linewidth=0.3)
    m.drawmapboundary(fill_color=water_color)
    m.fillcontinents(color=land_color, lake_color=water_color)
    m.drawparallels(np.arange(-90.0, 91.0, 30.0), linewidth=0.5, alpha=0.7)
    m.drawmeridians(np.arange(-180.0, 181.0, 60.0), linewidth=0.5, alpha=0.7)

    lon = track.longitude_deg.copy()
    lat = track.latitude_deg.copy()

    # find jumps across the dateline
    jumps = np.abs(np.diff(lon)) > 180.0

    # insert NaNs after jump locations
    lon_plot = lon.astype(float)
    lat_plot = lat.astype(float)

    lon_plot = np.insert(lon_plot, np.where(jumps)[0] + 1, np.nan)
    lat_plot = np.insert(lat_plot, np.where(jumps)[0] + 1, np.nan)

    ax.plot(
        lon_plot,
        lat_plot,
        lw=0.1,
        alpha=0.9,
        color="whitesmoke",
    )

    if observatories is not None:
        for obs in observatories:
            x, y = m(obs.longitude_deg, obs.latitude_deg)
            x = float(str(x))
            y = float(str(y))

            ax.scatter(
                x,
                y,
                s=121,
                marker=r"$⬢$",
                color=cmr.pride(0.6),
                edgecolors="whitesmoke",
                linewidths=0.7,
                zorder=49,
            )

            if show_labels:
                ax.annotate(
                    obs.name,
                    xy=(x, y),
                    xytext=(7, 4),
                    textcoords="offset points",
                    fontsize=9,
                    ha="left",
                    va="bottom",
                    zorder=49,
                    bbox=dict(
                        boxstyle="round,pad=0.2",
                        facecolor="whitesmoke",
                        edgecolor="#222222",
                        linewidth=0.4,
                        alpha=1,
                    ),
                )

    ax.set_xlabel("Longitude [deg]")
    ax.set_ylabel("Latitude [deg]")
    ax.set_title(f"Ground track: {track.orbit.name}")
    ax.set_xticks(np.arange(-120, 121, 60))
    ax.set_yticks(np.arange(-60, 61, 30))
    ax.grid(True)

    return ax