R/Layout.R

#########################################################################/**
# @RdocClass Layout
#
# @title "The Layout class"
#
# \description{
#  @classhierarchy
#
#  The Layout class describes the layout of a microarray slide, 
#  such as the number of spots and the layout of the grids etc.
#  A microarray slide is layout in a number of grids, where each grid
#  is refered to by its row and its column. Within each grid, each spot
#  is refered to by its row and its column \emph{within that grid}. For
#  example, a microarray slide with 4*3 grids and where each grid has 
#  12*10 spots, has in total 4*3*12*10 = 12*120 = 1440 spots. The spot
#  in the top left corner is located at grid (1,1) and spot (1,1) and
#  we say it has the \emph{location} (1,1,1,1). In the above example, 
#  the spot in the lower right corner of the slide has the location 
#  (4,3,12,10). Further more, each spot on a microarray slide has a
#  unique \emph{index}. This index starts counting starts with grid 1,
#  moves along row 1 from column 1 until
#  the last column, then advances to the next row. After all rows in
#  grid 1 are indexed, counting proceeds to grid 2 and so on. This way of
#  indexing the spots is used by for instance GenePix, Spot and ScanAlyze.
#  Continuing the example above, the spot at location (1,1,1,1) has index
#  1, the spot at location (4,3,12,10) has index 1440. The spot at location
#  (2,2,11,3) has index ((2-1)*3+(2-1))*12*10+(11-1)*10+3 = 483. 
#  To get the \emph{index} from a \emph{location} one do
#  \code{getIndex(layout, 2,2,11,3)} where 
#  \code{layout <- Layout(4,3,12,10)}. To get the \emph{location} from a
#  \emph{index} one do \code{getLocation(layout, 483)}, which
#  gives the vector (2, 2, 11, 3). To get the \emph{position}, i.e. the
#  overall row and column of a spot on the microarray slide one can do
#  \code{getPosition(483)} which give the position (23,13).\cr
#
#  All spots are refered to by their unique \emph{indices}.
# }
#
# @synopsis
#
# \arguments{
#   \item{nspot.r}{The number of rows of spots per grid.
#         This first argument can also be a list containing
#         the fields nspot.r, nspot.c, ngrid.r, and ngrid.c.}
#   \item{nspot.c}{The number of columns of spots per grid.}
#   \item{ngrid.r}{The number of rows of grids per slide.}
#   \item{ngrid.c}{The number of columns of grids per slide.}
#   \item{name}{A @vector of names for the spot. \bold{optional}.}
#   \item{id}{A @vector of ids for the spot. \bold{optional}.}
#   \item{printorder}{A @matrix or a @character string specifying the
#         order that spots where printed. For more details 
#         @seemethod "setPrintorder".}
#   \item{geneSpotMap}{.}
#   \item{plate}{.}
# }
#
# \section{Fields and Methods}{
#  \bold{Fields}
#  \tabular{rll}{
#   \tab \code{ngrid.r} \tab The number of rows of grids per slide. \cr
#   \tab \code{ngrid.c} \tab The number of columns of grids per slide. \cr
#   \tab \code{nspot.r} \tab The number of rows of spots in each grid. \cr
#   \tab \code{nspot.c} \tab The number of columns of spots in each grid. \cr
#   \tab \code{name}    \tab A @vector of strings which specified the name of each gene. \cr
#   \tab \code{id}      \tab A @vector of strings which specified the id of each gene. \cr
#  }
#
#  @allmethods "public"
# }
#
# \note{
#  There are several functions that returns a Layout object.
#  It is only in very special cases that you have to create one yourself.
#
#  In the sma package some functions are related to this class. This,
#  class might be backward compatible with these functions, but the reverse
#  is not true. The following functions are known be related to this class:
#  @see "sma::init.grid", @see "sma::id2image" (and \code{image2id}).
# }
#
# \details{
#   GenBank Accession numbers, SwissProt/TrEMBL Accession numbers or 
#   Entry Names.
# }
#
# @author
#
# \examples{
#   layout <- Layout(ngrid.r=4, ngrid.c=4, nspot.r=18, nspot.c=18)
#
#   SMA$loadData("mouse.setup")
#   layout <- Layout(mouse.setup)
#   # or, equivalent...
#   layout <- as.Layout(mouse.setup)
# }
#
# \seealso{
#  @see "MicroarrayData.getLayout".
# }
#*/#########################################################################
setConstructorS3("Layout", function(ngrid.r=0, ngrid.c=0, nspot.r=0, nspot.c=0, geneSpotMap=NULL, name=NULL, id=NULL, plate=NULL, printorder=NULL) {
  # This is to support old sma style too.
  if (is.list(ngrid.r)) {
    if ( !any(is.na(match(c("ngrid.r", "ngrid.c", "nspot.r", "nspot.c"), 
                                                       names(ngrid.r)))) ) {
      l <- ngrid.r;
      nspot.r <- l$nspot.r;
      nspot.c <- l$nspot.c; 
      ngrid.r <- l$ngrid.r; 
      ngrid.c <- l$ngrid.c;
    }
  }

  this <- extend(Object(), "Layout", 
    nspot.r      = nspot.r, 
    nspot.c      = nspot.c, 
    ngrid.r      = ngrid.r, 
    ngrid.c      = ngrid.c,
    id           = id,
    .name        = name,
    plate        = plate,
    .printorder  = NULL,
    geneSpotMap  = geneSpotMap,
    replicates   = NULL,
    plateGrps    = NULL,
    printdipGrps = NULL,
    printtipGrps = NULL,
    geneGrps     = NULL
  );

  if (inherits(ngrid.r, "Layout"))
    set(this, ngrid.r);
  if (!is.null(printorder))
    setPrintorder(this, printorder);

  this;
});


setMethodS3("as.character", "Layout", function(x, ...) {
  # To please R CMD check
  this <- x;

  s <- paste(sep="", data.class(this), ": ",
    "Grids: ", this$ngrid.r, "x", this$ngrid.c, " (=", 
                                          nbrOfGrids(this), "), ",
    "spots in grids: ", this$nspot.r, "x", this$nspot.c, " (=", 
                                          gridSize(this), "), ",
    "total number of spots: ", nbrOfSpots(this), "."
  );
  if (hasNames(this))
    s <- paste(sep="", s, " Spot names are specified.")
  if (hasIds(this))
    s <- paste(sep="", s, " Spot ids are specified.")
  if (!is.null(this$geneGrps))
    s <- paste(sep="", s, " ", this$geneGrps)
  if (hasPlates(this))
    s <- paste(sep="", s, " Spot plates are specified.")

  s;
})



#########################################################################/**
# @RdocMethod getId
#
# @title "Gets the id of one or more spots"
#
# @synopsis
#
# \arguments{
#   \item{index}{A @vector of indices indicating which ids to set. If 
#    @NULL, all ids are set.}
# }
#
# \value{
#   Returns the @vector of ids.
# }
#
# \description{
#  @get "title" given their indices.
# }
#
# @author
#
# \examples{
#   SMA$loadData(c("mouse.setup", "mouse.gnames"))
#   layout <- as.Layout(mouse.setup, id=mouse.gnames)
#
#   # Get the id of spot # 2453 and 2412:2417.
#   getId(layout, c(2453, 2412:2417))
# }
#
# \seealso{
#   @seemethod "indexOf",
#   @seemethod "setId".
#   @seeclass
# }
#*/#########################################################################
setMethodS3("getId", "Layout", function(this, index=NULL, ...) {
  if (is.null(index))
    as.character(this$id)
  else {
    n <- nbrOfSpots(this);   # Modulo n just in case
    index <- ((index-1) %% n)+1;
    as.character(this$id[index]);
  }
})


setMethodS3("hasIds", "Layout", function(this, ...) {
  (length(this$id) > 0)
})


setMethodS3("hasNames", "Layout", function(this, ...) {
  (length(this$.name) > 0)
})

setMethodS3("hasPlates", "Layout", function(this, ...) {
  (length(this$plate) > 0)
})


