R/geometry.R

Defines functions continuous calc.continuous.P0 gaussquad probabilitymap .interpolation subdivide.mesh get.geometry tricontour_step tricontourmap.list tricontourmap.matrix tricontourmap.inla.mesh tricontourmap tricontour.list display.dim.list generate.trigraph.properties tricontour.matrix tricontour.inla.mesh tricontour as.Lines.raw as.Polygons.raw as.fm_segm.outline as.sp.outline submesh.mesh.tri submesh.mesh submesh.grid outline.on.mesh outlinetri.on.mesh outline.on.grid connect.segments contour.pixels contour.segment.pixels

Documented in continuous submesh.grid submesh.mesh tricontour tricontour.inla.mesh tricontour.list tricontourmap tricontourmap.inla.mesh tricontourmap.list tricontourmap.matrix tricontour.matrix

## geometry.R
##
##   Copyright (C) 2014-2018 Finn Lindgren, David Bolin
##
##   This program is free software: you can redistribute it and/or modify
##   it under the terms of the GNU General Public License as published by
##   the Free Software Foundation, either version 3 of the License, or
##   (at your option) any later version.
##
##   This program is distributed in the hope that it will be useful,
##   but WITHOUT ANY WARRANTY; without even the implied warranty of
##   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
##   GNU General Public License for more details.
##
##   You should have received a copy of the GNU General Public License
##   along with this program.  If not, see <http://www.gnu.org/licenses/>.



## Trace a length 2 contour segment through pixels
##
contour.segment.pixels <- function(c.x, c.y,
                                   edge.x, edge.y,
                                   pairs = FALSE) {
  nx <- length(edge.x)
  ny <- length(edge.y)
  d.x <- diff(c.x)
  d.y <- diff(c.y)
  if (abs(d.x) < abs(d.y)) {
    ## Swap x and y
    idx <- contour.segment.pixels(c.y, c.x, edge.y, edge.x, TRUE)
    idx <- list(x = idx$y, y = idx$x)
  } else if (d.y < 0) {
    ## Flip ordering of segment
    idx <- contour.segment.pixels(c.x[2:1], c.y[2:1], edge.x, edge.y, TRUE)
    idx <- list(x = rev(idx$x), y = rev(idx$y))
  } else {
    idx <- list(x = numeric(0), y = numeric(0))
    ## |d.x| >= d.y >= 0
    dir.x <- sign(d.x) ## Can be 0 only if dir.y is 0
    dir.y <- sign(d.y) ## Must now be >= 0
    if (dir.y == 0) {
      start.y <- min(which((c.y[1] >= edge.y[-ny]) &
        (c.y[1] <= edge.y[-1])))
      if (dir.x == 0) {
        start.x <- min(which((c.x[1] >= edge.x[-nx]) &
          (c.x[1] <= edge.x[-1])))
        end.x <- start.x
      } else if (dir.x > 0) {
        start.x <- min(which((c.x[1] >= edge.x[-nx]) &
          (c.x[1] < edge.x[-1])))
        end.x <- min(which((c.x[2] > edge.x[-nx]) &
          (c.x[2] <= edge.x[-1])))
      } else { ## dir.x < 0
        start.x <- min(which((c.x[1] > edge.x[-nx]) &
          (c.x[1] <= edge.x[-1])))
        end.x <- min(which((c.x[2] >= edge.x[-nx]) &
          (c.x[2] < edge.x[-1])))
      }
      idx$x <- start.x:end.x
      idx$y <- rep(start.y, length(idx$x))
    } else { ## dir.y > 0
      start.y <- min(which((c.y[1] >= edge.y[-ny]) &
        (c.y[1] < edge.y[-1])))
      end.y <- min(which((c.y[2] > edge.y[-ny]) &
        (c.y[2] <= edge.y[-1])))
      if (dir.x > 0) {
        start.x <- min(which((c.x[1] >= edge.x[-nx]) &
          (c.x[1] < edge.x[-1])))
        end.x <- min(which((c.x[2] > edge.x[-nx]) &
          (c.x[2] <= edge.x[-1])))
      } else { ## dir.x < 0
        start.x <- min(which((c.x[1] > edge.x[-nx]) &
          (c.x[1] <= edge.x[-1])))
        end.x <- min(which((c.x[2] >= edge.x[-nx]) &
          (c.x[2] < edge.x[-1])))
      }
      if (start.y == end.y) {
        idx$x <- start.x:end.x
        idx$y <- rep(start.y, length(idx$x))
      } else {
        ## Need to step through each y-pixel-row.
        curr.y <- start.y
        curr.x <- start.x
        while (curr.y < end.y) {
          next.y <- curr.y + 1
          ## Find intersection with edge.y[next.y]
          ## (x-c.x[1])*d.y/d.x + c.y[1] = edge.y[next.y]
          intersect.x <- c.x[1] + d.x / d.y * (edge.y[next.y] - c.y[1])
          if (dir.x > 0) {
            next.x <- min(which((intersect.x >= edge.x[-nx]) &
              (intersect.x < edge.x[-1])))
          } else { ## dir.x < 0
            next.x <- min(which((intersect.x > edge.x[-nx]) &
              (intersect.x <= edge.x[-1])))
          }
          idx$x <- c(idx$x, curr.x:next.x)
          idx$y <- c(idx$y, rep(curr.y, length(curr.x:next.x)))
          curr.y <- next.y
          curr.x <- next.x
        }
        idx$x <- c(idx$x, curr.x:end.x)
        idx$y <- c(idx$y, rep(curr.y, length(curr.x:end.x)))
      }
    }
  }
  if (pairs) {
    return(idx)
  } else {
    return((idx$y - 1) * (nx - 1) + idx$x)
  }
}


contour.pixels <- function(contourlines, pixelgrid, do.plot = 0) {
  if (inherits(pixelgrid, "pgrid")) {
    mid.x <- pixelgrid$upx
    mid.y <- pixelgrid$upy
    edge.x <- pixelgrid$ubx
    edge.y <- pixelgrid$uby
  } else if (inherits(pixelgrid, c("fm_lattice_2d", "inla.mesh.lattice"))) {
    mid.x <- pixelgrid$x
    mid.y <- pixelgrid$y
    step.x <- mid.x[2] - mid.x[1]
    step.y <- mid.y[2] - mid.y[1]
    edge.x <- c(mid.x[1] - step.x / 2, mid.x + step.x / 2)
    edge.y <- c(mid.y[1] - step.y / 2, mid.y + step.y / 2)
  } else {
    stop("Unsupported grid specification class.")
  }
  nx <- length(mid.x)
  ny <- length(mid.y)
  which.pixel <- vector("list", length(contourlines))
  for (level in seq_along(contourlines)) {
    c.x <- contourlines[[level]]$x
    c.y <- contourlines[[level]]$y
    for (segment in seq_len(length(c.x) - 1)) {
      tmp <- contour.segment.pixels(
        c.x[segment + (0:1)],
        c.y[segment + (0:1)],
        edge.x,
        edge.y
      )
      which.pixel[[level]] <- c(which.pixel[[level]], tmp)
      if (do.plot > 1) {
        setvec <- rep(0, nx * ny)
        setvec[sort(unique(unlist(which.pixel)))] <- 1
        image(mid.x, mid.y, matrix(setvec, nx, ny), col = c(0, 3))
        setvec <- rep(0, nx * ny)
        setvec[tmp] <- 1
        image(mid.x, mid.y, matrix(setvec, nx, ny),
          col = c(0, 4),
          add = TRUE
        )
        # plot.contourLines(contourlines, add=TRUE)
        lines(c.x[segment + (0:1)], c.y[segment + (0:1)], col = 2)
        if (do.plot > 2) {
          readline("next")
        }
      }
    }
  }
  if (do.plot > 0) {
    setvec <- rep(0, nx * ny)
    setvec[sort(unique(unlist(which.pixel)))] <- 1
    image(mid.x, mid.y, matrix(setvec, nx, ny), col = c(0, 3))
    # plot.contourLines(contourlines, add=TRUE)
    lines(c.x[segment + (0:1)], c.y[segment + (0:1)], col = 2)
  }

  sort(unique(unlist(which.pixel)))
}


