Raw File
random.R
##
##    random.R
##
##    Functions for generating random point patterns
##
##    $Revision: 4.76 $   $Date: 2015/03/13 11:15:51 $
##
##
##    runifpoint()      n i.i.d. uniform random points ("binomial process")
##
##    runifpoispp()     uniform Poisson point process
##
##    rpoispp()         general Poisson point process (thinning method)
##
##    rpoint()          n independent random points (rejection/pixel list)
##
##    rMaternI()        Mat'ern model I 
##    rMaternII()       Mat'ern model II
##    rSSI()            Simple Sequential Inhibition process
##
##    rthin()           independent random thinning
##    rjitter()         random perturbation
##
##    Examples:
##          u01 <- owin(0:1,0:1)
##          plot(runifpoispp(100, u01))
##          X <- rpoispp(function(x,y) {100 * (1-x/2)}, 100, u01)
##          X <- rpoispp(function(x,y) {ifelse(x < 0.5, 100, 20)}, 100)
##          plot(X)
##          plot(rMaternI(100, 0.02))
##          plot(rMaternII(100, 0.05))
##

runifrect <- function(n, win=owin(c(0,1),c(0,1)), nsim=1, drop=TRUE)
{
  ## no checking
  xr <- win$xrange
  yr <- win$yrange
  if(nsim == 1) {
    x <- runif(n, min=xr[1], max=xr[2])
    y <- runif(n, min=yr[1], max=yr[2])
    X <- ppp(x, y, window=win, check=FALSE)
    return(if(drop) X else solist(X))
  } 
  result <- vector(mode="list", length=nsim)
  for(isim in 1:nsim) {
    x <- runif(n, min=xr[1], max=xr[2])
    y <- runif(n, min=yr[1], max=yr[2])
    result[[isim]] <- ppp(x, y, window=win, check=FALSE)
  }
  return(as.solist(result))
}

runifdisc <- function(n, radius=1, centre=c(0,0), ..., nsim=1, drop=TRUE)
{
  ## i.i.d. uniform points in the disc of radius r and centre (x,y)
  disque <- disc(centre=centre, radius=radius, ...)
  if(nsim == 1) {
    theta <- runif(n, min=0, max= 2 * pi)
    s <- sqrt(runif(n, min=0, max=radius^2))
    X <- ppp(centre[1] + s * cos(theta),
             centre[2] + s * sin(theta),
             window=disque, check=FALSE)
    return(if(drop) X else solist(X))
  }
  result <- vector(mode="list", length=nsim)
  for(isim in 1:nsim) {
    theta <- runif(n, min=0, max= 2 * pi)
    s <- sqrt(runif(n, min=0, max=radius^2))
    result[[isim]] <- ppp(centre[1] + s * cos(theta),
                          centre[2] + s * sin(theta),
                          window=disque, check=FALSE)
  }
  return(as.solist(result))
}


