├── .github └── workflows │ └── main.yml ├── .gitignore ├── CHANGES ├── LICENSE ├── MANIFEST.in ├── README.rst ├── pyavm ├── __init__.py ├── avm.py ├── cv.py ├── datatypes.py ├── embed.py ├── exceptions.py ├── extract.py ├── jpeg.py ├── png.py ├── specs.py ├── tests │ ├── __init__.py │ ├── data │ │ ├── 3c321.avm.xml │ │ ├── a3627_wcs_metadata.xml │ │ ├── eso_eso1723a_320.jpg │ │ ├── eso_eso1723a_320.png │ │ ├── example_header.hdr │ │ ├── heic0409a.xml │ │ ├── heic0515a.xml │ │ ├── kes75.xml │ │ ├── n132d.xml │ │ ├── opo0613a.xml │ │ ├── sig05-002a.xml │ │ ├── sig05-003a.xml │ │ ├── sig05-004a.xml │ │ ├── sig05-011.xml │ │ ├── sig05-013.xml │ │ ├── sig05-014.xml │ │ ├── sig05-015.xml │ │ ├── sig05-016.xml │ │ ├── sig05-017.xml │ │ ├── sig05-019b.xml │ │ ├── sig05-019d.xml │ │ ├── sig05-021-alpha.xml │ │ ├── sig05-021.xml │ │ ├── sig05-028a.xml │ │ ├── sig05-028b.xml │ │ ├── sig05-028c.xml │ │ ├── sig06-003.xml │ │ ├── sig06-010.xml │ │ ├── sig06-012b.xml │ │ ├── sig06-012c.xml │ │ ├── sig06-013.xml │ │ ├── sig06-021a.xml │ │ ├── sig06-027.xml │ │ ├── sig07-006.xml │ │ ├── sig07-009.xml │ │ ├── sig07-016.xml │ │ ├── snr0509_xray.xml │ │ ├── ssc2003-06b1.xml │ │ ├── ssc2003-06b2.xml │ │ ├── ssc2003-06c1.xml │ │ ├── ssc2003-06d1.xml │ │ ├── ssc2003-06d3.xml │ │ ├── ssc2003-06d4.xml │ │ ├── ssc2003-06d5.xml │ │ ├── ssc2004-04a1.xml │ │ ├── ssc2004-04a3.xml │ │ ├── ssc2004-04a4.xml │ │ ├── ssc2004-06a1-alpha.xml │ │ ├── ssc2004-06a1.xml │ │ ├── ssc2004-06b1-alpha.xml │ │ ├── ssc2004-06b1.xml │ │ ├── ssc2004-12a1.xml │ │ ├── ssc2004-12a2.xml │ │ ├── ssc2004-12a3.xml │ │ ├── ssc2004-19a2.xml │ │ ├── ssc2004-20a2.xml │ │ ├── ssc2005-02a2.xml │ │ ├── ssc2005-02a4.xml │ │ ├── ssc2005-11a1.xml │ │ ├── ssc2005-11a3.xml │ │ ├── ssc2005-23a1.xml │ │ ├── ssc2005-24a1.xml │ │ ├── ssc2006-01a1.xml │ │ ├── ssc2006-09a1.xml │ │ ├── ssc2006-11a1.xml │ │ ├── ssc2006-16a1.xml │ │ ├── ssc2006-16b1.xml │ │ ├── ssc2006-21a1.xml │ │ ├── ssc2007-03a1.xml │ │ ├── ssc2007-07a1.xml │ │ ├── ssc2007-07b1.xml │ │ ├── ssc2007-08a1.xml │ │ ├── ssc2007-08b1.xml │ │ ├── ssc2007-10a1.xml │ │ ├── ssc2007-13b1.xml │ │ └── wd2.xml │ ├── test_avm.py │ ├── test_header.py │ ├── test_io.py │ ├── test_main.py │ ├── test_specs.py │ └── test_wcs_utils.py └── wcs_utils.py ├── pyproject.toml └── tox.ini /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | 9 | tests: 10 | uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v1 11 | with: 12 | coverage: codecov 13 | envs: | 14 | # Minimal dependencies 15 | - linux: py38-test 16 | - windows: py39-test 17 | - macos: py310-test 18 | - linux: py311-test 19 | - windows: py312-test 20 | 21 | # All dependencies 22 | - linux: py38-test-all 23 | - windows: py39-test-all 24 | - macos: py310-test-all 25 | - linux: py311-test-all 26 | - windows: py312-test-all 27 | 28 | publish: 29 | uses: OpenAstronomy/github-actions-workflows/.github/workflows/publish_pure_python.yml@v1 30 | with: 31 | test_extras: test 32 | test_command: pytest --pyargs pyavm 33 | secrets: 34 | pypi_token: ${{ secrets.PYPI_TOKEN }} 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .DS_Store 3 | *.pyc 4 | .tox 5 | *.egg-info 6 | dist 7 | pyavm/_version.py 8 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | CHANGES 2 | ------- 3 | 4 | Version 0.9.6 5 | 6 | Fix header initialization when ``use_full_header=True`` 7 | 8 | Updated package infrastructure 9 | 10 | Remove unecessary print statement and reduce warnings 11 | 12 | Version 0.9.5 13 | 14 | Add `target_shape` argument to `AVM.to_wcs()` 15 | 16 | Fix compatibility with Python 3.8 17 | 18 | Version 0.9.4 19 | 20 | Properly extract scale and rotation for arbitrary WCS objects. 21 | 22 | Version 0.9.3 23 | 24 | Fix compatibility with Python 3. 25 | 26 | Add a keyword argument ``xmp_packet_index`` in AVM.from_image, which can 27 | be used to indicate which XMP packet to load if there are multiple ones. 28 | 29 | Version 0.9.2 30 | 31 | Fix compatibility with latest Astropy version. AVM.from_wcs now requires an 32 | image shape to be passed to set Spatial.ReferenceDimension, for recent 33 | versions of Astropy. [#28] 34 | 35 | Version 0.9.1 36 | 37 | Allow AVM meta-data to be read from any file format by simply searching 38 | for the XMP packet by scanning the file contents. 39 | 40 | Version 0.9.0 41 | 42 | Complete re-factoring. Embedding for PNG and JPEG is now 43 | standard-compliant. Initialization from headers, WCS objects, and images 44 | is now done via class methods (see README.md). 45 | 46 | Version 0.1.4 47 | 48 | Now write XMP packet using ElementTree to ensure XML compliance 49 | 50 | Version 0.1.3 51 | 52 | Rewrote XML parsing using ElementTree. Compact XMP tags are now read 53 | properly. All example images parse without errors. 54 | 55 | Version 0.1.2 56 | 57 | Added support for embedding AVM meta-data in TIF files 58 | 59 | Version 0.1.1 60 | 61 | Added support for embedding AVM meta-data in JPG and PNG files, and 62 | added methods to create AVM container from WCS or FITS header 63 | information. 64 | 65 | Version 0.1.0 66 | 67 | Initial Release 68 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ============================================================================= 2 | 3 | PyAVM - Simple pure-python AVM meta-data parsing 4 | Copyright (c) 2011-2013 Thomas P. Robitaille 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a 7 | copy of this software and associated documentation files (the "Software"), 8 | to deal in the Software without restriction, including without limitation 9 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | and/or sell copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | DEALINGS IN THE SOFTWARE. 23 | 24 | ============================================================================= 25 | PyAVM includes code adapted from the python-avm-library released under the 26 | following license: 27 | 28 | Copyright (c) 2009, European Space Agency & European Southern Observatory 29 | All rights reserved. 30 | 31 | Redistribution and use in source and binary forms, with or without 32 | modification, are permitted provided that the following conditions are met: 33 | 34 | * Redistributions of source code must retain the above copyright 35 | notice, this list of conditions and the following disclaimer. 36 | 37 | * Redistributions in binary form must reproduce the above copyright 38 | notice, this list of conditions and the following disclaimer in the 39 | documentation and/or other materials provided with the distribution. 40 | 41 | * Neither the name of the European Space Agency, European Southern 42 | Observatory nor the names of its contributors may be used to endorse or 43 | promote products derived from this software without specific prior 44 | written permission. 45 | 46 | THIS SOFTWARE IS PROVIDED BY ESA/ESO ``AS IS'' AND ANY EXPRESS OR IMPLIED 47 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 48 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 49 | EVENT SHALL ESA/ESO BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 50 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 51 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 52 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 53 | IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 54 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 55 | POSSIBILITY OF SUCH DAMAGE 56 | ============================================================================= 57 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | include CHANGES 3 | include LICENSE 4 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | |Build Status| |Coverage Status| 2 | 3 | About 4 | ----- 5 | 6 | PyAVM is a module to represent, read, and write metadata following the 7 | `*Astronomy Visualization 8 | Metadata* `__ (AVM) 9 | standard. 10 | 11 | Requirements 12 | ------------ 13 | 14 | PyAVM supports Python 3.8 and later. No other dependencies are needed 15 | simply to read and embed AVM meta-data. 16 | 17 | However, the following optional dependencies are needed for more 18 | advanced functionality: 19 | 20 | - `Numpy `__ 1.10 or later 21 | - `Astropy `__ to handle WCS objects and FITS 22 | headers 23 | - `py.test `__ and 24 | `PIL `__ for tests 25 | 26 | Installing and Reporting issues 27 | ------------------------------- 28 | 29 | PyAVM can be installed with pip:: 30 | 31 | pip install pyavm 32 | 33 | Please report any issues you encounter via the `issue 34 | tracker `__ on GitHub. 35 | 36 | Using PyAVM 37 | ----------- 38 | 39 | Importing 40 | ~~~~~~~~~ 41 | 42 | PyAVM provides the ``AVM`` class to represent AVM meta-data, and is 43 | imported as follows: 44 | 45 | .. code:: python 46 | 47 | >>> from pyavm import AVM 48 | 49 | Parsing files 50 | ~~~~~~~~~~~~~ 51 | 52 | To parse AVM meta-data from an existing image, simply call the 53 | ``from_image`` class method using the filename of the image (or any 54 | file-like object): 55 | 56 | .. code:: python 57 | 58 | >>> avm = AVM.from_image('myexample.jpg') 59 | 60 | Only JPEG and PNG files are properly supported in that the parsing 61 | follows the JPEG and PNG specification. For other file formats, PyAVM 62 | will simply scan the contents of the file, looking for an XMP packet. 63 | This method is less reliable, but should work in most real-life cases. 64 | 65 | Accessing and setting the meta-data 66 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 67 | 68 | You can view the contents of the AVM object by using 69 | 70 | .. code:: python 71 | 72 | >>> print(avm) 73 | 74 | The AVM meta-data can be accessed using the attribute notation: 75 | 76 | .. code:: python 77 | 78 | >>> avm.Spatial.Equinox 79 | 'J2000' 80 | >>> avm.Publisher 81 | 'Chandra X-ray Observatory' 82 | 83 | Tags can be modified: 84 | 85 | .. code:: python 86 | 87 | >>> avm.Spatial.Equinox = "B1950" 88 | >>> avm.Spatial.Notes = "The WCS information was updated on 04/02/2010" 89 | 90 | Creating an AVM object from scratch 91 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 92 | 93 | To create an empty AVM meta-data holder, simply call ``AVM()`` without 94 | any arguments: 95 | 96 | .. code:: python 97 | 98 | >>> avm = AVM() 99 | 100 | Note that this will create an AVM object following the 1.2 101 | specification. If necessary, you can specify which version of the 102 | standard to use: 103 | 104 | .. code:: python 105 | 106 | >>> avm = AVM(version=1.1) 107 | 108 | Converting to a WCS object 109 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 110 | 111 | It is possible to create an Astropy WCS object from the AVM meta-data: 112 | 113 | .. code:: python 114 | 115 | >>> wcs = avm.to_wcs() 116 | 117 | By default, ``Spatial.FITSheader`` will be used if available, but if 118 | not, the WCS information is extracted from the other ``Spatial.*`` tags. 119 | To force PyAVM to not try ``Spatial.FITSheader``, use: 120 | 121 | .. code:: python 122 | 123 | >>> wcs = avm.to_wcs(use_full_header=False) 124 | 125 | Initializing from a FITS header 126 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 127 | 128 | To create an AVM meta-data object from a FITS header, simply pass the 129 | header (as an Astropy Header instance) to the ``from_header`` class 130 | method: 131 | 132 | .. code:: python 133 | 134 | >>> from astropy.io import fits 135 | >>> header = fits.getheader('image.fits') 136 | >>> avm = AVM.from_header(header) 137 | 138 | By default, the AVM tag ``Spatial.FITSheader`` will be created, 139 | containing the full header (in addition to the other ``Spatial.*`` 140 | tags). This can be disabled with: 141 | 142 | .. code:: python 143 | 144 | >>> avm = AVM.from_header(header, include_full_header=False) 145 | 146 | Initializing from a WCS object 147 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 148 | 149 | Similarly, it is possible to create an AVM meta-data object from an 150 | Astropy WCS instance: 151 | 152 | .. code:: python 153 | 154 | >>> from astropy.wcs import WCS 155 | >>> from pyavm import AVM 156 | >>> wcs = WCS('image.fits') 157 | >>> avm = AVM.from_wcs(wcs) 158 | 159 | Tagging images with AVM meta-data 160 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 161 | 162 | It is possible to embed AVM meta-data into an image file: 163 | 164 | .. code:: python 165 | 166 | >>> avm.embed('original_image.jpg', 'tagged_image.jpg') 167 | 168 | At this time, only JPG and PNG files are supported for embedding. 169 | 170 | .. |Build Status| image:: https://github.com/astrofrog/pyavm/actions/workflows/main.yml/badge.svg 171 | :target: https://github.com/astrofrog/pyavm/actions/workflows/main.yml 172 | .. |Coverage Status| image:: https://coveralls.io/repos/astrofrog/pyavm/badge.svg?branch=master 173 | :target: https://coveralls.io/r/astrofrog/pyavm?branch=master 174 | -------------------------------------------------------------------------------- /pyavm/__init__.py: -------------------------------------------------------------------------------- 1 | from .avm import AVM, NoAVMPresent # noqa 2 | 3 | from ._version import __version__ 4 | -------------------------------------------------------------------------------- /pyavm/cv.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from __future__ import print_function, division 4 | 5 | """ 6 | Storage for controlled vocabulary fields 7 | """ 8 | 9 | TYPE_CHOICES = [ 10 | 'Observation', 11 | 'Artwork', 12 | 'Photographic', 13 | 'Planetary', 14 | 'Simulation', 15 | 'Collage', 16 | 'Chart' 17 | ] 18 | 19 | IMAGE_PRODUCT_QUALITY_CHOICES = [ 20 | 'Good', 21 | 'Moderate', 22 | 'Poor' 23 | ] 24 | 25 | SPECTRAL_COLOR_ASSIGNMENT_CHOICES = [ 26 | 'Purple', 27 | 'Blue', 28 | 'Cyan', 29 | 'Green', 30 | 'Yellow', 31 | 'Orange', 32 | 'Red', 33 | 'Magenta', 34 | 'Grayscale', 35 | 'Pseudocolor', 36 | 'Luminosity', 37 | ] 38 | 39 | SPECTRAL_BAND_CHOICES = [ 40 | 'Radio', 41 | 'Millimeter', 42 | 'Infrared', 43 | 'Optical', 44 | 'Ultraviolet', 45 | 'X-ray', 46 | 'Gamma-ray' 47 | ] 48 | 49 | SPATIAL_COORDINATE_FRAME_CHOICES = [ 50 | 'ICRS', 51 | 'FK5', 52 | 'FK4', 53 | 'ECL', 54 | 'GAL', 55 | 'SGAL', 56 | ] 57 | 58 | SPATIAL_EQUINOX_CHOICES = [ 59 | 'J2000', 60 | 'B1950', 61 | ] 62 | 63 | SPATIAL_COORDSYSTEM_PROJECTION_CHOICES = [ 64 | 'TAN', 65 | 'SIN', 66 | 'ARC', 67 | 'AIT', 68 | 'CAR', 69 | 'CEA', 70 | ] 71 | 72 | SPATIAL_QUALITY_CHOICES = [ 73 | 'Full', 74 | 'Position', 75 | ] 76 | 77 | METADATA_VERSION_CHOICES = [ 78 | 1.2, 79 | 1.1, 80 | 1.0, 81 | ] -------------------------------------------------------------------------------- /pyavm/embed.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, division 2 | 3 | import struct 4 | import warnings 5 | 6 | from .jpeg import is_jpeg, JPEGFile, JPEGSegment 7 | from .png import is_png, PNGFile, PNGChunk 8 | 9 | 10 | def embed_xmp(image_in, image_out, xmp_packet): 11 | 12 | if is_jpeg(image_in): 13 | 14 | # Check length 15 | if len(xmp_packet) >= 65503: 16 | raise Exception("XMP packet is too long to embed in JPG file") 17 | 18 | # XMP segment 19 | xmp_segment = JPEGSegment() 20 | 21 | # APP1 marker 22 | xmp_segment.bytes = b"\xff\xe1" 23 | 24 | # Length of XMP packet + 2 + 29 25 | xmp_segment.bytes += struct.pack('>H', len(xmp_packet) + 29 + 2) 26 | 27 | # XMP Namespace URI (NULL-terminated) 28 | xmp_segment.bytes += b"http://ns.adobe.com/xap/1.0/\x00" 29 | 30 | # XMP packet 31 | xmp_segment.bytes += xmp_packet 32 | 33 | # Read in input file 34 | jpeg_file = JPEGFile.read(image_in) 35 | 36 | # Check if there is already XMP data in the file 37 | existing = [] 38 | for segment in jpeg_file.segments: 39 | if segment.type == 'APP1': 40 | if segment.bytes[4:32] == b'http://ns.adobe.com/xap/1.0/': 41 | existing.append(segment) 42 | if existing: 43 | warnings.warn("Discarding existing XMP packet from JPEG file") 44 | for e in existing: 45 | jpeg_file.segments.remove(e) 46 | 47 | # Position at which to insert the packet 48 | markers = [x.type for x in jpeg_file.segments] 49 | 50 | if 'APP1' in markers: # Put it after existing APP1 51 | index = markers.index('APP1') + 1 52 | elif 'APP0' in markers: # Put it after existing APP0 53 | index = markers.index('APP0') + 1 54 | elif 'SOF' in markers: 55 | index = markers.index('SOF') 56 | else: 57 | raise ValueError("Could not find SOF marker") 58 | 59 | # Insert segment into JPEG file 60 | jpeg_file.segments.insert(index, xmp_segment) 61 | jpeg_file.write(image_out) 62 | 63 | elif is_png(image_in): 64 | 65 | xmp_chunk = PNGChunk() 66 | 67 | # Keyword 68 | xmp_chunk.data = b'XML:com.adobe.xmp' 69 | 70 | # Null separator 71 | xmp_chunk.data += b'\x00' 72 | 73 | # Compression flag 74 | xmp_chunk.data += b'\x00' 75 | 76 | # Compression method 77 | xmp_chunk.data += b'\x00' 78 | 79 | # Null separator 80 | xmp_chunk.data += b'\x00' 81 | 82 | # Null separator 83 | xmp_chunk.data += b'\x00' 84 | 85 | # Text 86 | xmp_chunk.data += xmp_packet 87 | 88 | # Set type 89 | xmp_chunk.type = b'iTXt' 90 | 91 | # Read in input file 92 | png_file = PNGFile.read(image_in) 93 | 94 | # Check if there is already XMP data in the file 95 | existing = [] 96 | for chunk in png_file.chunks: 97 | if chunk.type == b'iTXt': 98 | if chunk.data.startswith(b'XML:com.adobe.xmp'): 99 | existing.append(chunk) 100 | if existing: 101 | warnings.warn("Discarding existing XMP packet from PNG file") 102 | for e in existing: 103 | png_file.chunks.remove(e) 104 | 105 | # Insert chunk into PNG file 106 | png_file.chunks.insert(1, xmp_chunk) 107 | png_file.write(image_out) 108 | 109 | else: 110 | 111 | raise Exception("Only JPG and PNG files are supported at this time") 112 | -------------------------------------------------------------------------------- /pyavm/exceptions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from __future__ import print_function, division 4 | 5 | 6 | class AVMListLengthError(Exception): 7 | """ 8 | Raised when a list is not the correct length 9 | """ 10 | pass 11 | 12 | 13 | class AVMItemNotInControlledVocabularyError(Exception): 14 | """ 15 | Raised when an string is not in a required controlled vocabulary 16 | """ 17 | pass 18 | 19 | 20 | class AVMEmptyValueError(Exception): 21 | """ 22 | Raised when a list is given with no relevant data 23 | """ 24 | pass 25 | 26 | 27 | class NoXMPPacketFound(Exception): 28 | """ 29 | Raised when no XMP packet is found in a file 30 | """ 31 | pass 32 | -------------------------------------------------------------------------------- /pyavm/extract.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, division 2 | 3 | import re 4 | import warnings 5 | 6 | from .jpeg import is_jpeg, JPEGFile 7 | from .png import is_png, PNGFile 8 | from .exceptions import NoXMPPacketFound 9 | 10 | __all__ = ['extract_xmp'] 11 | 12 | 13 | def extract_xmp(image, xmp_packet_index=None): 14 | 15 | if is_jpeg(image): 16 | 17 | # Read in input file 18 | jpeg_file = JPEGFile.read(image) 19 | 20 | xmp_segments = [] 21 | 22 | # Loop through segments and search for XMP packet 23 | for segment in jpeg_file.segments: 24 | if segment.type == 'APP1': 25 | if segment.bytes[4:32] == b'http://ns.adobe.com/xap/1.0/': 26 | xmp_segments.append(segment.bytes[32:]) 27 | 28 | if len(xmp_segments) > 0: 29 | if xmp_packet_index is None: 30 | if len(xmp_segments) > 1: 31 | warnings.warn("Multiple XMP packets are present but " 32 | "xmp_packet_index was not specified, assuming " 33 | "xmp_packet_index=0") 34 | xmp_packet_index = 0 35 | elif xmp_packet_index >= len(xmp_segments): 36 | raise IndexError("xmp_packet was set to {0} but only {1} " 37 | "packets are present".format(xmp_packet_index, 38 | len(xmp_segments))) 39 | return xmp_segments[xmp_packet_index] 40 | else: # No XMP data was found 41 | raise NoXMPPacketFound("No XMP packet present in file") 42 | 43 | elif is_png(image): 44 | 45 | # Read in input file 46 | png_file = PNGFile.read(image) 47 | 48 | xmp_chunks = [] 49 | 50 | # Loop through chunks and search for XMP packet 51 | for chunk in png_file.chunks: 52 | if chunk.type == b'iTXt': 53 | if chunk.data.startswith(b'XML:com.adobe.xmp'): 54 | xmp_chunks.append(chunk.data[22:]) 55 | 56 | if len(xmp_chunks) > 0: 57 | if xmp_packet_index is None: 58 | if len(xmp_chunks) > 1: 59 | warnings.warn("Multiple XMP packets are present but " 60 | "xmp_packet_index was not specified, assuming " 61 | "xmp_packet_index=0") 62 | xmp_packet_index = 0 63 | elif xmp_packet_index >= len(xmp_chunks): 64 | raise IndexError("xmp_packet was set to {0} but only {1} " 65 | "packets are present".format(xmp_packet_index, 66 | len(xmp_chunks))) 67 | return xmp_chunks[xmp_packet_index] 68 | else: # No XMP data was found 69 | raise NoXMPPacketFound("No XMP packet present in file") 70 | 71 | else: 72 | 73 | warnings.warn("Only PNG and JPEG files can be properly parsed " 74 | "- scanning file contents for XMP packet") 75 | 76 | with open(image, 'rb') as fileobj: 77 | contents = fileobj.read() 78 | 79 | start_positions = [m.start() for m in re.finditer(b" 0: 82 | if xmp_packet_index is None: 83 | if len(start_positions) > 1: 84 | warnings.warn("Multiple XMP packets are present but " 85 | "xmp_packet_index was not specified, assuming " 86 | "xmp_packet_index=0") 87 | xmp_packet_index = 0 88 | elif xmp_packet_index >= len(start_positions): 89 | raise IndexError("xmp_packet was set to {0} but only {1} " 90 | "packets are present".format(xmp_packet_index, 91 | len(start_positions))) 92 | start = start_positions[xmp_packet_index] - 2 93 | end = contents.index(b"", start) + 12 94 | else: 95 | raise NoXMPPacketFound("No XMP packet present in file") 96 | 97 | return contents[start:end] 98 | -------------------------------------------------------------------------------- /pyavm/jpeg.py: -------------------------------------------------------------------------------- 1 | # Pure-python JPEG parser 2 | # Copyright (c) 2013 Thomas P. Robitaille 3 | 4 | from __future__ import print_function, division 5 | 6 | import struct 7 | 8 | # Define common markers 9 | 10 | MARKERS = {} 11 | MARKERS[b'\xd8'] = 'SOI' 12 | MARKERS[b'\xc0'] = 'SOF0' 13 | MARKERS[b'\xc2'] = 'SOF2' 14 | MARKERS[b'\xc4'] = 'DHT' 15 | MARKERS[b'\xdb'] = 'DQT' 16 | MARKERS[b'\xdd'] = 'DRI' 17 | MARKERS[b'\xda'] = 'SOS' 18 | MARKERS[b'\xd0'] = 'RST0' 19 | MARKERS[b'\xd1'] = 'RST1' 20 | MARKERS[b'\xd2'] = 'RST2' 21 | MARKERS[b'\xd3'] = 'RST3' 22 | MARKERS[b'\xd4'] = 'RST4' 23 | MARKERS[b'\xd5'] = 'RST5' 24 | MARKERS[b'\xd6'] = 'RST6' 25 | MARKERS[b'\xd7'] = 'RST7' 26 | MARKERS[b'\xe0'] = 'APP0' 27 | MARKERS[b'\xe1'] = 'APP1' 28 | MARKERS[b'\xe2'] = 'APP2' 29 | MARKERS[b'\xe3'] = 'APP3' 30 | MARKERS[b'\xe4'] = 'APP4' 31 | MARKERS[b'\xe5'] = 'APP5' 32 | MARKERS[b'\xe6'] = 'APP6' 33 | MARKERS[b'\xe7'] = 'APP7' 34 | MARKERS[b'\xe8'] = 'APP8' 35 | MARKERS[b'\xe9'] = 'APP9' 36 | MARKERS[b'\xfe'] = 'COM' 37 | MARKERS[b'\xd9'] = 'EOI' 38 | 39 | # Define some markers which are always followed by variable-length data 40 | 41 | VARIABLE = ['APP0', 'APP1', 'APP2', 'APP3', 'APP4', 42 | 'APP5', 'APP6', 'APP7', 'APP8', 'APP9'] 43 | 44 | 45 | def is_jpeg(filename): 46 | with open(filename, 'rb') as f: 47 | return f.read(4) == b'\xff\xd8\xff\xe0' 48 | 49 | 50 | class JPEGSegment(object): 51 | 52 | @classmethod 53 | def from_bytes(cls, bytes): 54 | self = cls() 55 | if bytes[1:2] in MARKERS: 56 | self.type = MARKERS[bytes[1:2]] 57 | else: 58 | self.type = "UNKNOWN" 59 | self.bytes = bytes 60 | return self 61 | 62 | def write(self, fileobj): 63 | fileobj.write(self.bytes) 64 | 65 | 66 | class JPEGFile(object): 67 | 68 | @classmethod 69 | def read(cls, filename): 70 | 71 | fileobj = open(filename, 'rb') 72 | contents = fileobj.read() 73 | fileobj.close() 74 | 75 | self = cls() 76 | 77 | # Search for all starts of segments. Segments all start with 0xff, but 78 | # we should ignore 0xff instances that are followed by 0x00. 79 | 80 | self.segments = [] 81 | 82 | start = end = 0 83 | while True: 84 | if contents[start + 1:start + 2] in MARKERS and MARKERS[contents[start + 1:start + 2]] in VARIABLE: 85 | end = contents.find(b'\xff', end + 4) 86 | else: 87 | end = contents.find(b'\xff', end + 1) 88 | if end == -1 or contents[end + 1:end + 2] not in [b'\xff', b'\x00']: 89 | if end == -1: 90 | end = len(contents) + 1 91 | self.segments.append(JPEGSegment.from_bytes(contents[start:end])) 92 | if end == len(contents) + 1: 93 | break 94 | else: 95 | start = end 96 | 97 | if self.segments[0].type != 'SOI': 98 | raise ValueError("Image did not start with SOI but with {0}".format(self.segments[0].type)) 99 | 100 | if self.segments[-1].type != 'EOI': 101 | raise ValueError("Image did not end with EOI but with {0}".format(self.segments[-1].type)) 102 | 103 | return self 104 | 105 | def write(self, filename): 106 | 107 | fileobj = open(filename, 'wb') 108 | 109 | for segment in self.segments: 110 | segment.write(fileobj) 111 | 112 | fileobj.close() 113 | -------------------------------------------------------------------------------- /pyavm/png.py: -------------------------------------------------------------------------------- 1 | # Pure-python PNG parser 2 | # Copyright (c) 2013 Thomas P. Robitaille 3 | 4 | from __future__ import print_function, division 5 | 6 | import struct 7 | 8 | PNG_SIGNATURE = b'\x89\x50\x4e\x47\x0d\x0a\x1a\x0a' 9 | 10 | 11 | def is_png(filename): 12 | with open(filename, 'rb') as f: 13 | return f.read(8) == b'\x89\x50\x4e\x47\x0d\x0a\x1a\x0a' 14 | 15 | 16 | class PNGChunk(object): 17 | 18 | @classmethod 19 | def read(cls, fileobj): 20 | 21 | self = cls() 22 | 23 | # Read in chunk length 24 | length = struct.unpack('>I', fileobj.read(4))[0] 25 | 26 | # Read in chunk type 27 | self.type = fileobj.read(4) 28 | 29 | # Read in data 30 | self.data = fileobj.read(length) 31 | 32 | # Read in CRC 33 | crc = struct.unpack('>I', fileobj.read(4))[0] 34 | 35 | # Check that the CRC matches the actual one 36 | if crc != self.crc: 37 | raise ValueError("CRC ({0}) does not match advertised ({1})".format(self.crc, crc)) 38 | 39 | if length != self.length: 40 | raise ValueError("Dynamic length ({0}) does not match original length ({1})".format(self.length, length)) 41 | 42 | return self 43 | 44 | def write(self, fileobj): 45 | 46 | # Write length 47 | fileobj.write(struct.pack('>I', self.length)) 48 | 49 | # Write type 50 | fileobj.write(self.type) 51 | 52 | # Write data 53 | fileobj.write(self.data) 54 | 55 | # Write CRC 56 | fileobj.write(struct.pack('>I', self.crc)) 57 | 58 | @property 59 | def crc(self): 60 | # Note from Python docs: "To generate the same numeric value across all 61 | # Python versions and platforms use crc32(data) & 0xffffffff. If you 62 | # are only using the checksum in packed binary format this is not 63 | # necessary as the return value is the correct 32bit binary 64 | # representation regardless of sign." 65 | # This is indeed true, I see different values in Python 2 and 3. 66 | from zlib import crc32 67 | return crc32(self.type + self.data) & 0xffffffff 68 | 69 | @property 70 | def length(self): 71 | return len(self.data) 72 | 73 | 74 | class PNGFile(object): 75 | 76 | @classmethod 77 | def read(cls, filename): 78 | 79 | fileobj = open(filename, 'rb') 80 | 81 | self = cls() 82 | 83 | # Read signature 84 | sig = fileobj.read(8) 85 | 86 | if sig != PNG_SIGNATURE: 87 | raise ValueError("Signature ({0}) does match expected ({1})".format(sig, PNG_SIGNATURE)) 88 | 89 | self.chunks = [] 90 | 91 | while True: 92 | chunk = PNGChunk.read(fileobj) 93 | self.chunks.append(chunk) 94 | if chunk.type == b'IEND': 95 | break 96 | 97 | fileobj.close() 98 | 99 | return self 100 | 101 | def write(self, filename): 102 | 103 | fileobj = open(filename, 'wb') 104 | 105 | fileobj.write(PNG_SIGNATURE) 106 | 107 | for chunk in self.chunks: 108 | chunk.write(fileobj) 109 | 110 | fileobj.close() 111 | -------------------------------------------------------------------------------- /pyavm/specs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from __future__ import print_function, division 4 | 5 | """ 6 | Specification for various versions of AVM 7 | """ 8 | 9 | from copy import deepcopy 10 | 11 | from .datatypes import * 12 | from .cv import * 13 | 14 | SPECS = {} 15 | 16 | SPECS[1.1] = { 17 | 18 | # Creator Metadata 19 | 'Creator': AVMString('photoshop:Source'), 20 | 'CreatorURL': AVMURL('Iptc4xmpCore:CreatorContactInfo.CiUrlWork'), 21 | 'Contact.Name': AVMOrderedList('dc:creator'), 22 | 'Contact.Email': AVMEmail('Iptc4xmpCore:CreatorContactInfo.CiEmailWork'), 23 | 'Contact.Address': AVMString('Iptc4xmpCore:CreatorContactInfo.CiAdrExtadr'), 24 | 'Contact.Telephone': AVMString('Iptc4xmpCore:CreatorContactInfo.CiTelWork'), 25 | 'Contact.City': AVMString('Iptc4xmpCore:CreatorContactInfo.CiAdrCity'), 26 | 'Contact.StateProvince': AVMString('Iptc4xmpCore:CreatorContactInfo.CiAdrRegion'), 27 | 'Contact.PostalCode': AVMString('Iptc4xmpCore:CreatorContactInfo.CiAdrPcode'), 28 | 'Contact.Country': AVMString('Iptc4xmpCore:CreatorContactInfo.CiAdrCtry'), 29 | 'Rights': AVMLocalizedString('xapRights:UsageTerms'), 30 | 31 | # Content Metadata 32 | 'Title': AVMLocalizedString('dc:title'), 33 | 'Headline': AVMString('photoshop:Headline'), 34 | 'Description': AVMLocalizedString('dc:description'), 35 | 'Subject.Category': AVMUnorderedStringList('avm:Subject.Category'), 36 | 'Subject.Name': AVMUnorderedStringList('dc:subject'), 37 | 'Distance': AVMOrderedFloatList('avm:Distance', length=2, strict_length=False), 38 | 'Distance.Notes': AVMString('avm:Distance.Notes'), 39 | 'ReferenceURL': AVMURL('avm:ReferenceURL'), 40 | 'Credit': AVMString('photoshop:Credit'), 41 | 'Date': AVMDateTime('photoshop:DateCreated'), 42 | 'ID': AVMString('avm:ID'), 43 | 'Type': AVMStringCVCapitalize('avm:Type', TYPE_CHOICES), 44 | 'Image.ProductQuality': AVMStringCVCapitalize('avm:Image.ProductQuality', IMAGE_PRODUCT_QUALITY_CHOICES), 45 | 46 | # Observation Metadata 47 | 'Facility': AVMOrderedList('avm:Facility'), 48 | 'Instrument': AVMOrderedList('avm:Instrument'), 49 | 'Spectral.ColorAssignment': AVMOrderedListCV('avm:Spectral.ColorAssignment', SPECTRAL_COLOR_ASSIGNMENT_CHOICES), 50 | 'Spectral.Band': AVMOrderedListCV('avm:Spectral.Band', SPECTRAL_BAND_CHOICES), 51 | 'Spectral.Bandpass': AVMOrderedList('avm:Spectral.Bandpass'), 52 | 'Spectral.CentralWavelength': AVMOrderedFloatList('avm:Spectral.CentralWavelength'), 53 | 'Spectral.Notes': AVMLocalizedString('avm:Spectral.Notes'), 54 | 'Temporal.StartTime': AVMDateTimeList('avm:Temporal.StartTime'), 55 | 'Temporal.IntegrationTime': AVMOrderedFloatList('avm:Temporal.IntegrationTime'), 56 | 'DatasetID': AVMOrderedList('avm:DatasetID'), 57 | 58 | # Coordinate Metadata 59 | 'Spatial.CoordinateFrame': AVMStringCVUpper('avm:Spatial.CoordinateFrame', SPATIAL_COORDINATE_FRAME_CHOICES), 60 | 'Spatial.Equinox': AVMString('avm:Spatial.Equinox'), 61 | 'Spatial.ReferenceValue': AVMOrderedFloatList('avm:Spatial.ReferenceValue', length=2, strict_length=True), 62 | 'Spatial.ReferenceDimension': AVMOrderedFloatList('avm:Spatial.ReferenceDimension', length=2, strict_length=True), 63 | 'Spatial.ReferencePixel': AVMOrderedFloatList('avm:Spatial.ReferencePixel', length=2, strict_length=True), 64 | 'Spatial.Scale': AVMOrderedFloatList('avm:Spatial.Scale', length=2, strict_length=True), 65 | 'Spatial.Rotation': AVMFloat('avm:Spatial.Rotation'), 66 | 'Spatial.CoordsystemProjection': AVMStringCVUpper('avm:Spatial.CoordsystemProjection', SPATIAL_COORDSYSTEM_PROJECTION_CHOICES), 67 | 'Spatial.Quality': AVMStringCVCapitalize('avm:Spatial.Quality', SPATIAL_QUALITY_CHOICES), 68 | 'Spatial.Notes': AVMLocalizedString('avm:Spatial.Notes'), 69 | 'Spatial.FITSheader': AVMString('avm:Spatial.FITSheader'), 70 | 'Spatial.CDMatrix': AVMOrderedFloatList('avm:Spatial.CDMatrix', length=4, strict_length=True, deprecated=True), 71 | 72 | # Publisher Metadata 73 | 'Publisher': AVMString('avm:Publisher'), 74 | 'PublisherID': AVMString('avm:PublisherID'), 75 | 'ResourceID': AVMString('avm:ResourceID'), 76 | 'ResourceURL': AVMURL('avm:ResourceURL'), 77 | 'RelatedResources': AVMUnorderedStringList('avm:RelatedResources'), 78 | 'MetadataDate': AVMDateTime('avm:MetadataDate'), 79 | 'MetadataVersion': AVMFloat('avm:MetadataVersion'), 80 | 81 | # FITS Liberator Metadata 82 | 83 | 'FL.BackgroundLevel': AVMOrderedFloatList('avm:FL.BackgroundLevel'), 84 | 'FL.BlackLevel': AVMOrderedFloatList('avm:FL.BlackLevel'), 85 | 'FL.ScaledPeakLevel': AVMOrderedFloatList('avm:FL.ScaledPeakLevel'), 86 | 'FL.PeakLevel': AVMOrderedFloatList('avm:FL.PeakLevel'), 87 | 'FL.WhiteLevel': AVMOrderedFloatList('avm:FL.WhiteLevel'), 88 | 'FL.ScaledBackgroundLevel': AVMOrderedFloatList('avm:FL.ScaledBackgroundLevel'), 89 | 'FL.StretchFunction': AVMOrderedList('avm:FL.StretchFunction') 90 | } 91 | 92 | # TODO: write specification for version 1.0 93 | SPECS[1.0] = deepcopy(SPECS[1.1]) 94 | 95 | SPECS[1.2] = deepcopy(SPECS[1.1]) 96 | 97 | # Content Metadata 98 | 99 | SPECS[1.2]['PublicationID'] = AVMUnorderedStringList('avm:PublicationID') 100 | SPECS[1.2]['ProposalID'] = AVMUnorderedStringList('avm:ProposalID') 101 | SPECS[1.2]["RelatedResources"] = AVMUnorderedStringList('avm:RelatedResources', deprecated=True) 102 | 103 | # Create reverse lookup 104 | 105 | REVERSE_SPECS = {} 106 | for spec in SPECS: 107 | REVERSE_SPECS[spec] = {} 108 | for key in SPECS[spec]: 109 | value = SPECS[spec][key] 110 | REVERSE_SPECS[spec][value.namespace, value.tag] = key 111 | -------------------------------------------------------------------------------- /pyavm/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrofrog/pyavm/9e9601315c902d6113cdc1182f28f1d8acacbd4d/pyavm/tests/__init__.py -------------------------------------------------------------------------------- /pyavm/tests/data/a3627_wcs_metadata.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | Observation 6 | ICRS 7 | J2000 8 | TAN 9 | 1.0 10 | 11 | 12 | 3.1000000000000E+00 13 | 14 | 15 | 16 | 17 | 2.4316946405039E+02 18 | -6.0787955033553E+01 19 | 20 | 21 | 22 | 23 | 2400 24 | 2180 25 | 26 | 27 | 28 | 29 | 3815.387 30 | -340.2559 31 | 32 | 33 | 34 | 35 | -2.82444435466E-05 36 | 2.87125388167E-05 37 | 38 | 39 | 40 | 41 | Linear 42 | 43 | 44 | 45 | 46 | 0.00000000000000 47 | 48 | 49 | 50 | 51 | 2.07566460267117e-05 52 | 53 | 54 | 55 | 56 | 0.00000000000000 57 | 58 | 59 | 60 | 61 | 2.07566460267117e-05 62 | 63 | 64 | 65 | 66 | 2.38130511406088e-06 67 | 68 | 69 | 70 | 71 | 4.89249959797963e-05 72 | 73 | 74 | 75 | 78 | uuid:124A050B69D711DCBAEBC093900089A9 79 | uuid:4E2CF2EDDE69DC1185D3E07FEF457848 80 | 81 | uuid:124A050869D711DCBAEBC093900089A9 82 | uuid:124A050869D711DCBAEBC093900089A9 83 | 84 | 85 | 87 | 2007-09-21T13:22:18-04:00 88 | 2007-09-21T14:15:55-04:00 89 | 2007-09-21T14:15:55-04:00 90 | Adobe Photoshop CS3 Macintosh 91 | 92 | 94 | image/tiff 95 | 96 | 97 | This composite image shows a tail that has been created as the galaxy ESO 137-001 plunges into the galaxy cluster Abell 3627. X-rays from Chandra (blue) and optical light (white and red) from the SOAR telescope show that as the galaxy plummets, it sheds material and is forming stars behind it in a tail that stretches over 200,000 light years long. This result demonstrates that stars can form well outside of their parent galaxy. 98 | 99 | 100 | 101 | 102 | Chandra X-ray Observatory 103 | 104 | 105 | 106 | 107 | Abell 3627 108 | 109 | 110 | 111 | 112 | http://chandra.harvard.edu/photo/image_use.html 113 | 114 | 115 | 116 | 118 | 3 119 | X-ray: NASA/CXC/MSU/M.Sun et al; H-alpha/Optical: SOAR (MSU/NOAO/UNC/CNPq-Brazil)/M.Sun et al. 120 | Chandra X-ray Observatory 121 | 2007-09-20 122 | ESO 137-001 in Abell 3627: Orphan Stars Found in Long Galaxy Tail 123 | 124 | 125 | 127 | 1 128 | 720000/10000 129 | 720000/10000 130 | 2 131 | 256,257,258,259,262,274,277,284,530,531,282,283,296,301,318,319,529,532,306,270,271,272,305,315,33432;F021367C6CBCDEA1AB937AEAF620A158 132 | 2400 133 | 2180 134 | 135 | 136 | 8 137 | 8 138 | 8 139 | 140 | 141 | 5 142 | 2 143 | 3 144 | 1 145 | 146 | 148 | 2400 149 | 2180 150 | -1 151 | 36864,40960,40961,37121,37122,40962,40963,37510,40964,36867,36868,33434,33437,34850,34852,34855,34856,37377,37378,37379,37380,37381,37382,37383,37384,37385,37386,37396,41483,41484,41486,41487,41488,41492,41493,41495,41728,41729,41730,41985,41986,41987,41988,41989,41990,41991,41992,41993,41994,41995,41996,42016,0,2,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,20,22,23,24,25,26,27,28,30;B0BC227E28DF828CBAD6B4637D9F57D0 152 | 153 | 155 | Print 156 | 157 | 159 | 160 | 60 Garden St 161 | Cambridge 162 | MA 163 | 02138 164 | USA 165 | http://chandra.harvard.edu 166 | 167 | 168 | 170 | 171 | 172 | Public Domain 173 | 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /pyavm/tests/data/eso_eso1723a_320.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrofrog/pyavm/9e9601315c902d6113cdc1182f28f1d8acacbd4d/pyavm/tests/data/eso_eso1723a_320.jpg -------------------------------------------------------------------------------- /pyavm/tests/data/eso_eso1723a_320.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astrofrog/pyavm/9e9601315c902d6113cdc1182f28f1d8acacbd4d/pyavm/tests/data/eso_eso1723a_320.png -------------------------------------------------------------------------------- /pyavm/tests/data/example_header.hdr: -------------------------------------------------------------------------------- 1 | SIMPLE = T 2 | BITPIX = -64 3 | NAXIS = 2 4 | NAXIS1 = 599 5 | NAXIS2 = 599 6 | EXTEND = T / FITS dataset may contain extensions 7 | COMMENT FITS (Flexible Image Transport System) format is defined in 'Astronomy 8 | COMMENT and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H 9 | CRPIX1 = 299.628 10 | CRVAL1 = 0.000000000 11 | CDELT1 = -0.001666666707 12 | CTYPE1 = 'GLON-CAR' 13 | CRPIX2 = 299.394 14 | CRVAL2 = 0.000000000 15 | CDELT2 = 0.001666666707 16 | CTYPE2 = 'GLAT-CAR' 17 | CROTA2 = 0.000000000 18 | LONPOLE = 0.000000000 19 | WAVELENG= 2.13400E-05 20 | BUNIT = 'W/m^2-sr' 21 | TELESCOP= 'MSX ' 22 | INSTRUME= 'SPIRITIII' 23 | ORIGIN = 'AFRL-VSBC' 24 | END 25 | -------------------------------------------------------------------------------- /pyavm/tests/data/heic0409a.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 39 | 40 | 41 | 8 42 | 8 43 | 8 44 | 45 | 46 | 47 | 48 | Hubble Space Telescope 49 | 50 | 51 | 52 | 53 | C.5.4.6. 54 | C.5.3.2. 55 | 56 | 57 | 58 | 59 | http://www.spacetelescope.org 60 | 61 | 62 | 63 | 64 | This artist's impression shows the dust torus around a super-massive black hole. Black holes lurk at the centres of active galaxies in environments not unlike those found in violent tornadoes on Earth. Just as in a tornado, where debris is often found spinning about the vortex, so in a black hole, a dust torus surrounds its waist. In some cases astronomers can look along the axis of the dust torus from above or from below and have a clear view of the black hole. Technically these objects are then called type 1 sources. Type 2 sources lie with the dust torus edge-on as viewed from Earth so our view of the black hole is totally blocked by the dust over a range of wavelengths from the near-infrared to soft X-rays. While many dust-obscured low-power black holes (called Seyfert 2s) were known, until recently few of their high-power counterparts were known. The identification of a population of high-power obscured black holes and the active galaxies surrounding them has been a key goal for astronomers and will lead to greater understanding and a refinement of the cosmological models describing our Universe. The European AVO science team led by Paolo Padovani from Space Telescope-European Coordinating Facility and the European Southern Observatory in Munich, Germany, has discovered a whole population of the obscured, powerful supermassive black holes. Thirty of these objects were found in the so-called GOODS (Great Observatories Origins Deep Survey) fields. The GOODS survey consists of two areas that include some of the deepest observations from space- and ground-based telescopes, including the NASA/ESA Hubble Space Telescope, and have become the best studied patches in the sky. In the illustration the jets coming out of the regions nearest the black hole are also seen. The jets emerge from an area close to the black hole where a disk of accreted material rotates (not seen here). 65 | 66 | 67 | 68 | 69 | Super-massive black hole 70 | 71 | 72 | 79 | 80 | 81 | http://www.spacetelescope.org/copyright.html 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /pyavm/tests/data/kes75.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | Observation 6 | J2000 7 | TAN 8 | 1.0 9 | Chandra X-ray Observatory 10 | http://chandra.harvard.edu 11 | http://chandra.harvard.edu/photo/2008/kes75/ 12 | Good 13 | 14 | 15 | 281.604034424 16 | -2.97508049011 17 | 18 | 19 | 20 | 21 | 1800 22 | 1800 23 | 24 | 25 | 26 | 27 | 999.000000000 28 | 1021.00000000 29 | 30 | 31 | 32 | 33 | -4.16972395163E-05 34 | -2.94965799441E-07 35 | -2.94965799441E-07 36 | 4.16972395163E-05 37 | 38 | 39 | 40 | 41 | Linear 42 | 43 | 44 | 45 | 46 | 0.00000000000000 47 | 48 | 49 | 50 | 51 | 44174.14939999999478 52 | 53 | 54 | 55 | 56 | 0.00000000000000 57 | 58 | 59 | 60 | 61 | 44174.14939999999478 62 | 63 | 64 | 65 | 66 | 1104.00000000000000 67 | 68 | 69 | 70 | 71 | 65407.00000000000000 72 | 73 | 74 | 75 | 76 | Chandra 77 | Chandra 78 | Chandra 79 | 80 | 81 | 82 | 83 | ACIS 84 | ACIS 85 | ACIS 86 | 87 | 88 | 89 | 90 | Red 91 | Green 92 | Blue 93 | 94 | 95 | 96 | 97 | X-ray 98 | X-ray 99 | X-ray 100 | 101 | 102 | 103 | 104 | X-ray 105 | X-ray 106 | X-ray 107 | 108 | 109 | 110 | 111 | 1.24 112 | 0.55 113 | 0.23 114 | 115 | 116 | 117 | 118 | 2006-06-05 0131 119 | 120 | 121 | 122 | 123 | 1583400 124 | 125 | 126 | 127 | 128 | http://chandra.harvard.edu/photo/2008/kes75/kes75.tif 129 | 130 | 131 | 132 | 133 | B.4.1.4 134 | 135 | 136 | 137 | 140 | uuid:011B632DE2AF11DC967B8E6B8DF40572 141 | uuid:BA50AC97D0E2DC11A712FF27D364C9AE 142 | 143 | uuid:011B632AE2AF11DC967B8E6B8DF40572 144 | uuid:011B632AE2AF11DC967B8E6B8DF40572 145 | 146 | 147 | 149 | 2008-02-22T08:16:36-05:00 150 | 2008-02-22T12:05:35-05:00 151 | 2008-03-10T19:21:46-04:00 152 | Adobe Photoshop CS3 Macintosh 153 | 154 | 156 | image/tiff 157 | 158 | 159 | Chandra X-ray Observatory Center 160 | 161 | 162 | 163 | 164 | Kes 75 165 | 166 | 167 | 168 | 169 | This deep Chandra X-ray Observatory image shows the supernova remnant Kes 75, located almost 20,000 light years away. The explosion of a massive star created the supernova remnant, along with a pulsar, a rapidly spinning neutron star. The low energy X-rays are colored red in this image and the high energy X-rays are colored blue. The pulsar is the bright spot near the center of the image. The rapid rotation and strong magnetic field of the pulsar have generated a wind of energetic matter and antimatter particles that rush out at near the speed of light. This pulsar wind has created a large, magnetized bubble of high-energy particles called a pulsar wind nebulae, seen as the blue region surrounding the pulsar. The magnetic field of the pulsar in Kes 75 is thought to be more powerful than most pulsars, but less powerful than magnetars, a class of neutron star with the most powerful magnetic fields known in the Universe. Scientists are seeking to understand the relationship between these two classes of object. 170 | 171 | 172 | 173 | 175 | 7FBC26978B45592E97413D35411E8142 176 | Chandra X-ray Observatory 177 | Kes 75: One Weird Star Starts Acting Like AnotherKes 75
 178 | Credit: NASA/CXC/GSFC/F.P.Gavriil et al. 179 | 2008-02-21 180 | 181 | 3 182 | sRGB IEC61966-2.1 183 | 184 | 186 | 1 187 | 1800 188 | 1800 189 | 2 190 | 3 191 | 72/1 192 | 72/1 193 | 2 194 | 195 | 196 | 16 197 | 16 198 | 16 199 | 200 | 201 | 202 | 204 | 1800 205 | 1800 206 | 1 207 | 0221 208 | 209 | 211 | True 212 | 213 | 215 | Print 216 | 217 | 219 | 220 | 60 Garden St. 221 | Cambridge 222 | MA 223 | 02138 224 | USA 225 | cxcpub@cfa.harvard.edu 226 | 617.496.7941 227 | 228 | 229 | 231 | 232 | 233 | http://chandra.harvard.edu/photo/image_use.html 234 | 235 | 236 | 237 | 238 | -------------------------------------------------------------------------------- /pyavm/tests/data/opo0613a.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 45 | 46 | 47 | 8 48 | 8 49 | 8 50 | 51 | 52 | 53 | 54 | Hubble Space Telescope 55 | 56 | 57 | 58 | 59 | ACS 60 | 61 | 62 | 63 | 64 | 2873 65 | 3885 66 | 67 | 68 | 69 | 70 | 13.2655260 71 | 56.6447590 72 | 73 | 74 | 75 | 76 | 1.38940980370425e-05 77 | 1.38933645396111e-05 78 | 79 | 80 | 81 | 82 | 5.42615996e-06 83 | -1.27849599e-05 84 | -1.2790729e-05 85 | -5.43786526e-06 86 | 87 | 88 | 89 | 90 | WCS info from Hubble 91 | 92 | 93 | 94 | 95 | 1437.5 96 | 1943.5 97 | 98 | 99 | 100 | 101 | B.4.2.3.2. 102 | B4.2.1. 103 | B.4.1.2. 104 | 105 | 106 | 107 | 108 | http://www.spacetelescope.org 109 | 110 | 111 | 112 | 113 | <p>The yearly ritual of spring cleaning clears a house of dust as well as dust "bunnies", those pesky dust balls that frolic under beds and behind furniture. <a href="http://www.nasa.gov">NASA</a>/<a href="http://www.esa.int">ESA</a> Hubble Space Telescope has photographed similar dense knots of dust and gas in our Milky Way Galaxy. This cosmic dust, however, is not a nuisance. It is a concentration of elements that are responsible for the formation of stars in our galaxy and throughout the universe.</p><p>These opaque, dark knots of gas and dust are called Bok globules, and they are absorbing light in the center of the nearby emission nebula and star-forming region, NGC 281. The globules are named after astronomer Bart Bok, who proposed their existence in the 1940's.</p> 114 | 115 | 116 | 117 | 118 | NGC 281 119 | bok globules 120 | 121 | 122 | 129 | 130 | 131 | http://www.spacetelescope.org/copyright.html 132 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /pyavm/tests/data/sig05-003a.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 1 6 | 2700 7 | 2700 8 | 2 9 | 3 10 | 300/1 11 | 300/1 12 | 2 13 | 14 | 15 | 8 16 | 8 17 | 8 18 | 19 | 20 | 21 | 23 | 2005-11-18T09:49:26-08:00 24 | 2008-04-10T10:17:15-07:00 25 | 0 26 | 1 27 | 2007-08-28T17:41:21-07:00 28 | Adobe Photoshop CS2 Macintosh 29 | 30 | 32 | adobe:docid:photoshop:9b89b7b4-d064-11d9-9f55-8b6c55130657 33 | uuid:553DFCD9571F11DCA41DAD3DC3FD194D 34 | 35 | 37 | image/tiff 38 | 39 | 40 | Spitzer Space Telescope 41 | 42 | 43 | 44 | 45 | Crab Nebula 46 | M1 47 | Messier 1 48 | NGC 1952 49 | 50 | 51 | 52 | 53 | http://www.spitzer.caltech.edu/Media/mediaimages/copyright.shtml 54 | 55 | 56 | 57 | 58 | The Crab Nebula is the shattered remnant of a massive star that ended its life in a massive supernova explosion. Nearly a thousand years old, the supernova was noted in the constellation of Taurus by Chinese astronomers in the year 1054 AD.

