Skip to content

Commit

Permalink
DEGA-49-iST-UMAP-Landscape-Matrix (#54)
Browse files Browse the repository at this point in the history
* adding spatialdata and spatialdata_io as requirements

* adding meta_cell and meta_cluster inputs for Landscape for pre-set custom data

* adding notes on where meta_cell and meta_cluster will be processed

* passing meta_cell into landscape_ist

* using meta_cell to set custom cell categories on ini, working on meta_cluster parsing

* custom cluster data is being brought in

* hacky umap overwrite position

* cleaning up umap coordinates

* adding umap/spatial button functions

* minimal UMAP/SPATIAL toggling

* v0.8.0 umap and ini cell clustering setting
  • Loading branch information
cornhundred authored Jan 9, 2025
1 parent e1af5ee commit 84fb420
Show file tree
Hide file tree
Showing 12 changed files with 301 additions and 410,838 deletions.
168 changes: 84 additions & 84 deletions docs/assets/js/widget.js

Large diffs are not rendered by default.

86 changes: 77 additions & 9 deletions js/deck-gl/cell_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ const cell_layer_onclick = async (info, d, deck_ist, layers_obj, viz_state) => {
})
}

// toggle_spatial_umap(deck_ist, layers_obj, viz_state)

update_cell_layer_id(layers_obj, inst_cat_name)
update_path_layer_id(layers_obj, inst_cat_name)
update_trx_layer_id(viz_state.genes, layers_obj)
Expand All @@ -89,26 +91,59 @@ export const ini_cell_layer = async (base_url, viz_state) => {
const cell_url = base_url + `/cell_metadata.parquet`;
var cell_arrow_table = await get_arrow_table(cell_url, options.fetch)

set_cell_names_array(viz_state.cats, cell_arrow_table)

let coords
let cell_umap_data

// let cell_scatter_data
if (viz_state.umap.has_umap){

const flatCoordinateArray_umap = new Float64Array(
viz_state.cats.cell_names_array.flatMap(cell_id => {

if (!viz_state.umap.umap[cell_id]) {
coords = [0, 0];
} else {
coords = viz_state.umap.umap[cell_id];
}

return coords; // Add UMAP coordinates [x, y]
})
);

// Create the scatter_data object
cell_umap_data = {
length: viz_state.cats.cell_names_array.length,
attributes: {
getPosition: { value: flatCoordinateArray_umap, size: 2 },
}
};

viz_state.umap.cell_umap_data = cell_umap_data

}

const cell_scatter_data = get_scatter_data(cell_arrow_table)

await set_color_dict_gene(viz_state.genes, base_url)
viz_state.umap.cell_scatter_data = cell_scatter_data

set_cell_names_array(viz_state.cats, cell_arrow_table)
await set_color_dict_gene(viz_state.genes, base_url)

set_cell_name_to_index_map(viz_state.cats)

// default clustering
var cluster_arrow_table = await get_arrow_table(base_url + `/cell_clusters/cluster.parquet`, options.fetch)
if (viz_state.cats.has_meta_cell){
viz_state.cats.cell_cats = viz_state.cats.cell_names_array.map(name => viz_state.cats.meta_cell[name])
} else {
// default clustering
var cluster_arrow_table = await get_arrow_table(base_url + `/cell_clusters/cluster.parquet`, options.fetch)
set_cell_cats(viz_state.cats, cluster_arrow_table, 'cluster')
}

// setting a single cell category for now
// set_cell_cats(cluster_arrow_table, 'cluster')
set_cell_cats(viz_state.cats, cluster_arrow_table, 'cluster')
set_dict_cell_cats(viz_state.cats)

// Combine names and positions into a single array of objects
const new_cell_names_array = cell_arrow_table.getChild("name").toArray()

// need to save the cell category
const flatCoordinateArray = cell_scatter_data.attributes.getPosition.value;

// save cell positions and categories in one place for updating cluster bar plot
Expand All @@ -119,15 +154,37 @@ export const ini_cell_layer = async (base_url, viz_state) => {
y: flatCoordinateArray[index * 2 + 1]
}))

