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

GCContent adapter #1624

Merged
merged 2 commits into from
Jan 22, 2021
Merged
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
1 change: 1 addition & 0 deletions plugins/sequence/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"peerDependencies": {
"@jbrowse/core": "^1.0.0",
"@jbrowse/plugin-linear-genome-view": "^1.0.0",
"@jbrowse/plugin-wiggle": "^1.0.0",
"@material-ui/core": "^4.9.13",
"mobx-react": "^6.0.0",
"mobx-state-tree": "3.14.1",
Expand Down
174 changes: 174 additions & 0 deletions plugins/sequence/src/GCContentAdapter/GCContentAdapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import {
BaseFeatureDataAdapter,
RegionsAdapter,
BaseOptions,
} from '@jbrowse/core/data_adapters/BaseAdapter'
import { Region, NoAssemblyRegion } from '@jbrowse/core/util/types'
import { getSubAdapterType } from '@jbrowse/core/data_adapters/dataAdapterCache'
import { ObservableCreate } from '@jbrowse/core/util/rxjs'
import SimpleFeature, { Feature } from '@jbrowse/core/util/simpleFeature'
import { readConfObject } from '@jbrowse/core/configuration'
import { AnyConfigurationModel } from '@jbrowse/core/configuration/configurationSchema'
import { toArray } from 'rxjs/operators'

import { blankStats, rectifyStats, scoresToStats } from '@jbrowse/plugin-wiggle'

export default class extends BaseFeatureDataAdapter implements RegionsAdapter {
private sequenceAdapter: BaseFeatureDataAdapter

private windowSize = 1000

private windowDelta = 1000

private gcMode = 'content'

public static capabilities = ['hasLocalStats']

public constructor(
config: AnyConfigurationModel,
getSubAdapter?: getSubAdapterType,
) {
super(config)
// instantiate the sequence adapter
const sequenceAdapterType = readConfObject(config, [
'sequenceAdapter',
'type',
])

const dataAdapter = getSubAdapter?.(
readConfObject(config, 'sequenceAdapter'),
).dataAdapter
if (dataAdapter instanceof BaseFeatureDataAdapter) {
this.sequenceAdapter = dataAdapter
} else {
throw new Error(
`Feature adapters cannot use sequence adapters of type '${sequenceAdapterType}'`,
)
}
}

public getRefNames() {
return this.sequenceAdapter.getRefNames()
}

public async getRegions(): Promise<NoAssemblyRegion[]> {
// @ts-ignore
return this.sequenceAdapter.getRegions()
}

// Taken from bigwigadapter
public getRegionStats(region: Region, opts: BaseOptions) {
const feats = this.getFeatures(region, opts)
return scoresToStats(region, feats)
}

// Taken from bigwigadapter
public async getMultiRegionStats(regions: Region[] = [], opts: BaseOptions) {
if (!regions.length) {
return blankStats()
}
const feats = await Promise.all(
regions.map(region => this.getRegionStats(region, opts)),
)

const scoreMax = feats
.map(s => s.scoreMax)
.reduce((acc, curr) => Math.max(acc, curr))
const scoreMin = feats
.map(s => s.scoreMin)
.reduce((acc, curr) => Math.min(acc, curr))
const scoreSum = feats.map(s => s.scoreSum).reduce((a, b) => a + b, 0)
const scoreSumSquares = feats
.map(s => s.scoreSumSquares)
.reduce((a, b) => a + b, 0)
const featureCount = feats
.map(s => s.featureCount)
.reduce((a, b) => a + b, 0)
const basesCovered = feats
.map(s => s.basesCovered)
.reduce((a, b) => a + b, 0)

return rectifyStats({
scoreMin,
scoreMax,
featureCount,
basesCovered,
scoreSumSquares,
scoreSum,
})
}

/**
* Fetch features for a certain region
* @param param -
* @returns Observable of Feature objects in the region
*/
public getFeatures(query: Region, opts: BaseOptions) {
this.windowSize = 1000
this.windowDelta = 1000
this.gcMode = 'content'
return ObservableCreate<Feature>(async observer => {
const hw = this.windowSize === 1 ? 1 : this.windowSize / 2 // Half the window size
const f = this.windowSize === 1

let { start: queryStart, end: queryEnd } = query
queryStart = Math.max(0, queryStart - hw)
queryEnd += hw

if (queryEnd < 0 || queryStart > queryEnd) {
observer.complete()
return
}

const ret = this.sequenceAdapter.getFeatures(
{ ...query, start: queryStart, end: queryEnd },
opts,
)
const [feat] = await ret.pipe(toArray()).toPromise()
const residues = feat.get('seq')

for (let i = hw; i < residues.length - hw; i += this.windowDelta) {
const r = f ? residues[i] : residues.slice(i - hw, i + hw)
let nc = 0
let ng = 0
let len = 0
for (let j = 0; j < r.length; j++) {
if (r[j] === 'c' || r[j] === 'C') {
nc++
} else if (r[j] === 'g' || r[j] === 'G') {
ng++
}
if (r[j] !== 'N') {
len++
}
}
const pos = queryStart
let score
if (this.gcMode === 'content') {
score = (ng + nc) / (len || 1)
} else if (this.gcMode === 'skew') {
score = (ng - nc) / (ng + nc || 1)
}

// if (r[Math.floor(r.length / 2)] !== 'N') {
observer.next(
new SimpleFeature({
uniqueId: `${this.id}_${pos + i}`,
start: pos + i,
end: pos + i + this.windowDelta,
score,
}),
)
// }
}
observer.complete()
})
}

/**
* called to provide a hint that data tied to a certain region
* will not be needed for the forseeable future and can be purged
* from caches, etc
*/
public freeResources(/* { region } */): void {}
}
12 changes: 12 additions & 0 deletions plugins/sequence/src/GCContentAdapter/configSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import PluginManager from '@jbrowse/core/PluginManager'
import { ConfigurationSchema } from '@jbrowse/core/configuration'

export default (pluginManager: PluginManager) => {
return ConfigurationSchema(
'GCContentAdapter',
{
sequenceAdapter: pluginManager.pluggableConfigSchemaType('adapter'),
},
{ explicitlyTyped: true },
)
}
10 changes: 10 additions & 0 deletions plugins/sequence/src/GCContentAdapter/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import PluginManager from '@jbrowse/core/PluginManager'
import configSchemaF from './configSchema'
import AdapterClass from './GCContentAdapter'

export default (pluginManager: PluginManager) => {
return {
configSchema: pluginManager.load(configSchemaF),
AdapterClass,
}
}
8 changes: 8 additions & 0 deletions plugins/sequence/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
AdapterClass as TwoBitAdapterClass,
configSchema as twoBitAdapterConfigSchema,
} from './TwoBitAdapter'
import GCContentAdapterF from './GCContentAdapter'
import { createReferenceSeqTrackConfig } from './referenceSeqTrackConfig'

