Esempio n. 1
0
    def subdivide_mask(self, n_shortside=3, n_longside=4, preview=False,
                       rotation_angle=None, padding=None, only_show=None,
                       save_plot=None):
        """
        Subdivide mask takes the image mask, draws a rectangle around the
        valid region of the mask, rotated to be as small as possible, and
        subdivides that rectangle.  The rotation angle of the box can be
        set manually and space can be trimmed or added from the sides of
        the box.

        Parameters
        ----------
        n_shortside : integer (optional)
            The number of cells along the short side of the rectangle.
            Default is 3.
            
        n_longside : integer (optional)
            The number of cells along the long side of the rectangle.
            Default is 4.
            
        preview : bool (optional)
            Should the mask show you what this set of parameters looks like
            but not store the results?  If preview == True, either a plot
            will be shown on your screen (no value given for save_plot) or
            saved to a file (save_plot specified).  The image mask object
            will not keep the division information.  If preview == False,
            which is the default, the values will be stored and a plot will
            only be made if save_plot is specified.
            
        rotation_angle : scalar (optional)
            Fixes the rotation angle that the mask's bounding box will be
            defined at with respect to the XY coordinate system of the
            mask.  By default, the routine will choose the integer angle in
            degrees which minimizes the area of the bounding box.
            
        padding : 4 element array-like (optional)
            How many pixels extra to allow in the rotated coordinates of
            the bounding box.  The order is [bottom, top, left, right].
            Positive padding corresponds to moving the edges outwards and
            leaving extra room.  Negative padding will move the bounding
            box inward and cut off part of the mask.
            
        only_show : integer (optional)
            If you only want to see the random points that fall into one of
            the cells in the subdivided mask, you set only_show to that
            cell number.  This will only matter if you have preview=True
            or have specified save_plot.
            
        save_plot : string (optional)
            Name with path from '/' of the file where you would like the
            plot of the subdivided mask saved.  If only_show is set to an
            integer that corresponds to a cell number, only the points in
            that cell will be shown.
        """
        
        #Start by putting down a bunch of randoms.
        ra, dec, __ = self.generate_random_sample(5.e4)
        x1, y1=self.ra_dec_to_xy(ra, dec)

        #Set the padding on each side
        if padding:
            try:
                pad_bottom, pad_top, pad_left, pad_right=padding
            except:
                #If we can't unpack the padding, either raise an
                #informative error (if we're doing the subdivision
                #permanently) or just a warning if we're only previewing
                says_str = "subdivide_mask says: "
                message_str = ("You have given me something I can't use "
                               "for padding.  Padding must be a 4-element "
                               "1D array in the format [bottom, top, left,"
                               " right].")
                if preview == True:
                    print (says_str + "WARNING!  " + message_str +
                           "  No padding used this time")
                    pad_bottom, pad_top, pad_left, pad_right=[0,0,0,0]
                else:
                    raise ValueError(says_str + "ERROR!  " + message_str)
        else:
            pad_bottom, pad_top, pad_left, pad_right=[0,0,0,0]
        
        #If we don't have an already chosen angle, choose a bunch of angles
        #and transform the coordinates to rotated systems and get the areas
        #of the rectangle enclosing all the data points at this angle. Take
        #The angle with the minimum area for the enclosing rectangle.
        if rotation_angle is None:
            thetas=np.radians(np.arange(90, dtype=np.float))
            areas=[]
            corners=[]
            for th in thetas:
                x2, y2= misc.rotate_coords(x1, y1, th)
                #Don't take padding into account to determine the angle.
                x2min=x2.min()
                x2max=x2.max()
                y2min=y2.min()
                y2max=y2.max()
                areas.append((x2.max()-x2.min()) * (y2.max()-y2.min()))

            areas=np.asarray(areas)
            which_theta=np.where(areas==areas.min())[0][0]
            use_theta=thetas[which_theta]
        #Else, use the given angle
        else:
            use_theta=np.radians(rotation_angle)

        #Define the edges of the regions
        x2, y2= misc.rotate_coords(x1, y1, use_theta)
        x2min=x2.min() - pad_left
        x2max=x2.max() + pad_right
        y2min=y2.min() - pad_bottom
        y2max=y2.max() + pad_top

        #Figure out the x2 and y2 bin divisions
        if (x2max-x2min) < (y2max-y2min):
            nx=n_shortside
            ny=n_longside
        else:
            ny=n_shortside
            nx=n_longside

        x2edges = np.linspace(x2min, x2max, nx+1)
        y2edges = np.linspace(y2min, y2max, ny+1)

        #-----------------#
        #- Make the plot -#
        #-----------------#
        if preview or save_plot:
            #Figure out what subregions we have
            subregions=self.return_subregions(ra, dec, theta=use_theta,
                                              rot_xedges=x2edges,
                                              rot_yedges=y2edges)
            outside = subregions == -1
            inside= np.invert(outside)
            if only_show is not None:
                this_box = subregions==only_show
                inside = inside & this_box

            #Make a figure and plot the random points
            fig=plt.figure()
            ax=fig.add_subplot(111)
            ax.scatter(x1[outside], y1[outside], c='LightGray')
            ax.scatter(x1[inside], y1[inside], c='Blue')  
                      
            #Plot the vertical lines
            for ix in range(nx+1):
                x2=[x2edges[ix], x2edges[ix]]
                y2=[y2min, y2max]
                x1, y1=misc.rotate_coords(x2, y2, -use_theta)
                ax.plot(x1, y1, color='Red', lw=2)
            #Plot the horizontal lines
            for iy in range(ny+1):
                x2=[x2min, x2max]
                y2=[y2edges[iy], y2edges[iy]]
                x1, y1=misc.rotate_coords(x2, y2, -use_theta)
                ax.plot(x1, y1, color='Red', lw=2)

            #Figure out the dimensions of the boxes in angular space
            x2=[x2edges[0], x2edges[0], x2edges[1]]
            y2=[y2edges[0], y2edges[1], y2edges[0]]
            ra_box, dec_box=self.xy_to_ra_dec(x2, y2)
            y_side=misc.ang_sep(ra_box[0], dec_box[0], ra_box[1], dec_box[1],
                               radians_in=False, radians_out=False) * 3600
            x_side=misc.ang_sep(ra_box[0], dec_box[0], ra_box[2], dec_box[2],
                               radians_in=False, radians_out=False) * 3600

            #Print out the parameters
            ax.text(.05, .95, "theta= "+str(np.degrees(use_theta))[0:5],
                    transform=ax.transAxes, fontsize=8)
            ax.text(.05, .925, "padding="+str(padding),
                    transform=ax.transAxes, fontsize=8)
            ax.text(.05, .9, "n_longside="+str(n_longside),
                    transform=ax.transAxes, fontsize=8)
            ax.text(.05, .875, "n_shortside="+str(n_shortside),
                    transform=ax.transAxes, fontsize=8)
            ax.text(.5, .05, ("box size: "+str(x_side)[0:5]+" by "+
                              str(y_side)[0:5]+" arcsec"),
                    transform=ax.transAxes, fontsize=8)

            #Label the subregions
            y_label_coord=.85
            avg_ngals=float(len(ra[np.invert(outside)]))/(nx*ny)
            ax.text(0.8, 0.95, "N_bin/N_avg", transform=ax.transAxes,
                    fontsize=12)
            ax.text(0.8, 0.9, "outside-> "+str(float(len(ra[outside]))/avg_ngals)[0:4],
                    transform=ax.transAxes, fontsize=9)
            for ix in range(nx):
                for iy in range(ny):
                    #What bin number is this?
                    bin_number= nx*iy + ix
                    #Where's the center of the box?
                    text_x2=(x2edges[ix] + x2edges[ix+1])/2.
                    text_y2=(y2edges[iy] + y2edges[iy+1])/2.
                    text_x1, text_y1=misc.rotate_coords(text_x2, text_y2,
                                                        -use_theta)
                    #Print the bin number at the center of the box
                    ax.text(text_x1, text_y1, str(bin_number), fontsize=20,
                            color='Lime', horizontalalignment='center',
                            verticalalignment='center')

                    #Print the number of galaxies in the upper right corner
                    thisbin= subregions==bin_number
                    n_thisbin= float(len(ra[thisbin]))
                    print ("bin "+str(bin_number)+" has "+str(n_thisbin)+
                           " randoms in it")
                    display_string=('bin '+str(bin_number)+'-> '+
                                    str(n_thisbin/avg_ngals)[0:4])
                    ax.text(0.85, y_label_coord, display_string,
                            transform=ax.transAxes, fontsize=9)
                    y_label_coord-=0.05

            if save_plot:
                plt.savefig(save_plot, bbox_inches='tight')
                plt.close()
            else:
                plt.show()

        #If we want to store the information, do so
        if not preview:
            #Store the subregion information
            self.set_subregions(use_theta, x2edges, y2edges)