#########################################################################/**
# @RdocMethod setId
#
# @title "Sets the id of one or more spots"
#
# @synopsis
#
# \arguments{
#   \item{id}{A @vector of ids.}
#   \item{index}{A @vector of indices indicating which ids to set. If 
#    @NULL, all ids are set.}
# }
#
# \description{
#  @get "title" given their indices.
# }
#
# @author
#
# \examples{
#   SMA$loadData(c("mouse.setup", "mouse.gnames"))
#   layout <- as.Layout(mouse.setup, id=mouse.gnames)
#
#   setId(layout, c("2412r", "2414r"), c(2412, 2414))
#
#   # Get the id of spot # 2453
#   getId(layout, c(2453, 2412:2417))
# }
#
# \seealso{
#   @seemethod "getId".
#   @seeclass
# }
#*/#########################################################################
setMethodS3("setId", "Layout", function(this, id, index=NULL, ...) {
  if (is.null(index))
    this$id <- as.character(id)
  else {
    n <- nbrOfSpots(this);   # Modulo n just in case
    index <- ((index-1) %% n)+1;
    this$id[index] <- as.character(id);
  }
})




#########################################################################/**
# @RdocMethod getName
#
# @title "Gets the name of one or more spots"
#
# @synopsis
#
# \arguments{
#   \item{index}{A @vector of indices indicating which names to set. If 
#    @NULL, all names are set.}
# }
#
# \value{
#   Returns the @vector of names.
# }
#
# \description{
#  @get "title" given their indices.
# }
#
# @author
#
# \examples{
#   SMA$loadData(c("mouse.setup", "mouse.gnames"))
#   layout <- as.Layout(mouse.setup, name=mouse.gnames)
#
#   # Get the name of spot # 2453 and 2412:2417.
#   getName(layout, c(2453, 2412:2417))
# }
#
# \seealso{
#   @seemethod "indexOf",
#   @seemethod "setName".
#   @seeclass
# }
#*/#########################################################################
setMethodS3("getName", "Layout", function(this, index=NULL, ...) {
  if (is.null(index))
    as.character(this$.name)
  else {
    n <- nbrOfSpots(this);   # Modulo n just in case
    index <- ((index-1) %% n)+1;
    as.character(this$.name[index]);
  }
})


#########################################################################/**
# @RdocMethod setName
#
# @title "Sets the name of one or more spots"
#
# @synopsis
#
# \arguments{
#   \item{name}{A @vector of names.}
#   \item{index}{A @vector of indices indicating which names to set. If 
#    @NULL, all names are set.}
# }
#
# \description{
#  @get "title" given their indices.
# }
#
# @author
#
# \examples{
#   SMA$loadData(c("mouse.setup", "mouse.gnames"))
#   layout <- as.Layout(mouse.setup, name=mouse.gnames)
#
#   setName(layout, c("2412r", "2414r"), c(2412, 2414))
#
#   # Get the name of spot # 2453
#   getName(layout, c(2453, 2412:2417))
# }
#
# \seealso{
#   @seemethod "getName".
#   @seeclass
# }
#*/#########################################################################
setMethodS3("setName", "Layout", function(this, name, index=NULL, ...) {
  if (is.null(index))
    this$.name <- as.character(name)
  else {
    n <- nbrOfSpots(this);   # Modulo n just in case
    index <- ((index-1) %% n)+1;
    this$.name[index] <- as.character(name);
  }
})


setMethodS3("rename", "Layout", function(this, from, to, what="name", ...) {
  from <- as.character(from);
  to   <- as.character(to);
  value <- this[[what]];
  if (is.null(value))
    throw("Unknown value of argument 'what': ", what);
  this[[what]] <- gsub(from, to, value);
}) # rename()



setMethodS3("getPlate", "Layout", function(this, index=NULL, ...) {
  if (is.null(index))
    as.character(this$plate)
  else {
    n <- nbrOfSpots(this);   # Modulo n just in case
    index <- ((index-1) %% n)+1;
    as.character(this$plate[index]);
  }
})


setMethodS3("getPlateNumber", "Layout", function(this, index=NULL, ...) {
  plate <- getPlate(this, index=index);
  plates <- unique(plate);
  match(plate, plates);
})


setMethodS3("nbrOfPlates", "Layout", function(this, ...) {
  plate <- getPlate(this);
  if (is.null(plate)) return(NULL);
  length(unique(plate));
})


setMethodS3("setPlate", "Layout", function(this, plate, index=NULL, ...) {
  if (is.null(index))
    this$plate <- as.character(plate)
  else {
    n <- nbrOfSpots(this);   # Modulo n just in case
    index <- ((index-1) %% n)+1;
    this$plate[index] <- as.character(plate);
  }
})


#########################################################################/**
# @RdocMethod getPrintorder
#
# @title "Gets the order of the spots in which they were printed"
#
# @synopsis
#
# \arguments{
#   \item{value}{The elements to be placed in the resulting @matrix.}
# }
#
# \description{
#   @get "title". The spots in column one were printed first, then the 
#   spots in column two and so on. 
#   By default the spot indices are returned.
# }
#
# \value{
#   Returns a @matrix with spot (values) printed at the same time in the
#   same column. The first spots printed are in column one and the last 
#   ones printed in the last column. 
#   Often this means that there are \code{nbrOfGrids(layout)} rows and
#   \code{gridSize(layout)} columns in the matrix. However, if the 
#   slide was printed in say two halfs (first half of the grids are
#   printed and then the second), then this is not true.
# }
#
# \section{Print order}{
#   The printing of a microarray is time consuming and often several
#   microarray slides are printed at the same time, since it is even more
#   time consuming to switch between the trays. When printing several
#   microarrays at the same time, the arrayer prints the first spot in
#   all grids on \emph{all} slides, before moving on to the second spot.
#   For a example, printing a batch of 100 slides with 6384 spots in 4x4
#   grids takes about 15 hours to print including manual work to switch
#   trays etc. Each grid contains 19*21 spots, i.e. the arrayer has to put
#   down the print tips 399 times on each slide, and in total 39900 times.
#   This is about 44 put-downs a minute. It takes about 45-50 minutes
#   to finish one row of spots.
# }
#
# \section{Different directions}{
#   The most common print-order directions are \code{"row-by-row"} and
#   \code{"column-by-column"}. 
#   In both cases, when printing a slide at each print step
#   \code{nbrOfGrids(layout)} spots are printed at the same time. 
#   The arrayer start of spotting the first spot in \emph{each} of the
#   grids. Then it cleans the print-tip heads, dries them, and go back
#   to the trays to get a \emph{new} set of cDNA and prints the second
#   spot in each of the grids. The second spot is to the right to
#   (\code{"row-by-row"}) or below (\code{"column-by-column"}) the
#   first spot. When the array gets to the end of a row (column) it
#   moves on to print the next row (column) and so on until all in
#   all grids have been printed.
# }
#
# \section{Print-order effects}{
#   An important factor for the quality of the printed spots is the
#   temperature and the humidity. Too high temperature and humidity
#   tends to produce too large spots that can even overlap [1]. If there
#   is no automatic control for temperature and humidity, the quality of
#   the spots could vary a lot between the spots printed during a 15 hours
#   printing process. With a varying printing climate we should expect to
#   see a variating of the quality of the spots along the order of which
#   the spots are printed.
#   The variation of temperature and humidity varies approximately in the
#   time scale of hours. As it takes about 45-50 minutes to print a row
#   of spots, we should therefore expect to see such a variation between
#   the rows in the grids.
# }
#
# \references{
#   [1] Microarrays in Three Easy Steps, Priti Hedge, The Institute for
#       Genomic Research, 200?.
# }
#
# \examples{
#   layout <- Layout(2,2, 3,3)
#
#   # No printorder specified - assumes de facto standard "row-by-row"
#   print(getPrintorder(layout))
#
#   #      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
#   # [1,]    1    2    3    4    5    6    7    8    9
#   # [2,]   10   11   12   13   14   15   16   17   18
#   # [3,]   19   20   21   22   23   24   25   26   27
#   # [4,]   28   29   30   31   32   33   34   35   36
#
#   # Spots (1,10,19,28) were printed first, then (2,11,20,29), ...
#
#   setPrintorder(layout, "column-by-column")
#   print(getPrintorder(layout))
#
#   #       [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
#   # [1,]    1    4    7    2    5    8    3    6    9
#   # [2,]   10   13   16   11   14   17   12   15   18
#   # [3,]   19   22   25   20   23   26   21   24   27
#   # [4,]   28   31   34   29   32   35   30   33   36
#
#   # Spots (1,10,19,28) were printed first, then (4,13,22,31) below, ...
# }
#
# @author
#
# \seealso{
#   @seeclass
# }
#*/#########################################################################
setMethodS3("getPrintorder", "Layout", function(this, value=1:nbrOfSpots(this), ...) {
  printorder <- this$.printorder;
  if (is.null(printorder)) {
    warning("Printorder not specified. Assume row-by-row printing.");
    index <- 1:nbrOfSpots(this);
    printorder <- matrix(index, nrow=nbrOfGrids(this), 
                              ncol=gridSize(this), byrow=TRUE);
  }
  res <- value[printorder];
  dim(res) <- dim(printorder);
  res;
})


