R/lintess.R

Defines functions lineartileindex as.data.frame.lintess as.linnet.lintess Window.lintess as.owin.lintess unstack.lintess print.summary.lintess summary.lintess unmark.lintess marks.lintess tilenames.lintess tile.lengths nobjects.lintess print.lintess lintess

Documented in as.data.frame.lintess as.linnet.lintess as.owin.lintess lineartileindex lintess marks.lintess nobjects.lintess print.lintess print.summary.lintess summary.lintess tile.lengths tilenames.lintess unmark.lintess unstack.lintess Window.lintess

#'
#'   lintess.R
#'
#'   Tessellations on a Linear Network
#'
#'   $Revision: 1.45 $   $Date: 2022/05/21 09:52:11 $
#'

lintess <- function(L, df, marks=NULL) {
  verifyclass(L, "linnet")

  if(missing(df) || is.null(df)) {
    # tessellation consisting of a single tile
    ns <- nsegments(L)
    df <- data.frame(seg=seq_len(ns), t0=0, t1=1, tile=factor(1))
    return(lintess(L, df, marks))
  }
  
  #' validate 'df'
  stopifnot(is.data.frame(df))
  dfnames <- colnames(df)
  needed <- c("seg", "t0", "t1", "tile")
  if(any(bad <- is.na(match(needed, dfnames))))
    stop(paste(ngettext(sum(bad), "Column", "Columns"),
               commasep(sQuote(needed[bad])),
               "missing from data frame"),
         call.=FALSE)
  #' straighten out
  df <- df[, needed]
  df$seg <- as.integer(df$seg)
  df$tile <- as.factor(df$tile)
  if(any(reversed <- with(df, t1 < t0)))
    df[reversed, c("t0", "t1")] <- df[reversed, c("t1", "t0")]
  with(df, {
    segU <- sortunique(seg)
    segN <- seq_len(nsegments(L))
    if(length(omitted <- setdiff(segN, segU)) > 0)
      stop(paste(ngettext(length(omitted), "Segment", "Segments"),
                 commasep(omitted),
                 "omitted from data"),
           call.=FALSE)
    if(length(unknown <- setdiff(segU, segN)) > 0)
      stop(paste(ngettext(length(unknown), "Segment", "Segments"),
                 commasep(unknown),
                 ngettext(length(unknown), "do not", "does not"),
                 "exist in the network"),
           call.=FALSE)
    pieces <- split(df, seg)
    for(piece in pieces) {
      t0 <- piece$t0
      t1 <- piece$t1
      thedata <- paste("Data for segment", piece$seg[[1L]])
      if(!any(t0 == 0))
        stop(paste(thedata, "do not contain an entry with t0 = 0"),
             call.=FALSE)
      if(!any(t1 == 1))
        stop(paste(thedata, "do not contain an entry with t1 = 1"),
             call.=FALSE)
      if(any(t1 < 1 & is.na(match(t1, t0))) |
         any(t0 > 0 & is.na(match(t0, t1))))
        stop(paste(thedata, "are inconsistent"),
             call.=FALSE)
    }
  })
  #' validate marks
  if(!is.null(marks)) {
    marks <- as.data.frame(marks)
    nr <- nrow(marks)
    nt <- length(levels(df$tile))
    if(nr == 1L) {
      marks <- marks[rep(1, nt), , drop=FALSE]
      row.names(marks) <- 1:nt
      nr <- nt
    } else if(nr != nt) {
      stop(paste("Wrong number of",
                 ngettext(ncol(marks), "mark values:", "rows of mark values:"),
                 nr, "should be", nt),
           call.=FALSE)
    }
  }
  out <- list(L=L, df=df, marks=marks)
  class(out) <- c("lintess", class(out))
  return(out)
}

print.lintess <- function(x, ...) {
  splat("Tessellation on a linear network")
  nt <- length(levels(x$df$tile))
  splat(nt, ngettext(nt, "tile", "tiles"))
  if(anyNA(x$df$tile)) splat("[An additional tile is labelled NA]")
  if(!is.null(marx <- x$marks)) {
    mvt <- markvaluetype(marx)
    if(length(mvt) == 1) {
      splat("Tessellation has", mvt, "marks")
    } else {
      splat("Tessellation has mark variables",
            commasep(paste(colnames(marx), paren(mvt))))
    }
  }
  return(invisible(NULL))
}