This view of the supernova remnant obtained by the Spitzer Space Telescope shows the infrared view of this complex object. The blue-white region traces the cloud of energetic electrons trapped within the star's magnetic field, emitting so-called "synchrotron" radiation. The red features follow the well-known filamentary structures that permeate this nebula. Though they are known to contain hot gasses, their exact nature is still a mystery that astronomers are examining.

The energetic cloud of electrons are driven by a rapidly rotating neutron star, or pulsar, at its core. The nebula is about 6,500 light-years away from the Earth, and is 5 light-years across.

This false-color image presents images from Spitzer's Infrared Array Camera (IRAC) at 3.6 (blue), 4.5 (green), and 8.0 (red) microns. 59 | 60 | 61 | 62 | 63 | Crab Nebula Supernova Remnant (IRAC Image) 64 | 65 | 66 | 67 | 69 | 2700 70 | 2700 71 | -1 72 | 0221 73 | 74 | 76 | Spitzer Space Telescope 77 | NASA/JPL-Caltech/L. Allen (Harva 78 | The Crab Nebula is the shattered remnant of a massive star that ended its life in a massive supernova explosion. Nearly a thousand years old, the supernova was noted in the constellation of Taurus by Chinese astronomers in the year 1054 AD. 79 | 2005-06-01 80 | E1DA90E6DDE320BA0BEC1052C2ACD65A 81 | 3 82 | 83 | 84 | 86 | http://www.spitzer.caltech.edu 87 | 1.1 88 | Good 89 | http://gallery.spitzer.caltech.edu/Imagegallery/image.php?image_name=sig05-003 90 | Observation 91 | sig05-003a 92 | FOV: 8.83 x 8.83 arcminutes; Ref coordinate: 5h34m23.8s 22d3m55.02s; derived from astrometry.net file sig05-003a.fits 93 | 1.275969547776 94 | Full 95 | TAN 96 | 2000.0 97 | ICRS 98 | Spitzer Science Center 99 | 100 | 101 | B.4.1.4. 102 | 103 | 104 | 105 | 106 | Spitzer 107 | Spitzer 108 | Spitzer 109 | 110 | 111 | 112 | 113 | IRAC 114 | IRAC 115 | IRAC 116 | 117 | 118 | 119 | 120 | Blue 121 | Green 122 | Red 123 | 124 | 125 | 126 | 127 | Infrared 128 | Infrared 129 | Infrared 130 | 131 | 132 | 133 | 134 | Near-Infrared 135 | Near-Infrared 136 | 137 | 138 | 139 | 140 | 3600 141 | 4500 142 | 8000 143 | 144 | 145 | 146 | 147 | -5.4509008380871E-05 148 | 5.4509008380871E-05 149 | 150 | 151 | 152 | 153 | 1852.07870483 154 | 2238.462005615 155 | 156 | 157 | 158 | 159 | 2700 160 | 2700 161 | 162 | 163 | 164 | 165 | 83.5991627865 166 | 22.0652841412 167 | 168 | 169 | 170 | 172 | True 173 | 174 | 176 | Print 177 | 178 | 180 | 181 | 1200 E. California Blvd. 182 | Pasadena 183 | CA 184 | 91125 185 | USA 186 | http://www.spitzer.caltech.edu 187 | 188 | 189 | 191 | 192 | 193 | Public Domain 194 | 195 | 196 | 197 | 198 | -------------------------------------------------------------------------------- /pyavm/tests/data/sig05-004a.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 1 6 | 2700 7 | 2700 8 | 2 9 | 3 10 | 300/1 11 | 300/1 12 | 2 13 | 14 | 15 | 8 16 | 8 17 | 8 18 | 19 | 20 | 21 | 23 | 2007-07-19T12:00:56-07:00 24 | 2008-04-10T10:17:23-07:00 25 | 0 26 | 1 27 | 2007-08-28T17:41:23-07:00 28 | Adobe Photoshop CS2 Macintosh 29 | 30 | 32 | adobe:docid:photoshop:616f0cef-d063-11d9-9f55-8b6c55130657 33 | uuid:388CED7B572011DCA41DAD3DC3FD194D 34 | 35 | 37 | image/tiff 38 | 39 | 40 | Spitzer Space Telescope 41 | 42 | 43 | 44 | 45 | Crab Nebula 46 | M1 47 | Messier 1 48 | NGC 1952 49 | 50 | 51 | 52 | 53 | http://www.spitzer.caltech.edu/Media/mediaimages/copyright.shtml 54 | 55 | 56 | 57 | 58 | The Crab Nebula is the shattered remnant of a massive star that ended its life in a massive supernova explosion. Nearly a thousand years old, the supernova was noted in the constellation of Taurus by Chinese astronomers in the year 1054 AD.

