Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: rotated 2D matrix #413

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions schema/gosling.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,9 @@
{
"$ref": "#/definitions/ExonSplitTransform"
},
{
"$ref": "#/definitions/RotateMatrixTransform"
},
{
"$ref": "#/definitions/CoverageTransform"
},
Expand Down Expand Up @@ -3974,6 +3977,27 @@
},
"type": "object"
},
"RotateMatrixTransform": {
"additionalProperties": false,
"properties": {
"genomicField1": {
"type": "string"
},
"genomicField2": {
"type": "string"
},
"type": {
"const": "rotateMatrix",
"type": "string"
}
},
"required": [
"type",
"genomicField1",
"genomicField2"
],
"type": "object"
},
"SingleTrack": {
"additionalProperties": false,
"properties": {
Expand Down
11 changes: 9 additions & 2 deletions src/core/gosling-to-higlass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { BoundingBox, RelativePosition } from './utils/bounding-box';
import { resolveSuperposedTracks } from './utils/overlay';
import { getGenomicChannelKeyFromTrack, getGenomicChannelFromTrack } from './utils/validate';
import { viridisColorMap } from './utils/colors';
import { IsDataDeep, IsChannelDeep, IsDataDeepTileset } from './gosling.schema.guards';
import { IsDataDeep, IsChannelDeep, IsDataDeepTileset, Is2DTrack } from './gosling.schema.guards';
import { DEFAULT_SUBTITLE_HEIGHT, DEFAULT_TITLE_HEIGHT } from './layout/defaults';
import { getTheme, Theme } from './utils/theme';

Expand Down Expand Up @@ -96,7 +96,7 @@ export function goslingToHiGlass(
};
}

const isMatrix = gosTrack.data?.type === 'matrix';
const isMatrix = Is2DTrack(gosTrack);
if (isMatrix) {
// Use HiGlass' heatmap track for matrix data
hgTrack.type = 'heatmap';
Expand All @@ -109,6 +109,13 @@ export function goslingToHiGlass(
hgTrack.options.colorbarPosition = (firstResolvedSpec.color as any)?.legend ? 'topRight' : 'hidden';
}

// TODO: Experimental
if (gosTrack.data?.type === 'matrix') {
hgTrack.filetype = 'cooler';
// ! To let HiGlass draw the linear heatmap, uncomment the following line.
// hgTrack.type = 'linear-heatmap';
}

if (gosTrack.overlayOnPreviousTrack) {
hgModel
.setViewOrientation(gosTrack.orientation) // TODO: Orientation should be assigned to 'individual' views
Expand Down
33 changes: 30 additions & 3 deletions src/core/gosling.schema.guards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ import {
OverlaidTracks,
StackedTracks,
BAMData,
Range
Range,
MatrixData
} from './gosling.schema';
import { SUPPORTED_CHANNELS } from './mark';
import { isArray } from 'lodash';
Expand Down Expand Up @@ -123,7 +124,33 @@ export function IsOverlaidTrack(track: Partial<Track>): track is OverlaidTrack {
* TODO: This should be more correctly determined, but we currently only support 2D tracks for matrix datasets.
*/
export function Is2DTrack(track: Track) {
return IsSingleTrack(track) && track.data?.type === 'matrix';
if (!IsSingleTrack(track)) {
return false;
}
const xChannel = track.x;
const yChannel = track.y;
return (
track.data?.type === 'matrix' &&
IsChannelDeep(xChannel) &&
IsChannelDeep(yChannel) &&
xChannel.type === 'genomic' &&
yChannel.type === 'genomic'
);
}

// TODO: To support overlaid tracks in rotated matrix tracks, this function should be updated as this only accepts single tracks.
export function IsRotatedMatrixTrack(track: Track) {
if (!IsSingleTrack(track)) {
return false;
}
const xChannel = track.x;
const yChannel = track.y;
return (
track.data?.type === 'matrix' &&
IsChannelDeep(xChannel) &&
xChannel.type === 'genomic' &&
(!IsChannelDeep(yChannel) || yChannel.type !== 'genomic')
);
}

export function IsChannelValue(
Expand All @@ -140,7 +167,7 @@ export function IsChannelBind(

export function IsDataDeepTileset(
_: DataDeep | undefined
): _ is BEDDBData | VectorData | MultivecData | BIGWIGData | BAMData {
): _ is BEDDBData | VectorData | MultivecData | BIGWIGData | BAMData | MatrixData {
return (
_ !== undefined &&
(_.type === 'vector' ||
Expand Down
12 changes: 11 additions & 1 deletion src/core/gosling.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -427,19 +427,20 @@ export interface BAMData {
url: string;
}

/* ----------------------------- DATA TRANSFORM ----------------------------- */
export interface MatrixData {
type: 'matrix';
url: string;
}

/* ----------------------------- DATA TRANSFORM ----------------------------- */
export type DataTransform =
| FilterTransform
| StrConcatTransform
| StrReplaceTransform
| LogTransform
| DisplaceTransform
| ExonSplitTransform
| RotateMatrixTransform
| CoverageTransform
| JSONParseTransform;

Expand Down Expand Up @@ -512,6 +513,15 @@ export interface ExonSplitTransform {
fields: { field: string; type: FieldType; newField: string; chrField: string }[];
}

export interface RotateMatrixTransform {
type: 'rotateMatrix';
genomicField1: string;
genomicField2: string;
// Currently concatenate a post string, '_rotated'
// newField1: string;
// newField2: string;
}

/**
* Aggregate rows and calculate coverage
*/
Expand Down
2 changes: 2 additions & 0 deletions src/core/higlass.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export interface EnumTrack {
server?: string;
tilesetUid?: string;
chromInfoPath?: string;
filetype?: string; // Added manually
data?: Data;
fromViewUid?: null | string;
width?: number;
Expand Down Expand Up @@ -183,6 +184,7 @@ export type EnumTrackType =
| 'horizontal-chromosome-labels'
| 'horizontal-divergent-bar'
| 'horizontal-gene-annotations'
| 'linear-heatmap'
| 'horizontal-heatmap'
| 'horizontal-line'
| 'horizontal-multivec'
Expand Down
40 changes: 39 additions & 1 deletion src/core/utils/data-transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
StrReplaceTransform,
CoverageTransform,
DisplaceTransform,
JSONParseTransform
JSONParseTransform,
RotateMatrixTransform
} from '../gosling.schema';
import {
getChannelKeysByAggregateFnc,
Expand Down Expand Up @@ -300,6 +301,43 @@ export function displace(t: DisplaceTransform, data: Datum[], scale: ScaleLinear
return base;
}

export function rotateMatrix(
rotate: RotateMatrixTransform,
data: Datum[],
scale: ScaleLinear<any, any>,
trackWidth: number
): Datum[] {
const { genomicField1, genomicField2 } = rotate;
const output: Datum[] = [];

Array.from(data).forEach(d => {
if (d[genomicField1] && d[genomicField2]) {
d[`x_rotated`] = (+d[genomicField1] + +d[genomicField2]) / 2.0;
d[`y_rotated`] = Math.abs(+d[genomicField1] - +d[genomicField2]) / 2.0;

// output.push(d);

// if(scale.invert(0) <= d[`x_rotated`] && d[`x_rotated`] <= scale.invert(trackWidth)) {
// // For the performance issue, we only store the data rows that are visible in the current view.
// output.push(d);
// }

// if(d[`y_rotated`] <= 5000) {
// For the performance issue, we only store the data rows that are visible in the current view.
// output.push(d);
// }

// TESSTING
// if(scale.invert(0) <= d.x && d.x <= scale.invert(trackWidth)) {
// // For the performance issue, we only store the data rows that are visible in the current view.
// output.push(d);
// }
}
});
console.log('scale.invert(0)', scale.invert(0), 'scale.invert(trackWidth)', scale.invert(trackWidth));
return output;
}

export function splitExon(split: ExonSplitTransform, data: Datum[], assembly: Assembly = 'hg38'): Datum[] {
const { separator, fields, flag } = split;
let output: Datum[] = Array.from(data);
Expand Down
8 changes: 7 additions & 1 deletion src/editor/example/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { GoslingSpec } from '../../core/gosling.schema';
import { EX_SPEC_LAYOUT_AND_ARRANGEMENT_1, EX_SPEC_LAYOUT_AND_ARRANGEMENT_2 } from './layout-and-arrangement';
import { EX_SPEC_DARK_THEME, EX_SPEC_VISUAL_ENCODING, EX_SPEC_VISUAL_ENCODING_CIRCULAR } from './visual-encoding';
import { EX_SPEC_CANCER_VARIANT_PROTOTYPE } from './cancer-variant';
import { EX_SPEC_MATRIX_HFFC6 } from './matrix-hffc6';
import { EX_SPEC_MATRIX_HFFC6, EX_SPEC_ROTATED_MATRIX } from './matrix-hffc6';
import { EX_SPEC_LINKING } from './visual-linking';
import { EX_SPEC_BASIC_SEMANTIC_ZOOM } from './basic-semantic-zoom';
import { EX_SPEC_MARK_DISPLACEMENT } from './mark-displacement';
Expand Down Expand Up @@ -101,6 +101,12 @@ export const examples: ReadonlyArray<{
id: 'MATRIX_HFFC6',
spec: EX_SPEC_MATRIX_HFFC6
},
{
name: 'Rotated Matrix (Hi-C)',
id: 'ROTATED_MATRIX',
spec: EX_SPEC_ROTATED_MATRIX,
forceShow: true
},
{
name: 'Circos',
id: 'CIRCOS',
Expand Down
84 changes: 83 additions & 1 deletion src/editor/example/matrix-hffc6.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { GoslingSpec } from '../../core/gosling.schema';
import { GoslingSpec, Track } from '../../core/gosling.schema';
import { GOSLING_PUBLIC_DATA } from './gosling-data';

export const EX_SPEC_MATRIX_HFFC6: GoslingSpec = {
Expand Down Expand Up @@ -486,3 +486,85 @@ export const EX_SPEC_MATRIX_HFFC6: GoslingSpec = {
],
style: { outlineWidth: 0 }
};

export const EX_SPEC_ROTATED_MATRIX: GoslingSpec = {
title: 'Rotated Matrix Visualization',
subtitle: 'Hi-C for HFFc6 Cells',
arrangement: 'vertical',
xDomain: { chromosome: '1', interval: [1, 10000] },
// xDomain: { chromosome: '1', interval: [1000000, 1010000] },
// xDomain: { chromosome: '7', interval: [77700000, 81000000] },
spacing: 0,
style: { outlineWidth: 1 },
views: [
{
tracks: [
{
title: 'HFFc6 Hi-C',
data: {
url: GOSLING_PUBLIC_DATA.matrixHiC,
type: 'matrix'
},
dataTransform: [{ type: 'rotateMatrix', genomicField1: 'x', genomicField2: 'y' }],
mark: 'point',
x: { field: 'x_rotated', type: 'genomic' },
y: { field: 'y_rotated', type: 'quantitative' },
color: { field: 'value', type: 'quantitative', legend: true },
width: 600,
height: 100
},
{
alignment: 'overlay',
tracks: [
{
data: {
url: GOSLING_PUBLIC_DATA.geneAnnotation,
type: 'beddb',
genomicFields: [
{ index: 1, name: 'start' },
{ index: 2, name: 'end' }
],
valueFields: [
{ index: 5, name: 'strand', type: 'nominal' },
{ index: 3, name: 'name', type: 'nominal' }
]
},
dataTransform: [{ type: 'filter', field: 'strand', oneOf: ['+'] }],
mark: 'triangleRight',
x: { field: 'start', type: 'genomic' },
size: { value: 13 },
stroke: { value: 'white' },
strokeWidth: { value: 1 },
row: { field: 'strand', type: 'nominal', domain: ['+', '-'] },
color: { value: '#CB7AA7' }
},
{
data: {
url: GOSLING_PUBLIC_DATA.geneAnnotation,
type: 'beddb',
genomicFields: [
{ index: 1, name: 'start' },
{ index: 2, name: 'end' }
],
valueFields: [
{ index: 5, name: 'strand', type: 'nominal' },
{ index: 3, name: 'name', type: 'nominal' }
]
},
dataTransform: [{ type: 'filter', field: 'strand', oneOf: ['-'] }],
mark: 'triangleLeft',
x: { field: 'start', type: 'genomic' },
stroke: { value: 'white' },
strokeWidth: { value: 1 },
size: { value: 13 },
row: { field: 'strand', type: 'nominal', domain: ['+', '-'] },
color: { value: '#029F73' }
}
],
width: 570,
height: 40
}
].slice(0, 1) as Track[]
}
]
};
Loading