././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1646929515.250823
aplpy-2.1.0/ 0000775 0001750 0001750 00000000000 00000000000 011022 5 ustar 00tom tom ././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1646929515.238823
aplpy-2.1.0/.circleci/ 0000775 0001750 0001750 00000000000 00000000000 012655 5 ustar 00tom tom ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/.circleci/config.yml 0000664 0001750 0001750 00000005166 00000000000 014655 0 ustar 00tom tom version: 2
jobs:
image-tests-mpl212:
docker:
- image: astropy/image-tests-py36-mpl212:1.10
steps:
- checkout
- run:
name: Install wheel
command: pip3 install wheel
- run:
name: Install dependencies
command: pip3 install .[test] codecov
- run:
name: Run tests
command: MPLBACKEND=Agg pytest --remote-data=any --mpl
- run:
name: Report coverage
command: codecov
image-tests-mpl222:
docker:
- image: astropy/image-tests-py36-mpl222:1.10
steps:
- checkout
- run:
name: Install wheel
command: pip3 install wheel
- run:
name: Install dependencies
command: pip3 install .[test] codecov
- run:
name: Run tests
command: MPLBACKEND=Agg pytest --remote-data=any --mpl
- run:
name: Report coverage
command: codecov
image-tests-mpl302:
docker:
- image: astropy/image-tests-py37-mpl302:1.10
steps:
- checkout
- run:
name: Install wheel
command: pip3 install wheel
- run:
name: Install dependencies
command: pip3 install .[test] codecov
- run:
name: Run tests
command: MPLBACKEND=Agg pytest --remote-data=any --mpl
- run:
name: Report coverage
command: codecov
image-tests-mpl311:
docker:
- image: astropy/image-tests-py37-mpl311:1.10
steps:
- checkout
- run:
name: Install wheel
command: pip3 install wheel
- run:
name: Install dependencies
command: pip3 install .[test] codecov
- run:
name: Run tests
command: MPLBACKEND=Agg pytest --remote-data=any --mpl
- run:
name: Report coverage
command: codecov
image-tests-mpldev:
docker:
- image: astropy/image-tests-py37-base:1.4
steps:
- checkout
- run:
name: Install wheel
command: pip3 install wheel
- run:
name: Install Matplotlib
command: pip3 install git+https://github.com/matplotlib/matplotlib.git
- run:
name: Install dependencies
command: pip3 install .[test] codecov
- run:
name: Run tests
command: MPLBACKEND=Agg pytest --remote-data=any --mpl
- run:
name: Report coverage
command: codecov
workflows:
version: 2
build_and_test:
jobs:
- image-tests-mpl212
- image-tests-mpl222
- image-tests-mpl302
- image-tests-mpl311
- image-tests-mpldev
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/.gitignore 0000664 0001750 0001750 00000001004 00000000000 013005 0 ustar 00tom tom # Compiled files
*.py[co]
*.a
*.o
*.so
__pycache__
# Ignore .c files by default to avoid including generated code. If you want to
# add a non-generated .c extension, use `git add -f filename.c`.
*.c
# Other generated files
*/version.py
*/cython_version.py
htmlcov
.coverage
# Sphinx
_build
# Packages/installer info
*.egg
*.egg-info
dist
build
eggs
parts
bin
var
sdist
develop-eggs
.installed.cfg
distribute-*.tar.gz
# Other
.*.swp
*~
.project
.pydevproject
.settings
# Mac OSX
.DS_Store
.tox
docs/api
.tmp
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/.readthedocs.yml 0000664 0001750 0001750 00000000252 00000000000 014107 0 ustar 00tom tom version: 2
build:
image: latest
python:
version: 3.7
install:
- method: pip
path: .
extra_requirements:
- docs
- all
formats: []
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646929473.0
aplpy-2.1.0/CHANGES.rst 0000664 0001750 0001750 00000040142 00000000000 012625 0 ustar 00tom tom CHANGES
--------
2.1.0 (2022-03-10)
------------------
- Updated package infrastructure to follow guidelines described in APE 17, namely
removing astropy-helpers and using tox for setting up testing environments. [#448]
- Fix compatibility with astropy 4.x and 5.0. [#448,#471]
- Fixed default color for ``show_lines`` (was previously transparent, now black). [#441]
- Fixed ``set_tick_direction`` to not error due to incorrect reference to ``ax``. [#450]
- Fixed compatibility with Matplotlib 3.4. [#466]
2.0.3 (2019-02-19)
------------------
- Fix 'arcsinh' stretch and fix definition of vmid to match that in 1.x. [#421]
2.0.2 (2019-02-18)
------------------
- Fixed ``set_nan_color``. [#419]
2.0.1 (2019-02-18)
------------------
- Remove unused arguments in ``make_rgb_cube`` and fix behavior of
``north=False``. [#417]
- Fixed bug in image extent for non-square RGB images. [#417]
2.0 (2019-02-17)
----------------
- Refactored APLpy to make use of WCSAxes to draw coordinate frames and
astropy.visualization to deal with image stretching. [#239, #364, #365]
- Use reproject package instead of montage-wrapper and Montage. [#360]
1.1.1 (2016-10-04)
------------------
- Added missing LICENSE and README to tarball.
- Fix undefined ``HDUList``. [#317]
1.1 (2016-09-23)
----------------
- Fixed compatibility of Scalebar.set with Matplotlib 1.5.x. [#272, #286]
- Fixed compatibility with Python 3.5.
- Astropy is now a required dependency, and PyFITS and PyWCS are no longer
supported.
1.0 (2015-02-18)
----------------
New features
~~~~~~~~~~~~
- Added new ``show_vectors`` to show vector maps. [#220]
Improvements
~~~~~~~~~~~~
- The ``auto_refresh`` option now defaults to ``False`` unless IPython is
being used and the Matplotlib backend is interactive. [#238]
Bug fixes
~~~~~~~~~
- Fix a bug that caused RGB images to be incorrectly displayed when zooming
in. [#235]
0.9.14 (2014-11-05)
-------------------
Bug fixes
~~~~~~~~~
- Fix a bug that caused smoothing to fail with integer arrays. [#165]
Improvements
~~~~~~~~~~~~
- Fix deprecation warnings from Astropy. [#173]
0.9.13 (2014-10-04)
-------------------
New features
~~~~~~~~~~~~
- Added ``FITSFigure.set_title`` method that can be used to set the title
of a figure. [#175]
Improvements
~~~~~~~~~~~~
- Beams and scalebars can now optionally be instantiated with Astropy
angular units and quantities. [#186]
- APLpy now includes image tests to ensure reliability over time. [#200]
- The code is now all Python 2 and 3 compatible without requiring 2to3.
[#198]
Bug fixes
~~~~~~~~~
- Fix bug that caused a crash when plotting an image with a single valid
pixel [#197]
- Fixed a severe bug that caused rotated images to have incorrect pixel
scales determined. [#211]
- Fixed a bug that caused set_nan_color to modify Matplotlib colormaps
globally rather than apply just to the desired FITSFigure. [#214]
0.9.12 (2014-07-17)
-------------------
This version fixes compatibility with Astropy 0.4
New features
~~~~~~~~~~~~
- Added the ability to call ``fig.show_contour()`` without arguments and
used the data used to initialize ``FITSFigure``. [#170]
- Added the ability to format colorbar ticks in exponential notation using
the ``log_format`` argument. [#143]
- Added the ability to make NaNs transparent in RGB image output, using the
``make_nans_transparent`` argument. [#138]
API Changes
~~~~~~~~~~~
- astropy.wcs.WCS no longer contains information about the original image
size. Any attempt to instantiate a FITSFigure from a WCS object will raise
a DeprecationException. A workaround is to add `naxisn` attributes to your
WCS object::
mywcs = wcs.WCS(header)
mywcs.naxis1 = header['NAXIS1']
mywcs.naxis2 = header['NAXIS2']
Bug fixes
~~~~~~~~~
- FITSFigure can now be instantiated using an astropy.io.fits.CompImageHDU
object. [#188]
- The coordinate grid is now plotted on the whole axes, not just the subset
containing the image (noticeable when zooming out). [#118]
0.9.11 (2013-11-29)
-------------------
Bug fixes
~~~~~~~~~
- Fix a bug that meant that pixel scales were incorrectly extracted for
some WCS settings. [#156]
0.9.10 (2013-11-25)
-------------------
This version restores compatibility with Astropy 0.3
New Features
~~~~~~~~~~~~
- `FITSFigure.recenter` can now be used with sliced data cubes. [#122]
Bug fixes
~~~~~~~~~
- Fixed issues related to bugs in matplotlib in `match_original` when using
patch collections. [#124]
- Fixed a bug that made images containing NaN or Inf values crash APLpy. [#113]
- Fixed a bug that meant that world2pix and pix2world could not take
Astropy table columns. [#114]
0.9.9 (2013-04-25)
------------------
The main change in this version is that APLpy is now an Astropy-affiliated
package. This means that the Astropy core package is now required, but
PyFITS and PyWCS are no longer required as dependencies.
This release is now no longer compatible with python-montage, and users
should use the montage-wrapper package instead:
http://www.astropy.org/montage-wrapper/
Similarly, PyAVM 0.9.1 or later is now required, and APLpy cannot use
earlier versions:
http://astrofrog.github.io/pyavm/
This release is also the first release fully compatible with Python 3.
New features
~~~~~~~~~~~~
- file-like objects can now be passed to ``FITSFigure.save()``
- the subplot= argument to ``FITSFigure`` can now take the tuple syntax
``(2, 2, 1)`` instead of the full axes box, e.g. ``[0.1, 0.1, 0.9,
0.9]`` (thanks to Anika Schmiedeke for a patch).
- a colorbar label can now be set (thanks to Daniel Goering for a patch).
API changes
~~~~~~~~~~~
- the ``smooth`` argument used for images and contours can no longer take
a tuple. It takes either a single value for symmetric kernels (``gauss``
and ``box``), or it can take a Numpy array for any other kernel shape.
Bug fixes
~~~~~~~~~
- fixed bug that caused regions read from a ds9 region file to be offset
by one pixel.
- fixed bug that caused an exception when adding a Beam
- fixed bug that caused labels in decimal degrees to disappear for images
near the poles.
- fixed a bug that caused RGB images to appear vertically flipped with
certain versions of Matplotlib.
- added a workaround for a bug in Matplotlib that caused patches to appear
filled even with ``facecolor='none'``.
- fixed bug that caused images with NaN or Inf values to not be smoothed
correctly.
Version 0.9.8
This version fixes issues that occurred when using APLpy with PyFITS
3.0.5. This version also contains the first unit/regression tests (which
we will include many more of in future).
New features
~~~~~~~~~~~~
- APLpy now includes regression tests that can be run with:
python setup.py test
- FITSFigure can now be initialized with files or HDUs with missing WCS
information.
- FITSFigure can now be initialized directly with Numpy arrays
- Added methods to show/hide ticks
Bug fixes
~~~~~~~~~
- Fixed a major bug that prevented data cube slicing with PyFITS 3.0.5
- Fixed a few bugs that occurred when plotting a grid for a few specific
projections.
- Fixed a bug that cause the error:
'Figure' object has no attribute '_auto_refresh'
Version 0.9.7
This version sees the re-write of the WCS support to enable plotting of
FITS files with arbitrary coordinates rather than just sky coordinates,
and to allow slicing of multi-dimensional data cubes. It is now very easy
for example to make position-velocity plots from a 3D FITS cube.
See http://aplpy.github.com/documentation/slicing.html for details.
New features
~~~~~~~~~~~~
- added the ability to plot arbitrary coordinate systems rather than just
sky coordinates.
- added the ability to slice multi-dimensional data cubes.
- added minor ticks.
- pressing 'c' toggles between showing world and pixel coordinates in the
bottom of the interactive window.
- added the ability to specify slice indices for 3D FITS cubes in
make_rgb_image.
- allow pyregion.ShapeList instance to be passed to show_regions.
- the 'logging' module is now used for any stdout output.
- added an overlap= argument to show_contour to force only contours with
at least one point in the image to be shown. This can result in
significantly improved performance, and smaller files, when large FITS
files are used to display contours.
Changes
~~~~~~~
- the default frame color for the 'pretty' theme (the default) is now
black
Version 0.9.6
APLpy is now released under an MIT license
New Features
~~~~~~~~~~~~
- support for plotting AVM-tagged images (requires PyAVM)
- make_rgb_image can now optionally embed AVM Spatial.* meta-data into
images (off by default, requires PyAVM)
- added support for multiple beams
- added method to show arrows
- added method to show lines (thanks to Moritz Guenther)
- added close() method to FITSFigure to free up memory (useful when
making many finder charts for example)
- added B1950 <-> J2000 conversion
- added axis_labels.set_x/yposition and tick_labels.set_x/yposition to
control whether labels are on top/bottom or left/right of the axes.
- now uses python-montage wrapper for reprojection
- added support for passing a WCS object to FITSFigure instead of a
file
- allow a custom box to be specified for the colorbar
API changes
~~~~~~~~~~~
- vmid has changed meaning for log image scaling. It is now the
baseline value to subtract from pixel values before taking the log
(defaults to zero)
- added interpolation= option to show_grayscale and show_colorscale
- added support for zorder= in methods to show shapes (controls which
layers appear on top of which)
- added north=, system=, and equinox= to make_rgb_cube for more control
over the final projection
Bug fixes
~~~~~~~~~
- added a few workaround for matplotlib bugs
- fixed a major bug with downsampling
- fixed bug with import of local modules
- other minor bug fixes
Version 0.9.5
New Features
~~~~~~~~~~~~
- Support for image and contour smoothing
- Support for slicing of n-dimensional datacubes
- Support for beam:
-> add_beam()
-> remove_beam()
- Support for colorbar:
-> add_colorbar()
-> remove_colorbar()
- Support for scalebar:
-> add_scalebar()
-> remove_scalebar()
- Support for plotting ds9 region files:
-> show_regions()
- Support for automatic bounding box adjustments when saving
- New method to set the color to use for NaN values:
-> set_nan_color()
- Auto refreshing has been improved. Figures only refresh once per user
command if refreshing is turned on.
- Ensure tick spacing / label format consistency
- New method to overlay polygons
API changes
~~~~~~~~~~~
- The API has been majorly overhauled. Methods that have been
deprecated will give instructions on the new methods to use.
Bug fixes
~~~~~~~~~
- Fixed a bug with filled contours
- Fixed bug with remove_layer
- Fixed bug with montage commands
Version 0.9.4
Important changes
~~~~~~~~~~~~~~~~~
Matplotlib 0.99 is now required for APLpy
New Features
~~~~~~~~~~~~
- methods such as show_contour, show_markers, etc. now return the
contour, marker, etc. object
- added a method to retrieve the object in a specific layer:
-> get_layer()
- ability to show the beam for the observations:
-> show_beam()
-> hide_beam()
-> set_beam_properties()
- added the ability to show/hide only the x or y axis/tick labels:
-> show_xtick_labels()
-> hide_xtick_labels()
-> show_ytick_labels()
-> hide_ytick_labels()
-> show_xaxis_labels()
-> hide_xaxis_labels()
-> show_yaxis_labels()
-> hide_yaxis_labels()
- convenience functions for world to pixel and pixel to world
conversion:
-> world2pixel()
-> pixel2world()
- added a convention= argument to FITSFigure() and show_contour(). This is
to be used in cases where the WCS interpretation is ambiguous. For
example, a -CAR projection with CRVAL2<>0 can be interpreted in two
different ways. If an ambiguous case pops up, APLpy will raise an
exception and ask for the convention to be specified.
API changes
~~~~~~~~~~~
- set_labels_latex() is now set_system_latex()
Bug fixes
~~~~~~~~~
- the current position of the cursor in world coordinates is now correctly
shown in interactive mode
- fixed an issue which caused RGB FITS cubes to be 64-bit
- fixed a bug which meant that the coordinate grid was not updated
immediately during pan and zoom
Version 0.9.3
New Features
~~~~~~~~~~~~
- added aplpy.make_rgb_cube() that allows users to make a FITS RGB cube
from three FITS files with different projections
- added aplpy.make_rgb_image() that allows users to make an RGB file in
standard image formats from a FITS RGB cube
- added width= and height= arguments to aplpy.FITSFigure.recenter()
method.
- added show_circles(), show_ellipses(), and show_rectangles() to
aplpy.FITSFigure
- new hide_grayscale() and hide_colorscale() methods
API changes
~~~~~~~~~~~
- changes to the API for set_tick_labels_* and set_axis_labels_* methods
- percentiles values are now specified between 0 and 100
Bug fixes
~~~~~~~~~
- fixed an issue which ocurred when reading in FITS cubes
- fixed an issue which led to the last tick along an axis being missing
for coarse images
- fixed a bug that occured if set_theme was called before showing the
image
- fixed a bug that occured when show_contour was called after removing a
layer
Acknowledgments
~~~~~~~~~~~~~~~
Thanks to Paul Ray, Adam Ginsburg, Gus Muench, and forum user hatchell for
bug reports and feature suggestions.
Version 0.9.2
Improvements
~~~~~~~~~~~~
- Improved compatibility issues with matplotlib 0.98.6svn
- Improved speed of initialization of FITSFigure
Version 0.9.1.2
Bug fixes
~~~~~~~~~
- fixed a major bug that occured when reading in Galactic -CAR images
Version 0.9.1
SciPy Dependency Dropped
~~~~~~~~~~~~~~~~~~~~~~~~
While SciPy is a great python package, it can be troublesome to build and
install from scratch. Thanks to Tom Aldcroft's suggestion and highlighting
some of his own code we were able to easily drop the SciPy dependency.
New Features
~~~~~~~~~~~~
- Users can now pass a pyfits HDU instance instead of filenames for both
FITSFigure() and show_contour() if desired.
- Users can now specify an existing figure with the figure= argument.
- Users with a recent enough version of matplotlib (0.98.6svn) can now use
the subplot= argument to place multiple plots in a single figure.
- New hide/show_tick_labels() and hide/show_axis_labels() methods.
- Show_grayscale and show_colorscale() now accept percentile_lower= and
percentile_higher= as arguments.
- Show_grayscale and show_colorscale() now print out vmin and vmax if
chosen automatically.
- New recenter() method to pan and zoom non-interactively.
General Fixes and Optimization
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Added HDU keyword for the show_contour() method.
- More robust reading in FITS file for the show_contour() method.
- Warning if FITS files do not exist for FITSFigure() or show_contour().
Version 0.9.0
First public beta release
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/CITATION 0000644 0001750 0001750 00000002462 00000000000 012161 0 ustar 00tom tom If you use APLpy for a publication, you can use the following acknowledgment:
This research made use of APLpy, an open-source plotting package for Python
(Robitaille and Bressert, 2012; Robitaille, 2019)
where (Robitaille and Bressert, 2012) is a citation to this ADS/ASCL entry:
http://adsabs.harvard.edu/abs/2012ascl.soft08017R
and (Robitaille, 2019) is a citation to the following Zenodo entry:
https://zenodo.org/record/2567476#.XGmAA5P7RZo
The BibTex entries are:
@misc{aplpy2012,
author = {{Robitaille}, T. and {Bressert}, E.},
title = "{APLpy: Astronomical Plotting Library in Python}",
keywords = {Software },
howpublished = {Astrophysics Source Code Library},
year = 2012,
month = aug,
archivePrefix = "ascl",
eprint = {1208.017},
adsurl = {http://adsabs.harvard.edu/abs/2012ascl.soft08017R},
adsnote = {Provided by the SAO/NASA Astrophysics Data System}
}
@misc{aplpy2019,
author = {Robitaille, Thomas},
title = {{APLpy v2.0: The Astronomical Plotting Library in Python}},
month = feb,
year = 2019,
doi = {10.5281/zenodo.2567476},
url = {https://doi.org/10.5281/zenodo.2567476}
}
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/LICENSE.md 0000644 0001750 0001750 00000002162 00000000000 012425 0 ustar 00tom tom APLpy - The Astronomical Plotting Library in Python
Copyright (c) 2010-2013 Thomas P. Robitaille and Eli Bressert
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/MANIFEST.in 0000664 0001750 0001750 00000000447 00000000000 012565 0 ustar 00tom tom include README.rst
include CHANGES.rst
include setup.cfg
include LICENSE.rst
include pyproject.toml
recursive-include aplpy *.pyx *.c *.pxd
recursive-include docs *
recursive-include licenses *
recursive-include scripts *
prune build
prune docs/_build
prune docs/api
global-exclude *.pyc *.o
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1646929515.250823
aplpy-2.1.0/PKG-INFO 0000664 0001750 0001750 00000003110 00000000000 012112 0 ustar 00tom tom Metadata-Version: 2.1
Name: aplpy
Version: 2.1.0
Summary: The Astronomical Plotting Library in Python
Home-page: http://aplpy.github.io
Author: Thomas Robitaille and Eli Bressert
Author-email: thomas.robitaille@gmail.com
License: MIT
Platform: UNKNOWN
Requires-Python: >=3.6
Description-Content-Type: text/x-rst
Provides-Extra: test
Provides-Extra: docs
License-File: LICENSE.md
|Azure| |CircleCI| |codecov| |Documentation Status|
About
-----
APLpy (the Astronomical Plotting Library in Python) is a Python module
aimed at producing publication-quality plots of astronomical imaging
data in FITS format.
PyPI: https://pypi.python.org/pypi/APLpy
Website: http://aplpy.github.io
Documentation: http://aplpy.readthedocs.io
APLpy is released under an MIT open-source license.
Installing
----------
You can install APLpy and all its dependencies with::
pip install aplpy
.. |Azure| image:: https://dev.azure.com/thomasrobitaille/aplpy/_apis/build/status/aplpy.aplpy?repoName=aplpy%2Faplpy&branchName=refs%2Fpull%2F441%2Fmerge
:target: https://dev.azure.com/thomasrobitaille/aplpy/_build/latest?definitionId=16&repoName=aplpy%2Faplpy&branchName=refs%2Fpull%2F441%2Fmerge
.. |CircleCI| image:: https://circleci.com/gh/aplpy/aplpy/tree/main.svg?style=svg
:target: https://circleci.com/gh/aplpy/aplpy/tree/main
.. |codecov| image:: https://codecov.io/gh/aplpy/aplpy/branch/main/graph/badge.svg
:target: https://codecov.io/gh/aplpy/aplpy
.. |Documentation Status| image:: https://img.shields.io/badge/docs-latest-brightgreen.svg?style=flat
:target: https://aplpy.readthedocs.io/en/latest/
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646929450.0
aplpy-2.1.0/README.rst 0000664 0001750 0001750 00000002312 00000000000 012507 0 ustar 00tom tom |Azure| |CircleCI| |codecov| |Documentation Status|
About
-----
APLpy (the Astronomical Plotting Library in Python) is a Python module
aimed at producing publication-quality plots of astronomical imaging
data in FITS format.
PyPI: https://pypi.python.org/pypi/APLpy
Website: http://aplpy.github.io
Documentation: http://aplpy.readthedocs.io
APLpy is released under an MIT open-source license.
Installing
----------
You can install APLpy and all its dependencies with::
pip install aplpy
.. |Azure| image:: https://dev.azure.com/thomasrobitaille/aplpy/_apis/build/status/aplpy.aplpy?repoName=aplpy%2Faplpy&branchName=refs%2Fpull%2F441%2Fmerge
:target: https://dev.azure.com/thomasrobitaille/aplpy/_build/latest?definitionId=16&repoName=aplpy%2Faplpy&branchName=refs%2Fpull%2F441%2Fmerge
.. |CircleCI| image:: https://circleci.com/gh/aplpy/aplpy/tree/main.svg?style=svg
:target: https://circleci.com/gh/aplpy/aplpy/tree/main
.. |codecov| image:: https://codecov.io/gh/aplpy/aplpy/branch/main/graph/badge.svg
:target: https://codecov.io/gh/aplpy/aplpy
.. |Documentation Status| image:: https://img.shields.io/badge/docs-latest-brightgreen.svg?style=flat
:target: https://aplpy.readthedocs.io/en/latest/
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1646929515.238823
aplpy-2.1.0/aplpy/ 0000775 0001750 0001750 00000000000 00000000000 012147 5 ustar 00tom tom ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/__init__.py 0000644 0001750 0001750 00000001526 00000000000 014262 0 ustar 00tom tom # Licensed under a 3-clause BSD style license - see LICENSE.rst
"""
APLpy : Astronomical Plotting Library in Python
"""
# Affiliated packages may add whatever they like to this file, but
# should keep this content at the top.
# ----------------------------------------------------------------------------
from ._astropy_init import * # noqa
# ----------------------------------------------------------------------------
if not _ASTROPY_SETUP_: # noqa
from .core import FITSFigure # noqa
from .rgb import make_rgb_image, make_rgb_cube # noqa
from .frame import Frame # noqa
from .overlays import Scalebar, Beam # noqa
from .colorbar import Colorbar # noqa
from .grid import Grid # noqa
from .ticks import Ticks # noqa
from .tick_labels import TickLabels # noqa
from .axis_labels import AxisLabels # noqa
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/_astropy_init.py 0000664 0001750 0001750 00000003407 00000000000 015410 0 ustar 00tom tom # Licensed under a 3-clause BSD style license - see LICENSE.rst
__all__ = ['__version__']
# this indicates whether or not we are in the package's setup.py
try:
_ASTROPY_SETUP_
except NameError:
import builtins
builtins._ASTROPY_SETUP_ = False
try:
from .version import version as __version__
except ImportError:
__version__ = ''
if not _ASTROPY_SETUP_: # noqa
import os
from warnings import warn
from astropy.config.configuration import (
update_default_config,
ConfigurationDefaultMissingError,
ConfigurationDefaultMissingWarning)
# Create the test function for self test
from astropy.tests.runner import TestRunner
test = TestRunner.make_test_runner_in(os.path.dirname(__file__))
test.__test__ = False
__all__ += ['test']
# add these here so we only need to cleanup the namespace at the end
config_dir = None
if not os.environ.get('ASTROPY_SKIP_CONFIG_UPDATE', False):
config_dir = os.path.dirname(__file__)
config_template = os.path.join(config_dir, __package__ + ".cfg")
if os.path.isfile(config_template):
try:
update_default_config(
__package__, config_dir, version=__version__)
except TypeError as orig_error:
try:
update_default_config(__package__, config_dir)
except ConfigurationDefaultMissingError as e:
wmsg = (e.args[0] +
" Cannot install default profile. If you are "
"importing from source, this is expected.")
warn(ConfigurationDefaultMissingWarning(wmsg))
del e
except Exception:
raise orig_error
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/axis_labels.py 0000664 0001750 0001750 00000015131 00000000000 015010 0 ustar 00tom tom from astropy.wcs.utils import wcs_to_celestial_frame
from astropy.coordinates import (ICRS, FK5, FK4, Galactic,
HeliocentricTrueEcliptic,
BarycentricTrueEcliptic)
from .decorators import auto_refresh, fixdocstring
__all__ = ['AxisLabels']
class AxisLabels(object):
def __init__(self, parent):
self._ax = parent.ax
self._wcs = parent.ax.wcs
self.x = parent.x
self.y = parent.y
self._ax.coords[self.x].set_axislabel_visibility_rule('always')
self._ax.coords[self.y].set_axislabel_visibility_rule('always')
xcoord_type = self._ax.coords[self.x].coord_type
ycoord_type = self._ax.coords[self.y].coord_type
if xcoord_type == 'longitude' and ycoord_type == 'latitude':
celestial = True
inverted = False
elif xcoord_type == 'latitude' and ycoord_type == 'longitude':
celestial = True
inverted = True
else:
celestial = inverted = False
if celestial:
frame = wcs_to_celestial_frame(self._wcs)
else:
frame = None
if isinstance(frame, ICRS):
xtext = 'RA (ICRS)'
ytext = 'Dec (ICRS)'
elif isinstance(frame, FK5):
equinox = "{:g}".format(FK5.equinox.jyear)
xtext = 'RA (J{0})'.format(equinox)
ytext = 'Dec (J{0})'.format(equinox)
elif isinstance(frame, FK4):
equinox = "{:g}".format(FK4.equinox.byear)
xtext = 'RA (B{0})'.format(equinox)
ytext = 'Dec (B{0})'.format(equinox)
elif isinstance(frame, Galactic):
xtext = 'Galactic Longitude'
ytext = 'Galactic Latitude'
elif isinstance(frame, (HeliocentricTrueEcliptic, BarycentricTrueEcliptic)):
# NOTE: once we support only Astropy 2.0+, we can use BaseEclipticFrame
xtext = 'Ecliptic Longitude'
ytext = 'Ecliptic Latitude'
else:
cunit_x = self._wcs.wcs.cunit[self.x]
cunit_y = self._wcs.wcs.cunit[self.y]
cname_x = self._wcs.wcs.cname[self.x]
cname_y = self._wcs.wcs.cname[self.y]
ctype_x = self._wcs.wcs.ctype[self.x]
ctype_y = self._wcs.wcs.ctype[self.y]
xunit = " (%s)" % cunit_x if cunit_x not in ["", None] else ""
yunit = " (%s)" % cunit_y if cunit_y not in ["", None] else ""
if len(cname_x) > 0:
xtext = cname_x + xunit
else:
if len(ctype_x) == 8 and ctype_x[4] == '-':
xtext = ctype_x[:4].replace('-', '') + xunit
else:
xtext = ctype_x + xunit
if len(cname_y) > 0:
ytext = cname_y + yunit
else:
if len(ctype_y) == 8 and ctype_y[4] == '-':
ytext = ctype_y[:4].replace('-', '') + yunit
else:
ytext = ctype_y + yunit
if inverted:
xtext, ytext = ytext, xtext
self.set_xtext(xtext)
self.set_ytext(ytext)
self.set_xposition('bottom')
self.set_yposition('left')
@auto_refresh
def set_xtext(self, label):
"""
Set the x-axis label text.
"""
self._x_text = label
self._ax.coords[self.x].set_axislabel(label)
@auto_refresh
def set_ytext(self, label):
"""
Set the y-axis label text.
"""
self._y_text = label
self._ax.coords[self.y].set_axislabel(label)
@auto_refresh
def set_xpad(self, pad):
"""
Set the x-axis label displacement in terms of the axis label font size.
"""
self._ax.coords[self.x].axislabels.set_minpad(pad)
@auto_refresh
def set_ypad(self, pad):
"""
Set the y-axis label displacement in terms of the axis label font size.
"""
self._ax.coords[self.y].axislabels.set_minpad(pad)
@auto_refresh
@fixdocstring
def set_font(self, **kwargs):
"""
Set the font of the axis labels.
Parameters
----------
common: family, style, variant, stretch, weight, size, fontproperties
Notes
-----
Default values are set by matplotlib or previously set values if
set_font has already been called. Global default values can be set by
editing the matplotlibrc file.
"""
self._ax.coords[self.x].axislabels.set(**kwargs)
self._ax.coords[self.y].axislabels.set(**kwargs)
@auto_refresh
def show(self):
"""
Show the x- and y-axis labels.
"""
self.show_x()
self.show_y()
@auto_refresh
def hide(self):
"""
Hide the x- and y-axis labels.
"""
self.hide_x()
self.hide_y()
@auto_refresh
def show_x(self):
"""
Show the x-axis label.
"""
if self._xposition == 'bottom':
self._ax.coords[self.x].set_axislabel_position('b')
else:
self._ax.coords[self.x].set_axislabel_position('t')
@auto_refresh
def hide_x(self):
"""
Hide the x-axis label.
"""
self._ax.coords[self.x].set_axislabel_position('')
@auto_refresh
def show_y(self):
"""
Show the y-axis label.
"""
if self._yposition == 'left':
self._ax.coords[self.y].set_axislabel_position('l')
else:
self._ax.coords[self.y].set_axislabel_position('r')
@auto_refresh
def hide_y(self):
"""
Hide the y-axis label.
"""
self._ax.coords[self.y].set_axislabel_position('')
@auto_refresh
def set_xposition(self, position):
"""
Set the position of the x-axis label ('top' or 'bottom')
"""
if position == 'bottom':
self._ax.coords[self.x].set_axislabel_position('b')
elif position == 'top':
self._ax.coords[self.x].set_axislabel_position('t')
else:
raise ValueError("position should be one of 'top' or 'bottom'")
self._xposition = position
@auto_refresh
def set_yposition(self, position):
"""
Set the position of the y-axis label ('left' or 'right')
"""
if position == 'left':
self._ax.coords[self.y].set_axislabel_position('l')
elif position == 'right':
self._ax.coords[self.y].set_axislabel_position('r')
else:
raise ValueError("position should be one of 'left' or 'right'")
self._yposition = position
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/colorbar.py 0000664 0001750 0001750 00000035570 00000000000 014336 0 ustar 00tom tom import warnings
import matplotlib.axes as maxes
from mpl_toolkits.axes_grid1 import make_axes_locatable
from matplotlib.font_manager import FontProperties
from matplotlib.ticker import LogFormatterMathtext
from .decorators import auto_refresh, fixdocstring
class Colorbar(object):
def __init__(self, parent):
self._figure = parent._figure
self._colorbar_axes = None
self._parent = parent
self._base_settings = {}
self._ticklabel_fontproperties = FontProperties()
self._axislabel_fontproperties = FontProperties()
@auto_refresh
def show(self, location='right', width=0.2, pad=0.05, ticks=None,
labels=True, log_format=False, box=None,
box_orientation='vertical', axis_label_text=None,
axis_label_rotation=None, axis_label_pad=5):
"""
Show a colorbar on the side of the image.
Parameters
----------
location : str, optional
Where to place the colorbar. Should be one of 'left', 'right',
'top', 'bottom'.
width : float, optional
The width of the colorbar relative to the canvas size.
pad : float, optional
The spacing between the colorbar and the image relative to the
canvas size.
ticks : list, optional
The position of the ticks on the colorbar.
labels : bool, optional
Whether to show numerical labels.
log_format : bool, optional
Whether to format ticks in exponential notation
box : list, optional
A custom box within which to place the colorbar. This should
be in the form [xmin, ymin, dx, dy] and be in relative figure
units. This overrides the location argument.
box_orientation str, optional
The orientation of the colorbar within the box. Can be
'horizontal' or 'vertical'
axis_label_text str, optional
Optional text label of the colorbar.
"""
self._base_settings['location'] = location
self._base_settings['width'] = width
self._base_settings['pad'] = pad
self._base_settings['ticks'] = ticks
self._base_settings['labels'] = labels
self._base_settings['log_format'] = log_format
self._base_settings['box'] = box
self._base_settings['box_orientation'] = box_orientation
self._base_settings['axis_label_text'] = axis_label_text
self._base_settings['axis_label_rotation'] = axis_label_rotation
self._base_settings['axis_label_pad'] = axis_label_pad
if self._parent.image:
if self._colorbar_axes:
self._figure.delaxes(self._colorbar_axes)
if box is None:
divider = make_axes_locatable(self._parent.ax)
if location == 'right':
self._colorbar_axes = divider.new_horizontal(size=width, pad=pad, axes_class=maxes.Axes)
orientation = 'vertical'
elif location == 'top':
self._colorbar_axes = divider.new_vertical(size=width, pad=pad, axes_class=maxes.Axes)
orientation = 'horizontal'
elif location == 'left':
warnings.warn("Left colorbar not fully implemented")
self._colorbar_axes = divider.new_horizontal(size=width, pad=pad, pack_start=True, axes_class=maxes.Axes)
locator = divider.new_locator(nx=0, ny=0)
self._colorbar_axes.set_axes_locator(locator)
orientation = 'vertical'
elif location == 'bottom':
warnings.warn("Bottom colorbar not fully implemented")
self._colorbar_axes = divider.new_vertical(size=width, pad=pad, pack_start=True, axes_class=maxes.Axes)
locator = divider.new_locator(nx=0, ny=0)
self._colorbar_axes.set_axes_locator(locator)
orientation = 'horizontal'
else:
raise Exception("location should be one of: right/top")
self._figure.add_axes(self._colorbar_axes)
else:
self._colorbar_axes = self._figure.add_axes(box)
orientation = box_orientation
if log_format:
format = LogFormatterMathtext()
else:
format = None
self._colorbar = self._figure.colorbar(self._parent.image, cax=self._colorbar_axes,
orientation=orientation, format=format,
ticks=ticks)
if axis_label_text:
if axis_label_rotation:
self._colorbar.set_label(axis_label_text, rotation=axis_label_rotation)
else:
self._colorbar.set_label(axis_label_text)
if location == 'right':
for tick in self._colorbar_axes.yaxis.get_major_ticks():
tick.tick1line.set_visible(True)
tick.tick2line.set_visible(True)
tick.label1.set_visible(False)
tick.label2.set_visible(labels)
self._colorbar_axes.yaxis.set_label_position('right')
self._colorbar_axes.yaxis.labelpad = axis_label_pad
elif location == 'top':
for tick in self._colorbar_axes.xaxis.get_major_ticks():
tick.tick1line.set_visible(True)
tick.tick2line.set_visible(True)
tick.label1.set_visible(False)
tick.label2.set_visible(labels)
self._colorbar_axes.xaxis.set_label_position('top')
self._colorbar_axes.xaxis.labelpad = axis_label_pad
elif location == 'left':
for tick in self._colorbar_axes.yaxis.get_major_ticks():
tick.tick1line.set_visible(True)
tick.tick2line.set_visible(True)
tick.label1.set_visible(labels)
tick.label2.set_visible(False)
self._colorbar_axes.yaxis.set_label_position('left')
self._colorbar_axes.yaxis.labelpad = axis_label_pad
elif location == 'bottom':
for tick in self._colorbar_axes.xaxis.get_major_ticks():
tick.tick1line.set_visible(True)
tick.tick2line.set_visible(True)
tick.label1.set_visible(labels)
tick.label2.set_visible(False)
self._colorbar_axes.xaxis.set_label_position('bottom')
self._colorbar_axes.xaxis.labelpad = axis_label_pad
else:
warnings.warn("No image is shown, therefore, no colorbar will be plotted")
@auto_refresh
def update(self):
if self._colorbar_axes:
self.show(**self._base_settings)
@auto_refresh
def hide(self):
self._figure.delaxes(self._colorbar_axes)
self._colorbar_axes = None
@auto_refresh
def _remove(self):
self._figure.delaxes(self._colorbar_axes)
# LOCATION AND SIZE
@auto_refresh
def set_location(self, location):
"""
Set the location of the colorbar.
Should be one of 'left', 'right', 'top', 'bottom'.
"""
self._base_settings['location'] = location
self.show(**self._base_settings)
self.set_font(fontproperties=self._ticklabel_fontproperties)
self.set_axis_label_font(fontproperties=self._axislabel_fontproperties)
@auto_refresh
def set_width(self, width):
"""
Set the width of the colorbar relative to the canvas size.
"""
self._base_settings['width'] = width
self.show(**self._base_settings)
self.set_font(fontproperties=self._ticklabel_fontproperties)
self.set_axis_label_font(fontproperties=self._axislabel_fontproperties)
@auto_refresh
def set_pad(self, pad):
"""
Set the spacing between the colorbar and the image relative to the
canvas size.
"""
self._base_settings['pad'] = pad
self.show(**self._base_settings)
self.set_font(fontproperties=self._ticklabel_fontproperties)
self.set_axis_label_font(fontproperties=self._axislabel_fontproperties)
@auto_refresh
def set_ticks(self, ticks):
"""
Set the position of the ticks on the colorbar.
"""
self._base_settings['ticks'] = ticks
self.show(**self._base_settings)
self.set_font(fontproperties=self._ticklabel_fontproperties)
self.set_axis_label_font(fontproperties=self._axislabel_fontproperties)
@auto_refresh
def set_labels(self, labels):
"""
Set whether to show numerical labels.
"""
self._base_settings['labels'] = labels
self.show(**self._base_settings)
self.set_font(fontproperties=self._ticklabel_fontproperties)
self.set_axis_label_font(fontproperties=self._axislabel_fontproperties)
@auto_refresh
def set_box(self, box, box_orientation='vertical'):
"""
Set the box within which to place the colorbar.
This should be in the form [xmin, ymin, dx, dy] and be in relative
figure units. The orientation of the colorbar within the box can be
controlled with the box_orientation argument.
"""
self._base_settings['box'] = box
self._base_settings['box_orientation'] = box_orientation
self.show(**self._base_settings)
self.set_font(fontproperties=self._ticklabel_fontproperties)
self.set_axis_label_font(fontproperties=self._axislabel_fontproperties)
@auto_refresh
def set_axis_label_text(self, axis_label_text):
"""
Set the colorbar label text.
"""
self._base_settings['axis_label_text'] = axis_label_text
self.show(**self._base_settings)
self.set_font(fontproperties=self._ticklabel_fontproperties)
self.set_axis_label_font(fontproperties=self._axislabel_fontproperties)
@auto_refresh
def set_axis_label_rotation(self, axis_label_rotation):
"""
Set the colorbar label rotation.
"""
self._base_settings['axis_label_rotation'] = axis_label_rotation
self.show(**self._base_settings)
self.set_font(fontproperties=self._ticklabel_fontproperties)
self.set_axis_label_font(fontproperties=self._axislabel_fontproperties)
@auto_refresh
def set_axis_label_pad(self, axis_label_pad):
"""
Set the colorbar label displacement, in points.
"""
self._base_settings['axis_label_pad'] = axis_label_pad
self.show(**self._base_settings)
self.set_font(fontproperties=self._ticklabel_fontproperties)
self.set_axis_label_font(fontproperties=self._axislabel_fontproperties)
# FONT PROPERTIES
@auto_refresh
def set_label_properties(self, *args, **kwargs):
warnings.warn("set_label_properties is deprecated - use set_font instead", DeprecationWarning)
self.set_font(*args, **kwargs)
@auto_refresh
@fixdocstring
def set_font(self, family=None, style=None, variant=None, stretch=None,
weight=None, size=None, fontproperties=None):
"""
Set the font of the tick labels.
Parameters
----------
common: family, style, variant, stretch, weight, size, fontproperties
Notes
-----
Default values are set by matplotlib or previously set values if
set_font has already been called. Global default values can be set by
editing the matplotlibrc file.
"""
if family:
self._ticklabel_fontproperties.set_family(family)
if style:
self._ticklabel_fontproperties.set_style(style)
if variant:
self._ticklabel_fontproperties.set_variant(variant)
if stretch:
self._ticklabel_fontproperties.set_stretch(stretch)
if weight:
self._ticklabel_fontproperties.set_weight(weight)
if size:
self._ticklabel_fontproperties.set_size(size)
if fontproperties:
self._ticklabel_fontproperties = fontproperties
# Update the tick label font properties
for label in self._colorbar_axes.get_xticklabels():
label.set_fontproperties(self._ticklabel_fontproperties)
for label in self._colorbar_axes.get_yticklabels():
label.set_fontproperties(self._ticklabel_fontproperties)
# Also update the offset text font properties
label = self._colorbar_axes.xaxis.get_offset_text()
label.set_fontproperties(self._ticklabel_fontproperties)
label = self._colorbar_axes.yaxis.get_offset_text()
label.set_fontproperties(self._ticklabel_fontproperties)
@auto_refresh
@fixdocstring
def set_axis_label_font(self, family=None, style=None, variant=None,
stretch=None, weight=None, size=None,
fontproperties=None):
"""
Set the font of the tick labels.
Parameters
----------
common: family, style, variant, stretch, weight, size, fontproperties
Notes
-----
Default values are set by matplotlib or previously set values if
set_font has already been called. Global default values can be set by
editing the matplotlibrc file.
"""
if family:
self._axislabel_fontproperties.set_family(family)
if style:
self._axislabel_fontproperties.set_style(style)
if variant:
self._axislabel_fontproperties.set_variant(variant)
if stretch:
self._axislabel_fontproperties.set_stretch(stretch)
if weight:
self._axislabel_fontproperties.set_weight(weight)
if size:
self._axislabel_fontproperties.set_size(size)
if fontproperties:
self._axislabel_fontproperties = fontproperties
# Update the label font properties
label = self._colorbar_axes.xaxis.get_label()
label.set_fontproperties(self._axislabel_fontproperties)
label = self._colorbar_axes.yaxis.get_label()
label.set_fontproperties(self._axislabel_fontproperties)
# FRAME PROPERTIES
@auto_refresh
def set_frame_linewidth(self, linewidth):
"""
Set the linewidth of the colorbar frame, in points.
"""
warnings.warn("This method is not functional at this time")
for key in self._colorbar_axes.spines:
self._colorbar_axes.spines[key].set_linewidth(linewidth)
@auto_refresh
def set_frame_color(self, color):
"""
Set the color of the colorbar frame, in points.
"""
warnings.warn("This method is not functional at this time")
for key in self._colorbar_axes.spines:
self._colorbar_axes.spines[key].set_edgecolor(color)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/compat.py 0000644 0001750 0001750 00000003474 00000000000 014012 0 ustar 00tom tom # The simple_norm function in astropy is missing a control for the log
# scaling as described in https://github.com/astropy/astropy/issues/8432,
# so for now we include a copy of the fixed function here.
from astropy.visualization.interval import (PercentileInterval,
AsymmetricPercentileInterval,
ManualInterval, MinMaxInterval)
from astropy.visualization.stretch import (LinearStretch, SqrtStretch,
PowerStretch, LogStretch,
AsinhStretch)
from astropy.visualization.mpl_normalize import ImageNormalize
__all__ = ['simple_norm']
def simple_norm(data, stretch='linear', power=1.0, asinh_a=0.1, log_a=1000,
min_cut=None, max_cut=None, min_percent=None, max_percent=None,
percent=None, clip=True):
if percent is not None:
interval = PercentileInterval(percent)
elif min_percent is not None or max_percent is not None:
interval = AsymmetricPercentileInterval(min_percent or 0.,
max_percent or 100.)
elif min_cut is not None or max_cut is not None:
interval = ManualInterval(min_cut, max_cut)
else:
interval = MinMaxInterval()
if stretch == 'linear':
stretch = LinearStretch()
elif stretch == 'sqrt':
stretch = SqrtStretch()
elif stretch == 'power':
stretch = PowerStretch(power)
elif stretch == 'log':
stretch = LogStretch(log_a)
elif stretch == 'asinh':
stretch = AsinhStretch(asinh_a)
else:
raise ValueError('Unknown stretch: {0}.'.format(stretch))
vmin, vmax = interval.get_limits(data)
return ImageNormalize(vmin=vmin, vmax=vmax, stretch=stretch, clip=clip)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/conftest.py 0000664 0001750 0001750 00000004071 00000000000 014350 0 ustar 00tom tom # This file is used to configure the behavior of pytest when using the Astropy
# test infrastructure. It needs to live inside the package in order for it to
# get picked up when running the tests inside an interpreter using
# packagename.test
import os
from astropy.version import version as astropy_version
# For Astropy 3.0 and later, we can use the standalone pytest plugin
if astropy_version < '3.0':
from astropy.tests.pytest_plugins import * # noqa
del pytest_report_header
ASTROPY_HEADER = True
else:
try:
from pytest_astropy_header.display import PYTEST_HEADER_MODULES, TESTED_VERSIONS
ASTROPY_HEADER = True
except ImportError:
ASTROPY_HEADER = False
def pytest_configure(config):
if ASTROPY_HEADER:
config.option.astropy_header = True
# Customize the following lines to add/remove entries from the list of
# packages for which version numbers are displayed when running the tests.
PYTEST_HEADER_MODULES['Astropy'] = 'astropy'
PYTEST_HEADER_MODULES['pyregion'] = 'pyregion'
PYTEST_HEADER_MODULES['PyAVM'] = 'PyAVM'
PYTEST_HEADER_MODULES['reproject'] = 'reproject'
del PYTEST_HEADER_MODULES['h5py']
from . import __version__
packagename = os.path.basename(os.path.dirname(__file__))
TESTED_VERSIONS[packagename] = __version__
# Uncomment the last two lines in this block to treat all DeprecationWarnings as
# exceptions. For Astropy v2.0 or later, there are 2 additional keywords,
# as follow (although default should work for most cases).
# To ignore some packages that produce deprecation warnings on import
# (in addition to 'compiler', 'scipy', 'pygments', 'ipykernel', and
# 'setuptools'), add:
# modules_to_ignore_on_import=['module_1', 'module_2']
# To ignore some specific deprecation warning messages for Python version
# MAJOR.MINOR or later, add:
# warnings_to_ignore_by_pyver={(MAJOR, MINOR): ['Message to ignore']}
# from astropy.tests.helper import enable_deprecations_as_exceptions # noqa
# enable_deprecations_as_exceptions()
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/convolve_util.py 0000664 0001750 0001750 00000002255 00000000000 015415 0 ustar 00tom tom import numpy as np
from astropy.convolution import (convolve as astropy_convolve,
Gaussian2DKernel, Box2DKernel)
def convolve(image, smooth=3, kernel='gauss'):
if smooth is None and isinstance(kernel, str) and kernel in ['box', 'gauss']:
return image
if smooth is not None and not np.isscalar(smooth):
raise ValueError("smooth= should be an integer - for more complex "
"kernels, pass an array containing the kernel "
"to the kernel= option")
# The Astropy convolution doesn't treat +/-Inf values correctly yet, so we
# convert to NaN here.
image_fixed = np.array(image, dtype=float, copy=True)
image_fixed[np.isinf(image)] = np.nan
if isinstance(kernel, str):
if kernel == 'gauss':
kernel = Gaussian2DKernel(smooth, x_size=smooth * 5, y_size=smooth * 5)
elif kernel == 'box':
kernel = Box2DKernel(smooth, x_size=smooth * 5, y_size=smooth * 5)
else:
raise ValueError("Unknown kernel: {0}".format(kernel))
else:
kernel = kernel
return astropy_convolve(image, kernel, boundary='extend')
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646929450.0
aplpy-2.1.0/aplpy/core.py 0000664 0001750 0001750 00000226342 00000000000 013462 0 ustar 00tom tom from distutils import version
import os
import operator
from functools import reduce
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.patches import Circle, Rectangle, Ellipse, Polygon, FancyArrow
from matplotlib.collections import PatchCollection, LineCollection
import numpy as np
from astropy import log
from astropy.wcs import WCS
from astropy.wcs.utils import proj_plane_pixel_scales
from astropy.io import fits
try:
from astropy.nddata.blocks import block_reduce
except ImportError: # astropy<5
from astropy.nddata.utils import block_reduce
from astropy.visualization import AsymmetricPercentileInterval
from astropy.visualization.wcsaxes import WCSAxes, WCSAxesSubplot
from astropy.coordinates import ICRS
from . import convolve_util
from . import header as header_util
from . import slicer
from .compat import simple_norm
from .layers import Layers
from .grid import Grid
from .ticks import Ticks
from .tick_labels import TickLabels
from .axis_labels import AxisLabels
from .overlays import Beam, Scalebar
from .regions import Regions
from .colorbar import Colorbar
from .frame import Frame
from .decorators import auto_refresh, fixdocstring
HDU_TYPES = tuple([fits.PrimaryHDU, fits.ImageHDU, fits.CompImageHDU])
__doctest_skip__ = ['FITSFigure.add_beam', 'FITSFigure.add_colorbar',
'FITSFigure.add_grid', 'FITSFigure.add_scalebar']
def uniformize_1d(*args):
if len(args) > 1:
return np.broadcast_arrays(np.atleast_1d(args[0]), *args[1:])
elif len(args) == 1:
return np.atleast_1d(args[0])
else:
raise ValueError("No arguments passed to uniformize_1d")
class FITSFigure(Layers, Regions):
"""
Create a FITSFigure instance.
This class is a wrapper around the Astropy WCSAxes class and provides
the same API as historical versions of APLpy.
Parameters
----------
data : see below
The FITS file to open. The following data types can be passed:
string
astropy.io.fits.PrimaryHDU
astropy.io.fits.ImageHDU
astropy.wcs.WCS
np.ndarray
RGB image with AVM meta-data
hdu : int, optional
By default, the image in the primary HDU is read in. If a
different HDU is required, use this argument.
figure : ~matplotlib.figure.Figure, optional
If specified, a subplot will be added to this existing
matplotlib figure() instance, rather than a new figure
being created from scratch.
subplot : tuple or list, optional
If specified, a subplot will be added at this position. If a tuple
of three values, the tuple should contain the standard matplotlib
subplot parameters, i.e. (ny, nx, subplot). If a list of four
values, the list should contain [xmin, ymin, dx, dy] where xmin
and ymin are the position of the bottom left corner of the
subplot, and dx and dy are the width and height of the subplot
respectively. These should all be given in units of the figure
width and height. For example, [0.1, 0.1, 0.8, 0.8] will almost
fill the entire figure, leaving a 10 percent margin on all sides.
downsample : int, optional
If this option is specified, the image will be downsampled
by a factor *downsample* when reading in the data.
north : bool, optional
Whether to rotate the image so that north is up. By default, this is
assumed to be 'north' in the ICRS frame, but you can also pass any
astropy :class:`~astropy.coordinates.BaseCoordinateFrame` to indicate
to use the north of that frame.
convention : str, optional
This is used in cases where a FITS header can be interpreted
in multiple ways. For example, for files with a -CAR
projection and CRVAL2=0, this can be set to 'wells' or
'calabretta' to choose the appropriate convention.
dimensions : tuple or list, optional
The index of the axes to use if the data has more than three
dimensions.
slices : tuple or list, optional
If a FITS file with more than two dimensions is specified,
then these are the slices to extract. If all extra dimensions
only have size 1, then this is not required.
auto_refresh : bool, optional
Whether to refresh the figure automatically every time a
plotting method is called. This can also be set using the
set_auto_refresh method. This defaults to `True` if and only if
APLpy is being used from IPython and the Matplotlib backend is
interactive.
kwargs
Any additional arguments are passed on to matplotlib's Figure()
class. For example, to set the figure size, use the
figsize=(xsize, ysize) argument (where xsize and ysize are in
inches). For more information on these additional arguments, see
the *Optional keyword arguments* section in the documentation for
:class:`~matplotlib.figure.Figure`.
"""
@auto_refresh
def __init__(self, data, hdu=0, figure=None, subplot=(1, 1, 1),
downsample=False, north=False, convention=None,
dimensions=[0, 1], slices=[], auto_refresh=None,
**kwargs):
self._wcsaxes_slices = ('x', 'y')
if 'figsize' not in kwargs:
kwargs['figsize'] = (10, 9)
# Set the grid type
if len(slices) > 0:
self.grid_type = 'contours'
else:
self.grid_type = 'lines'
if (isinstance(data, str) and
data.split('.')[-1].lower() in ['png', 'jpg', 'tif']):
try:
from PIL import Image
except ImportError:
try:
import Image
except ImportError:
raise ImportError("The Python Imaging Library (PIL) is "
"required to read in RGB images")
try:
import pyavm
except ImportError:
raise ImportError("PyAVM is required to read in AVM "
"meta-data from RGB images")
if version.LooseVersion(pyavm.__version__) < version.LooseVersion('0.9.1'):
raise ImportError("PyAVM installation is not recent enough "
"(version 0.9.1 or later is required).")
from pyavm import AVM
# Remember image filename
self._rgb_image = data
# Find image size
nx, ny = Image.open(data).size
# Now convert AVM information to WCS
data = AVM.from_image(data).to_wcs()
# Need to scale CDELT values sometimes the AVM meta-data is only
# really valid for the full-resolution image
data.wcs.cdelt = [data.wcs.cdelt[0] * nx / float(nx), data.wcs.cdelt[1] * ny / float(ny)]
data.wcs.crpix = [data.wcs.crpix[0] / nx * float(nx), data.wcs.crpix[1] / ny * float(ny)]
# Update the NAXIS values with the true dimensions of the RGB image
data.nx = nx
data.ny = ny
data.pixel_shape = (nx, ny)
if isinstance(data, WCS):
wcs = data
if wcs.naxis != 2:
raise ValueError("FITSFigure initialization via WCS objects "
"can only be done with 2-dimensional WCS "
"objects")
if wcs.pixel_shape is None:
raise ValueError("The WCS object does not contain any size "
"information")
header = wcs.to_header()
header['NAXIS1'], header['NAXIS2'] = wcs.pixel_shape
nx = header['NAXIS%i' % (dimensions[0] + 1)]
ny = header['NAXIS%i' % (dimensions[1] + 1)]
self._data = np.zeros((ny, nx), dtype=float)
self._header = header
self._wcs = WCS(header, relax=True)
self._wcs.nx = nx
self._wcs.ny = ny
if downsample:
log.warning("downsample argument is ignored if data "
"passed is a WCS object")
downsample = False
if north:
log.warning("north argument is ignored if data "
"passed is a WCS object")
north = False
else:
self._data, self._header, self._wcs, self._wcsaxes_slices = \
self._get_hdu(data, hdu, north, convention=convention,
dimensions=dimensions, slices=slices)
self._wcs.nx = self._header['NAXIS%i' % (dimensions[0] + 1)]
self._wcs.ny = self._header['NAXIS%i' % (dimensions[1] + 1)]
# Downsample if requested
if downsample:
nx_new = self._wcs.nx - np.mod(self._wcs.nx, downsample)
ny_new = self._wcs.ny - np.mod(self._wcs.ny, downsample)
self._data = self._data[0:ny_new, 0:nx_new]
self._data = block_reduce(self._data, downsample, func=np.mean)
self._wcs.nx, self._wcs.ny = nx_new, ny_new
# Open the figure
if figure:
self._figure = figure
else:
self._figure = plt.figure(**kwargs)
# Set whether to automatically refresh the display
self.set_auto_refresh(auto_refresh)
# Initialize axis instance
if type(subplot) == list and len(subplot) == 4:
self.ax = WCSAxes(self._figure, subplot, wcs=self._wcs,
slices=self._wcsaxes_slices, adjustable='datalim')
self._figure.add_axes(self.ax)
elif type(subplot) == tuple and len(subplot) == 3:
self.ax = WCSAxesSubplot(self._figure, *subplot, wcs=self._wcs,
slices=self._wcsaxes_slices)
self._figure.add_subplot(self.ax)
else:
raise ValueError("subplot= should be either a tuple of three "
"values, or a list of four values")
# Turn off autoscaling
self.ax.set_autoscale_on(False)
# Make sure axes are above everything else
self.ax.set_axisbelow(False)
# Set view to whole FITS file
self._initialize_view()
# Set the coordinates for x and y axis
self.x = dimensions[0]
self.y = dimensions[1]
# Initialize tick, label, and frame convenience wrappers (these dispatch
# calls to WCSAxes)
self.ticks = Ticks(self)
self.axis_labels = AxisLabels(self)
self.tick_labels = TickLabels(self)
self.frame = Frame(self)
# Display minor ticks
self.ax.coords[self.x].display_minor_ticks(True)
self.ax.coords[self.y].display_minor_ticks(True)
# Initialize layers list
self._initialize_layers()
# Set image holder to be empty
self.image = None
# Set default theme
self.set_theme(theme='pretty')
def _get_hdu(self, data, hdu, north, convention=None,
dimensions=[0, 1], slices=[]):
if isinstance(data, str):
filename = data
# Check file exists
if not os.path.exists(filename):
raise IOError("File not found: " + filename)
# Read in FITS file
try:
hdulist = fits.open(filename)
except Exception:
raise IOError("An error occurred while reading the FITS file")
# Check whether the HDU specified contains any data, otherwise
# cycle through all HDUs to find one that contains valid image data
if hdulist[hdu].data is None:
found = False
for alt_hdu in range(len(hdulist)):
if isinstance(hdulist[alt_hdu], HDU_TYPES):
if hdulist[alt_hdu].data is not None:
log.warning("hdu=%i does not contain any data, "
"using hdu=%i instead" % (hdu, alt_hdu))
hdu = hdulist[alt_hdu]
found = True
break
if not found:
raise Exception("FITS file does not contain any image data")
else:
hdu = hdulist[hdu]
elif type(data) == np.ndarray:
hdu = fits.ImageHDU(data)
elif isinstance(data, HDU_TYPES):
hdu = data
elif isinstance(data, fits.HDUList):
hdu = data[hdu]
else:
raise Exception("data argument should either be a filename, an HDU object from astropy.io.fits, a WCS object from astropy.wcs, or a Numpy array.")
# Check that we have at least 2-dimensional data
if hdu.header['NAXIS'] < 2:
raise ValueError("Data should have at least two dimensions")
# Check dimensions= argument
if type(dimensions) not in [list, tuple]:
raise ValueError('dimensions= should be a list or a tuple')
if len(set(dimensions)) != 2 or len(dimensions) != 2:
raise ValueError("dimensions= should be a tuple of two different values")
if dimensions[0] < 0 or dimensions[0] > hdu.header['NAXIS'] - 1:
raise ValueError('values of dimensions= should be between %i and %i' % (0, hdu.header['NAXIS'] - 1))
if dimensions[1] < 0 or dimensions[1] > hdu.header['NAXIS'] - 1:
raise ValueError('values of dimensions= should be between %i and %i' % (0, hdu.header['NAXIS'] - 1))
# Reproject to face north if requested
if north:
# Find rotated WCS
frame = ICRS() if north is True else north
from reproject.mosaicking import find_optimal_celestial_wcs
wcs, shape = find_optimal_celestial_wcs([hdu], frame=frame)
from reproject import reproject_interp
data, _ = reproject_interp(hdu, wcs, shape_out=shape)
header = wcs.to_header()
header['NAXIS1'] = shape[1]
header['NAXIS2'] = shape[0]
else:
# Now copy the data and header to new objects, since in astropy.io.fits
# the two attributes are linked, which can lead to confusing behavior.
# We just need to copy the header to avoid memory issues - as long as
# one item is copied, the two variables are decoupled.
data = hdu.data
header = hdu.header.copy()
del hdu
# If slices wasn't specified, check if we can guess
shape = data.shape
if len(shape) > 2:
n_total = reduce(operator.mul, shape)
n_image = (shape[len(shape) - 1 - dimensions[0]] *
shape[len(shape) - 1 - dimensions[1]])
if n_total == n_image:
slices = [0 for i in range(1, len(shape) - 1)]
log.info("Setting slices=%s" % str(slices))
# Extract slices
data, wcsaxes_slices = slicer.slice_hypercube(data, header,
dimensions=dimensions,
slices=slices)
# Check header
header = header_util.check(header, convention=convention,
dimensions=dimensions)
# Parse WCS info
wcs = WCS(header, relax=True)
return data, header, wcs, wcsaxes_slices
@auto_refresh
def set_title(self, title, **kwargs):
"""
Set the figure title
"""
self.ax.set_title(title, **kwargs)
@auto_refresh
def set_xaxis_coord_type(self, coord_type):
"""
Set the type of x coordinate.
Options are:
* ``scalar``: treat the values are normal decimal scalar values
* ``longitude``: treat the values as a longitude in the 0 to 360 range
* ``latitude``: treat the values as a latitude in the -90 to 90 range
"""
self.ax.coords[self.x].set_coord_type(coord_type)
@auto_refresh
def set_yaxis_coord_type(self, coord_type):
"""
Set the type of y coordinate.
Options are:
* ``scalar``: treat the values are normal decimal scalar values
* ``longitude``: treat the values as a longitude in the 0 to 360 range
* ``latitude``: treat the values as a latitude in the -90 to 90 range
"""
self.ax.coords[self.y].set_coord_type(coord_type)
@auto_refresh
def set_system_latex(self, usetex):
"""
Set whether to use a real LaTeX installation or the built-in matplotlib
LaTeX.
Parameters
----------
usetex : str
Whether to use a real LaTex installation (True) or the built-in
matplotlib LaTeX (False). Note that if the former is chosen, an
installation of LaTex is required.
"""
plt.rc('text', usetex=usetex)
@auto_refresh
def recenter(self, x, y, radius=None, width=None, height=None):
"""
Center the image on a given position and with a given radius.
Either the radius or width/height arguments should be specified. The
units of the radius or width/height should be the same as the world
coordinates in the WCS. For images of the sky, this is often (but not
always) degrees.
Parameters
----------
x, y : float
Coordinates to center on
radius : float, optional
Radius of the region to view in degrees. This produces a square plot.
width : float, optional
Width of the region to view. This should be given in
conjunction with the height argument.
height : float, optional
Height of the region to view. This should be given in
conjunction with the width argument.
"""
xpix, ypix = self.world2pixel(x, y)
pix_scale = proj_plane_pixel_scales(self._wcs)
sx, sy = pix_scale[self.x], pix_scale[self.y]
if radius:
dx_pix = radius / sx
dy_pix = radius / sy
elif width and height:
dx_pix = width / sx * 0.5
dy_pix = height / sy * 0.5
else:
raise Exception("Need to specify either radius= or width= and height= arguments")
if (xpix + dx_pix < -0.5 or
xpix - dx_pix > self._wcs.nx - 0.5 or
ypix + dy_pix < -0.5 or
ypix - dy_pix > self._wcs.ny):
raise Exception("Zoom region falls outside the image")
self.ax.set_xlim(xpix - dx_pix, xpix + dx_pix)
self.ax.set_ylim(ypix - dy_pix, ypix + dy_pix)
@auto_refresh
def show_grayscale(self, vmin=None, vmid=None, vmax=None,
pmin=0.25, pmax=99.75,
stretch='linear', exponent=2, invert='default',
smooth=None, kernel='gauss', aspect='equal',
interpolation='nearest'):
"""
Show a grayscale image of the FITS file.
Parameters
----------
vmin : None or float, optional
Minimum pixel value to use for the grayscale. If set to None,
the minimum pixel value is determined using pmin (default).
vmax : None or float, optional
Maximum pixel value to use for the grayscale. If set to None,
the maximum pixel value is determined using pmax (default).
pmin : float, optional
Percentile value used to determine the minimum pixel value to
use for the grayscale if vmin is set to None. The default
value is 0.25%.
pmax : float, optional
Percentile value used to determine the maximum pixel value to
use for the grayscale if vmax is set to None. The default
value is 99.75%.
stretch : { 'linear', 'log', 'sqrt', 'arcsinh', 'power' }, optional
The stretch function to use
vmid : None or float, optional
Baseline value used for the log and arcsinh stretches. If
set to None, this is set to zero for log stretches and to
vmin - (vmax - vmin) / 30. for arcsinh stretches
exponent : float, optional
If stretch is set to 'power', this is the exponent to use
invert : str, optional
Whether to invert the grayscale or not. The default is False,
unless set_theme is used, in which case the default depends on
the theme.
smooth : int or tuple, optional
Default smoothing scale is 3 pixels across. User can define
whether they want an NxN kernel (integer), or NxM kernel
(tuple). This argument corresponds to the 'gauss' and 'box'
smoothing kernels.
kernel : { 'gauss', 'box', numpy.array }, optional
Default kernel used for smoothing is 'gauss'. The user can
specify if they would prefer 'gauss', 'box', or a custom
kernel. All kernels are normalized to ensure flux retention.
aspect : { 'auto', 'equal' }, optional
Whether to change the aspect ratio of the image to match that
of the axes ('auto') or to change the aspect ratio of the axes
to match that of the data ('equal'; default)
interpolation : str, optional
The type of interpolation to use for the image. The default is
'nearest'. Other options include 'none' (no interpolation,
meaning that if exported to a postscript file, the grayscale
will be output at native resolution irrespective of the dpi
setting), 'bilinear', 'bicubic', and many more (see the
matplotlib documentation for imshow).
"""
if invert == 'default':
invert = self._get_invert_default()
if invert:
cmap = 'gist_yarg'
else:
cmap = 'gray'
self.show_colorscale(vmin=vmin, vmid=vmid, vmax=vmax,
pmin=pmin, pmax=pmax,
stretch=stretch, exponent=exponent, cmap=cmap,
smooth=smooth, kernel=kernel, aspect=aspect,
interpolation=interpolation)
@auto_refresh
def hide_grayscale(self, *args, **kwargs):
self.hide_colorscale(*args, **kwargs)
@auto_refresh
def show_colorscale(self, vmin=None, vmid=None, vmax=None, pmin=0.25,
pmax=99.75, stretch='linear', exponent=2,
cmap='default', smooth=None, kernel='gauss',
aspect='equal', interpolation='nearest'):
"""
Show a colorscale image of the FITS file.
Parameters
----------
vmin : None or float, optional
Minimum pixel value to use for the colorscale. If set to None,
the minimum pixel value is determined using pmin (default).
vmax : None or float, optional
Maximum pixel value to use for the colorscale. If set to None,
the maximum pixel value is determined using pmax (default).
pmin : float, optional
Percentile value used to determine the minimum pixel value to
use for the colorscale if vmin is set to None. The default
value is 0.25%.
pmax : float, optional
Percentile value used to determine the maximum pixel value to
use for the colorscale if vmax is set to None. The default
value is 99.75%.
stretch : { 'linear', 'log', 'sqrt', 'arcsinh', 'power' }, optional
The stretch function to use
vmid : None or float, optional
Baseline value used for the log and arcsinh stretches. If
not set, this defaults to zero for log stretches and to
vmin - (vmax - vmin) / 30. for arcsinh stretches
exponent : float, optional
If stretch is set to 'power', this is the exponent to use
cmap : str, optional
The name of the colormap to use
smooth : int or tuple, optional
Default smoothing scale is 3 pixels across. User can define
whether they want an NxN kernel (integer), or NxM kernel
(tuple). This argument corresponds to the 'gauss' and 'box'
smoothing kernels.
kernel : { 'gauss', 'box', numpy.array }, optional
Default kernel used for smoothing is 'gauss'. The user can
specify if they would prefer 'gauss', 'box', or a custom
kernel. All kernels are normalized to ensure flux retention.
aspect : { 'auto', 'equal' }, optional
Whether to change the aspect ratio of the image to match that
of the axes ('auto') or to change the aspect ratio of the axes
to match that of the data ('equal'; default)
interpolation : str, optional
The type of interpolation to use for the image. The default is
'nearest'. Other options include 'none' (no interpolation,
meaning that if exported to a postscript file, the colorscale
will be output at native resolution irrespective of the dpi
setting), 'bilinear', 'bicubic', and many more (see the
matplotlib documentation for imshow).
"""
if cmap == 'default':
cmap = self._get_colormap_default()
min_auto = vmin is None
max_auto = vmax is None
# The set of available functions
cmap = plt.cm.get_cmap(cmap)
if min_auto or max_auto:
interval = AsymmetricPercentileInterval(pmin, pmax, n_samples=10000)
try:
vmin_auto, vmax_auto = interval.get_limits(self._data)
except (IndexError, TypeError): # no valid values
vmin_auto = vmax_auto = 0
if min_auto:
vmin = vmin_auto
if max_auto:
vmax = vmax_auto
# Prepare normalizer object
if stretch == 'arcsinh':
stretch = 'asinh'
if stretch == 'log':
if vmid is None:
if vmin < 0:
raise ValueError("When using a log stretch, if vmin < 0, then vmid has to be specified")
else:
vmid = 0.
if vmin < vmid:
raise ValueError("When using a log stretch, vmin should be larger than vmid")
log_a = (vmax - vmid) / (vmin - vmid)
norm_kwargs = {'log_a': log_a}
elif stretch == 'asinh':
if vmid is None:
vmid = vmin - (vmax - vmin) / 30.
asinh_a = (vmid - vmin) / (vmax - vmin)
norm_kwargs = {'asinh_a': abs(asinh_a)}
else:
norm_kwargs = {}
normalizer = simple_norm(self._data, stretch=stretch, power=exponent,
min_cut=vmin, max_cut=vmax, clip=False,
**norm_kwargs)
# Adjust vmin/vmax if auto
if min_auto:
if stretch == 'linear':
vmin = -0.1 * (vmax - vmin) + vmin
log.info("Auto-setting vmin to %10.3e" % vmin)
if max_auto:
if stretch == 'linear':
vmax = 0.1 * (vmax - vmin) + vmax
log.info("Auto-setting vmax to %10.3e" % vmax)
# Update normalizer object
normalizer.vmin = vmin
normalizer.vmax = vmax
if self.image:
self.image.set_visible(True)
self.image.set_norm(normalizer)
self.image.set_cmap(cmap=cmap)
self.image.origin = 'lower'
self.image.set_interpolation(interpolation)
self.image.set_data(convolve_util.convolve(self._data,
smooth=smooth,
kernel=kernel))
else:
extent = -0.5, self._wcs.nx - 0.5, -0.5, self._wcs.ny - 0.5
convolved_data = convolve_util.convolve(self._data, smooth=smooth, kernel=kernel)
self.image = self.ax.imshow(convolved_data, cmap=cmap,
interpolation=interpolation,
origin='lower', norm=normalizer,
aspect=aspect, extent=extent)
xmin, xmax = self.ax.get_xbound()
if xmin == 0.0:
self.ax.set_xlim(0.5, xmax)
ymin, ymax = self.ax.get_ybound()
if ymin == 0.0:
self.ax.set_ylim(0.5, ymax)
if hasattr(self, 'colorbar'):
self.colorbar.update()
@auto_refresh
def hide_colorscale(self):
self.image.set_visible(False)
@auto_refresh
def set_nan_color(self, color):
"""
Set the color for NaN pixels.
Parameters
----------
color : str
This can be any valid matplotlib color
"""
from copy import deepcopy
cm = deepcopy(self.image.get_cmap())
cm.set_bad(color)
self.image.set_cmap(cm)
@auto_refresh
def show_rgb(self, filename=None, interpolation='nearest',
vertical_flip=False, horizontal_flip=False, flip=False):
"""
Show a 3-color image instead of the FITS file data.
Parameters
----------
filename, optional
The 3-color image should have exactly the same dimensions
as the FITS file, and will be shown with exactly the same
projection. If FITSFigure was initialized with an
AVM-tagged RGB image, the filename is not needed here.
vertical_flip : str, optional
Whether to vertically flip the RGB image
horizontal_flip : str, optional
Whether to horizontally flip the RGB image
"""
try:
from PIL import Image
except ImportError:
try:
import Image
except ImportError:
raise ImportError("The Python Imaging Library (PIL) is required to read in RGB images")
if flip:
log.warning("Note that show_rgb should now correctly flip RGB images, so the flip= argument is now deprecated. If you still need to flip an image vertically or horizontally, you can use the vertical_flip= and horizontal_flip arguments instead.")
if filename is None:
if hasattr(self, '_rgb_image'):
image = Image.open(self._rgb_image)
else:
raise Exception("Need to specify the filename of an RGB image")
else:
image = Image.open(filename)
if vertical_flip:
image = image.transpose(Image.FLIP_TOP_BOTTOM)
if horizontal_flip:
image = image.transpose(Image.FLIP_LEFT_RIGHT)
self.image = self.ax.imshow(image,
interpolation=interpolation,
origin='lower')
@auto_refresh
def show_contour(self, data=None, hdu=0, layer=None, levels=5,
filled=False, cmap=None, colors=None, returnlevels=False,
convention=None, dimensions=[0, 1], slices=[],
smooth=None, kernel='gauss', overlap=False, **kwargs):
"""
Overlay contours on the current plot.
Parameters
----------
data : see below
The FITS file to plot contours for. The following data types can be
passed:
string
astropy.io.fits.PrimaryHDU
astropy.io.fits.ImageHDU
astropy.wcs.WCS
np.ndarray
hdu : int, optional
By default, the image in the primary HDU is read in. If a
different HDU is required, use this argument.
layer : str, optional
The name of the contour layer. This is useful for giving
custom names to layers (instead of contour_set_n) and for
replacing existing layers.
levels : int or list, optional
This can either be the number of contour levels to compute
(if an integer is provided) or the actual list of contours
to show (if a list of floats is provided)
filled : str, optional
Whether to show filled or line contours
cmap : str, optional
The colormap to use for the contours
colors : str or tuple, optional
If a single string is provided, all contour levels will be
shown in this color. If a tuple of strings is provided,
each contour will be colored according to the corresponding
tuple element.
returnlevels : str, optional
Whether to return the list of contours to the caller.
convention : str, optional
This is used in cases where a FITS header can be interpreted
in multiple ways. For example, for files with a -CAR
projection and CRVAL2=0, this can be set to 'wells' or
'calabretta' to choose the appropriate convention.
dimensions : tuple or list, optional
The index of the axes to use if the data has more than three
dimensions.
slices : tuple or list, optional
If a FITS file with more than two dimensions is specified,
then these are the slices to extract. If all extra dimensions
only have size 1, then this is not required.
smooth : int or tuple, optional
Default smoothing scale is 3 pixels across. User can define
whether they want an NxN kernel (integer), or NxM kernel
(tuple). This argument corresponds to the 'gauss' and 'box'
smoothing kernels.
kernel : { 'gauss' , 'box' , numpy.array }, optional
Default kernel used for smoothing is 'gauss'. The user can
specify if they would prefer 'gauss', 'box', or a custom
kernel. All kernels are normalized to ensure flux retention.
overlap str, optional
Whether to include only contours that overlap with the image
area. This significantly speeds up the drawing of contours and
reduces file size when using a file for the contours covering
a much larger area than the image.
kwargs
Additional keyword arguments (such as alpha, linewidths, or
linestyles) will be passed on directly to Matplotlib's
:meth:`~matplotlib.axes.Axes.contour` or
:meth:`~matplotlib.axes.Axes.contourf` methods. For more
information on these additional arguments, see the *Optional
keyword arguments* sections in the documentation for those
methods.
"""
if layer:
self.remove_layer(layer, raise_exception=False)
if cmap:
cmap = plt.cm.get_cmap(cmap)
elif not colors:
cmap = plt.cm.get_cmap('viridis')
if data is not None:
data_contour, header_contour, wcs_contour, wcsaxes_slices = \
self._get_hdu(data, hdu, False, convention=convention,
dimensions=dimensions, slices=slices)
else:
data_contour = self._data
header_contour = self._header
wcs_contour = self._wcs
wcs_contour.nx = header_contour['NAXIS%i' % (dimensions[0] + 1)]
wcs_contour.ny = header_contour['NAXIS%i' % (dimensions[1] + 1)]
image_contour = convolve_util.convolve(data_contour, smooth=smooth, kernel=kernel)
if type(levels) == int:
interval = AsymmetricPercentileInterval(0.25, 99.75, n_samples=10000)
try:
vmin_auto, vmax_auto = interval.get_limits(image_contour)
except IndexError: # no valid values
vmin_auto = vmax_auto = 0
levels = np.linspace(vmin_auto, vmax_auto, levels)
if wcs_contour.wcs.ctype[self.x] == 'PIXEL' or wcs_contour.wcs.ctype[self.y] == 'PIXEL':
frame = 'pixel'
else:
frame = wcs_contour
if filled:
c = self.ax.contourf(image_contour, levels,
transform=self.ax.get_transform(frame),
cmap=cmap,
colors=colors, **kwargs)
else:
c = self.ax.contour(image_contour, levels,
transform=self.ax.get_transform(frame),
cmap=cmap,
colors=colors, **kwargs)
if layer:
contour_set_name = layer
else:
self._contour_counter += 1
contour_set_name = 'contour_set_' + str(self._contour_counter)
self._layers[contour_set_name] = c
if returnlevels:
return levels
@auto_refresh
def show_vectors(self, pdata, adata, phdu=0, ahdu=0, step=1, scale=1,
rotate=0, cutoff=0, units='degrees', layer=None,
convention=None, dimensions=[0, 1], slices=[], **kwargs):
"""
Overlay vectors on the current plot.
Parameters
----------
pdata : see below
The FITS file specifying the magnitude of vectors. The following data types can be passed:
string
astropy.io.fits.PrimaryHDU
astropy.io.fits.ImageHDU
astropy.wcs.WCS
np.ndarray
adata : see below
The FITS file specifying the angle of vectors. The following data types can be passed:
string
astropy.io.fits.PrimaryHDU
astropy.io.fits.ImageHDU
astropy.wcs.WCS
np.ndarray
phdu : int, optional
By default, the image in the primary HDU is read in. If a
different HDU is required for pdata, use this argument.
ahdu : int, optional
By default, the image in the primary HDU is read in. If a
different HDU is required for adata, use this argument.
step : int, optional
Derive a vector only from every 'step' pixels. You will
normally want this to be >1 to get sensible vector spacing.
scale : int, optional
The length, in pixels, of a vector with magnitude 1 in the image
specified by pdata. If pdata specifies fractional polarization,
make this comparable to step.
rotate : float, optional
An angle to rotate by, in units the same as those of the angle map.
cutoff : float, optional
The value of magnitude below which no
vectors should be plotted. The default value, zero,
excludes negative-length and NaN-masked data.
units : str, optional
Units to assume for the angle map. Valid values are 'degrees'
(the default) or 'radians' (or anything else), which will not
apply a scaling factor of pi/180 to the angle data.
layer : str, optional
The name of the vector layer. This is useful for giving
custom names to layers (instead of vector_set_n) and for
replacing existing layers.
convention : str, optional
This is used in cases where a FITS header can be interpreted
in multiple ways. For example, for files with a -CAR
projection and CRVAL2=0, this can be set to 'wells' or
'calabretta' to choose the appropriate convention.
dimensions : tuple or list, optional
The index of the axes to use if the data has more than three
dimensions.
slices : tuple or list, optional
If a FITS file with more than two dimensions is specified,
then these are the slices to extract. If all extra dimensions
only have size 1, then this is not required.
kwargs
Additional keyword arguments (such as alpha, linewidths, or color)
which are passed to Matplotlib's
:class:`~matplotlib.collections.LineCollection` class, and can be used to
control the appearance of the lines. For more
information on these additional arguments, see the *Optional
keyword arguments* sections in the documentation for those
methods.
"""
# over-ride default color (none) that will otherwise be set by
# show_lines()
if 'color' not in kwargs:
kwargs.setdefault('color', 'black')
if layer:
self.remove_layer(layer, raise_exception=False)
data_p, header_p, wcs_p, slices_p = \
self._get_hdu(pdata, phdu, False, convention=convention,
dimensions=dimensions, slices=slices)
data_a, header_a, wcs_a, slices_a = \
self._get_hdu(adata, ahdu, False, convention=convention,
dimensions=dimensions, slices=slices)
# TODO: use slices correctly
wcs_p.nx = header_p['NAXIS%i' % (dimensions[0] + 1)]
wcs_p.ny = header_p['NAXIS%i' % (dimensions[1] + 1)]
wcs_a.nx = header_a['NAXIS%i' % (dimensions[0] + 1)]
wcs_a.ny = header_a['NAXIS%i' % (dimensions[1] + 1)]
if (wcs_p.nx != wcs_a.nx or wcs_p.ny != wcs_a.ny):
raise Exception("Angle and magnitude images must be same size")
angle = data_a + rotate
if units == 'degrees':
angle = np.radians(angle)
linelist = []
for y in range(0, wcs_p.ny, step):
for x in range(0, wcs_p.nx, step):
if data_p[y, x] > cutoff and np.isfinite(angle[y, x]):
r = data_p[y, x] * 0.5 * scale
a = angle[y, x]
x1 = x + r * np.sin(a)
y1 = y - r * np.cos(a)
x2 = x - r * np.sin(a)
y2 = y + r * np.cos(a)
x_world, y_world = self.pixel2world([x1, x2], [y1, y2], wcs=wcs_p)
line = np.array([x_world, y_world])
linelist.append(line)
if layer:
vector_set_name = layer
else:
self._vector_counter += 1
vector_set_name = 'vector_set_' + str(self._vector_counter)
# Use show_lines to finish the process off
self.show_lines(linelist, layer=vector_set_name, **kwargs)
# This method plots markers. The input should be an Nx2 array with WCS
# coordinates in degree format.
@auto_refresh
def show_markers(self, xw, yw, layer=False, coords_frame='world', **kwargs):
"""
Overlay markers on the current plot.
Parameters
----------
xw : list or `~numpy.ndarray`
The x positions of the markers (in world coordinates)
yw : list or `~numpy.ndarray`
The y positions of the markers (in world coordinates)
layer : str, optional
The name of the scatter layer. This is useful for giving
custom names to layers (instead of marker_set_n) and for
replacing existing layers.
coords_frame : 'pixel' or 'world'
The reference frame in which the coordinates are defined. This is
used to interpret the values of ``xw`` and ``yw``.
kwargs
Additional keyword arguments (such as marker, facecolor,
edgecolor, alpha, or linewidth) will be passed on directly to
Matplotlib's :meth:`~matplotlib.axes.Axes.scatter` method (in
particular, have a look at the *Optional keyword arguments* in the
documentation for that method).
"""
if 'c' not in kwargs:
kwargs.setdefault('edgecolor', 'red')
kwargs.setdefault('facecolor', 'none')
kwargs.setdefault('s', 30)
if layer:
self.remove_layer(layer, raise_exception=False)
s = self.ax.scatter(xw, yw, transform=self.ax.get_transform(coords_frame), **kwargs)
if layer:
marker_set_name = layer
else:
self._scatter_counter += 1
marker_set_name = 'marker_set_' + str(self._scatter_counter)
self._layers[marker_set_name] = s
# Show circles. Different from markers as this method allows more
# definitions for the circles.
@auto_refresh
def show_circles(self, xw, yw, radius, layer=False, coords_frame='world', zorder=None, **kwargs):
"""
Overlay circles on the current plot.
Parameters
----------
xw : list or `~numpy.ndarray`
The x positions of the centers of the circles (in world coordinates)
yw : list or `~numpy.ndarray`
The y positions of the centers of the circles (in world coordinates)
radius : int or float or list or `~numpy.ndarray`
The radii of the circles (in world coordinates)
layer : str, optional
The name of the circle layer. This is useful for giving
custom names to layers (instead of circle_set_n) and for
replacing existing layers.
coords_frame : 'pixel' or 'world'
The reference frame in which the coordinates are defined. This is
used to interpret the values of ``xw`` and ``yw``.
kwargs
Additional keyword arguments (such as facecolor, edgecolor, alpha,
or linewidth) are passed to Matplotlib
:class:`~matplotlib.collections.PatchCollection` class, and can be
used to control the appearance of the circles.
"""
xw, yw, radius = uniformize_1d(xw, yw, radius)
if 'facecolor' not in kwargs:
kwargs.setdefault('facecolor', 'none')
if layer:
self.remove_layer(layer, raise_exception=False)
if coords_frame not in ['pixel', 'world']:
raise ValueError("coords_frame should be set to 'pixel' or 'world'")
# While we could plot the shape using the get_transform('world') mode
# from WCSAxes, the issue is that the rotation angle is also measured in
# world coordinates so will not be what the user is expecting. So we allow the user to specify the reference frame for the coordinates and for the rotation.
if coords_frame == 'pixel':
x, y = xw, yw
r = radius
else:
x, y = self.world2pixel(xw, yw)
pix_scale = proj_plane_pixel_scales(self._wcs)
sx, sy = pix_scale[self.x], pix_scale[self.y]
r = radius / np.sqrt(sx * sy)
patches = []
for i in range(len(xw)):
patches.append(Circle((x[i], y[i]), radius=r[i]))
# Due to bugs in matplotlib, we need to pass the patch properties
# directly to the PatchCollection rather than use match_original.
p = PatchCollection(patches, **kwargs)
if zorder is not None:
p.zorder = zorder
c = self.ax.add_collection(p)
if layer:
circle_set_name = layer
else:
self._circle_counter += 1
circle_set_name = 'circle_set_' + str(self._circle_counter)
self._layers[circle_set_name] = c
@auto_refresh
def show_ellipses(self, xw, yw, width, height, angle=0, layer=False,
zorder=None, coords_frame='world', **kwargs):
"""
Overlay ellipses on the current plot.
Parameters
----------
xw : list or `~numpy.ndarray`
The x positions of the centers of the ellipses (in world coordinates)
yw : list or `~numpy.ndarray`
The y positions of the centers of the ellipses (in world coordinates)
width : int or float or list or `~numpy.ndarray`
The width of the ellipse (in world coordinates)
height : int or float or list or `~numpy.ndarray`
The height of the ellipse (in world coordinates)
angle : int or float or list or `~numpy.ndarray`, optional
rotation in degrees (anti-clockwise). Default
angle is 0.0.
layer : str, optional
The name of the ellipse layer. This is useful for giving
custom names to layers (instead of ellipse_set_n) and for
replacing existing layers.
coords_frame : 'pixel' or 'world'
The reference frame in which the coordinates are defined. This is
used to interpret the values of ``xw``, ``yw``, ``width``, and
``height``.
kwargs
Additional keyword arguments (such as facecolor, edgecolor, alpha,
or linewidth) are passed to Matplotlib
:class:`~matplotlib.collections.PatchCollection` class, and can be
used to control the appearance of the ellipses.
"""
xw, yw, width, height, angle = uniformize_1d(xw, yw, width, height, angle)
if 'facecolor' not in kwargs:
kwargs.setdefault('facecolor', 'none')
if 'edgecolor' not in kwargs:
kwargs.setdefault('edgecolor', 'black')
if layer:
self.remove_layer(layer, raise_exception=False)
if coords_frame not in ['pixel', 'world']:
raise ValueError("coords_frame should be set to 'pixel' or 'world'")
# While we could plot the shape using the get_transform('world') mode
# from WCSAxes, the issue is that the rotation angle is also measured in
# world coordinates so will not be what the user is expecting. So we allow the user to specify the reference frame for the coordinates and for the rotation.
if coords_frame == 'pixel':
x, y = xw, yw
w = width
h = height
a = angle
transform = self.ax.transData
else:
x, y = self.world2pixel(xw, yw)
pix_scale = proj_plane_pixel_scales(self._wcs)
sx, sy = pix_scale[self.x], pix_scale[self.y]
w = width / sx
h = height / sy
a = angle
transform = self.ax.transData
patches = []
for i in range(len(x)):
patches.append(Ellipse((x[i], y[i]), width=w[i], height=h[i], angle=a[i]))
# Due to bugs in matplotlib, we need to pass the patch properties
# directly to the PatchCollection rather than use match_original.
p = PatchCollection(patches, transform=transform, **kwargs)
if zorder is not None:
p.zorder = zorder
c = self.ax.add_collection(p)
if layer:
ellipse_set_name = layer
else:
self._ellipse_counter += 1
ellipse_set_name = 'ellipse_set_' + str(self._ellipse_counter)
self._layers[ellipse_set_name] = c
@auto_refresh
def show_rectangles(self, xw, yw, width, height, angle=0, layer=False,
zorder=None, coords_frame='world', **kwargs):
"""
Overlay rectangles on the current plot.
Parameters
----------
xw : list or `~numpy.ndarray`
The x positions of the centers of the rectangles (in world coordinates)
yw : list or `~numpy.ndarray`
The y positions of the centers of the rectangles (in world coordinates)
width : int or float or list or `~numpy.ndarray`
The width of the rectangle (in world coordinates)
height : int or float or list or `~numpy.ndarray`
The height of the rectangle (in world coordinates)
angle : int or float or list or `~numpy.ndarray`, optional
rotation in degrees (anti-clockwise). Default
angle is 0.0.
layer : str, optional
The name of the rectangle layer. This is useful for giving
custom names to layers (instead of rectangle_set_n) and for
replacing existing layers.
coords_frame : 'pixel' or 'world'
The reference frame in which the coordinates are defined. This is
used to interpret the values of ``xw``, ``yw``, ``width``, and
``height``.
kwargs
Additional keyword arguments (such as facecolor, edgecolor, alpha,
or linewidth) are passed to Matplotlib
:class:`~matplotlib.collections.PatchCollection` class, and can be
used to control the appearance of the rectangles.
"""
xw, yw, width, height, angle = uniformize_1d(xw, yw, width, height, angle)
if 'facecolor' not in kwargs:
kwargs.setdefault('facecolor', 'none')
if layer:
self.remove_layer(layer, raise_exception=False)
if coords_frame not in ['pixel', 'world']:
raise ValueError("coords_frame should be set to 'pixel' or 'world'")
# While we could plot the shape using the get_transform('world') mode
# from WCSAxes, the issue is that the rotation angle is also measured in
# world coordinates so will not be what the user is expecting. So we
# allow the user to specify the reference frame for the coordinates and
# for the rotation.
if coords_frame == 'pixel':
x, y = xw, yw
w = width
h = height
a = angle
transform = self.ax.transData
else:
x, y = self.world2pixel(xw, yw)
pix_scale = proj_plane_pixel_scales(self._wcs)
sx, sy = pix_scale[self.x], pix_scale[self.y]
w = width / sx
h = height / sy
a = angle
transform = self.ax.transData
x = x - w / 2.
y = y - h / 2.
patches = []
for i in range(len(x)):
patches.append(Rectangle((x[i], y[i]), width=w[i], height=h[i], angle=a[i]))
# Due to bugs in matplotlib, we need to pass the patch properties
# directly to the PatchCollection rather than use match_original.
p = PatchCollection(patches, transform=transform, **kwargs)
if zorder is not None:
p.zorder = zorder
c = self.ax.add_collection(p)
if layer:
rectangle_set_name = layer
else:
self._rectangle_counter += 1
rectangle_set_name = 'rectangle_set_' + str(self._rectangle_counter)
self._layers[rectangle_set_name] = c
@auto_refresh
def show_lines(self, line_list, layer=False, zorder=None, **kwargs):
"""
Overlay lines on the current plot.
Parameters
----------
line_list : list
A list of one or more 2xN numpy arrays which contain
the [x, y] positions of the vertices in world coordinates.
layer : str, optional
The name of the line(s) layer. This is useful for giving
custom names to layers (instead of line_set_n) and for
replacing existing layers.
kwargs
Additional keyword arguments (such as color, offsets, linestyle,
or linewidth) are passed to Matplotlib
:class:`~matplotlib.collections.LineCollection` class, and can be used to
control the appearance of the lines.
"""
if 'color' not in kwargs:
kwargs.setdefault('color', 'none')
if layer:
self.remove_layer(layer, raise_exception=False)
lines = []
for line in line_list:
xw, yw = line[0, :], line[1, :]
lines.append(np.column_stack((xw, yw)))
lc = LineCollection(lines, transform=self.ax.get_transform('world'), **kwargs)
if zorder is not None:
lc.zorder = zorder
c = self.ax.add_collection(lc)
if layer:
line_set_name = layer
else:
self._linelist_counter += 1
line_set_name = 'line_set_' + str(self._linelist_counter)
self._layers[line_set_name] = c
@auto_refresh
def show_arrows(self, x, y, dx, dy, width='auto', head_width='auto',
head_length='auto', length_includes_head=True, layer=False,
zorder=None, **kwargs):
"""
Overlay arrows on the current plot.
Parameters
----------
x, y, dx, dy : float or list or `~numpy.ndarray`
Origin and displacement of the arrows in world coordinates.
These can either be scalars to plot a single arrow, or lists or
arrays to plot multiple arrows.
width : float, optional
The width of the arrow body, in pixels (default: 2% of the
arrow length)
head_width : float, optional
The width of the arrow head, in pixels (default: 5% of the
arrow length)
head_length : float, optional
The length of the arrow head, in pixels (default: 5% of the
arrow length)
length_includes_head : bool, optional
Whether the head includes the length
layer : str, optional
The name of the arrow(s) layer. This is useful for giving
custom names to layers (instead of line_set_n) and for
replacing existing layers.
kwargs
Additional keyword arguments (such as facecolor, edgecolor, alpha,
or linewidth) are passed to Matplotlib
:class:`~matplotlib.collections.PatchCollection` class, and can be
used to control the appearance of the arrows.
"""
x, y, dx, dy = uniformize_1d(x, y, dx, dy)
if layer:
self.remove_layer(layer, raise_exception=False)
arrows = []
# Here we don't make use of WCSAxes.get_transform('world') because
# otherwise the arrow heads will be distored. Instead, we work in pixel
# coordinates.
for i in range(len(x)):
xp1, yp1 = self.world2pixel(x[i], y[i])
xp2, yp2 = self.world2pixel(x[i] + dx[i], y[i] + dy[i])
if width == 'auto':
width = 0.02 * np.sqrt((xp2 - xp1) ** 2 + (yp2 - yp1) ** 2)
if head_width == 'auto':
head_width = 0.1 * np.sqrt((xp2 - xp1) ** 2 + (yp2 - yp1) ** 2)
if head_length == 'auto':
head_length = 0.1 * np.sqrt((xp2 - xp1) ** 2 + (yp2 - yp1) ** 2)
arrows.append(FancyArrow(xp1, yp1, xp2 - xp1, yp2 - yp1,
width=width, head_width=head_width,
head_length=head_length,
length_includes_head=length_includes_head)
)
# Due to bugs in matplotlib, we need to pass the patch properties
# directly to the PatchCollection rather than use match_original.
p = PatchCollection(arrows, **kwargs)
if zorder is not None:
p.zorder = zorder
c = self.ax.add_collection(p)
if layer:
line_set_name = layer
else:
self._linelist_counter += 1
line_set_name = 'arrow_set_' + str(self._linelist_counter)
self._layers[line_set_name] = c
@auto_refresh
def show_polygons(self, polygon_list, layer=False, zorder=None, **kwargs):
"""
Overlay polygons on the current plot.
Parameters
----------
polygon_list : list or tuple
A list of one or more 2xN or Nx2 Numpy arrays which contain
the [x, y] positions of the vertices in world coordinates.
Note that N should be greater than 2.
layer : str, optional
The name of the circle layer. This is useful for giving
custom names to layers (instead of circle_set_n) and for
replacing existing layers.
kwargs
Additional keyword arguments (such as facecolor, edgecolor, alpha,
or linewidth) are passed to Matplotlib
:class:`~matplotlib.collections.PatchCollection` class, and can be
used to control the appearance of the polygons.
"""
if 'facecolor' not in kwargs:
kwargs.setdefault('facecolor', 'none')
if layer:
self.remove_layer(layer, raise_exception=False)
if type(polygon_list) not in [list, tuple]:
raise Exception("polygon_list should be a list or tuple of Numpy arrays")
pix_polygon_list = []
for polygon in polygon_list:
if type(polygon) is not np.ndarray:
raise Exception("Polygon should be given as a Numpy array")
if polygon.shape[0] == 2 and polygon.shape[1] > 2:
xw = polygon[0, :]
yw = polygon[1, :]
elif polygon.shape[0] > 2 and polygon.shape[1] == 2:
xw = polygon[:, 0]
yw = polygon[:, 1]
else:
raise Exception("Polygon should have dimensions 2xN or Nx2 with N>2")
pix_polygon_list.append(np.column_stack((xw, yw)))
patches = []
for i in range(len(pix_polygon_list)):
patches.append(Polygon(pix_polygon_list[i], **kwargs))
# Due to bugs in matplotlib, we need to pass the patch properties
# directly to the PatchCollection rather than use match_original.
p = PatchCollection(patches, transform=self.ax.get_transform('world'), **kwargs)
if zorder is not None:
p.zorder = zorder
c = self.ax.add_collection(p)
if layer:
poly_set_name = layer
else:
self._poly_counter += 1
poly_set_name = 'poly_set_' + str(self._poly_counter)
self._layers[poly_set_name] = c
@auto_refresh
@fixdocstring
def add_label(self, x, y, text, relative=False, color='black',
family=None, style=None, variant=None, stretch=None,
weight=None, size=None, fontproperties=None,
horizontalalignment='center', verticalalignment='center',
layer=None, **kwargs):
"""
Add a text label.
Parameters
----------
x, y : float
Coordinates of the text label
text : str
The label
relative : str, optional
Whether the coordinates are to be interpreted as world
coordinates (e.g. RA/Dec or longitude/latitude), or
coordinates relative to the axes (where 0.0 is left or bottom
and 1.0 is right or top).
common: color, family, style, variant, stretch, weight, size, fontproperties, horizontalalignment, verticalalignment
"""
if layer:
self.remove_layer(layer, raise_exception=False)
# Can't pass fontproperties=None to text. Only pass it if it is not None.
if fontproperties:
kwargs['fontproperties'] = fontproperties
if not np.isscalar(x):
raise Exception("x should be a single value")
if not np.isscalar(y):
raise Exception("y should be a single value")
if not np.isscalar(text):
raise Exception("text should be a single value")
if relative:
lc = self.ax.text(x, y, text, color=color,
family=family, style=style, variant=variant,
stretch=stretch, weight=weight, size=size,
horizontalalignment=horizontalalignment,
verticalalignment=verticalalignment,
transform=self.ax.transAxes, **kwargs)
else:
lc = self.ax.text(x, y, text, color=color,
family=family, style=style, variant=variant,
stretch=stretch, weight=weight, size=size,
horizontalalignment=horizontalalignment,
verticalalignment=verticalalignment,
transform=self.ax.get_transform('world'),
**kwargs)
if layer:
label_name = layer
else:
self._label_counter += 1
label_name = 'label_' + str(self._label_counter)
self._layers[label_name] = lc
def set_auto_refresh(self, refresh):
"""
Set whether the display should refresh after each method call.
Parameters
----------
refresh : bool
Whether to refresh the display every time a FITSFigure method is
called. This defaults to `True` if and only if APLpy is being used
from IPython and the Matplotlib backend is interactive.
"""
if refresh is None:
if matplotlib.is_interactive():
try:
get_ipython()
except NameError:
refresh = False
else:
refresh = True
else:
refresh = False
elif not isinstance(refresh, bool):
raise TypeError("refresh argument should be boolean or `None`")
self._figure._auto_refresh = refresh
def refresh(self, force=True):
"""
Refresh the display.
Parameters
----------
force : str, optional
If set to False, refresh() will only have an effect if
auto refresh is on. If set to True, the display will be
refreshed whatever the auto refresh setting is set to.
The default is True.
"""
if self._figure._auto_refresh or force:
self._figure.canvas.draw()
def save(self, filename, dpi=None, transparent=False, adjust_bbox=True,
max_dpi=300, format=None):
"""
Save the current figure to a file.
Parameters
----------
filename : str or fileobj
The name of the file to save the plot to. This can be for
example a PS, EPS, PDF, PNG, JPEG, or SVG file. Note that it
is also possible to pass file-like object.
dpi : float, optional
The output resolution, in dots per inch. If the output file
is a vector graphics format (such as PS, EPS, PDF or SVG) only
the image itself will be rasterized. If the output is a PS or
EPS file and no dpi is specified, the dpi is automatically
calculated to match the resolution of the image. If this value is
larger than max_dpi, then dpi is set to max_dpi.
transparent : str, optional
Whether to preserve transparency
adjust_bbox : str, optional
Auto-adjust the bounding box for the output
max_dpi : float, optional
The maximum resolution to output images at. If no maximum is
wanted, enter None or 0.
format : str, optional
By default, APLpy tries to guess the file format based on the
file extension, but the format can also be specified
explicitly. Should be one of 'eps', 'ps', 'pdf', 'svg', 'png'.
"""
if isinstance(filename, str) and format is None:
format = os.path.splitext(filename)[1].lower()[1:]
if dpi is None and format in ['eps', 'ps', 'pdf']:
width = self.ax.get_position().width * self._figure.get_figwidth()
interval = self.ax.get_xlim()
nx = interval[1] - interval[0]
if max_dpi:
dpi = np.minimum(nx / width, max_dpi)
else:
dpi = nx / width
log.info("Auto-setting resolution to %g dpi" % dpi)
if adjust_bbox:
self._figure.savefig(filename, dpi=dpi, transparent=transparent,
bbox_inches='tight', format=format)
else:
self._figure.savefig(filename, dpi=dpi, transparent=transparent,
format=format)
def _initialize_view(self):
self.ax.set_xlim(-0.5, self._wcs.nx - 0.5)
self.ax.set_ylim(-0.5, self._wcs.ny - 0.5)
def _get_invert_default(self):
return self._figure.apl_grayscale_invert_default
def _get_colormap_default(self):
return self._figure.apl_colorscale_cmap_default
@auto_refresh
def set_theme(self, theme):
"""
Set the axes, ticks, grid, and image colors to a certain style
(experimental).
Parameters
----------
theme : str
The theme to use. At the moment, this can be 'pretty' (for
viewing on-screen) and 'publication' (which makes the ticks
and grid black, and displays the image in inverted grayscale)
"""
if theme == 'pretty':
self.frame.set_color('black')
self.frame.set_linewidth(1.0)
if self.ax.coords[self.x].ticks.get_tick_out():
self.ticks.set_color('black')
else:
self.ticks.set_color('white')
self._figure.apl_grayscale_invert_default = False
self._figure.apl_colorscale_cmap_default = 'viridis'
if self.image:
self.image.set_cmap(cmap=plt.cm.get_cmap('viridis'))
elif theme == 'publication':
self.frame.set_color('black')
self.frame.set_linewidth(1.0)
self.ticks.set_color('black')
self.ticks.set_length(7)
self._figure.apl_grayscale_invert_default = True
self._figure.apl_colorscale_cmap_default = 'gist_heat'
if self.image:
self.image.set_cmap(cmap=plt.cm.get_cmap('gist_yarg'))
def world2pixel(self, xw, yw, wcs=None):
"""
Convert world to pixel coordinates.
Parameters
----------
xw : float or iterable
x world coordinate
yw : float or iterable
y world coordinate
Returns
-------
xp : float or iterable
x pixel coordinate
yp : float or iterable
y pixel coordinate
"""
if wcs is None:
wcs = self._wcs
return wcs.wcs_world2pix(xw, yw, 0)
def pixel2world(self, xp, yp, wcs=None):
"""
Convert pixel to world coordinates.
Parameters
----------
xp : float or iterable
x pixel coordinate
yp : float or iterable
y pixel coordinate
Returns
-------
xw : float or iterable
x world coordinate
yw : float or iterable
y world coordinate
"""
if wcs is None:
wcs = self._wcs
return wcs.wcs_pix2world(xp, yp, 0)
@auto_refresh
def add_grid(self):
"""
Add a coordinate to the current figure.
Once this method has been run, a grid attribute becomes available,
and can be used to control the aspect of the grid::
>>> f = aplpy.FITSFigure(...)
>>> ...
>>> f.add_grid()
>>> f.grid.set_color('white')
>>> f.grid.set_alpha(0.5)
>>> ...
"""
if hasattr(self, 'grid'):
raise Exception("Grid already exists")
try:
self.grid = Grid(self)
self.grid.show()
except Exception:
del self.grid
raise
@auto_refresh
def remove_grid(self):
"""
Removes the grid from the current figure.
"""
self.grid.hide()
del self.grid
@auto_refresh
def add_beam(self, *args, **kwargs):
"""
Add a beam to the current figure.
Once this method has been run, a beam attribute becomes available,
and can be used to control the aspect of the beam::
>>> f = aplpy.FITSFigure(...)
>>> ...
>>> f.add_beam()
>>> f.beam.set_color('white')
>>> f.beam.set_hatch('+')
>>> ...
If more than one beam is added, the beam object becomes a list. In
this case, to control the aspect of one of the beams, you will need to
specify the beam index::
>>> ...
>>> f.beam[2].set_hatch('/')
>>> ...
"""
# Initalize the beam and set parameters
b = Beam(self)
b.show(*args, **kwargs)
if hasattr(self, 'beam'):
if type(self.beam) is list:
self.beam.append(b)
else:
self.beam = [self.beam, b]
else:
self.beam = b
@auto_refresh
def remove_beam(self, beam_index=None):
"""
Removes the beam from the current figure.
If more than one beam is present, the index of the beam should be
specified using beam_index=
"""
if type(self.beam) is list:
if beam_index is None:
raise Exception("More than one beam present - use beam_index= to specify which one to remove")
else:
b = self.beam.pop(beam_index)
b._remove()
del b
# If only one beam is present, remove containing list
if len(self.beam) == 1:
self.beam = self.beam[0]
else:
self.beam._remove()
del self.beam
@auto_refresh
def add_scalebar(self, length, *args, **kwargs):
"""
Add a scalebar to the current figure.
Once this method has been run, a scalebar attribute becomes
available, and can be used to control the aspect of the scalebar::
>>> f = aplpy.FITSFigure(...)
>>> ...
>>> f.add_scalebar(0.01) # length has to be specified
>>> f.scalebar.set_label('100 AU')
>>> ...
Parameters
----------
length : float, or quantity
The length of the scalebar in degrees, an angular quantity, or angular unit
label : str, optional
Label to place below the scalebar
corner : int, optional
Where to place the scalebar. Acceptable values are:, 'left',
'right', 'top', 'bottom', 'top left', 'top right', 'bottom
left' (default), 'bottom right'
frame : str, optional
Whether to display a frame behind the scalebar (default is False)
kwargs
Additional arguments are passed to the matplotlib Rectangle and
Text classes. See the matplotlib documentation for more details.
In cases where the same argument exists for the two objects, the
argument is passed to both the Text and Rectangle instance.
"""
if hasattr(self, 'scalebar'):
raise Exception("Scalebar already exists")
try:
self.scalebar = Scalebar(self)
self.scalebar.show(length, *args, **kwargs)
except Exception:
del self.scalebar
raise
@auto_refresh
def remove_scalebar(self):
"""
Removes the scalebar from the current figure.
"""
self.scalebar._remove()
del self.scalebar
@auto_refresh
def add_colorbar(self, *args, **kwargs):
"""
Add a colorbar to the current figure.
Once this method has been run, a colorbar attribute becomes
available, and can be used to control the aspect of the colorbar::
>>> f = aplpy.FITSFigure(...)
>>> ...
>>> f.add_colorbar()
>>> f.colorbar.set_width(0.3)
>>> f.colorbar.set_location('top')
>>> ...
"""
if hasattr(self, 'colorbar'):
raise Exception("Colorbar already exists")
if self.image is None:
raise Exception("No image is shown, so a colorbar cannot be displayed")
try:
self.colorbar = Colorbar(self)
self.colorbar.show(*args, **kwargs)
except Exception:
del self.colorbar
raise
@auto_refresh
def remove_colorbar(self):
"""
Removes the colorbar from the current figure.
"""
self.colorbar._remove()
del self.colorbar
def close(self):
"""
Close the figure and free up the memory.
"""
plt.close(self._figure)
savefig = save
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/decorators.py 0000664 0001750 0001750 00000005431 00000000000 014671 0 ustar 00tom tom import threading
from functools import wraps
mydata = threading.local()
__all__ = ['auto_refresh', 'fixdocstring']
def auto_refresh(f):
@wraps(f)
def wrapper(*args, **kwargs):
if 'refresh' in kwargs:
refresh = kwargs.pop('refresh')
else:
refresh = True
# The following is necessary rather than using mydata.nesting = 0 at the
# start of the file, because doing the latter caused issues with the Django
# development server.
mydata.nesting = getattr(mydata, 'nesting', 0) + 1
try:
return f(*args, **kwargs)
finally:
mydata.nesting -= 1
if hasattr(args[0], '_figure'):
if refresh and mydata.nesting == 0 and args[0]._figure._auto_refresh:
args[0]._figure.canvas.draw()
return wrapper
doc = {}
doc['size'] = """size : str or int or float, optional
The size of the font. This can either be a numeric value (e.g.
12), giving the size in points, or one of 'xx-small', 'x-small',
'small', 'medium', 'large', 'x-large', or 'xx-large'.
"""
doc['weight'] = """weight : str or int or float, optional
The weight (or boldness) of the font. This can either be a numeric
value in the range 0-1000 or one of 'ultralight', 'light', 'normal',
'regular', 'book', 'medium', 'roman', 'semibold', 'demibold', 'demi',
'bold', 'heavy', 'extra bold', 'black'.
"""
doc['stretch'] = """stretch : str or int or float, optional
The stretching (spacing between letters) for the font. This can either
be a numeric value in the range 0-1000 or one of 'ultra-condensed',
'extra-condensed', 'condensed', 'semi-condensed', 'normal',
'semi-expanded', 'expanded', 'extra-expanded' or 'ultra-expanded'.
"""
doc['family'] = """family : str, optional
The family of the font to use. This can either be a generic font
family name, either 'serif', 'sans-serif', 'cursive', 'fantasy', or
'monospace', or a list of font names in decreasing order of priority.
"""
doc['style'] = """style : str, optional
The font style. This can be 'normal', 'italic' or 'oblique'.
"""
doc['variant'] = """variant : str, optional
The font variant. This can be 'normal' or 'small-caps'
"""
def fixdocstring(func):
lines = func.__doc__.split('\n')
for i, line in enumerate(lines):
if 'common:' in line:
break
header = lines[:i]
footer = lines[i + 1:]
indent = lines[i].index('common:')
common = []
for item in lines[i].split(':')[1].split(','):
if item.strip() in doc:
common.append(" " * indent + doc[item.strip()].replace('\n', '\n' + " " * indent))
docstring = "\n".join(header + common + footer)
func.__doc__ = docstring
return func
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/frame.py 0000664 0001750 0001750 00000001236 00000000000 013615 0 ustar 00tom tom from .decorators import auto_refresh
class Frame(object):
@auto_refresh
def __init__(self, parent):
self.ax = parent.ax
@auto_refresh
def set_linewidth(self, linewidth):
"""
Set line width of the frame.
Parameters
----------
linewidth:
The linewidth to use for the frame.
"""
self.ax.coords.frame.set_linewidth(linewidth)
@auto_refresh
def set_color(self, color):
"""
Set color of the frame.
Parameters
----------
color:
The color to use for the frame.
"""
self.ax.coords.frame.set_color(color)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/grid.py 0000664 0001750 0001750 00000007345 00000000000 013457 0 ustar 00tom tom import numpy as np
from .decorators import auto_refresh
class Grid(object):
@auto_refresh
def __init__(self, parent):
# Save axes and wcs information
self.ax = parent.ax
self._wcs = parent._wcs
self._figure = parent._figure
self.x = parent.x
self.y = parent.y
self.x_unit = self._wcs.wcs.cunit[self.x]
self.y_unit = self._wcs.wcs.cunit[self.y]
self.grid_type = parent.grid_type
# Set defaults
self.default_color = 'white'
self.default_alpha = 0.5
@auto_refresh
def set_xspacing(self, xspacing):
"""
Set the grid line spacing in the longitudinal direction
Parameters
----------
xspacing : float or str
The spacing in the longitudinal direction. To set the spacing
to be the same as the ticks, set this to 'tick'
"""
if xspacing == 'tick':
self.ax.coords[self.x].grid(grid_type=self.grid_type)
elif np.isreal(xspacing):
self.ax.coords[self.x].set_ticks(spacing=xspacing * self.x_unit)
else:
raise ValueError("Grid spacing should be a scalar or 'tick'")
@auto_refresh
def set_yspacing(self, yspacing):
"""
Set the grid line spacing in the latitudinal direction
Parameters
----------
yspacing : { float, str }
The spacing in the latitudinal direction. To set the spacing
to be the same as the ticks, set this to 'tick'
"""
if yspacing == 'tick':
self.ax.coords[self.y].grid(grid_type=self.grid_type)
elif np.isreal(yspacing):
self.ax.coords[self.y].set_ticks(spacing=yspacing * self.y_unit)
else:
raise ValueError("Grid spacing should be a scalar or 'tick'")
@auto_refresh
def set_color(self, color):
"""
Set the color of the grid lines
Parameters
----------
color : str
The color of the grid lines
"""
self.default_color = color
self.ax.coords[self.x].grid(color=color, grid_type=self.grid_type)
self.ax.coords[self.y].grid(color=color, grid_type=self.grid_type)
@auto_refresh
def set_alpha(self, alpha):
"""
Set the alpha (transparency) of the grid lines
Parameters
----------
alpha : float
The alpha value of the grid. This should be a floating
point value between 0 and 1, where 0 is completely
transparent, and 1 is completely opaque.
"""
self.default_alpha = alpha
self.ax.coords[self.x].grid(alpha=alpha, grid_type=self.grid_type)
self.ax.coords[self.y].grid(alpha=alpha, grid_type=self.grid_type)
@auto_refresh
def set_linewidth(self, linewidth):
self.ax.coords[self.x].grid(linewidth=linewidth, grid_type=self.grid_type)
self.ax.coords[self.y].grid(linewidth=linewidth, grid_type=self.grid_type)
@auto_refresh
def set_linestyle(self, linestyle):
self.ax.coords[self.x].grid(linestyle=linestyle, grid_type=self.grid_type)
self.ax.coords[self.y].grid(linestyle=linestyle, grid_type=self.grid_type)
@auto_refresh
def show(self):
self.ax.coords[self.x].grid(grid_type=self.grid_type,
color=self.default_color,
alpha=self.default_alpha)
self.ax.coords[self.y].grid(grid_type=self.grid_type,
color=self.default_color,
alpha=self.default_alpha)
@auto_refresh
def hide(self):
self.ax.coords[self.x].grid(draw_grid=False)
self.ax.coords[self.y].grid(draw_grid=False)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/header.py 0000664 0001750 0001750 00000004375 00000000000 013762 0 ustar 00tom tom from astropy import log
PLATE_CAREE_ERROR = """
Projection is Plate Caree (-CAR) and CRVALy is not zero. This can be interpreted
either according to Wells (1981) or Calabretta (2002). The former defines the
projection as rectilinear regardless of the value of CRVALy, whereas the latter
defines the projection as rectilinear only when CRVALy is zero. You will need to
specify the convention to assume by setting either convention='wells' or
convention='calabretta' when initializing the FITSFigure instance.
""".strip()
def check(header, convention=None, dimensions=[0, 1]):
ix = dimensions[0] + 1
iy = dimensions[1] + 1
# If header does not contain CTYPE keywords, assume that the WCS is
# missing or incomplete, and replace it with a 1-to-1 pixel mapping
if 'CTYPE%i' % ix not in header or 'CTYPE%i' % iy not in header:
log.warning("No WCS information found in header - using pixel coordinates")
header['CTYPE%i' % ix] = 'PIXEL'
header['CTYPE%i' % iy] = 'PIXEL'
header['CRVAL%i' % ix] = 0.
header['CRVAL%i' % iy] = 0.
header['CRPIX%i' % ix] = 0.
header['CRPIX%i' % iy] = 0.
header['CDELT%i' % ix] = 1.
header['CDELT%i' % iy] = 1.
if header['CTYPE%i' % ix][4:] == '-CAR' and header['CTYPE%i' % iy][4:] == '-CAR':
if header['CTYPE%i' % ix][:4] == 'DEC-' or header['CTYPE%i' % ix][1:4] == 'LAT':
ilat = ix
elif header['CTYPE%i' % iy][:4] == 'DEC-' or header['CTYPE%i' % iy][1:4] == 'LAT':
ilat = iy
else:
ilat = None
if ilat is not None and header['CRVAL%i' % ilat] != 0:
if convention == 'calabretta':
pass # we don't need to do anything
elif convention == 'wells':
if 'CDELT%i' % ilat not in header:
raise Exception("Need CDELT%i to be present for wells convention" % ilat)
crpix = header['CRPIX%i' % ilat]
crval = header['CRVAL%i' % ilat]
cdelt = header['CDELT%i' % ilat]
crpix = crpix - crval / cdelt
header['CRPIX%i' % ilat] = crpix
header['CRVAL%i' % ilat] = 0.
else:
raise Exception(PLATE_CAREE_ERROR)
return header
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/layers.py 0000664 0001750 0001750 00000012672 00000000000 014030 0 ustar 00tom tom from collections import OrderedDict
from matplotlib.contour import ContourSet
from matplotlib.collections import RegularPolyCollection, \
PatchCollection, CircleCollection, LineCollection
from .regions import ArtistCollection
from .decorators import auto_refresh
class Layers(object):
def __init__(self):
pass
def _layer_type(self, layer):
if isinstance(self._layers[layer], ContourSet):
return 'contour'
elif isinstance(self._layers[layer], RegularPolyCollection):
return 'collection'
elif isinstance(self._layers[layer], PatchCollection):
return 'collection'
elif isinstance(self._layers[layer], CircleCollection):
return 'collection'
elif isinstance(self._layers[layer], LineCollection):
return 'collection'
elif isinstance(self._layers[layer], ArtistCollection):
return 'collection'
elif hasattr(self._layers[layer], 'remove') and hasattr(self._layers[layer], 'get_visible') and hasattr(self._layers[layer], 'set_visible'):
return 'collection'
else:
raise Exception("Unknown layer type: " +
str(type(self._layers[layer])))
def _initialize_layers(self):
self._layers = OrderedDict()
self._contour_counter = 0
self._vector_counter = 0
self._scatter_counter = 0
self._circle_counter = 0
self._ellipse_counter = 0
self._rectangle_counter = 0
self._linelist_counter = 0
self._region_counter = 0
self._label_counter = 0
self._poly_counter = 0
def list_layers(self):
"""
Print a list of layers to standard output.
"""
layers_list = []
for layer in self._layers:
layer_type = self._layer_type(layer)
if layer_type == 'contour':
visible = self._layers[layer].collections[0].get_visible()
elif layer_type == 'collection':
visible = self._layers[layer].get_visible()
layers_list.append({'name': layer, 'visible': visible})
n_layers = len(layers_list)
if n_layers == 0:
print("\n There are no layers in this figure")
else:
if n_layers == 1:
print("\n There is one layer in this figure:\n")
else:
print("\n There are " + str(n_layers) +
" layers in this figure:\n")
for layer in layers_list:
if layer['visible']:
print(" -> " + layer['name'])
else:
print(" -> " + layer['name'] + " (hidden)")
@auto_refresh
def remove_layer(self, layer, raise_exception=True):
"""
Remove a layer.
Parameters
----------
layer : str
The name of the layer to remove
"""
if layer in self._layers:
layer_type = self._layer_type(layer)
if layer_type == 'contour':
for contour in self._layers[layer].collections:
contour.remove()
self._layers.pop(layer)
elif layer_type == 'collection':
self._layers[layer].remove()
self._layers.pop(layer)
if (layer + '_txt') in self._layers:
self._layers[layer + '_txt'].remove()
self._layers.pop(layer + '_txt')
else:
if raise_exception:
raise Exception("Layer " + layer + " does not exist")
@auto_refresh
def hide_layer(self, layer, raise_exception=True):
"""
Hide a layer.
This differs from remove_layer in that if a layer is hidden
it can be shown again using show_layer.
Parameters
----------
layer : str
The name of the layer to hide
"""
if layer in self._layers:
layer_type = self._layer_type(layer)
if layer_type == 'contour':
for contour in self._layers[layer].collections:
contour.set_visible(False)
elif layer_type == 'collection':
self._layers[layer].set_visible(False)
else:
if raise_exception:
raise Exception("Layer " + layer + " does not exist")
@auto_refresh
def show_layer(self, layer, raise_exception=True):
"""
Show a layer.
This shows a layer previously hidden with hide_layer
Parameters
----------
layer : str
The name of the layer to show
"""
if layer in self._layers:
layer_type = self._layer_type(layer)
if layer_type == 'contour':
for contour in self._layers[layer].collections:
contour.set_visible(True)
elif layer_type == 'collection':
self._layers[layer].set_visible(True)
else:
if raise_exception:
raise Exception("Layer " + layer + " does not exist")
def get_layer(self, layer, raise_exception=True):
"""
Return a layer object.
Parameters
----------
layer : str
The name of the layer to return
"""
if layer in self._layers:
return self._layers[layer]
else:
if raise_exception:
raise Exception("Layer " + layer + " does not exist")
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/overlays.py 0000664 0001750 0001750 00000043014 00000000000 014367 0 ustar 00tom tom import warnings
from mpl_toolkits.axes_grid1.anchored_artists import (AnchoredEllipse,
AnchoredSizeBar)
import numpy as np
from matplotlib.font_manager import FontProperties
from astropy import units as u
from astropy.wcs.utils import proj_plane_pixel_scales
from .decorators import auto_refresh
corners = {}
corners['top right'] = 1
corners['top left'] = 2
corners['bottom left'] = 3
corners['bottom right'] = 4
corners['right'] = 5
corners['left'] = 6
corners['bottom'] = 8
corners['top'] = 9
class Scalebar(object):
def __init__(self, parent):
# Retrieve info from parent figure
self._ax = parent.ax
self._wcs = parent._wcs
self._figure = parent._figure
self._dimensions = [parent.x, parent.y]
# Initialize settings
self._base_settings = {}
self._scalebar_settings = {}
self._label_settings = {}
self._label_settings['fontproperties'] = FontProperties()
# LAYOUT
@auto_refresh
def show(self, length, label=None, corner='bottom right', frame=False,
borderpad=0.4, pad=0.5, **kwargs):
"""
Overlay a scale bar on the image.
Parameters
----------
length : float, or quantity
The length of the scalebar in degrees, an angular quantity, or angular unit
label : str, optional
Label to place below the scalebar
corner : int, optional
Where to place the scalebar. Acceptable values are:, 'left',
'right', 'top', 'bottom', 'top left', 'top right', 'bottom
left' (default), 'bottom right'
frame : str, optional
Whether to display a frame behind the scalebar (default is False)
kwargs
Additional arguments are passed to the matplotlib Rectangle and
Text classes. See the matplotlib documentation for more details.
In cases where the same argument exists for the two objects, the
argument is passed to both the Text and Rectangle instance.
"""
self._length = length
self._base_settings['corner'] = corner
self._base_settings['frame'] = frame
self._base_settings['borderpad'] = borderpad
self._base_settings['pad'] = pad
if isinstance(length, u.Quantity):
length = length.to(u.degree).value
elif isinstance(length, u.Unit):
length = length.to(u.degree)
if self._wcs.is_celestial:
pix_scale = proj_plane_pixel_scales(self._wcs)
sx = pix_scale[self._dimensions[0]]
sy = pix_scale[self._dimensions[1]]
degrees_per_pixel = np.sqrt(sx * sy)
else:
raise ValueError("Cannot show scalebar when WCS is not celestial")
length = length / degrees_per_pixel
try:
self._scalebar.remove()
except Exception:
pass
if isinstance(corner, str):
corner = corners[corner]
self._scalebar = AnchoredSizeBar(self._ax.transData, length, label,
corner, pad=pad, borderpad=borderpad,
sep=5, frameon=frame)
self._ax.add_artist(self._scalebar)
self.set(**kwargs)
@auto_refresh
def _remove(self):
self._scalebar.remove()
@auto_refresh
def hide(self):
"""
Hide the scalebar.
"""
try:
self._scalebar.remove()
except Exception:
pass
@auto_refresh
def set_length(self, length):
"""
Set the length of the scale bar.
"""
self.show(length, **self._base_settings)
self._set_scalebar_properties(**self._scalebar_settings)
self._set_label_properties(**self._scalebar_settings)
@auto_refresh
def set_label(self, label):
"""
Set the label of the scale bar.
"""
self._set_label_properties(text=label)
@auto_refresh
def set_corner(self, corner):
"""
Set where to place the scalebar.
Acceptable values are 'left', 'right', 'top', 'bottom', 'top left',
'top right', 'bottom left' (default), and 'bottom right'.
"""
self._base_settings['corner'] = corner
self.show(self._length, **self._base_settings)
self._set_scalebar_properties(**self._scalebar_settings)
self._set_label_properties(**self._scalebar_settings)
@auto_refresh
def set_frame(self, frame):
"""
Set whether to display a frame around the scalebar.
"""
self._base_settings['frame'] = frame
self.show(self._length, **self._base_settings)
self._set_scalebar_properties(**self._scalebar_settings)
self._set_label_properties(**self._scalebar_settings)
# APPEARANCE
@auto_refresh
def set_linewidth(self, linewidth):
"""
Set the linewidth of the scalebar, in points.
"""
self._set_scalebar_properties(linewidth=linewidth)
@auto_refresh
def set_linestyle(self, linestyle):
"""
Set the linestyle of the scalebar.
Should be one of 'solid', 'dashed', 'dashdot', or 'dotted'.
"""
self._set_scalebar_properties(linestyle=linestyle)
@auto_refresh
def set_alpha(self, alpha):
"""
Set the alpha value (transparency).
This should be a floating point value between 0 and 1.
"""
self._set_scalebar_properties(alpha=alpha)
self._set_label_properties(alpha=alpha)
@auto_refresh
def set_color(self, color):
"""
Set the label and scalebar color.
"""
self._set_scalebar_properties(color=color)
self._set_label_properties(color=color)
@auto_refresh
def set_font(self, family=None, style=None, variant=None, stretch=None,
weight=None, size=None, fontproperties=None):
"""
Set the font of the tick labels
Parameters
----------
common: family, style, variant, stretch, weight, size, fontproperties
Notes
-----
Default values are set by matplotlib or previously set values if
set_font has already been called. Global default values can be set by
editing the matplotlibrc file.
"""
if family:
self._label_settings['fontproperties'].set_family(family)
if style:
self._label_settings['fontproperties'].set_style(style)
if variant:
self._label_settings['fontproperties'].set_variant(variant)
if stretch:
self._label_settings['fontproperties'].set_stretch(stretch)
if weight:
self._label_settings['fontproperties'].set_weight(weight)
if size:
self._label_settings['fontproperties'].set_size(size)
if fontproperties:
self._label_settings['fontproperties'] = fontproperties
self._set_label_properties(fontproperties=self._label_settings['fontproperties'])
@auto_refresh
def _set_label_properties(self, **kwargs):
"""
Modify the scalebar label properties.
All arguments are passed to the matplotlib Text class. See the
matplotlib documentation for more details.
"""
for kwarg, val in kwargs.items():
try:
# Only set attributes that exist
kvpair = {kwarg: val}
self._scalebar.txt_label.get_children()[0].set(**kvpair)
self._label_settings[kwarg] = val
except AttributeError:
warnings.warn("Text labels do not have attribute {0}. Skipping.".format(kwarg))
@auto_refresh
def _set_scalebar_properties(self, **kwargs):
"""
Modify the scalebar properties.
All arguments are passed to the matplotlib Rectangle class. See the
matplotlib documentation for more details.
"""
for kwarg, val in kwargs.items():
try:
kvpair = {kwarg: val}
self._scalebar.size_bar.get_children()[0].set(**kvpair)
self._scalebar_settings[kwarg] = val
except AttributeError:
warnings.warn("Scalebar does not have attribute {0}. Skipping.".format(kwarg))
@auto_refresh
def set(self, **kwargs):
"""
Modify the scalebar and scalebar properties.
All arguments are passed to the matplotlib Rectangle and Text classes.
See the matplotlib documentation for more details. In cases where the
same argument exists for the two objects, the argument is passed to
both the Text and Rectangle instance.
"""
for kwarg in kwargs:
kwargs_single = {kwarg: kwargs[kwarg]}
try:
self._set_label_properties(**kwargs_single)
except (AttributeError, TypeError):
pass
try:
self._set_scalebar_properties(**kwargs_single)
except (AttributeError, TypeError):
pass
# DEPRECATED
@auto_refresh
def set_font_family(self, family):
warnings.warn("scalebar.set_font_family is deprecated - use scalebar.set_font instead", DeprecationWarning)
self.set_font(family=family)
@auto_refresh
def set_font_weight(self, weight):
warnings.warn("scalebar.set_font_weight is deprecated - use scalebar.set_font instead", DeprecationWarning)
self.set_font(weight=weight)
@auto_refresh
def set_font_size(self, size):
warnings.warn("scalebar.set_font_size is deprecated - use scalebar.set_font instead", DeprecationWarning)
self.set_font(size=size)
@auto_refresh
def set_font_style(self, style):
warnings.warn("scalebar.set_font_style is deprecated - use scalebar.set_font instead", DeprecationWarning)
self.set_font(style=style)
# For backward-compatibility
ScaleBar = Scalebar
class Beam(object):
def __init__(self, parent):
# Retrieve info from parent figure
self._figure = parent._figure
self._header = parent._header
self._ax = parent.ax
self._wcs = parent._wcs
self._dimensions = [parent.x, parent.y]
# Initialize settings
self._base_settings = {}
self._beam_settings = {}
# LAYOUT
@auto_refresh
def show(self, major='BMAJ', minor='BMIN', angle='BPA',
corner='bottom left', frame=False, borderpad=0.4, pad=0.5,
**kwargs):
"""
Display the beam shape and size for the primary image.
By default, this method will search for the BMAJ, BMIN, and BPA
keywords in the FITS header to set the major and minor axes and the
position angle on the sky.
Parameters
----------
major : float, quantity or unit, optional
Major axis of the beam in degrees or an angular quantity (overrides
BMAJ if present)
minor : float, quantity or unit, optional
Minor axis of the beam in degrees or an angular quantity (overrides
BMIN if present)
angle : float, quantity or unit, optional
Position angle of the beam on the sky in degrees or an angular
quantity (overrides BPA if present) in the anticlockwise direction.
corner : int, optional
The beam location. Acceptable values are 'left', 'right',
'top', 'bottom', 'top left', 'top right', 'bottom left'
(default), and 'bottom right'.
frame : str, optional
Whether to display a frame behind the beam (default is False)
kwargs
Additional arguments are passed to the matplotlib Ellipse class.
See the matplotlib documentation for more details.
"""
if isinstance(major, str):
major = self._header[major]
if isinstance(minor, str):
minor = self._header[minor]
if isinstance(angle, str):
angle = self._header[angle]
if isinstance(major, u.Quantity):
major = major.to(u.degree).value
elif isinstance(major, u.Unit):
major = major.to(u.degree)
if isinstance(minor, u.Quantity):
minor = minor.to(u.degree).value
elif isinstance(minor, u.Unit):
minor = minor.to(u.degree)
if isinstance(angle, u.Quantity):
angle = angle.to(u.degree).value
elif isinstance(angle, u.Unit):
angle = angle.to(u.degree)
if self._wcs.is_celestial:
pix_scale = proj_plane_pixel_scales(self._wcs)
sx = pix_scale[self._dimensions[0]]
sy = pix_scale[self._dimensions[1]]
degrees_per_pixel = np.sqrt(sx * sy)
else:
raise ValueError("Cannot show beam when WCS is not celestial")
self._base_settings['minor'] = minor
self._base_settings['major'] = major
self._base_settings['angle'] = angle
self._base_settings['corner'] = corner
self._base_settings['frame'] = frame
self._base_settings['borderpad'] = borderpad
self._base_settings['pad'] = pad
minor /= degrees_per_pixel
major /= degrees_per_pixel
try:
self._beam.remove()
except Exception:
pass
if isinstance(corner, str):
corner = corners[corner]
self._beam = AnchoredEllipse(self._ax.transData, width=minor,
height=major, angle=angle, loc=corner,
pad=pad, borderpad=borderpad,
frameon=frame)
self._ax.add_artist(self._beam)
self.set(**kwargs)
@auto_refresh
def _remove(self):
self._beam.remove()
@auto_refresh
def hide(self):
"""
Hide the beam
"""
try:
self._beam.remove()
except Exception:
pass
@auto_refresh
def set_major(self, major):
"""
Set the major axis of the beam, in degrees.
"""
self._base_settings['major'] = major
self.show(**self._base_settings)
self.set(**self._beam_settings)
@auto_refresh
def set_minor(self, minor):
"""
Set the minor axis of the beam, in degrees.
"""
self._base_settings['minor'] = minor
self.show(**self._base_settings)
self.set(**self._beam_settings)
@auto_refresh
def set_angle(self, angle):
"""
Set the position angle of the beam on the sky, in degrees.
"""
self._base_settings['angle'] = angle
self.show(**self._base_settings)
self.set(**self._beam_settings)
@auto_refresh
def set_corner(self, corner):
"""
Set the beam location.
Acceptable values are 'left', 'right', 'top', 'bottom', 'top left',
'top right', 'bottom left' (default), and 'bottom right'.
"""
self._base_settings['corner'] = corner
self.show(**self._base_settings)
self.set(**self._beam_settings)
@auto_refresh
def set_frame(self, frame):
"""
Set whether to display a frame around the beam.
"""
self._base_settings['frame'] = frame
self.show(**self._base_settings)
self.set(**self._beam_settings)
@auto_refresh
def set_borderpad(self, borderpad):
"""
Set the amount of padding within the beam object, relative to the
canvas size.
"""
self._base_settings['borderpad'] = borderpad
self.show(**self._base_settings)
self.set(**self._beam_settings)
@auto_refresh
def set_pad(self, pad):
"""
Set the amount of padding between the beam object and the image
corner/edge, relative to the canvas size.
"""
self._base_settings['pad'] = pad
self.show(**self._base_settings)
self.set(**self._beam_settings)
# APPEARANCE
@auto_refresh
def set_alpha(self, alpha):
"""
Set the alpha value (transparency).
This should be a floating point value between 0 and 1.
"""
self.set(alpha=alpha)
@auto_refresh
def set_color(self, color):
"""
Set the beam color.
"""
self.set(color=color)
@auto_refresh
def set_edgecolor(self, edgecolor):
"""
Set the color for the edge of the beam.
"""
self.set(edgecolor=edgecolor)
@auto_refresh
def set_facecolor(self, facecolor):
"""
Set the color for the interior of the beam.
"""
self.set(facecolor=facecolor)
@auto_refresh
def set_linestyle(self, linestyle):
"""
Set the line style for the edge of the beam.
This should be one of 'solid', 'dashed', 'dashdot', or 'dotted'.
"""
self.set(linestyle=linestyle)
@auto_refresh
def set_linewidth(self, linewidth):
"""
Set the line width for the edge of the beam, in points.
"""
self.set(linewidth=linewidth)
@auto_refresh
def set_hatch(self, hatch):
"""
Set the hatch pattern.
This should be one of '/', '\', '|', '-', '+', 'x', 'o', 'O', '.', or
'*'.
"""
self.set(hatch=hatch)
@auto_refresh
def set(self, **kwargs):
"""
Modify the beam properties. All arguments are passed to the matplotlib
Ellipse class. See the matplotlib documentation for more details.
"""
for kwarg in kwargs:
self._beam_settings[kwarg] = kwargs[kwarg]
self._beam.ellipse.set(**kwargs)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/regions.py 0000664 0001750 0001750 00000013431 00000000000 014171 0 ustar 00tom tom from astropy import log
from astropy import wcs
from .decorators import auto_refresh
class Regions(object):
"""
Regions sub-class of APLpy.
Used for overplotting various shapes and annotations on APLpy
fitsfigures.
Example:
# DS9 region file called "test.reg"
# (the coordinates are around l=28 in the Galactic Plane)
# Filename: test.fits
fk5
box(18:42:48.262,-04:01:17.91,505.668",459.714",0) # color=red dash=1
point(18:42:51.797,-03:59:44.82) # point=x color=red dash=1
point(18:42:50.491,-04:03:09.39) # point=box color=red dash=1
# vector(18:42:37.433,-04:02:10.77,107.966",115.201) vector=1 color=red dash=1
ellipse(18:42:37.279,-04:02:11.92,26.4336",40.225",0) # color=red dash=1
polygon(18:42:59.016,-03:58:22.06,18:42:58.219,-03:58:11.30,18:42:57.403,-03:58:35.86,18:42:58.094,-03:58:57.69,18:42:59.861,-03:58:41.60,18:42:59.707,-03:58:23.21) # color=red dash=1
point(18:42:52.284,-04:00:02.80) # point=diamond color=red dash=1
point(18:42:46.561,-03:58:01.57) # point=circle color=red dash=1
point(18:42:42.615,-03:58:25.84) # point=cross color=red dash=1
point(18:42:42.946,-04:01:44.74) # point=arrow color=red dash=1
point(18:42:41.961,-03:57:26.16) # point=boxcircle color=red dash=1
# text(18:42:41.961,-03:57:26.16) text={This is text} color=red
Code:
import aplpy
import regions
ff = aplpy.FITSFigure("test.fits")
ff.show_grayscale()
ff.show_regions('test.reg')
"""
@auto_refresh
def show_regions(self, region_file, layer=False, **kwargs):
"""
Overplot regions as specified in the region file.
Parameters
----------
region_file: string or pyregion.ShapeList
Path to a ds9 regions file or a ShapeList already read
in by pyregion.
layer: str, optional
The name of the layer
kwargs
Additional keyword arguments, e.g. zorder, will be passed to the
ds9 call and onto the patchcollections.
"""
PC, TC = ds9(region_file, flatten_header(self._header), **kwargs)
PC.add_to_axes(self.ax)
TC.add_to_axes(self.ax)
if layer:
region_set_name = layer
else:
self._region_counter += 1
region_set_name = 'region_set_' + str(self._region_counter)
self._layers[region_set_name] = PC
self._layers[region_set_name + "_txt"] = TC
def ds9(region_file, header, zorder=3, **kwargs):
"""
Wrapper to return a PatchCollection given a ds9 region file
and a fits header.
zorder - defaults to 3 so that regions are on top of contours
"""
try:
import pyregion
except Exception:
raise ImportError("The pyregion package is required to load region files")
# read region file
if isinstance(region_file, str):
rr = pyregion.open(region_file)
elif isinstance(region_file, pyregion.ShapeList):
rr = region_file
else:
raise Exception("Invalid type for region_file: %s - should be string or pyregion.ShapeList" % type(region_file))
if isinstance(header, wcs.WCS):
header = header.to_header()
# convert coordinates to image coordinates
rrim = rr.as_imagecoord(header)
# pyregion and aplpy both correct for the FITS standard origin=1,1
# need to avoid double-correcting. Also, only some items in `coord_list`
# are pixel coordinates, so which ones should be corrected depends on the
# shape.
for r in rrim:
if r.name == 'polygon':
correct = range(len(r.coord_list))
elif r.name == 'line':
correct = range(4)
elif r.name in ['rotbox', 'box', 'ellipse', 'annulus', 'circle', 'panda', 'pie', 'epanda', 'text', 'point', 'vector']:
correct = range(2)
else:
log.warning("Unknown region type '{0}' - please report to the developers")
correct = range(2)
for i in correct:
r.coord_list[i] += 1
if 'text_offset' in kwargs:
text_offset = kwargs['text_offset']
del kwargs['text_offset']
else:
text_offset = 5.0
# grab the shapes to overplot
pp, aa = rrim.get_mpl_patches_texts(text_offset=text_offset)
PC = ArtistCollection(pp, **kwargs) # preserves line style (dashed)
TC = ArtistCollection(aa, **kwargs)
PC.set_zorder(zorder)
TC.set_zorder(zorder)
return PC, TC
class ArtistCollection():
"""
Matplotlib collections can't handle Text.
This is a barebones collection for text objects
that supports removing and making (in)visible
"""
def __init__(self, artistlist):
"""
Pass in a list of matplotlib.text.Text objects
(or possibly any matplotlib Artist will work)
"""
self.artistlist = artistlist
def remove(self):
for T in self.artistlist:
T.remove()
def add_to_axes(self, ax):
for T in self.artistlist:
ax.add_artist(T)
def get_visible(self):
visible = True
for T in self.artistlist:
if not T.get_visible():
visible = False
return visible
def set_visible(self, visible=True):
for T in self.artistlist:
T.set_visible(visible)
def set_zorder(self, zorder):
for T in self.artistlist:
T.set_zorder(zorder)
def flatten_header(header):
"""
Attempt to turn an N-dimensional fits header into a 2-dimensional header
Turns all CRPIX[>2] etc. into new keywords with suffix 'A'
"""
orig_wcs = wcs.WCS(header)
newheader = orig_wcs.celestial.to_header()
newheader['NAXIS'] = 2
newheader['NAXIS1'] = header['NAXIS{0}'.format(orig_wcs.wcs.lng + 1)]
newheader['NAXIS2'] = header['NAXIS{0}'.format(orig_wcs.wcs.lat + 1)]
return newheader
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/rgb.py 0000664 0001750 0001750 00000025227 00000000000 013303 0 ustar 00tom tom from distutils import version
import os
import warnings
import numpy as np
from astropy import log
from astropy.io import fits
from astropy.coordinates import ICRS
from astropy.visualization import AsymmetricPercentileInterval, simple_norm
from reproject import reproject_interp
from reproject.mosaicking import find_optimal_celestial_wcs
def _data_stretch(image, vmin=None, vmax=None, pmin=0.25, pmax=99.75,
stretch='linear', vmid=None, exponent=2):
if vmin is None or vmax is None:
interval = AsymmetricPercentileInterval(pmin, pmax, n_samples=10000)
try:
vmin_auto, vmax_auto = interval.get_limits(image)
except IndexError: # no valid values
vmin_auto = vmax_auto = 0
if vmin is None:
log.info("vmin = %10.3e (auto)" % vmin_auto)
vmin = vmin_auto
else:
log.info("vmin = %10.3e" % vmin)
if vmax is None:
log.info("vmax = %10.3e (auto)" % vmax_auto)
vmax = vmax_auto
else:
log.info("vmax = %10.3e" % vmax)
if stretch == 'arcsinh':
stretch = 'asinh'
normalizer = simple_norm(image, stretch=stretch, power=exponent,
asinh_a=vmid, min_cut=vmin, max_cut=vmax, clip=False)
data = normalizer(image, clip=True).filled(0)
data = np.nan_to_num(data)
data = np.clip(data * 255., 0., 255.)
return data.astype(np.uint8)
def make_rgb_image(data, output, indices=(0, 1, 2), vmin_r=None, vmax_r=None,
pmin_r=0.25, pmax_r=99.75, stretch_r='linear', vmid_r=None,
exponent_r=2, vmin_g=None, vmax_g=None, pmin_g=0.25,
pmax_g=99.75, stretch_g='linear', vmid_g=None, exponent_g=2,
vmin_b=None, vmax_b=None, pmin_b=0.25, pmax_b=99.75,
stretch_b='linear', vmid_b=None, exponent_b=2,
make_nans_transparent=False, embed_avm_tags=True):
"""
Make an RGB image from a FITS RGB cube or from three FITS files.
Parameters
----------
data : str or tuple or list
If a string, this is the filename of an RGB FITS cube. If a tuple
or list, this should give the filename of three files to use for
the red, green, and blue channel.
output : str
The output filename. The image type (e.g. PNG, JPEG, TIFF, ...)
will be determined from the extension. Any image type supported by
the Python Imaging Library can be used.
indices : tuple, optional
If data is the filename of a FITS cube, these indices are the
positions in the third dimension to use for red, green, and
blue respectively. The default is to use the first three
indices.
vmin_r, vmin_g, vmin_b : float, optional
Minimum pixel value to use for the red, green, and blue channels.
If set to None for a given channel, the minimum pixel value for
that channel is determined using the corresponding pmin_x argument
(default).
vmax_r, vmax_g, vmax_b : float, optional
Maximum pixel value to use for the red, green, and blue channels.
If set to None for a given channel, the maximum pixel value for
that channel is determined using the corresponding pmax_x argument
(default).
pmin_r, pmin_r, pmin_g : float, optional
Percentile values used to determine for a given channel the
minimum pixel value to use for that channel if the corresponding
vmin_x is set to None. The default is 0.25% for all channels.
pmax_r, pmax_g, pmax_b : float, optional
Percentile values used to determine for a given channel the
maximum pixel value to use for that channel if the corresponding
vmax_x is set to None. The default is 99.75% for all channels.
stretch_r, stretch_g, stretch_b : { 'linear', 'log', 'sqrt', 'arcsinh', 'power' }
The stretch function to use for the different channels.
vmid_r, vmid_g, vmid_b : float, optional
Baseline values used for the log and arcsinh stretches. If
set to None, this is set to zero for log stretches and to
vmin - (vmax - vmin) / 30. for arcsinh stretches
exponent_r, exponent_g, exponent_b : float, optional
If stretch_x is set to 'power', this is the exponent to use.
make_nans_transparent : bool, optional
If set AND output is png, will add an alpha layer that sets pixels
containing a NaN to transparent.
embed_avm_tags : bool, optional
Whether to embed AVM tags inside the image - this can only be done for
JPEG and PNG files, and only if PyAVM is installed.
"""
try:
from PIL import Image
except ImportError:
try:
import Image
except ImportError:
raise ImportError("The Python Imaging Library (PIL) is required to make an RGB image")
if isinstance(data, str):
image = fits.getdata(data)
image_r = image[indices[0], :, :]
image_g = image[indices[1], :, :]
image_b = image[indices[2], :, :]
# Read in header
header = fits.getheader(data)
# Remove information about third dimension
header['NAXIS'] = 2
for key in ['NAXIS', 'CTYPE', 'CRPIX', 'CRVAL', 'CUNIT', 'CDELT', 'CROTA']:
for coord in range(3, 6):
name = key + str(coord)
if name in header:
header.__delitem__(name)
elif (type(data) == list or type(data) == tuple) and len(data) == 3:
filename_r, filename_g, filename_b = data
image_r = fits.getdata(filename_r)
image_g = fits.getdata(filename_g)
image_b = fits.getdata(filename_b)
# Read in header
header = fits.getheader(filename_r)
else:
raise Exception("data should either be the filename of a FITS cube or a list/tuple of three images")
# are we making a transparent layer?
do_alpha = make_nans_transparent and output.lower().endswith('.png')
if do_alpha:
log.info("Making alpha layer")
# initialize alpha layer
image_alpha = np.empty_like(image_r, dtype=np.uint8)
image_alpha[:] = 255
# look for nans in images
for im in [image_r, image_g, image_b]:
image_alpha[np.isnan(im)] = 0
log.info("Red:")
image_r = Image.fromarray(_data_stretch(image_r, vmin=vmin_r, vmax=vmax_r,
pmin=pmin_r, pmax=pmax_r, stretch=stretch_r,
vmid=vmid_r, exponent=exponent_r))
log.info("Green:")
image_g = Image.fromarray(_data_stretch(image_g,
vmin=vmin_g, vmax=vmax_g,
pmin=pmin_g, pmax=pmax_g,
stretch=stretch_g,
vmid=vmid_g,
exponent=exponent_g))
log.info("Blue:")
image_b = Image.fromarray(_data_stretch(image_b,
vmin=vmin_b, vmax=vmax_b,
pmin=pmin_b, pmax=pmax_b,
stretch=stretch_b,
vmid=vmid_b,
exponent=exponent_b))
img = Image.merge("RGB", (image_r, image_g, image_b))
if do_alpha:
# convert to RGBA and add alpha layer
image_alpha = Image.fromarray(image_alpha)
img.convert("RGBA")
img.putalpha(image_alpha)
img = img.transpose(Image.FLIP_TOP_BOTTOM)
img.save(output)
if embed_avm_tags:
try:
import pyavm
except ImportError:
warnings.warn("PyAVM 0.9.1 or later is not installed, so AVM tags will not be embedded in RGB image")
return
if version.LooseVersion(pyavm.__version__) < version.LooseVersion('0.9.1'):
warnings.warn("PyAVM 0.9.1 or later is not installed, so AVM tags will not be embedded in RGB image")
return
from pyavm import AVM
if output.lower().endswith(('.jpg', '.jpeg', '.png')):
avm = AVM.from_header(header)
avm.embed(output, output)
else:
warnings.warn("AVM tags will not be embedded in RGB image, as only JPEG and PNG files are supported")
def make_rgb_cube(files, output, north=True):
"""
Make an RGB data cube from a list of three FITS images.
This method can read in three FITS files with different
projections/sizes/resolutions and uses the `reproject
`_ package to reproject them all
to the same projection.
Two files are produced by this function. The first is a three-dimensional
FITS cube with a filename give by ``output``, where the third dimension
contains the different channels. The second is a two-dimensional FITS
image with a filename given by ``output`` with a `_2d` suffix. This file
contains the mean of the different channels, and is required as input to
FITSFigure if show_rgb is subsequently used to show a color image
generated from the FITS cube (to provide the correct WCS information to
FITSFigure).
Parameters
----------
files : tuple or list
A list of the filenames of three FITS filename to reproject.
The order is red, green, blue.
output : str
The filename of the output RGB FITS cube.
north : bool, optional
Whether to rotate the image so that north is up. By default, this is
assumed to be 'north' in the ICRS frame, but you can also pass any
astropy :class:`~astropy.coordinates.BaseCoordinateFrame` to indicate
to use the north of that frame.
"""
# Check that input files exist
for f in files:
if not os.path.exists(f):
raise Exception("File does not exist : " + f)
if north is not False:
frame = ICRS() if north is True else north
auto_rotate = False
else:
frame = None
auto_rotate = True
# Find optimal WCS and shape based on input images
wcs, shape = find_optimal_celestial_wcs(files, frame=frame, auto_rotate=auto_rotate)
header = wcs.to_header()
# Generate empty datacube
image_cube = np.zeros((len(files),) + shape, dtype=np.float32)
# Loop through files and reproject
for i, filename in enumerate(files):
image_cube[i, :, :] = reproject_interp(filename, wcs, shape_out=shape)[0]
# Write out final cube
fits.writeto(output, image_cube, header, overwrite=True)
# Write out collapsed version of cube
fits.writeto(output.replace('.fits', '_2d.fits'),
np.mean(image_cube, axis=0), header, overwrite=True)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/slicer.py 0000664 0001750 0001750 00000004143 00000000000 014004 0 ustar 00tom tom
def slice_hypercube(data, header, dimensions=[0, 1], slices=[]):
"""
Extract a slice from an n-dimensional HDU data/header pair, and return the
new data (without changing the header).
"""
if type(slices) == int:
slices = (slices, )
else:
slices = slices[:]
shape = data.shape
if len(shape) < 2:
raise Exception("FITS file does not have enough dimensions")
elif len(shape) == 2:
wcsaxes_slices = ('x', 'y')
if dimensions[1] < dimensions[0]:
data = data.transpose()
wcsaxes_slices = ('y', 'x')
return data, wcsaxes_slices
else:
if slices:
wcsaxes_slices = slices[:]
if dimensions[0] < dimensions[1]:
slices.insert(dimensions[0], slice(None, None, None))
slices.insert(dimensions[1], slice(None, None, None))
wcsaxes_slices.insert(dimensions[0], 'x')
wcsaxes_slices.insert(dimensions[1], 'y')
else:
slices.insert(dimensions[1], slice(None, None, None))
slices.insert(dimensions[0], slice(None, None, None))
wcsaxes_slices.insert(dimensions[1], 'y')
wcsaxes_slices.insert(dimensions[0], 'x')
if type(slices) == list:
slices = tuple(slices)
wcsaxes_slices = tuple(wcsaxes_slices)
data = data[slices[::-1]]
if dimensions[1] < dimensions[0]:
data = data.transpose()
else:
message = """
Attempted to read in %i-dimensional FITS cube, but
dimensions and slices were not specified. Please specify these
using the dimensions= and slices= argument. The cube dimensions
are:\n\n""" % len(shape)
for i in range(1, len(shape) + 1):
message += " " * 10
message += " %i %s %i\n" % (i - 1,
header["CTYPE%i" % i],
header["NAXIS%i" % i])
raise Exception(message)
return data, wcsaxes_slices
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1646929515.242823
aplpy-2.1.0/aplpy/tests/ 0000775 0001750 0001750 00000000000 00000000000 013311 5 ustar 00tom tom ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/tests/__init__.py 0000664 0001750 0001750 00000000615 00000000000 015424 0 ustar 00tom tom import matplotlib
MPL_VERSION = matplotlib.__version__
ROOT = "https://aplpy.github.io/aplpy-data/2.x/2018-10-28T17:43:23.106"
# The developer versions of the form 3.2.x+... contain changes that will only
# be included in the 3.3.x release, so we update this here.
if MPL_VERSION[:3] == '3.2' and '+' in MPL_VERSION:
MPL_VERSION = '3.3'
baseline_dir = ROOT + '/' + MPL_VERSION[:3] + '.x/'
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/coveragerc 0000644 0001750 0001750 00000001400 00000000000 015345 0 ustar 00tom tom [run]
source = {packagename}
omit =
{packagename}/_astropy_init*
{packagename}/conftest*
{packagename}/cython_version*
{packagename}/setup_package*
{packagename}/*/setup_package*
{packagename}/*/*/setup_package*
{packagename}/tests/*
{packagename}/*/tests/*
{packagename}/*/*/tests/*
{packagename}/version*
[report]
exclude_lines =
# Have to re-enable the standard pragma
pragma: no cover
# Don't complain about packages we have installed
except ImportError
# Don't complain if tests don't hit assertions
raise AssertionError
raise NotImplementedError
# Don't complain about script hooks
def main\(.*\):
# Ignore branches that don't pertain to this version of Python
pragma: py{ignore_python_version} ././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1646929515.242823
aplpy-2.1.0/aplpy/tests/data/ 0000775 0001750 0001750 00000000000 00000000000 014222 5 ustar 00tom tom ././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1646929515.246823
aplpy-2.1.0/aplpy/tests/data/2d_fits/ 0000775 0001750 0001750 00000000000 00000000000 015554 5 ustar 00tom tom ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_AIR.hdr 0000644 0001750 0001750 00000001667 00000000000 017604 0 ustar 00tom tom SIMPLE = T
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2
NAXIS1 = 192
NAXIS2 = 192
BUNIT = 'JY/BEAM '
CTYPE1 = 'RA---AIR'
CRPIX1 = 8.339330824421999
CDELT1 = -6.666666666667E-02
CRVAL1 = 0.000000000000E+00
CTYPE2 = 'DEC--AIR'
CRPIX2 = -234.7545010835
CDELT2 = 6.666666666667E-02
CRVAL2 = -9.000000000000E+01
LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole
LATPOLE = -9.000000000000E+01 / Native latitude of celestial pole
PV2_1 = 4.500000000000E+01 / Projection parameter 1
EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates
BMAJ = 2.399999936422E-01 / Beam major axis in degrees
BMIN = 2.399999936422E-01 / Beam minor axis in degrees
BPA = 0.000000000000E+00 / Beam position angle in degrees
RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_AIT.hdr 0000644 0001750 0001750 00000001577 00000000000 017606 0 ustar 00tom tom SIMPLE = T
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2
NAXIS1 = 192
NAXIS2 = 192
BUNIT = 'JY/BEAM '
CTYPE1 = 'RA---AIT'
CRPIX1 = 7.115850027049
CDELT1 = -6.666666666667E-02
CRVAL1 = 0.000000000000E+00
CTYPE2 = 'DEC--AIT'
CRPIX2 = -246.2317116277
CDELT2 = 6.666666666667E-02
CRVAL2 = -9.000000000000E+01
LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole
LATPOLE = 0.000000000000E+00 / Native latitude of celestial pole
EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates
BMAJ = 2.399999936422E-01 / Beam major axis in degrees
BMIN = 2.399999936422E-01 / Beam minor axis in degrees
BPA = 0.000000000000E+00 / Beam position angle in degrees
RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_ARC.hdr 0000644 0001750 0001750 00000001577 00000000000 017576 0 ustar 00tom tom SIMPLE = T
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2
NAXIS1 = 192
NAXIS2 = 192
BUNIT = 'JY/BEAM '
CTYPE1 = 'RA---ARC'
CRPIX1 = 5.082274450444
CDELT1 = -6.666666666667E-02
CRVAL1 = 0.000000000000E+00
CTYPE2 = 'DEC--ARC'
CRPIX2 = -246.941901905
CDELT2 = 6.666666666667E-02
CRVAL2 = -9.000000000000E+01
LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole
LATPOLE = -9.000000000000E+01 / Native latitude of celestial pole
EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates
BMAJ = 2.399999936422E-01 / Beam major axis in degrees
BMIN = 2.399999936422E-01 / Beam minor axis in degrees
BPA = 0.000000000000E+00 / Beam position angle in degrees
RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_AZP.hdr 0000644 0001750 0001750 00000001757 00000000000 017623 0 ustar 00tom tom SIMPLE = T
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2
NAXIS1 = 192
NAXIS2 = 192
BUNIT = 'JY/BEAM '
CTYPE1 = 'RA---AZP'
CRPIX1 = -11.34948542534
CDELT1 = -6.666666666667E-02
CRVAL1 = 0.000000000000E+00
CTYPE2 = 'DEC--AZP'
CRPIX2 = -254.1100848779
CDELT2 = 6.666666666667E-02
CRVAL2 = -9.000000000000E+01
LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole
LATPOLE = -9.000000000000E+01 / Native latitude of celestial pole
PV2_1 = 2.000000000000E+00 / Projection parameter 1
PV2_2 = 3.000000000000E+01 / Projection parameter 2
EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates
BMAJ = 2.399999936422E-01 / Beam major axis in degrees
BMIN = 2.399999936422E-01 / Beam minor axis in degrees
BPA = 0.000000000000E+00 / Beam position angle in degrees
RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_BON.hdr 0000644 0001750 0001750 00000001667 00000000000 017607 0 ustar 00tom tom SIMPLE = T
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2
NAXIS1 = 192
NAXIS2 = 192
BUNIT = 'JY/BEAM '
CTYPE1 = 'RA---BON'
CRPIX1 = -33.0741266819
CDELT1 = -6.666666666667E-02
CRVAL1 = 0.000000000000E+00
CTYPE2 = 'DEC--BON'
CRPIX2 = -243.1263982441
CDELT2 = 6.666666666667E-02
CRVAL2 = -9.000000000000E+01
LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole
LATPOLE = 0.000000000000E+00 / Native latitude of celestial pole
PV2_1 = 4.500000000000E+01 / Projection parameter 1
EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates
BMAJ = 2.399999936422E-01 / Beam major axis in degrees
BMIN = 2.399999936422E-01 / Beam minor axis in degrees
BPA = 0.000000000000E+00 / Beam position angle in degrees
RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_CAR.hdr 0000644 0001750 0001750 00000001577 00000000000 017576 0 ustar 00tom tom SIMPLE = T
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2
NAXIS1 = 192
NAXIS2 = 192
BUNIT = 'JY/BEAM '
CTYPE1 = 'RA---CAR'
CRPIX1 = 7.527038199745
CDELT1 = -6.666666666667E-02
CRVAL1 = 0.000000000000E+00
CTYPE2 = 'DEC--CAR'
CRPIX2 = -248.2173814412
CDELT2 = 6.666666666667E-02
CRVAL2 = -9.000000000000E+01
LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole
LATPOLE = 0.000000000000E+00 / Native latitude of celestial pole
EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates
BMAJ = 2.399999936422E-01 / Beam major axis in degrees
BMIN = 2.399999936422E-01 / Beam minor axis in degrees
BPA = 0.000000000000E+00 / Beam position angle in degrees
RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_CEA.hdr 0000644 0001750 0001750 00000001667 00000000000 017561 0 ustar 00tom tom SIMPLE = T
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2
NAXIS1 = 192
NAXIS2 = 192
BUNIT = 'JY/BEAM '
CTYPE1 = 'RA---CEA'
CRPIX1 = 7.688571124876
CDELT1 = -6.666666666667E-02
CRVAL1 = 0.000000000000E+00
CTYPE2 = 'DEC--CEA'
CRPIX2 = -248.2173814412
CDELT2 = 6.666666666667E-02
CRVAL2 = -9.000000000000E+01
LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole
LATPOLE = 0.000000000000E+00 / Native latitude of celestial pole
PV2_1 = 1.000000000000E+00 / Projection parameter 1
EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates
BMAJ = 2.399999936422E-01 / Beam major axis in degrees
BMIN = 2.399999936422E-01 / Beam minor axis in degrees
BPA = 0.000000000000E+00 / Beam position angle in degrees
RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_COD.hdr 0000644 0001750 0001750 00000001757 00000000000 017576 0 ustar 00tom tom SIMPLE = T
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2
NAXIS1 = 192
NAXIS2 = 192
BUNIT = 'JY/BEAM '
CTYPE1 = 'RA---COD'
CRPIX1 = 15.61302682707
CDELT1 = -6.666666666667E-02
CRVAL1 = 0.000000000000E+00
CTYPE2 = 'DEC--COD'
CRPIX2 = -215.3431714695
CDELT2 = 6.666666666667E-02
CRVAL2 = -9.000000000000E+01
LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole
LATPOLE = -4.500000000000E+01 / Native latitude of celestial pole
PV2_1 = 4.500000000000E+01 / Projection parameter 1
PV2_2 = 2.500000000000E+01 / Projection parameter 2
EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates
BMAJ = 2.399999936422E-01 / Beam major axis in degrees
BMIN = 2.399999936422E-01 / Beam minor axis in degrees
BPA = 0.000000000000E+00 / Beam position angle in degrees
RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_COE.hdr 0000644 0001750 0001750 00000001757 00000000000 017577 0 ustar 00tom tom SIMPLE = T
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2
NAXIS1 = 192
NAXIS2 = 192
BUNIT = 'JY/BEAM '
CTYPE1 = 'RA---COE'
CRPIX1 = -14.35249668783
CDELT1 = -6.666666666667E-02
CRVAL1 = 0.000000000000E+00
CTYPE2 = 'DEC--COE'
CRPIX2 = -223.0375366798
CDELT2 = 6.666666666667E-02
CRVAL2 = -9.000000000000E+01
LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole
LATPOLE = 4.500000000000E+01 / Native latitude of celestial pole
PV2_1 = -4.500000000000E+01 / Projection parameter 1
PV2_2 = 2.500000000000E+01 / Projection parameter 2
EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates
BMAJ = 2.399999936422E-01 / Beam major axis in degrees
BMIN = 2.399999936422E-01 / Beam minor axis in degrees
BPA = 0.000000000000E+00 / Beam position angle in degrees
RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_COO.hdr 0000644 0001750 0001750 00000001757 00000000000 017611 0 ustar 00tom tom SIMPLE = T
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2
NAXIS1 = 192
NAXIS2 = 192
BUNIT = 'JY/BEAM '
CTYPE1 = 'RA---COO'
CRPIX1 = 12.92640949564
CDELT1 = -6.666666666667E-02
CRVAL1 = 0.000000000000E+00
CTYPE2 = 'DEC--COO'
CRPIX2 = -213.6486051767
CDELT2 = 6.666666666667E-02
CRVAL2 = -9.000000000000E+01
LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole
LATPOLE = -4.500000000000E+01 / Native latitude of celestial pole
PV2_1 = 4.500000000000E+01 / Projection parameter 1
PV2_2 = 2.500000000000E+01 / Projection parameter 2
EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates
BMAJ = 2.399999936422E-01 / Beam major axis in degrees
BMIN = 2.399999936422E-01 / Beam minor axis in degrees
BPA = 0.000000000000E+00 / Beam position angle in degrees
RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_COP.hdr 0000644 0001750 0001750 00000001757 00000000000 017612 0 ustar 00tom tom SIMPLE = T
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2
NAXIS1 = 192
NAXIS2 = 192
BUNIT = 'JY/BEAM '
CTYPE1 = 'RA---COP'
CRPIX1 = 15.05768272737
CDELT1 = -6.666666666667E-02
CRVAL1 = 0.000000000000E+00
CTYPE2 = 'DEC--COP'
CRPIX2 = -215.1923139086
CDELT2 = 6.666666666667E-02
CRVAL2 = -9.000000000000E+01
LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole
LATPOLE = -4.500000000000E+01 / Native latitude of celestial pole
PV2_1 = 4.500000000000E+01 / Projection parameter 1
PV2_2 = 2.500000000000E+01 / Projection parameter 2
EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates
BMAJ = 2.399999936422E-01 / Beam major axis in degrees
BMIN = 2.399999936422E-01 / Beam minor axis in degrees
BPA = 0.000000000000E+00 / Beam position angle in degrees
RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_CSC.hdr 0000644 0001750 0001750 00000001577 00000000000 017601 0 ustar 00tom tom SIMPLE = T
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2
NAXIS1 = 192
NAXIS2 = 192
BUNIT = 'JY/BEAM '
CTYPE1 = 'RA---CSC'
CRPIX1 = -7.043520126533
CDELT1 = -6.666666666667E-02
CRVAL1 = 0.000000000000E+00
CTYPE2 = 'DEC--CSC'
CRPIX2 = -268.6531829635
CDELT2 = 6.666666666667E-02
CRVAL2 = -9.000000000000E+01
LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole
LATPOLE = 0.000000000000E+00 / Native latitude of celestial pole
EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates
BMAJ = 2.399999936422E-01 / Beam major axis in degrees
BMIN = 2.399999936422E-01 / Beam minor axis in degrees
BPA = 0.000000000000E+00 / Beam position angle in degrees
RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_CYP.hdr 0000644 0001750 0001750 00000001757 00000000000 017624 0 ustar 00tom tom SIMPLE = T
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2
NAXIS1 = 192
NAXIS2 = 192
BUNIT = 'JY/BEAM '
CTYPE1 = 'RA---CYP'
CRPIX1 = 20.56099939277
CDELT1 = -6.666666666667E-02
CRVAL1 = 0.000000000000E+00
CTYPE2 = 'DEC--CYP'
CRPIX2 = -147.1055514007
CDELT2 = 6.666666666667E-02
CRVAL2 = -9.000000000000E+01
LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole
LATPOLE = 0.000000000000E+00 / Native latitude of celestial pole
PV2_1 = 1.000000000000E+00 / Projection parameter 1
PV2_2 = 7.071067811870E-01 / Projection parameter 2
EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates
BMAJ = 2.399999936422E-01 / Beam major axis in degrees
BMIN = 2.399999936422E-01 / Beam minor axis in degrees
BPA = 0.000000000000E+00 / Beam position angle in degrees
RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_HPX.hdr 0000644 0001750 0001750 00000002403 00000000000 017615 0 ustar 00tom tom SIMPLE = T / file does conform to FITS standard
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2 / number of data axes
NAXIS1 = 192 / length of data axis 1
NAXIS2 = 192 / length of data axis 2
EXTEND = T / FITS dataset may contain extensions
COMMENT FITS (Flexible Image Transport System) format is defined in 'Astronomy
COMMENT and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H
BUNIT = 'Jy/beam ' / Pixel value is surface brightness
CTYPE1 = 'RA---HPX'
CRPIX1 = -8.21754831338666
CDELT1 = -0.0666666666666667
CRVAL1 = 0.
CTYPE2 = 'DEC--HPX'
CRPIX2 = -248.217381441188
CDELT2 = 0.0666666666666667
CRVAL2 = -90.
LONPOLE = 180. / Native longitude of celestial pole
LATPOLE = 0. / Native latitude of celestial pole
RADESYS = 'FK5 ' / Equatorial coordinate system
EQUINOX = 2000.0 / Equinox of equatorial coordinates
BMAJ = 0.24000 / Beam major axis in degrees
BMIN = 0.24000 / Beam minor axis in degrees
BPA = 0.0 / Beam position angle in degrees
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_MER.hdr 0000644 0001750 0001750 00000001577 00000000000 017614 0 ustar 00tom tom SIMPLE = T
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2
NAXIS1 = 192
NAXIS2 = 192
BUNIT = 'JY/BEAM '
CTYPE1 = 'RA---MER'
CRPIX1 = 7.364978412864
CDELT1 = -6.666666666667E-02
CRVAL1 = 0.000000000000E+00
CTYPE2 = 'DEC--MER'
CRPIX2 = -248.2173814412
CDELT2 = 6.666666666667E-02
CRVAL2 = -9.000000000000E+01
LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole
LATPOLE = 0.000000000000E+00 / Native latitude of celestial pole
EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates
BMAJ = 2.399999936422E-01 / Beam major axis in degrees
BMIN = 2.399999936422E-01 / Beam minor axis in degrees
BPA = 0.000000000000E+00 / Beam position angle in degrees
RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_MOL.hdr 0000644 0001750 0001750 00000001577 00000000000 017620 0 ustar 00tom tom SIMPLE = T
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2
NAXIS1 = 192
NAXIS2 = 192
BUNIT = 'JY/BEAM '
CTYPE1 = 'RA---MOL'
CRPIX1 = -2.310670994515
CDELT1 = -6.666666666667E-02
CRVAL1 = 0.000000000000E+00
CTYPE2 = 'DEC--MOL'
CRPIX2 = -212.7655947497
CDELT2 = 6.666666666667E-02
CRVAL2 = -9.000000000000E+01
LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole
LATPOLE = 0.000000000000E+00 / Native latitude of celestial pole
EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates
BMAJ = 2.399999936422E-01 / Beam major axis in degrees
BMIN = 2.399999936422E-01 / Beam minor axis in degrees
BPA = 0.000000000000E+00 / Beam position angle in degrees
RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_NCP.hdr 0000644 0001750 0001750 00000001757 00000000000 017611 0 ustar 00tom tom SIMPLE = T
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2
NAXIS1 = 192
NAXIS2 = 192
BUNIT = 'JY/BEAM '
CTYPE1 = 'RA---SIN'
CRPIX1 = 7.688572009351
CDELT1 = -6.666666666667E-02
CRVAL1 = 0.000000000000E+00
CTYPE2 = 'DEC--SIN'
CRPIX2 = -237.1895431541
CDELT2 = 6.666666666667E-02
CRVAL2 = -9.000000000000E+01
LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole
LATPOLE = -9.000000000000E+01 / Native latitude of celestial pole
PV2_1 = 0.000000000000E+00 / Projection parameter 1
PV2_2 = -1.216796447506E-08 / Projection parameter 2
EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates
BMAJ = 2.399999936422E-01 / Beam major axis in degrees
BMIN = 2.399999936422E-01 / Beam minor axis in degrees
BPA = 0.000000000000E+00 / Beam position angle in degrees
RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_PAR.hdr 0000644 0001750 0001750 00000001577 00000000000 017613 0 ustar 00tom tom SIMPLE = T
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2
NAXIS1 = 192
NAXIS2 = 192
BUNIT = 'JY/BEAM '
CTYPE1 = 'RA---PAR'
CRPIX1 = 3.322937769653
CDELT1 = -6.666666666667E-02
CRVAL1 = 0.000000000000E+00
CTYPE2 = 'DEC--PAR'
CRPIX2 = -246.5551494284
CDELT2 = 6.666666666667E-02
CRVAL2 = -9.000000000000E+01
LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole
LATPOLE = 0.000000000000E+00 / Native latitude of celestial pole
EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates
BMAJ = 2.399999936422E-01 / Beam major axis in degrees
BMIN = 2.399999936422E-01 / Beam minor axis in degrees
BPA = 0.000000000000E+00 / Beam position angle in degrees
RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_PCO.hdr 0000644 0001750 0001750 00000001577 00000000000 017612 0 ustar 00tom tom SIMPLE = T
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2
NAXIS1 = 192
NAXIS2 = 192
BUNIT = 'JY/BEAM '
CTYPE1 = 'RA---PCO'
CRPIX1 = 0.3620782775517
CDELT1 = -6.666666666667E-02
CRVAL1 = 0.000000000000E+00
CTYPE2 = 'DEC--PCO'
CRPIX2 = -246.2486098896
CDELT2 = 6.666666666667E-02
CRVAL2 = -9.000000000000E+01
LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole
LATPOLE = 0.000000000000E+00 / Native latitude of celestial pole
EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates
BMAJ = 2.399999936422E-01 / Beam major axis in degrees
BMIN = 2.399999936422E-01 / Beam minor axis in degrees
BPA = 0.000000000000E+00 / Beam position angle in degrees
RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_QSC.hdr 0000644 0001750 0001750 00000001577 00000000000 017617 0 ustar 00tom tom SIMPLE = T
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2
NAXIS1 = 192
NAXIS2 = 192
BUNIT = 'JY/BEAM '
CTYPE1 = 'RA---QSC'
CRPIX1 = -8.258194421088
CDELT1 = -6.666666666667E-02
CRVAL1 = 0.000000000000E+00
CTYPE2 = 'DEC--QSC'
CRPIX2 = -258.3408175994
CDELT2 = 6.666666666667E-02
CRVAL2 = -9.000000000000E+01
LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole
LATPOLE = 0.000000000000E+00 / Native latitude of celestial pole
EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates
BMAJ = 2.399999936422E-01 / Beam major axis in degrees
BMIN = 2.399999936422E-01 / Beam minor axis in degrees
BPA = 0.000000000000E+00 / Beam position angle in degrees
RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_SFL.hdr 0000644 0001750 0001750 00000001577 00000000000 017615 0 ustar 00tom tom SIMPLE = T
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2
NAXIS1 = 192
NAXIS2 = 192
BUNIT = 'JY/BEAM '
CTYPE1 = 'RA---SFL'
CRPIX1 = 7.527038199745
CDELT1 = -6.666666666667E-02
CRVAL1 = 0.000000000000E+00
CTYPE2 = 'DEC--SFL'
CRPIX2 = -246.3483086237
CDELT2 = 6.666666666667E-02
CRVAL2 = -9.000000000000E+01
LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole
LATPOLE = 0.000000000000E+00 / Native latitude of celestial pole
EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates
BMAJ = 2.399999936422E-01 / Beam major axis in degrees
BMIN = 2.399999936422E-01 / Beam minor axis in degrees
BPA = 0.000000000000E+00 / Beam position angle in degrees
RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_SIN.hdr 0000644 0001750 0001750 00000001757 00000000000 017622 0 ustar 00tom tom SIMPLE = T
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2
NAXIS1 = 192
NAXIS2 = 192
BUNIT = 'JY/BEAM '
CTYPE1 = 'RA---SIN'
CRPIX1 = 7.688571124876
CDELT1 = -6.666666666667E-02
CRVAL1 = 0.000000000000E+00
CTYPE2 = 'DEC--SIN'
CRPIX2 = -237.1895431541
CDELT2 = 6.666666666667E-02
CRVAL2 = -9.000000000000E+01
LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole
LATPOLE = -9.000000000000E+01 / Native latitude of celestial pole
PV2_1 = 0.000000000000E+00 / Projection parameter 1
PV2_2 = 0.000000000000E+00 / Projection parameter 2
EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates
BMAJ = 2.399999936422E-01 / Beam major axis in degrees
BMIN = 2.399999936422E-01 / Beam minor axis in degrees
BPA = 0.000000000000E+00 / Beam position angle in degrees
RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_STG.hdr 0000644 0001750 0001750 00000001577 00000000000 017626 0 ustar 00tom tom SIMPLE = T
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2
NAXIS1 = 192
NAXIS2 = 192
BUNIT = 'JY/BEAM '
CTYPE1 = 'RA---STG'
CRPIX1 = 3.744942537739
CDELT1 = -6.666666666667E-02
CRVAL1 = 0.000000000000E+00
CTYPE2 = 'DEC--STG'
CRPIX2 = -251.945990929
CDELT2 = 6.666666666667E-02
CRVAL2 = -9.000000000000E+01
LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole
LATPOLE = -9.000000000000E+01 / Native latitude of celestial pole
EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates
BMAJ = 2.399999936422E-01 / Beam major axis in degrees
BMIN = 2.399999936422E-01 / Beam minor axis in degrees
BPA = 0.000000000000E+00 / Beam position angle in degrees
RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_SZP.hdr 0000644 0001750 0001750 00000002047 00000000000 017636 0 ustar 00tom tom SIMPLE = T
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2
NAXIS1 = 192
NAXIS2 = 192
BUNIT = 'JY/BEAM '
CTYPE1 = 'RA---SZP'
CRPIX1 = -22.62051956373
CDELT1 = -6.666666666667E-02
CRVAL1 = 0.000000000000E+00
CTYPE2 = 'DEC--SZP'
CRPIX2 = -247.8656972779
CDELT2 = 6.666666666667E-02
CRVAL2 = -9.000000000000E+01
LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole
LATPOLE = -9.000000000000E+01 / Native latitude of celestial pole
PV2_1 = 2.000000000000E+00 / Projection parameter 1
PV2_2 = 1.800000000000E+02 / Projection parameter 2
PV2_3 = 6.000000000000E+01 / Projection parameter 3
EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates
BMAJ = 2.399999936422E-01 / Beam major axis in degrees
BMIN = 2.399999936422E-01 / Beam minor axis in degrees
BPA = 0.000000000000E+00 / Beam position angle in degrees
RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_TAN.hdr 0000644 0001750 0001750 00000001577 00000000000 017613 0 ustar 00tom tom SIMPLE = T
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2
NAXIS1 = 192
NAXIS2 = 192
BUNIT = 'JY/BEAM '
CTYPE1 = 'RA---TAN'
CRPIX1 = -0.5630437201085
CDELT1 = -6.666666666667E-02
CRVAL1 = 0.000000000000E+00
CTYPE2 = 'DEC--TAN'
CRPIX2 = -268.0658087122
CDELT2 = 6.666666666667E-02
CRVAL2 = -9.000000000000E+01
LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole
LATPOLE = -9.000000000000E+01 / Native latitude of celestial pole
EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates
BMAJ = 2.399999936422E-01 / Beam major axis in degrees
BMIN = 2.399999936422E-01 / Beam minor axis in degrees
BPA = 0.000000000000E+00 / Beam position angle in degrees
RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_TSC.hdr 0000644 0001750 0001750 00000001577 00000000000 017622 0 ustar 00tom tom SIMPLE = T
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2
NAXIS1 = 192
NAXIS2 = 192
BUNIT = 'JY/BEAM '
CTYPE1 = 'RA---TSC'
CRPIX1 = 20.37416464676
CDELT1 = -6.666666666667E-02
CRVAL1 = 0.000000000000E+00
CTYPE2 = 'DEC--TSC'
CRPIX2 = -189.7220156818
CDELT2 = 6.666666666667E-02
CRVAL2 = -9.000000000000E+01
LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole
LATPOLE = 0.000000000000E+00 / Native latitude of celestial pole
EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates
BMAJ = 2.399999936422E-01 / Beam major axis in degrees
BMIN = 2.399999936422E-01 / Beam minor axis in degrees
BPA = 0.000000000000E+00 / Beam position angle in degrees
RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_ZEA.hdr 0000644 0001750 0001750 00000001577 00000000000 017610 0 ustar 00tom tom SIMPLE = T
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2
NAXIS1 = 192
NAXIS2 = 192
BUNIT = 'JY/BEAM '
CTYPE1 = 'RA---ZEA'
CRPIX1 = 5.738055949994
CDELT1 = -6.666666666667E-02
CRVAL1 = 0.000000000000E+00
CTYPE2 = 'DEC--ZEA'
CRPIX2 = -244.4880690361
CDELT2 = 6.666666666667E-02
CRVAL2 = -9.000000000000E+01
LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole
LATPOLE = -9.000000000000E+01 / Native latitude of celestial pole
EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates
BMAJ = 2.399999936422E-01 / Beam major axis in degrees
BMIN = 2.399999936422E-01 / Beam minor axis in degrees
BPA = 0.000000000000E+00 / Beam position angle in degrees
RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/1904-66_ZPN.hdr 0000644 0001750 0001750 00000003751 00000000000 017634 0 ustar 00tom tom SIMPLE = T
BITPIX = -32 / IEEE (big-endian) 32-bit floating point data
NAXIS = 2
NAXIS1 = 192
NAXIS2 = 192
BUNIT = 'JY/BEAM '
CTYPE1 = 'RA---ZPN'
CRPIX1 = 22.09211120575
CDELT1 = -6.666666666667E-02
CRVAL1 = 0.000000000000E+00
CTYPE2 = 'DEC--ZPN'
CRPIX2 = -183.2937255632
CDELT2 = 6.666666666667E-02
CRVAL2 = -9.000000000000E+01
LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole
LATPOLE = -9.000000000000E+01 / Native latitude of celestial pole
PV2_0 = 5.000000000000E-02 / Projection parameter 0
PV2_1 = 9.750000000000E-01 / Projection parameter 1
PV2_2 = -8.070000000000E-01 / Projection parameter 2
PV2_3 = 3.370000000000E-01 / Projection parameter 3
PV2_4 = -6.500000000000E-02 / Projection parameter 4
PV2_5 = 1.000000000000E-02 / Projection parameter 5
PV2_6 = 3.000000000000E-03 / Projection parameter 6
PV2_7 = -1.000000000000E-03 / Projection parameter 7
PV2_8 = 0.000000000000E+00 / Projection parameter 8
PV2_9 = 0.000000000000E+00 / Projection parameter 9
PV2_10 = 0.000000000000E+00 / Projection parameter 10
PV2_11 = 0.000000000000E+00 / Projection parameter 11
PV2_12 = 0.000000000000E+00 / Projection parameter 12
PV2_13 = 0.000000000000E+00 / Projection parameter 13
PV2_14 = 0.000000000000E+00 / Projection parameter 14
PV2_15 = 0.000000000000E+00 / Projection parameter 15
PV2_16 = 0.000000000000E+00 / Projection parameter 16
PV2_17 = 0.000000000000E+00 / Projection parameter 17
PV2_18 = 0.000000000000E+00 / Projection parameter 18
PV2_19 = 0.000000000000E+00 / Projection parameter 19
EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates
BMAJ = 2.399999936422E-01 / Beam major axis in degrees
BMIN = 2.399999936422E-01 / Beam minor axis in degrees
BPA = 0.000000000000E+00 / Beam position angle in degrees
RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/2MASS_k.hdr 0000644 0001750 0001750 00000001256 00000000000 017414 0 ustar 00tom tom SIMPLE = T
BITPIX = 16
NAXIS = 2
NAXIS1 = 721
NAXIS2 = 720
EXTEND = T / FITS dataset may contain extensions
DATASET = '2MASS '
BAND = 'K '
CDATE = 'Wed Feb 25 11:57:21 2009'
CTYPE1 = 'RA---TAN'
CTYPE2 = 'DEC--TAN'
CRVAL1 = 266.400000
CRVAL2 = -28.933330
CRPIX1 = 361.
CRPIX2 = 360.5
CDELT1 = -0.001388889
CDELT2 = 0.001388889
CROTA2 = 0.000000
EQUINOX = 2000.0
MAGZP = 19.9757
BSCALE = 0.045777764213996
BZERO = 1500.
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/2d_fits/2MASS_k_rot.hdr 0000644 0001750 0001750 00000001256 00000000000 020300 0 ustar 00tom tom SIMPLE = T
BITPIX = 16
NAXIS = 2
NAXIS1 = 721
NAXIS2 = 720
EXTEND = T / FITS dataset may contain extensions
DATASET = '2MASS '
BAND = 'K '
CDATE = 'Wed Feb 25 11:57:21 2009'
CTYPE1 = 'RA---TAN'
CTYPE2 = 'DEC--TAN'
CRVAL1 = 266.400000
CRVAL2 = -28.933330
CRPIX1 = 361.
CRPIX2 = 360.5
CDELT1 = -0.001388889
CDELT2 = 0.001388889
CROTA2 = 30.000000
EQUINOX = 2000.0
MAGZP = 19.9757
BSCALE = 0.045777764213996
BZERO = 1500.
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1646929515.246823
aplpy-2.1.0/aplpy/tests/data/3d_fits/ 0000775 0001750 0001750 00000000000 00000000000 015555 5 ustar 00tom tom ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/3d_fits/cube.hdr 0000644 0001750 0001750 00000001362 00000000000 017172 0 ustar 00tom tom SIMPLE = T / Written by IDL: Fri Oct 27 10:49:59 2006
BITPIX = -32 / bits per data value
NAXIS = 3 / number of axes
NAXIS1 = 11
NAXIS2 = 12
NAXIS3 = 32
EXTEND = T /file may contain extensions
CRVAL1 = 57.6599999999 /
CRPIX1 = -799.000000000 /
CDELT1 = -0.00638888900000
CTYPE1 = 'RA---SFL' /
CRVAL2 = 0.00000000000 /
CRPIX2 = -4741.91300000 /
CDELT2 = 0.00638888900000
CTYPE2 = 'DEC--SFL' /
CRVAL3 = -9959.44378305 /
CRPIX3 = 1.00000 /
CDELT3 = 66.4236100000 /
CTYPE3 = 'VOPT' /
SPECSYS = 'LSRK' /
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/__init__.py 0000644 0001750 0001750 00000000000 00000000000 016317 0 ustar 00tom tom ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tests/data/shapes.reg 0000644 0001750 0001750 00000001316 00000000000 016203 0 ustar 00tom tom # Region file format: DS9 version 4.1
global color=green dashlist=8 3 width=1 font="helvetica 10 normal roman" select=1 highlite=1 dash=0 fixed=0 edit=1 move=1 delete=1 include=1 source=1
fk5
circle(17:46:26.948,-28:45:11.89,330.498")
ellipse(17:44:59.122,-28:44:47.18,505",185",360) # color=red width=2
box(17:45:50.090,-28:55:04.94,320",485",360) # color=blue width=4
line(17:46:39.285,-29:01:56.56,17:45:47.446,-29:06:42.46) # line=0 0
# vector(17:45:32.185,-29:06:07.48,924.175",13.1336) vector=1 color=yellow width=3
# text(17:45:40.580,-29:09:07.48) text={All you regions are belong to us}
panda(17:46:26.192,-28:45:41.91,360,720,4,39.1095",78.2191",1)
ellipse(17:45:09.384,-28:45:32.33,120",37.5",240",75",360)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/tests/helpers.py 0000664 0001750 0001750 00000002475 00000000000 015335 0 ustar 00tom tom import string
import random
import os
import numpy as np
from astropy.io import fits
from astropy.wcs import WCS
def random_id():
return ''.join(random.sample(string.ascii_letters + string.digits, 16))
def generate_header(header_file):
# Read in header
header = fits.Header.fromtextfile(header_file)
return header
def generate_data(header_file):
# Read in header
header = generate_header(header_file)
# Find shape of array
shape = []
for i in range(header['NAXIS']):
shape.append(header['NAXIS%i' % (i + 1)])
# Generate data array
data = np.zeros(shape[::-1])
return data
def generate_hdu(header_file):
# Read in header
header = generate_header(header_file)
# Generate data array
data = generate_data(header_file)
# Generate primary HDU
hdu = fits.PrimaryHDU(data=data, header=header)
return hdu
def generate_wcs(header_file):
# Read in header
header = generate_header(header_file)
# Compute WCS object
wcs = WCS(header)
return wcs
def generate_file(header_file, directory):
# Generate HDU object
hdu = generate_hdu(header_file)
# Write out to a temporary file in the specified directory
filename = os.path.join(directory, random_id() + '.fits')
hdu.writeto(filename)
return filename
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/tests/setup_package.py 0000664 0001750 0001750 00000000176 00000000000 016502 0 ustar 00tom tom
def get_package_data():
return {_ASTROPY_PACKAGE_NAME_ + '.tests': ['coveragerc', 'data/*.reg', 'data/*/*.hdr']} # noqa
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/tests/test_axis_labels.py 0000664 0001750 0001750 00000003135 00000000000 017212 0 ustar 00tom tom import numpy as np
from astropy.tests.helper import pytest
from .. import FITSFigure
def test_axis_labels_show_hide():
data = np.zeros((16, 16))
f = FITSFigure(data)
f.axis_labels.hide()
f.axis_labels.show()
f.axis_labels.hide_x()
f.axis_labels.show_x()
f.axis_labels.hide_y()
f.axis_labels.show_y()
f.close()
def test_axis_labels_text():
data = np.zeros((16, 16))
f = FITSFigure(data)
f.axis_labels.set_xtext('x')
f.axis_labels.set_ytext('y')
f.close()
def test_axis_labels_pad():
data = np.zeros((16, 16))
f = FITSFigure(data)
f.axis_labels.set_xpad(-1.)
f.axis_labels.set_ypad(0.5)
f.close()
def test_axis_labels_position():
data = np.zeros((16, 16))
f = FITSFigure(data)
f.axis_labels.set_xposition('top')
f.axis_labels.set_xposition('bottom')
f.axis_labels.set_yposition('right')
f.axis_labels.set_yposition('left')
f.close()
def test_axis_labels_position_invalid():
data = np.zeros((16, 16))
f = FITSFigure(data)
with pytest.raises(ValueError):
f.axis_labels.set_xposition('right')
with pytest.raises(ValueError):
f.axis_labels.set_xposition('left')
with pytest.raises(ValueError):
f.axis_labels.set_yposition('top')
with pytest.raises(ValueError):
f.axis_labels.set_yposition('bottom')
f.close()
def test_axis_labels_font():
data = np.zeros((16, 16))
f = FITSFigure(data)
f.axis_labels.set_font(size='small', weight='bold', stretch='normal',
family='serif', style='normal', variant='normal')
f.close()
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/tests/test_beam.py 0000664 0001750 0001750 00000012076 00000000000 015634 0 ustar 00tom tom import os
import pytest
import numpy as np
from astropy import units as u
from astropy.io import fits
from .. import FITSFigure
from ..overlays import Beam
header_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/2d_fits')
HEADER = fits.Header.fromtextfile(os.path.join(header_dir, '1904-66_TAN.hdr'))
HDU = fits.PrimaryHDU(np.zeros((16, 16)), HEADER)
def test_beam_add_remove():
f = FITSFigure(HDU)
f.show_grayscale()
f.add_beam(major=0.1, minor=0.04, angle=10.)
f.remove_beam()
f.add_beam(major=0.1, minor=0.04, angle=10.)
f.remove_beam()
f.close()
def test_beam_show_hide():
f = FITSFigure(HDU)
f.show_grayscale()
f.add_beam(major=0.1, minor=0.04, angle=10.)
f.beam.hide()
f.beam.show(major=0.1, minor=0.04, angle=10.)
f.close()
def test_beam_major():
f = FITSFigure(HDU)
f.show_grayscale()
f.add_beam(major=0.1, minor=0.04, angle=10.)
f.beam.set_major(0.5)
f.beam.set_major(1.0)
f.close()
@pytest.mark.parametrize('quantity', [u.arcsec, 5 * u.arcsec, 1 * u.degree, 1 * u.radian])
def test_beam_major_quantity(quantity):
f = FITSFigure(HDU)
f.show_grayscale()
f.add_beam(major=quantity, minor=0.04, angle=10.)
f.beam.set_major(quantity)
f.close()
def test_beam_minor():
f = FITSFigure(HDU)
f.show_grayscale()
f.add_beam(major=0.1, minor=0.04, angle=10.)
f.beam.set_minor(0.05)
f.beam.set_minor(0.08)
f.close()
@pytest.mark.parametrize('quantity', [u.arcsec, 5 * u.arcsec, 1 * u.degree, 1 * u.radian])
def test_beam_minor_quantity(quantity):
f = FITSFigure(HDU)
f.show_grayscale()
f.add_beam(major=0.1, minor=quantity, angle=10.)
assert type(f.beam) != list
f.beam.set_minor(quantity)
f.close()
def test_beam_angle():
f = FITSFigure(HDU)
f.show_grayscale()
f.add_beam(major=0.1, minor=0.04, angle=10.)
f.beam.set_angle(0.)
f.beam.set_angle(55.)
f.close()
@pytest.mark.parametrize('quantity', [u.arcsec, 5 * u.arcsec, 1 * u.degree, 1 * u.radian])
def test_beam_angle_quantity(quantity):
f = FITSFigure(HDU)
f.show_grayscale()
f.add_beam(major=0.1, minor=0.04, angle=quantity)
f.beam.set_angle(quantity)
f.close()
def test_beam_corner():
f = FITSFigure(HDU)
f.add_beam(major=0.1, minor=0.04, angle=10.)
for corner in ['top', 'bottom', 'left', 'right', 'top left', 'top right',
'bottom left', 'bottom right']:
f.beam.set_corner(corner)
f.close()
def test_beam_frame():
f = FITSFigure(HDU)
f.add_beam(major=0.1, minor=0.04, angle=10.)
f.beam.set_frame(True)
f.beam.set_frame(False)
f.close()
def test_beam_borderpad():
f = FITSFigure(HDU)
f.add_beam(major=0.1, minor=0.04, angle=10.)
f.beam.set_borderpad(0.1)
f.beam.set_borderpad(0.3)
f.close()
def test_beam_pad():
f = FITSFigure(HDU)
f.add_beam(major=0.1, minor=0.04, angle=10.)
f.beam.set_pad(0.1)
f.beam.set_pad(0.3)
f.close()
def test_beam_alpha():
f = FITSFigure(HDU)
f.add_beam(major=0.1, minor=0.04, angle=10.)
f.beam.set_alpha(0.1)
f.beam.set_alpha(0.2)
f.beam.set_alpha(0.5)
f.close()
def test_beam_color():
f = FITSFigure(HDU)
f.add_beam(major=0.1, minor=0.04, angle=10.)
f.beam.set_color('black')
f.beam.set_color('#003344')
f.beam.set_color((1.0, 0.4, 0.3))
f.close()
def test_beam_facecolor():
f = FITSFigure(HDU)
f.add_beam(major=0.1, minor=0.04, angle=10.)
f.beam.set_facecolor('black')
f.beam.set_facecolor('#003344')
f.beam.set_facecolor((1.0, 0.4, 0.3))
f.close()
def test_beam_edgecolor():
f = FITSFigure(HDU)
f.add_beam(major=0.1, minor=0.04, angle=10.)
f.beam.set_edgecolor('black')
f.beam.set_edgecolor('#003344')
f.beam.set_edgecolor((1.0, 0.4, 0.3))
f.close()
def test_beam_linestyle():
f = FITSFigure(HDU)
f.add_beam(major=0.1, minor=0.04, angle=10.)
f.beam.set_linestyle('solid')
f.beam.set_linestyle('dotted')
f.beam.set_linestyle('dashed')
f.close()
def test_beam_linewidth():
f = FITSFigure(HDU)
f.add_beam(major=0.1, minor=0.04, angle=10.)
f.beam.set_linewidth(0)
f.beam.set_linewidth(1)
f.beam.set_linewidth(5)
f.close()
def test_beam_hatch():
f = FITSFigure(HDU)
f.add_beam(major=0.1, minor=0.04, angle=10.)
for hatch in ['/', '\\', '|', '-', '+', 'x', 'o', 'O', '.', '*']:
f.beam.set_hatch(hatch)
f.close()
def test_beam_multiple():
f = FITSFigure(HDU)
f.show_grayscale()
f.add_beam(major=0.1, minor=0.04, angle=10.)
f.add_beam(major=0.2, minor=0.08, angle=10.)
f.add_beam(major=0.15, minor=0.08, angle=10.)
assert len(f.beam) == 3
with pytest.raises(Exception) as exc:
f.remove_beam()
assert exc.value.args[0] == ('More than one beam present - use beam_index= '
'to specify which one to remove')
f.remove_beam(beam_index=2)
assert len(f.beam) == 2
f.remove_beam(beam_index=1)
assert isinstance(f.beam, Beam)
f.remove_beam()
assert not hasattr(f, 'beam')
f.close()
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/tests/test_colorbar.py 0000664 0001750 0001750 00000004116 00000000000 016527 0 ustar 00tom tom import numpy as np
from astropy.tests.helper import pytest
from .. import FITSFigure
ARRAY = np.arange(256).reshape((16, 16))
def test_colorbar_invalid():
f = FITSFigure(ARRAY)
with pytest.raises(Exception):
f.add_colorbar() # no grayscale/colorscale was shown
def test_colorbar_addremove():
f = FITSFigure(ARRAY)
f.show_grayscale()
f.add_colorbar()
f.remove_colorbar()
f.add_colorbar()
f.close()
def test_colorbar_showhide():
f = FITSFigure(ARRAY)
f.show_grayscale()
f.add_colorbar()
f.colorbar.hide()
f.colorbar.show()
f.close()
def test_colorbar_location():
f = FITSFigure(ARRAY)
f.show_grayscale()
f.add_colorbar()
f.colorbar.set_location('top')
with pytest.warns(UserWarning, match='Bottom colorbar not fully implemented'):
f.colorbar.set_location('bottom')
with pytest.warns(UserWarning, match='Left colorbar not fully implemented'):
f.colorbar.set_location('left')
f.colorbar.set_location('right')
f.close()
def test_colorbar_width():
f = FITSFigure(ARRAY)
f.show_grayscale()
f.add_colorbar()
f.colorbar.set_width(0.1)
f.colorbar.set_width(0.2)
f.colorbar.set_width(0.5)
f.close()
def test_colorbar_pad():
f = FITSFigure(ARRAY)
f.show_grayscale()
f.add_colorbar()
f.colorbar.set_pad(0.1)
f.colorbar.set_pad(0.2)
f.colorbar.set_pad(0.5)
f.close()
def test_colorbar_font():
f = FITSFigure(ARRAY)
f.show_grayscale()
f.add_colorbar()
f.colorbar.set_font(size='small', weight='bold', stretch='normal',
family='serif', style='normal', variant='normal')
f.close()
def test_colorbar_axis_label():
f = FITSFigure(ARRAY)
f.show_grayscale()
f.add_colorbar()
f.colorbar.set_axis_label_text('Surface Brightness (MJy/sr)')
f.colorbar.set_axis_label_rotation(45.)
f.colorbar.set_axis_label_font(size='small', weight='bold', stretch='normal',
family='serif', style='normal', variant='normal')
f.colorbar.set_axis_label_pad(5.)
f.close()
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/tests/test_contour.py 0000664 0001750 0001750 00000000573 00000000000 016420 0 ustar 00tom tom import pytest
import numpy as np
from .. import FITSFigure
# Test simple contour generation with Numpy example
@pytest.mark.parametrize(('filled'), [True, False])
def test_numpy_contour(filled):
data = np.arange(256).reshape((16, 16))
f = FITSFigure(data)
f.show_grayscale()
f.show_contour(data, levels=np.linspace(1., 254., 10), filled=filled)
f.close()
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/tests/test_convolve.py 0000664 0001750 0001750 00000001530 00000000000 016554 0 ustar 00tom tom import numpy as np
from astropy.io import fits
from .. import FITSFigure
ARRAY = np.arange(256).reshape((16, 16))
def test_convolve_default():
hdu = fits.PrimaryHDU(ARRAY)
f = FITSFigure(hdu)
f.show_grayscale(smooth=3)
f.close()
def test_convolve_gauss():
hdu = fits.PrimaryHDU(ARRAY)
f = FITSFigure(hdu)
f.show_grayscale(kernel='gauss', smooth=3)
f.close()
def test_convolve_box():
hdu = fits.PrimaryHDU(ARRAY)
f = FITSFigure(hdu)
f.show_grayscale(kernel='box', smooth=3)
f.close()
def test_convolve_custom():
hdu = fits.PrimaryHDU(ARRAY)
f = FITSFigure(hdu)
f.show_grayscale(kernel=np.ones((3, 3)))
f.close()
def test_convolve_int():
# Regression test for aplpy/aplpy#165
hdu = fits.PrimaryHDU(ARRAY)
f = FITSFigure(hdu)
f.show_grayscale(smooth=3)
f.close()
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/tests/test_downsample.py 0000664 0001750 0001750 00000000305 00000000000 017071 0 ustar 00tom tom import numpy as np
from .. import FITSFigure
def test_numpy_downsample():
data = np.arange(256).reshape((16, 16))
f = FITSFigure(data, downsample=2)
f.show_grayscale()
f.close()
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/tests/test_frame.py 0000664 0001750 0001750 00000000657 00000000000 016024 0 ustar 00tom tom import numpy as np
from .. import FITSFigure
def test_frame_linewidth():
data = np.zeros((16, 16))
f = FITSFigure(data)
f.frame.set_linewidth(0)
f.frame.set_linewidth(1)
f.frame.set_linewidth(10)
f.close()
def test_frame_color():
data = np.zeros((16, 16))
f = FITSFigure(data)
f.frame.set_color('black')
f.frame.set_color('#003344')
f.frame.set_color((1.0, 0.4, 0.3))
f.close()
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/tests/test_grid.py 0000664 0001750 0001750 00000003025 00000000000 015647 0 ustar 00tom tom import pytest
import numpy as np
from .. import FITSFigure
def test_grid_addremove():
data = np.zeros((16, 16))
f = FITSFigure(data)
f.add_grid()
f.remove_grid()
f.add_grid()
f.close()
def test_grid_showhide():
data = np.zeros((16, 16))
f = FITSFigure(data)
f.add_grid()
f.grid.hide()
f.grid.show()
f.close()
def test_grid_spacing():
data = np.zeros((16, 16))
f = FITSFigure(data)
f.add_grid()
f.grid.set_xspacing(1.)
f.grid.set_xspacing('tick')
with pytest.raises(ValueError):
f.grid.set_xspacing('auto')
f.grid.set_yspacing(2.)
f.grid.set_yspacing('tick')
with pytest.raises(ValueError):
f.grid.set_yspacing('auto')
f.close()
def test_grid_color():
data = np.zeros((16, 16))
f = FITSFigure(data)
f.add_grid()
f.grid.set_color('black')
f.grid.set_color('#003344')
f.grid.set_color((1.0, 0.4, 0.3))
f.close()
def test_grid_alpha():
data = np.zeros((16, 16))
f = FITSFigure(data)
f.add_grid()
f.grid.set_alpha(0.0)
f.grid.set_alpha(0.3)
f.grid.set_alpha(1.0)
f.close()
def test_grid_linestyle():
data = np.zeros((16, 16))
f = FITSFigure(data)
f.add_grid()
f.grid.set_linestyle('solid')
f.grid.set_linestyle('dashed')
f.grid.set_linestyle('dotted')
f.close()
def test_grid_linewidth():
data = np.zeros((16, 16))
f = FITSFigure(data)
f.add_grid()
f.grid.set_linewidth(0)
f.grid.set_linewidth(2)
f.grid.set_linewidth(5)
f.close()
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/tests/test_images.py 0000664 0001750 0001750 00000020531 00000000000 016170 0 ustar 00tom tom import os
import tempfile
import pytest
import numpy as np
from .. import FITSFigure
from .helpers import generate_file
from . import baseline_dir
MODULEDIR = os.path.dirname(__file__)
DATADIR = os.path.abspath(os.path.join(MODULEDIR, 'data'))
class BaseImageTests(object):
@classmethod
def setup_class(cls):
cls._baseline_images_dir = os.path.abspath(os.path.join(MODULEDIR, 'baseline_images'))
header_1 = os.path.join(DATADIR, '2d_fits/1904-66_AIR.hdr')
cls.filename_1 = generate_file(header_1, str(tempfile.mkdtemp()))
header_2 = os.path.join(DATADIR, '2d_fits/2MASS_k.hdr')
cls.filename_2 = generate_file(header_2, str(tempfile.mkdtemp()))
header_3 = os.path.join(DATADIR, '3d_fits/cube.hdr')
cls.filename_3 = generate_file(header_3, str(tempfile.mkdtemp()))
header_4 = os.path.join(DATADIR, '2d_fits/2MASS_k_rot.hdr')
cls.filename_4 = generate_file(header_4, str(tempfile.mkdtemp()))
class TestBasic(BaseImageTests):
# Test for showing grayscale
@pytest.mark.remote_data
@pytest.mark.mpl_image_compare(style={}, savefig_kwargs={'adjust_bbox': False}, baseline_dir=baseline_dir, tolerance=7.5)
def test_basic_image(self):
f = FITSFigure(self.filename_2, figsize=(7, 5))
f.show_grayscale(vmin=0, vmax=1)
return f._figure
@pytest.mark.remote_data
@pytest.mark.mpl_image_compare(style={}, savefig_kwargs={'adjust_bbox': False}, baseline_dir=baseline_dir, tolerance=7.5)
def test_ticks_labels_options(self):
f = FITSFigure(self.filename_2, figsize=(7, 5))
# Force aspect ratio
f.show_grayscale()
f.hide_grayscale()
f.ticks.set_color('black')
f.axis_labels.set_xposition('top')
f.axis_labels.set_yposition('right')
f.axis_labels.set_font(size='medium', weight='medium',
stretch='normal', style='normal')
f.tick_labels.set_xformat('dd:mm:ss.ss')
f.tick_labels.set_yformat('hh:mm:ss.ss')
f.tick_labels.set_style('colons')
f.ticks.set_xspacing(0.2)
f.ticks.set_yspacing(0.2)
f.ticks.set_minor_frequency(10)
return f._figure
# Test for showing colorscale
@pytest.mark.remote_data
@pytest.mark.mpl_image_compare(style={}, savefig_kwargs={'adjust_bbox': False}, baseline_dir=baseline_dir, tolerance=5)
def test_show_colorbar_scalebar_beam(self):
f = FITSFigure(self.filename_1, figsize=(7, 5))
f.ticks.set_color('black')
f.show_colorscale(vmin=-0.1, vmax=0.1)
f.add_colorbar()
f.add_scalebar(7.5)
f.add_beam(major=0.5, minor=0.2, angle=10.)
f.tick_labels.hide()
return f._figure
# Test for overlaying shapes
@pytest.mark.remote_data
@pytest.mark.mpl_image_compare(style={}, savefig_kwargs={'adjust_bbox': False}, baseline_dir=baseline_dir, tolerance=1.5)
def test_overlay_shapes(self):
f = FITSFigure(self.filename_1, figsize=(7, 5))
# Force aspect ratio
f.show_grayscale()
f.hide_grayscale()
f.ticks.set_color('black')
# Markers
f.show_markers([360., 350., 340.], [-61., -62., -63], color='cyan')
f.show_markers([30, 40], [50, 70], coords_frame='pixel', edgecolor='blue')
# Circles
f.show_circles([360., 350., 340.], [-61., -62., -63], [0.5, 0.4, 0.3], edgecolor='purple')
f.show_circles([30, 40], [50, 70], [10, 20], coords_frame='pixel', edgecolor='orange')
# Ellipses
f.show_ellipses(340., -66., 1.5, 2., 10., edgecolor='red')
f.show_ellipses(120, 60, 20, 40, 20., coords_frame='pixel', edgecolor='green')
# Rectangles
f.show_rectangles([355., 350.], [-71, -72], [0.5, 1], [2, 1], angle=[20, 30], edgecolor='magenta')
f.show_rectangles([66, 80], [20, 30], [10, 14], [20, 22], angle=[20, 30], coords_frame='pixel', edgecolor='yellow')
# Arrows
f.show_arrows([340., 360], [-72, -68], [2, -2], [2, 2])
# Polygons
poly = np.array([[330, 340, 360], [-65, -61, -63]])
f.show_polygons([poly], edgecolor='0.3', zorder=10)
# Lines
f.show_lines([poly], zorder=9, lw=5, color='red', alpha=0.5)
# Labels
f.add_label(350., -66., 'text')
f.add_label(0.4, 0.25, 'text', relative=True)
f.frame.set_linewidth(1) # points
f.frame.set_color('black')
f.axis_labels.hide()
return f._figure
# Test for grid
@pytest.mark.remote_data
@pytest.mark.mpl_image_compare(style={}, savefig_kwargs={'adjust_bbox': False}, baseline_dir=baseline_dir, tolerance=7.5)
def test_grid(self):
f = FITSFigure(self.filename_1, figsize=(7, 5))
# Force aspect ratio
f.show_grayscale()
f.hide_grayscale()
f.ticks.set_color('black')
f.add_grid()
f.grid.set_color('red')
f.grid.set_alpha(0.8)
f.grid.set_linestyle('solid')
f.grid.set_xspacing('tick')
f.grid.set_yspacing(3)
return f._figure
# Test recenter
@pytest.mark.remote_data
@pytest.mark.mpl_image_compare(style={}, savefig_kwargs={'adjust_bbox': False}, baseline_dir=baseline_dir, tolerance=1.5)
def test_recenter(self):
f = FITSFigure(self.filename_2, figsize=(7, 5))
# Force aspect ratio
f.show_grayscale()
f.hide_grayscale()
f.ticks.set_color('black')
f.recenter(266.5, -29.0, width=0.1, height=0.1)
f.axis_labels.set_xpad(20)
f.axis_labels.set_ypad(20)
return f._figure
# Test overlaying contours
@pytest.mark.remote_data
@pytest.mark.mpl_image_compare(style={}, savefig_kwargs={'adjust_bbox': False}, baseline_dir=baseline_dir, tolerance=5)
def test_contours(self):
data = np.arange(256).reshape((16, 16))
f = FITSFigure(data, figsize=(7, 5))
# Force aspect ratio
f.show_grayscale()
f.hide_grayscale()
f.ticks.set_color('black')
f.show_contour(data, levels=np.linspace(1., 254., 10), filled=False)
return f._figure
# Test cube slice
@pytest.mark.remote_data
@pytest.mark.mpl_image_compare(style={}, savefig_kwargs={'adjust_bbox': False}, baseline_dir=baseline_dir, tolerance=5)
def test_cube_slice(self):
f = FITSFigure(self.filename_3, dimensions=[2, 0], slices=[10], figsize=(7, 5), subplot=[0.25, 0.1, 0.7, 0.8])
f.ticks.set_color('black')
f.add_grid()
f.grid.set_color('black')
f.grid.set_linestyle('solid')
f.grid.set_xspacing(250)
f.grid.set_yspacing(0.01)
f.tick_labels.set_xformat('%g')
f.tick_labels.set_yformat('dd:mm:ss.ss')
return f._figure
# Test for ds9 regions
@pytest.mark.remote_data
@pytest.mark.mpl_image_compare(style={}, savefig_kwargs={'adjust_bbox': False}, baseline_dir=baseline_dir, tolerance=5)
def test_regions(self):
f = FITSFigure(self.filename_2, figsize=(7, 5))
# Force aspect ratio
f.show_grayscale()
f.hide_grayscale()
f.show_regions(os.path.join(DATADIR, 'shapes.reg'))
f.axis_labels.hide()
f.tick_labels.hide()
f.ticks.hide()
return f._figure
@pytest.mark.remote_data
@pytest.mark.mpl_image_compare(style={}, savefig_kwargs={'adjust_bbox': False}, baseline_dir=baseline_dir, tolerance=5)
def test_north(self):
f = FITSFigure(self.filename_4, figsize=(3, 3), north=True)
f.show_grayscale(vmin=-1, vmax=1)
f.axis_labels.hide()
f.tick_labels.hide()
f.ticks.hide()
return f._figure
@pytest.mark.remote_data
@pytest.mark.mpl_image_compare(style={}, savefig_kwargs={'adjust_bbox': False}, baseline_dir=baseline_dir, tolerance=5)
def test_downsample(self):
data = np.arange(256).reshape((16, 16))
f = FITSFigure(data, downsample=2)
f.show_grayscale()
return f._figure
@pytest.mark.remote_data
@pytest.mark.mpl_image_compare(style={}, savefig_kwargs={'adjust_bbox': False}, baseline_dir=baseline_dir, tolerance=5)
def test_set_nan_color(self):
data = np.arange(56, dtype=float).reshape((8, 7))
data[3, :] = np.nan
f = FITSFigure(data, figsize=(3, 3))
f.show_colorscale()
f.axis_labels.hide()
f.tick_labels.hide()
f.ticks.hide()
f.set_nan_color('black')
return f._figure
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/tests/test_init_cube.py 0000664 0001750 0001750 00000007411 00000000000 016666 0 ustar 00tom tom import os
import pytest
import numpy as np
from astropy.io import fits
from .helpers import generate_file, generate_hdu, generate_wcs
from .. import FITSFigure
# The tests in this file check that the initialization and basic plotting do
# not crash for FITS files with 3+ dimensions. No reference images are
# required here.
ROOT = os.path.dirname(os.path.abspath(__file__))
HEADER_DIR = os.path.join(ROOT, 'data/3d_fits')
HEADERS = [os.path.join(HEADER_DIR, 'cube.hdr')]
REFERENCE = os.path.join(HEADER_DIR, 'cube.hdr')
VALID_DIMENSIONS = [(0, 1), (1, 0), (0, 2), (2, 0), (1, 2), (2, 1)]
INVALID_DIMENSIONS = [None, (1,), (0, 3), (-4, 2), (1, 1), (2, 2), (3, 3),
(1, 2, 3), (3, 5, 3, 2)]
# Test initialization through a filename
def test_file_init(tmpdir):
filename = generate_file(REFERENCE, str(tmpdir))
f = FITSFigure(filename, slices=[5])
f.show_grayscale()
f.close()
# Test initialization through an HDU object
def test_hdu_init():
hdu = generate_hdu(REFERENCE)
f = FITSFigure(hdu, slices=[5])
f.show_grayscale()
f.close()
# Test initialization through a WCS object
def test_wcs_init():
wcs = generate_wcs(REFERENCE)
with pytest.raises(ValueError) as exc:
FITSFigure(wcs, slices=[5])
assert exc.value.args[0] == "FITSFigure initialization via WCS objects can only be done with 2-dimensional WCS objects"
# Test initialization through an HDU object (no WCS)
def test_hdu_nowcs_init():
data = np.zeros((16, 16, 16))
hdu = fits.PrimaryHDU(data)
f = FITSFigure(hdu, slices=[5])
f.show_grayscale()
f.close()
# Test initalization through a Numpy array (no WCS)
def test_numpy_nowcs_init():
data = np.zeros((16, 16, 16))
f = FITSFigure(data, slices=[5])
f.show_grayscale()
f.close()
# Test that initialization without specifying slices raises an exception for a
# true 3D cube
def test_hdu_noslices():
hdu = generate_hdu(REFERENCE)
with pytest.raises(Exception):
FITSFigure(hdu)
# Test that initialization without specifying slices does *not* raise an
# exception if the remaining dimensions have size 1.
def test_hdu_noslices_2d():
data = np.zeros((1, 16, 16))
f = FITSFigure(data)
f.show_grayscale()
f.close()
# Now check initialization with valid and invalid dimensions. We just need to
# tes with HDU objects since we already tested that reading from files is ok.
# Test initialization with valid dimensions
@pytest.mark.parametrize(('dimensions'), VALID_DIMENSIONS)
def test_init_dimensions_valid(dimensions):
hdu = generate_hdu(REFERENCE)
f = FITSFigure(hdu, dimensions=dimensions, slices=[5])
f.show_grayscale()
f.close()
# Test initialization with invalid dimensions
@pytest.mark.parametrize(('dimensions'), INVALID_DIMENSIONS)
def test_init_dimensions_invalid(dimensions):
hdu = generate_hdu(REFERENCE)
with pytest.raises(ValueError):
FITSFigure(hdu, dimensions=dimensions, slices=[5])
# Now check initialization of different WCS projections, and we check only
# valid dimensions
valid_parameters = []
for h in HEADERS:
for d in VALID_DIMENSIONS:
valid_parameters.append((h, d))
@pytest.mark.parametrize(('header', 'dimensions'), valid_parameters)
def test_init_extensive_wcs(tmpdir, header, dimensions):
filename = generate_file(header, str(tmpdir))
f = FITSFigure(filename, dimensions=dimensions, slices=[5])
f.show_grayscale()
f.close()
# Test that recenter works for cube slices
# TODO: remove xfail once pix2world transformations work for multidimensional
# datasets
@pytest.mark.xfail
def test_recenter_cube_slices():
data = np.zeros((16, 16, 16))
hdu = fits.PrimaryHDU(data)
f = FITSFigure(hdu, slices=[5])
f.show_grayscale()
f.recenter(5., 5., width=3., height=3.)
f.close()
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/tests/test_init_image.py 0000664 0001750 0001750 00000013652 00000000000 017036 0 ustar 00tom tom import os
import pytest
import numpy as np
from astropy.io import fits
from astropy.wcs import WCS as AstropyWCS
from .helpers import generate_file, generate_hdu, generate_wcs
from .. import FITSFigure
# The tests in this file check that the initialization and basic plotting do
# not crash for FITS files with 2 dimensions. No reference images are
# required here.
header_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/2d_fits')
HEADERS = [os.path.join(header_dir, '1904-66_AIR.hdr'),
os.path.join(header_dir, '1904-66_AIT.hdr'),
os.path.join(header_dir, '1904-66_ARC.hdr'),
os.path.join(header_dir, '1904-66_AZP.hdr'),
os.path.join(header_dir, '1904-66_BON.hdr'),
os.path.join(header_dir, '1904-66_CAR.hdr'),
os.path.join(header_dir, '1904-66_CEA.hdr'),
os.path.join(header_dir, '1904-66_COD.hdr'),
os.path.join(header_dir, '1904-66_COE.hdr'),
os.path.join(header_dir, '1904-66_COO.hdr'),
os.path.join(header_dir, '1904-66_COP.hdr'),
os.path.join(header_dir, '1904-66_CSC.hdr'),
os.path.join(header_dir, '1904-66_CYP.hdr'),
os.path.join(header_dir, '1904-66_HPX.hdr'),
os.path.join(header_dir, '1904-66_MER.hdr'),
os.path.join(header_dir, '1904-66_MOL.hdr'),
os.path.join(header_dir, '1904-66_NCP.hdr'),
os.path.join(header_dir, '1904-66_PAR.hdr'),
os.path.join(header_dir, '1904-66_PCO.hdr'),
os.path.join(header_dir, '1904-66_QSC.hdr'),
os.path.join(header_dir, '1904-66_SFL.hdr'),
os.path.join(header_dir, '1904-66_SIN.hdr'),
os.path.join(header_dir, '1904-66_STG.hdr'),
os.path.join(header_dir, '1904-66_SZP.hdr'),
os.path.join(header_dir, '1904-66_TAN.hdr'),
os.path.join(header_dir, '1904-66_TSC.hdr'),
os.path.join(header_dir, '1904-66_ZEA.hdr'),
os.path.join(header_dir, '1904-66_ZPN.hdr')]
REFERENCE = os.path.join(header_dir, '1904-66_TAN.hdr')
CAR_REFERENCE = os.path.join(header_dir, '1904-66_CAR.hdr')
VALID_DIMENSIONS = [(0, 1), (1, 0)]
INVALID_DIMENSIONS = [None, (1,), (0, 2), (-4, 2), (1, 1), (2, 2), (1, 2, 3)]
# Test initialization through a filename
def test_file_init(tmpdir):
filename = generate_file(REFERENCE, str(tmpdir))
f = FITSFigure(filename)
f.show_grayscale()
f.close()
# Test initialization through an HDU object
def test_hdu_init():
hdu = generate_hdu(REFERENCE)
f = FITSFigure(hdu)
f.show_grayscale()
f.close()
# Test initialization through a WCS object
def test_wcs_init():
wcs = generate_wcs(REFERENCE)
f = FITSFigure(wcs)
f.show_grayscale()
f.close()
# Test initialization through a WCS object with wcs.to_header() as a go-between
# specifically for testing the cd -> pc -> cd hack, and has particular importance
# for AVM-generated headers
def test_wcs_toheader_init():
wcs = generate_wcs(REFERENCE)
header_ = fits.Header.fromtextfile(REFERENCE)
header = wcs.to_header()
wcs2 = AstropyWCS(header)
wcs2.pixel_shape = wcs.pixel_shape = (header_['NAXIS1'], header_['NAXIS2'])
f = FITSFigure(wcs2)
f.show_grayscale()
f.add_grid()
f.close()
# Test initialization through an HDU object (no WCS)
def test_hdu_nowcs_init():
data = np.zeros((16, 16))
hdu = fits.PrimaryHDU(data)
f = FITSFigure(hdu)
f.show_grayscale()
f.close()
# Test initalization through a Numpy array (no WCS)
def test_numpy_nowcs_init():
data = np.zeros((16, 16))
f = FITSFigure(data)
f.show_grayscale()
f.close()
# Now check initialization with valid and invalid dimensions. We just need to
# tes with HDU objects since we already tested that reading from files is ok.
# Test initialization with valid dimensions
@pytest.mark.parametrize(('dimensions'), VALID_DIMENSIONS)
def test_init_dimensions_valid(dimensions):
hdu = generate_hdu(REFERENCE)
f = FITSFigure(hdu, dimensions=dimensions)
f.show_grayscale()
f.close()
# Test initialization with invalid dimensions
@pytest.mark.parametrize(('dimensions'), INVALID_DIMENSIONS)
def test_init_dimensions_invalid(dimensions):
hdu = generate_hdu(REFERENCE)
with pytest.raises(ValueError):
FITSFigure(hdu, dimensions=dimensions)
# Now check initialization of different WCS projections, and we check only
# valid dimensions
valid_parameters = []
for h in HEADERS:
for d in VALID_DIMENSIONS:
valid_parameters.append((h, d))
@pytest.mark.parametrize(('header', 'dimensions'), valid_parameters)
def test_init_extensive_wcs(header, dimensions):
hdu = generate_hdu(header)
if 'CAR' in header:
f = FITSFigure(hdu, dimensions=dimensions, convention='calabretta')
else:
f = FITSFigure(hdu, dimensions=dimensions)
f.show_grayscale()
f.add_grid()
f.close()
# Check that for CAR projections, an exception is raised if no convention is specified
@pytest.mark.parametrize(('dimensions'), VALID_DIMENSIONS)
def test_init_car_invalid(dimensions):
hdu = generate_hdu(CAR_REFERENCE)
with pytest.raises(Exception):
FITSFigure(hdu, dimensions=dimensions)
# Check that images containing only NaN or Inf values don't crash FITSFigure
def test_init_only_naninf():
data = np.ones((10, 10)) * np.nan
data[::2, ::2] = np.inf
f = FITSFigure(data)
f.show_grayscale()
f.show_colorscale()
def test_init_single_pixel():
data = np.zeros((4, 4))
data[...] = np.nan
data[2, 2] = 1
f = FITSFigure(data)
f.show_grayscale()
def test_not_first_hdu(tmpdir):
# Test that data is fetched from the first compatible HDU with data
filename = tmpdir.join('test.fits').strpath
hdu0 = fits.PrimaryHDU()
hdu1 = fits.BinTableHDU()
hdu2 = fits.ImageHDU(np.zeros((12, 12)))
hdulist = fits.HDUList([hdu0, hdu1, hdu2])
hdulist.writeto(filename)
f = FITSFigure(filename)
assert f._data.shape == (12, 12)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/tests/test_layers.py 0000664 0001750 0001750 00000012214 00000000000 016221 0 ustar 00tom tom import pytest
import numpy as np
from .. import FITSFigure
def test_layers(capsys):
f = FITSFigure(np.arange(256).reshape((16, 16)))
capsys.readouterr()
# No layers
f.list_layers()
captured = capsys.readouterr()
assert captured.out.strip() == ('There are no layers in this figure')
# Two layers
f.show_markers([360., 350., 340.], [-61., -62., -63], color='cyan', layer='markers')
f.show_markers([360., 350., 340.], [-61., -62., -63], color='cyan', layer='extra_markers')
capsys.readouterr()
f.list_layers()
captured = capsys.readouterr()
assert captured.out.strip() == ('There are 2 layers in this figure:\n\n'
' -> markers\n'
' -> extra_markers')
# Check removing layers
f.remove_layer('extra_markers')
f.list_layers()
captured = capsys.readouterr()
assert captured.out.strip() == ('There is one layer in this figure:\n\n'
' -> markers')
# Check all layer types and overwriting layers
f.show_markers([30, 40], [50, 70], layer='markers')
f.show_circles([360., 350., 340.], [-61., -62., -63], [0.5, 0.4, 0.3], layer='circles')
f.show_circles([30, 40], [50, 70], [10, 20], layer='circles')
f.show_ellipses(340., -66., 1.5, 2., 10., layer='ellipses')
f.show_ellipses(120, 60, 20, 40, 20., layer='ellipses')
f.show_rectangles([355., 350.], [-71, -72], [0.5, 1], [2, 1], angle=[20, 30], layer='rectangles')
f.show_rectangles([66, 80], [20, 30], [10, 14], [20, 22], angle=[20, 30], layer='rectangles')
f.show_arrows([340., 360], [-72, -68], [2, -2], [2, 2], layer='arrows')
f.show_arrows([340., 360], [-72, -68], [2, -2], [2, 2], layer='arrows')
poly = np.array([[330, 340, 360], [-65, -61, -63]])
f.show_polygons([poly], layer='polygons')
f.show_polygons([poly], layer='polygons')
f.show_lines([poly], layer='lines')
f.show_lines([poly], layer='lines')
f.add_label(350., -66., 'text', layer='label')
f.add_label(0.4, 0.25, 'text', relative=True, layer='label')
# FIXME: Uncomment the following once
# https://github.com/astropy/astropy/pull/8321 is merged into Astropy and in
# a bug fix release.
# f.show_contour(layer='contours')
f.list_layers()
captured = capsys.readouterr()
assert captured.out.strip() == ('There are 8 layers in this figure:\n\n'
' -> markers\n'
' -> circles\n'
' -> ellipses\n'
' -> rectangles\n'
' -> arrows\n'
' -> polygons\n'
' -> lines\n'
' -> label')
# Hiding/showing layers
f.hide_layer('ellipses')
f.hide_layer('polygons')
f.list_layers()
captured = capsys.readouterr()
assert captured.out.strip() == ('There are 8 layers in this figure:\n\n'
' -> markers\n'
' -> circles\n'
' -> ellipses (hidden)\n'
' -> rectangles\n'
' -> arrows\n'
' -> polygons (hidden)\n'
' -> lines\n'
' -> label')
layer = f.get_layer('circles')
assert layer.get_visible()
layer = f.get_layer('ellipses')
assert not layer.get_visible()
f.show_layer('ellipses')
f.list_layers()
captured = capsys.readouterr()
assert captured.out.strip() == ('There are 8 layers in this figure:\n\n'
' -> markers\n'
' -> circles\n'
' -> ellipses\n'
' -> rectangles\n'
' -> arrows\n'
' -> polygons (hidden)\n'
' -> lines\n'
' -> label')
layer = f.get_layer('ellipses')
assert layer.get_visible()
def test_non_existent_layers():
# Handling non-existent layers
f = FITSFigure(np.arange(256).reshape((16, 16)))
with pytest.raises(Exception) as exc:
f.get_layer('banana')
assert exc.value.args[0] == 'Layer banana does not exist'
assert f.get_layer('banana', raise_exception=False) is None
with pytest.raises(Exception) as exc:
f.show_layer('banana')
assert exc.value.args[0] == 'Layer banana does not exist'
f.show_layer('banana', raise_exception=False)
with pytest.raises(Exception) as exc:
f.hide_layer('banana')
assert exc.value.args[0] == 'Layer banana does not exist'
f.hide_layer('banana', raise_exception=False)
with pytest.raises(Exception) as exc:
f.remove_layer('banana')
assert exc.value.args[0] == 'Layer banana does not exist'
f.remove_layer('banana', raise_exception=False)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/tests/test_misc.py 0000664 0001750 0001750 00000002040 00000000000 015651 0 ustar 00tom tom import numpy as np
from ..core import FITSFigure
def test_nan_color_copy():
"""
Regression test to ensure that NaN values set in one image don't affect
global Matplotlib colormap.
"""
data = np.zeros((16, 16))
f1 = FITSFigure(data)
f1.show_grayscale()
f1.set_nan_color('blue')
f2 = FITSFigure(data)
f2.show_grayscale()
f2.set_nan_color('red')
assert f1.image.get_cmap()._rgba_bad == (0.0, 0.0, 1.0, 1.0)
assert f2.image.get_cmap()._rgba_bad == (1.0, 0.0, 0.0, 1.0)
def test_stretches():
# Regression test to make sure none of the stretches crash
data = np.arange(256).reshape((16, 16))
f = FITSFigure(data)
f.show_grayscale()
f.show_grayscale(stretch='linear')
f.show_grayscale(stretch='sqrt')
f.show_grayscale(stretch='log')
f.show_grayscale(stretch='arcsinh')
f.show_grayscale(stretch='power')
f.show_grayscale(stretch='log', vmid=-10)
f.show_grayscale(stretch='arcsinh', vmid=10)
f.show_grayscale(stretch='power', exponent=3.0)
f.close()
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/tests/test_pixworldmarkers.py 0000664 0001750 0001750 00000003460 00000000000 020162 0 ustar 00tom tom import os
import pytest
import numpy as np
from astropy.table import Table
from astropy.io import fits
from .helpers import generate_wcs
from .. import FITSFigure
HEADER = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/2d_fits', '1904-66_TAN.hdr')
tab = Table({'RA': [347., 349.], 'DEC': [-68., -68]})
GOOD_INPUT = [[1, 2],
[[1, 2], [3, 4]],
[np.arange(2), np.arange(2)],
[tab['RA'], tab['DEC']]
]
BAD_INPUT = [[1, ['s', 'e']],
[np.arange(2), np.sum],
[tab['RA'], 'ewr']
]
@pytest.mark.parametrize(('inputval'), GOOD_INPUT)
def test_pixel_coords(inputval):
data = np.zeros((16, 16))
f = FITSFigure(data)
f.show_markers(inputval[0], inputval[1])
f.close()
@pytest.mark.parametrize(('inputval'), GOOD_INPUT)
def test_wcs_coords(inputval):
wcs = generate_wcs(HEADER)
header = fits.Header.fromtextfile(HEADER)
wcs.naxis1 = header['NAXIS1']
wcs.naxis2 = header['NAXIS2']
f = FITSFigure(wcs)
f.show_markers(inputval[0], inputval[1])
f.close()
@pytest.mark.parametrize(('inputval'), BAD_INPUT)
def test_pixel_coords_bad(inputval):
data = np.zeros((16, 16))
f = FITSFigure(data)
with pytest.raises(Exception) as exc:
f.show_markers(inputval[0], inputval[1])
assert exc.value.args[0] == "x and y must be the same size"
f.close()
@pytest.mark.parametrize(('inputval'), BAD_INPUT)
def test_wcs_coords_bad(inputval):
wcs = generate_wcs(HEADER)
header = fits.Header.fromtextfile(HEADER)
wcs.naxis1 = header['NAXIS1']
wcs.naxis2 = header['NAXIS2']
f = FITSFigure(wcs)
with pytest.raises(Exception) as exc:
f.show_markers(inputval[0], inputval[1])
f.close()
assert exc.value.args[0] == "x and y must be the same size"
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/tests/test_rgb.py 0000664 0001750 0001750 00000007522 00000000000 015502 0 ustar 00tom tom import os
import warnings
import pytest
import numpy as np
from astropy.io import fits
from astropy.coordinates import Galactic
from .. import FITSFigure
from ..rgb import make_rgb_image, make_rgb_cube
from .test_images import BaseImageTests
from . import baseline_dir
from .helpers import generate_header
ROOT = os.path.dirname(os.path.abspath(__file__))
HEADER = os.path.join(ROOT, 'data/2d_fits', '1904-66_TAN.hdr')
class TestRGB(BaseImageTests):
@pytest.mark.remote_data
@pytest.mark.mpl_image_compare(style={}, savefig_kwargs={'adjust_bbox': False},
baseline_dir=baseline_dir, tolerance=7.5,
filename='test_rgb.png')
@pytest.mark.parametrize('embed_avm_tags', (False, True))
def test_rgb(self, tmpdir, embed_avm_tags):
# Regression test to check that RGB recenter works properly
r_file = tmpdir.join('r.fits').strpath
g_file = tmpdir.join('g.fits').strpath
b_file = tmpdir.join('b.fits').strpath
rgb_file = tmpdir.join('rgb.png').strpath
np.random.seed(12345)
header = fits.Header.fromtextfile(HEADER)
r = fits.PrimaryHDU(np.random.random((12, 12)), header)
r.writeto(r_file)
g = fits.PrimaryHDU(np.random.random((12, 12)), header)
g.writeto(g_file)
b = fits.PrimaryHDU(np.random.random((12, 12)), header)
b.writeto(b_file)
make_rgb_image([r_file, g_file, b_file], rgb_file, embed_avm_tags=embed_avm_tags)
if embed_avm_tags:
f = FITSFigure(rgb_file, figsize=(7, 5))
else:
f = FITSFigure(r_file, figsize=(7, 5))
with warnings.catch_warnings():
warnings.simplefilter("ignore")
f.show_rgb(rgb_file)
f.tick_labels.set_xformat('dd.d')
f.tick_labels.set_yformat('dd.d')
f.recenter(359.3, -72.1, radius=0.05)
return f._figure
@pytest.mark.remote_data
@pytest.mark.parametrize('north', ['default', 'galactic', 'false'])
@pytest.mark.mpl_image_compare(style={}, savefig_kwargs={'adjust_bbox': False},
baseline_dir=baseline_dir, tolerance=7.5)
def test_make_rgb_cube(self, tmpdir, north):
# Regression test to check that RGB recenter works properly
header = generate_header(os.path.join(ROOT, 'data', '2d_fits', '2MASS_k_rot.hdr'))
header['CRPIX1'] = 6.5
header['CRPIX2'] = 6.5
header_r = header.copy()
header_r['CROTA2'] = 5
header_g = header.copy()
header_g['CROTA2'] = 35
header_b = header.copy()
header_b['CROTA2'] = 70
r_file = tmpdir.join('r.fits').strpath
g_file = tmpdir.join('g.fits').strpath
b_file = tmpdir.join('b.fits').strpath
rgb_cube = tmpdir.join('rgb.fits').strpath
rgb_file = tmpdir.join('rgb.png').strpath
header = fits.Header.fromtextfile(HEADER)
r = fits.PrimaryHDU(np.ones((128, 128)), header_r)
r.writeto(r_file)
g = fits.PrimaryHDU(np.ones((128, 128)), header_g)
g.writeto(g_file)
b = fits.PrimaryHDU(np.ones((128, 128)), header_b)
b.writeto(b_file)
if north == 'default':
kwargs = {}
elif north == 'galactic':
kwargs = {'north': Galactic()}
elif north == 'false':
kwargs = {'north': False}
make_rgb_cube([r_file, g_file, b_file], rgb_cube, **kwargs)
make_rgb_image(rgb_cube, rgb_file, embed_avm_tags=True,
vmin_r=0, vmax_r=1, vmin_g=0, vmax_g=1, vmin_b=0, vmax_b=1)
f = FITSFigure(rgb_file, figsize=(3, 3))
with warnings.catch_warnings():
warnings.simplefilter("ignore")
f.show_rgb(rgb_file)
f.tick_labels.hide()
f.axis_labels.hide()
f.add_grid()
return f._figure
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/tests/test_save.py 0000664 0001750 0001750 00000005277 00000000000 015673 0 ustar 00tom tom import os
import sys
from io import BytesIO as StringIO
import pytest
import numpy as np
from .. import FITSFigure
FORMATS = [None, 'png', 'pdf', 'eps', 'ps', 'svg']
ARRAY = np.arange(256).reshape((16, 16))
def is_format(filename, format):
if isinstance(filename, str):
f = open(filename, 'rb')
else:
f = filename
if format == 'png':
return f.read(8) == b'\x89\x50\x4e\x47\x0d\x0a\x1a\x0a'
elif format == 'pdf':
return f.read(4) == b'\x25\x50\x44\x46'
elif format == 'eps':
return f.read(23) == b'%!PS-Adobe-3.0 EPSF-3.0'
elif format == 'ps':
return f.read(14) == b'%!PS-Adobe-3.0'
elif format == 'svg':
from xml.dom import minidom
return minidom.parse(f).childNodes[-1].attributes['xmlns'].value == 'http://www.w3.org/2000/svg'
else:
raise Exception("Unknown format: %s" % format)
@pytest.mark.parametrize(('format'), FORMATS)
def test_write_png(tmpdir, format):
filename = os.path.join(str(tmpdir), 'test_output.png')
f = FITSFigure(ARRAY)
f.show_grayscale()
try:
f.save(filename, format=format)
except TypeError:
pytest.xfail()
finally:
f.close()
if format is None:
assert is_format(filename, 'png')
else:
assert is_format(filename, format)
@pytest.mark.parametrize(('format'), FORMATS)
def test_write_pdf(tmpdir, format):
filename = os.path.join(str(tmpdir), 'test_output.pdf')
f = FITSFigure(ARRAY)
f.show_grayscale()
try:
f.save(filename, format=format)
except TypeError:
pytest.xfail()
finally:
f.close()
if format is None:
assert is_format(filename, 'pdf')
else:
assert is_format(filename, format)
@pytest.mark.parametrize(('format'), FORMATS)
def test_write_eps(tmpdir, format):
filename = os.path.join(str(tmpdir), 'test_output.eps')
f = FITSFigure(ARRAY)
f.show_grayscale()
try:
f.save(filename, format=format)
except TypeError:
pytest.xfail()
finally:
f.close()
if format is None:
assert is_format(filename, 'eps')
else:
assert is_format(filename, format)
@pytest.mark.parametrize(('format'), FORMATS)
def test_write_stringio(tmpdir, format):
s = StringIO()
f = FITSFigure(ARRAY)
f.show_grayscale()
try:
f.save(s, format=format)
except TypeError:
pytest.xfail()
finally:
f.close()
try:
s.seek(0)
except ValueError:
if format == 'svg' and sys.version_info[:2] >= (3, 3):
pytest.xfail()
else:
raise
if format is None:
assert is_format(s, 'png')
else:
assert is_format(s, format)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/tests/test_scalebar.py 0000664 0001750 0001750 00000005255 00000000000 016505 0 ustar 00tom tom import os
import pytest
import numpy as np
from astropy import units as u
from astropy.io import fits
from .. import FITSFigure
ROOT = os.path.dirname(os.path.abspath(__file__))
HEADER_DIR = os.path.join(ROOT, 'data/2d_fits')
HEADER = fits.Header.fromtextfile(os.path.join(HEADER_DIR, '1904-66_TAN.hdr'))
HDU = fits.PrimaryHDU(np.zeros((16, 16)), HEADER)
def test_scalebar_add_invalid():
f = FITSFigure(HDU)
with pytest.raises(TypeError):
f.add_scalebar()
def test_scalebar_addremove():
f = FITSFigure(HDU)
f.add_scalebar(0.1)
f.remove_scalebar()
f.add_scalebar(0.1)
f.close()
def test_scalebar_showhide():
f = FITSFigure(HDU)
f.add_scalebar(0.1)
f.scalebar.hide()
f.scalebar.show(0.1)
f.close()
def test_scalebar_length():
f = FITSFigure(HDU)
f.add_scalebar(0.1)
f.scalebar.set_length(0.01)
f.scalebar.set_length(0.1)
f.close()
@pytest.mark.parametrize('quantity', [1 * u.arcsec, u.arcsec, 2 * u.degree, 5 * u.radian])
def test_scalebar_length_quantity(quantity):
f = FITSFigure(HDU)
f.add_scalebar(quantity)
f.scalebar.set_length(quantity)
f.close()
def test_scalebar_label():
f = FITSFigure(HDU)
f.add_scalebar(0.1)
f.scalebar.set_label('1 pc')
f.scalebar.set_label('5 AU')
f.scalebar.set_label('2"')
f.close()
def test_scalebar_corner():
f = FITSFigure(HDU)
f.add_scalebar(0.1)
for corner in ['top', 'bottom', 'left', 'right', 'top left', 'top right',
'bottom left', 'bottom right']:
f.scalebar.set_corner(corner)
f.close()
def test_scalebar_frame():
f = FITSFigure(HDU)
f.add_scalebar(0.1)
f.scalebar.set_frame(True)
f.scalebar.set_frame(False)
f.close()
def test_scalebar_color():
f = FITSFigure(HDU)
f.add_scalebar(0.1)
f.scalebar.set_color('black')
f.scalebar.set_color('#003344')
f.scalebar.set_color((1.0, 0.4, 0.3))
f.close()
def test_scalebar_alpha():
f = FITSFigure(HDU)
f.add_scalebar(0.1)
f.scalebar.set_alpha(0.1)
f.scalebar.set_alpha(0.2)
f.scalebar.set_alpha(0.5)
f.close()
def test_scalebar_font():
f = FITSFigure(HDU)
f.add_scalebar(0.1)
f.scalebar.set_font(size='small', weight='bold', stretch='normal',
family='serif', style='normal', variant='normal')
f.close()
def test_regression_exception_type():
# In Matplotlib 1.5, the exception type changed for when a property doesn't
# exist, so we need to catch both AttributeError and TypeError.
f = FITSFigure(HDU)
with pytest.warns(UserWarning, match='Scalebar does not have attribute family'):
f.add_scalebar(0.1, family='serif')
f.close()
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/tests/test_subplot.py 0000664 0001750 0001750 00000001173 00000000000 016414 0 ustar 00tom tom import pytest
import numpy as np
from .. import FITSFigure
def test_subplot_grid():
f = FITSFigure(np.zeros((10, 10)), subplot=(2, 2, 1))
f.show_grayscale()
f.close()
def test_subplot_box():
f = FITSFigure(np.zeros((10, 10)), subplot=[0.1, 0.1, 0.8, 0.8])
f.show_grayscale()
f.close()
@pytest.mark.parametrize('subplot', [(1, 2, 3, 4), [1, 2, 3], '111', 1.2])
def test_subplot_invalid(subplot):
with pytest.raises(ValueError) as exc:
FITSFigure(np.zeros((10, 10)), subplot=subplot)
assert exc.value.args[0] == "subplot= should be either a tuple of three values, or a list of four values"
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/tests/test_tick_labels.py 0000664 0001750 0001750 00000003475 00000000000 017207 0 ustar 00tom tom import os
import pytest
import numpy as np
from .helpers import generate_hdu
from .. import FITSFigure
ROOT = os.path.dirname(os.path.abspath(__file__))
HEADER_DIR = os.path.join(ROOT, 'data/2d_fits')
REFERENCE = os.path.join(HEADER_DIR, '1904-66_TAN.hdr')
def test_tick_labels_show_hide():
data = np.zeros((16, 16))
f = FITSFigure(data)
f.tick_labels.hide()
f.tick_labels.show()
f.tick_labels.hide_x()
f.tick_labels.show_x()
f.tick_labels.hide_y()
f.tick_labels.show_y()
f.close()
def test_tick_labels_format_scalar():
data = np.zeros((16, 16))
f = FITSFigure(data)
f.tick_labels.set_xformat('%i')
f.tick_labels.set_yformat('%i')
f.close()
def test_tick_labels_position():
data = np.zeros((16, 16))
f = FITSFigure(data)
f.tick_labels.set_xposition('top')
f.tick_labels.set_xposition('bottom')
f.tick_labels.set_yposition('right')
f.tick_labels.set_yposition('left')
f.close()
def test_tick_labels_position_invalid():
data = np.zeros((16, 16))
f = FITSFigure(data)
with pytest.raises(ValueError):
f.tick_labels.set_xposition('right')
with pytest.raises(ValueError):
f.tick_labels.set_xposition('left')
with pytest.raises(ValueError):
f.tick_labels.set_yposition('top')
with pytest.raises(ValueError):
f.tick_labels.set_yposition('bottom')
f.close()
def test_tick_labels_font():
data = np.zeros((16, 16))
f = FITSFigure(data)
f.tick_labels.set_font(size='small', weight='bold', stretch='normal',
family='serif', style='normal', variant='normal')
f.close()
def test_single_d_format():
hdu = generate_hdu(REFERENCE)
f = FITSFigure(hdu)
f.show_grayscale(vmin=0, vmax=1)
f.tick_labels.set_yformat('d.d')
f.save('test_label_format.png')
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/tests/test_ticks.py 0000664 0001750 0001750 00000002345 00000000000 016043 0 ustar 00tom tom import numpy as np
from .. import FITSFigure
def test_ticks_show_hide():
data = np.zeros((16, 16))
f = FITSFigure(data)
f.ticks.hide()
f.ticks.show()
f.ticks.hide_x()
f.ticks.show_x()
f.ticks.hide_y()
f.ticks.show_y()
f.close()
def test_ticks_spacing():
data = np.zeros((16, 16))
f = FITSFigure(data)
f.ticks.set_xspacing(0.5)
f.ticks.set_xspacing(1.)
f.ticks.set_yspacing(0.5)
f.ticks.set_yspacing(1.)
f.close()
def test_ticks_length():
data = np.zeros((16, 16))
f = FITSFigure(data)
f.ticks.set_length(0)
f.ticks.set_length(1)
f.ticks.set_length(10)
f.close()
def test_ticks_color():
data = np.zeros((16, 16))
f = FITSFigure(data)
f.ticks.set_color('black')
f.ticks.set_color('#003344')
f.ticks.set_color((1.0, 0.4, 0.3))
f.close()
def test_ticks_linewidth():
data = np.zeros((16, 16))
f = FITSFigure(data)
f.ticks.set_linewidth(1)
f.ticks.set_linewidth(3)
f.ticks.set_linewidth(10)
f.close()
def test_ticks_minor_frequency():
data = np.zeros((16, 16))
f = FITSFigure(data)
f.ticks.set_minor_frequency(1)
f.ticks.set_minor_frequency(5)
f.ticks.set_minor_frequency(10)
f.close()
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/tests/test_vectors.py 0000664 0001750 0001750 00000002105 00000000000 016405 0 ustar 00tom tom import pytest
import numpy as np
from ..core import FITSFigure
from .test_images import BaseImageTests
from . import baseline_dir
x = np.linspace(-1., 1., 10)
y = np.linspace(-1., 1., 10)
X, Y = np.meshgrid(x, y)
np.random.seed(12345)
IMAGE = np.random.random((10, 10))
PDATA = np.arange(100).reshape((10, 10)) / 50. + 0.5
ADATA = np.degrees(np.arctan2(Y, X))
class TestVectors(BaseImageTests):
@pytest.mark.remote_data
@pytest.mark.mpl_image_compare(style={}, savefig_kwargs={'adjust_bbox': False}, baseline_dir=baseline_dir, tolerance=1.5)
def test_default(self):
f = FITSFigure(IMAGE, figsize=(4, 4))
f.show_grayscale()
f.show_vectors(PDATA, ADATA, color='orange')
return f._figure
@pytest.mark.remote_data
@pytest.mark.mpl_image_compare(style={}, savefig_kwargs={'adjust_bbox': False}, baseline_dir=baseline_dir, tolerance=1.5)
def test_step_scale(self):
f = FITSFigure(IMAGE, figsize=(4, 4))
f.show_grayscale()
f.show_vectors(PDATA, ADATA, step=2, scale=0.8, color='orange')
return f._figure
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/aplpy/tick_labels.py 0000644 0001750 0001750 00000013511 00000000000 014774 0 ustar 00tom tom # -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, division, unicode_literals
from .decorators import auto_refresh, fixdocstring
__all__ = ['TickLabels']
class TickLabels(object):
def __init__(self, parent):
self._figure = parent._figure
self._ax = parent.ax
self._wcs = parent.ax.wcs
self.x = parent.x
self.y = parent.y
self.set_style('plain')
@auto_refresh
def set_xformat(self, xformat):
"""
Set the format of the x-axis tick labels.
If the x-axis type is ``longitude`` or ``latitude``, then the options
are:
* ``d.ddddd`` - decimal degrees, where the number of decimal places can be varied
* ``hh`` or ``dd`` - hours (or degrees)
* ``hh:mm`` or ``dd:mm`` - hours and minutes (or degrees and arcminutes)
* ``hh:mm:ss`` or ``dd:mm:ss`` - hours, minutes, and seconds (or degrees, arcminutes, and arcseconds)
* ``hh:mm:ss.ss`` or ``dd:mm:ss.ss`` - hours, minutes, and seconds (or degrees, arcminutes, and arcseconds), where the number of decimal places can be varied.
If the x-axis type is ``scalar``, then the format should be a valid
python string format beginning with a ``%``.
If one of these arguments is not specified, the format for that axis
is left unchanged.
"""
if 'dd.' in xformat:
xformat = xformat.replace('ddd.', 'd.').replace('dd.', 'd.')
self._ax.coords[self.x].set_major_formatter(xformat)
@auto_refresh
def set_yformat(self, yformat):
"""
Set the format of the y-axis tick labels.
If the y-axis type is ``longitude`` or ``latitude``, then the options
are:
* ``d.ddddd`` - decimal degrees, where the number of decimal places can be varied
* ``hh`` or ``dd`` - hours (or degrees)
* ``hh:mm`` or ``dd:mm`` - hours and minutes (or degrees and arcminutes)
* ``hh:mm:ss`` or ``dd:mm:ss`` - hours, minutes, and seconds (or degrees, arcminutes, and arcseconds)
* ``hh:mm:ss.ss`` or ``dd:mm:ss.ss`` - hours, minutes, and seconds (or degrees, arcminutes, and arcseconds), where the number of decimal places can be varied.
If the y-axis type is ``scalar``, then the format should be a valid
python string format beginning with a ``%``.
If one of these arguments is not specified, the format for that axis
is left unchanged.
"""
if 'dd.' in yformat:
yformat = yformat.replace('ddd.', 'd.').replace('dd.', 'd.')
self._ax.coords[self.y].set_major_formatter(yformat)
@auto_refresh
def set_style(self, style):
"""
Set the format of the x-axis tick labels.
This can be 'colons' or 'plain':
* 'colons' uses colons as separators, for example 31:41:59.26 +27:18:28.1
* 'plain' uses letters and symbols as separators, for example 31h41m59.26s +27º18'28.1"
"""
if style not in ['colons', 'plain']:
raise Exception("Label style should be one of colons/plain")
self.style = style
for coord in [self.x, self.y]:
coord_type = self._ax.coords[coord].coord_type
if coord_type in ['longitude', 'latitude']:
if style == 'colons':
sep = (':', ':', '')
else:
sep = None
self._ax.coords[coord].set_separator(sep)
@auto_refresh
@fixdocstring
def set_font(self, **kwargs):
"""
Set the font of the tick labels.
Parameters
----------
common: family, style, variant, stretch, weight, size, fontproperties
Notes
-----
Default values are set by matplotlib or previously set values if
set_font has already been called. Global default values can be set by
editing the matplotlibrc file.
"""
self._ax.coords[self.x].set_ticklabel(**kwargs)
self._ax.coords[self.y].set_ticklabel(**kwargs)
@auto_refresh
def show(self):
"""
Show the x- and y-axis tick labels.
"""
self.show_x()
self.show_y()
@auto_refresh
def hide(self):
"""
Hide the x- and y-axis tick labels.
"""
self.hide_x()
self.hide_y()
@auto_refresh
def show_x(self):
"""
Show the x-axis tick labels.
"""
self._ax.coords[self.x].set_ticklabel_visible(True)
@auto_refresh
def hide_x(self):
"""
Hide the x-axis tick labels.
"""
self._ax.coords[self.x].set_ticklabel_visible(False)
@auto_refresh
def show_y(self):
"""
Show the y-axis tick labels.
"""
self._ax.coords[self.y].set_ticklabel_visible(True)
@auto_refresh
def hide_y(self):
"""
Hide the y-axis tick labels.
"""
self._ax.coords[self.y].set_ticklabel_visible(False)
@auto_refresh
def set_xposition(self, position):
"""
Set the position of the x-axis tick labels ('top' or 'bottom')
"""
if position == 'bottom':
self._ax.coords[self.x].set_ticklabel_position('b')
elif position == 'top':
self._ax.coords[self.x].set_ticklabel_position('t')
else:
raise ValueError("position should be one of 'top' or 'bottom'")
@auto_refresh
def set_yposition(self, position):
"""
Set the position of the y-axis tick labels ('left' or 'right')
"""
if position == 'left':
self._ax.coords[self.y].set_ticklabel_position('l')
elif position == 'right':
self._ax.coords[self.y].set_ticklabel_position('r')
else:
raise ValueError("position should be one of 'left' or 'right'")
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/aplpy/ticks.py 0000664 0001750 0001750 00000007367 00000000000 013653 0 ustar 00tom tom from .decorators import auto_refresh
class Ticks(object):
@auto_refresh
def __init__(self, parent):
self._figure = parent._figure
self._ax = parent.ax
self.x = parent.x
self.y = parent.y
self._wcs = self._ax.wcs
def set_tick_direction(self, direction):
"""
Set the direction of the ticks to be facing out of the axes (``out``)
or into the axes (``in``).
"""
if direction in ('in', 'out'):
self._ax.coords[self.x].ticks.set_tick_out(direction == 'out')
self._ax.coords[self.y].ticks.set_tick_out(direction == 'out')
else:
raise ValueError("direction should be 'in' or 'out'")
@auto_refresh
def set_xspacing(self, spacing):
"""
Set the x-axis tick spacing, in degrees. To set the tick spacing to be
automatically determined, set this to 'auto'.
"""
self._set_spacing(self.x, spacing)
@auto_refresh
def set_yspacing(self, spacing):
"""
Set the y-axis tick spacing, in degrees. To set the tick spacing to be
automatically determined, set this to 'auto'.
"""
self._set_spacing(self.y, spacing)
@auto_refresh
def _set_spacing(self, coord, spacing):
if spacing == 'auto':
self._ax.coords[coord].set_ticks(spacing=None)
else:
coord_unit = self._wcs.wcs.cunit[coord]
self._ax.coords[coord].set_ticks(spacing=spacing * coord_unit)
@auto_refresh
def set_color(self, color):
"""
Set the color of the ticks
"""
self._ax.coords[self.x].set_ticks(color=color)
self._ax.coords[self.y].set_ticks(color=color)
@auto_refresh
def set_length(self, length, minor_factor=0.5):
"""
Set the length of the ticks (in points)
"""
# TODO: Can't set minor ticksize. Should we just remove that?
# Not mentioned in the APLpy in docs either
self._ax.coords[self.x].set_ticks(size=length)
self._ax.coords[self.y].set_ticks(size=length)
@auto_refresh
def set_linewidth(self, linewidth):
"""
Set the linewidth of the ticks (in points)
"""
self._ax.coords[self.x].set_ticks(width=linewidth)
self._ax.coords[self.y].set_ticks(width=linewidth)
@auto_refresh
def set_minor_frequency(self, xfrequency, yfrequency=None):
'''
Set the number of subticks per major tick.
Set to one to hide minor ticks. If not yfrequency given, frequency is
the same in both axis. Otherwise, xfrequency represents the frequency in
the xaxis and yfrequency the one in the yaxis.
'''
if yfrequency is None:
yfrequency = xfrequency
self._ax.coords[self.x].set_minor_frequency(xfrequency)
self._ax.coords[self.y].set_minor_frequency(yfrequency)
@auto_refresh
def show(self):
"""
Show the x- and y-axis ticks
"""
self.show_x()
self.show_y()
@auto_refresh
def hide(self):
"""
Hide the x- and y-axis ticks
"""
self.hide_x()
self.hide_y()
@auto_refresh
def show_x(self):
"""
Show the x-axis ticks
"""
self._ax.coords[self.x].set_ticks_visible(True)
@auto_refresh
def hide_x(self):
"""
Hide the x-axis ticks
"""
self._ax.coords[self.x].set_ticks_visible(False)
@auto_refresh
def show_y(self):
"""
Show the y-axis ticks
"""
self._ax.coords[self.y].set_ticks_visible(True)
@auto_refresh
def hide_y(self):
"""
Hide the y-axis ticks
"""
self._ax.coords[self.y].set_ticks_visible(False)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646929514.0
aplpy-2.1.0/aplpy/version.py 0000664 0001750 0001750 00000000521 00000000000 014204 0 ustar 00tom tom # Note that we need to fall back to the hard-coded version if either
# setuptools_scm can't be imported or setuptools_scm can't determine the
# version, so we catch the generic 'Exception'.
try:
from setuptools_scm import get_version
version = get_version(root='..', relative_to=__file__)
except Exception:
version = '2.1.0'
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1646929515.238823
aplpy-2.1.0/aplpy.egg-info/ 0000775 0001750 0001750 00000000000 00000000000 013641 5 ustar 00tom tom ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646929514.0
aplpy-2.1.0/aplpy.egg-info/PKG-INFO 0000664 0001750 0001750 00000003110 00000000000 014731 0 ustar 00tom tom Metadata-Version: 2.1
Name: aplpy
Version: 2.1.0
Summary: The Astronomical Plotting Library in Python
Home-page: http://aplpy.github.io
Author: Thomas Robitaille and Eli Bressert
Author-email: thomas.robitaille@gmail.com
License: MIT
Platform: UNKNOWN
Requires-Python: >=3.6
Description-Content-Type: text/x-rst
Provides-Extra: test
Provides-Extra: docs
License-File: LICENSE.md
|Azure| |CircleCI| |codecov| |Documentation Status|
About
-----
APLpy (the Astronomical Plotting Library in Python) is a Python module
aimed at producing publication-quality plots of astronomical imaging
data in FITS format.
PyPI: https://pypi.python.org/pypi/APLpy
Website: http://aplpy.github.io
Documentation: http://aplpy.readthedocs.io
APLpy is released under an MIT open-source license.
Installing
----------
You can install APLpy and all its dependencies with::
pip install aplpy
.. |Azure| image:: https://dev.azure.com/thomasrobitaille/aplpy/_apis/build/status/aplpy.aplpy?repoName=aplpy%2Faplpy&branchName=refs%2Fpull%2F441%2Fmerge
:target: https://dev.azure.com/thomasrobitaille/aplpy/_build/latest?definitionId=16&repoName=aplpy%2Faplpy&branchName=refs%2Fpull%2F441%2Fmerge
.. |CircleCI| image:: https://circleci.com/gh/aplpy/aplpy/tree/main.svg?style=svg
:target: https://circleci.com/gh/aplpy/aplpy/tree/main
.. |codecov| image:: https://codecov.io/gh/aplpy/aplpy/branch/main/graph/badge.svg
:target: https://codecov.io/gh/aplpy/aplpy
.. |Documentation Status| image:: https://img.shields.io/badge/docs-latest-brightgreen.svg?style=flat
:target: https://aplpy.readthedocs.io/en/latest/
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646929515.0
aplpy-2.1.0/aplpy.egg-info/SOURCES.txt 0000664 0001750 0001750 00000006242 00000000000 015531 0 ustar 00tom tom .gitignore
.readthedocs.yml
CHANGES.rst
CITATION
LICENSE.md
MANIFEST.in
README.rst
azure-pipelines.yml
conftest.py
pyproject.toml
setup.cfg
setup.py
tox.ini
.circleci/config.yml
aplpy/__init__.py
aplpy/_astropy_init.py
aplpy/axis_labels.py
aplpy/colorbar.py
aplpy/compat.py
aplpy/conftest.py
aplpy/convolve_util.py
aplpy/core.py
aplpy/decorators.py
aplpy/frame.py
aplpy/grid.py
aplpy/header.py
aplpy/layers.py
aplpy/overlays.py
aplpy/regions.py
aplpy/rgb.py
aplpy/slicer.py
aplpy/tick_labels.py
aplpy/ticks.py
aplpy/version.py
aplpy.egg-info/PKG-INFO
aplpy.egg-info/SOURCES.txt
aplpy.egg-info/dependency_links.txt
aplpy.egg-info/not-zip-safe
aplpy.egg-info/requires.txt
aplpy.egg-info/top_level.txt
aplpy/tests/__init__.py
aplpy/tests/coveragerc
aplpy/tests/helpers.py
aplpy/tests/setup_package.py
aplpy/tests/test_axis_labels.py
aplpy/tests/test_beam.py
aplpy/tests/test_colorbar.py
aplpy/tests/test_contour.py
aplpy/tests/test_convolve.py
aplpy/tests/test_downsample.py
aplpy/tests/test_frame.py
aplpy/tests/test_grid.py
aplpy/tests/test_images.py
aplpy/tests/test_init_cube.py
aplpy/tests/test_init_image.py
aplpy/tests/test_layers.py
aplpy/tests/test_misc.py
aplpy/tests/test_pixworldmarkers.py
aplpy/tests/test_rgb.py
aplpy/tests/test_save.py
aplpy/tests/test_scalebar.py
aplpy/tests/test_subplot.py
aplpy/tests/test_tick_labels.py
aplpy/tests/test_ticks.py
aplpy/tests/test_vectors.py
aplpy/tests/data/__init__.py
aplpy/tests/data/shapes.reg
aplpy/tests/data/2d_fits/1904-66_AIR.hdr
aplpy/tests/data/2d_fits/1904-66_AIT.hdr
aplpy/tests/data/2d_fits/1904-66_ARC.hdr
aplpy/tests/data/2d_fits/1904-66_AZP.hdr
aplpy/tests/data/2d_fits/1904-66_BON.hdr
aplpy/tests/data/2d_fits/1904-66_CAR.hdr
aplpy/tests/data/2d_fits/1904-66_CEA.hdr
aplpy/tests/data/2d_fits/1904-66_COD.hdr
aplpy/tests/data/2d_fits/1904-66_COE.hdr
aplpy/tests/data/2d_fits/1904-66_COO.hdr
aplpy/tests/data/2d_fits/1904-66_COP.hdr
aplpy/tests/data/2d_fits/1904-66_CSC.hdr
aplpy/tests/data/2d_fits/1904-66_CYP.hdr
aplpy/tests/data/2d_fits/1904-66_HPX.hdr
aplpy/tests/data/2d_fits/1904-66_MER.hdr
aplpy/tests/data/2d_fits/1904-66_MOL.hdr
aplpy/tests/data/2d_fits/1904-66_NCP.hdr
aplpy/tests/data/2d_fits/1904-66_PAR.hdr
aplpy/tests/data/2d_fits/1904-66_PCO.hdr
aplpy/tests/data/2d_fits/1904-66_QSC.hdr
aplpy/tests/data/2d_fits/1904-66_SFL.hdr
aplpy/tests/data/2d_fits/1904-66_SIN.hdr
aplpy/tests/data/2d_fits/1904-66_STG.hdr
aplpy/tests/data/2d_fits/1904-66_SZP.hdr
aplpy/tests/data/2d_fits/1904-66_TAN.hdr
aplpy/tests/data/2d_fits/1904-66_TSC.hdr
aplpy/tests/data/2d_fits/1904-66_ZEA.hdr
aplpy/tests/data/2d_fits/1904-66_ZPN.hdr
aplpy/tests/data/2d_fits/2MASS_k.hdr
aplpy/tests/data/2d_fits/2MASS_k_rot.hdr
aplpy/tests/data/3d_fits/cube.hdr
docs/Makefile
docs/aplpy_logo.png
docs/conf.py
docs/index.rst
docs/index.txt
docs/make.bat
docs/rgb.rst
docs/_templates/autosummary/base.rst
docs/_templates/autosummary/class.rst
docs/_templates/autosummary/module.rst
docs/fitsfigure/arbitrary_coordinate_systems.rst
docs/fitsfigure/buttons.png
docs/fitsfigure/howto_avm.rst
docs/fitsfigure/howto_noninteractive.rst
docs/fitsfigure/howto_subplot.rst
docs/fitsfigure/myfirstplot.png
docs/fitsfigure/quick_reference.rst
docs/fitsfigure/quickstart.rst
docs/fitsfigure/slicing.rst ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646929514.0
aplpy-2.1.0/aplpy.egg-info/dependency_links.txt 0000664 0001750 0001750 00000000001 00000000000 017707 0 ustar 00tom tom
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646929514.0
aplpy-2.1.0/aplpy.egg-info/not-zip-safe 0000664 0001750 0001750 00000000001 00000000000 016067 0 ustar 00tom tom
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646929515.0
aplpy-2.1.0/aplpy.egg-info/requires.txt 0000664 0001750 0001750 00000000262 00000000000 016241 0 ustar 00tom tom numpy
astropy>=3.1
matplotlib>=2.0
reproject>=0.4
pyregion>=2.0
pillow>=4.3
pyavm>=0.9.4
scikit-image>=0.14
shapely>=1.7
[docs]
sphinx-astropy
[test]
pytest-astropy
pytest-mpl
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646929515.0
aplpy-2.1.0/aplpy.egg-info/top_level.txt 0000664 0001750 0001750 00000000006 00000000000 016367 0 ustar 00tom tom aplpy
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1646928934.0
aplpy-2.1.0/azure-pipelines.yml 0000664 0001750 0001750 00000001413 00000000000 014660 0 ustar 00tom tom resources:
repositories:
- repository: OpenAstronomy
type: github
endpoint: aplpy
name: OpenAstronomy/azure-pipelines-templates
ref: master
jobs:
- template: run-tox-env.yml@OpenAstronomy
parameters:
coverage: codecov
envs:
- linux: codestyle
- macos: py37-test-cov
- macos: py38-test-cov
- macos: py39-test-cov
- macos: py310-test-cov
- linux: py36-test-oldestdeps-cov
- linux: py37-test-cov
- linux: py38-test-cov
- linux: py39-test-cov
- linux: py310-test-devdeps-cov
- windows: py36-test-oldestdeps-cov
- windows: py37-test-cov
- windows: py38-test-cov
- windows: py39-test-cov
- windows: py310-test-cov
- macos: build_docs
- linux: build_docs
- macos: build_docs
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/conftest.py 0000644 0001750 0001750 00000000050 00000000000 013212 0 ustar 00tom tom import matplotlib
matplotlib.use('Agg')
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1646929515.246823
aplpy-2.1.0/docs/ 0000775 0001750 0001750 00000000000 00000000000 011752 5 ustar 00tom tom ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/docs/Makefile 0000644 0001750 0001750 00000011164 00000000000 013413 0 ustar 00tom tom # Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
#This is needed with git because git doesn't create a dir if it's empty
$(shell [ -d "_static" ] || mkdir -p _static)
help:
@echo "Please use \`make ' where is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)
-rm -rf api
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Astropy.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Astropy.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/Astropy"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Astropy"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
make -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1646929515.234823
aplpy-2.1.0/docs/_templates/ 0000775 0001750 0001750 00000000000 00000000000 014107 5 ustar 00tom tom ././@PaxHeader 0000000 0000000 0000000 00000000033 00000000000 010211 x ustar 00 27 mtime=1646929515.246823
aplpy-2.1.0/docs/_templates/autosummary/ 0000775 0001750 0001750 00000000000 00000000000 016475 5 ustar 00tom tom ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/docs/_templates/autosummary/base.rst 0000644 0001750 0001750 00000000372 00000000000 020141 0 ustar 00tom tom {% extends "autosummary_core/base.rst" %}
{# The template this is inherited from is in astropy/sphinx/ext/templates/autosummary_core. If you want to modify this template, it is strongly recommended that you still inherit from the astropy template. #} ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/docs/_templates/autosummary/class.rst 0000644 0001750 0001750 00000000373 00000000000 020335 0 ustar 00tom tom {% extends "autosummary_core/class.rst" %}
{# The template this is inherited from is in astropy/sphinx/ext/templates/autosummary_core. If you want to modify this template, it is strongly recommended that you still inherit from the astropy template. #} ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/docs/_templates/autosummary/module.rst 0000644 0001750 0001750 00000000374 00000000000 020516 0 ustar 00tom tom {% extends "autosummary_core/module.rst" %}
{# The template this is inherited from is in astropy/sphinx/ext/templates/autosummary_core. If you want to modify this template, it is strongly recommended that you still inherit from the astropy template. #} ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1581700678.0
aplpy-2.1.0/docs/aplpy_logo.png 0000644 0001750 0001750 00000012562 00000000000 014631 0 ustar 00tom tom ‰PNG
IHDR Í i ™1§‘
HiCCPPhotoshop ICC profile HÇ–wTTׇϽwz¡Í0Òzïm轃ô*
ãÌ C`˜T,(¢EDЉ
0ŠÄŠ(‚‚*j@‚€ƒQDE%3ºVâËËËËÊï{¿µÏÞçî³÷>k] H>\^,@:OÀõv£GÇÄÒ±} x€f 0YÙ™a^á@$_Owz¶È ü‡^Hü¾mäL§+;ß7ñÛÖ”P矿¶à!ø{I³2ù `[°9Ù,Š8-W)¶ÏŠ˜º2UÌ0JÌ|Q‚"–sÒg6üäó™]ÄÌNç±E,Î9“Îs·ˆ·æ9"FD\”ÃåäŠøŽˆ5Ó„é\¿Ç¦s˜Ù Hb»€ÃJ±©ˆIüðPw; €#%}Á+¿`'O >”{Fæ*>7)Y@×eéÑÍlmtNnG 0
f²R™|6Ý=#=“É[Àç3’Œ¸¶tQ‘ÍlÌ;(Ôß.þC‰{û™^†|êDëýÃöW~u 0æDµÙñ‡me5 í›»ÿ‡Mó ’¢¾µÝüâ<4ñ¼$™v&&¹¹¹Æ\ËX\Ðßõþ¾øž±x»ßËC÷à$2…iº¸n¬Œ´!ŸžÉdqèFâø×y†r9|O)š2./IÔn›+àfðè\Þÿjâ¿û“>ϵH”ú€o¤nRòs/@Qˆ ‰Û/îúï}ÀGñÍ‹Rü<÷Ÿý÷®p™ø‘ÍMúçNg ù9Ÿ×Ä× H**@è#`l€=pžÀpV Hé€rA>( E ì »A
8 êAh'A;8.‚+à¸Áà˜¯ÁAXˆQ yHÒ‚ sˆ9AžP
Å@ Pă„P>´ *Ê¡èÔ }†.B× ~è4MC¿Bï`&ÁTXÖ†M`ì
ûÃáðr8 ΂WÃ…ðv¸
®ƒÃmðEø<ÂÏà9 D„†¨!FqG‚X$á#ëb¤©Cš‘N¤¹Œ"3È[EAÑQF({”*ÅBe¡Ö¡JQ5¨c¨6T7ê6j5‹úˆ&£•Ðh;´/:„ÎE¡+ÑGÐèËèAôú5ƒ¡at06L&³SŠÙ‡iÁ\ÀôcÆ1sX,Vk€uÄa™X¶[=Ž=ÀN`ßàˆ8Uœ9΋ãá6â*q¸s¸Ü$n/…×ÂÛáƒðlü*|¾ß‰¿‰ŸÀ/¤ :GB8!…P@¨"4.^‰Du¢-1„È%n VO¯ÇˆoI2$}’;)Ž$$m'%] Ý#½$“ÉÚdr,Y@ÞNn _"?"¿‘ HKøJ°%ÖKÔJ´IH<—ÄKjIºJ®\-Y)yJò¦äŒ^J[Ê]Š)µNªVê´Ô°Ôœ4EÚL:H:]ºTºQúšô”VF[ÆS†-S(sXæ’Ì8¡hPÜ),Ê&J=å2e‚Š¡êP}©)Ôê7Ô>ꬬŒ¬¥l¤lžlìYÙQBÓ¦ùÒÒhe´“´!Ú»%ÊK\—p–l[Ò¼d`ɼœ¢œ‹G®X®EnPî<]ÞS>U~§|»üC”‚¾BˆB®Â~…Ë
3ŠTE{E–b±âIÅûJ°’¾R¨Ò¥ÃJ½JsÊ*ÊÞÊ™ÊÕÊ—”gTh*.*)**çT¦U)ªNª\Õ
ÕóªOé²tWz½ŠÞMŸUSRóQªRëS[P×QPߨޢþPƒ ÁÐHÔ¨ÐèÒ˜ÕTÕÔÌ×lÒ¼¯…×bh%kíÑêÑš×ÖÑŽÒޢݮ=¥#§ã«³Z§Iç.Y×Y7K·N÷ŽF¡—ª·Oï–>¬o¥Ÿ¬_«Ó 6°6àì3è7DÚòë‡HF®F9FMFcÆ4ã ãÆíÆÏM4MbMvšô˜|4µ2M371“1ó3ÛhÖiö«¹¾9˼ÖüŽÙÂËb½E‡ÅKKŽå~Ë»V«@«-V]V¬m¬ùÖÍÖÓ6š6 6{m†TF0£”qÕmëf»ÞöŒí[;k;ÝI»_ììSíí§t8õãŽêŽLÇCŽ£Nt§§ƒN£ÎjÎLç:çÇ..l—#.“®z®)®Ç]Ÿ»™ºñÝZÝæÝíÜ׺_ð@<¼=Š=úXªµ”·´=ùí
z¬œü}&$8¤6äI¨Yh~hO%,>¬1ìu¸[xYøH„n„0¢+R22.²!r>Ê#ª³‹¯—˜–T–¼/e•^ÿÊ쫪¯·'nï+³.Û¿³ƒ·ch§óÎcåÒå«ËÇwîj« WW¼Ú¿ûZ¥eå=„=Â=£UUÕšÕ;ªß×$×ÖºÕ¶ìUÚ»mïü>ö¾ý.û›((9ðî ÷àÝCÞ‡Úê´ë*cç~RYßó5ãë†#
GJŽ|8Ê;:z,ôXwƒMCC£RcYÜ$lš>wüÖ7ßt45j¡µ”œ '„'ž~›ðíÐIÿ“]§§š¿Óúno+¥µ¸
j[Õ6ÛžÜ>ÚÓÑÚïtW§}gë÷Æß=£v¦ö¬ìÙ²s„s…çϯ>?w!óÂÌŤ‹ã]ñ]#—¢/Ýééî»ìùê¯+—z\{Î_u¼zæšÝµÓ××ÛoXßhëµêmýÁê‡Ö>ë¾¶›67;nÙÞêìwè?7à