## Connect segments into sequences looping around regions,
## in counterclockwise order.
## Initial segments have inner on their left hand side.
## grp.ccw keeps its orientation (multiple groups allowed)
## grp.cw is traversed in reverse orientation (multiple groups allowed)
##
## while (any segments remain) do
##   while (forward connected segments are found) do
##     follow segments forward
##   if (sequence not closed loop)
##     while (backward connected segments are found) do
##       follow segments backward
##
## sequences : list
## sequences[[k]] : node index vector for a single sequence
## seg : list
## seg[[k]] : segment index vector for a single sequence
## grp : list
## grp[[k]] : group index vector for a single sequence
connect.segments <- function(segment.set,
                             segment.grp = rep(0L, nrow(segment.set)),
                             grp.ccw = unique(segment.grp),
                             grp.cw = integer(0),
                             ccw = TRUE,
                             ambiguous.warning = FALSE) {
  ## Remove unneeded segments
  segment.idx <- seq_len(nrow(segment.set))
  segment.idx <- c(
    segment.idx[segment.grp %in% grp.ccw],
    segment.idx[segment.grp %in% grp.cw]
  )
  segment.set <- segment.set[segment.idx, , drop = FALSE]
  segment.grp <- as.integer(segment.grp[segment.idx])
  ## Unneeded segment removal done
  ## Reverse the direction of segments in grp.cw
  segment.set[segment.grp %in% grp.cw, ] <-
    segment.set[segment.grp %in% grp.cw, 2:1, drop = FALSE]
  ## Segment reversion done
  nE <- nrow(segment.set)
  if (nE == 0) {
    return(list(sequences = list(), seg = list(), grp = list()))
  }
  ## Remap nodes into 1...nV
  segments <- sort(unique(as.vector(segment.set)))
  nV <- length(segments)
  segments.reo <- Matrix::sparseMatrix(
    i = segments,
    j = rep(1, nV),
    x = seq_len(nV),
    dims = c(max(segments), 1)
  )
  segment.set <- matrix(
    segments.reo[as.vector(segment.set)],
    nrow(segment.set), ncol(segment.set)
  )
  ## Node remapping done

  segment.unused <- rep(TRUE, nE)
  segment.unused.idx <- which(segment.unused)
  segment.VV <- Matrix::sparseMatrix(
    i = segment.set[, 1],
    j = segment.set[, 2],
    x = seq_len(nE),
    dims = c(nV, nV)
  )
  loops.seg <- list()
  loops <- list()
  grp <- list()
  while (length(segment.unused.idx) > 0) {
    n <- 0
    loop.seg <- integer(0)
    ## Forward loop
    ##        message("Forwards")
    while (length(segment.unused.idx) > 0) {
      if (n == 0) {
        si <- segment.unused.idx[1]
      } else {
        si <- as.vector(as.matrix(segment.VV[segment.set[si, 2], ]))
        si <- si[si %in% segment.unused.idx]
        if (length(si) == 0) {
          ## End of sequence
          break
        } else {
          if ((length(si) > 1) && ambiguous.warning) {
            warning("Ambiguous segment sequence.")
          }
          si <- si[1]
        }
      }
      segment.unused.idx <-
        segment.unused.idx[segment.unused.idx != si]
      segment.unused[si] <- FALSE
      loop.seg <- c(loop.seg, si)
      n <- n + 1

      ##            print(loop.seg)
      ##            loop <- c(segment.set[loop.seg,1],
      ##                      segment.set[loop.seg[n],2])
      ##            print(loop)
    }
    if ((segment.set[loop.seg[n], 2] != segment.set[loop.seg[1], 1]) &&
      (length(segment.unused.idx) > 0)) {
      ## No closed sequence found
      ## Backward loop
      ##            message("Backwards")
      si <- loop.seg[1]
      while (length(segment.unused.idx) > 0) {
        si <- as.vector(as.matrix(segment.VV[, segment.set[si, 1]]))
        si <- si[si %in% segment.unused.idx]
        if (length(si) == 0) {
          ## End of sequence
          break
        } else if (length(si) > 1) {
          warning("Ambiguous segment sequence.")
          si <- si[1]
        } else {
          si <- si[1]
        }
        segment.unused.idx <-
          segment.unused.idx[segment.unused.idx != si]
        segment.unused[si] <- FALSE
        loop.seg <- c(si, loop.seg)
        n <- n + 1

        ##                print(loop.seg)
        ##                loop <- c(segment.set[loop.seg,1],
        ##                          segment.set[loop.seg[n],2])
        ##                print(loop)
      }
    }

    loop <- c(
      segment.set[loop.seg, 1],
      segment.set[loop.seg[n], 2]
    )
    loop.grp <- segment.grp[loop.seg]

    loops.seg <- c(loops.seg, list(loop.seg))
    loops <- c(loops, list(loop))
    grp <- c(grp, list(loop.grp))
  }

  ## Remap nodes and segments back to original indices
  for (k in seq_along(loops)) {
    loops[[k]] <- segments[loops[[k]]]
    loops.seg[[k]] <- segment.idx[loops.seg[[k]]]
  }
  ## Node and segment index remapping done

  if (!ccw) {
    for (k in seq_along(loops)) {
      loops[[k]] <- rev(loops[[k]])
      loops.seg[[k]] <- rev(loops.seg[[k]])
      grp[[k]] <- rev(grp[[k]])
    }
  }

  return(list(sequences = loops, seg = loops.seg, grp = grp))
}

## Compute simple outline of 1/0 set on a grid, eliminating spikes.
outline.on.grid <- function(z, grid) {
  if (missing(grid) || is.null(grid)) {
    if (!is.matrix(z)) {
      stop("grid not supplied, and z is not a matrix")
    }
    ni <- nrow(z)
    nj <- ncol(z)
    z <- (z != FALSE)
    grid <- list(x = seq(0, 1, length = ni), y = seq(0, 1, length = nj))
    grid$loc <- cbind(rep(grid$x, times = nj), rep(grid$y, each = ni))
  } else {
    ni <- grid$dims[1]
    nj <- grid$dims[2]
    z <- matrix(z != FALSE, ni, nj)
  }

  ij2k <- function(i, j) {
    return((j - 1) * ni + i)
  }

  seg <- matrix(integer(), 0, 2)
  bnd.seg <- matrix(integer(), 0, 2)

  ## Extract horizontal segment locations:
  zz.p <- z[-ni, -c(1, 2), drop = FALSE] + z[-1, -c(1, 2), drop = FALSE]
  zz.n <- z[-ni, -c(1, nj), drop = FALSE] + z[-1, -c(1, nj), drop = FALSE]
  zz.m <- z[-ni, -c(nj - 1, nj), drop = FALSE] + z[-1, -c(nj - 1, nj), drop = FALSE]
  zz <- (zz.n == 2) * (zz.p + zz.m > 0) * ((zz.p == 0) * 1 + (zz.m == 0) * 2)
  ## zz=0 : No segment, zz=1 : set is below, zz=2 : set is above
  ijv <- private.sparse.gettriplet(zz == 1)
  idx <- which(ijv$x > 0)
  seg <- rbind(
    seg,
    cbind(
      ij2k(ijv$i[idx] + 1, ijv$j[idx] + 1),
      ij2k(ijv$i[idx], ijv$j[idx] + 1)
    )
  )
  ijv <- private.sparse.gettriplet(zz == 2)
  idx <- which(ijv$x > 0)
  seg <- rbind(
    seg,
    cbind(
      ij2k(ijv$i[idx], ijv$j[idx] + 1),
      ij2k(ijv$i[idx] + 1, ijv$j[idx] + 1)
    )
  )

  ## Extract vertical segment locations:
  zz.p <- z[-c(1, 2), -nj, drop = FALSE] + z[-c(1, 2), -1, drop = FALSE]
  zz.n <- z[-c(1, ni), -nj, drop = FALSE] + z[-c(1, ni), -1, drop = FALSE]
  zz.m <- z[-c(ni - 1, ni), -nj, drop = FALSE] + z[-c(ni - 1, ni), -1, drop = FALSE]
  zz <- (zz.n == 2) * (zz.p + zz.m > 0) * ((zz.p == 0) * 1 + (zz.m == 0) * 2)
  ## zz=0 : No segment, zz=1 : set is on left, zz=2 : set is on right
  ijv <- private.sparse.gettriplet(zz == 1)
  idx <- which(ijv$x > 0)
  seg <- rbind(
    seg,
    cbind(
      ij2k(ijv$i[idx] + 1, ijv$j[idx]),
      ij2k(ijv$i[idx] + 1, ijv$j[idx] + 1)
    )
  )
  ijv <- private.sparse.gettriplet(zz == 2)
  idx <- which(ijv$x > 0)
  seg <- rbind(
    seg,
    cbind(
      ij2k(ijv$i[idx] + 1, ijv$j[idx] + 1),
      ij2k(ijv$i[idx] + 1, ijv$j[idx])
    )
  )

  ## Extract diagonal segment locations;
  ## Quadruples with three 1, one 0
  zz <- (z[-ni, -nj, drop = FALSE] + z[-1, -nj, drop = FALSE] +
    z[-ni, -1, drop = FALSE] + z[-1, -1, drop = FALSE])
  ## Which element was 0?
  zz <- (zz == 3) *
    (15 - (z[-ni, -nj, drop = FALSE] * 1 + z[-1, -nj, drop = FALSE] * 2 +
      z[-ni, -1, drop = FALSE] * 4 + z[-1, -1, drop = FALSE] * 8))
  ## zz=0 : No diagonal
  ## zz=1 : (0,0), zz=2 : (1,0), zz=4 : (0,1), zz=8 : (1,1)
  ijv <- private.sparse.gettriplet(zz == 1)
  idx <- which(ijv$x > 0)
  seg <- rbind(
    seg,
    cbind(
      ij2k(ijv$i[idx], ijv$j[idx] + 1),
      ij2k(ijv$i[idx] + 1, ijv$j[idx])
    )
  )
  ijv <- private.sparse.gettriplet(zz == 2)
  idx <- which(ijv$x > 0)
  seg <- rbind(
    seg,
    cbind(
      ij2k(ijv$i[idx], ijv$j[idx]),
      ij2k(ijv$i[idx] + 1, ijv$j[idx] + 1)
    )
  )
  ijv <- private.sparse.gettriplet(zz == 4)
  idx <- which(ijv$x > 0)
  seg <- rbind(
    seg,
    cbind(
      ij2k(ijv$i[idx] + 1, ijv$j[idx] + 1),
      ij2k(ijv$i[idx], ijv$j[idx])
    )
  )
  ijv <- private.sparse.gettriplet(zz == 8)
  idx <- which(ijv$x > 0)
  seg <- rbind(
    seg,
    cbind(
      ij2k(ijv$i[idx] + 1, ijv$j[idx]),
      ij2k(ijv$i[idx], ijv$j[idx] + 1)
    )
  )

  ## Extract horizontal boundary segment locations:
  zz.pm <- z[-ni, c(2, nj - 1), drop = FALSE] + z[-1, c(2, nj - 1), drop = FALSE]
  zz.n <- z[-ni, c(1, nj), drop = FALSE] + z[-1, c(1, nj), drop = FALSE]
  zz <- (zz.n == 2) * (zz.pm > 0) * matrix(rep(c(2, 1), each = ni - 1), ni - 1, 2)
  ## zz=0 : No segment, zz=1 : set is below, zz=2 : set is above
  ijv <- private.sparse.gettriplet(zz == 1)
  idx <- which(ijv$x > 0)
  bnd.seg <- rbind(
    bnd.seg,
    cbind(
      ij2k(ijv$i[idx] + 1, nj),
      ij2k(ijv$i[idx], nj)
    )
  )
  ijv <- private.sparse.gettriplet(zz == 2)
  idx <- which(ijv$x > 0)
  bnd.seg <- rbind(
    bnd.seg,
    cbind(
      ij2k(ijv$i[idx], 1),
      ij2k(ijv$i[idx] + 1, 1)
    )
  )

  ## Extract vertical boundary segment locations:
  zz.pm <- z[c(2, ni - 1), -nj, drop = FALSE] + z[c(2, ni - 1), -1, drop = FALSE]
  zz.n <- z[c(1, ni), -nj, drop = FALSE] + z[c(1, ni), -1, drop = FALSE]
  zz <- (zz.n == 2) * (zz.pm > 0) * matrix(rep(c(2, 1), times = nj - 1), 2, nj - 1)
  ## zz=0 : No segment, zz=1 : set is on left, zz=2 : set is on right
  ijv <- private.sparse.gettriplet(zz == 1)
  idx <- which(ijv$x > 0)
  bnd.seg <- rbind(
    bnd.seg,
    cbind(
      ij2k(ni, ijv$j[idx]),
      ij2k(ni, ijv$j[idx] + 1)
    )
  )
  ijv <- private.sparse.gettriplet(zz == 2)
  idx <- which(ijv$x > 0)
  bnd.seg <- rbind(
    bnd.seg,
    cbind(
      ij2k(1, ijv$j[idx] + 1),
      ij2k(1, ijv$j[idx])
    )
  )

  segment.grp <- rep(c(1L, 0L), c(nrow(seg), nrow(bnd.seg)))
  segment.set <- rbind(seg, bnd.seg)

  list(loc = grid$loc, idx = segment.set, grp = segment.grp)
}

