Exemplo n.º 1
0
    def get_aper_mask_qlp(self, sap_mask="round"):
        """
        This is an estimate of QLP aperture based on
        self.hdulist[1].header['BESTAP']

        See:
        https://archive.stsci.edu/hlsps/qlp/hlsp_qlp_tess_ffi_all_tess_v1_data-prod-desc.pdf
        """
        rad = float(self.header["BESTAP"].split(":")[0])
        self.aper_radius = round(rad)
        print(f"Estimating QLP aperture using r={rad} pix.")
        if self.ffi_cutout is None:
            # first download tpf cutout
            self.ffi_cutout = FFI_cutout(
                sector=self.sector,
                gaiaDR2id=self.gaiaid,
                toiid=self.toiid,
                ticid=self.ticid,
                search_radius=self.search_radius,
                quality_bitmask=self.quality_bitmask,
            )
        self.tpf_tesscut = self.ffi_cutout.get_tpf_tesscut()
        aper_mask = parse_aperture_mask(self.tpf_tesscut,
                                        sap_mask=sap_mask,
                                        aper_radius=self.aper_radius)
        self.aper_mask = aper_mask
        return aper_mask
Exemplo n.º 2
0
 def get_aper_mask_cdips(self, sap_mask="round"):
     """
     This is an estimate of CDIPS aperture since
     self.hdulist[1].data.names does not contain aperture
     """
     aper_pix = CDIPS_APER_PIX[int(self.aper_idx) - 1]  # aper_idx=(1,2,3)
     print(
         f"CDIPS has no aperture info in fits. Estimating aperture instead using aper_idx={aper_pix} pix."
     )
     if self.ffi_cutout is None:
         # first download tpf cutout
         self.ffi_cutout = FFI_cutout(
             sector=self.sector,
             gaiaDR2id=self.gaiaid,
             toiid=self.toiid,
             ticid=self.ticid,
             search_radius=self.search_radius,
             quality_bitmask=self.quality_bitmask,
         )
     self.tpf_tesscut = self.ffi_cutout.get_tpf_tesscut()
     idx = int(self.aper_idx) - 1  #
     aper_mask = parse_aperture_mask(
         self.tpf_tesscut,
         sap_mask=sap_mask,
         aper_radius=CDIPS_APER_PIX[idx],
     )
     self.aper_mask = aper_mask
     return aper_mask
Exemplo n.º 3
0
    def get_aper_mask(
        self,
        sector=None,
        sap_mask=None,
        aper_radius=None,
        percentile=None,
        threshold_sigma=None,
        tpf_size=None,
        verbose=True,
    ):
        """
        """
        sector = sector if sector else self.sector
        sap_mask = sap_mask if sap_mask else self.sap_mask
        aper_radius = aper_radius if aper_radius else self.aper_radius
        percentile = percentile if percentile else self.percentile
        threshold_sigma = (threshold_sigma
                           if threshold_sigma else self.threshold_sigma)
        cutout_size = tpf_size if tpf_size else self.cutout_size

        tpf = self.get_tpf_tesscut(sector=sector, cutout_size=cutout_size)

        aper_mask = parse_aperture_mask(
            tpf,
            sap_mask=sap_mask,
            aper_radius=aper_radius,
            percentile=percentile,
            threshold_sigma=threshold_sigma,
            verbose=verbose,
        )
        self.aper_mask = aper_mask
        return aper_mask
Exemplo n.º 4
0
 def get_aper_mask_diamante(self, sap_mask="round"):
     """
     This is an estimate of DIAmante aperture based on aper
     """
     print(f"Estimating DIAmante aperture using r={self.aper_radius} pix.")
     if self.ffi_cutout is None:
         # first download tpf cutout
         self.ffi_cutout = FFI_cutout(
             sector=self.sector,
             gaiaDR2id=self.gaiaid,
             toiid=self.toiid,
             ticid=self.ticid,
             search_radius=self.search_radius,
             quality_bitmask=self.quality_bitmask,
         )
     self.tpf_tesscut = self.ffi_cutout.get_tpf_tesscut()
     aper_mask = parse_aperture_mask(self.tpf_tesscut,
                                     sap_mask=sap_mask,
                                     aper_radius=self.aper_radius)
     self.aper_mask = aper_mask
     return aper_mask
