@@ -201,7 +201,7 @@ def get_sectors(self, *, coordinates=None, radius=0*u.deg, objectname=None, movi
201201 return Table (sector_dict )
202202
203203 def download_cutouts (self , * , coordinates = None , size = 5 , sector = None , path = "." , inflate = True ,
204- objectname = None , moving_target = False , mt_type = None ):
204+ objectname = None , moving_target = False , mt_type = None , verbose = False ):
205205 """
206206 Download cutout target pixel file(s) around the given coordinates with indicated size.
207207
@@ -301,7 +301,7 @@ def download_cutouts(self, *, coordinates=None, size=5, sector=None, path=".", i
301301 localpath_table ['Local Path' ] = [zipfile_path ]
302302 return localpath_table
303303
304- print ("Inflating..." )
304+ if verbose : print ("Inflating..." )
305305 # unzipping the zipfile
306306 zip_ref = zipfile .ZipFile (zipfile_path , 'r' )
307307 cutout_files = zip_ref .namelist ()
@@ -477,7 +477,8 @@ def get_surveys(self, coordinates, *, radius="0d"):
477477 warnings .warn ("Coordinates are not in an available deep field survey." , NoResultsWarning )
478478 return survey_json
479479
480- def download_cutouts (self , coordinates , * , size = 5 , survey = None , cutout_format = "fits" , path = "." , inflate = True , ** img_params ):
480+ def download_cutouts (self , coordinates , * , size = 5 , survey = None , cutout_format = "fits" , path = "." , inflate = True ,
481+ verbose = False , ** img_params ):
481482 """
482483 Download cutout FITS/image file(s) around the given coordinates with indicated size.
483484
@@ -560,7 +561,7 @@ def download_cutouts(self, coordinates, *, size=5, survey=None, cutout_format="f
560561 localpath_table ['Local Path' ] = [zipfile_path ]
561562 return localpath_table
562563
563- print ("Inflating..." )
564+ if verbose : print ("Inflating..." )
564565 # unzipping the zipfile
565566 zip_ref = zipfile .ZipFile (zipfile_path , 'r' )
566567 cutout_files = zip_ref .namelist ()
@@ -637,3 +638,164 @@ def get_cutouts(self, coordinates, *, size=5, survey=None):
637638
638639
639640Zcut = ZcutClass ()
641+
642+
643+ class HapcutClass (MastQueryWithLogin ):
644+ """
645+ MAST Hubble Advanced Product (HAP) cutout query class.
646+
647+ Class for accessing HAP image cutouts.
648+ """
649+
650+ def __init__ (self ):
651+
652+ super ().__init__ ()
653+
654+ services = {"astrocut" : {"path" : "astrocut" }}
655+
656+ self ._service_api_connection .set_service_params (services , "hapcut" )
657+
658+ def download_cutouts (self , coordinates , * , size = 5 , path = "." , inflate = True , verbose = False ):
659+ """
660+ Download cutout images around the given coordinates with indicated size.
661+
662+ Parameters
663+ ----------
664+ coordinates : str or `astropy.coordinates` object
665+ The target around which to search. It may be specified as a
666+ string or as the appropriate `astropy.coordinates` object.
667+ size : int, array-like, `~astropy.units.Quantity`
668+ Optional, default 5 pixels.
669+ The size of the cutout array. If ``size`` is a scalar number or
670+ a scalar `~astropy.units.Quantity`, then a square cutout of ``size``
671+ will be created. If ``size`` has two elements, they should be in
672+ ``(ny, nx)`` order. Scalar numbers in ``size`` are assumed to be in
673+ units of pixels. `~astropy.units.Quantity` objects must be in pixel or
674+ angular units.
675+ path : str
676+ Optional.
677+ The directory in which the cutouts will be saved.
678+ Defaults to current directory.
679+ inflate : bool
680+ Optional, default True.
681+ Cutout target pixel files are returned from the server in a zip file,
682+ by default they will be inflated and the zip will be removed.
683+ Set inflate to false to stop before the inflate step.
684+
685+ Returns
686+ -------
687+ response : `~astropy.table.Table`
688+ """
689+
690+ # Get Skycoord object for coordinates/object
691+ coordinates = parse_input_location (coordinates )
692+
693+ # Build initial astrocut request
694+ astrocut_request = f"astrocut?ra={ coordinates .ra .deg } &dec={ coordinates .dec .deg } "
695+
696+ # Add size parameters to request
697+ size_dict = _parse_cutout_size (size )
698+ astrocut_request += f"&x={ size_dict ['x' ]} &y={ size_dict ['y' ]} &units={ size_dict ['units' ]} "
699+
700+ # Build the URL
701+ astrocut_url = self ._service_api_connection .REQUEST_URL + astrocut_request
702+
703+ # Set up the download path
704+ path = os .path .join (path , '' )
705+ zipfile_path = "{}hapcut_{}.zip" .format (path , time .strftime ("%Y%m%d%H%M%S" ))
706+
707+ # Download
708+ self ._download_file (astrocut_url , zipfile_path )
709+ localpath_table = Table (names = ["Local Path" ], dtype = [str ])
710+
711+ # Checking if we got a zip file or a json no results message
712+ if not zipfile .is_zipfile (zipfile_path ):
713+ with open (zipfile_path , 'r' ) as FLE :
714+ response = json .load (FLE )
715+ warnings .warn (response ['msg' ], NoResultsWarning )
716+ return localpath_table
717+
718+ if not inflate : # not unzipping
719+ localpath_table ['Local Path' ] = [zipfile_path ]
720+ return localpath_table
721+
722+ if verbose : print ("Inflating..." )
723+ # unzipping the zipfile
724+ zip_ref = zipfile .ZipFile (zipfile_path , 'r' )
725+ cutout_files = zip_ref .namelist ()
726+ zip_ref .extractall (path , members = cutout_files )
727+ zip_ref .close ()
728+ os .remove (zipfile_path )
729+
730+ localpath_table ['Local Path' ] = [path + x for x in cutout_files ]
731+ return localpath_table
732+
733+
734+ def get_cutouts (self , coordinates , * , size = 5 ):
735+ """
736+ Get cutout image(s) around the given coordinates with indicated size,
737+ and return them as a list of `~astropy.io.fits.HDUList` objects.
738+
739+ Parameters
740+ ----------
741+ coordinates : str or `astropy.coordinates` object
742+ The target around which to search. It may be specified as a
743+ string or as the appropriate `astropy.coordinates` object.
744+ size : int, array-like, `~astropy.units.Quantity`
745+ Optional, default 5 pixels.
746+ The size of the cutout array. If ``size`` is a scalar number or
747+ a scalar `~astropy.units.Quantity`, then a square cutout of ``size``
748+ will be created. If ``size`` has two elements, they should be in
749+ ``(ny, nx)`` order. Scalar numbers in ``size`` are assumed to be in
750+ units of pixels. `~astropy.units.Quantity` objects must be in pixel or
751+ angular units.
752+
753+ Returns
754+ -------
755+ response : A list of `~astropy.io.fits.HDUList` objects.
756+ """
757+
758+ # Get Skycoord object for coordinates/object
759+ coordinates = parse_input_location (coordinates )
760+
761+ param_dict = _parse_cutout_size (size )
762+
763+ # Need to convert integers from numpy dtypes
764+ # so we can convert the dictionary into a JSON object
765+ # in service_request_async(...)
766+ param_dict ["x" ] = float (param_dict ["x" ])
767+ param_dict ["y" ] = float (param_dict ["y" ])
768+
769+ # Adding RA and DEC to parameters dictionary
770+ param_dict ["ra" ] = coordinates .ra .deg
771+ param_dict ["dec" ] = coordinates .dec .deg
772+
773+ response = self ._service_api_connection .service_request_async ("astrocut" , param_dict , use_json = True )
774+ response .raise_for_status () # Raise any errors
775+
776+ try :
777+ ZIPFILE = zipfile .ZipFile (BytesIO (response .content ), 'r' )
778+ except zipfile .BadZipFile :
779+ message = response .json ()
780+ if len (message ['results' ]) == 0 :
781+ warnings .warn (message ['msg' ], NoResultsWarning )
782+ return []
783+ else :
784+ raise
785+
786+ # Open all the contained fits files:
787+ # Since we cannot seek on a compressed zip file,
788+ # we have to read the data, wrap it in another BytesIO object,
789+ # and then open that using fits.open
790+ cutout_hdus_list = []
791+ for name in ZIPFILE .namelist ():
792+ CUTOUT = BytesIO (ZIPFILE .open (name ).read ())
793+ cutout_hdus_list .append (fits .open (CUTOUT ))
794+
795+ # preserve the original filename in the fits object
796+ cutout_hdus_list [- 1 ].filename = name
797+
798+ return cutout_hdus_list
799+
800+
801+ Hapcut = HapcutClass ()
0 commit comments