setMethodS3("setPrintorder", "Layout", function(this, printorder=c("row-by-row","column-by-column"), ...) {
  if (is.numeric(printorder)) {
    if (length(printorder) != nbrOfSpots(this)) {
      throw("Vector 'printorder' should contain ", nbrOfSpots(this), 
                                       " elements: ", length(printorder));
    }
    printorder <- as.matrix(printorder);
  } else if (is.character(printorder)) {
    printorder <- match.arg(printorder);
    if (printorder == "row-by-row") {
      index <- 1:nbrOfSpots(this);
      printorder <- matrix(index, nrow=nbrOfGrids(this), 
                                ncol=gridSize(this), byrow=TRUE);
    } else if (printorder == "column-by-column") {
      index <- 1:nbrOfSpots(this);
      printorder <- matrix(index, nrow=nbrOfGrids(this), 
                                ncol=gridSize(this), byrow=TRUE);
      perm <- as.vector(matrix(1:gridSize(this), 
                        nrow=this$nspot.r, ncol=this$nspot.c, byrow=TRUE));
      printorder <- printorder[,perm];
    }
  } else if (!is.null(printorder)) {
    throw("Unknown value of 'printorder': ", printorder);
  } 
  this$.printorder <- printorder;
})



#########################################################################/**
# @RdocMethod indexOf
#
# @title "Gets the index of one or more spots from their name or id"
#
# @synopsis
#
# \description{
#  @get "title".
# }
#
# \arguments{
#   \item{name}{A string @vector (or a single regular expression) to match
#      against the names.}
#   \item{id}{A string @vector (or a single regular expression) to match
#      against the ids.}
#   \item{plate}{A string @vector (or a single regular expression) to match
#      against the plates. If it is @numeric it will be matched against the
#      plate number as given by \code{getPlateNumber()}.}
#   \item{ignoreCase}{If @TRUE, the matching is not case sensitive,
#      otherwise it is.}
#   \item{regexpr}{If @TRUE, regular expression matching is used,
#      otherwise plain string comparison is used.}
#   At least one of the arguments \code{name} and \code{id} must be given.
#   If both are given, indices of spots that match \emph{either} the 
#   \code{name} search pattern \emph{or} the \code{id} search pattern.
# }
#
# \value{
#   Returns the @vector of indices of matched names or ids. Returns
#   \code{numeric(0)} if no matches were found.
# }
#
# @author
#
# \examples{
#   SMA$loadData(c("mouse.setup", "mouse.gnames"))
#   layout <- as.Layout(mouse.setup, id=mouse.gnames)
#
#   # Get the index of spots with id "54" and "232".
#   indexOf(layout, id=c("54", "232"))
#   # [1] 54 232
#
#   # Get the index of all spots with id beginning with "120" and
#   # having at least four characters.
#   indexOf(layout, id="^120.+", regexpr=TRUE)
#   # [1] 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209
# }
#
# \seealso{
#   For more help on regular expressions see
#   @see "base::grep" and @see "base::apropos".
#   @seemethod "getName".
#   @seemethod "getId".
#   @seeclass
# }
#*/#########################################################################
setMethodS3("indexOf", "Layout", function(this, name=NULL, id=name, plate=NULL, ignoreCase=TRUE, regexpr=FALSE, ...) {
  if (is.null(name) && is.null(id) && is.null(plate))
    throw("At least one of the arguments 'name', 'id' and 'plate' must be specified.");

  # Do only search if there actually are anything to search for.
  name.src <- getName(this);
  if (is.null(name.src))
    name <- NULL;
  
  id.src <- getId(this);
  if (is.null(id.src))
    id <- NULL;
  
  if (!is.numeric(plate))
    plate.src <- getPlate(this)
  else
    plate.src <- getPlateNumber(this);
  if (is.null(plate.src))
    plate <- NULL;

  if (is.null(name) && is.null(id) && is.null(plate))
    return(numeric(0));

  if (ignoreCase == TRUE) {
    if (!is.null(name))      name <- tolower(name);
    if (!is.null(id))        id <- tolower(id);
    if (!is.null(name.src))  name.src <- tolower(name.src);
    if (!is.null(id.src))    id.src <- tolower(id.src);
    if (!is.numeric(plate)) {
      if (!is.null(plate))     plate <- tolower(plate);
      if (!is.null(plate.src)) plate.src <- tolower(plate.src);
    }
  }
  
  # Search for names?
  if (!is.null(name)) {
    if (regexpr == TRUE)
      names <- which(regexpr(name, name.src) != -1)
    else
      names <- which(!is.na(match(name.src, name)))
  }
  
  # Search for ids?
  if (!is.null(id)) {
    if (regexpr == TRUE)
      ids <- which(regexpr(id, id.src) != -1)
    else
      ids <- which(!is.na(match(id.src, id)))
  }

  # Search for plates?
  if (!is.null(plate)) {
    if (regexpr == TRUE)
      plates <- which(regexpr(plate, plate.src) != -1)
    else
      plates <- which(!is.na(match(plate.src, plate)))
  }

  res <- NULL;
  if (!is.null(name))
    res <- union(res, names);
  if (!is.null(id))
    res <- union(res, ids);
  if (!is.null(plate))
    res <- union(res, plates);
  res;
})

setMethodS3("as.Layout", "Layout", function(this, ...) {
  this;
})

setMethodS3("as.Layout", "ANY", function(object, ...) {
  Layout(object, ...)
})


#########################################################################/**
# @RdocMethod getIndex
#
# @title "Gets the index of a spot given its location"
#
# @synopsis
#
# \description{
#  @get "title". The location can either be
#  a vector containing the grid row and the grid column and the spot row
#  and the spot column in that grid, or it can be the same fields as 
#  seperate arguments.
# }
#
# @author
#
# \examples{
#   # Example 1
#   layout <- Layout(4,4, 18,18)
#   idx <- getIndex(layout, 2, 3, 4, 3)     # 2001
#   idx <- getIndex(layout, c(2, 3, 4, 3))  # 2001 (equivalent)
#   loc <- getLocation(layout, idx)         # 2 3 4 3
#
#   # Example 2
#   SMA$loadData(c("mouse.data", "mouse.setup"))
#   raw <- RawData(mouse.data, layout=as.Layout(mouse.setup))
#   ma <- getSignal(raw)
#   layout <- getLayout(ma)
#
#   plotSpatial(ma)
#
#   # Highlights spot number 2462
#   idx <- 2462
#   highlight(ma, idx, col="purple")
#
#   # Highlights the spot at grid (2,3) and its spot (4,3)
#   idx <- getIndex(layout, 2, 3, 4, 3);  # Spot #2460
#   highlight(ma, idx, col="purple")
#
#   # Highlights all spots in grid (1,2)
#   idx <- getIndices(layout, 1,2, NULL,NULL)
#   highlight(ma, idx, col="purple")
# }
#
# \seealso{
#   This method corresponds to image2id (see @see "sma::id2image") in
#   the sma package.
#   @seeclass
# }
#*/#########################################################################
setMethodS3("getIndex", "Layout", function(this, gridRow, gridColumn=NULL, spotRow=NULL, spotColumn=NULL, ...) {
  if (length(gridRow) >= 4) {
    location   <- gridRow;
    gridRow    <- location[1];
    gridColumn <- location[2];
    spotRow    <- location[3];
    spotColumn <- location[4];
  }
  
  if (gridRow < 1 || gridRow > this$ngrid.r)
    throw("gridRow is out of range: ", gridRow);
  if (gridColumn < 1 || gridColumn > this$ngrid.c)
    throw("gridColumn is out of range: ", gridColumn);
  if (spotRow < 1 || spotRow > this$nspot.r)
    throw("spotRow is out of range: ", spotRow);
  if (spotColumn < 1 || spotColumn > this$nspot.c)
    throw("spotColumn is out of range:", spotColumn);

  nbrOfSpotsPerGrid <- this$nspot.r*this$nspot.c;
  return ( ((gridRow-1)*this$ngrid.c+(gridColumn-1))*nbrOfSpotsPerGrid +
            (spotRow-1)*this$nspot.c+(spotColumn-1) + 1);
})


