jbrowse-plugin-graphgenomeviewer 8.3.5 → 8.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{GbzBaseSyntenyAdapter-RRLBONL5.js → GbzBaseSyntenyAdapter-BG6FNGHY.js} +2 -2
- package/dist/chunks/WalkTabixSyntenyAdapter-7COETKFB.js +2 -0
- package/dist/chunks/WalkTabixSyntenyAdapter-7COETKFB.js.map +7 -0
- package/dist/chunks/{chunk-HMIJBEUE.js → chunk-HLWIFWDZ.js} +2 -2
- package/dist/chunks/{chunk-HMIJBEUE.js.map → chunk-HLWIFWDZ.js.map} +2 -2
- package/dist/jbrowse-plugin-graphgenomeviewer.esm.js +2 -2
- package/dist/jbrowse-plugin-graphgenomeviewer.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/RgfaTabixAdapter/test_data/walks5_skipfollow.GRCh38.walks.bed.gz +0 -0
- package/src/RgfaTabixAdapter/test_data/walks5_skipfollow.GRCh38.walks.bed.gz.tbi +0 -0
- package/src/RgfaTabixAdapter/test_data/walks5_skipinv.GRCh38.walks.bed.gz +0 -0
- package/src/RgfaTabixAdapter/test_data/walks5_skipinv.GRCh38.walks.bed.gz.tbi +0 -0
- package/src/WalkTabixSyntenyAdapter/WalkTabixSyntenyAdapter.test.ts +111 -84
- package/src/WalkTabixSyntenyAdapter/WalkTabixSyntenyAdapter.ts +96 -93
- package/src/WalkTabixSyntenyAdapter/walkLanes.ts +71 -48
- package/src/synteny/clipFeatureToRegion.ts +3 -2
- package/src/synteny/matchFeature.ts +2 -3
- package/src/version.ts +1 -1
- package/dist/chunks/WalkTabixSyntenyAdapter-4I32E62Y.js +0 -2
- package/dist/chunks/WalkTabixSyntenyAdapter-4I32E62Y.js.map +0 -7
- package/src/RgfaTabixAdapter/test_data/walks5_skippair.GRCh38.walks.bed.gz +0 -0
- package/src/RgfaTabixAdapter/test_data/walks5_skippair.GRCh38.walks.bed.gz.tbi +0 -0
- /package/dist/chunks/{GbzBaseSyntenyAdapter-RRLBONL5.js.map → GbzBaseSyntenyAdapter-BG6FNGHY.js.map} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jbrowse-plugin-graphgenomeviewer",
|
|
3
|
-
"version": "8.3.
|
|
3
|
+
"version": "8.3.6",
|
|
4
4
|
"description": "JBrowse 2 graph genome (pangenome) view: rGFA reference-anchored layouts, the Bandage force-directed engine, and haplotype lanes read from a gbz-base database",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"repository": {
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { TabixIndexedFile } from '@gmod/tabix'
|
|
1
2
|
import { graphTablesGFA } from '@jbrowse/bandage-core/gfa/graphTables'
|
|
2
3
|
import { walkRowsOf } from '@jbrowse/bandage-core/layout/walkRowRuns'
|
|
3
4
|
import { walkRows } from '@jbrowse/bandage-core/layout/walkRows'
|
|
4
5
|
import { loadGraph } from '@jbrowse/bandage-core/pipeline'
|
|
5
6
|
import PluginManager from '@jbrowse/core/PluginManager'
|
|
6
7
|
import { readConfObject } from '@jbrowse/core/configuration'
|
|
8
|
+
import { WalkReader } from '@jbrowse/graph-walks'
|
|
7
9
|
import { firstValueFrom } from 'rxjs'
|
|
8
10
|
import { toArray } from 'rxjs/operators'
|
|
9
11
|
|
|
@@ -247,74 +249,136 @@ test('a window on a haplotype lane answers nothing', async () => {
|
|
|
247
249
|
expect(found).toEqual([])
|
|
248
250
|
})
|
|
249
251
|
|
|
250
|
-
// gfa-to-tabix --walks --chunk 1000 of
|
|
251
|
-
//
|
|
252
|
-
// W GRCh38 0
|
|
253
|
-
// W HG002 1
|
|
254
|
-
// W
|
|
255
|
-
// W
|
|
256
|
-
|
|
257
|
-
|
|
252
|
+
// gfa-to-tabix --walks --chunk 1000 of twenty 1 kb reference nodes; every
|
|
253
|
+
// haplotype but HG003 skips 2000-5000, so chunks 2000-4000 hold skip rows:
|
|
254
|
+
// W GRCh38 0 chr1 >1>2>3>...>20
|
|
255
|
+
// W HG002 1 ctg >1>2>6>...>20
|
|
256
|
+
// W HG004 1 ctg <20<...<6<2<1 the same walk backwards
|
|
257
|
+
// W HG003 1 ctg >1>2>3>...>20
|
|
258
|
+
// W HG007 1 ctg >1>2>6>...>20 a W line starting at 100,000
|
|
259
|
+
// W HG005 1 ctg >1>2>99>6>...>20 500 bp of node 99 in the gap
|
|
260
|
+
const skipWindow = { ...window, start: 3100, end: 3900 }
|
|
261
|
+
const skipFeats = (
|
|
262
|
+
opts: Record<string, unknown> = {},
|
|
263
|
+
name = 'walks5_skipfollow',
|
|
264
|
+
) =>
|
|
258
265
|
firstValueFrom(
|
|
259
|
-
makeAdapter({},
|
|
266
|
+
makeAdapter({}, name)
|
|
260
267
|
.getFeaturesInMultipleRegions([skipWindow], opts as never)
|
|
261
268
|
.pipe(toArray()),
|
|
262
269
|
)
|
|
263
|
-
|
|
264
|
-
|
|
270
|
+
const skipSummary = (f: { get: (k: string) => unknown }) => ({
|
|
271
|
+
...summary(f),
|
|
272
|
+
strand: f.get('strand'),
|
|
273
|
+
spansWindow: f.get('spansWindow'),
|
|
274
|
+
})
|
|
275
|
+
const deletion = (lane: string, mate: string, strand: number) => ({
|
|
276
|
+
lane,
|
|
277
|
+
refName: 'chr1',
|
|
278
|
+
start: 2000,
|
|
279
|
+
end: 5000,
|
|
280
|
+
mate,
|
|
281
|
+
cigar: undefined,
|
|
282
|
+
strand,
|
|
283
|
+
spansWindow: true,
|
|
284
|
+
})
|
|
285
|
+
const crossings = [
|
|
286
|
+
deletion('HG002#1', 'ctg:2000-2000', 1),
|
|
287
|
+
deletion('HG004#1', 'ctg:15000-15000', -1),
|
|
288
|
+
deletion('HG005#1', 'ctg:2000-2000', 1),
|
|
289
|
+
deletion('HG007#1', 'ctg:102000-102000', 1),
|
|
290
|
+
]
|
|
291
|
+
|
|
292
|
+
test('a haplotype whose deletion spans the window is one spansWindow record of the whole deletion, its mate where it crosses', async () => {
|
|
265
293
|
const found = await skipFeats()
|
|
266
|
-
expect(found.map(
|
|
294
|
+
expect(found.map(skipSummary)).toEqual([
|
|
267
295
|
{
|
|
268
296
|
lane: 'HG003#1',
|
|
269
297
|
refName: 'chr1',
|
|
270
298
|
start: 2000,
|
|
271
|
-
end:
|
|
272
|
-
mate: 'ctg:2000-
|
|
273
|
-
cigar: '
|
|
274
|
-
strand: 1,
|
|
275
|
-
},
|
|
276
|
-
{
|
|
277
|
-
lane: 'HG002#1',
|
|
278
|
-
refName: 'chr1',
|
|
279
|
-
start: 2000,
|
|
280
|
-
end: 8000,
|
|
281
|
-
mate: 'ctg:2000-2000',
|
|
282
|
-
cigar: undefined,
|
|
299
|
+
end: 5000,
|
|
300
|
+
mate: 'ctg:2000-5000',
|
|
301
|
+
cigar: '3000=',
|
|
283
302
|
strand: 1,
|
|
303
|
+
spansWindow: undefined,
|
|
284
304
|
},
|
|
285
|
-
|
|
286
|
-
lane: 'HG004#1',
|
|
287
|
-
refName: 'chr1',
|
|
288
|
-
start: 2000,
|
|
289
|
-
end: 8000,
|
|
290
|
-
mate: 'ctg:2000-2000',
|
|
291
|
-
cigar: undefined,
|
|
292
|
-
strand: -1,
|
|
293
|
-
},
|
|
305
|
+
...crossings,
|
|
294
306
|
])
|
|
307
|
+
expect(found[1]!.get('identity')).toBeUndefined()
|
|
295
308
|
})
|
|
296
309
|
|
|
297
|
-
test("the display's clip
|
|
310
|
+
test("the display's clip passes a deletion through whole, so its extent is the deletion's length", async () => {
|
|
298
311
|
const found = await skipFeats({
|
|
299
312
|
haplotypes: ['HG002'],
|
|
300
313
|
clipToRegion: true,
|
|
301
314
|
splitAtGapBp: 10_000,
|
|
302
315
|
})
|
|
303
|
-
expect(found.map(
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
316
|
+
expect(found.map(skipSummary)).toEqual([crossings[0]])
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
test('a walk passing the node it leaves at inverted still crosses after it', async () => {
|
|
320
|
+
const found = await skipFeats({ haplotypes: ['HG008'] }, 'walks5_skipinv')
|
|
321
|
+
expect(found.map(skipSummary)).toEqual([
|
|
322
|
+
deletion('HG008#1', 'ctg:2000-2000', 1),
|
|
323
|
+
])
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
test('a skipping haplotype outside the lanes asked for opens no lane', async () => {
|
|
327
|
+
const found = await skipFeats({ haplotypes: ['HG003'] })
|
|
328
|
+
expect(found.map(f => mateOf(f).assemblyName)).toEqual(['HG003#1'])
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
test('the crossing cut reads only the chunks before the node a haplotype leaves', async () => {
|
|
332
|
+
const getLines = vi.spyOn(TabixIndexedFile.prototype, 'getLines')
|
|
333
|
+
await skipFeats({ haplotypes: ['HG002'] })
|
|
334
|
+
const reads = getLines.mock.calls.map(call => `${call[1]}-${call[2]}`)
|
|
335
|
+
getLines.mockRestore()
|
|
336
|
+
expect(reads.at(-1)).toBe('0-2000')
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
test('a haplotype whose leaving node the cut lacks is crossed from the node it rejoins', async () => {
|
|
340
|
+
const cut = WalkReader.prototype.cut
|
|
341
|
+
const spy = vi
|
|
342
|
+
.spyOn(WalkReader.prototype, 'cut')
|
|
343
|
+
.mockImplementation(async function (
|
|
344
|
+
this: WalkReader,
|
|
345
|
+
refName,
|
|
346
|
+
region,
|
|
347
|
+
opts,
|
|
348
|
+
) {
|
|
349
|
+
const found = await cut.call(this, refName, region, opts)
|
|
350
|
+
return opts?.contained && region.end === 2000
|
|
351
|
+
? { ...found, fragments: [] }
|
|
352
|
+
: found
|
|
353
|
+
})
|
|
354
|
+
const found = await skipFeats()
|
|
355
|
+
spy.mockRestore()
|
|
356
|
+
// HG005 rejoins past the 500 bp it carries in the gap
|
|
357
|
+
expect(found.slice(1).map(skipSummary)).toEqual([
|
|
358
|
+
crossings[0],
|
|
359
|
+
crossings[1],
|
|
360
|
+
deletion('HG005#1', 'ctg:2500-2500', 1),
|
|
361
|
+
crossings[3],
|
|
312
362
|
])
|
|
313
363
|
})
|
|
314
364
|
|
|
365
|
+
test('a failed crossing cut drops only its own lanes', async () => {
|
|
366
|
+
const cut = WalkReader.prototype.cut
|
|
367
|
+
const spy = vi
|
|
368
|
+
.spyOn(WalkReader.prototype, 'cut')
|
|
369
|
+
.mockImplementation(function (this: WalkReader, refName, region, opts) {
|
|
370
|
+
return opts?.contained
|
|
371
|
+
? Promise.reject(new Error('no such chunk'))
|
|
372
|
+
: cut.call(this, refName, region, opts)
|
|
373
|
+
})
|
|
374
|
+
const found = await skipFeats()
|
|
375
|
+
spy.mockRestore()
|
|
376
|
+
expect(found.map(f => mateOf(f).assemblyName)).toEqual(['HG003#1'])
|
|
377
|
+
})
|
|
378
|
+
|
|
315
379
|
// RgfaTabixAdapter/walkFiles.test.ts's walks5_skip: HG002#1 >1>2>9>10 over
|
|
316
380
|
// ten 10 bp reference nodes in 10 bp chunks
|
|
317
|
-
test('a named haplotype skipping the window
|
|
381
|
+
test('a named haplotype skipping the window is a deletion of the skipped stretch', async () => {
|
|
318
382
|
const found = await firstValueFrom(
|
|
319
383
|
makeAdapter({}, 'walks5_skip')
|
|
320
384
|
.getFeatures(
|
|
@@ -323,7 +387,7 @@ test('a named haplotype skipping the window opens its lane at the crossing', asy
|
|
|
323
387
|
)
|
|
324
388
|
.pipe(toArray()),
|
|
325
389
|
)
|
|
326
|
-
expect(found.map(
|
|
390
|
+
expect(found.map(skipSummary)).toEqual([
|
|
327
391
|
{
|
|
328
392
|
lane: 'HG002#1',
|
|
329
393
|
refName: 'chr1',
|
|
@@ -331,45 +395,8 @@ test('a named haplotype skipping the window opens its lane at the crossing', asy
|
|
|
331
395
|
end: 80,
|
|
332
396
|
mate: 'ctg:20-20',
|
|
333
397
|
cigar: undefined,
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
})
|
|
337
|
-
|
|
338
|
-
test('a skipping haplotype outside the lanes asked for opens no lane', async () => {
|
|
339
|
-
const found = await skipFeats({ haplotypes: ['HG003'] })
|
|
340
|
-
expect(found.map(f => mateOf(f).assemblyName)).toEqual(['HG003#1'])
|
|
341
|
-
})
|
|
342
|
-
|
|
343
|
-
test('a lane pair with one lane skipping the window joins the other lane to the crossing, either way round', async () => {
|
|
344
|
-
const found = await skipFeats({
|
|
345
|
-
lanePairs: [
|
|
346
|
-
{ queryAssemblyName: 'HG003#1', targetAssemblyName: 'HG002#1' },
|
|
347
|
-
{ queryAssemblyName: 'HG002#1', targetAssemblyName: 'HG003#1' },
|
|
348
|
-
{ queryAssemblyName: 'HG002#1', targetAssemblyName: 'HG004#1' },
|
|
349
|
-
],
|
|
350
|
-
clipToRegion: true,
|
|
351
|
-
splitAtGapBp: 10_000,
|
|
352
|
-
})
|
|
353
|
-
expect(
|
|
354
|
-
found.map(f => ({ assemblyName: f.get('assemblyName'), ...summary(f) })),
|
|
355
|
-
).toEqual([
|
|
356
|
-
{
|
|
357
|
-
assemblyName: 'HG003#1',
|
|
358
|
-
lane: 'HG002#1',
|
|
359
|
-
refName: 'ctg',
|
|
360
|
-
start: 2000,
|
|
361
|
-
end: 7000,
|
|
362
|
-
mate: 'ctg:2000-2000',
|
|
363
|
-
cigar: undefined,
|
|
364
|
-
},
|
|
365
|
-
{
|
|
366
|
-
assemblyName: 'HG002#1',
|
|
367
|
-
lane: 'HG003#1',
|
|
368
|
-
refName: 'ctg',
|
|
369
|
-
start: 2000,
|
|
370
|
-
end: 2000,
|
|
371
|
-
mate: 'ctg:2000-7000',
|
|
372
|
-
cigar: undefined,
|
|
398
|
+
strand: 1,
|
|
399
|
+
spansWindow: true,
|
|
373
400
|
},
|
|
374
401
|
])
|
|
375
402
|
})
|
|
@@ -13,11 +13,9 @@ import { walksKept } from '@jbrowse/graph-walks'
|
|
|
13
13
|
import {
|
|
14
14
|
anchorAligner,
|
|
15
15
|
chainFeature,
|
|
16
|
-
crossingFeature,
|
|
17
|
-
crossingSide,
|
|
18
16
|
lanePairAligner,
|
|
19
17
|
skipCrossing,
|
|
20
|
-
|
|
18
|
+
skipFeature,
|
|
21
19
|
} from './walkLanes.ts'
|
|
22
20
|
import { WalkIndex } from '../RgfaTabixAdapter/walkReader.ts'
|
|
23
21
|
import { PanSNRefNames, openTabixSlot } from '../panSNTabix.ts'
|
|
@@ -30,9 +28,9 @@ import {
|
|
|
30
28
|
import { laneZoomNotice } from '../synteny/laneZoomNotice.ts'
|
|
31
29
|
|
|
32
30
|
import type { WalkTabixSyntenyAdapterConfig } from './configSchema.ts'
|
|
31
|
+
import type { SkipCrossing, SkipSide } from './walkLanes.ts'
|
|
33
32
|
import type { SubgraphAdapterOptions } from '../GetSubgraph.ts'
|
|
34
33
|
import type { LaneFeatureOptions, LanePair } from '../synteny/lanePairs.ts'
|
|
35
|
-
import type { MatchSide } from '../synteny/matchFeature.ts'
|
|
36
34
|
import type { BaseOptions } from '@jbrowse/core/data_adapters/BaseAdapter'
|
|
37
35
|
import type { Feature } from '@jbrowse/core/util'
|
|
38
36
|
import type { Region } from '@jbrowse/core/util/types'
|
|
@@ -144,51 +142,82 @@ export default class WalkTabixSyntenyAdapter extends ComparativeAdapterBase<Walk
|
|
|
144
142
|
}
|
|
145
143
|
|
|
146
144
|
/**
|
|
147
|
-
* Where each haplotype
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
145
|
+
* Where each skipping haplotype crosses its deletion, read from contained
|
|
146
|
+
* cuts over the reference node it leaves, one cut per chunk those nodes
|
|
147
|
+
* fall in, and for any that cut misses, over the node it rejoins. A cut
|
|
148
|
+
* that fails drops only its own haplotypes' crossings.
|
|
151
149
|
*/
|
|
152
150
|
private async skipCrossings(
|
|
153
151
|
refName: string,
|
|
154
|
-
|
|
152
|
+
skips: WalkSkip[],
|
|
153
|
+
opts: LaneFeatureOptions,
|
|
154
|
+
) {
|
|
155
|
+
const found: SkipCrossing[] = []
|
|
156
|
+
let missed = skips
|
|
157
|
+
for (const side of ['leave', 'rejoin'] as const) {
|
|
158
|
+
const crossings = await this.sideCrossings(refName, missed, side, opts)
|
|
159
|
+
for (const crossing of crossings) {
|
|
160
|
+
found.push(crossing)
|
|
161
|
+
}
|
|
162
|
+
const placed = new Set(crossings.map(crossing => crossing.skip))
|
|
163
|
+
missed = missed.filter(skip => !placed.has(skip))
|
|
164
|
+
}
|
|
165
|
+
return found
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private async sideCrossings(
|
|
169
|
+
refName: string,
|
|
170
|
+
skips: WalkSkip[],
|
|
171
|
+
side: SkipSide,
|
|
155
172
|
opts: LaneFeatureOptions,
|
|
156
173
|
) {
|
|
157
174
|
const { signal } = opts
|
|
158
|
-
const walked = new Set(fragments.map(haplotypeOf))
|
|
159
|
-
const crossing = skips.filter(skip => !walked.has(haplotypeOf(skip)))
|
|
160
175
|
const { chunk } = await this.walks.reader.header({ signal })
|
|
176
|
+
const boundary = (skip: WalkSkip) =>
|
|
177
|
+
side === 'leave' ? skip.refStart - 1 : skip.refEnd
|
|
161
178
|
const byChunk = new Map<
|
|
162
179
|
number,
|
|
163
180
|
{ group: WalkSkip[]; start: number; end: number }
|
|
164
181
|
>()
|
|
165
|
-
for (const skip of
|
|
166
|
-
const
|
|
182
|
+
for (const skip of skips) {
|
|
183
|
+
const at = boundary(skip)
|
|
184
|
+
const cs = Math.floor(at / chunk)
|
|
167
185
|
const held = byChunk.get(cs)
|
|
168
186
|
if (held) {
|
|
169
187
|
held.group.push(skip)
|
|
170
|
-
held.start = Math.min(held.start,
|
|
171
|
-
held.end = Math.max(held.end,
|
|
188
|
+
held.start = Math.min(held.start, at)
|
|
189
|
+
held.end = Math.max(held.end, at + 1)
|
|
172
190
|
} else {
|
|
173
|
-
byChunk.set(cs, {
|
|
174
|
-
group: [skip],
|
|
175
|
-
start: skip.refStart - 1,
|
|
176
|
-
end: skip.refStart,
|
|
177
|
-
})
|
|
191
|
+
byChunk.set(cs, { group: [skip], start: at, end: at + 1 })
|
|
178
192
|
}
|
|
179
193
|
}
|
|
180
194
|
const found = await Promise.all(
|
|
181
195
|
[...byChunk.values()].map(async ({ group, start, end }) => {
|
|
182
196
|
const names = new Set(group.map(skip => skip.name))
|
|
183
197
|
const keep = (name: string) => names.has(name)
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
198
|
+
try {
|
|
199
|
+
const { graph, fragments } = await this.walks.reader.cut(
|
|
200
|
+
refName,
|
|
201
|
+
{ start, end },
|
|
202
|
+
{
|
|
203
|
+
...this.walks.budgets(),
|
|
204
|
+
signal,
|
|
205
|
+
keep,
|
|
206
|
+
expand: keep,
|
|
207
|
+
context: 0,
|
|
208
|
+
contained: true,
|
|
209
|
+
links: false,
|
|
210
|
+
},
|
|
211
|
+
)
|
|
212
|
+
return group.map(skip =>
|
|
213
|
+
skipCrossing(skip, refName, fragments, graph.nodes, side),
|
|
214
|
+
)
|
|
215
|
+
} catch (error) {
|
|
216
|
+
if (signal?.aborted) {
|
|
217
|
+
throw error
|
|
218
|
+
}
|
|
219
|
+
return []
|
|
220
|
+
}
|
|
192
221
|
}),
|
|
193
222
|
)
|
|
194
223
|
return found.flat().filter(crossing => crossing !== undefined)
|
|
@@ -197,8 +226,8 @@ export default class WalkTabixSyntenyAdapter extends ComparativeAdapterBase<Walk
|
|
|
197
226
|
/**
|
|
198
227
|
* One record per stretch a haplotype's walk shares with the reference's,
|
|
199
228
|
* on the reference with the haplotype as its mate. A haplotype whose
|
|
200
|
-
* deletion spans the window
|
|
201
|
-
*
|
|
229
|
+
* deletion spans the window, with no walk there on any contig, is one
|
|
230
|
+
* `spansWindow` record of the skipped stretch.
|
|
202
231
|
*/
|
|
203
232
|
private async anchorFeatures(
|
|
204
233
|
refName: string,
|
|
@@ -238,10 +267,18 @@ export default class WalkTabixSyntenyAdapter extends ComparativeAdapterBase<Walk
|
|
|
238
267
|
prefixes === undefined
|
|
239
268
|
? 0
|
|
240
269
|
: prefixes.findIndex(prefix => panSNMatchesPrefix(name, prefix))
|
|
270
|
+
// NA19036#1 skips the IGL locus on one contig and walks it on another
|
|
271
|
+
const walking = new Set(fragments.map(haplotypeOf))
|
|
241
272
|
const crossings = await updateStatus(status, opts.statusCallback, () =>
|
|
242
273
|
this.skipCrossings(
|
|
243
274
|
refName,
|
|
244
|
-
|
|
275
|
+
skips.filter(
|
|
276
|
+
skip =>
|
|
277
|
+
rank(skip) >= 0 &&
|
|
278
|
+
!walking.has(haplotypeOf(skip)) &&
|
|
279
|
+
skip.refStart <= start &&
|
|
280
|
+
skip.refEnd >= end,
|
|
281
|
+
),
|
|
245
282
|
opts,
|
|
246
283
|
),
|
|
247
284
|
)
|
|
@@ -267,25 +304,17 @@ export default class WalkTabixSyntenyAdapter extends ComparativeAdapterBase<Walk
|
|
|
267
304
|
),
|
|
268
305
|
),
|
|
269
306
|
}))
|
|
270
|
-
const skipped = crossings.map(crossing => {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
end: skip.refEnd,
|
|
282
|
-
},
|
|
283
|
-
query: crossingSide(crossing, laneOf(skip)),
|
|
284
|
-
strand: crossing.strand,
|
|
285
|
-
}),
|
|
286
|
-
],
|
|
287
|
-
}
|
|
288
|
-
})
|
|
307
|
+
const skipped = crossings.map(crossing => ({
|
|
308
|
+
rank: rank(crossing.skip),
|
|
309
|
+
features: [
|
|
310
|
+
skipFeature({
|
|
311
|
+
crossing,
|
|
312
|
+
refName: region.refName,
|
|
313
|
+
assemblyName: region.assemblyName,
|
|
314
|
+
mateAssemblyName: laneOf(crossing.skip),
|
|
315
|
+
}),
|
|
316
|
+
],
|
|
317
|
+
}))
|
|
289
318
|
return [...walked, ...skipped]
|
|
290
319
|
.sort((a, b) => a.rank - b.rank)
|
|
291
320
|
.flatMap(lane => lane.features)
|
|
@@ -295,8 +324,7 @@ export default class WalkTabixSyntenyAdapter extends ComparativeAdapterBase<Walk
|
|
|
295
324
|
* Each pair's two lanes read out of the anchor window, all pairs in one
|
|
296
325
|
* read, and each walk of a pair's query lane aligned to each walk of its
|
|
297
326
|
* target lane. A pair's records sit on its query lane's contigs, the lane
|
|
298
|
-
* the display draws on top.
|
|
299
|
-
* point where it crosses its deletion.
|
|
327
|
+
* the display draws on top.
|
|
300
328
|
*/
|
|
301
329
|
private async pairFeatures(
|
|
302
330
|
refName: string,
|
|
@@ -309,57 +337,32 @@ export default class WalkTabixSyntenyAdapter extends ComparativeAdapterBase<Walk
|
|
|
309
337
|
featurePrefix: resolvePanSNPrefix(this, pair.queryAssemblyName),
|
|
310
338
|
matePrefix: resolvePanSNPrefix(this, pair.targetAssemblyName),
|
|
311
339
|
}))
|
|
312
|
-
const
|
|
313
|
-
const { graph, fragments, skips } = await this.laneWalks(
|
|
340
|
+
const { graph, fragments } = await this.laneWalks(
|
|
314
341
|
refName,
|
|
315
342
|
region,
|
|
316
343
|
sides.flatMap(side => [side.featurePrefix, side.matePrefix]),
|
|
317
344
|
opts,
|
|
318
|
-
|
|
319
|
-
)
|
|
320
|
-
const crossings = await updateStatus(status, opts.statusCallback, () =>
|
|
321
|
-
this.skipCrossings(refName, { skips, fragments }, opts),
|
|
345
|
+
lanePairsStatus(pairs),
|
|
322
346
|
)
|
|
323
347
|
const align = lanePairAligner(graph.nodes)
|
|
324
348
|
const walksOf = (prefix: string) =>
|
|
325
349
|
fragments.filter(f => panSNMatchesPrefix(f.name, prefix))
|
|
326
|
-
|
|
327
|
-
walksOf(
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
return [
|
|
339
|
-
...walksOf(featurePrefix).flatMap(target =>
|
|
340
|
-
walksOf(matePrefix).flatMap(query =>
|
|
341
|
-
align(query, target).map(chain =>
|
|
342
|
-
chainFeature({
|
|
343
|
-
chain,
|
|
344
|
-
target,
|
|
345
|
-
query,
|
|
346
|
-
assemblyName: queryAssemblyName,
|
|
347
|
-
refName: panSNContig(target.name),
|
|
348
|
-
mateAssemblyName: targetAssemblyName,
|
|
349
|
-
}),
|
|
350
|
-
),
|
|
350
|
+
return sides.flatMap(({ pair, featurePrefix, matePrefix }) =>
|
|
351
|
+
walksOf(featurePrefix).flatMap(target =>
|
|
352
|
+
walksOf(matePrefix).flatMap(query =>
|
|
353
|
+
align(query, target).map(chain =>
|
|
354
|
+
chainFeature({
|
|
355
|
+
chain,
|
|
356
|
+
target,
|
|
357
|
+
query,
|
|
358
|
+
assemblyName: pair.queryAssemblyName,
|
|
359
|
+
refName: panSNContig(target.name),
|
|
360
|
+
mateAssemblyName: pair.targetAssemblyName,
|
|
361
|
+
}),
|
|
351
362
|
),
|
|
352
363
|
),
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
crossingSides(matePrefix, targetAssemblyName),
|
|
356
|
-
),
|
|
357
|
-
...crossed(
|
|
358
|
-
crossingSides(featurePrefix, queryAssemblyName),
|
|
359
|
-
walkSides(matePrefix, targetAssemblyName),
|
|
360
|
-
),
|
|
361
|
-
]
|
|
362
|
-
})
|
|
364
|
+
),
|
|
365
|
+
)
|
|
363
366
|
}
|
|
364
367
|
|
|
365
368
|
// The files are filed under the reference alone, so a window on a
|