runifpoint <- function(n, win=owin(c(0,1),c(0,1)),
                       giveup=1000, warn=TRUE, ...,
                       nsim=1, drop=TRUE, ex=NULL)
{
  if(missing(n) && missing(win) && !is.null(ex)) {
    stopifnot(is.ppp(ex))
    n <- npoints(ex)
    win <- Window(ex)
  } else {
    win <- as.owin(win)
    check.1.integer(n)
    stopifnot(n >= 0)
  }

  if(n == 0) {
    emp <- ppp(numeric(0), numeric(0), window=win)
    if(nsim == 1) return(emp)
    result <- rep(list(emp), nsim)
    names(result) <- paste("Simulation", 1:nsim)
    return(as.solist(result))
  }

  if(warn) {
    nhuge <- spatstat.options("huge.npoints")
    if(n > nhuge) {
      whinge <- paste("Attempting to generate", n, "random points")
      message(whinge)
      warning(whinge, call.=FALSE)
    }
  }

  switch(win$type,
         rectangle = {
           return(runifrect(n, win, nsim=nsim))
         },
         mask = {
           dx <- win$xstep
           dy <- win$ystep
           ## extract pixel coordinates and probabilities
           rxy <- rasterxy.mask(win, drop=TRUE)
           xpix <- rxy$x
           ypix <- rxy$y
           ## make a list of nsim point patterns
           result <- vector(mode="list", length=nsim)
           for(isim in 1:nsim) {
             ## select pixels with equal probability
             id <- sample(seq_along(xpix), n, replace=TRUE)
             ## extract pixel centres and randomise within pixels
             x <- xpix[id] + runif(n, min= -dx/2, max=dx/2)
             y <- ypix[id] + runif(n, min= -dy/2, max=dy/2)
             result[[isim]] <- ppp(x, y, window=win, check=FALSE)
           }
         },
         polygonal={
           ## make a list of nsim point patterns
           result <- vector(mode="list", length=nsim)
           for(isim in 1:nsim) {
             ## rejection method
             ## initialise empty pattern
             x <- numeric(0)
             y <- numeric(0)
             X <- ppp(x, y, window=win)
             ##
             ## rectangle in which trial points will be generated
             box <- boundingbox(win)
             ## 
             ntries <- 0
             repeat {
               ntries <- ntries + 1
               ## generate trial points in batches of n
               qq <- runifrect(n, box) 
               ## retain those which are inside 'win'
               qq <- qq[win]
               ## add them to result
               X <- superimpose(X, qq, W=win, check=FALSE)
               ## if we have enough points, exit
               if(X$n > n) {
                 result[[isim]] <- X[1:n]
                 break
               } else if(X$n == n) {
                 result[[isim]] <- X
                 break
               } else if(ntries >= giveup) {
                 ## otherwise get bored eventually
                 stop(paste("Gave up after", giveup * n, "trials,",
                            X$n, "points accepted"))
               }
             }
           }
         },
         stop("Unrecognised window type")
         )
  
  ## list of point patterns produced.
  if(nsim == 1 && drop)
    return(result[[1]])
  names(result) <- paste("Simulation", 1:nsim)
  return(as.solist(result))
}

runifpoispp <- function(lambda, win = owin(c(0,1),c(0,1)), ...,
                        nsim=1, drop=TRUE) {
  win <- as.owin(win)
  if(!is.numeric(lambda) || length(lambda) > 1 ||
     !is.finite(lambda) || lambda < 0)
    stop("Intensity lambda must be a single finite number >= 0")

  if(lambda == 0) {
    ## return empty pattern
    emp <- ppp(numeric(0), numeric(0), window=win)
    if(nsim == 1 && drop) return(emp)
    result <- rep(list(emp), nsim)
    names(result) <- paste("Simulation", 1:nsim)
    return(as.solist(result))
  }

  ## will generate Poisson process in enclosing rectangle and trim it
  box <- boundingbox(win)
  meanN <- lambda * area(box)
  
  if(nsim == 1) {
    n <- rpois(1, meanN)
    if(!is.finite(n))
      stop(paste("Unable to generate Poisson process with a mean of",
                 meanN, "points"))
    X <- runifpoint(n, box)
    ## trim to window
    if(win$type != "rectangle")
      X <- X[win]
    return(if(drop) X else solist(X))
  }
  result <- vector(mode="list", length=nsim)
  for(isim in 1:nsim) {
    n <- rpois(1, meanN)
    if(!is.finite(n))
      stop(paste("Unable to generate Poisson process with a mean of",
                 meanN, "points"))
    X <- runifpoint(n, box)
    ## trim to window
    if(win$type != "rectangle")
      X <- X[win]
    result[[isim]] <- X
  }
  names(result) <- paste("Simulation", 1:nsim)
  return(as.solist(result))
}