#########################################################################/**
# @RdocMethod getIndices
#
# @title "Gets the indices of the spots at the given locations"
#
# @synopsis
#
# \arguments{
#  \item{gridRows}{The grid rows to be included. If @NULL all are included.}
#  \item{gridColumns}{The grid column to be included. If @NULL all are included.}
#  \item{spotRows}{The spot rows to be included. If @NULL all are included.}
#  \item{spotColumns}{The spot column to be included. If @NULL all are included.}
# }
#
# \description{
#  @get "title".
#  This method is very useful for instance when one would like to look for
#  spatial effects such as edge effects etc.
# }
#
# @author
#
# \examples{
#   SMA$loadData(c("mouse.data", "mouse.setup"))
#   raw <- RawData(mouse.data, layout=as.Layout(mouse.setup))
#   ma <- getSignal(raw)
#   layout <- getLayout(ma)
#
#   plotSpatial(ma)
#
#   # Highlights all spots in grid (1,2)
#   idx <- getIndices(layout, gridRows=1, gridColumns=2);
#   highlight(ma, idx, col="purple")
#
#   # Highlight all spots in column 1, 6 and 12 in 
#   # grid (3,2), (3,3), (4,2) and (4,3).
#   idx <- getIndices(layout, gridRows=3:4, gridColumns=2:3, spotColumns=c(1,6,12));
#   highlight(ma, idx, col="orange")
#
#   # Highlight all "alley spots" of each printtip group, i.e. those spots that
#   # do *not* have eight neighbors and do a background "alley" next to them.
#   alley <- getIndices(layout, spotRows=c(1,layout$nspot.r))
#   alley <- union(alley, getIndices(layout, spotColumns=c(1,layout$nspot.c)));
#   highlight(ma, alley, col="pink")
# }
#*/#########################################################################
setMethodS3("getIndices", "Layout", function(this, gridRows=NULL, gridColumns=NULL, spotRows=NULL, spotColumns=NULL, ...) {
  if (is.null(gridRows))
    gridRows <- 1:this$ngrid.r;
  if (is.null(gridColumns))
    gridColumns <- 1:this$ngrid.c;
  if (is.null(spotRows))
    spotRows <- 1:this$nspot.r;
  if (is.null(spotColumns))
    spotColumns <- 1:this$nspot.c;

  if (any(gridRows < 1 || gridRows > this$ngrid.r))
    throw("gridRows is out of range.");
  if (any(gridColumns < 1 || gridColumns > this$ngrid.c))
    throw("gridColumns is out of range.");
  if (any(spotRows < 1 || spotRows > this$nspot.r))
    throw("spotRows is out of range.");
  if (any(spotColumns < 1 || spotColumns > this$nspot.c))
    throw("spotColumns is out of range.");

  indices <- c();
  for(gridRow in gridRows) {
    for(gridColumn in gridColumns) {
      for(spotRow in spotRows) {
        for(spotColumn in spotColumns) {
          indices <- c(indices, 
                   this$getIndex(gridRow, gridColumn, spotRow, spotColumn));
        }
      }
    }
  }
  
  sort(indices);
})




#########################################################################/**
# @RdocMethod getLocation
#
# @title "Gets the location of a spot given its index"
#
# @synopsis
#
# \arguments{
#  \item{index}{The spot index of one or many spots to be found. All values
#   much be within a valid range otherwise an exception is thrown.}
# }
#
# \description{
#  @get "title". Returns a @vector containing
#  the grid row and the grid column and the spot row and the spot column in
#  that grid.
# }
#
# @author
#
# \examples{
#    layout <- Layout(4,4, 18,18)
#    loc <- getLocation(layout, 2001)      # 2 3 4 3
#    idx <- getIndex(layout, loc)          # 2001
# }
#
# \seealso{
#   This method corresponds to @see "sma::id2image" in the sma
#   package.
#   @seeclass
# }
#*/#########################################################################
setMethodS3("getLocation", "Layout", function(this, index=NULL, ...) {
  if (is.null(index)) {
    index <- 1:nbrOfSpots(this);
  } else if (any(index < 1))
    throw("index is out of range.");
  
  n <- nbrOfSpots(this);   # Modulo n just in case
  index <- ((index-1) %% n)+1;

  nbrOfSpotsPerGrid <- this$nspot.r*this$nspot.c;
  nbrOfSpotsPerGridRow <- this$ngrid.c * nbrOfSpotsPerGrid;

  locations <- c();
  for (idx in index) {
    gridIndex <- (idx-1) %/% nbrOfSpotsPerGrid + 1;
    gridOffset <- (gridIndex-1) * nbrOfSpotsPerGrid;
    gridRow <- (idx-1) %/% nbrOfSpotsPerGridRow + 1;
    gridColumn <- (gridIndex-1) %% this$ngrid.c + 1;

    spotOffset <- idx - gridOffset;
    spotRow <- (spotOffset-1) %/% this$nspot.c + 1;  
    spotColumn <- (spotOffset-1) %% this$nspot.c + 1;  
    
    location <- c(gridRow, gridColumn, spotRow, spotColumn);
    locations <- c(locations, location);
  }

  matrix(locations, ncol=4, byrow=TRUE);
})


#########################################################################/**
# @RdocMethod getPosition
#
# @title "Gets the position of a set of spots given their indices"
#
# @synopsis
#
# \arguments{
#  \item{index}{The spot index of one or many spots to be found. All values
#   much be within a valid range otherwise an exception is thrown.
#   If @NULL, all spots are considered.}
# }
#
# \description{
#  @get "title". The position of
#  a spot is the pair (row, column) where row is the row in the microarray
#  and column is the column in the microarray where the spot is positioned.
# }
#
# \value{
#  Returns a "named" @matrix where each row contains the row and the 
#  column of the spots.
# }
#
# @author
#
# \examples{
#    layout <- Layout(4,4, 18,18)
#    xy <- getPosition(layout, 2001)           # 22 39
#    xy <- getPosition(layout, c(2001,2002))   # 22 39; 22 40
# }
#
# \seealso{
#   @seemethod "getIndex", @seemethod "getIndices", 
#   @seemethod "getLocation".
#   @seeclass
# }
#*/#########################################################################
setMethodS3("getPosition", "Layout", function(this, index=NULL, ...) {
  if (is.null(index)) {
    index <- 1:nbrOfSpots(this);
  } else if (any(index < 1))
    throw("index is out of range.");

  n <- nbrOfSpots(this);
  index <- ((index-1) %% n)+1;                     # Modulo n just in case
 
  # Index map for the whole layout. Independent of the argument 'index'!
  idxmap <- toXYMatrix(this, 1:n);
  rows <- c();  cols <- c();
  for (kk in 1:nbrOfRows(this)) rows[idxmap[kk,]] <- kk;
  for (kk in 1:nbrOfColumns(this)) cols[idxmap[,kk]] <- kk;

  rownames <- index;
  colnames <- c("row", "column");
  matrix(c(rows[index],cols[index]), ncol=2, byrow=FALSE, 
                                   dimnames=list(rownames, colnames));
})



#########################################################################/**
# @RdocMethod equals
#
# @title "Checks if a Layout object is equals to some other object"
#
# @synopsis
#
# \arguments{
#  \item{obj}{The other object for which this object should be compared to.}
# }
#
# \description{
#  Checks if a \code{Layout} object is equal to some other object, which
#  normally is another \code{Layout} object. It could however also be a
#  list with the same fields as a \code{Layout} object.
#  \cr
#  A layout is equal to another layout if it has 1) the same number of
#  rows and columns of grids as the other object, 2) the same number of
#  rows and columns of spots as the other object.
# }
#
# @author
#
# \examples{
#    layout1 <- Layout(4,4, 18,18)
#    layout2 <- Layout(4,4, 18,18)
#    layout3 <- Layout(4,4, 16,16)
#    equals(layout1, layout1)   # TRUE (of course)
#    equals(layout1, layout2)   # TRUE
#    equals(layout2, layout1)   # TRUE (symmetric)
#    equals(layout1, layout3)   # FALSE
#    equals(layout2, layout3)   # FALSE
# }
#*/#########################################################################
setMethodS3("equals", "Layout", function(this, obj, ...) {
  if (is.null(obj))
    return (FALSE);

  if (!inherits(obj, "Object") && !is.list(obj))
    return (FALSE);

  if (this$nspot.r != obj$nspot.r) return(FALSE);
  if (this$nspot.c != obj$nspot.c) return(FALSE);
  if (this$ngrid.r != obj$ngrid.r) return(FALSE);
  if (this$ngrid.c != obj$ngrid.c) return(FALSE);

  TRUE;
})
            
            

