class OrbitPlotter3D(_BaseOrbitPlotter): """OrbitPlotter3D class. """ def __init__(self): super().__init__() self._layout = Layout( autosize=True, scene=dict( xaxis=dict( title="x (km)", ), yaxis=dict( title="y (km)", ), zaxis=dict( title="z (km)", ), aspectmode="data", # Important! ), ) def _plot_sphere(self, radius, color, name, center=[0, 0, 0] * u.km): xx, yy, zz = _generate_sphere(radius, center) sphere = Surface( x=xx.to(u.km).value, y=yy.to(u.km).value, z=zz.to(u.km).value, name=name, colorscale=[[0, color], [1, color]], cauto=False, cmin=1, cmax=1, showscale=False, # Boilerplate ) return sphere @u.quantity_input(elev=u.rad, azim=u.rad, distance=u.km) def set_view(self, elev, azim, distance=5 * u.km): x = distance * np.cos(elev) * np.cos(azim) y = distance * np.cos(elev) * np.sin(azim) z = distance * np.sin(elev) self._layout.update({ "scene": { "camera": { "eye": { "x": x.to(u.km).value, "y": y.to(u.km).value, "z": z.to(u.km).value } } } }) def _plot_trajectory(self, trajectory, label, color, dashed): trace = Scatter3d( x=trajectory.x.to(u.km).value, y=trajectory.y.to(u.km).value, z=trajectory.z.to(u.km).value, name=label, line=dict( color=color, width=5, dash='dash' if dashed else 'solid', ), mode="lines", # Boilerplate ) self._data.append(trace)
class OrbitPlotter2D(_BaseOrbitPlotter): """OrbitPlotter2D class. .. versionadded:: 0.9.0 """ def __init__(self): super().__init__() self._layout = Layout( autosize=True, xaxis=dict( title="x (km)", constrain="domain", ), yaxis=dict( title="y (km)", scaleanchor="x", ), ) self._layout.update({ "shapes": [] }) def _plot_sphere(self, radius, color, name, center=[0, 0, 0] * u.km): xx, yy = _generate_circle(radius, center) x_center, y_center, z_center = center trace = Scatter(x=xx.to(u.km).value, y=yy.to(u.km).value, mode='markers', line=dict(color=color, width=5, dash='dash',), name=name) self._layout["shapes"] += ( { 'type': 'circle', 'xref': 'x', 'yref': 'y', 'x0': (x_center - radius).to(u.km).value, 'y0': (y_center - radius).to(u.km).value, 'x1': (x_center + radius).to(u.km).value, 'y1': (y_center + radius).to(u.km).value, 'opacity': 1, 'fillcolor': color, 'line': { 'color': color, }, }, ) return trace def _plot_trajectory(self, trajectory, label, color, dashed): trace = Scatter( x=trajectory.x.to(u.km).value, y=trajectory.y.to(u.km).value, name=label, line=dict( color=color, width=2, dash='dash' if dashed else 'solid', ), mode="lines", # Boilerplate ) self._data.append(trace)
class OrbitPlotter3D(BaseOrbitPlotter): """OrbitPlotter3D class. """ def __init__(self, figure=None, dark=False): super().__init__(figure) self._layout = Layout( autosize=True, scene=dict( xaxis=dict(title="x (km)"), yaxis=dict(title="y (km)"), zaxis=dict(title="z (km)"), aspectmode="data", # Important! ), ) if dark: self._layout.template = "plotly_dark" def _plot_point(self, radius, color, name, center=[0, 0, 0] * u.km): # We use _plot_sphere here because it's not easy to specify the size of a marker # in data units instead of pixels, see # https://stackoverflow.com/q/47086547 return self._plot_sphere(radius, color, name, center) def _plot_sphere(self, radius, color, name, center=[0, 0, 0] * u.km): xx, yy, zz = generate_sphere(radius, center) sphere = Surface( x=xx.to(u.km).value, y=yy.to(u.km).value, z=zz.to(u.km).value, name=name, colorscale=[[0, color], [1, color]], cauto=False, cmin=1, cmax=1, showscale=False, ) self._figure.add_trace(sphere) return sphere def _plot_trajectory(self, trajectory, label, color, dashed): trace = Scatter3d( x=trajectory.x.to(u.km).value, y=trajectory.y.to(u.km).value, z=trajectory.z.to(u.km).value, name=label, line=dict(color=color, width=5, dash="dash" if dashed else "solid"), mode="lines", # Boilerplate ) self._figure.add_trace(trace) return trace @u.quantity_input(elev=u.rad, azim=u.rad, distance=u.km) def set_view(self, elev, azim, distance=5 * u.km): x = distance * np.cos(elev) * np.cos(azim) y = distance * np.cos(elev) * np.sin(azim) z = distance * np.sin(elev) self._layout.update( { "scene": { "camera": { "eye": { "x": x.to(u.km).value, "y": y.to(u.km).value, "z": z.to(u.km).value, } } } } ) if not self._figure._in_batch_mode: return self.show()
class OrbitPlotter3D: """OrbitPlotter3D class. """ def __init__(self): self._layout = Layout( autosize=True, scene=dict( xaxis=dict(title="x (km)", ), yaxis=dict(title="y (km)", ), zaxis=dict(title="z (km)", ), aspectmode="data", # Important! ), ) self._data = [] # type: List[dict] # TODO: Refactor? self._attractor = None self._attractor_data = {} # type: dict self._attractor_radius = np.inf * u.km self._color_cycle = cycle(plotly.colors.DEFAULT_PLOTLY_COLORS) @property def figure(self): return dict( data=self._data + [self._attractor_data], layout=self._layout, ) def _redraw_attractor(self, min_radius=0 * u.km): # Select a sensible value for the radius: realistic for low orbits, # visible for high and very high orbits radius = max(self._attractor.R.to(u.km), min_radius.to(u.km)) # If the resulting radius is smaller than the current one, redraw it if radius < self._attractor_radius: sphere = _plot_sphere( radius, BODY_COLORS.get(self._attractor.name, "#999999"), self._attractor.name) # Overwrite stored properties self._attractor_radius = radius self._attractor_data = sphere def _plot_trajectory(self, trajectory, label, color, dashed): trace = Scatter3d( x=trajectory.x.to(u.km).value, y=trajectory.y.to(u.km).value, z=trajectory.z.to(u.km).value, name=label, line=dict( color=color, width=5, dash='dash' if dashed else 'solid', ), mode="lines", # Boilerplate ) self._data.append(trace) def set_attractor(self, attractor): """Sets plotting attractor. Parameters ---------- attractor : ~poliastro.bodies.Body Central body. """ if self._attractor is None: self._attractor = attractor elif attractor is not self._attractor: raise NotImplementedError( "Attractor has already been set to {}.".format( self._attractor.name)) @u.quantity_input(elev=u.rad, azim=u.rad) def set_view(self, elev, azim, distance=5): x = distance * np.cos(elev) * np.cos(azim) y = distance * np.cos(elev) * np.sin(azim) z = distance * np.sin(elev) self._layout.update({ "scene": { "camera": { "eye": { "x": x.to(u.km).value, "y": y.to(u.km).value, "z": z.to(u.km).value } } } }) def plot(self, orbit, *, label=None, color=None): """Plots state and osculating orbit in their plane. """ if color is None: color = next(self._color_cycle) self.set_attractor(orbit.attractor) self._redraw_attractor(orbit.r_p * 0.15) # Arbitrary threshold label = _generate_label(orbit, label) trajectory = orbit.sample() self._plot_trajectory(trajectory, label, color, True) # Plot sphere in the position of the body radius = min(self._attractor_radius * 0.5, (norm(orbit.r) - orbit.attractor.R) * 0.3) # Arbitrary thresholds sphere = _plot_sphere(radius, color, label, center=orbit.r) self._data.append(sphere) def plot_trajectory(self, trajectory, *, label=None, color=None): """Plots a precomputed trajectory. An attractor must be set first. Parameters ---------- trajectory : ~astropy.coordinates.CartesianRepresentation Trajectory to plot. """ if self._attractor is None: raise ValueError("An attractor must be set up first, please use " "set_attractor(Major_Body).") else: self._redraw_attractor(trajectory.norm().min() * 0.15) # Arbitrary threshold self._plot_trajectory(trajectory, str(label), color, False) def _prepare_plot(self, **layout_kwargs): # If there are no orbits, draw only the attractor if not self._data: self._redraw_attractor() if layout_kwargs: self._layout.update(layout_kwargs) def show(self, **layout_kwargs): self._prepare_plot(**layout_kwargs) plot(self.figure) def savefig(self, filename, **layout_kwargs): self._prepare_plot(**layout_kwargs) basename, ext = os.path.splitext(filename) export( self.figure, image=ext[1:], image_filename=basename, show_link=False, # Boilerplate )
class PlotlyGraph: def __init__(self, graph_title: object, random_colors: object = False, y_type: object = None) -> object: self.random_colors = random_colors self.default_colors = chain([ 'rgb(47, 117, 181)', 'rgb(84, 130, 53)', 'rgb(198, 89, 17)', 'rgb(51, 63, 79)', 'rgb(110, 65, 143)', 'rgb(165, 27, 27)' ]) # TODO possible crash if there are more then 6 scatters self.data = [] self.layout = Layout(title=graph_title, separators=', ') # self.layout = Layout(title=graph_title, separators=', ', width=1000, height=900) # TODO: resolution for PNG # self.layout.update(titlefont=dict(size=36)) # self.layout.update(width=900, height=500) # self.layout.update(font=dict(family='Courier New, monospace', size=25, color='#7f7f7f')) # self.layout() self.layout.update(xaxis=dict(title='x_axis', showline=True, showticklabels=True, ticks='outside', separatethousands=True)) if y_type == 'log': self.layout.update(yaxis=dict(title='y_axis', showline=True, anchor='x', side='left', showticklabels=True, ticks='outside', separatethousands=True, exponentformat='none', type='log', autorange=True)) else: self.layout.update(yaxis=dict(title='y_axis', showline=True, anchor='x', side='left', showticklabels=True, ticks='outside', separatethousands=True, exponentformat='none')) self.layout.update(yaxis2=dict(title='y2_axis')) self.layout.update( legend=dict(orientation='h', xanchor='center', y=-0.3)) self.shapes = [] self.max_x = 0 self.max_y = 0 self.max_y2 = 0 @staticmethod def _moving_average(interval, window_size): window = numpy.ones(int(window_size)) / float(window_size) return numpy.convolve(interval, window, 'same') @staticmethod def _random_color(): return 'rgb({r}, {g}, {b})'.format(r=randint(0, 255), g=randint(0, 255), b=randint(0, 255)) def append_data(self, name: str, x: list, y: list = [], y2: bool = False, sma: bool = False, sma_interval: int = 5, line_color: list = {}, line_type: str = 'line'): if line_color == {}: if self.random_colors: line_color = self._random_color() else: line_color = next(self.default_colors) if line_type == 'line': if len(x) is 0 or len(y) is 0: raise ValueError('"x" or "y" must not be empty') if len(x) != len(y): raise ValueError('"x" and "y" must be the same length') self.max_x = max([self.max_x, max(x)]) if y2: self.max_y2 = max( [self.max_y2, max(n for n in y if n is not None)]) self.layout.update( yaxis2=dict(title=self.layout['yaxis2']['title'], showline=True, anchor='x', overlaying='y', side='right', showticklabels=True, ticks='outside', showgrid=False, separatethousands=True, range=[0, self.max_y2])) else: self.max_y = max( [self.max_y, max(n for n in y if n is not None)]) self.layout.update(yaxis=dict(range=[0, self.max_y])) scatter_params = dict() scatter_params['x'] = x scatter_params['y'] = y if sma: scatter_params['opacity'] = 0.5 scatter_params['line'] = plotly.graph_objs.scatter.Line( width=2, color=line_color) scatter_params['name'] = name if y2: scatter_params['yaxis'] = 'y2' self.data.append(Scatter(**scatter_params)) if sma: # filtering None values sma_x = [ x[offset] for offset, value in enumerate(y) if value is not None ] sma_y = [value for value in y if value is not None] ma = self._moving_average(sma_y, sma_interval) ma_scatter_params = dict() ma_scatter_params['x'] = sma_x[sma_interval:1 - sma_interval] ma_scatter_params['y'] = ma[sma_interval:1 - sma_interval] ma_scatter_params['line'] = plotly.graph_objs.scatter.Line( width=2, color=line_color) ma_scatter_params['name'] = name + ' (sma)' if y2: ma_scatter_params['yaxis'] = 'y2' self.data.append(Scatter(**ma_scatter_params)) elif line_type == 'histogramm': scatter_params = dict() scatter_params['x'] = x if len(y) > 0: scatter_params['y'] = y scatter_params['histfunc'] = 'sum' scatter_params['name'] = name scatter_params['type'] = 'histogram' scatter_params['marker'] = dict(color=line_color) self.data.append(scatter_params) def config_axes(self, x_sign: str = False, y_sign: str = False, y2_sign: str = False, x_max=False, y_max=False, y2_max=False): self.sign_axes(x_sign=x_sign, y_sign=y_sign, y2_sign=y2_sign) if x_max: self.layout.update(xaxis=dict(range=[0, x_max])) if y_max: self.layout.update(yaxis=dict(range=[0, y_max])) if y2_max: self.layout.update(yaxis2=dict(range=[0, y2_max])) def sign_axes(self, x_sign: str = False, y_sign: str = False, y2_sign: str = False): if x_sign: self.layout.update(xaxis=dict(title=x_sign)) if y_sign: self.layout.update(yaxis=dict(title=y_sign)) if y2_sign: self.layout.update(yaxis2=dict(title=y2_sign)) # to-do: исправить следующее поведение # add_vertical_line/add_horizontal_line в случае использивания имен # должны вызываться после добавления всех линий на график # в противном случае они будут идти не через весь график def add_vertical_line(self, x, color='rgb(55, 128, 191)', width=3, name=None): if name != None: scatter_params = dict() scatter_params['x'] = [x, x] scatter_params['y'] = [0, self.max_y] scatter_params['line'] = Line(width=width, color=color) scatter_params['name'] = name self.data.append(Scatter(**scatter_params)) else: self.shapes.append( dict(type='line', x0=x, y0=0, x1=x, y1=None, line=dict(color=color, width=width))) def add_horizontal_line(self, y, color='rgb(55, 128, 191)', width=3, name=None): if name != None: scatter_params = dict() scatter_params['x'] = [0, self.max_x] scatter_params['y'] = [y, y] scatter_params['line'] = Line(width=width, color=color) scatter_params['name'] = name self.data.append(Scatter(**scatter_params)) else: self.shapes.append( dict(type='line', x0=0, y0=y, x1=None, y1=y, line=dict(color=color, width=width))) def add_redline(self, x): self.add_vertical_line(x=x, color='red', width=3) def plot(self, file_name): if len(self.data) == 0: print('No data to plot') return 1 if len(self.shapes) > 0: for shape in self.shapes: if shape['x1'] is None: shape['x1'] = self.max_x elif shape['y1'] is None: shape['y1'] = self.max_y self.layout.update(shapes=self.shapes) plotly.offline.plot( dict(layout=self.layout, data=self.data), filename=file_name, auto_open=False, # image='png', image_filename='io', image_width=700, image_height=450, show_link=False)
class OrbitPlotter3D(BaseOrbitPlotter): """OrbitPlotter3D class. """ def __init__(self, figure=None, dark=False): super().__init__(figure) self._layout = Layout( autosize=True, scene=dict( xaxis=dict(title="x (km)"), yaxis=dict(title="y (km)"), zaxis=dict(title="z (km)"), aspectmode="data", # Important! ), ) if dark: self._layout.template = "plotly_dark" def _plot_point(self, radius, color, name, center=[0, 0, 0] * u.km): # We use _plot_sphere here because it's not easy to specify the size of a marker # in data units instead of pixels, see # https://stackoverflow.com/q/47086547 return self._plot_sphere(radius, color, name, center) def _plot_sphere(self, radius, color, name, center=[0, 0, 0] * u.km): xx, yy, zz = generate_sphere(radius, center) sphere = Surface( x=xx.to(u.km).value, y=yy.to(u.km).value, z=zz.to(u.km).value, name=name, colorscale=[[0, color], [1, color]], cauto=False, cmin=1, cmax=1, showscale=False, ) self._figure.add_trace(sphere) return sphere def _plot_trajectory(self, trajectory, label, color, dashed): trace = Scatter3d( x=trajectory.x.to(u.km).value, y=trajectory.y.to(u.km).value, z=trajectory.z.to(u.km).value, name=label, line=dict(color=color, width=5, dash="dash" if dashed else "solid"), mode="lines", # Boilerplate ) self._figure.add_trace(trace) return trace @u.quantity_input(elev=u.rad, azim=u.rad, distance=u.km) def set_view(self, elev, azim, distance=5 * u.km): x = distance * np.cos(elev) * np.cos(azim) y = distance * np.cos(elev) * np.sin(azim) z = distance * np.sin(elev) self._layout.update({ "scene": { "camera": { "eye": { "x": x.to(u.km).value, "y": y.to(u.km).value, "z": z.to(u.km).value, } } } }) return self.show()
class PlotlyGraph: def __init__(self, graph_title, random_colors: bool = False): self.random_colors = random_colors self.default_colors = chain([ 'rgb(47, 117, 181)', 'rgb(84, 130, 53)', 'rgb(198, 89, 17)', 'rgb(51, 63, 79)', 'rgb(110, 65, 143)', 'rgb(165, 27, 27)' ]) self.data = [] self.layout = Layout(title=graph_title, separators=', ') # self.layout = Layout(title=graph_title, separators=', ', width=1000, height=900) # TODO: resolution for PNG # self.layout.update(titlefont=dict(size=36)) # self.layout.update(width=900, height=500) # self.layout.update(font=dict(family='Courier New, monospace', size=25, color='#7f7f7f')) # self.layout() self.layout.update(xaxis=dict(title='x_axis', showline=True, showticklabels=True, ticks='outside', separatethousands=True)) self.layout.update(yaxis=dict(title='y_axis', showline=True, anchor='x', side='left', showticklabels=True, ticks='outside', separatethousands=True, exponentformat='none')) self.layout.update( legend=dict(orientation='h', xanchor='middle', y=-0.3)) self.shapes = [] self.max_x = 0 self.max_y = 0 self.max_y2 = 0 @staticmethod def _moving_average(interval, window_size): window = numpy.ones(int(window_size)) / float(window_size) return numpy.convolve(interval, window, 'same') @staticmethod def _random_color(): return 'rgb({r}, {g}, {b})'.format(r=randint(0, 255), g=randint(0, 255), b=randint(0, 255)) def append_data(self, name: str, x: list, y: list, y2: bool = False, sma: bool = False, sma_interval: int = 5): if len(x) is 0 or len(y) is 0: raise ValueError('"x" or "y" must not be empty') if len(x) != len(y): raise ValueError('"x" and "y" must be the same length') if self.random_colors: line_color = self._random_color() else: line_color = next(self.default_colors) self.max_x = max([self.max_x, max(x)]) if y2: self.max_y2 = max( [self.max_y2, max(n for n in y if n is not None)]) self.layout.update(yaxis2=dict(title='y2_axis', showline=True, anchor='x', overlaying='y', side='right', showticklabels=True, ticks='outside', showgrid=False, separatethousands=True, range=[0, self.max_y2])) else: self.max_y = max([self.max_y, max(n for n in y if n is not None)]) self.layout.update(yaxis=dict(range=[0, self.max_y])) scatter_params = dict() scatter_params['x'] = x scatter_params['y'] = y if sma: scatter_params['opacity'] = '0.5' scatter_params['line'] = Line(width=2, color=line_color) scatter_params['name'] = name if y2: scatter_params['yaxis'] = 'y2' self.data.append(Scatter(**scatter_params)) if sma: # filtering None values sma_x = [ x[offset] for offset, value in enumerate(y) if value is not None ] sma_y = [value for value in y if value is not None] ma = self._moving_average(sma_y, sma_interval) ma_scatter_params = dict() ma_scatter_params['x'] = sma_x[sma_interval:1 - sma_interval] ma_scatter_params['y'] = ma[sma_interval:1 - sma_interval] ma_scatter_params['line'] = Line(width=2, color=line_color) ma_scatter_params['name'] = name + ' (sma)' if y2: ma_scatter_params['yaxis'] = 'y2' self.data.append(Scatter(**ma_scatter_params)) def config_axes(self, x_sign: str = False, y_sign: str = False, y2_sign: str = False, x_max=False, y_max=False, y2_max=False): self.sign_axes(x_sign=x_sign, y_sign=y_sign, y2_sign=y2_sign) if x_max: self.layout.update(xaxis=dict(range=[0, x_max])) if y_max: self.layout.update(yaxis=dict(range=[0, y_max])) if y2_max: self.layout.update(yaxis2=dict(range=[0, y2_max])) def sign_axes(self, x_sign: str = False, y_sign: str = False, y2_sign: str = False): if x_sign: self.layout.update(xaxis=dict(title=x_sign)) if y_sign: self.layout.update(yaxis=dict(title=y_sign)) if y2_sign: self.layout.update(yaxis2=dict(title=y2_sign)) def add_vertical_line(self, x, color='rgb(55, 128, 191)', width=3): self.shapes.append( dict(type='line', x0=x, y0=0, x1=x, y1=None, line=dict(color=color, width=width))) def add_horizontal_line(self, y, color='rgb(55, 128, 191)', width=3): self.shapes.append( dict(type='line', x0=0, y0=y, x1=None, y1=y, line=dict(color=color, width=width))) def add_redline(self, x): self.add_vertical_line(x=x, color='red', width=3) def plot(self, file_name): if len(self.data) == 0: print('No data to plot') return 1 if len(self.shapes) > 0: for shape in self.shapes: if shape['x1'] is None: shape['x1'] = self.max_x elif shape['y1'] is None: shape['y1'] = self.max_y self.layout.update(shapes=self.shapes) plotly.offline.plot( dict(layout=self.layout, data=self.data), filename=file_name, auto_open=False, # image='png', image_filename='io', image_width=700, image_height=450, show_link=False)
class OrbitPlotter2D: """OrbitPlotter2D class. .. versionadded:: 0.9.0 Experimental alternative to :py:class:`OrbitPlotter` that uses Plotly instead of matplotlib. Some visualization issues pending, use with care. """ def __init__(self): self._layout = Layout( autosize=True, xaxis=dict( title="x (km)", constrain="domain", ), yaxis=dict( title="y (km)", scaleanchor="x", ), ) self._layout.update({ "shapes": [] }) self._data = [] # type: List[dict] self._attractor = None self._attractor_data = {} # type: dict self._attractor_radius = np.inf * u.km self._color_cycle = cycle(plotly.colors.DEFAULT_PLOTLY_COLORS) @property def figure(self): return dict( data=self._data + [self._attractor_data], layout=self._layout, ) def _plot_circle(self, radius, color, name, center=[0, 0, 0] * u.km): xx, yy = _generate_circle(radius, center) x_center, y_center, z_center = center trace = Scatter(x=xx.to(u.km).value, y=yy.to(u.km).value, mode='markers', line=dict(color=color, width=5, dash='dash',), name=name) self._layout["shapes"].append( { 'type': 'circle', 'xref': 'x', 'yref': 'y', 'x0': (x_center - radius).to(u.km).value, 'y0': (y_center - radius).to(u.km).value, 'x1': (x_center + radius).to(u.km).value, 'y1': (y_center + radius).to(u.km).value, 'opacity': 1, 'fillcolor': color, 'line': { 'color': color, }, }, ) return trace def _redraw_attractor(self, min_radius=0 * u.km): # Select a sensible value for the radius: realistic for low orbits, # visible for high and very high orbits radius = max(self._attractor.R.to(u.km), min_radius.to(u.km)) # If the resulting radius is smaller than the current one, redraw it if radius < self._attractor_radius: circle = self._plot_circle( radius, BODY_COLORS.get(self._attractor.name, "#999999"), self._attractor.name) # Overwrite stored properties self._attractor_radius = radius self._attractor_data = circle def _plot_trajectory(self, trajectory, label, color, dashed): trace = Scatter( x=trajectory.x.to(u.km).value, y=trajectory.y.to(u.km).value, name=label, line=dict( color=color, width=2, dash='dash' if dashed else 'solid', ), mode="lines", # Boilerplate ) self._data.append(trace) def set_attractor(self, attractor): """Sets plotting attractor. Parameters ---------- attractor : ~poliastro.bodies.Body Central body. """ if self._attractor is None: self._attractor = attractor elif attractor is not self._attractor: raise NotImplementedError("Attractor has already been set to {}.".format(self._attractor.name)) def plot(self, orbit, *, label=None, color=None): """Plots state and osculating orbit in their plane. Parameters ---------- orbit : ~poliastro.twobody.orbit.Orbit label : string, optional color : string, optional """ if color is None: color = next(self._color_cycle) self.set_attractor(orbit.attractor) self._redraw_attractor(orbit.r_p * 0.15) # Arbitrary threshold label = _generate_label(orbit, label) _, trajectory = orbit.sample() self._plot_trajectory(trajectory, label, color, True) # Plot sphere in the position of the body radius = min(self._attractor_radius * 0.5, (norm(orbit.r) - orbit.attractor.R) * 0.3) # Arbitrary thresholds circle = self._plot_circle( radius, color, label, center=orbit.r) self._data.append(circle) def plot_trajectory(self, trajectory, *, label=None, color=None): """Plots a precomputed trajectory. An attractor must be set first. Parameters ---------- trajectory : ~astropy.coordinates.CartesianRepresentation Trajectory to plot. label : string, optional color : string, optional """ if self._attractor is None: raise ValueError("An attractor must be set up first, please use " "set_attractor(Major_Body).") else: self._redraw_attractor(trajectory.norm().min() * 0.15) # Arbitrary threshold self._plot_trajectory(trajectory, str(label), color, False) def _prepare_plot(self, **layout_kwargs): # If there are no orbits, draw only the attractor if not self._data: self._redraw_attractor() if layout_kwargs: self._layout.update(layout_kwargs) def show(self, **layout_kwargs): """Shows the plot in the Notebook. """ self._prepare_plot(**layout_kwargs) iplot(self.figure) def savefig(self, filename, **layout_kwargs): """Function for saving the plot locally. Parameters ---------- filename : string, format = "anyname.anyformat" anyformat can only be jpeg, png, svg, webp """ self._prepare_plot(**layout_kwargs) basename, ext = os.path.splitext(filename) export( self.figure, image=ext[1:], image_filename=basename, show_link=False, # Boilerplate )
# Finally, we create the plotly graph by creating a separate subplot for each channel # In[15]: step = 1. / n_channels kwargs = dict(domain=[1 - step, 1], showticklabels=False, zeroline=False, showgrid=False) # create objects for layout and traces layout = Layout(yaxis=YAxis(kwargs), showlegend=False) traces = [Scatter(x=times, y=data.T[:, 0])] # loop over the channels for ii in range(1, n_channels): kwargs.update(domain=[1 - (ii + 1) * step, 1 - ii * step]) layout.update({'yaxis%d' % (ii + 1): YAxis(kwargs), 'showlegend': False}) traces.append(Scatter(x=times, y=data.T[:, ii], yaxis='y%d' % (ii + 1))) # add channel names using Annotations annotations = Annotations([Annotation(x=-0.06, y=0, xref='paper', yref='y%d' % (ii + 1), text=ch_name, font=Font(size=9), showarrow=False) for ii, ch_name in enumerate(ch_names)]) layout.update(annotations=annotations) # set the size of the figure and plot it layout.update(autosize=False, width=1000, height=600) fig = Figure(data=Data(traces), layout=layout) py.iplot(fig, filename='shared xaxis') # We can look at the list of bad channels from the ``info`` dictionary