rpoint <- function(n, f, fmax=NULL,
                   win=unit.square(), ..., giveup=1000,verbose=FALSE,
                   nsim=1, drop=TRUE) {
  
  if(missing(f) || (is.numeric(f) && length(f) == 1))
    ## uniform distribution
    return(runifpoint(n, win, giveup, nsim=nsim, drop=drop))
  
  ## non-uniform distribution....
  if(!is.function(f) && !is.im(f))
    stop(paste(sQuote("f"),
               "must be either a function or an",
               sQuote("im"), "object"))
  
  if(is.im(f)) {
    ## ------------ PIXEL IMAGE ---------------------
    wf <- as.owin(f)
    if(n == 0) {
      ## return empty pattern(s)
      emp <- ppp(numeric(0), numeric(0), window=wf)
      if(nsim == 1 && drop) return(emp)
      result <- rep(list(emp), nsim)
      names(result) <- paste("Simulation", 1:nsim)
      return(as.solist(result))
    }
    w <- as.mask(wf)
    M <- w$m
    dx <- w$xstep
    dy <- w$ystep
    ## extract pixel coordinates and probabilities
    rxy <- rasterxy.mask(w, drop=TRUE)
    xpix <- rxy$x
    ypix <- rxy$y
    ppix <- as.vector(f$v[M]) ## not normalised - OK
    ##
    if(nsim == 1) {
      ## select pixels
      id <- sample(length(xpix), n, replace=TRUE, prob=ppix)
      ## extract pixel centres and randomise within pixels
      x <- xpix[id] + runif(n, min= -dx/2, max=dx/2)
      y <- ypix[id] + runif(n, min= -dy/2, max=dy/2)
      X <- ppp(x, y, window=wf, check=FALSE)
      return(if(drop) X else solist(X))
    }
    result <- vector(mode="list", length=nsim)
    for(isim in 1:nsim) {
      ## select pixels
      id <- sample(length(xpix), n, replace=TRUE, prob=ppix)
      ## extract pixel centres and randomise within pixels
      x <- xpix[id] + runif(n, min= -dx/2, max=dx/2)
      y <- ypix[id] + runif(n, min= -dy/2, max=dy/2)
      result[[isim]] <- ppp(x, y, window=wf, check=FALSE)
    }
    names(result) <- paste("Simulation", 1:nsim)
    return(as.solist(result))
  }

  ## ------------ FUNCTION  ---------------------  
  ## Establish parameters for rejection method

  verifyclass(win, "owin")

  if(n == 0) {
    ## return empty pattern(s)
    emp <- ppp(numeric(0), numeric(0), window=win)
    if(nsim == 1 && drop) return(emp)
    result <- rep(list(emp), nsim)
    names(result) <- paste("Simulation", 1:nsim)
    return(as.solist(result))
  }
  
  if(is.null(fmax)) {
    ## compute approx maximum value of f
    imag <- as.im(f, win, ...)
    summ <- summary(imag)
    fmax <- summ$max + 0.05 * diff(summ$range)
  }
  irregular <- (win$type != "rectangle")
  box <- boundingbox(win)

  result <- vector(mode="list", length=nsim)
  for(isim in 1:nsim) {

    ## initialise empty pattern
    X <- ppp(numeric(0), numeric(0), window=win)
  
    pbar <- 1
    nremaining <- n
    totngen <- 0
    
    ## generate uniform random points in batches
    ## and apply the rejection method.
    ## Collect any points that are retained in X

    ntries <- 0
    repeat{
      ntries <- ntries + 1
      ## proposal points
      ngen <- nremaining/pbar + 10
      totngen <- totngen + ngen
      prop <- runifrect(ngen, box)
      if(irregular)
        prop <- prop[win]
      if(prop$n > 0) {
        fvalues <- f(prop$x, prop$y, ...)
        paccept <- fvalues/fmax
        u <- runif(prop$n)
        ## accepted points
        Y <- prop[u < paccept]
        if(Y$n > 0) {
          ## add to X
          X <- superimpose(X, Y, W=win, check=FALSE)
          nX <- X$n
          pbar <- nX/totngen
          nremaining <- n - nX
          if(nremaining <= 0) {
            ## we have enough!
            if(verbose)
              splat("acceptance rate = ", round(100 * pbar, 2), "%")
            result[[isim]] <- if(nX == n) X else X[1:n]
            break
          }
        }
      }
      if(ntries > giveup)
        stop(paste("Gave up after",giveup * n,"trials with",
                   X$n, "points accepted"))
    }
  }
  if(nsim == 1 && drop) return(result[[1]])
  names(result) <- paste("Simulation", 1:nsim)
  return(as.solist(result))
}

