Skip to content

Commit

Permalink
Rendering UTRs
Browse files Browse the repository at this point in the history
  • Loading branch information
danvk committed Mar 18, 2015
1 parent 61e6705 commit 4be6876
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 157 deletions.
23 changes: 0 additions & 23 deletions lib/mocha.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,2 @@
declare function describe(description: string, spec: () => void): void;
declare function it(expectation: string, assertion: (done: () => void) => void): void;

/*
declare function before(action: () => void): void;
declare function before(action: (done: MochaDone) => void): void;
declare function setup(action: () => void): void;
declare function setup(action: (done: MochaDone) => void): void;
declare function after(action: () => void): void;
declare function after(action: (done: MochaDone) => void): void;
declare function teardown(action: () => void): void;
declare function teardown(action: (done: MochaDone) => void): void;
declare function beforeEach(action: () => void): void;
declare function beforeEach(action: (done: MochaDone) => void): void;
*/
4 changes: 4 additions & 0 deletions lib/underscore.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ declare module "underscore" {
declare function range(a: number, b: number): Array<number>;
declare function extend<S, T>(o1: S, o2: T): S & T;

declare function zip<S, T>(a1: S[], a2: T[]): Array<[S, T]>;

declare function flatten<S>(a: S[][]): S[];

declare function chain<S>(obj: S): any;
}

2 changes: 0 additions & 2 deletions playground.html
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,3 @@
</body>

<script src="build/all.js"></script>

<script src="//cdnjs.cloudflare.com/ajax/libs/q.js/1.2.0/q.js"></script>
45 changes: 36 additions & 9 deletions src/BigBedDataSource.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,49 @@
/* @flow */
'use strict';

var Events = require('backbone').Events,
_ = require('underscore');
_ = require('underscore'),
Q = require('q');


var ContigInterval = require('./ContigInterval'),
Interval = require('./Interval');


// TODO: move this into BigBed.js

type Gene = {
position: ContigInterval;
id: string; // transcript ID, e.g. "ENST00000269305"
strand: string; // '+' or '-'
codingStart: number; // locus of coding start
codingStop: number;
codingRegion: Interval; // locus of coding start
exons: Array<Interval>;
geneId: string; // ensembl gene ID
name: string; // human-readable name, e.g. "TP53"
}

// TODO: move this into BigBed.js and get it to type check.
type BedRow = {
// Half-open interval for the BED row.
contig: string;
start: number;
stop: number;
// Remaining fields in the BED row (typically tab-delimited)
rest: string;
}

declare class BigBed {
getFeaturesInRange: (contig: string, start: number, stop: number) => Q.Promise<Array<BedRow>>;
}


// Flow type for export.
type BigBedSource = {
rangeChanged: (newRange: GenomeRange) => void;
getGenesInRange: (range: ContigInterval) => Gene[];
on: (event: string, handler: Function) => void;
off: (event: string) => void;
}