Exemplo n.º 5
0
 def get_aper_mask_pathos(self, sap_mask="round"):
     """
     This is an estimate of PATHOS aperture only
     """
     print(
         "PATHOS has no aperture info in fits. Estimating aperture instead."
     )
     # first download tpf cutout
     self.ffi_cutout = FFI_cutout(
         sector=self.sector,
         gaiaDR2id=self.gaiaid,
         toiid=self.toiid,
         ticid=self.ticid,
         search_radius=self.search_radius,
         quality_bitmask=self.quality_bitmask,
     )
     tpf = self.ffi_cutout.get_tpf_tesscut()
     idx = int(self.aper_idx) - 1  #
     aper_mask = parse_aperture_mask(tpf,
                                     sap_mask=sap_mask,
                                     aper_radius=idx)
     return aper_mask
Exemplo n.º 6
0
    def get_aper_mask(
        self,
        sector=None,
        sap_mask=None,
        aper_radius=None,
        percentile=None,
        threshold_sigma=None,
        verbose=True,
    ):
        """
        """
        sector = sector if sector else self.sector
        sap_mask = sap_mask if sap_mask else self.sap_mask
        aper_radius = aper_radius if aper_radius else self.aper_radius
        percentile = percentile if percentile else self.percentile
        threshold_sigma = (threshold_sigma
                           if threshold_sigma else self.threshold_sigma)

        if self.tpf is None:
            tpf, tpf_info = self.get_tpf(sector=sector, return_df=True)
        else:
            if self.tpf.sector == sector:
                tpf = self.tpf
            else:
                tpf, tpf_info = self.get_tpf(sector=sector, return_df=True)

        aper_mask = parse_aperture_mask(
            tpf,
            sap_mask=sap_mask,
            aper_radius=aper_radius,
            percentile=percentile,
            threshold_sigma=threshold_sigma,
            verbose=verbose,
        )
        self.aper_mask = aper_mask
        return aper_mask
Exemplo n.º 7
0
    def make_custom_lc(
        self,
        sector=None,
        sap_mask=None,
        aper_radius=None,
        percentile=None,
        threshold_sigma=None,
        use_pld=True,
        pixel_components=3,
        spline_n_knots=100,
        spline_degree=3,
        background_mask=None,
        pca_nterms=5,
        with_offset=True,
    ):
        """
        create a custom lightcurve with background subtraction, based on this tutorial:
        https://docs.lightkurve.org/tutorials/04-how-to-remove-tess-scattered-light-using-regressioncorrector.html

        Parameters
        ----------
        sector : int or str
            specific sector or all
        aper_radius: int
            aperture mask radius
        percentile: float
            aperture mask percentile
        threshold_sigma: float
            aperture mask threshold [sigma]
        pca_nterms : int
            number of pca terms to use

        Returns
        -------
        corrected_lc : lightkurve object
        """
        if self.verbose:
            print("Using lightcurve with custom aperture.")
        sector = sector if sector is not None else self.sector
        sap_mask = sap_mask if sap_mask else self.sap_mask
        aper_radius = aper_radius if aper_radius else self.aper_radius
        percentile = percentile if percentile else self.percentile
        threshold_sigma = (threshold_sigma
                           if threshold_sigma else self.threshold_sigma)
        if self.tpf is None:
            tpf, tpf_info = self.get_tpf(sector=sector, return_df=True)
        else:
            if self.tpf.sector == sector:
                tpf = self.tpf
            else:
                tpf, tpf_info = self.get_tpf(sector=sector, return_df=True)
        # Make an aperture mask and a raw light curve
        self.aper_mask = parse_aperture_mask(
            tpf,
            sap_mask=sap_mask,
            aper_radius=aper_radius,
            percentile=percentile,
            threshold_sigma=threshold_sigma,
            verbose=False,
        )
        raw_lc = tpf.to_lightcurve(method="aperture",
                                   aperture_mask=self.aper_mask)
        # remove nans
        idx = (np.isnan(raw_lc.time)
               | np.isnan(raw_lc.flux)
               | np.isnan(raw_lc.flux_err))
        self.tpf = tpf[~idx]
        self.raw_lc = raw_lc[~idx]

        if use_pld:
            if self.verbose:
                print("Removing scattered light + applying PLD")
            pld = lk.TessPLDCorrector(self.tpf, aperture_mask=self.aper_mask)
            if background_mask is None:
                background_mask = ~self.aper_mask
            corrected_lc = pld.correct(
                pixel_components=pixel_components,
                spline_n_knots=spline_n_knots,
                spline_degree=spline_degree,
                background_mask=background_mask,
            )
            self.corrector = pld
        else:
            if self.verbose:
                print("Removing scattered light")
            # Make a design matrix and pass it to a linear regression corrector
            regressors = tpf.flux[~idx][:, ~self.aper_mask]
            dm = (lk.DesignMatrix(
                regressors, name="pixels").pca(pca_nterms).append_constant())

            # Regression Corrector Object
            rc = lk.RegressionCorrector(self.raw_lc)
            self.corrector = rc
            corrected_lc = rc.correct(dm)

            # Optional: Remove the scattered light, allowing for the large offset from scattered light
            if with_offset:
                corrected_lc = (self.raw_lc - rc.model_lc +
                                np.percentile(rc.model_lc.flux, q=5))
        lc = corrected_lc.normalize()
        self.lc_custom = lc
        # compute Contamination
        if self.gaia_sources is None:
            gaia_sources = self.query_gaia_dr2_catalog(radius=120,
                                                       verbose=False)
        else:
            gaia_sources = self.gaia_sources
        fluxes = get_fluxes_within_mask(self.tpf, self.aper_mask, gaia_sources)
        self.contratio = sum(fluxes) - 1
        if self.tic_params is None:
            _ = self.query_tic_catalog(return_nearest_xmatch=True)
        tic_contratio = self.tic_params.contratio
        dcontratio = abs(tic_contratio - self.contratio)
        if (tic_contratio is not None) & (dcontratio > 0.5):
            print(f"contratio: {self.contratio:.2f} (TIC={tic_contratio:.2f})")

        # add method
        lc.detrend = lambda: detrend(lc)
        return lc
