diff --git a/examples/data.js b/examples/data.js
index 77d16e35..af1bb412 100644
--- a/examples/data.js
+++ b/examples/data.js
@@ -28,6 +28,16 @@ var sources = [
data: pileup.formats.vcf({
url: '/test-data/snv.chr17.vcf'
}),
+ options: {
+ variantHeightByFrequency: true,
+ onVariantClicked: function(data) {
+ var content = "Variants:\n";
+ for (var i =0;i< data.length;i++) {
+ content +=data[i].id+" - "+data[i].vcfLine+"\n";
+ }
+ alert(content);
+ },
+ },
name: 'Variants'
},
{
diff --git a/examples/index.html b/examples/index.html
index d174f5d7..51f631b6 100644
--- a/examples/index.html
+++ b/examples/index.html
@@ -3,6 +3,7 @@
+
diff --git a/src/main/Root.js b/src/main/Root.js
index c5c63c06..2927dfb6 100644
--- a/src/main/Root.js
+++ b/src/main/Root.js
@@ -97,6 +97,7 @@ class Root extends React.Component {
range={this.state.range}
onRangeChange={this.handleRangeChange.bind(this)}
source={track.source}
+ options={track.track.options}
referenceSource={this.props.referenceSource}
ref = {(c) => {this.trackReactElements[intKey]=c}}
/>);
diff --git a/src/main/VisualizationWrapper.js b/src/main/VisualizationWrapper.js
index d0d34915..bb4810ab 100644
--- a/src/main/VisualizationWrapper.js
+++ b/src/main/VisualizationWrapper.js
@@ -27,6 +27,7 @@ type Props = {
onRangeChange: (newRange: GenomeRange) => void;
referenceSource: TwoBitSource;
source: any;
+ options: ?Object;
};
class VisualizationWrapper extends React.Component {
@@ -136,6 +137,7 @@ class VisualizationWrapper extends React.Component {
if (!range) {
return ;
}
+ var options = _.extend({},this.props.visualization.options,this.props.options);
var el = React.createElement(component, ({
range: range,
@@ -143,7 +145,7 @@ class VisualizationWrapper extends React.Component {
referenceSource: this.props.referenceSource,
width: this.state.width,
height: this.state.height,
- options: this.props.visualization.options
+ options: options
} : VizProps));
return {el}
;
diff --git a/src/main/data/vcf.js b/src/main/data/vcf.js
index 558eb623..880a4f1b 100644
--- a/src/main/data/vcf.js
+++ b/src/main/data/vcf.js
@@ -16,6 +16,13 @@ export type Variant = {
position: number;
ref: string;
alt: string;
+ id: string;
+ //this is the bigest allel frequency for single vcf entry
+ //single vcf entry might contain more than one variant like the example below
+ //20 1110696 rs6040355 A G,T 67 PASS NS=2;DP=10;AF=0.333,0.667;AA=T;DB
+ majorFrequency: ?number;
+ //this is the smallest allel frequency for single vcf entry
+ minorFrequency: ?number;
vcfLine: string;
}
@@ -41,12 +48,33 @@ function extractLocusLine(vcfLine: string): LocusLine {
function extractVariant(vcfLine: string): Variant {
var parts = vcfLine.split('\t');
+ var maxFrequency = null;
+ var minFrequency = null;
+ if (parts.length>=7){
+ var params = parts[7].split(';');
+ for (var i=0;i;
+ return ;
}
componentDidMount() {
@@ -80,11 +82,31 @@ class VariantTrack extends React.Component {
ctx.fillStyle = style.VARIANT_FILL;
ctx.strokeStyle = style.VARIANT_STROKE;
variants.forEach(variant => {
+ var variantHeightRatio = 1.0;
+ if (this.props.options.variantHeightByFrequency) {
+ var frequency = null;
+ if (this.props.options.allelFrequencyStrategy === undefined) { //default startegy
+ frequency = variant.majorFrequency;
+ } else if (this.props.options.allelFrequencyStrategy === AllelFrequencyStrategy.Major) {
+ frequency = variant.majorFrequency;
+ } else if (this.props.options.allelFrequencyStrategy === AllelFrequencyStrategy.Minor) {
+ frequency = variant.minorFrequency;
+ } else {
+ console.log("Unknown AllelFrequencyStrategy: ",this.props.options.allelFrequencyStrategy);
+ }
+ if (frequency !== null && frequency !== undefined) {
+ variantHeightRatio = frequency;
+ }
+ }
+ var height = style.VARIANT_HEIGHT*variantHeightRatio;
+ var variantY = y - 0.5 + style.VARIANT_HEIGHT - height;
+ var variantX = Math.round(scale(variant.position)) - 0.5;
+ var width = Math.round(scale(variant.position + 1)) - 0.5 - variantX;
+
ctx.pushObject(variant);
- var x = Math.round(scale(variant.position));
- var width = Math.round(scale(variant.position + 1)) - 1 - x;
- ctx.fillRect(x - 0.5, y - 0.5, width, style.VARIANT_HEIGHT);
- ctx.strokeRect(x - 0.5, y - 0.5, width, style.VARIANT_HEIGHT);
+
+ ctx.fillRect(variantX, variantY, width, height);
+ ctx.strokeRect(variantX, variantY, width, height);
ctx.popObject();
});
@@ -99,10 +121,19 @@ class VariantTrack extends React.Component {
ctx = canvasUtils.getContext(canvas),
trackingCtx = new dataCanvas.ClickTrackingContext(ctx, x, y);
this.renderScene(trackingCtx);
- var variant = trackingCtx.hit && trackingCtx.hit[0];
- var alert = window.alert || console.log;
- if (variant) {
- alert(JSON.stringify(variant));
+
+ var variants = trackingCtx.hit;
+ if (variants && variants.length>0) {
+ var data = [];
+ for (var i=0;i {
+ expect(features).to.have.length(1);
+ expect(features[0].contig).to.equal('20');
+ expect(features[0].majorFrequency).to.equal(0.7);
+ expect(features[0].minorFrequency).to.equal(0.7);
+ });
+ });
+
+ it('should have highest frequency', function() {
+ var vcf = new VcfFile(new RemoteFile('/test-data/allelFrequency.vcf'));
+ var range = new ContigInterval('chr20', 61730, 61740);
+ return vcf.getFeaturesInRange(range).then(features => {
+ expect(features).to.have.length(1);
+ expect(features[0].contig).to.equal('20');
+ expect(features[0].majorFrequency).to.equal(0.6);
+ expect(features[0].minorFrequency).to.equal(0.3);
+ });
+ });
+
it('should add chr', function() {
var vcf = new VcfFile(new RemoteFile('/test-data/snv.vcf'));
var range = new ContigInterval('chr20', 63799, 69094);
diff --git a/src/test/viz/VariantTrack.js b/src/test/viz/VariantTrack.js
new file mode 100644
index 00000000..06842461
--- /dev/null
+++ b/src/test/viz/VariantTrack.js
@@ -0,0 +1,75 @@
+/**
+ * @flow
+ */
+'use strict';
+
+import {expect} from 'chai';
+
+import pileup from '../../main/pileup';
+import dataCanvas from 'data-canvas';
+import {waitFor} from '../async';
+
+import ReactTestUtils from 'react-addons-test-utils';
+
+describe('VariantTrack', function() {
+ var testDiv = document.getElementById('testdiv');
+
+ beforeEach(() => {
+ testDiv.style.width = '700px';
+ dataCanvas.RecordingContext.recordAll();
+ });
+
+ afterEach(() => {
+ dataCanvas.RecordingContext.reset();
+ // avoid pollution between tests.
+ testDiv.innerHTML = '';
+ });
+ var {drawnObjects} = dataCanvas.RecordingContext;
+
+ function ready() {
+ return testDiv.getElementsByTagName('canvas').length > 0 &&
+ drawnObjects(testDiv, '.variants').length > 0;
+ }
+
+ it('should render variants', function() {
+ var variantClickedData = null;
+ var variantClicked = function (data) {
+ variantClickedData = data;
+ };
+ var p = pileup.create(testDiv, {
+ range: {contig: '17', start: 9386380, stop: 9537390},
+ tracks: [
+ {
+ viz: pileup.viz.genome(),
+ data: pileup.formats.twoBit({
+ url: '/test-data/test.2bit'
+ }),
+ isReference: true
+ },
+ {
+ data: pileup.formats.vcf({
+ url: '/test-data/test.vcf'
+ }),
+ viz: pileup.viz.variants(),
+ options: {onVariantClicked: variantClicked},
+ }
+ ]
+ });
+
+ return waitFor(ready, 2000)
+ .then(() => {
+ var variants = drawnObjects(testDiv, '.variants');
+ expect(variants.length).to.be.equal(1);
+ var canvasList = testDiv.getElementsByTagName('canvas');
+ var canvas = canvasList[1];
+ expect(variantClickedData).to.be.null;
+
+ //check clicking on variant
+ ReactTestUtils.Simulate.click(canvas,{nativeEvent: {offsetX: -0.5, offsetY: -15.5}});
+
+ expect(variantClickedData).to.not.be.null;
+ p.destroy();
+ });
+ });
+
+});
diff --git a/test-data/allelFrequency.vcf b/test-data/allelFrequency.vcf
new file mode 100644
index 00000000..c63f5379
--- /dev/null
+++ b/test-data/allelFrequency.vcf
@@ -0,0 +1,21 @@
+##fileformat=VCFv4.1
+##source=VarScan2
+##INFO=
+##INFO=
+##INFO=
+##INFO=
+##INFO=
+##INFO=
+##FILTER=
+##FILTER=
+##FORMAT=
+##FORMAT=
+##FORMAT=
+##FORMAT=
+##FORMAT=
+##FORMAT=
+##FORMAT=
+#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT NORMAL TUMOR
+20 61795 . G T . PASS DP=81;SS=1;SSC=2;GPV=4.6768E-16;SPV=5.4057E-1;AF=0.7 GT:GQ:DP:RD:AD:FREQ:DP4 0/1:.:44:22:22:50%:16,6,9,13 0/1:.:37:18:19:51.35%:10,8,10,9
+20 62731 . C A,G . PASS DP=68;SS=1;SSC=1;GPV=1.4855E-11;SPV=7.5053E-1;AF=0.4,0.5 GT:GQ:DP:RD:AD:FREQ:DP4 0/1:.:32:17:15:46.88%:9,8,9,6 0/1:.:36:21:15:41.67%:8,13,8,7
+20 61731 . C A,G,T . PASS DP=68;SS=1;SSC=1;GPV=1.4855E-11;SPV=7.5053E-1;AF=0.4,0.6,0.3 GT:GQ:DP:RD:AD:FREQ:DP4 0/1:.:32:17:15:46.88%:9,8,9,6 0/1:.:36:21:15:41.67%:8,13,8,7
diff --git a/test-data/snv.chr17.vcf b/test-data/snv.chr17.vcf
index bcb95918..f391885c 100644
--- a/test-data/snv.chr17.vcf
+++ b/test-data/snv.chr17.vcf
@@ -18,7 +18,7 @@
#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT NORMAL TUMOR
17 125 . G T . PASS DP=81;SS=1;SSC=2;GPV=4.6768E-16;SPV=5.4057E-1 GT:GQ:DP:RD:AD:FREQ:DP4 0/1:.:44:22:22:50%:16,6,9,13 0/1:.:37:18:19:51.35%:10,8,10,9
17 7512444 . G T . PASS DP=81;SS=1;SSC=2;GPV=4.6768E-16;SPV=5.4057E-1 GT:GQ:DP:RD:AD:FREQ:DP4 0/1:.:44:22:22:50%:16,6,9,13 0/1:.:37:18:19:51.35%:10,8,10,9
-17 7512454 . C A . PASS DP=68;SS=1;SSC=1;GPV=1.4855E-11;SPV=7.5053E-1 GT:GQ:DP:RD:AD:FREQ:DP4 0/1:.:32:17:15:46.88%:9,8,9,6 0/1:.:36:21:15:41.67%:8,13,8,7
+17 7512454 . C A . PASS DP=68;SS=1;SSC=1;GPV=1.4855E-11;SPV=7.5053E-1;AF=0.73 GT:GQ:DP:RD:AD:FREQ:DP4 0/1:.:32:17:15:46.88%:9,8,9,6 0/1:.:36:21:15:41.67%:8,13,8,7
17 7512544 . C T . PASS DP=72;SS=1;SSC=7;GPV=3.6893E-16;SPV=1.8005E-1 GT:GQ:DP:RD:AD:FREQ:DP4 0/1:.:39:19:19:50%:8,11,11,8 0/1:.:33:12:21:63.64%:5,7,8,13
17 7512644 . G T . PASS DP=35;SS=1;SSC=0;GPV=7.8434E-5;SPV=8.2705E-1 GT:GQ:DP:RD:AD:FREQ:DP4 0/1:.:21:13:8:38.1%:4,9,0,8 0/1:.:14:10:4:28.57%:2,8,0,4
17 7512244 . G A . PASS DP=53;SS=1;SSC=0;GPV=1.5943E-31;SPV=1E0 GT:GQ:DP:RD:AD:FREQ:DP4 1/1:.:26:0:26:100%:0,0,12,14 1/1:.:27:0:27:100%:0,0,15,12
diff --git a/test-data/test.vcf b/test-data/test.vcf
new file mode 100644
index 00000000..732d6007
--- /dev/null
+++ b/test-data/test.vcf
@@ -0,0 +1,19 @@
+##fileformat=VCFv4.1
+##source=VarScan2
+##INFO=
+##INFO=
+##INFO=
+##INFO=
+##INFO=
+##INFO=
+##FILTER=
+##FILTER=
+##FORMAT=
+##FORMAT=
+##FORMAT=
+##FORMAT=
+##FORMAT=
+##FORMAT=
+##FORMAT=
+#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT NORMAL TUMOR
+17 9386385 . G T . PASS DP=81;SS=1;SSC=2;GPV=4.6768E-16;SPV=5.4057E-1;AF=0.7 GT:GQ:DP:RD:AD:FREQ:DP4 0/1:.:44:22:22:50%:16,6,9,13 0/1:.:37:18:19:51.35%:10,8,10,9