quiver.py 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219
  1. """
  2. Support for plotting vector fields.
  3. Presently this contains Quiver and Barb. Quiver plots an arrow in the
  4. direction of the vector, with the size of the arrow related to the
  5. magnitude of the vector.
  6. Barbs are like quiver in that they point along a vector, but
  7. the magnitude of the vector is given schematically by the presence of barbs
  8. or flags on the barb.
  9. This will also become a home for things such as standard
  10. deviation ellipses, which can and will be derived very easily from
  11. the Quiver code.
  12. """
  13. import math
  14. import weakref
  15. import numpy as np
  16. from numpy import ma
  17. from matplotlib import cbook, docstring, font_manager
  18. import matplotlib.artist as martist
  19. import matplotlib.collections as mcollections
  20. from matplotlib.patches import CirclePolygon
  21. import matplotlib.text as mtext
  22. import matplotlib.transforms as transforms
  23. _quiver_doc = """
  24. Plot a 2D field of arrows.
  25. Call signature::
  26. quiver([X, Y], U, V, [C], **kw)
  27. *X*, *Y* define the arrow locations, *U*, *V* define the arrow directions, and
  28. *C* optionally sets the color.
  29. **Arrow size**
  30. The default settings auto-scales the length of the arrows to a reasonable size.
  31. To change this behavior see the *scale* and *scale_units* parameters.
  32. **Arrow shape**
  33. The defaults give a slightly swept-back arrow; to make the head a
  34. triangle, make *headaxislength* the same as *headlength*. To make the
  35. arrow more pointed, reduce *headwidth* or increase *headlength* and
  36. *headaxislength*. To make the head smaller relative to the shaft,
  37. scale down all the head parameters. You will probably do best to leave
  38. minshaft alone.
  39. **Arrow outline**
  40. *linewidths* and *edgecolors* can be used to customize the arrow
  41. outlines.
  42. Parameters
  43. ----------
  44. X, Y : 1D or 2D array-like, optional
  45. The x and y coordinates of the arrow locations.
  46. If not given, they will be generated as a uniform integer meshgrid based
  47. on the dimensions of *U* and *V*.
  48. If *X* and *Y* are 1D but *U*, *V* are 2D, *X*, *Y* are expanded to 2D
  49. using ``X, Y = np.meshgrid(X, Y)``. In this case ``len(X)`` and ``len(Y)``
  50. must match the column and row dimensions of *U* and *V*.
  51. U, V : 1D or 2D array-like
  52. The x and y direction components of the arrow vectors.
  53. They must have the same number of elements, matching the number of arrow
  54. locations. *U* and *V* may be masked. Only locations unmasked in
  55. *U*, *V*, and *C* will be drawn.
  56. C : 1D or 2D array-like, optional
  57. Numeric data that defines the arrow colors by colormapping via *norm* and
  58. *cmap*.
  59. This does not support explicit colors. If you want to set colors directly,
  60. use *color* instead. The size of *C* must match the number of arrow
  61. locations.
  62. units : {'width', 'height', 'dots', 'inches', 'x', 'y' 'xy'}, default: 'width'
  63. The arrow dimensions (except for *length*) are measured in multiples of
  64. this unit.
  65. The following values are supported:
  66. - 'width', 'height': The width or height of the axis.
  67. - 'dots', 'inches': Pixels or inches based on the figure dpi.
  68. - 'x', 'y', 'xy': *X*, *Y* or :math:`\\sqrt{X^2 + Y^2}` in data units.
  69. The arrows scale differently depending on the units. For
  70. 'x' or 'y', the arrows get larger as one zooms in; for other
  71. units, the arrow size is independent of the zoom state. For
  72. 'width or 'height', the arrow size increases with the width and
  73. height of the axes, respectively, when the window is resized;
  74. for 'dots' or 'inches', resizing does not change the arrows.
  75. angles : {'uv', 'xy'} or array-like, default: 'uv'
  76. Method for determining the angle of the arrows.
  77. - 'uv': The arrow axis aspect ratio is 1 so that
  78. if *U* == *V* the orientation of the arrow on the plot is 45 degrees
  79. counter-clockwise from the horizontal axis (positive to the right).
  80. Use this if the arrows symbolize a quantity that is not based on
  81. *X*, *Y* data coordinates.
  82. - 'xy': Arrows point from (x, y) to (x+u, y+v).
  83. Use this for plotting a gradient field, for example.
  84. - Alternatively, arbitrary angles may be specified explicitly as an array
  85. of values in degrees, counter-clockwise from the horizontal axis.
  86. In this case *U*, *V* is only used to determine the length of the
  87. arrows.
  88. Note: inverting a data axis will correspondingly invert the
  89. arrows only with ``angles='xy'``.
  90. scale : float, optional
  91. Number of data units per arrow length unit, e.g., m/s per plot width; a
  92. smaller scale parameter makes the arrow longer. Default is *None*.
  93. If *None*, a simple autoscaling algorithm is used, based on the average
  94. vector length and the number of vectors. The arrow length unit is given by
  95. the *scale_units* parameter.
  96. scale_units : {'width', 'height', 'dots', 'inches', 'x', 'y', 'xy'}, optional
  97. If the *scale* kwarg is *None*, the arrow length unit. Default is *None*.
  98. e.g. *scale_units* is 'inches', *scale* is 2.0, and ``(u, v) = (1, 0)``,
  99. then the vector will be 0.5 inches long.
  100. If *scale_units* is 'width' or 'height', then the vector will be half the
  101. width/height of the axes.
  102. If *scale_units* is 'x' then the vector will be 0.5 x-axis
  103. units. To plot vectors in the x-y plane, with u and v having
  104. the same units as x and y, use
  105. ``angles='xy', scale_units='xy', scale=1``.
  106. width : float, optional
  107. Shaft width in arrow units; default depends on choice of units,
  108. above, and number of vectors; a typical starting value is about
  109. 0.005 times the width of the plot.
  110. headwidth : float, default: 3
  111. Head width as multiple of shaft width.
  112. headlength : float, default: 5
  113. Head length as multiple of shaft width.
  114. headaxislength : float, default: 4.5
  115. Head length at shaft intersection.
  116. minshaft : float, default: 1
  117. Length below which arrow scales, in units of head length. Do not
  118. set this to less than 1, or small arrows will look terrible!
  119. minlength : float, default: 1
  120. Minimum length as a multiple of shaft width; if an arrow length
  121. is less than this, plot a dot (hexagon) of this diameter instead.
  122. pivot : {'tail', 'mid', 'middle', 'tip'}, default: 'tail'
  123. The part of the arrow that is anchored to the *X*, *Y* grid. The arrow
  124. rotates about this point.
  125. 'mid' is a synonym for 'middle'.
  126. color : color or color sequence, optional
  127. Explicit color(s) for the arrows. If *C* has been set, *color* has no
  128. effect.
  129. This is a synonym for the `~.PolyCollection` *facecolor* parameter.
  130. Other Parameters
  131. ----------------
  132. **kwargs : `~matplotlib.collections.PolyCollection` properties, optional
  133. All other keyword arguments are passed on to `.PolyCollection`:
  134. %(PolyCollection)s
  135. See Also
  136. --------
  137. .Axes.quiverkey : Add a key to a quiver plot.
  138. """ % docstring.interpd.params
  139. class QuiverKey(martist.Artist):
  140. """Labelled arrow for use as a quiver plot scale key."""
  141. halign = {'N': 'center', 'S': 'center', 'E': 'left', 'W': 'right'}
  142. valign = {'N': 'bottom', 'S': 'top', 'E': 'center', 'W': 'center'}
  143. pivot = {'N': 'middle', 'S': 'middle', 'E': 'tip', 'W': 'tail'}
  144. def __init__(self, Q, X, Y, U, label,
  145. *, angle=0, coordinates='axes', color=None, labelsep=0.1,
  146. labelpos='N', labelcolor=None, fontproperties=None,
  147. **kw):
  148. """
  149. Add a key to a quiver plot.
  150. The positioning of the key depends on *X*, *Y*, *coordinates*, and
  151. *labelpos*. If *labelpos* is 'N' or 'S', *X*, *Y* give the position of
  152. the middle of the key arrow. If *labelpos* is 'E', *X*, *Y* positions
  153. the head, and if *labelpos* is 'W', *X*, *Y* positions the tail; in
  154. either of these two cases, *X*, *Y* is somewhere in the middle of the
  155. arrow+label key object.
  156. Parameters
  157. ----------
  158. Q : `matplotlib.quiver.Quiver`
  159. A `.Quiver` object as returned by a call to `~.Axes.quiver()`.
  160. X, Y : float
  161. The location of the key.
  162. U : float
  163. The length of the key.
  164. label : str
  165. The key label (e.g., length and units of the key).
  166. angle : float, default: 0
  167. The angle of the key arrow, in degrees anti-clockwise from the
  168. x-axis.
  169. coordinates : {'axes', 'figure', 'data', 'inches'}, default: 'axes'
  170. Coordinate system and units for *X*, *Y*: 'axes' and 'figure' are
  171. normalized coordinate systems with (0, 0) in the lower left and
  172. (1, 1) in the upper right; 'data' are the axes data coordinates
  173. (used for the locations of the vectors in the quiver plot itself);
  174. 'inches' is position in the figure in inches, with (0, 0) at the
  175. lower left corner.
  176. color : color
  177. Overrides face and edge colors from *Q*.
  178. labelpos : {'N', 'S', 'E', 'W'}
  179. Position the label above, below, to the right, to the left of the
  180. arrow, respectively.
  181. labelsep : float, default: 0.1
  182. Distance in inches between the arrow and the label.
  183. labelcolor : color, default: :rc:`text.color`
  184. Label color.
  185. fontproperties : dict, optional
  186. A dictionary with keyword arguments accepted by the
  187. `~matplotlib.font_manager.FontProperties` initializer:
  188. *family*, *style*, *variant*, *size*, *weight*.
  189. **kwargs
  190. Any additional keyword arguments are used to override vector
  191. properties taken from *Q*.
  192. """
  193. martist.Artist.__init__(self)
  194. self.Q = Q
  195. self.X = X
  196. self.Y = Y
  197. self.U = U
  198. self.angle = angle
  199. self.coord = coordinates
  200. self.color = color
  201. self.label = label
  202. self._labelsep_inches = labelsep
  203. self.labelsep = (self._labelsep_inches * Q.axes.figure.dpi)
  204. # try to prevent closure over the real self
  205. weak_self = weakref.ref(self)
  206. def on_dpi_change(fig):
  207. self_weakref = weak_self()
  208. if self_weakref is not None:
  209. self_weakref.labelsep = self_weakref._labelsep_inches * fig.dpi
  210. # simple brute force update works because _init is called at
  211. # the start of draw.
  212. self_weakref._initialized = False
  213. self._cid = Q.axes.figure.callbacks.connect(
  214. 'dpi_changed', on_dpi_change)
  215. self.labelpos = labelpos
  216. self.labelcolor = labelcolor
  217. self.fontproperties = fontproperties or dict()
  218. self.kw = kw
  219. _fp = self.fontproperties
  220. # boxprops = dict(facecolor='red')
  221. self.text = mtext.Text(
  222. text=label, # bbox=boxprops,
  223. horizontalalignment=self.halign[self.labelpos],
  224. verticalalignment=self.valign[self.labelpos],
  225. fontproperties=font_manager.FontProperties._from_any(_fp))
  226. if self.labelcolor is not None:
  227. self.text.set_color(self.labelcolor)
  228. self._initialized = False
  229. self.zorder = Q.zorder + 0.1
  230. def remove(self):
  231. # docstring inherited
  232. self.Q.axes.figure.callbacks.disconnect(self._cid)
  233. self._cid = None
  234. super().remove() # pass the remove call up the stack
  235. def _init(self):
  236. if True: # not self._initialized:
  237. if not self.Q._initialized:
  238. self.Q._init()
  239. self._set_transform()
  240. with cbook._setattr_cm(self.Q, pivot=self.pivot[self.labelpos],
  241. # Hack: save and restore the Umask
  242. Umask=ma.nomask):
  243. u = self.U * np.cos(np.radians(self.angle))
  244. v = self.U * np.sin(np.radians(self.angle))
  245. angle = (self.Q.angles if isinstance(self.Q.angles, str)
  246. else 'uv')
  247. self.verts = self.Q._make_verts(
  248. np.array([u]), np.array([v]), angle)
  249. kw = self.Q.polykw
  250. kw.update(self.kw)
  251. self.vector = mcollections.PolyCollection(
  252. self.verts,
  253. offsets=[(self.X, self.Y)],
  254. transOffset=self.get_transform(),
  255. **kw)
  256. if self.color is not None:
  257. self.vector.set_color(self.color)
  258. self.vector.set_transform(self.Q.get_transform())
  259. self.vector.set_figure(self.get_figure())
  260. self._initialized = True
  261. def _text_x(self, x):
  262. if self.labelpos == 'E':
  263. return x + self.labelsep
  264. elif self.labelpos == 'W':
  265. return x - self.labelsep
  266. else:
  267. return x
  268. def _text_y(self, y):
  269. if self.labelpos == 'N':
  270. return y + self.labelsep
  271. elif self.labelpos == 'S':
  272. return y - self.labelsep
  273. else:
  274. return y
  275. @martist.allow_rasterization
  276. def draw(self, renderer):
  277. self._init()
  278. self.vector.draw(renderer)
  279. x, y = self.get_transform().transform((self.X, self.Y))
  280. self.text.set_x(self._text_x(x))
  281. self.text.set_y(self._text_y(y))
  282. self.text.draw(renderer)
  283. self.stale = False
  284. def _set_transform(self):
  285. self.set_transform(cbook._check_getitem({
  286. "data": self.Q.axes.transData,
  287. "axes": self.Q.axes.transAxes,
  288. "figure": self.Q.axes.figure.transFigure,
  289. "inches": self.Q.axes.figure.dpi_scale_trans,
  290. }, coordinates=self.coord))
  291. def set_figure(self, fig):
  292. martist.Artist.set_figure(self, fig)
  293. self.text.set_figure(fig)
  294. def contains(self, mouseevent):
  295. inside, info = self._default_contains(mouseevent)
  296. if inside is not None:
  297. return inside, info
  298. # Maybe the dictionary should allow one to
  299. # distinguish between a text hit and a vector hit.
  300. if (self.text.contains(mouseevent)[0] or
  301. self.vector.contains(mouseevent)[0]):
  302. return True, {}
  303. return False, {}
  304. @cbook.deprecated("3.2")
  305. @property
  306. def quiverkey_doc(self):
  307. return self.__init__.__doc__
  308. def _parse_args(*args, caller_name='function'):
  309. """
  310. Helper function to parse positional parameters for colored vector plots.
  311. This is currently used for Quiver and Barbs.
  312. Parameters
  313. ----------
  314. *args : list
  315. list of 2-5 arguments. Depending on their number they are parsed to::
  316. U, V
  317. U, V, C
  318. X, Y, U, V
  319. X, Y, U, V, C
  320. caller_name : str
  321. Name of the calling method (used in error messages).
  322. """
  323. X = Y = C = None
  324. len_args = len(args)
  325. if len_args == 2:
  326. # The use of atleast_1d allows for handling scalar arguments while also
  327. # keeping masked arrays
  328. U, V = np.atleast_1d(*args)
  329. elif len_args == 3:
  330. U, V, C = np.atleast_1d(*args)
  331. elif len_args == 4:
  332. X, Y, U, V = np.atleast_1d(*args)
  333. elif len_args == 5:
  334. X, Y, U, V, C = np.atleast_1d(*args)
  335. else:
  336. raise TypeError(f'{caller_name} takes 2-5 positional arguments but '
  337. f'{len_args} were given')
  338. nr, nc = (1, U.shape[0]) if U.ndim == 1 else U.shape
  339. if X is not None:
  340. X = X.ravel()
  341. Y = Y.ravel()
  342. if len(X) == nc and len(Y) == nr:
  343. X, Y = [a.ravel() for a in np.meshgrid(X, Y)]
  344. elif len(X) != len(Y):
  345. raise ValueError('X and Y must be the same size, but '
  346. f'X.size is {X.size} and Y.size is {Y.size}.')
  347. else:
  348. indexgrid = np.meshgrid(np.arange(nc), np.arange(nr))
  349. X, Y = [np.ravel(a) for a in indexgrid]
  350. # Size validation for U, V, C is left to the set_UVC method.
  351. return X, Y, U, V, C
  352. def _check_consistent_shapes(*arrays):
  353. all_shapes = {a.shape for a in arrays}
  354. if len(all_shapes) != 1:
  355. raise ValueError('The shapes of the passed in arrays do not match')
  356. class Quiver(mcollections.PolyCollection):
  357. """
  358. Specialized PolyCollection for arrows.
  359. The only API method is set_UVC(), which can be used
  360. to change the size, orientation, and color of the
  361. arrows; their locations are fixed when the class is
  362. instantiated. Possibly this method will be useful
  363. in animations.
  364. Much of the work in this class is done in the draw()
  365. method so that as much information as possible is available
  366. about the plot. In subsequent draw() calls, recalculation
  367. is limited to things that might have changed, so there
  368. should be no performance penalty from putting the calculations
  369. in the draw() method.
  370. """
  371. _PIVOT_VALS = ('tail', 'middle', 'tip')
  372. @docstring.Substitution(_quiver_doc)
  373. def __init__(self, ax, *args,
  374. scale=None, headwidth=3, headlength=5, headaxislength=4.5,
  375. minshaft=1, minlength=1, units='width', scale_units=None,
  376. angles='uv', width=None, color='k', pivot='tail', **kw):
  377. """
  378. The constructor takes one required argument, an Axes
  379. instance, followed by the args and kwargs described
  380. by the following pyplot interface documentation:
  381. %s
  382. """
  383. self._axes = ax # The attr actually set by the Artist.axes property.
  384. X, Y, U, V, C = _parse_args(*args, caller_name='quiver()')
  385. self.X = X
  386. self.Y = Y
  387. self.XY = np.column_stack((X, Y))
  388. self.N = len(X)
  389. self.scale = scale
  390. self.headwidth = headwidth
  391. self.headlength = float(headlength)
  392. self.headaxislength = headaxislength
  393. self.minshaft = minshaft
  394. self.minlength = minlength
  395. self.units = units
  396. self.scale_units = scale_units
  397. self.angles = angles
  398. self.width = width
  399. if pivot.lower() == 'mid':
  400. pivot = 'middle'
  401. self.pivot = pivot.lower()
  402. cbook._check_in_list(self._PIVOT_VALS, pivot=self.pivot)
  403. self.transform = kw.pop('transform', ax.transData)
  404. kw.setdefault('facecolors', color)
  405. kw.setdefault('linewidths', (0,))
  406. mcollections.PolyCollection.__init__(self, [], offsets=self.XY,
  407. transOffset=self.transform,
  408. closed=False,
  409. **kw)
  410. self.polykw = kw
  411. self.set_UVC(U, V, C)
  412. self._initialized = False
  413. weak_self = weakref.ref(self) # Prevent closure over the real self.
  414. def on_dpi_change(fig):
  415. self_weakref = weak_self()
  416. if self_weakref is not None:
  417. # vertices depend on width, span which in turn depend on dpi
  418. self_weakref._new_UV = True
  419. # simple brute force update works because _init is called at
  420. # the start of draw.
  421. self_weakref._initialized = False
  422. self._cid = ax.figure.callbacks.connect('dpi_changed', on_dpi_change)
  423. @cbook.deprecated("3.3", alternative="axes")
  424. def ax(self):
  425. return self.axes
  426. def remove(self):
  427. # docstring inherited
  428. self.axes.figure.callbacks.disconnect(self._cid)
  429. self._cid = None
  430. super().remove() # pass the remove call up the stack
  431. def _init(self):
  432. """
  433. Initialization delayed until first draw;
  434. allow time for axes setup.
  435. """
  436. # It seems that there are not enough event notifications
  437. # available to have this work on an as-needed basis at present.
  438. if True: # not self._initialized:
  439. trans = self._set_transform()
  440. self.span = trans.inverted().transform_bbox(self.axes.bbox).width
  441. if self.width is None:
  442. sn = np.clip(math.sqrt(self.N), 8, 25)
  443. self.width = 0.06 * self.span / sn
  444. # _make_verts sets self.scale if not already specified
  445. if not self._initialized and self.scale is None:
  446. self._make_verts(self.U, self.V, self.angles)
  447. self._initialized = True
  448. def get_datalim(self, transData):
  449. trans = self.get_transform()
  450. transOffset = self.get_offset_transform()
  451. full_transform = (trans - transData) + (transOffset - transData)
  452. XY = full_transform.transform(self.XY)
  453. bbox = transforms.Bbox.null()
  454. bbox.update_from_data_xy(XY, ignore=True)
  455. return bbox
  456. @martist.allow_rasterization
  457. def draw(self, renderer):
  458. self._init()
  459. verts = self._make_verts(self.U, self.V, self.angles)
  460. self.set_verts(verts, closed=False)
  461. self._new_UV = False
  462. mcollections.PolyCollection.draw(self, renderer)
  463. self.stale = False
  464. def set_UVC(self, U, V, C=None):
  465. # We need to ensure we have a copy, not a reference
  466. # to an array that might change before draw().
  467. U = ma.masked_invalid(U, copy=True).ravel()
  468. V = ma.masked_invalid(V, copy=True).ravel()
  469. if C is not None:
  470. C = ma.masked_invalid(C, copy=True).ravel()
  471. for name, var in zip(('U', 'V', 'C'), (U, V, C)):
  472. if not (var is None or var.size == self.N or var.size == 1):
  473. raise ValueError(f'Argument {name} has a size {var.size}'
  474. f' which does not match {self.N},'
  475. ' the number of arrow positions')
  476. mask = ma.mask_or(U.mask, V.mask, copy=False, shrink=True)
  477. if C is not None:
  478. mask = ma.mask_or(mask, C.mask, copy=False, shrink=True)
  479. if mask is ma.nomask:
  480. C = C.filled()
  481. else:
  482. C = ma.array(C, mask=mask, copy=False)
  483. self.U = U.filled(1)
  484. self.V = V.filled(1)
  485. self.Umask = mask
  486. if C is not None:
  487. self.set_array(C)
  488. self._new_UV = True
  489. self.stale = True
  490. def _dots_per_unit(self, units):
  491. """
  492. Return a scale factor for converting from units to pixels
  493. """
  494. if units in ('x', 'y', 'xy'):
  495. if units == 'x':
  496. dx0 = self.axes.viewLim.width
  497. dx1 = self.axes.bbox.width
  498. elif units == 'y':
  499. dx0 = self.axes.viewLim.height
  500. dx1 = self.axes.bbox.height
  501. else: # 'xy' is assumed
  502. dxx0 = self.axes.viewLim.width
  503. dxx1 = self.axes.bbox.width
  504. dyy0 = self.axes.viewLim.height
  505. dyy1 = self.axes.bbox.height
  506. dx1 = np.hypot(dxx1, dyy1)
  507. dx0 = np.hypot(dxx0, dyy0)
  508. dx = dx1 / dx0
  509. else:
  510. if units == 'width':
  511. dx = self.axes.bbox.width
  512. elif units == 'height':
  513. dx = self.axes.bbox.height
  514. elif units == 'dots':
  515. dx = 1.0
  516. elif units == 'inches':
  517. dx = self.axes.figure.dpi
  518. else:
  519. raise ValueError('unrecognized units')
  520. return dx
  521. def _set_transform(self):
  522. """
  523. Set the PolyCollection transform to go
  524. from arrow width units to pixels.
  525. """
  526. dx = self._dots_per_unit(self.units)
  527. self._trans_scale = dx # pixels per arrow width unit
  528. trans = transforms.Affine2D().scale(dx)
  529. self.set_transform(trans)
  530. return trans
  531. def _angles_lengths(self, U, V, eps=1):
  532. xy = self.axes.transData.transform(self.XY)
  533. uv = np.column_stack((U, V))
  534. xyp = self.axes.transData.transform(self.XY + eps * uv)
  535. dxy = xyp - xy
  536. angles = np.arctan2(dxy[:, 1], dxy[:, 0])
  537. lengths = np.hypot(*dxy.T) / eps
  538. return angles, lengths
  539. def _make_verts(self, U, V, angles):
  540. uv = (U + V * 1j)
  541. str_angles = angles if isinstance(angles, str) else ''
  542. if str_angles == 'xy' and self.scale_units == 'xy':
  543. # Here eps is 1 so that if we get U, V by diffing
  544. # the X, Y arrays, the vectors will connect the
  545. # points, regardless of the axis scaling (including log).
  546. angles, lengths = self._angles_lengths(U, V, eps=1)
  547. elif str_angles == 'xy' or self.scale_units == 'xy':
  548. # Calculate eps based on the extents of the plot
  549. # so that we don't end up with roundoff error from
  550. # adding a small number to a large.
  551. eps = np.abs(self.axes.dataLim.extents).max() * 0.001
  552. angles, lengths = self._angles_lengths(U, V, eps=eps)
  553. if str_angles and self.scale_units == 'xy':
  554. a = lengths
  555. else:
  556. a = np.abs(uv)
  557. if self.scale is None:
  558. sn = max(10, math.sqrt(self.N))
  559. if self.Umask is not ma.nomask:
  560. amean = a[~self.Umask].mean()
  561. else:
  562. amean = a.mean()
  563. # crude auto-scaling
  564. # scale is typical arrow length as a multiple of the arrow width
  565. scale = 1.8 * amean * sn / self.span
  566. if self.scale_units is None:
  567. if self.scale is None:
  568. self.scale = scale
  569. widthu_per_lenu = 1.0
  570. else:
  571. if self.scale_units == 'xy':
  572. dx = 1
  573. else:
  574. dx = self._dots_per_unit(self.scale_units)
  575. widthu_per_lenu = dx / self._trans_scale
  576. if self.scale is None:
  577. self.scale = scale * widthu_per_lenu
  578. length = a * (widthu_per_lenu / (self.scale * self.width))
  579. X, Y = self._h_arrows(length)
  580. if str_angles == 'xy':
  581. theta = angles
  582. elif str_angles == 'uv':
  583. theta = np.angle(uv)
  584. else:
  585. theta = ma.masked_invalid(np.deg2rad(angles)).filled(0)
  586. theta = theta.reshape((-1, 1)) # for broadcasting
  587. xy = (X + Y * 1j) * np.exp(1j * theta) * self.width
  588. XY = np.stack((xy.real, xy.imag), axis=2)
  589. if self.Umask is not ma.nomask:
  590. XY = ma.array(XY)
  591. XY[self.Umask] = ma.masked
  592. # This might be handled more efficiently with nans, given
  593. # that nans will end up in the paths anyway.
  594. return XY
  595. def _h_arrows(self, length):
  596. """Length is in arrow width units."""
  597. # It might be possible to streamline the code
  598. # and speed it up a bit by using complex (x, y)
  599. # instead of separate arrays; but any gain would be slight.
  600. minsh = self.minshaft * self.headlength
  601. N = len(length)
  602. length = length.reshape(N, 1)
  603. # This number is chosen based on when pixel values overflow in Agg
  604. # causing rendering errors
  605. # length = np.minimum(length, 2 ** 16)
  606. np.clip(length, 0, 2 ** 16, out=length)
  607. # x, y: normal horizontal arrow
  608. x = np.array([0, -self.headaxislength,
  609. -self.headlength, 0],
  610. np.float64)
  611. x = x + np.array([0, 1, 1, 1]) * length
  612. y = 0.5 * np.array([1, 1, self.headwidth, 0], np.float64)
  613. y = np.repeat(y[np.newaxis, :], N, axis=0)
  614. # x0, y0: arrow without shaft, for short vectors
  615. x0 = np.array([0, minsh - self.headaxislength,
  616. minsh - self.headlength, minsh], np.float64)
  617. y0 = 0.5 * np.array([1, 1, self.headwidth, 0], np.float64)
  618. ii = [0, 1, 2, 3, 2, 1, 0, 0]
  619. X = x[:, ii]
  620. Y = y[:, ii]
  621. Y[:, 3:-1] *= -1
  622. X0 = x0[ii]
  623. Y0 = y0[ii]
  624. Y0[3:-1] *= -1
  625. shrink = length / minsh if minsh != 0. else 0.
  626. X0 = shrink * X0[np.newaxis, :]
  627. Y0 = shrink * Y0[np.newaxis, :]
  628. short = np.repeat(length < minsh, 8, axis=1)
  629. # Now select X0, Y0 if short, otherwise X, Y
  630. np.copyto(X, X0, where=short)
  631. np.copyto(Y, Y0, where=short)
  632. if self.pivot == 'middle':
  633. X -= 0.5 * X[:, 3, np.newaxis]
  634. elif self.pivot == 'tip':
  635. # numpy bug? using -= does not work here unless we multiply by a
  636. # float first, as with 'mid'.
  637. X = X - X[:, 3, np.newaxis]
  638. elif self.pivot != 'tail':
  639. cbook._check_in_list(["middle", "tip", "tail"], pivot=self.pivot)
  640. tooshort = length < self.minlength
  641. if tooshort.any():
  642. # Use a heptagonal dot:
  643. th = np.arange(0, 8, 1, np.float64) * (np.pi / 3.0)
  644. x1 = np.cos(th) * self.minlength * 0.5
  645. y1 = np.sin(th) * self.minlength * 0.5
  646. X1 = np.repeat(x1[np.newaxis, :], N, axis=0)
  647. Y1 = np.repeat(y1[np.newaxis, :], N, axis=0)
  648. tooshort = np.repeat(tooshort, 8, 1)
  649. np.copyto(X, X1, where=tooshort)
  650. np.copyto(Y, Y1, where=tooshort)
  651. # Mask handling is deferred to the caller, _make_verts.
  652. return X, Y
  653. quiver_doc = _quiver_doc
  654. _barbs_doc = r"""
  655. Plot a 2D field of barbs.
  656. Call signature::
  657. barbs([X, Y], U, V, [C], **kw)
  658. Where *X*, *Y* define the barb locations, *U*, *V* define the barb
  659. directions, and *C* optionally sets the color.
  660. All arguments may be 1D or 2D. *U*, *V*, *C* may be masked arrays, but masked
  661. *X*, *Y* are not supported at present.
  662. Barbs are traditionally used in meteorology as a way to plot the speed
  663. and direction of wind observations, but can technically be used to
  664. plot any two dimensional vector quantity. As opposed to arrows, which
  665. give vector magnitude by the length of the arrow, the barbs give more
  666. quantitative information about the vector magnitude by putting slanted
  667. lines or a triangle for various increments in magnitude, as show
  668. schematically below::
  669. : /\ \
  670. : / \ \
  671. : / \ \ \
  672. : / \ \ \
  673. : ------------------------------
  674. The largest increment is given by a triangle (or "flag"). After those
  675. come full lines (barbs). The smallest increment is a half line. There
  676. is only, of course, ever at most 1 half line. If the magnitude is
  677. small and only needs a single half-line and no full lines or
  678. triangles, the half-line is offset from the end of the barb so that it
  679. can be easily distinguished from barbs with a single full line. The
  680. magnitude for the barb shown above would nominally be 65, using the
  681. standard increments of 50, 10, and 5.
  682. See also https://en.wikipedia.org/wiki/Wind_barb.
  683. Parameters
  684. ----------
  685. X, Y : 1D or 2D array-like, optional
  686. The x and y coordinates of the barb locations. See *pivot* for how the
  687. barbs are drawn to the x, y positions.
  688. If not given, they will be generated as a uniform integer meshgrid based
  689. on the dimensions of *U* and *V*.
  690. If *X* and *Y* are 1D but *U*, *V* are 2D, *X*, *Y* are expanded to 2D
  691. using ``X, Y = np.meshgrid(X, Y)``. In this case ``len(X)`` and ``len(Y)``
  692. must match the column and row dimensions of *U* and *V*.
  693. U, V : 1D or 2D array-like
  694. The x and y components of the barb shaft.
  695. C : 1D or 2D array-like, optional
  696. Numeric data that defines the barb colors by colormapping via *norm* and
  697. *cmap*.
  698. This does not support explicit colors. If you want to set colors directly,
  699. use *barbcolor* instead.
  700. length : float, default: 7
  701. Length of the barb in points; the other parts of the barb
  702. are scaled against this.
  703. pivot : {'tip', 'middle'} or float, default: 'tip'
  704. The part of the arrow that is anchored to the *X*, *Y* grid. The barb
  705. rotates about this point. This can also be a number, which shifts the
  706. start of the barb that many points away from grid point.
  707. barbcolor : color or color sequence
  708. The color of all parts of the barb except for the flags. This parameter
  709. is analogous to the *edgecolor* parameter for polygons, which can be used
  710. instead. However this parameter will override facecolor.
  711. flagcolor : color or color sequence
  712. The color of any flags on the barb. This parameter is analogous to the
  713. *facecolor* parameter for polygons, which can be used instead. However,
  714. this parameter will override facecolor. If this is not set (and *C* has
  715. not either) then *flagcolor* will be set to match *barbcolor* so that the
  716. barb has a uniform color. If *C* has been set, *flagcolor* has no effect.
  717. sizes : dict, optional
  718. A dictionary of coefficients specifying the ratio of a given
  719. feature to the length of the barb. Only those values one wishes to
  720. override need to be included. These features include:
  721. - 'spacing' - space between features (flags, full/half barbs)
  722. - 'height' - height (distance from shaft to top) of a flag or full barb
  723. - 'width' - width of a flag, twice the width of a full barb
  724. - 'emptybarb' - radius of the circle used for low magnitudes
  725. fill_empty : bool, default: False
  726. Whether the empty barbs (circles) that are drawn should be filled with
  727. the flag color. If they are not filled, the center is transparent.
  728. rounding : bool, default: True
  729. Whether the vector magnitude should be rounded when allocating barb
  730. components. If True, the magnitude is rounded to the nearest multiple
  731. of the half-barb increment. If False, the magnitude is simply truncated
  732. to the next lowest multiple.
  733. barb_increments : dict, optional
  734. A dictionary of increments specifying values to associate with
  735. different parts of the barb. Only those values one wishes to
  736. override need to be included.
  737. - 'half' - half barbs (Default is 5)
  738. - 'full' - full barbs (Default is 10)
  739. - 'flag' - flags (default is 50)
  740. flip_barb : bool or array-like of bool, default: False
  741. Whether the lines and flags should point opposite to normal.
  742. Normal behavior is for the barbs and lines to point right (comes from wind
  743. barbs having these features point towards low pressure in the Northern
  744. Hemisphere).
  745. A single value is applied to all barbs. Individual barbs can be flipped by
  746. passing a bool array of the same size as *U* and *V*.
  747. Returns
  748. -------
  749. barbs : `~matplotlib.quiver.Barbs`
  750. Other Parameters
  751. ----------------
  752. **kwargs
  753. The barbs can further be customized using `.PolyCollection` keyword
  754. arguments:
  755. %(PolyCollection)s
  756. """ % docstring.interpd.params
  757. docstring.interpd.update(barbs_doc=_barbs_doc)
  758. class Barbs(mcollections.PolyCollection):
  759. """
  760. Specialized PolyCollection for barbs.
  761. The only API method is :meth:`set_UVC`, which can be used to
  762. change the size, orientation, and color of the arrows. Locations
  763. are changed using the :meth:`set_offsets` collection method.
  764. Possibly this method will be useful in animations.
  765. There is one internal function :meth:`_find_tails` which finds
  766. exactly what should be put on the barb given the vector magnitude.
  767. From there :meth:`_make_barbs` is used to find the vertices of the
  768. polygon to represent the barb based on this information.
  769. """
  770. # This may be an abuse of polygons here to render what is essentially maybe
  771. # 1 triangle and a series of lines. It works fine as far as I can tell
  772. # however.
  773. @docstring.interpd
  774. def __init__(self, ax, *args,
  775. pivot='tip', length=7, barbcolor=None, flagcolor=None,
  776. sizes=None, fill_empty=False, barb_increments=None,
  777. rounding=True, flip_barb=False, **kw):
  778. """
  779. The constructor takes one required argument, an Axes
  780. instance, followed by the args and kwargs described
  781. by the following pyplot interface documentation:
  782. %(barbs_doc)s
  783. """
  784. self.sizes = sizes or dict()
  785. self.fill_empty = fill_empty
  786. self.barb_increments = barb_increments or dict()
  787. self.rounding = rounding
  788. self.flip = np.atleast_1d(flip_barb)
  789. transform = kw.pop('transform', ax.transData)
  790. self._pivot = pivot
  791. self._length = length
  792. barbcolor = barbcolor
  793. flagcolor = flagcolor
  794. # Flagcolor and barbcolor provide convenience parameters for
  795. # setting the facecolor and edgecolor, respectively, of the barb
  796. # polygon. We also work here to make the flag the same color as the
  797. # rest of the barb by default
  798. if None in (barbcolor, flagcolor):
  799. kw['edgecolors'] = 'face'
  800. if flagcolor:
  801. kw['facecolors'] = flagcolor
  802. elif barbcolor:
  803. kw['facecolors'] = barbcolor
  804. else:
  805. # Set to facecolor passed in or default to black
  806. kw.setdefault('facecolors', 'k')
  807. else:
  808. kw['edgecolors'] = barbcolor
  809. kw['facecolors'] = flagcolor
  810. # Explicitly set a line width if we're not given one, otherwise
  811. # polygons are not outlined and we get no barbs
  812. if 'linewidth' not in kw and 'lw' not in kw:
  813. kw['linewidth'] = 1
  814. # Parse out the data arrays from the various configurations supported
  815. x, y, u, v, c = _parse_args(*args, caller_name='barbs()')
  816. self.x = x
  817. self.y = y
  818. xy = np.column_stack((x, y))
  819. # Make a collection
  820. barb_size = self._length ** 2 / 4 # Empirically determined
  821. mcollections.PolyCollection.__init__(self, [], (barb_size,),
  822. offsets=xy,
  823. transOffset=transform, **kw)
  824. self.set_transform(transforms.IdentityTransform())
  825. self.set_UVC(u, v, c)
  826. def _find_tails(self, mag, rounding=True, half=5, full=10, flag=50):
  827. """
  828. Find how many of each of the tail pieces is necessary. Flag
  829. specifies the increment for a flag, barb for a full barb, and half for
  830. half a barb. Mag should be the magnitude of a vector (i.e., >= 0).
  831. This returns a tuple of:
  832. (*number of flags*, *number of barbs*, *half_flag*, *empty_flag*)
  833. The bool *half_flag* indicates whether half of a barb is needed,
  834. since there should only ever be one half on a given
  835. barb. *empty_flag* flag is an array of flags to easily tell if
  836. a barb is empty (too low to plot any barbs/flags.
  837. """
  838. # If rounding, round to the nearest multiple of half, the smallest
  839. # increment
  840. if rounding:
  841. mag = half * (mag / half + 0.5).astype(int)
  842. num_flags = np.floor(mag / flag).astype(int)
  843. mag = mag % flag
  844. num_barb = np.floor(mag / full).astype(int)
  845. mag = mag % full
  846. half_flag = mag >= half
  847. empty_flag = ~(half_flag | (num_flags > 0) | (num_barb > 0))
  848. return num_flags, num_barb, half_flag, empty_flag
  849. def _make_barbs(self, u, v, nflags, nbarbs, half_barb, empty_flag, length,
  850. pivot, sizes, fill_empty, flip):
  851. """
  852. Create the wind barbs.
  853. Parameters
  854. ----------
  855. u, v
  856. Components of the vector in the x and y directions, respectively.
  857. nflags, nbarbs, half_barb, empty_flag
  858. Respectively, the number of flags, number of barbs, flag for
  859. half a barb, and flag for empty barb, ostensibly obtained from
  860. :meth:`_find_tails`.
  861. length
  862. The length of the barb staff in points.
  863. pivot : {"tip", "middle"} or number
  864. The point on the barb around which the entire barb should be
  865. rotated. If a number, the start of the barb is shifted by that
  866. many points from the origin.
  867. sizes : dict
  868. Coefficients specifying the ratio of a given feature to the length
  869. of the barb. These features include:
  870. - *spacing*: space between features (flags, full/half barbs).
  871. - *height*: distance from shaft of top of a flag or full barb.
  872. - *width*: width of a flag, twice the width of a full barb.
  873. - *emptybarb*: radius of the circle used for low magnitudes.
  874. fill_empty : bool
  875. Whether the circle representing an empty barb should be filled or
  876. not (this changes the drawing of the polygon).
  877. flip : list of bool
  878. Whether the features should be flipped to the other side of the
  879. barb (useful for winds in the southern hemisphere).
  880. Returns
  881. -------
  882. list of arrays of vertices
  883. Polygon vertices for each of the wind barbs. These polygons have
  884. been rotated to properly align with the vector direction.
  885. """
  886. # These control the spacing and size of barb elements relative to the
  887. # length of the shaft
  888. spacing = length * sizes.get('spacing', 0.125)
  889. full_height = length * sizes.get('height', 0.4)
  890. full_width = length * sizes.get('width', 0.25)
  891. empty_rad = length * sizes.get('emptybarb', 0.15)
  892. # Controls y point where to pivot the barb.
  893. pivot_points = dict(tip=0.0, middle=-length / 2.)
  894. endx = 0.0
  895. try:
  896. endy = float(pivot)
  897. except ValueError:
  898. endy = pivot_points[pivot.lower()]
  899. # Get the appropriate angle for the vector components. The offset is
  900. # due to the way the barb is initially drawn, going down the y-axis.
  901. # This makes sense in a meteorological mode of thinking since there 0
  902. # degrees corresponds to north (the y-axis traditionally)
  903. angles = -(ma.arctan2(v, u) + np.pi / 2)
  904. # Used for low magnitude. We just get the vertices, so if we make it
  905. # out here, it can be reused. The center set here should put the
  906. # center of the circle at the location(offset), rather than at the
  907. # same point as the barb pivot; this seems more sensible.
  908. circ = CirclePolygon((0, 0), radius=empty_rad).get_verts()
  909. if fill_empty:
  910. empty_barb = circ
  911. else:
  912. # If we don't want the empty one filled, we make a degenerate
  913. # polygon that wraps back over itself
  914. empty_barb = np.concatenate((circ, circ[::-1]))
  915. barb_list = []
  916. for index, angle in np.ndenumerate(angles):
  917. # If the vector magnitude is too weak to draw anything, plot an
  918. # empty circle instead
  919. if empty_flag[index]:
  920. # We can skip the transform since the circle has no preferred
  921. # orientation
  922. barb_list.append(empty_barb)
  923. continue
  924. poly_verts = [(endx, endy)]
  925. offset = length
  926. # Handle if this barb should be flipped
  927. barb_height = -full_height if flip[index] else full_height
  928. # Add vertices for each flag
  929. for i in range(nflags[index]):
  930. # The spacing that works for the barbs is a little to much for
  931. # the flags, but this only occurs when we have more than 1
  932. # flag.
  933. if offset != length:
  934. offset += spacing / 2.
  935. poly_verts.extend(
  936. [[endx, endy + offset],
  937. [endx + barb_height, endy - full_width / 2 + offset],
  938. [endx, endy - full_width + offset]])
  939. offset -= full_width + spacing
  940. # Add vertices for each barb. These really are lines, but works
  941. # great adding 3 vertices that basically pull the polygon out and
  942. # back down the line
  943. for i in range(nbarbs[index]):
  944. poly_verts.extend(
  945. [(endx, endy + offset),
  946. (endx + barb_height, endy + offset + full_width / 2),
  947. (endx, endy + offset)])
  948. offset -= spacing
  949. # Add the vertices for half a barb, if needed
  950. if half_barb[index]:
  951. # If the half barb is the first on the staff, traditionally it
  952. # is offset from the end to make it easy to distinguish from a
  953. # barb with a full one
  954. if offset == length:
  955. poly_verts.append((endx, endy + offset))
  956. offset -= 1.5 * spacing
  957. poly_verts.extend(
  958. [(endx, endy + offset),
  959. (endx + barb_height / 2, endy + offset + full_width / 4),
  960. (endx, endy + offset)])
  961. # Rotate the barb according the angle. Making the barb first and
  962. # then rotating it made the math for drawing the barb really easy.
  963. # Also, the transform framework makes doing the rotation simple.
  964. poly_verts = transforms.Affine2D().rotate(-angle).transform(
  965. poly_verts)
  966. barb_list.append(poly_verts)
  967. return barb_list
  968. def set_UVC(self, U, V, C=None):
  969. self.u = ma.masked_invalid(U, copy=False).ravel()
  970. self.v = ma.masked_invalid(V, copy=False).ravel()
  971. # Flip needs to have the same number of entries as everything else.
  972. # Use broadcast_to to avoid a bloated array of identical values.
  973. # (can't rely on actual broadcasting)
  974. if len(self.flip) == 1:
  975. flip = np.broadcast_to(self.flip, self.u.shape)
  976. else:
  977. flip = self.flip
  978. if C is not None:
  979. c = ma.masked_invalid(C, copy=False).ravel()
  980. x, y, u, v, c, flip = cbook.delete_masked_points(
  981. self.x.ravel(), self.y.ravel(), self.u, self.v, c,
  982. flip.ravel())
  983. _check_consistent_shapes(x, y, u, v, c, flip)
  984. else:
  985. x, y, u, v, flip = cbook.delete_masked_points(
  986. self.x.ravel(), self.y.ravel(), self.u, self.v, flip.ravel())
  987. _check_consistent_shapes(x, y, u, v, flip)
  988. magnitude = np.hypot(u, v)
  989. flags, barbs, halves, empty = self._find_tails(magnitude,
  990. self.rounding,
  991. **self.barb_increments)
  992. # Get the vertices for each of the barbs
  993. plot_barbs = self._make_barbs(u, v, flags, barbs, halves, empty,
  994. self._length, self._pivot, self.sizes,
  995. self.fill_empty, flip)
  996. self.set_verts(plot_barbs)
  997. # Set the color array
  998. if C is not None:
  999. self.set_array(c)
  1000. # Update the offsets in case the masked data changed
  1001. xy = np.column_stack((x, y))
  1002. self._offsets = xy
  1003. self.stale = True
  1004. def set_offsets(self, xy):
  1005. """
  1006. Set the offsets for the barb polygons. This saves the offsets passed
  1007. in and masks them as appropriate for the existing U/V data.
  1008. Parameters
  1009. ----------
  1010. xy : sequence of pairs of floats
  1011. """
  1012. self.x = xy[:, 0]
  1013. self.y = xy[:, 1]
  1014. x, y, u, v = cbook.delete_masked_points(
  1015. self.x.ravel(), self.y.ravel(), self.u, self.v)
  1016. _check_consistent_shapes(x, y, u, v)
  1017. xy = np.column_stack((x, y))
  1018. mcollections.PolyCollection.set_offsets(self, xy)
  1019. self.stale = True
  1020. barbs_doc = _barbs_doc