[AstroPy] how to display an image on an different WCS

Hessman, Frederic fhessma at uni-goettingen.de
Tue Apr 23 11:14:45 EDT 2024


There are tutorials and plenty of examples showing how to put coordinates onto a plot of an image given the image's WCS, but what about the other way around:  define the coordinate system of the plot and then place the image in that frame?

Let's say one has a 101x101 image with a flat blob on it and an outer frame (so one can easily see rotations) and whose pixels are in an unrotated FK5 frame, centered at (00:11:22,-33:44:55) : the accompanying script will demo all of this.

I've got a WCS for the image, called "wcs"

CTYPE : 'RA---TAN' 'DEC--TAN'
CRVAL : 2.8416666666666663 33.74861111111111
CRPIX : 51.0 51.0
PC1_1 PC1_2  : 1.0 0.0
PC2_1 PC2_2  : 0.0 1.0
CDELT : 0.000277777777777777 0.000277777777777777
NAXIS : 101  101

I want to display the image where the galactic coordinates are the main axes, so I've created a galactic WCS from scratch at the same central position, called "gal_wcs".

CTYPE : 'GLON-TAN' 'GLAT-TAN'
CRVAL : 113.46911966371806 -28.38577360534605
CRPIX : 51.0 51.0
PC1_1 PC1_2  : 1.0 0.0
PC2_1 PC2_2  : 0.0 1.0
CDELT : 0.000277777777777777 0.000277777777777777
NAXIS : 101  101

Naively, I'd expect to be able to do it this way :

fig = plt.figure(figsize=(4,4))

ax = plt.subplot (projection=gal_wcs, label='galactic')
ax.coords.grid (True, color='gray',ls='solid')
ax.coords[0].set_axislabel (r'Galactic Longitude')
ax.coords[1].set_axislabel (r'Galactic Latitude')

overlay = ax.get_coords_overlay ('fk5')
overlay.grid (True, color='gray',ls='dotted')
overlay[0].set_axislabel (r'Right Ascension (J2000)')
overlay[1].set_axislabel (r'Declination (J2000)')

fk5_2_gal = ax.get_transform(wcs) # TRANSFORMATION FROM wcs=FK5 TO ax=galactic
ax.imshow (data,origin='lower',interpolation='nearest',transform=fk5_to_gal)

but this doesn't work: the image contents are rotated but matplotlib creates data outside of the frame to maintain an un-rotated area!  😳

In order to see what's happening, one can increase the size of the coordinate space, e.g.

plt.xlim (right=0-0.2*nx,left=nx-1+0.2*nx)
plt.ylim (top=0-0.2*ny,bottom=ny-1+0.2*ny)

but this doesn't really change anything: one sees the displayed image is not rotated, just the contents, and one sees the area of data created by matplotlib outside of the rotated image.  The area used is not the same in the two cases, so it's not simply matplotlib filling in the area defined by gal_wcs.

Where am I confused?  Can someone suggest an example I've missed?  Running python 3.9.11, astropy 6.0.0, matplotlib 3.5.1 on Darwin 12.7.4 / Monterey.

Rick



-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.python.org/pipermail/astropy/attachments/20240423/a8c38ab7/attachment-0001.html>
-------------- next part --------------
# test.py

"""
Plots a fake (RA,DEC) image in a galactic reference frame.
"""

import astropy.units      as u
import matplotlib.pyplot  as plt
import numpy              as np

from astropy.coordinates import FK5, Galactic, SkyCoord
from astropy.wcs         import WCS, utils

# ---- DEFINE CENTER OF IMAGE

center = SkyCoord ('00:11:22','+33:44:55',frame='icrs',unit=(u.hourangle,u.deg))
print ('Center:',center)

# ---- CREATE FAKE IMAGE

data = np.zeros((101,101))
ny,nx = data.shape
x,y = np.meshgrid (np.arange(nx),np.arange(ny))
x0,y0 = nx//2+1,ny//2+1
data = np.exp(-((x-x0)**2/20**2+(y-y0)**2/3**2))
mask = (x == 0)+(x == nx-1)+(y == 0//3)+(y == ny-1)
data[np.where(mask)] = 1.0
plt.imshow (data)
plt.show ()

hdr = utils.celestial_frame_to_wcs (FK5()).to_header()
hdr['CRPIX1'] = nx//2+1
hdr['CRPIX2'] = ny//2+1
hdr['CDELT1'] = 1./3600	# ARCSEC
hdr['CDELT2'] = 1./3600 # ARCSEC
hdr['CRVAL1'] = center.ra.deg
hdr['CRVAL2'] = center.dec.deg
hdr['NAXIS']  = 2
hdr['NAXIS1'] = nx
hdr['NAXIS2'] = ny

"""
hdr['PC1_1'] = np.cos(45*np.pi/180)
hdr['PC1_2'] = np.sin(45*np.pi/180)
hdr['PC2_1'] = hdr['PC1_2']
hdr['PC2_2'] = hdr['PC1_1']
"""
wcs  = WCS(hdr)
print ('\nWCS of image:\n',wcs)

# ---- CREATE A CORRESPONDING GALACTIC WCS

adjacent = utils.pixel_to_skycoord (nx//2,ny//2,wcs)
cdelt = np.abs(center.separation (adjacent).to(u.deg))

gal_hdr = utils.celestial_frame_to_wcs (Galactic()).to_header()
gal_hdr['CRPIX1'] = nx//2+1
gal_hdr['CRPIX2'] = ny//2+1
gal_hdr['CDELT1'] = hdr['CDELT1']
gal_hdr['CDELT2'] = hdr['CDELT2']
gal_hdr['CRVAL1'] = center.galactic.l.value
gal_hdr['CRVAL2'] = center.galactic.b.value
gal_hdr['NAXIS']  = 2
gal_hdr['NAXIS1'] = nx
gal_hdr['NAXIS2'] = ny
gal_wcs = WCS(gal_hdr)
print ('\nWCS of equivalent galactic frame:\n',gal_wcs)

# ---- PLOT

fig = plt.figure(figsize=(4,4))

ax = plt.subplot (projection=gal_wcs, label='galactic')
ax.coords.grid (True, color='gray',ls='solid')
ax.coords[0].set_axislabel (r'Galactic Longitude')
ax.coords[1].set_axislabel (r'Galactic Latitude')

overlay = ax.get_coords_overlay ('fk5')
overlay.grid (True, color='gray',ls='dotted')
overlay[0].set_axislabel (r'Right Ascension (J2000)')
overlay[1].set_axislabel (r'Declination (J2000)')

fk5gal = ax.get_transform(wcs)
ax.imshow (data,origin='lower',interpolation='nearest',transform=fk5gal)

# ---- SHOW RESULT

plt.xlim (right=0-0.2*nx,left=nx-1+0.2*nx)
plt.ylim (top=0-0.2*ny,bottom=ny-1+0.2*ny)
plt.show ()




More information about the AstroPy mailing list