rpoispp <- function(lambda, lmax=NULL, win = owin(), ...,
                    nsim=1, drop=TRUE, ex=NULL) {
  ## arguments:
  ##     lambda  intensity: constant, function(x,y,...) or image
  ##     lmax     maximum possible value of lambda(x,y,...)
  ##     win     default observation window (of class 'owin')
  ##   ...       arguments passed to lambda(x, y, ...)
  ##     nsim    number of replicate simulations

  if(missing(lambda) && is.null(lmax) && missing(win) && !is.null(ex)) {
    lambda <- intensity(unmark(ex))
    win <- Window(ex)
  } else {
    if(!(is.numeric(lambda) || is.function(lambda) || is.im(lambda)))
      stop(paste(sQuote("lambda"),
                 "must be a constant, a function or an image"))
    if(is.numeric(lambda) && !(length(lambda) == 1 && lambda >= 0))
      stop(paste(sQuote("lambda"),
                 "must be a single, nonnegative number"))
    if(!is.null(lmax)) {
      if(!is.numeric(lmax))
        stop("lmax should be a number")
      if(length(lmax) > 1)
        stop("lmax should be a single number")
    }
    win <- if(is.im(lambda))
      rescue.rectangle(as.owin(lambda))
    else
      as.owin(win)
  }
  
  if(is.numeric(lambda)) 
    ## uniform Poisson
    return(runifpoispp(lambda, win, nsim=nsim, drop=drop))

  ## inhomogeneous Poisson
  ## perform thinning of uniform Poisson

  if(is.null(lmax)) {
    imag <- as.im(lambda, win, ...)
    summ <- summary(imag)
    lmax <- summ$max + 0.05 * diff(summ$range)
  } 

  if(is.function(lambda)) {
    ## function lambda
    if(nsim == 1) {
      X <- runifpoispp(lmax, win)  ## includes sanity checks on `lmax'
      if(X$n == 0) return(X)
      prob <- lambda(X$x, X$y, ...)/lmax
      u <- runif(X$n)
      retain <- (u <= prob)
      X <- X[retain, ]
      return(if(drop) X else solist(X))
    }
    result <- runifpoispp(lmax, win, nsim=nsim, drop=FALSE)
    for(isim in 1:nsim) {
      X <- result[[isim]]
      if(X$n > 0) {
        prob <- lambda(X$x, X$y, ...)/lmax
        u <- runif(X$n)
        retain <- (u <= prob)
        result[[isim]] <- X[retain, ]
      }
    }
    return(as.solist(result))
  }

  if(is.im(lambda)) {
    ## image lambda
    if(nsim == 1) {
      X <- runifpoispp(lmax, win)
      if(X$n == 0) return(X)
      prob <- lambda[X]/lmax
      u <- runif(X$n)
      retain <- (u <= prob)
      X <- X[retain, ]
      return(if(drop) X else solist(X))
    }
    result <- runifpoispp(lmax, win, nsim=nsim, drop=FALSE)
    for(isim in 1:nsim) {
      X <- result[[isim]]
      if(X$n > 0) {
        prob <- lambda[X]/lmax
        u <- runif(X$n)
        retain <- (u <= prob)
        result[[isim]] <- X[retain, ]
      }
    }
    return(as.solist(result))
  }
  stop(paste(sQuote("lambda"), "must be a constant, a function or an image"))
}
    
rMaternI <- function(kappa, r, win = owin(c(0,1),c(0,1)), stationary=TRUE,
                     ..., nsim=1, drop=TRUE)
{
  win <- as.owin(win)
  stopifnot(is.numeric(r) && length(r) == 1)
  if(stationary) {
    ## generate in a larger window
    bigbox <- grow.rectangle(as.rectangle(win), r)
    X <- rpoispp(kappa, win=bigbox, nsim=nsim)
  } else {
    X <- rpoispp(kappa, win=win, nsim=nsim)
  }
  result <- if(nsim == 1) solist(X) else X
  for(isim in 1:nsim) {
    Y <- result[[isim]]
    if(npoints(Y) > 1) {
      d <- nndist(Y)
      Y <- Y[d > r]
    }
    if(stationary)
      Y <- Y[win]
    result[[isim]] <- Y
  }
  if(nsim == 1 && drop) return(result[[1]])
  return(as.solist(result))
}
    