nobjects.lintess <- function(x) {
  length(levels(x$df$tile))
}

tile.lengths <- function(x) {
  if(!inherits(x, "lintess"))
    stop("x should be a tessellation on a linear network (class 'lintess')",
         call.=FALSE)
  seglen <- lengths_psp(as.psp(x$L))
  df <- x$df
  df$fraglen <- with(df, seglen[seg] * (t1-t0))
  tilelen <- with(df, tapplysum(fraglen, list(tile)))
  return(tilelen)
}

tilenames.lintess <- function(x) {
 levels(x$df$tile) 
}

"tilenames<-.lintess" <- function(x, value) {
  levels(x$df$tile) <- value
  return(x)
}

marks.lintess <- function(x, ...) { x$marks }

"marks<-.lintess" <- function(x, ..., value) {
  if(!is.null(value)) {
    value <- as.data.frame(value)
    nt <- length(levels(x$df$tile))
    if(nrow(value) != nt)
      stop(paste("replacement value for marks has wrong length:",
                 nrow(value), "should be", nt),
           call.=FALSE)
    rownames(value) <- NULL
    if(ncol(value) == 1) colnames(value) <- "marks"
  }
  x$marks <- value
  return(x)
}

unmark.lintess <- function(X) {
  X$marks <- NULL
  return(X)
}

summary.lintess <- function(object, ...) {
  df <- object$df
  lev <- levels(df$tile)
  nt <- length(lev)
  nr <- nrow(df)
  seglen <- lengths_psp(as.psp(object$L))
  df$fraglen <- with(df, seglen[seg] * (t1-t0))
  tilelen <- with(df, tapplysum(fraglen, list(tile)))
  hasna <- anyNA(df$tile)
  nalen <- if(hasna) (sum(seglen) - sum(tilelen)) else 0
  marx <- object$marks
  if(!is.null(marx)) {
    mvt <- markvaluetype(marx)
    names(mvt) <- colnames(marx)
    marx <- summary(marx)
  } else mvt <- NULL
  y <- list(nt=nt, nr=nr, lev=lev, seglen=seglen, tilelen=tilelen,
            hasna=hasna, nalen=nalen, marx=marx, mvt=mvt)
  class(y) <- c("summary.lintess", class(y))
  return(y)
}

print.summary.lintess <- function(x, ...) {
  splat("Tessellation on a linear network")
  with(x, {
    splat(nt, "tiles")
    if(hasna) splat("[An additional tile is labelled NA]")
    if(nt <= 30) {
      splat("Tile labels:", paste(lev, collapse=" "))
      splat("Tile lengths:")
      print(signif(tilelen, 4))
    } else {
      splat("Tile lengths (summary):")
      print(summary(tilelen))
    }
    if(hasna) splat("Tile labelled NA has length", nalen)
    if(!is.null(marx)) {
      splat("Tessellation is marked")
      if(length(mvt) == 1) {
        splat("Marks are of type", sQuote(mvt))
      } else {
        splat("Mark variables:",
              commasep(paste(names(mvt), paren(unname(mvt)))))
      }
      splat("Summary of marks:")
      print(marx)
    }
  })
  return(invisible(NULL))
}

