Skip to content

Commit

Permalink
Shoebox updates
Browse files Browse the repository at this point in the history
  • Loading branch information
jrobinso committed Oct 26, 2024
1 parent 73ef5f1 commit 15db658
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 86 deletions.
82 changes: 42 additions & 40 deletions dev/shoebox/shoebox.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,52 +23,54 @@ <h1>Shoebox</h1>
{
sampleNameViewportWidth: 6,
genome: "hg38",
locus: "chr1:1,001,575-1,002,408",
"locus": "chr11:47,376,930-47,380,077",
"roi": [
{
"name": "ROI regions",
"url": "https://storage.googleapis.com/broad-buenrostro-pipeline-genome-annotations/neva/BMMC_roi/BMMC_20celltype_multiscale_roi_regions.bed",
"isVisible": false
}
],
tracks: [
{
"url": "https://www.dropbox.com/scl/fi/ayu63q7raqi47yduvvte5/pseudobulk_0.bed.gz?rlkey=ci5oqo928iljje4igk4wwjoor&dl=0",
"indexURL": "https://www.dropbox.com/scl/fi/bxz53av4v0ri9snidxj8g/pseudobulk_0.bed.gz.tbi?rlkey=qvc7zk8si0ays89cqjp05fdw9&dl=0",
// visibilityWindow: 10000
// "name": "pseudobulk 14",
// "type": "shoebox",
// "format": "shoebox"
"url": "https://storage.googleapis.com/broad-buenrostro-pipeline-genome-annotations/neva/BMMC_roi/NK.bed.gz",
"indexURL": "https://storage.googleapis.com/broad-buenrostro-pipeline-genome-annotations/neva/BMMC_roi/NK.bed.gz.tbi",
"name": "NK",
"format": "bed",
"type": "shoebox",
"color": "rgb(73, 26, 136)"
},
{
"url": "https://storage.googleapis.com/broad-buenrostro-pipeline-genome-annotations/neva/BMMC_roi/CLP.bed.gz",
"indexURL": "https://storage.googleapis.com/broad-buenrostro-pipeline-genome-annotations/neva/BMMC_roi/CLP.bed.gz.tbi",
"name": "CLP",
"format": "bed",
"type": "shoebox",
"color": "rgb(106, 207, 255)"
},
{
"url": "https://storage.googleapis.com/broad-buenrostro-pipeline-genome-annotations/neva/BMMC_roi/late-Ery.bed.gz",
"indexURL": "https://storage.googleapis.com/broad-buenrostro-pipeline-genome-annotations/neva/BMMC_roi/late-Ery.bed.gz.tbi",
"name": "late-Ery",
"format": "bed",
"type": "shoebox",
"color": "rgb(137, 17, 0)"
},
{
"name": "Homo sapiens HepG2 H3K4me3 ",
"url": "https://www.encodeproject.org/files/ENCFF752OCZ/@@download/ENCFF752OCZ.bigWig",
"color": "rgb(0,150,0)",
"metadata": {
"Biosample": "Homo sapiens HepG2",
"AssayType": "ChIP-seq",
"Target": "H3K4me3 ",
"BioRep": "1",
"TechRep": "1_1",
"OutputType": "signal p-value",
"Format": "bigWig",
"Lab": "John Stamatoyannopoulos, UW",
"Accession": "ENCFF752OCZ",
"Experiment": "ENCSR000DUF"
},
"format": "bigwig",
"type": "wig"
"url": "https://storage.googleapis.com/broad-buenrostro-pipeline-genome-annotations/neva/BMMC_roi/CD16mono.bed.gz",
"indexURL": "https://storage.googleapis.com/broad-buenrostro-pipeline-genome-annotations/neva/BMMC_roi/CD16mono.bed.gz.tbi",
"name": "CD16mono",
"format": "bed",
"type": "shoebox",
"color": "rgb(255, 136, 2)",
},
{
"name": "Homo sapiens HepG2 CTCF ",
"url": "https://www.encodeproject.org/files/ENCFF160QOX/@@download/ENCFF160QOX.bigWig",
"metadata": {
"Biosample": "Homo sapiens HepG2",
"AssayType": "ChIP-seq",
"Target": "CTCF ",
"BioRep": "1",
"TechRep": "1_1",
"OutputType": "fold change over control",
"Format": "bigWig",
"Lab": "Richard Myers, HAIB",
"Accession": "ENCFF160QOX",
"Experiment": "ENCSR000BIE"
},
"format": "bigwig",
"type": "wig"
"url": "https://storage.googleapis.com/broad-buenrostro-pipeline-genome-annotations/neva/BMMC_roi/NaiveB.bed.gz",
"indexURL": "https://storage.googleapis.com/broad-buenrostro-pipeline-genome-annotations/neva/BMMC_roi/NaiveB.bed.gz.tbi",
"name": "NaiveB",
"format": "bed",
"type": "shoebox",
"color": "rgb(210, 120, 255)"
}
]
}
Expand Down
24 changes: 1 addition & 23 deletions js/feature/decode/ucsc.js
Original file line number Diff line number Diff line change
Expand Up @@ -538,28 +538,6 @@ function decodeSNP(tokens, header) {

}

