Coveralls logob
Coveralls logo
  • Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaAstro / FITSIO.jl / 206

9 Jan 2018 - 2:02 coverage: 81.889% (+0.02%) from 81.865%
206

Pull #92

travis-ci

9181eb84f9c35729a3bad740fb7f9d93?size=18&default=identiconweb-flow
Drop support for unmaintained Julia 0.5
Pull Request #92: Drop support for unmaintained Julia 0.5

633 of 773 relevant lines covered (81.89%)

39.7 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.33
/src/header.jl
1
# FITSHeader methods
2

3
# -----------------------------------------------------------------------------
4
# Helper functions
5
#
6
# Used here and in other files. Functions that operate on FITSFile
7
# start with `fits_`.
8

9
function try_parse_hdrval(::Type{Bool}, s::String)
10
    if length(s) == 1
110×
11
        if s[1] == 'T'
52×
12
            return Nullable(true)
22×
13
        elseif s[1] == 'F'
30×
14
            return Nullable(false)
4×
15
        end
16
    end
17
    return Nullable{Bool}()
84×
18
end
19

20
# Note that trailing whitespace is not significant in FITS header
21
# keywords, but *leading* whitespace is, so "'    '" parses as " " (a
22
# single space).  See CFITSIO manual section 4.5 for details.
23
#
24
# TODO: parse '' within the string as a single '.
25
function try_parse_hdrval(::Type{String}, s::String)
26
    if length(s) < 2 || s[1] != '\'' || s[end] != '\''
84×
27
        return Nullable{String}()
72×
28
    end
29

30
    i = endof(s) - 1
12×
31
    while i > 2
12×
32
        if s[i] != ' '
18×
33
            return Nullable(s[2:i])
12×
34
        end
35
        i -= 1
6×
36
    end
37
    return Nullable(s[2:i])
!
38
end
39

40
try_parse_hdrval(::Type{Float64}, s::String) = tryparse(Float64, s)
18×
41
try_parse_hdrval(::Type{Int}, s::String) = tryparse(Int, s)
72×
42

43
# Try to parse the header value as any type
44
function try_parse_hdrval(s::String)
45
    length(s) == 0 && return Nullable(nothing)
138×
46

47
    nb = try_parse_hdrval(Bool, s)
110×
48
    isnull(nb) || return nb
110×
49

50
    ns = try_parse_hdrval(String, s)
84×
51
    isnull(ns) || return ns
84×
52

53
    ni = try_parse_hdrval(Int, s)
72×
54
    isnull(ni) || return ni
72×
55

56
    nf = try_parse_hdrval(Float64, s)
18×
57
    isnull(nf) || return nf
18×
58

59
    return Nullable{Any}()
4×
60
end
61

62
# functions for displaying header values in show(io, header)
63
hdrval_repr(v::Bool) = v ? "T" : "F"
2×
64
hdrval_repr(v::String) = @sprintf "'%s'" v
2×
65
hdrval_repr(v::Union{AbstractFloat, Integer}) = string(v)
4×
66

67
# returns one of: String, Bool, Int, Float64, nothing
68
# (never error)
69
function parse_header_val(s::String)
70
    nval = try_parse_hdrval(s)
138×
71
    return isnull(nval) ? s : get(nval)
138×
72
end
73

74
# Try to read the raw keys in order given; returns Nullable.
75
# (null if no key exists or if parsing an existing key is unsuccessful.)
76
function fits_try_read_keys{T}(f::FITSFile, ::Type{T}, keys)
77
    status = Cint[0]
52×
78
    value = Vector{UInt8}(71)
52×
79
    for key in keys
52×
80
        ccall((:ffgkey, libcfitsio), Cint,
104×
81
              (Ptr{Void},Ptr{UInt8},Ptr{UInt8},Ptr{UInt8},Ptr{Cint}),
82
              f.ptr, key, value, C_NULL, status)
83

84
        # If the key is found, return it. If there was some other error
85
        # besides key not found, throw an error.
86
        if status[1] == 0
104×
87
            return try_parse_hdrval(T, unsafe_string(pointer(value)))
!
88
        elseif status[1] != 202
104×
UNCOV
89
            error(fits_get_errstatus(status[1]))
