Skip to content

Commit 9baaab2

Browse files
cornhundredhuanlityCopilot
authored
DEGA-248-Visium-HD-Tile (#187)
* debugging landscape_sst * tiles showing up in landscape_sst * added notebook * gene seach starting to work * removing console logs * added aws4fetch * error with linked landscape_clustergram * added spot check notebook * Handle legacy click data in landscape updates (#189) * Make clustergram updates robust to bad triggers * tested notebooks * running notesbooks * Visium HD auto-zoom and center working * Fix SST gene info box (#192) * fix gene info box for sst * renamed notebook * added enrichment analysis to notebook * cleaning * update notebook * link to dataset * clean nb * remove one output from nb to reduce file size * add create_barcode_to_cell_id_mapping() and _load_visium_hd_cluster_data() * address github QC error * rectify imagecodecs version * rectify again * adding statsmodels to toml * fixed typo in toml * remove imagecodecs pinned version * formatted js * removed unused python function, testing revert to esbuild version * fixed typos * renamed Xenium notebook * reformatted ruff * Update js/viz/landscape_sst.js Co-authored-by: Copilot <[email protected]> * Update js/viz/landscape_sst.js Co-authored-by: Copilot <[email protected]> * Update js/viz/landscape_sst.js Co-authored-by: Copilot <[email protected]> * Update js/widget_interactions/update_ist_landscape_from_cgm.js Co-authored-by: Copilot <[email protected]> * renaming notebooks, manual testing * renaming notebooks, manual testing * adding defaults for image_tile_layer * renaming notebooks, manual testing * reformatted * updated notebook * pinning prettier version for github checks * ran manual test --------- Co-authored-by: Huan Wang <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent f3c398e commit 9baaab2

17 files changed

+3456
-454161
lines changed

js/global_variables/tile_color_dict.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@ import { options } from '../global_variables/fetch_options';
22
import { get_arrow_table } from '../read_parquet/get_arrow_table';
33
import { hexToRgb } from '../utils/hexToRgb';
44

5-
export const set_tile_color_dict = async (base_url) => {
5+
export const set_tile_color_dict = async (viz_state, base_url) => {
66
const tile_color_dict = {};
77

88
const df_colors_url = `${base_url}/df_colors.parquet`;
9-
const df_colors = await get_arrow_table(df_colors_url, options.fetch);
9+
const df_colors = await get_arrow_table(
10+
df_colors_url,
11+
options.fetch,
12+
viz_state.aws
13+
);
1014

1115
let names = [];
1216
let colors = [];

js/global_variables/tile_exp_array.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ export const update_tile_exp_array = async (viz_state, inst_gene) => {
66

77
const exp_table = await get_arrow_table(
88
`${base_url}/tbg/${inst_gene}.parquet`,
9-
options.fetch
9+
options.fetch,
10+
viz_state.aws
1011
);
1112
const tile_names = exp_table.getChild('__index_level_0__').toArray();
1213
const tile_exp = exp_table.getChild(inst_gene).toArray();

js/obs_store/obs_store.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export const create_obs_store = () => {
4242
nbhd_layer: true,
4343
trx_data: true,
4444
path_data: true,
45+
square_scatter_data: true,
4546
}),
4647
deck_ready: Observable(false),
4748
};

js/ui/gene_search.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ const sst_gene_search_callback = async (deck_sst, viz_state, layers_sst) => {
1818

1919
viz_state.obs_store.deck_check.set({
2020
...viz_state.obs_store.deck_check.get(),
21-
cell_layer: false,
22-
trx_layer: false,
21+
square_scatter_layer: false,
2322
});
2423

2524
update_selected_genes(

js/ui/ui_containers.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,39 @@ export const make_sst_ui_container = (deck_sst, layers_sst, viz_state) => {
281281

282282
set_gene_search('sst', deck_sst, layers_sst, viz_state);
283283

284+
// add subscriber for gene search and gene_text_box
285+
viz_state.obs_store.selected_genes.subscribe(async (selected_genes) => {
286+
if (selected_genes.length === 1) {
287+
const inst_gene = selected_genes[0];
288+
289+
viz_state.genes.gene_search_input.value = inst_gene;
290+
291+
if (inst_gene !== '') {
292+
if (viz_state.genes.gene_names.includes(inst_gene)) {
293+
viz_state.genes.gene_text_box.textContent = 'loading';
294+
await uniprot_get_request(inst_gene);
295+
const gene_data = uniprot_data[inst_gene];
296+
297+
if (gene_data && gene_data.name && gene_data.description) {
298+
viz_state.genes.gene_text_box.innerHTML = `<span style="color: blue;">${gene_data.name}</span><br>${gene_data.description}`;
299+
} else {
300+
viz_state.genes.gene_text_box.textContent = '';
301+
}
302+
}
303+
} else {
304+
viz_state.genes.gene_text_box.textContent = '';
305+
}
306+
307+
viz_state.genes.gene_text_box.scrollTo({
308+
top: 0,
309+
behavior: 'smooth',
310+
});
311+
} else if (selected_genes.length === 0) {
312+
viz_state.genes.gene_search_input.value = '';
313+
viz_state.genes.gene_text_box.textContent = '';
314+
}
315+
});
316+
284317
ctrl_container.appendChild(image_container);
285318
ctrl_container.appendChild(tile_container);
286319
ctrl_container.appendChild(viz_state.genes.gene_search);

js/viz/landscape_sst.js

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { AwsClient } from 'aws4fetch';
12
import * as d3 from 'd3';
23

34
import { ini_deck_sst } from '../deck-gl/core/deck_sst';
@@ -19,6 +20,7 @@ import {
1920
set_tile_name_to_index_map,
2021
} from '../global_variables/tile_names_array';
2122
import { set_tile_scatter_data } from '../global_variables/tile_scatter_data';
23+
import { create_obs_store } from '../obs_store/obs_store';
2224
import { get_arrow_table } from '../read_parquet/get_arrow_table';
2325
import { get_scatter_data } from '../read_parquet/get_scatter_data';
2426
import { make_sst_ui_container } from '../ui/ui_containers';
@@ -36,17 +38,22 @@ export const landscape_sst = async (
3638
square_tile_size = 1.4,
3739
_dataset_name = '',
3840
width = 0,
39-
height = 800
41+
height = 800,
42+
creds = {}
4043
) => {
4144
if (width === 0) {
4245
width = '100%';
4346
}
4447

4548
// Create and append the visualization container
4649
const root = document.createElement('div');
47-
root.style.height = '800px';
50+
root.style.height = `${height}px`;
51+
root.style.width = typeof width === 'number' ? `${width}px` : width;
4852

4953
const viz_state = {};
54+
55+
viz_state.obs_store = create_obs_store();
56+
5057
set_options(token);
5158
set_global_base_url(viz_state, base_url);
5259

@@ -58,8 +65,55 @@ export const landscape_sst = async (
5865

5966
await set_landscape_parameters(viz_state.img, base_url);
6067

68+
// AWS credentials setup
69+
70+
if ('accessKeyId' in creds) {
71+
viz_state.aws = new AwsClient({
72+
accessKeyId: creds.accessKeyId,
73+
secretAccessKey: creds.secretAccessKey,
74+
sessionToken: creds.sessionToken,
75+
region: 'us-east-1',
76+
service: 's3',
77+
});
78+
79+
// fetch after initialization of aws client is apparently required?
80+
const response = await viz_state.aws.fetch(
81+
`${base_url}/landscape_parameters.json`
82+
);
83+
84+
if (!response.ok) {
85+
throw new Error(`Fetch failed: ${response.statusText}`);
86+
}
87+
} else {
88+
viz_state.aws = null;
89+
}
90+
6191
await set_dimensions(viz_state, base_url, 'cells');
6292

93+
const parseDim = (val, ref) => {
94+
if (typeof val === 'string' && val.includes('%')) {
95+
const ratio = parseFloat(val) / 100;
96+
return ref * ratio;
97+
}
98+
return parseFloat(val) || ref;
99+
};
100+
101+
const imgWidth = viz_state.dimensions.width;
102+
const imgHeight = viz_state.dimensions.height;
103+
const containerWidth = parseDim(width, el.clientWidth || imgWidth);
104+
const containerHeight = parseDim(height, el.clientHeight || imgHeight);
105+
const scale = Math.min(
106+
containerWidth / imgWidth,
107+
containerHeight / imgHeight
108+
);
109+
const autoZoom = Math.log2(scale);
110+
111+
if (ini_x === 0 && ini_y === 0 && ini_zoom === 0) {
112+
ini_x = imgWidth / 2;
113+
ini_y = imgHeight / 2;
114+
ini_zoom = autoZoom;
115+
}
116+
63117
viz_state.buttons = {};
64118
viz_state.buttons.blue = '#8797ff';
65119
viz_state.buttons.gray = 'gray';
@@ -95,7 +149,7 @@ export const landscape_sst = async (
95149

96150
viz_state.cats.square_tile_size = square_tile_size;
97151

98-
await set_meta_gene(viz_state.genes, base_url);
152+
await set_meta_gene(viz_state.genes, base_url, 'default', viz_state.aws);
99153

100154
// move this to landscape_parameters
101155
const _info = {
@@ -105,7 +159,11 @@ export const landscape_sst = async (
105159

106160
const tile_url = `${base_url}/tile_geometries.parquet`;
107161

108-
const tile_arrow_table = await get_arrow_table(tile_url, options.fetch);
162+
const tile_arrow_table = await get_arrow_table(
163+
tile_url,
164+
options.fetch,
165+
viz_state.aws
166+
);
109167

110168
viz_state.cats.tile_cats_array = tile_arrow_table
111169
.getChild('cluster')
@@ -122,7 +180,10 @@ export const landscape_sst = async (
122180
);
123181
set_tile_name_to_index_map(viz_state.cats);
124182

125-
viz_state.cats.tile_color_dict = await set_tile_color_dict(base_url);
183+
viz_state.cats.tile_color_dict = await set_tile_color_dict(
184+
viz_state,
185+
base_url
186+
);
126187

127188
const simple_image_layer = await make_simple_image_layer(viz_state, _info);
128189
const square_scatter_layer = ini_square_scatter_layer(viz_state.cats);

js/widget_interactions/update_ist_landscape_from_cgm.js

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,19 @@ export const update_ist_landscape_from_cgm = async (
99
layers_obj,
1010
viz_state
1111
) => {
12-
const click_info = viz_state.model.get('update_trigger');
13-
if (!click_info || !click_info.type) {
12+
const raw_click = viz_state.model.get('update_trigger');
13+
if (!raw_click || typeof raw_click !== 'object') {
14+
return;
15+
}
16+
17+
const click_info = {
18+
type: raw_click.type || raw_click.click_type,
19+
value: raw_click.value || raw_click.click_value,
20+
};
21+
22+
const click_type = click_info.type?.replace('-', '_');
23+
24+
if (!click_type) {
1425
return;
1526
}
1627

@@ -19,7 +30,7 @@ export const update_ist_landscape_from_cgm = async (
1930

2031
// add try catch block
2132
try {
22-
if (click_info.type === 'row_label') {
33+
if (click_type === 'row_label') {
2334
inst_gene = click_info.value.name;
2435

2536
new_cat = inst_gene === viz_state.cats.cat ? 'cluster' : inst_gene;
@@ -44,7 +55,7 @@ export const update_ist_landscape_from_cgm = async (
4455
);
4556

4657
refresh_layer(viz_state, layers_obj, 'cell_layer');
47-
} else if (click_info.type === 'col_label') {
58+
} else if (click_type === 'col_label') {
4859
inst_gene = 'cluster';
4960
new_cat = click_info.value.name;
5061

@@ -53,24 +64,14 @@ export const update_ist_landscape_from_cgm = async (
5364
update_selected_genes(viz_state.genes, [], viz_state.obs_store);
5465

5566
refresh_layer(viz_state, layers_obj, 'cell_layer');
56-
} else if (click_info.type === 'col_dendro') {
57-
inst_gene = 'cluster';
58-
59-
inst_gene = 'cluster';
67+
} else if (click_type === 'col_dendro') {
6068
const new_cats = click_info.value.selected_names;
6169

6270
update_cat(viz_state.cats, 'cluster');
6371
update_selected_cats(viz_state.cats, new_cats, viz_state.obs_store);
6472
update_selected_genes(viz_state.genes, [], viz_state.obs_store);
6573

6674
refresh_layer(viz_state, layers_obj, 'cell_layer');
67-
68-
update_cat(viz_state.cats, inst_gene);
69-
update_selected_cats(
70-
viz_state.cats,
71-
click_info.click_value,
72-
viz_state.obs_store
73-
);
7475
}
7576
} catch (error) {
7677
handleAsyncError(error, {

js/widget_interactions/update_tile_landscape_from_cgm.js

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,61 @@
11
// import { model } from '../global_variables/model'
22
import { update_square_scatter_layer } from '../deck-gl/layers/square_scatter_layer';
33
import { update_cat, update_selected_cats } from '../global_variables/cat';
4+
import { update_selected_genes } from '../global_variables/selected_genes';
45
import { update_tile_exp_array } from '../global_variables/tile_exp_array';
56

67
export const update_tile_landscape_from_cgm = async (
78
deck_sst,
89
layers_sst,
910
viz_state
1011
) => {
11-
const click_info = viz_state.model.get('update_trigger');
12+
const raw_click = viz_state.model.get('update_trigger');
13+
if (!raw_click || typeof raw_click !== 'object') {
14+
return;
15+
}
16+
17+
const click_info = {
18+
type: raw_click.type || raw_click.click_type,
19+
value: raw_click.value || raw_click.click_value,
20+
};
21+
22+
// legacy click names sometimes use dashes
23+
const click_type = click_info.type?.replace('-', '_');
24+
25+
if (!click_type) {
26+
return;
27+
}
1228

1329
let inst_gene;
1430

15-
if (click_info.click_type === 'row-label') {
16-
inst_gene = click_info.click_value;
31+
if (click_type === 'row_label') {
32+
inst_gene = click_info.value.name || click_info.value;
1733
update_cat(viz_state.cats, inst_gene);
1834
await update_tile_exp_array(viz_state, inst_gene);
19-
} else if (click_info.click_type === 'col-label') {
35+
update_selected_genes(viz_state.genes, [inst_gene], viz_state.obs_store);
36+
37+
if (viz_state.genes && viz_state.genes.gene_search_input) {
38+
viz_state.genes.gene_search_input.value = inst_gene;
39+
}
40+
} else if (click_type === 'col_label') {
2041
update_cat(viz_state.cats, 'cluster');
2142
update_selected_cats(
2243
viz_state.cats,
23-
[click_info.click_value],
44+
[click_info.value.name || click_info.value],
2445
viz_state.obs_store
2546
);
26-
} else if (click_info.click_type === 'col-dendro') {
47+
update_selected_genes(viz_state.genes, [], viz_state.obs_store);
48+
} else if (click_type === 'col_dendro') {
2749
update_cat(viz_state.cats, 'cluster');
2850
update_selected_cats(
2951
viz_state.cats,
30-
click_info.click_value,
52+
click_info.value.selected_names || click_info.value,
3153
viz_state.obs_store
3254
);
55+
update_selected_genes(viz_state.genes, [], viz_state.obs_store);
3356
} else {
34-
update_cat('cluster');
57+
update_cat(viz_state.cats, 'cluster');
58+
update_selected_genes(viz_state.genes, [], viz_state.obs_store);
3559
}
3660

3761
update_square_scatter_layer(viz_state, layers_sst);

0 commit comments

Comments
 (0)