Published
Edited
Dec 20, 2021
Insert cell
Insert cell
mutable save_lasso = false
Insert cell
default_name_lasso = 'region-0'
Insert cell
mutable saved_lasso_name = ''
Insert cell
lasso_scale_x = d3.scaleLinear()
.domain([0, deckgl_width])
.range([view_bounds[0], view_bounds[1]])
Insert cell
lasso_scale_y = d3.scaleLinear()
.domain([0, deckgl_height])
.range([view_bounds[2], view_bounds[3]])
Insert cell
drawn_polygon_scaled
Insert cell
import {colorPicker} from "@vizgen/color-picker"
Insert cell
color_picker
Insert cell
// d3.select('#container').empty()
Insert cell
visible_heatmap = true
Insert cell
{if (visible_heatmap === true){
d3.select('#heatmap_container').style('display', 'block')
} else {
d3.select('#heatmap_container').style('display', 'none')
}}
Insert cell
mutable delete_lasso = false
Insert cell
mutable clicked_lasso = null
Insert cell
select_meta
Insert cell
Insert cell
Insert cell
drawn_polygon_scaled
Insert cell
drawn_polygon_scaled = drawn_polygon.map(x => [lasso_scale_x(x[0]), lasso_scale_y(x[1])])
Insert cell
deckgl_polygon = ({"coordinates":[], "name": default_name_lasso, "color":'red'})
Insert cell
deckgl_polygons = [deckgl_polygon]
Insert cell
Insert cell
bar_opacity = 0.5
Insert cell
viewof gene_bar_chart = {
const svg = d3.create("svg")
.attr("width", width)
.attr("height", y_bar_gene.range()[1])
.attr("font-family", "sans-serif")
.attr("font-size", "16")
.attr("text-anchor", "end");
// initialized
svg.property('value', {
'term_name': 'Select Term',
'term_genes': []
})

const bar = svg.selectAll("g")
.data(gene_counts)
// .transition()
// .duration(1000)
// .ease(d3.easeLinear)
.join("g")
.attr("transform", (d, i) => `translate(0,${y_bar_gene(i)})`)
.on('click', function(d){

var inst_name = d3.select(this).text().split(': ')[0]

if (mutable select_meta !== inst_name){
mutable select_meta = inst_name
// mutable selected_leidens = []
// d3.select('#meta_dropdown').select('select').node().value = select_meta
} else {
mutable select_meta = 'leiden'
// mutable selected_leidens = []
// d3.select('#meta_dropdown').select('select').node().value = 'leiden'
}

})
.style('cursor', 'pointer')
bar.append("rect")
.attr('fill', d => cat_colors[d.name] )
.attr('opacity', bar_opacity)
.attr("width", function(d){return x_bar_gene(d.value)})
.attr("height", y_bar_gene.bandwidth() - 1)
.style('cursor', 'pointer')

bar.append("text")
.attr("fill", 'black')
.attr("x", '5px')
.attr("y", y_bar_gene.bandwidth() / 2)
.attr("dy", "0.35em")
.attr('text-anchor', 'start')
// .text(d => d.name + ': ' + String(d.value))
.text(d => d.name)
.style('cursor', 'pointer')
.style('user-select', 'none')

return svg.node();
}
Insert cell
viewof cluster_bar_chart = {
const svg = d3.create("svg")
.attr("width", width)
.attr("height", y_bar_cluster.range()[1])
.attr("font-family", "sans-serif")
.attr("font-size", "16")
.attr("text-anchor", "end");
// initialized
svg.property('value', {
'term_name': 'Select Term',
'term_genes': []
})

const bar = svg.selectAll("g")
.data(cluster_counts)
.join("g")
.attr("transform", (d, i) => `translate(0,${y_bar_cluster(i)})`)
.on('click', function(d){

var inst_name = d3.select(this).text().split(': ')[0]

if (mutable selected_leidens[0] !== inst_name){
mutable select_meta = 'leiden'
mutable selected_leidens = [inst_name]
// d3.select('#meta_dropdown').select('select').node().value = 'none'
} else {
mutable select_meta = 'leiden'
mutable selected_leidens = []
// d3.select('#meta_dropdown').select('select').node().value = 'leiden'
}

})
.style('cursor', 'pointer')
bar.append("rect")
.attr('fill', d => cat_colors[d.name] )
.attr('opacity', bar_opacity)
.attr("width", function(d){return x_bar_cluster(d.value)})
.attr("height", y_bar_cluster.bandwidth() - 1)
.style('cursor', 'pointer')

bar.append("text")
.attr("fill", 'black')
.attr("x", '5px')
.attr("y", y_bar_cluster.bandwidth() / 2)
.attr("dy", "0.35em")
.attr('text-anchor', 'start')
// .text(d => d.name + ': ' + String(d.value))
.text(d => d.name)
.style('cursor', 'pointer')
.style('user-select', 'none')

return svg.node();
}
Insert cell
Insert cell
Insert cell
max_bar_width = 100
Insert cell
Insert cell
x_bar_cluster = d3.scaleLinear()
.domain([0, d3.max(cluster_counts.map(x => x.value))])
.range([0, max_bar_width])
Insert cell
y_bar_cluster = d3.scaleBand()
.domain(d3.range(cluster_counts.map(x => x.value).length))
.range([0, 22 * cluster_counts.map(x => x.value).length])
Insert cell
Insert cell
x_bar_gene = d3.scaleLinear()
.domain([0, d3.max(gene_counts.map(x => x.value))])
.range([0, max_bar_width])
Insert cell
y_bar_gene = d3.scaleBand()
.domain(d3.range(gene_counts.map(x => x.value).length))
.range([0, 22 * gene_counts.map(x => x.value).length])
Insert cell
Insert cell
Insert cell
deckgl_width = 800
Insert cell
deckgl_height = 800
Insert cell
Insert cell
mutable inst_zoom = zoom
Insert cell
// initialize view_bounds
mutable view_bounds = {
var height = deckgl_height
var width = deckgl_width

var ini_zoom = Math.pow(2, zoom)

var target_x = ini_x
var target_y = ini_y
var min_x = target_x - width/(2 * ini_zoom)
var max_x = target_x + width/(2 * ini_zoom)

var min_y = target_y - height/(2 * ini_zoom)
var max_y = target_y + height/(2 * ini_zoom)

var ini_bounds = [min_x, max_x, min_y, max_y]
return ini_bounds
}