rMaternII <- function(kappa, r, win = owin(c(0,1),c(0,1)), stationary=TRUE,
                      ..., nsim=1, drop=TRUE)
{
  win <- as.owin(win)
  stopifnot(is.numeric(r) && length(r) == 1)
  if(stationary) {
    bigbox <- grow.rectangle(as.rectangle(win), r)
    X <- rpoispp(kappa, win=bigbox, nsim=nsim)
  } else {
    X <- rpoispp(kappa, win=win, nsim=nsim)
  }
  result <- if(nsim == 1) list(X) else X
  for(isim in 1:nsim) {
    Y <- result[[isim]]
    nY <- npoints(Y)
    if(nY > 1) {
      ## matrix of squared pairwise distances
      d2 <- pairdist(Y, squared=TRUE)
      close <- (d2 <= r^2)
      ## random order 1:n
      age <- sample(seq_len(nY), nY, replace=FALSE)
      earlier <- outer(age, age, ">")
      conflict <- close & earlier
      ## delete <- apply(conflict, 1, any)
      delete <- matrowany(conflict)
      Y <- Y[!delete]
    }
    if(stationary)
      Y <- Y[win]
    result[[isim]] <- Y
  }
  if(nsim == 1 && drop) return(result[[1]])
  return(as.solist(result))
}
  
rSSI <- function(r, n=Inf, win = square(1), 
                 giveup = 1000, x.init=NULL, ...,
                 f=NULL, fmax=NULL,
                 nsim=1, drop=TRUE)
{
  win.given <- !missing(win) && !is.null(win)
  stopifnot(is.numeric(r) && length(r) == 1 && r >= 0)
  stopifnot(is.numeric(n) && length(n) == 1 && n >= 0)
  ##
  if(!is.null(f)) {
    stopifnot(is.numeric(f) || is.im(f) || is.function(f))
    if(is.null(fmax) && !is.numeric(f))
      fmax <- if(is.im(f)) max(f) else max(as.im(f, win))
  }
  ##
  if(nsim > 1) {
    result <- vector(mode="list", length=nsim)
    if(!win.given) win <- square(1)
    for(isim in 1:nsim) {
      progressreport(isim, nsim)
      result[[isim]] <- rSSI(r=r, n=n, win=win, giveup=giveup, x.init=x.init,
                             f=f, fmax=fmax)
    }
    names(result) <- paste("Simulation", 1:nsim)
    return(as.solist(result))
  }
  ## Simple Sequential Inhibition process
  ## fixed number of points
  ## Naive implementation, proposals are uniform
  if(is.null(x.init)) {
    ## start with empty pattern in specified window
    win <- as.owin(win)
    x.init <- ppp(numeric(0),numeric(0), window=win)
  } else {
    ## start with specified pattern
    stopifnot(is.ppp(x.init))
    if(!win.given) {
      win <- as.owin(x.init)
    } else {
      ## check compatibility of windows
      if(!identical(win, as.owin(x.init)))
        warning(paste("Argument", sQuote("win"),
                      "is not the same as the window of", sQuote("x.init")))
      x.init.new <- x.init[win]
      if(npoints(x.init.new) == 0)
        stop(paste("No points of x.init lie inside the specified window",
                   sQuote("win")))
      nlost <- npoints(x.init) - npoints(x.init.new)
      if(nlost > 0) 
        warning(paste(nlost, "out of",
                      npoints(x.init), "points of the pattern x.init",
                      "lay outside the specified window",
                      sQuote("win")))
      x.init <- x.init.new
    }
    if(n < npoints(x.init))
      stop(paste("x.init contains", npoints(x.init), "points",
                 "but a pattern containing only n =", n, "points", 
                 "is required"))
    if(n == npoints(x.init)) {
      warning(paste("Initial state x.init already contains", n, "points;",
                    "no further points were added"))
      return(if(drop) x.init else solist(x.init))
    }
  }
  X <- x.init
  r2 <- r^2
  if(!is.infinite(n) && (n * pi * r2/4  > area(win)))
    warning(paste("Window is too small to fit", n, "points",
               "at minimum separation", r))
  ntries <- 0
  while(ntries < giveup) {
    ntries <- ntries + 1
    qq <- if(is.null(f)) runifpoint(1, win) else rpoint(1, f, fmax, win)
    dx <- qq$x[1] - X$x
    dy <- qq$y[1] - X$y
    if(all(dx^2 + dy^2 > r2))
      X <- superimpose(X, qq, W=win, check=FALSE)
    if(X$n >= n)
      return(if(drop) X else solist(X))
  }
  if(!is.infinite(n))
    warning(paste("Gave up after", giveup,
                  "attempts with only", X$n, "points placed out of", n))
  return(if(drop) X else solist(X))
}

