def test_pyregion_subset(): header = dict(naxis=2, crpix1=15, crpix2=15, crval1=0.1, crval2=0.1, cdelt1=-1. / 3600, cdelt2=1. / 3600., ctype1='GLON-CAR', ctype2='GLAT-CAR') mywcs = wcs.WCS(header) # circle with radius 10" at 0.1, 0.1 shape = Shape('circle', (0.1, 0.1, 10. / 3600.)) shape.coord_format = 'galactic' shape.coord_list = (0.1, 0.1, 10. / 3600.) shape.attr = ([], {}) data = np.ones([40, 40]) # The following line raises a DeprecationWarning from pyregion, ignore it with warnings.catch_warnings(): warnings.filterwarnings('ignore') (xlo, xhi, ylo, yhi), d = utils.pyregion_subset(shape, data, mywcs) # sticky note over check-engine light solution... but this problem is too # large in scope to address here. See # https://github.com/astropy/astropy/pull/3992 assert d.sum() >= 313 & d.sum() <= 315 # VERY approximately pi np.testing.assert_almost_equal(xlo, data.shape[0] / 2 - mywcs.wcs.crpix[0] - 1) np.testing.assert_almost_equal(xhi, data.shape[0] - mywcs.wcs.crpix[0] - 1) np.testing.assert_almost_equal(ylo, data.shape[1] / 2 - mywcs.wcs.crpix[1] - 1) np.testing.assert_almost_equal(yhi, data.shape[1] - mywcs.wcs.crpix[1] - 1)
def footprint_to_reg(footprint): """ ALMA footprints have the form: 'Polygon ICRS 266.519781 -28.724666 266.524678 -28.731930 266.536683 -28.737784 266.543860 -28.737586 266.549277 -28.733370 266.558133 -28.729545 266.560136 -28.724666 266.558845 -28.719605 266.560133 -28.694332 266.555234 -28.687069 266.543232 -28.681216 266.536058 -28.681414 266.530644 -28.685630 266.521788 -28.689453 266.519784 -28.694332 266.521332 -28.699778' Some of them have *additional* polygons """ if footprint[:7] != 'Polygon': raise ValueError("Unrecognized footprint type") from pyregion.parser_helper import Shape entries = footprint.split() polygons = [ii for ii, xx in enumerate(entries) if xx == 'Polygon'] reglist = [] for start, stop in zip(polygons, polygons[1:]+[None]): reg = Shape('polygon', [float(x) for x in entries[start+2:stop]]) reg.coord_format = footprint.split()[1].lower() reg.coord_list = reg.params # the coord_list attribute is needed somewhere reg.attr = ([], {'color': 'green', 'dash': '0 ', 'dashlist': '8 3', 'delete': '1 ', 'edit': '1 ', 'fixed': '0 ', 'font': '"helvetica 10 normal roman"', 'highlite': '1 ', 'include': '1 ', 'move': '1 ', 'select': '1', 'source': '1', 'text': '', 'width': '1 '}) reglist.append(reg) return reglist
def test_pyregion_subset(): header = dict(naxis=2, crpix1=15, crpix2=15, crval1=0.1, crval2=0.1, cdelt1=-1. / 3600, cdelt2=1. / 3600., ctype1='GLON-CAR', ctype2='GLAT-CAR') mywcs = wcs.WCS(header) # circle with radius 10" at 0.1, 0.1 shape = Shape('circle', (0.1, 0.1, 10. / 3600.)) shape.coord_format = 'galactic' shape.coord_list = (0.1, 0.1, 10. / 3600.) shape.attr = ([], {}) data = np.ones([40, 40]) (xlo, xhi, ylo, yhi), d = utils.pyregion_subset(shape, data, mywcs) assert d.sum() == 314 # approximately pi np.testing.assert_almost_equal(xlo, data.shape[0] / 2 - mywcs.wcs.crpix[0] - 1) np.testing.assert_almost_equal(xhi, data.shape[0] - mywcs.wcs.crpix[0] - 1) np.testing.assert_almost_equal(ylo, data.shape[1] / 2 - mywcs.wcs.crpix[1] - 1) np.testing.assert_almost_equal(yhi, data.shape[1] - mywcs.wcs.crpix[1] - 1)
def test_pyregion_subset(): header = dict(naxis=2, crpix1=15, crpix2=15, crval1=0.1, crval2=0.1, cdelt1=-1. / 3600, cdelt2=1. / 3600., ctype1='GLON-CAR', ctype2='GLAT-CAR') mywcs = wcs.WCS(header) # circle with radius 10" at 0.1, 0.1 shape = Shape('circle', (0.1, 0.1, 10. / 3600.)) shape.coord_format = 'galactic' shape.coord_list = (0.1, 0.1, 10. / 3600.) shape.attr = ([], {}) data = np.ones([40, 40]) (xlo, xhi, ylo, yhi), d = utils.pyregion_subset(shape, data, mywcs) # sticky note over check-engine light solution... but this problem is too # large in scope to address here. See # https://github.com/astropy/astropy/pull/3992 assert d.sum() >= 313 & d.sum() <= 315 # VERY approximately pi np.testing.assert_almost_equal(xlo, data.shape[0] / 2 - mywcs.wcs.crpix[0] - 1) np.testing.assert_almost_equal(xhi, data.shape[0] - mywcs.wcs.crpix[0] - 1) np.testing.assert_almost_equal(ylo, data.shape[1] / 2 - mywcs.wcs.crpix[1] - 1) np.testing.assert_almost_equal(yhi, data.shape[1] - mywcs.wcs.crpix[1] - 1)
def table_to_circ_region(table, outfile, racol='RA', deccol='DEC', sizecol='size', color='red', label=True): """ Get a table with ra, dec, size and generate a circular ds9 region TODO: if cat is given, add a small circle around all sources on the edge """ regions = [] for i, r in enumerate(table): s = Shape('circle', None) s.coord_format = 'fk5' s.coord_list = [r[racol], r[deccol], r[sizecol]] # ra, dec, radius s.coord_format = 'fk5' s.attr = ([], { 'width': '2', 'point': 'cross', 'font': '"helvetica 16 normal roman"' }) if label: s.comment = 'color={} text="{}"'.format(color, str(i)) else: s.comment = 'color={}'.format(color) regions.append(s) regions = pyregion.ShapeList(regions) check_rm(outfile) regions.write(outfile)
def makeBeamReg(self, outfile, pb_cut=None, to_null=False, freq='mid'): """ Create a ds9 region of the beam outfile : str output file pb_cut : float, optional diameter of the beam to_null : bool, optional arrive to the first null, not the FWHM freq: min,max,med which frequency to use to estimate the beam size """ logger.debug('Making PB region: '+outfile) ra, dec = self.getPhaseCentre() if pb_cut is None: radius = self.getFWHM(freq=freq)/2. else: radius = pb_cut/2. if to_null: radius *= 2 # rough estimation s = Shape('circle', None) s.coord_format = 'fk5' s.coord_list = [ ra, dec, radius ] # ra, dec, radius s.coord_format = 'fk5' s.attr = ([], {'width': '2', 'point': 'cross', 'font': '"helvetica 16 normal roman"'}) s.comment = 'color=red text="beam"' regions = pyregion.ShapeList([s]) lib_util.check_rm(outfile) regions.write(outfile)
def test_pyregion_subset(): header = dict(naxis=2, crpix1=15, crpix2=15, crval1=0.1, crval2=0.1, cdelt1=-1./3600, cdelt2=1./3600., ctype1='GLON-CAR', ctype2='GLAT-CAR') mywcs = wcs.WCS(header) # circle with radius 10" at 0.1, 0.1 shape = Shape('circle', (0.1, 0.1, 10./3600.)) shape.coord_format = 'galactic' shape.coord_list = (0.1, 0.1, 10./3600.) shape.attr = ([], {}) data = np.ones([40,40]) (xlo,xhi,ylo,yhi), d = utils.pyregion_subset(shape, data, mywcs) assert d.sum() == 314 # approximately pi np.testing.assert_almost_equal(xlo, data.shape[0]/2-mywcs.wcs.crpix[0]-1) np.testing.assert_almost_equal(xhi, data.shape[0]-mywcs.wcs.crpix[0]-1) np.testing.assert_almost_equal(ylo, data.shape[1]/2-mywcs.wcs.crpix[1]-1) np.testing.assert_almost_equal(yhi, data.shape[1]-mywcs.wcs.crpix[1]-1)
def set_region(self, loc): """ Creates a ds9 regionfile that covers the DD-cal model """ assert self.size is not None and self.position is not None # we need this to be already set self.region_file = loc+'/'+self.name+'.reg' s = Shape('circle', None) s.coord_format = 'fk5' s.coord_list = [ self.position[0], self.position[1], self.size/2. ] # ra, dec, radius s.coord_format = 'fk5' s.attr = ([], {'width': '2', 'point': 'cross', 'font': '"helvetica 16 normal roman"'}) if not self.peel_off: s.comment = 'color=red text="%s"' % self.name else: s.comment = 'color=blue text="%s"' % self.name regions = pyregion.ShapeList([s]) lib_util.check_rm(self.region_file) regions.write(self.region_file)
def makeBeamReg(self, outfile, pb_cut=None, to_null=False): """ Create a ds9 region of the beam outfile : str output file pb_cut : float, optional diameter of the beam to_null : bool, optional arrive to the first null, not the FWHM """ logger.debug('Making PB region: ' + outfile) ra, dec = self.getPhaseCentre() if pb_cut is None: if 'OUTER' in self.getObsMode(): size = 8. / 2. elif 'SPARSE' in self.getObsMode(): size = 12. / 2. elif 'INNER' in self.getObsMode(): size = 16. / 2. else: logger.error( 'Cannot find beam size, only LBA_OUTER or LBA_SPARSE_* are implemented. Assuming beam diameter = 8 deg.' ) size = 8. / 2. else: size = pb_cut / 2. if to_null: size *= 1.7 # rough estimation s = Shape('circle', None) s.coord_format = 'fk5' s.coord_list = [ra, dec, size] # ra, dec, radius s.coord_format = 'fk5' s.attr = ([], { 'width': '2', 'point': 'cross', 'font': '"helvetica 16 normal roman"' }) s.comment = 'color=red text="beam"' regions = pyregion.ShapeList([s]) lib_util.check_rm(outfile) regions.write(outfile)
def make_voronoi_reg(directions, fitsfile, outdir_reg='regions', out_mask='facet.fits', beam_reg=None, png=None): """ Take a list of coordinates and an image and voronoi tesselate the sky. It saves ds9 regions + fits mask of the facets directions : dict with {'Dir_0':[ra,dec], 'Dir_1':[ra,dec]...} - note that the "Dir_##" naming is important firsfile : mask fits file to tassellate (used for coordinates and to avoid splitting islands) outdir* : dir where to save regions/masks beam_reg : a ds9 region showing the the primary beam, exclude directions outside it """ def closest_node(node, nodes): """ Return closest values to node from nodes """ nodes = np.asarray(nodes) dist_2 = np.sum((nodes - node)**2, axis=1) return np.argmin(dist_2) import lib_img logger.debug("Image used for tasselation reference: " + fitsfile) fits = pyfits.open(fitsfile) hdr, data = lib_img.flatten(fits) w = pywcs.WCS(hdr) pixsize = np.abs(hdr['CDELT1']) # Get facets central pixels ras = np.array([directions[d][0].degree for d in directions]) decs = np.array([directions[d][1].degree for d in directions]) x_fs, y_fs = w.all_world2pix(ras, decs, 0, ra_dec_order=True) # keep trak of numbers in the direction names to name correctly patches in the fits files # in this way Dir_12 will have "12" into the fits for that patch. nums = [int(d.split('_')[1]) for d in directions.keys()] x_c = data.shape[0] / 2. y_c = data.shape[1] / 2. if beam_reg is None: # no beam, use all directions for facets idx_for_facet = range(len(directions)) else: r = pyregion.open(beam_reg) beam_mask = r.get_mask(header=hdr, shape=data.shape) beamradius_pix = r[0].coord_list[2] / pixsize idx_for_facet = [] for i, dd in enumerate(t): if beam_mask[t['x'][i], t['y'][i]] == True: idx_for_facet.append(i) # convert to pixel space (voronoi must be in eucledian space) x1 = 0 y1 = 0 x2 = data.shape[0] y2 = data.shape[1] # do tasselization vor = Voronoi( np.array((x_fs[idx_for_facet], y_fs[idx_for_facet])).transpose()) box = np.array([[x1, y1], [x2, y2]]) impoly = voronoi_finite_polygons_2d_box(vor, box) # create fits mask (each region one number) x, y = np.meshgrid(np.arange(x2), np.arange(y2)) # make a canvas with coordinates x, y = x.flatten(), y.flatten() pixels = np.vstack((x, y)).T data_facet = np.zeros(shape=data.shape) for num, poly in zip(nums, impoly): p = Path(poly) pixels_region = p.contains_points(pixels) # iterate through direction centres and find which one belongs to this region, then use the dir name to set the number # this is important as the vornoi tassellation has to have the same names of the original tassellation #for x,y,d in zip(x_fs, y_fs, directions.keys()): # if pixels_region.reshape(x2,y2)[int(np.rint(x)),int(np.rint(y))] == True: # num = int(d.split('_')[1]) # print num,x,y,d data_facet[pixels_region.reshape(x2, y2)] = num # put all values in each island equal to the closest region struct = generate_binary_structure(2, 2) data = binary_dilation(data, structure=struct, iterations=3).astype(data.dtype) # expand masks blobs, number_of_blobs = label(data.astype(int).squeeze(), structure=[[1, 1, 1], [1, 1, 1], [1, 1, 1]]) center_of_masses = center_of_mass(data, blobs, range(number_of_blobs + 1)) for blob in xrange(1, number_of_blobs + 1): # get closer facet facet_num = closest_node(center_of_masses[blob], np.array([y_fs, x_fs]).T) # put all pixel of that mask to that facet value data_facet[blobs == blob] = nums[facet_num] # save fits mask pyfits.writeto(out_mask, data_facet, hdr, overwrite=True) # save regions all_s = [] for i, poly in enumerate(impoly): ra, dec = w.all_pix2world(poly[:, 0], poly[:, 1], 0, ra_dec_order=True) coords = np.array([ra, dec]).T.flatten() s = Shape('Polygon', None) s.coord_format = 'fk5' s.coord_list = coords # ra, dec, radius s.coord_format = 'fk5' s.attr = ([], { 'width': '2', 'point': 'cross', 'font': '"helvetica 16 normal roman"' }) s.comment = 'color=red' all_s.append(s) regions = pyregion.ShapeList([s]) regionfile = outdir_reg + '/' + directions.keys()[ idx_for_facet[i]] + '.reg' regions.write(regionfile) # add names for all.reg for d_name, d_coord in directions.iteritems(): s = Shape('circle', None) s.coord_format = 'fk5' s.coord_list = [d_coord[0].degree, d_coord[1].degree, 0.01] # ra, dec, radius s.coord_format = 'fk5' s.attr = ([], { 'width': '1', 'point': 'cross', 'font': '"helvetica 16 normal roman"' }) s.comment = 'color=white text="%s"' % d_name all_s.append(s) regions = pyregion.ShapeList(all_s) regionfile = outdir_reg + '/all.reg' regions.write(regionfile) logger.debug( 'There are %i regions within the PB and %i outside (no facet).' % (len(idx_for_facet), len(directions) - len(idx_for_facet))) # plot tesselization if png is not None: import matplotlib.pyplot as pl pl.figure(figsize=(8, 8)) ax1 = pl.gca() voronoi_plot_2d(vor, ax1, show_vertices=True, line_colors='black', line_width=2, point_size=4) for i, d in enumerate(directions): ax1.text(x_fs[i], y_fs[i], d, fontsize=15) if not beam_reg is None: c1 = pl.Circle((x_c, y_c), beamradius_pix, color='g', fill=False) ax1.add_artist(c1) ax1.plot([x1, x1, x2, x2, x1], [y1, y2, y2, y1, y1]) ax1.set_xlabel('RA (pixel)') ax1.set_ylabel('Dec (pixel)') ax1.set_xlim(x1, x2) ax1.set_ylim(y1, y2) logger.debug('Save plot: %s' % png) pl.savefig(png)
def make_finder_chart_from_image_and_catalog( image, catalog, save_prefix, alma_kwargs={ 'public': False, 'science': False }, bands=(3, 4, 5, 6, 7, 8, 9), private_band_colors=('maroon', 'red', 'orange', 'coral', 'brown', 'yellow', 'mediumorchid'), public_band_colors=('blue', 'cyan', 'green', 'turquoise', 'teal', 'darkslategrey', 'chartreuse'), integration_time_contour_levels=np.logspace(0, 5, base=2, num=6), save_masks=False, use_saved_masks=False, ): """ Create a "finder chart" showing where ALMA has pointed in various bands, including different color coding for public/private data and each band. Contours are set at various integration times. Parameters ---------- image : fits.PrimaryHDU or fits.ImageHDU object The image to overlay onto catalog : astropy.Table object The catalog of ALMA observations save_prefix : str The prefix for the output files. Both .reg and .png files will be written. The .reg files will have the band numbers and public/private appended, while the .png file will be named prefix_almafinderchart.png alma_kwargs : dict Keywords to pass to the ALMA archive when querying. private_band_colors / public_band_colors : tuple A tuple or list of colors to be associated with private/public observations in the various bands integration_time_contour_levels : list or np.array The levels at which to draw contours in units of seconds. Default is log-spaced (2^n) seconds: [ 1., 2., 4., 8., 16., 32.]) """ import aplpy import pyregion from pyregion.parser_helper import Shape primary_beam_radii = [ approximate_primary_beam_sizes(row['Frequency support']) for row in catalog ] all_bands = bands bands = used_bands = np.unique(catalog['Band']) log.info("The bands used include: {0}".format(used_bands)) band_colors_priv = dict(zip(all_bands, private_band_colors)) band_colors_pub = dict(zip(all_bands, public_band_colors)) log.info("Color map private: {0}".format(band_colors_priv)) log.info("Color map public: {0}".format(band_colors_pub)) if use_saved_masks: hit_mask_public = {} hit_mask_private = {} for band in bands: pubfile = '{0}_band{1}_public.fits'.format(save_prefix, band) if os.path.exists(pubfile): hit_mask_public[band] = fits.getdata(pubfile) privfile = '{0}_band{1}_private.fits'.format(save_prefix, band) if os.path.exists(privfile): hit_mask_private[band] = fits.getdata(privfile) else: today = np.datetime64('today') private_circle_parameters = { band: [(row['RA'], row['Dec'], np.mean(rad).to(u.deg).value) for row, rad in zip(catalog, primary_beam_radii) if not row['Release date'] or (np.datetime64( row['Release date']) > today and row['Band'] == band)] for band in bands } public_circle_parameters = { band: [(row['RA'], row['Dec'], np.mean(rad).to(u.deg).value) for row, rad in zip(catalog, primary_beam_radii) if row['Release date'] and (np.datetime64( row['Release date']) <= today and row['Band'] == band)] for band in bands } unique_private_circle_parameters = { band: np.array(list(set(private_circle_parameters[band]))) for band in bands } unique_public_circle_parameters = { band: np.array(list(set(public_circle_parameters[band]))) for band in bands } release_dates = np.array(catalog['Release date'], dtype=np.datetime64) for band in bands: log.info("BAND {0}".format(band)) privrows = sum((catalog['Band'] == band) & (release_dates > today)) pubrows = sum((catalog['Band'] == band) & (release_dates <= today)) log.info("PUBLIC: Number of rows: {0}. Unique pointings: " "{1}".format(pubrows, len(unique_public_circle_parameters[band]))) log.info("PRIVATE: Number of rows: {0}. Unique pointings: " "{1}".format(privrows, len(unique_private_circle_parameters[band]))) prv_regions = { band: pyregion.ShapeList([ Shape('circle', [x, y, r]) for x, y, r in private_circle_parameters[band] ]) for band in bands } pub_regions = { band: pyregion.ShapeList([ Shape('circle', [x, y, r]) for x, y, r in public_circle_parameters[band] ]) for band in bands } for band in bands: circle_pars = np.vstack([ x for x in (private_circle_parameters[band], public_circle_parameters[band]) if any(x) ]) for r, (x, y, c) in zip(prv_regions[band] + pub_regions[band], circle_pars): r.coord_format = 'fk5' r.coord_list = [x, y, c] r.attr = ([], { 'color': 'green', 'dash': '0 ', 'dashlist': '8 3', 'delete': '1 ', 'edit': '1 ', 'fixed': '0 ', 'font': '"helvetica 10 normal roman"', 'highlite': '1 ', 'include': '1 ', 'move': '1 ', 'select': '1', 'source': '1', 'text': '', 'width': '1 ' }) if prv_regions[band]: prv_regions[band].write('{0}_band{1}_private.reg'.format( save_prefix, band)) if pub_regions[band]: pub_regions[band].write('{0}_band{1}_public.reg'.format( save_prefix, band)) prv_mask = { band: fits.PrimaryHDU(prv_regions[band].get_mask(image).astype('int'), header=image.header) for band in bands if prv_regions[band] } pub_mask = { band: fits.PrimaryHDU(pub_regions[band].get_mask(image).astype('int'), header=image.header) for band in bands if pub_regions[band] } hit_mask_public = { band: np.zeros_like(image.data) for band in pub_mask } hit_mask_private = { band: np.zeros_like(image.data) for band in prv_mask } mywcs = wcs.WCS(image.header) for band in bands: log.debug('Band: {0}'.format(band)) for row, rad in ProgressBar(list(zip(catalog, primary_beam_radii))): shape = Shape( 'circle', (row['RA'], row['Dec'], np.mean(rad).to(u.deg).value)) shape.coord_format = 'fk5' shape.coord_list = (row['RA'], row['Dec'], np.mean(rad).to(u.deg).value) shape.attr = ([], { 'color': 'green', 'dash': '0 ', 'dashlist': '8 3 ', 'delete': '1 ', 'edit': '1 ', 'fixed': '0 ', 'font': '"helvetica 10 normal roman"', 'highlite': '1 ', 'include': '1 ', 'move': '1 ', 'select': '1 ', 'source': '1', 'text': '', 'width': '1 ' }) log.debug('{1} {2}: {0}'.format(shape, row['Release date'], row['Band'])) if not row['Release date']: reldate = False else: reldate = np.datetime64(row['Release date']) if (((not reldate) or (reldate > today)) and (row['Band'] == band) and (band in prv_mask)): # private: release_date = 'sometime' says when it will be released (xlo, xhi, ylo, yhi), mask = pyregion_subset(shape, hit_mask_private[band], mywcs) log.debug("{0},{1},{2},{3}: {4}".format( xlo, xhi, ylo, yhi, mask.sum())) hit_mask_private[band][ ylo:yhi, xlo:xhi] += row['Integration'] * mask elif (reldate and (reldate <= today) and (row['Band'] == band) and (band in pub_mask)): # public: release_date = '' should mean already released (xlo, xhi, ylo, yhi), mask = pyregion_subset(shape, hit_mask_public[band], mywcs) log.debug("{0},{1},{2},{3}: {4}".format( xlo, xhi, ylo, yhi, mask.sum())) hit_mask_public[band][ylo:yhi, xlo:xhi] += row['Integration'] * mask if save_masks: for band in bands: if band in hit_mask_public: hdu = fits.PrimaryHDU(data=hit_mask_public[band], header=image.header) hdu.writeto('{0}_band{1}_public.fits'.format( save_prefix, band), clobber=True) if band in hit_mask_private: hdu = fits.PrimaryHDU(data=hit_mask_private[band], header=image.header) hdu.writeto('{0}_band{1}_private.fits'.format( save_prefix, band), clobber=True) fig = aplpy.FITSFigure(fits.HDUList(image), convention='calabretta') fig.show_grayscale(stretch='arcsinh') for band in bands: if band in hit_mask_public: fig.show_contour(fits.PrimaryHDU(data=hit_mask_public[band], header=image.header), levels=integration_time_contour_levels, colors=[band_colors_pub[int(band)]] * len(integration_time_contour_levels), convention='calabretta') if band in hit_mask_private: fig.show_contour(fits.PrimaryHDU(data=hit_mask_private[band], header=image.header), levels=integration_time_contour_levels, colors=[band_colors_priv[int(band)]] * len(integration_time_contour_levels), convention='calabretta') fig.save('{0}_almafinderchart.png'.format(save_prefix)) return image, catalog, hit_mask_public, hit_mask_private
def writeRegionFile(outname, catalog, matches, color='blue', width=2, fonttype='helvetica', fontsize=10, fontweight='normal', fontfamily='roman'): ''' Writes the region file. Keywords: outname -- A string containing the name of the output file. gParam -- A dictionary of global setup parameters. catalog -- A dictionary containing the different region. matches -- A list containing the indices matching the search keyword(s). ''' fontpars = dict(fontweight=fontweight, fonttype=fonttype, fontfamily=fontfamily, fontsize=fontsize) shapeList = [] for row in matches: name = row['name'] stype = row['stype'] coords = row['coord'] epoch = row['epoch'] shape = row['shape'] sunit = row['sunit'] text = row['text'] # # do some coordinate system conversion, if necessary if 'equatorial' in row['ctype']: if epoch == 1950: frame = coord.FK4 pos = coord.SkyCoord(coords, frame=frame, unit=(u.hourangle, u.deg)) pos_fk5 = pos.fk5 elif epoch == 2000: frame = coord.FK5 pos_fk5 = coord.SkyCoord(coords, frame=frame, unit=(u.hourangle, u.deg)) else: print("Cannot read epoch and/or ctype. Skipping index %i" % index) continue if 'ellipse' in stype: shape_list = shape.split(',') rmaj = u.Quantity(float(shape_list[0].strip()), sunit) rmin = u.Quantity(float(shape_list[1].strip()), sunit) rpa = shape_list[2].strip() shape = Shape(stype, (pyregion.region_numbers.SimpleNumber(pos_fk5.ra.deg), pyregion.region_numbers.SimpleNumber(pos_fk5.dec.deg), pyregion.region_numbers.AngularDistance('{0}"'.format(rmaj.to(u.arcsec).value)), pyregion.region_numbers.AngularDistance('{0}"'.format(rmin.to(u.arcsec).value)), pyregion.region_numbers.AngularDistance(rpa),)) shape.coord_format = 'fk5' shape.attr = ([],{}) shape.attr[1].update({'color': color, 'text': "{0}{2}{1}".format(name,text, ", " if text else ""), 'width': str(width), 'font': "{fonttype} {fontsize} {fontweight} {fontfamily}".format(**fontpars), }) shape.coord_list = [pos_fk5.ra.deg, pos_fk5.dec.deg, rmaj.to(u.deg).value, rmin.to(u.deg).value, float(rpa)] shape.comment = "text={{{text}}}".format(**shape.attr[1]) print(shape, shape.attr) shapeList.append(shape) SL = pyregion.ShapeList(shapeList) SL.write(outname) return SL
fig = aplpy.FITSFigure(m83_images[0]) fig.show_grayscale(stretch='arcsinh', vmid=0.1) fig.show_circles(unique_public_circle_parameters[:, 0], unique_public_circle_parameters[:, 1], unique_public_circle_parameters[:, 2], color='b', alpha=0.2) # Use pyregion to write the observed regions to disk. Pyregion has a very # awkward API; there is (in principle) work in progress to improve that # situation but for now one must do all this extra work. import pyregion from pyregion.parser_helper import Shape prv_regions = pyregion.ShapeList( [Shape('circle', [x, y, r]) for x, y, r in private_circle_parameters]) pub_regions = pyregion.ShapeList( [Shape('circle', [x, y, r]) for x, y, r in public_circle_parameters]) for r, (x, y, c) in zip( prv_regions + pub_regions, np.vstack([private_circle_parameters, public_circle_parameters])): r.coord_format = 'fk5' r.coord_list = [x, y, c] r.attr = ([], { 'color': 'green', 'dash': '0 ', 'dashlist': '8 3 ', 'delete': '1 ', 'edit': '1 ', 'fixed': '0 ', 'font': '"helvetica 10 normal roman"',
def make_voronoi_reg(directions, fitsfile, outdir='regions/', beam_reg='', png=None): """ Take a list of coordinates and an image and voronoi tesselate the sky. It saves ds9 regions of the facets and and return facet sizes directions : dict with {'dir0':[ra,dec], 'dir1':[ra,dec]...} firsfile : model fits file to tassellate (used for coordinates) outdir : dir where to save regions beam_reg : a ds9 region showing the the primary beam, exclude directions outside it """ logger.debug("Image used for tasselation reference: " + fitsfile) fits = pyfits.open(fitsfile) hdr, data = flatten(fits) w = pywcs.WCS(hdr) pixsize = np.abs(hdr['CDELT1']) # Add facet size column ras = np.array([directions[d][0].degree for d in directions]) decs = np.array([directions[d][1].degree for d in directions]) x, y = w.all_world2pix(ras, decs, 0, ra_dec_order=True) x_c = data.shape[0] / 2. y_c = data.shape[1] / 2. if beam_reg == '': # no beam, use all directions for facets idx_for_facet = range(len(directions)) else: r = pyregion.open(beam_reg) beam_mask = r.get_mask(header=hdr, shape=data.shape) beamradius_pix = r[0].coord_list[2] / pixsize idx_for_facet = [] for i, dd in enumerate(t): if beam_mask[t['x'][i], t['y'][i]] == True: idx_for_facet.append(i) # convert to pixel space (voronoi must be in eucledian space) x1 = 0 y1 = 0 x2 = data.shape[0] y2 = data.shape[1] # do tasselization vor = Voronoi(np.array((x[idx_for_facet], y[idx_for_facet])).transpose()) box = np.array([[x1, y1], [x2, y2]]) impoly = voronoi_finite_polygons_2d_box(vor, box) # save regions all_s = [] for i, poly in enumerate(impoly): ra, dec = w.all_pix2world(poly[:, 0], poly[:, 1], 0, ra_dec_order=True) coords = np.array([ra, dec]).T.flatten() s = Shape('Polygon', None) s.coord_format = 'fk5' s.coord_list = coords # ra, dec, radius s.coord_format = 'fk5' s.attr = ([], { 'width': '2', 'point': 'cross', 'font': '"helvetica 16 normal roman"' }) s.comment = 'color=red' all_s.append(s) regions = pyregion.ShapeList([s]) regionfile = outdir + directions.keys()[idx_for_facet[i]] + '.reg' regions.write(regionfile) #if beam_reg != '': npix = size_from_reg(fitsfile, [regionfile, beam_reg], [ras[idx_for_facet[i]], decs[idx_for_facet[i]]]) #else: npix = size_from_reg(fitsfile, [regionfile], [ras[idx_for_facet[i]], decs[idx_for_facet[i]]]) # add names for all.reg for d_name, d_coord in directions.iteritems(): s = Shape('circle', None) s.coord_format = 'fk5' s.coord_list = [d_coord[0].degree, d_coord[1].degree, 0.01] # ra, dec, radius s.coord_format = 'fk5' s.attr = ([], { 'width': '1', 'point': 'cross', 'font': '"helvetica 16 normal roman"' }) s.comment = 'color=white text="%s"' % d_name all_s.append(s) regions = pyregion.ShapeList(all_s) regionfile = outdir + 'all.reg' regions.write(regionfile) logger.debug( 'There are %i calibrator within the PB and %i outside (no facet).' % (len(idx_for_facet), len(directions) - len(idx_for_facet))) # plot tesselization if png is not None: import matplotlib.pyplot as pl pl.figure(figsize=(8, 8)) ax1 = pl.gca() ax1.plot(x, y, '*', color='red') for i, d in enumerate(directions): ax1.text(x[i], y[i], d, fontsize=15) if beam_reg != '': c1 = pl.Circle((x_c, y_c), beamradius_pix, color='g', fill=False) ax1.add_artist(c1) ax1.plot([x1, x1, x2, x2, x1], [y1, y2, y2, y1, y1]) for p in impoly: pp = p.transpose() ax1.plot(pp[0], pp[1]) ax1.set_xlabel('RA (pixel)') ax1.set_ylabel('Dec (pixel)') logger.debug('Save plot: %s' % png) pl.savefig(png)
xhi=xhi, yhi=yhi)) subwcs = mywcs[ylo:yhi, xlo:xhi] subhdr = subwcs.sub([wcs.WCSSUB_CELESTIAL]).to_header() subdata = data[ylo:yhi, xlo:xhi] mask = shapelist.get_mask(header=subhdr, shape=subdata.shape) log.debug("Shapes: data={0}, subdata={2}, mask={1}".format(data.shape, mask.shape, subdata.shape)) return (xlo, xhi, ylo, yhi), mask for row, rad in zip(m83, primary_beam_radii): shape = Shape('circle', (row['RA'], row['Dec'], np.mean(rad).to(u.deg).value)) shape.coord_format = 'fk5' shape.coord_list = (row['RA'], row['Dec'], np.mean(rad).to(u.deg).value) shape.attr = ([], {'color': 'green', 'dash': '0 ', 'dashlist': '8 3 ', 'delete': '1 ', 'edit': '1 ', 'fixed': '0 ', 'font': '"helvetica 10 normal roman"', 'highlite': '1 ', 'include': '1 ', 'move': '1 ', 'select': '1 ', 'source': '1', 'text': '', 'width': '1 '}) if row['Release date']==b'' and row['Band']==3: (xlo, xhi, ylo, yhi), mask = pyregion_subset(shape, hit_mask_band3_private, mywcs) hit_mask_band3_private[ylo:yhi, xlo:xhi] += row['Integration']*mask elif row['Release date'] and row['Band']==3: (xlo, xhi, ylo, yhi), mask = pyregion_subset(shape, hit_mask_band3_public, mywcs) hit_mask_band3_public[ylo:yhi, xlo:xhi] += row['Integration']*mask elif row['Release date'] and row['Band']==6: (xlo, xhi, ylo, yhi), mask = pyregion_subset(shape, hit_mask_band6_public, mywcs)
def make_finder_chart(target, radius, save_prefix, service=SkyView.get_images, service_kwargs={'survey': ['2MASS-K'], 'pixels': 500}, alma_kwargs={'public': False, 'science': False}, private_band_colors=('red', 'darkred', 'orange', 'brown', 'maroon'), public_band_colors=('blue', 'cyan', 'green', 'turquoise', 'teal'), integration_time_contour_levels=np.logspace(0, 5, base=2, num=6), ): """ Create a "finder chart" showing where ALMA has pointed in various bands, including different color coding for public/private data and each band. Contours are set at various integration times. Parameters ---------- target : `astropy.coordinates` or str A legitimate target name radius : `astropy.units.Quantity` A degree-equivalent radius save_prefix : str The prefix for the output files. Both .reg and .png files will be written. The .reg files will have the band numbers and public/private appended, while the .png file will be named prefix_almafinderchart.png service : function The `get_images` function of an astroquery service, e.g. SkyView. service_kwargs : dict The keyword arguments to pass to the specified service. For example, for SkyView, you can give it the survey ID (e.g., 2MASS-K) and the number of pixels in the resulting image. See the documentation for the individual services for more details. alma_kwargs : dict Keywords to pass to the ALMA archive when querying. private_band_colors / public_band_colors : tuple A tuple or list of colors to be associated with private/public observations in the various bands integration_time_contour_levels : list or np.array The levels at which to draw contours in units of seconds. Default is log-spaced (2^n) seconds: [ 1., 2., 4., 8., 16., 32.]) """ import aplpy import pyregion from pyregion.parser_helper import Shape log.info("Querying {0} for images".format(service)) images = service(target, radius=radius, **service_kwargs) log.info("Querying ALMA around {0}".format(target)) catalog = Alma.query_region(coordinate=target, radius=radius, **alma_kwargs) primary_beam_radii = [ approximate_primary_beam_sizes(row['Frequency support']) for row in catalog] bands = np.unique(catalog['Band']) log.info("The bands used include: {0}".format(bands)) band_colors_priv = dict(zip(bands, private_band_colors)) band_colors_pub = dict(zip(bands, public_band_colors)) private_circle_parameters = { band: [(row['RA'], row['Dec'], np.mean(rad).to(u.deg).value) for row, rad in zip(catalog, primary_beam_radii) if row['Release date'] != '' and row['Band'] == band] for band in bands} public_circle_parameters = { band: [(row['RA'], row['Dec'], np.mean(rad).to(u.deg).value) for row, rad in zip(catalog, primary_beam_radii) if row['Release date'] == '' and row['Band'] == band] for band in bands} unique_private_circle_parameters = { band: np.array(list(set(private_circle_parameters[band]))) for band in bands} unique_public_circle_parameters = { band: np.array(list(set(public_circle_parameters[band]))) for band in bands} for band in bands: log.info("BAND {0}".format(band)) privrows = sum((catalog['Band'] == band) & (catalog['Release date'] != '')) pubrows = sum((catalog['Band'] == band) & (catalog['Release date'] == '')) log.info("PUBLIC: Number of rows: {0}. Unique pointings: " "{1}".format(pubrows, len(unique_public_circle_parameters[band]))) log.info("PRIVATE: Number of rows: {0}. Unique pointings: " "{1}".format(privrows, len(unique_private_circle_parameters[band]))) prv_regions = { band: pyregion.ShapeList([Shape('circle', [x, y, r]) for x, y, r in private_circle_parameters[band]]) for band in bands} pub_regions = { band: pyregion.ShapeList([Shape('circle', [x, y, r]) for x, y, r in public_circle_parameters[band]]) for band in bands} for band in bands: circle_pars = np.vstack( [x for x in (private_circle_parameters[band], public_circle_parameters[band]) if any(x)]) for r, (x, y, c) in zip(prv_regions[band] + pub_regions[band], circle_pars): r.coord_format = 'fk5' r.coord_list = [x, y, c] r.attr = ([], {'color': 'green', 'dash': '0 ', 'dashlist': '8 3', 'delete': '1 ', 'edit': '1 ', 'fixed': '0 ', 'font': '"helvetica 10 normal roman"', 'highlite': '1 ', 'include': '1 ', 'move': '1 ', 'select': '1', 'source': '1', 'text': '', 'width': '1 '}) if prv_regions[band]: prv_regions[band].write( '{0}_band{1}_private.reg'.format(save_prefix, band)) if pub_regions[band]: pub_regions[band].write( '{0}_band{1}_public.reg'.format(save_prefix, band)) prv_mask = { band: fits.PrimaryHDU( prv_regions[band].get_mask(images[0][0]).astype('int'), header=images[0][0].header) for band in bands if prv_regions[band]} pub_mask = { band: fits.PrimaryHDU( pub_regions[band].get_mask(images[0][0]).astype('int'), header=images[0][0].header) for band in bands if pub_regions[band]} hit_mask_public = {band: np.zeros_like(images[0][0].data) for band in pub_mask} hit_mask_private = {band: np.zeros_like(images[0][0].data) for band in prv_mask} mywcs = wcs.WCS(images[0][0].header) for band in bands: log.debug('Band: {0}'.format(band)) for row, rad in zip(catalog, primary_beam_radii): shape = Shape('circle', (row['RA'], row['Dec'], np.mean(rad).to(u.deg).value)) shape.coord_format = 'fk5' shape.coord_list = (row['RA'], row['Dec'], np.mean(rad).to(u.deg).value) shape.attr = ([], {'color': 'green', 'dash': '0 ', 'dashlist': '8 3 ', 'delete': '1 ', 'edit': '1 ', 'fixed': '0 ', 'font': '"helvetica 10 normal roman"', 'highlite': '1 ', 'include': '1 ', 'move': '1 ', 'select': '1 ', 'source': '1', 'text': '', 'width': '1 '}) log.debug('{1} {2}: {0}' .format(shape, row['Release date'], row['Band'])) if (row['Release date'] != '' and row['Band'] == band and band in prv_mask): (xlo, xhi, ylo, yhi), mask = pyregion_subset( shape, hit_mask_private[band], mywcs) log.debug("{0},{1},{2},{3}: {4}" .format(xlo, xhi, ylo, yhi, mask.sum())) hit_mask_private[band][ylo:yhi, xlo:xhi] += row['Integration']*mask if (row['Release date'] == '' and row['Band'] == band and band in pub_mask): (xlo, xhi, ylo, yhi), mask = pyregion_subset( shape, hit_mask_public[band], mywcs) log.debug("{0},{1},{2},{3}: {4}" .format(xlo, xhi, ylo, yhi, mask.sum())) hit_mask_public[band][ylo:yhi, xlo:xhi] += row['Integration']*mask fig = aplpy.FITSFigure(images[0]) fig.show_grayscale(stretch='arcsinh') for band in bands: if band in pub_mask: fig.show_contour(fits.PrimaryHDU(data=hit_mask_public[band], header=images[0][0].header), levels=integration_time_contour_levels, colors=[band_colors_pub[band]] * 6) if band in prv_mask: fig.show_contour(fits.PrimaryHDU(data=hit_mask_private[band], header=images[0][0].header), levels=integration_time_contour_levels, colors=[band_colors_priv[band]] * 6) fig.save('{0}_almafinderchart.png'.format(save_prefix)) return images, catalog, hit_mask_public, hit_mask_private
dist = lib_util.distanceOnSphere(phasecentre[0], phasecentre[1], peelsou['RA'], peelsou['DEC']) if dist < 0.5: continue logger.info('Peeling %s (%.1f Jy)' % (name, peelsou['Total_flux'])) with w.if_todo('peel-%s' % name): lib_util.check_rm('peel-%s' % name) os.system('mkdir peel-%s' % name) # make a region from pyregion.parser_helper import Shape import pyregion peel_region_file = 'peel-' + name + '/' + name + '.reg' sh = Shape('circle', None) sh.coord_format = 'fk5' sh.coord_list = [peelsou['RA'], peelsou['DEC'], 0.1] # ra, dec, diam sh.coord_format = 'fk5' sh.attr = ([], { 'width': '2', 'point': 'cross', 'font': '"helvetica 16 normal roman"' }) sh.comment = 'color=red text="%s"' % (name + '.reg') regions = pyregion.ShapeList([sh]) lib_util.check_rm(peel_region_file) regions.write(peel_region_file) # copy and blank models
def make_voronoi_reg(directions, fitsfile, outdir_reg='regions', out_mask='facet.fits', png=None): """ Take a list of coordinates and an image and voronoi tesselate the sky. It saves ds9 regions + fits mask of the facets directions : array of Direction objects firsfile : mask fits file to tassellate (used for coordinates and as template for the out_mask) outdir_reg : dir where to save regions out_mask : output mask with different numbers in each facet png : output png file that shows the tassellation """ def closest_node(node, nodes): """ Return closest values to node from nodes """ nodes = np.asarray(nodes) dist_2 = np.sum((nodes - node)**2, axis=1) return np.argmin(dist_2) logger.debug("Image used for tasselation reference: " + fitsfile) fits = pyfits.open(fitsfile) hdr, data = lib_img.flatten(fits) w = pywcs.WCS(hdr) pixsize = np.abs(hdr['CDELT1']) # Get facets central pixels ras = np.array([d.position_cal[0] for d in directions]) decs = np.array([d.position_cal[1] for d in directions]) x_fs, y_fs = w.all_world2pix(ras, decs, 0, ra_dec_order=True) # keep trak of numbers in the direction names to name correctly patches in the fits files # in this way Isl_patch_12 will have "12" into the fits for that patch. nums = [d.isl_num for d in directions] x_c = data.shape[0] / 2. y_c = data.shape[1] / 2. # Check if dir is in img, otherwise drop idx_for_facet = [] for i, direction in enumerate(directions): x, y = w.all_world2pix(ras[i], decs[i], 0, ra_dec_order=True) if x < 0 or x > data.shape[0] or y < 0 or y > data.shape[1]: logger.info( 'Direction %s is outside the primary beam and will not have a facet (it will still be a calibrator).' % direction.name) else: idx_for_facet.append(i) # convert to pixel space (voronoi must be in eucledian space) x1 = 0 y1 = 0 x2 = data.shape[1] # note that y is before x in fits.data y2 = data.shape[0] # do tasselization vor = Voronoi( np.array((x_fs[idx_for_facet], y_fs[idx_for_facet])).transpose()) box = np.array([[x1, y1], [x2, y2]]) impoly = voronoi_finite_polygons_2d_box(vor, box) # create fits mask (each region one number) x, y = np.meshgrid(np.arange(x2), np.arange(y2)) # make a canvas with coordinates x, y = x.flatten(), y.flatten() pixels = np.vstack((x, y)).T data_facet = np.zeros(shape=data.shape) for num, poly in zip(nums, impoly): p = Path(poly) pixels_region = p.contains_points(pixels) data_facet[pixels_region.reshape(y2, x2)] = num # put all values in each island equal to the closest region struct = generate_binary_structure(2, 2) data = binary_dilation(data, structure=struct, iterations=3).astype(data.dtype) # expand masks blobs, number_of_blobs = label(data.astype(int).squeeze(), structure=[[1, 1, 1], [1, 1, 1], [1, 1, 1]]) center_of_masses = center_of_mass(data, blobs, list(range(number_of_blobs + 1))) for blob in range(1, number_of_blobs + 1): # get closer facet facet_num = closest_node( center_of_masses[blob], np.array([y_fs[idx_for_facet], x_fs[idx_for_facet]]).T) # put all pixel of that mask to that facet value data_facet[blobs == blob] = nums[facet_num] # save fits mask pyfits.writeto(out_mask, data_facet, hdr, overwrite=True) # save regions if not os.path.isdir(outdir_reg): os.makedirs(outdir_reg) all_s = [] for i, poly in enumerate(impoly): ra, dec = w.all_pix2world(poly[:, 0], poly[:, 1], 0, ra_dec_order=True) coords = np.array([ra, dec]).T.flatten() s = Shape('Polygon', None) s.coord_format = 'fk5' s.coord_list = coords # ra, dec, radius s.coord_format = 'fk5' s.attr = ([], { 'width': '2', 'point': 'cross', 'font': '"helvetica 16 normal roman"' }) s.comment = 'color=red' all_s.append(s) regions = pyregion.ShapeList([s]) regionfile = outdir_reg + '/' + directions[ idx_for_facet[i]].name + '.reg' regions.write(regionfile) # add names for all.reg for d in directions: s = Shape('circle', None) s.coord_format = 'fk5' s.coord_list = [d.position_cal[0], d.position_cal[1], 0.01] # ra, dec, radius s.coord_format = 'fk5' s.attr = ([], { 'width': '1', 'point': 'cross', 'font': '"helvetica 16 normal roman"' }) s.comment = 'color=white text="%s"' % d.name all_s.append(s) regions = pyregion.ShapeList(all_s) regionfile = outdir_reg + '/all.reg' regions.write(regionfile) logger.debug( 'There are %i regions within the PB and %i outside (no facet).' % (len(idx_for_facet), len(directions) - len(idx_for_facet))) # plot tesselization if png is not None: import matplotlib.pyplot as pl pl.figure(figsize=(8, 8)) ax1 = pl.gca() voronoi_plot_2d(vor, ax1, show_vertices=True, line_colors='black', line_width=2, point_size=4) for i, d in enumerate(directions): ax1.text(x_fs[i], y_fs[i], d.name, fontsize=15) ax1.plot([x1, x1, x2, x2, x1], [y1, y2, y2, y1, y1]) ax1.set_xlabel('RA (pixel)') ax1.set_ylabel('Dec (pixel)') ax1.set_xlim(x1, x2) ax1.set_ylim(y1, y2) logger.debug('Save plot: %s' % png) pl.savefig(png)
def make_finder_chart(target, radius, save_prefix, service=SkyView.get_images, service_kwargs={'survey':['2MASS-K'], 'pixels':500}, alma_kwargs={'public':False, 'science':False}, private_band_colors=('red','darkred','orange','brown','maroon'), public_band_colors=('blue','cyan','green','turquoise','teal'), integration_time_contour_levels=np.logspace(0,5,base=2, num=6), ): """ Create a "finder chart" showing where ALMA has pointed in various bands, including different color coding for public/private data and each band. Contours are set at various integration times. Parameters ---------- target : `astropy.coordinates` or str A legitimate target name radius : `astropy.units.Quantity` A degree-equivalent radius save_prefix : str The prefix for the output files. Both .reg and .png files will be written. The .reg files will have the band numbers and public/private appended, while the .png file will be named prefix_almafinderchart.png service : function The `get_images` function of an astroquery service, e.g. SkyView. service_kwargs : dict The keyword arguments to pass to the specified service. For example, for SkyView, you can give it the survey ID (e.g., 2MASS-K) and the number of pixels in the resulting image. See the documentation for the individual services for more details. alma_kwargs : dict Keywords to pass to the ALMA archive when querying. private_band_colors / public_band_colors : tuple A tuple or list of colors to be associated with private/public observations in the various bands integration_time_contour_levels : list or np.array The levels at which to draw contours in units of seconds. Default is log-spaced (2^n) seconds: [ 1., 2., 4., 8., 16., 32.]) """ import aplpy import pyregion from pyregion.parser_helper import Shape log.info("Querying {0} for images".format(service)) images = service(target, radius=radius, **service_kwargs) log.info("Querying ALMA around {0}".format(target)) catalog = Alma.query_region(coordinate=target, radius=radius, **alma_kwargs) primary_beam_radii = [approximate_primary_beam_sizes(row['Frequency support']) for row in catalog] bands = np.unique(catalog['Band']) log.info("The bands used include: {0}".format(bands)) band_colors_priv = dict(zip(bands, private_band_colors)) band_colors_pub = dict(zip(bands, public_band_colors)) private_circle_parameters = {band: [(row['RA'],row['Dec'],np.mean(rad).to(u.deg).value) for row,rad in zip(catalog, primary_beam_radii) if row['Release date']!='' and row['Band']==band] for band in bands} public_circle_parameters = {band: [(row['RA'],row['Dec'],np.mean(rad).to(u.deg).value) for row,rad in zip(catalog, primary_beam_radii) if row['Release date']=='' and row['Band']==band] for band in bands} unique_private_circle_parameters = {band: np.array(list(set(private_circle_parameters[band]))) for band in bands} unique_public_circle_parameters = {band: np.array(list(set(public_circle_parameters[band]))) for band in bands} for band in bands: log.info( "BAND {0}".format(band) ) privrows = sum((catalog['Band']==band) & (catalog['Release date'] != '')) pubrows = sum((catalog['Band']==band) & (catalog['Release date'] == '')) log.info("PUBLIC: Number of rows: {0}. Unique pointings: " "{1}".format(pubrows, len(unique_public_circle_parameters[band]))) log.info( "PRIVATE: Number of rows: {0}. Unique pointings: " "{1}".format(privrows, len(unique_private_circle_parameters[band]))) prv_regions = {band: pyregion.ShapeList([Shape('circle',[x,y,r]) for x,y,r in private_circle_parameters[band]]) for band in bands} pub_regions = {band: pyregion.ShapeList([Shape('circle',[x,y,r]) for x,y,r in public_circle_parameters[band]]) for band in bands} for band in bands: circle_pars = np.vstack([x for x in (private_circle_parameters[band], public_circle_parameters[band]) if any(x)]) for r,(x,y,c) in zip(prv_regions[band]+pub_regions[band], circle_pars): r.coord_format = 'fk5' r.coord_list = [x,y,c] r.attr = ([], {'color': 'green', 'dash': '0 ', 'dashlist': '8 3', 'delete': '1 ', 'edit': '1 ', 'fixed': '0 ', 'font': '"helvetica 10 normal roman"', 'highlite': '1 ', 'include': '1 ', 'move': '1 ', 'select': '1', 'source': '1', 'text': '', 'width': '1 '}) if prv_regions[band]: prv_regions[band].write('{0}_band{1}_private.reg'.format(save_prefix, band)) if pub_regions[band]: pub_regions[band].write('{0}_band{1}_public.reg'.format(save_prefix, band)) prv_mask = {band: fits.PrimaryHDU(prv_regions[band].get_mask(images[0][0]).astype('int'), header=images[0][0].header) for band in bands if prv_regions[band]} pub_mask = {band: fits.PrimaryHDU(pub_regions[band].get_mask(images[0][0]).astype('int'), header=images[0][0].header) for band in bands if pub_regions[band]} hit_mask_public = {band: np.zeros_like(images[0][0].data) for band in pub_mask} hit_mask_private = {band: np.zeros_like(images[0][0].data) for band in prv_mask} mywcs = wcs.WCS(images[0][0].header) for band in bands: log.debug('Band: {0}'.format(band)) for row,rad in zip(catalog, primary_beam_radii): shape = Shape('circle', (row['RA'], row['Dec'],np.mean(rad).to(u.deg).value)) shape.coord_format = 'fk5' shape.coord_list = (row['RA'], row['Dec'],np.mean(rad).to(u.deg).value) shape.attr = ([], {'color': 'green', 'dash': '0 ', 'dashlist': '8 3 ', 'delete': '1 ', 'edit': '1 ', 'fixed': '0 ', 'font': '"helvetica 10 normal roman"', 'highlite': '1 ', 'include': '1 ', 'move': '1 ', 'select': '1 ', 'source': '1', 'text': '', 'width': '1 '}) log.debug('{1} {2}: {0}'.format(shape, row['Release date'], row['Band'])) if row['Release date']!='' and row['Band']==band and band in prv_mask: (xlo,xhi,ylo,yhi),mask = pyregion_subset(shape, hit_mask_private[band], mywcs) log.debug("{0},{1},{2},{3}: {4}".format(xlo,xhi,ylo,yhi,mask.sum())) hit_mask_private[band][ylo:yhi,xlo:xhi] += row['Integration']*mask if row['Release date']=='' and row['Band']==band and band in pub_mask: (xlo,xhi,ylo,yhi),mask = pyregion_subset(shape, hit_mask_public[band], mywcs) log.debug("{0},{1},{2},{3}: {4}".format(xlo,xhi,ylo,yhi,mask.sum())) hit_mask_public[band][ylo:yhi,xlo:xhi] += row['Integration']*mask fig = aplpy.FITSFigure(images[0]) fig.show_grayscale(stretch='arcsinh') for band in bands: if band in pub_mask: fig.show_contour(fits.PrimaryHDU(data=hit_mask_public[band], header=images[0][0].header), levels=integration_time_contour_levels, colors=[band_colors_pub[band]]*6) if band in prv_mask: fig.show_contour(fits.PrimaryHDU(data=hit_mask_private[band], header=images[0][0].header), levels=integration_time_contour_levels, colors=[band_colors_priv[band]]*6) fig.save('{0}_almafinderchart.png'.format(save_prefix)) return images, catalog, hit_mask_public, hit_mask_private
def make_finder_chart_from_image_and_catalog(image, catalog, save_prefix, alma_kwargs={'public': False, 'science': False}, bands=(3,4,5,6,7,8,9), private_band_colors=('maroon', 'red', 'orange', 'coral', 'brown', 'yellow', 'mediumorchid'), public_band_colors=('blue', 'cyan', 'green', 'turquoise', 'teal', 'darkslategrey', 'chartreuse'), integration_time_contour_levels=np.logspace(0, 5, base=2, num=6), save_masks=False, use_saved_masks=False, ): """ Create a "finder chart" showing where ALMA has pointed in various bands, including different color coding for public/private data and each band. Contours are set at various integration times. Parameters ---------- image : fits.PrimaryHDU or fits.ImageHDU object The image to overlay onto catalog : astropy.Table object The catalog of ALMA observations save_prefix : str The prefix for the output files. Both .reg and .png files will be written. The .reg files will have the band numbers and public/private appended, while the .png file will be named prefix_almafinderchart.png alma_kwargs : dict Keywords to pass to the ALMA archive when querying. private_band_colors / public_band_colors : tuple A tuple or list of colors to be associated with private/public observations in the various bands integration_time_contour_levels : list or np.array The levels at which to draw contours in units of seconds. Default is log-spaced (2^n) seconds: [ 1., 2., 4., 8., 16., 32.]) """ import aplpy import pyregion from pyregion.parser_helper import Shape primary_beam_radii = [ approximate_primary_beam_sizes(row['Frequency support']) for row in catalog] all_bands = bands bands = used_bands = np.unique(catalog['Band']) log.info("The bands used include: {0}".format(used_bands)) band_colors_priv = dict(zip(all_bands, private_band_colors)) band_colors_pub = dict(zip(all_bands, public_band_colors)) log.info("Color map private: {0}".format(band_colors_priv)) log.info("Color map public: {0}".format(band_colors_pub)) if use_saved_masks: hit_mask_public = {} hit_mask_private = {} for band in bands: pubfile = '{0}_band{1}_public.fits'.format(save_prefix, band) if os.path.exists(pubfile): hit_mask_public[band] = fits.getdata(pubfile) privfile = '{0}_band{1}_private.fits'.format(save_prefix, band) if os.path.exists(privfile): hit_mask_private[band] = fits.getdata(privfile) else: today = np.datetime64('today') private_circle_parameters = { band: [(row['RA'], row['Dec'], np.mean(rad).to(u.deg).value) for row, rad in zip(catalog, primary_beam_radii) if not row['Release date'] or (np.datetime64(row['Release date']) > today and row['Band'] == band)] for band in bands} public_circle_parameters = { band: [(row['RA'], row['Dec'], np.mean(rad).to(u.deg).value) for row, rad in zip(catalog, primary_beam_radii) if row['Release date'] and (np.datetime64(row['Release date']) <= today and row['Band'] == band)] for band in bands} unique_private_circle_parameters = { band: np.array(list(set(private_circle_parameters[band]))) for band in bands} unique_public_circle_parameters = { band: np.array(list(set(public_circle_parameters[band]))) for band in bands} release_dates = np.array(catalog['Release date'], dtype=np.datetime64) for band in bands: log.info("BAND {0}".format(band)) privrows = sum((catalog['Band'] == band) & (release_dates > today)) pubrows = sum((catalog['Band'] == band) & (release_dates <= today)) log.info("PUBLIC: Number of rows: {0}. Unique pointings: " "{1}".format(pubrows, len(unique_public_circle_parameters[band]))) log.info("PRIVATE: Number of rows: {0}. Unique pointings: " "{1}".format(privrows, len(unique_private_circle_parameters[band]))) prv_regions = { band: pyregion.ShapeList([Shape('circle', [x, y, r]) for x, y, r in private_circle_parameters[band]]) for band in bands} pub_regions = { band: pyregion.ShapeList([Shape('circle', [x, y, r]) for x, y, r in public_circle_parameters[band]]) for band in bands} for band in bands: circle_pars = np.vstack( [x for x in (private_circle_parameters[band], public_circle_parameters[band]) if any(x)]) for r, (x, y, c) in zip(prv_regions[band] + pub_regions[band], circle_pars): r.coord_format = 'fk5' r.coord_list = [x, y, c] r.attr = ([], {'color': 'green', 'dash': '0 ', 'dashlist': '8 3', 'delete': '1 ', 'edit': '1 ', 'fixed': '0 ', 'font': '"helvetica 10 normal roman"', 'highlite': '1 ', 'include': '1 ', 'move': '1 ', 'select': '1', 'source': '1', 'text': '', 'width': '1 '}) if prv_regions[band]: prv_regions[band].write( '{0}_band{1}_private.reg'.format(save_prefix, band)) if pub_regions[band]: pub_regions[band].write( '{0}_band{1}_public.reg'.format(save_prefix, band)) prv_mask = { band: fits.PrimaryHDU( prv_regions[band].get_mask(image).astype('int'), header=image.header) for band in bands if prv_regions[band]} pub_mask = { band: fits.PrimaryHDU( pub_regions[band].get_mask(image).astype('int'), header=image.header) for band in bands if pub_regions[band]} hit_mask_public = {band: np.zeros_like(image.data) for band in pub_mask} hit_mask_private = {band: np.zeros_like(image.data) for band in prv_mask} mywcs = wcs.WCS(image.header) for band in bands: log.debug('Band: {0}'.format(band)) for row, rad in ProgressBar(list(zip(catalog, primary_beam_radii))): shape = Shape('circle', (row['RA'], row['Dec'], np.mean(rad).to(u.deg).value)) shape.coord_format = 'fk5' shape.coord_list = (row['RA'], row['Dec'], np.mean(rad).to(u.deg).value) shape.attr = ([], {'color': 'green', 'dash': '0 ', 'dashlist': '8 3 ', 'delete': '1 ', 'edit': '1 ', 'fixed': '0 ', 'font': '"helvetica 10 normal roman"', 'highlite': '1 ', 'include': '1 ', 'move': '1 ', 'select': '1 ', 'source': '1', 'text': '', 'width': '1 '}) log.debug('{1} {2}: {0}' .format(shape, row['Release date'], row['Band'])) if not row['Release date']: reldate = False else: reldate = np.datetime64(row['Release date']) if (((not reldate) or (reldate > today)) and (row['Band'] == band) and (band in prv_mask) ): # private: release_date = 'sometime' says when it will be released (xlo, xhi, ylo, yhi), mask = pyregion_subset( shape, hit_mask_private[band], mywcs) log.debug("{0},{1},{2},{3}: {4}" .format(xlo, xhi, ylo, yhi, mask.sum())) hit_mask_private[band][ylo:yhi, xlo:xhi] += row['Integration']*mask elif (reldate and (reldate <= today) and (row['Band'] == band) and (band in pub_mask)): # public: release_date = '' should mean already released (xlo, xhi, ylo, yhi), mask = pyregion_subset( shape, hit_mask_public[band], mywcs) log.debug("{0},{1},{2},{3}: {4}" .format(xlo, xhi, ylo, yhi, mask.sum())) hit_mask_public[band][ylo:yhi, xlo:xhi] += row['Integration']*mask if save_masks: for band in bands: if band in hit_mask_public: hdu = fits.PrimaryHDU(data=hit_mask_public[band], header=image.header) hdu.writeto('{0}_band{1}_public.fits'.format(save_prefix, band), clobber=True) if band in hit_mask_private: hdu = fits.PrimaryHDU(data=hit_mask_private[band], header=image.header) hdu.writeto('{0}_band{1}_private.fits'.format(save_prefix, band), clobber=True) fig = aplpy.FITSFigure(fits.HDUList(image), convention='calabretta') fig.show_grayscale(stretch='arcsinh') for band in bands: if band in hit_mask_public: fig.show_contour(fits.PrimaryHDU(data=hit_mask_public[band], header=image.header), levels=integration_time_contour_levels, colors=[band_colors_pub[band]] * len(integration_time_contour_levels), convention='calabretta') if band in hit_mask_private: fig.show_contour(fits.PrimaryHDU(data=hit_mask_private[band], header=image.header), levels=integration_time_contour_levels, colors=[band_colors_priv[band]] * len(integration_time_contour_levels), convention='calabretta') fig.save('{0}_almafinderchart.png'.format(save_prefix)) return image, catalog, hit_mask_public, hit_mask_private