Recreating a wrapper for the astroquery.astrometry_net using a local installation of Astrometry.net instead

44 views
Skip to first unread message

Aaron Lancaster

unread,
Mar 21, 2024, 2:48:53 PMMar 21
to astrometry
Hello group, 

As the subject line says, I am currently attempting to recreate a wrapper for querying Astrometry.net with the astroquery.astrometry_net module so that it instead uses a local installation of the astrometry.net software. 

I am running a local installation of Astrometry.net using Ubuntu and have created a Python function for calling the solve-field command from outside the WSL, which looks like this: 

import subprocess

def run_astrometry_solvefield(image_path,cfg_path):
    # Convert Windows paths to WSL paths
    wsl_image_path = image_path.replace('\\','/').replace("C:", "/mnt/c")
    wsl_cfg_path = cfg_path.replace('\\','/').replace("C:", "/mnt/c")
   
    # Command to run in WSL
    command = f"wsl /bin/bash -c 'solve-field {wsl_image_path} --continue --config {wsl_cfg_path}'"
   
    try:
        # Run the command in the WSL environment
        result = subprocess.run(command, shell=True, capture_output=True, text=True)
       
        # Check if the command ran successfully
        if result.returncode == 0:
            print("Astrometry.net command executed successfully")
            print("Output:")
            print(result.stdout)
        else:
            print("Error running Astrometry.net command:") # Indicates an error in the execution of the command in WSL
            print(result.stderr)
           
    except Exception as e:
        print("An error occurred:", e) # Indicates an error in the 'try' block/subprocess module
       
# Call the function with the image path and config file path
# image_path = r"C:\Users\alancaster\astrometry_net\demos\apod4.jpg"
# cfg_path = r"/home/alancaster/astrometry.net-0.93/etc/astrometry.cfg"
# run_astrometry_solvefield(image_path,cfg_path)      


Now I would like to utilize this function to solve for a list of sources using the solve-field command (thus using the local installation of Astrometry.net) in place of the astroquery.astrometry_net module in my colleague's wrapper below:

def plate_solve(sources, image_width, image_height, radec=None, radius=1.0, scale_upper=10.0, scale_lower=0.01, api_key=None):
    """
    Query astrometry.net with a list of point source positions and return a WCS solution. Source list should be sorted
    by brightness in descending order.

    Add your astrometry.net API key to the astroquery.cfg file per these directions:

    Alternatively, manually supply your API key as an argument.

    Parameters
    ----------
    sources : QTable
        Table of sources as produced by DAO Star Finder or similar
    image_width : num
        Width of frame.
    image_height : num
        Height of frame.
    radec : (num, num), optional
        Provide an initial guess for the RA & Dec coordinates for the center of the field.
    radius: num, optional
        Provide a radius in degrees for the uncertainty of the provided RA & Dec. Default is 1.0 deg.
    scale_upper : num
        The upper limit of the width of the field in degrees. Default is 10.0 deg.
    scale_lower : num
        The lower limit of the width of the field in degrees. Default is 0.01 deg.
    api_key : None or string
        If None, astroquery will try to use the API key in the config file. Otherwise will use the supplied string as the API key.

    Returns
    -------
    WCS object
        Astropy WCS object. False if plate solution is unsuccesful.

    """
    ast = AstrometryNet()

    if api_key is not None:
        ast.api_key = api_key

    if ast.api_key == "":
        logger.critical("No API key provided. Cannot query astrometry.net without an API key.")
        return False

    try_again = True
    submission_id = None

    while try_again:
        try:
            if not submission_id:
                if radec is None:
                    logger.info("Querying Astrometry.net for plate solution...")
                    # Add 1 to coordinates as astrometry.net assumes fits image origin of 1,1
                    wcs_header = ast.solve_from_source_list(sources['xcentroid'].value + 1.0, sources['ycentroid'].value + 1.0,
                                                            image_width, image_height, crpix_center=True,
                                                            scale_units="degwidth", scale_type="ul", scale_upper=scale_upper, scale_lower=scale_lower,
                                                            positional_error=3, tweak_order=3,
                                                            publicly_visible='n', allow_commercial_use='y',
                                                            submission_id=submission_id
                                                            )
                else:
                    logger.info("Querying Astrometry.net for plate solution...")
                    # Add 1 to coordinates as astrometry.net assumes fits image origin of 1,1
                    wcs_header = ast.solve_from_source_list(sources['xcentroid'].value + 1.0, sources['ycentroid'].value + 1.0,
                                                            image_width, image_height, crpix_center=True,
                                                            center_ra=radec[0], center_dec=radec[1], radius=radius,
                                                            scale_units="degwidth", scale_type="ul", scale_upper=scale_upper, scale_lower=scale_lower,
                                                            positional_error=3, tweak_order=3,
                                                            publicly_visible='n', allow_commercial_use='y',
                                                            submission_id=submission_id
                                                            )
            else:
                wcs_header = ast.monitor_submission(submission_id, solve_timeout=300)
       
        except astroquery.exceptions.TimeoutError as e:
            # Did not receive result before timeout
            logger.warning(e)
            logger.warning("Did not receive result from Astrometry.net before timeout. Will keep checking for solution.")
            submission_id = e.args[1]
       
        except Exception as e:
            if e.__str__() == "Unable to log in to astrometry.net":
                logger.error(e.__str__() + ", check API key.")
            else:
                logger.error(e)
            return False
               
        else:
            # Received result
            try_again = False

    if wcs_header:
        logger.success("Recieved succesful result from Astrometry.net!")
        return WCS(wcs_header)
    else:
        logger.error("Failed to recieve succesful solution from Astrometry.net!")
        return False

