Skip to content

plotting

cosmica.dynamics.plotting

__all__ module-attribute

__all__ = [
    "visualize_grouped_constellation",
    "visualize_multi_orbital_plane_constellation",
]

InPlaneIndex

InPlaneIndex = int

PlaneId

PlaneId = int

visualize_grouped_constellation

visualize_grouped_constellation(
    constellation: Constellation[
        tuple[PlaneId, InPlaneIndex]
    ],
    propagation_result: Mapping[
        ConstellationSatellite, SatelliteOrbitState
    ],
    *,
    time_index: int = 0
) -> None

Visualize a grouped constellation in 3D.

The constellation must be parameterized as Constellation[tuple[int, int]] where each key is (plane_id, in_plane_index). Plane structure is derived entirely from the dict keys — not from orbital parameters or satellite.id.

Plots one orbital trajectory per plane (using the first satellite in each plane) and marks all satellite positions at the given time_index.

PARAMETER DESCRIPTION
constellation

Constellation with (plane_id, in_plane_index) keys.

TYPE: Constellation[tuple[PlaneId, InPlaneIndex]]

propagation_result

Mapping from satellite objects to propagation results.

TYPE: Mapping[ConstellationSatellite, SatelliteOrbitState]

time_index

Time step index at which to plot satellite positions.

TYPE: int DEFAULT: 0

Source code in src/cosmica/dynamics/plotting.py
 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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
def visualize_grouped_constellation(
    constellation: Constellation[tuple[PlaneId, InPlaneIndex]],
    propagation_result: Mapping[ConstellationSatellite, SatelliteOrbitState],
    *,
    time_index: int = 0,
) -> None:
    """Visualize a grouped constellation in 3D.

    The constellation must be parameterized as `Constellation[tuple[int, int]]`
    where each key is `(plane_id, in_plane_index)`. Plane structure is derived
    entirely from the dict keys — not from orbital parameters or `satellite.id`.

    Plots one orbital trajectory per plane (using the first satellite in each
    plane) and marks all satellite positions at the given `time_index`.

    Args:
        constellation: Constellation with `(plane_id, in_plane_index)` keys.
        propagation_result: Mapping from satellite objects to propagation results.
        time_index: Time step index at which to plot satellite positions.

    """
    fig = plt.figure()
    ax: Axes3D = fig.add_subplot(111, projection="3d")

    cmap = mpl.colormaps["tab20"]

    # Plot the Earth
    ax.plot_surface(*_ms(0, 0, 0, EARTH_RADIUS), color="blue", alpha=0.2)

    # Group satellites by plane_id (first element of the structural key).
    # All structural information comes from dict keys, not from satellite.id.
    planes_dd: defaultdict[PlaneId, list[tuple[InPlaneIndex, ConstellationSatellite]]] = defaultdict(list)
    for (plane_id, in_plane_index), satellite in constellation.satellites.items():
        planes_dd[plane_id].append((in_plane_index, satellite))

    planes = dict(planes_dd)

    for i, plane_id in enumerate(sorted(planes)):  # sort by plane_id
        # Sort by in_plane_index to find the first satellite
        sats_in_plane = sorted(planes[plane_id])
        first_satellite = sats_in_plane[0][1]

        # Plot trajectory of the first satellite in this plane
        ax.plot(
            propagation_result[first_satellite].position_eci[:, 0],
            propagation_result[first_satellite].position_eci[:, 1],
            propagation_result[first_satellite].position_eci[:, 2],
            color=cmap(i),
            linewidth=0.5,
            label=f"Plane {plane_id}",
        )

    # Plot all satellite positions at the given time index
    for satellite in constellation.satellites.values():
        ax.plot(
            propagation_result[satellite].position_eci[time_index, 0],
            propagation_result[satellite].position_eci[time_index, 1],
            propagation_result[satellite].position_eci[time_index, 2],
            "ro",
            markersize=2,
        )

    ax.set_xlabel("X (km)")
    ax.set_ylabel("Y (km)")
    ax.set_zlabel("Z (km)")
    ax.set_box_aspect([1, 1, 1])

    ax.legend()

    plt.show()

visualize_multi_orbital_plane_constellation

visualize_multi_orbital_plane_constellation(
    constellation: MultiOrbitalPlaneConstellation[
        CircularSatelliteOrbit
    ],
    propagation_result: Mapping[
        ConstellationSatellite, SatelliteOrbitState
    ],
    time_index: int = 0,
) -> None
Source code in src/cosmica/dynamics/plotting.py
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
@deprecated("Use visualize_grouped_constellation() instead.")
def visualize_multi_orbital_plane_constellation(
    constellation: MultiOrbitalPlaneConstellation[CircularSatelliteOrbit],
    propagation_result: Mapping[ConstellationSatellite, SatelliteOrbitState],
    time_index: int = 0,
) -> None:
    fig = plt.figure()
    ax: Axes3D = fig.add_subplot(111, projection="3d")

    cmap = mpl.colormaps["tab20"]

    # Plot the Earth
    ax.plot_surface(*_ms(0, 0, 0, EARTH_RADIUS), color="blue", alpha=0.2)

    # Plot the orbit trajectory of the first satellite in each orbital plane
    for i, plane_id in enumerate(constellation.plane_ids):
        satellite = constellation.plane_id_to_satellites[plane_id][0]
        ax.plot(
            propagation_result[satellite].position_eci[:, 0],
            propagation_result[satellite].position_eci[:, 1],
            propagation_result[satellite].position_eci[:, 2],
            color=cmap(i),
            linewidth=0.5,
            label=f"Plane {plane_id}",
        )
        for satellite in constellation.satellites:
            # Plot the position of each satellite at the `time_index`
            ax.plot(
                propagation_result[satellite].position_eci[time_index, 0],
                propagation_result[satellite].position_eci[time_index, 1],
                propagation_result[satellite].position_eci[time_index, 2],
                "ro",
                markersize=2,
            )

    # Set plot labels and aspect ratio
    ax.set_xlabel("X (km)")
    ax.set_ylabel("Y (km)")
    ax.set_zlabel("Z (km)")
    ax.set_box_aspect([1, 1, 1])

    ax.legend()

    plt.show()