def norm_dist(src1, src2): """ Calculate the normalised distance between two sources. Sources are elliptical Gaussians. The normalised distance is calculated as the GCD distance between the centers, divided by quadrature sum of the radius of each ellipse along a line joining the two ellipses. For ellipses that touch at a single point, the normalized distance will be 1/sqrt(2). Parameters ---------- src1, src2 : object The two positions to compare. Objects must have the following parameters: (ra, dec, a, b, pa). Returns ------- dist: float The normalised distance. """ if src1 == src2: return 0 dist = gcd(src1.ra, src1.dec, src2.ra, src2.dec) # degrees # the angle between the ellipse centers phi = bear(src1.ra, src1.dec, src2.ra, src2.dec) # Degrees # Calculate the radius of each ellipse along a line that joins their centers. r1 = src1.a * src1.b / np.hypot(src1.a * np.sin(np.radians(phi - src1.pa)), src1.b * np.cos(np.radians(phi - src1.pa))) r2 = src2.a * src2.b / np.hypot( src2.a * np.sin(np.radians(180 + phi - src2.pa)), src2.b * np.cos(np.radians(180 + phi - src2.pa))) R = dist / (np.hypot(r1, r2) / 3600) return R
def pix2sky_vec(self, pixel, r, theta): """ Convert a vector from pixel to sky coords vector is calculated at an origin pixel=(x,y) and has a magnitude (r) [in pixels] and an angle (theta) [in degrees] input: pixel - (x,y) of origin r - magnitude in pixels theta - in degrees return: ra,dec - corresponding to pixels x,y r,pa - magnitude and angle (degrees) of the original vector, as measured on the sky """ ra1, dec1 = self.pix2sky(pixel) x, y = pixel a = [ x + r * np.cos(np.radians(theta)), y + r * np.sin(np.radians(theta)) ] locations = self.pix2sky(a) ra2, dec2 = locations a = gcd(ra1, dec1, ra2, dec2) pa = bear(ra1, dec1, ra2, dec2) return ra1, dec1, a, pa
def pix2sky_ellipse(self, pixel, sx, sy, theta): """ Convert an ellipse from pixel to sky coords sx/sy vectors are calculated at an origin pos=(x,y) Input parameters are: x,y - the x,y pixels corresponding to the ra/dec position sx, sy - the major minor axes (FWHM) in pixels theta - the position angle in degrees Output params are all in degrees :param pixel: [x,y] of the ellipse center :param sx: major axis :param sy: minor axis :param theta: position angle :return: ra, dec, a, b, pa """ ra, dec = self.pix2sky(pixel) x, y = pixel v_sx = [ x + sx * np.cos(np.radians(theta)), y + sx * np.sin(np.radians(theta)) ] ra2, dec2 = self.pix2sky(v_sx) major = gcd(ra, dec, ra2, dec2) pa = bear(ra, dec, ra2, dec2) v_sy = [ x + sy * np.cos(np.radians(theta - 90)), y + sy * np.sin(np.radians(theta - 90)) ] ra2, dec2 = self.pix2sky(v_sy) minor = gcd(ra, dec, ra2, dec2) pa2 = bear(ra, dec, ra2, dec2) - 90 # The a/b vectors are perpendicular in sky space, but not always in pixel space # so we have to account for this by calculating the angle between the two vectors # and modifying the minor axis length defect = pa - pa2 minor *= abs(np.cos(np.radians(defect))) return ra, dec, major, minor, pa
def sky_sep(self, pix1, pix2): """ calculate the sky separation between two pixels Input: pix1 = [x1,y1] pix2 = [x2,y2] Returns: sep = separation in degrees """ pos1 = self.pix2sky(pix1) pos2 = self.pix2sky(pix2) sep = gcd(pos1[0], pos1[1], pos2[0], pos2[1]) return sep
def pix2sky_ellipse(self, pixel, sx, sy, theta): """ Convert an ellipse from pixel to sky coords sx/sy vectors are calculated at an origin pos=(x,y) Input parameters are: x,y - the x,y pixels corresponding to the ra/dec position sx, sy - the major minor axes (FWHM) in pixels theta - the position angle in degrees Output params are all in degrees :param pixel: [x,y] of the ellipse center :param sx: major axis :param sy: minor axis :param theta: position angle :return: ra, dec, a, b, pa """ ra, dec = self.pix2sky(pixel) x, y = pixel v_sx = [x + sx * np.cos(np.radians(theta)), y + sx * np.sin(np.radians(theta))] ra2, dec2 = self.pix2sky(v_sx) major = gcd(ra, dec, ra2, dec2) pa = bear(ra, dec, ra2, dec2) v_sy = [x + sy * np.cos(np.radians(theta-90)), y + sy * np.sin(np.radians(theta-90))] ra2, dec2 = self.pix2sky(v_sy) minor = gcd(ra, dec, ra2, dec2) pa2 = bear(ra, dec, ra2, dec2) - 90 # The a/b vectors are perpendicular in sky space, but not always in pixel space # so we have to account for this by calculating the angle between the two vectors # and modifying the minor axis length defect = pa - pa2 minor *= abs(np.cos(np.radians(defect))) return ra, dec, major, minor, pa
def norm_dist(src1,src2): """ Calculate the normalised distance between two sources. Sources are elliptical gaussians. :param src1: :param src2: :return: """ if src1 == src2: return 0 dist = gcd(src1.ra, src1.dec, src2.ra, src2.dec) # degrees # the angle between the ellipse centers phi = bear(src1.ra, src1.dec, src2.ra, src2.dec) # Degrees # Calculate the radius of each ellpise along a line that joins their centers. r1 = src1.a*src1.b / np.hypot(src1.a * np.sin(np.radians(phi - src1.pa)), src1.b * np.cos(np.radians(phi - src1.pa))) r2 = src2.a*src2.b / np.hypot(src2.a * np.sin(np.radians(180 + phi - src2.pa)), src2.b * np.cos(np.radians(180 + phi - src2.pa))) R = dist / (np.hypot(r1,r2) / 3600) return R
def norm_dist(src1, src2): """ Calculate the normalised distance between two sources. Sources are elliptical gaussians. :param src1: :param src2: :return: """ if src1 == src2: return 0 dist = gcd(src1.ra, src1.dec, src2.ra, src2.dec) # degrees # the angle between the ellipse centers phi = bear(src1.ra, src1.dec, src2.ra, src2.dec) # Degrees # Calculate the radius of each ellpise along a line that joins their centers. r1 = src1.a * src1.b / np.hypot(src1.a * np.sin(np.radians(phi - src1.pa)), src1.b * np.cos(np.radians(phi - src1.pa))) r2 = src2.a * src2.b / np.hypot( src2.a * np.sin(np.radians(180 + phi - src2.pa)), src2.b * np.cos(np.radians(180 + phi - src2.pa))) R = dist / (np.hypot(r1, r2) / 3600) return R
def pix2sky_vec(self, pixel, r, theta): """ Convert a vector from pixel to sky coords vector is calculated at an origin pixel=(x,y) and has a magnitude (r) [in pixels] and an angle (theta) [in degrees] input: pixel - (x,y) of origin r - magnitude in pixels theta - in degrees return: ra,dec - corresponding to pixels x,y r,pa - magnitude and angle (degrees) of the original vector, as measured on the sky """ ra1, dec1 = self.pix2sky(pixel) x, y = pixel a = [x + r * np.cos(np.radians(theta)), y + r * np.sin(np.radians(theta))] locations = self.pix2sky(a) ra2, dec2 = locations a = gcd(ra1, dec1, ra2, dec2) pa = bear(ra1, dec1, ra2, dec2) return ra1, dec1, a, pa
def sky_dist(src1, src2): """ Great circle distance between two sources. A check is made to determine if the two sources are the same object, in this case the distance is zero. Parameters ---------- src1, src2 : object Two sources to check. Objects must have parameters (ra,dec) in degrees. Returns ------- distance : float The distance between the two sources. See Also -------- :func:`AegeanTools.angle_tools.gcd` """ dist = gcd(src1.ra, src1.dec, src2.ra, src2.dec) # degrees return dist
def best_dist(src1, src2): """ Calculate the seperation in standard deviations between sources based on their sky position and relative flux using greater circle method. Parameters ---------- src1, src2 : object Two sources to check. Objects must have parameters (ra,dec) in degrees. Returns ------- separation : float The separation in standard deviations between the two sources. """ ra_error1 = src1.err_ra ra_error2 = src2.err_ra seperation = ( (((gcd(src1.ra, src1.dec, src2.ra, src2.dec))**2) / ((ra_error1**2 + ra_error2**2 + src1.err_dec**2 + src2.err_dec**2)** (0.5))) + (((src1.peak_flux - src2.peak_flux)**2) / ((src1.err_peak_flux**2 + src1.err_peak_flux**2)**(0.5))))**0.5 return seperation
def errors(source, model, wcshelper): """ Convert pixel based errors into sky coord errors :param source: Source object :param wcshelper: WCSHelper object :return: """ # if the source wasn't fit then all errors are -1 if source.flags & (flags.NOTFIT | flags.FITERR): source.err_peak_flux = source.err_a = source.err_b = source.err_pa = -1 source.err_ra = source.err_dec = source.err_int_flux = -1 return source # copy the errors from the model prefix = "c{0}_".format(source.source) err_amp = model[prefix+'amp'].stderr xo,yo = model[prefix+'xo'].value, model[prefix+'yo'].value err_xo = model[prefix+'xo'].stderr err_yo = model[prefix+'yo'].stderr sx, sy = model[prefix+'sx'].value, model[prefix+'sy'].value err_sx = model[prefix+'sx'].stderr err_sy = model[prefix+'sy'].stderr theta = model[prefix+'theta'].value err_theta = model[prefix+'theta'].stderr source.err_peak_flux = err_amp pix_errs = [err_xo,err_yo,err_sx,err_sy,err_theta] # check for inf/nan errors -> these sources have poor fits. if not all([ a is not None and np.isfinite(a) for a in pix_errs]): source.flags |= flags.FITERR source.err_peak_flux = source.err_a = source.err_b = source.err_pa = -1 source.err_ra = source.err_dec = source.err_int_flux = -1 return source # position errors if model[prefix + 'xo'].vary and model[prefix + 'yo'].vary: ref = wcshelper.pix2sky([xo,yo]) offset = wcshelper.pix2sky([xo+err_xo,yo+err_yo]) source.err_ra = gcd(ref[0], ref[1], offset[0], ref[1]) source.err_dec = gcd(ref[0], ref[1], ref[0], offset[1]) else: source.err_ra = source.err_dec = -1 if model[prefix + 'sx'].vary and model[prefix + 'sy'].vary: # major axis error ref = wcshelper.pix2sky([xo+sx*np.cos(np.radians(theta)),yo+sy*np.sin(np.radians(theta))]) offset = wcshelper.pix2sky([xo+(sx+err_sx)*np.cos(np.radians(theta)),yo+sy*np.sin(np.radians(theta))]) source.err_a = gcd(ref[0],ref[1],offset[0],offset[1]) * 3600 # minor axis error ref = wcshelper.pix2sky([xo+sx*np.cos(np.radians(theta+90)),yo+sy*np.sin(np.radians(theta+90))]) offset = wcshelper.pix2sky([xo+sx*np.cos(np.radians(theta+90)),yo+(sy+err_sy)*np.sin(np.radians(theta+90))]) source.err_b = gcd(ref[0], ref[1], offset[0], offset[1]) * 3600 else: source.err_a = source.err_b = -1 if model[prefix+'theta'].vary: # pa error ref = wcshelper.pix2sky([xo,yo]) off1 = wcshelper.pix2sky([xo+sx*np.cos(np.radians(theta)),yo+sy*np.sin(np.radians(theta))]) off2 = wcshelper.pix2sky([xo+sx*np.cos(np.radians(theta+err_theta)),yo+sy*np.sin(np.radians(theta+err_theta))]) source.err_pa = abs(bear(ref[0], ref[1], off1[0], off1[1]) - bear(ref[0], ref[1], off2[0], off2[1])) else: source.err_pa = -1 sqerr = 0 sqerr += (source.err_peak_flux/source.peak_flux)**2 if source.err_peak_flux >0 else 0 sqerr += (source.err_a/source.a)**2 if source.err_a > 0 else 0 sqerr += (source.err_b/source.b)**2 if source.err_b > 0 else 0 source.err_int_flux = source.int_flux*np.sqrt(sqerr) # logging.info("src ({0},{1})".format(source.island,source.source)) # logging.info(" pixel errs {0}".format([err_xo, err_yo, err_sx, err_sy, err_theta])) # logging.info(" sky errs {0}".format([source.err_ra, source.err_dec, source.err_a, source.err_b, source.err_pa])) return source
def errors(source, model, wcshelper): """ Convert pixel based errors into sky coord errors :param source: Source object :param wcshelper: WCSHelper object :return: """ # if the source wasn't fit then all errors are -1 if source.flags & (flags.NOTFIT | flags.FITERR): source.err_peak_flux = source.err_a = source.err_b = source.err_pa = -1 source.err_ra = source.err_dec = source.err_int_flux = -1 return source # copy the errors from the model prefix = "c{0}_".format(source.source) err_amp = model[prefix + 'amp'].stderr xo, yo = model[prefix + 'xo'].value, model[prefix + 'yo'].value err_xo = model[prefix + 'xo'].stderr err_yo = model[prefix + 'yo'].stderr sx, sy = model[prefix + 'sx'].value, model[prefix + 'sy'].value err_sx = model[prefix + 'sx'].stderr err_sy = model[prefix + 'sy'].stderr theta = model[prefix + 'theta'].value err_theta = model[prefix + 'theta'].stderr source.err_peak_flux = err_amp pix_errs = [err_xo, err_yo, err_sx, err_sy, err_theta] # check for inf/nan errors -> these sources have poor fits. if not all([a is not None and np.isfinite(a) for a in pix_errs]): source.flags |= flags.FITERR source.err_peak_flux = source.err_a = source.err_b = source.err_pa = -1 source.err_ra = source.err_dec = source.err_int_flux = -1 return source # position errors if model[prefix + 'xo'].vary and model[prefix + 'yo'].vary: ref = wcshelper.pix2sky([xo, yo]) offset = wcshelper.pix2sky([xo + err_xo, yo + err_yo]) source.err_ra = gcd(ref[0], ref[1], offset[0], ref[1]) source.err_dec = gcd(ref[0], ref[1], ref[0], offset[1]) else: source.err_ra = source.err_dec = -1 if model[prefix + 'sx'].vary and model[prefix + 'sy'].vary: # major axis error ref = wcshelper.pix2sky([ xo + sx * np.cos(np.radians(theta)), yo + sy * np.sin(np.radians(theta)) ]) offset = wcshelper.pix2sky([ xo + (sx + err_sx) * np.cos(np.radians(theta)), yo + sy * np.sin(np.radians(theta)) ]) source.err_a = gcd(ref[0], ref[1], offset[0], offset[1]) * 3600 # minor axis error ref = wcshelper.pix2sky([ xo + sx * np.cos(np.radians(theta + 90)), yo + sy * np.sin(np.radians(theta + 90)) ]) offset = wcshelper.pix2sky([ xo + sx * np.cos(np.radians(theta + 90)), yo + (sy + err_sy) * np.sin(np.radians(theta + 90)) ]) source.err_b = gcd(ref[0], ref[1], offset[0], offset[1]) * 3600 else: source.err_a = source.err_b = -1 if model[prefix + 'theta'].vary: # pa error ref = wcshelper.pix2sky([xo, yo]) off1 = wcshelper.pix2sky([ xo + sx * np.cos(np.radians(theta)), yo + sy * np.sin(np.radians(theta)) ]) off2 = wcshelper.pix2sky([ xo + sx * np.cos(np.radians(theta + err_theta)), yo + sy * np.sin(np.radians(theta + err_theta)) ]) source.err_pa = abs( bear(ref[0], ref[1], off1[0], off1[1]) - bear(ref[0], ref[1], off2[0], off2[1])) else: source.err_pa = -1 sqerr = 0 sqerr += (source.err_peak_flux / source.peak_flux)**2 if source.err_peak_flux > 0 else 0 sqerr += (source.err_a / source.a)**2 if source.err_a > 0 else 0 sqerr += (source.err_b / source.b)**2 if source.err_b > 0 else 0 source.err_int_flux = source.int_flux * np.sqrt(sqerr) # logging.info("src ({0},{1})".format(source.island,source.source)) # logging.info(" pixel errs {0}".format([err_xo, err_yo, err_sx, err_sy, err_theta])) # logging.info(" sky errs {0}".format([source.err_ra, source.err_dec, source.err_a, source.err_b, source.err_pa])) return source