|
9 | 9 | import numpy as np |
10 | 10 | import pandas as pd |
11 | 11 | import shapely |
| 12 | +from matplotlib.patches import Polygon |
12 | 13 | from mpl_toolkits.axes_grid1 import make_axes_locatable |
13 | 14 | from numpy.typing import NDArray |
14 | 15 |
|
|
39 | 40 | PEDPY_RED = (233 / 255, 117 / 255, 134 / 255) |
40 | 41 |
|
41 | 42 |
|
| 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 | + |
42 | 124 | def _plot_series( # pylint: disable=too-many-arguments |
43 | 125 | axes: matplotlib.axes.Axes, |
44 | 126 | title: str, |
@@ -362,8 +444,13 @@ def plot_neighborhood( |
362 | 444 | color = neighbor_color |
363 | 445 | alpha = 0.5 |
364 | 446 |
|
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 | + ) |
367 | 454 | axes.set_aspect("equal") |
368 | 455 |
|
369 | 456 | return axes |
@@ -521,21 +608,21 @@ def plot_walkable_area( |
521 | 608 | hole_color = kwargs.pop("hole_color", "lightgrey") |
522 | 609 | hole_alpha = kwargs.pop("hole_alpha", 1.0) |
523 | 610 |
|
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, |
528 | 619 | ) |
529 | 620 |
|
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 | | - |
536 | 621 | axes.set_xlabel(r"x/m") |
537 | 622 | axes.set_ylabel(r"y/m") |
538 | 623 |
|
| 624 | + axes.autoscale_view() |
| 625 | + |
539 | 626 | return axes |
540 | 627 |
|
541 | 628 |
|
@@ -660,25 +747,23 @@ def plot_measurement_setup( |
660 | 747 | if axes is None: |
661 | 748 | axes = plt.gca() |
662 | 749 |
|
| 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 | + |
663 | 761 | if walkable_area is not None: |
664 | 762 | plot_walkable_area(walkable_area=walkable_area, axes=axes, **kwargs) |
665 | 763 |
|
666 | 764 | if traj is not None: |
667 | 765 | plot_trajectories(traj=traj, walkable_area=None, axes=axes, **kwargs) |
668 | 766 |
|
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 | | - |
682 | 767 | if measurement_lines is not None: |
683 | 768 | for measurement_line in measurement_lines: |
684 | 769 | axes.plot(*measurement_line.xy, color=ml_color, linewidth=ml_width) |
@@ -810,18 +895,23 @@ def inverse(values): |
810 | 895 | else: |
811 | 896 | color = np.array([1, 1, 1]) |
812 | 897 |
|
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, |
816 | 904 | ) |
817 | 905 |
|
818 | 906 | if INTERSECTION_COL in data.columns: |
819 | 907 | if not shapely.is_empty(row[INTERSECTION_COL]): |
820 | 908 | 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, |
825 | 915 | ) |
826 | 916 |
|
827 | 917 | if traj_data: |
|
0 commit comments