Insert cell
function something(viewState){
var height = viewState.height
var width = viewState.width

// log2 zoom for python
mutable inst_zoom = viewState.zoom
var zoom = Math.pow(2, viewState.zoom)

var target_x = viewState.target[0]
var target_y = viewState.target[1]
var min_x = target_x - width/(2*zoom)
var max_x = target_x + width/(2*zoom)

var min_y = target_y - height/(2*zoom)
var max_y = target_y + height/(2*zoom)

mutable view_bounds = [min_x, max_x, min_y, max_y]

}
Insert cell
debounce_time = 250
Insert cell
debounced_something = debounce(something, debounce_time);
Insert cell
function debounce(func, wait, immediate) {
// 'private' variable for instance
// The returned function will be able to reference this due to closure.
// Each call to the returned function will share this common timer.
var timeout;

// Calling debounce returns a new anonymous function
return function() {
// reference the context and args for the setTimeout function
var context = this,
args = arguments;

// Should the function be called now? If immediate is true
// and not already in a timeout then the answer is: Yes
var callNow = immediate && !timeout;

// This is the basic debounce behaviour where you can call this
// function several times, but it will only execute once
// [before or after imposing a delay].
// Each time the returned function is called, the timer starts over.
clearTimeout(timeout);

// Set the new timeout
timeout = setTimeout(function() {

// Inside the timeout function, clear the timeout variable
// which will let the next execution run when in 'immediate' mode
timeout = null;

// Check if the function already ran with the immediate flag
if (!immediate) {
// Call the original function with apply
// apply lets you define the 'this' object as well as the arguments
// (both captured before setTimeout)
func.apply(context, args);
}
}, wait);

// Immediate mode and no wait timer? Execute the function..
if (callNow) func.apply(context, args);
}
}