!
90
        end
91
    end
92
    return Nullable{T}()
52×
93
end
94

95
# Build a string with extension keywords, if present.
96
# This is a helper function for show(::HDU).
97
const EXTNAME_KEYS = ["EXTNAME", "HDUNAME"]
98
const EXTVER_KEYS = ["EXTVER", "HDUVER"]
99
fits_try_read_extname(f::FITSFile) =
26×
100
    fits_try_read_keys(f, String, EXTNAME_KEYS)
101
fits_try_read_extver(f::FITSFile) = fits_try_read_keys(f, Int, EXTVER_KEYS)
26×
102

103
function fits_get_ext_info_string(f::FITSFile)
104
    extname = fits_try_read_extname(f)
4×
105
    extver = fits_try_read_extver(f)
4×
106
    if !isnull(extname) && !isnull(extver)
4×
107
        return " (name=$(repr(get(extname))), ver=$(get(extver)))"
!
108
    elseif !isnull(extname)
4×
109
        return " (name=$(repr(get(extname))))"
!
110
    end
111
    return ""
4×
112
end
113

114

115
# Return indices of reserved keys in a header.
116
# This is more complex than you would think because some reserved keys
117
# are only reserved when other keys are present. Also, in general a key
118
# may appear more than once in a header.
119
const RESERVED_KEYS = ["SIMPLE","EXTEND","XTENSION","BITPIX","PCOUNT","GCOUNT",
120
                       "THEAP","EXTNAME","BSCALE","BZERO","BLANK",
121
                       "ZQUANTIZ","ZDITHER0","ZIMAGE","ZCMPTYPE","ZSIMPLE",
122
                       "ZTENSION","ZPCOUNT","ZGCOUNT","ZBITPIX","ZEXTEND",
123
                       "CHECKSUM","DATASUM"]
124
function reserved_key_indices(hdr::FITSHeader)
125
    nhdr = length(hdr)
6×
126
    indices = Int[]
6×
127
    for i=1:nhdr
6×
128
        if in(hdr.keys[i], RESERVED_KEYS)
40×
129
            push!(indices, i)
6×
130
        end
131
    end
132

133
    # Note that this removes anything matching NAXIS\d regardless of # of axes.
134
    if in("NAXIS", hdr.keys)
6×
135
        for i=1:nhdr
2×
136
            if ismatch(r"^NAXIS\d*$", hdr.keys[i])
16×
137
                push!(indices, i)
6×
138
            end
139
        end
140
    end
141

142
    if in("ZNAXIS", hdr.keys)
6×
143
        for i=1:nhdr
!
144
            if (ismatch(r"^ZNAXIS\d*$", hdr.keys[i]) ||
!
145
                ismatch(r"^ZTILE\d*$", hdr.keys[i]) ||
146
                ismatch(r"^ZNAME\d*$", hdr.keys[i]) ||
147
                ismatch(r"^ZVAL\d*$", hdr.keys[i]))
148
                push!(indices, i)
!
149
            end
150
        end
151
    end
152

153
    if in("TFIELDS", hdr.keys)
6×
154
        for i=1:nhdr
!
155
            for re in [r"^TFORM\d*$", r"^TTYPE\d*$", r"^TDIM\d*$",
!
156
                       r"^TUNIT\d*$", r"^TSCAL\d*$", r"^TZERO\d*$",
157
                       r"^TNULL\d*$", r"^TDISP\d*$", r"^TDMIN\d*$",
158
                       r"^TDMAX\d*$", r"^TDESC\d*$", r"^TROTA\d*$",
159
                       r"^TRPIX\d*$", r"^TRVAL\d*$", r"^TDELT\d*$",
160
                       r"^TCUNI\d*$", r"^TFIELDS$"]
UNCOV
161
                if ismatch(re, hdr.keys[i])
!
UNCOV
162
                    push!(indices, i)
!
163
                end
164
            end
165
        end
166
    end
167

168
    return indices
6×
169
end
170

171

172
# Write header to CHDU.
173
# If `clean` is true, skip writing reserved header keywords.
174
function fits_write_header(f::FITSFile, hdr::FITSHeader, clean::Bool=true)
175
    indices = clean ? reserved_key_indices(hdr) : Int[]