This view of the supernova remnant obtained by the Spitzer Space Telescope shows the infrared view of this complex object. The blue region traces the cloud of energetic electrons trapped within the star's magnetic field, emitting so-called "synchrotron" radiation. The yellow-red features follow the well-known filamentary structures that permeate this nebula. Though they are known to contain hot gasses, their exact nature is still a mystery that astronomers are examining.

The energetic cloud of electrons are driven by a rapidly rotating neutron star, or pulsar, at its core. The nebula is about 6,500 light-years away from the Earth, and is 5 light-years across.

This false-color image presents images from Spitzer's Infrared Array Camera (IRAC) and Multiband Imaging Photometer (MIPS) at 3.6 (blue), 8.0 (green), 24 (red) microns. 59 | 60 | 61 | 62 | 63 | Crab Nebula Supernova Remnant (IRAC-MIPS Image) 64 | 65 | 66 | 67 | 69 | 2700 70 | 2700 71 | -1 72 | 0221 73 | 74 | 76 | Spitzer Space Telescope 77 | NASA/JPL-Caltech/L. Allen (Harva 78 | The Crab Nebula is the shattered remnant of a massive star that ended its life in a massive supernova explosion. Nearly a thousand years old, the supernova was noted in the constellation of Taurus by Chinese astronomers in the year 1054 AD. 79 | 2005-06-11 80 | 7000351DA83670EEA7BC1F8D68B34311 81 | 3 82 | 83 | 84 | 86 | http://www.spitzer.caltech.edu 87 | 1.1 88 | Good 89 | http://gallery.spitzer.caltech.edu/Imagegallery/image.php?image_name=sig05-004 90 | sig05-004a 91 | Observation 92 | FOV: 8.82 x 8.82 arcminutes; Ref coordinate: 5h34m23.8s 22d3m55.02s; derived from astrometry.net file sig05-004a.fits 93 | 1.3278773198075 94 | Full 95 | TAN 96 | 2000.0 97 | ICRS 98 | Spitzer Science Center 99 | 100 | 101 | B.4.1.4. 102 | 103 | 104 | 105 | 106 | Spitzer 107 | Spitzer 108 | Spitzer 109 | 110 | 111 | 112 | 113 | IRAC 114 | IRAC 115 | MIPS 116 | 117 | 118 | 119 | 120 | Blue 121 | Green 122 | Red 123 | 124 | 125 | 126 | 127 | Infrared 128 | Infrared 129 | Infrared 130 | 131 | 132 | 133 | 134 | Near-Infrared 135 | Near-Infrared 136 | Mid-Infrared 137 | 138 | 139 | 140 | 141 | 3600 142 | 8000 143 | 2400 144 | 145 | 146 | 147 | 148 | -5.4454429540852E-05 149 | 5.4454429540852E-05 150 | 151 | 152 | 153 | 154 | 1851.74807739 155 | 2237.110237122 156 | 157 | 158 | 159 | 160 | 2700 161 | 2700 162 | 163 | 164 | 165 | 166 | 83.5991627865 167 | 22.0652841412 168 | 169 | 170 | 171 | 173 | True 174 | 175 | 177 | Print 178 | 179 | 181 | 182 | 1200 E. California Blvd. 183 | Pasadena 184 | CA 185 | 91125 186 | USA 187 | http://www.spitzer.caltech.edu 188 | 189 | 190 | 192 | 193 | 194 | Public Domain 195 | 196 | 197 | 198 | 199 | -------------------------------------------------------------------------------- /pyavm/tests/data/sig05-021-alpha.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 21 | 22 | 23 | Public Domain 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /pyavm/tests/data/sig06-013.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | image/tiff 6 | 7 | 8 | Spitzer Space Telescope 9 | 10 | 11 | 12 | 13 | Witch Head Nebula 14 | 15 | 16 | 17 | 18 | http://www.spitzer.caltech.edu/Media/mediaimages/copyright.shtml 19 | 20 | 21 | 22 | 23 | Eight hundred light-years away in the Orion constellation, a gigantic murky cloud called the "Witch Head Nebula" is teeming with dust-obscured newborn stars waiting to be uncovered. 