I would like to do this in a way that minimizes the number of changes we need to make to his code (as this is just one custom module of many contributing to his larger project). Note that 'sources', 'image_height', and 'image_width' are all defined in the parent script which is calling this function. 

The 'sources' have been detected from an image and are saved in a QTable. Here is an example of what it looks like printed out using his pprint_all function:

sources printed.PNG

I would like to find the simplest way to call the solve-field command with a variation of my function using his list of sources. Is it possible to do this while keeping the data in this format as he has done with the astroquery.astrometry_net module? Or do I need to somehow convert this list of sources into a FITS BinTable and save it as an .xyls file -- then call the solve-field command as I would for an .xyls? 

Somewhat related, I've had some difficulties using the solve-field command for .xyls files. Namely, I was able to solve the field for the demo image "apod4" when using the xy-list "apod4-indx.xyls" but not when using "apod4.xyls". Is there a reason that it only works for the former, or should it work for both? What is the difference between the two? 

Thanks in advance for taking the time to read all this and any suggestions.

Best,
-Aaron 

Dustin Lang

unread,
Mar 21, 2024, 2:59:54 PMMar 21
to Aaron Lancaster, astrometry
Hi,


The 'sources' have been detected from an image and are saved in a QTable. Here is an example of what it looks like printed out using his pprint_all function:

sources printed.PNG

I would like to find the simplest way to call the solve-field command with a variation of my function using his list of sources. Is it possible to do this while keeping the data in this format as he has done with the astroquery.astrometry_net module? Or do I need to somehow convert this list of sources into a FITS BinTable and save it as an .xyls file -- then call the solve-field command as I would for an .xyls? 

To me this sounds like your easiest option -- if you're calling solve-field as an external program then you need to pass the data in some way or another.

Since you've already got astropy and the data are in an astropy QTable, I would look at writing it out as FITS, https://docs.astropy.org/en/stable/io/unified.html#table-io-fits


Somewhat related, I've had some difficulties using the solve-field command for .xyls files. Namely, I was able to solve the field for the demo image "apod4" when using the xy-list "apod4-indx.xyls" but not when using "apod4.xyls". Is there a reason that it only works for the former, or should it work for both? What is the difference between the two? 

apod4-indx.xyls are the pixel locations of INDEX stars -- this would have been written out by a previous successful solve.


cheers,
dustin

Aaron Lancaster

unread,
Mar 21, 2024, 4:38:09 PMMar 21
to astrometry
Great, thanks Dustin! And that's what I thought for the xyls files-- I am still having some issues when trying to use solve-field for xyls files. Here is what my command and output look like: 

alancaster@SWD-ALANCASTER:~$ solve-field /home/alancaster/astrometry.net-0.93/demo/apod1.xyls --height 526 --width 800 --continue --config /home/alancaster/astrometry.net-0.93/etc/astrometry.cfg
\Reading input file 1 of 1: "/home/alancaster/astrometry.net-0.93/demo/apod1.xyls"...
(null): EOF / read error reading magic number
(null): EOF / read error reading magic number
solve-field.c:327:plot_source_overlay Plotting command failed
 solve-field.c:133:run_command Command was: "/usr/bin/plotxy -I /tmp/tmp.ppm.PKXhm9 -i /home/alancaster/astrometry.net-0.93/demo/apod1.axy -C red -w 2 -N 50 -x 1 -y 1 -P | /usr/bin/plotxy -i /home/alancaster/astrometry.net-0.93/demo/apod1.axy -I - -w 2 -r 3 -C red -n 50 -N 200 -x 1 -y 1 > /home/alancaster/astrometry.net-0.93/demo/apod1-objs.png"

 solve-field.c:132:run_command Command exited with exit status 1
Solving...
Reading file "/home/alancaster/astrometry.net-0.93/demo/apod1.axy"...
CPU time limit reached!
Field 1 did not solve (index index-4119.fits).
Field: /home/alancaster/astrometry.net-0.93/demo/apod1.xyls
Warning: there was already a WCS file, and its timestamp has not changed.
Field center: (RA,Dec) = (100.215766, 9.831532) deg.
Field center: (RA H:M:S, Dec D:M:S) = (06:40:51.784, +09:49:53.514).
Field size: 90.3627 x 59.3878 arcminutes
Field rotation angle: up is -91.1074 degrees E of N
Field parity: neg

Can you tell me what I'm doing wrong here?

Thanks again,
-Aaron

Dustin Lang

unread,
Mar 21, 2024, 4:43:33 PMMar 21
to Aaron Lancaster, astrometry
You need specific index files for the different example files - see


--
You received this message because you are subscribed to the Google Groups "astrometry" group.
To unsubscribe from this group and stop receiving emails from it, send an email to astrometry+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/astrometry/7396c33b-4d39-447c-90ab-7926a07b3bafn%40googlegroups.com.
Reply all
Reply to author
Forward
Message has been deleted
Message has been deleted
0 new messages