def get_healpixes_touching_wcs(tan, nside=None, topscale=256.): ''' tan: TanWCS database object. ''' from astrometry.util.starutil_numpy import degrees_between if nside is None: # print 'Pixscale', tan.get_pixscale() nside = 2 ** int(np.round(np.log2(topscale / tan.get_pixscale()))) # print 'Nside', nside nside = int(np.clip(nside, 1, 2**10)) # print 'Nside', nside r1,d1 = healpix_to_radecdeg(0, nside, 0., 0.) r2,d2 = healpix_to_radecdeg(0, nside, 0.5, 0.5) hpradius = degrees_between(r1,d1, r2,d2) # HACK -- padding for squished parallelograms hpradius *= 1.5 r,d,radius = tan.get_center_radecradius() radius = np.hypot(radius, hpradius) hh = healpix_rangesearch_radec(r, d, radius, nside) hh.sort() #print 'Healpixes:', hh return (nside, hh)
def get_healpixes_touching_wcs(tan, nside=None, topscale=256.): ''' tan: TanWCS database object. ''' from astrometry.util.starutil_numpy import degrees_between if nside is None: # print 'Pixscale', tan.get_pixscale() nside = 2**int(np.round(np.log2(topscale / tan.get_pixscale()))) # print 'Nside', nside nside = int(np.clip(nside, 1, 2**10)) # print 'Nside', nside r1, d1 = healpix_to_radecdeg(0, nside, 0., 0.) r2, d2 = healpix_to_radecdeg(0, nside, 0.5, 0.5) hpradius = degrees_between(r1, d1, r2, d2) # HACK -- padding for squished parallelograms hpradius *= 1.5 r, d, radius = tan.get_center_radecradius() radius = np.hypot(radius, hpradius) hh = healpix_rangesearch_radec(r, d, radius, nside) hh.sort() #print 'Healpixes:', hh return (nside, hh)
def ccds_touching_wcs(targetwcs, T, ccdrad=0.17, polygons=True): ''' targetwcs: wcs object describing region of interest T: fits_table object of CCDs ccdrad: radius of CCDs, in degrees. Default 0.17 is for DECam. #If None, computed from T. Returns: index array I of CCDs within range. ''' trad = targetwcs.radius() if ccdrad is None: ccdrad = max(np.sqrt(np.abs(T.cd1_1 * T.cd2_2 - T.cd1_2 * T.cd2_1)) * np.hypot(T.width, T.height) / 2.) rad = trad + ccdrad #r,d = targetwcs.crval r,d = targetwcs.radec_center() #print len(T), 'ccds' #print 'trad', trad, 'ccdrad', ccdrad I = np.flatnonzero(np.abs(T.dec - d) < rad) #print 'Cut to', len(I), 'on Dec' I = I[degrees_between(T.ra[I], T.dec[I], r, d) < rad] #print 'Cut to', len(I), 'on RA,Dec' if not polygons: return I # now check actual polygon intersection tw,th = targetwcs.imagew, targetwcs.imageh targetpoly = [(0.5,0.5),(tw+0.5,0.5),(tw+0.5,th+0.5),(0.5,th+0.5)] cd = targetwcs.get_cd() tdet = cd[0]*cd[3] - cd[1]*cd[2] #print 'tdet', tdet if tdet > 0: targetpoly = list(reversed(targetpoly)) targetpoly = np.array(targetpoly) keep = [] for i in I: W,H = T.width[i],T.height[i] wcs = Tan(*[float(x) for x in [T.crval1[i], T.crval2[i], T.crpix1[i], T.crpix2[i], T.cd1_1[i], T.cd1_2[i], T.cd2_1[i], T.cd2_2[i], W, H]]) cd = wcs.get_cd() wdet = cd[0]*cd[3] - cd[1]*cd[2] #print 'wdet', wdet poly = [] for x,y in [(0.5,0.5),(W+0.5,0.5),(W+0.5,H+0.5),(0.5,H+0.5)]: rr,dd = wcs.pixelxy2radec(x,y) ok,xx,yy = targetwcs.radec2pixelxy(rr,dd) poly.append((xx,yy)) if wdet > 0: poly = list(reversed(poly)) poly = np.array(poly) if polygons_intersect(targetpoly, poly): keep.append(i) I = np.array(keep) #print 'Cut to', len(I), 'on polygons' return I
def ccds_touching_wcs(targetwcs, ccds, ccdrad=0.17, polygons=True): ''' targetwcs: wcs object describing region of interest ccds: fits_table object of CCDs ccdrad: radius of CCDs, in degrees. Default 0.17 is for DECam. #If None, computed from T. Returns: index array I of CCDs within range. ''' trad = targetwcs.radius() if ccdrad is None: ccdrad = max( np.sqrt(np.abs(ccds.cd1_1 * ccds.cd2_2 - ccds.cd1_2 * ccds.cd2_1)) * np.hypot(ccds.width, ccds.height) / 2.) rad = trad + ccdrad r, d = targetwcs.radec_center() I, = np.nonzero(np.abs(ccds.dec - d) < rad) I = I[np.atleast_1d(degrees_between(ccds.ra[I], ccds.dec[I], r, d) < rad)] if not polygons: return I # now check actual polygon intersection tw, th = targetwcs.imagew, targetwcs.imageh targetpoly = [(0.5, 0.5), (tw + 0.5, 0.5), (tw + 0.5, th + 0.5), (0.5, th + 0.5)] cd = targetwcs.get_cd() tdet = cd[0] * cd[3] - cd[1] * cd[2] if tdet > 0: targetpoly = list(reversed(targetpoly)) targetpoly = np.array(targetpoly) keep = [] for i in I: W, H = ccds.width[i], ccds.height[i] wcs = Tan(*[ float(x) for x in [ ccds.crval1[i], ccds.crval2[i], ccds.crpix1[i], ccds.crpix2[i], ccds.cd1_1[i], ccds.cd1_2[i], ccds.cd2_1[i], ccds.cd2_2[i], W, H ] ]) cd = wcs.get_cd() wdet = cd[0] * cd[3] - cd[1] * cd[2] poly = [] for x, y in [(0.5, 0.5), (W + 0.5, 0.5), (W + 0.5, H + 0.5), (0.5, H + 0.5)]: rr, dd = wcs.pixelxy2radec(x, y) ok, xx, yy = targetwcs.radec2pixelxy(rr, dd) poly.append((xx, yy)) if wdet > 0: poly = list(reversed(poly)) poly = np.array(poly) if polygons_intersect(targetpoly, poly): keep.append(i) I = np.array(keep) return I
def cat_sdss(req, ver): import json import numpy as np from astrometry.util.starutil_numpy import degrees_between, radectoxyz, xyztoradec from map.views import sdss_ccds_near from astrometry.util.fits import fits_table, merge_tables tag = 'sdss-cat' ralo = float(req.GET['ralo']) rahi = float(req.GET['rahi']) declo = float(req.GET['declo']) dechi = float(req.GET['dechi']) ver = int(ver) if not ver in catversions[tag]: raise RuntimeError('Invalid version %i for tag %s' % (ver, tag)) rad = degrees_between(ralo, declo, rahi, dechi) / 2. xyz1 = radectoxyz(ralo, declo) xyz2 = radectoxyz(rahi, dechi) xyz = (xyz1 + xyz2) xyz /= np.sqrt(np.sum(xyz**2)) rc,dc = xyztoradec(xyz) rad = rad + np.hypot(10.,14.)/2./60. ccds = sdss_ccds_near(rc[0], dc[0], rad) if ccds is None: print('No SDSS CCDs nearby') return HttpResponse(json.dumps(dict(rd=[])), content_type='application/json') print(len(ccds), 'SDSS CCDs') T = [] for ccd in ccds: # env/BOSS_PHOTOOBJ/301/2073/3/photoObj-002073-3-0088.fits fn = os.path.join(settings.SDSS_BASEDIR, 'env', 'BOSS_PHOTOOBJ', str(ccd.rerun), str(ccd.run), str(ccd.camcol), 'photoObj-%06i-%i-%04i.fits' % (ccd.run, ccd.camcol, ccd.field)) print('Reading', fn) T.append(fits_table(fn, columns='ra dec objid mode objc_type objc_flags objc_flags nchild tai expflux devflux psfflux cmodelflux fracdev mjd'.split())) T = merge_tables(T) T.cut((T.dec >= declo) * (T.dec <= dechi)) # FIXME T.cut((T.ra >= ralo) * (T.ra <= rahi)) # primary T.cut(T.mode == 1) types = ['P' if t == 6 else 'C' for t in T.objc_type] fluxes = [p if t == 6 else c for t,p,c in zip(T.objc_type, T.psfflux, T.cmodelflux)] return HttpResponse(json.dumps(dict( rd=[(float(o.ra),float(o.dec)) for o in T], sourcetype=types, fluxes = [dict(u=float(f[0]), g=float(f[1]), r=float(f[2]), i=float(f[3]), z=float(f[4])) for f in fluxes], )), content_type='application/json')
def ccds_touching_wcs(targetwcs, ccds, ccdrad=0.17, polygons=True): ''' targetwcs: wcs object describing region of interest ccds: fits_table object of CCDs ccdrad: radius of CCDs, in degrees. Default 0.17 is for DECam. #If None, computed from T. Returns: index array I of CCDs within range. ''' trad = targetwcs.radius() if ccdrad is None: ccdrad = max(np.sqrt(np.abs(ccds.cd1_1 * ccds.cd2_2 - ccds.cd1_2 * ccds.cd2_1)) * np.hypot(ccds.width, ccds.height) / 2.) rad = trad + ccdrad r,d = targetwcs.radec_center() I, = np.nonzero(np.abs(ccds.dec - d) < rad) I = I[np.atleast_1d(degrees_between(ccds.ra[I], ccds.dec[I], r, d) < rad)] if not polygons: return I # now check actual polygon intersection tw,th = targetwcs.imagew, targetwcs.imageh targetpoly = [(0.5,0.5),(tw+0.5,0.5),(tw+0.5,th+0.5),(0.5,th+0.5)] cd = targetwcs.get_cd() tdet = cd[0]*cd[3] - cd[1]*cd[2] if tdet > 0: targetpoly = list(reversed(targetpoly)) targetpoly = np.array(targetpoly) keep = [] for i in I: W,H = ccds.width[i],ccds.height[i] wcs = Tan(*[float(x) for x in [ccds.crval1[i], ccds.crval2[i], ccds.crpix1[i], ccds.crpix2[i], ccds.cd1_1[i], ccds.cd1_2[i], ccds.cd2_1[i], ccds.cd2_2[i], W, H]]) cd = wcs.get_cd() wdet = cd[0]*cd[3] - cd[1]*cd[2] poly = [] for x,y in [(0.5,0.5),(W+0.5,0.5),(W+0.5,H+0.5),(0.5,H+0.5)]: rr,dd = wcs.pixelxy2radec(x,y) ok,xx,yy = targetwcs.radec2pixelxy(rr,dd) poly.append((xx,yy)) if wdet > 0: poly = list(reversed(poly)) poly = np.array(poly) if polygons_intersect(targetpoly, poly): keep.append(i) I = np.array(keep) return I
def unwise_tiles_touching_wcs(wcs, polygons=True): ''' Returns a FITS table (with RA,Dec,coadd_id) of unWISE tiles ''' from astrometry.util.miscutils import polygons_intersect from astrometry.util.starutil_numpy import degrees_between from pkg_resources import resource_filename atlasfn = resource_filename('legacypipe', 'data/wise-tiles.fits') T = fits_table(atlasfn) trad = wcs.radius() wrad = np.sqrt(2.) / 2. * 2048 * 2.75 / 3600. rad = trad + wrad r, d = wcs.radec_center() I, = np.nonzero(np.abs(T.dec - d) < rad) I = I[degrees_between(T.ra[I], T.dec[I], r, d) < rad] if not polygons: return T[I] # now check actual polygon intersection tw, th = wcs.imagew, wcs.imageh targetpoly = [(0.5, 0.5), (tw + 0.5, 0.5), (tw + 0.5, th + 0.5), (0.5, th + 0.5)] cd = wcs.get_cd() tdet = cd[0] * cd[3] - cd[1] * cd[2] if tdet > 0: targetpoly = list(reversed(targetpoly)) targetpoly = np.array(targetpoly) keep = [] for i in I: wwcs = unwise_tile_wcs(T.ra[i], T.dec[i]) cd = wwcs.get_cd() wdet = cd[0] * cd[3] - cd[1] * cd[2] H, W = wwcs.shape poly = [] for x, y in [(0.5, 0.5), (W + 0.5, 0.5), (W + 0.5, H + 0.5), (0.5, H + 0.5)]: rr, dd = wwcs.pixelxy2radec(x, y) _, xx, yy = wcs.radec2pixelxy(rr, dd) poly.append((xx, yy)) if wdet > 0: poly = list(reversed(poly)) poly = np.array(poly) if polygons_intersect(targetpoly, poly): keep.append(i) I = np.array(keep) return T[I]
def unWISE2BOSS(tilename, channelNumber, BOSSra, BOSSdec, plot=True, vmin=-50,vmax=300): # Input tile name RA = BOSSra.copy() DEC =BOSSdec.copy() tileName = tilename channel = channelNumber # Contructing file address fileaddress = get_unwise_filename(tileName, channel) tol = 2.00 if 'm' in tileName: ra = float(tileName.split('m')[0])/10.0 dec = float(tileName.split('m')[1])/10.0 else: ra = float(tileName.split('p')[0])/10.0 dec = float(tileName.split('p')[1])/10.0 iBool = degrees_between(ra, dec, RA,DEC) <tol # Getting x,y positions of the objects near by the center of the tile. wcs = Tan(fileaddress) ok, x, y = wcs.radec2pixelxy(RA[np.where(iBool==True)], DEC[np.where(iBool==True)]) a = np.isnan(x) b = np.isnan(y) x[a] = 0 y[b] = 0 iBool = (1<x)&(x<2048)&(1<y)&(y<2048) x -= 1 y -= 1 x=x[iBool] y=y[iBool] #From the result, I choose to use 1504p196 if plot: objs1 = fitsio.FITS(fileaddress) blockImage =objs1[0][:,:] plt.imshow(blockImage, cmap='gray', vmin=vmin, vmax=vmax, origin='lower',interpolation='nearest') # 10/8/2015: Becareful about the orientation of the matrix. plt.scatter(x, y, facecolors='none', edgecolors='r',s=100) plt.show() #Return RA/DEC return x, y
def unwise_tiles_touching_wcs(wcs, polygons=True): ''' Returns a FITS table (with RA,Dec,coadd_id) of unWISE tiles ''' atlasfn = os.path.join(os.path.dirname(__file__), 'allsky-atlas.fits') T = fits_table(atlasfn) trad = wcs.radius() wrad = np.sqrt(2.) / 2. * 2048 * 2.75 / 3600. rad = trad + wrad r, d = wcs.radec_center() I, = np.nonzero(np.abs(T.dec - d) < rad) I = I[degrees_between(T.ra[I], T.dec[I], r, d) < rad] if not polygons: return T[I] # now check actual polygon intersection tw, th = wcs.imagew, wcs.imageh targetpoly = [(0.5, 0.5), (tw + 0.5, 0.5), (tw + 0.5, th + 0.5), (0.5, th + 0.5)] cd = wcs.get_cd() tdet = cd[0] * cd[3] - cd[1] * cd[2] if tdet > 0: targetpoly = list(reversed(targetpoly)) targetpoly = np.array(targetpoly) keep = [] for i in I: wwcs = unwise_tile_wcs(T.ra[i], T.dec[i]) cd = wwcs.get_cd() wdet = cd[0] * cd[3] - cd[1] * cd[2] H, W = wwcs.shape poly = [] for x, y in [(0.5, 0.5), (W + 0.5, 0.5), (W + 0.5, H + 0.5), (0.5, H + 0.5)]: rr, dd = wwcs.pixelxy2radec(x, y) ok, xx, yy = wcs.radec2pixelxy(rr, dd) poly.append((xx, yy)) if wdet > 0: poly = list(reversed(poly)) poly = np.array(poly) if polygons_intersect(targetpoly, poly): keep.append(i) I = np.array(keep) return T[I]
def cat_kd(req, ver, tag, fn): tag = 'spec' ralo = float(req.GET['ralo']) rahi = float(req.GET['rahi']) declo = float(req.GET['declo']) dechi = float(req.GET['dechi']) ver = int(ver) if not ver in catversions[tag]: raise RuntimeError('Invalid version %i for tag %s' % (ver, tag)) import numpy as np from astrometry.util.fits import fits_table, merge_tables from astrometry.libkd.spherematch import tree_open, tree_search_radec from astrometry.util.starutil_numpy import radectoxyz, xyztoradec, degrees_between xyz1 = radectoxyz(ralo, declo) xyz2 = radectoxyz(rahi, dechi) xyz = (xyz1 + xyz2) / 2. xyz /= np.sqrt(np.sum(xyz**2)) rc, dc = xyztoradec(xyz) rc = rc[0] dc = dc[0] rad = degrees_between(rc, dc, ralo, declo) kd = tree_open(fn) I = tree_search_radec(kd, rc, dc, rad) print('Matched', len(I), 'from', fn) if len(I) == 0: return None T = fits_table(fn, rows=I) debug(len(T), 'spectra') if ralo > rahi: # RA wrap T.cut( np.logical_or(T.ra > ralo, T.ra < rahi) * (T.dec > declo) * (T.dec < dechi)) else: T.cut( (T.ra > ralo) * (T.ra < rahi) * (T.dec > declo) * (T.dec < dechi)) debug(len(T), 'in cut') return T
def unwise_tiles_touching_wcs(wcs, polygons=True): ''' Returns a FITS table (with RA,Dec,coadd_id) of unWISE tiles ''' atlasfn = os.path.join(os.path.dirname(__file__), 'allsky-atlas.fits') T = fits_table(atlasfn) trad = wcs.radius() wrad = np.sqrt(2.)/2. * 2048 * 2.75/3600. rad = trad + wrad r,d = wcs.radec_center() I, = np.nonzero(np.abs(T.dec - d) < rad) I = I[degrees_between(T.ra[I], T.dec[I], r, d) < rad] if not polygons: return T[I] # now check actual polygon intersection tw,th = wcs.imagew, wcs.imageh targetpoly = [(0.5,0.5),(tw+0.5,0.5),(tw+0.5,th+0.5),(0.5,th+0.5)] cd = wcs.get_cd() tdet = cd[0]*cd[3] - cd[1]*cd[2] if tdet > 0: targetpoly = list(reversed(targetpoly)) targetpoly = np.array(targetpoly) keep = [] for i in I: wwcs = unwise_tile_wcs(T.ra[i], T.dec[i]) cd = wwcs.get_cd() wdet = cd[0]*cd[3] - cd[1]*cd[2] H,W = wwcs.shape poly = [] for x,y in [(0.5,0.5),(W+0.5,0.5),(W+0.5,H+0.5),(0.5,H+0.5)]: rr,dd = wwcs.pixelxy2radec(x,y) ok,xx,yy = wcs.radec2pixelxy(rr,dd) poly.append((xx,yy)) if wdet > 0: poly = list(reversed(poly)) poly = np.array(poly) if polygons_intersect(targetpoly, poly): keep.append(i) I = np.array(keep) return T[I]
def bricks_touching_radec_box(self, bricks, ralo, rahi, declo, dechi): ''' Returns an index vector of the bricks that touch the given RA,Dec box. ''' if bricks is None: bricks = self.get_bricks_readonly() if self.cache_tree and bricks == self.bricks: from astrometry.libkd.spherematch import tree_build_radec, tree_search_radec # Use kdtree if self.bricktree is None: self.bricktree = tree_build_radec(bricks.ra, bricks.dec) # brick size radius = np.sqrt(2.)/2. * self.bricksize # + RA,Dec box size radius = radius + degrees_between(ralo, declo, rahi, dechi) / 2. dec = (dechi + declo) / 2. c = (np.cos(np.deg2rad(rahi)) + np.cos(np.deg2rad(ralo))) / 2. s = (np.sin(np.deg2rad(rahi)) + np.sin(np.deg2rad(ralo))) / 2. ra = np.rad2deg(np.arctan2(s, c)) J = tree_search_radec(self.bricktree, ra, dec, radius) I = J[np.nonzero((bricks.ra1[J] <= rahi ) * (bricks.ra2[J] >= ralo) * (bricks.dec1[J] <= dechi) * (bricks.dec2[J] >= declo))[0]] return I if rahi < ralo: # Wrap-around print('In Dec slice:', len(np.flatnonzero((bricks.dec1 <= dechi) * (bricks.dec2 >= declo)))) print('Above RAlo=', ralo, ':', len(np.flatnonzero(bricks.ra2 >= ralo))) print('Below RAhi=', rahi, ':', len(np.flatnonzero(bricks.ra1 <= rahi))) print('In RA slice:', len(np.nonzero(np.logical_or(bricks.ra2 >= ralo, bricks.ra1 <= rahi)))) I, = np.nonzero(np.logical_or(bricks.ra2 >= ralo, bricks.ra1 <= rahi) * (bricks.dec1 <= dechi) * (bricks.dec2 >= declo)) print('In RA&Dec slice', len(I)) else: I, = np.nonzero((bricks.ra1 <= rahi ) * (bricks.ra2 >= ralo) * (bricks.dec1 <= dechi) * (bricks.dec2 >= declo)) return I
def bricks_touching_radec_box(self, bricks, ralo, rahi, declo, dechi): ''' Returns an index vector of the bricks that touch the given RA,Dec box. ''' if bricks is None: bricks = self.get_bricks_readonly() if self.cache_tree and bricks == self.bricks: from astrometry.libkd.spherematch import tree_build_radec, tree_search_radec # Use kdtree if self.bricktree is None: self.bricktree = tree_build_radec(bricks.ra, bricks.dec) # brick size radius = np.sqrt(2.)/2. * self.bricksize # + RA,Dec box size radius = radius + degrees_between(ralo, declo, rahi, dechi) / 2. dec = (dechi + declo) / 2. c = (np.cos(np.deg2rad(rahi)) + np.cos(np.deg2rad(ralo))) / 2. s = (np.sin(np.deg2rad(rahi)) + np.sin(np.deg2rad(ralo))) / 2. ra = np.rad2deg(np.arctan2(s, c)) J = tree_search_radec(self.bricktree, ra, dec, radius) I = J[np.nonzero((bricks.ra1[J] <= rahi ) * (bricks.ra2[J] >= ralo) * (bricks.dec1[J] <= dechi) * (bricks.dec2[J] >= declo))[0]] return I if rahi < ralo: # Wrap-around print('In Dec slice:', len(np.flatnonzero((bricks.dec1 <= dechi) * (bricks.dec2 >= declo)))) print('Above RAlo=', ralo, ':', len(np.flatnonzero(bricks.ra2 >= ralo))) print('Below RAhi=', rahi, ':', len(np.flatnonzero(bricks.ra1 <= rahi))) print('In RA slice:', len(np.nonzero(np.logical_or(bricks.ra2 >= ralo, bricks.ra1 <= rahi)))) I, = np.nonzero(np.logical_or(bricks.ra2 >= ralo, bricks.ra1 <= rahi) * (bricks.dec1 <= dechi) * (bricks.dec2 >= declo)) print('In RA&Dec slice', len(I)) else: I, = np.nonzero((bricks.ra1 <= rahi ) * (bricks.ra2 >= ralo) * (bricks.dec1 <= dechi) * (bricks.dec2 >= declo)) return I
def cat_targets_dr2(req, ver): import json tag = 'targets-dr2' ralo = float(req.GET['ralo']) rahi = float(req.GET['rahi']) declo = float(req.GET['declo']) dechi = float(req.GET['dechi']) ver = int(ver) if not ver in catversions[tag]: raise RuntimeError('Invalid version %i for tag %s' % (ver, tag)) from astrometry.util.fits import fits_table, merge_tables import numpy as np from cat.models import DR2_Target as Target from astrometry.util.starutil_numpy import radectoxyz, xyztoradec, degrees_between xyz1 = radectoxyz(ralo, declo) xyz2 = radectoxyz(rahi, dechi) xyz = (xyz1 + xyz2) / 2. xyz /= np.sqrt(np.sum(xyz**2)) rc, dc = xyztoradec(xyz) rc = rc[0] dc = dc[0] rad = degrees_between(rc, dc, ralo, declo) objs = Target.objects.extra(where=[ 'q3c_radial_query(target.ra, target.dec, %.4f, %.4f, %g)' % (rc, dc, rad * 1.01) ]) print('Got', objs.count(), 'targets') print('types:', np.unique([o.type for o in objs])) print('versions:', np.unique([o.version for o in objs])) return HttpResponse(json.dumps( dict( rd=[(float(o.ra), float(o.dec)) for o in objs], name=[o.type for o in objs], )), content_type='application/json')
def cat_kd(req, ver, tag, fn): tag = 'spec' ralo = float(req.GET['ralo']) rahi = float(req.GET['rahi']) declo = float(req.GET['declo']) dechi = float(req.GET['dechi']) ver = int(ver) if not ver in catversions[tag]: raise RuntimeError('Invalid version %i for tag %s' % (ver, tag)) import numpy as np from astrometry.util.fits import fits_table, merge_tables from astrometry.libkd.spherematch import tree_open, tree_search_radec from astrometry.util.starutil_numpy import radectoxyz, xyztoradec, degrees_between xyz1 = radectoxyz(ralo, declo) xyz2 = radectoxyz(rahi, dechi) xyz = (xyz1 + xyz2)/2. xyz /= np.sqrt(np.sum(xyz**2)) rc,dc = xyztoradec(xyz) rc = rc[0] dc = dc[0] rad = degrees_between(rc, dc, ralo, declo) kd = tree_open(fn) I = tree_search_radec(kd, rc, dc, rad) print('Matched', len(I), 'from', fn) if len(I) == 0: return None T = fits_table(fn, rows=I) debug(len(T), 'spectra') if ralo > rahi: # RA wrap T.cut(np.logical_or(T.ra > ralo, T.ra < rahi) * (T.dec > declo) * (T.dec < dechi)) else: T.cut((T.ra > ralo) * (T.ra < rahi) * (T.dec > declo) * (T.dec < dechi)) debug(len(T), 'in cut') return T
def cat_targets_dr2(req, ver): import json tag = 'targets-dr2' ralo = float(req.GET['ralo']) rahi = float(req.GET['rahi']) declo = float(req.GET['declo']) dechi = float(req.GET['dechi']) ver = int(ver) if not ver in catversions[tag]: raise RuntimeError('Invalid version %i for tag %s' % (ver, tag)) from astrometry.util.fits import fits_table, merge_tables import numpy as np from cat.models import DR2_Target as Target from astrometry.util.starutil_numpy import radectoxyz, xyztoradec, degrees_between xyz1 = radectoxyz(ralo, declo) xyz2 = radectoxyz(rahi, dechi) xyz = (xyz1 + xyz2)/2. xyz /= np.sqrt(np.sum(xyz**2)) rc,dc = xyztoradec(xyz) rc = rc[0] dc = dc[0] rad = degrees_between(rc, dc, ralo, declo) objs = Target.objects.extra(where=[ 'q3c_radial_query(target.ra, target.dec, %.4f, %.4f, %g)' % (rc, dc, rad * 1.01)]) print('Got', objs.count(), 'targets') print('types:', np.unique([o.type for o in objs])) print('versions:', np.unique([o.version for o in objs])) return HttpResponse(json.dumps(dict( rd=[(float(o.ra),float(o.dec)) for o in objs], name=[o.type for o in objs], )), content_type='application/json')
def BOSS2unWISE(ra, dec, tileID, tileRA, tileDEC,plot=True): tol = 1.56 iBool = degrees_between(ra, dec, tileRA,tileDEC) <tol tilesCandidates = tileID[iBool] tiles = [] for tName in tilesCandidates: channel = 'w1' fileaddress = get_unwise_filename(tName, channel) wcs = Tan(fileaddress) ok, x, y = wcs.radec2pixelxy(ra, dec) x -= 1 y -= 1 iTile= (0<x) &(x<2047)&(0<y)&(y<2047) if iTile: tiles = np.append(tiles, [tName]) if plot: objs1 = fitsio.FITS(fileaddress) blockImage =objs1[0][:,:] plt.imshow(blockImage, cmap='gray', vmin=-50, vmax=300, origin='lower',interpolation='nearest') # 10/8/2015: Becareful about the orientation of the matrix. plt.scatter(x, y, facecolors='none', edgecolors='r',s=100) plt.show() return tiles
def read_star_clusters(targetwcs): """The code to generate the NGC-star-clusters-fits catalog is in legacypipe/bin/build-cluster-catalog.py. """ from pkg_resources import resource_filename from astrometry.util.starutil_numpy import degrees_between clusterfile = resource_filename('legacypipe', 'data/NGC-star-clusters.fits') debug('Reading {}'.format(clusterfile)) clusters = fits_table(clusterfile, columns=['ra', 'dec', 'radius', 'type', 'ba', 'pa']) clusters.ref_id = np.arange(len(clusters)) radius = 1. rc, dc = targetwcs.radec_center() d = degrees_between(rc, dc, clusters.ra, clusters.dec) clusters.cut(d < radius) if len(clusters) == 0: return None debug('Cut to {} star cluster(s) within the brick'.format(len(clusters))) clusters.ref_cat = np.array(['CL'] * len(clusters)) # Radius in degrees clusters.radius = clusters.radius clusters.radius[np.logical_not(np.isfinite(clusters.radius))] = 1. / 60. # Set isbright=True clusters.isbright = np.zeros(len(clusters), bool) clusters.iscluster = np.ones(len(clusters), bool) clusters.sources = np.array([None] * len(clusters)) return clusters
def distanceFrom(self, pos): from astrometry.util.starutil_numpy import degrees_between return degrees_between(self.ra, self.dec, pos.ra, pos.dec)
H, W = 100, 100 ## WCS. targetwcs = Tan(ra, dec, W / 2. + 0.5, H / 2. + 0.5, -ps, 0., 0., ps, float(W), float(H)) wcs = tractor.ConstantFitsWcs(targetwcs) targetrd = np.array([ targetwcs.pixelxy2radec(x, y) for x, y in [(1, 1), (W, 1), (W, H), (1, H), (1, 1)] ]) ## Bricks. B = survey.get_bricks_readonly() B.about() B.cut(np.argsort(degrees_between(ra, dec, B.ra, B.dec))) brick = B[0] tims = [] for band in bands: sky_level_sig, psf_fwhm, zpt, psf_theta, psf_ell = unpack_ccds( band=band, index=0) ## Gaussian approx. psf_sigma = psf_fwhm / (2. * np.sqrt(2. * np.log(2.))) psf_sigma2 = psf_sigma**2. psfnorm = 1. / (2. * np.sqrt(np.pi) * psf_sigma)
def annotate_one_ccd(X): ccd, survey, normalizePsf, carryOn = X print('Annotating CCD', ccd.image_filename.strip(), 'expnum', ccd.expnum, 'CCD', ccd.ccdname) result = {} try: im = survey.get_image_object(ccd) except: print('Failed to get_image_object()') import traceback traceback.print_exc() if carryOn: return result else: raise X = im.get_good_image_subregion() reg = [-1, -1, -1, -1] for i, x in enumerate(X): if x is not None: reg[i] = x result.update(good_region=reg) kwargs = dict(pixPsf=True, splinesky=True, subsky=False, pixels=False, dq=False, invvar=False, normalizePsf=normalizePsf) psf = None wcs = None sky = None if ccd.ccdnastrom == 0: # something went terribly wrong print('ccdnastrom == 0; bailing on annotation') return result try: tim = im.get_tractor_image(**kwargs) except: print('Failed to get_tractor_image') import traceback traceback.print_exc() if carryOn: return result else: raise if tim is None: print('Failed to get_tractor_image; bailing on annotation') return result psf = tim.psf wcs = tim.wcs.wcs sky = tim.sky hdr = tim.primhdr result.update(humidity=hdr.get('HUMIDITY'), outtemp=hdr.get('OUTTEMP'), plver=tim.plver, procdate=tim.procdate, plprocid=tim.plprocid) # parse 'DECaLS_15150_r' to get tile number obj = ccd.object.strip() words = obj.split('_') if len(words) == 3 and words[0] in ('DECaLS', 'MzLS', 'MOSAIC'): try: tileid = int(words[1]) result.update(tileid=tileid) except: import traceback traceback.print_exc() # Instantiate PSF on a grid S = 32 W, H = ccd.width, ccd.height xx = np.linspace(1 + S, W - S, 5) yy = np.linspace(1 + S, H - S, 5) xx, yy = np.meshgrid(xx, yy) psfnorms = [] galnorms = [] for x, y in zip(xx.ravel(), yy.ravel()): tim.psf = psf.constantPsfAt(x, y) try: p = im.psf_norm(tim, x=x, y=y) g = im.galaxy_norm(tim, x=x, y=y) psfnorms.append(p) galnorms.append(g) except: pass tim.psf = psf result.update(psfnorm_mean=np.mean(psfnorms), psfnorm_std=np.std(psfnorms), galnorm_mean=np.mean(galnorms), galnorm_std=np.std(galnorms)) # PSF in center of field cx, cy = (W + 1) / 2., (H + 1) / 2. p = psf.getPointSourcePatch(cx, cy).patch ph, pw = p.shape px, py = np.meshgrid(np.arange(pw), np.arange(ph)) psum = np.sum(p) p /= psum # centroids cenx = np.sum(p * px) ceny = np.sum(p * py) # second moments x2 = np.sum(p * (px - cenx)**2) y2 = np.sum(p * (py - ceny)**2) xy = np.sum(p * (px - cenx) * (py - ceny)) # semi-major/minor axes and position angle theta = np.rad2deg(np.arctan2(2 * xy, x2 - y2) / 2.) theta = np.abs(theta) * np.sign(xy) s = np.sqrt(((x2 - y2) / 2.)**2 + xy**2) a = np.sqrt((x2 + y2) / 2. + s) b = np.sqrt((x2 + y2) / 2. - s) ell = 1. - b / a result.update(psf_mx2=x2, psf_my2=y2, psf_mxy=xy, psf_a=a, psf_b=b, psf_theta=theta, psf_ell=ell) # Galaxy norm using Gaussian approximation of PSF. realpsf = tim.psf tim.psf = im.read_psf_model(0, 0, gaussPsf=True, psf_sigma=tim.psf_sigma) result.update(gaussgalnorm=im.galaxy_norm(tim, x=cx, y=cy)) tim.psf = realpsf has_skygrid = hasattr(sky, 'evaluateGrid') # Sky -- evaluate on a grid (every ~10th pixel) if has_skygrid: skygrid = sky.evaluateGrid( np.linspace(0, ccd.width - 1, int(1 + ccd.width / 10)), np.linspace(0, ccd.height - 1, int(1 + ccd.height / 10))) result.update(meansky=np.mean(skygrid), stdsky=np.std(skygrid), maxsky=skygrid.max(), minsky=skygrid.min()) else: skyval = sky.getConstant() result.update(meansky=skyval, stdsky=0., maxsky=skyval, minsky=skyval) # WCS r0, d0 = wcs.pixelxy2radec(1, 1) r1, d1 = wcs.pixelxy2radec(1, H) r2, d2 = wcs.pixelxy2radec(W, H) r3, d3 = wcs.pixelxy2radec(W, 1) result.update(ra0=r0, dec0=d0, ra1=r1, dec1=d1, ra2=r2, dec2=d2, ra3=r3, dec3=d3) midx, midy = (W + 1) / 2., (H + 1) / 2. rc, dc = wcs.pixelxy2radec(midx, midy) ra, dec = wcs.pixelxy2radec([1, W, midx, midx], [midy, midy, 1, H]) result.update(dra=max(degrees_between(ra, dc + np.zeros_like(ra), rc, dc)), ddec=max( degrees_between(rc + np.zeros_like(dec), dec, rc, dc)), ra_center=rc, dec_center=dc) # Compute scale change across the chip # how many pixels to step step = 10 xx = np.linspace(1 + step, W - step, 5) yy = np.linspace(1 + step, H - step, 5) xx, yy = np.meshgrid(xx, yy) pixscale = [] for x, y in zip(xx.ravel(), yy.ravel()): sx = [x - step, x - step, x + step, x + step, x - step] sy = [y - step, y + step, y + step, y - step, y - step] sr, sd = wcs.pixelxy2radec(sx, sy) rc, dc = wcs.pixelxy2radec(x, y) # project around a tiny little TAN WCS at (x,y), with 1" pixels locwcs = Tan(rc, dc, 0., 0., 1. / 3600, 0., 0., 1. / 3600, 1., 1.) ok, lx, ly = locwcs.radec2pixelxy(sr, sd) A = polygon_area((lx, ly)) pixscale.append(np.sqrt(A / (2 * step)**2)) result.update(pixscale_mean=np.mean(pixscale), pixscale_std=np.std(pixscale), pixscale_min=min(pixscale), pixscale_max=max(pixscale)) result.update(annotated=True) print('Finished annotation') return result
def main(outfn='ccds-annotated.fits', ccds=None): decals = Decals() if ccds is None: ccds = decals.get_ccds() # File from the "observing" svn repo: # https://desi.lbl.gov/svn/decam/code/observing/trunk tiles = fits_table('decam-tiles_obstatus.fits') #ccds.cut(np.arange(100)) #print("HACK!") #ccds.cut(np.array([name in ['N15', 'N16', 'N21', 'N9'] # for name in ccds.ccdname]) * # ccds.expnum == 229683) I = decals.photometric_ccds(ccds) ccds.photometric = np.zeros(len(ccds), bool) ccds.photometric[I] = True I = decals.apply_blacklist(ccds) ccds.blacklist_ok = np.zeros(len(ccds), bool) ccds.blacklist_ok[I] = True ccds.good_region = np.empty((len(ccds), 4), np.int16) ccds.good_region[:,:] = -1 ccds.ra0 = np.zeros(len(ccds), np.float64) ccds.dec0 = np.zeros(len(ccds), np.float64) ccds.ra1 = np.zeros(len(ccds), np.float64) ccds.dec1 = np.zeros(len(ccds), np.float64) ccds.ra2 = np.zeros(len(ccds), np.float64) ccds.dec2 = np.zeros(len(ccds), np.float64) ccds.ra3 = np.zeros(len(ccds), np.float64) ccds.dec3 = np.zeros(len(ccds), np.float64) ccds.dra = np.zeros(len(ccds), np.float32) ccds.ddec = np.zeros(len(ccds), np.float32) ccds.ra_center = np.zeros(len(ccds), np.float64) ccds.dec_center = np.zeros(len(ccds), np.float64) ccds.sig1 = np.zeros(len(ccds), np.float32) ccds.meansky = np.zeros(len(ccds), np.float32) ccds.stdsky = np.zeros(len(ccds), np.float32) ccds.maxsky = np.zeros(len(ccds), np.float32) ccds.minsky = np.zeros(len(ccds), np.float32) ccds.pixscale_mean = np.zeros(len(ccds), np.float32) ccds.pixscale_std = np.zeros(len(ccds), np.float32) ccds.pixscale_max = np.zeros(len(ccds), np.float32) ccds.pixscale_min = np.zeros(len(ccds), np.float32) ccds.psfnorm_mean = np.zeros(len(ccds), np.float32) ccds.psfnorm_std = np.zeros(len(ccds), np.float32) ccds.galnorm_mean = np.zeros(len(ccds), np.float32) ccds.galnorm_std = np.zeros(len(ccds), np.float32) gaussgalnorm = np.zeros(len(ccds), np.float32) # 2nd moments ccds.psf_mx2 = np.zeros(len(ccds), np.float32) ccds.psf_my2 = np.zeros(len(ccds), np.float32) ccds.psf_mxy = np.zeros(len(ccds), np.float32) # ccds.psf_a = np.zeros(len(ccds), np.float32) ccds.psf_b = np.zeros(len(ccds), np.float32) ccds.psf_theta = np.zeros(len(ccds), np.float32) ccds.psf_ell = np.zeros(len(ccds), np.float32) ccds.humidity = np.zeros(len(ccds), np.float32) ccds.outtemp = np.zeros(len(ccds), np.float32) ccds.tileid = np.zeros(len(ccds), np.int32) ccds.tilepass = np.zeros(len(ccds), np.uint8) ccds.tileebv = np.zeros(len(ccds), np.float32) plvers = [] for iccd,ccd in enumerate(ccds): im = decals.get_image_object(ccd) print('Reading CCD %i of %i:' % (iccd+1, len(ccds)), im) X = im.get_good_image_subregion() for i,x in enumerate(X): if x is not None: ccds.good_region[iccd,i] = x W,H = ccd.width, ccd.height psf = None wcs = None sky = None try: tim = im.get_tractor_image(pixPsf=True, splinesky=True, subsky=False, pixels=False) except: import traceback traceback.print_exc() plvers.append('') continue if tim is None: plvers.append('') continue psf = tim.psf wcs = tim.wcs.wcs sky = tim.sky hdr = tim.primhdr # print('Got PSF', psf) # print('Got sky', type(sky)) # print('Got WCS', wcs) ccds.humidity[iccd] = hdr.get('HUMIDITY') ccds.outtemp[iccd] = hdr.get('OUTTEMP') ccds.sig1[iccd] = tim.sig1 plvers.append(tim.plver) obj = hdr.get('OBJECT') # parse 'DECaLS_15150_r' words = obj.split('_') tile = None if len(words) == 3 and words[0] == 'DECaLS': try: tileid = int(words[1]) tile = tiles[tileid - 1] if tile.tileid != tileid: I = np.flatnonzero(tile.tileid == tileid) tile = tiles[I[0]] except: pass if tile is not None: ccds.tileid [iccd] = tile.tileid ccds.tilepass[iccd] = tile.get('pass') ccds.tileebv [iccd] = tile.ebv_med # Instantiate PSF on a grid S = 32 xx = np.linspace(1+S, W-S, 5) yy = np.linspace(1+S, H-S, 5) xx,yy = np.meshgrid(xx, yy) psfnorms = [] galnorms = [] for x,y in zip(xx.ravel(), yy.ravel()): p = im.psf_norm(tim, x=x, y=y) g = im.galaxy_norm(tim, x=x, y=y) psfnorms.append(p) galnorms.append(g) ccds.psfnorm_mean[iccd] = np.mean(psfnorms) ccds.psfnorm_std [iccd] = np.std (psfnorms) ccds.galnorm_mean[iccd] = np.mean(galnorms) ccds.galnorm_std [iccd] = np.std (galnorms) # PSF in center of field cx,cy = (W+1)/2., (H+1)/2. p = psf.getPointSourcePatch(cx, cy).patch ph,pw = p.shape px,py = np.meshgrid(np.arange(pw), np.arange(ph)) psum = np.sum(p) # print('psum', psum) p /= psum # centroids cenx = np.sum(p * px) ceny = np.sum(p * py) # print('cenx,ceny', cenx,ceny) # second moments x2 = np.sum(p * (px - cenx)**2) y2 = np.sum(p * (py - ceny)**2) xy = np.sum(p * (px - cenx)*(py - ceny)) # semi-major/minor axes and position angle theta = np.rad2deg(np.arctan2(2 * xy, x2 - y2) / 2.) theta = np.abs(theta) * np.sign(xy) s = np.sqrt(((x2 - y2)/2.)**2 + xy**2) a = np.sqrt((x2 + y2) / 2. + s) b = np.sqrt((x2 + y2) / 2. - s) ell = 1. - b/a # print('PSF second moments', x2, y2, xy) # print('PSF position angle', theta) # print('PSF semi-axes', a, b) # print('PSF ellipticity', ell) ccds.psf_mx2[iccd] = x2 ccds.psf_my2[iccd] = y2 ccds.psf_mxy[iccd] = xy ccds.psf_a[iccd] = a ccds.psf_b[iccd] = b ccds.psf_theta[iccd] = theta ccds.psf_ell [iccd] = ell # Galaxy norm using Gaussian approximation of PSF. realpsf = tim.psf tim.psf = im.read_psf_model(0, 0, gaussPsf=True, psf_sigma=tim.psf_sigma) gaussgalnorm[iccd] = im.galaxy_norm(tim, x=cx, y=cy) tim.psf = realpsf # Sky mod = np.zeros((ccd.height, ccd.width), np.float32) sky.addTo(mod) ccds.meansky[iccd] = np.mean(mod) ccds.stdsky[iccd] = np.std(mod) ccds.maxsky[iccd] = mod.max() ccds.minsky[iccd] = mod.min() # WCS ccds.ra0[iccd],ccds.dec0[iccd] = wcs.pixelxy2radec(1, 1) ccds.ra1[iccd],ccds.dec1[iccd] = wcs.pixelxy2radec(1, H) ccds.ra2[iccd],ccds.dec2[iccd] = wcs.pixelxy2radec(W, H) ccds.ra3[iccd],ccds.dec3[iccd] = wcs.pixelxy2radec(W, 1) midx, midy = (W+1)/2., (H+1)/2. rc,dc = wcs.pixelxy2radec(midx, midy) ra,dec = wcs.pixelxy2radec([1,W,midx,midx], [midy,midy,1,H]) ccds.dra [iccd] = max(degrees_between(ra, dc+np.zeros_like(ra), rc, dc)) ccds.ddec[iccd] = max(degrees_between(rc+np.zeros_like(dec), dec, rc, dc)) ccds.ra_center [iccd] = rc ccds.dec_center[iccd] = dc # Compute scale change across the chip # how many pixels to step step = 10 xx = np.linspace(1+step, W-step, 5) yy = np.linspace(1+step, H-step, 5) xx,yy = np.meshgrid(xx, yy) pixscale = [] for x,y in zip(xx.ravel(), yy.ravel()): sx = [x-step, x-step, x+step, x+step, x-step] sy = [y-step, y+step, y+step, y-step, y-step] sr,sd = wcs.pixelxy2radec(sx, sy) rc,dc = wcs.pixelxy2radec(x, y) # project around a tiny little TAN WCS at (x,y), with 1" pixels locwcs = Tan(rc, dc, 0., 0., 1./3600, 0., 0., 1./3600, 1., 1.) ok,lx,ly = locwcs.radec2pixelxy(sr, sd) #print('local x,y:', lx, ly) A = polygon_area((lx, ly)) pixscale.append(np.sqrt(A / (2*step)**2)) # print('Pixel scales:', pixscale) ccds.pixscale_mean[iccd] = np.mean(pixscale) ccds.pixscale_min[iccd] = min(pixscale) ccds.pixscale_max[iccd] = max(pixscale) ccds.pixscale_std[iccd] = np.std(pixscale) ccds.plver = np.array(plvers) sfd = tractor.sfd.SFDMap() allbands = 'ugrizY' filts = ['%s %s' % ('DES', f) for f in allbands] wisebands = ['WISE W1', 'WISE W2', 'WISE W3', 'WISE W4'] ebv,ext = sfd.extinction(filts + wisebands, ccds.ra_center, ccds.dec_center, get_ebv=True) ext = ext.astype(np.float32) ccds.ebv = ebv.astype(np.float32) ccds.decam_extinction = ext[:,:len(allbands)] ccds.wise_extinction = ext[:,len(allbands):] # Depth detsig1 = ccds.sig1 / ccds.psfnorm_mean depth = 5. * detsig1 # that's flux in nanomaggies -- convert to mag ccds.psfdepth = -2.5 * (np.log10(depth) - 9) detsig1 = ccds.sig1 / ccds.galnorm_mean depth = 5. * detsig1 # that's flux in nanomaggies -- convert to mag ccds.galdepth = -2.5 * (np.log10(depth) - 9) # Depth using Gaussian FWHM. psf_sigma = ccds.fwhm / 2.35 gnorm = 1./(2. * np.sqrt(np.pi) * psf_sigma) detsig1 = ccds.sig1 / gnorm depth = 5. * detsig1 # that's flux in nanomaggies -- convert to mag ccds.gausspsfdepth = -2.5 * (np.log10(depth) - 9) # Gaussian galaxy depth detsig1 = ccds.sig1 / gaussgalnorm depth = 5. * detsig1 # that's flux in nanomaggies -- convert to mag ccds.gaussgaldepth = -2.5 * (np.log10(depth) - 9) ccds.writeto(outfn)
def update_for_image(self, M, forcepass=None, now=None): # M: measurements for the image that's we're going to use to update # exposure times. # now: for testing purposes only; defaults to ephem.now() # filename patterns for the exposure and slew scripts expscriptpat = os.path.join(self.scriptdir, self.expscriptpattern) slewscriptpat = os.path.join(self.scriptdir, self.slewscriptpattern) if M is not None: # Choose the pass, based on... trans = M['transparency'] seeing = M['seeing'] skybright = M['skybright'] # eg, nominal = 20, sky = 19, brighter is 1 mag brighter than nom. meas_band = M['band'] nomsky = self.nom.sky(meas_band) brighter = nomsky - skybright print('Transparency: %6.02f' % trans) print('Seeing : %6.02f' % seeing) print('Sky : %6.02f' % skybright) print('Nominal sky : %6.02f' % nomsky) print('Sky over nom: %6.02f (positive means brighter than nominal)' % brighter) nextpass = choose_pass(trans, seeing, skybright, nomsky, forcedir=self.scriptdir) elif forcepass is not None: nextpass = forcepass else: nextpass = self.opt.passnum print() print('Selected pass:'******'%s: reading sequence number from %s' % (str(ephem.now()), self.seqnumpath)) f = open(self.seqnumpath, 'r') s = f.read() f.close() seqnum = int(s) else: # Tonight.sh may not have started yet -- pretend that # we're currently taking seqnum=0, so the next one to be # planned is seqnum=1. seqnum = 0 print('%s: sequence number: %i' % (str(now), seqnum)) # 'iplan': the tile index we will use for exposure # 'seqnum' iplan = None if self.opt.sequence: iplan = seqnum + self.sequence_offset # Check whether the next tile will be observed more than an hour # later than its scheduled time -- if so, skip a block of tiles. if iplan < len(J): j = J[iplan] tstart = ephem.Date(str(j['approx_datetime'])) if now > (tstart + 3600./86400.): print('The next tile will be observed more than an hour late. Skipping a block of observations...') for i,j in enumerate(J[iplan:]): tstart = ephem.Date(str(j['approx_datetime'])) if tstart <= now: continue print('Found tile', j['object'], 'which starts at', str(tstart)) print('Skipping', i, 'tiles') self.sequence_offset += i iplan += i break elif self.opt.cut_before_now: # The usual case -- find the next tile scheduled for after now. for i,j in enumerate(J): tstart = ephem.Date(str(j['approx_datetime'])) if tstart <= now: continue print('Found tile', j['object'], 'which starts at', str(tstart)) iplan = i break if iplan is None: print('Could not find a JSON observation in pass', nextpass, 'with approx_datetime after now =', str(now), '-- latest one', str(tstart)) return False else: # For when we want to observe every tile in the plan: # 'seqnum' is the exposure currently running; # seqnum is 1-indexed, so that's the index we want for iplan. iplan = seqnum # Set observing conditions for computing exposure time self.obs.date = now # Planned exposures: P = fits_table() P.type = [] P.tilename = [] P.filter = [] P.exptime = [] P.ra = [] P.dec = [] P.passnumber = [] lasttile = None lastdate = None iahead = 0 for ii,jplan in enumerate(J[iplan:]): from astrometry.util.starutil_numpy import degrees_between if iahead >= self.Nahead: break tilename = str(jplan['object']) nextseq = seqnum + 1 + iahead if self.n_exposures > 0 and nextseq > self.n_exposures: print('Planned tile is beyond the exposure number in ', 'tonight.sh -- RESTART MOSBOT') return False print('Considering planning tile %s for exp %i'%(tilename,nextseq)) # Check all planned tiles before this one for a duplicate tile. dup = False for s in range(nextseq-1, 0, -1): if tilename == self.planned_tiles[s]: dup = True print('Wanted to plan tile %s (pass %i element %i) for exp %i' % (tilename, nextpass, iplan+ii, nextseq), 'but it was already planned for exp %i' % s) break if dup: continue self.planned_tiles[nextseq] = tilename iahead += 1 # Find this tile in the tiles table. tile = get_tile_from_name(tilename, self.tiles) ebv = tile.ebv_med nextband = str(jplan['filter'])[0] print('Selected tile:', tile.tileid, nextband) rastr = ra2hms (jplan['RA' ]) decstr = dec2dms(jplan['dec']) ephemstr = str('%s,f,%s,%s,20' % (tilename, rastr, decstr)) etile = ephem.readdb(ephemstr) etile.compute(self.obs) airmass = get_airmass(float(etile.alt)) print('Airmass of planned tile:', airmass) print('Time of observation:', self.obs.date) # HACK -- try setting the date to the planned datetime from the plan file. #self.obs.date = ephem.Date(str(jplan['approx_datetime'])) lst = np.rad2deg(float(self.obs.sidereal_time())) ha = lst - jplan['RA'] if ha < -180: ha += 360. print('LST:', lst, 'Tile RA:', jplan['RA'], 'Dec', jplan['dec']) print('HA of planned tile:', ha) readout_during_move = True # Check for large slew slew = 0. if lasttile is None: # Look up the tile we previously planned prevname = self.planned_tiles.get(nextseq - 1, None) Jall = self.J1 + self.J2 + self.J3 I, = np.nonzero([str(j['object']) == prevname for j in Jall]) if len(I) == 0: print('Could not find previous tile "%s"' % prevname) else: jprev = Jall[I[0]] lasttile = jprev lastdate = ephem.Date(str(jprev['approx_datetime'])) if lasttile is not None: slew = degrees_between(lasttile['RA'], lasttile['dec'], jplan['RA'], jplan['dec']) print('Slew: %.1f degrees' % slew) # Compute HA for previous tile thedate = self.obs.date self.obs.date = lastdate last_lst = np.rad2deg(float(self.obs.sidereal_time())) self.obs.date = thedate last_ha = last_lst - lasttile['RA'] if last_ha < -180: last_ha += 360. print('Last tile LST:', last_lst, 'Tile RA:', lasttile['RA'], 'Dec', lasttile['dec']) print('Last HA:', last_ha) fuzz = 3. maybe_flip = maybe_ha_flip(ha, last_ha, fuzz) print('Over-the-pole flip possible:', maybe_flip) if maybe_flip: readout_during_move = False if iahead == 1 and slew > 10.: # Beep the terminal -- OA has to okay it (?) print() print() print('Large slew from current exposure to next: ' + 'RA,Dec %.1f, %.1f to %.1f, %.1f ==> %.1f degrees' % (jprev['RA'], jprev['dec'], jplan['RA'], jplan['dec'], slew)) print() print() os.system('tput bel; sleep 0.2; ' * 5) if slew > 35: # We risk a timeout -- modify the "slewread" # script we write out so that we read out before # commanding the telescope to move. print('Large slew from current exposure to next: reading out before moving the telescope') readout_during_move = False lasttile = jplan lastdate = self.obs.date if M is not None: if M['band'] == nextband: nextsky = skybright else: # Guess that the sky is as much brighter than canonical # in the next band as it is in this one! nextsky = ((skybright - nomsky) + self.nom.sky(nextband)) fid = self.nom.fiducial_exptime(nextband) expfactor = exposure_factor(fid, self.nom, airmass, ebv, seeing, nextsky, trans) print('Exposure factor:', expfactor) expfactor_orig = expfactor # Adjust for previous exposures? if self.opt.adjust: debug=True adjfactor,others = self.adjust_for_previous( tile, nextband, fid, debug=debug, get_others=True) # Don't adjust exposure times down, only up. adjfactor = max(adjfactor, 1.0) expfactor *= adjfactor else: adjfactor = 0. others = [] exptime = expfactor * fid.exptime ### HACK -- safety factor! print('Exposure time:', exptime) exptime *= 1.1 print('Exposure time with safety factor:', exptime) exptime_unclipped = exptime exptime = np.clip(exptime, fid.exptime_min, fid.exptime_max) print('Clipped exptime', exptime) exptime_satclipped = 0. if nextband == 'z': # Compute cap on exposure time to avoid saturation / # loss of dynamic range. t_sat = self.nom.saturation_time(nextband, nextsky) if exptime > t_sat: exptime_satclipped = t_sat exptime = t_sat print('Reduced exposure time to avoid z-band saturation:', '%.1f' % exptime) exptime = int(np.ceil(exptime)) print('Changing exptime from', jplan['expTime'], 'to', exptime) jplan['expTime'] = exptime # Update the computed exposure-time database. if self.opt.db: try: # NOTE, these kwargs MUST match the names in models.py self.update_exptime_db( nextseq, others, tileid=tile.tileid, passnumber=nextpass, band=nextband, airmass=airmass, ebv=ebv, meas_band=meas_band, zeropoint=M['zp'], transparency=trans, seeing=seeing, sky=skybright, expfactor=expfactor_orig, adjfactor=adjfactor, exptime_unclipped=exptime_unclipped, exptime_satclipped=exptime_satclipped, exptime=exptime) except: print('Failed to update computed-exptime database.') import traceback traceback.print_exc() # carry on print('Predict tile will be observed at', str(self.obs.date), 'vs approx_datetime', jplan.get('approx_datetime',None)) status = ('Exp %i: Tile %s, Pass %i, RA %s, Dec %s' % (nextseq, tilename, nextpass, rastr, decstr)) print('%s: updating exposure %i to tile %s' % (str(ephem.now()), nextseq, tilename)) expscriptfn = expscriptpat % (nextseq) exptmpfn = expscriptfn + '.tmp' f = open(exptmpfn, 'w') f.write(('# Exp %i Tile: %s, set at Seq %i, %s\n' % (nextseq, tilename, seqnum, str(ephem.now()))) + expscript_for_json(jplan, status=status)) f.close() slewscriptfn = slewscriptpat % (nextseq) slewtmpfn = slewscriptfn + '.tmp' f = open(slewtmpfn, 'w') f.write(slewscript_for_json(jplan, readout_during_move=readout_during_move)) f.close() os.rename(exptmpfn, expscriptfn) print('Wrote', expscriptfn) os.rename(slewtmpfn, slewscriptfn) print('Wrote', slewscriptfn) exptime = jplan['expTime'] self.obs.date += (exptime + self.nom.overhead) / 86400. print('%s: updated exposure %i to tile %s' % (str(ephem.now()), nextseq, tilename)) P.tilename.append(tilename) P.filter.append(nextband) P.exptime.append(exptime) P.ra.append(jplan['RA']) P.dec.append(jplan['dec']) P.passnumber.append(nextpass) P.type.append('P') if iahead < self.Nahead: # We ran out of tiles. # Overwrite the default planned exposures with blanks to avoid # running the defaults (eg, at end of night). seqstart = seqnum + 1 + iahead seqend = self.n_exposures print('Overwriting remaining exposure scripts (%i to %i inclusive) with blanks.' % (seqstart, seqend)) for nextseq in range(seqstart, seqend+1): print('Blanking out exposure %i' % nextseq) fn = self.expscriptpattern % nextseq path = os.path.join(self.scriptdir, fn) f = open(path, 'w') f.write('\n') f.close() fn = self.slewscriptpattern % nextseq path = os.path.join(self.scriptdir, fn) f = open(path, 'w') f.write('\n') f.close() for i,J in enumerate([self.J1,self.J2,self.J3]): passnum = i+1 for j in J: tstart = ephem.Date(str(j['approx_datetime'])) if self.opt.cut_before_now and tstart < now: continue P.tilename.append(str(j['object'])) filt = str(j['filter'])[0] P.filter.append(filt) P.exptime.append(j['expTime']) P.ra.append(j['RA']) P.dec.append(j['dec']) P.passnumber.append(passnum) P.type.append('%i' % passnum) P.to_np_arrays() fn = 'mosbot-plan.fits' tmpfn = fn + '.tmp' P.writeto(tmpfn) os.rename(tmpfn, fn) print('Wrote', fn) return True
def main(): import optparse import sys parser = optparse.OptionParser(usage='%prog <json>') parser.add_option('--base', default='plan', help='Plot base filename') parser.add_option('-t', '--obstatus', help='Show already-observed tiles?') parser.add_option( '--bands', help='Plot only already-observed tiles in the given bands', default='g,r,z') parser.add_option('--sgc', action='store_true', help='Center on SGC?') parser.add_option('--ralo', type=float, default=None) parser.add_option('--rahi', type=float, default=None) parser.add_option('--declo', type=float, default=None) parser.add_option('--dechi', type=float, default=None) parser.add_option( '--scaled', action='store_true', default=False, help='Scale plot so that 1 deg RA = 1 deg Dec (no COS term)') parser.add_option('--wide', action='store_true', default=False, help='Make wider plots?') parser.add_option('--also', action='append', default=[], help='Also plot the plan from the given filename.') parser.add_option('--mosaic', action='store_true', help='Set defaults for Mosaic survey') parser.add_option( '--start-time', help= 'Start time for this plan, HH:MM:SS UTC. Default: 12-degree twilight tonight.' ) parser.add_option('--start-date', help='Start date for this plan, YYYY-MM-DD UTC.') parser.add_option( '--stop-time', help='Stop time for this plan, HH:MM:SS UTC. Default: no limit.') parser.add_option( '--second-half', action='store_true', help='This plan starts at the start of the second half-night.') parser.add_option('--skip', type=int, default=1, help='Write every Nth plot only') parser.add_option('--threads', type=int, help='Multi-processing?') opt, args = parser.parse_args() if len(args) != 1: parser.print_help() sys.exit(-1) if opt.mosaic: dd = dict(ralo=0, rahi=360, declo=30, dechi=88) else: dd = dict(ralo=0, rahi=360, declo=-10, dechi=35) for k in dd.keys(): if getattr(opt, k, None) is None: setattr(opt, k, dd[k]) start_date_specified = (opt.start_date is not None) if opt.start_date is None: # Get date at start of night, where we define a new day as # starting at noon UTC. now = datetime.datetime.utcnow() # noon nightstart = now - datetime.timedelta(0, 12 * 3600) d = nightstart.date() opt.start_date = '%04i-%02i-%02i' % (d.year, d.month, d.day) print('Set start date to', opt.start_date) if opt.mosaic: from camera_mosaic import ephem_observer else: from camera_decam import ephem_observer obs = ephem_observer() obs.temp = 10.0 # deg celsius; average temp for August obs.pressure = 780.0 # mbar ### HACK obs.date = ephem.Date(opt.start_date + ' 8:00:00') #print('Obs date:', obs.date) daystart = obs.date obs.horizon = -ephem.degrees('12:00:00.0') sun = ephem.Sun() eve_twi = obs.next_setting(sun) obs.date = eve_twi morn_twi = obs.next_rising(sun) print('Evening twilight:', eve_twi) print('Morning twilight:', morn_twi) assert (morn_twi > eve_twi) obs.horizon = 0. print('Eve twi:', eve_twi, 'Morning:', morn_twi) if opt.second_half: # Set start-time to the midpoint between 12-degree twilights. obs.date = ephem.Date((eve_twi + morn_twi) / 2.) print('Second half starts at', obs.date) elif opt.start_time is None: # 12-degree twilight on start_date obs.date = eve_twi else: obs.date = ephem.Date(opt.start_date + ' ' + opt.start_time) if not start_date_specified and obs.date < daystart: # If --start-date is, eg, 2am, assume it's during the night starting on daystart. obs.date = ephem.Date(float(obs.date) + 1.) print('Start date:', obs.date) if opt.stop_time is not None: # The date should be unambiguous -- try the same as obs.date = # start time, add one day if necessary. date = obs.date.datetime() stopdate = ephem.Date('%04i-%02i-%02i' % (date.year, date.month, date.day) + ' ' + opt.stop_time) if stopdate < obs.date: stopdate = ephem.Date(float(stopdate) + 1.) print('Stop date:', stopdate) jfn = args[0] print('Reading JSON file', jfn) J = json.loads(open(jfn, 'rb').read()) print(len(J), 'entries') Jalso = [json.loads(open(fn, 'rb').read()) for fn in opt.also] # Get times when exposures should occur. times = [] LSTs = [] # If the JSON files include estimated times, use those if 'approx_datetime' in J[0]: for i, j in enumerate(J): obs.date = ephem.Date(str(j['approx_datetime'])) if opt.stop_time is not None and obs.date > stopdate: print('Tile', i, 'is after --stopdate') J = J[:i] assert (len(J) == len(times)) break times.append(ephem.Date(obs.date)) LSTs.append(np.rad2deg(float(obs.sidereal_time()))) print('Date', obs.date) print('LST', obs.sidereal_time()) else: # Predict overheads lastra, lastdec = None, None for i in range(len(J)): print('Exposure', i, 'should start at', str(obs.date)) if opt.stop_time is not None and obs.date > stopdate: print('Tile', J[i], 'is after --stopdate') break times.append(ephem.Date(obs.date)) LSTs.append(np.rad2deg(float(obs.sidereal_time()))) overhead = 30. if lastra is not None: slew = degrees_between(lastra, lastdec, ras[i], decs[i]) lastra = ras[i] lastdec = decs[i] # Add 3 seconds per degree for slews longer than 2 degrees overhead += np.maximum(0, slew - 2.) * 3. # Add overhead print('Adding', exptime[i], 'seconds exptime plus', overhead, 'seconds overhead') obs.date += (exptime[i] + overhead) / (24 * 3600.) tiles = None if opt.obstatus is not None: from astrometry.util.fits import fits_table tiles = fits_table(opt.obstatus) print('Read', len(tiles), 'tiles') tiles = tiles[(tiles.in_des == 0) * np.logical_or( (tiles.in_sdss == 1), (tiles.in_sdss == 0) * (tiles.in_desi == 1))] print(len(tiles), 'in footprint') fcmap = dict(g='g', r='r', z='m', zd='m') ddecmap = dict(g=-0.2, r=0, z=0.2, zd=0.2) ras = np.array([j['RA'] for j in J]) decs = np.array([j['dec'] for j in J]) filts = np.array([j['filter'] for j in J]) exptime = np.array([j['expTime'] for j in J]) fieldname = [j['object'] for j in J] passnum = np.zeros(len(J), int) filtcc = np.array([fcmap[f] for f in filts]) ddecs = np.array([ddecmap[f] for f in filts]) # passmap = { 1: dict(marker='.'), # 2: dict(marker='o', mfc='none'), # 3: dict(marker='x') } passmap = { 1: dict(marker='.'), 2: dict(marker='.'), 3: dict(marker='.'), } opt.bands = opt.bands.split(',') if len(opt.bands) == 1: filtddec = {'g': 0, 'r': 0, 'z': 0} else: ddec = 0.4 filtddec = {'g': -ddec, 'r': 0, 'z': ddec} seqmap = ['r', 'y', 'g', 'b', 'm'] #seqcc = np.array([seqmap[s % len(seqmap)] for s in seqnum]) #seqcc = np.array([seqmap[s % len(seqmap)] for s in seqid]) ax = [ transform_ra(opt.rahi, opt), transform_ra(opt.ralo, opt), opt.declo, opt.dechi ] alsocolors = 'kbr' also = [] for Ja in Jalso: # We assume the --also plan files contain approx_datetime... atimes = np.array([ephem.Date(str(j['approx_datetime'])) for j in Ja]) aras = np.array([j['RA'] for j in Ja]) adecs = np.array([j['dec'] for j in Ja]) afilts = np.array([j['filter'] for j in Ja]) aexptime = np.array([j['expTime'] for j in Ja]) afieldname = [j['object'] for j in Ja] apassnum = np.zeros(len(Ja), int) if tiles is not None: for i, f in enumerate(afieldname): tile = get_tile_from_name(f, tiles) if tile is None: continue pa = tile.get('pass') apassnum[i] = pa also.append( (atimes, aras, adecs, afilts, aexptime, afieldname, apassnum)) # Try to get the pass number via parsing the field name to get tile id # and looking up the pass number in the tiles table. if tiles is not None: for i, f in enumerate(fieldname): tile = get_tile_from_name(f, tiles) if tile is None: continue pa = tile.get('pass') passnum[i] = pa print('Field', f, 'tileid', tile.tileid, 'pass', pa) allargs = [] for i in reversed(range(0, len(J), opt.skip)): #print('Exposure', i, 'of', len(J)) fn = '%s-%03i.png' % (opt.base, i) fn = os.path.join(os.path.dirname(args[0]), fn) pargs = (opt, ax, tiles, filtddec, fcmap, passmap, also, LSTs, times, ras, decs, ddecs, fieldname, passnum, exptime, i, filtcc, alsocolors, ddecmap, fn) allargs.append(pargs) if opt.threads: from astrometry.util.multiproc import multiproc mp = multiproc(opt.threads, init=plot_init) mp.map(plot_one, allargs) else: plot_init() map(plot_one, allargs) #plot_one(pargs) print() cmd = 'avconv -r 4 -i %s-%%03d.png -y %s.mov' % (opt.base, opt.base) print(cmd) os.system(cmd)
def cat_sdss(req, ver): import json import numpy as np from astrometry.util.starutil_numpy import degrees_between, radectoxyz, xyztoradec from map.views import sdss_ccds_near from astrometry.util.fits import fits_table, merge_tables tag = 'sdss-cat' ralo = float(req.GET['ralo']) rahi = float(req.GET['rahi']) declo = float(req.GET['declo']) dechi = float(req.GET['dechi']) ver = int(ver) if not ver in catversions[tag]: raise RuntimeError('Invalid version %i for tag %s' % (ver, tag)) rad = degrees_between(ralo, declo, rahi, dechi) / 2. xyz1 = radectoxyz(ralo, declo) xyz2 = radectoxyz(rahi, dechi) xyz = (xyz1 + xyz2) xyz /= np.sqrt(np.sum(xyz**2)) rc, dc = xyztoradec(xyz) rad = rad + np.hypot(10., 14.) / 2. / 60. ccds = sdss_ccds_near(rc[0], dc[0], rad) if ccds is None: print('No SDSS CCDs nearby') return HttpResponse(json.dumps(dict(rd=[])), content_type='application/json') print(len(ccds), 'SDSS CCDs') T = [] for ccd in ccds: # env/BOSS_PHOTOOBJ/301/2073/3/photoObj-002073-3-0088.fits fn = os.path.join( settings.SDSS_BASEDIR, 'env', 'BOSS_PHOTOOBJ', str(ccd.rerun), str(ccd.run), str(ccd.camcol), 'photoObj-%06i-%i-%04i.fits' % (ccd.run, ccd.camcol, ccd.field)) print('Reading', fn) T.append( fits_table( fn, columns= 'ra dec objid mode objc_type objc_flags objc_flags nchild tai expflux devflux psfflux cmodelflux fracdev mjd' .split())) T = merge_tables(T) T.cut((T.dec >= declo) * (T.dec <= dechi)) # FIXME T.cut((T.ra >= ralo) * (T.ra <= rahi)) # primary T.cut(T.mode == 1) types = ['P' if t == 6 else 'C' for t in T.objc_type] fluxes = [ p if t == 6 else c for t, p, c in zip(T.objc_type, T.psfflux, T.cmodelflux) ] return HttpResponse(json.dumps( dict( rd=[(float(o.ra), float(o.dec)) for o in T], sourcetype=types, fluxes=[ dict(u=float(f[0]), g=float(f[1]), r=float(f[2]), i=float(f[3]), z=float(f[4])) for f in fluxes ], )), content_type='application/json')
def map_decals_wl(req, ver, zoom, x, y): tag = 'decals-wl' ignoreCached = False filename = None forcecache = False from decals import settings savecache = settings.SAVE_CACHE zoom = int(zoom) zoomscale = 2.**zoom x = int(x) y = int(y) if zoom < 0 or x < 0 or y < 0 or x >= zoomscale or y >= zoomscale: raise RuntimeError('Invalid zoom,x,y %i,%i,%i' % (zoom,x,y)) ver = int(ver) if not ver in tileversions[tag]: raise RuntimeError('Invalid version %i for tag %s' % (ver, tag)) basedir = settings.DATA_DIR tilefn = os.path.join(basedir, 'tiles', tag, '%i/%i/%i/%i.jpg' % (ver, zoom, x, y)) if os.path.exists(tilefn) and not ignoreCached: print('Cached:', tilefn) return send_file(tilefn, 'image/jpeg', expires=oneyear, modsince=req.META.get('HTTP_IF_MODIFIED_SINCE'), filename=filename) else: print('Tile image does not exist:', tilefn) from astrometry.util.resample import resample_with_wcs, OverlapError from astrometry.util.util import Tan from astrometry.libkd.spherematch import match_radec from astrometry.util.fits import fits_table from astrometry.util.starutil_numpy import degrees_between import numpy as np import fitsio try: wcs, W, H, zoomscale, zoom,x,y = get_tile_wcs(zoom, x, y) except RuntimeError as e: return HttpResponse(e.strerror) mydir = os.path.join(basedir, 'coadd', 'weak-lensing') rlo,d = wcs.pixelxy2radec(W, H/2)[-2:] rhi,d = wcs.pixelxy2radec(1, H/2)[-2:] r,d1 = wcs.pixelxy2radec(W/2, 1)[-2:] r,d2 = wcs.pixelxy2radec(W/2, H)[-2:] #dlo = min(d1, d2) #dhi = max(d1, d2) r,d = wcs.pixelxy2radec(W/2, H/2)[-2:] rad = degrees_between(r, d, rlo, d1) fn = os.path.join(mydir, 'index.fits') if not os.path.exists(fn): # ii,rr,dd = [],[],[] for i in range(1, 52852+1): imgfn = os.path.join(mydir, 'map%i.fits' % i) hdr = fitsio.read_header(imgfn) r = hdr['CRVAL1'] d = hdr['CRVAL2'] ii.append(i) rr.append(r) dd.append(d) T = fits_table() T.ra = np.array(rr) T.dec = np.array(dd) T.i = np.array(ii) T.writeto(fn) T = fits_table(fn) I,J,d = match_radec(T.ra, T.dec, r, d, rad + 0.2) T.cut(I) print(len(T), 'weak-lensing maps in range') if len(I) == 0: from django.http import HttpResponseRedirect if forcecache: # create symlink to blank.jpg! trymakedirs(tilefn) src = os.path.join(settings.STATIC_ROOT, 'blank.jpg') if os.path.exists(tilefn): os.unlink(tilefn) os.symlink(src, tilefn) print('Symlinked', tilefn, '->', src) return HttpResponseRedirect(settings.STATIC_URL + 'blank.jpg') r,d = wcs.pixelxy2radec([1,1,1,W/2,W,W,W,W/2], [1,H/2,H,H,H,H/2,1,1])[-2:] foundany = False rimg = np.zeros((H,W), np.float32) rn = np.zeros((H,W), np.uint8) for tilei in T.i: fn = os.path.join(mydir, 'map%i.fits' % tilei) try: bwcs = _read_tan_wcs(fn, 0) except: print('Failed to read WCS:', fn) savecache = False import traceback import sys traceback.print_exc(None, sys.stdout) continue foundany = True print('Reading', fn) ok,xx,yy = bwcs.radec2pixelxy(r, d) xx = xx.astype(np.int) yy = yy.astype(np.int) imW,imH = int(bwcs.get_width()), int(bwcs.get_height()) M = 10 xlo = np.clip(xx.min() - M, 0, imW) xhi = np.clip(xx.max() + M, 0, imW) ylo = np.clip(yy.min() - M, 0, imH) yhi = np.clip(yy.max() + M, 0, imH) if xlo >= xhi or ylo >= yhi: continue subwcs = bwcs.get_subimage(xlo, ylo, xhi-xlo, yhi-ylo) slc = slice(ylo,yhi), slice(xlo,xhi) try: f = fitsio.FITS(fn)[0] img = f[slc] del f except: print('Failed to read image and WCS:', fn) savecache = False import traceback import sys traceback.print_exc(None, sys.stdout) continue try: Yo,Xo,Yi,Xi,nil = resample_with_wcs(wcs, subwcs, [], 3) except OverlapError: print('Resampling exception') continue rimg[Yo,Xo] += img[Yi,Xi] rn [Yo,Xo] += 1 rimg /= np.maximum(rn, 1) if forcecache: savecache = True if savecache: trymakedirs(tilefn) else: import tempfile f,tilefn = tempfile.mkstemp(suffix='.jpg') os.close(f) import pylab as plt # S/N #lo,hi = 1.5, 5.0 lo,hi = 0, 5.0 rgb = plt.cm.hot((rimg - lo) / (hi - lo)) plt.imsave(tilefn, rgb) print('Wrote', tilefn) return send_file(tilefn, 'image/jpeg', unlink=(not savecache), filename=filename)
def cat_targets_drAB(req, ver, cats=[], tag='', bgs=False, sky=False, bright=False, dark=False, color_name_func=desitarget_color_names): ''' color_name_func: function that selects names and colors for targets (eg based on targeting bit values) ''' import json ralo = float(req.GET['ralo']) rahi = float(req.GET['rahi']) declo = float(req.GET['declo']) dechi = float(req.GET['dechi']) ver = int(ver) if not ver in catversions[tag]: raise RuntimeError('Invalid version %i for tag %s' % (ver, tag)) from astrometry.util.fits import fits_table, merge_tables from astrometry.libkd.spherematch import tree_open, tree_search_radec import numpy as np from astrometry.util.starutil_numpy import radectoxyz, xyztoradec, degrees_between xyz1 = radectoxyz(ralo, declo) xyz2 = radectoxyz(rahi, dechi) xyz = (xyz1 + xyz2) / 2. xyz /= np.sqrt(np.sum(xyz**2)) rc, dc = xyztoradec(xyz) rc = rc[0] dc = dc[0] rad = degrees_between(rc, dc, ralo, declo) ''' startree -i /project/projectdirs/desi/target/catalogs/targets-dr4-0.20.0.fits -o data/targets-dr4-0.20.0.kd.fits -P -k -T ''' TT = [] for fn in cats: kd = tree_open(fn) I = tree_search_radec(kd, rc, dc, rad) print('Matched', len(I), 'from', fn) if len(I) == 0: continue T = fits_table(fn, rows=I) TT.append(T) if len(TT) == 0: return HttpResponse(json.dumps(dict(rd=[], name=[])), content_type='application/json') T = merge_tables(TT, columns='fillzero') if bgs: T.cut(T.bgs_target > 0) if bright: T.cut(np.logical_or(T.bgs_target > 0, T.mws_target > 0)) if dark: T.cut(T.desi_target > 0) names = None colors = None if color_name_func is not None: names, colors = color_name_func(T) if sky: fluxes = [ dict(g=float(g), r=float(r), z=float(z)) for (g, r, z) in zip(T.apflux_g[:, 0], T.apflux_r[:, 0], T.apflux_z[:, 0]) ] nobs = None else: fluxes = [ dict(g=float(g), r=float(r), z=float(z), W1=float(W1), W2=float(W2)) for (g, r, z, W1, W2) in zip(T.flux_g, T.flux_r, T.flux_z, T.flux_w1, T.flux_w2) ] nobs = [ dict(g=int(g), r=int(r), z=int(z)) for g, r, z in zip(T.nobs_g, T.nobs_r, T.nobs_z) ], rtn = dict( rd=[(t.ra, t.dec) for t in T], targetid=[int(t) for t in T.targetid], fluxes=fluxes, ) if names is not None: rtn.update(name=names) if colors is not None: rtn.update(color=colors) if nobs is not None: rtn.update(nobs=nobs) return HttpResponse(json.dumps(rtn), content_type='application/json')
def map_sdss(req, ver, zoom, x, y, savecache=None, tag='sdss', get_images=False, ignoreCached=False, wcs=None, forcecache=False, forcescale=None, bestOnly=False, bands='gri', **kwargs): from decals import settings if savecache is None: savecache = settings.SAVE_CACHE zoom = int(zoom) zoomscale = 2.**zoom x = int(x) y = int(y) if zoom < 0 or x < 0 or y < 0 or x >= zoomscale or y >= zoomscale: raise RuntimeError('Invalid zoom,x,y %i,%i,%i' % (zoom, x, y)) ver = int(ver) if not ver in tileversions[tag]: raise RuntimeError('Invalid version %i for tag %s' % (ver, tag)) basedir = settings.DATA_DIR tilefn = os.path.join(basedir, 'tiles', tag, '%i/%i/%i/%i.jpg' % (ver, zoom, x, y)) if os.path.exists(tilefn) and not ignoreCached: if get_images: return None return send_file(tilefn, 'image/jpeg', expires=oneyear, modsince=req.META.get('HTTP_IF_MODIFIED_SINCE')) if not savecache: import tempfile f, tilefn = tempfile.mkstemp(suffix='.jpg') os.close(f) if wcs is None: try: wcs, W, H, zoomscale, zoom, x, y = get_tile_wcs(zoom, x, y) except RuntimeError as e: if get_images: return None return HttpResponse(e.strerror) else: W = wcs.get_width() H = wcs.get_height() from astrometry.util.fits import fits_table import numpy as np from astrometry.libkd.spherematch import tree_build_radec, tree_search_radec from astrometry.util.starutil_numpy import degrees_between, arcsec_between from astrometry.util.resample import resample_with_wcs, OverlapError from astrometry.util.util import Tan, Sip import fitsio print('Tile wcs: center', wcs.radec_center(), 'pixel scale', wcs.pixel_scale()) global w_flist global w_flist_tree if w_flist is None: w_flist = fits_table( os.path.join(settings.DATA_DIR, 'sdss', 'window_flist.fits'), columns=['run', 'rerun', 'camcol', 'field', 'ra', 'dec', 'score']) print('Read', len(w_flist), 'window_flist entries') w_flist.cut(w_flist.rerun == '301') print('Cut to', len(w_flist), 'in rerun 301') w_flist_tree = tree_build_radec(w_flist.ra, w_flist.dec) # SDSS field size radius = 1.01 * np.hypot(10., 14.) / 2. / 60. # leaflet tile size ra, dec = wcs.pixelxy2radec(W / 2., H / 2.)[-2:] r0, d0 = wcs.pixelxy2radec(1, 1)[-2:] r1, d1 = wcs.pixelxy2radec(W, H)[-2:] radius = radius + max(degrees_between(ra, dec, r0, d0), degrees_between(ra, dec, r1, d1)) J = tree_search_radec(w_flist_tree, ra, dec, radius) print(len(J), 'overlapping SDSS fields found') if len(J) == 0: if get_images: return None if forcecache: # create symlink to blank.jpg! trymakedirs(tilefn) src = os.path.join(settings.STATIC_ROOT, 'blank.jpg') if os.path.exists(tilefn): os.unlink(tilefn) os.symlink(src, tilefn) print('Symlinked', tilefn, '->', src) from django.http import HttpResponseRedirect return HttpResponseRedirect(settings.STATIC_URL + 'blank.jpg') ww = [1, W * 0.25, W * 0.5, W * 0.75, W] hh = [1, H * 0.25, H * 0.5, H * 0.75, H] r, d = wcs.pixelxy2radec( [1] * len(hh) + ww + [W] * len(hh) + list(reversed(ww)), hh + [1] * len(ww) + list(reversed(hh)) + [H] * len(ww))[-2:] scaled = 0 scalepat = None scaledir = 'sdss' if zoom <= 13 and forcescale is None: # Get *actual* pixel scales at the top & bottom r1, d1 = wcs.pixelxy2radec(W / 2., H)[-2:] r2, d2 = wcs.pixelxy2radec(W / 2., H - 1.)[-2:] r3, d3 = wcs.pixelxy2radec(W / 2., 1.)[-2:] r4, d4 = wcs.pixelxy2radec(W / 2., 2.)[-2:] # Take the min = most zoomed-in scale = min(arcsec_between(r1, d1, r2, d2), arcsec_between(r3, d3, r4, d4)) native_scale = 0.396 scaled = int(np.floor(np.log2(scale / native_scale))) print('Zoom:', zoom, 'x,y', x, y, 'Tile pixel scale:', scale, 'Scale step:', scaled) scaled = np.clip(scaled, 1, 7) dirnm = os.path.join(basedir, 'scaled', scaledir) scalepat = os.path.join( dirnm, '%(scale)i%(band)s', '%(rerun)s', '%(run)i', '%(camcol)i', 'sdss-%(run)i-%(camcol)i-%(field)i-%(band)s.fits') if forcescale is not None: scaled = forcescale rimgs = [np.zeros((H, W), np.float32) for band in bands] rns = [np.zeros((H, W), np.float32) for band in bands] from astrometry.sdss import AsTransWrapper, DR9 sdss = DR9(basedir=settings.SDSS_DIR) sdss.saveUnzippedFiles(settings.SDSS_DIR) #sdss.setFitsioReadBZ2() if settings.SDSS_PHOTOOBJS: sdss.useLocalTree(photoObjs=settings.SDSS_PHOTOOBJS, resolve=settings.SDSS_RESOLVE) for jnum, j in enumerate(J): print('SDSS field', jnum, 'of', len(J), 'for zoom', zoom, 'x', x, 'y', y) im = w_flist[j] if im.score >= 0.5: weight = 1. else: weight = 0.001 for band, rimg, rn in zip(bands, rimgs, rns): if im.rerun != '301': continue tmpsuff = '.tmp%08i' % np.random.randint(100000000) basefn = sdss.retrieve('frame', im.run, im.camcol, field=im.field, band=band, rerun=im.rerun, tempsuffix=tmpsuff) if scaled > 0: fnargs = dict(band=band, rerun=im.rerun, run=im.run, camcol=im.camcol, field=im.field) fn = get_scaled(scalepat, fnargs, scaled, basefn, read_base_wcs=read_astrans, read_wcs=read_sip_wcs) print('get_scaled:', fn) else: fn = basefn frame = None if fn == basefn: frame = sdss.readFrame(im.run, im.camcol, im.field, band, filename=fn) h, w = frame.getImageShape() # Trim off the overlapping top of the image # Wimp out and instead of trimming 128 pix, trim 124! trim = 124 subh = h - trim astrans = frame.getAsTrans() fwcs = AsTransWrapper(astrans, w, subh) fullimg = frame.getImage() fullimg = fullimg[:-trim, :] else: fwcs = Sip(fn) fitsimg = fitsio.FITS(fn)[0] h, w = fitsimg.get_info()['dims'] fullimg = fitsimg.read() try: #Yo,Xo,Yi,Xi,nil = resample_with_wcs(wcs, fwcs, [], 3) Yo, Xo, Yi, Xi, [resamp ] = resample_with_wcs(wcs, fwcs, [fullimg], 2) except OverlapError: continue if len(Xi) == 0: #print 'No overlap' continue if sdssps is not None: x0 = Xi.min() x1 = Xi.max() y0 = Yi.min() y1 = Yi.max() slc = (slice(y0, y1 + 1), slice(x0, x1 + 1)) if frame is not None: img = frame.getImageSlice(slc) else: img = fitsimg[slc] #rimg[Yo,Xo] += img[Yi-y0, Xi-x0] if bestOnly: K = np.flatnonzero(weight > rn[Yo, Xo]) print('Updating', len(K), 'of', len(Yo), 'pixels') if len(K): rimg[Yo[K], Xo[K]] = resamp[K] * weight rn[Yo[K], Xo[K]] = weight else: rimg[Yo, Xo] += resamp * weight rn[Yo, Xo] += weight if sdssps is not None: # goodpix = np.ones(img.shape, bool) # fpM = sdss.readFpM(im.run, im.camcol, im.field, band) # for plane in [ 'INTERP', 'SATUR', 'CR', 'GHOST' ]: # fpM.setMaskedPixels(plane, goodpix, False, roi=[x0,x1,y0,y1]) plt.clf() #ima = dict(vmin=-0.05, vmax=0.5) #ima = dict(vmin=-0.5, vmax=2.) ima = dict(vmax=np.percentile(img, 99)) plt.subplot(2, 3, 1) dimshow(img, ticks=False, **ima) plt.title('image') rthis = np.zeros_like(rimg) #rthis[Yo,Xo] += img[Yi-y0, Xi-x0] rthis[Yo, Xo] += resamp plt.subplot(2, 3, 2) dimshow(rthis, ticks=False, **ima) plt.title('resampled') # plt.subplot(2,3,3) # dimshow(goodpix, ticks=False, vmin=0, vmax=1) # plt.title('good pix') plt.subplot(2, 3, 4) dimshow(rimg / np.maximum(rn, 1), ticks=False, **ima) plt.title('coadd') plt.subplot(2, 3, 5) dimshow(rn, vmin=0, ticks=False) plt.title('coverage: max %i' % rn.max()) plt.subplot(2, 3, 6) rgb = sdss_rgb( [rimg / np.maximum(rn, 1) for rimg, rn in zip(rimgs, rns)], bands) dimshow(rgb) plt.suptitle('SDSS %s, R/C/F %i/%i/%i' % (band, im.run, im.camcol, im.field)) sdssps.savefig() for rimg, rn in zip(rimgs, rns): rimg /= np.maximum(rn, 1e-3) del rns if get_images: return rimgs rgb = sdss_rgb(rimgs, bands) trymakedirs(tilefn) save_jpeg(tilefn, rgb) print('Wrote', tilefn) return send_file(tilefn, 'image/jpeg', unlink=(not savecache))
def cat_targets_drAB(req, ver, cats=None, tag='', bgs=False, sky=False, bright=False, dark=False, color_name_func=desitarget_color_names): ''' color_name_func: function that selects names and colors for targets (eg based on targeting bit values) ''' if cats is None: cats = [] import json ralo = float(req.GET['ralo']) rahi = float(req.GET['rahi']) declo = float(req.GET['declo']) dechi = float(req.GET['dechi']) ver = int(ver) if not ver in catversions[tag]: raise RuntimeError('Invalid version %i for tag %s' % (ver, tag)) from astrometry.util.fits import fits_table, merge_tables from astrometry.libkd.spherematch import tree_open, tree_search_radec import numpy as np from astrometry.util.starutil_numpy import radectoxyz, xyztoradec, degrees_between xyz1 = radectoxyz(ralo, declo) xyz2 = radectoxyz(rahi, dechi) xyz = (xyz1 + xyz2)/2. xyz /= np.sqrt(np.sum(xyz**2)) rc,dc = xyztoradec(xyz) rc = rc[0] dc = dc[0] rad = degrees_between(rc, dc, ralo, declo) ''' startree -i /project/projectdirs/desi/target/catalogs/targets-dr4-0.20.0.fits -o data/targets-dr4-0.20.0.kd.fits -P -k -T ''' TT = [] for fn in cats: kd = tree_open(fn) I = tree_search_radec(kd, rc, dc, rad) print('Matched', len(I), 'from', fn) if len(I) == 0: continue T = fits_table(fn, rows=I) TT.append(T) if len(TT) == 0: return HttpResponse(json.dumps(dict(rd=[], name=[])), content_type='application/json') T = merge_tables(TT, columns='fillzero') if bgs: T.cut(T.bgs_target > 0) if bright: T.cut(np.logical_or(T.bgs_target > 0, T.mws_target > 0)) if dark: T.cut(T.desi_target > 0) names = None colors = None if color_name_func is not None: names,colors = color_name_func(T) if sky: fluxes = [dict(g=float(g), r=float(r), z=float(z)) for (g,r,z) in zip(T.apflux_g[:,0], T.apflux_r[:,0], T.apflux_z[:,0])] nobs = None else: fluxes = [dict(g=float(g), r=float(r), z=float(z), W1=float(W1), W2=float(W2)) for (g,r,z,W1,W2) in zip(T.flux_g, T.flux_r, T.flux_z, T.flux_w1, T.flux_w2)] nobs=[dict(g=int(g), r=int(r), z=int(z)) for g,r,z in zip(T.nobs_g, T.nobs_r, T.nobs_z)], rtn = dict(rd=[(t.ra, t.dec) for t in T], targetid=[int(t) for t in T.targetid], fluxes=fluxes, ) if names is not None: rtn.update(name=names) if colors is not None: rtn.update(color=colors) if nobs is not None: rtn.update(nobs=nobs) # Convert targetid to string to prevent rounding errors rtn['targetid'] = [str(s) for s in rtn['targetid']] return HttpResponse(json.dumps(rtn), content_type='application/json')
def run_tiles(X): tiles, tag = X print('Running', tag, '-', len(tiles), 'tiles') # Aaron's file has all images share the boresight CRVAL, so they have large CRPIX values. T = fits_table( '/global/cfs/cdirs/desi/users/ameisner/GFA/gfa_reduce_etc/gfa_wcs+focus.bigtan-zenith.fits' ) Nbright = 10 tiles_ann = fits_table() tiles_ann.index = tiles.index gfa_regions = [] maxr = 0. #t.cd[0,0], t.cd[0,1], t.cd[1,0], t.cd[1,1], for t in T: wcs = Tan(0., 0., t.crpix[0], t.crpix[1], t.cd[0, 0], t.cd[1, 0], t.cd[0, 1], t.cd[1, 1], float(t.naxis[0]), float(t.naxis[1])) ctype = t.extname[:5] cnum = int(t.extname[5]) h, w = wcs.shape x, y = [1, 1, w, w, 1], [1, h, h, 1, 1] r, d = wcs.pixelxy2radec(x, y) dists = degrees_between(0., 0., r, d) maxr = max(maxr, max(dists)) # x0, y0, x1, y1 rois = [] if ctype == 'FOCUS': # add the two half-chips. # wcs.get_subimage moves the CRPIX, but leave CRVAL unchanged, so tx,ty still work unchanged. # Aaron's WCS templates correct for the overscans #wcs_subs.append((cstr, cnum, 'a', wcs.get_subimage(0, 0, 1024, h))) #wcs_subs.append((cstr, cnum, 'b', wcs.get_subimage(1024, 0, 1024, h))) #all_sub_wcs[(cstr, cnum, 1)] = (tx, ty, wcs.get_subimage(50, 0, 1024, 1032)) #all_sub_wcs[(cstr, cnum, 2)] = (tx, ty, wcs.get_subimage(1174, 0, 1024, 1032)) # Add (negative) margin for donut size and telescope pointing uncertainty. # ~10" for donuts and ~10" for telescope pointing #margin = 100 #wcs_subs.append((cstr, cnum, 'a_margin', wcs.get_subimage(margin, margin, 1024-2*margin, h-2*margin))) #wcs_subs.append((cstr, cnum, 'b_margin', wcs.get_subimage(1024+margin, margin, 1024-2*margin, h-2*margin))) # Also add a positive margin for bright-star reflections off filters #margin = 125 #wcs_subs.append((cstr, cnum, 'expanded', wcs.get_subimage(-margin, -margin, w+2*margin, h+2*margin))) rois.append(('a', 0, 0, 1024, h)) rois.append(('b', 1024, 0, 2048, h)) margin = 100 rois.append( ('a_margin', margin, margin, 1024 - margin, h - margin)) rois.append( ('b_margin', 1024 + margin, margin, 2048 - margin, h - margin)) margin = 125 rois.append(('expanded', -margin, -margin, w + margin, h + margin)) else: # Guide chips include overscan pixels -- including a blank region in the middle. #print(cstr,cnum, 'shape', wcs.shape) #wcs_subs.append((cstr, cnum, 'ccd', wcs)) rois.append(('ccd', 0, 0, w, h)) # Add expanded GUIDE chips -- 25" margin / 0.2"/pix = 125 pix margin = 125 #wcs_subs.append((cstr, cnum, 'expanded', wcs.get_subimage(-margin, -margin, w+2*margin, h+2*margin))) rois.append(('expanded', -margin, -margin, w + margin, h + margin)) margin = 125 expwcs = wcs.get_subimage(-margin, -margin, w + 2 * margin, h + 2 * margin) newrois = [] for tag, x0, y0, x1, y1 in rois: name = '%s_%i_%s' % (ctype.lower(), cnum, tag) arr = np.zeros(len(tiles), (np.float32, Nbright)) tiles_ann.set('brightest_' + name, arr) # (the rois have zero-indexed x0,y0, and non-inclusive x1,y1!) newrois.append((name, arr, 1 + x0, 1 + y0, x1, y1)) gfa_regions.append((ctype, cnum, wcs, expwcs, newrois)) # DEBUG WCS # s = [] # for ctype,cnum,wcs,expwcs,rois in gfa_regions: # WCS = wcs # h,w = WCS.shape # #print('Expwcs:', w, 'x', h) # x = [1,1,w,w,1] # y = [1,h,h,1,1] # r,d = WCS.pixelxy2radec(x, y) # p = ','.join(['%.4f,%.4f' % (rr,dd) for rr,dd in zip(r,d)]) # s.append(p) # s = ';'.join(s) # print('http://legacysurvey.org/viewer/?ra=0&dec=0&poly='+s) # sys.exit(0) gaia = CachingGaiaCatalog(columns=[ 'ra', 'dec', 'phot_g_mean_mag', 'phot_bp_mean_mag', 'phot_rp_mean_mag', 'astrometric_excess_noise', 'astrometric_params_solved', 'source_id', 'pmra_error', 'pmdec_error', 'parallax_error', 'ra_error', 'dec_error', 'pmra', 'pmdec', 'parallax', 'ref_epoch' ]) tyc2fn = '/global/cfs/cdirs/cosmo/staging/tycho2/tycho2.kd.fits' tycho_kd = tree_open(tyc2fn) tycho_cat = fits_table(tyc2fn) maxrad = maxr * 1.05 for itile, tile in enumerate(tiles): #if not tile.in_imaging: # continue #if tile.centerid % 10 == 0: #print('tile program', tile.program, 'pass', tile.get('pass'), 'id', tile.centerid, gaia.get_healpix_tree.cache_info()) I = tree_search_radec(tycho_kd, tile.ra, tile.dec, maxrad) tycstars = tycho_cat[I] fix_tycho(tycstars) for cstr, cname, chipwcs, bigwcs, rois in gfa_regions: h, w = chipwcs.shape chipwcs.set_crval(tile.ra, tile.dec) bigwcs.set_crval(tile.ra, tile.dec) gstars = gaia.get_catalog_in_wcs(bigwcs, step=1032, margin=0) fix_gaia(gstars) bh, bw = bigwcs.shape ok, x, y = bigwcs.radec2pixelxy(tycstars.ra, tycstars.dec) tstars = tycstars[(x >= 1) * (y >= 1) * (x <= bw) * (y <= bh)] #print('Tile', tile.program, 'p', tile.get('pass'), tile.centerid, # 'GFA', cstr, cname, ':', len(gstars), 'Gaia stars', len(tstars), 'Tycho-2 stars') if len(gstars) + len(tstars) == 0: print('No stars in tile centerid', tile.centerid, 'chip', name) continue if len(gstars) > 0 and len(tstars) > 0: merge_gaia_tycho(gstars, tstars) stars = merge_tables([gstars, tstars], columns='fillzero') elif len(tstars) > 0: stars = tstars else: stars = gstars ok, x, y = chipwcs.radec2pixelxy(stars.ra, stars.dec) for name, arr, x0, y0, x1, y1 in rois: J = np.flatnonzero( (x >= x0) * (x <= x1) * (y >= y0) * (y <= y1)) mags = stars.mag[J] #print(' ', len(mags), 'in name') K = np.argsort(mags) K = K[:Nbright] arr[itile, :len(K)] = mags[K] #tiles.add_columns_from(tiles_ann) return tiles_ann
def main(outfn='ccds-annotated.fits', ccds=None): decals = Decals() if ccds is None: ccds = decals.get_ccds() # File from the "observing" svn repo: # https://desi.lbl.gov/svn/decam/code/observing/trunk tiles = fits_table('decam-tiles_obstatus.fits') #ccds.cut(np.arange(100)) #print("HACK!") #ccds.cut(np.array([name in ['N15', 'N16', 'N21', 'N9'] # for name in ccds.ccdname]) * # ccds.expnum == 229683) I = decals.photometric_ccds(ccds) ccds.photometric = np.zeros(len(ccds), bool) ccds.photometric[I] = True I = decals.apply_blacklist(ccds) ccds.blacklist_ok = np.zeros(len(ccds), bool) ccds.blacklist_ok[I] = True ccds.good_region = np.empty((len(ccds), 4), np.int16) ccds.good_region[:, :] = -1 ccds.ra0 = np.zeros(len(ccds), np.float64) ccds.dec0 = np.zeros(len(ccds), np.float64) ccds.ra1 = np.zeros(len(ccds), np.float64) ccds.dec1 = np.zeros(len(ccds), np.float64) ccds.ra2 = np.zeros(len(ccds), np.float64) ccds.dec2 = np.zeros(len(ccds), np.float64) ccds.ra3 = np.zeros(len(ccds), np.float64) ccds.dec3 = np.zeros(len(ccds), np.float64) ccds.dra = np.zeros(len(ccds), np.float32) ccds.ddec = np.zeros(len(ccds), np.float32) ccds.ra_center = np.zeros(len(ccds), np.float64) ccds.dec_center = np.zeros(len(ccds), np.float64) ccds.sig1 = np.zeros(len(ccds), np.float32) ccds.meansky = np.zeros(len(ccds), np.float32) ccds.stdsky = np.zeros(len(ccds), np.float32) ccds.maxsky = np.zeros(len(ccds), np.float32) ccds.minsky = np.zeros(len(ccds), np.float32) ccds.pixscale_mean = np.zeros(len(ccds), np.float32) ccds.pixscale_std = np.zeros(len(ccds), np.float32) ccds.pixscale_max = np.zeros(len(ccds), np.float32) ccds.pixscale_min = np.zeros(len(ccds), np.float32) ccds.psfnorm_mean = np.zeros(len(ccds), np.float32) ccds.psfnorm_std = np.zeros(len(ccds), np.float32) ccds.galnorm_mean = np.zeros(len(ccds), np.float32) ccds.galnorm_std = np.zeros(len(ccds), np.float32) gaussgalnorm = np.zeros(len(ccds), np.float32) # 2nd moments ccds.psf_mx2 = np.zeros(len(ccds), np.float32) ccds.psf_my2 = np.zeros(len(ccds), np.float32) ccds.psf_mxy = np.zeros(len(ccds), np.float32) # ccds.psf_a = np.zeros(len(ccds), np.float32) ccds.psf_b = np.zeros(len(ccds), np.float32) ccds.psf_theta = np.zeros(len(ccds), np.float32) ccds.psf_ell = np.zeros(len(ccds), np.float32) ccds.humidity = np.zeros(len(ccds), np.float32) ccds.outtemp = np.zeros(len(ccds), np.float32) ccds.tileid = np.zeros(len(ccds), np.int32) ccds.tilepass = np.zeros(len(ccds), np.uint8) ccds.tileebv = np.zeros(len(ccds), np.float32) plvers = [] for iccd, ccd in enumerate(ccds): im = decals.get_image_object(ccd) print('Reading CCD %i of %i:' % (iccd + 1, len(ccds)), im) X = im.get_good_image_subregion() for i, x in enumerate(X): if x is not None: ccds.good_region[iccd, i] = x W, H = ccd.width, ccd.height psf = None wcs = None sky = None try: tim = im.get_tractor_image(pixPsf=True, splinesky=True, subsky=False, pixels=False) except: import traceback traceback.print_exc() plvers.append('') continue if tim is None: plvers.append('') continue psf = tim.psf wcs = tim.wcs.wcs sky = tim.sky hdr = tim.primhdr # print('Got PSF', psf) # print('Got sky', type(sky)) # print('Got WCS', wcs) ccds.humidity[iccd] = hdr.get('HUMIDITY') ccds.outtemp[iccd] = hdr.get('OUTTEMP') ccds.sig1[iccd] = tim.sig1 plvers.append(tim.plver) obj = hdr.get('OBJECT') # parse 'DECaLS_15150_r' words = obj.split('_') tile = None if len(words) == 3 and words[0] == 'DECaLS': try: tileid = int(words[1]) tile = tiles[tileid - 1] if tile.tileid != tileid: I = np.flatnonzero(tile.tileid == tileid) tile = tiles[I[0]] except: pass if tile is not None: ccds.tileid[iccd] = tile.tileid ccds.tilepass[iccd] = tile.get('pass') ccds.tileebv[iccd] = tile.ebv_med # Instantiate PSF on a grid S = 32 xx = np.linspace(1 + S, W - S, 5) yy = np.linspace(1 + S, H - S, 5) xx, yy = np.meshgrid(xx, yy) psfnorms = [] galnorms = [] for x, y in zip(xx.ravel(), yy.ravel()): p = im.psf_norm(tim, x=x, y=y) g = im.galaxy_norm(tim, x=x, y=y) psfnorms.append(p) galnorms.append(g) ccds.psfnorm_mean[iccd] = np.mean(psfnorms) ccds.psfnorm_std[iccd] = np.std(psfnorms) ccds.galnorm_mean[iccd] = np.mean(galnorms) ccds.galnorm_std[iccd] = np.std(galnorms) # PSF in center of field cx, cy = (W + 1) / 2., (H + 1) / 2. p = psf.getPointSourcePatch(cx, cy).patch ph, pw = p.shape px, py = np.meshgrid(np.arange(pw), np.arange(ph)) psum = np.sum(p) # print('psum', psum) p /= psum # centroids cenx = np.sum(p * px) ceny = np.sum(p * py) # print('cenx,ceny', cenx,ceny) # second moments x2 = np.sum(p * (px - cenx)**2) y2 = np.sum(p * (py - ceny)**2) xy = np.sum(p * (px - cenx) * (py - ceny)) # semi-major/minor axes and position angle theta = np.rad2deg(np.arctan2(2 * xy, x2 - y2) / 2.) theta = np.abs(theta) * np.sign(xy) s = np.sqrt(((x2 - y2) / 2.)**2 + xy**2) a = np.sqrt((x2 + y2) / 2. + s) b = np.sqrt((x2 + y2) / 2. - s) ell = 1. - b / a # print('PSF second moments', x2, y2, xy) # print('PSF position angle', theta) # print('PSF semi-axes', a, b) # print('PSF ellipticity', ell) ccds.psf_mx2[iccd] = x2 ccds.psf_my2[iccd] = y2 ccds.psf_mxy[iccd] = xy ccds.psf_a[iccd] = a ccds.psf_b[iccd] = b ccds.psf_theta[iccd] = theta ccds.psf_ell[iccd] = ell # Galaxy norm using Gaussian approximation of PSF. realpsf = tim.psf tim.psf = im.read_psf_model(0, 0, gaussPsf=True, psf_sigma=tim.psf_sigma) gaussgalnorm[iccd] = im.galaxy_norm(tim, x=cx, y=cy) tim.psf = realpsf # Sky mod = np.zeros((ccd.height, ccd.width), np.float32) sky.addTo(mod) ccds.meansky[iccd] = np.mean(mod) ccds.stdsky[iccd] = np.std(mod) ccds.maxsky[iccd] = mod.max() ccds.minsky[iccd] = mod.min() # WCS ccds.ra0[iccd], ccds.dec0[iccd] = wcs.pixelxy2radec(1, 1) ccds.ra1[iccd], ccds.dec1[iccd] = wcs.pixelxy2radec(1, H) ccds.ra2[iccd], ccds.dec2[iccd] = wcs.pixelxy2radec(W, H) ccds.ra3[iccd], ccds.dec3[iccd] = wcs.pixelxy2radec(W, 1) midx, midy = (W + 1) / 2., (H + 1) / 2. rc, dc = wcs.pixelxy2radec(midx, midy) ra, dec = wcs.pixelxy2radec([1, W, midx, midx], [midy, midy, 1, H]) ccds.dra[iccd] = max( degrees_between(ra, dc + np.zeros_like(ra), rc, dc)) ccds.ddec[iccd] = max( degrees_between(rc + np.zeros_like(dec), dec, rc, dc)) ccds.ra_center[iccd] = rc ccds.dec_center[iccd] = dc # Compute scale change across the chip # how many pixels to step step = 10 xx = np.linspace(1 + step, W - step, 5) yy = np.linspace(1 + step, H - step, 5) xx, yy = np.meshgrid(xx, yy) pixscale = [] for x, y in zip(xx.ravel(), yy.ravel()): sx = [x - step, x - step, x + step, x + step, x - step] sy = [y - step, y + step, y + step, y - step, y - step] sr, sd = wcs.pixelxy2radec(sx, sy) rc, dc = wcs.pixelxy2radec(x, y) # project around a tiny little TAN WCS at (x,y), with 1" pixels locwcs = Tan(rc, dc, 0., 0., 1. / 3600, 0., 0., 1. / 3600, 1., 1.) ok, lx, ly = locwcs.radec2pixelxy(sr, sd) #print('local x,y:', lx, ly) A = polygon_area((lx, ly)) pixscale.append(np.sqrt(A / (2 * step)**2)) # print('Pixel scales:', pixscale) ccds.pixscale_mean[iccd] = np.mean(pixscale) ccds.pixscale_min[iccd] = min(pixscale) ccds.pixscale_max[iccd] = max(pixscale) ccds.pixscale_std[iccd] = np.std(pixscale) ccds.plver = np.array(plvers) sfd = tractor.sfd.SFDMap() allbands = 'ugrizY' filts = ['%s %s' % ('DES', f) for f in allbands] wisebands = ['WISE W1', 'WISE W2', 'WISE W3', 'WISE W4'] ebv, ext = sfd.extinction(filts + wisebands, ccds.ra_center, ccds.dec_center, get_ebv=True) ext = ext.astype(np.float32) ccds.ebv = ebv.astype(np.float32) ccds.decam_extinction = ext[:, :len(allbands)] ccds.wise_extinction = ext[:, len(allbands):] # Depth detsig1 = ccds.sig1 / ccds.psfnorm_mean depth = 5. * detsig1 # that's flux in nanomaggies -- convert to mag ccds.psfdepth = -2.5 * (np.log10(depth) - 9) detsig1 = ccds.sig1 / ccds.galnorm_mean depth = 5. * detsig1 # that's flux in nanomaggies -- convert to mag ccds.galdepth = -2.5 * (np.log10(depth) - 9) # Depth using Gaussian FWHM. psf_sigma = ccds.fwhm / 2.35 gnorm = 1. / (2. * np.sqrt(np.pi) * psf_sigma) detsig1 = ccds.sig1 / gnorm depth = 5. * detsig1 # that's flux in nanomaggies -- convert to mag ccds.gausspsfdepth = -2.5 * (np.log10(depth) - 9) # Gaussian galaxy depth detsig1 = ccds.sig1 / gaussgalnorm depth = 5. * detsig1 # that's flux in nanomaggies -- convert to mag ccds.gaussgaldepth = -2.5 * (np.log10(depth) - 9) ccds.writeto(outfn)
def distanceFrom(self, pos): from astrometry.util.starutil_numpy import degrees_between return degrees_between(self.ra, self.dec, pos.ra, pos.dec)
def annotate(ccds, survey, mzls=False, normalizePsf=False): # File from the "observing" svn repo: if mzls: # https://desi.lbl.gov/svn/decam/code/mosaic3/trunk tiles = fits_table('mosaic-tiles_obstatus.fits') else: # https://desi.lbl.gov/svn/decam/code/observing/trunk tiles = fits_table('decam-tiles_obstatus.fits') # Map tile IDs back to index in the obstatus file. tileid_to_index = np.empty(max(tiles.tileid)+1, int) tileid_to_index[:] = -1 tileid_to_index[tiles.tileid] = np.arange(len(tiles)) #assert('ccd_cuts' in ccds.get_columns()) gaussgalnorm = np.zeros(len(ccds), np.float32) for iccd,ccd in enumerate(ccds): print('Reading CCD %i of %i:' % (iccd+1, len(ccds)), 'file', ccd.image_filename, 'CCD', ccd.expnum, ccd.ccdname) try: im = survey.get_image_object(ccd) except: print('Failed to get_image_object()') import traceback traceback.print_exc() continue print('Reading CCD %i of %i:' % (iccd+1, len(ccds)), im, 'file', ccd.image_filename, 'CCD', ccd.ccdname) X = im.get_good_image_subregion() for i,x in enumerate(X): if x is not None: ccds.good_region[iccd,i] = x W,H = ccd.width, ccd.height kwargs = dict(pixPsf=True, splinesky=True, subsky=False, pixels=False, dq=False, invvar=False, normalizePsf=normalizePsf) psf = None wcs = None sky = None try: tim = im.get_tractor_image(**kwargs) except: print('Failed to get_tractor_image') import traceback traceback.print_exc() continue if tim is None: continue psf = tim.psf wcs = tim.wcs.wcs sky = tim.sky hdr = tim.primhdr # print('CCD fwhm:', ccd.fwhm) # print('im fwhm:', im.fwhm) # print('tim psf_fwhm', tim.psf_fwhm) # print('tim psf_sigma:', tim.psf_sigma) # print('Got PSF', psf) # print('Got sky', type(sky)) # print('Got WCS', wcs) # print('sig1', tim.sig1) ccds.humidity[iccd] = hdr.get('HUMIDITY') ccds.outtemp[iccd] = hdr.get('OUTTEMP') ccds.sig1[iccd] = tim.sig1 ccds.plver[iccd] = tim.plver # parse 'DECaLS_15150_r' to get tile number obj = ccd.object.strip() words = obj.split('_') tile = None if len(words) == 3 and ( ((not mzls) and (words[0] == 'DECaLS')) or ( mzls and (words[0] in ['MzLS','MOSAIC']))): try: tileid = int(words[1]) tile = tiles[tileid_to_index[tileid]] assert(tile.tileid == tileid) except: import traceback traceback.print_exc() pass if tile is not None: ccds.tileid [iccd] = tile.tileid ccds.tilepass[iccd] = tile.get('pass') ccds.tileebv [iccd] = tile.ebv_med # Instantiate PSF on a grid S = 32 xx = np.linspace(1+S, W-S, 5) yy = np.linspace(1+S, H-S, 5) xx,yy = np.meshgrid(xx, yy) psfnorms = [] galnorms = [] for x,y in zip(xx.ravel(), yy.ravel()): # HACK -- DR4 PSF sampling issue tim.psf = psf.constantPsfAt(x, y) p = im.psf_norm(tim, x=x, y=y) g = im.galaxy_norm(tim, x=x, y=y) psfnorms.append(p) galnorms.append(g) tim.psf = psf ccds.psfnorm_mean[iccd] = np.mean(psfnorms) ccds.psfnorm_std [iccd] = np.std (psfnorms) ccds.galnorm_mean[iccd] = np.mean(galnorms) ccds.galnorm_std [iccd] = np.std (galnorms) #print('psf norm', ccds.psfnorm_mean[iccd]) #print('gal norm', ccds.galnorm_mean[iccd]) # PSF in center of field cx,cy = (W+1)/2., (H+1)/2. p = psf.getPointSourcePatch(cx, cy).patch ph,pw = p.shape px,py = np.meshgrid(np.arange(pw), np.arange(ph)) psum = np.sum(p) # print('psum', psum) p /= psum # centroids cenx = np.sum(p * px) ceny = np.sum(p * py) # print('cenx,ceny', cenx,ceny) # second moments x2 = np.sum(p * (px - cenx)**2) y2 = np.sum(p * (py - ceny)**2) xy = np.sum(p * (px - cenx)*(py - ceny)) # semi-major/minor axes and position angle theta = np.rad2deg(np.arctan2(2 * xy, x2 - y2) / 2.) theta = np.abs(theta) * np.sign(xy) s = np.sqrt(((x2 - y2)/2.)**2 + xy**2) a = np.sqrt((x2 + y2) / 2. + s) b = np.sqrt((x2 + y2) / 2. - s) ell = 1. - b/a # print('PSF second moments', x2, y2, xy) # print('PSF position angle', theta) # print('PSF semi-axes', a, b) # print('PSF ellipticity', ell) ccds.psf_mx2[iccd] = x2 ccds.psf_my2[iccd] = y2 ccds.psf_mxy[iccd] = xy ccds.psf_a[iccd] = a ccds.psf_b[iccd] = b ccds.psf_theta[iccd] = theta ccds.psf_ell [iccd] = ell #print('Computing Gaussian approximate PSF quantities...') # Galaxy norm using Gaussian approximation of PSF. realpsf = tim.psf #print('FWHM', ccds.fwhm[i]) #print('-> sigma', ccds.fwhm[i] / 2.35) #print('tim.PSF sigma', tim.psf_sigma) tim.psf = im.read_psf_model(0, 0, gaussPsf=True, psf_sigma=tim.psf_sigma) gaussgalnorm[iccd] = im.galaxy_norm(tim, x=cx, y=cy) psfnorm = im.psf_norm(tim) pnorm = 1./(2. * np.sqrt(np.pi) * tim.psf_sigma) #print('Gaussian PSF norm:', psfnorm, 'vs analytic', pnorm) #print('Gaussian gal norm:', gaussgalnorm[iccd]) tim.psf = realpsf has_skygrid = hasattr(sky, 'evaluateGrid') # Sky -- evaluate on a grid (every ~10th pixel) if has_skygrid: skygrid = sky.evaluateGrid(np.linspace(0, ccd.width-1, int(1+ccd.width/10)), np.linspace(0, ccd.height-1, int(1+ccd.height/10))) ccds.meansky[iccd] = np.mean(skygrid) ccds.stdsky[iccd] = np.std(skygrid) ccds.maxsky[iccd] = skygrid.max() ccds.minsky[iccd] = skygrid.min() else: skyval = sky.getConstant() ccds.meansky[iccd] = skyval ccds.stdsky[iccd] = 0. ccds.maxsky[iccd] = skyval ccds.minsky[iccd] = skyval # WCS ccds.ra0[iccd],ccds.dec0[iccd] = wcs.pixelxy2radec(1, 1) ccds.ra1[iccd],ccds.dec1[iccd] = wcs.pixelxy2radec(1, H) ccds.ra2[iccd],ccds.dec2[iccd] = wcs.pixelxy2radec(W, H) ccds.ra3[iccd],ccds.dec3[iccd] = wcs.pixelxy2radec(W, 1) midx, midy = (W+1)/2., (H+1)/2. rc,dc = wcs.pixelxy2radec(midx, midy) ra,dec = wcs.pixelxy2radec([1,W,midx,midx], [midy,midy,1,H]) ccds.dra [iccd] = max(degrees_between(ra, dc+np.zeros_like(ra), rc, dc)) ccds.ddec[iccd] = max(degrees_between(rc+np.zeros_like(dec), dec, rc, dc)) ccds.ra_center [iccd] = rc ccds.dec_center[iccd] = dc # Compute scale change across the chip # how many pixels to step step = 10 xx = np.linspace(1+step, W-step, 5) yy = np.linspace(1+step, H-step, 5) xx,yy = np.meshgrid(xx, yy) pixscale = [] for x,y in zip(xx.ravel(), yy.ravel()): sx = [x-step, x-step, x+step, x+step, x-step] sy = [y-step, y+step, y+step, y-step, y-step] sr,sd = wcs.pixelxy2radec(sx, sy) rc,dc = wcs.pixelxy2radec(x, y) # project around a tiny little TAN WCS at (x,y), with 1" pixels locwcs = Tan(rc, dc, 0., 0., 1./3600, 0., 0., 1./3600, 1., 1.) ok,lx,ly = locwcs.radec2pixelxy(sr, sd) #print('local x,y:', lx, ly) A = polygon_area((lx, ly)) pixscale.append(np.sqrt(A / (2*step)**2)) # print('Pixel scales:', pixscale) ccds.pixscale_mean[iccd] = np.mean(pixscale) ccds.pixscale_min[iccd] = min(pixscale) ccds.pixscale_max[iccd] = max(pixscale) ccds.pixscale_std[iccd] = np.std(pixscale) ccds.annotated[iccd] = True sfd = tractor.sfd.SFDMap() allbands = 'ugrizY' filts = ['%s %s' % ('DES', f) for f in allbands] wisebands = ['WISE W1', 'WISE W2', 'WISE W3', 'WISE W4'] ebv,ext = sfd.extinction(filts + wisebands, ccds.ra_center, ccds.dec_center, get_ebv=True) ext[np.logical_not(ccds.annotated),:] = 0. ebv[np.logical_not(ccds.annotated)] = 0. ext = ext.astype(np.float32) ccds.ebv = ebv.astype(np.float32) ccds.decam_extinction = ext[:,:len(allbands)] ccds.wise_extinction = ext[:,len(allbands):] # Depth detsig1 = ccds.sig1 / ccds.psfnorm_mean depth = 5. * detsig1 # that's flux in nanomaggies -- convert to mag ccds.psfdepth = -2.5 * (np.log10(depth) - 9) detsig1 = ccds.sig1 / ccds.galnorm_mean depth = 5. * detsig1 # that's flux in nanomaggies -- convert to mag ccds.galdepth = -2.5 * (np.log10(depth) - 9) # Depth using Gaussian FWHM. psf_sigma = ccds.fwhm / 2.35 gnorm = 1./(2. * np.sqrt(np.pi) * psf_sigma) detsig1 = ccds.sig1 / gnorm depth = 5. * detsig1 # that's flux in nanomaggies -- convert to mag ccds.gausspsfdepth = -2.5 * (np.log10(depth) - 9) # Gaussian galaxy depth detsig1 = ccds.sig1 / gaussgalnorm depth = 5. * detsig1 # that's flux in nanomaggies -- convert to mag ccds.gaussgaldepth = -2.5 * (np.log10(depth) - 9) # NaN depths -> 0 for X in [ccds.psfdepth, ccds.galdepth, ccds.gausspsfdepth, ccds.gaussgaldepth]: X[np.logical_not(np.isfinite(X))] = 0.
def read_star_clusters(targetwcs): """ Code to regenerate the NGC-star-clusters-fits catalog: wget https://raw.githubusercontent.com/mattiaverga/OpenNGC/master/NGC.csv import os import numpy as np import numpy.ma as ma from astropy.io import ascii from astrometry.util.starutil_numpy import hmsstring2ra, dmsstring2dec import desimodel.io import desimodel.footprint tiles = desimodel.io.load_tiles(onlydesi=True) names = ('name', 'type', 'ra_hms', 'dec_dms', 'const', 'majax', 'minax', 'pa', 'bmag', 'vmag', 'jmag', 'hmag', 'kmag', 'sbrightn', 'hubble', 'cstarumag', 'cstarbmag', 'cstarvmag', 'messier', 'ngc', 'ic', 'cstarnames', 'identifiers', 'commonnames', 'nednotes', 'ongcnotes') NGC = ascii.read('NGC.csv', delimiter=';', names=names) NGC = NGC[(NGC['ra_hms'] != 'N/A')] ra, dec = [], [] for _ra, _dec in zip(ma.getdata(NGC['ra_hms']), ma.getdata(NGC['dec_dms'])): ra.append(hmsstring2ra(_ra.replace('h', ':').replace('m', ':').replace('s',''))) dec.append(dmsstring2dec(_dec.replace('d', ':').replace('m', ':').replace('s',''))) NGC['ra'] = ra NGC['dec'] = dec objtype = np.char.strip(ma.getdata(NGC['type'])) # Keep all globular clusters and planetary nebulae keeptype = ('PN', 'GCl') keep = np.zeros(len(NGC), dtype=bool) for otype in keeptype: ww = [otype == tt for tt in objtype] keep = np.logical_or(keep, ww) print(np.sum(keep)) clusters = NGC[keep] # Fill missing major axes with a nominal 0.4 arcmin (roughly works # for NGC7009, which is the only missing PN in the footprint). ma.set_fill_value(clusters['majax'], 0.4) clusters['majax'] = ma.filled(clusters['majax'].data) indesi = desimodel.footprint.is_point_in_desi(tiles, ma.getdata(clusters['ra']), ma.getdata(clusters['dec'])) print(np.sum(indesi)) bb = clusters[indesi] bb[np.argsort(bb['majax'])[::-1]]['name', 'ra', 'dec', 'majax', 'type'] clusters.write('NGC-star-clusters.fits', overwrite=True) # Code to help visually check all open clusters that are in the DESI footprint. checktype = ('OCl', 'Cl+N') check = np.zeros(len(NGC), dtype=bool) for otype in checktype: ww = [otype == tt for tt in objtype] check = np.logical_or(check, ww) check_clusters = NGC[check] # 845 of them # Write out a catalog, load it into the viewer and look at each of them. check_clusters[['ra', 'dec', 'name']][indesi].write('check.fits', overwrite=True) # 25 of them """ from pkg_resources import resource_filename from astrometry.util.starutil_numpy import degrees_between clusterfile = resource_filename('legacypipe', 'data/NGC-star-clusters.fits') debug('Reading {}'.format(clusterfile)) clusters = fits_table(clusterfile, columns=['ra', 'dec', 'majax', 'type']) clusters.ref_id = np.arange(len(clusters)) radius = 1. rc, dc = targetwcs.radec_center() d = degrees_between(rc, dc, clusters.ra, clusters.dec) clusters.cut(d < radius) if len(clusters) == 0: return None debug('Cut to {} star cluster(s) within the brick'.format(len(clusters))) # For each cluster, add a single faint star at the same coordinates, but # set the isbright bit so we get all the brightstarinblob logic. #clusters.ref_cat = clusters.name clusters.ref_cat = np.array(['CL'] * len(clusters)) clusters.mag = np.array([35] * len(clusters)) # Radius in degrees (from "majax" in arcmin) clusters.radius = clusters.majax / 60. clusters.radius[np.logical_not(np.isfinite(clusters.radius))] = 1. / 60. # Set isbright=True clusters.isbright = np.zeros(len(clusters), bool) clusters.iscluster = np.ones(len(clusters), bool) return clusters
def map_decam_depth(req, ver, zoom, x, y, savecache=False, band=None, ignoreCached=False): global Tdepth global Tdepthkd if band is None: band = req.GET.get('band') if not band in ['g', 'r', 'z']: raise RuntimeError('Invalid band') tag = 'decam-depth-%s' % band zoom = int(zoom) zoomscale = 2.**zoom x = int(x) y = int(y) if zoom < 0 or x < 0 or y < 0 or x >= zoomscale or y >= zoomscale: raise RuntimeError('Invalid zoom,x,y %i,%i,%i' % (zoom, x, y)) ver = int(ver) if not ver in tileversions[tag]: raise RuntimeError('Invalid version %i for tag %s' % (ver, tag)) basedir = settings.DATA_DIR tilefn = os.path.join(basedir, 'tiles', tag, '%i/%i/%i/%i.jpg' % (ver, zoom, x, y)) if os.path.exists(tilefn) and not ignoreCached: print('Cached:', tilefn) return send_file(tilefn, 'image/jpeg', expires=oneyear, modsince=req.META.get('HTTP_IF_MODIFIED_SINCE')) from astrometry.util.util import Tan from astrometry.libkd.spherematch import match_radec from astrometry.libkd.spherematch import tree_build_radec, tree_search_radec from astrometry.util.fits import fits_table from astrometry.util.starutil_numpy import degrees_between import numpy as np import fitsio try: wcs, W, H, zoomscale, zoom, x, y = get_tile_wcs(zoom, x, y) except RuntimeError as e: return HttpResponse(e.strerror) rlo, d = wcs.pixelxy2radec(W, H / 2)[-2:] rhi, d = wcs.pixelxy2radec(1, H / 2)[-2:] r, d1 = wcs.pixelxy2radec(W / 2, 1)[-2:] r, d2 = wcs.pixelxy2radec(W / 2, H)[-2:] r, d = wcs.pixelxy2radec(W / 2, H / 2)[-2:] rad = max(degrees_between(r, d, rlo, d1), degrees_between(r, d, rhi, d2)) if Tdepth is None: T = fits_table(os.path.join(basedir, 'decals-zpt-nondecals.fits'), columns=[ 'ccdra', 'ccddec', 'arawgain', 'avsky', 'ccdzpt', 'filter', 'crpix1', 'crpix2', 'crval1', 'crval2', 'cd1_1', 'cd1_2', 'cd2_1', 'cd2_2', 'naxis1', 'naxis2', 'exptime', 'fwhm' ]) T.rename('ccdra', 'ra') T.rename('ccddec', 'dec') Tdepth = {} Tdepthkd = {} for b in ['g', 'r', 'z']: Tdepth[b] = T[T.filter == b] Tdepthkd[b] = tree_build_radec(Tdepth[b].ra, Tdepth[b].dec) T = Tdepth[band] Tkd = Tdepthkd[band] #I,J,d = match_radec(T.ra, T.dec, r, d, rad + 0.2) I = tree_search_radec(Tkd, r, d, rad + 0.2) print(len(I), 'CCDs in range') if len(I) == 0: from django.http import HttpResponseRedirect return HttpResponseRedirect(settings.STATIC_URL + 'blank.jpg') depthiv = np.zeros((H, W), np.float32) for t in T[I]: twcs = Tan(*[ float(x) for x in [ t.crval1, t.crval2, t.crpix1, t.crpix2, t.cd1_1, t.cd1_2, t.cd2_1, t.cd2_2, t.naxis1, t.naxis2 ] ]) w, h = t.naxis1, t.naxis2 r, d = twcs.pixelxy2radec([1, 1, w, w], [1, h, h, 1]) ok, x, y = wcs.radec2pixelxy(r, d) #print 'x,y coords of CCD:', x, y x0 = int(x.min()) x1 = int(x.max()) y0 = int(y.min()) y1 = int(y.max()) if y1 < 0 or x1 < 0 or x0 >= W or y0 >= H: continue readnoise = 10. # e-; 7.0 to 15.0 according to DECam Data Handbook skysig = np.sqrt(t.avsky * t.arawgain + readnoise**2) / t.arawgain zpscale = 10.**((t.ccdzpt - 22.5) / 2.5) * t.exptime sig1 = skysig / zpscale psf_sigma = t.fwhm / 2.35 # point-source depth psfnorm = 1. / (2. * np.sqrt(np.pi) * psf_sigma) detsig1 = sig1 / psfnorm #print '5-sigma point-source depth:', NanoMaggies.nanomaggiesToMag(detsig1 * 5.) div = 1 / detsig1**2 depthiv[max(y0, 0):min(y1, H), max(x0, 0):min(x1, W)] += div ptsrc = -2.5 * (np.log10(np.sqrt(1. / depthiv) * 5) - 9) ptsrc[depthiv == 0] = 0. if savecache: trymakedirs(tilefn) else: import tempfile f, tilefn = tempfile.mkstemp(suffix='.jpg') os.close(f) import pylab as plt plt.imsave(tilefn, ptsrc, vmin=22., vmax=25., cmap='hot') #nipy_spectral') return send_file(tilefn, 'image/jpeg', unlink=(not savecache))
def map_sdss(req, ver, zoom, x, y, savecache=None, tag='sdss', get_images=False, ignoreCached=False, wcs=None, forcecache=False, forcescale=None, **kwargs): from decals import settings if savecache is None: savecache = settings.SAVE_CACHE zoom = int(zoom) zoomscale = 2.**zoom x = int(x) y = int(y) if zoom < 0 or x < 0 or y < 0 or x >= zoomscale or y >= zoomscale: raise RuntimeError('Invalid zoom,x,y %i,%i,%i' % (zoom,x,y)) ver = int(ver) if not ver in tileversions[tag]: raise RuntimeError('Invalid version %i for tag %s' % (ver, tag)) basedir = settings.DATA_DIR tilefn = os.path.join(basedir, 'tiles', tag, '%i/%i/%i/%i.jpg' % (ver, zoom, x, y)) if os.path.exists(tilefn) and not ignoreCached: if get_images: return None return send_file(tilefn, 'image/jpeg', expires=oneyear, modsince=req.META.get('HTTP_IF_MODIFIED_SINCE')) if not savecache: import tempfile f,tilefn = tempfile.mkstemp(suffix='.jpg') os.close(f) if wcs is None: try: wcs, W, H, zoomscale, zoom,x,y = get_tile_wcs(zoom, x, y) except RuntimeError as e: if get_images: return None return HttpResponse(e.strerror) else: W = wcs.get_width() H = wcs.get_height() from astrometry.util.fits import fits_table import numpy as np from astrometry.libkd.spherematch import tree_build_radec, tree_search_radec from astrometry.util.starutil_numpy import degrees_between, arcsec_between from astrometry.util.resample import resample_with_wcs, OverlapError from astrometry.util.util import Tan, Sip import fitsio print 'Tile wcs: center', wcs.radec_center(), 'pixel scale', wcs.pixel_scale() global w_flist global w_flist_tree if w_flist is None: w_flist = fits_table(os.path.join(settings.DATA_DIR, 'sdss', 'window_flist.fits'), columns=['run','rerun','camcol','field','ra','dec','score']) print 'Read', len(w_flist), 'window_flist entries' w_flist.cut(w_flist.rerun == '301') print 'Cut to', len(w_flist), 'in rerun 301' w_flist_tree = tree_build_radec(w_flist.ra, w_flist.dec) # SDSS field size radius = 1.01 * np.hypot(10., 14.)/2. / 60. # leaflet tile size ra,dec = wcs.pixelxy2radec(W/2., H/2.)[-2:] r0,d0 = wcs.pixelxy2radec(1, 1)[-2:] r1,d1 = wcs.pixelxy2radec(W, H)[-2:] radius = radius + max(degrees_between(ra,dec, r0,d0), degrees_between(ra,dec, r1,d1)) J = tree_search_radec(w_flist_tree, ra, dec, radius) print len(J), 'overlapping SDSS fields found' if len(J) == 0: if get_images: return None if forcecache: # create symlink to blank.jpg! trymakedirs(tilefn) src = os.path.join(settings.STATIC_ROOT, 'blank.jpg') if os.path.exists(tilefn): os.unlink(tilefn) os.symlink(src, tilefn) print 'Symlinked', tilefn, '->', src from django.http import HttpResponseRedirect return HttpResponseRedirect(settings.STATIC_URL + 'blank.jpg') ww = [1, W*0.25, W*0.5, W*0.75, W] hh = [1, H*0.25, H*0.5, H*0.75, H] r,d = wcs.pixelxy2radec( [1]*len(hh) + ww + [W]*len(hh) + list(reversed(ww)), hh + [1]*len(ww) + list(reversed(hh)) + [H]*len(ww))[-2:] scaled = 0 scalepat = None scaledir = 'sdss' if zoom <= 13 and forcescale is None: # Get *actual* pixel scales at the top & bottom r1,d1 = wcs.pixelxy2radec(W/2., H)[-2:] r2,d2 = wcs.pixelxy2radec(W/2., H-1.)[-2:] r3,d3 = wcs.pixelxy2radec(W/2., 1.)[-2:] r4,d4 = wcs.pixelxy2radec(W/2., 2.)[-2:] # Take the min = most zoomed-in scale = min(arcsec_between(r1,d1, r2,d2), arcsec_between(r3,d3, r4,d4)) native_scale = 0.396 scaled = int(np.floor(np.log2(scale / native_scale))) print 'Zoom:', zoom, 'x,y', x,y, 'Tile pixel scale:', scale, 'Scale step:', scaled scaled = np.clip(scaled, 1, 7) dirnm = os.path.join(basedir, 'scaled', scaledir) scalepat = os.path.join(dirnm, '%(scale)i%(band)s', '%(rerun)s', '%(run)i', '%(camcol)i', 'sdss-%(run)i-%(camcol)i-%(field)i-%(band)s.fits') if forcescale is not None: scaled = forcescale bands = 'gri' rimgs = [np.zeros((H,W), np.float32) for band in bands] rns = [np.zeros((H,W), np.float32) for band in bands] from astrometry.sdss import AsTransWrapper, DR9 sdss = DR9(basedir=settings.SDSS_DIR) sdss.saveUnzippedFiles(settings.SDSS_DIR) #sdss.setFitsioReadBZ2() if settings.SDSS_PHOTOOBJS: sdss.useLocalTree(photoObjs=settings.SDSS_PHOTOOBJS, resolve=settings.SDSS_RESOLVE) for jnum,j in enumerate(J): print 'SDSS field', jnum, 'of', len(J), 'for zoom', zoom, 'x', x, 'y', y im = w_flist[j] if im.score >= 0.5: weight = 1. else: weight = 0.001 for band,rimg,rn in zip(bands, rimgs, rns): if im.rerun != '301': continue tmpsuff = '.tmp%08i' % np.random.randint(100000000) basefn = sdss.retrieve('frame', im.run, im.camcol, field=im.field, band=band, rerun=im.rerun, tempsuffix=tmpsuff) if scaled > 0: fnargs = dict(band=band, rerun=im.rerun, run=im.run, camcol=im.camcol, field=im.field) fn = get_scaled(scalepat, fnargs, scaled, basefn, read_base_wcs=read_astrans, read_wcs=_read_sip_wcs) print 'get_scaled:', fn else: fn = basefn frame = None if fn == basefn: frame = sdss.readFrame(im.run, im.camcol, im.field, band, filename=fn) h,w = frame.getImageShape() # Trim off the overlapping top of the image # Wimp out and instead of trimming 128 pix, trim 124! trim = 124 subh = h - trim astrans = frame.getAsTrans() fwcs = AsTransWrapper(astrans, w, subh) fullimg = frame.getImage() fullimg = fullimg[:-trim,:] else: fwcs = Sip(fn) fitsimg = fitsio.FITS(fn)[0] h,w = fitsimg.get_info()['dims'] fullimg = fitsimg.read() try: #Yo,Xo,Yi,Xi,nil = resample_with_wcs(wcs, fwcs, [], 3) Yo,Xo,Yi,Xi,[resamp] = resample_with_wcs(wcs, fwcs, [fullimg], 2) except OverlapError: continue if len(Xi) == 0: #print 'No overlap' continue if sdssps is not None: x0 = Xi.min() x1 = Xi.max() y0 = Yi.min() y1 = Yi.max() slc = (slice(y0,y1+1), slice(x0,x1+1)) if frame is not None: img = frame.getImageSlice(slc) else: img = fitsimg[slc] #rimg[Yo,Xo] += img[Yi-y0, Xi-x0] rimg[Yo,Xo] += resamp * weight rn [Yo,Xo] += weight if sdssps is not None: # goodpix = np.ones(img.shape, bool) # fpM = sdss.readFpM(im.run, im.camcol, im.field, band) # for plane in [ 'INTERP', 'SATUR', 'CR', 'GHOST' ]: # fpM.setMaskedPixels(plane, goodpix, False, roi=[x0,x1,y0,y1]) plt.clf() #ima = dict(vmin=-0.05, vmax=0.5) #ima = dict(vmin=-0.5, vmax=2.) ima = dict(vmax=np.percentile(img, 99)) plt.subplot(2,3,1) dimshow(img, ticks=False, **ima) plt.title('image') rthis = np.zeros_like(rimg) #rthis[Yo,Xo] += img[Yi-y0, Xi-x0] rthis[Yo,Xo] += resamp plt.subplot(2,3,2) dimshow(rthis, ticks=False, **ima) plt.title('resampled') # plt.subplot(2,3,3) # dimshow(goodpix, ticks=False, vmin=0, vmax=1) # plt.title('good pix') plt.subplot(2,3,4) dimshow(rimg / np.maximum(rn, 1), ticks=False, **ima) plt.title('coadd') plt.subplot(2,3,5) dimshow(rn, vmin=0, ticks=False) plt.title('coverage: max %i' % rn.max()) plt.subplot(2,3,6) rgb = sdss_rgb([rimg/np.maximum(rn,1) for rimg,rn in zip(rimgs,rns)], bands) dimshow(rgb) plt.suptitle('SDSS %s, R/C/F %i/%i/%i' % (band, im.run, im.camcol, im.field)) sdssps.savefig() for rimg,rn in zip(rimgs, rns): rimg /= np.maximum(rn, 1e-3) del rns if get_images: return rimgs rgb = sdss_rgb(rimgs, bands) trymakedirs(tilefn) save_jpeg(tilefn, rgb) print 'Wrote', tilefn return send_file(tilefn, 'image/jpeg', unlink=(not savecache))
def map_decals_wl(req, ver, zoom, x, y): tag = 'decals-wl' ignoreCached = False filename = None forcecache = False from decals import settings savecache = settings.SAVE_CACHE zoom = int(zoom) zoomscale = 2.**zoom x = int(x) y = int(y) if zoom < 0 or x < 0 or y < 0 or x >= zoomscale or y >= zoomscale: raise RuntimeError('Invalid zoom,x,y %i,%i,%i' % (zoom, x, y)) ver = int(ver) if not ver in tileversions[tag]: raise RuntimeError('Invalid version %i for tag %s' % (ver, tag)) basedir = settings.DATA_DIR tilefn = os.path.join(basedir, 'tiles', tag, '%i/%i/%i/%i.jpg' % (ver, zoom, x, y)) if os.path.exists(tilefn) and not ignoreCached: print('Cached:', tilefn) return send_file(tilefn, 'image/jpeg', expires=oneyear, modsince=req.META.get('HTTP_IF_MODIFIED_SINCE'), filename=filename) else: print('Tile image does not exist:', tilefn) from astrometry.util.resample import resample_with_wcs, OverlapError from astrometry.util.util import Tan from astrometry.libkd.spherematch import match_radec from astrometry.util.fits import fits_table from astrometry.util.starutil_numpy import degrees_between import numpy as np import fitsio try: wcs, W, H, zoomscale, zoom, x, y = get_tile_wcs(zoom, x, y) except RuntimeError as e: return HttpResponse(e.strerror) mydir = os.path.join(basedir, 'coadd', 'weak-lensing') rlo, d = wcs.pixelxy2radec(W, H / 2)[-2:] rhi, d = wcs.pixelxy2radec(1, H / 2)[-2:] r, d1 = wcs.pixelxy2radec(W / 2, 1)[-2:] r, d2 = wcs.pixelxy2radec(W / 2, H)[-2:] #dlo = min(d1, d2) #dhi = max(d1, d2) r, d = wcs.pixelxy2radec(W / 2, H / 2)[-2:] rad = degrees_between(r, d, rlo, d1) fn = os.path.join(mydir, 'index.fits') if not os.path.exists(fn): # ii, rr, dd = [], [], [] for i in range(1, 52852 + 1): imgfn = os.path.join(mydir, 'map%i.fits' % i) hdr = fitsio.read_header(imgfn) r = hdr['CRVAL1'] d = hdr['CRVAL2'] ii.append(i) rr.append(r) dd.append(d) T = fits_table() T.ra = np.array(rr) T.dec = np.array(dd) T.i = np.array(ii) T.writeto(fn) T = fits_table(fn) I, J, d = match_radec(T.ra, T.dec, r, d, rad + 0.2) T.cut(I) print(len(T), 'weak-lensing maps in range') if len(I) == 0: from django.http import HttpResponseRedirect if forcecache: # create symlink to blank.jpg! trymakedirs(tilefn) src = os.path.join(settings.STATIC_ROOT, 'blank.jpg') if os.path.exists(tilefn): os.unlink(tilefn) os.symlink(src, tilefn) print('Symlinked', tilefn, '->', src) return HttpResponseRedirect(settings.STATIC_URL + 'blank.jpg') r, d = wcs.pixelxy2radec([1, 1, 1, W / 2, W, W, W, W / 2], [1, H / 2, H, H, H, H / 2, 1, 1])[-2:] foundany = False rimg = np.zeros((H, W), np.float32) rn = np.zeros((H, W), np.uint8) for tilei in T.i: fn = os.path.join(mydir, 'map%i.fits' % tilei) try: bwcs = read_tan_wcs(fn, 0) except: print('Failed to read WCS:', fn) savecache = False import traceback import sys traceback.print_exc(None, sys.stdout) continue foundany = True print('Reading', fn) ok, xx, yy = bwcs.radec2pixelxy(r, d) xx = xx.astype(np.int) yy = yy.astype(np.int) imW, imH = int(bwcs.get_width()), int(bwcs.get_height()) M = 10 xlo = np.clip(xx.min() - M, 0, imW) xhi = np.clip(xx.max() + M, 0, imW) ylo = np.clip(yy.min() - M, 0, imH) yhi = np.clip(yy.max() + M, 0, imH) if xlo >= xhi or ylo >= yhi: continue subwcs = bwcs.get_subimage(xlo, ylo, xhi - xlo, yhi - ylo) slc = slice(ylo, yhi), slice(xlo, xhi) try: f = fitsio.FITS(fn)[0] img = f[slc] del f except: print('Failed to read image and WCS:', fn) savecache = False import traceback import sys traceback.print_exc(None, sys.stdout) continue try: Yo, Xo, Yi, Xi, nil = resample_with_wcs(wcs, subwcs, [], 3) except OverlapError: print('Resampling exception') continue rimg[Yo, Xo] += img[Yi, Xi] rn[Yo, Xo] += 1 rimg /= np.maximum(rn, 1) if forcecache: savecache = True if savecache: trymakedirs(tilefn) else: import tempfile f, tilefn = tempfile.mkstemp(suffix='.jpg') os.close(f) import pylab as plt # S/N #lo,hi = 1.5, 5.0 lo, hi = 0, 5.0 rgb = plt.cm.hot((rimg - lo) / (hi - lo)) plt.imsave(tilefn, rgb) print('Wrote', tilefn) return send_file(tilefn, 'image/jpeg', unlink=(not savecache), filename=filename)
def map_decam_depth(req, ver, zoom, x, y, savecache=False, band=None, ignoreCached=False): global Tdepth global Tdepthkd if band is None: band = req.GET.get('band') if not band in ['g','r','z']: raise RuntimeError('Invalid band') tag = 'decam-depth-%s' % band zoom = int(zoom) zoomscale = 2.**zoom x = int(x) y = int(y) if zoom < 0 or x < 0 or y < 0 or x >= zoomscale or y >= zoomscale: raise RuntimeError('Invalid zoom,x,y %i,%i,%i' % (zoom,x,y)) ver = int(ver) if not ver in tileversions[tag]: raise RuntimeError('Invalid version %i for tag %s' % (ver, tag)) basedir = settings.DATA_DIR tilefn = os.path.join(basedir, 'tiles', tag, '%i/%i/%i/%i.jpg' % (ver, zoom, x, y)) if os.path.exists(tilefn) and not ignoreCached: print 'Cached:', tilefn return send_file(tilefn, 'image/jpeg', expires=oneyear, modsince=req.META.get('HTTP_IF_MODIFIED_SINCE')) from astrometry.util.util import Tan from astrometry.libkd.spherematch import match_radec from astrometry.libkd.spherematch import tree_build_radec, tree_search_radec from astrometry.util.fits import fits_table from astrometry.util.starutil_numpy import degrees_between import numpy as np import fitsio try: wcs, W, H, zoomscale, zoom,x,y = get_tile_wcs(zoom, x, y) except RuntimeError as e: return HttpResponse(e.strerror) rlo,d = wcs.pixelxy2radec(W, H/2)[-2:] rhi,d = wcs.pixelxy2radec(1, H/2)[-2:] r,d1 = wcs.pixelxy2radec(W/2, 1)[-2:] r,d2 = wcs.pixelxy2radec(W/2, H)[-2:] r,d = wcs.pixelxy2radec(W/2, H/2)[-2:] rad = max(degrees_between(r, d, rlo, d1), degrees_between(r, d, rhi, d2)) if Tdepth is None: T = fits_table(os.path.join(basedir, 'decals-zpt-nondecals.fits'), columns=['ccdra','ccddec','arawgain', 'avsky', 'ccdzpt', 'filter', 'crpix1','crpix2', 'crval1','crval2','cd1_1','cd1_2', 'cd2_1','cd2_2', 'naxis1', 'naxis2', 'exptime', 'fwhm']) T.rename('ccdra', 'ra') T.rename('ccddec', 'dec') Tdepth = {} Tdepthkd = {} for b in ['g','r','z']: Tdepth[b] = T[T.filter == b] Tdepthkd[b] = tree_build_radec(Tdepth[b].ra, Tdepth[b].dec) T = Tdepth[band] Tkd = Tdepthkd[band] #I,J,d = match_radec(T.ra, T.dec, r, d, rad + 0.2) I = tree_search_radec(Tkd, r, d, rad + 0.2) print len(I), 'CCDs in range' if len(I) == 0: from django.http import HttpResponseRedirect return HttpResponseRedirect(settings.STATIC_URL + 'blank.jpg') depthiv = np.zeros((H,W), np.float32) for t in T[I]: twcs = Tan(*[float(x) for x in [ t.crval1, t.crval2, t.crpix1, t.crpix2, t.cd1_1, t.cd1_2, t.cd2_1, t.cd2_2, t.naxis1, t.naxis2]]) w,h = t.naxis1, t.naxis2 r,d = twcs.pixelxy2radec([1,1,w,w], [1,h,h,1]) ok,x,y = wcs.radec2pixelxy(r, d) #print 'x,y coords of CCD:', x, y x0 = int(x.min()) x1 = int(x.max()) y0 = int(y.min()) y1 = int(y.max()) if y1 < 0 or x1 < 0 or x0 >= W or y0 >= H: continue readnoise = 10. # e-; 7.0 to 15.0 according to DECam Data Handbook skysig = np.sqrt(t.avsky * t.arawgain + readnoise**2) / t.arawgain zpscale = 10.**((t.ccdzpt - 22.5)/2.5) * t.exptime sig1 = skysig / zpscale psf_sigma = t.fwhm / 2.35 # point-source depth psfnorm = 1./(2. * np.sqrt(np.pi) * psf_sigma) detsig1 = sig1 / psfnorm #print '5-sigma point-source depth:', NanoMaggies.nanomaggiesToMag(detsig1 * 5.) div = 1 / detsig1**2 depthiv[max(y0,0):min(y1,H), max(x0,0):min(x1,W)] += div ptsrc = -2.5 * (np.log10(np.sqrt(1./depthiv) * 5) - 9) ptsrc[depthiv == 0] = 0. if savecache: trymakedirs(tilefn) else: import tempfile f,tilefn = tempfile.mkstemp(suffix='.jpg') os.close(f) import pylab as plt plt.imsave(tilefn, ptsrc, vmin=22., vmax=25., cmap='hot')#nipy_spectral') return send_file(tilefn, 'image/jpeg', unlink=(not savecache))