GSoC final evaluation report

Link to GitHub repository: http://github.com/hipspy/hips

In addition to the main hips repository, I also maintained my personal HIPS-to-Py repository on GitHub. This contains Jupyter notebooks which showcase the functionality in hips and numerous related Python scripts. The Wiki page contains a short description on hips. It also contains links to resource documents and telcon notes, which are hosted on Google Docs.

List of Pull Requests

Work related with HiPS tile drawing
Work related with HiPS tiles
Work related with WCSGeometry
Work related with HEALPix and HiPS utility functions
Work related with HiPS Surveys
Documentation changes
Miscellaneous

List of commits

List of commit diffs

commit 98ff03be7cd921b06f4ebb77fa2cd7db7124e3ca
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Mon Aug 28 20:49:24 2017 +0500

    Restructure high level function fetch_tiles

diff --git a/hips/tiles/fetch.py b/hips/tiles/fetch.py
index 5c6063a..1af267c 100644
--- a/hips/tiles/fetch.py
+++ b/hips/tiles/fetch.py
@@ -75,83 +75,83 @@ def fetch_tiles(tile_metas: List[HipsTileMeta], hips_survey: HipsSurveyPropertie
     else:
         raise ValueError(f'Invalid package name: {fetch_package}')

-    tiles = fetch_fct(tile_metas, hips_survey, progress_bar, n_parallel, timeout)
+    _tile_urls = tile_urls(tile_metas, hips_survey)
+    response_all = fetch_fct(_tile_urls, hips_survey, progress_bar, n_parallel, timeout)

     # Sort tiles to match the tile_meta list
     # TODO: this doesn't seem like a great solution.
     # Use OrderedDict instead?
-    out = []
-    for tile_meta in tile_metas:
-        for tile in tiles:
-            if tile.meta == tile_meta:
-                out.append(tile)
-                continue
-    return out
+    tiles = []
+    for tile_url in _tile_urls:
+        for idx, response in enumerate(response_all):
+            if response['url'] == tile_url:
+                tiles.append(HipsTile(tile_metas[idx], response['raw_data']))

+    return tiles
+
+def tile_urls(tile_metas: List[HipsTileMeta], hips_survey: HipsSurveyProperties) -> List[str]:
+    """Retrun list of tile URLs"""
+    return [hips_survey.tile_url(meta) for meta in tile_metas]

-def fetch_tile_urllib(url: str, meta: HipsTileMeta, timeout: float) -> HipsTile:
+def fetch_tile_urllib(url: str, timeout: float) -> dict:
     """Fetch a HiPS tile asynchronously."""
     with urllib.request.urlopen(url, timeout=timeout) as conn:
-        raw_data = conn.read()
-        return HipsTile(meta, raw_data)
+        return {'raw_data': conn.read(), 'url': url}


-def tiles_urllib(tile_metas: List[HipsTileMeta], hips_survey: HipsSurveyProperties,
-                 progress_bar: bool, n_parallel, timeout: float) -> List[HipsTile]:
+def tiles_urllib(tile_urls: List[str], hips_survey: HipsSurveyProperties,
+                 progress_bar: bool, n_parallel, timeout: float) -> List[dict]:
     """Generator function to fetch HiPS tiles from a remote URL."""
     with concurrent.futures.ThreadPoolExecutor(max_workers=n_parallel) as executor:
         futures = []
-        for meta in tile_metas:
-            url = hips_survey.tile_url(meta)
-            future = executor.submit(fetch_tile_urllib, url, meta, timeout)
+        for url in tile_urls:
+            future = executor.submit(fetch_tile_urllib, url, timeout)
             futures.append(future)

         futures = concurrent.futures.as_completed(futures)
         if progress_bar:
             from tqdm import tqdm
-            futures = tqdm(futures, total=len(tile_metas), desc='Fetching tiles')
+            futures = tqdm(futures, total=len(tile_urls), desc='Fetching tiles')

-        tiles = []
+        response_all = []
         for future in futures:
-            tiles.append(future.result())
+            response_all.append(future.result())

-    return tiles
+    return response_all


-async def fetch_tile_aiohttp(url: str, meta: HipsTileMeta, session, timeout: float) -> HipsTile:
+async def fetch_tile_aiohttp(url: str, session, timeout: float) -> dict:
     """Fetch a HiPS tile asynchronously using aiohttp."""
     async with session.get(url, timeout=timeout) as response:
-        raw_data = await response.read()
-        return HipsTile(meta, raw_data)
+        return {'raw_data': await response.read(), 'url': url}


-async def fetch_all_tiles_aiohttp(tile_metas: List[HipsTileMeta], hips_survey: HipsSurveyProperties,
-                                  progress_bar: bool, n_parallel: int, timeout: float) -> List[HipsTile]:
+async def fetch_all_tiles_aiohttp(tile_urls: List[str], hips_survey: HipsSurveyProperties,
+                                  progress_bar: bool, n_parallel: int, timeout: float) -> List[dict]:
     """Generator function to fetch HiPS tiles from a remote URL using aiohttp."""
     import aiohttp

     connector = aiohttp.TCPConnector(limit=n_parallel)
     async with aiohttp.ClientSession(connector=connector) as session:
         futures = []
-        for meta in tile_metas:
-            url = hips_survey.tile_url(meta)
-            future = asyncio.ensure_future(fetch_tile_aiohttp(url, meta, session, timeout))
+        for url in tile_urls:
+            future = asyncio.ensure_future(fetch_tile_aiohttp(url, session, timeout))
             futures.append(future)

         futures = asyncio.as_completed(futures)
         if progress_bar:
             from tqdm import tqdm
-            futures = tqdm(futures, total=len(tile_metas), desc='Fetching tiles')
+            futures = tqdm(futures, total=len(tile_urls), desc='Fetching tiles')

-        tiles = []
+        response_all = []
         for future in futures:
-            tiles.append(await future)
+            response_all.append(await future)

-    return tiles
+    return response_all


-def tiles_aiohttp(tile_metas: List[HipsTileMeta], hips_survey: HipsSurveyProperties,
-                  progress_bar: bool, n_parallel: int, timeout: float) -> List[HipsTile]:
+def tiles_aiohttp(tile_urls: List[str], hips_survey: HipsSurveyProperties,
+                  progress_bar: bool, n_parallel: int, timeout: float) -> List[dict]:
     return asyncio.get_event_loop().run_until_complete(
-        fetch_all_tiles_aiohttp(tile_metas, hips_survey, progress_bar, n_parallel, timeout)
+        fetch_all_tiles_aiohttp(tile_urls, hips_survey, progress_bar, n_parallel, timeout)
     )

commit d7979afd03c5376c4cd1216e6b20c5e2ee3b7627
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Aug 25 20:35:59 2017 +0500

    Add timeout and n_parallel option to tiles_aiohttp function

diff --git a/hips/tiles/fetch.py b/hips/tiles/fetch.py
index 8a8c004..54a5729 100644
--- a/hips/tiles/fetch.py
+++ b/hips/tiles/fetch.py
@@ -118,40 +118,40 @@ def tiles_urllib(tile_metas: List[HipsTileMeta], hips_survey: HipsSurveyProperti
     return tiles


-async def fetch_tile_aiohttp(url: str, meta: HipsTileMeta, session) -> Generator:
+async def fetch_tile_aiohttp(url: str, meta: HipsTileMeta, session, timeout: float) -> Generator:
     """Fetch a HiPS tile asynchronously using aiohttp."""
-    async with session.get(url) as response:
+    async with session.get(url, timeout=timeout) as response:
         raw_data = await response.read()
         return HipsTile(meta, raw_data)


-async def fetch_all_tiles_aiohttp(tile_metas: List[HipsTileMeta],
-                                  hips_survey: HipsSurveyProperties, progress_bar: bool) -> List[HipsTile]:
+async def fetch_all_tiles_aiohttp(tile_metas: List[HipsTileMeta], hips_survey: HipsSurveyProperties,
+                                  progress_bar: bool, n_parallel: int, timeout: float) -> List[HipsTile]:
     """Generator function to fetch HiPS tiles from a remote URL using aiohttp."""
     import aiohttp

-    async with aiohttp.ClientSession() as session:
-        futures = []
-        for meta in tile_metas:
-            url = hips_survey.tile_url(meta)
-            future = asyncio.ensure_future(fetch_tile_aiohttp(url, meta, session))
-            futures.append(future)
+    with await asyncio.Semaphore(n_parallel):
+        async with aiohttp.ClientSession() as session:
+            futures = []
+            for meta in tile_metas:
+                url = hips_survey.tile_url(meta)
+                future = asyncio.ensure_future(fetch_tile_aiohttp(url, meta, session, timeout))
+                futures.append(future)

-        futures = asyncio.as_completed(futures)
-        if progress_bar:
-            from tqdm import tqdm
-            futures = tqdm(futures, total=len(tile_metas), desc='Fetching tiles')
+            futures = asyncio.as_completed(futures)
+            if progress_bar:
+                from tqdm import tqdm
+                futures = tqdm(futures, total=len(tile_metas), desc='Fetching tiles')

-        tiles = []
-        for future in futures:
-            tiles.append(await future)
+            tiles = []
+            for future in futures:
+                tiles.append(await future)

     return tiles


 def tiles_aiohttp(tile_metas: List[HipsTileMeta], hips_survey: HipsSurveyProperties,
                   progress_bar: bool, n_parallel: int = 10, timeout: float = 10) -> List[HipsTile]:
-    # TODO: implement n_parallel and timeout
     return asyncio.get_event_loop().run_until_complete(
-        fetch_all_tiles_aiohttp(tile_metas, hips_survey, progress_bar)
+        fetch_all_tiles_aiohttp(tile_metas, hips_survey, progress_bar, n_parallel, timeout)
     )

commit d27168bce994e772302636facb30fd7482c82023
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Tue Aug 22 18:31:40 2017 +0500

    Add example section for fetch_tiles function docstring

diff --git a/hips/draw/paint.py b/hips/draw/paint.py
index 502a9e8..69fbc87 100644
--- a/hips/draw/paint.py
+++ b/hips/draw/paint.py
@@ -37,7 +37,8 @@ class HipsPainter:
     progress_bar : bool
         Show a progress bar for tile fetching and drawing
     fetch_opts : dict
-        Keyword arguments for fetching HiPS tiles
+        Keyword arguments for fetching HiPS tiles. To see the
+        list of passable arguments, refer to `~fetch_tiles`

     Examples
     --------
@@ -127,7 +128,7 @@ class HipsPainter:

         if self._tiles is None:
             self._tiles = fetch_tiles(tile_metas=tile_metas, hips_survey=self.hips_survey,
-                                      progress_bar=self.progress_bar, **self.fetch_opts)
+                                      progress_bar=self.progress_bar, **(self.fetch_opts or {}))

         return self._tiles

diff --git a/hips/draw/ui.py b/hips/draw/ui.py
index e950d40..1b25e8e 100644
--- a/hips/draw/ui.py
+++ b/hips/draw/ui.py
@@ -34,7 +34,8 @@ def make_sky_image(geometry: Union[dict, WCSGeometry], hips_survey: Union[str, '
     progress_bar : bool
         Show a progress bar for tile fetching and drawing
     fetch_opts : dict
-        Keyword arguments for fetching HiPS tiles
+        Keyword arguments for fetching HiPS tiles. To see the
+        list of passable arguments, refer to `~hips.fetch_tiles`

     Returns
     -------
diff --git a/hips/tiles/fetch.py b/hips/tiles/fetch.py
index 28afbf6..a898f78 100644
--- a/hips/tiles/fetch.py
+++ b/hips/tiles/fetch.py
@@ -9,8 +9,14 @@ __all__ = [
     'fetch_tiles',
 ]

+__doctest_skip__ = [
+    'fetch_tiles',
+]
+
+
 def fetch_tiles(tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperties,
-                progress_bar: bool = False, n_parallel: int = 10,  timeout: int = 10, fetch_package : str = 'urllib') -> List[HipsTile]:
+                progress_bar: bool = False, n_parallel: int = 10,
+                timeout: float = 10, fetch_package : str = 'urllib') -> List[HipsTile]:
     """Fetch a list of HiPS tiles.

     This function fetches a list of HiPS tiles based
@@ -28,10 +34,37 @@ def fetch_tiles(tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperti
         Show a progress bar for tile fetching and drawing
     n_parallel : int
         Number of threads to use for fetching HiPS tiles
-    timeout : int
+    timeout : float
         Seconds to timeout for fetching a HiPS tile
     fetch_package : {'urllib', 'aiohttp'}
         Package to use for fetching HiPS tiles
+
+    Examples
+    --------
+    >>> from hips import HipsSurveyProperties
+    >>> from hips import fetch_tiles
+    >>> url = 'http://alasky.unistra.fr/DSS/DSS2Merged/properties'
+    >>> hips_survey = HipsSurveyProperties.fetch(url)
+    >>> tile_indices = [69623, 69627, 69628, 69629, 69630, 69631]
+    >>> tile_metas = []
+    >>> for healpix_pixel_index in tile_indices:
+    ...    tile_meta = HipsTileMeta(
+    ...        order=7,
+    ...        ipix=healpix_pixel_index,
+    ...        frame=hips_survey.astropy_frame,
+    ...        file_format='fits',
+    ...    )
+    ...    tile_metas.append(tile_meta)
+    >>> tiles = fetch_tiles(tile_metas, hips_survey)
+    >>> tiles[0].meta.file_format
+    fits
+    >>> tiles[0].meta.order
+    7
+
+    Returns
+    -------
+    tiles : List[HipsTile]
+        A Python list of HiPS tiles
     """
     if fetch_package == 'aiohttp':
         return tiles_aiohttp(tile_metas, hips_survey, progress_bar)
@@ -40,36 +73,30 @@ def fetch_tiles(tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperti
     else:
         raise ValueError(f'Invalid package name: {fetch_package}')

-def tile_urls(tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperties) -> List[str]:
-    """List of tile URLs"""
-    return [hips_survey.tile_url(meta) for meta in tile_metas]
-
-def fetch_tile_urllib(url: str, meta: HipsTileMeta, timeout: int) -> Generator:
+def fetch_tile_urllib(url: str, meta: HipsTileMeta, timeout: float) -> Generator:
     """Fetch a HiPS tile asynchronously."""
     with urllib.request.urlopen(url, timeout=timeout) as conn:
         raw_data = conn.read()
         return HipsTile(meta, raw_data)

 def tiles_urllib(tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperties,
-                 progress_bar: bool = False, n_parallel: int = 10,  timeout: int = 10) -> List[HipsTile]:
+                 progress_bar: bool = False, n_parallel: int = 10,  timeout: float = 10) -> List[HipsTile]:
     """Generator function to fetch HiPS tiles from a remote URL."""
     with concurrent.futures.ThreadPoolExecutor(max_workers=n_parallel) as executor:
-        future_to_url = {executor.submit(
-            fetch_tile_urllib,
-            url,
-            tile_metas[idx],
-            timeout)
-            : url for idx, url in enumerate(tile_urls(tile_metas, hips_survey))}
+        futures = []
+        for meta in tile_metas:
+            url = hips_survey.tile_url(meta)
+            futures.append(executor.submit(fetch_tile_urllib, url, meta, timeout))

         if progress_bar:
-            from tqdm import tqdm
-            requests = tqdm(future_to_url, total=len(future_to_url), desc='Fetching tiles')
+            from tqdm import tqdm as progress_bar
         else:
-            requests = future_to_url
+            def progress_bar(*args):
+                return args[0]

         tiles = []
-        for request in requests:
-            tiles.append(request.result())
+        for future in progress_bar(concurrent.futures.as_completed(futures), total=len(futures), desc='Fetching tiles'):
+            tiles.append(future.result())

     return tiles

@@ -79,26 +106,31 @@ async def fetch_tile_aiohttp(url: str, meta : HipsTileMeta, session) -> Generato
         raw_data = await response.read()
         return HipsTile(meta, raw_data)

-async def fetch_all_tiles_aiohttp(tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperties, progress_bar: bool) -> List[HipsTile]:
+async def fetch_all_tiles_aiohttp(tile_metas: List[HipsTileMeta],
+                                  hips_survey : HipsSurveyProperties, progress_bar: bool) -> List[HipsTile]:
     """Generator function to fetch HiPS tiles from a remote URL using aiohttp."""
     import aiohttp

-    tasks = []
     async with aiohttp.ClientSession() as session:
-        for idx, url in enumerate(tile_urls(tile_metas, hips_survey)):
-            task = asyncio.ensure_future(fetch_tile_aiohttp(url.format(idx), tile_metas[idx], session))
-            tasks.append(task)
+        futures = []
+        for meta in tile_metas:
+            url = hips_survey.tile_url(meta)
+            futures.append(asyncio.ensure_future(fetch_tile_aiohttp(url, meta, session)))

         if progress_bar:
-            from tqdm import tqdm
-            tiles = []
-            for f in tqdm(tasks, total=len(tasks), desc='Fetching tiles'):
-                tiles.append(await f)
+            from tqdm import tqdm as progress_bar
         else:
-            tiles = await asyncio.gather(*tasks)
+            def progress_bar(*args):
+                return args[0]
+
+        tiles = []
+        for future in progress_bar(asyncio.as_completed(futures), total=len(futures), desc='Fetching tiles'):
+            tiles.append(await future)

     return tiles

 def tiles_aiohttp(tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperties,
                   progress_bar: bool) -> List[HipsTile]:
-    return asyncio.get_event_loop().run_until_complete(fetch_all_tiles_aiohttp(tile_metas, hips_survey, progress_bar))
+    return asyncio.get_event_loop().run_until_complete(
+        fetch_all_tiles_aiohttp(tile_metas, hips_survey, progress_bar)
+    )
diff --git a/hips/tiles/tests/test_fetch.py b/hips/tiles/tests/test_fetch.py
index 53d7ff6..3ba29d6 100644
--- a/hips/tiles/tests/test_fetch.py
+++ b/hips/tiles/tests/test_fetch.py
@@ -8,7 +8,7 @@ from ..tile import HipsTileMeta

 TILE_FETCH_TEST_CASES = [
     dict(
-        tile_indices=[69623, 69627, 69628, 69629, 69630, 69631],
+        tile_indices=[69623],
         tile_format='fits',
         order=7,
         url='http://alasky.unistra.fr/DSS/DSS2Merged/properties',
@@ -17,7 +17,7 @@ TILE_FETCH_TEST_CASES = [
         fetch_package='urllib'
     ),
     dict(
-        tile_indices=[69623, 69627, 69628, 69629, 69630, 69631],
+        tile_indices=[69623],
         tile_format='fits',
         order=7,
         url='http://alasky.unistra.fr/DSS/DSS2Merged/properties',

commit 1116a7a81b01295e5aac21cfdd9e044accf258c2
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Mon Aug 21 22:31:44 2017 +0500

    Update aiohttp dependency config

diff --git a/.rtd-environment.yml b/.rtd-environment.yml
index 6c2884b..529543d 100644
--- a/.rtd-environment.yml
+++ b/.rtd-environment.yml
@@ -12,6 +12,7 @@ dependencies:
     - reproject
     - matplotlib
     - tqdm
+    - aiohttp
     # There's a problem with Sphinx 1.6 with astropy-helpers
     # For now, we pin the Sphinx version to something that works
     - sphinx==1.5.6
diff --git a/.travis.yml b/.travis.yml
index a486285..49e12dd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -27,7 +27,7 @@ env:
         - MAIN_CMD='python setup.py'
         - SETUP_CMD='test'
         - EVENT_TYPE='pull_request push'
-        - CONDA_DEPENDENCIES='healpy scikit-image Pillow reproject matplotlib tqdm'
+        - CONDA_DEPENDENCIES='healpy scikit-image Pillow reproject matplotlib tqdm aiohttp'
         - PIP_DEPENDENCIES=''
         - CONDA_CHANNELS='conda-forge astropy-ci-extras astropy'
         - SETUP_XVFB=True
diff --git a/docs/installation.rst b/docs/installation.rst
index 28bdc62..d88d8f4 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -80,5 +80,6 @@ In addition, the following packages are needed for optional functionality:

 * `Matplotlib`_ 2.0 or later. Used for plotting in examples.
 * `tqdm`_. Used for showing progress bar either on terminal or in Jupyter notebook.
+* `aiohttp`_. Used for fetching HiPS tiles.

 We have some info at :ref:`py3` on why we don't support legacy Python (Python 2).
\ No newline at end of file
diff --git a/docs/references.txt b/docs/references.txt
index 9230626..53d1586 100644
--- a/docs/references.txt
+++ b/docs/references.txt
@@ -6,4 +6,5 @@
 .. _HiPS paper: https://www.aanda.org/articles/aa/pdf/2015/06/aa26075-15.pdf
 .. _HiPS IVOA recommendation: http://www.ivoa.net/documents/HiPS/
 .. _HiPS at CDS: http://aladin.u-strasbg.fr/hips/
-.. _tqdm: https://pypi.python.org/pypi/tqdm
\ No newline at end of file
+.. _tqdm: https://pypi.python.org/pypi/tqdm
+.. _aiohttp: http://aiohttp.readthedocs.io/en/stable/
\ No newline at end of file
diff --git a/hips/draw/paint.py b/hips/draw/paint.py
index b76cb59..502a9e8 100644
--- a/hips/draw/paint.py
+++ b/hips/draw/paint.py
@@ -4,7 +4,7 @@ import numpy as np
 from typing import List, Tuple, Union, Dict, Any
 from astropy.wcs.utils import proj_plane_pixel_scales
 from skimage.transform import ProjectiveTransform, warp
-from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta, HipsTileFetcher
+from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta, fetch_tiles
 from ..tiles.tile import compute_image_shape
 from ..utils import WCSGeometry, healpix_pixels_in_sky_image, hips_order_for_pixel_resolution

@@ -36,8 +36,8 @@ class HipsPainter:
         Use the precise drawing algorithm
     progress_bar : bool
         Show a progress bar for tile fetching and drawing
-    fetch_package : {'urllib', 'aiohttp'}
-        Package to use for fetching HiPS tiles
+    fetch_opts : dict
+        Keyword arguments for fetching HiPS tiles

     Examples
     --------
@@ -61,13 +61,13 @@ class HipsPainter:
     """

     def __init__(self, geometry: Union[dict, WCSGeometry], hips_survey: Union[str, HipsSurveyProperties],
-                 tile_format: str, precise: bool = False, progress_bar: bool = True, fetch_package: str = 'urllib') -> None:
+                 tile_format: str, precise: bool = False, progress_bar: bool = True, fetch_opts : dict = None) -> None:
         self.geometry = WCSGeometry.make(geometry)
         self.hips_survey = HipsSurveyProperties.make(hips_survey)
         self.tile_format = tile_format
         self.precise = precise
         self.progress_bar = progress_bar
-        self.fetch_package = fetch_package
+        self.fetch_opts = fetch_opts
         self._tiles = None
         self.float_image = None
         self._stats: Dict[str, Any] = {}
@@ -125,11 +125,9 @@ class HipsPainter:
             )
             tile_metas.append(tile_meta)

-        tile_fetcher = HipsTileFetcher(tile_metas=tile_metas, hips_survey=self.hips_survey,
-                                       progress_bar=self.progress_bar, fetch_package=self.fetch_package)
-
         if self._tiles is None:
-            self._tiles = tile_fetcher.tiles
+            self._tiles = fetch_tiles(tile_metas=tile_metas, hips_survey=self.hips_survey,
+                                      progress_bar=self.progress_bar, **self.fetch_opts)

         return self._tiles

diff --git a/hips/draw/tests/test_paint.py b/hips/draw/tests/test_paint.py
index 0515014..dfb2db8 100644
--- a/hips/draw/tests/test_paint.py
+++ b/hips/draw/tests/test_paint.py
@@ -20,7 +20,8 @@ class TestHipsPainter:
             width=2000, height=1000, fov="3 deg",
             coordsys='icrs', projection='AIT',
         )
-        cls.painter = HipsPainter(cls.geometry, cls.hips_survey, 'fits', fetch_package='aiohttp')
+        fetch_opts = dict(fetch_package='urllib', timeout=30, n_parallel=10)
+        cls.painter = HipsPainter(cls.geometry, cls.hips_survey, 'fits', fetch_opts=fetch_opts)

     def test_draw_hips_order(self):
         assert self.painter.draw_hips_order == 7
@@ -43,7 +44,8 @@ class TestHipsPainter:
             coordsys='icrs', projection='AIT',
         )

-        simple_tile_painter = HipsPainter(geometry, self.hips_survey, 'fits', fetch_package='aiohttp')
+        fetch_opts = dict(fetch_package='urllib', timeout=30, n_parallel=10)
+        simple_tile_painter = HipsPainter(geometry, self.hips_survey, 'fits', fetch_opts=fetch_opts)
         assert simple_tile_painter.draw_hips_order == pars['order']

     def test_run(self):
diff --git a/hips/draw/tests/test_ui.py b/hips/draw/tests/test_ui.py
index 569e047..d699868 100644
--- a/hips/draw/tests/test_ui.py
+++ b/hips/draw/tests/test_ui.py
@@ -61,7 +61,9 @@ def test_make_sky_image(tmpdir, pars):
     hips_survey = HipsSurveyProperties.fetch(url=pars['url'])
     geometry = make_test_wcs_geometry()

-    result = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=pars['file_format'], precise=pars['precise'])
+    fetch_opts = dict(fetch_package='urllib', timeout=30, n_parallel=10)
+    result = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=pars['file_format'],
+                            precise=pars['precise'], fetch_opts=fetch_opts)

     assert result.image.shape == pars['shape']
     assert result.image.dtype == pars['dtype']
diff --git a/hips/draw/ui.py b/hips/draw/ui.py
index 8d7e1a8..e950d40 100644
--- a/hips/draw/ui.py
+++ b/hips/draw/ui.py
@@ -15,7 +15,7 @@ __all__ = [


 def make_sky_image(geometry: Union[dict, WCSGeometry], hips_survey: Union[str, 'HipsSurveyProperties'],
-                   tile_format: str, precise: bool = False, progress_bar: bool = True, fetch_package: str = 'urllib') -> 'HipsDrawResult':
+                   tile_format: str, precise: bool = False, progress_bar: bool = True, fetch_opts: dict = None) -> 'HipsDrawResult':
     """Make sky image: fetch tiles and draw.

     The example for this can be found on the :ref:`gs` page.
@@ -33,15 +33,15 @@ def make_sky_image(geometry: Union[dict, WCSGeometry], hips_survey: Union[str, '
         Use the precise drawing algorithm
     progress_bar : bool
         Show a progress bar for tile fetching and drawing
-    fetch_package : {'urllib', 'aiohttp'}
-        Package to use for fetching HiPS tiles
+    fetch_opts : dict
+        Keyword arguments for fetching HiPS tiles

     Returns
     -------
     result : `~hips.HipsDrawResult`
         Result object
     """
-    painter = HipsPainter(geometry, hips_survey, tile_format, precise, progress_bar, fetch_package)
+    painter = HipsPainter(geometry, hips_survey, tile_format, precise, progress_bar, fetch_opts)
     painter.run()
     return HipsDrawResult.from_painter(painter)

diff --git a/hips/tiles/fetch.py b/hips/tiles/fetch.py
index a776ec3..28afbf6 100644
--- a/hips/tiles/fetch.py
+++ b/hips/tiles/fetch.py
@@ -1,18 +1,23 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
-import numpy as np
+import asyncio
 import urllib.request
 import concurrent.futures
 from typing import Generator, List
 from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta

 __all__ = [
-    'HipsTileFetcher',
+    'fetch_tiles',
 ]

-
-class HipsTileFetcher:
+def fetch_tiles(tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperties,
+                progress_bar: bool = False, n_parallel: int = 10,  timeout: int = 10, fetch_package : str = 'urllib') -> List[HipsTile]:
     """Fetch a list of HiPS tiles.

+    This function fetches a list of HiPS tiles based
+    on their URLs, which are generated using `hips_survey`
+    and `tile_metas`. The tiles are then fetched asynchronously
+    using urllib or aiohttp.
+
     Parameters
     ----------
     tile_metas : List[HipsTileMeta]
@@ -28,86 +33,72 @@ class HipsTileFetcher:
     fetch_package : {'urllib', 'aiohttp'}
         Package to use for fetching HiPS tiles
     """
-
-    def __init__(self, tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperties,
-                 progress_bar: bool = False, n_parallel: int = 10,  timeout: int = 10, fetch_package : str = 'urllib') -> None:
-        self.tile_metas = tile_metas
-        self.hips_survey = hips_survey
-        self.progress_bar = progress_bar
-        self.n_parallel = n_parallel
-        self.timeout = timeout
-        self.fetch_package = fetch_package
-
-    @property
-    def tile_urls(self) -> List[str]:
-        """List of tile URLs"""
-        tile_urls = []
-        for meta in self.tile_metas:
-            tile_urls.append(self.hips_survey.tile_url(meta))
-
-        return tile_urls
-
-    @property
-    def tiles(self):
-        if self.fetch_package == 'aiohttp':
-            return self.tiles_aiohttp
-        elif self.fetch_package == 'urllib':
-            return self.tiles_urllib
+    if fetch_package == 'aiohttp':
+        return tiles_aiohttp(tile_metas, hips_survey, progress_bar)
+    elif fetch_package == 'urllib':
+        return tiles_urllib(tile_metas, hips_survey, progress_bar, n_parallel, timeout)
+    else:
+        raise ValueError(f'Invalid package name: {fetch_package}')
+
+def tile_urls(tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperties) -> List[str]:
+    """List of tile URLs"""
+    return [hips_survey.tile_url(meta) for meta in tile_metas]
+
+def fetch_tile_urllib(url: str, meta: HipsTileMeta, timeout: int) -> Generator:
+    """Fetch a HiPS tile asynchronously."""
+    with urllib.request.urlopen(url, timeout=timeout) as conn:
+        raw_data = conn.read()
+        return HipsTile(meta, raw_data)
+
+def tiles_urllib(tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperties,
+                 progress_bar: bool = False, n_parallel: int = 10,  timeout: int = 10) -> List[HipsTile]:
+    """Generator function to fetch HiPS tiles from a remote URL."""
+    with concurrent.futures.ThreadPoolExecutor(max_workers=n_parallel) as executor:
+        future_to_url = {executor.submit(
+            fetch_tile_urllib,
+            url,
+            tile_metas[idx],
+            timeout)
+            : url for idx, url in enumerate(tile_urls(tile_metas, hips_survey))}
+
+        if progress_bar:
+            from tqdm import tqdm
+            requests = tqdm(future_to_url, total=len(future_to_url), desc='Fetching tiles')
         else:
-            raise ValueError(f'Invalid package name: {self.fetch_package}')
-
-    def fetch_tile_urllib(self, url: str, meta : HipsTileMeta) -> Generator:
-        """Fetch a HiPS tile asynchronously."""
-        with urllib.request.urlopen(url, timeout=self.timeout) as conn:
-            raw_data = conn.read()
-            return HipsTile(meta, raw_data)
-
-    @property
-    def tiles_urllib(self) -> np.ndarray:
-        """Generator function to fetch HiPS tiles from a remote URL."""
-        with concurrent.futures.ThreadPoolExecutor(max_workers=self.n_parallel) as executor:
-            future_to_url = {executor.submit(self.fetch_tile_urllib, url, self.tile_metas[idx]) : url for idx, url in enumerate(self.tile_urls)}
-
-            if self.progress_bar:
-                from tqdm import tqdm
-                requests = tqdm(concurrent.futures.as_completed(future_to_url), total=len(future_to_url), desc='Fetching tiles')
-            else:
-                requests = future_to_url#concurrent.futures.as_completed(future_to_url)
+            requests = future_to_url
+
+        tiles = []
+        for request in requests:
+            tiles.append(request.result())
+
+    return tiles
+
+async def fetch_tile_aiohttp(url: str, meta : HipsTileMeta, session) -> Generator:
+    """Fetch a HiPS tile asynchronously using aiohttp."""
+    async with session.get(url) as response:
+        raw_data = await response.read()
+        return HipsTile(meta, raw_data)

+async def fetch_all_tiles_aiohttp(tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperties, progress_bar: bool) -> List[HipsTile]:
+    """Generator function to fetch HiPS tiles from a remote URL using aiohttp."""
+    import aiohttp
+
+    tasks = []
+    async with aiohttp.ClientSession() as session:
+        for idx, url in enumerate(tile_urls(tile_metas, hips_survey)):
+            task = asyncio.ensure_future(fetch_tile_aiohttp(url.format(idx), tile_metas[idx], session))
+            tasks.append(task)
+
+        if progress_bar:
+            from tqdm import tqdm
             tiles = []
-            for future in requests:
-                tiles.append(future.result())
-
-        return tiles
-
-    async def fetch_tile_aiohttp(self, url: str, meta : HipsTileMeta, session) -> Generator:
-        """Fetch a HiPS tile asynchronously using aiohttp."""
-        async with session.get(url) as response:
-            raw_data = await response.read()
-            return HipsTile(meta, raw_data)
-
-    @property
-    async def fetch_all_tiles_aiohttp(self) -> np.ndarray:
-        """Generator function to fetch HiPS tiles from a remote URL using aiohttp."""
-        import aiohttp, asyncio
-
-        tasks = []
-        async with aiohttp.ClientSession() as session:
-            for idx, url in enumerate(self.tile_urls):
-                task = asyncio.ensure_future(self.fetch_tile_aiohttp(url.format(idx), self.tile_metas[idx], session))
-                tasks.append(task)
-
-            if self.progress_bar:
-                from tqdm import tqdm
-                tiles = []
-                for f in tqdm(tasks, total=len(tasks), desc='Fetching tiles'):
-                    tiles.append(await f)
-            else:
-                tiles = await asyncio.gather(*tasks)
-
-        return tiles
-
-    @property
-    def tiles_aiohttp(self) -> np.ndarray:
-        import asyncio
-        return asyncio.get_event_loop().run_until_complete(self.fetch_all_tiles_aiohttp)
+            for f in tqdm(tasks, total=len(tasks), desc='Fetching tiles'):
+                tiles.append(await f)
+        else:
+            tiles = await asyncio.gather(*tasks)
+
+    return tiles
+
+def tiles_aiohttp(tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperties,
+                  progress_bar: bool) -> List[HipsTile]:
+    return asyncio.get_event_loop().run_until_complete(fetch_all_tiles_aiohttp(tile_metas, hips_survey, progress_bar))
diff --git a/hips/tiles/tests/test_fetch.py b/hips/tiles/tests/test_fetch.py
index d7956df..53d7ff6 100644
--- a/hips/tiles/tests/test_fetch.py
+++ b/hips/tiles/tests/test_fetch.py
@@ -1,34 +1,47 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
+import pytest
 from astropy.tests.helper import remote_data
 from numpy.testing import assert_allclose
-from ..fetch import HipsTileFetcher
+from ..fetch import fetch_tiles
 from ..survey import HipsSurveyProperties
 from ..tile import HipsTileMeta

-class TestHipsTileFetcher:
-    @classmethod
-    def setup_class(cls):
-        url = 'http://alasky.unistra.fr/DSS/DSS2Merged/properties'
-        hips_survey = HipsSurveyProperties.fetch(url)
+TILE_FETCH_TEST_CASES = [
+    dict(
+        tile_indices=[69623, 69627, 69628, 69629, 69630, 69631],
+        tile_format='fits',
+        order=7,
+        url='http://alasky.unistra.fr/DSS/DSS2Merged/properties',
+        progress_bar=True,
+        data=[2101, 1680, 1532, 1625, 2131],
+        fetch_package='urllib'
+    ),
+    dict(
+        tile_indices=[69623, 69627, 69628, 69629, 69630, 69631],
+        tile_format='fits',
+        order=7,
+        url='http://alasky.unistra.fr/DSS/DSS2Merged/properties',
+        progress_bar=True,
+        data=[2101, 1680, 1532, 1625, 2131],
+        fetch_package='aiohttp'
+    ),
+]

-        tile_metas, tile_indices = [], [69623, 69627, 69628, 69629, 69630, 69631]
-        for healpix_pixel_index in tile_indices:
-            tile_meta = HipsTileMeta(
-                order=7,
-                ipix=healpix_pixel_index,
-                frame=hips_survey.astropy_frame,
-                file_format='fits',
-            )
-            tile_metas.append(tile_meta)

-        cls.fetcher = HipsTileFetcher(tile_metas, hips_survey, progress_bar=False)
+@pytest.mark.parametrize('pars', TILE_FETCH_TEST_CASES)
+@remote_data
+def test_fetch_tiles(pars):
+    hips_survey = HipsSurveyProperties.fetch(pars['url'])

-    @remote_data
-    def test_tiles(self):
-        tiles = self.fetcher.tiles
-        assert_allclose(tiles[0].data[0][5:10], [2101, 1680, 1532, 1625, 2131])
+    tile_metas = []
+    for healpix_pixel_index in pars['tile_indices']:
+        tile_meta = HipsTileMeta(
+            order=pars['order'],
+            ipix=healpix_pixel_index,
+            frame=hips_survey.astropy_frame,
+            file_format=pars['tile_format'],
+        )
+        tile_metas.append(tile_meta)

-    @remote_data
-    def test_tiles_aiohttp(self):
-        tiles = self.fetcher.tiles_aiohttp
-        assert_allclose(tiles[0].data[0][5:10], [2101, 1680, 1532, 1625, 2131])
+    tiles = fetch_tiles(tile_metas, hips_survey, progress_bar=pars['progress_bar'], fetch_package=pars['fetch_package'])
+    assert_allclose(tiles[0].data[0][5:10], [2101, 1680, 1532, 1625, 2131])
diff --git a/setup.py b/setup.py
old mode 100755
new mode 100644
index af0099f..3ed902f
--- a/setup.py
+++ b/setup.py
@@ -107,6 +107,7 @@ extras_require = dict(
         'matplotlib>=2.0',
         'reproject>=0.3.1',
         'tqdm',
+        'aiohttp',
     ],
     develop=[
         'matplotlib>=2.0',
@@ -114,6 +115,7 @@ extras_require = dict(
         'pytest>=3.0',
         'mypy>=0.501',
         'tqdm',
+        'aiohttp',
     ],
 )


commit e5118522ba544bab164116a01b1a87a8c3c4f5a2
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Thu Aug 17 20:47:31 2017 +0500

    Add tqdm progress bar to urllib and aiohttp fetch methods

diff --git a/hips/draw/paint.py b/hips/draw/paint.py
index 07affb9..b76cb59 100644
--- a/hips/draw/paint.py
+++ b/hips/draw/paint.py
@@ -1,9 +1,7 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 import time
-import aiohttp
-import asyncio
 import numpy as np
-from typing import List, Tuple, Union, Dict, Any, Generator
+from typing import List, Tuple, Union, Dict, Any
 from astropy.wcs.utils import proj_plane_pixel_scales
 from skimage.transform import ProjectiveTransform, warp
 from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta, HipsTileFetcher
@@ -38,6 +36,8 @@ class HipsPainter:
         Use the precise drawing algorithm
     progress_bar : bool
         Show a progress bar for tile fetching and drawing
+    fetch_package : {'urllib', 'aiohttp'}
+        Package to use for fetching HiPS tiles

     Examples
     --------
@@ -61,12 +61,13 @@ class HipsPainter:
     """

     def __init__(self, geometry: Union[dict, WCSGeometry], hips_survey: Union[str, HipsSurveyProperties],
-                 tile_format: str, precise: bool = False, progress_bar: bool = True) -> None:
+                 tile_format: str, precise: bool = False, progress_bar: bool = True, fetch_package: str = 'urllib') -> None:
         self.geometry = WCSGeometry.make(geometry)
         self.hips_survey = HipsSurveyProperties.make(hips_survey)
         self.tile_format = tile_format
         self.precise = precise
         self.progress_bar = progress_bar
+        self.fetch_package = fetch_package
         self._tiles = None
         self.float_image = None
         self._stats: Dict[str, Any] = {}
@@ -111,21 +112,10 @@ class HipsPainter:
         pt.estimate(src, dst)
         return pt

-    async def fetch_tile_threaded(self, url: str, session: aiohttp.client.ClientSession) -> Generator:
-        """Fetch a HiPS tile asynchronously."""
-        async with session.get(url) as response:
-            return await response.read()
-
-    async def _fetch_tiles(self) -> List[HipsTile]:
-        """Generator function to fetch HiPS tiles from a remote URL."""
-        if self.progress_bar:
-            from tqdm import tqdm
-            tile_indices = tqdm(self.tile_indices, desc='Fetching tiles')
-        else:
-            tile_indices = self.tile_indices
-
-        for healpix_pixel_index in tile_indices:
-        tile_urls, tile_metas = [], []
+    @property
+    def tiles(self) -> List[HipsTile]:
+        """List of `~hips.HipsTile` (cached on multiple access)."""
+        tile_metas = []
         for healpix_pixel_index in self.tile_indices:
             tile_meta = HipsTileMeta(
                 order=self.draw_hips_order,
@@ -133,27 +123,12 @@ class HipsPainter:
                 frame=self.hips_survey.astropy_frame,
                 file_format=self.tile_format,
             )
-            tile_urls.append(self.hips_survey.tile_url(tile_meta))
             tile_metas.append(tile_meta)

-        tasks = []
-        async with aiohttp.ClientSession() as session:
-            for idx, url in enumerate(tile_urls):
-                task = asyncio.ensure_future(self.fetch_tile_threaded(url.format(idx), session))
-                tasks.append(task)
-
-            raw_responses = await asyncio.gather(*tasks)
-
-        tiles = []
-        for idx, raw_data in enumerate(raw_responses):
-            tiles.append(HipsTile(tile_metas[idx], raw_data))
-        return tiles
+        tile_fetcher = HipsTileFetcher(tile_metas=tile_metas, hips_survey=self.hips_survey,
+                                       progress_bar=self.progress_bar, fetch_package=self.fetch_package)

-    @property
-    def tiles(self) -> List[HipsTile]:
-        """List of `~hips.HipsTile` (cached on multiple access)."""
         if self._tiles is None:
-            # self._tiles = asyncio.get_event_loop().run_until_complete(tile_fetcher.tiles)
             self._tiles = tile_fetcher.tiles

         return self._tiles
diff --git a/hips/draw/tests/test_paint.py b/hips/draw/tests/test_paint.py
index 817e143..0515014 100644
--- a/hips/draw/tests/test_paint.py
+++ b/hips/draw/tests/test_paint.py
@@ -20,7 +20,7 @@ class TestHipsPainter:
             width=2000, height=1000, fov="3 deg",
             coordsys='icrs', projection='AIT',
         )
-        cls.painter = HipsPainter(cls.geometry, cls.hips_survey, 'fits')
+        cls.painter = HipsPainter(cls.geometry, cls.hips_survey, 'fits', fetch_package='aiohttp')

     def test_draw_hips_order(self):
         assert self.painter.draw_hips_order == 7
@@ -43,7 +43,7 @@ class TestHipsPainter:
             coordsys='icrs', projection='AIT',
         )

-        simple_tile_painter = HipsPainter(geometry, self.hips_survey, 'fits')
+        simple_tile_painter = HipsPainter(geometry, self.hips_survey, 'fits', fetch_package='aiohttp')
         assert simple_tile_painter.draw_hips_order == pars['order']

     def test_run(self):
diff --git a/hips/draw/ui.py b/hips/draw/ui.py
index 47716ea..8d7e1a8 100644
--- a/hips/draw/ui.py
+++ b/hips/draw/ui.py
@@ -15,7 +15,7 @@ __all__ = [


 def make_sky_image(geometry: Union[dict, WCSGeometry], hips_survey: Union[str, 'HipsSurveyProperties'],
-                   tile_format: str, precise: bool = False, progress_bar: bool = True) -> 'HipsDrawResult':
+                   tile_format: str, precise: bool = False, progress_bar: bool = True, fetch_package: str = 'urllib') -> 'HipsDrawResult':
     """Make sky image: fetch tiles and draw.

     The example for this can be found on the :ref:`gs` page.
@@ -33,13 +33,15 @@ def make_sky_image(geometry: Union[dict, WCSGeometry], hips_survey: Union[str, '
         Use the precise drawing algorithm
     progress_bar : bool
         Show a progress bar for tile fetching and drawing
+    fetch_package : {'urllib', 'aiohttp'}
+        Package to use for fetching HiPS tiles

     Returns
     -------
     result : `~hips.HipsDrawResult`
         Result object
     """
-    painter = HipsPainter(geometry, hips_survey, tile_format, precise, progress_bar)
+    painter = HipsPainter(geometry, hips_survey, tile_format, precise, progress_bar, fetch_package)
     painter.run()
     return HipsDrawResult.from_painter(painter)

diff --git a/hips/tiles/fetch.py b/hips/tiles/fetch.py
index fe5c9ce..a776ec3 100644
--- a/hips/tiles/fetch.py
+++ b/hips/tiles/fetch.py
@@ -1,118 +1,113 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
-import aiohttp
-import asyncio
+import numpy as np
 import urllib.request
 import concurrent.futures
-import numpy as np
-from typing import Generator
+from typing import Generator, List
 from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta

 __all__ = [
     'HipsTileFetcher',
 ]

-__doctest_skip__ = [
-    'HipsTileFetcher',
-]
-

 class HipsTileFetcher:
     """Fetch a list of HiPS tiles.

     Parameters
     ----------
-    geometry : dict or `~hips.utils.WCSGeometry`
-        An object of WCSGeometry
-    hips_survey : str or `~hips.HipsSurveyProperties`
+    tile_metas : List[HipsTileMeta]
+        Python list of HipsTileMeta
+    hips_survey : `~hips.HipsSurveyProperties`
         HiPS survey properties
-    tile_format : {'fits', 'jpg', 'png'}
-        Format of HiPS tile
-    precise : bool
-        Use the precise drawing algorithm
+    progress_bar : bool
+        Show a progress bar for tile fetching and drawing
+    n_parallel : int
+        Number of threads to use for fetching HiPS tiles
+    timeout : int
+        Seconds to timeout for fetching a HiPS tile
+    fetch_package : {'urllib', 'aiohttp'}
+        Package to use for fetching HiPS tiles
     """

-    def __init__(self, tile_indices: np.ndarray, hips_order: int, hips_survey: HipsSurveyProperties, tile_format: str,
-                 progress_bar: bool, use_aiohttp: bool) -> None:
-        self.tile_indices = tile_indices
-        self.hips_order = hips_order
+    def __init__(self, tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperties,
+                 progress_bar: bool = False, n_parallel: int = 10,  timeout: int = 10, fetch_package : str = 'urllib') -> None:
+        self.tile_metas = tile_metas
         self.hips_survey = hips_survey
-        self.tile_format = tile_format
+        self.progress_bar = progress_bar
+        self.n_parallel = n_parallel
+        self.timeout = timeout
+        self.fetch_package = fetch_package

-    def fetch_tile_threaded(self, url: str, session: aiohttp.client.ClientSession) -> Generator:
+    @property
+    def tile_urls(self) -> List[str]:
+        """List of tile URLs"""
+        tile_urls = []
+        for meta in self.tile_metas:
+            tile_urls.append(self.hips_survey.tile_url(meta))
+
+        return tile_urls
+
+    @property
+    def tiles(self):
+        if self.fetch_package == 'aiohttp':
+            return self.tiles_aiohttp
+        elif self.fetch_package == 'urllib':
+            return self.tiles_urllib
+        else:
+            raise ValueError(f'Invalid package name: {self.fetch_package}')
+
+    def fetch_tile_urllib(self, url: str, meta : HipsTileMeta) -> Generator:
         """Fetch a HiPS tile asynchronously."""
-        with urllib.request.urlopen(url, timeout=60) as conn:
-            return conn.read()
+        with urllib.request.urlopen(url, timeout=self.timeout) as conn:
+            raw_data = conn.read()
+            return HipsTile(meta, raw_data)

     @property
-    def tiles(self) -> np.ndarray:
+    def tiles_urllib(self) -> np.ndarray:
         """Generator function to fetch HiPS tiles from a remote URL."""
-        tile_urls, tile_metas = [], []
-        for healpix_pixel_index in self.tile_indices:
-            tile_meta = HipsTileMeta(
-                order=self.hips_order,
-                ipix=healpix_pixel_index,
-                frame=self.hips_survey.astropy_frame,
-                file_format=self.tile_format,
-            )
-            tile_urls.append(self.hips_survey.tile_url(tile_meta))
-            tile_metas.append(tile_meta)
-
-        raw_responses = []
-        with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
-            # Start the load operations and mark each future with its URL
-            future_to_url = {executor.submit(self.fetch_tile_threaded, url, 60): url for url in tile_urls}
-            for future in concurrent.futures.as_completed(future_to_url):
-                url = future_to_url[future]
-                # try:
-                raw_responses.append(future.result())
-                # except Exception as exc:
-                #     print('%r generated an exception: %s' % (url, exc))
-                # else:
-                #     print('%r page is %d bytes' % (url, len(future.result())))
-
-
-        #
-        # tasks = []
-        # async with aiohttp.ClientSession() as session:
-        #     for idx, url in enumerate(tile_urls):
-        #         task = asyncio.ensure_future(self.fetch_tile_threaded(url.format(idx), session))
-        #         tasks.append(task)
-        #
-        #     raw_responses = await asyncio.gather(*tasks)
-        #
-        tiles = []
-        for idx, raw_data in enumerate(raw_responses):
-            tiles.append(HipsTile(tile_metas[idx], raw_data))
+        with concurrent.futures.ThreadPoolExecutor(max_workers=self.n_parallel) as executor:
+            future_to_url = {executor.submit(self.fetch_tile_urllib, url, self.tile_metas[idx]) : url for idx, url in enumerate(self.tile_urls)}
+
+            if self.progress_bar:
+                from tqdm import tqdm
+                requests = tqdm(concurrent.futures.as_completed(future_to_url), total=len(future_to_url), desc='Fetching tiles')
+            else:
+                requests = future_to_url#concurrent.futures.as_completed(future_to_url)
+
+            tiles = []
+            for future in requests:
+                tiles.append(future.result())
+
+        return tiles
+
+    async def fetch_tile_aiohttp(self, url: str, meta : HipsTileMeta, session) -> Generator:
+        """Fetch a HiPS tile asynchronously using aiohttp."""
+        async with session.get(url) as response:
+            raw_data = await response.read()
+            return HipsTile(meta, raw_data)
+
+    @property
+    async def fetch_all_tiles_aiohttp(self) -> np.ndarray:
+        """Generator function to fetch HiPS tiles from a remote URL using aiohttp."""
+        import aiohttp, asyncio
+
+        tasks = []
+        async with aiohttp.ClientSession() as session:
+            for idx, url in enumerate(self.tile_urls):
+                task = asyncio.ensure_future(self.fetch_tile_aiohttp(url.format(idx), self.tile_metas[idx], session))
+                tasks.append(task)
+
+            if self.progress_bar:
+                from tqdm import tqdm
+                tiles = []
+                for f in tqdm(tasks, total=len(tasks), desc='Fetching tiles'):
+                    tiles.append(await f)
+            else:
+                tiles = await asyncio.gather(*tasks)
+
         return tiles

-    # async def fetch_tile_threaded(self, url: str, session: aiohttp.client.ClientSession) -> Generator:
-    #     """Fetch a HiPS tile asynchronously."""
-    #     async with session.get(url) as response:
-    #         return await response.read()
-    #
-    # @property
-    # async def tiles(self) -> np.ndarray:
-    #     """Generator function to fetch HiPS tiles from a remote URL."""
-    #     tile_urls, tile_metas = [], []
-    #     for healpix_pixel_index in self.tile_indices:
-    #         tile_meta = HipsTileMeta(
-    #             order=self.hips_order,
-    #             ipix=healpix_pixel_index,
-    #             frame=self.hips_survey.astropy_frame,
-    #             file_format=self.tile_format,
-    #         )
-    #         tile_urls.append(self.hips_survey.tile_url(tile_meta))
-    #         tile_metas.append(tile_meta)
-    #
-    #     tasks = []
-    #     async with aiohttp.ClientSession() as session:
-    #         for idx, url in enumerate(tile_urls):
-    #             task = asyncio.ensure_future(self.fetch_tile_threaded(url.format(idx), session))
-    #             tasks.append(task)
-    #
-    #         raw_responses = await asyncio.gather(*tasks)
-    #
-    #     tiles = []
-    #     for idx, raw_data in enumerate(raw_responses):
-    #         tiles.append(HipsTile(tile_metas[idx], raw_data))
-    #     return tiles
\ No newline at end of file
+    @property
+    def tiles_aiohttp(self) -> np.ndarray:
+        import asyncio
+        return asyncio.get_event_loop().run_until_complete(self.fetch_all_tiles_aiohttp)
diff --git a/hips/tiles/tests/test_fetch.py b/hips/tiles/tests/test_fetch.py
new file mode 100644
index 0000000..d7956df
--- /dev/null
+++ b/hips/tiles/tests/test_fetch.py
@@ -0,0 +1,34 @@
+# Licensed under a 3-clause BSD style license - see LICENSE.rst
+from astropy.tests.helper import remote_data
+from numpy.testing import assert_allclose
+from ..fetch import HipsTileFetcher
+from ..survey import HipsSurveyProperties
+from ..tile import HipsTileMeta
+
+class TestHipsTileFetcher:
+    @classmethod
+    def setup_class(cls):
+        url = 'http://alasky.unistra.fr/DSS/DSS2Merged/properties'
+        hips_survey = HipsSurveyProperties.fetch(url)
+
+        tile_metas, tile_indices = [], [69623, 69627, 69628, 69629, 69630, 69631]
+        for healpix_pixel_index in tile_indices:
+            tile_meta = HipsTileMeta(
+                order=7,
+                ipix=healpix_pixel_index,
+                frame=hips_survey.astropy_frame,
+                file_format='fits',
+            )
+            tile_metas.append(tile_meta)
+
+        cls.fetcher = HipsTileFetcher(tile_metas, hips_survey, progress_bar=False)
+
+    @remote_data
+    def test_tiles(self):
+        tiles = self.fetcher.tiles
+        assert_allclose(tiles[0].data[0][5:10], [2101, 1680, 1532, 1625, 2131])
+
+    @remote_data
+    def test_tiles_aiohttp(self):
+        tiles = self.fetcher.tiles_aiohttp
+        assert_allclose(tiles[0].data[0][5:10], [2101, 1680, 1532, 1625, 2131])

commit 0eba105e1822e0e50645344e1839e8ec48d5d6de
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Thu Aug 17 19:07:57 2017 +0500

    Add option to fetch tiles asynchronously using urllib

diff --git a/hips/draw/paint.py b/hips/draw/paint.py
index 4a33100..07affb9 100644
--- a/hips/draw/paint.py
+++ b/hips/draw/paint.py
@@ -6,7 +6,7 @@ import numpy as np
 from typing import List, Tuple, Union, Dict, Any, Generator
 from astropy.wcs.utils import proj_plane_pixel_scales
 from skimage.transform import ProjectiveTransform, warp
-from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta
+from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta, HipsTileFetcher
 from ..tiles.tile import compute_image_shape
 from ..utils import WCSGeometry, healpix_pixels_in_sky_image, hips_order_for_pixel_resolution

@@ -153,7 +153,8 @@ class HipsPainter:
     def tiles(self) -> List[HipsTile]:
         """List of `~hips.HipsTile` (cached on multiple access)."""
         if self._tiles is None:
-            self._tiles = asyncio.get_event_loop().run_until_complete(self._fetch_tiles())
+            # self._tiles = asyncio.get_event_loop().run_until_complete(tile_fetcher.tiles)
+            self._tiles = tile_fetcher.tiles

         return self._tiles

diff --git a/hips/tiles/__init__.py b/hips/tiles/__init__.py
index bc0ac4a..0f5268e 100644
--- a/hips/tiles/__init__.py
+++ b/hips/tiles/__init__.py
@@ -3,3 +3,4 @@
 from .tile import *
 from .survey import *
 from .allsky import *
+from .fetch import *
diff --git a/hips/tiles/fetch.py b/hips/tiles/fetch.py
new file mode 100644
index 0000000..fe5c9ce
--- /dev/null
+++ b/hips/tiles/fetch.py
@@ -0,0 +1,118 @@
+# Licensed under a 3-clause BSD style license - see LICENSE.rst
+import aiohttp
+import asyncio
+import urllib.request
+import concurrent.futures
+import numpy as np
+from typing import Generator
+from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta
+
+__all__ = [
+    'HipsTileFetcher',
+]
+
+__doctest_skip__ = [
+    'HipsTileFetcher',
+]
+
+
+class HipsTileFetcher:
+    """Fetch a list of HiPS tiles.
+
+    Parameters
+    ----------
+    geometry : dict or `~hips.utils.WCSGeometry`
+        An object of WCSGeometry
+    hips_survey : str or `~hips.HipsSurveyProperties`
+        HiPS survey properties
+    tile_format : {'fits', 'jpg', 'png'}
+        Format of HiPS tile
+    precise : bool
+        Use the precise drawing algorithm
+    """
+
+    def __init__(self, tile_indices: np.ndarray, hips_order: int, hips_survey: HipsSurveyProperties, tile_format: str,
+                 progress_bar: bool, use_aiohttp: bool) -> None:
+        self.tile_indices = tile_indices
+        self.hips_order = hips_order
+        self.hips_survey = hips_survey
+        self.tile_format = tile_format
+
+    def fetch_tile_threaded(self, url: str, session: aiohttp.client.ClientSession) -> Generator:
+        """Fetch a HiPS tile asynchronously."""
+        with urllib.request.urlopen(url, timeout=60) as conn:
+            return conn.read()
+
+    @property
+    def tiles(self) -> np.ndarray:
+        """Generator function to fetch HiPS tiles from a remote URL."""
+        tile_urls, tile_metas = [], []
+        for healpix_pixel_index in self.tile_indices:
+            tile_meta = HipsTileMeta(
+                order=self.hips_order,
+                ipix=healpix_pixel_index,
+                frame=self.hips_survey.astropy_frame,
+                file_format=self.tile_format,
+            )
+            tile_urls.append(self.hips_survey.tile_url(tile_meta))
+            tile_metas.append(tile_meta)
+
+        raw_responses = []
+        with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
+            # Start the load operations and mark each future with its URL
+            future_to_url = {executor.submit(self.fetch_tile_threaded, url, 60): url for url in tile_urls}
+            for future in concurrent.futures.as_completed(future_to_url):
+                url = future_to_url[future]
+                # try:
+                raw_responses.append(future.result())
+                # except Exception as exc:
+                #     print('%r generated an exception: %s' % (url, exc))
+                # else:
+                #     print('%r page is %d bytes' % (url, len(future.result())))
+
+
+        #
+        # tasks = []
+        # async with aiohttp.ClientSession() as session:
+        #     for idx, url in enumerate(tile_urls):
+        #         task = asyncio.ensure_future(self.fetch_tile_threaded(url.format(idx), session))
+        #         tasks.append(task)
+        #
+        #     raw_responses = await asyncio.gather(*tasks)
+        #
+        tiles = []
+        for idx, raw_data in enumerate(raw_responses):
+            tiles.append(HipsTile(tile_metas[idx], raw_data))
+        return tiles
+
+    # async def fetch_tile_threaded(self, url: str, session: aiohttp.client.ClientSession) -> Generator:
+    #     """Fetch a HiPS tile asynchronously."""
+    #     async with session.get(url) as response:
+    #         return await response.read()
+    #
+    # @property
+    # async def tiles(self) -> np.ndarray:
+    #     """Generator function to fetch HiPS tiles from a remote URL."""
+    #     tile_urls, tile_metas = [], []
+    #     for healpix_pixel_index in self.tile_indices:
+    #         tile_meta = HipsTileMeta(
+    #             order=self.hips_order,
+    #             ipix=healpix_pixel_index,
+    #             frame=self.hips_survey.astropy_frame,
+    #             file_format=self.tile_format,
+    #         )
+    #         tile_urls.append(self.hips_survey.tile_url(tile_meta))
+    #         tile_metas.append(tile_meta)
+    #
+    #     tasks = []
+    #     async with aiohttp.ClientSession() as session:
+    #         for idx, url in enumerate(tile_urls):
+    #             task = asyncio.ensure_future(self.fetch_tile_threaded(url.format(idx), session))
+    #             tasks.append(task)
+    #
+    #         raw_responses = await asyncio.gather(*tasks)
+    #
+    #     tiles = []
+    #     for idx, raw_data in enumerate(raw_responses):
+    #         tiles.append(HipsTile(tile_metas[idx], raw_data))
+    #     return tiles
\ No newline at end of file

commit 6c1295814affecd4abe5e2c6e44e22df24ae3ae6
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Tue Aug 15 21:28:00 2017 +0500

    Introduce asynchronous fetching of HiPS tiles

diff --git a/hips/draw/paint.py b/hips/draw/paint.py
index 482b729..4a33100 100644
--- a/hips/draw/paint.py
+++ b/hips/draw/paint.py
@@ -1,7 +1,9 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 import time
+import aiohttp
+import asyncio
 import numpy as np
-from typing import List, Tuple, Union, Dict, Any, Iterator
+from typing import List, Tuple, Union, Dict, Any, Generator
 from astropy.wcs.utils import proj_plane_pixel_scales
 from skimage.transform import ProjectiveTransform, warp
 from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta
@@ -109,7 +111,12 @@ class HipsPainter:
         pt.estimate(src, dst)
         return pt

-    def _fetch_tiles(self) -> Iterator[HipsTile]:
+    async def fetch_tile_threaded(self, url: str, session: aiohttp.client.ClientSession) -> Generator:
+        """Fetch a HiPS tile asynchronously."""
+        async with session.get(url) as response:
+            return await response.read()
+
+    async def _fetch_tiles(self) -> List[HipsTile]:
         """Generator function to fetch HiPS tiles from a remote URL."""
         if self.progress_bar:
             from tqdm import tqdm
@@ -118,21 +125,35 @@ class HipsPainter:
             tile_indices = self.tile_indices

         for healpix_pixel_index in tile_indices:
+        tile_urls, tile_metas = [], []
+        for healpix_pixel_index in self.tile_indices:
             tile_meta = HipsTileMeta(
                 order=self.draw_hips_order,
                 ipix=healpix_pixel_index,
                 frame=self.hips_survey.astropy_frame,
                 file_format=self.tile_format,
             )
-            url = self.hips_survey.tile_url(tile_meta)
-            tile = HipsTile.fetch(tile_meta, url)
-            yield tile
+            tile_urls.append(self.hips_survey.tile_url(tile_meta))
+            tile_metas.append(tile_meta)
+
+        tasks = []
+        async with aiohttp.ClientSession() as session:
+            for idx, url in enumerate(tile_urls):
+                task = asyncio.ensure_future(self.fetch_tile_threaded(url.format(idx), session))
+                tasks.append(task)
+
+            raw_responses = await asyncio.gather(*tasks)
+
+        tiles = []
+        for idx, raw_data in enumerate(raw_responses):
+            tiles.append(HipsTile(tile_metas[idx], raw_data))
+        return tiles

     @property
     def tiles(self) -> List[HipsTile]:
         """List of `~hips.HipsTile` (cached on multiple access)."""
         if self._tiles is None:
-            self._tiles = list(self._fetch_tiles())
+            self._tiles = asyncio.get_event_loop().run_until_complete(self._fetch_tiles())

         return self._tiles

@@ -163,7 +184,6 @@ class HipsPainter:
             self._stats['consumed_memory'] += len(tile.raw_data)


-
     def make_tile_list(self):
         parent_tiles = self.tiles


commit 25dd9ef7f46e21876968ec784a93f2c14df9c0c4
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Wed Aug 16 22:48:31 2017 +0500

    Update .travis.yml

diff --git a/.travis.yml b/.travis.yml
index ebe7b8a..76fea2b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -74,7 +74,7 @@ matrix:
         # Run static code analysis
         - os: linux
           env: MAIN_CMD='make code-analysis' SETUP_CMD=''
-               PIP_DEPENDENCIES='mypy pycodestyle'
+               PIP_DEPENDENCIES='mypy pycodestyle tqdm'


 install:
diff --git a/setup.py b/setup.py
index 3678fa8..4e0cf5d 100755
--- a/setup.py
+++ b/setup.py
@@ -105,6 +105,7 @@ install_requires = [
 extras_require = dict(
     recommended=[
         'matplotlib>=2.0',
+
         'tqdm>=4.15',
         'reproject>=0.3.1',
     ],
@@ -113,6 +114,7 @@ extras_require = dict(
         'reproject>=0.3.1',
         'pytest>=3.0',
         'mypy>=0.501',
+        'tqdm>=4.15',
     ],
 )


commit 88f4293f12ba0173bc86ad6e39223b5aff04e4ae
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Wed Aug 16 19:33:54 2017 +0500

    Update getting started example to contain information on progress bar

diff --git a/docs/getting_started.rst b/docs/getting_started.rst
index c308ea1..3ce3d93 100644
--- a/docs/getting_started.rst
+++ b/docs/getting_started.rst
@@ -33,7 +33,10 @@ To draw a sky image from HiPS image tiles with the `hips` package, follow the fo
     hips_survey = 'CDS/P/DSS2/red'

 3. Call the `~hips.make_sky_image` function to fetch the HiPS data
-   and draw it, returning an object of `~hips.HipsDrawResult`::
+   and draw it, returning an object of `~hips.HipsDrawResult`.
+   By default a progress bar is shown for fetching and
+   drawing HiPS tiles. For batch processing, this can be
+   turned off by passing a keyword argument: `progress_bar=False`::

     from hips import make_sky_image
     result = make_sky_image(geometry, hips_survey, 'fits')
diff --git a/docs/installation.rst b/docs/installation.rst
index df4bcda..a1dbe1b 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -79,5 +79,6 @@ The ``hips`` package has the following requirements:
 In addition, the following packages are needed for optional functionality:

 * `Matplotlib`_ 2.0 or later. Used for plotting in examples.
+* `tqdm`_ 4.15 or later. Used for showing progress bar either on terminal or in Jupyter notebook.

 We have some info at :ref:`py3` on why we don't support legacy Python (Python 2).
\ No newline at end of file
diff --git a/docs/references.txt b/docs/references.txt
index cfe3062..9230626 100644
--- a/docs/references.txt
+++ b/docs/references.txt
@@ -5,4 +5,5 @@
 .. _Pillow: https://python-pillow.org/
 .. _HiPS paper: https://www.aanda.org/articles/aa/pdf/2015/06/aa26075-15.pdf
 .. _HiPS IVOA recommendation: http://www.ivoa.net/documents/HiPS/
-.. _HiPS at CDS: http://aladin.u-strasbg.fr/hips/
\ No newline at end of file
+.. _HiPS at CDS: http://aladin.u-strasbg.fr/hips/
+.. _tqdm: https://pypi.python.org/pypi/tqdm
\ No newline at end of file
diff --git a/hips/draw/paint.py b/hips/draw/paint.py
index 18848df..482b729 100644
--- a/hips/draw/paint.py
+++ b/hips/draw/paint.py
@@ -59,7 +59,7 @@ class HipsPainter:
     """

     def __init__(self, geometry: Union[dict, WCSGeometry], hips_survey: Union[str, HipsSurveyProperties],
-                 tile_format: str, precise: bool = False, progress_bar: bool = False) -> None:
+                 tile_format: str, precise: bool = False, progress_bar: bool = True) -> None:
         self.geometry = WCSGeometry.make(geometry)
         self.hips_survey = HipsSurveyProperties.make(hips_survey)
         self.tile_format = tile_format
@@ -113,7 +113,7 @@ class HipsPainter:
         """Generator function to fetch HiPS tiles from a remote URL."""
         if self.progress_bar:
             from tqdm import tqdm
-            tile_indices = tqdm(self.tile_indices, desc='Fetching tiles', disable=not self.progress_bar)
+            tile_indices = tqdm(self.tile_indices, desc='Fetching tiles')
         else:
             tile_indices = self.tile_indices

@@ -196,7 +196,7 @@ class HipsPainter:
         image = self._make_empty_sky_image()
         if self.progress_bar:
             from tqdm import tqdm
-            tiles = tqdm(self.draw_tiles, desc='Drawing tiles', disable=not self.progress_bar)
+            tiles = tqdm(self.draw_tiles, desc='Drawing tiles')
         else:
             tiles = self.draw_tiles

diff --git a/hips/draw/ui.py b/hips/draw/ui.py
index b608ac9..47716ea 100644
--- a/hips/draw/ui.py
+++ b/hips/draw/ui.py
@@ -15,7 +15,7 @@ __all__ = [


 def make_sky_image(geometry: Union[dict, WCSGeometry], hips_survey: Union[str, 'HipsSurveyProperties'],
-                   tile_format: str, precise: bool = False, progress_bar: bool = False) -> 'HipsDrawResult':
+                   tile_format: str, precise: bool = False, progress_bar: bool = True) -> 'HipsDrawResult':
     """Make sky image: fetch tiles and draw.

     The example for this can be found on the :ref:`gs` page.
diff --git a/setup.py b/setup.py
index c517001..3678fa8 100755
--- a/setup.py
+++ b/setup.py
@@ -105,6 +105,7 @@ install_requires = [
 extras_require = dict(
     recommended=[
         'matplotlib>=2.0',
+        'tqdm>=4.15',
         'reproject>=0.3.1',
     ],
     develop=[

commit e2c9c69989072fffb6271b43273fee65d186e274
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Wed Aug 16 18:15:08 2017 +0500

    Make tqdm an optional dependency

diff --git a/hips/draw/paint.py b/hips/draw/paint.py
index d227364..18848df 100644
--- a/hips/draw/paint.py
+++ b/hips/draw/paint.py
@@ -1,7 +1,6 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 import time
 import numpy as np
-from tqdm import tqdm
 from typing import List, Tuple, Union, Dict, Any, Iterator
 from astropy.wcs.utils import proj_plane_pixel_scales
 from skimage.transform import ProjectiveTransform, warp
@@ -112,7 +111,13 @@ class HipsPainter:

     def _fetch_tiles(self) -> Iterator[HipsTile]:
         """Generator function to fetch HiPS tiles from a remote URL."""
-        for healpix_pixel_index in tqdm(self.tile_indices, desc='Fetching tiles', disable=not self.progress_bar):
+        if self.progress_bar:
+            from tqdm import tqdm
+            tile_indices = tqdm(self.tile_indices, desc='Fetching tiles', disable=not self.progress_bar)
+        else:
+            tile_indices = self.tile_indices
+
+        for healpix_pixel_index in tile_indices:
             tile_meta = HipsTileMeta(
                 order=self.draw_hips_order,
                 ipix=healpix_pixel_index,
@@ -189,8 +194,13 @@ class HipsPainter:
     def draw_all_tiles(self):
         """Make an empty sky image and draw all the tiles."""
         image = self._make_empty_sky_image()
+        if self.progress_bar:
+            from tqdm import tqdm
+            tiles = tqdm(self.draw_tiles, desc='Drawing tiles', disable=not self.progress_bar)
+        else:
+            tiles = self.draw_tiles

-        for tile in tqdm(self.draw_tiles, desc='Drawing tiles', disable=not self.progress_bar):
+        for tile in tiles:
             tile_image = self.warp_image(tile)
             # TODO: put better algorithm here instead of summing pixels
             # this can lead to pixels that are painted twice and become to bright

commit ba813ef65fe503bb879927fd25f2975c1a735176
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Aug 11 18:48:03 2017 +0500

    Add progress bar support for fetching and drawing HiPS tiles

diff --git a/hips/draw/paint.py b/hips/draw/paint.py
index f0120c6..d227364 100644
--- a/hips/draw/paint.py
+++ b/hips/draw/paint.py
@@ -1,6 +1,7 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 import time
 import numpy as np
+from tqdm import tqdm
 from typing import List, Tuple, Union, Dict, Any, Iterator
 from astropy.wcs.utils import proj_plane_pixel_scales
 from skimage.transform import ProjectiveTransform, warp
@@ -34,6 +35,8 @@ class HipsPainter:
         Format of HiPS tile
     precise : bool
         Use the precise drawing algorithm
+    progress_bar : bool
+        Show a progress bar for tile fetching and drawing

     Examples
     --------
@@ -56,11 +59,13 @@ class HipsPainter:
     (1000, 2000)
     """

-    def __init__(self, geometry: Union[dict, WCSGeometry], hips_survey: Union[str, HipsSurveyProperties], tile_format: str, precise: bool = False) -> None:
+    def __init__(self, geometry: Union[dict, WCSGeometry], hips_survey: Union[str, HipsSurveyProperties],
+                 tile_format: str, precise: bool = False, progress_bar: bool = False) -> None:
         self.geometry = WCSGeometry.make(geometry)
         self.hips_survey = HipsSurveyProperties.make(hips_survey)
         self.tile_format = tile_format
         self.precise = precise
+        self.progress_bar = progress_bar
         self._tiles = None
         self.float_image = None
         self._stats: Dict[str, Any] = {}
@@ -107,7 +112,7 @@ class HipsPainter:

     def _fetch_tiles(self) -> Iterator[HipsTile]:
         """Generator function to fetch HiPS tiles from a remote URL."""
-        for healpix_pixel_index in self.tile_indices:
+        for healpix_pixel_index in tqdm(self.tile_indices, desc='Fetching tiles', disable=not self.progress_bar):
             tile_meta = HipsTileMeta(
                 order=self.draw_hips_order,
                 ipix=healpix_pixel_index,
@@ -121,12 +126,6 @@ class HipsPainter:
     @property
     def tiles(self) -> List[HipsTile]:
         """List of `~hips.HipsTile` (cached on multiple access)."""
-        # TODO: add progress bar reporting here???
-        # Do it in a separate pull request
-        # It has to work in the terminal and Jupyter notebook
-        # Users have to be able to turn it off
-        # So you have an option for it.
-        # Maybe if you add it now, make it off by default.
         if self._tiles is None:
             self._tiles = list(self._fetch_tiles())

@@ -159,6 +158,7 @@ class HipsPainter:
             self._stats['consumed_memory'] += len(tile.raw_data)


+
     def make_tile_list(self):
         parent_tiles = self.tiles

@@ -190,9 +190,7 @@ class HipsPainter:
         """Make an empty sky image and draw all the tiles."""
         image = self._make_empty_sky_image()

-        # TODO: this is the second place where we should add
-        # progress reporting
-        for tile in self.draw_tiles:
+        for tile in tqdm(self.draw_tiles, desc='Drawing tiles', disable=not self.progress_bar):
             tile_image = self.warp_image(tile)
             # TODO: put better algorithm here instead of summing pixels
             # this can lead to pixels that are painted twice and become to bright
diff --git a/hips/draw/ui.py b/hips/draw/ui.py
index fb56b4e..b608ac9 100644
--- a/hips/draw/ui.py
+++ b/hips/draw/ui.py
@@ -15,7 +15,7 @@ __all__ = [


 def make_sky_image(geometry: Union[dict, WCSGeometry], hips_survey: Union[str, 'HipsSurveyProperties'],
-                   tile_format: str, precise: bool = False) -> 'HipsDrawResult':
+                   tile_format: str, precise: bool = False, progress_bar: bool = False) -> 'HipsDrawResult':
     """Make sky image: fetch tiles and draw.

     The example for this can be found on the :ref:`gs` page.
@@ -31,13 +31,15 @@ def make_sky_image(geometry: Union[dict, WCSGeometry], hips_survey: Union[str, '
         (some surveys are available in several formats, so this extra argument is needed)
     precise : bool
         Use the precise drawing algorithm
+    progress_bar : bool
+        Show a progress bar for tile fetching and drawing

     Returns
     -------
     result : `~hips.HipsDrawResult`
         Result object
     """
-    painter = HipsPainter(geometry, hips_survey, tile_format, precise)
+    painter = HipsPainter(geometry, hips_survey, tile_format, precise, progress_bar)
     painter.run()
     return HipsDrawResult.from_painter(painter)


commit c026c96aa19e46f66a8e84a1c3429476c2803bb4
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Wed Aug 9 21:45:14 2017 +0500

    Add reporting functionality for HipsPainter

diff --git a/hips/draw/paint.py b/hips/draw/paint.py
index efaa251..142f4b8 100644
--- a/hips/draw/paint.py
+++ b/hips/draw/paint.py
@@ -153,13 +153,11 @@ class HipsPainter:
         t1 = time.time()
         self._stats['draw_time'] = t1 - t0

-        # Todo for Adeel: copy over the stats dir to the DrawResult
-        # and print it out nicely for the user in the report.
-        # TODO: could add more code here that e.g. tries to measure memory usage
-        # this could be done with https://pypi.python.org/pypi/psutil
-        # or you could just compute the size of `raw_data` and `data` for each tile and sum them up
-        # and also measure the memory used by the output sky image in MB
-        # Other useful info: number of tiles fetched
+        self._stats['tile_count'] = len(self.draw_tiles)
+        self._stats['consumed_memory'] = 0
+        for tile in self.draw_tiles:
+            self._stats['consumed_memory'] += len(tile.raw_data)
+

     def make_tile_list(self):
         parent_tiles = self.tiles
diff --git a/hips/draw/tests/test_ui.py b/hips/draw/tests/test_ui.py
index dc8238d..569e047 100644
--- a/hips/draw/tests/test_ui.py
+++ b/hips/draw/tests/test_ui.py
@@ -71,3 +71,4 @@ def test_make_sky_image(tmpdir, pars):
     assert_allclose(result.image[200, 995], pars['data_2'])
     result.write_image(str(tmpdir / 'test.' + pars['file_format']))
     result.plot()
+    result.report()
diff --git a/hips/draw/ui.py b/hips/draw/ui.py
index a7857c1..fb56b4e 100644
--- a/hips/draw/ui.py
+++ b/hips/draw/ui.py
@@ -55,13 +55,16 @@ class HipsDrawResult:
         Format of HiPS tile
     tiles : list
         Python list of `~hips.HipsTile` objects that were used
+    stats : dict
+        Information including time for fetching and drawing HiPS tiles
     """

-    def __init__(self, image: np.ndarray, geometry: WCSGeometry, tile_format: str, tiles: List[HipsTile]) -> None:
+    def __init__(self, image: np.ndarray, geometry: WCSGeometry, tile_format: str, tiles: List[HipsTile], stats: dict) -> None:
         self.image = image
         self.geometry = geometry
         self.tile_format = tile_format
         self.tiles = tiles
+        self.stats = stats

     def __str__(self):
         return (
@@ -89,6 +92,7 @@ class HipsDrawResult:
             geometry=painter.geometry,
             tile_format=painter.tile_format,
             tiles=painter.tiles,
+            stats=painter._stats,
         )

     def write_image(self, filename: str) -> None:
@@ -120,3 +124,13 @@ class HipsDrawResult:
             ax.plot(corners.data.lon.deg, corners.data.lat.deg,
                     transform=ax.get_transform('world'), **opts)
         ax.imshow(self.image, origin='lower')
+
+    def report(self) -> None:
+        """Print a brief report for the fetched data."""
+
+        print (
+            f"Time for fetching tiles = {self.stats['fetch_time']} seconds\n"
+            f"Time for drawing tiles = {self.stats['draw_time']} seconds\n"
+            f"Total memory consumed = {self.stats['consumed_memory'] / 1e6} MB\n"
+            f"Total tiles fetched = {self.stats['tile_count']}\n"
+        )

commit 0981f309aade1f33e0586341108dfff0fd108ab2
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Tue Aug 8 13:59:32 2017 +0500

    Reformat docstring for create_from_dict class method

diff --git a/hips/tiles/tests/test_allsky.py b/hips/tiles/tests/test_allsky.py
index d933938..625281a 100644
--- a/hips/tiles/tests/test_allsky.py
+++ b/hips/tiles/tests/test_allsky.py
@@ -11,7 +11,7 @@ TEST_CASES = [
     dict(
         label='fits',
         meta=dict(order=3, ipix=-1, file_format='fits'),
-        url='http://alasky.unistra.fr/IRAC4/Norder3/Allsky.fits',
+        url='https://github.com/hipspy/hips-extra/blob/master/datasets/samples/IRAC4/Norder3/Allsky.fits?raw=true',
         filename='datasets/samples/IRAC4/Norder3/Allsky.fits',

         repr="HipsTileAllskyArray(format='fits', order=3, width=1728, "
diff --git a/hips/utils/wcs.py b/hips/utils/wcs.py
index b052dec..5e6c240 100644
--- a/hips/utils/wcs.py
+++ b/hips/utils/wcs.py
@@ -98,7 +98,7 @@ class WCSGeometry:
             Sky coordinate of the WCS reference point
         width, height : int
             Width and height of the image in pixels
-        fov: str or `~astropy.coordinates.Angle`
+        fov : str or `~astropy.coordinates.Angle`
             Field of view
         coordsys : {'icrs', 'galactic'}
             Coordinate system
@@ -160,9 +160,20 @@ class WCSGeometry:

     @classmethod
     def create_from_dict(cls, params: dict) -> 'WCSGeometry':
-        """Create WCS object from a dictionary (`WCSGeometry`)."""
+        """Create WCS object from a dictionary (`WCSGeometry`).
+
+        The extra options are passed to the ``create`` class method, it can take the following parameters:
+
+        * target
+        * width
+        * height
+        * fov
+        * coordsys
+        * projection
+
+        For detailed description, see the ``create`` class method's docstring.
+        """
         skycoord = SkyCoord.from_name(params.pop('target'), frame=params['coordsys'])
-        # print(**params)
         return cls.create(skycoord, **params)

     @classmethod

commit 8140f2943de907bc6bd45f6a06d337b781dd6ff1
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Mon Aug 7 09:33:55 2017 +0500

    Fix tile splitting criterion

diff --git a/hips/draw/paint.py b/hips/draw/paint.py
index efaa251..9f647a8 100644
--- a/hips/draw/paint.py
+++ b/hips/draw/paint.py
@@ -260,8 +260,8 @@ def is_tile_distorted(corners: tuple) -> bool:

     return bool(
         max(edges) > 300 or
-        max(diagonals) > 150 or
-        diagonal_ratio < 0.7
+        (max(diagonals) > 150 and
+        diagonal_ratio < 0.7)
     )


diff --git a/hips/tiles/tests/test_allsky.py b/hips/tiles/tests/test_allsky.py
index d933938..625281a 100644
--- a/hips/tiles/tests/test_allsky.py
+++ b/hips/tiles/tests/test_allsky.py
@@ -11,7 +11,7 @@ TEST_CASES = [
     dict(
         label='fits',
         meta=dict(order=3, ipix=-1, file_format='fits'),
-        url='http://alasky.unistra.fr/IRAC4/Norder3/Allsky.fits',
+        url='https://github.com/hipspy/hips-extra/blob/master/datasets/samples/IRAC4/Norder3/Allsky.fits?raw=true',
         filename='datasets/samples/IRAC4/Norder3/Allsky.fits',

         repr="HipsTileAllskyArray(format='fits', order=3, width=1728, "
diff --git a/hips/utils/tests/test_wcs.py b/hips/utils/tests/test_wcs.py
index cbab50f..26cf46f 100644
--- a/hips/utils/tests/test_wcs.py
+++ b/hips/utils/tests/test_wcs.py
@@ -1,4 +1,5 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
+from astropy.tests.helper import remote_data
 from numpy.testing import assert_allclose
 from astropy.coordinates import SkyCoord
 from ..wcs import WCSGeometry
@@ -35,6 +36,7 @@ class TestWCSGeometry:
         assert_allclose(self.geometry.wcs.wcs.crpix, [1000., 500.])
         assert_allclose(self.geometry.wcs.wcs.cdelt, [-0.0015, 0.0015])

+    @remote_data
     def test_create_from_dict(self):
         params = dict(target='crab', width=2000, height=1000, fov='3 deg',
                       coordsys='galactic', projection='AIT')
diff --git a/hips/utils/wcs.py b/hips/utils/wcs.py
index b052dec..f47bb6b 100644
--- a/hips/utils/wcs.py
+++ b/hips/utils/wcs.py
@@ -98,7 +98,7 @@ class WCSGeometry:
             Sky coordinate of the WCS reference point
         width, height : int
             Width and height of the image in pixels
-        fov: str or `~astropy.coordinates.Angle`
+        fov : str or `~astropy.coordinates.Angle`
             Field of view
         coordsys : {'icrs', 'galactic'}
             Coordinate system
@@ -162,7 +162,6 @@ class WCSGeometry:
     def create_from_dict(cls, params: dict) -> 'WCSGeometry':
         """Create WCS object from a dictionary (`WCSGeometry`)."""
         skycoord = SkyCoord.from_name(params.pop('target'), frame=params['coordsys'])
-        # print(**params)
         return cls.create(skycoord, **params)

     @classmethod

commit b447864b37ea070f2b8b2662ac0797cc2f151c76
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Wed Aug 2 16:36:27 2017 +0500

    Update test case for create_from_dict classmethod

diff --git a/hips/utils/tests/test_wcs.py b/hips/utils/tests/test_wcs.py
index 7c72be6..cbab50f 100644
--- a/hips/utils/tests/test_wcs.py
+++ b/hips/utils/tests/test_wcs.py
@@ -40,5 +40,9 @@ class TestWCSGeometry:
                       coordsys='galactic', projection='AIT')
         geometry = WCSGeometry.create_from_dict(params)

-        c = self.geometry.center_skycoord
+        c = geometry.center_skycoord
         assert c.frame.name == 'galactic'
+        assert_allclose(c.l.deg, 184.55, atol=1e-2)
+        assert_allclose(c.b.deg, -5.78, atol=1e-2)
+        assert_allclose(self.geometry.wcs.wcs.crpix, [1000., 500.])
+        assert_allclose(self.geometry.wcs.wcs.cdelt, [-0.0015, 0.0015])

commit 651a5ae14a8896ca81558d9f084a9afe764bdd44
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Wed Aug 2 13:29:26 2017 +0500

    Add classmethod create_from_dict in WCSGeometry class

diff --git a/hips/draw/paint.py b/hips/draw/paint.py
index 7ea03b1..efaa251 100644
--- a/hips/draw/paint.py
+++ b/hips/draw/paint.py
@@ -26,7 +26,7 @@ class HipsPainter:

     Parameters
     ----------
-    geometry : `~hips.utils.WCSGeometry`
+    geometry : dict or `~hips.utils.WCSGeometry`
         An object of WCSGeometry
     hips_survey : str or `~hips.HipsSurveyProperties`
         HiPS survey properties
@@ -56,8 +56,8 @@ class HipsPainter:
     (1000, 2000)
     """

-    def __init__(self, geometry: WCSGeometry, hips_survey: Union[str, HipsSurveyProperties], tile_format: str, precise: bool = False) -> None:
-        self.geometry = geometry
+    def __init__(self, geometry: Union[dict, WCSGeometry], hips_survey: Union[str, HipsSurveyProperties], tile_format: str, precise: bool = False) -> None:
+        self.geometry = WCSGeometry.make(geometry)
         self.hips_survey = HipsSurveyProperties.make(hips_survey)
         self.tile_format = tile_format
         self.precise = precise
diff --git a/hips/draw/ui.py b/hips/draw/ui.py
index 8a1db36..a7857c1 100644
--- a/hips/draw/ui.py
+++ b/hips/draw/ui.py
@@ -14,7 +14,7 @@ __all__ = [
 ]


-def make_sky_image(geometry: WCSGeometry, hips_survey: Union[str, 'HipsSurveyProperties'],
+def make_sky_image(geometry: Union[dict, WCSGeometry], hips_survey: Union[str, 'HipsSurveyProperties'],
                    tile_format: str, precise: bool = False) -> 'HipsDrawResult':
     """Make sky image: fetch tiles and draw.

@@ -22,7 +22,7 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: Union[str, 'HipsSurveyPro

     Parameters
     ----------
-    geometry : `~hips.utils.WCSGeometry`
+    geometry : dict or `~hips.utils.WCSGeometry`
         Geometry of the output image
     hips_survey : str or `~hips.HipsSurveyProperties`
         HiPS survey properties
diff --git a/hips/utils/tests/test_wcs.py b/hips/utils/tests/test_wcs.py
index 22832f7..7c72be6 100644
--- a/hips/utils/tests/test_wcs.py
+++ b/hips/utils/tests/test_wcs.py
@@ -34,3 +34,11 @@ class TestWCSGeometry:
         assert_allclose(c.dec.deg, 0.00075, atol=1e-2)
         assert_allclose(self.geometry.wcs.wcs.crpix, [1000., 500.])
         assert_allclose(self.geometry.wcs.wcs.cdelt, [-0.0015, 0.0015])
+
+    def test_create_from_dict(self):
+        params = dict(target='crab', width=2000, height=1000, fov='3 deg',
+                      coordsys='galactic', projection='AIT')
+        geometry = WCSGeometry.create_from_dict(params)
+
+        c = self.geometry.center_skycoord
+        assert c.frame.name == 'galactic'
diff --git a/hips/utils/wcs.py b/hips/utils/wcs.py
index bf2574e..b052dec 100644
--- a/hips/utils/wcs.py
+++ b/hips/utils/wcs.py
@@ -158,6 +158,23 @@ class WCSGeometry:

         return cls(w, width, height)

+    @classmethod
+    def create_from_dict(cls, params: dict) -> 'WCSGeometry':
+        """Create WCS object from a dictionary (`WCSGeometry`)."""
+        skycoord = SkyCoord.from_name(params.pop('target'), frame=params['coordsys'])
+        # print(**params)
+        return cls.create(skycoord, **params)
+
+    @classmethod
+    def make(cls, geometry: Union[dict, 'WCSGeometry']) -> 'WCSGeometry':
+        """Convenience constructor for create_from_dict classmethod or existing object (`WCSGeometry`)."""
+        if isinstance(geometry, str):
+            return WCSGeometry.create_from_dict(geometry)
+        elif isinstance(geometry, WCSGeometry):
+            return geometry
+        else:
+            raise TypeError(f'geometry must be of type dict or `WCSGeometry`. You gave {type(geometry)}')
+
     @property
     def celestial_frame(self) -> str:
         """Celestial frame for the given WCS (str).

commit 1980881927671773de80051ea78b79bdac61a484
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Tue Aug 1 16:03:48 2017 +0500

    Introduce precise drawing algorithm

diff --git a/hips/draw/paint.py b/hips/draw/paint.py
index 8114b02..7ea03b1 100644
--- a/hips/draw/paint.py
+++ b/hips/draw/paint.py
@@ -17,8 +17,6 @@ __doctest_skip__ = [
 ]


-# TODO for Adeel: add option "precise".
-# Should be default "true" eventually, for now, can use "false" if it's very slow.
 class HipsPainter:
     """Paint a sky image from HiPS image tiles.

@@ -34,6 +32,8 @@ class HipsPainter:
         HiPS survey properties
     tile_format : {'fits', 'jpg', 'png'}
         Format of HiPS tile
+    precise : bool
+        Use the precise drawing algorithm

     Examples
     --------
@@ -56,10 +56,11 @@ class HipsPainter:
     (1000, 2000)
     """

-    def __init__(self, geometry: WCSGeometry, hips_survey: Union[str, HipsSurveyProperties], tile_format: str) -> None:
+    def __init__(self, geometry: WCSGeometry, hips_survey: Union[str, HipsSurveyProperties], tile_format: str, precise: bool = False) -> None:
         self.geometry = geometry
         self.hips_survey = HipsSurveyProperties.make(hips_survey)
         self.tile_format = tile_format
+        self.precise = precise
         self._tiles = None
         self.float_image = None
         self._stats: Dict[str, Any] = {}
@@ -161,21 +162,23 @@ class HipsPainter:
         # Other useful info: number of tiles fetched

     def make_tile_list(self):
-        # Fetch all tiles needed
         parent_tiles = self.tiles

-        # TODO for Adeel: implement distortion correction here.
-        # Create new list of tiles, where some have been replaced by 4 children.
         # Suggestion: for now, just split once, no recursion.
         # Leave TODO, to discuss with Thomas next week.
         # See also: https://github.com/hipspy/hips/issues/92

-        # For now, we just create a 1:1 copy
-        tiles = []
-        for tile in parent_tiles:
-            tiles.append(tile)
-
-        self.draw_tiles = tiles
+        if self.precise == True:
+            tiles = []
+            for tile in parent_tiles:
+                corners = tile.meta.skycoord_corners.to_pixel(self.geometry.wcs)
+                if is_tile_distorted(corners):
+                    tiles.append(tile.children)
+                else:
+                    tiles.append(tile)
+            self.draw_tiles = [tile for children in tiles for tile in children]
+        else:
+            self.draw_tiles = parent_tiles

     def _make_empty_sky_image(self):
         shape = compute_image_shape(
@@ -207,7 +210,8 @@ class HipsPainter:
         not something end-users will call or need to know about.
         """
         import matplotlib.pyplot as plt
-        for tile in self.tiles:
+        self.make_tile_list()
+        for tile in self.draw_tiles:
             corners = tile.meta.skycoord_corners.transform_to(self.geometry.celestial_frame)
             ax = plt.subplot(projection=self.geometry.wcs)
             opts = dict(color='red', lw=1, )
diff --git a/hips/draw/tests/test_ui.py b/hips/draw/tests/test_ui.py
index 401874c..dc8238d 100644
--- a/hips/draw/tests/test_ui.py
+++ b/hips/draw/tests/test_ui.py
@@ -16,7 +16,8 @@ make_sky_image_pars = [
         data_2=2296,
         data_sum=8756493140,
         dtype='>i2',
-        repr='HipsDrawResult(width=1000, height=2000, channels=2, dtype=>i2, format=fits)'
+        repr='HipsDrawResult(width=1000, height=2000, channels=2, dtype=>i2, format=fits)',
+        precise=False
     ),
     dict(
         file_format='jpg',
@@ -26,7 +27,19 @@ make_sky_image_pars = [
         data_2=[137, 116, 114],
         data_sum=828908873,
         dtype='uint8',
-        repr='HipsDrawResult(width=1000, height=2000, channels=3, dtype=uint8, format=jpg)'
+        repr='HipsDrawResult(width=1000, height=2000, channels=3, dtype=uint8, format=jpg)',
+        precise=False
+    ),
+    dict(
+        file_format='jpg',
+        shape=(1000, 2000, 3),
+        url='https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/FermiColor/properties',
+        data_1=[139, 114, 115],
+        data_2=[142, 113, 110],
+        data_sum=825148172,
+        dtype='uint8',
+        repr='HipsDrawResult(width=1000, height=2000, channels=3, dtype=uint8, format=jpg)',
+        precise=True
     ),
     dict(
         file_format='png',
@@ -36,7 +49,8 @@ make_sky_image_pars = [
         data_2=[227, 217, 205, 255],
         data_sum=1635622838,
         dtype='uint8',
-        repr='HipsDrawResult(width=1000, height=2000, channels=3, dtype=uint8, format=png)'
+        repr='HipsDrawResult(width=1000, height=2000, channels=3, dtype=uint8, format=png)',
+        precise=False
     ),
 ]

@@ -47,7 +61,7 @@ def test_make_sky_image(tmpdir, pars):
     hips_survey = HipsSurveyProperties.fetch(url=pars['url'])
     geometry = make_test_wcs_geometry()

-    result = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=pars['file_format'])
+    result = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=pars['file_format'], precise=pars['precise'])

     assert result.image.shape == pars['shape']
     assert result.image.dtype == pars['dtype']
diff --git a/hips/draw/ui.py b/hips/draw/ui.py
index 11facac..8a1db36 100644
--- a/hips/draw/ui.py
+++ b/hips/draw/ui.py
@@ -15,7 +15,7 @@ __all__ = [


 def make_sky_image(geometry: WCSGeometry, hips_survey: Union[str, 'HipsSurveyProperties'],
-                   tile_format: str) -> 'HipsDrawResult':
+                   tile_format: str, precise: bool = False) -> 'HipsDrawResult':
     """Make sky image: fetch tiles and draw.

     The example for this can be found on the :ref:`gs` page.
@@ -29,13 +29,15 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: Union[str, 'HipsSurveyPro
     tile_format : {'fits', 'jpg', 'png'}
         Format of HiPS tile to use
         (some surveys are available in several formats, so this extra argument is needed)
+    precise : bool
+        Use the precise drawing algorithm

     Returns
     -------
     result : `~hips.HipsDrawResult`
         Result object
     """
-    painter = HipsPainter(geometry, hips_survey, tile_format)
+    painter = HipsPainter(geometry, hips_survey, tile_format, precise)
     painter.run()
     return HipsDrawResult.from_painter(painter)

diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py
index eeef134..c2712f7 100644
--- a/hips/tiles/tests/test_tile.py
+++ b/hips/tiles/tests/test_tile.py
@@ -82,7 +82,7 @@ HIPS_TILE_TEST_CASES = [
         child_shape=(256, 256),
         child_ipix=[1852, 1853, 1854, 1855],
         child_pix_idx=[[255], [255]],
-        child_pix_val=[2153, 2418, 2437, 2124],
+        child_pix_val=[2437, 2153, 2124, 2418],
     ),
     dict(
         label='jpg',
@@ -99,7 +99,7 @@ HIPS_TILE_TEST_CASES = [
         child_shape=(256, 256, 3),
         child_ipix=[1852, 1853, 1854, 1855],
         child_pix_idx=[[255], [255]],
-        child_pix_val=[[[245, 214, 211]], [[137, 97, 87]], [[255, 241, 225]], [[109, 95, 86]]],
+        child_pix_val=[[[255, 241, 225]], [[245, 214, 211]], [[109, 95, 86]], [[137, 97, 87]]],
     ),
     dict(
         label='png',
@@ -116,7 +116,7 @@ HIPS_TILE_TEST_CASES = [
         child_shape=(256, 256, 4),
         child_ipix=[24448, 24449, 24450, 24451],
         child_pix_idx=[[255], [255]],
-        child_pix_val=[[[17, 17, 17, 255]], [[13, 13, 13, 255]], [[15, 15, 15, 255]], [[20, 20, 20, 255]]],
+        child_pix_val=[[[15,  15,  15, 255]], [[17, 17, 17, 255]], [[20, 20, 20, 255]], [[13, 13, 13, 255]]],
     ),
 ]

diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py
index eebaeb7..88ae6ca 100644
--- a/hips/tiles/tile.py
+++ b/hips/tiles/tile.py
@@ -231,10 +231,10 @@ class HipsTile:
         """Create four children tiles from parent tile."""
         w = self.data.shape[0] // 2
         data = [
-            self.data[0: w, 0: w],
-            self.data[0: w, w: w * 2],
             self.data[w: w * 2, 0: w],
-            self.data[w: w * 2, w: w * 2]
+            self.data[0: w, 0: w],
+            self.data[w: w * 2, w: w * 2],
+            self.data[0: w, w: w * 2]
         ]

         tiles = []
@@ -243,7 +243,8 @@ class HipsTile:
                 self.meta.order + 1,
                 self.meta.ipix * 4 + idx,
                 self.meta.file_format,
-                self.meta.frame
+                self.meta.frame,
+                len(data[0])
             )
             tile = self.from_numpy(meta, data[idx])
             tiles.append(tile)

commit 09906ec66668c829a9ca0375840e77090d50de2a
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Thu Jul 27 18:19:06 2017 +0500

    Add option to create HipsSurveyProperties from survey name

diff --git a/docs/getting_started.rst b/docs/getting_started.rst
index 88cdf06..c308ea1 100644
--- a/docs/getting_started.rst
+++ b/docs/getting_started.rst
@@ -26,21 +26,16 @@ To draw a sky image from HiPS image tiles with the `hips` package, follow the fo
          coordsys='galactic', projection='AIT',
     )

-
-2. Specify the HiPS survey you want by creating a `~hips.HipsSurveyProperties` object.
+2. Specify the HiPS survey you want. You just need to provide a valid HiPS survey ID.

    A good address that lists available HiPS data is http://aladin.u-strasbg.fr/hips/list ::

-    from hips import HipsSurveyProperties
-    url = 'http://alasky.unistra.fr/DSS/DSS2Merged/properties'
-    hips_survey = HipsSurveyProperties.fetch(url)
-
+    hips_survey = 'CDS/P/DSS2/red'

 3. Call the `~hips.make_sky_image` function to fetch the HiPS data
    and draw it, returning an object of `~hips.HipsDrawResult`::

     from hips import make_sky_image
-
     result = make_sky_image(geometry, hips_survey, 'fits')

 Of course, you could change the parameters to chose any sky image geometry and
diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index 5f06e9d..a8fe05f 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -4,7 +4,7 @@ import time
 import numpy as np
 from PIL import Image
 from astropy.io import fits
-from typing import List, Tuple
+from typing import List, Tuple, Union
 from astropy.wcs.utils import proj_plane_pixel_scales
 from skimage.transform import ProjectiveTransform, warp
 from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta
@@ -61,7 +61,7 @@ class HipsPainter:
     (1000, 2000)
     """

-    def __init__(self, geometry: WCSGeometry, hips_survey: HipsSurveyProperties, tile_format: str) -> None:
+    def __init__(self, geometry: WCSGeometry, hips_survey: Union[str, HipsSurveyProperties], tile_format: str) -> None:
         self.geometry = geometry
         self.hips_survey = HipsSurveyProperties.make(hips_survey)
         self.tile_format = tile_format
@@ -382,7 +382,7 @@ def plot_mpl_single_tile(geometry: WCSGeometry, tile: HipsTile, image: np.ndarra
     ax.imshow(image, origin='lower')


-def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties, tile_format: str) -> 'HipsDrawResult':
+def make_sky_image(geometry: WCSGeometry, hips_survey: Union[str, 'HipsSurveyProperties'], tile_format: str) -> 'HipsDrawResult':
     """Make sky image: fetch tiles and draw.

     The example for this can be found on the :ref:`gs` page.
@@ -399,8 +399,8 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties, til

     Returns
     -------
-    image : `~numpy.ndarray`
-        Output image pixels
+    result : `~hips.HipsDrawResult`
+        Result object
     """
     painter = HipsPainter(geometry, hips_survey, tile_format)
     painter.run()
diff --git a/hips/tiles/surveys.py b/hips/tiles/surveys.py
index 614ce1f..41165f4 100644
--- a/hips/tiles/surveys.py
+++ b/hips/tiles/surveys.py
@@ -3,7 +3,7 @@ from collections import OrderedDict
 from io import StringIO
 from csv import DictWriter
 import urllib.request
-from typing import List
+from typing import List, Union
 from astropy.table import Table
 from .tile import HipsTileMeta

@@ -44,33 +44,22 @@ class HipsSurveyProperties:
     def __init__(self, data: OrderedDict) -> None:
         self.data = data

-    # TODO for Adeel: implement this (add docstring & test)
     @classmethod
-    def from_name(cls, name):
-        """
-        """
+    def from_name(cls, name: str) -> 'HipsSurveyProperties':
+        """Create object from Survey ID (`HipsSurveyProperties`)."""
         # TODO: implement some kind of caching for HipsSurveyPropertiesList
-        # Also update the getting started example to use this simple solution.
-        # Discuss with Thomas how to do it.
-        # See https://github.com/hipspy/hips/issues/81
         surveys = HipsSurveyPropertiesList.fetch()
-        for survey in surveys.data:
-            if survey.data['ID'].strip() == name.strip():
-                return survey
-
-        raise KeyError(f'Survey not found: {name}')
+        return surveys.from_name(name)

     @classmethod
-    def make(cls, hips_survey):
-        """Convenience constructor from string or existing object."""
-        return hips_survey
-        # TODO for Adeel: Implement the `HipsSurveyProperties.from_name` and add a test for this lookup by name
-        # if isinstance(hips_survey, str):
-        #     hips_survey = HipsSurveyProperties.from_name(hips_survey)
-        # elif isinstance(hips_survey, HipsSurveyProperties):
-        #     pass
-        # else:
-        #     raise TypeError(f'hips_survey must be str or HipsSurveyProperties. You gave {type(hips_survey)}')
+    def make(cls, hips_survey: Union[str, 'HipsSurveyProperties']) -> 'HipsSurveyProperties':
+        """Convenience constructor for from_string classmethod or existing object (`HipsSurveyProperties`)."""
+        if isinstance(hips_survey, str):
+            return HipsSurveyProperties.from_name(hips_survey)
+        elif isinstance(hips_survey, HipsSurveyProperties):
+            return hips_survey
+        else:
+            raise TypeError(f'hips_survey must be of type str or `HipsSurveyProperties`. You gave {type(hips_survey)}')

     @classmethod
     def read(cls, filename: str) -> 'HipsSurveyProperties':
@@ -294,3 +283,11 @@ class HipsSurveyPropertiesList:
         writer.writeheader()
         writer.writerows(rows)
         return Table.read(buffer.getvalue(), format='ascii.csv', guess=False)
+
+    def from_name(self, name: str) -> 'HipsSurveyProperties':
+        """Return a matching HiPS survey (`HipsSurveyProperties`)."""
+        for survey in self.data:
+            if survey.data['ID'].strip() == name.strip():
+                return survey
+
+        raise KeyError(f'Survey not found: {name}')
diff --git a/hips/tiles/tests/test_surveys.py b/hips/tiles/tests/test_surveys.py
index b6d6a94..e8fb0be 100644
--- a/hips/tiles/tests/test_surveys.py
+++ b/hips/tiles/tests/test_surveys.py
@@ -1,4 +1,5 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
+import pytest
 from numpy.testing import assert_allclose
 from astropy.utils.data import get_pkg_data_filename
 from astropy.tests.helper import remote_data
@@ -11,33 +12,44 @@ class TestHipsSurveyProperties:
     @classmethod
     def setup_class(cls):
         filename = get_pkg_data_filename('data/properties.txt')
-        cls.hips_survey = HipsSurveyProperties.read(filename)
+        cls.survey = HipsSurveyProperties.read(filename)
+
+    @remote_data
+    def test_from_name(self):
+        survey = HipsSurveyProperties.from_name('CDS/P/2MASS/color')
+        assert survey.title == '2MASS color J (1.23 microns), H (1.66 microns), K (2.16 microns)'
+
+    @remote_data
+    def test_make(self):
+        survey = HipsSurveyProperties.make('CDS/P/EGRET/Dif/300-500')
+        assert survey.title == 'EGRET Dif 300-500'
+        assert self.survey is HipsSurveyProperties.make(self.survey)

     def test_title(self):
-        assert self.hips_survey.title == 'DSS colored'
+        assert self.survey.title == 'DSS colored'

     def test_hips_version(self):
-        assert self.hips_survey.hips_version == '1.31'
+        assert self.survey.hips_version == '1.31'

     def test_hips_frame(self):
-        assert self.hips_survey.hips_frame == 'equatorial'
+        assert self.survey.hips_frame == 'equatorial'

     def test_astropy_frame(self):
-        assert self.hips_survey.astropy_frame == 'icrs'
+        assert self.survey.astropy_frame == 'icrs'

     def test_hips_order(self):
-        assert self.hips_survey.hips_order == 9
+        assert self.survey.hips_order == 9

     def test_tile_format(self):
-        assert self.hips_survey.tile_format == 'jpeg'
+        assert self.survey.tile_format == 'jpeg'

     def test_base_url(self):
         expected = 'http://alasky.u-strasbg.fr/DSS/DSSColor'
-        assert self.hips_survey.base_url == expected
+        assert self.survey.base_url == expected

     def test_tile_default_url(self):
         tile_meta = HipsTileMeta(order=9, ipix=54321, file_format='fits')
-        url = self.hips_survey.tile_url(tile_meta)
+        url = self.survey.tile_url(tile_meta)
         assert url == 'http://alasky.u-strasbg.fr/DSS/DSSColor/Norder9/Dir50000/Npix54321.fits'

     @staticmethod
@@ -85,8 +97,12 @@ class TestHipsSurveyPropertiesList:
         surveys = HipsSurveyPropertiesList.fetch()
         assert len(surveys.data) > 3

-        # TODO: look up survey by name here
-        # Otherwise this will break when the list changes
-        survey = surveys.data[0]
+        survey = surveys.from_name('CDS/P/2MASS/H')
         assert survey.title == '2MASS H (1.66 microns)'
         assert survey.hips_order == 9
+
+    @remote_data
+    def test_key_error(self):
+        with pytest.raises(KeyError):
+            surveys = HipsSurveyPropertiesList.fetch()
+            surveys.from_name('Kronka Lonka')

commit 821f335f3f6c5ba912bcfdbf2ea55e1d51e6be82
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Tue Jul 25 19:30:10 2017 +0500

    Update high-level docs example

diff --git a/docs/getting_started.rst b/docs/getting_started.rst
index ac958e1..0771e95 100644
--- a/docs/getting_started.rst
+++ b/docs/getting_started.rst
@@ -37,22 +37,34 @@ To make a sky image with the `hips` package, follow the following three steps:


 3. Call the `~hips.make_sky_image` function to fetch the HiPS data
-   and draw it, returning the sky image pixel data as a Numpy array::
+   and draw it, returning an object of `~hips.HipsDrawResult`::

     from hips import make_sky_image

-    data = make_sky_image(geometry, hips_survey, 'fits')
+    result = make_sky_image(geometry, hips_survey, 'fits')


 That's it. Go ahead and try it out for your favourite sky region and survey.

 Now you can then save the sky image to local disk e.g. FITS file format::

-    from astropy.io import fits
-    hdu = fits.PrimaryHDU(data=data, header=geometry.fits_header)
-    hdu.writeto('my_image.fits')
+    result.write_image('my_image.fits')

-or plot and analyse the sky image however you like.
+The ``result`` object also contains other useful information, such as::
+
+    result.image
+
+will return a NumPy array containing pixel data, you can also get the WCS information using::
+
+    result.geometry
+
+If you want, you could also print out information about the ``result``::
+
+    print(result)
+
+or plot and analyse the sky image using::
+
+    result.plot()

 If you execute the example above, you will get this sky image which was plotted using `astropy.visualization.wcsaxes`

diff --git a/docs/plot_fits.py b/docs/plot_fits.py
index 14bcb18..bc1f860 100644
--- a/docs/plot_fits.py
+++ b/docs/plot_fits.py
@@ -10,11 +10,11 @@ geometry = WCSGeometry.create(
     width=2000, height=1000, fov="3 deg",
     coordsys='galactic', projection='AIT',
 )
-image = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format='fits')
+result = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format='fits')

 # Draw the sky image
 import matplotlib.pyplot as plt
 from astropy.visualization.mpl_normalize import simple_norm
 ax = plt.subplot(projection=geometry.wcs)
-norm = simple_norm(image, 'sqrt', min_percent=1, max_percent=99)
-ax.imshow(image, origin='lower', norm=norm, cmap='gray')
+norm = simple_norm(result.image, 'sqrt', min_percent=1, max_percent=99)
+ax.imshow(result.image, origin='lower', norm=norm, cmap='gray')
diff --git a/docs/plot_jpg.py b/docs/plot_jpg.py
index ba3b852..1eb111b 100644
--- a/docs/plot_jpg.py
+++ b/docs/plot_jpg.py
@@ -10,9 +10,9 @@ geometry = WCSGeometry.create(
     width=2000, height=1000, fov="3 deg",
     coordsys='galactic', projection='AIT',
 )
-image = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format='jpg')
+result = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format='jpg')

 # Draw the sky image
 import matplotlib.pyplot as plt
 ax = plt.subplot(projection=geometry.wcs)
-ax.imshow(image, origin='lower')
+ax.imshow(result.image, origin='lower')
diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index 2539df3..a6dc4d7 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -129,7 +129,8 @@ class SimpleTilePainter:

     @property
     def result(self) -> 'HipsDrawResult':
-        return HipsDrawResult(self.image, self.geometry, self.tile_format)
+        """Return an object of `~hips.HipsDrawResult` class."""
+        return HipsDrawResult(self.image, self.geometry, self.tile_format, self.tiles)

     def warp_image(self, tile: HipsTile) -> np.ndarray:
         """Warp a HiPS tile and a sky image."""
@@ -189,25 +190,31 @@ class HipsDrawResult:
         An object of WCSGeometry
     tile_format : {'fits', 'jpg', 'png'}
         Format of HiPS tile
+    tiles: List[HipsTile]
     """

-    def __init__(self, image: np.ndarray, geometry: WCSGeometry, tile_format: str) -> None:
+    def __init__(self, image: np.ndarray, geometry: WCSGeometry, tile_format: str, tiles: List[HipsTile]) -> None:
         self.image = image
         self.geometry = geometry
         self.tile_format = tile_format
+        self.tiles = tiles

     def __str__(self):
         return (
-            'This class object contains two attributes: image and geometry'
+            'HiPS draw result:\n'
+            f'Sky image: shape={self.image.shape}, dtype={self.image.dtype}\n'
+            f'WCS geometry: {self.geometry}\n'
         )

     def __repr__(self):
         return (
+            'HipsDrawResult('
             f'width={self.image.shape[0]}, '
             f'height={self.image.shape[1]}, '
             f'channels={self.image.ndim}, '
             f'dtype={self.image.dtype}, '
             f'format={self.tile_format}'
+            ')'
         )

     def write_image(self, filename: str) -> None:
@@ -219,12 +226,24 @@ class HipsDrawResult:
             Filename
         """
         if self.tile_format == 'fits':
-            hdu = fits.PrimaryHDU(self.image)
+            hdu = fits.PrimaryHDU(data=self.image, header=self.geometry.fits_header)
             hdu.writeto(filename)
         else:
             image = Image.fromarray(self.image)
             image.save(filename)

+    def plot(self) -> None:
+        """Plot the all sky image using `astropy.visualization.wcsaxes` and showing the HEALPix grid."""
+        import matplotlib.pyplot as plt
+        for tile in self.tiles:
+            corners = tile.meta.skycoord_corners.transform_to(self.geometry.celestial_frame)
+            ax = plt.subplot(projection=self.geometry.wcs)
+            opts = dict(color='red', lw=1, )
+            ax.plot(corners.data.lon.deg, corners.data.lat.deg,
+                    transform=ax.get_transform('world'), **opts)
+        ax.imshow(self.image, origin='lower')
+
+
 def measure_tile_shape(corners: tuple) -> Tuple[List[float]]:
     """Compute length of tile edges and diagonals."""
     x, y = corners
diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py
index d038f43..4287862 100644
--- a/hips/draw/tests/test_simple.py
+++ b/hips/draw/tests/test_simple.py
@@ -19,7 +19,7 @@ make_sky_image_pars = [
         data_2=2296,
         data_sum=8756493140,
         dtype='>i2',
-        repr='width=1000, height=2000, channels=2, dtype=>i2, format=fits'
+        repr='HipsDrawResult(width=1000, height=2000, channels=2, dtype=>i2, format=fits)'
     ),
     dict(
         file_format='jpg',
@@ -29,7 +29,7 @@ make_sky_image_pars = [
         data_2=[137, 116, 114],
         data_sum=828908873,
         dtype='uint8',
-        repr='width=1000, height=2000, channels=3, dtype=uint8, format=jpg'
+        repr='HipsDrawResult(width=1000, height=2000, channels=3, dtype=uint8, format=jpg)'
     ),
     dict(
         file_format='png',
@@ -39,24 +39,25 @@ make_sky_image_pars = [
         data_2=[227, 217, 205, 255],
         data_sum=1635622838,
         dtype='uint8',
-        repr='width=1000, height=2000, channels=3, dtype=uint8, format=png'
+        repr='HipsDrawResult(width=1000, height=2000, channels=3, dtype=uint8, format=png)'
     ),
 ]


 @remote_data
 @pytest.mark.parametrize('pars', make_sky_image_pars)
-def test_make_sky_image(pars):
+def test_make_sky_image(tmpdir, pars):
     hips_survey = HipsSurveyProperties.fetch(url=pars['url'])
     geometry = make_test_wcs_geometry()
     result = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=pars['file_format'])
     assert result.image.shape == pars['shape']
     assert result.image.dtype == pars['dtype']
+    assert repr(result) == pars['repr']
     assert_allclose(np.sum(result.image), pars['data_sum'])
     assert_allclose(result.image[200, 994], pars['data_1'])
     assert_allclose(result.image[200, 995], pars['data_2'])
-    # result.write_image('test.' + pars['file_format'])
-    assert repr(result) == pars['repr']
+    result.write_image(str(tmpdir / 'test.' + pars['file_format']))
+    result.plot()

 @remote_data
 class TestSimpleTilePainter:
diff --git a/hips/utils/wcs.py b/hips/utils/wcs.py
index 6268dc8..e43b049 100644
--- a/hips/utils/wcs.py
+++ b/hips/utils/wcs.py
@@ -58,6 +58,13 @@ class WCSGeometry:
         self.wcs = wcs
         self.shape = Shape(width=width, height=height)

+    def __str__(self):
+        return (
+            'WCSGeometry data:\n'
+            f'WCS: {self.wcs}\n'
+            f'Shape: {self.shape}\n'
+        )
+
     @property
     def center_pix(self) -> Tuple[float, float]:
         """Image center in pixel coordinates (tuple of x, y)."""

commit c15092b116d5b02b601a2c087ef6a80fb82107d2
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Tue Jul 25 19:24:10 2017 +0500

    Add method write_image in HipsDrawResult class

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index c47eaa0..2539df3 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -1,6 +1,8 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 """HiPS tile drawing -- simple method."""
 import numpy as np
+from PIL import Image
+from astropy.io import fits
 from typing import List, Tuple
 from astropy.wcs.utils import proj_plane_pixel_scales
 from skimage.transform import ProjectiveTransform, warp
@@ -127,7 +129,7 @@ class SimpleTilePainter:

     @property
     def result(self) -> 'HipsDrawResult':
-        return HipsDrawResult(self.image, self.geometry)
+        return HipsDrawResult(self.image, self.geometry, self.tile_format)

     def warp_image(self, tile: HipsTile) -> np.ndarray:
         """Warp a HiPS tile and a sky image."""
@@ -177,12 +179,51 @@ class SimpleTilePainter:


 class HipsDrawResult:
-    """Container class for reporting information related with fetching / drawing of HiPS tiles."""
+    """Container class for reporting information related with fetching / drawing of HiPS tiles.

-    def __init__(self, image: np.ndarray, geometry: WCSGeometry) -> None:
+    Parameters
+    ----------
+    image: `~numpy.ndarray`
+        Container for HiPS tile data
+    geometry : `~hips.utils.WCSGeometry`
+        An object of WCSGeometry
+    tile_format : {'fits', 'jpg', 'png'}
+        Format of HiPS tile
+    """
+
+    def __init__(self, image: np.ndarray, geometry: WCSGeometry, tile_format: str) -> None:
         self.image = image
         self.geometry = geometry
+        self.tile_format = tile_format
+
+    def __str__(self):
+        return (
+            'This class object contains two attributes: image and geometry'
+        )

+    def __repr__(self):
+        return (
+            f'width={self.image.shape[0]}, '
+            f'height={self.image.shape[1]}, '
+            f'channels={self.image.ndim}, '
+            f'dtype={self.image.dtype}, '
+            f'format={self.tile_format}'
+        )
+
+    def write_image(self, filename: str) -> None:
+        """Write image to file.
+
+        Parameters
+        ----------
+        filename : str
+            Filename
+        """
+        if self.tile_format == 'fits':
+            hdu = fits.PrimaryHDU(self.image)
+            hdu.writeto(filename)
+        else:
+            image = Image.fromarray(self.image)
+            image.save(filename)

 def measure_tile_shape(corners: tuple) -> Tuple[List[float]]:
     """Compute length of tile edges and diagonals."""
diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py
index 8985954..d038f43 100644
--- a/hips/draw/tests/test_simple.py
+++ b/hips/draw/tests/test_simple.py
@@ -19,6 +19,7 @@ make_sky_image_pars = [
         data_2=2296,
         data_sum=8756493140,
         dtype='>i2',
+        repr='width=1000, height=2000, channels=2, dtype=>i2, format=fits'
     ),
     dict(
         file_format='jpg',
@@ -28,6 +29,7 @@ make_sky_image_pars = [
         data_2=[137, 116, 114],
         data_sum=828908873,
         dtype='uint8',
+        repr='width=1000, height=2000, channels=3, dtype=uint8, format=jpg'
     ),
     dict(
         file_format='png',
@@ -37,6 +39,7 @@ make_sky_image_pars = [
         data_2=[227, 217, 205, 255],
         data_sum=1635622838,
         dtype='uint8',
+        repr='width=1000, height=2000, channels=3, dtype=uint8, format=png'
     ),
 ]

@@ -52,7 +55,8 @@ def test_make_sky_image(pars):
     assert_allclose(np.sum(result.image), pars['data_sum'])
     assert_allclose(result.image[200, 994], pars['data_1'])
     assert_allclose(result.image[200, 995], pars['data_2'])
-
+    # result.write_image('test.' + pars['file_format'])
+    assert repr(result) == pars['repr']

 @remote_data
 class TestSimpleTilePainter:

commit eddcaaa07028e59d2d8e8567cbf073d30bbd9ada
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Tue Jul 25 16:45:32 2017 +0500

    Introduce class HipsDrawResult in simple.py

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index b53bad8..c47eaa0 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -125,6 +125,10 @@ class SimpleTilePainter:

         return self._tiles

+    @property
+    def result(self) -> 'HipsDrawResult':
+        return HipsDrawResult(self.image, self.geometry)
+
     def warp_image(self, tile: HipsTile) -> np.ndarray:
         """Warp a HiPS tile and a sky image."""
         return warp(
@@ -172,6 +176,14 @@ class SimpleTilePainter:
         ax.imshow(self.image, origin='lower')


+class HipsDrawResult:
+    """Container class for reporting information related with fetching / drawing of HiPS tiles."""
+
+    def __init__(self, image: np.ndarray, geometry: WCSGeometry) -> None:
+        self.image = image
+        self.geometry = geometry
+
+
 def measure_tile_shape(corners: tuple) -> Tuple[List[float]]:
     """Compute length of tile edges and diagonals."""
     x, y = corners
@@ -268,7 +280,7 @@ def plot_mpl_single_tile(geometry: WCSGeometry, tile: HipsTile, image: np.ndarra
     ax.imshow(image, origin='lower')


-def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties, tile_format: str) -> np.ndarray:
+def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties, tile_format: str) -> 'HipsDrawResult':
     """Make sky image: fetch tiles and draw.

     The example for this can be found on the :ref:`gs` page.
@@ -291,4 +303,4 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties, til
     painter = SimpleTilePainter(geometry, hips_survey, tile_format)
     painter.run()

-    return painter.image
+    return painter.result
diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py
index 6dbf340..8985954 100644
--- a/hips/draw/tests/test_simple.py
+++ b/hips/draw/tests/test_simple.py
@@ -46,12 +46,12 @@ make_sky_image_pars = [
 def test_make_sky_image(pars):
     hips_survey = HipsSurveyProperties.fetch(url=pars['url'])
     geometry = make_test_wcs_geometry()
-    image = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=pars['file_format'])
-    assert image.shape == pars['shape']
-    assert image.dtype == pars['dtype']
-    assert_allclose(np.sum(image), pars['data_sum'])
-    assert_allclose(image[200, 994], pars['data_1'])
-    assert_allclose(image[200, 995], pars['data_2'])
+    result = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=pars['file_format'])
+    assert result.image.shape == pars['shape']
+    assert result.image.dtype == pars['dtype']
+    assert_allclose(np.sum(result.image), pars['data_sum'])
+    assert_allclose(result.image[200, 994], pars['data_1'])
+    assert_allclose(result.image[200, 995], pars['data_2'])


 @remote_data

commit 808e758d2cd9c04c31e0dc49318cf55d4f92cd18
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Mon Jul 24 14:09:33 2017 +0500

    Add method to_split in SimpleTilePainter for checking whether to split a tile

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index e9237e0..862f9e8 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -172,6 +172,29 @@ class SimpleTilePainter:
         ax.imshow(self.image, origin='lower')


+def _measure_tile_shape(corners: tuple) -> List[list]:
+    """Compute length of tile edges and diagonals."""
+    x, y = corners
+
+    def compute_distance(i: int, j: int) -> float:
+        """Compute distance between two points."""
+        return np.sqrt((x[i] - x[j]) ** 2 + (y[i] - y[j]) ** 2)
+
+    edges = [compute_distance((i + 1) % 4, i) for i in range(4)]
+    diagonals = [compute_distance(0, 2), compute_distance(1, 3)]
+    ratio = float(np.min(diagonals) / np.max(diagonals))
+
+    return [edges, diagonals, ratio]
+
+def _is_tile_distorted(corners: tuple) -> bool:
+    """Implement tile splitting criteria as mentioned in :ref:`drawing_algo` page."""
+    edges, diagonals, ratio = _measure_tile_shape(corners)
+
+    return max(edges) > 300 or \
+           max(diagonals) > 150 or \
+           ratio < 0.7
+
+
 def tile_corner_pixel_coordinates(width, file_format) -> np.ndarray:
     """Tile corner pixel coordinates for projective transform.

diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py
index d3368f7..85e2d78 100644
--- a/hips/draw/tests/test_simple.py
+++ b/hips/draw/tests/test_simple.py
@@ -5,7 +5,7 @@ from numpy.testing import assert_allclose
 from astropy.coordinates import SkyCoord
 from astropy.tests.helper import remote_data
 from ...tiles import HipsSurveyProperties
-from ..simple import make_sky_image, SimpleTilePainter, plot_mpl_single_tile
+from ..simple import make_sky_image, SimpleTilePainter, plot_mpl_single_tile, _is_tile_distorted, _measure_tile_shape
 from ...utils.wcs import WCSGeometry
 from ...utils.testing import make_test_wcs_geometry, requires_hips_extra

@@ -86,6 +86,7 @@ class TestSimpleTilePainter:
             width=2000, height=1000, fov=pars['fov'],
             coordsys='icrs', projection='AIT',
         )
+
         simple_tile_painter = SimpleTilePainter(geometry, self.hips_survey, 'fits')
         assert simple_tile_painter.draw_hips_order == pars['order']

@@ -101,3 +102,28 @@ class TestSimpleTilePainter:
         tile = self.painter.tiles[3]
         image = self.painter.image
         plot_mpl_single_tile(self.geometry, tile, image)
+
+    def test_corners(self):
+        tile = self.painter.tiles[3]
+        x, y = tile.meta.skycoord_corners.to_pixel(self.geometry.wcs)
+
+        assert_allclose(x, [764.627476, 999., 764.646551, 530.26981])
+        assert_allclose(y, [300.055412, 101.107245, -97.849955, 101.105373])
+
+    def test_is_tile_distorted(self):
+        tile = self.painter.tiles[3]
+        corners = tile.meta.skycoord_corners.to_pixel(self.geometry.wcs)
+        assert _is_tile_distorted(corners) == True
+
+    def test_measure_tile_shape(self):
+        tile = self.painter.tiles[3]
+        corners = tile.meta.skycoord_corners.to_pixel(self.geometry.wcs)
+        edges, diagonals, ratio = _measure_tile_shape(corners)
+
+        edges_precomp = [307.426175, 307.417479, 307.434024, 307.41606]
+        diagonals_precomp = [397.905367, 468.73019]
+        ratio_precomp = 0.848900658905216
+
+        assert_allclose(edges_precomp, edges)
+        assert_allclose(diagonals_precomp, diagonals)
+        assert_allclose(ratio_precomp, ratio)

commit e08ddcdf3c655021d5a0b96bb26ad50577e0e02a
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Thu Jul 20 12:32:51 2017 +0500

    Add property 'children' for creating four children tiles from parent tile

diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py
index f8f156b..2957e40 100644
--- a/hips/tiles/tests/test_tile.py
+++ b/hips/tiles/tests/test_tile.py
@@ -59,8 +59,13 @@ HIPS_TILE_TEST_CASES = [

         dtype='int16',
         shape=(512, 512),
+        child_order=4,
+        child_shape=(256, 256),
         pix_idx=[[510], [5]],
         pix_val=[3047],
+        child_ipix=[1852, 1853, 1854, 1855],
+        child_pix_idx=[[0], [255]],
+        child_pix_val=[2407, 2321, 2465, 2835],
     ),
     dict(
         meta=dict(order=3, ipix=463, file_format='jpg'),
@@ -69,8 +74,13 @@ HIPS_TILE_TEST_CASES = [

         dtype='uint8',
         shape=(512, 512, 3),
+        child_order=4,
+        child_shape=(256, 256, 3),
         pix_idx=[[510], [5]],
         pix_val=[[116, 81, 61]],
+        child_ipix=[1852, 1853, 1854, 1855],
+        child_pix_idx=[[0], [255]],
+        child_pix_val=[[[255, 241, 225]], [[109, 95, 86]], [[245, 214, 211]], [[137, 97, 87]]],
     ),
     dict(
         meta=dict(order=6, ipix=6112, file_format='png'),
@@ -79,8 +89,13 @@ HIPS_TILE_TEST_CASES = [

         dtype='uint8',
         shape=(512, 512, 4),
+        child_order=7,
+        child_shape=(256, 256, 4),
         pix_idx=[[253], [5]],
         pix_val=[[19, 19, 19, 255]],
+        child_ipix=[24448, 24449, 24450, 24451],
+        child_pix_idx=[[0], [255]],
+        child_pix_val=[[[15, 15, 15, 255]], [[20, 20, 20, 255]], [[17, 17, 17, 255]], [[13, 13, 13, 255]]],
     ),
 ]

@@ -117,7 +132,19 @@ class TestHipsTile:
         data = tile.data
         assert data.shape == pars['shape']
         assert data.dtype.name == pars['dtype']
-        assert_equal(tile.data[pars['pix_idx']], pars['pix_val'])
+        assert_equal(data[pars['pix_idx']], pars['pix_val'])
+
+    @requires_hips_extra()
+    @pytest.mark.parametrize('pars', HIPS_TILE_TEST_CASES)
+    def test_children(self, pars):
+        tile = self._read_tile(pars)
+        child_data = [_.data[pars['child_pix_idx']] for _ in tile.children]
+        child_ipix = [_.meta.ipix for _ in tile.children]
+
+        assert tile.children[0].meta.order == pars['child_order']
+        assert tile.children[0].data.shape == pars['child_shape']
+        assert_equal(child_ipix, pars['child_ipix'])
+        assert_equal(child_data, pars['child_pix_val'])

     @remote_data
     @requires_hips_extra()
diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py
index 2a86467..32f98c2 100644
--- a/hips/tiles/tile.py
+++ b/hips/tiles/tile.py
@@ -11,6 +11,7 @@ from astropy.coordinates import SkyCoord
 from astropy.io import fits
 from ..utils import healpix_pixel_corners
 from .io import tile_default_url, tile_default_path
+from typing import List

 __all__ = [
     'HipsTileMeta',
@@ -167,6 +168,29 @@ class HipsTile:
         return cls(meta, raw_data)

     @property
+    def children(self) -> List['HipsTile']:
+        """Create four children tiles from parent tile."""
+        children_tiles = []
+        w = self.data.shape[0] // 2
+        child_data = [
+            self.data[0: w, 0: w],
+            self.data[0: w, w: w * 2],
+            self.data[w: w * 2, 0: w],
+            self.data[w: w * 2, w: w * 2]
+        ]
+
+        for index, data in enumerate(child_data):
+            meta = HipsTileMeta(
+                self.meta.order + 1,
+                self.meta.ipix * 4 + index,
+                self.meta.file_format,
+                self.meta.frame
+            )
+            children_tiles.append(self.from_numpy(meta, data))
+
+        return children_tiles
+
+    @property
     def data(self) -> np.ndarray:
         """Tile pixel data (`~numpy.ndarray`)."""
         fmt = self.meta.file_format

commit 9005878e851ee8f64ac498599bba8737a1fd83f4
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Tue Jul 18 13:31:41 2017 +0500

    Add methods for drawing markers over sky image

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index ae2dfc9..9811238 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -1,7 +1,7 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 """HiPS tile drawing -- simple method."""
-from typing import Tuple, List
 import numpy as np
+from typing import Tuple, List
 from astropy.wcs.utils import proj_plane_pixel_scales
 from skimage.transform import ProjectiveTransform, warp
 from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta
@@ -9,8 +9,7 @@ from ..utils import WCSGeometry, compute_healpix_pixel_indices, get_hips_order_f

 __all__ = [
     'make_sky_image',
-    'SimpleTilePainter',
-
+    'SimpleTilePainter'
 ]

 __doctest_skip__ = [
@@ -178,11 +177,49 @@ class SimpleTilePainter:

         return image

+    def draw_hips_tile_grid(self) -> None:
+        """Draw lines on the output image (mainly used for debugging)."""
+        import matplotlib.pyplot as plt
+        for tile in self.tiles:
+            corners = tile.meta.skycoord_corners.transform_to(self.geometry.celestial_frame)
+            ax = plt.subplot(projection=self.geometry.wcs)
+            ax.plot(corners.data.lon.deg, corners.data.lat.deg,
+                    'red', lw=1, transform=ax.get_transform('icrs'))
+        ax.imshow(self.image, origin='lower')
+
     def run(self) -> None:
         """Run all steps of the naive algorithm."""
         self.float_image = self.draw_tiles()


+def draw_debug_image(geometry: WCSGeometry, tile: HipsTile, image: np.ndarray) -> None:
+    """Draw markers on the output image (mainly used for debugging).
+
+    The following denotes their correspondence:
+    * red <=> North
+    * green <=> West
+    * blue <=> South
+    * yellow <=> East
+
+    Parameters
+    ----------
+    geometry : `~hips.utils.WCSGeometry`
+        Geometry of the output image
+    tile : HipsTile
+        HiPS tile
+    image : np.ndarray
+        Image containing HiPS tiles
+    """
+    import matplotlib.pyplot as plt
+    corners = tile.meta.skycoord_corners.transform_to(geometry.celestial_frame)
+    colors = ['red', 'green', 'blue', 'yellow']
+    ax = plt.subplot(projection=geometry.wcs)
+    for index, corner in enumerate(corners):
+        ax.scatter(corner.data.lon.deg, corner.data.lat.deg,
+                   s=80, transform=ax.get_transform('icrs'), color=colors[index])
+    ax.imshow(image, origin='lower')
+
+
 def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties, tile_format: str) -> np.ndarray:
     """Make sky image: fetch tiles and draw.

diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py
index 22150ce..7e8faa2 100644
--- a/hips/draw/tests/test_simple.py
+++ b/hips/draw/tests/test_simple.py
@@ -5,7 +5,7 @@ from numpy.testing import assert_allclose
 from astropy.coordinates import SkyCoord
 from astropy.tests.helper import remote_data
 from ...tiles import HipsSurveyProperties
-from ..simple import make_sky_image, SimpleTilePainter
+from ..simple import make_sky_image, SimpleTilePainter, draw_debug_image
 from ...utils.wcs import WCSGeometry
 from ...utils.testing import make_test_wcs_geometry, requires_hips_extra

@@ -95,3 +95,9 @@ class TestSimpleTilePainter:
     def test_run(self):
         self.simple_tile_painter.run()
         assert_allclose(self.simple_tile_painter.image[200, 994], 2120)
+
+    def test_draw_hips_tile_grid(self):
+        self.simple_tile_painter.draw_hips_tile_grid()
+
+    def test_draw_debug_image(self):
+        draw_debug_image(self.geometry, self.simple_tile_painter.tiles[3], self.simple_tile_painter.image)

commit 46c78a036dec051bb9e6fb75adbf7fcd6a585301
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Tue Jul 18 20:16:34 2017 +0500

    Fix tile_width property in HipsSurveyProperties class

diff --git a/hips/tiles/surveys.py b/hips/tiles/surveys.py
index 28bc65b..f5c462b 100644
--- a/hips/tiles/surveys.py
+++ b/hips/tiles/surveys.py
@@ -128,7 +128,10 @@ class HipsSurveyProperties:
     @property
     def tile_width(self) -> int:
         """HiPS tile width"""
-        return int(self.data['hips_tile_width']) or 512
+        try:
+            return int(self.data['hips_tile_width'])
+        except KeyError:
+            return 512

     @property
     def tile_format(self) -> str:

commit 6139a0bf4047b303a01fefb61ef5288de55486c6
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Mon Jul 17 14:13:40 2017 +0500

    Fix RGB dtype issue

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index 235b235..f144533 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -63,6 +63,11 @@ class SimpleTilePainter:
         self._tiles = None

     @property
+    def image(self) -> np.ndarray:
+        """All sky image converted to HiPS tile format."""
+        return self.float_image.astype(self.tiles[0].data.dtype)
+
+    @property
     def draw_hips_order(self) -> int:
         """Compute HiPS tile order matching a given image pixel size."""
         # Sky image angular resolution (pixel size in degrees)
@@ -159,7 +164,7 @@ class SimpleTilePainter:
         """Draw HiPS tiles onto an empty image."""
         tiles = self.tiles

-        image = np.zeros(self.shape)
+        image = np.zeros(self.shape, dtype=np.float32)
         for tile in tiles:
             tile_image = self.warp_image(tile)
             # TODO: put better algorithm here instead of summing pixels
@@ -170,7 +175,7 @@ class SimpleTilePainter:

     def run(self) -> None:
         """Run all steps of the naive algorithm."""
-        self.image = self.draw_tiles()
+        self.float_image = self.draw_tiles()


 def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties, tile_format: str) -> np.ndarray:
diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py
index 7ec0e2a..22150ce 100644
--- a/hips/draw/tests/test_simple.py
+++ b/hips/draw/tests/test_simple.py
@@ -14,25 +14,28 @@ make_sky_image_pars = [
         file_format='fits',
         shape=(1000, 2000),
         url='http://alasky.unistra.fr/DSS/DSS2Merged/properties',
-        data_1=2213.30874796,
-        data_2=2296.93885940,
-        data_sum=8757489268.044867,
+        data_1=2213,
+        data_2=2296,
+        data_sum=8756493140,
+        dtype='>i2'
     ),
     dict(
         file_format='jpg',
         shape=(1000, 2000, 3),
         url='https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/FermiColor/properties',
-        data_1=[145.388459, 98.579295, 49.792577],
-        data_2=[146.197811, 99.531895, 56.889927],
-        data_sum=813159920.0305891,
+        data_1=[145, 98, 49],
+        data_2=[146, 99, 56],
+        data_sum=808113247,
+        dtype='uint8'
     ),
     dict(
         file_format='png',
         shape=(1000, 2000, 4),
         url='https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/AKARI-FIS/properties',
-        data_1=[249.064796, 237.805612, 190.408181, 255.],
-        data_2=[249.013527, 238.79403, 195.793398, 255.],
-        data_sum=1645783166.1630714
+        data_1=[249, 237, 190, 255.],
+        data_2=[249, 238, 195, 255.],
+        data_sum=1632505453,
+        dtype='uint8'
     ),
 ]

@@ -44,7 +47,7 @@ def test_make_sky_image(pars):
     geometry = make_test_wcs_geometry(case=2)
     image = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=pars['file_format'])
     assert image.shape == pars['shape']
-    assert image.dtype == np.float64
+    assert image.dtype == pars['dtype']
     assert_allclose(np.sum(image), pars['data_sum'])
     assert_allclose(image[200, 994], pars['data_1'])
     assert_allclose(image[200, 995], pars['data_2'])
@@ -91,4 +94,4 @@ class TestSimpleTilePainter:

     def test_run(self):
         self.simple_tile_painter.run()
-        assert_allclose(self.simple_tile_painter.image[200, 994], 2120.9609175)
+        assert_allclose(self.simple_tile_painter.image[200, 994], 2120)

commit eb5cd378624001de9588a4eb7095361b1f9b94d5
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Tue Jul 11 20:29:14 2017 +0500

    Reformat SimpleTilePainter class

diff --git a/docs/plot_fits.py b/docs/plot_fits.py
index 67b03d9..70cac7a 100644
--- a/docs/plot_fits.py
+++ b/docs/plot_fits.py
@@ -6,7 +6,6 @@ from astropy.visualization.mpl_normalize import simple_norm
 from hips import HipsSurveyProperties
 from hips import make_sky_image
 from hips.utils import WCSGeometry
-import numpy as np
 url = 'http://alasky.unistra.fr/DSS/DSS2Merged/properties'
 hips_survey = HipsSurveyProperties.fetch(url)
 geometry = WCSGeometry.create_simple(
@@ -14,7 +13,7 @@ geometry = WCSGeometry.create_simple(
     width=2000, height=1000, fov="3 deg",
     coordsys='galactic', projection='AIT'
 )
-data = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format='fits')
+image = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format='fits')
 ax = plt.subplot(projection=geometry.wcs)
-norm = simple_norm(data, 'sqrt')
-ax.imshow(data, origin='lower', norm=norm, cmap='gray')
+norm = simple_norm(image, 'sqrt')
+ax.imshow(image, origin='lower', norm=norm, cmap='gray')
diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index 2664a59..b0c4ed9 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -1,80 +1,90 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 """HiPS tile drawing -- simple method."""
-from typing import Generator, Any
 import numpy as np
+from typing import Tuple
 from astropy.wcs.utils import proj_plane_pixel_scales
 from skimage.transform import ProjectiveTransform, warp
 from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta
-from ..utils import WCSGeometry, compute_healpix_pixel_indices
+from ..utils import WCSGeometry, compute_healpix_pixel_indices, get_hips_order_for_resolution
+

 __all__ = [
-    'draw_sky_image',
     'make_sky_image',
     'SimpleTilePainter',
-    'compute_matching_hips_order'
+
 ]

 __doctest_skip__ = [
-    'compute_matching_hips_order',
+    'SimpleTilePainter'
 ]


-# TODO: Fix type annotation issue
-def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any],
-                   hips_survey: HipsSurveyProperties, tile_format: str) -> np.ndarray:
+class SimpleTilePainter:
     """Draw sky image using the simple and quick method.

+    Paint HiPS tiles onto an all-sky image using a simple projective
+    transformation method. The algorithm implemented is described
+    here: :ref:`drawing_algo`.
+
     Parameters
     ----------
     geometry : `~hips.utils.WCSGeometry`
         An object of WCSGeometry
-    tiles : List[HipsTile]
-        A list of HipsTile
     hips_survey : `~hips.HipsSurveyProperties`
         HiPS survey properties
     tile_format : `str`
         Format of HiPS tile

-    Returns
-    -------
-    np.ndarray
-        Returns a numpy array containing all HiPS tiles projected onto it
-    """
-    if tile_format == 'jpg':
-        shape = (geometry.shape.height, geometry.shape.width, 3)
-    elif tile_format == 'png':
-        shape = (geometry.shape.height, geometry.shape.width, 4)
-    else:
-        shape = (geometry.shape.height, geometry.shape.width)
-    image = np.zeros(shape)
-    for tile in tiles:
-        painter = SimpleTilePainter(geometry, hips_survey, tile)
-        image += painter.warp_image()
-    return image
-
-
-class SimpleTilePainter:
-    """Paint a single tile using a simple projective transformation method.
-    The algorithm implemented is described here: :ref:`drawing_algo`.
-
-    Parameters
-    ----------
-    geometry : `~hips.utils.WCSGeometry`
-        An object of WCSGeometry
-    hips_survey : `~hips.HipsSurveyProperties`
-        HiPS survey properties
-    tile : `HipsTile`
-       An object of HipsTile
+    Examples
+    --------
+    >>> from hips.utils import WCSGeometry
+    >>> from hips.draw import SimpleTilePainter
+    >>> from hips.tiles import HipsSurveyProperties
+    >>> from astropy.coordinates import SkyCoord
+    >>> url = 'http://alasky.unistra.fr/DSS/DSS2Merged/properties'
+    >>> hips_survey = HipsSurveyProperties.fetch(url)
+    >>> geometry = WCSGeometry.create_simple(
+    ...     skydir=SkyCoord(0, 0, unit='deg', frame='icrs'),
+    ...     width=2000, height=1000, fov="3 deg",
+    ...     coordsys='icrs', projection='AIT'
+    ... )
+    >>> painter = SimpleTilePainter(geometry, hips_survey, 'fits')
+    >>> painter.draw_hips_order
+    7
+    >>> painter.run()
+    >>> painter.image.shape
+    (1000, 2000)
     """

-    def __init__(self, geometry: WCSGeometry, hips_survey: HipsSurveyProperties, tile: HipsTile) -> None:
+    def __init__(self, geometry: WCSGeometry, hips_survey: HipsSurveyProperties, tile_format: str) -> None:
         self.geometry = geometry
         self.hips_survey = hips_survey
-        self.tile = tile
+        self.tile_format = tile_format
+        self._tiles = None
+
+    @property
+    def draw_hips_order(self) -> int:
+        """Compute HiPS tile order matching a given image pixel size."""
+        # Sky image angular resolution (pixel size in degrees)
+        resolution = np.min(proj_plane_pixel_scales(self.geometry.wcs))
+        desired_order = get_hips_order_for_resolution(self.hips_survey.tile_width, resolution)
+        # Return the desired order, or the highest resolution available.
+        # Note that HiPS never has resolution less than 3,
+        # and that limit is handled in _get_hips_order_for_resolution
+        return np.min([desired_order, self.hips_survey.hips_order])
+
+    @property
+    def tile_indices(self):
+        """Get list of index values for HiPS tiles."""
+        return compute_healpix_pixel_indices(
+            wcs_geometry=self.geometry,
+            order=self.draw_hips_order,
+            healpix_frame=self.hips_survey.astropy_frame,
+        )

     @property
     def dst(self) -> np.ndarray:
-        """Destination array for projective transform"""
+        """Destination array for projective transform."""
         width = self.hips_survey.tile_width
         return np.array(
             [[width - 1, 0],
@@ -83,112 +93,74 @@ class SimpleTilePainter:
              [0, 0]],
         )

-    @property
-    def projection(self) -> ProjectiveTransform:
-        """Estimate projective transformation on a HiPS tile"""
-        corners = self.tile.meta.skycoord_corners.to_pixel(self.geometry.wcs)
+    def projection(self, tile: HipsTile) -> ProjectiveTransform:
+        """Estimate projective transformation on a HiPS tile."""
+        corners = tile.meta.skycoord_corners.to_pixel(self.geometry.wcs)
         src = np.array(corners).T.reshape((4, 2))
         dst = self.dst
         pt = ProjectiveTransform()
         pt.estimate(src, dst)
         return pt

-    def warp_image(self) -> np.ndarray:
-        """Warp a HiPS tile and a sky image"""
+    def _fetch_tiles(self) -> 'HipsTile':
+        """Generator function to fetch HiPS tiles from a remote URL."""
+        for healpix_pixel_index in self.tile_indices:
+            tile_meta = HipsTileMeta(
+                order=self.draw_hips_order,
+                ipix=healpix_pixel_index,
+                frame=self.hips_survey.astropy_frame,
+                file_format=self.tile_format,
+            )
+            url = self.hips_survey.tile_access_url(order=self.draw_hips_order, ipix=healpix_pixel_index) + tile_meta.filename
+            tile = HipsTile.fetch(tile_meta, url)
+            yield tile
+
+    @property
+    def tiles(self):
+        if self._tiles is None:
+            self._tiles = list(self._fetch_tiles())
+        return self._tiles
+
+    @property
+    def shape(self) -> Tuple[int]:
+        """Shape of the output image.
+
+        The shape will be two dimensional in case of FITS file format,
+        three dimensions (RGB) in case of JPG, and four channels (RGBA)
+        in case of PNG tile. We follow the same axis order and coordinate
+        conventions that are used by others for grayscale and RGB images.
+        """
+        if self.tile_format == 'jpg':
+            return self.geometry.shape.height, self.geometry.shape.width, 3
+        elif self.tile_format == 'png':
+            return self.geometry.shape.height, self.geometry.shape.width, 4
+        else:
+            return self.geometry.shape.height, self.geometry.shape.width
+
+    def warp_image(self, tile: HipsTile) -> np.ndarray:
+        """Warp a HiPS tile and a sky image."""
         return warp(
-            self.tile.data,
-            self.projection,
+            tile.data,
+            self.projection(tile),
             output_shape=self.geometry.shape,
             preserve_range=True,
         )

+    def draw_tiles(self):
+        """Draw HiPS tiles onto an empty image."""
+        image = np.zeros(self.shape)
+        for tile in self.tiles:
+            image += self.warp_image(tile)
+        return image

-def fetch_tiles(healpix_pixel_indices: np.ndarray, order: int,
-                hips_survey: HipsSurveyProperties, tile_format: str) -> 'HipsTile':
-    """Fetch HiPS tiles from a remote URL.
-
-    Parameters
-    ----------
-    healpix_pixel_indices : np.ndarray
-        A list of HEALPix pixel indices
-    order : int
-        Order of the HEALPix map
-    hips_survey : HipsSurveyProperties
-        An object of HipsSurveyProperties
-    tile_format : `str`
-        Format of HiPS tile
-
-    Returns
-    -------
-    'HipsTile'
-        Returns an object of HipsTile
-    """
-    for healpix_pixel_index in healpix_pixel_indices:
-        tile_meta = HipsTileMeta(
-            order=order,
-            ipix=healpix_pixel_index,
-            frame=hips_survey.astropy_frame,
-            file_format=tile_format,
-        )
-        tile = HipsTile.fetch(tile_meta, hips_survey.tile_access_url(order=order, ipix=healpix_pixel_index) + tile_meta.filename)
-        yield tile
-
-
-def compute_matching_hips_order(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -> int:
-    """Compute HiPS tile order matching a given image pixel size.
-
-    Parameters
-    ----------
-    geometry : WCSGeometry
-        Geometry of the output image
-    hips_survey : HipsSurveyProperties
-        An object of HipsSurveyProperties
-
-    Returns
-    -------
-    'int'
-        Returns HiPS order
-
-    Examples
-    --------
-    >>> from hips.draw import compute_matching_hips_order
-    >>> from astropy.coordinates import SkyCoord
-    >>> url = 'http://alasky.unistra.fr/DSS/DSS2Merged/properties'
-    >>> hips_survey = HipsSurveyProperties.fetch(url)
-    >>> geometry = WCSGeometry.create_simple(
-    ...     skydir=SkyCoord(0, 0, unit='deg', frame='icrs'),
-    ...     width=2000, height=1000, fov="3 deg",
-    ...     coordsys='icrs', projection='AIT'
-    ... )
-    >>> compute_matching_hips_order(geometry, hips_survey)
-    7
-    """
-
-    # Sky image angular resolution (pixel size in degree)
-    resolution = np.min(proj_plane_pixel_scales(geometry.wcs))
-    desired_order = _get_hips_order_for_resolution(hips_survey.tile_width, resolution)
-    # Return the desired order, or the highest resolution available.
-    # Note that HiPS never has resolution less than 3,
-    # and that limit is handled in _get_hips_order_for_resolution
-    return np.min([desired_order, hips_survey.hips_order])
-
-
-def _get_hips_order_for_resolution(tile_width, resolution):
-    """Finding the best HiPS order by looping through all possible options."""
-    tile_order = np.log2(tile_width)
-    full_sphere_area = 4 * np.pi * np.square(180 / np.pi)
-    # 29 is the maximum order supported by healpy and 3 is the minimum order
-    for candidate_tile_order in range(3, 29 + 1):
-        tile_resolution = np.sqrt(full_sphere_area / 12 / 4 ** (candidate_tile_order + tile_order))
-        # Finding the smaller tile order with a resolution equal or better than geometric resolution
-        if tile_resolution <= resolution:
-            break
-
-    return candidate_tile_order
+    def run(self) -> None:
+        """Run all steps of the naive algorithm."""
+        self.image = self.draw_tiles()


 def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties, tile_format: str) -> np.ndarray:
     """Make sky image: fetch tiles and draw.
+
     The example for this can be found on the :ref:`gs` page.

     Parameters
@@ -202,18 +174,11 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties, til

     Returns
     -------
-    data : `~numpy.ndarray`
+    image : `~numpy.ndarray`
         Output image pixels
     """
-    order = compute_matching_hips_order(geometry, hips_survey)
-    healpix_pixel_indices = compute_healpix_pixel_indices(
-        wcs_geometry=geometry,
-        order=order,
-        healpix_frame=hips_survey.astropy_frame,
-    )
-    # TODO: this isn't a good API. Will become better when we have a cache.
-    tiles = fetch_tiles(healpix_pixel_indices, order, hips_survey, tile_format)
-
-    image_data = draw_sky_image(geometry, tiles, hips_survey, tile_format)
-
-    return image_data
+
+    painter = SimpleTilePainter(geometry, hips_survey, tile_format)
+    painter.run()
+
+    return painter.image
diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py
index 4614d9e..7a9dba0 100644
--- a/hips/draw/tests/test_simple.py
+++ b/hips/draw/tests/test_simple.py
@@ -1,102 +1,76 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 import pytest
 import numpy as np
-import healpy as hp
-from astropy.coordinates import SkyCoord
 from numpy.testing import assert_allclose
+from astropy.coordinates import SkyCoord
 from astropy.tests.helper import remote_data
-from ..simple import make_sky_image, draw_sky_image, compute_matching_hips_order, _get_hips_order_for_resolution
-from ...tiles import HipsSurveyProperties, HipsTileMeta, HipsTile
-from ...utils import WCSGeometry
-from ...utils.testing import get_hips_extra_file, make_test_wcs_geometry, requires_hips_extra
-
-
-def get_test_tiles(file_format, survey, order, ipix_list):
-    filename = get_hips_extra_file('datasets/samples/' + survey + '/properties')
-    hips_survey = HipsSurveyProperties.read(filename)
-
-    tiles = []
-    for ipix in ipix_list:
-        tiles.append(HipsTile.read(
-            meta=HipsTileMeta(order=order, ipix=ipix, file_format=file_format, frame=hips_survey.astropy_frame),
-            full_path=get_hips_extra_file('datasets/samples/' + survey + hips_survey.tile_path(order=order, ipix=ipix, tile_format=file_format)),
-        ))
-
-    return tiles
-
-
-draw_sky_image_pars = [
-    dict(file_format='fits', shape=(1000, 2000), survey='DSS2Red', data_1=2866.0101409848185,
-         data_2=2563.6916727348043, data_sum=4575235421.512643, order=3, ipix_list=[450, 451]),
-    dict(file_format='jpg', shape=(1000, 2000, 3), survey='DSS2Red', data_1=[13.040878, 13.040878, 13.040878],
-         data_2=[17.235874, 17.235874, 17.235874], data_sum=243177268.56158745, order=3, ipix_list=[450, 451]),
-    dict(file_format='png', shape=(1000, 2000, 4), survey='AKARI-FIS', data_1=[254., 254., 254., 255.],
-         data_2=[254., 254., 254., 255.], data_sum=946809963.7487414, order=3, ipix_list=[450, 451])
+from ...tiles import HipsSurveyProperties
+from ..simple import make_sky_image, SimpleTilePainter
+from ...utils.wcs import WCSGeometry
+from ...utils.testing import make_test_wcs_geometry, requires_hips_extra
+
+make_sky_image_pars = [
+    dict(file_format='fits', shape=(1000, 2000), url='http://alasky.unistra.fr/DSS/DSS2Merged/properties',
+         data_1=2213.30874796, data_2=2296.93885940, data_sum=8757489268.044867),
+    dict(file_format='jpg', shape=(1000, 2000, 3), url='https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/FermiColor/properties',
+         data_1=[145.388459, 98.579295, 49.792577], data_2=[146.197811, 99.531895, 56.889927], data_sum=813159920.0305891),
+    dict(file_format='png', shape=(1000, 2000, 4), url='https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/AKARI-FIS/properties',
+         data_1=[249.064796, 237.805612, 190.408181, 255.], data_2=[249.013527, 238.79403, 195.793398, 255.], data_sum=1645783166.1630714)
 ]


 @remote_data
-@requires_hips_extra()
-@pytest.mark.parametrize('pars', draw_sky_image_pars)
-def test_draw_sky_image(pars):
+@pytest.mark.parametrize('pars', make_sky_image_pars)
+def test_make_sky_image(pars):
+    hips_survey = HipsSurveyProperties.fetch(url=pars['url'])
     geometry = make_test_wcs_geometry(case=2)
-    tiles = get_test_tiles(file_format=pars['file_format'], survey=pars['survey'], order=pars['order'], ipix_list=pars['ipix_list'])
-    url = 'https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/' + pars['survey'] + '/properties'
-    hips_survey = HipsSurveyProperties.fetch(url)
-
-    data = draw_sky_image(geometry=geometry, tiles=tiles, hips_survey=hips_survey, tile_format=pars['file_format'])
-
-    assert data.shape == pars['shape']
-    assert data.dtype == np.float64
-    assert_allclose(np.sum(data), pars['data_sum'])
-    assert_allclose(data[400, 500], pars['data_1'])
-    assert_allclose(data[400, 501], pars['data_2'])
+    image = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=pars['file_format'])
+    assert image.shape == pars['shape']
+    assert image.dtype == np.float64
+    assert_allclose(np.sum(image), pars['data_sum'])
+    assert_allclose(image[200, 994], pars['data_1'])
+    assert_allclose(image[200, 995], pars['data_2'])


 @remote_data
-def test_make_sky_image():
-    # The same example is used in the high level docs getting started page
-    url = 'http://alasky.unistra.fr/DSS/DSS2Merged/properties'
-    hips_survey = HipsSurveyProperties.fetch(url)
-    geometry = make_test_wcs_geometry(case=2)
-    data = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format='fits')
-    assert data.shape == geometry.shape
-    assert data.dtype == np.float64
-
-    assert_allclose(np.sum(data), 8757489268.044867)
-    assert_allclose(data[200, 994], 2213.30874796)
-    assert_allclose(data[200, 995], 2296.93885940)
-
-hips_order_pars = [
-    dict(order=7, fov="3 deg"),
-    dict(order=5, fov="10 deg"),
-    dict(order=4, fov="15 deg"),
-]
-
-
-@requires_hips_extra()
-@pytest.mark.parametrize('pars', hips_order_pars)
-def test_compute_matching_hips_order(pars):
-    full_path = get_hips_extra_file('datasets/samples/2MASS6XH/properties')
-    hips_survey = HipsSurveyProperties.read(filename=full_path)
-    geometry = WCSGeometry.create_simple(
-        skydir=SkyCoord(0, 0, unit='deg', frame='icrs'),
-        width=2000, height=1000, fov=pars['fov'],
-        coordsys='icrs', projection='AIT'
-    )
-    assert compute_matching_hips_order(geometry, hips_survey) == pars['order']
-
-get_hips_order_for_resolution_pars = [
-    dict(tile_width=512, resolution=0.01232, resolution_res=0.06395791924665553, order=4),
-    dict(tile_width=256, resolution=0.0016022, resolution_res=0.003997369952915971, order=8),
-    dict(tile_width=128, resolution=0.00009032, resolution_res=0.00012491781102862408, order=13),
-]
-
-
-@pytest.mark.parametrize('pars', get_hips_order_for_resolution_pars)
-def test_get_hips_order_for_resolution(pars):
-    hips_order = _get_hips_order_for_resolution(pars['tile_width'], pars['resolution'])
-    assert hips_order == pars['order']
-    hips_resolution = hp.nside2resol(hp.order2nside(hips_order))
-    assert_allclose(hips_resolution, pars['resolution_res'])
-# TODO: add tests for SimpleTilePainter with asserts on the intermediate computed things.
+class TestSimpleTilePainter:
+    @classmethod
+    def setup_class(cls):
+        url = 'http://alasky.unistra.fr/DSS/DSS2Merged/properties'
+        cls.hips_survey = HipsSurveyProperties.fetch(url)
+        cls.geometry = WCSGeometry.create_simple(
+            skydir=SkyCoord(0, 0, unit='deg', frame='icrs'),
+            width=2000, height=1000, fov="3 deg",
+            coordsys='icrs', projection='AIT'
+        )
+        cls.simple_tile_painter = SimpleTilePainter(cls.geometry, cls.hips_survey, 'fits')
+
+    def test_draw_hips_order(self):
+        assert self.simple_tile_painter.draw_hips_order == 7
+
+    def test_shape(self):
+        assert self.simple_tile_painter.shape == (1000, 2000)
+
+    def test_tile_indices(self):
+        assert list(self.simple_tile_painter.tile_indices)[:4] == [69623, 69627, 69628, 69629]
+
+    draw_hips_order_pars = [
+        dict(order=7, fov="3 deg"),
+        dict(order=5, fov="10 deg"),
+        dict(order=4, fov="15 deg"),
+    ]
+
+    @requires_hips_extra()
+    @pytest.mark.parametrize('pars', draw_hips_order_pars)
+    def test_compute_matching_hips_order(self, pars):
+        geometry = WCSGeometry.create_simple(
+            skydir=SkyCoord(0, 0, unit='deg', frame='icrs'),
+            width=2000, height=1000, fov=pars['fov'],
+            coordsys='icrs', projection='AIT'
+        )
+        simple_tile_painter = SimpleTilePainter(geometry, self.hips_survey, 'fits')
+        assert simple_tile_painter.draw_hips_order == pars['order']
+
+    def test_run(self):
+        self.simple_tile_painter.run()
+        assert_allclose(self.simple_tile_painter.image[200, 994], 2120.9609175)
diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py
index 6e8213f..50e314e 100644
--- a/hips/utils/healpix.py
+++ b/hips/utils/healpix.py
@@ -13,7 +13,8 @@ from .wcs import WCSGeometry

 __all__ = [
     'boundaries',
-    'compute_healpix_pixel_indices'
+    'compute_healpix_pixel_indices',
+    'get_hips_order_for_resolution'
 ]

 __doctest_skip__ = ['boundaries', 'compute_healpix_pixel_indices']
@@ -109,3 +110,30 @@ def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, order: int, healpix
     theta, phi = _skycoord_to_theta_phi(pixel_coords)
     ipix = hp.ang2pix(nside, theta, phi, nest=True)
     return np.unique(ipix)
+
+
+def get_hips_order_for_resolution(tile_width: int, resolution: int) -> int:
+    """Find the best HiPS order by looping through all possible options.
+
+    Parameters
+    ----------
+    tile_width : int
+        HiPS tile width
+    resolution : int
+        Sky image angular resolution (pixel size in degrees)
+
+    Returns
+    -------
+    candidate_tile_order : int
+        Best HiPS tile order
+    """
+    tile_order = np.log2(tile_width)
+    full_sphere_area = 4 * np.pi * np.square(180 / np.pi)
+    # 29 is the maximum order supported by healpy and 3 is the minimum order
+    for candidate_tile_order in range(3, 29 + 1):
+        tile_resolution = np.sqrt(full_sphere_area / 12 / 4 ** (candidate_tile_order + tile_order))
+        # Finding the smaller tile order with a resolution equal to or better than geometric resolution
+        if tile_resolution <= resolution:
+            break
+
+    return candidate_tile_order
diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py
index aff6dc3..6c8b231 100644
--- a/hips/utils/tests/test_healpix.py
+++ b/hips/utils/tests/test_healpix.py
@@ -4,7 +4,7 @@ import numpy as np
 from numpy.testing import assert_allclose
 from astropy.coordinates import SkyCoord
 import healpy as hp
-from ..healpix import boundaries, compute_healpix_pixel_indices
+from ..healpix import boundaries, compute_healpix_pixel_indices, get_hips_order_for_resolution
 from ..testing import make_test_wcs_geometry


@@ -33,3 +33,17 @@ def test_wcs_healpix_pixel_indices(pars):
     geometry = make_test_wcs_geometry(case=2)
     healpix_pixel_indices = compute_healpix_pixel_indices(geometry, order=3, healpix_frame=pars['frame'])
     assert list(healpix_pixel_indices) == pars['ipix']
+
+hips_order_for_resolution_pars = [
+    dict(tile_width=512, resolution=0.01232, resolution_res=0.06395791924665553, order=4),
+    dict(tile_width=256, resolution=0.0016022, resolution_res=0.003997369952915971, order=8),
+    dict(tile_width=128, resolution=0.00009032, resolution_res=0.00012491781102862408, order=13),
+]
+
+
+@pytest.mark.parametrize('pars', hips_order_for_resolution_pars)
+def test_get_hips_order_for_resolution(pars):
+    hips_order = get_hips_order_for_resolution(pars['tile_width'], pars['resolution'])
+    assert hips_order == pars['order']
+    hips_resolution = hp.nside2resol(hp.order2nside(hips_order))
+    assert_allclose(hips_resolution, pars['resolution_res'])

commit e2541ae378982e9261dde09ed7b389a8d4b1b47b
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Wed Jul 12 12:07:34 2017 +0500

    Add plot in high level docs

diff --git a/docs/getting_started.rst b/docs/getting_started.rst
index 05e9d49..bf8ff0d 100644
--- a/docs/getting_started.rst
+++ b/docs/getting_started.rst
@@ -54,7 +54,9 @@ Now you can then save the sky image to local disk e.g. FITS file format::

 or plot and analyse the sky image however you like.

-TODO: show how to plot the image with `astropy.visualization.wcsaxes`.
+If you execute the example above, you will get this sky image which was plotted using `astropy.visualization.wcsaxes`
+
+.. plot:: plot_fits.py

 HiPS data
 ---------
diff --git a/docs/plot_fits.py b/docs/plot_fits.py
new file mode 100644
index 0000000..67b03d9
--- /dev/null
+++ b/docs/plot_fits.py
@@ -0,0 +1,20 @@
+"""This script plots the all-sky image following the example on getting started page"""
+import matplotlib.pyplot as plt
+from astropy.coordinates import SkyCoord
+from astropy.visualization.mpl_normalize import simple_norm
+
+from hips import HipsSurveyProperties
+from hips import make_sky_image
+from hips.utils import WCSGeometry
+import numpy as np
+url = 'http://alasky.unistra.fr/DSS/DSS2Merged/properties'
+hips_survey = HipsSurveyProperties.fetch(url)
+geometry = WCSGeometry.create_simple(
+    skydir=SkyCoord(0, 0, unit='deg', frame='galactic'),
+    width=2000, height=1000, fov="3 deg",
+    coordsys='galactic', projection='AIT'
+)
+data = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format='fits')
+ax = plt.subplot(projection=geometry.wcs)
+norm = simple_norm(data, 'sqrt')
+ax.imshow(data, origin='lower', norm=norm, cmap='gray')
diff --git a/setup.cfg b/setup.cfg
index 36b3fd1..02f3659 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -11,6 +11,7 @@ show-response = 1
 minversion = 3.0
 norecursedirs = build docs/_build
 doctest_plus = enabled
+doctest_norecursedirs = docs/plot_fits.py

 [ah_bootstrap]
 auto_use = True

commit af4eb6f7cac27c06f60468912388dd2f33a3b851
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Wed Jul 12 10:17:36 2017 +0500

    Fix tile width height issue

diff --git a/docs/getting_started.rst b/docs/getting_started.rst
index 9248dd4..05e9d49 100644
--- a/docs/getting_started.rst
+++ b/docs/getting_started.rst
@@ -41,7 +41,7 @@ To make a sky image with the `hips` package, follow the following three steps:

     from hips.draw import make_sky_image

-    data = make_sky_image(geometry, hips_survey)
+    data = make_sky_image(geometry, hips_survey, 'fits')


 That's it. Go ahead and try it out for your favourite sky region and survey.
diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index c6d2ad5..2664a59 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -41,11 +41,11 @@ def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any],
         Returns a numpy array containing all HiPS tiles projected onto it
     """
     if tile_format == 'jpg':
-        shape = (geometry.shape.width, geometry.shape.height, 3)
+        shape = (geometry.shape.height, geometry.shape.width, 3)
     elif tile_format == 'png':
-        shape = (geometry.shape.width, geometry.shape.height, 4)
+        shape = (geometry.shape.height, geometry.shape.width, 4)
     else:
-        shape = (geometry.shape.width, geometry.shape.height)
+        shape = (geometry.shape.height, geometry.shape.width)
     image = np.zeros(shape)
     for tile in tiles:
         painter = SimpleTilePainter(geometry, hips_survey, tile)
diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py
index b910517..4614d9e 100644
--- a/hips/draw/tests/test_simple.py
+++ b/hips/draw/tests/test_simple.py
@@ -26,12 +26,12 @@ def get_test_tiles(file_format, survey, order, ipix_list):


 draw_sky_image_pars = [
-    dict(file_format='fits', shape=(2000, 1000), survey='DSS2Red', data_1=2866.0101409848185,
-         data_2=2563.6916727348043, data_sum=2992643842.729775, order=3, ipix_list=[450, 451]),
-    dict(file_format='jpg', shape=(2000, 1000, 3), survey='DSS2Red', data_1=[13.040878, 13.040878, 13.040878],
-         data_2=[17.235874, 17.235874, 17.235874], data_sum=155233709.20236143, order=3, ipix_list=[450, 451]),
-    dict(file_format='png', shape=(2000, 1000, 4), survey='AKARI-FIS', data_1=[254., 254., 254., 255.],
-         data_2=[254., 254., 254., 255.], data_sum=586208559.2450126, order=3, ipix_list=[450, 451])
+    dict(file_format='fits', shape=(1000, 2000), survey='DSS2Red', data_1=2866.0101409848185,
+         data_2=2563.6916727348043, data_sum=4575235421.512643, order=3, ipix_list=[450, 451]),
+    dict(file_format='jpg', shape=(1000, 2000, 3), survey='DSS2Red', data_1=[13.040878, 13.040878, 13.040878],
+         data_2=[17.235874, 17.235874, 17.235874], data_sum=243177268.56158745, order=3, ipix_list=[450, 451]),
+    dict(file_format='png', shape=(1000, 2000, 4), survey='AKARI-FIS', data_1=[254., 254., 254., 255.],
+         data_2=[254., 254., 254., 255.], data_sum=946809963.7487414, order=3, ipix_list=[450, 451])
 ]


@@ -62,7 +62,8 @@ def test_make_sky_image():
     data = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format='fits')
     assert data.shape == geometry.shape
     assert data.dtype == np.float64
-    assert_allclose(np.sum(data), 7615817463.1612253)
+
+    assert_allclose(np.sum(data), 8757489268.044867)
     assert_allclose(data[200, 994], 2213.30874796)
     assert_allclose(data[200, 995], 2296.93885940)

diff --git a/hips/tiles/tests/test_surveys.py b/hips/tiles/tests/test_surveys.py
index ee1d856..47aab83 100644
--- a/hips/tiles/tests/test_surveys.py
+++ b/hips/tiles/tests/test_surveys.py
@@ -5,6 +5,7 @@ from astropy.tests.helper import remote_data
 from ..surveys import HipsSurveyProperties, HipsSurveyPropertiesList
 from ...utils.testing import get_hips_extra_file, requires_hips_extra

+
 class TestHipsSurveyProperties:
     @classmethod
     def setup_class(cls):
diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py
index 9d0dd47..aff6dc3 100644
--- a/hips/utils/tests/test_healpix.py
+++ b/hips/utils/tests/test_healpix.py
@@ -23,8 +23,8 @@ def test_boundaries():


 compute_healpix_pixel_indices_pars = [
-    dict(frame='galactic', ipix=[269, 271, 280, 282, 283, 304, 305, 307, 308, 310]),
-    dict(frame='icrs', ipix=[448, 449, 450, 451, 454, 456, 457, 458, 459, 460]),
+    dict(frame='galactic', ipix=[269, 270, 271, 280, 282, 283, 292, 293, 295, 304, 305, 306]),
+    dict(frame='icrs', ipix=[448, 449, 450, 451, 454, 456, 457, 460, 661, 663, 669]),
 ]


diff --git a/hips/utils/wcs.py b/hips/utils/wcs.py
index 7dd3db2..305092b 100644
--- a/hips/utils/wcs.py
+++ b/hips/utils/wcs.py
@@ -15,7 +15,7 @@ __doctest_skip__ = [
     '*',
 ]

-Shape = namedtuple('Shape', ['width', 'height'])
+Shape = namedtuple('Shape', ['height', 'width'])
 """Helper for 2-dim image shape, to make it clearer what value is width and height."""



commit 8ca56a7036540d528fb891a811a9bec901086aa3
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Mon Jul 10 19:33:10 2017 +0500

    Add support for RGB tiles

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index bcac055..c6d2ad5 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -18,8 +18,10 @@ __doctest_skip__ = [
     'compute_matching_hips_order',
 ]

+
 # TODO: Fix type annotation issue
-def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any], hips_survey: HipsSurveyProperties) -> np.ndarray:
+def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any],
+                   hips_survey: HipsSurveyProperties, tile_format: str) -> np.ndarray:
     """Draw sky image using the simple and quick method.

     Parameters
@@ -30,13 +32,21 @@ def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any],
         A list of HipsTile
     hips_survey : `~hips.HipsSurveyProperties`
         HiPS survey properties
+    tile_format : `str`
+        Format of HiPS tile

     Returns
     -------
     np.ndarray
         Returns a numpy array containing all HiPS tiles projected onto it
     """
-    image = np.zeros(geometry.shape)
+    if tile_format == 'jpg':
+        shape = (geometry.shape.width, geometry.shape.height, 3)
+    elif tile_format == 'png':
+        shape = (geometry.shape.width, geometry.shape.height, 4)
+    else:
+        shape = (geometry.shape.width, geometry.shape.height)
+    image = np.zeros(shape)
     for tile in tiles:
         painter = SimpleTilePainter(geometry, hips_survey, tile)
         image += painter.warp_image()
@@ -45,7 +55,6 @@ def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any],

 class SimpleTilePainter:
     """Paint a single tile using a simple projective transformation method.
-
     The algorithm implemented is described here: :ref:`drawing_algo`.

     Parameters
@@ -73,6 +82,7 @@ class SimpleTilePainter:
              [0, width - 1],
              [0, 0]],
         )
+
     @property
     def projection(self) -> ProjectiveTransform:
         """Estimate projective transformation on a HiPS tile"""
@@ -93,7 +103,8 @@ class SimpleTilePainter:
         )


-def fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: HipsSurveyProperties) -> 'HipsTile':
+def fetch_tiles(healpix_pixel_indices: np.ndarray, order: int,
+                hips_survey: HipsSurveyProperties, tile_format: str) -> 'HipsTile':
     """Fetch HiPS tiles from a remote URL.

     Parameters
@@ -104,18 +115,20 @@ def fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: Hips
         Order of the HEALPix map
     hips_survey : HipsSurveyProperties
         An object of HipsSurveyProperties
+    tile_format : `str`
+        Format of HiPS tile

     Returns
     -------
     'HipsTile'
-        Returns an object of  HipsTile
+        Returns an object of HipsTile
     """
     for healpix_pixel_index in healpix_pixel_indices:
         tile_meta = HipsTileMeta(
             order=order,
             ipix=healpix_pixel_index,
             frame=hips_survey.astropy_frame,
-            file_format='fits',
+            file_format=tile_format,
         )
         tile = HipsTile.fetch(tile_meta, hips_survey.tile_access_url(order=order, ipix=healpix_pixel_index) + tile_meta.filename)
         yield tile
@@ -174,9 +187,8 @@ def _get_hips_order_for_resolution(tile_width, resolution):
     return candidate_tile_order


-def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -> np.ndarray:
+def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties, tile_format: str) -> np.ndarray:
     """Make sky image: fetch tiles and draw.
-
     The example for this can be found on the :ref:`gs` page.

     Parameters
@@ -185,6 +197,8 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) ->
         Geometry of the output image
     hips_survey : `~hips.HipsSurveyProperties`
         HiPS survey properties
+    tile_format : `str`
+        Format of HiPS tile

     Returns
     -------
@@ -198,8 +212,8 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) ->
         healpix_frame=hips_survey.astropy_frame,
     )
     # TODO: this isn't a good API. Will become better when we have a cache.
-    tiles = fetch_tiles(healpix_pixel_indices, order, hips_survey)
+    tiles = fetch_tiles(healpix_pixel_indices, order, hips_survey, tile_format)

-    image_data = draw_sky_image(geometry, tiles, hips_survey)
+    image_data = draw_sky_image(geometry, tiles, hips_survey, tile_format)

     return image_data
diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py
index 249d77e..b910517 100644
--- a/hips/draw/tests/test_simple.py
+++ b/hips/draw/tests/test_simple.py
@@ -4,45 +4,53 @@ import numpy as np
 import healpy as hp
 from astropy.coordinates import SkyCoord
 from numpy.testing import assert_allclose
-from astropy.utils.data import get_pkg_data_filename
 from astropy.tests.helper import remote_data
 from ..simple import make_sky_image, draw_sky_image, compute_matching_hips_order, _get_hips_order_for_resolution
 from ...tiles import HipsSurveyProperties, HipsTileMeta, HipsTile
 from ...utils import WCSGeometry
 from ...utils.testing import get_hips_extra_file, make_test_wcs_geometry, requires_hips_extra

-def get_test_tiles():
-    filename = get_pkg_data_filename('../../tiles/tests/data/properties.txt')
+
+def get_test_tiles(file_format, survey, order, ipix_list):
+    filename = get_hips_extra_file('datasets/samples/' + survey + '/properties')
     hips_survey = HipsSurveyProperties.read(filename)

-    tile1 = HipsTile.read(
-        meta=HipsTileMeta(order=3, ipix=450, file_format='fits', frame=hips_survey.astropy_frame),
-        full_path=get_hips_extra_file('datasets/samples/DSS2Red/Norder3/Dir0/Npix450.fits'),
-    )
+    tiles = []
+    for ipix in ipix_list:
+        tiles.append(HipsTile.read(
+            meta=HipsTileMeta(order=order, ipix=ipix, file_format=file_format, frame=hips_survey.astropy_frame),
+            full_path=get_hips_extra_file('datasets/samples/' + survey + hips_survey.tile_path(order=order, ipix=ipix, tile_format=file_format)),
+        ))

-    tile2 = HipsTile.read(
-        meta=HipsTileMeta(order=3, ipix=451, file_format='fits', frame=hips_survey.astropy_frame),
-        full_path=get_hips_extra_file('datasets/samples/DSS2Red/Norder3/Dir0/Npix451.fits'),
-    )
+    return tiles

-    return [tile1, tile2]
+
+draw_sky_image_pars = [
+    dict(file_format='fits', shape=(2000, 1000), survey='DSS2Red', data_1=2866.0101409848185,
+         data_2=2563.6916727348043, data_sum=2992643842.729775, order=3, ipix_list=[450, 451]),
+    dict(file_format='jpg', shape=(2000, 1000, 3), survey='DSS2Red', data_1=[13.040878, 13.040878, 13.040878],
+         data_2=[17.235874, 17.235874, 17.235874], data_sum=155233709.20236143, order=3, ipix_list=[450, 451]),
+    dict(file_format='png', shape=(2000, 1000, 4), survey='AKARI-FIS', data_1=[254., 254., 254., 255.],
+         data_2=[254., 254., 254., 255.], data_sum=586208559.2450126, order=3, ipix_list=[450, 451])
+]


 @remote_data
 @requires_hips_extra()
-def test_draw_sky_image():
+@pytest.mark.parametrize('pars', draw_sky_image_pars)
+def test_draw_sky_image(pars):
     geometry = make_test_wcs_geometry(case=2)
-    tiles = get_test_tiles()
-    url = 'https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties'
+    tiles = get_test_tiles(file_format=pars['file_format'], survey=pars['survey'], order=pars['order'], ipix_list=pars['ipix_list'])
+    url = 'https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/' + pars['survey'] + '/properties'
     hips_survey = HipsSurveyProperties.fetch(url)

-    data = draw_sky_image(geometry, tiles, hips_survey)
+    data = draw_sky_image(geometry=geometry, tiles=tiles, hips_survey=hips_survey, tile_format=pars['file_format'])

-    assert data.shape == geometry.shape
+    assert data.shape == pars['shape']
     assert data.dtype == np.float64
-    assert_allclose(np.sum(data), 2992643842.729775)
-    assert_allclose(data[400, 500], 2866.0101409848185)
-    assert_allclose(data[400, 501], 2563.6916727348043)
+    assert_allclose(np.sum(data), pars['data_sum'])
+    assert_allclose(data[400, 500], pars['data_1'])
+    assert_allclose(data[400, 501], pars['data_2'])


 @remote_data
@@ -51,9 +59,10 @@ def test_make_sky_image():
     url = 'http://alasky.unistra.fr/DSS/DSS2Merged/properties'
     hips_survey = HipsSurveyProperties.fetch(url)
     geometry = make_test_wcs_geometry(case=2)
-    data = make_sky_image(geometry, hips_survey)
+    data = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format='fits')
     assert data.shape == geometry.shape
     assert data.dtype == np.float64
+    assert_allclose(np.sum(data), 7615817463.1612253)
     assert_allclose(data[200, 994], 2213.30874796)
     assert_allclose(data[200, 995], 2296.93885940)

diff --git a/hips/tiles/surveys.py b/hips/tiles/surveys.py
index e93a579..bfe814d 100644
--- a/hips/tiles/surveys.py
+++ b/hips/tiles/surveys.py
@@ -150,6 +150,20 @@ class HipsSurveyProperties:
         """
         return self.base_url + '/Norder' + str(order) + '/Dir' + str(self.directory(ipix)) + '/'

+    def tile_path(self, order: int, ipix: int, tile_format: str) -> str:
+        """Tile access URL
+
+        Parameters
+        ----------
+        order : int
+            HiPS order
+        ipix : int
+            Index of the HiPS tile
+        tile_format : str
+            HiPS tile URL
+        """
+        return '/Norder' + str(order) + '/Dir' + str(self.directory(ipix)) + '/Npix' + str(ipix) + '.' + tile_format
+
     @property
     def hips_service_url(self) -> str:
         """HiPS service base URL (`str`)."""

commit 10ada966037b2a71aec42b3c85a9773d2bab6a48
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Sun Jul 9 11:23:26 2017 +0500

    Update base_url property in HipsSurveyProperties class

diff --git a/hips/tiles/surveys.py b/hips/tiles/surveys.py
index e93a579..248b112 100644
--- a/hips/tiles/surveys.py
+++ b/hips/tiles/surveys.py
@@ -69,16 +69,18 @@ class HipsSurveyProperties:

         with urllib.request.urlopen(url) as response:
             text = response.read().decode('utf-8')
-        return cls.parse(text)
+        return cls.parse(text, url)

     @classmethod
-    def parse(cls, text: str) -> 'HipsSurveyProperties':
+    def parse(cls, text: str, url: str = None) -> 'HipsSurveyProperties':
         """Parse HiPS survey description text (`HipsSurveyProperties`).

         Parameters
         ----------
         text : str
             Text containing HiPS survey properties
+        url : str
+            Properties URL of HiPS
         """
         data = OrderedDict()
         for line in text.split('\n'):
@@ -92,6 +94,9 @@ class HipsSurveyProperties:
                 # Skip bad lines (silently, might not be a good idea to do this)
                 continue

+        if url is not None:
+            data['properties_url'] = url.rsplit('/', 1)[0]
+
         return cls(data)

     @property
@@ -127,7 +132,16 @@ class HipsSurveyProperties:
     @property
     def base_url(self) -> str:
         """HiPS access url"""
-        return self.data['moc_access_url'].rsplit('/', 1)[0]
+        try:
+            return self.data['hips_service_url']
+        except KeyError:
+            try:
+                return self.data['moc_access_url'].rsplit('/', 1)[0]
+            except KeyError:
+                try:
+                    return self.data['properties_url']
+                except:
+                    return ValueError('URL does not exist!')

     @property
     def tile_width(self) -> int:
diff --git a/hips/tiles/tests/test_surveys.py b/hips/tiles/tests/test_surveys.py
index f1a031e..ee1d856 100644
--- a/hips/tiles/tests/test_surveys.py
+++ b/hips/tiles/tests/test_surveys.py
@@ -36,6 +36,13 @@ class TestHipsSurveyProperties:
         assert self.hips_survey_property.tile_access_url(order=9, ipix=54321) == 'http://alasky.u-strasbg.fr/DSS/DSSColor/Norder9/Dir50000/'


+@remote_data
+def test_base_url():
+    url = 'http://alasky.u-strasbg.fr/DSS/DSS2-NIR/properties'
+    survey = HipsSurveyProperties.fetch(url)
+
+    assert survey.base_url == 'http://alasky.u-strasbg.fr/DSS/DSS2-NIR'
+
     @requires_hips_extra()
     def test_tile_width(self):
         filename = get_hips_extra_file('datasets/samples/Planck-HFI143/properties')

commit c853878ab027abf70b2f771796ff7ae98c37c6f3
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Thu Jul 6 23:03:54 2017 +0500

    Add test case for get_order function

diff --git a/.travis.yml b/.travis.yml
index 3cbe1ae..ebe7b8a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -46,7 +46,7 @@ matrix:
     include:
         # Main build -- used for coverage
         - os: linux
-          env: SETUP_CMD='test --coverage'
+          env: SETUP_CMD='test --coverage --remote-data -V'

         # Docs build
         - os: linux
@@ -55,7 +55,7 @@ matrix:

         # Check that install / tests work with minimal required dependencies
         - os: linux
-          env: SETUP_CMD='test'
+          env: SETUP_CMD='test -V'
                CONDA_DEPENDENCIES='healpy scikit-image Pillow'

         # Try MacOS X
diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index 2fb930e..bcac055 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -10,9 +10,13 @@ from ..utils import WCSGeometry, compute_healpix_pixel_indices
 __all__ = [
     'draw_sky_image',
     'make_sky_image',
-    'SimpleTilePainter'
+    'SimpleTilePainter',
+    'compute_matching_hips_order'
 ]

+__doctest_skip__ = [
+    'compute_matching_hips_order',
+]

 # TODO: Fix type annotation issue
 def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any], hips_survey: HipsSurveyProperties) -> np.ndarray:
@@ -117,20 +121,57 @@ def fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: Hips
         yield tile


-def get_order(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -> int:
-    """Compute the tile order suited for the given geometry and hips_survey"""
+def compute_matching_hips_order(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -> int:
+    """Compute HiPS tile order matching a given image pixel size.
+
+    Parameters
+    ----------
+    geometry : WCSGeometry
+        Geometry of the output image
+    hips_survey : HipsSurveyProperties
+        An object of HipsSurveyProperties

+    Returns
+    -------
+    'int'
+        Returns HiPS order
+
+    Examples
+    --------
+    >>> from hips.draw import compute_matching_hips_order
+    >>> from astropy.coordinates import SkyCoord
+    >>> url = 'http://alasky.unistra.fr/DSS/DSS2Merged/properties'
+    >>> hips_survey = HipsSurveyProperties.fetch(url)
+    >>> geometry = WCSGeometry.create_simple(
+    ...     skydir=SkyCoord(0, 0, unit='deg', frame='icrs'),
+    ...     width=2000, height=1000, fov="3 deg",
+    ...     coordsys='icrs', projection='AIT'
+    ... )
+    >>> compute_matching_hips_order(geometry, hips_survey)
+    7
+    """
+
+    # Sky image angular resolution (pixel size in degree)
     resolution = np.min(proj_plane_pixel_scales(geometry.wcs))
+    desired_order = _get_hips_order_for_resolution(hips_survey.tile_width, resolution)
+    # Return the desired order, or the highest resolution available.
+    # Note that HiPS never has resolution less than 3,
+    # and that limit is handled in _get_hips_order_for_resolution
+    return np.min([desired_order, hips_survey.hips_order])
+

-    tile_order = np.log2(hips_survey.tile_width)
+def _get_hips_order_for_resolution(tile_width, resolution):
+    """Finding the best HiPS order by looping through all possible options."""
+    tile_order = np.log2(tile_width)
     full_sphere_area = 4 * np.pi * np.square(180 / np.pi)
+    # 29 is the maximum order supported by healpy and 3 is the minimum order
     for candidate_tile_order in range(3, 29 + 1):
-        tile_resolution = np.sqrt(full_sphere_area / 12 / 4**(candidate_tile_order + tile_order))
-
+        tile_resolution = np.sqrt(full_sphere_area / 12 / 4 ** (candidate_tile_order + tile_order))
+        # Finding the smaller tile order with a resolution equal or better than geometric resolution
         if tile_resolution <= resolution:
             break

-    return np.min([candidate_tile_order, hips_survey.hips_order])
+    return candidate_tile_order


 def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -> np.ndarray:
@@ -150,7 +191,7 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) ->
     data : `~numpy.ndarray`
         Output image pixels
     """
-    order = get_order(geometry, hips_survey)
+    order = compute_matching_hips_order(geometry, hips_survey)
     healpix_pixel_indices = compute_healpix_pixel_indices(
         wcs_geometry=geometry,
         order=order,
diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py
index 68b7855..249d77e 100644
--- a/hips/draw/tests/test_simple.py
+++ b/hips/draw/tests/test_simple.py
@@ -1,13 +1,16 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
+import pytest
 import numpy as np
+import healpy as hp
+from astropy.coordinates import SkyCoord
 from numpy.testing import assert_allclose
 from astropy.utils.data import get_pkg_data_filename
 from astropy.tests.helper import remote_data
-from ..simple import make_sky_image, draw_sky_image
+from ..simple import make_sky_image, draw_sky_image, compute_matching_hips_order, _get_hips_order_for_resolution
 from ...tiles import HipsSurveyProperties, HipsTileMeta, HipsTile
+from ...utils import WCSGeometry
 from ...utils.testing import get_hips_extra_file, make_test_wcs_geometry, requires_hips_extra

-
 def get_test_tiles():
     filename = get_pkg_data_filename('../../tiles/tests/data/properties.txt')
     hips_survey = HipsSurveyProperties.read(filename)
@@ -44,14 +47,46 @@ def test_draw_sky_image():

 @remote_data
 def test_make_sky_image():
-    url = 'https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties'
+    # The same example is used in the high level docs getting started page
+    url = 'http://alasky.unistra.fr/DSS/DSS2Merged/properties'
     hips_survey = HipsSurveyProperties.fetch(url)
     geometry = make_test_wcs_geometry(case=2)
     data = make_sky_image(geometry, hips_survey)
     assert data.shape == geometry.shape
     assert data.dtype == np.float64
-    assert_allclose(data[200, 994], 3717.10091363)
-    assert_allclose(data[200, 995], 3402.55292158)
+    assert_allclose(data[200, 994], 2213.30874796)
+    assert_allclose(data[200, 995], 2296.93885940)
+
+hips_order_pars = [
+    dict(order=7, fov="3 deg"),
+    dict(order=5, fov="10 deg"),
+    dict(order=4, fov="15 deg"),
+]
+
+
+@requires_hips_extra()
+@pytest.mark.parametrize('pars', hips_order_pars)
+def test_compute_matching_hips_order(pars):
+    full_path = get_hips_extra_file('datasets/samples/2MASS6XH/properties')
+    hips_survey = HipsSurveyProperties.read(filename=full_path)
+    geometry = WCSGeometry.create_simple(
+        skydir=SkyCoord(0, 0, unit='deg', frame='icrs'),
+        width=2000, height=1000, fov=pars['fov'],
+        coordsys='icrs', projection='AIT'
+    )
+    assert compute_matching_hips_order(geometry, hips_survey) == pars['order']
+
+get_hips_order_for_resolution_pars = [
+    dict(tile_width=512, resolution=0.01232, resolution_res=0.06395791924665553, order=4),
+    dict(tile_width=256, resolution=0.0016022, resolution_res=0.003997369952915971, order=8),
+    dict(tile_width=128, resolution=0.00009032, resolution_res=0.00012491781102862408, order=13),
+]


+@pytest.mark.parametrize('pars', get_hips_order_for_resolution_pars)
+def test_get_hips_order_for_resolution(pars):
+    hips_order = _get_hips_order_for_resolution(pars['tile_width'], pars['resolution'])
+    assert hips_order == pars['order']
+    hips_resolution = hp.nside2resol(hp.order2nside(hips_order))
+    assert_allclose(hips_resolution, pars['resolution_res'])
 # TODO: add tests for SimpleTilePainter with asserts on the intermediate computed things.

commit e1955f6ef699669ff4209cf9a72d26488af6154e
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Thu Jul 6 21:48:05 2017 +0500

    Add function for computing HiPS order

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index c912b62..2fb930e 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -2,6 +2,7 @@
 """HiPS tile drawing -- simple method."""
 from typing import Generator, Any
 import numpy as np
+from astropy.wcs.utils import proj_plane_pixel_scales
 from skimage.transform import ProjectiveTransform, warp
 from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta
 from ..utils import WCSGeometry, compute_healpix_pixel_indices
@@ -116,6 +117,22 @@ def fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: Hips
         yield tile


+def get_order(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -> int:
+    """Compute the tile order suited for the given geometry and hips_survey"""
+
+    resolution = np.min(proj_plane_pixel_scales(geometry.wcs))
+
+    tile_order = np.log2(hips_survey.tile_width)
+    full_sphere_area = 4 * np.pi * np.square(180 / np.pi)
+    for candidate_tile_order in range(3, 29 + 1):
+        tile_resolution = np.sqrt(full_sphere_area / 12 / 4**(candidate_tile_order + tile_order))
+
+        if tile_resolution <= resolution:
+            break
+
+    return np.min([candidate_tile_order, hips_survey.hips_order])
+
+
 def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -> np.ndarray:
     """Make sky image: fetch tiles and draw.

@@ -133,13 +150,14 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) ->
     data : `~numpy.ndarray`
         Output image pixels
     """
+    order = get_order(geometry, hips_survey)
     healpix_pixel_indices = compute_healpix_pixel_indices(
         wcs_geometry=geometry,
-        order=hips_survey.hips_order,
+        order=order,
         healpix_frame=hips_survey.astropy_frame,
     )
     # TODO: this isn't a good API. Will become better when we have a cache.
-    tiles = fetch_tiles(healpix_pixel_indices, hips_survey.hips_order, hips_survey)
+    tiles = fetch_tiles(healpix_pixel_indices, order, hips_survey)

     image_data = draw_sky_image(geometry, tiles, hips_survey)


commit 8cdd34cad90facf9348d9bf7014260e9f75aae77
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Mon Jul 3 15:11:40 2017 +0500

    Add classmethod create_simple to WCSGeometry

diff --git a/docs/getting_started.rst b/docs/getting_started.rst
index 3133c61..9248dd4 100644
--- a/docs/getting_started.rst
+++ b/docs/getting_started.rst
@@ -20,10 +20,10 @@ To make a sky image with the `hips` package, follow the following three steps:
     from astropy.coordinates import SkyCoord
     from hips.utils import WCSGeometry

-    geometry = WCSGeometry.create(
+    geometry = WCSGeometry.create_simple(
          skydir=SkyCoord(0, 0, unit='deg', frame='galactic'),
-         shape=(1000, 2000), coordsys='GAL',
-         projection='AIT', cdelt=0.01, crpix=(1000, 500),
+         width=2000, height=1000, fov="3 deg",
+         coordsys='galactic', projection='AIT'
     )


diff --git a/hips/utils/testing.py b/hips/utils/testing.py
index 0563f7a..cdbbbcf 100644
--- a/hips/utils/testing.py
+++ b/hips/utils/testing.py
@@ -28,7 +28,6 @@ def has_hips_extra():
         path = Path(os.environ['HIPS_EXTRA']) / 'datasets/samples/DSS2Red/properties'
         if path.is_file():
             return True
-
     return False


@@ -43,19 +42,19 @@ def make_test_wcs_geometry(case=0):
     if case == 0:
         return WCSGeometry.create(
             skydir=SkyCoord(3, 4, unit='deg', frame='galactic'),
-            shape=(2, 3), coordsys='GAL',
+            width=2, height=3, coordsys='galactic',
             projection='CAR', cdelt=1.0, crpix=(1, 1),
         )
     elif case == 1:
         return WCSGeometry.create(
             skydir=SkyCoord(10, 20, unit='deg', frame='galactic'),
-            shape=(10, 20), coordsys='GAL',
+            width=20, height=10, coordsys='galactic',
             projection='CAR', cdelt=1.0, crpix=(1, 1),
         )
     elif case == 2:
         return WCSGeometry.create(
             skydir=SkyCoord(0, 0, unit='deg', frame='galactic'),
-            shape=(1000, 2000), coordsys='GAL',
+            width=2000, height=1000, coordsys='galactic',
             projection='AIT', cdelt=0.01, crpix=(1000, 500),
         )
     else:
diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py
index 9094894..9d0dd47 100644
--- a/hips/utils/tests/test_healpix.py
+++ b/hips/utils/tests/test_healpix.py
@@ -5,7 +5,7 @@ from numpy.testing import assert_allclose
 from astropy.coordinates import SkyCoord
 import healpy as hp
 from ..healpix import boundaries, compute_healpix_pixel_indices
-from .test_wcs import make_test_wcs_geometry
+from ..testing import make_test_wcs_geometry


 def test_boundaries():
@@ -23,8 +23,8 @@ def test_boundaries():


 compute_healpix_pixel_indices_pars = [
-    dict(frame='galactic', ipix=[269, 270, 271, 280, 282, 283, 292, 293, 295, 304, 305, 306]),
-    dict(frame='icrs', ipix=[448, 449, 450, 451, 454, 456, 457, 460, 661, 663, 669]),
+    dict(frame='galactic', ipix=[269, 271, 280, 282, 283, 304, 305, 307, 308, 310]),
+    dict(frame='icrs', ipix=[448, 449, 450, 451, 454, 456, 457, 458, 459, 460]),
 ]


diff --git a/hips/utils/tests/test_wcs.py b/hips/utils/tests/test_wcs.py
index e4f6749..6673f69 100644
--- a/hips/utils/tests/test_wcs.py
+++ b/hips/utils/tests/test_wcs.py
@@ -1,18 +1,39 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 from numpy.testing import assert_allclose
-from ..testing import make_test_wcs_geometry
-
+from astropy.coordinates import SkyCoord
+from ..wcs import WCSGeometry

 class TestWCSGeometry:
     def setup(self):
-        self.wcs_geometry = make_test_wcs_geometry(case=0)
+        self.wcs_geometry = WCSGeometry.create_simple(
+            skydir=SkyCoord(0, 0, unit='deg', frame='galactic'),
+            width=2000, height=1000, fov="3 deg",
+            coordsys='galactic', projection='AIT'
+        )

-    def test_center(self):
+    def test_galactic_frame_center(self):
         c = self.wcs_geometry.center_skycoord
+
         assert c.frame.name == 'galactic'
-        assert_allclose(c.l.deg, 2, atol=1e-2)
-        assert_allclose(c.b.deg, 4.5, atol=1e-2)
+        assert_allclose(c.l.deg, 359.99, atol=1e-2)
+        assert_allclose(c.b.deg, 0.00075, atol=1e-2)
+        assert_allclose(self.wcs_geometry.wcs.wcs.crpix, [1000., 500.])
+        assert_allclose(self.wcs_geometry.wcs.wcs.cdelt, [-0.0015, 0.0015])

     def test_celestial_frame(self):
-        pass
-        # TODO (Adeel): add a test
+        wcs_geometry = WCSGeometry.create_simple(
+            skydir=SkyCoord(0, 0, unit='deg', frame='icrs'),
+            width=2000, height=1000, fov="3 deg",
+            coordsys='icrs', projection='AIT'
+        )
+        c = wcs_geometry.center_skycoord
+
+        assert c.frame.name == 'icrs'
+        assert_allclose(c.ra.deg, 359.99, atol=1e-2)
+        assert_allclose(c.dec.deg, 0.00075, atol=1e-2)
+        assert_allclose(self.wcs_geometry.wcs.wcs.crpix, [1000., 500.])
+        assert_allclose(self.wcs_geometry.wcs.wcs.cdelt, [-0.0015, 0.0015])
+
+    def skycoord_separation(self):
+        skydir=SkyCoord(0, 0, unit='deg', frame='icrs')
+        skydir.separation(skydir).degree == 2
diff --git a/hips/utils/wcs.py b/hips/utils/wcs.py
index 5e43f6f..31e2e9b 100644
--- a/hips/utils/wcs.py
+++ b/hips/utils/wcs.py
@@ -4,16 +4,19 @@ from typing import Tuple
 import numpy as np
 from astropy.coordinates import SkyCoord
 from astropy.wcs import WCS
+from astropy.coordinates import Angle
 from astropy.wcs.utils import pixel_to_skycoord, wcs_to_celestial_frame

 __all__ = [
     'WCSGeometry',
 ]

-__doctest_skip__ = ['WCSGeometry']
+__doctest_skip__ = [
+    '*',
+]

-Shape = namedtuple('Shape', ['ny', 'nx'])
-"""Helper for 2-dim image shape, to make it clearer what value is x and y."""
+Shape = namedtuple('Shape', ['width', 'height'])
+"""Helper for 2-dim image shape, to make it clearer what value is width and height."""


 class WCSGeometry:
@@ -23,8 +26,10 @@ class WCSGeometry:
     ----------
     wcs : `~astropy.wcs.WCS`
         WCS projection object
-    shape : tuple
-        Shape of the image
+    width : int
+        Width of the image in pixels
+    height : int
+        Height of the image in pixels

     Examples
     --------
@@ -32,7 +37,7 @@ class WCSGeometry:
     >>> from hips.utils import WCSGeometry
     >>> skycoord = SkyCoord(10, 20, unit='deg')
     >>> wcs_geometry = WCSGeometry.create(
-    ...     skydir=skycoord, shape=(10, 20),
+    ...     skydir=skycoord, width=20, height=10,
     ...     coordsys='CEL', projection='AIT',
     ...     cdelt=1.0, crpix=(1., 1.),
     ... )
@@ -46,20 +51,20 @@ class WCSGeometry:
     CDELT : -1.0  1.0
     NAXIS : 0  0
     >>> wcs_geometry.shape
-    Shape(ny=10, nx=20)
+    Shape(width=20, height=10)
     """
     WCS_ORIGIN_DEFAULT = 0
     """Default WCS transform origin, to be used in all WCS pix <-> world calls."""

-    def __init__(self, wcs: WCS, shape: tuple) -> None:
+    def __init__(self, wcs: WCS, width: int, height: int) -> None:
         self.wcs = wcs
-        self.shape = Shape(*shape)
+        self.shape = Shape(*(width, height))

     @property
     def center_pix(self) -> Tuple[float, float]:
         """Image center in pixel coordinates (tuple of x, y)."""
-        x = float(self.shape.nx - 1) / 2
-        y = float(self.shape.ny - 1) / 2
+        x = float(self.shape.width - 1) / 2
+        y = float(self.shape.height - 1) / 2
         return x, y

     @property
@@ -78,7 +83,7 @@ class WCSGeometry:
         return pixel_to_skycoord(x, y, self.wcs, self.WCS_ORIGIN_DEFAULT)

     @classmethod
-    def create(cls, skydir: SkyCoord, shape: tuple, coordsys: str = 'CEL',
+    def create(cls, skydir: SkyCoord, width: int, height: int, coordsys: str = 'icrs',
                projection: str = 'AIT', cdelt: float = 1.0, crpix: tuple = (1., 1.)) -> 'WCSGeometry':
         """Create WCS object programmatically (`WCSGeometry`).

@@ -86,27 +91,30 @@ class WCSGeometry:
         ----------
         skydir : `~astropy.coordinates.SkyCoord`
             Sky coordinate of the WCS reference point
-        shape : `tuple`
-            Shape of the image (Numpy axis order: y, x)
-        coordsys : `str`
+        width : `int`
+            Width of the image in pixels
+        height : `int`
+            Height of the image in pixels
+        coordsys : {'icrs', 'galactic'}
             Coordinate system
         projection : `str`
-            Projection of the WCS object
+            Projection of the WCS object.
+            To see list of supported projections
+            visit: http://docs.astropy.org/en/stable/wcs/#supported-projections
         cdelt : `float`
             Coordinate increment at reference point
         crpix : `tuple`
             Pixel coordinates of reference point
             (WCS axis order: x, y and FITS convention origin=1)
         """
-
         w = WCS(naxis=2)

-        if coordsys == 'CEL':
+        if coordsys == 'icrs':
             w.wcs.ctype[0] = f'RA---{projection}'
             w.wcs.ctype[1] = f'DEC--{projection}'
             w.wcs.crval[0] = skydir.icrs.ra.deg
             w.wcs.crval[1] = skydir.icrs.dec.deg
-        elif coordsys == 'GAL':
+        elif coordsys == 'galactic':
             w.wcs.ctype[0] = f'GLON-{projection}'
             w.wcs.ctype[1] = f'GLAT-{projection}'
             w.wcs.crval[0] = skydir.galactic.l.deg
@@ -122,7 +130,57 @@ class WCSGeometry:

         w = WCS(w.to_header())

-        return cls(w, shape)
+        return cls(w, width, height)
+
+    @classmethod
+    def create_simple(cls, skydir: SkyCoord, width: int, height: int, fov: {str, Angle},
+                      coordsys: str = 'icrs', projection: str = 'AIT') -> 'WCSGeometry':
+        """Create WCS object programmatically using field of view (`WCSGeometry`).
+
+        Parameters
+        ----------
+        skydir : `~astropy.coordinates.SkyCoord`
+            Sky coordinate of the WCS reference point
+        width : `int`
+            Width of the image in pixels
+        height : `int`
+            Height of the image in pixels
+        fov: `str` or Angle
+            Field of view
+        coordsys : {'icrs', 'galactic'}
+            Coordinate system
+        projection : `str`
+            Projection of the WCS object.
+            To see list of supported projections
+            visit: http://docs.astropy.org/en/stable/wcs/#supported-projections
+
+        Examples
+        --------
+        >>> from astropy.coordinates import SkyCoord
+        >>> from hips.utils import WCSGeometry
+        >>> skycoord = SkyCoord(10, 20, unit='deg')
+        >>> wcs_geometry = WCSGeometry.create_simple(
+        ...     skydir=SkyCoord(0, 0, unit='deg', frame='galactic'),
+        ...     width=2000, height=1000, fov="3 deg",
+        ...     coordsys='galactic', projection='AIT'
+        ... )
+        >>> wcs_geometry.wcs
+        Number of WCS axes: 2
+        CTYPE : 'GLON-AIT'  'GLAT-AIT'
+        CRVAL : 0.0  0.0
+        CRPIX : 500.0  1000.0
+        PC1_1 PC1_2  : 1.0  0.0
+        PC2_1 PC2_2  : 0.0  1.0
+        CDELT : -0.0015  0.0015
+        NAXIS : 0  0
+        >>> wcs_geometry.shape
+        Shape(width=2000, height=1000)
+        """
+        fov = Angle(fov)
+        crpix = (float(width / 2), float(height / 2))
+        cdelt = float(fov.degree) / float(max(width, height))
+
+        return cls.create(skydir, width, height, coordsys, projection, cdelt, crpix)

     @property
     def celestial_frame(self) -> str:

commit 439bd58bc190c9d957ce4fce9e0acf9c45a40537
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Wed Jul 5 14:40:03 2017 +0500

    Remove tile_width from HipsTileMeta

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index eb28f51..c912b62 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -14,7 +14,7 @@ __all__ = [


 # TODO: Fix type annotation issue
-def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any]) -> np.ndarray:
+def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any], hips_survey: HipsSurveyProperties) -> np.ndarray:
     """Draw sky image using the simple and quick method.

     Parameters
@@ -23,6 +23,8 @@ def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any])
         An object of WCSGeometry
     tiles : List[HipsTile]
         A list of HipsTile
+    hips_survey : `~hips.HipsSurveyProperties`
+        HiPS survey properties

     Returns
     -------
@@ -31,7 +33,7 @@ def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any])
     """
     image = np.zeros(geometry.shape)
     for tile in tiles:
-        painter = SimpleTilePainter(geometry, tile)
+        painter = SimpleTilePainter(geometry, hips_survey, tile)
         image += painter.warp_image()
     return image

@@ -45,20 +47,33 @@ class SimpleTilePainter:
     ----------
     geometry : `~hips.utils.WCSGeometry`
         An object of WCSGeometry
+    hips_survey : `~hips.HipsSurveyProperties`
+        HiPS survey properties
     tile : `HipsTile`
        An object of HipsTile
     """

-    def __init__(self, geometry: WCSGeometry, tile: HipsTile) -> None:
+    def __init__(self, geometry: WCSGeometry, hips_survey: HipsSurveyProperties, tile: HipsTile) -> None:
         self.geometry = geometry
+        self.hips_survey = hips_survey
         self.tile = tile

     @property
+    def dst(self) -> np.ndarray:
+        """Destination array for projective transform"""
+        width = self.hips_survey.tile_width
+        return np.array(
+            [[width - 1, 0],
+             [width - 1, width - 1],
+             [0, width - 1],
+             [0, 0]],
+        )
+    @property
     def projection(self) -> ProjectiveTransform:
         """Estimate projective transformation on a HiPS tile"""
         corners = self.tile.meta.skycoord_corners.to_pixel(self.geometry.wcs)
         src = np.array(corners).T.reshape((4, 2))
-        dst = self.tile.meta.dst
+        dst = self.dst
         pt = ProjectiveTransform()
         pt.estimate(src, dst)
         return pt
@@ -126,6 +141,6 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) ->
     # TODO: this isn't a good API. Will become better when we have a cache.
     tiles = fetch_tiles(healpix_pixel_indices, hips_survey.hips_order, hips_survey)

-    image_data = draw_sky_image(geometry, tiles)
+    image_data = draw_sky_image(geometry, tiles, hips_survey)

     return image_data
diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py
index ec6bf1a..276c301 100644
--- a/hips/draw/tests/test_simple.py
+++ b/hips/draw/tests/test_simple.py
@@ -13,25 +13,27 @@ def get_test_tiles():
     hips_survey = HipsSurveyProperties.read(filename)

     tile1 = HipsTile.read(
-        meta=HipsTileMeta(order=3, ipix=450, file_format='fits', frame=hips_survey.astropy_frame,
-                          tile_width=512),
+        meta=HipsTileMeta(order=3, ipix=450, file_format='fits', frame=hips_survey.astropy_frame),
         full_path=get_hips_extra_file('datasets/samples/DSS2Red/Norder3/Dir0/Npix450.fits'),
     )

     tile2 = HipsTile.read(
-        meta=HipsTileMeta(order=3, ipix=451, file_format='fits', frame=hips_survey.astropy_frame,
-                          tile_width=512),
+        meta=HipsTileMeta(order=3, ipix=451, file_format='fits', frame=hips_survey.astropy_frame),
         full_path=get_hips_extra_file('datasets/samples/DSS2Red/Norder3/Dir0/Npix451.fits'),
     )

     return [tile1, tile2]


+@remote_data
 @requires_hips_extra()
 def test_draw_sky_image():
     geometry = make_test_wcs_geometry(case=2)
     tiles = get_test_tiles()
-    data = draw_sky_image(geometry, tiles)
+    url = 'https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties'
+    hips_survey = HipsSurveyProperties.fetch(url)
+
+    data = draw_sky_image(geometry, tiles, hips_survey)

     assert data.shape == geometry.shape
     assert data.dtype == np.float64
diff --git a/hips/tiles/surveys.py b/hips/tiles/surveys.py
index 8013ef5..0190a06 100644
--- a/hips/tiles/surveys.py
+++ b/hips/tiles/surveys.py
@@ -128,7 +128,13 @@ class HipsSurveyProperties:
         """HiPS access url"""
         return self.data['moc_access_url'].rsplit('/', 1)[0]

+    @property
+    def tile_width(self) -> int:
+        """HiPS tile width"""
+        return int(self.data['hips_tile_width']) or 512
+
     def directory(self, ipix: int) -> int:
+        """Directory index containing HiPS tile(s)"""
         return (ipix // 10000) * 10000

     def tile_access_url(self, order: int, ipix: int) -> str:
diff --git a/hips/tiles/tests/test_surveys.py b/hips/tiles/tests/test_surveys.py
index df4e812..5192f45 100644
--- a/hips/tiles/tests/test_surveys.py
+++ b/hips/tiles/tests/test_surveys.py
@@ -2,7 +2,7 @@
 from astropy.utils.data import get_pkg_data_filename
 from astropy.tests.helper import remote_data
 from ..surveys import HipsSurveyProperties, HipsSurveyPropertiesList
-
+from ...utils.testing import get_hips_extra_file, requires_hips_extra

 class TestHipsSurveyProperties:
     @classmethod
@@ -35,6 +35,13 @@ class TestHipsSurveyProperties:
         assert self.hips_survey_property.tile_access_url(order=9, ipix=54321) == 'http://alasky.u-strasbg.fr/DSS/DSSColor/Norder9/Dir50000/'


+    @requires_hips_extra()
+    def test_tile_width(self):
+        filename = get_hips_extra_file('datasets/samples/Planck-HFI143/properties')
+        survey = HipsSurveyProperties.read(filename)
+        assert survey.tile_width == 256
+
+
 class TestHipsSurveyPropertiesList:
     @classmethod
     def setup_class(cls):
diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py
index 62aa85d..022ad92 100644
--- a/hips/tiles/tests/test_tile.py
+++ b/hips/tiles/tests/test_tile.py
@@ -69,7 +69,7 @@ class TestHipsTile:
 class TestHipsTileMeta:
     @classmethod
     def setup_class(cls):
-        cls.meta = HipsTileMeta(order=3, ipix=450, file_format='fits', frame='icrs', tile_width=512)
+        cls.meta = HipsTileMeta(order=3, ipix=450, file_format='fits', frame='icrs')

     def test_path(self):
         assert str(self.meta.path) == 'hips/tiles/tests/data'
@@ -83,16 +83,12 @@ class TestHipsTileMeta:
     def test_nside(self):
         assert self.meta.nside == 8

-    def test_dst(self):
-        dst = np.array([[511, 0], [511, 511], [0, 511], [0, 0]])
-        assert_allclose(self.meta.dst, dst)
-
     def test_skycoord_corners(self):
         assert_allclose(self.meta.skycoord_corners.data.lat.deg, [-24.624318, -30., -35.685335, -30.])
         assert_allclose(self.meta.skycoord_corners.data.lon.deg, [264.375, 258.75, 264.375, 270.])
         assert self.meta.skycoord_corners.frame.name == 'icrs'

-        meta = HipsTileMeta(order=3, ipix=450, file_format='fits', frame='galactic', tile_width=512)
+        meta = HipsTileMeta(order=3, ipix=450, file_format='fits', frame='galactic')
         assert_allclose(meta.skycoord_corners.data.lat.deg, [-24.624318, -30., -35.685335, -30.])
         assert_allclose(meta.skycoord_corners.data.lon.deg, [264.375, 258.75, 264.375, 270.])
         assert meta.skycoord_corners.frame.name == 'galactic'
diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py
index 550969c..1868daf 100644
--- a/hips/tiles/tile.py
+++ b/hips/tiles/tile.py
@@ -34,32 +34,28 @@ class HipsTileMeta:
         File format
     frame : {'icrs', 'galactic', 'ecliptic'}
         Sky coordinate frame
-    tile_width : `int`
-        Tile width (in pixels)

     Examples
     --------
     >>> from hips.tiles import HipsTileMeta
-    >>> tile_meta = HipsTileMeta(order=3, ipix=450, file_format='fits', frame='icrs', tile_width=512)
+    >>> tile_meta = HipsTileMeta(order=3, ipix=450, file_format='fits', frame='icrs')
     >>> tile_meta.skycoord_corners
     <SkyCoord (ICRS): (ra, dec) in deg
     [( 264.375, -24.62431835), ( 258.75 , -30.        ),
     ( 264.375, -35.68533471), ( 270.   , -30.        )]>
     """

-    def __init__(self, order: int, ipix: int, file_format: str, frame: str = 'galactic', tile_width: int = 512) -> None:
+    def __init__(self, order: int, ipix: int, file_format: str, frame: str = 'galactic') -> None:
         self.order = order
         self.ipix = ipix
         self.file_format = file_format
         self.frame = frame
-        self.tile_width = tile_width

     def __eq__(self, other: 'HipsTileMeta') -> bool:
         return (
             self.order == other.order and
             self.ipix == other.ipix and
-            self.file_format == other.file_format and
-            self.tile_width == other.tile_width
+            self.file_format == other.file_format
         )

     @property
@@ -83,16 +79,6 @@ class HipsTileMeta:
         return hp.order2nside(self.order)

     @property
-    def dst(self) -> np.ndarray:
-        """Destination array for projective transform"""
-        return np.array(
-            [[self.tile_width - 1, 0],
-             [self.tile_width - 1, self.tile_width - 1],
-             [0, self.tile_width - 1],
-             [0, 0]],
-        )
-
-    @property
     def skycoord_corners(self) -> SkyCoord:
         """Corner values for a HiPS tile"""
         theta, phi = boundaries(self.nside, self.ipix)

commit 18fc6b656fe873955c10d7505c207cfed030615f
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Wed Jul 5 13:26:31 2017 +0500

    Refactor test cases for HipsTile

diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py
index e6324bf..ec6bf1a 100644
--- a/hips/draw/tests/test_simple.py
+++ b/hips/draw/tests/test_simple.py
@@ -15,13 +15,13 @@ def get_test_tiles():
     tile1 = HipsTile.read(
         meta=HipsTileMeta(order=3, ipix=450, file_format='fits', frame=hips_survey.astropy_frame,
                           tile_width=512),
-        filename=get_hips_extra_file('datasets/samples/DSS2Red/Norder3/Dir0/Npix450.fits'),
+        full_path=get_hips_extra_file('datasets/samples/DSS2Red/Norder3/Dir0/Npix450.fits'),
     )

     tile2 = HipsTile.read(
         meta=HipsTileMeta(order=3, ipix=451, file_format='fits', frame=hips_survey.astropy_frame,
                           tile_width=512),
-        filename=get_hips_extra_file('datasets/samples/DSS2Red/Norder3/Dir0/Npix451.fits'),
+        full_path=get_hips_extra_file('datasets/samples/DSS2Red/Norder3/Dir0/Npix451.fits'),
     )

     return [tile1, tile2]
diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py
index a3e972d..62aa85d 100644
--- a/hips/tiles/tests/test_tile.py
+++ b/hips/tiles/tests/test_tile.py
@@ -1,62 +1,70 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
+import pytest
 import numpy as np
 from astropy.tests.helper import remote_data
 from numpy.testing import assert_allclose
 from numpy.testing import assert_equal
 from ..tile import HipsTile, HipsTileMeta
-
+from ...utils.testing import get_hips_extra_file, requires_hips_extra

 class TestHipsTile:
-    @remote_data
-    def test_fetch_read_write_fits(self, tmpdir):
-        meta = HipsTileMeta(order=6, ipix=30889, file_format='fits')
-        url = 'http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.fits'
-        tile = HipsTile.fetch(meta, url)
-
-        assert tile.data.shape == (512, 512)
-        assert_equal(tile.data[510][5:7], [1, 0])

-        filename = str(tmpdir / 'Npix30889.fits')
-        tile.write(filename)
-        tile2 = HipsTile.read(meta, filename=filename)
-
-        assert tile == tile2
+    fetch_read_pars = [
+        dict(url='http://alasky.unistra.fr/DSS/DSS2Merged/Norder3/Dir0/Npix463.fits',
+             full_path='datasets/samples/DSS2Red/Norder3/Dir0/Npix463.fits',
+             file_name='Npix463.fits', file_format='fits', order=3, ipix=463),
+        dict(url='http://alasky.unistra.fr/DSS/DSS2Merged/Norder3/Dir0/Npix463.jpg',
+             full_path='datasets/samples/DSS2Red/Norder3/Dir0/Npix463.jpg',
+             file_name='Npix463.jpg', file_format='jpg', order=3, ipix=463),
+        dict(url='http://alasky.unistra.fr/2MASS6X/2MASS6X_H/Norder6/Dir0/Npix6112.png',
+             full_path='datasets/samples/2MASS6XH/Norder6/Dir0/Npix6112.png',
+             file_name='Npix6112.png', file_format='png', order=6, ipix=6112)
+    ]

     @remote_data
-    def test_fetch_read_write_jpg(self, tmpdir):
-        meta = HipsTileMeta(order=6, ipix=30889, file_format='jpg')
-        url = 'http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.jpg'
-        tile = HipsTile.fetch(meta, url)
-
-        assert tile.data.shape == (512, 512, 3)
-        assert_equal(tile.data[510][5:7], [[0, 0, 0], [1, 1, 1]])
-
-        filename = str(tmpdir / 'Npix30889.jpg')
+    @requires_hips_extra()
+    @pytest.mark.parametrize('pars', fetch_read_pars)
+    def test_fetch_read(self, pars):
+        meta = HipsTileMeta(order=pars['order'], ipix=pars['ipix'], file_format=pars['file_format'])
+        tile_fetched = HipsTile.fetch(meta, url=pars['url'])
+
+        full_path = get_hips_extra_file(pars['full_path'])
+        tile_local = HipsTile.read(meta, full_path=full_path)
+
+        assert tile_fetched == tile_local
+
+    read_write_pars = [
+        dict(full_path='datasets/samples/DSS2Red/Norder3/Dir0/Npix463.fits', file_name='Npix463.fits',
+             file_format='fits', order=3, ipix=463, shape=(512, 512), tile_data=[3047], index=[[510], [5]]),
+        dict(full_path='datasets/samples/DSS2Red/Norder3/Dir0/Npix463.jpg', file_name='Npix463.jpg',
+             file_format='jpg', order=3, ipix=463, shape=(512, 512, 3), tile_data=[[10, 10, 10]], index=[[510], [5]]),
+        dict(full_path='datasets/samples/2MASS6XH/Norder6/Dir0/Npix6112.png', file_name='Npix6112.png',
+             file_format='png', order=6, ipix=6112, shape=(512, 512, 4), tile_data=[[19, 19, 19, 255]], index=[[253], [5]])
+    ]
+
+    @requires_hips_extra()
+    @pytest.mark.parametrize('pars', read_write_pars)
+    def test_read_write(self, tmpdir, pars):
+        meta = HipsTileMeta(order=pars['order'], ipix=pars['ipix'], file_format=pars['file_format'])
+        full_path = get_hips_extra_file(pars['full_path'])
+        tile = HipsTile.read(meta, full_path)
+
+        assert tile.data.shape == pars['shape']
+        assert_equal(tile.data[pars['index']], pars['tile_data'])
+
+        filename = str(tmpdir / pars['file_name'])
         tile.write(filename)
-        tile2 = HipsTile.read(meta, filename=filename)
-
-        # The following assert fails, because on JPEG write / read
-        # the data is different (for unknown reasons).
-        # TODO: Figure out what's wrong here and fix!
-        # print(tile.data.sum())
-        # print(tile2.data.sum())
-        # print((tile == tile2).all())
-        # assert tile == tile2
-
-    @remote_data
-    def test_fetch_read_write_png(self, tmpdir):
-        meta = HipsTileMeta(order=6, ipix=463, file_format='png')
-        url = 'http://alasky.unistra.fr/2MASS6X/2MASS6X_H/Norder6/Dir0/Npix463.png'
-        tile = HipsTile.fetch(meta, url)
+        tile2 = HipsTile.read(meta, full_path=filename)

-        assert tile.data.shape == (512, 512, 4)
-        assert_equal(tile.data[510][5:7], [[17, 17, 17, 255], [18, 18, 18, 255]])
+        # TODO: Fix JPG write issue
+        # assert tile == tile2

-        filename = str(tmpdir / 'Npix463.png')
-        tile.write(filename)
-        tile2 = HipsTile.read(meta, filename=filename)
+    def test_value_error(self):
+        with pytest.raises(ValueError):
+            meta = HipsTileMeta(order=3, ipix=463, file_format='jpeg')
+            full_path = get_hips_extra_file('datasets/samples/DSS2Red/Norder3/Dir0/Npix463.jpg')
+            HipsTile.read(meta, full_path)

-        assert tile == tile2

 class TestHipsTileMeta:
     @classmethod
diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py
index 4a3373c..550969c 100644
--- a/hips/tiles/tile.py
+++ b/hips/tiles/tile.py
@@ -159,17 +159,17 @@ class HipsTile:
         return cls._from_raw_data(meta, raw_data)

     @classmethod
-    def read(cls, meta: HipsTileMeta, filename: str = None) -> 'HipsTile':
+    def read(cls, meta: HipsTileMeta, full_path: str = None) -> 'HipsTile':
         """Read HiPS tile data from a directory and load into memory (`HipsTile`).

         Parameters
         ----------
         meta : `HipsTileMeta`
             Metadata of HiPS tile
-        filename : `str`
+        full_path : `str`
             File path to store a HiPS tile
         """
-        path = Path(filename) if filename else meta.full_path
+        path = Path(full_path) or meta.full_path
         with path.open(mode='rb') as fh:
             raw_data = BytesIO(fh.read())

@@ -197,15 +197,15 @@ class HipsTile:
             raise ValueError(f'Tile file format not supported: {meta.file_format}. '
                               'Supported formats: fits, jpg, png')

-    def write(self, filename: str = None) -> None:
+    def write(self, full_path: str = None) -> None:
         """Write HiPS tile by a given filename.

         Parameters
         ----------
-        filename : `str`
+        full_path : `str`
             Name of the file
         """
-        path = Path(filename) if filename else self.meta.full_path
+        path = Path(full_path) or meta.full_path
         file_format = self.meta.file_format

         if file_format == 'fits':
@@ -218,5 +218,5 @@ class HipsTile:
             image = Image.fromarray(self.data)
             image.save(str(path))
         else:
-            raise ValueError(f'Tile file format not supported: {meta.file_format}. '
-                              'Supported formats: fits, jpg, png')
+            raise ValueError(f'Tile file format not supported: {file_format}. '
+                              'Supported formats: fits, jpg, png')  # pragma: no cover

commit b0f8d4daa85e32a07677522f948b7a9925b07ef7
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Wed Jul 5 12:20:03 2017 +0500

    Add test for PNG tiles

diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py
index 2f3b909..a3e972d 100644
--- a/hips/tiles/tests/test_tile.py
+++ b/hips/tiles/tests/test_tile.py
@@ -43,6 +43,20 @@ class TestHipsTile:
         # print((tile == tile2).all())
         # assert tile == tile2

+    @remote_data
+    def test_fetch_read_write_png(self, tmpdir):
+        meta = HipsTileMeta(order=6, ipix=463, file_format='png')
+        url = 'http://alasky.unistra.fr/2MASS6X/2MASS6X_H/Norder6/Dir0/Npix463.png'
+        tile = HipsTile.fetch(meta, url)
+
+        assert tile.data.shape == (512, 512, 4)
+        assert_equal(tile.data[510][5:7], [[17, 17, 17, 255], [18, 18, 18, 255]])
+
+        filename = str(tmpdir / 'Npix463.png')
+        tile.write(filename)
+        tile2 = HipsTile.read(meta, filename=filename)
+
+        assert tile == tile2

 class TestHipsTileMeta:
     @classmethod
diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py
index f337e67..4a3373c 100644
--- a/hips/tiles/tile.py
+++ b/hips/tiles/tile.py
@@ -189,15 +189,13 @@ class HipsTile:
                     data = hdu_list[0].data
                     header = hdu_list[0].header
             return cls(meta, data, header)
-        elif meta.file_format == 'jpg':
+        elif meta.file_format in {'jpg', 'png'}:
             with Image.open(raw_data) as image:
                 data = np.array(image)
             return cls(meta, data)
-        elif meta.file_format == 'png':
-            raise NotImplementedError()
         else:
             raise ValueError(f'Tile file format not supported: {meta.file_format}. '
-                             'Supported formats: fits, jpg, png')
+                              'Supported formats: fits, jpg, png')

     def write(self, filename: str = None) -> None:
         """Write HiPS tile by a given filename.
@@ -216,11 +214,9 @@ class HipsTile:
                 warnings.simplefilter('ignore', VerifyWarning)
                 hdu = fits.PrimaryHDU(self.data, header=self.header)
                 hdu.writeto(str(path))
-        elif file_format == 'jpg':
+        elif file_format in {'jpg', 'png'}:
             image = Image.fromarray(self.data)
             image.save(str(path))
-        elif file_format == 'png':
-            raise NotImplementedError()
         else:
             raise ValueError(f'Tile file format not supported: {meta.file_format}. '
-                             'Supported formats: fits, jpg, png')
+                              'Supported formats: fits, jpg, png')

commit f7aa6c9aa4e6bee51dab7801d7910f02b69dc0eb
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Mon Jul 3 22:29:06 2017 +0500

    Update tile_access_url method

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index 6f23f60..eb28f51 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -97,7 +97,7 @@ def fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: Hips
             frame=hips_survey.astropy_frame,
             file_format='fits',
         )
-        tile = HipsTile.fetch(tile_meta, hips_survey.tile_access_url + tile_meta.filename)
+        tile = HipsTile.fetch(tile_meta, hips_survey.tile_access_url(order=order, ipix=healpix_pixel_index) + tile_meta.filename)
         yield tile


diff --git a/hips/tiles/surveys.py b/hips/tiles/surveys.py
index 1bfd784..8013ef5 100644
--- a/hips/tiles/surveys.py
+++ b/hips/tiles/surveys.py
@@ -1,5 +1,6 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 from collections import OrderedDict
+import numpy as np
 import urllib.request
 from typing import List
 from astropy.table import Table
@@ -123,14 +124,24 @@ class HipsSurveyProperties:
         return self.data['hips_tile_format']

     @property
-    def base_url(self):
+    def base_url(self) -> str:
         """HiPS access url"""
         return self.data['moc_access_url'].rsplit('/', 1)[0]

-    @property
-    def tile_access_url(self):
-        """Tile access URL for a HiPS surveys"""
-        return self.base_url + '/Norder' + str(self.hips_order) + '/Dir0/'
+    def directory(self, ipix: int) -> int:
+        return (ipix // 10000) * 10000
+
+    def tile_access_url(self, order: int, ipix: int) -> str:
+        """Tile access URL
+
+        Parameters
+        ----------
+        order : int
+            HiPS order
+        ipix : int
+            Index of the HiPS tile
+        """
+        return self.base_url + '/Norder' + str(order) + '/Dir' + str(self.directory(ipix)) + '/'

     @property
     def hips_service_url(self) -> str:
diff --git a/hips/tiles/tests/test_surveys.py b/hips/tiles/tests/test_surveys.py
index 1130fd9..df4e812 100644
--- a/hips/tiles/tests/test_surveys.py
+++ b/hips/tiles/tests/test_surveys.py
@@ -32,7 +32,7 @@ class TestHipsSurveyProperties:
         assert self.hips_survey_property.base_url == 'http://alasky.u-strasbg.fr/DSS/DSSColor'

     def test_tile_access_url(self):
-        assert self.hips_survey_property.tile_access_url == 'http://alasky.u-strasbg.fr/DSS/DSSColor/Norder9/Dir0/'
+        assert self.hips_survey_property.tile_access_url(order=9, ipix=54321) == 'http://alasky.u-strasbg.fr/DSS/DSSColor/Norder9/Dir50000/'


 class TestHipsSurveyPropertiesList:

commit f746d04e6a3cbe73c4c8ce6b8a32d3020c83bce5
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Mon Jul 3 18:33:27 2017 +0500

    Make improvements to compute_healpix_pixel_indices utility function

diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py
index 0849789..6e8213f 100644
--- a/hips/utils/healpix.py
+++ b/hips/utils/healpix.py
@@ -69,11 +69,12 @@ def boundaries(nside: int, pix: int, nest: bool = True) -> tuple:
     return theta, phi


-def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, order: int, healpix_frame: str = None) -> np.ndarray:
+def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, order: int, healpix_frame: str) -> np.ndarray:
     """Compute HEALPix pixels within a minimal disk covering a given WCSGeometry.

-    This function calls `healpy.pixelfunc.ang2vec` and `healpy.query_disc`
-    to compute the HEALPix pixel indices, which will be used in tile drawing.
+    This function computes pixel coordinates for the given WCS object and
+    then calls `healpy.pixelfunc.ang2pix` and `numpy.unique` to compute
+    HEALPix pixel indices, which will be used in tile drawing.

     Parameters
     ----------
@@ -82,8 +83,7 @@ def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, order: int, healpix
     order : int
         The order of the HEALPix
     healpix_frame : {'icrs', 'galactic', 'ecliptic'}
-        Coordinate system frame in which to compute the HEALPix pixel indices.
-        The default ``None`` means: take the frame from ``geometry``.
+        Coordinate system frame in which to compute the HEALPix pixel indices

     Returns
     -------
@@ -101,19 +101,11 @@ def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, order: int, healpix
     ...     coordsys='CEL', projection='AIT',
     ...     cdelt=1.0, crpix=(1., 1.),
     ... )
-    >>> compute_healpix_pixel_indices(wcs_geometry, order=3)
-    array([176, 207, 208, 239, 240, 271, 272])
+    >>> compute_healpix_pixel_indices(wcs_geometry, order=3, healpix_frame='galactic')
+    array([321, 611, 614, 615, 617, 618, 619, 620, 621, 622])
     """
-    healpix_frame = healpix_frame or wcs_geometry.celestial_frame
-
-    center_coord = wcs_geometry.center_skycoord.transform_to(healpix_frame)
-
-    pixel_coords = wcs_geometry.pixel_skycoords
-    separation = center_coord.separation(pixel_coords)
-    radius = np.nanmax(separation.rad)
-
-    vec = _skycoord_to_vec(center_coord)
     nside = hp.order2nside(order)
-    ipix = hp.query_disc(nside, vec, radius, nest=True)
-
-    return ipix
+    pixel_coords = wcs_geometry.pixel_skycoords.transform_to(healpix_frame)
+    theta, phi = _skycoord_to_theta_phi(pixel_coords)
+    ipix = hp.ang2pix(nside, theta, phi, nest=True)
+    return np.unique(ipix)
diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py
index 65b8632..9094894 100644
--- a/hips/utils/tests/test_healpix.py
+++ b/hips/utils/tests/test_healpix.py
@@ -23,8 +23,8 @@ def test_boundaries():


 compute_healpix_pixel_indices_pars = [
-    dict(frame=None, ipix=[269, 270, 271, 282, 293, 304, 305, 306]),
-    dict(frame='icrs', ipix=[448, 449, 450, 451, 456, 457, 663]),
+    dict(frame='galactic', ipix=[269, 270, 271, 280, 282, 283, 292, 293, 295, 304, 305, 306]),
+    dict(frame='icrs', ipix=[448, 449, 450, 451, 454, 456, 457, 460, 661, 663, 669]),
 ]



commit 3121de53a68eabea67fdce9eae1a26412484edaf
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Mon Jul 3 14:00:17 2017 +0500

    Fix typo

diff --git a/docs/getting_started.rst b/docs/getting_started.rst
index 9e2e621..3133c61 100644
--- a/docs/getting_started.rst
+++ b/docs/getting_started.rst
@@ -79,5 +79,5 @@ What next?
 That's it, now you've seen the main features of the `hips` package.
 Note that there is API documentation explaining all available functions, classes and parameters.

-If you have any questions, or find something now working or a missing feature,
+If you have any questions, or find something not working or a missing feature,
 please get in touch by posting on our Github issue tracker.

commit 880d4a2e9e9ce31e86b508750bc65ea33f4a3b4b
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 30 18:32:43 2017 +0500

    Add test case test_wcs_healpix_pixel_indices

diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py
index 2d47d4c..a0931bc 100644
--- a/hips/draw/tests/test_simple.py
+++ b/hips/draw/tests/test_simple.py
@@ -7,7 +7,7 @@ from numpy.testing import assert_allclose
 from ..simple import make_sky_image, draw_sky_image
 from ...tiles import HipsSurveyProperties, HipsTileMeta, HipsTile
 from ...utils.testing import get_hips_extra_file, make_test_wcs_geometry, requires_hips_extra
-
+from ...utils import compute_healpix_pixel_indices

 def get_test_tiles():
     filename = get_pkg_data_filename('../../tiles/tests/data/properties.txt')
@@ -51,3 +51,11 @@ def test_make_sky_image():
     assert data.dtype == np.float64
     assert_allclose(data[200, 994], 3717.10091363)
     assert_allclose(data[200, 995], 3402.55292158)
+
+def test_wcs_healpix_pixel_indices():
+    url = 'https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties'
+    hips_survey = HipsSurveyProperties.fetch(url)
+
+    geometry = make_test_wcs_geometry(case=2)
+    healpix_pixel_indices = compute_healpix_pixel_indices(geometry, hips_survey.hips_order)
+    assert list(healpix_pixel_indices) == [450, 451]

commit 5fe3ed0aa5be6544a1b71b786f0014df626d1b21
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 30 15:58:32 2017 +0500

    Some code reformatting

diff --git a/docs/getting_started.rst b/docs/getting_started.rst
index e730eac..57274f7 100644
--- a/docs/getting_started.rst
+++ b/docs/getting_started.rst
@@ -1,7 +1,7 @@
 .. include:: references.txt

-.. _gs:
 .. doctest-skip-all
+.. _gs:

 ***************
 Getting started
@@ -25,7 +25,7 @@ Then it saves it on local disk in FITS file format.
          projection='AIT', cdelt=0.01, crpix=(1000, 500),
     )
     url = 'https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties'
-    hips_survey = HipsSurveyProperties.fetch(url)  # doctest: +REMOTE_DATA
+    hips_survey = HipsSurveyProperties.fetch(url)
     data = make_sky_image(geometry, hips_survey)
     hdu = fits.PrimaryHDU(data=data, header=geometry.fits_header)
     hdu.writeto('my_image.fits')
diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index f93d9f4..55798b4 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -67,7 +67,8 @@ class SimpleTilePainter:

     def warp_image(self) -> np.ndarray:
         """Warp a HiPS tile and a sky image"""
-        return tf.warp(self.tile.data, self.projection, output_shape=self.geometry.shape, preserve_range=True)
+        return tf.warp(self.tile.data, self.projection,
+                       output_shape=self.geometry.shape, preserve_range=True)


 def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: HipsSurveyProperties) -> 'HipsTile':

commit 145bcafab6c47ced08191a45f1feaa69907da4a8
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 30 15:54:01 2017 +0500

    Add attribute frames in HipsSurveyProperties

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index 6918052..f93d9f4 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -6,7 +6,7 @@ from skimage import transform as tf
 from typing import List, Generator, Any

 from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta
-from ..utils import WCSGeometry, compute_healpix_pixel_indices, frames
+from ..utils import WCSGeometry, compute_healpix_pixel_indices

 __all__ = [
     'draw_sky_image',
@@ -89,7 +89,7 @@ def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: Hip
     """
     for healpix_pixel_index in healpix_pixel_indices:
         tile_meta = HipsTileMeta(order=order, ipix=healpix_pixel_index,
-                                 frame=frames()[hips_survey.hips_frame], file_format='fits')
+                                 frame=hips_survey.frames[hips_survey.hips_frame], file_format='fits')
         tile = HipsTile.fetch(tile_meta, hips_survey.tile_access_url + tile_meta.filename)
         yield tile

diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py
index e4608ca..2d47d4c 100644
--- a/hips/draw/tests/test_simple.py
+++ b/hips/draw/tests/test_simple.py
@@ -6,7 +6,6 @@ from numpy.testing import assert_allclose

 from ..simple import make_sky_image, draw_sky_image
 from ...tiles import HipsSurveyProperties, HipsTileMeta, HipsTile
-from ...utils import frames
 from ...utils.testing import get_hips_extra_file, make_test_wcs_geometry, requires_hips_extra


@@ -15,13 +14,13 @@ def get_test_tiles():
     hips_survey = HipsSurveyProperties.read(filename)

     tile1 = HipsTile.read(
-        meta=HipsTileMeta(order=3, ipix=450, file_format='fits', frame=frames()[hips_survey.hips_frame],
+        meta=HipsTileMeta(order=3, ipix=450, file_format='fits', frame=hips_survey.frames[hips_survey.hips_frame],
                           tile_width=512),
         filename=get_hips_extra_file('datasets/samples/DSS2Red/Norder3/Dir0/Npix450.fits'),
     )

     tile2 = HipsTile.read(
-        meta=HipsTileMeta(order=3, ipix=451, file_format='fits', frame=frames()[hips_survey.hips_frame],
+        meta=HipsTileMeta(order=3, ipix=451, file_format='fits', frame=hips_survey.frames[hips_survey.hips_frame],
                           tile_width=512),
         filename=get_hips_extra_file('datasets/samples/DSS2Red/Norder3/Dir0/Npix451.fits'),
     )
diff --git a/hips/tiles/description.py b/hips/tiles/description.py
index 90c4bca..c8792e0 100644
--- a/hips/tiles/description.py
+++ b/hips/tiles/description.py
@@ -27,6 +27,7 @@ class HipsSurveyProperties:

     def __init__(self, data: OrderedDict) -> None:
         self.data = data
+        self.frames = dict({'equatorial': 'icrs', 'galactic': 'galactic', 'ecliptic': 'ecliptic'})

     @classmethod
     def read(cls, filename: str) -> 'HipsSurveyProperties':
@@ -116,4 +117,5 @@ class HipsSurveyProperties:

     @property
     def tile_access_url(self):
+        """Tile access URL for a HiPS surveys"""
         return self.access_url + '/Norder' + str(self.hips_order) + '/Dir0/'
diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py
index 9eaffad..8e84629 100644
--- a/hips/utils/healpix.py
+++ b/hips/utils/healpix.py
@@ -13,8 +13,7 @@ from .wcs import WCSGeometry

 __all__ = [
     'boundaries',
-    'compute_healpix_pixel_indices',
-    'frames'
+    'compute_healpix_pixel_indices'
 ]

 __doctest_skip__ = ['boundaries', 'compute_healpix_pixel_indices']
@@ -110,7 +109,3 @@ def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, order: int) -> np.n
     vec = _skycoord_to_vec(center_coord)
     nside = hp.order2nside(order)
     return hp.query_disc(nside, vec, radius)
-
-def frames() -> dict:
-    """A dictionary mapping commonly used frames"""
-    return dict({'equatorial': 'icrs', 'galactic': 'galactic', 'ecliptic': 'ecliptic'})

commit 5c85e128e37e4c0cc86166e08fc13b1d51e3a5c0
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 30 15:42:16 2017 +0500

    Add property tile_access_url in HipsSurveyProperties

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index feae223..6918052 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -87,11 +87,10 @@ def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: Hip
     'HipsTile'
         Returns an object of  HipsTile
     """
-    base_url = hips_survey.access_url + '/Norder' + str(hips_survey.hips_order) + '/Dir0/'
     for healpix_pixel_index in healpix_pixel_indices:
         tile_meta = HipsTileMeta(order=order, ipix=healpix_pixel_index,
                                  frame=frames()[hips_survey.hips_frame], file_format='fits')
-        tile = HipsTile.fetch(tile_meta, base_url + tile_meta.filename)
+        tile = HipsTile.fetch(tile_meta, hips_survey.tile_access_url + tile_meta.filename)
         yield tile


diff --git a/hips/tiles/description.py b/hips/tiles/description.py
index 15d5a9a..90c4bca 100644
--- a/hips/tiles/description.py
+++ b/hips/tiles/description.py
@@ -113,3 +113,7 @@ class HipsSurveyProperties:
     def access_url(self):
         """HiPS access url"""
         return self.data['moc_access_url'].rsplit('/', 1)[0]
+
+    @property
+    def tile_access_url(self):
+        return self.access_url + '/Norder' + str(self.hips_order) + '/Dir0/'
diff --git a/hips/tiles/tests/test_description.py b/hips/tiles/tests/test_description.py
index d0e87dc..9874916 100644
--- a/hips/tiles/tests/test_description.py
+++ b/hips/tiles/tests/test_description.py
@@ -29,3 +29,6 @@ class TestHipsSurveyProperties:

     def test_access_url(self):
         assert self.hips_survey_property.access_url == 'http://alasky.u-strasbg.fr/DSS/DSSColor'
+
+    def test_tile_access_url(self):
+        assert self.hips_survey_property.tile_access_url == 'http://alasky.u-strasbg.fr/DSS/DSSColor/Norder9/Dir0/'

commit a4b6846ef107572e7b542114b731089346c3b1f5
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 30 15:24:59 2017 +0500

    Remove attribute self.pt from SimpleTilePainter

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index d29f619..feae223 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -54,7 +54,6 @@ class SimpleTilePainter:
     def __init__(self, geometry: WCSGeometry, tile: HipsTile) -> None:
         self.geometry = geometry
         self.tile = tile
-        self.pt = None

     @property
     def projection(self) -> tf.ProjectiveTransform:
@@ -62,9 +61,9 @@ class SimpleTilePainter:
         corners = self.tile.meta.skycoord_corners.to_pixel(self.geometry.wcs)
         src = np.array(corners).T.reshape((4, 2))
         dst = self.tile.meta.dst
-        self.pt = tf.ProjectiveTransform()
-        self.pt.estimate(src, dst)
-        return self.pt
+        pt = tf.ProjectiveTransform()
+        pt.estimate(src, dst)
+        return pt

     def warp_image(self) -> np.ndarray:
         """Warp a HiPS tile and a sky image"""

commit 69e09af162b071c3a1f271883ebf51fd00aceee5
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 30 15:23:22 2017 +0500

    Skip example from doctest

diff --git a/docs/getting_started.rst b/docs/getting_started.rst
index 81c48db..e730eac 100644
--- a/docs/getting_started.rst
+++ b/docs/getting_started.rst
@@ -1,6 +1,7 @@
 .. include:: references.txt

 .. _gs:
+.. doctest-skip-all

 ***************
 Getting started
@@ -10,19 +11,21 @@ The example below shows a high level use case of the ``hips`` package.
 It fetches a HiPS tile from a remote URL and draws it on a sky image.
 Then it saves it on local disk in FITS file format.

->>> from astropy.io import fits
->>> from astropy.coordinates import SkyCoord
->>> from astropy.tests.helper import remote_data
->>> from hips.utils import WCSGeometry
->>> from hips.draw import make_sky_image
->>> from hips.tiles import HipsSurveyProperties
->>> geometry = WCSGeometry.create(
-...     skydir=SkyCoord(0, 0, unit='deg', frame='galactic'),
-...     shape=(1000, 2000), coordsys='GAL',
-...     projection='AIT', cdelt=0.01, crpix=(1000, 500),
-... )
->>> url = 'https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties'
->>> hips_survey = HipsSurveyProperties.fetch(url)  # doctest: +REMOTE_DATA
->>> data = make_sky_image(geometry, hips_survey)
->>> hdu = fits.PrimaryHDU(data=data, header=geometry.fits_header)
->>> hdu.writeto('my_image.fits')
\ No newline at end of file
+::
+
+    from astropy.io import fits
+    from astropy.coordinates import SkyCoord
+    from astropy.tests.helper import remote_data
+    from hips.utils import WCSGeometry
+    from hips.draw import make_sky_image
+    from hips.tiles import HipsSurveyProperties
+    geometry = WCSGeometry.create(
+         skydir=SkyCoord(0, 0, unit='deg', frame='galactic'),
+         shape=(1000, 2000), coordsys='GAL',
+         projection='AIT', cdelt=0.01, crpix=(1000, 500),
+    )
+    url = 'https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties'
+    hips_survey = HipsSurveyProperties.fetch(url)  # doctest: +REMOTE_DATA
+    data = make_sky_image(geometry, hips_survey)
+    hdu = fits.PrimaryHDU(data=data, header=geometry.fits_header)
+    hdu.writeto('my_image.fits')

commit 112fb37cd4514e84449be3e3543164cc4f171383
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 30 15:02:32 2017 +0500

    Update example for HipsTileMeta

diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py
index c05c52a..f47888a 100644
--- a/hips/tiles/tile.py
+++ b/hips/tiles/tile.py
@@ -41,9 +41,9 @@ class HipsTileMeta:
     >>> from hips.tiles import HipsTileMeta
     >>> tile_meta = HipsTileMeta(order=3, ipix=450, file_format='fits', frame='icrs', tile_width=512)
     >>> tile_meta.skycoord_corners
-    <SkyCoord (Galactic): (l, b) in deg
+    <SkyCoord (ICRS): (ra, dec) in deg
     [( 264.375, -24.62431835), ( 258.75 , -30.        ),
-     ( 264.375, -35.68533471), ( 270.   , -30.        )]>
+    ( 264.375, -35.68533471), ( 270.   , -30.        )]>
     """

     def __init__(self, order: int, ipix: int, file_format: str, frame: str = 'galactic', tile_width: int = 512) -> None:

commit c34d4061a05eae864ae8de05b7fd639da36910dc
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 30 14:31:47 2017 +0500

    Update requires_hips_extra decorator function

diff --git a/hips/utils/testing.py b/hips/utils/testing.py
index d9d6d69..0563f7a 100644
--- a/hips/utils/testing.py
+++ b/hips/utils/testing.py
@@ -22,11 +22,19 @@ def get_hips_extra_file(filename):
     return path / filename


+def has_hips_extra():
+    """Is hips-extra available? (bool)"""
+    if 'HIPS_EXTRA' in os.environ:
+        path = Path(os.environ['HIPS_EXTRA']) / 'datasets/samples/DSS2Red/properties'
+        if path.is_file():
+            return True
+
+    return False
+
+
 def requires_hips_extra():
-    """Decorator to mark tests requiring ``hips-extra`` data.
-    """
-    has_hips_extra = get_hips_extra_file('datasets/samples/DSS2Red/properties').is_file()
-    skip_it = not has_hips_extra
+    """Decorator to mark tests requiring ``hips-extra`` data."""
+    skip_it = not has_hips_extra()
     reason = 'No hips-extra data available.'
     return pytest.mark.skipif(skip_it, reason=reason)


commit 451b32a2985b55fad3bb2a0de8eb25ff3c75b218
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 30 14:10:51 2017 +0500

    Update read classmethod in HipsTile class

diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py
index b7d95fc..c05c52a 100644
--- a/hips/tiles/tile.py
+++ b/hips/tiles/tile.py
@@ -96,6 +96,7 @@ class HipsTileMeta:
         theta, phi = boundaries(self.nside, self.ipix)
         return SkyCoord(phi, np.pi / 2 - theta, unit='radian', frame=self.frame)

+
 class HipsTile:
     """HiPS tile container.

@@ -174,9 +175,9 @@ class HipsTile:
         path = Path(filename) if filename else meta.full_path

         if meta.file_format == 'fits':
-            hdu_list = fits.open(str(path))
-            data = hdu_list[0].data
-            header = hdu_list[0].header
+            with fits.open(str(path)) as hdu_list:
+                data = hdu_list[0].data
+                header = hdu_list[0].header
             return cls(meta, data, header)
         else:
             image = Image.open(str(path))

commit 186286c3a64f92ee1f9a50b053e5bc9724dd438f
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 30 14:07:33 2017 +0500

    Update test_skycoord_corners in TestHipsTileMeta

diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py
index 7d85e81..c0a8ccd 100644
--- a/hips/tiles/tests/test_tile.py
+++ b/hips/tiles/tests/test_tile.py
@@ -67,9 +67,11 @@ class TestHipsTileMeta:
         assert_allclose(self.meta.dst, dst)

     def test_skycoord_corners(self):
-        assert_allclose(self.meta.skycoord_corners.ra.deg, [264.375, 258.75, 264.375, 270.])
-        assert_allclose(self.meta.skycoord_corners.dec.deg, [-24.624318, -30., -35.685335, -30.])
+        assert_allclose(self.meta.skycoord_corners.data.lat.deg, [-24.624318, -30., -35.685335, -30.])
+        assert_allclose(self.meta.skycoord_corners.data.lon.deg, [264.375, 258.75, 264.375, 270.])
+        assert self.meta.skycoord_corners.frame.name == 'icrs'

         meta = HipsTileMeta(order=3, ipix=450, file_format='fits', frame='galactic', tile_width=512)
-        assert_allclose(meta.skycoord_corners.l.deg, [264.375, 258.75, 264.375, 270.])
-        assert_allclose(meta.skycoord_corners.b.deg, [-24.624318, -30., -35.685335, -30.])
+        assert_allclose(meta.skycoord_corners.data.lat.deg, [-24.624318, -30., -35.685335, -30.])
+        assert_allclose(meta.skycoord_corners.data.lon.deg, [264.375, 258.75, 264.375, 270.])
+        assert meta.skycoord_corners.frame.name == 'galactic'
diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py
index 3ae0f4f..b7d95fc 100644
--- a/hips/tiles/tile.py
+++ b/hips/tiles/tile.py
@@ -94,11 +94,7 @@ class HipsTileMeta:
     def skycoord_corners(self) -> SkyCoord:
         """Corner values for a HiPS tile"""
         theta, phi = boundaries(self.nside, self.ipix)
-        if self.frame == 'galactic':
-            return SkyCoord(l=phi, b=np.pi / 2 - theta, unit='radian', frame=self.frame)
-        else:
-            return SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit='radian', frame=self.frame)
-
+        return SkyCoord(phi, np.pi / 2 - theta, unit='radian', frame=self.frame)

 class HipsTile:
     """HiPS tile container.

commit de5566394997a6a2d7a87e548f12b3b179aa1f53
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 30 12:46:30 2017 +0500

    Add function frames in utils/healpix.py

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index ea61810..d29f619 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -6,7 +6,7 @@ from skimage import transform as tf
 from typing import List, Generator, Any

 from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta
-from ..utils import WCSGeometry, compute_healpix_pixel_indices
+from ..utils import WCSGeometry, compute_healpix_pixel_indices, frames

 __all__ = [
     'draw_sky_image',
@@ -89,10 +89,9 @@ def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: Hip
         Returns an object of  HipsTile
     """
     base_url = hips_survey.access_url + '/Norder' + str(hips_survey.hips_order) + '/Dir0/'
-    frames = dict({'equatorial': 'icrs', 'galactic': 'galactic', 'ecliptic': 'ecliptic'})
     for healpix_pixel_index in healpix_pixel_indices:
         tile_meta = HipsTileMeta(order=order, ipix=healpix_pixel_index,
-                                 frame=frames[hips_survey.hips_frame], file_format='fits')
+                                 frame=frames()[hips_survey.hips_frame], file_format='fits')
         tile = HipsTile.fetch(tile_meta, base_url + tile_meta.filename)
         yield tile

diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py
index 80818f9..e4608ca 100644
--- a/hips/draw/tests/test_simple.py
+++ b/hips/draw/tests/test_simple.py
@@ -6,21 +6,23 @@ from numpy.testing import assert_allclose

 from ..simple import make_sky_image, draw_sky_image
 from ...tiles import HipsSurveyProperties, HipsTileMeta, HipsTile
+from ...utils import frames
 from ...utils.testing import get_hips_extra_file, make_test_wcs_geometry, requires_hips_extra


 def get_test_tiles():
-    frames = dict({'equatorial': 'icrs', 'galactic': 'galactic', 'ecliptic': 'ecliptic'})
     filename = get_pkg_data_filename('../../tiles/tests/data/properties.txt')
     hips_survey = HipsSurveyProperties.read(filename)

     tile1 = HipsTile.read(
-        meta=HipsTileMeta(order=3, ipix=450, file_format='fits', frame=frames[hips_survey.hips_frame], tile_width=512),
+        meta=HipsTileMeta(order=3, ipix=450, file_format='fits', frame=frames()[hips_survey.hips_frame],
+                          tile_width=512),
         filename=get_hips_extra_file('datasets/samples/DSS2Red/Norder3/Dir0/Npix450.fits'),
     )

     tile2 = HipsTile.read(
-        meta=HipsTileMeta(order=3, ipix=451, file_format='fits', frame=frames[hips_survey.hips_frame], tile_width=512),
+        meta=HipsTileMeta(order=3, ipix=451, file_format='fits', frame=frames()[hips_survey.hips_frame],
+                          tile_width=512),
         filename=get_hips_extra_file('datasets/samples/DSS2Red/Norder3/Dir0/Npix451.fits'),
     )

diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py
index 829248f..9eaffad 100644
--- a/hips/utils/healpix.py
+++ b/hips/utils/healpix.py
@@ -14,6 +14,7 @@ from .wcs import WCSGeometry
 __all__ = [
     'boundaries',
     'compute_healpix_pixel_indices',
+    'frames'
 ]

 __doctest_skip__ = ['boundaries', 'compute_healpix_pixel_indices']
@@ -109,3 +110,7 @@ def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, order: int) -> np.n
     vec = _skycoord_to_vec(center_coord)
     nside = hp.order2nside(order)
     return hp.query_disc(nside, vec, radius)
+
+def frames() -> dict:
+    """A dictionary mapping commonly used frames"""
+    return dict({'equatorial': 'icrs', 'galactic': 'galactic', 'ecliptic': 'ecliptic'})

commit a9b0984a670a624b791fc99f11e93972edf97aad
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 30 12:37:38 2017 +0500

    Rename draw_tile to painter

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index ef0908f..ea61810 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -33,8 +33,8 @@ def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any])
     # TODO: Fix type annotation issue
     sky_sky = np.zeros(geometry.shape)
     for tile in tiles:
-        draw_tile = SimpleTilePainter(geometry, tile)
-        sky_sky += draw_tile.warp_image()
+        painter = SimpleTilePainter(geometry, tile)
+        sky_sky += painter.warp_image()
     return sky_sky



commit a0ad4cb044373e879a2d88b5559de91c91557e19
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 30 12:36:50 2017 +0500

    Rename all_sky to sky_sky

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index 9a849cd..ef0908f 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -31,11 +31,11 @@ def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any])
         Returns a numpy array containing all HiPS tiles projected onto it
     """
     # TODO: Fix type annotation issue
-    all_sky = np.zeros(geometry.shape)
+    sky_sky = np.zeros(geometry.shape)
     for tile in tiles:
         draw_tile = SimpleTilePainter(geometry, tile)
-        all_sky += draw_tile.warp_image()
-    return all_sky
+        sky_sky += draw_tile.warp_image()
+    return sky_sky


 class SimpleTilePainter:

commit 9b79b9d19c6faa8f1bf5386076af04dc3340e995
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 30 12:33:05 2017 +0500

    Update test_skycoord_corners in TestHipsTileMeta

diff --git a/hips/tiles/__init__.py b/hips/tiles/__init__.py
index 0dfc508..3e45f09 100644
--- a/hips/tiles/__init__.py
+++ b/hips/tiles/__init__.py
@@ -1,6 +1,5 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 """Classes and functions to manage HiPS tiles."""
 from .tile import *
-from .tile_meta import *
 from .description import *
 from .surveys import *
diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py
index 95701cd..7d85e81 100644
--- a/hips/tiles/tests/test_tile.py
+++ b/hips/tiles/tests/test_tile.py
@@ -69,3 +69,7 @@ class TestHipsTileMeta:
     def test_skycoord_corners(self):
         assert_allclose(self.meta.skycoord_corners.ra.deg, [264.375, 258.75, 264.375, 270.])
         assert_allclose(self.meta.skycoord_corners.dec.deg, [-24.624318, -30., -35.685335, -30.])
+
+        meta = HipsTileMeta(order=3, ipix=450, file_format='fits', frame='galactic', tile_width=512)
+        assert_allclose(meta.skycoord_corners.l.deg, [264.375, 258.75, 264.375, 270.])
+        assert_allclose(meta.skycoord_corners.b.deg, [-24.624318, -30., -35.685335, -30.])

commit 3c8204abecb09c62748100cdac40d0b33def4513
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 30 12:25:52 2017 +0500

    Add property projection in SimpleTilePainter

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index 4808c6d..9a849cd 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -34,7 +34,6 @@ def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any])
     all_sky = np.zeros(geometry.shape)
     for tile in tiles:
         draw_tile = SimpleTilePainter(geometry, tile)
-        draw_tile.apply_projection()
         all_sky += draw_tile.warp_image()
     return all_sky

@@ -57,19 +56,19 @@ class SimpleTilePainter:
         self.tile = tile
         self.pt = None

-    def apply_projection(self) -> None:
-        """Apply projective transformation on a HiPS tile"""
+    @property
+    def projection(self) -> tf.ProjectiveTransform:
+        """Estimate projective transformation on a HiPS tile"""
         corners = self.tile.meta.skycoord_corners.to_pixel(self.geometry.wcs)
         src = np.array(corners).T.reshape((4, 2))
         dst = self.tile.meta.dst
         self.pt = tf.ProjectiveTransform()
         self.pt.estimate(src, dst)
+        return self.pt

     def warp_image(self) -> np.ndarray:
         """Warp a HiPS tile and a sky image"""
-        # This converts a big-endian byte integer to a float
-        data = self.tile.data.astype('float')
-        return tf.warp(data, self.pt, output_shape=self.geometry.shape)
+        return tf.warp(self.tile.data, self.projection, output_shape=self.geometry.shape, preserve_range=True)


 def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: HipsSurveyProperties) -> 'HipsTile':
@@ -90,8 +89,10 @@ def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: Hip
         Returns an object of  HipsTile
     """
     base_url = hips_survey.access_url + '/Norder' + str(hips_survey.hips_order) + '/Dir0/'
+    frames = dict({'equatorial': 'icrs', 'galactic': 'galactic', 'ecliptic': 'ecliptic'})
     for healpix_pixel_index in healpix_pixel_indices:
-        tile_meta = HipsTileMeta(order=order, ipix=healpix_pixel_index, file_format='fits')
+        tile_meta = HipsTileMeta(order=order, ipix=healpix_pixel_index,
+                                 frame=frames[hips_survey.hips_frame], file_format='fits')
         tile = HipsTile.fetch(tile_meta, base_url + tile_meta.filename)
         yield tile


commit a7d4ba4faf0dd826e94181a5ecd092036fdb12bd
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 30 12:22:16 2017 +0500

    Update test case for HipsTileMeta class

diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py
index cc0e65a..95701cd 100644
--- a/hips/tiles/tests/test_tile.py
+++ b/hips/tiles/tests/test_tile.py
@@ -1,10 +1,10 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
+import numpy as np
 from astropy.tests.helper import remote_data
 from numpy.testing import assert_allclose
 from numpy.testing import assert_equal

-from ..tile import HipsTile
-from ..tile_meta import HipsTileMeta
+from ..tile import HipsTile, HipsTileMeta


 class TestHipsTile:
@@ -46,7 +46,26 @@ class TestHipsTile:


 class TestHipsTileMeta:
+    @classmethod
+    def setup_class(cls):
+        cls.meta = HipsTileMeta(order=3, ipix=450, file_format='fits', frame='icrs', tile_width=512)
+
+    def test_path(self):
+        assert str(self.meta.path) == 'hips/tiles/tests/data'
+
+    def test_filename(self):
+        assert self.meta.filename == 'Npix450.fits'
+
+    def test_full_path(self):
+        assert str(self.meta.full_path) == 'hips/tiles/tests/data/Npix450.fits'
+
+    def test_nside(self):
+        assert self.meta.nside == 8
+
+    def test_dst(self):
+        dst = np.array([[511, 0], [511, 511], [0, 511], [0, 0]])
+        assert_allclose(self.meta.dst, dst)
+
     def test_skycoord_corners(self):
-        meta = HipsTileMeta(order=3, ipix=450, file_format='fits', frame='icrs', tile_width=512)
-        assert_allclose(meta.skycoord_corners.ra.deg, [264.375, 258.75, 264.375, 270.])
-        assert_allclose(meta.skycoord_corners.dec.deg, [-24.624318, -30., -35.685335, -30.])
+        assert_allclose(self.meta.skycoord_corners.ra.deg, [264.375, 258.75, 264.375, 270.])
+        assert_allclose(self.meta.skycoord_corners.dec.deg, [-24.624318, -30., -35.685335, -30.])

commit 2846fca6dd4eb6027b428920f8a81940c1e5a881
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 30 12:08:08 2017 +0500

    Merge HipsTile and HipsTileMeta within same file

diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py
index 76e2428..3ae0f4f 100644
--- a/hips/tiles/tile.py
+++ b/hips/tiles/tile.py
@@ -1,20 +1,105 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
-from pathlib import Path
 import urllib.request
 from io import BytesIO
+from pathlib import Path
+
+import healpy as hp
 import numpy as np
 from PIL import Image
+from astropy.coordinates import SkyCoord
 from astropy.io import fits
 from astropy.io.fits.header import Header
-from .tile_meta import HipsTileMeta
+
+from ..utils import boundaries

 __all__ = [
     'HipsTile',
+    'HipsTileMeta'
 ]

 __doctest_skip__ = ['HipsTile']


+class HipsTileMeta:
+    """HiPS tile metadata.
+
+    Parameters
+    ----------
+    order : `int`
+        HEALPix order
+    ipix : `int`
+        HEALPix pixel number
+    file_format : {'fits', 'jpg', 'png'}
+        File format
+    frame : {'icrs', 'galactic', 'ecliptic'}
+        Sky coordinate frame
+    tile_width : `int`
+        Tile width (in pixels)
+
+    Examples
+    --------
+    >>> from hips.tiles import HipsTileMeta
+    >>> tile_meta = HipsTileMeta(order=3, ipix=450, file_format='fits', frame='icrs', tile_width=512)
+    >>> tile_meta.skycoord_corners
+    <SkyCoord (Galactic): (l, b) in deg
+    [( 264.375, -24.62431835), ( 258.75 , -30.        ),
+     ( 264.375, -35.68533471), ( 270.   , -30.        )]>
+    """
+
+    def __init__(self, order: int, ipix: int, file_format: str, frame: str = 'galactic', tile_width: int = 512) -> None:
+        self.order = order
+        self.ipix = ipix
+        self.file_format = file_format
+        self.frame = frame
+        self.tile_width = tile_width
+
+    def __eq__(self, other: 'HipsTileMeta') -> bool:
+        return (
+            self.order == other.order and
+            self.ipix == other.ipix and
+            self.file_format == other.file_format and
+            self.tile_width == other.tile_width
+        )
+
+    @property
+    def path(self) -> Path:
+        """Default path for tile storage (`~pathlib.Path`)."""
+        return Path('hips', 'tiles', 'tests', 'data')
+
+    @property
+    def filename(self) -> str:
+        """Filename for HiPS tile (`str`)."""
+        return ''.join(['Npix', str(self.ipix), '.', self.file_format])
+
+    @property
+    def full_path(self) -> Path:
+        """Full path (folder and filename) (`~pathlib.Path`)"""
+        return self.path / self.filename
+
+    @property
+    def nside(self) -> int:
+        """nside of the HEALPix map"""
+        return hp.order2nside(self.order)
+
+    @property
+    def dst(self) -> np.ndarray:
+        """Destination array for projective transform"""
+        return np.array(
+            [[self.tile_width - 1, 0],
+             [self.tile_width - 1, self.tile_width - 1],
+             [0, self.tile_width - 1],
+             [0, 0]])
+
+    @property
+    def skycoord_corners(self) -> SkyCoord:
+        """Corner values for a HiPS tile"""
+        theta, phi = boundaries(self.nside, self.ipix)
+        if self.frame == 'galactic':
+            return SkyCoord(l=phi, b=np.pi / 2 - theta, unit='radian', frame=self.frame)
+        else:
+            return SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit='radian', frame=self.frame)
+
+
 class HipsTile:
     """HiPS tile container.

@@ -31,8 +116,7 @@ class HipsTile:

     Examples
     --------
-    >>> from hips.tiles import HipsTile
-    >>> from hips.tiles import HipsTileMeta
+    >>> from hips.tiles import HipsTile, HipsTileMeta
     >>> meta = HipsTileMeta(order=6, ipix=30889, file_format='fits')
     >>> url = 'http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.fits'
     >>> tile = HipsTile.fetch(meta, url)
diff --git a/hips/tiles/tile_meta.py b/hips/tiles/tile_meta.py
deleted file mode 100644
index 29488bc..0000000
--- a/hips/tiles/tile_meta.py
+++ /dev/null
@@ -1,92 +0,0 @@
-# Licensed under a 3-clause BSD style license - see LICENSE.rst
-from pathlib import Path
-
-import healpy as hp
-import numpy as np
-from astropy.coordinates import SkyCoord
-
-from ..utils import boundaries
-
-__all__ = [
-    'HipsTileMeta',
-]
-
-
-class HipsTileMeta:
-    """HiPS tile metadata.
-
-    Parameters
-    ----------
-    order : `int`
-        HEALPix order
-    ipix : `int`
-        HEALPix pixel number
-    file_format : {'fits', 'jpg', 'png'}
-        File format
-    frame : {'icrs', 'galactic', 'ecliptic'}
-        Sky coordinate frame
-    tile_width : `int`
-        Tile width (in pixels)
-
-    Examples
-    --------
-    >>> from hips.tiles import HipsTileMeta
-    >>> tile_meta = HipsTileMeta(order=3, ipix=450, file_format='fits', frame='galactic', tile_width=512)
-    >>> tile_meta.skycoord_corners
-    <SkyCoord (Galactic): (l, b) in deg
-    [( 264.375, -24.62431835), ( 258.75 , -30.        ),
-     ( 264.375, -35.68533471), ( 270.   , -30.        )]>
-    """
-
-    def __init__(self, order: int, ipix: int, file_format: str, frame: str = 'galactic', tile_width: int = 512) -> None:
-        self.order = order
-        self.ipix = ipix
-        self.file_format = file_format
-        self.frame = frame
-        self.tile_width = tile_width
-
-    def __eq__(self, other: 'HipsTileMeta') -> bool:
-        return (
-            self.order == other.order and
-            self.ipix == other.ipix and
-            self.file_format == other.file_format and
-            self.tile_width == other.tile_width
-        )
-
-    @property
-    def path(self) -> Path:
-        """Default path for tile storage (`~pathlib.Path`)."""
-        return Path('hips', 'tiles', 'tests', 'data')
-
-    @property
-    def filename(self) -> str:
-        """Filename for HiPS tile (`str`)."""
-        return ''.join(['Npix', str(self.ipix), '.', self.file_format])
-
-    @property
-    def full_path(self) -> Path:
-        """Full path (folder and filename) (`~pathlib.Path`)"""
-        return self.path / self.filename
-
-    @property
-    def nside(self) -> int:
-        """nside of the HEALPix map"""
-        return hp.order2nside(self.order)
-
-    @property
-    def dst(self) -> np.ndarray:
-        """Destination array for projective transform"""
-        return np.array(
-            [[self.tile_width - 1, 0],
-             [self.tile_width - 1, self.tile_width - 1],
-             [0, self.tile_width - 1],
-             [0, 0]])
-
-    @property
-    def skycoord_corners(self) -> SkyCoord:
-        """Corner values for a HiPS tile"""
-        theta, phi = boundaries(self.nside, self.ipix)
-        if self.frame == 'galactic':
-            return SkyCoord(l=phi, b=np.pi / 2 - theta, unit='radian', frame=self.frame)
-        else:
-            return SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit='radian', frame=self.frame)

commit 309a75a2c1b468dcd7e5969d2bd01f60f6a9005e
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 30 11:57:09 2017 +0500

    Pass fits_header to fits.PrimaryHDU function

diff --git a/docs/getting_started.rst b/docs/getting_started.rst
index 7018ccf..81c48db 100644
--- a/docs/getting_started.rst
+++ b/docs/getting_started.rst
@@ -24,5 +24,5 @@ Then it saves it on local disk in FITS file format.
 >>> url = 'https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties'
 >>> hips_survey = HipsSurveyProperties.fetch(url)  # doctest: +REMOTE_DATA
 >>> data = make_sky_image(geometry, hips_survey)
->>> hdu = fits.PrimaryHDU(data=data)
+>>> hdu = fits.PrimaryHDU(data=data, header=geometry.fits_header)
 >>> hdu.writeto('my_image.fits')
\ No newline at end of file

commit a0759cbe24b41ac50a86165db4438d474385291f
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 30 11:11:23 2017 +0500

    Remove test case for SimpleTilePainter class

diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py
index e99338f..80818f9 100644
--- a/hips/draw/tests/test_simple.py
+++ b/hips/draw/tests/test_simple.py
@@ -4,7 +4,7 @@ import pytest
 from astropy.utils.data import get_pkg_data_filename
 from numpy.testing import assert_allclose

-from ..simple import make_sky_image, draw_sky_image, SimpleTilePainter
+from ..simple import make_sky_image, draw_sky_image
 from ...tiles import HipsSurveyProperties, HipsTileMeta, HipsTile
 from ...utils.testing import get_hips_extra_file, make_test_wcs_geometry, requires_hips_extra

@@ -31,7 +31,6 @@ def get_test_tiles():
 def test_draw_sky_image():
     geometry = make_test_wcs_geometry(case=2)
     tiles = get_test_tiles()
-
     data = draw_sky_image(geometry, tiles)

     assert data.shape == geometry.shape
@@ -51,21 +50,3 @@ def test_make_sky_image():
     assert data.dtype == np.float64
     assert_allclose(data[200, 994], 3717.10091363)
     assert_allclose(data[200, 995], 3402.55292158)
-
-
-class TestSimpleTilePainter:
-    @classmethod
-    def setup_class(cls):
-        geometry = make_test_wcs_geometry(case=2)
-        tile = HipsTile.read(
-            meta=HipsTileMeta(order=3, ipix=450, file_format='fits', frame='icrs', tile_width=512),
-            filename=get_hips_extra_file('datasets/samples/DSS2Red/Norder3/Dir0/Npix450.fits'),
-        )
-        cls.simple_tile_painter = SimpleTilePainter(geometry, tile)
-
-    # def test_warp_image(self):
-    #     self.simple_tile_painter.warp_image()
-    #     assert_allclose(self.simple_tile_painter.tile.meta.skycoord_corners.ra.deg,
-    #                     [264.375, 258.75, 264.375, 270.])
-    #     assert_allclose(self.simple_tile_painter.tile.meta.skycoord_corners.dec.deg,
-    #                     [-24.624318, -30., -35.685335, -30.])

commit 4571dd88dbd34a408ed934cb47cddfa752642119
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 30 11:11:00 2017 +0500

    Add comment on data type conversion

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index f188755..4808c6d 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -67,7 +67,9 @@ class SimpleTilePainter:

     def warp_image(self) -> np.ndarray:
         """Warp a HiPS tile and a sky image"""
-        return tf.warp(self.tile.data.astype('float'), self.pt, output_shape=self.geometry.shape)
+        # This converts a big-endian byte integer to a float
+        data = self.tile.data.astype('float')
+        return tf.warp(data, self.pt, output_shape=self.geometry.shape)


 def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: HipsSurveyProperties) -> 'HipsTile':

commit 9e073c9370924fb0651db0c97631e53635b17abd
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 30 11:01:02 2017 +0500

    Remove docstring

diff --git a/hips/utils/testing.py b/hips/utils/testing.py
index ce8556c..d9d6d69 100644
--- a/hips/utils/testing.py
+++ b/hips/utils/testing.py
@@ -32,7 +32,6 @@ def requires_hips_extra():


 def make_test_wcs_geometry(case=0):
-    """An example Galactic CAR WCS that """
     if case == 0:
         return WCSGeometry.create(
             skydir=SkyCoord(3, 4, unit='deg', frame='galactic'),

commit 9f2e5fac6222fdaa6ddde8a1d6d973fe782968d6
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 30 11:00:12 2017 +0500

    Add test case for HipsTileMeta

diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py
index 1514ea6..cc0e65a 100644
--- a/hips/tiles/tests/test_tile.py
+++ b/hips/tiles/tests/test_tile.py
@@ -1,6 +1,8 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
-from numpy.testing import assert_equal
 from astropy.tests.helper import remote_data
+from numpy.testing import assert_allclose
+from numpy.testing import assert_equal
+
 from ..tile import HipsTile
 from ..tile_meta import HipsTileMeta

@@ -41,3 +43,10 @@ class TestHipsTile:
         # print(tile2.data.sum())
         # print((tile == tile2).all())
         # assert tile == tile2
+
+
+class TestHipsTileMeta:
+    def test_skycoord_corners(self):
+        meta = HipsTileMeta(order=3, ipix=450, file_format='fits', frame='icrs', tile_width=512)
+        assert_allclose(meta.skycoord_corners.ra.deg, [264.375, 258.75, 264.375, 270.])
+        assert_allclose(meta.skycoord_corners.dec.deg, [-24.624318, -30., -35.685335, -30.])

commit 69ae7642417b6b76796a448b9b9a3f4b387a9021
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 30 10:54:44 2017 +0500

    Add function apply_projection in SimpleTilePainter

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index 0f8b418..f188755 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -2,7 +2,6 @@
 """HiPS tile drawing -- simple method."""

 import numpy as np
-from astropy.coordinates import SkyCoord
 from skimage import transform as tf
 from typing import List, Generator, Any

@@ -31,9 +30,11 @@ def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any])
     np.ndarray
         Returns a numpy array containing all HiPS tiles projected onto it
     """
+    # TODO: Fix type annotation issue
     all_sky = np.zeros(geometry.shape)
     for tile in tiles:
         draw_tile = SimpleTilePainter(geometry, tile)
+        draw_tile.apply_projection()
         all_sky += draw_tile.warp_image()
     return all_sky

@@ -54,11 +55,19 @@ class SimpleTilePainter:
     def __init__(self, geometry: WCSGeometry, tile: HipsTile) -> None:
         self.geometry = geometry
         self.tile = tile
+        self.pt = None
+
+    def apply_projection(self) -> None:
+        """Apply projective transformation on a HiPS tile"""
+        corners = self.tile.meta.skycoord_corners.to_pixel(self.geometry.wcs)
+        src = np.array(corners).T.reshape((4, 2))
+        dst = self.tile.meta.dst
+        self.pt = tf.ProjectiveTransform()
+        self.pt.estimate(src, dst)

     def warp_image(self) -> np.ndarray:
         """Warp a HiPS tile and a sky image"""
-        pt = self.tile.meta.apply_projection(self.geometry.wcs)
-        return tf.warp(self.tile.data.astype('float'), pt, output_shape=self.geometry.shape)
+        return tf.warp(self.tile.data.astype('float'), self.pt, output_shape=self.geometry.shape)


 def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: HipsSurveyProperties) -> 'HipsTile':
diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py
index acec27f..e99338f 100644
--- a/hips/draw/tests/test_simple.py
+++ b/hips/draw/tests/test_simple.py
@@ -63,9 +63,9 @@ class TestSimpleTilePainter:
         )
         cls.simple_tile_painter = SimpleTilePainter(geometry, tile)

-    def test_warp_image(self):
-        self.simple_tile_painter.warp_image()
-        assert_allclose(self.simple_tile_painter.tile.meta.skycoord_corners.ra.deg,
-                        [264.375, 258.75, 264.375, 270.])
-        assert_allclose(self.simple_tile_painter.tile.meta.skycoord_corners.dec.deg,
-                        [-24.624318, -30., -35.685335, -30.])
+    # def test_warp_image(self):
+    #     self.simple_tile_painter.warp_image()
+    #     assert_allclose(self.simple_tile_painter.tile.meta.skycoord_corners.ra.deg,
+    #                     [264.375, 258.75, 264.375, 270.])
+    #     assert_allclose(self.simple_tile_painter.tile.meta.skycoord_corners.dec.deg,
+    #                     [-24.624318, -30., -35.685335, -30.])
diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py
index 234fb5d..76e2428 100644
--- a/hips/tiles/tile.py
+++ b/hips/tiles/tile.py
@@ -95,7 +95,6 @@ class HipsTile:

         if meta.file_format == 'fits':
             hdu_list = fits.open(str(path))
-            # This converts a big-endian byte integer to a float
             data = hdu_list[0].data
             header = hdu_list[0].header
             return cls(meta, data, header)

commit c7d5db5d8c54bb91fd3e91fd24d3991215bcdc1a
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 30 10:53:45 2017 +0500

    Remove function apply_projection from HipsTileMeta

diff --git a/hips/tiles/tile_meta.py b/hips/tiles/tile_meta.py
index 826e56b..29488bc 100644
--- a/hips/tiles/tile_meta.py
+++ b/hips/tiles/tile_meta.py
@@ -4,8 +4,6 @@ from pathlib import Path
 import healpy as hp
 import numpy as np
 from astropy.coordinates import SkyCoord
-from astropy.wcs import WCS
-from skimage import transform as tf

 from ..utils import boundaries

@@ -92,12 +90,3 @@ class HipsTileMeta:
             return SkyCoord(l=phi, b=np.pi / 2 - theta, unit='radian', frame=self.frame)
         else:
             return SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit='radian', frame=self.frame)
-
-    def apply_projection(self, wcs: WCS) -> tf.ProjectiveTransform:
-        """Apply projective transformation on a HiPS tile"""
-        corners = self.skycoord_corners.to_pixel(wcs)
-        src = np.array(corners).T.reshape((4, 2))
-        dst = self.dst
-        pt = tf.ProjectiveTransform()
-        pt.estimate(src, dst)
-        return pt

commit bee4bcd0542657c74b040947ec20a6d9ab41edc3
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 30 10:46:03 2017 +0500

    Document frame parameter in HipsTileMeta

diff --git a/hips/tiles/tile_meta.py b/hips/tiles/tile_meta.py
index 67759fb..826e56b 100644
--- a/hips/tiles/tile_meta.py
+++ b/hips/tiles/tile_meta.py
@@ -25,6 +25,8 @@ class HipsTileMeta:
         HEALPix pixel number
     file_format : {'fits', 'jpg', 'png'}
         File format
+    frame : {'icrs', 'galactic', 'ecliptic'}
+        Sky coordinate frame
     tile_width : `int`
         Tile width (in pixels)

@@ -38,7 +40,7 @@ class HipsTileMeta:
      ( 264.375, -35.68533471), ( 270.   , -30.        )]>
     """

-    def __init__(self, order: int, ipix: int, file_format: str, frame='galactic', tile_width: int = 512) -> None:
+    def __init__(self, order: int, ipix: int, file_format: str, frame: str = 'galactic', tile_width: int = 512) -> None:
         self.order = order
         self.ipix = ipix
         self.file_format = file_format

commit 9c5e687cf2948241dd13cedfe0ec79aba0389f38
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 30 10:43:07 2017 +0500

    Add missing import

diff --git a/docs/getting_started.rst b/docs/getting_started.rst
index ad0bdaa..7018ccf 100644
--- a/docs/getting_started.rst
+++ b/docs/getting_started.rst
@@ -11,6 +11,7 @@ It fetches a HiPS tile from a remote URL and draws it on a sky image.
 Then it saves it on local disk in FITS file format.

 >>> from astropy.io import fits
+>>> from astropy.coordinates import SkyCoord
 >>> from astropy.tests.helper import remote_data
 >>> from hips.utils import WCSGeometry
 >>> from hips.draw import make_sky_image

commit 9c82bc4c642c9417659ade65653490a39294b797
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 30 10:42:52 2017 +0500

    Use assert_allclose instead of assert

diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py
index de1b950..acec27f 100644
--- a/hips/draw/tests/test_simple.py
+++ b/hips/draw/tests/test_simple.py
@@ -36,9 +36,9 @@ def test_draw_sky_image():

     assert data.shape == geometry.shape
     assert data.dtype == np.float64
-    assert np.sum(data) == 4575235421.5126467
-    assert data[400, 500] == 2866.0101409848185
-    assert data[400, 501] == 2563.6916727348043
+    assert_allclose(np.sum(data), 4575235421.5126467)
+    assert_allclose(data[400, 500], 2866.0101409848185)
+    assert_allclose(data[400, 501], 2563.6916727348043)


 @pytest.mark.xfail
@@ -49,8 +49,8 @@ def test_make_sky_image():
     data = make_sky_image(geometry, hips_survey)
     assert data.shape == geometry.shape
     assert data.dtype == np.float64
-    assert data[200, 994] == 3717.10091363
-    assert data[200, 995] == 3402.55292158
+    assert_allclose(data[200, 994], 3717.10091363)
+    assert_allclose(data[200, 995], 3402.55292158)


 class TestSimpleTilePainter:

commit 8adff08b687c280f302df01bd8d1306a5e0a4008
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Thu Jun 29 23:41:41 2017 +0500

    Update drawing code

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index c6652d6..0f8b418 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -34,8 +34,6 @@ def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any])
     all_sky = np.zeros(geometry.shape)
     for tile in tiles:
         draw_tile = SimpleTilePainter(geometry, tile)
-        draw_tile.compute_corners()
-        draw_tile.compute_projection()
         all_sky += draw_tile.warp_image()
     return all_sky

@@ -56,20 +54,11 @@ class SimpleTilePainter:
     def __init__(self, geometry: WCSGeometry, tile: HipsTile) -> None:
         self.geometry = geometry
         self.tile = tile
-        self.corners = None
-        self.pt = None
-
-    def compute_corners(self):
-        self.corners = self.tile.meta.skycoord_corners.to_pixel(self.geometry.wcs)
-
-    def compute_projection(self) -> None:
-        src = np.array(self.corners).T.reshape((4, 2))
-        dst = self.tile.meta.dst
-        self.pt = tf.ProjectiveTransform()
-        self.pt.estimate(src, dst)

     def warp_image(self) -> np.ndarray:
-        return tf.warp(self.tile.data.astype('float'), self.pt, output_shape=self.geometry.shape)
+        """Warp a HiPS tile and a sky image"""
+        pt = self.tile.meta.apply_projection(self.geometry.wcs)
+        return tf.warp(self.tile.data.astype('float'), pt, output_shape=self.geometry.shape)


 def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: HipsSurveyProperties) -> 'HipsTile':
@@ -89,7 +78,7 @@ def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: Hip
     'HipsTile'
         Returns an object of  HipsTile
     """
-    base_url = hips_survey.data['moc_access_url'].rsplit('/', 1)[0] + '/Norder' + str(hips_survey.hips_order) + '/Dir0/'
+    base_url = hips_survey.access_url + '/Norder' + str(hips_survey.hips_order) + '/Dir0/'
     for healpix_pixel_index in healpix_pixel_indices:
         tile_meta = HipsTileMeta(order=order, ipix=healpix_pixel_index, file_format='fits')
         tile = HipsTile.fetch(tile_meta, base_url + tile_meta.filename)
@@ -99,6 +88,8 @@ def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: Hip
 def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -> np.ndarray:
     """Make sky image: fetch tiles and draw.

+    The example for this can be found on the :ref:`gs` page.
+
     Parameters
     ----------
     geometry : `~hips.utils.WCSGeometry`
@@ -110,23 +101,6 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) ->
     -------
     data : `~numpy.ndarray`
         Output image pixels
-
-    Examples
-    --------
-    >>> from astropy.io import fits
-    >>> from hips.utils import WCSGeometry
-    >>> from hips.draw import make_sky_image
-    >>> from hips.tiles import HipsSurveyProperties
-    >>> geometry = WCSGeometry.create(
-    ...     skydir=SkyCoord(0, 0, unit='deg', frame='galactic'),
-    ...     shape=(1000, 2000), coordsys='GAL',
-    ...     projection='AIT', cdelt=0.01, crpix=(1000, 500),
-    ... )
-    >>> url = 'https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties'
-    >>> hips_survey = HipsSurveyProperties.fetch(url)
-    >>> data = make_sky_image(geometry, hips_survey)
-    >>> hdu = fits.PrimaryHDU(data=data)
-    >>> hdu.writeto('my_image.fits')
     """
     healpix_pixel_indices = compute_healpix_pixel_indices(geometry, hips_survey.hips_order)
     # TODO: this isn't a good API. Will become better when we have a cache.
diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py
index 827b283..de1b950 100644
--- a/hips/draw/tests/test_simple.py
+++ b/hips/draw/tests/test_simple.py
@@ -1,6 +1,6 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
-import pytest
 import numpy as np
+import pytest
 from astropy.utils.data import get_pkg_data_filename
 from numpy.testing import assert_allclose

@@ -63,7 +63,9 @@ class TestSimpleTilePainter:
         )
         cls.simple_tile_painter = SimpleTilePainter(geometry, tile)

-    def test_compute_corners(self):
-        self.simple_tile_painter.compute_corners()
-        assert_allclose(self.simple_tile_painter.corners[0], [728.54880122, 1453.32078085, 1662.33429379, 931.69072155])
-        assert_allclose(self.simple_tile_painter.corners[1], [880.33972146, 997.1604671, 288.00518917, 175.4190427])
+    def test_warp_image(self):
+        self.simple_tile_painter.warp_image()
+        assert_allclose(self.simple_tile_painter.tile.meta.skycoord_corners.ra.deg,
+                        [264.375, 258.75, 264.375, 270.])
+        assert_allclose(self.simple_tile_painter.tile.meta.skycoord_corners.dec.deg,
+                        [-24.624318, -30., -35.685335, -30.])

commit 1f67310f98e5b873d6309928f428c66245ce3ba9
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Thu Jun 29 23:41:21 2017 +0500

    Add function apply_projection in HipsTileMeta

diff --git a/hips/tiles/tile_meta.py b/hips/tiles/tile_meta.py
index 6f2862d..67759fb 100644
--- a/hips/tiles/tile_meta.py
+++ b/hips/tiles/tile_meta.py
@@ -4,6 +4,8 @@ from pathlib import Path
 import healpy as hp
 import numpy as np
 from astropy.coordinates import SkyCoord
+from astropy.wcs import WCS
+from skimage import transform as tf

 from ..utils import boundaries

@@ -53,12 +55,12 @@ class HipsTileMeta:

     @property
     def path(self) -> Path:
-        """Return the default path for tile storage (`~pathlib.Path`)."""
+        """Default path for tile storage (`~pathlib.Path`)."""
         return Path('hips', 'tiles', 'tests', 'data')

     @property
     def filename(self) -> str:
-        """Return the filename of HiPS tile (`str`)."""
+        """Filename for HiPS tile (`str`)."""
         return ''.join(['Npix', str(self.ipix), '.', self.file_format])

     @property
@@ -67,13 +69,13 @@ class HipsTileMeta:
         return self.path / self.filename

     @property
-    def nside(self):
-        """Return the nside of the HEALPix map"""
+    def nside(self) -> int:
+        """nside of the HEALPix map"""
         return hp.order2nside(self.order)

     @property
-    def dst(self):
-        """Return destination array for projective transform"""
+    def dst(self) -> np.ndarray:
+        """Destination array for projective transform"""
         return np.array(
             [[self.tile_width - 1, 0],
              [self.tile_width - 1, self.tile_width - 1],
@@ -81,10 +83,19 @@ class HipsTileMeta:
              [0, 0]])

     @property
-    def skycoord_corners(self):
-        """Return corner values for a HiPS tile"""
+    def skycoord_corners(self) -> SkyCoord:
+        """Corner values for a HiPS tile"""
         theta, phi = boundaries(self.nside, self.ipix)
         if self.frame == 'galactic':
             return SkyCoord(l=phi, b=np.pi / 2 - theta, unit='radian', frame=self.frame)
         else:
             return SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit='radian', frame=self.frame)
+
+    def apply_projection(self, wcs: WCS) -> tf.ProjectiveTransform:
+        """Apply projective transformation on a HiPS tile"""
+        corners = self.skycoord_corners.to_pixel(wcs)
+        src = np.array(corners).T.reshape((4, 2))
+        dst = self.dst
+        pt = tf.ProjectiveTransform()
+        pt.estimate(src, dst)
+        return pt

commit 05b8ac8e97467576883da6c2dbd2be786ead4e73
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Thu Jun 29 23:40:31 2017 +0500

    Add test case for access_url property

diff --git a/hips/tiles/tests/test_description.py b/hips/tiles/tests/test_description.py
index b36aa02..d0e87dc 100644
--- a/hips/tiles/tests/test_description.py
+++ b/hips/tiles/tests/test_description.py
@@ -26,3 +26,6 @@ class TestHipsSurveyProperties:

     def test_tile_format(self):
         assert self.hips_survey_property.tile_format == 'jpeg'
+
+    def test_access_url(self):
+        assert self.hips_survey_property.access_url == 'http://alasky.u-strasbg.fr/DSS/DSSColor'

commit f7625a909254992cb4bbd0fc4ba7237bead65e8a
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Thu Jun 29 23:39:59 2017 +0500

    Add property access_url in HipsSurveyProperties

diff --git a/hips/tiles/description.py b/hips/tiles/description.py
index 21d2eb3..15d5a9a 100644
--- a/hips/tiles/description.py
+++ b/hips/tiles/description.py
@@ -108,3 +108,8 @@ class HipsSurveyProperties:
     def tile_format(self) -> str:
         """HiPS tile format (`str`)."""
         return self.data['hips_tile_format']
+
+    @property
+    def access_url(self):
+        """HiPS access url"""
+        return self.data['moc_access_url'].rsplit('/', 1)[0]

commit a32ba1b946c22c81d41bc8291206d90134d123d2
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Thu Jun 29 23:39:14 2017 +0500

    Add example in Getting Started page

diff --git a/docs/getting_started.rst b/docs/getting_started.rst
index e586127..ad0bdaa 100644
--- a/docs/getting_started.rst
+++ b/docs/getting_started.rst
@@ -6,4 +6,22 @@
 Getting started
 ***************

-Coming soon ...
+The example below shows a high level use case of the ``hips`` package.
+It fetches a HiPS tile from a remote URL and draws it on a sky image.
+Then it saves it on local disk in FITS file format.
+
+>>> from astropy.io import fits
+>>> from astropy.tests.helper import remote_data
+>>> from hips.utils import WCSGeometry
+>>> from hips.draw import make_sky_image
+>>> from hips.tiles import HipsSurveyProperties
+>>> geometry = WCSGeometry.create(
+...     skydir=SkyCoord(0, 0, unit='deg', frame='galactic'),
+...     shape=(1000, 2000), coordsys='GAL',
+...     projection='AIT', cdelt=0.01, crpix=(1000, 500),
+... )
+>>> url = 'https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties'
+>>> hips_survey = HipsSurveyProperties.fetch(url)  # doctest: +REMOTE_DATA
+>>> data = make_sky_image(geometry, hips_survey)
+>>> hdu = fits.PrimaryHDU(data=data)
+>>> hdu.writeto('my_image.fits')
\ No newline at end of file

commit 42ceb7af3d271bf60cd82f6b49136d1fe4bb3920
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Thu Jun 29 20:35:59 2017 +0500

    Get HiPS order from HipsSurveyProperties class

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index 006867d..c6652d6 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -89,7 +89,7 @@ def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: Hip
     'HipsTile'
         Returns an object of  HipsTile
     """
-    base_url = hips_survey.data['moc_access_url'].rsplit('/', 1)[0] + '/Norder3/Dir0/'
+    base_url = hips_survey.data['moc_access_url'].rsplit('/', 1)[0] + '/Norder' + str(hips_survey.hips_order) + '/Dir0/'
     for healpix_pixel_index in healpix_pixel_indices:
         tile_meta = HipsTileMeta(order=order, ipix=healpix_pixel_index, file_format='fits')
         tile = HipsTile.fetch(tile_meta, base_url + tile_meta.filename)

commit e487e44c68b8b4a8da44fc9fb82d932767abc471
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Thu Jun 29 20:33:32 2017 +0500

    Update drawing code

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index c106c8c..006867d 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -1,15 +1,13 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 """HiPS tile drawing -- simple method."""
-import os
-from pathlib import Path

 import numpy as np
 from astropy.coordinates import SkyCoord
 from skimage import transform as tf
-from typing import List
+from typing import List, Generator, Any

 from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta
-from ..utils import WCSGeometry, compute_healpix_pixel_indices, boundaries
+from ..utils import WCSGeometry, compute_healpix_pixel_indices

 __all__ = [
     'draw_sky_image',
@@ -18,7 +16,7 @@ __all__ = [
 ]


-def draw_sky_image(geometry: WCSGeometry, tiles: List[HipsTile]) -> np.ndarray:
+def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any]) -> np.ndarray:
     """Draw sky image using the simple and quick method.

     Parameters
@@ -35,7 +33,7 @@ def draw_sky_image(geometry: WCSGeometry, tiles: List[HipsTile]) -> np.ndarray:
     """
     all_sky = np.zeros(geometry.shape)
     for tile in tiles:
-        draw_tile = SimpleTilePainter(geometry, tile, all_sky.shape)
+        draw_tile = SimpleTilePainter(geometry, tile)
         draw_tile.compute_corners()
         draw_tile.compute_projection()
         all_sky += draw_tile.warp_image()
@@ -43,7 +41,9 @@ def draw_sky_image(geometry: WCSGeometry, tiles: List[HipsTile]) -> np.ndarray:


 class SimpleTilePainter:
-    """A class which iteratively draws a tile following the naive algorithm steps mentioned `here <https://hips.readthedocs.io/en/latest/drawing_algo.html#naive-algorithm>`_.
+    """Paint a single tile using a simple projective transformation method.
+
+    The algorithm implemented is described here: :ref:`drawing_algo`.

     Parameters
     ----------
@@ -51,21 +51,16 @@ class SimpleTilePainter:
         An object of WCSGeometry
     tile : `HipsTile`
        An object of HipsTile
-    shape : tuple
-        Shape of the all-sky image
     """

-    def __init__(self, geometry: WCSGeometry, tile: HipsTile, shape: tuple) -> None:
+    def __init__(self, geometry: WCSGeometry, tile: HipsTile) -> None:
         self.geometry = geometry
         self.tile = tile
-        self.shape = shape
         self.corners = None
         self.pt = None

-    def compute_corners(self) -> None:
-        theta, phi = boundaries(self.tile.meta.nside, self.tile.meta.ipix)
-        radec = SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit='radian', frame='icrs')
-        self.corners = radec.to_pixel(self.geometry.wcs)
+    def compute_corners(self):
+        self.corners = self.tile.meta.skycoord_corners.to_pixel(self.geometry.wcs)

     def compute_projection(self) -> None:
         src = np.array(self.corners).T.reshape((4, 2))
@@ -74,7 +69,7 @@ class SimpleTilePainter:
         self.pt.estimate(src, dst)

     def warp_image(self) -> np.ndarray:
-        return tf.warp(self.tile.data, self.pt, output_shape=self.shape)
+        return tf.warp(self.tile.data.astype('float'), self.pt, output_shape=self.geometry.shape)


 def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: HipsSurveyProperties) -> 'HipsTile':
@@ -94,9 +89,10 @@ def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: Hip
     'HipsTile'
         Returns an object of  HipsTile
     """
+    base_url = hips_survey.data['moc_access_url'].rsplit('/', 1)[0] + '/Norder3/Dir0/'
     for healpix_pixel_index in healpix_pixel_indices:
         tile_meta = HipsTileMeta(order=order, ipix=healpix_pixel_index, file_format='fits')
-        tile = HipsTile.fetch(tile_meta, hips_survey.base_url)
+        tile = HipsTile.fetch(tile_meta, base_url + tile_meta.filename)
         yield tile


@@ -129,23 +125,12 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) ->
     >>> url = 'https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties'
     >>> hips_survey = HipsSurveyProperties.fetch(url)
     >>> data = make_sky_image(geometry, hips_survey)
-    >>> hdu = fits.PrimaryHDU(data=data, header=geometry.header)
+    >>> hdu = fits.PrimaryHDU(data=data)
     >>> hdu.writeto('my_image.fits')
     """
     healpix_pixel_indices = compute_healpix_pixel_indices(geometry, hips_survey.hips_order)
-    """TODO: Take user input for HiPS survey"""
-    path = Path(os.environ['HIPS_EXTRA'])
-    tiles_path = path / 'datasets' / 'samples' / 'DSS2Red' / 'Norder3' / 'Dir0'
-
-    tiles = []
-    for pixel_index in healpix_pixel_indices:
-        meta = HipsTileMeta(hips_survey.hips_order, pixel_index, 'fits')
-        filepath = str(tiles_path / meta.filename)
-        tiles.append(HipsTile.read(meta, filepath))
-
-    # Fetch the tiles
     # TODO: this isn't a good API. Will become better when we have a cache.
-    # tiles = _fetch_tiles(healpix_pixel_indices, order, hips_survey)
+    tiles = _fetch_tiles(healpix_pixel_indices, hips_survey.hips_order, hips_survey)

     image_data = draw_sky_image(geometry, tiles)

diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py
index 81f3ce4..827b283 100644
--- a/hips/draw/tests/test_simple.py
+++ b/hips/draw/tests/test_simple.py
@@ -1,23 +1,26 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
-import numpy as np
 import pytest
-from astropy.tests.helper import remote_data
+import numpy as np
+from astropy.utils.data import get_pkg_data_filename
 from numpy.testing import assert_allclose

-from ..simple import make_sky_image, draw_sky_image
+from ..simple import make_sky_image, draw_sky_image, SimpleTilePainter
 from ...tiles import HipsSurveyProperties, HipsTileMeta, HipsTile
 from ...utils.testing import get_hips_extra_file, make_test_wcs_geometry, requires_hips_extra


 def get_test_tiles():
-    # TODO: check if this tile is inside our image
+    frames = dict({'equatorial': 'icrs', 'galactic': 'galactic', 'ecliptic': 'ecliptic'})
+    filename = get_pkg_data_filename('../../tiles/tests/data/properties.txt')
+    hips_survey = HipsSurveyProperties.read(filename)
+
     tile1 = HipsTile.read(
-        meta=HipsTileMeta(order=3, ipix=450, file_format='fits', tile_width=512),
+        meta=HipsTileMeta(order=3, ipix=450, file_format='fits', frame=frames[hips_survey.hips_frame], tile_width=512),
         filename=get_hips_extra_file('datasets/samples/DSS2Red/Norder3/Dir0/Npix450.fits'),
     )

     tile2 = HipsTile.read(
-        meta=HipsTileMeta(order=3, ipix=451, file_format='fits', tile_width=512),
+        meta=HipsTileMeta(order=3, ipix=451, file_format='fits', frame=frames[hips_survey.hips_frame], tile_width=512),
         filename=get_hips_extra_file('datasets/samples/DSS2Red/Norder3/Dir0/Npix451.fits'),
     )

@@ -34,19 +37,33 @@ def test_draw_sky_image():
     assert data.shape == geometry.shape
     assert data.dtype == np.float64
     assert np.sum(data) == 4575235421.5126467
-    assert_allclose(data[400, 500:504], [2866.010141, 2563.691673, 2580.759013, 2746.608711])
+    assert data[400, 500] == 2866.0101409848185
+    assert data[400, 501] == 2563.6916727348043


 @pytest.mark.xfail
-@remote_data
 def test_make_sky_image():
     url = 'https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties'
     hips_survey = HipsSurveyProperties.fetch(url)
     geometry = make_test_wcs_geometry(case=2)
-
     data = make_sky_image(geometry, hips_survey)
-
     assert data.shape == geometry.shape
     assert data.dtype == np.float64
-    assert_allclose(data[200, 994:1000], [3717.10091363, 3402.55292158, 3181.16613051, 2868.45175662, 2832.23001706,
-                                          2779.23366271])
+    assert data[200, 994] == 3717.10091363
+    assert data[200, 995] == 3402.55292158
+
+
+class TestSimpleTilePainter:
+    @classmethod
+    def setup_class(cls):
+        geometry = make_test_wcs_geometry(case=2)
+        tile = HipsTile.read(
+            meta=HipsTileMeta(order=3, ipix=450, file_format='fits', frame='icrs', tile_width=512),
+            filename=get_hips_extra_file('datasets/samples/DSS2Red/Norder3/Dir0/Npix450.fits'),
+        )
+        cls.simple_tile_painter = SimpleTilePainter(geometry, tile)
+
+    def test_compute_corners(self):
+        self.simple_tile_painter.compute_corners()
+        assert_allclose(self.simple_tile_painter.corners[0], [728.54880122, 1453.32078085, 1662.33429379, 931.69072155])
+        assert_allclose(self.simple_tile_painter.corners[1], [880.33972146, 997.1604671, 288.00518917, 175.4190427])
diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py
index 70423b2..234fb5d 100644
--- a/hips/tiles/tile.py
+++ b/hips/tiles/tile.py
@@ -95,7 +95,8 @@ class HipsTile:

         if meta.file_format == 'fits':
             hdu_list = fits.open(str(path))
-            data = hdu_list[0].data.astype('float')
+            # This converts a big-endian byte integer to a float
+            data = hdu_list[0].data
             header = hdu_list[0].header
             return cls(meta, data, header)
         else:

commit c3d3d2729a4467667793af560df9b6c371625360
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Thu Jun 29 20:33:21 2017 +0500

    Fix link to WCSGeometry class in docs

diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py
index f98a2bf..829248f 100644
--- a/hips/utils/healpix.py
+++ b/hips/utils/healpix.py
@@ -77,7 +77,7 @@ def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, order: int) -> np.n

     Parameters
     ----------
-    wcs_geometry : WCSGeometry
+    wcs_geometry : `WCSGeometry`
         Container for WCS object and image shape
     order : int
         The order of the HEALPix

commit 1e4bb3b268f7b5893c5279a472d88b26fb4ecafc
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Thu Jun 29 20:30:48 2017 +0500

    Add property skycoord_corner to HipsTileMeta

diff --git a/hips/tiles/tile_meta.py b/hips/tiles/tile_meta.py
index 53a2a21..6f2862d 100644
--- a/hips/tiles/tile_meta.py
+++ b/hips/tiles/tile_meta.py
@@ -3,6 +3,9 @@ from pathlib import Path

 import healpy as hp
 import numpy as np
+from astropy.coordinates import SkyCoord
+
+from ..utils import boundaries

 __all__ = [
     'HipsTileMeta',
@@ -22,12 +25,22 @@ class HipsTileMeta:
         File format
     tile_width : `int`
         Tile width (in pixels)
+
+    Examples
+    --------
+    >>> from hips.tiles import HipsTileMeta
+    >>> tile_meta = HipsTileMeta(order=3, ipix=450, file_format='fits', frame='galactic', tile_width=512)
+    >>> tile_meta.skycoord_corners
+    <SkyCoord (Galactic): (l, b) in deg
+    [( 264.375, -24.62431835), ( 258.75 , -30.        ),
+     ( 264.375, -35.68533471), ( 270.   , -30.        )]>
     """

-    def __init__(self, order: int, ipix: int, file_format: str, tile_width: int = 512) -> None:
+    def __init__(self, order: int, ipix: int, file_format: str, frame='galactic', tile_width: int = 512) -> None:
         self.order = order
         self.ipix = ipix
         self.file_format = file_format
+        self.frame = frame
         self.tile_width = tile_width

     def __eq__(self, other: 'HipsTileMeta') -> bool:
@@ -60,5 +73,18 @@ class HipsTileMeta:

     @property
     def dst(self):
+        """Return destination array for projective transform"""
         return np.array(
-            [[self.tile_width - 1, 0], [self.tile_width - 1, self.tile_width - 1], [0, self.tile_width - 1], [0, 0]])
+            [[self.tile_width - 1, 0],
+             [self.tile_width - 1, self.tile_width - 1],
+             [0, self.tile_width - 1],
+             [0, 0]])
+
+    @property
+    def skycoord_corners(self):
+        """Return corner values for a HiPS tile"""
+        theta, phi = boundaries(self.nside, self.ipix)
+        if self.frame == 'galactic':
+            return SkyCoord(l=phi, b=np.pi / 2 - theta, unit='radian', frame=self.frame)
+        else:
+            return SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit='radian', frame=self.frame)

commit 7897504b97bc594d55aa23182af76dfd29ab152d
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Thu Jun 29 10:46:33 2017 +0500

    Fix drawing code

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index 64c5eed..c106c8c 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -49,7 +49,7 @@ class SimpleTilePainter:
     ----------
     geometry : `~hips.utils.WCSGeometry`
         An object of WCSGeometry
-    tile : HipsTile
+    tile : `HipsTile`
        An object of HipsTile
     shape : tuple
         Shape of the all-sky image
@@ -65,12 +65,10 @@ class SimpleTilePainter:
     def compute_corners(self) -> None:
         theta, phi = boundaries(self.tile.meta.nside, self.tile.meta.ipix)
         radec = SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit='radian', frame='icrs')
-        self.corners = []
-        for i in range(len(radec.ra.deg)):
-            self.corners.append([radec.ra.deg[i], radec.dec.deg[i]])
+        self.corners = radec.to_pixel(self.geometry.wcs)

     def compute_projection(self) -> None:
-        src = self.geometry.wcs.wcs_world2pix(self.corners, 0)
+        src = np.array(self.corners).T.reshape((4, 2))
         dst = self.tile.meta.dst
         self.pt = tf.ProjectiveTransform()
         self.pt.estimate(src, dst)
diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py
index 8c7aa97..81f3ce4 100644
--- a/hips/draw/tests/test_simple.py
+++ b/hips/draw/tests/test_simple.py
@@ -2,6 +2,7 @@
 import numpy as np
 import pytest
 from astropy.tests.helper import remote_data
+from numpy.testing import assert_allclose

 from ..simple import make_sky_image, draw_sky_image
 from ...tiles import HipsSurveyProperties, HipsTileMeta, HipsTile
@@ -11,19 +12,18 @@ from ...utils.testing import get_hips_extra_file, make_test_wcs_geometry, requir
 def get_test_tiles():
     # TODO: check if this tile is inside our image
     tile1 = HipsTile.read(
-        meta=HipsTileMeta(order=3, ipix=658, file_format='fits', tile_width=512),
-        filename=get_hips_extra_file('datasets/samples/DSS2Red/Norder3/Dir0/Npix658.fits'),
+        meta=HipsTileMeta(order=3, ipix=450, file_format='fits', tile_width=512),
+        filename=get_hips_extra_file('datasets/samples/DSS2Red/Norder3/Dir0/Npix450.fits'),
     )

     tile2 = HipsTile.read(
-        meta=HipsTileMeta(order=3, ipix=659, file_format='fits', tile_width=512),
-        filename=get_hips_extra_file('datasets/samples/DSS2Red/Norder3/Dir0/Npix659.fits'),
+        meta=HipsTileMeta(order=3, ipix=451, file_format='fits', tile_width=512),
+        filename=get_hips_extra_file('datasets/samples/DSS2Red/Norder3/Dir0/Npix451.fits'),
     )

     return [tile1, tile2]


-@pytest.mark.xfail
 @requires_hips_extra()
 def test_draw_sky_image():
     geometry = make_test_wcs_geometry(case=2)
@@ -33,10 +33,11 @@ def test_draw_sky_image():

     assert data.shape == geometry.shape
     assert data.dtype == np.float64
-
-    assert data[0, 0] == 0.0
+    assert np.sum(data) == 4575235421.5126467
+    assert_allclose(data[400, 500:504], [2866.010141, 2563.691673, 2580.759013, 2746.608711])


+@pytest.mark.xfail
 @remote_data
 def test_make_sky_image():
     url = 'https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties'
@@ -47,4 +48,5 @@ def test_make_sky_image():

     assert data.shape == geometry.shape
     assert data.dtype == np.float64
-    assert data[800, 1000] == 1794.7673494847763
+    assert_allclose(data[200, 994:1000], [3717.10091363, 3402.55292158, 3181.16613051, 2868.45175662, 2832.23001706,
+                                          2779.23366271])

commit 53a0df576c6f1be0659cd4927609ae134cdb3643
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Wed Jun 28 17:51:42 2017 +0500

    Reformat some code

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index 342a107..64c5eed 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -3,10 +3,8 @@
 import os
 from pathlib import Path

-import healpy as hp
 import numpy as np
 from astropy.coordinates import SkyCoord
-from astropy.wcs import WCS
 from skimage import transform as tf
 from typing import List

@@ -66,19 +64,14 @@ class SimpleTilePainter:

     def compute_corners(self) -> None:
         theta, phi = boundaries(self.tile.meta.nside, self.tile.meta.ipix)
-        self.corners = SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit='radian', frame='icrs')
-        self.test_corners = []
-        for i in range(len(self.corners.ra.deg)):
-            self.test_corners.append([self.corners.ra.deg[i], self.corners.dec.deg[i]])
+        radec = SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit='radian', frame='icrs')
+        self.corners = []
+        for i in range(len(radec.ra.deg)):
+            self.corners.append([radec.ra.deg[i], radec.dec.deg[i]])

     def compute_projection(self) -> None:
-        # print(self.corners.to_pixel(self.geometry.wcs))
-        # src = self.geometry.wcs.wcs_world2pix(self.corners.to_pixel(self.geometry.wcs), 0)
-        src = np.array(self.corners.to_pixel(self.geometry.wcs))
-        print('Corners: ', self.test_corners)
-        print('Source: ', src)
-        print(self.geometry.wcs.wcs_world2pix(self.corners.to_pixel(self.geometry.wcs), 0))
-        dst = np.array([[511, 0], [511, 511], [0, 511], [0, 0]])
+        src = self.geometry.wcs.wcs_world2pix(self.corners, 0)
+        dst = self.tile.meta.dst
         self.pt = tf.ProjectiveTransform()
         self.pt.estimate(src, dst)

@@ -142,6 +135,7 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) ->
     >>> hdu.writeto('my_image.fits')
     """
     healpix_pixel_indices = compute_healpix_pixel_indices(geometry, hips_survey.hips_order)
+    """TODO: Take user input for HiPS survey"""
     path = Path(os.environ['HIPS_EXTRA'])
     tiles_path = path / 'datasets' / 'samples' / 'DSS2Red' / 'Norder3' / 'Dir0'

diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py
index 4f89897..f98a2bf 100644
--- a/hips/utils/healpix.py
+++ b/hips/utils/healpix.py
@@ -4,10 +4,10 @@
 This module contains wrapper functions around HEALPix utilizing
 the healpy library.
 """
-from typing import Tuple
+import healpy as hp
 import numpy as np
 from astropy.coordinates import SkyCoord
-import healpy as hp
+from typing import Tuple

 from .wcs import WCSGeometry


commit 243ef55f752c701c94f4b3736c20524f62b560a1
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Wed Jun 28 17:50:52 2017 +0500

    Add dst property in HipsTileMeta class

diff --git a/hips/tiles/tile_meta.py b/hips/tiles/tile_meta.py
index fe0bacb..53a2a21 100644
--- a/hips/tiles/tile_meta.py
+++ b/hips/tiles/tile_meta.py
@@ -1,6 +1,8 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 from pathlib import Path
+
 import healpy as hp
+import numpy as np

 __all__ = [
     'HipsTileMeta',
@@ -54,4 +56,9 @@ class HipsTileMeta:
     @property
     def nside(self):
         """Return the nside of the HEALPix map"""
-        return hp.order2nside(self.order)
\ No newline at end of file
+        return hp.order2nside(self.order)
+
+    @property
+    def dst(self):
+        return np.array(
+            [[self.tile_width - 1, 0], [self.tile_width - 1, self.tile_width - 1], [0, self.tile_width - 1], [0, 0]])

commit 3acf68e070ceeac9cce7e86c89f3c0350ed478aa
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Wed Jun 28 10:36:37 2017 +0500

    Update corner computation

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index 9ab55e9..342a107 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -16,6 +16,7 @@ from ..utils import WCSGeometry, compute_healpix_pixel_indices, boundaries
 __all__ = [
     'draw_sky_image',
     'make_sky_image',
+    'SimpleTilePainter'
 ]


@@ -24,7 +25,7 @@ def draw_sky_image(geometry: WCSGeometry, tiles: List[HipsTile]) -> np.ndarray:

     Parameters
     ----------
-    geometry : 'WCSGeometry'
+    geometry : `~hips.utils.WCSGeometry`
         An object of WCSGeometry
     tiles : List[HipsTile]
         A list of HipsTile
@@ -36,23 +37,23 @@ def draw_sky_image(geometry: WCSGeometry, tiles: List[HipsTile]) -> np.ndarray:
     """
     all_sky = np.zeros(geometry.shape)
     for tile in tiles:
-        draw_tile = DrawSkyImageOneTile(geometry, tile, all_sky.shape)
+        draw_tile = SimpleTilePainter(geometry, tile, all_sky.shape)
         draw_tile.compute_corners()
-        draw_tile.apply_projection()
+        draw_tile.compute_projection()
         all_sky += draw_tile.warp_image()
     return all_sky


-class DrawSkyImageOneTile:
-    """A private function for drawing a tile over a sky image.
+class SimpleTilePainter:
+    """A class which iteratively draws a tile following the naive algorithm steps mentioned `here <https://hips.readthedocs.io/en/latest/drawing_algo.html#naive-algorithm>`_.

     Parameters
     ----------
-    geometry : 'WCSGeometry'
+    geometry : `~hips.utils.WCSGeometry`
         An object of WCSGeometry
     tile : HipsTile
        An object of HipsTile
-    shape ; tuple
+    shape : tuple
         Shape of the all-sky image
     """

@@ -60,20 +61,23 @@ class DrawSkyImageOneTile:
         self.geometry = geometry
         self.tile = tile
         self.shape = shape
-        self.wcs = WCS(tile.header)
         self.corners = None
         self.pt = None

     def compute_corners(self) -> None:
-        nside = hp.order2nside(self.tile.meta.order)
-        theta, phi = boundaries(nside, self.tile.meta.ipix)
-        radec = SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit='radian', frame='icrs')
-        self.corners = []
-        for i in range(len(radec.ra.deg)):
-            self.corners.append([radec.ra.deg[i], radec.dec.deg[i]])
-
-    def apply_projection(self) -> None:
-        src = self.geometry.wcs.wcs_world2pix(self.corners, 0)
+        theta, phi = boundaries(self.tile.meta.nside, self.tile.meta.ipix)
+        self.corners = SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit='radian', frame='icrs')
+        self.test_corners = []
+        for i in range(len(self.corners.ra.deg)):
+            self.test_corners.append([self.corners.ra.deg[i], self.corners.dec.deg[i]])
+
+    def compute_projection(self) -> None:
+        # print(self.corners.to_pixel(self.geometry.wcs))
+        # src = self.geometry.wcs.wcs_world2pix(self.corners.to_pixel(self.geometry.wcs), 0)
+        src = np.array(self.corners.to_pixel(self.geometry.wcs))
+        print('Corners: ', self.test_corners)
+        print('Source: ', src)
+        print(self.geometry.wcs.wcs_world2pix(self.corners.to_pixel(self.geometry.wcs), 0))
         dst = np.array([[511, 0], [511, 511], [0, 511], [0, 0]])
         self.pt = tf.ProjectiveTransform()
         self.pt.estimate(src, dst)
diff --git a/hips/tiles/tile_meta.py b/hips/tiles/tile_meta.py
index e1fca4c..fe0bacb 100644
--- a/hips/tiles/tile_meta.py
+++ b/hips/tiles/tile_meta.py
@@ -1,5 +1,6 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 from pathlib import Path
+import healpy as hp

 __all__ = [
     'HipsTileMeta',
@@ -49,3 +50,8 @@ class HipsTileMeta:
     def full_path(self) -> Path:
         """Full path (folder and filename) (`~pathlib.Path`)"""
         return self.path / self.filename
+
+    @property
+    def nside(self):
+        """Return the nside of the HEALPix map"""
+        return hp.order2nside(self.order)
\ No newline at end of file

commit c838f6412cfeddea00d8449ee048d0429ed6a09d
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Tue Jun 27 23:18:42 2017 +0500

    Update drawing code

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index f82df43..9ab55e9 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -34,48 +34,52 @@ def draw_sky_image(geometry: WCSGeometry, tiles: List[HipsTile]) -> np.ndarray:
     np.ndarray
         Returns a numpy array containing all HiPS tiles projected onto it
     """
-    # TODO: copy over (and clean up a little) the drawing code you already have in notebooks.
-    # Suggestion: don't debug the code now, just put it here.
-    # First implement simple tile caching so that you can draw without needing to make web queries.
-
-    # Probably this function should just loop over tiles and call a `_draw_sky_image_one_tile`
-    # helper function, and sum up the results.
     all_sky = np.zeros(geometry.shape)
     for tile in tiles:
-        nside = hp.order2nside(tile.meta.order)
-        theta, phi = boundaries(nside, tile.meta.ipix)
-        radec = SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit='radian', frame='icrs')
-        corners = []
-        for i in range(len(radec.ra.deg)):
-            corners.append([radec.ra.deg[i], radec.dec.deg[i]])
-        all_sky += _draw_sky_image_one_tile(corners, WCS(tile.header), tile, all_sky.shape)
+        draw_tile = DrawSkyImageOneTile(geometry, tile, all_sky.shape)
+        draw_tile.compute_corners()
+        draw_tile.apply_projection()
+        all_sky += draw_tile.warp_image()
     return all_sky


-def _draw_sky_image_one_tile(corners: list, wcs: WCS, tile: HipsTile, shape: tuple) -> np.ndarray:
+class DrawSkyImageOneTile:
     """A private function for drawing a tile over a sky image.

     Parameters
     ----------
-    corners : `~astropy.coordinates.SkyCoord`
-        Four corners of a HiPS tile
-    wcs : `~astropy.wcs.WCS`
-        WCS projection of a HiPS tile
-    tile : 'HipsTile'
-        An object of HipsTile
+    geometry : 'WCSGeometry'
+        An object of WCSGeometry
+    tile : HipsTile
+       An object of HipsTile
     shape ; tuple
         Shape of the all-sky image
-
-    Returns
-    -------
-    np.ndarray
-        Returns a numpy array containing the projected HiPS tile
     """
-    src = wcs.wcs_world2pix(corners, 0)
-    dst = np.array([[511, 0], [511, 511], [0, 511], [0, 0]])
-    pt = tf.ProjectiveTransform()
-    pt.estimate(src, dst)
-    return tf.warp(tile.data, pt, output_shape=shape)
+
+    def __init__(self, geometry: WCSGeometry, tile: HipsTile, shape: tuple) -> None:
+        self.geometry = geometry
+        self.tile = tile
+        self.shape = shape
+        self.wcs = WCS(tile.header)
+        self.corners = None
+        self.pt = None
+
+    def compute_corners(self) -> None:
+        nside = hp.order2nside(self.tile.meta.order)
+        theta, phi = boundaries(nside, self.tile.meta.ipix)
+        radec = SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit='radian', frame='icrs')
+        self.corners = []
+        for i in range(len(radec.ra.deg)):
+            self.corners.append([radec.ra.deg[i], radec.dec.deg[i]])
+
+    def apply_projection(self) -> None:
+        src = self.geometry.wcs.wcs_world2pix(self.corners, 0)
+        dst = np.array([[511, 0], [511, 511], [0, 511], [0, 0]])
+        self.pt = tf.ProjectiveTransform()
+        self.pt.estimate(src, dst)
+
+    def warp_image(self) -> np.ndarray:
+        return tf.warp(self.tile.data, self.pt, output_shape=self.shape)


 def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: HipsSurveyProperties) -> 'HipsTile':
@@ -118,26 +122,21 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) ->

     Examples
     --------
-    Define which image you want:
-
-    >>> geometry = tbd
-
-    Define which HiPS survey you want:
-
-    >>> hips_survey = tbd
-
-    Compute the image:
-
-    >>> from hips import make_sky_image
-    >>> data = make_sky_image(geometry, hips_survey)
-
-    If you want, you can save the image to file:
     >>> from astropy.io import fits
+    >>> from hips.utils import WCSGeometry
+    >>> from hips.draw import make_sky_image
+    >>> from hips.tiles import HipsSurveyProperties
+    >>> geometry = WCSGeometry.create(
+    ...     skydir=SkyCoord(0, 0, unit='deg', frame='galactic'),
+    ...     shape=(1000, 2000), coordsys='GAL',
+    ...     projection='AIT', cdelt=0.01, crpix=(1000, 500),
+    ... )
+    >>> url = 'https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties'
+    >>> hips_survey = HipsSurveyProperties.fetch(url)
+    >>> data = make_sky_image(geometry, hips_survey)
     >>> hdu = fits.PrimaryHDU(data=data, header=geometry.header)
     >>> hdu.writeto('my_image.fits')
     """
-    # Compute list of tiles needed
-
     healpix_pixel_indices = compute_healpix_pixel_indices(geometry, hips_survey.hips_order)
     path = Path(os.environ['HIPS_EXTRA'])
     tiles_path = path / 'datasets' / 'samples' / 'DSS2Red' / 'Norder3' / 'Dir0'
@@ -152,7 +151,6 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) ->
     # TODO: this isn't a good API. Will become better when we have a cache.
     # tiles = _fetch_tiles(healpix_pixel_indices, order, hips_survey)

-    # Draw the image
     image_data = draw_sky_image(geometry, tiles)

     return image_data
diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py
index 8f293a7..8c7aa97 100644
--- a/hips/draw/tests/test_simple.py
+++ b/hips/draw/tests/test_simple.py
@@ -34,7 +34,7 @@ def test_draw_sky_image():
     assert data.shape == geometry.shape
     assert data.dtype == np.float64

-    assert data[0, 0] == 0.060903854985925092
+    assert data[0, 0] == 0.0


 @remote_data
@@ -47,5 +47,4 @@ def test_make_sky_image():

     assert data.shape == geometry.shape
     assert data.dtype == np.float64
-
-    assert data[0, 0] == 0.060903854985925092
+    assert data[800, 1000] == 1794.7673494847763

commit 91e7e394a4f7bf363122703f55048ddcfee5118b
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Tue Jun 27 23:18:27 2017 +0500

    Store FITS data as float type

diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py
index 76e2428..70423b2 100644
--- a/hips/tiles/tile.py
+++ b/hips/tiles/tile.py
@@ -95,7 +95,7 @@ class HipsTile:

         if meta.file_format == 'fits':
             hdu_list = fits.open(str(path))
-            data = hdu_list[0].data
+            data = hdu_list[0].data.astype('float')
             header = hdu_list[0].header
             return cls(meta, data, header)
         else:

commit 818a9102b3f12ab61ed13d3987b31b227719a700
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Tue Jun 27 11:49:58 2017 +0500

    Update drawing function

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index 8d7c268..f82df43 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -1,9 +1,15 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 """HiPS tile drawing -- simple method."""
-import numpy as np
+import os
+from pathlib import Path
+
 import healpy as hp
-from typing import List
+import numpy as np
+from astropy.coordinates import SkyCoord
+from astropy.wcs import WCS
 from skimage import transform as tf
+from typing import List
+
 from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta
 from ..utils import WCSGeometry, compute_healpix_pixel_indices, boundaries

@@ -16,7 +22,17 @@ __all__ = [
 def draw_sky_image(geometry: WCSGeometry, tiles: List[HipsTile]) -> np.ndarray:
     """Draw sky image using the simple and quick method.

-    TODO: fill in code here
+    Parameters
+    ----------
+    geometry : 'WCSGeometry'
+        An object of WCSGeometry
+    tiles : List[HipsTile]
+        A list of HipsTile
+
+    Returns
+    -------
+    np.ndarray
+        Returns a numpy array containing all HiPS tiles projected onto it
     """
     # TODO: copy over (and clean up a little) the drawing code you already have in notebooks.
     # Suggestion: don't debug the code now, just put it here.
@@ -24,24 +40,68 @@ def draw_sky_image(geometry: WCSGeometry, tiles: List[HipsTile]) -> np.ndarray:

     # Probably this function should just loop over tiles and call a `_draw_sky_image_one_tile`
     # helper function, and sum up the results.
-    for tile in HipsTile:
+    all_sky = np.zeros(geometry.shape)
+    for tile in tiles:
         nside = hp.order2nside(tile.meta.order)
-        corners = boundaries(nside, tile.meta.ipix)
-        # tile.meta.
+        theta, phi = boundaries(nside, tile.meta.ipix)
+        radec = SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit='radian', frame='icrs')
+        corners = []
+        for i in range(len(radec.ra.deg)):
+            corners.append([radec.ra.deg[i], radec.dec.deg[i]])
+        all_sky += _draw_sky_image_one_tile(corners, WCS(tile.header), tile, all_sky.shape)
+    return all_sky

-    return np.zeros(geometry.shape)

-def _draw_sky_image_one_tile(corners: tuple, tile: HipsTile) -> np.ndarray:
+def _draw_sky_image_one_tile(corners: list, wcs: WCS, tile: HipsTile, shape: tuple) -> np.ndarray:
+    """A private function for drawing a tile over a sky image.

+    Parameters
+    ----------
+    corners : `~astropy.coordinates.SkyCoord`
+        Four corners of a HiPS tile
+    wcs : `~astropy.wcs.WCS`
+        WCS projection of a HiPS tile
+    tile : 'HipsTile'
+        An object of HipsTile
+    shape ; tuple
+        Shape of the all-sky image

-def _fetch_tiles(healpix_pixel_indices, order, hips_survey):
+    Returns
+    -------
+    np.ndarray
+        Returns a numpy array containing the projected HiPS tile
+    """
+    src = wcs.wcs_world2pix(corners, 0)
+    dst = np.array([[511, 0], [511, 511], [0, 511], [0, 0]])
+    pt = tf.ProjectiveTransform()
+    pt.estimate(src, dst)
+    return tf.warp(tile.data, pt, output_shape=shape)
+
+
+def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: HipsSurveyProperties) -> 'HipsTile':
+    """Fetch HiPS tiles from a remote URL.
+
+    Parameters
+    ----------
+    healpix_pixel_indices : np.ndarray
+        A list of HEALPix pixel indices
+    order : int
+        Order of the HEALPix map
+    hips_survey : HipsSurveyProperties
+        An object of HipsSurveyProperties
+
+    Returns
+    -------
+    'HipsTile'
+        Returns an object of  HipsTile
+    """
     for healpix_pixel_index in healpix_pixel_indices:
-        tile_meta = HipsTileMeta(order=order, ipix=healpix_pixel_index, file_format='jpg')
+        tile_meta = HipsTileMeta(order=order, ipix=healpix_pixel_index, file_format='fits')
         tile = HipsTile.fetch(tile_meta, hips_survey.base_url)
         yield tile


-def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties):
+def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -> np.ndarray:
     """Make sky image: fetch tiles and draw.

     Parameters
@@ -77,12 +137,20 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties):
     >>> hdu.writeto('my_image.fits')
     """
     # Compute list of tiles needed
-    order = hips_survey.hips_order
-    healpix_pixel_indices = compute_healpix_pixel_indices(geometry, order)
+
+    healpix_pixel_indices = compute_healpix_pixel_indices(geometry, hips_survey.hips_order)
+    path = Path(os.environ['HIPS_EXTRA'])
+    tiles_path = path / 'datasets' / 'samples' / 'DSS2Red' / 'Norder3' / 'Dir0'
+
+    tiles = []
+    for pixel_index in healpix_pixel_indices:
+        meta = HipsTileMeta(hips_survey.hips_order, pixel_index, 'fits')
+        filepath = str(tiles_path / meta.filename)
+        tiles.append(HipsTile.read(meta, filepath))

     # Fetch the tiles
     # TODO: this isn't a good API. Will become better when we have a cache.
-    tiles = _fetch_tiles(healpix_pixel_indices, order, hips_survey)
+    # tiles = _fetch_tiles(healpix_pixel_indices, order, hips_survey)

     # Draw the image
     image_data = draw_sky_image(geometry, tiles)
diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py
index fae10bc..8f293a7 100644
--- a/hips/draw/tests/test_simple.py
+++ b/hips/draw/tests/test_simple.py
@@ -1,32 +1,31 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
-import pytest
 import numpy as np
+import pytest
 from astropy.tests.helper import remote_data
-from ...tiles import HipsSurveyProperties, HipsTileMeta, HipsTile
+
 from ..simple import make_sky_image, draw_sky_image
+from ...tiles import HipsSurveyProperties, HipsTileMeta, HipsTile
 from ...utils.testing import get_hips_extra_file, make_test_wcs_geometry, requires_hips_extra


 def get_test_tiles():
-    # Could make manually a list of two tiles to draw
-    # Pointing to FITS tiles in hips-extra like e.g.
-    # hips-extra/datasets/samples//DSS2Red/Norder3/Dir0/Npix659.fits
-
     # TODO: check if this tile is inside our image
     tile1 = HipsTile.read(
-        meta=HipsTileMeta(order=3, ipix=659, file_format='fits', tile_width=512),
-        filename=get_hips_extra_file('datasets/samples//DSS2Red/Norder3/Dir0/Npix659.fits'),
+        meta=HipsTileMeta(order=3, ipix=658, file_format='fits', tile_width=512),
+        filename=get_hips_extra_file('datasets/samples/DSS2Red/Norder3/Dir0/Npix658.fits'),
     )

-    # TODO: also fetch a second one.
+    tile2 = HipsTile.read(
+        meta=HipsTileMeta(order=3, ipix=659, file_format='fits', tile_width=512),
+        filename=get_hips_extra_file('datasets/samples/DSS2Red/Norder3/Dir0/Npix659.fits'),
+    )

-    return [tile1]
+    return [tile1, tile2]


+@pytest.mark.xfail
 @requires_hips_extra()
 def test_draw_sky_image():
-    # filename = get_pkg_data_filename('../../tiles/tests/data/properties.txt')
-    # hips_survey = HipsSurveyProperties.read(filename)
     geometry = make_test_wcs_geometry(case=2)
     tiles = get_test_tiles()

@@ -35,13 +34,12 @@ def test_draw_sky_image():
     assert data.shape == geometry.shape
     assert data.dtype == np.float64

-    assert data[100, 200] == 42
+    assert data[0, 0] == 0.060903854985925092


-@pytest.mark.xfail
 @remote_data
 def test_make_sky_image():
-    url = 'https://raw.githubusercontent.com/hipspy/hips/master/hips/tiles/tests/data/properties.txt'
+    url = 'https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties'
     hips_survey = HipsSurveyProperties.fetch(url)
     geometry = make_test_wcs_geometry(case=2)

@@ -50,4 +48,4 @@ def test_make_sky_image():
     assert data.shape == geometry.shape
     assert data.dtype == np.float64

-    assert data[100, 200] == 42
+    assert data[0, 0] == 0.060903854985925092
diff --git a/hips/utils/testing.py b/hips/utils/testing.py
index 50c7e11..ce8556c 100644
--- a/hips/utils/testing.py
+++ b/hips/utils/testing.py
@@ -2,10 +2,12 @@

 Not of use for users / outside this package.
 """
-import pytest
 import os
 from pathlib import Path
+
+import pytest
 from astropy.coordinates import SkyCoord
+
 from .wcs import WCSGeometry


@@ -50,4 +52,4 @@ def make_test_wcs_geometry(case=0):
             projection='AIT', cdelt=0.01, crpix=(1000, 500),
         )
     else:
-        raise ValueError()
+        raise ValueError()  # pragma: no cover

commit 120ff07dd6b4cd13bcd0131927746fab7dbb7f7d
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Mon Jun 26 13:49:52 2017 +0500

    Add skeleton of drawing code

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index 309c15d..8d7c268 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -1,9 +1,11 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 """HiPS tile drawing -- simple method."""
-from typing import List
 import numpy as np
+import healpy as hp
+from typing import List
+from skimage import transform as tf
 from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta
-from ..utils import WCSGeometry, compute_healpix_pixel_indices
+from ..utils import WCSGeometry, compute_healpix_pixel_indices, boundaries

 __all__ = [
     'draw_sky_image',
@@ -22,9 +24,15 @@ def draw_sky_image(geometry: WCSGeometry, tiles: List[HipsTile]) -> np.ndarray:

     # Probably this function should just loop over tiles and call a `_draw_sky_image_one_tile`
     # helper function, and sum up the results.
+    for tile in HipsTile:
+        nside = hp.order2nside(tile.meta.order)
+        corners = boundaries(nside, tile.meta.ipix)
+        # tile.meta.

     return np.zeros(geometry.shape)

+def _draw_sky_image_one_tile(corners: tuple, tile: HipsTile) -> np.ndarray:
+

 def _fetch_tiles(healpix_pixel_indices, order, hips_survey):
     for healpix_pixel_index in healpix_pixel_indices:

commit 1dcc81164f456a9bd4fa7f7905630c94c429d308
Merge: a5ac131 32db7fb
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Mon Jun 26 12:04:52 2017 +0500

    Merge branch 'master' of https://github.com/hipspy/hips into tiles.draw

commit a3f7261fe255ac8a6904741f576d99bddeba3ffd
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Sun Jun 25 17:25:00 2017 +0500

    Add documentation on HIPS_EXTRA environment variable

diff --git a/docs/develop.rst b/docs/develop.rst
index c34c07f..4f2fdd5 100644
--- a/docs/develop.rst
+++ b/docs/develop.rst
@@ -6,6 +6,13 @@
 Develop
 *******

+To run tests accessing files from `hips-extra <https://github.com/hipspy/hips-extra>`_
+repository, users must have it cloned on their system, otherwise some test cases
+will be skipped. This contains tiles from different HiPS surveys which are used
+by the drawing module. After this, the ``HIPS_EXTRA`` environment variable must
+be set up on their system. On UNIX operating systems, this can be set using
+``export HIPS_EXTRA=path/to/hips-extra``.
+
 Want to contribute to the ``hips`` package?

 Great! Talk to us by filing a Github issue any time
diff --git a/docs/installation.rst b/docs/installation.rst
index 094aeeb..2e33e82 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -80,6 +80,10 @@ To check if there are any issues with your installation, you can run the tests:

     python -c 'import hips; hips.test()'

+.. note::
+
+    For more information, see the :ref:`develop` page.
+
 Development version
 ===================


commit 8add0250a612c22b9a5ecb5dbd4ce9e4a9369f89
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Thu Jun 22 18:01:32 2017 +0500

    Add function make_sky_image

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index 362127d..3dc8f05 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -1,5 +1,23 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 """HiPS tile drawing -- simple method."""

+import healpy as hp
+
+from ..tiles import HipsSurveyProperties
+from ..tiles import HipsTile
+from ..tiles import HipsTileMeta
+from ..utils import WCSGeometry
+from ..utils import compute_healpix_pixel_indices
+
 __all__ = [
+    'make_sky_image'
 ]
+
+
+def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties):
+    order = hips_survey.hips_order
+    healpix_pixel_indices = compute_healpix_pixel_indices(geometry, order)
+    tiles = list()
+    for healpix_pixel_index in healpix_pixel_indices:
+        tile_meta = HipsTileMeta(order=order, ipix=healpix_pixel_index, file_format='jpg')
+        tiles.append(HipsTile(tile_meta))
diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py
index 9dce85d..9f8d838 100644
--- a/hips/draw/tests/test_simple.py
+++ b/hips/draw/tests/test_simple.py
@@ -1 +1,21 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
+
+from astropy.coordinates import SkyCoord
+from astropy.tests.helper import remote_data
+
+from hips.tiles import HipsSurveyProperties
+from hips.utils import WCSGeometry
+from ..simple import make_sky_image
+
+
+@remote_data
+def test_make_sky_image():
+    url = 'https://raw.githubusercontent.com/hipspy/hips/master/hips/tiles/tests/data/properties.txt'
+    hips_survey = HipsSurveyProperties.fetch(url)
+
+    shape = (1000, 2000)
+    y_center, x_center = shape[0] / 2, shape[1] / 2
+    skycoord = SkyCoord(0, 0, unit="deg")
+    wcs_geometry = WCSGeometry.create(skydir=skycoord, shape=shape, coordsys='CEL', projection='AIT', cdelt=0.01,
+                                      crpix=(y_center, x_center))
+    make_sky_image(wcs_geometry, hips_survey)
diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py
index 63f1cd7..4f89897 100644
--- a/hips/utils/healpix.py
+++ b/hips/utils/healpix.py
@@ -8,6 +8,7 @@ from typing import Tuple
 import numpy as np
 from astropy.coordinates import SkyCoord
 import healpy as hp
+
 from .wcs import WCSGeometry

 __all__ = [
@@ -68,7 +69,7 @@ def boundaries(nside: int, pix: int, nest: bool = True) -> tuple:
     return theta, phi


-def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, nside: int) -> np.ndarray:
+def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, order: int) -> np.ndarray:
     """Compute HEALPix pixels within a minimal disk covering a given WCSGeometry.

     This function calls `healpy.pixelfunc.ang2vec` and `healpy.query_disc`
@@ -78,8 +79,8 @@ def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, nside: int) -> np.n
     ----------
     wcs_geometry : WCSGeometry
         Container for WCS object and image shape
-    nside : int
-        The nside of the HEALPix map
+    order : int
+        The order of the HEALPix

     Returns
     -------
@@ -89,7 +90,6 @@ def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, nside: int) -> np.n
     Examples
     --------
     >>> from astropy.coordinates import SkyCoord
-    >>> import healpy as hp
     >>> from hips.utils import WCSGeometry
     >>> from hips.utils import compute_healpix_pixel_indices
     >>> skycoord = SkyCoord(10, 20, unit="deg")
@@ -98,8 +98,7 @@ def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, nside: int) -> np.n
     ...     coordsys='CEL', projection='AIT',
     ...     cdelt=1.0, crpix=(1., 1.),
     ... )
-    >>> nside = hp.order2nside(order=3)
-    >>> compute_healpix_pixel_indices(wcs_geometry, nside)
+    >>> compute_healpix_pixel_indices(wcs_geometry, order=3)
     array([176, 207, 208, 239, 240, 271, 272])
     """
     center_coord = wcs_geometry.center_skycoord
@@ -108,5 +107,5 @@ def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, nside: int) -> np.n
     radius = np.nanmax(separation.rad)

     vec = _skycoord_to_vec(center_coord)
-
+    nside = hp.order2nside(order)
     return hp.query_disc(nside, vec, radius)
diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py
index 0fc0267..864eadb 100644
--- a/hips/utils/tests/test_healpix.py
+++ b/hips/utils/tests/test_healpix.py
@@ -23,6 +23,5 @@ def test_boundaries():

 def test_compute_healpix_pixel_indices():
     wcs_geometry = make_test_wcs_geometry(case=1)
-    nside = hp.order2nside(order=3)
-    pixels = compute_healpix_pixel_indices(wcs_geometry, nside)
+    pixels = compute_healpix_pixel_indices(wcs_geometry, order=3)
     assert_allclose(pixels, [176, 207, 208, 239, 240, 271, 272])

commit 449e4df92ed21259d527e9b2fb325ae708e46045
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Mon Jun 19 18:05:07 2017 +0500

    Add class HipsSurveyList for parsing list of hips surveys

diff --git a/hips/tiles/description.py b/hips/tiles/description.py
index 86ccfae..2062836 100644
--- a/hips/tiles/description.py
+++ b/hips/tiles/description.py
@@ -1,26 +1,34 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
+import urllib.request
 from collections import OrderedDict

 __all__ = [
-    'HipsDescription',
+    'HipsSurveyProperties',
 ]


-class HipsDescription:
+class HipsSurveyProperties:
     """HiPS properties container.

     Parameters
     ----------
     properties : `~collections.OrderedDict`
-        HiPS description properties
+        HiPS survey properties
+
+    Examples
+    --------
+        >>> url = 'https://raw.githubusercontent.com/hipspy/hips/master/hips/tiles/tests/data/properties.txt'
+        >>> hips_survey_property = HipsSurveyProperties.fetch(url)
+        >>> hips_survey_property.base_url
+        'http://alasky.u-strasbg.fr/DSS/DSSColor'
     """

     def __init__(self, properties: OrderedDict) -> None:
         self.properties = properties

     @classmethod
-    def read(cls, filename: str) -> 'HipsDescription':
-        """Read from HiPS description file (`HipsDescription`).
+    def read(cls, filename: str) -> 'HipsSurveyProperties':
+        """Read from HiPS survey description file (`HipsSurveyProperties`).

         Parameters
         ----------
@@ -33,20 +41,33 @@ class HipsDescription:
         return cls.parse(text)

     @classmethod
-    def parse(cls, text: str) -> 'HipsDescription':
-        """Parse HiPS description text (`HipsDescription`).
+    def fetch(cls, url: str) -> 'HipsSurveyProperties':
+        """Read from HiPS survey description file from remote URL (`HipsSurveyProperties`).
+
+        Parameters
+        ----------
+        url : str
+            URL containing HiPS properties
+        """
+
+        response = urllib.request.urlopen(url).read()
+        text = response.decode('utf-8')
+        return cls.parse(text)
+
+    @classmethod
+    def parse(cls, text: str) -> 'HipsSurveyProperties':
+        """Parse HiPS survey description text (`HipsSurveyProperties`).

         Parameters
         ----------
         text : str
-            HiPS properties text
+            Text containing HiPS survey properties
         """
         properties = OrderedDict()
         for line in text.split('\n'):
             # Skip empty or comment lines
             if line == '' or line.startswith('#'):
                 continue
-
             try:
                 key, value = [_.strip() for _ in line.split('=')]
                 properties[key] = value
diff --git a/hips/tiles/surveys.py b/hips/tiles/surveys.py
new file mode 100644
index 0000000..511dca5
--- /dev/null
+++ b/hips/tiles/surveys.py
@@ -0,0 +1,70 @@
+# Licensed under a 3-clause BSD style license - see LICENSE.rst
+import urllib.request
+from typing import List
+from astropy.table import Table
+from collections import OrderedDict
+from .description import HipsSurveyProperties
+
+__all__ = [
+    'HipsSurveyPropertiesList',
+]
+
+
+class HipsSurveyPropertiesList:
+    """HiPS survey container.
+
+    Parameters
+    ----------
+    surveys : List[HipsSurveyProperties]
+        HiPS surveys
+    """
+
+    def __init__(self, surveys: List[HipsSurveyProperties]) -> None:
+        self.surveys = surveys
+
+    @classmethod
+    def read(cls, filename: str) -> 'HipsSurveyPropertiesList':
+        """Read HiPS list from file (`HipsSurveyPropertiesList`).
+
+        Parameters
+        ----------
+        filename : str
+            HiPS list filename
+        """
+        with open(filename, encoding='utf-8', errors='ignore') as fh:
+            text = fh.read()
+        return cls.parse(text)
+
+    @classmethod
+    def fetch(cls, url: str='http://alasky.unistra.fr/MocServer/query?hips_service_url=*&dataproduct_type=!catalog&dataproduct_type=!cube&get=record') -> 'HipsSurveyPropertiesList':
+        """Fetch HiPS list text from remote location (`HipsSurveyPropertiesList`).
+
+        Parameters
+        ----------
+        url : str
+            HiPS list URL
+        """
+        response = urllib.request.urlopen(url).read()
+        text = str(response)
+        return cls.parse(text)
+
+    @classmethod
+    def parse(cls, text: str) -> 'HipsSurveyPropertiesList':
+        """Parse HiPS list text (`HipsSurveyPropertiesList`).
+
+        Parameters
+        ----------
+        text : str
+            HiPS list text
+        """
+        surveys = list()
+        for raw_survey in text.split('\n\n'):
+            surveys.append(HipsSurveyProperties.parse(raw_survey))
+        return cls(surveys)
+
+    @property
+    def table(self) -> Table:
+        """Astropy Table containing HiPS surveys (`HipsSurveyPropertiesList`)."""
+        table = Table()
+        table['surveys'] = [survey.properties for survey in self.surveys]
+        return table
diff --git a/hips/tiles/tests/data/surveys.txt b/hips/tiles/tests/data/surveys.txt
new file mode 100644
index 0000000..876234c
--- /dev/null
+++ b/hips/tiles/tests/data/surveys.txt
@@ -0,0 +1,202 @@
+ID                   = CDS/C/MUSE-M42
+creator_did          = ivo://CDS/C/MUSE-M42
+obs_collection       = MUSE-M42
+obs_title            = MUSE map of the central Orion Nebula (M 42)
+obs_description      = Integral-field spectroscopic dataset of the central part of the Orion Nebula (M 42), observed with the MUSE instrument at the ESO VLT (reduced the data with the public MUSE pipeline) representing a FITS cube with a spatial size of ~5.9'x4.9' (corresponding to ~0.76 pc x 0.63 pc) and a contiguous wavelength coverage of 4595...9366 Angstrom, spatially sampled at 0.2", with a sampling of 1.25 Angstrom in dispersion direction.
+obs_ack              = Based on data obtained from the ESO/VLT
+prov_progenitor      = MUSE Consortium
+bib_reference        = 2015A&A...582A.114W
+bib_reference_url    = http://simbad.u-strasbg.fr/simbad/sim-ref?bibcode=2015A%26A...582A.114W
+obs_copyright        = Copyright mention of the original data
+obs_copyright_url    = http://muse-vlt.eu/science/m42/
+hips_release_date    = 2015-07-07T00:29Z
+hips_builder         = Aladin/HipsGen v9.505
+hips_order           = 12
+hips_pixel_cut       = 0 7760
+hips_tile_format     = png fits
+hips_cube_depth      = 3818
+hips_cube_firstframe  = 1909
+hips_frame           = equatorial
+dataproduct_type     = image
+t_min                = 56693
+t_max                = 56704
+em_min               = 4,595e-7
+em_max               = 9,366e-7
+hips_version         = 1.31
+hips_creation_date   = 03/07/15 12:00:30
+hips_creator         = CDS (P.Fernique)
+hips_tile_width      = 512
+hips_status          = public master clonableOnce
+hips_pixel_bitpix    = -32
+data_pixel_bitpix    = -32
+hips_hierarchy       = mean
+hips_initial_ra      = 83.82094
+hips_initial_dec     = -5.39542
+hips_initial_fov     = 0.09811
+hips_pixel_scale     = 2.795E-5
+s_pixel_scale        = 5.555E-5
+moc_sky_fraction     = 2.980E-7
+hips_estsize         = 87653
+data_bunit           = 10**(-20)*erg/s/cm**2/Angstrom
+data_cube_crpix3     = 1
+data_cube_crval3     = 4595
+data_cube_cdelt3     = 1.25
+data_cube_bunit3     = Angstrom
+client_application   = AladinDesktopBeta
+client_application   = AladinDesktop
+hips_copyright       = CNRS/Unistra
+obs_regime           = Optical
+hips_service_url     = http://alasky.unistra.fr/MUSE/MUSE-M42
+hips_service_url_1   = http://alaskybis.unistra.fr/MUSE/MUSE-M42
+hips_status_1        = public mirror clonable
+hips_service_url_2   = https://alaskybis.unistra.fr/MUSE/MUSE-M42
+hips_status_2        = public mirror clonable
+moc_order            = 12
+obs_initial_ra       = 83.82094
+obs_initial_dec      = -5.39542
+obs_initial_fov      = 0.014314526715905856
+TIMESTAMP            = 1490387811000
+
+ID                   = CDS/P/2MASS/H
+creator_did          = ivo://CDS/P/2MASS/H
+obs_collection       = The Two Micron All Sky Survey - H band (2MASS H)
+obs_title            = 2MASS H (1.66 microns)
+obs_description      = 2MASS has uniformly scanned the entire sky in three near-infrared bands to detect and characterize point sources brighter than about 1 mJy in each band, with signal-to-noise ratio (SNR) greater than 10, using a pixel size of 2.0". This has achieved an 80,000-fold improvement in sensitivity relative to earlier surveys. 2MASS used two highly-automated 1.3-m telescopes, one at Mt. Hopkins, AZ, and one at CTIO, Chile. Each telescope was equipped with a three-channel camera, each channel consisting of a 256x256 array of HgCdTe detectors, capable of observing the sky simultaneously at J (1.25 microns), H (1.65 microns), and Ks (2.17 microns). The University of Massachusetts (UMass) was responsible for the overall management of the project, and for developing the infrared cameras and on-site computing systems at both facilities. The Infrared Processing and Analysis Center (IPAC) is responsible for all data processing through the Production Pipeline, and construction and distribution of the data products. Funding is provided primarily by NASA and the NSF
+obs_copyright_url    = http://www.ipac.caltech.edu/2mass/
+prov_progenitor      = IPAC/NASA
+client_category      = Image/Infrared/2MASS
+client_sort_key      = 04-001-03
+hips_creation_date   = 2013-05-06T20:36Z
+hips_release_date    = 2016-04-22T13:48Z
+hips_builder         = Aladin/HipsGen v9.017
+hips_creator         = CDS (A.Oberto)
+hips_version         = 1.31
+hips_order           = 9
+hips_frame           = equatorial
+hips_tile_width      = 512
+hips_tile_format     = jpeg fits
+dataproduct_type     = image
+hips_pixel_cut       = 0 60
+moc_access_url       = http://alasky.u-strasbg.fr/2MASS/H/Moc.fits
+hips_service_url     = http://alasky.unistra.fr/2MASS/H
+hips_status          = public master clonableOnce
+hips_copyright       = CNRS/Unistra
+obs_ack              = University of Massachusetts & IPAC/Caltech
+bib_reference        = 2006AJ....131.1163S
+bib_reference_url    = http://cdsbib.u-strasbg.fr/cgi-bin/cdsbib?2006AJ....131.1163S
+obs_copyright        = University of Massachusetts & IPAC/Caltech
+t_min                = 50600
+t_max                = 51941
+obs_regime           = Infrared
+em_min               = 1.525E-6
+em_max               = 1.798E-6
+hips_hierarchy       = mean
+hips_pixel_scale     = 2.236E-4
+hips_service_url_1   = http://alaskybis.unistra.fr/2MASS/H
+hips_status_1        = public mirror clonableOnce
+hips_service_url_2   = https://alaskybis.unistra.fr/2MASS/H
+hips_status_2        = public mirror clonableOnce
+moc_sky_fraction     = 1
+moc_order            = 9
+obs_initial_ra       = 0
+obs_initial_dec      = +0
+obs_initial_fov      = 0.11451621372724685
+client_application   = AladinDesktop
+TIMESTAMP            = 1490028262000
+# a test comment
+
+ID                   = CDS/P/2MASS/J
+creator_did          = ivo://CDS/P/2MASS/J
+obs_collection       = The Two Micron All Sky Survey - J band (2MASS J)
+obs_title            = 2MASS J (1.23 microns)
+obs_description      = 2MASS has uniformly scanned the entire sky in three near-infrared bands to detect and characterize point sources brighter than about 1 mJy in each band, with signal-to-noise ratio (SNR) greater than 10, using a pixel size of 2.0". This has achieved an 80,000-fold improvement in sensitivity relative to earlier surveys. 2MASS used two highly-automated 1.3-m telescopes, one at Mt. Hopkins, AZ, and one at CTIO, Chile. Each telescope was equipped with a three-channel camera, each channel consisting of a 256x256 array of HgCdTe detectors, capable of observing the sky simultaneously at J (1.25 microns), H (1.65 microns), and Ks (2.17 microns). The University of Massachusetts (UMass) was responsible for the overall management of the project, and for developing the infrared cameras and on-site computing systems at both facilities. The Infrared Processing and Analysis Center (IPAC) is responsible for all data processing through the Production Pipeline, and construction and distribution of the data products. Funding is provided primarily by NASA and the NSF
+obs_copyright_url    = http://www.ipac.caltech.edu/2mass/
+prov_progenitor      = IPAC/NASA
+client_category      = Image/Infrared/2MASS
+client_sort_key      = 04-001-02
+hips_creation_date   = 2014-02-11T11:28Z
+hips_release_date    = 2016-04-22T14:03Z
+hips_builder         = Aladin/HipsGen v9.017
+hips_creator         = CDS (A.Oberto)
+hips_version         = 1.31
+hips_order           = 9
+hips_frame           = equatorial
+hips_tile_width      = 512
+hips_tile_format     = jpeg fits
+dataproduct_type     = image
+hips_pixel_cut       = -1 116
+moc_access_url       = http://alasky.u-strasbg.fr/2MASS/J/Moc.fits
+hips_service_url     = http://alasky.unistra.fr/2MASS/J
+hips_status          = public master clonableOnce
+hips_copyright       = CNRS/Unistra
+obs_ack              = University of Massachusetts & IPAC/Caltech
+bib_reference        = 2006AJ....131.1163S
+bib_reference_url    = http://cdsbib.u-strasbg.fr/cgi-bin/cdsbib?2006AJ....131.1163S
+obs_copyright        = University of Massachusetts & IPAC/Caltech
+t_min                = 50600
+t_max                = 51941
+obs_regime           = Infrared
+em_min               = 1.147E-6
+em_max               = 1.323E-6
+hips_hierarchy       = mean
+hips_pixel_scale     = 2.236E-4
+hips_progenitor_url  = http://alasky.unistra.fr/2MASS/J/HpxFinder
+hips_service_url_1   = http://alaskybis.unistra.fr/2MASS/J
+hips_status_1        = public mirror clonableOnce
+hips_service_url_2   = https://alaskybis.unistra.fr/2MASS/J
+hips_status_2        = public mirror clonableOnce
+moc_sky_fraction     = 1
+moc_order            = 29
+obs_initial_ra       = 0
+obs_initial_dec      = +0
+obs_initial_fov      = 1.0921117184376416E-7
+client_application   = AladinDesktop
+TIMESTAMP            = 1491570319000
+
+ID                   = CDS/P/2MASS/K
+creator_did          = ivo://CDS/P/2MASS/K
+obs_collection       = The Two Micron All Sky Survey - K band (2MASS K)
+obs_title            = 2MASS K (2.16 microns)
+obs_description      = 2MASS has uniformly scanned the entire sky in three near-infrared bands to detect and characterize point sources brighter than about 1 mJy in each band, with signal-to-noise ratio (SNR) greater than 10, using a pixel size of 2.0". This has achieved an 80,000-fold improvement in sensitivity relative to earlier surveys. 2MASS used two highly-automated 1.3-m telescopes, one at Mt. Hopkins, AZ, and one at CTIO, Chile. Each telescope was equipped with a three-channel camera, each channel consisting of a 256x256 array of HgCdTe detectors, capable of observing the sky simultaneously at J (1.25 microns), H (1.65 microns), and Ks (2.17 microns). The University of Massachusetts (UMass) was responsible for the overall management of the project, and for developing the infrared cameras and on-site computing systems at both facilities. The Infrared Processing and Analysis Center (IPAC) is responsible for all data processing through the Production Pipeline, and construction and distribution of the data products. Funding is provided primarily by NASA and the NSF
+obs_copyright_url    = http://www.ipac.caltech.edu/2mass/
+prov_progenitor      = IPAC/NASA
+client_category      = Image/Infrared/2MASS
+client_sort_key      = 04-001-04
+hips_creation_date   = 2013-01-14T09:45Z
+hips_release_date    = 2016-04-22T14:23Z
+hips_builder         = Aladin/HipsGen v9.017
+hips_creator         = CDS (A.Oberto)
+hips_version         = 1.31
+hips_order           = 9
+hips_frame           = equatorial
+hips_tile_width      = 512
+hips_tile_format     = jpeg fits
+dataproduct_type     = image
+hips_pixel_cut       = -2 35
+moc_access_url       = http://alasky.u-strasbg.fr/2MASS/K/Moc.fits
+hips_service_url     = http://alasky.unistra.fr/2MASS/K
+hips_status          = public master clonableOnce
+hips_copyright       = CNRS/Unistra
+obs_ack              = University of Massachusetts & IPAC/Caltech
+bib_reference        = 2006AJ....131.1163S
+bib_reference_url    = http://cdsbib.u-strasbg.fr/cgi-bin/cdsbib?2006AJ....131.1163S
+obs_copyright        = University of Massachusetts & IPAC/Caltech
+t_min                = 50600
+t_max                = 51941
+obs_regime           = Infrared
+em_min               = 2.015E-6
+em_max               = 2.303E-6
+hips_hierarchy       = mean
+hips_pixel_scale     = 2.236E-4
+hips_progenitor_url  = http://alasky.unistra.fr/2MASS/K/HpxFinder
+hips_service_url_1   = http://alaskybis.unistra.fr/2MASS/K
+hips_status_1        = public mirror clonableOnce
+hips_service_url_2   = https://alaskybis.unistra.fr/2MASS/K
+hips_status_2        = public mirror clonableOnce
+moc_sky_fraction     = 1
+moc_order            = 29
+obs_initial_ra       = 0
+obs_initial_dec      = +0
+obs_initial_fov      = 1.0921117184376416E-7
+client_application   = AladinDesktop
+TIMESTAMP            = 1491570319000
\ No newline at end of file
diff --git a/hips/tiles/tests/test_description.py b/hips/tiles/tests/test_description.py
index cd5c24b..b36aa02 100644
--- a/hips/tiles/tests/test_description.py
+++ b/hips/tiles/tests/test_description.py
@@ -1,28 +1,28 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 from astropy.utils.data import get_pkg_data_filename
-from ..description import HipsDescription
+from ..description import HipsSurveyProperties


-class TestHiPSDescription:
+class TestHipsSurveyProperties:
     @classmethod
     def setup_class(cls):
         filename = get_pkg_data_filename('data/properties.txt')
-        cls.hipsdescription = HipsDescription.read(filename)
+        cls.hips_survey_property = HipsSurveyProperties.read(filename)

     def test_base_url(self):
-        assert self.hipsdescription.base_url == 'http://alasky.u-strasbg.fr/DSS/DSSColor'
+        assert self.hips_survey_property.base_url == 'http://alasky.u-strasbg.fr/DSS/DSSColor'

     def test_title(self):
-        assert self.hipsdescription.title == 'DSS colored'
+        assert self.hips_survey_property.title == 'DSS colored'

     def test_hips_version(self):
-        assert self.hipsdescription.hips_version == '1.31'
+        assert self.hips_survey_property.hips_version == '1.31'

     def test_hips_frame(self):
-        assert self.hipsdescription.hips_frame == 'equatorial'
+        assert self.hips_survey_property.hips_frame == 'equatorial'

     def test_hips_order(self):
-        assert self.hipsdescription.hips_order == 9
+        assert self.hips_survey_property.hips_order == 9

     def test_tile_format(self):
-        assert self.hipsdescription.tile_format == 'jpeg'
+        assert self.hips_survey_property.tile_format == 'jpeg'
diff --git a/hips/tiles/tests/test_surveys.py b/hips/tiles/tests/test_surveys.py
new file mode 100644
index 0000000..99dd3d4
--- /dev/null
+++ b/hips/tiles/tests/test_surveys.py
@@ -0,0 +1,21 @@
+# Licensed under a 3-clause BSD style license - see LICENSE.rst
+from astropy.utils.data import get_pkg_data_filename
+from astropy.tests.helper import remote_data
+from ..surveys import HipsSurveyPropertiesList
+
+class TestHipsSurveyPropertiesList:
+    @classmethod
+    def setup_class(cls):
+        filename = get_pkg_data_filename('data/surveys.txt')
+        cls.hips_survey_list = HipsSurveyPropertiesList.read(filename)
+
+    @remote_data
+    def test_fetch(self):
+        self.hips_survey_list.fetch()
+
+    def test_surveys(self):
+        assert len(self.hips_survey_list.surveys) == 4
+        assert self.hips_survey_list.surveys[0].properties['creator_did'] == 'ivo://CDS/C/MUSE-M42'
+        assert self.hips_survey_list.surveys[0].properties['obs_collection'] == 'MUSE-M42'
+        assert self.hips_survey_list.surveys[0].properties['hips_tile_format'] == 'png fits'
+        assert self.hips_survey_list.table['surveys'][0]['hips_tile_format'] == 'png fits'

commit 0bdb052115291c0657841c6becf973cec2ff59d6
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 23 09:48:15 2017 +0500

    Fix type annotation issue

diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py
index 0764fe7..2fabf49 100644
--- a/hips/tiles/tests/test_tile.py
+++ b/hips/tiles/tests/test_tile.py
@@ -22,11 +22,11 @@ class TestHipsTile:
         assert jpg_tile.data.shape == (512, 512, 3)
         assert list(fits_tile.data[510][:12]) == data_precomp

-        jpg_tile.write('Npix30889.jpg', tmpdir)
-        fits_tile.write('Npix30889.fits', tmpdir)
+        jpg_tile.write('Npix30889.jpg', str(tmpdir / 'Npix30889.jpg'))
+        fits_tile.write('Npix30889.fits', str(tmpdir / 'Npix30889.fits'))

-        jpg_tile = HipsTile.read(self.hips_tile_meta_jpg, tmpdir)
-        fits_tile = HipsTile.read(self.hips_tile_meta_fits, tmpdir)
+        jpg_tile = HipsTile.read(self.hips_tile_meta_jpg, str(tmpdir / 'Npix30889.jpg'))
+        fits_tile = HipsTile.read(self.hips_tile_meta_fits, str(tmpdir / 'Npix30889.fits'))

         data_precomp = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]
         assert fits_tile.data.shape == (512, 512)
diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py
index c521df6..a6b846b 100644
--- a/hips/tiles/tile.py
+++ b/hips/tiles/tile.py
@@ -8,7 +8,7 @@ from astropy.io import fits
 from astropy.io.fits.header import Header

 from .tile_meta import HipsTileMeta
-import py._path.local
+
 __all__ = [
     'HipsTile',
 ]
@@ -73,18 +73,20 @@ class HipsTile:
             return cls(meta, data)

     @classmethod
-    def read(cls, meta: HipsTileMeta, tmpdir: py._path.local = None) -> 'HipsTile':
+    def read(cls, meta: HipsTileMeta, file_path: str = None) -> 'HipsTile':
         """Read HiPS tile data from a directory and load into memory (`HipsTile`).

         Parameters
         ----------
         meta : `HipsTileMeta`
             Metadata of HiPS tile
+        file_path : `str`
+            File path to store a HiPS tile
         """
-        if tmpdir == None:
-            path = meta.path / meta.filename
+        if file_path is None:
+            path = meta.path / meta.filename  # pragma: no cover
         else:
-            path = tmpdir / meta.filename
+            path = file_path

         if meta.file_format == 'fits':
             hdulist = fits.open(str(path))
@@ -95,18 +97,20 @@ class HipsTile:
             data = np.array(Image.open(str(path)))
             return cls(meta, data)

-    def write(self, filename: str, tmpdir: py._path.local = None) -> None:
+    def write(self, filename: str, file_path: str = None) -> None:
         """Write HiPS tile by a given filename.

         Parameters
         ----------
         filename : `str`
             Name of the file
+        file_path : `str`
+            File path to store a HiPS tile
         """
-        if tmpdir == None:
-            path = self.meta.path / self.meta.filename
+        if file_path is None:
+            path = self.meta.path / self.meta.filename  # pragma: no cover
         else:
-            path = tmpdir / self.meta.filename
+            path = file_path

         if self.meta.file_format == 'fits':
             hdu = fits.PrimaryHDU(self.data, header=self.header).writeto(str(path))
diff --git a/hips/tiles/tile_meta.py b/hips/tiles/tile_meta.py
index 3789609..0461b7d 100644
--- a/hips/tiles/tile_meta.py
+++ b/hips/tiles/tile_meta.py
@@ -30,11 +30,11 @@ class HipsTileMeta:
         self.tile_width = tile_width

     @property
-    def path(self) -> str:
-        """Return the default path for tile storage (`str`)."""
+    def path(self) -> Path:  # pragma: no cover
+        """Return the default path for tile storage (`Path`)."""
         return Path('hips', 'tiles', 'tests', 'data')

     @property
-    def filename(self) -> str:
+    def filename(self) -> str:  # pragma: no cover
         """Return the filename of HiPS tile (`str`)."""
         return ''.join(['Npix', str(self.ipix), '.', self.file_format])

commit 1136490773612a0601b06b5c8a644d728f0f659b
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Thu Jun 22 18:52:50 2017 +0500

    Update tiles.hips module

diff --git a/hips/tiles/__init__.py b/hips/tiles/__init__.py
index f6101be..2adbd76 100644
--- a/hips/tiles/__init__.py
+++ b/hips/tiles/__init__.py
@@ -1,4 +1,5 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 """Classes and functions to manage HiPS tiles."""
 from .tile import *
+from .tile_meta import *
 from .description import *
diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py
index f27f1b9..0764fe7 100644
--- a/hips/tiles/tests/test_tile.py
+++ b/hips/tiles/tests/test_tile.py
@@ -7,11 +7,11 @@ from ..tile_meta import HipsTileMeta

 class TestHipsTile:
     def setup(self):
-        self.hips_tile_meta_fits = HipsTileMeta(order=6, ipix=30889, format='fits')
-        self.hips_tile_meta_jpg = HipsTileMeta(order=6, ipix=30889, format='jpg')
+        self.hips_tile_meta_fits = HipsTileMeta(order=6, ipix=30889, file_format='fits')
+        self.hips_tile_meta_jpg = HipsTileMeta(order=6, ipix=30889, file_format='jpg')

     @remote_data
-    def test_fetch_read_write(self):
+    def test_fetch_read_write(self, tmpdir):
         fits_tile = HipsTile.fetch(self.hips_tile_meta_fits,
                                    'http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.fits')
         jpg_tile = HipsTile.fetch(self.hips_tile_meta_jpg,
@@ -22,11 +22,11 @@ class TestHipsTile:
         assert jpg_tile.data.shape == (512, 512, 3)
         assert list(fits_tile.data[510][:12]) == data_precomp

-        jpg_tile.write('Npix30889.jpg')
-        fits_tile.write('Npix30889.fits')
+        jpg_tile.write('Npix30889.jpg', tmpdir)
+        fits_tile.write('Npix30889.fits', tmpdir)

-        fits_tile = HipsTile.read(self.hips_tile_meta_fits)
-        jpg_tile = HipsTile.read(self.hips_tile_meta_jpg)
+        jpg_tile = HipsTile.read(self.hips_tile_meta_jpg, tmpdir)
+        fits_tile = HipsTile.read(self.hips_tile_meta_fits, tmpdir)

         data_precomp = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]
         assert fits_tile.data.shape == (512, 512)
diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py
index bf8ae51..c521df6 100644
--- a/hips/tiles/tile.py
+++ b/hips/tiles/tile.py
@@ -8,7 +8,7 @@ from astropy.io import fits
 from astropy.io.fits.header import Header

 from .tile_meta import HipsTileMeta
-
+import py._path.local
 __all__ = [
     'HipsTile',
 ]
@@ -48,7 +48,6 @@ class HipsTile:

     def __init__(self, meta: HipsTileMeta, data: np.ndarray = None, header: Header = None) -> None:
         self.meta = meta
-        self.format = format
         self.data = data
         self.header = header

@@ -64,7 +63,7 @@ class HipsTile:
             URL containing HiPS tile
         """
         raw_image = BytesIO(urllib.request.urlopen(url).read())
-        if meta.format == 'fits':
+        if meta.file_format == 'fits':
             hdulist = fits.open(raw_image)
             data = np.array(hdulist[0].data)
             header = hdulist[0].header
@@ -74,7 +73,7 @@ class HipsTile:
             return cls(meta, data)

     @classmethod
-    def read(cls, meta: HipsTileMeta) -> 'HipsTile':
+    def read(cls, meta: HipsTileMeta, tmpdir: py._path.local = None) -> 'HipsTile':
         """Read HiPS tile data from a directory and load into memory (`HipsTile`).

         Parameters
@@ -82,9 +81,13 @@ class HipsTile:
         meta : `HipsTileMeta`
             Metadata of HiPS tile
         """
-        path = meta.path / meta.filename
-        if meta.format == 'fits':
-            hdulist = fits.open(path)
+        if tmpdir == None:
+            path = meta.path / meta.filename
+        else:
+            path = tmpdir / meta.filename
+
+        if meta.file_format == 'fits':
+            hdulist = fits.open(str(path))
             data = np.array(hdulist[0].data)
             header = hdulist[0].header
             return cls(meta, data, header)
@@ -92,7 +95,7 @@ class HipsTile:
             data = np.array(Image.open(str(path)))
             return cls(meta, data)

-    def write(self, filename: str) -> None:
+    def write(self, filename: str, tmpdir: py._path.local = None) -> None:
         """Write HiPS tile by a given filename.

         Parameters
@@ -100,8 +103,12 @@ class HipsTile:
         filename : `str`
             Name of the file
         """
-        path = self.meta.path / self.meta.filename
-        if self.meta.format == 'fits':
+        if tmpdir == None:
+            path = self.meta.path / self.meta.filename
+        else:
+            path = tmpdir / self.meta.filename
+
+        if self.meta.file_format == 'fits':
             hdu = fits.PrimaryHDU(self.data, header=self.header).writeto(str(path))
         else:
             Image.fromarray(self.data).save(str(path))
diff --git a/hips/tiles/tile_meta.py b/hips/tiles/tile_meta.py
index 50ed011..3789609 100644
--- a/hips/tiles/tile_meta.py
+++ b/hips/tiles/tile_meta.py
@@ -23,10 +23,10 @@ class HipsTileMeta:
         HiPS tile width
     """

-    def __init__(self, order: int, ipix: int, format: str, tile_width: int = 512) -> None:
-        self.order = format
+    def __init__(self, order: int, ipix: int, file_format: str, tile_width: int = 512) -> None:
+        self.order = order
         self.ipix = ipix
-        self.format = format
+        self.file_format = file_format
         self.tile_width = tile_width

     @property
@@ -37,4 +37,4 @@ class HipsTileMeta:
     @property
     def filename(self) -> str:
         """Return the filename of HiPS tile (`str`)."""
-        return ''.join(['Npix', str(self.ipix), '.', self.format])
+        return ''.join(['Npix', str(self.ipix), '.', self.file_format])

commit 5db3ef2ae3c3958ae86906e840c4bc4793845adf
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Thu Jun 22 09:28:31 2017 +0500

    Update tiles.hips module

diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py
index a3a3397..f27f1b9 100644
--- a/hips/tiles/tests/test_tile.py
+++ b/hips/tiles/tests/test_tile.py
@@ -25,8 +25,8 @@ class TestHipsTile:
         jpg_tile.write('Npix30889.jpg')
         fits_tile.write('Npix30889.fits')

-        fits_tile = HipsTile.read(self.hips_tile_meta_fits, 'Npix30889.fits')
-        jpg_tile = HipsTile.read(self.hips_tile_meta_jpg, 'Npix30889.jpg')
+        fits_tile = HipsTile.read(self.hips_tile_meta_fits)
+        jpg_tile = HipsTile.read(self.hips_tile_meta_jpg)

         data_precomp = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]
         assert fits_tile.data.shape == (512, 512)
diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py
index b822d69..bf8ae51 100644
--- a/hips/tiles/tile.py
+++ b/hips/tiles/tile.py
@@ -8,7 +8,6 @@ from astropy.io import fits
 from astropy.io.fits.header import Header

 from .tile_meta import HipsTileMeta
-from ..utils.tile import tile_path

 __all__ = [
     'HipsTile',
@@ -28,15 +27,15 @@ class HipsTile:
         Metadata of HiPS tile
     data : `~numpy.ndarray`
         Data containing HiPS tile
-    header : `astropy.io.fits.Header`
+    header : `~astropy.io.fits.Header`
         Header of HiPS tile

     Examples
     --------
         >>> from hips.tiles import HipsTile
         >>> from hips.tiles import HipsTileMeta
-        >>> hips_tile_met = HipsTileMeta(order=6, ipix=30889, format='fits')
-        >>> hips_tile = HipsTile.read(meta, 'Npix30889.fits')
+        >>> meta = HipsTileMeta(order=6, ipix=30889, format='fits')
+        >>> hips_tile = HipsTile.read(meta)
         >>> hips_tile.data
         array([[0, 0, 0, ..., 0, 0, 0],
                [0, 0, 0, ..., 0, 0, 0],
@@ -75,17 +74,15 @@ class HipsTile:
             return cls(meta, data)

     @classmethod
-    def read(cls, meta: HipsTileMeta, filename: str) -> 'HipsTile':
+    def read(cls, meta: HipsTileMeta) -> 'HipsTile':
         """Read HiPS tile data from a directory and load into memory (`HipsTile`).

         Parameters
         ----------
         meta : `HipsTileMeta`
             Metadata of HiPS tile
-        filename : `str`
-            File name of HiPS tile
         """
-        path = tile_path().absolute() / filename
+        path = meta.path / meta.filename
         if meta.format == 'fits':
             hdulist = fits.open(path)
             data = np.array(hdulist[0].data)
@@ -103,7 +100,7 @@ class HipsTile:
         filename : `str`
             Name of the file
         """
-        path = tile_path().absolute() / filename
+        path = self.meta.path / self.meta.filename
         if self.meta.format == 'fits':
             hdu = fits.PrimaryHDU(self.data, header=self.header).writeto(str(path))
         else:
diff --git a/hips/tiles/tile_meta.py b/hips/tiles/tile_meta.py
index 8ac403b..50ed011 100644
--- a/hips/tiles/tile_meta.py
+++ b/hips/tiles/tile_meta.py
@@ -1,5 +1,7 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst

+from pathlib import Path
+
 __all__ = [
     'HipsTileMeta',
 ]
@@ -26,3 +28,13 @@ class HipsTileMeta:
         self.ipix = ipix
         self.format = format
         self.tile_width = tile_width
+
+    @property
+    def path(self) -> str:
+        """Return the default path for tile storage (`str`)."""
+        return Path('hips', 'tiles', 'tests', 'data')
+
+    @property
+    def filename(self) -> str:
+        """Return the filename of HiPS tile (`str`)."""
+        return ''.join(['Npix', str(self.ipix), '.', self.format])
diff --git a/hips/utils/tile.py b/hips/utils/tile.py
deleted file mode 100644
index c3937ea..0000000
--- a/hips/utils/tile.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Licensed under a 3-clause BSD style license - see LICENSE.rst
-"""HiPS tile utility functions.
-
-"""
-
-from pathlib import Path
-
-__all__ = [
-    'tile_path',
-]
-
-
-def tile_path() -> Path:
-    """Return default path of HiPS tile storage."""
-    path = Path('hips', 'tiles', 'tests', 'data')
-    return path

commit 9d2ef083c0dc9b73fb00e4228daf3179ca482348
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Wed Jun 21 23:20:30 2017 +0500

    Refactor some code

diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py
index efd0bb9..b822d69 100644
--- a/hips/tiles/tile.py
+++ b/hips/tiles/tile.py
@@ -24,23 +24,19 @@ class HipsTile:

     Parameters
     ----------
-    hips_tile_meta : `HipsTileMeta`
+    meta : `HipsTileMeta`
         Metadata of HiPS tile
-    format : `str`
-        Format of HiPS tile
-    data : `np.ndarray`
+    data : `~numpy.ndarray`
         Data containing HiPS tile
     header : `astropy.io.fits.Header`
         Header of HiPS tile

     Examples
     --------
-        >>> import urllib.request
         >>> from hips.tiles import HipsTile
         >>> from hips.tiles import HipsTileMeta
-        >>> from astropy.tests.helper import remote_data
-        >>> hips_tile_metadata = HipsTileMeta(order=6, ipix=30889, format='fits')
-        >>> hips_tile = HipsTile.read(hips_tile_metadata, 'Npix30889.fits')
+        >>> hips_tile_met = HipsTileMeta(order=6, ipix=30889, format='fits')
+        >>> hips_tile = HipsTile.read(meta, 'Npix30889.fits')
         >>> hips_tile.data
         array([[0, 0, 0, ..., 0, 0, 0],
                [0, 0, 0, ..., 0, 0, 0],
@@ -51,56 +47,56 @@ class HipsTile:
                [0, 0, 0, ..., 1, 0, 1]], dtype=int16)
         """

-    def __init__(self, hips_tile_meta: HipsTileMeta, data: np.ndarray = None, header: Header = None) -> None:
-        self.hips_tile_meta = hips_tile_meta
+    def __init__(self, meta: HipsTileMeta, data: np.ndarray = None, header: Header = None) -> None:
+        self.meta = meta
         self.format = format
         self.data = data
         self.header = header

     @classmethod
-    def fetch(cls, hips_tile_meta: HipsTileMeta, url: str) -> 'HipsTile':
+    def fetch(cls, meta: HipsTileMeta, url: str) -> 'HipsTile':
         """Fetch HiPS tile and load into memory (`HipsTile`).

         Parameters
         ----------
-        hips_tile_meta : `HipsTileMeta`
+        meta : `HipsTileMeta`
             Metadata of HiPS tile
         url : `str`
             URL containing HiPS tile
         """
         raw_image = BytesIO(urllib.request.urlopen(url).read())
-        if hips_tile_meta.format == 'fits':
+        if meta.format == 'fits':
             hdulist = fits.open(raw_image)
             data = np.array(hdulist[0].data)
             header = hdulist[0].header
-            return cls(hips_tile_meta, data, header)
+            return cls(meta, data, header)
         else:
             data = np.array(Image.open(raw_image))
-            return cls(hips_tile_meta, data)
+            return cls(meta, data)

     @classmethod
-    def read(cls, hips_tile_meta: HipsTileMeta, filename: str) -> 'HipsTile':
+    def read(cls, meta: HipsTileMeta, filename: str) -> 'HipsTile':
         """Read HiPS tile data from a directory and load into memory (`HipsTile`).

         Parameters
         ----------
-        hips_tile_meta : `HipsTileMeta`
+        meta : `HipsTileMeta`
             Metadata of HiPS tile
         filename : `str`
             File name of HiPS tile
         """
         path = tile_path().absolute() / filename
-        if hips_tile_meta.format == 'fits':
+        if meta.format == 'fits':
             hdulist = fits.open(path)
             data = np.array(hdulist[0].data)
             header = hdulist[0].header
-            return cls(hips_tile_meta, data, header)
+            return cls(meta, data, header)
         else:
             data = np.array(Image.open(str(path)))
-            return cls(hips_tile_meta, data)
+            return cls(meta, data)

     def write(self, filename: str) -> None:
-        """Write HiPS tile by a given filename (`None`).
+        """Write HiPS tile by a given filename.

         Parameters
         ----------
@@ -108,10 +104,7 @@ class HipsTile:
             Name of the file
         """
         path = tile_path().absolute() / filename
-        if self.hips_tile_meta.format == 'fits':
-            hdu = fits.PrimaryHDU(self.data, header=self.header)
-            hdulist = fits.HDUList([hdu])
-            hdulist.writeto(str(path))
-            hdulist.close()
+        if self.meta.format == 'fits':
+            hdu = fits.PrimaryHDU(self.data, header=self.header).writeto(str(path))
         else:
             Image.fromarray(self.data).save(str(path))
diff --git a/hips/tiles/tile_meta.py b/hips/tiles/tile_meta.py
index 9ee8445..8ac403b 100644
--- a/hips/tiles/tile_meta.py
+++ b/hips/tiles/tile_meta.py
@@ -7,7 +7,6 @@ __all__ = [

 class HipsTileMeta:
     """HiPS tile metadata container.
-
     This class stores HiPS tile meta attributes.

     Parameters
@@ -16,8 +15,8 @@ class HipsTileMeta:
         Order of HiPS tile
     ipix : `int`
         Pixel number of HiPS tile
-    format : `str`
-        Format of the HiPS tile
+    format : {'fits', 'jpg', 'png'}
+        File format of the HiPS tile
     tile_width : `int`
         HiPS tile width
     """
diff --git a/hips/utils/tile.py b/hips/utils/tile.py
index f08d640..c3937ea 100644
--- a/hips/utils/tile.py
+++ b/hips/utils/tile.py
@@ -1,8 +1,6 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
-"""HEALpy wrapper functions.
+"""HiPS tile utility functions.

-This module contains wrapper functions around HEALPix utilizing
-the healpy library.
 """

 from pathlib import Path

commit 616340bd6c87cb070d324b1631ced8cbe76654b8
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Wed Jun 21 22:33:36 2017 +0500

    Add class HipsTileMeta and update test case

diff --git a/hips/tiles/tests/data/Npix30889.fits b/hips/tiles/tests/data/Npix30889.fits
deleted file mode 100644
index 522852f..0000000
Binary files a/hips/tiles/tests/data/Npix30889.fits and /dev/null differ
diff --git a/hips/tiles/tests/data/Npix30889.jpg b/hips/tiles/tests/data/Npix30889.jpg
deleted file mode 100644
index b6365db..0000000
Binary files a/hips/tiles/tests/data/Npix30889.jpg and /dev/null differ
diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py
index d82e8a0..a3a3397 100644
--- a/hips/tiles/tests/test_tile.py
+++ b/hips/tiles/tests/test_tile.py
@@ -1,34 +1,34 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 from astropy.tests.helper import remote_data
+
 from ..tile import HipsTile
+from ..tile_meta import HipsTileMeta
+
+
+class TestHipsTile:
+    def setup(self):
+        self.hips_tile_meta_fits = HipsTileMeta(order=6, ipix=30889, format='fits')
+        self.hips_tile_meta_jpg = HipsTileMeta(order=6, ipix=30889, format='jpg')
+
+    @remote_data
+    def test_fetch_read_write(self):
+        fits_tile = HipsTile.fetch(self.hips_tile_meta_fits,
+                                   'http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.fits')
+        jpg_tile = HipsTile.fetch(self.hips_tile_meta_jpg,
+                                  'http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.jpg')
+
+        data_precomp = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]
+        assert fits_tile.data.shape == (512, 512)
+        assert jpg_tile.data.shape == (512, 512, 3)
+        assert list(fits_tile.data[510][:12]) == data_precomp
+
+        jpg_tile.write('Npix30889.jpg')
+        fits_tile.write('Npix30889.fits')
+
+        fits_tile = HipsTile.read(self.hips_tile_meta_fits, 'Npix30889.fits')
+        jpg_tile = HipsTile.read(self.hips_tile_meta_jpg, 'Npix30889.jpg')

-@remote_data
-def test_fetch():
-    fits_tile = HipsTile.fetch('fits', 'http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.fits')
-    jpg_tile = HipsTile.fetch('jpg', 'http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.jpg')
-
-    shape_fits_precomp = (512, 512)
-    shape_jpg_precomp = (512, 512, 3)
-    data_precomp = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]
-    assert fits_tile.data.shape == shape_fits_precomp
-    assert jpg_tile.data.shape == shape_jpg_precomp
-    assert list(fits_tile.data[510][:12]) == data_precomp
-
-def test_read_write():
-    fits_tile = HipsTile.read('fits', 'Npix30889.fits')
-    jpg_tile = HipsTile.read('jpg', 'Npix30889.jpg')
-
-    shape_fits_precomp = (512, 512)
-    shape_jpg_precomp = (512, 512, 3)
-    data_precomp = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]
-    assert (fits_tile.data.shape) == shape_fits_precomp
-    assert (jpg_tile.data.shape) == shape_jpg_precomp
-    assert list(fits_tile.data[510][:12]) == data_precomp
-
-    fits_tile.write('test_file.fits')
-    jpg_tile.write('test_file.jpg')
-
-    path_fits = fits_tile.path / 'test_file.fits'
-    path_jpg = jpg_tile.path / 'test_file.jpg'
-    assert True == path_fits.is_file()
-    assert True == path_jpg.is_file()
+        data_precomp = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]
+        assert fits_tile.data.shape == (512, 512)
+        assert jpg_tile.data.shape == (512, 512, 3)
+        assert list(fits_tile.data[510][:12]) == data_precomp
diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py
index 509667e..efd0bb9 100644
--- a/hips/tiles/tile.py
+++ b/hips/tiles/tile.py
@@ -1,16 +1,21 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
-from astropy.io.fits.header import Header
-from astropy.io import fits
-from pathlib import Path
-from io import BytesIO
-from PIL import Image
 import urllib.request
+from io import BytesIO
+
 import numpy as np
+from PIL import Image
+from astropy.io import fits
+from astropy.io.fits.header import Header
+
+from .tile_meta import HipsTileMeta
+from ..utils.tile import tile_path

 __all__ = [
     'HipsTile',
 ]

+__doctest_skip__ = ['HipsTile']
+

 class HipsTile:
     """HiPS tile container.
@@ -19,21 +24,23 @@ class HipsTile:

     Parameters
     ----------
+    hips_tile_meta : `HipsTileMeta`
+        Metadata of HiPS tile
     format : `str`
         Format of HiPS tile
-    data : `int`
+    data : `np.ndarray`
         Data containing HiPS tile
-    header : `format`
+    header : `astropy.io.fits.Header`
         Header of HiPS tile

     Examples
     --------
-    ::
-
         >>> import urllib.request
+        >>> from hips.tiles import HipsTile
+        >>> from hips.tiles import HipsTileMeta
         >>> from astropy.tests.helper import remote_data
-        >>> text = urllib.request.urlopen('https://raw.githubusercontent.com/hipspy/hips/master/hips/tiles/tests/data/properties.txt').read() # doctest: +REMOTE_DATA
-        >>> hips_tile = HipsTile.read('fits', 'Npix30889.fits')
+        >>> hips_tile_metadata = HipsTileMeta(order=6, ipix=30889, format='fits')
+        >>> hips_tile = HipsTile.read(hips_tile_metadata, 'Npix30889.fits')
         >>> hips_tile.data
         array([[0, 0, 0, ..., 0, 0, 0],
                [0, 0, 0, ..., 0, 0, 0],
@@ -44,57 +51,53 @@ class HipsTile:
                [0, 0, 0, ..., 1, 0, 1]], dtype=int16)
         """

-    def __init__(self, format: str, data: np.ndarray=None, header: Header=None) -> None:
+    def __init__(self, hips_tile_meta: HipsTileMeta, data: np.ndarray = None, header: Header = None) -> None:
+        self.hips_tile_meta = hips_tile_meta
         self.format = format
         self.data = data
         self.header = header

-    @property
-    def path(self) -> Path:
-        """Default path for tile storage (`Path`)."""
-        return Path('hips', 'tiles', 'tests', 'data')
-
     @classmethod
-    def fetch(cls, format: str, url: str) -> HipsTile:
+    def fetch(cls, hips_tile_meta: HipsTileMeta, url: str) -> 'HipsTile':
         """Fetch HiPS tile and load into memory (`HipsTile`).

         Parameters
         ----------
-        format : `str`
-            Format of HiPS tile
+        hips_tile_meta : `HipsTileMeta`
+            Metadata of HiPS tile
         url : `str`
             URL containing HiPS tile
         """
         raw_image = BytesIO(urllib.request.urlopen(url).read())
-        if format == 'fits':
+        if hips_tile_meta.format == 'fits':
             hdulist = fits.open(raw_image)
             data = np.array(hdulist[0].data)
             header = hdulist[0].header
-            return cls(format, data, header)
+            return cls(hips_tile_meta, data, header)
         else:
             data = np.array(Image.open(raw_image))
-            return cls(format, data)
+            return cls(hips_tile_meta, data)

     @classmethod
-    def read(cls, format: str, filename: str) -> 'HipsTile':
+    def read(cls, hips_tile_meta: HipsTileMeta, filename: str) -> 'HipsTile':
         """Read HiPS tile data from a directory and load into memory (`HipsTile`).

         Parameters
         ----------
-        format : `str`
-            Format of HiPS tile
+        hips_tile_meta : `HipsTileMeta`
+            Metadata of HiPS tile
         filename : `str`
             File name of HiPS tile
         """
-        path = Path('hips', 'tiles', 'tests', 'data') / filename
-        if format == 'fits':
+        path = tile_path().absolute() / filename
+        if hips_tile_meta.format == 'fits':
             hdulist = fits.open(path)
             data = np.array(hdulist[0].data)
             header = hdulist[0].header
-            return cls(format, data, header)
+            return cls(hips_tile_meta, data, header)
         else:
             data = np.array(Image.open(str(path)))
-            return cls(format, data)
+            return cls(hips_tile_meta, data)

     def write(self, filename: str) -> None:
         """Write HiPS tile by a given filename (`None`).
@@ -104,11 +107,11 @@ class HipsTile:
         filename : `str`
             Name of the file
         """
-        path = self.path / filename
-        if self.format == 'fits':
+        path = tile_path().absolute() / filename
+        if self.hips_tile_meta.format == 'fits':
             hdu = fits.PrimaryHDU(self.data, header=self.header)
             hdulist = fits.HDUList([hdu])
-            hdulist.writeto(path)
+            hdulist.writeto(str(path))
             hdulist.close()
         else:
             Image.fromarray(self.data).save(str(path))
diff --git a/hips/tiles/tile_meta.py b/hips/tiles/tile_meta.py
new file mode 100644
index 0000000..9ee8445
--- /dev/null
+++ b/hips/tiles/tile_meta.py
@@ -0,0 +1,29 @@
+# Licensed under a 3-clause BSD style license - see LICENSE.rst
+
+__all__ = [
+    'HipsTileMeta',
+]
+
+
+class HipsTileMeta:
+    """HiPS tile metadata container.
+
+    This class stores HiPS tile meta attributes.
+
+    Parameters
+    ----------
+    order : `int`
+        Order of HiPS tile
+    ipix : `int`
+        Pixel number of HiPS tile
+    format : `str`
+        Format of the HiPS tile
+    tile_width : `int`
+        HiPS tile width
+    """
+
+    def __init__(self, order: int, ipix: int, format: str, tile_width: int = 512) -> None:
+        self.order = format
+        self.ipix = ipix
+        self.format = format
+        self.tile_width = tile_width
diff --git a/hips/utils/tile.py b/hips/utils/tile.py
new file mode 100644
index 0000000..f08d640
--- /dev/null
+++ b/hips/utils/tile.py
@@ -0,0 +1,18 @@
+# Licensed under a 3-clause BSD style license - see LICENSE.rst
+"""HEALpy wrapper functions.
+
+This module contains wrapper functions around HEALPix utilizing
+the healpy library.
+"""
+
+from pathlib import Path
+
+__all__ = [
+    'tile_path',
+]
+
+
+def tile_path() -> Path:
+    """Return default path of HiPS tile storage."""
+    path = Path('hips', 'tiles', 'tests', 'data')
+    return path

commit 6250e9ec0c665151a84403d986a4b71a804ea632
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Wed Jun 21 16:15:41 2017 +0500

    Minimize HipsTile classs functionality

diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py
index 1316b5d..d82e8a0 100644
--- a/hips/tiles/tests/test_tile.py
+++ b/hips/tiles/tests/test_tile.py
@@ -1,61 +1,34 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
-from astropy.utils.data import get_pkg_data_filename
 from astropy.tests.helper import remote_data
-from ..description import HipsDescription
 from ..tile import HipsTile
-from pathlib import Path
-import numpy as np

-class TestHipsTile:
-    @classmethod
-    def setup_class(cls):
-        filename = get_pkg_data_filename('data/properties.txt')
-        hips_description = HipsDescription.read(filename)
-
-        cls.fits_tile = HipsTile(hips_description=hips_description, order=6,
-                            ipix=30889, format='fits', tile_width=512)
-        cls.jpg_tile = HipsTile(hips_description=hips_description, order=6,
-                            ipix=30889, format='jpg', tile_width=512)
-        cls.fits_tile.base_url = 'http://alasky.unistra.fr/2MASS/H'
-        cls.jpg_tile.base_url = 'http://alasky.unistra.fr/2MASS/H'
-
-    def test_base_url(self):
-        assert self.fits_tile.base_url == 'http://alasky.unistra.fr/2MASS/H'
-        assert self.jpg_tile.base_url == 'http://alasky.unistra.fr/2MASS/H'
-
-    def test_tile_url(self):
-        assert self.fits_tile.tile_url == 'http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.fits'
-        assert self.jpg_tile.tile_url == 'http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.jpg'
-
-    @remote_data
-    def test_fetch(self):
-        self.fits_tile.fetch()
-        self.jpg_tile.fetch()
-
-        shape_fits_precomp = (512, 512)
-        shape_jpg_precomp = (512, 512, 3)
-        data_precomp = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]
-        assert self.fits_tile.data.shape == shape_fits_precomp
-        assert self.jpg_tile.data.shape == shape_jpg_precomp
-        assert list(self.fits_tile.data[510][:12]) == data_precomp
-
-    def test_read(self):
-        self.fits_tile.read()
-        self.jpg_tile.read()
-
-        shape_fits_precomp = (512, 512)
-        shape_jpg_precomp = (512, 512, 3)
-        data_precomp = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]
-        assert (self.fits_tile.data.shape) == shape_fits_precomp
-        assert (self.jpg_tile.data.shape) == shape_jpg_precomp
-        assert list(self.fits_tile.data[510][:12]) == data_precomp
-
-    def test_write(self):
-        filename = 'test_file'
-        self.fits_tile.write(filename)
-        self.jpg_tile.write(filename)
-
-        path_fits = self.fits_tile.path.joinpath(''.join([filename, '.', self.fits_tile.format]))
-        path_jpg = self.jpg_tile.path.joinpath(''.join([filename, '.', self.jpg_tile.format]))
-        assert True == path_fits.is_file()
-        assert True == path_jpg.is_file()
+@remote_data
+def test_fetch():
+    fits_tile = HipsTile.fetch('fits', 'http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.fits')
+    jpg_tile = HipsTile.fetch('jpg', 'http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.jpg')
+
+    shape_fits_precomp = (512, 512)
+    shape_jpg_precomp = (512, 512, 3)
+    data_precomp = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]
+    assert fits_tile.data.shape == shape_fits_precomp
+    assert jpg_tile.data.shape == shape_jpg_precomp
+    assert list(fits_tile.data[510][:12]) == data_precomp
+
+def test_read_write():
+    fits_tile = HipsTile.read('fits', 'Npix30889.fits')
+    jpg_tile = HipsTile.read('jpg', 'Npix30889.jpg')
+
+    shape_fits_precomp = (512, 512)
+    shape_jpg_precomp = (512, 512, 3)
+    data_precomp = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]
+    assert (fits_tile.data.shape) == shape_fits_precomp
+    assert (jpg_tile.data.shape) == shape_jpg_precomp
+    assert list(fits_tile.data[510][:12]) == data_precomp
+
+    fits_tile.write('test_file.fits')
+    jpg_tile.write('test_file.jpg')
+
+    path_fits = fits_tile.path / 'test_file.fits'
+    path_jpg = jpg_tile.path / 'test_file.jpg'
+    assert True == path_fits.is_file()
+    assert True == path_jpg.is_file()
diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py
index 8b033e1..509667e 100644
--- a/hips/tiles/tile.py
+++ b/hips/tiles/tile.py
@@ -1,5 +1,5 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
-from .description import HipsDescription
+from astropy.io.fits.header import Header
 from astropy.io import fits
 from pathlib import Path
 from io import BytesIO
@@ -15,39 +15,25 @@ __all__ = [
 class HipsTile:
     """HiPS tile container.

-    This class provides methods for fetching, reading,
-    and writing a HiPS tile. It also contains a few
-    getters and setters around frequently used
-    HiPS tile attributes.
+    This class provides methods for fetching, reading, and writing a HiPS tile.

     Parameters
     ----------
-    hips_description : `HipsDescription`
-        Class HipsDescription contains HiPS properties
-    order : `int`
-        Order of the HiPS tile
-    ipix : `int`
-        HEALPix pixel number
-    format : `format`
+    format : `str`
         Format of HiPS tile
-    data : `numpy.ndarray`
-        Pixel values of HiPS tile
-    tile_width : `int`
-        Width of HiPS tile
-    base_url : `str`
-        Base URL of HiPS tile
+    data : `int`
+        Data containing HiPS tile
+    header : `format`
+        Header of HiPS tile

     Examples
     --------
     ::

         >>> import urllib.request
-        >>> from hips.tiles import HipsDescription
         >>> from astropy.tests.helper import remote_data
         >>> text = urllib.request.urlopen('https://raw.githubusercontent.com/hipspy/hips/master/hips/tiles/tests/data/properties.txt').read() # doctest: +REMOTE_DATA
-        >>> hips_description = HipsDescription.parse(str(text))
-        >>> hips_tile = HipsTile(hips_description=hips_description, order=6, ipix=30889, format='fits', tile_width=512)
-        >>> hips_tile.read()
+        >>> hips_tile = HipsTile.read('fits', 'Npix30889.fits')
         >>> hips_tile.data
         array([[0, 0, 0, ..., 0, 0, 0],
                [0, 0, 0, ..., 0, 0, 0],
@@ -58,51 +44,57 @@ class HipsTile:
                [0, 0, 0, ..., 1, 0, 1]], dtype=int16)
         """

-    def __init__(self, hips_description: HipsDescription, order: int, ipix: int, format: str,
-                 data: np.ndarray=None, tile_width: int=512, base_url: str=None) -> None:
-        self.hips_description = hips_description
-        self.order = order
-        self.ipix = ipix
-        self.tile_width = tile_width
+    def __init__(self, format: str, data: np.ndarray=None, header: Header=None) -> None:
         self.format = format
         self.data = data
-        self.base_url = base_url
-
-    @staticmethod
-    def _directory(ipix: int) -> int:
-        """Directory of the HiPS tile (`int`)."""
-        return np.around(ipix, decimals=-(len(str(ipix)) - 1))
+        self.header = header

     @property
     def path(self) -> Path:
         """Default path for tile storage (`Path`)."""
         return Path('hips', 'tiles', 'tests', 'data')

-    @property
-    def tile_url(self) -> str:
-        """HiPS tile url (`str`)."""
-        return ''.join([self.base_url, '/Norder', str(self.order), '/Dir',
-               str(self._directory(self.ipix)), '/Npix', str(self.ipix), '.', self.format])
+    @classmethod
+    def fetch(cls, format: str, url: str) -> HipsTile:
+        """Fetch HiPS tile and load into memory (`HipsTile`).

-    def fetch(self) -> None:
-        """Fetch HiPS tile and load into memory (`None`)."""
-        raw_image = BytesIO(urllib.request.urlopen(self.tile_url).read())
-        if self.format == 'fits':
+        Parameters
+        ----------
+        format : `str`
+            Format of HiPS tile
+        url : `str`
+            URL containing HiPS tile
+        """
+        raw_image = BytesIO(urllib.request.urlopen(url).read())
+        if format == 'fits':
             hdulist = fits.open(raw_image)
-            self.data = np.array(hdulist[0].data)
-            self.header = hdulist[0].header
+            data = np.array(hdulist[0].data)
+            header = hdulist[0].header
+            return cls(format, data, header)
         else:
-            self.data = np.array(Image.open(raw_image))
+            data = np.array(Image.open(raw_image))
+            return cls(format, data)

-    def read(self) -> None:
-        """Read HiPS tile data from a directory and load into memory (`None`)."""
-        path = self.path / (''.join(['Npix', str(self.ipix), '.', self.format]))
-        if self.format == 'fits':
+    @classmethod
+    def read(cls, format: str, filename: str) -> 'HipsTile':
+        """Read HiPS tile data from a directory and load into memory (`HipsTile`).
+
+        Parameters
+        ----------
+        format : `str`
+            Format of HiPS tile
+        filename : `str`
+            File name of HiPS tile
+        """
+        path = Path('hips', 'tiles', 'tests', 'data') / filename
+        if format == 'fits':
             hdulist = fits.open(path)
-            self.data = np.array(hdulist[0].data)
-            self.header = hdulist[0].header
+            data = np.array(hdulist[0].data)
+            header = hdulist[0].header
+            return cls(format, data, header)
         else:
-            self.data = np.array(Image.open(str(path)))
+            data = np.array(Image.open(str(path)))
+            return cls(format, data)

     def write(self, filename: str) -> None:
         """Write HiPS tile by a given filename (`None`).
@@ -112,7 +104,7 @@ class HipsTile:
         filename : `str`
             Name of the file
         """
-        path = self.path / (''.join([filename, '.', self.format]))
+        path = self.path / filename
         if self.format == 'fits':
             hdu = fits.PrimaryHDU(self.data, header=self.header)
             hdulist = fits.HDUList([hdu])

commit 12f0b4fe059cdd29ecb1f37956320570c87791b8
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Tue Jun 20 17:58:47 2017 +0500

    Use / instead of joinpath

diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py
index a0eca23..8b033e1 100644
--- a/hips/tiles/tile.py
+++ b/hips/tiles/tile.py
@@ -96,7 +96,7 @@ class HipsTile:

     def read(self) -> None:
         """Read HiPS tile data from a directory and load into memory (`None`)."""
-        path = self.path.joinpath(''.join(['Npix', str(self.ipix), '.', self.format]))
+        path = self.path / (''.join(['Npix', str(self.ipix), '.', self.format]))
         if self.format == 'fits':
             hdulist = fits.open(path)
             self.data = np.array(hdulist[0].data)
@@ -112,7 +112,7 @@ class HipsTile:
         filename : `str`
             Name of the file
         """
-        path = self.path.joinpath(''.join([filename, '.', self.format]))
+        path = self.path / (''.join([filename, '.', self.format]))
         if self.format == 'fits':
             hdu = fits.PrimaryHDU(self.data, header=self.header)
             hdulist = fits.HDUList([hdu])

commit 2876957883d4eacc7984fa554dc176b75f35c1ad
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Tue Jun 20 17:53:59 2017 +0500

    Change path opening method

diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py
index 5f979d2..a0eca23 100644
--- a/hips/tiles/tile.py
+++ b/hips/tiles/tile.py
@@ -97,14 +97,12 @@ class HipsTile:
     def read(self) -> None:
         """Read HiPS tile data from a directory and load into memory (`None`)."""
         path = self.path.joinpath(''.join(['Npix', str(self.ipix), '.', self.format]))
-        with path.open('rb') as rf:
-            raw_img = BytesIO(rf.read())
-            if self.format == 'fits':
-                hdulist = fits.open(raw_img)
-                self.data = np.array(hdulist[0].data)
-                self.header = hdulist[0].header
-            else:
-                self.data = np.array(Image.open(raw_img))
+        if self.format == 'fits':
+            hdulist = fits.open(path)
+            self.data = np.array(hdulist[0].data)
+            self.header = hdulist[0].header
+        else:
+            self.data = np.array(Image.open(str(path)))

     def write(self, filename: str) -> None:
         """Write HiPS tile by a given filename (`None`).

commit b937d1486e2c67c2519c8b572b43e899536ca24c
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Tue Jun 20 14:30:02 2017 +0500

    Update tiles.hips module

diff --git a/hips/tiles/tests/data/Npix24185.fits b/hips/tiles/tests/data/Npix24185.fits
deleted file mode 100644
index d9786f5..0000000
Binary files a/hips/tiles/tests/data/Npix24185.fits and /dev/null differ
diff --git a/hips/tiles/tests/data/Npix24185.jpg b/hips/tiles/tests/data/Npix24185.jpg
deleted file mode 100644
index 63254fa..0000000
Binary files a/hips/tiles/tests/data/Npix24185.jpg and /dev/null differ
diff --git a/hips/tiles/tests/data/Npix30889.fits b/hips/tiles/tests/data/Npix30889.fits
new file mode 100644
index 0000000..522852f
Binary files /dev/null and b/hips/tiles/tests/data/Npix30889.fits differ
diff --git a/hips/tiles/tests/data/Npix30889.jpg b/hips/tiles/tests/data/Npix30889.jpg
new file mode 100644
index 0000000..b6365db
Binary files /dev/null and b/hips/tiles/tests/data/Npix30889.jpg differ
diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py
index a6c2107..1316b5d 100644
--- a/hips/tiles/tests/test_tile.py
+++ b/hips/tiles/tests/test_tile.py
@@ -1,5 +1,6 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 from astropy.utils.data import get_pkg_data_filename
+from astropy.tests.helper import remote_data
 from ..description import HipsDescription
 from ..tile import HipsTile
 from pathlib import Path
@@ -11,38 +12,50 @@ class TestHipsTile:
         filename = get_pkg_data_filename('data/properties.txt')
         hips_description = HipsDescription.read(filename)

-        cls.tile = HipsTile(hips_description, 6, 24185, 'jpg', 512)
-        cls.tile.base_url = 'http://alasky.unistra.fr/DSS/DSSColor'
+        cls.fits_tile = HipsTile(hips_description=hips_description, order=6,
+                            ipix=30889, format='fits', tile_width=512)
+        cls.jpg_tile = HipsTile(hips_description=hips_description, order=6,
+                            ipix=30889, format='jpg', tile_width=512)
+        cls.fits_tile.base_url = 'http://alasky.unistra.fr/2MASS/H'
+        cls.jpg_tile.base_url = 'http://alasky.unistra.fr/2MASS/H'

     def test_base_url(self):
-        assert self.tile.base_url == 'http://alasky.unistra.fr/DSS/DSSColor'
+        assert self.fits_tile.base_url == 'http://alasky.unistra.fr/2MASS/H'
+        assert self.jpg_tile.base_url == 'http://alasky.unistra.fr/2MASS/H'

     def test_tile_url(self):
-        assert self.tile.tile_url == 'http://alasky.unistra.fr/DSS/DSSColor/Norder6/Dir20000/Npix24185.jpg'
+        assert self.fits_tile.tile_url == 'http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.fits'
+        assert self.jpg_tile.tile_url == 'http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.jpg'

+    @remote_data
     def test_fetch(self):
-        self.tile.fetch()
+        self.fits_tile.fetch()
+        self.jpg_tile.fetch()

-        """
-        This data was obtain from 'http://alasky.unistra.fr/DSS/DSSColor/Norder6/Dir20000/Npix24185.jpg'
-        at the index [10, 20]
-        """
-        data_precomp = [0, 0, 0]
-        assert self.data[10, 20] == data_precomp
+        shape_fits_precomp = (512, 512)
+        shape_jpg_precomp = (512, 512, 3)
+        data_precomp = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]
+        assert self.fits_tile.data.shape == shape_fits_precomp
+        assert self.jpg_tile.data.shape == shape_jpg_precomp
+        assert list(self.fits_tile.data[510][:12]) == data_precomp

     def test_read(self):
-        self.tile.read()
+        self.fits_tile.read()
+        self.jpg_tile.read()

-        """
-        This data was obtain from 'http://alasky.unistra.fr/DSS/DSSColor/Norder6/Dir20000/Npix24185.jpg'
-        at the index [10, 20]
-        """
-        data_precomp = [0, 0, 0]
-        assert list(self.tile.data[10, 20]) == data_precomp
+        shape_fits_precomp = (512, 512)
+        shape_jpg_precomp = (512, 512, 3)
+        data_precomp = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]
+        assert (self.fits_tile.data.shape) == shape_fits_precomp
+        assert (self.jpg_tile.data.shape) == shape_jpg_precomp
+        assert list(self.fits_tile.data[510][:12]) == data_precomp

-    def test_store(self):
+    def test_write(self):
         filename = 'test_file'
-        self.tile.store(filename)
+        self.fits_tile.write(filename)
+        self.jpg_tile.write(filename)

-        path = self.tile.path.joinpath(''.join([filename, '.', self.tile.format]))
-        assert True == path.is_file()
+        path_fits = self.fits_tile.path.joinpath(''.join([filename, '.', self.fits_tile.format]))
+        path_jpg = self.jpg_tile.path.joinpath(''.join([filename, '.', self.jpg_tile.format]))
+        assert True == path_fits.is_file()
+        assert True == path_jpg.is_file()
diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py
index a55aa20..5f979d2 100644
--- a/hips/tiles/tile.py
+++ b/hips/tiles/tile.py
@@ -1,12 +1,11 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
-from astropy.utils.data import get_pkg_data_filename
 from .description import HipsDescription
 from astropy.io import fits
 from pathlib import Path
 from io import BytesIO
 from PIL import Image
-import numpy as np
 import urllib.request
+import numpy as np

 __all__ = [
     'HipsTile',
@@ -17,8 +16,8 @@ class HipsTile:
     """HiPS tile container.

     This class provides methods for fetching, reading,
-    and stroing a HiPS tile. It also contains a few
-    getters and setters around most commonly used
+    and writing a HiPS tile. It also contains a few
+    getters and setters around frequently used
     HiPS tile attributes.

     Parameters
@@ -26,43 +25,48 @@ class HipsTile:
     hips_description : `HipsDescription`
         Class HipsDescription contains HiPS properties
     order : `int`
-        Order of the HiPS
+        Order of the HiPS tile
     ipix : `int`
         HEALPix pixel number
-    tile_width : `int`
-        Width of HiPS tile
     format : `format`
         Format of HiPS tile
-    data : `list`
+    data : `numpy.ndarray`
         Pixel values of HiPS tile
+    tile_width : `int`
+        Width of HiPS tile
+    base_url : `str`
+        Base URL of HiPS tile

     Examples
     --------
     ::

+        >>> import urllib.request
         >>> from hips.tiles import HipsDescription
-        >>> from astropy.utils.data import get_pkg_data_filename
-        >>> filename = get_pkg_data_filename('tests/data/properties.txt')
-        >>> hips_description = HipsDescription.read(filename)
-        >>> hips_tile = HipsTile(hips_description, 6, 24185, 'fits')
-        >>> tile.read()
-        >>> tile.data
-        [[3617 3304 4196 ..., 4545 4800 5701]
-         [3638 3288 3934 ..., 4448 5029 4951]
-         [3116 3653 3412 ..., 5282 4406 4967]
-         ...,
-         [3890 3463 3448 ..., 5980 6902 6543]
-         [4845 4065 3821 ..., 6928 6557 7197]
-         [4261 3581 3842 ..., 7095 6390 6191]]
-    """
+        >>> from astropy.tests.helper import remote_data
+        >>> text = urllib.request.urlopen('https://raw.githubusercontent.com/hipspy/hips/master/hips/tiles/tests/data/properties.txt').read() # doctest: +REMOTE_DATA
+        >>> hips_description = HipsDescription.parse(str(text))
+        >>> hips_tile = HipsTile(hips_description=hips_description, order=6, ipix=30889, format='fits', tile_width=512)
+        >>> hips_tile.read()
+        >>> hips_tile.data
+        array([[0, 0, 0, ..., 0, 0, 0],
+               [0, 0, 0, ..., 0, 0, 0],
+               [0, 0, 0, ..., 0, 0, 0],
+               ...,
+               [0, 0, 0, ..., 1, 0, 0],
+               [0, 0, 0, ..., 1, 0, 1],
+               [0, 0, 0, ..., 1, 0, 1]], dtype=int16)
+        """

-    def __init__(self, hips_description: HipsDescription, order: int, ipix: int, format: str, data: list=None, tile_width: int=512) -> None:
+    def __init__(self, hips_description: HipsDescription, order: int, ipix: int, format: str,
+                 data: np.ndarray=None, tile_width: int=512, base_url: str=None) -> None:
         self.hips_description = hips_description
         self.order = order
         self.ipix = ipix
         self.tile_width = tile_width
         self.format = format
         self.data = data
+        self.base_url = base_url

     @staticmethod
     def _directory(ipix: int) -> int:
@@ -70,11 +74,6 @@ class HipsTile:
         return np.around(ipix, decimals=-(len(str(ipix)) - 1))

     @property
-    def base_url(self) -> str:
-        """HiPS tile base url (`str`)."""
-        return self._base_url
-
-    @property
     def path(self) -> Path:
         """Default path for tile storage (`Path`)."""
         return Path('hips', 'tiles', 'tests', 'data')
@@ -82,17 +81,13 @@ class HipsTile:
     @property
     def tile_url(self) -> str:
         """HiPS tile url (`str`)."""
-        return ''.join([self._base_url, '/Norder', str(self.order), '/Dir', str(self._directory(self.ipix)), '/Npix', str(self.ipix), '.', self.format])
-
-    @base_url.setter
-    def base_url(self, base_url: str) -> None:
-        """Set the HiPS tile base url (`None`)."""
-        self._base_url = base_url
+        return ''.join([self.base_url, '/Norder', str(self.order), '/Dir',
+               str(self._directory(self.ipix)), '/Npix', str(self.ipix), '.', self.format])

     def fetch(self) -> None:
         """Fetch HiPS tile and load into memory (`None`)."""
         raw_image = BytesIO(urllib.request.urlopen(self.tile_url).read())
-        if self.format is 'fits':
+        if self.format == 'fits':
             hdulist = fits.open(raw_image)
             self.data = np.array(hdulist[0].data)
             self.header = hdulist[0].header
@@ -104,15 +99,15 @@ class HipsTile:
         path = self.path.joinpath(''.join(['Npix', str(self.ipix), '.', self.format]))
         with path.open('rb') as rf:
             raw_img = BytesIO(rf.read())
-            if self.format is 'fits':
+            if self.format == 'fits':
                 hdulist = fits.open(raw_img)
                 self.data = np.array(hdulist[0].data)
                 self.header = hdulist[0].header
             else:
                 self.data = np.array(Image.open(raw_img))

-    def store(self, filename: str) -> None:
-        """Store HiPS tile by a given filename (`None`).
+    def write(self, filename: str) -> None:
+        """Write HiPS tile by a given filename (`None`).

         Parameters
         ----------
@@ -120,12 +115,10 @@ class HipsTile:
             Name of the file
         """
         path = self.path.joinpath(''.join([filename, '.', self.format]))
-        print(path)
-        with path.open('w') as wf:
-            if self.format is 'fits':
-                hdu = fits.PrimaryHDU(self.data, header=self.header)
-                hdulist = fits.HDUList([hdu])
-                hdulist.writeto(wf)
-                hdulist.close()
-            else:
-                Image.fromarray(self.data).save(wf)
+        if self.format == 'fits':
+            hdu = fits.PrimaryHDU(self.data, header=self.header)
+            hdulist = fits.HDUList([hdu])
+            hdulist.writeto(path)
+            hdulist.close()
+        else:
+            Image.fromarray(self.data).save(str(path))

commit 7869a5d5e36a1bd2219b5b9212b2dcd68ffc77f9
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Mon Jun 19 10:34:01 2017 +0500

    Add tile_width property to HipsTile

diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py
index 535b005..a6c2107 100644
--- a/hips/tiles/tests/test_tile.py
+++ b/hips/tiles/tests/test_tile.py
@@ -11,7 +11,7 @@ class TestHipsTile:
         filename = get_pkg_data_filename('data/properties.txt')
         hips_description = HipsDescription.read(filename)

-        cls.tile = HipsTile(hips_description, 6, 24185, 'jpg')
+        cls.tile = HipsTile(hips_description, 6, 24185, 'jpg', 512)
         cls.tile.base_url = 'http://alasky.unistra.fr/DSS/DSSColor'

     def test_base_url(self):
diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py
index 4f0fd3e..a55aa20 100644
--- a/hips/tiles/tile.py
+++ b/hips/tiles/tile.py
@@ -29,6 +29,8 @@ class HipsTile:
         Order of the HiPS
     ipix : `int`
         HEALPix pixel number
+    tile_width : `int`
+        Width of HiPS tile
     format : `format`
         Format of HiPS tile
     data : `list`
@@ -54,10 +56,11 @@ class HipsTile:
          [4261 3581 3842 ..., 7095 6390 6191]]
     """

-    def __init__(self, hips_description: HipsDescription, order: int, ipix: int, format: str, data: list=None) -> None:
+    def __init__(self, hips_description: HipsDescription, order: int, ipix: int, format: str, data: list=None, tile_width: int=512) -> None:
         self.hips_description = hips_description
         self.order = order
         self.ipix = ipix
+        self.tile_width = tile_width
         self.format = format
         self.data = data


commit 27cca875b322df5a4b0a9caa01d081f9fb3054b0
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 16 20:16:00 2017 +0500

    Update tiles.hips module

    This commit adds some extra methods for reading tiles (from disk and URL). It also provides a method
    for storing a HiPS tile on disk. The docstring is updated to contain an example showing fetching of a HiPS tile.

diff --git a/hips/tiles/tests/data/Npix24185.fits b/hips/tiles/tests/data/Npix24185.fits
new file mode 100644
index 0000000..d9786f5
Binary files /dev/null and b/hips/tiles/tests/data/Npix24185.fits differ
diff --git a/hips/tiles/tests/data/Npix24185.jpg b/hips/tiles/tests/data/Npix24185.jpg
new file mode 100644
index 0000000..63254fa
Binary files /dev/null and b/hips/tiles/tests/data/Npix24185.jpg differ
diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py
index f1f050b..535b005 100644
--- a/hips/tiles/tests/test_tile.py
+++ b/hips/tiles/tests/test_tile.py
@@ -1,8 +1,9 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 from astropy.utils.data import get_pkg_data_filename
-from ..tile import HipsTile
 from ..description import HipsDescription
-
+from ..tile import HipsTile
+from pathlib import Path
+import numpy as np

 class TestHipsTile:
     @classmethod
@@ -11,14 +12,37 @@ class TestHipsTile:
         hips_description = HipsDescription.read(filename)

         cls.tile = HipsTile(hips_description, 6, 24185, 'jpg')
-        cls.tile.data = [0, 0, 0, 0]
         cls.tile.base_url = 'http://alasky.unistra.fr/DSS/DSSColor'

     def test_base_url(self):
         assert self.tile.base_url == 'http://alasky.unistra.fr/DSS/DSSColor'

-    def test_data(self):
-        assert self.tile.data == [0, 0, 0, 0]
-
     def test_tile_url(self):
-        assert self.tile.get_tile_url(6, 24185, 'jpg') == 'http://alasky.unistra.fr/DSS/DSSColor/Norder6/Dir20000/Npix24185.jpg'
+        assert self.tile.tile_url == 'http://alasky.unistra.fr/DSS/DSSColor/Norder6/Dir20000/Npix24185.jpg'
+
+    def test_fetch(self):
+        self.tile.fetch()
+
+        """
+        This data was obtain from 'http://alasky.unistra.fr/DSS/DSSColor/Norder6/Dir20000/Npix24185.jpg'
+        at the index [10, 20]
+        """
+        data_precomp = [0, 0, 0]
+        assert self.data[10, 20] == data_precomp
+
+    def test_read(self):
+        self.tile.read()
+
+        """
+        This data was obtain from 'http://alasky.unistra.fr/DSS/DSSColor/Norder6/Dir20000/Npix24185.jpg'
+        at the index [10, 20]
+        """
+        data_precomp = [0, 0, 0]
+        assert list(self.tile.data[10, 20]) == data_precomp
+
+    def test_store(self):
+        filename = 'test_file'
+        self.tile.store(filename)
+
+        path = self.tile.path.joinpath(''.join([filename, '.', self.tile.format]))
+        assert True == path.is_file()
diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py
index 70cad39..4f0fd3e 100644
--- a/hips/tiles/tile.py
+++ b/hips/tiles/tile.py
@@ -1,6 +1,12 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
+from astropy.utils.data import get_pkg_data_filename
 from .description import HipsDescription
+from astropy.io import fits
+from pathlib import Path
+from io import BytesIO
+from PIL import Image
 import numpy as np
+import urllib.request

 __all__ = [
     'HipsTile',
@@ -10,36 +16,113 @@ __all__ = [
 class HipsTile:
     """HiPS tile container.

+    This class provides methods for fetching, reading,
+    and stroing a HiPS tile. It also contains a few
+    getters and setters around most commonly used
+    HiPS tile attributes.
+
+    Parameters
+    ----------
+    hips_description : `HipsDescription`
+        Class HipsDescription contains HiPS properties
+    order : `int`
+        Order of the HiPS
+    ipix : `int`
+        HEALPix pixel number
+    format : `format`
+        Format of HiPS tile
+    data : `list`
+        Pixel values of HiPS tile
+
+    Examples
+    --------
+    ::
+
+        >>> from hips.tiles import HipsDescription
+        >>> from astropy.utils.data import get_pkg_data_filename
+        >>> filename = get_pkg_data_filename('tests/data/properties.txt')
+        >>> hips_description = HipsDescription.read(filename)
+        >>> hips_tile = HipsTile(hips_description, 6, 24185, 'fits')
+        >>> tile.read()
+        >>> tile.data
+        [[3617 3304 4196 ..., 4545 4800 5701]
+         [3638 3288 3934 ..., 4448 5029 4951]
+         [3116 3653 3412 ..., 5282 4406 4967]
+         ...,
+         [3890 3463 3448 ..., 5980 6902 6543]
+         [4845 4065 3821 ..., 6928 6557 7197]
+         [4261 3581 3842 ..., 7095 6390 6191]]
     """

-    def __init__(self, hips_description: HipsDescription, order: int, ipix: int, format: str) -> None:
+    def __init__(self, hips_description: HipsDescription, order: int, ipix: int, format: str, data: list=None) -> None:
         self.hips_description = hips_description
         self.order = order
         self.ipix = ipix
         self.format = format
+        self.data = data
+
+    @staticmethod
+    def _directory(ipix: int) -> int:
+        """Directory of the HiPS tile (`int`)."""
+        return np.around(ipix, decimals=-(len(str(ipix)) - 1))

     @property
     def base_url(self) -> str:
         """HiPS tile base url (`str`)."""
-        return self.base_url
+        return self._base_url

     @property
-    def data(self) -> list:
-        """HiPS tile data (`list`)."""
-        return self.data
+    def path(self) -> Path:
+        """Default path for tile storage (`Path`)."""
+        return Path('hips', 'tiles', 'tests', 'data')

     @property
     def tile_url(self) -> str:
         """HiPS tile url (`str`)."""
-        directory = np.around(self.ipix, decimals=-(len(str(self.ipix)) - 1))
-        return ''.join([self.base_url, '/Norder', str(self.order), '/Dir', str(directory), '/Npix', str(self.ipix), '.', self.format])
+        return ''.join([self._base_url, '/Norder', str(self.order), '/Dir', str(self._directory(self.ipix)), '/Npix', str(self.ipix), '.', self.format])

     @base_url.setter
     def base_url(self, base_url: str) -> None:
         """Set the HiPS tile base url (`None`)."""
         self._base_url = base_url

-    @data.setter
-    def data(self, data: list) -> None:
-        """Set the HiPS tile data (`None`)."""
-        self.data = data
+    def fetch(self) -> None:
+        """Fetch HiPS tile and load into memory (`None`)."""
+        raw_image = BytesIO(urllib.request.urlopen(self.tile_url).read())
+        if self.format is 'fits':
+            hdulist = fits.open(raw_image)
+            self.data = np.array(hdulist[0].data)
+            self.header = hdulist[0].header
+        else:
+            self.data = np.array(Image.open(raw_image))
+
+    def read(self) -> None:
+        """Read HiPS tile data from a directory and load into memory (`None`)."""
+        path = self.path.joinpath(''.join(['Npix', str(self.ipix), '.', self.format]))
+        with path.open('rb') as rf:
+            raw_img = BytesIO(rf.read())
+            if self.format is 'fits':
+                hdulist = fits.open(raw_img)
+                self.data = np.array(hdulist[0].data)
+                self.header = hdulist[0].header
+            else:
+                self.data = np.array(Image.open(raw_img))
+
+    def store(self, filename: str) -> None:
+        """Store HiPS tile by a given filename (`None`).
+
+        Parameters
+        ----------
+        filename : `str`
+            Name of the file
+        """
+        path = self.path.joinpath(''.join([filename, '.', self.format]))
+        print(path)
+        with path.open('w') as wf:
+            if self.format is 'fits':
+                hdu = fits.PrimaryHDU(self.data, header=self.header)
+                hdulist = fits.HDUList([hdu])
+                hdulist.writeto(wf)
+                hdulist.close()
+            else:
+                Image.fromarray(self.data).save(wf)

commit b5a268688e48d775a6aceb82898af8fc745c3b9f
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Thu Jun 15 15:32:30 2017 +0500

    Update tiles.hips module

    Renamed the main Hips class to HipsTile. Added additional methods and test cases

diff --git a/hips/tiles/__init__.py b/hips/tiles/__init__.py
index 12ac39b..f6101be 100644
--- a/hips/tiles/__init__.py
+++ b/hips/tiles/__init__.py
@@ -1,4 +1,4 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 """Classes and functions to manage HiPS tiles."""
-from .hips import *
+from .tile import *
 from .description import *
diff --git a/hips/tiles/hips.py b/hips/tiles/hips.py
deleted file mode 100644
index c01c0b8..0000000
--- a/hips/tiles/hips.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# Licensed under a 3-clause BSD style license - see LICENSE.rst
-from .description import HipsDescription
-import numpy as np
-
-__all__ = [
-    'Hips',
-]
-
-
-class Hips:
-    """HiPS base url container.
-
-    """
-
-    def __init__(self) -> None:
-        pass
-
-    @property
-    def base_url(self) -> str:
-        """HiPS base url (`str`)."""
-        return self.base_url
-
-    @base_url.setter
-    def base_url(self, base_url: str) -> None:
-        """Set the HiPS base url (`None`)."""
-        self.base_url = base_url
-
-    def get_tile_url(self, order: int, ipix: int, format: str) -> str:
-        """HiPS tile url (`str`)."""
-        directory = np.around(ipix, decimals=-(len(str(ipix)) - 1))
-        return ''.join([self.base_url, '/Norder', str(order), '/Dir', str(directory), '/Npix', str(ipix), '.', format])
diff --git a/hips/tiles/tests/test_hips.py b/hips/tiles/tests/test_hips.py
deleted file mode 100644
index 831ccb3..0000000
--- a/hips/tiles/tests/test_hips.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# Licensed under a 3-clause BSD style license - see LICENSE.rst
-from ..hips import Hips
-
-
-class TestHiPS:
-    @classmethod
-    def setup_class(cls):
-        cls.hips = Hips
-        cls.hips.base_url = 'http://alasky.unistra.fr/DSS/DSSColor'
-
-    def test_base_url(self):
-        assert self.hips.base_url == 'http://alasky.unistra.fr/DSS/DSSColor'
-
-    def test_tile_url(self):
-        assert self.hips.get_tile_url(self.hips, 6, 24185, 'jpg') == 'http://alasky.unistra.fr/DSS/DSSColor/Norder6/Dir20000/Npix24185.jpg'
diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py
new file mode 100644
index 0000000..f1f050b
--- /dev/null
+++ b/hips/tiles/tests/test_tile.py
@@ -0,0 +1,24 @@
+# Licensed under a 3-clause BSD style license - see LICENSE.rst
+from astropy.utils.data import get_pkg_data_filename
+from ..tile import HipsTile
+from ..description import HipsDescription
+
+
+class TestHipsTile:
+    @classmethod
+    def setup_class(cls):
+        filename = get_pkg_data_filename('data/properties.txt')
+        hips_description = HipsDescription.read(filename)
+
+        cls.tile = HipsTile(hips_description, 6, 24185, 'jpg')
+        cls.tile.data = [0, 0, 0, 0]
+        cls.tile.base_url = 'http://alasky.unistra.fr/DSS/DSSColor'
+
+    def test_base_url(self):
+        assert self.tile.base_url == 'http://alasky.unistra.fr/DSS/DSSColor'
+
+    def test_data(self):
+        assert self.tile.data == [0, 0, 0, 0]
+
+    def test_tile_url(self):
+        assert self.tile.get_tile_url(6, 24185, 'jpg') == 'http://alasky.unistra.fr/DSS/DSSColor/Norder6/Dir20000/Npix24185.jpg'
diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py
new file mode 100644
index 0000000..70cad39
--- /dev/null
+++ b/hips/tiles/tile.py
@@ -0,0 +1,45 @@
+# Licensed under a 3-clause BSD style license - see LICENSE.rst
+from .description import HipsDescription
+import numpy as np
+
+__all__ = [
+    'HipsTile',
+]
+
+
+class HipsTile:
+    """HiPS tile container.
+
+    """
+
+    def __init__(self, hips_description: HipsDescription, order: int, ipix: int, format: str) -> None:
+        self.hips_description = hips_description
+        self.order = order
+        self.ipix = ipix
+        self.format = format
+
+    @property
+    def base_url(self) -> str:
+        """HiPS tile base url (`str`)."""
+        return self.base_url
+
+    @property
+    def data(self) -> list:
+        """HiPS tile data (`list`)."""
+        return self.data
+
+    @property
+    def tile_url(self) -> str:
+        """HiPS tile url (`str`)."""
+        directory = np.around(self.ipix, decimals=-(len(str(self.ipix)) - 1))
+        return ''.join([self.base_url, '/Norder', str(self.order), '/Dir', str(directory), '/Npix', str(self.ipix), '.', self.format])
+
+    @base_url.setter
+    def base_url(self, base_url: str) -> None:
+        """Set the HiPS tile base url (`None`)."""
+        self._base_url = base_url
+
+    @data.setter
+    def data(self, data: list) -> None:
+        """Set the HiPS tile data (`None`)."""
+        self.data = data

commit 1f0b0dfb873e83e3af0d432e49a245ec650636da
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Wed Jun 14 09:27:43 2017 +0500

    Remove blank lines and docstring

diff --git a/hips/tiles/hips.py b/hips/tiles/hips.py
index 1c7fafd..c01c0b8 100644
--- a/hips/tiles/hips.py
+++ b/hips/tiles/hips.py
@@ -1,7 +1,4 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
-"""Classes and functions to manage HiPS tiles."""
-
-
 from .description import HipsDescription
 import numpy as np


commit 6826c7b9f97f45b2ce62b6f700a7a8758c9661a3
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Wed Jun 14 09:24:54 2017 +0500

    Update .gitignore to ignore Mypy cache directory

diff --git a/.gitignore b/.gitignore
index 21dfb4f..ad63e7f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,6 +29,9 @@ docs/_build
 # Pycharm editor project files
 .idea

+# Mypy cache
+.mypy_cache
+
 # Floobits project files
 .floo
 .flooignore

commit a16a78f789b5f96f814a8856f503f979693202cb
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Wed Jun 14 09:24:00 2017 +0500

    Add tiles.hips package

diff --git a/hips/tiles/__init__.py b/hips/tiles/__init__.py
index d19ff3e..12ac39b 100644
--- a/hips/tiles/__init__.py
+++ b/hips/tiles/__init__.py
@@ -1,3 +1,4 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 """Classes and functions to manage HiPS tiles."""
+from .hips import *
 from .description import *
diff --git a/hips/tiles/hips.py b/hips/tiles/hips.py
new file mode 100644
index 0000000..1c7fafd
--- /dev/null
+++ b/hips/tiles/hips.py
@@ -0,0 +1,34 @@
+# Licensed under a 3-clause BSD style license - see LICENSE.rst
+"""Classes and functions to manage HiPS tiles."""
+
+
+from .description import HipsDescription
+import numpy as np
+
+__all__ = [
+    'Hips',
+]
+
+
+class Hips:
+    """HiPS base url container.
+
+    """
+
+    def __init__(self) -> None:
+        pass
+
+    @property
+    def base_url(self) -> str:
+        """HiPS base url (`str`)."""
+        return self.base_url
+
+    @base_url.setter
+    def base_url(self, base_url: str) -> None:
+        """Set the HiPS base url (`None`)."""
+        self.base_url = base_url
+
+    def get_tile_url(self, order: int, ipix: int, format: str) -> str:
+        """HiPS tile url (`str`)."""
+        directory = np.around(ipix, decimals=-(len(str(ipix)) - 1))
+        return ''.join([self.base_url, '/Norder', str(order), '/Dir', str(directory), '/Npix', str(ipix), '.', format])
diff --git a/hips/tiles/tests/test_hips.py b/hips/tiles/tests/test_hips.py
new file mode 100644
index 0000000..831ccb3
--- /dev/null
+++ b/hips/tiles/tests/test_hips.py
@@ -0,0 +1,15 @@
+# Licensed under a 3-clause BSD style license - see LICENSE.rst
+from ..hips import Hips
+
+
+class TestHiPS:
+    @classmethod
+    def setup_class(cls):
+        cls.hips = Hips
+        cls.hips.base_url = 'http://alasky.unistra.fr/DSS/DSSColor'
+
+    def test_base_url(self):
+        assert self.hips.base_url == 'http://alasky.unistra.fr/DSS/DSSColor'
+
+    def test_tile_url(self):
+        assert self.hips.get_tile_url(self.hips, 6, 24185, 'jpg') == 'http://alasky.unistra.fr/DSS/DSSColor/Norder6/Dir20000/Npix24185.jpg'

commit 0ffe20e0ea787f11df65cd384c63e03c522aa514
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Wed Jun 21 13:31:42 2017 +0500

    Update hips.utils package

diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py
index 584be5f..c485bc0 100644
--- a/hips/utils/healpix.py
+++ b/hips/utils/healpix.py
@@ -5,6 +5,8 @@ This module contains wrapper functions around HEALPix utilizing
 the healpy library
 """

+__doctest_skip__ = ['boundaries', 'compute_healpix_pixel_indices']
+
 __all__ = [
     'boundaries', 'compute_healpix_pixel_indices'
 ]
@@ -87,15 +89,17 @@ def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, nside: int) -> np.n
         >>> order = 3
         >>> nside = hp.order2nside(order)
         >>> skycoord = SkyCoord(10, 20, unit="deg")
-        >>> wcs_geometry = WCSGeometry.create(skydir=skycoord, shape=(10, 20), coordsys='CEL', projection='AIT', cdelt=1.0, crpix=(1., 1.))
+        >>> wcs_geometry = WCSGeometry.create(skydir=skycoord, shape=(10, 20), \
+coordsys='CEL', projection='AIT', \
+cdelt=1.0, crpix=(1., 1.))
         >>> compute_healpix_pixel_indices(wcs_geometry, nside)
         [ 84 111 112 113 142 143 144 145 146 174 175 176 177 178 206 207 208 209
         210 238 239 240 241 270 271 272 273 274 302 303 304 305 334 335 336 337
         367 368 399]
     """
-    y_center, x_center = wcs_geometry.shape[0] // 2, wcs_geometry.shape[1] // 2
+    y_center, x_center = wcs_geometry.Shape.ny / 2, wcs_geometry.Shape.nx / 2
     lon_center, lat_center = wcs_geometry.wcs.all_pix2world(x_center, y_center, 1)
-    vec = hp.ang2vec(lon_center, lat_center, lonlat=True)
     separations = angular_separation(x_center, y_center, lon_center, lat_center)
     max_separation = np.nanmax(separations)
+    vec = hp.ang2vec(lon_center, lat_center, lonlat=True)
     return hp.query_disc(nside, vec, max_separation)
diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py
index 3d8952e..ec2dc3d 100644
--- a/hips/utils/tests/test_healpix.py
+++ b/hips/utils/tests/test_healpix.py
@@ -26,15 +26,14 @@ def test_boundaries():
     assert_allclose([radec.ra, radec.dec], radec_precomp)

 def test_compute_healpix_pixel_indices():
-    order = 3
-    nside = hp.order2nside(order)
+    nside = hp.order2nside(order=3)

     skycoord = SkyCoord(10, 20, unit="deg")
-    wcs_geometry = WCSGeometry.create(skydir=skycoord, shape=(10, 20), coordsys='CEL', projection='AIT', cdelt=1.0, crpix=(1., 1.))
-
+    wcs_geometry_CEL = WCSGeometry.create(skydir=skycoord, shape=(10, 20), coordsys='CEL',
+                                      projection='AIT', cdelt=1.0, crpix=(1., 1.))
+    wcs_geometry_GAL = WCSGeometry.create(skydir=skycoord, shape=(10, 20), coordsys='GAL',
+                                      projection='AIT', cdelt=1.0, crpix=(1., 1.))
     pixels_precomp = [84, 111, 112, 113]
-    len_precomp = 39
-    pixels = compute_healpix_pixel_indices(wcs_geometry, nside)
-    print(pixels)
-    assert_allclose(len(pixels), len_precomp)
+    pixels = compute_healpix_pixel_indices(wcs_geometry_CEL, nside)
+    assert len(pixels) == 39
     assert_allclose(pixels[0:4], pixels_precomp)
diff --git a/hips/utils/wcs.py b/hips/utils/wcs.py
index d1d365e..9a58482 100644
--- a/hips/utils/wcs.py
+++ b/hips/utils/wcs.py
@@ -1,8 +1,11 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 import numpy as np
 from astropy.wcs import WCS
+from collections import namedtuple
 from astropy.coordinates import SkyCoord

+__doctest_skip__ = ['WCSGeometry']
+
 __all__ = [
     'WCSGeometry',
 ]
@@ -25,7 +28,9 @@ class WCSGeometry:
         >>> from hips.utils import WCSGeometry
         >>> from astropy.coordinates import SkyCoord
         >>> skycoord = SkyCoord(10, 20, unit="deg")
-        >>> wcs_geometry = WCSGeometry.create(skydir=skycoord, shape=(10, 20), coordsys='CEL', projection='AIT', cdelt=1.0, crpix=1.)
+        >>> wcs_geometry = WCSGeometry.create(skydir=skycoord, shape=(10, 20), \
+coordsys='CEL', projection='AIT', \
+cdelt=1.0, crpix=1.)
         >>> wcs_geometry.wcs
         Number of WCS axes: 2
         CTYPE : 'RA---AIT'  'DEC--AIT'
@@ -41,7 +46,7 @@ class WCSGeometry:

     def __init__(self, wcs: WCS, shape: tuple) -> None:
         self.wcs = wcs
-        self.shape = shape
+        self.Shape = namedtuple('Shape', ['ny', 'nx'])(*shape)

     @classmethod
     def create(cls, skydir: SkyCoord, shape: tuple, coordsys: str='CEL',
@@ -77,7 +82,7 @@ class WCSGeometry:
             w.wcs.crval[0] = skydir.galactic.l.deg
             w.wcs.crval[1] = skydir.galactic.b.deg
         else:
-            raise ValueError('Unrecognized coordinate system.')
+            raise ValueError('Unrecognized coordinate system.') # pragma: no cover

         w.wcs.crpix[0] = crpix[0]
         w.wcs.crpix[1] = crpix[1]

commit 8f3139dfd23e9624e4cd9dafd412889cc49a340e
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Mon Jun 19 13:41:23 2017 +0500

    Update hips.utils package

diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py
index 4dfb22a..584be5f 100644
--- a/hips/utils/healpix.py
+++ b/hips/utils/healpix.py
@@ -58,11 +58,11 @@ def boundaries(nside: int, pix: int, nest: bool=True) -> tuple:
     return theta, phi

 def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, nside: int) -> np.ndarray:
-    """Returns an array containing pixels corresponding to an image.
+    """Returns an array containing HEALPix pixels corresponding to disk regions.

     This function calls `healpy.pixelfunc.ang2vec`, `healpy.query_disc`, and
     `astropy.coordinates.angle_utilities.angular_separation` to compute
-    the pixel values, which will be use in tile drawing.
+    the HEALPix pixel indices, which will be used in tile drawing.

     Parameters
     ----------
@@ -75,6 +75,23 @@ def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, nside: int) -> np.n
     -------
     pixels : `numpy.ndarray`
         HEALPix pixel numbers
+
+    Examples
+    --------
+    ::
+
+        >>> import healpy as hp
+        >>> from hips.utils import WCSGeometry
+        >>> from astropy.coordinates import SkyCoord
+        >>> from hips.utils import compute_healpix_pixel_indices
+        >>> order = 3
+        >>> nside = hp.order2nside(order)
+        >>> skycoord = SkyCoord(10, 20, unit="deg")
+        >>> wcs_geometry = WCSGeometry.create(skydir=skycoord, shape=(10, 20), coordsys='CEL', projection='AIT', cdelt=1.0, crpix=(1., 1.))
+        >>> compute_healpix_pixel_indices(wcs_geometry, nside)
+        [ 84 111 112 113 142 143 144 145 146 174 175 176 177 178 206 207 208 209
+        210 238 239 240 241 270 271 272 273 274 302 303 304 305 334 335 336 337
+        367 368 399]
     """
     y_center, x_center = wcs_geometry.shape[0] // 2, wcs_geometry.shape[1] // 2
     lon_center, lat_center = wcs_geometry.wcs.all_pix2world(x_center, y_center, 1)
diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py
index 8945ee4..3d8952e 100644
--- a/hips/utils/tests/test_healpix.py
+++ b/hips/utils/tests/test_healpix.py
@@ -30,13 +30,11 @@ def test_compute_healpix_pixel_indices():
     nside = hp.order2nside(order)

     skycoord = SkyCoord(10, 20, unit="deg")
-    wcs_geometry = WCSGeometry.create(skycoord, (10, 20), 'CEL', 'AIT', 1.0, 1)
-
-    """
-    These pixel values were obtained for the all-sky image located at:
-    https://github.com/gammapy/gammapy-extra/blob/master/datasets/catalogs/fermi/gll_psch_v08.fit.gz?raw=true
-    """
-    pixels_precomp = [0, 767]
+    wcs_geometry = WCSGeometry.create(skydir=skycoord, shape=(10, 20), coordsys='CEL', projection='AIT', cdelt=1.0, crpix=(1., 1.))

+    pixels_precomp = [84, 111, 112, 113]
+    len_precomp = 39
     pixels = compute_healpix_pixel_indices(wcs_geometry, nside)
-    assert_allclose([pixels[0], pixels[-1]], pixels_precomp)
+    print(pixels)
+    assert_allclose(len(pixels), len_precomp)
+    assert_allclose(pixels[0:4], pixels_precomp)
diff --git a/hips/utils/wcs.py b/hips/utils/wcs.py
index 472d009..d1d365e 100644
--- a/hips/utils/wcs.py
+++ b/hips/utils/wcs.py
@@ -17,25 +17,36 @@ class WCSGeometry:
         WCS projection object
     shape : tuple
         Shape of the image
-    """

-    def __init__(self, wcs: WCS, shape: tuple) -> None:
-        self._wcs = wcs
-        self._shape = shape
+    Examples
+    --------
+    ::

-    @property
-    def shape(self) -> None:
-        """Shape of the image (`None`)."""
-        return self._shape
+        >>> from hips.utils import WCSGeometry
+        >>> from astropy.coordinates import SkyCoord
+        >>> skycoord = SkyCoord(10, 20, unit="deg")
+        >>> wcs_geometry = WCSGeometry.create(skydir=skycoord, shape=(10, 20), coordsys='CEL', projection='AIT', cdelt=1.0, crpix=1.)
+        >>> wcs_geometry.wcs
+        Number of WCS axes: 2
+        CTYPE : 'RA---AIT'  'DEC--AIT'
+        CRVAL : 10.0  20.0
+        CRPIX : 1.0  1.0
+        PC1_1 PC1_2  : 1.0  0.0
+        PC2_1 PC2_2  : 0.0  1.0
+        CDELT : -1.0  1.0
+        NAXIS : 0  0
+        >>> wcs_geometry.shape
+        (10, 20)
+    """

-    @property
-    def wcs(self) -> None:
-        """WCS object containing FITS image header (`None`)."""
-        return self._wcs
+    def __init__(self, wcs: WCS, shape: tuple) -> None:
+        self.wcs = wcs
+        self.shape = shape

     @classmethod
-    def create(cls, skydir: SkyCoord, shape: tuple, coordsys: str='CEL', projection: str='AIT', cdelt: float=1.0, crpix: float=1.) -> 'WCSGeometry':
-        """Read from HiPS description file (`WCSGeometry`).
+    def create(cls, skydir: SkyCoord, shape: tuple, coordsys: str='CEL',
+        projection: str='AIT', cdelt: float=1.0, crpix: tuple=(1., 1.)) -> 'WCSGeometry':
+        """Create WCS object programmatically (`WCSGeometry`).

         Parameters
         ----------
@@ -49,15 +60,11 @@ class WCSGeometry:
             Projection of the WCS object
         cdelt : `float`
             Coordinate increment at reference point
-        crpix : `float`
-            Pixel coordinate of reference point
+        crpix : `tuple`
+            Pixel coordinates of reference point
         """

-        naxis = 2
-        if shape is not None:
-            naxis += len(shape)
-
-        w = WCS(naxis=naxis)
+        w = WCS(naxis=2)

         if coordsys == 'CEL':
             w.wcs.ctype[0] = 'RA---{}'.format(projection)
@@ -72,12 +79,9 @@ class WCSGeometry:
         else:
             raise ValueError('Unrecognized coordinate system.')

-        try:
-            w.wcs.crpix[0] = crpix[0]
-            w.wcs.crpix[1] = crpix[1]
-        except:
-            w.wcs.crpix[0] = crpix
-            w.wcs.crpix[1] = crpix
+        w.wcs.crpix[0] = crpix[0]
+        w.wcs.crpix[1] = crpix[1]
+
         w.wcs.cdelt[0] = -cdelt
         w.wcs.cdelt[1] = cdelt


commit 81d74de79ec5971d0e528a3f5c2d8e0e21fc7e0a
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Sun Jun 18 09:17:17 2017 +0500

    Add classmethod for creating a WCS object

diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py
index 4c74dd7..4dfb22a 100644
--- a/hips/utils/healpix.py
+++ b/hips/utils/healpix.py
@@ -6,7 +6,7 @@ the healpy library
 """

 __all__ = [
-    'boundaries', 'compute_image_pixels'
+    'boundaries', 'compute_healpix_pixel_indices'
 ]

 import healpy as hp
diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py
index afdd80d..8945ee4 100644
--- a/hips/utils/tests/test_healpix.py
+++ b/hips/utils/tests/test_healpix.py
@@ -3,11 +3,9 @@ import numpy as np
 import healpy as hp
 from ..wcs import WCSGeometry
 from ..healpix import boundaries
-from ..healpix import compute_image_pixels
-from astropy.io import fits
-from astropy.wcs import WCS
 from astropy.coordinates import SkyCoord
 from numpy.testing import assert_allclose
+from ..healpix import compute_healpix_pixel_indices


 def test_boundaries():
@@ -31,9 +29,8 @@ def test_compute_healpix_pixel_indices():
     order = 3
     nside = hp.order2nside(order)

-    hdu = fits.open('https://github.com/gammapy/gammapy-extra/blob/master/datasets/catalogs/fermi/gll_psch_v08.fit.gz?raw=true')
-    wcs = WCS(hdu[0].header)
-    wcs_geometry = WCSGeometry(wcs, hdu[0].data.shape)
+    skycoord = SkyCoord(10, 20, unit="deg")
+    wcs_geometry = WCSGeometry.create(skycoord, (10, 20), 'CEL', 'AIT', 1.0, 1)

     """
     These pixel values were obtained for the all-sky image located at:
@@ -41,5 +38,5 @@ def test_compute_healpix_pixel_indices():
     """
     pixels_precomp = [0, 767]

-    pixels = compute_image_pixels(wcs_geometry, nside)
+    pixels = compute_healpix_pixel_indices(wcs_geometry, nside)
     assert_allclose([pixels[0], pixels[-1]], pixels_precomp)
diff --git a/hips/utils/wcs.py b/hips/utils/wcs.py
index 45b5541..472d009 100644
--- a/hips/utils/wcs.py
+++ b/hips/utils/wcs.py
@@ -1,6 +1,7 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 import numpy as np
 from astropy.wcs import WCS
+from astropy.coordinates import SkyCoord

 __all__ = [
     'WCSGeometry',
@@ -33,17 +34,53 @@ class WCSGeometry:
         return self._wcs

     @classmethod
-    def create(cls, projection: str='AIT', cdelt: float=1.0, crpix: float=1., shape: tuple) -> 'WCSGeometry':
+    def create(cls, skydir: SkyCoord, shape: tuple, coordsys: str='CEL', projection: str='AIT', cdelt: float=1.0, crpix: float=1.) -> 'WCSGeometry':
         """Read from HiPS description file (`WCSGeometry`).

         Parameters
         ----------
-        projection : `str`
-            Project of the WCS object
-        cdelt : `str`
-        crpix : `str`
+        skydir : `~astropy.coordinates.SkyCoord`
+            Sky coordinate of the WCS reference point
         shape : `tuple`
-            Image shape
+            Shape of the image
+        coordsys : `str`
+            Coordinate system
+        projection : `str`
+            Projection of the WCS object
+        cdelt : `float`
+            Coordinate increment at reference point
+        crpix : `float`
+            Pixel coordinate of reference point
         """
-        # TODO
-        pass
+
+        naxis = 2
+        if shape is not None:
+            naxis += len(shape)
+
+        w = WCS(naxis=naxis)
+
+        if coordsys == 'CEL':
+            w.wcs.ctype[0] = 'RA---{}'.format(projection)
+            w.wcs.ctype[1] = 'DEC--{}'.format(projection)
+            w.wcs.crval[0] = skydir.icrs.ra.deg
+            w.wcs.crval[1] = skydir.icrs.dec.deg
+        elif coordsys == 'GAL':
+            w.wcs.ctype[0] = 'GLON-{}'.format(projection)
+            w.wcs.ctype[1] = 'GLAT-{}'.format(projection)
+            w.wcs.crval[0] = skydir.galactic.l.deg
+            w.wcs.crval[1] = skydir.galactic.b.deg
+        else:
+            raise ValueError('Unrecognized coordinate system.')
+
+        try:
+            w.wcs.crpix[0] = crpix[0]
+            w.wcs.crpix[1] = crpix[1]
+        except:
+            w.wcs.crpix[0] = crpix
+            w.wcs.crpix[1] = crpix
+        w.wcs.cdelt[0] = -cdelt
+        w.wcs.cdelt[1] = cdelt
+
+        w = WCS(w.to_header())
+
+        return cls(w, shape)

commit 5f52d62d5402080dcd271b106670d86878856f3b
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 16 22:35:54 2017 +0500

    Add class WCSGeometry

    This commit renames the compute_image_pixels function to compute_healpix_pixel_indices and wrap the WCS object and image shape in a separate class. Also, some refactoring is done in the test case.

diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py
index 0ab2549..4c74dd7 100644
--- a/hips/utils/healpix.py
+++ b/hips/utils/healpix.py
@@ -11,6 +11,7 @@ __all__ = [

 import healpy as hp
 import numpy as np
+from .wcs import WCSGeometry
 from astropy.coordinates.angle_utilities import angular_separation
 from astropy.wcs import WCS

@@ -52,13 +53,12 @@ def boundaries(nside: int, pix: int, nest: bool=True) -> tuple:
         [( 264.375, -24.62431835), ( 258.75 , -30.        ),
          ( 264.375, -35.68533471), ( 270.   , -30.        )]>
     """
-
     boundary_coords = hp.boundaries(nside, pix, nest=nest)
     theta, phi = hp.vec2ang(np.transpose(boundary_coords))
     return theta, phi

-def compute_image_pixels(nside: int, shape: tuple, wcs: WCS) -> np.ndarray:
-    """Returns an array containing the pixels corresponding to an image.
+def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, nside: int) -> np.ndarray:
+    """Returns an array containing pixels corresponding to an image.

     This function calls `healpy.pixelfunc.ang2vec`, `healpy.query_disc`, and
     `astropy.coordinates.angle_utilities.angular_separation` to compute
@@ -66,22 +66,18 @@ def compute_image_pixels(nside: int, shape: tuple, wcs: WCS) -> np.ndarray:

     Parameters
     ----------
+    wcs_geometry : WCSGeometry
+        Container for WCS object and image shape
     nside : int
         The nside of the HEALPix map
-    shape : tuple
-        Shape of the image
-    wcs : astropy.wcs.wcs.WCS
-        A WCS object containing the image header

     Returns
     -------
     pixels : `numpy.ndarray`
-        Returns a list of pixel values
-
+        HEALPix pixel numbers
     """
-
-    y_center, x_center = shape[0] // 2, shape[1] // 2
-    lon_center, lat_center = wcs.all_pix2world(x_center, y_center, 1)
+    y_center, x_center = wcs_geometry.shape[0] // 2, wcs_geometry.shape[1] // 2
+    lon_center, lat_center = wcs_geometry.wcs.all_pix2world(x_center, y_center, 1)
     vec = hp.ang2vec(lon_center, lat_center, lonlat=True)
     separations = angular_separation(x_center, y_center, lon_center, lat_center)
     max_separation = np.nanmax(separations)
diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py
index da358dd..afdd80d 100644
--- a/hips/utils/tests/test_healpix.py
+++ b/hips/utils/tests/test_healpix.py
@@ -1,6 +1,7 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 import numpy as np
 import healpy as hp
+from ..wcs import WCSGeometry
 from ..healpix import boundaries
 from ..healpix import compute_image_pixels
 from astropy.io import fits
@@ -26,43 +27,19 @@ def test_boundaries():
                      [-24.624318,  -30.,  -35.685335,  -30.]]
     assert_allclose([radec.ra, radec.dec], radec_precomp)

-def test_compute_image_pixels():
+def test_compute_healpix_pixel_indices():
     order = 3
     nside = hp.order2nside(order)

     hdu = fits.open('https://github.com/gammapy/gammapy-extra/blob/master/datasets/catalogs/fermi/gll_psch_v08.fit.gz?raw=true')
     wcs = WCS(hdu[0].header)
+    wcs_geometry = WCSGeometry(wcs, hdu[0].data.shape)

     """
     These pixel values were obtained for the all-sky image located at:
     https://github.com/gammapy/gammapy-extra/blob/master/datasets/catalogs/fermi/gll_psch_v08.fit.gz?raw=true
     """
-    pixels_precomp = \
-    [  0, 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15, 20, 21, 22, 23,
-      24, 25, 26, 27, 28, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 54, 55,
-      56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 77, 78, 79, 80, 81, 82, 83,
-      84, 85, 86, 87, 88, 89, 90, 91, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
-     114, 115, 116, 117, 118, 119, 120, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145,
-     146, 147, 148, 149, 150, 151, 152, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178,
-     179, 180, 181, 182, 183, 184, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210,
-     211, 212, 213, 214, 215, 216, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243,
-     244, 245, 246, 247, 248, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275,
-     276, 277, 278, 279, 280, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308,
-     309, 310, 311, 312, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340,
-     341, 342, 343, 344, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373,
-     374, 375, 376, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405,
-     406, 407, 408, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438,
-     439, 440, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470,
-     471, 472, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503,
-     504, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535,
-     536, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568,
-     583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600,
-     616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 647,
-     648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 676, 677,
-     678, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 701, 702, 703, 704, 705,
-     706, 707, 708, 709, 710, 711, 712, 713, 722, 723, 724, 725, 726, 727, 728, 729, 730, 731,
-     732, 739, 740, 741, 742, 743, 744, 745, 746, 747, 752, 753, 754, 755, 756, 757, 758, 761,
-     762, 763, 764, 765, 766, 767]
+    pixels_precomp = [0, 767]

-    pixels = compute_image_pixels(nside, hdu[0].data.shape, wcs)
-    assert_allclose(pixels, pixels_precomp)
+    pixels = compute_image_pixels(wcs_geometry, nside)
+    assert_allclose([pixels[0], pixels[-1]], pixels_precomp)
diff --git a/hips/utils/wcs.py b/hips/utils/wcs.py
new file mode 100644
index 0000000..45b5541
--- /dev/null
+++ b/hips/utils/wcs.py
@@ -0,0 +1,49 @@
+# Licensed under a 3-clause BSD style license - see LICENSE.rst
+import numpy as np
+from astropy.wcs import WCS
+
+__all__ = [
+    'WCSGeometry',
+]
+
+
+class WCSGeometry:
+    """Container for WCS object and image shape.
+
+    Parameters
+    ----------
+    wcs : `~astropy.wcs.WCS`
+        WCS projection object
+    shape : tuple
+        Shape of the image
+    """
+
+    def __init__(self, wcs: WCS, shape: tuple) -> None:
+        self._wcs = wcs
+        self._shape = shape
+
+    @property
+    def shape(self) -> None:
+        """Shape of the image (`None`)."""
+        return self._shape
+
+    @property
+    def wcs(self) -> None:
+        """WCS object containing FITS image header (`None`)."""
+        return self._wcs
+
+    @classmethod
+    def create(cls, projection: str='AIT', cdelt: float=1.0, crpix: float=1., shape: tuple) -> 'WCSGeometry':
+        """Read from HiPS description file (`WCSGeometry`).
+
+        Parameters
+        ----------
+        projection : `str`
+            Project of the WCS object
+        cdelt : `str`
+        crpix : `str`
+        shape : `tuple`
+            Image shape
+        """
+        # TODO
+        pass

commit 6e2dd380d3d397e08a2e0f79489d10756a876787
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Thu Jun 15 11:02:58 2017 +0500

    Add compute_image_pixels function in hips.utils

diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py
index 36bff9d..0ab2549 100644
--- a/hips/utils/healpix.py
+++ b/hips/utils/healpix.py
@@ -6,11 +6,13 @@ the healpy library
 """

 __all__ = [
-    'boundaries',
+    'boundaries', 'compute_image_pixels'
 ]

 import healpy as hp
 import numpy as np
+from astropy.coordinates.angle_utilities import angular_separation
+from astropy.wcs import WCS


 def boundaries(nside: int, pix: int, nest: bool=True) -> tuple:
@@ -54,3 +56,33 @@ def boundaries(nside: int, pix: int, nest: bool=True) -> tuple:
     boundary_coords = hp.boundaries(nside, pix, nest=nest)
     theta, phi = hp.vec2ang(np.transpose(boundary_coords))
     return theta, phi
+
+def compute_image_pixels(nside: int, shape: tuple, wcs: WCS) -> np.ndarray:
+    """Returns an array containing the pixels corresponding to an image.
+
+    This function calls `healpy.pixelfunc.ang2vec`, `healpy.query_disc`, and
+    `astropy.coordinates.angle_utilities.angular_separation` to compute
+    the pixel values, which will be use in tile drawing.
+
+    Parameters
+    ----------
+    nside : int
+        The nside of the HEALPix map
+    shape : tuple
+        Shape of the image
+    wcs : astropy.wcs.wcs.WCS
+        A WCS object containing the image header
+
+    Returns
+    -------
+    pixels : `numpy.ndarray`
+        Returns a list of pixel values
+
+    """
+
+    y_center, x_center = shape[0] // 2, shape[1] // 2
+    lon_center, lat_center = wcs.all_pix2world(x_center, y_center, 1)
+    vec = hp.ang2vec(lon_center, lat_center, lonlat=True)
+    separations = angular_separation(x_center, y_center, lon_center, lat_center)
+    max_separation = np.nanmax(separations)
+    return hp.query_disc(nside, vec, max_separation)
diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py
index 34373ad..da358dd 100644
--- a/hips/utils/tests/test_healpix.py
+++ b/hips/utils/tests/test_healpix.py
@@ -1,7 +1,10 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 import numpy as np
 import healpy as hp
-from .. healpix import boundaries
+from ..healpix import boundaries
+from ..healpix import compute_image_pixels
+from astropy.io import fits
+from astropy.wcs import WCS
 from astropy.coordinates import SkyCoord
 from numpy.testing import assert_allclose

@@ -22,3 +25,44 @@ def test_boundaries():
     radec_precomp = [[264.375,  258.75,  264.375,  270.],
                      [-24.624318,  -30.,  -35.685335,  -30.]]
     assert_allclose([radec.ra, radec.dec], radec_precomp)
+
+def test_compute_image_pixels():
+    order = 3
+    nside = hp.order2nside(order)
+
+    hdu = fits.open('https://github.com/gammapy/gammapy-extra/blob/master/datasets/catalogs/fermi/gll_psch_v08.fit.gz?raw=true')
+    wcs = WCS(hdu[0].header)
+
+    """
+    These pixel values were obtained for the all-sky image located at:
+    https://github.com/gammapy/gammapy-extra/blob/master/datasets/catalogs/fermi/gll_psch_v08.fit.gz?raw=true
+    """
+    pixels_precomp = \
+    [  0, 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15, 20, 21, 22, 23,
+      24, 25, 26, 27, 28, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 54, 55,
+      56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 77, 78, 79, 80, 81, 82, 83,
+      84, 85, 86, 87, 88, 89, 90, 91, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
+     114, 115, 116, 117, 118, 119, 120, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145,
+     146, 147, 148, 149, 150, 151, 152, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178,
+     179, 180, 181, 182, 183, 184, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210,
+     211, 212, 213, 214, 215, 216, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243,
+     244, 245, 246, 247, 248, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275,
+     276, 277, 278, 279, 280, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308,
+     309, 310, 311, 312, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340,
+     341, 342, 343, 344, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373,
+     374, 375, 376, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405,
+     406, 407, 408, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438,
+     439, 440, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470,
+     471, 472, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503,
+     504, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535,
+     536, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568,
+     583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600,
+     616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 647,
+     648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 676, 677,
+     678, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 701, 702, 703, 704, 705,
+     706, 707, 708, 709, 710, 711, 712, 713, 722, 723, 724, 725, 726, 727, 728, 729, 730, 731,
+     732, 739, 740, 741, 742, 743, 744, 745, 746, 747, 752, 753, 754, 755, 756, 757, 758, 761,
+     762, 763, 764, 765, 766, 767]
+
+    pixels = compute_image_pixels(nside, hdu[0].data.shape, wcs)
+    assert_allclose(pixels, pixels_precomp)

commit d369652b5edc9cda4705d0199a862bd8d226f526
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Mon Jun 12 18:55:36 2017 +0500

    Update class docstring

diff --git a/hips/tiles/description.py b/hips/tiles/description.py
index e270d1d..d58c7ee 100644
--- a/hips/tiles/description.py
+++ b/hips/tiles/description.py
@@ -15,8 +15,8 @@ class HipsDescription:

     Parameters
     ----------
-    properties : str
-        A string containing HiPS tile properties
+    properties : OrderedDict
+        An ordered dictionary containing the HiPS file properties
     """

     def __init__(self, properties: OrderedDict) -> None:

commit a245521987a4971aaa70f842bb045f72512c760e
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Mon Jun 12 11:55:31 2017 +0500

    Update tiles.hipsdescription module

    This commits modifies the classmethod to return the appropriate type, and changes the file reading technique.

diff --git a/hips/tiles/description.py b/hips/tiles/description.py
index a0d7bdb..e270d1d 100644
--- a/hips/tiles/description.py
+++ b/hips/tiles/description.py
@@ -19,9 +19,8 @@ class HipsDescription:
         A string containing HiPS tile properties
     """

-    def __init__(self, properties: str) -> None:
-        super(HipsDescription, self).__init__()
-        self.properties = self.parse_file_properties(properties)
+    def __init__(self, properties: OrderedDict) -> None:
+        self.properties = properties

     @classmethod
     def read_file(cls, filename: str) -> OrderedDict:
@@ -39,8 +38,9 @@ class HipsDescription:
         -------
         dict_properties : OrderedDict
         """
-        file = open(filename, 'r')
-        return cls.parse_file_properties(file.read())
+        with open(filename) as file:
+            text = file.read()
+        return cls.parse_file_properties(text)

     @classmethod
     def parse_file_properties(cls, properties: str) -> OrderedDict:
@@ -58,15 +58,15 @@ class HipsDescription:
         -------
         list_properties : OrderedDict
         """
-        properties = properties.split('\n')
+        properties_lines = properties.split('\n')
         list_properties = []
-        for property in properties:
+        for property in properties_lines:
             key_value = property.split('=')
             try:
                 list_properties.append((key_value[0].strip(), key_value[1].strip()))
-            except IndexError: # The case where a property contains comment or a blank line
+            except IndexError: # the case where a property contains a comment or a blank line
                 pass
-        return OrderedDict(list_properties)
+        return cls(OrderedDict(list_properties))

     @property
     def base_url(self) -> str:
diff --git a/hips/tiles/tests/test_description.py b/hips/tiles/tests/test_description.py
index 75d42d2..5e4a2b2 100644
--- a/hips/tiles/tests/test_description.py
+++ b/hips/tiles/tests/test_description.py
@@ -58,7 +58,7 @@ class TestHiPSDescription:
         isColor            = true
         ~
         """
-        cls.hipsdescription = HipsDescription(hips_properties)
+        cls.hipsdescription = HipsDescription.parse_file_properties(hips_properties)

     def test_base_url(cls):
         assert cls.hipsdescription.base_url == 'http://alasky.u-strasbg.fr/DSS/DSSColor'

commit dae30ac9cff524a0dc64bb638c5e6b62bc4011d4
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 9 22:46:14 2017 +0500

    Update class docstring

diff --git a/hips/tiles/__init__.py b/hips/tiles/__init__.py
index 1c77ba4..d19ff3e 100644
--- a/hips/tiles/__init__.py
+++ b/hips/tiles/__init__.py
@@ -1,3 +1,3 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
-"""HiPS description class -- provides methods for parsing the HiPS file properties."""
+"""Classes and functions to manage HiPS tiles."""
 from .description import *

commit fbf89687c9e5f6dab8b0de8a638abaf433ec6a87
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 9 22:43:58 2017 +0500

    Update tiles.hipsdescription module

    This commits renames the hipsdescription file to description and converts the getters methods to properties. It also fixes some formatting issue.

diff --git a/docs/api.rst b/docs/api.rst
index 46c5c1f..eb152d9 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -2,4 +2,3 @@ Reference/API
 =============

 .. automodapi:: hips
-.. automodapi:: hips.tiles
diff --git a/hips/__init__.py b/hips/__init__.py
index 8d3819e..ce0fdf7 100644
--- a/hips/__init__.py
+++ b/hips/__init__.py
@@ -19,3 +19,4 @@ from ._astropy_init import *
 if not _ASTROPY_SETUP_:
     # For egg_info test builds to pass, put package imports here.
     from .draw import *
+    from .tiles import *
diff --git a/hips/tiles/__init__.py b/hips/tiles/__init__.py
index bb73205..1c77ba4 100644
--- a/hips/tiles/__init__.py
+++ b/hips/tiles/__init__.py
@@ -1,3 +1,3 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 """HiPS description class -- provides methods for parsing the HiPS file properties."""
-from .hipsdescription import *
+from .description import *
diff --git a/hips/tiles/description.py b/hips/tiles/description.py
new file mode 100644
index 0000000..a0d7bdb
--- /dev/null
+++ b/hips/tiles/description.py
@@ -0,0 +1,99 @@
+# Licensed under a 3-clause BSD style license - see LICENSE.rst
+"""Classes and functions to manage HiPS tiles."""
+
+
+from collections import OrderedDict
+
+__all__ = [
+    'HipsDescription',
+]
+
+class HipsDescription:
+    """This class provides methods for parsing the HiPS properties file.
+
+    It also provides multiple getters around the commonly used HiPS properties.
+
+    Parameters
+    ----------
+    properties : str
+        A string containing HiPS tile properties
+    """
+
+    def __init__(self, properties: str) -> None:
+        super(HipsDescription, self).__init__()
+        self.properties = self.parse_file_properties(properties)
+
+    @classmethod
+    def read_file(cls, filename: str) -> OrderedDict:
+        """Reads text from a file and returns an ordered dictionary containing the HiPS file properties.
+
+        This method reads a file given by the parameter and returns an ordered dictionary
+        containing the HiPS file properties by calling the `parse_file_properties` method.
+
+        Parameters
+        ----------
+        filename : str
+            Name of the file containing HiPS file properties
+
+        Returns
+        -------
+        dict_properties : OrderedDict
+        """
+        file = open(filename, 'r')
+        return cls.parse_file_properties(file.read())
+
+    @classmethod
+    def parse_file_properties(cls, properties: str) -> OrderedDict:
+        """Returns an ordered dictionary containing the HiPS file properties.
+
+        This method parses the HiPS file properties and loads it in a dictionary,
+        then is casts it to `collections.OrderedDict` type.
+
+        Parameters
+        ----------
+        properties : str
+            HiPS file properties
+
+        Returns
+        -------
+        list_properties : OrderedDict
+        """
+        properties = properties.split('\n')
+        list_properties = []
+        for property in properties:
+            key_value = property.split('=')
+            try:
+                list_properties.append((key_value[0].strip(), key_value[1].strip()))
+            except IndexError: # The case where a property contains comment or a blank line
+                pass
+        return OrderedDict(list_properties)
+
+    @property
+    def base_url(self) -> str:
+        """Returns the base url from the HiPS file properties (`str`)."""
+        return self.properties['hips_service_url']
+
+    @property
+    def title(self) -> str:
+        """Returns the title from the HiPS file properties (`str`)."""
+        return self.properties['obs_title']
+
+    @property
+    def hips_version(self) -> float:
+        """Returns the HiPS version from the HiPS file properties (`float`)."""
+        return float(self.properties['hips_version'])
+
+    @property
+    def hips_frame(self) -> str:
+        """Returns the HiPS frame from the HiPS file properties (`str`)."""
+        return self.properties['hips_frame']
+
+    @property
+    def hips_order(self) -> int:
+        """Returns the HiPS order from the HiPS file properties (`int`)."""
+        return int(self.properties['hips_order'])
+
+    @property
+    def tile_format(self) -> str:
+        """Returns the HiPS tile format from the HiPS file properties (`str`)."""
+        return self.properties['hips_tile_format']
diff --git a/hips/tiles/hipsdescription.py b/hips/tiles/hipsdescription.py
deleted file mode 100644
index f13624a..0000000
--- a/hips/tiles/hipsdescription.py
+++ /dev/null
@@ -1,75 +0,0 @@
-# Licensed under a 3-clause BSD style license - see LICENSE.rst
-"""HiPS description class -- provides methods for parsing the HiPS file properties."""
-
-__all__ = [
-    'HipsDescription',
-]
-
-from collections import OrderedDict
-
-
-class HipsDescription:
-    """This class provides methods for parsing the HiPS properties file.
-       It also provides multiple getters around the commonly used properties.
-    """
-
-    def __init__(self, properties: str) -> None:
-        super(HipsDescription, self).__init__()
-        self.properties = self.parse_file_properties(properties)
-
-    @classmethod
-    def parse_file_properties(cls, properties: str) -> OrderedDict:
-        """Returns an ordered dictionary containing the HiPS file properties.
-
-        This method parses the HiPS file properties and loads it in a dictionary,
-        then is casts it to `collections.OrderedDict` type.
-
-        Parameters
-        ----------
-        properties : str
-            HiPS file properties
-        
-        Returns
-        -------
-        dict_properties : OrderedDict
-        """
-        properties = properties.split('\n')
-        dict_properties = {}
-        for property in properties:
-            key_value = property.split('=')
-            try:
-                dict_properties[key_value[0].strip()] = key_value[1].strip()
-            except:
-                pass
-        return OrderedDict(dict_properties)
-
-    def get_properties(self) -> OrderedDict:
-        """OrderedDict: Returns the ordered dictionary containing the HiPS properties."""
-        return self.properties
-
-    def get_base_url(self) -> str:
-        """str: Returns the base url from the HiPS file properties."""
-        try:
-            return self.properties['hips_service_url']
-        except:
-            return None
-
-    def get_title(self) -> str:
-        """str: Returns the title from the HiPS file properties."""
-        return self.properties['obs_title']
-
-    def get_hips_version(self) -> float:
-        """float: Returns the HiPS version from the HiPS file properties."""
-        return float(self.properties['hips_version'])
-
-    def get_hips_frame(self) -> str:
-        """str: Returns the HiPS frame from the HiPS file properties."""
-        return self.properties['hips_frame']
-
-    def get_hips_order(self) -> int:
-        """int: Returns the HiPS order from the HiPS file properties."""
-        return int(self.properties['hips_order'])
-
-    def get_tile_format(self) -> str:
-        """str: Returns the HiPS tile format from the HiPS file properties."""
-        return self.properties['hips_tile_format']
\ No newline at end of file
diff --git a/hips/tiles/tests/test_description.py b/hips/tiles/tests/test_description.py
new file mode 100644
index 0000000..75d42d2
--- /dev/null
+++ b/hips/tiles/tests/test_description.py
@@ -0,0 +1,79 @@
+# Licensed under a 3-clause BSD style license - see LICENSE.rst
+
+from ..description import HipsDescription
+
+class TestHiPSDescription:
+    @classmethod
+    def setup_class(cls):
+        # These HiPS properties were obtained from: http://alasky.u-strasbg.fr/DSS/DSSColor/properties
+        hips_properties = """
+        creator_did          = ivo://CDS/P/DSS2/color
+        obs_collection       = DSS colored
+        obs_title            = DSS colored
+        obs_description      = Color composition generated by CDS. This HiPS survey is based on 2 others HiPS surveys, respectively DSS2-red and DSS2-blue HiPS, both of them directly generated from original scanned plates downloaded from STScI site. The red component has been built from POSS-II F, AAO-SES,SR and SERC-ER plates. The blue component has been build from POSS-II J and SERC-J,EJ. The green component is based on the mean of other components. Three missing plates from red survey (253, 260, 359) has been replaced by pixels from the DSSColor STScI jpeg survey. The 11 missing blue plates (mainly in galactic plane) have not been replaced (only red component).
+        obs_copyright        = Digitized Sky Survey - STScI/NASA, Colored & Healpixed by CDS
+        obs_copyright_url    = http://archive.stsci.edu/dss/acknowledging.html
+        client_category      = Image/Optical/DSS
+        client_sort_key      = 03-00
+        hips_builder         = Aladin/HipsGen v9.039
+        hips_creation_date   = 2010-05-01T19:05Z
+        hips_release_date    = 2015-05-11T08:45Z
+        # hips_release_date    = 2016-12-13T14:51Z
+        hips_creator         = CDS (A.Oberto, P.Fernique)
+        hips_version         = 1.31
+        hips_order           = 9
+        hips_frame           = equatorial
+        hips_tile_width      = 512
+        hips_tile_format     = jpeg
+        dataproduct_type     = image
+        client_application   = AladinLite
+        moc_access_url       = http://alasky.u-strasbg.fr/DSS/DSSColor/Moc.fits
+        hips_service_url     = http://alasky.u-strasbg.fr/DSS/DSSColor
+        hips_status          = public master clonableOnce
+        hips_rgb_red         = DSS2Merged [1488.0 8488.8125 14666.0 Linear]
+        hips_rgb_blue        = DSS2-blue-XJ-S [4286.0 12122.5 19959.0 Linear]
+        hips_hierarchy       = median
+        hips_pixel_scale     = 2.236E-4
+        hips_initial_ra      = 085.30251
+        hips_initial_dec     = -02.25468
+        hips_initial_fov     = 2
+        moc_sky_fraction     = 1
+        dataproduct_subtype  = color
+        hips_copyright       = CNRS/Unistra
+        obs_ack              = The Digitized Sky Surveys were produced at the Space Telescope Science Institute under U.S. Government grant NAG W-2166. The images of these surveys are based on photographic data obtained using the Oschin Schmidt Telescope on Palomar Mountain and the UK Schmidt Telescope. The plates were processed into the present compressed digital form with the permission of these institutions
+        prov_progenitor      = STScI
+        bib_reference        = 2008AJ....136..735L
+        bib_reference_url    = http://simbad.u-strasbg.fr/simbad/sim-ref?bibcode=2008AJ....136..735L
+        # 1975-1999
+        t_min                = 42413
+        t_max                = 51179
+        obs_regime           = Optical
+        # Bandpass  422-967 THz
+        em_min               = 7.104086682464e-7
+        em_max               = 3.100232244054e-7
+        # hips_master_url     = ex: http://yourHipsServer/null
+        # For compatibility
+        label              = DSS colored
+        coordsys           = C
+        isColor            = true
+        ~
+        """
+        cls.hipsdescription = HipsDescription(hips_properties)
+
+    def test_base_url(cls):
+        assert cls.hipsdescription.base_url == 'http://alasky.u-strasbg.fr/DSS/DSSColor'
+
+    def test_title(cls):
+        assert cls.hipsdescription.title == 'DSS colored'
+
+    def test_hips_version(cls):
+        assert cls.hipsdescription.hips_version == 1.31
+
+    def test_hips_frame(cls):
+        assert cls.hipsdescription.hips_frame == 'equatorial'
+
+    def test_hips_order(cls):
+        assert cls.hipsdescription.hips_order == 9
+
+    def test_tile_format(cls):
+        assert cls.hipsdescription.tile_format == 'jpeg'
diff --git a/hips/tiles/tests/test_hipsdescription.py b/hips/tiles/tests/test_hipsdescription.py
deleted file mode 100644
index 458e976..0000000
--- a/hips/tiles/tests/test_hipsdescription.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# Licensed under a 3-clause BSD style license - see LICENSE.rst
-
-from .. hipsdescription import HipsDescription
-
-class TestHiPSDescription:
-    @classmethod
-    def setup_class(cls):
-        # These HiPS properties were obtained from: http://alasky.u-strasbg.fr/DSS/DSSColor/properties
-        hips_properties = """
-        creator_did          = ivo://CDS/P/DSS2/color
-        obs_collection       = DSS colored
-        obs_title            = DSS colored
-        obs_description      = Color composition generated by CDS. This HiPS survey is based on 2 others HiPS surveys, respectively DSS2-red and DSS2-blue HiPS, both of them directly generated from original scanned plates downloaded from STScI site. The red component has been built from POSS-II F, AAO-SES,SR and SERC-ER plates. The blue component has been build from POSS-II J and SERC-J,EJ. The green component is based on the mean of other components. Three missing plates from red survey (253, 260, 359) has been replaced by pixels from the DSSColor STScI jpeg survey. The 11 missing blue plates (mainly in galactic plane) have not been replaced (only red component).
-        obs_copyright        = Digitized Sky Survey - STScI/NASA, Colored & Healpixed by CDS
-        obs_copyright_url    = http://archive.stsci.edu/dss/acknowledging.html
-        client_category      = Image/Optical/DSS
-        client_sort_key      = 03-00
-        hips_builder         = Aladin/HipsGen v9.039
-        hips_creation_date   = 2010-05-01T19:05Z
-        hips_release_date    = 2015-05-11T08:45Z
-        # hips_release_date    = 2016-12-13T14:51Z
-        hips_creator         = CDS (A.Oberto, P.Fernique)
-        hips_version         = 1.31
-        hips_order           = 9
-        hips_frame           = equatorial
-        hips_tile_width      = 512
-        hips_tile_format     = jpeg
-        dataproduct_type     = image
-        client_application   = AladinLite
-        moc_access_url       = http://alasky.u-strasbg.fr/DSS/DSSColor/Moc.fits
-        hips_service_url     = http://alasky.u-strasbg.fr/DSS/DSSColor
-        hips_status          = public master clonableOnce
-        hips_rgb_red         = DSS2Merged [1488.0 8488.8125 14666.0 Linear]
-        hips_rgb_blue        = DSS2-blue-XJ-S [4286.0 12122.5 19959.0 Linear]
-        hips_hierarchy       = median
-        hips_pixel_scale     = 2.236E-4
-        hips_initial_ra      = 085.30251
-        hips_initial_dec     = -02.25468
-        hips_initial_fov     = 2
-        moc_sky_fraction     = 1
-        dataproduct_subtype  = color
-        hips_copyright       = CNRS/Unistra
-        obs_ack              = The Digitized Sky Surveys were produced at the Space Telescope Science Institute under U.S. Government grant NAG W-2166. The images of these surveys are based on photographic data obtained using the Oschin Schmidt Telescope on Palomar Mountain and the UK Schmidt Telescope. The plates were processed into the present compressed digital form with the permission of these institutions
-        prov_progenitor      = STScI
-        bib_reference        = 2008AJ....136..735L
-        bib_reference_url    = http://simbad.u-strasbg.fr/simbad/sim-ref?bibcode=2008AJ....136..735L
-        # 1975-1999
-        t_min                = 42413
-        t_max                = 51179
-        obs_regime           = Optical
-        # Bandpass  422-967 THz
-        em_min               = 7.104086682464e-7
-        em_max               = 3.100232244054e-7
-        # hips_master_url     = ex: http://yourHipsServer/null
-        # For compatibility
-        label              = DSS colored
-        coordsys           = C
-        isColor            = true
-        ~
-        """
-        cls.hipsdescription = HipsDescription(hips_properties)
-
-    def test_get_base_url(cls):
-        assert cls.hipsdescription.get_base_url() == 'http://alasky.u-strasbg.fr/DSS/DSSColor'
-
-    def test_get_title(cls):
-        assert cls.hipsdescription.get_title() == 'DSS colored'
-
-    def test_get_hips_version(cls):
-        assert cls.hipsdescription.get_hips_version() == 1.31
-
-    def test_get_hips_frame(cls):
-        assert cls.hipsdescription.get_hips_frame() == 'equatorial'
-
-    def test_get_hips_order(cls):
-        assert cls.hipsdescription.get_hips_order() == 9
-
-    def test_get_tile_format(cls):
-        assert cls.hipsdescription.get_tile_format() == 'jpeg'
\ No newline at end of file

commit 69a91f0a35369ccd018990746b2f830d0ecb74d8
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 9 15:06:30 2017 +0500

    Remove whitespace

diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py
index a104bdc..36bff9d 100644
--- a/hips/utils/healpix.py
+++ b/hips/utils/healpix.py
@@ -18,7 +18,7 @@ def boundaries(nside: int, pix: int, nest: bool=True) -> tuple:

     This function calls `healpy.boundaries` and `healpy.pixelfunc.vec2ang`
     and computes the four corners of a HiPS tile. The order of the returned
-    corners is: N, W, S, E where N (resp. W, S, E) is the corner roughly 
+    corners is: N, W, S, E where N (resp. W, S, E) is the corner roughly
     pointing towards the North (resp. West, South and East).

     Parameters

commit 8c235c2ebe5861aa2ea213ff4fe0f5d7f7c89d99
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 9 14:05:56 2017 +0500

    Update docstring

    This commit updates the docstring to show the order of the returned corners.

diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py
index 6ea5681..a104bdc 100644
--- a/hips/utils/healpix.py
+++ b/hips/utils/healpix.py
@@ -13,10 +13,13 @@ import healpy as hp
 import numpy as np


-def boundaries(nside: int, pix: int, nest: bool=False) -> tuple:
+def boundaries(nside: int, pix: int, nest: bool=True) -> tuple:
     """Returns an array containing the angle (theta and phi) in radians.

-    This function calls `healpy.boundaries` and `healpy.vec2ang`.
+    This function calls `healpy.boundaries` and `healpy.pixelfunc.vec2ang`
+    and computes the four corners of a HiPS tile. The order of the returned
+    corners is: N, W, S, E where N (resp. W, S, E) is the corner roughly 
+    pointing towards the North (resp. West, South and East).

     Parameters
     ----------
@@ -41,7 +44,7 @@ def boundaries(nside: int, pix: int, nest: bool=False) -> tuple:
         >>> from astropy.coordinates import SkyCoord
         >>> nside = 8
         >>> pix = 450
-        >>> theta, phi = boundaries(nside, pix, nest=True)
+        >>> theta, phi = boundaries(nside, pix)
         >>> SkyCoord(ra=phi, dec=np.pi/2 - theta, unit='radian', frame='icrs')
         <SkyCoord (ICRS): (ra, dec) in deg
         [( 264.375, -24.62431835), ( 258.75 , -30.        ),
diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py
index 7606819..34373ad 100644
--- a/hips/utils/tests/test_healpix.py
+++ b/hips/utils/tests/test_healpix.py
@@ -10,7 +10,7 @@ def test_boundaries():
     order = 3
     nside = hp.order2nside(order)
     pix = 450
-    theta, phi = boundaries(nside, pix, nest=True)
+    theta, phi = boundaries(nside, pix)

     radec = SkyCoord(ra=phi, dec=np.pi/2 - theta, unit='radian', frame='icrs')


commit 45b4117c3e85bb6fac72aeb790dfa483c03f0b12
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 9 13:14:04 2017 +0500

    Add entry in api.rst

    This commit fixes some formatting issues and adds an entry for the
    tiles.hipsdescription module in api.rst file.

diff --git a/docs/api.rst b/docs/api.rst
index eb152d9..46c5c1f 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -2,3 +2,4 @@ Reference/API
 =============

 .. automodapi:: hips
+.. automodapi:: hips.tiles
diff --git a/hips/tiles/hipsdescription.py b/hips/tiles/hipsdescription.py
index 8c81957..f13624a 100644
--- a/hips/tiles/hipsdescription.py
+++ b/hips/tiles/hipsdescription.py
@@ -9,8 +9,8 @@ from collections import OrderedDict


 class HipsDescription:
-    """This class provides method for parsing the HiPS properties file.
-       It also provides various getters around the commonly used properties.
+    """This class provides methods for parsing the HiPS properties file.
+       It also provides multiple getters around the commonly used properties.
     """

     def __init__(self, properties: str) -> None:
@@ -28,6 +28,7 @@ class HipsDescription:
         ----------
         properties : str
             HiPS file properties
+        
         Returns
         -------
         dict_properties : OrderedDict
@@ -46,7 +47,7 @@ class HipsDescription:
         """OrderedDict: Returns the ordered dictionary containing the HiPS properties."""
         return self.properties

-    def get_base_url(self) -> [str, None]:
+    def get_base_url(self) -> str:
         """str: Returns the base url from the HiPS file properties."""
         try:
             return self.properties['hips_service_url']

commit ff3299abeb476ffb1937b6647f6bdae26d659e34
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 9 12:16:52 2017 +0500

    Add tiles.hipsdescription module

    This commits adds a class named HipsDescription which provides methods for parsing the HiPS properties.
    It parses the given string and returns an object of OrderedDict. Additional getter methods are also included.
    For the test cases, a sample properties file was obtained from http://alasky.unistra.fr/DSS/DSSColor/properties

diff --git a/hips/tiles/__init__.py b/hips/tiles/__init__.py
new file mode 100644
index 0000000..bb73205
--- /dev/null
+++ b/hips/tiles/__init__.py
@@ -0,0 +1,3 @@
+# Licensed under a 3-clause BSD style license - see LICENSE.rst
+"""HiPS description class -- provides methods for parsing the HiPS file properties."""
+from .hipsdescription import *
diff --git a/hips/tiles/hipsdescription.py b/hips/tiles/hipsdescription.py
new file mode 100644
index 0000000..8c81957
--- /dev/null
+++ b/hips/tiles/hipsdescription.py
@@ -0,0 +1,74 @@
+# Licensed under a 3-clause BSD style license - see LICENSE.rst
+"""HiPS description class -- provides methods for parsing the HiPS file properties."""
+
+__all__ = [
+    'HipsDescription',
+]
+
+from collections import OrderedDict
+
+
+class HipsDescription:
+    """This class provides method for parsing the HiPS properties file.
+       It also provides various getters around the commonly used properties.
+    """
+
+    def __init__(self, properties: str) -> None:
+        super(HipsDescription, self).__init__()
+        self.properties = self.parse_file_properties(properties)
+
+    @classmethod
+    def parse_file_properties(cls, properties: str) -> OrderedDict:
+        """Returns an ordered dictionary containing the HiPS file properties.
+
+        This method parses the HiPS file properties and loads it in a dictionary,
+        then is casts it to `collections.OrderedDict` type.
+
+        Parameters
+        ----------
+        properties : str
+            HiPS file properties
+        Returns
+        -------
+        dict_properties : OrderedDict
+        """
+        properties = properties.split('\n')
+        dict_properties = {}
+        for property in properties:
+            key_value = property.split('=')
+            try:
+                dict_properties[key_value[0].strip()] = key_value[1].strip()
+            except:
+                pass
+        return OrderedDict(dict_properties)
+
+    def get_properties(self) -> OrderedDict:
+        """OrderedDict: Returns the ordered dictionary containing the HiPS properties."""
+        return self.properties
+
+    def get_base_url(self) -> [str, None]:
+        """str: Returns the base url from the HiPS file properties."""
+        try:
+            return self.properties['hips_service_url']
+        except:
+            return None
+
+    def get_title(self) -> str:
+        """str: Returns the title from the HiPS file properties."""
+        return self.properties['obs_title']
+
+    def get_hips_version(self) -> float:
+        """float: Returns the HiPS version from the HiPS file properties."""
+        return float(self.properties['hips_version'])
+
+    def get_hips_frame(self) -> str:
+        """str: Returns the HiPS frame from the HiPS file properties."""
+        return self.properties['hips_frame']
+
+    def get_hips_order(self) -> int:
+        """int: Returns the HiPS order from the HiPS file properties."""
+        return int(self.properties['hips_order'])
+
+    def get_tile_format(self) -> str:
+        """str: Returns the HiPS tile format from the HiPS file properties."""
+        return self.properties['hips_tile_format']
\ No newline at end of file
diff --git a/hips/tiles/tests/__init__.py b/hips/tiles/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/hips/tiles/tests/test_hipsdescription.py b/hips/tiles/tests/test_hipsdescription.py
new file mode 100644
index 0000000..458e976
--- /dev/null
+++ b/hips/tiles/tests/test_hipsdescription.py
@@ -0,0 +1,79 @@
+# Licensed under a 3-clause BSD style license - see LICENSE.rst
+
+from .. hipsdescription import HipsDescription
+
+class TestHiPSDescription:
+    @classmethod
+    def setup_class(cls):
+        # These HiPS properties were obtained from: http://alasky.u-strasbg.fr/DSS/DSSColor/properties
+        hips_properties = """
+        creator_did          = ivo://CDS/P/DSS2/color
+        obs_collection       = DSS colored
+        obs_title            = DSS colored
+        obs_description      = Color composition generated by CDS. This HiPS survey is based on 2 others HiPS surveys, respectively DSS2-red and DSS2-blue HiPS, both of them directly generated from original scanned plates downloaded from STScI site. The red component has been built from POSS-II F, AAO-SES,SR and SERC-ER plates. The blue component has been build from POSS-II J and SERC-J,EJ. The green component is based on the mean of other components. Three missing plates from red survey (253, 260, 359) has been replaced by pixels from the DSSColor STScI jpeg survey. The 11 missing blue plates (mainly in galactic plane) have not been replaced (only red component).
+        obs_copyright        = Digitized Sky Survey - STScI/NASA, Colored & Healpixed by CDS
+        obs_copyright_url    = http://archive.stsci.edu/dss/acknowledging.html
+        client_category      = Image/Optical/DSS
+        client_sort_key      = 03-00
+        hips_builder         = Aladin/HipsGen v9.039
+        hips_creation_date   = 2010-05-01T19:05Z
+        hips_release_date    = 2015-05-11T08:45Z
+        # hips_release_date    = 2016-12-13T14:51Z
+        hips_creator         = CDS (A.Oberto, P.Fernique)
+        hips_version         = 1.31
+        hips_order           = 9
+        hips_frame           = equatorial
+        hips_tile_width      = 512
+        hips_tile_format     = jpeg
+        dataproduct_type     = image
+        client_application   = AladinLite
+        moc_access_url       = http://alasky.u-strasbg.fr/DSS/DSSColor/Moc.fits
+        hips_service_url     = http://alasky.u-strasbg.fr/DSS/DSSColor
+        hips_status          = public master clonableOnce
+        hips_rgb_red         = DSS2Merged [1488.0 8488.8125 14666.0 Linear]
+        hips_rgb_blue        = DSS2-blue-XJ-S [4286.0 12122.5 19959.0 Linear]
+        hips_hierarchy       = median
+        hips_pixel_scale     = 2.236E-4
+        hips_initial_ra      = 085.30251
+        hips_initial_dec     = -02.25468
+        hips_initial_fov     = 2
+        moc_sky_fraction     = 1
+        dataproduct_subtype  = color
+        hips_copyright       = CNRS/Unistra
+        obs_ack              = The Digitized Sky Surveys were produced at the Space Telescope Science Institute under U.S. Government grant NAG W-2166. The images of these surveys are based on photographic data obtained using the Oschin Schmidt Telescope on Palomar Mountain and the UK Schmidt Telescope. The plates were processed into the present compressed digital form with the permission of these institutions
+        prov_progenitor      = STScI
+        bib_reference        = 2008AJ....136..735L
+        bib_reference_url    = http://simbad.u-strasbg.fr/simbad/sim-ref?bibcode=2008AJ....136..735L
+        # 1975-1999
+        t_min                = 42413
+        t_max                = 51179
+        obs_regime           = Optical
+        # Bandpass  422-967 THz
+        em_min               = 7.104086682464e-7
+        em_max               = 3.100232244054e-7
+        # hips_master_url     = ex: http://yourHipsServer/null
+        # For compatibility
+        label              = DSS colored
+        coordsys           = C
+        isColor            = true
+        ~
+        """
+        cls.hipsdescription = HipsDescription(hips_properties)
+
+    def test_get_base_url(cls):
+        assert cls.hipsdescription.get_base_url() == 'http://alasky.u-strasbg.fr/DSS/DSSColor'
+
+    def test_get_title(cls):
+        assert cls.hipsdescription.get_title() == 'DSS colored'
+
+    def test_get_hips_version(cls):
+        assert cls.hipsdescription.get_hips_version() == 1.31
+
+    def test_get_hips_frame(cls):
+        assert cls.hipsdescription.get_hips_frame() == 'equatorial'
+
+    def test_get_hips_order(cls):
+        assert cls.hipsdescription.get_hips_order() == 9
+
+    def test_get_tile_format(cls):
+        assert cls.hipsdescription.get_tile_format() == 'jpeg'
\ No newline at end of file

commit 0b1359036a1436551234dd21ce47149c533c1bea
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Wed Jun 7 14:12:46 2017 +0500

    Update utils.healpix module

    This commit addresses import conventions and also some formatting issues. An example was also added and the test case was updated to follow this issue: https://github.com/healpy/healpy/issues/393#issuecomment-305994042.

diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py
index f9a86e6..6ea5681 100644
--- a/hips/utils/healpix.py
+++ b/hips/utils/healpix.py
@@ -9,14 +9,14 @@ __all__ = [
     'boundaries',
 ]

-import healpy
+import healpy as hp
 import numpy as np


 def boundaries(nside: int, pix: int, nest: bool=False) -> tuple:
     """Returns an array containing the angle (theta and phi) in radians.

-    This method calls `healpy.boundaries` and `healpy.vec2ang`.
+    This function calls `healpy.boundaries` and `healpy.vec2ang`.

     Parameters
     ----------
@@ -33,14 +33,21 @@ def boundaries(nside: int, pix: int, nest: bool=False) -> tuple:
         Returns the angle (theta and phi) in radians

     Examples
-    -------
-    nside = 8
-    pix = 450
-    theta, phi = boundaries(nside, pix, nest=True)
-
-    radec = SkyCoord(ra=phi, dec=np.pi/2 - theta, unit='radian', frame='icrs')
+    --------
+    ::
+
+        >>> import numpy as np
+        >>> from hips.utils import boundaries
+        >>> from astropy.coordinates import SkyCoord
+        >>> nside = 8
+        >>> pix = 450
+        >>> theta, phi = boundaries(nside, pix, nest=True)
+        >>> SkyCoord(ra=phi, dec=np.pi/2 - theta, unit='radian', frame='icrs')
+        <SkyCoord (ICRS): (ra, dec) in deg
+        [( 264.375, -24.62431835), ( 258.75 , -30.        ),
+         ( 264.375, -35.68533471), ( 270.   , -30.        )]>
     """

-    boundary_coords = healpy.boundaries(nside, pix, nest=nest)
-    theta, phi = healpy.vec2ang(np.transpose(boundary_coords))
+    boundary_coords = hp.boundaries(nside, pix, nest=nest)
+    theta, phi = hp.vec2ang(np.transpose(boundary_coords))
     return theta, phi
diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py
index 93056e4..7606819 100644
--- a/hips/utils/tests/test_healpix.py
+++ b/hips/utils/tests/test_healpix.py
@@ -1,15 +1,24 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
+import numpy as np
+import healpy as hp
 from .. healpix import boundaries
 from astropy.coordinates import SkyCoord
-import numpy as np
+from numpy.testing import assert_allclose


 def test_boundaries():
-    nside = 8
+    order = 3
+    nside = hp.order2nside(order)
     pix = 450
     theta, phi = boundaries(nside, pix, nest=True)

     radec = SkyCoord(ra=phi, dec=np.pi/2 - theta, unit='radian', frame='icrs')
+
+    """
+    These HEALPix corner values were verified through Aladin Lite with the "Show
+    healpix grid" option turned on. More information can be found on this GitHub
+    issue: https://github.com/healpy/healpy/issues/393#issuecomment-305994042
+    """
     radec_precomp = [[264.375,  258.75,  264.375,  270.],
                      [-24.624318,  -30.,  -35.685335,  -30.]]
-    np.testing.assert_allclose([radec.ra, radec.dec], radec_precomp)
+    assert_allclose([radec.ra, radec.dec], radec_precomp)

commit fa832132c016e7285d24abe58d1438fff27e649b
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Tue Jun 6 22:34:14 2017 +0500

    Add example

diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py
index 5ebe49a..f9a86e6 100644
--- a/hips/utils/healpix.py
+++ b/hips/utils/healpix.py
@@ -31,6 +31,14 @@ def boundaries(nside: int, pix: int, nest: bool=False) -> tuple:
     -------
     theta, phi : float, array
         Returns the angle (theta and phi) in radians
+
+    Examples
+    -------
+    nside = 8
+    pix = 450
+    theta, phi = boundaries(nside, pix, nest=True)
+
+    radec = SkyCoord(ra=phi, dec=np.pi/2 - theta, unit='radian', frame='icrs')
     """

     boundary_coords = healpy.boundaries(nside, pix, nest=nest)

commit 5de3e05971f7570eeb4402a266e8918b14d9e9ca
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Tue Jun 6 22:30:41 2017 +0500

    Update test case

diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py
index 7f63ebc..93056e4 100644
--- a/hips/utils/tests/test_healpix.py
+++ b/hips/utils/tests/test_healpix.py
@@ -1,5 +1,6 @@
 # Licensed under a 3-clause BSD style license - see LICENSE.rst
 from .. healpix import boundaries
+from astropy.coordinates import SkyCoord
 import numpy as np


@@ -8,7 +9,7 @@ def test_boundaries():
     pix = 450
     theta, phi = boundaries(nside, pix, nest=True)

-    thetaphi_precomp = ([[2.00057176,  2.0943951,  2.19362291,  2.0943951],
-                         [4.61421421,  4.51603944,  4.61421421,  4.71238898]])
-
-    np.testing.assert_allclose([theta, phi], thetaphi_precomp)
+    radec = SkyCoord(ra=phi, dec=np.pi/2 - theta, unit='radian', frame='icrs')
+    radec_precomp = [[264.375,  258.75,  264.375,  270.],
+                     [-24.624318,  -30.,  -35.685335,  -30.]]
+    np.testing.assert_allclose([radec.ra, radec.dec], radec_precomp)

commit aca32b631e949222e88bfc48ff25b1ecaa28f3cd
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Tue Jun 6 22:17:09 2017 +0500

    Update formatting

diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py
index 30996fa..5ebe49a 100644
--- a/hips/utils/healpix.py
+++ b/hips/utils/healpix.py
@@ -15,21 +15,17 @@ import numpy as np

 def boundaries(nside: int, pix: int, nest: bool=False) -> tuple:
     """Returns an array containing the angle (theta and phi) in radians.
-    This method calls :py:meth:`.healpy.boundaries` and :py:meth:`.healpy.vec2ang`
-    
-    Parameters
-    ----------
-        nside : int
-
-            The nside of the HEALPix map
-
-        pix : int

-            Pixel identifier
+    This method calls `healpy.boundaries` and `healpy.vec2ang`.

-        nest : bool, optional
-
-            If True, assume NESTED pixel ordering, otherwise, RING pixel ordering
+    Parameters
+    ----------
+    nside : int
+        The nside of the HEALPix map
+    pix : int
+        Pixel identifier
+    nest : bool, optional
+        If True, assume NESTED pixel ordering, otherwise, RING pixel ordering

     Returns
     -------

commit 9da1f1fd3e79a552839bff00fc814396dcba7920
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Tue Jun 6 14:48:28 2017 +0500

    Add newline

diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py
index 2cd3c73..30996fa 100644
--- a/hips/utils/healpix.py
+++ b/hips/utils/healpix.py
@@ -16,6 +16,7 @@ import numpy as np
 def boundaries(nside: int, pix: int, nest: bool=False) -> tuple:
     """Returns an array containing the angle (theta and phi) in radians.
     This method calls :py:meth:`.healpy.boundaries` and :py:meth:`.healpy.vec2ang`
+    
     Parameters
     ----------
         nside : int

commit bbb48be4b0c77180fa34de83a2b6a3614639e3c1
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Tue Jun 6 14:43:01 2017 +0500

    Update utils.healpix module

diff --git a/docs/api.rst b/docs/api.rst
index eb152d9..0b198d9 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -2,3 +2,4 @@ Reference/API
 =============

 .. automodapi:: hips
+.. automodapi:: hips.utils
diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py
index 8e85569..2cd3c73 100644
--- a/hips/utils/healpix.py
+++ b/hips/utils/healpix.py
@@ -10,11 +10,12 @@ __all__ = [
 ]

 import healpy
+import numpy as np


-def boundaries(nside, pix, step=1, nest=False):
-    """Returns an array containing the longitude and latitude.
-
+def boundaries(nside: int, pix: int, nest: bool=False) -> tuple:
+    """Returns an array containing the angle (theta and phi) in radians.
+    This method calls :py:meth:`.healpy.boundaries` and :py:meth:`.healpy.vec2ang`
     Parameters
     ----------
         nside : int
@@ -25,20 +26,16 @@ def boundaries(nside, pix, step=1, nest=False):

             Pixel identifier

-        step : int, optional
-
-            Number of elements for each side of the pixel
-
         nest : bool, optional

             If True, assume NESTED pixel ordering, otherwise, RING pixel ordering

     Returns
     -------
-    longitude, latitude : float, array
-        Longitude and latitude positions
+    theta, phi : float, array
+        Returns the angle (theta and phi) in radians
     """

     boundary_coords = healpy.boundaries(nside, pix, nest=nest)
-    lon, lat = healpy.vec2ang(boundary_coords, lonlat=True)
-    return [lon, lat]
+    theta, phi = healpy.vec2ang(np.transpose(boundary_coords))
+    return theta, phi
diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py
index 82b2223..7f63ebc 100644
--- a/hips/utils/tests/test_healpix.py
+++ b/hips/utils/tests/test_healpix.py
@@ -6,8 +6,9 @@ import numpy as np
 def test_boundaries():
     nside = 8
     pix = 450
-    lon = boundaries(nside, pix, nest=True)
-    lonlat_precomp = [[242.19350089, 270., 226.97382512, 229.39870535],
-                      [-22.6263803, -43.1943471, -19.37793463, -33.05573115]]
+    theta, phi = boundaries(nside, pix, nest=True)

-    np.testing.assert_array_almost_equal(lon, lonlat_precomp, decimal=8)
+    thetaphi_precomp = ([[2.00057176,  2.0943951,  2.19362291,  2.0943951],
+                         [4.61421421,  4.51603944,  4.61421421,  4.71238898]])
+
+    np.testing.assert_allclose([theta, phi], thetaphi_precomp)

commit e4c00b949e0ca03f06c115927256f0539cdde9ad
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Sun Jun 4 19:47:11 2017 +0500

    Add utils.healpix module

diff --git a/hips/utils/__init__.py b/hips/utils/__init__.py
index d3f4517..ed56631 100644
--- a/hips/utils/__init__.py
+++ b/hips/utils/__init__.py
@@ -2,3 +2,4 @@

 # This sub-module is destined for common non-package specific utility
 # functions.
+from .healpix import *
diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py
new file mode 100644
index 0000000..8e85569
--- /dev/null
+++ b/hips/utils/healpix.py
@@ -0,0 +1,44 @@
+# Licensed under a 3-clause BSD style license - see LICENSE.rst
+"""HEALpy wrapper functions.
+
+This module contains wrapper functions around HEALPix utilizing
+the healpy library
+"""
+
+__all__ = [
+    'boundaries',
+]
+
+import healpy
+
+
+def boundaries(nside, pix, step=1, nest=False):
+    """Returns an array containing the longitude and latitude.
+
+    Parameters
+    ----------
+        nside : int
+
+            The nside of the HEALPix map
+
+        pix : int
+
+            Pixel identifier
+
+        step : int, optional
+
+            Number of elements for each side of the pixel
+
+        nest : bool, optional
+
+            If True, assume NESTED pixel ordering, otherwise, RING pixel ordering
+
+    Returns
+    -------
+    longitude, latitude : float, array
+        Longitude and latitude positions
+    """
+
+    boundary_coords = healpy.boundaries(nside, pix, nest=nest)
+    lon, lat = healpy.vec2ang(boundary_coords, lonlat=True)
+    return [lon, lat]
diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py
new file mode 100644
index 0000000..82b2223
--- /dev/null
+++ b/hips/utils/tests/test_healpix.py
@@ -0,0 +1,13 @@
+# Licensed under a 3-clause BSD style license - see LICENSE.rst
+from .. healpix import boundaries
+import numpy as np
+
+
+def test_boundaries():
+    nside = 8
+    pix = 450
+    lon = boundaries(nside, pix, nest=True)
+    lonlat_precomp = [[242.19350089, 270., 226.97382512, 229.39870535],
+                      [-22.6263803, -43.1943471, -19.37793463, -33.05573115]]
+
+    np.testing.assert_array_almost_equal(lon, lonlat_precomp, decimal=8)

commit 53a607b4fa0031fe3fedd0b6bf89ba03436d715a
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Fri Jun 2 11:38:40 2017 +0500

    Update Sphinx docs

diff --git a/docs/index.rst b/docs/index.rst
index 3e75ee2..3ba6afe 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -4,8 +4,11 @@
 Python HiPS package documentation
 #################################

-.. warning::
-
+.. note::
+ 
+    This package is being developed as part of Google Summer of Code 2017 program. The progress 
+    of this package will be updated on this `blog <https://adl1995.github.io>`_ and the student's
+    application can be found `here <https://github.com/adl1995/HIPS-to-Py/blob/master/documents/application.md>`_.
     This ``hips`` package is in a very early stage of development.
     We started to work on it in June 2017 and expect to have a first v0.1 release in summer 2017.
     That said, please have a look and try to use it for your applications.

commit c7224fe181a18d041513973f043337e3d5af33fc
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Wed May 31 11:24:44 2017 +0500

    Update Makefile to use python3 command

diff --git a/Makefile b/Makefile
index 508b55f..c883419 100644
--- a/Makefile
+++ b/Makefile
@@ -22,12 +22,12 @@ help:
    @echo ''
    @echo ' Common `setup.py` commands:'
    @echo ''
-   @echo '     python setup.py --help-commands'
-   @echo '     python setup.py install'
-   @echo '     python setup.py develop'
-   @echo '     python setup.py test -V'
-   @echo '     python setup.py test --help # to see available options'
-   @echo '     python setup.py build_docs # use `-l` for clean build'
+   @echo '     python3 setup.py --help-commands'
+   @echo '     python3 setup.py install'
+   @echo '     python3 setup.py develop'
+   @echo '     python3 setup.py test -V'
+   @echo '     python3 setup.py test --help # to see available options'
+   @echo '     python3 setup.py build_docs # use `-l` for clean build'
    @echo ''
    @echo ' More info:'
    @echo ''
@@ -36,14 +36,14 @@ help:
    @echo ''

 test:
-   python -m pytest hips
+   python3 -m pytest hips

 # TODO: add flake8 and pylint
 # TODO: activate mypy testing. Fix or configure to ignore these issues:
 # https://travis-ci.org/hipspy/hips/jobs/236913996#L694
 #  python -m mypy hips
 code-analysis:
-   python -m pycodestyle hips --count
+   python3 -m pycodestyle hips --count

 doc-show:
    open docs/_build/html/index.html
@@ -63,4 +63,4 @@ trailing-spaces:
    find $(PROJECT) docs -name "*.rst" -exec perl -pi -e 's/[ \t]*$$//' {} \;

 conda:
-   python setup.py bdist_conda
+   python3 setup.py bdist_conda

commit c5942b52a60a45dbe05e154dc381dd8ef3d2b46c
Author: Adeel Ahmad <adeelahmad14@hotmail.com>
Date:   Mon May 29 10:34:09 2017 +0500

    Update simple.py

    Return correct value.

diff --git a/hips/draw/simple.py b/hips/draw/simple.py
index 21c131a..f1a1675 100644
--- a/hips/draw/simple.py
+++ b/hips/draw/simple.py
@@ -29,4 +29,4 @@ def hello(name: str) -> int:
         Meaning of life
     """
     print('Hello {}'.format(name))
-    return 42
+    return 43

blogroll

social