#
#   pcfinhom.R
#
#   $Revision: 1.28 $   $Date: 2023/03/11 06:04:23 $
#
#   inhomogeneous pair correlation function of point pattern 
#
#

pcfinhom <- function(X, lambda=NULL, ..., r=NULL,
                     kernel="epanechnikov",
                     bw=NULL, adjust.bw=1, stoyan=0.15,
                     correction=c("translate", "Ripley"),
                     divisor=c("r","d"),
                     renormalise=TRUE,
                     normpower=1,
                     update=TRUE, leaveoneout=TRUE,
                     reciplambda=NULL, 
                     sigma=NULL, adjust.sigma=1,
                     varcov=NULL, close=NULL)
{
  verifyclass(X, "ppp")
#  r.override <- !is.null(r)
  
  win <- X$window
  areaW <- area(win)
  npts <- npoints(X)

  kernel <- match.kernel(kernel)
  
  correction.given <- !missing(correction)
  correction <- pickoption("correction", correction,
                           c(isotropic="isotropic",
                             Ripley="isotropic",
                             trans="translate",
                             translate="translate",
                             translation="translate",
                             good="good",
                             best="best"),
                           multi=TRUE)
  if("good" %in% correction)
    correction[correction == "good"] <- good.correction.K(X)
  correction <- implemented.for.K(correction, win$type, correction.given)

  divisor <- match.arg(divisor)
  
  if(is.null(bw) && kernel=="epanechnikov") {
    # Stoyan & Stoyan 1995, eq (15.16), page 285
    h <- stoyan /sqrt(npts/areaW)
    hmax <- h
    # conversion to standard deviation
    bw <- h/sqrt(5)
  } else if(is.numeric(bw)) {
    # standard deviation of kernel specified
    # upper bound on half-width
    hmax <- 3 * bw
  } else {
    # data-dependent bandwidth selection: guess upper bound on half-width
    hmax <- 2 * stoyan /sqrt(npts/areaW)
  }


  ########## intensity values #########################

  a <- resolve.reciplambda(X, lambda=lambda, reciplambda=reciplambda,
                           ..., sigma=sigma, adjust=adjust.sigma, varcov=varcov,
                           leaveoneout=leaveoneout, update=update, check=TRUE)
  reciplambda <- a$reciplambda
  lambda      <- a$lambda
  danger      <- a$danger
  dangerous   <- a$dangerous
  
  # renormalise
  if(renormalise && npts > 0) {
    check.1.real(normpower)
    stopifnot(normpower %in% 1:2)
    renorm.factor <- (areaW/sum(reciplambda))^normpower
  } 
  
  ########## r values ############################
  # handle arguments r and breaks 

  rmaxdefault <- rmax.rule("K", win, lambda)        
  breaks <- handle.r.b.args(r, NULL, win, rmaxdefault=rmaxdefault)
  if(!(breaks$even))
    stop("r values must be evenly spaced")
  # extract r values
  r <- breaks$r
  rmax <- breaks$max
  # recommended range of r values for plotting
  alim <- c(0, min(rmax, rmaxdefault))

  ########## smoothing parameters for pcf ############################  
  # arguments for 'density'

  denargs <- resolve.defaults(list(kernel=kernel, bw=bw, adjust=adjust.bw),
                              list(...),
                              list(n=length(r), from=0, to=rmax))
  
  #################################################
  
  # compute pairwise distances

  if(npts > 1) {
    if(is.null(close)) {
      #' find close pairs
      close <- closepairs(X, rmax+hmax)
    } else {
      #' check 'close' has correct format
      needed <- c("i", "j", "xi", "yi", "xj", "yj", "dx", "dy", "d")
      if(any(is.na(match(needed, names(close)))))
        stop(paste("Argument", sQuote("close"),
                   "should have components named",
                   commasep(sQuote(needed))),
             call.=FALSE)
    }
    dIJ <- close$d
    I <- close$i
    J <- close$j
    XI <- ppp(close$xi, close$yi, window=win, check=FALSE)
    wIJ <- reciplambda[I] * reciplambda[J]
  } else {
    undefined <- rep(NaN, length(r))
  }

  # initialise fv object
  
  df <- data.frame(r=r, theo=rep.int(1,length(r)))
  out <- fv(df, "r",
            quote(g[inhom](r)), "theo", ,
            alim,
            c("r","{%s[%s]^{pois}}(r)"),
            c("distance argument r", "theoretical Poisson %s"),
            fname=c("g", "inhom"))

  ###### compute #######

  if(any(correction=="translate")) {
    # translation correction
    if(npts > 1) {
      XJ <- ppp(close$xj, close$yj, window=win, check=FALSE)
      edgewt <- edge.Trans(XI, XJ, paired=TRUE)
      gT <- sewpcf(dIJ, edgewt * wIJ, denargs, areaW, divisor)$g
      if(renormalise) gT <- gT * renorm.factor
    } else gT <- undefined
    out <- bind.fv(out,
                   data.frame(trans=gT),
                   "{hat(%s)[%s]^{Trans}}(r)",
                   "translation-corrected estimate of %s",
                   "trans")
  }
  if(any(correction=="isotropic")) {
    # Ripley isotropic correction
    if(npts > 1) {
      edgewt <- edge.Ripley(XI, matrix(dIJ, ncol=1))
      gR <- sewpcf(dIJ, edgewt * wIJ, denargs, areaW, divisor)$g
      if(renormalise) gR <- gR * renorm.factor
    } else gR <- undefined
    out <- bind.fv(out,
                   data.frame(iso=gR),
                   "{hat(%s)[%s]^{Ripley}}(r)",
                   "isotropic-corrected estimate of %s",
                   "iso")
  }
  
  # sanity check
  if(is.null(out)) {
    warning("Nothing computed - no edge corrections chosen")
    return(NULL)
  }
  
  # which corrections have been computed?
  corrxns <- rev(setdiff(names(out), "r"))

  # default is to display them all
  formula(out) <- . ~ r
  fvnames(out, ".") <- corrxns

  unitname(out) <- unitname(X)
  if(danger)
    attr(out, "dangerous") <- dangerous
  return(out)
}