plot.lintess <- local({
  
  plot.lintess <- function(x, ..., main, add=FALSE,
                           style=c("colour", "width", "image"),
                           col=NULL,
                           values=marks(x),
                           ribbon=TRUE,
                           ribargs=list(),
                           multiplot=TRUE,
                           do.plot=TRUE
                           ) {
    if(missing(main)) main <- short.deparse(substitute(x))
    style <- match.arg(style)
    df <- x$df
    ntiles <- length(levels(df$tile))
    #' Associate 'values' with tiles
    if(markformat(values) == "hyperframe") 
      values <- as.data.frame(values) #' automatic warning
    switch(markformat(values),
           none = {
             #' no values assigned.
             #' default is tile name
             tn <- tilenames(x)
             values <- factor(tn, levels=tn)
           },
           vector = {
             #' vector of values.
             #' validate length of vector
             check.anyvector(values, ntiles, things="tiles", vname="values")
           },
           dataframe = {
             #' data frame or matrix of values.
             values <- as.data.frame(values)
             if(nrow(values) != ntiles)
               stop(paste("Number of rows of values =", nrow(values),
                          "!=", ntiles, "= number of tiles"),
                    call.=FALSE)
               if(multiplot && ncol(values) > 1 && !add) {
                 #' Multiple Panel Plot
                 result <- multi.plot.lintess(x, ..., style=style, 
                                              main=main, do.plot=do.plot,
                                              ribbon=ribbon, ribargs=ribargs,
                                              col=col)
                 return(invisible(result))
               }
               if(ncol(values) > 1)
                 warning("Using only the first column of values")
                 values <- values[,1]
           },
           stop("Format of values is not understood")
           )
  
    #' Single Panel Plot
    if(style == "image") {
      z <- plot(as.linfun(x, values=values),
                main=main, ..., add=add, do.plot=do.plot,
                ribbon=ribbon, ribargs=ribargs, col=col)
      return(invisible(z))
    }
    #' convert to marked psp object
    L <- as.linnet(x)
    from <- L$from[df$seg]
    to   <- L$to[df$seg]
    V <- vertices(L)
    vx <- V$x
    vy <- V$y
    segdata <- with(df, list(x0=vx[from] * (1-t0) + vx[to] * t0,
                             y0=vy[from] * (1-t0) + vy[to] * t0,
                             x1=vx[from] * (1-t1) + vx[to] * t1,
                             y1=vy[from] * (1-t1) + vy[to] * t1,
                             marks=values[as.integer(tile)]))
    S <- as.psp(segdata, window=Window(L))
    cmap <- plot(S, style=style, add=add, do.plot=do.plot, main=main, 
                 ribbon=ribbon, ribargs=ribargs, col=col, ...)
    return(invisible(cmap))
  }

  multi.plot.lintess <- function(x, ...,
                                 zlim=NULL, col=NULL, equal.ribbon=FALSE) {
    if(equal.ribbon && is.null(zlim) && !inherits(col, "colourmap"))
      zlim <- range(marks(x))
    if(!is.null(zlim)) {
      result <- plot(unstack(x), ..., zlim=zlim, col=col)
    } else {
      result <- plot(unstack(x), ..., col=col)
    }
    return(invisible(result))
  }
  
  plot.lintess
})

  
unstack.lintess <- function(x, ...) {
  marx <- marks(x)
  if(is.null(marx) || is.null(dim(marx)) || ncol(marx) <= 1)
    return(solist(x))
  ux <- unmark(x)
  y <- solapply(as.list(marx), setmarks, x=ux)
  return(y)
} 

as.owin.lintess <- function(W, ...) { as.owin(as.linnet(W), ...) }

Window.lintess <- function(X, ...) { as.owin(as.linnet(X)) }

domain.lintess <- as.linnet.lintess <- function(X, ...) { X$L }

as.data.frame.lintess <- function(x, ...) {
  df <- x$df
  if(!is.null(marx <- marks(x))) {
    marx <- as.data.frame(marx)
    if(ncol(marx) == 1L) colnames(marx) <- "marks"
    marx <- marx[as.integer(df$tile), , drop=FALSE]
    df <- cbind(df, marx)
  }
  df <- as.data.frame(df, ...)
  return(df)
}