Insert cell
Insert cell
mutable drawn_polygon = []
Insert cell
viewof state = {
const selection = d3.create("svg")
// .attr("viewBox", [0, 0, width, height])
.attr("viewBox", [0, 0, deckgl_width, deckgl_height])
.style('width', deckgl_width + 'px')
.style('height', deckgl_height + 'px');
var svg = selection.node();
var path = d3.geoPath();
var l = selection.append("path").attr("id", "lasso");
var g = selection.append("g");

selection
.append("defs")
.append("style")
.text(`
#lasso { fill-rule: evenodd; fill-opacity: 0.1; stroke-width: 1.5; stroke: #000; }
`);

function draw(polygon) {
l.datum({
type: "LineString",
coordinates: polygon
}).attr("d", path);

// svg.value = { polygon, selected };
svg.value = { polygon };
// svg.value = {};

mutable drawn_polygon = polygon
svg.dispatchEvent(new CustomEvent('input'));
}

selection.call(lasso().on("start lasso end", draw));

// // draw default lasso
// draw(default_lasso);

return svg;
}
Insert cell
function lasso() {
const dispatch = d3.dispatch("start", "lasso", "end");
const lasso = function(selection) {
const node = selection.node();
const polygon = [];

selection
.on("touchmove", e => e.preventDefault()) // prevent scrolling
.on("pointerdown", e => {
trackPointer(e, {
start: p => {
polygon.length = 0;
dispatch.call("start", node, polygon);

// start dispaying when lasso starts
d3.select('#lasso').style('display', 'block');

// display name lasso input
d3.select('#Name-Lasso')
.style('display', 'inline')

// turn on save lasso button
d3.select('#Save-Lasso')
.style('display', 'inline')
// .text('Save Lasso')

// turn on color_picker
d3.select('#Color-Picker')
.style('display', 'inline')
},
move: p => {
polygon.push(p.point);
dispatch.call("lasso", node, polygon);
},
end: p => {
dispatch.call("end", node, polygon);
}
});
});
};
lasso.on = function(type, _) {
return _ ? (dispatch.on(...arguments), lasso) : dispatch.on(...arguments);
};

return lasso;
}
Insert cell
default_lasso = []
Insert cell
lasso_data = {
const rngx = d3.randomNormal(width / 2, Math.min(width, height) / 3),
rngy = d3.randomNormal(height / 2, Math.min(width, height) / 3);
return Array.from({ length: 500 }, () => [rngx(), rngy()]);
}
Insert cell
// import { trackPointer } from "@fil/pointer-events"
import { trackPointer } from "@vizgen/pointer-events"
Insert cell
Insert cell
container = html `<div id='the-container' style="height:800px; width:800px; border-style:solid; border-color:#d3d3d3; border-width:1px; position: relative;"></div>`
Insert cell
Insert cell
Insert cell
data_ini = obs_data['data']
Insert cell
data = data_ini.map(x => {
var inst_obj = ({
'name': x['name'],
'leiden': x['leiden'],
'x': x['x'],
'y': x['y'],
'umap-x': x['umap-x'],
'umap-y': x['umap-y'],
'tooltip': x['leiden']
})

return inst_obj
})
Insert cell
Insert cell
Insert cell
Insert cell
// // cell_data = inputs['data'].map(x => {
// // x.tooltip_name = 'Leiden-' + x.leiden
// // return x
// // })