/* adjust in both directions */
Expand Down Expand Up @@ -83,6 +84,13 @@ export default class SequencePlugin extends Plugin {
}),
)

pluginManager.addAdapterType(
() =>
new AdapterType({
name: 'GCContentAdapter',
...pluginManager.load(GCContentAdapterF),
}),
)
pluginManager.addTrackType(() => {
const configSchema = createReferenceSeqTrackConfig(pluginManager)

Expand Down
1 change: 1 addition & 0 deletions plugins/wiggle/src/LinearWiggleDisplay/models/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,7 @@ const stateModelFactory = (
},
)) as FeatureStats
const { scoreMin, scoreMean, scoreStdDev } = results

// localsd uses heuristic to avoid unnecessary scoreMin<0
// if the scoreMin is never less than 0
// helps with most coverage bigwigs just being >0
Expand Down
22 changes: 22 additions & 0 deletions test_data/config_demo.json
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,28 @@
}
}
},
{
"type": "QuantitativeTrack",
"trackId": "gccontent_hg19",
"name": "GCContent",
"assemblyNames": ["hg19"],
"adapter": {
"type": "GCContentAdapter",
"sequenceAdapter": {
"type": "BgzipFastaAdapter",
"fastaLocation": {
"uri": "https://jbrowse.org/genomes/hg19/fasta/hg19.fa.gz"
},
"faiLocation": {
"uri": "https://jbrowse.org/genomes/hg19/fasta/hg19.fa.gz.fai"
},
"gziLocation": {
"uri": "https://jbrowse.org/genomes/hg19/fasta/hg19.fa.gz.gzi"
}
}
}
},

{
"type": "AlignmentsTrack",
"trackId": "illumina_hg002",
Expand Down