## Compute simple outline of 1/0 set on a mesh, eliminating spikes.
## Returns a vector of triangle indices for the indicator set.
outlinetri.on.mesh <- function(z, mesh, complement = FALSE) {
  t.count <- rowSums(matrix((z >= 0.5)[mesh$graph$tv], nrow(mesh$graph$tv), 3))

  if (complement) {
    t.keep <- which(t.count < 3)
  } else {
    t.keep <- which(t.count == 3)
  }

  t.keep
}

## Compute simple outline of 1/0 set on a mesh, eliminating spikes.
outline.on.mesh <- function(z, mesh, complement = FALSE) {
  t.keep <- outlinetri.on.mesh(z, mesh, complement)

  if (length(t.keep) > 0) {
    graph <-
      generate.trigraph.properties(
        list(tv = mesh$graph$tv[t.keep, , drop = FALSE]),
        Nv = nrow(mesh$loc)
      )
    idx <- graph$ev[is.na(graph$ee), , drop = FALSE]
  } else {
    idx <- matrix(0L, 0, 2)
  }

  if (complement) {
    grp <- rep(1L, nrow(idx))
  } else {
    grp <- rep(3L, nrow(idx))
  }

  list(loc = mesh$loc, idx = idx, grp = grp)
}


#' Extract a part of a grid
#'
#' Extracts a part of a grid.
#'
#' @param z A matrix with values indicating which nodes that should be
#' present in the submesh.
#' @param grid A list with locations and dimensions of the grid.
#'
#' @return An \code{inla.mesh} object.
#' @export
#' @author Finn Lindgren \email{finn.lindgren@@gmail.com}
#' @examples
#' \dontrun{
#' if (require("fmesher")) {
#'   nxy <- 40
#'   x <- seq(from = 0, to = 4, length.out = nxy)
#'   lattice <- fm_lattice_2d(x = x, y = x)
#'   mesh <- fm_rcdt_2d_inla(lattice = lattice, extend = FALSE, refine = FALSE)
#'
#'   # extract a part of the mesh inside a circle
#'   xy.in <- rowSums((mesh$loc[, 1:2] - 2)^2) < 1
#'   submesh <- submesh.grid(
#'     matrix(xy.in, nxy, nxy),
#'     list(loc = mesh$loc, dim = c(nxy, nxy))
#'   )
#'   plot(mesh$loc[, 1:2])
#'   lines(2 + cos(seq(0, 2 * pi, length.out = 100)), 2 + sin(seq(0, 2 * pi, length.out = 100)))
#'   plot(submesh, add = TRUE)
#'   points(mesh$loc[xy.in, 1:2], col = "2")
#' }
#' }
#'
submesh.grid <- function(z, grid = NULL) {
  outline <- outline.on.grid(z, grid)
  fmesher::fm_rcdt_2d_inla(
    loc = grid$loc,
    boundary = as.fm_segm.outline(outline),
    refine = FALSE
  )
}


#' Extract a part of a mesh
#'
#' Extracts a part of a mesh
#'
#' @param z A matrix with values indicating which nodes that should be
#' present in the submesh.
#' @param mesh An \code{fm_mesh_2d} object.
#'
#' @return An \code{fm_mesh_2d} object.
#' @export
#' @author Finn Lindgren \email{finn.lindgren@@gmail.com}
#' @examples
#' \dontrun{
#' if (require(fmesher)) {
#'   nxy <- 30
#'   x <- seq(from = 0, to = 4, length.out = nxy)
#'   lattice <- fm_lattice_2d(x = x, y = x)
#'   mesh <- fm_mesh_2d_inla(lattice = lattice, extend = FALSE, refine = FALSE)
#'
#'   # extract a part of the mesh inside a circle
#'   xy.in <- rowSums((mesh$loc[, 1:2] - 2)^2) < 1
#'   submesh <- excursions:::submesh.mesh(matrix(xy.in, nxy, nxy), mesh)
#'   plot(mesh$loc[, 1:2])
#'   lines(2 + cos(seq(0, 2 * pi, length.out = 100)), 2 + sin(seq(0, 2 * pi, length.out = 100)))
#'   plot(submesh, add = TRUE)
#'   points(mesh$loc[xy.in, 1:2], col = "2")
#' }
#' }
#'
submesh.mesh <- function(z, mesh) {
  submesh.mesh.tri(outlinetri.on.mesh(z, mesh), mesh)
}
submesh.mesh.tri <- function(tri, mesh) {
  tv <- mesh$graph$tv[tri, , drop = FALSE]
  v <- sort(unique(as.vector(tv)))
  idx <- rep(as.integer(NA), nrow(mesh$loc))
  idx[v] <- seq_len(length(v))
  tv <- matrix(idx[tv], nrow(tv), 3)
  loc <- mesh$loc[v, , drop = FALSE]

  mesh <- fmesher::fm_rcdt_2d_inla(loc = loc, tv = tv, refine = FALSE)

  idx <- rep(as.integer(NA), length(idx))
  idx[v] <- mesh$idx$loc
  mesh$idx$loc <- idx

  mesh
}


as.sp.outline <- function(outline,
                          grp.ccw = unique(outline$grp),
                          grp.cw = integer(0),
                          ccw = FALSE,
                          ambiguous.warning = FALSE,
                          ID = "outline",
                          closed = TRUE,
                          ...) {
  seg <- connect.segments(outline$idx, outline$grp,
    grp.ccw = grp.ccw,
    grp.cw = grp.cw,
    ccw = ccw,
    ambiguous.warning = ambiguous.warning
  )

  coords <- list()
  for (k in seq_along(seg$sequences)) {
    coords <- c(coords, list(outline$loc[seg$sequences[[k]],
      1:2,
      drop = FALSE
    ]))
  }

  if (closed) {
    as.Polygons.raw(coords, ID = ID)
  } else {
    as.Lines.raw(coords, ID = ID)
  }
}


as.fm_segm.outline <- function(outline,
                               grp.ccw = unique(outline$grp),
                               grp.cw = integer(0),
                               grp,
                               ...) {
  ik.ccw <- outline$grp %in% grp.ccw
  ik.cw <- outline$grp %in% grp.cw
  if (missing(grp)) {
    grp <- c(outline$grp[ik.ccw], outline$grp[ik.cw])
  }
  fmesher::fm_segm(
    loc = outline$loc,
    idx = rbind(
      outline$idx[ik.ccw, , drop = FALSE],
      outline$idx[ik.cw, 2:1, drop = FALSE]
    ),
    grp = grp
  )
}



as.Polygons.raw <- function(sequences, ID = " ") {
  if (!requireNamespace("sp", quietly = TRUE)) {
    stop("The 'sp' package is needed.")
  }
  polys <- lapply(
    sequences,
    function(x) {
      if (is.list(x)) {
        p <- sp::Polygon(cbind(x$x, x$y))
      } else {
        p <- sp::Polygon(x)
      }
      if (p@area > 0) {
        p
      } else {
        warning("Skipping zero area polygon, in as.Polygons.raw.")
        NULL
      }
    }
  )
  if (length(polys) == 0) {
    sp <- NULL
  } else {
    ok <- unlist(lapply(polys, function(x) is(x, "Polygon")))
    sp <- sp::Polygons(polys[ok], ID = ID)
  }
  sp
}

as.Lines.raw <- function(cl, ID = " ") {
  if (!requireNamespace("sp", quietly = TRUE)) {
    stop("The 'sp' package is needed.")
  }
  polys <- lapply(
    cl,
    function(x) {
      if (is.list(x)) {
        p <- sp::Line(cbind(x$x, x$y))
      } else {
        p <- sp::Line(x)
      }
      p
    }
  )
  if (length(polys) == 0) {
    sp <- NULL
  } else {
    sp <- sp::Lines(polys, ID = ID)
  }
  sp
}

