Skip to content

Commit 10ca0bd

Browse files
authored
Merge pull request #135 from daisybio/topceRNA_fix
Topce rna fix
2 parents 4d6c37c + f5a5ff9 commit 10ca0bd

File tree

56 files changed

+7765
-3118
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+7765
-3118
lines changed

angular.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
{
4444
"type": "initial",
4545
"maximumWarning": "500kB",
46-
"maximumError": "2MB"
46+
"maximumError": "3MB"
4747
},
4848
{
4949
"type": "anyComponentStyle",

package-lock.json

Lines changed: 4454 additions & 2176 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"@sigma/export-image": "^3.0.0",
2929
"@sigma/node-square": "^3.0.0",
3030
"@visa-ge/ng-igv": "^0.0.11",
31-
"file-saver": "^2.0.5",
31+
"file-saver-es": "^2.0.5",
3232
"graphology": "^0.25.4",
3333
"graphology-layout-force": "^0.2.4",
3434
"katex": "^0.16.21",
@@ -46,6 +46,7 @@
4646
"@angular/cli": "^19.2.7",
4747
"@angular/compiler-cli": "^19.2.6",
4848
"@types/file-saver": "^2.0.7",
49+
"@types/file-saver-es": "^2.0.3",
4950
"@types/jasmine": "~5.1.0",
5051
"@types/katex": "^0.16.7",
5152
"@types/lodash": "^4.17.16",

src/app/components/browse-views/browse-views.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
</mat-tab>
1616
}
1717
<mat-tab label="Expression heatmap">
18-
<app-heatmap [browseService]="browseService()" [refreshSignal]="refresh$()"></app-heatmap>
18+
<app-gene-expression-heatmap [browseService]="browseService()" [refreshSignal]="refresh$()"></app-gene-expression-heatmap>
1919
</mat-tab>
2020
<mat-tab [label]="(capitalize(level() || '') || 'Gene') + 's'">
2121
<app-nodes [browseService]="browseService()"></app-nodes>

src/app/components/browse-views/browse-views.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { ReactiveFormsModule } from '@angular/forms';
55
import { MatExpansionModule } from '@angular/material/expansion';
66
import { InteractionsComponent } from '../../components/browse-views/interactions/interactions.component';
77
import { NetworkComponent } from '../../components/browse-views/network/network.component';
8-
import { HeatmapComponent } from '../../components/browse-views/heatmap/heatmap.component';
8+
import { GeneExpressionHeatmapComponent } from '../../components/browse-views/heatmap/heatmap.component';
99
import { BrowseService } from '../../services/browse.service';
1010
import { SurvivalAnalysisComponent } from '../../components/browse-views/survival-analysis/survival-analysis.component';
1111
import { ActiveEntitiesComponent } from '../../components/browse-views/active-entities/active-entities.component';
@@ -27,7 +27,7 @@ import { capitalize } from 'lodash';
2727
MatExpansionModule,
2828
InteractionsComponent,
2929
NetworkComponent,
30-
HeatmapComponent,
30+
GeneExpressionHeatmapComponent,
3131
SurvivalAnalysisComponent,
3232
ActiveEntitiesComponent,
3333
MatProgressSpinnerModule,

src/app/components/browse-views/gsea/gsea-barplot/gsea-barplot.component.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,15 @@ export class GseaBarplotComponent implements OnDestroy {
5858
yaxis: {
5959
title: 'Gene Set',
6060
automargin: true,
61+
tickmode: 'linear',
62+
dtick: 1, // This ensures that each term is displayed in any zoom level
6163
},
6264
height: Math.max(400, terms.length * 20), // Adjust height based on number of terms
6365
margin: {
6466
l: 200, // Increase left margin for term labels
6567
},
68+
paper_bgcolor: 'rgba(0,0,0,0)',
69+
plot_bgcolor: 'rgba(0,0,0,0)',
6670
};
6771

6872
Plotly.newPlot(plotElement.nativeElement, data, layout);

