Raw File
density.lpp.R
#'
#'    density.lpp.R
#'
#'    Method for 'density' for lpp objects
#'
#'    Copyright (C) 2017 Greg McSwiggan and Adrian Baddeley
#'

density.lpp <- function(x, sigma, ...,
                        weights=NULL,
                        kernel="gaussian", 
                        continuous=TRUE,
                        epsilon=1e-6,
                        verbose=TRUE, debug=FALSE, savehistory=TRUE,
                        old=FALSE) {
  stopifnot(inherits(x, "lpp"))
  kernel <- match.kernel(kernel)

  if(continuous && (kernel == "gaussian") && !old)
     return(PDEdensityLPP(x, sigma, ..., weights=weights))

  L <- as.linnet(x)
  # weights
  np <- npoints(x)
  if(is.null(weights)) {
    weights <- rep(1, np)
  } else {
    stopifnot(is.numeric(weights))
    check.nvector(weights, np, oneok=TRUE)
    if(length(weights) == 1L) weights <- rep(weights, np) 
  }
  # pixellate linear network
  Llines <- as.psp(L)
  linemask <- as.mask.psp(Llines, ...)
  lineimage <- as.im(linemask, value=0)
  # extract pixel centres
  xx <- raster.x(linemask)
  yy <- raster.y(linemask)
  mm <- linemask$m
  xx <- as.vector(xx[mm])
  yy <- as.vector(yy[mm])
  pixelcentres <- ppp(xx, yy, window=as.rectangle(linemask), check=FALSE)
  pixdf <- data.frame(xc=xx, yc=yy)
  # project pixel centres onto lines
  p2s <- project2segment(pixelcentres, Llines)
  projloc <- as.data.frame(p2s$Xproj)
  projmap <- as.data.frame(p2s[c("mapXY", "tp")])
  projdata <- cbind(pixdf, projloc, projmap)
  # initialise pixel values
  values <- rep(0, nrow(pixdf))
  # Extract local coordinates of data
  n <- npoints(x)
  coo <- coords(x)
  seg <- coo$seg
  tp  <- coo$tp
  # lengths of network segments
  Llengths <- lengths.psp(Llines)
  # initialise stack
  stack <- data.frame(seg=integer(0), from=logical(0), 
                  distance=numeric(0), weight=numeric(0))
  # process each data point
  for(i in seq_len(n)) {
    segi <- seg[i]
    tpi  <- tp[i]
    len <- Llengths[segi]
    # evaluate kernel on segment containing x[i]
    relevant <- (projmap$mapXY == segi)
    values[relevant] <- values[relevant] +
      dkernel(len * (projmap$tp[relevant] - tpi),
              kernel=kernel, sd=sigma)
    # push the two tails onto the stack
    stack <- rbind(data.frame(seg = c(segi, segi),
                              from  = c(TRUE, FALSE), 
                              distance = len * c(tpi, 1-tpi),
                              weight = rep(weights[i], 2L)),
                   stack)
  }
  Lfrom <- L$from
  Lto   <- L$to
  if(verbose)
    niter <- 0
  if(savehistory)
    history <- data.frame(iter=integer(0), qlen=integer(0),
                          totmass=numeric(0), maxmass=numeric(0))
  # process the stack
  while(nrow(stack) > 0) {
    if(debug) print(stack)
    masses <- with(stack, abs(weight) * pkernel(distance,
                                                kernel=kernel,
                                                sd=sigma,
                                                lower.tail=FALSE))
    totmass <- sum(masses)
    maxmass <- max(masses)
    if(savehistory)
      history <- rbind(history,
                       data.frame(iter=nrow(history)+1L,
                                  qlen=nrow(stack),
                                  totmass=totmass,
                                  maxmass=maxmass))
    if(verbose) {
      niter <- niter + 1L
      cat(paste("Iteration", niter, "\tStack length", nrow(stack), "\n"))
      cat(paste("Total stack mass", totmass, "\tMaximum", maxmass, "\n"))
    }
    # trim
    tiny <- (masses < epsilon)
    if(any(tiny)) {
      if(verbose) {
        ntiny <- sum(tiny)
        cat(paste("Removing", ntiny,
                  "tiny", ngettext(ntiny, "tail", "tails"), "\n"))
      }
      stack <- stack[!tiny, ]
    }
    if(nrow(stack) == 0)
      break;
    # pop the top of the stack
    H  <- stack[1L, , drop=FALSE]
    stack <- stack[-1L, , drop=FALSE]
    # segment and vertex
    Hseg <- H$seg
    Hvert <- if(H$from) Lfrom[Hseg] else Lto[Hseg]
    Hdist <- H$distance
    # find all segments incident to this vertex
    incident <- which((Lfrom == Hvert) | (Lto == Hvert))
    degree <- length(incident)
    # exclude reflecting paths?
    if(!continuous)
      incident <- setdiff(incident, Hseg)
    for(J in incident) {
      lenJ <- Llengths[J]
      # determine whether Hvert is the 'to' or 'from' endpoint of segment J
      H.is.from <- (Lfrom[J] == Hvert)
      # update weight
      if(continuous) {
        Jweight <- H$weight * (2/degree - (J == Hseg))
      } else {
        Jweight <- H$weight/(degree-1)
      }
      # increment density on segment
      relevant <- (projmap$mapXY == J)
      tp.rel <- projmap$tp[relevant]
      d.rel <- lenJ * (if(H.is.from) tp.rel else (1 - tp.rel))
      values[relevant] <- values[relevant] +
        Jweight * dkernel(d.rel + Hdist, kernel=kernel, sd=sigma)
      # push other end of segment onto stack
      stack <- rbind(data.frame(seg = J,
                                from  = !(H.is.from),
                                distance = lenJ + Hdist,
                                weight = Jweight),
                     stack)
    }
  }
  # attach values to nearest pixels
  Z <- lineimage
  Z[pixelcentres] <- values
  # attach exact line position data
  df <- cbind(projdata, values)
  out <- linim(L, Z, df=df)
  if(savehistory)
    attr(out, "history") <- history
  return(out)
}

