Coverage for src/gncpy/plotting.py: 55%

44 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-07-19 05:48 +0000

1"""Defines utility functions for plotting routines.""" 

2import numpy as np 

3from numpy.linalg import eigh 

4import numpy.random as rnd 

5import matplotlib.pyplot as plt 

6 

7 

8def calc_error_ellipse(cov, n_sig): 

9 """Calculates parameters for an error ellipse. 

10 

11 This calucates the error ellipse for a given sigma 

12 number according to :cite:`Hoover1984_AlgorithmsforConfidenceCirclesandEllipses`. 

13 

14 Parameters 

15 ---------- 

16 cov : 2 x 2 numpy array 

17 covariance matrix. 

18 n_sig : float 

19 Sigma number, must be positive. 

20 

21 Returns 

22 ------- 

23 width : float 

24 The width of the ellipse 

25 height :float 

26 The height of the ellipse 

27 angle : float 

28 The rotation angle in degrees of the semi-major axis. Measured up from 

29 the positive x-axis. 

30 """ 

31 # get and sort eigne values 

32 vals, vecs = eigh(cov) 

33 order = vals.argsort()[::-1] 

34 vals = vals[order] 

35 vecs = vecs[:, order] 

36 

37 # find rotation angle from positive x-axis, and width/height 

38 angle = 180 / np.pi * np.arctan2(*vecs[:, 0][::-1]) 

39 width, height = 2 * n_sig * np.sqrt(vals) 

40 

41 return 2 * width, 2 * height, angle 

42 

43 

44def init_plotting_opts( 

45 f_hndl=None, 

46 lgnd_loc=None, 

47 sig_bnd=1, 

48 time_vec=None, 

49 true_states=None, 

50 rng=rnd.default_rng(1), 

51 meas_inds=None, 

52 marker="o", 

53 ttl_fontsize=12, 

54 ttl_fontstyle="normal", 

55 ttl_fontfamily="sans-serif", 

56 ax_fontsize=10, 

57 ax_fontstyle="normal", 

58 ax_fontfamily="sans-serif", 

59): 

60 """Processes common plotting options in a common interface. 

61 

62 Parameters 

63 ---------- 

64 f_hndl : matplotlib figure, optional 

65 Current to figure to plot on. Pass None to create a new figure. The 

66 default is None. 

67 lgnd_loc : string, optional 

68 Location of the legend. Set to none to skip creating a legend. The 

69 default is None. 

70 sig_bnd : int, optional 

71 If set and the covariances are saved, the sigma bounds are scaled by 

72 this number and plotted for each track. The default is 1. 

73 time_vec : list, optional 

74 List of time values. The default is None. 

75 true_states : list, optional 

76 Each element is a N x 1 numpy array representing the true state. 

77 If not given true states are not plotted. The default is None. 

78 rng : numpy random generator, optional 

79 For generating random numbers. The default is rnd.default_rng(1). 

80 meas_inds : list, optional 

81 List of indices in the measurement vector to plot if this is specified 

82 all available measurements will be plotted. Note, x-axis is first, then 

83 y-axis. Also note, if gating is on then gated measurements will not be 

84 plotted. The default is None. 

85 marker : string, optional 

86 Shape to use as a marker, can be any valid value used by matplotlib. 

87 The default is 'o'. 

88 ttl_fontsize : int, optional 

89 Title font size. The default is 12. 

90 ttl_fontstyle : string, optional 

91 Matplotlib font style for the title. The default is 'normal'. 

92 ttl_fontfamily : string, optional 

93 Matplotlib font family for the title. The default is 'sans-serif'. 

94 ax_fontsize : int, optional 

95 Axis label font size. The default is 10. 

96 ax_fontstyle : string, optional 

97 Matplotlib font style for the axis label. The default is 'normal'. 

98 ax_fontfamily : string, optional 

99 Matplotlib font family for the axis label. The default is 'sans-serif'. 

100 

101 Returns 

102 ------- 

103 opts : dict 

104 Plotting options with default values where custom ones were not specified. 

105 """ 

106 opts = {} 

107 

108 opts["f_hndl"] = f_hndl 