// The fields are described at http://genome.ucsc.edu/FAQ/FAQformat#format1
function parseBedFeature(f): Gene {
var position = new ContigInterval(f.contig, f.start, f.stop),
Expand All @@ -28,23 +52,22 @@ function parseBedFeature(f): Gene {
exonStarts = x[8].split(',').map(Number),
exons = _.zip(exonStarts, exonLengths)
.map(function([start, length]) {
return new Interval(start, start + length);
return new Interval(f.start + start, f.start + start + length);
});

return {
position,
id: x[0], // e.g. ENST00000359597
strand: x[2], // either + or -
codingStart: Number(x[3]),
codingStop: Number(x[4]),
codingRegion: new Interval(Number(x[3]), Number(x[4])),
geneId: x[9],
name: x[10],
exons
};
}


function createBigBedDataSource(remoteSource: BigBed) {
function createBigBedDataSource(remoteSource: BigBed): BigBedSource {
// Collection of genes that have already been loaded.
var genes: Array<Gene> = [];
window.genes = genes;
Expand Down Expand Up @@ -75,7 +98,11 @@ function createBigBedDataSource(remoteSource: BigBed) {
.then(() => o.trigger('newdata', newRange))
.done();
},
getGenesInRange
getGenesInRange,

// These are here to make Flow happy.
on: () => {},
off: () => {}
};
_.extend(o, Events); // Make this an event emitter

Expand Down
169 changes: 46 additions & 123 deletions src/GeneTrack.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
/**
* Visualization of genes, including exons and coding regions.
* @flow
*/
var React = require('react/addons'),
_ = require('underscore'),
d3 = require('d3'),
types = require('./types');
types = require('./types'),
bedtools = require('./bedtools');

var GeneTrack = React.createClass({
propTypes: {
Expand All @@ -21,7 +26,7 @@ var GeneTrack = React.createClass({

var NonEmptyGeneTrack = React.createClass({
propTypes: {
range: types.GenomeRange,
range: types.GenomeRange.isRequired,
genes: React.PropTypes.array.isRequired,
onRangeChange: React.PropTypes.func.isRequired
},
Expand All @@ -33,32 +38,39 @@ var NonEmptyGeneTrack = React.createClass({
svg = d3.select(div)
.append('svg');

// These define the left/right arrow patterns for sense/antisense genes.
var defs = svg.append('defs')

defs
.append('pattern')
.attr('id', 'antisense')
.attr('patternUnits', 'userSpaceOnUse')
.attr('width', 30)
.attr('height', 9)
.attr('x', 0)
.attr('y', -4)
.append('path')
.attr('d', 'M4,0 L0,4 L4,8')
.attr('fill', 'none')
.attr('stroke-width', 1);
defs
.append('pattern')
.attr('id', 'sense')
.attr('patternUnits', 'userSpaceOnUse')
.attr('width', 30)
.attr('height', 9)
.attr('x', 0)
.attr('y', -4)
.append('path')
.attr('d', 'M0,0 L4,4 L0,8')
.attr('fill', 'none')
.attr('stroke-width', 1);
defs.append('pattern')
.attr({
'id': 'antisense',
'patternUnits': 'userSpaceOnUse',
'width': 30,
'height': 9,
'x': 0,
'y': -4
})
.append('path')
.attr({
'd': 'M4,0 L0,4 L4,8', // Arrow pointing left
'fill': 'none',
'stroke-width': 1
});

defs.append('pattern')
.attr({
'id': 'sense',
'patternUnits': 'userSpaceOnUse',
'width': 30,
'height': 9,
'x': 0,
'y': -4
})
.append('path')
.attr({
'd': 'M0,0 L4,4 L0,8', // Arrow pointing right
'fill': 'none',
'stroke-width': 1
});

this.updateVisualization();
},
Expand Down Expand Up @@ -111,9 +123,8 @@ var NonEmptyGeneTrack = React.createClass({
geneLineG.append('rect').attr('class', 'strand');

geneLineG.selectAll('rect.exon')
.data(g => g.exons.map(function(x) {
return [x.start, x.stop, g.position.start()]
}))
.data(g => bedtools.splitCodingExons(g.exons, g.codingRegion)
.map(x => [x, g.position.start()]))
.enter()
.append('rect')
.attr('class', 'exon')
Expand Down Expand Up @@ -158,15 +169,11 @@ var NonEmptyGeneTrack = React.createClass({

track.selectAll('rect.exon')
.attr({
'x': function([start, stop, gStart]) {
return scale(start) - scale(0);
},
'y': -3,
'height': 6,
'width': function([start, stop]) {
return scale(stop) - scale(start);
}
})
'x': ([exon, gStart]) => scale(exon.start - gStart) - scale(0),
'y': ([exon]) => -3 * (exon.isCoding ? 2 : 1),
'height': ([exon]) => 6 * (exon.isCoding ? 2 : 1),
'width': ([exon]) => scale(exon.stop) - scale(exon.start)
});

// Exit
genes.exit().remove();
Expand All @@ -180,87 +187,3 @@ var EmptyTrack = React.createClass({
});

module.exports = GeneTrack;

/*
TP53 record:
chr17
7512444
7531642
1 ENST00000269305
2 0
3 -
4 7513651
5 7520637
6 0
7 11
8 1289,107,74,137,110,113,184,279,22,102,223,
9 0,2207,5133,5299,5779,6457,6651,7592,7980,8119,18975,
10 ENSG00000141510
11 TP53
4: Transcript ID
5: (always 0)
6: Strand ("-" = right to left, "+" = left to right)
7: left coordinate of translated region
8: right coordinate of translated region
9: (always 0)
10: number of exons
11: lengths of exons, left to right
12: start of exons offset from start of gene, left to right
13: Ensembl Gene ID
14: Canonical name
Here's a description of this format (http://genome.ucsc.edu/FAQ/FAQformat#format1)
1. chrom
2. chromStart
3. chromEnd
4. name
5. score
6. strand
7. thickStart
8. thickEnd
9. itemRgb
10. blockCount
11. blockSize
11. blockStarts
exon 11: chr17:7512445-7513733
exon 10: chr17:7514652-7514758
exon 9: chr17:7517578-7517651
..
exon 1: chr17:7531420-7531642
--->
exon 11: chr17: 0- 1288
exon 10: chr17: 2207- 2313
exon 9: chr17: 5133- 5206
..
exon 1: chr17:18975-19197
In "collapsed" view, IGV merges all transcripts onto the same line.
Also:
chr17
7517349
7531521
0 ENST00000359597
1 0
2 -
3 7517349
4 7520637
5 0
6 10
7 33,74,137,110,113,184,279,22,102,102,
8 0,228,394,874,1552,1746,2687,3075,3214,14070,
9 ENSG00000141510
10 TP53
*/
4 changes: 4 additions & 0 deletions src/Interval.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ class Interval {
return value >= this.start && value <= this.stop;
}

containsInterval(other: Interval): boolean {
return this.contains(other.start) && this.contains(other.stop);
}

clone(): Interval {
return new Interval(this.start, this.stop);
}
Expand Down
Loading

0 comments on commit 4be6876

Please sign in to comment.