rPoissonCluster <-
  function(kappa, expand, rcluster, win = owin(c(0,1),c(0,1)), ...,
           lmax=NULL, nsim=1, drop=TRUE)
{
  ## Generic Poisson cluster process
  ## Implementation for bounded cluster radius
  ##
  ## 'rcluster' is a function(x,y) that takes the coordinates
  ## (x,y) of the parent point and generates a list(x,y) of offspring
  ##
  ## "..." are arguments to be passed to 'rcluster()'
  ##

  ## Catch old argument name rmax for expand, and allow rmax to be
  ## passed to rcluster (and then be ignored)
  if(missing(expand) && !is.null(rmax <- list(...)$rmax)){
      expand <- rmax
      f <- rcluster
      rcluster <- function(..., rmax) f(...)
  }
  win <- as.owin(win)
  
  ## Generate parents in dilated window
  frame <- boundingbox(win)
  dilated <- owin(frame$xrange + c(-expand, expand),
                  frame$yrange + c(-expand, expand))
  if(is.im(kappa) && !is.subset.owin(dilated, as.owin(kappa)))
    stop(paste("The window in which the image",
               sQuote("kappa"),
               "is defined\n",
               "is not large enough to contain the dilation of the window",
               sQuote("win")))
  parentlist <- rpoispp(kappa, lmax=lmax, win=dilated, nsim=nsim)
  if(nsim == 1) parentlist <- list(parentlist)

  resultlist <- vector(mode="list", length=nsim)
  for(isim in 1:nsim) {
    parents <- parentlist[[isim]]
    result <- NULL
    ## generate clusters
    np <- parents$n
    if(np > 0) {
      xparent <- parents$x
      yparent <- parents$y
      for(i in seq_len(np)) {
        ## generate random offspring of i-th parent point
        cluster <- rcluster(xparent[i], yparent[i], ...)
        if(!inherits(cluster, "ppp"))
          cluster <- ppp(cluster$x, cluster$y, window=frame, check=FALSE)
        ## skip if cluster is empty
        if(cluster$n > 0) {
          ## trim to window
          cluster <- cluster[win]
          if(is.null(result)) {
            ## initialise offspring pattern and offspring-to-parent map
            result <- cluster
            parentid <- rep.int(1, cluster$n)
          } else {
            ## add to pattern
            result <- superimpose(result, cluster, W=win, check=FALSE)
            ## update offspring-to-parent map
            parentid <- c(parentid, rep.int(i, cluster$n))
          }
        }
      }
    } else {
      ## no parents - empty pattern
      result <- ppp(numeric(0), numeric(0), window=win)
      parentid <- integer(0)
    }

    attr(result, "parents") <- parents
    attr(result, "parentid") <- parentid
    attr(result, "expand") <- expand

    resultlist[[isim]] <- result
  }

  if(nsim == 1 && drop) return(resultlist[[1]])

  names(resultlist) <- paste("Simulation", 1:nsim)
  return(as.solist(resultlist))
}  

rGaussPoisson <- local({
  
  rGaussPoisson <- function(kappa, r, p2, win=owin(c(0,1), c(0,1)),
                            ..., nsim=1, drop=TRUE) {
    ## Gauss-Poisson process
    result <- rPoissonCluster(kappa, 1.05 * r, oneortwo,
                              win, radius=r/2, p2=p2, nsim=nsim, drop=drop)
    return(result)
  }

  oneortwo <- function(x0, y0, radius, p2) {
    if(runif(1) > p2) 
      ## one point
      return(list(x=x0, y=y0))
    ## two points
    theta <- runif(1, min=0, max=2*pi)
    return(list(x=x0+c(-1,1)*radius*cos(theta),
                y=y0+c(-1,1)*radius*sin(theta)))
  }

  rGaussPoisson
})

  
rstrat <- function(win=square(1), nx, ny=nx, k=1, nsim=1, drop=TRUE) {
  win <- as.owin(win)
  stopifnot(nx >= 1 && ny >= 1)
  stopifnot(k >= 1)
  if(nsim == 1) {
    xy <- stratrand(win, nx, ny, k)
    Xbox <- ppp(xy$x, xy$y, win$xrange, win$yrange, check=FALSE)
    X <- Xbox[win]
    return(if(drop) X else solist(X))
  }
  result <- vector(mode="list", length=nsim)
  for(isim in 1:nsim) {
    xy <- stratrand(win, nx, ny, k)
    Xbox <- ppp(xy$x, xy$y, win$xrange, win$yrange, check=FALSE)
    result[[isim]] <- Xbox[win]
  }
  names(result) <- paste("Simulation", 1:nsim)
  return(as.solist(result))
}