#########################################################################/**
# @RdocMethod set
#
# @title "Sets the layout"
#
# @synopsis
#
# \arguments{
#   \item{nspot.r}{The number of rows of spots per grid.}
#   \item{nspot.c}{The number of columns of spots per grid.}
#   \item{ngrid.r}{The number of rows of grids per slide.}
#   \item{ngrid.c}{The number of columns of grids per slide.}
#   \item{name}{A @vector if names for the spot.}
#   \item{id}{A @vector if ids for the spot.}
# }
#
# \description{
#  Sets the layout by either 1) explicitly setting the number of rows and
#  columns of grids and the number of rows and columns of spots within each
#  grid, or 2) by giving another \code{Layout} object.
# }
#
# @author
#
# \examples{
#    layout1 <- Layout(4,4, 18,18)
#    layout2 <- Layout()
#    set(layout2, 4,4, 16,16)   # Alternative 1
#    set(layout2, layout1)      # Alternative 2
# }
#*/#########################################################################
setMethodS3("set", "Layout", function(this, ngrid.r=NULL, ngrid.c=NULL, 
                   nspot.r=NULL, nspot.c=NULL, name=NULL, id=NULL, ...) {
  if (inherits(ngrid.r, "Layout")) {
    other <- ngrid.r;
    this$nspot.r <- other$nspot.r;
    this$nspot.c <- other$nspot.c;
    this$ngrid.r <- other$ngrid.r;
    this$ngrid.c <- other$ngrid.c;
    this$id      <- other$id;
  } else {
    if (!is.null(nspot.r)) this$nspot.r <- nspot.r;
    if (!is.null(nspot.c)) this$nspot.c <- nspot.c;
    if (!is.null(ngrid.r)) this$ngrid.r <- ngrid.r;
    if (!is.null(ngrid.c)) this$ngrid.c <- ngrid.c;
    if (!is.null(name))    this$.name   <- name;
    if (!is.null(id))      this$id      <- id;
  }
  invisible(this);
})


#########################################################################/**
# @RdocMethod nbrOfSpots
#
# @title "Gets the size of a microarray"
#
# \description{
#  Calculates the total number of spots on the microarray slide.
# }
#
# @synopsis
#
# @author
#
# \examples{
#   layout <- Layout(ngrid.r=4, ngrid.c=4, nspot.r=18, nspot.c=18)
#   print(nbrOfSpots(layout))  # 5184
# }
#*/#########################################################################
setMethodS3("nbrOfSpots", "Layout", function(this, ...) {
  this$ngrid.r * this$ngrid.c * this$nspot.r * this$nspot.c;
})



#########################################################################/**
# @RdocMethod nbrOfGrids
#
# @title "Gets the number of grids on a microarray"
#
# \description{
#  Calculates the number of grids on the microarray slide.
# }
#
# @synopsis
#
# @author
#
# \examples{
#   layout <- Layout(ngrid.r=4, ngrid.c=4, nspot.r=18, nspot.c=18)
#   print(nbrOfGrids(layout))  # 16
# }
#*/#########################################################################
setMethodS3("nbrOfGrids", "Layout", function(this, ...) {
  this$ngrid.r * this$ngrid.c;
})




#########################################################################/**
# @RdocMethod nbrOfRows
#
# @title "Gets the number of rows on a microarray"
#
# \description{
#  @get "title".
# }
#
# @synopsis
#
# @author
#
# \examples{
#   layout <- Layout(ngrid.r=4, ngrid.c=4, nspot.r=18, nspot.c=18)
#   print(nbrOfRows(layout))  # 72 = 4*18
# }
#*/#########################################################################
setMethodS3("nbrOfRows", "Layout", function(this, ...) {
  this$ngrid.r * this$nspot.r;
})





#########################################################################/**
# @RdocMethod nbrOfColumns
#
# @title "Gets the number of columns on a microarray"
#
# \description{
#  @get "title".
# }
#
# @synopsis
#
# @author
#
# \examples{
#   layout <- Layout(ngrid.r=4, ngrid.c=4, nspot.r=18, nspot.c=18)
#   print(nbrOfColumns(layout))  # 72 = 4*18
# }
#*/#########################################################################
setMethodS3("nbrOfColumns", "Layout", function(this, ...) {
  this$ngrid.c * this$nspot.c;
})





#########################################################################/**
# @RdocMethod size
#
# @title "Gets the size of a microarray"
#
# \description{
#  Calculates the total number of spots on the microarray slide.
# }
#
# @synopsis
#
# @author
#
# \examples{
#   layout <- Layout(ngrid.r=4, ngrid.c=4, nspot.r=18, nspot.c=18)
#   print(size(layout))  # 5184
# }
#
# \seealso{
#   @seemethod "nbrOfSpots".
#   @seeclass
# }
#*/#########################################################################
setMethodS3("size", "Layout", function(this, ...) {
  nbrOfSpots(this);
})





#########################################################################/**
# @RdocMethod gridSize
#
# @title "Gets the number of spots in each grid"
#
# \description{
#  @get "title".
# }
#
# @synopsis
#
# @author
#
# \examples{
#   layout <- Layout(ngrid.r=4, ngrid.c=4, nspot.r=18, nspot.c=18)
#   print(gridSize(layout))   # 324 = 18*18
# }
#*/#########################################################################
setMethodS3("gridSize", "Layout", function(this, ...) {
  this$nspot.r * this$nspot.c;
})




#########################################################################/**
# @RdocMethod toXYMatrix
#
# @title "Layouts out values in the same way as the spots are layout"
#
# \description{
#  @get "title".
# }
#
# @synopsis
#
# \arguments{
#   \item{values}{The values to be put into the matrix.}
#   \item{flip}{If @TRUE the resulting matrix is flipped
#      vertically before returned.}
# }
#
# @author
#
# \examples{
#   layout <- Layout(3,2, 2,3)
#
#   # Print the indices of the each of the spots as they appear
#   # on the slide(s).
#   print(toXYMatrix(layout))
#
#   #      [,1] [,2] [,3] [,4] [,5] [,6]
#   # [1,]    1    2    3    7    8    9
#   # [2,]    4    5    6   10   11   12
#   # [3,]   13   14   15   19   20   21
#   # [4,]   16   17   18   22   23   24
#   # [5,]   25   26   27   31   32   33
#   # [6,]   28   29   30   34   35   36
# }
#*/#########################################################################
setMethodS3("toXYMatrix", "Layout", function(this, values=seq(nbrOfSpots(this)), flip=FALSE, ...) {
  gc <- this$ngrid.c;
  gr <- this$ngrid.r;
  sc <- this$nspot.c;
  sr <- this$nspot.r;
  
  n <- nbrOfSpots(this);
  nbrOfGrids <- nbrOfGrids(this);
  gridSize <- gridSize(this);

  # Split the values gridwise
  gridIdx <- rep(1:(nbrOfGrids), rep(gridSize, nbrOfGrids));
  gridMs  <- split(values, gridIdx);

  # For each grid, create a matrix. Result is put into a list
  imgGrids <- lapply(gridMs, FUN=matrix, nrow=sr, ncol=sc, byrow=TRUE);

  # Number of spots per grid row
  nbrOfSpotsPerGridRow <- gridSize*gc;

  # Grid column each spot is located in.
  gridRowIdx <- rep(1:gr, rep(nbrOfSpotsPerGridRow, gr));

  # The grid rows, row by row.
  gridRows <- split(unlist(imgGrids), gridRowIdx);

  # Turn the the each row of grids into grid matrices
  grids <- lapply(gridRows, FUN=matrix, nrow=sr);

  img <- NULL;
  # For each grid row
  for (i in 1:gr)
   img <- rbind(img, grids[[i]]);

  if (flip) {
    # Flip image vertically. Image size = rows x columns
    img <- t(apply(img, MARGIN=2, FUN=rev)); 
  }

  img;
})




