plotting.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. """Plot single geometries using Matplotlib.
  2. Note: this module is experimental, and mainly targeting (interactive)
  3. exploration, debugging and illustration purposes.
  4. """
  5. import numpy as np
  6. import shapely
  7. def _default_ax():
  8. import matplotlib.pyplot as plt
  9. ax = plt.gca()
  10. ax.grid(True)
  11. ax.set_aspect("equal")
  12. return ax
  13. def _path_from_polygon(polygon):
  14. from matplotlib.path import Path
  15. from shapely.ops import orient
  16. if isinstance(polygon, shapely.MultiPolygon):
  17. return Path.make_compound_path(
  18. *[_path_from_polygon(poly) for poly in polygon.geoms]
  19. )
  20. else:
  21. polygon = orient(polygon)
  22. return Path.make_compound_path(
  23. Path(np.asarray(polygon.exterior.coords)[:, :2]),
  24. *[Path(np.asarray(ring.coords)[:, :2]) for ring in polygon.interiors],
  25. )
  26. def patch_from_polygon(polygon, **kwargs):
  27. """Get a Matplotlib patch from a (Multi)Polygon.
  28. Note: this function is experimental, and mainly targeting (interactive)
  29. exploration, debugging and illustration purposes.
  30. Parameters
  31. ----------
  32. polygon : shapely.Polygon or shapely.MultiPolygon
  33. The polygon to convert to a Matplotlib Patch.
  34. **kwargs
  35. Additional keyword arguments passed to the matplotlib Patch.
  36. Returns
  37. -------
  38. Matplotlib artist (PathPatch)
  39. """
  40. from matplotlib.patches import PathPatch
  41. return PathPatch(_path_from_polygon(polygon), **kwargs)
  42. def plot_polygon(
  43. polygon,
  44. ax=None,
  45. add_points=True,
  46. color=None,
  47. facecolor=None,
  48. edgecolor=None,
  49. linewidth=None,
  50. **kwargs,
  51. ):
  52. """Plot a (Multi)Polygon.
  53. Note: this function is experimental, and mainly targeting (interactive)
  54. exploration, debugging and illustration purposes.
  55. Parameters
  56. ----------
  57. polygon : shapely.Polygon or shapely.MultiPolygon
  58. The polygon to plot.
  59. ax : matplotlib Axes, default None
  60. The axes on which to draw the plot. If not specified, will get the
  61. current active axes or create a new figure.
  62. add_points : bool, default True
  63. If True, also plot the coordinates (vertices) as points.
  64. color : matplotlib color specification
  65. Color for both the polygon fill (face) and boundary (edge). By default,
  66. the fill is using an alpha of 0.3. You can specify `facecolor` and
  67. `edgecolor` separately for greater control.
  68. facecolor : matplotlib color specification
  69. Color for the polygon fill.
  70. edgecolor : matplotlib color specification
  71. Color for the polygon boundary.
  72. linewidth : float
  73. The line width for the polygon boundary.
  74. **kwargs
  75. Additional keyword arguments passed to the matplotlib Patch.
  76. Returns
  77. -------
  78. Matplotlib artist (PathPatch), if `add_points` is false.
  79. A tuple of Matplotlib artists (PathPatch, Line2D), if `add_points` is true.
  80. """
  81. from matplotlib import colors
  82. if ax is None:
  83. ax = _default_ax()
  84. if color is None:
  85. color = "C0"
  86. color = colors.to_rgba(color)
  87. if facecolor is None:
  88. facecolor = list(color)
  89. facecolor[-1] = 0.3
  90. facecolor = tuple(facecolor)
  91. if edgecolor is None:
  92. edgecolor = color
  93. patch = patch_from_polygon(
  94. polygon, facecolor=facecolor, edgecolor=edgecolor, linewidth=linewidth, **kwargs
  95. )
  96. ax.add_patch(patch)
  97. ax.autoscale_view()
  98. if add_points:
  99. line = plot_points(polygon, ax=ax, color=color)
  100. return patch, line
  101. return patch
  102. def plot_line(line, ax=None, add_points=True, color=None, linewidth=2, **kwargs):
  103. """Plot a (Multi)LineString/LinearRing.
  104. Note: this function is experimental, and mainly targeting (interactive)
  105. exploration, debugging and illustration purposes.
  106. Parameters
  107. ----------
  108. line : shapely.LineString or shapely.LinearRing
  109. The line to plot.
  110. ax : matplotlib Axes, default None
  111. The axes on which to draw the plot. If not specified, will get the
  112. current active axes or create a new figure.
  113. add_points : bool, default True
  114. If True, also plot the coordinates (vertices) as points.
  115. color : matplotlib color specification
  116. Color for the line (edgecolor under the hood) and points.
  117. linewidth : float, default 2
  118. The line width for the polygon boundary.
  119. **kwargs
  120. Additional keyword arguments passed to the matplotlib Patch.
  121. Returns
  122. -------
  123. Matplotlib artist (PathPatch)
  124. """
  125. from matplotlib.patches import PathPatch
  126. from matplotlib.path import Path
  127. if ax is None:
  128. ax = _default_ax()
  129. if color is None:
  130. color = "C0"
  131. if isinstance(line, shapely.MultiLineString):
  132. path = Path.make_compound_path(
  133. *[Path(np.asarray(mline.coords)[:, :2]) for mline in line.geoms]
  134. )
  135. else:
  136. path = Path(np.asarray(line.coords)[:, :2])
  137. patch = PathPatch(
  138. path, facecolor="none", edgecolor=color, linewidth=linewidth, **kwargs
  139. )
  140. ax.add_patch(patch)
  141. ax.autoscale_view()
  142. if add_points:
  143. line = plot_points(line, ax=ax, color=color)
  144. return patch, line
  145. return patch
  146. def plot_points(geom, ax=None, color=None, marker="o", **kwargs):
  147. """Plot a Point/MultiPoint or the vertices of any other geometry type.
  148. Parameters
  149. ----------
  150. geom : shapely.Geometry
  151. Any shapely Geometry object, from which all vertices are extracted
  152. and plotted.
  153. ax : matplotlib Axes, default None
  154. The axes on which to draw the plot. If not specified, will get the
  155. current active axes or create a new figure.
  156. color : matplotlib color specification
  157. Color for the filled points. You can use `markeredgecolor` and
  158. `markerfacecolor` to have different edge and fill colors.
  159. marker : str, default "o"
  160. The matplotlib marker for the points.
  161. **kwargs
  162. Additional keyword arguments passed to matplotlib `plot` (Line2D).
  163. Returns
  164. -------
  165. Matplotlib artist (Line2D)
  166. """
  167. if ax is None:
  168. ax = _default_ax()
  169. coords = shapely.get_coordinates(geom)
  170. (line,) = ax.plot(
  171. coords[:, 0], coords[:, 1], linestyle="", marker=marker, color=color, **kwargs
  172. )
  173. return line