Skip to content

Commit

Permalink
Refactor CNVPytorTrack to allow conversion from a VariantTrack
Browse files Browse the repository at this point in the history
  • Loading branch information
jrobinso committed Aug 30, 2023
1 parent 766fa63 commit 14e2915
Show file tree
Hide file tree
Showing 7 changed files with 251 additions and 40 deletions.
56 changes: 56 additions & 0 deletions dev/cnvpytor/cnvpytorTrackVCF.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link href=https://igv.org/web/img/favicon.ico rel="shortcut icon">
<title>igv</title>

</head>

<body>

<h2>CNVpytor Track</h2>

<button id="log-state">Log Session Json</button>
<button id="pytor">Change to Pytor</button>

<div id="igvDiv" style="padding-top: 50px;padding-bottom: 20px; height: auto"></div>

<script type="module">

import igv from "../../js/index.js"

const options =
{
genome: "hg19",
locus: "chr16",
tracks: [
// {
// type: "cnvpytor",
// name: "HepG2 VCF",
// url: "https://igv-genepattern-org.s3.amazonaws.com/test/pytor/HepG2.sample.vcf.gz"
//
// },
{
type: "variant",
name: "HepG2 VCF",
//url: "https://storage.googleapis.com/cnvpytor_data/HepG2.vcf.gz",
url: "https://igv-genepattern-org.s3.amazonaws.com/test/pytor/HepG2.sample.vcf.gz"
}
]
}

igv.createBrowser(document.getElementById('igvDiv'), options)
.then(browser => {
const track = browser.findTracks("type", "variant")[0];
document.getElementById("log-state").addEventListener("click", () => console.log(browser.toJSON()))
document.getElementById("pytor").addEventListener("click", () => {
track.convertToPytor()
})
})


</script>

</body>

</html>
50 changes: 50 additions & 0 deletions dev/cnvpytor/cnvpytorTrackVCF_large.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link href=https://igv.org/web/img/favicon.ico rel="shortcut icon">
<title>igv</title>

</head>

<body>

<h2>CNVpytor Track</h2>

<button id="log-state">Log Session Json</button>
<button id="pytor">Change to Pytor</button>

<div id="igvDiv" style="padding-top: 50px;padding-bottom: 20px; height: auto"></div>

<script type="module">

import igv from "../../js/index.js"

const options =
{
genome: "hg19",
locus: "chr16",
tracks: [
{
type: "variant",
name: "HepG2 VCF",
//maxVariantCount: 1000000,
url: "https://igv-genepattern-org.s3.amazonaws.com/test/pytor/HepG2.vcf.gz"
}
]
}

igv.createBrowser(document.getElementById('igvDiv'), options)
.then(browser => {
const track = browser.findTracks("type", "variant")[0];
document.getElementById("log-state").addEventListener("click", () => console.log(browser.toJSON()))
document.getElementById("pytor").addEventListener("click", () => {
track.convertToPytor()
})
})


</script>

</body>

</html>
4 changes: 2 additions & 2 deletions examples/cnvpytor/cnvpytorTrackVCF.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<body>

<h2>CNVpytor Track</h2>

<h4>Input: VCF file with ~ 3,336,000 sites</h4>

<div id="igvDiv" style="padding-top: 50px;padding-bottom: 20px; height: auto"></div>

Expand All @@ -24,7 +24,7 @@ <h2>CNVpytor Track</h2>
{
type: "cnvpytor",
name: "HepG2 VCF",
url: "https://storage.googleapis.com/cnvpytor_data/HepG2.vcf.gz",
url: "https://igv-genepattern-org.s3.amazonaws.com/test/pytor/HepG2.vcf.gz",

}
]
Expand Down
41 changes: 41 additions & 0 deletions examples/cnvpytor/cnvpytorTrackVCF_small.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link href=https://igv.org/web/img/favicon.ico rel="shortcut icon">
<title>igv</title>

</head>

<body>

<h2>CNVpytor Track</h2>
<h4>Input: VCF file with ~16,700 sites</h4>


<div id="igvDiv" style="padding-top: 50px;padding-bottom: 20px; height: auto"></div>

<script type="module">

import igv from "../../dist/igv.esm.js"
const options =
{
genome: "hg19",
locus: "chr16",
tracks: [
{
type: "cnvpytor",
name: "HepG2 VCF",
url: "https://igv-genepattern-org.s3.amazonaws.com/test/pytor/HepG2.sample.vcf.gz",

}
]
}

