Skip to content

Commit d0531b1

Browse files
committed
feat: add "choro_point" legends
1 parent 2067d1b commit d0531b1

File tree

7 files changed

+617
-42
lines changed

7 files changed

+617
-42
lines changed

R/leg.R

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@
1414
#' * **prop_line** for proportional lines maps,
1515
#' * **grad_line** for graduated lines maps,
1616
#' * **histo** for histograms.
17+
#' * **choro_point** for choropleth points maps
1718
#' @param val
1819
#' vector of value(s) (for "prop" and "prop_line", at least c(min, max)
1920
#' for "cont"),
2021
#' vector of categories (for "symb" and "typo"),
21-
#' break labels (for "choro" and "grad_line"), histogram parameters
22+
#' break labels (for "choro", "choro_point" and "grad_line"), histogram parameters
2223
#' (for "histo").
2324
#' @param pos position of the legend. It can be one of 'topleft', 'top',
2425
#' 'topright', 'right', 'bottomright', 'bottom','bottomleft',
@@ -46,7 +47,8 @@
4647
#' @param bg background color of the legend
4748
#' @param fg foreground color of the legend
4849
#' @param box_border border color of legend boxes
49-
#' @param box_cex width and height size expansion for boxes, histogram or lines
50+
#' @param box_cex width and height size expansion for boxes, histogram
51+
#' circles, squares or lines
5052
#' @param mar plot margins
5153
#' @param return_bbox return only bounding box of the legend.
5254
#' No legend is plotted.
@@ -81,6 +83,7 @@
8183
#' * `leg(type = "prop_line", val, col, lwd, val_rnd, val_big, val_dec)`
8284
#' * `leg(type = "grad_line", val, col, lwd, val_rnd, val_big, val_dec)`
8385
#' * `leg(type = "histo", val, pal, box_border, val_rnd, val_big, val_dec)`
86+
#' * `leg(type = "choro_point", val, pal, symbol, border, cex, val_rnd, val_big, val_dec, col_na, no_data, no_data_txt, horiz)`
8487
#'
8588
#' @examples
8689
#' # minimal example
@@ -96,7 +99,6 @@
9699
#' type = "grad_line", val = c(1, 4, 10, 15), pos = "bottomright",
97100
#' lwd = c(1, 5, 10)
98101
#' )
99-
#'
100102
#' plot.new()
101103
#' plot.window(xlim = c(0, 1), ylim = c(0, 1), asp = 1)
102104
#' leg(type = "prop", val = c(10, 50, 100), pos = "topleft", horiz = TRUE)
@@ -105,6 +107,11 @@
105107
#' type = "cont", val = c(10, 20, 30, 40, 50), pos = "bottomleft",
106108
#' horiz = TRUE
107109
#' )
110+
#' leg(type = "choro_point", val = c(10, 20, 30, 40, 50), pos = "top")
111+
#' leg(
112+
#' type = "choro_point", val = c(10, 20, 30, 40, 50), pos = "right",
113+
#' horiz = TRUE
114+
#' )
108115
#' leg(
109116
#' type = "cont", val = c(10, 20, 30, 40, 50), pos = "topright",
110117
#' horiz = FALSE
@@ -249,6 +256,23 @@
249256
#' title = "Histogram"
250257
#' )
251258
#'
259+
#' plot.new()
260+
#' plot.window(xlim = c(0, 1), ylim = c(0, 1), asp = 1)
261+
#' leg(
262+
#' type = "choro_point",
263+
#' alpha = 1,
264+
#' val = c(10, 20, 30, 40, 50),
265+
#' pos = "top",
266+
#' pal = c("#7F000D", "#B56C6F", "#DBBABB", "#F1F1F1"),
267+
#' val_rnd = 2,
268+
#' col_na = "grey",
269+
#' no_data = TRUE,
270+
#' no_data_txt = "No data",
271+
#' border = "tomato4",
272+
#' cex = 1.5,
273+
#' title = "Choropleth (sequential)"
274+
#' )
275+
#'
252276
#' # Positions
253277
#' plot.new()
254278
#' plot.window(xlim = c(0, 1), ylim = c(0, 1), asp = 1)