density.splitppx <- function(x, sigma, ...) {
  if(!all(sapply(x, is.lpp)))
    stop("Only implemented for patterns on a linear network")
  solapply(x, density.lpp, sigma=sigma, ...)
}

PDEdensityLPP <- function(x, sigma, ..., weights=NULL, 
                          dx=NULL, dt=NULL,
                          fun=FALSE, 
                          finespacing=FALSE, finedata=finespacing) {
  stopifnot(is.lpp(x))
  L <- as.linnet(x)
  check.1.real(sigma)
  check.finite(sigma)
  if(!is.null(weights)) 
    check.nvector(weights, npoints(x))
  if(is.null(dx)) {
    #' default rule for spacing of sample points
    lenths <- lengths.psp(as.psp(L))
    lbar <- mean(lenths)
    nseg <- length(lenths)
    ltot <- lbar * nseg
    if(finespacing) {
      #' specify 30 steps per segment, on average
      dx <- lbar/30
    } else {
      #' use pixel size
      argh <- list(...)
      W <- Frame(x)
      eps <- if(!is.null(argh$eps)) {
               min(argh$eps)
             } else if(!is.null(argh$dimyx)) {
               min(sidelengths(W)/argh$dimyx)
             } else if(!is.null(argh$xy)) {
               with(as.mask(W, xy=xy), min(xstep, ystep))
             } else min(sidelengths(W)/spatstat.options("npixel"))
      dx <- max(eps/1.4, lbar/30)
    }
    D <- ceiling(ltot/dx)
    D <- min(D, .Machine$integer.max)
    dx <- ltot/D
  }
  verdeg <- vertexdegree(L)
  amb <- max(verdeg[L$from] + verdeg[L$to])
  dtmax <- min(0.95 * (dx^2)/amb, sigma^2/(2 * 10), sigma * dx/6)
  if(is.null(dt)) {
    dt <- dtmax
  } else if(dt > dtmax) {
    stop(paste("dt is too large: maximum value", dtmax),
         call.=FALSE)
  }
  a <- FDMKERNEL(lppobj=x, sigma=sigma, dtx=dx, dtt=dt,
                 weights=weights,
                 iterMax=1e6, sparse=TRUE)
  f <- a$kernel_fun
  if(fun) {
    result <- f
  } else if(!finespacing) {
    result <- as.linim(f, ...)
  } else {
    Z <- as.im(as.linim(f, ...))
    df <- a$df
    colnames(df)[colnames(df) == "seg"] <- "mapXY"
    ij <- nearest.valid.pixel(df$x, df$y, Z)
    xy <- data.frame(xc = Z$xcol[ij$col],
                     yc = Z$yrow[ij$row])
    df <- cbind(xy, df)
    result <- linim(domain(f), Z, restrict=FALSE, df=df)
  }
  attr(result, "sigma") <- sigma
  attr(result, "dx") <- a$deltax
  attr(result, "dt") <- a$deltat
  return(result)
}

