Skip to content

Commit 517c360

Browse files
committed
Generalize logic of determining field of view that works for all-sky images and re-instate generation of lower resolution tiles
1 parent 3db6470 commit 517c360

File tree

3 files changed

+109
-77
lines changed

3 files changed

+109
-77
lines changed

reproject/hips/_dask_array.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ def __getitem__(self, item):
105105

106106
return self._get_tile(level=self._level, index=index)
107107

108-
@functools.lru_cache(maxsize=128)
108+
@functools.lru_cache(maxsize=128) # noqa: B019
109109
def _get_tile(self, *, level, index):
110110

111111
filename_or_url = tile_filename(

reproject/hips/high_level.py

Lines changed: 106 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
from pathlib import Path
99

1010
import numpy as np
11+
from astropy import units as u
1112
from astropy.coordinates import ICRS, BarycentricTrueEcliptic, Galactic
1213
from astropy.io import fits
14+
from astropy.nddata import block_reduce
1315
from astropy_healpix import (
1416
HEALPix,
1517
level_to_nside,
@@ -215,7 +217,30 @@ def reproject_to_hips(
215217
cen_world = wcs_in.pixel_to_world(cen_x, cen_y)
216218
cor_world = wcs_in.pixel_to_world(cor_x, cor_y)
217219

218-
radius = cor_world.separation(cen_world).max() * 2
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
219244

220245
# TODO: in future if astropy-healpix implements polygon searches, we could
221246
# use that instead
@@ -225,9 +250,10 @@ def reproject_to_hips(
225250
nside = level_to_nside(level)
226251
hp = HEALPix(nside=nside, order="nested", frame=frame)
227252

228-
# indices = hp.cone_search_skycoord(cen_world, radius=radius)
229-
230-
indices = np.arange(hp.npix)
253+
if radius > 120 * u.deg:
254+
indices = np.arange(hp.npix)
255+
else:
256+
indices = hp.cone_search_skycoord(cen_world, radius=radius)
231257

232258
logger.info(f"Found {len(indices)} tiles (at most) to generate at level {level}")
233259

@@ -310,76 +336,82 @@ def process(index):
310336

311337
indices = np.array(generated_indices)
312338

313-
# # Iterate over higher levels and compute lower resolution tiles
314-
# for ilevel in range(level - 1, -1, -1):
315-
316-
# # Find index of tiles to produce at lower-resolution levels
317-
# indices = np.sort(np.unique(indices // 4))
318-
319-
# make_tile_folders(level=ilevel, indices=indices, output_directory=output_directory)
320-
321-
# for index in indices:
322-
323-
# header = tile_header(level=ilevel, index=index, frame=frame, tile_size=tile_size)
324-
325-
# if tile_format == "fits":
326-
# array = np.zeros((tile_size, tile_size))
327-
# elif tile_format == "png":
328-
# array = np.zeros((tile_size, tile_size, 4), dtype=np.uint8)
329-
# else:
330-
# array = np.zeros((tile_size, tile_size, 3), dtype=np.uint8)
331-
332-
# for subindex in range(4):
333-
334-
# current_index = 4 * index + subindex
335-
# subtile_filename = tile_filename(
336-
# level=ilevel + 1,
337-
# index=current_index,
338-
# output_directory=output_directory,
339-
# extension=EXTENSION[tile_format],
340-
# )
341-
342-
# if os.path.exists(subtile_filename):
343-
344-
# if tile_format == "fits":
345-
# data = block_reduce(fits.getdata(subtile_filename), 2, func=np.mean)
346-
# else:
347-
# data = block_reduce(
348-
# np.array(Image.open(subtile_filename))[::-1], (2, 2, 1), func=np.mean
349-
# )
350-
351-
# if subindex == 0:
352-
# array[256:, :256] = data
353-
# elif subindex == 2:
354-
# array[256:, 256:] = data
355-
# elif subindex == 1:
356-
# array[:256, :256] = data
357-
# elif subindex == 3:
358-
# array[:256, 256:] = data
359-
360-
# if tile_format == "fits":
361-
# fits.writeto(
362-
# tile_filename(
363-
# level=ilevel,
364-
# index=index,
365-
# output_directory=output_directory,
366-
# extension=EXTENSION[tile_format],
367-
# ),
368-
# array,
369-
# header,
370-
# )
371-
# else:
372-
# image = as_transparent_rgb(array.transpose(2, 0, 1))
373-
# if tile_format == "jpeg":
374-
# image = image.convert("RGB")
375-
# image.save(
376-
# tile_filename(
377-
# level=ilevel,
378-
# index=index,
379-
# output_directory=output_directory,
380-
# extension=EXTENSION[tile_format],
381-
# )
382-
# )
339+
# Iterate over higher levels and compute lower resolution tiles
340+
341+
half_tile_size = tile_size // 2
342+
343+
for ilevel in range(level - 1, -1, -1):
344+
345+
# Find index of tiles to produce at lower-resolution levels
346+
indices = np.sort(np.unique(indices // 4))
347+
348+
make_tile_folders(level=ilevel, indices=indices, output_directory=output_directory)
349+
350+
for index in indices:
351+
352+
header = tile_header(level=ilevel, index=index, frame=frame, tile_size=tile_size)
353+
354+
if isinstance(header, tuple):
355+
header = header[0]
356+
357+
if tile_format == "fits":
358+
array = np.zeros((tile_size, tile_size))
359+
elif tile_format == "png":
360+
array = np.zeros((tile_size, tile_size, 4), dtype=np.uint8)
361+
else:
362+
array = np.zeros((tile_size, tile_size, 3), dtype=np.uint8)
363+
364+
for subindex in range(4):
365+
366+
current_index = 4 * index + subindex
367+
subtile_filename = tile_filename(
368+
level=ilevel + 1,
369+
index=current_index,
370+
output_directory=output_directory,
371+
extension=EXTENSION[tile_format],
372+
)
373+
374+
if os.path.exists(subtile_filename):
375+
376+
if tile_format == "fits":
377+
data = block_reduce(fits.getdata(subtile_filename), 2, func=np.mean)
378+
else:
379+
data = block_reduce(
380+
np.array(Image.open(subtile_filename))[::-1], (2, 2, 1), func=np.mean
381+
)
382+
383+
if subindex == 0:
384+
array[half_tile_size:, :half_tile_size] = data
385+
elif subindex == 2:
386+
array[half_tile_size:, half_tile_size:] = data
387+
elif subindex == 1:
388+
array[:half_tile_size, :half_tile_size] = data
389+
elif subindex == 3:
390+
array[:half_tile_size, half_tile_size:] = data
391+
392+
if tile_format == "fits":
393+
fits.writeto(
394+
tile_filename(
395+
level=ilevel,
396+
index=index,
397+
output_directory=output_directory,
398+
extension=EXTENSION[tile_format],
399+
),
400+
array,
401+
header,
402+
)
403+
else:
404+
image = as_transparent_rgb(array.transpose(2, 0, 1))
405+
if tile_format == "jpeg":
406+
image = image.convert("RGB")
407+
image.save(
408+
tile_filename(
409+
level=ilevel,
410+
index=index,
411+
output_directory=output_directory,
412+
extension=EXTENSION[tile_format],
413+
)
414+
)
383415

384416
# Generate properties file
385417

reproject/hips/tests/test_dask_array.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,11 @@ def test_roundtrip(self, tmp_path, frame, level):
5353
# adjacent to non-empty tiles, we set the values to the boundaries of
5454
# the non-empty neighbouring tiles so that interpolation doesn't run
5555
# into any issues. In theory there should be around 90500 pixels inside
56-
# the valid region of the image, so we require at least 90400 valid
56+
# the valid region of the image, so we require at least 90000 valid
5757
# values.
5858

5959
valid = ~np.isnan(final_array)
60-
assert np.sum(valid) > 90400
60+
assert np.sum(valid) > 90000
6161
np.testing.assert_allclose(final_array[valid], self.original_array[valid], rtol=0.01)
6262

6363
def test_level_validation(self, tmp_path):

0 commit comments

Comments
 (0)