R/lg_choro_point.R

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
#' @title Plot a legend for a choropleth map
2+
#' @description This function plots a legend for a choropleth map.
3+
#'
4+
#' @param pal a set of colors
5+
#' @param alpha if \code{pal} is a \link{hcl.colors} palette name, the
6+
#' alpha-transparency level in the range \[0,1\]
7+
#' @param col_na color for missing values
8+
#' @param pos position of the legend, one of "topleft", "top",
9+
#' "topright", "right", "bottomright", "bottom", "bottomleft",
10+
#' "left", "interactive" or a vector of two coordinates in map units
11+
#' (c(x, y))
12+
#' @param val break labels
13+
#' @param title title of the legend
14+
#' @param title_cex size of the legend title
15+
#' @param val_cex size of the values in the legend
16+
#' @param val_rnd number of decimal places of the values in
17+
#' the legend.
18+
#' @param val_dec decimal separator
19+
#' @param val_big thousands separator
20+
#' @param no_data if TRUE a "missing value" box is plotted
21+
#' @param no_data_txt label for missing values
22+
#' @param frame whether to add a frame to the legend (TRUE) or not (FALSE)
23+
#' @param border color of the boxes' borders
24+
#' @param size size of the legend; 2 means two times bigger
25+
#' @param bg background of the legend
26+
#' @param fg foreground of the legend
27+
#' @param cex width and height cex of boxes
28+
#' @param return_bbox return only bounding box of the legend.
29+
#' No legend is plotted.
30+
#' @param adj adj
31+
#' @param frame_border border color of the frame
32+
#' @keywords internal
33+
#' @noRd
34+
#' @import graphics
35+
#' @return No return value, a legend is displayed.
36+
#' @examples
37+
#' plot.new()
38+
#' plot.window(xlim = c(0, 1), ylim = c(0, 1), asp = 1)
39+
#' leg_choro(val = c(1, 2, 3, 4), pal = c("red1", "red3", "red4"))
40+
leg_choro_point <- function(pos = "left",
41+
val,
42+
pal = "Inferno",
43+
alpha = NULL,
44+
symbol = "circle",
45+
title = "Legend Title",
46+
title_cex = .8 * size,
47+
val_cex = .6 * size,
48+
val_rnd = 0,
49+
val_dec = ".",
50+
val_big = "",
51+
col_na = "white",
52+
no_data = FALSE,
53+
no_data_txt = "No Data",
54+
frame = FALSE,
55+
frame_border = fg,
56+
border = "#333333",
57+
bg = "#f7f7f7",
58+
fg = "#333333",
59+
size = 1,
60+
cex = 1,
61+
return_bbox = FALSE,
62+
adj = c(0, 0)) {
63+
# spacings
64+
x_spacing <- xinch(par("csi")) / 4
65+
y_spacing <- yinch(par("csi")) / 4
66+
67+
# boxes sizes
68+
w_box <- cex * size * x_spacing * 5 * 2 / 3
69+
h_box <- cex * size * y_spacing * 5 * 2 / 3
70+
71+
72+
# Nb. boxes and values
73+
n_val <- length(val)
74+
n_box <- n_val - 1
75+
if (n_val < 2) {
76+
stop("You need to provide at least two values for 'val'", call. = FALSE)
77+
}
78+
79+
# rounded and ordered values for the legend
80+
val <- rev(get_val_rnd(
81+
val = val, val_rnd = val_rnd, val_dec = val_dec,
82+
val_big = val_big
83+
))
84+
85+
# box colors
86+
pal <- rev(get_pal(pal, n_box, alpha = alpha))
87+
88+
# title dimensions
89+
title_dim <- get_title_dim(title, title_cex)
90+
91+
# boxes dimensions
92+
boxes_dim <- list(w = w_box, h = n_box * h_box)
93+
94+
# label dimension
95+
labels_dim <- list(
96+
w = max(strwidth(val, units = "user", cex = val_cex, font = 1)),
97+
h = strheight(val[1], units = "user", cex = val_cex, font = 1) / 2 +
98+
strheight(val[n_val], units = "user", cex = val_cex, font = 1) / 2 +
99+
n_box * h_box
100+
)
101+
102+
# NA box and label dimensions
103+
if (isTRUE(no_data)) {
104+
na_box_dim <- list(w = w_box, h = h_box)
105+
na_label_dim <- list(
106+
w = strwidth(no_data_txt, units = "user", cex = val_cex, font = 1),
107+
h = max(
108+
strheight(no_data_txt, units = "user", cex = val_cex, font = 1),
109+
h_box
110+
)
111+
)
112+
} else {
113+
na_box_dim <- list(w = 0, h = 0)
114+
na_label_dim <- list(w = 0, h = 0)
115+
no_data_txt <- ""
116+
}
117+
118+
# legend dimension
119+
legend_dim <- list(
120+
w = x_spacing +
121+
max(
122+
title_dim$w,
123+
boxes_dim$w + labels_dim$w + x_spacing,
124+
na_box_dim$w + na_label_dim$w + x_spacing
125+
) +
126+
x_spacing,
127+
h = y_spacing +
128+
ifelse(title_dim$h != 0, title_dim$h + 2 * y_spacing * size, 0) +
129+
labels_dim$h +
130+
ifelse(na_label_dim$h != 0, na_label_dim$h + y_spacing * size, 0) +
131+
y_spacing
132+
)
133+
134+
# get legend coordinates
135+
legend_coords <- get_legend_coords(
136+
pos = pos, legend_dim = legend_dim,
137+
adj = adj, frame = frame,
138+
x_spacing = x_spacing,
139+
y_spacing = y_spacing
140+
)
141+
142+
# return legend coordinates only
143+
if (return_bbox) {
144+
return(invisible(legend_coords))
145+
}
146+
147+
148+
# display frame
149+
plot_frame(
150+
frame = frame, legend_coords = legend_coords,
151+
bg = bg, frame_border = frame_border,
152+
x_spacing = x_spacing, y_spacing = y_spacing
153+
)
154+
155+
# display title
156+
plot_title(
157+
title = title, title_cex = title_cex, title_dim = title_dim,
158+
fg = fg, legend_coords = legend_coords,
159+
x_spacing = x_spacing, y_spacing = y_spacing
160+
)
161+
162+
# display boxes
163+
left <- rep(legend_coords$left + x_spacing, n_box)
164+
right <- left + w_box
165+
y <- legend_coords$top - y_spacing -
166+
ifelse(title_dim$h != 0, title_dim$h + 2 * y_spacing * size, 0) -
167+
strheight(val[1], units = "user", cex = val_cex, font = 1) / 2
168+
top <- rep(NA, n_box)
169+
for (i in 1:n_box) {
170+
top[i] <- y - (i - 1) * h_box
171+
}
172+
bottom <- top - h_box
173+
174+
if (symbol == "square") {
175+
rect(
176+
xleft = left, ybottom = bottom, xright = right, ytop = top,
177+
col = pal, border = border, lwd = .7
178+
)
179+
}
180+
181+
if (symbol == "circle") {
182+
symbols(
183+
x = left + (right - left) / 2, y = bottom + (top - bottom) / 2,
184+
circles = (right - left) / 2, inches = F, add = T,
185+
bg = pal, fg = border, lwd = .7
186+
)
187+
}
188+
189+
# display labels
190+
x <- rep(legend_coords$left + x_spacing + w_box + x_spacing, n_val)
191+
y <- rep(NA, n_val)
192+
top <- top[1]
193+
for (i in 1:n_val) {
194+
y[i] <- top - (i - 1) * h_box
195+
}
196+
text(x = x, y = y, labels = val, cex = val_cex, adj = c(0, 0.5), col = fg)
197+
198+
if (isTRUE(no_data)) {
199+
# display na box
200+
left <- legend_coords$left + x_spacing
201+
right <- left + w_box
202+
bottom <- legend_coords$bottom + y_spacing +
203+
(na_label_dim$h - na_box_dim$h) / 2
204+
top <- bottom + h_box
205+
if (symbol == "square") {
206+
rect(
207+
xleft = left, ybottom = bottom, xright = right, ytop = top,
208+
col = col_na, border = border, lwd = .7
209+
)
210+
}
211+
if (symbol == "circle") {
212+
symbols(
213+
x = left + (right - left) / 2, y = bottom + (top - bottom) / 2,
214+
circles = (right - left) / 2, inches = F, add = T,
215+
bg = col_na, fg = border, lwd = .7
216+
)
217+
}
218+
# display na label
219+
x <- legend_coords$left + x_spacing + w_box + x_spacing
220+
y <- bottom + (top - bottom) / 2
221+
text(
222+
x = x, y = y, labels = no_data_txt, cex = val_cex, adj = c(0, 0.5),
223+
col = fg
224+
)
225+
}
226+
return(invisible(NULL))
227+
}

0 commit comments

Comments
 (0)