1111# ' The \code{location} aesthetic is required.
1212# ' \code{geom_geoscatter()} understands the same aesthetics as [ggplot2::geom_point()].
1313# '
14- # ' @param sample_type sampling type (see the \code{type} argument of [sf::st_sample()]).
15- # ' \code{"random"} will place points randomly inside the boundaries, whereas
16- # ' \code{"regular"} and \code{"hexagonal"} will evenly space points, leaving
17- # ' a small margin close to the boundaries.
1814# ' @inheritParams ggmapinset::geom_sf_inset
1915# ' @inheritParams stat_geoscatter
2016# '
2723# ' cartographer::nc_type_example_2 |>
2824# ' ggplot(aes(location = county)) +
2925# ' geom_boundaries(feature_type = "sf.nc") +
30- # ' geom_geoscatter(aes(colour = type), size = 0.5) +
26+ # ' geom_geoscatter(aes(colour = type), size = 0.5, seed = 123 ) +
3127# ' coord_automap(feature_type = "sf.nc")
3228geom_geoscatter <- function (
3329 mapping = aes(),
@@ -37,33 +33,28 @@ geom_geoscatter <- function(
3733 ... ,
3834 feature_type = NA ,
3935 sample_type = " random" ,
36+ seed = 12345 ,
4037 inset = waiver(),
4138 map_base = " clip" ,
4239 map_inset = " auto" ,
4340 na.rm = TRUE ,
4441 show.legend = " point" ,
4542 inherit.aes = TRUE
4643) {
47- sample_type <- rlang :: arg_match0(
48- sample_type ,
49- c(" random" , " regular" , " hexagonal" )
50- )
51-
52- params <- rlang :: list2(
53- feature_type = feature_type ,
54- sample_type = sample_type ,
55- na.rm = na.rm ,
56- ...
57- )
58-
5944 build_sf_inset_layers(
6045 data = data ,
6146 mapping = mapping ,
6247 stat = stat ,
6348 position = position ,
6449 show.legend = show.legend ,
6550 inherit.aes = inherit.aes ,
66- params = params ,
51+ params = rlang :: list2(
52+ feature_type = feature_type ,
53+ sample_type = sample_type ,
54+ na.rm = na.rm ,
55+ seed = seed ,
56+ ...
57+ ),
6758 inset = inset ,
6859 map_base = map_base ,
6960 map_inset = map_inset
@@ -82,6 +73,8 @@ geom_geoscatter <- function(
8273# ' \code{"random"} will place points randomly inside the boundaries, whereas
8374# ' \code{"regular"} and \code{"hexagonal"} will evenly space points, leaving
8475# ' a small margin close to the boundaries.
76+ # ' @param seed random seed, used when `sample_type` is `"random"`.
77+ # ' When `NA`, the global seed, if any, is used instead of a fixed seed.
8578# ' @param mapping,data,stat,geom,position,na.rm,show.legend,inherit.aes,... See [ggplot2::geom_sf()].
8679# ' @inheritParams cartographer::resolve_feature_type
8780# '
@@ -94,14 +87,10 @@ stat_geoscatter <- function(
9487 ... ,
9588 feature_type = NA ,
9689 sample_type = " random" ,
90+ seed = 12345 ,
9791 show.legend = NA ,
9892 inherit.aes = TRUE
9993) {
100- sample_type <- rlang :: arg_match0(
101- sample_type ,
102- c(" random" , " regular" , " hexagonal" )
103- )
104-
10594 ggplot2 :: layer_sf(
10695 data = data ,
10796 mapping = mapping ,
@@ -113,6 +102,7 @@ stat_geoscatter <- function(
113102 params = rlang :: list2(
114103 feature_type = feature_type ,
115104 sample_type = sample_type ,
105+ seed = seed ,
116106 ...
117107 )
118108 )
@@ -124,40 +114,87 @@ stat_geoscatter <- function(
124114# '
125115# ' @importFrom rlang .data
126116# ' @export
127- StatGeoscatter <- ggplot2 :: ggproto(" StatGeoscatter" , ggmapinset :: StatSfInset ,
117+ StatGeoscatter <- ggproto(
118+ " StatGeoscatter" ,
119+ ggmapinset :: StatSfInset ,
128120 required_aes = c(" location" ),
129121
130- compute_panel = function (data , scales , coord , feature_type = NA , sample_type = " random" ) {
122+ setup_params = function (data , params ) {
123+ params $ sample_type <- rlang :: arg_match0(
124+ params $ sample_type ,
125+ c(" random" , " regular" , " hexagonal" )
126+ )
127+
128+ params
129+ },
130+
131+ compute_panel = function (
132+ data ,
133+ scales ,
134+ coord ,
135+ feature_type = NA ,
136+ sample_type = " random" ,
137+ seed = NA
138+ ) {
139+ if (sample_type == " random" && ! is.na(seed )) {
140+ old_seed <- .GlobalEnv $ .Random.seed
141+ on.exit(
142+ {
143+ if (! is.null(old_seed )) {
144+ .GlobalEnv $ .Random.seed <- old_seed
145+ } else {
146+ rm(" .Random.seed" , envir = .GlobalEnv )
147+ }
148+ },
149+ add = TRUE
150+ )
151+ set.seed(seed )
152+ }
153+
131154 feature_type <- get_feature_type(feature_type , coord , data $ location )
132- data $ location <- cartographer :: resolve_feature_names(data $ location , feature_type )
155+ data $ location <- cartographer :: resolve_feature_names(
156+ data $ location ,
157+ feature_type
158+ )
133159
134160 data $ ggautomap__row <- seq_len(nrow(data ))
135161
136- coords <- dplyr :: group_modify(dplyr :: group_by(data , .data $ location ), function (dat , grp ) {
137- geom <- cartographer :: map_sfc(grp $ location [[1 ]], feature_type )
162+ coords <- dplyr :: group_modify(
163+ dplyr :: group_by(data , .data $ location ),
164+ function (dat , grp ) {
165+ geom <- cartographer :: map_sfc(grp $ location [[1 ]], feature_type )
138166
139- # work in a CRS that isn't distorted near the middle of the map
140- crs_orig <- sf :: st_crs(geom )
141- crs_working <- crs_eqc_midpoint(feature_type )
142- geom <- sf :: st_transform(geom , crs = crs_working )
167+ # work in a CRS that isn't distorted near the middle of the map
168+ crs_orig <- sf :: st_crs(geom )
169+ crs_working <- crs_eqc_midpoint(feature_type )
170+ geom <- sf :: st_transform(geom , crs = crs_working )
143171
144- # shrink the geom a little so we don't get points near boundaries
145- bbox <- sf :: st_bbox(geom )
146- geom <- sf :: st_buffer(geom , dist = - 0.05 * min(bbox [[3 ]] - bbox [[1 ]], bbox [[4 ]] - bbox [[2 ]]))
172+ # shrink the geom a little so we don't get points near boundaries
173+ bbox <- sf :: st_bbox(geom )
174+ geom <- sf :: st_buffer(
175+ geom ,
176+ dist = - 0.05 * min(bbox [[3 ]] - bbox [[1 ]], bbox [[4 ]] - bbox [[2 ]])
177+ )
147178
148- size <- nrow(dat )
149- if (sample_type != " random" ) {
150- size <- as.integer(size * 1.3 )
151- }
179+ size <- nrow(dat )
180+ if (sample_type != " random" ) {
181+ size <- as.integer(size * 1.3 )
182+ }
152183
153- # FIXME ... additional arguments passed to [sf::st_sample()].
154- points <- sf :: st_sample(geom , size = size , type = sample_type , exact = TRUE )
155- points <- sf :: st_transform(points , crs = crs_orig )
156- # FIXME: it's still possible for non-random modes to generate too few points
184+ # FIXME ... additional arguments passed to [sf::st_sample()].
185+ points <- sf :: st_sample(
186+ geom ,
187+ size = size ,
188+ type = sample_type ,
189+ exact = TRUE
190+ )
191+ points <- sf :: st_transform(points , crs = crs_orig )
192+ # FIXME: it's still possible for non-random modes to generate too few points
157193
158- dat $ geometry <- points
159- dat
160- })
194+ dat $ geometry <- points
195+ dat
196+ }
197+ )
161198 coords <- dplyr :: ungroup(coords )
162199 coords <- dplyr :: arrange(coords , .data $ ggautomap__row )
163200 coords <- coords [, setdiff(names(coords ), " ggautomap__row" )]
0 commit comments