def _checkNeighbors(self, qs, root, x, y, tolerance): i = qs.id(root, x, y) neighbors = qs.getNeighbors(i) centers = [qs.getCenter(n) for n in neighbors] c = qs.getCenter(i) for v in centers: self.assertTrue(geom.cartesianAngularSep(c, v) < tolerance)
def getGeometry(self, pixelId, fiducial=False): """Returns a spherical convex polygon corresponding to the fiducial or padded boundaries of the sky-pixel with the specified id. """ root, ix, iy = self.coords(pixelId) xl = 2.0 * float(ix) / float(self.resolution) - 1.0 xr = 2.0 * float(ix + 1) / float(self.resolution) - 1.0 yb = 2.0 * float(iy) / float(self.resolution) - 1.0 yt = 2.0 * float(iy + 1) / float(self.resolution) - 1.0 c, x, y = self.center[root], self.x[root], self.y[root] v = list( map(geom.normalize, [(c[0] + xl * x[0] + yb * y[0], c[1] + xl * x[1] + yb * y[1], c[2] + xl * x[2] + yb * y[2]), (c[0] + xr * x[0] + yb * y[0], c[1] + xr * x[1] + yb * y[1], c[2] + xr * x[2] + yb * y[2]), (c[0] + xr * x[0] + yt * y[0], c[1] + xr * x[1] + yt * y[1], c[2] + xr * x[2] + yt * y[2]), (c[0] + xl * x[0] + yt * y[0], c[1] + xl * x[1] + yt * y[1], c[2] + xl * x[2] + yt * y[2])])) if not fiducial and self.padding > 0.0: # Determine angles by which edge planes must be rotated outwards sp = math.sin(self.padding) theta = [ 0.5 * geom.cartesianAngularSep(x[0], x[1]) for x in ((v[0], v[3]), (v[1], v[0]), (v[2], v[1]), (v[3], v[2])) ] sina = [sp / math.cos(math.radians(x)) for x in theta] cosa = [math.sqrt(1.0 - x * x) for x in sina] # find plane equations of fiducial pixel boundaries xlp = self.xplane[root][ix][0] ybp = self.yplane[root][iy][0] xrp = self.xplane[root][ix + 1][0] ytp = self.yplane[root][iy + 1][0] # rotate edge planes outwards xlp = self.xrot[root](xlp, -sina[0], cosa[0]) ybp = self.yrot[root](ybp, -sina[1], cosa[1]) xrp = self.xrot[root](xrp, sina[2], cosa[2]) ytp = self.yrot[root](ytp, sina[3], cosa[3]) # intersect rotated planes to find vertices of padded sky-pixel v = list( map(geom.normalize, [ geom.cross(xlp, ybp), geom.cross(xrp, ybp), geom.cross(xrp, ytp), geom.cross(xlp, ytp) ])) return geom.SphericalConvexPolygon(v)
def getGeometry(self, pixelId, fiducial=False): """Returns a spherical convex polygon corresponding to the fiducial or padded boundaries of the sky-pixel with the specified id. """ root, ix, iy = self.coords(pixelId) xl = 2.0 * float(ix) / float(self.resolution) - 1.0 xr = 2.0 * float(ix + 1) / float(self.resolution) - 1.0 yb = 2.0 * float(iy) / float(self.resolution) - 1.0 yt = 2.0 * float(iy + 1) / float(self.resolution) - 1.0 c, x, y = self.center[root], self.x[root], self.y[root] v = map(geom.normalize, [(c[0] + xl * x[0] + yb * y[0], c[1] + xl * x[1] + yb * y[1], c[2] + xl * x[2] + yb * y[2]), (c[0] + xr * x[0] + yb * y[0], c[1] + xr * x[1] + yb * y[1], c[2] + xr * x[2] + yb * y[2]), (c[0] + xr * x[0] + yt * y[0], c[1] + xr * x[1] + yt * y[1], c[2] + xr * x[2] + yt * y[2]), (c[0] + xl * x[0] + yt * y[0], c[1] + xl * x[1] + yt * y[1], c[2] + xl * x[2] + yt * y[2])]) if not fiducial and self.padding > 0.0: # Determine angles by which edge planes must be rotated outwards sp = math.sin(self.padding) theta = map(lambda x: 0.5 * geom.cartesianAngularSep(x[0], x[1]), ((v[0],v[3]), (v[1],v[0]), (v[2],v[1]), (v[3],v[2]))) sina = map(lambda x: sp / math.cos(math.radians(x)), theta) cosa = map(lambda x: math.sqrt(1.0 - x * x), sina) # find plane equations of fiducial pixel boundaries xlp = self.xplane[root][ix][0] ybp = self.yplane[root][iy][0] xrp = self.xplane[root][ix + 1][0] ytp = self.yplane[root][iy + 1][0] # rotate edge planes outwards xlp = self.xrot[root](xlp, -sina[0], cosa[0]) ybp = self.yrot[root](ybp, -sina[1], cosa[1]) xrp = self.xrot[root](xrp, sina[2], cosa[2]) ytp = self.yrot[root](ytp, sina[3], cosa[3]) # intersect rotated planes to find vertices of padded sky-pixel v = map(geom.normalize, [geom.cross(xlp, ybp), geom.cross(xrp, ybp), geom.cross(xrp, ytp), geom.cross(xlp, ytp)]) return geom.SphericalConvexPolygon(v)
def __init__(self, resolution, paddingRad): """Creates a new quad-sphere sky pixelisation. resolution: the number of pixels along the x and y axes of each root pixel. paddingRad: the angular separation (rad) by which fiducial sky-pixels are to be padded. """ if not isinstance(resolution, numbers.Integral): raise TypeError( 'Quad-sphere resolution parameter must be an integer') if resolution < 3 or resolution > 16384: raise RuntimeError( 'Quad-sphere resolution must be in range [3, 16384]') if not isinstance(paddingRad, float): raise TypeError('Quad-sphere pixel padding angle must be a float') if paddingRad < 0.0 or paddingRad >= math.pi * 0.25: raise RuntimeError( 'Quad-sphere pixel padding radius must be in range [0, 45) deg' ) R = resolution self.resolution = R self.padding = paddingRad x, y, z = (1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0) nx, ny, nz = (-1.0, 0.0, 0.0), (0.0, -1.0, 0.0), (0.0, 0.0, -1.0) # center of each root pixel self.center = [z, x, y, nx, ny, nz] # x basis vector of each root pixel self.x = [ny, y, nx, ny, x, ny] # y basis vector of each root pixel self.y = [x, z, z, z, z, nx] # functions for rotating vectors in direction of increasing x (per root pixel) self.xrot = [_rotX, _rotZ, _rotZ, _rotZ, _rotZ, _rotNX] # functions for rotating vectors in direction of increasing y (per root pixel) self.yrot = [_rotY, _rotNY, _rotX, _rotY, _rotNX, _rotY] # Compute fiducial and splitting x/y planes for each root pixel self.xplane = [] self.yplane = [] sp = math.sin(self.padding) for root in range(6): xplanes = [] yplanes = [] c, x, y = self.center[root], self.x[root], self.y[root] for i in range(R + 1): xfp = self._fiducialXPlane(root, i) yfp = self._fiducialYPlane(root, i) f = 2.0 * float(i) / float(R) - 1.0 thetaX = math.radians(0.5 * geom.cartesianAngularSep( (c[0] + f * x[0] + y[0], c[1] + f * x[1] + y[1], c[2] + f * x[2] + y[2]), (c[0] + f * x[0] - y[0], c[1] + f * x[1] - y[1], c[2] + f * x[2] - y[2]))) thetaY = math.radians(0.5 * geom.cartesianAngularSep( (c[0] + x[0] + f * y[0], c[1] + x[1] + f * y[1], c[2] + x[2] + f * y[2]), (c[0] - x[0] + f * y[0], c[1] - x[1] + f * y[1], c[2] - x[2] + f * y[2]))) sinrx = sp / math.cos(thetaX) sinry = sp / math.cos(thetaY) cosrx = math.sqrt(1.0 - sinrx * sinrx) cosry = math.sqrt(1.0 - sinry * sinry) if i == 0: xlp = None ybp = None else: xlp = self.xrot[root](xfp, sinrx, cosrx) ybp = self.yrot[root](yfp, sinry, cosry) xlp = (-xlp[0], -xlp[1], -xlp[2]) ybp = (-ybp[0], -ybp[1], -ybp[2]) if i == R: xrp = None ytp = None else: xrp = self.xrot[root](xfp, -sinrx, cosrx) ytp = self.yrot[root](yfp, -sinry, cosry) xplanes.append((xfp, xlp, xrp)) yplanes.append((yfp, ybp, ytp)) self.xplane.append(xplanes) self.yplane.append(yplanes) # Corner pixel neighbors. self.cornerNeighbors = ( # root pixel 0 ( # x, y = 0, 0 (self.id(0, 1, 0), self.id(0, 1, 1), self.id(0, 0, 1), self.id(2, R - 1, R - 1), self.id(2, R - 2, R - 1), self.id(3, 0, R - 1), self.id(3, 1, R - 1)), # x, y = 0, R-1 (self.id(0, 1, R - 1), self.id(0, 1, R - 2), self.id(0, 0, R - 2), self.id(1, R - 1, R - 1), self.id(1, R - 2, R - 1), self.id(2, 0, R - 1), self.id(2, 1, R - 1)), # x, y = R-1, 0 (self.id(0, R - 2, 0), self.id(0, R - 2, 1), self.id(0, R - 1, 1), self.id(3, R - 2, R - 1), self.id(3, R - 1, R - 1), self.id(4, 0, R - 1), self.id(4, 1, R - 1)), # x, y = R-1, R-1 (self.id(0, R - 2, R - 1), self.id(0, R - 2, R - 2), self.id(0, R - 1, R - 2), self.id(1, 0, R - 1), self.id(1, 1, R - 1), self.id(4, R - 2, R - 1), self.id(4, R - 1, R - 1))), # root pixel 1 ( # x, y = 0, 0 (self.id(1, 1, 0), self.id(1, 1, 1), self.id(1, 0, 1), self.id(4, R - 1, 0), self.id(4, R - 1, 1), self.id(5, R - 2, 0), self.id(5, R - 1, 0)), # x, y = 0, R-1 (self.id(1, 1, R - 1), self.id(1, 1, R - 2), self.id(1, 0, R - 2), self.id(4, R - 1, R - 2), self.id(4, R - 1, R - 1), self.id(0, R - 2, R - 1), self.id(0, R - 1, R - 1)), # x, y = R-1, 0 (self.id(1, R - 2, 0), self.id(1, R - 2, 1), self.id(1, R - 1, 1), self.id(2, 0, 0), self.id(2, 0, 1), self.id(5, 0, 0), self.id(5, 1, 0)), # x, y = R-1, R-1 (self.id(1, R - 2, R - 1), self.id(1, R - 2, R - 2), self.id(1, R - 1, R - 2), self.id(2, 0, R - 2), self.id(2, 0, R - 1), self.id(0, 0, R - 1), self.id(0, 1, R - 1))), # root pixel 2 ( # x, y = 0, 0 (self.id(2, 1, 0), self.id(2, 1, 1), self.id(2, 0, 1), self.id(1, R - 1, 0), self.id(1, R - 1, 1), self.id(5, 0, 0), self.id(5, 0, 1)), # x, y = 0, R-1 (self.id(2, 1, R - 1), self.id(2, 1, R - 2), self.id(2, 0, R - 2), self.id(1, R - 1, R - 2), self.id(1, R - 1, R - 1), self.id(0, 0, R - 2), self.id(0, 0, R - 1)), # x, y = R-1, 0 (self.id(2, R - 2, 0), self.id(2, R - 2, 1), self.id(2, R - 1, 1), self.id(3, 0, 0), self.id(3, 0, 1), self.id(5, 0, R - 2), self.id(5, 0, R - 1)), # x, y = R-1, R-1 (self.id(2, R - 2, R - 1), self.id(2, R - 2, R - 2), self.id(2, R - 1, R - 2), self.id(3, 0, R - 2), self.id(3, 0, R - 1), self.id(0, 0, 0), self.id(0, 0, 1))), # root pixel 3 ( # x, y = 0, 0 (self.id(3, 1, 0), self.id(3, 1, 1), self.id(3, 0, 1), self.id(2, R - 1, 0), self.id(2, R - 1, 1), self.id(5, 0, R - 1), self.id(5, 1, R - 1)), # x, y = 0, R-1 (self.id(3, 1, R - 1), self.id(3, 1, R - 2), self.id(3, 0, R - 2), self.id(2, R - 1, R - 2), self.id(2, R - 1, R - 1), self.id(0, 0, 0), self.id(0, 1, 0)), # x, y = R-1, 0 (self.id(3, R - 2, 0), self.id(3, R - 2, 1), self.id(3, R - 1, 1), self.id(4, 0, 0), self.id(4, 0, 1), self.id(5, R - 2, R - 1), self.id(5, R - 1, R - 1)), # x, y = R-1, R-1 (self.id(3, R - 2, R - 1), self.id(3, R - 2, R - 2), self.id(3, R - 1, R - 2), self.id(4, 0, R - 2), self.id(4, 0, R - 1), self.id(0, R - 2, 0), self.id(0, R - 1, 0))), # root pixel 4 ( # x, y = 0, 0 (self.id(4, 1, 0), self.id(4, 1, 1), self.id(4, 0, 1), self.id(3, R - 1, 0), self.id(3, R - 1, 1), self.id(5, R - 1, R - 2), self.id(5, R - 1, R - 1)), # x, y = 0, R-1 (self.id(4, 1, R - 1), self.id(4, 1, R - 2), self.id(4, 0, R - 2), self.id(3, R - 1, R - 2), self.id(3, R - 1, R - 1), self.id(0, R - 1, 0), self.id(0, R - 1, 1)), # x, y = R-1, 0 (self.id(4, R - 2, 0), self.id(4, R - 2, 1), self.id(4, R - 1, 1), self.id(1, 0, 0), self.id(1, 0, 1), self.id(5, R - 1, 0), self.id(5, R - 1, 1)), # x, y = R-1, R-1 (self.id(4, R - 2, R - 1), self.id(4, R - 2, R - 2), self.id(4, R - 1, R - 2), self.id(1, 0, R - 2), self.id(1, 0, R - 1), self.id(0, R - 1, R - 2), self.id(0, R - 1, R - 1))), # root pixel 5 ( # x, y = 0, 0 (self.id(5, 1, 0), self.id(5, 1, 1), self.id(5, 0, 1), self.id(1, R - 2, 0), self.id(1, R - 1, 0), self.id(2, 0, 0), self.id(2, 1, 0)), # x, y = 0, R-1 (self.id(5, 1, R - 1), self.id(5, 1, R - 2), self.id(5, 0, R - 2), self.id(2, R - 2, 0), self.id(2, R - 1, 0), self.id(3, 0, 0), self.id(3, 1, 0)), # x, y = R-1, 0 (self.id(5, R - 2, 0), self.id(5, R - 2, 1), self.id(5, R - 1, 1), self.id(1, 0, 0), self.id(1, 1, 0), self.id(4, R - 2, 0), self.id(4, R - 1, 0)), # x, y = R-1, R-1 (self.id(5, R - 2, R - 1), self.id(5, R - 2, R - 2), self.id(5, R - 1, R - 2), self.id(3, R - 2, 0), self.id(3, R - 1, 0), self.id(4, 0, 0), self.id(4, 1, 0))), )
def getAllSipWcs(cursor, qsp, kind): """Constructs a Wcs object from each entry in the Science_Ccd_Exposure table. SIP distortion parameters are read in from Science_Ccd_Exposure_Metadata. Returns a 3-tuple with the following contents: - a mapping from sky-tiles to a list of (WCS, filter) tuples for overlapping science CCDs - a list of all (WCS, filter) tuples read in - a bounding circle (lsst.geom.SphericalCircle) for all science CCDs. """ wcsMap = {} wcsList = [] offset = 0 centers = [] radius = 0.0 n = 0 blocksize = 1000 # An alternative would be to create WCSes from FITS metadata or FITS # files themselves, but these aren't always available. while True: cursor.execute("""SELECT scienceCcdExposureId, filterId, raDeSys, equinox, ctype1, ctype2, crpix1, crpix2, crval1, crval2, cd1_1, cd1_2, cd2_1, cd2_2 FROM Science_Ccd_Exposure LIMIT %d, %d """ % (offset, blocksize)) rows = cursor.fetchall() if len(rows) == 0: break # For each CCD in the results, build up a metadata PropertySet # including SIP distortion terms and use it to obtain a WCS. for row in rows: ps = dafBase.PropertySet() ps.setString("RADESYS", row[2]) ps.setDouble("EQUINOX", row[3]) ps.setString("CTYPE1", row[4]) ps.setString("CTYPE2", row[5]) ps.setString("CUNIT1", "deg") ps.setString("CUNIT2", "deg") ps.setDouble("CRPIX1", row[6]) ps.setDouble("CRPIX2", row[7]) ps.setDouble("CRVAL1", row[8]) ps.setDouble("CRVAL2", row[9]) ps.setDouble("CD1_1", row[10]) ps.setDouble("CD1_2", row[11]) ps.setDouble("CD2_1", row[12]) ps.setDouble("CD2_2", row[13]) if row[4].endswith("-SIP") and row[5].endswith("-SIP"): cursor.execute(""" SELECT metadataKey, intValue FROM Science_Ccd_Exposure_Metadata WHERE scienceCcdExposureId = %d AND intValue IS NOT NULL AND metadataKey RLIKE '^[AB]P?_ORDER$' """ % row[0]) for k, v in cursor.fetchall(): ps.setInt(k, v) cursor.execute(""" SELECT metadataKey, doubleValue FROM Science_Ccd_Exposure_Metadata WHERE scienceCcdExposureId = %d AND doubleValue IS NOT NULL AND metadataKey RLIKE '^[AB]P?_[0-9]+_[0-9]+$' """ % row[0]) for k, v in cursor.fetchall(): ps.setDouble(k, v) wcs = afwImage.makeWcs(ps) wcsList.append([wcs, row[1]]) if kind == 'imsim': poly = qs.imageToPolygon(wcs, 4072.0, 4000.0, 0.0) else: poly = qs.imageToPolygon(wcs, 2048.0, 4610.0, 0.0) bc = poly.getBoundingCircle() radius = max(radius, bc.getRadius()) centers.append(geom.cartesianUnitVector(bc.getCenter())) pix = qsp.intersect(poly) for skyTile in pix: if skyTile in wcsMap: wcsMap[skyTile].append([wcs, row[1]]) else: wcsMap[skyTile] = [[wcs, row[1]]] n = offset + len(rows) print "... %d" % n offset += blocksize x, y, z = 0.0, 0.0, 0.0 for v in centers: x += v[0] y += v[1] z += v[2] cen = geom.normalize((x, y, z)) r = max(geom.cartesianAngularSep(cen, v) for v in centers) + 2.0 * radius return wcsMap, wcsList, geom.SphericalCircle(cen, r)
def __init__(self, resolution, paddingRad): """Creates a new quad-sphere sky pixelisation. resolution: the number of pixels along the x and y axes of each root pixel. paddingRad: the angular separation (rad) by which fiducial sky-pixels are to be padded. """ if not isinstance(resolution, (int, long)): raise TypeError( 'Quad-sphere resolution parameter must be an int or long') if resolution < 3 or resolution > 16384: raise RuntimeError( 'Quad-sphere resolution must be in range [3, 16384]') if not isinstance(paddingRad, float): raise TypeError( 'Quad-sphere pixel padding angle must be a float') if paddingRad < 0.0 or paddingRad >= math.pi * 0.25: raise RuntimeError( 'Quad-sphere pixel padding radius must be in range [0, 45) deg') R = resolution self.resolution = R self.padding = paddingRad x, y, z = (1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0) nx, ny, nz = (-1.0, 0.0, 0.0), (0.0, -1.0, 0.0), (0.0, 0.0, -1.0) # center of each root pixel self.center = [z, x, y, nx, ny, nz] # x basis vector of each root pixel self.x = [ny, y, nx, ny, x, ny] # y basis vector of each root pixel self.y = [x, z, z, z, z, nx] # functions for rotating vectors in direction of increasing x (per root pixel) self.xrot = [_rotX, _rotZ, _rotZ, _rotZ, _rotZ, _rotNX] # functions for rotating vectors in direction of increasing y (per root pixel) self.yrot = [_rotY, _rotNY, _rotX, _rotY, _rotNX, _rotY] # Compute fiducial and splitting x/y planes for each root pixel self.xplane = [] self.yplane = [] sp = math.sin(self.padding) for root in xrange(6): xplanes = [] yplanes = [] c, x, y = self.center[root], self.x[root], self.y[root] for i in xrange(R + 1): xfp = self._fiducialXPlane(root, i) yfp = self._fiducialYPlane(root, i) f = 2.0 * float(i) / float(R) - 1.0 thetaX = math.radians(0.5 * geom.cartesianAngularSep( (c[0] + f * x[0] + y[0], c[1] + f * x[1] + y[1], c[2] + f * x[2] + y[2]), (c[0] + f * x[0] - y[0], c[1] + f * x[1] - y[1], c[2] + f * x[2] - y[2]))) thetaY = math.radians(0.5 * geom.cartesianAngularSep( (c[0] + x[0] + f * y[0], c[1] + x[1] + f * y[1], c[2] + x[2] + f * y[2]), (c[0] - x[0] + f * y[0], c[1] - x[1] + f * y[1], c[2] - x[2] + f * y[2]))) sinrx = sp / math.cos(thetaX) sinry = sp / math.cos(thetaY) cosrx = math.sqrt(1.0 - sinrx * sinrx) cosry = math.sqrt(1.0 - sinry * sinry) if i == 0: xlp = None ybp = None else: xlp = self.xrot[root](xfp, sinrx, cosrx) ybp = self.yrot[root](yfp, sinry, cosry) xlp = (-xlp[0], -xlp[1], -xlp[2]) ybp = (-ybp[0], -ybp[1], -ybp[2]) if i == R: xrp = None ytp = None else: xrp = self.xrot[root](xfp, -sinrx, cosrx) ytp = self.yrot[root](yfp, -sinry, cosry) xplanes.append((xfp, xlp, xrp)) yplanes.append((yfp, ybp, ytp)) self.xplane.append(xplanes) self.yplane.append(yplanes) # Corner pixel neighbors. self.cornerNeighbors = ( # root pixel 0 ( # x, y = 0, 0 (self.id(0, 1, 0), self.id(0, 1, 1), self.id(0, 0, 1), self.id(2, R - 1, R - 1), self.id(2, R - 2, R - 1), self.id(3, 0, R - 1), self.id(3, 1, R - 1)), # x, y = 0, R-1 (self.id(0, 1, R - 1), self.id(0, 1, R - 2), self.id(0, 0, R - 2), self.id(1, R - 1, R - 1), self.id(1, R - 2, R - 1), self.id(2, 0, R - 1), self.id(2, 1, R - 1)), # x, y = R-1, 0 (self.id(0, R - 2, 0), self.id(0, R - 2, 1), self.id(0, R - 1, 1), self.id(3, R - 2, R - 1), self.id(3, R - 1, R - 1), self.id(4, 0, R - 1), self.id(4, 1, R - 1)), # x, y = R-1, R-1 (self.id(0, R - 2, R - 1), self.id(0, R - 2, R - 2), self.id(0, R - 1, R - 2), self.id(1, 0, R - 1), self.id(1, 1, R - 1), self.id(4, R - 2, R - 1), self.id(4, R - 1, R - 1)) ), # root pixel 1 ( # x, y = 0, 0 (self.id(1, 1, 0), self.id(1, 1, 1), self.id(1, 0, 1), self.id(4, R - 1, 0), self.id(4, R - 1, 1), self.id(5, R - 2, 0), self.id(5, R - 1, 0)), # x, y = 0, R-1 (self.id(1, 1, R - 1), self.id(1, 1, R - 2), self.id(1, 0, R - 2), self.id(4, R - 1, R - 2), self.id(4, R - 1, R - 1), self.id(0, R - 2, R - 1), self.id(0, R - 1, R - 1)), # x, y = R-1, 0 (self.id(1, R - 2, 0), self.id(1, R - 2, 1), self.id(1, R - 1, 1), self.id(2, 0, 0), self.id(2, 0, 1), self.id(5, 0, 0), self.id(5, 1, 0)), # x, y = R-1, R-1 (self.id(1, R - 2, R - 1), self.id(1, R - 2, R - 2), self.id(1, R - 1, R - 2), self.id(2, 0, R - 2), self.id(2, 0, R - 1), self.id(0, 0, R - 1), self.id(0, 1, R - 1)) ), # root pixel 2 ( # x, y = 0, 0 (self.id(2, 1, 0), self.id(2, 1, 1), self.id(2, 0, 1), self.id(1, R - 1, 0), self.id(1, R - 1, 1), self.id(5, 0, 0), self.id(5, 0, 1)), # x, y = 0, R-1 (self.id(2, 1, R - 1), self.id(2, 1, R - 2), self.id(2, 0, R - 2), self.id(1, R - 1, R - 2), self.id(1, R - 1, R - 1), self.id(0, 0, R - 2), self.id(0, 0, R - 1)), # x, y = R-1, 0 (self.id(2, R - 2, 0), self.id(2, R - 2, 1), self.id(2, R - 1, 1), self.id(3, 0, 0), self.id(3, 0, 1), self.id(5, 0, R - 2), self.id(5, 0, R - 1)), # x, y = R-1, R-1 (self.id(2, R - 2, R - 1), self.id(2, R - 2, R - 2), self.id(2, R - 1, R - 2), self.id(3, 0, R - 2), self.id(3, 0, R - 1), self.id(0, 0, 0), self.id(0, 0, 1)) ), # root pixel 3 ( # x, y = 0, 0 (self.id(3, 1, 0), self.id(3, 1, 1), self.id(3, 0, 1), self.id(2, R - 1, 0), self.id(2, R - 1, 1), self.id(5, 0, R - 1), self.id(5, 1, R - 1)), # x, y = 0, R-1 (self.id(3, 1, R - 1), self.id(3, 1, R - 2), self.id(3, 0, R - 2), self.id(2, R - 1, R - 2), self.id(2, R - 1, R - 1), self.id(0, 0, 0), self.id(0, 1, 0)), # x, y = R-1, 0 (self.id(3, R - 2, 0), self.id(3, R - 2, 1), self.id(3, R - 1, 1), self.id(4, 0, 0), self.id(4, 0, 1), self.id(5, R - 2, R - 1), self.id(5, R - 1, R - 1)), # x, y = R-1, R-1 (self.id(3, R - 2, R - 1), self.id(3, R - 2, R - 2), self.id(3, R - 1, R - 2), self.id(4, 0, R - 2), self.id(4, 0, R - 1), self.id(0, R - 2, 0), self.id(0, R - 1, 0)) ), # root pixel 4 ( # x, y = 0, 0 (self.id(4, 1, 0), self.id(4, 1, 1), self.id(4, 0, 1), self.id(3, R - 1, 0), self.id(3, R - 1, 1), self.id(5, R - 1, R - 2), self.id(5, R - 1, R - 1)), # x, y = 0, R-1 (self.id(4, 1, R - 1), self.id(4, 1, R - 2), self.id(4, 0, R - 2), self.id(3, R - 1, R - 2), self.id(3, R - 1, R - 1), self.id(0, R - 1, 0), self.id(0, R - 1, 1)), # x, y = R-1, 0 (self.id(4, R - 2, 0), self.id(4, R - 2, 1), self.id(4, R - 1, 1), self.id(1, 0, 0), self.id(1, 0, 1), self.id(5, R - 1, 0), self.id(5, R - 1, 1)), # x, y = R-1, R-1 (self.id(4, R - 2, R - 1), self.id(4, R - 2, R - 2), self.id(4, R - 1, R - 2), self.id(1, 0, R - 2), self.id(1, 0, R - 1), self.id(0, R - 1, R - 2), self.id(0, R - 1, R - 1)) ), # root pixel 5 ( # x, y = 0, 0 (self.id(5, 1, 0), self.id(5, 1, 1), self.id(5, 0, 1), self.id(1, R - 2, 0), self.id(1, R - 1, 0), self.id(2, 0, 0), self.id(2, 1, 0)), # x, y = 0, R-1 (self.id(5, 1, R - 1), self.id(5, 1, R - 2), self.id(5, 0, R - 2), self.id(2, R - 2, 0), self.id(2, R - 1, 0), self.id(3, 0, 0), self.id(3, 1, 0)), # x, y = R-1, 0 (self.id(5, R - 2, 0), self.id(5, R - 2, 1), self.id(5, R - 1, 1), self.id(1, 0, 0), self.id(1, 1, 0), self.id(4, R - 2, 0), self.id(4, R - 1, 0)), # x, y = R-1, R-1 (self.id(5, R - 2, R - 1), self.id(5, R - 2, R - 2), self.id(5, R - 1, R - 2), self.id(3, R - 2, 0), self.id(3, R - 1, 0), self.id(4, 0, 0), self.id(4, 1, 0)) ), )
def verifySkyTiles(cursor, qsp, kind, wcsMap, wcsList, inButler): """Verifies that the sky-tile to CCD mapping in the registry used by inButler is correct. Also computes statistics on the angular separation between amp corners pre and post WCS determination. """ if kind == 'imsim': geomPolicy = cameraGeomUtils.getGeomPolicy( pexPolicy.Policy.createPolicy( pexPolicy.DefaultPolicyFile("obs_lsstSim", "Full_STA_geom.paf", "description"))) else: geomPolicy = cameraGeomUtils.getGeomPolicy( pexPolicy.Policy.createPolicy( pexPolicy.DefaultPolicyFile("obs_cfht", "Full_Megacam_geom.paf", "megacam/description"))) camera = cameraGeomUtils.makeCamera(geomPolicy) ampDiskLayout = {} for p in geomPolicy.get("CcdDiskLayout").getArray("Amp"): ampDiskLayout[p.get("serial")] = (p.get("flipLR"), p.get("flipTB")) actualTiles = set(wcsMap.keys()) processedTiles = set(inButler.queryMetadata("raw", "skyTile")) emptyTiles = processedTiles - actualTiles missedTiles = actualTiles - processedTiles if len(emptyTiles) > 0: print dedent("""\ %d tiles not overlapping any Science CCDs (calexps) were found. This can happen if the run didn't span all the input amps in the registry, or when a raw amp gets shifted off a tile after WCS determination. So this is normal, but note that empty sky-tiles should not contain any SourceAssoc output.""" % len(emptyTiles)) for tile in emptyTiles: print "\t%d" % tile if len(missedTiles) > 0: print dedent("""\ %d tiles overlapping Science CCDs (calexps) were not processed. This indicates the raw amp padding radius used by the registry creation script is too small!""" % len(missedTiles)) for tile in missedTiles: print "\t%d" % tile print "----" missedAmps = set() for tile in actualTiles: tileGeom = qsp.getGeometry(tile) tileAmps = set() if kind == "imsim": for visit, raft, sensor, channel in inButler.queryMetadata( "raw", "channel", ("visit", "raft", "sensor", "channel"), skyTile=tile, snap=0): s1, comma, s2 = sensor c1, comma, c2 = channel raftNum = lsstSimRafts.index(raft) ccdNum = int(s1) * 3 + int(s2) ampNum = (int(c2) << 3) + int(c1) tileAmps.add((long(visit), raftNum, ccdNum, ampNum)) else: for visit, ccd, amp in inButler.queryMetadata( "raw", "amp", ("visit", "ccd", "amp"), skyTile=tile): tileAmps.add((long(visit), 0, int(ccd), int(amp))) for ccdData in wcsMap[tile]: amps = getAmps(ccdData, camera, ampDiskLayout, cursor, kind) for ampData in amps: sciCoords = [ ccdData[0].pixelToSky(c[0], c[1]) for c in ampData[2] ] # find amps that should have been included in tile ampSerial = ampData[0].getId().getSerial() if (tile in missedTiles or not (ccdData[2], ccdData[3], ccdData[4], ampSerial) in tileAmps): if tileGeom.intersects(coordsToPolygon(sciCoords)): print( "Tile %d missing visit %s raft %s ccd %s amp %d" % (tile, str(ccdData[1]), str( ccdData[2]), str(ccdData[3]), ampSerial)) missedAmps.add((ccdData[1], ampSerial)) print "----" print "Computing statistics on angular separation between" print "science corners and raw corners/edges..." nSamples = 0 maxDist, maxEdgeDist, maxMissEdgeDist = 0.0, 0.0, 0.0 meanDist, meanEdgeDist, meanMissEdgeDist = 0.0, 0.0, 0.0 minDist, minEdgeDist, minMissEdgeDist = 180.0, 180.0, 180.0 for ccdData in wcsList: amps = getAmps(ccdData, camera, ampDiskLayout, cursor, kind) for ampData in amps: sciCoords = [ccdData[0].pixelToSky(c[0], c[1]) for c in ampData[2]] rawCoords = [ampData[1].pixelToSky(c[0], c[1]) for c in ampData[3]] rawPoly = coordsToPolygon(rawCoords) v = rawPoly.getVertices() e = rawPoly.getEdges() for raw, sci in izip(rawCoords, sciCoords): sciPos = tuple(sci.getVector()) dist = geom.cartesianAngularSep(tuple(raw.getVector()), tuple(sciPos)) minDist = min(dist, minDist) maxDist = max(dist, maxDist) meanDist += dist edgeDist = 180.0 for i in xrange(len(v)): edgeDist = min( edgeDist, geom.minEdgeSep(sciPos, e[i], v[i - 1], v[i])) minEdgeDist = min(edgeDist, minEdgeDist) maxEdgeDist = max(edgeDist, maxEdgeDist) meanEdgeDist += edgeDist nSamples += 1 if (ccdData[1], ampData[0].getId().getSerial()) in missedAmps: minMissEdgeDist = min(edgeDist, minMissEdgeDist) maxMissEdgeDist = max(edgeDist, maxMissEdgeDist) meanMissEdgeDist += edgeDist meanDist /= nSamples meanEdgeDist /= nSamples print "----" print "Number of amps examined: %d" % nSamples print "Min raw to science corner distance: %.6f deg" % minDist print "Max raw to science corner distance: %.6f deg" % maxDist print "Mean raw to science corner distance: %.6f deg" % meanDist print "Min min science corner to raw amp edge distance: %.6f deg" % minEdgeDist print "Max min science corner to raw amp edge distance: %.6f deg" % maxEdgeDist print "Mean min science corner to raw amp edge distance: %.6f deg" % meanEdgeDist print "Number of amps missed: %d" % len(missedAmps) if len(missedAmps) > 0: meanMissEdgeDist /= len(missedAmps) print "Min min science corner to missed raw amp edge distance: %.6f deg" % minMissEdgeDist print "Max min science corner to missed raw amp edge distance: %.6f deg" % maxMissEdgeDist print "Mean min science corner to missed raw amp edge distance: %.6f deg" % meanMissEdgeDist
def verifySkyTiles(cursor, qsp, kind, wcsMap, wcsList, inButler): """Verifies that the sky-tile to CCD mapping in the registry used by inButler is correct. Also computes statistics on the angular separation between amp corners pre and post WCS determination. """ if kind == 'imsim': geomPolicy = cameraGeomUtils.getGeomPolicy(pexPolicy.Policy.createPolicy( pexPolicy.DefaultPolicyFile("obs_lsstSim", "Full_STA_geom.paf", "description"))) else: geomPolicy = cameraGeomUtils.getGeomPolicy(pexPolicy.Policy.createPolicy( pexPolicy.DefaultPolicyFile("obs_cfht", "Full_Megacam_geom.paf", "megacam/description"))) camera = cameraGeomUtils.makeCamera(geomPolicy) ampDiskLayout = {} for p in geomPolicy.get("CcdDiskLayout").getArray("Amp"): ampDiskLayout[p.get("serial")] = (p.get("flipLR"), p.get("flipTB")) actualTiles = set(wcsMap.keys()) processedTiles = set(inButler.queryMetadata("raw", "skyTile")) emptyTiles = processedTiles - actualTiles missedTiles = actualTiles - processedTiles if len(emptyTiles) > 0: print dedent("""\ %d tiles not overlapping any Science CCDs (calexps) were found. This can happen if the run didn't span all the input amps in the registry, or when a raw amp gets shifted off a tile after WCS determination. So this is normal, but note that empty sky-tiles should not contain any SourceAssoc output.""" % len(emptyTiles)) for tile in emptyTiles: print "\t%d" % tile if len(missedTiles) > 0: print dedent("""\ %d tiles overlapping Science CCDs (calexps) were not processed. This indicates the raw amp padding radius used by the registry creation script is too small!""" % len(missedTiles)) for tile in missedTiles: print "\t%d" % tile print "----" missedAmps = set() for tile in actualTiles: tileGeom = qsp.getGeometry(tile) tileAmps = set() if kind == "imsim": for visit, raft, sensor, channel in inButler.queryMetadata( "raw", "channel", ("visit", "raft", "sensor", "channel"), skyTile=tile, snap=0): s1, comma, s2 = sensor c1, comma, c2 = channel raftNum = lsstSimRafts.index(raft) ccdNum = int(s1) * 3 + int(s2) ampNum = (int(c2) << 3) + int(c1) tileAmps.add((long(visit), raftNum, ccdNum, ampNum)) else: for visit, ccd, amp in inButler.queryMetadata( "raw", "amp", ("visit", "ccd", "amp"), skyTile=tile): tileAmps.add((long(visit), 0, int(ccd), int(amp))) for ccdData in wcsMap[tile]: amps = getAmps(ccdData, camera, ampDiskLayout, cursor, kind) for ampData in amps: sciCoords = [ccdData[0].pixelToSky(c[0], c[1]) for c in ampData[2]] # find amps that should have been included in tile ampSerial = ampData[0].getId().getSerial() if (tile in missedTiles or not (ccdData[2], ccdData[3], ccdData[4], ampSerial) in tileAmps): if tileGeom.intersects(coordsToPolygon(sciCoords)): print ("Tile %d missing visit %s raft %s ccd %s amp %d" % (tile, str(ccdData[1]), str(ccdData[2]), str(ccdData[3]), ampSerial)) missedAmps.add((ccdData[1], ampSerial)) print "----" print "Computing statistics on angular separation between" print "science corners and raw corners/edges..." nSamples = 0 maxDist, maxEdgeDist, maxMissEdgeDist = 0.0, 0.0, 0.0 meanDist, meanEdgeDist, meanMissEdgeDist = 0.0, 0.0, 0.0 minDist, minEdgeDist, minMissEdgeDist = 180.0, 180.0, 180.0 for ccdData in wcsList: amps = getAmps(ccdData, camera, ampDiskLayout, cursor, kind) for ampData in amps: sciCoords = [ccdData[0].pixelToSky(c[0], c[1]) for c in ampData[2]] rawCoords = [ampData[1].pixelToSky(c[0], c[1]) for c in ampData[3]] rawPoly = coordsToPolygon(rawCoords) v = rawPoly.getVertices() e = rawPoly.getEdges() for raw, sci in izip(rawCoords, sciCoords): sciPos = tuple(sci.getVector()) dist = geom.cartesianAngularSep(tuple(raw.getVector()), tuple(sciPos)) minDist = min(dist, minDist) maxDist = max(dist, maxDist) meanDist += dist edgeDist = 180.0 for i in xrange(len(v)): edgeDist = min(edgeDist, geom.minEdgeSep(sciPos, e[i], v[i - 1], v[i])) minEdgeDist = min(edgeDist, minEdgeDist) maxEdgeDist = max(edgeDist, maxEdgeDist) meanEdgeDist += edgeDist nSamples += 1 if (ccdData[1], ampData[0].getId().getSerial()) in missedAmps: minMissEdgeDist = min(edgeDist, minMissEdgeDist) maxMissEdgeDist = max(edgeDist, maxMissEdgeDist) meanMissEdgeDist += edgeDist meanDist /= nSamples meanEdgeDist /= nSamples print "----" print "Number of amps examined: %d" % nSamples print "Min raw to science corner distance: %.6f deg" % minDist print "Max raw to science corner distance: %.6f deg" % maxDist print "Mean raw to science corner distance: %.6f deg" % meanDist print "Min min science corner to raw amp edge distance: %.6f deg" % minEdgeDist print "Max min science corner to raw amp edge distance: %.6f deg" % maxEdgeDist print "Mean min science corner to raw amp edge distance: %.6f deg" % meanEdgeDist print "Number of amps missed: %d" % len(missedAmps) if len(missedAmps) > 0: meanMissEdgeDist /= len(missedAmps) print "Min min science corner to missed raw amp edge distance: %.6f deg" % minMissEdgeDist print "Max min science corner to missed raw amp edge distance: %.6f deg" % maxMissEdgeDist print "Mean min science corner to missed raw amp edge distance: %.6f deg" % meanMissEdgeDist
def getAllSipWcs(cursor, qsp, kind): """Constructs a Wcs object from each entry in the Science_Ccd_Exposure table. SIP distortion parameters are read in from Science_Ccd_Exposure_Metadata. Returns a 3-tuple with the following contents: - a mapping from sky-tiles to a list of (WCS, filter) tuples for overlapping science CCDs - a list of all (WCS, filter) tuples read in - a bounding circle (lsst.geom.SphericalCircle) for all science CCDs. """ wcsMap = {} wcsList = [] offset = 0 centers = [] radius = 0.0 n = 0 blocksize = 1000 # An alternative would be to create WCSes from FITS metadata or FITS # files themselves, but these aren't always available. while True: cursor.execute( """SELECT scienceCcdExposureId, filterId, raDeSys, equinox, ctype1, ctype2, crpix1, crpix2, crval1, crval2, cd1_1, cd1_2, cd2_1, cd2_2 FROM Science_Ccd_Exposure LIMIT %d, %d """ % (offset, blocksize)) rows = cursor.fetchall() if len(rows) == 0: break # For each CCD in the results, build up a metadata PropertySet # including SIP distortion terms and use it to obtain a WCS. for row in rows: ps = dafBase.PropertySet() ps.setString("RADESYS", row[2]) ps.setDouble("EQUINOX", row[3]) ps.setString("CTYPE1", row[4]) ps.setString("CTYPE2", row[5]) ps.setString("CUNIT1", "deg") ps.setString("CUNIT2", "deg") ps.setDouble("CRPIX1", row[6]) ps.setDouble("CRPIX2", row[7]) ps.setDouble("CRVAL1", row[8]) ps.setDouble("CRVAL2", row[9]) ps.setDouble("CD1_1", row[10]) ps.setDouble("CD1_2", row[11]) ps.setDouble("CD2_1", row[12]) ps.setDouble("CD2_2", row[13]) if row[4].endswith("-SIP") and row[5].endswith("-SIP"): cursor.execute(""" SELECT metadataKey, intValue FROM Science_Ccd_Exposure_Metadata WHERE scienceCcdExposureId = %d AND intValue IS NOT NULL AND metadataKey RLIKE '^[AB]P?_ORDER$' """ % row[0]) for k, v in cursor.fetchall(): ps.setInt(k, v) cursor.execute(""" SELECT metadataKey, doubleValue FROM Science_Ccd_Exposure_Metadata WHERE scienceCcdExposureId = %d AND doubleValue IS NOT NULL AND metadataKey RLIKE '^[AB]P?_[0-9]+_[0-9]+$' """ % row[0]) for k, v in cursor.fetchall(): ps.setDouble(k, v) wcs = afwImage.makeWcs(ps) wcsList.append([wcs, row[1]]) if kind == 'imsim': poly = qs.imageToPolygon(wcs, 4072.0, 4000.0, 0.0) else: poly = qs.imageToPolygon(wcs, 2048.0, 4610.0, 0.0) bc = poly.getBoundingCircle() radius = max(radius, bc.getRadius()) centers.append(geom.cartesianUnitVector(bc.getCenter())) pix = qsp.intersect(poly) for skyTile in pix: if skyTile in wcsMap: wcsMap[skyTile].append([wcs, row[1]]) else: wcsMap[skyTile] = [[wcs, row[1]]] n = offset + len(rows) print "... %d" % n offset += blocksize x, y, z = 0.0, 0.0, 0.0 for v in centers: x += v[0]; y += v[1]; z += v[2] cen = geom.normalize((x, y, z)) r = max(geom.cartesianAngularSep(cen, v) for v in centers) + 2.0*radius return wcsMap, wcsList, geom.SphericalCircle(cen, r)