#' Calculate contour curves on a triangulation
#'
#' Calculates contour curves and/or regions between them,
#' for functions defined on a triangulation
#'
#' @export
#' @param x An object generated by a call to \code{inla.mesh.2d} or
#'   \code{inla.mesh.create}, a triangle-vertex index matrix, or a list
#'   of triangulation information, \code{list(loc, graph=list(tv))}.
#' @param z A vector containing the values to be contoured
#'   (\code{NA}s are allowed).
#' @param nlevels Number of contour levels desired, if and only if
#'   \code{levels} is not supplied.
#' @param levels Numeric vector of levels at which to calculate contour lines.
#' @param ... Additional arguments passed to the other methods.
#' @return For \code{tricontour}, a list with some of the fields that
#'   \code{inla.mesh.segment} objects have:
#'   \item{loc}{A coordinate matrix}
#'   \item{idx}{Contour segment indices, as a 2-column matrix, each row
#'     indexing a single segment}
#'   \item{grp}{A vector of group labels.  Each segment has a label, in
#'     \code{1,...,nlevels*2+1}, where even labels indicate interior
#'     on-level contour segments, and odd labels indicate boundary segments
#'     between levels.}
#'   For \code{tricontourmap}, a list:
#'   \item{contour}{A list of \code{sp} or \code{inla.mesh.segment} objects
#'     defining countour curves (level sets)}
#'   \item{map}{A list of \code{sp} or \code{inla.mesh.segment} objects
#'     enclosing regions between level sets}
#'
#' @author Finn Lindgren \email{finn.lindgren@@gmail.com}
#'
#' @examples
#' \dontrun{
#' if (require("fmesher")) {
#'   ## Generate mesh and SPDE model
#'   n.lattice <- 20 # increase for more interesting, but slower, examples
#'   x <- seq(from = 0, to = 10, length.out = n.lattice)
#'   lattice <- fm_lattice_2d(x = x, y = x)
#'   mesh <- fm_rcdt_2d_inla(lattice = lattice, extend = FALSE, refine = FALSE)
#'
#'   ## Generate an artificial sample
#'   sigma2.e <- 0.1
#'   n.obs <- 1000
#'   obs.loc <- cbind(
#'     runif(n.obs) * diff(range(x)) + min(x),
#'     runif(n.obs) * diff(range(x)) + min(x)
#'   )
#'   Q <- fm_matern_precision(mesh, alpha = 2, rho = 3, sigma = 1)
#'   x <- fm_sample(n = 1, Q = Q)
#'   A <- fm_basis(mesh, loc = obs.loc)
#'   Y <- as.vector(A %*% x + rnorm(n.obs) * sqrt(sigma2.e))
#'
#'   ## Calculate posterior
#'   Q.post <- (Q + (t(A) %*% A) / sigma2.e)
#'   mu.post <- as.vector(solve(Q.post, (t(A) %*% Y) / sigma2.e))
#'
#'   ## Calculate continuous contours
#'   tric <- tricontour(mesh,
#'     z = mu.post,
#'     levels = as.vector(quantile(x, c(0.25, 0.75)))
#'   )
#'
#'   ## Discrete domain contours
#'   map <- contourmap(
#'     n.levels = 2, mu = mu.post, Q = Q.post,
#'     alpha = 0.1, compute = list(F = FALSE), max.threads = 1
#'   )
#'
#'   ## Calculate continuous contour map
#'   setsc <- tricontourmap(mesh,
#'     z = mu.post,
#'     levels = as.vector(quantile(x, c(0.25, 0.75)))
#'   )
#'
#'   ## Plot the results
#'   reo <- mesh$idx$lattice
#'   idx.setsc <- setdiff(names(setsc$map), "-1")
#'   cols2 <- contourmap.colors(map,
#'     col = heat.colors(100, 0.5, rev = TRUE),
#'     credible.col = grey(0.5, 0)
#'   )
#'   names(cols2) <- as.character(-1:2)
#'
#'   par(mfrow = c(1, 2))
#'   image(matrix(mu.post[reo], n.lattice, n.lattice),
#'     main = "mean", axes = FALSE, asp = 1
#'   )
#'   plot(setsc$map[idx.setsc], col = cols2[idx.setsc])
#'   par(mfrow = c(1, 1))
#' }
#' }
tricontour <-
  function(x, z, nlevels = 10,
           levels = pretty(range(z, na.rm = TRUE), nlevels),
           ...) {
    UseMethod("tricontour")
  }

#' @rdname tricontour
#' @export
tricontour.inla.mesh <-
  function(x, z, nlevels = 10,
           levels = pretty(range(z, na.rm = TRUE), nlevels),
           ...) {
    tricontour.list(x$graph,
      z = z,
      nlevels = nlevels, levels = levels,
      loc = x$loc, ...
    )
  }

#' @rdname tricontour
#' @export
#' @param loc coordinate matrix, to be supplied when \code{x} is given as a
#'   triangle-vertex index matrix only.
tricontour.matrix <-
  function(x, z, nlevels = 10,
           levels = pretty(range(z, na.rm = TRUE), nlevels),
           loc, ...) {
    tricontour.list(list(tv = x),
      z = z,
      nlevels = nlevels, levels = levels,
      loc = loc, ...
    )
  }



## Generate triangulation graph properties
## Nt,Ne,Nv,ev,et,eti,ee,te,tt,tti
generate.trigraph.properties <- function(x, Nv = NULL) {
  stopifnot(is.list(x))
  stopifnot("tv" %in% names(x))

  x$Nt <- nrow(x$tv)
  x$Ne <- 3 * x$Nt ## number of unidirectional edges
  if (is.null(Nv)) {
    x$Nv <- max(as.vector(x$tv))
  } else {
    x$Nv <- Nv
  }

  x$ev <- cbind(
    as.vector(x$tv[, c(2, 3, 1)]),
    as.vector(x$tv[, c(3, 1, 2)])
  )
  x$et <- rep(seq_len(x$Nt), times = 3)
  x$eti <- rep(1:3, each = x$Nt) ## Opposing vertex within-triangle-indices
  x$te <- matrix(seq_len(x$Ne), x$Nt, 3)
  ev <- Matrix::sparseMatrix(
    i = rep(seq_len(x$Ne), times = 2),
    j = as.vector(x$ev),
    x = rep(1, x$Ne * 2),
    dims = c(x$Ne, x$Nv)
  )
  ev.tr <- private.sparse.gettriplet(ev %*% t(ev))
  ee <- cbind(
    ev.tr$i[(ev.tr$x == 2) & (ev.tr$i != ev.tr$j)],
    ev.tr$j[(ev.tr$x == 2) & (ev.tr$i != ev.tr$j)]
  )
  x$ee <- rep(NA, x$Ne)
  x$ee[ee[, 1]] <- ee[, 2]

  if (is.null(x$tt) ||
    (nrow(x$tt) != x$Nt)) { ## Workaround for bug in fmesher < 2014-09-12
    x$tt <- matrix(x$et[x$ee], x$Nt, 3)
  }
  if (is.null(x$tti)) {
    x$tti <- matrix(x$eti[x$ee], x$Nt, 3)
  }

  x
}



display.dim.list <- function(x) {
  lapply(
    as.list(sort(names(x))),
    function(xx) {
      sz <- dim(x[[xx]])
      type <- mode(x[[xx]])
      cl <- class(x[[xx]])[[1]]
      if (is.null(sz)) {
        sz <- length(x[[xx]])
      }
      message(paste(xx, " = ", paste(sz, collapse = " x "),
        " (", type, ", ", cl, ")",
        sep = ""
      ))
    }
  )
  invisible()
}