function decodeShoebox(tokens, header, maxColumnCount = Number.MAX_SAFE_INTEGER) {

if (tokens.length < 4) return undefined

const chr = tokens[0]
const start = parseInt(tokens[1])
const end = tokens.length > 2 ? parseInt(tokens[2]) : start + 1
if (isNaN(start) || isNaN(end)) {
return new DecodeError(`Unparsable bed record.`)
}
const feature = new UCSCBedFeature({chr: chr, start: start, end: end, score: 1000})

const values = []
for(let i = 3; i< tokens.length; i++) {
values.push(Number.parseInt(tokens[i]))
}
feature.values = values;


return feature
}


class UCSCBedFeature {

Expand Down Expand Up @@ -673,6 +651,6 @@ class PSLFeature {

export {
decodeBed, decodeBedGraph, decodeGenePred, decodeGenePredExt, decodePeak, decodeReflat, decodeRepeatMasker,
decodeSNP, decodeWig, decodePSL, decodeBedmethyl, decodeGappedPeak, decodeNarrowPeak, decodeShoebox
decodeSNP, decodeWig, decodePSL, decodeBedmethyl, decodeGappedPeak, decodeNarrowPeak
}

2 changes: 1 addition & 1 deletion js/feature/featureParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ import {
decodePeak,
decodeReflat,
decodeRepeatMasker,
decodeShoebox,
decodeSNP,
decodeWig
} from "./decode/ucsc.js"
Expand All @@ -45,6 +44,7 @@ import {decodeFusionJuncSpan} from "./decode/fusionJuncSpan.js"
import {decodeGtexGWAS} from "./decode/gtexGWAS.js"
import {decodeCustom} from "./decode/custom.js"
import {decodeGcnv} from "../gcnv/gcnvDecoder.js"
import decodeShoebox from "../shoebox/decodeShoebox.js"
import DecodeError from "./decode/decodeError.js"
import GFFHelper from "./gff/gffHelper.js"

Expand Down
23 changes: 23 additions & 0 deletions js/shoebox/decodeShoebox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import DecodeError from "../feature/decode/decodeError.js"

export default function decodeShoebox(tokens, header, maxColumnCount = Number.MAX_SAFE_INTEGER) {

if (tokens.length < 4) return undefined

const chr = tokens[0]
const start = parseInt(tokens[1])
const end = tokens.length > 2 ? parseInt(tokens[2]) : start + 1
if (isNaN(start) || isNaN(end)) {
return new DecodeError(`Unparsable bed record.`)
}
const feature = {chr, start, end}

const values = []
for(let i = 3; i< tokens.length; i++) {
values.push(Number.parseFloat(tokens[i]))
}
feature.values = values;


return feature
}
14 changes: 10 additions & 4 deletions js/shoebox/shoeboxColorScale.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ class ShoeboxColorScale {
this.nbins = 1000
this.binsize = (this.max - this.min) / this.nbins
this.updateColor(scale.color || "rgb(0,0,255)")
this.br = 255
this.bg = 255
this.bb = 255

}

Expand All @@ -57,13 +60,16 @@ class ShoeboxColorScale {
}

getColor(value) {
const low = 0
if (value < this.min) return "white"
if (value <= this.min) return "white"
else if (value >= this.max) return `rgb(${this.r},${this.g},${this.b})`

const bin = Math.floor((Math.min(this.max, value) - this.min) / this.binsize)

if (undefined === this.cache[bin]) {
const alpha = (IGVMath.clamp(value, low, this.max) - low) / (this.max - low)
this.cache[bin] = `rgba(${this.r},${this.g},${this.b}, ${alpha})`
const alpha = (value - this.min) / (this.max - this.min)
const beta = 1 - alpha
this.cache[bin] = //`rgba(${this.r},${this.g},${this.b}, ${alpha})`
`rgb(${ Math.floor(alpha*this.r + beta*this.br)},${Math.floor(alpha*this.g + beta*this.bg)},${Math.floor(alpha*this.b + beta*this.bb)})`
}
return this.cache[bin]
}
Expand Down
55 changes: 39 additions & 16 deletions js/shoebox/shoeboxTrack.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,47 @@ import TrackBase from "../trackBase.js"
import IGVGraphics from "../igv-canvas.js"
import ShoeboxColorScale from "./shoeboxColorScale.js"

/**
* Configurable properties
* min, max (viewlimits) - min / max values of the color scale. Alpha is linearly varied from 100% (min) to 0% (max)
* color - base color before alpha is applied
* rowHeight - height of each row
*/

class ShoeboxTrack extends TrackBase {

static defaults = {
height: 300,
rowHeight: 3,
max: 3000,
visibilityWindow: 10000
min: 0.5,
max: 3,
scale: 1.0,
visibilityWindow: 10000,
supportHiDPI: false
}

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

init(config) {

super.init(config)

this.type = "shoebox"
this.height = config.height || 300
this.rowHeight = config.rowHeight || 3
this.max = config.max || 3000
this.colorScale = new ShoeboxColorScale({min: 0, max: 3000, color: this.color})


// Hardcoded -- todo get from track line
// Hardcoded -- todo, perhaps, get from track line
this.sampleKeys = []
for (let i = 1; i <= 100; i++) {
this.sampleKeys.push(i)
}

if(config.max) {
this.dataRange = {
min: config.min || 0,
max: config.max
}
}
// Create featureSource
const configCopy = Object.assign({}, this.config)
configCopy.format = 'shoebox' // bit of a hack
Expand All @@ -47,9 +58,22 @@ class ShoeboxTrack extends TrackBase {
}
// Set properties from track line
if (this.header) {
if(this.header.scale) {
this.header.scale = Number.parseFloat(this.header.scale)
}
this.setTrackProperties(this.header)
}

// Must do the following after setting track properties as they can be overriden via a track line

// Color settings
const min = this.dataRange.min
const max = this.dataRange.max
this.colorScale = new ShoeboxColorScale({min, max, color: this.color})

// This shouldn't be neccessary
if(!this.scale) this.scale = 1.0

}

get color() {
Expand Down Expand Up @@ -92,6 +116,7 @@ class ShoeboxTrack extends TrackBase {
setDataRange({min, max}) {
this.dataRange.min = min
this.dataRange.max = max
this.colorScale.setMinMax(min, max)
this.trackView.repaintViews()
}

Expand All @@ -115,7 +140,6 @@ class ShoeboxTrack extends TrackBase {

draw({context, pixelTop, pixelWidth, pixelHeight, features, bpPerPixel, bpStart}) {


IGVGraphics.fillRect(context, 0, pixelTop, pixelWidth, pixelHeight, {'fillStyle': "rgb(255, 255, 255)"})

if (features && features.length > 0) {
Expand All @@ -132,15 +156,14 @@ class ShoeboxTrack extends TrackBase {
if (f.end < bpStart || f.start > bpEnd) continue

// Pixel x values
const xLeft = Math.round((f.start - bpStart) / bpPerPixel)
const xRight = Math.round((f.end - bpStart) / bpPerPixel)
const w = xRight - xLeft
const xLeft = Math.floor((f.start - bpStart) / bpPerPixel)
const xRight = Math.floor((f.end - bpStart) / bpPerPixel)
const w = Math.max(1, xRight - xLeft)

// Loop through value array

for (let i = f.values.length - 1; i >= 0; i--) {

const v = f.values[i]
const v = f.values[i] // / this.scale

if(v >= this.dataRange.min) {

Expand All @@ -153,11 +176,11 @@ class ShoeboxTrack extends TrackBase {
}

const color = this.colorScale.getColor(v)

context.fillStyle = color

context.fillRect(xLeft, y, w, h)

}

}
}
} else {
Expand Down
2 changes: 1 addition & 1 deletion js/trackBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ class TrackBase {
}
break
case "viewlimits":
if (!this.config.autoscale) { // autoscale in the config has precedence
if (!this.config.autoscale && !this.config.max) { //config has precedence
tokens = properties[key].split(":")
let min = 0
let max
Expand Down
2 changes: 1 addition & 1 deletion js/ui/navbarButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class NavbarButton {
}

configureTextButton(textContent) {
console.log(`text ${this.title}`)

this.button.classList.add('igv-navbar-text-button')

const tempDiv = document.createElement('div')
Expand Down

0 comments on commit 15db658

Please sign in to comment.