Skip to content

Commit f309230

Browse files
authored
Add function to plot polygons (#347)
The new function now plots the polygons correctly, meaning no longer overlapping of the fill color and the lines for narrow line widths. This will also remove the error of gaps in the boundary at the closing point of the polygons.
1 parent 5327f57 commit f309230

1 file changed

Lines changed: 122 additions & 32 deletions

File tree

pedpy/plotting/plotting.py

Lines changed: 122 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import numpy as np
1010
import pandas as pd
1111
import shapely
12+
from matplotlib.patches import Polygon
1213
from mpl_toolkits.axes_grid1 import make_axes_locatable
1314
from numpy.typing import NDArray
1415

@@ -39,6 +40,87 @@
3940
PEDPY_RED = (233 / 255, 117 / 255, 134 / 255)
4041

4142

43+
def _plot_polygon(
44+
*,
45+
axes: matplotlib.axes.Axes,
46+
polygon: shapely.Polygon,
47+
polygon_color: Any,
48+
polygon_alpha: float = 1,
49+
line_color: Any = PEDPY_GREY,
50+
line_width: float = 1,
51+
hole_color: Any = "lightgrey",
52+
hole_alpha: float = 1,
53+
) -> matplotlib.axes.Axes:
54+
"""Plot the shapely polygon (including holes).
55+
56+
Args:
57+
polygon (shapely.Polygon): polygon to plot
58+
axes (matplotlib.axes.Axes): Axes to plot on, if None new will be created
59+
polygon_color (Any): background color of the polygon
60+
polygon_alpha (float): alpha of the background for the polygon
61+
line_color (Any): color of the borders
62+
line_width (float): line width of the borders
63+
hole_color (Any): background color of holes
64+
hole_alpha (float): alpha of background color for holes
65+
66+
Returns:
67+
matplotlib.axes.Axes instance where the polygon is plotted
68+
69+
"""
70+
# Plot the boundary of the polygon/holes separately to get the same color
71+
# as the outside as alpha modifies all colors
72+
73+
# Plot the exterior of the polygon
74+
exterior_coords = list(polygon.exterior.coords)
75+
exterior_polygon_border = Polygon(
76+
exterior_coords,
77+
edgecolor=line_color,
78+
facecolor="none",
79+
linewidth=line_width,
80+
closed=True,
81+
zorder=1000,
82+
)
83+
axes.add_patch(exterior_polygon_border)
84+
85+
exterior_polygon_fill = Polygon(
86+
exterior_coords,
87+
edgecolor="none",
88+
facecolor=polygon_color,
89+
linewidth=line_width,
90+
alpha=polygon_alpha,
91+
closed=True,
92+
zorder=1000,
93+
)
94+
axes.add_patch(exterior_polygon_fill)
95+
96+
# Plot the interiors (holes) of the polygon
97+
for interior in polygon.interiors:
98+
interior_coords = list(interior.coords)
99+
interior_polygon_border = Polygon(
100+
interior_coords,
101+
edgecolor=line_color,
102+
facecolor="none",
103+
linewidth=line_width,
104+
alpha=1,
105+
closed=True,
106+
zorder=1000,
107+
)
108+
axes.add_patch(interior_polygon_border)
109+
110+
interior_polygon_fill = Polygon(
111+
interior_coords,
112+
edgecolor="none",
113+
facecolor=hole_color,
114+
linewidth=line_width,
115+
alpha=hole_alpha,
116+
closed=True,
117+
zorder=1000,
118+
)
119+
axes.add_patch(interior_polygon_fill)
120+
121+
return axes
122+
123+
42124
def _plot_series( # pylint: disable=too-many-arguments
43125
axes: matplotlib.axes.Axes,
44126
title: str,
@@ -362,8 +444,13 @@ def plot_neighborhood(
362444
color = neighbor_color
363445
alpha = 0.5
364446

365-
axes.plot(*poly.exterior.xy, alpha=1, color=color)
366-
axes.fill(*poly.exterior.xy, alpha=alpha, color=color)
447+
_plot_polygon(
448+
axes=axes,
449+
polygon=poly,
450+
line_color=color,
451+
polygon_color=color,
452+
polygon_alpha=alpha,
453+
)
367454
axes.set_aspect("equal")
368455

369456
return axes
@@ -521,21 +608,21 @@ def plot_walkable_area(
521608
hole_color = kwargs.pop("hole_color", "lightgrey")
522609
hole_alpha = kwargs.pop("hole_alpha", 1.0)
523610

524-
axes.plot(
525-
*walkable_area.polygon.exterior.xy,
526-
color=line_color,
527-
linewidth=line_width,
611+
axes = _plot_polygon(
612+
polygon=walkable_area.polygon,
613+
polygon_color="none",
614+
line_color=line_color,
615+
line_width=line_width,
616+
hole_color=hole_color,
617+
hole_alpha=hole_alpha,
618+
axes=axes,
528619
)
529620

530-
for hole in walkable_area.polygon.interiors:
531-
axes.plot(*hole.xy, color=line_color, linewidth=line_width)
532-
# Paint all holes first white, then with the desired color
533-
axes.fill(*hole.xy, color="w", alpha=1)
534-
axes.fill(*hole.xy, color=hole_color, alpha=hole_alpha)
535-
536621
axes.set_xlabel(r"x/m")
537622
axes.set_ylabel(r"y/m")
538623

624+
axes.autoscale_view()
625+
539626
return axes
540627

541628

@@ -660,25 +747,23 @@ def plot_measurement_setup(
660747
if axes is None:
661748
axes = plt.gca()
662749

750+
if measurement_areas is not None:
751+
for measurement_area in measurement_areas:
752+
_plot_polygon(
753+
axes=axes,
754+
polygon=measurement_area.polygon,
755+
line_color=ma_line_color,
756+
line_width=ma_line_width,
757+
polygon_alpha=ma_alpha,
758+
polygon_color=ma_color,
759+
)
760+
663761
if walkable_area is not None:
664762
plot_walkable_area(walkable_area=walkable_area, axes=axes, **kwargs)
665763

666764
if traj is not None:
667765
plot_trajectories(traj=traj, walkable_area=None, axes=axes, **kwargs)
668766

669-
if measurement_areas is not None:
670-
for measurement_area in measurement_areas:
671-
axes.plot(
672-
*measurement_area.polygon.exterior.xy,
673-
color=ma_line_color,
674-
linewidth=ma_line_width,
675-
)
676-
axes.fill(
677-
*measurement_area.polygon.exterior.xy,
678-
color=ma_color,
679-
alpha=ma_alpha,
680-
)
681-
682767
if measurement_lines is not None:
683768
for measurement_line in measurement_lines:
684769
axes.plot(*measurement_line.xy, color=ml_color, linewidth=ml_width)
@@ -810,18 +895,23 @@ def inverse(values):
810895
else:
811896
color = np.array([1, 1, 1])
812897

813-
axes.plot(*poly.exterior.xy, alpha=1, color=voronoi_border_color)
814-
axes.fill(
815-
*poly.exterior.xy, facecolor=color, alpha=voronoi_outside_ma_alpha
898+
_plot_polygon(
899+
axes=axes,
900+
polygon=poly,
901+
line_color=voronoi_border_color,
902+
polygon_color=color,
903+
polygon_alpha=voronoi_outside_ma_alpha,
816904
)
817905

818906
if INTERSECTION_COL in data.columns:
819907
if not shapely.is_empty(row[INTERSECTION_COL]):
820908
intersection_poly = row[INTERSECTION_COL]
821-
axes.fill(
822-
*intersection_poly.exterior.xy,
823-
facecolor=color,
824-
alpha=voronoi_inside_ma_alpha,
909+
_plot_polygon(
910+
axes=axes,
911+
polygon=intersection_poly,
912+
line_color="none",
913+
polygon_color=color,
914+
polygon_alpha=voronoi_inside_ma_alpha,
825915
)
826916

827917
if traj_data:

0 commit comments

Comments
 (0)