#' @rdname tricontour
#' @export
#' @param type \code{"+"} or \code{"-"}, indicating positive or negative
#'  association. For \code{+}, the generated contours enclose regions
#'  where \eqn{u_1 \leq z < u_2}, for \code{-} the regions fulfil \eqn{u_1
#'    < z \leq u_2}.
#' @param tol tolerance for determining if the value at a vertex lies on a level.
tricontour.list <- function(x, z, nlevels = 10,
                            levels = pretty(range(z, na.rm = TRUE), nlevels),
                            loc, type = c("+", "-"), tol = 1e-7, ...) {
  ## Returns val=list(loc, idx, grp), where
  ##   grp = 1,...,nlevels*2+1, level groups are even, 2,4,...
  ## Suitable for
  ##   inla.mesh.segment(val$loc, val$idx[val$grp==k], val$idx[val$grp==k])
  ##     (supports R2 and S2)
  ## and, for odd k=1,3,...,nlevels*2-1,nlevels*2+1,
  ##   seg <- as.fm_segm.outline(val, grp.ccw=c(k-1,k), grp.cw=c(k+1))
  ##   sp <- as.sp.outline(val, grp.ccw=c(k-1,k), grp.cw=c(k+1), ccw=FALSE)

  type <- match.arg(type)
  nlevels <- length(levels)

  x <- generate.trigraph.properties(x)
  Nv <- x$Nv

  ## Find vertices on levels
  ## For each edge on a level, store edge if
  ##   it is a boundary edge, or
  ##   opposing vertices are +/-, and either
  ##     0/- (type="+", u1 <= z < u2) or
  ##     +/0 (type="-", u1 < z <= u2)
  ## For each edge crossing at least one level,
  ##   calculate splitting vertices
  ##   if boundary edge, store new split edges
  ## For each triangle, find non-level edge crossings, and
  ##   store new vertex-edge crossing edges
  ##   store new edge-edge crossing edges
  idx <- matrix(0, 0, 2)
  grp <- integer(0)

  ## Find vertices on levels
  vcross.lev <- integer(length(z))
  for (lev in seq_along(levels)) {
    signv <- (z > levels[lev] + tol) - (z < levels[lev] - tol)
    vcross.lev[signv == 0] <- lev
  }
  ## Find level crossing span for each edge (includes flat edges in levels)
  ecross.grp.lower <- rep(1L, x$Ne)
  ecross.grp.upper <- rep(2L * length(levels) + 1L, x$Ne)
  for (lev in seq_along(levels)) {
    signv <- (z > levels[lev] + tol) - (z < levels[lev] - tol)
    lev.grp <- 2L * lev
    i <- pmin(signv[x$ev[, 1]], signv[x$ev[, 2]])
    ecross.grp.lower[i == 0] <- lev.grp
    ecross.grp.lower[i > 0] <- lev.grp + 1L
  }
  for (lev in rev(seq_along(levels))) {
    signv <- (z > levels[lev] + tol) - (z < levels[lev] - tol)
    lev.grp <- 2L * lev
    i <- pmax(signv[x$ev[, 1]], signv[x$ev[, 2]])
    ecross.grp.upper[i == 0] <- lev.grp
    ecross.grp.upper[i < 0] <- lev.grp - 1L
  }

  ## For each edge on a level, store edge if ...
  ##   it is a boundary edge, or
  ##   opposing vertices are +/-, and either
  ##     0/- (type="+", u1 <= z < u2) or
  ##     +/0 (type="-", u1 < z <= u2)
  cross1 <- vcross.lev[x$ev[, 1]] ## left neighbour
  cross2 <- vcross.lev[x$ev[, 2]] ## right neighbour
  vv.edges <- which((cross1 > 0) & (cross2 > 0) & (cross1 == cross2))
  for (edge in vv.edges) {
    lev <- cross1[edge]
    v1 <- x$tv[x$et[edge], x$eti[edge]]
    sign1 <- (z[v1] > levels[lev] + tol) - (z[v1] < levels[lev] - tol)
    neighb.t <- x$tt[x$et[edge] + (x$eti[edge] - 1) * x$Nt]
    if (is.na(neighb.t)) {
      v2 <- NA
      sign2 <- NA
    } else {
      v2 <- x$tv[neighb.t, x$tti[x$et[edge] + (x$eti[edge] - 1) * x$Nt]]
      sign2 <- (z[v2] > levels[lev] + tol) - (z[v2] < levels[lev] - tol)
    }
    if (is.na(neighb.t)) {
      ## Make sure the edge gets the right label
      if (sign1 != 0) {
        idx <- rbind(idx, x$ev[edge, ])
        ## Associate with the neighbour
        grp <- c(grp, lev * 2L + sign1)
      } else {
        idx <- rbind(idx, x$ev[edge, ])
        grp <- c(grp, lev * 2L + (type == "+") - (type == "-"))
      }
    } else if (((sign1 > 0) && (sign2 < 0)) ||
      ((type == "+") && ((sign1 == 0) && (sign2 < 0))) ||
      ((type == "-") && ((sign1 > 0) && (sign2 == 0)))) {
      idx <- rbind(idx, x$ev[edge, ])
      grp <- c(grp, lev * 2L)
    }
  }

  ## For each boundary edge entirely between levels
  ##   store edge
  e.lower <- ecross.grp.lower + ((ecross.grp.lower - 1) %% 2)
  e.upper <- ecross.grp.upper - ((ecross.grp.upper - 1) %% 2)
  e.on.bnd <- which(is.na(x$tti[x$et + (x$eti - 1) * x$Nt]))
  e.noncrossing <- e.on.bnd[e.lower[e.on.bnd] == e.upper[e.on.bnd]]
  idx <- rbind(idx, x$ev[e.noncrossing, , drop = FALSE])
  grp <- c(grp, as.integer(e.lower[e.noncrossing]))

  ## For each edge crossing at least one level,
  ##   calculate splitting vertices
  ##   if boundary edge, store new split edges
  e.crossing <- which(e.lower < e.upper)
  e.bndcrossing <- e.on.bnd[e.lower[e.on.bnd] < e.upper[e.on.bnd]]
  Nnewv <- (sum(e.upper[e.crossing] - e.lower[e.crossing]) / 2 +
    sum(e.upper[e.bndcrossing] - e.lower[e.bndcrossing]) / 2) / 2
  loc.new <- matrix(NA, Nnewv, ncol(loc))
  loc.last <- 0L
  e.newv <- sparseMatrix(
    i = integer(0), j = integer(0), x = double(0),
    dims = c(x$Ne, length(levels))
  )
  for (edge in e.crossing) {
    is.boundary.edge <- is.na(x$tt[x$et[edge], x$eti[edge]])
    if (is.boundary.edge) {
      edge.reverse <- NA
    } else {
      ## Interior edges appear twice; handle each only once.
      if (x$ev[edge, 1] > x$ev[edge, 2]) {
        next
      }
      edge.reverse <- x$te[
        x$tt[x$et[edge], x$eti[edge]],
        x$tti[x$et[edge], x$eti[edge]]
      ]
    }
    ## lev = (1-beta_lev) * z1 + beta_lev * z2
    ##     = z1 + beta_lev * (z2-z1)
    ## beta_lev = (lev-z1)/(z2-z1)
    e.levels <- ((e.lower[edge] + 1) / 2):((e.upper[edge] - 1) / 2)
    loc.new.idx <- loc.last + seq_along(e.levels)
    beta <- ((levels[e.levels] - z[x$ev[edge, 1]]) /
      (z[x$ev[edge, 2]] - z[x$ev[edge, 1]]))
    ## print(str(loc.new))
    ## print(e.levels)
    ## print(c(loc.last, loc.new.idx))
    ##    message("???")
    ##    message(paste(paste(dim(loc.new), collapse=", "),
    ##                  loc.new.idx, collapse="; "))
    ## Temporary workaround for boundary part error:
    if (max(loc.new.idx) > nrow(loc.new)) {
      ##      browser()
      loc.new <- rbind(
        loc.new,
        matrix(
          NA,
          max(loc.new.idx) - nrow(loc.new),
          ncol(loc.new)
        )
      )
    }

    ##    xxx <-
    ##      (as.matrix(1-beta) %*% loc[x$ev[edge,1],,drop=FALSE]+
    ##       as.matrix(beta) %*% loc[x$ev[edge,2],,drop=FALSE])

    loc.new[loc.new.idx, ] <-
      (as.matrix(1 - beta) %*% loc[x$ev[edge, 1], , drop = FALSE] +
        as.matrix(beta) %*% loc[x$ev[edge, 2], , drop = FALSE])

    e.newv[edge, e.levels] <- Nv + loc.new.idx
    if (!is.boundary.edge) {
      e.newv[edge.reverse, e.levels] <- Nv + loc.new.idx
    } else { ## edge is a boundary edge, handle now
      ev <- x$ev[edge, ]
      the.levels <- which(e.newv[edge, ] > 0)
      the.loc.idx <- e.newv[edge, the.levels]
      the.levels <- c(min(the.levels) - 1L, the.levels) * 2L + 1L
      if (z[ev[1]] > z[ev[2]]) {
        the.loc.idx <- rev(the.loc.idx)
        the.levels <- rev(the.levels)
      }

      idx <- rbind(idx, cbind(
        c(ev[1], the.loc.idx),
        c(the.loc.idx, ev[2])
      ))
      grp <- c(grp, the.levels)
    }
    loc.last <- loc.last + length(e.levels)
  }
  loc <- rbind(loc, loc.new)
  Nv <- nrow(loc)

  ## For each triangle, find non-level edge crossings, and
  ##   store new vertex-edge crossing edges
  ##   store new edge-edge crossing edges
  tris <- unique(x$et[e.crossing])
  for (tri in tris) {
    ## connect vertex-edge
    v.lev <- vcross.lev[x$tv[tri, ]]
    for (vi in which(v.lev > 0)) {
      opposite.edge <- x$te[tri, vi]
      opposite.v <- e.newv[opposite.edge, v.lev[vi]]
      if (opposite.v > 0) {
        ## v2 on the right, v3 on the left
        v123 <- x$tv[tri, ((vi + (0:2) - 1) %% 3) + 1]
        if (z[v123[3]] > z[v123[1]]) {
          idx <- rbind(idx, cbind(v123[1], opposite.v))
        } else {
          idx <- rbind(idx, cbind(opposite.v, v123[1]))
        }
        grp <- c(grp, v.lev[vi] * 2L)
      }
    }
    ## connect edge-edge
    for (ei in 1:3) {
      edge <- x$te[tri, ei]
      next.edge <- x$te[tri, (ei %% 3L) + 1L]
      e.lev <- which(e.newv[edge, ] > 0)
      e.lev <- e.lev[e.newv[next.edge, e.lev] > 0]
      if (length(e.lev) > 0) {
        ## v1 on the left, v2 on the right
        v12 <- x$ev[edge, ]
        if (z[v12[1]] > z[v12[2]]) {
          idx <- rbind(idx, cbind(
            e.newv[edge, e.lev],
            e.newv[next.edge, e.lev]
          ))
        } else {
          idx <- rbind(idx, cbind(
            e.newv[next.edge, e.lev],
            e.newv[edge, e.lev]
          ))
        }
        grp <- c(grp, e.lev * 2L)
      }
    }
  }

  ## Filter out unused nodes
  reo <- sort(unique(as.vector(idx)))
  loc <- loc[reo, , drop = FALSE]
  ireo <- integer(Nv)
  ireo[reo] <- seq_along(reo)
  idx <- matrix(ireo[idx], nrow(idx), 2)

  list(loc = loc, idx = idx, grp = grp)
}






#' @rdname tricontour
#' @export
tricontourmap <- function(x, z, nlevels = 10,
                          levels = pretty(range(z, na.rm = TRUE), nlevels),
                          ...) {
  UseMethod("tricontourmap")
}

#' @rdname tricontour
#' @export
tricontourmap.inla.mesh <-
  function(x, z, nlevels = 10,
           levels = pretty(range(z, na.rm = TRUE), nlevels),
           ...) {
    tricontourmap.list(x$graph,
      z = z,
      nlevels = nlevels, levels = levels,
      loc = x$loc, ...
    )
  }

#' @rdname tricontour
#' @export
tricontourmap.matrix <-
  function(x, z, nlevels = 10,
           levels = pretty(range(z, na.rm = TRUE), nlevels),
           loc, ...) {
    tricontourmap.list(list(tv = x),
      z = z,
      nlevels = nlevels, levels = levels,
      loc = loc, ...
    )
  }



