open-space-toolkit-astrodynamics 12.0.1__py39-none-manylinux2014_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. open_space_toolkit_astrodynamics-12.0.1.dist-info/METADATA +30 -0
  2. open_space_toolkit_astrodynamics-12.0.1.dist-info/RECORD +100 -0
  3. open_space_toolkit_astrodynamics-12.0.1.dist-info/WHEEL +5 -0
  4. open_space_toolkit_astrodynamics-12.0.1.dist-info/top_level.txt +1 -0
  5. open_space_toolkit_astrodynamics-12.0.1.dist-info/zip-safe +1 -0
  6. ostk/__init__.py +1 -0
  7. ostk/astrodynamics/OpenSpaceToolkitAstrodynamicsPy.cpython-39-x86_64-linux-gnu.so +0 -0
  8. ostk/astrodynamics/__init__.py +11 -0
  9. ostk/astrodynamics/converters.py +128 -0
  10. ostk/astrodynamics/dataframe.py +477 -0
  11. ostk/astrodynamics/display.py +220 -0
  12. ostk/astrodynamics/libopen-space-toolkit-astrodynamics.so.12 +0 -0
  13. ostk/astrodynamics/pytrajectory/__init__.py +1 -0
  14. ostk/astrodynamics/pytrajectory/pystate.py +196 -0
  15. ostk/astrodynamics/test/__init__.py +1 -0
  16. ostk/astrodynamics/test/access/__init__.py +1 -0
  17. ostk/astrodynamics/test/access/test_generator.py +248 -0
  18. ostk/astrodynamics/test/conftest.py +119 -0
  19. ostk/astrodynamics/test/conjunction/message/ccsds/__init__.py +1 -0
  20. ostk/astrodynamics/test/conjunction/message/ccsds/conftest.py +325 -0
  21. ostk/astrodynamics/test/conjunction/message/ccsds/data/cdm.json +303 -0
  22. ostk/astrodynamics/test/conjunction/message/ccsds/test_cdm.py +416 -0
  23. ostk/astrodynamics/test/dynamics/__init__.py +1 -0
  24. ostk/astrodynamics/test/dynamics/data/Tabulated_Earth_Gravity.csv +565 -0
  25. ostk/astrodynamics/test/dynamics/data/Tabulated_Earth_Gravity_Truth.csv +100 -0
  26. ostk/astrodynamics/test/dynamics/test_atmospheric_drag.py +128 -0
  27. ostk/astrodynamics/test/dynamics/test_central_body_gravity.py +58 -0
  28. ostk/astrodynamics/test/dynamics/test_dynamics.py +50 -0
  29. ostk/astrodynamics/test/dynamics/test_position_derivative.py +51 -0
  30. ostk/astrodynamics/test/dynamics/test_tabulated.py +138 -0
  31. ostk/astrodynamics/test/dynamics/test_third_body_gravity.py +67 -0
  32. ostk/astrodynamics/test/dynamics/test_thruster.py +142 -0
  33. ostk/astrodynamics/test/event_condition/test_angular_condition.py +113 -0
  34. ostk/astrodynamics/test/event_condition/test_boolean_condition.py +55 -0
  35. ostk/astrodynamics/test/event_condition/test_coe_condition.py +87 -0
  36. ostk/astrodynamics/test/event_condition/test_instant_condition.py +48 -0
  37. ostk/astrodynamics/test/event_condition/test_logical_condition.py +120 -0
  38. ostk/astrodynamics/test/event_condition/test_real_condition.py +50 -0
  39. ostk/astrodynamics/test/flight/__init__.py +1 -0
  40. ostk/astrodynamics/test/flight/system/__init__.py +1 -0
  41. ostk/astrodynamics/test/flight/system/test_propulsion_system.py +73 -0
  42. ostk/astrodynamics/test/flight/system/test_satellite_system.py +91 -0
  43. ostk/astrodynamics/test/flight/system/test_satellite_system_builder.py +71 -0
  44. ostk/astrodynamics/test/flight/test_maneuver.py +212 -0
  45. ostk/astrodynamics/test/flight/test_profile.py +242 -0
  46. ostk/astrodynamics/test/flight/test_system.py +55 -0
  47. ostk/astrodynamics/test/guidance_law/test_constant_thrust.py +91 -0
  48. ostk/astrodynamics/test/guidance_law/test_qlaw.py +138 -0
  49. ostk/astrodynamics/test/solvers/__init__.py +1 -0
  50. ostk/astrodynamics/test/solvers/test_finite_difference_solver.py +181 -0
  51. ostk/astrodynamics/test/solvers/test_temporal_condition_solver.py +153 -0
  52. ostk/astrodynamics/test/test_access.py +128 -0
  53. ostk/astrodynamics/test/test_converters.py +290 -0
  54. ostk/astrodynamics/test/test_dataframe.py +875 -0
  55. ostk/astrodynamics/test/test_display.py +114 -0
  56. ostk/astrodynamics/test/test_event_condition.py +58 -0
  57. ostk/astrodynamics/test/test_import.py +26 -0
  58. ostk/astrodynamics/test/test_root_solver.py +70 -0
  59. ostk/astrodynamics/test/test_trajectory.py +40 -0
  60. ostk/astrodynamics/test/test_utilities.py +106 -0
  61. ostk/astrodynamics/test/test_viewer.py +129 -0
  62. ostk/astrodynamics/test/trajectory/__init__.py +1 -0
  63. ostk/astrodynamics/test/trajectory/orbit/__init__.py +1 -0
  64. ostk/astrodynamics/test/trajectory/orbit/message/__init__.py +1 -0
  65. ostk/astrodynamics/test/trajectory/orbit/message/spacex/__init__.py +1 -0
  66. ostk/astrodynamics/test/trajectory/orbit/message/spacex/conftest.py +18 -0
  67. ostk/astrodynamics/test/trajectory/orbit/message/spacex/data/opm_1.yaml +44 -0
  68. ostk/astrodynamics/test/trajectory/orbit/message/spacex/test_opm.py +108 -0
  69. ostk/astrodynamics/test/trajectory/orbit/models/__init__.py +1 -0
  70. ostk/astrodynamics/test/trajectory/orbit/models/kepler/__init__.py +1 -0
  71. ostk/astrodynamics/test/trajectory/orbit/models/kepler/test_brouwer_lyddane_mean.py +65 -0
  72. ostk/astrodynamics/test/trajectory/orbit/models/kepler/test_brouwer_lyddane_mean_long.py +102 -0
  73. ostk/astrodynamics/test/trajectory/orbit/models/kepler/test_brouwer_lyddane_mean_short.py +102 -0
  74. ostk/astrodynamics/test/trajectory/orbit/models/kepler/test_coe.py +167 -0
  75. ostk/astrodynamics/test/trajectory/orbit/models/sgp4/__init__.py +1 -0
  76. ostk/astrodynamics/test/trajectory/orbit/models/sgp4/test_tle.py +331 -0
  77. ostk/astrodynamics/test/trajectory/orbit/models/test_kepler.py +130 -0
  78. ostk/astrodynamics/test/trajectory/orbit/models/test_propagated.py +234 -0
  79. ostk/astrodynamics/test/trajectory/orbit/models/test_sgp4.py +1 -0
  80. ostk/astrodynamics/test/trajectory/orbit/models/test_tabulated.py +380 -0
  81. ostk/astrodynamics/test/trajectory/orbit/test_model.py +1 -0
  82. ostk/astrodynamics/test/trajectory/orbit/test_pass.py +75 -0
  83. ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_angular_velocity.py +30 -0
  84. ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_attitude_quaternion.py +18 -0
  85. ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_cartesian_position.py +107 -0
  86. ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_cartesian_velocity.py +115 -0
  87. ostk/astrodynamics/test/trajectory/state/test_coordinate_broker.py +84 -0
  88. ostk/astrodynamics/test/trajectory/state/test_coordinate_subset.py +46 -0
  89. ostk/astrodynamics/test/trajectory/state/test_numerical_solver.py +314 -0
  90. ostk/astrodynamics/test/trajectory/test_local_orbital_frame_direction.py +81 -0
  91. ostk/astrodynamics/test/trajectory/test_local_orbital_frame_factory.py +108 -0
  92. ostk/astrodynamics/test/trajectory/test_model.py +1 -0
  93. ostk/astrodynamics/test/trajectory/test_orbit.py +196 -0
  94. ostk/astrodynamics/test/trajectory/test_propagator.py +458 -0
  95. ostk/astrodynamics/test/trajectory/test_segment.py +305 -0
  96. ostk/astrodynamics/test/trajectory/test_sequence.py +477 -0
  97. ostk/astrodynamics/test/trajectory/test_state.py +375 -0
  98. ostk/astrodynamics/test/trajectory/test_state_builder.py +171 -0
  99. ostk/astrodynamics/utilities.py +245 -0
  100. ostk/astrodynamics/viewer.py +392 -0
