Skip to content

Commit

Permalink
Perspective plot for marked ppp
Browse files Browse the repository at this point in the history
  • Loading branch information
baddstats committed Sep 7, 2024
1 parent 54cf854 commit 079f0f3
Show file tree
Hide file tree
Showing 7 changed files with 311 additions and 5 deletions.
4 changes: 2 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: spatstat.geom
Version: 3.3-2.001
Date: 2024-07-25
Version: 3.3-2.002
Date: 2024-09-07
Title: Geometrical Functionality of the 'spatstat' Family
Authors@R: c(person("Adrian", "Baddeley",
role = c("aut", "cre", "cph"),
Expand Down
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,7 @@ export("persp.funxy")
export("persp.im")
export("perspLines")
export("perspPoints")
export("persp.ppp")
export("perspSegments")
export("perspVisible")
export("pHcolour")
Expand Down Expand Up @@ -1494,6 +1495,7 @@ S3method("periodify", "ppp")
S3method("periodify", "psp")
S3method("persp", "funxy")
S3method("persp", "im")
S3method("persp", "ppp")
S3method("pixellate", "owin")
S3method("pixellate", "ppp")
S3method("pixellate", "psp")
Expand Down
11 changes: 10 additions & 1 deletion NEWS
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
CHANGES IN spatstat.geom VERSION 3.3-2.001
CHANGES IN spatstat.geom VERSION 3.3-2.002

OVERVIEW

o Perspective plot of spatial point pattern with numerical marks.

o Tweaks to documentation.

NEW FUNCTIONS

o persp.ppp
For a spatial point pattern with numeric marks,
generate a perspective plot in which each data point is
shown as a vertical spike, with height proportional to the mark value.

CHANGES IN spatstat.geom VERSION 3.3-2

OVERVIEW
Expand Down
162 changes: 162 additions & 0 deletions R/persp.ppp.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
#' persp.ppp.R
#'
#' Perspective plot for marked point pattern
#'
#' Copyright (C) Adrian Baddeley 2024
#' GPL Public Licence >= 2.0
#'
#' $Revision: 1.3 $ $Date: 2024/09/07 01:34:04 $

persp.ppp <- local({

persp.ppp <- function(x, ..., main, grid=TRUE, ngrid=10,
col.grid="grey", col.base="white",
win.args=list(),
spike.args=list(),
neg.args=list(), which.marks=1,
zlab=NULL, zlim=NULL,
zadjust=1) {
if(missing(main)) main <- short.deparse(substitute(x))
W <- Window(x)
R <- Frame(x)
marx <- marks(x)
dotargs <- list(...)
#' ensure numeric marks
mf <- markformat(marx)
switch(mf,
none = {
stop("point pattern must have marks", call.=FALSE)
},
vector = {
if(is.null(zlab)) zlab <- "mark"
},
dataframe = {
if(missing(which.marks)) {
marx <- numeric.columns(marx)
}
cn <- colnames(marx)
stopifnot(length(which.marks) == 1)
if(is.character(which.marks)) {
k <- match(which.marks, cn)
if(is.na(k))
stop(paste("unrecognised selection of mark column",
sQuote(which.marks)),
call.=FALSE)
which.marks <- k
}
if(missing(zlab)) zlab <- colnames(marx)[which.marks]
marx <- marx[,which.marks]
},
stop("marks should be a vector or a data frame", call.=FALSE)
)
marx <- as.numeric(marx)
#' rescale marks to a scale commensurate with window
#' (to achieve appropriate default scale in persp.default)
maxmark <- max(abs(marx))
if(maxmark > .Machine$double.eps) {
marx <- marx * (max(sidelengths(R))/maxmark)
}
#' set up perspective transformation and plot horizontal plane
Rplus <- grow.rectangle(R, fraction=1/(2*ngrid))
Z <- as.im(0, W=Rplus, dimyx=rev(ngrid)+1)
if(is.null(zlim)) zlim <- range(marx, 0)
check.range(zlim)
col.grid.used <- if(grid && (zlim[1] >= 0)) col.grid else NA
argh <- resolve.defaults(list(x=Z, main=main,
border=col.grid.used,
col=col.base),
dotargs,
list(axes=FALSE, box=FALSE,
zlim=zlim, zlab=zlab,
#' do not independently rescale x & y
scale=FALSE,
#' expand=0.1 is default in persp.default
expand=zadjust * 0.1))
M <- do.call.matched(persp.im, argh,
funargs=graphicsPars("persp"))
#' create spikes
S <- xyzsegmentdata(x$x, x$y, 0,
x$x, x$y, marx)
if(grid) {
if(zlim[1] < 0) {
#' first draw downward spikes
downward <- (marx < 0)
if(any(downward)) {
SD <- S[downward, , drop=FALSE]
spectiveSegments(SD, neg.args, spike.args, ..., M=M)
S <- S[!downward, , drop=FALSE]
}
#' plot baseline grid
spectiveFlatGrid(R, ngrid, M, col=col.grid)
}
}
if(!is.rectangle(W)) {
#' plot window
spectiveFlatPolygons(W, M, win.args, dotargs)
}
#' draw upward spikes
if(nrow(S) > 0) {
spectiveSegments(S, spike.args, dotargs, M=M)
}
#'
invisible(M)
}

xyzsegmentdata <- function(x0, y0, z0, x1, y1, z1) {
data.frame(x0=x0, y0=y0, z0=z0, x1=x1, y1=y1, z1=z1)
}

trans3dz <- function(x,y,z,pmat) {
tr <- cbind(x, y, z, 1) %*% pmat
list(x = tr[, 1]/tr[, 4],
y = tr[, 2]/tr[, 4],
z = tr[, 3]/tr[, 4])
}

spectiveFlatGrid <- function(B, ngrid, M, ...) {
## arguments ... should be lists of parameters
B <- Frame(B)
xr <- B$xrange
yr <- B$yrange
ngrid <- ensure2vector(ngrid)
xx <- seq(xr[1], xr[2], length.out=ngrid[1]+1)
yy <- seq(yr[1], yr[2], length.out=ngrid[2]+1)
horiz <- xyzsegmentdata(xr[1], yy, 0, xr[2], yy, 0)
vert <- xyzsegmentdata(xx, yr[1], 0, xx, yr[2], 0)
spectiveSegments(horiz, ..., M=M)
spectiveSegments(vert, ..., M=M)
invisible(NULL)
}

spectiveFlatPolygons <- function(W, M, ...) {
## arguments ... should be lists of parameters
Wbdry <- as.polygonal(W)$bdry
Pbdry <- lapply(Wbdry,
function(p, M) {
as.list(trans3dz(p$x, p$y, 0, M))[c("x","y")]
}, M=M)
P <- owin(poly=Pbdry, check=FALSE, fix=FALSE)
do.call(plot.owin,
resolve.defaults(list(quote(P)),
...,
list(add=TRUE)))
}

spectiveSegments <- function(df, ..., M) {
## arguments ... should be lists of parameters
a0 <- with(df, trans3dz(x0, y0, z0, M))
a1 <- with(df, trans3dz(x1, y1, z1, M))
do.call.matched(segments,
resolve.defaults(
list(x0=a0$x,
y0=a0$y,
x1=a1$x,
y1=a1$y),
...))
invisible(NULL)
}

persp.ppp
})


2 changes: 1 addition & 1 deletion inst/doc/packagesizes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ date version nhelpfiles nobjects ndatasets Rlines srclines
"2024-02-28" "3.2-9" 452 1209 0 36325 15824
"2024-07-05" "3.3-0" 442 1186 0 35638 15596
"2024-07-09" "3.3-2" 442 1186 0 35638 15596
"2024-07-25" "3.3-2.001" 442 1186 0 35638 15596
"2024-09-07" "3.3-2.002" 443 1187 0 35800 15596
2 changes: 1 addition & 1 deletion inst/info/packagesizes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ date version nhelpfiles nobjects ndatasets Rlines srclines
"2024-02-28" "3.2-9" 452 1209 0 36325 15824
"2024-07-05" "3.3-0" 442 1186 0 35638 15596
"2024-07-09" "3.3-2" 442 1186 0 35638 15596
"2024-07-25" "3.3-2.001" 442 1186 0 35638 15596
"2024-09-07" "3.3-2.002" 443 1187 0 35800 15596
133 changes: 133 additions & 0 deletions man/persp.ppp.Rd
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
\name{persp.ppp}
\alias{persp.ppp}
\title{
Perspective Plot of Marked Point Pattern
}
\description{
For a spatial point pattern with numeric marks,
generate a perspective plot in which each data point is
shown as a vertical spike, with height proportional to the mark value.
}
\usage{
\method{persp}{ppp}(x, \dots, main, grid = TRUE, ngrid = 10,
col.grid = "grey", col.base = "white",
win.args=list(), spike.args = list(), neg.args = list(),
which.marks = 1, zlab = NULL, zlim = NULL, zadjust = 1)
}
\arguments{
\item{x}{
A spatial point pattern (object of class \code{"ppp"})
with numeric marks, or a data frame of marks.
}
\item{\dots}{
Additional graphical arguments passed to
\code{\link{persp.default}} to determine the perspective view
(for example the rotation angle \code{theta} and the elevation angle
\code{phi})
or passed to \code{\link[graphics]{segments}} to control the drawing
(for example \code{lwd} for line width).
}
\item{main}{
Optional main title for the plot.
}
\item{grid}{
Logical value specifying whether to draw a grid of reference lines
on the horizontal plane.
}
\item{ngrid}{
Number of grid lines to draw in each direction, if \code{grid=TRUE}.
An integer, or a pair of integers specifying the number of grid
lines along the horizontal and vertical axes respectively.
}
\item{col.grid}{
Colour of grid lines, if \code{grid=TRUE}.
}
\item{col.base}{
Colour with which to fill the horizontal plane.
}
\item{win.args}{
List of arguments passed to \code{\link{plot.owin}}
to control the drawing of the window of \code{x}.
Applicable only when the window is not a rectangle.
}
\item{spike.args}{
List of arguments passed to \code{\link[graphics]{segments}}
to control the drawing of the spikes.
}
\item{neg.args}{
List of arguments passed to \code{\link[graphics]{segments}}
applicable only to those spikes which have negative height
(corresponding to a mark value which is negative).
}
\item{which.marks}{
Integer, or character name, identifying the column of marks which
should be used, when \code{marks(x)} is a data frame.
}
\item{zlab}{
Optional. Label for the vertical axis.
Character string or expression.
}
\item{zlim}{
Optional. Range of values on the vertical axis.
A numeric vector of length 2.
}
\item{zadjust}{
Scale adjustment factor controlling the height of spikes.
}
}
\details{
The function \code{\link[graphics]{persp}} is generic. This is the
method for spatial point patterns (objects of class \code{"ppp"}).
The argument \code{x} must be a point pattern with numeric marks,
or with a data frame of marks.

A perspective view will be plotted. The eye position is determined by
the arguments \code{theta} and \code{phi}
passed to \code{\link[graphics]{persp.default}}.

First the horizontal plane is drawn in perspective view,
using a faint grid of lines to help suggest the perspective.
Next the observation window of \code{x} is placed on the horizontal
plane and its edges are drawn in perspective view.
Finally for each data point in \code{x}, a vertical spike is erected
at the spatial location of the data point, with height equal to the
mark value of the point.

If any mark values are negative, the corresponding spikes will
penetrate below the horizontal plane. They can be drawn in a different
colour by specifying \code{neg.args} as shown in the examples.

Like all spatial plots in the \pkg{spatstat} family,
\code{persp.ppp} does not independently rescale
the \eqn{x} and \eqn{y} coordinates. A long narrow window will be
represented as a long narrow window in the perspective view.
To override this and allow the coordinates to be independently
rescaled, use the argument \code{scale=TRUE} which will be passed
to \code{\link[graphics]{persp.default}}.
}
\value{
(Invisibly) the perspective transformation matrix.
}
\author{
Adrian Baddeley.
}
\examples{
persp(longleaf, theta=-30, phi=35, spike.args=list(lwd=3), zadjust=1.5)

# negative mark values
X <- longleaf
marks(X) <- marks(X) - 20
persp(X, theta=80, phi=35, neg.args=list(col="red"),
spike.args=list(lwd=3), zadjust=1.2)

# irregular window
Australia <- Window(austates)
Y <- runifrect(70, Frame(Australia))[Australia]
marks(Y) <- runif(npoints(Y))
persp(Y, theta=30, phi=20, col.base="lightblue",
win.args=list(col="pink", border=NA),
spike.args=list(lwd=2), zadjust=1.5)

}
\keyword{hplot}
\keyword{spatial}

0 comments on commit 079f0f3

Please sign in to comment.