Skip to content

Commit

Permalink
Add better support for bedMethyl files (#4662)
Browse files Browse the repository at this point in the history
  • Loading branch information
cmdcolin authored Nov 18, 2024
1 parent 2d52a06 commit a439fd6
Show file tree
Hide file tree
Showing 21 changed files with 649 additions and 411 deletions.
15 changes: 15 additions & 0 deletions packages/core/data_adapters/BaseAdapter/BaseFeatureDataAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,4 +253,19 @@ export abstract class BaseFeatureDataAdapter extends BaseAdapter {
}
return this.getRegionFeatureDensityStats(regions[0]!, opts)
}

async getSources(
regions: Region[],
): Promise<{ name: string; color?: string; [key: string]: unknown }[]> {
const features = await firstValueFrom(
this.getFeaturesInMultipleRegions(regions).pipe(toArray()),
)
const sources = new Set<string>()
for (const f of features) {
sources.add(f.get('source'))
}
return [...sources].map(source => ({
name: source,
}))
}
}
29 changes: 18 additions & 11 deletions plugins/bed/src/BedAdapter/BedAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import {
} from '@jbrowse/core/data_adapters/BaseAdapter'
import { openLocation } from '@jbrowse/core/util/io'
import { ObservableCreate } from '@jbrowse/core/util/rxjs'
import { Region, Feature, fetchAndMaybeUnzip } from '@jbrowse/core/util'
import {
Region,
Feature,
fetchAndMaybeUnzip,
SimpleFeature,
} from '@jbrowse/core/util'
import IntervalTree from '@flatten-js/interval-tree'