#' @rdname tricontour
#' @param output The format of the generated output.  Implemented options
#'   are \code{"sp"} (default) and \code{"fm"} (and deprecated \code{"inla.mesh.segment"}).
#' @importFrom fmesher fm_segm
#' @export
tricontourmap.list <-
  function(x, z, nlevels = 10,
           levels = pretty(range(z, na.rm = TRUE), nlevels),
           loc, type = c("+", "-"), tol = 1e-7,
           output = c("sp", "fm", "inla.mesh.segment"), ...) {
    type <- match.arg(type)
    output <- match.arg(output)
    if (output == "inla.mesh.segment") {
      output <- "fm"
    }
    nlevels <- length(levels)

    if (output == "sp") {
      if (!requireNamespace("sp", quietly = TRUE)) {
        stop("The 'sp' package is needed.")
      }
    }

    tric <- tricontour(
      x = x, z = z, nlevels = nlevels, levels = levels,
      loc = loc, type = type, tol = tol, ...
    )

    out <- list(map = list(), contour = list())
    for (k in seq_len(nlevels + 1L) * 2L - 1L) {
      ID <- as.character((k - 1) / 2)
      if (output == "sp") {
        spobj <- tryCatch(
          as.sp.outline(tric,
            grp.ccw = c(k - 1, k),
            grp.cw = c(k + 1),
            ccw = FALSE,
            closed = TRUE,
            ID = ID
          ),
          error = function(e) NULL
        )
        if (!is.null(spobj)) {
          out$map[[ID]] <- spobj
        }
      } else {
        out$map[[ID]] <-
          as.fm_segm.outline(tric,
            grp.ccw = c(k - 1, k),
            grp.cw = c(k + 1),
            grp = (k - 1) / 2
          )
      }
    }
    if (output == "sp") {
      out$map <- sp::SpatialPolygons(out$map)
    } else {
      out$map <- do.call(fm_segm, out$map)
    }
    for (k in seq_len(nlevels)) {
      if (output == "sp") {
        ID <- as.character(k)
        spobj <- tryCatch(
          as.sp.outline(tric,
            grp.ccw = k * 2L,
            ccw = FALSE,
            closed = FALSE,
            ID = ID
          ),
          error = function(e) NULL
        )
        if (!is.null(spobj)) {
          out$contour[[ID]] <- spobj
        }
      } else {
        out$contour[[as.character(k)]] <-
          as.fm_segm.outline(tric,
            grp.ccw = k * 2L,
            grp = k
          )
      }
    }
    if (output == "sp") {
      if (length(out$contour) > 0) {
        out$contour <- sp::SpatialLines(out$contour)
      } else {
        out$contour <- NULL
      }
    } else {
      out$contour <- do.call(fm_segm, out$contour)
    }

    out
  }


tricontour_step <- function(x, z, levels, loc, ...) {
  stopifnot(length(levels) == 1)

  x <- list(loc = loc, graph = x)
  outline.lower <- outline.on.mesh(z >= levels, x, TRUE)
  outline.upper <- outline.on.mesh(z >= levels, x, FALSE)

  list(
    loc = loc,
    idx = rbind(outline.lower$idx, outline.upper$idx),
    grp = c(outline.lower$grp, outline.upper$grp)
  )
}





## excursions --> E,F
## contourmap --> E,P0123,F
## simconf
## continterp(excurobj, grid or mesh, outputgrid(opt), alpha, method)
## gaussint


## Input: One of
##   inla.mesh
##   inla.mesh.lattice
##   list(loc, dims, ...)
##   list(x, y, ...)
## The last 3 are all treated as topological lattices, and code in
## submesh.grid() assumes that the lattice boxes are convex.
## Output:
##   list(loc, dims, geometry, manifold)
get.geometry <- function(geometry) {
  geometrytype <- ""
  manifoldtype <- ""
  if (inherits(geometry, c("fm_mesh_2d", "inla.mesh"))) {
    loc <- geometry$loc
    dims <- nrow(loc)
    geometrytype <- "mesh"
    manifoldtype <- geometry$manifold
  } else if (inherits(geometry, c("fm_lattice_2d", "inla.mesh.lattice")) ||
    is.list(geometry)) {
    if (("loc" %in% names(geometry)) && ("dims" %in% names(geometry))) {
      loc <- geometry$loc
      dims <- geometry$dims
      geometrytype <- "lattice"
      manifoldtype <- "R2"
    } else if ("x" %in% names(geometry)) {
      geometrytype <- "lattice"
      if ("y" %in% names(geometry)) {
        loc <- as.matrix(expand.grid(geometry$x, geometry$y))
        dims <- c(length(geometry$x), length(geometry$y))
        manifoldtype <- "R2"
      } else {
        loc <- geometry$x
        dims <- length(loc)
        manifoldtype <- "R1"
      }
    }
  }
  geometrytype <- match.arg(geometrytype, c("mesh", "lattice"))
  manifoldtype <- match.arg(manifoldtype, c("M", "R1", "R2", "S2"))
  list(loc = loc, dims = dims, geometry = geometrytype, manifold = manifoldtype)
}


## Input:
##   list(loc, graph=list(tv, ...)) or an inla.mesh
## Output:
##   list(loc, graph=list(tv), A, idx=list(loc))
subdivide.mesh <- function(mesh) {
  graph <- generate.trigraph.properties(mesh$graph, nrow(mesh$loc))

  v1 <- seq_len(graph$Nv)
  edges.boundary <- which(is.na(graph$ee))
  edges.interior <- which(!is.na(graph$ee))
  edges.interior.main <- edges.interior[graph$ev[edges.interior, 1] <
    graph$ev[edges.interior, 2]]
  edges.interior.secondary <- graph$ee[edges.interior.main]
  Neb <- length(edges.boundary)
  Nei <- length(edges.interior.main)
  Nv2 <- graph$Nv + Neb + Nei
  v2.boundary <- graph$Nv + seq_len(Neb)
  v2.interior <- graph$Nv + Neb + seq_len(Nei)
  edge.split.v <- integer(length(graph$ee))
  edge.split.v[edges.boundary] <- v2.boundary
  edge.split.v[edges.interior.main] <- v2.interior
  edge.split.v[edges.interior.secondary] <- v2.interior

  ## Mapping matrix from mesh nodes to sub-mesh nodes
  idx <- seq_len(graph$Nv) ## Same indices as in input mesh
  ridx <- seq_len(graph$Nv)
  ##  ridx[mesh$idx$loc[!is.na(mesh$idx$loc)]] <-
  ##    which(!is.na(mesh$idx$loc))
  A <- sparseMatrix(
    i = (c(
      v1,
      rep(v2.boundary, times = 2),
      rep(v2.interior, times = 2)
    )),
    j = (ridx[c(
      v1,
      as.vector(graph$ev[edges.boundary, ]),
      as.vector(graph$ev[edges.interior.main, ])
    )]),
    x = (c(
      rep(1, graph$Nv),
      rep(1 / 2, 2 * Neb),
      rep(1 / 2, 2 * Nei)
    )),
    dims = c(Nv2, length(ridx))
  )
  loc <- as.matrix(A[, ridx, drop = FALSE] %*% mesh$loc)
  tv <- rbind(
    cbind(
      edge.split.v[graph$te[, 1]],
      edge.split.v[graph$te[, 2]],
      edge.split.v[graph$te[, 3]]
    ),
    cbind(
      graph$tv[, 1],
      edge.split.v[graph$te[, 3]],
      edge.split.v[graph$te[, 2]]
    ),
    cbind(
      graph$tv[, 2],
      edge.split.v[graph$te[, 1]],
      edge.split.v[graph$te[, 3]]
    ),
    cbind(
      graph$tv[, 3],
      edge.split.v[graph$te[, 2]],
      edge.split.v[graph$te[, 1]]
    )
  )

  newmesh <- fmesher::fm_rcdt_2d_inla(loc = loc, tv = tv)
  ## Handle possible node reordering in inla.mesh.create()
  newmesh$idx$loc <- newmesh$idx$loc[idx]
  ## Add mapping matrix
  newmesh$A <- A

  newmesh
}







## New geometry implementation ####

# Copy 'G' and interpolate 'F' within coherent single level regions.
F.interpolation <- function(F.geometry, F, G, type, method, subdivisions = 1) {
  # Construct interpolation mesh
  F.geometry.A <- list()
  for (subdivision in seq_len(subdivisions)) {
    F.geometry <- subdivide.mesh(F.geometry)
    F.geometry.A <- c(F.geometry.A, list(F.geometry$A))
  }

  if (method == "log") {
    F.zero <- -Inf
    F <- log(F)
    F[is.infinite(F) & F < 0] <- F.zero
  } else if (method == "linear") {
    F.zero <- 0
  } else {
    ## 'step'
    F.zero <- 0
  }

  ## For ordinary excursions, the input set/group information is not used.
  if (type == ">") {
    G <- rep(1, length(G))
  } else if (type == "<") {
    G <- rep(0, length(G))
  }
  ## Copy 'G' and interpolate 'F' within coherent single level regions.
  G.interp <- G
  F.interp <- F
  for (subdivision in seq_len(subdivisions)) {
    G.input <- G.interp
    F.input <- F.interp
    G.interp <- rep(-1, nrow(F.geometry.A[[subdivision]]))
    F.interp <- rep(F.zero, nrow(F.geometry.A[[subdivision]]))
    for (k in unique(G[G >= 0])) {
      ok.in <- (G.input == k)
      ok.out <- (rowSums(F.geometry.A[[subdivision]][, ok.in, drop = FALSE]) >
        1 - 1e-12)
      G.interp[ok.out] <- k
    }
    ok.in <- (G.input >= 0)
    ok.out <- (G.interp >= 0)
    if (method == "step") {
      for (vtx in which(ok.out)) {
        F.interp[vtx] <- min(F.input[F.geometry.A[[subdivision]][vtx, ] > 0])
      }
    } else {
      F.interp[ok.out] <-
        as.vector(F.geometry.A[[subdivision]][ok.out, ok.in, drop = FALSE] %*%
          F.input[ok.in])
    }
  }

  if (method == "log") {
    F <- exp(F.interp)
  } else if (method == "linear") {
    F <- F.interp
  } else {
    ## 'step'
    F <- F.interp
  }
  F[G.interp == -1] <- 0
  G <- G.interp

  F.geometry <-
    fmesher::fm_rcdt_2d_inla(
      loc = F.geometry$loc,
      tv = F.geometry$graph$tv
    )
  ## Handle possible node reordering in inla.mesh.create()
  F[F.geometry$idx$loc] <- F
  G[F.geometry$idx$loc] <- G

  list(F = F, G = G, F.geometry = F.geometry)
}








