Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plot.cca with optimize=TRUE and ordipointlabel in pipe #674

Merged
merged 26 commits into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3a23391
ordipointlabel can be used in a pipe ... almost
jarioksa Jul 19, 2024
3b8d92b
ordipointlabel(decorana(dune)) failed (perhaps some other, too)
jarioksa Jul 20, 2024
c60754a
plot.ordipointlable: message on size differences of new and original
jarioksa Jul 20, 2024
5a74486
ordipointlabel: tweak optimization rules
jarioksa Jul 21, 2024
2c1f98e
ordipointlabel: do not pass dot args to ordiplot(..., type="n")
jarioksa Jul 22, 2024
b3e457e
ordipointlabel: recycle cex, font, pch, col for 'display'
jarioksa Jul 22, 2024
5b1beab
ordipointlabel: strwidth/height do not use vector cex, font
jarioksa Jul 22, 2024
6988979
ordipointlabel gains arg 'label' & labels() function to see current ones
jarioksa Jul 22, 2024
6742b76
ordipointlabel failed with one 'display'; default cex to standard 0.7
jarioksa Jul 22, 2024
8598554
ordilabel: drqwing polygons is slow - dev.hold()/dev.flush() helps
jarioksa Jul 23, 2024
937466f
ordipointlabel gained arg 'bg' to draw text onnon-transparent background
jarioksa Jul 23, 2024
b4479b6
ordipointlabel *always* draws points, even with add=TRUE
jarioksa Jul 23, 2024
395c3b6
ordipointlabel did not set font with one 'display'
jarioksa Jul 23, 2024
1c28d06
importFrom(grDevices, dev.hold, dev.flush)
jarioksa Jul 23, 2024
2374ce0
ordipointlabel: default to add = TRUE when used in a pipe
jarioksa Jul 23, 2024
5dbb747
ordipointlabel: adapt help to using pipes
jarioksa Jul 25, 2024
d60b652
text.ordiplot and hence plot.cca can optimize text locations
jarioksa Jul 25, 2024
da6821f
scores.default: ignore 'display' if input is a single matrix
jarioksa Jul 25, 2024
4ef1752
ordilabel: border defaults to text colour
jarioksa Jul 25, 2024
3dc7ebb
plot.cca: set explicitly cex=1 instead of assuming text/points do so
jarioksa Jul 25, 2024
91b7a08
plot.cca: fix warnings on non-graphical arg 'optimize'
jarioksa Jul 26, 2024
fcf3299
text.ordiplot and hence plot.cca handle 'bg' with 'optimize'
jarioksa Jul 28, 2024
a23633e
plot.cca handles 'optimize = TRUE'
jarioksa Jul 28, 2024
9210de3
update help pages for ordipointlabel pipe and next stage of plot.cca
jarioksa Jul 28, 2024
965d70b
ordipointlabel could drop dimensions
jarioksa Jul 29, 2024
4d0eb85
plot.cca with optimize=T could over-write user parameters
jarioksa Jul 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ importFrom(graphics, abline, arrows, axis, barplot, box, contour,
import(permute) ## vegan depends on permute: import everything
importFrom(utils, news, vignette, combn, flush.console, modifyList,
object.size, read.fortran, read.fwf, str)
importFrom(grDevices, bmp, check.options, chull, col2rgb, dev.off,
heat.colors, jpeg, palette, pdf, png, postscript, rainbow,
importFrom(grDevices, bmp, check.options, chull, col2rgb, dev.hold, dev.flush,
dev.off, heat.colors, jpeg, palette, pdf, png, postscript, rainbow,
rgb, svg, tiff, xfig, xy.coords)
## import(grDevices) ## too many functions to be listed separately
## import(lattice) # vegan depends on lattice: import all
Expand Down Expand Up @@ -249,6 +249,7 @@ S3method(identify, ordiplot)
# labels: base
S3method(labels, cca)
S3method(labels, envfit)
S3method(labels, ordipointlabel)
# lines: graphics
S3method(lines, fitspecaccum)
S3method(lines, permat)
Expand Down
3 changes: 2 additions & 1 deletion R/ordiArgAbsorber.R
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
### List non-graphical arguments used in vegan plot commands
`ordiArgAbsorber` <- function(..., shrink, origin, scaling, triangular,
display, choices, const, truemean, FUN)
display, choices, const, truemean, optimize, FUN)
match.fun(FUN)(...)
12 changes: 6 additions & 6 deletions R/ordilabel.R
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,13 @@
w <- (strwidth(labels, cex=cex,...) + em/1.5)/2
h <- (strheight(labels, cex = cex, ...) + ex/1.5)/2
if (is.null(col))
if (!is.null(border))
col <- border
else
col <- par("fg")
col <- par("fg")
col <- rep(col, length=nrow(sco))[ord]
if(!is.null(border))
border <- rep(border, length=nrow(sco))[ord]
if (is.null(border))
border <- col
border <- rep(border, length=nrow(sco))[ord]
fill <- rep(fill, length=nrow(sco))[ord]
dev.hold()
for (i in 1:nrow(sco)) {
ordiArgAbsorber(sco[i,1] + c(-1,1,1,-1)*w[i],
sco[i,2] + c(-1,-1,1,1)*h[i],
Expand All @@ -42,6 +41,7 @@
ordiArgAbsorber(sco[i,1], sco[i,2], labels = labels[i], cex = cex,
col = col[i], xpd = xpd, FUN = text, ...)
}
dev.flush()
invisible(x)
}

111 changes: 80 additions & 31 deletions R/ordipointlabel.R
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
### Modelled after maptools:::pointLabel.
`ordipointlabel` <-
function(x, display = c("sites", "species"), choices = c(1,2), col=c(1,2),
pch=c("o","+"), font = c(1,1), cex=c(0.8, 0.8), add = FALSE,
select, ...)
pch=c("o","+"), font = c(1,1), cex=c(0.7, 0.7),
add = inherits(x, "ordiplot"), labels, bg, select, ...)
{
xy <- list()
## Some 'scores' accept only one 'display': a workaround
Expand All @@ -19,33 +19,50 @@
}
}
if (length(display) > 1) {
col <- rep(col, sapply(xy, nrow))
pch <- rep(pch, sapply(xy, nrow))
font <- rep(font, sapply(xy, nrow))
cex <- rep(cex, sapply(xy, nrow))
tmp <- xy[[1]]
for (i in 2:length(display))
tmp <- rbind(tmp, xy[[i]])
xy <- tmp
ld <- length(display)
col <- rep(rep(col, length=ld), sapply(xy, nrow))
pch <- rep(rep(pch, length=ld), sapply(xy, nrow))
font <- rep(rep(font, length=ld), sapply(xy, nrow))
cex <- rep(rep(cex, length=ld), sapply(xy, nrow))
if (!missing(bg))
fill <- rep(rep(bg, length=ld), sapply(xy, nrow))
xy <- do.call(rbind, xy)
}
else {
xy <- xy[[1]]
if (length(col) < nrow(xy))
col <- col[1]
col <- rep(col[1], nrow(xy))
if (length(pch) < nrow(xy))
pch <- pch[1]
pch <- rep(pch[1], nrow(xy))
if (length(cex) < nrow(xy))
cex <- rep(cex[1], length = nrow(xy))
if (length(font) < nrow(xy))
font <- font[1]
font <- rep(font[1], length = nrow(xy))
if (!missing(bg) && length(bg) < nrow(xy))
fill <- rep(bg[1], length = nrow(xy))
}
if (!add)
pl <- ordiplot(x, display = display, choices = choices, type="n", ...)
labels <- rownames(xy)
pl <- ordiplot(xy, display = "sites", type="n")
if (!missing(labels)) {
if (length(labels) != nrow(xy))
stop(gettextf(
"you need %d labels but arg 'labels' only had %d: arg ignored",
nrow(xy), length(labels)))
} else {
labels <- rownames(xy)
}
em <- strwidth("m", cex = min(cex), font = min(font))
ex <- strheight("x", cex = min(cex), font = min(font))
ltr <- em*ex
w <- strwidth(labels, cex = cex, font = font) + em
h <- strheight(labels, cex = cex, font = font) + ex
box <- cbind(w, h)
## bounding box: strwidth/height do not accept vector cex and font
## and we loop
box <- matrix(0, nrow(xy), 2)
for (i in seq_len(nrow(xy))) {
box[i,1] <- strwidth(labels[i], cex = cex[i], font = font[i]) +
strwidth("m", cex = cex[i], font = font[i])
box[i,2] <- strheight(labels[i], cex = cex[i], font = font[i]) +
strheight("x", cex = cex[i], font = font[i])
}
## offset: 1 up, 2..4 sides, 5..8 corners
makeoff <- function(pos, lab) {
cbind(c(0,1,0,-1,0.9,0.9,-0.9,-0.9)[pos] * lab[,1]/2,
Expand All @@ -69,14 +86,21 @@
k <- k[maylap]
jk <- sort(unique(c(j,k)))
## SANN: no. of iterations & starting positions
nit <- min(48 * length(jk), 10000)
pos <- rep(1, n)
## Criterion: overlap + penalty for positions other than directly
## above and especially for corners
nit <- min(64 * length(jk), 10000)
pos <- ifelse(xy[,2] > 0, 1, 3)
## Criterion: overlap + penalty for moving towards origin and also
## for corners. Penalty is mild: max 1 ltr and one-character
## string > 3*ltr due to padding (em, ex) of the bounding box.
fn <- function(pos) {
move <- makeoff(pos, matrix(1, 1, 2))
off <- makeoff(pos, box)
val <- sum(overlap(xy[j,]+off[j,], box[j,], xy[k,]+off[k,], box[k,]))
val <- val/ltr + sum(pos>1)*0.1 + sum(pos>4)*0.1
val <- sum(overlap(xy[j,,drop=FALSE]+off[j,,drop=FALSE],
box[j,,drop=FALSE],
xy[k,,drop=FALSE]+off[k,,drop=FALSE],
box[k,,drop=FALSE]))
val <- val/ltr + sum(move[,1] * xy[,1] < 0) * 0.4 +
sum(move[,2] * xy[,2] < 0) * 0.4 +
sum(pos > 4) * 0.2
}
## Move a label of one point
gr <- function(pos) {
Expand All @@ -87,15 +111,32 @@
## Simulated annealing
sol <- optim(par = pos, fn = fn, gr = gr, method="SANN",
control=list(maxit=nit))
if (!add)
##points(xy, pch = pch, col = col, cex=cex, ...)
ordiArgAbsorber(xy, pch = pch, col = col, cex = cex, FUN = points,
...)
lab <- xy + makeoff(sol$par, box)
dev.hold()
## draw optional lab background first so it does not cover points
if (!missing(bg)) {
for(i in seq_len(nrow(lab))) {
polygon(lab[i,1] + c(-1,1,1,-1)*box[i,1]/2.2,
lab[i,2] + c(-1,-1,1,1)*box[i,2]/2.2,
col = fill[i], border = col[i], xpd = TRUE)
ordiArgAbsorber(lab[i,1], lab[i,2], labels = labels[i],
col = col[i], cex = cex[i], font = font[i],
FUN = text, ...)
}
} else {
ordiArgAbsorber(lab, labels=labels, col = col, cex = cex,
font = font, FUN = text, ...)
}

## always plot points (heck, the function is ordi*point*label)
ordiArgAbsorber(xy, pch = pch, col = col, cex = cex, FUN = points,
...)
##text(lab, labels=labels, col = col, cex = cex, font = font, ...)
ordiArgAbsorber(lab, labels=labels, col = col, cex = cex, font = font,
FUN = text, ...)
pl <- list(points = xy)
dev.flush()
if (!inherits(x, "ordiplot"))
pl <- list(points = xy)
else
pl <- x
pl$labels <- lab
attr(pl$labels, "font") <- font
args <- list(tcex = cex, tcol = col, pch = pch, pcol = col,
Expand All @@ -107,3 +148,11 @@
class(pl) <- c("ordipointlabel", "orditkplot", class(pl))
invisible(pl)
}

### Extract labels: useful if arg labels= is given in ordipointlabel call

`labels.ordipointlabel` <-
function(object, ...)
{
rownames(object$labels)
}
30 changes: 23 additions & 7 deletions R/plot.cca.R
Original file line number Diff line number Diff line change
Expand Up @@ -72,29 +72,34 @@
if (length(g$centroids) > 0 && all(is.na(g$centroids))) NA else g$centroids[, 2],
na.rm = TRUE)
}
plot(g[[1]], xlim = xlim, ylim = ylim, type = "n", asp = 1,
...)
ordiArgAbsorber(g[[1]], xlim = xlim, ylim = ylim, type = "n", asp = 1,
FUN = plot, ...)
abline(h = 0, lty = 3)
abline(v = 0, lty = 3)
## set up lists for graphical parameters
GlobalPar <- list("type" = type)
dots <- match.call(expand.dots = FALSE)$...
if (!is.null(dots))
if (!is.null(dots)) {
## dots par optimize must be eval'ed to force it logical
if (!is.null(dots$optimize))
dots$optimize <- eval(dots$optimize)
GlobalPar <- modifyList(GlobalPar, dots)
}
## Default graphical parameters
defParText <- list("species" = list("col" = 2, "cex" = 0.7),
"sites" = list("cex" = 0.7),
"constraints" = list("col" = "darkgreen", "cex" = 0.7),
"biplot" = list("col" = "blue"),
"regression" = list("col" = "purple4"),
"centroids" = list("col" = "blue"))
"biplot" = list("col" = "blue", "cex" = 1.0),
"regression" = list("col" = "purple4", "cex" = 1.0),
"centroids" = list("col" = "blue", "cex" = 1.0))
defParPoints <- list("species" = list("col" = 2, "cex" = 0.7, "pch" = "+"),
"sites" = list("cex" = 0.7, pch = 1),
"constraints" = list("col" = "darkgreen", "cex" = 0.7,
"pch" = 2),
"biplot" = list("col" = "blue"),
"regression" = list("col" = "purple4"),
"centroids" = list("col" = "blue", "pch" = "x"))
"centroids" = list("col" = "blue", "pch" = "x",
"cex" = 1.0))
UserPar <- list("species" = spe.par,
"sites" = sit.par,
"constraints" = con.par,
Expand All @@ -118,6 +123,17 @@
par <- modifyList(par, GlobalPar)
if (!is.null(UserPar[[kind]]))
par <- modifyList(par, UserPar[[kind]])
## sanitize par combinations
if (score == "points") # points cannot be optimized
par <- modifyList(par, list(optimize = NULL))
else if (score == "text") {
if (isTRUE(par$optimize)) {
if (isTRUE(par$arrows) || kind %in% c("biplot", "regression"))
message("'optimize = TRUE' and arrows do not mix nicely")
if (is.null(par$pch)) # optimize=TRUE needs points
par <- modifyList(par, list(pch = defParPoints[[kind]]$pch))
}
}
## add arguments for text/points.ordiplot, remove type
par <- modifyList(par, list("x" = g, "what" = kind, "type" = NULL))
do.call(score, par)
Expand Down
5 changes: 5 additions & 0 deletions R/plot.ordipointlabel.R
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,10 @@ plot.ordipointlabel <- function (x, ...)
font <- par("font")
text(x$labels, rownames(x$labels), cex = x$args$tcex, col = x$args$tcol,
font = font, ...)
psize <- par("din")
if(any(abs(psize - x$dim)/x$dim > 0.1))
message(gettextf(
"original plot size was %.1f x %.1f, current is %.1f x %.1f",
x$dim[1], x$dim[2], psize[1], psize[2]))
invisible(x)
}
14 changes: 8 additions & 6 deletions R/scores.default.R
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
"scores.default" <-
`scores.default` <-
function (x, choices, display = c("sites", "species", "both"),
tidy = FALSE, ...)
{
display <- match.arg(display)
if (is.list(x)) {
## why not display <- match.arg(display, names(x)) ?
display <- match.arg(display)
if (tidy)
display <- "both"
att <- names(x)
}
X <- Y <- NULL
if (tidy)
display <- "both"
att <- names(x)
if (is.data.frame(x) && all(sapply(x, is.numeric)))
x <- as.matrix(x)
if (is.list(x) && display %in% c("sites", "both")) {
Expand Down Expand Up @@ -44,7 +47,6 @@
else { # "both" may be non-chalant: only warn
warning("cannot find species scores")
}

}
else if (is.numeric(x)) {
X <- as.matrix(x)
Expand Down
11 changes: 8 additions & 3 deletions R/text.ordiplot.R
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
`text.ordiplot` <-
function (x, what, labels, select, arrows = FALSE, length = 0.05,
arr.mul, bg, ...)
function (x, what, labels, select, optimize = FALSE, arrows = FALSE,
length = 0.05, arr.mul, bg, ...)
{
sco <- scores(x, what)
if (!missing(labels))
Expand All @@ -21,7 +21,12 @@
arrows(0, 0, sco[,1], sco[,2], length = length, ...)
sco <- ordiArrowTextXY(sco, rownames(sco), rescale = FALSE, ...)
}
if (missing(bg))
if (optimize) {
if (missing(bg))
ordipointlabel(sco, display = what, add = TRUE, ...)
else
ordipointlabel(sco, display = what, bg = bg, add = TRUE, ...)
} else if (missing(bg))
text(sco, labels = rownames(sco), ...)
else
ordilabel(sco, labels = rownames(sco), fill = bg, ...)
Expand Down
Loading
Loading