diff --git a/src/app/components/analysis-panel/analysis-panel.component.html b/src/app/components/analysis-panel/analysis-panel.component.html index 5d005275bf7aca0e25c141f9690360841dc1b1f2..c074cc08091b8158d50461469eb76f97d525c114 100644 --- a/src/app/components/analysis-panel/analysis-panel.component.html +++ b/src/app/components/analysis-panel/analysis-panel.component.html @@ -127,7 +127,7 @@ <div class="tab-content" *ngIf="task && task.info.done" [class.is-visible]="tab === 'network'"> <div class="card-image canvas-content" #networkWithLegend> <div *ngIf="myConfig.showLegend"> - <app-network-legend [config]="myConfig" [analysis]="false"></app-network-legend> + <app-network-legend [config]="myConfig" [context]="legendContext"></app-network-legend> </div> <div class="fullheight center image1" #network> <button class="button is-loading center">Loading</button> diff --git a/src/app/components/analysis-panel/analysis-panel.component.ts b/src/app/components/analysis-panel/analysis-panel.component.ts index e0145d58f63d66dc618ad709ec21c668329baaa9..e2dd40eff872f2b1526d41348d7277fa5e9c1202 100644 --- a/src/app/components/analysis-panel/analysis-panel.component.ts +++ b/src/app/components/analysis-panel/analysis-panel.component.ts @@ -15,9 +15,11 @@ import {algorithmNames, AnalysisService} from '../../services/analysis/analysis. import { Drug, EdgeType, + ExpressionMap, getDrugNodeId, getProteinNodeId, getWrapperFromNode, + legendContext, Node, Task, Tissue, @@ -98,6 +100,8 @@ export class AnalysisPanelComponent implements OnInit, OnChanges { public tableNormalize = false; public tableHasScores = false; + public legendContext: legendContext = 'drugTarget'; + public expressionExpanded = false; public selectedTissue: Tissue | null = null; @@ -106,6 +110,8 @@ export class AnalysisPanelComponent implements OnInit, OnChanges { public tableDrugScoreTooltip = ''; public tableProteinScoreTooltip = ''; + public expressionMap: ExpressionMap; + constructor(private http: HttpClient, public analysis: AnalysisService, public netex: NetexControllerService) { } @@ -316,6 +322,9 @@ export class AnalysisPanelComponent implements OnInit, OnChanges { } } this.emitVisibleItems(true); + + this.setLegendContext(); + } public emitVisibleItems(on: boolean) { @@ -431,9 +440,6 @@ export class AnalysisPanelComponent implements OnInit, OnChanges { const config = result.parameters.config; this.myConfig = config; - console.log(config) - console.log('config') - const identifier = this.myConfig.identifier; // add drugGroup and foundNodesGroup for added nodes @@ -486,6 +492,16 @@ export class AnalysisPanelComponent implements OnInit, OnChanges { }; } + public setLegendContext() { + const target = this.task.info.target; + if (target === 'drug') { + this.legendContext = "drug"; + } else if (target === 'drug-target') { + this.legendContext = 'drugTarget' + } else { + throw `Could not set legend context based on ${target}.` + } + } public updateAdjacentDrugs(bool: boolean) { this.adjacentDrugs = bool; @@ -503,11 +519,14 @@ export class AnalysisPanelComponent implements OnInit, OnChanges { this.nodeData.nodes.add(this.adjacentDrugList); this.nodeData.edges.add(this.adjacentDrugEdgesList); }) + this.legendContext = 'drug' } else { this.nodeData.nodes.remove(this.adjacentDrugList); this.nodeData.edges.remove(this.adjacentDrugEdgesList); this.adjacentDrugList = []; this.adjacentDrugEdgesList = []; + + this.setLegendContext() } } @@ -572,71 +591,79 @@ export class AnalysisPanelComponent implements OnInit, OnChanges { } public selectTissue(tissue: Tissue | null) { - /*if (!tissue) { + this.expressionExpanded = false; + if (!tissue) { this.selectedTissue = null; const updatedNodes = []; - for (const protein of this.proteins) { - const item = getWrapperFromNode(protein); - const node = this.nodeData.nodes.get(item.nodeId); + for (const item of this.proteins) { + if (item.netexId === undefined) { + // nodes that are not mapped to backend remain untouched + continue; + } + const node: Node = this.nodeData.nodes.get(item.id); if (!node) { continue; } - const pos = this.network.getPositions([item.nodeId]); - node.x = pos[item.nodeId].x; - node.y = pos[item.nodeId].y; - Object.assign(node, + const pos = this.network.getPositions([item.id]); + node.x = pos[item.id].x; + node.y = pos[item.id].y; + Object.assign( + node, NetworkSettings.getNodeStyle( - node.wrapper.type, - node.isSeed, - this.analysis.inSelection(item), - undefined, - undefined, - 1.0)); - node.wrapper = item; - node.gradient = 1.0; - protein.expressionLevel = undefined; - (node.wrapper.data as Node).expressionLevel = undefined; + node, + this.myConfig, + false, + this.analysis.inSelection(getWrapperFromNode(item)), + 1.0 + ) + ) updatedNodes.push(node); } this.nodeData.nodes.update(updatedNodes); + // delete expression values + this.expressionMap = undefined; } else { - this.selectedTissue = tissue; + this.selectedTissue = tissue const minExp = 0.3; - this.http.get<Array<{ protein: Node, level: number }>>( - `${environment.backend}tissue_expression/?tissue=${tissue.id}&token=${this.token}`) - .subscribe((levels) => { - const updatedNodes = []; - const maxExpr = Math.max(...levels.map(lvl => lvl.level)); - for (const lvl of levels) { - const item = getWrapperFromNode(lvl.protein); - const node = this.nodeData.nodes.get(item.nodeId); - if (!node) { - continue; - } - const gradient = lvl.level !== null ? (Math.pow(lvl.level / maxExpr, 1 / 3) * (1 - minExp) + minExp) : -1; - const pos = this.network.getPositions([item.nodeId]); - node.x = pos[item.nodeId].x; - node.y = pos[item.nodeId].y; - Object.assign(node, - NetworkSettings.getNodeStyle( - node.wrapper.type, - node.isSeed, - this.analysis.inSelection(item), - undefined, - undefined, - gradient)); - node.wrapper = item; - node.gradient = gradient; - this.proteins.find(prot => getProteinNodeId(prot) === item.nodeId).expressionLevel = lvl.level; - (node.wrapper.data as Node).expressionLevel = lvl.level; - updatedNodes.push(node); - } - this.nodeData.nodes.update(updatedNodes); + // filter out non-proteins, e.g. drugs + const proteinNodes = []; + this.nodeData.nodes.forEach(element => { + if (element.id.startsWith('p') && element.netexId !== undefined) { + proteinNodes.push(element); + } + }); + this.netex.tissueExpressionGenes(this.selectedTissue, proteinNodes).subscribe((response) => { + this.expressionMap = response; + const updatedNodes = []; + // mapping from netex IDs to network IDs, TODO check if this step is necessary + const networkIdMappping = {} + this.nodeData.nodes.forEach(element => { + networkIdMappping[element.netexId] = element.id }); + const maxExpr = Math.max(...Object.values(this.expressionMap)); + for (const [netexId, expressionlvl] of Object.entries(this.expressionMap)) { + const networkId = networkIdMappping[netexId] + const node = this.nodeData.nodes.get(networkId); + if (node === null) { + continue; + } + const wrapper = getWrapperFromNode(node) + const gradient = expressionlvl !== null ? (Math.pow(expressionlvl / maxExpr, 1 / 3) * (1 - minExp) + minExp) : -1; + const pos = this.network.getPositions([networkId]); + node.x = pos[networkId].x; + node.y = pos[networkId].y; + Object.assign(node, + NetworkSettings.getNodeStyle( + node, + this.myConfig, + node.isSeed, + this.analysis.inSelection(wrapper), + gradient)); + node.gradient = gradient; + updatedNodes.push(node); + } + this.nodeData.nodes.update(updatedNodes); + }) } - this.emitVisibleItems(true);*/ } - } - - diff --git a/src/app/components/network-legend/network-legend.component.html b/src/app/components/network-legend/network-legend.component.html index 3ae77c85d58bcbf28311a7fd554ba4539c5a40d8..8fe27208029852f0a2afec03c8bb6a55da1773e8 100644 --- a/src/app/components/network-legend/network-legend.component.html +++ b/src/app/components/network-legend/network-legend.component.html @@ -1,61 +1,61 @@ -<div class="legend" [class.right]="this.legendConfig.legendPos === 'right'"> +<div class="legend" [class.right]="this.config.legendPos === 'right'"> <!-- default legend in html --> - <table *ngIf="!this.legendConfig.legendUrl.length"> - <ng-container *ngIf="this.legendConfig.showLegendNodes"> - <tr *ngFor="let nodeGroup of this.legendConfig.nodeGroups | keyvalue" class="list-item"> + <table *ngIf="!this.config.legendUrl.length"> + <ng-container *ngIf="this.config.showLegendNodes"> + <tr *ngFor="let nodeGroup of this.config.nodeGroups | keyvalue" class="list-item"> - <ng-container *ngIf="!analysis && nodeGroup.key"> + <ng-container *ngIf="nodeGroup.key && checkContext(nodeGroup.key)"> <ng-container *ngIf="nodeGroup.value.image"> <!-- group icon given, use icon in legend --> - <td> + <th> <img [src]="nodeGroup.value.image" class="legend-icon"/> - </td> + </th> <td> {{ nodeGroup.value.groupName }}</td> </ng-container> <ng-container *ngIf="!nodeGroup.value.image" [ngSwitch]="nodeGroup.value.shape"> <!-- no image given, create icon programmatically --> - <td *ngSwitchCase="'text'"> + <th *ngSwitchCase="'text'"> <span class="node text"> text </span> - </td> - <td *ngSwitchCase="'hexagon'"> + </th> + <th *ngSwitchCase="'hexagon'"> <span class="node hexagon" [style.color]=nodeGroup.value.color.background> ⬣ </span> - </td> - <td *ngSwitchCase="'triangle'"> + </th> + <th *ngSwitchCase="'triangle'"> <span class="node triangle" [style.border-bottom-color]=nodeGroup.value.color.background> </span> - </td> - <td *ngSwitchCase="'triangleDown'"> + </th> + <th *ngSwitchCase="'triangleDown'"> <span class="node triangleDown" [style.border-top-color]=nodeGroup.value.color.background> </span> - </td> - <td *ngSwitchCase="'diamond'"> + </th> + <th *ngSwitchCase="'diamond'"> <span class="node diamond" [style.background-color]=nodeGroup.value.color.background > </span> - </td> - <td *ngSwitchCase="'star'"> + </th> + <th *ngSwitchCase="'star'"> <span class="node star" [style.border-bottom-color]=nodeGroup.value.color.background [style.color]=nodeGroup.value.color.background > </span> - </td> - <td *ngSwitchCase="'circle'"> + </th> + <th *ngSwitchCase="'circle'"> <span class="node circle" [style.background-color]=nodeGroup.value.color.background> ... </span> - </td> - <td *ngSwitchDefault> + </th> + <th *ngSwitchDefault> <span class="node {{ nodeGroup.value.shape }}" [style.background-color]=nodeGroup.value.color.background> </span> - </td> + </th> <td> {{ nodeGroup.value.groupName }}</td> </ng-container> </ng-container> @@ -63,12 +63,12 @@ </tr> </ng-container> - <ng-container *ngIf="this.legendConfig.showLegendEdges"> - <tr *ngFor="let edgeGroup of this.legendConfig.edgeGroups | keyvalue" class="list-item"> - <td> + <ng-container *ngIf="this.config.showLegendEdges"> + <tr *ngFor="let edgeGroup of this.config.edgeGroups | keyvalue" class="list-item"> + <th> <hr *ngIf="!edgeGroup.value.dashes" class="edge" [style.background-color]=edgeGroup.value.color> <hr *ngIf="edgeGroup.value.dashes" class="edge dashes" [style.color]=edgeGroup.value.color> - </td> + </th> <td> {{ edgeGroup.value.groupName }}</td> </tr> </ng-container> @@ -76,6 +76,6 @@ <!-- custom legend image if url given by user --> - <img *ngIf="this.legendConfig.legendUrl.length" [src]="this.legendConfig.legendUrl" [ngClass]="this.legendConfig.legendClass"/> + <img *ngIf="this.config.legendUrl.length" [src]="this.config.legendUrl" [ngClass]="this.config.legendClass"/> </div> diff --git a/src/app/components/network-legend/network-legend.component.scss b/src/app/components/network-legend/network-legend.component.scss index 01304490d417a3c654a767e904d69dcddb1f80f5..ede8ee234b8b46b9cac9f7f654ea21ecf8d526c6 100644 --- a/src/app/components/network-legend/network-legend.component.scss +++ b/src/app/components/network-legend/network-legend.component.scss @@ -12,18 +12,19 @@ div.legend { } tr.list-item{ line-height: calc(#{$legend-row-height} / 2); - td{ + th{ height: $legend-row-height; + width: $legend-row-height; vertical-align: middle !important; padding: 0 !important; .text{ font-style: italic; } .diamond { - height: 75%; + height: $legend-diamond-size; text-align: center; transform: rotate(45deg); - width: 75%; + width: $legend-diamond-size; margin-left: 12.5%; display: inline-block; } @@ -124,9 +125,13 @@ div.legend { border-top: dotted; background-color: transparent; } - } } + td { + height: $legend-row-height; + vertical-align: middle !important; + padding: 0 !important; + } } } diff --git a/src/app/components/network-legend/network-legend.component.ts b/src/app/components/network-legend/network-legend.component.ts index 10bdb07d08babfc97da52b6c2fff39075b855361..826135a8b3a7fa6005f01ad02b9cf5d9df195ae8 100644 --- a/src/app/components/network-legend/network-legend.component.ts +++ b/src/app/components/network-legend/network-legend.component.ts @@ -1,4 +1,5 @@ import {Component, Input, OnInit} from '@angular/core'; +import { legendContext } from 'src/app/interfaces'; import {IConfig} from '../../config'; @Component({ @@ -7,22 +8,27 @@ import {IConfig} from '../../config'; styleUrls: ['./network-legend.component.scss'] }) export class NetworkLegendComponent implements OnInit { - public legendConfig: IConfig; - @Input() analysis: boolean; - @Input() set config(value: IConfig) { - // copy to not override user config - value = JSON.parse(JSON.stringify(value)); - // remove selected node group since it is just a border - delete value.nodeGroups.selectedNode; - if (!this.analysis) { - // do not show the analysis-groups in the explorer network - delete value.nodeGroups.foundNode; - delete value.nodeGroups.foundDrug; - delete value.nodeGroups.seedNode; + @Input() context: legendContext; + @Input() config: IConfig; + + private contextGroupsToDelete = { + 'explorer': ['foundNode', 'foundDrug', 'seedNode'], + 'adjacentDrugs': ['foundNode', 'seedNode'], + 'drugTarget': ['foundDrug', 'seedNode'], + 'drug': ['seedNode'] + } + + public checkContext(nodeGroupKey) { + if (nodeGroupKey === 'selectedNode') { + // selected node is not supposed to appear in legend + return false; } - this.legendConfig = value; - }; + if (this.contextGroupsToDelete[this.context].includes(nodeGroupKey)) { + return false; + } + return true; + } constructor() { } diff --git a/src/app/config.ts b/src/app/config.ts index f104d015feadca523d42800154ebe7101bc50946..cab2597f4be4dc30a7fe35dd07b26f87a048e838 100644 --- a/src/app/config.ts +++ b/src/app/config.ts @@ -142,7 +142,7 @@ export const defaultConfig: IConfig = { background: '#F12590' }, }, - shape: 'star', + shape: 'diamond', type: 'default drug type', }, seedNode: { diff --git a/src/app/interfaces.ts b/src/app/interfaces.ts index f7c46aa3bea2f9580438db58eb4e7ef9995c9fec..2a7cb686e0af524db8817d53a1b7620c1be9f2bf 100644 --- a/src/app/interfaces.ts +++ b/src/app/interfaces.ts @@ -30,6 +30,8 @@ export interface Tissue { name: string; } +export type legendContext = 'explorer' | 'adjacentDrugs' | 'drug' | 'drugTarget'; + /// netexId to expressionlvl export type ExpressionMap = { string: number }; diff --git a/src/app/pages/explorer-page/explorer-page.component.html b/src/app/pages/explorer-page/explorer-page.component.html index 87baa8c76dfc39dbe226d0dfbded0d09ed322239..48d85dfe475735443028514ba91dbf3fd1f5c737 100644 --- a/src/app/pages/explorer-page/explorer-page.component.html +++ b/src/app/pages/explorer-page/explorer-page.component.html @@ -123,7 +123,7 @@ <div class="card-content fullheight"> <div class="card-image canvas-content" #networkWithLegend> <div *ngIf="myConfig.showLegend"> - <app-network-legend [config]="myConfig" [analysis]="false"></app-network-legend> + <app-network-legend [config]="myConfig" [context]="legendContext"></app-network-legend> </div> <div class="parent fullheight"> <div class="center image1 fullheight" #network> diff --git a/src/app/pages/explorer-page/explorer-page.component.ts b/src/app/pages/explorer-page/explorer-page.component.ts index c5d09b1a9830814a6bdc3d470353336d7b617434..ba76f096f1063a55c6d6d6916a624da091307087 100644 --- a/src/app/pages/explorer-page/explorer-page.component.ts +++ b/src/app/pages/explorer-page/explorer-page.component.ts @@ -13,7 +13,8 @@ import { Tissue, ExpressionMap, getDrugNodeId, - Drug + Drug, + legendContext } from '../../interfaces'; import {mapCustomEdge, mapCustomNode, ProteinNetwork} from '../../main-network'; import {AnalysisService} from '../../services/analysis/analysis.service'; @@ -166,6 +167,8 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { public expressionExpanded = false; public selectedTissue: Tissue | null = null; + public legendContext: legendContext = 'explorer'; + // keys are node netexIds public expressionMap: ExpressionMap = undefined; @@ -445,11 +448,14 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { this.nodeData.nodes.add(this.adjacentDrugList); this.nodeData.edges.add(this.adjacentDrugEdgesList); }) + this.legendContext = 'adjacentDrugs' } else { this.nodeData.nodes.remove(this.adjacentDrugList); this.nodeData.edges.remove(this.adjacentDrugEdgesList); this.adjacentDrugList = []; this.adjacentDrugEdgesList = []; + + this.legendContext = 'explorer' } } @@ -528,6 +534,8 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { nodeGroups = merge(defaultNodeGroups, nodeGroups); // overwrite default node groups this.myConfig[key] = nodeGroups; + console.log('nodeGroups after preprocessing') + console.log(nodeGroups) } /** diff --git a/src/styles.scss b/src/styles.scss index e5b747ef308fd349856e8d0ad64ef708087c7734..330eb5c0a9393cfc8b8bfa89c1efdd075e56d9d5 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -182,7 +182,7 @@ div.covex.explorer { .analysis-view { height: 100%; - width: calc(100% - 20px); + width: 100%; position: absolute; margin-top: 0; z-index: 10; diff --git a/src/variables.scss b/src/variables.scss index b3e7dcceb31be59eade74da88b57290f4c5ad8d4..9a5f2a0121a5fe963122b9aea40a4780308e820b 100644 --- a/src/variables.scss +++ b/src/variables.scss @@ -13,6 +13,7 @@ $legend-star-color: #FC0; $legend-triangle-size: $legend-row-height; $legend-triangle-height: 29px; $legend-hexagon-font-size: 38px; +$legend-diamond-size: 25px; $height: 100%;