Skip to content

Commit 5926d23

Browse files
aquatikogiordano
authored andcommitted
Reproject-core (#3)
* reproject-core function added * docs and logic fixes * CI error fixes * incresing coverage
1 parent 12a90b7 commit 5926d23

File tree

5 files changed

+165
-1
lines changed

5 files changed

+165
-1
lines changed

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ version = "0.1.0"
55

66
[deps]
77
FITSIO = "525bcba6-941b-5504-bd06-fd0dc1a4d2eb"
8+
Interpolations = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59"
9+
SkyCoords = "fc659fc5-75a3-5475-a2ea-3da92c065361"
810
WCS = "15f3aee2-9e10-537f-b834-a6fb8bdb944d"
911

1012
[compat]

src/Reproject.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
module Reproject
22

3-
using FITSIO, WCS
3+
using FITSIO, WCS, Interpolations, SkyCoords
4+
using SkyCoords: lat,lon
45

56
include("parsers.jl")
67
include("utils.jl")
8+
include("core.jl")
9+
10+
export
11+
reproject
712

813
end # module

src/core.jl

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
"""
2+
reproject(input_data, output_projection; shape_out = nothing, order = 1, hdu_in = 1, hdu_out = 1)
3+
4+
Reprojects image data to a new projection using interpolation.
5+
6+
# Arguments
7+
- `input_data`: Image data which is being reprojected.
8+
It can be an ImageHDU, FITS object or name of a FITS file.
9+
- `output_projection`: Frame in which data is reprojected.
10+
Frame can be taken from WCSTransform object, ImageHDU, FITS or name of FITS file.
11+
- `shape_out`: Shape of image after reprojection.
12+
- `order`: Order of interpolation.
13+
0: Nearest-neighbor
14+
1: Linear
15+
2: Quadratic
16+
- `hdu_in`: Used to specify HDU number when giving input as FITS or name of FITS file.
17+
- `hud_out:` Used to specify HDU number when giving output projection as FITS or name of FITS file.
18+
"""
19+
function reproject(input_data, output_projection; shape_out = nothing, order::Int = 1, hdu_in::Int = 1, hdu_out::Int = 1)
20+
if input_data isa ImageHDU
21+
array_in, wcs_out = parse_input_data(input_data)
22+
else
23+
array_in, wcs_out = parse_input_data(input_data, hdu_in)
24+
end
25+
26+
if output_projection isa FITS || output_projection isa String
27+
wcs_in, shape_out = parse_output_projection(output_projection, hdu_out)
28+
else
29+
wcs_in, shape_out = parse_output_projection(output_projection, shape_out)
30+
end
31+
32+
type_in = wcs_to_celestial_frame(wcs_in)
33+
type_out = wcs_to_celestial_frame(wcs_out)
34+
35+
if type_in == type_out && shape_out === nothing
36+
return array_in
37+
end
38+
39+
img_out = fill(NaN, shape_out)
40+
41+
itp = interpolator(array_in, order)
42+
shape_in = size(array_in)
43+
44+
for i in 1:shape_out[1]
45+
for j in 1:shape_out[2]
46+
pix_coord_in = [float(i), float(j)]
47+
world_coord_in = pix_to_world(wcs_in, pix_coord_in)
48+
49+
if type_in == "ICRS"
50+
coord_in = ICRSCoords(deg2rad(world_coord_in[1]), deg2rad(world_coord_in[2]))
51+
elseif type_in == "Gal"
52+
coord_in = GalCoords(deg2rad(world_coord_in[1]), deg2rad(world_coord_in[2]))
53+
elseif type_in == "FK5"
54+
coord_in = FK5Coords{wcs_in.equinox}(deg2rad(world_coord_in[1]), deg2rad(world_coord_in[2]))
55+
else
56+
throw(ArgumentError("Unsupported output WCS coordinate type"))
57+
end
58+
59+
if type_out == "ICRS"
60+
coord_out = convert(ICRSCoords, coord_in)
61+
elseif type_out == "Gal"
62+
coord_out = convert(GalCoords, coord_in)
63+
elseif type_out == "FK5"
64+
coord_out = convert(FK5Coords{wcs_out.equinox}, coord_in)
65+
else
66+
throw(ArgumentError("Unsupported input WCS coordinate type"))
67+
end
68+
69+
pix_coord_out = world_to_pix(wcs_out, [rad2deg(lon(coord_out)), rad2deg(lat(coord_out))])
70+
71+
if 1 <= pix_coord_out[1] <= shape_in[1] && 1 <= pix_coord_out[2] <= shape_in[2]
72+
img_out[i,j] = itp(pix_coord_out[1], pix_coord_out[2])
73+
end
74+
end
75+
end
76+
77+
return img_out, (!isnan).(img_out)
78+
end
79+
80+
81+
"""
82+
interpolator(array_in, order::Int)
83+
84+
Returns an interpolator with the given array and order of interpolation.
85+
"""
86+
function interpolator(array_in::AbstractArray, order::Int)
87+
if order == 0
88+
itp = interpolate(array_in, BSpline(Constant()))
89+
elseif order == 1
90+
itp = interpolate(array_in, BSpline(Linear()))
91+
else
92+
itp = interpolate(array_in, BSpline(Quadratic(InPlace(OnCell()))))
93+
end
94+
95+
return itp
96+
end

test/core.jl

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
@testset "reproject-core" begin
2+
if !isfile("data/gc_2mass_k.fits")
3+
mkpath("data")
4+
download("https://astropy.stsci.edu/data/galactic_center/gc_2mass_k.fits", "data/gc_2mass_k.fits")
5+
end
6+
if !isfile("data/gc_msx_e.fits")
7+
mkpath("data")
8+
download("https://astropy.stsci.edu/data/galactic_center/gc_msx_e.fits", "data/gc_msx_e.fits")
9+
end
10+
11+
imgin = FITS("data/gc_msx_e.fits") # project this
12+
imgout = FITS("data/gc_2mass_k.fits") # into this coordinate
13+
14+
hdu1 = astropy.io.fits.open("data/gc_2mass_k.fits")[1]
15+
hdu2 = astropy.io.fits.open("data/gc_msx_e.fits")[1]
16+
17+
function check_diff(arr1, arr2)
18+
diff = 1e-3
19+
count = 0
20+
for i in 1 : size(arr1)[1] - 1
21+
for j in 1 : size(arr2)[2] - 1
22+
if abs(arr1[i,j] - arr2[j,i]) > diff
23+
count+=1
24+
end
25+
end
26+
end
27+
return iszero(count)
28+
end
29+
30+
@test check_diff(reproject(imgin, imgout, order = 0)[1], rp.reproject_interp(hdu2, hdu1.header, order = 0)[1]) == true
31+
@test check_diff(reproject(imgout, imgin, order = 0)[1], rp.reproject_interp(hdu1, hdu2.header, order = 0)[1]) == true
32+
@test check_diff(reproject(imgin, imgout, order = 1)[1], rp.reproject_interp(hdu2, hdu1.header, order = 1)[1]) == true
33+
@test check_diff(reproject(imgin, imgout, order = 2)[1], rp.reproject_interp(hdu2, hdu1.header, order = 2)[1]) == true
34+
@test check_diff(reproject(imgin[1], imgout[1], shape_out = (1000,1000))[1],
35+
rp.reproject_interp(hdu2, astropy.wcs.WCS(hdu1.header), shape_out = (1000,1000))[1]) == true
36+
37+
wcs = WCSTransform(2; ctype = ["RA---AIR", "DEC--AIR"], radesys = "UNK")
38+
@test_throws ArgumentError reproject(imgin, wcs, shape_out = (100,100))
39+
40+
fname = tempname() * ".fits"
41+
f = FITS(fname, "w")
42+
inhdr = FITSHeader(["CTYPE1", "CTYPE2", "RADESYS", "FLTKEY", "INTKEY", "BOOLKEY", "STRKEY", "COMMENT",
43+
"HISTORY"],
44+
["RA---TAN", "DEC--TAN", "UNK", 1.0, 1, true, "string value", nothing, nothing],
45+
["",
46+
"",
47+
"",
48+
"floating point keyword",
49+
"",
50+
"boolean keyword",
51+
"string value",
52+
"this is a comment",
53+
"this is a history"])
54+
55+
indata = reshape(Float32[1:100;], 5, 20)
56+
write(f, indata; header=inhdr)
57+
@test_throws ArgumentError reproject(f[1], imgin, shape_out = (100,100))
58+
close(f)
59+
end

test/runtests.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ ENV["PYTHON"]=""
77
Conda.add_channel("astropy")
88
Conda.add("reproject")
99
rp = pyimport("reproject")
10+
astropy = pyimport("astropy")
1011

1112
@testset "Reproject.jl" begin
1213
include("parsers.jl")
1314
include("utils.jl")
15+
include("core.jl")
1416
end

0 commit comments

Comments
 (0)