src/app/components/browse-views/gsea/gsea-volcanoplot/gsea-volcanoplot.component.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ export class GseaVolcanoplotComponent implements OnDestroy {
9191
},
9292
],
9393
showlegend: false,
94-
};
94+
paper_bgcolor: 'rgba(0,0,0,0)',
95+
plot_bgcolor: 'rgba(0,0,0,0)', };
9596

9697
Plotly.newPlot(plotElement.nativeElement, data, layout);
9798
});
Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1-
@if (plotData.isLoading()) {
1+
<!-- @if (plotData.isLoading()) {
22
<div class="card-container">
33
<mat-progress-spinner mode="indeterminate"></mat-progress-spinner>
44
</div>
55
}
6-
<div #heatmap style="height: 800px; width: 100%"></div>
6+
<div #heatmap style="height: 800px; width: 100%"></div> -->
7+
8+
<app-heatmap-plot
9+
[dataSource]="heatmapDataSource"
10+
[params]="heatmapParams()"
11+
[refreshSignal]="refreshSignal">
12+
</app-heatmap-plot>

src/app/components/browse-views/heatmap/heatmap.component.ts

Lines changed: 63 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -12,117 +12,89 @@ import {
1212
import { BrowseService } from '../../../services/browse.service';
1313
import { BackendService } from '../../../services/backend.service';
1414
import { VersionsService } from '../../../services/versions.service';
15-
import { MatProgressSpinner } from '@angular/material/progress-spinner';
1615
import { capitalize } from 'lodash';
16+
import { ReusableHeatmapComponent, HeatmapDataSource } from '../../../components/heatmap-plot/heatmap-plot.component';
17+
import { CommonModule } from '@angular/common';
1718

1819
declare const Plotly: any;
1920

21+
2022
@Component({
21-
selector: 'app-heatmap',
22-
imports: [MatProgressSpinner],
23+
selector: 'app-gene-expression-heatmap',
2324
templateUrl: './heatmap.component.html',
2425
styleUrl: './heatmap.component.scss',
26+
imports: [CommonModule, ReusableHeatmapComponent]
2527
})
26-
export class HeatmapComponent implements OnDestroy {
28+
export class GeneExpressionHeatmapComponent {
2729
browseService = input.required<BrowseService>();
2830
backend = inject(BackendService);
2931
versions = inject(VersionsService);
3032

31-
level$ = computed(() => this.browseService().level$());
33+
// Inputs
3234
refreshSignal = input.required<any>();
33-
heatmap = viewChild.required<ElementRef<HTMLDivElement>>('heatmap');
3435

35-
plotData = resource({
36-
request: computed(() => {
37-
return {
38-
nodes: this.browseService().nodes$(),
39-
disease: this.browseService().disease$(),
40-
level: this.browseService().level$(),
41-
version: this.versions.versionReadOnly()(),
42-
};
43-
}),
44-
loader: async (param) => {
45-
const nodes = param.request.nodes;
46-
const disease = param.request.disease;
47-
const version = param.request.version;
48-
const level = param.request.level;
49-
if (nodes === undefined || disease === undefined || level === undefined)
50-
return;
36+
// State
37+
level$ = computed(() => this.browseService().level$());
38+
39+
// Parameters derived from services
40+
heatmapParams = computed(() => ({
41+
nodes: this.browseService().nodes$(),
42+
disease: this.browseService().disease$(),
43+
level: this.browseService().level$(),
44+
version: this.versions.versionReadOnly()(),
45+
}));
5146

52-
const identifiers = nodes.map((node) => BrowseService.getNodeID(node));
47+
// Create data source for the heatmap
48+
heatmapDataSource: HeatmapDataSource = {
49+
getData: async (params) => {
50+
const { nodes, disease, version, level } = params;
51+
52+
if (!nodes || !disease || !level) {
53+
return [];
54+
}
5355

54-
const expression = await this.backend.getExpression(
55-
version,
56-
identifiers,
57-
disease,
58-
level
59-
);
56+
const identifiers = nodes.map((node: any) => BrowseService.getNodeID(node));
6057

61-
const expressionMap = new Map<string, Map<string, number>>();
62-
const samples = new Set<string>();
63-
for (const expr of expression) {
64-
const identifier =
65-
'gene' in expr
66-
? expr.gene.gene_symbol || expr.gene.ensg_number
67-
: expr.transcript.enst_number;
68-
if (!expressionMap.has(identifier)) {
69-
expressionMap.set(identifier, new Map<string, number>());
70-
}
71-
samples.add(expr.sample_ID);
72-
expressionMap.get(identifier)!.set(expr.sample_ID, expr.expr_value);
58+
// Fetch expression data with pagination
59+
const expressionData = await this.backend.fetchExpressionData(version, identifiers, disease.dataset_ID, disease.disease_name, level);
60+
61+
// Handle disease subtypes
62+
if (disease.disease_name === 'pancancer') {
63+
await this.handlePancancerSubtypes(expressionData);
64+
} else {
65+
await ReusableHeatmapComponent.handleDiseaseSubtypes(expressionData, disease.disease_name, this.backend);
7366
}
7467

75-
const geneSymbols = Array.from(expressionMap.keys());
76-
const sampleIDs = Array.from(samples);
77-
const values = geneSymbols.map((gene) =>
78-
sampleIDs.map((sample) => expressionMap.get(gene)!.get(sample))
79-
);
80-
81-
return {
82-
x: sampleIDs,
83-
y: geneSymbols,
84-
z: values,
85-
type: 'heatmap',
86-
};
68+
return expressionData;
8769
},
88-
});
89-
90-
refreshEffect = effect(() => {
91-
this.refreshSignal();
92-
this.refresh();
93-
});
94-
95-
plotUpdateEffect = effect(() => {
96-
const data = this.plotData.value();
97-
if (!data) return;
98-
99-
const heatmap = this.heatmap().nativeElement;
100-
101-
Plotly.newPlot(heatmap, [data], {
102-
title: `${capitalize(this.level$())} Expression Heatmap - ${capitalize(
103-
this.browseService().disease$()?.disease_name
104-
)}`,
105-
xaxis: {
106-
title: 'Sample ID',
107-
automargin: true,
108-
},
109-
yaxis: {
110-
title: capitalize(this.level$()),
111-
automargin: true,
112-
},
113-
});
114-
});
70+
71+
getTitle: (params) => {
72+
return `${capitalize(params.level)} Expression Heatmap - ${capitalize(params.disease?.disease_name || 'Unknown')}`;
73+
},
74+
75+
getYAxisTitle: () => {
76+
return capitalize(this.level$());
77+
},
78+
79+
getZAxisTitle: () => {
80+
return 'Normalized<br>expression';
81+
},
82+
83+
getZMid: () => 0,
84+
85+
getColorScale: () => 'RdBu'
86+
};
11587

116-
refresh() {
117-
const heatmap = this.heatmap().nativeElement;
118-
if (heatmap.checkVisibility()) {
119-
Plotly.Plots.resize(heatmap);
88+
private async handlePancancerSubtypes(expressionData: any[]): Promise<void> {
89+
// Fetch the mapping from TSS codes to disease names
90+
const mapping = await this.backend.getDiseaseFromSample();
91+
92+
// Add the disease name to the expression data
93+
for (const e of expressionData) {
94+
const sampleId = e.sample_ID;
95+
// mapSampleToDisease from ReusableHeatmapComponent
96+
const diseaseName = await ReusableHeatmapComponent.mapSampleToDisease(sampleId, mapping);
97+
e.disease_subtype = diseaseName;
12098
}
12199
}
122-
123-
ngOnDestroy(): void {
124-
Plotly.purge(this.heatmap().nativeElement);
125-
this.refreshEffect.destroy();
126-
this.plotUpdateEffect.destroy();
127-
}
128-
}
100+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@if (heatmapResource.isLoading()) {
2+
<div style="width: 100%; display: flex; flex-direction: row; justify-content: center; align-items: center;">
3+
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
4+
</div>
5+
} @else {
6+
@if (!heatmapResource.value()) {
7+
<p>No data available</p>
8+
}
9+
}
10+
<div #heatmap class="heatmap-container" [style.width]="width()" [style.height]="height()"></div>

0 commit comments

Comments
 (0)