## In-filled points at transitions should have G[i] == -1
## to get a conservative approximation.
## To get only an "over/under set", use a constant non-negative integer G
##   and let calc.complement=FALSE
probabilitymap <-
  function(mesh, F, level, G,
           calc.complement = TRUE,
           tol = 1e-7,
           output = c("sp", "fm", "inla.mesh.segment"),
           method, ...) {
    output <- match.arg(output)
    if (output == "inla.mesh.segment") {
      output <- "fm"
    }

    if (output == "sp") {
      if (!requireNamespace("sp", quietly = TRUE)) {
        stop("The 'sp' package is needed.")
      }
    }

    if (calc.complement &&
      (output == "fm")) {
      stop("Output format '", output, "' not supported for 'calc.complement = TRUE'.")
    }

    spout <- list()
    inlaout <- list()

    ## Find individual avoidance/between-level/under/over sets.
    for (k in sort(unique(G[G >= 0]))) {
      active.triangles <-
        which((rowSums(matrix(
          G[mesh$graph$tv] == k,
          nrow(mesh$graph$tv), 3
        )) == 3) &
          (rowSums(matrix(
            is.finite(F[mesh$graph$tv]),
            nrow(mesh$graph$tv), 3
          )) == 3))

      if (length(active.triangles) > 0) {
        submesh <- submesh.mesh.tri(active.triangles, mesh)
        active.nodes.idx <- which(!is.na(submesh$idx$loc))
        subF <- rep(NA, length(active.nodes.idx))
        subF[submesh$idx$loc[active.nodes.idx]] <- F[active.nodes.idx]

        if (nrow(submesh$loc) > 0) {
          ##      submesh$n <- nrow(submesh$loc)
          ##      submesh$graph <-
          ##        generate.trigraph.properties(
          ##          list(tv=submesh$graph$tv),
          ##          Nv=nrow(submesh$loc))

          #      if (FALSE) { ## Debugging plots
          #        op <- par(mfrow=c(2,1))
          #        on.exit(par(op))

          #        class(mesh) <- "inla.mesh"
          #        mesh$n <- nrow(mesh$loc)
          #        mesh$manifold <- "R2"
          #        proj <- inla.mesh.projector(mesh)
          #        image(proj$x, proj$y, inla.mesh.project(proj, field=exp(F)),
          #              zlim=range(exp(F)))
          #        if (length(spout) > 0) {
          #          plot(sp::SpatialPolygons(spout), add=TRUE, col="blue")
          #        }
          #        proj <- inla.mesh.projector(submesh)
          #        image.plot(proj$x, proj$y, inla.mesh.project(proj, field=exp(subF)),
          #                   xlim=range(mesh$loc[,1]), ylim=range(mesh$loc[,1]),
          #                   zlim=range(exp(F)))
          #        plot(submesh, add=TRUE)
          #      }

          subF[is.na(subF)] <- 0

          if (method == "step") {
            tric <- tricontour_step(
              x = submesh$graph, z = subF, levels = level,
              loc = submesh$loc
            )
          } else {
            tric <- tricontour(
              x = submesh$graph, z = subF, levels = level,
              loc = submesh$loc, type = "+", tol = tol, ...
            )
          }
          ID <- as.character(k)

          if (output == "sp" || calc.complement) {
            spobj <- tryCatch(
              as.sp.outline(tric,
                grp.ccw = c(2, 3),
                grp.cw = c(),
                ccw = FALSE,
                closed = TRUE,
                ID = ID
              ),
              error = function(e) NULL
            )
            if (!is.null(spobj)) {
              if (spobj@area == 0) {
                warning("Skipping zero area polygon in probabilitymap.")
              } else {
                spout[[ID]] <- spobj
              }
            }
          }
          if (output == "fm") {
            inlaout[[ID]] <-
              as.fm_segm.outline(tric,
                grp.ccw = c(2, 3),
                grp.cw = c(),
                grp = k
              )
          }
        }
      }
    }

    if (calc.complement) {
      ## Find contour set
      if (!requireNamespace("sf", quietly = TRUE)) {
        stop("Package 'sf' required for set complement calculations.")
      }

      ID <- "-1"
      outline <- fmesher::fm_segm(mesh, boundary = TRUE)
      sp.domain <- as.sp.outline(outline,
        grp.ccw = unique(outline$grp),
        grp.cw = integer(0),
        ID = ID,
        closed = TRUE
      )
      sp.domain <- sp::SpatialPolygons(list(sp.domain))

      if (length(spout) == 0) {
        ## Complement is the entire domain
        spout[[ID]] <- sp.domain@polygons[[1]]
      } else {
        spout.joined <- sp::SpatialPolygons(spout)
        spout.union <- sf::st_union(sf::st_as_sfc(spout.joined))
        sp.diff <- sf::st_difference(sf::st_as_sfc(sp.domain), spout.union)
        sp.diff <- sf::as_Spatial(sp.diff)
        if (!is.null(sp.diff)) {
          spout[[ID]] <- sp.diff@polygons[[1]]
        }
      }
      if (!is.null(spout[[ID]])) {
        spout[[ID]]@ID <- ID

        if (output == "fm") {
          inlaout[[ID]] <- fmesher::fm_as_segm(spout[[ID]])
        }
      }
    }

    if ((output == "sp") && (length(spout) > 0)) {
      out <- sp::SpatialPolygons(spout)
    } else if ((output == "fm") && (length(inlaout) > 0)) {
      out <- do.call(fm_segm, do.call(c, inlaout))
    } else {
      out <- NULL
    }

    out
  }




gaussquad <- function(mesh, method = c("direct", "make.A")) {
  method <- match.arg(method)
  if (mesh$manifold == "M" && method == "make.A") {
    stop("Guassian quadrature method 'make.A' not supported for 'M' manifolds.")
  }

  ## Construct cubic Gauss-quadrature points and weights
  nT <- nrow(mesh$graph$tv)
  I.w <- fmesher::fm_fem(mesh, order = 0)$ta
  if (method == "make.A") {
    ## Old method, kept for sanity checking.
    I.loc <-
      rbind(
        (mesh$loc[mesh$graph$tv[, 1], , drop = FALSE] +
          mesh$loc[mesh$graph$tv[, 2], , drop = FALSE] +
          mesh$loc[mesh$graph$tv[, 3], , drop = FALSE]) / 3,
        (mesh$loc[mesh$graph$tv[, 1], , drop = FALSE] * 3 +
          mesh$loc[mesh$graph$tv[, 2], , drop = FALSE] +
          mesh$loc[mesh$graph$tv[, 3], , drop = FALSE]) / 5,
        (mesh$loc[mesh$graph$tv[, 1], , drop = FALSE] +
          mesh$loc[mesh$graph$tv[, 2], , drop = FALSE] * 3 +
          mesh$loc[mesh$graph$tv[, 3], , drop = FALSE]) / 5,
        (mesh$loc[mesh$graph$tv[, 1], , drop = FALSE] +
          mesh$loc[mesh$graph$tv[, 2], , drop = FALSE] +
          mesh$loc[mesh$graph$tv[, 3], , drop = FALSE] * 3) / 5
      )
    if (mesh$manifold == "S2") {
      I.loc <- I.loc / sqrt(rowSums(I.loc^2))
    }
    A <- fmesher::fm_basis(mesh, I.loc)
  } else {
    A <- sparseMatrix(
      i = (rep(seq_len(nT), times = 3 * 4) +
        rep(c(0, 1, 2, 3) * nT, each = nT * 3)),
      j = rep(
        c(
          mesh$graph$tv[, 1],
          mesh$graph$tv[, 2],
          mesh$graph$tv[, 3]
        ),
        times = 4
      ),
      x = rep(
        c(
          c(1, 1, 1) / 3,
          c(3, 1, 1) / 5,
          c(1, 3, 1) / 5,
          c(1, 1, 3) / 5
        ),
        each = nT
      ),
      dims = c(nT * 4, mesh$n)
    )
  }

  I.w <- (rep(I.w, times = 4) *
    rep(c(-27, 25, 25, 25) / 48, each = nT))

  list(A = A, w = I.w)
}


calc.continuous.P0 <- function(F, G, F.geometry, method) {
  tri <-
    which((G[F.geometry$graph$tv[, 1]] >= 0) &
      (G[F.geometry$graph$tv[, 1]] == G[F.geometry$graph$tv[, 2]]) &
      (G[F.geometry$graph$tv[, 1]] == G[F.geometry$graph$tv[, 3]]) &
      (rowSums(matrix(
        is.finite(F[F.geometry$graph$tv]),
        nrow(F.geometry$graph$tv), 3
      )) == 3))
  submesh <- submesh.mesh.tri(tri, F.geometry)
  active.nodes.idx <- which(!is.na(submesh$idx$loc))
  subF <- rep(NA, length(active.nodes.idx))
  subF[submesh$idx$loc[active.nodes.idx]] <- F[active.nodes.idx]

  tot.area <- sum(fmesher::fm_fem(F.geometry, order = 0)$ta)

  if (method == "linear") {
    I.w <- fmesher::fm_fem(submesh, order = 0)$va
    P0 <- sum(I.w * subF) / tot.area
  } else if (method == "log") {
    II <- gaussquad(submesh)
    tmp <- exp(as.vector(II$A %*% log(subF)))
    P0 <- sum(II$w * tmp) / tot.area
  } else {
    ## "step"
    I.w <- fmesher::fm_fem(submesh, order = 0)$ta
    tmp <- matrix(
      subF[submesh$graph$tv],
      nrow(submesh$graph$tv),
      ncol(submesh$graph$tv)
    )
    tmp <- apply(tmp, 1, min)
    P0 <- sum(I.w * tmp) / tot.area
  }
  P0
}