Exemplo n.º 8
0
    def make_custom_lc(
        self,
        sector=None,
        tpf_size=None,
        sap_mask=None,
        aper_radius=None,
        percentile=None,
        threshold_sigma=None,
        use_pld=True,
        pixel_components=3,
        spline_n_knots=100,
        spline_degree=3,
        background_mask=None,
        pca_nterms=5,
        with_offset=True,
    ):
        """
        create a custom lightcurve based on this tutorial:
        https://docs.lightkurve.org/tutorials/04-how-to-remove-tess-scattered-light-using-regressioncorrector.html

        Parameters
        ----------
        sector : int or str
            specific sector or all
        cutout_size : tuple
            tpf cutout size
        aper_radius: int
            aperture mask radius
        percentile: float
            aperture mask percentile
        threshold_sigma: float
            aperture mask threshold [sigma]
        method : float
            PLD (default)

        Returns
        -------
        corrected_lc : lightkurve object
        """
        if self.verbose:
            print("Using lightcurve with custom aperture.")
        sector = sector if sector is not None else self.sector
        sap_mask = sap_mask if sap_mask else self.sap_mask
        aper_radius = aper_radius if aper_radius else self.aper_radius
        percentile = percentile if percentile else self.percentile
        threshold_sigma = (threshold_sigma
                           if threshold_sigma else self.threshold_sigma)
        cutout_size = tpf_size if tpf_size else self.cutout_size

        tpf_tesscut = self.get_tpf_tesscut(sector=sector,
                                           cutout_size=cutout_size)

        self.aper_mask = parse_aperture_mask(
            tpf_tesscut,
            sap_mask=sap_mask,
            aper_radius=aper_radius,
            percentile=percentile,
            threshold_sigma=threshold_sigma,
            verbose=False,
        )

        raw_lc = tpf_tesscut.to_lightcurve(method="aperture",
                                           aperture_mask=self.aper_mask)
        # remove nans
        idx = (np.isnan(raw_lc.time)
               | np.isnan(raw_lc.flux)
               | np.isnan(raw_lc.flux_err))
        self.tpf_tesscut = tpf_tesscut[~idx]
        self.lc_custom_raw = raw_lc[~idx]

        if use_pld:
            if self.verbose:
                print("Removing scattered light + applying PLD")
            pld = lk.TessPLDCorrector(self.tpf_tesscut,
                                      aperture_mask=self.aper_mask)
            if background_mask is None:
                background_mask = ~self.aper_mask
            corrected_lc = pld.correct(
                pixel_components=pixel_components,
                spline_n_knots=spline_n_knots,
                spline_degree=spline_degree,
                background_mask=background_mask,
            )
            self.corrector = pld
        else:
            if self.verbose:
                print("Removing scattered light")
            # Make a design matrix and pass it to a linear regression corrector
            regressors = tpf_tesscut.flux[~idx][:, ~self.aper_mask]
            dm = (lk.DesignMatrix(
                regressors,
                name="regressors").pca(nterms=pca_nterms).append_constant())
            rc = lk.RegressionCorrector(raw_lc)
            self.corrector = rc
            corrected_lc = rc.correct(dm)

            # Optional: Remove the scattered light, allowing for the large offset from scattered light
            if with_offset:
                corrected_lc = (raw_lc - rc.model_lc +
                                np.percentile(rc.model_lc.flux, q=5))
        lc = corrected_lc.normalize()
        self.lc_custom = lc

        # compute Contamination
        if self.gaia_sources is None:
            gaia_sources = self.query_gaia_dr2_catalog(radius=120)
        else:
            gaia_sources = self.gaia_sources
        fluxes = get_fluxes_within_mask(self.tpf_tesscut, self.aper_mask,
                                        gaia_sources)
        self.contratio = sum(fluxes) - 1
        # add method
        lc.detrend = lambda: detrend(lc)
        return lc