# Greg's code 
FDMKERNEL <- function(lppobj, sigma, dtt, weights=NULL, iterMax=5000, 
	              sparse=FALSE, dtx) {
  net2 <- as.linnet(lppobj)
#  ends1 <- net2$lines$ends
  lenfs <- lengths.psp(as.psp(net2))
  seg_in_lengths <- pmax(1, round(lenfs/dtx))
  new_lpp <- lixellate(lppobj, nsplit=seg_in_lengths)
  net_nodes <- as.linnet(new_lpp)
  vvv <- as.data.frame(vertices(net_nodes)) 
  vertco_new <- vvv[, c("x", "y")]
  vertseg_new <- vvv$segcoarse # marks
  verttp_new <- vvv$tpcoarse   # marks
  if(npoints(lppobj) == 0) {
    U0 <- numeric(npoints(net_nodes$vertices))
  } else {
    tp1 <- as.numeric(new_lpp$data$tp)
    tp2 <- as.vector(rbind(1 - tp1, tp1))
    newseg <- as.integer(new_lpp$data$seg)
    vert_init_events1 <- as.vector(rbind(net_nodes$from[newseg],
                                         net_nodes$to[newseg]))
    highest_vert <- npoints(net_nodes$vertices)
    vert_numbers <- seq_len(highest_vert)
    ff <- factor(vert_init_events1, levels=vert_numbers)
    ww <- if(is.null(weights)) tp2 else (rep(weights, each=2) * tp2)
    ww <- ww/dtx
    U0 <- tapply(ww, ff, sum)
    U0[is.na(U0)] <- 0
  } 
  M <- round((sigma^2)/(2*dtt))
  if(M < 10) stop("No of time iterations must be > 10, decrease dtt")
  if(M > iterMax)
    stop("No of time iterations exceeds iterMax; increase dtt or increase iterMax")

  alpha <- dtt/(dtx^2)

  A1 <- net_nodes$m *1
#  ml <- nrow(net_nodes$m)

  degree <- colSums(A1)
  dmax <- max(degree)

  A2 <- A1 * alpha
  diag(A2) <- 1 - alpha * degree
  
  if(1 - dmax*alpha < 0)
     stop("alpha must satisfy (1 - HIGHEST VERTEX DEGREE * ALPHA) > 0; decrease dtt or decrease D")

  if(npoints(lppobj) > 0) {
    v <- as.numeric(U0)
    for(j in 1:M)
      v <- A2 %*% v
    finalU <- as.numeric(v)
  } else finalU <- U0

  vert_new <- cbind(vertco_new, vertseg_new, verttp_new)
  colnames(vert_new) <- c("x", "y", "seg", "tp")
  Nodes <- lpp(vert_new, net2, check=FALSE)
  nodemap <- nnfun(Nodes)
  interpUxyst <- function(x, y, seg, tp) {
    finalU[nodemap(x,y,seg,tp)]
  }
  interpU <- linfun(interpUxyst, net2)
  df <- cbind(vert_new, data.frame(values=finalU))
  out <- list(kernel_fun = interpU,
              df         = df, 
              deltax     = dtx,
              deltat     = dtt)
  return(out)
}
back to top