// cell_data = data.map(x => {
// x.tooltip_name = 'Leiden-' + x.leiden
// return x
// })
Insert cell
Insert cell
cat_name = 'leiden'
// cat_name = 'name'
Insert cell
mutable select_cat = 'all'
Insert cell
Insert cell
Insert cell
Insert cell
check_ini_cat = ini_cat
Insert cell
Insert cell
// radius_min_pixels = 5
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
cell_metadata = Object.keys(data[0]).filter(x => non_param_cols.includes(x) === false)
Insert cell
meta_data_cols = cell_metadata.concat(all_genes)
Insert cell
all_genes = Object.keys(gex_dict).sort()
Insert cell
mutable selected_leidens = []
Insert cell
zoom = 1
Insert cell
min_zoom = zoom - 1
Insert cell
cell_opacity = 0.9
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// viewof cell_opacity = slider({
// value: 1.00,
// min: 0.0,
// max: 1.0,
// precision: 2,
// description: "cell opacity",
// })
Insert cell
ini_min_radius = 1
Insert cell
viewof cell_min_radius = slider({
value: ini_min_radius,
min: 0.1,
max: 5.0,
precision: 2,
description: "min cell size"
})
Insert cell
Insert cell
viewof select_meta_dropdown = select({'options':['none'].concat(meta_data_cols), value: ini_cat})
Insert cell
Insert cell
ini_draw_state = ''
Insert cell
default_draw_text_color = '#1e4057' //'#142b3a'
Insert cell
// // initialize Save Lasso button as not visible
// d3.select('#Save-Lasso')
// .style('display', 'none')
Insert cell
Insert cell
viewof map_type = {
const element = html`
<div style="display: inline-block; user-select: none;"></div>`;

d3.select(element)
.style('padding-right', '25px')
.selectAll('div')
.data(['UMAP', '/','Spatial'])
.join('span')
.on('click', function(d){
if (d3.select(this).text() !== '/'){
d3.select(element).selectAll('span')
.style('color', '#808080')
d3.select(this)
.style('color', 'blue')
element.value = d3.select(this).attr('id'); // d//.replace(', ', '')
element.dispatchEvent(new CustomEvent("input"));
}
})
.style('min-width', '200px')
.style('max-width', '200px')
.style('margin-right', '2px')
.text(d => d)
.style('font-weight', '550px')
.style("font-family", "sans-serif")
.style("font-size", "16")
.style("text-anchor", "end")
.style('font-weight', 'bold')
.style('color', d => d === ini_map_type ? 'blue': '#808080')
.attr('id', d => d)
element.value = ini_map_type
element.dispatchEvent(new CustomEvent("input"))

return element
}
Insert cell
Insert cell
data
Insert cell
cell_layer = new deck.ScatterplotLayer({
id: 'cell_layer',
// data: cell_data,
data: data,
getPosition: apply_map_morph,


getFillColor: d => {
var inst_color = [0, 255, 0, 255]
if (select_meta === 'none'){

// default coloring
////////////////////////////////////////
inst_color = [50, 50, 50, 100]

} else if (select_meta === 'leiden') {
if (selected_leidens.length === 0){
// single leiden
///////////////////
var rgb = d3.color(cat_colors[d[select_meta]])
inst_color = [rgb.r, rgb.g, rgb.b, 255 * cat_opacity]
} else {
// multiple leidens
/////////////////////
if (selected_leidens.includes(d['leiden'])){
var rgb = d3.color(cat_colors[d[select_meta]])
inst_color = [rgb.r, rgb.g, rgb.b, 255 * cat_opacity]
} else {
inst_color = [50, 50, 50, 10]
}

}
} else if (select_meta === 'mean-gene') {

// mean gene expression
////////////////////////////////////////
inst_color = [0, 0, 0, 255]
} else {
// single gene expression
////////////////////////////////////////
if (all_genes.includes(select_meta)){
if (d.name in gex_dict[select_meta]){
var inst_expression = gex_dict[select_meta][d.name]
var inst_opacity = opacity_scale(inst_expression)
inst_color = [255, 0, 0, inst_opacity]
} else {
inst_color = [50, 50, 50, 2]
}
}
}
return inst_color
},
getRadius: cell_radius,
pickable: is_pickable,
highlightColor: d => [50, 50, 50],
radiusMinPixels: cell_min_radius,
opacity: cell_opacity,
updateTriggers: {
getFillColor: [select_meta, selected_leidens], // , mean_gene_data],
getPosition: map_type,
},
transitions: transitions,
onClick: (info, event) => {
if (mutable selected_leidens[0] !== info.object[cat_name]){
mutable select_meta = 'leiden'
mutable selected_leidens = [info.object[cat_name]]
d3.select('#meta_dropdown').select('select').node().value = 'none'
} else {
mutable select_meta = 'leiden'
mutable selected_leidens = []
d3.select('#meta_dropdown').select('select').node().value = 'leiden'
}
}
})
Insert cell
polygon_layer = new deck.PolygonLayer({
id: 'polygon_layer',
data: deckgl_polygons,
getFillColor: d => {
var color_name = d.color
var rgb_array = d3.color(color_name)
var rgb = [rgb_array.r, rgb_array.g, rgb_array.b]

var inst_color = rgb
return inst_color
},
// getLineColor: d => {
// var color_name = cluster_colors[d.neighborhood]
// var rgb_array = d3.color(color_name)
// var rgb = [rgb_array.r/2, rgb_array.g/2, rgb_array.b/2]
// return rgb
// },
// getLineWidth: d => poly_line_width,
lineWidthMinPixels: 1,
getPolygon: d => d.coordinates,
updateTriggers: {
// getFillColor: select_cluster,
getFillColor: selected_leidens,
},
stroked: false,
filled: true,
pickable: true,
// opacity: neighborhood_opacity,
onClick: (info, event) => {

if (d3.select('#Delete-Lasso').style('display') === 'none'){
d3.select('#Delete-Lasso')
.style('display', 'inline')
}
else if (d3.select('#Delete-Lasso').style('display') === 'inline'){
d3.select('#Delete-Lasso')
.style('display', 'none')
}

mutable clicked_lasso = info.object.name

}
});
Insert cell
Insert cell
Insert cell
layers = [polygon_layer, cell_layer]
Insert cell
Insert cell
deckgl.setProps({layers: layers});
Insert cell
Insert cell
Insert cell
Insert cell
deckgl = {
console.log('--- deckgl\n----------')
var deckgl = new deck.DeckGL({
container,
width: '800px',
height: '800px',
top: '175px',
views:[view],
initialViewState: initial_view_state,
// controller: true,
controller: {doubleClickZoom: false},
// getTooltip: ({object}) => {
// return object &&
// `${object['name']}`
// // `pos: ${object.x}, ${object['y']}\n`
// },
getTooltip: ({object}) => object && {
html: `${object.tooltip}`,
style: {
fontSize: '0.8em',
padding: '5px',
}
},
onViewStateChange: ({viewState}) => {
debounced_something(viewState)
return viewState
},

});

return deckgl
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
gex_opacity_contrast_scale = 1
Insert cell
opacity_scale = d3.scaleLinear()
.domain([0, meta_max * gex_opacity_contrast_scale])
.range([0, 255])
Insert cell
meta_max = {
var meta_max
if (select_meta in gex_dict){
meta_max = d3.max(Object.values(gex_dict[select_meta]))
} else if (select_meta === 'mean-gene') {

// set up calculation of mean gene max
meta_max = 0
} else {
meta_max = 0
}
return meta_max
}
Insert cell
Insert cell
Insert cell
// row_selection = Generators.observe(notify => {

// const mousemove = () => {
// // if (cgm.params.tooltip.tooltip_type.includes('dendro')){
// if (cgm.params.tooltip.tooltip_type === 'row-dendro'){
// mutable select_meta = 'mean-gene'
// mutable dendro_selected_genes = cgm.params.dendro.selected_clust_names
// }
// }
// viewof cgm.addEventListener("click", mousemove);
// // notify(cgm.params.dendro.selected_clust_names.map(x => x.replace('Leiden-', '')));

// return () => viewof cgm.removeEventListener("mousemove", mousemove);
// })
Insert cell
col_selection = Generators.observe(notify => {

const mousemove = () => {
if (cgm.params.tooltip.tooltip_type === 'col-dendro'){
mutable select_meta = 'leiden'
mutable dendro_selected_genes = []
mutable selected_leidens = cgm.params.dendro.selected_clust_names.map(x => x.replace('Leiden-', ''))
}

}
viewof cgm.addEventListener("click", mousemove);
notify(cgm.params.dendro.selected_clust_names.map(x => x.replace('Leiden-', '')));

return () => viewof cgm.removeEventListener("mousemove", mousemove);
})
Insert cell
Insert cell
heatmap_row = Generators.observe(notify => {
const mouse_click = () => {

// clicking gene row
//////////////////////////////
if (cgm.params.tooltip.tooltip_type === 'row-label'){
var inst_name = cgm.params.int.mouseover.row.name
mutable dendro_selected_genes = []
mutable select_meta = inst_name
d3.select('#meta_dropdown').select('select').node().value = inst_name
}
notify(cgm.params.int.mouseover.row.name)
}
viewof cgm.addEventListener("click", mouse_click);
notify(cgm.params.int.mouseover.row.name);

return () => viewof cgm.removeEventListener("mousemove", mouse_click);
})
Insert cell
heatmap_col = Generators.observe(notify => {
const mouse_click = () => {

// clicking cluster col
//////////////////////////////
if (cgm.params.tooltip.tooltip_type === 'col-label'){
mutable select_meta = 'leiden'
mutable dendro_selected_genes = []
mutable selected_leidens = [cgm.params.int.mouseover.col.name.replace('Leiden-', '')]

// if (mutable selected_leidens[0] !== info.object[cat_name]){
// mutable select_meta = 'leiden'
// mutable selected_leidens = [info.object[cat_name]]
// d3.select('#meta_dropdown').select('select').node().value = 'none'
// } else {
// mutable select_meta = 'leiden'
// mutable selected_leidens = []
// d3.select('#meta_dropdown').select('select').node().value = 'leiden'
// }
}
notify([cgm.params.int.mouseover.col.name])
}
viewof cgm.addEventListener("click", mouse_click);
notify([cgm.params.int.mouseover.col.name]);

return () => viewof cgm.removeEventListener("mousemove", mouse_click);
})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// d3 = require("d3@5")
// this is changing the behavior of D3 selections
// d3 = require("d3@5", "d3-selection@2.0.0-rc.2")
// d3 = require("d3@5", "d3-selection")
d3 = require('d3@5', 'd3-selection@2')
Insert cell
Insert cell
pako = require('pako/dist/pako.min.js')
Insert cell
import {slider, select} from '@jashkenas/inputs'
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more