// console.log("Scatter data length:", cell_scatter_data.length);
// console.log("Scatter data position size:", cell_scatter_data.attributes.getPosition.size);
// console.log("Scatter data value length:", cell_scatter_data.attributes.getPosition.value.length);
// console.log("Expected value length:", cell_scatter_data.length * cell_scatter_data.attributes.getPosition.size);

// console.log("UMAP data length:", cell_umap_data.length);
// console.log("UMAP data position size:", cell_umap_data.attributes.getPosition.size);
// console.log("UMAP data value length:", cell_umap_data.attributes.getPosition.value.length);
// console.log("Expected value length:", cell_umap_data.length * cell_umap_data.attributes.getPosition.size);


const transitions = {
getPosition: {
duration: 3000,
easing: d3.easeCubic
}
}

let cell_layer = new ScatterplotLayer({
id: 'cell-layer',
radiusMinPixels: 1,
getRadius: 5.0,
pickable: true,
getColor: (i, d) => get_cell_color(viz_state.cats, i, d),
data: cell_scatter_data,
// data: cell_umap_data,
// transitions: transitions,
})



return cell_layer

}
Expand Down Expand Up @@ -160,4 +217,15 @@ export const update_cell_pickable_state = (layers_obj, pickable) => {
layers_obj.cell_layer = layers_obj.cell_layer.clone({
pickable: pickable,
});
}

