Coverage for pyrc \ tools \ plotting.py: 25%

59 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-13 16:59 +0200

1# ------------------------------------------------------------------------------- 

2# Copyright (C) 2026 Joel Kimmich, Tim Jourdan 

3# ------------------------------------------------------------------------------ 

4# License 

5# This file is part of PyRC, distributed under GPL-3.0-or-later. 

6# ------------------------------------------------------------------------------ 

7 

8from datetime import timedelta, datetime 

9 

10import matplotlib.dates as mdates 

11from mpl_toolkits.mplot3d.proj3d import proj_transform 

12from mpl_toolkits.mplot3d.axes3d import Axes3D 

13import numpy as np 

14from matplotlib.patches import FancyArrowPatch 

15 

16 

17class Arrow3D(FancyArrowPatch): 

18 def __init__(self, start, end, *args, **kwargs): 

19 super().__init__((0, 0), (0, 0), *args, **kwargs) 

20 self._start = tuple(start) 

21 self._end = tuple(end) 

22 

23 def make_arrow(self): 

24 x1, y1, z1 = self._start 

25 x2, y2, z2 = self._end 

26 

27 xs, ys, zs = proj_transform((x1, x2), (y1, y2), (z1, z2), self.axes.M) 

28 self.set_positions((xs[0], ys[0]), (xs[1], ys[1])) 

29 return zs 

30 

31 def draw(self, renderer): 

32 self.make_arrow() 

33 super().draw(renderer) 

34 

35 def do_3d_projection(self, renderer=None): 

36 zs = self.make_arrow() 

37 return np.min(zs) 

38 

39 

40def custom_numeric_ticks_formatter(x, pos): 

41 """ 

42 Format numbers/ticks to match ISO 80000. 

43 

44 Parameters 

45 ---------- 

46 x : float | int 

47 The number to format. 

48 pos 

49 

50 Returns 

51 ------- 

52 str : 

53 The formatted number with a short protected space as thousands separator and a "," as decimal. 

54 """ 

55 if abs(x) >= 1000: 

56 s = f"{x:,.0f}".replace(",", "\u202f").replace(".", ",") 

57 return s 

58 else: 

59 return f"{x:g}".replace(".", ",") 

60 

61 

62def format_date_x_axis(start_date: datetime, end_date: datetime, ax, return_version: bool = True) -> str | None: 

63 """ 

64 Formats the ticks of an axis as date. 

65 

66 Depending on the date range it parses the time/date to days, weeks, months and years with matching minor ticks. 

67 

68 Parameters 

69 ---------- 

70 start_date : datetime 

71 The start date of the axis. 

72 end_date : datetime 

73 The end date of the axis. 

74 ax : matplotlib.axis.Axis 

75 The axis to format. 

76 return_version : bool 

77 Whether to return the version of the axis or not. 

78 

79 Returns 

80 ------- 

81 str | None : 

82 The formatted Axis if return_version is True, otherwise None. 

83 """ 

84 time_delta = end_date - start_date 

85 if time_delta <= timedelta(days=3): 

86 version = "day" 

87 ax.xaxis.set_major_locator(mdates.HourLocator(byhour=(0, 6, 12, 18))) 

88 ax.xaxis.set_minor_locator(mdates.HourLocator()) 

89 ax.xaxis.set_major_formatter(mdates.DateFormatter("%d.%m. %-H Uhr")) 

90 elif time_delta <= timedelta(days=10): 

91 version = "week" 

92 ax.xaxis.set_major_locator(mdates.DayLocator()) 

93 ax.xaxis.set_minor_locator(mdates.HourLocator(byhour=(0, 6, 12, 18))) 

94 ax.xaxis.set_major_formatter(mdates.DateFormatter("%d.%m.")) 

95 elif time_delta <= timedelta(days=80): 

96 version = "month" 

97 ax.xaxis.set_major_locator(mdates.DayLocator(bymonthday=(1, 7, 15, 23))) 

98 ax.xaxis.set_minor_locator(mdates.DayLocator()) 

99 ax.xaxis.set_major_formatter(mdates.DateFormatter("%d.%m.")) 

100 else: 

101 version = "year" 

102 ax.xaxis.set_major_locator(mdates.MonthLocator()) 

103 ax.xaxis.set_minor_locator(mdates.DayLocator(bymonthday=(1, 7, 14, 21, 28))) 

104 ax.xaxis.set_major_formatter(mdates.DateFormatter("%-m.")) 

105 ax.grid(True) 

106 ax.tick_params(axis="x", which="minor", bottom=True) 

107 ax.set_xlim(left=start_date) 

108 ax.set_xlim(right=end_date) 

109 if return_version: 

110 return version 

111 

112 

113def _arrow3D(ax, start, end, *args, **kwargs): 

114 """Add an 3d arrow to an `Axes3D` instance.""" 

115 

116 arrow = Arrow3D(start, end, *args, **kwargs) 

117 ax.add_artist(arrow) 

118 

119 

120setattr(Axes3D, "arrow3D", _arrow3D) 

121 

122# example usage 

123# fig = plt.figure() 

124# ax = fig.add_subplot(111, projection='3d') 

125# ax.set_xlim(0,2) 

126# ax.arrow3D((0,0,0), 

127# (1,1,1), 

128# mutation_scale=10, 

129# arrowstyle="-|>", 

130# linestyle="-") 

131# ax.arrow3D((1,0,0), 

132# (2,1,1), 

133# mutation_scale=20, 

134# ec ="green", 

135# fc="red") 

136# ax.set_title('3D Arrows Demo') 

137# ax.set_xlabel('x') 

138# ax.set_ylabel('y') 

139# ax.set_zlabel('z') 

140# fig.tight_layout() 

141# plt.show()