@gmod/cram
Version:
read CRAM files with pure Javascript
509 lines (378 loc) • 17.1 kB
Markdown
# @gmod/cram
[](https://npmjs.org/package/@gmod/cram)
[](https://codecov.io/gh/GMOD/cram-js/branch/master)
[](https://github.com/GMOD/cram-js/actions?query=branch%3Amaster+workflow%3APush+)
Read CRAM files (indexed or unindexed) with pure JS, works in node or in the
browser.
- Reads CRAM 3.x and 2.x (3.1 added in v1.6.0)
- Does not read CRAM 1.x
- Can use .crai indexes out of the box, for efficient sequence fetching, but
also has an [index API](#craiindex) that would allow use with other index
types
- Has preliminary support for bzip2 and lzma codecs. lzma requires the latest
@gmod/cram version, and uses webassembly. If you find you are unable to
compile it, you can try downgrading
## Install
```bash
$ npm install --save @gmod/cram
# or
$ yarn add @gmod/cram
```
## Usage
```js
const { IndexedCramFile, CramFile, CraiIndex } = require('@gmod/cram')
// Use indexedfasta library for seqFetch, if using local file (see below)
const { IndexedFasta, BgzipIndexedFasta } = require('@gmod/indexedfasta')
// this uses local file paths for node.js for IndexedFasta, for usages using
// remote URLs see indexedfasta docs for filehandles and
// https://github.com/gmod/generic-filehandle2
const t = new IndexedFasta({
path: '/filesystem/yourfile.fa',
faiPath: '/filesystem/yourfile.fa.fai',
})
// example of fetching records from an indexed CRAM file.
// NOTE: only numeric IDs for the reference sequence are accepted.
// For indexedfasta the numeric ID is the order in which the sequence names
// appear in the header
// Wrap in an async and then run
run = async () => {
const idToName = []
const nameToId = {}
// example opening local files on node.js
// can also pass `cramUrl` (for the IndexedCramFile class), and `url` (for
// the CraiIndex) params to open remote URLs
//
// alternatively `cramFilehandle` (for the IndexedCramFile class) and
// `filehandle` (for the CraiIndex) can be used, see for examples
// https://github.com/gmod/generic-filehandle2
const indexedFile = new IndexedCramFile({
cramPath: '/filesystem/yourfile.cram',
//or
//cramUrl: 'url/to/file.cram'
//cramFilehandle: a generic-filehandle2 or similar filehandle
index: new CraiIndex({
path: '/filesystem/yourfile.cram.crai',
// or
// url: 'url/to/file.cram.crai'
// filehandle: a generic-filehandle2 or similar filehandle
}),
seqFetch: async (seqId, start, end) => {
// note:
// * seqFetch should return a promise for a string, in this instance retrieved from IndexedFasta
// * we use start-1 because cram-js uses 1-based but IndexedFasta uses 0-based coordinates
// * the seqId is a numeric identifier, so we convert it back to a name with idToName
// * you can return an empty string from this function for testing if you want, but you may not get proper interpretation of record.readFeatures
return t.getSequence(idToName[seqId], start - 1, end)
},
checkSequenceMD5: false,
})
const samHeader = await indexedFile.cram.getSamHeader()
// use the @SQ lines in the header to figure out the
// mapping between ref ID numbers and names
const sqLines = samHeader.filter(l => l.tag === 'SQ')
sqLines.forEach((sqLine, refId) => {
sqLine.data.forEach(item => {
if (item.tag === 'SN') {
// this is the ref name
const refName = item.value
nameToId[refName] = refId
idToName[refId] = refName
}
})
})
const records = await indexedFile.getRecordsForRange(
nameToId['chr1'],
10000,
20000,
)
records.forEach(record => {
console.log(`got a record named ${record.readName}`)
if (record.readFeatures != undefined) {
record.readFeatures.forEach(({ code, pos, refPos, ref, sub }) => {
// process the read features. this can be used similar to
// CIGAR/MD strings in SAM. see CRAM specs for more details.
if (code === 'X') {
console.log(
`${record.readName} shows a base substitution of ${ref}->${sub} at ${refPos}`,
)
}
})
}
})
}
run()
// can also pass `cramUrl` (for the IndexedCramFile class), and `url` (for the CraiIndex) params to open remote URLs
// alternatively `cramFilehandle` (for the IndexedCramFile class) and `filehandle` (for the CraiIndex) can be used, see for examples https://github.com/gmod/generic-filehandle2
```
You can use cram-js without NPM also with the cram-bundle.js. See the example
directory for usage with script tag
## API (auto-generated)
- [CramRecord](#cramrecord) - format of CRAM records returned by this API
- [ReadFeatures](#readfeatures) - format of read features on records
- [IndexedCramFile](#indexedcramfile) - indexed access into a CRAM file
- [CramFile](#cramfile) - .cram API
- [CraiIndex](#craiindex) - .crai index API
- [Error Classes](#error-classes) - special error classes thrown by this API
### CramRecord
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
##### Table of Contents
- [CramRecord](#cramrecord)
- [Parameters](#parameters)
- [isPaired](#ispaired)
- [isProperlyPaired](#isproperlypaired)
- [isSegmentUnmapped](#issegmentunmapped)
- [isMateUnmapped](#ismateunmapped)
- [isReverseComplemented](#isreversecomplemented)
- [isMateReverseComplemented](#ismatereversecomplemented)
- [isRead1](#isread1)
- [isRead2](#isread2)
- [isSecondary](#issecondary)
- [isFailedQc](#isfailedqc)
- [isDuplicate](#isduplicate)
- [isSupplementary](#issupplementary)
- [isDetached](#isdetached)
- [hasMateDownStream](#hasmatedownstream)
- [isPreservingQualityScores](#ispreservingqualityscores)
- [isUnknownBases](#isunknownbases)
- [getReadBases](#getreadbases)
- [getPairOrientation](#getpairorientation)
- [addReferenceSequence](#addreferencesequence)
- [Parameters](#parameters-1)
#### CramRecord
Class of each CRAM record returned by this API.
##### Parameters
- `$0` **any** 
- `$0.flags`  
- `$0.cramFlags`  
- `$0.readLength`  
- `$0.mappingQuality`  
- `$0.lengthOnRef`  
- `$0.qualityScores`  
- `$0.mateRecordNumber`  
- `$0.readBases`  
- `$0.readFeatures`  
- `$0.mateToUse`  
- `$0.readGroupId`  
- `$0.readName`  
- `$0.sequenceId`  
- `$0.uniqueId`  
- `$0.templateSize`  
- `$0.alignmentStart`  
- `$0.tags`  
##### isPaired
Returns
**[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)**
true if the read is paired, regardless of whether both segments are mapped
##### isProperlyPaired
Returns
**[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)**
true if the read is paired, and both segments are mapped
##### isSegmentUnmapped
Returns
**[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)**
true if the read itself is unmapped; conflictive with isProperlyPaired
##### isMateUnmapped
Returns
**[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)**
true if the read itself is unmapped; conflictive with isProperlyPaired
##### isReverseComplemented
Returns
**[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)**
true if the read is mapped to the reverse strand
##### isMateReverseComplemented
Returns
**[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)**
true if the mate is mapped to the reverse strand
##### isRead1
Returns
**[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)**
true if this is read number 1 in a pair
##### isRead2
Returns
**[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)**
true if this is read number 2 in a pair
##### isSecondary
Returns
**[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)**
true if this is a secondary alignment
##### isFailedQc
Returns
**[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)**
true if this read has failed QC checks
##### isDuplicate
Returns
**[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)**
true if the read is an optical or PCR duplicate
##### isSupplementary
Returns
**[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)**
true if this is a supplementary alignment
##### isDetached
Returns
**[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)**
true if the read is detached
##### hasMateDownStream
Returns
**[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)**
true if the read has a mate in this same CRAM segment
##### isPreservingQualityScores
Returns
**[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)**
true if the read contains qual scores
##### isUnknownBases
Returns
**[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)**
true if the read has no sequence bases
##### getReadBases
Get the original sequence of this read.
Returns
**[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**
sequence basepairs
##### getPairOrientation
Get the pair orientation of a paired read. Adapted from igv.js
Returns
**[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**
of paired orientatin
##### addReferenceSequence
Annotates this feature with the given reference sequence basepair information.
This will add a `sub` and a `ref` item to base substitution read features given
the actual substituted and reference base pairs, and will make the
`getReadSequence()` method work.
###### Parameters
- `refRegion`
**[object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)** 
- `refRegion.start`
**[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** 
- `refRegion.end`
**[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** 
- `refRegion.seq`
**[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** 
- `compressionScheme` **CramContainerCompressionScheme** 
Returns
**[undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined)**
nothing
### ReadFeatures
The feature objects appearing in the `readFeatures` member of CramRecord objects
that show insertions, deletions, substitutions, etc.
#### Static fields
- **code** (`character`): One of "bqBXIDiQNSPH". See page 15 of the CRAM v3 spec
for their meanings.
- **data** (`any`): the data associated with the feature. The format of this
varies depending on the feature code.
- **pos** (`number`): location relative to the read (1-based)
- **refPos** (`number`): location relative to the reference (1-based)
### IndexedCramFile
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
##### Table of Contents
- [constructor](#constructor)
- [Parameters](#parameters)
- [getRecordsForRange](#getrecordsforrange)
- [Parameters](#parameters-1)
- [hasDataForReferenceSequence](#hasdataforreferencesequence)
- [Parameters](#parameters-2)
#### constructor
##### Parameters
- `args`
**[object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)** 
- `args.cram` **CramFile** 
- `args.index` **Index-like** object that supports
getEntriesForRange(seqId,start,end) -> Promise\[Array\[index entries]]
- `args.cacheSize`
**[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
optional maximum number of CRAM records to cache. default 20,000
- `args.checkSequenceMD5`
**[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?**
default true. if false, disables verifying the MD5 checksum of the reference
sequence underlying a slice. In some applications, this check can cause an
inconvenient amount (many megabases) of sequences to be fetched.
#### getRecordsForRange
##### Parameters
- `seq`
**[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)**
numeric ID of the reference sequence
- `start`
**[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)**
start of the range of interest. 1-based closed coordinates.
- `end`
**[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)**
end of the range of interest. 1-based closed coordinates.
- `opts` **{viewAsPairs:
[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?,
pairAcrossChr:
[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?,
maxInsertSize:
[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?}**
(optional, default `{}`)
#### hasDataForReferenceSequence
##### Parameters
- `seqId`
**[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** 
Returns
**[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)**
true if the CRAM file contains data for the given reference sequence numerical
ID
### CramFile
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
##### Table of Contents
- [containerCount](#containercount)
#### containerCount
Returns
**[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<([number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)
|
[undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))>** 
### CraiIndex
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
##### Table of Contents
- [constructor](#constructor)
- [Parameters](#parameters)
- [hasDataForReferenceSequence](#hasdataforreferencesequence)
- [Parameters](#parameters-1)
- [getEntriesForRange](#getentriesforrange)
- [Parameters](#parameters-2)
#### constructor
##### Parameters
- `args`
**[object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)** 
- `args.path`
**[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** 
- `args.url`
**[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** 
- `args.filehandle` **FileHandle?** 
#### hasDataForReferenceSequence
##### Parameters
- `seqId`
**[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** 
Returns
**[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)**
true if the index contains entries for the given reference sequence ID, false
otherwise
#### getEntriesForRange
fetch index entries for the given range
##### Parameters
- `seqId`
**[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** 
- `queryStart`
**[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** 
- `queryEnd`
**[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** 
Returns
**[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)**
promise for an array of objects of the form
`{start, span, containerStart, sliceStart, sliceBytes }`
### CramUnimplementedError
**Extends Error**
Error caused by encountering a part of the CRAM spec that has not yet been
implemented
### CramMalformedError
**Extends CramError**
An error caused by malformed data.
### CramBufferOverrunError
**Extends CramMalformedError**
An error caused by attempting to read beyond the end of the defined data.
## Academic Use
This package was written with funding from the [NHGRI](http://genome.gov) as
part of the [JBrowse](http://jbrowse.org) project. If you use it in an academic
project that you publish, please cite the most recent JBrowse paper, which will
be linked from [jbrowse.org](http://jbrowse.org).
## License
MIT © [Robert Buels](https://github.com/rbuels)