############################################################################
############################################################################
## 
##  PLOTTING & GRAPHICAL METHODS
## 
############################################################################
############################################################################

setMethodS3("put", "Layout", function(this, x,y, width="20%", height=width, border="black", labels=NULL, offset=c(0.5,0.5), col=NULL, lty=NULL, ...) {

  height <- height;
  if (is.null(offset))
    throw("Argument 'offset' can't be NULL.");
  offset <- rep(offset, length.out=2);

  plot.area <- par("usr");

  if (is.character(width)) {
    tmp <- as.integer(unlist(strsplit(width, "%"))[1]);
    width <- (plot.area[2]-plot.area[1])*tmp/100;
  } else if (is.null(width)) {
    throw("Argument 'width' can not be NULL.");
  }
  if (is.character(height)) {
    tmp <- as.integer(unlist(strsplit(height, "%"))[1]);
    height <- (plot.area[4]-plot.area[3])*tmp/100;
  } else if (is.null(height)) {
    throw("Argument 'height' can not be NULL.");
  }

  if (is.character(x)) {
    tmp <- as.integer(unlist(strsplit(x, "%"))[1]);
    x <- plot.area[1]+(plot.area[2]-plot.area[1]-width)*tmp/100;
  } else if (is.null(x)) {
    throw("Argument 'x' can not be NULL.");
  }
  if (is.character(y)) {
    tmp <- as.integer(unlist(strsplit(y, "%"))[1]);
    y <- plot.area[3]+(plot.area[4]-plot.area[3]-height)*tmp/100;
  } else if (is.null(y)) {
    throw("Argument 'y' can not be NULL.");
  }

  x0 <- x; x1 <- x+width;
  y0 <- y; y1 <- y+height;

  nrow <- this$ngrid.r;
  ncol <- this$ngrid.c;
  dx <- width/ncol;
  dy <- height/nrow;

  # Plot the grid layout
  for (row in 0:nrow) {
    yy <- y0+row*dy;
    lines(x=c(x0,x1), y=c(yy,yy), col=border, ...);
  }
  for (column in 0:ncol) {
    xx <- x0+column*dx;
    lines(x=c(xx,xx), y=c(y0,y1), col=border, ...);
  }

  if (is.null(labels)) labels <- 1:(ncol*nrow);

  if (!is.null(col) && col=="auto")
    col <- rainbow(ncol)
  else if (is.null(col)) 
    col <- par("col");
  col <- rep(col, length=ncol*nrow);

  if (!is.null(lty) && lty=="auto")
    lty <- matrix(1:nrow, nrow=nrow, ncol=ncol, byrow=TRUE);
  lty <- rep(lty, length.out=ncol*nrow);

  grids <- matrix(1:nbrOfGrids(this), ncol=ncol, byrow=TRUE);
  for (row in 1:nrow) {
    rect.y0 <- y1-(row-1+0.1)*dy;
    rect.y1 <- y1-(row-1+0.9)*dy;
    yy <- y1-(row-1+offset[2])*dy;
    for (column in 1:ncol) {
      rect.x0 <- x0+(column-1+0.1)*dx;
      rect.x1 <- x0+(column-1+0.9)*dx;
      xx <- x0+(column-1+offset[1])*dx;
      idx <- grids[row,column];
      text(xx,yy, labels=labels[idx], col=col[idx], ...);
      if (!is.null(lty[idx]))
        rect(rect.x0,rect.y0,rect.x1,rect.y1, border=col[idx], lty=lty[idx], ...)
    }
  }
})



#########################################################################/**
# @RdocMethod toPrintorderMatrix
#
# @title "Gets a matrix of spot indices in the order they were printed"
#
# @synopsis
#
# \arguments{
#   \item{value}{The elements to be placed in the resulting matrix.}
# }
#
# \description{
#   @get "title". The
#   spots in column one were printed first, then the spots in column two
#   and so on. By default the spot indices are returned.
# }
#
# \value{
#   Returns a @matrix with \code{nbrOfGrids(layout)} rows and
#   \code{gridSize(layout)} columns.
# }
#
# \details{
#   When printing a slide at each print step \code{nbrOfGrids(layout)}
#   spots are printed at the same time. The arrayer start of spotting the
#   first spot in \emph{each} of the grids. The it cleans the print-tip
#   heads, dries them, and go back to the trays to get a \emph{new} set of
#   cDNA and prints the second spot in each of the grids. The second spot
#   is to the right of the first spot. When the array gets to the end of
#   a row it has printed the first line of each grid. The arrayer now moves
#   on to print the spots in line two, three and so on until all lines in
#   all grids have been printed.\cr
#
#   The printing of a microarray is time consuming and often several
#   microarray slides are printed at the same time, since it is even more
#   time consuming to switch between the trays. When printing several
#   microarrays at the same time, the arrayer prints the first spot in
#   all grids on \emph{all} slides, before moving on to the second spot.
#   For a example, printing a batch of 100 slides with 6384 spots in 4x4
#   grids takes about 15 hours to print including manual work to switch
#   trays etc. Each grid contains 19*21 spots, i.e. the arrayer has to put
#   down the print tips 399 times on each slide, and in total 39900 times.
#   This is about 44 put-downs a minute. It takes about 45-50 minutes
#   to finish one row of spots.
#
#   An important factor for the quality of the printed spots is the
#   temperature and the humidity. Too high temperature and humidity
#   tends to produce too large spots that can even overlap [1]. If there
#   is no automatic control for temperature and humidity, the quality of
#   the spots could vary a lot between the spots printed during a 15 hours
#   printing process. With a varying printing climate we should expect to
#   see a variating of the quality of the spots along the order of which
#   the spots are printed.
#   The variation of temperature and humidity varies approximately in the
#   time scale of hours. As it takes about 45-50 minutes to print a row
#   of spots, we should therefore expect to see such a variation between
#   the rows in the grids.
# }
#
# \references{
#   [1] Microarrays in Three Easy Steps, Priti Hedge, The Institute for
#       Genomic Research, 200?.
# }
#
# \examples{
#   layout <- Layout(2,2, 3,3)
#
#   print(toPrintorderMatrix(layout))
#
#   #      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
#   # [1,]    1    2    3    4    5    6    7    8    9
#   # [2,]   10   11   12   13   14   15   16   17   18
#   # [3,]   19   20   21   22   23   24   25   26   27
#   # [4,]   28   29   30   31   32   33   34   35   36
#
#   # Spot 1, 10, 19 and 28 were printed first, then 2, 11, 20, and 29...
# }
#
# @author
#
# \seealso{
#   @seeclass
# }
#*/#########################################################################
setMethodS3("toPrintorderMatrix", "Layout", function(this, value=seq(nbrOfSpots(this)), ...) {
  getPrintorder(this, value=value);
}, private=TRUE)





setMethodS3("as.data.frame", "Layout", function(x, ...) {
  # To please R CMD check...
  this <- x;

  grow <- matrix(1:this$ngrid.r, nrow=this$ngrid.r, ncol=this$ngrid.c*gridSize(this), byrow=FALSE);
  grow <- as.vector(t(grow));
  gcol <- matrix(1:this$ngrid.c, nrow=gridSize(this), ncol=this$ngrid.c*this$ngrid.r, byrow=TRUE);
  gcol <- as.vector(gcol);
  row <- matrix(1:this$nspot.r, nrow=this$nspot.r, ncol=this$nspot.c, byrow=FALSE);
  row <- as.vector(t(row));
  row <- rep(row, times=this$ngrid.r*this$ngrid.c);
  col <- matrix(1:this$nspot.c, nrow=this$nspot.c, ncol=this$nspot.r, byrow=FALSE);
  col <- as.vector(col);
  col <- rep(col, times=this$ngrid.r*this$ngrid.c);
  df <- data.frame(gridRow=grow, gridColumn=gcol, spotRow=row, spotColumn=col);

  fields <- getFields(this, private=TRUE);
  fields <- sub("^[.]", "", fields);
#  fields <- c("name", "id", "plate", "acc", "clid", "type");
  for (field in fields) {
    value <- this[[field]];
    if (inherits(value, "AsIs"))
      value <- unclass(value);
    if (is.vector(value) && length(value) == nrow(df)) {
      names <- c(names(df), field);
      df <- cbind(df, I(value));
      names(df) <- names;
    }
  }
  df;  
});


