import * as React from 'react'; import { computed, makeObservable } from 'mobx'; import { observer, Observer } from 'mobx-react'; import { MolecularProfile } from 'cbioportal-ts-api-client'; import { GeneticEntity } from '../ResultsViewPageStore'; import { getSampleViewUrl } from '../../../shared/api/urls'; import './styles.scss'; import { bind } from 'bind-decorator'; import ScatterPlot from '../../../shared/components/plots/ScatterPlot'; import { DownloadControls } from 'cbioportal-frontend-commons'; import { axisLabel, getDownloadData, isNotProfiled, } from './CoExpressionPlotUtils'; import _ from 'lodash'; import { scatterPlotSize } from '../../../shared/components/plots/PlotUtils'; import { IAxisLogScaleParams } from 'pages/resultsView/plots/PlotsTabUtils'; import autobind from 'autobind-decorator'; export interface ICoExpressionPlotProps { xAxisGeneticEntity: GeneticEntity; yAxisGeneticEntity: GeneticEntity; data: CoExpressionPlotData[]; molecularProfileY: MolecularProfile; molecularProfileX: MolecularProfile; height: number; width: number; showLogScaleControls?: boolean; showMutationControls?: boolean; showMutations?: boolean; logScale?: boolean; showRegressionLine: boolean; handlers: { onClickShowMutations?: () => void; onClickLogScale?: () => void; onClickShowRegressionLine: () => void; }; } export type CoExpressionPlotData = { x: number; y: number; mutationsX: string; mutationsY: string; profiledX: boolean; profiledY: boolean; studyId: string; sampleId: string; }; class CoExpressionScatterPlot extends ScatterPlot {} const NO_MUT_STROKE = '#0174df'; const NO_MUT_FILL = '#58acfa'; const X_MUT_STROKE = '#886A08'; const X_MUT_FILL = '#DBA901'; const Y_MUT_STROKE = '#F7819F'; const Y_MUT_FILL = '#F5A9F2'; const BOTH_MUT_STROKE = '#B40404'; const BOTH_MUT_FILL = '#FF0000'; const NOT_PROFILED_STROKE = '#d3d3d3'; const NOT_PROFILED_FILL = '#ffffff'; function noop() {} const SVG_ID = 'coexpression-plot-svg'; @observer export default class CoExpressionPlot extends React.Component< ICoExpressionPlotProps, {} > { constructor(props: ICoExpressionPlotProps) { super(props); makeObservable(this); } @bind private getSvg() { return document.getElementById(SVG_ID) as SVGElement | null; } @computed get stroke() { if (this.props.showMutations) { return (d: { mutationsX: string; mutationsY: string; profiledX: boolean; profiledY: boolean; }) => { if (isNotProfiled(d)) { return NOT_PROFILED_STROKE; } else if (d.mutationsX && d.mutationsY) { return BOTH_MUT_STROKE; } else if (!d.mutationsX && !d.mutationsY) { return NO_MUT_STROKE; } else if (d.mutationsX) { return X_MUT_STROKE; } else { return Y_MUT_STROKE; } }; } else { return NO_MUT_STROKE; } } @computed get fill() { if (this.props.showMutations) { return (d: { mutationsX: string; mutationsY: string; profiledX: boolean; profiledY: boolean; }) => { if (isNotProfiled(d)) { return NOT_PROFILED_FILL; } else if (d.mutationsX && d.mutationsY) { return BOTH_MUT_FILL; } else if (!d.mutationsX && !d.mutationsY) { return NO_MUT_FILL; } else if (d.mutationsX) { return X_MUT_FILL; } else { return Y_MUT_FILL; } }; } else { return NO_MUT_FILL; } } @computed get mutationLegendElements() { if (!this.props.showMutations) { return []; } let hasBoth = false; let hasX = false; let hasY = false; let hasNone = false; let hasNotProfiled = false; for (const d of this.data) { if (isNotProfiled(d)) { hasNotProfiled = true; } if (d.mutationsX && d.mutationsY) { hasBoth = true; } else if (!d.mutationsX && !d.mutationsY) { hasNone = true; } else if (d.mutationsX) { hasX = true; } else { hasY = true; } } const ret = []; if (hasX) { ret.push({ name: [ this.props.xAxisGeneticEntity.geneticEntityName, 'mutated', ], symbol: { fill: X_MUT_FILL, stroke: X_MUT_STROKE }, }); } if (hasY) { ret.push({ name: [ this.props.yAxisGeneticEntity.geneticEntityName, 'mutated', ], symbol: { fill: Y_MUT_FILL, stroke: Y_MUT_STROKE }, }); } if (hasBoth) { ret.push({ name: `Both mutated`, symbol: { fill: BOTH_MUT_FILL, stroke: BOTH_MUT_STROKE }, }); } if (hasNone) { ret.push({ name: `Neither mutated`, symbol: { fill: NO_MUT_FILL, stroke: NO_MUT_STROKE }, }); } if (hasNotProfiled) { ret.push({ name: 'Not profiled for mutations', symbol: { fill: NOT_PROFILED_FILL, stroke: NOT_PROFILED_STROKE, }, }); } return ret; } @computed get data() { // sort in order to set z index in plot // order: both mutated, one mutated, neither mutated, not profiled return _.sortBy(this.props.data, d => { if (d.mutationsX && d.mutationsY) { return 3; } else if (d.mutationsX || d.mutationsY) { return 2; } else if (!isNotProfiled(d)) { return 1; } else { return 0; } }); } private get title() { return `${this.props.xAxisGeneticEntity.geneticEntityName} vs. ${this.props.yAxisGeneticEntity.geneticEntityName}`; } @computed get axisLabelX() { return axisLabel( { geneticEntityName: this.props.xAxisGeneticEntity .geneticEntityName, cytoband: this.props.xAxisGeneticEntity.cytoband, }, this.axisLogScaleFunction, this.props.molecularProfileX.name ); } @computed get axisLabelY() { return axisLabel( { geneticEntityName: this.props.yAxisGeneticEntity .geneticEntityName, }, this.axisLogScaleFunction, this.props.molecularProfileY.name ); } @computed get axisLogScaleFunction(): IAxisLogScaleParams | undefined { const MIN_LOG_ARGUMENT = 0.01; if (!this.props.logScale) { return undefined; } return { label: 'log2', fLogScale: (x: number, offset: number) => Math.log2(Math.max(x, MIN_LOG_ARGUMENT)), fInvLogScale: (x: number) => Math.pow(2, x), }; } @bind private plot() { if (!this.data.length) { return
No data to plot.
; } return ( ); } @bind private toolbar() { return (
{this.props.showMutationControls && (
)} {this.props.showLogScaleControls && (
)}
); } @autobind private getDownloadData() { return getDownloadData( this.props.data, this.props.xAxisGeneticEntity, this.props.yAxisGeneticEntity, this.props.molecularProfileX, this.props.molecularProfileY, !!this.props.showMutations ); } @bind private tooltip(d: CoExpressionPlotData) { return (
Sample ID: {d.sampleId}
{this.props.xAxisGeneticEntity.geneticEntityName}:  {d.x.toFixed(2)}
{this.props.yAxisGeneticEntity.geneticEntityName}:  {d.y.toFixed(2)}
{d.mutationsX && (
{this.props.xAxisGeneticEntity.geneticEntityName}{' '} Mutation:  {d.mutationsX}
)} {d.mutationsY && (
{this.props.yAxisGeneticEntity.geneticEntityName}{' '} Mutation:  {d.mutationsY}
)}
); } render() { return (
{this.toolbar} {this.plot}
); } }