#' Find geodesic distance along neuronal tree from the soma to every synapse
#'
#' @description Find the geodesic distance (i.e. path length along) the neuron
#' from the the root (usually soma) to all synapses or other selected nodes.
#' @param x A \code{\link{neuron}}
#' @param root The index of the node to use as the root for the calculation.
#' Defaults to the soma.
#' @param nodes Optional argument specifying to which nodes you want to measure
#' distances. By default this will be all connectors in the neuron, but you
#' can also specify all nodes, or pass in a any data frame whose first columm
#' corresponds to (a subset) of the node identifiers of the neuron.
#' @param rval What kind of object to return
#' @return A vector of distances, a data.frame or a \code{\link{neuron}} object
#' with an extra column in the connectors data.frame entitled
#' \code{rootdistance}.
#' @export
#'
#' @examples
#' # nb pick first neuron out of neuron list returned by neuprint_read_neurons
#' dl4=neuprint_read_neurons('DL4')[[1]]
#' hist(geodesic_distances(dl4), br=100)
#' df=geodesic_distances(dl4, rval='dataframe')
#' # the correction factor of 125 scales to microns
#' plot(dl4/125, col='black', WithNodes=F)
#' points(xyzmatrix(df)[,c(1,2)]/125, col=topo.colors(10)[cut(df$rootdistance, 10)], pch=19)
#' # plot the soma position
#' points(xyzmatrix(dl4)[rootpoints(dl4),,drop=FALSE]/125, col='red', pch=19)
geodesic_distances <- function(x, root=rootpoints(x),
nodes=c("synapses", "nodes"),
rval=c("distance", "dataframe", 'neuron')) {
rval=match.arg(rval)
if(is.character(nodes)) {
nodes=match.arg(nodes)
nodes <- if(nodes=="synapses") x$connectors else x$d
}
if(is.null(nodes))
return(NULL)
if(!is.data.frame(nodes))
stop("I cannot understand the nodes argument. See docs for help!")
g=as.ngraph(x, weights=TRUE)
# Some nodes may make multiple connections, igraph::distances won't cope
havedups=any(duplicated(nodes[[1]]))
tns <- if(havedups) unique(nodes[[1]]) else nodes[[1]]
# nb converting the to nodes to character handles both the cases where they are
# 64 bit ids (typical of catmaid neurons) and the case where they are indices
# into the point array (typical of neuprint neurons)
dd=igraph::distances(g, v=root, to=as.character(tns), mode='all')
if(havedups) {
# re-expand to match the incoming data.frame of connectors
dd=dd[match(nodes[[1]], tns)]
}
if(rval=='distance')
return(dd)
nodes$rootdistance=dd
if(rval=='dataframe') nodes else {x$connectors=nodes; x}
}
@dokato I forgot to mention this issue which includes a specimen geodesic_distances function that I have been meaning to add to nat. You might want to try this out.
See https://groups.google.com/forum/#!topic/nat-user/3oozKMYg6zY