In this image, the super sensitive infrared eyes of NASA's Spitzer Space Telescope reveals 12 new baby stars in a small portion of the cloud commonly referred to as the Witch Head's "pointy chin." 

Observations are currently underway to search for infant stars in the rest of the cloud. 

The image is a four-color composite where blue represents 3.6 microns, green depicts 4.5 microns, yellow is 5.8 microns, and red is 8.0 microns. 24 | 25 | 26 | 27 | 28 | Baby Stars Brewing in the Witch Head Nebula 29 | 30 | 31 | 32 | 34 | 2005-11-18T09:49:26-08:00 35 | 2008-04-10T10:22:01-07:00 36 | 0 37 | 1 38 | 2007-08-02T11:23:40-07:00 39 | Adobe Photoshop CS2 Macintosh 40 | 41 | 44 | uuid:1693733F427D11DCB9198828B70AAAB6 45 | uuid:16937340427D11DCB9198828B70AAAB6 46 | 47 | uuid:C668EC02E8DE11DAB87998868466F49A 48 | uuid:C668EC01E8DE11DAB87998868466F49A 49 | 50 | 51 | 53 | 1 54 | 640 55 | 530 56 | 2 57 | 3 58 | 300/1 59 | 300/1 60 | 2 61 | 62 | 63 | 8 64 | 8 65 | 8 66 | 67 | 68 | 69 | 71 | 640 72 | 530 73 | -1 74 | 0221 75 | 76 | 78 | Spitzer Space Telescope 79 | NASA/JPL-Caltech/L. Allen (Harva 80 | Eight hundred light-years away in the Orion constellation, a gigantic murky cloud called the "Witch Head Nebula" is teeming with dust-obscured newborn stars waiting to be uncovered. 81 | 2006-05-19 82 | CD4DB0EB31BFD2507A594BBC13B42C6C 83 | 3 84 | 85 | 86 | 88 | http://www.spitzer.caltech.edu 89 | 1.1 90 | Good 91 | http://gallery.spitzer.caltech.edu/Imagegallery/image.php?image_name=sig06-013 92 | Observation 93 | sig06-013 94 | ICRS 95 | 2000.0 96 | TAN 97 | Full 98 | 1.1120872384585 99 | FOV: 18.4 x 15.24 arcminutes; Ref coordinate: 5h7m50.25s -6d21m31.55s; derived from astrometry.net file sig06-013.fits 100 | Spitzer Science Center 101 | 102 | 103 | B.4.1.2. 104 | 105 | 106 | 107 | 108 | Spitzer 109 | Spitzer 110 | Spitzer 111 | Spitzer 112 | 113 | 114 | 115 | 116 | IRAC 117 | IRAC 118 | IRAC 119 | IRAC 120 | 121 | 122 | 123 | 124 | Blue 125 | Green 126 | Orange 127 | Red 128 | 129 | 130 | 131 | 132 | Infrared 133 | Infrared 134 | Infrared 135 | Infrared 136 | 137 | 138 | 139 | 140 | Near-Infrared 141 | Near-Infrared 142 | Near-Infrared 143 | Near-Infrared 144 | 145 | 146 | 147 | 148 | 3600 149 | 4500 150 | 5800 151 | 8000 152 | 153 | 154 | 155 | 156 | 76.9593794408 157 | -6.35876297559 158 | 159 | 160 | 161 | 162 | 640 163 | 530 164 | 165 | 166 | 167 | 168 | 181.103597164 169 | 109.291885376 170 | 171 | 172 | 173 | 174 | -0.00047928461208012 175 | 0.00047928461208012 176 | 177 | 178 | 179 | 181 | True 182 | 183 | 185 | Print 186 | 187 | 189 | 190 | 1200 E. California Blvd. 191 | Pasadena 192 | CA 193 | 91125 194 | USA 195 | http://www.spitzer.caltech.edu 196 | 197 | 198 | 200 | 201 | 202 | Public Domain 203 | 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /pyavm/tests/data/sig07-009.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | image/tiff 6 | 7 | 8 | Spitzer Space Telescope 9 | 10 | 11 | 12 | 13 | M81 14 | NGC 3031 15 | Bode's Galaxy 16 | 17 | 18 | 19 | 20 | http://www.spitzer.caltech.edu/Media/mediaimages/copyright.shtml 21 | 22 | 23 | 24 | 25 | This beautiful galaxy is tilted at an oblique angle on to our line of sight, giving a "birds-eye view" of the spiral structure. The galaxy is similar to our Milky Way, but our favorable view provides a better picture of the typical architecture of spiral galaxies.

M81 may be undergoing a surge of star formation along the spiral arms due to a close encounter it may have had with its nearby spiral galaxy NGC 3077 and a nearby starburst galaxy (M82) about 300 million years ago.

M81 is one of the brightest galaxies that can be seen from the Earth. It is high in the northern sky in the circumpolar constellation Ursa Major, the Great Bear. At an apparent magnitude of 6.8 it is just at the limit of naked-eye visibility. The galaxy's angular size is about the same as that of the Full Moon.

This image combines data from the Hubble Space Telescope, the Spitzer Space Telescope, and the Galaxy Evolution Explorer (GALEX) missions. The GALEX ultraviolet data were from the far-UV portion of the spectrum (135 to 175 nanometers). The Spitzer infrared data were taken with the IRAC 4 detector (8 microns). The Hubble data were taken at the blue portion of the spectrum. 26 | 27 | 28 | 29 | 30 | Multiwavelength M81 31 | 32 | 33 | 34 | 36 | 2005-11-18T09:49:26-08:00 37 | 2008-04-10T10:22:54-07:00 38 | 0 39 | 1 40 | 2007-08-03T12:39:51-07:00 41 | Adobe Photoshop CS2 Macintosh 42 | 43 | 46 | uuid:3A31EE60103311DCB41FDE12EA9BA875 47 | uuid:F46426A2435011DC9555DB2FFED8D008 48 | 49 | uuid:6492F8880A1811DCA13AE9385EA31A1E 50 | uuid:BAA200280A1511DCA13AE9385EA31A1E 51 | 52 | 53 | 55 | 1 56 | 3180 57 | 2456 58 | 2 59 | 3 60 | 300/1 61 | 300/1 62 | 2 63 | 64 | 65 | 8 66 | 8 67 | 8 68 | 69 | 70 | 71 | 73 | 3180 74 | 2456 75 | -1 76 | 0221 77 | 78 | 80 | Spitzer Space Telescope 81 | Hubble data: NASA, ESA, and A. Z 82 | This beautiful galaxy is tilted at an oblique angle on to our line of sight, giving a "birds-eye view" of the spiral structure. The galaxy is similar to our Milky Way, but our favorable view provides a better picture of the typical architecture of spiral galaxies. 83 | 2007-05-30 84 | C3681B1AD9B5E3C971B5F23EA518CB91 85 | 3 86 | 87 | 88 | 90 | http://www.spitzer.caltech.edu 91 | 1.1 92 | Good 93 | http://gallery.spitzer.caltech.edu/Imagegallery/image.php?image_name=sig07-009 94 | Observation 95 | sig07-009 96 | ICRS 97 | 2000.0 98 | TAN 99 | Full 100 | 89.508107562815 101 | FOV: 19.85 x 15.33 arcminutes; Ref coordinate: 9h56m43.3s 69d3m50.47s; derived from astrometry.net file sig07-009.fits 102 | Spitzer Science Center 103 | 104 | 105 | C.5.1.1. 106 | 107 | 108 | 109 | 110 | 149.180437303 111 | 69.0640182609 112 | 113 | 114 | 115 | 116 | 3180 117 | 2456 118 | 119 | 120 | 121 | 122 | 1649.4630127 123 | 242.20446777 124 | 125 | 126 | 127 | 128 | -0.00010401480269299 129 | 0.00010401480269299 130 | 131 | 132 | 133 | 135 | True 136 | 137 | 139 | Print 140 | 141 | 143 | 144 | 1200 E. California Blvd. 145 | Pasadena 146 | CA 147 | 91125 148 | USA 149 | http://www.spitzer.caltech.edu 150 | 151 | 152 | 154 | 155 | 156 | Public Domain 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /pyavm/tests/data/ssc2003-06b2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 1 6 | 869 7 | 911 8 | 0221 9 | 10 | 12 | 1 13 | 869 14 | 911 15 | 2 16 | 3 17 | 300/1 18 | 300/1 19 | 2 20 | 21 | 22 | 8 23 | 8 24 | 8 25 | 26 | 27 | 28 | 30 | 2007-07-16T14:38:31-07:00 31 | 2007-07-16T14:38:31-07:00 32 | 2008-04-10T10:23:29-07:00 33 | Adobe Photoshop CS2 Macintosh 34 | 0 35 | 1 36 | 37 | 40 | uuid:8E5895B6353C11DC9AC3BE10008F35B5 41 | uuid:8E5895B7353C11DC9AC3BE10008F35B5 42 | 43 | adobe:docid:photoshop:0d29ea27-28b8-11d8-a38b-afa5f7cb3379 44 | adobe:docid:photoshop:0d29ea27-28b8-11d8-a38b-afa5f7cb3379 45 | 46 | 47 | 49 | image/tiff 50 | 51 | 52 | Spitzer Space Telescope 53 | 54 | 55 | 56 | 57 | Elephant's Trunk Nebula 58 | 59 | 60 | 61 | 62 | http://www.spitzer.caltech.edu/Media/mediaimages/copyright.shtml 63 | 64 | 65 | 66 | 67 | NASA's Spitzer Space Telescope has captured a glowing stellar nursery within a dark globule that is opaque at visible light. These new images pierce through the obscuration to reveal the birth of new protostars, or embryonic stars, and young stars never before seen.

The Elephant's Trunk Nebula is an elongated dark globule within the emission nebula IC 1396 in the constellation of Cepheus. Located at a distance of 2,450 light-years, the globule is a condensation of dense gas that is barely surviving the strong ionizing radiation from a nearby massive star. The globule is being compressed by the surrounding ionized gas.

The large composite image on the left is a product of combining data from the observatory's multiband imaging photometer and the infrared array camera. The thermal emission at 24 microns measured by the photometer (red) is combined with near-infrared emission from the camera at 3.6/4.5 microns (blue) and from 5.8/8.0 microns (green). The colors of the diffuse emission and filaments vary, and are a combination of molecular hydrogen (which tends to be green) and polycyclic aromatic hydrocarbon (brown) emissions.

Within the globule, a half dozen newly discovered protostars are easily discernible as the bright red-tinted objects, mostly along the southern rim of the globule. These were previously undetected at visible wavelengths due to obscuration by the thick cloud ('globule body') and by dust surrounding the newly forming stars. The newborn stars form in the dense gas because of compression by the wind and radiation from a nearby massive star (located outside the field of view to the left). The winds from this unseen star are also responsible for producing the spectacular filamentary appearance of the globule itself, which resembles that of a flying dragon.

The Spitzer Space Telescope also sees many newly discovered young stars, often enshrouded in dust, which may be starting the nuclear fusion that defines a star. These young stars 68 | 69 | 70 | 71 | 72 | Dark Globule in IC 1396 73 | 74 | 75 | 76 | 78 | Spitzer Space Telescope 79 | NASA/JPL-Caltech/W. Reach (Calte 80 | NASA's Spitzer Space Telescope has captured a glowing stellar nursery within a dark globule that is opaque at visible light. These new images pierce through the obscuration to reveal the birth of new protostars, or embryonic stars, and young stars never before seen. 81 | 2003-11-24 82 | 68255AA522715708874CB45F53017CD7 83 | 3 84 | sRGB IEC61966-2.1 85 | 86 | 88 | http://www.spitzer.caltech.edu 89 | 1.1 90 | Good 91 | http://gallery.spitzer.caltech.edu/Imagegallery/image.php?image_name=ssc2003-06b 92 | Observation 93 | ssc2003-06b2 94 | FOV: 17.52 x 18.37 arcminutes; Ref coordinate: 21h36m0.52s 57d20m27.97s; derived from astrometry.net file ssc2003-06b1.fits 95 | 14.049462045345 96 | Full 97 | TAN 98 | 2000.0 99 | ICRS 100 | Spitzer Science Center 101 | 102 | 103 | B.4.1.2. 104 | 105 | 106 | 107 | 108 | Spitzer 109 | 110 | 111 | 112 | 113 | MIPS 114 | 115 | 116 | 117 | 118 | Red 119 | 120 | 121 | 122 | 123 | Infrared 124 | 125 | 126 | 127 | 128 | Mid-Infrared 129 | 130 | 131 | 132 | 133 | 24000 134 | 135 | 136 | 137 | 138 | -0.00033605382133014 139 | 0.00033605382133014 140 | 141 | 142 | 143 | 144 | 560.148353577 145 | 114.534042358 146 | 147 | 148 | 149 | 150 | 869 151 | 911 152 | 153 | 154 | 155 | 156 | 324.002167372 157 | 57.3411033796 158 | 159 | 160 | 161 | 163 | True 164 | 165 | 167 | Print 168 | 169 | 171 | 172 | 1200 E. California Blvd. 173 | Pasadena 174 | CA 175 | 91125 176 | USA 177 | http://www.spitzer.caltech.edu 178 | 179 | 180 | 182 | 183 | 184 | Public Domain 185 | 186 | 187 | 188 | 189 | -------------------------------------------------------------------------------- /pyavm/tests/data/ssc2003-06d3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 1 6 | 1158 7 | 878 8 | 0221 9 | 10 | 12 | 1 13 | 1158 14 | 878 15 | 2 16 | 3 17 | 300/1 18 | 300/1 19 | 2 20 | 21 | 22 | 8 23 | 8 24 | 8 25 | 26 | 27 | 28 | 30 | 2007-07-12T11:14:20-07:00 31 | 2008-04-10T10:23:58-07:00 32 | 0 33 | 1 34 | 2007-09-11T15:34:13-07:00 35 | Adobe Photoshop CS2 Macintosh 36 | 37 | 40 | uuid:C0A0560831ED11DCBF77DF7D603504A9 41 | uuid:C08C92FE620E11DC84DCAB9C2D22EBB1 42 | 43 | adobe:docid:photoshop:c73bef3f-20cd-11d8-8f48-8714036358bc 44 | adobe:docid:photoshop:c73bef3f-20cd-11d8-8f48-8714036358bc 45 | 46 | 47 | 49 | image/tiff 50 | 51 | 52 | Spitzer Space Telescope 53 | 54 | 55 | 56 | 57 | Messier 81 58 | M81 59 | Bode's Galaxy 60 | NGC3031 61 | 62 | 63 | 64 | 65 | http://www.spitzer.caltech.edu/Media/mediaimages/copyright.shtml 66 | 67 | 68 | 69 | 70 | The magnificent spiral arms of the nearby galaxy Messier 81 are highlighted in this image from NASA's Spitzer Space Telescope. Located in the northern constellation of Ursa Major (which also includes the Big Dipper), this galaxy is easily visible through binoculars or a small telescope. M81 is located at a distance of 12 million light-years.

The 24-micron multiband imaging photometer image shows emission from warm dust heated by the most luminous young stars. The infrared-bright clumpy knots within the spiral arms show where massive stars are being born in giant H II (ionized hydrogen) regions. Studying the locations of these star forming regions with respect to the overall mass distribution and other constituents of the galaxy (e.g., gas) will help identify the conditions and processes needed for star formation. 71 | 72 | 73 | 74 | 75 | Messier 81 76 | 77 | 78 | 79 | 81 | Spitzer Space Telescope 82 | NASA/JPL-Caltech/K. Gordon (Univ 83 | The magnificent spiral arms of the nearby galaxy Messier 81 are highlighted in this image from NASA's Spitzer Space Telescope. Located in the northern constellation of Ursa Major (which also includes the Big Dipper), this galaxy is easily visible through binoculars or a small telescope. M81 is located at a distance of 12 million light-years. 84 | CBB59594E703E633C2A6A7F51609C300 85 | 3 86 | sRGB IEC61966-2.1 87 | 88 | 90 | http://www.spitzer.caltech.edu 91 | 1.1 92 | Good 93 | http://gallery.spitzer.caltech.edu/Imagegallery/image.php?image_name=ssc2003-06d 94 | Observation 95 | ssc2003-06d3 96 | ICRS 97 | 2000.0 98 | TAN 99 | Full 100 | -91.091605547339 101 | FOV: 23.54 x 17.85 arcminutes; Ref coordinate: 9h55m3.8s 69d11m23.36s; derived from astrometry.net file ssc2003-06d1.fits 102 | Spitzer Science Center 103 | 104 | 105 | C.5.1.1. 106 | 107 | 108 | 109 | 110 | Spitzer 111 | 112 | 113 | 114 | 115 | MIPS 116 | 117 | 118 | 119 | 120 | Red 121 | Pseudocolor 122 | 123 | 124 | 125 | 126 | Infrared 127 | 128 | 129 | 130 | 131 | Near-Infrared 132 | 133 | 134 | 135 | 136 | 2400 137 | 138 | 139 | 140 | 141 | 148.765826253 142 | 69.1898220838 143 | 144 | 145 | 146 | 147 | 1158 148 | 878 149 | 150 | 151 | 152 | 153 | 947.545150756 154 | 298.468009949 155 | 156 | 157 | 158 | 159 | -0.00033880733719918 160 | 0.00033880733719918 161 | 162 | 163 | 164 | 166 | True 167 | 168 | 170 | Print 171 | 172 | 174 | 175 | 1200 E. California Blvd. 176 | Pasadena 177 | CA 178 | 91125 179 | USA 180 | http://www.spitzer.caltech.edu 181 | 182 | 183 | 185 | 186 | 187 | Public Domain 188 | 189 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /pyavm/tests/data/ssc2003-06d4.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 1 6 | 1158 7 | 878 8 | 0221 9 | 10 | 12 | 1 13 | 1158 14 | 878 15 | 2 16 | 3 17 | 300/1 18 | 300/1 19 | 2 20 | 21 | 22 | 8 23 | 8 24 | 8 25 | 26 | 27 | 28 | 30 | 2007-07-12T11:24:44-07:00 31 | 2008-04-10T10:24:07-07:00 32 | 0 33 | 1 34 | 2007-07-12T11:24:44-07:00 35 | Adobe Photoshop CS2 Macintosh 36 | 37 | 40 | uuid:5F39E27631FB11DCBF77DF7D603504A9 41 | uuid:5F39E27731FB11DCBF77DF7D603504A9 42 | 43 | adobe:docid:photoshop:c73bef3f-20cd-11d8-8f48-8714036358bc 44 | adobe:docid:photoshop:c73bef3f-20cd-11d8-8f48-8714036358bc 45 | 46 | 47 | 49 | image/tiff 50 | 51 | 52 | Spitzer Space Telescope 53 | 54 | 55 | 56 | 57 | M 81 58 | Messier 81 59 | NGC 3031 60 | Bode's Galaxy 61 | 62 | 63 | 64 | 65 | http://www.spitzer.caltech.edu/Media/mediaimages/copyright.shtml 66 | 67 | 68 | 69 | 70 | The magnificent spiral arms of the nearby galaxy Messier 81 are highlighted in this image from NASA's Spitzer Space Telescope. Located in the northern constellation of Ursa Major (which also includes the Big Dipper), this galaxy is easily visible through binoculars or a small telescope. M81 is located at a distance of 12 million light-years.

As one moves to longer wavelengths, the spiral arms become the dominant feature of the galaxy. The 8-micron emission is dominated by infrared light radiated by hot dust that has been heated by nearby luminous stars. Dust in the galaxy is bathed by ultraviolet and visible light from nearby stars. Upon absorbing an ultraviolet or visible-light photon, a dust grain is heated and re-emits the energy at longer infrared wavelengths. The dust particles are composed of silicates (chemically similar to beach sand), carbonaceous grains and polycyclic aromatic hydrocarbons and trace the gas distribution in the galaxy. The well-mixed gas (which is best detected at radio wavelengths) and dust provide a reservoir of raw materials for future star formation. 71 | 72 | 73 | 74 | 75 | Messier 81 76 | 77 | 78 | 79 | 81 | Spitzer Space Telescope 82 | NASA/JPL-Caltech/K. Gordon (Univ 83 | The magnificent spiral arms of the nearby galaxy Messier 81 are highlighted in this image from NASA's Spitzer Space Telescope. Located in the northern constellation of Ursa Major (which also includes the Big Dipper), this galaxy is easily visible through binoculars or a small telescope. M81 is located at a distance of 12 million light-years. 84 | 63D5E20E678F28C90AC8B45680569642 85 | 3 86 | sRGB IEC61966-2.1 87 | 88 | 90 | http://www.spitzer.caltech.edu 91 | 1.1 92 | Good 93 | http://gallery.spitzer.caltech.edu/Imagegallery/image.php?image_name=ssc2003-06d 94 | Observation 95 | ssc2003-06d4 96 | ICRS 97 | 2000.0 98 | TAN 99 | Full 100 | -91.091605547339 101 | FOV: 23.54 x 17.85 arcminutes; Ref coordinate: 9h55m3.8s 69d11m23.36s; derived from astrometry.net file ssc2003-06d1.fits 102 | Spitzer Science Center 103 | 104 | 105 | C.5.1.1. 106 | 107 | 108 | 109 | 110 | Spitzer 111 | 112 | 113 | 114 | 115 | IRAC 116 | 117 | 118 | 119 | 120 | Green 121 | Pseudocolor 122 | 123 | 124 | 125 | 126 | Infrared 127 | 128 | 129 | 130 | 131 | Mid-Infrared 132 | 133 | 134 | 135 | 136 | 8000 137 | 138 | 139 | 140 | 141 | 148.765826253 142 | 69.1898220838 143 | 144 | 145 | 146 | 147 | 1158 148 | 878 149 | 150 | 151 | 152 | 153 | 947.545150756 154 | 298.468009949 155 | 156 | 157 | 158 | 159 | -0.00033880733719918 160 | 0.00033880733719918 161 | 162 | 163 | 164 | 166 | True 167 | 168 | 170 | Print 171 | 172 | 174 | 175 | 1200 E. California Blvd. 176 | Pasadena 177 | CA 178 | 91125 179 | USA 180 | http://www.spitzer.caltech.edu 181 | 182 | 183 | 185 | 186 | 187 | Public Domain 188 | 189 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /pyavm/tests/data/ssc2003-06d5.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 1 6 | 1158 7 | 878 8 | 0221 9 | 10 | 12 | 1 13 | 1158 14 | 878 15 | 2 16 | 3 17 | 300/1 18 | 300/1 19 | 2 20 | 21 | 22 | 8 23 | 8 24 | 8 25 | 26 | 27 | 28 | 30 | 2007-07-12T13:51:57-07:00 31 | 2008-04-10T10:24:15-07:00 32 | 0 33 | 1 34 | 2007-07-12T13:51:57-07:00 35 | Adobe Photoshop CS2 Macintosh 36 | 37 | 40 | uuid:4B4AC5A8321111DCBF77DF7D603504A9 41 | uuid:4B4AC5A9321111DCBF77DF7D603504A9 42 | 43 | adobe:docid:photoshop:c73bef3f-20cd-11d8-8f48-8714036358bc 44 | adobe:docid:photoshop:c73bef3f-20cd-11d8-8f48-8714036358bc 45 | 46 | 47 | 49 | image/tiff 50 | 51 | 52 | Spitzer Space Telescope 53 | 54 | 55 | 56 | 57 | M81 58 | Messier81 59 | NGC 3031 60 | Bode's Galaxy 61 | 62 | 63 | 64 | 65 | http://www.spitzer.caltech.edu/Media/mediaimages/copyright.shtml 66 | 67 | 68 | 69 | 70 | The magnificent spiral arms of the nearby galaxy Messier 81 are highlighted in this image from NASA's Spitzer Space Telescope. Located in the northern constellation of Ursa Major (which also includes the Big Dipper), this galaxy is easily visible through binoculars or a small telescope. M81 is located at a distance of 12 million light-years.

 71 | 72 | 73 | 74 | 75 | Messier 81 76 | 77 | 78 | 79 | 81 | Spitzer Space Telescope 82 | NASA/JPL-Caltech/K. Gordon (Univ 83 | The magnificent spiral arms of the nearby galaxy Messier 81 are highlighted in this image from NASA's Spitzer Space Telescope. Located in the northern constellation of Ursa Major (which also includes the Big Dipper), this galaxy is easily visible through binoculars or a small telescope. M81 is located at a distance of 12 million light-years. 84 | B2112FD72295A88246CB4B079628E605 85 | 3 86 | sRGB IEC61966-2.1 87 | 88 | 90 | http://www.spitzer.caltech.edu 91 | 1.1 92 | Good 93 | http://gallery.spitzer.caltech.edu/Imagegallery/image.php?image_name=ssc2003-06d 94 | ssc2003-06d5 95 | ICRS 96 | 2000.0 97 | TAN 98 | Full 99 | -91.091605547339 100 | FOV: 23.54 x 17.85 arcminutes; Ref coordinate: 9h55m3.8s 69d11m23.36s; derived from astrometry.net file ssc2003-06d1.fits 101 | Spitzer Science Center 102 | 103 | 104 | C.5.1.1. 105 | 106 | 107 | 108 | 109 | Spitzer 110 | 111 | 112 | 113 | 114 | IRAC 115 | 116 | 117 | 118 | 119 | Blue 120 | Pseudocolor 121 | 122 | 123 | 124 | 125 | Infrared 126 | 127 | 128 | 129 | 130 | Near-Infrared 131 | 132 | 133 | 134 | 135 | 3600 136 | 137 | 138 | 139 | 140 | 148.765826253 141 | 69.1898220838 142 | 143 | 144 | 145 | 146 | 1158 147 | 878 148 | 149 | 150 | 151 | 152 | 947.545150756 153 | 298.468009949 154 | 155 | 156 | 157 | 158 | -0.00033880733719918 159 | 0.00033880733719918 160 | 161 | 162 | 163 | 165 | True 166 | 167 | 169 | Print 170 | 171 | 173 | 174 | 1200 E. California Blvd. 175 | Pasadena 176 | CA 177 | 91125 178 | USA 179 | http://www.spitzer.caltech.edu 180 | 181 | 182 | 184 | 185 | 186 | Public Domain 187 | 188 | 189 | 190 | 191 | -------------------------------------------------------------------------------- /pyavm/tests/data/ssc2004-04a4.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -1 6 | 2262 7 | 1899 8 | 0221 9 | 10 | 12 | 1 13 | 2262 14 | 1899 15 | 2 16 | 3 17 | 300/1 18 | 300/1 19 | 2 20 | 21 | 22 | 8 23 | 8 24 | 8 25 | 26 | 27 | 28 | 30 | 2007-07-16T16:51:49-07:00 31 | 2008-04-10T10:24:53-07:00 32 | 0 33 | 1 34 | 2007-07-16T16:51:49-07:00 35 | Adobe Photoshop CS2 Macintosh 36 | 37 | 40 | uuid:283F9B74354F11DC9AC3BE10008F35B5 41 | uuid:283F9B75354F11DC9AC3BE10008F35B5 42 | 43 | adobe:docid:photoshop:206543bb-6ed2-11d8-b8fc-c19c7e679515 44 | adobe:docid:photoshop:206543bb-6ed2-11d8-b8fc-c19c7e679515 45 | 46 | 47 | 49 | image/tiff 50 | 51 | 52 | Spitzer Space Telescope 53 | 54 | 55 | 56 | 57 | Henize 206 58 | 59 | 60 | 61 | 62 | http://www.spitzer.caltech.edu/Media/mediaimages/copyright.shtml 63 | 64 | 65 | 66 | 67 | Within the Large Magellanic Cloud (LMC), a nearby and irregularly-shaped galaxy seen in the Southern Hemisphere, lies a star-forming region heavily obscured by interstellar dust. NASA's Spitzer Space Telescope has used its infrared eyes to poke through the cosmic veil to reveal a striking nebula where the entire lifecycle of stars is seen in splendid detail.

The LMC is a small satellite galaxy gravitationally bound to our own Milky Way. Yet the gravitational effects are tearing the companion to shreds in a long-playing drama of 'intergalactic cannibalism.' These disruptions lead to a recurring cycle of star birth and star death.

Astronomers are particularly interested in the LMC because its fractional content of heavy metals is two to five times lower than is seen in our solar neighborhood. [In this context, 'heavy elements' refer to those elements not present in the primordial universe. Such elements as carbon, oxygen and others are produced by nucleosynthesis and are ejected into the interstellar medium via mass loss by stars, including supernova explosions.] As such, the LMC provides a nearby cosmic laboratory that may resemble the distant universe in its chemical composition.

The primary Spitzer image, showing the wispy filamentary structure of Henize 206, is a four-color composite mosaic created by combining data from an infrared array camera (IRAC) at near-infrared wavelengths and the mid-infrared data from a multiband imaging photometer (MIPS). Blue represents invisible infrared light at wavelengths of 3.6 and 4.5 microns. Note that most of the stars in the field of view radiate primarily at these short infrared wavelengths. Cyan denotes emission at 5.8 microns, green depicts the 8.0 micron light, and red is used to trace the thermal emission from dust at 24 microns. The separate instrument images are included as insets to the main composite.

An inclined ring of emission dominates the central and upper regions of the image. This delineates 68 | 69 | 70 | 71 | 72 | Star Formation in Henize 206 73 | 74 | 75 | 76 | 78 | Spitzer Space Telescope 79 | NASA/JPL-Caltech/V. Gorjian (JPL 80 | Within the Large Magellanic Cloud (LMC), a nearby and irregularly-shaped galaxy seen in the Southern Hemisphere, lies a star-forming region heavily obscured by interstellar dust. NASA's Spitzer Space Telescope has used its infrared eyes to poke through the cosmic veil to reveal a striking nebula where the entire lifecycle of stars is seen in splendid detail. 81 | 2003-11-24 82 | EB03F747535F0AFF587F966E93A27F88 83 | 3 84 | 85 | 86 | 88 | http://www.spitzer.caltech.edu 89 | 1.1 90 | Good 91 | http://gallery.spitzer.caltech.edu/Imagegallery/image.php?image_name=ssc2004-04a 92 | Observation 93 | ssc2004-04a4 94 | ICRS 95 | 2000.0 96 | TAN 97 | Full 98 | 26.222441553073 99 | FOV: 23 x 19.31 arcminutes; Ref coordinate: 5h29m21.67s -71d14m53.07s; derived from astrometry.net file ssc2004-04a3.fits 100 | Spitzer Science Center 101 | 102 | 103 | C.4.1.2. 104 | 105 | 106 | 107 | 108 | Spitzer 109 | 110 | 111 | 112 | 113 | MIPS 114 | 115 | 116 | 117 | 118 | Red 119 | 120 | 121 | 122 | 123 | Infrared 124 | 125 | 126 | 127 | 128 | Mid-Infrared 129 | 130 | 131 | 132 | 133 | 24000 134 | 135 | 136 | 137 | 138 | 82.3403017571 139 | -71.248073755 140 | 141 | 142 | 143 | 144 | 2262 145 | 1899 146 | 147 | 148 | 149 | 150 | 2009.13531494 151 | 177.91699219 152 | 153 | 154 | 155 | 156 | -0.00016945135470942 157 | 0.00016945135470942 158 | 159 | 160 | 161 | 163 | True 164 | 165 | 167 | Print 168 | 169 | 171 | 172 | 1200 E. California Blvd. 173 | Pasadena 174 | CA 175 | 91125 176 | USA 177 | http://www.spitzer.caltech.edu 178 | 179 | 180 | 182 | 183 | 184 | Public Domain 185 | 186 | 187 | 188 | 189 | -------------------------------------------------------------------------------- /pyavm/tests/data/ssc2004-06a1-alpha.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 21 | 22 | 23 | Public Domain 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /pyavm/tests/data/ssc2004-06b1-alpha.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 21 | 22 | 23 | Public Domain 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /pyavm/tests/data/ssc2004-20a2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -1 6 | 479 7 | 479 8 | 0221 9 | 10 | 12 | 1 13 | 479 14 | 479 15 | 2 16 | 3 17 | 300/1 18 | 300/1 19 | 2 20 | 21 | 22 | 8 23 | 8 24 | 8 25 | 26 | 27 | 28 | 30 | 2007-07-18T12:47:26-07:00 31 | 2008-04-10T10:26:48-07:00 32 | 2007-07-18T12:47:26-07:00 33 | Adobe Photoshop CS2 Macintosh 34 | 35 | 38 | uuid:5995027436BF11DCA5008AF93A8B7B53 39 | uuid:5995027536BF11DCA5008AF93A8B7B53 40 | 41 | adobe:docid:photoshop:e0acbba8-3303-11d9-b89c-82225fe2a55b 42 | adobe:docid:photoshop:e0acbba8-3303-11d9-b89c-82225fe2a55b 43 | 44 | 45 | 47 | image/tiff 48 | 49 | 50 | L1014 51 | 52 | 53 | 54 | 55 | The "Cores to Disks" Spitzer Legacy team is using the two infrared cameras on NASA's Spitzer Space Telescope to search dense regions of interstellar molecular clouds (known as "cores") for evidence of star formation. Part of the study targeted a group of objects with no known stars to study the properties of such regions before any stars have formed. The first of these "starless cores" to be examined held a surprise: a source of infrared light appeared where none was expected.

The core is known as L1014, the 1,014th object in a list of dark, dusty "clouds" compiled by astronomer Beverly Lynds over 40 years ago. These have proved to be homes to a rich variety of molecules and are the birthplaces of stars and planets.

The Spitzer image is a 3.6 micron (blue), 8.0 micron (green) and 24.0 micron (red) composite image. The light seen in the infrared image originates from very different sources. The bright yellow object at the center of the image is the object detected in the "starless core". The red ring surrounding the object is an artifact of the reduced spatial resolution of the telescope at 24 microns.

At 3.6 microns the light comes mainly from the object at the heart of the core. At longer wavelengths, the light from the object becomes stronger, a signature that it is not a background star. Also in the longer wavelengths (8.0 to 24.0 microns), astronomers saw the glow from interstellar dust, glowing green to red in the Spitzer composite image. This dust consists mainly of a variety of carbon-based organic molecules known collectively as polycyclic aromatic hydrocarbons. The red color traces a cooler dust component.

No previous observations showed any hint of a source in L1014. For example, the visible light image is from the Digital Sky Survey and is a B-, R-, and I-band composite image (wavelengths ranging from 0.4 to 0.7 microns). The dark cloud in the center of the image is the core, completely opaque in the visible due to obscuration by dust. 56 | 57 | 58 | 59 | 60 | The Starless Core That Isn't 61 | 62 | 63 | 64 | 66 | The "Cores to Disks" Spitzer Legacy team is using the two infrared cameras on NASA's Spitzer Space Telescope to search dense regions of interstellar molecular clouds (known as "cores") for evidence of star formation. Part of the study targeted a group of objects with no known stars to study the properties of such regions before any stars have formed. The first of these "starless cores" to be examined held a surprise: a source of infrared light appeared where none was expected. 67 | NASA/JPL-Caltech/N. Evans (Univ. 68 | 2003-12-02 69 | C832CA065DEB4EB4943120235E78EADD 70 | 3 71 | 72 | Spitzer Space Telescope 73 | 74 | 76 | http://gallery.spitzer.caltech.edu/Imagegallery/image.php?image_name=ssc2004-20a 77 | Observation 78 | ssc2004-20a2 79 | 1.1 80 | ICRS 81 | 2000.0 82 | TAN 83 | Full 84 | -65.437373628521 85 | FOV: 4.9 x 4.9 arcminutes; Ref coordinate: 21h24m6.71s 49d58m11.54s; derived from astrometry.net file ssc2004-20a2.fits 86 | Spitzer Science Center 87 | 88 | 89 | C.3. 90 | 91 | 92 | 93 | 94 | Spitzer 95 | Spitzer 96 | Spitzer 97 | 98 | 99 | 100 | 101 | IRAC 102 | IRAC 103 | MIPS 104 | 105 | 106 | 107 | 108 | Blue 109 | Green 110 | Red 111 | 112 | 113 | 114 | 115 | Infrared 116 | Infrared 117 | Infrared 118 | 119 | 120 | 121 | 122 | Near-Infrared 123 | Near-Infrared 124 | Mid-Infrared 125 | 126 | 127 | 128 | 129 | 3600 130 | 8000 131 | 24000 132 | 133 | 134 | 135 | 136 | 321.027942926 137 | 49.9698717647 138 | 139 | 140 | 141 | 142 | 479 143 | 479 144 | 145 | 146 | 147 | 148 | 158.42910862 149 | 195.856796265 150 | 151 | 152 | 153 | 154 | -0.00017065088956606 155 | 0.00017065088956606 156 | 157 | 158 | 159 | 161 | True 162 | 163 | 165 | Print 166 | 167 | 169 | 170 | 1200 E. California Blvd. 171 | Pasadena 172 | CA 173 | 91125 174 | USA 175 | http://www.spitzer.caltech.edu 176 | 177 | 178 | 180 | 181 | 182 | Public Domain 183 | 184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /pyavm/tests/data/ssc2005-02a4.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 1 6 | 867 7 | 1412 8 | 0221 9 | 10 | 12 | 1 13 | 867 14 | 1412 15 | 2 16 | 3 17 | 300/1 18 | 300/1 19 | 2 20 | 21 | 22 | 8 23 | 8 24 | 8 25 | 26 | 27 | 28 | 30 | 2007-07-18T12:45:06-07:00 31 | 2008-04-10T10:27:06-07:00 32 | 0 33 | 1 34 | 2007-07-18T12:45:06-07:00 35 | Adobe Photoshop CS2 Macintosh 36 | 37 | 40 | uuid:607F30DB36BE11DCA5008AF93A8B7B53 41 | uuid:607F30DC36BE11DCA5008AF93A8B7B53 42 | 43 | adobe:docid:photoshop:b8f8298e-5b1d-11d9-86d5-ed29226d8143 44 | adobe:docid:photoshop:b8f8298e-5b1d-11d9-86d5-ed29226d8143 45 | 46 | 47 | 49 | image/tiff 50 | 51 | 52 | Spitzer Space Telescope 53 | 54 | 55 | 56 | 57 | Messier 20 58 | M20 59 | Trifid Nebula 60 | 61 | 62 | 63 | 64 | http://www.spitzer.caltech.edu/Media/mediaimages/copyright.shtml 65 | 66 | 67 | 68 | 69 | This image composite compares the well-known visible-light picture of the glowing Trifid Nebula (left panel) with infrared views from NASA's Spitzer Space Telescope (remaining three panels). The Trifid Nebula is a giant star-forming cloud of gas and dust located 5,400 light-years away in the constellation Sagittarius.

The false-color Spitzer images reveal a different side of the Trifid Nebula. Where dark lanes of dust are visible trisecting the nebula in the visible-light picture, bright regions of star-forming activity are seen in the Spitzer pictures. All together, Spitzer uncovered 30 massive embryonic stars and 120 smaller newborn stars throughout the Trifid Nebula, in both its dark lanes and luminous clouds. These stars are visible in all the Spitzer images, mainly as yellow or red spots. Embryonic stars are developing stars about to burst into existence. Ten of the 30 massive embryos discovered by Spitzer were found in four dark cores, or stellar "incubators," where stars are born. Astronomers using data from the Institute of Radioastronomy millimeter telescope in Spain had previously identified these cores but thought they were not quite ripe for stars. Spitzer's highly sensitive infrared eyes were able to penetrate all four cores to reveal rapidly growing embryos.

Astronomers can actually count the individual embryos tucked inside the cores by looking closely at the Spitzer image taken by its infrared array camera (top right). This instrument has the highest spatial resolution of Spitzer's imaging cameras. The Spitzer image from the multiband imaging photometer (bottom right), on the other hand, specializes in detecting cooler materials. Its view highlights the relatively cool core material falling onto the Trifid's growing embryos. The middle panel is a combination of Spitzer data from both of these instruments.

The embryos are thought to have been triggered by a massive "type O" star, which can be seen as a white spot at the center of the ne 70 | 71 | 72 | 73 | 74 | New Views of an Old Beauty 75 | 76 | 77 | 78 | 80 | Spitzer Space Telescope 81 | NASA/JPL-Caltech/J. Rho (SSC/Cal 82 | This image composite compares the well-known visible-light picture of the glowing Trifid Nebula (left panel) with infrared views from NASA's Spitzer Space Telescope (remaining three panels). The Trifid Nebula is a giant star-forming cloud of gas and dust located 5,400 light-years away in the constellation Sagittarius. 83 | 2005-01-12 84 | 132396282DA3BF74A8168EE549AC2318 85 | 3 86 | sRGB IEC61966-2.1 87 | 88 | 90 | http://www.spitzer.caltech.edu 91 | 1.1 92 | Good 93 | http://gallery.spitzer.caltech.edu/Imagegallery/image.php?image_name=ssc2005-02a 94 | Observation 95 | ssc2005-02a4 96 | ICRS 97 | 2000.0 98 | TAN 99 | Full 100 | 1.4729660891169 101 | FOV: 17.39 x 28.32 arcminutes; Ref coordinate: 18h2m1.06s -23d8m57.03s; derived from astrometry.net file ssc2005-02a2.fits 102 | Spitzer Science Center 103 | 104 | 105 | B.4.1.2. 106 | 107 | 108 | 109 | 110 | Spitzer 111 | 112 | 113 | 114 | 115 | MIPS 116 | 117 | 118 | 119 | 120 | Infrared 121 | 122 | 123 | 124 | 125 | Mid-Infrared 126 | 127 | 128 | 129 | 130 | 24000 131 | 132 | 133 | 134 | 135 | 270.504424479 136 | -23.149175556 137 | 138 | 139 | 140 | 141 | 867 142 | 1412 143 | 144 | 145 | 146 | 147 | 768.139846802 148 | 170.88540649 149 | 150 | 151 | 152 | 153 | -0.00033423983861413 154 | 0.00033423983861413 155 | 156 | 157 | 158 | 160 | True 161 | 162 | 164 | Print 165 | 166 | 168 | 169 | 1200 E. California Blvd. 170 | Pasadena 171 | CA 172 | 91125 173 | USA 174 | http://www.spitzer.caltech.edu 175 | 176 | 177 | 179 | 180 | 181 | Public Domain 182 | 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /pyavm/tests/data/ssc2005-11a1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 3000 6 | 1681 7 | -1 8 | 0221 9 | 10 | 12 | 1 13 | 3000 14 | 1681 15 | 2 16 | 3 17 | 300/1 18 | 300/1 19 | 2 20 | 21 | 22 | 8 23 | 8 24 | 8 25 | 26 | 27 | 28 | 30 | 2005-11-18T09:49:26-08:00 31 | 2008-04-10T10:27:15-07:00 32 | 0 33 | 1 34 | 2007-12-12T23:13:37-08:00 35 | Adobe Photoshop CS3 Macintosh 36 | 37 | 40 | adobe:docid:photoshop:659dcdeb-b983-11d9-96b2-d80e86aef8a7 41 | uuid:7CB0BEFEA5EB11DC9B3FDBD2C82F9EA0 42 | 43 | uuid:9e23f5fc-b411-11d9-a7a9-edf99f849549 44 | adobe:docid:photoshop:bd292885-b270-11d9-af4a-e00e99a6e16b 45 | 46 | 47 | 49 | image/tiff 50 | 51 | 52 | Spitzer Space Telescope 53 | 54 | 55 | 56 | 57 | http://www.spitzer.caltech.edu/Media/mediaimages/copyright.shtml 58 | 59 | 60 | 61 | 62 | NASA's Spitzer and Hubble Space Telescopes joined forces to create this striking composite image of one of the most popular sights in the universe. Messier 104 is commonly known as the Sombrero galaxy because in visible light, it resembles the broad-brimmed Mexican hat. However, in Spitzer's striking infrared view, the galaxy looks more like a "bull's eye."

In Hubble's visible light image (lower left panel), only the near rim of dust can be clearly seen in silhouette. Recent observations using Spitzer's infrared array camera (lower right panel) uncovered the bright, smooth ring of dust circling the galaxy, seen in red. Spitzer's infrared view of the starlight, piercing through the obscuring dust, is easily seen, along with the bulge of stars and an otherwise hidden disk of stars within the dust ring.

Spitzer's full view shows the disk is warped, which is often the result of a gravitational encounter with another galaxy, and clumpy areas spotted in the far edges of the ring indicate young star-forming regions.

The Sombrero galaxy is located some 28 million light-years away. Viewed from Earth, it is just six degrees south of its equatorial plane. Spitzer detected infrared emission not only from the ring, but from the center of the galaxy too, where there is a huge black hole, believed to be a billion times more massive than our Sun.

The Spitzer picture is composed of four images taken at 3.6 (blue), 4.5 (green), 5.8 (orange), and 8.0 (red) microns. The contribution from starlight (measured at 3.6 microns) has been subtracted from the 5.8 and 8-micron images to enhance the visibility of the dust features.

The Hubble Heritage Team took these observations in May-June 2003 with the space telescope's Advanced Camera for Surveys. Images were taken in three filters (red, green, and blue) to yield a natural-color image. The team took six pictures of the galaxy and then stitched them together to create the final composite image. This magnificent galaxy 63 | 64 | 65 | 66 | 67 | Sombrero Galaxy 68 | M104 69 | Messier 104 70 | 71 | 72 | 73 | 74 | Spitzer Spies Spectacular Sombrero 75 | 76 | 77 | 78 | 80 | Spitzer Space Telescope 81 | Infrared: NASA/JPL-Caltech/R. Ke 82 | NASA's Spitzer and Hubble Space Telescopes joined forces to create this striking composite image of one of the most popular sights in the universe. Messier 104 is commonly known as the Sombrero galaxy because in visible light, it resembles the broad-brimmed Mexican hat. However, in Spitzer's striking infrared view, the galaxy looks more like a "bull's eye." 83 | 2005-05-04 84 | 707DC4C98B5774DBCB2130B36B40D396 85 | 3 86 | 87 | 88 | 90 | http://www.spitzer.caltech.edu 91 | 1.1 92 | Good 93 | http://gallery.spitzer.caltech.edu/Imagegallery/image.php?image_name=ssc2005-11a 94 | Observation 95 | ssc2005-11a1 96 | ICRS 97 | 2000.0 98 | TAN 99 | Full 100 | -5.0978925445378 101 | FOV: 9.55 x 5.35 arcminutes; Ref coordinate: 12h39m45.9s -11d35m13.09s; derived from astrometry.net file ssc2005-11a1.fits 102 | Spitzer Science Center 103 | 104 | 105 | 189.941259706 106 | -11.5869704984 107 | 108 | 109 | 110 | 111 | 3000 112 | 1681 113 | 114 | 115 | 116 | 117 | 2587.89355469 118 | 1425.447177887 119 | 120 | 121 | 122 | 123 | -5.3083114895765E-05 124 | 5.3083114895765E-05 125 | 126 | 127 | 128 | 129 | C.5.1.1. 130 | 131 | 132 | 133 | 135 | True 136 | 137 | 139 | Print 140 | 141 | 143 | 144 | 1200 E. California Blvd. 145 | Pasadena 146 | CA 147 | 91125 148 | USA 149 | http://www.spitzer.caltech.edu 150 | 151 | 152 | 154 | 155 | 156 | Public Domain 157 | 158 | 159 | 160 | 162 | 163 | 164 | Other Keywords|Sombrero Galaxy 165 | 166 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /pyavm/tests/data/ssc2007-08a1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | Observation 6 | ICRS 7 | 2000.0 8 | TAN 9 | 1.1 10 | Good 11 | http://www.spitzer.caltech.edu 12 | Full 13 | -85.807813893047 14 | ssc2007-08a 15 | http://gallery.spitzer.caltech.edu/Imagegallery/image.php?image_name=ssc2007-08a 16 | FOV: 33.93 x 29.26 arcminutes; Ref coordinate: 6h32m23.18s 4d49m0.69s; derived from astrometry.net file ssc2007-08a1.fits 17 | Spitzer Science Center 18 | 19 | 20 | 98.0965674959 21 | 4.81685920513 22 | 23 | 24 | 25 | 26 | 1669 27 | 1439 28 | 29 | 30 | 31 | 32 | 4500 33 | 8000 34 | 24000 35 | 36 | 37 | 38 | 39 | Near-Infrared 40 | Near-Infrared 41 | Mid-Infrared 42 | 43 | 44 | 45 | 46 | Infrared 47 | Infrared 48 | Infrared 49 | 50 | 51 | 52 | 53 | Blue 54 | Green 55 | Red 56 | 57 | 58 | 59 | 60 | IRAC 61 | IRAC 62 | MIPS 63 | 64 | 65 | 66 | 67 | Spitzer 68 | Spitzer 69 | Spitzer 70 | 71 | 72 | 73 | 74 | -0.00033886430202736 75 | 0.00033886430202736 76 | 77 | 78 | 79 | 80 | B.3.3.1 81 | B.4.1.2 82 | B.4.2.1.1 83 | B.4.2.3 84 | 85 | 86 | 87 | 88 | 518.485176086 89 | 772.734962463 90 | 91 | 92 | 93 | 95 | NASA/JPL-Caltech/Z. Balog (Univ. 96 | Spitzer Space Telescope 97 | 2007-04-18 98 | This infrared image from NASA's Spitzer Space Telescope shows the Rosette nebula, a pretty star-forming region more than 5,000 light-years away in the constellation Monoceros. In optical light, the nebula looks like a rosebud, or the "rosette" adornments that date back to antiquity. 99 | 912E76B3DCD42F4ED3EBA4D3C967E8C0 100 | 3 101 | sRGB IEC61966-2.1 102 | 103 | 105 | 2007-04-17T13:45:11-07:00 106 | Adobe Photoshop CS2 Macintosh 107 | 2007-08-03T12:39:41-07:00 108 | 2008-04-10T10:29:26-07:00 109 | 1 110 | 0 111 | 112 | 114 | image/tiff 115 | 116 | 117 | Spitzer Space Telescope 118 | 119 | 120 | 121 | 122 | Rosette Nebula 123 | NGC2244 124 | 125 | 126 | 127 | 128 | http://www.spitzer.caltech.edu/Media/mediaimages/copyright.shtml 129 | 130 | 131 | 132 | 133 | This infrared image from NASA's Spitzer Space Telescope shows the Rosette nebula, a pretty star-forming region more than 5,000 light-years away in the constellation Monoceros. In optical light, the nebula looks like a rosebud, or the "rosette" adornments that date back to antiquity. But lurking inside this delicate cosmic rosebud are super hot stars, called O-stars, whose radiation and winds have collectively excavated layers of dust (green) and gas away, revealing the cavity of cooler dust (red). Some of the Rosette's O-stars can be seen in the bubble-like, red cavity; however the largest two blue stars in this picture are in the foreground, and not in the nebula itself. This image shows infrared light captured by Spitzer's infrared array camera. Light with wavelengths of 24 microns is red; light of 8 microns is green; and light of 4.5 microns is blue. 134 | 135 | 136 | 137 | 138 | Heart of the Rosette 139 | 140 | 141 | 142 | 145 | uuid:ADB4CE6E434811DC9555DB2FFED8D008 146 | uuid:0D0666E9EE7C11DBBCFBF05B80655647 147 | 148 | uuid:DCA0EE0EEE7911DBBCFBF05B80655647 149 | 150 | 151 | 153 | 1 154 | 1669 155 | 1439 156 | 2 157 | 3 158 | 300/1 159 | 300/1 160 | 2 161 | 162 | 163 | 8 164 | 8 165 | 8 166 | 167 | 168 | 169 | 171 | 1669 172 | 1439 173 | 1 174 | 0221 175 | 176 | 178 | True 179 | 180 | 182 | Print 183 | 184 | 186 | 187 | 1200 E. California Blvd. 188 | Pasadena 189 | CA 190 | 91125 191 | USA 192 | http://www.spitzer.caltech.edu 193 | 194 | 195 | 197 | 198 | 199 | Public Domain 200 | 201 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /pyavm/tests/data/wd2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | Observation 6 | J2000 7 | TAN 8 | 1.0 9 | Chandra X-ray Observatory 10 | http://chandra.harvard.edu 11 | http://chandra.harvard.edu/photo/2008/wd2/ 12 | Good 13 | 1 14 | FK5 15 | Full 16 | 17 | 18 | 155.988418579 19 | -57.7658996582 20 | 21 | 22 | 23 | 24 | 4200 25 | 4200 26 | 27 | 28 | 29 | 30 | 2079.00000000 31 | 1919.00000000 32 | 33 | 34 | 35 | 36 | -3.33126623911E-05 37 | -8.75816896658E-09 38 | -8.75816896658E-09 39 | 3.33126623911E-05 40 | 41 | 42 | 43 | 44 | Linear 45 | 46 | 47 | 48 | 49 | 0.00000000000000 50 | 51 | 52 | 53 | 54 | 211.98999999999998 55 | 56 | 57 | 58 | 59 | 0.00000000000000 60 | 61 | 62 | 63 | 64 | 211.98999999999998 65 | 66 | 67 | 68 | 69 | 2.00000000000000 70 | 71 | 72 | 73 | 74 | 255.00000000000000 75 | 76 | 77 | 78 | 79 | Chandra 80 | Chandra 81 | Chandra 82 | 83 | 84 | 85 | 86 | ACIS 87 | ACIS 88 | ACIS 89 | 90 | 91 | 92 | 93 | Red 94 | Green 95 | Blue 96 | 97 | 98 | 99 | 100 | X-ray 101 | X-ray 102 | X-ray 103 | 104 | 105 | 106 | 107 | X-ray 108 | X-ray 109 | X-ray 110 | 111 | 112 | 113 | 114 | 136800 115 | 136800 116 | 136800 117 | 118 | 119 | 120 | 121 | http://chandra.harvard.edu/photo/2008/wd2/wd2.tif 122 | 123 | 124 | 125 | 126 | 2003-08-23-1820 127 | 2003-08-23-1820 128 | 2003-08-23-1820 129 | 130 | 131 | 132 | 133 | B.3.1.2 134 | B.3.6.4 135 | 136 | 137 | 138 | 139 | 1.9 140 | 0.83 141 | 0.21 142 | 143 | 144 | 145 | 148 | uuid:0C6AF641E6D611DC96FACE9FF167CC61 149 | uuid:E1AE726AE88311DCA2F2E5071A5F9A6E 150 | 151 | uuid:0C6AF63EE6D611DC96FACE9FF167CC61 152 | uuid:0C6AF63EE6D611DC96FACE9FF167CC61 153 | 154 | 155 | 157 | 2008-02-27T15:08:30-05:00 158 | 2008-02-29T18:30:14-05:00 159 | 2008-03-10T19:21:46-04:00 160 | Adobe Photoshop CS2 Macintosh 161 | 162 | 164 | image/tiff 165 | 166 | 167 | Chandra X-ray Observatory Center 168 | 169 | 170 | 171 | 172 | Westerlund 2 173 | Wd 2 174 | 175 | 176 | 177 | 178 | This Chandra X-ray Observatory image shows Westerlund 2, a young star cluster with an estimated age of about one or two million years that contains some of the hottest, brightest, and most massive stars known. In this image, low-energy X-rays are colored red, intermediate-energy X-rays in green, and high-energy X-rays in blue. The image shows a very high density of massive stars that are bright in X-rays, plus diffuse X-ray emission. An incredibly massive double star system called WR20a is visible as the bright yellow point just below and to the right of the cluster's center. 179 | 180 | 181 | 182 | 184 | 8E2A5B2B8589F60995711C610F0C7B9E 185 | Chandra X-ray Observatory 186 | Westerlund 2: A Stellar Sight 187 | NASA/CXC/Univ. de Liège/Y. Naze et al 188 | 2008-01-23 189 | 190 | 3 191 | sRGB IEC61966-2.1 192 | 193 | 195 | 1 196 | 4200 197 | 4200 198 | 2 199 | 3 200 | 72/1 201 | 72/1 202 | 2 203 | 204 | 205 | 8 206 | 8 207 | 8 208 | 209 | 210 | 211 | 213 | 4200 214 | 4200 215 | 1 216 | 0221 217 | 218 | 220 | True 221 | 222 | 224 | Print 225 | 226 | 228 | 229 | cxcpub@cfa.harvard.edu 230 | 617.496.7941 231 | 60 Garden St. 232 | Cambridge 233 | MA 234 | 02138 235 | USA 236 | 237 | 238 | 240 | 241 | 242 | http://chandra.harvard.edu/photo/image_use.html 243 | 244 | 245 | 246 | 247 | -------------------------------------------------------------------------------- /pyavm/tests/test_avm.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pytest 3 | 4 | from ..avm import AVM 5 | 6 | ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data') 7 | 8 | 9 | def test_from_image_jpg(): 10 | avm = AVM.from_image(os.path.join(ROOT, 'eso_eso1723a_320.jpg')) 11 | assert avm.ID == 'eso1723a' 12 | 13 | 14 | def test_from_image_png(): 15 | avm = AVM.from_image(os.path.join(ROOT, 'eso_eso1723a_320.png')) 16 | assert avm.ID == 'eso1723a' 17 | 18 | 19 | def test_from_image_other(tmpdir): 20 | # Here we test the brute-force search 21 | with open(os.path.join(ROOT, '3c321.avm.xml'), 'rb') as f: 22 | content = f.read() 23 | filename = tmpdir.join('testfile').strpath 24 | with open(filename, 'wb') as f: 25 | f.write(b'" id="W5M0MpCehiHzreSzNTczkc9d"?>') 26 | f.write(content) 27 | f.write(b'') 28 | avm = AVM.from_image(filename) 29 | assert avm.Publisher == 'Chandra X-ray Observatory' 30 | 31 | 32 | def test_from_wcs_cd(): 33 | pytest.importorskip('astropy') 34 | pytest.importorskip('numpy') 35 | import numpy as np 36 | from astropy.wcs import WCS 37 | scale = np.array([[-1.5, 0], [0, 2.5]]) 38 | theta = np.radians(60) 39 | rotation = np.array([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]]) 40 | cd = np.matmul(rotation, scale) 41 | wcs = WCS(naxis=2) 42 | wcs.wcs.ctype = ['RA---TAN', 'DEC--TAN'] 43 | wcs.wcs.cd = cd 44 | avm = AVM.from_wcs(wcs) 45 | np.testing.assert_allclose(avm.Spatial.Scale, [-1.5, 2.5]) 46 | np.testing.assert_allclose(avm.Spatial.Rotation, 60) 47 | -------------------------------------------------------------------------------- /pyavm/tests/test_header.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, division 2 | 3 | try: 4 | unicode 5 | except: 6 | basestring = unicode = str 7 | 8 | import os 9 | import pytest 10 | 11 | pytest.importorskip('astropy') 12 | 13 | from astropy.io import fits 14 | from astropy.wcs import WCS 15 | 16 | from ..avm import AVM, NoSpatialInformation 17 | 18 | ROOT = os.path.dirname(os.path.abspath(__file__)) 19 | 20 | 21 | def test_from_header(): 22 | header = fits.Header.fromtextfile(os.path.join(ROOT, 'data', 'example_header.hdr')) 23 | a = AVM.from_header(header) 24 | assert isinstance(a.Spatial.FITSheader, basestring) 25 | assert a.Spatial.FITSheader == header 26 | # assert a.Spatial.Equinox == 2000. # returns NaN at the moment 27 | assert a.Spatial.CoordsystemProjection == 'CAR' 28 | assert a.Spatial.ReferenceDimension[0] == 599 29 | assert a.Spatial.ReferenceDimension[1] == 599 30 | assert a.Spatial.ReferenceValue[0] == 0. 31 | assert a.Spatial.ReferenceValue[1] == 0. 32 | assert a.Spatial.ReferencePixel[0] == 299.628 33 | assert a.Spatial.ReferencePixel[1] == 299.394 34 | assert a.Spatial.Scale[0] == -0.001666666707 35 | assert a.Spatial.Scale[1] == +0.001666666707 36 | assert a.Spatial.Quality == 'Full' 37 | 38 | 39 | def test_from_header_cd(): 40 | header = fits.Header.fromtextfile(os.path.join(ROOT, 'data', 'example_header.hdr')) 41 | header['CD1_1'] = header.pop('CDELT1') 42 | header['CD2_2'] = header.pop('CDELT2') 43 | a = AVM.from_header(header) 44 | assert isinstance(a.Spatial.FITSheader, basestring) 45 | assert a.Spatial.FITSheader == header 46 | # assert a.Spatial.Equinox == 2000. # returns NaN at the moment 47 | assert a.Spatial.CoordsystemProjection == 'CAR' 48 | assert a.Spatial.ReferenceDimension[0] == 599 49 | assert a.Spatial.ReferenceDimension[1] == 599 50 | assert a.Spatial.ReferenceValue[0] == 0. 51 | assert a.Spatial.ReferenceValue[1] == 0. 52 | assert a.Spatial.ReferencePixel[0] == 299.628 53 | assert a.Spatial.ReferencePixel[1] == 299.394 54 | assert a.Spatial.Scale[0] == -0.001666666707 55 | assert a.Spatial.Scale[1] == +0.001666666707 56 | assert a.Spatial.Quality == 'Full' 57 | 58 | 59 | def test_wcs_1(): 60 | header = fits.Header.fromtextfile(os.path.join(ROOT, 'data', 'example_header.hdr')) 61 | a = AVM.from_header(header) 62 | b = AVM.from_wcs(a.to_wcs(), shape=(header['NAXIS2'], header['NAXIS1'])) 63 | # assert a.Spatial.Equinox == b.Spatial.Equinox # returns NaN at the moment 64 | assert a.Spatial.CoordsystemProjection == b.Spatial.CoordsystemProjection 65 | assert a.Spatial.ReferenceDimension[0] == b.Spatial.ReferenceDimension[0] 66 | assert a.Spatial.ReferenceDimension[1] == b.Spatial.ReferenceDimension[1] 67 | assert a.Spatial.ReferenceValue[0] == b.Spatial.ReferenceValue[0] 68 | assert a.Spatial.ReferenceValue[1] == b.Spatial.ReferenceValue[1] 69 | assert a.Spatial.ReferencePixel[0] == b.Spatial.ReferencePixel[0] 70 | assert a.Spatial.ReferencePixel[1] == b.Spatial.ReferencePixel[1] 71 | assert a.Spatial.Scale[0] == b.Spatial.Scale[0] 72 | assert a.Spatial.Scale[1] == b.Spatial.Scale[1] 73 | assert a.Spatial.Quality == b.Spatial.Quality 74 | 75 | 76 | def test_wcs_2(): 77 | header = fits.Header.fromtextfile(os.path.join(ROOT, 'data', 'example_header.hdr')) 78 | a = WCS(header) 79 | b = AVM.from_wcs(a).to_wcs() 80 | # assert a.wcs.equinox == b.wcs.equinox 81 | assert a.wcs.ctype[0] == b.wcs.ctype[0] 82 | assert a.wcs.ctype[1] == b.wcs.ctype[1] 83 | assert a.wcs.crval[0] == b.wcs.crval[0] 84 | assert a.wcs.crval[1] == b.wcs.crval[1] 85 | assert a.wcs.crpix[0] == b.wcs.crpix[0] 86 | assert a.wcs.crpix[1] == b.wcs.crpix[1] 87 | assert a.wcs.cdelt[0] == b.wcs.cdelt[0] 88 | assert a.wcs.cdelt[1] == b.wcs.cdelt[1] 89 | assert a.wcs.crota[0] == b.wcs.crota[0] 90 | assert a.wcs.crota[1] == b.wcs.crota[1] 91 | assert a.wcs.radesys == b.wcs.radesys 92 | -------------------------------------------------------------------------------- /pyavm/tests/test_io.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, division 2 | 3 | import os 4 | import glob 5 | import warnings 6 | 7 | import pytest 8 | 9 | pytest.importorskip('PIL') 10 | pytest.importorskip('numpy') 11 | 12 | from PIL import Image 13 | import numpy as np 14 | 15 | from .. import AVM 16 | 17 | ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data') 18 | 19 | XML_FILES = glob.glob(os.path.join(ROOT, '*.xml')) 20 | 21 | 22 | @pytest.mark.parametrize('xml_file', XML_FILES) 23 | def test_io_png(tmpdir, xml_file): 24 | avm = AVM.from_xml_file(xml_file) 25 | filename_in = tmpdir.join('test_in.png').strpath 26 | filename_out = tmpdir.join('test_out.png').strpath 27 | i = Image.fromarray(np.ones((16, 16), dtype=np.uint8)) 28 | i.save(filename_in) 29 | avm.embed(filename_in, filename_out, verify=True) 30 | 31 | 32 | @pytest.mark.parametrize('xml_file', XML_FILES) 33 | def test_io_jpeg(tmpdir, xml_file): 34 | avm = AVM.from_xml_file(xml_file) 35 | filename_in = tmpdir.join('test_in.jpg').strpath 36 | filename_out = tmpdir.join('test_out.jpg').strpath 37 | i = Image.fromarray(np.ones((16, 16), dtype=np.uint8)) 38 | i.save(filename_in) 39 | avm.embed(filename_in, filename_out, verify=True) 40 | 41 | 42 | @pytest.mark.parametrize('xml_file', XML_FILES) 43 | def test_io_png_repeat(tmpdir, xml_file): 44 | warnings.simplefilter('always') 45 | avm = AVM.from_xml_file(xml_file) 46 | filename_in = tmpdir.join('test_in.png').strpath 47 | filename_out_1 = tmpdir.join('test_out_1.png').strpath 48 | filename_out_2 = tmpdir.join('test_out_2.png').strpath 49 | i = Image.fromarray(np.ones((16, 16), dtype=np.uint8)) 50 | i.save(filename_in) 51 | with warnings.catch_warnings(record=True) as w: 52 | avm.embed(filename_in, filename_out_1, verify=True) 53 | messages = [str(x.message) for x in w] 54 | assert 'Discarding existing XMP packet from PNG file' not in messages 55 | with warnings.catch_warnings(record=True) as w: 56 | avm.embed(filename_out_1, filename_out_2, verify=True) 57 | messages = [str(x.message) for x in w] 58 | assert 'Discarding existing XMP packet from PNG file' in messages 59 | 60 | 61 | @pytest.mark.parametrize('xml_file', XML_FILES) 62 | def test_io_jpeg_repeat(tmpdir, xml_file): 63 | warnings.simplefilter('always') 64 | avm = AVM.from_xml_file(xml_file) 65 | filename_in = tmpdir.join('test_in.jpg').strpath 66 | filename_out_1 = tmpdir.join('test_out_1.jpg').strpath 67 | filename_out_2 = tmpdir.join('test_out_2.jpg').strpath 68 | i = Image.fromarray(np.ones((16, 16), dtype=np.uint8)) 69 | i.save(filename_in) 70 | with warnings.catch_warnings(record=True) as w: 71 | avm.embed(filename_in, filename_out_1, verify=True) 72 | messages = [str(x.message) for x in w] 73 | assert 'Discarding existing XMP packet from JPEG file' not in messages 74 | with warnings.catch_warnings(record=True) as w: 75 | avm.embed(filename_out_1, filename_out_2, verify=True) 76 | messages = [str(x.message) for x in w] 77 | assert 'Discarding existing XMP packet from JPEG file' in messages 78 | -------------------------------------------------------------------------------- /pyavm/tests/test_main.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function, division 2 | 3 | import os 4 | import glob 5 | 6 | import pytest 7 | 8 | from ..avm import AVM, NoSpatialInformation 9 | 10 | ROOT = os.path.dirname(os.path.abspath(__file__)) 11 | 12 | XML_FILES = glob.glob(os.path.join(ROOT, 'data', '*.xml')) 13 | 14 | 15 | @pytest.mark.parametrize('filename', XML_FILES) 16 | def test_parse(filename): 17 | AVM.from_xml_file(filename) 18 | 19 | 20 | @pytest.mark.parametrize('filename', XML_FILES) 21 | def test_to_xml(filename): 22 | a = AVM.from_xml_file(filename) 23 | a.to_xml() 24 | 25 | 26 | @pytest.mark.parametrize('filename', XML_FILES) 27 | def test_to_xmp(filename): 28 | a = AVM.from_xml_file(filename) 29 | a.to_xmp() 30 | 31 | 32 | NO_WCS = [os.path.join(ROOT, 'data', 'heic0409a.xml'), 33 | os.path.join(ROOT, 'data', 'sig05-021-alpha.xml'), 34 | os.path.join(ROOT, 'data', 'ssc2004-06a1-alpha.xml'), 35 | os.path.join(ROOT, 'data', 'ssc2004-06b1-alpha.xml')] 36 | 37 | XML_FILES_WCS = [x for x in XML_FILES if x not in NO_WCS] 38 | 39 | 40 | @pytest.mark.parametrize('filename', XML_FILES_WCS) 41 | def test_to_wcs(filename): 42 | pytest.importorskip('astropy') 43 | a = AVM.from_xml_file(filename) 44 | a.to_wcs() 45 | 46 | 47 | @pytest.mark.parametrize('filename', XML_FILES_WCS) 48 | def test_to_wcs_target_image(filename, tmpdir): 49 | pytest.importorskip('PIL') 50 | pytest.importorskip('astropy') 51 | from PIL import Image 52 | image = Image.frombytes(data=b"1111", size=(2, 2), mode="L") 53 | image_file = tmpdir.join('test.png').strpath 54 | image.save(image_file) 55 | image.close() 56 | a = AVM.from_xml_file(filename) 57 | a.Spatial.ReferenceDimension = (30, 30) 58 | a.to_wcs(target_image=image_file) 59 | 60 | 61 | @pytest.mark.parametrize('filename', XML_FILES_WCS) 62 | def test_to_wcs_target_shape(filename, tmpdir): 63 | pytest.importorskip('PIL') 64 | pytest.importorskip('astropy') 65 | a = AVM.from_xml_file(filename) 66 | a.Spatial.ReferenceDimension = (30, 30) 67 | a.to_wcs(target_shape=(2, 2)) 68 | 69 | 70 | @pytest.mark.parametrize('filename', NO_WCS) 71 | def test_to_wcs_nowcs(filename): 72 | pytest.importorskip('astropy') 73 | a = AVM.from_xml_file(filename) 74 | with pytest.raises(NoSpatialInformation): 75 | a.to_wcs() 76 | -------------------------------------------------------------------------------- /pyavm/tests/test_specs.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import warnings 3 | warnings.filterwarnings('always') 4 | 5 | from ..avm import AVM, AVMContainer 6 | 7 | 8 | @pytest.mark.parametrize('version', [1.1, 1.2]) 9 | def test_specs(version): 10 | 11 | a = AVM(version=version) 12 | 13 | # Creator Metadata 14 | a.Creator = "PyAVM" 15 | a.CreatorURL = "http://www.github.com" 16 | a.Contact.Name = ["Thomas Robitaille"] 17 | a.Contact.Email = "thomas.robitaille@gmail.com" 18 | a.Contact.Address = "None of your business" 19 | a.Contact.Telephone = "I think we're getting a little too personal" 20 | a.Contact.City = "Heidelberg" 21 | a.Contact.StateProvince = "Baden-Wuttemburg" 22 | a.Contact.PostalCode = "What could you possibly need this for?" 23 | a.Contact.Country = "Germany" 24 | a.Rights = "Wrongs" 25 | 26 | # Content Metadata 27 | a.Title = "A very thorough test" 28 | a.Headline = "What I said above" 29 | a.Description = "Um, I guess there's not much more to say about this!" 30 | a.Subject.Category = ["Tests"] 31 | a.Subject.Name = ["PyAVM"] 32 | a.Distance = ["3"] 33 | a.Distance.Notes = "Not much to say, really" 34 | a.ReferenceURL = "http://www.github.com" 35 | a.Credit = "Me" 36 | a.Date = "10 April 2013" 37 | a.ID = "123123123" 38 | a.Type = "Simulation" 39 | a.Image.ProductQuality = "Moderate" 40 | 41 | # Observation Metadata 42 | a.Facility = ["Python"] 43 | a.Instrument = ["CPython"] 44 | a.Spectral.ColorAssignment = ["Purple"] 45 | a.Spectral.Band = ["Optical"] 46 | a.Spectral.Bandpass = ["Arbitrary"] 47 | a.Spectral.CentralWavelength = [5.] 48 | a.Spectral.Notes = "Still testing" 49 | a.Temporal.StartTime = ["5 Feb 2011"] 50 | a.Temporal.IntegrationTime = [4.4] 51 | a.DatasetID = ["12421412"] 52 | 53 | # Coordinate Metadata 54 | a.Spatial.CoordinateFrame = "GAL" 55 | a.Spatial.Equinox = '2000' 56 | a.Spatial.ReferenceValue = [33.3, 44.4] 57 | a.Spatial.ReferenceDimension = [300, 400] 58 | a.Spatial.ReferencePixel = [2., 3.] 59 | a.Spatial.Scale = [0.2, 0.3] 60 | a.Spatial.Rotation = 122. 61 | a.Spatial.CoordsystemProjection = "CAR" 62 | a.Spatial.Quality = "Full" 63 | a.Spatial.Notes = "Not much to say" 64 | a.Spatial.FITSheader = "SIMPLE = T" 65 | a.Spatial.CDMatrix = [3.4, 3.3, 5.5, 2.1] 66 | 67 | # Publisher Metadata 68 | a.Publisher = "Tom" 69 | a.PublisherID = "125521" 70 | a.ResourceID = "3995611" 71 | a.ResourceURL = "http://www.github.com" 72 | a.RelatedResources = ["Testing", "Python", "PyAVM"] 73 | a.MetadataDate = "20 April 2013" 74 | 75 | # FITS Liberator Metadata 76 | 77 | a.FL.BackgroundLevel = [3.4] 78 | a.FL.BlackLevel = [4.4] 79 | a.FL.ScaledPeakLevel = [5.5] 80 | a.FL.PeakLevel = [10.2] 81 | a.FL.WhiteLevel = [11.3] 82 | a.FL.ScaledBackgroundLevel = [4.5] 83 | a.FL.StretchFunction = ['Log'] 84 | 85 | # Spec-dependent keywords 86 | if version == 1.1: 87 | with pytest.raises(AttributeError) as exc: 88 | a.ProposalID = ["12421412"] 89 | assert exc.value.args[0] == "ProposalID is not a valid AVM group or tag in the 1.1 standard" 90 | with pytest.raises(AttributeError) as exc: 91 | a.PublicationID = ['799292'] 92 | assert exc.value.args[0] == "PublicationID is not a valid AVM group or tag in the 1.1 standard" 93 | else: 94 | a.ProposalID = ["12421412"] 95 | a.PublicationID = ['799292'] 96 | 97 | x = a.to_xml() 98 | 99 | b = AVM.from_xml(x) 100 | 101 | for key in a._items: 102 | if isinstance(a._items[key], AVMContainer): 103 | for subkey in a._items[key]._items: 104 | assert a._items[key]._items[subkey] == b._items[key]._items[subkey] 105 | else: 106 | assert a._items[key] == b._items[key] 107 | 108 | 109 | def test_warning(): 110 | 111 | # Start of with a version=1.2 AVM object 112 | a = AVM(version=1.2) 113 | a.ProposalID = ["25661"] 114 | 115 | # Then change to version=1.1, which doesn't contain ProposalID 116 | with warnings.catch_warnings(record=True) as w: 117 | a.MetadataVersion = 1.1 118 | assert len(w) == 1 119 | assert str(w[0].message) == "ProposalID is not defined in format specification 1.1 and will be deleted" 120 | 121 | try: 122 | a.ProposalID = ["44663"] 123 | except AttributeError as exc: 124 | assert exc.args[0] == "ProposalID is not a valid AVM group or tag in the 1.1 standard" 125 | -------------------------------------------------------------------------------- /pyavm/tests/test_wcs_utils.py: -------------------------------------------------------------------------------- 1 | from itertools import product 2 | import pytest 3 | 4 | pytest.importorskip('numpy') 5 | pytest.importorskip('astropy') 6 | 7 | import numpy as np 8 | from astropy.wcs import WCS 9 | 10 | from ..wcs_utils import get_cdelt_crota 11 | 12 | SCALES = [(0.4, 0.5), 13 | (-0.2, 0.9), 14 | (0.2, -0.5), 15 | (-0.4, -4.3)] 16 | 17 | ROTATIONS = np.linspace(0, 330, 12) 18 | 19 | 20 | @pytest.mark.parametrize(('scale', 'rotation'), product(SCALES, ROTATIONS)) 21 | def test_get_cdelt_crota(scale, rotation): 22 | 23 | scale_matrix = np.array([[scale[0], 0], [0, scale[1]]]) 24 | theta = np.radians(rotation) 25 | rotation_matrix = np.array([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]]) 26 | cd = np.matmul(rotation_matrix, scale_matrix) 27 | 28 | wcs = WCS(naxis=2) 29 | wcs.wcs.ctype = ['RA---TAN', 'DEC--TAN'] 30 | wcs.wcs.cd = cd 31 | 32 | cdelt1, cdelt2, crota2 = get_cdelt_crota(wcs) 33 | 34 | try: 35 | np.testing.assert_allclose(crota2, rotation) 36 | np.testing.assert_allclose(cdelt1, scale[0]) 37 | np.testing.assert_allclose(cdelt2, scale[1]) 38 | except AssertionError: 39 | np.testing.assert_allclose(crota2 + 180, rotation) 40 | np.testing.assert_allclose(cdelt1, -scale[0]) 41 | np.testing.assert_allclose(cdelt2, -scale[1]) 42 | -------------------------------------------------------------------------------- /pyavm/wcs_utils.py: -------------------------------------------------------------------------------- 1 | def arg(x, y): 2 | import numpy as np 3 | return np.arctan2(y, x) 4 | 5 | 6 | def get_cd(wcs): 7 | 8 | pc11, pc12, pc21, pc22 = wcs.wcs.get_pc().ravel() 9 | cdelt1, cdelt2 = wcs.wcs.get_cdelt() 10 | 11 | cd11 = cdelt1 * pc11 12 | cd12 = cdelt1 * pc12 13 | cd21 = cdelt2 * pc21 14 | cd22 = cdelt2 * pc22 15 | 16 | return cd11, cd12, cd21, cd22 17 | 18 | 19 | def get_cdelt_crota(wcs): 20 | 21 | import numpy as np 22 | 23 | # This implements the algorithm from: 24 | # 25 | # Representations of celestial coordinates in FITS 26 | # Calabretta & Greisen (2002) 27 | # 28 | # Section: 6.2. Supporting old interpreters 29 | 30 | cd11, cd12, cd21, cd22 = get_cd(wcs) 31 | 32 | if cd21 > 0: 33 | rho_a = arg(cd11, cd21) 34 | elif cd21 == 0: 35 | rho_a = 0 36 | else: 37 | rho_a = arg(-cd11, -cd21) 38 | 39 | if cd12 > 0: 40 | rho_b = arg(-cd22, cd12) 41 | elif cd12 == 0: 42 | rho_b = 0 43 | else: 44 | rho_b = arg(cd22, -cd12) 45 | 46 | rho_diff = abs(rho_a - rho_b) 47 | 48 | if rho_diff > np.pi: 49 | rho_diff -= 2 * np.pi 50 | 51 | if rho_diff > 1e-10: 52 | raise ValueError("WCS cannot be represented by CDELT/CROTA") 53 | 54 | rho = 0.5 * (rho_a + rho_b) 55 | 56 | if np.abs(np.cos(rho)) > 0.5: 57 | cdelt1 = cd11 / np.cos(rho) 58 | cdelt2 = cd22 / np.cos(rho) 59 | else: 60 | cdelt1 = cd21 / np.sin(rho) 61 | cdelt2 = -cd12 / np.sin(rho) 62 | 63 | crota2 = np.degrees(rho) % 360 64 | 65 | return cdelt1, cdelt2, crota2 66 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "PyAVM" 3 | description = "Simple pure-python AVM meta-data handling" 4 | readme = "README.rst" 5 | keywords = [ 6 | "Scientific/Engineering", 7 | ] 8 | license = { text = "MIT" } 9 | authors = [ 10 | { name = "Thomas Robitaille", email = "thomas.robitaille@gmail.com" } 11 | ] 12 | requires-python = ">=3.8" 13 | classifiers = [ 14 | "Development Status :: 4 - Beta", 15 | "License :: OSI Approved :: MIT License", 16 | "Programming Language :: Python", 17 | "Programming Language :: Python :: 3 :: Only", 18 | "Programming Language :: Python :: 3.8", 19 | "Programming Language :: Python :: 3.9", 20 | "Programming Language :: Python :: 3.10", 21 | "Programming Language :: Python :: 3.11", 22 | "Programming Language :: Python :: 3.12", 23 | ] 24 | dynamic = [ 25 | "version", 26 | ] 27 | [project.optional-dependencies] 28 | test = [ 29 | "pytest", 30 | "pytest-cov", 31 | ] 32 | testall = [ 33 | "astropy", 34 | "numpy", 35 | "pillow", 36 | ] 37 | [project.urls] 38 | documentation = "http://astrofrog.github.io/pyavm/" 39 | homepage = "http://astrofrog.github.io/pyavm/" 40 | repository = "https://github.com/astrofrog/pyavm" 41 | 42 | [build-system] 43 | build-backend = 'setuptools.build_meta' 44 | requires = [ 45 | "setuptools", 46 | "setuptools_scm", 47 | "wheel", 48 | ] 49 | 50 | [tool.setuptools] 51 | include-package-data = true 52 | license-files = ["LICENSE"] 53 | 54 | [tool.setuptools.packages.find] 55 | include = ["pyavm", "pyavm.tests", "pyavm.tests.data"] 56 | namespaces = false 57 | 58 | [tool.setuptools.package-data] 59 | "pyavm.tests" = [ 60 | "data/*", 61 | "data/*.jpg", 62 | "data/*.png", 63 | "data/*.xml", 64 | "data/*.hdr" 65 | ] 66 | 67 | [tool.setuptools_scm] 68 | write_to = "pyavm/_version.py" 69 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = 3 | py{38,39,310,311,312}-test{,-all} 4 | codestyle 5 | requires = 6 | setuptools >= 30.3.0 7 | pip >= 19.3.1 8 | isolated_build = true 9 | 10 | [testenv] 11 | changedir = 12 | test: .tmp/{envname} 13 | description = 14 | test: run tests with pytest 15 | extras = 16 | test 17 | all: testall 18 | commands = 19 | pip freeze 20 | python -m pytest --pyargs pyavm {posargs} 21 | 22 | [testenv:codestyle] 23 | skip_install = true 24 | description = invoke style checks on package code 25 | deps = flake8 26 | commands = flake8 fast_histogram 27 | --------------------------------------------------------------------------------