#' Calculate continuous domain excursion and credible contour sets
#'
#' Calculates continuous domain excursion and credible contour sets
#'
#' @param ex An \code{excurobj} object generated by a call to \code{\link{excursions}}
#' or \code{\link{contourmap}}.
#' @param geometry Specification of the lattice or triangulation geometry of the input.
#' One of \code{list(x, y)}, \code{list(loc, dims)}, \code{fm_lattice_2d},
#' \code{inla.mesh.lattice}, \code{fm_mesh_2d}, or
#' \code{inla.mesh}, where \code{x} and \code{y} are vectors, \code{loc} is
#' a two-column matrix of coordinates, and \code{dims} is the lattice size vector.
#' The first three versions are all treated topologically as lattices, and the
#' lattice boxes are assumed convex.
#' @param alpha The target error probability.  A warning is given if it is detected
#' that the information \code{ex} isn't sufficient for the given \code{alpha}.
#' Defaults to the value used when calculating \code{ex}.
#' @param method The spatial probability interpolation transformation method to use.
#' One of \code{log}, \code{linear}, or \code{step}.  For \code{log}, the probabilities
#' are interpolated linearly in the transformed scale. For \code{step}, a conservative
#' step function is used.
#' @param output Specifies what type of object should be generated. \code{sp} gives a
#' \code{SpatialPolygons} object, and \code{fm} or \code{inla} gives a \code{fm_segm} object.
#' @param subdivisions The number of mesh triangle subdivisions to perform for the
#' interpolation of the excursions or contour function. 0 is no subdivision.
#' The setting has a small effect on the evaluation of \code{P0} for the \code{log}
#' method (higher values giving higher accuracy) but the main effect is on the visual
#' appearance of the interpolation. Default=1.
#' @param calc.credible Logical, if TRUE (default), calculate credible contour region
#' objects in addition to avoidance sets.
#'
#' @return A list with elements
#' \item{M}{\code{SpatialPolygons} or \code{inla.mesh.segment} object. The subsets
#' are tagged, so that credible regions are tagged \code{"-1"}, and regions between
#' levels are tagged \code{as.character(0:nlevels)}.}
#' \item{F}{Interpolated F function.}
#' \item{G}{Contour and inter-level set indices for the interpolation.}
#' \item{F.geometry}{Mesh geometry for the interpolation.}
#' \item{P0}{P0 measure based on interpolated F function (only for \code{contourmap}
#' input).}
#' @export
#' @author Finn Lindgren \email{finn.lindgren@gmail.com}
#' @references Bolin, D. and Lindgren, F. (2017) \emph{Quantifying the uncertainty of contour maps}, Journal of Computational and Graphical Statistics, vol 26, no 3, pp 513-524.
#'
#' Bolin, D. and Lindgren, F. (2018), \emph{Calculating Probabilistic Excursion Sets and Related Quantities Using excursions}, Journal of Statistical Software, vol 86, no 1, pp 1-20.
#'
#' @examples
#' \dontrun{
#' if (require("fmesher")) {
#'   # Generate mesh and SPDE model
#'   n.lattice <- 10 # Increase for more interesting, but slower, examples
#'   x <- seq(from = 0, to = 10, length.out = n.lattice)
#'   lattice <- fm_lattice_2d(x = x, y = x)
#'   mesh <- fm_rcdt_2d_inla(lattice = lattice, extend = FALSE, refine = FALSE)
#'
#'   # Generate an artificial sample
#'   sigma2.e <- 0.1
#'   n.obs <- 100
#'   obs.loc <- cbind(
#'     runif(n.obs) * diff(range(x)) + min(x),
#'     runif(n.obs) * diff(range(x)) + min(x)
#'   )
#'   Q <- fm_matern_precision(mesh, alpha = 2, rho = 3, sigma = 1)
#'   x <- fm_sample(n = 1, Q = Q)
#'   A <- fm_basis(mesh, loc = obs.loc)
#'   Y <- as.vector(A %*% x + rnorm(n.obs) * sqrt(sigma2.e))
#'
#'   ## Calculate posterior
#'   Q.post <- (Q + (t(A) %*% A) / sigma2.e)
#'   mu.post <- as.vector(solve(Q.post, (t(A) %*% Y) / sigma2.e))
#'   vars.post <- excursions.variances(chol(Q.post))
#'
#'   ## Calculate contour map with two levels
#'   map <- contourmap(
#'     n.levels = 2, mu = mu.post, Q = Q.post,
#'     alpha = 0.1, F.limit = 0.1, max.threads = 1
#'   )
#'
#'   ## Calculate the continuous representation
#'   sets <- continuous(map, mesh, alpha = 0.1)
#'
#'   ## Plot the results
#'   reo <- mesh$idx$lattice
#'   cols <- contourmap.colors(map,
#'     col = heat.colors(100, 1, rev = TRUE),
#'     credible.col = grey(0.5, 1)
#'   )
#'   names(cols) <- as.character(-1:2)
#'
#'   par(mfrow = c(2, 2))
#'   image(matrix(mu.post[reo], n.lattice, n.lattice),
#'     main = "mean", axes = FALSE, asp = 1
#'   )
#'   image(matrix(sqrt(vars.post[reo]), n.lattice, n.lattice),
#'     main = "sd", axes = FALSE, asp = 1
#'   )
#'   image(matrix(map$M[reo], n.lattice, n.lattice),
#'     col = cols, axes = FALSE, asp = 1
#'   )
#'   idx.M <- setdiff(names(sets$M), "-1")
#'   plot(sets$M[idx.M], col = cols[idx.M])
#' }
#' }
#'
continuous <- function(ex,
                       geometry,
                       alpha,
                       method = c("log", "linear", "step"),
                       output = c("sp", "fm", "inla"),
                       subdivisions = 1,
                       calc.credible = TRUE) {
  stopifnot(inherits(ex, "excurobj"))
  method <- match.arg(method)
  output <- match.arg(output)
  if (output == "inla") {
    output <- "fm"
  }

  if (!(ex$meta$calculation %in% c(
    "excursions",
    "contourmap"
  ))) {
    stop(paste("Unsupported calculation '",
      ex$meta$calculation, "'.",
      sep = ""
    ))
  }

  if (missing(alpha)) {
    alpha <- ex$meta$alpha
  }
  if (alpha > ex$meta$F.limit) {
    warning(paste("Insufficient data: alpha = ", alpha,
      " > F.limit = ", ex$meta$F.limit,
      sep = ""
    ))
  }

  info <- get.geometry(geometry)
  if (!fmesher::fm_manifold(info, c("M", "R2", "S2"))) {
    stop(paste("Unsupported manifold type '", fmesher::fm_manifold(info), "'.", sep = ""))
  }
  if ((output == "sp") && !fmesher::fm_manifold(info, c("R2"))) {
    stop(paste("Unsupported manifold type '", fmesher::fm_manifold(info), "' for 'sp' output.", sep = ""))
  }
  if (calc.credible &&
    (output == "fm")) {
    stop("Output format 'fm' not supported for 'calc.credible = TRUE'.")
  }

  if (length(ex$F) != prod(info$dims)) {
    stop(paste("The number of computed F-values (", length(ex$F), ") must match \n",
      "the number of elements of the continuous geometry definition (",
      prod(info$dims), ").",
      sep = ""
    ))
  }

  if (ex$meta$type == "=") {
    type <- "!="
    F.ex <- 1 - ex$F
  } else {
    type <- ex$meta$type
    F.ex <- ex$F
  }
  F.ex[is.na(F.ex)] <- 0

  if (is.null(ex$meta$ind)) {
    active.nodes <- rep(TRUE, length(ex$F))
  } else {
    active.nodes <- logical(length(ex$F))
    active.nodes[ex$meta$ind] <- TRUE
  }
  if (info$geometry == "mesh") {
    mesh <- submesh.mesh(active.nodes, geometry)
  } else if (info$geometry == "lattice") {
    mesh <- submesh.grid(active.nodes, geometry)
  }
  mesh$graph <-
    generate.trigraph.properties(mesh$graph, Nv = nrow(mesh$loc))

  active.nodes <- !is.na(mesh$idx$loc)
  F.ex[mesh$idx$loc[active.nodes]] <- F.ex[active.nodes]
  G.ex <- rep(-1, nrow(mesh$loc))
  G.ex[mesh$idx$loc[active.nodes]] <- ex$G[active.nodes]

  ## Construct interpolation
  interpolated <-
    F.interpolation(mesh, F.ex, G.ex, ex$meta$type, method,
      subdivisions = subdivisions
    )

  if (ex$meta$type == "=") {
    interpolated$F <- 1 - interpolated$F
    ##    F.ex <- 1-F.ex   ????????????????????
  }

  F.geometry <- mesh
  if (method == "log") {
    F.ex <- log(F.ex)
    level <- log(1 - alpha)
    F.ex[!is.finite(F.ex)] <- NA
  } else if (method == "linear") {
    level <- 1 - alpha
  } else {
    ## 'step'
    level <- 1 - alpha
  }

  ## For ordinary excursions, the input set/group information is not used.
  if (type == ">") {
    G.ex <- rep(1, length(G.ex))
  } else if (type == "<") {
    G.ex <- rep(0, length(G.ex))
  }

  M <- probabilitymap(F.geometry,
    F = F.ex,
    level = level,
    G = G.ex,
    calc.complement = calc.credible,
    method = method,
    output = output
  )

  if (method == "log") {
    F.ex <- exp(F.ex)
  }

  F.ex[is.na(F.ex)] <- 0
  interpolated$F[is.na(interpolated$F)] <- 0
  out <- list(
    F = interpolated$F, G = interpolated$G,
    M = M, F.geometry = interpolated$F.geometry
  )

  if (!is.null(ex$P0)) {
    out$P0 <- calc.continuous.P0(
      interpolated$F, interpolated$G,
      interpolated$F.geometry, method
    )
  }

  out
}

Try the excursions package in your browser

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

excursions documentation built on Oct. 23, 2023, 5:07 p.m.