setMethodS3("fromDataFrame", "Layout", function(this, df, ...) {
  if (!is.data.frame(df))
    throw("Argument 'df' is not a data frame.");
  header <- c("gridRow", "gridColumn", "spotRow", "spotColumn");
  if (!all(is.element(header, names(df)))) {
    headerStr <- paste("'", header, "'", sep="", collapse=", ");
    throw("The data frame must contain all of the fields ", headerStr, ": ", paste(names(df), collapse=", "));
  }
  ngrid.r <- max(df[[header[1]]]);
  ngrid.c <- max(df[[header[2]]]);
  nspot.r <- max(df[[header[3]]]);
  nspot.c <- max(df[[header[4]]]);
  id      <- as.vector(df[["id"]]);
  name    <- as.vector(df[["name"]]);
  plate   <- as.vector(df[["plate"]]);
  layout <- Layout(ngrid.r, ngrid.c, nspot.r, nspot.c, id=id, name=name, plate=plate)
  for (name in setdiff(colnames(df), c("gridRow", "gridColumn", "spotRow", "spotColumn", "id", "name", "plate")))
    layout[[name]] <- as.vector(df[[name]]);
  layout;
}, static=TRUE);



#########################################################################/**
# @RdocMethod read
#
# @title "Reads layout information from a tab-delimited file"
#
# \description{
#  Static method that reads layout information from a tab-delimited file.
# }
#
# @synopsis
#
# \arguments{
#   \item{filename}{The name of the file.}
#   \item{path}{Optional path where the data should be written.}
#   \item{sep}{Separator @character between columns.}
#   \item{header}{If @TRUE column headers are written, otherwise not.}
#   \item{...}{Other arguments accepted by \code{read.table()}.}
# }
#
# @author
#
# \examples{
#   layout <- Layout$read("MouseArray.Layout.dat", path=system.file("data-ex", package="aroma"))
#   print(layout)
# }
#
# \seealso{
#   For writing a Layout object to a file @seemethod "write".
#   See also \code{read.table()}.
#   @seeclass
# }
#*/#########################################################################
setMethodS3("read", "Layout", function(this, filename, path=NULL, header=TRUE, sep="\t", quote="", ...) {
  filename <- Arguments$getReadablePathname(filename, path);  

  # Support gzip'ed files too.
  if (regexpr("[.]gz$", filename) != -1) {
    tmpname <- tempfile();
    n <- gunzip(filename, tmpname);
    filename <- tmpname;
    on.exit(file.remove(tmpname));
  } 

  df <- read.table(file=filename, sep=sep, header=header, quote=quote, ...);
  for (k in seq(along=length(df))) {
  }

  Layout$fromDataFrame(df);
}, static=TRUE); 




#########################################################################/**
# @RdocMethod write
#
# @title "Writes the layout information to a tab-delimited file"
#
# \description{
#  @get "title".
# }
#
# @synopsis
#
# \arguments{
#   \item{filename}{The name of the file.}
#   \item{path}{Optional path where the data should be written.}
#   \item{row.names}{If @TRUE row names are written, otherwise not.}
#   \item{sep}{Separator @character between columns.}
#   \item{...}{Other arguments accepted by \code{write.table()}.}
# }
#
# @author
#
# \examples{
#   layout <- Layout(3,2, 2,3)
#   tmpfile <- tempfile()
#   write(layout, tmpfile)
#   file.show(tmpfile)
#   unlink(tmpfile)
# }
#
# \seealso{
#   For read a Layout object from file see @seemethod "read".
#   See also \code{write.table()}.
#   @seeclass
# }
#*/#########################################################################
setMethodS3("write", "Layout", function(this, filename, path=NULL, overwrite=FALSE, row.names=FALSE, sep="\t", quote=FALSE, ...) {
  filename <- Arguments$getWritablePathname(filename, path, mustNotExist=!overwrite);  
  
  df <- as.data.frame(this);
  write.table(df, file=filename, row.names=row.names, sep=sep, quote=quote, ...);
});



setMethodS3("getLayoutGroupsByName", "Layout", function(this, name, ...) {
  if (!is.character(name) && length(name) != 1)
    throw("Argument 'name' must be a single character string.");

  methodName <- paste("get", capitalize(name), "Groups", sep="");

  # Subclasses of Layout that we should look in.
  classes <- class(this);
  last <- which(classes == "Layout");
  classes <- classes[1:last];

  methods <- paste(methodName, classes, sep=".");

  for (method in methods) {
    if (exists(method, mode="function")) {
      fcn <- get(method, mode="function");
      layoutGroup <- fcn(this);
      if (!inherits(layoutGroup, "LayoutGroups"))
        throw("Method did not return a LayoutGroups object as expected: ", data.class(layoutGroup));
      return(layoutGroup);
    }
  }

  throw("No method corresponding to LayoutGroup '", name, "' exists: ", methodName);
})


setMethodS3("getGeneGroups", "Layout", function(object, ...) {
  # Cache it
  if (is.null(object$geneGrps))
    object$geneGrps <- GeneGroups(object)
  object$geneGrps;
})

setMethodS3("setGeneGroups", "Layout", function(object, genes, ...) {
  if (is.null(genes) || inherits(genes, "GeneGroups"))
    object$geneGrps <- genes;
})

setMethodS3("getPlateGroups", "Layout", function(object, ...) {
  # Cache it
  if (is.null(object$plateGrps))
    object$plateGrps <- PlateGroups(object)
  object$plateGrps;
})

setMethodS3("setPlateGroups", "Layout", function(object, plates, ...) {
  if (is.null(plates) || inherits(plates, "PlateGroups"))
    object$plateGrps <- plates;
})

setMethodS3("getPrintdipGroups", "Layout", function(object, ...) {
  # Cache it
  if (is.null(object$printdipGrps))
    object$printdipGrps <- PrintdipGroups(object)
  object$printdipGrps;
})

setMethodS3("setPrintdipGroups", "Layout", function(object, printdips, ...) {
  if (is.null(printdips) || inherits(printdips, "PrintdipGroups"))
    object$printdipGrps <- printdips;
})


setMethodS3("getPrinttipGroups", "Layout", function(object, ...) {
  # Cache it
  if (is.null(object$printtipGrps))
    object$printtipGrps <- PrinttipGroups(object)
  object$printtipGrps;
})

setMethodS3("setPrinttipGroups", "Layout", function(object, printtips, ...) {
  if (is.null(printtips) || inherits(printtips, "PrinttipGroups"))
    object$printtipGrps <- printtips;
})


setMethodS3("getSlideRowGroups", "Layout", function(object, ...) {
  # Cache it
  if (is.null(object$slideRowGrps))
    object$slideRowGrps <- SlideRowGroups(object)
  object$slideRowGrps;
})

setMethodS3("setSlideRowGroups", "Layout", function(object, slideRows, ...) {
  if (is.null(slideRows) || inherits(slideRows, "SlideRowGroups"))
    object$slideRowGrps <- slideRows;
})


setMethodS3("getSlideColumnGroups", "Layout", function(object, ...) {
  # Cache it
  if (is.null(object$slideColumnGrps))
    object$slideColumnGrps <- SlideColumnGroups(object)
  object$slideColumnGrps;
})

setMethodS3("setSlideColumnGroups", "Layout", function(object, slideColumns, ...) {
  if (is.null(slideColumns) || inherits(slideColumns, "SlideColumnGroups"))
    object$slideColumnGrps <- slideColumns;
})



setMethodS3("getBlank", "Layout", function(object, blanks="blank|empty", ...) {
  result <- c();
  count <- 0;
  s <- getName(object);
  if (length(s) > 0) {
    s <- tolower(s);
    result <- (regexpr(blanks, s) != -1);
    count <- count + 1;
  }
  s <- getId(object);
  if (length(s) > 0) {
    s <- tolower(s);
    result <- (regexpr(blanks, s) != -1);
    count <- count + 1;
  }
  if (count == 0)
    warning("No blanks were found because no names nor ids are defined.");
  result
})


