Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DM-40746: Fix size and shape of HiPS Allsky file. #831

Merged
merged 2 commits into from
Sep 14, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 22 additions & 6 deletions python/lsst/pipe/tasks/hips.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@
from lsst.afw.geom import makeHpxWcs
from lsst.resources import ResourcePath

from .healSparseMapping import _is_power_of_two


class HighResolutionHipsConnections(pipeBase.PipelineTaskConnections,
dimensions=("healpix9", "band"),
Expand Down Expand Up @@ -829,8 +831,9 @@ class GenerateHipsConfig(pipeBase.PipelineTaskConfig,
)
allsky_tilesize = pexConfig.Field(
dtype=int,
doc="Allsky.png tile size. Must be power of 2.",
default=512,
doc="Allsky tile size; must be power of 2. HiPS standard recommends 64x64 tiles.",
default=64,
check=_is_power_of_two,
)
png_gray_asinh_minimum = pexConfig.Field(
doc="AsinhMapping intensity to be mapped to black for grayscale png scaling.",
Expand Down Expand Up @@ -1532,7 +1535,20 @@ def _write_allsky_file(self, hips_base_path, allsky_order):
HEALPix order of the minimum order to make allsky file.
"""
tile_size = self.config.allsky_tilesize
n_tiles_per_side = int(np.sqrt(hpg.nside_to_npixel(hpg.order_to_nside(allsky_order))))

# The Allsky file format is described in
# https://www.ivoa.net/documents/HiPS/20170519/REC-HIPS-1.0-20170519.pdf
# From S4.3.2:
# The Allsky file is built as an array of tiles, stored side by side in
# the left-to-right order. The width of this array must be the square
# root of the number of the tiles of the order. For instance, the width
# of this array at order 3 is 27 ( (int)sqrt(768) ). To avoid having a
# too large Allsky file, the resolution of each tile may be reduced but
# must stay a power of two (typically 64x64 pixels rather than 512x512).

n_tiles = hpg.nside_to_npixel(hpg.order_to_nside(allsky_order))
n_tiles_wide = int(np.floor(np.sqrt(n_tiles)))
n_tiles_high = int(np.ceil(n_tiles / n_tiles_wide))

allsky_image = None

Expand All @@ -1549,15 +1565,15 @@ def _write_allsky_file(self, hips_base_path, allsky_order):
matches = re.match(pixel_regex, png_uri.basename())
pix_num = int(matches.group(1))
tile_image = Image.open(io.BytesIO(png_uri.read()))
row = math.floor(pix_num//n_tiles_per_side)
column = pix_num % n_tiles_per_side
row = math.floor(pix_num//n_tiles_wide)
column = pix_num % n_tiles_wide
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Row and column both use the width?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it's div and mod to get x/y from an ordered list.

box = (column*tile_size, row*tile_size, (column + 1)*tile_size, (row + 1)*tile_size)
tile_image_shrunk = tile_image.resize((tile_size, tile_size))

if allsky_image is None:
allsky_image = Image.new(
tile_image.mode,
(n_tiles_per_side*tile_size, n_tiles_per_side*tile_size),
(n_tiles_wide*tile_size, n_tiles_high*tile_size),
)
allsky_image.paste(tile_image_shrunk, box)

Expand Down