#' The connector class to KEGG Pathway database.
#'
#' This is a concrete connector class. It must never be instantiated directly,
#' but instead be instantiated through the factory \code{BiodbFactory}.
#' Only specific methods are described here. See super classes for the
#' description of inherited methods.
#'
#' @seealso \code{\link{KeggConn}}.
#'
#' @examples
#' # Create an instance with default settings:
#' mybiodb <- biodb::newInst()
#'
#' # Get connector
#' conn=mybiodb$getFactory()$createConn('kegg.pathway')
#'
#' # Retrieve all reactions related to a mouse pathway:
#' reactions=conn$getReactions('mmu00260')
#'
#' # Get a pathway graph
#' graph=conn$buildPathwayGraph('mmu00260')
#'
#' # Terminate instance.
#' mybiodb$terminate()
#'
#' @include KeggConn.R
#' @export
KeggPathwayConn <- R6::R6Class("KeggPathwayConn",
inherit=KeggConn,
public=list(
#' @description
#' New instance initializer. Connector classes must not be instantiated
#' directly. Instead, you must use the createConn() method of the factory class.
#' @param ... All parameters are passed to the super class initializer.
#' @return Nothing.
initialize=function(...) {
super$initialize(db.name='pathway', db.abbrev='path', ...)
},
#' @description
#' Retrieves all reactions part of a KEGG pathway. Connects to
#' KEGG databases, and walk through all pathways submitted, and
#' their modules, to find all reactions they are composed of.
#' @param id A character vector of entry IDs.
#' @param drop If set to TRUE, returns a single KEGG reaction object
#' instead of a list, if the list contains only one element.
#' @return A list of KEGG reaction objects.
getReactions=function(id, drop=TRUE) {
reactions <- list()
react_ids <- character()
kegg.mod.conn <- self$getBiodb()$getFactory()$getConn('kegg.module')
# Loop on all Pathway IDs
for (path.id in id) {
path <- self$getEntry(path.id)
if ( ! is.null(path) && path$hasField('kegg.module.id')) {
# Loop on all modules
for (mod.id in path$getFieldValue('kegg.module.id')) {
mod <- kegg.mod.conn$getEntry(mod.id)
if ( ! is.null(mod) && mod$hasField('kegg.reaction.id'))
react_ids <- c(react_ids,
mod$getFieldValue('kegg.reaction.id'))
}
}
}
react_ids <- unique(react_ids)
kegg.react.conn <- self$getBiodb()$getFactory()$getConn('kegg.reaction')
reactions <- kegg.react.conn$getEntry(react_ids, drop=FALSE)
reactions <- reactions[ ! vapply(reactions, is.null, FUN.VALUE=TRUE)]
# Drop
if (drop && length(reactions) <= 1) {
reactions <- (if (length(reactions) == 1) reactions[[1]] else NULL)
}
return(reactions)
},
#' @description
#' Takes a list of pathways IDs and converts them to the specified organism,
#' filtering out the ones that do not exist in KEGG.
#' @param id A character vector of entry IDs.
#' @param org The organism in which to search for pathways, as a KEGG organism
#' code (3-4 letters code, like 'hsa', 'mmu', ...). See
#' https //www.genome.jp/kegg/catalog/org_list.html for a complete list of KEGG
#' organism codes.
#' @return A character vector, the same length as `id`, containing the
#' converted IDs.
convertToOrgPathways=function(id, org) {
# Set organism code in IDs
id <- sub('^[^0-9]+', org, id)
# Get entries to check existence
entries <- self$getEntry(id, drop=FALSE)
# Filter out non existing entries
id <- id[ ! vapply(entries, is.null, FUN.VALUE=TRUE)]
return(id)
},
#' @description
#' Builds a pathway graph in the form of two tables of vertices and edges,
#' using KEGG database.
#' @param id A character vector of KEGG pathway entry IDs.
#' @param directed If set to TRUE, use available direction information to
#' create directed edges, duplicating if necessary the vertices.
#' @param drop If set to TRUE and the output list contains only one element,
#' then the returned value is a single list of two data frames.
#' @return A named list whose names are the pathway IDs, and values are lists
#' containing two data frames named vertices and edges.
buildPathwayGraph=function(id, directed=FALSE, drop=TRUE) {
graph <- list()
# Loop on all pathway IDs
for (path.id in id) {
edg <- NULL
vert <- NULL
# Loop on all reactions
for (react in self$getReactions(id)) {
g <- private$buildReactionGraph(react, directed)
if ( ! is.null(g)) {
edg <- rbind(edg, g$edges)
vert <- rbind(vert, g$vertices)
}
}
# Build graph
if ( ! is.null(edg)) {
vert <- vert[ ! duplicated(vert[['name']]), ]
graph[[path.id]]=list(vertices=vert, edges=edg)
}
else
graph[[path.id]]=NULL
}
# Drop
if (drop && length(graph) <= 1)
graph <- if (length(graph) == 1) graph[[1]] else NULL
return(graph)
},
#' @description
#' Builds a pathway graph, as an igraph object, using KEGG database.
#' @param id A character vector of KEGG pathway entry IDs.
#' @param directed If set to TRUE, use available direction information to
#' create directed edges, duplicating if necessary the vertices.
#' @param drop If set to TRUE and the output list contains only one element,
#' then the returned value is a single igraph object.
#' @return A list of igraph objects, or an empty list if the igraph library is
#' not available.
getPathwayIgraph=function(id, directed=FALSE, drop=TRUE) {
graph <- list()
if (require('igraph', quietly=TRUE, warn.conflicts=FALSE)) {
detach('package:igraph') # Force using namespace.
g <- self$buildPathwayGraph(id=id, directed=directed, drop=FALSE)
for (n in names(g)) {
# Get edges and vertices
e <- g[[n]][['edges']]
v <- g[[n]][['vertices']]
# Set colors and shapes
# Create igraph object
graph[[n]] <- igraph::graph_from_data_frame(e, directed=directed,
vertices <- v)
}
}
# Drop
if (drop && length(graph) <= 1)
graph <- if (length(graph) == 1) graph[[1]] else NULL
return(graph)
},
#' @description
#' Create a pathway graph picture, with some of its elements colorized.
#' @param id A KEGG pathway ID.
#' @param color2ids A named list defining colors for entry IDs that are present
#' on the graph. The names of the list are standard color names. The values are
#' character vector of entry IDs.
#' @return An image object or NULL if the package magick is not available.
getDecoratedGraphPicture=function(id, color2ids) {
pix <- NULL
if (require('magick', quietly=TRUE, warn.conflicts=FALSE)) {
detach('package:magick') # Force using namespace.
# Get image
pix <- private$getPathwayImage(id)
# Extract shapes
shapes <- self$extractPathwayMapShapes(id=id, color2ids=color2ids)
# Draw shapes
dev <- magick::image_draw(pix)
for (shape in shapes)
shape$draw()
dev.off()
pix <- dev
}
return(pix)
}
#' @description
#' Extracts shapes from a pathway map image.
#' @param id A KEGG pathway ID.
#' @param color2ids A named list defining colors for entry IDs that are present
#' on the graph. The names of the list are standard color names. The values are
#' character vector of entry IDs.
#' @return A list of BiodbShape objects.
,extractPathwayMapShapes=function(id, color2ids) {
shapes <- list()
html <- private$getPathwayHtml(id)
for (color in names(color2ids)) {
for (id in color2ids[[color]]) {
# Escape special chars
eid <- gsub('\\.', '\\\\.', id)
# Look for lines containing shapes definition
# <area shape="circle" coords="336,170,4" href="/dbget-bin/www_bget?C00089"
# title="C00089 (Sucrose)" onmouseover="popupTimer("C00089",
# "C00089 (Sucrose)", "#ffffff")" onmouseout="hideMapTn()">
#
# <area shape="rect" coords="1015,505,1061,522"
# href="/dbget-bin/www_bget?K05992+K01208+3.2.1.133+R02112"
# title="K05992 (amyM), K01208 (cd), 3.2.1.133, R02112">
regex=paste0('shape="?([^" ]+)"?\\s+',
'coords="?([^" ]+)"?\\s+.+',
'title="([^"]+[ ,])?(', eid, ')[ ,][^"]*"')
g <- stringr::str_match_all(html, regex)[[1]]
if (nrow(g) > 0)
shapes <- private$extractShapes(shapes=shapes, g=g, color=color)
}
}
return(shapes)
}
),
private=list(
extractShapes=function(shapes, g, color) {
for (i in seq_len(nrow(g))) {
type <- g[i, 2]
c <- as.integer(strsplit(g[i, 3], ',')[[1]])
s <- switch(type,
rect=KeggRect$new(label=g[i, 5], color=color,
left=c[[1]], top=c[[2]], right=c[[3]],
bottom=c[[4]]),
circle=KeggCircle$new(label=g[i, 5], color=color,
x=c[[1]], y=c[[2]], r=c[[3]]),
NULL)
# Append new shape to list
if ( ! is.null(s)) {
is_new <- TRUE
for (shape in shapes) {
if (shape$equals(s)) {
is_new <- FALSE
break
}
}
if (is_new)
shapes <- c(shapes, list(s))
}
}
return(shapes)
}
,getPathwayHtml=function(id) {
# Extract pathway number
path_idx <- sub('^[^0-9]+', '', id)
# Build Request
url=biodb::BiodbUrl$new(url=c(self$getPropValSlot('urls', 'base.url'), 'kegg-bin',
'show_pathway'),
params=c(org_name='map', mapno=path_idx,
mapscale='1.0', show_description='hide'))
request=self$makeRequest(url=url)
# Send request and get HTML page
html=self$getBiodb()$getRequestScheduler()$sendRequest(request)
return(html)
},
getPathwayImage=function(id) {
html <- private$getPathwayHtml(id)
path_idx <- sub('^[^0-9]+', '', id)
cache <- self$getBiodb()$getPersistentCache()
img_filename <- paste0('pathwaymap-', path_idx)
cid <- self$getCacheId()
if ( ! cache$fileExists(cid, img_filename, 'png')) {
img_url <- stringr::str_match(html,
'src="([^"]+)"(\\s+.*)?\\s+(name|id)="pathwayimage"')
if (is.na(img_url[1, 1]))
biodb::error0('Impossible to find pathway image path inside',
' HTML page for pathway ID ', id, '.')
u <- self$getPropValSlot('urls', 'base.url')
img_url <- biodb::BiodbUrl$new(url=c(u, img_url[1, 2]))
tmp_file <- file.path(cache$getTmpFolderPath(),
paste(img_filename, 'png', sep='.'))
self$getBiodb()$getRequestScheduler()$downloadFile(url=img_url,
dest.file=tmp_file)
cache$addFilesToCache(tmp_file, cache.id=cid, name=img_filename,
ext='png', action='move')
}
img_file <- cache$getFilePath(cid, img_filename, 'png')
return(magick::image_read(img_file))
},
buildReactionGraph=function(react, directed) {
graph <- NULL
# Get substrates and products
subst=react$getFieldValue('substrates')
prod=react$getFieldValue('products')
if ( ! is.null(subst) && ! is.null(prod)) {
# Create compound vertices
ids <- c(subst, prod)
vert=data.frame(name=ids, type='compound', id=ids)
# Create reaction edge
rid=react$getFieldValue('accession')
vert=rbind(vert,
data.frame(name=rid, type='reaction', id=rid))
edg=rbind(data.frame(from=subst, to=rid),
data.frame(from=rid, to=prod))
# Create reverse reaction edge
if (directed) {
rvid=paste(rid, 'rev', sep='_')
vert=rbind(vert,
data.frame(name=rvid, type='reaction',
id=rid))
edg=rbind(edg, data.frame(from=prod, to=rvid),
data.frame(from=rvid, to=subst))
}
graph=list(edges=edg, vertices=vert)
}
return(graph)
}
))
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.