def _check_transform(coords): # Coords is a shape.Subinterval wcsprm = Wcsprm() coord_array = np.array([[coords.lower, coords.upper]]) sky_transform = wcsprm.p2s(coord_array, ORIGIN) pix_transform = wcsprm.s2p(sky_transform['world'], ORIGIN) transformed_coords = pix_transform['pixcrd'] if not (transformed_coords[0][0] == coords.lower and transformed_coords[0][1] == coords.upper): raise ValueError( "Could not transform coordinates pixel to sky, sky to pixel")
def _check_transform(coords): # Coords is a shape.Subinterval wcsprm = Wcsprm() coord_array = np.array([[coords.lower, coords.upper]]) sky_transform = wcsprm.p2s(coord_array, ORIGIN) wcsprm.s2p(sky_transform['world'], ORIGIN)
def _check_transform(lower, upper): wcsprm = Wcsprm() coord_array = np.array([[lower, upper]]) sky_transform = wcsprm.p2s(coord_array, ORIGIN) wcsprm.s2p(sky_transform['world'], ORIGIN)
def add_wcs_coordinates(objects, catParNames, catParFormt, catParUnits, Parameters): try: hdulist = fits.open(Parameters["import"]["inFile"]) header = hdulist[0].header hdulist.close() # Fix headers where "per second" is written "/S" instead of "/s" # (assuming they mean "per second" and not "per Siemens"). if "cunit3" in header and "/S" in header["cunit3"]: err.warning("Converting '/S' to '/s' in CUNIT3.") header["cunit3"] = header["cunit3"].replace("/S","/s") # Check if there is a Nmap/GIPSY FITS header keyword value present gipsyKey = [k for k in ["FREQ-OHEL", "FREQ-OLSR", "FREQ-RHEL", "FREQ-RLSR"] if (k in [header[key] for key in header if ("CTYPE" in key)])] if gipsyKey: err.message("GIPSY header found. Trying to convert to FITS standard.") from astropy.wcs import Wcsprm header = fix_gipsy_header(header) wcsin = Wcsprm(str(header)) wcsin.sptr("VOPT-F2W") #if header["naxis"] == 4: # objects = np.concatenate((objects, wcsin.p2s(np.concatenate((objects[:, catParNames.index("x"):catParNames. index("x") + 3], np.zeros((objects.shape[0], 1))), axis=1), 0)["world"][:,:-1]), axis=1) #else: # objects = np.concatenate((objects, wcsin.p2s(objects[:, catParNames.index("x"):catParNames.index("x") + 3], 0)["world"]), axis=1) objects = np.concatenate((objects, wcsin.p2s(objects[:, catParNames.index("x"):catParNames.index("x") + 3], 0)["world"]), axis=1) catParUnits = tuple(list(catParUnits) + [str(cc).replace(" ", "") for cc in wcsin.cunit]) catParNames = tuple(list(catParNames) + [(cc.split("--")[0]).lower() for cc in wcsin.ctype]) catParFormt = tuple(list(catParFormt) + ["%15.7e", "%15.7e", "%15.7e"]) else: # Constrain the RA axis reference value CRVAL_ to be between 0 and 360 deg rafound = 0 for kk in range(header["naxis"]): if header["ctype1"][:2] == "RA": rafound = 1 break if rafound: if header["crval%i" % (kk + 1)] < 0: err.warning("Adding 360 deg to RA reference value.") header["crval%i" % (kk + 1)] += 360 elif header["crval%i" % (kk + 1)] > 360: err.warning("Subtracting 360 deg from RA reference value.") header["crval%i" % (kk + 1)] -= 360 #if header["naxis"] == 4: wcsin = wcs.WCS(header, naxis=[wcs.WCSSUB_CELESTIAL, wcs.WCSSUB_SPECTRAL, wcs.WCSSUB_STOKES]) #else: wcsin = wcs.WCS(header, naxis=[wcs.WCSSUB_CELESTIAL, wcs.WCSSUB_SPECTRAL]) wcsin = wcs.WCS(header, naxis=[wcs.WCSSUB_CELESTIAL, wcs.WCSSUB_SPECTRAL]) xyz = objects[:, catParNames.index("x"):catParNames.index("x") + 3].astype(float) if "cellscal" in header and header["cellscal"] == "1/F": err.warning( "CELLSCAL keyword with value of 1/F found.\n" "Will account for varying pixel scale in WCS coordinate calculation.") x0, y0 = header["crpix1"] - 1, header["crpix2"] - 1 # Will calculate the pixscale factor of each channel as: # pixscale = ref_frequency / frequency if header["ctype3"] == "VELO-HEL": pixscale = (1 - header["crval3"] / scipy.constants.c) / (1 - (((xyz[:, 2] + 1) - header["crpix3"]) * header["cdelt3"] + header["crval3"]) / scipy.constants.c) else: err.warning("Cannot convert 3rd axis coordinates to frequency. Ignoring the effect of CELLSCAL = 1/F.") pixscale = 1.0 xyz[:, 0] = (xyz[:, 0] - x0) * pixscale + x0 xyz[:, 1] = (xyz[:, 1] - y0) * pixscale + y0 #if header["naxis"] == 4: objects = np.concatenate((objects, wcsin.wcs_pix2world(np.concatenate((xyz, np.zeros((objects.shape[0], 1))), axis=1), 0)[:, :-1]), axis=1) #else: objects = np.concatenate((objects, wcsin.wcs_pix2world(xyz, 0)), axis=1) objects = np.concatenate((objects, wcsin.wcs_pix2world(xyz, 0)), axis=1) catParUnits = tuple(list(catParUnits) + [str(cc).replace(" ", "") for cc in wcsin.wcs.cunit]) catParNames = tuple(list(catParNames) + [(cc.split("--")[0]).lower() for cc in wcsin.wcs.ctype]) catParFormt = tuple(list(catParFormt) + ["%15.7e", "%15.7e", "%15.7e"]) #if header["naxis"] == 4: # catParUnits = catParUnits[:-1] # catParNames= catParNames[:-1] err.message("WCS coordinates added to catalogue.") # Create IAU-compliant source name: # WARNING: This currently assumes a regular, ≥ 2-dim. data cube where the first two axes are longitude and latitude. n_src = objects.shape[0] n_par = objects.shape[1] iau_names = np.empty([n_src, 1], dtype=object) if header["ctype1"][:4] == "RA--": # Equatorial coordinates; try to figure out equinox: iau_coord = "equ" if "equinox" in header: if int(header["equinox"]) >= 2000: iau_equinox = "J" else: iau_equinox = "B" elif "epoch" in header: # Assume that EPOCH has been abused to record the equinox: if int(header["epoch"]) >= 2000: iau_equinox = "J" else: iau_equinox = "B" else: # Equinox undefined: iau_equinox = "X" elif header["ctype1"][:4] == "GLON": # Galactic coordinates: iau_coord = "gal" iau_equinox = "G" else: # Unsupported coordinate system: iau_coord = "" iau_equinox = "" for src in xrange(n_src): lon = objects[src][n_par - 3] lat = objects[src][n_par - 2] if iau_coord == "equ": ra = Longitude(lon, unit=u.deg) dec = Latitude(lat, unit=u.deg) iau_pos = ra.to_string(unit=u.h, decimal=False, sep="", precision=2, alwayssign=False, pad=True, fields=3) iau_pos += dec.to_string(unit=u.deg, decimal=False, sep="", precision=1, alwayssign=True, pad=True, fields=3) else: iau_pos = "{0:08.4f}".format(lon) if lat < 0.0: iau_pos += "-" else: iau_pos += "+" iau_pos += "{0:07.4f}".format(abs(lat)) iau_names[src][0] = "SoFiA " + iau_equinox + iau_pos objects = np.concatenate((objects, iau_names), axis = 1) catParUnits = tuple(list(catParUnits) + ["-"]) catParNames = tuple(list(catParNames) + ["name"]) catParFormt = tuple(list(catParFormt) + ["%30s"]) except: err.warning("WCS conversion of parameters failed.") return (objects, catParNames, catParFormt, catParUnits)
def add_wcs_coordinates(objects, catParNames, catParFormt, catParUnits, Parameters): try: hdulist = fits.open(Parameters["import"]["inFile"]) header = hdulist[0].header hdulist.close() # Fix headers where "per second" is written "/S" instead of "/s" # (assuming they mean "per second" and not "per Siemens"). if "cunit3" in header and "/S" in header["cunit3"]: err.warning("Converting '/S' to '/s' in CUNIT3.") header["cunit3"] = header["cunit3"].replace("/S", "/s") # Check if there is a Nmap/GIPSY FITS header keyword value present gipsyKey = [ k for k in ["FREQ-OHEL", "FREQ-OLSR", "FREQ-RHEL", "FREQ-RLSR"] if (k in [header[key] for key in header if ("CTYPE" in key)]) ] if gipsyKey: err.message( "GIPSY header found. Trying to convert to FITS standard.") from astropy.wcs import Wcsprm header = fix_gipsy_header(header) wcsin = Wcsprm(str(header)) wcsin.sptr("VOPT-F2W") #if header["naxis"] == 4: # objects = np.concatenate((objects, wcsin.p2s(np.concatenate((objects[:, catParNames.index("x"):catParNames. index("x") + 3], np.zeros((objects.shape[0], 1))), axis=1), 0)["world"][:,:-1]), axis=1) #else: # objects = np.concatenate((objects, wcsin.p2s(objects[:, catParNames.index("x"):catParNames.index("x") + 3], 0)["world"]), axis=1) objects = np.concatenate( (objects, wcsin.p2s( objects[:, catParNames.index("x"):catParNames.index("x") + 3], 0)["world"]), axis=1) catParUnits = tuple( list(catParUnits) + [str(cc).replace(" ", "") for cc in wcsin.cunit]) catParNames = tuple( list(catParNames) + [(cc.split("--")[0]).lower() for cc in wcsin.ctype]) catParFormt = tuple( list(catParFormt) + ["%15.7e", "%15.7e", "%15.7e"]) else: # Constrain the RA axis reference value CRVAL_ to be between 0 and 360 deg rafound = 0 for kk in range(header["naxis"]): if header["ctype1"][:2] == "RA": rafound = 1 break if rafound: if header["crval%i" % (kk + 1)] < 0: err.warning("Adding 360 deg to RA reference value.") header["crval%i" % (kk + 1)] += 360 elif header["crval%i" % (kk + 1)] > 360: err.warning("Subtracting 360 deg from RA reference value.") header["crval%i" % (kk + 1)] -= 360 #if header["naxis"] == 4: wcsin = wcs.WCS(header, naxis=[wcs.WCSSUB_CELESTIAL, wcs.WCSSUB_SPECTRAL, wcs.WCSSUB_STOKES]) #else: wcsin = wcs.WCS(header, naxis=[wcs.WCSSUB_CELESTIAL, wcs.WCSSUB_SPECTRAL]) wcsin = wcs.WCS(header, naxis=[wcs.WCSSUB_CELESTIAL, wcs.WCSSUB_SPECTRAL]) xyz = objects[:, catParNames.index("x"):catParNames.index("x") + 3].astype(float) if "cellscal" in header and header["cellscal"] == "1/F": err.warning( "CELLSCAL keyword with value of 1/F found.\n" "Will account for varying pixel scale in WCS coordinate calculation." ) x0, y0 = header["crpix1"] - 1, header["crpix2"] - 1 # Will calculate the pixscale factor of each channel as: # pixscale = ref_frequency / frequency if header["ctype3"] == "VELO-HEL": pixscale = (1 - header["crval3"] / scipy.constants.c) / ( 1 - (((xyz[:, 2] + 1) - header["crpix3"]) * header["cdelt3"] + header["crval3"]) / scipy.constants.c) else: err.warning( "Cannot convert 3rd axis coordinates to frequency. Ignoring the effect of CELLSCAL = 1/F." ) pixscale = 1.0 xyz[:, 0] = (xyz[:, 0] - x0) * pixscale + x0 xyz[:, 1] = (xyz[:, 1] - y0) * pixscale + y0 #if header["naxis"] == 4: objects = np.concatenate((objects, wcsin.wcs_pix2world(np.concatenate((xyz, np.zeros((objects.shape[0], 1))), axis=1), 0)[:, :-1]), axis=1) #else: objects = np.concatenate((objects, wcsin.wcs_pix2world(xyz, 0)), axis=1) objects = np.concatenate((objects, wcsin.wcs_pix2world(xyz, 0)), axis=1) catParUnits = tuple( list(catParUnits) + [str(cc).replace(" ", "") for cc in wcsin.wcs.cunit]) catParNames = tuple( list(catParNames) + [(cc.split("--")[0]).lower() for cc in wcsin.wcs.ctype]) catParFormt = tuple( list(catParFormt) + ["%15.7e", "%15.7e", "%15.7e"]) #if header["naxis"] == 4: # catParUnits = catParUnits[:-1] # catParNames= catParNames[:-1] err.message("WCS coordinates added to catalogue.") # Create IAU-compliant source name: # WARNING: This currently assumes a regular, ≥ 2-dim. data cube where the first two axes are longitude and latitude. n_src = objects.shape[0] n_par = objects.shape[1] iau_names = np.empty([n_src, 1], dtype=object) if header["ctype1"][:4] == "RA--": # Equatorial coordinates; try to figure out equinox: iau_coord = "equ" if "equinox" in header: if int(header["equinox"]) >= 2000: iau_equinox = "J" else: iau_equinox = "B" elif "epoch" in header: # Assume that EPOCH has been abused to record the equinox: if int(header["epoch"]) >= 2000: iau_equinox = "J" else: iau_equinox = "B" else: # Equinox undefined: iau_equinox = "X" elif header["ctype1"][:4] == "GLON": # Galactic coordinates: iau_coord = "gal" iau_equinox = "G" else: # Unsupported coordinate system: iau_coord = "" iau_equinox = "" for src in xrange(n_src): lon = objects[src][n_par - 3] lat = objects[src][n_par - 2] if iau_coord == "equ": ra = Longitude(lon, unit=u.deg) dec = Latitude(lat, unit=u.deg) iau_pos = ra.to_string(unit=u.h, decimal=False, sep="", precision=2, alwayssign=False, pad=True, fields=3) iau_pos += dec.to_string(unit=u.deg, decimal=False, sep="", precision=1, alwayssign=True, pad=True, fields=3) else: iau_pos = "{0:08.4f}".format(lon) if lat < 0.0: iau_pos += "-" else: iau_pos += "+" iau_pos += "{0:07.4f}".format(abs(lat)) iau_names[src][0] = "SoFiA " + iau_equinox + iau_pos objects = np.concatenate((objects, iau_names), axis=1) catParUnits = tuple(list(catParUnits) + ["-"]) catParNames = tuple(list(catParNames) + ["name"]) catParFormt = tuple(list(catParFormt) + ["%30s"]) except: err.warning("WCS conversion of parameters failed.") return (objects, catParNames, catParFormt, catParUnits)
def addpv(header, order=5, radial=False, ndata=64): logger = logging.getLogger(__name__) if isinstance(ndata, (tuple, list)): ndata = dict(x=ndata[0], y=ndata[1]) elif not isinstance(ndata, dict): ndata = dict(x=ndata, y=ndata) header = header.copy() crpix = dict(x=header['crpix1'], y=header['crpix2']) shape = dict(x=header['naxis1'], y=header['naxis2']) crval = dict(x=header['crval1'], y=header['crval2']) with warnings.catch_warnings(): warnings.filterwarnings( 'ignore', category=FITSFixedWarning, message="Removed redundant " "SCAMP distortion parameters because SIP parameters " "are also present") wcs = WCS(header) start, step = {}, {} if shape['x'] >= ndata['x']: step['x'] = shape['x'] // ndata['x'] start['x'] = (shape['x'] % ndata['x']) // 2 else: step['x'] = 1 start['x'] = 0 if shape['y'] >= ndata['y']: step['y'] = shape['y'] // ndata['y'] start['y'] = (shape['y'] % ndata['y']) // 2 else: step['y'] = 1 start['y'] = 0 xpix, ypix = np.mgrid[start['x']:shape['x']:step['x'], start['y']:shape['y']:step['y']] px = xpix.ravel() py = ypix.ravel() ra, dec = wcs.all_pix2world(px, py, 1) mask = slice(55, 60) ra, dec = wcs.wcs_pix2world(px, py, 1) cd = wcs.pixel_scale_matrix dpx = px - crpix['x'] dpy = py - crpix['y'] x = cd[0, 0] * dpx + cd[0, 1] * dpy y = cd[1, 0] * dpx + cd[1, 1] * dpy r = np.sqrt(x * x + y * y) phi = np.arctan2(x, -y) theta = np.arctan(180 / np.pi / r) raref = np.radians(crval['x']) decref = np.radians(crval['y']) arg = np.sin(theta) * np.sin(decref) - np.cos(theta) * np.cos( phi) * np.cos(decref) dec = np.arcsin(arg) arg = np.cos(theta) * np.sin(phi) / np.cos(dec) ra = np.arcsin(arg) ra += raref dec = np.degrees(dec) ra = np.degrees(ra) # Set up the args for minimization tra, tdec = wcs.all_pix2world(px, py, 1) tra, tdec = np.radians(tra), np.radians(tdec) p = np.zeros(2 * 40) p[1] = 1 p[41] = 1 pv = p.reshape(2, -1) pixels = np.asarray([px, py]) wcspv = WCSParam() wcspv.cd = wcs.wcs.cd.copy() wcspv.crpix = wcs.wcs.crpix.copy() wcspv.crval = np.radians(wcs.wcs.crval) ra, dec = wcs2pv(pv, pixels, wcspv) args = tra, tdec, pixels, wcspv, order, radial before = error(p, *args) results = minimize(error, p, args) pv = results['x'].reshape(2, -1) after = error(results['x'], *args) pv[np.abs(pv) < np.finfo(pv.dtype).tiny] = 0.0 logger.debug("New PV keywords:") for i in range(pv.shape[-1]): if pv[0, i] != 0.0: key = "PV1_{i:d}".format(i=i) header[key] = pv[0, i] logger.debug("%s = %f", key, pv[0, i]) if pv[1, i] != 0.0: key = "PV2_{i:d}".format(i=i) header[key] = pv[1, i] logger.debug("%s = %f", key, pv[0, i]) fraction = before / after logger.info("Difference improvement = %.5e / %.5e = %.2f%%", before, after, 100 * fraction) return header
def add_wcs_coordinates(objects,catParNames,catParFormt,catParUnits,Parameters): import imp import sys import numpy as np c = 299792458.0 # speed of light in m/s try: imp.find_module('astropy') found = True except ImportError: found = False if found: try: from astropy import wcs from astropy.io import fits hdulist = fits.open(Parameters['import']['inFile']) header = hdulist[0].header hdulist.close() # Fix headers where "per second" is written "/S" instead of "/s" # (assuming they mean "per second" and not "per Siemens"). if 'cunit3' in header and '/S' in header['cunit3']: sys.stderr.write('WARNING: Converting "/S" to "/s" in CUNIT3.\n') header['cunit3']=header['cunit3'].replace('/S','/s') ## check if there is a Nmap/GIPSY FITS header keyword value present gipsyKey = [k for k in ['FREQ-OHEL','FREQ-OLSR','FREQ-RHEL','FREQ-RLSR'] if (k in [header[key] for key in header if ('CTYPE' in key)])] if gipsyKey: print ('GIPSY header found. Trying to convert it.') from astropy.wcs import Wcsprm header = fix_gipsy_header(header) wcsin = Wcsprm(str(header)) wcsin.sptr('VOPT-F2W') #if header['naxis'] == 4: # objects = np.concatenate((objects, wcsin.p2s(np.concatenate((objects[:, catParNames.index('x'):catParNames.index('x')+3], np.zeros((objects.shape[0], 1))), axis=1),0)['world'][:,:-1]), axis=1) #else: # objects = np.concatenate((objects, wcsin.p2s(objects[:, catParNames.index('x'):catParNames.index('x')+3], 0)['world']), axis=1) objects = np.concatenate((objects, wcsin.p2s(objects[:, catParNames.index('x'):catParNames.index('x')+3], 0)['world']), axis=1) catParUnits = tuple(list(catParUnits) + [str(cc).replace(' ','') for cc in wcsin.cunit]) catParNames = tuple(list(catParNames) + [(cc.split('--')[0]).lower() for cc in wcsin.ctype]) catParFormt = tuple(list(catParFormt) + ['%15.7e', '%15.7e', '%15.7e']) else: # constrain the RA axis reference value CRVAL_ to be between 0 and 360 deg rafound = 0 for kk in range(header['naxis']): if header['ctype1'][:2] == 'RA': rafound = 1 break if rafound: if header['crval%i'%(kk + 1)] < 0: sys.stderr.write("WARNING: adding 360 deg to RA reference value.\n") header['crval%i'%(kk + 1)] += 360 elif header['crval%i'%(kk + 1)] > 360: sys.stderr.write("WARNING: subtracting 360 deg from RA reference value.\n") header['crval%i'%(kk + 1)] -= 360 #if header['naxis'] == 4: wcsin = wcs.WCS(header, naxis=[wcs.WCSSUB_CELESTIAL, wcs.WCSSUB_SPECTRAL,wcs.WCSSUB_STOKES]) #else: wcsin = wcs.WCS(header, naxis=[wcs.WCSSUB_CELESTIAL, wcs.WCSSUB_SPECTRAL]) wcsin = wcs.WCS(header, naxis=[wcs.WCSSUB_CELESTIAL, wcs.WCSSUB_SPECTRAL]) xyz = objects[:,catParNames.index('x'):catParNames.index('x')+3].astype(float) if 'cellscal' in header and header['cellscal'] == '1/F': sys.stderr.write('WARNING: CELLSCAL keyword with value 1/F found.\n') sys.stderr.write(' Will account for varying pixel scale in WCS coordinate calculation.\n') x0, y0 = header['crpix1'] - 1, header['crpix2'] - 1 # Will calculate the pixscale factor of each channel as: # pixscale = ref_frequency / frequency if header['ctype3'] == 'VELO-HEL': pixscale = (1 - header['crval3'] / c) / (1 - (((xyz[:,2] + 1) - header['crpix3']) * header['cdelt3'] + header['crval3']) / c) else: sys.stderr.write("WARNING: Cannot convert 3rd axis coordinates to frequency. Will ignore the effect of CELLSCAL = 1/F.\n") pixscale = 1. xyz[:,0] = (xyz[:,0] - x0) * pixscale + x0 xyz[:,1] = (xyz[:,1] - y0) * pixscale + y0 #if header['naxis'] == 4: objects = np.concatenate((objects, wcsin.wcs_pix2world(np.concatenate((xyz, np.zeros((objects.shape[0], 1))), axis=1), 0)[:,:-1]), axis=1) #else: objects = np.concatenate((objects, wcsin.wcs_pix2world(xyz, 0)), axis=1) objects = np.concatenate((objects, wcsin.wcs_pix2world(xyz, 0)), axis=1) catParUnits = tuple(list(catParUnits) + [str(cc).replace(' ','') for cc in wcsin.wcs.cunit]) catParNames = tuple(list(catParNames) + [(cc.split('--')[0]).lower() for cc in wcsin.wcs.ctype]) catParFormt = tuple(list(catParFormt) + ['%15.7e', '%15.7e', '%15.7e']) #if header['naxis'] == 4: # catParUnits = catParUnits[:-1] # catParNames= catParNames[:-1] print ('WCS coordinates added to catalogue.') # Create IAU-compliant source name: # WARNING: This currently assumes a regular, ≥ 2-dim. data cube where the first two axes are longitude and latitude. n_src = objects.shape[0] n_par = objects.shape[1] iau_names = np.empty([n_src, 1], dtype=object) if header["ctype1"][:4] == "RA--": # Equatorial coordinates try to figure out equinox: iau_coord = "equ" if "equinox" in header: if int(header["equinox"]) >= 2000: iau_equinox = "J" else: iau_equinox = "B" elif "epoch" in header: # Assume that EPOCH has been abused to record the equinox: if int(header["epoch"]) >= 2000: iau_equinox = "J" else: iau_equinox = "B" else: # Equinox undefined: iau_equinox = "X" elif header["ctype1"][:4] == "GLON": # Galactic coordinates: iau_coord = "gal" iau_equinox = "G" else: # Unsupported coordinate system: iau_coord = "" iau_equinox = "" for src in xrange(n_src): lon = objects[src][n_par - 3] lat = objects[src][n_par - 2] if iau_coord == "equ": ra = lon / 15.0 # convert assumed degrees to hours ra_h = int(ra) ra_m = int((ra - ra_h) * 60.0) ra_s = (ra - ra_h - (ra_m / 60.0)) * 3600.0 dec_sgn = np.sign(lat) dec = abs(lat) dec_d = int(dec) dec_m = int((dec - dec_d) * 60.0) dec_s = (dec - dec_d - (dec_m / 60.0)) * 3600.0 iau_pos = "{0:02d}{1:02d}{2:05.2f}".format(ra_h, ra_m, ra_s) if dec_sgn < 0: iau_pos += "-" else: iau_pos += "+" iau_pos += "{0:02d}{1:02d}{2:04.1f}".format(dec_d, dec_m, dec_s) else: iau_pos = "{0:08.4f}".format(lon) if lat < 0.0: iau_pos += "-" else: iau_pos += "+" iau_pos += "{0:07.4f}".format(abs(lat)) iau_names[src][0] = "SoFiA " + iau_equinox + iau_pos objects = np.concatenate((objects, iau_names), axis = 1) catParUnits = tuple(list(catParUnits) + ["-"]) catParNames = tuple(list(catParNames) + ["name"]) catParFormt = tuple(list(catParFormt) + ["%30s"]) except: sys.stderr.write("WARNING: WCS conversion of parameters failed.\n") return(objects, catParNames, catParFormt, catParUnits)
def add_wcs_coordinates(objects,catParNames,catParFormt,catParUnits,Parameters): import imp import sys import numpy as np try: imp.find_module('astropy') found = True except ImportError: found = False if found: try: from astropy import wcs from astropy.io import fits hdulist = fits.open(Parameters['import']['inFile']) header = hdulist[0].header hdulist.close() ## check if there is a Nmap/GIPSY FITS header keyword value present gipsyKey = [k for k in ['FREQ-OHEL','FREQ-OLSR','FREQ-RHEL','FREQ-RLSR'] if (k in [header[key] for key in header if ('CTYPE' in key)])] if gipsyKey: print 'GIPSY header found. Trying to convert it.' from astropy.wcs import Wcsprm header = fix_gipsy_header(header) wcsin = Wcsprm(str(header)) wcsin.sptr('VOPT-F2W') if header['naxis']==4: objects = np.concatenate((objects,wcsin.p2s(np.concatenate((objects[:,catParNames.index('Xm'):catParNames.index('Xm')+3],np.zeros((objects.shape[0],1))),axis=1),0)['world'][:,:-1]),axis=1) else: objects = np.concatenate((objects,wcsin.p2s(objects[:,catParNames.index('Xm'):catParNames.index('Xm')+3],0)['world']),axis=1) catParUnits = tuple(list(catParUnits) + [str(cc).replace(' ','') for cc in wcsin.cunit]) catParNames = tuple(list(catParNames) + [cc.split('--')[0]+'m' for cc in wcsin.ctype]) catParFormt = tuple(list(catParFormt) + ['%15.7e', '%15.7e', '%15.7e']) else: # constrain the RA axis reference value CRVAL_ to be between 0 and 360 deg rafound=0 for kk in range(header['naxis']): if header['ctype1'][:2]=='RA': rafound=1 break if rafound: if header['crval%i'%(kk+1)]<0: print 'WARNING: adding 360 deg to RA reference value' header['crval%i'%(kk+1)]+=360 elif header['crval%i'%(kk+1)]>360: print 'WARNING: subtracting 360 deg from RA reference value' header['crval%i'%(kk+1)]-=360 wcsin = wcs.WCS(header) if header['naxis']==4: objects=np.concatenate((objects,wcsin.wcs_pix2world(np.concatenate((objects[:,catParNames.index('Xm'):catParNames.index('Xm')+3],np.zeros((objects.shape[0],1))),axis=1),0)[:,:-1]),axis=1) else: objects=np.concatenate((objects,wcsin.wcs_pix2world(objects[:,catParNames.index('Xm'):catParNames.index('Xm')+3],0)),axis=1) catParUnits = tuple(list(catParUnits) + [str(cc).replace(' ','') for cc in wcsin.wcs.cunit]) catParNames = tuple(list(catParNames) + [cc.split('--')[0]+'m' for cc in wcsin.wcs.ctype]) catParFormt = tuple(list(catParFormt) + ['%15.7e', '%15.7e', '%15.7e']) if header['naxis']==4: catParUnits = catParUnits[:-1] catParNames= catParNames[:-1] print "WCS coordinates added to the catalog." except: print "WARNING: WCS conversion of parameters could not be executed!\n" return(objects, catParNames, catParFormt, catParUnits)
def main(): """Perform photometry for the given file.""" print("Program version: 0.2") StartTime = datetime.now() args = parseArguments() if (args.ignore_warnings): warnings.simplefilter('ignore', UserWarning) fits_image_filenames = args.input #print(fits_image_filenames) #for directories search for appropriate fits files if (os.path.isdir(fits_image_filenames[0])): print( "detected a directory. Will search for fits files in it that already have astrometry calibration and therefor contain _astro" ) path = fits_image_filenames[0] fits_image_filenames = [] for file in os.listdir(path): if file.endswith(".fits") and "_astro" in file: fits_image_filenames.append(path + "/" + file) print(fits_image_filenames) for fits_image_filename in fits_image_filenames: print("") print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>") print("> Photometry for {} ".format(fits_image_filename)) with fits.open(fits_image_filename) as hdul: #print(hdul.info()) if (args.verbose): print( "if image is not at first position in the fits file the program will break later on" ) #print(hdul[0].header) hdu = hdul[0] hdr = hdu.header image_or = hdul[0].data.astype(float) image = image_or - np.median(image_or) wcsprm = Wcsprm(hdr.tostring().encode( 'utf-8')) #everything else gave me errors with python 3 #tranlating aperture into pixel: on_sky = wcsprm.p2s([[0, 0], [1, 1]], 0)["world"] px_scale = np.sqrt((on_sky[0, 0] - on_sky[1, 0])**2 + (on_sky[0, 1] - on_sky[1, 1])**2) px_scale = px_scale * 60 * 60 #in arcsec aperture = args.aperture / px_scale #aperture n pixel print("aperture") observation = find_sources(image, aperture) #print(observation) positions = (observation['xcenter'], observation['ycenter']) apertures = CircularAperture(positions, r=4.) #get rough coordinates coord = SkyCoord(wcsprm.crval[0], wcsprm.crval[1], unit=(u.deg, u.deg), frame="icrs") #put in nice wrapper! with repeated tries and maybe try synchron! print(">Dowloading catalog data") #WCS.calc_footprint(header=None, undistort=True, axes=None, center=True) radius = u.Quantity(5, u.arcmin) #should be enough for all images catalog_data, band_name, catalog_name, mag_sys = query.get_photometry_data( coord, radius, args.band, args.catalog) #throwing out blended sources (should be improved, TODO) apertures_catalog = CircularAperture(wcsprm.s2p( catalog_data[["ra", "dec"]], 1)['pixcrd'], r=5.) obs_matched, cat_matched, distances = register.find_matches_keep_catalog_info( observation, catalog_data, wcsprm, threshold=3) print("Found {} matches".format(obs_matched.shape[0])) MAG_CALC = True if (obs_matched.shape[0] == 0): MAG_CALC = False obs_matched["aperture_sum"] cat_matched[band_name] #mag = -2.5 log10(cts) + ZP ZP = -1 * (-2.5 * np.log10(obs_matched["aperture_sum"].values) - cat_matched[band_name].values) #FIGURE OUT LINEAR RANGE TOTO ZP_median = np.median(ZP[~np.isnan(ZP)]) new_magnitudes = -2.5 * np.log10( observation["aperture_sum"].values) + ZP_median ra = args.ra dec = args.dec if (args.name): targets = pd.read_csv("targets.csv") if ((targets["NAME"] == args.name).sum()): ra = targets.loc[targets["NAME"] == args.name, "RA"].values[0] dec = targets.loc[targets["NAME"] == args.name, "DEC"].values[0] print("For {} the following coordinates where found".format( args.name)) print("ra: {} dec: {}".format(ra, dec)) else: print("{} not found".format(args.name)) if (ra and dec): c_unknown = SkyCoord(ra, dec, unit=(u.deg, u.deg), frame="icrs") #print(observation[["xcenter", "ycenter"]].values) coordinats_obs = wcsprm.p2s( observation[["xcenter", "ycenter"]].values, 1)["world"] #print(coordinats_obs[17]) c_obs = SkyCoord(coordinats_obs[:, 0], coordinats_obs[:, 1], unit=(u.deg, u.deg), frame="icrs") #print(coordinats_obs) from astropy.coordinates import match_coordinates_sky idx, d2d, d3d = match_coordinates_sky(c_unknown, c_obs) cand_dups = d2d < 5 * u.arcsec mean, median, std = sigma_clipped_stats(image, sigma=3.0) ap_area = CircularAperture((2, 2), r=aperture) sig3_limiting_mag = -2.5 * np.log10( 3 * std * np.sqrt(ap_area.area())) + ZP_median sig5_limiting_mag = -2.5 * np.log10( 5 * std * np.sqrt(ap_area.area())) + ZP_median ####################### #estimating the error via random apertures # print(std*np.sqrt(aperture_obj.area())) ran_x = 2 * aperture + np.random.random(1000) * (image.shape[0] - 4 * aperture) ran_y = 2 * aperture + np.random.random(1000) * (image.shape[0] - 4 * aperture) apertures_error = CircularAperture((ran_x, ran_y), r=aperture) phot_table = aperture_photometry(image, apertures_error) phot_table[ 'aperture_sum'].info.format = '%.8g' # for consistent table output random_extractions = Table(phot_table).to_pandas() #print(np.mean(np.abs(random_extractions["aperture_sum"]))) # print(np.median(np.abs(random_extractions["aperture_sum"]))) # sig5_limiting_mag_apertures = -2.5*np.log10(5*np.median(np.abs(random_extractions["aperture_sum"]))) + ZP_median # print(sig5_limiting_mag_apertures) m, _, std_apertures = sigma_clipped_stats( random_extractions["aperture_sum"], sigma=3) print(std_apertures) sig5_limiting_mag_apertures = -2.5 * np.log10( 5 * std_apertures) + ZP_median # print(sig5_limiting_mag_apertures) # print(np.mean(random_extractions.loc[random_extractions["aperture_sum"]<0,"aperture_sum"])) ########################### mag = 0 if (cand_dups.sum() > 0): print( "Position was given. Found {} sources in a 5 arcsec radius. Here is the magnitude:" .format(cand_dups.sum())) print(new_magnitudes[idx]) print("----") aperture_obj = CircularAperture( ((observation["xcenter"].values)[idx], (observation["ycenter"].values)[idx]), r=aperture) #noise = (observation["aperture_sum"].values)[idx] /(std*np.sqrt(aperture_obj.area())) #https://en.wikipedia.org/wiki/Sum_of_normally_distributed_random_variables #the std**2 is added, so we get a square root for the area! noise = ( observation["aperture_sum"].values)[idx] / std_apertures #print(noise_prob_wrong) #print(noise_prob_wrong2) print("We get a signal to noise of {} for the fixed aperture". format(noise)) mag = new_magnitudes[idx] mag_err = 2.5 * np.log10(1 + 1 / noise) text = "found detection in {} band,\n {:.4g} +- {:.2g} {}mag, {:.3g} S/N \n 5 sig limiting mag is {:.4g}".format( args.band, mag, mag_err, mag_sys, noise, sig5_limiting_mag_apertures) else: print( "Position was given. No object was found at that position." ) pix_unknown = wcsprm.s2p([[ra, dec]], 1) pix_unknown = pix_unknown['pixcrd'] aperture_obj = CircularAperture(pix_unknown, r=aperture) phot_table = aperture_photometry(image, aperture_obj) phot_table[ 'aperture_sum'].info.format = '%.8g' # for consistent table output #print(phot_table) forced_phot = Table(phot_table).to_pandas() forced_mag = -2.5 * np.log10( forced_phot["aperture_sum"].values) + ZP_median forced_mag = forced_mag[0] #noise = (forced_phot["aperture_sum"].values)[0] /(std*np.sqrt(aperture_obj.area())) noise = (forced_phot["aperture_sum"].values)[0] / std_apertures print("Forced photometry gives a magnitude of {}".format( forced_mag)) #local error # pix_unknown = pix_unknown[0] # mean_loc, median_loc, std_loc = sigma_clipped_stats(image[int(pix_unknown[0])-50:int(pix_unknown[0])+50,int(pix_unknown[1])-50:int(pix_unknown[1])+50], sigma=3.0) # ap_area= CircularAperture((2,2), r=aperture) # sig5_limiting_mag_local = -2.5*np.log10(5*std_loc*np.sqrt(ap_area.area())) + ZP_median # print((std_loc*np.sqrt(aperture_obj.area()))) # print(sig5_limiting_mag_local) print("We get a signal to noise of {} for the fixed aperture". format(noise)) mag = forced_mag mag_err = 2.5 * np.log10(1 + 1 / noise) text = "forced photometry in {} band,\n {:.4g} +- {:.2g} {}mag, {:.3g} S/N \n 5 sig limiting mag is {:.4g} ".format( args.band, mag, mag_err, mag_sys, noise, sig5_limiting_mag_apertures) else: print(new_magnitudes) mag = 0 obs_with_photometry = observation.copy() obs_with_photometry["mag"] = new_magnitudes #search for targeted object and print out its magnitude TODO if (ra and dec): plt.figure(figsize=(15, 8)) plt.subplot(1, 2, 1) pix_unknown = wcsprm.s2p([[ra, dec]], 1) pix_unknown = pix_unknown['pixcrd'] pix_unknown = pix_unknown[0] plt.xlabel("pixel x direction") plt.ylabel("pixel y direction") plt.title(text, size=20) #plt.imshow(image[int( pix_unknown[0])-30: int(pix_unknown[0])+30, int(pix_unknown[1])-30:int(pix_unknown[1])+30],cmap='Greys', origin='lower', norm=LogNorm()) #plt.plot(30, 30, "+", color="red", markersize=20) plt.imshow(image, cmap='Greys', origin='lower', norm=LogNorm()) plt.xlim(int(pix_unknown[0]) - 20, int(pix_unknown[0]) + 20) plt.ylim(int(pix_unknown[1]) - 20, int(pix_unknown[1]) + 20) aperture_obj.plot(color='blue', lw=1.5, alpha=0.5, label=str(args.aperture) + " arcsec aperture") plt.plot(pix_unknown[0], pix_unknown[1], "+", color="red", markersize=20, linewidth=10, label="predicted target position") plt.legend(bbox_to_anchor=(1.1, -0.1), ncol=2) plt.subplot(1, 2, 2) plt.title("Photometric calibration with {} sources from {}".format( obs_matched.shape[0], catalog_name)) plt.plot(cat_matched[band_name].values, ZP, "o", markersize=20, label="catalog objects") if (MAG_CALC): plt.ylim(np.nanmin(ZP) - 1, np.nanmax(ZP) + 1) else: plt.ylim(10, 30) if (mag): plt.axvline(x=mag, linewidth=4, color="green", label="target") plt.xlabel("catalog " + band_name + " magnitude") plt.ylabel( "Zeropoint (should be constant in linear part of the detector)" ) plt.legend() outputname = fits_image_filename.replace('.fits', '') plt.savefig(outputname + "_ra{}dec{}.pdf".format(ra, dec)) plt.show() else: plt.figure() plt.plot(cat_matched[band_name].values, ZP, "o", markersize=20, label="catalog objects") plt.ylim(np.nanmin(ZP) - 1, np.nanmax(ZP) + 1) if (mag): plt.axvline(x=mag, linewidth=4, color="green", label="target") plt.xlabel("catalog " + band_name + " magnitude") plt.ylabel( "Zeropoint (should be constant in linear part of the detector)" ) plt.legend() plt.show() print("overall time taken") print(datetime.now() - StartTime) # if(args.show_images): # plt.show() print("-- finished --")
def galaxy_mask(header,leda): ra, dec, theta, diam, ba = leda sz = (header["NAXIS1"], header["NAXIS2"]) outmsk = np.zeros((sz[0],sz[1]),dtype=bool) w = WCS(header) ##CCD corners (these are actually in the header for DECam) xc = [0,0,sz[0]-1,sz[0]-1] yc = [0,sz[1]-1,0,sz[1]-1] xcen = (sz[0]-1)/2 ycen = (sz[1]-1)/2 c = w.pixel_to_world(xcen,ycen) sep = c.separation(w.pixel_to_world(xc, yc)) racen = c.ra.degree deccen = c.dec.degree dac = np.max(sep.degree) coordleda = SkyCoord(ra,dec,frame='icrs',unit='deg') dangle = c.separation(coordleda).degree keep = dangle < dac + diam #filter out galaxies with too large angle displace to consider if sum(keep) == 0: return outmsk relevant = w.world_to_pixel(coordleda[keep]) with warnings.catch_warnings(): warnings.simplefilter('ignore', category=(AstropyWarning,RuntimeWarning)) wperm = Wcsprm(header=header.tostring().encode('utf-8')) cd = wperm.cd #* wperm.cdelt[np.mod(np.linspace(0,3,4,dtype=int),2).reshape(2,2)] pscl = np.mean(np.sqrt(np.sum(cd**2,axis=1)))*3600 #convert back to arcsec from deg for interp msz = np.ceil(3600*diam[keep]/pscl).astype(int) #this is the mask size in the pixel scale msz += np.mod(msz,2) == 0 #adjust even size to be odd so that the center means something mh = (msz-1)//2 #I think this -1 is necessary becase we are all odd by construction ix = np.round(relevant[0]).astype(int) iy = np.round(relevant[1]).astype(int) #largest circle approx must fall in ccd insideim = ((ix+mh) >= 0) & ((ix-mh) < sz[0]) & ((iy+mh) >= 0) & (iy-mh < sz[1]) nim = sum(insideim) #filter out galaxy with no overlap if nim == 0: return outmsk rainl = coordleda[keep][insideim].ra.degree decinl = coordleda[keep][insideim].dec.degree thetal = np.deg2rad(theta[keep][insideim]) print("Masked Galaxies: %d" % nim) for i in range(0,nim): un, ue = tan_unit_vectors(rainl[i],decinl[i],racen,deccen) ueofn = un*np.cos(thetal[i])+ue*np.sin(thetal[i]) angle = np.rad2deg(np.arctan2(ueofn[1],ueofn[0])) ixn = ix[insideim][i] iyn = iy[insideim][i] mhn = mh[insideim][i] mszn = msz[insideim][i] e = Ellipse2D(x_0=mhn, y_0=mhn, a=mhn, b=ba[keep][insideim][i]*mhn, theta=np.deg2rad(angle)) y, x = np.mgrid[0:mszn, 0:mszn] smsk = (e(x, y) == 1) outmsk[np.clip(ixn-mhn,a_min=0,a_max=None):np.clip(ixn+mhn+1,a_min=None,a_max=sz[0]), np.clip(iyn-mhn,a_min=0,a_max=None):np.clip(iyn+mhn+1,a_min=None,a_max=sz[1])] |= smsk[ np.clip(mhn-ixn,a_min=0,a_max=None):np.clip(sz[0]-ixn+mhn,a_min=None,a_max=mszn), np.clip(mhn-iyn,a_min=0,a_max=None):np.clip(sz[1]-iyn+mhn,a_min=None,a_max=mszn)] return outmsk.T #revist need for transpose
def add_wcs_coordinates(objects,catParNames,catParFormt,catParUnits,Parameters): import imp import sys import numpy as np try: imp.find_module('astropy') found = True except ImportError: found = False if found: try: from astropy import wcs from astropy.io import fits hdulist = fits.open(Parameters['import']['inFile']) header = hdulist[0].header hdulist.close() # Fix headers where "per second" is written "/S" instead of "/s" # (assuming they mean "per second" and not "per Siemens"). if 'cunit3' in header and '/S' in header['cunit3']: print 'WARNING: Converting "/S" to "/s" in CUNIT3.' header['cunit3']=header['cunit3'].replace('/S','/s') ## check if there is a Nmap/GIPSY FITS header keyword value present gipsyKey = [k for k in ['FREQ-OHEL','FREQ-OLSR','FREQ-RHEL','FREQ-RLSR'] if (k in [header[key] for key in header if ('CTYPE' in key)])] if gipsyKey: print 'GIPSY header found. Trying to convert it.' from astropy.wcs import Wcsprm header = fix_gipsy_header(header) wcsin = Wcsprm(str(header)) wcsin.sptr('VOPT-F2W') if header['naxis']==4: objects = np.concatenate((objects,wcsin.p2s(np.concatenate((objects[:,catParNames.index('x'):catParNames.index('x')+3],np.zeros((objects.shape[0],1))),axis=1),0)['world'][:,:-1]),axis=1) else: objects = np.concatenate((objects,wcsin.p2s(objects[:,catParNames.index('x'):catParNames.index('x')+3],0)['world']),axis=1) catParUnits = tuple(list(catParUnits) + [str(cc).replace(' ','') for cc in wcsin.cunit]) catParNames = tuple(list(catParNames) + [(cc.split('--')[0]).lower() for cc in wcsin.ctype]) catParFormt = tuple(list(catParFormt) + ['%15.7e', '%15.7e', '%15.7e']) else: # constrain the RA axis reference value CRVAL_ to be between 0 and 360 deg rafound=0 for kk in range(header['naxis']): if header['ctype1'][:2]=='RA': rafound=1 break if rafound: if header['crval%i'%(kk+1)]<0: sys.stderr.write("WARNING: adding 360 deg to RA reference value.\n") header['crval%i'%(kk+1)]+=360 elif header['crval%i'%(kk+1)]>360: sys.stderr.write("WARNING: subtracting 360 deg from RA reference value.\n") header['crval%i'%(kk+1)]-=360 wcsin = wcs.WCS(header) xyz=objects[:,catParNames.index('x'):catParNames.index('x')+3].astype(float) if 'cellscal' in header and header['cellscal'] == '1/F': print 'WARNING: CELLSCAL keyword with value 1/F found.' print 'Will take into account varying pixel scale when calculating wcs coordinates.' x0,y0=header['crpix1']-1,header['crpix2']-1 # Will calculate the pixscale factor of each channel as: # pixscale = ref_frequency / frequency if header['ctype3']=='VELO-HEL': pixscale=(1-header['crval3']/2.99792458e+8)/(1-(((xyz[:,2]+1)-header['crpix3'])*header['cdelt3']+header['crval3'])/2.99792458e+8) else: sys.stderr.write("WARNING: Cannot convert axis3 coordinates to frequency. Will ignore the effect of CELLSCAL = 1/F.\n") pixscale=1. xyz[:,0]=(xyz[:,0]-x0)*pixscale+x0 xyz[:,1]=(xyz[:,1]-y0)*pixscale+y0 if header['naxis']==4: objects=np.concatenate((objects,wcsin.wcs_pix2world(np.concatenate((xyz,np.zeros((objects.shape[0],1))),axis=1),0)[:,:-1]),axis=1) else: objects=np.concatenate((objects,wcsin.wcs_pix2world(xyz,0)),axis=1) catParUnits = tuple(list(catParUnits) + [str(cc).replace(' ','') for cc in wcsin.wcs.cunit]) catParNames = tuple(list(catParNames) + [(cc.split('--')[0]).lower() for cc in wcsin.wcs.ctype]) catParFormt = tuple(list(catParFormt) + ['%15.7e', '%15.7e', '%15.7e']) if header['naxis']==4: catParUnits = catParUnits[:-1] catParNames= catParNames[:-1] print "WCS coordinates added to the catalogue." except: sys.stderr.write("WARNING: WCS conversion of parameters failed.\n") return(objects, catParNames, catParFormt, catParUnits)