@@ -0,0 +1,245 @@
1
+ # Apache License 2.0
2
+
3
+ from datetime import datetime
4
+ from datetime import timezone
5
+
6
+ from .OpenSpaceToolkitAstrodynamicsPy import *
7
+
8
+ from ostk.physics import Environment
9
+ from ostk.physics.time import Scale
10
+ from ostk.physics.time import Instant
11
+ from ostk.physics.time import Duration
12
+ from ostk.physics.time import Interval
13
+ from ostk.physics.unit import Length
14
+ from ostk.physics.coordinate.spherical import LLA
15
+ from ostk.physics.coordinate.spherical import AER
16
+ from ostk.physics.coordinate import Position
17
+ from ostk.physics.coordinate import Frame
18
+ from ostk.physics.environment.object.celestial import Earth
19
+ from ostk.physics.environment.gravitational import Earth as EarthGravitationalModel
20
+
21
+
22
+ def lla_from_state(state: trajectory.State) -> LLA:
23
+ """
24
+ Return latitude (degrees), longitude (degrees), altitude (meters) float list from a state.
25
+
26
+ Args:
27
+ state (trajectory.State): A state.
28
+
29
+ Returns:
30
+ LLA: The LLA.
31
+ """
32
+
33
+ return lla_from_position(state.get_position(), state.get_instant())
34
+
35
+
36
+ def lla_from_position(
37
+ position: Position,
38
+ instant: Instant | None = None,
39
+ ) -> LLA:
40
+ """
41
+ Return LLA from position and instant.
42
+
43
+ Args:
44
+ position (Position): A position.
45
+ instant (Instant): An instant.
46
+
47
+ Returns:
48
+ LLA: The LLA.
49
+ """
50
+
51
+ if position.access_frame() != Frame.ITRF():
52
+ if instant is None:
53
+ raise ValueError(
54
+ "Instant must be provided if position is not expressed in ECEF."
55
+ )
56
+ position: Position = position.in_frame(Frame.ITRF(), instant)
57
+
58
+ return LLA.cartesian(
59
+ position.get_coordinates(),
60
+ EarthGravitationalModel.EGM2008.equatorial_radius,
61
+ EarthGravitationalModel.EGM2008.flattening,
62
+ )
63
+
64
+
65
+ def position_from_lla(lla: LLA) -> Position:
66
+ """
67
+ Return position from lla.
68
+
69
+ Args:
70
+ lla (LLA): A LLA.
71
+
72
+ Returns:
73
+ Position: The position.
74
+ """
75
+
76
+ return Position.meters(
77
+ lla.to_cartesian(
78
+ EarthGravitationalModel.EGM2008.equatorial_radius,
79
+ EarthGravitationalModel.EGM2008.flattening,
80
+ ),
81
+ Frame.ITRF(),
82
+ )
83
+
84
+
85
+ def compute_aer(
86
+ instant: Instant,
87
+ from_position: Position,
88
+ to_position: Position,
89
+ environment: Environment,
90
+ ) -> AER:
91
+ """
92
+ Return AER from Instant and Positions (observer, target).
93
+
94
+ Args:
95
+ instant (Instant): An instant.
96
+ from_position (Position): An observer position.
97
+ to_position (Position): A target position.
98
+ environment (Environment): An environment.
99
+
100
+ Returns:
101
+ AER: The AER.
102
+ """
103
+
104
+ from_lla: LLA = lla_from_position(from_position, instant)
105
+
106
+ earth: Earth = environment.access_celestial_object_with_name("Earth")
107
+ ned_frame: Frame = earth.get_frame_at(from_lla, Earth.FrameType.NED)
108
+
109
+ from_position_NED: Position = from_position.in_frame(ned_frame, instant)
110
+ to_position_NED: Position = to_position.in_frame(ned_frame, instant)
111
+
112
+ return AER.from_position_to_position(from_position_NED, to_position_NED, True)
113
+
114
+
115
+ def compute_time_lla_aer_coordinates(
116
+ state: trajectory.State,
117
+ from_position: Position,
118
+ environment: Environment,
119
+ ) -> tuple[datetime, float, float, float, float, float, float]:
120
+ """
121
+ Return [datetime, latitude, longitude, altitude, azimuth, elevation, range] from State and observer Position.
122
+
123
+ Args:
124
+ state (trajectory.State): A state.
125
+ from_position (Position): An observer position.
126
+ environment (Environment): An environment.
127
+
128
+ Returns:
129
+ tuple[datetime, float, float, float, float, float, float]: The [datetime, latitude, longitude, altitude, azimuth, elevation, range].
130
+ """
131
+
132
+ instant: Instant = state.get_instant()
133
+
134
+ lla: LLA = lla_from_state(state)
135
+
136
+ aer: AER = compute_aer(
137
+ instant,
138
+ from_position,
139
+ state.get_position().in_frame(Frame.ITRF(), state.get_instant()),
140
+ environment,
141
+ )
142
+
143
+ return (
144
+ instant.get_date_time(Scale.UTC).replace(tzinfo=timezone.utc),
145
+ float(lla.get_latitude().in_degrees()),
146
+ float(lla.get_longitude().in_degrees()),
147
+ float(lla.get_altitude().in_meters()),
148
+ float(aer.get_azimuth().in_degrees()),
149
+ float(aer.get_elevation().in_degrees()),
150
+ float(aer.get_range().in_meters()),
151
+ )
152
+
153
+
154
+ def compute_trajectory_geometry(
155
+ trajectory: Trajectory,
156
+ interval: Interval,
157
+ step: Duration = Duration.minutes(1.0),
158
+ ) -> list[LLA]:
159
+ """
160
+ Return list of LLA along a Trajectory during the Interval at the provided step.
161
+
162
+ Args:
163
+ trajectory (Trajectory): A trajectory.
164
+ interval (Interval): An interval.
165
+ step (Duration): A step. Defaults to 1.0 minutes.
166
+
167
+ Returns:
168
+ list[LLA]: The list of LLA.
169
+ """
170
+
171
+ return [
172
+ lla_from_state(state)
173
+ for state in trajectory.get_states_at(interval.generate_grid(step))
174
+ ]
175
+
176
+
177
+ def compute_ground_track(
178
+ trajectory: Trajectory,
179
+ interval: Interval,
180
+ step: Duration = Duration.minutes(1.0),
181
+ ) -> list[LLA]:
182
+ """
183
+ Return list of LLA along a Trajectory ground track (altitude set to 10.0 meters) during the Interval at the provided step.
184
+
185
+ Args:
186
+ trajectory (Trajectory): A trajectory.
187
+ interval (Interval): An interval.
188
+ step (Duration): A step. Defaults to 1.0 minutes.
189
+
190
+ Returns:
191
+ list[LLA]: The list of LLA.
192
+ """
193
+
194
+ ground_llas: list[LLA] = []
195
+ for state in trajectory.get_states_at(interval.generate_grid(step)):
196
+ lla: LLA = lla_from_state(state)
197
+
198
+ ground_lla: LLA = LLA(
199
+ lla.get_latitude(),
200
+ lla.get_longitude(),
201
+ Length.meters(10.0),
202
+ )
203
+
204
+ ground_llas.append(ground_lla)
205
+
206
+ return ground_llas
207
+
208
+
209
+ def convert_state(
210
+ state: trajectory.State,
211
+ ) -> tuple[str, float, float, float, float, float, float, float, float, float]:
212
+ """
213
+ Convert a State into dataframe-ready values.
214
+ - Instant (str)
215
+ - Modified Julian Date (float)
216
+ - Position X [meters] (float)
217
+ - Position Y [meters] (float)
218
+ - Position Z [meters] (float)
219
+ - Velocity X [meters/second] (float)
220
+ - Velocity Y [meters/second] (float)
221
+ - Velocity Z [meters/second] (float)
222
+ - Latitude [degrees] (float)
223
+ - Longitude [degrees] (float)
224
+ - Altitude [meters] (float)
225
+
226
+ Args:
227
+ state (trajectory.State): A state.
228
+
229
+ Returns:
230
+ tuple[str, float, float, float, float, float, float, float, float, float]: The dataframe-ready values.
231
+ """
232
+
233
+ lla: LLA = lla_from_state(state)
234
+
235
+ instant: Instant = state.get_instant()
236
+
237
+ return (
238
+ str(instant.to_string()),
239
+ float(instant.get_modified_julian_date(Scale.UTC)),
240
+ *state.get_position().get_coordinates().transpose().tolist(),
241
+ *state.get_velocity().get_coordinates().transpose().tolist(),
242
+ float(lla.get_latitude().in_degrees()),
243
+ float(lla.get_longitude().in_degrees()),
244
+ float(lla.get_altitude().in_meters()),
245
+ )
@@ -0,0 +1,392 @@
1
+ # Apache License 2.0
2
+
3
+ from __future__ import annotations
4
+
5
+ import functools
6
+ import operator
7
+ from dataclasses import dataclass
8
+
9
+ import numpy as np
10
+
11
+ try:
12
+ import cesiumpy
13
+
14
+ except ImportError:
15
+ ...
16
+
17
+ from ostk.mathematics.geometry.d3.transformation.rotation import Quaternion
18
+
19
+ from ostk.physics.unit import Length
20
+ from ostk.physics.unit import Angle
21
+ from ostk.physics.time import Instant, Interval, Duration
22
+ from ostk.physics.coordinate import Position
23
+ from ostk.physics.coordinate import Frame
24
+ from ostk.physics.coordinate.spherical import LLA
25
+
26
+ from ostk.astrodynamics.flight import Profile
27
+ from ostk.astrodynamics.trajectory import State
28
+
29
+ from .converters import coerce_to_datetime
30
+ from .utilities import lla_from_position
31
+
32
+
33
+ @dataclass
34
+ class Sensor:
35
+ name: str
36
+ direction: tuple[float, float, float] | np.ndarray
37
+ color: str
38
+
39
+
40
+ @dataclass
41
+ class ConicSensor(Sensor):
42
+ half_angle: Angle
43
+ length: Length
44
+
45
+
46
+ @dataclass
47
+ class RectangularSensor(Sensor):
48
+ x_half_angle: Angle
49
+ y_half_angle: Angle
50
+ radius: Length
51
+
52
+
53
+ class Viewer:
54
+ def __init__(
55
+ self,
56
+ interval: Interval,
57
+ cesium_token: str | None = None,
58
+ width: str = "1500px",
59
+ height: str = "800px",
60
+ ) -> None:
61
+ self._interval: Interval = interval
62
+
63
+ self._viewer: cesiumpy.Viewer = cesiumpy.Viewer(
64
+ width=width,
65
+ height=height,
66
+ clock_view_model=cesiumpy.ClockViewModel(
67
+ clock=cesiumpy.Clock(
68
+ start_time=coerce_to_datetime(interval.get_start()),
69
+ stop_time=coerce_to_datetime(interval.get_end()),
70
+ clock_range=cesiumpy.Clock.Range.CLAMPED,
71
+ can_animate=True,
72
+ should_animate=False,
73
+ )
74
+ ),
75
+ fullscreen_button=False,
76
+ home_button=False,
77
+ info_box=False,
78
+ timeline=True,
79
+ navigation_help_button=False,
80
+ navigation_instructions_initially_visible=False,
81
+ scene_mode_picker=False,
82
+ selection_indicator=False,
83
+ scene3d_only=True,
84
+ zoom_to_entity=True,
85
+ track_entity=True,
86
+ default_access_token=cesium_token,
87
+ )
88
+
89
+ @property
90
+ def interval(self) -> Interval:
91
+ return self._interval
92
+
93
+ def add_profile(
94
+ self,
95
+ profile: Profile,
96
+ step: Duration,
97
+ show_orbital_track: bool = False,
98
+ cesium_asset_id: int | None = None,
99
+ sensors: list[Sensor] | None = None,
100
+ show_xyz_axes: bool = False,
101
+ ) -> None:
102
+ """
103
+ Add Profile to Viewer.
104
+
105
+ Args:
106
+ profile (Profile): Profile to be added.
107
+ step (Duration): Step between two consecutive states.
108
+ show_orbital_track (bool, optional): Whether to show the orbital track. Defaults to False.
109
+ cesium_asset_id (int, optional): The Cesium asset ID. Defaults to None.
110
+ sensors (list[Sensor], optional): Sensors to be added to the asset. Defaults to None.
111
+ show_xyz_axes (bool, optional): Whether to show the XYZ axes. Defaults to False.
112
+ """
113
+
114
+ instants: list[Instant] = self._interval.generate_grid(step)
115
+ states: list[State] = profile.get_states_at(instants)
116
+ llas: list[LLA] = _generate_llas(states)
117
+
118
+ sensors = sensors or []
119
+ if show_xyz_axes:
120
+ sensors.extend(
121
+ [
122
+ ConicSensor(
123
+ name="x_axis",
124
+ direction=(+1.0, 0.0, 0.0),
125
+ half_angle=Angle.degrees(1.0),
126
+ length=Length.meters(2.0),
127
+ color="red",
128
+ ),
129
+ ConicSensor(
130
+ name="y_axis",
131
+ direction=(0.0, +1.0, 0.0),
132
+ half_angle=Angle.degrees(1.0),
133
+ length=Length.meters(2.0),
134
+ color="blue",
135
+ ),
136
+ ConicSensor(
137
+ name="z_axis",
138
+ direction=(0.0, 0.0, +1.0),
139
+ half_angle=Angle.degrees(1.0),
140
+ length=Length.meters(2.0),
141
+ color="green",
142
+ ),
143
+ ]
144
+ )
145
+
146
+ satellite = cesiumpy.Satellite(
147
+ position=_generate_sampled_position(instants, llas),
148
+ orientation=_generate_sampled_orientation(states),
149
+ availability=cesiumpy.TimeIntervalCollection(
150
+ intervals=[
151
+ cesiumpy.TimeInterval(
152
+ start=coerce_to_datetime(self._interval.get_start()),
153
+ stop=coerce_to_datetime(self._interval.get_end()),
154
+ ),
155
+ ],
156
+ ),
157
+ model=cesiumpy.IonResource(
158
+ asset_id=cesium_asset_id or 0
159
+ ), # TBM: Should be made more robust
160
+ sensors=[_cesium_from_ostk_sensor(sensor) for sensor in sensors or []],
161
+ )
162
+
163
+ satellite.render(self._viewer)
164
+
165
+ if show_orbital_track:
166
+ self._viewer.entities.add(
167
+ cesiumpy.Polyline(
168
+ positions=cesiumpy.entities.cartesian.Cartesian3Array(
169
+ functools.reduce(
170
+ operator.iconcat,
171
+ [
172
+ [
173
+ float(lla.get_longitude().in_degrees()),
174
+ float(lla.get_latitude().in_degrees()),
175
+ float(lla.get_altitude().in_meters()),
176
+ ]
177
+ for lla in _generate_llas(states)
178
+ ],
179
+ [],
180
+ )
181
+ ),
182
+ width=1,
183
+ )
184
+ )
185
+
186
+ def add_target(
187
+ self,
188
+ position: Position,
189
+ size: int | None = None,
190
+ color: str | None = None,
191
+ ) -> None:
192
+ """
193
+ Add target to Viewer.
194
+
195
+ Args:
196
+ position (Position): Target position.
197
+ size (int, optional): Target size. Defaults to None.
198
+ color (str, optional): Target color. Defaults to None.
199
+ """
200
+
201
+ self._viewer.entities.add(
202
+ cesiumpy.Point(
203
+ position=_cesium_from_ostk_position(position),
204
+ pixel_size=size or 10,
205
+ color=color or cesiumpy.color.YELLOW,
206
+ )
207
+ )
208
+
209
+ def add_line(
210
+ self,
211
+ positions: list[Position],
212
+ size: int | None = None,
213
+ color: str | None = None,
214
+ ) -> None:
215
+ """
216
+ Add line to Viewer.
217
+
218
+ Args:
219
+ positions (list[Position]): Line positions.
220
+ size (int, optional): Line size. Defaults to None.
221
+ color (str, optional): Line color. Defaults to None.
222
+ """
223
+
224
+ self._viewer.entities.add(
225
+ cesiumpy.Polyline(
226
+ positions=cesiumpy.entities.cartesian.Cartesian3Array(
227
+ functools.reduce(
228
+ operator.iconcat,
229
+ [
230
+ [
231
+ float(lla.get_longitude().in_degrees()),
232
+ float(lla.get_latitude().in_degrees()),
233
+ 10.0,
234
+ ]
235
+ for lla in map(lla_from_position, positions)
236
+ ],
237
+ [],
238
+ )
239
+ ),
240
+ width=size or 1,
241
+ material=color or cesiumpy.color.YELLOW,
242
+ )
243
+ )
244
+
245
+ def render(self) -> str:
246
+ """
247
+ Render Viewer as HTML string.
248
+
249
+ Returns:
250
+ str: Rendered HTML string.
251
+ """
252
+
253
+ return self._viewer.to_html()
254
+
255
+ def _repr_html_(self) -> str:
256
+ return self.render()
257
+
258
+
259
+ def _generate_llas(states: list[State]) -> list[LLA]:
260
+ return [
261
+ lla_from_position(state.get_position(), state.get_instant()) for state in states
262
+ ]
263
+
264
+
265
+ def _generate_sampled_position(
266
+ instants: list[Instant],
267
+ llas: list[LLA],
268
+ ) -> cesiumpy.SampledPositionProperty:
269
+ """
270
+ Generate a sampled position property from a list of OSTk LLAs.
271
+
272
+ Args:
273
+ states (list[LLA]): A list of OSTk LLAs.
274
+
275
+ Returns:
276
+ cesiumpy.SampledPositionProperty: Sampled position property.
277
+ """
278
+
279
+ return cesiumpy.SampledPositionProperty(
280
+ samples=[
281
+ (
282
+ coerce_to_datetime(instant),
283
+ _cesium_from_ostk_lla(lla),
284
+ None,
285
+ )
286
+ for instant, lla in zip(instants, llas)
287
+ ],
288
+ )
289
+
290
+
291
+ def _generate_sampled_orientation(states: list[State]) -> cesiumpy.SampledProperty:
292
+ """
293
+ Generate a sampled orientation property from a list of OSTk States.
294
+
295
+ Args:
296
+ states (list[State]): A list of OSTk States.
297
+
298
+ Returns:
299
+ cesiumpy.SampledProperty: Sampled orientation property.
300
+ """
301
+
302
+ return cesiumpy.SampledProperty(
303
+ type=cesiumpy.Quaternion,
304
+ samples=[
305
+ (
306
+ coerce_to_datetime(state.get_instant()),
307
+ _cesium_from_ostk_quaternion(state.in_frame(Frame.ITRF()).get_attitude()),
308
+ None,
309
+ )
310
+ for state in states
311
+ ],
312
+ )
313
+
314
+
315
+ def _cesium_from_ostk_position(
316
+ position: Position,
317
+ instant: Instant | None = None,
318
+ ) -> cesiumpy.Cartesian3:
319
+ """
320
+ Convert OSTk Position into Cesium Cartesian3.
321
+
322
+ Args:
323
+ position (Position): Position to convert.
324
+ instant (Instant, optional): Instant at which the position is valid (required if position is not expressed in ECEF).
325
+
326
+ Returns:
327
+ cesiumpy.Cartesian3: Converted position.
328
+ """
329
+
330
+ return _cesium_from_ostk_lla(lla_from_position(position, instant))
331
+
332
+
333
+ def _cesium_from_ostk_lla(
334
+ lla: LLA,
335
+ ) -> cesiumpy.Cartesian3:
336
+ """
337
+ Convert OSTk LLA into Cesium Cartesian3.
338
+
339
+ Args:
340
+ lla (LLA): LLA to convert.
341
+
342
+ Returns:
343
+ cesiumpy.Cartesian3: Converted LLA.
344
+ """
345
+
346
+ return cesiumpy.Cartesian3.fromDegrees(
347
+ float(lla.get_longitude().in_degrees()),
348
+ float(lla.get_latitude().in_degrees()),
349
+ float(lla.get_altitude().in_meters()),
350
+ )
351
+
352
+
353
+ def _cesium_from_ostk_quaternion(quaternion: Quaternion) -> cesiumpy.Quaternion:
354
+ """
355
+ Convert OSTk Quaternion into Cesium Quaternion.
356
+
357
+ Args:
358
+ quaternion (Quaternion): Quaternion to convert.
359
+
360
+ Returns:
361
+ cesiumpy.Quaternion: Converted quaternion.
362
+ """
363
+
364
+ return cesiumpy.Quaternion(
365
+ float(quaternion.x()),
366
+ float(quaternion.y()),
367
+ float(quaternion.z()),
368
+ float(quaternion.s()),
369
+ )
370
+
371
+
372
+ def _cesium_from_ostk_sensor(sensor: Sensor) -> cesiumpy.Sensor:
373
+ if isinstance(sensor, ConicSensor):
374
+ return cesiumpy.ConicSensor(
375
+ name=sensor.name,
376
+ direction=cesiumpy.Cartesian3(*sensor.direction),
377
+ half_angle=float(sensor.half_angle.in_radians()),
378
+ length=float(sensor.length.in_meters()),
379
+ material=sensor.color or cesiumpy.color.RED,
380
+ )
381
+
382
+ elif isinstance(sensor, RectangularSensor):
383
+ return cesiumpy.RectangularSensor(
384
+ name=sensor.name,
385
+ direction=cesiumpy.Cartesian3(*sensor.direction),
386
+ x_half_angle=float(sensor.x_half_angle.in_radians()),
387
+ y_half_angle=float(sensor.y_half_angle.in_radians()),
388
+ radius=float(sensor.radius.in_meters()),
389
+ material=sensor.color or cesiumpy.color.ORANGE,
390
+ )
391
+
392
+ raise NotImplementedError("{sensor.__name__} is not supported yet.")