6×
176
    for i=1:length(hdr)
6×
177
        if clean && in(i, indices)
40×
178
            continue
12×
179
        end
180
        if hdr.keys[i] == "COMMENT"
28×
181
            fits_write_comment(f, hdr.comments[i])
4×
182
        elseif hdr.keys[i] == "HISTORY"
24×
183
            fits_write_history(f, hdr.comments[i])
4×
184
        elseif hdr.comments[i] == ""
20×
185
            fits_update_key(f, hdr.keys[i], hdr.values[i])
!
186
        else
187
            fits_update_key(f, hdr.keys[i], hdr.values[i], hdr.comments[i])
20×
188
        end
189
    end
190
end
191

192

193
# -----------------------------------------------------------------------------
194
# Public API
195

196
"""
197
    read_key(hdu, key::String) -> (value, comment)
198
    read_key(hdu, key::Integer) -> (keyname, value, comment)
199

200
Read the HDU header record specified by keyword or position.
201
"""
202
function read_key(hdu::HDU, key::Integer)
203
    fits_assert_open(hdu.fitsfile)
12×
204
    fits_movabs_hdu(hdu.fitsfile, hdu.ext)
12×
205
    keyout, value, comment = fits_read_keyn(hdu.fitsfile, key)
12×
206
    keyout, parse_header_val(value), comment
12×
207
end
208

209
function read_key(hdu::HDU, key::String)
210
    fits_assert_open(hdu.fitsfile)
12×
211
    fits_movabs_hdu(hdu.fitsfile, hdu.ext)
12×
212
    value, comment = fits_read_keyword(hdu.fitsfile, key)
12×
213
    parse_header_val(value), comment
12×
214
end
215

216

217
"""
218
    write_key(hdu, key::String, value[, comment])
219

220
Write a keyword value the HDU's header. `value` can be a standard
221
header type (`String`, `Bool`, `Integer`, `AbstractFloat`) or
222
`nothing`, in which case the value part of the record will be
223
empty. If the keyword already exists, the value will be
224
overwritten. The comment will only be overwritten if given. If the
225
keyword does not already exist, a new record will be appended at the
226
end of the header.
227
"""
228
function write_key(hdu::HDU, key::String,
229
                   value::Union{String, Bool, Integer, AbstractFloat, Void},
230
                   comment::Union{String, Ptr{Void}}=C_NULL)
231
    fits_assert_open(hdu.fitsfile)
14×
232
    fits_movabs_hdu(hdu.fitsfile, hdu.ext)
12×
233
    fits_update_key(hdu.fitsfile, key, value, comment)
12×
234
end
235

236

237
"""
238
    read_header(hdu) -> FITSHeader
239

240
Read the entire header from the given HDU and return a `FITSHeader` object.
241
The value of each header record is parsed as `Int`, `Float64`, `String`,
242
`Bool` or `nothing` according to the FITS standard.
243

244
If the value cannot be parsed according to the FITS standard, the value is
245
stored as the raw unparsed `String`.
246
"""
247
function read_header(hdu::HDU)
248
    fits_assert_open(hdu.fitsfile)
10×
249
    fits_movabs_hdu(hdu.fitsfile, hdu.ext)
10×
250

251
    # Below, we use a direct call to ffgkyn so that we can keep reusing the
252
    # same buffers.
253
    key = Vector{UInt8}(81)
10×
254
    value = Vector{UInt8}(81)
10×
255
    comment = Vector{UInt8}(81)
10×
256
    status = Cint[0]
10×
257

258
    nkeys, morekeys = fits_get_hdrspace(hdu.fitsfile)
10×
259

260
    # Initialize output arrays
261
    keys = Vector{String}(nkeys)
10×
262
    values = Vector{Any}(nkeys)
10×
263
    comments = Vector{String}(nkeys)
10×
264
    for i=1:nkeys
10×
265
        ccall((:ffgkyn,libcfitsio), Cint,
114×
266
              (Ptr{Void},Cint,Ptr{UInt8},Ptr{UInt8},Ptr{UInt8},Ptr{Cint}),
267
              hdu.fitsfile.ptr, i, key, value, comment, status)
268
        keys[i] = unsafe_string(pointer(key))