Exemplo n.º 9
0
def plot_gaia_sources_on_survey(
    tpf,
    target_gaiaid,
    gaia_sources=None,
    fov_rad=None,
    depth=0.0,
    kmax=1.0,
    sap_mask="pipeline",
    survey="DSS2 Red",
    ax=None,
    color_aper="C0",  # pink
    figsize=None,
    invert_xaxis=False,
    invert_yaxis=False,
    pix_scale=TESS_pix_scale,
    verbose=True,
    **mask_kwargs,
):
    """Plot (superpose) Gaia sources on archival image

    Parameters
    ----------
    target_coord : astropy.coordinates
        target coordinate
    gaia_sources : pd.DataFrame
        gaia sources table
    fov_rad : astropy.unit
        FOV radius
    survey : str
        image survey; see from astroquery.skyview import SkyView;
        SkyView.list_surveys()
    verbose : bool
        print texts
    ax : axis
        subplot axis
    color_aper : str
        aperture outline color (default=C6)
    kwargs : dict
        keyword arguments for aper_radius, percentile
    Returns
    -------
    ax : axis
        subplot axis

    TODO: correct for proper motion difference between
    survey image and gaia DR2 positions
    """
    if verbose:
        print("Plotting nearby gaia sources on survey image.")
    assert target_gaiaid is not None
    ny, nx = tpf.flux.shape[1:]
    if fov_rad is None:
        diag = np.sqrt(nx**2 + ny**2)
        fov_rad = (0.4 * diag * pix_scale).to(u.arcmin).round(0)
    target_coord = SkyCoord(ra=tpf.ra * u.deg, dec=tpf.dec * u.deg)
    if gaia_sources is None:
        print(
            "Querying Gaia sometimes hangs. Provide `gaia_sources` if you can."
        )
        gaia_sources = Catalogs.query_region(target_coord,
                                             radius=fov_rad,
                                             catalog="Gaia",
                                             version=2).to_pandas()
    assert len(gaia_sources) > 1, "gaia_sources contains single entry"
    # make aperture mask
    mask = parse_aperture_mask(tpf, sap_mask=sap_mask, **mask_kwargs)
    maskhdr = tpf.hdu[2].header
    # make aperture mask outline
    contour = np.zeros((ny, nx))
    contour[np.where(mask)] = 1
    contour = np.lib.pad(contour, 1, PadWithZeros)
    highres = zoom(contour, 100, order=0, mode="nearest")
    extent = np.array([-1, nx, -1, ny])

    if verbose:
        print(
            f"Querying {survey} ({fov_rad:.2f} x {fov_rad:.2f}) archival image"
        )
    # -----------create figure---------------#
    if ax is None:
        # get img hdu for subplot projection
        try:
            hdu = SkyView.get_images(
                position=target_coord.icrs.to_string(),
                coordinates="icrs",
                survey=survey,
                radius=fov_rad,
                grid=False,
            )[0][0]
        except Exception:
            errmsg = "survey image not available"
            raise FileNotFoundError(errmsg)
        fig = pl.figure(figsize=figsize)
        # define scaling in projection
        ax = fig.add_subplot(111, projection=WCS(hdu.header))
    # plot survey img
    if str(target_coord.distance) == "nan":
        target_coord = SkyCoord(ra=target_coord.ra, dec=target_coord.dec)
    nax, hdu = plot_finder_image(target_coord,
                                 ax=ax,
                                 fov_radius=fov_rad,
                                 survey=survey,
                                 reticle=False)
    imgwcs = WCS(hdu.header)
    mx, my = hdu.data.shape
    # plot mask
    _ = ax.contour(
        highres,
        levels=[0.5],
        extent=extent,
        origin="lower",
        linewidths=[3],
        colors=color_aper,
        transform=ax.get_transform(WCS(maskhdr)),
    )
    idx = gaia_sources["source_id"].astype(int).isin([target_gaiaid])
    target_gmag = gaia_sources.loc[idx, "phot_g_mean_mag"].values[0]

    for index, row in gaia_sources.iterrows():
        marker, s = "o", 100
        r, d, mag, id = row[["ra", "dec", "phot_g_mean_mag", "source_id"]]
        pix = imgwcs.all_world2pix(np.c_[r, d], 1)[0]
        if int(id) != int(target_gaiaid):
            gamma = 1 + 10**(0.4 * (mag - target_gmag))
            if depth > kmax / gamma:
                # too deep to have originated from secondary star
                edgecolor = "C1"
                alpha = 1  # 0.5
            else:
                # possible NEBs
                edgecolor = "C3"
                alpha = 1
        else:
            s = 200
            edgecolor = "C2"
            marker = "s"
            alpha = 1
        nax.scatter(
            pix[0],
            pix[1],
            marker=marker,
            s=s,
            edgecolor=edgecolor,
            alpha=alpha,
            facecolor="none",
        )
    # orient such that north is up; left is east
    if invert_yaxis:
        # ax.invert_yaxis()
        raise NotImplementedError()
    if invert_xaxis:
        # ax.invert_xaxis()
        raise NotImplementedError()
    if hasattr(ax, "coords"):
        ax.coords[0].set_major_formatter("dd:mm")
        ax.coords[1].set_major_formatter("dd:mm")
    # set img limits
    pl.setp(
        nax,
        xlim=(0, mx),
        ylim=(0, my),
        title="{0} ({1:.2f}' x {1:.2f}')".format(survey, fov_rad.value),
    )
    return ax
