From 5e5c3f3c91209be4cb02d96ed2503630da2fef84 Mon Sep 17 00:00:00 2001 From: Colin Date: Tue, 19 Jan 2021 09:10:11 -0700 Subject: [PATCH 1/2] Updates --- plugins/sequence/package.json | 1 + .../src/GCContentAdapter/GCContentAdapter.ts | 178 ++++++++++++++++++ .../src/GCContentAdapter/configSchema.ts | 12 ++ .../sequence/src/GCContentAdapter/index.ts | 10 + plugins/sequence/src/index.ts | 8 + .../src/LinearWiggleDisplay/models/model.ts | 1 + test_data/config_demo.json | 22 +++ 7 files changed, 232 insertions(+) create mode 100644 plugins/sequence/src/GCContentAdapter/GCContentAdapter.ts create mode 100644 plugins/sequence/src/GCContentAdapter/configSchema.ts create mode 100644 plugins/sequence/src/GCContentAdapter/index.ts diff --git a/plugins/sequence/package.json b/plugins/sequence/package.json index d9a1da7659..2e50b59783 100644 --- a/plugins/sequence/package.json +++ b/plugins/sequence/package.json @@ -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", diff --git a/plugins/sequence/src/GCContentAdapter/GCContentAdapter.ts b/plugins/sequence/src/GCContentAdapter/GCContentAdapter.ts new file mode 100644 index 0000000000..548a0b2a4f --- /dev/null +++ b/plugins/sequence/src/GCContentAdapter/GCContentAdapter.ts @@ -0,0 +1,178 @@ +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/src/statsUtil' + +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 { + // @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(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 {} +} diff --git a/plugins/sequence/src/GCContentAdapter/configSchema.ts b/plugins/sequence/src/GCContentAdapter/configSchema.ts new file mode 100644 index 0000000000..8614a53b25 --- /dev/null +++ b/plugins/sequence/src/GCContentAdapter/configSchema.ts @@ -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 }, + ) +} diff --git a/plugins/sequence/src/GCContentAdapter/index.ts b/plugins/sequence/src/GCContentAdapter/index.ts new file mode 100644 index 0000000000..acc483fc43 --- /dev/null +++ b/plugins/sequence/src/GCContentAdapter/index.ts @@ -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, + } +} diff --git a/plugins/sequence/src/index.ts b/plugins/sequence/src/index.ts index c72c3da536..83fa0d662f 100644 --- a/plugins/sequence/src/index.ts +++ b/plugins/sequence/src/index.ts @@ -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 */ @@ -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) diff --git a/plugins/wiggle/src/LinearWiggleDisplay/models/model.ts b/plugins/wiggle/src/LinearWiggleDisplay/models/model.ts index d47b850f2c..3962593c6d 100644 --- a/plugins/wiggle/src/LinearWiggleDisplay/models/model.ts +++ b/plugins/wiggle/src/LinearWiggleDisplay/models/model.ts @@ -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 diff --git a/test_data/config_demo.json b/test_data/config_demo.json index 78b0613aed..b1977171e8 100644 --- a/test_data/config_demo.json +++ b/test_data/config_demo.json @@ -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", From e424990f721ad6e8d3f2e4c096a353b453f12eba Mon Sep 17 00:00:00 2001 From: Colin Date: Tue, 19 Jan 2021 10:40:52 -0700 Subject: [PATCH 2/2] Import statsUtils from root instead of src --- plugins/sequence/src/GCContentAdapter/GCContentAdapter.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/plugins/sequence/src/GCContentAdapter/GCContentAdapter.ts b/plugins/sequence/src/GCContentAdapter/GCContentAdapter.ts index 548a0b2a4f..9a91e440fa 100644 --- a/plugins/sequence/src/GCContentAdapter/GCContentAdapter.ts +++ b/plugins/sequence/src/GCContentAdapter/GCContentAdapter.ts @@ -11,11 +11,7 @@ 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/src/statsUtil' +import { blankStats, rectifyStats, scoresToStats } from '@jbrowse/plugin-wiggle' export default class extends BaseFeatureDataAdapter implements RegionsAdapter { private sequenceAdapter: BaseFeatureDataAdapter