88from pathlib import Path
99
1010import numpy as np
11+ from astropy import units as u
1112from astropy .coordinates import ICRS , BarycentricTrueEcliptic , Galactic
1213from astropy .io import fits
1314from astropy .nddata import block_reduce
2223from ..utils import as_transparent_rgb , is_jpeg , is_png , parse_input_data
2324from ..wcs_utils import has_celestial , pixel_scale
2425from .utils import (
26+ load_properties ,
2527 make_tile_folders ,
28+ save_properties ,
2629 tile_filename ,
2730 tile_header ,
2831)
@@ -202,6 +205,8 @@ def reproject_to_hips(
202205 # Determine center of image and radius to furthest corner, to determine
203206 # which HiPS tiles need to be generated
204207
208+ # TODO: this will fail for e.g. allsky maps
209+
205210 ny , nx = array_in .shape [- 2 :]
206211
207212 cen_x , cen_y = (nx - 1 ) / 2 , (ny - 1 ) / 2
@@ -212,7 +217,30 @@ def reproject_to_hips(
212217 cen_world = wcs_in .pixel_to_world (cen_x , cen_y )
213218 cor_world = wcs_in .pixel_to_world (cor_x , cor_y )
214219
215- radius = cor_world .separation (cen_world ).max ()
220+ separations = cor_world .separation (cen_world )
221+
222+ if np .any (np .isnan (separations )):
223+
224+ # At least one of the corners is outside of the region of validity of
225+ # the WCS, so we use a different approach where we randomly sample a
226+ # number of positions in the image and then check the maximum
227+ # separation between any pair of points.
228+
229+ n_ran = 1000
230+ ran_x = np .random .uniform (- 0.5 , nx - 0.5 , n_ran )
231+ ran_y = np .random .uniform (- 0.5 , nx - 0.5 , n_ran )
232+
233+ ran_world = wcs_in .pixel_to_world (ran_x , ran_y )
234+
235+ separations = ran_world [:, None ].separation (ran_world [None , :])
236+
237+ max_separation = np .nanmax (separations )
238+
239+ else :
240+
241+ max_separation = separations .max ()
242+
243+ radius = 1.5 * max_separation
216244
217245 # TODO: in future if astropy-healpix implements polygon searches, we could
218246 # use that instead
@@ -222,7 +250,10 @@ def reproject_to_hips(
222250 nside = level_to_nside (level )
223251 hp = HEALPix (nside = nside , order = "nested" , frame = frame )
224252
225- indices = hp .cone_search_skycoord (cen_world , radius = radius )
253+ if radius > 120 * u .deg :
254+ indices = np .arange (hp .npix )
255+ else :
256+ indices = hp .cone_search_skycoord (cen_world , radius = radius )
226257
227258 logger .info (f"Found { len (indices )} tiles (at most) to generate at level { level } " )
228259
@@ -234,12 +265,29 @@ def reproject_to_hips(
234265
235266 # Iterate over the tiles and generate them
236267 def process (index ):
237- header = tile_header (level = level , index = index , frame = frame , tile_size = tile_size )
238268 if hasattr (wcs_in , "deepcopy" ):
239269 wcs_in_copy = wcs_in .deepcopy ()
240270 else :
241271 wcs_in_copy = deepcopy (wcs_in )
242- array_out , footprint = reproject_function ((array_in , wcs_in_copy ), header , ** kwargs )
272+
273+ header = tile_header (level = level , index = index , frame = frame , tile_size = tile_size )
274+
275+ if isinstance (header , tuple ):
276+ array_out1 , footprint1 = reproject_function (
277+ (array_in , wcs_in_copy ), header [0 ], ** kwargs
278+ )
279+ array_out2 , footprint2 = reproject_function (
280+ (array_in , wcs_in_copy ), header [1 ], ** kwargs
281+ )
282+ with np .errstate (invalid = "ignore" ):
283+ array_out = (
284+ np .nan_to_num (array_out1 ) * footprint1 + np .nan_to_num (array_out2 ) * footprint2
285+ ) / (footprint1 + footprint2 )
286+ footprint = (footprint1 + footprint2 ) / 2
287+ header = header [0 ]
288+ else :
289+ array_out , footprint = reproject_function ((array_in , wcs_in_copy ), header , ** kwargs )
290+
243291 if tile_format != "png" :
244292 array_out [np .isnan (array_out )] = 0.0
245293 if np .all (footprint == 0 ):
@@ -253,6 +301,7 @@ def process(index):
253301 extension = EXTENSION [tile_format ],
254302 ),
255303 array_out ,
304+ header ,
256305 )
257306 else :
258307 if tile_format == "png" :
@@ -288,6 +337,9 @@ def process(index):
288337 indices = np .array (generated_indices )
289338
290339 # Iterate over higher levels and compute lower resolution tiles
340+
341+ half_tile_size = tile_size // 2
342+
291343 for ilevel in range (level - 1 , - 1 , - 1 ):
292344
293345 # Find index of tiles to produce at lower-resolution levels
@@ -299,6 +351,9 @@ def process(index):
299351
300352 header = tile_header (level = ilevel , index = index , frame = frame , tile_size = tile_size )
301353
354+ if isinstance (header , tuple ):
355+ header = header [0 ]
356+
302357 if tile_format == "fits" :
303358 array = np .zeros ((tile_size , tile_size ))
304359 elif tile_format == "png" :
@@ -326,13 +381,13 @@ def process(index):
326381 )
327382
328383 if subindex == 0 :
329- array [256 :, :256 ] = data
384+ array [half_tile_size :, :half_tile_size ] = data
330385 elif subindex == 2 :
331- array [256 :, 256 :] = data
386+ array [half_tile_size :, half_tile_size :] = data
332387 elif subindex == 1 :
333- array [:256 , :256 ] = data
388+ array [:half_tile_size , :half_tile_size ] = data
334389 elif subindex == 3 :
335- array [:256 , 256 :] = data
390+ array [:half_tile_size , half_tile_size :] = data
336391
337392 if tile_format == "fits" :
338393 fits .writeto (
@@ -403,21 +458,6 @@ def save_index(directory):
403458 f .write (INDEX_HTML )
404459
405460
406- def save_properties (directory , properties ):
407- with open (os .path .join (directory , "properties" ), "w" ) as f :
408- for key , value in properties .items ():
409- f .write (f"{ key :20s} = { value } \n " )
410-
411-
412- def load_properties (directory ):
413- properties = {}
414- with open (os .path .join (directory , "properties" )) as f :
415- for line in f :
416- key , value = line .split ("=" )
417- properties [key .strip ()] = value .strip ()
418- return properties
419-
420-
421461def coadd_hips (input_directories , output_directory ):
422462 """
423463 Given multiple HiPS directories, combine these into a single HiPS.
0 commit comments