// locals
Expand Down Expand Up @@ -123,17 +128,19 @@ export default class BedAdapter extends BaseFeatureDataAdapter {
const names = await this.getNames()

const intervalTree = new IntervalTree()
const ret = lines.map((f, i) => {
const ret = lines.map((line, i) => {
const uniqueId = `${this.id}-${refName}-${i}`
return featureData(
f,
colRef,
colStart,
colEnd,
scoreColumn,
parser,
uniqueId,
names,
return new SimpleFeature(
featureData({
line,
colRef,
colStart,
colEnd,
scoreColumn,
parser,
uniqueId,
names,
}),
)
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,15 @@ exports[`adapter can fetch features bed with autosql 1`] = `
"Variant_Type": "SNP",
"alcohol_history": "--",
"alcohol_intensity": "--",
"blockCount": 1,
"blockSizes": [
1,
],
"bmi": "--",
"case_id": "09454ed6-64bc-4a35-af44-7c4344623d45",
"chromStarts": [
0,
],
"cigarettes_per_day": "--",
"days_to_death": "--",
"dbSNP_RS": "novel",
Expand Down Expand Up @@ -160,6 +167,8 @@ exports[`adapter can fetch features bed with autosql 1`] = `
"uniqueId": "test-ctgA-0-0",
},
],
"thickEnd": 1815757,
"thickStart": 1815756,
"type": undefined,
"uniqueId": "test-ctgA-0",
"weight": "--",
Expand Down
30 changes: 17 additions & 13 deletions plugins/bed/src/BedTabixAdapter/BedTabixAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import {
} from '@jbrowse/core/data_adapters/BaseAdapter'
import { openLocation } from '@jbrowse/core/util/io'
import { ObservableCreate } from '@jbrowse/core/util/rxjs'
import { FileLocation, Region, Feature } from '@jbrowse/core/util'
import {
FileLocation,
Region,
Feature,
SimpleFeature,
} from '@jbrowse/core/util'
import { TabixIndexedFile } from '@gmod/tabix'
import PluginManager from '@jbrowse/core/PluginManager'
import { AnyConfigurationModel } from '@jbrowse/core/configuration'
Expand Down Expand Up @@ -78,22 +83,21 @@ export default class BedTabixAdapter extends BaseFeatureDataAdapter {
const colRef = columnNumbers.ref - 1
const colStart = columnNumbers.start - 1
const colEnd = columnNumbers.end - 1
// colSame handles special case for tabix where a single column is both
// the start and end, this is assumed to be covering the base at this
// position (e.g. tabix -s 1 -b 2 -e 2) begin and end are same
const names = await this.getNames()
await this.bed.getLines(query.refName, query.start, query.end, {
lineCallback: (line, fileOffset) => {
observer.next(
featureData(
line,
colRef,
colStart,
colEnd,
this.scoreColumn,
this.parser,
`${this.id}-${fileOffset}`,
names,
new SimpleFeature(
featureData({
line,
colRef,
colStart,
colEnd,
scoreColumn: this.scoreColumn,
parser: this.parser,
uniqueId: `${this.id}-${fileOffset}`,
names,
}),
),
)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,15 @@ exports[`adapter can fetch features bed with autosql 1`] = `
"Variant_Type": "SNP",
"alcohol_history": "--",
"alcohol_intensity": "--",
"blockCount": 1,
"blockSizes": [
1,
],
"bmi": "--",
"case_id": "09454ed6-64bc-4a35-af44-7c4344623d45",
"chromStarts": [
0,
],
"cigarettes_per_day": "--",
"days_to_death": "--",
"dbSNP_RS": "novel",
Expand Down Expand Up @@ -160,6 +167,8 @@ exports[`adapter can fetch features bed with autosql 1`] = `
"uniqueId": "test-52986-0",
},
],
"thickEnd": 1815757,
"thickStart": 1815756,
"type": undefined,
"uniqueId": "test-52986",
"weight": "--",
Expand Down
105 changes: 16 additions & 89 deletions plugins/bed/src/BigBedAdapter/BigBedAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,12 @@ import {
min,
Feature,
SimpleFeature,
SimpleFeatureSerializedNoId,
SimpleFeatureSerialized,
} from '@jbrowse/core/util'
import { firstValueFrom, Observer, toArray } from 'rxjs'

// locals
import {
isUcscProcessedTranscript,
ucscProcessedTranscript,
makeRepeatTrackDescription,
makeBlocks,
arrayify,
} from '../util'
import { featureData2 } from '../util'

export default class BigBedAdapter extends BaseFeatureDataAdapter {
private cachedP?: Promise<{
Expand Down Expand Up @@ -148,19 +142,14 @@ export default class BigBedAdapter extends BaseFeatureDataAdapter {
}
}

const parentAggregation = {} as Record<
string,
SimpleFeatureSerializedNoId[]
>
const parentAggregation = {} as Record<string, SimpleFeatureSerialized[]>

if (feats.some(f => f.uniqueId === undefined)) {
throw new Error('found uniqueId undefined')
}
for (const feat of feats) {
const data = parser.parseLine(
`${query.refName}\t${feat.start}\t${feat.end}\t${feat.rest}`,
{ uniqueId: feat.uniqueId! },
)
const line = `${query.refName}\t${feat.start}\t${feat.end}\t${feat.rest}`
const data = parser.parseLine(line, { uniqueId: feat.uniqueId! })

const aggr = data[aggregateField]
if (!parentAggregation[aggr]) {
Expand All @@ -183,89 +172,27 @@ export default class BigBedAdapter extends BaseFeatureDataAdapter {
strand,
...rest
} = data
const chromStarts = arrayify(chromStarts2)
const blockStarts = arrayify(blockStarts2)
const blockSizes = arrayify(blockSizes2)
const score = scoreColumn ? +data[scoreColumn] : +score2

const subfeatures = makeBlocks({
chromStarts,
blockStarts,
blockSizes,
blockCount,
const f = featureData2({
...rest,
scoreColumn,
line,
parser,
uniqueId,
refName: query.refName,
start: feat.start,
end: feat.end,
refName: query.refName,
})

if (
isUcscProcessedTranscript({
strand,
blockCount,
thickStart,
description,
})
) {
const f = ucscProcessedTranscript({
...rest,
strand,
uniqueId,
type,
start: feat.start,
end: feat.end,
refName: query.refName,
score,
description,
chromStarts: chromStarts!,
blockSizes: blockSizes!,
blockCount,
thickStart,
thickEnd,
subfeatures,
})
if (aggr) {
parentAggregation[aggr].push(f)
} else {
if (
doesIntersect2(
f.start,
f.end,
originalQuery.start,
originalQuery.end,
)
) {
observer.next(
new SimpleFeature({
id: `${this.id}-${uniqueId}`,
data: f,
}),
)
}
}
if (aggr) {
parentAggregation[aggr].push(f)
} else {
if (
doesIntersect2(
feat.start,
feat.end,
originalQuery.start,
originalQuery.end,
)
doesIntersect2(f.start, f.end, originalQuery.start, originalQuery.end)
) {
observer.next(
new SimpleFeature({
id: `${this.id}-${uniqueId}`,
data: {
...rest,
...makeRepeatTrackDescription(description),
start: feat.start,
end: feat.end,
strand,
uniqueId,
type,
score,
refName: query.refName,
subfeatures,
},
data: f,
}),
)
}
Expand Down
67 changes: 67 additions & 0 deletions plugins/bed/src/generateBedMethylFeature.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
export function isBedMethylFeature({
splitLine,
start,
end,
}: {
splitLine: string[]
start: number
end: number
}) {
return +(splitLine[6] || 0) === start && +(splitLine[7] || 0) === end
}
export function generateBedMethylFeature({
line,
uniqueId,
refName,
start,
end,
}: {
line: string
uniqueId: string
refName: string
start: number
end: number
}) {
// see
// https://github.com/nanoporetech/modkit?tab=readme-ov-file#description-of-bedmethyl-output
const [
,
,
,
code,
,
strand,
,
,
color,
n_valid_cov,
fraction_modified,
n_mod,
n_canonical,
n_other_mod,
n_delete,
n_fail,
n_diff,
n_nocall,
] = line.split('\t')
return {
uniqueId,
refName,
start,
end,
code,
score: fraction_modified,
strand,
color,
source: code,
n_valid_cov,
fraction_modified,
n_mod,
n_canonical,
n_other_mod,
n_delete,
n_fail,
n_diff,
n_nocall,
}
}
Loading

0 comments on commit a439fd6

Please sign in to comment.