114×
269
        values[i] = parse_header_val(unsafe_string(pointer(value)))
114×
270
        comments[i] = unsafe_string(pointer(comment))
114×
271
    end
272
    fits_assert_ok(status[1])
10×
273
    FITSHeader(keys, values, comments)
10×
274
end
275

276

277
"""
278
    read_header(hdu, String) -> String
279

280
Read the entire header from the given HDU as a single string.
281
"""
282
function read_header(hdu::HDU, ::Type{String})
283
    fits_assert_open(hdu.fitsfile)
4×
284
    fits_movabs_hdu(hdu.fitsfile, hdu.ext)
4×
285
    fits_hdr2str(hdu.fitsfile)
4×
286
end
287

288

289
"""
290
    length(hdr)
291

292
Number of records.
293
"""
294
length(hdr::FITSHeader) = length(hdr.keys)
18×
295

296
"""
297
    haskey(hdr)
298

299
Header keyword exists.
300
"""
301
haskey(hdr::FITSHeader, key::String) = in(key, hdr.keys)
2×
302

303
"""
304
    keys(hdr)
305

306
Array of keywords (not a copy).
307
"""
308
keys(hdr::FITSHeader) = hdr.keys
!
309

310
"""
311
    values(hdr)
312

313
Array of values (not a copy).
314
"""
315
values(hdr::FITSHeader) = hdr.values
!
316

317
getindex(hdr::FITSHeader, key::String) = hdr.values[hdr.map[key]]
10×
318
getindex(hdr::FITSHeader, i::Integer) = hdr.values[i]
!
319

320
function setindex!(hdr::FITSHeader, value::Any, key::String)
321
    fits_assert_isascii(key)
4×
322
    if in(key, hdr.keys)
4×
323
        hdr.values[hdr.map[key]] = value
4×
324
    else
325
        push!(hdr.keys, key)
!
326
        push!(hdr.values, value)
!
327
        push!(hdr.comments, "")
!
328
        hdr.map[key] = length(hdr.keys)
!
329
    end
330
end
331

332
function setindex!(hdr::FITSHeader, value::Any, i::Integer)
333
    hdr.values[i] = value
2×
334
end
335

336
# Comments
337
"""
338
    get_comment(hdr, key)
339

340
Get the comment based on keyword or index.
341
"""
342
get_comment(hdr::FITSHeader, key::String) = hdr.comments[hdr.map[key]]
2×
343
get_comment(hdr::FITSHeader, i::Integer) = hdr.comments[i]
4×
344

345
"""
346
    set_comment!(hdr, key, comment)
347

348
Set the comment baed on keyword or index.
349
"""
350
function set_comment!(hdr::FITSHeader, key::String, comment::String)
351
    fits_assert_isascii(comment)
2×
352
    hdr.comments[hdr.map[key]] = comment
2×
353
end
354
function set_comment!(hdr::FITSHeader, i::Integer, comment::String)
355
    fits_assert_isascii(comment)
!
356
    hdr.comments[i] = comment
!
357
end
358

359
# Display the header
360
function show(io::IO, hdr::FITSHeader)
361
    n = length(hdr)
2×
362
    for i=1:n
2×
363
        if hdr.keys[i] == "COMMENT" || hdr.keys[i] == "HISTORY"
12×
364
            @printf io "%s %s" hdr.keys[i] hdr.comments[i][1:min(71, end)]
4×
365
        else
366
            @printf io "%-8s" hdr.keys[i]
8×
367
            if hdr.values[i] === nothing
8×
368
                print(io, "                      ")
!
369
                rc = 50  # remaining characters on line
!
370
            else
371
                val = hdrval_repr(hdr.values[i])
8×
372
                @printf io "= %20s" val
8×
373
                rc = length(val) <= 20 ? 50 : 70 - length(val)
8×
374
            end
375

376
            if length(hdr.comments[i]) > 0
8×
377
                @printf io " / %s" hdr.comments[i][1:min(rc-3, end)]
6×
378
            end
379
        end
380
        i != n && println(io)
12×
381
    end
382
end
Troubleshooting · Open an Issue · Sales · Support · ENTERPRISE · CAREERS · STATUS
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2023 Coveralls, Inc