lineartileindex <- function(seg, tp, Z,
                            method=c("encode", "C", "interpreted")) {
  method <- match.arg(method)
  df <- if(inherits(Z, "lintess")) Z$df else
        if(is.data.frame(Z)) Z else stop("Format of Z is unrecognised")
  switch(method,
         interpreted = {
           n <- length(seg)
           #' extract tessellation data
           tilenames <- levels(df$tile)
           answer <- factor(rep(NA_integer_, n),
                            levels=seq_along(tilenames),
                            labels=tilenames)
           for(i in seq_along(seg)) {
             tpi <- tp[i]
             segi <- seg[i]
             j <- which(df$seg == segi)
             kk <- which(df[j, "t0"] <= tpi & df[j, "t1"] >= tpi)
             answer[i] <- if(length(kk) == 0) NA else df[j[min(kk)], "tile"]
           }
         },
         encode = {
           #' encode locations as numeric
           loc <- seg - 1 + tp
           #' extract tessellation data and sort them
           df <- df[order(df$seg, df$t0), , drop=FALSE]
           m <- nrow(df)
           #' encode breakpoints as numeric
           bks <- with(df, c(seg - 1 + t0, seg[m]))
           #' which piece contains each query point
           jj <- findInterval(loc, bks,
                              left.open=TRUE, all.inside=TRUE,
                              rightmost.closed=TRUE)
           answer <- df$tile[jj]
         },
         C = {
           #' sort query data
           oo <- order(seg, tp)
           seg <- seg[oo]
           tp  <- tp[oo]
           n <- length(seg)
           #' extract tessellation data and sort them
           df <- df[order(df$seg, df$t0), , drop=FALSE]
           m <- nrow(df)
           #' handle factor 
           dftile <- df$tile
           tilecode <- as.integer(dftile)
           tilenames <- levels(dftile)
           #' launch
           z <- .C(SL_lintileindex,
                   n      = as.integer(n),
                   seg    = as.integer(seg),
                   tp     = as.double(tp),
                   dfn    = as.integer(m),
                   dfseg  = as.integer(df$seg),
                   dft0   = as.double(df$t0),
                   dft1   = as.double(df$t1),
                   dftile = as.integer(tilecode),
                   answer = as.integer(integer(n)),
                   PACKAGE="spatstat.linnet")
           z <- z$answer
           z[z == 0] <- NA
           answer <- integer(n)
           answer[oo] <- z
           answer <- factor(answer,
                            levels=seq_along(tilenames),
                            labels=tilenames)
           })
  return(answer)
}

as.linfun.lintess <- local({

  as.linfun.lintess <- function(X, ..., values=marks(X), navalue=NA) {
    Xdf <- X$df
    tilenames <- levels(Xdf$tile)
    value.is.tile <- is.null(values)
    if(value.is.tile) {
      tilevalues <- factor(tilenames, levels=tilenames)
    } else {
      if(!is.null(dim(values))) values <- values[,1]
      if(length(values) != length(tilenames))
        stop("Length of 'values' should equal the number of tiles", call.=FALSE)
      tilevalues <- values
    }
    f <- function(x, y, seg, tp) {
      k <- as.integer(lineartileindex(seg, tp, Xdf))
      if(!anyNA(k)) {
        result <- tilevalues[k]
      } else {
        ok <- !is.na(k)
        result <- rep(navalue, length(seg))
        result[ok] <- tilevalues[k[ok]]
      }
      return(result)
    }
    g <- linfun(f, X$L)
    attr(g, "explain") <- uitleggen
    return(g)
  }

  uitleggen <- function(x, ...) {
    envf <- environment(attr(x, "f"))
    Xdf <- get("Xdf", envir=envf)
    value.is.tile <- get("value.is.tile", envir=envf) %orifnull% FALSE
    if(value.is.tile) {
      valuename <- "the tile index"
    } else {
      tilevalues <- get("tilevalues", envir=envf)
      valuetype <- typeof(tilevalues)
      valuename <- paste("a value", paren(sQuote(valuetype)),
                         "associated with each tile")
    }
    splat("Function on a network, associated with a tessellation,")
    splat("\treturns", valuename)
    nt <- length(levels(Xdf$tile))
    splat("Tessellation has", nt, ngettext(nt, "tile", "tiles"))
    splat("Function domain:")
    print(as.linnet(x))
    return(invisible(NULL))
  }

  as.linfun.lintess
})

Try the spatstat.linnet package in your browser

Any scripts or data that you put into this service are public.

spatstat.linnet documentation built on Sept. 20, 2024, 5:06 p.m.