htslib 0.0.10 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +16 -16
- data/lib/hts/bam/aux.rb +39 -0
- data/lib/hts/bam/cigar.rb +0 -3
- data/lib/hts/bam/flag.rb +21 -57
- data/lib/hts/bam/header.rb +1 -4
- data/lib/hts/bam/record.rb +91 -47
- data/lib/hts/bam.rb +14 -15
- data/lib/hts/bcf/format.rb +82 -11
- data/lib/hts/bcf/info.rb +56 -31
- data/lib/hts/bcf/record.rb +43 -16
- data/lib/hts/bcf.rb +40 -15
- data/lib/hts/faidx.rb +0 -3
- data/lib/hts/ffi_ext/pointer.rb +18 -0
- data/lib/hts/hts.rb +22 -4
- data/lib/hts/libhts/constants.rb +3 -3
- data/lib/hts/libhts/cram.rb +287 -292
- data/lib/hts/libhts/vcf.rb +14 -0
- data/lib/hts/libhts.rb +4 -0
- data/lib/hts/{tabix.rb → tbx.rb} +4 -11
- data/lib/hts/version.rb +1 -1
- data/lib/htslib.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c90062954caa8dc2155f77ff3d03534d933ba81148f9bdb7c8dc173f73efca64
|
4
|
+
data.tar.gz: fb24ced637a8b9b897ecd9c5afed1c6f75902995e48c25daa849baa06611517b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fed4e6689d48493416ebd409401df284fedaddb567d3558196b3ba3c253376dd1c4bea8db536fdb6ff8bbebe85512850d7d5c17605544ce26a09e814e7172eb5
|
7
|
+
data.tar.gz: b9d596e6445671254568b4bd3cc5b694a1011914ae67b1afc11aaa66a195f49b7371d001f9e10cc204a4c20e501cd8eb63092d63b9b392c701571a12843b5c91
|
data/README.md
CHANGED
@@ -43,21 +43,21 @@ Read SAM / BAM / CRAM - Sequence Alignment Map file
|
|
43
43
|
```ruby
|
44
44
|
require 'htslib'
|
45
45
|
|
46
|
-
bam = HTS::Bam.open("
|
46
|
+
bam = HTS::Bam.open("test/fixtures/moo.bam")
|
47
47
|
|
48
48
|
bam.each do |r|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
49
|
+
pp name: r.qname,
|
50
|
+
flag: r.flag,
|
51
|
+
chrm: r.chrom,
|
52
|
+
strt: r.pos + 1,
|
53
|
+
mapq: r.mapq,
|
54
|
+
cigr: r.cigar.to_s,
|
55
|
+
mchr: r.mate_chrom,
|
56
|
+
mpos: r.mpos + 1,
|
57
|
+
isiz: r.isize,
|
58
|
+
seqs: r.seq,
|
59
|
+
qual: r.qual.map { |i| (i + 33).chr }.join,
|
60
|
+
MC: r.aux("MC")
|
61
61
|
end
|
62
62
|
|
63
63
|
bam.close
|
@@ -75,9 +75,9 @@ bcf.each do |r|
|
|
75
75
|
qual: r.qual.round(2),
|
76
76
|
ref: r.ref,
|
77
77
|
alt: r.alt,
|
78
|
-
filter: r.filter
|
79
|
-
|
80
|
-
|
78
|
+
filter: r.filter,
|
79
|
+
info: r.info.to_h,
|
80
|
+
format: r.format.to_h
|
81
81
|
end
|
82
82
|
|
83
83
|
bcf.close
|
data/lib/hts/bam/aux.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HTS
|
4
|
+
class Bam < Hts
|
5
|
+
class Aux
|
6
|
+
def initialize(record)
|
7
|
+
@record = record
|
8
|
+
end
|
9
|
+
|
10
|
+
def get(key, type = nil)
|
11
|
+
aux = LibHTS.bam_aux_get(@record.struct, key)
|
12
|
+
return nil if aux.null?
|
13
|
+
|
14
|
+
type ||= aux.read_string(1)
|
15
|
+
|
16
|
+
# A (character), B (general array),
|
17
|
+
# f (real number), H (hexadecimal array),
|
18
|
+
# i (integer), or Z (string).
|
19
|
+
|
20
|
+
case type
|
21
|
+
when "i", "I", "c", "C", "s", "S"
|
22
|
+
LibHTS.bam_aux2i(aux)
|
23
|
+
when "f", "d"
|
24
|
+
LibHTS.bam_aux2f(aux)
|
25
|
+
when "Z", "H"
|
26
|
+
LibHTS.bam_aux2Z(aux)
|
27
|
+
when "A" # char
|
28
|
+
LibHTS.bam_aux2A(aux).chr
|
29
|
+
else
|
30
|
+
raise NotImplementedError, "type: #{t}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def [](key)
|
35
|
+
get(key)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/hts/bam/cigar.rb
CHANGED
data/lib/hts/bam/flag.rb
CHANGED
@@ -27,70 +27,34 @@ module HTS
|
|
27
27
|
# BAM_FDUP = 1024
|
28
28
|
# BAM_FSUPPLEMENTARY = 2048
|
29
29
|
|
30
|
-
# TODO: Enabling bitwise operations
|
31
|
-
# hts-nim
|
32
|
-
# proc `and`*(f: Flag, o: uint16): uint16 {. borrow, inline .}
|
33
|
-
# proc `and`*(f: Flag, o: Flag): uint16 {. borrow, inline .}
|
34
|
-
# proc `or`*(f: Flag, o: uint16): uint16 {. borrow .}
|
35
|
-
# proc `or`*(o: uint16, f: Flag): uint16 {. borrow .}
|
36
|
-
# proc `==`*(f: Flag, o: Flag): bool {. borrow, inline .}
|
37
|
-
# proc `==`*(f: Flag, o: uint16): bool {. borrow, inline .}
|
38
|
-
# proc `==`*(o: uint16, f: Flag): bool {. borrow, inline .}
|
30
|
+
# TODO: Enabling bitwise operations?
|
39
31
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
def mate_unmapped?
|
53
|
-
has_flag? LibHTS::BAM_FMUNMAP
|
54
|
-
end
|
55
|
-
|
56
|
-
def reverse?
|
57
|
-
has_flag? LibHTS::BAM_FREVERSE
|
58
|
-
end
|
59
|
-
|
60
|
-
def mate_reverse?
|
61
|
-
has_flag? LibHTS::BAM_FMREVERSE
|
62
|
-
end
|
63
|
-
|
64
|
-
def read1?
|
65
|
-
has_flag? LibHTS::BAM_FREAD1
|
66
|
-
end
|
67
|
-
|
68
|
-
def read2?
|
69
|
-
has_flag? LibHTS::BAM_FREAD2
|
70
|
-
end
|
71
|
-
|
72
|
-
def secondary?
|
73
|
-
has_flag? LibHTS::BAM_FSECONDARY
|
74
|
-
end
|
75
|
-
|
76
|
-
def qcfail?
|
77
|
-
has_flag? LibHTS::BAM_FQCFAIL
|
78
|
-
end
|
79
|
-
|
80
|
-
def dup?
|
81
|
-
has_flag? LibHTS::BAM_FDUP
|
82
|
-
end
|
32
|
+
TABLE = { paired?: LibHTS::BAM_FPAIRED,
|
33
|
+
proper_pair?: LibHTS::BAM_FPROPER_PAIR,
|
34
|
+
unmapped?: LibHTS::BAM_FUNMAP,
|
35
|
+
mate_unmapped?: LibHTS::BAM_FMUNMAP,
|
36
|
+
reverse?: LibHTS::BAM_FREVERSE,
|
37
|
+
mate_reverse?: LibHTS::BAM_FMREVERSE,
|
38
|
+
read1?: LibHTS::BAM_FREAD1,
|
39
|
+
read2?: LibHTS::BAM_FREAD2,
|
40
|
+
secondary?: LibHTS::BAM_FSECONDARY,
|
41
|
+
qcfail?: LibHTS::BAM_FQCFAIL,
|
42
|
+
duplicate?: LibHTS::BAM_FDUP,
|
43
|
+
supplementary?: LibHTS::BAM_FSUPPLEMENTARY }.freeze
|
83
44
|
|
84
|
-
|
85
|
-
|
45
|
+
TABLE.each do |name, v|
|
46
|
+
define_method(name) do
|
47
|
+
has_flag?(v)
|
48
|
+
end
|
86
49
|
end
|
87
50
|
|
88
|
-
def has_flag?(
|
89
|
-
(@value &
|
51
|
+
def has_flag?(f)
|
52
|
+
(@value & f) != 0
|
90
53
|
end
|
91
54
|
|
92
55
|
def to_s
|
93
|
-
|
56
|
+
LibHTS.bam_flag2str(@value)
|
57
|
+
# "0x#{format('%x', @value)}\t#{@value}\t#{LibHTS.bam_flag2str(@value)}"
|
94
58
|
end
|
95
59
|
end
|
96
60
|
end
|
data/lib/hts/bam/header.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Based on hts-python
|
4
|
-
# https://github.com/quinlan-lab/hts-python
|
5
|
-
|
6
3
|
module HTS
|
7
4
|
class Bam < Hts
|
8
5
|
class Header
|
@@ -28,7 +25,7 @@ module HTS
|
|
28
25
|
end
|
29
26
|
end
|
30
27
|
|
31
|
-
def
|
28
|
+
def target_len
|
32
29
|
Array.new(target_count) do |i|
|
33
30
|
LibHTS.sam_hdr_tid2len(@sam_hdr, i)
|
34
31
|
end
|
data/lib/hts/bam/record.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
require_relative "flag"
|
4
|
+
require_relative "cigar"
|
5
|
+
require_relative "aux"
|
5
6
|
|
6
7
|
module HTS
|
7
8
|
class Bam < Hts
|
@@ -38,26 +39,49 @@ module HTS
|
|
38
39
|
@bam1[:core][:tid]
|
39
40
|
end
|
40
41
|
|
42
|
+
def tid=(tid)
|
43
|
+
@bam1[:core][:tid] = tid
|
44
|
+
end
|
45
|
+
|
41
46
|
# returns the tid of the mate or -1 if not mapped.
|
42
|
-
def
|
47
|
+
def mtid
|
43
48
|
@bam1[:core][:mtid]
|
44
49
|
end
|
45
50
|
|
51
|
+
def mtid=(mtid)
|
52
|
+
@bam1[:core][:mtid] = mtid
|
53
|
+
end
|
54
|
+
|
46
55
|
# returns 0-based start position.
|
47
|
-
def
|
56
|
+
def pos
|
48
57
|
@bam1[:core][:pos]
|
49
58
|
end
|
50
59
|
|
51
|
-
|
52
|
-
|
53
|
-
LibHTS.bam_endpos @bam1
|
60
|
+
def pos=(pos)
|
61
|
+
@bam1[:core][:pos] = pos
|
54
62
|
end
|
55
63
|
|
56
64
|
# returns 0-based mate position
|
57
|
-
def
|
65
|
+
def mpos
|
58
66
|
@bam1[:core][:mpos]
|
59
67
|
end
|
60
|
-
|
68
|
+
|
69
|
+
def mpos=(mpos)
|
70
|
+
@bam1[:core][:mpos] = mpos
|
71
|
+
end
|
72
|
+
|
73
|
+
def bin
|
74
|
+
@bam1[:core][:bin]
|
75
|
+
end
|
76
|
+
|
77
|
+
def bin=(bin)
|
78
|
+
@bam1[:core][:bin] = bin
|
79
|
+
end
|
80
|
+
|
81
|
+
# returns end position of the read.
|
82
|
+
def endpos
|
83
|
+
LibHTS.bam_endpos @bam1
|
84
|
+
end
|
61
85
|
|
62
86
|
# returns the chromosome or '' if not mapped.
|
63
87
|
def chrom
|
@@ -66,37 +90,44 @@ module HTS
|
|
66
90
|
LibHTS.sam_hdr_tid2name(@header, tid)
|
67
91
|
end
|
68
92
|
|
69
|
-
|
70
|
-
def contig
|
71
|
-
chrom
|
72
|
-
end
|
93
|
+
alias contig chrom
|
73
94
|
|
74
95
|
# returns the chromosome of the mate or '' if not mapped.
|
75
96
|
def mate_chrom
|
76
|
-
mtid = mate_tid
|
77
97
|
return "" if mtid == -1
|
78
98
|
|
79
99
|
LibHTS.sam_hdr_tid2name(@header, mtid)
|
80
100
|
end
|
81
101
|
|
102
|
+
alias mate_contig mate_chrom
|
103
|
+
|
104
|
+
# Get strand information.
|
82
105
|
def strand
|
83
106
|
LibHTS.bam_is_rev(@bam1) ? "-" : "+"
|
84
107
|
end
|
85
108
|
|
86
|
-
# def start=(v)
|
87
|
-
# raise 'Not Implemented'
|
88
|
-
# end
|
89
|
-
|
90
109
|
# insert size
|
91
110
|
def insert_size
|
92
111
|
@bam1[:core][:isize]
|
93
112
|
end
|
94
113
|
|
114
|
+
alias isize insert_size
|
115
|
+
|
116
|
+
def insert_size=(isize)
|
117
|
+
@bam1[:core][:isize] = isize
|
118
|
+
end
|
119
|
+
|
120
|
+
alias isize= insert_size=
|
121
|
+
|
95
122
|
# mapping quality
|
96
|
-
def
|
123
|
+
def mapq
|
97
124
|
@bam1[:core][:qual]
|
98
125
|
end
|
99
126
|
|
127
|
+
def mapq=(mapq)
|
128
|
+
@bam1[:core][:qual] = mapq
|
129
|
+
end
|
130
|
+
|
100
131
|
# returns a `Cigar` object.
|
101
132
|
def cigar
|
102
133
|
Cigar.new(LibHTS.bam_get_cigar(@bam1), @bam1[:core][:n_cigar])
|
@@ -117,7 +148,7 @@ module HTS
|
|
117
148
|
end
|
118
149
|
|
119
150
|
# return the read sequence
|
120
|
-
def
|
151
|
+
def seq
|
121
152
|
r = LibHTS.bam_get_seq(@bam1)
|
122
153
|
seq = String.new
|
123
154
|
(@bam1[:core][:l_qseq]).times do |i|
|
@@ -125,64 +156,77 @@ module HTS
|
|
125
156
|
end
|
126
157
|
seq
|
127
158
|
end
|
159
|
+
alias sequence seq
|
160
|
+
|
161
|
+
def len
|
162
|
+
@bam1[:core][:l_qseq]
|
163
|
+
end
|
128
164
|
|
129
165
|
# return only the base of the requested index "i" of the query sequence.
|
130
|
-
def
|
166
|
+
def base(n)
|
131
167
|
n += @bam1[:core][:l_qseq] if n < 0
|
132
|
-
return "." if (n >= @bam1[:core][:l_qseq]) || (n < 0) # eg.
|
168
|
+
return "." if (n >= @bam1[:core][:l_qseq]) || (n < 0) # eg. base(-1000)
|
133
169
|
|
134
170
|
r = LibHTS.bam_get_seq(@bam1)
|
135
171
|
SEQ_NT16_STR[LibHTS.bam_seqi(r, n)]
|
136
172
|
end
|
137
173
|
|
138
174
|
# return the base qualities
|
139
|
-
def
|
175
|
+
def qual
|
140
176
|
q_ptr = LibHTS.bam_get_qual(@bam1)
|
141
177
|
q_ptr.read_array_of_uint8(@bam1[:core][:l_qseq])
|
142
178
|
end
|
143
179
|
|
144
180
|
# return only the base quality of the requested index "i" of the query sequence.
|
145
|
-
def
|
181
|
+
def base_qual(n)
|
146
182
|
n += @bam1[:core][:l_qseq] if n < 0
|
147
|
-
return 0 if (n >= @bam1[:core][:l_qseq]) || (n < 0) # eg.
|
183
|
+
return 0 if (n >= @bam1[:core][:l_qseq]) || (n < 0) # eg. base_qual(-1000)
|
148
184
|
|
149
185
|
q_ptr = LibHTS.bam_get_qual(@bam1)
|
150
186
|
q_ptr.get_uint8(n)
|
151
187
|
end
|
152
188
|
|
153
|
-
def flag_str
|
154
|
-
LibHTS.bam_flag2str(@bam1[:core][:flag])
|
155
|
-
end
|
156
|
-
|
157
189
|
# returns a `Flag` object.
|
158
190
|
def flag
|
159
191
|
Flag.new(@bam1[:core][:flag])
|
160
192
|
end
|
161
193
|
|
162
|
-
def
|
163
|
-
|
164
|
-
|
194
|
+
def flag=(flag)
|
195
|
+
case flag
|
196
|
+
when Integer
|
197
|
+
@bam1[:core][:flag] = flag
|
198
|
+
when Flag
|
199
|
+
@bam1[:core][:flag] = flag.value
|
200
|
+
else
|
201
|
+
raise "Invalid flag type: #{flag.class}"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# retruns the auxillary fields.
|
206
|
+
def aux(key = nil)
|
207
|
+
aux = Aux.new(self)
|
208
|
+
if key
|
209
|
+
aux.get(key)
|
210
|
+
else
|
211
|
+
aux
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
# TODO: add a method to get the auxillary fields as a hash.
|
165
216
|
|
166
|
-
|
217
|
+
# TODO: add a method to set the auxillary fields.
|
167
218
|
|
168
|
-
|
169
|
-
# f (real number), H (hexadecimal array),
|
170
|
-
# i (integer), or Z (string).
|
219
|
+
# TODO: add a method to remove the auxillary fields.
|
171
220
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
LibHTS.bam_aux2Z(aux)
|
179
|
-
when "A" # char
|
180
|
-
LibHTS.bam_aux2A(aux).chr
|
221
|
+
# TODO: add a method to set variable length data (qname, cigar, seq, qual).
|
222
|
+
|
223
|
+
# Calling flag is delegated to the Flag object.
|
224
|
+
Flag::TABLE.each_key do |m|
|
225
|
+
define_method(m) do
|
226
|
+
flag.send(m)
|
181
227
|
end
|
182
228
|
end
|
183
229
|
|
184
|
-
# def tags; end
|
185
|
-
|
186
230
|
def to_s
|
187
231
|
kstr = LibHTS::KString.new
|
188
232
|
raise "Failed to format bam record" if LibHTS.sam_format1(@header.struct, @bam1, kstr) == -1
|
data/lib/hts/bam.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Based on hts-python
|
4
|
-
# https://github.com/quinlan-lab/hts-python
|
5
|
-
|
6
3
|
require_relative "../htslib"
|
7
4
|
|
8
5
|
require_relative "hts"
|
@@ -15,7 +12,7 @@ module HTS
|
|
15
12
|
class Bam
|
16
13
|
include Enumerable
|
17
14
|
|
18
|
-
attr_reader :file_name, :
|
15
|
+
attr_reader :file_name, :index_name, :mode, :header
|
19
16
|
|
20
17
|
def self.open(*args, **kw)
|
21
18
|
file = new(*args, **kw) # do not yield
|
@@ -38,9 +35,10 @@ module HTS
|
|
38
35
|
|
39
36
|
# NOTE: Do not check for the existence of local files, since file_names may be remote URIs.
|
40
37
|
|
41
|
-
@file_name
|
42
|
-
@
|
43
|
-
@
|
38
|
+
@file_name = file_name
|
39
|
+
@index_name = index
|
40
|
+
@mode = mode
|
41
|
+
@hts_file = LibHTS.hts_open(@file_name, mode)
|
44
42
|
|
45
43
|
raise Errno::ENOENT, "Failed to open #{@file_name}" if @hts_file.null?
|
46
44
|
|
@@ -66,11 +64,10 @@ module HTS
|
|
66
64
|
end
|
67
65
|
|
68
66
|
def create_index(index_name = nil)
|
67
|
+
warn "Create index for #{@file_name} to #{index_name}"
|
69
68
|
if index
|
70
|
-
warn "Create index for #{@file_name} to #{index_name}"
|
71
69
|
LibHTS.sam_index_build2(@file_name, index_name, -1)
|
72
70
|
else
|
73
|
-
warn "Create index for #{@file_name} to #{index_name}"
|
74
71
|
LibHTS.sam_index_build(@file_name, -1)
|
75
72
|
end
|
76
73
|
end
|
@@ -91,21 +88,20 @@ module HTS
|
|
91
88
|
def close
|
92
89
|
LibHTS.hts_idx_destroy(@idx) if @idx&.null?
|
93
90
|
@idx = nil
|
94
|
-
|
95
|
-
@hts_file = nil
|
96
|
-
end
|
97
|
-
|
98
|
-
def closed?
|
99
|
-
@hts_file.nil? || @hts_file.null?
|
91
|
+
super
|
100
92
|
end
|
101
93
|
|
102
94
|
def write_header(header)
|
95
|
+
raise IOError, "closed stream" if closed?
|
96
|
+
|
103
97
|
@header = header.dup
|
104
98
|
LibHTS.hts_set_fai_filename(@hts_file, @file_name)
|
105
99
|
LibHTS.sam_hdr_write(@hts_file, header)
|
106
100
|
end
|
107
101
|
|
108
102
|
def write(aln)
|
103
|
+
raise IOError, "closed stream" if closed?
|
104
|
+
|
109
105
|
aln_dup = aln.dup
|
110
106
|
LibHTS.sam_write1(@hts_file, header, aln_dup) > 0 || raise
|
111
107
|
end
|
@@ -114,6 +110,7 @@ module HTS
|
|
114
110
|
# Generate a new Record object each time.
|
115
111
|
# Slower than each.
|
116
112
|
def each_copy
|
113
|
+
raise IOError, "closed stream" if closed?
|
117
114
|
return to_enum(__method__) unless block_given?
|
118
115
|
|
119
116
|
while LibHTS.sam_read1(@hts_file, header, bam1 = LibHTS.bam_init1) != -1
|
@@ -127,6 +124,7 @@ module HTS
|
|
127
124
|
# Record object is reused.
|
128
125
|
# Faster than each_copy.
|
129
126
|
def each
|
127
|
+
raise IOError, "closed stream" if closed?
|
130
128
|
# Each does not always start at the beginning of the file.
|
131
129
|
# This is the common behavior of IO objects in Ruby.
|
132
130
|
# This may change in the future.
|
@@ -140,6 +138,7 @@ module HTS
|
|
140
138
|
|
141
139
|
# query [WIP]
|
142
140
|
def query(region)
|
141
|
+
raise IOError, "closed stream" if closed?
|
143
142
|
raise "Index file is required to call the query method." unless index_loaded?
|
144
143
|
|
145
144
|
qiter = LibHTS.sam_itr_querys(@idx, header, region)
|
data/lib/hts/bcf/format.rb
CHANGED
@@ -1,9 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# https://github.com/brentp/hts-nim/blob/master/src/hts/vcf.nim
|
4
|
-
# This is a port from Nim.
|
5
|
-
# TODO: Make it more like Ruby.
|
6
|
-
|
7
3
|
module HTS
|
8
4
|
class Bcf < Hts
|
9
5
|
class Format
|
@@ -32,6 +28,10 @@ module HTS
|
|
32
28
|
get(key, :string)
|
33
29
|
end
|
34
30
|
|
31
|
+
def [](key)
|
32
|
+
get(key)
|
33
|
+
end
|
34
|
+
|
35
35
|
def get(key, type = nil)
|
36
36
|
n = FFI::MemoryPointer.new(:int)
|
37
37
|
p1 = @p1
|
@@ -45,7 +45,15 @@ module HTS
|
|
45
45
|
p1.read_pointer
|
46
46
|
end
|
47
47
|
|
48
|
-
|
48
|
+
# The GT FORMAT field is special in that it is marked as a string in the header,
|
49
|
+
# but it is actually encoded as an integer.
|
50
|
+
if key == "GT"
|
51
|
+
type = :int
|
52
|
+
elsif type.nil?
|
53
|
+
type = ht_type_to_sym(get_fmt_type(key))
|
54
|
+
end
|
55
|
+
|
56
|
+
case type&.to_sym
|
49
57
|
when :int, :int32
|
50
58
|
format_values.call(LibHTS::BCF_HT_INT)
|
51
59
|
.read_array_of_int32(n.read_int)
|
@@ -53,22 +61,85 @@ module HTS
|
|
53
61
|
format_values.call(LibHTS::BCF_HT_REAL)
|
54
62
|
.read_array_of_float(n.read_int)
|
55
63
|
when :flag
|
56
|
-
raise NotImplementedError, "Flag type not implemented yet."
|
64
|
+
raise NotImplementedError, "Flag type not implemented yet. " \
|
65
|
+
"Please file an issue on GitHub."
|
57
66
|
# format_values.call(LibHTS::BCF_HT_FLAG)
|
58
67
|
# .read_int == 1
|
59
68
|
when :string, :str
|
60
|
-
raise NotImplementedError, "String type not implemented yet."
|
69
|
+
raise NotImplementedError, "String type not implemented yet. " \
|
70
|
+
"Please file an issue on GitHub."
|
61
71
|
# format_values.call(LibHTS::BCF_HT_STR)
|
62
72
|
# .read_string
|
63
73
|
end
|
64
74
|
end
|
65
75
|
|
66
|
-
def
|
76
|
+
def fields
|
77
|
+
ids.map do |id|
|
78
|
+
name = LibHTS.bcf_hdr_int2id(@record.header.struct, LibHTS::BCF_DT_ID, id)
|
79
|
+
num = LibHTS.bcf_hdr_id2number(@record.header.struct, LibHTS::BCF_HL_FMT, id)
|
80
|
+
type = LibHTS.bcf_hdr_id2type(@record.header.struct, LibHTS::BCF_HL_FMT, id)
|
81
|
+
{
|
82
|
+
name: name,
|
83
|
+
n: num,
|
84
|
+
type: ht_type_to_sym(type),
|
85
|
+
id: id
|
86
|
+
}
|
87
|
+
end
|
88
|
+
end
|
67
89
|
|
68
|
-
|
69
|
-
|
90
|
+
def length
|
91
|
+
@record.struct[:n_fmt]
|
92
|
+
end
|
93
|
+
|
94
|
+
def size
|
95
|
+
length
|
96
|
+
end
|
97
|
+
|
98
|
+
def to_h
|
99
|
+
ret = {}
|
100
|
+
ids.each do |id|
|
101
|
+
name = LibHTS.bcf_hdr_int2id(@record.header.struct, LibHTS::BCF_DT_ID, id)
|
102
|
+
ret[name] = get(name)
|
103
|
+
end
|
104
|
+
ret
|
105
|
+
end
|
70
106
|
|
71
|
-
def genotypes; end
|
107
|
+
# def genotypes; end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def fmt_ptr
|
112
|
+
@record.struct[:d][:fmt].to_ptr
|
113
|
+
end
|
114
|
+
|
115
|
+
def ids
|
116
|
+
fmt_ptr.read_array_of_struct(LibHTS::BcfFmt, length).map do |fmt|
|
117
|
+
fmt[:id]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def get_fmt_type(qname)
|
122
|
+
@record.struct[:n_fmt].times do |i|
|
123
|
+
fmt = LibHTS::BcfFmt.new(@record.struct[:d][:fmt] + i * LibHTS::BcfFmt.size)
|
124
|
+
id = fmt[:id]
|
125
|
+
name = LibHTS.bcf_hdr_int2id(@record.header.struct, LibHTS::BCF_DT_ID, id)
|
126
|
+
if name == qname
|
127
|
+
type = LibHTS.bcf_hdr_id2type(@record.header.struct, LibHTS::BCF_HL_FMT, id)
|
128
|
+
return type
|
129
|
+
end
|
130
|
+
end
|
131
|
+
nil
|
132
|
+
end
|
133
|
+
|
134
|
+
def ht_type_to_sym(t)
|
135
|
+
case t
|
136
|
+
when LibHTS::BCF_HT_FLAG then :flag
|
137
|
+
when LibHTS::BCF_HT_INT then :int
|
138
|
+
when LibHTS::BCF_HT_REAL then :float
|
139
|
+
when LibHTS::BCF_HT_STR then :string
|
140
|
+
when LibHTS::BCF_HT_LONG then :float
|
141
|
+
end
|
142
|
+
end
|
72
143
|
end
|
73
144
|
end
|
74
145
|
end
|