test_quiver.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. import platform
  2. import sys
  3. import numpy as np
  4. import pytest
  5. from matplotlib import pyplot as plt
  6. from matplotlib.testing.decorators import image_comparison
  7. from matplotlib.testing.decorators import check_figures_equal
  8. def draw_quiver(ax, **kwargs):
  9. X, Y = np.meshgrid(np.arange(0, 2 * np.pi, 1),
  10. np.arange(0, 2 * np.pi, 1))
  11. U = np.cos(X)
  12. V = np.sin(Y)
  13. Q = ax.quiver(U, V, **kwargs)
  14. return Q
  15. @pytest.mark.skipif(platform.python_implementation() != 'CPython',
  16. reason='Requires CPython')
  17. def test_quiver_memory_leak():
  18. fig, ax = plt.subplots()
  19. Q = draw_quiver(ax)
  20. ttX = Q.X
  21. orig_refcount = sys.getrefcount(ttX)
  22. Q.remove()
  23. del Q
  24. assert sys.getrefcount(ttX) < orig_refcount
  25. @pytest.mark.skipif(platform.python_implementation() != 'CPython',
  26. reason='Requires CPython')
  27. def test_quiver_key_memory_leak():
  28. fig, ax = plt.subplots()
  29. Q = draw_quiver(ax)
  30. qk = ax.quiverkey(Q, 0.5, 0.92, 2, r'$2 \frac{m}{s}$',
  31. labelpos='W',
  32. fontproperties={'weight': 'bold'})
  33. orig_refcount = sys.getrefcount(qk)
  34. qk.remove()
  35. assert sys.getrefcount(qk) < orig_refcount
  36. def test_quiver_number_of_args():
  37. X = [1, 2]
  38. with pytest.raises(
  39. TypeError,
  40. match='takes from 2 to 5 positional arguments but 1 were given'):
  41. plt.quiver(X)
  42. with pytest.raises(
  43. TypeError,
  44. match='takes from 2 to 5 positional arguments but 6 were given'):
  45. plt.quiver(X, X, X, X, X, X)
  46. def test_quiver_arg_sizes():
  47. X2 = [1, 2]
  48. X3 = [1, 2, 3]
  49. with pytest.raises(
  50. ValueError, match=('X and Y must be the same size, but '
  51. 'X.size is 2 and Y.size is 3.')):
  52. plt.quiver(X2, X3, X2, X2)
  53. with pytest.raises(
  54. ValueError, match=('Argument U has a size 3 which does not match '
  55. '2, the number of arrow positions')):
  56. plt.quiver(X2, X2, X3, X2)
  57. with pytest.raises(
  58. ValueError, match=('Argument V has a size 3 which does not match '
  59. '2, the number of arrow positions')):
  60. plt.quiver(X2, X2, X2, X3)
  61. with pytest.raises(
  62. ValueError, match=('Argument C has a size 3 which does not match '
  63. '2, the number of arrow positions')):
  64. plt.quiver(X2, X2, X2, X2, X3)
  65. def test_no_warnings():
  66. fig, ax = plt.subplots()
  67. X, Y = np.meshgrid(np.arange(15), np.arange(10))
  68. U = V = np.ones_like(X)
  69. phi = (np.random.rand(15, 10) - .5) * 150
  70. ax.quiver(X, Y, U, V, angles=phi)
  71. fig.canvas.draw() # Check that no warning is emitted.
  72. def test_zero_headlength():
  73. # Based on report by Doug McNeil:
  74. # https://discourse.matplotlib.org/t/quiver-warnings/16722
  75. fig, ax = plt.subplots()
  76. X, Y = np.meshgrid(np.arange(10), np.arange(10))
  77. U, V = np.cos(X), np.sin(Y)
  78. ax.quiver(U, V, headlength=0, headaxislength=0)
  79. fig.canvas.draw() # Check that no warning is emitted.
  80. @image_comparison(['quiver_animated_test_image.png'])
  81. def test_quiver_animate():
  82. # Tests fix for #2616
  83. fig, ax = plt.subplots()
  84. Q = draw_quiver(ax, animated=True)
  85. ax.quiverkey(Q, 0.5, 0.92, 2, r'$2 \frac{m}{s}$',
  86. labelpos='W', fontproperties={'weight': 'bold'})
  87. @image_comparison(['quiver_with_key_test_image.png'])
  88. def test_quiver_with_key():
  89. fig, ax = plt.subplots()
  90. ax.margins(0.1)
  91. Q = draw_quiver(ax)
  92. ax.quiverkey(Q, 0.5, 0.95, 2,
  93. r'$2\, \mathrm{m}\, \mathrm{s}^{-1}$',
  94. angle=-10,
  95. coordinates='figure',
  96. labelpos='W',
  97. fontproperties={'weight': 'bold', 'size': 'large'})
  98. @image_comparison(['quiver_single_test_image.png'], remove_text=True)
  99. def test_quiver_single():
  100. fig, ax = plt.subplots()
  101. ax.margins(0.1)
  102. ax.quiver([1], [1], [2], [2])
  103. def test_quiver_copy():
  104. fig, ax = plt.subplots()
  105. uv = dict(u=np.array([1.1]), v=np.array([2.0]))
  106. q0 = ax.quiver([1], [1], uv['u'], uv['v'])
  107. uv['v'][0] = 0
  108. assert q0.V[0] == 2.0
  109. @image_comparison(['quiver_key_pivot.png'], remove_text=True)
  110. def test_quiver_key_pivot():
  111. fig, ax = plt.subplots()
  112. u, v = np.mgrid[0:2*np.pi:10j, 0:2*np.pi:10j]
  113. q = ax.quiver(np.sin(u), np.cos(v))
  114. ax.set_xlim(-2, 11)
  115. ax.set_ylim(-2, 11)
  116. ax.quiverkey(q, 0.5, 1, 1, 'N', labelpos='N')
  117. ax.quiverkey(q, 1, 0.5, 1, 'E', labelpos='E')
  118. ax.quiverkey(q, 0.5, 0, 1, 'S', labelpos='S')
  119. ax.quiverkey(q, 0, 0.5, 1, 'W', labelpos='W')
  120. @image_comparison(['quiver_key_xy.png'], remove_text=True)
  121. def test_quiver_key_xy():
  122. # With scale_units='xy', ensure quiverkey still matches its quiver.
  123. # Note that the quiver and quiverkey lengths depend on the axes aspect
  124. # ratio, and that with angles='xy' their angles also depend on the axes
  125. # aspect ratio.
  126. X = np.arange(8)
  127. Y = np.zeros(8)
  128. angles = X * (np.pi / 4)
  129. uv = np.exp(1j * angles)
  130. U = uv.real
  131. V = uv.imag
  132. fig, axs = plt.subplots(2)
  133. for ax, angle_str in zip(axs, ('uv', 'xy')):
  134. ax.set_xlim(-1, 8)
  135. ax.set_ylim(-0.2, 0.2)
  136. q = ax.quiver(X, Y, U, V, pivot='middle',
  137. units='xy', width=0.05,
  138. scale=2, scale_units='xy',
  139. angles=angle_str)
  140. for x, angle in zip((0.2, 0.5, 0.8), (0, 45, 90)):
  141. ax.quiverkey(q, X=x, Y=0.8, U=1, angle=angle, label='', color='b')
  142. @image_comparison(['barbs_test_image.png'], remove_text=True)
  143. def test_barbs():
  144. x = np.linspace(-5, 5, 5)
  145. X, Y = np.meshgrid(x, x)
  146. U, V = 12*X, 12*Y
  147. fig, ax = plt.subplots()
  148. ax.barbs(X, Y, U, V, np.hypot(U, V), fill_empty=True, rounding=False,
  149. sizes=dict(emptybarb=0.25, spacing=0.2, height=0.3),
  150. cmap='viridis')
  151. @image_comparison(['barbs_pivot_test_image.png'], remove_text=True)
  152. def test_barbs_pivot():
  153. x = np.linspace(-5, 5, 5)
  154. X, Y = np.meshgrid(x, x)
  155. U, V = 12*X, 12*Y
  156. fig, ax = plt.subplots()
  157. ax.barbs(X, Y, U, V, fill_empty=True, rounding=False, pivot=1.7,
  158. sizes=dict(emptybarb=0.25, spacing=0.2, height=0.3))
  159. ax.scatter(X, Y, s=49, c='black')
  160. @image_comparison(['barbs_test_flip.png'], remove_text=True)
  161. def test_barbs_flip():
  162. """Test barbs with an array for flip_barb."""
  163. x = np.linspace(-5, 5, 5)
  164. X, Y = np.meshgrid(x, x)
  165. U, V = 12*X, 12*Y
  166. fig, ax = plt.subplots()
  167. ax.barbs(X, Y, U, V, fill_empty=True, rounding=False, pivot=1.7,
  168. sizes=dict(emptybarb=0.25, spacing=0.2, height=0.3),
  169. flip_barb=Y < 0)
  170. def test_barb_copy():
  171. fig, ax = plt.subplots()
  172. u = np.array([1.1])
  173. v = np.array([2.2])
  174. b0 = ax.barbs([1], [1], u, v)
  175. u[0] = 0
  176. assert b0.u[0] == 1.1
  177. v[0] = 0
  178. assert b0.v[0] == 2.2
  179. def test_bad_masked_sizes():
  180. """Test error handling when given differing sized masked arrays."""
  181. x = np.arange(3)
  182. y = np.arange(3)
  183. u = np.ma.array(15. * np.ones((4,)))
  184. v = np.ma.array(15. * np.ones_like(u))
  185. u[1] = np.ma.masked
  186. v[1] = np.ma.masked
  187. fig, ax = plt.subplots()
  188. with pytest.raises(ValueError):
  189. ax.barbs(x, y, u, v)
  190. def test_angles_and_scale():
  191. # angles array + scale_units kwarg
  192. fig, ax = plt.subplots()
  193. X, Y = np.meshgrid(np.arange(15), np.arange(10))
  194. U = V = np.ones_like(X)
  195. phi = (np.random.rand(15, 10) - .5) * 150
  196. ax.quiver(X, Y, U, V, angles=phi, scale_units='xy')
  197. @image_comparison(['quiver_xy.png'], remove_text=True)
  198. def test_quiver_xy():
  199. # simple arrow pointing from SW to NE
  200. fig, ax = plt.subplots(subplot_kw=dict(aspect='equal'))
  201. ax.quiver(0, 0, 1, 1, angles='xy', scale_units='xy', scale=1)
  202. ax.set_xlim(0, 1.1)
  203. ax.set_ylim(0, 1.1)
  204. ax.grid()
  205. def test_quiverkey_angles():
  206. # Check that only a single arrow is plotted for a quiverkey when an array
  207. # of angles is given to the original quiver plot
  208. fig, ax = plt.subplots()
  209. X, Y = np.meshgrid(np.arange(2), np.arange(2))
  210. U = V = angles = np.ones_like(X)
  211. q = ax.quiver(X, Y, U, V, angles=angles)
  212. qk = ax.quiverkey(q, 1, 1, 2, 'Label')
  213. # The arrows are only created when the key is drawn
  214. fig.canvas.draw()
  215. assert len(qk.verts) == 1
  216. def test_quiverkey_angles_xy_aitoff():
  217. # GH 26316 and GH 26748
  218. # Test that only one arrow will be plotted with non-cartesian
  219. # when angles='xy' and/or scale_units='xy'
  220. # only for test purpose
  221. # scale_units='xy' may not be a valid use case for non-cartesian
  222. kwargs_list = [
  223. {'angles': 'xy'},
  224. {'angles': 'xy', 'scale_units': 'xy'},
  225. {'scale_units': 'xy'}
  226. ]
  227. for kwargs_dict in kwargs_list:
  228. x = np.linspace(-np.pi, np.pi, 11)
  229. y = np.ones_like(x) * np.pi / 6
  230. vx = np.zeros_like(x)
  231. vy = np.ones_like(x)
  232. fig = plt.figure()
  233. ax = fig.add_subplot(projection='aitoff')
  234. q = ax.quiver(x, y, vx, vy, **kwargs_dict)
  235. qk = ax.quiverkey(q, 0, 0, 1, '1 units')
  236. fig.canvas.draw()
  237. assert len(qk.verts) == 1
  238. def test_quiverkey_angles_scale_units_cartesian():
  239. # GH 26316
  240. # Test that only one arrow will be plotted with normal cartesian
  241. # when angles='xy' and/or scale_units='xy'
  242. kwargs_list = [
  243. {'angles': 'xy'},
  244. {'angles': 'xy', 'scale_units': 'xy'},
  245. {'scale_units': 'xy'}
  246. ]
  247. for kwargs_dict in kwargs_list:
  248. X = [0, -1, 0]
  249. Y = [0, -1, 0]
  250. U = [1, -1, 1]
  251. V = [1, -1, 0]
  252. fig, ax = plt.subplots()
  253. q = ax.quiver(X, Y, U, V, **kwargs_dict)
  254. ax.quiverkey(q, X=0.3, Y=1.1, U=1,
  255. label='Quiver key, length = 1', labelpos='E')
  256. qk = ax.quiverkey(q, 0, 0, 1, '1 units')
  257. fig.canvas.draw()
  258. assert len(qk.verts) == 1
  259. def test_quiver_setuvc_numbers():
  260. """Check that it is possible to set all arrow UVC to the same numbers"""
  261. fig, ax = plt.subplots()
  262. X, Y = np.meshgrid(np.arange(2), np.arange(2))
  263. U = V = np.ones_like(X)
  264. q = ax.quiver(X, Y, U, V)
  265. q.set_UVC(0, 1)
  266. def draw_quiverkey_zorder_argument(fig, zorder=None):
  267. """Draw Quiver and QuiverKey using zorder argument"""
  268. x = np.arange(1, 6, 1)
  269. y = np.arange(1, 6, 1)
  270. X, Y = np.meshgrid(x, y)
  271. U, V = 2, 2
  272. ax = fig.subplots()
  273. q = ax.quiver(X, Y, U, V, pivot='middle')
  274. ax.set_xlim(0.5, 5.5)
  275. ax.set_ylim(0.5, 5.5)
  276. if zorder is None:
  277. ax.quiverkey(q, 4, 4, 25, coordinates='data',
  278. label='U', color='blue')
  279. ax.quiverkey(q, 5.5, 2, 20, coordinates='data',
  280. label='V', color='blue', angle=90)
  281. else:
  282. ax.quiverkey(q, 4, 4, 25, coordinates='data',
  283. label='U', color='blue', zorder=zorder)
  284. ax.quiverkey(q, 5.5, 2, 20, coordinates='data',
  285. label='V', color='blue', angle=90, zorder=zorder)
  286. def draw_quiverkey_setzorder(fig, zorder=None):
  287. """Draw Quiver and QuiverKey using set_zorder"""
  288. x = np.arange(1, 6, 1)
  289. y = np.arange(1, 6, 1)
  290. X, Y = np.meshgrid(x, y)
  291. U, V = 2, 2
  292. ax = fig.subplots()
  293. q = ax.quiver(X, Y, U, V, pivot='middle')
  294. ax.set_xlim(0.5, 5.5)
  295. ax.set_ylim(0.5, 5.5)
  296. qk1 = ax.quiverkey(q, 4, 4, 25, coordinates='data',
  297. label='U', color='blue')
  298. qk2 = ax.quiverkey(q, 5.5, 2, 20, coordinates='data',
  299. label='V', color='blue', angle=90)
  300. if zorder is not None:
  301. qk1.set_zorder(zorder)
  302. qk2.set_zorder(zorder)
  303. @pytest.mark.parametrize('zorder', [0, 2, 5, None])
  304. @check_figures_equal(extensions=['png'])
  305. def test_quiverkey_zorder(fig_test, fig_ref, zorder):
  306. draw_quiverkey_zorder_argument(fig_test, zorder=zorder)
  307. draw_quiverkey_setzorder(fig_ref, zorder=zorder)