3
3
# ' Polygons are very similar to paths (as drawn by [geom_path()])
4
4
# ' except that the start and end points are connected and the inside is
5
5
# ' coloured by `fill`. The `group` aesthetic determines which cases
6
- # ' are connected together into a polygon.
6
+ # ' are connected together into a polygon. From R 3.6 and onwards it is possible
7
+ # ' to draw polygons with holes by providing a subgroup aesthetic that
8
+ # ' differentiates the outer ring points from those describing holes in the
9
+ # ' polygon.
7
10
# '
8
11
# ' @eval rd_aesthetics("geom", "polygon")
9
12
# ' @seealso
12
15
# ' @export
13
16
# ' @inheritParams layer
14
17
# ' @inheritParams geom_point
18
+ # ' @param rule Either `"evenodd"` or `"winding"`. If polygons with holes are
19
+ # ' being drawn (using the `subgroup` aesthetic) this argument defines how the
20
+ # ' hole coordinates are interpreted. See the examples in [grid::pathGrob()] for
21
+ # ' an explanation.
15
22
# ' @examples
16
23
# ' # When using geom_polygon, you will typically need two data frames:
17
24
# ' # one contains the coordinates of each polygon (positions), and the
52
59
# '
53
60
# ' # And if the positions are in longitude and latitude, you can use
54
61
# ' # coord_map to produce different map projections.
62
+ # '
63
+ # ' if (packageVersion("grid") >= "3.6") {
64
+ # ' # As of R version 3.6 geom_polygon() supports polygons with holes
65
+ # ' # Use the subgroup aesthetic to differentiate holes from the main polygon
66
+ # '
67
+ # ' holes <- do.call(rbind, lapply(split(datapoly, datapoly$id), function(df) {
68
+ # ' df$x <- df$x + 0.5 * (mean(df$x) - df$x)
69
+ # ' df$y <- df$y + 0.5 * (mean(df$y) - df$y)
70
+ # ' df
71
+ # ' }))
72
+ # ' datapoly$subid <- 1L
73
+ # ' holes$subid <- 2L
74
+ # ' datapoly <- rbind(datapoly, holes)
75
+ # '
76
+ # ' p <- ggplot(datapoly, aes(x = x, y = y)) +
77
+ # ' geom_polygon(aes(fill = value, group = id, subgroup = subid))
78
+ # ' p
79
+ # ' }
80
+ # '
55
81
geom_polygon <- function (mapping = NULL , data = NULL ,
56
82
stat = " identity" , position = " identity" ,
83
+ rule = " evenodd" ,
57
84
... ,
58
85
na.rm = FALSE ,
59
86
show.legend = NA ,
@@ -68,6 +95,7 @@ geom_polygon <- function(mapping = NULL, data = NULL,
68
95
inherit.aes = inherit.aes ,
69
96
params = list (
70
97
na.rm = na.rm ,
98
+ rule = rule ,
71
99
...
72
100
)
73
101
)
@@ -78,35 +106,69 @@ geom_polygon <- function(mapping = NULL, data = NULL,
78
106
# ' @usage NULL
79
107
# ' @export
80
108
GeomPolygon <- ggproto(" GeomPolygon" , Geom ,
81
- draw_panel = function (data , panel_params , coord ) {
109
+ draw_panel = function (data , panel_params , coord , rule = " evenodd " ) {
82
110
n <- nrow(data )
83
111
if (n == 1 ) return (zeroGrob())
84
112
85
113
munched <- coord_munch(coord , data , panel_params )
86
- # Sort by group to make sure that colors, fill, etc. come in same order
87
- munched <- munched [order(munched $ group ), ]
88
114
89
- # For gpar(), there is one entry per polygon (not one entry per point).
90
- # We'll pull the first value from each group, and assume all these values
91
- # are the same within each group.
92
- first_idx <- ! duplicated(munched $ group )
93
- first_rows <- munched [first_idx , ]
115
+ if (is.null(munched $ subgroup )) {
116
+ # Sort by group to make sure that colors, fill, etc. come in same order
117
+ munched <- munched [order(munched $ group ), ]
118
+
119
+ # For gpar(), there is one entry per polygon (not one entry per point).
120
+ # We'll pull the first value from each group, and assume all these values
121
+ # are the same within each group.
122
+ first_idx <- ! duplicated(munched $ group )
123
+ first_rows <- munched [first_idx , ]
94
124
95
- ggname(" geom_polygon" ,
96
- polygonGrob(munched $ x , munched $ y , default.units = " native" ,
97
- id = munched $ group ,
98
- gp = gpar(
99
- col = first_rows $ colour ,
100
- fill = alpha(first_rows $ fill , first_rows $ alpha ),
101
- lwd = first_rows $ size * .pt ,
102
- lty = first_rows $ linetype
125
+ ggname(
126
+ " geom_polygon" ,
127
+ polygonGrob(
128
+ munched $ x , munched $ y , default.units = " native" ,
129
+ id = munched $ group ,
130
+ gp = gpar(
131
+ col = first_rows $ colour ,
132
+ fill = alpha(first_rows $ fill , first_rows $ alpha ),
133
+ lwd = first_rows $ size * .pt ,
134
+ lty = first_rows $ linetype
135
+ )
103
136
)
104
137
)
105
- )
138
+ } else {
139
+ if (utils :: packageVersion(' grid' ) < " 3.6" ) {
140
+ stop(" Polygons with holes requires R 3.6 or above" , call. = FALSE )
141
+ }
142
+ # Sort by group to make sure that colors, fill, etc. come in same order
143
+ munched <- munched [order(munched $ group , munched $ subgroup ), ]
144
+ id <- match(munched $ subgroup , unique(munched $ subgroup ))
145
+
146
+ # For gpar(), there is one entry per polygon (not one entry per point).
147
+ # We'll pull the first value from each group, and assume all these values
148
+ # are the same within each group.
149
+ first_idx <- ! duplicated(munched $ group )
150
+ first_rows <- munched [first_idx , ]
151
+
152
+ ggname(
153
+ " geom_polygon" ,
154
+ pathGrob(
155
+ munched $ x , munched $ y , default.units = " native" ,
156
+ id = id , pathId = munched $ group ,
157
+ rule = rule ,
158
+ gp = gpar(
159
+ col = first_rows $ colour ,
160
+ fill = alpha(first_rows $ fill , first_rows $ alpha ),
161
+ lwd = first_rows $ size * .pt ,
162
+ lty = first_rows $ linetype
163
+ )
164
+ )
165
+ )
166
+ }
167
+
106
168
},
107
169
108
170
default_aes = aes(colour = " NA" , fill = " grey20" , size = 0.5 , linetype = 1 ,
109
- alpha = NA ),
171
+ alpha = NA , subgroup = NULL ),
110
172
111
173
handle_na = function (data , params ) {
112
174
data
0 commit comments