setMethodS3("anonymize", "Layout", function(object, method=c("numerate", "randomize", "reshuffle"), blanks="blank|empty", ...) {
  BLANK <- "BLANK";

  method <- tolower(method[1]);
  if (!is.element(method, c("numerate", "randomize", "reshuffle")))
    throw("Unknown anonymizer method: ", method);
  
  n <- nbrOfSpots(object);
  # Keep the blanks...
  blanks <- getBlank(object, blanks=blanks);
#  blanks <- rep(FALSE, n);
  if (hasNames(object)) {
    s <- getName(object);
    s <- tolower(s);
    uniques <- unique(s);
    nu <- length(uniques);
    match <- match(s, uniques);
    if (method == "numerate") {
      map <- 1:nu;
    } else if (method == "randomize") {
      map <- sample(1:nu, nu);
    } else if (method == "reshuffle") {
      map <- sample(uniques, nu);
    }
    ws <- (nchar(s) == 0);
    s[!blanks & !ws] <- map[match][!blanks & !ws];
    s[blanks] <- BLANK;
    setName(object, s);
  }
  if (hasIds(object)) {
    s <- getId(object);
    s <- tolower(s);
    uniques <- unique(s);
    nu <- length(uniques);
    match <- match(s, uniques);
    if (method == "numerate") {
      map <- 1:nu;
    } else if (method == "randomize") {
      map <- sample(1:nu, nu);
    } else if (method == "reshuffle") {
      map <- sample(uniques, nu);
    }
    ws <- (nchar(s) == 0);
    s[!blanks & !ws] <- map[match][!blanks & !ws];
    s[blanks] <- BLANK;
    setId(object, s);
  }
  if (hasPlates(object)) {
    s <- getPlate(object);
    s <- tolower(s);
    uniques <- unique(s);
    nu <- length(uniques);
    match <- match(s, uniques);
    if (method == "numerate") {
      map <- 1:nu;
    } else if (method == "randomize") {
      map <- sample(1:nu, nu);
    } else if (method == "reshuffle") {
      map <- sample(uniques, nu);
    }
    s <- map[match];
    setPlate(object, s);
  }
})



# - Deprecated stuff - #

setMethodS3("getID", "Layout", function(this, ...) {
  getId(this, ...);
}, deprecated=TRUE, private=TRUE)

setMethodS3("hasIDs", "Layout", function(this, ...) {
  hasIds(this, ...);
}, deprecated=TRUE, private=TRUE)

setMethodS3("setID", "Layout", function(this, ...) {
  setId(this, ...);
}, deprecated=TRUE, private=TRUE)


setMethodS3("setByLayout", "Layout", function(this, other, ...) {
  this$nspot.r <- other$nspot.r;
  this$nspot.c <- other$nspot.c;
  this$ngrid.r <- other$ngrid.r;
  this$ngrid.c <- other$ngrid.c;
  this$id      <- other$id;

  this;
}, private=TRUE);
            

setMethodS3("setExplicit", "Layout", function(this, ngrid.r=NULL, 
            ngrid.c=NULL, nspot.r=NULL, nspot.c=NULL, name=NULL, id=NULL, ...) {
  if (!is.null(nspot.r)) this$nspot.r <- nspot.r;
  if (!is.null(nspot.c)) this$nspot.c <- nspot.c;
  if (!is.null(ngrid.r)) this$ngrid.r <- ngrid.r;
  if (!is.null(ngrid.c)) this$ngrid.c <- ngrid.c;
  if (!is.null(name))    this$.name   <- name;
  if (!is.null(id))      this$id      <- id;
  this;
}, private=TRUE);

############################################################################
# HISTORY:
# 2005-10-21
# o Replace 'overwrite' arguments with 'mustNotExist' in calls to Arguments. 
# 2005-07-19
# o Replaced all path="" arguments with path=NULL.
# 2005-06-11
# o Making use of Arguments in R.utils.
# 2005-03-08
# o Added automatic detection and reading of gunzipped files (*.gz).
# 2005-02-12
# o getPosition() now returned a "named" matrix, i.e. with column names
#   "row" and "column", and rownames for the spot indicies.
# 2004-08-15
# o Updates "name's" <- "names", "id's" <- "ids" etc. 
# 2003-09-27
# o Added getLayoutGroupsByName(). Very useful in all plot and normalization
#   methods etc that have a groupBy argument.
# 2003-09-20
# o Added getPrintorder() and setPrintorder(). getPrintorder() is to
#   replace toPrintorderMatrix().
# o Removed obsolete '.return' argument from set().
# o Made setByLayout() and setExplicit() deprecated.
# 2003-07-28
# o Added an example to getIndices() how to get the "alley spots", i.e.
#   those spots in each printtip groups that do not have eight neighbors.
# 2002-12-11
# o Renamed getID(), setID() hasIDs() to getId(), setId() and hasIds()
#   according to RCC. The old ones are kept for backward compatibilities.
# o Added rename().
# 2002-12-05
# o Add getSlideRowGroups() and getSlideColumnGroups().
# 2002-11-27
# o fromDataFrame() now deals with all objects (also private) that are
#   vectors and with the correct length.
# o as.data.frame() now includes data from all objects (also private with
#   available get<Field>() functions defined) such that the returned object
#   is a vector and with the correct length.
# o The above will make write() save more fields.
# o as.data.frame() now make use of I() (the "AsIs" class).
# o BUG FIX: setGeneGroups(), setPrinttipGroups() etc all contained syntax
#   errors.
# 2002-11-20
# o Updated as.character() to report data.class(this) instead of hardcoded 
#   "Layout".
# 2002-05-04
# o Added getPrintdipGroups().
# 2002-05-03
# o Added anonymize() and getBlank().
# o Extracted a lot of methods identified to be obsolete into file
#   Layout.obsolete.R
# 2002-05-02
# o TYPO FIX: Type fix in string returned by as.character().
# 2002-05-01
# * Added field plate, getPlate(), setPlate(), hasPlate(), getPlateNumber()
#   and nbrOfPlates().
# * Added as.data.frame(), read() and write().
# 2002-04-21
# * Added getGeneReplicateIndex() and getGeneSlideReplicateIndex()
#   to MicroarrayData.
# * Removed obsolete getReferenceFields().
# 2002-04-13
# * Added optional arguments ignoreCase=TRUE and regexpr=FALSE to indexOf().
#   Updated the Rdoc accordingly and added a few more examples.
#   This update was trigged by questions from Lei Jiang.
# 2002-04-05
# * Added default values of 'index' in getLocation() and getPosition().
# 2002-03-29
# * BUG FIX: Added as.character() to getID() and getName() just to make sure
#   it is not a factor that is returned. Will fix this in GenePixData.R too.
#   This is a double security so it won't happend again.
# 2002-02-26
# * Updated the Rdoc's.
# 2001-01-24
# * Modified source to make use of setMethodS3 and setClassS3.
# * Added nbrOfReplicates().
# 2001-11-18
# * Freshend the code of setReplicates().
# 2001-08-08
# * Added toPrintorderMatrix().
# 2001-08-06
# * BUG FIX: equals() didn't work correctly.
# 2001-08-01
# * Moving the functionalites of replicates back to Layout. For now only
#   the API, but later also the internal code... maybe.
# 2001-07-18
# * Renamed the fields .name and .id to name and id.
# * Created a new class Replicates and moved all replicate/duplicate 
#   functionalities there.
# 2001-07-15
# * Added findReplicates().
# * Added support to get, set and check replicates of genes. Will have to
#   define the concept of a gene index, but it should be pretty straight-
#   forward. Also, I would like to add a method which automatically
#   generates the replicate matrix by look at the list of Names or IDs.
# 2001-07-12
# * Added get- and setName(). GenePix is using both a Name and a ID field.
# 2001-07-11
# * Updated the Rdoc comments.
# 2001-07-09
# * Improved the speed of getPosition() by 30 times!
# 2001-07-07
# * Moved all duplicates/replicates methods in MicroarrayData here.
# * Made all function take modulo n of all indices.
# 2001-07-04
# * Added toXYMatrix(). Useful for plotSpatial() etc.
# 2001-07-01
# * Updated some of the Rdoc comments.
# 2001-06-30
# * Added id's to the Layout class.
# 2001-06-24
# * Made getLocation() work on vectors too. 
# * Added some Rd comments.
# 2001-05-14
# * Added getInternalReferences() for improving gco() performance.
# 2001-04-08
# * Bug fix: Argument indices in getPositions(indices) was misspelled.
# 2001-04-02
# * Added getIndices(),getLocations(), getPostion(), getPositions().
#   However, these methods need some speed optimization because now they're
#   quite slow. I don't wanna spend more time on this right now!
# 2001-03-25
# * Added Rdoc comments.
# 2001-03-19
# * Created.
############################################################################
HenrikBengtsson/aroma documentation built on May 7, 2019, 12:56 a.m.