export const toggle_spatial_umap = (deck_ist, layers_obj, viz_state) => {

layers_obj.cell_layer = layers_obj.cell_layer.clone({
data: viz_state.umap.state ? viz_state.umap.cell_umap_data : viz_state.umap.cell_scatter_data,
})

const layers_list = get_layers_list(layers_obj, viz_state.close_up)
deck_ist.setProps({layers: layers_list})

}
69 changes: 44 additions & 25 deletions js/global_variables/meta_cluster.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,39 +28,58 @@ export const update_meta_cluster = (cats, new_meta_cluster) => {

export const set_cluster_metadata = async (viz_state) => {

// will improve this file naming later
const meta_cell_url = viz_state.global_base_url + `/cell_clusters/meta_cluster.parquet`
if (viz_state.cats.has_meta_cluster) {

var meta_cell_arrow_table = await get_arrow_table(meta_cell_url, options.fetch)
// loop through the keys of meta_cluster and assemble a dictionary of colors use a map or something functional
for (const cluster_name in viz_state.cats.meta_cluster) {
viz_state.cats.color_dict_cluster[cluster_name] = hexToRgb(viz_state.cats.meta_cluster[cluster_name]['color'])
}

let cluster_names = []
let colors = []
let counts = []
// loop through the keys and assembe cluster_counts
for (const cluster_name in viz_state.cats.meta_cluster) {
viz_state.cats.cluster_counts.push({
name: cluster_name,
value: viz_state.cats.meta_cluster[cluster_name]['count']
})
}

const cluster_name_column = meta_cell_arrow_table.getChild('__index_level_0__')
const color_column = meta_cell_arrow_table.getChild('color')
const counts_column = meta_cell_arrow_table.getChild('count')

let column_names = []
for (const field of meta_cell_arrow_table.schema.fields) {
column_names.push(field.name)
}
} else {

if (cluster_name_column && color_column) {
cluster_names = cluster_name_column.toArray()
colors = color_column.toArray()
counts = counts_column.toArray()
}
// will improve this file naming later
const meta_cell_url = viz_state.global_base_url + `/cell_clusters/meta_cluster.parquet`

cluster_names.forEach((cluster_name, index) => {
viz_state.cats.color_dict_cluster[cluster_name] = hexToRgb(colors[index])
var meta_cell_arrow_table = await get_arrow_table(meta_cell_url, options.fetch)

viz_state.cats.cluster_counts.push({
name: cluster_name,
value: Number(counts[index])
})
let cluster_names = []
let colors = []
let counts = []

const cluster_name_column = meta_cell_arrow_table.getChild('__index_level_0__')
const color_column = meta_cell_arrow_table.getChild('color')
const counts_column = meta_cell_arrow_table.getChild('count')

let column_names = []
for (const field of meta_cell_arrow_table.schema.fields) {
column_names.push(field.name)
}

})
if (cluster_name_column && color_column) {
cluster_names = cluster_name_column.toArray()
colors = color_column.toArray()
counts = counts_column.toArray()
}

cluster_names.forEach((cluster_name, index) => {
viz_state.cats.color_dict_cluster[cluster_name] = hexToRgb(colors[index])

viz_state.cats.cluster_counts.push({
name: cluster_name,
value: Number(counts[index])
})

})
}

viz_state.cats.cluster_counts.sort((a, b) => b.value - a.value)

Expand Down
2 changes: 2 additions & 0 deletions js/read_parquet/get_scatter_data.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export const get_scatter_data = (arrow_table) => {
return combined;
}, new Float64Array(0));

// console.log(flatCoordinateArray/2)

const scatter_data = {
length: arrow_table.numRows,
attributes: {
Expand Down
36 changes: 34 additions & 2 deletions js/ui/text_buttons.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import { toggle_visibility_image_layers, toggle_visibility_single_image_layer }
// import { deck_sst } from '../deck-gl/deck_sst'
import { toggle_background_layer_visibility } from '../deck-gl/background_layer'
import { toggle_path_layer_visibility } from '../deck-gl/path_layer'
import { new_toggle_cell_layer_visibility } from '../deck-gl/cell_layer'
import { new_toggle_cell_layer_visibility, toggle_spatial_umap } from '../deck-gl/cell_layer'
import { toggle_trx_layer_visibility } from '../deck-gl/trx_layer'
import { get_layers_list } from '../deck-gl/layers_ist'
import { toggle_slider } from './sliders'
import { get_mat_layers_list } from '../deck-gl/matrix/matrix_layers'
import { toggle_dendro_layer_visibility } from '../deck-gl/matrix/dendro_layers'


let is_visible

let img_layer_visible = true
Expand Down Expand Up @@ -158,11 +159,16 @@ export const make_button = (container, technology, text, color='blue', width=40,
callback = (event) => trx_button_callback_ist(event, inst_deck, layers_obj, viz_state)
} else if (text === 'CELL'){
callback = (event) => cell_button_callback(event, inst_deck, layers_obj, viz_state)
} else if (text === 'UMAP') {
callback = (event) => umap_button_callback(event, inst_deck, layers_obj, viz_state)

} else if (text === 'SPATIAL') {
callback = (event) => spatial_button_callback(event, inst_deck, layers_obj, viz_state)
} else {
callback = make_ist_img_layer_button_callback(text, inst_deck, layers_obj, viz_state)
}

d3.select(container)
const inst_button = d3.select(container)
.append('div')
.attr('class', button_class)
.text(text)
Expand All @@ -178,6 +184,9 @@ export const make_button = (container, technology, text, color='blue', width=40,
.style('font-family', '-apple-system, BlinkMacSystemFont, "San Francisco", "Helvetica Neue", Helvetica, Arial, sans-serif;')
.on('click', callback)

const button_name = text.toLowerCase()
viz_state.buttons.buttons[button_name] = inst_button

}

export const make_edit_button = (deck_ist, layers_obj, viz_state, container, text, width, edit_button_callback) => {
Expand Down Expand Up @@ -309,3 +318,26 @@ const cell_button_callback = async (event, deck_ist, layers_obj, viz_state) => {
const layers_list = get_layers_list(layers_obj, viz_state.close_up)
deck_ist.setProps({layers: layers_list})
}

const umap_button_callback = async (event, deck_ist, layers_obj, viz_state) => {
viz_state.umap.state = true
toggle_spatial_umap(deck_ist, layers_obj, viz_state)

viz_state.buttons.buttons.umap.style('color', 'blue')
viz_state.buttons.buttons.spatial.style('color', 'gray')

// placeholder for turning off visibility on other layers
viz_state.buttons.buttons.img.node().click()
}

const spatial_button_callback = async (event, deck_ist, layers_obj, viz_state) => {
viz_state.umap.state = false
toggle_spatial_umap(deck_ist, layers_obj, viz_state)

viz_state.buttons.buttons.umap.style('color', 'gray')
viz_state.buttons.buttons.spatial.style('color', 'blue')

// click the img button
viz_state.buttons.buttons.img.node().click()

}
14 changes: 13 additions & 1 deletion js/ui/ui_containers.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,19 @@ export const make_ist_ui_container = (dataset_name, deck_ist, layers_obj, viz_st
const cell_slider_container = make_slider_container('cell_slider_container')
const trx_slider_container = make_slider_container('trx_slider_container')

make_button(viz_state.containers.image, 'ist', 'IMG', 'blue', 30, 'button', deck_ist, layers_obj, viz_state)
let spatial_toggle_container = flex_container('image_layer_container', 'row')

// make_button(viz_state.containers.image, 'ist', 'IMG', 'blue', 30, 'button', deck_ist, layers_obj, viz_state)
// make_button(viz_state.containers.image, 'ist', 'UMAP', 'blue', 30, 'button', deck_ist, layers_obj, viz_state)

if (viz_state.umap.has_umap === true){
make_button(spatial_toggle_container, 'ist', 'UMAP', 'gray', 35, 'button', deck_ist, layers_obj, viz_state)
make_button(spatial_toggle_container, 'ist', 'SPATIAL', 'blue', 50, 'button', deck_ist, layers_obj, viz_state)
}

make_button(spatial_toggle_container, 'ist', 'IMG', 'blue', 30, 'button', deck_ist, layers_obj, viz_state)

viz_state.containers.image.appendChild(spatial_toggle_container)

const get_slider_by_name = (img, name) => {
return img.image_layer_sliders.filter(slider => slider.name === name);
Expand Down
32 changes: 30 additions & 2 deletions js/viz/landscape_ist.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,12 @@ export const landscape_ist = async (
trx_radius=0.25,
width = 0,
height = 800,
meta_cell={},
meta_cluster={},
umap={},
view_change_custom_callback=null
) => {


if (width === 0){
width = '100%'
}
Expand All @@ -57,6 +59,7 @@ export const landscape_ist = async (
viz_state.buttons.blue = '#8797ff'
viz_state.buttons.gray = 'gray'
viz_state.buttons.light_gray = '#EEEEEE'
viz_state.buttons.buttons = {}

set_global_base_url(viz_state, base_url)

Expand Down Expand Up @@ -121,6 +124,32 @@ export const landscape_ist = async (
viz_state.cats.polygon_cell_names = []
viz_state.cats.svg_bar_cluster = d3.create("svg")

// check if meta_cell is an empty object
if (Object.keys(meta_cell).length === 0) {
viz_state.cats.has_meta_cell = false
} else {
viz_state.cats.has_meta_cell = true
}
viz_state.cats.meta_cell = meta_cell


if (Object.keys(meta_cluster).length === 0) {
viz_state.cats.has_meta_cluster = false
} else {
viz_state.cats.has_meta_cluster = true
}
viz_state.cats.meta_cluster = meta_cluster

viz_state.umap = {}
if (Object.keys(umap).length === 0) {
viz_state.umap.has_umap = false
} else {
viz_state.umap.has_umap = true
}
viz_state.umap.umap = umap

viz_state.umap.state = false

viz_state.genes = {}
viz_state.genes.color_dict_gene = {}
viz_state.genes.gene_names = []
Expand Down Expand Up @@ -242,7 +271,6 @@ export const landscape_ist = async (
set_edit_layer_on_click(deck_ist, layers_obj, viz_state)
set_nbhd_layer_onclick(deck_ist, layers_obj, viz_state)


update_trx_layer_radius(layers_obj, trx_radius)

const layers_list = get_layers_list(layers_obj, viz_state.close_up)
Expand Down
Loading

0 comments on commit 84fb420

Please sign in to comment.