mirror of
https://github.com/haniffalab/scRNA-seq_analysis.git
synced 2024-10-23 08:29:24 -07:00
scRNA-seq_analysis
This commit is contained in:
commit
82cc2d191e
188 changed files with 146184 additions and 0 deletions
93
tools/AGA/AGA_from_Seurat.py
Executable file
93
tools/AGA/AGA_from_Seurat.py
Executable file
|
|
@ -0,0 +1,93 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Wed Aug 1 15:18:12 2018
|
||||
|
||||
@author: doru
|
||||
"""
|
||||
|
||||
import sys
|
||||
args = sys.argv
|
||||
CWD = args[1]
|
||||
#split_cat = args[2]
|
||||
from os import chdir
|
||||
chdir(CWD)
|
||||
|
||||
import matplotlib; matplotlib.use('Agg');
|
||||
import scanpy.api as sc;
|
||||
import pandas as pd
|
||||
from scipy.sparse import csr_matrix
|
||||
import numpy as np
|
||||
|
||||
sc.settings.verbosity = 3
|
||||
|
||||
scObj = sc.read("./material/raw_data.mtx", cache = False).T
|
||||
|
||||
# load gene names
|
||||
scObj.var_names = pd.read_csv("./material/genenames.csv").iloc[:, 1]
|
||||
|
||||
# load cell names
|
||||
scObj.obs_names = pd.read_csv("./material/cellnames.csv").iloc[:, 1]
|
||||
|
||||
# filter out genes present in less than 3 cells
|
||||
sc.pp.filter_genes(scObj, min_cells=3)
|
||||
|
||||
# log-normalize the data
|
||||
scObj.raw = sc.pp.log1p(scObj, copy=True)
|
||||
sc.pp.normalize_per_cell(scObj, counts_per_cell_after=1e4)
|
||||
|
||||
# variable genes
|
||||
filter_result = sc.pp.filter_genes_dispersion(
|
||||
scObj.X, min_mean=0.0125, max_mean=3, min_disp=0.5)
|
||||
# subset data on variable genes
|
||||
scObj = scObj[:, filter_result.gene_subset]
|
||||
# not sure?
|
||||
sc.pp.log1p(scObj)
|
||||
|
||||
# scale the data
|
||||
sc.pp.scale(scObj, max_value=10)
|
||||
|
||||
# run pca
|
||||
sc.tl.pca(scObj)
|
||||
|
||||
# compunte neighborhood graph
|
||||
sc.pp.neighbors(scObj, n_neighbors = 15, n_pcs = 20, knn = True, random_state = 10, method = "gauss")
|
||||
|
||||
# add cell labels
|
||||
cell_labels = pd.read_csv("./material/cell_labels.csv", index_col = 0)
|
||||
|
||||
scObj.obs["cell_labels"] = cell_labels
|
||||
|
||||
# run aga
|
||||
sc.tl.paga(scObj, groups = "cell_labels")
|
||||
|
||||
# save the scipy paga graph to disk for comparison to the plot generated by ggplot - for trouble shooting
|
||||
|
||||
sc.pl.paga(scObj, save = "_ugly_scanpy_plot.pdf", show = True, edge_width_scale = .4, solid_edges = "connectivities", layout="fa")
|
||||
|
||||
#sc.pl.paga(scObj, save = "ugly_scanpy_plot.pdf", show = False, edge_width_scale = .4, solid_edges = "connectivities",
|
||||
# layout = "fa")
|
||||
|
||||
#sc.pl.paga(scObj, save = "_ugly_scanpy_plot.pdf", show = True, edge_width_scale = .4, solid_edges = "connectivities", layout = "fa")
|
||||
|
||||
#sc.pl.paga(scObj, save = "{split_cat}_ugly_scanpy_plot.pdf".format(split_cat=split_cat), show = True, edge_width_scale = .4, solid_edges = "connectivities", layout = "fa")
|
||||
|
||||
#sc.pl.paga(scObj, save = "{split_cat}_ugly_scanpy_plot.pdf".format(split_cat=split_cat),
|
||||
# show = True, edge_width_scale = .4, solid_edges = "connectivities",
|
||||
# layout = "fa") # layout = "fa"
|
||||
|
||||
# prepare the output and save it to disk
|
||||
cell_cats = list(scObj.obs["cell_labels"].cat.categories)
|
||||
population_size = cell_labels["Labels"].value_counts()
|
||||
population_size = population_size[cell_cats].values
|
||||
|
||||
connectivities = np.array(csr_matrix.todense(scObj.uns["paga"]["connectivities"]), dtype = "float64")
|
||||
connectivities[connectivities < .05] = 0.0
|
||||
|
||||
#connectivities[connectivities < .1] = 0.0
|
||||
connectivities = pd.DataFrame(connectivities, columns = cell_cats, index = cell_cats)
|
||||
connectivities.to_csv("./AGA_folder/connectivities.csv", index = True, header = True)
|
||||
|
||||
coordinates = pd.DataFrame(scObj.uns["paga"]["pos"], columns = ["X", "Y"], index = cell_cats)
|
||||
coordinates["Size"] = pd.Series(population_size, coordinates.index)
|
||||
coordinates.to_csv("AGA_folder/coordinates.csv", index = True, header = True)
|
||||
354
tools/bunddle_utils.R
Executable file
354
tools/bunddle_utils.R
Executable file
|
|
@ -0,0 +1,354 @@
|
|||
tool_addr = "../../tools"
|
||||
python.addr = "python3.6"
|
||||
|
||||
# a function to compute force-directed graph; return coordinates as a data frame
|
||||
# arguments:
|
||||
# pca.df: pca data as a data frame
|
||||
# snn : a nearest-neighbor graph as a sparse data matrix
|
||||
runFDG = function(pca.df, snn, iterations = 600, tool_addr, python.addr){
|
||||
current.wd = getwd()
|
||||
setwd(file.path(tool_addr, "force_abstract_graph_2D"))
|
||||
# generate unique name for pca data file
|
||||
pca.data.fname = paste(sample(LETTERS, 20, TRUE), collapse = "")
|
||||
pca.data.fname = paste(pca.data.fname, ".csv", sep = "")
|
||||
# generate unique name for snn file
|
||||
snn.fname = paste(sample(LETTERS, 20, TRUE), collapse = "")
|
||||
snn.fname = paste(snn.fname, ".smm", sep = "")
|
||||
# generate unique name for fdg coordinates
|
||||
fdg.coordinates.fname = paste(sample(LETTERS, 20, TRUE), collapse = "")
|
||||
fdg.coordinates.fname = paste(fdg.coordinates.fname, ".csv", sep = "")
|
||||
write.csv(pca.df, pca.data.fname)
|
||||
writeMM(obj=snn, file=snn.fname)
|
||||
command = gsub(pattern="ITER", replacement=as.character(iterations), paste(python.addr, "./make_fdg.py ITER", sep = " "))
|
||||
command = paste(command, paste(c(pca.data.fname, snn.fname, fdg.coordinates.fname), collapse = " "), sep = " ")
|
||||
system(command, wait = T)
|
||||
fdg_coordinates = read.csv(fdg.coordinates.fname, header = FALSE)
|
||||
colnames(fdg_coordinates) = c("X", "Y")
|
||||
rownames(fdg_coordinates) = rownames(pca.df)
|
||||
file.remove(c(pca.data.fname, snn.fname, fdg.coordinates.fname))
|
||||
setwd(current.wd)
|
||||
return(fdg_coordinates)
|
||||
}
|
||||
|
||||
# a function to perform UMAP on a seurat object using PCA coordinates
|
||||
RunUMAP = function(pca.df, tool_addr, python.addr){
|
||||
current.wd = getwd()
|
||||
setwd(file.path(tool_addr, "umap"))
|
||||
print("writting pca data to disk...")
|
||||
# generate unique name for pca data file
|
||||
pca.data.fname = paste(sample(LETTERS, 20, TRUE), collapse = "")
|
||||
pca.data.fname = paste(pca.data.fname, ".csv", sep = "")
|
||||
# generate unique name for umap coordinates data file
|
||||
umap.coordinates.fname = paste(sample(LETTERS, 20, TRUE), collapse = "")
|
||||
umap.coordinates.fname = paste(umap.coordinates.fname, ".csv", sep = "")
|
||||
write.csv(pca.df, pca.data.fname)
|
||||
print("perfoming UMAP")
|
||||
command = paste(python.addr, "umap_compute.py", sep = " ")
|
||||
command = paste(command, pca.data.fname, sep = ' ')
|
||||
command = paste(command, umap.coordinates.fname, ' ')
|
||||
system(command, wait = T)
|
||||
print("Reading results...")
|
||||
umap.coordinates = read.csv(umap.coordinates.fname, stringsAsFactors = F)
|
||||
file.remove(c(pca.data.fname, umap.coordinates.fname))
|
||||
setwd(current.wd)
|
||||
umap.coordinates = umap.coordinates[, c("UMAPx", "UMAPy")]
|
||||
return(umap.coordinates)
|
||||
}
|
||||
|
||||
# a function that load a SVM models and make predictions like cell types or doublet.singlets
|
||||
Apply_Classifier_On_Seurat_Object = function(seurat.obj, classifier.fname, tool_addr, python.addr){
|
||||
current.wd = getwd()
|
||||
setwd(file.path(tool_addr, 'predict_by_classifier'))
|
||||
predictor.addr = file.path("../../resources", classifier.fname, sep = "")
|
||||
print(predictor.addr)
|
||||
if(!dir.exists(predictor.addr)){
|
||||
print("classifier does not exists")
|
||||
available.classifiers = list.dirs("../../resources", full.names=F)
|
||||
available.classifiers = available.classifiers[grepl("classifier_", available.classifiers)]
|
||||
print(paste("Available doublets identifiers: ", paste(available.classifiers, collapse = ", "), sep = ""))
|
||||
setwd(current.wd)
|
||||
return(NULL)
|
||||
}
|
||||
tryCatch({
|
||||
OK = FALSE
|
||||
print("reading feature genes ...")
|
||||
feature.genes = readRDS(file.path(predictor.addr, "feature_genes.RDS"))
|
||||
features.present = feature.genes[feature.genes %in% rownames(seurat.obj@data)]
|
||||
features.not.present = feature.genes[!(feature.genes %in% rownames(seurat.obj@data))]
|
||||
expr.data = as.data.frame(t(as.matrix(seurat.obj@data[features.present, ])))
|
||||
if (length(features.not.present) > 0){
|
||||
zeros.data = as.data.frame(t(as.matrix(seurat.obj@data[1:length(features.not.present),])))
|
||||
zeros.data[] = 0
|
||||
colnames(zeros.data) = features.not.present
|
||||
expr.data=cbind(expr.data, zeros.data)
|
||||
expr.data = expr.data[, feature.genes]
|
||||
}
|
||||
print("Writting data to disk ... ")
|
||||
data.fname = paste(sample(LETTERS, 20, TRUE), collapse = "")
|
||||
data.fname = paste(data.fname, ".csv", sep = '')
|
||||
write.csv(expr.data, data.fname)
|
||||
|
||||
print(sprintf("Dims are: %s", dim(expr.data)))
|
||||
print("Copying pickle file ... ")
|
||||
model.fname = paste(sample(LETTERS, 20, T), collapse = '')
|
||||
model.fname = paste(model.fname, '.pickle', sep = "")
|
||||
file.copy(from=file.path(predictor.addr, "model.pickle"), to=model.fname)
|
||||
|
||||
pca.fname = paste(sample(LETTERS, 20, T), collapse = '')
|
||||
pca.fname = paste(pca.fname, '.pickle', sep = "")
|
||||
file.copy(from=file.path(predictor.addr, "pca.pickle"), to=pca.fname)
|
||||
|
||||
print("Running classifier in Python ... ")
|
||||
predictions.fname = paste(sample(LETTERS, 20, TRUE), collapse = "")
|
||||
predictions.fname = paste(predictions.fname, ".csv", sep = "")
|
||||
command = paste(python.addr, "predict.py", sep = " ")
|
||||
command = paste(command, paste(c(model.fname, data.fname, predictions.fname, pca.fname), collapse = " "), sep = " ")
|
||||
system(command, wait = T)
|
||||
predictions = read.csv(predictions.fname)
|
||||
OK = TRUE
|
||||
},
|
||||
warning = function(warning_conditions){print("")},
|
||||
error = function(error_condition){
|
||||
print("Errors occured. Cleaning up and then returning NULL ...")
|
||||
file.remove(c(model.fname, data.fname, predictions.fname, pca.fname))
|
||||
setwd(current.wd)
|
||||
})
|
||||
if(OK){
|
||||
if(length(features.not.present) > 0){
|
||||
print('Everything went well except the fact that some of the features genes were not present in the data. These are:')
|
||||
print(paste(features.not.present, collapse = ", "))
|
||||
}else{
|
||||
print("Everything went smooth. Cleaning up and returning the predictions ... ")
|
||||
}
|
||||
file.remove(c(model.fname, data.fname, predictions.fname, pca.fname))
|
||||
setwd(current.wd)
|
||||
return(predictions$X0)
|
||||
}
|
||||
return(NULL)
|
||||
}
|
||||
|
||||
# a function that takes 3D coordinates (e.g. from diffusion map) and generates an
|
||||
# interactive html page
|
||||
make_3D_interactive_page = function(data_frame_3D, tool_addr, python.addr, save.to){
|
||||
data.frame.fname = paste(sample(LETTERS, 20, T), collapse = '')
|
||||
data.frame.fname = paste(data.frame.fname, ".csv", sep = "")
|
||||
save.to = file.path(getwd(), save.to)
|
||||
command = gsub(pattern="save.to", replacement=save.to, x=paste(python.addr, './html_WebGL_3D_viewer.py save.to', sep = " "))
|
||||
command = paste(command, data.frame.fname, sep = " ")
|
||||
current.wd = getwd()
|
||||
setwd(file.path(tool_addr, "interactive_3D_viewer"))
|
||||
write.csv(data_frame_3D, data.frame.fname, row.names = F)
|
||||
system(command, wait=T)
|
||||
file.remove(data.frame.fname)
|
||||
setwd(current.wd)
|
||||
}
|
||||
|
||||
# a function that takes 2D coordinates and generates an interactive html page
|
||||
make_2D_interactive_page = function(data_frame_2D, tool_addr, python.addr, save.to="./"){
|
||||
data.frame.fname = paste(sample(LETTERS, 20, T), collapse = '')
|
||||
data.frame.fname = paste(data.frame.fname, ".csv", sep = "")
|
||||
save.to = file.path(getwd(), save.to)
|
||||
command = gsub(pattern="save.to", replacement=save.to, x=paste(python.addr, './html_WebGL_2D_viewer.py save.to', sep = " "))
|
||||
command = paste(command, data.frame.fname, sep = " ")
|
||||
current.wd = getwd()
|
||||
setwd(file.path(tool_addr, "interactive_2D_viewer"))
|
||||
write.csv(data_frame_2D, data.frame.fname, row.names = F)
|
||||
system(command, wait=T)
|
||||
file.remove(data.frame.fname)
|
||||
setwd(current.wd)
|
||||
}
|
||||
|
||||
# a function that create interactic html pages to explore gene expression in data parsed by different categories
|
||||
create_gene_expression_viewer_apps = function(seurat.obj, dim.type = 'umap', save.to, tool_addr, python.addr, categories.colours=NA){
|
||||
categories = c("cell.labels", "fetal.ids", "sort.ids", "lanes", "stages", "gender", "doublets")
|
||||
if(is.na(categories.colours)){
|
||||
categories.colours = rep(NA, length(categories))
|
||||
}
|
||||
categories.data = as.data.frame(seurat.obj@meta.data[names(seurat.obj@ident), categories])
|
||||
for(j in 1:length(categories)){
|
||||
category = categories[j]
|
||||
category.colour.scheme = categories.colours[j]
|
||||
if (!is.na(category.colour.scheme)){
|
||||
category.colour.scheme = read.csv(category.colour.scheme)
|
||||
category.colour.scheme = mapvalues(x=categories.data[, category], from=as.vector(unique(category.colour.scheme$CellTypes)), to=as.vector(unique(category.colour.scheme$Colours)))
|
||||
}else{
|
||||
category.colour.scheme = sample(colorRampPalette(brewer.pal(12, "Paired"))(length(as.vector(unique(categories.data[, category])))))
|
||||
category.colour.scheme = mapvalues(x=categories.data[, category], from=as.vector(unique(categories.data[, category])), to=category.colour.scheme)
|
||||
}
|
||||
category = paste(category, "colours", sep = "_")
|
||||
categories.data[, category] = category.colour.scheme
|
||||
}
|
||||
eval(parse(text = sprintf("dim.data = seurat.obj@dr$%s@cell.embeddings[names(seurat.obj@ident), 1:2]", dim.type)))
|
||||
n.categories = length(categories)
|
||||
|
||||
genes.by.file = round( 20 * 26149846 / ncol(seurat.obj@data))
|
||||
approx.no.of.files = round(nrow(seurat.obj@data) / genes.by.file)
|
||||
gene.names = sort(rownames(seurat.obj@data))
|
||||
gene.names = sort(seurat.obj@var.genes)
|
||||
gene.splits = split(gene.names, sort(1:length(gene.names) %% approx.no.of.files))
|
||||
curdir = getwd()
|
||||
unlink(x=save.to, recursive=T)
|
||||
dir.create(save.to)
|
||||
setwd(file.path(tool_addr, 'gene_expression_viewer_apps'))
|
||||
folder_name = paste(sample(LETTERS, 20, T), collapse = "")
|
||||
dir.create(folder_name)
|
||||
for (l in 1:length(gene.splits)){
|
||||
gene.split = unlist(gene.splits[[l]])
|
||||
expression.data = as.data.frame(as.matrix(t(seurat.obj@data[gene.split, names(seurat.obj@ident)])))
|
||||
expression.data = cbind(dim.data, categories.data, expression.data)
|
||||
first_gene = gene.split[1]
|
||||
last_gene = gene.split[length(gene.split)]
|
||||
print(sprintf("Creating app for: %s", paste(c(first_gene, "to", last_gene), collapse = "_")))
|
||||
expression.data.fname = paste(c(first_gene, "to", last_gene), collapse = "_")
|
||||
expression.data.fname = paste(expression.data.fname, ".csv", sep = "")
|
||||
expression.data.fname = file.path(folder_name, expression.data.fname)
|
||||
save_to = paste(c(first_gene, "to", last_gene), collapse = "_")
|
||||
save_to = paste(save_to, ".html", sep = "")
|
||||
save_to = file.path(file.path(curdir, save.to), save_to)
|
||||
command = sprintf("%s gene_expression_viewer_apps.py %s %s %s", python.addr, save_to, expression.data.fname, n.categories)
|
||||
write.csv(expression.data, expression.data.fname, row.names = F)
|
||||
system(command, wait = T)
|
||||
}
|
||||
unlink(x=folder_name, recursive=T, force=T)
|
||||
setwd(curdir)
|
||||
}
|
||||
|
||||
# a plotting function for indexed legend
|
||||
plot.indexed.legend = function(label.vector, color.vector, ncols = 2, left.limit = 3.4, symbol.size = 8, text.size = 10, padH = 1, padV = 1, padRight = 0){
|
||||
if (length(label.vector) != length(color.vector)){
|
||||
stop("number of labels is different from number colors\nAdvice: learn to count!")
|
||||
}
|
||||
if (length(ncol) > length(label.vector)){
|
||||
stop("You cannot have more columns than labels\nSolution: Learn to count")
|
||||
}
|
||||
indices.vector = 1:length(label.vector)
|
||||
label.no = length(label.vector)
|
||||
nrows = ceiling(label.no / ncols)
|
||||
legend.frame = data.frame(X = rep(0, label.no), Y = rep(0, label.no), CS = color.vector, Txt = label.vector)
|
||||
legend.frame$X = rep(1:ncols, each=nrows)[1:nrow(legend.frame)]
|
||||
legend.frame$Y = rep(nrows:1, times = ncols)[1:nrow(legend.frame)]
|
||||
Xrange = range(legend.frame$X)
|
||||
Yrange = range(legend.frame$Y)
|
||||
plot.obj = ggplot(data = legend.frame, aes(x = X, y = Y))
|
||||
plot.obj = plot.obj + geom_point(size = symbol.size, colour = color.vector)
|
||||
plot.obj = plot.obj + scale_x_continuous(limits = c(Xrange[1] - padRight, Xrange[2] + padH))
|
||||
plot.obj = plot.obj + scale_y_continuous(limits = c(Yrange[1] - padV, Yrange[2] + padV))
|
||||
plot.obj = plot.obj + theme_void()
|
||||
|
||||
plot.obj = plot.obj + annotate("text", x=legend.frame$X, y = legend.frame$Y, label = indices.vector, size = text.size)
|
||||
plot.obj = plot.obj + annotate("text", x=legend.frame$X+.1, y = legend.frame$Y, label=legend.frame$Txt, hjust = 0, size = text.size)
|
||||
return(plot.obj)
|
||||
}
|
||||
|
||||
# plotting function for dimensionaly-reduced data to label population by a round indexed label
|
||||
dr.plot = function(point.labels, dr1, dr2, dr1.name, dr2.name, no.legend = F, plt.lb.sz = 5, txt.lb.size = 3, pt.size = .2, random_state = 2, use.cols = NULL, use.labels = NULL, limits = NULL, annotate.plot = T, index.map = NA){
|
||||
if(!is.na(overlay.data)){
|
||||
df.dr = data.frame("Cell Labels" = point.labels, DR1 = dr1, DR2 = dr2, overlay.data=factor(df$overlay.data))
|
||||
}
|
||||
else{
|
||||
df.dr = data.frame("Cell Labels" = point.labels, DR1 = dr1, DR2 = dr2)
|
||||
}
|
||||
if(is.null(use.labels)){
|
||||
p.labels = sort(unique(as.vector(point.labels)))
|
||||
}
|
||||
else{
|
||||
p.labels = use.labels
|
||||
}
|
||||
df.dr$Cell.Labels = factor(df.dr$Cell.Labels, levels=p.labels)
|
||||
p.labels.medians = aggregate(df.dr[, 2:3], list(df.dr$Cell.Labels), median)
|
||||
df.dr$Cell.Labels = mapvalues(x = df.dr$Cell.Labels, from = p.labels, to = paste(1:length(p.labels), p.labels, sep = " "))
|
||||
if(is.null(use.cols)){
|
||||
set.seed(random_state)
|
||||
plt.colours = sample(colorRampPalette(brewer.pal(12, "Paired"))(length(p.labels)))
|
||||
}else{
|
||||
plt.colours = use.cols
|
||||
}
|
||||
if(is.na(index.map)){
|
||||
index.map = 1:length(p.labels)
|
||||
}
|
||||
if(!is.na(overlay.data)){
|
||||
plot.obj = ggplot(data = df.dr, aes(x = DR1, y = DR2, color = Cell.Labels, shape = factor(overlay.data, levels=overlay.data.ordered)))
|
||||
print("levels=overlay.data.ordered")
|
||||
print(overlay.data.ordered)
|
||||
}
|
||||
else{
|
||||
plot.obj = ggplot(data = df.dr, aes(x = DR1, y = DR2, color = Cell.Labels))
|
||||
}
|
||||
# this line should give different sizes for different values in overlay.data metadata column
|
||||
if(!is.na(overlay.data)){
|
||||
plot.obj = plot.obj + geom_point(size=pt.size)
|
||||
plot.obj = plot.obj + geom_point(data = subset(df.dr, overlay.data == overlay.data.ordered[2]))
|
||||
print("overlay.data.ordered[2]")
|
||||
print(overlay.data.ordered[2])
|
||||
}
|
||||
else{
|
||||
plot.obj = plot.obj + geom_point(size=pt.size)
|
||||
}
|
||||
plot.obj = plot.obj + scale_color_manual(values=plt.colours)
|
||||
if(annotate.plot){
|
||||
if(!is.na(overlay.data)){
|
||||
plot.obj = plot.obj + geom_point(data=p.labels.medians,aes(x = DR1, y = DR2), colour = "gray", size = plt.lb.sz, fill = plt.colours, alpha = .5, pch = 21, shape=factor(df.dr$overlay.data, levels=overlay.data.ordered))
|
||||
}
|
||||
else{
|
||||
plot.obj = plot.obj + geom_point(data=p.labels.medians,aes(x = DR1, y = DR2), colour = "gray", size = plt.lb.sz, fill = plt.colours, alpha = .5, pch = 21)
|
||||
}
|
||||
plot.obj = plot.obj + annotate("text", x=p.labels.medians$DR1, y = p.labels.medians$DR2, label = index.map, size = txt.lb.size)
|
||||
|
||||
}
|
||||
if (no.legend){
|
||||
plot.obj = plot.obj + theme(legend.position="none")
|
||||
}else{
|
||||
plot.obj = plot.obj + guides(color = guide_legend(override.aes = list(size=5)))
|
||||
}
|
||||
plot.obj = plot.obj + xlab(dr1.name) + ylab(dr2.name)
|
||||
if(!is.null(limits)){
|
||||
X0 = limits[1]; X1 = limits[2]; Y0 = limits[3]; Y1 = limits[4];
|
||||
plot.obj = plot.obj + scale_x_continuous(limits = c(X0, X1))
|
||||
plot.obj = plot.obj + scale_y_continuous(limits = c(Y0, Y1))
|
||||
}
|
||||
return(plot.obj)
|
||||
}
|
||||
|
||||
|
||||
# plotting function for dimensionaly-reduced data to label population by a round indexed label
|
||||
dr.plot.numerical = function(point.labels, dr1, dr2, dr1.name, dr2.name, no.legend = F, plt.lb.sz = 5, txt.lb.size = 3, pt.size = .2, random_state = 2, use.cols = NULL, use.labels = NULL, limits = NULL, annotate.plot = T, index.map = NA){
|
||||
df.dr = data.frame("Cell Labels" = point.labels, DR1 = dr1, DR2 = dr2)
|
||||
if(is.null(use.labels)){
|
||||
p.labels = sort(unique(as.vector(point.labels)))
|
||||
}
|
||||
else{
|
||||
p.labels = use.labels
|
||||
}
|
||||
df.dr$Cell.Labels = factor(df.dr$Cell.Labels, levels=p.labels)
|
||||
p.labels.medians = aggregate(df.dr[, 2:3], list(df.dr$Cell.Labels), median)
|
||||
#df.dr$Cell.Labels = mapvalues(x = df.dr$Cell.Labels, from = p.labels, to = paste(1:length(p.labels), p.labels, sep = " "))
|
||||
if(is.null(use.cols)){
|
||||
set.seed(random_state)
|
||||
plt.colours = sample(colorRampPalette(brewer.pal(12, "Paired"))(length(p.labels)))
|
||||
}else{
|
||||
plt.colours = use.cols
|
||||
}
|
||||
if(is.na(index.map)){
|
||||
#index.map = 1:length(p.labels)
|
||||
index.map = p.labels
|
||||
}
|
||||
plot.obj = ggplot(data = df.dr, aes(x = DR1, y = DR2, color = Cell.Labels))
|
||||
plot.obj = plot.obj + geom_point(size = pt.size)
|
||||
plot.obj = plot.obj + scale_color_manual(values=plt.colours)
|
||||
if(annotate.plot){
|
||||
plot.obj = plot.obj + geom_point(data=p.labels.medians,aes(x = DR1, y = DR2), colour = "gray", size = plt.lb.sz, fill = plt.colours, alpha = .5, pch = 21)
|
||||
plot.obj = plot.obj + annotate("text", x=p.labels.medians$DR1, y = p.labels.medians$DR2, label = index.map, size = txt.lb.size)
|
||||
}
|
||||
if (no.legend){
|
||||
plot.obj = plot.obj + theme(legend.position="none")
|
||||
}else{
|
||||
plot.obj = plot.obj + guides(color = guide_legend(override.aes = list(size=5)))
|
||||
}
|
||||
plot.obj = plot.obj + xlab(dr1.name) + ylab(dr2.name)
|
||||
if(!is.null(limits)){
|
||||
X0 = limits[1]; X1 = limits[2]; Y0 = limits[3]; Y1 = limits[4];
|
||||
plot.obj = plot.obj + scale_x_continuous(limits = c(X0, X1))
|
||||
plot.obj = plot.obj + scale_y_continuous(limits = c(Y0, Y1))
|
||||
}
|
||||
return(plot.obj)
|
||||
}
|
||||
261
tools/color_management.html
Executable file
261
tools/color_management.html
Executable file
|
|
@ -0,0 +1,261 @@
|
|||
<!doctype html>
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<title>Color Picker</title>
|
||||
<meta name="description" content="interactive page for color picker">
|
||||
<meta name="author" content="Dorin-Mirel Popescu">
|
||||
<style>
|
||||
#div_cats{
|
||||
overflow-y: scroll;
|
||||
height: 400px;
|
||||
}
|
||||
#instructionBtn {
|
||||
margin-bottom: 1em;
|
||||
font-size: 1em;
|
||||
background-color: #00bfff;
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 10px 10px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
#end_div {
|
||||
margin-bottom: 3em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h2>Colors Management</h2>
|
||||
<input id = 'instructionBtn' type = 'button' value = 'Show description and instructions' onclick = 'showHideInstructions(value)' />
|
||||
<div id = 'instructionBoard'>
|
||||
<div><b>Author:</b> Dorin-Mirel Popescu</div>
|
||||
<div><b>Description: </b>This is a tool used for generating color mappings. The output can be saved as a csv file that maps colours to categories.</div>
|
||||
<div>
|
||||
<b>Instructions:</b>
|
||||
<ul>
|
||||
<li>Enter a number in the text area right to "Number of categories:"</li>
|
||||
<li>Press the button "Make". This will generate coloured dots on the plot at right. For each dot there is a corresponding control panel in the column at left</li>
|
||||
<li>Each dot and its menu are linked by name. At generation names have the format "Name indes" (e.g. "Name 0", "Name 1")</li>
|
||||
<li>Names can be edited. Any change will update the colour area</li>
|
||||
<li>The colour of each category can be change by the 3 sliders in each colour menu</li>
|
||||
<li>The sliders are "H" (for hue), "S" (for saturation) and L ('light')</li>
|
||||
<li>The colour key can be saved to a csv file by pressing the button "Save color key as csv file"</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id = 'end_div'><hr/></div>
|
||||
</div>
|
||||
|
||||
<table border='1'>
|
||||
<tr>
|
||||
<td>
|
||||
Number of categories: <input type = 'text' size = '2' id='noOfCats'> <input type ='button' value = 'Make' id='makeButton' onclick='makeCategories()'>
|
||||
</td>
|
||||
<td>
|
||||
<input type='button' value='Save color key as csv file' disabled id='saveAsBttn' onclick='saveColorKey()'>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: left;vertical-align: top;">
|
||||
<div id='div_cats'></div>
|
||||
</td>
|
||||
<td>
|
||||
<div>
|
||||
<label for='white_bg_input'>White background</label><input type='radio' id='white_bg_input' name='bg_color_input' value='white' checked onchange="setBG(value)">
|
||||
<label for='black_bg_input'>Black background</label><input type='radio' id='black_bg_input' name='bg_color_input' value='black' onchange="setBG(value)"></br>
|
||||
<canvas width='640' height='400' id='canvas'></canvas>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<script type="text/javascript">
|
||||
var canvas = document.getElementById('canvas'),
|
||||
context = canvas.getContext('2d'),
|
||||
makeButton = document.getElementById('makeButton'),
|
||||
noOfCats = document.getElementById('noOfCats'),
|
||||
div_cats = document.getElementById('div_cats'),
|
||||
saveAsBttn = document.getElementById('saveAsBttn'),
|
||||
instructionBoard = document.getElementById('instructionBoard'),
|
||||
categories = [],
|
||||
categories_name = [],
|
||||
categories_coordinates = [],
|
||||
white_bg = true;
|
||||
context.textAlign = 'center'
|
||||
context.textBaseline = 'middle'
|
||||
instructionBoard.style.display = 'none'
|
||||
|
||||
// function to show/hide description and instructions
|
||||
function showHideInstructions(val){
|
||||
if (val == 'Show description and instructions'){
|
||||
instructionBtn.value = 'Hide description and instructions'
|
||||
instructionBoard.style.display = 'block'
|
||||
}else{
|
||||
instructionBtn.value = 'Show description and instructions'
|
||||
instructionBoard.style.display = 'none'
|
||||
}
|
||||
}
|
||||
|
||||
function saveColorKey(){
|
||||
var num_items = categories.length,
|
||||
content = "";
|
||||
for(i=0;i<num_items;i++){
|
||||
var hsl = categories[i]
|
||||
h = hsl[0],
|
||||
s = hsl[1],
|
||||
l = hsl[2],
|
||||
rgb = hslToHex(h, s, l),
|
||||
category_name = categories_name[i]
|
||||
content = content + category_name + "," + rgb + "\n"
|
||||
}
|
||||
uriContent = "data:application/octet-stream," + encodeURIComponent(content)
|
||||
var fname = prompt("Please enter file name (must end with .csv otherwise ... best of luck!");
|
||||
saveAs(uriContent, fname)
|
||||
}
|
||||
|
||||
function saveAs(uri, filename) {
|
||||
var link = document.createElement('a');
|
||||
if (typeof link.download === 'string') {
|
||||
document.body.appendChild(link); // Firefox requires the link to be in the body
|
||||
link.download = filename;
|
||||
link.href = uri;
|
||||
link.click();
|
||||
document.body.removeChild(link); // remove the link when done
|
||||
} else {
|
||||
location.replace(uri);
|
||||
}
|
||||
}
|
||||
|
||||
function hslToHex(h, s, l) {
|
||||
h /= 360;
|
||||
s /= 100;
|
||||
l /= 100;
|
||||
let r, g, b;
|
||||
if (s === 0) {
|
||||
r = g = b = l; // achromatic
|
||||
} else {
|
||||
const hue2rgb = (p, q, t) => {
|
||||
if (t < 0) t += 1;
|
||||
if (t > 1) t -= 1;
|
||||
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
||||
if (t < 1 / 2) return q;
|
||||
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
||||
return p;
|
||||
};
|
||||
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
const p = 2 * l - q;
|
||||
r = hue2rgb(p, q, h + 1 / 3);
|
||||
g = hue2rgb(p, q, h);
|
||||
b = hue2rgb(p, q, h - 1 / 3);
|
||||
}
|
||||
const toHex = x => {
|
||||
const hex = Math.round(x * 255).toString(16);
|
||||
return hex.length === 1 ? '0' + hex : hex;
|
||||
};
|
||||
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
||||
}
|
||||
function setBG(value){
|
||||
white_bg = false
|
||||
console.log(white_bg)
|
||||
if (value == 'white'){
|
||||
white_bg = true
|
||||
}
|
||||
paintAll()
|
||||
}
|
||||
function paintAll(){
|
||||
context.fillStyle = '#000000';
|
||||
if(white_bg){
|
||||
context.fillStyle = '#FFFFFF';
|
||||
}
|
||||
context.fillRect(0, 0, canvas.width, canvas.height)
|
||||
var num_items = categories.length
|
||||
for(i=0;i<num_items;i++){
|
||||
var flagCanvas = div_cats.children[i].children[1].children[0]
|
||||
flagContext = flagCanvas.getContext('2d');
|
||||
hsl = categories[i],
|
||||
h = hsl[0],
|
||||
s = hsl[1],
|
||||
l = hsl[2],
|
||||
rgb = hslToHex(h, s, l),
|
||||
x = categories_coordinates[i][0],
|
||||
y = categories_coordinates[i][1],
|
||||
category_name = categories_name[i]
|
||||
flagContext.fillStyle = rgb
|
||||
flagContext.fillRect(0, 0, flagCanvas.width, flagCanvas.height)
|
||||
context.fillStyle = rgb
|
||||
context.beginPath()
|
||||
context.arc(x, y, 25, 0, 2*Math.PI, false)
|
||||
context.fill()
|
||||
context.closePath()
|
||||
context.fillStyle = '#FFFFFF';
|
||||
if(white_bg){
|
||||
context.fillStyle = '#000000';
|
||||
}
|
||||
context.fillText(category_name, x, y - 35)
|
||||
}
|
||||
}
|
||||
function updateHSL(){
|
||||
var num_items = categories.length
|
||||
for(i=0;i<num_items;i++){
|
||||
var inputs = div_cats.children[i].children[0],
|
||||
name_input = inputs.children[0],
|
||||
h_input = inputs.children[2],
|
||||
s_input = inputs.children[4],
|
||||
l_input = inputs.children[6],
|
||||
h = h_input.value,
|
||||
s = s_input.value,
|
||||
l = l_input.value;
|
||||
categories[i] = [h, s, l]
|
||||
categories_name[i] = name_input.value
|
||||
}
|
||||
paintAll()
|
||||
}
|
||||
function makeCategories(){
|
||||
var numOfCategories = parseInt(noOfCats.value)
|
||||
if(isNaN(numOfCategories)){
|
||||
div_cats.innerHTML = '<h3>please introduce an integer</h3>'
|
||||
saveAsBttn.disabled = true
|
||||
}else if (numOfCategories < 41){
|
||||
categories_name = []
|
||||
categories = []
|
||||
saveAsBttn.disabled = false
|
||||
div_cats.innerHTML = ""
|
||||
for (i=0;i<numOfCategories;i++){
|
||||
var h = parseInt(Math.random() * 359),
|
||||
s = parseInt(Math.random() * 100),
|
||||
l = parseInt(Math.random() * 100);
|
||||
categories[i] = [h, s, l]
|
||||
name_i = 'Name ' + i
|
||||
categories_name[i] = name_i
|
||||
y = 40 + Math.floor(i / 8) * 80
|
||||
x = 40 + (i % 8) * 80
|
||||
categories_coordinates[i] = [x,y]
|
||||
var innerDiv = document.createElement('div')
|
||||
leftDiv = document.createElement('div'),
|
||||
rightDiv = document.createElement('div');
|
||||
div_cats.appendChild(innerDiv)
|
||||
innerDiv.appendChild(leftDiv)
|
||||
innerDiv.appendChild(rightDiv)
|
||||
leftContent = '<input type="text" onchange = "updateHSL()" size = 35 value ="' + name_i + '"></br>'
|
||||
leftContent = leftContent + 'H <input type="range" onchange="updateHSL()" min="0" max="360" value="' + h + '"></br>'
|
||||
leftContent = leftContent + 'S <input type="range" onchange="updateHSL()" min="0" max="100" value="' + s + '"></br>'
|
||||
leftContent = leftContent + 'L <input type="range" onchange="updateHSL()" min="0" max="100" value="' + l + '">'
|
||||
leftDiv.innerHTML = leftContent
|
||||
rightContent = '<canvas width="80" height="80"></canvas>'
|
||||
rightDiv.innerHTML = rightContent
|
||||
leftDiv.style.float = "left"
|
||||
rightDiv.style.float = "right"
|
||||
}
|
||||
paintAll()
|
||||
}else{
|
||||
div_cats.innerHTML = '<h3>40 maximum categories permisible</h3>'
|
||||
saveAsBttn.disabled = true
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
33
tools/force_abstract_graph_2D/make_fdg.py
Executable file
33
tools/force_abstract_graph_2D/make_fdg.py
Executable file
|
|
@ -0,0 +1,33 @@
|
|||
import sys
|
||||
args = sys.argv
|
||||
n_iterations = int(args[1])
|
||||
pca_data_fname = args[2]
|
||||
snn_fname = args[3]
|
||||
fdg_coordinates_fname = args[4]
|
||||
|
||||
from fa2 import ForceAtlas2
|
||||
import pandas as pd
|
||||
from scipy.io import mmread
|
||||
import numpy as np
|
||||
|
||||
# load pca, SNN and label colours data
|
||||
# the first 2 PC form PCA are used as initial conditions
|
||||
# SNN is used for building the force directed graph
|
||||
pca_data = pd.read_csv(pca_data_fname, index_col = 0)
|
||||
snn = mmread(snn_fname)
|
||||
|
||||
# set initialposition as the first 2 PCs
|
||||
positions = pca_data.values[:, 0:2]
|
||||
|
||||
# initialize force directed graph class instance
|
||||
forceatlas2 = ForceAtlas2(outboundAttractionDistribution=False, linLogMode=False,
|
||||
adjustSizes=False, edgeWeightInfluence=1.0,
|
||||
jitterTolerance=1.0, barnesHutTheta = .8,
|
||||
barnesHutOptimize=True, multiThreaded=False,
|
||||
scalingRatio=2.0, strongGravityMode=True, gravity=1, verbose=True)
|
||||
|
||||
# run force directed graph; for each iterations generates the coordinates use din each frame
|
||||
coord = forceatlas2.forceatlas2(G = snn, pos = positions, iterations = n_iterations)
|
||||
coord = np.array(coord)
|
||||
|
||||
np.savetxt( fname = fdg_coordinates_fname, X = coord, delimiter = ",")
|
||||
629
tools/gene_expression_viewer_apps/gene_expression_viewer_apps.py
Executable file
629
tools/gene_expression_viewer_apps/gene_expression_viewer_apps.py
Executable file
|
|
@ -0,0 +1,629 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Fri Sep 7 11:42:51 2018
|
||||
|
||||
@author: doru
|
||||
"""
|
||||
|
||||
import sys
|
||||
args = sys.argv
|
||||
save_to = args[1]
|
||||
expression_data_fname = args[2]
|
||||
no_of_categories = int(args[3])
|
||||
|
||||
template_str = """
|
||||
<!doctype html>
|
||||
|
||||
<html lang='en'>
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
|
||||
<title>3D viewer</title>
|
||||
<meta name='description' content='The HTML5 Herald'>
|
||||
<meta name='author' content='Dorin-Mirel Popescu'>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<td align='left'>
|
||||
<form>
|
||||
<fieldset>
|
||||
<legend><b>Visualisation options</b></legend>
|
||||
<label for = 'particleSizeBar'>Particle size: </label>
|
||||
<input type='range' name = 'particleSizeBar' min = 1 max = 14 step=0.1 oninput='setParticleSize(value)' value = 2 /><br />
|
||||
|
||||
<label for = 'alphaInput'>Transparency: </label>
|
||||
<input type='range' name = 'alphaInput' min = 0 max = 1000 oninput='setAlpha(value)' value = 1000 /><br />
|
||||
|
||||
<label for = 'canvasSizeInput'>Canvas size: </label>
|
||||
<input type='range' name = 'canvasSizeInput' min = 200 max = 2000 oninput='setCanvasSize(value)' value = 500 /><br />
|
||||
|
||||
<label for = 'bgInput'>Dark background: </label>
|
||||
<input type='radio' name = 'bgInput' oninput='setBackground(value)' value = 'dark' />
|
||||
<label for = 'bgInput'>White background: </label>
|
||||
<input type='radio' name = 'bgInput' oninput='setBackground(value)' value = 'white' checked />
|
||||
<br />
|
||||
</fieldset>
|
||||
</form>
|
||||
</td>
|
||||
<td style='vertical-align: top' rowspan='2'>
|
||||
|
||||
<form>
|
||||
<fieldset>
|
||||
<legend><b>Colour by:</b></legend>
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<label for='colourType'><input type='radio' name='colourType' onchange='setColourBy(value)' value='gene_expression' />Gene expression: </label>
|
||||
</td>
|
||||
<td>
|
||||
<label for='geneSelector'><select name='geneSelector' id='geneSelector' onchange='selectFeature()'>gene_options_here</select></label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan = '2' align='center'>
|
||||
<canvas id='canvasColorScale' width = 200 height=40></canvas>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<label for='colourType'><input type='radio' name='colourType' checked onchange='setColourBy(value)' value='category' />Category:</label>
|
||||
</td>
|
||||
<td>
|
||||
<label for='categorySelector'><select name='categorySelector' id='categorySelector' onchange = 'setCategory()'>category_options_here</select></label>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</fieldset>
|
||||
</form>
|
||||
<br />
|
||||
<div>
|
||||
<fieldset>
|
||||
<legend><b>Cell types:</b></legend>
|
||||
<label for='toggleRadio'><input type='checkbox' name = 'toggleRadio' id='toggleRadio' onchange='toggleAllTypes()' checked />Show all:</label>
|
||||
<form id = 'typesControlPanel'>
|
||||
|
||||
</form>
|
||||
</fieldset>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style='vertical-align: text-top' >
|
||||
<canvas id='canvas' width=600 height=600></canvas>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<script id='vertex-shader' type='x-shader/x-fragment'>
|
||||
attribute vec4 a_Position;
|
||||
attribute vec3 a_Color;
|
||||
uniform float u_basePointSize;
|
||||
uniform float u_Alpha;
|
||||
uniform int u_PaintFeatureScale;
|
||||
varying vec4 v_Color;
|
||||
void main() {
|
||||
gl_Position = a_Position;
|
||||
gl_PointSize = u_basePointSize;
|
||||
if (u_PaintFeatureScale == 0){
|
||||
v_Color = vec4(a_Color, u_Alpha);
|
||||
}
|
||||
else{
|
||||
float r = 0.0;
|
||||
float g = 0.0;
|
||||
float b = 0.0;
|
||||
r = max(0.0, 2.0 * a_Color.r - 1.0);
|
||||
b = max(0.0, 2.0 * (1.0 - a_Color.r) - 1.0);
|
||||
g = 1.0 - 2.0 * abs(a_Color.r - 0.5);
|
||||
v_Color = vec4(r, g, b, u_Alpha);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script id ='fragment-shader' type='x-shader/x-fragment'>
|
||||
precision mediump float;
|
||||
varying vec4 v_Color;
|
||||
void main() {
|
||||
float r = 0.0;
|
||||
vec2 cxy = 2.0 * gl_PointCoord - 1.0;
|
||||
r = dot(cxy, cxy);
|
||||
if (r > 1.0){
|
||||
discard;
|
||||
}
|
||||
gl_FragColor = v_Color;
|
||||
}
|
||||
</script>
|
||||
<script type = 'text/javascript'>
|
||||
|
||||
var Matrix4 = function(opt_src) {
|
||||
var i, s, d;
|
||||
if (opt_src && typeof opt_src === 'object' && opt_src.hasOwnProperty('elements')) {
|
||||
s = opt_src.elements;
|
||||
d = new Float32Array(16);
|
||||
for (i = 0; i < 16; ++i) {
|
||||
d[i] = s[i];
|
||||
}
|
||||
this.elements = d;
|
||||
} else {
|
||||
this.elements = new Float32Array([1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]);
|
||||
}
|
||||
};
|
||||
|
||||
Matrix4.prototype.setTranslate = function(x, y, z) {
|
||||
var e = this.elements;
|
||||
e[0] = 1; e[4] = 0; e[8] = 0; e[12] = x;
|
||||
e[1] = 0; e[5] = 1; e[9] = 0; e[13] = y;
|
||||
e[2] = 0; e[6] = 0; e[10] = 1; e[14] = z;
|
||||
e[3] = 0; e[7] = 0; e[11] = 0; e[15] = 1;
|
||||
return this;
|
||||
};
|
||||
|
||||
Matrix4.prototype.setLookAt = function(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) {
|
||||
var e, fx, fy, fz, rlf, sx, sy, sz, rls, ux, uy, uz;
|
||||
|
||||
fx = centerX - eyeX;
|
||||
fy = centerY - eyeY;
|
||||
fz = centerZ - eyeZ;
|
||||
|
||||
// Normalize f.
|
||||
rlf = 1 / Math.sqrt(fx*fx + fy*fy + fz*fz);
|
||||
fx *= rlf;
|
||||
fy *= rlf;
|
||||
fz *= rlf;
|
||||
|
||||
// Calculate cross product of f and up.
|
||||
sx = fy * upZ - fz * upY;
|
||||
sy = fz * upX - fx * upZ;
|
||||
sz = fx * upY - fy * upX;
|
||||
|
||||
// Normalize s.
|
||||
rls = 1 / Math.sqrt(sx*sx + sy*sy + sz*sz);
|
||||
sx *= rls;
|
||||
sy *= rls;
|
||||
sz *= rls;
|
||||
|
||||
// Calculate cross product of s and f.
|
||||
ux = sy * fz - sz * fy;
|
||||
uy = sz * fx - sx * fz;
|
||||
uz = sx * fy - sy * fx;
|
||||
|
||||
// Set to this.
|
||||
e = this.elements;
|
||||
e[0] = sx;
|
||||
e[1] = ux;
|
||||
e[2] = -fx;
|
||||
e[3] = 0;
|
||||
|
||||
e[4] = sy;
|
||||
e[5] = uy;
|
||||
e[6] = -fy;
|
||||
e[7] = 0;
|
||||
|
||||
e[8] = sz;
|
||||
e[9] = uz;
|
||||
e[10] = -fz;
|
||||
e[11] = 0;
|
||||
|
||||
e[12] = 0;
|
||||
e[13] = 0;
|
||||
e[14] = 0;
|
||||
e[15] = 1;
|
||||
|
||||
// Translate.
|
||||
return this.translate(-eyeX, -eyeY, -eyeZ);
|
||||
};
|
||||
|
||||
Matrix4.prototype.translate = function(x, y, z) {
|
||||
var e = this.elements;
|
||||
e[12] += e[0] * x + e[4] * y + e[8] * z;
|
||||
e[13] += e[1] * x + e[5] * y + e[9] * z;
|
||||
e[14] += e[2] * x + e[6] * y + e[10] * z;
|
||||
e[15] += e[3] * x + e[7] * y + e[11] * z;
|
||||
return this;
|
||||
};
|
||||
|
||||
Matrix4.prototype.setPerspective = function(fovy, aspect, near, far) {
|
||||
var e, rd, s, ct;
|
||||
|
||||
if (near === far || aspect === 0) {
|
||||
throw 'null frustum';
|
||||
}
|
||||
if (near <= 0) {
|
||||
throw 'near <= 0';
|
||||
}
|
||||
if (far <= 0) {
|
||||
throw 'far <= 0';
|
||||
}
|
||||
|
||||
fovy = Math.PI * fovy / 180 / 2;
|
||||
s = Math.sin(fovy);
|
||||
if (s === 0) {
|
||||
throw 'null frustum';
|
||||
}
|
||||
|
||||
rd = 1 / (far - near);
|
||||
ct = Math.cos(fovy) / s;
|
||||
|
||||
e = this.elements;
|
||||
|
||||
e[0] = ct / aspect;
|
||||
e[1] = 0;
|
||||
e[2] = 0;
|
||||
e[3] = 0;
|
||||
|
||||
e[4] = 0;
|
||||
e[5] = ct;
|
||||
e[6] = 0;
|
||||
e[7] = 0;
|
||||
|
||||
e[8] = 0;
|
||||
e[9] = 0;
|
||||
e[10] = -(far + near) * rd;
|
||||
e[11] = -1;
|
||||
|
||||
e[12] = 0;
|
||||
e[13] = 0;
|
||||
e[14] = -2 * near * far * rd;
|
||||
e[15] = 0;
|
||||
|
||||
return this;
|
||||
};
|
||||
</script>
|
||||
<script type='text/javascript'>
|
||||
|
||||
function buildCategoryRadioButtons(){
|
||||
category_type = categorySelector.options[categorySelector.selectedIndex].value;
|
||||
current_indices = indices_all;
|
||||
// create radio commands from categories
|
||||
typesControlPanel.innerHTML = "";
|
||||
radio_commands_HTML = "";
|
||||
for(name in categories_indices[category_type]){
|
||||
f_index = categories_indices[category_type][name][0]
|
||||
cols = categories_colours[category_type].slice(3 * f_index, 3 * f_index + 3)
|
||||
col_label = "#";
|
||||
for(k=0;k<cols.length;k++){col_hex = Math.round(255 * cols[k]).toString(16).padStart(2, '0'); col_label = col_label + col_hex}
|
||||
radio_command = "<div style='background-color:" + col_label + "'>";
|
||||
radio_command = radio_command + "<input style='float:left' type='checkbox' id='" + name;
|
||||
radio_command = radio_command + "' checked onchange='toggleCategoryAction()' /><label style='float:left' for='" + name + "'";
|
||||
radio_command = radio_command + ">" + name + ": </label><br/></div>"
|
||||
radio_commands_HTML = radio_commands_HTML + radio_command
|
||||
}
|
||||
typesControlPanel.innerHTML = radio_commands_HTML;
|
||||
}
|
||||
|
||||
function toggleCategoryAction(){
|
||||
updateBuffer()
|
||||
draw()
|
||||
}
|
||||
|
||||
function setCategory(){
|
||||
buildCategoryRadioButtons()
|
||||
updateBuffer()
|
||||
draw()
|
||||
}
|
||||
|
||||
function setColourBy(value){
|
||||
colour_by = value;
|
||||
if (colour_by =='category'){
|
||||
PaintFeatureScale = 0;
|
||||
}else{
|
||||
PaintFeatureScale = 1;
|
||||
}
|
||||
gl_context.uniform1i(u_PaintFeatureScale, PaintFeatureScale)
|
||||
updateBuffer()
|
||||
draw()
|
||||
}
|
||||
|
||||
|
||||
function toggleAllTypes(){
|
||||
controlRadios = typesControlPanel.elements
|
||||
for(i=0;i<controlRadios.length;i++){
|
||||
controlRadios[i].checked = toggleRadio.checked
|
||||
}
|
||||
updateBuffer()
|
||||
draw()
|
||||
}
|
||||
|
||||
function selectFeature(){
|
||||
feature = geneSelector.value
|
||||
updateBuffer()
|
||||
draw()
|
||||
console.log('selected features')
|
||||
}
|
||||
|
||||
function draw(){
|
||||
if(bg_color == "white"){
|
||||
gl_context.clearColor(1, 1, 1, 1)
|
||||
}else{
|
||||
gl_context.clearColor(0, 0, 0, 1)
|
||||
}
|
||||
gl_context.clear(gl_context.COLOR_BUFFER_BIT);
|
||||
gl_context.bufferData(gl_context.ARRAY_BUFFER, buffer_data_array, gl_context.STATIC_DRAW)
|
||||
gl_context.drawArrays(gl_context.POINTS, 0, n)
|
||||
}
|
||||
|
||||
function updateBuffer(){
|
||||
var buffer_data = [];
|
||||
// first update indices to be used - for this read the category control panel radio buttons
|
||||
controlRadios = typesControlPanel.elements
|
||||
current_indices = []
|
||||
for(i=0;i<controlRadios.length;i++){
|
||||
if(controlRadios[i].checked){
|
||||
radio_type = controlRadios[i].id
|
||||
current_indices = current_indices.concat(categories_indices[category_type][radio_type])
|
||||
}
|
||||
}
|
||||
// now just populate the buffer_data
|
||||
if(colour_by == 'gene_expression'){
|
||||
current_indices.forEach(function(index, i){
|
||||
buffer_data.push(coordinates_data[2 * index])
|
||||
buffer_data.push(coordinates_data[2 * index + 1])
|
||||
buffer_data.push(gene_expression[feature][index])
|
||||
buffer_data.push(gene_expression[feature][index])
|
||||
buffer_data.push(gene_expression[feature][index])
|
||||
})
|
||||
}else{
|
||||
current_indices.forEach(function(index, i){
|
||||
buffer_data.push(coordinates_data[2 * index])
|
||||
buffer_data.push(coordinates_data[2 * index + 1])
|
||||
buffer_data.push(categories_colours[category_type][3 * index])
|
||||
buffer_data.push(categories_colours[category_type][3 * index + 1])
|
||||
buffer_data.push(categories_colours[category_type][3 * index + 2])
|
||||
})
|
||||
}
|
||||
buffer_data_array = new Float32Array(buffer_data)
|
||||
n = buffer_data_array.length / 5
|
||||
}
|
||||
|
||||
function setParticleSize(value){
|
||||
particleSize = parseInt(value)
|
||||
gl_context.uniform1f(u_basePointSize, particleSize)
|
||||
updateBuffer()
|
||||
draw()
|
||||
}
|
||||
|
||||
function setAlpha(value){
|
||||
alphaValue = parseInt(value) / 1000
|
||||
gl_context.uniform1f(u_Alpha, alphaValue)
|
||||
updateBuffer()
|
||||
draw()
|
||||
}
|
||||
|
||||
function setCanvasSize(value){
|
||||
value = parseInt(value)
|
||||
canvas.width = value
|
||||
canvas.height = value
|
||||
gl_context = getContext(canvas)
|
||||
gl_context = initContext(gl_context)
|
||||
gl_context.viewport(0, 0, canvas.width, canvas.height)
|
||||
updateBuffer()
|
||||
draw()
|
||||
}
|
||||
|
||||
function setBackground(value){
|
||||
bg_color = value;
|
||||
draw()
|
||||
}
|
||||
|
||||
function shadersFromScriptElement(gl, ID, type){
|
||||
shaderScript = document.getElementById(ID)
|
||||
var str = ''
|
||||
var k = shaderScript.firstChild;
|
||||
while(k){
|
||||
if (k.nodeType == 3){
|
||||
str += k.textContent;
|
||||
}
|
||||
k = k.nextSibling
|
||||
}
|
||||
var shader = gl.createShader(type)
|
||||
gl.shaderSource(shader, str)
|
||||
gl.compileShader(shader)
|
||||
return shader
|
||||
}
|
||||
|
||||
function getContext(canvasWidget){
|
||||
var names = ['webgl', 'experimental-webgl', 'webkit-3d', 'moz-webgl'];
|
||||
for(var i=0; i<names.length; i++){
|
||||
try{
|
||||
var gl = canvasWidget.getContext(names[i])
|
||||
}catch(e){}
|
||||
if(gl){i=names.length}
|
||||
}
|
||||
|
||||
var vshader = shadersFromScriptElement(gl, 'vertex-shader', gl.VERTEX_SHADER),
|
||||
fshader = shadersFromScriptElement(gl, 'fragment-shader', gl.FRAGMENT_SHADER)
|
||||
program = gl.createProgram();
|
||||
gl.attachShader(program, vshader)
|
||||
gl.attachShader(program, fshader)
|
||||
gl.linkProgram(program)
|
||||
gl.useProgram(program)
|
||||
gl.program = program
|
||||
return gl
|
||||
}
|
||||
|
||||
function initContext(gl){
|
||||
n = buffer_data_array.length / 5
|
||||
var vertexColourBuffer = gl.createBuffer()
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, vertexColourBuffer)
|
||||
|
||||
var FSIZE = buffer_data_array.BYTES_PER_ELEMENT;
|
||||
|
||||
var a_Position = gl.getAttribLocation(gl.program, 'a_Position')
|
||||
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 5, 0)
|
||||
gl.enableVertexAttribArray(a_Position)
|
||||
|
||||
var a_Color = gl.getAttribLocation(gl.program, 'a_Color')
|
||||
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 5, 2 * FSIZE)
|
||||
gl.enableVertexAttribArray(a_Color)
|
||||
|
||||
u_basePointSize = gl.getUniformLocation(gl.program, 'u_basePointSize')
|
||||
gl.uniform1f(u_basePointSize, particleSize)
|
||||
|
||||
u_Alpha = gl.getUniformLocation(gl.program, "u_Alpha")
|
||||
gl.uniform1f(u_Alpha, alphaValue)
|
||||
|
||||
u_PaintFeatureScale = gl.getUniformLocation(gl.program, 'u_PaintFeatureScale')
|
||||
gl.uniform1i(u_PaintFeatureScale, PaintFeatureScale)
|
||||
|
||||
gl.clearColor(1, 1, 1, 1);
|
||||
if(bg_color == "dark"){
|
||||
gl.clearColor(0, 0, 0, 1)
|
||||
}
|
||||
|
||||
gl.disable(gl.DEPTH_TEST)
|
||||
gl.enable(gl.BLEND)
|
||||
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
|
||||
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
return gl
|
||||
}
|
||||
|
||||
var categorySelector = document.getElementById('categorySelector'),
|
||||
geneSelector = document.getElementById('geneSelector'),
|
||||
typesControlPanel = document.getElementById('typesControlPanel'),
|
||||
toggleRadio = document.getElementById('toggleRadio')
|
||||
|
||||
var canvas = document.getElementById('canvas'),
|
||||
particleSize = 5,
|
||||
alphaValue = 1.0,
|
||||
bg_color = "white",
|
||||
n = 0,
|
||||
particleSize = 2,
|
||||
PaintFeatureScale = 0;
|
||||
|
||||
coordinates_data = [coordinates_data_here]
|
||||
gene_expression = []; gene_expression_colour_coded;
|
||||
|
||||
categories_colours = []
|
||||
categories_colours_data_here
|
||||
categories_indices = []
|
||||
categories_indices_data_here
|
||||
|
||||
// initialize flags
|
||||
// when toggling between gene expression and category, do not slice data i.e. do not recompute index data
|
||||
// when choosing a category always re-initiate index data
|
||||
|
||||
var colour_by = 'category', // the other options is can be 'category'
|
||||
category_types = [],
|
||||
category_type = '',
|
||||
features = [],
|
||||
feature = '';
|
||||
|
||||
// set category
|
||||
for(name in categories_colours){category_types.push(name)}
|
||||
category_type = category_types[0]
|
||||
|
||||
// set feature
|
||||
for(name in gene_expression){features.push(name)}
|
||||
feature = features[0];
|
||||
|
||||
// create global data holders
|
||||
var indices_all = [],
|
||||
current_indices = [],
|
||||
current_colours = [],
|
||||
buffer_data_array = [];
|
||||
|
||||
for(j=0;j<categories_colours[category_type].length/3;j++){indices_all.push(j)}
|
||||
|
||||
// build the categories buttons for the first time
|
||||
buildCategoryRadioButtons()
|
||||
|
||||
updateBuffer()
|
||||
|
||||
// create the renderer
|
||||
var gl_context = getContext(canvas);
|
||||
gl_context = initContext(gl_context)
|
||||
|
||||
// now draw
|
||||
draw()
|
||||
|
||||
// draw the scale
|
||||
var canvasColorScale = document.getElementById('canvasColorScale'),
|
||||
canvas_ctx = canvasColorScale.getContext('2d'),
|
||||
scale_gradient = canvas_ctx.createLinearGradient(0, 0, 200, 0);
|
||||
|
||||
scale_gradient.addColorStop(0, 'blue');
|
||||
scale_gradient.addColorStop(0.5, 'green');
|
||||
scale_gradient.addColorStop(1, 'red');
|
||||
canvas_ctx.fillStyle = scale_gradient;
|
||||
canvas_ctx.fillRect(0, 20, canvasColorScale.width, canvasColorScale.height)
|
||||
canvas_ctx.fillStyle = 'black'
|
||||
canvas_ctx.fillText('0', 10, 10)
|
||||
canvas_ctx.fillText('8', 190, 10)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
data = pd.read_csv(expression_data_fname, index_col = None)
|
||||
|
||||
# convert Colours to r, g, b values, then to floats < 1.0
|
||||
def hexdec_to_1floats(hexdec):
|
||||
return np.array([int(hexdec[1:][i:(i+2)], 16) for i in (0, 2, 4)]) / 255.0
|
||||
|
||||
gene_names = [gene_name for gene_name in data.columns[(2 + 2 * no_of_categories):]]
|
||||
raw_expression = data.values[:, (2 + 2 * no_of_categories):]
|
||||
gene_options = []
|
||||
gene_expression_colour_coded = []
|
||||
max_expression = round(raw_expression.max())
|
||||
raw_expression /= max_expression
|
||||
for index, gene_name in enumerate(gene_names):
|
||||
gene_expression = raw_expression[:, index]
|
||||
gene_expression = [str(value)[:min(4, len(str(value)))] for value in gene_expression]
|
||||
gene_expression = ",".join(gene_expression)
|
||||
gene_expression_colour_coded.append("gene_expression['{gn}'] = [{ge}]".format(gn = gene_name, ge = gene_expression))
|
||||
gene_options.append("<option value='{gn}'>{gn}</option>".format(gn = gene_name))
|
||||
gene_options = "".join(gene_options)
|
||||
gene_expression_colour_coded = ";".join(gene_expression_colour_coded)
|
||||
|
||||
# make coordinates data string
|
||||
coordinates = data.values[:, 0:2].astype('float32')
|
||||
# next few steps are compressing the data into a stadard cube centered at (0,0,0) and L = 200
|
||||
Xrange = np.percentile(coordinates[:, 0], q = [1, 98]) * 1.2
|
||||
Yrange = np.percentile(coordinates[:, 1], q = [1, 98]) * 1.2
|
||||
center = np.array((np.mean(Xrange), np.mean(Yrange)))
|
||||
coordinates = coordinates - np.tile(center, (coordinates.shape[0], 1))
|
||||
ratio = max(np.abs(np.percentile(coordinates[:, 0], q = [1, 98]) * 1.2))
|
||||
ratio = max(ratio, max(np.abs(np.percentile(coordinates[:, 1], q = [1, 98]) * 1.2)))
|
||||
ratio = 1.0 / ratio
|
||||
coordinates = coordinates * ratio
|
||||
coordinates = ",".join([str(value)[:min(6, len(str(value)))] for value in coordinates.ravel()])
|
||||
|
||||
categories = [str(value).replace(".", " ") for value in data.columns[2:(2 + no_of_categories)]]
|
||||
categories_options = ["<option value='{cat}'>{cat}</option>".format(cat=cat) for cat in categories]
|
||||
categories_options = "".join(categories_options)
|
||||
|
||||
categories_colours = []
|
||||
categories_indices = []
|
||||
for cat_index in range(no_of_categories):
|
||||
category_name = data.columns[2 + cat_index]
|
||||
category_name = category_name.replace(".", " ")
|
||||
category_colours = [hexdec_to_1floats(colour) for colour in data.values[:, 2 + cat_index + no_of_categories]]
|
||||
category_colours = [",".join([str(value)[:min(4, len(str(value)))] for value in colour]) for colour in category_colours]
|
||||
category_colours = ",".join(category_colours)
|
||||
categories_colours.append("categories_colours['{cn}'] = [{cc}]".format(cn = category_name, cc = category_colours))
|
||||
types = [value for value in np.unique(data.values[:, 2 + cat_index])]
|
||||
cat_indices = []
|
||||
categories_indices.append("categories_indices['{cn}'] = []".format(cn = category_name))
|
||||
for t_name in types:
|
||||
indices = data.values[:, 2 + cat_index] == t_name
|
||||
indices = np.where(indices)[0]
|
||||
indices = ",".join([str(value) for value in indices])
|
||||
cat_indices.append("categories_indices['{cn}']['{tn}'] = [{ind}]".format(cn = category_name, tn = t_name, ind = indices))
|
||||
cat_indices = "\n".join(cat_indices)
|
||||
categories_indices.append(cat_indices)
|
||||
categories_indices = "\n".join(categories_indices)
|
||||
categories_colours = "\n".join(categories_colours)
|
||||
|
||||
template_str = template_str.replace('gene_options_here', gene_options)
|
||||
template_str = template_str.replace('gene_expression_colour_coded', gene_expression_colour_coded)
|
||||
template_str = template_str.replace('coordinates_data_here', coordinates)
|
||||
template_str = template_str.replace('category_options_here', categories_options)
|
||||
template_str = template_str.replace('categories_colours_data_here', categories_colours)
|
||||
template_str = template_str.replace('categories_indices_data_here', categories_indices)
|
||||
with open(save_to, 'w') as result:
|
||||
result.write(template_str)
|
||||
318
tools/interactive_2D_viewer/html_WebGL_2D_viewer.py
Executable file
318
tools/interactive_2D_viewer/html_WebGL_2D_viewer.py
Executable file
|
|
@ -0,0 +1,318 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Tue Aug 7 13:45:37 2018
|
||||
|
||||
@author: Dorin-Mirel Popescu
|
||||
"""
|
||||
|
||||
import sys
|
||||
args = sys.argv
|
||||
save_to = args[1]
|
||||
data_frame_fname = args[2]
|
||||
|
||||
template = """
|
||||
<!doctype html>
|
||||
|
||||
<html lang='en'>
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
|
||||
<title>3D viewer</title>
|
||||
<meta name='description' content='The HTML5 Herald'>
|
||||
<meta name='author' content='Dorin-Mirel Popescu'>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<th align='left'>
|
||||
|
||||
<label for = 'particleSizeBar'>Particle size: </label>
|
||||
<input type='range' name = 'particleSizeBar' min = 0 max = 50 onchange='setParticleSize(value)' value = 5 /><br />
|
||||
|
||||
<label for = 'alphaInput'>Transparency: </label>
|
||||
<input type='range' name = 'alphaInput' min = 0 max = 1000 onchange='setAlpha(value)' value = 1000 /><br />
|
||||
|
||||
<label for = 'canvasSizeInput'>Canvas size: </label>
|
||||
<input type='range' name = 'canvasSizeInput' min = 200 max = 2000 onchange='setCanvasSize(value)' value = 500 /><br />
|
||||
|
||||
<label for = 'bgInput'>Dark background: </label>
|
||||
<input type='radio' name = 'bgInput' onchange='setBackground(value)' value = 'dark' />
|
||||
<label for = 'bgInput'>White background: </label>
|
||||
<input type='radio' name = 'bgInput' onchange='setBackground(value)' value = 'white' checked />
|
||||
<br />
|
||||
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<canvas id='canvas' width=600 height=600></canvas>
|
||||
</td>
|
||||
<td style='vertical-align: top'>
|
||||
<label>Categories: </label><br />
|
||||
<label for='toggleRadio'>Show all:</label>
|
||||
<input type='checkbox' name = 'toggleRadio' id='toggleRadio' onchange='toggleShowTypes()' checked /><br />
|
||||
<form id = 'ControlPanel'>
|
||||
radiocommands
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<script id='vertex-shader' type='x-shader/x-fragment'>
|
||||
attribute vec4 a_Position;
|
||||
attribute vec3 a_Color;
|
||||
uniform float u_basePointSize;
|
||||
uniform float u_Alpha;
|
||||
varying vec4 v_Color;
|
||||
void main() {
|
||||
gl_Position = a_Position;
|
||||
gl_PointSize = u_basePointSize;
|
||||
v_Color = vec4(a_Color, u_Alpha);
|
||||
}
|
||||
</script>
|
||||
<script id ='fragment-shader' type='x-shader/x-fragment'>
|
||||
precision mediump float;
|
||||
varying vec4 v_Color;
|
||||
void main() {
|
||||
float r = 0.0;
|
||||
vec2 cxy = 2.0 * gl_PointCoord - 1.0;
|
||||
r = dot(cxy, cxy);
|
||||
if (r > 1.0){
|
||||
discard;
|
||||
}
|
||||
gl_FragColor = v_Color;
|
||||
}
|
||||
</script>
|
||||
<script type='text/javascript'>
|
||||
|
||||
function selectData(){
|
||||
controlPanel = document.getElementById('ControlPanel')
|
||||
controlRadios = controlPanel.elements
|
||||
values = []
|
||||
for(i=0;i<controlRadios.length;i++){
|
||||
if(controlRadios[i].checked){
|
||||
values = values.concat(index_table[controlRadios[i].id])
|
||||
}
|
||||
}
|
||||
new_indices = []
|
||||
for (i=0;i<values.length;i++){
|
||||
v = values[i]
|
||||
new_indices.push(5*v)
|
||||
new_indices.push(5*v+1)
|
||||
new_indices.push(5*v+2)
|
||||
new_indices.push(5*v+3)
|
||||
new_indices.push(5*v+4)
|
||||
}
|
||||
current_data_buffer = []
|
||||
new_indices.forEach(function(val, i){current_data_buffer.push(data_buffer[val])})
|
||||
current_data_buffer = new Float32Array(current_data_buffer)
|
||||
|
||||
gl_context.bufferData(gl_context.ARRAY_BUFFER, current_data_buffer, gl_context.STATIC_DRAW); // load data to buffer
|
||||
n = current_data_buffer.length / 5
|
||||
gl_context.clear(gl_context.COLOR_BUFFER_BIT)
|
||||
gl_context.drawArrays(gl_context.POINTS, 0, n)
|
||||
}
|
||||
|
||||
function toggleShowTypes(){
|
||||
toggleRadio = document.getElementById('toggleRadio')
|
||||
controlPanel = document.getElementById('ControlPanel')
|
||||
controlRadios = controlPanel.elements
|
||||
for(i=0;i<controlRadios.length;i++){
|
||||
controlRadios[i].checked = toggleRadio.checked
|
||||
}
|
||||
selectData()
|
||||
}
|
||||
|
||||
function setParticleSize(value){
|
||||
particleSize = parseInt(value)
|
||||
gl_context.uniform1f(u_basePointSize, particleSize)
|
||||
gl_context.clear(gl_context.COLOR_BUFFER_BIT)
|
||||
gl_context.drawArrays(gl_context.POINTS, 0, n)
|
||||
}
|
||||
|
||||
function setAlpha(value){
|
||||
alphaValue = parseInt(value) / 1000
|
||||
gl_context.uniform1f(u_Alpha, alphaValue)
|
||||
gl_context.clear(gl_context.COLOR_BUFFER_BIT);
|
||||
gl_context.drawArrays(gl_context.POINTS, 0, n)
|
||||
}
|
||||
|
||||
function setCanvasSize(value){
|
||||
value = parseInt(value)
|
||||
canvas.width = value
|
||||
canvas.height = value
|
||||
gl_context = getContext(canvas)
|
||||
gl_context = initContext(gl_context)
|
||||
gl_context.viewport(0, 0, canvas.width, canvas.height)
|
||||
if(bg_color == "white"){
|
||||
gl_context.clearColor(1, 1, 1, 1)
|
||||
}else{
|
||||
gl_context.clearColor(0, 0, 0, 1)
|
||||
}
|
||||
gl_context.clear(gl_context.COLOR_BUFFER_BIT)
|
||||
gl_context.drawArrays(gl_context.POINTS, 0, n)
|
||||
}
|
||||
|
||||
function setBackground(value){
|
||||
if(value == "dark"){
|
||||
gl_context.clearColor(0, 0, 0, 1)
|
||||
bg_color = "dark"
|
||||
}else{
|
||||
gl_context.clearColor(1, 1, 1, 1)
|
||||
bg_color = "white"
|
||||
}
|
||||
gl_context.clear(gl_context.COLOR_BUFFER_BIT)
|
||||
gl_context.drawArrays(gl_context.POINTS, 0, n)
|
||||
}
|
||||
|
||||
function shadersFromScriptElement(gl, ID, type){
|
||||
shaderScript = document.getElementById(ID)
|
||||
var str = ''
|
||||
var k = shaderScript.firstChild;
|
||||
while(k){
|
||||
if (k.nodeType == 3){
|
||||
str += k.textContent;
|
||||
}
|
||||
k = k.nextSibling
|
||||
}
|
||||
var shader = gl.createShader(type)
|
||||
gl.shaderSource(shader, str)
|
||||
gl.compileShader(shader)
|
||||
return shader
|
||||
}
|
||||
|
||||
function getContext(canvasWidget){
|
||||
var names = ['webgl', 'experimental-webgl', 'webkit-3d', 'moz-webgl'];
|
||||
for(var i=0; i<names.length; i++){
|
||||
try{
|
||||
var gl = canvasWidget.getContext(names[i])
|
||||
}catch(e){}
|
||||
if(gl){i=names.length}
|
||||
}
|
||||
|
||||
var vshader = shadersFromScriptElement(gl, 'vertex-shader', gl.VERTEX_SHADER),
|
||||
fshader = shadersFromScriptElement(gl, 'fragment-shader', gl.FRAGMENT_SHADER)
|
||||
program = gl.createProgram();
|
||||
gl.attachShader(program, vshader)
|
||||
gl.attachShader(program, fshader)
|
||||
gl.linkProgram(program)
|
||||
gl.useProgram(program)
|
||||
gl.program = program
|
||||
return gl
|
||||
}
|
||||
|
||||
function initContext(gl){
|
||||
n = current_data_buffer.length / 5
|
||||
var vertexColourBuffer = gl.createBuffer()
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, vertexColourBuffer)
|
||||
gl.bufferData(gl.ARRAY_BUFFER, current_data_buffer, gl.STATIC_DRAW)
|
||||
|
||||
var FSIZE = data_buffer.BYTES_PER_ELEMENT;
|
||||
|
||||
var a_Position = gl.getAttribLocation(gl.program, 'a_Position')
|
||||
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 5, 0)
|
||||
gl.enableVertexAttribArray(a_Position)
|
||||
|
||||
var a_Color = gl.getAttribLocation(gl.program, 'a_Color')
|
||||
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 5, 2 * FSIZE)
|
||||
gl.enableVertexAttribArray(a_Color)
|
||||
|
||||
u_basePointSize = gl.getUniformLocation(gl.program, 'u_basePointSize')
|
||||
gl.uniform1f(u_basePointSize, particleSize)
|
||||
|
||||
u_Alpha = gl.getUniformLocation(gl.program, "u_Alpha")
|
||||
gl.uniform1f(u_Alpha, alphaValue)
|
||||
|
||||
gl.clearColor(1, 1, 1, 1); // add ternary conditional
|
||||
if(bg_color == "dark"){
|
||||
gl.clearColor(0, 0, 0, 1)
|
||||
}
|
||||
|
||||
gl.disable(gl.DEPTH_TEST)
|
||||
gl.enable(gl.BLEND)
|
||||
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
|
||||
//gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)
|
||||
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
return gl
|
||||
}
|
||||
|
||||
var canvas = document.getElementById('canvas'),
|
||||
particleSize = 5,
|
||||
alphaValue = 1.0,
|
||||
bg_color = "white"
|
||||
|
||||
data_buffer = new Float32Array([
|
||||
datahere
|
||||
])
|
||||
|
||||
index_table = []
|
||||
indiceshere
|
||||
|
||||
current_data_buffer = data_buffer
|
||||
|
||||
gl_context = getContext(canvas)
|
||||
gl_context = initContext(gl_context)
|
||||
gl_context.drawArrays(gl_context.POINTS, 0, n)
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
data = pd.read_csv(data_frame_fname, index_col = None)
|
||||
|
||||
# convert Colours to r, g, b values, then to floats < 1.0
|
||||
def hexdec_to_1floats(hexdec):
|
||||
return np.array([int(hexdec[1:][i:(i+2)], 16) for i in (0, 2, 4)]) / 255.0
|
||||
|
||||
# map Labels to colours
|
||||
labels = sorted(list(data.Labels.unique()))
|
||||
index_table = []
|
||||
radio_commands = []
|
||||
for index, label in enumerate(labels):
|
||||
indices = data.Labels == label
|
||||
indices = indices.values
|
||||
indices = np.where(indices)
|
||||
indices = ','.join([str(i) for i in indices[0]])
|
||||
indices = "[{indices}]".format(indices = indices)
|
||||
index_table.append("index_table['{label}'] = {indices}".format(label = label, indices = indices))
|
||||
colour = data.Colours[data.Labels == label].values[0]
|
||||
radio_command = "<div style='background-color:{colour}'><input style='float:left' type='checkbox' id='{label}' checked onchange='selectData()' /><label style='float:left' for='{label}'>{label}: </label><br /></div>".format(colour = colour, label = label)
|
||||
radio_commands.append(radio_command)
|
||||
index_table = ';\n '.join(index_table)
|
||||
radio_commands = '\n '.join(radio_commands)
|
||||
|
||||
# make data string
|
||||
coordinates = data.values[:, 0:2].astype('float32')
|
||||
# next few steps are compressing the data into a stadard cube centered at (0,0,0) and L = 200
|
||||
Xrange = np.percentile(coordinates[:, 0], q = [1, 98]) * 1.2
|
||||
Yrange = np.percentile(coordinates[:, 1], q = [1, 98]) * 1.2
|
||||
center = np.array((np.mean(Xrange), np.mean(Yrange)))
|
||||
coordinates = coordinates - np.tile(center, (coordinates.shape[0], 1))
|
||||
ratio = max(np.abs(np.percentile(coordinates[:, 0], q = [1, 98]) * 1.2))
|
||||
ratio = max(ratio, max(np.abs(np.percentile(coordinates[:, 1], q = [1, 98]) * 1.2)))
|
||||
ratio = 1.0 / ratio
|
||||
coordinates = coordinates * ratio
|
||||
|
||||
# next few steps the buffer data is created as string
|
||||
colours = data.Colours.values
|
||||
buffer_data = []
|
||||
for index in range(coordinates.shape[0]):
|
||||
coordinate = [str(i) for i in coordinates[index, :]]
|
||||
colour = [str(i) for i in hexdec_to_1floats(colours[index]).astype('float32')]
|
||||
vertex_data = coordinate + colour
|
||||
buffer_data.append(",".join(vertex_data))
|
||||
buffer_data = ",".join(buffer_data)
|
||||
|
||||
template_str = template.replace('datahere', buffer_data)
|
||||
template_str = template_str.replace('indiceshere', index_table)
|
||||
template_str = template_str.replace('radiocommands', radio_commands)
|
||||
with open(save_to, 'w') as result:
|
||||
result.write(template_str)
|
||||
|
||||
589
tools/interactive_3D_viewer/html_WebGL_3D_viewer.py
Executable file
589
tools/interactive_3D_viewer/html_WebGL_3D_viewer.py
Executable file
|
|
@ -0,0 +1,589 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Tue Aug 7 13:45:37 2018
|
||||
|
||||
@author: Dorin-Mirel Popescu
|
||||
"""
|
||||
|
||||
import sys
|
||||
args = sys.argv
|
||||
save_to = args[1]
|
||||
data_frame_fname = args[2]
|
||||
|
||||
template = """
|
||||
<!doctype html>
|
||||
|
||||
<html lang='en'>
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
|
||||
<title>3D viewer</title>
|
||||
<meta name='description' content='The HTML5 Herald'>
|
||||
<meta name='author' content='Dorin-Mirel Popescu'>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<th align='left'>
|
||||
|
||||
<label for = 'particleSizeBar'>Particle size: </label>
|
||||
<input type='range' name = 'particleSizeBar' min = 10 max = 300 onchange='setParticleSize(value)' value = 150 /><br />
|
||||
|
||||
<label for = 'alphaInput'>Transparency: </label>
|
||||
<input type='range' name = 'alphaInput' min = 0 max = 1000 onchange='setAlpha(value)' value = 1000 /><br />
|
||||
|
||||
<label for = 'canvasSizeInput'>Canvas size: </label>
|
||||
<input type='range' name = 'canvasSizeInput' min = 200 max = 2000 onchange='setCanvasSize(value)' value = 500 /><br />
|
||||
|
||||
<label for = "zoom">Zoom: </label>
|
||||
<input type='range' name = 'zoom' min = 100 max = 1000 onchange='setZoom(value)' value = 400 /><br />
|
||||
|
||||
<label for = 'bgInput'>Dark background: </label>
|
||||
<input type='radio' name = 'bgInput' onchange='setBackground(value)' value = 'dark' />
|
||||
<label for = 'bgInput'>White background: </label>
|
||||
<input type='radio' name = 'bgInput' onchange='setBackground(value)' value = 'white' checked />
|
||||
<br />
|
||||
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style='vertical-align: text-top' >
|
||||
<canvas id='canvas' width=600 height=600></canvas>
|
||||
</td>
|
||||
<td style='vertical-align: top'>
|
||||
<label>Categories: </label><br />
|
||||
<label for='toggleRadio'>Show all:</label>
|
||||
<input type='checkbox' name = 'toggleRadio' id='toggleRadio' onchange='toggleShowTypes()' checked /><br />
|
||||
<form id = 'ControlPanel'>
|
||||
radiocommands
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<script id='vertex-shader' type='x-shader/x-fragment'>
|
||||
attribute vec4 a_Position;
|
||||
attribute vec3 a_Color;
|
||||
uniform mat4 u_ModelMatrix;
|
||||
uniform mat4 u_ViewMatrix;
|
||||
uniform mat4 u_ProjMatrix;
|
||||
uniform float u_basePointSize;
|
||||
uniform float u_Alpha;
|
||||
varying vec4 v_Color;
|
||||
void main() {
|
||||
vec4 cubePos = u_ProjMatrix * u_ViewMatrix * u_ModelMatrix * a_Position;
|
||||
float currentWidth = 0.0;
|
||||
//currentWidth = u_basePointSize;
|
||||
currentWidth = 3.0 + (u_basePointSize - 3.0) * (1.0 - cubePos.z / cubePos.w) / 2.0;
|
||||
gl_Position = cubePos;
|
||||
gl_PointSize = currentWidth;
|
||||
v_Color = vec4(a_Color, u_Alpha);
|
||||
}
|
||||
</script>
|
||||
<script id ='fragment-shader' type='x-shader/x-fragment'>
|
||||
precision mediump float;
|
||||
varying vec4 v_Color;
|
||||
void main() {
|
||||
float r = 0.0;
|
||||
vec2 cxy = 2.0 * gl_PointCoord - 1.0;
|
||||
r = dot(cxy, cxy);
|
||||
if (r > 1.0){
|
||||
discard;
|
||||
}
|
||||
vec2 D = vec2(0.0, 0.0), centers = vec2(.65, .35);
|
||||
float light = 0.0;
|
||||
light = length(centers - gl_PointCoord);
|
||||
light = .1 + .9 * (pow(50.0, -light));
|
||||
gl_FragColor = v_Color * light + (1.0 - light) * vec4(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
</script>
|
||||
<script type = 'text/javascript'>
|
||||
|
||||
var Matrix4 = function(opt_src) {
|
||||
var i, s, d;
|
||||
if (opt_src && typeof opt_src === 'object' && opt_src.hasOwnProperty('elements')) {
|
||||
s = opt_src.elements;
|
||||
d = new Float32Array(16);
|
||||
for (i = 0; i < 16; ++i) {
|
||||
d[i] = s[i];
|
||||
}
|
||||
this.elements = d;
|
||||
} else {
|
||||
this.elements = new Float32Array([1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]);
|
||||
}
|
||||
};
|
||||
|
||||
Matrix4.prototype.setTranslate = function(x, y, z) {
|
||||
var e = this.elements;
|
||||
e[0] = 1; e[4] = 0; e[8] = 0; e[12] = x;
|
||||
e[1] = 0; e[5] = 1; e[9] = 0; e[13] = y;
|
||||
e[2] = 0; e[6] = 0; e[10] = 1; e[14] = z;
|
||||
e[3] = 0; e[7] = 0; e[11] = 0; e[15] = 1;
|
||||
return this;
|
||||
};
|
||||
|
||||
Matrix4.prototype.setLookAt = function(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) {
|
||||
var e, fx, fy, fz, rlf, sx, sy, sz, rls, ux, uy, uz;
|
||||
|
||||
fx = centerX - eyeX;
|
||||
fy = centerY - eyeY;
|
||||
fz = centerZ - eyeZ;
|
||||
|
||||
// Normalize f.
|
||||
rlf = 1 / Math.sqrt(fx*fx + fy*fy + fz*fz);
|
||||
fx *= rlf;
|
||||
fy *= rlf;
|
||||
fz *= rlf;
|
||||
|
||||
// Calculate cross product of f and up.
|
||||
sx = fy * upZ - fz * upY;
|
||||
sy = fz * upX - fx * upZ;
|
||||
sz = fx * upY - fy * upX;
|
||||
|
||||
// Normalize s.
|
||||
rls = 1 / Math.sqrt(sx*sx + sy*sy + sz*sz);
|
||||
sx *= rls;
|
||||
sy *= rls;
|
||||
sz *= rls;
|
||||
|
||||
// Calculate cross product of s and f.
|
||||
ux = sy * fz - sz * fy;
|
||||
uy = sz * fx - sx * fz;
|
||||
uz = sx * fy - sy * fx;
|
||||
|
||||
// Set to this.
|
||||
e = this.elements;
|
||||
e[0] = sx;
|
||||
e[1] = ux;
|
||||
e[2] = -fx;
|
||||
e[3] = 0;
|
||||
|
||||
e[4] = sy;
|
||||
e[5] = uy;
|
||||
e[6] = -fy;
|
||||
e[7] = 0;
|
||||
|
||||
e[8] = sz;
|
||||
e[9] = uz;
|
||||
e[10] = -fz;
|
||||
e[11] = 0;
|
||||
|
||||
e[12] = 0;
|
||||
e[13] = 0;
|
||||
e[14] = 0;
|
||||
e[15] = 1;
|
||||
|
||||
// Translate.
|
||||
return this.translate(-eyeX, -eyeY, -eyeZ);
|
||||
};
|
||||
|
||||
Matrix4.prototype.translate = function(x, y, z) {
|
||||
var e = this.elements;
|
||||
e[12] += e[0] * x + e[4] * y + e[8] * z;
|
||||
e[13] += e[1] * x + e[5] * y + e[9] * z;
|
||||
e[14] += e[2] * x + e[6] * y + e[10] * z;
|
||||
e[15] += e[3] * x + e[7] * y + e[11] * z;
|
||||
return this;
|
||||
};
|
||||
|
||||
Matrix4.prototype.setPerspective = function(fovy, aspect, near, far) {
|
||||
var e, rd, s, ct;
|
||||
|
||||
if (near === far || aspect === 0) {
|
||||
throw 'null frustum';
|
||||
}
|
||||
if (near <= 0) {
|
||||
throw 'near <= 0';
|
||||
}
|
||||
if (far <= 0) {
|
||||
throw 'far <= 0';
|
||||
}
|
||||
|
||||
fovy = Math.PI * fovy / 180 / 2;
|
||||
s = Math.sin(fovy);
|
||||
if (s === 0) {
|
||||
throw 'null frustum';
|
||||
}
|
||||
|
||||
rd = 1 / (far - near);
|
||||
ct = Math.cos(fovy) / s;
|
||||
|
||||
e = this.elements;
|
||||
|
||||
e[0] = ct / aspect;
|
||||
e[1] = 0;
|
||||
e[2] = 0;
|
||||
e[3] = 0;
|
||||
|
||||
e[4] = 0;
|
||||
e[5] = ct;
|
||||
e[6] = 0;
|
||||
e[7] = 0;
|
||||
|
||||
e[8] = 0;
|
||||
e[9] = 0;
|
||||
e[10] = -(far + near) * rd;
|
||||
e[11] = -1;
|
||||
|
||||
e[12] = 0;
|
||||
e[13] = 0;
|
||||
e[14] = -2 * near * far * rd;
|
||||
e[15] = 0;
|
||||
|
||||
return this;
|
||||
};
|
||||
</script>
|
||||
<script type='text/javascript'>
|
||||
|
||||
function selectData(){
|
||||
controlPanel = document.getElementById('ControlPanel')
|
||||
controlRadios = controlPanel.elements
|
||||
values = []
|
||||
for(i=0;i<controlRadios.length;i++){
|
||||
if(controlRadios[i].checked){
|
||||
values = values.concat(index_table[controlRadios[i].id])
|
||||
}
|
||||
}
|
||||
new_indices = []
|
||||
for (i=0;i<values.length;i++){
|
||||
v = values[i]
|
||||
new_indices.push(6*v)
|
||||
new_indices.push(6*v+1)
|
||||
new_indices.push(6*v+2)
|
||||
new_indices.push(6*v+3)
|
||||
new_indices.push(6*v+4)
|
||||
new_indices.push(6*v+5)
|
||||
}
|
||||
current_data_buffer = []
|
||||
new_indices.forEach(function(val, i){current_data_buffer.push(data_buffer[val])})
|
||||
current_data_buffer = new Float32Array(current_data_buffer)
|
||||
|
||||
gl_context.bufferData(gl_context.ARRAY_BUFFER, current_data_buffer, gl_context.STATIC_DRAW); // load data to buffer
|
||||
n = current_data_buffer.length / 6
|
||||
gl_context.clear(gl_context.COLOR_BUFFER_BIT)
|
||||
gl_context.drawArrays(gl_context.POINTS, 0, n)
|
||||
|
||||
}
|
||||
|
||||
function toggleShowTypes(){
|
||||
toggleRadio = document.getElementById('toggleRadio')
|
||||
controlPanel = document.getElementById('ControlPanel')
|
||||
controlRadios = controlPanel.elements
|
||||
for(i=0;i<controlRadios.length;i++){
|
||||
controlRadios[i].checked = toggleRadio.checked
|
||||
}
|
||||
selectData()
|
||||
}
|
||||
|
||||
function setParticleSize(value){
|
||||
particleSize = parseInt(value)
|
||||
gl_context.uniform1f(u_basePointSize, particleSize)
|
||||
gl_context.clear(gl_context.COLOR_BUFFER_BIT)
|
||||
gl_context.drawArrays(gl_context.POINTS, 0, n)
|
||||
}
|
||||
|
||||
function setAlpha(value){
|
||||
alphaValue = parseInt(value) / 1000
|
||||
gl_context.uniform1f(u_Alpha, alphaValue)
|
||||
gl_context.clear(gl_context.COLOR_BUFFER_BIT);
|
||||
gl_context.drawArrays(gl_context.POINTS, 0, n)
|
||||
}
|
||||
|
||||
function setCanvasSize(value){
|
||||
value = parseInt(value)
|
||||
canvas.width = value
|
||||
canvas.height = value
|
||||
gl_context = getContext(canvas)
|
||||
gl_context = initContext(gl_context)
|
||||
gl_context.viewport(0, 0, canvas.width, canvas.height)
|
||||
if(bg_color == "white"){
|
||||
gl_context.clearColor(1, 1, 1, 1)
|
||||
}else{
|
||||
gl_context.clearColor(0, 0, 0, 1)
|
||||
}
|
||||
gl_context.clear(gl_context.COLOR_BUFFER_BIT)
|
||||
gl_context.drawArrays(gl_context.POINTS, 0, n)
|
||||
}
|
||||
|
||||
function setZoom(value){
|
||||
eyeVN = parseInt(value)
|
||||
farField = eyeVN + 100;
|
||||
rotateData(0, 0)
|
||||
}
|
||||
|
||||
function setBackground(value){
|
||||
if(value == "dark"){
|
||||
gl_context.clearColor(0, 0, 0, 1)
|
||||
bg_color = "dark"
|
||||
}else{
|
||||
gl_context.clearColor(1, 1, 1, 1)
|
||||
bg_color = "white"
|
||||
}
|
||||
gl_context.clear(gl_context.COLOR_BUFFER_BIT)
|
||||
gl_context.drawArrays(gl_context.POINTS, 0, n)
|
||||
}
|
||||
|
||||
function shadersFromScriptElement(gl, ID, type){
|
||||
shaderScript = document.getElementById(ID)
|
||||
var str = ''
|
||||
var k = shaderScript.firstChild;
|
||||
while(k){
|
||||
if (k.nodeType == 3){
|
||||
str += k.textContent;
|
||||
}
|
||||
k = k.nextSibling
|
||||
}
|
||||
var shader = gl.createShader(type)
|
||||
gl.shaderSource(shader, str)
|
||||
gl.compileShader(shader)
|
||||
return shader
|
||||
}
|
||||
|
||||
function getContext(canvasWidget){
|
||||
var names = ['webgl', 'experimental-webgl', 'webkit-3d', 'moz-webgl'];
|
||||
for(var i=0; i<names.length; i++){
|
||||
try{
|
||||
var gl = canvasWidget.getContext(names[i])
|
||||
}catch(e){}
|
||||
if(gl){i=names.length}
|
||||
}
|
||||
|
||||
var vshader = shadersFromScriptElement(gl, 'vertex-shader', gl.VERTEX_SHADER),
|
||||
fshader = shadersFromScriptElement(gl, 'fragment-shader', gl.FRAGMENT_SHADER)
|
||||
program = gl.createProgram();
|
||||
gl.attachShader(program, vshader)
|
||||
gl.attachShader(program, fshader)
|
||||
gl.linkProgram(program)
|
||||
gl.useProgram(program)
|
||||
gl.program = program
|
||||
return gl
|
||||
}
|
||||
|
||||
function initContext(gl){
|
||||
n = current_data_buffer.length / 6
|
||||
var vertexColourBuffer = gl.createBuffer()
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, vertexColourBuffer)
|
||||
gl.bufferData(gl.ARRAY_BUFFER, current_data_buffer, gl.STATIC_DRAW)
|
||||
|
||||
var FSIZE = data_buffer.BYTES_PER_ELEMENT;
|
||||
|
||||
var a_Position = gl.getAttribLocation(gl.program, 'a_Position')
|
||||
gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE * 6, 0)
|
||||
gl.enableVertexAttribArray(a_Position)
|
||||
|
||||
var a_Color = gl.getAttribLocation(gl.program, 'a_Color')
|
||||
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, 3 * FSIZE)
|
||||
gl.enableVertexAttribArray(a_Color)
|
||||
|
||||
u_basePointSize = gl.getUniformLocation(gl.program, 'u_basePointSize')
|
||||
gl.uniform1f(u_basePointSize, particleSize)
|
||||
|
||||
u_Alpha = gl.getUniformLocation(gl.program, "u_Alpha")
|
||||
gl.uniform1f(u_Alpha, alphaValue)
|
||||
|
||||
u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
|
||||
u_ViewMatrix = gl.getUniformLocation(gl.program, 'u_ViewMatrix');
|
||||
u_ProjMatrix = gl.getUniformLocation(gl.program, 'u_ProjMatrix');
|
||||
|
||||
modelMatrix = new Matrix4(); // The model matrix
|
||||
viewMatrix = new Matrix4(); // The view matrix
|
||||
projMatrix = new Matrix4(); // The projection matrix
|
||||
|
||||
modelMatrix.setTranslate(0, 0, 0); //
|
||||
viewMatrix.setLookAt(eyeX, eyeY, eyeZ, 0, 0, 0, upX, upY, upZ); // eyeX, eyeY, eyeZ, camX, camY, camZ, upX, upY, upY
|
||||
projMatrix.setPerspective(30, canvas.width/canvas.height, 100, farField); // fov, ratio, near, far
|
||||
// Pass the model, view, and projection matrix to the uniform variable respectively
|
||||
gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);
|
||||
gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);
|
||||
gl.uniformMatrix4fv(u_ProjMatrix, false, projMatrix.elements);
|
||||
|
||||
gl.clearColor(1, 1, 1, 1); // add ternary conditional
|
||||
|
||||
gl.enable(gl.DEPTH_TEST)
|
||||
gl.enable(gl.BLEND)
|
||||
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
|
||||
//gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)
|
||||
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
return gl
|
||||
}
|
||||
|
||||
var canvas = document.getElementById('canvas'),
|
||||
particleSize = 150,
|
||||
alphaValue = 1.0,
|
||||
bg_color = "dark",
|
||||
eyeX = 0.0,
|
||||
eyeY = 0.0,
|
||||
eyeZ = 400.0,
|
||||
upX = 0.0,
|
||||
upY = 1.0,
|
||||
upZ = 0.0,
|
||||
eyeVN = 400.0,
|
||||
farField = 500.0,
|
||||
previousX = null,
|
||||
previousY = null,
|
||||
currentX = null,
|
||||
currentY = null;
|
||||
|
||||
data_buffer = new Float32Array([
|
||||
datahere
|
||||
])
|
||||
|
||||
index_table = []
|
||||
indiceshere
|
||||
|
||||
current_data_buffer = data_buffer
|
||||
|
||||
gl_context = getContext(canvas)
|
||||
gl_context = initContext(gl_context)
|
||||
gl_context.drawArrays(gl_context.POINTS, 0, n)
|
||||
|
||||
function negCrossProduct(vecA, vecB){
|
||||
crossproduct = [ - vecA[1] * vecB[2] + vecA[2] * vecB[1],
|
||||
- vecA[2] * vecB[0] + vecA[0] * vecB[2],
|
||||
- vecA[0] * vecB[1] + vecA[1] * vecB[0]
|
||||
]
|
||||
return(crossproduct)
|
||||
}
|
||||
|
||||
function vectNorm(vector){
|
||||
return(Math.sqrt((vector[0] * vector[0]) + (vector[1] * vector[1]) + (vector[2] * vector[2])))
|
||||
}
|
||||
|
||||
function rotateData(hAngle, vAngle){
|
||||
// change vector for very small angles is approximately the cross product of the eye vector and up vector
|
||||
change = negCrossProduct([eyeX, eyeY, eyeZ], [upX, upY, upZ])
|
||||
// normalize the change vector
|
||||
normChange = vectNorm(change)
|
||||
// scale the change vector by the horizontal angle
|
||||
change = [hAngle * change[0]/normChange, hAngle * change[1]/normChange, hAngle * change[2]/normChange]
|
||||
// update the eye vector by adding the change vector
|
||||
eyeX = eyeX - change[0]
|
||||
eyeY = eyeY - change[1]
|
||||
eyeZ = eyeZ - change[2]
|
||||
// renormalize the eye vector, otherwise it will increase with each change (due to approx error)
|
||||
normEye = vectNorm([eyeX, eyeY, eyeZ])
|
||||
eyeX = eyeVN * eyeX / normEye
|
||||
eyeY = eyeVN * eyeY / normEye
|
||||
eyeZ = eyeVN * eyeZ / normEye
|
||||
|
||||
// get the (eye, up) plane normal
|
||||
planeInvNormal = negCrossProduct([eyeX, eyeY, eyeZ], [upX, upY, upZ])
|
||||
// in the case of vertical angle, the up vector is already the change vector
|
||||
normChange = vectNorm([upX, upY, upZ])
|
||||
change = [vAngle * upX / normChange, vAngle * upY / normChange, vAngle * upZ / normChange]
|
||||
// update the eye vector by adding the change vector
|
||||
eyeX = eyeX + change[0]
|
||||
eyeY = eyeY + change[1]
|
||||
eyeZ = eyeZ + change[2]
|
||||
// renormalize the eye vector, other it will increase with each change (due to approx error)
|
||||
normEye = Math.sqrt((eyeX * eyeX)+(eyeY * eyeY)+(eyeZ * eyeZ))
|
||||
eyeX = eyeVN * eyeX / normEye
|
||||
eyeY = eyeVN * eyeY / normEye
|
||||
eyeZ = eyeVN * eyeZ / normEye
|
||||
// but the up vector needs changing as well
|
||||
newUp = negCrossProduct([eyeX, eyeY, eyeZ], planeInvNormal)
|
||||
newUpNormal = vectNorm(newUp)
|
||||
upX = -newUp[0] / newUpNormal
|
||||
upY = -newUp[1] / newUpNormal
|
||||
upZ = -newUp[2] / newUpNormal
|
||||
|
||||
gl_context.clear(gl_context.COLOR_BUFFER_BIT);
|
||||
viewMatrix.setLookAt(eyeX, eyeY, eyeZ, 0, 0, 0, upX, upY, upZ);
|
||||
projMatrix.setPerspective(30, canvas.width/canvas.height, 100, farField);
|
||||
gl_context.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);
|
||||
gl_context.uniformMatrix4fv(u_ProjMatrix, false, projMatrix.elements);
|
||||
gl_context.drawArrays(gl_context.POINTS, 0, n);
|
||||
}
|
||||
|
||||
function startRotating(ev){
|
||||
previousX = ev.clientX
|
||||
previousY = ev.clientY
|
||||
canvas.addEventListener('mousemove', rotateEvent)
|
||||
canvas.addEventListener('mouseup', stopRotation)
|
||||
canvas.addEventListener('mouseout', stopRotation)
|
||||
}
|
||||
|
||||
function stopRotation(ev){
|
||||
canvas.removeEventListener('mousemove', rotateEvent)
|
||||
canvas.removeEventListener('mouseup', stopRotation)
|
||||
canvas.removeEventListener('mouseout', stopRotation)
|
||||
}
|
||||
|
||||
function rotateEvent(ev){
|
||||
currentX = ev.clientX
|
||||
currentY = ev.clientY
|
||||
var dX = currentX - previousX,
|
||||
dY = currentY - previousY;
|
||||
rotateData(2.0 * dX, 2.0 * dY)
|
||||
previousX = currentX;
|
||||
previousY = currentY;
|
||||
}
|
||||
|
||||
canvas.addEventListener('mousedown', startRotating)
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
data = pd.read_csv(data_frame_fname, index_col = None)
|
||||
|
||||
# convert Colours to r, g, b values, then to floats < 1.0
|
||||
def hexdec_to_1floats(hexdec):
|
||||
return np.array([int(hexdec[1:][i:(i+2)], 16) for i in (0, 2, 4)]) / 255.0
|
||||
|
||||
# map Labels to colours
|
||||
labels = sorted(list(data.Labels.unique()))
|
||||
index_table = []
|
||||
radio_commands = []
|
||||
for index, label in enumerate(labels):
|
||||
indices = data.Labels == label
|
||||
indices = indices.values
|
||||
indices = np.where(indices)
|
||||
indices = ','.join([str(i) for i in indices[0]])
|
||||
indices = "[{indices}]".format(indices = indices)
|
||||
index_table.append("index_table['{label}'] = {indices}".format(label = label, indices = indices))
|
||||
colour = data.Colours[data.Labels == label].values[0]
|
||||
radio_command = "<div style='background-color:{colour}'><input style='float:left' type='checkbox' id='{label}' checked onchange='selectData()' /><label style='float:left' for='{label}'>{label}: </label><br /></div>".format(colour = colour, label = label)
|
||||
radio_commands.append(radio_command)
|
||||
index_table = ';\n '.join(index_table)
|
||||
radio_commands = '\n '.join(radio_commands)
|
||||
|
||||
# make data string
|
||||
coordinates = data.values[:, 0:3].astype('float32')
|
||||
# next few steps are compressing the data into a stadard cube centered at (0,0,0) and L = 200
|
||||
Xrange = np.percentile(coordinates[:, 0], q = [1, 99]) * 1.2
|
||||
Yrange = np.percentile(coordinates[:, 1], q = [1, 99]) * 1.2
|
||||
Zrange = np.percentile(coordinates[:, 2], q = [1, 99]) * 1.2
|
||||
center = np.tile(np.array([np.mean(Xrange), np.mean(Yrange), np.mean(Zrange)]),
|
||||
(coordinates.shape[0], 1))
|
||||
coordinates = coordinates - center
|
||||
Xrange = Xrange[1] - Xrange[0]
|
||||
Yrange = Yrange[1] - Yrange[0]
|
||||
Zrange = Zrange[1] - Zrange[0]
|
||||
maxRange = max((Xrange, Yrange, Zrange))
|
||||
ratio = 180.0 / maxRange
|
||||
coordinates = coordinates * ratio
|
||||
|
||||
# next few steps the buffer data is created as string
|
||||
colours = data.values[:, 4]
|
||||
buffer_data = []
|
||||
for index in range(coordinates.shape[0]):
|
||||
coordinate = [str(i) for i in coordinates[index, :]]
|
||||
colour = [str(i) for i in hexdec_to_1floats(colours[index]).astype('float32')]
|
||||
vertex_data = coordinate + colour
|
||||
buffer_data.append(",".join(vertex_data))
|
||||
buffer_data = ",".join(buffer_data)
|
||||
|
||||
template_str = template.replace('datahere', buffer_data)
|
||||
template_str = template_str.replace('indiceshere', index_table)
|
||||
template_str = template_str.replace('radiocommands', radio_commands)
|
||||
with open(save_to, 'w') as result:
|
||||
result.write(template_str)
|
||||
|
||||
23
tools/predict_by_classifier/predict.py
Executable file
23
tools/predict_by_classifier/predict.py
Executable file
|
|
@ -0,0 +1,23 @@
|
|||
import sys
|
||||
args = sys.argv
|
||||
model_fname = args[1]
|
||||
data_fname = args[2]
|
||||
predictions_fname = args[3]
|
||||
pca_fname = args[4]
|
||||
|
||||
import pickle
|
||||
import pandas as pd
|
||||
|
||||
print("loading model ...")
|
||||
model = pickle.loads(open(model_fname, "rb").read())
|
||||
pca = pickle.loads(open(pca_fname, "rb").read())
|
||||
|
||||
print("loading data....")
|
||||
predictions = pd.read_csv(data_fname, sep = ",", index_col = 0).values
|
||||
print("classifying cells ... ")
|
||||
predictions = pca.transform(predictions);
|
||||
predictions = model.predict(predictions)
|
||||
print("writing results to disk...")
|
||||
df = pd.DataFrame(predictions)
|
||||
df.to_csv(predictions_fname)
|
||||
print("Classification finished. Python will now say goodbye. Handing over to R.")
|
||||
20
tools/umap/umap_compute.py
Executable file
20
tools/umap/umap_compute.py
Executable file
|
|
@ -0,0 +1,20 @@
|
|||
import sys
|
||||
args = sys.argv
|
||||
|
||||
pca_data_fname = args[1]
|
||||
umap_coordinates_fname = args[2]
|
||||
|
||||
import pandas as pd
|
||||
import umap
|
||||
|
||||
print("Loading data ...")
|
||||
pca = pd.read_csv(pca_data_fname, sep = ",", index_col = 0).values
|
||||
|
||||
print("creating UMAP ... ")
|
||||
embedding = umap.UMAP(n_neighbors = 5, min_dist = .3, metric = "correlation", random_state = 17)
|
||||
embedding = embedding.fit_transform(pca)
|
||||
|
||||
print("saving UMAP coordinates to disk ...")
|
||||
df = pd.DataFrame(embedding)
|
||||
df.columns = ["UMAPx", "UMAPy"]
|
||||
df.to_csv(umap_coordinates_fname)
|
||||
Loading…
Add table
Add a link
Reference in a new issue