xy.grid <- function(xr, yr, nx, ny, dx, dy) {
  nx.given <- !is.null(nx)
  ny.given <- !is.null(ny)
  dx.given <- !is.null(dx)
  dy.given <- !is.null(dy)
  if(nx.given && dx.given)
    stop("Do not give both nx and dx")    
  if(nx.given) {
    stopifnot(nx >= 1)
    x0 <- seq(from=xr[1], to=xr[2], length.out=nx+1)
    dx <- diff(xr)/nx
  } else if(dx.given) {
    stopifnot(dx > 0)
    x0 <- seq(from=xr[1], to=xr[2], by=dx)
    nx <- length(x0) - 1
  } else stop("Need either nx or dx")
  ## determine y grid
  if(ny.given && dy.given)
    stop("Do not give both ny and dy")    
  if(ny.given) {
    stopifnot(ny >= 1)
    y0 <- seq(from=yr[1], to=yr[2], length.out=ny+1)
    dy <- diff(yr)/ny
  } else {
    if(is.null(dy)) dy <- dx
    stopifnot(dy > 0)
    y0 <- seq(from=yr[1], to=yr[2], by=dy)
    ny <- length(y0) - 1
  }
  return(list(x0=x0, y0=y0, nx=nx, ny=ny, dx=dx, dy=dy))
}
  
rsyst <- function(win=square(1), nx=NULL, ny=nx, ..., dx=NULL, dy=dx,
                  nsim=1, drop=TRUE) {
  win <- as.owin(win)
  xr <- win$xrange
  yr <- win$yrange
  ## determine grid coordinates 
  if(missing(ny)) ny <- NULL
  if(missing(dy)) dy <- NULL
  g <- xy.grid(xr, yr, nx, ny, dx, dy)
  x0 <- g$x0
  y0 <- g$y0
  dx <- g$dx
  dy <- g$dy
  ## assemble grid and randomise location
  xy0 <- expand.grid(x=x0, y=y0)
  if(nsim == 1) {
    x <- xy0$x + runif(1, min = 0, max = dx)
    y <- xy0$y + runif(1, min = 0, max = dy)
    Xbox <- ppp(x, y, xr, yr, check=FALSE)
    ## trim to window
    X <- Xbox[win]
    return(if(drop) X else solist(X))
  }
  result <- vector(mode="list", length=nsim)
  for(isim in 1:nsim) {
    x <- xy0$x + runif(1, min = 0, max = dx)
    y <- xy0$y + runif(1, min = 0, max = dy)
    Xbox <- ppp(x, y, xr, yr, check=FALSE)
    ## trim to window
    result[[isim]] <- Xbox[win]
  }
  names(result) <- paste("Simulation", 1:nsim)
  return(as.solist(result))
}

rcellnumber <- function(n, N=10) {
  if(!missing(N)) {
    if(round(N) != N) stop("N must be an integer")
    stopifnot(is.finite(N))
    stopifnot(N > 1)
  }
  u <- runif(n, min=0, max=1)
  p0 <- 1/N
  pN <- 1/(N * (N-1))
  k <- ifelse(u < p0, 0, ifelse(u < (1 - pN), 1, N))
  return(k)
}

rcell <- function(win=square(1), nx=NULL, ny=nx, ...,
                  dx=NULL, dy=dx, N=10, nsim=1, drop=TRUE) {
  win <- as.owin(win)
  xr <- win$xrange
  yr <- win$yrange
  ## determine grid coordinates 
  if(missing(ny)) ny <- NULL
  if(missing(dy)) dy <- NULL
  g <- xy.grid(xr, yr, nx, ny, dx, dy)
  nx <- g$nx
  ny <- g$ny
  x0 <- g$x0
  y0 <- g$y0
  dx <- g$dx
  dy <- g$dy
  ## generate pattern(s)
  result <- vector(mode="list", length=nsim)
  for(isim in 1:nsim) {
    x <- numeric(0)
    y <- numeric(0)
    for(ix in seq_len(nx))
      for(iy in seq_len(ny)) {
        nij <- rcellnumber(1, N)
        x <- c(x, x0[ix] + runif(nij, min=0, max=dx))
        y <- c(y, y0[iy] + runif(nij, min=0, max=dy))
      }
    Xbox <- ppp(x, y, xr, yr, check=FALSE)
    result[[isim]] <- Xbox[win]
  }
  if(nsim == 1 && drop) return(result[[1]])
  names(result) <- paste("Simulation", 1:nsim)
  return(as.solist(result))
}