igv.createBrowser(document.getElementById('igvDiv'), options)


</script>

</body>

</html>
3 changes: 2 additions & 1 deletion examples/cnvpytor/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ <h2>cnvpytor Tracks</h2

<ul>
<li><a href=cnvpytorTrack.html>cnvpytorTrack.html</a> Reads a cnvpytor file and generate view</li>
<li><a href=cnvpytorTrackVCF.html>cnvpytorTrackVCF.html</a> Read and process a VCF file for read depth and BAF information, and creates a view. User need to wait a bit as it process the input file on fly</li>
<li><a href=cnvpytorTrackVCF.html>cnvpytorTrackVCF.html</a> Read and process a large VCF file (~3,000,000 sites) for read depth and BAF information, and creates a view. User need to wait a bit as it process the input file on fly</li>
<li><a href=cnvpytorTrackVCF_small.html>cnvpytorTrackVCF_SMALL.html</a> Read and process a small VCF file (~16,500 sites) for read depth and BAF information, and creates a view. User need to wait a bit as it process the input file on fly</li>
</ul>

</body>
Expand Down
107 changes: 70 additions & 37 deletions js/cnvpytor/cnvpytorTrack.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,27 +46,28 @@ class CNVPytorTrack extends TrackBase {

constructor(config, browser) {
super(config, browser)
}

async init(config) {

super.init(config)
this.featureType = 'numeric'
this.paintAxis = paintAxis

if (!config.max) {
this.defaultScale = true
this.autoscale = false
}

// Invoke height setter last to allocated to coverage and alignment tracks
this.height = (config.height !== undefined ? config.height : DEFAULT_TRACK_HEIGHT)
}

async init(config) {

this.type = "cnvpytor"
this.graphType = config.graphType || "points"
this.bin_size = config.bin_size || 100000
this.signal_name = config.signal_name || "rd_snp"
this.cnv_caller = config.cnv_caller || '2D'
this.colors = config.colors || ['gray', 'black', 'green', 'blue']
super.init(config)


}

Expand All @@ -93,67 +94,77 @@ class CNVPytorTrack extends TrackBase {
}

get_signal_colors() {

let signal_colors = [
{ singal_name: 'RD_Raw', color: this.colors[0] },
{ singal_name: 'RD_Raw_gc_coor', color: this.colors[1] },
{ singal_name: 'ReadDepth', color: this.colors[2] },
{ singal_name: '2D', color: this.colors[2] },
{ singal_name: 'BAF1', color: this.colors[3] },
{ singal_name: 'BAF2', color: this.colors[3] },
{singal_name: 'RD_Raw', color: this.colors[0]},
{singal_name: 'RD_Raw_gc_coor', color: this.colors[1]},
{singal_name: 'ReadDepth', color: this.colors[2]},
{singal_name: '2D', color: this.colors[2]},
{singal_name: 'BAF1', color: this.colors[3]},
{singal_name: 'BAF2', color: this.colors[3]},
]
return signal_colors
}

async postInit() {

if (this.config.format == 'vcf') {
this.featureSource = FeatureSource(this.config, this.browser.genome)
this.header = await this.getHeader()

let allVariants
if (this.config.allVariants) {
allVariants = this.config.allVariants
delete this.config.allVariants // Transient variable -- don't store in session
} else {
this.featureSource = FeatureSource(this.config, this.browser.genome)
this.header = await this.getHeader()
allVariants = this.featureSource.reader.features.reduce(function (r, a) {
r[a.chr] = r[a.chr] || []
r[a.chr].push(a)
return r
}, Object.create(null))
}

var allVariants = this.featureSource.reader.features.reduce(function (r, a) {
r[a.chr] = r[a.chr] || []
r[a.chr].push(a)
return r
}, Object.create(null))
// downsample
if(this.config.maxVariantCount) {
allVariants = downscaleVariants(allVariants, this.config.maxVariantCount)
}

const cnvpytor_obj = new CNVpytorVCF(allVariants, this.bin_size)
let wigFeatures;
let bafFeatures;

let wigFeatures
let bafFeatures
this.wigFeatures_obj = {}
this.wigFeatures_obj[this.bin_size] = {}

let dataWigs;
if(this.config.cnv_caller == '2D'){
let dataWigs
if (this.config.cnv_caller == '2D') {

dataWigs = await cnvpytor_obj.read_rd_baf('2D')

wigFeatures = dataWigs[0]
bafFeatures = dataWigs[1]
this.wigFeatures_obj[this.bin_size]['2D'] = wigFeatures[2]

this.available_callers = ['2D']
}else{
} else {
dataWigs = await cnvpytor_obj.read_rd_baf()
wigFeatures = dataWigs[0]
bafFeatures = dataWigs[1]
this.wigFeatures_obj[this.bin_size]['ReadDepth'] = wigFeatures[2]
this.available_callers = ['ReadDepth']
}

this.wigFeatures_obj[this.bin_size]['RD_Raw'] = wigFeatures[0]
this.wigFeatures_obj[this.bin_size]['RD_Raw_gc_coor'] = wigFeatures[1]
this.wigFeatures_obj[this.bin_size]['BAF1'] = bafFeatures[0]
this.wigFeatures_obj[this.bin_size]['BAF2'] = bafFeatures[1]

this.available_bins = [this.bin_size]

this.set_available_callers()

} else {
this.cnvpytor_obj = new HDF5IndexedReader(this.config.url, this.bin_size)
this.cnvpytor_obj = new HDF5IndexedReader(this.config.url, this.bin_size)
this.wigFeatures_obj = await this.cnvpytor_obj.get_rd_signal(this.bin_size)
this.available_bins = this.cnvpytor_obj.available_bins
this.available_callers = this.cnvpytor_obj.callers
Expand Down Expand Up @@ -343,7 +354,7 @@ class CNVPytorTrack extends TrackBase {
tconf.isMergedTrack = true
tconf.features = wig
tconf.name = signal_name
tconf.color = this.signal_colors.filter(x => x.singal_name === signal_name).map(x => x.color)
tconf.color = this.signal_colors.filter(x => x.singal_name === signal_name).map(x => x.color)
const t = await this.browser.createTrack(tconf)
if (t) {
t.autoscale = false // Scaling done from merged track
Expand Down Expand Up @@ -379,7 +390,7 @@ class CNVPytorTrack extends TrackBase {

async getFeatures(chr, bpStart, bpEnd, bpPerPixel) {

if(this.tracks) {
if (this.tracks) {
const promises = this.tracks.map((t) => t.getFeatures(chr, bpStart, bpEnd, bpPerPixel))
return Promise.all(promises)
} else {
Expand Down Expand Up @@ -412,7 +423,7 @@ class CNVPytorTrack extends TrackBase {

// const mergedFeatures = options.features // Array of feature arrays, 1 for each track
const mergedFeatures = options.features
if(!mergedFeatures) return
if (!mergedFeatures) return

if (this.defaultScale) {
if (this.signal_name == 'rd_snp') {
Expand All @@ -437,7 +448,7 @@ class CNVPytorTrack extends TrackBase {
this.dataRange = autoscale(options.referenceFrame.chr, mergedFeatures)
}

if(this.tracks) {
if (this.tracks) {
for (let i = 0, len = this.tracks.length; i < len; i++) {
const trackOptions = Object.assign({}, options)
trackOptions.features = mergedFeatures[i]
Expand All @@ -457,8 +468,8 @@ class CNVPytorTrack extends TrackBase {
? this.computeYPixelValueInLogScale(yValue, scaleFactor)
: this.computeYPixelValue(yValue, scaleFactor)

// Draw guidelines
if (this.config.hasOwnProperty('guideLines')) {
// Draw guidelines
if (this.config.hasOwnProperty('guideLines')) {
for (let line of this.config.guideLines) {
if (line.hasOwnProperty('color') && line.hasOwnProperty('y') && line.hasOwnProperty('dotted')) {
let y = yScale(line.y)
Expand Down Expand Up @@ -508,4 +519,26 @@ function autoscale(chr, featureArrays) {
return {min: min, max: max}
}

function downscaleVariants(allVariants, targetCount) {

const totalCount = Object.values(allVariants).reduce((accumulator, currentValue) => accumulator + currentValue.length, 0);
const prob = targetCount / totalCount
if(prob < 1) {
const downsampledVariants = {}
for(let key of Object.keys(allVariants)) {
const tmp = []
for(let v of allVariants[key]) {
if(Math.random() < prob || tmp.length < 10) { // Keep at least 10 for each chromosome
tmp.push(v)
}
}
downsampledVariants[key] = tmp
}
return downsampledVariants
} else {
return allVariants
}

}

export default CNVPytorTrack
Loading

0 comments on commit 14e2915

Please sign in to comment.