Esempio n. 2
0
    def return_subregions(self, ra, dec, theta=None, rot_xedges=None,
                          rot_yedges=None):
        """
        Returns the subregion number for each pair of RA and Dec given the
        parameters either stored or given as function parameters.

        Parameters
        ----------
        ra : array-like
            A list of RAs to get subregion numbers for (in degrees)
            
        dec : array-like
            A list of Decs to get subregion numbers for (in degrees)
            
        theta : scalar (optional)
            Rotation angle that the mask's bounding box will be defined at
            with respect to the XY coordinate system of the mask.  If not
            given, the routine will look for a stored theta.  Units are
            degrees
            
        rot_xedges : array-like (optional)
            The x coordinates of the cell boundaries in the rotated
            coordinate system.  If not given, the routine will look for a
            stored theta.
            
        rot_yedges : array-like (optional)
            The x coordinates of the cell boundaries in the rotated
            coordinate system.  If not given, the routine will look for a
            stored theta.

        Returns
        -------
        subregions : numpy ndarray
            The subregion number for each of the points input.  The array
            shape is (len(ra),).  The subregion -1 is outside the bounding
            box (this only happens if you've set negative padding somewhere
            or have asked for things outside the mask).
        """
        #Check to make sure we have what we need, pull to local if we have
        #stored values but no given values
        if (theta is None):
            if (self._subregion_rotation is None):
                raise ValueError("ImageMask.return_subregions says: "
                                 "ERROR!  I don't have the rotation "
                                 "angle.  Please provide one.")
            else:
                theta=self._subregion_rotation
                
        if (rot_xedges is None):
            if (self._subregion_rotated_xedges is None):
                raise ValueError("ImageMask.return_subregions says: "
                                 "ERROR!  I don't have rotated x edges."
                                 "  Please provide them.")
            else:
                rot_xedges=self._subregion_rotated_xedges
                
        if (rot_yedges is None):
            if (self._subregion_rotated_yedges is None):
                raise ValueError("ImageMask.return_subregions says: "
                                 "ERROR!  I don't have rotated y edges.  "
                                 "Please provide them.")
            else:
                rot_yedges=self._subregion_rotated_yedges

        #Now that we know we have everything, put the ra and decs into x
        #and y coords
        x1, y1=self.ra_dec_to_xy(ra, dec)

        #Transform to the rotated coordinate system
        x2, y2=misc.rotate_coords(x1, y1, theta)

        #Now make masks for each row and column
        nx=len(rot_xedges)-1
        ny=len(rot_yedges)-1
        ymasks={}
        xmasks={}
        for i in range(nx):
            xmasks[i]=ma.masked_inside(x2, rot_xedges[i],
                                       rot_xedges[i+1]).mask
        for i in range(ny):
            ymasks[i]=ma.masked_inside(y2, rot_yedges[i],
                                       rot_yedges[i+1]).mask

        #Now use the masks to put numbers to each galaxy
        #No subregion defaults to -1
        subregion=-np.ones(len(ra))
        for ix in range(nx):
            for iy in range(ny):
                bin_number= nx*iy + ix
                thismask = xmasks[ix] & ymasks[iy]
                subregion[thismask]=bin_number

        return subregion