109 opts["lgnd_loc"] = lgnd_loc 

110 

111 opts["sig_bnd"] = sig_bnd 

112 opts["time_vec"] = time_vec 

113 opts["true_states"] = true_states 

114 opts["rng"] = rng 

115 opts["meas_inds"] = meas_inds 

116 

117 opts["marker"] = marker 

118 

119 opts["ttl_fontsize"] = ttl_fontsize 

120 opts["ttl_fontstyle"] = ttl_fontstyle 

121 opts["ttl_fontfamily"] = ttl_fontfamily 

122 

123 opts["ax_fontsize"] = ax_fontsize 

124 opts["ax_fontstyle"] = ax_fontstyle 

125 opts["ax_fontfamily"] = ax_fontfamily 

126 

127 return opts 

128 

129 

130def set_title_label( 

131 fig, ax_num, opts, ttl=None, x_lbl=None, y_lbl=None, z_lbl=None, use_local=False 

132): 

133 """Sets the figure/window title, and axis labels with the given options. 

134 

135 Parameters 

136 ---------- 

137 fig : matplot figure object 

138 Current figure to edit. 

139 ax_num : int 

140 Index into the axes object to modify. 

141 opts : dict 

142 Standard dictionary from :func:`.plotting.init_plotting_opts`. 

143 ttl : string, optional 

144 Title string to use. This is set to the proper size, family, and 

145 style. It also becomes the window title. The default is None. 

146 x_lbl : string, optional 

147 Label for the x-axis. This is set to the proper size, family, and 

148 style. The default is None. 

149 y_lbl : string, optional 

150 Label for the y-axis. This is set to the proper size, family, and 

151 style. The default is None. 

152 z_lbl : string, optional 

153 Label for the z-axis. This is set to the proper size, family, and 

154 style. The default is None. 

155 use_local : bool, optional 

156 Flag indicating if the local title or suptitle should be set. The 

157 default is False. 

158 

159 Returns 

160 ------- 

161 None. 

162 """ 

163 if ttl is not None: 

164 if use_local: 

165 fig.axes[ax_num].set_title( 

166 ttl, 

167 fontsize=opts["ttl_fontsize"], 

168 fontstyle=opts["ttl_fontstyle"], 

169 fontfamily=opts["ttl_fontfamily"], 

170 ) 

171 else: 

172 fig.suptitle( 

173 ttl, 

174 fontsize=opts["ttl_fontsize"], 

175 fontstyle=opts["ttl_fontstyle"], 

176 fontfamily=opts["ttl_fontfamily"], 

177 ) 

178 if fig.canvas.manager is not None: 

179 fig.canvas.manager.set_window_title(ttl) 

180 

181 if x_lbl is not None: 

182 fig.axes[ax_num].set_xlabel( 

183 x_lbl, 

184 fontsize=opts["ax_fontsize"], 

185 fontstyle=opts["ax_fontstyle"], 

186 fontfamily=opts["ax_fontfamily"], 

187 ) 

188 if y_lbl is not None: 

189 fig.axes[ax_num].set_ylabel( 

190 y_lbl, 

191 fontsize=opts["ax_fontsize"], 

192 fontstyle=opts["ax_fontstyle"], 

193 fontfamily=opts["ax_fontfamily"], 

194 ) 

195 if z_lbl is not None: 

196 fig.axes[ax_num].set_zlabel( 

197 z_lbl, 

198 fontsize=opts["ax_fontsize"], 

199 fontstyle=opts["ax_fontstyle"], 

200 fontfamily=opts["ax_fontfamily"], 

201 ) 

202 

203 

204def get_cmap(n, name="Dark2"): 

205 """Returns a function that generates a color map. 

206 

207 Returns a function thata maps each index in 0, 1, ..., n-1 to a distinct 

208 RGB color; the keyword argument name must be a standard mpl colormap name. 

209 

210 Parameters 

211 ---------- 

212 n : int 

213 Number of colors in the map. 

214 name : string 

215 name of the colormap, valid for `pyplot.cm.get_cmap` function 

216 """ 

217 return plt.cm.get_cmap(name, n)