_plotutils.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. from __future__ import division, print_function, absolute_import
  2. import numpy as np
  3. from scipy._lib.decorator import decorator as _decorator
  4. __all__ = ['delaunay_plot_2d', 'convex_hull_plot_2d', 'voronoi_plot_2d']
  5. @_decorator
  6. def _held_figure(func, obj, ax=None, **kw):
  7. import matplotlib.pyplot as plt
  8. if ax is None:
  9. fig = plt.figure()
  10. ax = fig.gca()
  11. return func(obj, ax=ax, **kw)
  12. # As of matplotlib 2.0, the "hold" mechanism is deprecated.
  13. # When matplotlib 1.x is no longer supported, this check can be removed.
  14. was_held = getattr(ax, 'ishold', lambda: True)()
  15. if was_held:
  16. return func(obj, ax=ax, **kw)
  17. try:
  18. ax.hold(True)
  19. return func(obj, ax=ax, **kw)
  20. finally:
  21. ax.hold(was_held)
  22. def _adjust_bounds(ax, points):
  23. margin = 0.1 * points.ptp(axis=0)
  24. xy_min = points.min(axis=0) - margin
  25. xy_max = points.max(axis=0) + margin
  26. ax.set_xlim(xy_min[0], xy_max[0])
  27. ax.set_ylim(xy_min[1], xy_max[1])
  28. @_held_figure
  29. def delaunay_plot_2d(tri, ax=None):
  30. """
  31. Plot the given Delaunay triangulation in 2-D
  32. Parameters
  33. ----------
  34. tri : scipy.spatial.Delaunay instance
  35. Triangulation to plot
  36. ax : matplotlib.axes.Axes instance, optional
  37. Axes to plot on
  38. Returns
  39. -------
  40. fig : matplotlib.figure.Figure instance
  41. Figure for the plot
  42. See Also
  43. --------
  44. Delaunay
  45. matplotlib.pyplot.triplot
  46. Notes
  47. -----
  48. Requires Matplotlib.
  49. Examples
  50. --------
  51. >>> import matplotlib.pyplot as plt
  52. >>> from scipy.spatial import Delaunay, delaunay_plot_2d
  53. The Delaunay triangulation of a set of random points:
  54. >>> points = np.random.rand(30, 2)
  55. >>> tri = Delaunay(points)
  56. Plot it:
  57. >>> _ = delaunay_plot_2d(tri)
  58. >>> plt.show()
  59. """
  60. if tri.points.shape[1] != 2:
  61. raise ValueError("Delaunay triangulation is not 2-D")
  62. x, y = tri.points.T
  63. ax.plot(x, y, 'o')
  64. ax.triplot(x, y, tri.simplices.copy())
  65. _adjust_bounds(ax, tri.points)
  66. return ax.figure
  67. @_held_figure
  68. def convex_hull_plot_2d(hull, ax=None):
  69. """
  70. Plot the given convex hull diagram in 2-D
  71. Parameters
  72. ----------
  73. hull : scipy.spatial.ConvexHull instance
  74. Convex hull to plot
  75. ax : matplotlib.axes.Axes instance, optional
  76. Axes to plot on
  77. Returns
  78. -------
  79. fig : matplotlib.figure.Figure instance
  80. Figure for the plot
  81. See Also
  82. --------
  83. ConvexHull
  84. Notes
  85. -----
  86. Requires Matplotlib.
  87. Examples
  88. --------
  89. >>> import matplotlib.pyplot as plt
  90. >>> from scipy.spatial import ConvexHull, convex_hull_plot_2d
  91. The convex hull of a random set of points:
  92. >>> points = np.random.rand(30, 2)
  93. >>> hull = ConvexHull(points)
  94. Plot it:
  95. >>> _ = convex_hull_plot_2d(hull)
  96. >>> plt.show()
  97. """
  98. from matplotlib.collections import LineCollection
  99. if hull.points.shape[1] != 2:
  100. raise ValueError("Convex hull is not 2-D")
  101. ax.plot(hull.points[:,0], hull.points[:,1], 'o')
  102. line_segments = [hull.points[simplex] for simplex in hull.simplices]
  103. ax.add_collection(LineCollection(line_segments,
  104. colors='k',
  105. linestyle='solid'))
  106. _adjust_bounds(ax, hull.points)
  107. return ax.figure
  108. @_held_figure
  109. def voronoi_plot_2d(vor, ax=None, **kw):
  110. """
  111. Plot the given Voronoi diagram in 2-D
  112. Parameters
  113. ----------
  114. vor : scipy.spatial.Voronoi instance
  115. Diagram to plot
  116. ax : matplotlib.axes.Axes instance, optional
  117. Axes to plot on
  118. show_points: bool, optional
  119. Add the Voronoi points to the plot.
  120. show_vertices : bool, optional
  121. Add the Voronoi vertices to the plot.
  122. line_colors : string, optional
  123. Specifies the line color for polygon boundaries
  124. line_width : float, optional
  125. Specifies the line width for polygon boundaries
  126. line_alpha: float, optional
  127. Specifies the line alpha for polygon boundaries
  128. point_size: float, optional
  129. Specifies the size of points
  130. Returns
  131. -------
  132. fig : matplotlib.figure.Figure instance
  133. Figure for the plot
  134. See Also
  135. --------
  136. Voronoi
  137. Notes
  138. -----
  139. Requires Matplotlib.
  140. Examples
  141. --------
  142. Set of point:
  143. >>> import matplotlib.pyplot as plt
  144. >>> points = np.random.rand(10,2) #random
  145. Voronoi diagram of the points:
  146. >>> from scipy.spatial import Voronoi, voronoi_plot_2d
  147. >>> vor = Voronoi(points)
  148. using `voronoi_plot_2d` for visualisation:
  149. >>> fig = voronoi_plot_2d(vor)
  150. using `voronoi_plot_2d` for visualisation with enhancements:
  151. >>> fig = voronoi_plot_2d(vor, show_vertices=False, line_colors='orange',
  152. ... line_width=2, line_alpha=0.6, point_size=2)
  153. >>> plt.show()
  154. """
  155. from matplotlib.collections import LineCollection
  156. if vor.points.shape[1] != 2:
  157. raise ValueError("Voronoi diagram is not 2-D")
  158. if kw.get('show_points', True):
  159. point_size = kw.get('point_size', None)
  160. ax.plot(vor.points[:,0], vor.points[:,1], '.', markersize=point_size)
  161. if kw.get('show_vertices', True):
  162. ax.plot(vor.vertices[:,0], vor.vertices[:,1], 'o')
  163. line_colors = kw.get('line_colors', 'k')
  164. line_width = kw.get('line_width', 1.0)
  165. line_alpha = kw.get('line_alpha', 1.0)
  166. center = vor.points.mean(axis=0)
  167. ptp_bound = vor.points.ptp(axis=0)
  168. finite_segments = []
  169. infinite_segments = []
  170. for pointidx, simplex in zip(vor.ridge_points, vor.ridge_vertices):
  171. simplex = np.asarray(simplex)
  172. if np.all(simplex >= 0):
  173. finite_segments.append(vor.vertices[simplex])
  174. else:
  175. i = simplex[simplex >= 0][0] # finite end Voronoi vertex
  176. t = vor.points[pointidx[1]] - vor.points[pointidx[0]] # tangent
  177. t /= np.linalg.norm(t)
  178. n = np.array([-t[1], t[0]]) # normal
  179. midpoint = vor.points[pointidx].mean(axis=0)
  180. direction = np.sign(np.dot(midpoint - center, n)) * n
  181. far_point = vor.vertices[i] + direction * ptp_bound.max()
  182. infinite_segments.append([vor.vertices[i], far_point])
  183. ax.add_collection(LineCollection(finite_segments,
  184. colors=line_colors,
  185. lw=line_width,
  186. alpha=line_alpha,
  187. linestyle='solid'))
  188. ax.add_collection(LineCollection(infinite_segments,
  189. colors=line_colors,
  190. lw=line_width,
  191. alpha=line_alpha,
  192. linestyle='dashed'))
  193. _adjust_bounds(ax, vor.points)
  194. return ax.figure