rthin <- function(X, P, ..., nsim=1, drop=TRUE) {
  verifyclass(X, "ppp")

  nX <- npoints(X)
  if(nX == 0) {
    if(nsim == 1 && drop) return(X)
    result <- rep(list(X), nsim)
    names(result) <- paste("Simulation", 1:nsim)
    return(as.solist(result))
  }

  if(is.numeric(P)) {
    ## vector of retention probabilities
    pX <- P
    if(length(pX) != nX) {
      if(length(pX) == 1)
        pX <- rep.int(pX, nX)
      else 
        stop("Length of vector P does not match number of points of X")
    }
    if(any(is.na(pX)))
      stop("P contains NA's")
  } else if(is.function(P)) {
    ## function - evaluate it at points of X
    pX <- P(X$x, X$y, ...)
    if(length(pX) != nX)
      stop("Function P returned a vector of incorrect length")
    if(!is.numeric(pX))
      stop("Function P returned non-numeric values")
    if(any(is.na(pX)))
      stop("Function P returned some NA values")
  } else if(is.im(P)) {
    ## image - look it up
    if(!(P$type %in% c("integer", "real")))
      stop("Values of image P should be numeric")
    pX <- P[X, drop=FALSE]
    if(any(is.na(pX)))
      stop("some points of X lie outside the domain of image P")
  } else
  stop("Unrecognised format for P")

  if(min(pX) < 0) stop("some probabilities are negative")
  if(max(pX) > 1) stop("some probabilities are greater than 1")

  if(nsim == 1) {
    retain <- (runif(length(pX)) < pX)
    Y <- X[retain]
    ## also handle offspring-to-parent map if present
    if(!is.null(parentid <- attr(X, "parentid")))
      attr(Y, "parentid") <- parentid[retain]
    return(if(drop) Y else solist(Y))
  }
  result <- vector(mode="list", length=nsim)
  for(isim in 1:nsim) {
    retain <- (runif(length(pX)) < pX)
    Y <- X[retain]
    ## also handle offspring-to-parent map if present
    if(!is.null(parentid <- attr(X, "parentid")))
      attr(Y, "parentid") <- parentid[retain]
    result[[isim]] <- Y
  }
  names(result) <- paste("Simulation", 1:nsim)
  return(as.solist(result))
}


## rjitter

rjitter <- function(X, radius, retry=TRUE, giveup=10000, ...,
                    nsim=1, drop=TRUE) {
  verifyclass(X, "ppp")
  if(missing(radius) || is.null(radius))
    radius <- bw.stoyan(X)
  if(nsim > 1) {
    result <- vector(mode="list", length=nsim)
    for(isim in 1:nsim)
      result[[isim]] <- rjitter(X, radius=radius,
                                retry=retry, giveup=giveup, ...)
    names(result) <- paste("Simulation", 1:nsim)
    return(as.solist(result))
  }
  nX <- npoints(X)
  if(nX == 0) return(X)
  W <- X$window
  if(!retry) {
    ## points outside window are lost
    D <- runifdisc(nX, radius=radius)
    xnew <- X$x + D$x
    ynew <- X$y + D$y
    ok <- inside.owin(xnew, ynew, W)
    return(ppp(xnew[ok], ynew[ok], window=W))
  }
  ## retry = TRUE: condition on points being inside window
  undone <- rep.int(TRUE, nX)
  while(any(undone)) {
    giveup <- giveup - 1
    if(giveup <= 0)
      return(X)
    Y <- X[undone]
    D <- runifdisc(Y$n, radius=radius)
    xnew <- Y$x + D$x
    ynew <- Y$y + D$y
    ok <- inside.owin(xnew, ynew, W)
    if(any(ok)) {
      changed <- seq_len(nX)[undone][ok]
      X$x[changed] <- xnew[ok]
      X$y[changed] <- ynew[ok]
      undone[changed] <- FALSE
    }
  }
  return(if(drop) X else solist(X))
}

back to top