Exemplo n.º 10
0
def plot_gaia_sources_on_tpf(
    tpf,
    target_gaiaid,
    gaia_sources=None,
    sap_mask="pipeline",
    depth=None,
    kmax=1,
    dmag_limit=8,
    fov_rad=None,
    cmap="viridis",
    figsize=None,
    ax=None,
    invert_xaxis=False,
    invert_yaxis=False,
    pix_scale=TESS_pix_scale,
    verbose=True,
    **mask_kwargs,
):
    """
    plot gaia sources brighter than dmag_limit; only annotated with starids
    are those that are bright enough to cause reproduce the transit depth;
    starids are in increasing separation

    dmag_limit : float
        maximum delta mag to consider; computed based on depth if None

    TODO: correct for proper motion difference between
    survey image and gaia DR2 positions
    """
    if verbose:
        print("Plotting nearby gaia sources on tpf.")
    assert target_gaiaid is not None
    img = np.nanmedian(tpf.flux, axis=0)
    # make aperture mask
    mask = parse_aperture_mask(tpf, sap_mask=sap_mask, **mask_kwargs)
    ax = plot_aperture_outline(img,
                               mask=mask,
                               imgwcs=tpf.wcs,
                               figsize=figsize,
                               cmap=cmap,
                               ax=ax)
    if fov_rad is None:
        nx, ny = tpf.shape[1:]
        diag = np.sqrt(nx**2 + ny**2)
        fov_rad = (0.4 * diag * pix_scale).to(u.arcmin).round(0)

    if gaia_sources is None:
        print(
            "Querying Gaia sometimes hangs. Provide `gaia_sources` if you can."
        )
        target_coord = SkyCoord(ra=tpf.header["RA_OBJ"],
                                dec=tpf.header["DEC_OBJ"],
                                unit="deg")
        gaia_sources = Catalogs.query_region(target_coord,
                                             radius=fov_rad,
                                             catalog="Gaia",
                                             version=2).to_pandas()
    assert len(gaia_sources) > 1, "gaia_sources contains single entry"
    # find sources within mask
    # target is assumed to be the first row
    idx = gaia_sources["source_id"].astype(int).isin([target_gaiaid])
    target_gmag = gaia_sources.loc[idx, "phot_g_mean_mag"].values[0]
    # sources_inside_aperture = []
    if depth is not None:
        # compute delta mag limit given transit depth
        dmag_limit = (np.log10(kmax / depth -
                               1) if dmag_limit is None else dmag_limit)

        # get min_gmag inside mask
        ra, dec = gaia_sources[["ra", "dec"]].values.T
        pix_coords = tpf.wcs.all_world2pix(np.c_[ra, dec], 0)
        contour_points = measure.find_contours(mask, level=0.1)[0]
        isinside = [
            is_point_inside_mask(contour_points, pix) for pix in pix_coords
        ]
        # sources_inside_aperture.append(isinside)
        min_gmag = gaia_sources.loc[isinside, "phot_g_mean_mag"].min()
        if (target_gmag - min_gmag) != 0:
            print(
                f"target Gmag={target_gmag:.2f} is not the brightest within aperture (Gmag={min_gmag:.2f})"
            )
    else:
        min_gmag = gaia_sources.phot_g_mean_mag.min()  # brightest
        dmag_limit = (gaia_sources.phot_g_mean_mag.max()
                      if dmag_limit is None else dmag_limit)

    base_ms = 128.0  # base marker size
    starid = 1
    # if very crowded, plot only top N
    gmags = gaia_sources.phot_g_mean_mag
    dmags = gmags - target_gmag
    rank = np.argsort(dmags.values)
    for index, row in gaia_sources.iterrows():
        # FIXME: why some indexes are missing?
        ra, dec, gmag, id = row[["ra", "dec", "phot_g_mean_mag", "source_id"]]
        dmag = gmag - target_gmag
        pix = tpf.wcs.all_world2pix(np.c_[ra, dec], 0)[0]
        contour_points = measure.find_contours(mask, level=0.1)[0]

        color, alpha = "red", 1.0
        # change marker color and transparency depending on the location and dmag
        if is_point_inside_mask(contour_points, pix):
            if int(id) == int(target_gaiaid):
                # plot x on target
                ax.plot(
                    pix[1],
                    pix[0],
                    marker="x",
                    ms=base_ms / 16,
                    c="k",
                    zorder=3,
                )
            if depth is not None:
                # compute flux ratio with respect to brightest star
                gamma = 1 + 10**(0.4 * (min_gmag - gmag))
                if depth > kmax / gamma:
                    # orange if flux is insignificant
                    color = "C1"
        else:
            # outside aperture
            color, alpha = "C1", 0.5

        ax.scatter(
            pix[1],
            pix[0],
            s=base_ms / 2**dmag,  # fainter -> smaller
            c=color,
            alpha=alpha,
            zorder=2,
            edgecolor=None,
        )
        # choose which star to annotate
        if len(gmags) < 20:
            # sparse: annotate all
            ax.text(pix[1], pix[0], str(starid), color="white", zorder=100)
        elif len(gmags) > 50:
            # crowded: annotate only 15 smallest dmag ones
            if rank[starid - 1] < 15:
                ax.text(pix[1], pix[0], str(starid), color="white", zorder=100)
            elif (color == "red") & (dmag < dmag_limit):
                # plot if within aperture and significant source of dilution
                ax.text(pix[1], pix[0], str(starid), color="white", zorder=100)
        elif color == "red":
            # neither sparse nor crowded
            # annotate if inside aperture
            ax.text(pix[1], pix[0], str(starid), color="white", zorder=100)
        starid += 1
    # Make legend with 4 sizes representative of delta mags
    dmags = dmags[dmags < dmag_limit]
    _, dmags = pd.cut(dmags, 3, retbins=True)
    for dmag in dmags:
        size = base_ms / 2**dmag
        # -1, -1 is outside the fov
        # dmag = 0 if float(dmag)==0 else 0
        ax.scatter(
            -1,
            -1,
            s=size,
            c="red",
            alpha=0.6,
            edgecolor=None,
            zorder=10,
            clip_on=True,
            label=r"$\Delta m= $" + f"{dmag:.1f}",
        )
    ax.legend(fancybox=True, framealpha=0.5)
    # set img limits
    xdeg = (nx * pix_scale).to(u.arcmin)
    ydeg = (ny * pix_scale).to(u.arcmin)
    # orient such that north is up; east is left
    if invert_yaxis:
        # ax.invert_yaxis()  # increasing upward
        raise NotImplementedError()
    if invert_xaxis:
        # ax.invert_xaxis() #decresing rightward
        raise NotImplementedError()
    if hasattr(ax, "coords"):
        ax.coords[0].set_major_formatter("dd:mm")
        ax.coords[1].set_major_formatter("dd:mm")
    pl.setp(ax,
            xlim=(0, nx),
            ylim=(0, ny),
            xlabel=f"({xdeg:.2f} x {ydeg:.2f})")
    return ax